@shipload/sdk 1.0.0-next.5 → 1.0.0-next.50

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.
Files changed (144) hide show
  1. package/lib/scan.d.ts +52 -0
  2. package/lib/scan.js +162 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +152 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +2881 -1149
  7. package/lib/shipload.js +13031 -5511
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +12807 -5460
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +1028 -0
  12. package/lib/testing.js +4336 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4330 -0
  15. package/lib/testing.m.js.map +1 -0
  16. package/package.json +20 -2
  17. package/src/capabilities/craftable.ts +51 -0
  18. package/src/capabilities/crafting.test.ts +7 -0
  19. package/src/capabilities/crafting.ts +6 -7
  20. package/src/capabilities/gathering.test.ts +29 -0
  21. package/src/capabilities/gathering.ts +37 -19
  22. package/src/capabilities/index.ts +0 -1
  23. package/src/capabilities/modules.ts +22 -0
  24. package/src/capabilities/storage.ts +16 -1
  25. package/src/contracts/platform.ts +231 -3
  26. package/src/contracts/server.ts +1160 -520
  27. package/src/coordinates/address.ts +88 -0
  28. package/src/coordinates/constants.test.ts +15 -0
  29. package/src/coordinates/constants.ts +23 -0
  30. package/src/coordinates/index.ts +15 -0
  31. package/src/coordinates/memo.test.ts +47 -0
  32. package/src/coordinates/memo.ts +20 -0
  33. package/src/coordinates/permutation.ts +77 -0
  34. package/src/coordinates/regions.ts +48 -0
  35. package/src/coordinates/sectors.ts +115 -0
  36. package/src/data/capabilities.ts +31 -16
  37. package/src/data/capability-formulas.ts +21 -14
  38. package/src/data/catalog.ts +0 -5
  39. package/src/data/colors.ts +13 -47
  40. package/src/data/entities.json +154 -10
  41. package/src/data/item-ids.ts +31 -12
  42. package/src/data/items.json +192 -74
  43. package/src/data/kind-registry.json +131 -0
  44. package/src/data/kind-registry.ts +172 -0
  45. package/src/data/metadata.ts +166 -48
  46. package/src/data/recipes-runtime.ts +5 -23
  47. package/src/data/recipes.json +1003 -163
  48. package/src/derivation/build-methods.test.ts +13 -0
  49. package/src/derivation/build-methods.ts +48 -0
  50. package/src/derivation/capabilities.test.ts +151 -0
  51. package/src/derivation/capabilities.ts +527 -0
  52. package/src/derivation/capability-mappings.ts +58 -16
  53. package/src/derivation/crafting.ts +36 -33
  54. package/src/derivation/index.ts +28 -2
  55. package/src/derivation/recipe-usage.test.ts +81 -0
  56. package/src/derivation/recipe-usage.ts +141 -0
  57. package/src/derivation/reserve-regen.ts +34 -0
  58. package/src/derivation/resources.ts +98 -19
  59. package/src/derivation/rollups.test.ts +55 -0
  60. package/src/derivation/rollups.ts +56 -0
  61. package/src/derivation/stars.test.ts +67 -0
  62. package/src/derivation/stars.ts +25 -0
  63. package/src/derivation/stats.ts +6 -6
  64. package/src/derivation/stratum.ts +26 -22
  65. package/src/derivation/tiers.ts +40 -7
  66. package/src/derivation/upgrades.ts +14 -0
  67. package/src/derivation/wormhole.ts +141 -0
  68. package/src/entities/entity.ts +98 -0
  69. package/src/entities/gamestate.ts +3 -28
  70. package/src/entities/makers.ts +131 -134
  71. package/src/entities/slot-multiplier.ts +43 -0
  72. package/src/errors.ts +12 -16
  73. package/src/format.ts +26 -4
  74. package/src/index-module.ts +290 -34
  75. package/src/managers/actions.ts +602 -105
  76. package/src/managers/base.ts +6 -2
  77. package/src/managers/cluster.test.ts +39 -0
  78. package/src/managers/cluster.ts +53 -0
  79. package/src/managers/construction-types.ts +80 -0
  80. package/src/managers/construction.ts +412 -0
  81. package/src/managers/context.ts +29 -1
  82. package/src/managers/coordinates.ts +14 -0
  83. package/src/managers/entities.ts +27 -66
  84. package/src/managers/epochs.ts +40 -0
  85. package/src/managers/flatten-gather-plan.test.ts +80 -0
  86. package/src/managers/index.ts +20 -1
  87. package/src/managers/locations.ts +25 -29
  88. package/src/managers/nft.test.ts +14 -0
  89. package/src/managers/nft.ts +70 -0
  90. package/src/managers/players.ts +25 -0
  91. package/src/managers/plot.ts +122 -0
  92. package/src/nft/atomicassets.abi.json +1342 -0
  93. package/src/nft/atomicassets.ts +237 -0
  94. package/src/nft/atomicdata.ts +130 -0
  95. package/src/nft/buildImmutableData.ts +336 -0
  96. package/src/nft/description.ts +104 -36
  97. package/src/nft/index.ts +3 -0
  98. package/src/planner/index.ts +242 -0
  99. package/src/planner/planner.test.ts +325 -0
  100. package/src/resolution/describe-module.ts +43 -25
  101. package/src/resolution/display-name.ts +38 -10
  102. package/src/resolution/resolve-item.test.ts +48 -0
  103. package/src/resolution/resolve-item.ts +74 -47
  104. package/src/scan/index.ts +244 -0
  105. package/src/scan/scan-wasm.base64.ts +2 -0
  106. package/src/scheduling/accessor.ts +65 -23
  107. package/src/scheduling/availability.ts +108 -0
  108. package/src/scheduling/cancel.test.ts +348 -0
  109. package/src/scheduling/cancel.ts +209 -0
  110. package/src/scheduling/energy.ts +47 -0
  111. package/src/scheduling/idle-resolve.ts +45 -0
  112. package/src/scheduling/lane-core.ts +128 -0
  113. package/src/scheduling/lanes.test.ts +249 -0
  114. package/src/scheduling/lanes.ts +198 -0
  115. package/src/scheduling/projection.ts +210 -107
  116. package/src/scheduling/schedule.ts +240 -117
  117. package/src/scheduling/task-cargo.ts +46 -0
  118. package/src/scheduling/unwrap.test.ts +86 -0
  119. package/src/scheduling/unwrap.ts +190 -0
  120. package/src/shipload.ts +26 -1
  121. package/src/subscriptions/manager.cluster.test.ts +51 -0
  122. package/src/subscriptions/manager.ts +245 -172
  123. package/src/subscriptions/mappers.ts +5 -8
  124. package/src/subscriptions/types.ts +28 -3
  125. package/src/testing/catalog-hash.ts +19 -0
  126. package/src/testing/index.ts +2 -0
  127. package/src/testing/projection-parity.ts +167 -0
  128. package/src/travel/reach.ts +23 -0
  129. package/src/travel/route-planner.ts +277 -0
  130. package/src/travel/travel.ts +148 -117
  131. package/src/types/capabilities.ts +29 -6
  132. package/src/types/entity.ts +3 -3
  133. package/src/types/index.ts +0 -1
  134. package/src/types.ts +36 -14
  135. package/src/utils/cargo.test.ts +14 -0
  136. package/src/utils/cargo.ts +29 -0
  137. package/src/utils/display-name.ts +70 -0
  138. package/src/utils/system.ts +36 -24
  139. package/src/capabilities/loading.ts +0 -8
  140. package/src/entities/container.ts +0 -123
  141. package/src/entities/ship-deploy.ts +0 -295
  142. package/src/entities/ship.ts +0 -221
  143. package/src/entities/warehouse.ts +0 -127
  144. package/src/types/entity-traits.ts +0 -69
