@shipload/sdk 1.0.0-next.7 → 1.0.0-next.71

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 +5822 -1484
  7. package/lib/shipload.js +20838 -5419
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +20674 -5669
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1691 -0
  12. package/lib/testing.js +7109 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +7103 -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 +2793 -478
  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 +425 -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 +950 -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 +96 -0
  129. package/src/managers/jobs.ts +58 -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 +448 -0
  161. package/src/scheduling/jobs.ts +343 -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 +279 -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,102 @@
1
+ import {UInt64} from '@wharfkit/antelope'
2
+ import {ServerContract} from '../contracts'
3
+ import {
4
+ CAP_DEMOLISH,
5
+ CAP_MODULES,
6
+ CAP_UNDEPLOY,
7
+ CAP_WRAP,
8
+ type EntityClass,
9
+ getEntityClass,
10
+ kindCan,
11
+ } from '../data/kind-registry'
12
+ import {InventoryAccessor} from './inventory-accessor'
13
+ import {Location} from './location'
14
+ import {ScheduleAccessor} from '../scheduling/accessor'
15
+ import * as schedule from '../scheduling/schedule'
16
+ import type {EntityInventory} from './entity-inventory'
17
+
18
+ export class Entity extends ServerContract.Types.entity_info {
19
+ private _sched?: ScheduleAccessor
20
+ private _inv?: InventoryAccessor
21
+
22
+ get name(): string {
23
+ return this.entity_name
24
+ }
25
+
26
+ get location(): Location {
27
+ return Location.from(this.coordinates)
28
+ }
29
+
30
+ get isIdle(): boolean {
31
+ return schedule.isIdle(this)
32
+ }
33
+
34
+ get hasPendingCapper(): boolean {
35
+ return schedule.hasPendingCapper(this)
36
+ }
37
+
38
+ get sched(): ScheduleAccessor {
39
+ this._sched ??= new ScheduleAccessor(this)
40
+ return this._sched
41
+ }
42
+
43
+ get inv(): InventoryAccessor {
44
+ this._inv ??= new InventoryAccessor(this)
45
+ return this._inv
46
+ }
47
+
48
+ get inventory(): EntityInventory[] {
49
+ return this.inv.items
50
+ }
51
+
52
+ get totalCargoMass(): UInt64 {
53
+ return this.inv.totalMass
54
+ }
55
+
56
+ get maxCapacity(): UInt64 {
57
+ return UInt64.from(this.capacity ?? 0)
58
+ }
59
+
60
+ get availableCapacity(): UInt64 {
61
+ const cargo = this.totalCargoMass
62
+ const max = this.maxCapacity
63
+ return cargo.gte(max) ? UInt64.from(0) : max.subtracting(cargo)
64
+ }
65
+
66
+ get isFull(): boolean {
67
+ return this.totalCargoMass.gte(this.maxCapacity)
68
+ }
69
+
70
+ get totalMass(): UInt64 {
71
+ const hull = this.hullmass ? UInt64.from(this.hullmass) : UInt64.from(0)
72
+ return hull.adding(this.totalCargoMass)
73
+ }
74
+
75
+ get entityClass(): EntityClass {
76
+ return getEntityClass(this.type)
77
+ }
78
+
79
+ get canWrap(): boolean {
80
+ return kindCan(this.type, CAP_WRAP)
81
+ }
82
+
83
+ get canUndeploy(): boolean {
84
+ return kindCan(this.type, CAP_UNDEPLOY)
85
+ }
86
+
87
+ get canDemolish(): boolean {
88
+ return kindCan(this.type, CAP_DEMOLISH)
89
+ }
90
+
91
+ get canUseModules(): boolean {
92
+ return kindCan(this.type, CAP_MODULES)
93
+ }
94
+
95
+ isLoading(now: Date): boolean {
96
+ return schedule.isLoading(this, now)
97
+ }
98
+
99
+ isUnloading(now: Date): boolean {
100
+ return schedule.isUnloading(this, now)
101
+ }
102
+ }
@@ -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,58 @@
1
- import {Name, UInt16, UInt32, UInt64, UInt8} from '@wharfkit/antelope'
1
+ import {Name, TimePoint, 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
+ projectedAt?: Date
36
+ }
18
37
 
