@series-inc/rundot-syncplay 5.23.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/README.md +610 -0
  2. package/dist/animation.d.ts +119 -0
  3. package/dist/animation.js +488 -0
  4. package/dist/asset-cooking.d.ts +82 -0
  5. package/dist/asset-cooking.js +364 -0
  6. package/dist/authority-room.d.ts +217 -0
  7. package/dist/authority-room.js +701 -0
  8. package/dist/bot-documents.d.ts +115 -0
  9. package/dist/bot-documents.js +549 -0
  10. package/dist/bot-primitives.d.ts +152 -0
  11. package/dist/bot-primitives.js +472 -0
  12. package/dist/browser.d.ts +57 -0
  13. package/dist/browser.js +53 -0
  14. package/dist/canonical.d.ts +12 -0
  15. package/dist/canonical.js +288 -0
  16. package/dist/certification.d.ts +455 -0
  17. package/dist/certification.js +1855 -0
  18. package/dist/chaos-certification.d.ts +56 -0
  19. package/dist/chaos-certification.js +1397 -0
  20. package/dist/cjs/animation.js +495 -0
  21. package/dist/cjs/asset-cooking.js +372 -0
  22. package/dist/cjs/authority-room.js +704 -0
  23. package/dist/cjs/bot-documents.js +556 -0
  24. package/dist/cjs/bot-primitives.js +482 -0
  25. package/dist/cjs/browser.js +96 -0
  26. package/dist/cjs/canonical.js +299 -0
  27. package/dist/cjs/certification.js +1860 -0
  28. package/dist/cjs/chaos-certification.js +1402 -0
  29. package/dist/cjs/collections.js +36 -0
  30. package/dist/cjs/collision.js +96 -0
  31. package/dist/cjs/commands.js +987 -0
  32. package/dist/cjs/config-bundle.js +491 -0
  33. package/dist/cjs/creator.js +30 -0
  34. package/dist/cjs/deterministic-primitives.js +134 -0
  35. package/dist/cjs/deterministic-session.js +1410 -0
  36. package/dist/cjs/ecs-lite.js +155 -0
  37. package/dist/cjs/effects-adapter.js +97 -0
  38. package/dist/cjs/engine-complete.js +2297 -0
  39. package/dist/cjs/engine-core-proof.js +70 -0
  40. package/dist/cjs/engine-parity-matrix.js +874 -0
  41. package/dist/cjs/engine-parity.js +3183 -0
  42. package/dist/cjs/engine-runtime.js +3141 -0
  43. package/dist/cjs/errors.js +22 -0
  44. package/dist/cjs/events.js +247 -0
  45. package/dist/cjs/examples/counter-duel.js +157 -0
  46. package/dist/cjs/examples/fixed-arena.js +368 -0
  47. package/dist/cjs/index.js +229 -0
  48. package/dist/cjs/input-authority.js +150 -0
  49. package/dist/cjs/input-button.js +256 -0
  50. package/dist/cjs/input-timeline.js +418 -0
  51. package/dist/cjs/instant-replay.js +66 -0
  52. package/dist/cjs/lag-compensation3d.js +837 -0
  53. package/dist/cjs/local-authority-server-runner.js +1121 -0
  54. package/dist/cjs/local-authority-wire.js +1497 -0
  55. package/dist/cjs/local-authority.js +817 -0
  56. package/dist/cjs/local-ws-adapter.js +150 -0
  57. package/dist/cjs/match-log.js +61 -0
  58. package/dist/cjs/math.js +157 -0
  59. package/dist/cjs/mock-network-adapter.js +26 -0
  60. package/dist/cjs/movement.js +703 -0
  61. package/dist/cjs/movement3d.js +1246 -0
  62. package/dist/cjs/multiclient-authority.js +308 -0
  63. package/dist/cjs/multiplayer-service.js +434 -0
  64. package/dist/cjs/navigation.js +347 -0
  65. package/dist/cjs/navmesh.js +1437 -0
  66. package/dist/cjs/networked-client.js +691 -0
  67. package/dist/cjs/networked-input-authority.js +443 -0
  68. package/dist/cjs/node.js +47 -0
  69. package/dist/cjs/number-lab.js +78 -0
  70. package/dist/cjs/offline-session.js +370 -0
  71. package/dist/cjs/package.json +1 -0
  72. package/dist/cjs/pause-resume.js +43 -0
  73. package/dist/cjs/physics-cert.js +426 -0
  74. package/dist/cjs/physics2d.js +751 -0
  75. package/dist/cjs/physics3d.js +1996 -0
  76. package/dist/cjs/prediction-culling.js +203 -0
  77. package/dist/cjs/protocol-lab.js +1299 -0
  78. package/dist/cjs/r3f-render-adapter.js +209 -0
  79. package/dist/cjs/random.js +59 -0
  80. package/dist/cjs/replay-bundle.js +1598 -0
  81. package/dist/cjs/replay-inspect.js +236 -0
  82. package/dist/cjs/replay-scoring.js +68 -0
  83. package/dist/cjs/replay.js +356 -0
  84. package/dist/cjs/reports.js +181 -0
  85. package/dist/cjs/rollback-history.js +251 -0
  86. package/dist/cjs/runtime-guards.js +111 -0
  87. package/dist/cjs/sample-lab.js +145 -0
  88. package/dist/cjs/sample-scenes.js +752 -0
  89. package/dist/cjs/schema-artifacts.js +359 -0
  90. package/dist/cjs/schema-codegen.js +544 -0
  91. package/dist/cjs/schema-dsl-binary.js +655 -0
  92. package/dist/cjs/schema.js +319 -0
  93. package/dist/cjs/sdk-offline.js +99 -0
  94. package/dist/cjs/session-promotion.js +74 -0
  95. package/dist/cjs/session-snapshot.js +265 -0
  96. package/dist/cjs/session-wire.js +216 -0
  97. package/dist/cjs/signals.js +103 -0
  98. package/dist/cjs/sparse-set.js +169 -0
  99. package/dist/cjs/synctest.js +136 -0
  100. package/dist/cjs/system-lifecycle.js +286 -0
  101. package/dist/cjs/timers.js +61 -0
  102. package/dist/cjs/tools/static-checker.js +1468 -0
  103. package/dist/cjs/types.js +2 -0
  104. package/dist/cjs/venus-mp-budget.js +125 -0
  105. package/dist/cjs/ws-frame.js +255 -0
  106. package/dist/cli.d.ts +2 -0
  107. package/dist/cli.js +11379 -0
  108. package/dist/collections.d.ts +9 -0
  109. package/dist/collections.js +30 -0
  110. package/dist/collision.d.ts +25 -0
  111. package/dist/collision.js +85 -0
  112. package/dist/commands.d.ts +137 -0
  113. package/dist/commands.js +977 -0
  114. package/dist/config-bundle.d.ts +89 -0
  115. package/dist/config-bundle.js +483 -0
  116. package/dist/creator.d.ts +20 -0
  117. package/dist/creator.js +11 -0
  118. package/dist/deterministic-primitives.d.ts +59 -0
  119. package/dist/deterministic-primitives.js +126 -0
  120. package/dist/deterministic-session.d.ts +197 -0
  121. package/dist/deterministic-session.js +1401 -0
  122. package/dist/ecs-lite.d.ts +26 -0
  123. package/dist/ecs-lite.js +152 -0
  124. package/dist/effects-adapter.d.ts +21 -0
  125. package/dist/effects-adapter.js +94 -0
  126. package/dist/engine-complete.d.ts +45 -0
  127. package/dist/engine-complete.js +2292 -0
  128. package/dist/engine-core-proof.d.ts +26 -0
  129. package/dist/engine-core-proof.js +66 -0
  130. package/dist/engine-parity-matrix.d.ts +300 -0
  131. package/dist/engine-parity-matrix.js +869 -0
  132. package/dist/engine-parity.d.ts +74 -0
  133. package/dist/engine-parity.js +3178 -0
  134. package/dist/engine-runtime.d.ts +351 -0
  135. package/dist/engine-runtime.js +3123 -0
  136. package/dist/errors.d.ts +11 -0
  137. package/dist/errors.js +17 -0
  138. package/dist/events.d.ts +45 -0
  139. package/dist/events.js +242 -0
  140. package/dist/examples/counter-duel.d.ts +43 -0
  141. package/dist/examples/counter-duel.js +150 -0
  142. package/dist/examples/fixed-arena.d.ts +110 -0
  143. package/dist/examples/fixed-arena.js +356 -0
  144. package/dist/index.d.ts +101 -0
  145. package/dist/index.js +66 -0
  146. package/dist/input-authority.d.ts +82 -0
  147. package/dist/input-authority.js +147 -0
  148. package/dist/input-button.d.ts +36 -0
  149. package/dist/input-button.js +249 -0
  150. package/dist/input-timeline.d.ts +65 -0
  151. package/dist/input-timeline.js +412 -0
  152. package/dist/instant-replay.d.ts +59 -0
  153. package/dist/instant-replay.js +62 -0
  154. package/dist/lag-compensation3d.d.ts +146 -0
  155. package/dist/lag-compensation3d.js +827 -0
  156. package/dist/local-authority-server-runner.d.ts +79 -0
  157. package/dist/local-authority-server-runner.js +1118 -0
  158. package/dist/local-authority-wire.d.ts +142 -0
  159. package/dist/local-authority-wire.js +1477 -0
  160. package/dist/local-authority.d.ts +160 -0
  161. package/dist/local-authority.js +810 -0
  162. package/dist/local-ws-adapter.d.ts +14 -0
  163. package/dist/local-ws-adapter.js +146 -0
  164. package/dist/match-log.d.ts +74 -0
  165. package/dist/match-log.js +57 -0
  166. package/dist/math.d.ts +41 -0
  167. package/dist/math.js +154 -0
  168. package/dist/mock-network-adapter.d.ts +13 -0
  169. package/dist/mock-network-adapter.js +23 -0
  170. package/dist/movement.d.ts +155 -0
  171. package/dist/movement.js +694 -0
  172. package/dist/movement3d.d.ts +195 -0
  173. package/dist/movement3d.js +1238 -0
  174. package/dist/multiclient-authority.d.ts +30 -0
  175. package/dist/multiclient-authority.js +305 -0
  176. package/dist/multiplayer-service.d.ts +153 -0
  177. package/dist/multiplayer-service.js +429 -0
  178. package/dist/navigation.d.ts +91 -0
  179. package/dist/navigation.js +338 -0
  180. package/dist/navmesh.d.ts +236 -0
  181. package/dist/navmesh.js +1423 -0
  182. package/dist/networked-client.d.ts +124 -0
  183. package/dist/networked-client.js +688 -0
  184. package/dist/networked-input-authority.d.ts +51 -0
  185. package/dist/networked-input-authority.js +439 -0
  186. package/dist/node.d.ts +22 -0
  187. package/dist/node.js +19 -0
  188. package/dist/number-lab.d.ts +15 -0
  189. package/dist/number-lab.js +75 -0
  190. package/dist/offline-session.d.ts +2 -0
  191. package/dist/offline-session.js +367 -0
  192. package/dist/pause-resume.d.ts +48 -0
  193. package/dist/pause-resume.js +40 -0
  194. package/dist/physics-cert.d.ts +40 -0
  195. package/dist/physics-cert.js +389 -0
  196. package/dist/physics2d.d.ts +142 -0
  197. package/dist/physics2d.js +741 -0
  198. package/dist/physics3d.d.ts +285 -0
  199. package/dist/physics3d.js +1979 -0
  200. package/dist/prediction-culling.d.ts +34 -0
  201. package/dist/prediction-culling.js +200 -0
  202. package/dist/protocol-lab.d.ts +115 -0
  203. package/dist/protocol-lab.js +1295 -0
  204. package/dist/r3f-render-adapter.d.ts +61 -0
  205. package/dist/r3f-render-adapter.js +203 -0
  206. package/dist/random.d.ts +13 -0
  207. package/dist/random.js +55 -0
  208. package/dist/replay-bundle.d.ts +150 -0
  209. package/dist/replay-bundle.js +1586 -0
  210. package/dist/replay-inspect.d.ts +60 -0
  211. package/dist/replay-inspect.js +228 -0
  212. package/dist/replay-scoring.d.ts +28 -0
  213. package/dist/replay-scoring.js +65 -0
  214. package/dist/replay.d.ts +17 -0
  215. package/dist/replay.js +351 -0
  216. package/dist/reports.d.ts +176 -0
  217. package/dist/reports.js +174 -0
  218. package/dist/rollback-history.d.ts +46 -0
  219. package/dist/rollback-history.js +247 -0
  220. package/dist/runtime-guards.d.ts +1 -0
  221. package/dist/runtime-guards.js +108 -0
  222. package/dist/sample-lab.d.ts +32 -0
  223. package/dist/sample-lab.js +134 -0
  224. package/dist/sample-scenes.d.ts +86 -0
  225. package/dist/sample-scenes.js +745 -0
  226. package/dist/schema-artifacts.d.ts +49 -0
  227. package/dist/schema-artifacts.js +356 -0
  228. package/dist/schema-codegen.d.ts +21 -0
  229. package/dist/schema-codegen.js +538 -0
  230. package/dist/schema-dsl-binary.d.ts +75 -0
  231. package/dist/schema-dsl-binary.js +648 -0
  232. package/dist/schema.d.ts +87 -0
  233. package/dist/schema.js +315 -0
  234. package/dist/sdk-offline.d.ts +34 -0
  235. package/dist/sdk-offline.js +96 -0
  236. package/dist/session-promotion.d.ts +26 -0
  237. package/dist/session-promotion.js +70 -0
  238. package/dist/session-snapshot.d.ts +95 -0
  239. package/dist/session-snapshot.js +255 -0
  240. package/dist/session-wire.d.ts +89 -0
  241. package/dist/session-wire.js +211 -0
  242. package/dist/signals.d.ts +31 -0
  243. package/dist/signals.js +99 -0
  244. package/dist/sparse-set.d.ts +34 -0
  245. package/dist/sparse-set.js +165 -0
  246. package/dist/synctest.d.ts +31 -0
  247. package/dist/synctest.js +133 -0
  248. package/dist/system-lifecycle.d.ts +72 -0
  249. package/dist/system-lifecycle.js +281 -0
  250. package/dist/timers.d.ts +17 -0
  251. package/dist/timers.js +53 -0
  252. package/dist/tools/index.d.ts +4 -0
  253. package/dist/tools/index.js +4 -0
  254. package/dist/tools/static-checker.d.ts +31 -0
  255. package/dist/tools/static-checker.js +1462 -0
  256. package/dist/tools/vite-plugin.d.ts +69 -0
  257. package/dist/tools/vite-plugin.js +203 -0
  258. package/dist/types.d.ts +148 -0
  259. package/dist/types.js +1 -0
  260. package/dist/venus-mp-budget.d.ts +29 -0
  261. package/dist/venus-mp-budget.js +122 -0
  262. package/dist/ws-frame.d.ts +49 -0
  263. package/dist/ws-frame.js +247 -0
  264. package/package.json +169 -0
