@shipload/sdk 1.0.0-next.4 → 1.0.0-next.41

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.
Files changed (134) hide show
  1. package/lib/scan.d.ts +34 -0
  2. package/lib/scan.js +136 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +129 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +2473 -973
  7. package/lib/shipload.js +11529 -5211
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +11338 -5162
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +970 -0
  12. package/lib/testing.js +4013 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4007 -0
  15. package/lib/testing.m.js.map +1 -0
  16. package/package.json +20 -2
  17. package/src/capabilities/craftable.ts +51 -0
  18. package/src/capabilities/crafting.test.ts +7 -0
  19. package/src/capabilities/crafting.ts +5 -6
  20. package/src/capabilities/gathering.test.ts +16 -0
  21. package/src/capabilities/gathering.ts +35 -18
  22. package/src/capabilities/index.ts +0 -1
  23. package/src/capabilities/modules.ts +9 -0
  24. package/src/capabilities/storage.ts +16 -1
  25. package/src/contracts/platform.ts +231 -3
  26. package/src/contracts/server.ts +1021 -481
  27. package/src/coordinates/address.ts +88 -0
  28. package/src/coordinates/constants.test.ts +15 -0
  29. package/src/coordinates/constants.ts +23 -0
  30. package/src/coordinates/index.ts +15 -0
  31. package/src/coordinates/memo.test.ts +47 -0
  32. package/src/coordinates/memo.ts +20 -0
  33. package/src/coordinates/permutation.ts +77 -0
  34. package/src/coordinates/regions.ts +48 -0
  35. package/src/coordinates/sectors.ts +115 -0
  36. package/src/data/capabilities.ts +12 -5
  37. package/src/data/capability-formulas.ts +14 -7
  38. package/src/data/catalog.ts +0 -5
  39. package/src/data/colors.ts +14 -47
  40. package/src/data/entities.json +76 -10
  41. package/src/data/item-ids.ts +18 -12
  42. package/src/data/items.json +321 -38
  43. package/src/data/kind-registry.json +109 -0
  44. package/src/data/kind-registry.ts +165 -0
  45. package/src/data/metadata.ts +119 -33
  46. package/src/data/recipes-runtime.ts +3 -23
  47. package/src/data/recipes.json +238 -117
  48. package/src/derivation/build-methods.ts +45 -0
  49. package/src/derivation/capabilities.test.ts +151 -0
  50. package/src/derivation/capabilities.ts +512 -0
  51. package/src/derivation/capability-mappings.ts +9 -12
  52. package/src/derivation/crafting.ts +23 -24
  53. package/src/derivation/index.ts +25 -2
  54. package/src/derivation/recipe-usage.test.ts +78 -0
  55. package/src/derivation/recipe-usage.ts +141 -0
  56. package/src/derivation/reserve-regen.ts +34 -0
  57. package/src/derivation/resources.ts +125 -38
  58. package/src/derivation/rollups.test.ts +55 -0
  59. package/src/derivation/rollups.ts +56 -0
  60. package/src/derivation/stars.test.ts +51 -0
  61. package/src/derivation/stars.ts +15 -0
  62. package/src/derivation/stats.ts +6 -6
  63. package/src/derivation/stratum.ts +17 -20
  64. package/src/derivation/tiers.ts +40 -7
  65. package/src/derivation/wormhole.ts +136 -0
  66. package/src/entities/entity.ts +98 -0
  67. package/src/entities/gamestate.ts +3 -28
  68. package/src/entities/makers.ts +124 -134
  69. package/src/entities/slot-multiplier.ts +43 -0
  70. package/src/errors.ts +12 -16
  71. package/src/format.ts +26 -4
  72. package/src/index-module.ts +267 -47
  73. package/src/managers/actions.ts +528 -95
  74. package/src/managers/base.ts +6 -2
  75. package/src/managers/construction-types.ts +80 -0
  76. package/src/managers/construction.ts +412 -0
  77. package/src/managers/context.ts +20 -1
  78. package/src/managers/coordinates.ts +14 -0
  79. package/src/managers/entities.ts +18 -66
  80. package/src/managers/epochs.ts +40 -0
  81. package/src/managers/index.ts +17 -1
  82. package/src/managers/locations.ts +25 -29
  83. package/src/managers/nft.test.ts +14 -0
  84. package/src/managers/nft.ts +70 -0
  85. package/src/managers/plot.ts +122 -0
  86. package/src/nft/atomicassets.abi.json +1342 -0
  87. package/src/nft/atomicassets.ts +237 -0
  88. package/src/nft/atomicdata.ts +130 -0
  89. package/src/nft/buildImmutableData.ts +338 -0
  90. package/src/nft/description.ts +98 -24
  91. package/src/nft/index.ts +3 -0
  92. package/src/planner/index.ts +127 -0
  93. package/src/planner/planner.test.ts +319 -0
  94. package/src/resolution/describe-module.ts +18 -13
  95. package/src/resolution/display-name.ts +38 -10
  96. package/src/resolution/resolve-item.test.ts +37 -0
  97. package/src/resolution/resolve-item.ts +55 -24
  98. package/src/scan/index.ts +180 -0
  99. package/src/scan/scan-wasm.base64.ts +2 -0
  100. package/src/scheduling/accessor.ts +68 -22
  101. package/src/scheduling/availability.ts +108 -0
  102. package/src/scheduling/cancel.test.ts +348 -0
  103. package/src/scheduling/cancel.ts +209 -0
  104. package/src/scheduling/energy.ts +47 -0
  105. package/src/scheduling/idle-resolve.ts +45 -0
  106. package/src/scheduling/lane-core.ts +128 -0
  107. package/src/scheduling/lanes.test.ts +249 -0
  108. package/src/scheduling/lanes.ts +198 -0
  109. package/src/scheduling/projection.ts +209 -105
  110. package/src/scheduling/schedule.ts +241 -104
  111. package/src/scheduling/task-cargo.ts +46 -0
  112. package/src/shipload.ts +21 -1
  113. package/src/subscriptions/manager.ts +229 -142
  114. package/src/subscriptions/mappers.ts +5 -8
  115. package/src/subscriptions/types.ts +11 -3
  116. package/src/testing/catalog-hash.ts +19 -0
  117. package/src/testing/index.ts +2 -0
  118. package/src/testing/projection-parity.ts +167 -0
  119. package/src/travel/reach.ts +23 -0
  120. package/src/travel/route-planner.ts +196 -0
  121. package/src/travel/travel.ts +200 -112
  122. package/src/types/capabilities.ts +29 -6
  123. package/src/types/entity.ts +3 -3
  124. package/src/types/index.ts +0 -1
  125. package/src/types.ts +28 -13
  126. package/src/utils/cargo.ts +27 -0
  127. package/src/utils/display-name.ts +70 -0
  128. package/src/utils/system.ts +36 -24
  129. package/src/capabilities/loading.ts +0 -8
  130. package/src/entities/container.ts +0 -108
  131. package/src/entities/ship-deploy.ts +0 -259
  132. package/src/entities/ship.ts +0 -204
  133. package/src/entities/warehouse.ts +0 -119
  134. package/src/types/entity-traits.ts +0 -69