19
38
  function assignModulesToSlots(
20
- packedEntityItemId: number,
39
+ slots: EntitySlot[],
21
40
  modules: PackedModuleInput[],
22
41
  entityLabel: string
23
42
  ): ServerContract.Types.module_entry[] {
24
- const layout = getEntityLayout(packedEntityItemId)
25
- const slots = layout?.slots ?? []
26
43
  const result: Array<{type: number; installed?: ServerContract.Types.packed_module}> = slots.map(
27
44
  (s) => ({type: moduleSlotTypeToCode(s.type), installed: undefined})
28
45
  )
29
46
 
30
47
  for (const mod of modules) {
31
- const itemId = Number(UInt16.from(mod.itemId).value.toString())
32
- const modType = getModuleCapabilityType(itemId)
48
+ const modType = getModuleCapabilityType(mod.itemId)
33
49
  const slotIdx = result.findIndex((r) => !r.installed && moduleAccepts(r.type, modType))
34
50
  if (slotIdx === -1) {
35
51
  let modName: string
36
52
  try {
37
- modName = getItem(itemId).name
53
+ modName = getItem(mod.itemId).name
38
54
  } catch {
39
- modName = itemMetadata[itemId]?.name ?? `item ${itemId}`
55
+ modName = itemMetadata[mod.itemId]?.name ?? `item ${mod.itemId}`
40
56
  }
41
57
  throw new Error(
42
58
  `No compatible slot for module ${modName} (type ${modType}) on ${entityLabel}`
@@ -56,141 +72,133 @@ function assignModulesToSlots(
56
72
  )
57
73
  }
58
74
 
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
- }
75
+ const ZERO_HULL_STATS: Record<string, number> = {
76
+ density: 0,
77
+ strength: 0,
78
+ hardness: 0,
79
+ cohesion: 0,
64
80
  }
65
81
 
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
82
+ export function makeEntity(packedItemId: number, state: EntityStateInput): Entity {
83
+ const template = getTemplateMeta(packedItemId)
84
+ if (!template) {
85
+ throw new Error(`Unknown packed entity item ID: ${packedItemId}`)
76
86
  }
77
- return totalBonus
78
- }
79
87
 
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
- }
88
+ const kind = template.kind.toString() as EntityTypeName
89
+ const layout = getEntityLayout(packedItemId)?.slots ?? []
90
+ const mods = state.modules ?? []
91
+
92
+ const lanes =
93
+ state.lanes ??
94
+ (state.schedule
95
+ ? [
96
+ ServerContract.Types.lane.from({
97
+ lane_key: UInt8.from(LANE_MOBILITY),
98
+ schedule: state.schedule,
99
+ }),
100
+ ]
101
+ : [])
92
102
 
93
- export function makeShip(state: ShipStateInput): Ship {
94
103
  const info: Record<string, unknown> = {
95
- type: Name.from('ship'),
104
+ type: template.kind,
96
105
  id: UInt64.from(state.id),
97
106
  owner: Name.from(state.owner),
98
107
  entity_name: state.name,
99
108
  coordinates: ServerContract.Types.coordinates.from(state.coordinates),
100
- cargomass: UInt32.from(0),
109
+ item_id: UInt16.from(state.itemId ?? template.itemId),
110
+ cargomass: UInt32.from(state.cargomass ?? 0),
101
111
  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: [],
112
+ lanes,
113
+ holds: [],
114
+ projected_at: TimePoint.fromMilliseconds(state.projectedAt?.getTime() ?? 0),
106
115
  }
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')
116
+
117
+ if (state.energy !== undefined) info.energy = UInt32.from(state.energy)
118
+
119
+ if (kind === 'container') {
120
+ info.modules = []
121
+ if (state.hullmass !== undefined) info.hullmass = UInt32.from(state.hullmass)
127
122
  if (state.capacity !== undefined) info.capacity = UInt32.from(state.capacity)
128
- }
123
+ } else {
124
+ const entityLabel = getKindMeta(template.kind)?.defaultLabel ?? kind
125
+ const moduleEntries = assignModulesToSlots(layout, mods, entityLabel)
126
+ info.modules = moduleEntries
129
127
 
130
- info.modules = moduleEntries
128
+ const installed = packedModulesToInstalled(moduleEntries)
129
+ const caps = computeEntityCapabilities(ZERO_HULL_STATS, packedItemId, installed, layout)
131
130
 
132
- const entityInfo = ServerContract.Types.entity_info.from(info)
133
- return new Ship(entityInfo)
134
- }
131
+ if (state.hullmass !== undefined) {
132
+ info.hullmass = UInt32.from(state.hullmass)
133
+ } else if (installed.length > 0) {
134
+ info.hullmass = UInt32.from(caps.hullmass)
135
+ }
135
136
 
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
137
+ if (state.capacity !== undefined) {
138
+ info.capacity = UInt32.from(state.capacity)
139
+ } else {
140
+ info.capacity = UInt32.from(caps.capacity)
141
+ }
164
142
 
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')
143
+ if (caps.engines) info.engines = caps.engines
144
+ if (caps.generator) info.generator = caps.generator
145
+ if (caps.hauler) {
146
+ info.hauler = ServerContract.Types.hauler_stats.from({
147
+ capacity: caps.hauler.capacity,
148
+ efficiency: caps.hauler.efficiency,
149
+ drain: caps.hauler.drain,
150
+ capacity_by_tier: caps.hauler.capacityByTier,
151
+ })
152
+ }
153
+ if (caps.warp) info.warp = caps.warp
154
+ if (caps.launcher) {
155
+ info.launcher = ServerContract.Types.launcher_stats.from({
156
+ charge_rate: caps.launcher.chargeRate,
157
+ velocity: caps.launcher.velocity,
158
+ drain: caps.launcher.drain,
159
+ })
160
+ }
161
+
162
+ info.gatherer_lanes = (caps.gathererLanes ?? []).map((l) =>
163
+ ServerContract.Types.gatherer_lane.from({
164
+ slot_index: l.slotIndex,
165
+ yield: l.yield,
166
+ drain: l.drain,
167
+ depth: l.depth,
168
+ output_pct: l.outputPct,
169
+ })
170
+ )
171
+ info.crafter_lanes = (caps.crafterLanes ?? []).map((l) =>
172
+ ServerContract.Types.crafter_lane.from({
173
+ slot_index: l.slotIndex,
174
+ speed: l.speed,
175
+ drain: l.drain,
176
+ output_pct: l.outputPct,
177
+ })
178
+ )
179
+ info.builder_lanes = (caps.builderLanes ?? []).map((l) =>
180
+ ServerContract.Types.builder_lane.from({
181
+ slot_index: l.slotIndex,
182
+ speed: l.speed,
183
+ drain: l.drain,
184
+ output_pct: l.outputPct,
185
+ })
186
+ )
187
+ info.loader_lanes = (caps.loaderLanes ?? []).map((l) =>
188
+ ServerContract.Types.loader_lane.from({
189
+ slot_index: l.slotIndex,
190
+ mass: l.mass,
191
+ thrust: l.thrust,
192
+ output_pct: l.outputPct,
193
+ })
194
+ )
169
195
  }
170
196
 
171
- info.modules = moduleEntries
197
+ if (!info.gatherer_lanes) info.gatherer_lanes = []
198
+ if (!info.crafter_lanes) info.crafter_lanes = []
199
+ if (!info.builder_lanes) info.builder_lanes = []
200
+ if (!info.loader_lanes) info.loader_lanes = []
172
201
 
173
202
  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)
203
+ return new Entity(entityInfo)
196
204
  }
@@ -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
+ }