@shipload/sdk 1.0.0-next.35 → 1.0.0-next.36
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 +237 -80
- package/lib/shipload.js +2979 -2598
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +2960 -2599
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +66 -20
- package/lib/testing.js +95 -57
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +95 -57
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/crafting.ts +2 -3
- package/src/capabilities/gathering.test.ts +16 -0
- package/src/capabilities/gathering.ts +8 -11
- package/src/contracts/server.ts +45 -29
- package/src/coordinates/address.ts +9 -5
- package/src/coordinates/constants.test.ts +15 -0
- package/src/coordinates/constants.ts +5 -3
- package/src/coordinates/index.ts +11 -0
- package/src/coordinates/memo.test.ts +47 -0
- package/src/coordinates/memo.ts +20 -0
- package/src/data/capability-formulas.ts +0 -1
- package/src/data/entities.json +4 -4
- package/src/data/items.json +5 -5
- package/src/data/recipes.json +39 -65
- package/src/derivation/capabilities.test.ts +133 -0
- package/src/derivation/capabilities.ts +66 -14
- package/src/derivation/rollups.test.ts +55 -0
- package/src/derivation/rollups.ts +56 -0
- package/src/entities/makers.ts +30 -3
- package/src/index-module.ts +15 -2
- package/src/managers/actions.ts +34 -3
- package/src/managers/construction.ts +6 -4
- package/src/managers/context.ts +9 -0
- package/src/managers/coordinates.ts +14 -0
- package/src/managers/plot.ts +2 -4
- package/src/nft/description.ts +25 -6
- package/src/planner/index.ts +127 -0
- package/src/planner/planner.test.ts +319 -0
- package/src/resolution/resolve-item.ts +4 -1
- package/src/scheduling/cancel.test.ts +21 -0
- package/src/scheduling/lanes.test.ts +249 -0
- package/src/scheduling/lanes.ts +140 -2
- package/src/scheduling/projection.ts +73 -16
- package/src/shipload.ts +5 -0
- package/src/testing/projection-parity.ts +26 -2
- package/src/travel/travel.ts +102 -101
- package/src/types/capabilities.ts +23 -6
- package/src/types/entity.ts +3 -3
- package/src/types.ts +1 -1
package/src/scheduling/lanes.ts
CHANGED
|
@@ -1,12 +1,47 @@
|
|
|
1
1
|
import type {ServerContract} from '../contracts'
|
|
2
2
|
import {getItem} from '../data/catalog'
|
|
3
|
+
import {getEntityLayout} from '../data/recipes-runtime'
|
|
4
|
+
import {decodeStat} from '../derivation/crafting'
|
|
5
|
+
import {gathererDepthForTier} from '../derivation/capabilities'
|
|
6
|
+
import {applySlotMultiplier, getSlotAmp} from '../entities/slot-multiplier'
|
|
7
|
+
import {
|
|
8
|
+
computeGathererYield,
|
|
9
|
+
computeGathererDrain,
|
|
10
|
+
computeLoaderThrust,
|
|
11
|
+
computeLoaderMass,
|
|
12
|
+
computeCrafterSpeed,
|
|
13
|
+
computeCrafterDrain,
|
|
14
|
+
} from '../nft/description'
|
|
3
15
|
import type {ModuleType} from '../types'
|
|
4
|
-
import {getLane, type ScheduleData} from './schedule'
|
|
16
|
+
import {getLane, LANE_MOBILITY, type ScheduleData} from './schedule'
|
|
5
17
|
|
|
6
18
|
type ModuleEntry = ServerContract.Types.module_entry
|
|
7
19
|
type Lane = ServerContract.Types.lane
|
|
8
20
|
type Schedule = ServerContract.Types.schedule
|
|
9
21
|
|
|
22
|
+
export interface ResolvedGathererLane {
|
|
23
|
+
slotIndex: number
|
|
24
|
+
yield: number
|
|
25
|
+
drain: number
|
|
26
|
+
depth: number
|
|
27
|
+
outputPct: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ResolvedCrafterLane {
|
|
31
|
+
slotIndex: number
|
|
32
|
+
speed: number
|
|
33
|
+
drain: number
|
|
34
|
+
outputPct: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ResolvedLoaderLane {
|
|
38
|
+
slotIndex: number
|
|
39
|
+
thrust: number
|
|
40
|
+
mass: number
|
|
41
|
+
outputPct: number
|
|
42
|
+
valid: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
10
45
|
export function laneKeyForModule(slotIndex: number): number {
|
|
11
46
|
return slotIndex + 1
|
|
12
47
|
}
|
|
@@ -16,11 +51,114 @@ function laneIsFree(lanes: Lane[], laneKey: number): boolean {
|
|
|
16
51
|
return lane ? lane.schedule.tasks.length === 0 : true
|
|
17
52
|
}
|
|
18
53
|
|
|
54
|
+
export function resolveLaneGatherer(
|
|
55
|
+
modules: ModuleEntry[],
|
|
56
|
+
entityItemId: number,
|
|
57
|
+
laneKey: number
|
|
58
|
+
): ResolvedGathererLane {
|
|
59
|
+
const idx = laneKey - 1
|
|
60
|
+
const installed = idx >= 0 && idx < modules.length ? modules[idx].installed : undefined
|
|
61
|
+
if (!installed) throw new Error('gatherer lane has no module')
|
|
62
|
+
const item = getItem(Number(installed.item_id.value ?? installed.item_id))
|
|
63
|
+
if (item.moduleType !== 'gatherer') throw new Error('lane module is not a gatherer')
|
|
64
|
+
const stats = BigInt(installed.stats.toString())
|
|
65
|
+
const str = decodeStat(stats, 0)
|
|
66
|
+
const tol = decodeStat(stats, 1)
|
|
67
|
+
const con = decodeStat(stats, 2)
|
|
68
|
+
const layout = getEntityLayout(entityItemId)?.slots ?? []
|
|
69
|
+
const amp = getSlotAmp(layout, idx)
|
|
70
|
+
const yieldVal = applySlotMultiplier(computeGathererYield(str), amp)
|
|
71
|
+
const drain = computeGathererDrain(con)
|
|
72
|
+
const depth = gathererDepthForTier(tol, item.tier ?? 1)
|
|
73
|
+
return {slotIndex: idx, yield: yieldVal, drain, depth, outputPct: amp}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Encapsulates the gather handler's lane selection (gathering.cpp:108-112): both error paths.
|
|
77
|
+
export function selectGatherLane(
|
|
78
|
+
modules: ModuleEntry[],
|
|
79
|
+
entityItemId: number,
|
|
80
|
+
lanes: Lane[],
|
|
81
|
+
stratum: number,
|
|
82
|
+
explicitSlot?: number
|
|
83
|
+
): number {
|
|
84
|
+
if (explicitSlot !== undefined) {
|
|
85
|
+
const laneKey = laneKeyForModule(explicitSlot)
|
|
86
|
+
const lane = resolveLaneGatherer(modules, entityItemId, laneKey)
|
|
87
|
+
if (stratum > lane.depth) throw new Error('stratum exceeds gatherer depth')
|
|
88
|
+
return laneKey
|
|
89
|
+
}
|
|
90
|
+
return workerLaneKey(modules, 'gatherer', lanes, stratum)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function resolveLaneCrafter(
|
|
94
|
+
modules: ModuleEntry[],
|
|
95
|
+
entityItemId: number,
|
|
96
|
+
laneKey: number
|
|
97
|
+
): ResolvedCrafterLane {
|
|
98
|
+
const idx = laneKey - 1
|
|
99
|
+
const installed = idx >= 0 && idx < modules.length ? modules[idx].installed : undefined
|
|
100
|
+
if (!installed) throw new Error('crafter lane has no module')
|
|
101
|
+
const item = getItem(Number(installed.item_id.value ?? installed.item_id))
|
|
102
|
+
if (item.moduleType !== 'crafter') throw new Error('lane module is not a crafter')
|
|
103
|
+
const stats = BigInt(installed.stats.toString())
|
|
104
|
+
const rea = decodeStat(stats, 0)
|
|
105
|
+
const fin = decodeStat(stats, 1)
|
|
106
|
+
const layout = getEntityLayout(entityItemId)?.slots ?? []
|
|
107
|
+
const amp = getSlotAmp(layout, idx)
|
|
108
|
+
const speed = applySlotMultiplier(computeCrafterSpeed(rea), amp)
|
|
109
|
+
const drain = computeCrafterDrain(fin)
|
|
110
|
+
return {slotIndex: idx, speed, drain, outputPct: amp}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// LANE_MOBILITY or a missing module soft-returns valid=false (never throws); callers check `valid`.
|
|
114
|
+
export function resolveLaneLoader(
|
|
115
|
+
modules: ModuleEntry[],
|
|
116
|
+
entityItemId: number,
|
|
117
|
+
laneKey: number
|
|
118
|
+
): ResolvedLoaderLane {
|
|
119
|
+
if (laneKey === LANE_MOBILITY) {
|
|
120
|
+
return {slotIndex: -1, thrust: 0, mass: 0, outputPct: 0, valid: false}
|
|
121
|
+
}
|
|
122
|
+
const idx = laneKey - 1
|
|
123
|
+
const installed = idx >= 0 && idx < modules.length ? modules[idx].installed : undefined
|
|
124
|
+
if (!installed) {
|
|
125
|
+
return {slotIndex: idx, thrust: 0, mass: 0, outputPct: 0, valid: false}
|
|
126
|
+
}
|
|
127
|
+
const stats = BigInt(installed.stats.toString())
|
|
128
|
+
const ins = decodeStat(stats, 0)
|
|
129
|
+
const pla = decodeStat(stats, 1)
|
|
130
|
+
const layout = getEntityLayout(entityItemId)?.slots ?? []
|
|
131
|
+
const amp = getSlotAmp(layout, idx)
|
|
132
|
+
const thrust = applySlotMultiplier(computeLoaderThrust(pla), amp)
|
|
133
|
+
const mass = computeLoaderMass(ins)
|
|
134
|
+
return {slotIndex: idx, thrust, mass, outputPct: amp, valid: true}
|
|
135
|
+
}
|
|
136
|
+
|
|
19
137
|
export function workerLaneKey(
|
|
20
138
|
modules: ModuleEntry[],
|
|
21
139
|
moduleSubtype: ModuleType,
|
|
22
|
-
lanes: Lane[]
|
|
140
|
+
lanes: Lane[],
|
|
141
|
+
stratum?: number
|
|
23
142
|
): number {
|
|
143
|
+
if (moduleSubtype === 'gatherer' && stratum !== undefined) {
|
|
144
|
+
let lowestReaching: number | undefined
|
|
145
|
+
for (let i = 0; i < modules.length; i++) {
|
|
146
|
+
const installed = modules[i].installed
|
|
147
|
+
if (!installed) continue
|
|
148
|
+
const item = getItem(Number(installed.item_id.value ?? installed.item_id))
|
|
149
|
+
if (item.moduleType !== 'gatherer') continue
|
|
150
|
+
const stats = BigInt(installed.stats.toString())
|
|
151
|
+
const tol = decodeStat(stats, 1)
|
|
152
|
+
const depth = gathererDepthForTier(tol, item.tier ?? 1)
|
|
153
|
+
if (depth < stratum) continue
|
|
154
|
+
const laneKey = laneKeyForModule(i)
|
|
155
|
+
if (lowestReaching === undefined) lowestReaching = laneKey
|
|
156
|
+
if (laneIsFree(lanes, laneKey)) return laneKey
|
|
157
|
+
}
|
|
158
|
+
if (lowestReaching === undefined) throw new Error('no gatherer reaches this stratum')
|
|
159
|
+
return lowestReaching
|
|
160
|
+
}
|
|
161
|
+
|
|
24
162
|
const occupiedMatchingLaneKeys: number[] = []
|
|
25
163
|
|
|
26
164
|
for (let slotIndex = 0; slotIndex < modules.length; slotIndex++) {
|
|
@@ -2,7 +2,6 @@ import {Name, UInt16, UInt32, UInt64} from '@wharfkit/antelope'
|
|
|
2
2
|
import {ServerContract} from '../contracts'
|
|
3
3
|
import {Coordinates, TaskType} from '../types'
|
|
4
4
|
import {
|
|
5
|
-
capsHasLoaders,
|
|
6
5
|
capsHasMovement,
|
|
7
6
|
capsHasStorage,
|
|
8
7
|
type EntityCapabilities,
|
|
@@ -18,7 +17,7 @@ import {
|
|
|
18
17
|
} from '../errors'
|
|
19
18
|
import {getEntityLayout, getRecipe, type RecipeInput} from '../data/recipes-runtime'
|
|
20
19
|
import {computeEntityCapabilities} from '../derivation/capabilities'
|
|
21
|
-
import {decodeCraftedItemStats} from '../derivation/crafting'
|
|
20
|
+
import {decodeCraftedItemStats, decodeStat} from '../derivation/crafting'
|
|
22
21
|
import {packedModulesToInstalled, type InstalledModule} from '../entities/slot-multiplier'
|
|
23
22
|
import {lerp} from '../travel/travel'
|
|
24
23
|
import {
|
|
@@ -39,11 +38,13 @@ export interface ProjectedEntity {
|
|
|
39
38
|
shipMass: UInt32
|
|
40
39
|
capacity?: UInt64
|
|
41
40
|
engines?: ServerContract.Types.movement_stats
|
|
42
|
-
|
|
41
|
+
loaderLanes: ServerContract.Types.loader_lane[]
|
|
43
42
|
generator?: ServerContract.Types.energy_stats
|
|
44
43
|
hauler?: ServerContract.Types.hauler_stats
|
|
45
44
|
readonly cargoMass: UInt64
|
|
46
45
|
readonly totalMass: UInt64
|
|
46
|
+
readonly gathererLanes: ServerContract.Types.gatherer_lane[]
|
|
47
|
+
readonly crafterLanes: ServerContract.Types.crafter_lane[]
|
|
47
48
|
|
|
48
49
|
hasMovement(): boolean
|
|
49
50
|
hasStorage(): boolean
|
|
@@ -59,7 +60,9 @@ export interface Projectable extends ScheduleData {
|
|
|
59
60
|
hullmass?: UInt32
|
|
60
61
|
generator?: ServerContract.Types.energy_stats
|
|
61
62
|
engines?: ServerContract.Types.movement_stats
|
|
62
|
-
|
|
63
|
+
loader_lanes?: ServerContract.Types.loader_lane[]
|
|
64
|
+
gatherer_lanes?: ServerContract.Types.gatherer_lane[]
|
|
65
|
+
crafter_lanes?: ServerContract.Types.crafter_lane[]
|
|
63
66
|
hauler?: ServerContract.Types.hauler_stats
|
|
64
67
|
capacity?: UInt32
|
|
65
68
|
cargo: ServerContract.Types.cargo_item[]
|
|
@@ -84,7 +87,9 @@ interface ProjectedCaps {
|
|
|
84
87
|
capacity?: UInt32
|
|
85
88
|
engines?: ServerContract.Types.movement_stats
|
|
86
89
|
generator?: ServerContract.Types.energy_stats
|
|
87
|
-
|
|
90
|
+
loaderLanes: ServerContract.Types.loader_lane[]
|
|
91
|
+
gathererLanes: ServerContract.Types.gatherer_lane[]
|
|
92
|
+
crafterLanes: ServerContract.Types.crafter_lane[]
|
|
88
93
|
hauler?: ServerContract.Types.hauler_stats
|
|
89
94
|
}
|
|
90
95
|
|
|
@@ -101,10 +106,53 @@ function recomputeCaps(entity: Projectable): ProjectedCaps | undefined {
|
|
|
101
106
|
typeof entity.item_id === 'number' ? entity.item_id : entity.item_id.value
|
|
102
107
|
)
|
|
103
108
|
const hullStats = decodeCraftedItemStats(itemId, entity.stats)
|
|
109
|
+
if (hullStats.strength === undefined) hullStats.strength = decodeStat(entity.stats, 0)
|
|
110
|
+
if (hullStats.hardness === undefined) hullStats.hardness = decodeStat(entity.stats, 2)
|
|
104
111
|
const layout = getEntityLayout(itemId)?.slots ?? []
|
|
105
112
|
const installed = toInstalledModules(entity.modules)
|
|
106
113
|
const caps = computeEntityCapabilities(hullStats, itemId, installed, layout)
|
|
107
114
|
|
|
115
|
+
const toLoaderLane = (l: {
|
|
116
|
+
slotIndex: number
|
|
117
|
+
mass: number
|
|
118
|
+
thrust: number
|
|
119
|
+
outputPct: number
|
|
120
|
+
}): ServerContract.Types.loader_lane =>
|
|
121
|
+
ServerContract.Types.loader_lane.from({
|
|
122
|
+
slot_index: l.slotIndex,
|
|
123
|
+
mass: l.mass,
|
|
124
|
+
thrust: l.thrust,
|
|
125
|
+
output_pct: l.outputPct,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const toGathererLane = (l: {
|
|
129
|
+
slotIndex: number
|
|
130
|
+
yield: number
|
|
131
|
+
drain: number
|
|
132
|
+
depth: number
|
|
133
|
+
outputPct: number
|
|
134
|
+
}): ServerContract.Types.gatherer_lane =>
|
|
135
|
+
ServerContract.Types.gatherer_lane.from({
|
|
136
|
+
slot_index: l.slotIndex,
|
|
137
|
+
yield: l.yield,
|
|
138
|
+
drain: l.drain,
|
|
139
|
+
depth: l.depth,
|
|
140
|
+
output_pct: l.outputPct,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const toCrafterLane = (l: {
|
|
144
|
+
slotIndex: number
|
|
145
|
+
speed: number
|
|
146
|
+
drain: number
|
|
147
|
+
outputPct: number
|
|
148
|
+
}): ServerContract.Types.crafter_lane =>
|
|
149
|
+
ServerContract.Types.crafter_lane.from({
|
|
150
|
+
slot_index: l.slotIndex,
|
|
151
|
+
speed: l.speed,
|
|
152
|
+
drain: l.drain,
|
|
153
|
+
output_pct: l.outputPct,
|
|
154
|
+
})
|
|
155
|
+
|
|
108
156
|
return {
|
|
109
157
|
hullmass: UInt32.from(caps.hullmass),
|
|
110
158
|
capacity: UInt32.from(caps.capacity),
|
|
@@ -112,15 +160,23 @@ function recomputeCaps(entity: Projectable): ProjectedCaps | undefined {
|
|
|
112
160
|
generator: caps.generator
|
|
113
161
|
? ServerContract.Types.energy_stats.from(caps.generator)
|
|
114
162
|
: undefined,
|
|
115
|
-
|
|
163
|
+
loaderLanes: (caps.loaderLanes ?? []).map(toLoaderLane),
|
|
164
|
+
gathererLanes: (caps.gathererLanes ?? []).map(toGathererLane),
|
|
165
|
+
crafterLanes: (caps.crafterLanes ?? []).map(toCrafterLane),
|
|
116
166
|
hauler: caps.hauler ? ServerContract.Types.hauler_stats.from(caps.hauler) : undefined,
|
|
117
167
|
}
|
|
118
168
|
}
|
|
119
169
|
|
|
170
|
+
function loaderLanesTotalMass(lanes: ServerContract.Types.loader_lane[]): UInt64 {
|
|
171
|
+
let total = 0
|
|
172
|
+
for (const l of lanes) total += Number(l.mass)
|
|
173
|
+
return UInt64.from(total)
|
|
174
|
+
}
|
|
175
|
+
|
|
120
176
|
export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
121
177
|
const needsRecompute =
|
|
122
178
|
entity.hullmass === undefined ||
|
|
123
|
-
entity.
|
|
179
|
+
entity.loader_lanes === undefined ||
|
|
124
180
|
entity.engines === undefined ||
|
|
125
181
|
entity.generator === undefined ||
|
|
126
182
|
entity.hauler === undefined ||
|
|
@@ -128,7 +184,9 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
128
184
|
const caps = needsRecompute ? recomputeCaps(entity) : undefined
|
|
129
185
|
|
|
130
186
|
const shipMass = UInt32.from(entity.hullmass ?? caps?.hullmass ?? 0)
|
|
131
|
-
const
|
|
187
|
+
const loaderLanes = entity.loader_lanes ?? caps?.loaderLanes ?? []
|
|
188
|
+
const gathererLanes = entity.gatherer_lanes ?? caps?.gathererLanes ?? []
|
|
189
|
+
const crafterLanes = entity.crafter_lanes ?? caps?.crafterLanes ?? []
|
|
132
190
|
const engines = entity.engines ?? caps?.engines
|
|
133
191
|
const generator = entity.generator ?? caps?.generator
|
|
134
192
|
const hauler = entity.hauler ?? caps?.hauler
|
|
@@ -145,18 +203,18 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
145
203
|
engines,
|
|
146
204
|
generator,
|
|
147
205
|
hauler,
|
|
148
|
-
|
|
206
|
+
loaderLanes,
|
|
207
|
+
gathererLanes,
|
|
208
|
+
crafterLanes,
|
|
149
209
|
|
|
150
210
|
get cargoMass() {
|
|
151
211
|
return calcStacksMass(this.cargo)
|
|
152
212
|
},
|
|
153
213
|
|
|
154
214
|
get totalMass() {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
return mass
|
|
215
|
+
return UInt64.from(this.shipMass)
|
|
216
|
+
.adding(this.cargoMass)
|
|
217
|
+
.adding(loaderLanesTotalMass(this.loaderLanes))
|
|
160
218
|
},
|
|
161
219
|
|
|
162
220
|
hasMovement() {
|
|
@@ -168,7 +226,7 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
168
226
|
},
|
|
169
227
|
|
|
170
228
|
hasLoaders() {
|
|
171
|
-
return
|
|
229
|
+
return this.loaderLanes.length > 0
|
|
172
230
|
},
|
|
173
231
|
|
|
174
232
|
capabilities(): EntityCapabilities {
|
|
@@ -177,7 +235,6 @@ export function createProjectedEntity(entity: Projectable): ProjectedEntity {
|
|
|
177
235
|
capacity: this.capacity ? UInt32.from(this.capacity) : undefined,
|
|
178
236
|
engines: this.engines,
|
|
179
237
|
generator: this.generator,
|
|
180
|
-
loaders: this.loaders,
|
|
181
238
|
}
|
|
182
239
|
},
|
|
183
240
|
|
package/src/shipload.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {GameContext} from './managers/context'
|
|
|
7
7
|
import type {EntitiesManager} from './managers/entities'
|
|
8
8
|
import type {PlayersManager} from './managers/players'
|
|
9
9
|
import type {LocationsManager} from './managers/locations'
|
|
10
|
+
import type {CoordinatesManager} from './managers/coordinates'
|
|
10
11
|
import type {EpochsManager} from './managers/epochs'
|
|
11
12
|
import type {ActionsManager} from './managers/actions'
|
|
12
13
|
import type {NftManager} from './managers/nft'
|
|
@@ -110,6 +111,10 @@ export class Shipload {
|
|
|
110
111
|
return this._context.locations
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
get coordinates(): CoordinatesManager {
|
|
115
|
+
return this._context.coordinates
|
|
116
|
+
}
|
|
117
|
+
|
|
113
118
|
get epochs(): EpochsManager {
|
|
114
119
|
return this._context.epochs
|
|
115
120
|
}
|
|
@@ -12,9 +12,11 @@ export interface ContractProjectedState {
|
|
|
12
12
|
hullmass?: UInt32
|
|
13
13
|
capacity?: UInt32
|
|
14
14
|
engines?: ServerContract.Types.movement_stats
|
|
15
|
-
loaders?: ServerContract.Types.loader_stats
|
|
16
15
|
generator?: ServerContract.Types.energy_stats
|
|
17
16
|
hauler?: ServerContract.Types.hauler_stats
|
|
17
|
+
gatherer_lanes: ServerContract.Types.gatherer_lane[]
|
|
18
|
+
crafter_lanes: ServerContract.Types.crafter_lane[]
|
|
19
|
+
loader_lanes: ServerContract.Types.loader_lane[]
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export interface ProjectionComparisonOptions {
|
|
@@ -55,10 +57,32 @@ export function assertProjectionEquals(
|
|
|
55
57
|
record('capacity', toNum(contract.capacity), sdk.capacity ? Number(sdk.capacity) : undefined)
|
|
56
58
|
|
|
57
59
|
recordStatBlock('engines', contract.engines, sdk.engines)
|
|
58
|
-
recordStatBlock('loaders', contract.loaders, sdk.loaders)
|
|
59
60
|
recordStatBlock('generator', contract.generator, sdk.generator)
|
|
60
61
|
recordStatBlock('hauler', contract.hauler, sdk.hauler)
|
|
61
62
|
|
|
63
|
+
const normLane = (l: {slot_index?: unknown; [k: string]: unknown}) =>
|
|
64
|
+
Object.fromEntries(
|
|
65
|
+
Object.entries(l)
|
|
66
|
+
.filter(([k]) => k !== 'slot_index')
|
|
67
|
+
.map(([k, v]) => [k, toNum(v) ?? 0])
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const compareLanes = (name: string, cLanes: unknown[], sLanes: unknown[]) => {
|
|
71
|
+
if (cLanes.length !== sLanes.length) {
|
|
72
|
+
mismatches.push(` ${name}.length: contract=${cLanes.length} sdk=${sLanes.length}`)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
for (let i = 0; i < cLanes.length; i++) {
|
|
76
|
+
const cn = JSON.stringify(normLane(cLanes[i] as Record<string, unknown>))
|
|
77
|
+
const sn = JSON.stringify(normLane(sLanes[i] as Record<string, unknown>))
|
|
78
|
+
if (cn !== sn) mismatches.push(` ${name}[${i}]: contract=${cn} sdk=${sn}`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
compareLanes('gatherer_lanes', contract.gatherer_lanes as unknown[], sdk.gathererLanes ?? [])
|
|
83
|
+
compareLanes('crafter_lanes', contract.crafter_lanes as unknown[], sdk.crafterLanes ?? [])
|
|
84
|
+
compareLanes('loader_lanes', contract.loader_lanes as unknown[], sdk.loaderLanes ?? [])
|
|
85
|
+
|
|
62
86
|
if (contract.cargo.length > 0 || sdk.cargo.length > 0) {
|
|
63
87
|
const contractCargo = normaliseCargo(mergeContractCargo(contract.cargo))
|
|
64
88
|
const sdkCargo = normaliseCargo(sdk.cargo)
|