@shipload/sdk 1.0.0-next.20 → 1.0.0-next.21
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 +23 -1
- package/lib/shipload.js +144 -13
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +144 -13
- package/lib/shipload.m.js.map +1 -1
- package/package.json +1 -1
- package/src/index-module.ts +2 -0
- package/src/managers/construction-types.ts +21 -0
- package/src/managers/construction.ts +160 -15
- package/src/managers/index.ts +2 -0
package/package.json
CHANGED
package/src/index-module.ts
CHANGED
|
@@ -32,10 +32,15 @@ export interface SourceEntityRef {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export interface SourceCargoStack {
|
|
35
|
+
key: string
|
|
36
|
+
rowId: UInt64
|
|
35
37
|
itemId: number
|
|
36
38
|
item: Item
|
|
39
|
+
stats: UInt64
|
|
40
|
+
modules: ServerContract.Types.module_entry[]
|
|
37
41
|
available: number
|
|
38
42
|
plotNeeds: number
|
|
43
|
+
reserved: number
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
export interface FinalizerEntityRef {
|
|
@@ -45,3 +50,19 @@ export interface FinalizerEntityRef {
|
|
|
45
50
|
crafterSpeed: number
|
|
46
51
|
estimatedDuration: UInt32
|
|
47
52
|
}
|
|
53
|
+
|
|
54
|
+
export interface InboundTransfer {
|
|
55
|
+
sourceEntityId: UInt64
|
|
56
|
+
sourceEntityType: Name
|
|
57
|
+
sourceName: string
|
|
58
|
+
itemId: number
|
|
59
|
+
quantity: number
|
|
60
|
+
etaSeconds: number
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface Reservation {
|
|
64
|
+
targetEntityId: UInt64
|
|
65
|
+
targetEntityType: Name
|
|
66
|
+
itemId: number
|
|
67
|
+
quantity: number
|
|
68
|
+
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import type {UInt32} from '@wharfkit/antelope'
|
|
1
|
+
import type {UInt64, UInt32} from '@wharfkit/antelope'
|
|
2
2
|
import {BaseManager} from './base'
|
|
3
3
|
import type {ServerContract} from '../contracts'
|
|
4
4
|
import {PlotManager} from './plot'
|
|
5
5
|
import {getItem} from '../data/catalog'
|
|
6
6
|
import {calc_craft_duration} from '../capabilities/crafting'
|
|
7
|
+
import {TaskType} from '../types'
|
|
7
8
|
import type {
|
|
8
9
|
BuildableTarget,
|
|
9
10
|
FinalizerEntityRef,
|
|
11
|
+
InboundTransfer,
|
|
12
|
+
Reservation,
|
|
10
13
|
SourceCargoStack,
|
|
11
14
|
SourceEntityRef,
|
|
12
15
|
} from './construction-types'
|
|
@@ -74,6 +77,79 @@ export class ConstructionManager extends BaseManager {
|
|
|
74
77
|
return out.sort((a, b) => a.estimatedDuration.value - b.estimatedDuration.value)
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
inboundTransfersTo(
|
|
81
|
+
plotId: UInt64,
|
|
82
|
+
entities: ServerContract.Types.entity_info[],
|
|
83
|
+
now: Date
|
|
84
|
+
): InboundTransfer[] {
|
|
85
|
+
return this.inboundTransfersByTarget(entities, now).get(plotId.toString()) ?? []
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
inboundTransfersByTarget(
|
|
89
|
+
entities: ServerContract.Types.entity_info[],
|
|
90
|
+
now: Date
|
|
91
|
+
): Map<string, InboundTransfer[]> {
|
|
92
|
+
const buckets = new Map<string, Map<string, InboundTransfer>>()
|
|
93
|
+
const nowMs = now.getTime()
|
|
94
|
+
for (const entity of entities) {
|
|
95
|
+
const schedule = entity.schedule
|
|
96
|
+
if (!schedule) continue
|
|
97
|
+
const entityIdStr = entity.id.toString()
|
|
98
|
+
const sourceName = entity.entity_name || entityIdStr
|
|
99
|
+
const startedMs = schedule.started.toDate().getTime()
|
|
100
|
+
let cumulativeSec = 0
|
|
101
|
+
for (const task of schedule.tasks) {
|
|
102
|
+
cumulativeSec += task.duration.toNumber()
|
|
103
|
+
if (!isTransferTask(task)) continue
|
|
104
|
+
if (!task.entitytarget) continue
|
|
105
|
+
const targetIdStr = task.entitytarget.entity_id.toString()
|
|
106
|
+
const etaSeconds = Math.max(
|
|
107
|
+
0,
|
|
108
|
+
Math.round((startedMs + cumulativeSec * 1000 - nowMs) / 1000)
|
|
109
|
+
)
|
|
110
|
+
let perTarget = buckets.get(targetIdStr)
|
|
111
|
+
if (!perTarget) {
|
|
112
|
+
perTarget = new Map()
|
|
113
|
+
buckets.set(targetIdStr, perTarget)
|
|
114
|
+
}
|
|
115
|
+
for (const c of task.cargo) {
|
|
116
|
+
const itemId = c.item_id.toNumber()
|
|
117
|
+
const quantity = c.quantity.toNumber()
|
|
118
|
+
if (quantity === 0) continue
|
|
119
|
+
const key = `${entityIdStr}#${itemId}`
|
|
120
|
+
const existing = perTarget.get(key)
|
|
121
|
+
if (existing) {
|
|
122
|
+
existing.quantity += quantity
|
|
123
|
+
existing.etaSeconds = Math.min(existing.etaSeconds, etaSeconds)
|
|
124
|
+
} else {
|
|
125
|
+
perTarget.set(key, {
|
|
126
|
+
sourceEntityId: entity.id,
|
|
127
|
+
sourceEntityType: entity.type,
|
|
128
|
+
sourceName,
|
|
129
|
+
itemId,
|
|
130
|
+
quantity,
|
|
131
|
+
etaSeconds,
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const out = new Map<string, InboundTransfer[]>()
|
|
138
|
+
for (const [targetId, perTarget] of buckets) {
|
|
139
|
+
out.set(targetId, Array.from(perTarget.values()))
|
|
140
|
+
}
|
|
141
|
+
return out
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
reservationsFrom(
|
|
145
|
+
sourceEntityId: UInt64,
|
|
146
|
+
entities: ServerContract.Types.entity_info[]
|
|
147
|
+
): Reservation[] {
|
|
148
|
+
const source = entities.find((e) => e.id.equals(sourceEntityId))
|
|
149
|
+
if (!source) return []
|
|
150
|
+
return reservationsOf(source)
|
|
151
|
+
}
|
|
152
|
+
|
|
77
153
|
estimateFinalizeDuration(target: BuildableTarget, crafterSpeed: number): UInt32 {
|
|
78
154
|
return calc_craft_duration(crafterSpeed, target.progress.massRequired)
|
|
79
155
|
}
|
|
@@ -90,28 +166,54 @@ function coordsEqual(
|
|
|
90
166
|
return a.x.equals(b.x) && a.y.equals(b.y)
|
|
91
167
|
}
|
|
92
168
|
|
|
169
|
+
function moduleKey(module: ServerContract.Types.module_entry): string {
|
|
170
|
+
const installed = module.installed
|
|
171
|
+
if (!installed) return `${module.type.toNumber()}:empty`
|
|
172
|
+
|
|
173
|
+
return `${module.type.toNumber()}:${installed.item_id.toNumber()}:${installed.stats.toString()}`
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function sourceStackKey(cargo: ServerContract.Types.cargo_row): string {
|
|
177
|
+
return `${cargo.item_id.toNumber()}#${cargo.stats.toString()}#${(cargo.modules ?? [])
|
|
178
|
+
.map(moduleKey)
|
|
179
|
+
.join(',')}`
|
|
180
|
+
}
|
|
181
|
+
|
|
93
182
|
function matchRelevantCargo(
|
|
94
183
|
entity: ServerContract.Types.entity_info,
|
|
95
184
|
target: BuildableTarget,
|
|
96
|
-
cargo: ServerContract.Types.cargo_row[]
|
|
185
|
+
cargo: ServerContract.Types.cargo_row[],
|
|
186
|
+
reservedByItem: Map<number, number>
|
|
97
187
|
): SourceCargoStack[] {
|
|
98
|
-
const
|
|
188
|
+
const needsByItemId = new Map(
|
|
189
|
+
target.progress.rows.filter((row) => row.missing > 0).map((row) => [row.itemId, row])
|
|
190
|
+
)
|
|
191
|
+
const remainingReserved = new Map(reservedByItem)
|
|
192
|
+
const out: SourceCargoStack[] = []
|
|
99
193
|
for (const c of cargo) {
|
|
100
194
|
if (!c.entity_id.equals(entity.id)) continue
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const available =
|
|
195
|
+
const itemId = c.item_id.toNumber()
|
|
196
|
+
const need = needsByItemId.get(itemId)
|
|
197
|
+
if (!need) continue
|
|
198
|
+
const gross = c.quantity.toNumber()
|
|
199
|
+
if (gross === 0) continue
|
|
200
|
+
const reservedRemaining = remainingReserved.get(itemId) ?? 0
|
|
201
|
+
const reserved = Math.min(gross, reservedRemaining)
|
|
202
|
+
const available = gross - reserved
|
|
203
|
+
if (reserved > 0) {
|
|
204
|
+
remainingReserved.set(itemId, reservedRemaining - reserved)
|
|
205
|
+
}
|
|
109
206
|
if (available === 0) continue
|
|
110
207
|
out.push({
|
|
111
|
-
|
|
112
|
-
|
|
208
|
+
key: sourceStackKey(c),
|
|
209
|
+
rowId: c.id,
|
|
210
|
+
itemId,
|
|
211
|
+
item: getItem(itemId),
|
|
212
|
+
stats: c.stats,
|
|
213
|
+
modules: c.modules ?? [],
|
|
113
214
|
available,
|
|
114
|
-
plotNeeds:
|
|
215
|
+
plotNeeds: need.missing,
|
|
216
|
+
reserved,
|
|
115
217
|
})
|
|
116
218
|
}
|
|
117
219
|
return out
|
|
@@ -128,7 +230,8 @@ function partitionSources(
|
|
|
128
230
|
if (!entity.owner.equals(target.ownerName)) continue
|
|
129
231
|
if (entity.id.equals(target.entityId)) continue
|
|
130
232
|
if (!coordsEqual(entity.coordinates, target.coordinates)) continue
|
|
131
|
-
const
|
|
233
|
+
const reserved = reservedByItemFor(entity)
|
|
234
|
+
const relevant = matchRelevantCargo(entity, target, cargo, reserved)
|
|
132
235
|
if (relevant.length === 0) continue
|
|
133
236
|
const loaderCount = entity.loaders?.quantity.toNumber() ?? 0
|
|
134
237
|
const loaderTotalMass = entity.loaders?.mass.toNumber() ?? 0
|
|
@@ -145,3 +248,45 @@ function partitionSources(
|
|
|
145
248
|
}
|
|
146
249
|
return {eligible, unreachable}
|
|
147
250
|
}
|
|
251
|
+
|
|
252
|
+
function isTransferTask(task: ServerContract.Types.task): boolean {
|
|
253
|
+
const type = task.type.toNumber()
|
|
254
|
+
return type === TaskType.LOAD || type === TaskType.UNLOAD
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function reservationsOf(source: ServerContract.Types.entity_info): Reservation[] {
|
|
258
|
+
if (!source.schedule) return []
|
|
259
|
+
const out = new Map<string, Reservation>()
|
|
260
|
+
for (const task of source.schedule.tasks) {
|
|
261
|
+
if (!isTransferTask(task)) continue
|
|
262
|
+
if (!task.entitytarget) continue
|
|
263
|
+
const targetType = task.entitytarget.entity_type
|
|
264
|
+
const targetId = task.entitytarget.entity_id
|
|
265
|
+
for (const c of task.cargo) {
|
|
266
|
+
const itemId = c.item_id.toNumber()
|
|
267
|
+
const quantity = c.quantity.toNumber()
|
|
268
|
+
if (quantity === 0) continue
|
|
269
|
+
const key = `${targetId.toString()}#${itemId}`
|
|
270
|
+
const existing = out.get(key)
|
|
271
|
+
if (existing) {
|
|
272
|
+
existing.quantity += quantity
|
|
273
|
+
} else {
|
|
274
|
+
out.set(key, {
|
|
275
|
+
targetEntityId: targetId,
|
|
276
|
+
targetEntityType: targetType,
|
|
277
|
+
itemId,
|
|
278
|
+
quantity,
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return Array.from(out.values())
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function reservedByItemFor(source: ServerContract.Types.entity_info): Map<number, number> {
|
|
287
|
+
const out = new Map<number, number>()
|
|
288
|
+
for (const r of reservationsOf(source)) {
|
|
289
|
+
out.set(r.itemId, (out.get(r.itemId) ?? 0) + r.quantity)
|
|
290
|
+
}
|
|
291
|
+
return out
|
|
292
|
+
}
|