@shipload/sdk 1.0.0-next.41 → 1.0.0-next.43

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipload/sdk",
3
3
  "description": "SDKs for Shipload",
4
- "version": "1.0.0-next.41",
4
+ "version": "1.0.0-next.43",
5
5
  "homepage": "https://github.com/shipload/toolkit/tree/master/packages/sdk",
6
6
  "repository": {
7
7
  "type": "git",
@@ -8,6 +8,14 @@ export interface StatMapping {
8
8
  stat: string
9
9
  capability: string
10
10
  attribute: string
11
+ source: string // producing module/role, always present
12
+ }
13
+
14
+ export interface CapabilityAttributeRow {
15
+ capability: string
16
+ attribute: string
17
+ description: string
18
+ source?: string // producing module/role; absent when no formula-derived producer exists
11
19
  }
12
20
 
13
21
  export const capabilityNames: string[] = [
@@ -376,7 +376,7 @@
376
376
  "quantity": 300
377
377
  },
378
378
  {
379
- "itemId": 10006,
379
+ "itemId": 10010,
380
380
  "quantity": 300
381
381
  }
382
382
  ],
@@ -875,147 +875,5 @@
875
875
  }
876
876
  ],
877
877
  "blendWeights": []
878
- },
879
- {
880
- "outputItemId": 20001,
881
- "outputMass": 500,
882
- "inputs": [
883
- {
884
- "itemId": 10001,
885
- "quantity": 200
886
- },
887
- {
888
- "itemId": 102,
889
- "quantity": 15
890
- }
891
- ],
892
- "statSlots": [
893
- {
894
- "sources": [
895
- {
896
- "inputIndex": 0,
897
- "statIndex": 0
898
- },
899
- {
900
- "inputIndex": 1,
901
- "statIndex": 0
902
- }
903
- ]
904
- },
905
- {
906
- "sources": [
907
- {
908
- "inputIndex": 0,
909
- "statIndex": 1
910
- },
911
- {
912
- "inputIndex": 1,
913
- "statIndex": 2
914
- }
915
- ]
916
- }
917
- ],
918
- "blendWeights": [
919
- 1,
920
- 1
921
- ]
922
- },
923
- {
924
- "outputItemId": 20002,
925
- "outputMass": 300,
926
- "inputs": [
927
- {
928
- "itemId": 10002,
929
- "quantity": 200
930
- },
931
- {
932
- "itemId": 402,
933
- "quantity": 10
934
- },
935
- {
936
- "itemId": 502,
937
- "quantity": 20
938
- }
939
- ],
940
- "statSlots": [
941
- {
942
- "sources": [
943
- {
944
- "inputIndex": 0,
945
- "statIndex": 0
946
- },
947
- {
948
- "inputIndex": 1,
949
- "statIndex": 1
950
- }
951
- ]
952
- },
953
- {
954
- "sources": [
955
- {
956
- "inputIndex": 0,
957
- "statIndex": 1
958
- },
959
- {
960
- "inputIndex": 2,
961
- "statIndex": 2
962
- }
963
- ]
964
- }
965
- ],
966
- "blendWeights": [
967
- 1,
968
- 1,
969
- 1
970
- ]
971
- },
972
- {
973
- "outputItemId": 20200,
974
- "outputMass": 80000,
975
- "inputs": [
976
- {
977
- "itemId": 20001,
978
- "quantity": 600
979
- },
980
- {
981
- "itemId": 20002,
982
- "quantity": 200
983
- }
984
- ],
985
- "statSlots": [
986
- {
987
- "sources": [
988
- {
989
- "inputIndex": 0,
990
- "statIndex": 0
991
- }
992
- ]
993
- },
994
- {
995
- "sources": [
996
- {
997
- "inputIndex": 0,
998
- "statIndex": 1
999
- }
1000
- ]
1001
- },
1002
- {
1003
- "sources": [
1004
- {
1005
- "inputIndex": 1,
1006
- "statIndex": 0
1007
- }
1008
- ]
1009
- },
1010
- {
1011
- "sources": [
1012
- {
1013
- "inputIndex": 1,
1014
- "statIndex": 1
1015
- }
1016
- ]
1017
- }
1018
- ],
1019
- "blendWeights": []
1020
878
  }
