@series-inc/rundot-syncplay 5.24.0-beta.0 → 5.24.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +42 -14
  2. package/dist/authority-room.d.ts +17 -0
  3. package/dist/authority-room.js +48 -1
  4. package/dist/browser.d.ts +14 -3
  5. package/dist/browser.js +7 -1
  6. package/dist/cjs/authority-room.js +48 -1
  7. package/dist/cjs/browser.js +23 -2
  8. package/dist/cjs/cosmetic-physics3d.js +201 -0
  9. package/dist/cjs/creator.js +26 -1
  10. package/dist/cjs/engine-complete.js +10 -8
  11. package/dist/cjs/engine-parity-matrix.js +11 -2
  12. package/dist/cjs/engine-parity.js +6 -3
  13. package/dist/cjs/index.js +28 -2
  14. package/dist/cjs/input-authority.js +66 -2
  15. package/dist/cjs/local-authority-server-runner.js +3 -1
  16. package/dist/cjs/local-authority-wire.js +20 -1
  17. package/dist/cjs/match-log.js +27 -0
  18. package/dist/cjs/math.js +30 -0
  19. package/dist/cjs/networked-client.js +94 -13
  20. package/dist/cjs/node.js +19 -1
  21. package/dist/cjs/physics-cert.js +1 -1
  22. package/dist/cjs/physics3d-destruction-cert.js +279 -0
  23. package/dist/cjs/physics3d-heightfield.js +51 -0
  24. package/dist/cjs/physics3d-joints.js +92 -18
  25. package/dist/cjs/physics3d-mass.js +131 -0
  26. package/dist/cjs/physics3d-shared.js +5 -1
  27. package/dist/cjs/physics3d-solver.js +204 -51
  28. package/dist/cjs/physics3d-toi.js +23 -0
  29. package/dist/cjs/physics3d-voxel.js +94 -0
  30. package/dist/cjs/physics3d.js +327 -27
  31. package/dist/cjs/replay.js +25 -1
  32. package/dist/cjs/sdk-offline.js +9 -1
  33. package/dist/cjs/sdk-session.js +9 -0
  34. package/dist/cjs/secret-authority.js +647 -0
  35. package/dist/cjs/secret-client.js +615 -0
  36. package/dist/cjs/secret-command-classification.js +8 -0
  37. package/dist/cjs/secret-config.js +211 -0
  38. package/dist/cjs/secret-crypto.js +208 -0
  39. package/dist/cjs/secret-protocol.js +238 -0
  40. package/dist/cjs/session-build.js +7 -4
  41. package/dist/cjs/session-wire.js +12 -6
  42. package/dist/cjs/tools/static-checker.js +61 -13
  43. package/dist/cli.js +77 -10
  44. package/dist/cosmetic-physics3d.d.ts +41 -0
  45. package/dist/cosmetic-physics3d.js +196 -0
  46. package/dist/creator.d.ts +8 -0
  47. package/dist/creator.js +7 -0
  48. package/dist/deterministic-primitives.d.ts +1 -0
  49. package/dist/engine-complete.js +10 -8
  50. package/dist/engine-parity-matrix.js +11 -2
  51. package/dist/engine-parity.js +6 -3
  52. package/dist/index.d.ts +17 -2
  53. package/dist/index.js +9 -1
  54. package/dist/input-authority.d.ts +6 -0
  55. package/dist/input-authority.js +67 -3
  56. package/dist/local-authority-server-runner.d.ts +1 -0
  57. package/dist/local-authority-server-runner.js +2 -1
  58. package/dist/local-authority-wire.js +20 -1
  59. package/dist/match-log.d.ts +4 -1
  60. package/dist/match-log.js +27 -0
  61. package/dist/math.d.ts +4 -0
  62. package/dist/math.js +30 -0
  63. package/dist/networked-client.d.ts +17 -0
  64. package/dist/networked-client.js +94 -13
  65. package/dist/node.d.ts +5 -0
  66. package/dist/node.js +4 -0
  67. package/dist/physics-cert.js +1 -1
  68. package/dist/physics3d-destruction-cert.d.ts +35 -0
  69. package/dist/physics3d-destruction-cert.js +276 -0
  70. package/dist/physics3d-heightfield.d.ts +17 -0
  71. package/dist/physics3d-heightfield.js +47 -0
  72. package/dist/physics3d-joints.d.ts +6 -1
  73. package/dist/physics3d-joints.js +93 -19
  74. package/dist/physics3d-mass.d.ts +11 -0
  75. package/dist/physics3d-mass.js +126 -0
  76. package/dist/physics3d-shared.d.ts +1 -0
  77. package/dist/physics3d-shared.js +4 -1
  78. package/dist/physics3d-solver.d.ts +41 -4
  79. package/dist/physics3d-solver.js +202 -51
  80. package/dist/physics3d-toi.d.ts +9 -0
  81. package/dist/physics3d-toi.js +22 -0
  82. package/dist/physics3d-voxel.d.ts +20 -0
  83. package/dist/physics3d-voxel.js +91 -0
  84. package/dist/physics3d.d.ts +38 -3
  85. package/dist/physics3d.js +329 -30
  86. package/dist/replay.d.ts +5 -0
  87. package/dist/replay.js +25 -1
  88. package/dist/reports.d.ts +6 -1
  89. package/dist/sdk-offline.d.ts +4 -0
  90. package/dist/sdk-offline.js +9 -1
  91. package/dist/sdk-session.d.ts +6 -0
  92. package/dist/sdk-session.js +9 -0
  93. package/dist/secret-authority.d.ts +90 -0
  94. package/dist/secret-authority.js +643 -0
  95. package/dist/secret-client.d.ts +89 -0
  96. package/dist/secret-client.js +611 -0
  97. package/dist/secret-command-classification.d.ts +1 -0
  98. package/dist/secret-command-classification.js +5 -0
  99. package/dist/secret-config.d.ts +45 -0
  100. package/dist/secret-config.js +206 -0
  101. package/dist/secret-crypto.d.ts +29 -0
  102. package/dist/secret-crypto.js +190 -0
  103. package/dist/secret-protocol.d.ts +181 -0
  104. package/dist/secret-protocol.js +230 -0
  105. package/dist/session-build.d.ts +16 -0
  106. package/dist/session-build.js +7 -4
  107. package/dist/session-wire.js +12 -6
  108. package/dist/tools/static-checker.d.ts +11 -0
  109. package/dist/tools/static-checker.js +60 -13
  110. package/package.json +6 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # RUN Syncplay
