@series-inc/rundot-syncplay 5.23.0 → 5.24.0-beta.1
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 +53 -12
- package/dist/authority-room.d.ts +11 -0
- package/dist/authority-room.js +43 -0
- package/dist/browser.d.ts +11 -1
- package/dist/browser.js +5 -0
- package/dist/checksum-basis.d.ts +4 -0
- package/dist/checksum-basis.js +17 -0
- package/dist/cjs/authority-room.js +43 -0
- package/dist/cjs/browser.js +18 -2
- package/dist/cjs/checksum-basis.js +22 -0
- package/dist/cjs/collider-cooking.js +834 -0
- package/dist/cjs/deterministic-session.js +2 -0
- package/dist/cjs/engine-complete.js +1 -4
- package/dist/cjs/engine-parity-matrix.js +41 -9
- package/dist/cjs/engine-parity.js +2 -2
- package/dist/cjs/index.js +24 -2
- package/dist/cjs/input-authority.js +34 -2
- package/dist/cjs/instant-replay.js +4 -9
- package/dist/cjs/local-authority-server-runner.js +3 -1
- package/dist/cjs/local-authority-wire.js +20 -1
- package/dist/cjs/match-log.js +27 -0
- package/dist/cjs/movement3d.js +17 -1
- package/dist/cjs/networked-client.js +70 -20
- package/dist/cjs/node.js +23 -1
- package/dist/cjs/offline-session.js +52 -24
- package/dist/cjs/physics-cert.js +8 -3
- package/dist/cjs/physics3d-heightfield.js +51 -0
- package/dist/cjs/physics3d-hull.js +381 -0
- package/dist/cjs/physics3d-joints.js +255 -49
- package/dist/cjs/physics3d-mass.js +131 -0
- package/dist/cjs/physics3d-recipes.js +197 -0
- package/dist/cjs/physics3d-shared.js +11 -1
- package/dist/cjs/physics3d-solver.js +845 -98
- package/dist/cjs/physics3d-stacking.js +154 -0
- package/dist/cjs/physics3d-toi.js +274 -0
- package/dist/cjs/physics3d-vehicle.js +35 -10
- package/dist/cjs/physics3d.js +601 -38
- package/dist/cjs/replay.js +25 -1
- package/dist/cjs/runtime-support.js +71 -0
- package/dist/cjs/sdk-offline.js +9 -1
- package/dist/cjs/sdk-session.js +8 -0
- package/dist/cjs/secret-authority.js +647 -0
- package/dist/cjs/secret-client.js +615 -0
- package/dist/cjs/secret-command-classification.js +8 -0
- package/dist/cjs/secret-config.js +211 -0
- package/dist/cjs/secret-crypto.js +208 -0
- package/dist/cjs/secret-protocol.js +238 -0
- package/dist/cjs/session-build.js +7 -4
- package/dist/cjs/session-promotion.js +5 -5
- package/dist/cjs/synctest.js +49 -0
- package/dist/cjs/testing.js +4 -1
- package/dist/cjs/tools/static-checker.js +61 -13
- package/dist/cli.js +193 -48
- package/dist/collider-cooking.d.ts +141 -0
- package/dist/collider-cooking.js +829 -0
- package/dist/deterministic-primitives.d.ts +1 -0
- package/dist/deterministic-session.js +2 -0
- package/dist/engine-complete.js +1 -4
- package/dist/engine-parity-matrix.js +41 -9
- package/dist/engine-parity.js +2 -2
- package/dist/index.d.ts +12 -1
- package/dist/index.js +6 -0
- package/dist/input-authority.d.ts +3 -0
- package/dist/input-authority.js +35 -3
- package/dist/instant-replay.d.ts +2 -2
- package/dist/instant-replay.js +4 -9
- package/dist/local-authority-server-runner.d.ts +1 -0
- package/dist/local-authority-server-runner.js +2 -1
- package/dist/local-authority-wire.js +20 -1
- package/dist/match-log.d.ts +4 -1
- package/dist/match-log.js +27 -0
- package/dist/movement3d.js +18 -2
- package/dist/networked-client.d.ts +8 -0
- package/dist/networked-client.js +70 -20
- package/dist/node.d.ts +7 -0
- package/dist/node.js +6 -0
- package/dist/offline-session.js +53 -25
- package/dist/physics-cert.js +8 -3
- package/dist/physics3d-heightfield.d.ts +17 -0
- package/dist/physics3d-heightfield.js +47 -0
- package/dist/physics3d-hull.d.ts +53 -0
- package/dist/physics3d-hull.js +376 -0
- package/dist/physics3d-joints.d.ts +13 -1
- package/dist/physics3d-joints.js +256 -50
- package/dist/physics3d-mass.d.ts +11 -0
- package/dist/physics3d-mass.js +126 -0
- package/dist/physics3d-recipes.d.ts +29 -0
- package/dist/physics3d-recipes.js +193 -0
- package/dist/physics3d-shared.d.ts +1 -0
- package/dist/physics3d-shared.js +10 -1
- package/dist/physics3d-solver.d.ts +78 -2
- package/dist/physics3d-solver.js +842 -98
- package/dist/physics3d-stacking.d.ts +26 -0
- package/dist/physics3d-stacking.js +147 -0
- package/dist/physics3d-toi.d.ts +79 -0
- package/dist/physics3d-toi.js +267 -0
- package/dist/physics3d-vehicle.js +35 -10
- package/dist/physics3d.d.ts +28 -3
- package/dist/physics3d.js +601 -40
- package/dist/replay.d.ts +5 -0
- package/dist/replay.js +25 -1
- package/dist/reports.d.ts +6 -1
- package/dist/runtime-support.d.ts +60 -0
- package/dist/runtime-support.js +66 -0
- package/dist/sdk-offline.d.ts +4 -0
- package/dist/sdk-offline.js +9 -1
- package/dist/sdk-session.d.ts +4 -0
- package/dist/sdk-session.js +8 -0
- package/dist/secret-authority.d.ts +90 -0
- package/dist/secret-authority.js +643 -0
- package/dist/secret-client.d.ts +89 -0
- package/dist/secret-client.js +611 -0
- package/dist/secret-command-classification.d.ts +1 -0
- package/dist/secret-command-classification.js +5 -0
- package/dist/secret-config.d.ts +45 -0
- package/dist/secret-config.js +206 -0
- package/dist/secret-crypto.d.ts +29 -0
- package/dist/secret-crypto.js +190 -0
- package/dist/secret-protocol.d.ts +181 -0
- package/dist/secret-protocol.js +230 -0
- package/dist/session-build.d.ts +16 -0
- package/dist/session-build.js +7 -4
- package/dist/session-promotion.js +5 -5
- package/dist/synctest.d.ts +1 -1
- package/dist/synctest.js +49 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +1 -0
- package/dist/tools/static-checker.d.ts +11 -0
- package/dist/tools/static-checker.js +60 -13
- package/dist/types.d.ts +21 -2
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -8,18 +8,49 @@ rollback — with no game-specific server logic.
|
|
|
8
8
|
This package is intentionally standalone. It does not depend on Venus MP,
|
|
9
9
|
Cloud Run, React Three Fiber, Rapier, Firebase, or the SDK bridge.
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
## Public and private projections
|
|
12
|
+
|
|
13
|
+
The deterministic session remains a shared public projection: every client
|
|
14
|
+
applies the same confirmed inputs and authority commands. Optional secret
|
|
15
|
+
systems add a separate authority-owned projection for simultaneous choices,
|
|
16
|
+
cryptographic random draws, decks, bags, and roles. Private values go only to
|
|
17
|
+
their owners; signed public consequences join exactly one confirmed frame and
|
|
18
|
+
therefore survive rollback, replay, reconnect, and match-log verification.
|
|
19
|
+
|
|
20
|
+
The RUN room authority is trusted and can see secret values. SHA-256
|
|
21
|
+
commitments, hash chains, Merkle proofs, and Ed25519 receipts prevent peers or
|
|
22
|
+
tampered logs from forging outcomes; they do not hide a player's own values
|
|
23
|
+
from that player's browser or make the authority zero-knowledge. Public donated
|
|
24
|
+
snapshots remain secret-free, while server-only secret state restores against a
|
|
25
|
+
cryptographic configuration hash.
|
|
26
|
+
|
|
27
|
+
## BETA Status
|
|
28
|
+
|
|
29
|
+
The standalone engine, certification CLI, curated public entrypoints, SDK bridge,
|
|
30
|
+
generic input authority, replay verification, and hosted multiplayer path are in
|
|
31
|
+
active development. Syncplay has not gone live. Package publication, production
|
|
32
|
+
rollout, and 1.0 graduation are separate release decisions; this build remains
|
|
33
|
+
explicitly BETA.
|
|
34
|
+
|
|
35
|
+
## State Ownership Contract
|
|
36
|
+
|
|
37
|
+
`step` must not mutate the state object it receives or anything reachable from
|
|
38
|
+
it. Copy every array, map, set, typed array, or object before modifying it.
|
|
39
|
+
Sharing unmodified structure with the prior state is expected, and returning the
|
|
40
|
+
same state unchanged is legal.
|
|
41
|
+
|
|
42
|
+
Syncplay retains each returned state object in the snapshot ring by reference
|
|
43
|
+
and hands it back on rollback without copying. This makes snapshot and restore
|
|
44
|
+
O(1); violating the contract corrupts rollback history. Run
|
|
45
|
+
`runSyncplaySynctest` in CI: its `aliasing`, `hydration`, and `codec` phases catch
|
|
46
|
+
mutation, normalization-dependent behavior, and lossy codecs.
|
|
47
|
+
|
|
48
|
+
For large, slowly changing state, keep an incremental running hash in immutable
|
|
49
|
+
state and expose it through `config.checksum`. Recorded checksums can then be
|
|
50
|
+
O(1) and interval-gated. Wire snapshots still serialize and checksum full state
|
|
51
|
+
on demand; never treat `config.checksum` as the late-join checksum. See
|
|
52
|
+
[`docs/syncplay-state-contract.md`](../../docs/syncplay-state-contract.md) for
|
|
53
|
+
the worked pattern and validation-latency tradeoff.
|
|
23
54
|
|
|
24
55
|
## Testing
|
|
25
56
|
|
|
@@ -537,6 +568,16 @@ remediation text. Valid suppressions must use
|
|
|
537
568
|
`// rdm-ignore-next-line <rule-id>: <reason>` and are reported with file, line,
|
|
538
569
|
rule, and reason; invalid suppressions are errors.
|
|
539
570
|
|
|
571
|
+
`static:determinism` with explicit paths (`--entry`, `--paths`) follows the
|
|
572
|
+
import graph transitively by default (`--no-follow-imports` opts out); the
|
|
573
|
+
JSON artifact records `requestedPaths` (CLI inputs), `checkedPaths` (files
|
|
574
|
+
actually checked, post-traversal), `followImports`, and `unresolvedImports`
|
|
575
|
+
(relative imports that resolve to no file — unchecked holes). Bare package
|
|
576
|
+
specifiers are never followed; if your simulation uses tsconfig path aliases,
|
|
577
|
+
pass `--project tsconfig.json` so aliased imports resolve and get checked —
|
|
578
|
+
without it they are skipped like package imports. Package self-certification
|
|
579
|
+
(no explicit paths) checks its curated source list without following imports.
|
|
580
|
+
|
|
540
581
|
The checker covers host random/time, timers, async simulation work, I/O,
|
|
541
582
|
unordered object iteration, random sort comparators, render-object mutation,
|
|
542
583
|
uncertified physics stepping, and descriptor-declared state-field access.
|
package/dist/authority-room.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type ConfirmedInputFrame, type EncodedInput } from './input-authority.js';
|
|
2
2
|
import type { SyncplayMatchLog } from './match-log.js';
|
|
3
|
+
import { type SyncplaySecretAuthoritySnapshot } from './secret-authority.js';
|
|
4
|
+
import type { SyncplaySecretAuthorityCrypto } from './secret-crypto.js';
|
|
5
|
+
import type { SyncplaySecretSystemsConfig } from './secret-config.js';
|
|
3
6
|
/**
|
|
4
7
|
* Transport-agnostic, game-agnostic deterministic authority room (§6 M2).
|
|
5
8
|
*
|
|
@@ -89,6 +92,10 @@ export interface DeterministicAuthorityRoomConfig {
|
|
|
89
92
|
* MUST call `resyncSeatedPlayers()` so surviving clients re-seat.
|
|
90
93
|
*/
|
|
91
94
|
readonly restoreFrom?: DeterministicAuthorityRoomRestoreState;
|
|
95
|
+
readonly authorityId?: string;
|
|
96
|
+
readonly secrets?: SyncplaySecretSystemsConfig;
|
|
97
|
+
readonly secretCrypto?: SyncplaySecretAuthorityCrypto;
|
|
98
|
+
readonly secretMsgType?: string;
|
|
92
99
|
}
|
|
93
100
|
export interface DeterministicAuthorityRoomRestoreState {
|
|
94
101
|
readonly now: number;
|
|
@@ -98,6 +105,7 @@ export interface DeterministicAuthorityRoomRestoreState {
|
|
|
98
105
|
readonly tail: readonly ConfirmedInputFrame[];
|
|
99
106
|
readonly snapshotBytes?: string;
|
|
100
107
|
readonly schemaHash?: string;
|
|
108
|
+
readonly secretState?: SyncplaySecretAuthoritySnapshot;
|
|
101
109
|
}
|
|
102
110
|
export interface AuthorityRoomCatchUpEvent {
|
|
103
111
|
readonly playerId: string;
|
|
@@ -178,6 +186,7 @@ export interface DeterministicAuthorityRoom {
|
|
|
178
186
|
/** Seat a read-only spectator (G11): role 'spectator', no slot, full catch-up + live follow. */
|
|
179
187
|
onSpectatorJoin(playerId: string): boolean;
|
|
180
188
|
onPlayerMessage(playerId: string, envelope: string): void;
|
|
189
|
+
onPlayerSecretMessage(playerId: string, envelope: string): void;
|
|
181
190
|
onPlayerLeave(playerId: string): void;
|
|
182
191
|
/**
|
|
183
192
|
* A player's transport re-attached inside the host's grace window (socket
|
|
@@ -213,5 +222,7 @@ export interface DeterministicAuthorityRoom {
|
|
|
213
222
|
* `preserveFullHistory`, or the match outlived `historyRetentionTicks`).
|
|
214
223
|
*/
|
|
215
224
|
matchLog(): SyncplayMatchLog | undefined;
|
|
225
|
+
/** Server-only secret state. Never include this value in public snapshots or match logs. */
|
|
226
|
+
secretSnapshot(): SyncplaySecretAuthoritySnapshot | undefined;
|
|
216
227
|
}
|
|
217
228
|
export declare function createDeterministicAuthorityRoom(transport: AuthorityRoomTransport, config: DeterministicAuthorityRoomConfig): DeterministicAuthorityRoom;
|
package/dist/authority-room.js
CHANGED
|
@@ -4,7 +4,9 @@ import { createInputAuthority } from './input-authority.js';
|
|
|
4
4
|
import { createDeterministicLocalAuthoritySnapshotChunkEnvelope, createDeterministicLocalAuthoritySnapshotCompleteEnvelope, createDeterministicLocalAuthoritySnapshotOfferEnvelope, createDeterministicLocalAuthoritySnapshotRequestEnvelope, decodeDeterministicLocalAuthorityWireEnvelope, } from './local-authority-wire.js';
|
|
5
5
|
import { DEFAULT_SNAPSHOT_CHUNK_SIZE, createSnapshotTransferCollector, createSnapshotTransferFromBytes, decodeDeterministicStateSnapshot, deterministicRoomDescriptorHash, } from './session-snapshot.js';
|
|
6
6
|
import { decodeDeterministicSessionMessage, encodeDeterministicSessionMessage } from './session-wire.js';
|
|
7
|
+
import { createSyncplaySecretAuthority, } from './secret-authority.js';
|
|
7
8
|
const DEFAULT_SESSION_MSG_TYPE = 'rdm';
|
|
9
|
+
const DEFAULT_SECRET_MSG_TYPE = 'rdm-secret';
|
|
8
10
|
/** One hour at 60 Hz — see DeterministicAuthorityRoomConfig.historyRetentionTicks. */
|
|
9
11
|
const DEFAULT_HISTORY_RETENTION_TICKS = 216_000;
|
|
10
12
|
/** Frames per history-replay unicast. Bounds message size during catch-up. */
|
|
@@ -48,6 +50,18 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
48
50
|
// colliding with a still-present player and orphaning the vacated slot.
|
|
49
51
|
let assignedSlots = 0;
|
|
50
52
|
let now = -1;
|
|
53
|
+
const secretAuthority = config.secrets === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: createSyncplaySecretAuthority({
|
|
56
|
+
authorityId: requireSecretAuthorityId(config.authorityId),
|
|
57
|
+
playerCount: config.playerCount,
|
|
58
|
+
config: config.secrets,
|
|
59
|
+
crypto: requireSecretCrypto(config.secretCrypto),
|
|
60
|
+
sendPrivate(playerId, encoded) {
|
|
61
|
+
transport.sendTo(playerId, config.secretMsgType ?? DEFAULT_SECRET_MSG_TYPE, encoded);
|
|
62
|
+
},
|
|
63
|
+
...(config.restoreFrom?.secretState === undefined ? {} : { restoreFrom: config.restoreFrom.secretState }),
|
|
64
|
+
});
|
|
51
65
|
// M5.5 snapshot acquisition state. The authority stores at most one validated
|
|
52
66
|
// snapshot and runs at most one donor transfer at a time.
|
|
53
67
|
const snapshotCadenceTicks = config.snapshotCadenceTicks ?? DEFAULT_SNAPSHOT_CADENCE_TICKS;
|
|
@@ -227,6 +241,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
227
241
|
const plan = catchUpPlan();
|
|
228
242
|
invariant(plan !== 'unavailable', 'a restored room must have a catch-up plan for seated players', 'authority-room.restore-catchup');
|
|
229
243
|
sendSessionStart(playerId, slot);
|
|
244
|
+
secretAuthority?.onPlayerReconnected(playerId, slot);
|
|
230
245
|
sendCatchUpTo(playerId, plan, 'resync');
|
|
231
246
|
}
|
|
232
247
|
}
|
|
@@ -246,6 +261,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
246
261
|
}
|
|
247
262
|
slotByPlayer.set(playerId, formerSlot);
|
|
248
263
|
sendSessionStart(playerId, formerSlot);
|
|
264
|
+
secretAuthority?.onPlayerReconnected(playerId, formerSlot);
|
|
249
265
|
sendCatchUpTo(playerId, rejoinPlan, 'rejoin');
|
|
250
266
|
return { accepted: true, slot: formerSlot, rejoined: true };
|
|
251
267
|
}
|
|
@@ -264,6 +280,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
264
280
|
slotByPlayer.set(playerId, slot);
|
|
265
281
|
everSlotByPlayer.set(playerId, slot);
|
|
266
282
|
sendSessionStart(playerId, slot);
|
|
283
|
+
secretAuthority?.onPlayerJoined(playerId, slot);
|
|
267
284
|
sendCatchUpTo(playerId, plan, 'join');
|
|
268
285
|
return { accepted: true, slot };
|
|
269
286
|
}
|
|
@@ -283,6 +300,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
283
300
|
return false;
|
|
284
301
|
spectators.add(playerId);
|
|
285
302
|
sendSessionStart(playerId, -1, 'spectator');
|
|
303
|
+
secretAuthority?.onSpectatorJoined(playerId);
|
|
286
304
|
sendCatchUpTo(playerId, plan, 'join');
|
|
287
305
|
return true;
|
|
288
306
|
}
|
|
@@ -309,8 +327,14 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
309
327
|
// client that fell behind the snapshot tick, so the stored snapshot rides
|
|
310
328
|
// along — the client applies it only when it is ahead of its own state.
|
|
311
329
|
const reconnectPlan = !historyReachesTickZero() && storedSnapshot !== undefined ? 'snapshot' : 'history';
|
|
330
|
+
secretAuthority?.onPlayerReconnected(playerId, slot);
|
|
312
331
|
sendCatchUpTo(playerId, reconnectPlan, 'reconnect');
|
|
313
332
|
}
|
|
333
|
+
function onPlayerSecretMessage(playerId, envelope) {
|
|
334
|
+
if (secretAuthority === undefined)
|
|
335
|
+
return;
|
|
336
|
+
secretAuthority.onPlayerRequest(playerId, slotByPlayer.get(playerId) ?? -1, envelope);
|
|
337
|
+
}
|
|
314
338
|
function onPlayerMessage(playerId, envelope) {
|
|
315
339
|
// Fail closed: unknown/malformed messages are dropped. Snapshot transfer
|
|
316
340
|
// envelopes are the OTHER frozen wire family (schemaVersion vs v field) —
|
|
@@ -571,7 +595,10 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
571
595
|
// Fixed roster for v1 (late-join/reconnect is M5). Dropping the mapping stops accepting the
|
|
572
596
|
// departed player's input; their slot stays reserved because `assignedSlots` never decreases,
|
|
573
597
|
// so the authority substitutes for it rather than handing the number to a new joiner.
|
|
598
|
+
const slot = slotByPlayer.get(playerId);
|
|
574
599
|
slotByPlayer.delete(playerId);
|
|
600
|
+
if (slot !== undefined)
|
|
601
|
+
secretAuthority?.onPlayerLeft(playerId, slot);
|
|
575
602
|
if (pendingTransfer?.donorId === playerId) {
|
|
576
603
|
failPendingTransfer('snapshot-donor-left');
|
|
577
604
|
}
|
|
@@ -586,6 +613,9 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
586
613
|
return;
|
|
587
614
|
}
|
|
588
615
|
now += 1;
|
|
616
|
+
for (const command of secretAuthority?.onTick(now, [...slotByPlayer.values()]) ?? []) {
|
|
617
|
+
authority.queueCommand(now, command);
|
|
618
|
+
}
|
|
589
619
|
authority.advance(now);
|
|
590
620
|
maintainSnapshotAcquisition();
|
|
591
621
|
if (authority.confirmedThrough < 0) {
|
|
@@ -636,6 +666,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
636
666
|
onPlayerJoin,
|
|
637
667
|
onSpectatorJoin,
|
|
638
668
|
onPlayerMessage,
|
|
669
|
+
onPlayerSecretMessage,
|
|
639
670
|
onPlayerLeave,
|
|
640
671
|
onPlayerReconnected,
|
|
641
672
|
onTick,
|
|
@@ -672,6 +703,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
672
703
|
if (plan === 'unavailable')
|
|
673
704
|
return false;
|
|
674
705
|
sendSessionStart(playerId, slot);
|
|
706
|
+
secretAuthority?.onPlayerReconnected(playerId, slot);
|
|
675
707
|
sendCatchUpTo(playerId, plan, 'resync');
|
|
676
708
|
return true;
|
|
677
709
|
},
|
|
@@ -697,5 +729,16 @@ export function createDeterministicAuthorityRoom(transport, config) {
|
|
|
697
729
|
frames: authority.confirmedFrameRange(0, authority.confirmedThrough),
|
|
698
730
|
};
|
|
699
731
|
},
|
|
732
|
+
secretSnapshot() {
|
|
733
|
+
return secretAuthority?.snapshot();
|
|
734
|
+
},
|
|
700
735
|
};
|
|
701
736
|
}
|
|
737
|
+
function requireSecretAuthorityId(authorityId) {
|
|
738
|
+
invariant(typeof authorityId === 'string' && authorityId.length > 0, 'secret authorityId is required', 'authority-room.secret-authority-id');
|
|
739
|
+
return authorityId;
|
|
740
|
+
}
|
|
741
|
+
function requireSecretCrypto(crypto) {
|
|
742
|
+
invariant(crypto !== undefined, 'secret signing is unavailable', 'authority-room.secret-signing');
|
|
743
|
+
return crypto;
|
|
744
|
+
}
|
package/dist/browser.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export { createOfflineSession } from './offline-session.js';
|
|
|
30
30
|
export { createPauseController } from './pause-resume.js';
|
|
31
31
|
export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D } from './physics2d.js';
|
|
32
32
|
export { applyDeterministicImpulse3D, applyDeterministicImpulses3D, contactDetailsDeterministicPhysics3D, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
|
|
33
|
+
export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
|
|
33
34
|
export { createDeterministicR3fRenderAdapter as createDeterministicR3fAdapter, createDeterministicR3fRenderAdapter, interpolateDeterministicRenderNumber, interpolateDeterministicRenderVec2 } from './r3f-render-adapter.js';
|
|
34
35
|
export { verifyReplayWithDescriptor as verifyDeterministicReplay, verifyReplay } from './replay.js';
|
|
35
36
|
export { createDeterministicReplayBundle, createDeterministicReplayDebugTables, createDeterministicReplayRuntimeManifest, minimizeDeterministicReplayDesync, minimizeDeterministicReplayDesyncBundle, sliceDeterministicReplayBundle, verifyDeterministicFrameRuntimeReplayBundle, verifyDeterministicReplayBundle, verifyDeterministicSelfContainedFrameRuntimeReplayBundle, verifyReplayScoreAgainstTrustedRuntime } from './replay-bundle.js';
|
|
@@ -74,7 +75,9 @@ export type { CookedDeterministicNavmesh, DeterministicNavmeshAgent, Determinist
|
|
|
74
75
|
export type { NetworkedClientTransport, NetworkedSyncplayClient, NetworkedSyncplayClientInputDelayOptions, NetworkedSyncplayClientNetStats, NetworkedSyncplayClientOptions, NetworkedSyncplayClientPacingOptions } from './networked-client.js';
|
|
75
76
|
export type { DeterministicPauseController, DeterministicPauseState, DeterministicPauseStatus, PauseControllerOptions, PauseWrappedState } from './pause-resume.js';
|
|
76
77
|
export type { DeterministicBodyKind, DeterministicContactEvent2D, DeterministicContactEventType, DeterministicPhysicsAabbQuery2D, DeterministicPhysicsActivePair2D, DeterministicPhysicsBody2D, DeterministicPhysicsCircleCastQuery2D, DeterministicPhysicsConstraint2D, DeterministicPhysicsHit2D, DeterministicPhysicsQueryOptions2D, DeterministicPhysicsRaycastQuery2D, DeterministicPhysicsShape2D, DeterministicPhysicsShapeOverlapQuery2D, DeterministicPhysicsStepOptions2D, DeterministicPhysicsStepResult2D, DeterministicPhysicsWorld2D } from './physics2d.js';
|
|
77
|
-
export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsHit3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D } from './physics3d.js';
|
|
78
|
+
export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D } from './physics3d.js';
|
|
79
|
+
export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
|
|
80
|
+
export type { DeterministicVehicle3D, DeterministicVehicleImpulse3D, DeterministicVehicleInput3D, DeterministicVehicleStepOptions3D, DeterministicVehicleStepResult3D, DeterministicVehicleWheel3D, DeterministicVehicleWheelTelemetry3D } from './physics3d-vehicle.js';
|
|
78
81
|
export type { DeterministicFrameRuntimeReplayBundleOptions, DeterministicFrameRuntimeReplayBundleVerification, DeterministicReplayBundle, DeterministicReplayBundleMismatch, DeterministicReplayBundleVerification, DeterministicReplayDebugTables, DeterministicReplayDesyncMinimization, DeterministicReplayRuntimeBeforeFrameId, DeterministicReplayRuntimeManifest, DeterministicReplayRuntimeManifestSource, DeterministicReplayRuntimeSystemId, DeterministicReplaySemanticModel, DeterministicReplaySemanticOperation, DeterministicReplaySliceOrigin, DeterministicReplaySnapshot, DeterministicReplayVerificationManifest, ReplayScoreVerificationReason, ReplayScoreVerificationResult } from './replay-bundle.js';
|
|
79
82
|
export type { AssetDescriptor, CollectionOrdering, CommandChannelDescriptor, ConfigDescriptor, DeterministicGameDescriptor, EventDescriptor, InputFieldDescriptor, InputFieldPolicy, NumericMode, PrototypeDescriptor, SchemaFieldDescriptor, SchemaFieldKind, StateFieldDescriptor, SystemDescriptor, ValidatedDeterministicGameDescriptor } from './schema.js';
|
|
80
83
|
export type { DeterministicSchemaArtifacts, SchemaFrameDump } from './schema-artifacts.js';
|
|
@@ -87,3 +90,10 @@ export type { DeterministicSignal, DeterministicSignalBus, DeterministicSignalBu
|
|
|
87
90
|
export type { DeterministicSparseSet, DeterministicSparseSetSnapshot } from './sparse-set.js';
|
|
88
91
|
export type { DeterministicLifecycleCounterSummary, DeterministicLifecycleRunner, DeterministicLifecycleSignal, DeterministicLifecycleSnapshot, DeterministicLifecycleSystemContext, DeterministicLifecycleSystemDescriptor, DeterministicLifecycleSystemHooks } from './system-lifecycle.js';
|
|
89
92
|
export type { DeterministicStep, ReplayFile, ReplayVerificationResult } from './types.js';
|
|
93
|
+
export { syncplaySecretConfigHash, syncplaySecretLimits, validateSyncplaySecretSystemsConfig } from './secret-config.js';
|
|
94
|
+
export { decodeSyncplaySecretPrivateMessage, isSyncplaySecretCommand, syncplaySecretProtocol } from './secret-protocol.js';
|
|
95
|
+
export { createBrowserSyncplaySecretCrypto, verifySyncplaySignedReceipt } from './secret-crypto.js';
|
|
96
|
+
export { createSyncplaySecretEventReader, createSyncplaySecretsClient } from './secret-client.js';
|
|
97
|
+
export type { SyncplaySecretSystemDefinition, SyncplaySecretSystemsConfig } from './secret-config.js';
|
|
98
|
+
export type { SyncplayOwnedSecretToken, SyncplaySecretCommand, SyncplaySecretErrorCode, SyncplaySecretPrivateMessage, SyncplaySignedReceipt } from './secret-protocol.js';
|
|
99
|
+
export type { SyncplayChoiceClient, SyncplayChoiceResolvedEvent, SyncplayCollectionClient, SyncplayCollectionRevealedEvent, SyncplayOwnedSecret, SyncplayRandomClient, SyncplayRandomDrawnEvent, SyncplayRoleClient, SyncplayRoleRevealedEvent, SyncplaySecretEventReader, SyncplaySecretsClient } from './secret-client.js';
|
package/dist/browser.js
CHANGED
|
@@ -31,6 +31,7 @@ export { createOfflineSession } from './offline-session.js';
|
|
|
31
31
|
export { createPauseController } from './pause-resume.js';
|
|
32
32
|
export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D } from './physics2d.js';
|
|
33
33
|
export { applyDeterministicImpulse3D, applyDeterministicImpulses3D, contactDetailsDeterministicPhysics3D, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
|
|
34
|
+
export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
|
|
34
35
|
export { createDeterministicR3fRenderAdapter as createDeterministicR3fAdapter, createDeterministicR3fRenderAdapter, interpolateDeterministicRenderNumber, interpolateDeterministicRenderVec2 } from './r3f-render-adapter.js';
|
|
35
36
|
export { verifyReplayWithDescriptor as verifyDeterministicReplay, verifyReplay } from './replay.js';
|
|
36
37
|
export { createDeterministicReplayBundle, createDeterministicReplayDebugTables, createDeterministicReplayRuntimeManifest, minimizeDeterministicReplayDesync, minimizeDeterministicReplayDesyncBundle, sliceDeterministicReplayBundle, verifyDeterministicFrameRuntimeReplayBundle, verifyDeterministicReplayBundle, verifyDeterministicSelfContainedFrameRuntimeReplayBundle, verifyReplayScoreAgainstTrustedRuntime } from './replay-bundle.js';
|
|
@@ -46,3 +47,7 @@ export { decodeDeterministicSessionMessage, deterministicSessionWireVersion, enc
|
|
|
46
47
|
export { createDeterministicSignalBus } from './signals.js';
|
|
47
48
|
export { createDeterministicSparseSet } from './sparse-set.js';
|
|
48
49
|
export { createDeterministicLifecycleRunner, summarizeDeterministicLifecycleCounters } from './system-lifecycle.js';
|
|
50
|
+
export { syncplaySecretConfigHash, syncplaySecretLimits, validateSyncplaySecretSystemsConfig } from './secret-config.js';
|
|
51
|
+
export { decodeSyncplaySecretPrivateMessage, isSyncplaySecretCommand, syncplaySecretProtocol } from './secret-protocol.js';
|
|
52
|
+
export { createBrowserSyncplaySecretCrypto, verifySyncplaySignedReceipt } from './secret-crypto.js';
|
|
53
|
+
export { createSyncplaySecretEventReader, createSyncplaySecretsClient } from './secret-client.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DeterministicSessionConfig } from './types.js';
|
|
2
|
+
export declare function serializeStateWith<State, Input>(config: DeterministicSessionConfig<State, Input>, state: State): Uint8Array;
|
|
3
|
+
export declare function deserializeStateWith<State, Input>(config: DeterministicSessionConfig<State, Input>, bytes: Uint8Array): State;
|
|
4
|
+
export declare function computeWireStateChecksum<State, Input>(config: DeterministicSessionConfig<State, Input>, state: State): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { decodeCanonicalBytes, defaultChecksum, encodeCanonicalBytes, } from './canonical.js';
|
|
2
|
+
export function serializeStateWith(config, state) {
|
|
3
|
+
// Keep this byte-identical to the engine's egress serializer. Some builders
|
|
4
|
+
// use canonicalizeState for full state while others use a projection.
|
|
5
|
+
return config.serializeState?.(state)
|
|
6
|
+
?? encodeCanonicalBytes((config.canonicalizeState?.(state) ?? state));
|
|
7
|
+
}
|
|
8
|
+
export function deserializeStateWith(config, bytes) {
|
|
9
|
+
return config.deserializeState?.(bytes) ?? decodeCanonicalBytes(bytes);
|
|
10
|
+
}
|
|
11
|
+
export function computeWireStateChecksum(config, state) {
|
|
12
|
+
// The wire basis is always the round-tripped state. It deliberately ignores
|
|
13
|
+
// config.checksum and never checksums canonicalizeState's output directly:
|
|
14
|
+
// both may be projections that are valid for recorded desync cadence but
|
|
15
|
+
// cannot verify the full state a late joiner hydrates.
|
|
16
|
+
return defaultChecksum(deserializeStateWith(config, serializeStateWith(config, state)));
|
|
17
|
+
}
|
|
@@ -7,7 +7,9 @@ const input_authority_js_1 = require("./input-authority.js");
|
|
|
7
7
|
const local_authority_wire_js_1 = require("./local-authority-wire.js");
|
|
8
8
|
const session_snapshot_js_1 = require("./session-snapshot.js");
|
|
9
9
|
const session_wire_js_1 = require("./session-wire.js");
|
|
10
|
+
const secret_authority_js_1 = require("./secret-authority.js");
|
|
10
11
|
const DEFAULT_SESSION_MSG_TYPE = 'rdm';
|
|
12
|
+
const DEFAULT_SECRET_MSG_TYPE = 'rdm-secret';
|
|
11
13
|
/** One hour at 60 Hz — see DeterministicAuthorityRoomConfig.historyRetentionTicks. */
|
|
12
14
|
const DEFAULT_HISTORY_RETENTION_TICKS = 216_000;
|
|
13
15
|
/** Frames per history-replay unicast. Bounds message size during catch-up. */
|
|
@@ -51,6 +53,18 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
51
53
|
// colliding with a still-present player and orphaning the vacated slot.
|
|
52
54
|
let assignedSlots = 0;
|
|
53
55
|
let now = -1;
|
|
56
|
+
const secretAuthority = config.secrets === undefined
|
|
57
|
+
? undefined
|
|
58
|
+
: (0, secret_authority_js_1.createSyncplaySecretAuthority)({
|
|
59
|
+
authorityId: requireSecretAuthorityId(config.authorityId),
|
|
60
|
+
playerCount: config.playerCount,
|
|
61
|
+
config: config.secrets,
|
|
62
|
+
crypto: requireSecretCrypto(config.secretCrypto),
|
|
63
|
+
sendPrivate(playerId, encoded) {
|
|
64
|
+
transport.sendTo(playerId, config.secretMsgType ?? DEFAULT_SECRET_MSG_TYPE, encoded);
|
|
65
|
+
},
|
|
66
|
+
...(config.restoreFrom?.secretState === undefined ? {} : { restoreFrom: config.restoreFrom.secretState }),
|
|
67
|
+
});
|
|
54
68
|
// M5.5 snapshot acquisition state. The authority stores at most one validated
|
|
55
69
|
// snapshot and runs at most one donor transfer at a time.
|
|
56
70
|
const snapshotCadenceTicks = config.snapshotCadenceTicks ?? DEFAULT_SNAPSHOT_CADENCE_TICKS;
|
|
@@ -230,6 +244,7 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
230
244
|
const plan = catchUpPlan();
|
|
231
245
|
(0, errors_js_1.invariant)(plan !== 'unavailable', 'a restored room must have a catch-up plan for seated players', 'authority-room.restore-catchup');
|
|
232
246
|
sendSessionStart(playerId, slot);
|
|
247
|
+
secretAuthority?.onPlayerReconnected(playerId, slot);
|
|
233
248
|
sendCatchUpTo(playerId, plan, 'resync');
|
|
234
249
|
}
|
|
235
250
|
}
|
|
@@ -249,6 +264,7 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
249
264
|
}
|
|
250
265
|
slotByPlayer.set(playerId, formerSlot);
|
|
251
266
|
sendSessionStart(playerId, formerSlot);
|
|
267
|
+
secretAuthority?.onPlayerReconnected(playerId, formerSlot);
|
|
252
268
|
sendCatchUpTo(playerId, rejoinPlan, 'rejoin');
|
|
253
269
|
return { accepted: true, slot: formerSlot, rejoined: true };
|
|
254
270
|
}
|
|
@@ -267,6 +283,7 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
267
283
|
slotByPlayer.set(playerId, slot);
|
|
268
284
|
everSlotByPlayer.set(playerId, slot);
|
|
269
285
|
sendSessionStart(playerId, slot);
|
|
286
|
+
secretAuthority?.onPlayerJoined(playerId, slot);
|
|
270
287
|
sendCatchUpTo(playerId, plan, 'join');
|
|
271
288
|
return { accepted: true, slot };
|
|
272
289
|
}
|
|
@@ -286,6 +303,7 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
286
303
|
return false;
|
|
287
304
|
spectators.add(playerId);
|
|
288
305
|
sendSessionStart(playerId, -1, 'spectator');
|
|
306
|
+
secretAuthority?.onSpectatorJoined(playerId);
|
|
289
307
|
sendCatchUpTo(playerId, plan, 'join');
|
|
290
308
|
return true;
|
|
291
309
|
}
|
|
@@ -312,8 +330,14 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
312
330
|
// client that fell behind the snapshot tick, so the stored snapshot rides
|
|
313
331
|
// along — the client applies it only when it is ahead of its own state.
|
|
314
332
|
const reconnectPlan = !historyReachesTickZero() && storedSnapshot !== undefined ? 'snapshot' : 'history';
|
|
333
|
+
secretAuthority?.onPlayerReconnected(playerId, slot);
|
|
315
334
|
sendCatchUpTo(playerId, reconnectPlan, 'reconnect');
|
|
316
335
|
}
|
|
336
|
+
function onPlayerSecretMessage(playerId, envelope) {
|
|
337
|
+
if (secretAuthority === undefined)
|
|
338
|
+
return;
|
|
339
|
+
secretAuthority.onPlayerRequest(playerId, slotByPlayer.get(playerId) ?? -1, envelope);
|
|
340
|
+
}
|
|
317
341
|
function onPlayerMessage(playerId, envelope) {
|
|
318
342
|
// Fail closed: unknown/malformed messages are dropped. Snapshot transfer
|
|
319
343
|
// envelopes are the OTHER frozen wire family (schemaVersion vs v field) —
|
|
@@ -574,7 +598,10 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
574
598
|
// Fixed roster for v1 (late-join/reconnect is M5). Dropping the mapping stops accepting the
|
|
575
599
|
// departed player's input; their slot stays reserved because `assignedSlots` never decreases,
|
|
576
600
|
// so the authority substitutes for it rather than handing the number to a new joiner.
|
|
601
|
+
const slot = slotByPlayer.get(playerId);
|
|
577
602
|
slotByPlayer.delete(playerId);
|
|
603
|
+
if (slot !== undefined)
|
|
604
|
+
secretAuthority?.onPlayerLeft(playerId, slot);
|
|
578
605
|
if (pendingTransfer?.donorId === playerId) {
|
|
579
606
|
failPendingTransfer('snapshot-donor-left');
|
|
580
607
|
}
|
|
@@ -589,6 +616,9 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
589
616
|
return;
|
|
590
617
|
}
|
|
591
618
|
now += 1;
|
|
619
|
+
for (const command of secretAuthority?.onTick(now, [...slotByPlayer.values()]) ?? []) {
|
|
620
|
+
authority.queueCommand(now, command);
|
|
621
|
+
}
|
|
592
622
|
authority.advance(now);
|
|
593
623
|
maintainSnapshotAcquisition();
|
|
594
624
|
if (authority.confirmedThrough < 0) {
|
|
@@ -639,6 +669,7 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
639
669
|
onPlayerJoin,
|
|
640
670
|
onSpectatorJoin,
|
|
641
671
|
onPlayerMessage,
|
|
672
|
+
onPlayerSecretMessage,
|
|
642
673
|
onPlayerLeave,
|
|
643
674
|
onPlayerReconnected,
|
|
644
675
|
onTick,
|
|
@@ -675,6 +706,7 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
675
706
|
if (plan === 'unavailable')
|
|
676
707
|
return false;
|
|
677
708
|
sendSessionStart(playerId, slot);
|
|
709
|
+
secretAuthority?.onPlayerReconnected(playerId, slot);
|
|
678
710
|
sendCatchUpTo(playerId, plan, 'resync');
|
|
679
711
|
return true;
|
|
680
712
|
},
|
|
@@ -700,5 +732,16 @@ function createDeterministicAuthorityRoom(transport, config) {
|
|
|
700
732
|
frames: authority.confirmedFrameRange(0, authority.confirmedThrough),
|
|
701
733
|
};
|
|
702
734
|
},
|
|
735
|
+
secretSnapshot() {
|
|
736
|
+
return secretAuthority?.snapshot();
|
|
737
|
+
},
|
|
703
738
|
};
|
|
704
739
|
}
|
|
740
|
+
function requireSecretAuthorityId(authorityId) {
|
|
741
|
+
(0, errors_js_1.invariant)(typeof authorityId === 'string' && authorityId.length > 0, 'secret authorityId is required', 'authority-room.secret-authority-id');
|
|
742
|
+
return authorityId;
|
|
743
|
+
}
|
|
744
|
+
function requireSecretCrypto(crypto) {
|
|
745
|
+
(0, errors_js_1.invariant)(crypto !== undefined, 'secret signing is unavailable', 'authority-room.secret-signing');
|
|
746
|
+
return crypto;
|
|
747
|
+
}
|
package/dist/cjs/browser.js
CHANGED
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.sortAabbsByStableId = exports.segmentsIntersect = exports.segmentSegmentDistance = exports.segmentPointDistance = exports.pointOnSegment = exports.orientation = exports.fixedVec2 = exports.fixedCircle = exports.fixedAabb = exports.closestPointOnSegment = exports.circleIntersectsAabb = exports.circleIntersects = exports.aabbIntersects = exports.aabbContainsPoint = exports.stableSortedKeys = exports.stableEntries = exports.deterministicShuffle = exports.createStableIndexedMap = exports.encodeUtf8 = exports.encodeCanonicalBytes = exports.defaultChecksum = exports.decodeUtf8 = exports.decodeCanonicalValue = exports.decodeCanonicalBytes = exports.cloneCanonical = exports.canonicalStringify = exports.canonicalize = exports.tickDeterministicHfsm = exports.tickDeterministicBehaviorTreeWithMemory = exports.tickDeterministicBehaviorTree = exports.selectDeterministicUtility = exports.runDeterministicBotSimulation = exports.getBlackboardValue = exports.createDeterministicBotBlackboard = exports.cookDeterministicBotAsset = exports.traceDeterministicBotDocument = exports.summarizeDeterministicBotDocumentTraceCoverage = exports.summarizeDeterministicBotDocumentTraceCounterDelta = exports.compileDeterministicBotDocument = exports.createDeterministicAuthorityRoom = exports.validatePrototypeEntitiesAgainstFrameRuntime = exports.stableAssetCookJson = exports.materializePrototypeEntities = exports.materializeDeterministicMapEntities = exports.cookDeterministicAssets = exports.assertAssetManifestMatches = exports.stepDeterministicAnimation = exports.reconstructDeterministicAnimationEvents = exports.createDeterministicAnimationRuntimeState = exports.cookDeterministicAnimation = void 0;
|
|
5
5
|
exports.createDeterministicLocalAuthoritySnapshotOfferEnvelope = exports.createDeterministicLocalAuthoritySnapshotEnvelope = exports.createDeterministicLocalAuthoritySnapshotCompleteEnvelope = exports.createDeterministicLocalAuthoritySnapshotChunkEnvelope = exports.createDeterministicLocalAuthorityInputBundleEnvelope = exports.createDeterministicLocalAuthorityCorrectionEnvelope = exports.createDeterministicLocalAuthorityCommandBundleEnvelope = exports.createDeterministicLocalAuthorityAckEnvelope = exports.applyDeterministicLocalAuthorityInputBundleEnvelope = exports.applyDeterministicLocalAuthorityCommandBundleEnvelope = exports.createDeterministicLocalAuthorityRuntime = exports.validateDeterministicLagCompensatedShot3D = exports.validateDeterministicAuthoritativeLagCompensatedShot3D = exports.rewindDeterministicLagCompensatedTargets3D = exports.createDeterministicLagCompensation3DHistory = exports.buildDeterministicLagCompensatedHitboxProxies3D = exports.runInstantReplay = exports.createInstantReplayRunner = exports.sortDeterministicInputTimelineFlags = exports.evaluateDeterministicInputTimelineResult = exports.accumulateDeterministicInputTimelineState = exports.createInputAuthority = exports.filterDeterministicEventsForPlayer = exports.createDeterministicEventTimeline = exports.validateDeterministicFrameSystemsStateless = exports.runDeterministicFrameRuntimeMutationCorpus = exports.isDeterministicFrameHeapStaleHandleError = exports.generateDeterministicFrameRuntimeSource = exports.createDeterministicSparseSetFrameRuntime = exports.createDeterministicSlotMap = exports.createDeterministicFrameRuntimeAccessors = exports.createDeterministicFrameRuntime = exports.createDeterministicBitset = exports.compileDeterministicDslRuntimeDescriptor = exports.assertDeterministicFrameSystemsStateless = exports.assertDeterministicFrameHeapStaleHandleError = exports.createDeterministicEventEffectsAdapter = exports.verifyDeterministicSessionRuntimeReplay = exports.createDeterministicSessionReplayBundle = exports.createDeterministicSessionCore = exports.verifyDeterministicConfigBundle = exports.summarizeDeterministicConfigBundleFailureReasons = exports.evaluateDeterministicConfigBundleParityResult = exports.cookDeterministicConfigBundle = exports.evaluateDeterministicCommandTimelineMigrationResult = exports.deterministicPayloadMatchesFields = exports.createDeterministicCommandTimelineSnapshot = exports.createDeterministicCommandTimeline = exports.assertDeterministicPayloadFields = exports.translateAabb = void 0;
|
|
6
6
|
exports.stepDeterministicPhysicsWorld2D = exports.raycastDeterministicPhysics2D = exports.raycastAllDeterministicPhysics2D = exports.queryDeterministicPhysicsShapeOverlap2D = exports.queryDeterministicPhysicsAabb2D = exports.nearestDeterministicPhysicsHit2D = exports.createDeterministicPhysicsWorld2D = exports.circleCastDeterministicPhysics2D = exports.createPauseController = exports.createOfflineSession = exports.valueNoise2D = exports.valueNoise1D = exports.hashUint32 = exports.hash2 = exports.fbm2D = exports.createNetworkedSyncplayClient = exports.stepDeterministicNavmeshOffMeshTraversal = exports.stepDeterministicNavmeshAgents = exports.smoothDeterministicNavmeshPath = exports.runDeterministicNavmeshStress = exports.locateDeterministicNavmeshPolygon = exports.findDeterministicNavmeshPointPath = exports.findDeterministicNavmeshPath = exports.cookImportedDeterministicNavmesh = exports.cookDeterministicNavmesh = exports.stepDeterministicNavigationAgents = exports.smoothDeterministicPath = exports.findDeterministicPath = exports.createDeterministicFlowField = exports.cookTilemapNavigationGraph = exports.cookDeterministicNavigationGraph = exports.applyDeterministicReciprocalAvoidance = exports.createSyncplayClient = exports.stepDeterministicKcc3D = exports.stepDeterministicCarry3D = exports.validateDeterministicLagCompensatedHit = exports.stepDeterministicTopDownMover = exports.stepDeterministicKcc2D = exports.stepDeterministicFpsAim = exports.runDeterministicMovementSimulation = exports.runDeterministicMovementCrowdSimulation = exports.rewindDeterministicTargets = exports.createDeterministicMath = exports.encodeDeterministicLocalAuthorityWireEnvelope = exports.deterministicLocalAuthorityWireProtocol = exports.decodeDeterministicLocalAuthorityWireEnvelope = exports.createDeterministicLocalAuthorityWireReceiver = exports.createDeterministicLocalAuthorityWireAuth = exports.createDeterministicLocalAuthoritySnapshotTransfer = exports.createDeterministicLocalAuthoritySnapshotRequestEnvelope = void 0;
|
|
7
|
-
exports.
|
|
8
|
-
exports.summarizeDeterministicLifecycleCounters = exports.createDeterministicLifecycleRunner = exports.createDeterministicSparseSet = exports.createDeterministicSignalBus = exports.encodeDeterministicSessionMessage = exports.deterministicSessionWireVersion = exports.decodeDeterministicSessionMessage = exports.encodeDeterministicStateSnapshot = exports.deterministicRoomDescriptorHash = exports.DEFAULT_SNAPSHOT_CHUNK_SIZE = exports.DEFAULT_MAX_SNAPSHOT_BYTES = exports.decodeDeterministicStateSnapshot = exports.createSnapshotTransferFromBytes = void 0;
|
|
7
|
+
exports.bytesToBase64 = exports.base64ToBytes = exports.exportPromotionSnapshot = exports.createSessionFromPromotionSnapshot = exports.createSyncplaySession = exports.createSyncplayOfflineRuntime = exports.createSyncplayGameRuntime = exports.stableDeterministicDslSchemaJson = exports.parseDeterministicSchemaDsl = exports.deterministicDslMaxPlayers = exports.createDeterministicBinarySerializer = exports.proveSchemaBinaryRoundTrip = exports.generateSchemaTypes = exports.generateSchemaBinarySource = exports.createDeterministicDescriptorBinarySchema = exports.createSchemaArtifacts = exports.stableDescriptorId = exports.defineSyncplayGame = exports.verifyReplayScoreAgainstTrustedRuntime = exports.verifyDeterministicSelfContainedFrameRuntimeReplayBundle = exports.verifyDeterministicReplayBundle = exports.verifyDeterministicFrameRuntimeReplayBundle = exports.sliceDeterministicReplayBundle = exports.minimizeDeterministicReplayDesyncBundle = exports.minimizeDeterministicReplayDesync = exports.createDeterministicReplayRuntimeManifest = exports.createDeterministicReplayDebugTables = exports.createDeterministicReplayBundle = exports.verifyReplay = exports.verifyDeterministicReplay = exports.interpolateDeterministicRenderVec2 = exports.interpolateDeterministicRenderNumber = exports.createDeterministicR3fRenderAdapter = exports.createDeterministicR3fAdapter = exports.stepDeterministicVehicle3D = exports.stepDeterministicPhysicsWorld3D = exports.shapeCastDeterministicPhysics3DDetailed = exports.shapeCastDeterministicPhysics3D = exports.setDeterministicBodyVelocity3D = exports.runDeterministicPhysics3DStress = exports.raycastDeterministicPhysics3DDetailed = exports.raycastDeterministicPhysics3D = exports.overlapDeterministicPhysics3D = exports.deterministicPhysicsCallback3D = exports.deterministicPhysicsBodyById3D = exports.deriveInverseInertia = exports.createDeterministicPhysicsWorld3D = exports.contactDetailsDeterministicPhysics3D = exports.applyDeterministicImpulses3D = exports.applyDeterministicImpulse3D = void 0;
|
|
8
|
+
exports.createSyncplaySecretsClient = exports.createSyncplaySecretEventReader = exports.verifySyncplaySignedReceipt = exports.createBrowserSyncplaySecretCrypto = exports.syncplaySecretProtocol = exports.isSyncplaySecretCommand = exports.decodeSyncplaySecretPrivateMessage = exports.validateSyncplaySecretSystemsConfig = exports.syncplaySecretLimits = exports.syncplaySecretConfigHash = exports.summarizeDeterministicLifecycleCounters = exports.createDeterministicLifecycleRunner = exports.createDeterministicSparseSet = exports.createDeterministicSignalBus = exports.encodeDeterministicSessionMessage = exports.deterministicSessionWireVersion = exports.decodeDeterministicSessionMessage = exports.encodeDeterministicStateSnapshot = exports.deterministicRoomDescriptorHash = exports.DEFAULT_SNAPSHOT_CHUNK_SIZE = exports.DEFAULT_MAX_SNAPSHOT_BYTES = exports.decodeDeterministicStateSnapshot = exports.createSnapshotTransferFromBytes = exports.createSnapshotTransferCollector = void 0;
|
|
9
9
|
var animation_js_1 = require("./animation.js");
|
|
10
10
|
Object.defineProperty(exports, "cookDeterministicAnimation", { enumerable: true, get: function () { return animation_js_1.cookDeterministicAnimation; } });
|
|
11
11
|
Object.defineProperty(exports, "createDeterministicAnimationRuntimeState", { enumerable: true, get: function () { return animation_js_1.createDeterministicAnimationRuntimeState; } });
|
|
@@ -203,6 +203,8 @@ Object.defineProperty(exports, "setDeterministicBodyVelocity3D", { enumerable: t
|
|
|
203
203
|
Object.defineProperty(exports, "shapeCastDeterministicPhysics3D", { enumerable: true, get: function () { return physics3d_js_1.shapeCastDeterministicPhysics3D; } });
|
|
204
204
|
Object.defineProperty(exports, "shapeCastDeterministicPhysics3DDetailed", { enumerable: true, get: function () { return physics3d_js_1.shapeCastDeterministicPhysics3DDetailed; } });
|
|
205
205
|
Object.defineProperty(exports, "stepDeterministicPhysicsWorld3D", { enumerable: true, get: function () { return physics3d_js_1.stepDeterministicPhysicsWorld3D; } });
|
|
206
|
+
var physics3d_vehicle_js_1 = require("./physics3d-vehicle.js");
|
|
207
|
+
Object.defineProperty(exports, "stepDeterministicVehicle3D", { enumerable: true, get: function () { return physics3d_vehicle_js_1.stepDeterministicVehicle3D; } });
|
|
206
208
|
var r3f_render_adapter_js_1 = require("./r3f-render-adapter.js");
|
|
207
209
|
Object.defineProperty(exports, "createDeterministicR3fAdapter", { enumerable: true, get: function () { return r3f_render_adapter_js_1.createDeterministicR3fRenderAdapter; } });
|
|
208
210
|
Object.defineProperty(exports, "createDeterministicR3fRenderAdapter", { enumerable: true, get: function () { return r3f_render_adapter_js_1.createDeterministicR3fRenderAdapter; } });
|
|
@@ -266,3 +268,17 @@ Object.defineProperty(exports, "createDeterministicSparseSet", { enumerable: tru
|
|
|
266
268
|
var system_lifecycle_js_1 = require("./system-lifecycle.js");
|
|
267
269
|
Object.defineProperty(exports, "createDeterministicLifecycleRunner", { enumerable: true, get: function () { return system_lifecycle_js_1.createDeterministicLifecycleRunner; } });
|
|
268
270
|
Object.defineProperty(exports, "summarizeDeterministicLifecycleCounters", { enumerable: true, get: function () { return system_lifecycle_js_1.summarizeDeterministicLifecycleCounters; } });
|
|
271
|
+
var secret_config_js_1 = require("./secret-config.js");
|
|
272
|
+
Object.defineProperty(exports, "syncplaySecretConfigHash", { enumerable: true, get: function () { return secret_config_js_1.syncplaySecretConfigHash; } });
|
|
273
|
+
Object.defineProperty(exports, "syncplaySecretLimits", { enumerable: true, get: function () { return secret_config_js_1.syncplaySecretLimits; } });
|
|
274
|
+
Object.defineProperty(exports, "validateSyncplaySecretSystemsConfig", { enumerable: true, get: function () { return secret_config_js_1.validateSyncplaySecretSystemsConfig; } });
|
|
275
|
+
var secret_protocol_js_1 = require("./secret-protocol.js");
|
|
276
|
+
Object.defineProperty(exports, "decodeSyncplaySecretPrivateMessage", { enumerable: true, get: function () { return secret_protocol_js_1.decodeSyncplaySecretPrivateMessage; } });
|
|
277
|
+
Object.defineProperty(exports, "isSyncplaySecretCommand", { enumerable: true, get: function () { return secret_protocol_js_1.isSyncplaySecretCommand; } });
|
|
278
|
+
Object.defineProperty(exports, "syncplaySecretProtocol", { enumerable: true, get: function () { return secret_protocol_js_1.syncplaySecretProtocol; } });
|
|
279
|
+
var secret_crypto_js_1 = require("./secret-crypto.js");
|
|
280
|
+
Object.defineProperty(exports, "createBrowserSyncplaySecretCrypto", { enumerable: true, get: function () { return secret_crypto_js_1.createBrowserSyncplaySecretCrypto; } });
|
|
281
|
+
Object.defineProperty(exports, "verifySyncplaySignedReceipt", { enumerable: true, get: function () { return secret_crypto_js_1.verifySyncplaySignedReceipt; } });
|
|
282
|
+
var secret_client_js_1 = require("./secret-client.js");
|
|
283
|
+
Object.defineProperty(exports, "createSyncplaySecretEventReader", { enumerable: true, get: function () { return secret_client_js_1.createSyncplaySecretEventReader; } });
|
|
284
|
+
Object.defineProperty(exports, "createSyncplaySecretsClient", { enumerable: true, get: function () { return secret_client_js_1.createSyncplaySecretsClient; } });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serializeStateWith = serializeStateWith;
|
|
4
|
+
exports.deserializeStateWith = deserializeStateWith;
|
|
5
|
+
exports.computeWireStateChecksum = computeWireStateChecksum;
|
|
6
|
+
const canonical_js_1 = require("./canonical.js");
|
|
7
|
+
function serializeStateWith(config, state) {
|
|
8
|
+
// Keep this byte-identical to the engine's egress serializer. Some builders
|
|
9
|
+
// use canonicalizeState for full state while others use a projection.
|
|
10
|
+
return config.serializeState?.(state)
|
|
11
|
+
?? (0, canonical_js_1.encodeCanonicalBytes)((config.canonicalizeState?.(state) ?? state));
|
|
12
|
+
}
|
|
13
|
+
function deserializeStateWith(config, bytes) {
|
|
14
|
+
return config.deserializeState?.(bytes) ?? (0, canonical_js_1.decodeCanonicalBytes)(bytes);
|
|
15
|
+
}
|
|
16
|
+
function computeWireStateChecksum(config, state) {
|
|
17
|
+
// The wire basis is always the round-tripped state. It deliberately ignores
|
|
18
|
+
// config.checksum and never checksums canonicalizeState's output directly:
|
|
19
|
+
// both may be projections that are valid for recorded desync cadence but
|
|
20
|
+
// cannot verify the full state a late joiner hydrates.
|
|
21
|
+
return (0, canonical_js_1.defaultChecksum)(deserializeStateWith(config, serializeStateWith(config, state)));
|
|
22
|
+
}
|