@shipload/sdk 1.0.0-next.7 → 1.0.0-next.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/scan.d.ts +52 -0
- package/lib/scan.js +162 -0
- package/lib/scan.js.map +1 -0
- package/lib/scan.m.js +152 -0
- package/lib/scan.m.js.map +1 -0
- package/lib/shipload.d.ts +5696 -1503
- package/lib/shipload.js +20294 -5200
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +20371 -5683
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +1648 -0
- package/lib/testing.js +6980 -0
- package/lib/testing.js.map +1 -0
- package/lib/testing.m.js +6974 -0
- package/lib/testing.m.js.map +1 -0
- package/package.json +20 -2
- package/src/capabilities/craftable.test.ts +82 -0
- package/src/capabilities/craftable.ts +82 -0
- package/src/capabilities/crafting.test.ts +80 -0
- package/src/capabilities/crafting.ts +50 -8
- package/src/capabilities/gathering.test.ts +29 -0
- package/src/capabilities/gathering.ts +38 -20
- package/src/capabilities/hauling.ts +0 -5
- package/src/capabilities/index.ts +0 -1
- package/src/capabilities/modules.ts +27 -30
- package/src/capabilities/movement.ts +1 -1
- package/src/capabilities/storage.test.ts +26 -0
- package/src/capabilities/storage.ts +22 -7
- package/src/contracts/fund.ts +306 -0
- package/src/contracts/platform.ts +312 -3
- package/src/contracts/server.ts +2661 -466
- package/src/contracts/token.ts +170 -0
- package/src/contracts.ts +2 -0
- package/src/coordinates/address.ts +88 -0
- package/src/coordinates/constants.test.ts +15 -0
- package/src/coordinates/constants.ts +23 -0
- package/src/coordinates/index.ts +15 -0
- package/src/coordinates/memo.test.ts +47 -0
- package/src/coordinates/memo.ts +20 -0
- package/src/coordinates/permutation.ts +77 -0
- package/src/coordinates/regions.ts +48 -0
- package/src/coordinates/sectors.ts +115 -0
- package/src/data/capabilities.ts +49 -18
- package/src/data/capability-formulas.ts +26 -14
- package/src/data/catalog.test.ts +19 -0
- package/src/data/catalog.ts +13 -3
- package/src/data/charters.json +396 -0
- package/src/data/citizenry-adjectives.json +42 -0
- package/src/data/citizenry-nouns.json +50 -0
- package/src/data/colors.ts +14 -32
- package/src/data/entities.json +421 -15
- package/src/data/item-ids.ts +53 -13
- package/src/data/items.json +330 -77
- package/src/data/kind-registry.json +212 -0
- package/src/data/kind-registry.ts +192 -0
- package/src/data/metadata.ts +312 -51
- package/src/data/recipes-runtime.ts +6 -23
- package/src/data/recipes.json +2173 -173
- package/src/data/tiers.ts +9 -6
- package/src/derivation/build-methods.test.ts +71 -0
- package/src/derivation/build-methods.ts +50 -0
- package/src/derivation/capabilities.test.ts +348 -0
- package/src/derivation/capabilities.ts +707 -0
- package/src/derivation/capability-mappings.ts +60 -16
- package/src/derivation/crafting.test.ts +17 -0
- package/src/derivation/crafting.ts +57 -34
- package/src/derivation/index.ts +27 -2
- package/src/derivation/recipe-usage.test.ts +88 -0
- package/src/derivation/recipe-usage.ts +141 -0
- package/src/derivation/reserve-regen.ts +34 -0
- package/src/derivation/resources.ts +9 -1
- package/src/derivation/rollups.test.ts +55 -0
- package/src/derivation/rollups.ts +72 -0
- package/src/derivation/stars.test.ts +67 -0
- package/src/derivation/stars.ts +25 -0
- package/src/derivation/stat-scaling.ts +12 -0
- package/src/derivation/stats.ts +6 -6
- package/src/derivation/strata.ts +5 -3
- package/src/derivation/stratum.ts +26 -22
- package/src/derivation/tiers.ts +40 -7
- package/src/derivation/upgrades.ts +14 -0
- package/src/derivation/wormhole.ts +154 -0
- package/src/entities/entity.ts +102 -0
- package/src/entities/gamestate.ts +3 -28
- package/src/entities/makers.ts +144 -136
- package/src/entities/slot-multiplier.ts +43 -0
- package/src/entity/hydrate.test.ts +156 -0
- package/src/entity/hydrate.ts +86 -0
- package/src/errors.ts +21 -16
- package/src/format.ts +42 -9
- package/src/index-module.ts +411 -32
- package/src/influence/ballot.test.ts +103 -0
- package/src/influence/ballot.ts +98 -0
- package/src/influence/categories.ts +32 -0
- package/src/influence/charter-metadata.test.ts +136 -0
- package/src/influence/charter-metadata.ts +236 -0
- package/src/influence/charters.test.ts +112 -0
- package/src/influence/charters.ts +150 -0
- package/src/influence/citizenry.ts +38 -0
- package/src/influence/civic-shuttle.test.ts +202 -0
- package/src/influence/civic-shuttle.ts +142 -0
- package/src/influence/civic.test.ts +287 -0
- package/src/influence/civic.ts +269 -0
- package/src/influence/constants.ts +84 -0
- package/src/influence/decay.ts +20 -0
- package/src/influence/decomp.ts +95 -0
- package/src/influence/demand.ts +106 -0
- package/src/influence/duration.ts +101 -0
- package/src/influence/index.ts +118 -0
- package/src/influence/influence.test.ts +450 -0
- package/src/influence/quality.ts +25 -0
- package/src/influence/valuation.ts +154 -0
- package/src/managers/actions.ts +940 -93
- package/src/managers/balances.test.ts +161 -0
- package/src/managers/balances.ts +93 -0
- package/src/managers/base.ts +10 -2
- package/src/managers/cluster.test.ts +39 -0
- package/src/managers/cluster.ts +53 -0
- package/src/managers/construction-types.ts +80 -0
- package/src/managers/construction.ts +453 -0
- package/src/managers/context.ts +58 -1
- package/src/managers/coordinates.ts +14 -0
- package/src/managers/entities.ts +35 -63
- package/src/managers/epochs.ts +72 -1
- package/src/managers/index.ts +38 -1
- package/src/managers/influence.test.ts +56 -0
- package/src/managers/influence.ts +425 -0
- package/src/managers/jobs.test.ts +80 -0
- package/src/managers/jobs.ts +54 -0
- package/src/managers/locations.ts +25 -29
- package/src/managers/nft.test.ts +21 -0
- package/src/managers/nft.ts +110 -0
- package/src/managers/players.ts +25 -0
- package/src/managers/plot.ts +122 -0
- package/src/nft/atomicassets.abi.json +1342 -0
- package/src/nft/atomicassets.ts +237 -0
- package/src/nft/atomicdata.ts +130 -0
- package/src/nft/buildImmutableData.ts +388 -0
- package/src/nft/description.ts +222 -100
- package/src/nft/index.ts +3 -0
- package/src/planner/index.ts +296 -0
- package/src/planner/planner.test.ts +459 -0
- package/src/receipts.ts +22 -0
- package/src/resolution/describe-module.ts +43 -25
- package/src/resolution/display-name.ts +39 -11
- package/src/resolution/resolve-item.test.ts +48 -0
- package/src/resolution/resolve-item.ts +112 -58
- package/src/scan/index.ts +244 -0
- package/src/scan/scan-wasm.base64.ts +2 -0
- package/src/scheduling/accessor.ts +65 -23
- package/src/scheduling/availability.test.ts +320 -0
- package/src/scheduling/availability.ts +251 -0
- package/src/scheduling/cancel.test.ts +550 -0
- package/src/scheduling/cancel.ts +254 -0
- package/src/scheduling/cluster-stock.test.ts +31 -0
- package/src/scheduling/cluster-stock.ts +15 -0
- package/src/scheduling/energy.ts +47 -0
- package/src/scheduling/epoch.ts +1 -0
- package/src/scheduling/idle-resolve.ts +45 -0
- package/src/scheduling/jobs.test.ts +49 -0
- package/src/scheduling/jobs.ts +137 -0
- package/src/scheduling/lane-core.ts +128 -0
- package/src/scheduling/lanes.test.ts +250 -0
- package/src/scheduling/lanes.ts +227 -0
- package/src/scheduling/projection.ts +265 -149
- package/src/scheduling/schedule.test.ts +163 -0
- package/src/scheduling/schedule.ts +282 -116
- package/src/scheduling/task-cargo.test.ts +110 -0
- package/src/scheduling/task-cargo.ts +62 -0
- package/src/scheduling/task-effects.test.ts +35 -0
- package/src/scheduling/task-effects.ts +34 -0
- package/src/scheduling/unwrap.test.ts +86 -0
- package/src/scheduling/unwrap.ts +190 -0
- package/src/shipload.ts +63 -4
- package/src/subscriptions/manager.cluster.test.ts +51 -0
- package/src/subscriptions/manager.ts +245 -172
- package/src/subscriptions/mappers.ts +5 -8
- package/src/subscriptions/types.ts +28 -3
- package/src/testing/catalog-hash.ts +19 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/projection-parity.ts +167 -0
- package/src/travel/reach.test.ts +86 -0
- package/src/travel/reach.ts +34 -0
- package/src/travel/route-build.test.ts +63 -0
- package/src/travel/route-build.ts +60 -0
- package/src/travel/route-planner.ts +288 -0
- package/src/travel/route-simulator.ts +170 -0
- package/src/travel/travel.ts +190 -123
- package/src/types/capabilities.ts +37 -8
- package/src/types/entity.ts +3 -3
- package/src/types/index.ts +0 -1
- package/src/types.ts +81 -23
- package/src/utils/cargo.test.ts +14 -0
- package/src/utils/cargo.ts +2 -0
- package/src/utils/display-name.ts +70 -0
- package/src/utils/hash.ts +22 -2
- package/src/utils/system.ts +24 -5
- package/src/capabilities/loading.ts +0 -8
- package/src/entities/container.ts +0 -123
- package/src/entities/ship-deploy.ts +0 -295
- package/src/entities/ship.ts +0 -221
- package/src/entities/warehouse.ts +0 -127
- package/src/types/entity-traits.ts +0 -69
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import type {UInt64, UInt32} from '@wharfkit/antelope'
|
|
2
|
+
import {BaseManager} from './base'
|
|
3
|
+
import type {ServerContract} from '../contracts'
|
|
4
|
+
import {PlotManager} from './plot'
|
|
5
|
+
import {getItem} from '../data/catalog'
|
|
6
|
+
import {calc_build_duration} from '../capabilities/crafting'
|
|
7
|
+
import {getLanes, getTasks, isIdle} from '../scheduling/schedule'
|
|
8
|
+
import {HoldKind, TaskType} from '../types'
|
|
9
|
+
import type {
|
|
10
|
+
BuildableTarget,
|
|
11
|
+
FinalizerEntityRef,
|
|
12
|
+
InboundTransfer,
|
|
13
|
+
Reservation,
|
|
14
|
+
ScheduledBuild,
|
|
15
|
+
SourceCargoStack,
|
|
16
|
+
SourceEntityRef,
|
|
17
|
+
} from './construction-types'
|
|
18
|
+
|
|
19
|
+
const CONSTRUCTION_KINDS = new Set<string>(['plot'])
|
|
20
|
+
|
|
21
|
+
export class ConstructionManager extends BaseManager {
|
|
22
|
+
private readonly plot = new PlotManager(this.context)
|
|
23
|
+
|
|
24
|
+
getTarget(
|
|
25
|
+
entity: ServerContract.Types.entity_row,
|
|
26
|
+
cargo: ServerContract.Types.cargo_row[],
|
|
27
|
+
activeTask?: ServerContract.Types.task,
|
|
28
|
+
scheduledBuild?: ScheduledBuild
|
|
29
|
+
): BuildableTarget | null {
|
|
30
|
+
const kind = entity.kind.toString()
|
|
31
|
+
if (kind === 'plot') {
|
|
32
|
+
return this.plot.buildableTarget(entity, cargo, activeTask, scheduledBuild)
|
|
33
|
+
}
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
canAbandon(
|
|
38
|
+
entity: ServerContract.Types.entity_row,
|
|
39
|
+
cargo: ServerContract.Types.cargo_row[]
|
|
40
|
+
): boolean {
|
|
41
|
+
if (entity.kind.toString() !== 'plot') return false
|
|
42
|
+
if (!isIdle(entity)) return false
|
|
43
|
+
if (Number(entity.cargomass) !== 0) return false
|
|
44
|
+
return !cargo.some((c) => c.entity_id.equals(entity.id))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
eligibleSources(
|
|
48
|
+
target: BuildableTarget,
|
|
49
|
+
entities: ServerContract.Types.entity_info[],
|
|
50
|
+
cargo: ServerContract.Types.cargo_row[]
|
|
51
|
+
): SourceEntityRef[] {
|
|
52
|
+
return partitionSources(target, entities, cargo).eligible
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
unreachableSources(
|
|
56
|
+
target: BuildableTarget,
|
|
57
|
+
entities: ServerContract.Types.entity_info[],
|
|
58
|
+
cargo: ServerContract.Types.cargo_row[]
|
|
59
|
+
): SourceEntityRef[] {
|
|
60
|
+
return partitionSources(target, entities, cargo).unreachable
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
partitionSources(
|
|
64
|
+
target: BuildableTarget,
|
|
65
|
+
entities: ServerContract.Types.entity_info[],
|
|
66
|
+
cargo: ServerContract.Types.cargo_row[]
|
|
67
|
+
): {eligible: SourceEntityRef[]; unreachable: SourceEntityRef[]} {
|
|
68
|
+
return partitionSources(target, entities, cargo)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
eligibleFinalizers(
|
|
72
|
+
target: BuildableTarget,
|
|
73
|
+
entities: ServerContract.Types.entity_info[]
|
|
74
|
+
): FinalizerEntityRef[] {
|
|
75
|
+
const out: FinalizerEntityRef[] = []
|
|
76
|
+
for (const entity of entities) {
|
|
77
|
+
if (!entity.owner.equals(target.ownerName)) continue
|
|
78
|
+
if (entity.id.equals(target.entityId)) continue
|
|
79
|
+
if (!coordsEqual(entity.coordinates, target.coordinates)) continue
|
|
80
|
+
const builderLanes = entity.builder_lanes ?? []
|
|
81
|
+
if (builderLanes.length === 0) continue
|
|
82
|
+
const speed = builderLanes.reduce((s, l) => s + Number(l.speed), 0)
|
|
83
|
+
out.push({
|
|
84
|
+
entityId: entity.id,
|
|
85
|
+
entityType: entity.type,
|
|
86
|
+
name: entity.entity_name,
|
|
87
|
+
capability: 'builder',
|
|
88
|
+
builderSpeed: speed,
|
|
89
|
+
estimatedDuration: this.estimateFinalizeDuration(target, speed),
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
return out.sort((a, b) => a.estimatedDuration.value - b.estimatedDuration.value)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
inboundTransfersTo(
|
|
96
|
+
plotId: UInt64,
|
|
97
|
+
entities: ServerContract.Types.entity_info[],
|
|
98
|
+
now: Date
|
|
99
|
+
): InboundTransfer[] {
|
|
100
|
+
return this.inboundTransfersByTarget(entities, now).get(plotId.toString()) ?? []
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
inboundTransfersByTarget(
|
|
104
|
+
entities: ServerContract.Types.entity_info[],
|
|
105
|
+
now: Date
|
|
106
|
+
): Map<string, InboundTransfer[]> {
|
|
107
|
+
const buckets = new Map<string, Map<string, InboundTransfer>>()
|
|
108
|
+
const entitiesById = new Map(entities.map((entity) => [entity.id.toString(), entity]))
|
|
109
|
+
const nowMs = now.getTime()
|
|
110
|
+
for (const entity of entities) {
|
|
111
|
+
for (const lane of getLanes(entity)) {
|
|
112
|
+
const startedMs = lane.schedule.started.toDate().getTime()
|
|
113
|
+
let cumulativeSec = 0
|
|
114
|
+
for (const task of lane.schedule.tasks) {
|
|
115
|
+
cumulativeSec += task.duration.toNumber()
|
|
116
|
+
if (!isDeliveryTask(task)) continue
|
|
117
|
+
const target = deliveryTarget(task)
|
|
118
|
+
if (!target) continue
|
|
119
|
+
const source = deliverySource(task, entity, entitiesById)
|
|
120
|
+
if (!source) continue
|
|
121
|
+
const projectedEndMs = startedMs + cumulativeSec * 1000
|
|
122
|
+
if (projectedEndMs < nowMs) continue
|
|
123
|
+
const targetIdStr = target.entity_id.toString()
|
|
124
|
+
const sourceIdStr = source.id.toString()
|
|
125
|
+
const sourceName = source.entity_name || sourceIdStr
|
|
126
|
+
const etaSeconds = Math.max(0, Math.round((projectedEndMs - nowMs) / 1000))
|
|
127
|
+
let perTarget = buckets.get(targetIdStr)
|
|
128
|
+
if (!perTarget) {
|
|
129
|
+
perTarget = new Map()
|
|
130
|
+
buckets.set(targetIdStr, perTarget)
|
|
131
|
+
}
|
|
132
|
+
for (const c of task.cargo) {
|
|
133
|
+
const itemId = c.item_id.toNumber()
|
|
134
|
+
const quantity = c.quantity.toNumber()
|
|
135
|
+
if (quantity === 0) continue
|
|
136
|
+
const key = `${sourceIdStr}#${itemId}`
|
|
137
|
+
const existing = perTarget.get(key)
|
|
138
|
+
if (existing) {
|
|
139
|
+
existing.quantity += quantity
|
|
140
|
+
existing.etaSeconds = Math.min(existing.etaSeconds, etaSeconds)
|
|
141
|
+
} else {
|
|
142
|
+
perTarget.set(key, {
|
|
143
|
+
sourceEntityId: source.id,
|
|
144
|
+
sourceEntityType: source.type,
|
|
145
|
+
sourceName,
|
|
146
|
+
itemId,
|
|
147
|
+
quantity,
|
|
148
|
+
etaSeconds,
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const out = new Map<string, InboundTransfer[]>()
|
|
156
|
+
for (const [targetId, perTarget] of buckets) {
|
|
157
|
+
out.set(targetId, Array.from(perTarget.values()))
|
|
158
|
+
}
|
|
159
|
+
return out
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private plotReservation(
|
|
163
|
+
plot: ServerContract.Types.entity_info,
|
|
164
|
+
builder: ServerContract.Types.entity_info | undefined,
|
|
165
|
+
now: Date
|
|
166
|
+
): {
|
|
167
|
+
builderId: UInt64
|
|
168
|
+
startsAt: number
|
|
169
|
+
completesAt: number
|
|
170
|
+
hasStarted: boolean
|
|
171
|
+
} | null {
|
|
172
|
+
const hold = plot.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
|
|
173
|
+
if (!hold) return null
|
|
174
|
+
const builderId = hold.counterpart.entity_id
|
|
175
|
+
const completesAt = hold.until.toDate().getTime()
|
|
176
|
+
const startsAt = this.builderBuildStart(builder, plot.id) ?? completesAt
|
|
177
|
+
return {
|
|
178
|
+
builderId,
|
|
179
|
+
startsAt,
|
|
180
|
+
completesAt,
|
|
181
|
+
hasStarted: startsAt <= now.getTime(),
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private builderBuildStart(
|
|
186
|
+
builder: ServerContract.Types.entity_info | undefined,
|
|
187
|
+
plotId: UInt64
|
|
188
|
+
): number | undefined {
|
|
189
|
+
if (!builder) return undefined
|
|
190
|
+
for (const lane of getLanes(builder)) {
|
|
191
|
+
const startedMs = lane.schedule.started.toDate().getTime()
|
|
192
|
+
let startSec = 0
|
|
193
|
+
for (const task of lane.schedule.tasks) {
|
|
194
|
+
if (isBuildOfPlot(task, plotId)) return startedMs + startSec * 1000
|
|
195
|
+
startSec += task.duration.toNumber()
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return undefined
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private builderCancelability(
|
|
202
|
+
builder: ServerContract.Types.entity_info | undefined,
|
|
203
|
+
plotId: UInt64
|
|
204
|
+
): {cancelable: boolean; blockingTaskCount: number} {
|
|
205
|
+
if (!builder) {
|
|
206
|
+
return {cancelable: false, blockingTaskCount: 0}
|
|
207
|
+
}
|
|
208
|
+
for (const lane of getLanes(builder)) {
|
|
209
|
+
const tasks = lane.schedule.tasks
|
|
210
|
+
const buildIdx = tasks.findIndex((t) => isBuildOfPlot(t, plotId))
|
|
211
|
+
if (buildIdx < 0) continue
|
|
212
|
+
const trailing = tasks.length - 1 - buildIdx
|
|
213
|
+
return {cancelable: trailing === 0, blockingTaskCount: trailing}
|
|
214
|
+
}
|
|
215
|
+
return {cancelable: false, blockingTaskCount: 0}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private buildFromReservation(
|
|
219
|
+
res: {
|
|
220
|
+
builderId: UInt64
|
|
221
|
+
startsAt: number
|
|
222
|
+
completesAt: number
|
|
223
|
+
hasStarted: boolean
|
|
224
|
+
},
|
|
225
|
+
plotId: UInt64,
|
|
226
|
+
builder: ServerContract.Types.entity_info | undefined
|
|
227
|
+
): ScheduledBuild {
|
|
228
|
+
const {cancelable, blockingTaskCount} = this.builderCancelability(builder, plotId)
|
|
229
|
+
return {
|
|
230
|
+
shipId: res.builderId,
|
|
231
|
+
shipName: builder?.entity_name || res.builderId.toString(),
|
|
232
|
+
hasStarted: res.hasStarted,
|
|
233
|
+
startsAt: res.startsAt,
|
|
234
|
+
completesAt: res.completesAt,
|
|
235
|
+
cancelable,
|
|
236
|
+
blockingTaskCount,
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
scheduledBuildFor(
|
|
241
|
+
plot: ServerContract.Types.entity_info,
|
|
242
|
+
entities: ServerContract.Types.entity_info[],
|
|
243
|
+
now: Date
|
|
244
|
+
): ScheduledBuild | null {
|
|
245
|
+
const hold = plot.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
|
|
246
|
+
if (!hold) return null
|
|
247
|
+
const builder = entities.find((e) => e.id.equals(hold.counterpart.entity_id))
|
|
248
|
+
const res = this.plotReservation(plot, builder, now)
|
|
249
|
+
if (!res) return null
|
|
250
|
+
return this.buildFromReservation(res, plot.id, builder)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
scheduledBuildsByTarget(
|
|
254
|
+
entities: ServerContract.Types.entity_info[],
|
|
255
|
+
now: Date
|
|
256
|
+
): Map<string, ScheduledBuild> {
|
|
257
|
+
const byId = new Map(entities.map((e) => [e.id.toString(), e]))
|
|
258
|
+
const out = new Map<string, ScheduledBuild>()
|
|
259
|
+
for (const entity of entities) {
|
|
260
|
+
if (entity.type.toString() !== 'plot') continue
|
|
261
|
+
const hold = entity.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
|
|
262
|
+
if (!hold) continue
|
|
263
|
+
const builder = byId.get(hold.counterpart.entity_id.toString())
|
|
264
|
+
const res = this.plotReservation(entity, builder, now)
|
|
265
|
+
if (!res) continue
|
|
266
|
+
out.set(entity.id.toString(), this.buildFromReservation(res, entity.id, builder))
|
|
267
|
+
}
|
|
268
|
+
return out
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
reservationsFrom(
|
|
272
|
+
sourceEntityId: UInt64,
|
|
273
|
+
entities: ServerContract.Types.entity_info[]
|
|
274
|
+
): Reservation[] {
|
|
275
|
+
const source = entities.find((e) => e.id.equals(sourceEntityId))
|
|
276
|
+
if (!source) return []
|
|
277
|
+
return reservationsOf(source)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
estimateFinalizeDuration(target: BuildableTarget, builderSpeed: number): UInt32 {
|
|
281
|
+
return calc_build_duration(builderSpeed, target.progress.massRequired)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
static isConstructionKind(kind: string): boolean {
|
|
285
|
+
return CONSTRUCTION_KINDS.has(kind)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function coordsEqual(
|
|
290
|
+
a: ServerContract.Types.coordinates,
|
|
291
|
+
b: ServerContract.Types.coordinates
|
|
292
|
+
): boolean {
|
|
293
|
+
return a.x.equals(b.x) && a.y.equals(b.y)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function moduleKey(module: ServerContract.Types.module_entry): string {
|
|
297
|
+
const installed = module.installed
|
|
298
|
+
if (!installed) return `${module.type.toNumber()}:empty`
|
|
299
|
+
|
|
300
|
+
return `${module.type.toNumber()}:${installed.item_id.toNumber()}:${installed.stats.toString()}`
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function sourceStackKey(cargo: ServerContract.Types.cargo_row): string {
|
|
304
|
+
return `${cargo.item_id.toNumber()}#${cargo.stats.toString()}#${(cargo.modules ?? [])
|
|
305
|
+
.map(moduleKey)
|
|
306
|
+
.join(',')}`
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function matchRelevantCargo(
|
|
310
|
+
entity: ServerContract.Types.entity_info,
|
|
311
|
+
target: BuildableTarget,
|
|
312
|
+
cargo: ServerContract.Types.cargo_row[],
|
|
313
|
+
reservedByItem: Map<number, number>
|
|
314
|
+
): SourceCargoStack[] {
|
|
315
|
+
const needsByItemId = new Map(
|
|
316
|
+
target.progress.rows.filter((row) => row.missing > 0).map((row) => [row.itemId, row])
|
|
317
|
+
)
|
|
318
|
+
const remainingReserved = new Map(reservedByItem)
|
|
319
|
+
const out: SourceCargoStack[] = []
|
|
320
|
+
for (const c of cargo) {
|
|
321
|
+
if (!c.entity_id.equals(entity.id)) continue
|
|
322
|
+
const itemId = c.item_id.toNumber()
|
|
323
|
+
const need = needsByItemId.get(itemId)
|
|
324
|
+
if (!need) continue
|
|
325
|
+
const gross = c.quantity.toNumber()
|
|
326
|
+
if (gross === 0) continue
|
|
327
|
+
const reservedRemaining = remainingReserved.get(itemId) ?? 0
|
|
328
|
+
const reserved = Math.min(gross, reservedRemaining)
|
|
329
|
+
const available = gross - reserved
|
|
330
|
+
if (reserved > 0) {
|
|
331
|
+
remainingReserved.set(itemId, reservedRemaining - reserved)
|
|
332
|
+
}
|
|
333
|
+
if (available === 0) continue
|
|
334
|
+
out.push({
|
|
335
|
+
key: sourceStackKey(c),
|
|
336
|
+
rowId: c.id,
|
|
337
|
+
itemId,
|
|
338
|
+
item: getItem(itemId),
|
|
339
|
+
stats: c.stats,
|
|
340
|
+
modules: c.modules ?? [],
|
|
341
|
+
available,
|
|
342
|
+
plotNeeds: need.missing,
|
|
343
|
+
reserved,
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
return out
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function partitionSources(
|
|
350
|
+
target: BuildableTarget,
|
|
351
|
+
entities: ServerContract.Types.entity_info[],
|
|
352
|
+
cargo: ServerContract.Types.cargo_row[]
|
|
353
|
+
): {eligible: SourceEntityRef[]; unreachable: SourceEntityRef[]} {
|
|
354
|
+
const eligible: SourceEntityRef[] = []
|
|
355
|
+
const unreachable: SourceEntityRef[] = []
|
|
356
|
+
for (const entity of entities) {
|
|
357
|
+
if (!entity.owner.equals(target.ownerName)) continue
|
|
358
|
+
if (entity.id.equals(target.entityId)) continue
|
|
359
|
+
if (!coordsEqual(entity.coordinates, target.coordinates)) continue
|
|
360
|
+
const reserved = reservedByItemFor(entity)
|
|
361
|
+
const relevant = matchRelevantCargo(entity, target, cargo, reserved)
|
|
362
|
+
if (relevant.length === 0) continue
|
|
363
|
+
const loaderLanes = entity.loader_lanes ?? []
|
|
364
|
+
const loaderCount = loaderLanes.length
|
|
365
|
+
const loaderTotalMass = loaderLanes.reduce((s, l) => s + Number(l.mass), 0)
|
|
366
|
+
const ref: SourceEntityRef = {
|
|
367
|
+
entityId: entity.id,
|
|
368
|
+
name: entity.id.toString(),
|
|
369
|
+
hasLoaders: loaderCount > 0,
|
|
370
|
+
loaderCount,
|
|
371
|
+
loaderTotalMass,
|
|
372
|
+
relevantCargo: relevant,
|
|
373
|
+
}
|
|
374
|
+
if (ref.hasLoaders) eligible.push(ref)
|
|
375
|
+
else unreachable.push(ref)
|
|
376
|
+
}
|
|
377
|
+
return {eligible, unreachable}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function isDeliveryTask(task: ServerContract.Types.task): boolean {
|
|
381
|
+
const type = task.type.toNumber()
|
|
382
|
+
return type === TaskType.UNLOAD || type === TaskType.SHUTTLE
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function deliveryTarget(
|
|
386
|
+
task: ServerContract.Types.task
|
|
387
|
+
): ServerContract.Types.entity_ref | undefined {
|
|
388
|
+
if (task.type.toNumber() === TaskType.SHUTTLE) {
|
|
389
|
+
return task.couplings.find((coupling) => coupling.kind.toNumber() === HoldKind.PUSH)
|
|
390
|
+
?.counterpart
|
|
391
|
+
}
|
|
392
|
+
return task.couplings[0]?.counterpart
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function deliverySource(
|
|
396
|
+
task: ServerContract.Types.task,
|
|
397
|
+
transporter: ServerContract.Types.entity_info,
|
|
398
|
+
entitiesById: ReadonlyMap<string, ServerContract.Types.entity_info>
|
|
399
|
+
): ServerContract.Types.entity_info | undefined {
|
|
400
|
+
if (task.type.toNumber() !== TaskType.SHUTTLE) return transporter
|
|
401
|
+
const source = task.couplings.find(
|
|
402
|
+
(coupling) => coupling.kind.toNumber() === HoldKind.PULL
|
|
403
|
+
)?.counterpart
|
|
404
|
+
return source ? entitiesById.get(source.entity_id.toString()) : undefined
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function isReservingPushTask(task: ServerContract.Types.task): boolean {
|
|
408
|
+
return task.type.toNumber() === TaskType.UNLOAD
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function isBuildOfPlot(task: ServerContract.Types.task, plotId: UInt64): boolean {
|
|
412
|
+
return (
|
|
413
|
+
task.type.toNumber() === TaskType.BUILDPLOT &&
|
|
414
|
+
task.couplings.length > 0 &&
|
|
415
|
+
task.couplings[0].counterpart.entity_id.equals(plotId)
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function reservationsOf(source: ServerContract.Types.entity_info): Reservation[] {
|
|
420
|
+
const out = new Map<string, Reservation>()
|
|
421
|
+
for (const task of getTasks(source)) {
|
|
422
|
+
if (!isReservingPushTask(task)) continue
|
|
423
|
+
if (task.couplings.length === 0) continue
|
|
424
|
+
const targetType = task.couplings[0].counterpart.entity_type
|
|
425
|
+
const targetId = task.couplings[0].counterpart.entity_id
|
|
426
|
+
for (const c of task.cargo) {
|
|
427
|
+
const itemId = c.item_id.toNumber()
|
|
428
|
+
const quantity = c.quantity.toNumber()
|
|
429
|
+
if (quantity === 0) continue
|
|
430
|
+
const key = `${targetId.toString()}#${itemId}`
|
|
431
|
+
const existing = out.get(key)
|
|
432
|
+
if (existing) {
|
|
433
|
+
existing.quantity += quantity
|
|
434
|
+
} else {
|
|
435
|
+
out.set(key, {
|
|
436
|
+
targetEntityId: targetId,
|
|
437
|
+
targetEntityType: targetType,
|
|
438
|
+
itemId,
|
|
439
|
+
quantity,
|
|
440
|
+
})
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return Array.from(out.values())
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function reservedByItemFor(source: ServerContract.Types.entity_info): Map<number, number> {
|
|
448
|
+
const out = new Map<number, number>()
|
|
449
|
+
for (const r of reservationsOf(source)) {
|
|
450
|
+
out.set(r.itemId, (out.get(r.itemId) ?? 0) + r.quantity)
|
|
451
|
+
}
|
|
452
|
+
return out
|
|
453
|
+
}
|
package/src/managers/context.ts
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import type {APIClient} from '@wharfkit/antelope'
|
|
2
2
|
import type {Contract} from '@wharfkit/contract'
|
|
3
3
|
import type {PlatformContract} from '../contracts'
|
|
4
|
+
import {FundContract} from '../contracts'
|
|
4
5
|
import {GameState} from '../entities/gamestate'
|
|
5
6
|
|
|
6
7
|
import {EntitiesManager} from './entities'
|
|
7
8
|
import {PlayersManager} from './players'
|
|
8
9
|
import {LocationsManager} from './locations'
|
|
10
|
+
import {CoordinatesManager} from './coordinates'
|
|
9
11
|
import {EpochsManager} from './epochs'
|
|
10
12
|
import {ActionsManager} from './actions'
|
|
13
|
+
import {ClusterManager} from './cluster'
|
|
14
|
+
import {NftManager} from './nft'
|
|
15
|
+
import {BalancesManager} from './balances'
|
|
16
|
+
import {JobsManager} from './jobs'
|
|
17
|
+
import {InfluenceManager} from './influence'
|
|
11
18
|
import {SubscriptionsManager} from '../subscriptions/manager'
|
|
12
19
|
|
|
13
20
|
export class GameContext {
|
|
14
21
|
private _entities?: EntitiesManager
|
|
15
22
|
private _players?: PlayersManager
|
|
16
23
|
private _locations?: LocationsManager
|
|
24
|
+
private _coordinates?: CoordinatesManager
|
|
17
25
|
private _epochs?: EpochsManager
|
|
18
26
|
private _actions?: ActionsManager
|
|
27
|
+
private _clusters?: ClusterManager
|
|
28
|
+
private _nft?: NftManager
|
|
29
|
+
private _balances?: BalancesManager
|
|
30
|
+
private _jobs?: JobsManager
|
|
31
|
+
private _influence?: InfluenceManager
|
|
19
32
|
private _subscriptions?: SubscriptionsManager
|
|
20
33
|
private _subscriptionsUrl?: string
|
|
21
34
|
|
|
@@ -25,7 +38,9 @@ export class GameContext {
|
|
|
25
38
|
constructor(
|
|
26
39
|
public readonly client: APIClient,
|
|
27
40
|
public readonly server: Contract,
|
|
28
|
-
public readonly platform: Contract
|
|
41
|
+
public readonly platform: Contract,
|
|
42
|
+
public readonly atomicAssetsAccount: string = 'atomicassets',
|
|
43
|
+
public readonly fund: Contract = new FundContract.Contract({client})
|
|
29
44
|
) {}
|
|
30
45
|
|
|
31
46
|
get entities(): EntitiesManager {
|
|
@@ -49,6 +64,13 @@ export class GameContext {
|
|
|
49
64
|
return this._locations
|
|
50
65
|
}
|
|
51
66
|
|
|
67
|
+
get coordinates(): CoordinatesManager {
|
|
68
|
+
if (!this._coordinates) {
|
|
69
|
+
this._coordinates = new CoordinatesManager(this)
|
|
70
|
+
}
|
|
71
|
+
return this._coordinates
|
|
72
|
+
}
|
|
73
|
+
|
|
52
74
|
get epochs(): EpochsManager {
|
|
53
75
|
if (!this._epochs) {
|
|
54
76
|
this._epochs = new EpochsManager(this)
|
|
@@ -63,6 +85,41 @@ export class GameContext {
|
|
|
63
85
|
return this._actions
|
|
64
86
|
}
|
|
65
87
|
|
|
88
|
+
get clusters(): ClusterManager {
|
|
89
|
+
if (!this._clusters) {
|
|
90
|
+
this._clusters = new ClusterManager(this)
|
|
91
|
+
}
|
|
92
|
+
return this._clusters
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get nft(): NftManager {
|
|
96
|
+
if (!this._nft) {
|
|
97
|
+
this._nft = new NftManager(this)
|
|
98
|
+
}
|
|
99
|
+
return this._nft
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get balances(): BalancesManager {
|
|
103
|
+
if (!this._balances) {
|
|
104
|
+
this._balances = new BalancesManager(this)
|
|
105
|
+
}
|
|
106
|
+
return this._balances
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get jobs(): JobsManager {
|
|
110
|
+
if (!this._jobs) {
|
|
111
|
+
this._jobs = new JobsManager(this)
|
|
112
|
+
}
|
|
113
|
+
return this._jobs
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get influence(): InfluenceManager {
|
|
117
|
+
if (!this._influence) {
|
|
118
|
+
this._influence = new InfluenceManager(this)
|
|
119
|
+
}
|
|
120
|
+
return this._influence
|
|
121
|
+
}
|
|
122
|
+
|
|
66
123
|
setSubscriptionsUrl(url: string) {
|
|
67
124
|
this._subscriptionsUrl = url
|
|
68
125
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {BaseManager} from './base'
|
|
2
|
+
import {type CoordinateAddress, decodeAddress, encodeAddressMemo} from '../coordinates'
|
|
3
|
+
|
|
4
|
+
export class CoordinatesManager extends BaseManager {
|
|
5
|
+
async encode(x: number, y: number): Promise<CoordinateAddress> {
|
|
6
|
+
const game = await this.getGame()
|
|
7
|
+
return encodeAddressMemo(game.config.seed, x, y)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async decode(addr: CoordinateAddress): Promise<{x: number; y: number}> {
|
|
11
|
+
const game = await this.getGame()
|
|
12
|
+
return decodeAddress(game.config.seed, addr)
|
|
13
|
+
}
|
|
14
|
+
}
|