@shipload/sdk 1.0.0-next.31 → 1.0.0-next.33

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.
@@ -357,7 +357,7 @@
357
357
  },
358
358
  {
359
359
  "id": 10002,
360
- "mass": 6000,
360
+ "mass": 4000,
361
361
  "type": "component",
362
362
  "tier": 1
363
363
  },
@@ -369,13 +369,13 @@
369
369
  },
370
370
  {
371
371
  "id": 10004,
372
- "mass": 6000,
372
+ "mass": 4000,
373
373
  "type": "component",
374
374
  "tier": 1
375
375
  },
376
376
  {
377
377
  "id": 10005,
378
- "mass": 6000,
378
+ "mass": 4000,
379
379
  "type": "component",
380
380
  "tier": 1
381
381
  },
@@ -399,106 +399,106 @@
399
399
  },
400
400
  {
401
401
  "id": 10009,
402
- "mass": 6000,
402
+ "mass": 4000,
403
403
  "type": "component",
404
404
  "tier": 1
405
405
  },
406
406
  {
407
407
  "id": 10010,
408
- "mass": 6000,
408
+ "mass": 4000,
409
409
  "type": "component",
410
410
  "tier": 1
411
411
  },
412
412
  {
413
413
  "id": 10100,
414
- "mass": 900000,
414
+ "mass": 960000,
415
415
  "type": "module",
416
416
  "tier": 1,
417
417
  "subtype": "engine"
418
418
  },
419
419
  {
420
420
  "id": 10101,
421
- "mass": 1400000,
421
+ "mass": 960000,
422
422
  "type": "module",
423
423
  "tier": 1,
424
424
  "subtype": "generator"
425
425
  },
426
426
  {
427
427
  "id": 10102,
428
- "mass": 1200000,
428
+ "mass": 960000,
429
429
  "type": "module",
430
430
  "tier": 1,
431
431
  "subtype": "gatherer"
432
432
  },
433
433
  {
434
434
  "id": 10103,
435
- "mass": 1000000,
435
+ "mass": 960000,
436
436
  "type": "module",
437
437
  "tier": 1,
438
438
  "subtype": "loader"
439
439
  },
440
440
  {
441
441
  "id": 10104,
442
- "mass": 1200000,
442
+ "mass": 960000,
443
443
  "type": "module",
444
444
  "tier": 1,
445
445
  "subtype": "crafter"
446
446
  },
447
447
  {
448
448
  "id": 10105,
449
- "mass": 1200000,
449
+ "mass": 960000,
450
450
  "type": "module",
451
451
  "tier": 1,
452
452
  "subtype": "storage"
453
453
  },
454
454
  {
455
455
  "id": 10106,
456
- "mass": 1400000,
456
+ "mass": 960000,
457
457
  "type": "module",
458
458
  "tier": 1,
459
459
  "subtype": "hauler"
460
460
  },
461
461
  {
462
462
  "id": 10107,
463
- "mass": 1400000,
463
+ "mass": 960000,
464
464
  "type": "module",
465
465
  "tier": 1,
466
466
  "subtype": "warp"
467
467
  },
468
468
  {
469
469
  "id": 10108,
470
- "mass": 1000000,
470
+ "mass": 960000,
471
471
  "type": "module",
472
472
  "tier": 1,
473
473
  "subtype": "battery"
474
474
  },
475
475
  {
476
476
  "id": 10200,
477
- "mass": 1500000,
477
+ "mass": 1300000,
478
478
  "type": "entity",
479
479
  "tier": 1
480
480
  },
481
481
  {
482
482
  "id": 10201,
483
- "mass": 2300000,
483
+ "mass": 1900000,
484
484
  "type": "entity",
485
485
  "tier": 1
486
486
  },
487
487
  {
488
488
  "id": 10202,
489
- "mass": 5800000,
489
+ "mass": 4800000,
490
490
  "type": "entity",
491
491
  "tier": 1
492
492
  },
493
493
  {
494
494
  "id": 10203,
495
- "mass": 4400000,
495
+ "mass": 3700000,
496
496
  "type": "entity",
497
497
  "tier": 1
498
498
  },
499
499
  {
500
500
  "id": 10204,
501
- "mass": 5500000,
501
+ "mass": 4600000,
502
502
  "type": "entity",
503
503
  "tier": 1
504
504
  },
@@ -30,15 +30,15 @@
30
30
  },
