@shipload/sdk 1.0.0-next.3 → 1.0.0-next.30
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 +1847 -962
- package/lib/shipload.js +9088 -4854
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +8957 -4805
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +856 -0
- package/lib/testing.js +3739 -0
- package/lib/testing.js.map +1 -0
- package/lib/testing.m.js +3733 -0
- package/lib/testing.m.js.map +1 -0
- package/package.json +15 -2
- package/src/capabilities/craftable.ts +51 -0
- package/src/capabilities/crafting.test.ts +7 -0
- package/src/capabilities/crafting.ts +3 -3
- package/src/capabilities/gathering.ts +17 -7
- package/src/capabilities/index.ts +0 -1
- package/src/capabilities/modules.ts +6 -0
- package/src/capabilities/storage.ts +16 -1
- package/src/contracts/platform.ts +231 -3
- package/src/contracts/server.ts +816 -471
- package/src/data/capabilities.ts +14 -329
- package/src/data/capability-formulas.ts +76 -0
- package/src/data/catalog.ts +0 -5
- package/src/data/colors.ts +14 -47
- package/src/data/entities.json +46 -10
- package/src/data/item-ids.ts +15 -12
- package/src/data/items.json +302 -38
- package/src/data/kind-registry.json +85 -0
- package/src/data/kind-registry.ts +150 -0
- package/src/data/metadata.ts +100 -31
- package/src/data/recipes-runtime.ts +3 -23
- package/src/data/recipes.json +250 -113
- package/src/derivation/build-methods.ts +45 -0
- package/src/derivation/capabilities.ts +415 -0
- package/src/derivation/capability-mappings.ts +117 -0
- package/src/derivation/crafting.ts +23 -24
- package/src/derivation/index.ts +17 -2
- package/src/derivation/reserve-regen.ts +34 -0
- package/src/derivation/resources.ts +125 -38
- package/src/derivation/stars.test.ts +51 -0
- package/src/derivation/stars.ts +15 -0
- package/src/derivation/stats.ts +6 -6
- package/src/derivation/stratum.ts +15 -19
- package/src/derivation/tiers.ts +28 -7
- package/src/entities/entity.ts +98 -0
- package/src/entities/gamestate.ts +3 -28
- package/src/entities/makers.ts +91 -136
- package/src/entities/slot-multiplier.ts +39 -0
- package/src/errors.ts +10 -15
- package/src/format.ts +26 -4
- package/src/index-module.ts +189 -47
- package/src/managers/actions.ts +252 -83
- package/src/managers/base.ts +6 -2
- package/src/managers/construction-types.ts +79 -0
- package/src/managers/construction.ts +396 -0
- package/src/managers/context.ts +11 -1
- package/src/managers/entities.ts +18 -66
- package/src/managers/epochs.ts +40 -0
- package/src/managers/index.ts +17 -1
- package/src/managers/locations.ts +25 -29
- package/src/managers/nft.ts +28 -0
- package/src/managers/plot.ts +127 -0
- package/src/nft/atomicassets.abi.json +1342 -0
- package/src/nft/atomicassets.ts +237 -0
- package/src/nft/atomicdata.ts +130 -0
- package/src/nft/buildImmutableData.ts +321 -0
- package/src/nft/description.ts +37 -15
- package/src/nft/index.ts +3 -0
- package/src/resolution/describe-module.ts +5 -8
- package/src/resolution/display-name.ts +38 -10
- package/src/resolution/resolve-item.ts +22 -20
- package/src/scheduling/accessor.ts +68 -22
- package/src/scheduling/availability.ts +108 -0
- package/src/scheduling/energy.ts +48 -0
- package/src/scheduling/lane-core.ts +130 -0
- package/src/scheduling/lanes.ts +60 -0
- package/src/scheduling/projection.ts +121 -94
- package/src/scheduling/schedule.ts +237 -103
- package/src/scheduling/task-cargo.ts +46 -0
- package/src/shipload.ts +16 -1
- package/src/subscriptions/manager.ts +40 -6
- package/src/subscriptions/mappers.ts +3 -8
- package/src/subscriptions/types.ts +3 -2
- package/src/testing/catalog-hash.ts +19 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/projection-parity.ts +143 -0
- package/src/travel/travel.ts +90 -13
- package/src/types/capabilities.ts +1 -0
- package/src/types/index.ts +0 -1
- package/src/types.ts +19 -12
- package/src/utils/cargo.ts +27 -0
- package/src/utils/display-name.ts +61 -0
- package/src/utils/system.ts +25 -24
- package/src/capabilities/loading.ts +0 -8
- package/src/entities/container.ts +0 -108
- package/src/entities/ship-deploy.ts +0 -258
- package/src/entities/ship.ts +0 -204
- package/src/entities/warehouse.ts +0 -119
- package/src/types/entity-traits.ts +0 -69
|
@@ -1,179 +1,313 @@
|
|
|
1
1
|
import type {ServerContract} from '../contracts'
|
|
2
2
|
import {TaskType} from '../types'
|
|
3
|
+
import * as core from './lane-core'
|
|
3
4
|
|
|
4
5
|
type Schedule = ServerContract.Types.schedule
|
|
5
6
|
type Task = ServerContract.Types.task
|
|
7
|
+
type Lane = ServerContract.Types.lane
|
|
8
|
+
|
|
9
|
+
export const LANE_MOBILITY = 0
|
|
10
|
+
export const LANE_BARRIER = 255
|
|
6
11
|
|
|
7
12
|
export interface ScheduleData {
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
13
|
+
lanes?: Lane[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface LaneView {
|
|
17
|
+
laneKey: number
|
|
18
|
+
schedule: Schedule
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
laneStartsIn,
|
|
23
|
+
currentTaskIndexForLane,
|
|
24
|
+
laneTaskComplete,
|
|
25
|
+
laneTaskInProgress,
|
|
26
|
+
laneCompletesAt,
|
|
27
|
+
currentTaskProgressFloatForLane,
|
|
28
|
+
} from './lane-core'
|
|
29
|
+
|
|
30
|
+
export function getLanes(entity: ScheduleData): LaneView[] {
|
|
31
|
+
const lanes = entity.lanes
|
|
32
|
+
if (!lanes || lanes.length === 0) return []
|
|
33
|
+
return lanes.map((l) => ({laneKey: l.lane_key.toNumber(), schedule: l.schedule}))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getLane(entity: ScheduleData, laneKey: number): LaneView | undefined {
|
|
37
|
+
const lanes = entity.lanes
|
|
38
|
+
if (!lanes) return undefined
|
|
39
|
+
for (const l of lanes) {
|
|
40
|
+
if (l.lane_key.toNumber() === laneKey) return {laneKey, schedule: l.schedule}
|
|
41
|
+
}
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function mobilityLane(entity: ScheduleData): LaneView | undefined {
|
|
46
|
+
return getLane(entity, LANE_MOBILITY)
|
|
29
47
|
}
|
|
30
48
|
|
|
31
49
|
export function hasSchedule(entity: ScheduleData): boolean {
|
|
32
|
-
|
|
50
|
+
const lanes = entity.lanes
|
|
51
|
+
if (!lanes) return false
|
|
52
|
+
return lanes.some((l) => l.schedule.tasks.length > 0)
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
export function isIdle(entity: ScheduleData): boolean {
|
|
36
56
|
return !hasSchedule(entity)
|
|
37
57
|
}
|
|
38
58
|
|
|
59
|
+
export function isEntityIdle(entity: ScheduleData, now: Date): boolean {
|
|
60
|
+
const lanes = entity.lanes
|
|
61
|
+
if (!lanes) return true
|
|
62
|
+
return lanes.every((l) => core.currentTaskIndexForLane(l.schedule, now) < 0)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function entityIdleAt(entity: ScheduleData, _now: Date): Date | undefined {
|
|
66
|
+
const lanes = entity.lanes
|
|
67
|
+
if (!lanes) return undefined
|
|
68
|
+
let maxMs: number | undefined
|
|
69
|
+
for (const l of lanes) {
|
|
70
|
+
if (l.schedule.tasks.length === 0) continue
|
|
71
|
+
const endMs = l.schedule.started.toDate().getTime() + core.laneDuration(l.schedule) * 1000
|
|
72
|
+
if (maxMs === undefined || endMs > maxMs) maxMs = endMs
|
|
73
|
+
}
|
|
74
|
+
return maxMs === undefined ? undefined : new Date(maxMs)
|
|
75
|
+
}
|
|
76
|
+
|
|
39
77
|
export function getTasks(entity: ScheduleData): Task[] {
|
|
40
|
-
|
|
78
|
+
const lanes = entity.lanes
|
|
79
|
+
if (!lanes) return []
|
|
80
|
+
return lanes.flatMap((l) => l.schedule.tasks)
|
|
41
81
|
}
|
|
42
82
|
|
|
43
83
|
export function scheduleDuration(entity: ScheduleData): number {
|
|
44
|
-
|
|
45
|
-
|
|
84
|
+
let max = 0
|
|
85
|
+
for (const l of entity.lanes ?? []) max = Math.max(max, core.laneDuration(l.schedule))
|
|
86
|
+
return max
|
|
46
87
|
}
|
|
47
88
|
|
|
48
89
|
export function scheduleElapsed(entity: ScheduleData, now: Date): number {
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
return Math.max(0, elapsed)
|
|
90
|
+
let max = 0
|
|
91
|
+
for (const l of entity.lanes ?? []) max = Math.max(max, core.laneElapsed(l.schedule, now))
|
|
92
|
+
return max
|
|
53
93
|
}
|
|
54
94
|
|
|
55
95
|
export function scheduleRemaining(entity: ScheduleData, now: Date): number {
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
96
|
+
let remaining = 0
|
|
97
|
+
for (const l of entity.lanes ?? []) {
|
|
98
|
+
remaining = Math.max(remaining, core.laneRemaining(l.schedule, now))
|
|
99
|
+
}
|
|
100
|
+
return remaining
|
|
60
101
|
}
|
|
61
102
|
|
|
62
103
|
export function scheduleComplete(entity: ScheduleData, now: Date): boolean {
|
|
63
|
-
|
|
104
|
+
const lanes = entity.lanes
|
|
105
|
+
if (!lanes) return false
|
|
106
|
+
let hasAnyTask = false
|
|
107
|
+
let remaining = 0
|
|
108
|
+
for (const l of lanes) {
|
|
109
|
+
const tasks = l.schedule.tasks
|
|
110
|
+
if (tasks.length > 0) {
|
|
111
|
+
hasAnyTask = true
|
|
112
|
+
for (const t of tasks) {
|
|
113
|
+
if (t.type.toNumber() === TaskType.RESERVED) return false
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
remaining = Math.max(remaining, core.laneRemaining(l.schedule, now))
|
|
117
|
+
}
|
|
118
|
+
if (!hasAnyTask) return false
|
|
119
|
+
return remaining === 0
|
|
64
120
|
}
|
|
65
121
|
|
|
66
|
-
|
|
67
|
-
|
|
122
|
+
// Mirrors contract lane_front_complete: any lane whose front task is complete and non-reserved.
|
|
123
|
+
export function hasResolvable(entity: ScheduleData, now: Date): boolean {
|
|
124
|
+
for (const l of entity.lanes ?? []) {
|
|
125
|
+
if (core.laneTaskComplete(l.schedule, 0, now)) return true
|
|
126
|
+
}
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
68
129
|
|
|
69
|
-
|
|
70
|
-
|
|
130
|
+
export function currentTaskForLane(
|
|
131
|
+
entity: ScheduleData,
|
|
132
|
+
laneKey: number,
|
|
133
|
+
now: Date
|
|
134
|
+
): Task | undefined {
|
|
135
|
+
const lane = getLane(entity, laneKey)
|
|
136
|
+
return lane ? core.currentTask(lane.schedule, now) : undefined
|
|
137
|
+
}
|
|
71
138
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
139
|
+
export function currentTaskTypeForLane(
|
|
140
|
+
entity: ScheduleData,
|
|
141
|
+
laneKey: number,
|
|
142
|
+
now: Date
|
|
143
|
+
): TaskType | undefined {
|
|
144
|
+
const lane = getLane(entity, laneKey)
|
|
145
|
+
return lane ? core.currentTaskType(lane.schedule, now) : undefined
|
|
146
|
+
}
|
|
79
147
|
|
|
80
|
-
|
|
148
|
+
export function activeTasks(entity: ScheduleData, now: Date): Task[] {
|
|
149
|
+
const out: Task[] = []
|
|
150
|
+
for (const l of entity.lanes ?? []) {
|
|
151
|
+
const idx = core.currentTaskIndexForLane(l.schedule, now)
|
|
152
|
+
if (idx >= 0) out.push(l.schedule.tasks[idx])
|
|
153
|
+
}
|
|
154
|
+
return out
|
|
81
155
|
}
|
|
82
156
|
|
|
83
|
-
export
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
157
|
+
export interface ResolvedEvent {
|
|
158
|
+
laneKey: number
|
|
159
|
+
taskIndex: number
|
|
160
|
+
task: Task
|
|
161
|
+
completesAt: Date
|
|
87
162
|
}
|
|
88
163
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
164
|
+
// Canonical lane-front order (mirrors contract front_precedes): completion, then RECHARGE-last, then lane key.
|
|
165
|
+
function frontPrecedes(
|
|
166
|
+
a: {completesAt: Date; task: Task; laneKey: number},
|
|
167
|
+
b: {completesAt: Date; task: Task; laneKey: number}
|
|
168
|
+
): number {
|
|
169
|
+
if (a.completesAt.getTime() !== b.completesAt.getTime()) {
|
|
170
|
+
return a.completesAt.getTime() - b.completesAt.getTime()
|
|
171
|
+
}
|
|
172
|
+
const aRecharge = a.task.type.toNumber() === TaskType.RECHARGE
|
|
173
|
+
const bRecharge = b.task.type.toNumber() === TaskType.RECHARGE
|
|
174
|
+
if (aRecharge !== bRecharge) return aRecharge ? 1 : -1
|
|
175
|
+
return a.laneKey - b.laneKey
|
|
93
176
|
}
|
|
94
177
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (
|
|
99
|
-
|
|
178
|
+
// Completed lane-fronts in canonical order (mirrors contract front_precedes).
|
|
179
|
+
export function resolveOrder(entity: ScheduleData, now: Date): ResolvedEvent[] {
|
|
180
|
+
const events: ResolvedEvent[] = []
|
|
181
|
+
for (const l of entity.lanes ?? []) {
|
|
182
|
+
const laneKey = l.lane_key.toNumber()
|
|
183
|
+
const startedMs = l.schedule.started.toDate().getTime()
|
|
184
|
+
let endSec = 0
|
|
185
|
+
for (let i = 0; i < l.schedule.tasks.length; i++) {
|
|
186
|
+
const task = l.schedule.tasks[i]
|
|
187
|
+
endSec += task.duration.toNumber()
|
|
188
|
+
const completesAt = new Date(startedMs + endSec * 1000)
|
|
189
|
+
if (task.type.toNumber() === TaskType.RESERVED) break
|
|
190
|
+
if (completesAt.getTime() > now.getTime()) break
|
|
191
|
+
events.push({laneKey, taskIndex: i, task, completesAt})
|
|
192
|
+
}
|
|
100
193
|
}
|
|
101
|
-
|
|
194
|
+
events.sort(frontPrecedes)
|
|
195
|
+
return events
|
|
102
196
|
}
|
|
103
197
|
|
|
104
|
-
export
|
|
105
|
-
|
|
198
|
+
export interface OrderedTask {
|
|
199
|
+
laneKey: number
|
|
200
|
+
taskIndex: number
|
|
201
|
+
task: Task
|
|
202
|
+
startsAt: Date
|
|
203
|
+
completesAt: Date
|
|
204
|
+
}
|
|
106
205
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
206
|
+
// Every task across all lanes in canonical order (mirrors contract front_precedes).
|
|
207
|
+
export function orderedTasks(entity: ScheduleData): OrderedTask[] {
|
|
208
|
+
const out: OrderedTask[] = []
|
|
209
|
+
for (const l of entity.lanes ?? []) {
|
|
210
|
+
const laneKey = l.lane_key.toNumber()
|
|
211
|
+
const startedMs = l.schedule.started.toDate().getTime()
|
|
212
|
+
let endSec = 0
|
|
213
|
+
for (let i = 0; i < l.schedule.tasks.length; i++) {
|
|
214
|
+
const task = l.schedule.tasks[i]
|
|
215
|
+
const startsAt = new Date(startedMs + endSec * 1000)
|
|
216
|
+
endSec += task.duration.toNumber()
|
|
217
|
+
const completesAt = new Date(startedMs + endSec * 1000)
|
|
218
|
+
out.push({laneKey, taskIndex: i, task, startsAt, completesAt})
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
out.sort(frontPrecedes)
|
|
222
|
+
return out
|
|
223
|
+
}
|
|
110
224
|
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
return
|
|
225
|
+
export function laneRemainingOf(entity: ScheduleData, laneKey: number, now: Date): number {
|
|
226
|
+
const lane = getLane(entity, laneKey)
|
|
227
|
+
return lane ? core.laneRemaining(lane.schedule, now) : 0
|
|
114
228
|
}
|
|
115
229
|
|
|
116
|
-
export function
|
|
117
|
-
|
|
230
|
+
export function laneStartsInOf(entity: ScheduleData, laneKey: number, now: Date): number {
|
|
231
|
+
const lane = getLane(entity, laneKey)
|
|
232
|
+
return lane ? core.laneStartsIn(lane.schedule, now) : 0
|
|
233
|
+
}
|
|
118
234
|
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
return
|
|
235
|
+
export function laneCompleteOf(entity: ScheduleData, laneKey: number, now: Date): boolean {
|
|
236
|
+
const lane = getLane(entity, laneKey)
|
|
237
|
+
return lane ? core.laneComplete(lane.schedule, now) : false
|
|
122
238
|
}
|
|
123
239
|
|
|
124
|
-
export function
|
|
125
|
-
|
|
240
|
+
export function laneProgressOf(entity: ScheduleData, laneKey: number, now: Date): number {
|
|
241
|
+
const lane = getLane(entity, laneKey)
|
|
242
|
+
return lane ? core.laneProgress(lane.schedule, now) : 0
|
|
243
|
+
}
|
|
126
244
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
245
|
+
export function laneTaskElapsedOf(
|
|
246
|
+
entity: ScheduleData,
|
|
247
|
+
laneKey: number,
|
|
248
|
+
index: number,
|
|
249
|
+
now: Date
|
|
250
|
+
): number {
|
|
251
|
+
const lane = getLane(entity, laneKey)
|
|
252
|
+
return lane ? core.laneTaskElapsed(lane.schedule, index, now) : 0
|
|
130
253
|
}
|
|
131
254
|
|
|
132
|
-
export function
|
|
133
|
-
|
|
255
|
+
export function laneTaskRemainingOf(
|
|
256
|
+
entity: ScheduleData,
|
|
257
|
+
laneKey: number,
|
|
258
|
+
index: number,
|
|
259
|
+
now: Date
|
|
260
|
+
): number {
|
|
261
|
+
const lane = getLane(entity, laneKey)
|
|
262
|
+
return lane ? core.laneTaskRemaining(lane.schedule, index, now) : 0
|
|
263
|
+
}
|
|
134
264
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
265
|
+
export function laneTaskCompleteOf(
|
|
266
|
+
entity: ScheduleData,
|
|
267
|
+
laneKey: number,
|
|
268
|
+
index: number,
|
|
269
|
+
now: Date
|
|
270
|
+
): boolean {
|
|
271
|
+
const lane = getLane(entity, laneKey)
|
|
272
|
+
return lane ? core.laneTaskComplete(lane.schedule, index, now) : false
|
|
138
273
|
}
|
|
139
274
|
|
|
140
|
-
export function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return
|
|
275
|
+
export function laneTaskInProgressOf(
|
|
276
|
+
entity: ScheduleData,
|
|
277
|
+
laneKey: number,
|
|
278
|
+
index: number,
|
|
279
|
+
now: Date
|
|
280
|
+
): boolean {
|
|
281
|
+
const lane = getLane(entity, laneKey)
|
|
282
|
+
return lane ? core.laneTaskInProgress(lane.schedule, index, now) : false
|
|
148
283
|
}
|
|
149
284
|
|
|
150
|
-
export function
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
const elapsed = scheduleElapsed(entity, now)
|
|
154
|
-
return Math.min(1, elapsed / duration)
|
|
285
|
+
export function currentTaskIndexOf(entity: ScheduleData, laneKey: number, now: Date): number {
|
|
286
|
+
const lane = getLane(entity, laneKey)
|
|
287
|
+
return lane ? core.currentTaskIndexForLane(lane.schedule, now) : -1
|
|
155
288
|
}
|
|
156
289
|
|
|
157
|
-
|
|
158
|
-
return
|
|
290
|
+
function entityDoesTaskType(entity: ScheduleData, taskType: TaskType, now: Date): boolean {
|
|
291
|
+
return activeTasks(entity, now).some((t) => t.type.toNumber() === taskType)
|
|
159
292
|
}
|
|
160
293
|
|
|
161
294
|
export function isInFlight(entity: ScheduleData, now: Date): boolean {
|
|
162
|
-
|
|
295
|
+
const lane = mobilityLane(entity)
|
|
296
|
+
return lane ? core.currentTaskType(lane.schedule, now) === TaskType.TRAVEL : false
|
|
163
297
|
}
|
|
164
298
|
|
|
165
299
|
export function isRecharging(entity: ScheduleData, now: Date): boolean {
|
|
166
|
-
return
|
|
300
|
+
return entityDoesTaskType(entity, TaskType.RECHARGE, now)
|
|
167
301
|
}
|
|
168
302
|
|
|
169
303
|
export function isLoading(entity: ScheduleData, now: Date): boolean {
|
|
170
|
-
return
|
|
304
|
+
return entityDoesTaskType(entity, TaskType.LOAD, now)
|
|
171
305
|
}
|
|
172
306
|
|
|
173
307
|
export function isUnloading(entity: ScheduleData, now: Date): boolean {
|
|
174
|
-
return
|
|
308
|
+
return entityDoesTaskType(entity, TaskType.UNLOAD, now)
|
|
175
309
|
}
|
|
176
310
|
|
|
177
311
|
export function isGathering(entity: ScheduleData, now: Date): boolean {
|
|
178
|
-
return
|
|
312
|
+
return entityDoesTaskType(entity, TaskType.GATHER, now)
|
|
179
313
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type {ServerContract} from '../contracts'
|
|
2
|
+
import {TaskType} from '../types'
|
|
3
|
+
|
|
4
|
+
export type TaskCargoDirection = 'in' | 'out'
|
|
5
|
+
|
|
6
|
+
export interface TaskCargoChange {
|
|
7
|
+
direction: TaskCargoDirection
|
|
8
|
+
item_id: number
|
|
9
|
+
stats: bigint
|
|
10
|
+
modules: ServerContract.Types.module_entry[]
|
|
11
|
+
quantity: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function toChange(
|
|
15
|
+
item: ServerContract.Types.cargo_item,
|
|
16
|
+
direction: TaskCargoDirection
|
|
17
|
+
): TaskCargoChange {
|
|
18
|
+
return {
|
|
19
|
+
direction,
|
|
20
|
+
item_id: Number(item.item_id),
|
|
21
|
+
stats: BigInt(item.stats.toString()),
|
|
22
|
+
modules: item.modules ?? [],
|
|
23
|
+
quantity: Number(item.quantity),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function taskCargoChanges(task: ServerContract.Types.task): TaskCargoChange[] {
|
|
28
|
+
const items = task.cargo ?? []
|
|
29
|
+
if (items.length === 0) return []
|
|
30
|
+
switch (Number(task.type)) {
|
|
31
|
+
case TaskType.LOAD:
|
|
32
|
+
case TaskType.UNWRAP:
|
|
33
|
+
return items.map((i) => toChange(i, 'in'))
|
|
34
|
+
case TaskType.GATHER:
|
|
35
|
+
return task.entitytarget ? [] : items.map((i) => toChange(i, 'in'))
|
|
36
|
+
case TaskType.UNLOAD:
|
|
37
|
+
return items.map((i) => toChange(i, 'out'))
|
|
38
|
+
case TaskType.CRAFT:
|
|
39
|
+
return [
|
|
40
|
+
...items.slice(0, -1).map((i) => toChange(i, 'out')),
|
|
41
|
+
toChange(items[items.length - 1], 'in'),
|
|
42
|
+
]
|
|
43
|
+
default:
|
|
44
|
+
return []
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/shipload.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {PlayersManager} from './managers/players'
|
|
|
9
9
|
import type {LocationsManager} from './managers/locations'
|
|
10
10
|
import type {EpochsManager} from './managers/epochs'
|
|
11
11
|
import type {ActionsManager} from './managers/actions'
|
|
12
|
+
import type {NftManager} from './managers/nft'
|
|
12
13
|
import type {SubscriptionsManager} from './subscriptions/manager'
|
|
13
14
|
import type {GameState} from './entities/gamestate'
|
|
14
15
|
|
|
@@ -17,6 +18,7 @@ interface ShiploadOptions {
|
|
|
17
18
|
serverContractName?: string
|
|
18
19
|
client?: APIClient
|
|
19
20
|
subscriptionsUrl?: string
|
|
21
|
+
atomicAssetsAccount?: string
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
interface ShiploadConstructorOptions extends ShiploadOptions {
|
|
@@ -39,7 +41,12 @@ export class Shipload {
|
|
|
39
41
|
? serverContract
|
|
40
42
|
: new ServerContract.Contract({client: apiClient})
|
|
41
43
|
|
|
42
|
-
this._context = new GameContext(
|
|
44
|
+
this._context = new GameContext(
|
|
45
|
+
apiClient,
|
|
46
|
+
server,
|
|
47
|
+
platform,
|
|
48
|
+
constructorOptions?.atomicAssetsAccount ?? 'atomicassets'
|
|
49
|
+
)
|
|
43
50
|
|
|
44
51
|
if (constructorOptions?.subscriptionsUrl) {
|
|
45
52
|
this._context.setSubscriptionsUrl(constructorOptions.subscriptionsUrl)
|
|
@@ -79,6 +86,10 @@ export class Shipload {
|
|
|
79
86
|
return this._context.client
|
|
80
87
|
}
|
|
81
88
|
|
|
89
|
+
get atomicAssetsAccount(): string {
|
|
90
|
+
return this._context.atomicAssetsAccount
|
|
91
|
+
}
|
|
92
|
+
|
|
82
93
|
get server(): Contract {
|
|
83
94
|
return this._context.server
|
|
84
95
|
}
|
|
@@ -107,6 +118,10 @@ export class Shipload {
|
|
|
107
118
|
return this._context.actions
|
|
108
119
|
}
|
|
109
120
|
|
|
121
|
+
get nft(): NftManager {
|
|
122
|
+
return this._context.nft
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
get subscriptions(): SubscriptionsManager {
|
|
111
126
|
return this._context.subscriptions
|
|
112
127
|
}
|
|
@@ -13,12 +13,10 @@ import type {
|
|
|
13
13
|
WireEntity,
|
|
14
14
|
} from './types'
|
|
15
15
|
import {mapEntity, parseWireEntity} from './mappers'
|
|
16
|
-
import type {
|
|
17
|
-
import type {Warehouse} from '../entities/warehouse'
|
|
18
|
-
import type {Container} from '../entities/container'
|
|
16
|
+
import type {Entity} from '../entities/entity'
|
|
19
17
|
|
|
20
|
-
export type SubscriptionEntityType = 'ship' | 'warehouse' | 'container'
|
|
21
|
-
export type EntityInstance =
|
|
18
|
+
export type SubscriptionEntityType = 'ship' | 'warehouse' | 'container' | 'nexus'
|
|
19
|
+
export type EntityInstance = Entity
|
|
22
20
|
|
|
23
21
|
export interface SubscriptionsOptions {
|
|
24
22
|
url: string
|
|
@@ -34,6 +32,12 @@ export interface BoundsSubscriptionHandle {
|
|
|
34
32
|
current: Map<number, EntityInstance>
|
|
35
33
|
}
|
|
36
34
|
|
|
35
|
+
export interface OwnerSubscriptionHandle {
|
|
36
|
+
readonly subId: string
|
|
37
|
+
unsubscribe(): void
|
|
38
|
+
current: Map<number, EntityInstance>
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
export interface EntitySubscriptionHandle {
|
|
38
42
|
readonly subId: string
|
|
39
43
|
readonly entityType: SubscriptionEntityType
|
|
@@ -62,7 +66,7 @@ export class SubscriptionsManager {
|
|
|
62
66
|
onSnapshot?: (entities: EntityInstance[]) => void
|
|
63
67
|
onUpdate?: (entity: EntityInstance) => void
|
|
64
68
|
onBoundsDelta?: (entered: EntityInstance[], exited: number[]) => void
|
|
65
|
-
handle: BoundsSubscriptionHandle
|
|
69
|
+
handle: BoundsSubscriptionHandle | OwnerSubscriptionHandle
|
|
66
70
|
}
|
|
67
71
|
>()
|
|
68
72
|
private subCounter = 0
|
|
@@ -162,6 +166,36 @@ export class SubscriptionsManager {
|
|
|
162
166
|
return handle
|
|
163
167
|
}
|
|
164
168
|
|
|
169
|
+
subscribeOwner(
|
|
170
|
+
owner: string,
|
|
171
|
+
handlers: {
|
|
172
|
+
onSnapshot?: (entities: EntityInstance[]) => void
|
|
173
|
+
onUpdate?: (entity: EntityInstance) => void
|
|
174
|
+
} = {}
|
|
175
|
+
): OwnerSubscriptionHandle {
|
|
176
|
+
const subId = this.generateSubID('own')
|
|
177
|
+
const msg: SubscribeMessage = {
|
|
178
|
+
type: 'subscribe',
|
|
179
|
+
sub_id: subId,
|
|
180
|
+
owner,
|
|
181
|
+
}
|
|
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
|
+
}
|
|
198
|
+
|
|
165
199
|
private unsubscribeBounds(subId: string) {
|
|
166
200
|
this.boundsSubs.delete(subId)
|
|
167
201
|
this.sendMessage({type: 'unsubscribe', sub_id: subId})
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import {ServerContract} from '../contracts'
|
|
2
|
-
import {
|
|
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):
|
|
8
|
-
|
|
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 {
|
|
@@ -39,7 +39,7 @@ export type UnsubscribeMessage = {
|
|
|
39
39
|
export type SubscribeEntityMessage = {
|
|
40
40
|
type: 'subscribe_entity'
|
|
41
41
|
sub_id: string
|
|
42
|
-
entity_type: 'ship' | 'warehouse' | 'container'
|
|
42
|
+
entity_type: 'ship' | 'warehouse' | 'container' | 'nexus'
|
|
43
43
|
entity_id: string
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -80,10 +80,11 @@ export type AckMessage = {
|
|
|
80
80
|
|
|
81
81
|
export type WireEntity = Record<string, unknown> & {
|
|
82
82
|
type: number
|
|
83
|
-
type_name: 'ship' | 'warehouse' | 'container'
|
|
83
|
+
type_name: 'ship' | 'warehouse' | 'container' | 'nexus'
|
|
84
84
|
id: string | number
|
|
85
85
|
owner: string
|
|
86
86
|
coordinates: WireCoordinates
|
|
87
|
+
item_id: number
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
export type SnapshotMessage = {
|
|
@@ -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
|
+
}
|