@shipload/sdk 1.0.0-next.64 → 1.0.0-next.66
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/lib/shipload.d.ts +754 -316
- package/lib/shipload.js +2650 -1336
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +2564 -1308
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +219 -145
- package/lib/testing.js +910 -557
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +910 -557
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/crafting.test.ts +51 -6
- package/src/capabilities/crafting.ts +19 -4
- package/src/capabilities/gathering.ts +2 -2
- package/src/capabilities/storage.test.ts +26 -0
- package/src/capabilities/storage.ts +6 -6
- package/src/contracts/fund.ts +110 -5
- package/src/contracts/platform.ts +10 -1
- package/src/contracts/server.ts +498 -331
- package/src/data/charters.json +325 -127
- package/src/data/entities.json +24 -24
- package/src/data/items.json +114 -114
- package/src/data/kind-registry.json +3 -3
- package/src/data/kind-registry.ts +5 -0
- package/src/data/recipes.json +56 -95
- package/src/derivation/build-methods.test.ts +59 -1
- package/src/derivation/build-methods.ts +2 -0
- package/src/derivation/capabilities.ts +7 -7
- package/src/derivation/strata.ts +5 -3
- package/src/derivation/tiers.ts +5 -5
- package/src/entity/hydrate.test.ts +156 -0
- package/src/entity/hydrate.ts +86 -0
- package/src/errors.ts +9 -0
- package/src/format.ts +15 -13
- package/src/index-module.ts +33 -5
- package/src/influence/ballot.test.ts +103 -0
- package/src/influence/ballot.ts +98 -0
- package/src/influence/charter-metadata.test.ts +136 -0
- package/src/influence/charter-metadata.ts +236 -0
- package/src/influence/charters.test.ts +51 -65
- package/src/influence/charters.ts +70 -40
- package/src/influence/civic.test.ts +278 -0
- package/src/influence/civic.ts +269 -0
- package/src/influence/constants.ts +30 -9
- package/src/influence/decomp.ts +15 -15
- package/src/influence/duration.ts +42 -6
- package/src/influence/index.ts +49 -6
- package/src/influence/influence.test.ts +76 -9
- package/src/influence/valuation.ts +17 -17
- package/src/managers/actions.ts +65 -37
- package/src/managers/base.ts +4 -0
- package/src/managers/context.ts +3 -1
- package/src/managers/index.ts +5 -2
- package/src/managers/influence.test.ts +18 -20
- package/src/managers/influence.ts +162 -84
- package/src/managers/jobs.test.ts +2 -2
- package/src/managers/jobs.ts +4 -4
- package/src/nft/description.ts +10 -11
- package/src/planner/index.ts +54 -0
- package/src/planner/planner.test.ts +127 -2
- package/src/resolution/display-name.ts +1 -1
- package/src/scheduling/availability.test.ts +113 -0
- package/src/scheduling/availability.ts +48 -8
- package/src/scheduling/jobs.ts +1 -1
- package/src/scheduling/projection.ts +2 -16
- package/src/scheduling/task-cargo.test.ts +62 -1
- package/src/scheduling/task-cargo.ts +25 -10
- package/src/scheduling/task-effects.test.ts +35 -0
- package/src/scheduling/task-effects.ts +34 -0
- package/src/scheduling/unwrap.test.ts +3 -3
- package/src/scheduling/unwrap.ts +3 -3
- package/src/shipload.ts +23 -4
- package/src/travel/reach.test.ts +86 -0
- package/src/travel/reach.ts +23 -10
- package/src/travel/route-build.test.ts +63 -0
- package/src/travel/route-build.ts +60 -0
- package/src/travel/travel.ts +2 -1
- package/src/types.ts +6 -2
- package/src/utils/hash.ts +18 -2
package/src/shipload.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {APIClient} from '@wharfkit/antelope'
|
|
2
|
-
import {PlatformContract, ServerContract} from './contracts'
|
|
2
|
+
import {FundContract, PlatformContract, ServerContract} from './contracts'
|
|
3
3
|
import type {ChainDefinition} from '@wharfkit/common'
|
|
4
|
-
import ContractKit,
|
|
4
|
+
import {ContractKit, type Contract} from '@wharfkit/contract'
|
|
5
5
|
|
|
6
6
|
import {GameContext} from './managers/context'
|
|
7
7
|
import type {EntitiesManager} from './managers/entities'
|
|
@@ -21,6 +21,7 @@ import type {GameState} from './entities/gamestate'
|
|
|
21
21
|
interface ShiploadOptions {
|
|
22
22
|
platformContractName?: string
|
|
23
23
|
serverContractName?: string
|
|
24
|
+
fundContractName?: string
|
|
24
25
|
client?: APIClient
|
|
25
26
|
subscriptionsUrl?: string
|
|
26
27
|
atomicAssetsAccount?: string
|
|
@@ -29,13 +30,14 @@ interface ShiploadOptions {
|
|
|
29
30
|
interface ShiploadConstructorOptions extends ShiploadOptions {
|
|
30
31
|
platformContract?: Contract
|
|
31
32
|
serverContract?: Contract
|
|
33
|
+
fundContract?: Contract
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export class Shipload {
|
|
35
37
|
private readonly _context: GameContext
|
|
36
38
|
|
|
37
39
|
constructor(chain: ChainDefinition, constructorOptions?: ShiploadConstructorOptions) {
|
|
38
|
-
const {client, platformContract, serverContract} = constructorOptions || {}
|
|
40
|
+
const {client, platformContract, serverContract, fundContract} = constructorOptions || {}
|
|
39
41
|
const apiClient = client || new APIClient({url: chain.url})
|
|
40
42
|
|
|
41
43
|
const platform = platformContract
|
|
@@ -46,11 +48,14 @@ export class Shipload {
|
|
|
46
48
|
? serverContract
|
|
47
49
|
: new ServerContract.Contract({client: apiClient})
|
|
48
50
|
|
|
51
|
+
const fund = fundContract ? fundContract : new FundContract.Contract({client: apiClient})
|
|
52
|
+
|
|
49
53
|
this._context = new GameContext(
|
|
50
54
|
apiClient,
|
|
51
55
|
server,
|
|
52
56
|
platform,
|
|
53
|
-
constructorOptions?.atomicAssetsAccount ?? 'atomicassets'
|
|
57
|
+
constructorOptions?.atomicAssetsAccount ?? 'atomicassets',
|
|
58
|
+
fund
|
|
54
59
|
)
|
|
55
60
|
|
|
56
61
|
if (constructorOptions?.subscriptionsUrl) {
|
|
@@ -80,10 +85,20 @@ export class Shipload {
|
|
|
80
85
|
server = await contractKit.load(shiploadOptions.serverContractName)
|
|
81
86
|
}
|
|
82
87
|
|
|
88
|
+
let fund: Contract = new FundContract.Contract({
|
|
89
|
+
client: new APIClient({url: chain.url}),
|
|
90
|
+
})
|
|
91
|
+
if (shiploadOptions?.fundContractName) {
|
|
92
|
+
const client = shiploadOptions.client || new APIClient({url: chain.url})
|
|
93
|
+
const contractKit = new ContractKit({client})
|
|
94
|
+
fund = await contractKit.load(shiploadOptions.fundContractName)
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
return new Shipload(chain, {
|
|
84
98
|
...shiploadOptions,
|
|
85
99
|
platformContract: platform,
|
|
86
100
|
serverContract: server,
|
|
101
|
+
fundContract: fund,
|
|
87
102
|
})
|
|
88
103
|
}
|
|
89
104
|
|
|
@@ -103,6 +118,10 @@ export class Shipload {
|
|
|
103
118
|
return this._context.platform
|
|
104
119
|
}
|
|
105
120
|
|
|
121
|
+
get fund(): Contract {
|
|
122
|
+
return this._context.fund
|
|
123
|
+
}
|
|
124
|
+
|
|
106
125
|
get entities(): EntitiesManager {
|
|
107
126
|
return this._context.entities
|
|
108
127
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {expect, test} from 'bun:test'
|
|
2
|
+
import type {RouteMoverInput} from './route-simulator'
|
|
3
|
+
import {computeGroupPerLegReach, computePerLegReach, type ReachStats} from './reach'
|
|
4
|
+
|
|
5
|
+
// Type-level: a RouteMoverInput (webapp's mover shape) must satisfy ReachStats with no cast.
|
|
6
|
+
function acceptsReachStats(_s: ReachStats): void {}
|
|
7
|
+
function checkRouteMoverAssignable(m: RouteMoverInput) {
|
|
8
|
+
acceptsReachStats(m)
|
|
9
|
+
}
|
|
10
|
+
void checkRouteMoverAssignable
|
|
11
|
+
|
|
12
|
+
test('capacity basis: reach is generator capacity over engine drain, unrounded', () => {
|
|
13
|
+
const reach = computePerLegReach({generator: {capacity: 550n}, engines: {drain: 100n}})
|
|
14
|
+
expect(reach).toBe(5.5)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('capacity basis is the default when opts is omitted', () => {
|
|
18
|
+
const reach = computePerLegReach({generator: {capacity: 1000n}, engines: {drain: 250n}})
|
|
19
|
+
expect(reach).toBe(4)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('current basis prefers generator capacity over current energy, matching the webapp fallback', () => {
|
|
23
|
+
const reach = computePerLegReach(
|
|
24
|
+
{generator: {capacity: 1000n}, engines: {drain: 100n}, energy: 250n},
|
|
25
|
+
{basis: 'current'}
|
|
26
|
+
)
|
|
27
|
+
expect(reach).toBe(10)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('current basis falls back to current energy when there is no generator', () => {
|
|
31
|
+
const reach = computePerLegReach({engines: {drain: 100n}, energy: 350n}, {basis: 'current'})
|
|
32
|
+
expect(reach).toBe(3.5)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('current basis with a lower current energy still returns the capacity-derived reach', () => {
|
|
36
|
+
const reach = computePerLegReach(
|
|
37
|
+
{generator: {capacity: 900n}, engines: {drain: 90n}, energy: 90n},
|
|
38
|
+
{basis: 'current'}
|
|
39
|
+
)
|
|
40
|
+
expect(reach).toBe(10)
|
|
41
|
+
expect(reach).not.toBe(1)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('capacity basis throws for a generator-less mover', () => {
|
|
45
|
+
expect(() => computePerLegReach({engines: {drain: 100n}})).toThrow()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('current basis throws when there is no current energy figure either', () => {
|
|
49
|
+
expect(() => computePerLegReach({engines: {drain: 100n}}, {basis: 'current'})).toThrow()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('an empty mover list has zero reach rather than throwing', () => {
|
|
53
|
+
expect(computeGroupPerLegReach([])).toBe(0)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('a group with no moving entity has zero reach', () => {
|
|
57
|
+
const reach = computeGroupPerLegReach([{generator: {capacity: 1000n}}])
|
|
58
|
+
expect(reach).toBe(0)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('a RouteMoverInput-shaped mover with hasMovement: false is excluded from the group', () => {
|
|
62
|
+
const mover: ReachStats = {
|
|
63
|
+
hasMovement: false,
|
|
64
|
+
engines: {drain: 100},
|
|
65
|
+
generator: {capacity: 1000},
|
|
66
|
+
}
|
|
67
|
+
expect(computeGroupPerLegReach([mover])).toBe(0)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('hasMovement defaults to true when absent', () => {
|
|
71
|
+
const mover: ReachStats = {engines: {drain: 100}, generator: {capacity: 1000}}
|
|
72
|
+
expect(computeGroupPerLegReach([mover])).toBe(10)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('group reach is the minimum across movers', () => {
|
|
76
|
+
const fast: ReachStats = {engines: {drain: 100n}, generator: {capacity: 1000n}}
|
|
77
|
+
const slow: ReachStats = {engines: {drain: 100n}, generator: {capacity: 550n}}
|
|
78
|
+
expect(computeGroupPerLegReach([fast, slow])).toBe(5.5)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Settled by contracts/test/server/reach-parity.test.ts: the contract truncates, so ceiling here would overclaim reach.
|
|
82
|
+
test('rounding: a fractional reach is left unrounded, not ceiled', () => {
|
|
83
|
+
const reach = computePerLegReach({generator: {capacity: 550n}, engines: {drain: 100n}})
|
|
84
|
+
expect(reach).not.toBe(Math.ceil(reach))
|
|
85
|
+
expect(Math.floor(reach)).toBe(5)
|
|
86
|
+
})
|
package/src/travel/reach.ts
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
export interface ReachStats {
|
|
2
|
-
generator?: {capacity: bigint}
|
|
3
|
-
engines?: {drain: bigint}
|
|
2
|
+
generator?: {capacity: bigint | number}
|
|
3
|
+
engines?: {drain: bigint | number}
|
|
4
|
+
energy?: bigint | number
|
|
5
|
+
hasMovement?: boolean
|
|
4
6
|
}
|
|
5
7
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
+
export interface ComputePerLegReachOptions {
|
|
9
|
+
basis?: 'capacity' | 'current'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function computePerLegReach(s: ReachStats, opts: ComputePerLegReachOptions = {}): number {
|
|
13
|
+
const basis = opts.basis ?? 'capacity'
|
|
14
|
+
const energy =
|
|
15
|
+
basis === 'capacity' ? s.generator?.capacity : (s.generator?.capacity ?? s.energy)
|
|
8
16
|
const drain = s.engines?.drain
|
|
9
|
-
if (
|
|
17
|
+
if (energy === undefined || drain === undefined || Number(drain) === 0) {
|
|
10
18
|
throw new Error('entity has no usable engine/generator (cannot compute per-leg reach)')
|
|
11
19
|
}
|
|
12
|
-
return Number(
|
|
20
|
+
return Number(energy) / Number(drain)
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
export function computeGroupPerLegReach(
|
|
16
|
-
|
|
23
|
+
export function computeGroupPerLegReach(
|
|
24
|
+
participants: ReachStats[],
|
|
25
|
+
opts: ComputePerLegReachOptions = {}
|
|
26
|
+
): number {
|
|
27
|
+
const movers = participants.filter(
|
|
28
|
+
(p) => (p.hasMovement ?? true) && p.engines !== undefined && Number(p.engines.drain) !== 0
|
|
29
|
+
)
|
|
17
30
|
if (movers.length === 0) {
|
|
18
|
-
|
|
31
|
+
return 0
|
|
19
32
|
}
|
|
20
|
-
return Math.min(...movers.map(computePerLegReach))
|
|
33
|
+
return Math.min(...movers.map((m) => computePerLegReach(m, opts)))
|
|
21
34
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {expect, test} from 'bun:test'
|
|
2
|
+
import type {Coord, SystemGraph} from './route-planner'
|
|
3
|
+
import {planRouteWithRetry} from './route-build'
|
|
4
|
+
|
|
5
|
+
function countingGraph(opts: {
|
|
6
|
+
reachable: boolean
|
|
7
|
+
nearby: (c: Coord) => {coord: Coord; dist: number}[]
|
|
8
|
+
}): {graph: SystemGraph; hasSystemCalls: () => number} {
|
|
9
|
+
let calls = 0
|
|
10
|
+
const graph: SystemGraph = {
|
|
11
|
+
hasSystem: () => {
|
|
12
|
+
calls++
|
|
13
|
+
return opts.reachable
|
|
14
|
+
},
|
|
15
|
+
nearby: opts.nearby,
|
|
16
|
+
}
|
|
17
|
+
return {graph, hasSystemCalls: () => calls}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test('a route found on the first attempt does not retry', () => {
|
|
21
|
+
const origin: Coord = {x: 0, y: 0}
|
|
22
|
+
const dest: Coord = {x: 3, y: 0}
|
|
23
|
+
const {graph, hasSystemCalls} = countingGraph({reachable: true, nearby: () => []})
|
|
24
|
+
|
|
25
|
+
const result = planRouteWithRetry({origin, dest, perLegReach: 5, graph})
|
|
26
|
+
|
|
27
|
+
expect(result.ok).toBe(true)
|
|
28
|
+
expect(hasSystemCalls()).toBe(1)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('a no-path result widens the corridor and retries until a route is found', () => {
|
|
32
|
+
const origin: Coord = {x: 0, y: 0}
|
|
33
|
+
const dest: Coord = {x: 100, y: 0}
|
|
34
|
+
const mid: Coord = {x: 50, y: 28.4}
|
|
35
|
+
const perLegReach = 6.4
|
|
36
|
+
|
|
37
|
+
const {graph, hasSystemCalls} = countingGraph({
|
|
38
|
+
reachable: true,
|
|
39
|
+
nearby: (c) => {
|
|
40
|
+
if (c.x === origin.x && c.y === origin.y) return [{coord: mid, dist: 0}]
|
|
41
|
+
if (c.x === mid.x && c.y === mid.y) return [{coord: dest, dist: 0}]
|
|
42
|
+
return []
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const result = planRouteWithRetry({origin, dest, perLegReach, graph})
|
|
47
|
+
|
|
48
|
+
expect(result.ok).toBe(true)
|
|
49
|
+
// Excluded from the default corridor, found once corridorSlack widens to perLegReach * 3.
|
|
50
|
+
expect(hasSystemCalls()).toBe(2)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('a non-no-path failure returns immediately without retrying', () => {
|
|
54
|
+
const origin: Coord = {x: 0, y: 0}
|
|
55
|
+
const dest: Coord = {x: 100, y: 0}
|
|
56
|
+
const {graph, hasSystemCalls} = countingGraph({reachable: false, nearby: () => []})
|
|
57
|
+
|
|
58
|
+
const result = planRouteWithRetry({origin, dest, perLegReach: 5, graph})
|
|
59
|
+
|
|
60
|
+
expect(result.ok).toBe(false)
|
|
61
|
+
if (!result.ok) expect(result.reason).toBe('empty-destination')
|
|
62
|
+
expect(hasSystemCalls()).toBe(1)
|
|
63
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {calc_rechargetime} from './travel'
|
|
2
|
+
import {MAX_LEGS, planRoute} from './route-planner'
|
|
3
|
+
import type {
|
|
4
|
+
Coord,
|
|
5
|
+
RouteHeuristicCost,
|
|
6
|
+
RouteLegCost,
|
|
7
|
+
RouteResult,
|
|
8
|
+
SystemGraph,
|
|
9
|
+
} from './route-planner'
|
|
10
|
+
import {simulateRoute} from './route-simulator'
|
|
11
|
+
import type {RouteMoverInput} from './route-simulator'
|
|
12
|
+
|
|
13
|
+
export function planRouteWithRetry(input: {
|
|
14
|
+
origin: Coord
|
|
15
|
+
dest: Coord
|
|
16
|
+
perLegReach: number
|
|
17
|
+
graph: SystemGraph
|
|
18
|
+
maxLegs?: number
|
|
19
|
+
legCost?: RouteLegCost
|
|
20
|
+
heuristicCost?: RouteHeuristicCost
|
|
21
|
+
}): RouteResult {
|
|
22
|
+
const args = {...input, maxLegs: input.maxLegs ?? MAX_LEGS}
|
|
23
|
+
const first = planRoute(args)
|
|
24
|
+
if (first.ok || first.reason !== 'no-path') return first
|
|
25
|
+
const widened = planRoute({...args, corridorSlack: input.perLegReach * 3})
|
|
26
|
+
if (widened.ok || widened.reason !== 'no-path') return widened
|
|
27
|
+
// The chain imposes no corridor, only per-hop reach, so exhaust the graph (nodeBudget-bounded) before declaring no-path.
|
|
28
|
+
return planRoute({...args, corridorSlack: Number.POSITIVE_INFINITY})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function contractRouteLegCost(movers: readonly RouteMoverInput[]): RouteLegCost {
|
|
32
|
+
return ({from, to, isDestination}) => {
|
|
33
|
+
const sim = simulateRoute([...movers], [to], from, true)
|
|
34
|
+
const leg = sim.legs[0]
|
|
35
|
+
if (!sim.reachable || !leg) return null
|
|
36
|
+
|
|
37
|
+
let rechargeAfter = 0
|
|
38
|
+
for (const mover of movers) {
|
|
39
|
+
if (!mover.hasMovement || !mover.generator) continue
|
|
40
|
+
const cost = leg.energyCostByMover[String(mover.ref.entityId)] ?? 0
|
|
41
|
+
rechargeAfter = Math.max(
|
|
42
|
+
rechargeAfter,
|
|
43
|
+
Number(
|
|
44
|
+
calc_rechargetime(
|
|
45
|
+
mover.generator.capacity,
|
|
46
|
+
Math.max(0, mover.generator.capacity - cost),
|
|
47
|
+
mover.generator.recharge
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return leg.flightSeconds + (isDestination ? 0 : rechargeAfter)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function contractRouteHeuristic(movers: readonly RouteMoverInput[]): RouteHeuristicCost {
|
|
58
|
+
return (from, dest) =>
|
|
59
|
+
simulateRoute([...movers], [dest], from, true).legs[0]?.flightSeconds ?? 0
|
|
60
|
+
}
|
package/src/travel/travel.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type CargoMassInfo,
|
|
28
28
|
type Distance,
|
|
29
29
|
HAULER_EFFICIENCY_DENOM,
|
|
30
|
+
MASS_STAT_SCALE,
|
|
30
31
|
MAX_ORBITAL_ALTITUDE,
|
|
31
32
|
MIN_ORBITAL_ALTITUDE,
|
|
32
33
|
MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL,
|
|
@@ -253,7 +254,7 @@ export function calc_ship_acceleration(ship: ShipLike, mass: UInt64): number {
|
|
|
253
254
|
}
|
|
254
255
|
|
|
255
256
|
export function calc_acceleration(thrust: number, mass: number): number {
|
|
256
|
-
return (thrust / mass) * PRECISION
|
|
257
|
+
return (thrust / (mass * MASS_STAT_SCALE)) * PRECISION
|
|
257
258
|
}
|
|
258
259
|
|
|
259
260
|
export function calc_ship_mass(ship: ShipLike, cargos: CargoMassInfo[]): UInt64 {
|
package/src/types.ts
CHANGED
|
@@ -11,7 +11,9 @@ import {ServerContract} from './contracts'
|
|
|
11
11
|
import {COORD_MAX, COORD_MIN} from './coordinates/constants'
|
|
12
12
|
|
|
13
13
|
export const PRECISION = 10000
|
|
14
|
-
export const
|
|
14
|
+
export const MASS_UNITS_PER_TONNE = 10
|
|
15
|
+
export const MASS_STAT_SCALE = 100
|
|
16
|
+
export const CRAFT_ENERGY_DIVISOR = 1500
|
|
15
17
|
|
|
16
18
|
export const PLANETARY_STRUCTURE_Z = 0
|
|
17
19
|
|
|
@@ -25,7 +27,7 @@ export const HAULER_EFFICIENCY_DENOM = 10000
|
|
|
25
27
|
export const MIN_ORBITAL_ALTITUDE = 800
|
|
26
28
|
export const MAX_ORBITAL_ALTITUDE = 3000
|
|
27
29
|
|
|
28
|
-
export const BASE_ORBITAL_MASS =
|
|
30
|
+
export const BASE_ORBITAL_MASS = 1000
|
|
29
31
|
|
|
30
32
|
export const MIN_TRANSFER_DISTANCE_PLANETARY_STRUCTURE = 100
|
|
31
33
|
export const MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL = 200
|
|
@@ -72,6 +74,8 @@ export enum TaskType {
|
|
|
72
74
|
SHUTTLE = 18,
|
|
73
75
|
REFIT = 19,
|
|
74
76
|
CONTRIBUTE = 20,
|
|
77
|
+
DEPOT_STORE = 21,
|
|
78
|
+
DEPOT_TAKE = 22,
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
export enum RefitOp {
|
package/src/utils/hash.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import {Bytes, Checksum256, type Checksum256Type, Checksum512} from '@wharfkit/antelope'
|
|
2
2
|
|
|
3
|
+
type NativeHasher = new (
|
|
4
|
+
algorithm: 'sha256' | 'sha512'
|
|
5
|
+
) => {
|
|
6
|
+
update(data: Uint8Array): {digest(): Uint8Array}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// antelope hashes through pure-JS hash.js; bun's native hasher is ~30x faster and byte-identical.
|
|
10
|
+
const nativeHasher: NativeHasher | undefined = (globalThis as {Bun?: {CryptoHasher?: NativeHasher}})
|
|
11
|
+
.Bun?.CryptoHasher
|
|
12
|
+
|
|
13
|
+
function digest(algorithm: 'sha256' | 'sha512', bytes: Bytes): Uint8Array | undefined {
|
|
14
|
+
return nativeHasher ? new nativeHasher(algorithm).update(bytes.array).digest() : undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
export function hash(seed: Checksum256Type, string: string): Checksum256 {
|
|
4
18
|
const bytes = Bytes.from(`${seed}${string}`, 'utf8')
|
|
5
|
-
|
|
19
|
+
const native = digest('sha256', bytes)
|
|
20
|
+
return native ? new Checksum256(native) : Checksum256.hash(bytes)
|
|
6
21
|
}
|
|
7
22
|
|
|
8
23
|
export function hash512(seed: Checksum256Type, string: string): Checksum512 {
|
|
9
24
|
const bytes = Bytes.from(`${seed}${string}`, 'utf8')
|
|
10
|
-
|
|
25
|
+
const native = digest('sha512', bytes)
|
|
26
|
+
return native ? new Checksum512(native) : Checksum512.hash(bytes)
|
|
11
27
|
}
|
|
12
28
|
|
|
13
29
|
export function uint16(hash: Checksum512, offset: number): number {
|