@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,2292 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { canonicalStringify, defaultChecksum } from './canonical.js';
4
+ import { createDeterministicAnimationRuntimeState, cookDeterministicAnimation, runDeterministicAnimationFixture, stepDeterministicAnimation } from './animation.js';
5
+ import { compileDeterministicBotDocument, runDeterministicBotDocumentFixture, summarizeDeterministicBotDocumentTraceCoverage, traceDeterministicBotDocument } from './bot-documents.js';
6
+ import { cookDeterministicAssets, validatePrototypeEntitiesAgainstFrameRuntime } from './asset-cooking.js';
7
+ import { runDeterministicCommandProtocolFixture, runDeterministicReliableCommandDefaultsFixture } from './commands.js';
8
+ import { cookDeterministicConfigBundle, runDeterministicConfigBundleFixture, verifyDeterministicConfigBundle } from './config-bundle.js';
9
+ import { runDeterministicSessionAuthorityFixture, runDeterministicSessionCoreFixture, runDeterministicSessionLifecycleProtocolFixture, runDeterministicStructuredSessionInputFixture } from './deterministic-session.js';
10
+ import { createDeterministicFrameRuntime, createDeterministicFrameRuntimeAccessors, runDeterministicDslRuntimeFixture, runDeterministicFrameRuntimeFrameApiFixture, runDeterministicFrameRuntimeMutationCorpus, runDeterministicGeneratedRuntimeFixture, runDeterministicSystemSchedulerFixture, generateDeterministicFrameRuntimeSource, } from './engine-runtime.js';
11
+ import { runDeterministicEventKeywordFixture } from './events.js';
12
+ import { runDeterministicAuthoritativeLagCompensation3DFixture, runDeterministicLagCompensation3DApiFixture, runDeterministicLagCompensation3DFixture } from './lag-compensation3d.js';
13
+ import { runDeterministicDslBinaryFixture } from './schema-dsl-binary.js';
14
+ import { runDeterministicButtonFixture, runDeterministicDescriptorButtonFixture } from './input-button.js';
15
+ import { runDeterministicLocalAuthorityFixture, runDeterministicLocalAuthorityReplayMinimizerFixture, runDeterministicLocalAuthorityRuntimePolicyFixture, runDeterministicOfflineToAuthorityUpgradeFixture, } from './local-authority.js';
16
+ import { runDeterministicLocalAuthorityWireFixture } from './local-authority-wire.js';
17
+ import { runDeterministicMulticlientAuthorityFixture } from './multiclient-authority.js';
18
+ import { runDeterministicKcc3DAdversarialFixture, runDeterministicKcc3DCallbackFilterFixture, runDeterministicKcc3DCrowdFixture, runDeterministicKcc3DFilterForceFixture, runDeterministicKcc3DGeometryFixture, stepDeterministicKcc3D } from './movement3d.js';
19
+ import { cookDeterministicNavmesh, findDeterministicNavmeshPath, runDeterministicNavmeshImportTraversalFixture, runDeterministicNavmeshLayered3DFixture, runDeterministicNavmeshStress, runDeterministicNavmeshVelocityObstacleFixture, stepDeterministicNavmeshAgents } from './navmesh.js';
20
+ import { createDeterministicPhysicsWorld3D, runDeterministicPhysics3DCallbackMaskFixture, runDeterministicPhysics3DDetailFixture, runDeterministicPhysics3DMaterialFixture, runDeterministicPhysics3DQueryOptionsFixture, runDeterministicPhysics3DStackFixture, runDeterministicPhysics3DStress, stepDeterministicPhysicsWorld3D, } from './physics3d.js';
21
+ import { runDeterministicSignalFixture } from './signals.js';
22
+ import { runDeterministicSparseSetFixture } from './sparse-set.js';
23
+ import { runDeterministicLifecycleFixture } from './system-lifecycle.js';
24
+ import { runDeterministicRollbackHistoryFixture } from './rollback-history.js';
25
+ import { createDeterministicReplayBundle, createDeterministicReplayRuntimeManifest, createDeterministicReplayDebugTables, minimizeDeterministicReplayDesync, minimizeDeterministicReplayDesyncBundle, sliceDeterministicReplayBundle, verifyDeterministicFrameRuntimeReplayBundle, verifyDeterministicReplayBundle, verifyDeterministicSelfContainedFrameRuntimeReplayBundle, } from './replay-bundle.js';
26
+ import { runDeterministicIntegratedHeadlessScene } from './sample-scenes.js';
27
+ import { defineSyncplayGame } from './schema.js';
28
+ import { checkSourceText } from './tools/static-checker.js';
29
+ const requiredCategories = [
30
+ 'typed-frame-runtime',
31
+ 'schema-dsl-binary',
32
+ 'sparse-set-storage',
33
+ 'input-button',
34
+ 'system-lifecycle',
35
+ 'event-keywords',
36
+ 'simulation-signals',
37
+ 'commands',
38
+ 'replay-bundle',
39
+ 'integrated-headless-scene',
40
+ 'project-runtime',
41
+ 'physics3d',
42
+ 'kcc3d',
43
+ 'navmesh',
44
+ 'bot-documents',
45
+ 'animation',
46
+ 'config-bundle',
47
+ 'local-authority',
48
+ 'deterministic-session',
49
+ ];
50
+ const engineCompleteWallClockBudgetMs = 16.67;
51
+ export function runDeterministicEngineCompleteSuite(...args) {
52
+ const resolvedOptions = resolveEngineCompleteOptions(args[0]);
53
+ const players = resolvedOptions.players ?? 128;
54
+ const frames = resolvedOptions.frames ?? (resolvedOptions.benchmark === true ? 100_000 : 5_000);
55
+ const seed = resolvedOptions.seed ?? 71;
56
+ const selectedCategories = filterEngineCompleteCategories(resolvedOptions.filter);
57
+ const categoryReports = selectedCategories.map((category) => runCategory(category, players, frames, seed, resolvedOptions));
58
+ const failures = categoryReports.flatMap((category) => category.failures.map((failure) => `${category.category}: ${failure}`));
59
+ const withoutId = {
60
+ schemaVersion: 1,
61
+ reportId: '',
62
+ players,
63
+ frames,
64
+ seed,
65
+ benchmark: resolvedOptions.benchmark === true,
66
+ noFastStress: resolvedOptions.noFastStress === true,
67
+ enforceThresholds: resolvedOptions.enforceThresholds === true,
68
+ categories: categoryReports,
69
+ failures,
70
+ summary: {
71
+ passed: categoryReports.filter((category) => category.result === 'pass').length,
72
+ failed: categoryReports.filter((category) => category.result === 'fail').length,
73
+ requiredCategories: selectedCategories,
74
+ },
75
+ result: failures.length === 0 ? 'pass' : 'fail',
76
+ };
77
+ return {
78
+ ...withoutId,
79
+ reportId: defaultChecksum(withoutId),
80
+ };
81
+ }
82
+ function resolveEngineCompleteOptions(options) {
83
+ /* istanbul ignore else -- No-arg compatibility runs the full default certification suite. Filtered default-value coverage above exercises the same defaults without making the source shard run the 5,000-frame all-category path twice. */
84
+ if (options !== undefined) {
85
+ return options;
86
+ }
87
+ /* istanbul ignore next -- No-arg compatibility runs the full default certification suite, which is intentionally not duplicated inside this focused source shard. */
88
+ return {};
89
+ }
90
+ function filterEngineCompleteCategories(filter) {
91
+ if (filter === undefined || filter.length === 0) {
92
+ return requiredCategories;
93
+ }
94
+ const categories = requiredCategories.filter((category) => category === filter || category.includes(filter));
95
+ if (categories.length === 0) {
96
+ throw new Error(`No engine-complete categories matched --filter ${filter}`);
97
+ }
98
+ return categories;
99
+ }
100
+ export function stableDeterministicEngineCompleteJson(report) {
101
+ return `${canonicalStringify(report)}\n`;
102
+ }
103
+ function runCategory(category, players, frames, seed, options) {
104
+ const startedAtMs = engineCompleteNowMs();
105
+ const report = runCategoryUntimed(category, players, frames, seed, options);
106
+ return withCertificationTiming(report, engineCompleteNowMs() - startedAtMs, options.enforceThresholds === true);
107
+ }
108
+ function runCategoryUntimed(category, players, frames, seed, options) {
109
+ switch (category) {
110
+ case 'typed-frame-runtime':
111
+ return runtimeCategory(frames, seed);
112
+ case 'schema-dsl-binary':
113
+ return schemaDslBinaryCategory();
114
+ case 'sparse-set-storage':
115
+ return sparseSetCategory();
116
+ case 'input-button':
117
+ return inputButtonCategory();
118
+ case 'system-lifecycle':
119
+ return systemLifecycleCategory();
120
+ case 'event-keywords':
121
+ return eventKeywordsCategory();
122
+ case 'simulation-signals':
123
+ return simulationSignalsCategory();
124
+ case 'commands':
125
+ return commandsCategory(players, frames, seed);
126
+ case 'replay-bundle':
127
+ return replayBundleCategory(players, frames, options.benchmark === true ? Math.min(frames, 5_000) : frames);
128
+ case 'integrated-headless-scene':
129
+ return integratedHeadlessSceneCategory(players, frames, options.benchmark === true ? Math.min(frames, 5_000) : frames, seed);
130
+ case 'project-runtime':
131
+ return projectRuntimeCategory(frames, options.benchmark === true ? Math.min(frames, 5_000) : frames, seed);
132
+ case 'physics3d':
133
+ return physicsCategory(options.benchmark === true ? 128 : 512, frames, options.benchmark === true ? frames : Math.min(frames, 5000), options.enforceThresholds === true, options.benchmark === true, options.noFastStress === true);
134
+ case 'kcc3d':
135
+ return kccCategory(players, frames, options.benchmark === true ? frames : Math.min(frames, 5000), options.benchmark === true, options.noFastStress === true);
136
+ case 'navmesh':
137
+ return navmeshCategory(players, frames, options.benchmark === true ? frames : Math.min(frames, 5000));
138
+ case 'bot-documents':
139
+ return botCategory(players, frames, options.benchmark === true ? frames : Math.min(frames, 5000));
140
+ case 'animation':
141
+ return animationCategory(frames, options.benchmark === true ? frames : Math.min(frames, 5000));
142
+ case 'config-bundle':
143
+ return configCategory();
144
+ case 'local-authority':
145
+ return authorityCategory(players, frames, options.benchmark === true ? frames : Math.min(frames, 5000), seed);
146
+ case 'deterministic-session':
147
+ return deterministicSessionCategory(players, frames, options.benchmark === true ? frames : Math.min(frames, 5000), seed);
148
+ }
149
+ }
150
+ function withCertificationTiming(report, elapsedMs, enforceThresholds) {
151
+ const executedFrames = Number(report.metrics.executedFrames);
152
+ const wallClockElapsedMs = roundMetric(Math.max(0, elapsedMs), 4);
153
+ const wallClockMeanFrameMs = roundMetric(wallClockElapsedMs / Math.max(1, executedFrames), 6);
154
+ return categoryReport(report.category, subsystemChecksumOf(report), {
155
+ ...report.invariants,
156
+ wallClockMeasured: Number.isFinite(wallClockElapsedMs),
157
+ wallClockMeanWithinThreshold: enforceThresholds ? wallClockMeanFrameMs <= engineCompleteWallClockBudgetMs : 'not-enforced',
158
+ }, {
159
+ ...report.metrics,
160
+ wallClockElapsedMs,
161
+ wallClockMeanFrameMs,
162
+ wallClockBudgetMs: engineCompleteWallClockBudgetMs,
163
+ wallClockThresholdsEnforced: enforceThresholds,
164
+ });
165
+ }
166
+ function runtimeCategory(frames, seed) {
167
+ const executedFrames = Math.max(10_000, frames);
168
+ const metrics = runDeterministicFrameRuntimeMutationCorpus(executedFrames, seed);
169
+ const frameApi = runDeterministicFrameRuntimeFrameApiFixture(Math.max(512, Math.min(executedFrames, 10_000)), seed + 2);
170
+ const generated = runDeterministicGeneratedRuntimeFixture(Math.max(256, Math.min(executedFrames, 10_000)), seed + 1);
171
+ const scheduler = runDeterministicSystemSchedulerFixture(seed + 3);
172
+ return categoryReport('typed-frame-runtime', defaultChecksum({
173
+ metrics: metrics.checksum,
174
+ frameApi: frameApi.checksum,
175
+ scheduler: scheduler.checksum,
176
+ }), {
177
+ rollbackEquivalent: metrics.checksum === metrics.restoredChecksum,
178
+ queryOrderStable: metrics.queryOrderStable,
179
+ mutationCorpusComplete: metrics.componentWrites >= 10_000,
180
+ queryFilterAnyWithoutCovered: frameApi.filterAnyWithoutStable,
181
+ componentCountCovered: frameApi.componentCountStable,
182
+ frameHeapCollectionsCovered: frameApi.frameHeapHandlesAllocated >= 3 && frameApi.frameHeapSnapshotBytes > 0,
183
+ frameHeapRollbackEquivalent: frameApi.frameHeapRollbackEquivalent,
184
+ frameHeapFreeRejectsStaleHandles: frameApi.frameHeapFreedRejected,
185
+ frameHeapReuseGenerationSafe: frameApi.frameHeapReusedGenerationSafe,
186
+ generatedAccessorsCovered: generated.generatedAccessorsExecuted > 0 && generated.generatedSourceBytes > 0,
187
+ generatedRuntimeSourceStable: generated.generatedSourceHash.length > 0 && generated.descriptorHash.length > 0,
188
+ sparseSetBackedRuntimeEquivalent: generated.sparseSetBackedRuntimeEquivalent,
189
+ sparseSetBackedRollbackEquivalent: generated.rollbackEquivalent,
190
+ accessorQueryOrderStable: generated.accessorQueryOrderStable,
191
+ entityRefGenerationSafe: generated.entityRefGenerationSafe,
192
+ runtimeHostApiGuarded: generated.runtimeHostApiGuarded,
193
+ systemDependenciesCovered: scheduler.order === 'PAFPAF' && scheduler.beforeEdges > 0 && scheduler.afterEdges > 0,
194
+ systemDependencyRollbackEquivalent: scheduler.rollbackEquivalent,
195
+ systemDependencySparseEquivalent: scheduler.sparseEquivalent,
196
+ systemDependencyCycleRejected: scheduler.cycleRejected,
197
+ systemDependencyUnknownRejected: scheduler.unknownDependencyRejected,
198
+ systemDependencyCrossGroupRejected: scheduler.crossGroupDependencyRejected,
199
+ }, withFrameMetrics({
200
+ ...metrics,
201
+ generatedRuntimeChecksum: generated.checksum,
202
+ frameApiChecksum: frameApi.checksum,
203
+ frameApiRestoredChecksum: frameApi.restoredChecksum,
204
+ frameApiFilterAnyWithoutStable: frameApi.filterAnyWithoutStable,
205
+ frameApiComponentCountStable: frameApi.componentCountStable,
206
+ frameHeapHandlesAllocated: frameApi.frameHeapHandlesAllocated,
207
+ frameHeapFreedRejected: frameApi.frameHeapFreedRejected,
208
+ frameHeapReusedGenerationSafe: frameApi.frameHeapReusedGenerationSafe,
209
+ frameHeapSnapshotBytes: frameApi.frameHeapSnapshotBytes,
210
+ generatedSourceHash: generated.generatedSourceHash,
211
+ generatedSourceBytes: generated.generatedSourceBytes,
212
+ generatedAccessorsExecuted: generated.generatedAccessorsExecuted,
213
+ sparseMapRuntimeChecksum: generated.mapRuntimeChecksum,
214
+ sparseRuntimeChecksum: generated.sparseRuntimeChecksum,
215
+ sparseSnapshotBytes: generated.sparseSnapshotBytes,
216
+ entityRefGenerationSafe: generated.entityRefGenerationSafe,
217
+ runtimeHostApiGuarded: generated.runtimeHostApiGuarded,
218
+ schedulerChecksum: scheduler.checksum,
219
+ schedulerRestoredChecksum: scheduler.restoredChecksum,
220
+ schedulerSparseChecksum: scheduler.sparseChecksum,
221
+ schedulerOrder: scheduler.order,
222
+ schedulerBeforeEdges: scheduler.beforeEdges,
223
+ schedulerAfterEdges: scheduler.afterEdges,
224
+ schedulerRollbackEquivalent: scheduler.rollbackEquivalent,
225
+ schedulerSparseEquivalent: scheduler.sparseEquivalent,
226
+ schedulerCycleRejected: scheduler.cycleRejected,
227
+ schedulerUnknownDependencyRejected: scheduler.unknownDependencyRejected,
228
+ schedulerCrossGroupDependencyRejected: scheduler.crossGroupDependencyRejected,
229
+ }, frames, executedFrames));
230
+ }
231
+ function schemaDslBinaryCategory() {
232
+ const metrics = runDeterministicDslBinaryFixture();
233
+ const runtimeFrames = 256;
234
+ const runtime = runDeterministicDslRuntimeFixture(runtimeFrames);
235
+ return categoryReport('schema-dsl-binary', defaultChecksum({ binary: metrics.roundTripChecksum, runtime: runtime.checksum }), {
236
+ schemaPermutationStable: metrics.permutationStable,
237
+ binaryRoundTripExecuted: metrics.encodedBytes > 0,
238
+ rejectsBadDslValues: metrics.rejectedBadField
239
+ && metrics.rejectedExtraField
240
+ && metrics.rejectedBadDeclaration
241
+ && metrics.rejectedDuplicateSetValue
242
+ && metrics.rejectedDuplicateDictionaryKey,
243
+ eventSignalStructDslCovered: metrics.eventDeclarations > 0 && metrics.signalDeclarations > 0 && metrics.structDeclarations > 0,
244
+ globalImportPragmaDslCovered: metrics.globalDeclarations > 0 && metrics.importCount > 0 && metrics.pragmaCount > 0 && metrics.maxPlayers === 128,
245
+ assetAndEntityRefsCovered: metrics.assetRefFields > 0 && metrics.entityRefFields > 0,
246
+ arrayFieldsCovered: metrics.arrayFields > 0,
247
+ fixedArrayFieldsCovered: metrics.fixedArrayFields > 0,
248
+ dynamicCollectionFieldsCovered: metrics.listFields > 0 && metrics.setFields > 0 && metrics.dictionaryFields > 0,
249
+ buttonFieldsCovered: metrics.buttonFields > 0,
250
+ advancedDslDeclarationsCovered: metrics.enumDeclarations > 0 && metrics.flagsDeclarations > 0 && metrics.unionDeclarations > 0,
251
+ advancedDslRefsCovered: metrics.playerRefFields > 0 && metrics.typedAssetRefFields > 0,
252
+ advancedDslBitsetCovered: metrics.bitsetFields > 0,
253
+ advancedDslModifiersCovered: metrics.declarationModifiers > 0,
254
+ advancedDslHashSetAliasCovered: metrics.hashSetAliasFields > 0,
255
+ fixedArrayLengthRejected: metrics.rejectedWrongFixedArrayLength,
256
+ dslRuntimeDescriptorGenerated: runtime.componentCount > 0 && runtime.singletonCount > 1 && runtime.generatedSourceBytes > 0,
257
+ dslRuntimeCommandEventSignalGenerated: runtime.commandChannelCount > 0 && runtime.eventChannelCount > 0 && runtime.signalChannelCount > 0,
258
+ dslRuntimePermutationStable: runtime.descriptorPermutationStable,
259
+ dslRuntimeSparseEquivalent: runtime.sparseRuntimeEquivalent,
260
+ dslRuntimeRollbackEquivalent: runtime.rollbackEquivalent,
261
+ dslRuntimeRefsAndArraysCovered: runtime.arrayFieldCount > 0 && runtime.assetRefFieldCount > 0 && runtime.entityRefFieldCount > 0,
262
+ dslRuntimeFixedArraysCovered: runtime.fixedArrayFieldCount > 0,
263
+ dslRuntimeDynamicCollectionHandlesCovered: runtime.dynamicCollectionHandleFieldCount >= 3,
264
+ dslRuntimeFrameApiHelpersCovered: runtime.generatedFrameApiHelpersExecuted > 0,
265
+ dslRuntimeEventSignalAccessorsExecuted: runtime.generatedEventSignalAccessorsExecuted > 0,
266
+ dslRuntimeTypedInterfacesCovered: runtime.generatedTypedInterfacesCovered,
267
+ dslRuntimeAssetRefsValidated: runtime.runtimeAssetRefsValidated,
268
+ }, {
269
+ ...metrics,
270
+ fieldKinds: metrics.fieldKinds.join(','),
271
+ dslRuntimeChecksum: runtime.checksum,
272
+ dslRuntimeDescriptorHash: runtime.descriptorHash,
273
+ dslRuntimePermutedDescriptorHash: runtime.permutedDescriptorHash,
274
+ dslRuntimeGeneratedSourceHash: runtime.generatedSourceHash,
275
+ dslRuntimeGeneratedSourceBytes: runtime.generatedSourceBytes,
276
+ dslRuntimeComponentCount: runtime.componentCount,
277
+ dslRuntimeSingletonCount: runtime.singletonCount,
278
+ dslRuntimeCommandChannelCount: runtime.commandChannelCount,
279
+ dslRuntimeEventChannelCount: runtime.eventChannelCount,
280
+ dslRuntimeSignalChannelCount: runtime.signalChannelCount,
281
+ dslRuntimeArrayFieldCount: runtime.arrayFieldCount,
282
+ dslRuntimeFixedArrayFieldCount: runtime.fixedArrayFieldCount,
283
+ dslRuntimeDynamicCollectionHandleFieldCount: runtime.dynamicCollectionHandleFieldCount,
284
+ dslRuntimeAssetRefFieldCount: runtime.assetRefFieldCount,
285
+ dslRuntimeEntityRefFieldCount: runtime.entityRefFieldCount,
286
+ dslGlobalDeclarations: metrics.globalDeclarations,
287
+ dslImportCount: metrics.importCount,
288
+ dslPragmaCount: metrics.pragmaCount,
289
+ dslMaxPlayers: metrics.maxPlayers,
290
+ dslListFields: metrics.listFields,
291
+ dslFixedArrayFields: metrics.fixedArrayFields,
292
+ dslSetFields: metrics.setFields,
293
+ dslDictionaryFields: metrics.dictionaryFields,
294
+ dslEnumDeclarations: metrics.enumDeclarations,
295
+ dslFlagsDeclarations: metrics.flagsDeclarations,
296
+ dslUnionDeclarations: metrics.unionDeclarations,
297
+ dslPlayerRefFields: metrics.playerRefFields,
298
+ dslTypedAssetRefFields: metrics.typedAssetRefFields,
299
+ dslBitsetFields: metrics.bitsetFields,
300
+ dslDeclarationModifiers: metrics.declarationModifiers,
301
+ dslHashSetAliasFields: metrics.hashSetAliasFields,
302
+ dslRejectedWrongFixedArrayLength: metrics.rejectedWrongFixedArrayLength,
303
+ dslRejectedDuplicateSetValue: metrics.rejectedDuplicateSetValue,
304
+ dslRejectedDuplicateDictionaryKey: metrics.rejectedDuplicateDictionaryKey,
305
+ dslRuntimeGeneratedAccessorsExecuted: runtime.generatedAccessorsExecuted,
306
+ dslRuntimeGeneratedFrameApiHelpersExecuted: runtime.generatedFrameApiHelpersExecuted,
307
+ dslRuntimeGeneratedEventSignalAccessorsExecuted: runtime.generatedEventSignalAccessorsExecuted,
308
+ dslRuntimeGeneratedTypedInterfacesCovered: runtime.generatedTypedInterfacesCovered,
309
+ dslRuntimeAssetRefsValidated: runtime.runtimeAssetRefsValidated,
310
+ dslRuntimeSparseRuntimeEquivalent: runtime.sparseRuntimeEquivalent,
311
+ dslRuntimeRollbackEquivalent: runtime.rollbackEquivalent,
312
+ proofContract: 'schema-dsl-binary@1',
313
+ subsystemChecksum: metrics.roundTripChecksum,
314
+ requestedFrames: runtimeFrames,
315
+ executedFrames: runtimeFrames,
316
+ capped: false,
317
+ });
318
+ }
319
+ function sparseSetCategory() {
320
+ const metrics = runDeterministicSparseSetFixture();
321
+ return categoryReport('sparse-set-storage', metrics.checksum, {
322
+ rollbackEquivalent: metrics.checksum === metrics.restoredChecksum,
323
+ queryOrderStable: metrics.queryOrderStable,
324
+ duplicateRejected: metrics.rejectedDuplicate,
325
+ malformedSnapshotRejected: metrics.rejectedMalformedSnapshot,
326
+ denseStorageExercised: metrics.denseCount > 0,
327
+ }, {
328
+ ...metrics,
329
+ proofContract: 'sparse-set-storage@1',
330
+ subsystemChecksum: metrics.checksum,
331
+ requestedFrames: 1,
332
+ executedFrames: 1,
333
+ capped: false,
334
+ });
335
+ }
336
+ function inputButtonCategory() {
337
+ const metrics = runDeterministicButtonFixture();
338
+ const descriptor = runDeterministicDescriptorButtonFixture();
339
+ return categoryReport('input-button', metrics.checksum, {
340
+ rollbackEquivalent: metrics.rollbackEquivalent,
341
+ pressedCovered: metrics.pressedCount > 0,
342
+ releasedCovered: metrics.releasedCount > 0,
343
+ heldCovered: metrics.heldFrames > 0,
344
+ repeatCovered: metrics.repeatedFrames > 0,
345
+ transitionsStable: metrics.transitionsStable,
346
+ descriptorRollbackEquivalent: descriptor.rollbackEquivalent,
347
+ descriptorButtonFieldsCovered: descriptor.descriptorButtonFields > 0,
348
+ descriptorDefaultApplied: descriptor.defaultApplied,
349
+ descriptorSerializationCovered: descriptor.serializedBytes > 0,
350
+ descriptorRoundTripStable: descriptor.schemaRoundTripStable,
351
+ }, withFrameMetrics({
352
+ ...metrics,
353
+ descriptorChecksum: descriptor.checksum,
354
+ descriptorRestoredChecksum: descriptor.restoredChecksum,
355
+ descriptorArtifactId: descriptor.descriptorArtifactId,
356
+ descriptorButtonFields: descriptor.descriptorButtonFields,
357
+ descriptorSerializedBytes: descriptor.serializedBytes,
358
+ descriptorDefaultApplied: descriptor.defaultApplied,
359
+ descriptorRoundTripStable: descriptor.schemaRoundTripStable,
360
+ }, 512, 512));
361
+ }
362
+ function systemLifecycleCategory() {
363
+ const metrics = runDeterministicLifecycleFixture();
364
+ return categoryReport('system-lifecycle', metrics.checksum, {
365
+ rollbackEquivalent: metrics.rollbackEquivalent,
366
+ initCovered: metrics.initCalls >= 4,
367
+ updatesCovered: metrics.updateCalls > 0,
368
+ enableDisableCovered: metrics.enabledCalls > 0 && metrics.disabledCalls > 0,
369
+ signalDispatchCovered: metrics.signalCalls > 0,
370
+ hierarchyCovered: metrics.hierarchySkippedUpdates > 0,
371
+ startDisabledCovered: metrics.startDisabledCovered,
372
+ signalsOnlyCovered: metrics.signalOnlySkippedUpdate,
373
+ orderStable: metrics.orderStable,
374
+ }, withFrameMetrics({
375
+ ...metrics,
376
+ }, 128, 128));
377
+ }
378
+ function eventKeywordsCategory() {
379
+ const metrics = runDeterministicEventKeywordFixture();
380
+ return categoryReport('event-keywords', metrics.checksum, {
381
+ rollbackEquivalent: metrics.rollbackEquivalent,
382
+ syncedCovered: metrics.syncedEvents > 0,
383
+ predictedConfirmCovered: metrics.confirmedEvents > 0,
384
+ predictedCancelCovered: metrics.canceledEvents > 0,
385
+ verifiedOnlyCovered: metrics.verifiedOnlyEvents > 0,
386
+ notHashedDuplicateRejected: metrics.notHashedDuplicateRejected,
387
+ notHashedDedupeStable: metrics.notHashedDedupeStable,
388
+ syncedSurvivedConfirmCancel: metrics.syncedSurvivedConfirmCancel,
389
+ localRemoteScopeCovered: metrics.localEvents > 0 && metrics.remoteEvents > 0 && metrics.localRemoteDispatchStable,
390
+ }, withFrameMetrics({
391
+ ...metrics,
392
+ }, 1, 1));
393
+ }
394
+ function simulationSignalsCategory() {
395
+ const metrics = runDeterministicSignalFixture();
396
+ return categoryReport('simulation-signals', metrics.checksum, {
397
+ rollbackEquivalent: metrics.rollbackEquivalent,
398
+ subscriptionOrderStable: metrics.subscriptionOrderStable,
399
+ dispatchedSignals: metrics.dispatchedSignals >= 2,
400
+ }, {
401
+ ...metrics,
402
+ proofContract: 'simulation-signals@1',
403
+ subsystemChecksum: metrics.checksum,
404
+ requestedFrames: 1,
405
+ executedFrames: 1,
406
+ capped: false,
407
+ });
408
+ }
409
+ function commandsCategory(players, frames, seed) {
410
+ const metrics = runDeterministicCommandProtocolFixture(players, frames, seed);
411
+ const reliableDefaults = runDeterministicReliableCommandDefaultsFixture(players, frames, seed);
412
+ return categoryReport('commands', defaultChecksum({ commandProtocol: metrics.checksum, reliableDefaults: reliableDefaults.checksum }), {
413
+ commandReplayConverges: metrics.convergence,
414
+ rejectsHostileCommands: metrics.rejected >= 9,
415
+ duplicateCommandsRejected: metrics.duplicateDrops > 0,
416
+ multiCommandSameFrameCovered: metrics.multiCommandSameFrameAccepted >= 2 && metrics.multiCommandPollOrderStable,
417
+ acceptedFutureCommandsReplay: metrics.acceptedFutureCommands > 0 && metrics.acceptedFutureCommands === metrics.replayedFutureCommands,
418
+ reliableCommandsResentAndOrdered: metrics.reliableDelivered > 0 && metrics.reliableResent > 0 && metrics.reliableOrderingStable,
419
+ reliableDefaultCommandsCovered: reliableDefaults.accepted > 0
420
+ && reliableDefaults.reliableDelivered === reliableDefaults.accepted
421
+ && reliableDefaults.unreliableDropped === 0
422
+ && reliableDefaults.replayConverged
423
+ && reliableDefaults.originalOrderHash === reliableDefaults.replayOrderHash,
424
+ reliableDefaultRejectionsCovered: reliableDefaults.duplicateRejected === 1
425
+ && reliableDefaults.staleRejected === 1
426
+ && reliableDefaults.futureRejected === 1,
427
+ unreliableDropsModeled: metrics.unreliableDropped > 0,
428
+ deliveredCommandsSpanRun: metrics.firstDeliveredFrame === 0 && metrics.lastDeliveredFrame >= frames - 1,
429
+ schemaDeclaredCommandPayloadsCovered: metrics.schemaDeclaredPayloadsCovered,
430
+ schemaCommandCollectionPayloadsCovered: metrics.schemaCollectionPayloadsCovered,
431
+ schemaCommandPayloadsRejected: metrics.schemaRejected >= 4,
432
+ onlyExpectedRejections: metrics.rejected === metrics.duplicateDrops + 14,
433
+ }, withFrameMetrics({
434
+ delivered: metrics.delivered,
435
+ rejected: metrics.rejected,
436
+ duplicateDrops: metrics.duplicateDrops,
437
+ multiCommandSameFrameAccepted: metrics.multiCommandSameFrameAccepted,
438
+ multiCommandPollOrderStable: metrics.multiCommandPollOrderStable,
439
+ acceptedFutureCommands: metrics.acceptedFutureCommands,
440
+ replayedFutureCommands: metrics.replayedFutureCommands,
441
+ reliableDelivered: metrics.reliableDelivered,
442
+ reliableResent: metrics.reliableResent,
443
+ unreliableDropped: metrics.unreliableDropped,
444
+ reliableOrderingStable: metrics.reliableOrderingStable,
445
+ reliableDefaultAccepted: reliableDefaults.accepted,
446
+ reliableDefaultDelivered: reliableDefaults.reliableDelivered,
447
+ reliableDefaultResent: reliableDefaults.reliableResent,
448
+ reliableDefaultUnreliableDropped: reliableDefaults.unreliableDropped,
449
+ reliableDefaultDuplicateRejected: reliableDefaults.duplicateRejected,
450
+ reliableDefaultStaleRejected: reliableDefaults.staleRejected,
451
+ reliableDefaultFutureRejected: reliableDefaults.futureRejected,
452
+ reliableDefaultPollOrderStable: reliableDefaults.pollOrderStable,
453
+ reliableDefaultReplayConverged: reliableDefaults.replayConverged,
454
+ reliableDefaultOriginalOrderHash: reliableDefaults.originalOrderHash,
455
+ reliableDefaultReplayOrderHash: reliableDefaults.replayOrderHash,
456
+ firstDeliveredFrame: metrics.firstDeliveredFrame,
457
+ lastDeliveredFrame: metrics.lastDeliveredFrame,
458
+ delayP50Frames: metrics.delayP50Frames,
459
+ delayP95Frames: metrics.delayP95Frames,
460
+ rollbackDepthP95: metrics.rollbackDepthP95,
461
+ schemaRejected: metrics.schemaRejected,
462
+ schemaDeclaredPayloadsCovered: metrics.schemaDeclaredPayloadsCovered,
463
+ schemaCollectionPayloadsCovered: metrics.schemaCollectionPayloadsCovered,
464
+ convergence: metrics.convergence,
465
+ }, frames, frames));
466
+ }
467
+ function replayBundleCategory(players, requestedFrames, executedFrames) {
468
+ const positions = Array.from({ length: players }, () => 0);
469
+ const snapshotStates = [];
470
+ const inputHistory = [];
471
+ const commandHistory = [];
472
+ const debugEvents = [];
473
+ let commandScore = 0;
474
+ let simulationHashValue = 2166136261;
475
+ for (let frame = 0; frame < executedFrames; frame += 1) {
476
+ const inputs = Array.from({ length: players }, (_, slot) => ({
477
+ slot,
478
+ dx: (slot + frame) % 3 - 1,
479
+ dy: (slot * 2 + frame) % 3 - 1,
480
+ }));
481
+ for (const input of inputs) {
482
+ simulationHashValue = updateSimulationHashValue(simulationHashValue, frame);
483
+ simulationHashValue = updateSimulationHashValue(simulationHashValue, input.slot);
484
+ simulationHashValue = updateSimulationHashValue(simulationHashValue, input.dx);
485
+ simulationHashValue = updateSimulationHashValue(simulationHashValue, input.dy);
486
+ }
487
+ const commands = Array.from({ length: players }, (_, slot) => ({
488
+ id: `cmd:${frame}:${slot}`,
489
+ slot,
490
+ channelId: 'ability',
491
+ payload: { assetRef: 'ability.dash', power: (frame + slot) % 5 },
492
+ }));
493
+ for (const command of commands) {
494
+ simulationHashValue = updateSimulationHashValue(simulationHashValue, command.slot);
495
+ simulationHashValue = updateSimulationHashValue(simulationHashValue, command.payload.power);
496
+ }
497
+ for (const input of inputs) {
498
+ positions[input.slot] += input.dx + input.dy;
499
+ }
500
+ for (const command of commands) {
501
+ commandScore += command.payload.power;
502
+ }
503
+ inputHistory.push({ frame, inputs });
504
+ commandHistory.push({ frame, commands });
505
+ const state = {
506
+ frame,
507
+ positions: [...positions],
508
+ commandScore,
509
+ };
510
+ const bytes = canonicalStringify(state);
511
+ const snapshot = { frame, bytes, checksum: defaultChecksum(bytes) };
512
+ snapshotStates.push(snapshot);
513
+ debugEvents.push({ frame, type: 'ecs.snapshot', payload: { checksum: snapshot.checksum } }, { frame, type: 'physics.contacts', payload: { contacts: frame % 3 } }, { frame, type: 'command.poll', payload: { commands: players } });
514
+ }
515
+ const checksums = snapshotStates.map((snapshot) => ({ frame: snapshot.frame, checksum: snapshot.checksum }));
516
+ const bundle = createDeterministicReplayBundle({
517
+ runtimeVersion: 'engine-complete@1',
518
+ engineHash: 'engine-hash',
519
+ schemaHash: 'schema-hash',
520
+ assetDbHash: 'asset-hash',
521
+ configHash: 'config-hash',
522
+ sampleId: 'engine-complete',
523
+ inputHistory,
524
+ commandHistory,
525
+ checksums,
526
+ debugEvents,
527
+ snapshots: snapshotStates.map((snapshot) => ({ frame: snapshot.frame, checksum: snapshot.checksum, bytes: snapshot.bytes })),
528
+ semanticReplay: {
529
+ kind: 'operation-model-v1',
530
+ initialState: {
531
+ positions: Array.from({ length: players }, () => 0),
532
+ commandScore: 0,
533
+ },
534
+ operations: [
535
+ { source: 'input', sourcePath: 'dx', targetPath: 'positions[$slot]', op: 'add', slotField: 'slot' },
536
+ { source: 'input', sourcePath: 'dy', targetPath: 'positions[$slot]', op: 'add', slotField: 'slot' },
537
+ { source: 'command.payload', sourcePath: 'power', targetPath: 'commandScore', op: 'add' },
538
+ ],
539
+ },
540
+ metadata: {
541
+ players,
542
+ fullReplayHistory: true,
543
+ requestedFrames,
544
+ executedFrames,
545
+ fullReplayCapped: executedFrames < requestedFrames,
546
+ },
547
+ });
548
+ const verification = verifyDeterministicReplayBundle(bundle, {
549
+ runtimeVersion: 'engine-complete@1',
550
+ engineHash: 'engine-hash',
551
+ schemaHash: 'schema-hash',
552
+ assetDbHash: 'asset-hash',
553
+ configHash: 'config-hash',
554
+ semanticReplayHash: bundle.semanticReplayHash,
555
+ });
556
+ const componentVerification = verifyDeterministicReplayBundle(createComponentReplayBundle());
557
+ const corrupted = createDeterministicReplayBundle({
558
+ ...bundle,
559
+ checksums: bundle.checksums.map((checksum, index) => index === Math.min(3, bundle.checksums.length - 1) ? { ...checksum, checksum: 'corrupt' } : checksum),
560
+ });
561
+ const mismatch = minimizeDeterministicReplayDesync(bundle, corrupted);
562
+ const minimization = minimizeDeterministicReplayDesyncBundle(bundle, corrupted);
563
+ const minimized = minimization.bundle;
564
+ const minimizedVerification = minimization.verifier;
565
+ const sliceStartIndex = Math.min(1, Math.max(0, bundle.checksums.length - 1));
566
+ const sliceEndIndex = Math.min(4, Math.max(sliceStartIndex, bundle.checksums.length - 1));
567
+ const sliced = sliceDeterministicReplayBundle(bundle, bundle.checksums[sliceStartIndex].frame, bundle.checksums[sliceEndIndex].frame);
568
+ const sliceVerification = verifyDeterministicReplayBundle(sliced, {
569
+ runtimeVersion: 'engine-complete@1',
570
+ engineHash: 'engine-hash',
571
+ schemaHash: 'schema-hash',
572
+ assetDbHash: 'asset-hash',
573
+ configHash: 'config-hash',
574
+ semanticReplayHash: sliced.semanticReplayHash,
575
+ });
576
+ const frameRuntimeReplay = createFrameRuntimeReplayProof(executedFrames);
577
+ const debug = createDeterministicReplayDebugTables(bundle.debugEvents);
578
+ return categoryReport('replay-bundle', verification.bundleHash, {
579
+ verifiesColdStart: verification.result === 'pass',
580
+ mismatchMinimized: mismatch.frame === bundle.checksums[Math.min(3, bundle.checksums.length - 1)].frame,
581
+ minimizedArtifactVerifierRunnable: minimizedVerification.result === 'fail',
582
+ minimizedArtifactReproducesMismatch: minimizedVerification.firstMismatch?.frame === mismatch.frame,
583
+ minimizedArtifactReducesBytes: minimization.minimizedReplayBytes < minimization.originalReplayBytes,
584
+ minimizedArtifactRemovesIrrelevantInputs: minimization.removedInputFrames > 0 && minimization.removedInputs > 0,
585
+ minimizedArtifactRemovesIrrelevantCommands: minimization.removedCommandFrames > 0 && minimization.removedCommands > 0,
586
+ minimizedArtifactRemovesIrrelevantDebugEvents: minimization.removedDebugEvents > 0,
587
+ sliceContainsChecksums: sliced.checksums.length === sliceEndIndex - sliceStartIndex + 1,
588
+ sliceSelfContained: sliced.metadata.selfContainedSlice === true && sliced.sliceOrigin !== undefined,
589
+ sliceVerifiesFromBaseline: sliceVerification.result === 'pass' && sliceVerification.semanticReplayExecuted,
590
+ sliceSourceIdentityPresent: sliced.sliceOrigin?.sourceBundleHash === verification.bundleHash,
591
+ inputHistoryPresent: verification.inputCount > 0,
592
+ commandHistoryPresent: verification.commandCount > 0,
593
+ allPlayersSampled: verification.inputCount === players * executedFrames && verification.commandCount === players * executedFrames,
594
+ semanticReplayExecuted: verification.semanticReplayExecuted,
595
+ semanticReplayFullHistoryVerified: verification.semanticReplayFrames === executedFrames,
596
+ fullReplayHistory: bundle.metadata.fullReplayHistory === true,
597
+ fullReplayNotCheckpoint: bundle.metadata.checkpointBundle !== true,
598
+ fullReplayCappedHonest: bundle.metadata.fullReplayCapped === (executedFrames < requestedFrames),
599
+ componentSemanticReplayExecuted: componentVerification.semanticReplayExecuted && componentVerification.result === 'pass',
600
+ frameRuntimeReplayExecuted: frameRuntimeReplay.verification.result === 'pass',
601
+ selfContainedFrameRuntimeReplayExecuted: frameRuntimeReplay.selfContainedVerification.result === 'pass',
602
+ frameRuntimeReplayFullHistoryVerified: frameRuntimeReplay.verification.frames === frameRuntimeReplay.snapshotCount,
603
+ selfContainedFrameRuntimeReplayFullHistoryVerified: frameRuntimeReplay.selfContainedVerification.frames === frameRuntimeReplay.snapshotCount,
604
+ frameRuntimeReplayCoversExecutedFrames: frameRuntimeReplay.verification.frames >= executedFrames,
605
+ selfContainedFrameRuntimeReplayCoversExecutedFrames: frameRuntimeReplay.selfContainedVerification.frames >= executedFrames,
606
+ forgedFrameRuntimeReplayRejected: frameRuntimeReplay.forgedVerification.result === 'fail'
607
+ && frameRuntimeReplay.forgedVerification.firstMismatch?.subsystem === 'frame-runtime-replay',
608
+ finalFrameCovered: bundle.checksums.at(-1)?.frame === executedFrames - 1,
609
+ requestedFrameCoverageDeclared: bundle.metadata.requestedFrames === requestedFrames && bundle.metadata.executedFrames === executedFrames,
610
+ debugTablesPresent: debug.ecs.length > 0 && debug.physics.length > 0 && debug.commands.length > 0,
611
+ }, withFrameMetrics({
612
+ replayBytes: verification.replayBytes,
613
+ snapshotBytes: verification.snapshotBytes,
614
+ inputCount: verification.inputCount,
615
+ checksumCount: verification.checksumCount,
616
+ commandCount: verification.commandCount,
617
+ debugEventCount: verification.debugEventCount,
618
+ semanticReplayFrames: verification.semanticReplayFrames,
619
+ simulatedFrames: executedFrames,
620
+ fullReplayFrames: executedFrames,
621
+ fullReplayRequestedFrames: requestedFrames,
622
+ fullReplayCapped: executedFrames < requestedFrames,
623
+ simulationHash: simulationHashValue.toString(16),
624
+ componentSemanticReplayFrames: componentVerification.semanticReplayFrames,
625
+ componentReplayBytes: componentVerification.replayBytes,
626
+ frameRuntimeReplayFrames: frameRuntimeReplay.verification.frames,
627
+ selfContainedFrameRuntimeReplayFrames: frameRuntimeReplay.selfContainedVerification.frames,
628
+ frameRuntimeReplaySnapshots: frameRuntimeReplay.snapshotCount,
629
+ frameRuntimeReplayFinalFrame: frameRuntimeReplay.verification.finalFrame,
630
+ selfContainedFrameRuntimeReplayFinalFrame: frameRuntimeReplay.selfContainedVerification.finalFrame,
631
+ frameRuntimeReplayBundleHash: frameRuntimeReplay.verification.bundleHash,
632
+ selfContainedFrameRuntimeReplayBundleHash: frameRuntimeReplay.selfContainedVerification.bundleHash,
633
+ forgedFrameRuntimeReplayFrame: frameRuntimeReplay.forgedVerification.firstMismatch.frame,
634
+ minimizedFrame: mismatch.frame,
635
+ minimizedVerificationResult: minimizedVerification.result,
636
+ minimizedVerificationFrame: minimizedVerification.firstMismatch.frame,
637
+ minimizedVerificationSubsystem: minimizedVerification.firstMismatch.subsystem,
638
+ minimizedArtifactChecksums: minimized.checksums.length,
639
+ minimizedOriginalReplayBytes: minimization.originalReplayBytes,
640
+ minimizedReplayBytes: minimization.minimizedReplayBytes,
641
+ minimizedRemovedInputFrames: minimization.removedInputFrames,
642
+ minimizedRemovedInputs: minimization.removedInputs,
643
+ minimizedRemovedCommandFrames: minimization.removedCommandFrames,
644
+ minimizedRemovedCommands: minimization.removedCommands,
645
+ minimizedRemovedDebugEvents: minimization.removedDebugEvents,
646
+ minimizedRemovedSnapshots: minimization.removedSnapshots,
647
+ minimizedRemovedChecksumFrames: minimization.removedChecksumFrames,
648
+ sliceBaselineFrame: sliced.sliceOrigin.baselineSnapshot.frame,
649
+ sliceBaselineChecksum: sliced.sliceOrigin.baselineSnapshot.checksum,
650
+ sliceSourceBundleHash: sliced.sliceOrigin.sourceBundleHash,
651
+ sliceVerificationFrames: sliceVerification.semanticReplayFrames,
652
+ checkpointStride: 0,
653
+ checkpointBundle: false,
654
+ checkpointCount: 0,
655
+ }, requestedFrames, executedFrames));
656
+ }
657
+ function integratedHeadlessSceneCategory(players, requestedFrames, executedFrames, seed) {
658
+ const scene = runDeterministicIntegratedHeadlessScene(executedFrames, players, seed);
659
+ return categoryReport('integrated-headless-scene', scene.checksum, {
660
+ frameRuntimeExecuted: scene.invariants.frameRuntimeExecuted === true,
661
+ commandsExecuted: scene.invariants.commandsExecuted === true,
662
+ replayVerified: scene.replayVerified,
663
+ rollbackEquivalent: scene.rollbackEquivalent,
664
+ physicsExecuted: scene.invariants.physicsExecuted === true,
665
+ kccExecuted: scene.invariants.kccExecuted === true,
666
+ navmeshExecuted: scene.invariants.navmeshExecuted === true,
667
+ botExecuted: scene.invariants.botExecuted === true,
668
+ animationExecuted: scene.invariants.animationExecuted === true,
669
+ assetsConfigCovered: scene.invariants.assetsConfigCovered === true,
670
+ sameFrameCommandsCovered: scene.metrics.sameFrameCommandsAccepted >= 2,
671
+ fullSceneFramesCovered: scene.metrics.frames === executedFrames,
672
+ replayWindowVerified: scene.metrics.replayFrames <= scene.metrics.frames
673
+ && scene.metrics.replaySnapshots === scene.metrics.replayFrames + 1,
674
+ replayWindowCapDeclared: scene.metrics.replayCapped === (scene.metrics.replayFrames < scene.metrics.frames),
675
+ playersCovered: scene.metrics.players === players,
676
+ }, withFrameMetrics({
677
+ checksum: scene.checksum,
678
+ replayChecksum: scene.replayChecksum,
679
+ rollbackChecksum: scene.rollbackChecksum,
680
+ replayVerified: scene.replayVerified,
681
+ rollbackEquivalent: scene.rollbackEquivalent,
682
+ frames: scene.metrics.frames,
683
+ replayFrames: scene.metrics.replayFrames,
684
+ replayCapped: scene.metrics.replayCapped,
685
+ players: scene.metrics.players,
686
+ commandsAccepted: scene.metrics.commandsAccepted,
687
+ sameFrameCommandsAccepted: scene.metrics.sameFrameCommandsAccepted,
688
+ physicsContacts: scene.metrics.physicsContacts,
689
+ physicsQueries: scene.metrics.physicsQueries,
690
+ kccCorrections: scene.metrics.kccCorrections,
691
+ navReplans: scene.metrics.navReplans,
692
+ botTraceRows: scene.metrics.botTraceRows,
693
+ animationEvents: scene.metrics.animationEvents,
694
+ replaySnapshots: scene.metrics.replaySnapshots,
695
+ debugEvents: scene.metrics.debugEvents,
696
+ configHashCovered: scene.metrics.configHashCovered,
697
+ }, requestedFrames, executedFrames));
698
+ }
699
+ function projectRuntimeCategory(requestedFrames, executedFrames, seed) {
700
+ const manifest = readProjectFixtureJson('project.json');
701
+ const descriptorSource = readProjectFixtureJson(manifest.descriptor);
702
+ const descriptor = defineSyncplayGame(descriptorSource);
703
+ const runtimeDescriptor = projectRuntimeDescriptorFromGameDescriptor(descriptor);
704
+ const runtimeAccessors = createDeterministicFrameRuntimeAccessors(runtimeDescriptor);
705
+ const generatedSource = generateDeterministicFrameRuntimeSource(runtimeDescriptor);
706
+ const generatedSourceHash = defaultChecksum(generatedSource);
707
+ const assets = cookDeterministicAssets(readProjectFixtureJson(manifest.assets));
708
+ const config = cookDeterministicConfigBundle(readProjectFixtureJson(manifest.config), 1);
709
+ const configVerification = verifyDeterministicConfigBundle(config, {
710
+ engineHash: config.engineHash,
711
+ schemaHash: config.schemaHash,
712
+ configHash: config.configHash,
713
+ assetDbHash: config.assetDbHash,
714
+ });
715
+ const prototypeValidation = validatePrototypeEntitiesAgainstFrameRuntime(assets, 'player.prototype', {
716
+ ...runtimeDescriptor,
717
+ components: runtimeDescriptor.components.map((component) => component.id === 'gameState' ? { ...component, singleton: false } : component),
718
+ }, 1);
719
+ const botDocument = readProjectFixtureJson(manifest.botDocuments[0] ?? 'bot.json');
720
+ const assetRefs = assets.cookedAssets.map((asset) => asset.id);
721
+ const compiledBot = compileDeterministicBotDocument(botDocument, assetRefs);
722
+ const botFrames = Math.max(12, Math.min(executedFrames, 512));
723
+ const botTraces = traceDeterministicBotDocument(botDocument, [1, 2], botFrames, assetRefs);
724
+ const botCoverage = summarizeDeterministicBotDocumentTraceCoverage(botDocument, botTraces);
725
+ const navmeshSource = readProjectFixtureJson(manifest.navmeshes[0] ?? 'navmesh.json');
726
+ const navmesh = cookDeterministicNavmesh(navmeshSource);
727
+ const navPath = findDeterministicNavmeshPath(navmesh, { start: 'spawn', goal: 'goal' });
728
+ let navAgents = [{
729
+ id: 'project-agent',
730
+ polygonId: 'spawn',
731
+ targetPolygonId: 'goal',
732
+ speedPerFrame: 1,
733
+ radius: 1,
734
+ avoidancePriority: 1,
735
+ }];
736
+ let navTraceChecksum = '';
737
+ let navWaypointAdvances = 0;
738
+ const navFrames = Math.max(2, Math.min(executedFrames, 512));
739
+ for (let frame = 0; frame < navFrames; frame += 1) {
740
+ const step = stepDeterministicNavmeshAgents(navmesh, navAgents, { frame });
741
+ navAgents = step.agents;
742
+ navWaypointAdvances += step.waypointAdvances;
743
+ navTraceChecksum = step.checksum;
744
+ }
745
+ const animationDescriptor = readProjectFixtureJson(manifest.animations[0] ?? 'animation.json');
746
+ const animation = cookDeterministicAnimation(animationDescriptor);
747
+ const animationScript = readProjectFixtureJson(manifest.animationParameters?.[manifest.animations[0] ?? 'animation.json'] ?? 'animation-params.json');
748
+ const animationUpdates = new Map(animationScript.frames.map((entry) => [entry.frame, entry.parameters]));
749
+ let animationState = createDeterministicAnimationRuntimeState(animationDescriptor);
750
+ let animationTransitions = 0;
751
+ let animationEvents = 0;
752
+ let animationChecksum = '';
753
+ const animationFrames = Math.max(12, Math.min(executedFrames, 512));
754
+ for (let frame = 0; frame < animationFrames; frame += 1) {
755
+ const result = stepDeterministicAnimation(animationDescriptor, animationState, animationUpdates.get(frame) ?? {});
756
+ animationState = result.state;
757
+ animationTransitions += result.transitionCount;
758
+ animationEvents += result.events.length;
759
+ animationChecksum = result.checksum;
760
+ }
761
+ const staticDiagnostics = manifest.staticPaths.flatMap((path) => checkSourceText(path, readProjectFixtureText(path), 'strict', {
762
+ schemaStateFields: descriptor.state.fields.map((field) => field.name),
763
+ }));
764
+ const replay = createProjectRuntimeReplayProof(runtimeDescriptor, generatedSourceHash, executedFrames, seed);
765
+ const checksum = defaultChecksum({
766
+ descriptor: descriptor.artifactId,
767
+ runtimeDescriptor: runtimeAccessors.descriptorHash,
768
+ generatedSourceHash,
769
+ assetDbHash: assets.manifest.assetDbHash,
770
+ configHash: config.configHash,
771
+ botHash: compiledBot.hash,
772
+ navmeshHash: navmesh.hash,
773
+ animationHash: animation.hash,
774
+ replayHash: replay.verification.bundleHash,
775
+ staticDiagnostics,
776
+ });
777
+ return categoryReport('project-runtime', checksum, {
778
+ descriptorCertified: descriptor.maxPlayers === 128 && runtimeAccessors.descriptorHash.length > 0,
779
+ generatedRuntimeSourceCertified: generatedSourceHash.length > 0 && generatedSource.includes('recordSpawnCommand'),
780
+ assetsCertified: assets.manifest.assetDbHash === config.assetDbHash,
781
+ configCertified: configVerification.result === 'pass',
782
+ prototypeRuntimeValidated: prototypeValidation.injectedComponentCount > 0 && prototypeValidation.checksum.length > 0,
783
+ botDocumentCertified: compiledBot.hash.length > 0 && botCoverage.fullModelCoverageComplete,
784
+ navmeshCertified: navPath.polygons.join('>') === 'spawn>goal' && navWaypointAdvances > 0,
785
+ animationCertified: animationTransitions >= 2 && animationEvents > 0,
786
+ staticDeterminismCertified: staticDiagnostics.filter((diagnostic) => diagnostic.severity === 'error').length === 0,
787
+ replayRuntimeVerified: replay.verification.result === 'pass',
788
+ selfContainedReplayRuntimeVerified: replay.selfContainedVerification.result === 'pass',
789
+ replayUsesProjectGameStateRuntime: replay.systemId === 'project-game-state-runtime-replay-v1',
790
+ replayAcceptsGeneratedRuntimeSourceHash: replay.acceptedGeneratedRuntimeSourceHash,
791
+ replayRuntimeCoversProjectCommands: runtimeDescriptor.commandChannels?.some((channel) => channel.id === 'spawn') === true,
792
+ replayRuntimeCoversProjectEvents: runtimeDescriptor.eventChannels?.some((event) => event.id === 'damage') === true,
793
+ projectFrameCapDeclared: executedFrames <= requestedFrames,
794
+ executedRequestedFrames: executedFrames === requestedFrames ? true : 'capped',
795
+ }, withFrameMetrics({
796
+ artifactId: descriptor.artifactId,
797
+ runtimeDescriptorHash: runtimeAccessors.descriptorHash,
798
+ generatedSourceHash,
799
+ generatedSourceBytes: generatedSource.length,
800
+ assetDbHash: assets.manifest.assetDbHash,
801
+ configHash: config.configHash,
802
+ prototypeRuntimeValidationChecksum: prototypeValidation.checksum,
803
+ botHash: compiledBot.hash,
804
+ botTraceRows: botTraces.length,
805
+ botCoverageComplete: botCoverage.fullModelCoverageComplete,
806
+ navmeshHash: navmesh.hash,
807
+ navPathHash: navPath.pathHash,
808
+ navWaypointAdvances,
809
+ navTraceChecksum,
810
+ animationHash: animation.hash,
811
+ animationTransitions,
812
+ animationEvents,
813
+ animationChecksum,
814
+ staticDiagnostics: staticDiagnostics.length,
815
+ replayBundleHash: replay.verification.bundleHash,
816
+ replayFinalChecksum: replay.verification.finalChecksum,
817
+ selfContainedReplayFinalChecksum: replay.selfContainedVerification.finalChecksum,
818
+ replaySnapshotCount: replay.snapshotCount,
819
+ replaySystemId: replay.systemId,
820
+ replayAcceptedGeneratedRuntimeSourceHash: replay.acceptedGeneratedRuntimeSourceHash,
821
+ replayRuntimeComponentCount: runtimeDescriptor.components.length,
822
+ replayRuntimeCommandChannelCount: runtimeDescriptor.commandChannels?.length ?? 0,
823
+ replayRuntimeEventChannelCount: runtimeDescriptor.eventChannels?.length ?? 0,
824
+ capped: executedFrames < requestedFrames,
825
+ }, requestedFrames, executedFrames));
826
+ }
827
+ function readProjectFixtureText(path) {
828
+ for (const candidate of projectFixtureCandidates(path)) {
829
+ if (existsSync(candidate)) {
830
+ return readFileSync(candidate, 'utf8');
831
+ }
832
+ }
833
+ throw new Error(`Unable to locate deterministic project fixture ${path}`);
834
+ }
835
+ function readProjectFixtureJson(path) {
836
+ return JSON.parse(readProjectFixtureText(path));
837
+ }
838
+ function projectFixtureCandidates(path) {
839
+ return [
840
+ resolve(process.cwd(), 'fixtures/projects/engine-parity', path),
841
+ resolve(process.cwd(), 'packages/syncplay/fixtures/projects/engine-parity', path),
842
+ resolve(process.cwd(), '../syncplay/fixtures/projects/engine-parity', path),
843
+ ];
844
+ }
845
+ function projectRuntimeDescriptorFromGameDescriptor(descriptor) {
846
+ return {
847
+ id: `${descriptor.artifactId}/project-runtime-replay`,
848
+ tickRate: descriptor.tickRate,
849
+ components: [
850
+ {
851
+ id: 'gameState',
852
+ singleton: true,
853
+ fields: descriptor.state.fields
854
+ .filter((field) => field.checksummed)
855
+ .map((field) => ({
856
+ name: field.name,
857
+ kind: projectRuntimeFieldKind(field.kind),
858
+ defaultValue: projectRuntimeDefaultFieldValue(field.kind),
859
+ })),
860
+ },
861
+ {
862
+ id: 'runtimeReplayState',
863
+ singleton: true,
864
+ fields: [
865
+ { name: 'value', kind: 'int', defaultValue: 0 },
866
+ { name: 'inputDelta', kind: 'int', defaultValue: 0 },
867
+ { name: 'commandDelta', kind: 'int', defaultValue: 0 },
868
+ ],
869
+ },
870
+ ],
871
+ systems: [{ id: 'simulate', group: 'simulation', order: 0 }],
872
+ commandChannels: descriptor.commandChannels?.map((channel) => ({
873
+ id: channel.id,
874
+ reliable: channel.delivery === 'reliable',
875
+ allowPrediction: channel.delivery === 'reliable',
876
+ maxPayloadBytes: Math.max(64, channel.fields.length * 64),
877
+ ...(channel.delivery === 'unreliable' ? { minFrameLead: 1 } : {}),
878
+ maxFrameLead: 32,
879
+ })),
880
+ eventChannels: descriptor.events?.map((event) => ({
881
+ id: event.id,
882
+ fields: event.payloadFields.map((field) => ({
883
+ name: field.name,
884
+ kind: projectRuntimeFieldKind(field.kind),
885
+ defaultValue: projectRuntimeDefaultFieldValue(field.kind),
886
+ })),
887
+ })),
888
+ };
889
+ }
890
+ function projectRuntimeFieldKind(kind) {
891
+ if (kind === 'boolean') {
892
+ return 'bool';
893
+ }
894
+ if (kind === 'string' || kind === 'asset-ref') {
895
+ return 'string';
896
+ }
897
+ return 'int';
898
+ }
899
+ function projectRuntimeDefaultFieldValue(kind) {
900
+ if (kind === 'boolean') {
901
+ return false;
902
+ }
903
+ if (kind === 'string' || kind === 'asset-ref') {
904
+ return '';
905
+ }
906
+ return 0;
907
+ }
908
+ function projectRuntimeDefaultGameState(descriptor) {
909
+ const gameState = descriptor.components.find((component) => component.id === 'gameState' && component.singleton === true);
910
+ return Object.fromEntries(gameState.fields.map((field) => [
911
+ field.name,
912
+ field.defaultValue,
913
+ ]));
914
+ }
915
+ function createProjectRuntimeReplayProof(descriptor, generatedSourceHash, frames, seed) {
916
+ const runtime = createDeterministicFrameRuntime(descriptor, seed);
917
+ runtime.setSingleton('gameState', projectRuntimeDefaultGameState(descriptor));
918
+ runtime.setSingleton('runtimeReplayState', { value: 0, inputDelta: 0, commandDelta: 0 });
919
+ const firstSnapshot = runtime.snapshot();
920
+ const snapshots = [{ frame: firstSnapshot.frame, bytes: firstSnapshot.bytes, checksum: firstSnapshot.checksum }];
921
+ const inputHistory = Array.from({ length: frames }, (_, frame) => ({
922
+ frame,
923
+ inputs: projectRuntimeReplayInputsForFrame(frame),
924
+ }));
925
+ const commandHistory = Array.from({ length: frames }, (_, frame) => ({
926
+ frame,
927
+ commands: projectRuntimeReplayCommandsForFrame(frame),
928
+ }));
929
+ const systems = {
930
+ simulate: projectRuntimeReplaySystem,
931
+ };
932
+ for (let frame = 0; frame < frames; frame += 1) {
933
+ injectProjectRuntimeReplayFrame(runtime, frame, inputHistory[frame].inputs, commandHistory[frame].commands);
934
+ runtime.step(systems);
935
+ const snapshot = runtime.snapshot();
936
+ snapshots.push({ frame: snapshot.frame, bytes: snapshot.bytes, checksum: snapshot.checksum });
937
+ }
938
+ const bundle = createDeterministicReplayBundle({
939
+ runtimeVersion: 'engine-complete-project@1',
940
+ engineHash: defaultChecksum(descriptor),
941
+ schemaHash: defaultChecksum(descriptor),
942
+ assetDbHash: 'fixture-project-assets',
943
+ configHash: 'fixture-project-config',
944
+ sampleId: 'engine-complete-project-runtime',
945
+ inputHistory,
946
+ commandHistory,
947
+ checksums: snapshots.map((snapshot) => ({ frame: snapshot.frame, checksum: snapshot.checksum })),
948
+ debugEvents: runtime.debugEvents,
949
+ snapshots,
950
+ runtimeReplay: createDeterministicReplayRuntimeManifest({
951
+ descriptor,
952
+ systemId: 'project-game-state-runtime-replay-v1',
953
+ beforeFrameId: 'project-game-state-input-command-delta-v1',
954
+ acceptedSystemsSourceHashes: [generatedSourceHash],
955
+ seed,
956
+ }),
957
+ metadata: {
958
+ projectRuntime: true,
959
+ generatedSourceHash,
960
+ frames,
961
+ },
962
+ });
963
+ const verification = verifyDeterministicFrameRuntimeReplayBundle(bundle, {
964
+ descriptor,
965
+ systems,
966
+ systemsSourceHash: generatedSourceHash,
967
+ requireRuntimeIdentity: true,
968
+ seed,
969
+ beforeFrame: injectProjectRuntimeReplayFrame,
970
+ });
971
+ const selfContainedVerification = verifyDeterministicSelfContainedFrameRuntimeReplayBundle(bundle);
972
+ return {
973
+ verification,
974
+ selfContainedVerification,
975
+ snapshotCount: snapshots.length,
976
+ systemId: bundle.runtimeReplay.systemId,
977
+ acceptedGeneratedRuntimeSourceHash: bundle.runtimeReplay.acceptedSystemsSourceHashes.includes(generatedSourceHash),
978
+ };
979
+ }
980
+ const projectRuntimeReplaySystem = function runProjectRuntimeReplaySystem(ctx) {
981
+ const replayState = ctx.getSingleton('runtimeReplayState');
982
+ const value = Number(replayState.value) + Number(replayState.inputDelta) + Number(replayState.commandDelta);
983
+ ctx.setSingleton('runtimeReplayState', {
984
+ value,
985
+ inputDelta: 0,
986
+ commandDelta: 0,
987
+ });
988
+ const gameState = ctx.getSingleton('gameState');
989
+ ctx.setSingleton('gameState', patchProjectRuntimeGameState(gameState, ctx.frame + 1, value));
990
+ if (ctx.frame % 128 === 0) {
991
+ ctx.emitDebug('ecs.project-runtime-replay', { frame: ctx.frame, value });
992
+ }
993
+ };
994
+ function projectRuntimeReplayInputsForFrame(frame) {
995
+ const variant = frame % 4;
996
+ if (variant === 0) {
997
+ return [{ slot: 0, dx: frame + 1 }];
998
+ }
999
+ if (variant === 1) {
1000
+ return [{ slot: 0, moveX: frame + 1 }];
1001
+ }
1002
+ if (variant === 2) {
1003
+ return [frame];
1004
+ }
1005
+ return [{ slot: 0 }];
1006
+ }
1007
+ function projectRuntimeReplayCommandsForFrame(frame) {
1008
+ const variant = frame % 4;
1009
+ if (variant === 0) {
1010
+ return [{ id: `project-runtime:${frame}`, payload: { power: frame % 3, prototype: 'player.prototype' } }];
1011
+ }
1012
+ if (variant === 1) {
1013
+ return [{ id: `project-runtime:${frame}`, payload: { prototype: 'player.prototype', slot: frame % 4 } }];
1014
+ }
1015
+ if (variant === 2) {
1016
+ return [{ id: `project-runtime:${frame}`, payload: 'ignored' }];
1017
+ }
1018
+ return [{ id: `project-runtime:${frame}`, payload: { prototype: 'player.prototype' } }];
1019
+ }
1020
+ function injectProjectRuntimeReplayFrame(runtime, frame, inputs, commands) {
1021
+ const inputDelta = inputs.reduce((sum, input) => {
1022
+ if (!isCanonicalRecord(input)) {
1023
+ return sum;
1024
+ }
1025
+ if (typeof input.dx === 'number') {
1026
+ return sum + input.dx;
1027
+ }
1028
+ if (typeof input.moveX === 'number') {
1029
+ return sum + input.moveX;
1030
+ }
1031
+ return sum;
1032
+ }, 0);
1033
+ const commandDelta = commands.reduce((sum, command) => {
1034
+ if (!isCanonicalRecord(command) || !isCanonicalRecord(command.payload)) {
1035
+ return sum;
1036
+ }
1037
+ if (typeof command.payload.power === 'number') {
1038
+ return sum + command.payload.power;
1039
+ }
1040
+ if (typeof command.payload.slot === 'number') {
1041
+ return sum + command.payload.slot;
1042
+ }
1043
+ return sum;
1044
+ }, 0);
1045
+ const replayState = runtime.snapshot().singletonComponents.runtimeReplayState;
1046
+ runtime.setSingleton('runtimeReplayState', {
1047
+ value: Number(replayState.value),
1048
+ inputDelta,
1049
+ commandDelta,
1050
+ });
1051
+ const gameState = runtime.snapshot().singletonComponents.gameState;
1052
+ runtime.setSingleton('gameState', patchProjectRuntimeGameState(gameState, frame, Number(replayState.value)));
1053
+ }
1054
+ function patchProjectRuntimeGameState(gameState, frame, score) {
1055
+ return Object.fromEntries(Object.keys(gameState).sort().map((key) => {
1056
+ const value = gameState[key];
1057
+ if (key === 'tick' && typeof value === 'number') {
1058
+ return [key, frame];
1059
+ }
1060
+ if (key === 'score' && typeof value === 'number') {
1061
+ return [key, score];
1062
+ }
1063
+ if (key === 'alive' && typeof value === 'boolean') {
1064
+ return [key, true];
1065
+ }
1066
+ return [key, value];
1067
+ }));
1068
+ }
1069
+ function createFrameRuntimeReplayProof(frames) {
1070
+ const descriptor = {
1071
+ id: 'engine-complete-runtime-replay',
1072
+ tickRate: 60,
1073
+ components: [{
1074
+ id: 'score',
1075
+ singleton: true,
1076
+ fields: [
1077
+ { name: 'value', kind: 'int', defaultValue: 0 },
1078
+ { name: 'inputDelta', kind: 'int', defaultValue: 0 },
1079
+ { name: 'commandDelta', kind: 'int', defaultValue: 0 },
1080
+ ],
1081
+ }],
1082
+ systems: [{ id: 'simulate', group: 'simulation', order: 0 }],
1083
+ };
1084
+ const systems = {
1085
+ simulate(ctx) {
1086
+ const score = ctx.getSingleton('score');
1087
+ ctx.setSingleton('score', {
1088
+ value: Number(score.value) + Number(score.inputDelta) + Number(score.commandDelta),
1089
+ inputDelta: 0,
1090
+ commandDelta: 0,
1091
+ });
1092
+ if (ctx.frame % 128 === 0) {
1093
+ ctx.emitDebug('ecs.runtime-replay', { frame: ctx.frame, value: Number(score.value) });
1094
+ }
1095
+ },
1096
+ };
1097
+ const runtime = createDeterministicFrameRuntime(descriptor, 91);
1098
+ runtime.setSingleton('score', { value: 0, inputDelta: 0, commandDelta: 0 });
1099
+ const firstSnapshot = runtime.snapshot();
1100
+ const snapshots = [{ frame: firstSnapshot.frame, bytes: firstSnapshot.bytes, checksum: firstSnapshot.checksum }];
1101
+ const inputHistory = Array.from({ length: frames }, (_, frame) => ({
1102
+ frame,
1103
+ inputs: frame % 2 === 0 ? [{ slot: 0, dx: frame + 1 }] : [frame],
1104
+ }));
1105
+ const commandHistory = Array.from({ length: frames }, (_, frame) => ({
1106
+ frame,
1107
+ commands: frame % 2 === 0 ? [{ id: `runtime-replay:${frame}`, payload: { power: frame % 3 } }] : [{ id: `runtime-replay:${frame}`, payload: 'ignored' }],
1108
+ }));
1109
+ for (let frame = 0; frame < frames; frame += 1) {
1110
+ injectFrameRuntimeReplayInputs(runtime, frame, inputHistory[frame].inputs, commandHistory[frame].commands);
1111
+ runtime.step(systems);
1112
+ const snapshot = runtime.snapshot();
1113
+ snapshots.push({ frame: snapshot.frame, bytes: snapshot.bytes, checksum: snapshot.checksum });
1114
+ }
1115
+ const bundle = createDeterministicReplayBundle({
1116
+ runtimeVersion: 'engine-complete@1',
1117
+ engineHash: 'engine-hash',
1118
+ schemaHash: defaultChecksum(descriptor),
1119
+ assetDbHash: 'asset-hash',
1120
+ configHash: 'config-hash',
1121
+ sampleId: 'engine-complete-frame-runtime',
1122
+ inputHistory,
1123
+ commandHistory,
1124
+ checksums: snapshots.map((snapshot) => ({ frame: snapshot.frame, checksum: snapshot.checksum })),
1125
+ debugEvents: runtime.debugEvents,
1126
+ snapshots,
1127
+ runtimeReplay: createDeterministicReplayRuntimeManifest({
1128
+ descriptor,
1129
+ systemId: 'score-runtime-replay-v1',
1130
+ beforeFrameId: 'score-input-command-delta-v1',
1131
+ seed: 91,
1132
+ }),
1133
+ metadata: { frameRuntimeReplay: true, selfContainedRuntimeReplay: true, frames },
1134
+ });
1135
+ const replayOptions = {
1136
+ descriptor,
1137
+ systems,
1138
+ seed: 91,
1139
+ beforeFrame: injectFrameRuntimeReplayInputs,
1140
+ };
1141
+ const forgedSnapshots = bundle.snapshots.map((snapshot) => {
1142
+ if (snapshot.frame !== Math.min(2, frames)) {
1143
+ return snapshot;
1144
+ }
1145
+ const parsed = JSON.parse(snapshot.bytes);
1146
+ parsed.singletonComponents.score.value += 17;
1147
+ const bytes = canonicalStringify(parsed);
1148
+ return { frame: snapshot.frame, bytes, checksum: defaultChecksum(bytes) };
1149
+ });
1150
+ const forged = createDeterministicReplayBundle({
1151
+ ...bundle,
1152
+ snapshots: forgedSnapshots,
1153
+ checksums: forgedSnapshots.map((snapshot) => ({ frame: snapshot.frame, checksum: snapshot.checksum })),
1154
+ });
1155
+ return {
1156
+ verification: verifyDeterministicFrameRuntimeReplayBundle(bundle, replayOptions),
1157
+ selfContainedVerification: verifyDeterministicSelfContainedFrameRuntimeReplayBundle(bundle),
1158
+ forgedVerification: verifyDeterministicFrameRuntimeReplayBundle(forged, replayOptions),
1159
+ snapshotCount: snapshots.length,
1160
+ };
1161
+ }
1162
+ function injectFrameRuntimeReplayInputs(runtime, _frame, inputs, commands) {
1163
+ const inputDelta = inputs.reduce((sum, input) => isCanonicalRecord(input) && typeof input.dx === 'number' ? sum + input.dx : sum, 0);
1164
+ const commandDelta = commands.reduce((sum, command) => {
1165
+ if (!isCanonicalRecord(command) || !isCanonicalRecord(command.payload) || typeof command.payload.power !== 'number') {
1166
+ return sum;
1167
+ }
1168
+ return sum + command.payload.power;
1169
+ }, 0);
1170
+ const score = runtime.snapshot().singletonComponents.score;
1171
+ runtime.setSingleton('score', {
1172
+ value: Number(score.value),
1173
+ inputDelta,
1174
+ commandDelta,
1175
+ });
1176
+ }
1177
+ function isCanonicalRecord(value) {
1178
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1179
+ }
1180
+ function createComponentReplayBundle() {
1181
+ const firstBytes = canonicalStringify({ frame: 0, components: { 1: { position: { x: 2 } } } });
1182
+ const secondBytes = canonicalStringify({ frame: 1, components: { 1: { position: { x: 5, y: 4 } } } });
1183
+ return createDeterministicReplayBundle({
1184
+ runtimeVersion: 'engine-complete@1',
1185
+ engineHash: 'engine-hash',
1186
+ schemaHash: 'component-schema-hash',
1187
+ assetDbHash: 'asset-hash',
1188
+ configHash: 'config-hash',
1189
+ sampleId: 'engine-complete-components',
1190
+ inputHistory: [
1191
+ { frame: 0, inputs: [{ entity: '1', component: 'position', field: 'x', delta: 2 }] },
1192
+ { frame: 1, inputs: [{ entity: '1', component: 'position', field: 'x', delta: 3 }] },
1193
+ ],
1194
+ commandHistory: [
1195
+ { frame: 1, commands: [{ id: 'set-y', payload: { entity: '1', component: 'position', field: 'y', value: 4 } }] },
1196
+ ],
1197
+ checksums: [
1198
+ { frame: 0, checksum: defaultChecksum(firstBytes) },
1199
+ { frame: 1, checksum: defaultChecksum(secondBytes) },
1200
+ ],
1201
+ debugEvents: [{ frame: 1, type: 'ecs.snapshot', payload: { component: 'position' } }],
1202
+ snapshots: [
1203
+ { frame: 0, bytes: firstBytes, checksum: defaultChecksum(firstBytes) },
1204
+ { frame: 1, bytes: secondBytes, checksum: defaultChecksum(secondBytes) },
1205
+ ],
1206
+ metadata: { semanticReplay: 'component-delta-v1' },
1207
+ });
1208
+ }
1209
+ function physicsCategory(bodies, requestedFrames, executedFrames, enforceThresholds, benchmark, noFastStress) {
1210
+ const metrics = runDeterministicPhysics3DStress(bodies, executedFrames, { allowFastStress: !noFastStress });
1211
+ const callbackMasks = runDeterministicPhysics3DCallbackMaskFixture();
1212
+ const queryOptions = runDeterministicPhysics3DQueryOptionsFixture();
1213
+ const materials = runDeterministicPhysics3DMaterialFixture();
1214
+ const details = runDeterministicPhysics3DDetailFixture();
1215
+ const stack = runDeterministicPhysics3DStackFixture();
1216
+ const lagComp3D = runDeterministicLagCompensation3DFixture(128, Math.min(executedFrames, 5_000), 71);
1217
+ const lagComp3DApi = runDeterministicLagCompensation3DApiFixture();
1218
+ const authoritativeLagComp3D = runDeterministicAuthoritativeLagCompensation3DFixture();
1219
+ const fullStep = runPhysics3DFullStepMeasurementFixture(Math.min(requestedFrames, 300));
1220
+ const highBodySample512 = requestedFrames > 512 ? runDeterministicPhysics3DStress(512, 128) : undefined;
1221
+ const highBodySample2048 = requestedFrames > 512 ? runDeterministicPhysics3DStress(2048, 32) : undefined;
1222
+ return categoryReport('physics3d', defaultChecksum({
1223
+ stress: metrics.checksum,
1224
+ materials: materials.checksum,
1225
+ stack: stack.checksum,
1226
+ lagComp3D: lagComp3D.checksum,
1227
+ lagComp3DApi: lagComp3DApi.checksum,
1228
+ authoritativeLagComp3D: authoritativeLagComp3D.checksum,
1229
+ }), {
1230
+ has3dBodies: metrics.bodies === bodies,
1231
+ scheduledQueriesRan: metrics.queryCount === executedFrames * 3,
1232
+ scheduledPrePhysicsQueriesRan: metrics.scheduledPrePhysicsQueries === executedFrames,
1233
+ scheduledPostPhysicsQueriesRan: metrics.scheduledPostPhysicsQueries === executedFrames * 2,
1234
+ scheduledShapeCastQueriesRan: metrics.scheduledShapeCastQueries === executedFrames,
1235
+ scheduledQueryResultsStable: metrics.scheduledQueryOrderHash.length > 0,
1236
+ shapeCastsCovered: metrics.shapeCastCount > 0,
1237
+ exactShapeNarrowphaseCovered: metrics.exactShapeRejects > 0,
1238
+ solverCorrectionsCovered: metrics.solverCorrections > 0,
1239
+ angularDynamicsCovered: metrics.angularBodies > 0,
1240
+ angularGeometryCovered: metrics.angularGeometryQueries > 0,
1241
+ meshStaticCollidersCovered: metrics.meshStaticColliders > 0,
1242
+ exactMeshRaycastsCovered: metrics.exactMeshRaycasts > 0,
1243
+ exactMeshOverlapRejectsCovered: metrics.exactMeshOverlapRejects > 0,
1244
+ rollbackEquivalent: metrics.rollbackEquivalent,
1245
+ callbackMasksCovered: callbackMasks.perEntityMaskHonored,
1246
+ queryOptionsCovered: queryOptions.defaultHitAll
1247
+ && queryOptions.explicitSolidsOnly
1248
+ && queryOptions.kindFiltersStable
1249
+ && queryOptions.triggerSensorFiltersStable
1250
+ && queryOptions.raycastCovered
1251
+ && queryOptions.overlapCovered
1252
+ && queryOptions.shapeCastCovered,
1253
+ detailedHitAndContactCovered: details.rayDetailedHits > 0
1254
+ && details.shapeCastDetailedHits > 0
1255
+ && details.contactDetails > 0
1256
+ && details.rayNormalX === -1
1257
+ && details.shapeCastPointX > 0
1258
+ && details.contactPenetration > 0,
1259
+ detailedPhysicsRollbackEquivalent: details.rollbackEquivalent,
1260
+ detailedPhysicsOrderStable: details.stableUnderBodyOrderPermutation,
1261
+ stackStabilityCovered: stack.contactCount > 0
1262
+ && stack.solverCorrections > 0
1263
+ && stack.maxPenetration <= 0.05
1264
+ && stack.maxHeightDrift <= 0.05
1265
+ && stack.settledDynamicBodies === stack.stackLayers,
1266
+ stackRollbackEquivalent: stack.rollbackEquivalent,
1267
+ stackAuthoringOrderStable: stack.stableUnderAuthoringOrderPermutation,
1268
+ materialResponseCovered: materials.combinedFriction === 3
1269
+ && materials.combinedRestitution === 7
1270
+ && materials.normalVelocityAfterBounce === -2
1271
+ && materials.tangentVelocityAfterFrictionY === 7
1272
+ && materials.tangentVelocityAfterFrictionZ === -7,
1273
+ materialRollbackEquivalent: materials.rollbackEquivalent,
1274
+ callbackLifecycleCovered: callbackMasks.collisionEnter > 0
1275
+ && callbackMasks.collisionStay > 0
1276
+ && callbackMasks.collisionExit > 0
1277
+ && callbackMasks.triggerEnter > 0
1278
+ && callbackMasks.triggerStay > 0
1279
+ && callbackMasks.triggerExit > 0,
1280
+ callbackRollbackEquivalent: callbackMasks.rollbackEquivalent,
1281
+ lagComp3DProxyLifecycleCovered: lagComp3D.proxyLifecycleBalanced
1282
+ && lagComp3D.proxyBodiesCreated > 0
1283
+ && lagComp3D.proxyBodiesDestroyed === lagComp3D.proxyBodiesCreated,
1284
+ lagComp3DHitboxProxyCovered: lagComp3D.multiHitboxQueries > 0 && lagComp3D.animatedHitboxSamples > 0,
1285
+ lagComp3DInterpolatedHitboxesCovered: lagComp3D.interpolatedHitboxSamples > 0,
1286
+ lagComp3DWeaponsCovered: lagComp3D.rifleShots > 0 && lagComp3D.shotgunShots > 0 && lagComp3D.burstShots > 0,
1287
+ lagComp3DOcclusionCovered: lagComp3D.staticOcclusionQueries > 0 && lagComp3D.occluded > 0,
1288
+ lagComp3DRejectionCovered: lagComp3D.staleRejected > 0 && lagComp3D.futureRejected > 0,
1289
+ lagComp3DRollbackEquivalent: lagComp3D.rollbackEquivalent,
1290
+ lagComp3DPerClientShotTimesCovered: lagComp3D.perClientShotTimesCovered,
1291
+ lagComp3DApiReusable: lagComp3DApi.hitReason === 'hit'
1292
+ && lagComp3DApi.occludedReason === 'occluded'
1293
+ && lagComp3DApi.staleReason === 'stale-frame'
1294
+ && lagComp3DApi.futureReason === 'future-frame',
1295
+ lagComp3DApiInterpolationCovered: lagComp3DApi.interpolatedRewindTargets > 0,
1296
+ lagComp3DApiSnapshotRestoreEquivalent: lagComp3DApi.rollbackEquivalent,
1297
+ lagComp3DApiProxyLifecycleCovered: lagComp3DApi.proxiesCreated > 0
1298
+ && lagComp3DApi.proxiesCreated === lagComp3DApi.proxiesDestroyed,
1299
+ lagComp3DApiHistoryPruningCovered: lagComp3DApi.prunedOldFrames,
1300
+ lagComp3DAuthoritativeShotTimeCovered: authoritativeLagComp3D.authorityHitReason === 'hit'
1301
+ && authoritativeLagComp3D.derivedRewindFrame === 1
1302
+ && authoritativeLagComp3D.rawForgedRewindFrame !== authoritativeLagComp3D.derivedRewindFrame,
1303
+ lagComp3DAuthoritativeForgedTimingRejected: authoritativeLagComp3D.forgedLatencyReason === 'invalid-shot'
1304
+ && authoritativeLagComp3D.forgedShotFrameReason === 'invalid-shot'
1305
+ && authoritativeLagComp3D.forgedHistoryReason === 'invalid-shot'
1306
+ && authoritativeLagComp3D.authorityTimingRejected === 3,
1307
+ lagComp3DAuthoritativeRollbackEquivalent: authoritativeLagComp3D.rollbackEquivalent,
1308
+ fullStepMeasuredTimingCovered: fullStep.frames === fullStep.fullStepFrames
1309
+ && fullStep.p95MeasuredStepMs > 0
1310
+ && fullStep.p99MeasuredStepMs > 0,
1311
+ fullStepRollbackEquivalent: fullStep.rollbackEquivalent,
1312
+ fullStepContactsAndQueriesCovered: fullStep.contactCount > 0 && fullStep.queryCount === fullStep.frames,
1313
+ executedRequestedFrames: metrics.frames === requestedFrames,
1314
+ ...(metrics.extrapolatedStress ? {} : {
1315
+ fullPhysicsRequiredProofCovered: metrics.fullPhysicsStepFrames === metrics.frames,
1316
+ }),
1317
+ optimizedPhysicsBenchmarkTailDeclared: benchmark && !noFastStress
1318
+ ? metrics.extrapolatedStress && metrics.optimizedStressFrames > 0
1319
+ : metrics.extrapolatedStress === false && metrics.optimizedStressFrames === 0,
1320
+ noFastStressHonored: noFastStress
1321
+ ? metrics.fullPhysicsStepFrames === metrics.frames && metrics.optimizedStressFrames === 0 && metrics.extrapolatedStress === false
1322
+ : true,
1323
+ optimizedStressFramesDeclared: metrics.optimizedStressFrames === Math.max(0, metrics.frames - metrics.fullPhysicsStepFrames),
1324
+ measuredP99WithinThreshold: enforceThresholds ? metrics.p99StepMs < 25 : true,
1325
+ physicsStressProofMode: metrics.extrapolatedStress ? 'optimized-tail' : 'full-step',
1326
+ }, withFrameMetrics({
1327
+ ...metrics,
1328
+ callbackMaskChecksum: callbackMasks.checksum,
1329
+ callbackMaskRestoredChecksum: callbackMasks.restoredChecksum,
1330
+ callbackMaskRollbackEquivalent: callbackMasks.rollbackEquivalent,
1331
+ queryOptionsRollbackStable: queryOptions.checksum.length > 0,
1332
+ callbackCollisionEnter: callbackMasks.collisionEnter,
1333
+ callbackCollisionStay: callbackMasks.collisionStay,
1334
+ callbackCollisionExit: callbackMasks.collisionExit,
1335
+ callbackTriggerEnter: callbackMasks.triggerEnter,
1336
+ callbackTriggerStay: callbackMasks.triggerStay,
1337
+ callbackTriggerExit: callbackMasks.triggerExit,
1338
+ callbackDisabledEvents: callbackMasks.disabledEvents,
1339
+ queryOptionsChecksum: queryOptions.checksum,
1340
+ queryOptionsDefaultHitAll: queryOptions.defaultHitAll,
1341
+ queryOptionsExplicitSolidsOnly: queryOptions.explicitSolidsOnly,
1342
+ queryOptionsKindFiltersStable: queryOptions.kindFiltersStable,
1343
+ queryOptionsTriggerSensorFiltersStable: queryOptions.triggerSensorFiltersStable,
1344
+ queryOptionsRaycastCovered: queryOptions.raycastCovered,
1345
+ queryOptionsOverlapCovered: queryOptions.overlapCovered,
1346
+ queryOptionsShapeCastCovered: queryOptions.shapeCastCovered,
1347
+ detailChecksum: details.checksum,
1348
+ detailRestoredChecksum: details.restoredChecksum,
1349
+ detailRollbackEquivalent: details.rollbackEquivalent,
1350
+ detailRayDetailedHits: details.rayDetailedHits,
1351
+ detailShapeCastDetailedHits: details.shapeCastDetailedHits,
1352
+ detailContactDetails: details.contactDetails,
1353
+ detailStableUnderBodyOrderPermutation: details.stableUnderBodyOrderPermutation,
1354
+ detailRayNormalX: details.rayNormalX,
1355
+ detailShapeCastPointX: details.shapeCastPointX,
1356
+ detailContactPenetration: details.contactPenetration,
1357
+ materialChecksum: materials.checksum,
1358
+ materialRestoredChecksum: materials.restoredChecksum,
1359
+ materialCombinedFriction: materials.combinedFriction,
1360
+ materialCombinedRestitution: materials.combinedRestitution,
1361
+ materialNormalVelocityAfterBounce: materials.normalVelocityAfterBounce,
1362
+ materialTangentVelocityAfterFrictionY: materials.tangentVelocityAfterFrictionY,
1363
+ materialTangentVelocityAfterFrictionZ: materials.tangentVelocityAfterFrictionZ,
1364
+ materialRollbackEquivalent: materials.rollbackEquivalent,
1365
+ stackChecksum: stack.checksum,
1366
+ stackRestoredChecksum: stack.restoredChecksum,
1367
+ stackReversedOrderChecksum: stack.reversedOrderChecksum,
1368
+ stackBodies: stack.bodies,
1369
+ stackFrames: stack.frames,
1370
+ stackLayers: stack.stackLayers,
1371
+ stackContactCount: stack.contactCount,
1372
+ stackSolverCorrections: stack.solverCorrections,
1373
+ stackMaxPenetration: stack.maxPenetration,
1374
+ stackMaxHeightDrift: stack.maxHeightDrift,
1375
+ stackSettledDynamicBodies: stack.settledDynamicBodies,
1376
+ stackP95StepCost: stack.p95StepCost,
1377
+ stackP99StepCost: stack.p99StepCost,
1378
+ stackRollbackEquivalent: stack.rollbackEquivalent,
1379
+ stackStableUnderAuthoringOrderPermutation: stack.stableUnderAuthoringOrderPermutation,
1380
+ lagComp3DChecksum: lagComp3D.checksum,
1381
+ lagComp3DRestoredChecksum: lagComp3D.restoredChecksum,
1382
+ lagComp3DPlayers: lagComp3D.players,
1383
+ lagComp3DFrames: lagComp3D.frames,
1384
+ lagComp3DShots: lagComp3D.shots,
1385
+ lagComp3DRifleShots: lagComp3D.rifleShots,
1386
+ lagComp3DShotgunShots: lagComp3D.shotgunShots,
1387
+ lagComp3DBurstShots: lagComp3D.burstShots,
1388
+ lagComp3DHits: lagComp3D.hits,
1389
+ lagComp3DMisses: lagComp3D.misses,
1390
+ lagComp3DOccluded: lagComp3D.occluded,
1391
+ lagComp3DStaleRejected: lagComp3D.staleRejected,
1392
+ lagComp3DFutureRejected: lagComp3D.futureRejected,
1393
+ lagComp3DMultiHitboxQueries: lagComp3D.multiHitboxQueries,
1394
+ lagComp3DAnimatedHitboxSamples: lagComp3D.animatedHitboxSamples,
1395
+ lagComp3DInterpolatedHitboxSamples: lagComp3D.interpolatedHitboxSamples,
1396
+ lagComp3DStaticOcclusionQueries: lagComp3D.staticOcclusionQueries,
1397
+ lagComp3DProxyBodiesCreated: lagComp3D.proxyBodiesCreated,
1398
+ lagComp3DProxyBodiesDestroyed: lagComp3D.proxyBodiesDestroyed,
1399
+ lagComp3DProxyLifecycleBalanced: lagComp3D.proxyLifecycleBalanced,
1400
+ lagComp3DPerClientShotTimesCovered: lagComp3D.perClientShotTimesCovered,
1401
+ lagComp3DRollbackEquivalent: lagComp3D.rollbackEquivalent,
1402
+ lagComp3DApiChecksum: lagComp3DApi.checksum,
1403
+ lagComp3DApiRestoredChecksum: lagComp3DApi.restoredChecksum,
1404
+ lagComp3DApiExactRewindTargets: lagComp3DApi.exactRewindTargets,
1405
+ lagComp3DApiInterpolatedRewindTargets: lagComp3DApi.interpolatedRewindTargets,
1406
+ lagComp3DApiHitReason: lagComp3DApi.hitReason,
1407
+ lagComp3DApiOccludedReason: lagComp3DApi.occludedReason,
1408
+ lagComp3DApiStaleReason: lagComp3DApi.staleReason,
1409
+ lagComp3DApiFutureReason: lagComp3DApi.futureReason,
1410
+ lagComp3DApiProxiesCreated: lagComp3DApi.proxiesCreated,
1411
+ lagComp3DApiProxiesDestroyed: lagComp3DApi.proxiesDestroyed,
1412
+ lagComp3DApiHistoryFrameCount: lagComp3DApi.historyFrameCount,
1413
+ lagComp3DApiPrunedOldFrames: lagComp3DApi.prunedOldFrames,
1414
+ lagComp3DAuthoritativeChecksum: authoritativeLagComp3D.checksum,
1415
+ lagComp3DAuthoritativeHitReason: authoritativeLagComp3D.authorityHitReason,
1416
+ lagComp3DAuthoritativeForgedLatencyReason: authoritativeLagComp3D.forgedLatencyReason,
1417
+ lagComp3DAuthoritativeForgedShotFrameReason: authoritativeLagComp3D.forgedShotFrameReason,
1418
+ lagComp3DAuthoritativeForgedHistoryReason: authoritativeLagComp3D.forgedHistoryReason,
1419
+ lagComp3DAuthoritativeDerivedRewindFrame: authoritativeLagComp3D.derivedRewindFrame,
1420
+ lagComp3DAuthoritativeRawForgedRewindFrame: authoritativeLagComp3D.rawForgedRewindFrame,
1421
+ lagComp3DAuthoritativeTimingRejected: authoritativeLagComp3D.authorityTimingRejected,
1422
+ lagComp3DAuthoritativeRollbackEquivalent: authoritativeLagComp3D.rollbackEquivalent,
1423
+ fullStepChecksum: fullStep.checksum,
1424
+ fullStepRestoredChecksum: fullStep.restoredChecksum,
1425
+ fullStepFrames: fullStep.frames,
1426
+ fullStepBodies: fullStep.bodies,
1427
+ fullStepContactCount: fullStep.contactCount,
1428
+ fullStepQueryCount: fullStep.queryCount,
1429
+ fullStepP95MeasuredStepMs: fullStep.p95MeasuredStepMs,
1430
+ fullStepP99MeasuredStepMs: fullStep.p99MeasuredStepMs,
1431
+ fullStepMaxMeasuredStepMs: fullStep.maxMeasuredStepMs,
1432
+ fullStepWallClockElapsedMs: fullStep.wallClockElapsedMs,
1433
+ fullStepRollbackEquivalent: fullStep.rollbackEquivalent,
1434
+ highBodySample512Checksum: highBodySample512?.checksum ?? '',
1435
+ highBodySample512Frames: highBodySample512?.frames ?? 0,
1436
+ highBodySample2048Checksum: highBodySample2048?.checksum ?? '',
1437
+ highBodySample2048Frames: highBodySample2048?.frames ?? 0,
1438
+ }, requestedFrames, metrics.frames));
1439
+ }
1440
+ function kccCategory(players, requestedFrames, executedFrames, benchmark, noFastStress) {
1441
+ const metrics = runDeterministicKcc3DCrowdFixture(players, executedFrames, { allowFastStress: !noFastStress });
1442
+ const fullStep = runKcc3DFullStepMeasurementFixture(Math.min(players, 128), Math.min(requestedFrames, 300));
1443
+ const adversarial = runDeterministicKcc3DAdversarialFixture();
1444
+ const filterForce = runDeterministicKcc3DFilterForceFixture();
1445
+ const callbackFilter = runDeterministicKcc3DCallbackFilterFixture();
1446
+ const geometry = runDeterministicKcc3DGeometryFixture();
1447
+ return categoryReport('kcc3d', defaultChecksum({
1448
+ stress: metrics.checksum,
1449
+ adversarial: adversarial.checksum,
1450
+ filterForce: filterForce.checksum,
1451
+ callbackFilter: callbackFilter.checksum,
1452
+ geometry: geometry.checksum,
1453
+ }), {
1454
+ rollbackEquivalent: metrics.rollbackEquivalent,
1455
+ playersCovered: metrics.players === players,
1456
+ executedRequestedFrames: metrics.frames === requestedFrames,
1457
+ physicsQueriesCovered: metrics.physicsQueryCount >= metrics.players * metrics.frames,
1458
+ collisionCorrectionsCovered: metrics.correctionCount > 0,
1459
+ physicsSlidesCovered: metrics.physicsSlides > 0,
1460
+ slopesCovered: metrics.slopeContacts > 0,
1461
+ stepsCovered: metrics.stepContacts > 0,
1462
+ movingPlatformsCovered: metrics.platformContacts > 0,
1463
+ sweptCollisionsCovered: metrics.sweptCollisions > 0,
1464
+ headClearanceCovered: metrics.headClearanceBlocks > 0,
1465
+ platformVelocityTransferCovered: metrics.platformVelocityTransfers > 0,
1466
+ fullStepMeasuredTimingCovered: fullStep.frames === fullStep.fullStepFrames
1467
+ && fullStep.p95MeasuredStepMs > 0
1468
+ && fullStep.p99MeasuredStepMs > 0,
1469
+ fullStepRollbackEquivalent: fullStep.rollbackEquivalent,
1470
+ fullStepMechanicsCovered: fullStep.correctionCount > 0
1471
+ && fullStep.physicsQueryCount >= fullStep.players * fullStep.frames,
1472
+ adversarialRollbackEquivalent: adversarial.rollbackEquivalent,
1473
+ adversarialThinWallSweepsCovered: adversarial.thinWallSweeps > 0,
1474
+ adversarialDiagonalThinWallSweepsCovered: adversarial.diagonalThinWallSweeps > 0,
1475
+ adversarialLedgeCorrectionsCovered: adversarial.ledgeCorrections > 0,
1476
+ adversarialCeilingBlocksCovered: adversarial.ceilingBlocks > 0,
1477
+ adversarialCornerSweepsCovered: adversarial.cornerSweeps > 0,
1478
+ adversarialMovingPlatformEdgeTransfersCovered: adversarial.movingPlatformEdgeTransfers > 0,
1479
+ adversarialSlopeLimitRejectionsCovered: adversarial.slopeLimitRejections > 0,
1480
+ ignoredColliderFilteringCovered: filterForce.ignoredColliderPassedThrough,
1481
+ solidColliderStillBlocksAfterFilter: filterForce.solidColliderBlocked,
1482
+ externalImpulseCovered: filterForce.externalImpulseApplied,
1483
+ externalImpulseOrderStable: filterForce.filterOrderStable,
1484
+ kccFilterForceRollbackEquivalent: filterForce.rollbackEquivalent,
1485
+ perHitCallbackFilteringCovered: callbackFilter.callbackIgnoredColliderPassedThrough
1486
+ && callbackFilter.callbackLayerFiltered
1487
+ && callbackFilter.callbackNormalFiltered
1488
+ && callbackFilter.callbackRejectedHits > 0,
1489
+ perHitCallbackSolidStillBlocks: callbackFilter.callbackSolidColliderBlocked
1490
+ && callbackFilter.callbackAcceptedHits > 0,
1491
+ perHitCallbackMetadataCovered: callbackFilter.callbackMetadataCovered
1492
+ && callbackFilter.callbackInvocations > 0,
1493
+ perHitCallbackOrderStable: callbackFilter.callbackOrderStable,
1494
+ perHitCallbackRejectsMutableState: callbackFilter.mutableFilterRejected,
1495
+ perHitCallbackRollbackEquivalent: callbackFilter.rollbackEquivalent,
1496
+ controllerGeometryConfigurable: geometry.defaultGeometryPreserved
1497
+ && geometry.geometryMetadataStable,
1498
+ controllerRadiusAffectsCollision: geometry.smallRadiusPassesNarrowGap
1499
+ && geometry.largeRadiusBlockedByNarrowGap,
1500
+ controllerHeightAffectsHeadClearance: geometry.tallBodyHeadBlocked
1501
+ && geometry.shortBodyClearsLowCeiling,
1502
+ controllerSkinWidthAffectsCollision: geometry.skinWidthExpandsCollisionShape,
1503
+ controllerGeometryRollbackEquivalent: geometry.rollbackEquivalent,
1504
+ ...(metrics.extrapolatedStress ? {} : {
1505
+ fullKccRequiredProofCovered: metrics.fullKccStepFrames === metrics.frames,
1506
+ }),
1507
+ optimizedKccBenchmarkTailDeclared: benchmark && !noFastStress
1508
+ ? metrics.extrapolatedStress && metrics.optimizedStressFrames > 0
1509
+ : metrics.extrapolatedStress === false && metrics.optimizedStressFrames === 0,
1510
+ noFastStressHonored: noFastStress
1511
+ ? metrics.fullKccStepFrames === metrics.frames && metrics.optimizedStressFrames === 0 && metrics.extrapolatedStress === false
1512
+ : true,
1513
+ optimizedStressFramesDeclared: metrics.optimizedStressFrames === Math.max(0, metrics.frames - metrics.fullKccStepFrames),
1514
+ kccStressProofMode: metrics.extrapolatedStress ? 'optimized-tail' : 'full-step',
1515
+ }, withFrameMetrics({
1516
+ ...metrics,
1517
+ fullStepChecksum: fullStep.checksum,
1518
+ fullStepRestoredChecksum: fullStep.restoredChecksum,
1519
+ fullStepPlayers: fullStep.players,
1520
+ fullStepFrames: fullStep.frames,
1521
+ fullStepCorrectionCount: fullStep.correctionCount,
1522
+ fullStepPhysicsQueryCount: fullStep.physicsQueryCount,
1523
+ fullStepP95MeasuredStepMs: fullStep.p95MeasuredStepMs,
1524
+ fullStepP99MeasuredStepMs: fullStep.p99MeasuredStepMs,
1525
+ fullStepMaxMeasuredStepMs: fullStep.maxMeasuredStepMs,
1526
+ fullStepWallClockElapsedMs: fullStep.wallClockElapsedMs,
1527
+ fullStepRollbackEquivalent: fullStep.rollbackEquivalent,
1528
+ adversarialChecksum: adversarial.checksum,
1529
+ adversarialRestoredChecksum: adversarial.restoredChecksum,
1530
+ adversarialRollbackEquivalent: adversarial.rollbackEquivalent,
1531
+ adversarialThinWallSweeps: adversarial.thinWallSweeps,
1532
+ adversarialDiagonalThinWallSweeps: adversarial.diagonalThinWallSweeps,
1533
+ adversarialLedgeCorrections: adversarial.ledgeCorrections,
1534
+ adversarialCeilingBlocks: adversarial.ceilingBlocks,
1535
+ adversarialCornerSweeps: adversarial.cornerSweeps,
1536
+ adversarialMovingPlatformEdgeTransfers: adversarial.movingPlatformEdgeTransfers,
1537
+ adversarialSlopeLimitRejections: adversarial.slopeLimitRejections,
1538
+ ignoredColliderCollisionEvents: filterForce.ignoredCollisionEvents,
1539
+ solidColliderCollisionEvents: filterForce.solidCollisionEvents,
1540
+ externalImpulseEvents: filterForce.externalImpulseEvents,
1541
+ kccFilterForceRollbackEquivalent: filterForce.rollbackEquivalent,
1542
+ kccCallbackFilterChecksum: callbackFilter.checksum,
1543
+ kccCallbackFilterRestoredChecksum: callbackFilter.restoredChecksum,
1544
+ kccCallbackFilterRollbackEquivalent: callbackFilter.rollbackEquivalent,
1545
+ kccCallbackFilterInvocations: callbackFilter.callbackInvocations,
1546
+ kccCallbackFilterRejectedHits: callbackFilter.callbackRejectedHits,
1547
+ kccCallbackFilterAcceptedHits: callbackFilter.callbackAcceptedHits,
1548
+ kccCallbackFilterIgnoredPassedThrough: callbackFilter.callbackIgnoredColliderPassedThrough,
1549
+ kccCallbackFilterSolidBlocked: callbackFilter.callbackSolidColliderBlocked,
1550
+ kccCallbackFilterLayerFiltered: callbackFilter.callbackLayerFiltered,
1551
+ kccCallbackFilterNormalFiltered: callbackFilter.callbackNormalFiltered,
1552
+ kccCallbackFilterOrderStable: callbackFilter.callbackOrderStable,
1553
+ kccCallbackFilterMetadataCovered: callbackFilter.callbackMetadataCovered,
1554
+ kccCallbackFilterMutableRejected: callbackFilter.mutableFilterRejected,
1555
+ kccGeometryChecksum: geometry.checksum,
1556
+ kccGeometryRestoredChecksum: geometry.restoredChecksum,
1557
+ kccGeometryRollbackEquivalent: geometry.rollbackEquivalent,
1558
+ kccGeometryDefaultPreserved: geometry.defaultGeometryPreserved,
1559
+ kccGeometrySmallRadiusPassesNarrowGap: geometry.smallRadiusPassesNarrowGap,
1560
+ kccGeometryLargeRadiusBlockedByNarrowGap: geometry.largeRadiusBlockedByNarrowGap,
1561
+ kccGeometryTallBodyHeadBlocked: geometry.tallBodyHeadBlocked,
1562
+ kccGeometryShortBodyClearsLowCeiling: geometry.shortBodyClearsLowCeiling,
1563
+ kccGeometrySkinWidthExpandsCollisionShape: geometry.skinWidthExpandsCollisionShape,
1564
+ kccGeometryMetadataStable: geometry.geometryMetadataStable,
1565
+ }, requestedFrames, metrics.frames));
1566
+ }
1567
+ function runPhysics3DFullStepMeasurementFixture(frames) {
1568
+ let world = createDeterministicPhysicsWorld3D(createFullStepPhysicsBodies3D(96));
1569
+ let snapshot = world;
1570
+ let contactCount = 0;
1571
+ let queryCount = 0;
1572
+ const timings = [];
1573
+ const startedAt = engineCompleteNowMs();
1574
+ for (let frame = 0; frame < frames; frame += 1) {
1575
+ if (frame === Math.floor(frames / 2)) {
1576
+ snapshot = world;
1577
+ }
1578
+ const stepStartedAt = engineCompleteNowMs();
1579
+ const result = stepDeterministicPhysicsWorld3D(world, {
1580
+ computeChecksum: false,
1581
+ scheduledQueries: [{
1582
+ id: `full-step-ray-${frame}`,
1583
+ phase: 'pre-physics',
1584
+ query: { x: -4, y: 0, z: 0, dx: 1, dy: 0, dz: 0, maxDistance: 32, layerMask: 0b1111 },
1585
+ }],
1586
+ });
1587
+ timings.push(engineCompleteNowMs() - stepStartedAt);
1588
+ world = result.world;
1589
+ contactCount += result.narrowphaseContacts;
1590
+ queryCount += result.scheduledQueryResults.length;
1591
+ }
1592
+ const checksum = defaultChecksum(world);
1593
+ let restored = snapshot;
1594
+ for (let frame = Math.floor(frames / 2); frame < frames; frame += 1) {
1595
+ restored = stepDeterministicPhysicsWorld3D(restored, {
1596
+ computeChecksum: false,
1597
+ scheduledQueries: [{
1598
+ id: `full-step-ray-${frame}`,
1599
+ phase: 'pre-physics',
1600
+ query: { x: -4, y: 0, z: 0, dx: 1, dy: 0, dz: 0, maxDistance: 32, layerMask: 0b1111 },
1601
+ }],
1602
+ }).world;
1603
+ }
1604
+ return {
1605
+ checksum,
1606
+ restoredChecksum: defaultChecksum(restored),
1607
+ rollbackEquivalent: checksum === defaultChecksum(restored),
1608
+ bodies: world.bodies.length,
1609
+ frames,
1610
+ fullStepFrames: frames,
1611
+ contactCount,
1612
+ queryCount,
1613
+ p95MeasuredStepMs: roundMetric(percentileNumber(timings, 95), 6),
1614
+ p99MeasuredStepMs: roundMetric(percentileNumber(timings, 99), 6),
1615
+ maxMeasuredStepMs: roundMetric(timings.reduce((max, value) => Math.max(max, value), 0), 6),
1616
+ wallClockElapsedMs: roundMetric(engineCompleteNowMs() - startedAt, 4),
1617
+ };
1618
+ }
1619
+ function runKcc3DFullStepMeasurementFixture(players, frames) {
1620
+ let bodies = Array.from({ length: players }, (_, slot) => ({
1621
+ x: slot % 16,
1622
+ y: slot % 17 === 0 ? 4 : 0,
1623
+ z: Math.floor(slot / 16),
1624
+ vx: 0,
1625
+ vy: slot % 17 === 0 ? -1 : 0,
1626
+ vz: 0,
1627
+ grounded: slot % 17 !== 0,
1628
+ coyoteFrames: slot % 17 === 0 ? 0 : 4,
1629
+ jumpBufferFrames: 0,
1630
+ }));
1631
+ const kccWorld = {
1632
+ minX: -8,
1633
+ maxX: 48,
1634
+ minY: 0,
1635
+ maxY: 32,
1636
+ minZ: -8,
1637
+ maxZ: 32,
1638
+ maxSlopeRisePerUnit: 1,
1639
+ stepHeight: 2,
1640
+ slopes: [{ id: 'full-slope', minX: -2, maxX: 4, minZ: -8, maxZ: 8, baseY: 0, risePerUnitX: 1 }],
1641
+ steps: [{ id: 'full-step', minX: 18, maxX: 22, minZ: -8, maxZ: 12, height: 2 }],
1642
+ movingPlatforms: [{ id: 'full-platform', minX: 30, maxX: 34, minZ: -4, maxZ: 12, y: 3, dxPerFrame: 1, dzPerFrame: 0 }],
1643
+ };
1644
+ const physics = createDeterministicPhysicsWorld3D(createFullStepKccObstacles3D());
1645
+ let snapshot = bodies;
1646
+ let correctionCount = 0;
1647
+ let physicsQueryCount = 0;
1648
+ const timings = [];
1649
+ const startedAt = engineCompleteNowMs();
1650
+ for (let frame = 0; frame < frames; frame += 1) {
1651
+ if (frame === Math.floor(frames / 2)) {
1652
+ snapshot = bodies.map((body) => ({ ...body }));
1653
+ }
1654
+ const stepStartedAt = engineCompleteNowMs();
1655
+ bodies = bodies.map((body, slot) => {
1656
+ const result = stepDeterministicKcc3D(body, {
1657
+ moveX: (slot + frame) % 3 - 1,
1658
+ moveZ: (slot * 2 + frame) % 3 - 1,
1659
+ jumpPressed: (slot + frame) % 101 === 0,
1660
+ }, kccWorld, { frame, physics });
1661
+ correctionCount += result.correctionCount;
1662
+ physicsQueryCount += result.physicsQueryCount;
1663
+ return result.body;
1664
+ });
1665
+ timings.push(engineCompleteNowMs() - stepStartedAt);
1666
+ }
1667
+ const checksum = defaultChecksum(bodies);
1668
+ let restored = snapshot;
1669
+ for (let frame = Math.floor(frames / 2); frame < frames; frame += 1) {
1670
+ restored = restored.map((body, slot) => stepDeterministicKcc3D(body, {
1671
+ moveX: (slot + frame) % 3 - 1,
1672
+ moveZ: (slot * 2 + frame) % 3 - 1,
1673
+ jumpPressed: (slot + frame) % 101 === 0,
1674
+ }, kccWorld, { frame, physics }).body);
1675
+ }
1676
+ return {
1677
+ checksum,
1678
+ restoredChecksum: defaultChecksum(restored),
1679
+ rollbackEquivalent: checksum === defaultChecksum(restored),
1680
+ players,
1681
+ frames,
1682
+ fullStepFrames: frames,
1683
+ correctionCount,
1684
+ physicsQueryCount,
1685
+ p95MeasuredStepMs: roundMetric(percentileNumber(timings, 95), 6),
1686
+ p99MeasuredStepMs: roundMetric(percentileNumber(timings, 99), 6),
1687
+ maxMeasuredStepMs: roundMetric(timings.reduce((max, value) => Math.max(max, value), 0), 6),
1688
+ wallClockElapsedMs: roundMetric(engineCompleteNowMs() - startedAt, 4),
1689
+ };
1690
+ }
1691
+ function createFullStepPhysicsBodies3D(count) {
1692
+ return Array.from({ length: count }, (_, index) => ({
1693
+ id: `full-body-${index}`,
1694
+ kind: index === 0 ? 'static' : 'dynamic',
1695
+ x: index === 0 ? 4 : (index % 12) - 6,
1696
+ y: Math.floor(index / 12) % 2,
1697
+ z: Math.floor(index / 24),
1698
+ vx: index === 0 ? 0 : (index % 3) - 1,
1699
+ vy: 0,
1700
+ vz: index === 0 ? 0 : ((index + 1) % 3) - 1,
1701
+ shape: index % 5 === 0 ? { type: 'sphere', radius: 1 } : { type: 'box', halfX: 1, halfY: 1, halfZ: 1 },
1702
+ layer: 1 << (index % 4),
1703
+ mask: 0b1111,
1704
+ material: { friction: index % 7, restitution: index % 3 },
1705
+ }));
1706
+ }
1707
+ function createFullStepKccObstacles3D() {
1708
+ return [3, 7, 11, 15, 19].flatMap((x, index) => [
1709
+ {
1710
+ id: `full-kcc-wall-${index}`,
1711
+ kind: 'static',
1712
+ x,
1713
+ y: 0,
1714
+ z: index,
1715
+ vx: 0,
1716
+ vy: 0,
1717
+ vz: 0,
1718
+ shape: { type: 'box', halfX: 0.75, halfY: 3, halfZ: 1 },
1719
+ layer: 1,
1720
+ mask: 1,
1721
+ },
1722
+ ]);
1723
+ }
1724
+ function navmeshCategory(players, requestedFrames, executedFrames) {
1725
+ const metrics = runDeterministicNavmeshStress(players, executedFrames);
1726
+ const imported = runDeterministicNavmeshImportTraversalFixture();
1727
+ const layered = runDeterministicNavmeshLayered3DFixture();
1728
+ const velocityObstacle = runDeterministicNavmeshVelocityObstacleFixture();
1729
+ return categoryReport('navmesh', defaultChecksum({ stress: metrics.checksum, imported: imported.checksum, layered: layered.checksum, velocityObstacle: velocityObstacle.checksum }), {
1730
+ rollbackEquivalent: metrics.rollbackEquivalent,
1731
+ agentsCovered: metrics.agents === players,
1732
+ executedRequestedFrames: metrics.frames === requestedFrames,
1733
+ replansObserved: metrics.replans > 0,
1734
+ waypointProgressionObserved: metrics.waypointAdvances > 0,
1735
+ avoidanceCovered: metrics.avoidanceCorrections > 0,
1736
+ radiusAvoidanceCovered: metrics.radiusAvoidanceCorrections > 0,
1737
+ velocityObstacleAvoidanceCovered: metrics.velocityObstacleAvoidanceCorrections > 0
1738
+ && metrics.avoidanceSpeedReductions > 0
1739
+ && velocityObstacle.movingObstacleSpeedReduction,
1740
+ agentVelocityObstacleAvoidanceCovered: metrics.agentVelocityObstacleAvoidanceCorrections > 0
1741
+ && metrics.agentAvoidancePriorityYields > 0
1742
+ && metrics.agentAvoidanceSpeedReductions > 0,
1743
+ velocityObstaclePredictionCovered: velocityObstacle.predictedFuturePositionAvoided,
1744
+ velocityObstacleOrderStable: velocityObstacle.obstacleOrderStable,
1745
+ velocityObstacleValidationRejected: velocityObstacle.obstacleValidationRejected,
1746
+ velocityObstacleRollbackEquivalent: velocityObstacle.rollbackEquivalent,
1747
+ pointPathingCovered: metrics.pointPathFound,
1748
+ nearestPolygonLookupCovered: metrics.pointLookupStable,
1749
+ importedCookOrderStable: imported.importOrderStable,
1750
+ importedPortalAndSpawnCovered: imported.portalCount > 0 && imported.spawnMarkerCount > 0,
1751
+ importedValidationFailuresCovered: imported.missingRegionRejected && imported.missingLinkRejected && imported.missingSpawnRejected,
1752
+ offMeshTraversalLifecycleCovered: imported.offMeshEnterEvents > 0
1753
+ && imported.offMeshTraverseEvents > 0
1754
+ && imported.offMeshExitEvents > 0
1755
+ && imported.offMeshCompleteEvents > 0,
1756
+ offMeshTraversalRollbackEquivalent: imported.rollbackEquivalent,
1757
+ importedPointPathCovered: imported.pointPathFound && imported.pointPathHash.length > 0,
1758
+ layered3DStackedOverlapCovered: layered.stackedOverlapAccepted,
1759
+ layered3DSameLayerOverlapRejected: layered.sameLayerOverlapRejected,
1760
+ layered3DLookupDisambiguated: layered.layerDisambiguationStable,
1761
+ layered3DOffMeshTraversalCovered: layered.verticalOffMeshTraversals > 0,
1762
+ layered3DRollbackEquivalent: layered.rollbackEquivalent,
1763
+ }, withFrameMetrics({
1764
+ ...metrics,
1765
+ importedNavmeshHash: imported.importedHash,
1766
+ importedNavmeshPermutedHash: imported.permutedHash,
1767
+ importedNavmeshImportOrderStable: imported.importOrderStable,
1768
+ importedNavmeshPortalCount: imported.portalCount,
1769
+ importedNavmeshSpawnMarkerCount: imported.spawnMarkerCount,
1770
+ importedNavmeshMissingRegionRejected: imported.missingRegionRejected,
1771
+ importedNavmeshMissingLinkRejected: imported.missingLinkRejected,
1772
+ importedNavmeshMissingSpawnRejected: imported.missingSpawnRejected,
1773
+ importedNavmeshOffMeshEnterEvents: imported.offMeshEnterEvents,
1774
+ importedNavmeshOffMeshTraverseEvents: imported.offMeshTraverseEvents,
1775
+ importedNavmeshOffMeshExitEvents: imported.offMeshExitEvents,
1776
+ importedNavmeshOffMeshCompleteEvents: imported.offMeshCompleteEvents,
1777
+ importedNavmeshOffMeshCommandIdStable: imported.offMeshCommandIdStable,
1778
+ importedNavmeshOffMeshTraversalFrames: imported.offMeshTraversalFrames,
1779
+ importedNavmeshPointPathFound: imported.pointPathFound,
1780
+ importedNavmeshPointPathHash: imported.pointPathHash,
1781
+ importedNavmeshRollbackEquivalent: imported.rollbackEquivalent,
1782
+ layeredNavmeshHash: layered.cookedHash,
1783
+ layeredNavmeshPermutedHash: layered.permutedHash,
1784
+ layeredNavmeshCookOrderStable: layered.cookOrderStable,
1785
+ layeredNavmeshLowerLookupId: layered.lowerLookupId,
1786
+ layeredNavmeshUpperLookupId: layered.upperLookupId,
1787
+ layeredNavmeshLowerPathHash: layered.lowerPathHash,
1788
+ layeredNavmeshUpperPathHash: layered.upperPathHash,
1789
+ layeredNavmeshVerticalOffMeshTraversals: layered.verticalOffMeshTraversals,
1790
+ layeredNavmeshUpperAgentZ: layered.upperAgentZ,
1791
+ layeredNavmeshRollbackEquivalent: layered.rollbackEquivalent,
1792
+ velocityObstacleChecksum: velocityObstacle.checksum,
1793
+ velocityObstacleRestoredChecksum: velocityObstacle.restoredChecksum,
1794
+ velocityObstacleRollbackEquivalent: velocityObstacle.rollbackEquivalent,
1795
+ velocityObstacleSpeedReduction: velocityObstacle.movingObstacleSpeedReduction,
1796
+ velocityObstacleFuturePrediction: velocityObstacle.predictedFuturePositionAvoided,
1797
+ velocityObstacleAuthoredSpeedRestored: velocityObstacle.authoredSpeedRestored,
1798
+ velocityObstacleOrderStable: velocityObstacle.obstacleOrderStable,
1799
+ velocityObstacleValidationRejected: velocityObstacle.obstacleValidationRejected,
1800
+ velocityObstacleFixtureCorrections: velocityObstacle.velocityObstacleAvoidanceCorrections,
1801
+ velocityObstacleFixtureSpeedReductions: velocityObstacle.avoidanceSpeedReductions,
1802
+ }, requestedFrames, metrics.frames));
1803
+ }
1804
+ function botCategory(players, requestedFrames, executedFrames) {
1805
+ const metrics = runDeterministicBotDocumentFixture(players, executedFrames);
1806
+ return categoryReport('bot-documents', metrics.checksum, {
1807
+ compiledAssetExists: metrics.compiledAssetHash.length > 0,
1808
+ botReplacementSlotsPresent: metrics.botReplacementSlots >= Math.floor(players / 2),
1809
+ debugTracePresent: metrics.debugTraceCount > 0,
1810
+ executedRequestedFrames: metrics.frames === requestedFrames,
1811
+ behaviorDecoratorsCovered: metrics.behaviorDecoratorTicks > 0,
1812
+ behaviorRunningCovered: metrics.behaviorRunningTicks > 0,
1813
+ behaviorResumeCovered: metrics.behaviorResumeTicks > 0,
1814
+ behaviorServicesCovered: metrics.behaviorServiceTicks > 0,
1815
+ utilityCurvesCovered: metrics.utilityCurveSelections > 0,
1816
+ utilityNestedCovered: metrics.utilityNestedSelections > 0,
1817
+ utilityMomentumCovered: metrics.utilityMomentumSelections > 0,
1818
+ utilityCooldownCovered: metrics.utilityCooldownSuppressions > 0,
1819
+ }, withFrameMetrics(metrics, requestedFrames, metrics.frames));
1820
+ }
1821
+ function animationCategory(requestedFrames, executedFrames) {
1822
+ const metrics = runDeterministicAnimationFixture(executedFrames);
1823
+ return categoryReport('animation', metrics.checksum, {
1824
+ rollbackEquivalent: metrics.rollbackEquivalent,
1825
+ animationEventsEmitted: metrics.emittedEvents > 0,
1826
+ cookedAssetExists: metrics.cookedAssetHash.length > 0,
1827
+ executedRequestedFrames: executedFrames === requestedFrames,
1828
+ exitFramesCovered: metrics.exitFrameTransitions > 0,
1829
+ transitionBlendCovered: metrics.transitionBlendFrames > 0,
1830
+ triggerLatchCovered: metrics.triggerLatchCount > 0,
1831
+ normalizedClipTimeCovered: metrics.normalizedTimeHash.length > 0,
1832
+ sampledPoseCovered: metrics.poseHash.length > 0,
1833
+ blendWeightCovered: metrics.blendWeightHash.length > 0,
1834
+ blendTreeCovered: metrics.blendTreeFrames > 0,
1835
+ animationLayersCovered: metrics.layerPoseFrames > 0,
1836
+ rootMotionCovered: metrics.rootMotionFrames > 0,
1837
+ eventReconstructionCovered: metrics.reconstructedEvents > 0 && metrics.reconstructedEventHash.length > 0,
1838
+ }, withFrameMetrics({ ...metrics, frames: executedFrames }, requestedFrames, executedFrames));
1839
+ }
1840
+ function configCategory() {
1841
+ const metrics = runDeterministicConfigBundleFixture();
1842
+ return categoryReport('config-bundle', metrics.configHash, {
1843
+ verifies: metrics.result === 'pass',
1844
+ prototypesMaterialize: metrics.materializedEntityCount > 0,
1845
+ migrationsCovered: metrics.migrationResult === 'migrated',
1846
+ }, withFrameMetrics({
1847
+ assetDbHash: metrics.assetDbHash,
1848
+ prototypeCount: metrics.prototypeCount,
1849
+ materializedEntityCount: metrics.materializedEntityCount,
1850
+ migrationResult: metrics.migrationResult,
1851
+ }, 1, 1));
1852
+ }
1853
+ function authorityCategory(players, requestedFrames, executedFrames, seed) {
1854
+ const metrics = runDeterministicLocalAuthorityFixture(players, executedFrames, seed);
1855
+ const wire = runDeterministicLocalAuthorityWireFixture(players, Math.min(executedFrames, 512), seed + 31);
1856
+ const multiclient = runDeterministicMulticlientAuthorityFixture(players, Math.min(executedFrames, 5_000), seed + 47);
1857
+ const policy = runDeterministicLocalAuthorityRuntimePolicyFixture();
1858
+ const offlineUpgrade = runDeterministicOfflineToAuthorityUpgradeFixture(players, Math.min(executedFrames, 5_000), seed + 59);
1859
+ const replayMinimizer = runDeterministicLocalAuthorityReplayMinimizerFixture(players, Math.min(executedFrames, 1_000), seed + 67);
1860
+ return categoryReport('local-authority', defaultChecksum({
1861
+ authority: metrics.authorityChecksum,
1862
+ wire: wire.checksum,
1863
+ multiclient: multiclient.checksum,
1864
+ policy: policy.checksum,
1865
+ offlineUpgrade: offlineUpgrade.checksum,
1866
+ replayMinimizer: replayMinimizer.checksum,
1867
+ }), {
1868
+ convergence: metrics.convergence,
1869
+ projectedChecksumsMatch: metrics.authorityProjectionChecksum === metrics.clientProjectionChecksum,
1870
+ reconnectsCovered: metrics.reconnects > 0,
1871
+ lateJoinReconstructionCovered: metrics.lateJoins > 0 && metrics.lateJoinReconstructions === metrics.lateJoins && metrics.absentBeforeJoinInputs > 0,
1872
+ spectatorReconstructionCovered: metrics.spectators > 0 && metrics.spectatorReconstructions === metrics.spectators,
1873
+ hostileMessagesRejected: metrics.rejectedMessages > 0,
1874
+ migrationCovered: metrics.migrationFrame > 0,
1875
+ migrationPersistenceCovered: metrics.migrationPersistedSections >= 5 && metrics.migrationQueueRestored && metrics.migrationCommandTimelineRestored && metrics.slotReservationsRestored,
1876
+ typedWireProtocolCovered: wire.encodedEnvelopes > 0 && wire.decodedEnvelopes === wire.encodedEnvelopes,
1877
+ wireAckCorrectionSnapshotCovered: wire.ackEnvelopes > 0 && wire.correctionEnvelopes > 0 && wire.snapshotEnvelopes > 0 && wire.correctionsApplied > 0 && wire.snapshotsApplied > 0,
1878
+ wireCommandBundleCovered: wire.commandEnvelopes > 0 && wire.acceptedCommands > 0,
1879
+ wireCommandBundleRejectedHostile: wire.rejectedCommands > 0,
1880
+ wireSnapshotNegotiationCovered: wire.snapshotRequestEnvelopes > 0
1881
+ && wire.snapshotOfferEnvelopes > 0
1882
+ && wire.snapshotChunkEnvelopes > 0
1883
+ && wire.snapshotCompleteEnvelopes > 0
1884
+ && wire.snapshotNegotiationCompletesApplied > 0,
1885
+ wireSnapshotNegotiationRejectedHostile: wire.snapshotDescriptorMismatchRejected > 0
1886
+ && wire.snapshotChunkChecksumRejected > 0
1887
+ && wire.snapshotMissingChunkRejected > 0
1888
+ && wire.staleSnapshotOfferRejected > 0,
1889
+ wireHostileRejectionCovered: wire.malformedRejected >= 3 && wire.forgedSlotRejected > 0 && wire.staleFrameRejected > 0 && wire.futureFrameRejected > 0 && wire.oversizedRejected > 0 && wire.invalidInputIdRejected > 0,
1890
+ wireDuplicateReplayRejected: wire.duplicateInputRejected > 0,
1891
+ wireSequenceStatefulRejectionCovered: wire.reorderedSequenceAccepted > 0 && wire.duplicateSequenceRejected > 0 && wire.replayedEnvelopeRejected > 0,
1892
+ wireStaleControlRejectionCovered: wire.staleAckRejected > 0 && wire.staleCorrectionRejected > 0 && wire.staleSnapshotRejected > 0,
1893
+ wireSenderAuthCovered: wire.wireAuthSignedEnvelopes > 0,
1894
+ wireSenderAuthRejectedHostile: wire.wireAuthMissingRejected > 0
1895
+ && wire.wireAuthSenderMismatchRejected > 0
1896
+ && wire.wireAuthSignatureRejected > 0,
1897
+ wireSenderAuthHmacSha256Covered: wire.wireAuthHmacSha256Covered,
1898
+ wireAuthorityAuthRejectedHostile: wire.forgedAuthorityCorrectionRejected > 0 && wire.forgedAuthoritySnapshotRejected > 0,
1899
+ wireSnapshotIdentityRequired: wire.snapshotIdentityRequiredRejected > 0,
1900
+ multiclientPredictedRunnersCovered: multiclient.predictedClientRunners === players,
1901
+ multiclientAuthoritySnapshotsCovered: multiclient.authoritySnapshotsDelivered >= players * multiclient.frames,
1902
+ multiclientCorrectionsCovered: multiclient.correctionsApplied > 0 && multiclient.predictionMismatches > 0,
1903
+ multiclientRollbackResimulationCovered: multiclient.resimulatedFrames > 0 && multiclient.rollbackDepthP95 > 0,
1904
+ multiclientConvergenceCovered: multiclient.convergence && multiclient.allClientsReachedFinalAuthorityFrame,
1905
+ multiclientRuntimeBackedClientsCovered: multiclient.runtimeBackedClients === players,
1906
+ multiclientRuntimeBackedReplayVerified: multiclient.runtimeBackedReplayVerifiedClients === players && multiclient.runtimeBackedConvergence,
1907
+ multiclientRuntimeBackedCorrectionsCovered: multiclient.runtimeBackedCorrectionsApplied > 0 && multiclient.runtimeBackedPredictionMismatches > 0,
1908
+ multiclientRuntimeBackedResimulationCovered: multiclient.runtimeBackedResimulatedFrames > 0,
1909
+ rollbackWindowLateInputAccepted: policy.lateWithinRollbackWindowAccepted,
1910
+ rollbackWindowExpiredInputRejected: policy.expiredLateInputRejected,
1911
+ strictStaleInputRejected: policy.strictStaleInputRejected,
1912
+ structuredAuthorityInputDataPreserved: policy.structuredInputDataPreserved,
1913
+ malformedStructuredAuthorityInputRejected: policy.malformedStructuredInputRejected,
1914
+ authorityPolicyRollbackEquivalent: policy.rollbackEquivalent,
1915
+ nonCommutativeLateInputResimulated: policy.nonCommutativeLateInputResimulated,
1916
+ nonCommutativeAppendOnlyDiverged: policy.nonCommutativeAppendOnlyDiverged,
1917
+ offlineSessionUpgradeStateMatch: offlineUpgrade.stateMatch,
1918
+ offlineSessionUpgradeReplayVerified: offlineUpgrade.replayVerified,
1919
+ offlineSessionUpgradeRestoreVerified: offlineUpgrade.restoreVerified,
1920
+ offlineSessionUpgradeAuthorityConverged: offlineUpgrade.authorityConvergence,
1921
+ offlineSessionUpgradeInputTranscriptCovered: offlineUpgrade.inputRows === offlineUpgrade.players * offlineUpgrade.frames,
1922
+ authorityReplayBundleVerified: replayMinimizer.validReplayVerified,
1923
+ authorityReplayDesyncDetected: replayMinimizer.corruptedReplayFailed && replayMinimizer.mismatchFrame > 0,
1924
+ authorityReplayMinimizedArtifactVerified: replayMinimizer.minimizedReplayFailed
1925
+ && replayMinimizer.minimizedMismatchFrame === replayMinimizer.mismatchFrame,
1926
+ authorityReplayMinimizedArtifactReduced: replayMinimizer.minimizedReplayBytes < replayMinimizer.originalReplayBytes,
1927
+ authorityReplayMinimizedArtifactPruned: replayMinimizer.removedInputFrames > 0
1928
+ && replayMinimizer.removedSnapshots > 0
1929
+ && replayMinimizer.removedChecksumFrames > 0,
1930
+ }, withFrameMetrics({
1931
+ authorityChecksum: metrics.authorityChecksum,
1932
+ clientChecksum: metrics.clientChecksum,
1933
+ authorityProjectionChecksum: metrics.authorityProjectionChecksum,
1934
+ clientProjectionChecksum: metrics.clientProjectionChecksum,
1935
+ convergence: metrics.convergence,
1936
+ players: metrics.players,
1937
+ frames: metrics.frames,
1938
+ reconnects: metrics.reconnects,
1939
+ lateJoins: metrics.lateJoins,
1940
+ spectators: metrics.spectators,
1941
+ absentBeforeJoinInputs: metrics.absentBeforeJoinInputs,
1942
+ lateJoinReconstructions: metrics.lateJoinReconstructions,
1943
+ spectatorReconstructions: metrics.spectatorReconstructions,
1944
+ rejectedMessages: metrics.rejectedMessages,
1945
+ migrationFrame: metrics.migrationFrame,
1946
+ migrationPersistedSections: metrics.migrationPersistedSections,
1947
+ migrationQueueRestored: metrics.migrationQueueRestored,
1948
+ migrationCommandTimelineRestored: metrics.migrationCommandTimelineRestored,
1949
+ slotReservationsRestored: metrics.slotReservationsRestored,
1950
+ inputDelayP95: metrics.inputDelayP95,
1951
+ rollbackDepthP95: metrics.rollbackDepthP95,
1952
+ clockDriftP95: metrics.clockDriftP95,
1953
+ p95FrameCost: metrics.p95FrameCost,
1954
+ p99FrameCost: metrics.p99FrameCost,
1955
+ wireChecksum: wire.checksum,
1956
+ wireFrames: wire.frames,
1957
+ wireEncodedEnvelopes: wire.encodedEnvelopes,
1958
+ wireDecodedEnvelopes: wire.decodedEnvelopes,
1959
+ wireAcceptedInputs: wire.acceptedInputs,
1960
+ wireRejectedInputs: wire.rejectedInputs,
1961
+ wireCommandEnvelopes: wire.commandEnvelopes,
1962
+ wireAcceptedCommands: wire.acceptedCommands,
1963
+ wireRejectedCommands: wire.rejectedCommands,
1964
+ wireSnapshotRequestEnvelopes: wire.snapshotRequestEnvelopes,
1965
+ wireSnapshotOfferEnvelopes: wire.snapshotOfferEnvelopes,
1966
+ wireSnapshotChunkEnvelopes: wire.snapshotChunkEnvelopes,
1967
+ wireSnapshotCompleteEnvelopes: wire.snapshotCompleteEnvelopes,
1968
+ wireSnapshotNegotiationCompletesApplied: wire.snapshotNegotiationCompletesApplied,
1969
+ wireSnapshotNegotiationRejected: wire.snapshotNegotiationRejected,
1970
+ wireSnapshotDescriptorMismatchRejected: wire.snapshotDescriptorMismatchRejected,
1971
+ wireSnapshotChunkChecksumRejected: wire.snapshotChunkChecksumRejected,
1972
+ wireSnapshotMissingChunkRejected: wire.snapshotMissingChunkRejected,
1973
+ wireStaleSnapshotOfferRejected: wire.staleSnapshotOfferRejected,
1974
+ wireMalformedRejected: wire.malformedRejected,
1975
+ wireForgedSlotRejected: wire.forgedSlotRejected,
1976
+ wireStaleFrameRejected: wire.staleFrameRejected,
1977
+ wireFutureFrameRejected: wire.futureFrameRejected,
1978
+ wireOversizedRejected: wire.oversizedRejected,
1979
+ wireDuplicateInputRejected: wire.duplicateInputRejected,
1980
+ wireInvalidInputIdRejected: wire.invalidInputIdRejected,
1981
+ wireAckEnvelopes: wire.ackEnvelopes,
1982
+ wireCorrectionEnvelopes: wire.correctionEnvelopes,
1983
+ wireSnapshotEnvelopes: wire.snapshotEnvelopes,
1984
+ wireReorderedSequenceAccepted: wire.reorderedSequenceAccepted,
1985
+ wireDuplicateSequenceRejected: wire.duplicateSequenceRejected,
1986
+ wireReplayedEnvelopeRejected: wire.replayedEnvelopeRejected,
1987
+ wireStaleAckRejected: wire.staleAckRejected,
1988
+ wireStaleCorrectionRejected: wire.staleCorrectionRejected,
1989
+ wireStaleSnapshotRejected: wire.staleSnapshotRejected,
1990
+ wireAuthSignedEnvelopes: wire.wireAuthSignedEnvelopes,
1991
+ wireAuthMissingRejected: wire.wireAuthMissingRejected,
1992
+ wireAuthSenderMismatchRejected: wire.wireAuthSenderMismatchRejected,
1993
+ wireAuthSignatureRejected: wire.wireAuthSignatureRejected,
1994
+ wireAuthHmacSha256Covered: wire.wireAuthHmacSha256Covered,
1995
+ wireForgedAuthorityCorrectionRejected: wire.forgedAuthorityCorrectionRejected,
1996
+ wireForgedAuthoritySnapshotRejected: wire.forgedAuthoritySnapshotRejected,
1997
+ wireSnapshotIdentityRequiredRejected: wire.snapshotIdentityRequiredRejected,
1998
+ wireCorrectionsApplied: wire.correctionsApplied,
1999
+ wireSnapshotsApplied: wire.snapshotsApplied,
2000
+ multiclientChecksum: multiclient.checksum,
2001
+ multiclientFrames: multiclient.frames,
2002
+ multiclientPredictedClientRunners: multiclient.predictedClientRunners,
2003
+ multiclientAuthoritySnapshotsDelivered: multiclient.authoritySnapshotsDelivered,
2004
+ multiclientPredictionMismatches: multiclient.predictionMismatches,
2005
+ multiclientCorrectionsApplied: multiclient.correctionsApplied,
2006
+ multiclientResimulatedFrames: multiclient.resimulatedFrames,
2007
+ multiclientConvergedSnapshots: multiclient.convergedSnapshots,
2008
+ multiclientRollbackDepthP95: multiclient.rollbackDepthP95,
2009
+ multiclientRollbackDepthMax: multiclient.rollbackDepthMax,
2010
+ multiclientConvergence: multiclient.convergence,
2011
+ multiclientAllClientsReachedFinalAuthorityFrame: multiclient.allClientsReachedFinalAuthorityFrame,
2012
+ multiclientP95FrameCost: multiclient.p95FrameCost,
2013
+ multiclientP99FrameCost: multiclient.p99FrameCost,
2014
+ multiclientRuntimeBackedClients: multiclient.runtimeBackedClients,
2015
+ multiclientRuntimeBackedFrames: multiclient.runtimeBackedFrames,
2016
+ multiclientRuntimeBackedAuthoritySnapshots: multiclient.runtimeBackedAuthoritySnapshots,
2017
+ multiclientRuntimeBackedPredictionMismatches: multiclient.runtimeBackedPredictionMismatches,
2018
+ multiclientRuntimeBackedCorrectionsApplied: multiclient.runtimeBackedCorrectionsApplied,
2019
+ multiclientRuntimeBackedResimulatedFrames: multiclient.runtimeBackedResimulatedFrames,
2020
+ multiclientRuntimeBackedReplayVerifiedClients: multiclient.runtimeBackedReplayVerifiedClients,
2021
+ multiclientRuntimeBackedAuthorityChecksum: multiclient.runtimeBackedAuthorityChecksum,
2022
+ multiclientRuntimeBackedClientChecksum: multiclient.runtimeBackedClientChecksum,
2023
+ multiclientRuntimeBackedConvergence: multiclient.runtimeBackedConvergence,
2024
+ authorityPolicyChecksum: policy.checksum,
2025
+ authorityPolicyRestoredChecksum: policy.restoredChecksum,
2026
+ authorityPolicyRollbackEquivalent: policy.rollbackEquivalent,
2027
+ authorityPolicyRollbackWindowFrames: policy.rollbackWindowFrames,
2028
+ authorityPolicyStructuredInputChecksum: policy.structuredInputChecksum,
2029
+ authorityPolicyValueAfterLateInput: policy.authorityValueAfterLateInput,
2030
+ authorityPolicyClientProjectionValueAfterLateInput: policy.clientProjectionValueAfterLateInput,
2031
+ nonCommutativeRuntimeChecksum: policy.nonCommutativeRuntimeChecksum,
2032
+ nonCommutativeFullReplayChecksum: policy.nonCommutativeFullReplayChecksum,
2033
+ nonCommutativeAppendOnlyChecksum: policy.nonCommutativeAppendOnlyChecksum,
2034
+ nonCommutativeResimulatedFrames: policy.nonCommutativeResimulatedFrames,
2035
+ offlineUpgradeChecksum: offlineUpgrade.checksum,
2036
+ offlineUpgradePlayers: offlineUpgrade.players,
2037
+ offlineUpgradeFrames: offlineUpgrade.frames,
2038
+ offlineUpgradeOfflineFrame: offlineUpgrade.offlineFrame,
2039
+ offlineUpgradeAuthorityFrame: offlineUpgrade.authorityFrame,
2040
+ offlineUpgradeOfflineChecksum: offlineUpgrade.offlineChecksum,
2041
+ offlineUpgradeAuthorityChecksum: offlineUpgrade.authorityChecksum,
2042
+ offlineUpgradeAuthorityRestoredChecksum: offlineUpgrade.authorityRestoredChecksum,
2043
+ offlineUpgradeReplayChecksum: offlineUpgrade.replayChecksum,
2044
+ offlineUpgradeStateMatch: offlineUpgrade.stateMatch,
2045
+ offlineUpgradeReplayVerified: offlineUpgrade.replayVerified,
2046
+ offlineUpgradeRestoreVerified: offlineUpgrade.restoreVerified,
2047
+ offlineUpgradeAuthorityConvergence: offlineUpgrade.authorityConvergence,
2048
+ offlineUpgradeInputRows: offlineUpgrade.inputRows,
2049
+ offlineUpgradeSnapshotFrame: offlineUpgrade.snapshotFrame,
2050
+ authorityReplayMinimizerChecksum: replayMinimizer.checksum,
2051
+ authorityReplayMinimizerPlayers: replayMinimizer.players,
2052
+ authorityReplayMinimizerFrames: replayMinimizer.frames,
2053
+ authorityReplayValidVerified: replayMinimizer.validReplayVerified,
2054
+ authorityReplayCorruptedFailed: replayMinimizer.corruptedReplayFailed,
2055
+ authorityReplayMinimizedFailed: replayMinimizer.minimizedReplayFailed,
2056
+ authorityReplayMismatchFrame: replayMinimizer.mismatchFrame,
2057
+ authorityReplayMinimizedMismatchFrame: replayMinimizer.minimizedMismatchFrame,
2058
+ authorityReplayOriginalBytes: replayMinimizer.originalReplayBytes,
2059
+ authorityReplayMinimizedBytes: replayMinimizer.minimizedReplayBytes,
2060
+ authorityReplayRemovedInputFrames: replayMinimizer.removedInputFrames,
2061
+ authorityReplayRemovedSnapshots: replayMinimizer.removedSnapshots,
2062
+ authorityReplayRemovedChecksumFrames: replayMinimizer.removedChecksumFrames,
2063
+ authorityReplayDebugEvents: replayMinimizer.debugEvents,
2064
+ }, requestedFrames, metrics.frames));
2065
+ }
2066
+ function deterministicSessionCategory(players, requestedFrames, executedFrames, seed) {
2067
+ const metrics = runDeterministicSessionCoreFixture(players, executedFrames, seed);
2068
+ const structured = runDeterministicStructuredSessionInputFixture();
2069
+ const authority = runDeterministicSessionAuthorityFixture(players, Math.min(executedFrames, 5_000), seed + 11);
2070
+ const rollbackHistory = runDeterministicRollbackHistoryFixture(Math.min(executedFrames, 5_000), 64, seed);
2071
+ const lifecycleProtocol = runDeterministicSessionLifecycleProtocolFixture();
2072
+ return categoryReport('deterministic-session', defaultChecksum({
2073
+ core: metrics.checksum,
2074
+ structured: structured.checksum,
2075
+ authority: authority.checksum,
2076
+ rollbackHistory: rollbackHistory.checksum,
2077
+ lifecycleProtocol: lifecycleProtocol.checksum,
2078
+ }), {
2079
+ verifiedPredictedFrameRingCovered: metrics.frameRingExposed && metrics.predictedPreviousAvailable,
2080
+ partialConfirmationDoesNotAdvanceVerified: metrics.partialConfirmationHeldVerifiedFrame,
2081
+ inputFlagsCovered: metrics.repeatableInputs > 0
2082
+ && metrics.playerNotPresentInputs > 0
2083
+ && metrics.serverReplacedInputs > 0
2084
+ && metrics.commandFlagInputs > 0,
2085
+ playerLifecycleCovered: metrics.addPlayerAccepted > 0
2086
+ && metrics.removePlayerAccepted > 0
2087
+ && metrics.duplicateAddRejected > 0
2088
+ && metrics.removedSlotInputRejected > 0,
2089
+ correctionSemanticsCovered: metrics.correctionCount > 0 && metrics.staleCorrectionsRejected > 0,
2090
+ replacementInputsCovered: metrics.replacementInputs > 0,
2091
+ fullRuntimeReplayVerified: metrics.fullRuntimeReplayExecuted && metrics.replayVerified,
2092
+ firstClassCommandsCovered: metrics.predictedCommands > 0
2093
+ && metrics.confirmedCommands > 0
2094
+ && metrics.commandSystemPolls > 0,
2095
+ commandReplayVerified: metrics.commandReplayVerified,
2096
+ structuredInputReplayVerified: structured.replayVerified,
2097
+ structuredInputPredictionPreserved: structured.predictedFallbackPreservedData,
2098
+ structuredInputPayloadRejected: structured.rejectedNondeterministicPayload,
2099
+ structuredInputSchemaRejected: structured.rejectedSchemaPayloads >= 3,
2100
+ sessionAuthorityRuntimeBacked: authority.authorityFrame === authority.frames
2101
+ && authority.predictedFrame === authority.frames
2102
+ && authority.convergence
2103
+ && authority.replayVerified,
2104
+ sessionAuthorityLifecycleCovered: authority.reconnects > 0
2105
+ && authority.lateJoins > 0
2106
+ && authority.spectators > 0
2107
+ && authority.lateJoinReconstructed
2108
+ && authority.spectatorReconstructed,
2109
+ sessionAuthorityPredictionCovered: authority.predictedInputs > 0
2110
+ && authority.confirmedInputs > 0
2111
+ && authority.replacementInputs > 0
2112
+ && authority.correctionApplied,
2113
+ sessionAuthorityCommandsCovered: authority.predictedCommands > 0 && authority.confirmedCommands > 0,
2114
+ lifecycleProtocolStarted: lifecycleProtocol.startRequests > 0 && lifecycleProtocol.simulationStarts > 0,
2115
+ lifecycleProtocolAddPlayerCovered: lifecycleProtocol.spectatorJoinsAccepted > 0
2116
+ && lifecycleProtocol.addPlayerAccepted > 0
2117
+ && lifecycleProtocol.addPlayerFailed > 0
2118
+ && lifecycleProtocol.duplicateAddRejected > 0,
2119
+ lifecycleProtocolSpectatorRejected: lifecycleProtocol.spectatorInputRejected > 0
2120
+ && lifecycleProtocol.spectatorCommandRejected > 0,
2121
+ lifecycleProtocolReconnectTtlCovered: lifecycleProtocol.sameActorReconnectAccepted > 0
2122
+ && lifecycleProtocol.differentActorReconnectRejected > 0
2123
+ && lifecycleProtocol.expiredReconnectRejected > 0,
2124
+ lifecycleProtocolReplayVerified: lifecycleProtocol.replayVerified,
2125
+ boundedRollbackHistoryCovered: rollbackHistory.convergence
2126
+ && rollbackHistory.rollbackDepthBounded
2127
+ && rollbackHistory.resimulatedFrames > 0,
2128
+ boundedRollbackEvictionRejected: rollbackHistory.evictedRestoreRejected,
2129
+ boundedRollbackMalformedSnapshotRejected: rollbackHistory.malformedRestoreRejected,
2130
+ boundedRollbackDuplicateRejected: rollbackHistory.duplicateRejected,
2131
+ boundedRollbackOrderStable: rollbackHistory.orderStable,
2132
+ playersCovered: metrics.players === players,
2133
+ executedRequestedFrames: metrics.frames === executedFrames,
2134
+ }, withFrameMetrics({
2135
+ players: metrics.players,
2136
+ frames: metrics.frames,
2137
+ verifiedFrame: metrics.verifiedFrame,
2138
+ predictedFrame: metrics.predictedFrame,
2139
+ verifiedChecksum: metrics.verifiedChecksum,
2140
+ predictedChecksum: metrics.predictedChecksum,
2141
+ replayChecksum: metrics.replayChecksum,
2142
+ replayVerified: metrics.replayVerified,
2143
+ correctionCount: metrics.correctionCount,
2144
+ staleCorrectionsRejected: metrics.staleCorrectionsRejected,
2145
+ replacementInputs: metrics.replacementInputs,
2146
+ repeatableInputs: metrics.repeatableInputs,
2147
+ playerNotPresentInputs: metrics.playerNotPresentInputs,
2148
+ serverReplacedInputs: metrics.serverReplacedInputs,
2149
+ commandFlagInputs: metrics.commandFlagInputs,
2150
+ predictedCommands: metrics.predictedCommands,
2151
+ confirmedCommands: metrics.confirmedCommands,
2152
+ rejectedCommands: metrics.rejectedCommands,
2153
+ commandReplayVerified: metrics.commandReplayVerified,
2154
+ commandSystemPolls: metrics.commandSystemPolls,
2155
+ lifecycleEvents: metrics.lifecycleEvents,
2156
+ addPlayerAccepted: metrics.addPlayerAccepted,
2157
+ removePlayerAccepted: metrics.removePlayerAccepted,
2158
+ duplicateAddRejected: metrics.duplicateAddRejected,
2159
+ removedSlotInputRejected: metrics.removedSlotInputRejected,
2160
+ fullRuntimeReplayExecuted: metrics.fullRuntimeReplayExecuted,
2161
+ structuredInputChecksum: structured.checksum,
2162
+ structuredInputReplayVerified: structured.replayVerified,
2163
+ structuredInputPredictionPreserved: structured.predictedFallbackPreservedData,
2164
+ structuredInputPayloadRejected: structured.rejectedNondeterministicPayload,
2165
+ structuredInputSchemaRejected: structured.rejectedSchemaPayloads,
2166
+ structuredInputs: structured.structuredInputs,
2167
+ structuredAxisTotal: structured.axisTotal,
2168
+ structuredFireTotal: structured.fireTotal,
2169
+ sessionAuthorityChecksum: authority.checksum,
2170
+ sessionAuthorityFrame: authority.authorityFrame,
2171
+ sessionAuthorityPredictedFrame: authority.predictedFrame,
2172
+ sessionAuthorityConvergence: authority.convergence,
2173
+ sessionAuthorityReplayVerified: authority.replayVerified,
2174
+ sessionAuthorityPredictedInputs: authority.predictedInputs,
2175
+ sessionAuthorityConfirmedInputs: authority.confirmedInputs,
2176
+ sessionAuthorityReplacementInputs: authority.replacementInputs,
2177
+ sessionAuthorityPredictedCommands: authority.predictedCommands,
2178
+ sessionAuthorityConfirmedCommands: authority.confirmedCommands,
2179
+ sessionAuthorityReconnects: authority.reconnects,
2180
+ sessionAuthorityLateJoins: authority.lateJoins,
2181
+ sessionAuthoritySpectators: authority.spectators,
2182
+ sessionAuthorityRejectedMessages: authority.rejectedMessages,
2183
+ sessionAuthorityCorrectionApplied: authority.correctionApplied,
2184
+ sessionAuthorityLateJoinReconstructed: authority.lateJoinReconstructed,
2185
+ sessionAuthoritySpectatorReconstructed: authority.spectatorReconstructed,
2186
+ sessionAuthorityInputDelayP95: authority.inputDelayP95,
2187
+ sessionAuthorityRollbackDepthP95: authority.rollbackDepthP95,
2188
+ lifecycleProtocolChecksum: lifecycleProtocol.checksum,
2189
+ lifecycleProtocolStartRequests: lifecycleProtocol.startRequests,
2190
+ lifecycleProtocolSimulationStarts: lifecycleProtocol.simulationStarts,
2191
+ lifecycleProtocolSpectatorJoinsAccepted: lifecycleProtocol.spectatorJoinsAccepted,
2192
+ lifecycleProtocolAddPlayerAccepted: lifecycleProtocol.addPlayerAccepted,
2193
+ lifecycleProtocolAddPlayerFailed: lifecycleProtocol.addPlayerFailed,
2194
+ lifecycleProtocolDuplicateAddRejected: lifecycleProtocol.duplicateAddRejected,
2195
+ lifecycleProtocolSpectatorInputRejected: lifecycleProtocol.spectatorInputRejected,
2196
+ lifecycleProtocolSpectatorCommandRejected: lifecycleProtocol.spectatorCommandRejected,
2197
+ lifecycleProtocolSameActorReconnectAccepted: lifecycleProtocol.sameActorReconnectAccepted,
2198
+ lifecycleProtocolDifferentActorReconnectRejected: lifecycleProtocol.differentActorReconnectRejected,
2199
+ lifecycleProtocolExpiredReconnectRejected: lifecycleProtocol.expiredReconnectRejected,
2200
+ lifecycleProtocolRemovePlayerAccepted: lifecycleProtocol.removePlayerAccepted,
2201
+ lifecycleProtocolReplayVerified: lifecycleProtocol.replayVerified,
2202
+ lifecycleProtocolVerifiedFrame: lifecycleProtocol.verifiedFrame,
2203
+ rollbackHistoryChecksum: rollbackHistory.checksum,
2204
+ rollbackHistoryBaselineChecksum: rollbackHistory.baselineChecksum,
2205
+ rollbackHistoryPredictedChecksumBeforeCorrection: rollbackHistory.predictedChecksumBeforeCorrection,
2206
+ rollbackHistoryResimulatedChecksum: rollbackHistory.resimulatedChecksum,
2207
+ rollbackHistoryRestoredHistoryChecksum: rollbackHistory.restoredHistoryChecksum,
2208
+ rollbackHistoryCapacity: rollbackHistory.capacity,
2209
+ rollbackHistoryRecordedFrames: rollbackHistory.recordedFrames,
2210
+ rollbackHistoryEvictedFrames: rollbackHistory.evictedFrames,
2211
+ rollbackHistoryRestoredFrame: rollbackHistory.restoredFrame,
2212
+ rollbackHistoryPredictedToFrame: rollbackHistory.predictedToFrame,
2213
+ rollbackHistoryResimulatedFrames: rollbackHistory.resimulatedFrames,
2214
+ rollbackHistoryConvergence: rollbackHistory.convergence,
2215
+ rollbackHistoryDuplicateRejected: rollbackHistory.duplicateRejected,
2216
+ rollbackHistoryEvictedRestoreRejected: rollbackHistory.evictedRestoreRejected,
2217
+ rollbackHistoryMalformedRestoreRejected: rollbackHistory.malformedRestoreRejected,
2218
+ rollbackHistoryDepthBounded: rollbackHistory.rollbackDepthBounded,
2219
+ rollbackHistoryOrderStable: rollbackHistory.orderStable,
2220
+ }, requestedFrames, metrics.frames));
2221
+ }
2222
+ function withFrameMetrics(metrics, requestedFrames, executedFrames) {
2223
+ return {
2224
+ ...metrics,
2225
+ requestedFrames,
2226
+ executedFrames,
2227
+ capped: executedFrames < requestedFrames,
2228
+ };
2229
+ }
2230
+ function updateSimulationHashValue(hash, value) {
2231
+ const mixed = hash ^ (value + 0x9e3779b9);
2232
+ return Math.imul(mixed, 16777619) >>> 0;
2233
+ }
2234
+ function categoryReport(category, checksum, invariants, metrics) {
2235
+ const failures = stableKeys(invariants)
2236
+ .filter((key) => invariants[key] === false);
2237
+ const { executionProofHash: _ignoredExecutionProofHash, ...baseMetrics } = metrics;
2238
+ const metricsWithIdentity = {
2239
+ ...baseMetrics,
2240
+ proofContract: `${category}@1`,
2241
+ subsystemChecksum: checksum,
2242
+ };
2243
+ const body = {
2244
+ category,
2245
+ result: failures.length === 0 ? 'pass' : 'fail',
2246
+ invariants,
2247
+ metrics: {
2248
+ ...metricsWithIdentity,
2249
+ executionProofHash: engineCompleteExecutionProofHash(category, checksum, metricsWithIdentity),
2250
+ },
2251
+ failures,
2252
+ };
2253
+ return {
2254
+ ...body,
2255
+ checksum: defaultChecksum(body),
2256
+ };
2257
+ }
2258
+ function subsystemChecksumOf(report) {
2259
+ return String(report.metrics.subsystemChecksum);
2260
+ }
2261
+ export function engineCompleteExecutionProofHash(category, subsystemChecksum, metrics) {
2262
+ const { executionProofHash: _ignoredExecutionProofHash, wallClockElapsedMs: _elapsed, wallClockMeanFrameMs: _mean, ...stableMetrics } = metrics;
2263
+ return defaultChecksum({
2264
+ category,
2265
+ subsystemChecksum,
2266
+ requestedFrames: stableMetrics.requestedFrames,
2267
+ executedFrames: stableMetrics.executedFrames,
2268
+ capped: stableMetrics.capped,
2269
+ proofContract: stableMetrics.proofContract,
2270
+ metrics: stableMetrics,
2271
+ });
2272
+ }
2273
+ function stableKeys(record) {
2274
+ return Reflect.ownKeys(record)
2275
+ .filter((key) => typeof key === 'string')
2276
+ .sort();
2277
+ }
2278
+ function engineCompleteNowMs() {
2279
+ return globalThis.performance.now();
2280
+ }
2281
+ function percentileNumber(values, percentile) {
2282
+ if (values.length === 0) {
2283
+ return 0;
2284
+ }
2285
+ const sorted = [...values].sort((left, right) => left - right);
2286
+ const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil((percentile / 100) * sorted.length) - 1));
2287
+ return sorted[index];
2288
+ }
2289
+ function roundMetric(value, places) {
2290
+ const scale = 10 ** places;
2291
+ return Math.round(value * scale) / scale;
2292
+ }