@shipload/sdk 1.0.0-next.18 → 1.0.0-next.19

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.
@@ -0,0 +1,47 @@
1
+ import type {Name, UInt32, UInt64} from '@wharfkit/antelope'
2
+ import type {ServerContract} from '../contracts'
3
+ import type {Item} from '../types'
4
+ import type {Recipe} from '../data/recipes-runtime'
5
+ import type {PlotProgress} from './plot'
6
+
7
+ export type BuildState = 'initializing' | 'accepting' | 'ready' | 'finalizing'
8
+
9
+ export type FinalizerCapability = 'crafter'
10
+
11
+ export interface BuildableTarget {
12
+ entityId: UInt64
13
+ ownerName: Name
14
+ coordinates: ServerContract.Types.coordinates
15
+ targetItemId: number
16
+ targetItem: Item
17
+ state: BuildState
18
+ recipe: Recipe
19
+ progress: PlotProgress
20
+ finalizeAction: Name
21
+ finalizerCapability: FinalizerCapability
22
+ activeTask?: ServerContract.Types.task
23
+ }
24
+
25
+ export interface SourceEntityRef {
26
+ entityId: UInt64
27
+ name: string
28
+ hasLoaders: boolean
29
+ loaderCount: number
30
+ loaderTotalMass: number
31
+ relevantCargo: SourceCargoStack[]
32
+ }
33
+
34
+ export interface SourceCargoStack {
35
+ itemId: number
36
+ item: Item
37
+ available: number
38
+ plotNeeds: number
39
+ }
40
+
41
+ export interface FinalizerEntityRef {
42
+ entityId: UInt64
43
+ name: string
44
+ capability: FinalizerCapability
45
+ crafterSpeed: number
46
+ estimatedDuration: UInt32
47
+ }
@@ -0,0 +1,147 @@
1
+ import type {UInt32} from '@wharfkit/antelope'
2
+ import {BaseManager} from './base'
3
+ import type {ServerContract} from '../contracts'
4
+ import {PlotManager} from './plot'
5
+ import {getItem} from '../data/catalog'
6
+ import {calc_craft_duration} from '../capabilities/crafting'
7
+ import type {
8
+ BuildableTarget,
9
+ FinalizerEntityRef,
10
+ SourceCargoStack,
11
+ SourceEntityRef,
12
+ } from './construction-types'
13
+
14
+ const CONSTRUCTION_KINDS = new Set<string>(['plot'])
15
+
16
+ export class ConstructionManager extends BaseManager {
17
+ private readonly plot = new PlotManager(this.context)
18
+
19
+ getTarget(
20
+ entity: ServerContract.Types.entity_row,
21
+ cargo: ServerContract.Types.cargo_row[],
22
+ activeTask?: ServerContract.Types.task
23
+ ): BuildableTarget | null {
24
+ const kind = entity.kind.toString()
25
+ if (kind === 'plot') {
26
+ return this.plot.buildableTarget(entity, cargo, activeTask)
27
+ }
28
+ return null
29
+ }
30
+
31
+ eligibleSources(
32
+ target: BuildableTarget,
33
+ entities: ServerContract.Types.entity_row[],
34
+ cargo: ServerContract.Types.cargo_row[]
35
+ ): SourceEntityRef[] {
36
+ return partitionSources(target, entities, cargo).eligible
37
+ }
38
+
39
+ unreachableSources(
40
+ target: BuildableTarget,
41
+ entities: ServerContract.Types.entity_row[],
42
+ cargo: ServerContract.Types.cargo_row[]
43
+ ): SourceEntityRef[] {
44
+ return partitionSources(target, entities, cargo).unreachable
45
+ }
46
+
47
+ partitionSources(
48
+ target: BuildableTarget,
49
+ entities: ServerContract.Types.entity_row[],
50
+ cargo: ServerContract.Types.cargo_row[]
51
+ ): {eligible: SourceEntityRef[]; unreachable: SourceEntityRef[]} {
52
+ return partitionSources(target, entities, cargo)
53
+ }
54
+
55
+ eligibleFinalizers(
56
+ target: BuildableTarget,
57
+ entities: ServerContract.Types.entity_row[]
58
+ ): FinalizerEntityRef[] {
59
+ const out: FinalizerEntityRef[] = []
60
+ for (const entity of entities) {
61
+ if (!entity.owner.equals(target.ownerName)) continue
62
+ if (entity.id.equals(target.entityId)) continue
63
+ if (!coordsEqual(entity.coordinates, target.coordinates)) continue
64
+ const speed = entity.crafter?.speed.toNumber()
65
+ if (speed === undefined) continue
66
+ out.push({
67
+ entityId: entity.id,
68
+ name: entity.id.toString(),
69
+ capability: 'crafter',
70
+ crafterSpeed: speed,
71
+ estimatedDuration: this.estimateFinalizeDuration(target, speed),
72
+ })
73
+ }
74
+ return out.sort((a, b) => a.estimatedDuration.value - b.estimatedDuration.value)
75
+ }
76
+
77
+ estimateFinalizeDuration(target: BuildableTarget, crafterSpeed: number): UInt32 {
78
+ return calc_craft_duration(crafterSpeed, target.progress.massRequired)
79
+ }
80
+
81
+ static isConstructionKind(kind: string): boolean {
82
+ return CONSTRUCTION_KINDS.has(kind)
83
+ }
84
+ }
85
+
86
+ function coordsEqual(
87
+ a: ServerContract.Types.coordinates,
88
+ b: ServerContract.Types.coordinates
89
+ ): boolean {
90
+ return a.x.equals(b.x) && a.y.equals(b.y)
91
+ }
92
+
93
+ function matchRelevantCargo(
94
+ entity: ServerContract.Types.entity_row,
95
+ target: BuildableTarget,
96
+ cargo: ServerContract.Types.cargo_row[]
97
+ ): SourceCargoStack[] {
98
+ const quantityByItemId = new Map<number, number>()
99
+ for (const c of cargo) {
100
+ if (!c.entity_id.equals(entity.id)) continue
101
+ const id = c.item_id.toNumber()
102
+ quantityByItemId.set(id, (quantityByItemId.get(id) ?? 0) + c.quantity.toNumber())
103
+ }
104
+
105
+ const out: SourceCargoStack[] = []
106
+ for (const row of target.progress.rows) {
107
+ if (row.missing === 0) continue
108
+ const available = quantityByItemId.get(row.itemId) ?? 0
109
+ if (available === 0) continue
110
+ out.push({
111
+ itemId: row.itemId,
112
+ item: getItem(row.itemId),
113
+ available,
114
+ plotNeeds: row.missing,
115
+ })
116
+ }
117
+ return out
118
+ }
119
+
120
+ function partitionSources(
121
+ target: BuildableTarget,
122
+ entities: ServerContract.Types.entity_row[],
123
+ cargo: ServerContract.Types.cargo_row[]
124
+ ): {eligible: SourceEntityRef[]; unreachable: SourceEntityRef[]} {
125
+ const eligible: SourceEntityRef[] = []
126
+ const unreachable: SourceEntityRef[] = []
127
+ for (const entity of entities) {
128
+ if (!entity.owner.equals(target.ownerName)) continue
129
+ if (entity.id.equals(target.entityId)) continue
130
+ if (!coordsEqual(entity.coordinates, target.coordinates)) continue
131
+ const relevant = matchRelevantCargo(entity, target, cargo)
132
+ if (relevant.length === 0) continue
133
+ const loaderCount = entity.loaders?.quantity.toNumber() ?? 0
134
+ const loaderTotalMass = entity.loaders?.mass.toNumber() ?? 0
135
+ const ref: SourceEntityRef = {
136
+ entityId: entity.id,
137
+ name: entity.id.toString(),
138
+ hasLoaders: loaderCount > 0,
139
+ loaderCount,
140
+ loaderTotalMass,
141
+ relevantCargo: relevant,
142
+ }
143
+ if (ref.hasLoaders) eligible.push(ref)
144
+ else unreachable.push(ref)
145
+ }
146
+ return {eligible, unreachable}
147
+ }
@@ -9,3 +9,14 @@ export {EpochsManager} from './epochs'
9
9
  export {ActionsManager} from './actions'
