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

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 (134) hide show
  1. package/lib/scan.d.ts +34 -0
  2. package/lib/scan.js +136 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +129 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +2473 -973
  7. package/lib/shipload.js +11529 -5211
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +11338 -5162
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +970 -0
  12. package/lib/testing.js +4013 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4007 -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 +5 -6
  20. package/src/capabilities/gathering.test.ts +16 -0
  21. package/src/capabilities/gathering.ts +35 -18
  22. package/src/capabilities/index.ts +0 -1
  23. package/src/capabilities/modules.ts +9 -0
  24. package/src/capabilities/storage.ts +16 -1
  25. package/src/contracts/platform.ts +231 -3
  26. package/src/contracts/server.ts +1021 -481
  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 +12 -5
  37. package/src/data/capability-formulas.ts +14 -7
  38. package/src/data/catalog.ts +0 -5
  39. package/src/data/colors.ts +14 -47
  40. package/src/data/entities.json +76 -10
  41. package/src/data/item-ids.ts +18 -12
  42. package/src/data/items.json +321 -38
  43. package/src/data/kind-registry.json +109 -0
  44. package/src/data/kind-registry.ts +165 -0
  45. package/src/data/metadata.ts +119 -33
  46. package/src/data/recipes-runtime.ts +3 -23
  47. package/src/data/recipes.json +238 -117
  48. package/src/derivation/build-methods.ts +45 -0
  49. package/src/derivation/capabilities.test.ts +151 -0
  50. package/src/derivation/capabilities.ts +512 -0
  51. package/src/derivation/capability-mappings.ts +9 -12
  52. package/src/derivation/crafting.ts +23 -24
  53. package/src/derivation/index.ts +25 -2
  54. package/src/derivation/recipe-usage.test.ts +78 -0
  55. package/src/derivation/recipe-usage.ts +141 -0
  56. package/src/derivation/reserve-regen.ts +34 -0
  57. package/src/derivation/resources.ts +125 -38
  58. package/src/derivation/rollups.test.ts +55 -0
  59. package/src/derivation/rollups.ts +56 -0
  60. package/src/derivation/stars.test.ts +51 -0
  61. package/src/derivation/stars.ts +15 -0
  62. package/src/derivation/stats.ts +6 -6
  63. package/src/derivation/stratum.ts +17 -20
  64. package/src/derivation/tiers.ts +40 -7
  65. package/src/derivation/wormhole.ts +136 -0
  66. package/src/entities/entity.ts +98 -0
  67. package/src/entities/gamestate.ts +3 -28
  68. package/src/entities/makers.ts +124 -134
  69. package/src/entities/slot-multiplier.ts +43 -0
  70. package/src/errors.ts +12 -16
  71. package/src/format.ts +26 -4
  72. package/src/index-module.ts +267 -47
  73. package/src/managers/actions.ts +528 -95
  74. package/src/managers/base.ts +6 -2
  75. package/src/managers/construction-types.ts +80 -0
  76. package/src/managers/construction.ts +412 -0
  77. package/src/managers/context.ts +20 -1
  78. package/src/managers/coordinates.ts +14 -0
  79. package/src/managers/entities.ts +18 -66
  80. package/src/managers/epochs.ts +40 -0
  81. package/src/managers/index.ts +17 -1
  82. package/src/managers/locations.ts +25 -29
  83. package/src/managers/nft.test.ts +14 -0
  84. package/src/managers/nft.ts +70 -0
  85. package/src/managers/plot.ts +122 -0
  86. package/src/nft/atomicassets.abi.json +1342 -0
  87. package/src/nft/atomicassets.ts +237 -0
  88. package/src/nft/atomicdata.ts +130 -0
  89. package/src/nft/buildImmutableData.ts +338 -0
  90. package/src/nft/description.ts +98 -24
  91. package/src/nft/index.ts +3 -0
  92. package/src/planner/index.ts +127 -0
  93. package/src/planner/planner.test.ts +319 -0
  94. package/src/resolution/describe-module.ts +18 -13
  95. package/src/resolution/display-name.ts +38 -10
  96. package/src/resolution/resolve-item.test.ts +37 -0
  97. package/src/resolution/resolve-item.ts +55 -24
  98. package/src/scan/index.ts +180 -0
  99. package/src/scan/scan-wasm.base64.ts +2 -0
  100. package/src/scheduling/accessor.ts +68 -22
  101. package/src/scheduling/availability.ts +108 -0
  102. package/src/scheduling/cancel.test.ts +348 -0
  103. package/src/scheduling/cancel.ts +209 -0
  104. package/src/scheduling/energy.ts +47 -0
  105. package/src/scheduling/idle-resolve.ts +45 -0
  106. package/src/scheduling/lane-core.ts +128 -0
  107. package/src/scheduling/lanes.test.ts +249 -0
  108. package/src/scheduling/lanes.ts +198 -0
  109. package/src/scheduling/projection.ts +209 -105
  110. package/src/scheduling/schedule.ts +241 -104
  111. package/src/scheduling/task-cargo.ts +46 -0
  112. package/src/shipload.ts +21 -1
  113. package/src/subscriptions/manager.ts +229 -142
  114. package/src/subscriptions/mappers.ts +5 -8
  115. package/src/subscriptions/types.ts +11 -3
  116. package/src/testing/catalog-hash.ts +19 -0
  117. package/src/testing/index.ts +2 -0
  118. package/src/testing/projection-parity.ts +167 -0
  119. package/src/travel/reach.ts +23 -0
  120. package/src/travel/route-planner.ts +196 -0
  121. package/src/travel/travel.ts +200 -112
  122. package/src/types/capabilities.ts +29 -6
  123. package/src/types/entity.ts +3 -3
  124. package/src/types/index.ts +0 -1
  125. package/src/types.ts +28 -13
  126. package/src/utils/cargo.ts +27 -0
  127. package/src/utils/display-name.ts +70 -0
  128. package/src/utils/system.ts +36 -24
  129. package/src/capabilities/loading.ts +0 -8
  130. package/src/entities/container.ts +0 -108
  131. package/src/entities/ship-deploy.ts +0 -259
  132. package/src/entities/ship.ts +0 -204
  133. package/src/entities/warehouse.ts +0 -119
  134. package/src/types/entity-traits.ts +0 -69