1021
879
  ]
@@ -21,8 +21,8 @@ import {
21
21
  import type {InstalledModule} from '../entities/slot-multiplier'
22
22
  import type {EntitySlot} from '../data/recipes-runtime'
23
23
 
24
- function makeGathererStats(strength: number, tolerance: number, conductivity: number): bigint {
25
- return encodeStats([strength, tolerance, conductivity, 0])
24
+ function makeGathererStats(strength: number, tolerance: number, saturation: number): bigint {
25
+ return encodeStats([strength, tolerance, saturation, 0])
26
26
  }
27
27
 
28
28
  function makeCrafterStats(reactivity: number, conductivity: number): bigint {
@@ -72,8 +72,8 @@ test('computeEntityCapabilities emits gathererLanes alongside legacy gatherer su
72
72
  expect(result.gathererLanes![1].slotIndex).toBe(1)
73
73
 
74
74
  // Yields are amp-scaled and distinct
75
- const caps1 = computeGathererCapabilities({strength: 300, tolerance: 200, conductivity: 400}, 1)
76
- const caps2 = computeGathererCapabilities({strength: 500, tolerance: 100, conductivity: 300}, 1)
75
+ const caps1 = computeGathererCapabilities({strength: 300, tolerance: 200, saturation: 400}, 1)
76
+ const caps2 = computeGathererCapabilities({strength: 500, tolerance: 100, saturation: 300}, 1)
77
77
  const expectedYield1 = applySlotMultiplier(caps1.yield, 100)
78
78
  const expectedYield2 = applySlotMultiplier(caps2.yield, 100)
79
79
  expect(result.gathererLanes![0].yield).toBe(expectedYield1)
@@ -77,7 +77,7 @@ export function computeGathererCapabilities(
77
77
  depth: number
78
78
  } {
79
79
  const str = stats.strength
80
- const con = stats.conductivity
80
+ const con = stats.saturation
81
81
  const tol = stats.tolerance
82
82
 
83
83
  return {
@@ -18,7 +18,8 @@ import {
18
18
  ITEM_WAREHOUSE_T1_PACKED,
19
19
  ITEM_CONTAINER_T2_PACKED,
20
20
  } from '../data/item-ids'
21
- import type {StatMapping} from '../data/capabilities'
21
+ import type {StatMapping, CapabilityAttributeRow} from '../data/capabilities'
22
+ import {capabilityAttributes} from '../data/capabilities'
22
23
 
23
24
  export const KIND_TO_ITEM_ID: Record<SlotConsumerKind, number> = {
24
25
  engine: ITEM_ENGINE_T1,
@@ -69,6 +70,12 @@ function traceToRawCategoryStat(
69
70
  return traceToRawCategoryStat(subRecipe, subSource, nextVisited)
70
71
  }
71
72
 
73
+ // Producing role for a capability·attribute: entity hull slots all roll up to "Hull"; modules use their own name.
74
+ export function sourceLabelForOutput(itemId: number): string {
75
+ const item = getItem(itemId)
76
+ return item.type === 'entity' ? 'Hull' : item.name
77
+ }
78
+
72
79
  let cached: StatMapping[] | undefined
73
80
 
74
81
  export function deriveStatMappings(): StatMapping[] {
@@ -82,20 +89,22 @@ export function deriveStatMappings(): StatMapping[] {
82
89
  const itemId = KIND_TO_ITEM_ID[kind]
83
90
  const recipe = getRecipe(itemId)
84
91
  if (!recipe) continue
92
+ const source = sourceLabelForOutput(itemId)
85
93
  for (const [slotIdxStr, consumer] of Object.entries(slots)) {
86
94
  const slotIdx = Number(slotIdxStr)
87
95
  const slot = recipe.statSlots[slotIdx]
88
96
  if (!slot) continue
89
- for (const source of slot.sources) {
90
- const stat = traceToRawCategoryStat(recipe, source)
97
+ for (const src of slot.sources) {
98
+ const stat = traceToRawCategoryStat(recipe, src)
91
99
  if (!stat) continue
92
- const key = `${stat.label}|${consumer.capability}|${consumer.attribute}`
100
+ const key = `${stat.label}|${consumer.capability}|${consumer.attribute}|${source}`
93
101
  if (seen.has(key)) continue
94
102
  seen.add(key)
95
103
  out.push({
96
104
  stat: stat.label,
97
105
  capability: consumer.capability,
98
106
  attribute: consumer.attribute,
107
+ source,
99
108
  })
100
109
  }
101
110
  }
@@ -115,3 +124,39 @@ export function getStatMappingsForStat(stat: string): StatMapping[] {
115
124
  export function getStatMappingsForCapability(capability: string): StatMapping[] {
116
125
  return deriveStatMappings().filter((m) => m.capability === capability)
117
126
  }
127
+
128
+ export function getProducersForAttribute(capability: string, attribute: string): string[] {
129
+ const seen = new Set<string>()
130
+ const out: string[] = []
131
+ for (const m of deriveStatMappings()) {
132
+ if (m.capability !== capability || m.attribute !== attribute) continue
133
+ if (seen.has(m.source)) continue
134
+ seen.add(m.source)
135
+ out.push(m.source)
136
+ }
137
+ return out
138
+ }
139
+
140
+ export function getCapabilityAttributeRows(): CapabilityAttributeRow[] {
141
+ const rows: CapabilityAttributeRow[] = []
142
+ for (const ca of capabilityAttributes) {
143
+ const producers = getProducersForAttribute(ca.capability, ca.attribute)
144
+ if (producers.length === 0) {
145
+ rows.push({
146
+ capability: ca.capability,
147
+ attribute: ca.attribute,
148
+ description: ca.description,
149
+ })
150
+ continue
151
+ }
152
+ for (const source of producers) {
153
+ rows.push({
154
+ capability: ca.capability,
155
+ attribute: ca.attribute,
156
+ description: ca.description,
157
+ source,
158
+ })
159
+ }
160
+ }
161
+ return rows
162
+ }
@@ -26,14 +26,12 @@ test('getRecipeConsumers lists every recipe that consumes Sensor', () => {
26
26
  const consumers = getRecipeConsumers(ITEM_SENSOR)
27
27
  const ids = consumers.map((c) => c.outputItemId).sort((a, b) => a - b)
28
28
  expect(ids).toEqual(
29
- [ITEM_GATHERER_T1, ITEM_CRAFTER_T1, ITEM_SHIP_T1_PACKED, ITEM_EXTRACTOR_T1_PACKED].sort(
30
- (a, b) => a - b
31
- )
29
+ [ITEM_CRAFTER_T1, ITEM_SHIP_T1_PACKED, ITEM_EXTRACTOR_T1_PACKED].sort((a, b) => a - b)
32
30
  )
33
31
  })
34
32
 
35
- test('Sensor feeds the gatherer drain stat', () => {
36
- const consumers = getRecipeConsumers(ITEM_SENSOR)
33
+ test('Resin feeds the gatherer drain stat', () => {
34
+ const consumers = getRecipeConsumers(ITEM_RESIN)
37
35
  const gatherer = consumers.find((c) => c.outputItemId === ITEM_GATHERER_T1)
38
36
  expect(gatherer).toBeDefined()
39
37
  const drain = gatherer?.statFlows.find(
@@ -58,11 +56,12 @@ test('getResourceDemand traces a dual-resource component to both resources', ()
58
56
  })
59
57
 
60
58
  test('getResourceDemand recurses through a module to raw resources', () => {
61
- // Gatherer = 300 Beam (5 ore + 5 gas each) + 300 Sensor (10 crystal each)
59
+ // Gatherer = 300 Beam (5 ore + 5 gas each) + 300 Resin (5 biomass + 5 crystal each)
62
60
  expect(getResourceDemand(ITEM_GATHERER_T1)).toEqual({
63
61
  ore: 1500,
64
62
  gas: 1500,
65
- crystal: 3000,
63
+ biomass: 1500,
64
+ crystal: 1500,
66
65
  })
67
66
  })
68
67
 
@@ -70,9 +69,9 @@ test('getResourceDemand scales by quantity', () => {
70
69
  expect(getResourceDemand(ITEM_PLATE, 3)).toEqual({ore: 30})
71
70
  })
72
71
 
73
- test('getComponentDemand reports Resin as consumed by exactly one recipe', () => {
72
+ test('getComponentDemand reports Resin as consumed by two recipes', () => {
74
73
  const demand = getComponentDemand()
75
74
  const resin = demand.find((d) => d.itemId === ITEM_RESIN)
76
75
  expect(resin).toBeDefined()
77
- expect(resin?.consumerCount).toBe(1)
76
+ expect(resin?.consumerCount).toBe(2)
78
77
  })
@@ -1,5 +1,6 @@
1
1
  import type {Checksum256Type} from '@wharfkit/antelope'
2
2
  import {hash512} from '../utils/hash'
3
+ import {COORD_MAX, COORD_MIN} from '../coordinates/constants'
3
4
 
4
5
  export const WH = {
5
6
  RSIZE: 75,
@@ -72,6 +73,9 @@ function endpointInRegion(seed: Checksum256Type, R: Region, key: string): {x: nu
72
73
  function dist(a: {x: number; y: number}, b: {x: number; y: number}): number {
73
74
  return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2)
74
75
  }
76
+ function inBounds(c: {x: number; y: number}): boolean {
77
+ return c.x >= COORD_MIN && c.x <= COORD_MAX && c.y >= COORD_MIN && c.y <= COORD_MAX
78
+ }
75
79
  function wormholeOfRegion(
76
80
  seed: Checksum256Type,
77
81
  R: Region
@@ -82,6 +86,7 @@ function wormholeOfRegion(
82
86
  if (roll16(seed, `wh-exists-${key}`) >= WH.THRESHOLD) return null
83
87
  const A = endpointInRegion(seed, R, key)
84
88
  const B = endpointInRegion(seed, P, key)
89
+ if (!inBounds(A) || !inBounds(B)) return null
85
90
  if (dist(A, B) < WH.MIN_REACH) return null
86
91
  return {A, B}
87
92
  }
@@ -50,6 +50,7 @@ export {
50
50
  ConstructionManager,
51
51
  } from './managers'
52
52
  export type {
53
+ PlayerRosterEntry,
53
54
  LocationStratum,
54
55
  NftConfigForItem,
55
56
  BuildableTarget,
@@ -206,11 +207,12 @@ export type {
206
207
  HasScheduleAndLocation,
207
208
  } from './travel/travel'
208
209
 
209
- export {planRoute, sdkSystemGraph, MAX_LEGS} from './travel/route-planner'
210
+ export {planRoute, sdkSystemGraph, setScanProvider, MAX_LEGS} from './travel/route-planner'
210
211
  export type {
211
212
  Coord,
212
213
  Neighbor,
213
214
  SystemGraph,
215
+ ScanProvider,
214
216
  RoutePlan,
215
217
  RouteFailure,
216
218
  RouteResult,
@@ -281,6 +283,17 @@ export type {
281
283
  CancelEligibilityInput,
282
284
  } from './scheduling/cancel'
283
285
 
286
+ export {
287
+ derivedLoaders,
288
+ estimateUnwrapDuration,
289
+ incomingHoldMass,
290
+ projectedPeakCargomass,
291
+ receiveFits,
292
+ unwrapLoadDuration,
293
+ unwrapTransitDuration,
294
+ } from './scheduling/unwrap'
295
+ export type {DerivedLoaders, UnwrapDestination, UnwrapItem} from './scheduling/unwrap'
296
+
284
297
  export {
285
298
  projectedCargoAvailableAt,
286
299
  availableForItem,
@@ -346,13 +359,16 @@ export {
346
359
  isInvertedAttribute,
347
360
  getCapabilityAttributes,
348
361
  } from './data/capabilities'
349
- export type {CapabilityAttribute, StatMapping} from './data/capabilities'
362
+ export type {CapabilityAttribute, StatMapping, CapabilityAttributeRow} from './data/capabilities'
350
363
 
351
364
  export {
352
365
  deriveStatMappings,
353
366
  getStatMappings,
354
367
  getStatMappingsForStat,
355
368
  getStatMappingsForCapability,
369
+ getProducersForAttribute,
370
+ getCapabilityAttributeRows,
371
+ sourceLabelForOutput,
356
372
  } from './derivation/capability-mappings'
357
373
  export {SLOT_FORMULAS} from './data/capability-formulas'
358
374
  export type {SlotConsumer, SlotConsumerKind} from './data/capability-formulas'
@@ -420,6 +436,7 @@ export {
420
436
  feistelInv,
421
437
  regionOf,
422
438
  partnerRegion,
439
+ nearbyWormholes,
423
440
  wormholeAt,
424
441
  wormholeAtRegionEndpoint,
425
442
  isValidWormholePair,
@@ -593,6 +593,23 @@ export class ActionsManager extends BaseManager {
593
593
  )
594
594
  }
595
595
 
596
+ sendAsset(owner: NameType, recipient: NameType, assetId: UInt64Type, memo = ''): Action {
597
+ return Action.from(
598
+ {
599
+ account: this.atomicAssetsAccount,
600
+ name: 'transfer',
601
+ authorization: [{actor: Name.from(owner), permission: 'active'}],
602
+ data: {
603
+ from: Name.from(owner),
604
+ to: Name.from(recipient),
605
+ asset_ids: [UInt64.from(assetId)],
606
+ memo,
607
+ },
608
+ },
609
+ ATOMICASSETS_ABI
610
+ )
611
+ }
612
+
596
613
  // Two top-level actions the wallet signs to unwrap an NFT into a host's cargo.
597
614
  unwrapCargoTx(owner: NameType, assetId: UInt64Type, hostId: UInt64Type): Action[] {
598
615
  return [this.transferForUnwrap(owner, assetId), this.placecargo(owner, hostId, assetId)]
@@ -34,6 +34,15 @@ export class EntitiesManager extends BaseManager {
34
34
  return entities.map((e) => new Entity(e))
35
35
  }
36
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))
44
+ }
45
+
37
46
  async getSummaries(
38
47
  owner: NameType | ServerContract.Types.player_row,
39
48
  kind?: EntityTypeName
@@ -3,6 +3,7 @@ export {BaseManager} from './base'
3
3
  export {EntitiesManager} from './entities'
4
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'
@@ -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
  }
@@ -203,8 +203,8 @@ export function buildModuleImmutable(
203
203
  const ref = decodeStat(stats, 3)
204
204
  base.push({first: 'strength', second: ['uint16', str]})
205
205
  base.push({first: 'tolerance', second: ['uint16', tol]})
206
- base.push({first: 'conductivity', second: ['uint16', con]})
207
- base.push({first: 'reflectivity', second: ['uint16', ref]})
206
+ base.push({first: 'saturation', second: ['uint16', con]})
207
+ base.push({first: 'plasticity', second: ['uint16', ref]})
208
208
  base.push({first: 'yield', second: ['uint16', computeGathererYield(str)]})
209
209
  base.push({first: 'drain', second: ['uint16', computeGathererDrain(con)]})
210
210
  base.push({first: 'depth', second: ['uint16', computeGathererDepth(tol, item.tier)]})