@shipload/sdk 1.0.0-next.6 → 1.0.0-next.60

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 (155) 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 +3589 -1363
  7. package/lib/shipload.js +15700 -5454
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +15423 -5402
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1173 -0
  12. package/lib/testing.js +4947 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4941 -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 +35 -0
  20. package/src/capabilities/crafting.ts +34 -7
  21. package/src/capabilities/gathering.test.ts +29 -0
  22. package/src/capabilities/gathering.ts +37 -19
  23. package/src/capabilities/hauling.ts +0 -5
  24. package/src/capabilities/index.ts +0 -1
  25. package/src/capabilities/modules.ts +42 -0
  26. package/src/capabilities/movement.ts +1 -1
  27. package/src/capabilities/storage.ts +16 -1
  28. package/src/contracts/platform.ts +231 -3
  29. package/src/contracts/server.ts +1518 -501
  30. package/src/coordinates/address.ts +88 -0
  31. package/src/coordinates/constants.test.ts +15 -0
  32. package/src/coordinates/constants.ts +23 -0
  33. package/src/coordinates/index.ts +15 -0
  34. package/src/coordinates/memo.test.ts +47 -0
  35. package/src/coordinates/memo.ts +20 -0
  36. package/src/coordinates/permutation.ts +77 -0
  37. package/src/coordinates/regions.ts +48 -0
  38. package/src/coordinates/sectors.ts +115 -0
  39. package/src/data/capabilities.ts +49 -18
  40. package/src/data/capability-formulas.ts +26 -14
  41. package/src/data/catalog.ts +0 -5
  42. package/src/data/colors.ts +13 -47
  43. package/src/data/entities.json +379 -14
  44. package/src/data/item-ids.ts +48 -12
  45. package/src/data/items.json +301 -74
  46. package/src/data/kind-registry.json +195 -0
  47. package/src/data/kind-registry.ts +182 -0
  48. package/src/data/metadata.ts +277 -48
  49. package/src/data/recipes-runtime.ts +6 -23
  50. package/src/data/recipes.json +2074 -173
  51. package/src/derivation/build-methods.test.ts +13 -0
  52. package/src/derivation/build-methods.ts +48 -0
  53. package/src/derivation/capabilities.test.ts +348 -0
  54. package/src/derivation/capabilities.ts +692 -0
  55. package/src/derivation/capability-mappings.ts +60 -16
  56. package/src/derivation/crafting.test.ts +17 -0
  57. package/src/derivation/crafting.ts +56 -33
  58. package/src/derivation/index.ts +27 -2
  59. package/src/derivation/recipe-usage.test.ts +88 -0
  60. package/src/derivation/recipe-usage.ts +141 -0
  61. package/src/derivation/reserve-regen.ts +34 -0
  62. package/src/derivation/resources.ts +9 -1
  63. package/src/derivation/rollups.test.ts +55 -0
  64. package/src/derivation/rollups.ts +72 -0
  65. package/src/derivation/stars.test.ts +67 -0
  66. package/src/derivation/stars.ts +25 -0
  67. package/src/derivation/stat-scaling.ts +12 -0
  68. package/src/derivation/stats.ts +6 -6
  69. package/src/derivation/stratum.ts +26 -22
  70. package/src/derivation/tiers.ts +40 -7
  71. package/src/derivation/upgrades.ts +14 -0
  72. package/src/derivation/wormhole.ts +154 -0
  73. package/src/entities/entity.ts +102 -0
  74. package/src/entities/gamestate.ts +3 -28
  75. package/src/entities/makers.ts +141 -135
  76. package/src/entities/slot-multiplier.ts +43 -0
  77. package/src/errors.ts +12 -16
  78. package/src/format.ts +26 -4
  79. package/src/index-module.ts +333 -35
  80. package/src/managers/actions.ts +668 -103
  81. package/src/managers/base.ts +6 -2
  82. package/src/managers/cluster.test.ts +39 -0
  83. package/src/managers/cluster.ts +53 -0
  84. package/src/managers/construction-types.ts +80 -0
  85. package/src/managers/construction.ts +443 -0
  86. package/src/managers/context.ts +29 -1
  87. package/src/managers/coordinates.ts +14 -0
  88. package/src/managers/entities.ts +27 -66
  89. package/src/managers/epochs.ts +40 -0
  90. package/src/managers/index.ts +20 -1
  91. package/src/managers/locations.ts +25 -29
  92. package/src/managers/nft.test.ts +14 -0
  93. package/src/managers/nft.ts +70 -0
  94. package/src/managers/players.ts +25 -0
  95. package/src/managers/plot.ts +122 -0
  96. package/src/nft/atomicassets.abi.json +1342 -0
  97. package/src/nft/atomicassets.ts +237 -0
  98. package/src/nft/atomicdata.ts +130 -0
  99. package/src/nft/buildImmutableData.ts +385 -0
  100. package/src/nft/description.ts +225 -52
  101. package/src/nft/index.ts +3 -0
  102. package/src/planner/index.ts +242 -0
  103. package/src/planner/planner.test.ts +334 -0
  104. package/src/resolution/describe-module.ts +43 -25
  105. package/src/resolution/display-name.ts +38 -10
  106. package/src/resolution/resolve-item.test.ts +48 -0
  107. package/src/resolution/resolve-item.ts +112 -58
  108. package/src/scan/index.ts +244 -0
  109. package/src/scan/scan-wasm.base64.ts +2 -0
  110. package/src/scheduling/accessor.ts +65 -23
  111. package/src/scheduling/availability.test.ts +204 -0
  112. package/src/scheduling/availability.ts +211 -0
  113. package/src/scheduling/cancel.test.ts +542 -0
  114. package/src/scheduling/cancel.ts +254 -0
  115. package/src/scheduling/cluster-stock.test.ts +31 -0
  116. package/src/scheduling/cluster-stock.ts +15 -0
  117. package/src/scheduling/energy.ts +47 -0
  118. package/src/scheduling/idle-resolve.ts +45 -0
  119. package/src/scheduling/jobs.ts +71 -0
  120. package/src/scheduling/lane-core.ts +128 -0
  121. package/src/scheduling/lanes.test.ts +250 -0
  122. package/src/scheduling/lanes.ts +227 -0
  123. package/src/scheduling/projection.ts +257 -133
  124. package/src/scheduling/schedule.test.ts +119 -0
  125. package/src/scheduling/schedule.ts +257 -117
  126. package/src/scheduling/task-cargo.test.ts +44 -0
  127. package/src/scheduling/task-cargo.ts +46 -0
  128. package/src/scheduling/unwrap.test.ts +86 -0
  129. package/src/scheduling/unwrap.ts +190 -0
  130. package/src/shipload.ts +26 -1
  131. package/src/subscriptions/manager.cluster.test.ts +51 -0
  132. package/src/subscriptions/manager.ts +245 -172
  133. package/src/subscriptions/mappers.ts +5 -8
  134. package/src/subscriptions/types.ts +28 -3
  135. package/src/testing/catalog-hash.ts +19 -0
  136. package/src/testing/index.ts +2 -0
  137. package/src/testing/projection-parity.ts +167 -0
  138. package/src/travel/reach.ts +21 -0
  139. package/src/travel/route-planner.ts +288 -0
  140. package/src/travel/route-simulator.ts +170 -0
  141. package/src/travel/travel.ts +188 -122
  142. package/src/types/capabilities.ts +37 -8
  143. package/src/types/entity.ts +3 -3
  144. package/src/types/index.ts +0 -1
  145. package/src/types.ts +51 -16
  146. package/src/utils/cargo.test.ts +14 -0
  147. package/src/utils/cargo.ts +29 -0
  148. package/src/utils/display-name.ts +70 -0
  149. package/src/utils/system.ts +15 -16
  150. package/src/capabilities/loading.ts +0 -8
  151. package/src/entities/container.ts +0 -123
  152. package/src/entities/ship-deploy.ts +0 -295
  153. package/src/entities/ship.ts +0 -221
  154. package/src/entities/warehouse.ts +0 -127
  155. package/src/types/entity-traits.ts +0 -69