@@ -0,0 +1,55 @@
1
+ import {describe, expect, test} from 'bun:test'
2
+ import {ServerContract} from '../contracts'
3
+ import {rollupGatherer, rollupCrafter, rollupLoaders} from './rollups'
4
+
5
+ const gLane = (slot: number, y: number, d: number, depth: number) =>
6
+ ServerContract.Types.gatherer_lane.from({
7
+ slot_index: slot,
8
+ yield: y,
9
+ drain: d,
10
+ depth,
11
+ output_pct: 100,
12
+ })
13
+ const cLane = (slot: number, s: number, d: number) =>
14
+ ServerContract.Types.crafter_lane.from({slot_index: slot, speed: s, drain: d, output_pct: 100})
15
+ const lLane = (slot: number, m: number, t: number) =>
16
+ ServerContract.Types.loader_lane.from({slot_index: slot, mass: m, thrust: t, output_pct: 100})
17
+
18
+ describe('rollupGatherer', () => {
19
+ test('empty → undefined', () => {
20
+ expect(rollupGatherer([])).toBeUndefined()
21
+ })
22
+ test('sums yield/drain, takes MAX depth', () => {
23
+ const r = rollupGatherer([gLane(2, 300, 1250, 500), gLane(3, 250, 1000, 5495)])!
24
+ expect(r.yield.toNumber()).toBe(550)
25
+ expect(r.drain.toNumber()).toBe(2250)
26
+ expect(r.depth.toNumber()).toBe(5495)
27
+ })
28
+ test('clamps summed yield to uint16', () => {
29
+ const r = rollupGatherer([gLane(2, 60000, 0, 1), gLane(3, 60000, 0, 1)])!
30
+ expect(r.yield.toNumber()).toBe(65535)
31
+ })
32
+ })
33
+
34
+ describe('rollupCrafter', () => {
35
+ test('empty → undefined', () => {
36
+ expect(rollupCrafter([])).toBeUndefined()
37
+ })
38
+ test('sums speed/drain', () => {
39
+ const r = rollupCrafter([cLane(2, 100, 30), cLane(3, 140, 25)])!
40
+ expect(r.speed.toNumber()).toBe(240)
41
+ expect(r.drain.toNumber()).toBe(55)
42
+ })
43
+ })
44
+
45
+ describe('rollupLoaders', () => {
46
+ test('empty → undefined', () => {
47
+ expect(rollupLoaders([])).toBeUndefined()
48
+ })
49
+ test('integer-averages mass, sums thrust, counts quantity', () => {
50
+ const r = rollupLoaders([lLane(2, 200, 5), lLane(3, 201, 7)])!
51
+ expect(r.mass.toNumber()).toBe(200) // floor(401/2)
52
+ expect(r.thrust.toNumber()).toBe(12)
53
+ expect(r.quantity.toNumber()).toBe(2)
54
+ })
55
+ })
@@ -0,0 +1,56 @@
1
+ import {UInt8, UInt16, UInt32} from '@wharfkit/antelope'
2
+ import type {ServerContract} from '../contracts'
3
+
4
+ export function rollupGatherer(
5
+ lanes: ServerContract.Types.gatherer_lane[]
6
+ ): {yield: UInt16; drain: UInt32; depth: UInt16} | undefined {
7
+ if (lanes.length === 0) return undefined
8
+ let totalYield = 0
9
+ let totalDrain = 0
10
+ let maxDepth = 0
11
+ for (const l of lanes) {
12
+ totalYield += Number(l.yield)
13
+ totalDrain += Number(l.drain)
14
+ const d = Number(l.depth)
15
+ if (d > maxDepth) maxDepth = d
16
+ }
17
+ return {
18
+ yield: UInt16.from(Math.min(totalYield, 65535)),
19
+ drain: UInt32.from(totalDrain),
20
+ depth: UInt16.from(maxDepth),
21
+ }
22
+ }
23
+
24
+ export function rollupCrafter(
25
+ lanes: ServerContract.Types.crafter_lane[]
26
+ ): {speed: UInt16; drain: UInt32} | undefined {
27
+ if (lanes.length === 0) return undefined
28
+ let totalSpeed = 0
29
+ let totalDrain = 0
30
+ for (const l of lanes) {
31
+ totalSpeed += Number(l.speed)
32
+ totalDrain += Number(l.drain)
33
+ }
34
+ return {
35
+ speed: UInt16.from(Math.min(totalSpeed, 65535)),
36
+ drain: UInt32.from(totalDrain),
37
+ }
38
+ }
39
+
40
+ export function rollupLoaders(
41
+ lanes: ServerContract.Types.loader_lane[]
42
+ ): {mass: UInt32; thrust: UInt16; quantity: UInt8} | undefined {
43
+ if (lanes.length === 0) return undefined
44
+ const count = lanes.length
45
+ let totalMass = 0
46
+ let totalThrust = 0
47
+ for (const l of lanes) {
48
+ totalMass += Number(l.mass)
49
+ totalThrust += Number(l.thrust)
50
+ }
51
+ return {
52
+ mass: UInt32.from(Math.floor(totalMass / count)),
53
+ thrust: UInt16.from(Math.min(totalThrust, 65535)),
54
+ quantity: UInt8.from(count),
55
+ }
56
+ }
@@ -0,0 +1,51 @@
1
+ import {expect, test} from 'bun:test'
2
+ import {
3
+ MAX_STARS_PER_STAT,
4
+ MAX_STAR_RATING,
5
+ starRating,
6
+ starsForStat,
7
+ statMagnitude,
8
+ STAR_STEP,
9
+ } from './stars'
10
+
11
+ test('starsForStat bands at the 250 boundaries', () => {
12
+ expect(starsForStat(0)).toBe(0)
13
+ expect(starsForStat(1)).toBe(0)
14
+ expect(starsForStat(249)).toBe(0)
15
+ expect(starsForStat(250)).toBe(1)
16
+ expect(starsForStat(499)).toBe(1)
17
+ expect(starsForStat(500)).toBe(2)
18
+ expect(starsForStat(749)).toBe(2)
19
+ expect(starsForStat(750)).toBe(3)
20
+ })
21
+
22
+ test('starsForStat clamps to MAX_STARS_PER_STAT', () => {
23
+ expect(starsForStat(999)).toBe(MAX_STARS_PER_STAT)
24
+ expect(starsForStat(1000)).toBe(MAX_STARS_PER_STAT)
25
+ expect(starsForStat(10_000)).toBe(MAX_STARS_PER_STAT)
26
+ })
27
+
28
+ test('starsForStat never goes negative', () => {
29
+ expect(starsForStat(-50)).toBe(0)
30
+ })
31
+
32
+ test('starRating sums per-stat stars to a 0-9 grade', () => {
33
+ expect(starRating(0, 0, 0)).toBe(0)
34
+ expect(starRating(250, 0, 0)).toBe(1)
35
+ expect(starRating(750, 750, 750)).toBe(MAX_STAR_RATING)
36
+ expect(starRating(999, 999, 999)).toBe(MAX_STAR_RATING)
37
+ expect(starRating(599, 599, 599)).toBe(6)
38
+ expect(starRating(251, 251, 251)).toBe(3)
39
+ })
40
+
41
+ test('statMagnitude sums raw values for tiebreaking', () => {
42
+ expect(statMagnitude(599, 599, 599)).toBe(1797)
43
+ expect(statMagnitude(251, 251, 251)).toBe(753)
44
+ expect(statMagnitude(599, 599, 599)).toBeGreaterThan(statMagnitude(251, 251, 251))
45
+ })
46
+
47
+ test('constants hold their documented values', () => {
48
+ expect(STAR_STEP).toBe(250)
49
+ expect(MAX_STARS_PER_STAT).toBe(3)
50
+ expect(MAX_STAR_RATING).toBe(9)
51
+ })
@@ -0,0 +1,15 @@
1
+ export const STAR_STEP = 250
2
+ export const MAX_STARS_PER_STAT = 3
3
+ export const MAX_STAR_RATING = MAX_STARS_PER_STAT * 3
4
+
5
+ export function starsForStat(value: number): number {
6
+ return Math.max(0, Math.min(MAX_STARS_PER_STAT, Math.floor(value / STAR_STEP)))
7
+ }
8
+
9
+ export function starRating(stat1: number, stat2: number, stat3: number): number {
10
+ return starsForStat(stat1) + starsForStat(stat2) + starsForStat(stat3)
11
+ }
12
+
13
+ export function statMagnitude(stat1: number, stat2: number, stat3: number): number {
14
+ return stat1 + stat2 + stat3
15
+ }
@@ -25,8 +25,7 @@ const ORE_STATS: StatDefinition[] = [
25
25
  key: 'density',
26
26
  label: 'Density',
27
27
  abbreviation: 'DEN',
28
- purpose: 'Mass per unit',
29
- inverted: true,
28
+ purpose: 'Structural integrity — higher rolls produce lighter hulls',
30
29
  },
