@shipload/sdk 1.0.0-next.31 → 1.0.0-next.32
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 +70 -32
- package/lib/shipload.js +176 -100
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +176 -100
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +21 -10
- package/lib/testing.js +71 -32
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +71 -32
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/contracts/server.ts +64 -33
- package/src/entities/makers.ts +1 -0
- package/src/errors.ts +0 -1
- package/src/index-module.ts +3 -0
- package/src/managers/actions.ts +26 -17
- package/src/managers/construction.ts +50 -37
- package/src/scheduling/energy.ts +1 -2
- package/src/scheduling/idle-resolve.ts +44 -0
- package/src/scheduling/lane-core.ts +1 -3
- package/src/scheduling/projection.ts +1 -2
- package/src/scheduling/schedule.ts +10 -9
- package/src/subscriptions/mappers.ts +2 -0
- package/src/types.ts +7 -1
|
@@ -5,7 +5,7 @@ import {PlotManager} from './plot'
|
|
|
5
5
|
import {getItem} from '../data/catalog'
|
|
6
6
|
import {calc_craft_duration} from '../capabilities/crafting'
|
|
7
7
|
import {getLanes, getTasks} from '../scheduling/schedule'
|
|
8
|
-
import {TaskType} from '../types'
|
|
8
|
+
import {HoldKind, TaskType} from '../types'
|
|
9
9
|
import type {
|
|
10
10
|
BuildableTarget,
|
|
11
11
|
FinalizerEntityRef,
|
|
@@ -102,7 +102,7 @@ export class ConstructionManager extends BaseManager {
|
|
|
102
102
|
let cumulativeSec = 0
|
|
103
103
|
for (const task of lane.schedule.tasks) {
|
|
104
104
|
cumulativeSec += task.duration.toNumber()
|
|
105
|
-
if (!
|
|
105
|
+
if (!isPushTask(task)) continue
|
|
106
106
|
if (!task.entitytarget) continue
|
|
107
107
|
const projectedEndMs = startedMs + cumulativeSec * 1000
|
|
108
108
|
if (projectedEndMs < nowMs) continue
|
|
@@ -145,51 +145,53 @@ export class ConstructionManager extends BaseManager {
|
|
|
145
145
|
|
|
146
146
|
private plotReservation(
|
|
147
147
|
plot: ServerContract.Types.entity_info,
|
|
148
|
+
builder: ServerContract.Types.entity_info | undefined,
|
|
148
149
|
now: Date
|
|
149
150
|
): {
|
|
150
151
|
builderId: UInt64
|
|
151
|
-
group?: UInt64
|
|
152
152
|
startsAt: number
|
|
153
153
|
completesAt: number
|
|
154
154
|
hasStarted: boolean
|
|
155
155
|
} | null {
|
|
156
|
-
|
|
156
|
+
const hold = plot.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
|
|
157
|
+
if (!hold) return null
|
|
158
|
+
const builderId = hold.counterpart.entity_id
|
|
159
|
+
const completesAt = hold.until.toDate().getTime()
|
|
160
|
+
const startsAt = this.builderBuildStart(builder, plot.id) ?? completesAt
|
|
161
|
+
return {
|
|
162
|
+
builderId,
|
|
163
|
+
startsAt,
|
|
164
|
+
completesAt,
|
|
165
|
+
hasStarted: startsAt <= now.getTime(),
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private builderBuildStart(
|
|
170
|
+
builder: ServerContract.Types.entity_info | undefined,
|
|
171
|
+
plotId: UInt64
|
|
172
|
+
): number | undefined {
|
|
173
|
+
if (!builder) return undefined
|
|
174
|
+
for (const lane of getLanes(builder)) {
|
|
157
175
|
const startedMs = lane.schedule.started.toDate().getTime()
|
|
158
176
|
let startSec = 0
|
|
159
177
|
for (const task of lane.schedule.tasks) {
|
|
160
|
-
if (task
|
|
161
|
-
if (!task.entitytarget) return null
|
|
162
|
-
const startsAt = startedMs + startSec * 1000
|
|
163
|
-
const completesAt = startsAt + task.duration.toNumber() * 1000
|
|
164
|
-
return {
|
|
165
|
-
builderId: task.entitytarget.entity_id,
|
|
166
|
-
group: task.entitygroup ?? undefined,
|
|
167
|
-
startsAt,
|
|
168
|
-
completesAt,
|
|
169
|
-
hasStarted: startsAt <= now.getTime(),
|
|
170
|
-
}
|
|
171
|
-
}
|
|
178
|
+
if (isBuildOfPlot(task, plotId)) return startedMs + startSec * 1000
|
|
172
179
|
startSec += task.duration.toNumber()
|
|
173
180
|
}
|
|
174
181
|
}
|
|
175
|
-
return
|
|
182
|
+
return undefined
|
|
176
183
|
}
|
|
177
184
|
|
|
178
185
|
private builderCancelability(
|
|
179
186
|
builder: ServerContract.Types.entity_info | undefined,
|
|
180
|
-
|
|
187
|
+
plotId: UInt64
|
|
181
188
|
): {cancelable: boolean; blockingTaskCount: number} {
|
|
182
|
-
if (!builder
|
|
189
|
+
if (!builder) {
|
|
183
190
|
return {cancelable: false, blockingTaskCount: 0}
|
|
184
191
|
}
|
|
185
192
|
for (const lane of getLanes(builder)) {
|
|
186
193
|
const tasks = lane.schedule.tasks
|
|
187
|
-
const buildIdx = tasks.findIndex(
|
|
188
|
-
(t) =>
|
|
189
|
-
t.type.toNumber() === TaskType.BUILDPLOT &&
|
|
190
|
-
t.entitygroup !== undefined &&
|
|
191
|
-
t.entitygroup.equals(group)
|
|
192
|
-
)
|
|
194
|
+
const buildIdx = tasks.findIndex((t) => isBuildOfPlot(t, plotId))
|
|
193
195
|
if (buildIdx < 0) continue
|
|
194
196
|
const trailing = tasks.length - 1 - buildIdx
|
|
195
197
|
return {cancelable: trailing === 0, blockingTaskCount: trailing}
|
|
@@ -200,14 +202,14 @@ export class ConstructionManager extends BaseManager {
|
|
|
200
202
|
private buildFromReservation(
|
|
201
203
|
res: {
|
|
202
204
|
builderId: UInt64
|
|
203
|
-
group?: UInt64
|
|
204
205
|
startsAt: number
|
|
205
206
|
completesAt: number
|
|
206
207
|
hasStarted: boolean
|
|
207
208
|
},
|
|
209
|
+
plotId: UInt64,
|
|
208
210
|
builder: ServerContract.Types.entity_info | undefined
|
|
209
211
|
): ScheduledBuild {
|
|
210
|
-
const {cancelable, blockingTaskCount} = this.builderCancelability(builder,
|
|
212
|
+
const {cancelable, blockingTaskCount} = this.builderCancelability(builder, plotId)
|
|
211
213
|
return {
|
|
212
214
|
shipId: res.builderId,
|
|
213
215
|
shipName: builder?.entity_name || res.builderId.toString(),
|
|
@@ -224,10 +226,12 @@ export class ConstructionManager extends BaseManager {
|
|
|
224
226
|
entities: ServerContract.Types.entity_info[],
|
|
225
227
|
now: Date
|
|
226
228
|
): ScheduledBuild | null {
|
|
227
|
-
const
|
|
229
|
+
const hold = plot.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
|
|
230
|
+
if (!hold) return null
|
|
231
|
+
const builder = entities.find((e) => e.id.equals(hold.counterpart.entity_id))
|
|
232
|
+
const res = this.plotReservation(plot, builder, now)
|
|
228
233
|
if (!res) return null
|
|
229
|
-
|
|
230
|
-
return this.buildFromReservation(res, builder)
|
|
234
|
+
return this.buildFromReservation(res, plot.id, builder)
|
|
231
235
|
}
|
|
232
236
|
|
|
233
237
|
scheduledBuildsByTarget(
|
|
@@ -238,10 +242,12 @@ export class ConstructionManager extends BaseManager {
|
|
|
238
242
|
const out = new Map<string, ScheduledBuild>()
|
|
239
243
|
for (const entity of entities) {
|
|
240
244
|
if (entity.type.toString() !== 'plot') continue
|
|
241
|
-
const
|
|
245
|
+
const hold = entity.holds.find((h) => h.kind.toNumber() === HoldKind.BUILD)
|
|
246
|
+
if (!hold) continue
|
|
247
|
+
const builder = byId.get(hold.counterpart.entity_id.toString())
|
|
248
|
+
const res = this.plotReservation(entity, builder, now)
|
|
242
249
|
if (!res) continue
|
|
243
|
-
|
|
244
|
-
out.set(entity.id.toString(), this.buildFromReservation(res, builder))
|
|
250
|
+
out.set(entity.id.toString(), this.buildFromReservation(res, entity.id, builder))
|
|
245
251
|
}
|
|
246
252
|
return out
|
|
247
253
|
}
|
|
@@ -354,15 +360,22 @@ function partitionSources(
|
|
|
354
360
|
return {eligible, unreachable}
|
|
355
361
|
}
|
|
356
362
|
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
|
|
363
|
+
function isPushTask(task: ServerContract.Types.task): boolean {
|
|
364
|
+
return task.type.toNumber() === TaskType.UNLOAD
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function isBuildOfPlot(task: ServerContract.Types.task, plotId: UInt64): boolean {
|
|
368
|
+
return (
|
|
369
|
+
task.type.toNumber() === TaskType.BUILDPLOT &&
|
|
370
|
+
task.entitytarget !== undefined &&
|
|
371
|
+
task.entitytarget.entity_id.equals(plotId)
|
|
372
|
+
)
|
|
360
373
|
}
|
|
361
374
|
|
|
362
375
|
function reservationsOf(source: ServerContract.Types.entity_info): Reservation[] {
|
|
363
376
|
const out = new Map<string, Reservation>()
|
|
364
377
|
for (const task of getTasks(source)) {
|
|
365
|
-
if (!
|
|
378
|
+
if (!isPushTask(task)) continue
|
|
366
379
|
if (!task.entitytarget) continue
|
|
367
380
|
const targetType = task.entitytarget.entity_type
|
|
368
381
|
const targetId = task.entitytarget.entity_id
|
package/src/scheduling/energy.ts
CHANGED
|
@@ -20,12 +20,11 @@ export function energyAtTime(entity: Projectable, now: Date): number {
|
|
|
20
20
|
|
|
21
21
|
for (const {task, startsAt} of ordered) {
|
|
22
22
|
const duration = task.duration.toNumber()
|
|
23
|
-
const isReserved = task.type.toNumber() === TaskType.RESERVED
|
|
24
23
|
const elapsed = Math.min(
|
|
25
24
|
Math.max(0, Math.floor((nowMs - startsAt.getTime()) / 1000)),
|
|
26
25
|
duration
|
|
27
26
|
)
|
|
28
|
-
const complete =
|
|
27
|
+
const complete = elapsed >= duration
|
|
29
28
|
const inProgress = !complete && elapsed > 0 && elapsed < duration
|
|
30
29
|
|
|
31
30
|
if (!complete && !inProgress) continue
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type {Action, UInt64} from '@wharfkit/antelope'
|
|
2
|
+
import type {ServerContract} from '../contracts'
|
|
3
|
+
import type {ActionsManager} from '../managers/actions'
|
|
4
|
+
import {hasResolvable, type ScheduleData} from './schedule'
|
|
5
|
+
|
|
6
|
+
type EntityInfo = ServerContract.Types.entity_info
|
|
7
|
+
|
|
8
|
+
export type CounterpartLookup = (entityId: UInt64) => EntityInfo | undefined
|
|
9
|
+
|
|
10
|
+
export type IdleResolveTarget = ScheduleData & {id: UInt64}
|
|
11
|
+
|
|
12
|
+
// A hold's driving task lives on its counterpart, so a hold resolves the counterpart, never the blocker.
|
|
13
|
+
export function composeIdleResolve(
|
|
14
|
+
blocker: IdleResolveTarget,
|
|
15
|
+
action: Action,
|
|
16
|
+
actions: ActionsManager,
|
|
17
|
+
now: Date,
|
|
18
|
+
lookupCounterpart?: CounterpartLookup
|
|
19
|
+
): Action[] {
|
|
20
|
+
const ids: UInt64[] = []
|
|
21
|
+
const seen = new Set<string>()
|
|
22
|
+
|
|
23
|
+
const add = (id: UInt64) => {
|
|
24
|
+
const key = id.toString()
|
|
25
|
+
if (seen.has(key)) return
|
|
26
|
+
seen.add(key)
|
|
27
|
+
ids.push(id)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (hasResolvable(blocker, now)) {
|
|
31
|
+
add(blocker.id)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const hold of blocker.holds ?? []) {
|
|
35
|
+
const counterpartId = hold.counterpart.entity_id
|
|
36
|
+
if (lookupCounterpart) {
|
|
37
|
+
const counterpart = lookupCounterpart(counterpartId)
|
|
38
|
+
if (!counterpart || !hasResolvable(counterpart, now)) continue
|
|
39
|
+
}
|
|
40
|
+
add(counterpartId)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [...ids.map((id) => actions.resolve(id)), action]
|
|
44
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {ServerContract} from '../contracts'
|
|
2
|
-
import {TaskType} from '../types'
|
|
2
|
+
import type {TaskType} from '../types'
|
|
3
3
|
|
|
4
4
|
type Schedule = ServerContract.Types.schedule
|
|
5
5
|
type Task = ServerContract.Types.task
|
|
@@ -27,7 +27,6 @@ export function laneRemaining(schedule: Schedule, now: Date): number {
|
|
|
27
27
|
|
|
28
28
|
export function laneComplete(schedule: Schedule, now: Date): boolean {
|
|
29
29
|
if (schedule.tasks.length === 0) return false
|
|
30
|
-
if (schedule.tasks.some((t) => t.type.toNumber() === TaskType.RESERVED)) return false
|
|
31
30
|
return laneRemaining(schedule, now) === 0
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -87,7 +86,6 @@ export function laneTaskRemaining(schedule: Schedule, index: number, now: Date):
|
|
|
87
86
|
|
|
88
87
|
export function laneTaskComplete(schedule: Schedule, index: number, now: Date): boolean {
|
|
89
88
|
if (index < 0 || index >= schedule.tasks.length) return false
|
|
90
|
-
if (schedule.tasks[index].type.toNumber() === TaskType.RESERVED) return false
|
|
91
89
|
const taskDuration = schedule.tasks[index].duration.toNumber()
|
|
92
90
|
return laneTaskElapsed(schedule, index, now) >= taskDuration
|
|
93
91
|
}
|
|
@@ -442,12 +442,11 @@ export function projectEntityAt(entity: Projectable, now: Date): ProjectedEntity
|
|
|
442
442
|
|
|
443
443
|
for (const {task, startsAt} of ordered) {
|
|
444
444
|
const duration = task.duration.toNumber()
|
|
445
|
-
const isReserved = task.type.toNumber() === TaskType.RESERVED
|
|
446
445
|
const elapsed = Math.min(
|
|
447
446
|
Math.max(0, Math.floor((nowMs - startsAt.getTime()) / 1000)),
|
|
448
447
|
duration
|
|
449
448
|
)
|
|
450
|
-
const taskComplete =
|
|
449
|
+
const taskComplete = elapsed >= duration
|
|
451
450
|
const taskInProgress = elapsed > 0 && elapsed < duration
|
|
452
451
|
|
|
453
452
|
if (!taskComplete && !taskInProgress) {
|
|
@@ -5,12 +5,14 @@ import * as core from './lane-core'
|
|
|
5
5
|
type Schedule = ServerContract.Types.schedule
|
|
6
6
|
type Task = ServerContract.Types.task
|
|
7
7
|
type Lane = ServerContract.Types.lane
|
|
8
|
+
type Hold = ServerContract.Types.hold
|
|
8
9
|
|
|
9
10
|
export const LANE_MOBILITY = 0
|
|
10
11
|
export const LANE_BARRIER = 255
|
|
11
12
|
|
|
12
13
|
export interface ScheduleData {
|
|
13
14
|
lanes?: Lane[]
|
|
15
|
+
holds?: Hold[]
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export interface LaneView {
|
|
@@ -52,11 +54,17 @@ export function hasSchedule(entity: ScheduleData): boolean {
|
|
|
52
54
|
return lanes.some((l) => l.schedule.tasks.length > 0)
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
export function hasHolds(entity: ScheduleData): boolean {
|
|
58
|
+
const holds = entity.holds
|
|
59
|
+
return !!holds && holds.length > 0
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
export function isIdle(entity: ScheduleData): boolean {
|
|
56
|
-
return !hasSchedule(entity)
|
|
63
|
+
return !hasSchedule(entity) && !hasHolds(entity)
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
export function isEntityIdle(entity: ScheduleData, now: Date): boolean {
|
|
67
|
+
if (hasHolds(entity)) return false
|
|
60
68
|
const lanes = entity.lanes
|
|
61
69
|
if (!lanes) return true
|
|
62
70
|
return lanes.every((l) => core.currentTaskIndexForLane(l.schedule, now) < 0)
|
|
@@ -106,13 +114,7 @@ export function scheduleComplete(entity: ScheduleData, now: Date): boolean {
|
|
|
106
114
|
let hasAnyTask = false
|
|
107
115
|
let remaining = 0
|
|
108
116
|
for (const l of lanes) {
|
|
109
|
-
|
|
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
|
-
}
|
|
117
|
+
if (l.schedule.tasks.length > 0) hasAnyTask = true
|
|
116
118
|
remaining = Math.max(remaining, core.laneRemaining(l.schedule, now))
|
|
117
119
|
}
|
|
118
120
|
if (!hasAnyTask) return false
|
|
@@ -186,7 +188,6 @@ export function resolveOrder(entity: ScheduleData, now: Date): ResolvedEvent[] {
|
|
|
186
188
|
const task = l.schedule.tasks[i]
|
|
187
189
|
endSec += task.duration.toNumber()
|
|
188
190
|
const completesAt = new Date(startedMs + endSec * 1000)
|
|
189
|
-
if (task.type.toNumber() === TaskType.RESERVED) break
|
|
190
191
|
if (completesAt.getTime() > now.getTime()) break
|
|
191
192
|
events.push({laneKey, taskIndex: i, task, completesAt})
|
|
192
193
|
}
|
package/src/types.ts
CHANGED