@series-inc/rundot-syncplay 5.23.0-beta.7
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/README.md +610 -0
- package/dist/animation.d.ts +119 -0
- package/dist/animation.js +488 -0
- package/dist/asset-cooking.d.ts +82 -0
- package/dist/asset-cooking.js +364 -0
- package/dist/authority-room.d.ts +217 -0
- package/dist/authority-room.js +701 -0
- package/dist/bot-documents.d.ts +115 -0
- package/dist/bot-documents.js +549 -0
- package/dist/bot-primitives.d.ts +152 -0
- package/dist/bot-primitives.js +472 -0
- package/dist/browser.d.ts +57 -0
- package/dist/browser.js +53 -0
- package/dist/canonical.d.ts +12 -0
- package/dist/canonical.js +288 -0
- package/dist/certification.d.ts +455 -0
- package/dist/certification.js +1855 -0
- package/dist/chaos-certification.d.ts +56 -0
- package/dist/chaos-certification.js +1397 -0
- package/dist/cjs/animation.js +495 -0
- package/dist/cjs/asset-cooking.js +372 -0
- package/dist/cjs/authority-room.js +704 -0
- package/dist/cjs/bot-documents.js +556 -0
- package/dist/cjs/bot-primitives.js +482 -0
- package/dist/cjs/browser.js +96 -0
- package/dist/cjs/canonical.js +299 -0
- package/dist/cjs/certification.js +1860 -0
- package/dist/cjs/chaos-certification.js +1402 -0
- package/dist/cjs/collections.js +36 -0
- package/dist/cjs/collision.js +96 -0
- package/dist/cjs/commands.js +987 -0
- package/dist/cjs/config-bundle.js +491 -0
- package/dist/cjs/creator.js +30 -0
- package/dist/cjs/deterministic-primitives.js +134 -0
- package/dist/cjs/deterministic-session.js +1410 -0
- package/dist/cjs/ecs-lite.js +155 -0
- package/dist/cjs/effects-adapter.js +97 -0
- package/dist/cjs/engine-complete.js +2297 -0
- package/dist/cjs/engine-core-proof.js +70 -0
- package/dist/cjs/engine-parity-matrix.js +874 -0
- package/dist/cjs/engine-parity.js +3183 -0
- package/dist/cjs/engine-runtime.js +3141 -0
- package/dist/cjs/errors.js +22 -0
- package/dist/cjs/events.js +247 -0
- package/dist/cjs/examples/counter-duel.js +157 -0
- package/dist/cjs/examples/fixed-arena.js +368 -0
- package/dist/cjs/index.js +229 -0
- package/dist/cjs/input-authority.js +150 -0
- package/dist/cjs/input-button.js +256 -0
- package/dist/cjs/input-timeline.js +418 -0
- package/dist/cjs/instant-replay.js +66 -0
- package/dist/cjs/lag-compensation3d.js +837 -0
- package/dist/cjs/local-authority-server-runner.js +1121 -0
- package/dist/cjs/local-authority-wire.js +1497 -0
- package/dist/cjs/local-authority.js +817 -0
- package/dist/cjs/local-ws-adapter.js +150 -0
- package/dist/cjs/match-log.js +61 -0
- package/dist/cjs/math.js +157 -0
- package/dist/cjs/mock-network-adapter.js +26 -0
- package/dist/cjs/movement.js +703 -0
- package/dist/cjs/movement3d.js +1246 -0
- package/dist/cjs/multiclient-authority.js +308 -0
- package/dist/cjs/multiplayer-service.js +434 -0
- package/dist/cjs/navigation.js +347 -0
- package/dist/cjs/navmesh.js +1437 -0
- package/dist/cjs/networked-client.js +691 -0
- package/dist/cjs/networked-input-authority.js +443 -0
- package/dist/cjs/node.js +47 -0
- package/dist/cjs/number-lab.js +78 -0
- package/dist/cjs/offline-session.js +370 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/pause-resume.js +43 -0
- package/dist/cjs/physics-cert.js +426 -0
- package/dist/cjs/physics2d.js +751 -0
- package/dist/cjs/physics3d.js +1996 -0
- package/dist/cjs/prediction-culling.js +203 -0
- package/dist/cjs/protocol-lab.js +1299 -0
- package/dist/cjs/r3f-render-adapter.js +209 -0
- package/dist/cjs/random.js +59 -0
- package/dist/cjs/replay-bundle.js +1598 -0
- package/dist/cjs/replay-inspect.js +236 -0
- package/dist/cjs/replay-scoring.js +68 -0
- package/dist/cjs/replay.js +356 -0
- package/dist/cjs/reports.js +181 -0
- package/dist/cjs/rollback-history.js +251 -0
- package/dist/cjs/runtime-guards.js +111 -0
- package/dist/cjs/sample-lab.js +145 -0
- package/dist/cjs/sample-scenes.js +752 -0
- package/dist/cjs/schema-artifacts.js +359 -0
- package/dist/cjs/schema-codegen.js +544 -0
- package/dist/cjs/schema-dsl-binary.js +655 -0
- package/dist/cjs/schema.js +319 -0
- package/dist/cjs/sdk-offline.js +99 -0
- package/dist/cjs/session-promotion.js +74 -0
- package/dist/cjs/session-snapshot.js +265 -0
- package/dist/cjs/session-wire.js +216 -0
- package/dist/cjs/signals.js +103 -0
- package/dist/cjs/sparse-set.js +169 -0
- package/dist/cjs/synctest.js +136 -0
- package/dist/cjs/system-lifecycle.js +286 -0
- package/dist/cjs/timers.js +61 -0
- package/dist/cjs/tools/static-checker.js +1468 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/venus-mp-budget.js +125 -0
- package/dist/cjs/ws-frame.js +255 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +11379 -0
- package/dist/collections.d.ts +9 -0
- package/dist/collections.js +30 -0
- package/dist/collision.d.ts +25 -0
- package/dist/collision.js +85 -0
- package/dist/commands.d.ts +137 -0
- package/dist/commands.js +977 -0
- package/dist/config-bundle.d.ts +89 -0
- package/dist/config-bundle.js +483 -0
- package/dist/creator.d.ts +20 -0
- package/dist/creator.js +11 -0
- package/dist/deterministic-primitives.d.ts +59 -0
- package/dist/deterministic-primitives.js +126 -0
- package/dist/deterministic-session.d.ts +197 -0
- package/dist/deterministic-session.js +1401 -0
- package/dist/ecs-lite.d.ts +26 -0
- package/dist/ecs-lite.js +152 -0
- package/dist/effects-adapter.d.ts +21 -0
- package/dist/effects-adapter.js +94 -0
- package/dist/engine-complete.d.ts +45 -0
- package/dist/engine-complete.js +2292 -0
- package/dist/engine-core-proof.d.ts +26 -0
- package/dist/engine-core-proof.js +66 -0
- package/dist/engine-parity-matrix.d.ts +300 -0
- package/dist/engine-parity-matrix.js +869 -0
- package/dist/engine-parity.d.ts +74 -0
- package/dist/engine-parity.js +3178 -0
- package/dist/engine-runtime.d.ts +351 -0
- package/dist/engine-runtime.js +3123 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +17 -0
- package/dist/events.d.ts +45 -0
- package/dist/events.js +242 -0
- package/dist/examples/counter-duel.d.ts +43 -0
- package/dist/examples/counter-duel.js +150 -0
- package/dist/examples/fixed-arena.d.ts +110 -0
- package/dist/examples/fixed-arena.js +356 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +66 -0
- package/dist/input-authority.d.ts +82 -0
- package/dist/input-authority.js +147 -0
- package/dist/input-button.d.ts +36 -0
- package/dist/input-button.js +249 -0
- package/dist/input-timeline.d.ts +65 -0
- package/dist/input-timeline.js +412 -0
- package/dist/instant-replay.d.ts +59 -0
- package/dist/instant-replay.js +62 -0
- package/dist/lag-compensation3d.d.ts +146 -0
- package/dist/lag-compensation3d.js +827 -0
- package/dist/local-authority-server-runner.d.ts +79 -0
- package/dist/local-authority-server-runner.js +1118 -0
- package/dist/local-authority-wire.d.ts +142 -0
- package/dist/local-authority-wire.js +1477 -0
- package/dist/local-authority.d.ts +160 -0
- package/dist/local-authority.js +810 -0
- package/dist/local-ws-adapter.d.ts +14 -0
- package/dist/local-ws-adapter.js +146 -0
- package/dist/match-log.d.ts +74 -0
- package/dist/match-log.js +57 -0
- package/dist/math.d.ts +41 -0
- package/dist/math.js +154 -0
- package/dist/mock-network-adapter.d.ts +13 -0
- package/dist/mock-network-adapter.js +23 -0
- package/dist/movement.d.ts +155 -0
- package/dist/movement.js +694 -0
- package/dist/movement3d.d.ts +195 -0
- package/dist/movement3d.js +1238 -0
- package/dist/multiclient-authority.d.ts +30 -0
- package/dist/multiclient-authority.js +305 -0
- package/dist/multiplayer-service.d.ts +153 -0
- package/dist/multiplayer-service.js +429 -0
- package/dist/navigation.d.ts +91 -0
- package/dist/navigation.js +338 -0
- package/dist/navmesh.d.ts +236 -0
- package/dist/navmesh.js +1423 -0
- package/dist/networked-client.d.ts +124 -0
- package/dist/networked-client.js +688 -0
- package/dist/networked-input-authority.d.ts +51 -0
- package/dist/networked-input-authority.js +439 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +19 -0
- package/dist/number-lab.d.ts +15 -0
- package/dist/number-lab.js +75 -0
- package/dist/offline-session.d.ts +2 -0
- package/dist/offline-session.js +367 -0
- package/dist/pause-resume.d.ts +48 -0
- package/dist/pause-resume.js +40 -0
- package/dist/physics-cert.d.ts +40 -0
- package/dist/physics-cert.js +389 -0
- package/dist/physics2d.d.ts +142 -0
- package/dist/physics2d.js +741 -0
- package/dist/physics3d.d.ts +285 -0
- package/dist/physics3d.js +1979 -0
- package/dist/prediction-culling.d.ts +34 -0
- package/dist/prediction-culling.js +200 -0
- package/dist/protocol-lab.d.ts +115 -0
- package/dist/protocol-lab.js +1295 -0
- package/dist/r3f-render-adapter.d.ts +61 -0
- package/dist/r3f-render-adapter.js +203 -0
- package/dist/random.d.ts +13 -0
- package/dist/random.js +55 -0
- package/dist/replay-bundle.d.ts +150 -0
- package/dist/replay-bundle.js +1586 -0
- package/dist/replay-inspect.d.ts +60 -0
- package/dist/replay-inspect.js +228 -0
- package/dist/replay-scoring.d.ts +28 -0
- package/dist/replay-scoring.js +65 -0
- package/dist/replay.d.ts +17 -0
- package/dist/replay.js +351 -0
- package/dist/reports.d.ts +176 -0
- package/dist/reports.js +174 -0
- package/dist/rollback-history.d.ts +46 -0
- package/dist/rollback-history.js +247 -0
- package/dist/runtime-guards.d.ts +1 -0
- package/dist/runtime-guards.js +108 -0
- package/dist/sample-lab.d.ts +32 -0
- package/dist/sample-lab.js +134 -0
- package/dist/sample-scenes.d.ts +86 -0
- package/dist/sample-scenes.js +745 -0
- package/dist/schema-artifacts.d.ts +49 -0
- package/dist/schema-artifacts.js +356 -0
- package/dist/schema-codegen.d.ts +21 -0
- package/dist/schema-codegen.js +538 -0
- package/dist/schema-dsl-binary.d.ts +75 -0
- package/dist/schema-dsl-binary.js +648 -0
- package/dist/schema.d.ts +87 -0
- package/dist/schema.js +315 -0
- package/dist/sdk-offline.d.ts +34 -0
- package/dist/sdk-offline.js +96 -0
- package/dist/session-promotion.d.ts +26 -0
- package/dist/session-promotion.js +70 -0
- package/dist/session-snapshot.d.ts +95 -0
- package/dist/session-snapshot.js +255 -0
- package/dist/session-wire.d.ts +89 -0
- package/dist/session-wire.js +211 -0
- package/dist/signals.d.ts +31 -0
- package/dist/signals.js +99 -0
- package/dist/sparse-set.d.ts +34 -0
- package/dist/sparse-set.js +165 -0
- package/dist/synctest.d.ts +31 -0
- package/dist/synctest.js +133 -0
- package/dist/system-lifecycle.d.ts +72 -0
- package/dist/system-lifecycle.js +281 -0
- package/dist/timers.d.ts +17 -0
- package/dist/timers.js +53 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/static-checker.d.ts +31 -0
- package/dist/tools/static-checker.js +1462 -0
- package/dist/tools/vite-plugin.d.ts +69 -0
- package/dist/tools/vite-plugin.js +203 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.js +1 -0
- package/dist/venus-mp-budget.d.ts +29 -0
- package/dist/venus-mp-budget.js +122 -0
- package/dist/ws-frame.d.ts +49 -0
- package/dist/ws-frame.js +247 -0
- package/package.json +169 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { defaultChecksum } from '../canonical.js';
|
|
2
|
+
import { fixedCircle, circleIntersects } from '../collision.js';
|
|
3
|
+
import { createDeterministicEventTimeline } from '../events.js';
|
|
4
|
+
import { createOfflineSession } from '../offline-session.js';
|
|
5
|
+
import { DeterministicRandom } from '../random.js';
|
|
6
|
+
import { verifyReplay } from '../replay.js';
|
|
7
|
+
import { defineSyncplayGame } from '../schema.js';
|
|
8
|
+
const arenaHalfExtent = 2048;
|
|
9
|
+
const playerRadius = 24;
|
|
10
|
+
const maxVelocity = 48;
|
|
11
|
+
const dashImpulse = 9;
|
|
12
|
+
const thrustScale = 3;
|
|
13
|
+
export const fixedArenaDefaultInput = {
|
|
14
|
+
thrustX: 0,
|
|
15
|
+
thrustY: 0,
|
|
16
|
+
dash: false,
|
|
17
|
+
};
|
|
18
|
+
export function createFixedArenaDescriptor(maxPlayers = 128) {
|
|
19
|
+
return {
|
|
20
|
+
deterministicVersion: 'fixed-arena@1',
|
|
21
|
+
tickRate: 30,
|
|
22
|
+
maxPlayers,
|
|
23
|
+
input: {
|
|
24
|
+
id: 'fixed-arena-input/v1',
|
|
25
|
+
fields: [
|
|
26
|
+
{ name: 'thrustX', kind: 'integer', policy: 'repeat', defaultValue: 0, min: -1, max: 1 },
|
|
27
|
+
{ name: 'thrustY', kind: 'integer', policy: 'repeat', defaultValue: 0, min: -1, max: 1 },
|
|
28
|
+
{ name: 'dash', kind: 'boolean', policy: 'command', defaultValue: false },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
state: {
|
|
32
|
+
id: 'fixed-arena-state/v1',
|
|
33
|
+
fields: [
|
|
34
|
+
{ name: 'frame', kind: 'integer', numericMode: 'integer', checksummed: true },
|
|
35
|
+
{ name: 'x', kind: 'integer', numericMode: 'integer', collectionOrdering: 'indexed', checksummed: true, min: -8192, max: 8192 },
|
|
36
|
+
{ name: 'y', kind: 'integer', numericMode: 'integer', collectionOrdering: 'indexed', checksummed: true, min: -8192, max: 8192 },
|
|
37
|
+
{ name: 'vx', kind: 'integer', numericMode: 'integer', collectionOrdering: 'indexed', checksummed: true, min: -maxVelocity, max: maxVelocity },
|
|
38
|
+
{ name: 'vy', kind: 'integer', numericMode: 'integer', collectionOrdering: 'indexed', checksummed: true, min: -maxVelocity, max: maxVelocity },
|
|
39
|
+
{ name: 'score', kind: 'integer', numericMode: 'integer', collectionOrdering: 'indexed', checksummed: true, min: 0, max: 1_000_000 },
|
|
40
|
+
{ name: 'collisions', kind: 'integer', numericMode: 'integer', collectionOrdering: 'indexed', checksummed: true, min: 0, max: 1_000_000 },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
systems: [{ id: 'input' }, { id: 'movement' }, { id: 'bounds' }, { id: 'collision' }],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function createFixedArenaSession(players, seed, options = {}) {
|
|
47
|
+
// Stamp the CONTENT-HASHED identity so descriptor-based verification
|
|
48
|
+
// (verifyReplayWithDescriptor) agrees with these replays.
|
|
49
|
+
const identity = defineSyncplayGame(createFixedArenaDescriptor(players)).replayIdentity;
|
|
50
|
+
return createOfflineSession({
|
|
51
|
+
tickRate: 30,
|
|
52
|
+
playerCount: players,
|
|
53
|
+
initialState: createInitialFixedArenaState(players, seed),
|
|
54
|
+
step: fixedArenaStep,
|
|
55
|
+
defaultInput: fixedArenaDefaultInput,
|
|
56
|
+
inputSchemaId: identity.inputSchemaId,
|
|
57
|
+
stateSchemaId: identity.stateSchemaId,
|
|
58
|
+
deterministicVersion: 'fixed-arena@1',
|
|
59
|
+
randomSeed: seed,
|
|
60
|
+
snapshotBufferSize: options.snapshotBufferSize,
|
|
61
|
+
replayRecordingMode: options.replayRecordingMode,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function runFixedArenaExample(options) {
|
|
65
|
+
validateFixedArenaOptions(options);
|
|
66
|
+
const session = createFixedArenaSession(options.players, options.seed);
|
|
67
|
+
const inputRandom = new DeterministicRandom(options.seed);
|
|
68
|
+
const eventTimeline = createDeterministicEventTimeline();
|
|
69
|
+
for (let frame = 0; frame < options.frames; frame += 1) {
|
|
70
|
+
for (let slot = 0; slot < options.players; slot += 1) {
|
|
71
|
+
session.setInput(slot, makeFixedArenaInput(inputRandom));
|
|
72
|
+
}
|
|
73
|
+
session.stepFrames(1);
|
|
74
|
+
const nextState = session.getState();
|
|
75
|
+
for (const pair of findFixedArenaCollisionPairs(nextState)) {
|
|
76
|
+
eventTimeline.emitVerifiedOnly('fixed-arena.collision', nextState.frame, {
|
|
77
|
+
a: pair.a,
|
|
78
|
+
b: pair.b,
|
|
79
|
+
ax: nextState.x[pair.a],
|
|
80
|
+
ay: nextState.y[pair.a],
|
|
81
|
+
bx: nextState.x[pair.b],
|
|
82
|
+
by: nextState.y[pair.b],
|
|
83
|
+
}, `fixed-arena.collision:${nextState.frame}:${pair.a}:${pair.b}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const replay = session.exportReplay();
|
|
87
|
+
if (options.verify === true) {
|
|
88
|
+
verifyFixedArenaReplay(replay);
|
|
89
|
+
}
|
|
90
|
+
const finalChecksum = replay.checksums.at(-1)?.checksum ?? defaultChecksum(session.getState());
|
|
91
|
+
return {
|
|
92
|
+
frames: options.frames,
|
|
93
|
+
players: options.players,
|
|
94
|
+
events: eventTimeline.records().length,
|
|
95
|
+
replayChecksums: replay.checksums.length,
|
|
96
|
+
finalChecksum,
|
|
97
|
+
verified: options.verify === true,
|
|
98
|
+
result: 'pass',
|
|
99
|
+
replay,
|
|
100
|
+
renderState: projectFixedArenaRenderState(session.getState()),
|
|
101
|
+
eventTimeline: eventTimeline.records(),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function verifyFixedArenaReplay(replay) {
|
|
105
|
+
const identity = defineSyncplayGame(createFixedArenaDescriptor(replay.playerCount)).replayIdentity;
|
|
106
|
+
const result = verifyReplay({
|
|
107
|
+
replay,
|
|
108
|
+
step: fixedArenaStep,
|
|
109
|
+
defaultInput: fixedArenaDefaultInput,
|
|
110
|
+
inputSchemaId: identity.inputSchemaId,
|
|
111
|
+
stateSchemaId: identity.stateSchemaId,
|
|
112
|
+
deterministicVersion: 'fixed-arena@1',
|
|
113
|
+
hydrateInput: hydrateFixedArenaInput,
|
|
114
|
+
});
|
|
115
|
+
if (result.mismatches !== 0) {
|
|
116
|
+
throw new Error(`Fixed Arena replay mismatch: ${result.mismatches}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export function projectFixedArenaRenderState(state) {
|
|
120
|
+
return {
|
|
121
|
+
players: state.x.map((x, slot) => ({
|
|
122
|
+
key: `player-${slot}`,
|
|
123
|
+
x,
|
|
124
|
+
y: state.y[slot],
|
|
125
|
+
radius: playerRadius,
|
|
126
|
+
color: slot % 2 === 0 ? 'cyan' : 'magenta',
|
|
127
|
+
score: state.score[slot],
|
|
128
|
+
collisions: state.collisions[slot],
|
|
129
|
+
})),
|
|
130
|
+
bounds: {
|
|
131
|
+
minX: -arenaHalfExtent,
|
|
132
|
+
minY: -arenaHalfExtent,
|
|
133
|
+
maxX: arenaHalfExtent,
|
|
134
|
+
maxY: arenaHalfExtent,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function projectFixedArenaReferenceView(state) {
|
|
139
|
+
const leaderboard = state.score
|
|
140
|
+
.map((score, slot) => ({
|
|
141
|
+
slot,
|
|
142
|
+
score,
|
|
143
|
+
collisions: state.collisions[slot],
|
|
144
|
+
color: fixedArenaColor(slot),
|
|
145
|
+
}))
|
|
146
|
+
.sort((left, right) => {
|
|
147
|
+
if (left.score !== right.score) {
|
|
148
|
+
return right.score - left.score;
|
|
149
|
+
}
|
|
150
|
+
return left.slot - right.slot;
|
|
151
|
+
});
|
|
152
|
+
const ranks = new Map(leaderboard.map((entry, index) => [entry.slot, index + 1]));
|
|
153
|
+
const topScore = leaderboard[0]?.score ?? 0;
|
|
154
|
+
return {
|
|
155
|
+
camera: {
|
|
156
|
+
position: [0, 0, 10],
|
|
157
|
+
zoom: 0.1,
|
|
158
|
+
},
|
|
159
|
+
arena: {
|
|
160
|
+
width: (arenaHalfExtent * 2) / 100,
|
|
161
|
+
height: (arenaHalfExtent * 2) / 100,
|
|
162
|
+
wallThickness: 0.2,
|
|
163
|
+
backgroundColor: '#071014',
|
|
164
|
+
wallColor: '#10242b',
|
|
165
|
+
},
|
|
166
|
+
players: state.x.map((x, slot) => ({
|
|
167
|
+
key: `player-${slot}`,
|
|
168
|
+
slot,
|
|
169
|
+
position: [x / 100, state.y[slot] / 100, 0],
|
|
170
|
+
scale: [1 + minInt(state.score[slot], 20) / 50, 1 + minInt(state.collisions[slot], 10) / 40, 1],
|
|
171
|
+
color: fixedArenaColor(slot),
|
|
172
|
+
ringColor: ranks.get(slot) === 1 ? '#f5d76e' : '#d7eef2',
|
|
173
|
+
velocity: [state.vx[slot] / 100, state.vy[slot] / 100, 0],
|
|
174
|
+
score: state.score[slot],
|
|
175
|
+
collisions: state.collisions[slot],
|
|
176
|
+
rank: ranks.get(slot) ?? state.x.length,
|
|
177
|
+
})),
|
|
178
|
+
leaderboard,
|
|
179
|
+
debug: {
|
|
180
|
+
frame: state.frame,
|
|
181
|
+
players: state.x.length,
|
|
182
|
+
topScore,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
export function makeFixedArenaInput(random) {
|
|
187
|
+
return {
|
|
188
|
+
thrustX: random.nextInt(-1, 1),
|
|
189
|
+
thrustY: random.nextInt(-1, 1),
|
|
190
|
+
dash: random.nextInt(0, 31) === 0,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
export function createInitialFixedArenaState(players, seed) {
|
|
194
|
+
const columns = ceilSqrtInt(players);
|
|
195
|
+
const spacing = maxInt(playerRadius * 3, truncDivTowardZero(arenaHalfExtent * 2, columns + 1));
|
|
196
|
+
const random = new DeterministicRandom(seed).fork('fixed-arena-spawn');
|
|
197
|
+
return {
|
|
198
|
+
frame: 0,
|
|
199
|
+
x: Array.from({ length: players }, (_, slot) => {
|
|
200
|
+
const column = slot % columns;
|
|
201
|
+
return -arenaHalfExtent + spacing * (column + 1) + random.nextInt(-8, 8);
|
|
202
|
+
}),
|
|
203
|
+
y: Array.from({ length: players }, (_, slot) => {
|
|
204
|
+
const row = truncDivTowardZero(slot, columns);
|
|
205
|
+
return -arenaHalfExtent + spacing * (row + 1) + random.nextInt(-8, 8);
|
|
206
|
+
}),
|
|
207
|
+
vx: Array.from({ length: players }, () => 0),
|
|
208
|
+
vy: Array.from({ length: players }, () => 0),
|
|
209
|
+
score: Array.from({ length: players }, () => 0),
|
|
210
|
+
collisions: Array.from({ length: players }, () => 0),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
export function fixedArenaStep(state, inputs) {
|
|
214
|
+
const x = [...state.x];
|
|
215
|
+
const y = [...state.y];
|
|
216
|
+
const vx = [...state.vx];
|
|
217
|
+
const vy = [...state.vy];
|
|
218
|
+
const score = [...state.score];
|
|
219
|
+
const collisions = [...state.collisions];
|
|
220
|
+
for (let slot = 0; slot < inputs.length; slot += 1) {
|
|
221
|
+
const input = inputs[slot];
|
|
222
|
+
const impulse = input.dash ? dashImpulse : thrustScale;
|
|
223
|
+
vx[slot] = clamp(vx[slot] + input.thrustX * impulse, -maxVelocity, maxVelocity);
|
|
224
|
+
vy[slot] = clamp(vy[slot] + input.thrustY * impulse, -maxVelocity, maxVelocity);
|
|
225
|
+
x[slot] += vx[slot];
|
|
226
|
+
y[slot] += vy[slot];
|
|
227
|
+
if (x[slot] < -arenaHalfExtent || x[slot] > arenaHalfExtent) {
|
|
228
|
+
x[slot] = clamp(x[slot], -arenaHalfExtent, arenaHalfExtent);
|
|
229
|
+
vx[slot] = -truncDivTowardZero(vx[slot], 2);
|
|
230
|
+
}
|
|
231
|
+
if (y[slot] < -arenaHalfExtent || y[slot] > arenaHalfExtent) {
|
|
232
|
+
y[slot] = clamp(y[slot], -arenaHalfExtent, arenaHalfExtent);
|
|
233
|
+
vy[slot] = -truncDivTowardZero(vy[slot], 2);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
for (let left = 0; left < inputs.length; left += 1) {
|
|
237
|
+
for (let right = left + 1; right < inputs.length; right += 1) {
|
|
238
|
+
if (!circleIntersects(fixedCircle({ x: x[left], y: y[left] }, playerRadius), fixedCircle({ x: x[right], y: y[right] }, playerRadius))) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const axis = chooseResolutionAxis(x[left] - x[right], y[left] - y[right], left, right);
|
|
242
|
+
x[left] += axis.x * playerRadius;
|
|
243
|
+
y[left] += axis.y * playerRadius;
|
|
244
|
+
x[right] -= axis.x * playerRadius;
|
|
245
|
+
y[right] -= axis.y * playerRadius;
|
|
246
|
+
vx[left] = -truncDivTowardZero(vx[left], 2);
|
|
247
|
+
vy[left] = -truncDivTowardZero(vy[left], 2);
|
|
248
|
+
vx[right] = -truncDivTowardZero(vx[right], 2);
|
|
249
|
+
vy[right] = -truncDivTowardZero(vy[right], 2);
|
|
250
|
+
collisions[left] += 1;
|
|
251
|
+
collisions[right] += 1;
|
|
252
|
+
if ((state.frame + left + right) % 2 === 0) {
|
|
253
|
+
score[left] += 1;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
score[right] += 1;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
frame: state.frame + 1,
|
|
262
|
+
x,
|
|
263
|
+
y,
|
|
264
|
+
vx,
|
|
265
|
+
vy,
|
|
266
|
+
score,
|
|
267
|
+
collisions,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function findFixedArenaCollisionPairs(state) {
|
|
271
|
+
const pairs = [];
|
|
272
|
+
for (let left = 0; left < state.x.length; left += 1) {
|
|
273
|
+
for (let right = left + 1; right < state.x.length; right += 1) {
|
|
274
|
+
if (circleIntersects(fixedCircle({ x: state.x[left], y: state.y[left] }, playerRadius), fixedCircle({ x: state.x[right], y: state.y[right] }, playerRadius))) {
|
|
275
|
+
pairs.push({ a: left, b: right });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return pairs;
|
|
280
|
+
}
|
|
281
|
+
function chooseResolutionAxis(dx, dy, left, right) {
|
|
282
|
+
if (absInt(dx) > absInt(dy)) {
|
|
283
|
+
return { x: dx >= 0 ? 1 : -1, y: 0 };
|
|
284
|
+
}
|
|
285
|
+
if (absInt(dy) > 0) {
|
|
286
|
+
return { x: 0, y: dy >= 0 ? 1 : -1 };
|
|
287
|
+
}
|
|
288
|
+
return { x: left < right ? -1 : 1, y: 0 };
|
|
289
|
+
}
|
|
290
|
+
function hydrateFixedArenaInput(recordedInput) {
|
|
291
|
+
if (recordedInput === null ||
|
|
292
|
+
typeof recordedInput !== 'object' ||
|
|
293
|
+
typeof recordedInput.thrustX !== 'number' ||
|
|
294
|
+
typeof recordedInput.thrustY !== 'number' ||
|
|
295
|
+
typeof recordedInput.dash !== 'boolean') {
|
|
296
|
+
throw new Error('Invalid Fixed Arena replay input');
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
thrustX: clampInput(recordedInput.thrustX),
|
|
300
|
+
thrustY: clampInput(recordedInput.thrustY),
|
|
301
|
+
dash: recordedInput.dash,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function clampInput(value) {
|
|
305
|
+
if (!Number.isInteger(value)) {
|
|
306
|
+
throw new Error('Fixed Arena input thrust must be an integer');
|
|
307
|
+
}
|
|
308
|
+
return clamp(value, -1, 1);
|
|
309
|
+
}
|
|
310
|
+
function clamp(value, min, max) {
|
|
311
|
+
if (value < min) {
|
|
312
|
+
return min;
|
|
313
|
+
}
|
|
314
|
+
if (value > max) {
|
|
315
|
+
return max;
|
|
316
|
+
}
|
|
317
|
+
return value;
|
|
318
|
+
}
|
|
319
|
+
function ceilSqrtInt(value) {
|
|
320
|
+
let candidate = 1;
|
|
321
|
+
while (candidate * candidate < value) {
|
|
322
|
+
candidate += 1;
|
|
323
|
+
}
|
|
324
|
+
return candidate;
|
|
325
|
+
}
|
|
326
|
+
function maxInt(left, right) {
|
|
327
|
+
return left >= right ? left : right;
|
|
328
|
+
}
|
|
329
|
+
function minInt(left, right) {
|
|
330
|
+
return left <= right ? left : right;
|
|
331
|
+
}
|
|
332
|
+
function absInt(value) {
|
|
333
|
+
return value < 0 ? -value : value;
|
|
334
|
+
}
|
|
335
|
+
function fixedArenaColor(slot) {
|
|
336
|
+
return slot % 4 === 0 ? '#40d9ff' : slot % 4 === 1 ? '#ff4fd8' : slot % 4 === 2 ? '#7cff6b' : '#ffcc4d';
|
|
337
|
+
}
|
|
338
|
+
function truncDivTowardZero(value, divisor) {
|
|
339
|
+
if (!Number.isInteger(value) || !Number.isInteger(divisor) || divisor <= 0) {
|
|
340
|
+
throw new Error('Integer division requires integer value and positive integer divisor');
|
|
341
|
+
}
|
|
342
|
+
const positive = value >= 0 ? value : -value;
|
|
343
|
+
const quotient = (positive - (positive % divisor)) / divisor;
|
|
344
|
+
return value >= 0 ? quotient : -quotient;
|
|
345
|
+
}
|
|
346
|
+
function validateFixedArenaOptions(options) {
|
|
347
|
+
if (!Number.isInteger(options.players) || options.players < 2 || options.players > 128) {
|
|
348
|
+
throw new Error('Fixed Arena requires 2 to 128 players');
|
|
349
|
+
}
|
|
350
|
+
if (!Number.isInteger(options.frames) || options.frames < 1) {
|
|
351
|
+
throw new Error('Fixed Arena requires at least one frame');
|
|
352
|
+
}
|
|
353
|
+
if (!Number.isInteger(options.seed)) {
|
|
354
|
+
throw new Error('Fixed Arena seed must be an integer');
|
|
355
|
+
}
|
|
356
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export { canonicalize, canonicalStringify, cloneCanonical, decodeCanonicalValue, decodeUtf8, defaultChecksum, encodeUtf8 } from './canonical.js';
|
|
2
|
+
export type { CanonicalValue } from './canonical.js';
|
|
3
|
+
export * from './animation.js';
|
|
4
|
+
export { cookDeterministicBotAsset, createDeterministicBotBlackboard, getBlackboardValue, runDeterministicBotSimulation, selectDeterministicUtility, tickDeterministicBehaviorTree, tickDeterministicBehaviorTreeWithMemory, tickDeterministicHfsm, } from './bot-primitives.js';
|
|
5
|
+
export * from './bot-documents.js';
|
|
6
|
+
export * from './commands.js';
|
|
7
|
+
export * from './config-bundle.js';
|
|
8
|
+
export * from './input-timeline.js';
|
|
9
|
+
export * from './lag-compensation3d.js';
|
|
10
|
+
export type { CookedDeterministicBotAsset, DeterministicBehaviorTreeNode, DeterministicBehaviorTreeStatus, DeterministicBehaviorTreeTick, DeterministicBlackboardEntry, DeterministicBlackboardValue, DeterministicBotAssetSource, DeterministicBotBlackboard, DeterministicBotSimulationMetrics, DeterministicHfsmAgent, DeterministicHfsmDefinition, DeterministicHfsmState, DeterministicHfsmTransition, DeterministicUtilityConsideration, DeterministicUtilitySelection, } from './bot-primitives.js';
|
|
11
|
+
export { assertAssetManifestMatches, cookDeterministicAssets, materializePrototypeEntities, stableAssetCookJson, } from './asset-cooking.js';
|
|
12
|
+
export type { DeterministicAssetCookManifest, DeterministicAssetCookResult, DeterministicAssetDependency, DeterministicAssetRecord, DeterministicAssetSource, DeterministicCookedAsset, DeterministicPrototypeEntity, } from './asset-cooking.js';
|
|
13
|
+
export type { CertificationBudgetResult, CertificationBudgets, CertificationBotReplacementFixture, CertificationBotReplacementMetrics, CertificationEcsLiteFixture, CertificationEcsLiteMetrics, CertificationEventCallbacksFixture, CertificationEventCallbacksMetrics, CertificationExternalPhysicsGateFixture, CertificationExternalPhysicsGateMetrics, CertificationFixture, CertificationFixtureKind, CertificationFixtureReport, CertificationFixtureTier, CertificationFixedFoundationFixture, CertificationFixedFoundationMetrics, CertificationFrameModelMetrics, CertificationGenre128Fixture, CertificationGenre128Metrics, CertificationKccLiteFixture, CertificationKccLiteMetrics, CertificationManifest, CertificationMetrics, CertificationNetcodeFixture, CertificationNetcodeMetrics, CertificationNetcodeShape, CertificationPhysicsBuiltinFixture, CertificationPhysicsBuiltinMetrics, CertificationPredictionCullingFixture, CertificationPredictionCullingMetrics, CertificationProjectileHitscanFixture, CertificationProjectileHitscanMetrics, CertificationProtocolMatrixFixture, CertificationProtocolMatrixMetrics, CertificationProtocolProfileId, CertificationProtocolProfileMetrics, CertificationSchemaToolingFixture, CertificationSchemaToolingMetrics, CertificationStaticAnalysisFixture, CertificationStaticAnalysisMetrics, CertificationDesyncArtifact, CertificationReplayNodeFixture, CertificationReplayNodeMetrics, CertificationResult, CertificationRuntimeTarget, CertificationSuiteReport, CertificationSuiteRun, } from './certification.js';
|
|
14
|
+
export { aabbContainsPoint, aabbIntersects, circleIntersects, circleIntersectsAabb, fixedAabb, fixedCircle, fixedVec2, sortAabbsByStableId, translateAabb, } from './collision.js';
|
|
15
|
+
export type { FixedAabb, FixedCircle, FixedVec2 } from './collision.js';
|
|
16
|
+
export { createStableIndexedMap, deterministicShuffle, stableEntries, stableSortedKeys } from './collections.js';
|
|
17
|
+
export type { StableIndexedEntry } from './collections.js';
|
|
18
|
+
export { combineDeterministicPhysicsMaterial, cookStaticColliders, detectVerticalCcdHit, quantizeFpsAim, stepKccLiteBody, stepTopDownBody, } from './deterministic-primitives.js';
|
|
19
|
+
export type { CookedDeterministicStaticCollider, CookedStaticColliderSet, DeterministicPhysicsMaterial, DeterministicStaticCollider, FpsAimInput, MovementBody, MovementInput, QuantizedFpsAim, } from './deterministic-primitives.js';
|
|
20
|
+
export { createDeterministicEventEffectsAdapter } from './effects-adapter.js';
|
|
21
|
+
export type { DeterministicEffectDispatch, DeterministicEffectTarget, DeterministicEventEffectsAdapter, DeterministicEventEffectsAdapterConfig, DeterministicRenderSafeEffect, } from './effects-adapter.js';
|
|
22
|
+
export { createDeterministicEventTimeline, filterDeterministicEventsForPlayer, runDeterministicEventKeywordFixture } from './events.js';
|
|
23
|
+
export type { DeterministicEventDeliveryScope, DeterministicEventEmitOptions, DeterministicEventKeywordFixtureResult, DeterministicEventPredictionState, DeterministicEventRecord, DeterministicEventTimeline, } from './events.js';
|
|
24
|
+
export { createEcsLiteWorld } from './ecs-lite.js';
|
|
25
|
+
export type { EcsComponentName, EcsEntityId, EcsLiteQueryResult, EcsLiteSnapshot, EcsLiteWorld } from './ecs-lite.js';
|
|
26
|
+
export * from './engine-core-proof.js';
|
|
27
|
+
export * from './engine-complete.js';
|
|
28
|
+
export * from './engine-runtime.js';
|
|
29
|
+
export * from './deterministic-session.js';
|
|
30
|
+
export { deterministicButtonInitialState, runDeterministicButtonFixture, runDeterministicDescriptorButtonFixture, stepDeterministicButton, } from './input-button.js';
|
|
31
|
+
export type { DeterministicButtonFixtureResult, DeterministicButtonState, DeterministicButtonStep, DeterministicDescriptorButtonFixtureResult, } from './input-button.js';
|
|
32
|
+
export { createCounterDuelSession, counterDuelDefaultInput, makeCounterDuelInput, runCounterDuelExample, verifyCounterDuelReplay, } from './examples/counter-duel.js';
|
|
33
|
+
export { createFixedArenaDescriptor, createFixedArenaSession, createInitialFixedArenaState, fixedArenaDefaultInput, fixedArenaStep, makeFixedArenaInput, projectFixedArenaReferenceView, projectFixedArenaRenderState, runFixedArenaExample, verifyFixedArenaReplay, } from './examples/fixed-arena.js';
|
|
34
|
+
export type { CounterDuelInput, CounterDuelOptions, CounterDuelResult, CounterDuelSessionOptions, CounterDuelState, } from './examples/counter-duel.js';
|
|
35
|
+
export type { FixedArenaInput, FixedArenaOptions, FixedArenaReferenceView, FixedArenaRenderState, FixedArenaResult, FixedArenaSessionOptions, FixedArenaState, } from './examples/fixed-arena.js';
|
|
36
|
+
export { SyncplayError } from './errors.js';
|
|
37
|
+
export { createDeterministicMath } from './math.js';
|
|
38
|
+
export { createDeterministicMockNetworkAdapter } from './mock-network-adapter.js';
|
|
39
|
+
export type { DeterministicMockNetworkAdapter, DeterministicMockNetworkReplay } from './mock-network-adapter.js';
|
|
40
|
+
export { runDeterministicMovementCrowdSimulation, rewindDeterministicTargets, runDeterministicMovementSimulation, stepDeterministicFpsAim, stepDeterministicKcc2D, stepDeterministicTopDownMover, validateDeterministicLagCompensatedHit, } from './movement.js';
|
|
41
|
+
export * from './movement3d.js';
|
|
42
|
+
export type { DeterministicFpsAimState, DeterministicFpsInput, DeterministicKccBody, DeterministicKccInput, DeterministicKccStepResult, DeterministicMovementCrowdMetrics, DeterministicMovementSimulationMetrics, DeterministicMovementWorld, DeterministicMovingPlatform, DeterministicOneWayPlatform, DeterministicSlope, DeterministicMovementStep, DeterministicTargetSnapshot, DeterministicTopDownBody, DeterministicTopDownBounds, DeterministicTopDownInput, } from './movement.js';
|
|
43
|
+
export { applyDeterministicReciprocalAvoidance, cookDeterministicNavigationGraph, cookTilemapNavigationGraph, createDeterministicFlowField, findDeterministicPath, smoothDeterministicPath, stepDeterministicNavigationAgents, } from './navigation.js';
|
|
44
|
+
export * from './navmesh.js';
|
|
45
|
+
export * from './engine-parity-matrix.js';
|
|
46
|
+
export type { CookedDeterministicNavigationGraph, DeterministicFlowField, DeterministicNavEdge, DeterministicNavigationAgent, DeterministicNavigationGraphSource, DeterministicNavigationStepOptions, DeterministicNavigationStepResult, DeterministicNavNode, DeterministicNavRegion, DeterministicPathQuery, DeterministicPathResult, DeterministicTilemapSource, } from './navigation.js';
|
|
47
|
+
export { runNumberLab } from './number-lab.js';
|
|
48
|
+
export { createOfflineSession } from './offline-session.js';
|
|
49
|
+
export { createSyncplayOfflineRuntime } from './sdk-offline.js';
|
|
50
|
+
export * from './rollback-history.js';
|
|
51
|
+
export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D, } from './physics2d.js';
|
|
52
|
+
export * from './physics3d.js';
|
|
53
|
+
export type { DeterministicBodyKind, DeterministicContactEvent2D, DeterministicContactEventType, DeterministicPhysicsAabbQuery2D, DeterministicPhysicsBody2D, DeterministicPhysicsConstraint2D, DeterministicPhysicsHit2D, DeterministicPhysicsQueryOptions2D, DeterministicPhysicsRaycastQuery2D, DeterministicPhysicsCircleCastQuery2D, DeterministicPhysicsShape2D, DeterministicPhysicsShapeOverlapQuery2D, DeterministicPhysicsStepOptions2D, DeterministicPhysicsStepResult2D, DeterministicPhysicsWorld2D, } from './physics2d.js';
|
|
54
|
+
export { runPhysicsCert, runRapierPhysicsCert } from './physics-cert.js';
|
|
55
|
+
export { runPredictionCullingLab } from './prediction-culling.js';
|
|
56
|
+
export { createDeterministicR3fDebugOverlay, createDeterministicR3fRenderAdapter, interpolateDeterministicRenderNumber, interpolateDeterministicRenderVec2, } from './r3f-render-adapter.js';
|
|
57
|
+
export { runProtocolRollbackLab } from './protocol-lab.js';
|
|
58
|
+
export { createProtocolRollbackReport, diffDeterministicReports, renderDeterministicReportHtml, stableReportDiffJson, stableReportJson, } from './reports.js';
|
|
59
|
+
export { diffReplayFrames, explainReplayChecksum, inspectReplayFrame, inspectReplayFrameWithDescriptor, stableFrameDiffJson, stableFrameDumpJson, } from './replay-inspect.js';
|
|
60
|
+
export { deterministicSampleSceneIds, runAllDeterministicSampleScenes, runDeterministicIntegratedHeadlessScene, runDeterministicSampleScene, } from './sample-scenes.js';
|
|
61
|
+
export type { DeterministicIntegratedHeadlessSceneResult, DeterministicSampleSceneId, DeterministicSampleSceneResult, } from './sample-scenes.js';
|
|
62
|
+
export { DeterministicRandom } from './random.js';
|
|
63
|
+
export { verifyReplay, verifyReplayWithDescriptor } from './replay.js';
|
|
64
|
+
export * from './replay-scoring.js';
|
|
65
|
+
export { createDeterministicReplayBundle, createDeterministicReplayDebugTables, createDeterministicReplayRuntimeManifest, minimizeDeterministicReplayDesync, minimizeDeterministicReplayDesyncBundle, sliceDeterministicReplayBundle, verifyDeterministicFrameRuntimeReplayBundle, verifyDeterministicReplayBundle, verifyDeterministicSelfContainedFrameRuntimeReplayBundle, } from './replay-bundle.js';
|
|
66
|
+
export type { DeterministicFrameRuntimeReplayBundleOptions, DeterministicFrameRuntimeReplayBundleVerification, DeterministicReplayBundle, DeterministicReplayBundleMismatch, DeterministicReplayBundleVerification, DeterministicReplayDebugTables, DeterministicReplayDesyncMinimization, DeterministicReplayRuntimeBeforeFrameId, DeterministicReplayRuntimeManifest, DeterministicReplayRuntimeManifestSource, DeterministicReplayRuntimeSystemId, DeterministicReplaySemanticModel, DeterministicReplaySemanticOperation, DeterministicReplaySliceOrigin, DeterministicReplaySnapshot, } from './replay-bundle.js';
|
|
67
|
+
export * from './local-authority.js';
|
|
68
|
+
export * from './local-authority-wire.js';
|
|
69
|
+
export * from './input-authority.js';
|
|
70
|
+
export * from './session-wire.js';
|
|
71
|
+
export * from './session-snapshot.js';
|
|
72
|
+
export * from './networked-client.js';
|
|
73
|
+
export * from './authority-room.js';
|
|
74
|
+
export * from './multiclient-authority.js';
|
|
75
|
+
export { withRuntimeDeterminismGuards } from './runtime-guards.js';
|
|
76
|
+
export { createLabSession, hydrateLabInput, labDefaultInput, labStep } from './sample-lab.js';
|
|
77
|
+
export { createSchemaArtifacts } from './schema-artifacts.js';
|
|
78
|
+
export { createDeterministicDescriptorBinarySchema, generateSchemaBinarySource, generateSchemaTypes, proveSchemaBinaryRoundTrip, } from './schema-codegen.js';
|
|
79
|
+
export { defineSyncplayGame, stableDescriptorId } from './schema.js';
|
|
80
|
+
export * from './schema-dsl-binary.js';
|
|
81
|
+
export * from './signals.js';
|
|
82
|
+
export * from './sparse-set.js';
|
|
83
|
+
export * from './system-lifecycle.js';
|
|
84
|
+
export { createCooldown, framesToMilliseconds, millisecondsToFrames, popDueFrameEvents, scheduleFrameEvent, tickCooldown, } from './timers.js';
|
|
85
|
+
export { runVenusMpBudgetLab } from './venus-mp-budget.js';
|
|
86
|
+
export type { DeterministicCooldown, ScheduledFrameEvent } from './timers.js';
|
|
87
|
+
export type { DeterministicSchemaArtifacts, SchemaFrameDump } from './schema-artifacts.js';
|
|
88
|
+
export type { DeterministicOfflineFrame, DeterministicOfflineRuntime, DeterministicOfflineRuntimeConfig, } from './sdk-offline.js';
|
|
89
|
+
export type { SchemaBinaryProof, SchemaBinarySourceOptions, SchemaTypesOptions } from './schema-codegen.js';
|
|
90
|
+
export type { AssetDescriptor, CollectionOrdering, CommandChannelDescriptor, ConfigDescriptor, DeterministicGameDescriptor, EventDescriptor, InputFieldDescriptor, InputFieldPolicy, NumericMode, SchemaFieldDescriptor, SchemaFieldKind, StateFieldDescriptor, SystemDescriptor, PrototypeDescriptor, ValidatedDeterministicGameDescriptor, } from './schema.js';
|
|
91
|
+
export type { LabInput, LabSessionOptions, LabState } from './sample-lab.js';
|
|
92
|
+
export type { ProtocolRollbackEventPayload, ProtocolRollbackEventTimeline, ProtocolPredictionPolicy, ProtocolRollbackGame, ProtocolRollbackLabOptions, ProtocolRollbackLabResult, } from './protocol-lab.js';
|
|
93
|
+
export type { DeterministicReport, DeterministicAssetCookCliFileResult, DeterministicAssetCookCliReport, DeterministicReportDiff, DeterministicReportDiffEntry, ProtocolRollbackReport, ReplayVerifyCliFileResult, ReplayVerifyCliReport, StaticDeterminismReport, } from './reports.js';
|
|
94
|
+
export type { PredictionCullingMode, PredictionCullingOptions, PredictionCullingResult } from './prediction-culling.js';
|
|
95
|
+
export type { DeterministicR3fRenderAdapter, DeterministicR3fRenderAdapterConfig, DeterministicR3fRenderDebug, DeterministicR3fRenderFrame, DeterministicRenderReadonly, } from './r3f-render-adapter.js';
|
|
96
|
+
export type { DescriptorReplayInspectConfig, ReplayDiffResult, ReplayFrameDiff, ReplayFrameDump, ReplayInspectConfig, ReplayChecksumExplanation, ReplayChecksumTableExplanation, } from './replay-inspect.js';
|
|
97
|
+
export type { DescriptorReplayVerificationConfig, ReplayVerificationConfig } from './replay.js';
|
|
98
|
+
export type { NumberLabMode, NumberLabOptions, NumberLabResult } from './number-lab.js';
|
|
99
|
+
export type { PhysicsCertEngine, PhysicsCertOptions, PhysicsCertResult, PhysicsCertShape } from './physics-cert.js';
|
|
100
|
+
export type { VenusMpBudgetOptions, VenusMpBudgetResult } from './venus-mp-budget.js';
|
|
101
|
+
export type { DeterministicSessionConfig, DeterministicStep, DeterministicStepContext, FrameSnapshot, OfflineSession, RecordedChecksum, RecordedCommandFrame, RecordedDebugEvent, RecordedInputFrame, ReplayFile, ReplayRecordingMode, ReplayVerificationMismatch, ReplayVerificationResult, TickRate, } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export { canonicalize, canonicalStringify, cloneCanonical, decodeCanonicalValue, decodeUtf8, defaultChecksum, encodeUtf8 } from './canonical.js';
|
|
2
|
+
export * from './animation.js';
|
|
3
|
+
export { cookDeterministicBotAsset, createDeterministicBotBlackboard, getBlackboardValue, runDeterministicBotSimulation, selectDeterministicUtility, tickDeterministicBehaviorTree, tickDeterministicBehaviorTreeWithMemory, tickDeterministicHfsm, } from './bot-primitives.js';
|
|
4
|
+
export * from './bot-documents.js';
|
|
5
|
+
export * from './commands.js';
|
|
6
|
+
export * from './config-bundle.js';
|
|
7
|
+
export * from './input-timeline.js';
|
|
8
|
+
export * from './lag-compensation3d.js';
|
|
9
|
+
export { assertAssetManifestMatches, cookDeterministicAssets, materializePrototypeEntities, stableAssetCookJson, } from './asset-cooking.js';
|
|
10
|
+
export { aabbContainsPoint, aabbIntersects, circleIntersects, circleIntersectsAabb, fixedAabb, fixedCircle, fixedVec2, sortAabbsByStableId, translateAabb, } from './collision.js';
|
|
11
|
+
export { createStableIndexedMap, deterministicShuffle, stableEntries, stableSortedKeys } from './collections.js';
|
|
12
|
+
export { combineDeterministicPhysicsMaterial, cookStaticColliders, detectVerticalCcdHit, quantizeFpsAim, stepKccLiteBody, stepTopDownBody, } from './deterministic-primitives.js';
|
|
13
|
+
export { createDeterministicEventEffectsAdapter } from './effects-adapter.js';
|
|
14
|
+
export { createDeterministicEventTimeline, filterDeterministicEventsForPlayer, runDeterministicEventKeywordFixture } from './events.js';
|
|
15
|
+
export { createEcsLiteWorld } from './ecs-lite.js';
|
|
16
|
+
export * from './engine-core-proof.js';
|
|
17
|
+
export * from './engine-complete.js';
|
|
18
|
+
export * from './engine-runtime.js';
|
|
19
|
+
export * from './deterministic-session.js';
|
|
20
|
+
export { deterministicButtonInitialState, runDeterministicButtonFixture, runDeterministicDescriptorButtonFixture, stepDeterministicButton, } from './input-button.js';
|
|
21
|
+
export { createCounterDuelSession, counterDuelDefaultInput, makeCounterDuelInput, runCounterDuelExample, verifyCounterDuelReplay, } from './examples/counter-duel.js';
|
|
22
|
+
export { createFixedArenaDescriptor, createFixedArenaSession, createInitialFixedArenaState, fixedArenaDefaultInput, fixedArenaStep, makeFixedArenaInput, projectFixedArenaReferenceView, projectFixedArenaRenderState, runFixedArenaExample, verifyFixedArenaReplay, } from './examples/fixed-arena.js';
|
|
23
|
+
export { SyncplayError } from './errors.js';
|
|
24
|
+
export { createDeterministicMath } from './math.js';
|
|
25
|
+
export { createDeterministicMockNetworkAdapter } from './mock-network-adapter.js';
|
|
26
|
+
export { runDeterministicMovementCrowdSimulation, rewindDeterministicTargets, runDeterministicMovementSimulation, stepDeterministicFpsAim, stepDeterministicKcc2D, stepDeterministicTopDownMover, validateDeterministicLagCompensatedHit, } from './movement.js';
|
|
27
|
+
export * from './movement3d.js';
|
|
28
|
+
export { applyDeterministicReciprocalAvoidance, cookDeterministicNavigationGraph, cookTilemapNavigationGraph, createDeterministicFlowField, findDeterministicPath, smoothDeterministicPath, stepDeterministicNavigationAgents, } from './navigation.js';
|
|
29
|
+
export * from './navmesh.js';
|
|
30
|
+
export * from './engine-parity-matrix.js';
|
|
31
|
+
export { runNumberLab } from './number-lab.js';
|
|
32
|
+
export { createOfflineSession } from './offline-session.js';
|
|
33
|
+
export { createSyncplayOfflineRuntime } from './sdk-offline.js';
|
|
34
|
+
export * from './rollback-history.js';
|
|
35
|
+
export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D, } from './physics2d.js';
|
|
36
|
+
export * from './physics3d.js';
|
|
37
|
+
export { runPhysicsCert, runRapierPhysicsCert } from './physics-cert.js';
|
|
38
|
+
export { runPredictionCullingLab } from './prediction-culling.js';
|
|
39
|
+
export { createDeterministicR3fDebugOverlay, createDeterministicR3fRenderAdapter, interpolateDeterministicRenderNumber, interpolateDeterministicRenderVec2, } from './r3f-render-adapter.js';
|
|
40
|
+
export { runProtocolRollbackLab } from './protocol-lab.js';
|
|
41
|
+
export { createProtocolRollbackReport, diffDeterministicReports, renderDeterministicReportHtml, stableReportDiffJson, stableReportJson, } from './reports.js';
|
|
42
|
+
export { diffReplayFrames, explainReplayChecksum, inspectReplayFrame, inspectReplayFrameWithDescriptor, stableFrameDiffJson, stableFrameDumpJson, } from './replay-inspect.js';
|
|
43
|
+
export { deterministicSampleSceneIds, runAllDeterministicSampleScenes, runDeterministicIntegratedHeadlessScene, runDeterministicSampleScene, } from './sample-scenes.js';
|
|
44
|
+
export { DeterministicRandom } from './random.js';
|
|
45
|
+
export { verifyReplay, verifyReplayWithDescriptor } from './replay.js';
|
|
46
|
+
export * from './replay-scoring.js';
|
|
47
|
+
export { createDeterministicReplayBundle, createDeterministicReplayDebugTables, createDeterministicReplayRuntimeManifest, minimizeDeterministicReplayDesync, minimizeDeterministicReplayDesyncBundle, sliceDeterministicReplayBundle, verifyDeterministicFrameRuntimeReplayBundle, verifyDeterministicReplayBundle, verifyDeterministicSelfContainedFrameRuntimeReplayBundle, } from './replay-bundle.js';
|
|
48
|
+
export * from './local-authority.js';
|
|
49
|
+
export * from './local-authority-wire.js';
|
|
50
|
+
export * from './input-authority.js';
|
|
51
|
+
export * from './session-wire.js';
|
|
52
|
+
export * from './session-snapshot.js';
|
|
53
|
+
export * from './networked-client.js';
|
|
54
|
+
export * from './authority-room.js';
|
|
55
|
+
export * from './multiclient-authority.js';
|
|
56
|
+
export { withRuntimeDeterminismGuards } from './runtime-guards.js';
|
|
57
|
+
export { createLabSession, hydrateLabInput, labDefaultInput, labStep } from './sample-lab.js';
|
|
58
|
+
export { createSchemaArtifacts } from './schema-artifacts.js';
|
|
59
|
+
export { createDeterministicDescriptorBinarySchema, generateSchemaBinarySource, generateSchemaTypes, proveSchemaBinaryRoundTrip, } from './schema-codegen.js';
|
|
60
|
+
export { defineSyncplayGame, stableDescriptorId } from './schema.js';
|
|
61
|
+
export * from './schema-dsl-binary.js';
|
|
62
|
+
export * from './signals.js';
|
|
63
|
+
export * from './sparse-set.js';
|
|
64
|
+
export * from './system-lifecycle.js';
|
|
65
|
+
export { createCooldown, framesToMilliseconds, millisecondsToFrames, popDueFrameEvents, scheduleFrameEvent, tickCooldown, } from './timers.js';
|
|
66
|
+
export { runVenusMpBudgetLab } from './venus-mp-budget.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type CanonicalValue } from './canonical.js';
|
|
2
|
+
/**
|
|
3
|
+
* Game-agnostic input authority (server orders inputs, clients simulate — §2 Option 1).
|
|
4
|
+
*
|
|
5
|
+
* It orders per-slot inputs by tick and confirms each tick exactly once, in order,
|
|
6
|
+
* either when every player slot has reported or when a HardTolerance deadline elapses
|
|
7
|
+
* (substituting the slot's last confirmed input). It never runs the simulation — only
|
|
8
|
+
* inputs cross the wire and clients run the deterministic sim locally.
|
|
9
|
+
*
|
|
10
|
+
* This module is the reusable core: the M0 spike hosts it over sockets, and M2 lifts it
|
|
11
|
+
* into the mp-room-server room type unchanged.
|
|
12
|
+
*/
|
|
13
|
+
/** Opaque, engine-defined input payload. The authority never interprets it. */
|
|
14
|
+
export type EncodedInput = CanonicalValue;
|
|
15
|
+
export interface ConfirmedInputFrame {
|
|
16
|
+
readonly tick: number;
|
|
17
|
+
/** One entry per player slot, in slot order. */
|
|
18
|
+
readonly inputs: readonly EncodedInput[];
|
|
19
|
+
/** Slots whose input was substituted (repeated) because it never arrived in time. */
|
|
20
|
+
readonly substitutedSlots: readonly number[];
|
|
21
|
+
readonly checksum: string;
|
|
22
|
+
}
|
|
23
|
+
export interface InputAuthorityConfig {
|
|
24
|
+
readonly playerCount: number;
|
|
25
|
+
/** Confirm a tick once `now - tick >= hardToleranceTicks`, substituting any missing input. */
|
|
26
|
+
readonly hardToleranceTicks: number;
|
|
27
|
+
/** Substitute this when a slot has no prior confirmed input to repeat. */
|
|
28
|
+
readonly neutralInput: EncodedInput;
|
|
29
|
+
/** Reject inputs whose tick is further ahead than `confirmedThrough + maxFutureTicks`. */
|
|
30
|
+
readonly maxFutureTicks?: number;
|
|
31
|
+
/** Confirmed frames retained for the redundant broadcast window. */
|
|
32
|
+
readonly retainedConfirmedFrames?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Rebuild a previously-persisted authority (M6.5 crash recovery): the
|
|
35
|
+
* retained frames, frontier, and per-slot last-confirmed inputs (derived
|
|
36
|
+
* from the final frame — confirmed inputs equal the post-substitution
|
|
37
|
+
* last-confirmed values by construction). Fail-loud on gaps or a frontier
|
|
38
|
+
* mismatch.
|
|
39
|
+
*/
|
|
40
|
+
readonly restoreFrom?: InputAuthorityRestoreState;
|
|
41
|
+
}
|
|
42
|
+
export interface InputAuthorityRestoreState {
|
|
43
|
+
readonly confirmedThrough: number;
|
|
44
|
+
readonly frames: readonly ConfirmedInputFrame[];
|
|
45
|
+
}
|
|
46
|
+
export type InputRejectionReason = 'slot-out-of-range' | 'invalid-tick' | 'tick-already-confirmed' | 'tick-too-far-ahead';
|
|
47
|
+
export interface InputSubmissionResult {
|
|
48
|
+
readonly accepted: boolean;
|
|
49
|
+
readonly reason?: InputRejectionReason;
|
|
50
|
+
}
|
|
51
|
+
export interface InputAuthorityStats {
|
|
52
|
+
readonly confirmedTicks: number;
|
|
53
|
+
readonly substitutedInputs: number;
|
|
54
|
+
readonly droppedLateInputs: number;
|
|
55
|
+
readonly rejectedFutureInputs: number;
|
|
56
|
+
}
|
|
57
|
+
export interface InputAuthority {
|
|
58
|
+
readonly playerCount: number;
|
|
59
|
+
/** Highest confirmed tick, or -1 when nothing is confirmed yet. */
|
|
60
|
+
readonly confirmedThrough: number;
|
|
61
|
+
/** Lowest confirmed tick still retained, or -1 when nothing is confirmed yet. */
|
|
62
|
+
readonly earliestRetainedTick: number;
|
|
63
|
+
receiveInput(slot: number, tick: number, input: EncodedInput): InputSubmissionResult;
|
|
64
|
+
/** Confirm every tick that can be confirmed up to and including `now`. Returns the new frames in order. */
|
|
65
|
+
advance(now: number): readonly ConfirmedInputFrame[];
|
|
66
|
+
/** A confirmed frame still inside the retained window, for redundant re-broadcast. */
|
|
67
|
+
confirmedFrame(tick: number): ConfirmedInputFrame | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* The retained frames `from..to` inclusive, for persistence (M6.5). Throws
|
|
70
|
+
* if any tick in the range is no longer retained — a persisted record with
|
|
71
|
+
* a silent gap would strand every restored client.
|
|
72
|
+
*/
|
|
73
|
+
confirmedFrameRange(from: number, to: number): readonly ConfirmedInputFrame[];
|
|
74
|
+
/**
|
|
75
|
+
* Drop retained frames below `tick` (M5.5: history before a validated
|
|
76
|
+
* snapshot is no longer needed for late-join catch-up). Never moves the
|
|
77
|
+
* frontier; a no-op at or below the current earliest retained tick.
|
|
78
|
+
*/
|
|
79
|
+
pruneConfirmedBefore(tick: number): void;
|
|
80
|
+
stats(): InputAuthorityStats;
|
|
81
|
+
}
|
|
82
|
+
export declare function createInputAuthority(config: InputAuthorityConfig): InputAuthority;
|