@@ -1,99 +1,60 @@
1
1
  import {Name, type NameType, type UInt64Type} from '@wharfkit/antelope'
2
2
  import {BaseManager} from './base'
3
- import {Ship} from '../entities/ship'
4
- import {Warehouse} from '../entities/warehouse'
5
- import {Container} from '../entities/container'
3
+ import {Entity} from '../entities/entity'
4
+ import type {EntityTypeName} from '../data/kind-registry'
6
5
  import type {ServerContract} from '../contracts'
7
6
 
8
- export type EntityType = 'ship' | 'warehouse' | 'container' | 'location'
7
+ export type {EntityTypeName} from '../data/kind-registry'
9
8
 
10
9
  export class EntitiesManager extends BaseManager {
11
- async getEntity(type: EntityType, id: UInt64Type): Promise<Ship | Warehouse | Container> {
10
+ async getEntity(id: UInt64Type): Promise<Entity> {
12
11
  const result = await this.server.readonly('getentity', {
13
- entity_type: Name.from(type),
14
12
  entity_id: id,
15
13
  })
16
- const entityInfo = result as ServerContract.Types.entity_info
17
- return this.wrapEntity(entityInfo)
14
+ return new Entity(result as ServerContract.Types.entity_info)
15
+ }
16
+
17
+ async getProjection(id: UInt64Type, taskCount?: number): Promise<unknown> {
18
+ return this.server.readonly('getprojstate', {
19
+ entity_id: id,
20
+ task_count: taskCount,
21
+ })
18
22
  }
19
23
 
20
24
  async getEntities(
21
25
  owner: NameType | ServerContract.Types.player_row,
22
- type?: EntityType
23
- ): Promise<(Ship | Warehouse | Container)[]> {
26
+ kind?: EntityTypeName
27
+ ): Promise<Entity[]> {
24
28
  const ownerName = this.resolveOwner(owner)
25
29
  const result = await this.server.readonly('getentities', {
26
30
  owner: ownerName,
27
- entity_type: type ? Name.from(type) : null,
31
+ entity_type: kind,
28
32
  })
29
33
  const entities = result as ServerContract.Types.entity_info[]
30
- return entities.map((entity) => this.wrapEntity(entity))
34
+ return entities.map((e) => new Entity(e))
35
+ }
36
+
37
+ async getAllEntities(kind?: EntityTypeName): Promise<ServerContract.Types.entity_row[]> {
38
+ const rows = await this.server.table('entity').all()
39
+ if (!kind) {
40
+ return rows
41
+ }
42
+ const wanted = Name.from(kind)
43
+ return rows.filter((row) => wanted.equals(row.kind))
31
44
  }
32
45
 
33
46
  async getSummaries(
34
47
  owner: NameType | ServerContract.Types.player_row,
35
- type?: EntityType
48
+ kind?: EntityTypeName
36
49
  ): Promise<ServerContract.Types.entity_summary[]> {
37
50
  const ownerName = this.resolveOwner(owner)
38
51
  const result = await this.server.readonly('getsummaries', {
39
52
  owner: ownerName,
40
- entity_type: type ? Name.from(type) : null,
53
+ entity_type: kind,
41
54
  })
42
55
  return result as ServerContract.Types.entity_summary[]
43
56
  }
44
57
 
45
- async getShip(id: UInt64Type): Promise<Ship> {
46
- return (await this.getEntity('ship', id)) as Ship
47
- }
48
-
49
- async getWarehouse(id: UInt64Type): Promise<Warehouse> {
50
- return (await this.getEntity('warehouse', id)) as Warehouse
51
- }
52
-
53
- async getContainer(id: UInt64Type): Promise<Container> {
54
- return (await this.getEntity('container', id)) as Container
55
- }
56
-
57
- async getShips(owner: NameType | ServerContract.Types.player_row): Promise<Ship[]> {
58
- return (await this.getEntities(owner, 'ship')) as Ship[]
59
- }
60
-
61
- async getWarehouses(owner: NameType | ServerContract.Types.player_row): Promise<Warehouse[]> {
62
- return (await this.getEntities(owner, 'warehouse')) as Warehouse[]
63
- }
64
-
65
- async getContainers(owner: NameType | ServerContract.Types.player_row): Promise<Container[]> {
66
- return (await this.getEntities(owner, 'container')) as Container[]
67
- }
68
-
69
- async getShipSummaries(
70
- owner: NameType | ServerContract.Types.player_row
71
- ): Promise<ServerContract.Types.entity_summary[]> {
72
- return this.getSummaries(owner, 'ship')
73
- }
74
-
75
- async getWarehouseSummaries(
76
- owner: NameType | ServerContract.Types.player_row
77
- ): Promise<ServerContract.Types.entity_summary[]> {
78
- return this.getSummaries(owner, 'warehouse')
79
- }
80
-
81
- async getContainerSummaries(
82
- owner: NameType | ServerContract.Types.player_row
83
- ): Promise<ServerContract.Types.entity_summary[]> {
84
- return this.getSummaries(owner, 'container')
85
- }
86
-
87
- private wrapEntity(entity: ServerContract.Types.entity_info): Ship | Warehouse | Container {
88
- if (entity.type.equals('ship')) {
89
- return new Ship(entity)
90
- } else if (entity.type.equals('warehouse')) {
91
- return new Warehouse(entity)
92
- } else {
93
- return new Container(entity)
94
- }
95
- }
96
-
97
58
  private resolveOwner(owner: NameType | ServerContract.Types.player_row): Name {
98
59
  if (typeof owner === 'object' && owner !== null && 'owner' in owner) {
99
60
  return owner.owner
@@ -1,6 +1,7 @@
1
1
  import {UInt64, type UInt64Type} from '@wharfkit/antelope'
2
2
  import {BaseManager} from './base'
3
3
  import {type EpochInfo, getCurrentEpoch, getEpochInfo} from '../scheduling/epoch'
4
+ import type {ServerContract} from '../contracts'
4
5
 
5
6
  export class EpochsManager extends BaseManager {
6
7
  async getCurrentHeight(): Promise<UInt64> {
@@ -8,6 +9,11 @@ export class EpochsManager extends BaseManager {
8
9
  return getCurrentEpoch(game)
9
10
  }
10
11
 
12
+ async getFinalizedEpoch(reload = false): Promise<UInt64> {
13
+ const state = await this.getState(reload)
14
+ return state.currentEpoch
15
+ }
16
+
11
17
  async getCurrent(): Promise<EpochInfo> {
12
18
  const game = await this.getGame()
13
19
  const epoch = await this.getCurrentHeight()
@@ -44,4 +50,38 @@ export class EpochsManager extends BaseManager {
44
50
  const remaining = await this.getTimeRemaining()
45
51
  return durationMs <= remaining
46
52
  }
53
+
54
+ async getEpochRow(epoch: UInt64Type): Promise<ServerContract.Types.epoch_row | undefined> {
55
+ const target = UInt64.from(epoch)
56
+ return this.server.table('epoch').get(target)
57
+ }
58
+
59
+ async getActiveEpochInfo(): Promise<ServerContract.Types.epoch_row | undefined> {
60
+ const rows = await this.server.table('epoch').all()
61
+ if (rows.length === 0) {
62
+ return undefined
63
+ }
64
+ return rows[rows.length - 1]
65
+ }
66
+
67
+ async getOracles(): Promise<ServerContract.Types.oracle_row[]> {
68
+ return this.server.table('oracles').all()
69
+ }
70
+
71
+ async getThreshold(): Promise<number> {
72
+ const cfg = await this.server.table('oraclecfg').get()
73
+ return cfg ? Number(cfg.threshold) : 0
74
+ }
75
+
76
+ async getCommitsFor(epoch: UInt64Type): Promise<ServerContract.Types.commit_row[]> {
77
+ const target = UInt64.from(epoch)
78
+ const rows = await this.server.table('commit').all()
79
+ return rows.filter((r) => r.epoch.equals(target))
80
+ }
81
+
82
+ async getRevealsFor(epoch: UInt64Type): Promise<ServerContract.Types.reveal_row[]> {
83
+ const target = UInt64.from(epoch)
84
+ const rows = await this.server.table('reveal').all()
85
+ return rows.filter((r) => r.epoch.equals(target))
86
+ }
47
87
  }
@@ -0,0 +1,80 @@
1
+ import {expect, test, describe} from 'bun:test'
2
+ import {makeClient} from '@wharfkit/mock-data'
3
+ import {Serializer} from '@wharfkit/antelope'
4
+ import type {GatherPlan} from '../planner'
5
+ import {ActionsManager} from './actions'
6
+ import {ServerContract} from '../contracts'
7
+
8
+ const client = makeClient('https://jungle4.greymass.com')
9
+
10
+ function makeActions() {
11
+ const realServer = new ServerContract.Contract({client})
12
+ const stubServer = {
13
+ account: realServer.account,
14
+ action: realServer.action.bind(realServer),
15
+ }
16
+ const context = {server: stubServer} as any
17
+ return new ActionsManager(context)
18
+ }
19
+
20
+ function fakePlan(): GatherPlan {
21
+ return {
22
+ cycles: [
23
+ {
24
+ rechargeBefore: false,
25
+ rechargeSeconds: 0,
26
+ gatherSeconds: 10,
27
+ batchOre: 30,
28
+ limpets: [
29
+ {slot: 0, quantity: 20, durationSeconds: 10},
30
+ {slot: 1, quantity: 10, durationSeconds: 8},
31
+ ],
32
+ },
33
+ {
34
+ rechargeBefore: true,
35
+ rechargeSeconds: 5,
36
+ gatherSeconds: 10,
37
+ batchOre: 30,
38
+ limpets: [{slot: 0, quantity: 30, durationSeconds: 10}],
39
+ },
40
+ ],
41
+ cycleCount: 2,
42
+ totalOre: 60,
43
+ totalSeconds: 25,
44
+ cap: 'requested',
45
+ reachingCount: 2,
46
+ totalLimpets: 2,
47
+ warnings: [],
48
+ }
49
+ }
50
+
51
+ describe('flattenGatherPlan', () => {
52
+ test('flattens cycles to [recharge?, gather x limpets] in order with slots', () => {
53
+ const actions = makeActions().flattenGatherPlan(fakePlan(), {
54
+ sourceId: 1,
55
+ destinationId: 1,
56
+ stratum: 100,
57
+ })
58
+ expect(actions.map((a) => a.name.toString())).toEqual([
59
+ 'gather',
60
+ 'gather',
61
+ 'recharge',
62
+ 'gather',
63
+ ])
64
+ expect(actions.filter((a) => a.name.toString() === 'recharge').length).toBe(1)
65
+ const gatherSlots = actions
66
+ .filter((a) => a.name.toString() === 'gather')
67
+ .map((a) => {
68
+ const decoded = Serializer.decode({data: a.data, type: ServerContract.Types.gather})
69
+ return Number(decoded.slot)
70
+ })
71
+ expect(gatherSlots).toEqual([0, 1, 0])
72
+ })
73
+
74
+ test('empty plan produces no actions', () => {
75
+ const empty = {...fakePlan(), cycles: [], cycleCount: 0, totalOre: 0}
76
+ expect(
77
+ makeActions().flattenGatherPlan(empty, {sourceId: 1, destinationId: 1, stratum: 100})
78
+ ).toHaveLength(0)
79
+ })
80
+ })
@@ -1,9 +1,28 @@
1
1
  export {GameContext} from './context'
2
2
  export {BaseManager} from './base'
3
3
  export {EntitiesManager} from './entities'
4
- export type {EntityType} from './entities'
4
+ export type {EntityTypeName} from './entities'
5
5
  export {PlayersManager} from './players'
6
+ export type {PlayerRosterEntry} from './players'
6
7
  export {LocationsManager} from './locations'
7
8
  export type {LocationStratum} from './locations'
8
9
  export {EpochsManager} from './epochs'
9
10
  export {ActionsManager} from './actions'
11
+ export {ClusterManager, computeFreeCells} from './cluster'
12
+ export type {GridCell, ClusterCell, Cluster} from './cluster'
13
+ export {NftManager} from './nft'
14
+ export type {NftConfigForItem} from './nft'
15
+ export {PlotManager} from './plot'
16
+ export type {PlotProgress, PlotProgressInputRow} from './plot'
17
+ export {ConstructionManager} from './construction'
18
+ export type {
19
+ BuildableTarget,
20
+ BuildState,
21
+ SourceEntityRef,
22
+ SourceCargoStack,
23
+ FinalizerEntityRef,
24
+ FinalizerCapability,
25
+ InboundTransfer,
26
+ ScheduledBuild,
27
+ Reservation,
28
+ } from './construction-types'
@@ -1,10 +1,10 @@
1
- import {type UInt16Type, UInt64, type UInt64Type} from '@wharfkit/antelope'
1
+ import {BlockTimestamp, type UInt16Type} from '@wharfkit/antelope'
2
2
  import {BaseManager} from './base'
3
- import {type CoordinatesType, coordsToLocationId, type Distance} from '../types'
3
+ import type {CoordinatesType, Distance} from '../types'
4
4
  import {hasSystem} from '../utils/system'
5
5
  import {findNearbyPlanets} from '../travel/travel'
6
6
  import type {ServerContract} from '../contracts'
7
- import {type DerivedStratum, deriveStrata} from '../derivation'
7
+ import {type DerivedStratum, deriveStrata, getEffectiveReserve} from '../derivation'
8
8
 
9
9
  export interface LocationStratum extends DerivedStratum {
10
10
  reserveMax: number
@@ -24,7 +24,10 @@ export class LocationsManager extends BaseManager {
24
24
  return findNearbyPlanets(game.config.seed, origin, maxDistance)
25
25
  }
26
26
 
27
- async getStrata(coords: CoordinatesType): Promise<LocationStratum[]> {
27
+ async getStrata(
28
+ coords: CoordinatesType,
29
+ now: BlockTimestamp = BlockTimestamp.fromMilliseconds(Date.now())
30
+ ): Promise<LocationStratum[]> {
28
31
  const game = await this.getGame()
29
32
  const state = await this.getState()
30
33
 
@@ -36,33 +39,26 @@ export class LocationsManager extends BaseManager {
36
39
  y: coords.y,
37
40
  })) as ServerContract.Types.stratum_remaining[]
38
41
 
39
- const overrideMap = new Map<number, number>()
42
+ const epochSeconds = Number(game.config.epochtime)
43
+ const overrideMap = new Map<number, ServerContract.Types.stratum_remaining>()
40
44
  for (const o of overrides) {
41
- overrideMap.set(Number(o.stratum), Number(o.remaining))
45
+ overrideMap.set(Number(o.stratum), o)
42
46
  }
43
47
 
44
- return derived.map((s) => ({
45
- ...s,
46
- reserveMax: s.reserve,
47
- reserve: overrideMap.get(s.index) ?? s.reserve,
48
- }))
49
- }
50
-
51
- async getLocationEntity(
52
- id: UInt64Type
53
- ): Promise<ServerContract.Types.location_row | undefined> {
54
- const row = await this.server.table('location').get(UInt64.from(id))
55
- return row ?? undefined
56
- }
57
-
58
- async getLocationEntityAt(
59
- coords: CoordinatesType
60
- ): Promise<ServerContract.Types.location_row | undefined> {
61
- const id = coordsToLocationId(coords)
62
- return this.getLocationEntity(id)
63
- }
64
-
65
- async getAllLocationEntities(): Promise<ServerContract.Types.location_row[]> {
66
- return this.server.table('location').all()
48
+ return derived.map((s) => {
49
+ const override = overrideMap.get(s.index)
50
+ const reserve = override
51
+ ? getEffectiveReserve(
52
+ {
53
+ remaining: override.remaining,
54
+ max_reserve: s.reserve,
55
+ last_block: override.last_block,
56
+ },
57
+ now,
58
+ epochSeconds
59
+ )
60
+ : s.reserve
61
+ return {...s, reserveMax: s.reserve, reserve}
62
+ })
67
63
  }
68
64
  }
@@ -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
+ })
@@ -0,0 +1,70 @@
1
+ import {UInt64, type UInt64Type} from '@wharfkit/antelope'
2
+ import {BaseManager} from './base'
3
+ import type {PlatformContract, ServerContract} from '../contracts'
4
+
5
+ export interface NftConfigForItem {
6
+ templateId: number
7
+ schemaName: string
8
+ }
9
+
10
+ export interface WrapDeposit {
11
+ cost: bigint
12
+ refund: bigint
13
+ feePct: number
14
+ symbol: string
15
+ precision: number
16
+ tokenContract: string
17
+ }
18
+
19
+ export function resolveLockedAmount(cost: bigint, feePctBasisPoints: number): bigint {
20
+ const fee = (cost * BigInt(feePctBasisPoints)) / 10_000n
21
+ return cost - fee
22
+ }
23
+
24
+ export class NftManager extends BaseManager {
25
+ private cache = new Map<string, NftConfigForItem | null>()
26
+
27
+ async getNftConfigForItem(itemId: UInt64Type): Promise<NftConfigForItem | undefined> {
28
+ const id = UInt64.from(itemId)
29
+ const key = id.toString()
30
+ if (this.cache.has(key)) {
31
+ return this.cache.get(key) ?? undefined
32
+ }
33
+ const row = (await this.server.table('nftconfig').get(id)) as
34
+ | ServerContract.Types.nftconfig_row
35
+ | undefined
36
+ const result: NftConfigForItem | null = row
37
+ ? {templateId: Number(row.template_id), schemaName: String(row.schema_name)}
38
+ : null
39
+ this.cache.set(key, result)
40
+ return result ?? undefined
41
+ }
42
+
43
+ async getWrapDeposit(itemType: number, tier: number): Promise<WrapDeposit | null> {
44
+ const key = UInt64.from((BigInt(itemType) << 8n) | BigInt(tier))
45
+ const costRow = (await this.server.table('wrapcost').get(key)) as
46
+ | ServerContract.Types.wrapcost_row
47
+ | undefined
48
+ const cost = costRow ? BigInt(costRow.amount.toString()) : 0n
49
+ if (cost === 0n) return null
50
+
51
+ const cfg = (await this.server.table('wrapconfig').get()) as
52
+ | ServerContract.Types.wrapconfig_row
53
+ | undefined
54
+ const feePctBasisPoints = cfg ? Number(cfg.fee_pct) : 0
55
+
56
+ const depositCfg = (await this.platform.table('depositcfg').get()) as
57
+ | PlatformContract.Types.depositcfg_row
58
+ | undefined
59
+ if (!depositCfg) return null
60
+
61
+ return {
62
+ cost,
63
+ refund: resolveLockedAmount(cost, feePctBasisPoints),
64
+ feePct: feePctBasisPoints / 100,
65
+ symbol: depositCfg.token_symbol.code.toString(),
66
+ precision: depositCfg.token_symbol.precision,
67
+ tokenContract: depositCfg.token_contract.toString(),
68
+ }
69
+ }
70
+ }
@@ -2,6 +2,11 @@ import {Name, type NameType} from '@wharfkit/antelope'
2
2
  import {BaseManager} from './base'
3
3
  import {Player} from '../entities/player'
4
4
 
5
+ export interface PlayerRosterEntry {
6
+ owner: Name
7
+ company?: string
8
+ }
9
+
5
10
  export class PlayersManager extends BaseManager {
6
11
  async getPlayer(account: NameType): Promise<Player | undefined> {
7
12
  const playerRow = await this.server.table('player').get(Name.from(account))
@@ -10,4 +15,24 @@ export class PlayersManager extends BaseManager {
10
15
  }
11
16
  return new Player(playerRow)
12
17
  }
18
+
19
+ async getPlayers(): Promise<Player[]> {
20
+ const rows = await this.server.table('player').all()
21
+ return rows.map((row) => new Player(row))
22
+ }
23
+
24
+ async getRoster(): Promise<PlayerRosterEntry[]> {
25
+ const [players, companies] = await Promise.all([
26
+ this.server.table('player').all(),
27
+ this.platform.table('company').all(),
28
+ ])
29
+ const companyNames = new Map<string, string>()
30
+ for (const company of companies) {
31
+ companyNames.set(company.account.toString(), company.name)
32
+ }
33
+ return players.map((player) => ({
34
+ owner: player.owner,
35
+ company: companyNames.get(player.owner.toString()),
36
+ }))
37
+ }
13
38
  }
@@ -0,0 +1,122 @@
1
+ import {Name} from '@wharfkit/antelope'
2
+ import {getItem} from '../data/catalog'
3
+ import {getRecipe} from '../data/recipes-runtime'
4
+ import {computeInputMass} from '../derivation/crafting'
5
+ import {calc_craft_duration} from '../capabilities/crafting'
6
+ import {TaskType} from '../types'
7
+ import {BaseManager} from './base'
8
+ import type {CrafterStats} from '../types/capabilities'
9
+ import type {ServerContract} from '../contracts'
10
+ import type {BuildableTarget, ScheduledBuild} from './construction-types'
11
+
12
+ export interface PlotProgressInputRow {
13
+ itemId: number
14
+ required: number
15
+ provided: number
16
+ missing: number
17
+ }
18
+
19
+ export interface PlotProgress {
20
+ targetItemId: number
21
+ rows: PlotProgressInputRow[]
22
+ massProvided: number
23
+ massRequired: number
24
+ isComplete: boolean
25
+ }
26
+
27
+ export class PlotManager extends BaseManager {
28
+ progress(
29
+ plot: ServerContract.Types.entity_row,
30
+ cargo: ServerContract.Types.cargo_row[]
31
+ ): PlotProgress {
32
+ const targetItemId = Number(plot.item_id.toString())
33
+ const recipe = getRecipe(targetItemId)
34
+ if (!recipe) {
35
+ throw new Error(`No recipe found for item ${targetItemId}`)
36
+ }
37
+
38
+ const plotId = plot.id.toString()
39
+ const plotCargo = cargo.filter((c) => c.entity_id.toString() === plotId)
40
+
41
+ const quantityByItemId = new Map<number, number>()
42
+ for (const c of plotCargo) {
43
+ const id = c.item_id.toNumber()
44
+ quantityByItemId.set(
45
+ id,
46
+ (quantityByItemId.get(id) ?? 0) + Number(c.quantity.toString())
47
+ )
48
+ }
49
+
50
+ const rows: PlotProgressInputRow[] = recipe.inputs.map((input) => {
51
+ const itemId = input.itemId
52
+ const required = input.quantity
53
+ const provided = quantityByItemId.get(itemId) ?? 0
54
+ const missing = Math.max(0, required - provided)
55
+ return {itemId, required, provided, missing}
56
+ })
57
+
58
+ const massRequired = computeInputMass(targetItemId)
59
+ const massProvided = rows.reduce((sum, r) => {
60
+ const item = getItem(r.itemId)
61
+ return sum + item.mass * r.provided
62
+ }, 0)
63
+ const isComplete = rows.every((r) => r.missing === 0)
64
+
65
+ return {targetItemId, rows, massProvided, massRequired, isComplete}
66
+ }
67
+
68
+ buildableTarget(
69
+ plot: ServerContract.Types.entity_row,
70
+ cargo: ServerContract.Types.cargo_row[],
71
+ activeTask?: ServerContract.Types.task,
72
+ scheduledBuild?: ScheduledBuild
73
+ ): BuildableTarget {
74
+ const progress = this.progress(plot, cargo)
75
+ const targetItemId = Number(plot.item_id.toString())
76
+ const targetItem = getItem(targetItemId)
77
+ const recipe = getRecipe(targetItemId)
78
+ if (!recipe) {
79
+ throw new Error(`Plot target item ${targetItemId} has no recipe`)
80
+ }
81
+
82
+ let state: BuildableTarget['state']
83
+ if (scheduledBuild?.hasStarted) {
84
+ state = 'finalizing'
85
+ } else if (scheduledBuild) {
86
+ state = 'scheduled'
87
+ } else if (progress.isComplete) {
88
+ state = 'ready'
89
+ } else {
90
+ state = 'accepting'
91
+ }
92
+
93
+ return {
94
+ entityId: plot.id,
95
+ ownerName: plot.owner,
96
+ coordinates: plot.coordinates,
97
+ targetItemId,
98
+ targetItem,
99
+ state,
100
+ recipe,
101
+ progress,
102
+ finalizeAction: Name.from('buildplot'),
103
+ finalizerCapability: 'crafter',
104
+ activeTask,
105
+ scheduledBuild,
106
+ }
107
+ }
108
+
109
+ canBuild(
110
+ plot: ServerContract.Types.entity_row,
111
+ cargo: ServerContract.Types.cargo_row[]
112
+ ): boolean {
113
+ return this.progress(plot, cargo).isComplete
114
+ }
115
+
116
+ timeToComplete(plot: ServerContract.Types.entity_info, crafter: CrafterStats): number {
117
+ const capacity = Number(plot.capacity?.toString() ?? '0')
118
+ const speed = Number(crafter.speed.toString())
119
+ if (speed === 0) return 0
120
+ return calc_craft_duration(speed, capacity).toNumber()
121
+ }
122
+ }