@@ -15,11 +15,15 @@ export abstract class BaseManager {
15
15
  return this.context.platform
16
16
  }
17
17
 
18
+ protected get atomicAssetsAccount() {
19
+ return this.context.atomicAssetsAccount
20
+ }
21
+
18
22
  protected async getGame() {
19
23
  return this.context.getGame()
20
24
  }
21
25
 
22
- protected async getState() {
23
- return this.context.getState()
26
+ protected async getState(reload = false) {
27
+ return this.context.getState(reload)
24
28
  }
25
29
  }
@@ -0,0 +1,80 @@
1
+ import type {Name, UInt32, UInt64} from '@wharfkit/antelope'
2
+ import type {ServerContract} from '../contracts'
3
+ import type {Item} from '../types'
4
+ import type {Recipe} from '../data/recipes-runtime'
5
+ import type {PlotProgress} from './plot'
6
+
7
+ export type BuildState = 'initializing' | 'accepting' | 'ready' | 'scheduled' | 'finalizing'
8
+
9
+ export type FinalizerCapability = 'crafter'
10
+
11
+ export interface BuildableTarget {
12
+ entityId: UInt64
13
+ ownerName: Name
14
+ coordinates: ServerContract.Types.coordinates
15
+ targetItemId: number
16
+ targetItem: Item
17
+ state: BuildState
18
+ recipe: Recipe
19
+ progress: PlotProgress
20
+ finalizeAction: Name
21
+ finalizerCapability: FinalizerCapability
22
+ activeTask?: ServerContract.Types.task
23
+ scheduledBuild?: ScheduledBuild
24
+ }
25
+
26
+ export interface SourceEntityRef {
27
+ entityId: UInt64
28
+ name: string
29
+ hasLoaders: boolean
30
+ loaderCount: number
31
+ loaderTotalMass: number
32
+ relevantCargo: SourceCargoStack[]
33
+ }
34
+
35
+ export interface SourceCargoStack {
36
+ key: string
37
+ rowId: UInt64
38
+ itemId: number
39
+ item: Item
40
+ stats: UInt64
41
+ modules: ServerContract.Types.module_entry[]
42
+ available: number
43
+ plotNeeds: number
44
+ reserved: number
45
+ }
46
+
47
+ export interface FinalizerEntityRef {
48
+ entityId: UInt64
49
+ entityType: Name
50
+ name: string
51
+ capability: FinalizerCapability
52
+ crafterSpeed: number
53
+ estimatedDuration: UInt32
54
+ }
55
+
56
+ export interface InboundTransfer {
57
+ sourceEntityId: UInt64
58
+ sourceEntityType: Name
59
+ sourceName: string
60
+ itemId: number
61
+ quantity: number
62
+ etaSeconds: number
63
+ }
64
+
65
+ export interface ScheduledBuild {
66
+ shipId: UInt64
67
+ shipName: string
68
+ hasStarted: boolean
69
+ startsAt: number
70
+ completesAt: number
71
+ cancelable: boolean
72
+ blockingTaskCount: number
73
+ }
74
+
75
+ export interface Reservation {
76
+ targetEntityId: UInt64
77
+ targetEntityType: Name
78
+ itemId: number
79
+ quantity: number
80
+ }
@@ -0,0 +1,412 @@
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_craft_duration} from '../capabilities/crafting'
7
+ import {getLanes, getTasks} 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
+ eligibleSources(
38
+ target: BuildableTarget,
39
+ entities: ServerContract.Types.entity_info[],
40
+ cargo: ServerContract.Types.cargo_row[]
41
+ ): SourceEntityRef[] {
42
+ return partitionSources(target, entities, cargo).eligible
43
+ }
44
+
45
+ unreachableSources(
46
+ target: BuildableTarget,
47
+ entities: ServerContract.Types.entity_info[],
48
+ cargo: ServerContract.Types.cargo_row[]
49
+ ): SourceEntityRef[] {
50
+ return partitionSources(target, entities, cargo).unreachable
51
+ }
52
+
53
+ partitionSources(
54
+ target: BuildableTarget,
55
+ entities: ServerContract.Types.entity_info[],
56
+ cargo: ServerContract.Types.cargo_row[]
57
+ ): {eligible: SourceEntityRef[]; unreachable: SourceEntityRef[]} {
58
+ return partitionSources(target, entities, cargo)
59
+ }
60
+
61
+ eligibleFinalizers(
62
+ target: BuildableTarget,
63
+ entities: ServerContract.Types.entity_info[]
64
+ ): FinalizerEntityRef[] {
65
+ const out: FinalizerEntityRef[] = []
66
+ for (const entity of entities) {
67
+ if (!entity.owner.equals(target.ownerName)) continue
68
+ if (entity.id.equals(target.entityId)) continue
69
+ if (!coordsEqual(entity.coordinates, target.coordinates)) continue
70
+ const crafterLanes = entity.crafter_lanes ?? []
71
+ if (crafterLanes.length === 0) continue
72
+ const speed = crafterLanes.reduce((s, l) => s + Number(l.speed), 0)
73
+ out.push({
74
+ entityId: entity.id,
75
+ entityType: entity.type,
76
+ name: entity.entity_name,
77
+ capability: 'crafter',
78
+ crafterSpeed: speed,
79
+ estimatedDuration: this.estimateFinalizeDuration(target, speed),
80
+ })
81
+ }
82
+ return out.sort((a, b) => a.estimatedDuration.value - b.estimatedDuration.value)
83
+ }
84
+
85
+ inboundTransfersTo(
86
+ plotId: UInt64,
87
+ entities: ServerContract.Types.entity_info[],
88
+ now: Date
89
+ ): InboundTransfer[] {
90
+ return this.inboundTransfersByTarget(entities, now).get(plotId.toString()) ?? []
91
+ }
92
+
93
+ inboundTransfersByTarget(
94
+ entities: ServerContract.Types.entity_info[],
95
+ now: Date
96
+ ): Map<string, InboundTransfer[]> {
97
+ const buckets = new Map<string, Map<string, InboundTransfer>>()
98
+ const nowMs = now.getTime()
99
+ for (const entity of entities) {
100
+ const entityIdStr = entity.id.toString()
101
+ const sourceName = entity.entity_name || entityIdStr
102
+ for (const lane of getLanes(entity)) {
103
+ const startedMs = lane.schedule.started.toDate().getTime()
104
+ let cumulativeSec = 0
105
+ for (const task of lane.schedule.tasks) {
106
+ cumulativeSec += task.duration.toNumber()
107
+ if (!isPushTask(task)) continue
108
+ if (!task.entitytarget) continue
109
+ const projectedEndMs = startedMs + cumulativeSec * 1000
110
+ if (projectedEndMs < nowMs) continue
111
+ const targetIdStr = task.entitytarget.entity_id.toString()
112
+ const etaSeconds = Math.max(0, Math.round((projectedEndMs - nowMs) / 1000))
113
+ let perTarget = buckets.get(targetIdStr)
114
+ if (!perTarget) {
115
+ perTarget = new Map()
116
+ buckets.set(targetIdStr, perTarget)
117
+ }
118
+ for (const c of task.cargo) {
119
+ const itemId = c.item_id.toNumber()
120
+ const quantity = c.quantity.toNumber()
121
+ if (quantity === 0) continue
122
+ const key = `${entityIdStr}#${itemId}`
123
+ const existing = perTarget.get(key)
124
+ if (existing) {
125
+ existing.quantity += quantity
126
+ existing.etaSeconds = Math.min(existing.etaSeconds, etaSeconds)
127
+ } else {
128
+ perTarget.set(key, {
129
+ sourceEntityId: entity.id,
130
+ sourceEntityType: entity.type,
131
+ sourceName,
132
+ itemId,
133
+ quantity,
134
+ etaSeconds,
135
+ })
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ const out = new Map<string, InboundTransfer[]>()
142
+ for (const [targetId, perTarget] of buckets) {
143
+ out.set(targetId, Array.from(perTarget.values()))
144
+ }
145
+ return out
146
+ }
147
+
148
+ private plotReservation(
149
+ plot: ServerContract.Types.entity_info,
150
+ builder: ServerContract.Types.entity_info | undefined,
151
+ now: Date
152
+ ): {
153
+ builderId: UInt64
154
+ startsAt: number
155
+ completesAt: number
156
+ hasStarted: boolean
157
+ } | null {
158
+ const hold = plot.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
159
+ if (!hold) return null
160
+ const builderId = hold.counterpart.entity_id
161
+ const completesAt = hold.until.toDate().getTime()
162
+ const startsAt = this.builderBuildStart(builder, plot.id) ?? completesAt
163
+ return {
164
+ builderId,
165
+ startsAt,
166
+ completesAt,
167
+ hasStarted: startsAt <= now.getTime(),
168
+ }
169
+ }
170
+
171
+ private builderBuildStart(
172
+ builder: ServerContract.Types.entity_info | undefined,
173
+ plotId: UInt64
174
+ ): number | undefined {
175
+ if (!builder) return undefined
176
+ for (const lane of getLanes(builder)) {
177
+ const startedMs = lane.schedule.started.toDate().getTime()
178
+ let startSec = 0
179
+ for (const task of lane.schedule.tasks) {
180
+ if (isBuildOfPlot(task, plotId)) return startedMs + startSec * 1000
181
+ startSec += task.duration.toNumber()
182
+ }
183
+ }
184
+ return undefined
185
+ }
186
+
187
+ private builderCancelability(
188
+ builder: ServerContract.Types.entity_info | undefined,
189
+ plotId: UInt64
190
+ ): {cancelable: boolean; blockingTaskCount: number} {
191
+ if (!builder) {
192
+ return {cancelable: false, blockingTaskCount: 0}
193
+ }
194
+ for (const lane of getLanes(builder)) {
195
+ const tasks = lane.schedule.tasks
196
+ const buildIdx = tasks.findIndex((t) => isBuildOfPlot(t, plotId))
197
+ if (buildIdx < 0) continue
198
+ const trailing = tasks.length - 1 - buildIdx
199
+ return {cancelable: trailing === 0, blockingTaskCount: trailing}
200
+ }
201
+ return {cancelable: false, blockingTaskCount: 0}
202
+ }
203
+
204
+ private buildFromReservation(
205
+ res: {
206
+ builderId: UInt64
207
+ startsAt: number
208
+ completesAt: number
209
+ hasStarted: boolean
210
+ },
211
+ plotId: UInt64,
212
+ builder: ServerContract.Types.entity_info | undefined
213
+ ): ScheduledBuild {
214
+ const {cancelable, blockingTaskCount} = this.builderCancelability(builder, plotId)
215
+ return {
216
+ shipId: res.builderId,
217
+ shipName: builder?.entity_name || res.builderId.toString(),
218
+ hasStarted: res.hasStarted,
219
+ startsAt: res.startsAt,
220
+ completesAt: res.completesAt,
221
+ cancelable,
222
+ blockingTaskCount,
223
+ }
224
+ }
225
+
226
+ scheduledBuildFor(
227
+ plot: ServerContract.Types.entity_info,
228
+ entities: ServerContract.Types.entity_info[],
229
+ now: Date
230
+ ): ScheduledBuild | null {
231
+ const hold = plot.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
232
+ if (!hold) return null
233
+ const builder = entities.find((e) => e.id.equals(hold.counterpart.entity_id))
234
+ const res = this.plotReservation(plot, builder, now)
235
+ if (!res) return null
236
+ return this.buildFromReservation(res, plot.id, builder)
237
+ }
238
+
239
+ scheduledBuildsByTarget(
240
+ entities: ServerContract.Types.entity_info[],
241
+ now: Date
242
+ ): Map<string, ScheduledBuild> {
243
+ const byId = new Map(entities.map((e) => [e.id.toString(), e]))
244
+ const out = new Map<string, ScheduledBuild>()
245
+ for (const entity of entities) {
246
+ if (entity.type.toString() !== 'plot') continue
247
+ const hold = entity.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
248
+ if (!hold) continue
249
+ const builder = byId.get(hold.counterpart.entity_id.toString())
250
+ const res = this.plotReservation(entity, builder, now)
251
+ if (!res) continue
252
+ out.set(entity.id.toString(), this.buildFromReservation(res, entity.id, builder))
253
+ }
254
+ return out
255
+ }
256
+
257
+ reservationsFrom(
258
+ sourceEntityId: UInt64,
259
+ entities: ServerContract.Types.entity_info[]
260
+ ): Reservation[] {
261
+ const source = entities.find((e) => e.id.equals(sourceEntityId))
262
+ if (!source) return []
263
+ return reservationsOf(source)
264
+ }
265
+
266
+ estimateFinalizeDuration(target: BuildableTarget, crafterSpeed: number): UInt32 {
267
+ return calc_craft_duration(crafterSpeed, target.progress.massRequired)
268
+ }
269
+
270
+ static isConstructionKind(kind: string): boolean {
271
+ return CONSTRUCTION_KINDS.has(kind)
272
+ }
273
+ }
274
+
275
+ function coordsEqual(
276
+ a: ServerContract.Types.coordinates,
277
+ b: ServerContract.Types.coordinates
278
+ ): boolean {
279
+ return a.x.equals(b.x) && a.y.equals(b.y)
280
+ }
281
+
282
+ function moduleKey(module: ServerContract.Types.module_entry): string {
283
+ const installed = module.installed
284
+ if (!installed) return `${module.type.toNumber()}:empty`
285
+
286
+ return `${module.type.toNumber()}:${installed.item_id.toNumber()}:${installed.stats.toString()}`
287
+ }
288
+
289
+ function sourceStackKey(cargo: ServerContract.Types.cargo_row): string {
290
+ return `${cargo.item_id.toNumber()}#${cargo.stats.toString()}#${(cargo.modules ?? [])
291
+ .map(moduleKey)
292
+ .join(',')}`
293
+ }
294
+
295
+ function matchRelevantCargo(
296
+ entity: ServerContract.Types.entity_info,
297
+ target: BuildableTarget,
298
+ cargo: ServerContract.Types.cargo_row[],
299
+ reservedByItem: Map<number, number>
300
+ ): SourceCargoStack[] {
301
+ const needsByItemId = new Map(
302
+ target.progress.rows.filter((row) => row.missing > 0).map((row) => [row.itemId, row])
303
+ )
304
+ const remainingReserved = new Map(reservedByItem)
305
+ const out: SourceCargoStack[] = []
306
+ for (const c of cargo) {
307
+ if (!c.entity_id.equals(entity.id)) continue
308
+ const itemId = c.item_id.toNumber()
309
+ const need = needsByItemId.get(itemId)
310
+ if (!need) continue
311
+ const gross = c.quantity.toNumber()
312
+ if (gross === 0) continue
313
+ const reservedRemaining = remainingReserved.get(itemId) ?? 0
314
+ const reserved = Math.min(gross, reservedRemaining)
315
+ const available = gross - reserved
316
+ if (reserved > 0) {
317
+ remainingReserved.set(itemId, reservedRemaining - reserved)
318
+ }
319
+ if (available === 0) continue
320
+ out.push({
321
+ key: sourceStackKey(c),
322
+ rowId: c.id,
323
+ itemId,
324
+ item: getItem(itemId),
325
+ stats: c.stats,
326
+ modules: c.modules ?? [],
327
+ available,
328
+ plotNeeds: need.missing,
329
+ reserved,
330
+ })
331
+ }
332
+ return out
333
+ }
334
+
335
+ function partitionSources(
336
+ target: BuildableTarget,
337
+ entities: ServerContract.Types.entity_info[],
338
+ cargo: ServerContract.Types.cargo_row[]
339
+ ): {eligible: SourceEntityRef[]; unreachable: SourceEntityRef[]} {
340
+ const eligible: SourceEntityRef[] = []
341
+ const unreachable: SourceEntityRef[] = []
342
+ for (const entity of entities) {
343
+ if (!entity.owner.equals(target.ownerName)) continue
344
+ if (entity.id.equals(target.entityId)) continue
345
+ if (!coordsEqual(entity.coordinates, target.coordinates)) continue
346
+ const reserved = reservedByItemFor(entity)
347
+ const relevant = matchRelevantCargo(entity, target, cargo, reserved)
348
+ if (relevant.length === 0) continue
349
+ const loaderLanes = entity.loader_lanes ?? []
350
+ const loaderCount = loaderLanes.length
351
+ const loaderTotalMass = loaderLanes.reduce((s, l) => s + Number(l.mass), 0)
352
+ const ref: SourceEntityRef = {
353
+ entityId: entity.id,
354
+ name: entity.id.toString(),
355
+ hasLoaders: loaderCount > 0,
356
+ loaderCount,
357
+ loaderTotalMass,
358
+ relevantCargo: relevant,
359
+ }
360
+ if (ref.hasLoaders) eligible.push(ref)
361
+ else unreachable.push(ref)
362
+ }
363
+ return {eligible, unreachable}
364
+ }
365
+
366
+ function isPushTask(task: ServerContract.Types.task): boolean {
367
+ return task.type.toNumber() === TaskType.UNLOAD
368
+ }
369
+
370
+ function isBuildOfPlot(task: ServerContract.Types.task, plotId: UInt64): boolean {
371
+ return (
372
+ task.type.toNumber() === TaskType.BUILDPLOT &&
373
+ task.entitytarget !== undefined &&
374
+ task.entitytarget.entity_id.equals(plotId)
375
+ )
376
+ }
377
+
378
+ function reservationsOf(source: ServerContract.Types.entity_info): Reservation[] {
379
+ const out = new Map<string, Reservation>()
380
+ for (const task of getTasks(source)) {
381
+ if (!isPushTask(task)) continue
382
+ if (!task.entitytarget) continue
383
+ const targetType = task.entitytarget.entity_type
384
+ const targetId = task.entitytarget.entity_id
385
+ for (const c of task.cargo) {
386
+ const itemId = c.item_id.toNumber()
387
+ const quantity = c.quantity.toNumber()
388
+ if (quantity === 0) continue
389
+ const key = `${targetId.toString()}#${itemId}`
390
+ const existing = out.get(key)
391
+ if (existing) {
392
+ existing.quantity += quantity
393
+ } else {
394
+ out.set(key, {
395
+ targetEntityId: targetId,
396
+ targetEntityType: targetType,
397
+ itemId,
398
+ quantity,
399
+ })
400
+ }
401
+ }
402
+ }
403
+ return Array.from(out.values())
404
+ }
405
+
406
+ function reservedByItemFor(source: ServerContract.Types.entity_info): Map<number, number> {
407
+ const out = new Map<number, number>()
408
+ for (const r of reservationsOf(source)) {
409
+ out.set(r.itemId, (out.get(r.itemId) ?? 0) + r.quantity)
410
+ }
411
+ return out
412
+ }
@@ -6,16 +6,20 @@ import {GameState} from '../entities/gamestate'
6
6
  import {EntitiesManager} from './entities'
7
7
  import {PlayersManager} from './players'
8
8
  import {LocationsManager} from './locations'
9
+ import {CoordinatesManager} from './coordinates'
9
10
  import {EpochsManager} from './epochs'
10
11
  import {ActionsManager} from './actions'
12
+ import {NftManager} from './nft'
11
13
  import {SubscriptionsManager} from '../subscriptions/manager'
12
14
 
13
15
  export class GameContext {
14
16
  private _entities?: EntitiesManager
15
17
  private _players?: PlayersManager
16
18
  private _locations?: LocationsManager
19
+ private _coordinates?: CoordinatesManager
17
20
  private _epochs?: EpochsManager
18
21
  private _actions?: ActionsManager
22
+ private _nft?: NftManager
19
23
  private _subscriptions?: SubscriptionsManager
20
24
  private _subscriptionsUrl?: string
21
25
 
@@ -25,7 +29,8 @@ export class GameContext {
25
29
  constructor(
26
30
  public readonly client: APIClient,
27
31
  public readonly server: Contract,
28
- public readonly platform: Contract
32
+ public readonly platform: Contract,
33
+ public readonly atomicAssetsAccount: string = 'atomicassets'
29
34
  ) {}
30
35
 
31
36
  get entities(): EntitiesManager {
@@ -49,6 +54,13 @@ export class GameContext {
49
54
  return this._locations
50
55
  }
51
56
 
57
+ get coordinates(): CoordinatesManager {
58
+ if (!this._coordinates) {
59
+ this._coordinates = new CoordinatesManager(this)
60
+ }
61
+ return this._coordinates
62
+ }
63
+
52
64
  get epochs(): EpochsManager {
53
65
  if (!this._epochs) {
54
66
  this._epochs = new EpochsManager(this)
@@ -63,6 +75,13 @@ export class GameContext {
63
75
  return this._actions
64
76
  }
65
77
 
78
+ get nft(): NftManager {
79
+ if (!this._nft) {
80
+ this._nft = new NftManager(this)
81
+ }
82
+ return this._nft
83
+ }
84
+
66
85
  setSubscriptionsUrl(url: string) {
67
86
  this._subscriptionsUrl = url
68
87
  }
@@ -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
+ }
@@ -1,99 +1,51 @@
1
1
  import {Name, type NameType, type UInt64Type} from '@wharfkit/antelope'
2
2
  import {BaseManager} from './base'
3
- import {Ship} from '../entities/ship'
4
- import {Warehouse} from '../entities/warehouse'
5
- import {Container} from '../entities/container'
3
+ import {Entity} from '../entities/entity'
4
+ import type {EntityTypeName} from '../data/kind-registry'
6
5
  import type {ServerContract} from '../contracts'
7
6
 
8
- export type EntityType = 'ship' | 'warehouse' | 'container' | 'location'
7
+ export type {EntityTypeName} from '../data/kind-registry'
9
8
 
10
9
  export class EntitiesManager extends BaseManager {
11
- async getEntity(type: EntityType, id: UInt64Type): Promise<Ship | Warehouse | Container> {
10
+ async getEntity(id: UInt64Type): Promise<Entity> {
12
11
  const result = await this.server.readonly('getentity', {
13
- entity_type: Name.from(type),
14
12
  entity_id: id,
15
13
  })
16
- const entityInfo = result as ServerContract.Types.entity_info
17
- return this.wrapEntity(entityInfo)
14
+ return new Entity(result as ServerContract.Types.entity_info)
15
+ }
16
+
17
+ async getProjection(id: UInt64Type, taskCount?: number): Promise<unknown> {
18
+ return this.server.readonly('getprojstate', {
19
+ entity_id: id,
20
+ task_count: taskCount,
21
+ })
18
22
  }
19
23
 
20
24
  async getEntities(
21
25
  owner: NameType | ServerContract.Types.player_row,
22
- type?: EntityType
23
- ): Promise<(Ship | Warehouse | Container)[]> {
26
+ kind?: EntityTypeName
27
+ ): Promise<Entity[]> {
24
28
  const ownerName = this.resolveOwner(owner)
25
29
  const result = await this.server.readonly('getentities', {
26
30
  owner: ownerName,
27
- entity_type: type ? Name.from(type) : null,
31
+ entity_type: kind,
28
32
  })
29
33
  const entities = result as ServerContract.Types.entity_info[]
30
- return entities.map((entity) => this.wrapEntity(entity))
34
+ return entities.map((e) => new Entity(e))
31
35
  }
32
36
 
33
37
  async getSummaries(
34
38
  owner: NameType | ServerContract.Types.player_row,
35
- type?: EntityType
39
+ kind?: EntityTypeName
36
40
  ): Promise<ServerContract.Types.entity_summary[]> {
37
41
  const ownerName = this.resolveOwner(owner)
38
42
  const result = await this.server.readonly('getsummaries', {
39
43
  owner: ownerName,
40
- entity_type: type ? Name.from(type) : null,
44
+ entity_type: kind,
41
45
  })
42
46
  return result as ServerContract.Types.entity_summary[]
43
47
  }
44
48
 
45
- async getShip(id: UInt64Type): Promise<Ship> {
46
- return (await this.getEntity('ship', id)) as Ship
47
- }
48
-
49
- async getWarehouse(id: UInt64Type): Promise<Warehouse> {
50
- return (await this.getEntity('warehouse', id)) as Warehouse
51
- }
52
-
53
- async getContainer(id: UInt64Type): Promise<Container> {
54
- return (await this.getEntity('container', id)) as Container
55
- }
56
-
57
- async getShips(owner: NameType | ServerContract.Types.player_row): Promise<Ship[]> {
58
- return (await this.getEntities(owner, 'ship')) as Ship[]
59
- }
60
-
61
- async getWarehouses(owner: NameType | ServerContract.Types.player_row): Promise<Warehouse[]> {
62
- return (await this.getEntities(owner, 'warehouse')) as Warehouse[]
63
- }
64
-
65
- async getContainers(owner: NameType | ServerContract.Types.player_row): Promise<Container[]> {
66
- return (await this.getEntities(owner, 'container')) as Container[]
67
- }
68
-
69
- async getShipSummaries(
70
- owner: NameType | ServerContract.Types.player_row
71
- ): Promise<ServerContract.Types.entity_summary[]> {
72
- return this.getSummaries(owner, 'ship')
73
- }
74
-
75
- async getWarehouseSummaries(
76
- owner: NameType | ServerContract.Types.player_row
77
- ): Promise<ServerContract.Types.entity_summary[]> {
78
- return this.getSummaries(owner, 'warehouse')
79
- }
80
-
81
- async getContainerSummaries(
82
- owner: NameType | ServerContract.Types.player_row
83
- ): Promise<ServerContract.Types.entity_summary[]> {
84
- return this.getSummaries(owner, 'container')
85
- }
86
-
87
- private wrapEntity(entity: ServerContract.Types.entity_info): Ship | Warehouse | Container {
88
- if (entity.type.equals('ship')) {
89
- return new Ship(entity)
90
- } else if (entity.type.equals('warehouse')) {
91
- return new Warehouse(entity)
92
- } else {
93
- return new Container(entity)
94
- }
95
- }
96
-
97
49
  private resolveOwner(owner: NameType | ServerContract.Types.player_row): Name {
98
50
  if (typeof owner === 'object' && owner !== null && 'owner' in owner) {
99
51
  return owner.owner