@shipload/sdk 1.0.0-next.67 → 1.0.0-next.69

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.
@@ -18,6 +18,7 @@ function task(over: Partial<{type: number; duration: number; cancelable: number;
18
18
 
19
19
  function entity(tasks: ReturnType<typeof task>[], startedISO = T0) {
20
20
  return ServerContract.Types.entity_info.from({
21
+ projected_at: 0,
21
22
  type: 'ship',
22
23
  id: 1,
23
24
  owner: 'player.gm',
@@ -155,6 +156,7 @@ describe('cancelEligibility — effects', () => {
155
156
  const lt = loadTask('warehouse', 6, 1, 4)
156
157
  const upcoming = new Date('2026-06-18T23:59:50.000Z')
157
158
  const e = ServerContract.Types.entity_info.from({
159
+ projected_at: 0,
158
160
  type: 'ship',
159
161
  id: 1,
160
162
  owner: 'player.gm',
@@ -188,6 +190,7 @@ describe('cancelEligibility — effects', () => {
188
190
  const lt = loadTask('warehouse', 6, 1, 4)
189
191
  const upcoming = new Date('2026-06-18T23:59:50.000Z')
190
192
  const e = ServerContract.Types.entity_info.from({
193
+ projected_at: 0,
191
194
  type: 'ship',
192
195
  id: 1,
193
196
  owner: 'player.gm',
@@ -220,6 +223,7 @@ describe('cancelEligibility — effects', () => {
220
223
  test('uncoupled task emits no releasedHolds entry', () => {
221
224
  const upcoming = new Date('2026-06-18T23:59:50.000Z')
222
225
  const e = ServerContract.Types.entity_info.from({
226
+ projected_at: 0,
223
227
  type: 'ship',
224
228
  id: 1,
225
229
  owner: 'player.gm',
@@ -272,6 +276,7 @@ describe('cancelEligibility — feasibility', () => {
272
276
  couplings: [],
273
277
  })
274
278
  const e = ServerContract.Types.entity_info.from({
279
+ projected_at: 0,
275
280
  type: 'ship',
276
281
  id: 1,
277
282
  owner: 'player.gm',
@@ -318,6 +323,7 @@ describe('cancelEligibility — feasibility', () => {
318
323
  couplings: [],
319
324
  })
320
325
  const e = ServerContract.Types.entity_info.from({
326
+ projected_at: 0,
321
327
  type: 'ship',
322
328
  id: 1,
323
329
  owner: 'player.gm',
@@ -357,6 +363,7 @@ describe('cancelEligibility — feasibility', () => {
357
363
  couplings: [],
358
364
  })
359
365
  const e = ServerContract.Types.entity_info.from({
366
+ projected_at: 0,
360
367
  type: 'ship',
361
368
  id: 1,
362
369
  owner: 'player.gm',
@@ -397,6 +404,7 @@ describe('cancelEligibility — cross-entity strand (counterpart queued consumer
397
404
  }[] = []
398
405
  ) {
399
406
  return ServerContract.Types.entity_info.from({
407
+ projected_at: 0,
400
408
  type: 'ship',
401
409
  id,
402
410
  owner: 'player.gm',
@@ -1,4 +1,4 @@
1
- import {Name, type UInt16, UInt32, UInt64} from '@wharfkit/antelope'
1
+ import {Name, type TimePoint, type UInt16, UInt32, UInt64} from '@wharfkit/antelope'
2
2
  import {ServerContract} from '../contracts'
3
3
  import {Coordinates, TaskType} from '../types'
4
4
  import {
@@ -75,6 +75,7 @@ export interface Projectable extends ScheduleData {
75
75
  capacity?: UInt32
76
76
  cargo: ServerContract.Types.cargo_item[]
77
77
  cargomass: UInt32
78
+ projected_at?: TimePoint
78
79
  owner?: Name
79
80
  stats?: bigint
80
81
  item_id?: number | UInt16
@@ -425,6 +426,18 @@ export interface ProjectionOptions {
425
426
  upToTaskIndex?: number
426
427
  }
427
428
 
429
+ // Mirrors the contract's completed_task_count_at: inclusive at the anchor.
430
+ function anchoredSkipCount(entity: Projectable, ordered: readonly schedule.OrderedTask[]): number {
431
+ const anchor = entity.projected_at
432
+ if (anchor === undefined) return 0
433
+ const anchorMs = Number(anchor.toMilliseconds())
434
+ let count = 0
435
+ for (const {completesAt} of ordered) {
436
+ if (completesAt.getTime() <= anchorMs) count++
437
+ }
438
+ return count
439
+ }
440
+
428
441
  export function projectEntity(entity: Projectable, options?: ProjectionOptions): ProjectedEntity {
429
442
  const projected = createProjectedEntity(entity)
430
443
  const ordered = schedule.orderedTasks(entity)
@@ -435,17 +448,17 @@ export function projectEntity(entity: Projectable, options?: ProjectionOptions):
435
448
  ? Math.max(0, Math.min(options.upToTaskIndex, ordered.length))
436
449
  : ordered.length
437
450
 
438
- for (let i = 0; i < taskCount; i++) {
451
+ for (let i = anchoredSkipCount(entity, ordered); i < taskCount; i++) {
439
452
  applyTask(projected, ordered[i].task)
440
453
  }
441
454
  return projected
442
455
  }
443
456
 
444
457
  export function projectRemainingAt(entity: Projectable, _now: Date): ProjectedEntity {
445
- // Resolve is lazy/entity-global: completed tasks are unsettled until resolve, so replay all.
446
458
  const projected = createProjectedEntity(entity)
447
- for (const {task} of schedule.orderedTasks(entity)) {
448
- applyTask(projected, task)
459
+ const ordered = schedule.orderedTasks(entity)
460
+ for (let i = anchoredSkipCount(entity, ordered); i < ordered.length; i++) {
461
+ applyTask(projected, ordered[i].task)
449
462
  }
450
463
  return projected
451
464
  }
@@ -533,8 +546,10 @@ export function projectEntityAt(entity: Projectable, now: Date): ProjectedEntity
533
546
  }
534
547
 
535
548
  const nowMs = now.getTime()
549
+ const skip = anchoredSkipCount(entity, ordered)
536
550
 
537
- for (const {task, startsAt} of ordered) {
551
+ for (let i = skip; i < ordered.length; i++) {
552
+ const {task, startsAt} = ordered[i]
538
553
  const duration = task.duration.toNumber()
539
554
  const elapsed = Math.min(
540
555
  Math.max(0, Math.floor((nowMs - startsAt.getTime()) / 1000)),
@@ -21,6 +21,7 @@ function entity(
21
21
  startedISO = T0
22
22
  ) {
23
23
  return ServerContract.Types.entity_info.from({
24
+ projected_at: 0,
24
25
  type: 'ship',
25
26
  id: 1,
26
27
  owner: 'player.gm',