31
30
  ]
32
31
 
@@ -74,10 +73,11 @@ const GAS_STATS: StatDefinition[] = [
74
73
 
75
74
  const REGOLITH_STATS: StatDefinition[] = [
76
75
  {
77
- key: 'composition',
78
- label: 'Composition',
79
- abbreviation: 'COM',
80
- purpose: 'Mineral/metal mix — drives sensor, chip, and optic crafting quality',
76
+ key: 'cohesion',
77
+ label: 'Cohesion',
78
+ abbreviation: 'COH',
79
+ purpose:
80
+ 'Binding strength of the loose aggregate; higher cohesion yields more rigid frames and hulls',
81
81
  },
82
82
  {
83
83
  key: 'hardness',
@@ -1,8 +1,9 @@
1
1
  import {Bytes, Checksum256, type Checksum256Type} from '@wharfkit/antelope'
2
2
  import {hash512} from '../utils/hash'
3
3
  import {Coordinates, type CoordinatesType} from '../types'
4
- import {getEligibleResources, getResourceWeight, YIELD_THRESHOLD} from './resources'
5
- import {RESERVE_TIERS, rollTier, rollWithinTier} from './tiers'
4
+ import {getItem} from '../data/catalog'
5
+ import {getEligibleResources, getResourceWeight, yieldThresholdAt} from './resources'
6
+ import {RESERVE_TIERS, rollTier, rollWithinTier, applyResourceTierMultiplier} from './tiers'
6
7
 
7
8
  export interface StratumInfo {
8
9
  itemId: number
@@ -33,15 +34,8 @@ export function deriveStratum(
33
34
 
34
35
  const rawReserve = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0
35
36
 
36
- let reserve = 0
37
- if (rawReserve <= YIELD_THRESHOLD) {
38
- const tierRoll = ((bytes[18] << 8) | bytes[19]) >>> 0
39
- const withinRoll = ((bytes[20] << 8) | bytes[21]) >>> 0
40
- const tier = rollTier(tierRoll, stratum)
41
- reserve = rollWithinTier(withinRoll, RESERVE_TIERS[tier])
42
- }
43
-
44
- if (reserve === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
37
+ if (rawReserve > yieldThresholdAt(stratum))
38
+ return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
45
39
 
46
40
  const eligible = getEligibleResources(locationType, subtype, stratum)
47
41
  if (eligible.length === 0) return {itemId: 0, seed: 0n, richness: 0, reserve: 0}
@@ -66,6 +60,13 @@ export function deriveStratum(
66
60
  }
67
61
  }
68
62
 
63
+ const tierRoll = ((bytes[18] << 8) | bytes[19]) >>> 0
64
+ const withinRoll = ((bytes[20] << 8) | bytes[21]) >>> 0
65
+ const tier = rollTier(tierRoll, stratum)
66
+ const selected = getItem(selectedItemId)
67
+ const baseReserve = rollWithinTier(withinRoll, RESERVE_TIERS[tier], selected.mass)
68
+ const reserve = applyResourceTierMultiplier(baseReserve, selected.tier)
69
+
69
70
  const seedBigInt =
70
71
  (BigInt(bytes[8]) << 56n) |
71
72
  (BigInt(bytes[9]) << 48n) |
@@ -76,15 +77,11 @@ export function deriveStratum(
76
77
  (BigInt(bytes[14]) << 8n) |
77
78
  BigInt(bytes[15])
78
79
 
79
- const rawRichness = (bytes[16] << 8) | bytes[17]
80
- const normalized = rawRichness / 65535
81
- const baseRichness = Math.floor(normalized * normalized * 999) + 1
82
-
83
- let depthBonus = 0
84
- if (stratum > 1) {
85
- depthBonus = (50 * Math.log(stratum)) / Math.log(65535)
86
- }
87
- const richness = Math.min(Math.floor(baseRichness + depthBonus), 1000)
80
+ let byteSum = 0
81
+ for (let i = 22; i <= 33; i++) byteSum += bytes[i]
82
+ const z = (byteSum - 1530) / 256
83
+ const roll = 500 + 100 * z
84
+ const richness = Math.max(1, Math.min(999, Math.round(roll)))
88
85
 
89
86
  return {itemId: selectedItemId, seed: seedBigInt, richness, reserve}
90
87
  }
@@ -1,3 +1,5 @@
1
+ import {getItem} from '../data/catalog'
2
+
1
3
  export type ReserveTier = 'small' | 'medium' | 'large' | 'massive' | 'motherlode'
2
4
 
3
5
  export interface TierRange {
@@ -6,11 +8,11 @@ export interface TierRange {
6
8
  }
7
9
 
8
10
  export const RESERVE_TIERS: Record<ReserveTier, TierRange> = {
9
- small: {min: 15, max: 60},
10
- medium: {min: 100, max: 200},
11
- large: {min: 400, max: 700},
12
- massive: {min: 1000, max: 2500},
13
- motherlode: {min: 4000, max: 10000},
11
+ small: {min: 3_600_000, max: 14_400_000},
12
+ medium: {min: 24_000_000, max: 48_000_000},
13
+ large: {min: 96_000_000, max: 168_000_000},
14
+ massive: {min: 240_000_000, max: 600_000_000},
15
+ motherlode: {min: 960_000_000, max: 2_400_000_000},
14
16
  }
15
17
 
16
18
  const SHALLOW_THRESHOLDS = {
@@ -47,8 +49,39 @@ export function rollTier(tierRoll: number, stratum: number): ReserveTier {
47
49
  return 'motherlode'
48
50
  }
49
51
 
50
- export function rollWithinTier(withinRoll: number, range: TierRange): number {
52
+ export function rollWithinTier(
53
+ withinRoll: number,
54
+ range: TierRange,
55
+ resourceUnitMass: number
56
+ ): number {
51
57
  const u = withinRoll / 65535
52
58
  const skewed = u * u
53
- return Math.floor(range.min + skewed * (range.max - range.min))
59
+ const depositMass = range.min + skewed * (range.max - range.min)
60
+ return Math.max(1, Math.floor(depositMass / resourceUnitMass))
61
+ }
62
+
63
+ // Must mirror the contract tier-multiplier table in tiers.hpp byte-for-byte; values are in tenths (T1..T10).
64
+ export const RESOURCE_TIER_MULT_TENTHS = [200, 154, 118, 91, 70, 54, 41, 32, 24, 19] as const
65
+
66
+ export function applyResourceTierMultiplier(units: number, resourceTier: number): number {
67
+ const idx = resourceTier < 1 ? 0 : resourceTier > 10 ? 9 : resourceTier - 1
68
+ const scaled = Math.floor((units * RESOURCE_TIER_MULT_TENTHS[idx]) / 10)
69
+ return scaled > 0 ? scaled : 1
70
+ }
71
+
72
+ const RESERVE_TIER_ENTRIES = Object.entries(RESERVE_TIERS) as Array<[ReserveTier, TierRange]>
73
+
74
+ export function tierOfReserve(reserve: number, itemId: number): ReserveTier | null {
75
+ if (reserve <= 0) return null
76
+ const item = getItem(itemId)
77
+ if (item.mass <= 0) return null
78
+ // Reverse the resource-tier multiplier so bands read relative to the resource tier.
79
+ const idx = item.tier < 1 ? 0 : item.tier > 10 ? 9 : item.tier - 1
80
+ const baseReserve = (reserve * 10) / RESOURCE_TIER_MULT_TENTHS[idx]
81
+ const impliedMassLow = baseReserve * item.mass
82
+ const impliedMassHigh = impliedMassLow + item.mass
83
+ for (const [tier, range] of RESERVE_TIER_ENTRIES) {
84
+ if (impliedMassHigh > range.min && impliedMassLow <= range.max) return tier
85
+ }
86
+ return null
54
87
  }
@@ -0,0 +1,136 @@
1
+ import type {Checksum256Type} from '@wharfkit/antelope'
2
+ import {hash512} from '../utils/hash'
3
+
4
+ export const WH = {
5
+ RSIZE: 75,
6
+ ZONE: 16384,
7
+ THRESHOLD: 8192,
8
+ MIN_REACH: 50000,
9
+ TRANSIT_SPEED: 500,
10
+ } as const
11
+
12
+ const HALF = Math.round(Math.log2(WH.ZONE))
13
+ const MASK = WH.ZONE - 1
14
+
15
+ function roll16(seed: Checksum256Type, str: string): number {
16
+ const h = hash512(seed, str).array
17
+ return (h[0] << 8) | h[1]
18
+ }
19
+ function feistelF(seed: Checksum256Type, x: number, round: number, key: string): number {
20
+ return roll16(seed, `feistel-${key}-${round}-${x}`) & MASK
21
+ }
22
+ export function feistel(seed: Checksum256Type, idx: number, key: string): number {
23
+ let L = (idx >>> HALF) & MASK
24
+ let R = idx & MASK
25
+ for (let r = 0; r < 4; r++) {
26
+ const nR = L ^ feistelF(seed, R, r, key)
27
+ L = R
28
+ R = nR
29
+ }
30
+ return (L << HALF) | R
31
+ }
32
+ export function feistelInv(seed: Checksum256Type, idx: number, key: string): number {
33
+ let L = (idx >>> HALF) & MASK
34
+ let R = idx & MASK
35
+ for (let r = 3; r >= 0; r--) {
36
+ const nL = R ^ feistelF(seed, L, r, key)
37
+ R = L
38
+ L = nL
39
+ }
40
+ return (L << HALF) | R
41
+ }
42
+
43
+ type Region = {rx: number; ry: number}
44
+
45
+ export function regionOf(x: number, y: number): Region {
46
+ return {rx: Math.floor(x / WH.RSIZE), ry: Math.floor(y / WH.RSIZE)}
47
+ }
48
+ export function partnerRegion(seed: Checksum256Type, R: Region): Region {
49
+ const qx = Math.floor(R.rx / WH.ZONE)
50
+ const qy = Math.floor(R.ry / WH.ZONE)
51
+ const zx = qx * WH.ZONE
52
+ const zy = qy * WH.ZONE
53
+ const key = `${qx}:${qy}`
54
+ const idx = (R.ry - zy) * WH.ZONE + (R.rx - zx)
55
+ const p = feistelInv(seed, feistel(seed, idx, key) ^ 1, key)
56
+ return {rx: zx + (p % WH.ZONE), ry: zy + Math.floor(p / WH.ZONE)}
57
+ }
58
+ function regKey(R: Region): string {
59
+ return `${R.rx}:${R.ry}`
60
+ }
61
+ function pairKey(a: Region, b: Region): string {
62
+ const ka = regKey(a)
63
+ const kb = regKey(b)
64
+ return ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`
65
+ }
66
+ function endpointInRegion(seed: Checksum256Type, R: Region, key: string): {x: number; y: number} {
67
+ const h = hash512(seed, `wh-endpoint-${key}-${regKey(R)}`).array
68
+ const ox = ((h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]) >>> 0
69
+ const oy = ((h[4] << 24) | (h[5] << 16) | (h[6] << 8) | h[7]) >>> 0
70
+ return {x: R.rx * WH.RSIZE + (ox % WH.RSIZE), y: R.ry * WH.RSIZE + (oy % WH.RSIZE)}
71
+ }
72
+ function dist(a: {x: number; y: number}, b: {x: number; y: number}): number {
73
+ return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2)
74
+ }
75
+ function wormholeOfRegion(
76
+ seed: Checksum256Type,
77
+ R: Region
78
+ ): {A: {x: number; y: number}; B: {x: number; y: number}} | null {
79
+ const P = partnerRegion(seed, R)
80
+ if (P.rx === R.rx && P.ry === R.ry) return null
81
+ const key = pairKey(R, P)
82
+ if (roll16(seed, `wh-exists-${key}`) >= WH.THRESHOLD) return null
83
+ const A = endpointInRegion(seed, R, key)
84
+ const B = endpointInRegion(seed, P, key)
85
+ if (dist(A, B) < WH.MIN_REACH) return null
86
+ return {A, B}
87
+ }
88
+ export function wormholeAtRegionEndpoint(
89
+ seed: Checksum256Type,
90
+ rx: number,
91
+ ry: number
92
+ ): {from: {x: number; y: number}; to: {x: number; y: number}} | null {
93
+ const w = wormholeOfRegion(seed, {rx, ry})
94
+ if (!w) return null
95
+ return {from: w.A, to: w.B}
96
+ }
97
+ export function wormholeAt(
98
+ seed: Checksum256Type,
99
+ x: number,
100
+ y: number
101
+ ): {x: number; y: number} | null {
102
+ const w = wormholeOfRegion(seed, regionOf(x, y))
103
+ if (!w || w.A.x !== x || w.A.y !== y) return null
104
+ return w.B
105
+ }
106
+
107
+ // Wormhole mouths (the local A endpoint) within reachTiles of (x,y); regions are RSIZE-wide so only a few overlap.
108
+ export function nearbyWormholes(
109
+ seed: Checksum256Type,
110
+ x: number,
111
+ y: number,
112
+ reachTiles: number
113
+ ): {x: number; y: number}[] {
114
+ const min = regionOf(x - reachTiles, y - reachTiles)
115
+ const max = regionOf(x + reachTiles, y + reachTiles)
116
+ const out: {x: number; y: number}[] = []
117
+ for (let rx = min.rx; rx <= max.rx; rx++) {
118
+ for (let ry = min.ry; ry <= max.ry; ry++) {
119
+ const w = wormholeOfRegion(seed, {rx, ry})
120
+ if (!w) continue
121
+ if (w.A.x === x && w.A.y === y) continue
122
+ if (dist({x, y}, w.A) <= reachTiles) out.push(w.A)
123
+ }
124
+ }
125
+ return out
126
+ }
127
+ export function isValidWormholePair(
128
+ seed: Checksum256Type,
129
+ ax: number,
130
+ ay: number,
131
+ bx: number,
132
+ by: number
133
+ ): boolean {
134
+ const to = wormholeAt(seed, ax, ay)
135
+ return to !== null && to.x === bx && to.y === by
136
+ }
@@ -0,0 +1,98 @@
1
+ import {UInt64} from '@wharfkit/antelope'
2
+ import {ServerContract} from '../contracts'
3
+ import {
4
+ CAP_DEMOLISH,
5
+ CAP_MODULES,
6
+ CAP_UNDEPLOY,
7
+ CAP_WRAP,
8
+ type EntityClass,
9
+ getEntityClass,
10
+ kindCan,
11
+ } from '../data/kind-registry'
12
+ import {InventoryAccessor} from './inventory-accessor'
13
+ import {Location} from './location'
14
+ import {ScheduleAccessor} from '../scheduling/accessor'
15
+ import * as schedule from '../scheduling/schedule'
16
+ import type {EntityInventory} from './entity-inventory'
17
+
18
+ export class Entity extends ServerContract.Types.entity_info {
19
+ private _sched?: ScheduleAccessor
20
+ private _inv?: InventoryAccessor
21
+
22
+ get name(): string {
23
+ return this.entity_name
24
+ }
25
+
26
+ get location(): Location {
27
+ return Location.from(this.coordinates)
28
+ }
29
+
30
+ get isIdle(): boolean {
31
+ return schedule.isIdle(this)
32
+ }
33
+
34
+ get sched(): ScheduleAccessor {
35
+ this._sched ??= new ScheduleAccessor(this)
36
+ return this._sched
37
+ }
38
+
39
+ get inv(): InventoryAccessor {
40
+ this._inv ??= new InventoryAccessor(this)
41
+ return this._inv
42
+ }
43
+
44
+ get inventory(): EntityInventory[] {
45
+ return this.inv.items
46
+ }
47
+
48
+ get totalCargoMass(): UInt64 {
49
+ return this.inv.totalMass
50
+ }
51
+
52
+ get maxCapacity(): UInt64 {
53
+ return UInt64.from(this.capacity ?? 0)
54
+ }
55
+
56
+ get availableCapacity(): UInt64 {
57
+ const cargo = this.totalCargoMass
58
+ const max = this.maxCapacity
59
+ return cargo.gte(max) ? UInt64.from(0) : max.subtracting(cargo)
60
+ }
61
+
62
+ get isFull(): boolean {
63
+ return this.totalCargoMass.gte(this.maxCapacity)
64
+ }
65
+
66
+ get totalMass(): UInt64 {
67
+ const hull = this.hullmass ? UInt64.from(this.hullmass) : UInt64.from(0)
68
+ return hull.adding(this.totalCargoMass)
69
+ }
70
+
71
+ get entityClass(): EntityClass {
72
+ return getEntityClass(this.type)
73
+ }
74
+
75
+ get canWrap(): boolean {
76
+ return kindCan(this.type, CAP_WRAP)
77
+ }
78
+
79
+ get canUndeploy(): boolean {
80
+ return kindCan(this.type, CAP_UNDEPLOY)
81
+ }
82
+
83
+ get canDemolish(): boolean {
84
+ return kindCan(this.type, CAP_DEMOLISH)
85
+ }
86
+
87
+ get canUseModules(): boolean {
88
+ return kindCan(this.type, CAP_MODULES)
89
+ }
90
+
91
+ isLoading(now: Date): boolean {
92
+ return schedule.isLoading(this, now)
93
+ }
94
+
95
+ isUnloading(now: Date): boolean {
96
+ return schedule.isUnloading(this, now)
97
+ }
98
+ }
@@ -1,4 +1,4 @@
1
- import {Checksum256, Int64, type UInt64} from '@wharfkit/antelope'
1
+ import {Checksum256, Int64, UInt64} from '@wharfkit/antelope'
2
2
  import {type PlatformContract, ServerContract} from '../contracts'
3
3
  import {type EpochInfo, getCurrentEpoch, getEpochInfo} from '../scheduling/epoch'
4
4
  import {hasSystem} from '../utils/system'
@@ -34,7 +34,7 @@ export class GameState extends ServerContract.Types.state_row {
34
34
  * Get the current epoch number from the state
35
35
  */
36
36
  get currentEpoch(): UInt64 {
37
- return this.epoch
37
+ return UInt64.from(this.epoch.toString())
38
38
  }
39
39
 
40
40
  /**
@@ -58,27 +58,6 @@ export class GameState extends ServerContract.Types.state_row {
58
58
  return this.enabled
59
59
  }
60
60
 
61
- /**
62
- * Get the total number of ships in the game
63
- */
64
- get shipCount(): number {
65
- return Number(this.ships)
66
- }
67
-
68
- /**
69
- * Get the current salt value (used for random number generation)
70
- */
71
- get currentSalt(): UInt64 {
72
- return this.salt
73
- }
74
-
75
- /**
76
- * Get the commit hash for the next epoch
77
- */
78
- get nextEpochCommit(): Checksum256 {
79
- return this.commit
80
- }
81
-
82
61
  /**
83
62
  * Calculate the current epoch from game config (if game is set)
84
63
  * This might differ from state.epoch if the blockchain hasn't advanced yet
@@ -97,7 +76,7 @@ export class GameState extends ServerContract.Types.state_row {
97
76
  if (!this._game) {
98
77
  return undefined
99
78
  }
100
- return getEpochInfo(this._game, this.epoch)
79
+ return getEpochInfo(this._game, this.currentEpoch)
101
80
  }
102
81
 
103
82
  /**
@@ -137,16 +116,12 @@ export class GameState extends ServerContract.Types.state_row {
137
116
  get summary(): {
138
117
  enabled: boolean
139
118
  epoch: string
140
- ships: number
141
119
  hasSeed: boolean
142
- hasCommit: boolean
143
120
  } {
144
121
  return {
145
122
  enabled: this.enabled,
146
123
  epoch: this.epoch.toString(),
147
- ships: this.shipCount,
148
124
  hasSeed: !this.seed.equals(Checksum256.from('0'.repeat(64))),
149
- hasCommit: !this.commit.equals(Checksum256.from('0'.repeat(64))),
150
125
  }
151
126
  }
152
127
  }