@shipload/sdk 1.0.0-next.11 → 1.0.0-next.12

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.
@@ -1,147 +1,51 @@
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'
6
- import {Extractor} from '../entities/extractor'
7
- import {Factory} from '../entities/factory'
8
- import {Nexus} from '../entities/nexus'
3
+ import {Entity} from '../entities/entity'
4
+ import type {EntityTypeName} from '../data/kind-registry'
9
5
  import type {ServerContract} from '../contracts'
10
6
 
11
- export type EntityType = 'ship' | 'warehouse' | 'extractor' | 'factory' | 'container' | 'nexus'
7
+ export type {EntityTypeName} from '../data/kind-registry'
12
8
 
13
9
  export class EntitiesManager extends BaseManager {
14
- async getEntity(
15
- id: UInt64Type
16
- ): Promise<Ship | Warehouse | Extractor | Factory | Container | Nexus> {
10
+ async getEntity(id: UInt64Type): Promise<Entity> {
17
11
  const result = await this.server.readonly('getentity', {
18
12
  entity_id: id,
19
13
  })
20
- const entityInfo = result as ServerContract.Types.entity_info
21
- 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
+ })
22
22
  }
23
23
 
24
24
  async getEntities(
25
25
  owner: NameType | ServerContract.Types.player_row,
26
- type?: EntityType
27
- ): Promise<(Ship | Warehouse | Extractor | Factory | Container | Nexus)[]> {
26
+ kind?: EntityTypeName
27
+ ): Promise<Entity[]> {
28
28
  const ownerName = this.resolveOwner(owner)
29
29
  const result = await this.server.readonly('getentities', {
30
30
  owner: ownerName,
31
- entity_type: type,
31
+ entity_type: kind,
32
32
  })
33
33
  const entities = result as ServerContract.Types.entity_info[]
34
- return entities.map((entity) => this.wrapEntity(entity))
34
+ return entities.map((e) => new Entity(e))
35
35
  }
36
36
 
37
37
  async getSummaries(
38
38
  owner: NameType | ServerContract.Types.player_row,
39
- type?: EntityType
39
+ kind?: EntityTypeName
40
40
  ): Promise<ServerContract.Types.entity_summary[]> {
41
41
  const ownerName = this.resolveOwner(owner)
42
42
  const result = await this.server.readonly('getsummaries', {
43
43
  owner: ownerName,
44
- entity_type: type,
44
+ entity_type: kind,
45
45
  })
46
46
  return result as ServerContract.Types.entity_summary[]
47
47
  }
48
48
 