2
2
 
3
- Offline-first deterministic multiplayer engine for RUN.game. You author a
3
+ Offline-first deterministic multiplayer engine for RUN.world. You author a
4
4
  **deterministic** game (`defineSyncplayGame`, `DeterministicSystem`,
5
5
  `DeterministicRandom`) and Syncplay runs it — input authority, prediction, and
6
6
  rollback — with no game-specific server logic.
@@ -8,18 +8,29 @@ 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
- ## Preview Status
11
+ ## Public and private projections
12
12
 
13
- This is a headless engine and certification package. It is designed to prove
14
- deterministic simulation behavior from the CLI before renderer integration is
15
- added, and to hand deploy-check artifacts to Venus backend upload/room-server
16
- validation when a room bundle opts in.
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.
17
19
 
18
- Production matchmaking, ticket UX, client connection flows, and R3F renderer
19
- proof are not part of this package yet. The current production-facing Venus MP
20
- changes validate uploaded deterministic room proof metadata and pass normalized
21
- proof into room startup; they do not add new matchmaking or client connection
22
- semantics.
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.
23
34
 
24
35
  ## State Ownership Contract
25
36
 
@@ -171,9 +182,11 @@ Implemented:
171
182
  tests, `build:sdk`, and built `dist` import checks.
172
183
  - Curated creator subpath export at
173
184
  `@series-inc/rundot-game-sdk/syncplay/creator` for
174
- browser-safe offline runtime, descriptors, commands, schema artifacts, and
175
- replay verification without Node tooling, local servers, certification labs,
176
- render adapters, or engine internals.
185
+ browser-safe offline runtime, descriptors, commands, schema artifacts,
186
+ replay verification, and deterministic 3D physics (world create/step/edit,
187
+ voxel-chunk collider cooking, impulses, queries, vehicles) without Node
188
+ tooling, local servers, certification labs, render adapters, or engine
189
+ internals.
177
190
  - Manifest-driven H5 creator entrypoint certification that requires game code
178
191
  to import the safe `/creator` runtime surface and rejects broad SDK, Node,
179
192
  tooling, lab, certification, r3f, and internal runtime imports before release
@@ -305,6 +318,11 @@ Implemented:
305
318
  `applyDeterministicImpulse3D` / `setDeterministicBodyVelocity3D`; contact
306
319
  events carry `normalImpulse`. The contact/joint caches live in the world and
307
320
  are therefore checksummed and rollback-safe.
321
+ - **Render-only cosmetic 3D physics.** `createDeterministicCosmeticPhysicsWorld3D`
322
+ accepts dynamic cosmetic bodies plus static/kinematic authoritative proxies
323
+ and resolves contacts against the cosmetics only. Proxy state and impulses
324
+ are never written back, and the cosmetic layer has no checksum or participation
325
+ in Syncplay state, snapshots, replays, or network traffic.
308
326
  - **Deterministic raycast-wheel vehicles.** `stepDeterministicVehicle3D` drives
309
327
  a single dynamic chassis with N suspension raycasts (wheels are rays, not
310
328
  bodies — Bullet/Godot raycast-vehicle lineage): spring-damper suspension,
@@ -557,6 +575,16 @@ remediation text. Valid suppressions must use
557
575
  `// rdm-ignore-next-line <rule-id>: <reason>` and are reported with file, line,
558
576
  rule, and reason; invalid suppressions are errors.
559
577
 
578
+ `static:determinism` with explicit paths (`--entry`, `--paths`) follows the
579
+ import graph transitively by default (`--no-follow-imports` opts out); the
580
+ JSON artifact records `requestedPaths` (CLI inputs), `checkedPaths` (files
581
+ actually checked, post-traversal), `followImports`, and `unresolvedImports`
582
+ (relative imports that resolve to no file — unchecked holes). Bare package
583
+ specifiers are never followed; if your simulation uses tsconfig path aliases,
584
+ pass `--project tsconfig.json` so aliased imports resolve and get checked —
585
+ without it they are skipped like package imports. Package self-certification
586
+ (no explicit paths) checks its curated source list without following imports.
587
+
560
588
  The checker covers host random/time, timers, async simulation work, I/O,
561
589
  unordered object iteration, random sort comparators, render-object mutation,
562
590
  uncertified physics stepping, and descriptor-declared state-field access.
@@ -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
  *
@@ -63,6 +66,12 @@ export interface DeterministicAuthorityRoomConfig {
63
66
  readonly snapshotCadenceTicks?: number;
64
67
  /** Abandon a donor transfer that has not completed after this many ticks. Default 600. */
65
68
  readonly transferTimeoutTicks?: number;
69
+ /**
70
+ * Snapshot transfer ceiling in bytes for this room's collector (default
71
+ * DEFAULT_MAX_SNAPSHOT_BYTES). Offers above it are rejected with
72
+ * `snapshot-too-large`; keep it equal to the clients' configured ceiling.
73
+ */
74
+ readonly maxSnapshotBytes?: number;
66
75
  /** Snapshot acquisition telemetry: stored snapshots and rejected/abandoned transfers. */
67
76
  readonly onSnapshotEvent?: (event: AuthorityRoomSnapshotEvent) => void;
68
77
  /**
@@ -89,6 +98,10 @@ export interface DeterministicAuthorityRoomConfig {
89
98
  * MUST call `resyncSeatedPlayers()` so surviving clients re-seat.
90
99
  */
91
100
  readonly restoreFrom?: DeterministicAuthorityRoomRestoreState;
101
+ readonly authorityId?: string;
102
+ readonly secrets?: SyncplaySecretSystemsConfig;
103
+ readonly secretCrypto?: SyncplaySecretAuthorityCrypto;
104
+ readonly secretMsgType?: string;
92
105
  }