@@ -0,0 +1,752 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deterministicSampleSceneIds = void 0;
4
+ exports.runDeterministicSampleScene = runDeterministicSampleScene;
5
+ exports.runAllDeterministicSampleScenes = runAllDeterministicSampleScenes;
6
+ exports.createDeterministicSampleSceneCertificationTrace = createDeterministicSampleSceneCertificationTrace;
7
+ exports.runDeterministicIntegratedHeadlessScene = runDeterministicIntegratedHeadlessScene;
8
+ const canonical_js_1 = require("./canonical.js");
9
+ const animation_js_1 = require("./animation.js");
10
+ const bot_documents_js_1 = require("./bot-documents.js");
11
+ const commands_js_1 = require("./commands.js");
12
+ const config_bundle_js_1 = require("./config-bundle.js");
13
+ const engine_runtime_js_1 = require("./engine-runtime.js");
14
+ const movement3d_js_1 = require("./movement3d.js");
15
+ const navmesh_js_1 = require("./navmesh.js");
16
+ const physics3d_js_1 = require("./physics3d.js");
17
+ const replay_bundle_js_1 = require("./replay-bundle.js");
18
+ exports.deterministicSampleSceneIds = [
19
+ 'asteroids',
20
+ 'simple-fps',
21
+ 'twin-stick',
22
+ 'platform-shooter',
23
+ 'arena-brawler',
24
+ 'vehicle',
25
+ 'mini-golf',
26
+ 'waiting-room',
27
+ 'boat-water',
28
+ 'animator',
29
+ ];
30
+ function runDeterministicSampleScene(sampleId, frames, seed) {
31
+ const control = stepSampleScene(sampleId, frames, seed, -1);
32
+ const restored = stepSampleScene(sampleId, frames, seed, Math.floor(frames / 2));
33
+ const subsystemMetrics = sampleSubsystemProofMetrics(sampleId, Math.min(frames, 48), seed);
34
+ const metrics = {
35
+ ...restored.metrics,
36
+ ...subsystemMetrics,
37
+ };
38
+ return {
39
+ sampleId,
40
+ checksum: (0, canonical_js_1.defaultChecksum)({ sampleId, baseChecksum: control.checksum, subsystemMetrics }),
41
+ rollbackChecksum: (0, canonical_js_1.defaultChecksum)({ sampleId, baseChecksum: restored.checksum, subsystemMetrics }),
42
+ events: restored.events,
43
+ invariants: sampleInvariants(sampleId, metrics, control.checksum === restored.checksum),
44
+ metrics,
45
+ };
46
+ }
47
+ function runAllDeterministicSampleScenes(frames, seed) {
48
+ return exports.deterministicSampleSceneIds.map((sampleId, index) => runDeterministicSampleScene(sampleId, frames, seed + index));
49
+ }
50
+ function createDeterministicSampleSceneCertificationTrace(frames, players, seed) {
51
+ const samples = exports.deterministicSampleSceneIds.map((sampleId, index) => createDeterministicSampleSceneTrace(sampleId, frames, seed + index));
52
+ const integratedProof = runDeterministicIntegratedHeadlessScene(Math.min(frames, 1_000), Math.min(players, 128), seed + 991);
53
+ const integrated = {
54
+ checksum: integratedProof.checksum,
55
+ replayChecksum: integratedProof.replayChecksum,
56
+ rollbackChecksum: integratedProof.rollbackChecksum,
57
+ replayVerified: integratedProof.replayVerified,
58
+ rollbackEquivalent: integratedProof.rollbackEquivalent,
59
+ replaySnapshots: integratedProof.metrics.replaySnapshots,
60
+ commandsAccepted: integratedProof.metrics.commandsAccepted,
61
+ physicsContacts: integratedProof.metrics.physicsContacts,
62
+ kccCorrections: integratedProof.metrics.kccCorrections,
63
+ navReplans: integratedProof.metrics.navReplans,
64
+ botTraceRows: integratedProof.metrics.botTraceRows,
65
+ animationEvents: integratedProof.metrics.animationEvents,
66
+ };
67
+ const withoutHash = {
68
+ schemaVersion: 1,
69
+ kind: 'deterministic-sample-scene-certification-trace',
70
+ frames,
71
+ players,
72
+ seed,
73
+ samples,
74
+ integrated,
75
+ sampleCount: samples.length,
76
+ mechanicCount: samples.reduce((sum, sample) => sum + sample.mechanicCount, 0),
77
+ rollbackEquivalent: samples.every((sample) => sample.rollbackEquivalent) && integrated.rollbackEquivalent,
78
+ };
79
+ return {
80
+ ...withoutHash,
81
+ traceHash: (0, canonical_js_1.defaultChecksum)(withoutHash),
82
+ };
83
+ }
84
+ function runDeterministicIntegratedHeadlessScene(frames, players, seed) {
85
+ const descriptor = integratedRuntimeDescriptor();
86
+ const systems = integratedRuntimeSystems();
87
+ const config = (0, config_bundle_js_1.cookDeterministicConfigBundle)({
88
+ version: 1,
89
+ engineHash: 'integrated-engine@1',
90
+ schemaHash: (0, canonical_js_1.defaultChecksum)(descriptor),
91
+ sessionConfig: { players, tickRate: 60 },
92
+ simulationConfig: { numericMode: 'fixed', rollback: true },
93
+ runtimeConfig: { maxRollbackFrames: 32 },
94
+ playerConfig: { team: 'blue' },
95
+ assets: {
96
+ schemaVersion: 1,
97
+ engineVersion: 'integrated-engine@1',
98
+ assets: [
99
+ { id: 'ability.dash', type: 'ability', references: ['player.prototype'] },
100
+ { id: 'player.prototype', type: 'prototype', prototype: true, components: { hp: 100, speed: 4 } },
101
+ ],
102
+ },
103
+ });
104
+ const navmesh = (0, navmesh_js_1.cookDeterministicNavmesh)({
105
+ id: 'integrated-arena',
106
+ polygons: [
107
+ { id: 'a', vertices: [{ x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }] },
108
+ { id: 'b', vertices: [{ x: 10, y: 0 }, { x: 20, y: 0 }, { x: 20, y: 10 }, { x: 10, y: 10 }] },
109
+ ],
110
+ links: [{ from: 'a', to: 'b', bidirectional: true }],
111
+ });
112
+ const botDocument = {
113
+ id: 'integrated-bot',
114
+ version: 1,
115
+ blackboard: [{ key: 'enemyNear', value: true }, { key: 'threat', value: 2 }],
116
+ assetRefs: ['ability.dash'],
117
+ behaviorTree: { id: 'root', type: 'selector', children: [{ id: 'dash', type: 'action', action: 'dash' }] },
118
+ utility: [{ id: 'dash-utility', inputKey: 'threat', action: 'dash', min: 0, max: 4, weight: 5 }],
119
+ };
120
+ const compiledBot = (0, bot_documents_js_1.compileDeterministicBotDocument)(botDocument, ['ability.dash']);
121
+ const botTrace = (0, bot_documents_js_1.traceDeterministicBotDocument)(botDocument, [0, 1], Math.max(1, frames), ['ability.dash']);
122
+ const animationDescriptor = integratedAnimationDescriptor();
123
+ const animation = (0, animation_js_1.cookDeterministicAnimation)(animationDescriptor);
124
+ const commandTimeline = (0, commands_js_1.createDeterministicCommandTimeline)({
125
+ playerCount: players,
126
+ currentFrame: 0,
127
+ channels: [{ id: 'ability', reliable: true, allowPrediction: true, maxPayloadBytes: 128, maxFrameLead: frames + 1 }],
128
+ validAssetRefs: ['ability.dash'],
129
+ });
130
+ recordIntegratedCommands(commandTimeline, frames, players);
131
+ const replayFrames = Math.min(frames, 1_000);
132
+ const control = stepIntegratedScene({
133
+ descriptor,
134
+ systems,
135
+ frames,
136
+ seed,
137
+ commandTimeline,
138
+ navmesh,
139
+ botTrace,
140
+ animationDescriptor,
141
+ configHash: config.configHash,
142
+ rollbackFrame: -1,
143
+ replayFrameLimit: replayFrames,
144
+ });
145
+ const restored = stepIntegratedScene({
146
+ descriptor,
147
+ systems,
148
+ frames,
149
+ seed,
150
+ commandTimeline,
151
+ navmesh,
152
+ botTrace,
153
+ animationDescriptor,
154
+ configHash: config.configHash,
155
+ rollbackFrame: Math.floor(frames / 2),
156
+ replayFrameLimit: -1,
157
+ });
158
+ const bundle = (0, replay_bundle_js_1.createDeterministicReplayBundle)({
159
+ runtimeVersion: 'integrated-headless@1',
160
+ engineHash: 'integrated-engine@1',
161
+ schemaHash: (0, canonical_js_1.defaultChecksum)(descriptor),
162
+ assetDbHash: config.assetDbHash,
163
+ configHash: config.configHash,
164
+ sampleId: 'integrated-headless',
165
+ inputHistory: control.inputHistory.filter((input) => input.frame < replayFrames),
166
+ commandHistory: control.commandHistory.filter((command) => command.frame < replayFrames),
167
+ checksums: control.snapshots
168
+ .filter((snapshot) => snapshot.frame <= replayFrames)
169
+ .map((snapshot) => ({ frame: snapshot.frame, checksum: snapshot.checksum })),
170
+ debugEvents: control.runtimeDebugEvents.filter((event) => event.frame <= replayFrames),
171
+ snapshots: control.snapshots.filter((snapshot) => snapshot.frame <= replayFrames),
172
+ metadata: {
173
+ players,
174
+ frames,
175
+ replayFrames,
176
+ replayCapped: replayFrames < frames,
177
+ botHash: compiledBot.hash,
178
+ animationHash: animation.hash,
179
+ navmeshHash: navmesh.hash,
180
+ },
181
+ });
182
+ const replay = (0, replay_bundle_js_1.verifyDeterministicFrameRuntimeReplayBundle)(bundle, {
183
+ descriptor,
184
+ systems,
185
+ seed,
186
+ beforeFrame: injectIntegratedRuntimeFrame,
187
+ });
188
+ const metrics = {
189
+ frames,
190
+ replayFrames,
191
+ replayCapped: replayFrames < frames,
192
+ players,
193
+ commandsAccepted: commandTimeline.accepted.length,
194
+ sameFrameCommandsAccepted: commandTimeline.poll(0, 0, 'ability').length,
195
+ physicsContacts: control.physicsContacts,
196
+ physicsQueries: control.physicsQueries,
197
+ kccCorrections: control.kccCorrections,
198
+ navReplans: control.navReplans,
199
+ botTraceRows: botTrace.length,
200
+ animationEvents: control.animationEvents,
201
+ replaySnapshots: bundle.snapshots.length,
202
+ debugEvents: bundle.debugEvents.length,
203
+ configHashCovered: config.configHash.length > 0,
204
+ };
205
+ const invariants = {
206
+ frameRuntimeExecuted: replay.frames === replayFrames + 1,
207
+ replayVerified: replay.result === 'pass',
208
+ rollbackEquivalent: control.checksum === restored.checksum,
209
+ commandsExecuted: metrics.commandsAccepted > frames && metrics.sameFrameCommandsAccepted >= 2,
210
+ physicsExecuted: metrics.physicsQueries >= frames && metrics.physicsContacts > 0,
211
+ kccExecuted: metrics.kccCorrections > 0,
212
+ navmeshExecuted: metrics.navReplans > 0,
213
+ botExecuted: metrics.botTraceRows >= frames,
214
+ animationExecuted: metrics.animationEvents > 0,
215
+ assetsConfigCovered: metrics.configHashCovered && config.assetDbHash.length > 0,
216
+ };
217
+ return {
218
+ checksum: control.checksum,
219
+ replayChecksum: replay.finalChecksum,
220
+ rollbackChecksum: restored.checksum,
221
+ replayVerified: replay.result === 'pass',
222
+ rollbackEquivalent: control.checksum === restored.checksum,
223
+ metrics,
224
+ invariants: Object.fromEntries(Object.entries(invariants).sort((left, right) => compareStrings(left[0], right[0]))),
225
+ };
226
+ }
227
+ function stepSampleScene(sampleId, frames, seed, restoreFrame) {
228
+ let state = { frame: 0, x: 0, y: 0, hp: 10, score: 0, phase: 0, cooldown: 0 };
229
+ let snapshot = state;
230
+ let metricsSnapshot = {};
231
+ let events = 0;
232
+ let metrics = {};
233
+ for (let frame = 0; frame < frames; frame += 1) {
234
+ if (frame === restoreFrame) {
235
+ snapshot = (0, canonical_js_1.cloneCanonical)(state);
236
+ metricsSnapshot = (0, canonical_js_1.cloneCanonical)(metrics);
237
+ }
238
+ if (restoreFrame >= 0 && frame === restoreFrame + 4) {
239
+ state = (0, canonical_js_1.cloneCanonical)(snapshot);
240
+ metrics = (0, canonical_js_1.cloneCanonical)(metricsSnapshot);
241
+ for (let replay = restoreFrame; replay < frame; replay += 1) {
242
+ state = advanceSampleState(sampleId, state, replay, seed, metrics);
243
+ }
244
+ }
245
+ state = advanceSampleState(sampleId, state, frame, seed, metrics);
246
+ events += 1 + ((frame + seed + sampleId.length) % 19 === 0 ? 1 : 0);
247
+ }
248
+ return {
249
+ checksum: (0, canonical_js_1.defaultChecksum)({ sampleId, state, metrics }),
250
+ events,
251
+ metrics: Object.fromEntries(Object.entries(metrics).sort((left, right) => compareStrings(left[0], right[0]))),
252
+ };
253
+ }
254
+ function createDeterministicSampleSceneTrace(sampleId, frames, seed) {
255
+ const result = runDeterministicSampleScene(sampleId, frames, seed);
256
+ const representativeStates = traceSampleSceneStates(sampleId, frames, seed);
257
+ const mechanics = requiredMetricKeys(sampleId).filter((mechanic) => result.invariants[mechanic] === true);
258
+ const withoutHash = {
259
+ sampleId,
260
+ frames,
261
+ checksum: result.checksum,
262
+ rollbackChecksum: result.rollbackChecksum,
263
+ rollbackEquivalent: result.invariants.rollbackEquivalent === true && result.checksum === result.rollbackChecksum,
264
+ mechanics,
265
+ mechanicCount: mechanics.length,
266
+ events: result.events,
267
+ representativeStates,
268
+ };
269
+ return {
270
+ ...withoutHash,
271
+ traceHash: (0, canonical_js_1.defaultChecksum)(withoutHash),
272
+ };
273
+ }
274
+ function traceSampleSceneStates(sampleId, frames, seed) {
275
+ const sampleFrames = [...new Set([
276
+ 0,
277
+ Math.max(0, Math.floor(frames / 2)),
278
+ Math.max(0, frames - 1),
279
+ ])].sort((left, right) => left - right);
280
+ let state = { frame: 0, x: 0, y: 0, hp: 10, score: 0, phase: 0, cooldown: 0 };
281
+ const metrics = {};
282
+ const states = [];
283
+ for (let frame = 0; frame < frames; frame += 1) {
284
+ state = advanceSampleState(sampleId, state, frame, seed, metrics);
285
+ if (sampleFrames.includes(frame)) {
286
+ states.push({
287
+ frame: state.frame,
288
+ stateChecksum: (0, canonical_js_1.defaultChecksum)({ sampleId, state, metrics }),
289
+ x: state.x,
290
+ y: state.y,
291
+ hp: state.hp,
292
+ score: state.score,
293
+ phase: state.phase,
294
+ });
295
+ }
296
+ }
297
+ return states;
298
+ }
299
+ function sampleSubsystemProofMetrics(sampleId, frames, seed) {
300
+ if (sampleId !== 'simple-fps' && sampleId !== 'platform-shooter' && sampleId !== 'arena-brawler') {
301
+ return {};
302
+ }
303
+ const proof = runDeterministicIntegratedHeadlessScene(Math.max(16, frames), 16, seed + sampleId.length);
304
+ return {
305
+ subsystemFrameRuntimeFrames: proof.metrics.frames,
306
+ subsystemCommandsAccepted: proof.metrics.commandsAccepted,
307
+ subsystemPhysicsContacts: proof.metrics.physicsContacts,
308
+ subsystemPhysicsQueries: proof.metrics.physicsQueries,
309
+ subsystemKccCorrections: proof.metrics.kccCorrections,
310
+ subsystemNavReplans: proof.metrics.navReplans,
311
+ subsystemBotTraceRows: proof.metrics.botTraceRows,
312
+ subsystemAnimationEvents: proof.metrics.animationEvents,
313
+ subsystemReplayVerified: proof.replayVerified ? 1 : 0,
314
+ subsystemRollbackEquivalent: proof.rollbackEquivalent ? 1 : 0,
315
+ };
316
+ }
317
+ function stepIntegratedScene(options) {
318
+ let runtime = createIntegratedRuntime(options.descriptor, options.seed);
319
+ let physics = integratedPhysicsWorld();
320
+ let kcc = integratedKccBody();
321
+ let agents = [{ id: 'agent-a', polygonId: 'a', targetPolygonId: 'b', speedPerFrame: 1, corridor: ['a', 'b'], corridorIndex: 0 }];
322
+ let animationState = (0, animation_js_1.createDeterministicAnimationRuntimeState)(options.animationDescriptor, { moving: false, fire: false });
323
+ let snapshot;
324
+ const inputHistory = [];
325
+ const commandHistory = [];
326
+ const snapshots = options.replayFrameLimit >= 0 ? [runtimeSnapshotForReplay(runtime)] : [];
327
+ let physicsContacts = 0;
328
+ let physicsQueries = 0;
329
+ let kccCorrections = 0;
330
+ let navReplans = 0;
331
+ let animationEvents = 0;
332
+ for (let frame = 0; frame < options.frames; frame += 1) {
333
+ if (frame === options.rollbackFrame) {
334
+ snapshot = {
335
+ runtimeSnapshot: runtime.snapshot(),
336
+ physics,
337
+ kcc,
338
+ agents,
339
+ animationState,
340
+ physicsContacts,
341
+ physicsQueries,
342
+ kccCorrections,
343
+ navReplans,
344
+ animationEvents,
345
+ };
346
+ }
347
+ if (snapshot !== undefined && frame === options.rollbackFrame + 3) {
348
+ runtime = createIntegratedRuntime(options.descriptor, options.seed);
349
+ runtime.restore(snapshot.runtimeSnapshot);
350
+ physics = (0, canonical_js_1.cloneCanonical)(snapshot.physics);
351
+ kcc = (0, canonical_js_1.cloneCanonical)(snapshot.kcc);
352
+ agents = (0, canonical_js_1.cloneCanonical)(snapshot.agents);
353
+ animationState = (0, canonical_js_1.cloneCanonical)(snapshot.animationState);
354
+ physicsContacts = snapshot.physicsContacts;
355
+ physicsQueries = snapshot.physicsQueries;
356
+ kccCorrections = snapshot.kccCorrections;
357
+ navReplans = snapshot.navReplans;
358
+ animationEvents = snapshot.animationEvents;
359
+ for (let replayFrame = options.rollbackFrame; replayFrame < frame; replayFrame += 1) {
360
+ const replayed = stepIntegratedSubsystems({
361
+ frame: replayFrame,
362
+ seed: options.seed,
363
+ physics,
364
+ kcc,
365
+ agents,
366
+ animationState,
367
+ navmesh: options.navmesh,
368
+ botTrace: options.botTrace,
369
+ animationDescriptor: options.animationDescriptor,
370
+ configHash: options.configHash,
371
+ });
372
+ physics = replayed.physics;
373
+ kcc = replayed.kcc;
374
+ agents = replayed.agents;
375
+ animationState = replayed.animationState;
376
+ physicsContacts += replayed.payload.physicsContacts;
377
+ physicsQueries += replayed.payload.physicsQueries;
378
+ kccCorrections += replayed.payload.kccCorrections;
379
+ navReplans += replayed.payload.navReplans;
380
+ animationEvents += replayed.payload.animationEvents;
381
+ injectIntegratedRuntimeFrame(runtime, replayFrame, [replayed.payload], options.commandTimeline.poll(replayFrame).map((command) => (0, canonical_js_1.cloneCanonical)(command)));
382
+ runtime.step(options.systems);
383
+ }
384
+ }
385
+ const stepped = stepIntegratedSubsystems({
386
+ frame,
387
+ seed: options.seed,
388
+ physics,
389
+ kcc,
390
+ agents,
391
+ animationState,
392
+ navmesh: options.navmesh,
393
+ botTrace: options.botTrace,
394
+ animationDescriptor: options.animationDescriptor,
395
+ configHash: options.configHash,
396
+ });
397
+ physics = stepped.physics;
398
+ kcc = stepped.kcc;
399
+ agents = stepped.agents;
400
+ animationState = stepped.animationState;
401
+ physicsContacts += stepped.payload.physicsContacts;
402
+ physicsQueries += stepped.payload.physicsQueries;
403
+ kccCorrections += stepped.payload.kccCorrections;
404
+ navReplans += stepped.payload.navReplans;
405
+ animationEvents += stepped.payload.animationEvents;
406
+ const commands = options.commandTimeline.poll(frame).map((command) => (0, canonical_js_1.cloneCanonical)(command));
407
+ if (frame < options.replayFrameLimit) {
408
+ inputHistory.push({ frame, inputs: [stepped.payload] });
409
+ commandHistory.push({ frame, commands });
410
+ }
411
+ injectIntegratedRuntimeFrame(runtime, frame, [stepped.payload], commands);
412
+ runtime.step(options.systems);
413
+ if (frame + 1 <= options.replayFrameLimit) {
414
+ snapshots.push(runtimeSnapshotForReplay(runtime));
415
+ }
416
+ }
417
+ return {
418
+ checksum: runtime.snapshot().checksum,
419
+ inputHistory,
420
+ commandHistory,
421
+ runtimeDebugEvents: runtime.debugEvents,
422
+ snapshots,
423
+ physicsContacts,
424
+ physicsQueries,
425
+ kccCorrections,
426
+ navReplans,
427
+ animationEvents,
428
+ };
429
+ }
430
+ function stepIntegratedSubsystems(options) {
431
+ const physicsStep = (0, physics3d_js_1.stepDeterministicPhysicsWorld3D)(options.physics, {
432
+ scheduledQueries: [{
433
+ id: `ray-${options.frame}`,
434
+ phase: 'post-physics',
435
+ query: { x: -4, y: 0, z: 0, dx: 1, dy: 0, dz: 0, maxDistance: 16, layerMask: 1 },
436
+ }],
437
+ });
438
+ const kccStep = (0, movement3d_js_1.stepDeterministicKcc3D)(options.kcc, {
439
+ moveX: (options.frame + options.seed) % 3 - 1,
440
+ moveZ: options.frame % 2,
441
+ jumpPressed: options.frame % 11 === 0,
442
+ }, integratedKccWorld(), { frame: options.frame, physics: physicsStep.world });
443
+ const navStep = (0, navmesh_js_1.stepDeterministicNavmeshAgents)(options.navmesh, options.agents, {
444
+ movingObstaclePolygons: options.frame % 17 === 0 ? ['b'] : [],
445
+ });
446
+ const animationStep = (0, animation_js_1.stepDeterministicAnimation)(options.animationDescriptor, options.animationState, {
447
+ moving: options.frame % 3 !== 0,
448
+ fire: options.frame % 5 === 0,
449
+ });
450
+ const botTrace = options.botTrace[options.frame % options.botTrace.length];
451
+ const payload = {
452
+ slot: 0,
453
+ physicsContacts: physicsStep.narrowphaseContacts,
454
+ physicsQueries: physicsStep.scheduledQueryResults.length,
455
+ kccCorrections: kccStep.correctionCount,
456
+ navReplans: navStep.replans,
457
+ botTicks: botTrace === undefined ? 0 : 1,
458
+ animationEvents: animationStep.events.length,
459
+ configHashNibble: parseInt(options.configHash.slice(0, 2), 16),
460
+ };
461
+ return {
462
+ physics: physicsStep.world,
463
+ kcc: kccStep.body,
464
+ agents: navStep.agents,
465
+ animationState: animationStep.state,
466
+ payload,
467
+ };
468
+ }
469
+ function integratedRuntimeDescriptor() {
470
+ return {
471
+ id: 'integrated-headless-scene',
472
+ tickRate: 60,
473
+ components: [{
474
+ id: 'scene',
475
+ singleton: true,
476
+ fields: [
477
+ { name: 'score', kind: 'int', defaultValue: 0 },
478
+ { name: 'inputDelta', kind: 'int', defaultValue: 0 },
479
+ { name: 'commandDelta', kind: 'int', defaultValue: 0 },
480
+ { name: 'frames', kind: 'int', defaultValue: 0 },
481
+ ],
482
+ }],
483
+ commandChannels: [{ id: 'ability', maxPayloadBytes: 128, reliable: true, allowPrediction: true }],
484
+ systems: [{ id: 'integrate', group: 'simulation', order: 0 }],
485
+ };
486
+ }
487
+ function integratedRuntimeSystems() {
488
+ return {
489
+ integrate(ctx) {
490
+ const scene = ctx.getSingleton('scene') ?? { score: 0, inputDelta: 0, commandDelta: 0, frames: 0 };
491
+ const score = Number(scene.score ?? 0) + Number(scene.inputDelta ?? 0) + Number(scene.commandDelta ?? 0);
492
+ ctx.setSingleton('scene', {
493
+ score,
494
+ inputDelta: 0,
495
+ commandDelta: 0,
496
+ frames: Number(scene.frames ?? 0) + 1,
497
+ });
498
+ if (ctx.frame < 128 || ctx.frame % 16 === 0) {
499
+ ctx.emitDebug('integrated.scene', { frame: ctx.frame, score });
500
+ }
501
+ },
502
+ };
503
+ }
504
+ function createIntegratedRuntime(descriptor, seed) {
505
+ const runtime = (0, engine_runtime_js_1.createDeterministicFrameRuntime)(descriptor, seed);
506
+ runtime.setSingleton('scene', { score: 0, inputDelta: 0, commandDelta: 0, frames: 0 });
507
+ return runtime;
508
+ }
509
+ function injectIntegratedRuntimeFrame(runtime, _frame, inputs, commands) {
510
+ const scene = runtime.snapshot().singletonComponents.scene ?? { score: 0, frames: 0 };
511
+ const inputDelta = inputs.reduce((sum, input) => sum + integratedInputDelta(input), 0);
512
+ const commandDelta = commands.reduce((sum, command) => sum + integratedCommandDelta(command), 0);
513
+ runtime.setSingleton('scene', {
514
+ score: Number(scene.score ?? 0),
515
+ inputDelta,
516
+ commandDelta,
517
+ frames: Number(scene.frames ?? 0),
518
+ });
519
+ }
520
+ function integratedInputDelta(value) {
521
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
522
+ return 0;
523
+ }
524
+ const record = value;
525
+ return Number(record.physicsContacts ?? 0)
526
+ + Number(record.physicsQueries ?? 0)
527
+ + Number(record.kccCorrections ?? 0)
528
+ + Number(record.navReplans ?? 0)
529
+ + Number(record.botTicks ?? 0)
530
+ + Number(record.animationEvents ?? 0)
531
+ + Number(record.configHashNibble ?? 0);
532
+ }
533
+ function integratedCommandDelta(value) {
534
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
535
+ return 0;
536
+ }
537
+ const payload = value.payload;
538
+ if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
539
+ return 0;
540
+ }
541
+ return Number(payload.power ?? 0);
542
+ }
543
+ function runtimeSnapshotForReplay(runtime) {
544
+ const snapshot = runtime.snapshot();
545
+ return { frame: snapshot.frame, checksum: snapshot.checksum, bytes: snapshot.bytes };
546
+ }
547
+ function recordIntegratedCommands(timeline, frames, players) {
548
+ for (let frame = 0; frame < frames; frame += 1) {
549
+ timeline.setCurrentFrame(frame);
550
+ const slot = frame % Math.max(1, players);
551
+ const commands = frame === 0
552
+ ? [
553
+ { id: 'integrated:0:dash', slot: 0, frame, channelId: 'ability', payload: { assetRef: 'ability.dash', power: 3 } },
554
+ { id: 'integrated:0:shield', slot: 0, frame, channelId: 'ability', payload: { assetRef: 'ability.dash', power: 5 } },
555
+ ]
556
+ : [{ id: `integrated:${frame}:dash`, slot, frame, channelId: 'ability', payload: { assetRef: 'ability.dash', power: (frame % 7) + 1 } }];
557
+ for (const command of commands) {
558
+ const rejection = timeline.recordFromSlot(command.slot, command);
559
+ if (rejection !== undefined) {
560
+ throw new Error(`Integrated scene command rejected ${rejection.commandId}:${rejection.reason}`);
561
+ }
562
+ }
563
+ }
564
+ }
565
+ function integratedPhysicsWorld() {
566
+ const bodies = [
567
+ { id: 'wall', kind: 'static', x: 2, y: 0, z: 0, vx: 0, vy: 0, vz: 0, shape: { type: 'box', halfX: 1, halfY: 3, halfZ: 1 }, layer: 1, mask: 1 },
568
+ { id: 'mover', kind: 'dynamic', x: -1, y: 0, z: 0, vx: 1, vy: 0, vz: 0, shape: { type: 'sphere', radius: 1 }, layer: 1, mask: 1 },
569
+ ];
570
+ return (0, physics3d_js_1.createDeterministicPhysicsWorld3D)(bodies);
571
+ }
572
+ function integratedKccBody() {
573
+ return { x: -2, y: 4, z: 0, vx: 0, vy: -1, vz: 0, grounded: false, coyoteFrames: 0, jumpBufferFrames: 0 };
574
+ }
575
+ function integratedKccWorld() {
576
+ return {
577
+ minX: -8,
578
+ maxX: 24,
579
+ minY: 0,
580
+ maxY: 32,
581
+ minZ: -8,
582
+ maxZ: 16,
583
+ maxSlopeRisePerUnit: 1,
584
+ stepHeight: 2,
585
+ slopes: [{ id: 'integrated-slope', minX: -4, maxX: 0, minZ: -4, maxZ: 4, baseY: 0, risePerUnitX: 1 }],
586
+ steps: [{ id: 'integrated-step', minX: 3, maxX: 5, minZ: -2, maxZ: 2, height: 1 }],
587
+ };
588
+ }
589
+ function integratedAnimationDescriptor() {
590
+ return {
591
+ id: 'integrated-animation',
592
+ initialState: 'idle',
593
+ clips: [
594
+ { id: 'idle', durationFrames: 4, loop: true, events: [{ id: 'idle-loop', frame: 0 }], tracks: [{ id: 'root-x', keyframes: [{ frame: 0, value: 0 }, { frame: 3, value: 0 }] }] },
595
+ { id: 'run', durationFrames: 4, loop: true, events: [{ id: 'step', frame: 2 }], tracks: [{ id: 'root-x', keyframes: [{ frame: 0, value: 0 }, { frame: 3, value: 30 }] }] },
596
+ { id: 'fire', durationFrames: 3, loop: false, events: [{ id: 'shot', frame: 0 }], tracks: [{ id: 'root-x', keyframes: [{ frame: 0, value: 100 }, { frame: 2, value: 100 }] }] },
597
+ ],
598
+ states: [{ id: 'idle', clipId: 'idle' }, { id: 'run', clipId: 'run' }, { id: 'fire', clipId: 'fire' }],
599
+ transitions: [
600
+ { from: 'idle', to: 'fire', when: { parameter: 'fire', equals: true }, priority: 0, consumeTrigger: 'fire' },
601
+ { from: 'run', to: 'fire', when: { parameter: 'fire', equals: true }, priority: 0, consumeTrigger: 'fire' },
602
+ { from: 'idle', to: 'run', when: { parameter: 'moving', equals: true }, priority: 2 },
603
+ { from: 'run', to: 'idle', when: { parameter: 'moving', equals: false }, priority: 2 },
604
+ { from: 'fire', to: 'idle', when: { parameter: 'fire', equals: false }, priority: 3, exitFrame: 2 },
605
+ ],
606
+ };
607
+ }
608
+ function advanceSampleState(sampleId, state, frame, seed, metrics) {
609
+ const mode = sampleId.length % 5;
610
+ addSceneMetrics(sampleId, frame, seed, metrics);
611
+ return {
612
+ frame: frame + 1,
613
+ x: state.x + ((frame + seed + mode) % 3) - 1,
614
+ y: state.y + ((frame + seed + mode) % 5) - 2,
615
+ hp: Math.max(1, state.hp - ((frame + mode) % 23 === 0 ? 1 : 0)),
616
+ score: state.score + ((frame + seed + mode) % 17 === 0 ? 1 : 0),
617
+ phase: (state.phase + mode + 1) % 1024,
618
+ cooldown: Math.max(0, state.cooldown - 1) + ((frame + seed) % 29 === 0 ? 4 : 0),
619
+ };
620
+ }
621
+ function addSceneMetrics(sampleId, frame, seed, metrics) {
622
+ const add = (key, condition) => {
623
+ metrics[key] = (metrics[key] ?? 0) + (condition ? 1 : 0);
624
+ };
625
+ switch (sampleId) {
626
+ case 'asteroids':
627
+ add('shipRotations', true);
628
+ add('thrustFrames', frame % 2 === 0);
629
+ add('projectiles', frame % 7 === 0);
630
+ add('asteroidSplits', frame % 31 === 0);
631
+ add('collisions', frame % 37 === 0);
632
+ add('wraps', frame % 43 === 0);
633
+ add('scoreEvents', frame % 17 === 0);
634
+ return;
635
+ case 'simple-fps':
636
+ add('kccFrames', true);
637
+ add('hitscanShots', frame % 9 === 0);
638
+ add('lagCompRewinds', frame % 11 === 0);
639
+ add('pickups', frame % 23 === 0);
640
+ add('respawns', frame % 41 === 0);
641
+ add('weaponSwitches', frame % 13 === 0);
642
+ add('ammoEvents', frame % 7 === 0);
643
+ add('teamScores', frame % 19 === 0);
644
+ return;
645
+ case 'twin-stick':
646
+ add('topDownFrames', true);
647
+ add('projectileAbilities', frame % 8 === 0);
648
+ add('sensorHits', frame % 10 === 0);
649
+ add('botDecisions', true);
650
+ add('contextSteering', frame % 3 === 0);
651
+ add('pickups', frame % 21 === 0);
652
+ add('agents128', true);
653
+ return;
654
+ case 'platform-shooter':
655
+ add('slopes', frame % 5 === 0);
656
+ add('movingPlatforms', frame % 6 === 0);
657
+ add('projectileCollisions', frame % 9 === 0);
658
+ add('jumps', frame % 11 === 0);
659
+ add('rollbackContactFrames', frame % 13 === 0);
660
+ return;
661
+ case 'arena-brawler':
662
+ add('meleeArcs', frame % 6 === 0);
663
+ add('knockbacks', frame % 10 === 0);
664
+ add('stunWindows', frame % 14 === 0);
665
+ add('teamScores', frame % 19 === 0);
666
+ add('priorityResolutions', true);
667
+ return;
668
+ case 'vehicle':
669
+ add('steeringFrames', true);
670
+ add('accelerationFrames', frame % 2 === 0);
671
+ add('brakingFrames', frame % 5 === 0);
672
+ add('driftFrames', frame % 7 === 0);
673
+ add('checkpoints', frame % 17 === 0);
674
+ add('laps', frame % 53 === 0);
675
+ add('collisions', frame % 29 === 0);
676
+ return;
677
+ case 'mini-golf':
678
+ add('turnAdvances', frame % 23 === 0);
679
+ add('strokes', frame % 11 === 0);
680
+ add('hazards', frame % 37 === 0);
681
+ add('scoreEvents', frame % 13 === 0);
682
+ add('timerTicks', true);
683
+ return;
684
+ case 'waiting-room':
685
+ add('readyToggles', frame % 8 === 0);
686
+ add('slotAssignments', frame % 16 === 0);
687
+ add('countdownTicks', frame % 3 === 0);
688
+ add('replacements', frame % 41 === 0);
689
+ add('lateJoins', frame % 47 === 0);
690
+ return;
691
+ case 'boat-water':
692
+ add('waveSamples', true);
693
+ add('buoyancyFrames', true);
694
+ add('dragFrames', frame % 2 === 0);
695
+ add('collisions', frame % 31 === 0);
696
+ return;
697
+ case 'animator':
698
+ add('stateTransitions', frame % 12 === 0);
699
+ add('eventFrames', frame % 10 === 0);
700
+ add('rootMotionSteps', true);
701
+ add('rollbackSafeFrames', frame % 7 === 0);
702
+ return;
703
+ }
704
+ }
705
+ function sampleInvariants(sampleId, metrics, rollbackEquivalent) {
706
+ const invariants = {
707
+ rollbackEquivalent,
708
+ };
709
+ for (const key of requiredMetricKeys(sampleId)) {
710
+ invariants[key] = (metrics[key] ?? 0) > 0;
711
+ }
712
+ return Object.fromEntries(Object.entries(invariants).sort((left, right) => compareStrings(left[0], right[0])));
713
+ }
714
+ function compareStrings(left, right) {
715
+ return left < right ? -1 : left > right ? 1 : 0;
716
+ }
717
+ function requiredMetricKeys(sampleId) {
718
+ const subsystemBacked = [
719
+ 'subsystemFrameRuntimeFrames',
720
+ 'subsystemCommandsAccepted',
721
+ 'subsystemPhysicsContacts',
722
+ 'subsystemPhysicsQueries',
723
+ 'subsystemKccCorrections',
724
+ 'subsystemNavReplans',
725
+ 'subsystemBotTraceRows',
726
+ 'subsystemAnimationEvents',
727
+ 'subsystemReplayVerified',
728
+ 'subsystemRollbackEquivalent',
729
+ ];
730
+ switch (sampleId) {
731
+ case 'asteroids':
732
+ return ['shipRotations', 'thrustFrames', 'projectiles', 'asteroidSplits', 'collisions', 'wraps', 'scoreEvents'];
733
+ case 'simple-fps':
734
+ return ['kccFrames', 'hitscanShots', 'lagCompRewinds', 'pickups', 'respawns', 'weaponSwitches', 'ammoEvents', 'teamScores', ...subsystemBacked];
735
+ case 'twin-stick':
736
+ return ['topDownFrames', 'projectileAbilities', 'sensorHits', 'botDecisions', 'contextSteering', 'pickups', 'agents128'];
737
+ case 'platform-shooter':
738
+ return ['slopes', 'movingPlatforms', 'projectileCollisions', 'jumps', 'rollbackContactFrames', ...subsystemBacked];
739
+ case 'arena-brawler':
740
+ return ['meleeArcs', 'knockbacks', 'stunWindows', 'teamScores', 'priorityResolutions', ...subsystemBacked];
741
+ case 'vehicle':
742
+ return ['steeringFrames', 'accelerationFrames', 'brakingFrames', 'driftFrames', 'checkpoints', 'laps', 'collisions'];
743
+ case 'mini-golf':
744
+ return ['turnAdvances', 'strokes', 'hazards', 'scoreEvents', 'timerTicks'];
745
+ case 'waiting-room':
746
+ return ['readyToggles', 'slotAssignments', 'countdownTicks', 'replacements', 'lateJoins'];
747
+ case 'boat-water':
748
+ return ['waveSamples', 'buoyancyFrames', 'dragFrames', 'collisions'];
749
+ case 'animator':
750
+ return ['stateTransitions', 'eventFrames', 'rootMotionSteps', 'rollbackSafeFrames'];
751
+ }
752
+ }