10
10
  export {NftManager} from './nft'
11
11
  export type {NftConfigForItem} from './nft'
12
+ export {PlotManager} from './plot'
13
+ export type {PlotProgress, PlotProgressInputRow} from './plot'
14
+ export {ConstructionManager} from './construction'
15
+ export type {
16
+ BuildableTarget,
17
+ BuildState,
18
+ SourceEntityRef,
19
+ SourceCargoStack,
20
+ FinalizerEntityRef,
21
+ FinalizerCapability,
22
+ } from './construction-types'
@@ -0,0 +1,123 @@
1
+ import {Name} from '@wharfkit/antelope'
2
+ import {getItem} from '../data/catalog'
3
+ import {getRecipe, resolveRecipeInputItemId} 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 {ServerContract} from '../contracts'
9
+ import type {BuildableTarget} from './construction-types'
10
+
11
+ export interface PlotProgressInputRow {
12
+ itemId: number
13
+ required: number
14
+ provided: number
15
+ missing: number
16
+ }
17
+
18
+ export interface PlotProgress {
19
+ targetItemId: number
20
+ rows: PlotProgressInputRow[]
21
+ massProvided: number
22
+ massRequired: number
23
+ isComplete: boolean
24
+ }
25
+
26
+ export class PlotManager extends BaseManager {
27
+ progress(
28
+ plot: ServerContract.Types.entity_row,
29
+ cargo: ServerContract.Types.cargo_row[]
30
+ ): PlotProgress {
31
+ const targetItemId = Number(plot.item_id.toString())
32
+ const recipe = getRecipe(targetItemId)
33
+ if (!recipe) {
34
+ throw new Error(`No recipe found for item ${targetItemId}`)
35
+ }
36
+
37
+ const plotId = plot.id.toString()
38
+ const plotCargo = cargo.filter((c) => c.entity_id.toString() === plotId)
39
+
40
+ const quantityByItemId = new Map<number, number>()
41
+ for (const c of plotCargo) {
42
+ const id = c.item_id.toNumber()
43
+ quantityByItemId.set(
44
+ id,
45
+ (quantityByItemId.get(id) ?? 0) + Number(c.quantity.toString())
46
+ )
47
+ }
48
+
49
+ const rows: PlotProgressInputRow[] = recipe.inputs.map((input) => {
50
+ const itemId = resolveRecipeInputItemId(input)
51
+ const required = input.quantity
52
+ const provided = quantityByItemId.get(itemId) ?? 0
53
+ const missing = Math.max(0, required - provided)
54
+ return {itemId, required, provided, missing}
55
+ })
56
+
57
+ const massRequired = computeInputMass(targetItemId)
58
+ const massProvided = rows.reduce((sum, r) => {
59
+ const item = getItem(r.itemId)
60
+ return sum + item.mass * r.provided
61
+ }, 0)
62
+ const isComplete = rows.every((r) => r.missing === 0)
63
+
64
+ return {targetItemId, rows, massProvided, massRequired, isComplete}
65
+ }
66
+
67
+ buildableTarget(
68
+ plot: ServerContract.Types.entity_row,
69
+ cargo: ServerContract.Types.cargo_row[],
70
+ activeTask?: ServerContract.Types.task
71
+ ): BuildableTarget {
72
+ const progress = this.progress(plot, cargo)
73
+ const targetItemId = Number(plot.item_id.toString())
74
+ const targetItem = getItem(targetItemId)
75
+ const recipe = getRecipe(targetItemId)
76
+ if (!recipe) {
77
+ throw new Error(`Plot target item ${targetItemId} has no recipe`)
78
+ }
79
+
80
+ let state: BuildableTarget['state']
81
+ const taskType = activeTask?.type.toNumber()
82
+ if (taskType === TaskType.CLAIMPLOT) {
83
+ state = 'initializing'
84
+ } else if (taskType === TaskType.BUILDPLOT) {
85
+ state = 'finalizing'
86
+ } else if (progress.isComplete) {
87
+ state = 'ready'
88
+ } else {
89
+ state = 'accepting'
90
+ }
91
+
92
+ return {
93
+ entityId: plot.id,
94
+ ownerName: plot.owner,
95
+ coordinates: plot.coordinates,
96
+ targetItemId,
97
+ targetItem,
98
+ state,
99
+ recipe,
100
+ progress,
101
+ finalizeAction: Name.from('buildplot'),
102
+ finalizerCapability: 'crafter',
103
+ activeTask,
104
+ }
105
+ }
106
+
107
+ canBuild(
108
+ plot: ServerContract.Types.entity_row,
109
+ cargo: ServerContract.Types.cargo_row[]
110
+ ): boolean {
111
+ return this.progress(plot, cargo).isComplete
112
+ }
113
+
114
+ timeToComplete(
115
+ plot: ServerContract.Types.entity_row,
116
+ crafter: ServerContract.Types.crafter_stats
117
+ ): number {
118
+ const capacity = Number(plot.capacity?.toString() ?? '0')
119
+ const speed = Number(crafter.speed.toString())
120
+ if (speed === 0) return 0
121
+ return calc_craft_duration(speed, capacity).toNumber()
122
+ }
123
+ }
@@ -193,10 +193,12 @@ export function buildModuleImmutable(
193
193
  case MODULE_GATHERER: {
194
194
  const str = decodeStat(stats, 0)
195
195
  const tol = decodeStat(stats, 1)
196
- const con = decodeStat(stats, 3)
196
+ const con = decodeStat(stats, 2)
197
+ const ref = decodeStat(stats, 3)
197
198
  base.push({first: 'strength', second: ['uint16', str]})
198
199
  base.push({first: 'tolerance', second: ['uint16', tol]})
199
200
  base.push({first: 'conductivity', second: ['uint16', con]})
201
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
200
202
  base.push({first: 'yield', second: ['uint16', computeGathererYield(str)]})
201
203
  base.push({first: 'drain', second: ['uint16', computeGathererDrain(con)]})
202
204
  base.push({first: 'depth', second: ['uint16', computeGathererDepth(tol, item.tier)]})
@@ -241,17 +243,15 @@ export function buildModuleImmutable(
241
243
  break
242
244
  }
243
245
  case MODULE_HAULER: {
244
- const com = decodeStat(stats, 0)
246
+ const res = decodeStat(stats, 0)
245
247
  const con = decodeStat(stats, 1)
246
- const fin = decodeStat(stats, 2)
247
- const res = decodeStat(stats, 3)
248
- base.push({first: 'composition', second: ['uint16', com]})
249
- base.push({first: 'conductivity', second: ['uint16', con]})
250
- base.push({first: 'fineness', second: ['uint16', fin]})
248
+ const ref = decodeStat(stats, 2)
251
249
  base.push({first: 'resonance', second: ['uint16', res]})
252
- base.push({first: 'capacity', second: ['uint8', computeHaulerCapacity(com)]})
250
+ base.push({first: 'conductivity', second: ['uint16', con]})
251
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
252
+ base.push({first: 'capacity', second: ['uint8', computeHaulerCapacity(res)]})
253
253
  base.push({first: 'efficiency', second: ['uint16', computeHaulerEfficiency(con)]})
254
- base.push({first: 'drain', second: ['uint16', computeHaulerDrain(fin)]})
254
+ base.push({first: 'drain', second: ['uint16', computeHaulerDrain(ref)]})
255
255
  break
256
256
  }
257
257
  }
@@ -39,12 +39,12 @@ export function computeBaseHullmass(stats: bigint): number {
39
39
 
40
40
  export function computeBaseCapacityShip(stats: bigint): number {
41
41
  const s = decodeStat(stats, 0) + decodeStat(stats, 2) + decodeStat(stats, 3)
42
- return Math.floor(1_000_000 * 10 ** (s / 2997))
42
+ return Math.floor(5_000_000 * 10 ** (s / 2997))
43
43
  }
44
44
 
45
45
  export function computeBaseCapacityWarehouse(stats: bigint): number {
46
46
  const s = decodeStat(stats, 0) + decodeStat(stats, 2) + decodeStat(stats, 3)
47
- return Math.floor(20_000_000 * 10 ** (s / 2997))
47
+ return Math.floor(100_000_000 * 10 ** (s / 2997))
48
48
  }
49
49
 
50
50
  export const computeEngineThrust = (vol: number): number => 400 + idiv(vol * 3, 4)
@@ -131,7 +131,7 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
131
131
  case MODULE_GATHERER: {
132
132
  const str = decodeStat(stats, 0)
133
133
  const tol = decodeStat(stats, 1)
134
- const con = decodeStat(stats, 3)
134
+ const con = decodeStat(stats, 2)
135
135
  const tier = getItem(itemId).tier
136
136
  out += ` Yield ${computeGathererYield(str)} Depth ${computeGathererDepth(
137
137
  tol,
@@ -161,10 +161,10 @@ export function formatModuleLine(slot: number, itemId: number, stats: bigint): s
161
161
  break
162
162
  }
163
163
  case MODULE_HAULER: {
164
- const fin = decodeStat(stats, 0)
164
+ const res = decodeStat(stats, 0)
165
165
  const con = decodeStat(stats, 1)
166
- const com = decodeStat(stats, 2)
167
- out += ` Capacity ${computeHaulerCapacity(fin)} Efficiency ${computeHaulerEfficiency(con)} Drain ${computeHaulerDrain(com)}`
166
+ const ref = decodeStat(stats, 2)
167
+ out += ` Capacity ${computeHaulerCapacity(res)} Efficiency ${computeHaulerEfficiency(con)} Drain ${computeHaulerDrain(ref)}`
168
168
  break
169
169
  }
170
170
  case MODULE_WARP: {
@@ -63,14 +63,12 @@ const TEMPLATES: Record<string, TemplateSpec> = {
63
63
  },
64
64
  loader: {
65
65
  id: 'module.loader.description',
66
- template:
67
- '{quantity} loader that generates {thrust} thrust with a weight of {mass} per unit',
66
+ template: 'generates {thrust} thrust with a weight of {mass} per unit',
68
67
  params: [
69
- ['quantity', 'Quantity'],
70
68
  ['thrust', 'Thrust'],
71
69
  ['mass', 'Mass'],
72
70
  ],
73
- highlightKeys: ['quantity', 'thrust', 'mass'],
71
+ highlightKeys: ['thrust', 'mass'],
74
72
  },
75
73
  crafter: {
76
74
  id: 'module.crafter.description',
@@ -84,6 +84,7 @@ export type WireEntity = Record<string, unknown> & {
84
84
  id: string | number
85
85
  owner: string
86
86
  coordinates: WireCoordinates
87
+ item_id: number
87
88
  }
88
89
 
89
90
  export type SnapshotMessage = {
package/src/types.ts CHANGED
@@ -53,6 +53,8 @@ export enum TaskType {
53
53
  UNWRAP = 10,
54
54
  UNDEPLOY = 11,
55
55
  DEMOLISH = 13,
56
+ CLAIMPLOT = 14,
57
+ BUILDPLOT = 15,
56
58
  }
57
59
 
58
60
  export enum LocationType {