@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.
Files changed (131) hide show
  1. package/README.md +53 -12
  2. package/dist/authority-room.d.ts +11 -0
  3. package/dist/authority-room.js +43 -0
  4. package/dist/browser.d.ts +11 -1
  5. package/dist/browser.js +5 -0
  6. package/dist/checksum-basis.d.ts +4 -0
  7. package/dist/checksum-basis.js +17 -0
  8. package/dist/cjs/authority-room.js +43 -0
  9. package/dist/cjs/browser.js +18 -2
  10. package/dist/cjs/checksum-basis.js +22 -0
  11. package/dist/cjs/collider-cooking.js +834 -0
  12. package/dist/cjs/deterministic-session.js +2 -0
  13. package/dist/cjs/engine-complete.js +1 -4
  14. package/dist/cjs/engine-parity-matrix.js +41 -9
  15. package/dist/cjs/engine-parity.js +2 -2
  16. package/dist/cjs/index.js +24 -2
  17. package/dist/cjs/input-authority.js +34 -2
  18. package/dist/cjs/instant-replay.js +4 -9
  19. package/dist/cjs/local-authority-server-runner.js +3 -1
  20. package/dist/cjs/local-authority-wire.js +20 -1
  21. package/dist/cjs/match-log.js +27 -0
  22. package/dist/cjs/movement3d.js +17 -1
  23. package/dist/cjs/networked-client.js +70 -20
  24. package/dist/cjs/node.js +23 -1
  25. package/dist/cjs/offline-session.js +52 -24
  26. package/dist/cjs/physics-cert.js +8 -3
  27. package/dist/cjs/physics3d-heightfield.js +51 -0
  28. package/dist/cjs/physics3d-hull.js +381 -0
  29. package/dist/cjs/physics3d-joints.js +255 -49
  30. package/dist/cjs/physics3d-mass.js +131 -0
  31. package/dist/cjs/physics3d-recipes.js +197 -0
  32. package/dist/cjs/physics3d-shared.js +11 -1
  33. package/dist/cjs/physics3d-solver.js +845 -98
  34. package/dist/cjs/physics3d-stacking.js +154 -0
  35. package/dist/cjs/physics3d-toi.js +274 -0
  36. package/dist/cjs/physics3d-vehicle.js +35 -10
  37. package/dist/cjs/physics3d.js +601 -38
  38. package/dist/cjs/replay.js +25 -1
  39. package/dist/cjs/runtime-support.js +71 -0
  40. package/dist/cjs/sdk-offline.js +9 -1
  41. package/dist/cjs/sdk-session.js +8 -0
  42. package/dist/cjs/secret-authority.js +647 -0
  43. package/dist/cjs/secret-client.js +615 -0
  44. package/dist/cjs/secret-command-classification.js +8 -0
  45. package/dist/cjs/secret-config.js +211 -0
  46. package/dist/cjs/secret-crypto.js +208 -0
  47. package/dist/cjs/secret-protocol.js +238 -0
  48. package/dist/cjs/session-build.js +7 -4
  49. package/dist/cjs/session-promotion.js +5 -5
  50. package/dist/cjs/synctest.js +49 -0
  51. package/dist/cjs/testing.js +4 -1
  52. package/dist/cjs/tools/static-checker.js +61 -13
  53. package/dist/cli.js +193 -48
  54. package/dist/collider-cooking.d.ts +141 -0
  55. package/dist/collider-cooking.js +829 -0
  56. package/dist/deterministic-primitives.d.ts +1 -0
  57. package/dist/deterministic-session.js +2 -0
  58. package/dist/engine-complete.js +1 -4
  59. package/dist/engine-parity-matrix.js +41 -9
  60. package/dist/engine-parity.js +2 -2
  61. package/dist/index.d.ts +12 -1
  62. package/dist/index.js +6 -0
  63. package/dist/input-authority.d.ts +3 -0
  64. package/dist/input-authority.js +35 -3
  65. package/dist/instant-replay.d.ts +2 -2
  66. package/dist/instant-replay.js +4 -9
  67. package/dist/local-authority-server-runner.d.ts +1 -0
  68. package/dist/local-authority-server-runner.js +2 -1
  69. package/dist/local-authority-wire.js +20 -1
  70. package/dist/match-log.d.ts +4 -1
  71. package/dist/match-log.js +27 -0
  72. package/dist/movement3d.js +18 -2
  73. package/dist/networked-client.d.ts +8 -0
  74. package/dist/networked-client.js +70 -20
  75. package/dist/node.d.ts +7 -0
  76. package/dist/node.js +6 -0
  77. package/dist/offline-session.js +53 -25
  78. package/dist/physics-cert.js +8 -3
  79. package/dist/physics3d-heightfield.d.ts +17 -0
  80. package/dist/physics3d-heightfield.js +47 -0
  81. package/dist/physics3d-hull.d.ts +53 -0
  82. package/dist/physics3d-hull.js +376 -0
  83. package/dist/physics3d-joints.d.ts +13 -1
  84. package/dist/physics3d-joints.js +256 -50
  85. package/dist/physics3d-mass.d.ts +11 -0
  86. package/dist/physics3d-mass.js +126 -0
  87. package/dist/physics3d-recipes.d.ts +29 -0
  88. package/dist/physics3d-recipes.js +193 -0
  89. package/dist/physics3d-shared.d.ts +1 -0
  90. package/dist/physics3d-shared.js +10 -1
  91. package/dist/physics3d-solver.d.ts +78 -2
  92. package/dist/physics3d-solver.js +842 -98
  93. package/dist/physics3d-stacking.d.ts +26 -0
  94. package/dist/physics3d-stacking.js +147 -0
  95. package/dist/physics3d-toi.d.ts +79 -0
  96. package/dist/physics3d-toi.js +267 -0
  97. package/dist/physics3d-vehicle.js +35 -10
  98. package/dist/physics3d.d.ts +28 -3
  99. package/dist/physics3d.js +601 -40
  100. package/dist/replay.d.ts +5 -0
  101. package/dist/replay.js +25 -1
  102. package/dist/reports.d.ts +6 -1
  103. package/dist/runtime-support.d.ts +60 -0
  104. package/dist/runtime-support.js +66 -0
  105. package/dist/sdk-offline.d.ts +4 -0
  106. package/dist/sdk-offline.js +9 -1
  107. package/dist/sdk-session.d.ts +4 -0
  108. package/dist/sdk-session.js +8 -0
  109. package/dist/secret-authority.d.ts +90 -0
  110. package/dist/secret-authority.js +643 -0
  111. package/dist/secret-client.d.ts +89 -0
  112. package/dist/secret-client.js +611 -0
  113. package/dist/secret-command-classification.d.ts +1 -0
  114. package/dist/secret-command-classification.js +5 -0
  115. package/dist/secret-config.d.ts +45 -0
  116. package/dist/secret-config.js +206 -0
  117. package/dist/secret-crypto.d.ts +29 -0
  118. package/dist/secret-crypto.js +190 -0
  119. package/dist/secret-protocol.d.ts +181 -0
  120. package/dist/secret-protocol.js +230 -0
  121. package/dist/session-build.d.ts +16 -0
  122. package/dist/session-build.js +7 -4
  123. package/dist/session-promotion.js +5 -5
  124. package/dist/synctest.d.ts +1 -1
  125. package/dist/synctest.js +49 -0
  126. package/dist/testing.d.ts +2 -0
  127. package/dist/testing.js +1 -0
  128. package/dist/tools/static-checker.d.ts +11 -0
  129. package/dist/tools/static-checker.js +60 -13
  130. package/dist/types.d.ts +21 -2
  131. 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
- ## Preview Status
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.
17
-
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.
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.
@@ -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;
@@ -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
+ }
@@ -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.createSnapshotTransferCollector = 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.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 = 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
+ }