31
31
  {
32
32
  "outputItemId": 10002,
33
- "outputMass": 6000,
33
+ "outputMass": 4000,
34
34
  "inputs": [
35
35
  {
36
36
  "itemId": 401,
37
- "quantity": 6
37
+ "quantity": 5
38
38
  },
39
39
  {
40
40
  "itemId": 501,
41
- "quantity": 9
41
+ "quantity": 5
42
42
  }
43
43
  ],
44
44
  "statSlots": [
@@ -92,15 +92,15 @@
92
92
  },
93
93
  {
94
94
  "outputItemId": 10004,
95
- "outputMass": 6000,
95
+ "outputMass": 4000,
96
96
  "inputs": [
97
97
  {
98
98
  "itemId": 201,
99
- "quantity": 6
99
+ "quantity": 5
100
100
  },
101
101
  {
102
102
  "itemId": 101,
103
- "quantity": 9
103
+ "quantity": 5
104
104
  }
105
105
  ],
106
106
  "statSlots": [
@@ -125,15 +125,15 @@
125
125
  },
126
126
  {
127
127
  "outputItemId": 10005,
128
- "outputMass": 6000,
128
+ "outputMass": 4000,
129
129
  "inputs": [
130
130
  {
131
131
  "itemId": 101,
132
- "quantity": 6
132
+ "quantity": 5
133
133
  },
134
134
  {
135
135
  "itemId": 301,
136
- "quantity": 9
136
+ "quantity": 5
137
137
  }
138
138
  ],
139
139
  "statSlots": [
@@ -245,15 +245,15 @@
245
245
  },
246
246
  {
247
247
  "outputItemId": 10009,
248
- "outputMass": 6000,
248
+ "outputMass": 4000,
249
249
  "inputs": [
250
250
  {
251
251
  "itemId": 301,
252
- "quantity": 6
252
+ "quantity": 5
253
253
  },
254
254
  {
255
255
  "itemId": 401,
256
- "quantity": 9
256
+ "quantity": 5
257
257
  }
258
258
  ],
259
259
  "statSlots": [
@@ -278,15 +278,15 @@
278
278
  },
279
279
  {
280
280
  "outputItemId": 10010,
281
- "outputMass": 6000,
281
+ "outputMass": 4000,
282
282
  "inputs": [
283
283
  {
284
284
  "itemId": 501,
285
- "quantity": 6
285
+ "quantity": 5
286
286
  },
287
287
  {
288
288
  "itemId": 201,
289
- "quantity": 9
289
+ "quantity": 5
290
290
  }
291
291
  ],
292
292
  "statSlots": [
@@ -311,7 +311,7 @@
311
311
  },
312
312
  {
313
313
  "outputItemId": 10100,
314
- "outputMass": 900000,
314
+ "outputMass": 960000,
315
315
  "inputs": [
316
316
  {
317
317
  "itemId": 10003,
@@ -340,7 +340,7 @@
340
340
  },
341
341
  {
342
342
  "outputItemId": 10101,
343
- "outputMass": 1400000,
343
+ "outputMass": 960000,
344
344
  "inputs": [
345
345
  {
346
346
  "itemId": 10004,
@@ -369,7 +369,7 @@
369
369
  },
370
370
  {
371
371
  "outputItemId": 10102,
372
- "outputMass": 1200000,
372
+ "outputMass": 960000,
373
373
  "inputs": [
374
374
  {
375
375
  "itemId": 10005,
@@ -418,7 +418,7 @@
418
418
  },
419
419
  {
420
420
  "outputItemId": 10103,
421
- "outputMass": 1000000,
421
+ "outputMass": 960000,
422
422
  "inputs": [
423
423
  {
424
424
  "itemId": 10007,
@@ -447,7 +447,7 @@
447
447
  },
448
448
  {
449
449
  "outputItemId": 10104,
450
- "outputMass": 1200000,
450
+ "outputMass": 960000,
451
451
  "inputs": [
452
452
  {
453
453
  "itemId": 10008,
@@ -480,7 +480,7 @@
480
480
  },
481
481
  {
482
482
  "outputItemId": 10105,
483
- "outputMass": 1200000,
483
+ "outputMass": 960000,
484
484
  "inputs": [
485
485
  {
486
486
  "itemId": 10001,
@@ -529,7 +529,7 @@
529
529
  },
530
530
  {
531
531
  "outputItemId": 10106,
532
- "outputMass": 1400000,
532
+ "outputMass": 960000,
533
533
  "inputs": [
534
534
  {
535
535
  "itemId": 10004,
@@ -570,7 +570,7 @@
570
570
  },
571
571
  {
572
572
  "outputItemId": 10107,
573
- "outputMass": 1400000,
573
+ "outputMass": 960000,
574
574
  "inputs": [
575
575
  {
576
576
  "itemId": 10004,
@@ -602,7 +602,7 @@
602
602
  },
603
603
  {
604
604
  "outputItemId": 10108,
605
- "outputMass": 1000000,
605
+ "outputMass": 960000,
606
606
  "inputs": [
607
607
  {
608
608
  "itemId": 10003,
@@ -651,7 +651,7 @@
651
651
  },
652
652
  {
653
653
  "outputItemId": 10200,
654
- "outputMass": 1500000,
654
+ "outputMass": 1300000,
655
655
  "inputs": [
656
656
  {
657
657
  "itemId": 10001,
@@ -700,7 +700,7 @@
700
700
  },
701
701
  {
702
702
  "outputItemId": 10201,
703
- "outputMass": 2300000,
703
+ "outputMass": 1900000,
704
704
  "inputs": [
705
705
  {
706
706
  "itemId": 10001,
@@ -749,7 +749,7 @@
749
749
  },
750
750
  {
751
751
  "outputItemId": 10202,
752
- "outputMass": 5800000,
752
+ "outputMass": 4800000,
753
753
  "inputs": [
754
754
  {
755
755
  "itemId": 10001,
@@ -798,7 +798,7 @@
798
798
  },
799
799
  {
800
800
  "outputItemId": 10203,
801
- "outputMass": 4400000,
801
+ "outputMass": 3700000,
802
802
  "inputs": [
803
803
  {
804
804
  "itemId": 10001,
@@ -847,7 +847,7 @@
847
847
  },
848
848
  {
849
849
  "outputItemId": 10204,
850
- "outputMass": 5500000,
850
+ "outputMass": 4600000,
851
851
  "inputs": [
852
852
  {
853
853
  "itemId": 10001,
@@ -97,7 +97,7 @@ export function computeLoaderCapabilities(stats: Record<string, number>): {
97
97
 
98
98
  return {
99
99
  mass: Math.max(200, 2000 - Math.floor(insulation * 2)),
100
- thrust: 1 + Math.floor(plasticity / 500),
100
+ thrust: 1 + Math.floor((plasticity * plasticity) / 10000),
101
101
  quantity: 1,
102
102
  }
103
103
  }
@@ -109,6 +109,7 @@ export function makeEntity(packedItemId: number, state: EntityStateInput): Entit
109
109
  cargomass: UInt32.from(state.cargomass ?? 0),
110
110
  cargo: state.cargo || [],
111
111
  lanes,
112
+ holds: [],
112
113
  }
113
114
 
114
115
  if (state.energy !== undefined) info.energy = UInt16.from(state.energy)
package/src/errors.ts CHANGED
@@ -45,7 +45,6 @@ export const WAREHOUSE_ALREADY_AT_LOCATION = 'Warehouse already exists at this l
45
45
  export const CONTAINER_NOT_FOUND = 'Cannot find container for given id.'
46
46
  export const DESTINATION_CAPACITY_EXCEEDED =
47
47
  'Destination entity does not have enough capacity for the gather.'
48
- export const CANCEL_PAIRED_HAS_PENDING = 'Cannot cancel transfer, paired entity has pending tasks.'
49
48
  export const GROUP_EMPTY = 'Group travel requires at least one entity.'
50
49
  export const GROUP_NO_THRUST = 'Group travel requires at least one entity with engines.'
51
50
  export const GROUP_NOT_SAME_LOCATION = 'All entities must be at the same location for group travel.'
@@ -65,6 +65,8 @@ export type {
65
65
  Reservation,
66
66
  } from './managers'
67
67
  export type {EntityRefInput} from './managers/actions'
68
+ export type {WrapDeposit} from './managers/nft'
69
+ export {resolveLockedAmount} from './managers/nft'
68
70
 
69
71
  export {
70
72
  getItem,
@@ -227,6 +229,9 @@ export type {
227
229
  export {taskCargoChanges} from './scheduling/task-cargo'
228
230
  export type {TaskCargoChange, TaskCargoDirection} from './scheduling/task-cargo'
229
231
 
232
+ export {composeIdleResolve} from './scheduling/idle-resolve'
233
+ export type {CounterpartLookup, IdleResolveTarget} from './scheduling/idle-resolve'
234
+
230
235
  export {
231
236
  projectedCargoAvailableAt,
232
237
  availableForItem,
@@ -73,14 +73,6 @@ export class ActionsManager extends BaseManager {
73
73
  })
74
74
  }
75
75
 
76
- retarget(sourceId: UInt64Type, taskIndex: UInt64Type, newDestId: UInt64Type): Action {
77
- return this.server.action('retarget', {
78
- source_id: UInt64.from(sourceId),
79
- task_index: UInt64.from(taskIndex),
80
- new_dest_id: UInt64.from(newDestId),
81
- })
82
- }
83
-
84
76
  recharge(entityId: UInt64Type): Action {
85
77
  return this.server.action('recharge', {
86
78
  id: UInt64.from(entityId),
@@ -100,14 +92,26 @@ export class ActionsManager extends BaseManager {
100
92
  })
101
93
  }
102
94
 
103
- transfer(
104
- sourceId: UInt64Type,
105
- destId: UInt64Type,
95
+ load(
96
+ id: UInt64Type,
97
+ fromId: UInt64Type,
106
98
  items: ServerContract.ActionParams.Type.cargo_item[]
107
99
  ): Action {
108
- return this.server.action('transfer', {
109
- source_id: UInt64.from(sourceId),
110
- dest_id: UInt64.from(destId),
100
+ return this.server.action('load', {
101
+ id: UInt64.from(id),
102
+ from_id: UInt64.from(fromId),
103
+ items,
104
+ })
105
+ }
106
+
107
+ unload(
108
+ id: UInt64Type,
109
+ toId: UInt64Type,
110
+ items: ServerContract.ActionParams.Type.cargo_item[]
111
+ ): Action {
112
+ return this.server.action('unload', {
113
+ id: UInt64.from(id),
114
+ to_id: UInt64.from(toId),
111
115
  items,
112
116
  })
113
117
  }
@@ -154,14 +158,19 @@ export class ActionsManager extends BaseManager {
154
158
  entityId: UInt64Type,
155
159
  recipeId: number,
156
160
  quantity: number,
157
- inputs: ServerContract.ActionParams.Type.cargo_item[]
161
+ inputs: ServerContract.ActionParams.Type.cargo_item[],
162
+ target?: UInt64Type
158
163
  ): Action {
159
- return this.server.action('craft', {
164
+ const params: ServerContract.ActionParams.craft = {
160
165
  id: UInt64.from(entityId),
161
166
  recipe_id: UInt16.from(recipeId),
162
167
  quantity: UInt32.from(quantity),
163
168
  inputs,
164
- })
169
+ }
170
+ if (target !== undefined) {
171
+ params.target = UInt64.from(target)
172
+ }
173
+ return this.server.action('craft', params)
165
174
  }
166
175
 
167
176
  blend(entityId: UInt64Type, inputs: ServerContract.ActionParams.Type.cargo_item[]): Action {
@@ -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 (!isTransferTask(task)) continue
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
- for (const lane of getLanes(plot)) {
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.type.toNumber() === TaskType.RESERVED) {
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 null
182
+ return undefined
176
183
  }
177
184
 
178
185
  private builderCancelability(
179
186
  builder: ServerContract.Types.entity_info | undefined,
180
- group: UInt64 | undefined
187
+ plotId: UInt64
181
188
  ): {cancelable: boolean; blockingTaskCount: number} {
182
- if (!builder || group === undefined) {
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, res.group)
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 res = this.plotReservation(plot, now)
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
- const builder = entities.find((e) => e.id.equals(res.builderId))
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 res = this.plotReservation(entity, now)
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
- const builder = byId.get(res.builderId.toString())
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 isTransferTask(task: ServerContract.Types.task): boolean {
358
- const type = task.type.toNumber()
359
- return type === TaskType.LOAD || type === TaskType.UNLOAD
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 (!isTransferTask(task)) continue
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
@@ -0,0 +1,14 @@
1
+ import {describe, expect, test} from 'bun:test'
2
+ import {resolveLockedAmount} from './nft'
3
+
4
+ describe('resolveLockedAmount', () => {
5
+ test('no fee refunds the full cost', () => {
6
+ expect(resolveLockedAmount(5_0000n, 0)).toBe(5_0000n)
7
+ })
8
+ test('2% fee floors the fee and refunds the remainder', () => {
9
+ expect(resolveLockedAmount(5_0000n, 200)).toBe(4_9000n)
10
+ })
11
+ test('rounding floors the fee (contract uses integer division)', () => {
12
+ expect(resolveLockedAmount(101n, 250)).toBe(99n) // fee = floor(101*250/10000)=2
13
+ })
14
+ })