@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
@@ -0,0 +1,167 @@
1
+ import type {UInt16, UInt32} from '@wharfkit/antelope'
2
+ import type {ServerContract} from '../contracts'
3
+ import type {ProjectedEntity} from '../scheduling/projection'
4
+ import {type CargoStack, cargoItemToStack, mergeStacks} from '../capabilities/storage'
5
+
6
+ export interface ContractProjectedState {
7
+ owner: {toString(): string}
8
+ coordinates: ServerContract.Types.coordinates
9
+ energy?: UInt16
10
+ cargomass: UInt32
11
+ cargo: ServerContract.Types.cargo_view[]
12
+ hullmass?: UInt32
13
+ capacity?: UInt32
14
+ engines?: ServerContract.Types.movement_stats
15
+ generator?: ServerContract.Types.energy_stats
16
+ hauler?: ServerContract.Types.hauler_stats
17
+ gatherer_lanes: ServerContract.Types.gatherer_lane[]
18
+ crafter_lanes: ServerContract.Types.crafter_lane[]
19
+ loader_lanes: ServerContract.Types.loader_lane[]
20
+ }
21
+
22
+ export interface ProjectionComparisonOptions {
23
+ step?: number
24
+ }
25
+
26
+ export function assertProjectionEquals(
27
+ contract: ContractProjectedState,
28
+ sdk: ProjectedEntity,
29
+ options: ProjectionComparisonOptions = {}
30
+ ): void {
31
+ const mismatches: string[] = []
32
+
33
+ const record = (name: string, c: unknown, s: unknown) => {
34
+ if (c !== s) mismatches.push(` ${name}: contract=${fmt(c)} sdk=${fmt(s)}`)
35
+ }
36
+
37
+ const recordStatBlock = (name: string, c: unknown, s: unknown) => {
38
+ const cPresent = c !== undefined && c !== null
39
+ const sPresent = s !== undefined && s !== null
40
+ if (cPresent !== sPresent) {
41
+ mismatches.push(
42
+ ` ${name}: contract=${cPresent ? 'present' : 'absent'} sdk=${sPresent ? 'present' : 'absent'}`
43
+ )
44
+ return
45
+ }
46
+ if (!cPresent) return
47
+ const cn = JSON.stringify(normaliseStatBlock(c))
48
+ const sn = JSON.stringify(normaliseStatBlock(s))
49
+ if (cn !== sn) mismatches.push(` ${name}: contract=${cn} sdk=${sn}`)
50
+ }
51
+
52
+ record('coordinates.x', toNum(contract.coordinates.x), Number(sdk.location.x))
53
+ record('coordinates.y', toNum(contract.coordinates.y), Number(sdk.location.y))
54
+ record('energy', toNum(contract.energy), Number(sdk.energy))
55
+ record('cargomass', toNum(contract.cargomass), Number(sdk.cargoMass))
56
+ record('hullmass', toNum(contract.hullmass), Number(sdk.shipMass))
57
+ record('capacity', toNum(contract.capacity), sdk.capacity ? Number(sdk.capacity) : undefined)
58
+
59
+ recordStatBlock('engines', contract.engines, sdk.engines)
60
+ recordStatBlock('generator', contract.generator, sdk.generator)
61
+ recordStatBlock('hauler', contract.hauler, sdk.hauler)
62
+
63
+ const normLane = (l: {slot_index?: unknown; [k: string]: unknown}) =>
64
+ Object.fromEntries(
65
+ Object.entries(l)
66
+ .filter(([k]) => k !== 'slot_index')
67
+ .map(([k, v]) => [k, toNum(v) ?? 0])
68
+ )
69
+
70
+ const compareLanes = (name: string, cLanes: unknown[], sLanes: unknown[]) => {
71
+ if (cLanes.length !== sLanes.length) {
72
+ mismatches.push(` ${name}.length: contract=${cLanes.length} sdk=${sLanes.length}`)
73
+ return
74
+ }
75
+ for (let i = 0; i < cLanes.length; i++) {
76
+ const cn = JSON.stringify(normLane(cLanes[i] as Record<string, unknown>))
77
+ const sn = JSON.stringify(normLane(sLanes[i] as Record<string, unknown>))
78
+ if (cn !== sn) mismatches.push(` ${name}[${i}]: contract=${cn} sdk=${sn}`)
79
+ }
80
+ }
81
+
82
+ compareLanes('gatherer_lanes', contract.gatherer_lanes as unknown[], sdk.gathererLanes ?? [])
83
+ compareLanes('crafter_lanes', contract.crafter_lanes as unknown[], sdk.crafterLanes ?? [])
84
+ compareLanes('loader_lanes', contract.loader_lanes as unknown[], sdk.loaderLanes ?? [])
85
+
86
+ if (contract.cargo.length > 0 || sdk.cargo.length > 0) {
87
+ const contractCargo = normaliseCargo(mergeContractCargo(contract.cargo))
88
+ const sdkCargo = normaliseCargo(sdk.cargo)
89
+ if (contractCargo.length !== sdkCargo.length) {
90
+ mismatches.push(
91
+ ` cargo.length: contract=${contractCargo.length} sdk=${sdkCargo.length}`
92
+ )
93
+ } else {
94
+ for (let i = 0; i < contractCargo.length; i++) {
95
+ const c = contractCargo[i]
96
+ const s = sdkCargo[i]
97
+ if (c.itemId !== s.itemId || c.stats !== s.stats || c.quantity !== s.quantity) {
98
+ mismatches.push(
99
+ ` cargo[${i}]: contract={item:${c.itemId},stats:${c.stats},qty:${c.quantity}} sdk={item:${s.itemId},stats:${s.stats},qty:${s.quantity}}`
100
+ )
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ if (mismatches.length > 0) {
107
+ const header =
108
+ options.step !== undefined
109
+ ? `projection divergence at step ${options.step}:`
110
+ : 'projection divergence:'
111
+ throw new Error([header, ...mismatches].join('\n'))
112
+ }
113
+ }
114
+
115
+ interface NormalisedStack {
116
+ itemId: number
117
+ stats: string
118
+ quantity: string
119
+ }
120
+
121
+ function mergeContractCargo(cargo: ServerContract.Types.cargo_view[]): CargoStack[] {
122
+ return cargo.reduce<CargoStack[]>(
123
+ (acc, row) =>
124
+ mergeStacks(acc, cargoItemToStack(row as unknown as ServerContract.Types.cargo_item)),
125
+ []
126
+ )
127
+ }
128
+
129
+ function normaliseCargo(cargo: CargoStack[]): NormalisedStack[] {
130
+ return cargo
131
+ .map((s) => ({
132
+ itemId: Number(s.item_id),
133
+ stats: BigInt(s.stats.toString()).toString(),
134
+ quantity: BigInt(s.quantity.toString()).toString(),
135
+ }))
136
+ .sort(stackSort)
137
+ }
138
+
139
+ function stackSort(a: NormalisedStack, b: NormalisedStack): number {
140
+ if (a.itemId !== b.itemId) return a.itemId - b.itemId
141
+ return a.stats < b.stats ? -1 : a.stats > b.stats ? 1 : 0
142
+ }
143
+
144
+ function toNum(v: unknown): number | undefined {
145
+ if (v === undefined || v === null) return undefined
146
+ if (typeof v === 'number') return v
147
+ if (typeof v === 'bigint') return Number(v)
148
+ if (typeof (v as {toNumber?: unknown}).toNumber === 'function') {
149
+ return (v as {toNumber(): number}).toNumber()
150
+ }
151
+ return Number(v as number)
152
+ }
153
+
154
+ function fmt(v: unknown): string {
155
+ if (v === undefined) return 'undefined'
156
+ if (v === null) return 'null'
157
+ return String(v)
158
+ }
159
+
160
+ function normaliseStatBlock(block: unknown): Record<string, number> {
161
+ const out: Record<string, number> = {}
162
+ const obj = block as Record<string, unknown>
163
+ for (const k of Object.keys(obj).sort()) {
164
+ out[k] = toNum(obj[k]) ?? 0
165
+ }
166
+ return out
167
+ }
@@ -0,0 +1,23 @@
1
+ export interface ReachStats {
2
+ generator?: {capacity: bigint}
3
+ engines?: {drain: bigint}
4
+ hauler?: {drain: bigint}
5
+ }
6
+
7
+ export function computePerLegReach(s: ReachStats, haulCount = 0): number {
8
+ const capacity = s.generator?.capacity
9
+ const drain = s.engines?.drain
10
+ if (capacity === undefined || drain === undefined || drain === 0n) {
11
+ throw new Error('entity has no usable engine/generator (cannot compute per-leg reach)')
12
+ }
13
+ const haulDrain = s.hauler && haulCount > 0 ? s.hauler.drain * BigInt(haulCount) : 0n
14
+ return Number(capacity) / Number(drain + haulDrain)
15
+ }
16
+
17
+ export function computeGroupPerLegReach(participants: ReachStats[], haulCount: number): number {
18
+ const movers = participants.filter((p) => p.engines !== undefined && p.engines.drain !== 0n)
19
+ if (movers.length === 0) {
20
+ throw new Error('group has no moving entity (cannot compute per-leg reach)')
21
+ }
22
+ return Math.min(...movers.map((p) => computePerLegReach(p, haulCount)))
23
+ }
@@ -0,0 +1,196 @@
1
+ import {distanceBetweenPoints, findNearbyPlanets} from './travel'
2
+ import {hasSystem} from '../utils/system'
3
+ import {nearbyWormholes, wormholeAt} from '../derivation/wormhole'
4
+ import {PRECISION} from '../types'
5
+ import {Checksum256, type Checksum256Type} from '@wharfkit/antelope'
6
+
7
+ export interface Coord {
8
+ x: number
9
+ y: number
10
+ }
11
+
12
+ export interface Neighbor {
13
+ coord: Coord
14
+ dist: number
15
+ }
16
+
17
+ export interface SystemGraph {
18
+ hasSystem(c: Coord): boolean
19
+ nearby(c: Coord, reachTiles: number): Neighbor[]
20
+ }
21
+
22
+ export interface RoutePlan {
23
+ ok: true
24
+ waypoints: Coord[]
25
+ legs: number
26
+ totalDistance: number
27
+ }
28
+
29
+ export type RouteFailureReason = 'empty-destination' | 'no-path' | 'max-legs'
30
+
31
+ export interface RouteFailure {
32
+ ok: false
33
+ reason: RouteFailureReason
34
+ furthest?: Coord
35
+ legsNeeded?: number
36
+ partialWaypoints?: Coord[]
37
+ }
38
+
39
+ export type RouteResult = RoutePlan | RouteFailure
40
+
41
+ export interface PlanRouteParams {
42
+ origin: Coord
43
+ dest: Coord
44
+ perLegReach: number
45
+ graph: SystemGraph
46
+ corridorSlack?: number
47
+ nodeBudget?: number
48
+ maxLegs?: number
49
+ }
50
+
51
+ const key = (c: Coord): string => `${c.x},${c.y}`
52
+ const sameCoord = (a: Coord, b: Coord): boolean => a.x === b.x && a.y === b.y
53
+ const dist = (a: Coord, b: Coord): number => Math.hypot(a.x - b.x, a.y - b.y)
54
+
55
+ export const MAX_LEGS = 12
56
+
57
+ export function planRoute(params: PlanRouteParams): RouteResult {
58
+ const {origin, dest, perLegReach, graph} = params
59
+ const corridorSlack = params.corridorSlack ?? perLegReach
60
+ const nodeBudget = params.nodeBudget ?? 5000
61
+ const maxLegs = params.maxLegs ?? MAX_LEGS
62
+
63
+ if (!graph.hasSystem(dest)) {
64
+ return {ok: false, reason: 'empty-destination'}
65
+ }
66
+
67
+ const straightLine = dist(origin, dest)
68
+ const heuristic = (c: Coord): number => Math.ceil(dist(c, dest) / perLegReach)
69
+
70
+ const gScore = new Map<string, number>([[key(origin), 0]])
71
+ const cameFrom = new Map<string, Coord>()
72
+ const frontier: {coord: Coord; g: number; f: number; remaining: number}[] = [
73
+ {coord: origin, g: 0, f: heuristic(origin), remaining: straightLine},
74
+ ]
75
+
76
+ let furthest = origin
77
+ let furthestRemaining = dist(origin, dest)
78
+ let expansions = 0
79
+ let cappedByMaxLegs = false
80
+
81
+ while (frontier.length > 0) {
82
+ let bestIdx = 0
83
+ for (let i = 1; i < frontier.length; i++) {
84
+ const a = frontier[i]
85
+ const b = frontier[bestIdx]
86
+ if (a.f < b.f || (a.f === b.f && a.remaining < b.remaining)) {
87
+ bestIdx = i
88
+ }
89
+ }
90
+ const current = frontier.splice(bestIdx, 1)[0]
91
+
92
+ if (sameCoord(current.coord, dest)) {
93
+ return reconstruct(cameFrom, origin, dest)
94
+ }
95
+
96
+ if (current.remaining < furthestRemaining) {
97
+ furthestRemaining = current.remaining
98
+ furthest = current.coord
99
+ }
100
+
101
+ if (++expansions > nodeBudget) break
102
+
103
+ for (const n of graph.nearby(current.coord, perLegReach)) {
104
+ const inCorridor =
105
+ dist(origin, n.coord) + dist(n.coord, dest) <= straightLine + corridorSlack
106
+ if (!inCorridor) continue
107
+
108
+ const tentativeG = current.g + 1
109
+ if (tentativeG > maxLegs) {
110
+ cappedByMaxLegs = true
111
+ continue
112
+ }
113
+ const nk = key(n.coord)
114
+ if (tentativeG < (gScore.get(nk) ?? Infinity)) {
115
+ gScore.set(nk, tentativeG)
116
+ cameFrom.set(nk, current.coord)
117
+ const remaining = dist(n.coord, dest)
118
+ frontier.push({
119
+ coord: n.coord,
120
+ g: tentativeG,
121
+ f: tentativeG + Math.ceil(remaining / perLegReach),
122
+ remaining,
123
+ })
124
+ }
125
+ }
126
+ }
127
+
128
+ if (cappedByMaxLegs) {
129
+ return {
130
+ ok: false,
131
+ reason: 'max-legs',
132
+ furthest,
133
+ partialWaypoints: reconstructWaypoints(cameFrom, origin, furthest),
134
+ }
135
+ }
136
+ return {
137
+ ok: false,
138
+ reason: 'no-path',
139
+ furthest,
140
+ partialWaypoints: reconstructWaypoints(cameFrom, origin, furthest),
141
+ }
142
+ }
143
+
144
+ function reconstructWaypoints(cameFrom: Map<string, Coord>, origin: Coord, target: Coord): Coord[] {
145
+ if (sameCoord(target, origin)) return []
146
+ const path: Coord[] = [target]
147
+ let cur = target
148
+ while (!sameCoord(cur, origin)) {
149
+ const prev = cameFrom.get(key(cur))
150
+ if (!prev) break
151
+ path.unshift(prev)
152
+ cur = prev
153
+ }
154
+ return path.slice(1)
155
+ }
156
+
157
+ function reconstruct(cameFrom: Map<string, Coord>, origin: Coord, dest: Coord): RoutePlan {
158
+ const path: Coord[] = [dest]
159
+ let cur = dest
160
+ let totalDistance = 0
161
+ while (!sameCoord(cur, origin)) {
162
+ const prev = cameFrom.get(key(cur))
163
+ if (!prev) break
164
+ totalDistance += dist(prev, cur)
165
+ path.unshift(prev)
166
+ cur = prev
167
+ }
168
+ const waypoints = path.slice(1)
169
+ return {ok: true, waypoints, legs: waypoints.length, totalDistance}
170
+ }
171
+
172
+ export function sdkSystemGraph(seed: Checksum256Type): SystemGraph {
173
+ const s = Checksum256.from(seed)
174
+ // Travelable nodes mirror the contract's is_travelable: systems plus wormhole mouths.
175
+ return {
176
+ hasSystem: (c) => hasSystem(s, {x: c.x, y: c.y}) || wormholeAt(s, c.x, c.y) !== null,
177
+ nearby: (c, reachTiles) => {
178
+ const seen = new Set<string>([`${c.x},${c.y}`])
179
+ const out: Neighbor[] = []
180
+ for (const d of findNearbyPlanets(s, {x: c.x, y: c.y}, reachTiles * PRECISION)) {
181
+ const coord = {x: Number(d.destination.x), y: Number(d.destination.y)}
182
+ const k = `${coord.x},${coord.y}`
183
+ if (seen.has(k)) continue
184
+ seen.add(k)
185
+ out.push({coord, dist: Number(d.distance) / PRECISION})
186
+ }
187
+ for (const coord of nearbyWormholes(s, c.x, c.y, reachTiles)) {
188
+ const k = `${coord.x},${coord.y}`
189
+ if (seen.has(k)) continue
190
+ seen.add(k)
191
+ out.push({coord, dist: Math.hypot(coord.x - c.x, coord.y - c.y)})
192
+ }
193
+ return out
194
+ },
195
+ }
196
+ }