49
- async getShip(id: UInt64Type): Promise<Ship> {
50
- return (await this.getEntity(id)) as Ship
51
- }
52
-
53
- async getWarehouse(id: UInt64Type): Promise<Warehouse> {
54
- return (await this.getEntity(id)) as Warehouse
55
- }
56
-
57
- async getContainer(id: UInt64Type): Promise<Container> {
58
- return (await this.getEntity(id)) as Container
59
- }
60
-
61
- async getExtractor(id: UInt64Type): Promise<Extractor> {
62
- return (await this.getEntity(id)) as Extractor
63
- }
64
-
65
- async getFactory(id: UInt64Type): Promise<Factory> {
66
- return (await this.getEntity(id)) as Factory
67
- }
68
-
69
- async getShips(owner: NameType | ServerContract.Types.player_row): Promise<Ship[]> {
70
- return (await this.getEntities(owner, 'ship')) as Ship[]
71
- }
72
-
73
- async getWarehouses(owner: NameType | ServerContract.Types.player_row): Promise<Warehouse[]> {
74
- return (await this.getEntities(owner, 'warehouse')) as Warehouse[]
75
- }
76
-
77
- async getContainers(owner: NameType | ServerContract.Types.player_row): Promise<Container[]> {
78
- return (await this.getEntities(owner, 'container')) as Container[]
79
- }
80
-
81
- async getExtractors(owner: NameType | ServerContract.Types.player_row): Promise<Extractor[]> {
82
- return (await this.getEntities(owner, 'extractor')) as Extractor[]
83
- }
84
-
85
- async getFactories(owner: NameType | ServerContract.Types.player_row): Promise<Factory[]> {
86
- return (await this.getEntities(owner, 'factory')) as Factory[]
87
- }
88
-
89
- async getShipSummaries(
90
- owner: NameType | ServerContract.Types.player_row
91
- ): Promise<ServerContract.Types.entity_summary[]> {
92
- return this.getSummaries(owner, 'ship')
93
- }
94
-
95
- async getWarehouseSummaries(
96
- owner: NameType | ServerContract.Types.player_row
97
- ): Promise<ServerContract.Types.entity_summary[]> {
98
- return this.getSummaries(owner, 'warehouse')
99
- }
100
-
101
- async getContainerSummaries(
102
- owner: NameType | ServerContract.Types.player_row
103
- ): Promise<ServerContract.Types.entity_summary[]> {
104
- return this.getSummaries(owner, 'container')
105
- }
106
-
107
- async getExtractorSummaries(
108
- owner: NameType | ServerContract.Types.player_row
109
- ): Promise<ServerContract.Types.entity_summary[]> {
110
- return this.getSummaries(owner, 'extractor')
111
- }
112
-
113
- async getFactorySummaries(
114
- owner: NameType | ServerContract.Types.player_row
115
- ): Promise<ServerContract.Types.entity_summary[]> {
116
- return this.getSummaries(owner, 'factory')
117
- }
118
-
119
- async getNexus(id: UInt64Type): Promise<Nexus> {
120
- return (await this.getEntity(id)) as Nexus
121
- }
122
-
123
- async getNexuses(owner: NameType | ServerContract.Types.player_row): Promise<Nexus[]> {
124
- return (await this.getEntities(owner, 'nexus')) as Nexus[]
125
- }
126
-
127
- async getNexusSummaries(
128
- owner: NameType | ServerContract.Types.player_row
129
- ): Promise<ServerContract.Types.entity_summary[]> {
130
- return this.getSummaries(owner, 'nexus')
131
- }
132
-
133
- private wrapEntity(
134
- entity: ServerContract.Types.entity_info
135
- ): Ship | Warehouse | Extractor | Factory | Container | Nexus {
136
- if (entity.type.equals('ship')) return new Ship(entity)
137
- if (entity.type.equals('warehouse')) return new Warehouse(entity)
138
- if (entity.type.equals('extractor')) return new Extractor(entity)
139
- if (entity.type.equals('factory')) return new Factory(entity)
140
- if (entity.type.equals('container')) return new Container(entity)
141
- if (entity.type.equals('nexus')) return new Nexus(entity)
142
- throw new Error(`unknown entity type: ${entity.type}`)
143
- }
144
-
145
49
  private resolveOwner(owner: NameType | ServerContract.Types.player_row): Name {
146
50
  if (typeof owner === 'object' && owner !== null && 'owner' in owner) {
147
51
  return owner.owner
@@ -1,7 +1,7 @@
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
6
  export {LocationsManager} from './locations'
7
7
  export type {LocationStratum} from './locations'
@@ -103,6 +103,8 @@ export function deserializeAtomicData(
103
103
  result[field.name] = readZigzagInt64()
104
104
  break
105
105
  case 'string':
106
+ case 'image':
107
+ case 'ipfs':
106
108
  result[field.name] = readString()
107
109
  break
108
110
  case 'uint16[]': {
@@ -25,7 +25,7 @@ import {
25
25
  ITEM_WARP_T1,
26
26
  } from '../data/item-ids'
27
27
  import {decodeStat} from '../derivation/crafting'
28
- import {gathererDepthForTier} from '../entities/ship-deploy'
28
+ import {gathererDepthForTier} from '../derivation/capabilities'
29
29
  import {getItem} from '../data/catalog'
30
30
 
31
31
  function idiv(a: number, b: number): number {
@@ -26,8 +26,9 @@ import {
26
26
  computeLoaderCapabilities,
27
27
  computeShipHullCapabilities,
28
28
  computeWarehouseHullCapabilities,
29
- } from '../entities/ship-deploy'
30
- import {computeContainerCapabilities, computeContainerT2Capabilities} from '../entities/container'
29
+ computeContainerCapabilities,
30
+ computeContainerT2Capabilities,
31
+ } from '../derivation/capabilities'
31
32
  import {
32
33
  categoryColors,
33
34
  categoryIcons,
@@ -13,13 +13,10 @@ import type {
13
13
  WireEntity,
14
14
  } from './types'
15
15
  import {mapEntity, parseWireEntity} from './mappers'
16
- import type {Ship} from '../entities/ship'
17
- import type {Warehouse} from '../entities/warehouse'
18
- import type {Container} from '../entities/container'
19
- import type {Nexus} from '../entities/nexus'
16
+ import type {Entity} from '../entities/entity'
20
17
 
21
18
  export type SubscriptionEntityType = 'ship' | 'warehouse' | 'container' | 'nexus'
22
- export type EntityInstance = Ship | Warehouse | Container | Nexus
19
+ export type EntityInstance = Entity
23
20
 
24
21
  export interface SubscriptionsOptions {
25
22
  url: string
@@ -1,18 +1,9 @@
1
1
  import {ServerContract} from '../contracts'
2
- import {Ship} from '../entities/ship'
3
- import {Warehouse} from '../entities/warehouse'
4
- import {Container} from '../entities/container'
5
- import {Nexus} from '../entities/nexus'
2
+ import {Entity} from '../entities/entity'
6
3
  import type {WireEntity} from './types'
7
4
 
8
- export function mapEntity(
9
- ei: ServerContract.Types.entity_info
10
- ): Ship | Warehouse | Container | Nexus {
11
- if (ei.type.equals('ship')) return new Ship(ei)
12
- if (ei.type.equals('warehouse')) return new Warehouse(ei)
13
- if (ei.type.equals('container')) return new Container(ei)
14
- if (ei.type.equals('nexus')) return new Nexus(ei)
15
- throw new Error(`mapEntity: unknown entity type ${ei.type.toString()}`)
5
+ export function mapEntity(ei: ServerContract.Types.entity_info): Entity {
6
+ return new Entity(ei)
16
7
  }
17
8
 
18
9
  export function parseWireEntity(raw: WireEntity): ServerContract.Types.entity_info {
@@ -0,0 +1,19 @@
1
+ import {createHash} from 'node:crypto'
2
+ import {readFileSync} from 'node:fs'
3
+
4
+ export const CATALOG_FILES_REL = [
5
+ 'items.json',
6
+ 'recipes.json',
7
+ 'entities.json',
8
+ 'kind-registry.json',
9
+ 'item-ids.ts',
10
+ ] as const
11
+
12
+ export function computeCatalogHash(filePaths: ReadonlyArray<string>): string {
13
+ const hash = createHash('sha256')
14
+ for (const p of filePaths) {
15
+ hash.update(readFileSync(p))
16
+ hash.update('\0')
17
+ }
18
+ return hash.digest('hex')
19
+ }
@@ -0,0 +1,2 @@
1
+ export * from './catalog-hash'
2
+ export * from './projection-parity'
@@ -0,0 +1,143 @@
1
+ import type {UInt16, UInt32} from '@wharfkit/antelope'
2
+ import type {ServerContract} from '../contracts'
3
+ import type {ProjectedEntity} from '../scheduling/projection'
4
+ import {type CargoStack, cargoItemToStack, mergeStacks} from '../capabilities/storage'
5
+
6
+ export interface ContractProjectedState {
7
+ owner: {toString(): string}
8
+ coordinates: ServerContract.Types.coordinates
9
+ energy?: UInt16
10
+ cargomass: UInt32
11
+ cargo: ServerContract.Types.cargo_view[]
12
+ hullmass?: UInt32
13
+ capacity?: UInt32
14
+ engines?: ServerContract.Types.movement_stats
15
+ loaders?: ServerContract.Types.loader_stats
16
+ generator?: ServerContract.Types.energy_stats
17
+ hauler?: ServerContract.Types.hauler_stats
18
+ }
19
+
20
+ export interface ProjectionComparisonOptions {
21
+ step?: number
22
+ }
23
+
24
+ export function assertProjectionEquals(
25
+ contract: ContractProjectedState,
26
+ sdk: ProjectedEntity,
27
+ options: ProjectionComparisonOptions = {}
28
+ ): void {
29
+ const mismatches: string[] = []
30
+
31
+ const record = (name: string, c: unknown, s: unknown) => {
32
+ if (c !== s) mismatches.push(` ${name}: contract=${fmt(c)} sdk=${fmt(s)}`)
33
+ }
34
+
35
+ const recordStatBlock = (name: string, c: unknown, s: unknown) => {
36
+ const cPresent = c !== undefined && c !== null
37
+ const sPresent = s !== undefined && s !== null
38
+ if (cPresent !== sPresent) {
39
+ mismatches.push(
40
+ ` ${name}: contract=${cPresent ? 'present' : 'absent'} sdk=${sPresent ? 'present' : 'absent'}`
41
+ )
42
+ return
43
+ }
44
+ if (!cPresent) return
45
+ const cn = JSON.stringify(normaliseStatBlock(c))
46
+ const sn = JSON.stringify(normaliseStatBlock(s))
47
+ if (cn !== sn) mismatches.push(` ${name}: contract=${cn} sdk=${sn}`)
48
+ }
49
+
50
+ record('coordinates.x', toNum(contract.coordinates.x), Number(sdk.location.x))
51
+ record('coordinates.y', toNum(contract.coordinates.y), Number(sdk.location.y))
52
+ record('energy', toNum(contract.energy), Number(sdk.energy))
53
+ record('cargomass', toNum(contract.cargomass), Number(sdk.cargoMass))
54
+ record('hullmass', toNum(contract.hullmass), Number(sdk.shipMass))
55
+ record('capacity', toNum(contract.capacity), sdk.capacity ? Number(sdk.capacity) : undefined)
56
+
57
+ recordStatBlock('engines', contract.engines, sdk.engines)
58
+ recordStatBlock('loaders', contract.loaders, sdk.loaders)
59
+ recordStatBlock('generator', contract.generator, sdk.generator)
60
+ recordStatBlock('hauler', contract.hauler, sdk.hauler)
61
+
62
+ if (contract.cargo.length > 0 || sdk.cargo.length > 0) {
63
+ const contractCargo = normaliseCargo(mergeContractCargo(contract.cargo))
64
+ const sdkCargo = normaliseCargo(sdk.cargo)
65
+ if (contractCargo.length !== sdkCargo.length) {
66
+ mismatches.push(
67
+ ` cargo.length: contract=${contractCargo.length} sdk=${sdkCargo.length}`
68
+ )
69
+ } else {
70
+ for (let i = 0; i < contractCargo.length; i++) {
71
+ const c = contractCargo[i]
72
+ const s = sdkCargo[i]
73
+ if (c.itemId !== s.itemId || c.stats !== s.stats || c.quantity !== s.quantity) {
74
+ mismatches.push(
75
+ ` cargo[${i}]: contract={item:${c.itemId},stats:${c.stats},qty:${c.quantity}} sdk={item:${s.itemId},stats:${s.stats},qty:${s.quantity}}`
76
+ )
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ if (mismatches.length > 0) {
83
+ const header =
84
+ options.step !== undefined
85
+ ? `projection divergence at step ${options.step}:`
86
+ : 'projection divergence:'
87
+ throw new Error([header, ...mismatches].join('\n'))
88
+ }
89
+ }
90
+
91
+ interface NormalisedStack {
92
+ itemId: number
93
+ stats: string
94
+ quantity: string
95
+ }
96
+
97
+ function mergeContractCargo(cargo: ServerContract.Types.cargo_view[]): CargoStack[] {
98
+ return cargo.reduce<CargoStack[]>(
99
+ (acc, row) =>
100
+ mergeStacks(acc, cargoItemToStack(row as unknown as ServerContract.Types.cargo_item)),
101
+ []
102
+ )
103
+ }
104
+
105
+ function normaliseCargo(cargo: CargoStack[]): NormalisedStack[] {
106
+ return cargo
107
+ .map((s) => ({
108
+ itemId: Number(s.item_id),
109
+ stats: BigInt(s.stats.toString()).toString(),
110
+ quantity: BigInt(s.quantity.toString()).toString(),
111
+ }))
112
+ .sort(stackSort)
113
+ }
114
+
115
+ function stackSort(a: NormalisedStack, b: NormalisedStack): number {
116
+ if (a.itemId !== b.itemId) return a.itemId - b.itemId
117
+ return a.stats < b.stats ? -1 : a.stats > b.stats ? 1 : 0
118
+ }
119
+
120
+ function toNum(v: unknown): number | undefined {
121
+ if (v === undefined || v === null) return undefined
122
+ if (typeof v === 'number') return v
123
+ if (typeof v === 'bigint') return Number(v)
124
+ if (typeof (v as {toNumber?: unknown}).toNumber === 'function') {
125
+ return (v as {toNumber(): number}).toNumber()
126
+ }
127
+ return Number(v as number)
128
+ }
129
+
130
+ function fmt(v: unknown): string {
131
+ if (v === undefined) return 'undefined'
132
+ if (v === null) return 'null'
133
+ return String(v)
134
+ }
135
+
136
+ function normaliseStatBlock(block: unknown): Record<string, number> {
137
+ const out: Record<string, number> = {}
138
+ const obj = block as Record<string, unknown>
139
+ for (const k of Object.keys(obj).sort()) {
140
+ out[k] = toNum(obj[k]) ?? 0
141
+ }
142
+ return out
143
+ }
@@ -1,3 +1,2 @@
1
1
  export * from './capabilities'
2
2
  export * from './entity'
3
- export * from './entity-traits'
package/src/types.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  type Int64Type,
3
- Name,
4
3
  type UInt16,
5
4
  type UInt16Type,
6
5
  type UInt32,
@@ -72,14 +71,6 @@ export enum TaskCancelable {
72
71
  ALWAYS = 2,
73
72
  }
74
73
 
75
- export const EntityType = {
76
- SHIP: Name.from('ship'),
77
- WAREHOUSE: Name.from('warehouse'),
78
- CONTAINER: Name.from('container'),
79
- } as const
80
-
81
- export type EntityTypeName = (typeof EntityType)[keyof typeof EntityType]
82
-
83
74
  export type CoordinatesType =
84
75
  | Coordinates
85
76
  | ServerContract.Types.coordinates
@@ -1,123 +0,0 @@
1
- import {UInt64, type UInt64Type} from '@wharfkit/antelope'
2
- import {ServerContract} from '../contracts'
3
- import type {CoordinatesType} from '../types'
4
- import {type FloatPosition, getInterpolatedPosition} from '../travel/travel'
5
- import {Location} from './location'
6
- import {ScheduleAccessor} from '../scheduling/accessor'
7
- import * as schedule from '../scheduling/schedule'
8
-
9
- export interface ContainerStateInput {
10
- id: UInt64Type
11
- owner: string
12
- name: string
13
- coordinates: CoordinatesType | {x: number; y: number; z?: number}
14
- hullmass: number
15
- capacity: number
16
- cargomass?: number
17
- cargo?: ServerContract.Types.cargo_item[]
18
- schedule?: ServerContract.Types.schedule
19
- }
20
-
21
- export class Container extends ServerContract.Types.entity_info {
22
- private _sched?: ScheduleAccessor
23
-
24
- get name(): string {
25
- return this.entity_name
26
- }
27
-
28
- get entityClass(): 'orbital' {
29
- return 'orbital'
30
- }
31
-
32
- get canUndeploy(): boolean {
33
- return true
34
- }
35
-
36
- get sched(): ScheduleAccessor {
37
- this._sched ??= new ScheduleAccessor(this)
38
- return this._sched
39
- }
40
-
41
- get isIdle(): boolean {
42
- return this.is_idle
43
- }
44
-
45
- interpolatedPositionAt(now: Date): FloatPosition {
46
- const taskIndex = this.sched.currentTaskIndex(now)
47
- const progress = this.sched.currentTaskProgressFloat(now)
48
- return getInterpolatedPosition(this, taskIndex, progress)
49
- }
50
-
51
- isLoading(now: Date): boolean {
52
- return schedule.isLoading(this, now)
53
- }
54
-
55
- isUnloading(now: Date): boolean {
56
- return schedule.isUnloading(this, now)
57
- }
58
-
59
- get location(): Location {
60
- return Location.from(this.coordinates)
61
- }
62
-
63
- get totalMass(): UInt64 {
64
- return UInt64.from(this.hullmass ?? 0).adding(this.cargomass)
65
- }
66
-
67
- get maxCapacity(): UInt64 {
68
- return UInt64.from(this.capacity)
69
- }
70
-
71
- get availableCapacity(): UInt64 {
72
- const cargo = UInt64.from(this.cargomass)
73
- return cargo.gte(this.maxCapacity) ? UInt64.from(0) : this.maxCapacity.subtracting(cargo)
74
- }
75
-
76
- hasSpace(additionalMass: UInt64): boolean {
77
- return UInt64.from(this.cargomass).adding(additionalMass).lte(this.maxCapacity)
78
- }
79
-
80
- get isFull(): boolean {
81
- return UInt64.from(this.cargomass).gte(this.maxCapacity)
82
- }
83
-
84
- get orbitalAltitude(): number {
85
- return this.coordinates.z?.toNumber() || 0
86
- }
87
- }
88
-
89
- export function computeContainerCapabilities(stats: Record<string, number>): {
90
- hullmass: number
91
- capacity: number
92
- } {
93
- const density = stats.density
94
- const strength = stats.strength
95
- const hardness = stats.hardness
96
- const saturation = stats.saturation
97
-
98
- const hullmass = 100000 - 75 * density
99
-
100
- const statSum = strength + hardness + saturation
101
- const exponent = statSum / 2997
102
- const capacity = Math.floor(1000000 * 10 ** exponent)
103
-
104
- return {hullmass, capacity}
105
- }
106
-
107
- export function computeContainerT2Capabilities(stats: Record<string, number>): {
108
- hullmass: number
109
- capacity: number
110
- } {
111
- const strength = stats.strength
112
- const density = stats.density
113
- const hardness = stats.hardness
114
- const saturation = stats.saturation
115
-
116
- const hullmass = 70000 - 50 * density
117
-
118
- const statSum = strength + hardness + saturation
119
- const exponent = statSum / 2500
120
- const capacity = Math.floor(1500000 * 10 ** exponent)
121
-
122
- return {hullmass, capacity}
123
- }