93
106
  export interface DeterministicAuthorityRoomRestoreState {
94
107
  readonly now: number;
@@ -98,6 +111,7 @@ export interface DeterministicAuthorityRoomRestoreState {
98
111
  readonly tail: readonly ConfirmedInputFrame[];
99
112
  readonly snapshotBytes?: string;
100
113
  readonly schemaHash?: string;
114
+ readonly secretState?: SyncplaySecretAuthoritySnapshot;
101
115
  }
102
116
  export interface AuthorityRoomCatchUpEvent {
103
117
  readonly playerId: string;
@@ -178,6 +192,7 @@ export interface DeterministicAuthorityRoom {
178
192
  /** Seat a read-only spectator (G11): role 'spectator', no slot, full catch-up + live follow. */
179
193
  onSpectatorJoin(playerId: string): boolean;
180
194
  onPlayerMessage(playerId: string, envelope: string): void;
195
+ onPlayerSecretMessage(playerId: string, envelope: string): void;
181
196
  onPlayerLeave(playerId: string): void;
182
197
  /**
183
198
  * A player's transport re-attached inside the host's grace window (socket
@@ -213,5 +228,7 @@ export interface DeterministicAuthorityRoom {
213
228
  * `preserveFullHistory`, or the match outlived `historyRetentionTicks`).
214
229
  */
215
230
  matchLog(): SyncplayMatchLog | undefined;
231
+ /** Server-only secret state. Never include this value in public snapshots or match logs. */
232
+ secretSnapshot(): SyncplaySecretAuthoritySnapshot | undefined;
216
233
  }
217
234
  export declare function createDeterministicAuthorityRoom(transport: AuthorityRoomTransport, config: DeterministicAuthorityRoomConfig): DeterministicAuthorityRoom;
@@ -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. */
@@ -16,6 +18,9 @@ const DEFAULT_TRANSFER_TIMEOUT_TICKS = 600;
16
18
  export function createDeterministicAuthorityRoom(transport, config) {
17
19
  const sessionMsgType = config.sessionMsgType ?? DEFAULT_SESSION_MSG_TYPE;
18
20
  const historyRetentionTicks = config.historyRetentionTicks ?? DEFAULT_HISTORY_RETENTION_TICKS;
21
+ if (config.maxSnapshotBytes !== undefined && (!Number.isInteger(config.maxSnapshotBytes) || config.maxSnapshotBytes < 1)) {
22
+ throw new Error(`createDeterministicAuthorityRoom: maxSnapshotBytes must be a positive integer, received ${config.maxSnapshotBytes}`);
23
+ }
19
24
  const authority = createInputAuthority({
20
25
  playerCount: config.playerCount,
21
26
  hardToleranceTicks: config.hardToleranceTicks,
@@ -48,6 +53,18 @@ export function createDeterministicAuthorityRoom(transport, config) {
48
53
  // colliding with a still-present player and orphaning the vacated slot.
49
54
  let assignedSlots = 0;
50
55
  let now = -1;
56
+ const secretAuthority = config.secrets === undefined
57
+ ? undefined
58
+ : 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
+ });
51
68
  // M5.5 snapshot acquisition state. The authority stores at most one validated
52
69
  // snapshot and runs at most one donor transfer at a time.
53
70
  const snapshotCadenceTicks = config.snapshotCadenceTicks ?? DEFAULT_SNAPSHOT_CADENCE_TICKS;
@@ -227,6 +244,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
227
244
  const plan = catchUpPlan();
228
245
  invariant(plan !== 'unavailable', 'a restored room must have a catch-up plan for seated players', 'authority-room.restore-catchup');
229
246
  sendSessionStart(playerId, slot);
247
+ secretAuthority?.onPlayerReconnected(playerId, slot);
230
248
  sendCatchUpTo(playerId, plan, 'resync');
231
249
  }
232
250
  }
@@ -246,6 +264,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
246
264
  }
247
265
  slotByPlayer.set(playerId, formerSlot);
248
266
  sendSessionStart(playerId, formerSlot);
267
+ secretAuthority?.onPlayerReconnected(playerId, formerSlot);
249
268
  sendCatchUpTo(playerId, rejoinPlan, 'rejoin');
250
269
  return { accepted: true, slot: formerSlot, rejoined: true };
251
270
  }
@@ -264,6 +283,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
264
283
  slotByPlayer.set(playerId, slot);
265
284
  everSlotByPlayer.set(playerId, slot);
266
285
  sendSessionStart(playerId, slot);
286
+ secretAuthority?.onPlayerJoined(playerId, slot);
267
287
  sendCatchUpTo(playerId, plan, 'join');
268
288
  return { accepted: true, slot };
269
289
  }
@@ -283,6 +303,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
283
303
  return false;
284
304
  spectators.add(playerId);
285
305
  sendSessionStart(playerId, -1, 'spectator');
306
+ secretAuthority?.onSpectatorJoined(playerId);
286
307
  sendCatchUpTo(playerId, plan, 'join');
287
308
  return true;
288
309
  }