@@ -1,4 +1,4 @@
1
- import {Checksum256, Int64, type UInt64} from '@wharfkit/antelope'
1
+ import {Checksum256, Int64, UInt64} from '@wharfkit/antelope'
2
2
  import {type PlatformContract, ServerContract} from '../contracts'
3
3
  import {type EpochInfo, getCurrentEpoch, getEpochInfo} from '../scheduling/epoch'
4
4
  import {hasSystem} from '../utils/system'
@@ -34,7 +34,7 @@ export class GameState extends ServerContract.Types.state_row {
34
34
  * Get the current epoch number from the state
35
35
  */
36
36
  get currentEpoch(): UInt64 {
37
- return this.epoch
37
+ return UInt64.from(this.epoch.toString())
38
38
  }
39
39
 
40
40
  /**
@@ -58,27 +58,6 @@ export class GameState extends ServerContract.Types.state_row {
58
58
  return this.enabled
59
59
  }
60
60
 
61
- /**
62
- * Get the total number of ships in the game
63
- */
64
- get shipCount(): number {
65
- return Number(this.ships)
66
- }
67
-
68
- /**
69
- * Get the current salt value (used for random number generation)
70
- */
71
- get currentSalt(): UInt64 {
72
- return this.salt
73
- }
74
-
75
- /**
76
- * Get the commit hash for the next epoch
77
- */
78
- get nextEpochCommit(): Checksum256 {
79
- return this.commit
80
- }
81
-
82
61
  /**
83
62
  * Calculate the current epoch from game config (if game is set)
84
63
  * This might differ from state.epoch if the blockchain hasn't advanced yet
@@ -97,7 +76,7 @@ export class GameState extends ServerContract.Types.state_row {
97
76
  if (!this._game) {
98
77
  return undefined
99
78
  }
100
- return getEpochInfo(this._game, this.epoch)
79
+ return getEpochInfo(this._game, this.currentEpoch)
101
80
  }
102
81
 
103
82
  /**
@@ -137,16 +116,12 @@ export class GameState extends ServerContract.Types.state_row {
137
116
  get summary(): {
138
117
  enabled: boolean
139
118
  epoch: string
140
- ships: number
141
119
  hasSeed: boolean
142
- hasCommit: boolean
143
120
  } {
144
121
  return {
145
122
  enabled: this.enabled,
146
123
  epoch: this.epoch.toString(),
147
- ships: this.shipCount,
148
124
  hasSeed: !this.seed.equals(Checksum256.from('0'.repeat(64))),
149
- hasCommit: !this.commit.equals(Checksum256.from('0'.repeat(64))),
150
125
  }
151
126
  }
152
127
  }
@@ -1,42 +1,57 @@
1
1
  import {Name, UInt16, UInt32, UInt64, UInt8} from '@wharfkit/antelope'
2
+ import type {NameType, UInt64Type} from '@wharfkit/antelope'
2
3
  import {ServerContract} from '../contracts'
3
- import {type PackedModuleInput, Ship, type ShipStateInput} from './ship'
4
- import {computeWarehouseCapabilities, Warehouse, type WarehouseStateInput} from './warehouse'
5
- import {Container, type ContainerStateInput} from './container'
6
- import {ITEM_SHIP_T1_PACKED, ITEM_WAREHOUSE_T1_PACKED} from '../data/item-ids'
4
+ import {Entity} from './entity'
5
+ import {getKindMeta, getTemplateMeta} from '../data/kind-registry'
6
+ import type {EntityTypeName} from '../data/kind-registry'
7
7
  import {getEntityLayout} from '../data/recipes-runtime'
8
+ import type {EntitySlot} from '../data/recipes-runtime'
8
9
  import {itemMetadata} from '../data/metadata'
9
10
  import {getItem} from '../data/catalog'
10
- import {
11
- getModuleCapabilityType,
12
- MODULE_STORAGE,
13
- moduleAccepts,
14
- moduleSlotTypeToCode,
15
- } from '../capabilities/modules'
16
- import {computeShipCapabilities, computeStorageCapabilities} from './ship-deploy'
17
- import {decodeCraftedItemStats} from '../derivation/crafting'
11
+ import {getModuleCapabilityType, moduleAccepts, moduleSlotTypeToCode} from '../capabilities/modules'
12
+ import {computeEntityCapabilities} from '../derivation/capabilities'
13
+ import {packedModulesToInstalled} from './slot-multiplier'
14
+ import {LANE_MOBILITY} from '../scheduling/schedule'
15
+
16
+ export interface PackedModuleInput {
17
+ itemId: number
18
+ stats: bigint
19
+ }
20
+
21
+ export interface EntityStateInput {
22
+ id: UInt64Type
23
+ owner: NameType
24
+ name: string
25
+ coordinates: {x: number; y: number; z?: number}
26
+ itemId?: number
27
+ hullmass?: number
28
+ capacity?: number
29
+ cargomass?: number
30
+ energy?: number
31
+ modules?: PackedModuleInput[]
32
+ schedule?: ServerContract.Types.schedule
33
+ lanes?: ServerContract.Types.lane[]
34
+ cargo?: ServerContract.Types.cargo_item[]
35
+ }
18
36
 
19
37
  function assignModulesToSlots(
20
- packedEntityItemId: number,
38
+ slots: EntitySlot[],
21
39
  modules: PackedModuleInput[],
22
40
  entityLabel: string
23
41
  ): ServerContract.Types.module_entry[] {
24
- const layout = getEntityLayout(packedEntityItemId)
25
- const slots = layout?.slots ?? []
26
42
  const result: Array<{type: number; installed?: ServerContract.Types.packed_module}> = slots.map(
27
43
  (s) => ({type: moduleSlotTypeToCode(s.type), installed: undefined})
28
44
  )
29
45
 
30
46
  for (const mod of modules) {
31
- const itemId = Number(UInt16.from(mod.itemId).value.toString())
32
- const modType = getModuleCapabilityType(itemId)
47
+ const modType = getModuleCapabilityType(mod.itemId)
33
48
  const slotIdx = result.findIndex((r) => !r.installed && moduleAccepts(r.type, modType))
34
49
  if (slotIdx === -1) {
35
50
  let modName: string
36
51
  try {
37
- modName = getItem(itemId).name
52
+ modName = getItem(mod.itemId).name
38
53
  } catch {
39
- modName = itemMetadata[itemId]?.name ?? `item ${itemId}`
54
+ modName = itemMetadata[mod.itemId]?.name ?? `item ${mod.itemId}`
40
55
  }
41
56
  throw new Error(
42
57
  `No compatible slot for module ${modName} (type ${modType}) on ${entityLabel}`
@@ -56,141 +71,132 @@ function assignModulesToSlots(
56
71
  )
57
72
  }
58
73
 
59
- function decodePackedInput(m: PackedModuleInput): {itemId: number; stats: bigint} {
60
- return {
61
- itemId: Number(UInt16.from(m.itemId).value.toString()),
62
- stats: BigInt(UInt64.from(m.stats).toString()),
63
- }
74
+ const ZERO_HULL_STATS: Record<string, number> = {
75
+ density: 0,
76
+ strength: 0,
77
+ hardness: 0,
78
+ cohesion: 0,
64
79
  }
65
80
 
66
- function computeStorageBonus(
67
- decoded: {itemId: number; stats: bigint}[],
68
- baseCapacity: number
69
- ): number {
70
- let totalBonus = 0
71
- for (const m of decoded) {
72
- if (getModuleCapabilityType(m.itemId) !== MODULE_STORAGE) continue
73
- const stats = decodeCraftedItemStats(m.itemId, m.stats)
74
- const {capacityBonus} = computeStorageCapabilities(stats, baseCapacity)
75
- totalBonus += capacityBonus
81
+ export function makeEntity(packedItemId: number, state: EntityStateInput): Entity {
82
+ const template = getTemplateMeta(packedItemId)
83
+ if (!template) {
84
+ throw new Error(`Unknown packed entity item ID: ${packedItemId}`)
76
85
  }
77
- return totalBonus
78
- }
79
86
 
80
- function deriveShipFromModules(
81
- modules: PackedModuleInput[],
82
- baseCapacity: number
83
- ): {
84
- capabilities: ReturnType<typeof computeShipCapabilities>
85
- finalCapacity: number
86
- } {
87
- const decoded = modules.map(decodePackedInput)
88
- const capabilities = computeShipCapabilities(decoded)
89
- const totalBonus = computeStorageBonus(decoded, baseCapacity)
90
- return {capabilities, finalCapacity: baseCapacity + totalBonus}
91
- }
87
+ const kind = template.kind.toString() as EntityTypeName
88
+ const layout = getEntityLayout(packedItemId)?.slots ?? []
89
+ const mods = state.modules ?? []
90
+
91
+ const lanes =
92
+ state.lanes ??
93
+ (state.schedule
94
+ ? [
95
+ ServerContract.Types.lane.from({
96
+ lane_key: UInt8.from(LANE_MOBILITY),
97
+ schedule: state.schedule,
98
+ }),
99
+ ]
100
+ : [])
92
101
 
93
- export function makeShip(state: ShipStateInput): Ship {
94
102
  const info: Record<string, unknown> = {
95
- type: Name.from('ship'),
103
+ type: template.kind,
96
104
  id: UInt64.from(state.id),
97
105
  owner: Name.from(state.owner),
98
106
  entity_name: state.name,
99
107
  coordinates: ServerContract.Types.coordinates.from(state.coordinates),
100
- cargomass: UInt32.from(0),
108
+ item_id: UInt16.from(state.itemId ?? template.itemId),
109
+ cargomass: UInt32.from(state.cargomass ?? 0),
101
110
  cargo: state.cargo || [],
102
- is_idle: !state.schedule,
103
- current_task_elapsed: UInt32.from(0),
104
- current_task_remaining: UInt32.from(0),
105
- pending_tasks: [],
111
+ lanes,
112
+ holds: [],
106
113
  }
107
- if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
108
- if (state.energy !== undefined) info.energy = UInt16.from(state.energy)
109
- if (state.schedule) info.schedule = state.schedule
110
-
111
- let moduleEntries: ServerContract.Types.module_entry[] = []
112
- if (state.modules && state.modules.length > 0) {
113
- moduleEntries = assignModulesToSlots(ITEM_SHIP_T1_PACKED, state.modules, 'Ship T1')
114
- const {capabilities, finalCapacity} = deriveShipFromModules(
115
- state.modules,
116
- state.capacity ?? 0
117
- )
118
- if (capabilities.engines) info.engines = capabilities.engines
119
- if (capabilities.generator) info.generator = capabilities.generator
120
- if (capabilities.gatherer) info.gatherer = capabilities.gatherer
121
- if (capabilities.hauler) info.hauler = capabilities.hauler
122
- if (capabilities.loaders) info.loaders = capabilities.loaders
123
- if (capabilities.crafter) info.crafter = capabilities.crafter
124
- if (state.capacity !== undefined) info.capacity = UInt32.from(finalCapacity)
125
- } else {
126
- moduleEntries = assignModulesToSlots(ITEM_SHIP_T1_PACKED, [], 'Ship T1')
114
+
115
+ if (state.energy !== undefined) info.energy = UInt32.from(state.energy)
116
+
117
+ if (kind === 'container') {
118
+ info.modules = []
119
+ if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
127
120
  if (state.capacity !== undefined) info.capacity = UInt32.from(state.capacity)
128
- }
121
+ } else {
122
+ const entityLabel = getKindMeta(template.kind)?.defaultLabel ?? kind
123
+ const moduleEntries = assignModulesToSlots(layout, mods, entityLabel)
124
+ info.modules = moduleEntries
129
125
 
130
- info.modules = moduleEntries
126
+ const installed = packedModulesToInstalled(moduleEntries)
127
+ const caps = computeEntityCapabilities(ZERO_HULL_STATS, packedItemId, installed, layout)
131
128
 
132
- const entityInfo = ServerContract.Types.entity_info.from(info)
133
- return new Ship(entityInfo)
134
- }
129
+ if (state.hullmass !== undefined) {
130
+ info.hullmass = UInt32.from(state.hullmass)
131
+ } else if (installed.length > 0) {
132
+ info.hullmass = UInt32.from(caps.hullmass)
133
+ }
135
134
 
136
- export function makeWarehouse(state: WarehouseStateInput): Warehouse {
137
- const info: Record<string, unknown> = {
138
- type: Name.from('warehouse'),
139
- id: UInt64.from(state.id),
140
- owner: Name.from(state.owner),
141
- entity_name: state.name,
142
- coordinates: ServerContract.Types.coordinates.from(state.coordinates),
143
- capacity: UInt32.from(state.capacity),
144
- cargomass: UInt32.from(0),
145
- cargo: state.cargo || [],
146
- is_idle: !state.schedule,
147
- current_task_elapsed: UInt32.from(0),
148
- current_task_remaining: UInt32.from(0),
149
- pending_tasks: [],
150
- }
151
- if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
152
- if (state.schedule) info.schedule = state.schedule
153
-
154
- let moduleEntries: ServerContract.Types.module_entry[] = []
155
- if (state.modules && state.modules.length > 0) {
156
- moduleEntries = assignModulesToSlots(
157
- ITEM_WAREHOUSE_T1_PACKED,
158
- state.modules,
159
- 'Warehouse T1'
160
- )
161
- const decoded = state.modules.map(decodePackedInput)
162
- const capabilities = computeWarehouseCapabilities(decoded)
163
- if (capabilities.loaders) info.loaders = capabilities.loaders
135
+ if (state.capacity !== undefined) {
136
+ info.capacity = UInt32.from(state.capacity)
137
+ } else {
138
+ info.capacity = UInt32.from(caps.capacity)
139
+ }
164
140
 
165
- const totalBonus = computeStorageBonus(decoded, state.capacity)
166
- info.capacity = UInt32.from(state.capacity + totalBonus)
167
- } else {
168
- moduleEntries = assignModulesToSlots(ITEM_WAREHOUSE_T1_PACKED, [], 'Warehouse T1')
141
+ if (caps.engines) info.engines = caps.engines
142
+ if (caps.generator) info.generator = caps.generator
143
+ if (caps.hauler) {
144
+ info.hauler = ServerContract.Types.hauler_stats.from({
145
+ capacity: caps.hauler.capacity,
146
+ efficiency: caps.hauler.efficiency,
147
+ drain: caps.hauler.drain,
148
+ capacity_by_tier: caps.hauler.capacityByTier,
149
+ })
150
+ }
151
+ if (caps.warp) info.warp = caps.warp
152
+ if (caps.launcher) {
153
+ info.launcher = ServerContract.Types.launcher_stats.from({
154
+ charge_rate: caps.launcher.chargeRate,
155
+ velocity: caps.launcher.velocity,
156
+ drain: caps.launcher.drain,
157
+ })
158
+ }
159
+
160
+ info.gatherer_lanes = (caps.gathererLanes ?? []).map((l) =>
161
+ ServerContract.Types.gatherer_lane.from({
162
+ slot_index: l.slotIndex,
163
+ yield: l.yield,
164
+ drain: l.drain,
165
+ depth: l.depth,
166
+ output_pct: l.outputPct,
167
+ })
168
+ )
169
+ info.crafter_lanes = (caps.crafterLanes ?? []).map((l) =>
170
+ ServerContract.Types.crafter_lane.from({
171
+ slot_index: l.slotIndex,
172
+ speed: l.speed,
173
+ drain: l.drain,
174
+ output_pct: l.outputPct,
175
+ })
176
+ )
177
+ info.builder_lanes = (caps.builderLanes ?? []).map((l) =>
178
+ ServerContract.Types.builder_lane.from({
179
+ slot_index: l.slotIndex,
180
+ speed: l.speed,
181
+ drain: l.drain,
182
+ output_pct: l.outputPct,
183
+ })
184
+ )
185
+ info.loader_lanes = (caps.loaderLanes ?? []).map((l) =>
186
+ ServerContract.Types.loader_lane.from({
187
+ slot_index: l.slotIndex,
188
+ mass: l.mass,
189
+ thrust: l.thrust,
190
+ output_pct: l.outputPct,
191
+ })
192
+ )
169
193
  }
170
194
 
171
- info.modules = moduleEntries
195
+ if (!info.gatherer_lanes) info.gatherer_lanes = []
196
+ if (!info.crafter_lanes) info.crafter_lanes = []
197
+ if (!info.builder_lanes) info.builder_lanes = []
198
+ if (!info.loader_lanes) info.loader_lanes = []
172
199
 
173
200
  const entityInfo = ServerContract.Types.entity_info.from(info)
174
- return new Warehouse(entityInfo)
175
- }
176
-
177
- export function makeContainer(state: ContainerStateInput): Container {
178
- const entityInfo = ServerContract.Types.entity_info.from({
179
- type: Name.from('container'),
180
- id: UInt64.from(state.id),
181
- owner: Name.from(state.owner),
182
- entity_name: state.name,
183
- coordinates: ServerContract.Types.coordinates.from(state.coordinates),
184
- hullmass: UInt32.from(state.hullmass),
185
- capacity: UInt32.from(state.capacity),
186
- cargomass: UInt32.from(state.cargomass || 0),
187
- cargo: state.cargo || [],
188
- modules: [],
189
- is_idle: !state.schedule,
190
- current_task_elapsed: UInt32.from(0),
191
- current_task_remaining: UInt32.from(0),
192
- pending_tasks: [],
193
- schedule: state.schedule,
194
- })
195
- return new Container(entityInfo)
201
+ return new Entity(entityInfo)
196
202
  }
@@ -0,0 +1,43 @@
1
+ import type {EntitySlot} from '../data/recipes-runtime'
2
+ import type {ServerContract} from '../contracts'
3
+
4
+ export const U16_MAX = 65535
5
+
6
+ export interface InstalledModule {
7
+ slotIndex: number
8
+ itemId: number
9
+ stats: bigint
10
+ }
11
+
12
+ export function packedModulesToInstalled(
13
+ entries: ServerContract.Types.module_entry[]
14
+ ): InstalledModule[] {
15
+ const installed: InstalledModule[] = []
16
+ entries.forEach((entry, slotIndex) => {
17
+ if (!entry.installed) return
18
+ installed.push({
19
+ slotIndex,
20
+ itemId: Number(entry.installed.item_id.value),
21
+ stats: BigInt(entry.installed.stats.toString()),
22
+ })
23
+ })
24
+ return installed
25
+ }
26
+
27
+ export function clampUint16(value: number): number {
28
+ return Math.min(value, U16_MAX)
29
+ }
30
+
31
+ export const clampUint32 = (v: number): number => Math.min(Math.max(Math.floor(v), 0), 4294967295)
32
+
33
+ export function applySlotMultiplier(value: number, outputPct: number): number {
34
+ return clampUint16(Math.floor((value * outputPct) / 100))
35
+ }
36
+
37
+ export function applySlotMultiplierUint32(value: number, outputPct: number): number {
38
+ return clampUint32(Math.floor((value * outputPct) / 100))
39
+ }
40
+
41
+ export function getSlotAmp(layout: EntitySlot[], slotIndex: number): number {
42
+ return layout[slotIndex]?.outputPct ?? 100
43
+ }
package/src/errors.ts CHANGED
@@ -16,20 +16,18 @@ export const REQUIRES_POSITIVE_VALUE = 'Value must be greater than zero.'
16
16
  export const PLAYER_ALREADY_JOINED = 'Player has already joined the game.'
17
17
  export const PLAYER_NOT_JOINED = 'Player has not joined the game.'
18
18
  export const PLAYER_NOT_FOUND = 'Cannot find player for given account name.'
19
- export const STARTER_ALREADY_CLAIMED =
20
- 'Starter ship already claimed; destroy existing ships to re-claim.'
21
- export const SHIP_ALREADY_THERE = 'Ship cannot travel to the location its already at.'
19
+ export const ENTITY_ALREADY_THERE = 'Entity cannot travel to the location it is already at.'
22
20
  export const SHIP_ALREADY_TRAVELING = 'Ship is already traveling.'
23
21
  export const SHIP_CANNOT_BUY_TRAVELING = 'Ship cannot buy goods while traveling.'
24
22
  export const SHIP_CANNOT_UPDATE_TRAVELING = 'Ship cannot be updated while traveling.'
25
- export const SHIP_INVALID_DESTINATION = 'Ship cannot travel, no system at specified destination.'
26
- export const SHIP_INVALID_TRAVEL_DURATION =
23
+ export const ENTITY_INVALID_DESTINATION = 'Cannot travel: no system at specified destination.'
24
+ export const ENTITY_INVALID_TRAVEL_DURATION =
27
25
  'This trip cannot be made as it would exceed the maximum travel duration.'
28
26
  export const SHIP_NOT_ARRIVED = 'Ship has not yet arrived at its destination.'
29
- export const SHIP_NOT_ENOUGH_ENERGY =
30
- 'Ship does not have enough energy to travel to the destination.'
31
- export const SHIP_NOT_ENOUGH_ENERGY_CAPACITY =
32
- 'Ship does not have enough energy capacity to travel.'
27
+ export const ENTITY_NOT_ENOUGH_ENERGY =
28
+ 'Entity does not have enough energy to travel to the destination.'
29
+ export const ENTITY_NOT_ENOUGH_ENERGY_CAPACITY =
30
+ 'Entity does not have enough energy capacity to travel.'
33
31
  export const SHIP_NOT_FOUND = 'Cannot find ship for given account.'
34
32
  export const SHIP_NOT_OWNED = 'Ship is not owned by this account.'
35
33
  export const NO_SCHEDULE = 'No scheduled tasks.'
@@ -38,19 +36,15 @@ export const SHIP_NO_COMPLETED_TASKS = 'No completed tasks to resolve.'
38
36
  export const RESOLVE_COUNT_EXCEEDS_COMPLETED = 'Requested resolve count exceeds completed tasks.'
39
37
  export const SHIP_CANNOT_CANCEL_TASK = 'Cannot cancel task that is immutable or in progress.'
40
38
  export const SHIP_NO_TASKS_TO_CANCEL = 'No tasks to cancel.'
41
- export const SHIP_INVALID_CARGO = 'Invalid cargo specified for load/unload.'
42
- export const SHIP_CARGO_NOT_OWNED = 'Cannot load cargo that is not owned.'
43
- export const SHIP_CARGO_NOT_LOADED = 'Cannot unload cargo that is not loaded.'
44
- export const SHIP_CAPACITY_EXCEEDED = 'Ship cargo capacity would be exceeded.'
39
+ export const ENTITY_INVALID_CARGO = 'Invalid cargo specified for load/unload.'
40
+ export const ENTITY_CARGO_NOT_OWNED = 'Cannot load cargo that is not owned.'
41
+ export const ENTITY_CARGO_NOT_LOADED = 'Cannot unload cargo that is not loaded.'
45
42
  export const ENTITY_CAPACITY_EXCEEDED = 'Entity cargo capacity would be exceeded.'
46
43
  export const WAREHOUSE_NOT_FOUND = 'Cannot find warehouse for given id.'
47
44
  export const WAREHOUSE_ALREADY_AT_LOCATION = 'Warehouse already exists at this location.'
48
- export const WAREHOUSE_CAPACITY_EXCEEDED = 'Warehouse capacity would be exceeded.'
49
45
  export const CONTAINER_NOT_FOUND = 'Cannot find container for given id.'
50
- export const CONTAINER_CAPACITY_EXCEEDED = 'Container capacity would be exceeded.'
51
46
  export const DESTINATION_CAPACITY_EXCEEDED =
52
47
  'Destination entity does not have enough capacity for the gather.'
53
- export const CANCEL_PAIRED_HAS_PENDING = 'Cannot cancel transfer, paired entity has pending tasks.'
54
48
  export const GROUP_EMPTY = 'Group travel requires at least one entity.'
55
49
  export const GROUP_NO_THRUST = 'Group travel requires at least one entity with engines.'
56
50
  export const GROUP_NOT_SAME_LOCATION = 'All entities must be at the same location for group travel.'
@@ -62,6 +56,8 @@ export const GROUP_HAUL_CAPACITY_EXCEEDED =
62
56
  'Group travel requires sufficient hauler capacity for all non-self-propelled entities.'
63
57
  export const CANCEL_CONTAINS_GROUPED_TASK =
64
58
  'Cannot cancel range containing grouped task - cancel non-grouped tasks first.'
59
+ export const WOULD_STRAND = 'Cancelling this would leave a later task without the cargo it needs.'
60
+ export const WOULD_OVERFILL = 'Cancelling this would overfill the other entity with returned cargo.'
65
61
  export const WARP_NO_CAPABILITY = 'Entity does not have warp capability.'
66
62
  export const WARP_HAS_SCHEDULE = 'Entity must be idle to warp.'
67
63
  export const WARP_HAS_CARGO = 'Entity must have no cargo to warp.'
package/src/format.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  export function formatMass(kg: number): string {
2
- const t = kg / 1000
3
- const fixed = t.toFixed(2)
4
- const trimmed = fixed.replace(/\.?0+$/, '')
5
- return `${trimmed} t`
2
+ if (kg === 0) return '0 t'
3
+ const sign = kg < 0 ? '-' : ''
4
+ const centitonnes = Math.round(Math.abs(kg) / 10)
5
+ const t = Math.floor(centitonnes / 100)
6
+ const frac = centitonnes % 100
7
+ if (frac === 0) return `${sign}${t} t`
8
+ const fracStr = String(frac).padStart(2, '0').replace(/0$/, '')
9
+ return `${sign}${t}.${fracStr} t`
6
10
  }
7
11
 
8
12
  export function formatMassDelta(kg: number): string {
@@ -10,3 +14,21 @@ export function formatMassDelta(kg: number): string {
10
14
  const sign = kg > 0 ? '+' : '-'
11
15
  return `${sign}${formatMass(Math.abs(kg))}`
12
16
  }
17
+
18
+ export function formatLocation(loc: {x: number; y: number}): string {
19
+ return `${loc.x}, ${loc.y}`
20
+ }
21
+
22
+ function trim(n: number, digits = 1): string {
23
+ return n.toFixed(digits).replace(/\.?0+$/, '')
24
+ }
25
+
26
+ export function formatMassScaled(kg: number): string {
27
+ if (kg === 0) return '0 t'
28
+ const sign = kg < 0 ? '-' : ''
29
+ const tonnes = Math.abs(kg) / 1000
30
+ if (tonnes >= 1_000_000_000) return `${sign}${trim(tonnes / 1_000_000_000)}b t`
31
+ if (tonnes >= 1_000_000) return `${sign}${trim(tonnes / 1_000_000)}m t`
32
+ if (tonnes >= 1_000) return `${sign}${trim(tonnes / 1_000)}k t`
33
+ return formatMass(kg)
34
+ }