@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.
Files changed (203) hide show
  1. package/lib/scan.d.ts +52 -0
  2. package/lib/scan.js +162 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +152 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +5696 -1503
  7. package/lib/shipload.js +20294 -5200
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +20371 -5683
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1648 -0
  12. package/lib/testing.js +6980 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +6974 -0
  15. package/lib/testing.m.js.map +1 -0
  16. package/package.json +20 -2
  17. package/src/capabilities/craftable.test.ts +82 -0
  18. package/src/capabilities/craftable.ts +82 -0
  19. package/src/capabilities/crafting.test.ts +80 -0
  20. package/src/capabilities/crafting.ts +50 -8
  21. package/src/capabilities/gathering.test.ts +29 -0
  22. package/src/capabilities/gathering.ts +38 -20
  23. package/src/capabilities/hauling.ts +0 -5
  24. package/src/capabilities/index.ts +0 -1
  25. package/src/capabilities/modules.ts +27 -30
  26. package/src/capabilities/movement.ts +1 -1
  27. package/src/capabilities/storage.test.ts +26 -0
  28. package/src/capabilities/storage.ts +22 -7
  29. package/src/contracts/fund.ts +306 -0
  30. package/src/contracts/platform.ts +312 -3
  31. package/src/contracts/server.ts +2661 -466
  32. package/src/contracts/token.ts +170 -0
  33. package/src/contracts.ts +2 -0
  34. package/src/coordinates/address.ts +88 -0
  35. package/src/coordinates/constants.test.ts +15 -0
  36. package/src/coordinates/constants.ts +23 -0
  37. package/src/coordinates/index.ts +15 -0
  38. package/src/coordinates/memo.test.ts +47 -0
  39. package/src/coordinates/memo.ts +20 -0
  40. package/src/coordinates/permutation.ts +77 -0
  41. package/src/coordinates/regions.ts +48 -0
  42. package/src/coordinates/sectors.ts +115 -0
  43. package/src/data/capabilities.ts +49 -18
  44. package/src/data/capability-formulas.ts +26 -14
  45. package/src/data/catalog.test.ts +19 -0
  46. package/src/data/catalog.ts +13 -3
  47. package/src/data/charters.json +396 -0
  48. package/src/data/citizenry-adjectives.json +42 -0
  49. package/src/data/citizenry-nouns.json +50 -0
  50. package/src/data/colors.ts +14 -32
  51. package/src/data/entities.json +421 -15
  52. package/src/data/item-ids.ts +53 -13
  53. package/src/data/items.json +330 -77
  54. package/src/data/kind-registry.json +212 -0
  55. package/src/data/kind-registry.ts +192 -0
  56. package/src/data/metadata.ts +312 -51
  57. package/src/data/recipes-runtime.ts +6 -23
  58. package/src/data/recipes.json +2173 -173
  59. package/src/data/tiers.ts +9 -6
  60. package/src/derivation/build-methods.test.ts +71 -0
  61. package/src/derivation/build-methods.ts +50 -0
  62. package/src/derivation/capabilities.test.ts +348 -0
  63. package/src/derivation/capabilities.ts +707 -0
  64. package/src/derivation/capability-mappings.ts +60 -16
  65. package/src/derivation/crafting.test.ts +17 -0
  66. package/src/derivation/crafting.ts +57 -34
  67. package/src/derivation/index.ts +27 -2
  68. package/src/derivation/recipe-usage.test.ts +88 -0
  69. package/src/derivation/recipe-usage.ts +141 -0
  70. package/src/derivation/reserve-regen.ts +34 -0
  71. package/src/derivation/resources.ts +9 -1
  72. package/src/derivation/rollups.test.ts +55 -0
  73. package/src/derivation/rollups.ts +72 -0
  74. package/src/derivation/stars.test.ts +67 -0
  75. package/src/derivation/stars.ts +25 -0
  76. package/src/derivation/stat-scaling.ts +12 -0
  77. package/src/derivation/stats.ts +6 -6
  78. package/src/derivation/strata.ts +5 -3
  79. package/src/derivation/stratum.ts +26 -22
  80. package/src/derivation/tiers.ts +40 -7
  81. package/src/derivation/upgrades.ts +14 -0
  82. package/src/derivation/wormhole.ts +154 -0
  83. package/src/entities/entity.ts +102 -0
  84. package/src/entities/gamestate.ts +3 -28
  85. package/src/entities/makers.ts +144 -136
  86. package/src/entities/slot-multiplier.ts +43 -0
  87. package/src/entity/hydrate.test.ts +156 -0
  88. package/src/entity/hydrate.ts +86 -0
  89. package/src/errors.ts +21 -16
  90. package/src/format.ts +42 -9
  91. package/src/index-module.ts +411 -32
  92. package/src/influence/ballot.test.ts +103 -0
  93. package/src/influence/ballot.ts +98 -0
  94. package/src/influence/categories.ts +32 -0
  95. package/src/influence/charter-metadata.test.ts +136 -0
  96. package/src/influence/charter-metadata.ts +236 -0
  97. package/src/influence/charters.test.ts +112 -0
  98. package/src/influence/charters.ts +150 -0
  99. package/src/influence/citizenry.ts +38 -0
  100. package/src/influence/civic-shuttle.test.ts +202 -0
  101. package/src/influence/civic-shuttle.ts +142 -0
  102. package/src/influence/civic.test.ts +287 -0
  103. package/src/influence/civic.ts +269 -0
  104. package/src/influence/constants.ts +84 -0
  105. package/src/influence/decay.ts +20 -0
  106. package/src/influence/decomp.ts +95 -0
  107. package/src/influence/demand.ts +106 -0
  108. package/src/influence/duration.ts +101 -0
  109. package/src/influence/index.ts +118 -0
  110. package/src/influence/influence.test.ts +450 -0
  111. package/src/influence/quality.ts +25 -0
  112. package/src/influence/valuation.ts +154 -0
  113. package/src/managers/actions.ts +940 -93
  114. package/src/managers/balances.test.ts +161 -0
  115. package/src/managers/balances.ts +93 -0
  116. package/src/managers/base.ts +10 -2
  117. package/src/managers/cluster.test.ts +39 -0
  118. package/src/managers/cluster.ts +53 -0
  119. package/src/managers/construction-types.ts +80 -0
  120. package/src/managers/construction.ts +453 -0
  121. package/src/managers/context.ts +58 -1
  122. package/src/managers/coordinates.ts +14 -0
  123. package/src/managers/entities.ts +35 -63
  124. package/src/managers/epochs.ts +72 -1
  125. package/src/managers/index.ts +38 -1
  126. package/src/managers/influence.test.ts +56 -0
  127. package/src/managers/influence.ts +425 -0
  128. package/src/managers/jobs.test.ts +80 -0
  129. package/src/managers/jobs.ts +54 -0
  130. package/src/managers/locations.ts +25 -29
  131. package/src/managers/nft.test.ts +21 -0
  132. package/src/managers/nft.ts +110 -0
  133. package/src/managers/players.ts +25 -0
  134. package/src/managers/plot.ts +122 -0
  135. package/src/nft/atomicassets.abi.json +1342 -0
  136. package/src/nft/atomicassets.ts +237 -0
  137. package/src/nft/atomicdata.ts +130 -0
  138. package/src/nft/buildImmutableData.ts +388 -0
  139. package/src/nft/description.ts +222 -100
  140. package/src/nft/index.ts +3 -0
  141. package/src/planner/index.ts +296 -0
  142. package/src/planner/planner.test.ts +459 -0
  143. package/src/receipts.ts +22 -0
  144. package/src/resolution/describe-module.ts +43 -25
  145. package/src/resolution/display-name.ts +39 -11
  146. package/src/resolution/resolve-item.test.ts +48 -0
  147. package/src/resolution/resolve-item.ts +112 -58
  148. package/src/scan/index.ts +244 -0
  149. package/src/scan/scan-wasm.base64.ts +2 -0
  150. package/src/scheduling/accessor.ts +65 -23
  151. package/src/scheduling/availability.test.ts +320 -0
  152. package/src/scheduling/availability.ts +251 -0
  153. package/src/scheduling/cancel.test.ts +550 -0
  154. package/src/scheduling/cancel.ts +254 -0
  155. package/src/scheduling/cluster-stock.test.ts +31 -0
  156. package/src/scheduling/cluster-stock.ts +15 -0
  157. package/src/scheduling/energy.ts +47 -0
  158. package/src/scheduling/epoch.ts +1 -0
  159. package/src/scheduling/idle-resolve.ts +45 -0
  160. package/src/scheduling/jobs.test.ts +49 -0
  161. package/src/scheduling/jobs.ts +137 -0
  162. package/src/scheduling/lane-core.ts +128 -0
  163. package/src/scheduling/lanes.test.ts +250 -0
  164. package/src/scheduling/lanes.ts +227 -0
  165. package/src/scheduling/projection.ts +265 -149
  166. package/src/scheduling/schedule.test.ts +163 -0
  167. package/src/scheduling/schedule.ts +282 -116
  168. package/src/scheduling/task-cargo.test.ts +110 -0
  169. package/src/scheduling/task-cargo.ts +62 -0
  170. package/src/scheduling/task-effects.test.ts +35 -0
  171. package/src/scheduling/task-effects.ts +34 -0
  172. package/src/scheduling/unwrap.test.ts +86 -0
  173. package/src/scheduling/unwrap.ts +190 -0
  174. package/src/shipload.ts +63 -4
  175. package/src/subscriptions/manager.cluster.test.ts +51 -0
  176. package/src/subscriptions/manager.ts +245 -172
  177. package/src/subscriptions/mappers.ts +5 -8
  178. package/src/subscriptions/types.ts +28 -3
  179. package/src/testing/catalog-hash.ts +19 -0
  180. package/src/testing/index.ts +2 -0
  181. package/src/testing/projection-parity.ts +167 -0
  182. package/src/travel/reach.test.ts +86 -0
  183. package/src/travel/reach.ts +34 -0
  184. package/src/travel/route-build.test.ts +63 -0
  185. package/src/travel/route-build.ts +60 -0
  186. package/src/travel/route-planner.ts +288 -0
  187. package/src/travel/route-simulator.ts +170 -0
  188. package/src/travel/travel.ts +190 -123
  189. package/src/types/capabilities.ts +37 -8
  190. package/src/types/entity.ts +3 -3
  191. package/src/types/index.ts +0 -1
  192. package/src/types.ts +81 -23
  193. package/src/utils/cargo.test.ts +14 -0
  194. package/src/utils/cargo.ts +2 -0
  195. package/src/utils/display-name.ts +70 -0
  196. package/src/utils/hash.ts +22 -2
  197. package/src/utils/system.ts +24 -5
  198. package/src/capabilities/loading.ts +0 -8
  199. package/src/entities/container.ts +0 -123
  200. package/src/entities/ship-deploy.ts +0 -295
  201. package/src/entities/ship.ts +0 -221
  202. package/src/entities/warehouse.ts +0 -127
  203. 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
+ }
@@ -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
+ }