@@ -309,8 +330,14 @@ export function createDeterministicAuthorityRoom(transport, config) {
309
330
  // client that fell behind the snapshot tick, so the stored snapshot rides
310
331
  // along — the client applies it only when it is ahead of its own state.
311
332
  const reconnectPlan = !historyReachesTickZero() && storedSnapshot !== undefined ? 'snapshot' : 'history';
333
+ secretAuthority?.onPlayerReconnected(playerId, slot);
312
334
  sendCatchUpTo(playerId, reconnectPlan, 'reconnect');
313
335
  }
336
+ function onPlayerSecretMessage(playerId, envelope) {
337
+ if (secretAuthority === undefined)
338
+ return;
339
+ secretAuthority.onPlayerRequest(playerId, slotByPlayer.get(playerId) ?? -1, envelope);
340
+ }
314
341
  function onPlayerMessage(playerId, envelope) {
315
342
  // Fail closed: unknown/malformed messages are dropped. Snapshot transfer
316
343
  // envelopes are the OTHER frozen wire family (schemaVersion vs v field) —
@@ -516,7 +543,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
516
543
  donorId,
517
544
  minFrame: tick + 1,
518
545
  startedAt: now,
519
- collector: createSnapshotTransferCollector(),
546
+ collector: createSnapshotTransferCollector(config.maxSnapshotBytes === undefined ? {} : { maxSnapshotBytes: config.maxSnapshotBytes }),
520
547
  isRetry,
521
548
  };
522
549
  transport.sendTo(donorId, sessionMsgType, createDeterministicLocalAuthoritySnapshotRequestEnvelope(nextWireSequence(), { descriptorHash: roomDescriptorHash }, tick + 1));
@@ -571,7 +598,10 @@ export function createDeterministicAuthorityRoom(transport, config) {
571
598
  // Fixed roster for v1 (late-join/reconnect is M5). Dropping the mapping stops accepting the
572
599
  // departed player's input; their slot stays reserved because `assignedSlots` never decreases,
573
600
  // so the authority substitutes for it rather than handing the number to a new joiner.
601
+ const slot = slotByPlayer.get(playerId);
574
602
  slotByPlayer.delete(playerId);
603
+ if (slot !== undefined)
604
+ secretAuthority?.onPlayerLeft(playerId, slot);
575
605
  if (pendingTransfer?.donorId === playerId) {
576
606
  failPendingTransfer('snapshot-donor-left');
577
607
  }
@@ -586,6 +616,10 @@ export function createDeterministicAuthorityRoom(transport, config) {
586
616
  return;
587
617
  }
588
618
  now += 1;
619
+ authority.recordOccupiedSlots(now, [...slotByPlayer.values()]);
620
+ for (const command of secretAuthority?.onTick(now, [...slotByPlayer.values()]) ?? []) {
621
+ authority.queueCommand(now, command);
622
+ }
589
623
  authority.advance(now);
590
624
  maintainSnapshotAcquisition();
591
625
  if (authority.confirmedThrough < 0) {
@@ -636,6 +670,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
636
670
  onPlayerJoin,
637
671
  onSpectatorJoin,
638
672
  onPlayerMessage,
673
+ onPlayerSecretMessage,
639
674
  onPlayerLeave,
640
675
  onPlayerReconnected,
641
676
  onTick,
@@ -672,6 +707,7 @@ export function createDeterministicAuthorityRoom(transport, config) {
672
707
  if (plan === 'unavailable')
673
708
  return false;
674
709
  sendSessionStart(playerId, slot);
710
+ secretAuthority?.onPlayerReconnected(playerId, slot);
675
711
  sendCatchUpTo(playerId, plan, 'resync');
676
712
  return true;
677
713
  },
@@ -697,5 +733,16 @@ export function createDeterministicAuthorityRoom(transport, config) {
697
733
  frames: authority.confirmedFrameRange(0, authority.confirmedThrough),
698
734
  };
699
735
  },
736
+ secretSnapshot() {
737
+ return secretAuthority?.snapshot();
738
+ },
700
739
  };
701
740
  }
741
+ function requireSecretAuthorityId(authorityId) {
742
+ invariant(typeof authorityId === 'string' && authorityId.length > 0, 'secret authorityId is required', 'authority-room.secret-authority-id');
743
+ return authorityId;
744
+ }
745
+ function requireSecretCrypto(crypto) {
746
+ invariant(crypto !== undefined, 'secret signing is unavailable', 'authority-room.secret-signing');
747
+ return crypto;
748
+ }
package/dist/browser.d.ts CHANGED
@@ -29,7 +29,9 @@ export { fbm2D, hash2, hashUint32, valueNoise1D, valueNoise2D } from './noise.js
29
29
  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
