@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
@@ -3,6 +3,7 @@ import type {
3
3
  BoundingBox,
4
4
  BoundsDeltaMessage,
5
5
  ClientMessage,
6
+ EntityDeletedMessage,
6
7
  ServerMessage,
7
8
  SnapshotMessage,
8
9
  SubscribeEntityMessage,
@@ -13,12 +14,10 @@ import type {
13
14
  WireEntity,
14
15
  } from './types'
15
16
  import {mapEntity, parseWireEntity} from './mappers'
16
- import type {Ship} from '../entities/ship'
17
- import type {Warehouse} from '../entities/warehouse'
18
- import type {Container} from '../entities/container'
17
+ import type {Entity} from '../entities/entity'
19
18
 
20
- export type SubscriptionEntityType = 'ship' | 'warehouse' | 'container'
21
- export type EntityInstance = Ship | Warehouse | Container
19
+ export type SubscriptionEntityType = 'ship' | 'warehouse' | 'container' | 'nexus'
20
+ export type EntityInstance = Entity
22
21
 
23
22
  export interface SubscriptionsOptions {
24
23
  url: string
@@ -27,44 +26,73 @@ export interface SubscriptionsOptions {
27
26
  pongTimeoutMs?: number
28
27
  }
29
28
 
30
- export interface BoundsSubscriptionHandle {
29
+ export type ExactEntitySubscriptionFilter = {
30
+ id: string | number
31
+ owner?: never
32
+ bounds?: never
33
+ prioritizeOwner?: never
34
+ }
35
+
36
+ export type BroadEntitySubscriptionFilter = {
37
+ id?: undefined
38
+ owner?: string
39
+ bounds?: BoundingBox
40
+ prioritizeOwner?: string
41
+ }
42
+
43
+ export type EntitySubscriptionFilter = ExactEntitySubscriptionFilter | BroadEntitySubscriptionFilter
44
+
45
+ export interface EntitySubscriptionMeta {
46
+ seq?: number
47
+ truncated?: boolean
48
+ }
49
+
50
+ export interface EntitySubscriptionHandlers {
51
+ onSnapshot?: (entities: EntityInstance[], meta: EntitySubscriptionMeta) => void
52
+ onUpdate?: (entity: EntityInstance, meta: EntitySubscriptionMeta) => void
53
+ onBoundsDelta?: (
54
+ entered: EntityInstance[],
55
+ exited: number[],
56
+ meta: EntitySubscriptionMeta
57
+ ) => void
58
+ onDeleted?: (id: string, meta: EntitySubscriptionMeta) => void
59
+ onError?: (error: Error) => void
60
+ }
61
+
62
+ export interface EntitiesSubscriptionHandle {
31
63
  readonly subId: string
64
+ readonly filter: EntitySubscriptionFilter
32
65
  unsubscribe(): void
33
- updateBounds(bounds: BoundingBox): void
34
66
  current: Map<number, EntityInstance>
35
67
  }
36
68
 
37
- export interface EntitySubscriptionHandle {
38
- readonly subId: string
39
- readonly entityType: SubscriptionEntityType
40
- readonly entityId: string
41
- unsubscribe(): void
42
- current: EntityInstance | null
69
+ export type BoundsSubscriptionHandle = EntitiesSubscriptionHandle & {
70
+ readonly filter: BroadEntitySubscriptionFilter & {bounds: BoundingBox}
71
+ updateBounds(bounds: BoundingBox): void
72
+ }
73
+ export type OwnerSubscriptionHandle = EntitiesSubscriptionHandle & {
74
+ readonly filter: BroadEntitySubscriptionFilter
75
+ }
76
+ export type EntitySubscriptionHandle = EntitiesSubscriptionHandle & {
77
+ readonly filter: ExactEntitySubscriptionFilter
78
+ }
79
+
80
+ type EntitiesSubscriptionEntry = {
81
+ filter: InternalEntitySubscriptionFilter
82
+ handlers: EntitySubscriptionHandlers
83
+ handle: EntitiesSubscriptionHandle
84
+ }
85
+
86
+ type InternalEntitySubscriptionFilter = {
87
+ id?: string | number
88
+ owner?: string
89
+ bounds?: BoundingBox
90
+ prioritizeOwner?: string
43
91
  }
44
92
 
45
93
  export class SubscriptionsManager {
46
94
  private readonly conn: WebSocketConnection
47
- private readonly entitySubs = new Map<
48
- string,
49
- {
50
- type: SubscriptionEntityType
51
- id: string
52
- onUpdate: (e: EntityInstance) => void
53
- handle: EntitySubscriptionHandle
54
- }
55
- >()
56
- private readonly boundsSubs = new Map<
57
- string,
58
- {
59
- bounds?: BoundingBox
60
- owner?: string
61
- prioritizeOwner?: string
62
- onSnapshot?: (entities: EntityInstance[]) => void
63
- onUpdate?: (entity: EntityInstance) => void
64
- onBoundsDelta?: (entered: EntityInstance[], exited: number[]) => void
65
- handle: BoundsSubscriptionHandle
66
- }
67
- >()
95
+ private readonly entitySubs = new Map<string, EntitiesSubscriptionEntry>()
68
96
  private subCounter = 0
69
97
  private hasConnected = false
70
98
 
@@ -93,83 +121,162 @@ export class SubscriptionsManager {
93
121
  this.conn.send(msg)
94
122
  }
95
123
 
96
- subscribeEntity(
97
- type: SubscriptionEntityType,
98
- id: string,
99
- onUpdate: (e: EntityInstance) => void
100
- ): EntitySubscriptionHandle {
101
- const subId = this.generateSubID('ent')
102
- const msg: SubscribeEntityMessage = {
103
- type: 'subscribe_entity',
104
- sub_id: subId,
105
- entity_type: type,
106
- entity_id: id,
107
- }
108
- const handle: EntitySubscriptionHandle = {
124
+ subscribeEntities(
125
+ filter: BroadEntitySubscriptionFilter & {bounds: BoundingBox},
126
+ handlers?: EntitySubscriptionHandlers
127
+ ): BoundsSubscriptionHandle
128
+ subscribeEntities(
129
+ filter: ExactEntitySubscriptionFilter,
130
+ handlers?: EntitySubscriptionHandlers
131
+ ): EntitySubscriptionHandle
132
+ subscribeEntities(
133
+ filter: BroadEntitySubscriptionFilter,
134
+ handlers?: EntitySubscriptionHandlers
135
+ ): EntitiesSubscriptionHandle
136
+ subscribeEntities(
137
+ filter: EntitySubscriptionFilter,
138
+ handlers?: EntitySubscriptionHandlers
139
+ ): EntitiesSubscriptionHandle
140
+ subscribeEntities(
141
+ filter: EntitySubscriptionFilter,
142
+ handlers: EntitySubscriptionHandlers = {}
143
+ ): EntitiesSubscriptionHandle {
144
+ const storedFilter = this.normalizeFilter(filter)
145
+ const subId = this.generateSubID(this.subscriptionPrefix(storedFilter))
146
+ const handle: EntitiesSubscriptionHandle = {
109
147
  subId,
110
- entityType: type,
111
- entityId: id,
112
- unsubscribe: () => this.unsubscribeEntity(subId),
113
- current: null,
148
+ get filter() {
149
+ return SubscriptionsManager.publicFilter(storedFilter)
150
+ },
151
+ unsubscribe: () => this.unsubscribeEntities(subId),
152
+ current: new Map(),
114
153
  }
115
- this.entitySubs.set(subId, {type, id, onUpdate, handle})
116
- this.sendMessage(msg)
154
+ if (storedFilter.id === undefined && storedFilter.bounds) {
155
+ ;(handle as BoundsSubscriptionHandle).updateBounds = (bounds) =>
156
+ this.updateBounds(subId, bounds)
157
+ }
158
+
159
+ this.entitySubs.set(subId, {filter: storedFilter, handlers, handle})
160
+ this.sendMessage(this.subscribeMessage(subId, storedFilter))
117
161
  return handle
118
162
  }
119
163
 
120
- private unsubscribeEntity(subId: string) {
121
- const entry = this.entitySubs.get(subId)
122
- if (!entry) return
123
- this.entitySubs.delete(subId)
124
- const msg: UnsubscribeEntityMessage = {type: 'unsubscribe_entity', sub_id: subId}
125
- this.sendMessage(msg)
164
+ subscribeEntity(
165
+ id: string | number,
166
+ handlers: EntitySubscriptionHandlers
167
+ ): EntitySubscriptionHandle {
168
+ return this.subscribeEntities({id}, handlers)
169
+ }
170
+
171
+ subscribeOwner(
172
+ owner: string,
173
+ handlers: EntitySubscriptionHandlers = {}
174
+ ): OwnerSubscriptionHandle {
175
+ return this.subscribeEntities({owner}, handlers) as OwnerSubscriptionHandle
126
176
  }
127
177
 
128
178
  subscribeBounds(
129
179
  bounds: BoundingBox,
130
- handlers: {
131
- onSnapshot?: (entities: EntityInstance[]) => void
132
- onUpdate?: (entity: EntityInstance) => void
133
- onBoundsDelta?: (entered: EntityInstance[], exited: number[]) => void
180
+ handlers: EntitySubscriptionHandlers & {
134
181
  owner?: string
135
182
  prioritizeOwner?: string
136
- }
183
+ } = {}
137
184
  ): BoundsSubscriptionHandle {
138
- const subId = this.generateSubID('bnd')
139
- const msg: SubscribeMessage = {
185
+ return this.subscribeEntities(
186
+ {bounds, owner: handlers.owner, prioritizeOwner: handlers.prioritizeOwner},
187
+ handlers
188
+ )
189
+ }
190
+
191
+ subscribeAllEntities(handlers: EntitySubscriptionHandlers = {}): EntitiesSubscriptionHandle {
192
+ return this.subscribeEntities({}, handlers)
193
+ }
194
+
195
+ private normalizeFilter(filter: EntitySubscriptionFilter): InternalEntitySubscriptionFilter {
196
+ const raw = filter as InternalEntitySubscriptionFilter
197
+ if (
198
+ raw.id !== undefined &&
199
+ (raw.owner !== undefined ||
200
+ raw.bounds !== undefined ||
201
+ raw.prioritizeOwner !== undefined)
202
+ ) {
203
+ throw new Error(
204
+ 'Exact entity subscription filters cannot include owner, bounds, or prioritizeOwner'
205
+ )
206
+ }
207
+ if (raw.id !== undefined) {
208
+ return {id: raw.id}
209
+ }
210
+ return {
211
+ owner: raw.owner,
212
+ bounds: raw.bounds ? this.cloneBounds(raw.bounds) : undefined,
213
+ prioritizeOwner: raw.prioritizeOwner,
214
+ }
215
+ }
216
+
217
+ private static publicFilter(
218
+ filter: InternalEntitySubscriptionFilter
219
+ ): EntitySubscriptionFilter {
220
+ if (filter.id !== undefined) {
221
+ return Object.freeze({id: filter.id}) as ExactEntitySubscriptionFilter
222
+ }
223
+
224
+ return Object.freeze({
225
+ owner: filter.owner,
226
+ bounds: filter.bounds ? (Object.freeze({...filter.bounds}) as BoundingBox) : undefined,
227
+ prioritizeOwner: filter.prioritizeOwner,
228
+ }) as BroadEntitySubscriptionFilter
229
+ }
230
+
231
+ private cloneBounds(bounds: BoundingBox): BoundingBox {
232
+ return {...bounds}
233
+ }
234
+
235
+ private subscriptionPrefix(filter: InternalEntitySubscriptionFilter): string {
236
+ if (filter.id !== undefined) return 'ent'
237
+ if (filter.bounds) return 'bnd'
238
+ if (filter.owner) return 'own'
239
+ return 'all'
240
+ }
241
+
242
+ private subscribeMessage(
243
+ subId: string,
244
+ filter: InternalEntitySubscriptionFilter
245
+ ): SubscribeEntityMessage | SubscribeMessage {
246
+ if (filter.id !== undefined) {
247
+ return {
248
+ type: 'subscribe_entity',
249
+ sub_id: subId,
250
+ entity_id: String(filter.id),
251
+ }
252
+ }
253
+
254
+ return {
140
255
  type: 'subscribe',
141
256
  sub_id: subId,
142
- bounds,
143
- owner: handlers.owner,
144
- prioritize_owner: handlers.prioritizeOwner,
145
- }
146
- const handle: BoundsSubscriptionHandle = {
147
- subId,
148
- unsubscribe: () => this.unsubscribeBounds(subId),
149
- updateBounds: (b) => this.updateBounds(subId, b),
150
- current: new Map(),
257
+ owner: filter.owner,
258
+ bounds: filter.bounds,
259
+ prioritize_owner: filter.prioritizeOwner,
151
260
  }
152
- this.boundsSubs.set(subId, {
153
- bounds,
154
- owner: handlers.owner,
155
- prioritizeOwner: handlers.prioritizeOwner,
156
- onSnapshot: handlers.onSnapshot,
157
- onUpdate: handlers.onUpdate,
158
- onBoundsDelta: handlers.onBoundsDelta,
159
- handle,
160
- })
161
- this.sendMessage(msg)
162
- return handle
163
261
  }
164
262
 
165
- private unsubscribeBounds(subId: string) {
166
- this.boundsSubs.delete(subId)
263
+ private unsubscribeEntities(subId: string) {
264
+ const entry = this.entitySubs.get(subId)
265
+ if (!entry) return
266
+ this.entitySubs.delete(subId)
267
+ if (entry.filter.id !== undefined) {
268
+ const msg: UnsubscribeEntityMessage = {type: 'unsubscribe_entity', sub_id: subId}
269
+ this.sendMessage(msg)
270
+ return
271
+ }
167
272
  this.sendMessage({type: 'unsubscribe', sub_id: subId})
168
273
  }
169
274
 
170
275
  private updateBounds(subId: string, bounds: BoundingBox) {
171
- const entry = this.boundsSubs.get(subId)
172
- if (entry) entry.bounds = bounds
276
+ const entry = this.entitySubs.get(subId)
277
+ if (!entry) return
278
+ if (entry.filter.id !== undefined || !entry.filter.bounds) return
279
+ entry.filter.bounds = this.cloneBounds(bounds)
173
280
  const msg: UpdateBoundsMessage = {type: 'update_bounds', sub_id: subId, bounds}
174
281
  this.sendMessage(msg)
175
282
  }
@@ -181,23 +288,7 @@ export class SubscriptionsManager {
181
288
  return
182
289
  }
183
290
  for (const [subId, entry] of this.entitySubs) {
184
- const msg: SubscribeEntityMessage = {
185
- type: 'subscribe_entity',
186
- sub_id: subId,
187
- entity_type: entry.type,
188
- entity_id: entry.id,
189
- }
190
- this.sendMessage(msg)
191
- }
192
- for (const [subId, entry] of this.boundsSubs) {
193
- const msg: SubscribeMessage = {
194
- type: 'subscribe',
195
- sub_id: subId,
196
- bounds: entry.bounds,
197
- owner: entry.owner,
198
- prioritize_owner: entry.prioritizeOwner,
199
- }
200
- this.sendMessage(msg)
291
+ this.sendMessage(this.subscribeMessage(subId, entry.filter))
201
292
  }
202
293
  }
203
294
 
@@ -212,6 +303,9 @@ export class SubscriptionsManager {
212
303
  case 'bounds_delta':
213
304
  this.handleBoundsDelta(msg)
214
305
  break
306
+ case 'entity_deleted':
307
+ this.handleEntityDeleted(msg)
308
+ break
215
309
  case 'error':
216
310
  this.handleError(msg)
217
311
  break
@@ -224,60 +318,53 @@ export class SubscriptionsManager {
224
318
  }
225
319
 
226
320
  private handleSnapshot(msg: SnapshotMessage) {
227
- const entSub = this.entitySubs.get(msg.sub_id)
228
- if (entSub) {
229
- if (msg.entities.length > 0) {
230
- const ent = this.parseEntity(msg.entities[0])
231
- entSub.handle.current = ent
232
- entSub.onUpdate(ent)
233
- }
234
- return
235
- }
236
- const boundsSub = this.boundsSubs.get(msg.sub_id)
237
- if (boundsSub) {
238
- const ents = msg.entities.map((e) => this.parseEntity(e))
239
- boundsSub.handle.current.clear()
240
- for (const e of ents) boundsSub.handle.current.set(Number(e.id), e)
241
- boundsSub.onSnapshot?.(ents)
321
+ const sub = this.entitySubs.get(msg.sub_id)
322
+ if (!sub) return
323
+ const meta = {seq: msg.seq, truncated: msg.truncated === true}
324
+ const ents = msg.entities.map((e) => this.parseEntity(e))
325
+ sub.handle.current.clear()
326
+ for (const e of ents) sub.handle.current.set(Number(e.id), e)
327
+ sub.handlers.onSnapshot?.(ents, meta)
328
+ if (sub.filter.id !== undefined && ents[0]) {
329
+ sub.handlers.onUpdate?.(ents[0], {seq: msg.seq})
242
330
  }
243
331
  }
244
332
 
245
333
  private handleUpdate(msg: UpdateMessage) {
246
334
  const ent = this.parseEntity(msg.entity)
247
335
  for (const subId of msg.sub_ids) {
248
- const entSub = this.entitySubs.get(subId)
249
- if (entSub) {
250
- entSub.handle.current = ent
251
- entSub.onUpdate(ent)
252
- continue
253
- }
254
- const boundsSub = this.boundsSubs.get(subId)
255
- if (boundsSub) {
256
- boundsSub.handle.current.set(msg.entity_id, ent)
257
- boundsSub.onUpdate?.(ent)
258
- }
336
+ const sub = this.entitySubs.get(subId)
337
+ if (!sub) continue
338
+ sub.handle.current.set(msg.entity_id, ent)
339
+ sub.handlers.onUpdate?.(ent, {seq: msg.seq})
259
340
  }
260
341
  }
261
342
 
262
343
  private handleBoundsDelta(msg: BoundsDeltaMessage) {
263
- const sub = this.boundsSubs.get(msg.sub_id)
344
+ const sub = this.entitySubs.get(msg.sub_id)
264
345
  if (!sub) return
346
+ const meta = {seq: msg.seq, truncated: msg.truncated === true}
265
347
  const entered = msg.entered.map((e) => this.parseEntity(e))
266
348
  for (const e of entered) sub.handle.current.set(Number(e.id), e)
267
349
  for (const id of msg.exited) sub.handle.current.delete(id)
268
- sub.onBoundsDelta?.(entered, msg.exited)
350
+ sub.handlers.onBoundsDelta?.(entered, msg.exited, meta)
269
351
  }
270
352
 
271
353
  private handleError(msg: {sub_id?: string; error: string}) {
272
354
  if (!msg.sub_id) return
273
- const entSub = this.entitySubs.get(msg.sub_id)
274
- if (entSub) {
355
+ const sub = this.entitySubs.get(msg.sub_id)
356
+ if (!sub) return
357
+ this.entitySubs.delete(msg.sub_id)
358
+ sub.handlers.onError?.(new Error(msg.error))
359
+ }
360
+
361
+ private handleEntityDeleted(msg: EntityDeletedMessage) {
362
+ const sub = this.entitySubs.get(msg.sub_id)
363
+ if (!sub) return
364
+ sub.handle.current.delete(msg.entity_id)
365
+ if (sub.filter.id !== undefined) {
275
366
  this.entitySubs.delete(msg.sub_id)
276
- return
277
- }
278
- const boundsSub = this.boundsSubs.get(msg.sub_id)
279
- if (boundsSub) {
280
- this.boundsSubs.delete(msg.sub_id)
281
367
  }
368
+ sub.handlers.onDeleted?.(String(msg.entity_id), {seq: msg.seq})
282
369
  }
283
370
  }
@@ -1,14 +1,9 @@
1
1
  import {ServerContract} from '../contracts'
2
- import {Ship} from '../entities/ship'
3
- import {Warehouse} from '../entities/warehouse'
4
- import {Container} from '../entities/container'
2
+ import {Entity} from '../entities/entity'
5
3
  import type {WireEntity} from './types'
6
4
 
7
- export function mapEntity(ei: ServerContract.Types.entity_info): Ship | Warehouse | Container {
8
- if (ei.type.equals('ship')) return new Ship(ei)
9
- if (ei.type.equals('warehouse')) return new Warehouse(ei)
10
- if (ei.type.equals('container')) return new Container(ei)
11
- throw new Error(`mapEntity: unknown entity type ${ei.type.toString()}`)
5
+ export function mapEntity(ei: ServerContract.Types.entity_info): Entity {
6
+ return new Entity(ei)
12
7
  }
13
8
 
14
9
  export function parseWireEntity(raw: WireEntity): ServerContract.Types.entity_info {
@@ -24,5 +19,7 @@ export function parseWireEntity(raw: WireEntity): ServerContract.Types.entity_in
24
19
  }
25
20
  delete shaped.name
26
21
 
22
+ if (shaped.holds === undefined) shaped.holds = []
23
+
27
24
  return ServerContract.Types.entity_info.from(shaped)
28
25
  }
@@ -39,7 +39,6 @@ export type UnsubscribeMessage = {
39
39
  export type SubscribeEntityMessage = {
40
40
  type: 'subscribe_entity'
41
41
  sub_id: string
42
- entity_type: 'ship' | 'warehouse' | 'container'
43
42
  entity_id: string
44
43
  }
45
44
 
@@ -79,11 +78,12 @@ export type AckMessage = {
79
78
  }
80
79
 
81
80
  export type WireEntity = Record<string, unknown> & {
82
- type: number
83
- type_name: 'ship' | 'warehouse' | 'container'
81
+ type: number | string
82
+ type_name?: string
84
83
  id: string | number
85
84
  owner: string
86
85
  coordinates: WireCoordinates
86
+ item_id: number
87
87
  }
88
88
 
89
89
  export type SnapshotMessage = {
@@ -111,6 +111,13 @@ export type BoundsDeltaMessage = {
111
111
  truncated?: boolean
112
112
  }
113
113
 
114
+ export type EntityDeletedMessage = {
115
+ type: 'entity_deleted'
116
+ sub_id: string
117
+ entity_id: number
118
+ seq: number
119
+ }
120
+
114
121
  export type EventMessage = {
115
122
  type: 'event'
116
123
  sub_id: string
@@ -137,6 +144,7 @@ export type ServerMessage =
137
144
  | SnapshotMessage
138
145
  | UpdateMessage
139
146
  | BoundsDeltaMessage
147
+ | EntityDeletedMessage
140
148
  | EventMessage
141
149
  | EventCatchupCompleteMessage
142
150
  | PongMessage
@@ -0,0 +1,19 @@
1
+ import {createHash} from 'node:crypto'
2
+ import {readFileSync} from 'node:fs'
3
+
4
+ export const CATALOG_FILES_REL = [
5
+ 'items.json',
6
+ 'recipes.json',
7
+ 'entities.json',
8
+ 'kind-registry.json',
9
+ 'item-ids.ts',
10
+ ] as const
11
+
12
+ export function computeCatalogHash(filePaths: ReadonlyArray<string>): string {
13
+ const hash = createHash('sha256')
14
+ for (const p of filePaths) {
15
+ hash.update(readFileSync(p))
16
+ hash.update('\0')
17
+ }
18
+ return hash.digest('hex')
19
+ }
@@ -0,0 +1,2 @@
1
+ export * from './catalog-hash'
2
+ export * from './projection-parity'