@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,425 @@
1
+ import {Int64, Name, type NameType, UInt64} from '@wharfkit/antelope'
2
+ import {BaseManager} from './base'
3
+ import type {ServerContract} from '../contracts'
4
+ import {coordsToLocationId, locationIdToCoords, type CoordinatesType} from '../types'
5
+ import {
6
+ type BuiltCharter,
7
+ type WorldBuilding,
8
+ citizenryName,
9
+ contributeDuration,
10
+ decayActive,
11
+ deriveDemand,
12
+ pricingFromWeights,
13
+ valueCargoItem,
14
+ type DemandTriple,
15
+ type DemandView,
16
+ isCivicEntity,
17
+ type ValuedItem,
18
+ } from '../influence'
19
+ import {getItem} from '../data/catalog'
20
+
21
+ export interface InfluenceStanding {
22
+ epoch: number
23
+ standingLifetime: bigint
24
+ standingActive: bigint
25
+ citizenshipLifetime: bigint
26
+ citizenshipActive: bigint
27
+ locationLifetime: bigint
28
+ locationActive: bigint
29
+ spent: bigint
30
+ mandate: number
31
+ founder: Name
32
+ founded: number
33
+ }
34
+
35
+ export interface CharterProgress {
36
+ locationId: bigint
37
+ lifetime: bigint
38
+ spent: bigint
39
+ unassigned: bigint
40
+ mandate: number
41
+ nextCost: bigint
42
+ prereqsMet: boolean
43
+ buildable: boolean
44
+ epoch: number
45
+ built: BuiltCharter[]
46
+ ballotId: bigint
47
+ settling: boolean
48
+ queue: QueuedSeat[]
49
+ progress: NodeProgress[]
50
+ }
51
+
52
+ export interface NodeProgress {
53
+ nodeId: number
54
+ amount: bigint
55
+ }
56
+
57
+ export interface ContributePreviewRow {
58
+ itemId: number
59
+ quantity: number
60
+ stats: bigint
61
+ valueAtomic: bigint
62
+ mass: number
63
+ }
64
+
65
+ export interface ContributePreview {
66
+ rows: ContributePreviewRow[]
67
+ totalAtomic: bigint
68
+ totalMass: number
69
+ durationSeconds: number
70
+ demand: DemandView
71
+ }
72
+
73
+ export interface FoundedWorld {
74
+ locationId: bigint
75
+ x: number
76
+ y: number
77
+ lifetime: bigint
78
+ active: bigint
79
+ spent: bigint
80
+ ballotId: bigint
81
+ mandate: number | undefined
82
+ founder: Name
83
+ founded: number
84
+ }
85
+
86
+ export interface FoundedWorldRef {
87
+ x: number
88
+ y: number
89
+ mandate: number
90
+ }
91
+
92
+ export interface VoteOption {
93
+ nodeId: number
94
+ cost: bigint
95
+ seat: number
96
+ weight: bigint
97
+ rank: number
98
+ }
99
+
100
+ export interface VoteStandings {
101
+ locationId: bigint
102
+ ballotId: bigint
103
+ epoch: number
104
+ settledEpoch: number
105
+ seats: number
106
+ settling: boolean
107
+ queue: number[]
108
+ options: VoteOption[]
109
+ picks: number[]
110
+ }
111
+
112
+ export interface BallotVote {
113
+ account: Name
114
+ picks: number[]
115
+ }
116
+
117
+ export interface Ballot {
118
+ ballotId: bigint
119
+ kind: number
120
+ subject: bigint
121
+ seats: number
122
+ settledEpoch: number
123
+ queue: number[]
124
+ settling: boolean
125
+ }
126
+
127
+ export interface QueuedSeat {
128
+ nodeId: number
129
+ cost: bigint
130
+ progress: bigint
131
+ gap: bigint
132
+ }
133
+
134
+ function big(value: unknown): bigint {
135
+ return BigInt(String(value))
136
+ }
137
+
138
+ export class InfluenceManager extends BaseManager {
139
+ async getStanding(owner: NameType, location: CoordinatesType): Promise<InfluenceStanding> {
140
+ const result = (await this.server.readonly('getinfluence', {
141
+ owner: Name.from(owner),
142
+ x: Int64.from(location.x),
143
+ y: Int64.from(location.y),
144
+ })) as ServerContract.Types.influence_totals
145
+
146
+ return {
147
+ epoch: Number(result.epoch),
148
+ standingLifetime: big(result.standing_lifetime),
149
+ standingActive: big(result.standing_active),
150
+ citizenshipLifetime: big(result.citizenship_lifetime),
151
+ citizenshipActive: big(result.citizenship_active),
152
+ locationLifetime: big(result.location_lifetime),
153
+ locationActive: big(result.location_active),
154
+ spent: big(result.spent),
155
+ mandate: Number(result.mandate),
156
+ founder: Name.from(result.founder),
157
+ founded: Number(result.founded),
158
+ }
159
+ }
160
+
161
+ async getDemandConfig(): Promise<DemandTriple> {
162
+ const row = (await this.server.table('infdemand').get()) as
163
+ | ServerContract.Types.infdemand_row
164
+ | undefined
165
+ if (!row) throw new Error('influence demand config is unset')
166
+ return {peak: big(row.peak), base: big(row.base), floor: big(row.floor)}
167
+ }
168
+
169
+ async getQualityDivisor(): Promise<number> {
170
+ const row = (await this.server.table('infquality').get()) as
171
+ | ServerContract.Types.infquality_row
172
+ | undefined
173
+ if (!row) throw new Error('influence quality config is unset')
174
+ return Number(row.d1)
175
+ }
176
+
177
+ async getWeights(): Promise<ServerContract.Types.infweight_row[]> {
178
+ return (await this.server.table('infweight').all()) as ServerContract.Types.infweight_row[]
179
+ }
180
+
181
+ async getDemand(location: CoordinatesType): Promise<DemandView> {
182
+ const game = await this.getGame()
183
+ const state = await this.getState()
184
+ const triple = await this.getDemandConfig()
185
+ return deriveDemand({
186
+ gameSeed: game.config.seed,
187
+ epochSeed: state.epochSeed,
188
+ coordinates: location,
189
+ triple,
190
+ })
191
+ }
192
+
193
+ async getCivicOwner(): Promise<Name> {
194
+ const row = (await this.server.table('civicconfig').get()) as
195
+ | ServerContract.Types.civicconfig_row
196
+ | undefined
197
+ return row ? Name.from(row.civic_owner) : Name.from(this.server.account)
198
+ }
199
+
200
+ async isCivic(entity: {owner: NameType}): Promise<boolean> {
201
+ return isCivicEntity(entity, await this.getCivicOwner())
202
+ }
203
+
204
+ async getCitizenryName(location: CoordinatesType): Promise<string | undefined> {
205
+ const game = await this.getGame()
206
+ return citizenryName(game.config.seed, location)
207
+ }
208
+
209
+ async previewContribution(
210
+ location: CoordinatesType,
211
+ bundle: ValuedItem[],
212
+ opts: {altitudeZ?: number} = {}
213
+ ): Promise<ContributePreview> {
214
+ const [demand, d1, weights] = await Promise.all([
215
+ this.getDemand(location),
216
+ this.getQualityDivisor(),
217
+ this.getWeights(),
218
+ ])
219
+ const pricing = pricingFromWeights(
220
+ d1,
221
+ weights.map((w) => ({
222
+ category: Number(w.category),
223
+ tier: Number(w.tier),
224
+ weightFp: big(w.weight_fp),
225
+ }))
226
+ )
227
+
228
+ let totalAtomic = 0n
229
+ let totalMass = 0
230
+ const rows = bundle.map((item) => {
231
+ const valueAtomic = valueCargoItem(item, demand, pricing)
232
+ const mass = getItem(item.itemId).mass * item.quantity
233
+ totalAtomic += valueAtomic
234
+ totalMass += mass
235
+ return {...item, valueAtomic, mass}
236
+ })
237
+
238
+ return {
239
+ rows,
240
+ totalAtomic,
241
+ totalMass,
242
+ durationSeconds: contributeDuration(totalMass, opts.altitudeZ ?? 0),
243
+ demand,
244
+ }
245
+ }
246
+
247
+ async getCharter(location: CoordinatesType): Promise<CharterProgress> {
248
+ const result = (await this.server.readonly('getcharter', {
249
+ x: Int64.from(location.x),
250
+ y: Int64.from(location.y),
251
+ })) as ServerContract.Types.charter_result
252
+
253
+ return {
254
+ locationId: big(result.location_id),
255
+ lifetime: big(result.lifetime),
256
+ spent: big(result.spent),
257
+ unassigned: big(result.unassigned),
258
+ mandate: Number(result.mandate),
259
+ nextCost: big(result.next_cost),
260
+ prereqsMet: Boolean(result.prereqs_met),
261
+ buildable: Boolean(result.buildable),
262
+ epoch: Number(result.epoch),
263
+ built: result.built.map((b) => ({
264
+ nodeId: Number(b.node_id),
265
+ repeats: Number(b.repeats),
266
+ })),
267
+ ballotId: big(result.ballot_id),
268
+ settling: Boolean(result.settling),
269
+ queue: result.queue.map((seat) => ({
270
+ nodeId: Number(seat.node_id),
271
+ cost: big(seat.cost),
272
+ progress: big(seat.progress),
273
+ gap: big(seat.gap),
274
+ })),
275
+ progress: result.progress.map((p) => ({
276
+ nodeId: Number(p.node_id),
277
+ amount: big(p.amount),
278
+ })),
279
+ }
280
+ }
281
+
282
+ async getPools(): Promise<ServerContract.Types.pool_view[]> {
283
+ const result = (await this.server.readonly('getpools')) as ServerContract.Types.pools_result
284
+ return result.pools
285
+ }
286
+
287
+ async getPool(category: number, tier: number): Promise<ServerContract.Types.pool_pair_result> {
288
+ return (await this.server.readonly('getpool', {
289
+ category,
290
+ tier,
291
+ })) as ServerContract.Types.pool_pair_result
292
+ }
293
+
294
+ async getMintConfig(): Promise<ServerContract.Types.mintcfg_result> {
295
+ return (await this.server.readonly('getmintcfg')) as ServerContract.Types.mintcfg_result
296
+ }
297
+
298
+ async getMintReady(): Promise<number> {
299
+ return Number(await this.server.readonly('getmintready'))
300
+ }
301
+
302
+ async getVoteReady(): Promise<number> {
303
+ return Number(await this.server.readonly('getvoteready'))
304
+ }
305
+
306
+ async getCharterReady(): Promise<FoundedWorldRef[]> {
307
+ const result = (await this.server.readonly(
308
+ 'getchrtready'
309
+ )) as ServerContract.Types.charterready_result
310
+ return result.worlds.map((w) => ({
311
+ x: Number(w.x),
312
+ y: Number(w.y),
313
+ mandate: Number(w.mandate),
314
+ }))
315
+ }
316
+
317
+ async getVotes(location: CoordinatesType, player?: NameType): Promise<VoteStandings> {
318
+ const result = (await this.server.readonly('getvotes', {
319
+ x: Int64.from(location.x),
320
+ y: Int64.from(location.y),
321
+ player: Name.from(player ?? ''),
322
+ })) as ServerContract.Types.vote_result
323
+
324
+ return {
325
+ locationId: big(result.location_id),
326
+ ballotId: big(result.ballot_id),
327
+ epoch: Number(result.epoch),
328
+ settledEpoch: Number(result.settled_epoch),
329
+ seats: Number(result.seats),
330
+ settling: Boolean(result.settling),
331
+ queue: result.queue.map((n) => Number(n)),
332
+ options: result.options.map((option) => ({
333
+ nodeId: Number(option.node_id),
334
+ cost: big(option.cost),
335
+ seat: Number(option.seat),
336
+ weight: big(option.weight),
337
+ rank: Number(option.rank),
338
+ })),
339
+ picks: result.picks.map((n) => Number(n)),
340
+ }
341
+ }
342
+
343
+ async getBallotId(location: CoordinatesType): Promise<bigint | undefined> {
344
+ const row = (await this.server
345
+ .table('worlds')
346
+ .get(UInt64.from(coordsToLocationId(location)))) as
347
+ | ServerContract.Types.worlds_row
348
+ | undefined
349
+ return row ? big(row.ballot_id) : undefined
350
+ }
351
+
352
+ async getBallot(ballotId: bigint): Promise<Ballot | undefined> {
353
+ const row = (await this.server.table('ballot').get(UInt64.from(ballotId))) as
354
+ | ServerContract.Types.ballot_row
355
+ | undefined
356
+ if (!row) return undefined
357
+ const epoch = Number((await this.getState()).epoch)
358
+ const settledEpoch = Number(row.settled_epoch)
359
+ return {
360
+ ballotId: big(row.ballot_id),
361
+ kind: Number(row.kind),
362
+ subject: big(row.subject),
363
+ seats: Number(row.seats),
364
+ settledEpoch,
365
+ queue: row.queue.map((n) => Number(n)),
366
+ settling: settledEpoch < epoch,
367
+ }
368
+ }
369
+
370
+ async getBallotVotes(ballotId: bigint): Promise<BallotVote[]> {
371
+ const rows = (await this.server
372
+ .table('ballotpicks', UInt64.from(ballotId))
373
+ .all()) as ServerContract.Types.ballotpicks_row[]
374
+ return rows.map((row) => ({
375
+ account: Name.from(row.account),
376
+ picks: row.picks.map((n) => Number(n)),
377
+ }))
378
+ }
379
+
380
+ async getBuiltCharters(location: CoordinatesType): Promise<BuiltCharter[]> {
381
+ const rows = (await this.server
382
+ .table('mandates', coordsToLocationId(location))
383
+ .all()) as ServerContract.Types.mandates_row[]
384
+ return rows.map((row) => ({nodeId: Number(row.node_id), repeats: Number(row.repeats)}))
385
+ }
386
+
387
+ async getBuildings(location: CoordinatesType): Promise<WorldBuilding[]> {
388
+ const rows = (await this.server
389
+ .table('buildings', coordsToLocationId(location))
390
+ .all()) as ServerContract.Types.buildings_row[]
391
+ return rows.map((row) => ({entityId: big(row.entity_id), building: Number(row.building)}))
392
+ }
393
+
394
+ async getFoundedWorlds(opts: {withMandate?: boolean} = {}): Promise<FoundedWorld[]> {
395
+ const rows = (await this.server.table('worlds').all()) as ServerContract.Types.worlds_row[]
396
+ const epoch = (await this.getState()).epoch
397
+ const worlds = rows.map((row) => ({
398
+ locationId: big(row.location_id),
399
+ ...locationIdToCoords(row.location_id),
400
+ lifetime: big(row.lifetime),
401
+ active: decayActive(
402
+ big(row.active),
403
+ Math.max(0, Number(epoch) - Number(row.active_epoch))
404
+ ),
405
+ spent: big(row.spent),
406
+ ballotId: big(row.ballot_id),
407
+ mandate: undefined as number | undefined,
408
+ founder: Name.from(row.founder),
409
+ founded: Number(row.founded),
410
+ }))
411
+
412
+ if (!opts.withMandate) return worlds
413
+
414
+ for (const world of worlds) {
415
+ world.mandate = (await this.getCharter({x: world.x, y: world.y})).mandate
416
+ }
417
+ return worlds
418
+ }
419
+
420
+ async isFounded(location: CoordinatesType): Promise<boolean> {
421
+ const id = coordsToLocationId(location)
422
+ const row = await this.server.table('worlds').get(UInt64.from(id))
423
+ return row !== undefined
424
+ }
425
+ }
@@ -0,0 +1,80 @@
1
+ import {describe, expect, it} from 'bun:test'
2
+ import {Name} from '@wharfkit/antelope'
3
+ import {JobsManager} from './jobs'
4
+
5
+ const OWNER = 'eggmaple.gm'
6
+ const row = (over: Record<string, unknown> = {}) => ({
7
+ id: {toNumber: () => 7},
8
+ building: {toNumber: () => 42},
9
+ socket: {toNumber: () => 0},
10
+ owner: Name.from(OWNER),
11
+ ship_id: {toNumber: () => 100},
12
+ coords: {x: {toNumber: () => 12}, y: {toNumber: () => 34}},
13
+ starts_at: {toDate: () => new Date('2026-07-26T10:00:00Z')},
14
+ completes_at: {toDate: () => new Date('2026-07-26T11:00:00Z')},
15
+ recipe_id: {toNumber: () => 10001},
16
+ quantity: {toNumber: () => 5},
17
+ energy_paid: {toNumber: () => 0},
18
+ cargo: [{item: 'out'}],
19
+ ...over,
20
+ })
21
+
22
+ function managerWith(queryImpl: () => Promise<unknown[]>, allImpl: () => Promise<unknown[]>) {
23
+ const table = () => ({query: () => ({all: queryImpl}), all: allImpl})
24
+ const ctx = {server: {table}} as never
25
+ return new JobsManager(ctx)
26
+ }
27
+
28
+ describe('JobsManager.getOwnedJobs', () => {
29
+ it('parses a landed row: cargo is the output, no inputs, status derived', async () => {
30
+ const m = managerWith(
31
+ async () => [row()],
32
+ async () => []
33
+ )
34
+ const jobs = await m.getOwnedJobs(OWNER, {now: new Date('2026-07-26T11:30:00Z')})
35
+ expect(jobs).toHaveLength(1)
36
+ expect(jobs[0]).toMatchObject({id: 7, building: 42, quantity: 5, status: 'ready'})
37
+ expect(jobs[0].coords).toEqual({x: 12, y: 34})
38
+ expect(jobs[0].output).toEqual({item: 'out'} as never)
39
+ expect(jobs[0].inputs).toEqual([] as never)
40
+ })
41
+
42
+ it('reports a job In Line rather than ready, whatever its zero window says', async () => {
43
+ const inLine = row({
44
+ starts_at: {toDate: () => new Date(0)},
45
+ completes_at: {toDate: () => new Date(0)},
46
+ deposited: false,
47
+ cargo: [{item: 'in'}],
48
+ })
49
+ const m = managerWith(
50
+ async () => [inLine],
51
+ async () => []
52
+ )
53
+ const jobs = await m.getOwnedJobs(OWNER, {now: new Date('2026-07-26T11:30:00Z')})
54
+ expect(jobs[0].status).toBe('inline')
55
+ expect(jobs[0].deposited).toBe(false)
56
+ expect(jobs[0].output).toBeNull()
57
+ expect(jobs[0].inputs).toEqual([{item: 'in'}] as never)
58
+ })
59
+
60
+ it('drops rows whose owner does not match (positional-index safety re-filter)', async () => {
61
+ const other = row({owner: Name.from('someoneelse.gm')})
62
+ const m = managerWith(
63
+ async () => [row(), other],
64
+ async () => []
65
+ )
66
+ const jobs = await m.getOwnedJobs(OWNER)
67
+ expect(jobs).toHaveLength(1)
68
+ })
69
+
70
+ it('falls back to a full-scan .all() when the positional query throws', async () => {
71
+ const m = managerWith(
72
+ async () => {
73
+ throw new Error('bad index_position')
74
+ },
75
+ async () => [row()]
76
+ )
77
+ const jobs = await m.getOwnedJobs(OWNER)
78
+ expect(jobs).toHaveLength(1)
79
+ })
80
+ })
@@ -0,0 +1,54 @@
1
+ import {Name, type NameType} from '@wharfkit/antelope'
2
+ import {BaseManager} from './base'
3
+ import {jobDeposited, jobStatus, splitJobCargo, type OwnedJob} from '../scheduling/jobs'
4
+ import type {ServerContract} from '../contracts'
5
+
6
+ type JobRow = ServerContract.Types.craftjob_row
7
+
8
+ export class JobsManager extends BaseManager {
9
+ async getOwnedJobs(owner: NameType, opts?: {now?: Date}): Promise<OwnedJob[]> {
10
+ const ownerName = Name.from(owner)
11
+ const now = opts?.now ?? new Date()
12
+
13
+ let rows: JobRow[]
14
+ try {
15
+ // index_position 'tertiary' = nodeos slot 3 = owner's secondary index; no ABI metadata for it.
16
+ rows = (await this.server
17
+ .table('craftjobs')
18
+ .query({
19
+ index_position: 'tertiary',
20
+ key_type: 'i64',
21
+ from: ownerName.value,
22
+ to: ownerName.value,
23
+ })
24
+ .all()) as JobRow[]
25
+ } catch {
26
+ rows = (await this.server.table('craftjobs').all()) as JobRow[]
27
+ }
28
+
29
+ return rows.filter((r) => ownerName.equals(r.owner)).map((r) => this.parseOwnedJob(r, now))
30
+ }
31
+
32
+ private parseOwnedJob(r: JobRow, now: Date): OwnedJob {
33
+ const startsAt = r.starts_at.toDate()
34
+ const completesAt = r.completes_at.toDate()
35
+ const deposited = jobDeposited(r.deposited)
36
+ const {output, inputs} = splitJobCargo(r.cargo, deposited)
37
+ return {
38
+ id: r.id.toNumber(),
39
+ building: r.building.toNumber(),
40
+ socket: r.socket.toNumber(),
41
+ shipId: r.ship_id.toNumber(),
42
+ coords: {x: r.coords.x.toNumber(), y: r.coords.y.toNumber()},
43
+ startsAt,
44
+ completesAt,
45
+ recipeId: r.recipe_id.toNumber(),
46
+ quantity: r.quantity.toNumber(),
47
+ status: jobStatus({startsAt, completesAt, deposited}, now),
48
+ deposited,
49
+ output,
50
+ inputs,
51
+ outputStats: output?.stats === undefined ? undefined : BigInt(output.stats.toString()),
52
+ }
53
+ }
54
+ }
@@ -1,10 +1,10 @@
1
- import {type UInt16Type, UInt64, type UInt64Type} from '@wharfkit/antelope'
1
+ import {BlockTimestamp, type UInt16Type} from '@wharfkit/antelope'
2
2
  import {BaseManager} from './base'
