@shipload/sdk 1.0.0-next.33 → 1.0.0-next.35
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.
- package/lib/shipload.d.ts +271 -33
- package/lib/shipload.js +862 -134
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +843 -135
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +42 -0
- package/lib/testing.js +128 -1
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +128 -1
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/contracts/server.ts +120 -1
- package/src/coordinates/address.ts +84 -0
- package/src/coordinates/constants.ts +21 -0
- package/src/coordinates/index.ts +4 -0
- package/src/coordinates/permutation.ts +77 -0
- package/src/coordinates/regions.ts +48 -0
- package/src/coordinates/sectors.ts +115 -0
- package/src/derivation/wormhole.ts +115 -0
- package/src/errors.ts +2 -0
- package/src/index-module.ts +23 -0
- package/src/managers/actions.ts +51 -2
- package/src/managers/construction-types.ts +1 -0
- package/src/managers/construction.ts +2 -1
- package/src/scheduling/availability.ts +1 -1
- package/src/scheduling/cancel.test.ts +327 -0
- package/src/scheduling/cancel.ts +209 -0
- package/src/scheduling/idle-resolve.ts +5 -4
- package/src/scheduling/projection.ts +2 -0
- package/src/scheduling/schedule.ts +3 -1
- package/src/subscriptions/manager.ts +220 -167
- package/src/subscriptions/types.ts +10 -3
- package/src/travel/travel.ts +14 -4
- package/src/types.ts +1 -0
- package/src/utils/system.ts +11 -0
|
@@ -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,
|
|
@@ -25,50 +26,73 @@ export interface SubscriptionsOptions {
|
|
|
25
26
|
pongTimeoutMs?: number
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
33
60
|
}
|
|
34
61
|
|
|
35
|
-
export interface
|
|
62
|
+
export interface EntitiesSubscriptionHandle {
|
|
36
63
|
readonly subId: string
|
|
64
|
+
readonly filter: EntitySubscriptionFilter
|
|
37
65
|
unsubscribe(): void
|
|
38
66
|
current: Map<number, EntityInstance>
|
|
39
67
|
}
|
|
40
68
|
|
|
41
|
-
export
|
|
42
|
-
readonly
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
47
91
|
}
|
|
48
92
|
|
|
49
93
|
export class SubscriptionsManager {
|
|
50
94
|
private readonly conn: WebSocketConnection
|
|
51
|
-
private readonly entitySubs = new Map<
|
|
52
|
-
string,
|
|
53
|
-
{
|
|
54
|
-
type: SubscriptionEntityType
|
|
55
|
-
id: string
|
|
56
|
-
onUpdate: (e: EntityInstance) => void
|
|
57
|
-
handle: EntitySubscriptionHandle
|
|
58
|
-
}
|
|
59
|
-
>()
|
|
60
|
-
private readonly boundsSubs = new Map<
|
|
61
|
-
string,
|
|
62
|
-
{
|
|
63
|
-
bounds?: BoundingBox
|
|
64
|
-
owner?: string
|
|
65
|
-
prioritizeOwner?: string
|
|
66
|
-
onSnapshot?: (entities: EntityInstance[]) => void
|
|
67
|
-
onUpdate?: (entity: EntityInstance) => void
|
|
68
|
-
onBoundsDelta?: (entered: EntityInstance[], exited: number[]) => void
|
|
69
|
-
handle: BoundsSubscriptionHandle | OwnerSubscriptionHandle
|
|
70
|
-
}
|
|
71
|
-
>()
|
|
95
|
+
private readonly entitySubs = new Map<string, EntitiesSubscriptionEntry>()
|
|
72
96
|
private subCounter = 0
|
|
73
97
|
private hasConnected = false
|
|
74
98
|
|
|
@@ -97,113 +121,162 @@ export class SubscriptionsManager {
|
|
|
97
121
|
this.conn.send(msg)
|
|
98
122
|
}
|
|
99
123
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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 = {
|
|
113
147
|
subId,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
148
|
+
get filter() {
|
|
149
|
+
return SubscriptionsManager.publicFilter(storedFilter)
|
|
150
|
+
},
|
|
151
|
+
unsubscribe: () => this.unsubscribeEntities(subId),
|
|
152
|
+
current: new Map(),
|
|
118
153
|
}
|
|
119
|
-
|
|
120
|
-
|
|
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))
|
|
121
161
|
return handle
|
|
122
162
|
}
|
|
123
163
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
130
176
|
}
|
|
131
177
|
|
|
132
178
|
subscribeBounds(
|
|
133
179
|
bounds: BoundingBox,
|
|
134
|
-
handlers: {
|
|
135
|
-
onSnapshot?: (entities: EntityInstance[]) => void
|
|
136
|
-
onUpdate?: (entity: EntityInstance) => void
|
|
137
|
-
onBoundsDelta?: (entered: EntityInstance[], exited: number[]) => void
|
|
180
|
+
handlers: EntitySubscriptionHandlers & {
|
|
138
181
|
owner?: string
|
|
139
182
|
prioritizeOwner?: string
|
|
140
|
-
}
|
|
183
|
+
} = {}
|
|
141
184
|
): BoundsSubscriptionHandle {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
+
)
|
|
149
206
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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,
|
|
155
214
|
}
|
|
156
|
-
this.boundsSubs.set(subId, {
|
|
157
|
-
bounds,
|
|
158
|
-
owner: handlers.owner,
|
|
159
|
-
prioritizeOwner: handlers.prioritizeOwner,
|
|
160
|
-
onSnapshot: handlers.onSnapshot,
|
|
161
|
-
onUpdate: handlers.onUpdate,
|
|
162
|
-
onBoundsDelta: handlers.onBoundsDelta,
|
|
163
|
-
handle,
|
|
164
|
-
})
|
|
165
|
-
this.sendMessage(msg)
|
|
166
|
-
return handle
|
|
167
215
|
}
|
|
168
216
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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 {
|
|
178
255
|
type: 'subscribe',
|
|
179
256
|
sub_id: subId,
|
|
180
|
-
owner,
|
|
257
|
+
owner: filter.owner,
|
|
258
|
+
bounds: filter.bounds,
|
|
259
|
+
prioritize_owner: filter.prioritizeOwner,
|
|
181
260
|
}
|
|
182
|
-
const handle: OwnerSubscriptionHandle = {
|
|
183
|
-
subId,
|
|
184
|
-
unsubscribe: () => this.unsubscribeBounds(subId),
|
|
185
|
-
current: new Map(),
|
|
186
|
-
}
|
|
187
|
-
this.boundsSubs.set(subId, {
|
|
188
|
-
bounds: undefined,
|
|
189
|
-
owner,
|
|
190
|
-
prioritizeOwner: undefined,
|
|
191
|
-
onSnapshot: handlers.onSnapshot,
|
|
192
|
-
onUpdate: handlers.onUpdate,
|
|
193
|
-
handle,
|
|
194
|
-
})
|
|
195
|
-
this.sendMessage(msg)
|
|
196
|
-
return handle
|
|
197
261
|
}
|
|
198
262
|
|
|
199
|
-
private
|
|
200
|
-
this.
|
|
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
|
+
}
|
|
201
272
|
this.sendMessage({type: 'unsubscribe', sub_id: subId})
|
|
202
273
|
}
|
|
203
274
|
|
|
204
275
|
private updateBounds(subId: string, bounds: BoundingBox) {
|
|
205
|
-
const entry = this.
|
|
206
|
-
if (entry)
|
|
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)
|
|
207
280
|
const msg: UpdateBoundsMessage = {type: 'update_bounds', sub_id: subId, bounds}
|
|
208
281
|
this.sendMessage(msg)
|
|
209
282
|
}
|
|
@@ -215,23 +288,7 @@ export class SubscriptionsManager {
|
|
|
215
288
|
return
|
|
216
289
|
}
|
|
217
290
|
for (const [subId, entry] of this.entitySubs) {
|
|
218
|
-
|
|
219
|
-
type: 'subscribe_entity',
|
|
220
|
-
sub_id: subId,
|
|
221
|
-
entity_type: entry.type,
|
|
222
|
-
entity_id: entry.id,
|
|
223
|
-
}
|
|
224
|
-
this.sendMessage(msg)
|
|
225
|
-
}
|
|
226
|
-
for (const [subId, entry] of this.boundsSubs) {
|
|
227
|
-
const msg: SubscribeMessage = {
|
|
228
|
-
type: 'subscribe',
|
|
229
|
-
sub_id: subId,
|
|
230
|
-
bounds: entry.bounds,
|
|
231
|
-
owner: entry.owner,
|
|
232
|
-
prioritize_owner: entry.prioritizeOwner,
|
|
233
|
-
}
|
|
234
|
-
this.sendMessage(msg)
|
|
291
|
+
this.sendMessage(this.subscribeMessage(subId, entry.filter))
|
|
235
292
|
}
|
|
236
293
|
}
|
|
237
294
|
|
|
@@ -246,6 +303,9 @@ export class SubscriptionsManager {
|
|
|
246
303
|
case 'bounds_delta':
|
|
247
304
|
this.handleBoundsDelta(msg)
|
|
248
305
|
break
|
|
306
|
+
case 'entity_deleted':
|
|
307
|
+
this.handleEntityDeleted(msg)
|
|
308
|
+
break
|
|
249
309
|
case 'error':
|
|
250
310
|
this.handleError(msg)
|
|
251
311
|
break
|
|
@@ -258,60 +318,53 @@ export class SubscriptionsManager {
|
|
|
258
318
|
}
|
|
259
319
|
|
|
260
320
|
private handleSnapshot(msg: SnapshotMessage) {
|
|
261
|
-
const
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const boundsSub = this.boundsSubs.get(msg.sub_id)
|
|
271
|
-
if (boundsSub) {
|
|
272
|
-
const ents = msg.entities.map((e) => this.parseEntity(e))
|
|
273
|
-
boundsSub.handle.current.clear()
|
|
274
|
-
for (const e of ents) boundsSub.handle.current.set(Number(e.id), e)
|
|
275
|
-
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})
|
|
276
330
|
}
|
|
277
331
|
}
|
|
278
332
|
|
|
279
333
|
private handleUpdate(msg: UpdateMessage) {
|
|
280
334
|
const ent = this.parseEntity(msg.entity)
|
|
281
335
|
for (const subId of msg.sub_ids) {
|
|
282
|
-
const
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
continue
|
|
287
|
-
}
|
|
288
|
-
const boundsSub = this.boundsSubs.get(subId)
|
|
289
|
-
if (boundsSub) {
|
|
290
|
-
boundsSub.handle.current.set(msg.entity_id, ent)
|
|
291
|
-
boundsSub.onUpdate?.(ent)
|
|
292
|
-
}
|
|
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})
|
|
293
340
|
}
|
|
294
341
|
}
|
|
295
342
|
|
|
296
343
|
private handleBoundsDelta(msg: BoundsDeltaMessage) {
|
|
297
|
-
const sub = this.
|
|
344
|
+
const sub = this.entitySubs.get(msg.sub_id)
|
|
298
345
|
if (!sub) return
|
|
346
|
+
const meta = {seq: msg.seq, truncated: msg.truncated === true}
|
|
299
347
|
const entered = msg.entered.map((e) => this.parseEntity(e))
|
|
300
348
|
for (const e of entered) sub.handle.current.set(Number(e.id), e)
|
|
301
349
|
for (const id of msg.exited) sub.handle.current.delete(id)
|
|
302
|
-
sub.onBoundsDelta?.(entered, msg.exited)
|
|
350
|
+
sub.handlers.onBoundsDelta?.(entered, msg.exited, meta)
|
|
303
351
|
}
|
|
304
352
|
|
|
305
353
|
private handleError(msg: {sub_id?: string; error: string}) {
|
|
306
354
|
if (!msg.sub_id) return
|
|
307
|
-
const
|
|
308
|
-
if (
|
|
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) {
|
|
309
366
|
this.entitySubs.delete(msg.sub_id)
|
|
310
|
-
return
|
|
311
|
-
}
|
|
312
|
-
const boundsSub = this.boundsSubs.get(msg.sub_id)
|
|
313
|
-
if (boundsSub) {
|
|
314
|
-
this.boundsSubs.delete(msg.sub_id)
|
|
315
367
|
}
|
|
368
|
+
sub.handlers.onDeleted?.(String(msg.entity_id), {seq: msg.seq})
|
|
316
369
|
}
|
|
317
370
|
}
|
|
@@ -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' | 'nexus'
|
|
43
42
|
entity_id: string
|
|
44
43
|
}
|
|
45
44
|
|
|
@@ -79,8 +78,8 @@ export type AckMessage = {
|
|
|
79
78
|
}
|
|
80
79
|
|
|
81
80
|
export type WireEntity = Record<string, unknown> & {
|
|
82
|
-
type: number
|
|
83
|
-
type_name
|
|
81
|
+
type: number | string
|
|
82
|
+
type_name?: string
|
|
84
83
|
id: string | number
|
|
85
84
|
owner: string
|
|
86
85
|
coordinates: WireCoordinates
|
|
@@ -112,6 +111,13 @@ export type BoundsDeltaMessage = {
|
|
|
112
111
|
truncated?: boolean
|
|
113
112
|
}
|
|
114
113
|
|
|
114
|
+
export type EntityDeletedMessage = {
|
|
115
|
+
type: 'entity_deleted'
|
|
116
|
+
sub_id: string
|
|
117
|
+
entity_id: number
|
|
118
|
+
seq: number
|
|
119
|
+
}
|
|
120
|
+
|
|
115
121
|
export type EventMessage = {
|
|
116
122
|
type: 'event'
|
|
117
123
|
sub_id: string
|
|
@@ -138,6 +144,7 @@ export type ServerMessage =
|
|
|
138
144
|
| SnapshotMessage
|
|
139
145
|
| UpdateMessage
|
|
140
146
|
| BoundsDeltaMessage
|
|
147
|
+
| EntityDeletedMessage
|
|
141
148
|
| EventMessage
|
|
142
149
|
| EventCatchupCompleteMessage
|
|
143
150
|
| PongMessage
|
package/src/travel/travel.ts
CHANGED
|
@@ -37,9 +37,14 @@ import {
|
|
|
37
37
|
import {EntityClass} from '../data/kind-registry'
|
|
38
38
|
import {getItem} from '../data/catalog'
|
|
39
39
|
import {hasSystem} from '../utils/system'
|
|
40
|
+
import {WH} from '../derivation/wormhole'
|
|
40
41
|
import * as scheduleModel from '../scheduling/schedule'
|
|
41
42
|
import type {ScheduleData} from '../scheduling/schedule'
|
|
42
43
|
|
|
44
|
+
function isPositionalTask(task: ServerContract.Types.task): boolean {
|
|
45
|
+
return task.type.equals(TaskType.TRAVEL) || task.type.equals(TaskType.TRANSIT)
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
export function calc_orbital_altitude(mass: number): number {
|
|
44
49
|
if (mass <= BASE_ORBITAL_MASS) {
|
|
45
50
|
return MIN_ORBITAL_ALTITUDE
|
|
@@ -125,7 +130,7 @@ export function getInterpolatedPosition(
|
|
|
125
130
|
return {x: Number(settled.x), y: Number(settled.y)}
|
|
126
131
|
}
|
|
127
132
|
const task = tasks[taskIndex]
|
|
128
|
-
if (!task
|
|
133
|
+
if (!isPositionalTask(task) || !task.coordinates) {
|
|
129
134
|
const origin = getFlightOrigin(entity, taskIndex)
|
|
130
135
|
return {x: Number(origin.x), y: Number(origin.y)}
|
|
131
136
|
}
|
|
@@ -198,6 +203,11 @@ export function calc_flighttime(distance: UInt64Type, acceleration: number): UIn
|
|
|
198
203
|
return UInt32.from(2 * Math.sqrt(Number(distance) / acceleration))
|
|
199
204
|
}
|
|
200
205
|
|
|
206
|
+
export function calc_transit_duration(ax: number, ay: number, bx: number, by: number): UInt32 {
|
|
207
|
+
const distance = distanceBetweenPoints(ax, ay, bx, by)
|
|
208
|
+
return UInt32.from(Math.floor(distance.toNumber() / (PRECISION * WH.TRANSIT_SPEED)))
|
|
209
|
+
}
|
|
210
|
+
|
|
201
211
|
export function calc_loader_flighttime(ship: ShipLike, mass: UInt64, altitude?: number): UInt32 {
|
|
202
212
|
const z = altitude ?? ship.coordinates.z?.toNumber() ?? calc_orbital_altitude(Number(mass))
|
|
203
213
|
return calc_flighttime(z, calc_loader_acceleration(ship, mass))
|
|
@@ -449,7 +459,7 @@ export function getFlightOrigin(
|
|
|
449
459
|
let origin = entity.coordinates
|
|
450
460
|
for (let i = 0; i < flightTaskIndex && i < tasks.length; i++) {
|
|
451
461
|
const task = tasks[i]
|
|
452
|
-
if (task
|
|
462
|
+
if (isPositionalTask(task) && task.coordinates) {
|
|
453
463
|
origin = task.coordinates
|
|
454
464
|
}
|
|
455
465
|
}
|
|
@@ -462,7 +472,7 @@ export function getDestinationLocation(
|
|
|
462
472
|
const tasks = mobilityTasks(entity)
|
|
463
473
|
for (let i = tasks.length - 1; i >= 0; i--) {
|
|
464
474
|
const task = tasks[i]
|
|
465
|
-
if (task
|
|
475
|
+
if (isPositionalTask(task) && task.coordinates) {
|
|
466
476
|
return task.coordinates
|
|
467
477
|
}
|
|
468
478
|
}
|
|
@@ -485,7 +495,7 @@ export function getPositionAt(
|
|
|
485
495
|
|
|
486
496
|
const task = tasks[taskIndex]
|
|
487
497
|
|
|
488
|
-
if (!task
|
|
498
|
+
if (!isPositionalTask(task) || !task.coordinates) {
|
|
489
499
|
return getFlightOrigin(entity, taskIndex)
|
|
490
500
|
}
|
|
491
501
|
|
package/src/types.ts
CHANGED
package/src/utils/system.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {hash512} from './hash'
|
|
|
3
3
|
import {Coordinates, type CoordinatesType, LocationType} from '../types'
|
|
4
4
|
import {ServerContract} from '../contracts'
|
|
5
5
|
import {deriveLocationSize} from '../derivation/location-size'
|
|
6
|
+
import {wormholeAt} from '../derivation/wormhole'
|
|
6
7
|
import syllables from '../data/syllables.json'
|
|
7
8
|
import nebulaAdjectives from '../data/nebula-adjectives.json'
|
|
8
9
|
import nebulaNouns from '../data/nebula-nouns.json'
|
|
@@ -110,6 +111,16 @@ export function hasSystem(gameSeed: Checksum256Type, coordinates: CoordinatesTyp
|
|
|
110
111
|
return getLocationType(gameSeed, coordinates) !== LocationType.EMPTY
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
export function getLocationKind(
|
|
115
|
+
gameSeed: Checksum256Type,
|
|
116
|
+
x: number,
|
|
117
|
+
y: number
|
|
118
|
+
): 'wormhole' | 'system' | 'empty' {
|
|
119
|
+
if (wormholeAt(gameSeed, x, y)) return 'wormhole'
|
|
120
|
+
if (hasSystem(gameSeed, {x, y})) return 'system'
|
|
121
|
+
return 'empty'
|
|
122
|
+
}
|
|
123
|
+
|
|
113
124
|
export function deriveLocationStatic(
|
|
114
125
|
gameSeed: Checksum256Type,
|
|
115
126
|
coordinates: CoordinatesType
|