@shipload/sdk 1.0.0-next.24 → 1.0.0-next.26

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.
@@ -17,6 +17,7 @@ import {
17
17
  import {BaseManager} from './base'
18
18
  import type {CoordinatesType} from '../types'
19
19
  import {ServerContract} from '../contracts'
20
+ import {ATOMICASSETS_ABI, SHIPLOAD_COLLECTION} from '../nft/atomicassets'
20
21
 
21
22
  export type EntityRefInput = {
22
23
  entityType: NameType
@@ -206,14 +207,27 @@ export class ActionsManager extends BaseManager {
206
207
  })
207
208
  }
208
209
 
210
+ swapmodule(
211
+ entityId: UInt64Type,
212
+ moduleIndex: number,
213
+ moduleRef: ServerContract.ActionParams.Type.cargo_ref
214
+ ): Action {
215
+ return this.server.action('swapmodule', {
216
+ entity_id: UInt64.from(entityId),
217
+ module_index: moduleIndex,
218
+ module_ref: moduleRef,
219
+ })
220
+ }
221
+
209
222
  async wrap(
210
223
  owner: NameType,
211
224
  entityId: UInt64Type,
212
225
  nexusId: UInt64Type,
213
226
  cargoId: UInt64Type,
214
- quantity: UInt64Type
227
+ quantity: UInt64Type,
228
+ opts: {claimRam?: boolean} = {}
215
229
  ): Promise<Action[]> {
216
- return [
230
+ const actions: Action[] = [
217
231
  this.platform.action('wrapcargo', {
218
232
  game: this.server.account,
219
233
  owner: Name.from(owner),
@@ -223,6 +237,12 @@ export class ActionsManager extends BaseManager {
223
237
  quantity: UInt64.from(quantity),
224
238
  }),
225
239
  ]
240
+ const claimRam =
241
+ opts.claimRam ?? (this.atomicAssetsAccount ?? 'atomicassets') !== 'atomicassets'
242
+ if (claimRam) {
243
+ actions.push(this.setLastPayer(owner, SHIPLOAD_COLLECTION))
244
+ }
245
+ return actions
226
246
  }
227
247
 
228
248
  undeploy(hostId: UInt64Type, targetId: UInt64Type): Action {
@@ -232,12 +252,19 @@ export class ActionsManager extends BaseManager {
232
252
  })
233
253
  }
234
254
 
255
+ claimStarter(owner: NameType): Action {
256
+ return this.server.action('claimstarter', {
257
+ owner: Name.from(owner),
258
+ })
259
+ }
260
+
235
261
  async wrapEntity(
236
262
  owner: NameType,
237
263
  entityId: UInt64Type,
238
- nexusId: UInt64Type
264
+ nexusId: UInt64Type,
265
+ opts: {claimRam?: boolean} = {}
239
266
  ): Promise<Action[]> {
240
- return [
267
+ const actions: Action[] = [
241
268
  this.platform.action('wrapentity', {
242
269
  game: this.server.account,
243
270
  owner: Name.from(owner),
@@ -245,6 +272,12 @@ export class ActionsManager extends BaseManager {
245
272
  nexus_id: UInt64.from(nexusId),
246
273
  }),
247
274
  ]
275
+ const claimRam =
276
+ opts.claimRam ?? (this.atomicAssetsAccount ?? 'atomicassets') !== 'atomicassets'
277
+ if (claimRam) {
278
+ actions.push(this.setLastPayer(owner, SHIPLOAD_COLLECTION))
279
+ }
280
+ return actions
248
281
  }
249
282
 
250
283
  placecargo(owner: NameType, hostId: UInt64Type, assetId: UInt64Type): Action {
@@ -264,17 +297,20 @@ export class ActionsManager extends BaseManager {
264
297
  }
265
298
 
266
299
  transferForUnwrap(owner: NameType, assetId: UInt64Type): Action {
267
- return Action.from({
268
- account: 'atomicassets',
269
- name: 'transfer',
270
- authorization: [{actor: Name.from(owner), permission: 'active'}],
271
- data: {
272
- from: Name.from(owner),
273
- to: this.platform.account,
274
- asset_ids: [UInt64.from(assetId)],
275
- memo: 'unwrap',
300
+ return Action.from(
301
+ {
302
+ account: this.atomicAssetsAccount,
303
+ name: 'transfer',
304
+ authorization: [{actor: Name.from(owner), permission: 'active'}],
305
+ data: {
306
+ from: Name.from(owner),
307
+ to: this.platform.account,
308
+ asset_ids: [UInt64.from(assetId)],
309
+ memo: 'unwrap',
310
+ },
276
311
  },
277
- })
312
+ ATOMICASSETS_ABI
313
+ )
278
314
  }
279
315
 
280
316
  // Two top-level actions the wallet signs to unwrap an NFT into a host's cargo.
@@ -291,12 +327,27 @@ export class ActionsManager extends BaseManager {
291
327
  }
292
328
 
293
329
  setRamPayer(newPayer: NameType, assetId: UInt64Type): Action {
294
- return Action.from({
295
- account: 'atomicassets',
296
- name: 'setrampayer',
297
- authorization: [{actor: Name.from(newPayer), permission: 'active'}],
298
- data: {new_payer: Name.from(newPayer), asset_id: UInt64.from(assetId)},
299
- })
330
+ return Action.from(
331
+ {
332
+ account: this.atomicAssetsAccount,
333
+ name: 'setrampayer',
334
+ authorization: [{actor: Name.from(newPayer), permission: 'active'}],
335
+ data: {new_payer: Name.from(newPayer), asset_id: UInt64.from(assetId)},
336
+ },
337
+ ATOMICASSETS_ABI
338
+ )
339
+ }
340
+
341
+ setLastPayer(owner: NameType, collectionName: NameType): Action {
342
+ return Action.from(
343
+ {
344
+ account: this.atomicAssetsAccount,
345
+ name: 'setlastpayer',
346
+ authorization: [{actor: Name.from(owner), permission: 'active'}],
347
+ data: {owner: Name.from(owner), collection_name: Name.from(collectionName)},
348
+ },
349
+ ATOMICASSETS_ABI
350
+ )
300
351
  }
301
352
 
302
353
  demolish(entityId: UInt64Type): Action {
@@ -15,6 +15,10 @@ export abstract class BaseManager {
15
15
  return this.context.platform
16
16
  }
17
17
 
18
+ protected get atomicAssetsAccount() {
19
+ return this.context.atomicAssetsAccount
20
+ }
21
+
18
22
  protected async getGame() {
19
23
  return this.context.getGame()
20
24
  }
@@ -4,7 +4,7 @@ import type {Item} from '../types'
4
4
  import type {Recipe} from '../data/recipes-runtime'
5
5
  import type {PlotProgress} from './plot'
6
6
 
7
- export type BuildState = 'initializing' | 'accepting' | 'ready' | 'finalizing'
7
+ export type BuildState = 'initializing' | 'accepting' | 'ready' | 'scheduled' | 'finalizing'
8
8
 
9
9
  export type FinalizerCapability = 'crafter'
10
10
 
@@ -20,6 +20,7 @@ export interface BuildableTarget {
20
20
  finalizeAction: Name
21
21
  finalizerCapability: FinalizerCapability
22
22
  activeTask?: ServerContract.Types.task
23
+ scheduledBuild?: ScheduledBuild
23
24
  }
24
25
 
25
26
  export interface SourceEntityRef {
@@ -60,6 +61,16 @@ export interface InboundTransfer {
60
61
  etaSeconds: number
61
62
  }
62
63
 
64
+ export interface ScheduledBuild {
65
+ shipId: UInt64
66
+ shipName: string
67
+ hasStarted: boolean
68
+ startsAt: number
69
+ completesAt: number
70
+ cancelable: boolean
71
+ blockingTaskCount: number
72
+ }
73
+
63
74
  export interface Reservation {
64
75
  targetEntityId: UInt64
65
76
  targetEntityType: Name
@@ -10,6 +10,7 @@ import type {
10
10
  FinalizerEntityRef,
11
11
  InboundTransfer,
12
12
  Reservation,
13
+ ScheduledBuild,
13
14
  SourceCargoStack,
14
15
  SourceEntityRef,
15
16
  } from './construction-types'
@@ -22,11 +23,12 @@ export class ConstructionManager extends BaseManager {
22
23
  getTarget(
23
24
  entity: ServerContract.Types.entity_row,
24
25
  cargo: ServerContract.Types.cargo_row[],
25
- activeTask?: ServerContract.Types.task
26
+ activeTask?: ServerContract.Types.task,
27
+ scheduledBuild?: ScheduledBuild
26
28
  ): BuildableTarget | null {
27
29
  const kind = entity.kind.toString()
28
30
  if (kind === 'plot') {
29
- return this.plot.buildableTarget(entity, cargo, activeTask)
31
+ return this.plot.buildableTarget(entity, cargo, activeTask, scheduledBuild)
30
32
  }
31
33
  return null
32
34
  }
@@ -140,6 +142,107 @@ export class ConstructionManager extends BaseManager {
140
142
  return out
141
143
  }
142
144
 
145
+ private plotReservation(
146
+ plot: ServerContract.Types.entity_info,
147
+ now: Date
148
+ ): {
149
+ builderId: UInt64
150
+ group?: UInt64
151
+ startsAt: number
152
+ completesAt: number
153
+ hasStarted: boolean
154
+ } | null {
155
+ const schedule = plot.schedule
156
+ if (!schedule) return null
157
+ const tasks = schedule.tasks
158
+ const startedMs = schedule.started.toDate().getTime()
159
+ let startSec = 0
160
+ for (const task of tasks) {
161
+ if (task.type.toNumber() === TaskType.RESERVED) {
162
+ if (!task.entitytarget) return null
163
+ const startsAt = startedMs + startSec * 1000
164
+ const completesAt = startsAt + task.duration.toNumber() * 1000
165
+ return {
166
+ builderId: task.entitytarget.entity_id,
167
+ group: task.entitygroup ?? undefined,
168
+ startsAt,
169
+ completesAt,
170
+ hasStarted: startsAt <= now.getTime(),
171
+ }
172
+ }
173
+ startSec += task.duration.toNumber()
174
+ }
175
+ return null
176
+ }
177
+
178
+ private builderCancelability(
179
+ builder: ServerContract.Types.entity_info | undefined,
180
+ group: UInt64 | undefined
181
+ ): {cancelable: boolean; blockingTaskCount: number} {
182
+ if (!builder?.schedule || group === undefined) {
183
+ return {cancelable: false, blockingTaskCount: 0}
184
+ }
185
+ const tasks = builder.schedule.tasks
186
+ const buildIdx = tasks.findIndex(
187
+ (t) =>
188
+ t.type.toNumber() === TaskType.BUILDPLOT &&
189
+ t.entitygroup !== undefined &&
190
+ t.entitygroup.equals(group)
191
+ )
192
+ if (buildIdx < 0) return {cancelable: false, blockingTaskCount: 0}
193
+ const trailing = tasks.length - 1 - buildIdx
194
+ return {cancelable: trailing === 0, blockingTaskCount: trailing}
195
+ }
196
+
197
+ private buildFromReservation(
198
+ res: {
199
+ builderId: UInt64
200
+ group?: UInt64
201
+ startsAt: number
202
+ completesAt: number
203
+ hasStarted: boolean
204
+ },
205
+ builder: ServerContract.Types.entity_info | undefined
206
+ ): ScheduledBuild {
207
+ const {cancelable, blockingTaskCount} = this.builderCancelability(builder, res.group)
208
+ return {
209
+ shipId: res.builderId,
210
+ shipName: builder?.entity_name || res.builderId.toString(),
211
+ hasStarted: res.hasStarted,
212
+ startsAt: res.startsAt,
213
+ completesAt: res.completesAt,
214
+ cancelable,
215
+ blockingTaskCount,
216
+ }
217
+ }
218
+
219
+ scheduledBuildFor(
220
+ plot: ServerContract.Types.entity_info,
221
+ entities: ServerContract.Types.entity_info[],
222
+ now: Date
223
+ ): ScheduledBuild | null {
224
+ const res = this.plotReservation(plot, now)
225
+ if (!res) return null
226
+ const builder = entities.find((e) => e.id.equals(res.builderId))
227
+ return this.buildFromReservation(res, builder)
228
+ }
229
+
230
+ scheduledBuildsByTarget(
231
+ entities: ServerContract.Types.entity_info[],
232
+ now: Date
233
+ ): Map<string, ScheduledBuild> {
234
+ const byId = new Map(entities.map((e) => [e.id.toString(), e]))
235
+ const out = new Map<string, ScheduledBuild>()
236
+ for (const entity of entities) {
237
+ if (entity.type.toString() !== 'plot') continue
238
+ const res = this.plotReservation(entity, now)
239
+ if (!res) continue
240
+ const builder = byId.get(res.builderId.toString())
241
+ out.set(entity.id.toString(), this.buildFromReservation(res, builder))
242
+ }
243
+ return out
244
+ }
245
+
143
246
  reservationsFrom(
144
247
  sourceEntityId: UInt64,
145
248
  entities: ServerContract.Types.entity_info[]
@@ -27,7 +27,8 @@ export class GameContext {
27
27
  constructor(
28
28
  public readonly client: APIClient,
29
29
  public readonly server: Contract,
30
- public readonly platform: Contract
30
+ public readonly platform: Contract,
31
+ public readonly atomicAssetsAccount: string = 'atomicassets'
31
32
  ) {}
32
33
 
33
34
  get entities(): EntitiesManager {
@@ -20,5 +20,6 @@ export type {
20
20
  FinalizerEntityRef,
21
21
  FinalizerCapability,
22
22
  InboundTransfer,
23
+ ScheduledBuild,
23
24
  Reservation,
24
25
  } from './construction-types'
@@ -6,7 +6,7 @@ import {calc_craft_duration} from '../capabilities/crafting'
6
6
  import {TaskType} from '../types'
7
7
  import {BaseManager} from './base'
8
8
  import type {ServerContract} from '../contracts'
9
- import type {BuildableTarget} from './construction-types'
9
+ import type {BuildableTarget, ScheduledBuild} from './construction-types'
10
10
 
11
11
  export interface PlotProgressInputRow {
12
12
  itemId: number
@@ -67,7 +67,8 @@ export class PlotManager extends BaseManager {
67
67
  buildableTarget(
68
68
  plot: ServerContract.Types.entity_row,
69
69
  cargo: ServerContract.Types.cargo_row[],
70
- activeTask?: ServerContract.Types.task
70
+ activeTask?: ServerContract.Types.task,
71
+ scheduledBuild?: ScheduledBuild
71
72
  ): BuildableTarget {
72
73
  const progress = this.progress(plot, cargo)
73
74
  const targetItemId = Number(plot.item_id.toString())
@@ -79,10 +80,12 @@ export class PlotManager extends BaseManager {
79
80
 
80
81
  let state: BuildableTarget['state']
81
82
  const taskType = activeTask?.type.toNumber()
82
- if (taskType === TaskType.CLAIMPLOT) {
83
- state = 'initializing'
84
- } else if (taskType === TaskType.BUILDPLOT) {
83
+ if (scheduledBuild?.hasStarted) {
85
84
  state = 'finalizing'
85
+ } else if (scheduledBuild) {
86
+ state = 'scheduled'
87
+ } else if (taskType === TaskType.CLAIMPLOT) {
88
+ state = 'initializing'
86
89
  } else if (progress.isComplete) {
87
90
  state = 'ready'
88
91
  } else {
@@ -101,6 +104,7 @@ export class PlotManager extends BaseManager {
101
104
  finalizeAction: Name.from('buildplot'),
102
105
  finalizerCapability: 'crafter',
103
106
  activeTask,
107
+ scheduledBuild,
104
108
  }
105
109
  }
106
110