- export { applyDeterministicImpulse3D, applyDeterministicImpulses3D, contactDetailsDeterministicPhysics3D, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
32
+ export { applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
33
+ export { cookDeterministicVoxelChunkCollider3D } from './physics3d-voxel.js';
34
+ export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
33
35
  export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
34
36
  export { createDeterministicR3fRenderAdapter as createDeterministicR3fAdapter, createDeterministicR3fRenderAdapter, interpolateDeterministicRenderNumber, interpolateDeterministicRenderVec2 } from './r3f-render-adapter.js';
35
37
  export { verifyReplayWithDescriptor as verifyDeterministicReplay, verifyReplay } from './replay.js';
@@ -72,10 +74,12 @@ export type { DeterministicKccBody3D, DeterministicKccCarrierPose3D, Determinist
72
74
  export type { DeterministicAuthorityCertificationArtifact, DeterministicAuthorityCertificationVerification, DeterministicAuthorityChecksums, SyncplayClient, SyncplayClientConnection, SyncplayClientRole, SyncplayCommand, SyncplayCommandPollOptions, SyncplayCommandSubmitResult, SyncplayInput, SyncplayLifecycleResult, SyncplaySubmitResult } from './multiplayer-service.js';
73
75
  export type { CookedDeterministicNavigationGraph, DeterministicFlowField, DeterministicNavEdge, DeterministicNavigationAgent, DeterministicNavigationGraphSource, DeterministicNavigationStepOptions, DeterministicNavigationStepResult, DeterministicNavNode, DeterministicNavRegion, DeterministicPathQuery, DeterministicPathResult, DeterministicTilemapSource } from './navigation.js';
74
76
  export type { CookedDeterministicNavmesh, DeterministicNavmeshAgent, DeterministicNavmeshAgentStepResult, DeterministicNavmeshImportSource, DeterministicNavmeshLink, DeterministicNavmeshMovingAvoidanceObstacle, DeterministicNavmeshOffMeshTraversalState, DeterministicNavmeshOffMeshTraversalStep, DeterministicNavmeshPath, DeterministicNavmeshPoint, DeterministicNavmeshPolygon, DeterministicNavmeshSource } from './navmesh.js';
75
- export type { NetworkedClientTransport, NetworkedSyncplayClient, NetworkedSyncplayClientInputDelayOptions, NetworkedSyncplayClientNetStats, NetworkedSyncplayClientOptions, NetworkedSyncplayClientPacingOptions } from './networked-client.js';
77
+ export type { NetworkedClientTransport, NetworkedSyncplayClient, NetworkedSyncplayClientInputDelayOptions, NetworkedSyncplayClientNetStats, NetworkedSyncplayClientOptions, NetworkedSyncplayClientPacingOptions, NetworkedSyncplaySlotPresence } from './networked-client.js';
76
78
  export type { DeterministicPauseController, DeterministicPauseState, DeterministicPauseStatus, PauseControllerOptions, PauseWrappedState } from './pause-resume.js';
77
79
  export type { DeterministicBodyKind, DeterministicContactEvent2D, DeterministicContactEventType, DeterministicPhysicsAabbQuery2D, DeterministicPhysicsActivePair2D, DeterministicPhysicsBody2D, DeterministicPhysicsCircleCastQuery2D, DeterministicPhysicsConstraint2D, DeterministicPhysicsHit2D, DeterministicPhysicsQueryOptions2D, DeterministicPhysicsRaycastQuery2D, DeterministicPhysicsShape2D, DeterministicPhysicsShapeOverlapQuery2D, DeterministicPhysicsStepOptions2D, DeterministicPhysicsStepResult2D, DeterministicPhysicsWorld2D } from './physics2d.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';
80
+ 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, DeterministicPhysicsWorldEdit3D } from './physics3d.js';
81
+ export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D } from './physics3d-voxel.js';
82
+ export type { DeterministicAuthoritativeBodyProxy3D, DeterministicCosmeticPhysicsBody3D, DeterministicCosmeticPhysicsStepOptions3D, DeterministicCosmeticPhysicsStepResult3D, DeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
79
83
  export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
80
84
  export type { DeterministicVehicle3D, DeterministicVehicleImpulse3D, DeterministicVehicleInput3D, DeterministicVehicleStepOptions3D, DeterministicVehicleStepResult3D, DeterministicVehicleWheel3D, DeterministicVehicleWheelTelemetry3D } from './physics3d-vehicle.js';
81
85
  export type { DeterministicFrameRuntimeReplayBundleOptions, DeterministicFrameRuntimeReplayBundleVerification, DeterministicReplayBundle, DeterministicReplayBundleMismatch, DeterministicReplayBundleVerification, DeterministicReplayDebugTables, DeterministicReplayDesyncMinimization, DeterministicReplayRuntimeBeforeFrameId, DeterministicReplayRuntimeManifest, DeterministicReplayRuntimeManifestSource, DeterministicReplayRuntimeSystemId, DeterministicReplaySemanticModel, DeterministicReplaySemanticOperation, DeterministicReplaySliceOrigin, DeterministicReplaySnapshot, DeterministicReplayVerificationManifest, ReplayScoreVerificationReason, ReplayScoreVerificationResult } from './replay-bundle.js';
@@ -90,3 +94,10 @@ export type { DeterministicSignal, DeterministicSignalBus, DeterministicSignalBu
90
94
  export type { DeterministicSparseSet, DeterministicSparseSetSnapshot } from './sparse-set.js';
91
95
  export type { DeterministicLifecycleCounterSummary, DeterministicLifecycleRunner, DeterministicLifecycleSignal, DeterministicLifecycleSnapshot, DeterministicLifecycleSystemContext, DeterministicLifecycleSystemDescriptor, DeterministicLifecycleSystemHooks } from './system-lifecycle.js';
92
96
  export type { DeterministicStep, ReplayFile, ReplayVerificationResult } from './types.js';
97
+ export { syncplaySecretConfigHash, syncplaySecretLimits, validateSyncplaySecretSystemsConfig } from './secret-config.js';
98
+ export { decodeSyncplaySecretPrivateMessage, isSyncplaySecretCommand, syncplaySecretProtocol } from './secret-protocol.js';
99
+ export { createBrowserSyncplaySecretCrypto, verifySyncplaySignedReceipt } from './secret-crypto.js';
100
+ export { createSyncplaySecretEventReader, createSyncplaySecretsClient } from './secret-client.js';
101
+ export type { SyncplaySecretSystemDefinition, SyncplaySecretSystemsConfig } from './secret-config.js';
102
+ export type { SyncplayOwnedSecretToken, SyncplaySecretCommand, SyncplaySecretErrorCode, SyncplaySecretPrivateMessage, SyncplaySignedReceipt } from './secret-protocol.js';
103
+ export type { SyncplayChoiceClient, SyncplayChoiceResolvedEvent, SyncplayCollectionClient, SyncplayCollectionRevealedEvent, SyncplayOwnedSecret, SyncplayRandomClient, SyncplayRandomDrawnEvent, SyncplayRoleClient, SyncplayRoleRevealedEvent, SyncplaySecretEventReader, SyncplaySecretsClient } from './secret-client.js';
package/dist/browser.js CHANGED
@@ -30,7 +30,9 @@ export { fbm2D, hash2, hashUint32, valueNoise1D, valueNoise2D } from './noise.js
30
30
  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
- export { applyDeterministicImpulse3D, applyDeterministicImpulses3D, contactDetailsDeterministicPhysics3D, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
33
+ export { applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
34
+ export { cookDeterministicVoxelChunkCollider3D } from './physics3d-voxel.js';
35
+ export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
34
36
  export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
35
37
  export { createDeterministicR3fRenderAdapter as createDeterministicR3fAdapter, createDeterministicR3fRenderAdapter, interpolateDeterministicRenderNumber, interpolateDeterministicRenderVec2 } from './r3f-render-adapter.js';
36
38
  export { verifyReplayWithDescriptor as verifyDeterministicReplay, verifyReplay } from './replay.js';
@@ -47,3 +49,7 @@ export { decodeDeterministicSessionMessage, deterministicSessionWireVersion, enc
47
49
  export { createDeterministicSignalBus } from './signals.js';
48
50
  export { createDeterministicSparseSet } from './sparse-set.js';
49
51
  export { createDeterministicLifecycleRunner, summarizeDeterministicLifecycleCounters } from './system-lifecycle.js';
52
+ export { syncplaySecretConfigHash, syncplaySecretLimits, validateSyncplaySecretSystemsConfig } from './secret-config.js';
53
+ export { decodeSyncplaySecretPrivateMessage, isSyncplaySecretCommand, syncplaySecretProtocol } from './secret-protocol.js';
54
+ export { createBrowserSyncplaySecretCrypto, verifySyncplaySignedReceipt } from './secret-crypto.js';
55
+ export { createSyncplaySecretEventReader, createSyncplaySecretsClient } from './secret-client.js';
@@ -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. */
@@ -19,6 +21,9 @@ const DEFAULT_TRANSFER_TIMEOUT_TICKS = 600;
19
21
  function createDeterministicAuthorityRoom(transport, config) {
20
22
  const sessionMsgType = config.sessionMsgType ?? DEFAULT_SESSION_MSG_TYPE;
21
23
  const historyRetentionTicks = config.historyRetentionTicks ?? DEFAULT_HISTORY_RETENTION_TICKS;
24
+ if (config.maxSnapshotBytes !== undefined && (!Number.isInteger(config.maxSnapshotBytes) || config.maxSnapshotBytes < 1)) {
25
+ throw new Error(`createDeterministicAuthorityRoom: maxSnapshotBytes must be a positive integer, received ${config.maxSnapshotBytes}`);
26
+ }
22
27
  const authority = (0, input_authority_js_1.createInputAuthority)({
23
28
  playerCount: config.playerCount,
24
29
  hardToleranceTicks: config.hardToleranceTicks,
@@ -51,6 +56,18 @@ function createDeterministicAuthorityRoom(transport, config) {
51
56
  // colliding with a still-present player and orphaning the vacated slot.
52
57
  let assignedSlots = 0;
53
58
  let now = -1;
59
+ const secretAuthority = config.secrets === undefined
60
+ ? undefined
61
+ : (0, secret_authority_js_1.createSyncplaySecretAuthority)({
62
+ authorityId: requireSecretAuthorityId(config.authorityId),
63
+ playerCount: config.playerCount,
64
+ config: config.secrets,
65
+ crypto: requireSecretCrypto(config.secretCrypto),
66
+ sendPrivate(playerId, encoded) {
67
+ transport.sendTo(playerId, config.secretMsgType ?? DEFAULT_SECRET_MSG_TYPE, encoded);
68
+ },
69
+ ...(config.restoreFrom?.secretState === undefined ? {} : { restoreFrom: config.restoreFrom.secretState }),
70
+ });
54
71
  // M5.5 snapshot acquisition state. The authority stores at most one validated
55
72
  // snapshot and runs at most one donor transfer at a time.
56
73
  const snapshotCadenceTicks = config.snapshotCadenceTicks ?? DEFAULT_SNAPSHOT_CADENCE_TICKS;
@@ -230,6 +247,7 @@ function createDeterministicAuthorityRoom(transport, config) {
230
247
  const plan = catchUpPlan();
231
248
  (0, errors_js_1.invariant)(plan !== 'unavailable', 'a restored room must have a catch-up plan for seated players', 'authority-room.restore-catchup');
232
249
  sendSessionStart(playerId, slot);
250
+ secretAuthority?.onPlayerReconnected(playerId, slot);
233
251
  sendCatchUpTo(playerId, plan, 'resync');
234
252
  }
235
253
  }
@@ -249,6 +267,7 @@ function createDeterministicAuthorityRoom(transport, config) {
249
267
  }
250
268
  slotByPlayer.set(playerId, formerSlot);
251
269
  sendSessionStart(playerId, formerSlot);
270
+ secretAuthority?.onPlayerReconnected(playerId, formerSlot);
252
271
  sendCatchUpTo(playerId, rejoinPlan, 'rejoin');
253
272
  return { accepted: true, slot: formerSlot, rejoined: true };
254
273
  }
@@ -267,6 +286,7 @@ function createDeterministicAuthorityRoom(transport, config) {
267
286
  slotByPlayer.set(playerId, slot);
268
287
  everSlotByPlayer.set(playerId, slot);
269
288
  sendSessionStart(playerId, slot);
289
+ secretAuthority?.onPlayerJoined(playerId, slot);
270
290
  sendCatchUpTo(playerId, plan, 'join');
271
291
  return { accepted: true, slot };
272
292
  }
@@ -286,6 +306,7 @@ function createDeterministicAuthorityRoom(transport, config) {
286
306
  return false;
287
307
  spectators.add(playerId);
288
308
  sendSessionStart(playerId, -1, 'spectator');
309
+ secretAuthority?.onSpectatorJoined(playerId);
289
310
  sendCatchUpTo(playerId, plan, 'join');
290
311
  return true;
291
312
  }
@@ -312,8 +333,14 @@ function createDeterministicAuthorityRoom(transport, config) {
312
333
  // client that fell behind the snapshot tick, so the stored snapshot rides
313
334
  // along — the client applies it only when it is ahead of its own state.
314
335
  const reconnectPlan = !historyReachesTickZero() && storedSnapshot !== undefined ? 'snapshot' : 'history';
336
+ secretAuthority?.onPlayerReconnected(playerId, slot);
315
337
  sendCatchUpTo(playerId, reconnectPlan, 'reconnect');
316
338
  }
339
+ function onPlayerSecretMessage(playerId, envelope) {
340
+ if (secretAuthority === undefined)
341
+ return;
342
+ secretAuthority.onPlayerRequest(playerId, slotByPlayer.get(playerId) ?? -1, envelope);
343
+ }
317
344
  function onPlayerMessage(playerId, envelope) {
318
345
  // Fail closed: unknown/malformed messages are dropped. Snapshot transfer
319
346
  // envelopes are the OTHER frozen wire family (schemaVersion vs v field) —
@@ -519,7 +546,7 @@ function createDeterministicAuthorityRoom(transport, config) {
519
546
  donorId,
520
547
  minFrame: tick + 1,
521
548
  startedAt: now,
522
- collector: (0, session_snapshot_js_1.createSnapshotTransferCollector)(),
549
+ collector: (0, session_snapshot_js_1.createSnapshotTransferCollector)(config.maxSnapshotBytes === undefined ? {} : { maxSnapshotBytes: config.maxSnapshotBytes }),
523
550
  isRetry,
524
551
  };
525
552
  transport.sendTo(donorId, sessionMsgType, (0, local_authority_wire_js_1.createDeterministicLocalAuthoritySnapshotRequestEnvelope)(nextWireSequence(), { descriptorHash: roomDescriptorHash }, tick + 1));
@@ -574,7 +601,10 @@ function createDeterministicAuthorityRoom(transport, config) {
574
601
  // Fixed roster for v1 (late-join/reconnect is M5). Dropping the mapping stops accepting the
575
602
  // departed player's input; their slot stays reserved because `assignedSlots` never decreases,
576
603
  // so the authority substitutes for it rather than handing the number to a new joiner.
604
+ const slot = slotByPlayer.get(playerId);
577
605
  slotByPlayer.delete(playerId);
606
+ if (slot !== undefined)
607
+ secretAuthority?.onPlayerLeft(playerId, slot);
578
608
  if (pendingTransfer?.donorId === playerId) {
579
609
  failPendingTransfer('snapshot-donor-left');
580
610
  }
@@ -589,6 +619,10 @@ function createDeterministicAuthorityRoom(transport, config) {
589
619
  return;
590
620
  }
591
621
  now += 1;
622
+ authority.recordOccupiedSlots(now, [...slotByPlayer.values()]);
623
+ for (const command of secretAuthority?.onTick(now, [...slotByPlayer.values()]) ?? []) {
624
+ authority.queueCommand(now, command);
625
+ }
592
626
  authority.advance(now);
593
627
  maintainSnapshotAcquisition();
594
628
  if (authority.confirmedThrough < 0) {
@@ -639,6 +673,7 @@ function createDeterministicAuthorityRoom(transport, config) {
639
673
  onPlayerJoin,
640
674
  onSpectatorJoin,
641
675
  onPlayerMessage,
676
+ onPlayerSecretMessage,
642
677
  onPlayerLeave,
643
678
  onPlayerReconnected,
644
679
  onTick,
@@ -675,6 +710,7 @@ function createDeterministicAuthorityRoom(transport, config) {
675
710
  if (plan === 'unavailable')
676
711
  return false;
677
712
  sendSessionStart(playerId, slot);
713
+ secretAuthority?.onPlayerReconnected(playerId, slot);
678
714
  sendCatchUpTo(playerId, plan, 'resync');
679
715
  return true;
680
716
  },
@@ -700,5 +736,16 @@ function createDeterministicAuthorityRoom(transport, config) {
700
736
  frames: authority.confirmedFrameRange(0, authority.confirmedThrough),
701
737
  };
702
738
  },
739
+ secretSnapshot() {
740
+ return secretAuthority?.snapshot();
741
+ },
703
742
  };
704
743
  }
744
+ function requireSecretAuthorityId(authorityId) {
745
+ (0, errors_js_1.invariant)(typeof authorityId === 'string' && authorityId.length > 0, 'secret authorityId is required', 'authority-room.secret-authority-id');
746
+ return authorityId;
747
+ }
748
+ function requireSecretCrypto(crypto) {
749
+ (0, errors_js_1.invariant)(crypto !== undefined, 'secret signing is unavailable', 'authority-room.secret-signing');
750
+ return crypto;
751
+ }
@@ -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.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.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;
7
+ 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.stepDeterministicCosmeticPhysicsWorld3D = exports.setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D = exports.createDeterministicCosmeticPhysicsWorld3D = exports.cookDeterministicVoxelChunkCollider3D = 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.applyDeterministicWorldEdit3D = 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 = exports.bytesToBase64 = exports.base64ToBytes = exports.exportPromotionSnapshot = exports.createSessionFromPromotionSnapshot = exports.createSyncplaySession = 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; } });
@@ -190,6 +190,7 @@ Object.defineProperty(exports, "stepDeterministicPhysicsWorld2D", { enumerable:
190
190
  var physics3d_js_1 = require("./physics3d.js");
191
191
  Object.defineProperty(exports, "applyDeterministicImpulse3D", { enumerable: true, get: function () { return physics3d_js_1.applyDeterministicImpulse3D; } });
192
192
  Object.defineProperty(exports, "applyDeterministicImpulses3D", { enumerable: true, get: function () { return physics3d_js_1.applyDeterministicImpulses3D; } });
193
+ Object.defineProperty(exports, "applyDeterministicWorldEdit3D", { enumerable: true, get: function () { return physics3d_js_1.applyDeterministicWorldEdit3D; } });
193
194
  Object.defineProperty(exports, "contactDetailsDeterministicPhysics3D", { enumerable: true, get: function () { return physics3d_js_1.contactDetailsDeterministicPhysics3D; } });
194
195
  Object.defineProperty(exports, "createDeterministicPhysicsWorld3D", { enumerable: true, get: function () { return physics3d_js_1.createDeterministicPhysicsWorld3D; } });
195
196
  Object.defineProperty(exports, "deriveInverseInertia", { enumerable: true, get: function () { return physics3d_js_1.deriveInverseInertia; } });
@@ -203,6 +204,12 @@ Object.defineProperty(exports, "setDeterministicBodyVelocity3D", { enumerable: t
203
204
  Object.defineProperty(exports, "shapeCastDeterministicPhysics3D", { enumerable: true, get: function () { return physics3d_js_1.shapeCastDeterministicPhysics3D; } });
204
205
  Object.defineProperty(exports, "shapeCastDeterministicPhysics3DDetailed", { enumerable: true, get: function () { return physics3d_js_1.shapeCastDeterministicPhysics3DDetailed; } });
205
206
  Object.defineProperty(exports, "stepDeterministicPhysicsWorld3D", { enumerable: true, get: function () { return physics3d_js_1.stepDeterministicPhysicsWorld3D; } });
207
+ var physics3d_voxel_js_1 = require("./physics3d-voxel.js");
208
+ Object.defineProperty(exports, "cookDeterministicVoxelChunkCollider3D", { enumerable: true, get: function () { return physics3d_voxel_js_1.cookDeterministicVoxelChunkCollider3D; } });
209
+ var cosmetic_physics3d_js_1 = require("./cosmetic-physics3d.js");
210
+ Object.defineProperty(exports, "createDeterministicCosmeticPhysicsWorld3D", { enumerable: true, get: function () { return cosmetic_physics3d_js_1.createDeterministicCosmeticPhysicsWorld3D; } });
211
+ Object.defineProperty(exports, "setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D", { enumerable: true, get: function () { return cosmetic_physics3d_js_1.setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D; } });
212
+ Object.defineProperty(exports, "stepDeterministicCosmeticPhysicsWorld3D", { enumerable: true, get: function () { return cosmetic_physics3d_js_1.stepDeterministicCosmeticPhysicsWorld3D; } });
206
213
  var physics3d_vehicle_js_1 = require("./physics3d-vehicle.js");
207
214
  Object.defineProperty(exports, "stepDeterministicVehicle3D", { enumerable: true, get: function () { return physics3d_vehicle_js_1.stepDeterministicVehicle3D; } });
208
215
  var r3f_render_adapter_js_1 = require("./r3f-render-adapter.js");
@@ -268,3 +275,17 @@ Object.defineProperty(exports, "createDeterministicSparseSet", { enumerable: tru
268
275
  var system_lifecycle_js_1 = require("./system-lifecycle.js");
269
276
  Object.defineProperty(exports, "createDeterministicLifecycleRunner", { enumerable: true, get: function () { return system_lifecycle_js_1.createDeterministicLifecycleRunner; } });
270
277
  Object.defineProperty(exports, "summarizeDeterministicLifecycleCounters", { enumerable: true, get: function () { return system_lifecycle_js_1.summarizeDeterministicLifecycleCounters; } });
278
+ var secret_config_js_1 = require("./secret-config.js");
279
+ Object.defineProperty(exports, "syncplaySecretConfigHash", { enumerable: true, get: function () { return secret_config_js_1.syncplaySecretConfigHash; } });
280
+ Object.defineProperty(exports, "syncplaySecretLimits", { enumerable: true, get: function () { return secret_config_js_1.syncplaySecretLimits; } });
281
+ Object.defineProperty(exports, "validateSyncplaySecretSystemsConfig", { enumerable: true, get: function () { return secret_config_js_1.validateSyncplaySecretSystemsConfig; } });
282
+ var secret_protocol_js_1 = require("./secret-protocol.js");
283
+ Object.defineProperty(exports, "decodeSyncplaySecretPrivateMessage", { enumerable: true, get: function () { return secret_protocol_js_1.decodeSyncplaySecretPrivateMessage; } });
284
+ Object.defineProperty(exports, "isSyncplaySecretCommand", { enumerable: true, get: function () { return secret_protocol_js_1.isSyncplaySecretCommand; } });
285
+ Object.defineProperty(exports, "syncplaySecretProtocol", { enumerable: true, get: function () { return secret_protocol_js_1.syncplaySecretProtocol; } });
286
+ var secret_crypto_js_1 = require("./secret-crypto.js");
287
+ Object.defineProperty(exports, "createBrowserSyncplaySecretCrypto", { enumerable: true, get: function () { return secret_crypto_js_1.createBrowserSyncplaySecretCrypto; } });
288
+ Object.defineProperty(exports, "verifySyncplaySignedReceipt", { enumerable: true, get: function () { return secret_crypto_js_1.verifySyncplaySignedReceipt; } });
289
+ var secret_client_js_1 = require("./secret-client.js");
290
+ Object.defineProperty(exports, "createSyncplaySecretEventReader", { enumerable: true, get: function () { return secret_client_js_1.createSyncplaySecretEventReader; } });
291
+ Object.defineProperty(exports, "createSyncplaySecretsClient", { enumerable: true, get: function () { return secret_client_js_1.createSyncplaySecretsClient; } });