3
- import {type CoordinatesType, coordsToLocationId, type Distance} from '../types'
3
+ import type {CoordinatesType, Distance} from '../types'
4
4
  import {hasSystem} from '../utils/system'
5
5
  import {findNearbyPlanets} from '../travel/travel'
6
6
  import type {ServerContract} from '../contracts'
7
- import {type DerivedStratum, deriveStrata} from '../derivation'
7
+ import {type DerivedStratum, deriveStrata, getEffectiveReserve} from '../derivation'
8
8
 
9
9
  export interface LocationStratum extends DerivedStratum {
10
10
  reserveMax: number
@@ -24,7 +24,10 @@ export class LocationsManager extends BaseManager {
24
24
  return findNearbyPlanets(game.config.seed, origin, maxDistance)
25
25
  }
26
26
 
27
- async getStrata(coords: CoordinatesType): Promise<LocationStratum[]> {
27
+ async getStrata(
28
+ coords: CoordinatesType,
29
+ now: BlockTimestamp = BlockTimestamp.fromMilliseconds(Date.now())
30
+ ): Promise<LocationStratum[]> {
28
31
  const game = await this.getGame()
29
32
  const state = await this.getState()
30
33
 
@@ -36,33 +39,26 @@ export class LocationsManager extends BaseManager {
36
39
  y: coords.y,
37
40
  })) as ServerContract.Types.stratum_remaining[]
38
41
 
39
- const overrideMap = new Map<number, number>()
42
+ const epochSeconds = Number(game.config.epochtime)
43
+ const overrideMap = new Map<number, ServerContract.Types.stratum_remaining>()
40
44
  for (const o of overrides) {
41
- overrideMap.set(Number(o.stratum), Number(o.remaining))
45
+ overrideMap.set(Number(o.stratum), o)
42
46
  }
43
47
 
44
- return derived.map((s) => ({
45
- ...s,
46
- reserveMax: s.reserve,
47
- reserve: overrideMap.get(s.index) ?? s.reserve,
48
- }))
49
- }
50
-
51
- async getLocationEntity(
52
- id: UInt64Type
53
- ): Promise<ServerContract.Types.location_row | undefined> {
54
- const row = await this.server.table('location').get(UInt64.from(id))
55
- return row ?? undefined
56
- }
57
-
58
- async getLocationEntityAt(
59
- coords: CoordinatesType
60
- ): Promise<ServerContract.Types.location_row | undefined> {
61
- const id = coordsToLocationId(coords)
62
- return this.getLocationEntity(id)
63
- }
64
-
65
- async getAllLocationEntities(): Promise<ServerContract.Types.location_row[]> {
66
- return this.server.table('location').all()
48
+ return derived.map((s) => {
49
+ const override = overrideMap.get(s.index)
50
+ const reserve = override
51
+ ? getEffectiveReserve(
52
+ {
53
+ remaining: override.remaining,
54
+ max_reserve: s.reserve,
55
+ last_block: override.last_block,
56
+ },
57
+ now,
58
+ epochSeconds
59
+ )
60
+ : s.reserve
61
+ return {...s, reserveMax: s.reserve, reserve}
62
+ })
67
63
  }
68
64
  }
@@ -0,0 +1,21 @@
1
+ import {describe, expect, test} from 'bun:test'
2
+ import {resolveLockedAmount, wrapCostKey} from './nft'
3
+
4
+ describe('resolveLockedAmount', () => {
5
+ test('no fee refunds the full cost', () => {
6
+ expect(resolveLockedAmount(5_0000n, 0)).toBe(5_0000n)
7
+ })
8
+ test('2% fee floors the fee and refunds the remainder', () => {
9
+ expect(resolveLockedAmount(5_0000n, 200)).toBe(4_9000n)
10
+ })
11
+ test('rounding floors the fee (contract uses integer division)', () => {
12
+ expect(resolveLockedAmount(101n, 250)).toBe(99n) // fee = floor(101*250/10000)=2
13
+ })
14
+ })
15
+
16
+ describe('wrapCostKey', () => {
17
+ test('packs item type and tier as (type << 8) | tier', () => {
18
+ expect(wrapCostKey(0, 1).toString()).toBe('1')
19
+ expect(wrapCostKey(3, 2).toString()).toBe('770')
20
+ })
21
+ })