@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,429 @@
1
+ import { defaultChecksum } from './canonical.js';
2
+ import { createDeterministicCommandTimeline, createDeterministicCommandTimelineSnapshot, } from './commands.js';
3
+ import { createDeterministicLocalAuthorityRuntime, } from './local-authority.js';
4
+ import { deterministicLocalAuthorityWireProtocol } from './local-authority-wire.js';
5
+ export function createDeterministicAuthorityServer(options) {
6
+ if (!Number.isInteger(options.players) || options.players < 1) {
7
+ throw new Error('Deterministic authority server players must be a positive integer');
8
+ }
9
+ const runtime = createDeterministicLocalAuthorityRuntime(options.players, options);
10
+ const commandTimeline = createAuthorityCommandTimeline(options);
11
+ const botSlots = new Set();
12
+ const connectedSlots = new Set();
13
+ const inputHistory = [];
14
+ const lifecycleEvents = [];
15
+ let closed = false;
16
+ const server = {
17
+ protocol: deterministicLocalAuthorityWireProtocol,
18
+ players: options.players,
19
+ get botSlots() {
20
+ assertOpen();
21
+ return [...botSlots].sort(compareNumbers);
22
+ },
23
+ get state() {
24
+ assertOpen();
25
+ return runtime.state;
26
+ },
27
+ connectClient(clientOptions) {
28
+ assertOpen();
29
+ assertClientSlot(options.players, clientOptions.slot);
30
+ if (clientOptions.spectator === true && clientOptions.bot === true) {
31
+ throw new Error('Deterministic multiplayer client cannot be both a spectator and a bot');
32
+ }
33
+ const existingClient = runtime.state.clients[clientOptions.slot];
34
+ if (clientOptions.spectator === true && botSlots.has(clientOptions.slot)) {
35
+ throw new Error(`Deterministic multiplayer slot ${clientOptions.slot} is already registered as a bot`);
36
+ }
37
+ if (clientOptions.bot === true && existingClient?.spectator === true) {
38
+ throw new Error(`Deterministic multiplayer slot ${clientOptions.slot} is already registered as a spectator`);
39
+ }
40
+ if (connectedSlots.has(clientOptions.slot)) {
41
+ throw new Error(`Deterministic multiplayer slot ${clientOptions.slot} is already connected`);
42
+ }
43
+ if (clientOptions.lateJoin === true) {
44
+ runtime.lateJoin(clientOptions.slot);
45
+ lifecycleEvents.push({ kind: 'lateJoin', slot: clientOptions.slot, frame: runtime.state.frame });
46
+ }
47
+ if (clientOptions.spectator === true) {
48
+ runtime.addSpectator(clientOptions.slot);
49
+ lifecycleEvents.push({ kind: 'spectator', slot: clientOptions.slot, frame: runtime.state.frame });
50
+ }
51
+ if (clientOptions.bot === true) {
52
+ botSlots.add(clientOptions.slot);
53
+ }
54
+ connectedSlots.add(clientOptions.slot);
55
+ return createLocalClientConnection(runtime, commandTimeline, () => server.botSlots, () => inputHistory, () => lifecycleEvents, (input) => inputHistory.push(input), (event) => lifecycleEvents.push(event), clientOptions.slot, clientRole(clientOptions), assertOpen);
56
+ },
57
+ stepFrames(count) {
58
+ assertOpen();
59
+ if (!Number.isInteger(count) || count < 0) {
60
+ throw new Error('Deterministic authority server step count must be a non-negative integer');
61
+ }
62
+ for (let frame = 0; frame < count; frame += 1) {
63
+ runtime.step();
64
+ commandTimeline?.setCurrentFrame(runtime.state.frame);
65
+ }
66
+ },
67
+ pollCommands(frame, pollOptions = {}) {
68
+ assertOpen();
69
+ return requireCommandTimeline(commandTimeline).poll(frame, pollOptions.slot, pollOptions.channelId);
70
+ },
71
+ commandSnapshot() {
72
+ assertOpen();
73
+ return requireCommandTimeline(commandTimeline).snapshot();
74
+ },
75
+ snapshot() {
76
+ assertOpen();
77
+ return runtime.snapshot();
78
+ },
79
+ checksums() {
80
+ assertOpen();
81
+ return createAuthorityChecksums(runtime, commandTimeline);
82
+ },
83
+ exportCertificationArtifact() {
84
+ assertOpen();
85
+ return createCertificationArtifact(runtime, commandTimeline, server.botSlots, inputHistory, lifecycleEvents);
86
+ },
87
+ close() {
88
+ closed = true;
89
+ },
90
+ };
91
+ function assertOpen() {
92
+ if (closed) {
93
+ throw new Error('Deterministic authority server is closed');
94
+ }
95
+ }
96
+ return server;
97
+ }
98
+ export function createSyncplayClient(options) {
99
+ const connection = options.connection;
100
+ return {
101
+ slot: connection.slot,
102
+ role: connection.role,
103
+ submitInput(input) {
104
+ return connection.submitInput(input);
105
+ },
106
+ submitCommand(command) {
107
+ return connection.submitCommand(command);
108
+ },
109
+ pollCommands(frame, commandOptions = {}) {
110
+ return connection.pollCommands(frame, commandOptions);
111
+ },
112
+ commandSnapshot() {
113
+ return connection.commandSnapshot();
114
+ },
115
+ lateJoin() {
116
+ return connection.lateJoin();
117
+ },
118
+ disconnect() {
119
+ connection.disconnect();
120
+ },
121
+ reconnect() {
122
+ connection.reconnect();
123
+ },
124
+ snapshot() {
125
+ return connection.snapshot();
126
+ },
127
+ checksums() {
128
+ return connection.checksums();
129
+ },
130
+ exportCertificationArtifact() {
131
+ return connection.exportCertificationArtifact();
132
+ },
133
+ };
134
+ }
135
+ export function verifyDeterministicAuthorityCertificationArtifact(artifact) {
136
+ const diagnostics = [];
137
+ if (artifact.schemaVersion !== 1) {
138
+ diagnostics.push('schema-version-mismatch');
139
+ }
140
+ if (artifact.protocol !== deterministicLocalAuthorityWireProtocol) {
141
+ diagnostics.push('protocol-mismatch');
142
+ }
143
+ if (artifact.players !== artifact.snapshot.state.players) {
144
+ diagnostics.push('players-mismatch');
145
+ }
146
+ if (artifact.frame !== artifact.snapshot.state.frame) {
147
+ diagnostics.push('frame-mismatch');
148
+ }
149
+ if (artifact.snapshotChecksum !== defaultChecksum(artifact.snapshot)) {
150
+ diagnostics.push('snapshot-checksum-mismatch');
151
+ }
152
+ if (artifact.checksums.authorityChecksum !== defaultChecksum(artifact.snapshot.state)) {
153
+ diagnostics.push('authority-checksum-mismatch');
154
+ }
155
+ if (artifact.checksums.clientChecksum !== defaultChecksum(artifact.snapshot.clientStateBySlot)) {
156
+ diagnostics.push('client-checksum-mismatch');
157
+ }
158
+ if (artifact.checksums.authorityProjectionChecksum !== defaultChecksum(artifact.snapshot.state.stateBySlot)) {
159
+ diagnostics.push('authority-projection-checksum-mismatch');
160
+ }
161
+ if (artifact.checksums.clientProjectionChecksum !== defaultChecksum(artifact.snapshot.clientStateBySlot)) {
162
+ diagnostics.push('client-projection-checksum-mismatch');
163
+ }
164
+ if (artifact.checksums.commandChecksum !== artifact.snapshot.commandChecksum) {
165
+ diagnostics.push('command-checksum-mismatch');
166
+ }
167
+ if (artifact.checksums.convergence !== (artifact.checksums.authorityProjectionChecksum === artifact.checksums.clientProjectionChecksum)) {
168
+ diagnostics.push('convergence-mismatch');
169
+ }
170
+ if (!sameNumberList(artifact.botSlots, [...artifact.botSlots].sort(compareNumbers))) {
171
+ diagnostics.push('bot-slots-order-mismatch');
172
+ }
173
+ if (!sameNumberList(artifact.lateJoinSlots, artifact.snapshot.lateJoinSlots)) {
174
+ diagnostics.push('late-join-slots-mismatch');
175
+ }
176
+ if (!sameNumberList(artifact.spectatorSlots, artifact.snapshot.spectatorSlots)) {
177
+ diagnostics.push('spectator-slots-mismatch');
178
+ }
179
+ const replay = replayAuthorityCertificationHistory(artifact.players, artifact.frame, artifact.inputHistory, artifact.lifecycleEvents);
180
+ if (artifact.replay.mismatches !== replay.mismatches) {
181
+ diagnostics.push('replay-mismatches-mismatch');
182
+ }
183
+ if (artifact.replay.replayedAuthorityChecksum !== replay.replayedAuthorityChecksum) {
184
+ diagnostics.push('replay-authority-evidence-mismatch');
185
+ }
186
+ if (artifact.replay.replayedProjectionChecksum !== replay.replayedProjectionChecksum) {
187
+ diagnostics.push('replay-projection-evidence-mismatch');
188
+ }
189
+ if (replay.replayedAuthorityChecksum !== artifact.checksums.authorityChecksum) {
190
+ diagnostics.push('replay-authority-checksum-mismatch');
191
+ }
192
+ if (replay.replayedProjectionChecksum !== artifact.checksums.authorityProjectionChecksum) {
193
+ diagnostics.push('replay-projection-checksum-mismatch');
194
+ }
195
+ if (artifact.commandSnapshot === undefined) {
196
+ if (artifact.commandSnapshotChecksum !== undefined || artifact.checksums.commandTimelineChecksum !== undefined) {
197
+ diagnostics.push('command-snapshot-mismatch');
198
+ }
199
+ }
200
+ else {
201
+ const recomputedCommandSnapshot = createDeterministicCommandTimelineSnapshot({
202
+ currentFrame: artifact.commandSnapshot.currentFrame,
203
+ accepted: artifact.commandSnapshot.accepted,
204
+ rejected: artifact.commandSnapshot.rejected,
205
+ });
206
+ if (artifact.commandSnapshotChecksum !== recomputedCommandSnapshot.checksum) {
207
+ diagnostics.push('command-snapshot-checksum-mismatch');
208
+ }
209
+ if (artifact.checksums.commandTimelineChecksum !== defaultChecksum({
210
+ accepted: recomputedCommandSnapshot.accepted,
211
+ rejected: recomputedCommandSnapshot.rejected,
212
+ })) {
213
+ diagnostics.push('command-timeline-checksum-mismatch');
214
+ }
215
+ }
216
+ const replayMismatches = replay.mismatches;
217
+ return {
218
+ result: diagnostics.length === 0 ? 'pass' : 'fail',
219
+ diagnostics,
220
+ replayMismatches,
221
+ convergence: artifact.checksums.convergence,
222
+ };
223
+ }
224
+ function createLocalClientConnection(runtime, commandTimeline, getBotSlots, getInputHistory, getLifecycleEvents, recordInput, recordLifecycle, slot, role, assertOpen) {
225
+ return {
226
+ slot,
227
+ role,
228
+ submitInput(input) {
229
+ assertOpen();
230
+ const inputMessage = {
231
+ slot,
232
+ senderSlot: slot,
233
+ frame: input.frame,
234
+ value: input.value,
235
+ data: input.data,
236
+ delay: input.delay,
237
+ inputId: input.inputId,
238
+ };
239
+ const accepted = runtime.submitInput(inputMessage);
240
+ recordInput({ accepted, ...inputMessage });
241
+ return { accepted, slot, frame: input.frame };
242
+ },
243
+ submitCommand(command) {
244
+ assertOpen();
245
+ const timeline = requireCommandTimeline(commandTimeline);
246
+ const normalizedCommand = {
247
+ id: command.id,
248
+ slot: command.slot ?? slot,
249
+ frame: command.frame,
250
+ channelId: command.channelId,
251
+ payload: command.payload,
252
+ ...(command.predicted === undefined ? {} : { predicted: command.predicted }),
253
+ };
254
+ const rejection = role === 'spectator'
255
+ ? timeline.recordFromSlot(-1, normalizedCommand)
256
+ : timeline.recordFromSlot(slot, normalizedCommand);
257
+ return {
258
+ accepted: rejection === undefined,
259
+ slot,
260
+ frame: command.frame,
261
+ commandId: command.id,
262
+ ...(rejection === undefined ? {} : { rejectionReason: rejection.reason }),
263
+ };
264
+ },
265
+ pollCommands(frame, options = {}) {
266
+ assertOpen();
267
+ return requireCommandTimeline(commandTimeline).poll(frame, options.slot, options.channelId);
268
+ },
269
+ commandSnapshot() {
270
+ assertOpen();
271
+ return requireCommandTimeline(commandTimeline).snapshot();
272
+ },
273
+ lateJoin() {
274
+ assertOpen();
275
+ runtime.lateJoin(slot);
276
+ recordLifecycle({ kind: 'lateJoin', slot, frame: runtime.state.frame });
277
+ return { slot, frame: runtime.state.frame };
278
+ },
279
+ disconnect() {
280
+ assertOpen();
281
+ runtime.disconnect(slot);
282
+ recordLifecycle({ kind: 'disconnect', slot, frame: runtime.state.frame });
283
+ },
284
+ reconnect() {
285
+ assertOpen();
286
+ runtime.reconnect(slot);
287
+ recordLifecycle({ kind: 'reconnect', slot, frame: runtime.state.frame });
288
+ },
289
+ snapshot() {
290
+ assertOpen();
291
+ return runtime.snapshot();
292
+ },
293
+ checksums() {
294
+ assertOpen();
295
+ return createAuthorityChecksums(runtime, commandTimeline);
296
+ },
297
+ exportCertificationArtifact() {
298
+ assertOpen();
299
+ return createCertificationArtifact(runtime, commandTimeline, getBotSlots(), getInputHistory(), getLifecycleEvents());
300
+ },
301
+ };
302
+ }
303
+ function createCertificationArtifact(runtime, commandTimeline, botSlots, inputHistory, lifecycleEvents) {
304
+ const snapshot = runtime.snapshot();
305
+ const checksums = createAuthorityChecksums(runtime, commandTimeline);
306
+ const commandSnapshot = commandTimeline?.snapshot();
307
+ const replay = replayAuthorityCertificationHistory(snapshot.state.players, snapshot.state.frame, inputHistory, lifecycleEvents);
308
+ const artifact = {
309
+ schemaVersion: 1,
310
+ result: checksums.convergence ? 'pass' : 'fail',
311
+ protocol: deterministicLocalAuthorityWireProtocol,
312
+ players: snapshot.state.players,
313
+ frame: snapshot.state.frame,
314
+ replayMismatches: checksums.convergence ? 0 : 1,
315
+ botSlots: [...botSlots].sort(compareNumbers),
316
+ lateJoinSlots: snapshot.lateJoinSlots,
317
+ spectatorSlots: snapshot.spectatorSlots,
318
+ inputHistory: inputHistory.map((input) => ({ ...input })),
319
+ lifecycleEvents: lifecycleEvents.map((event) => ({ ...event })),
320
+ replay,
321
+ snapshotChecksum: defaultChecksum(snapshot),
322
+ checksums,
323
+ snapshot,
324
+ ...(commandSnapshot === undefined ? {} : {
325
+ commandSnapshotChecksum: commandSnapshot.checksum,
326
+ commandSnapshot,
327
+ }),
328
+ };
329
+ const verification = verifyDeterministicAuthorityCertificationArtifact(artifact);
330
+ return {
331
+ ...artifact,
332
+ result: verification.result,
333
+ replayMismatches: verification.replayMismatches,
334
+ };
335
+ }
336
+ function createAuthorityCommandTimeline(options) {
337
+ if (options.commandChannels === undefined) {
338
+ return undefined;
339
+ }
340
+ return createDeterministicCommandTimeline({
341
+ playerCount: options.players,
342
+ currentFrame: 0,
343
+ channels: options.commandChannels,
344
+ validAssetRefs: options.validCommandAssetRefs,
345
+ validatePayload: options.validateCommandPayload,
346
+ });
347
+ }
348
+ function requireCommandTimeline(commandTimeline) {
349
+ if (commandTimeline === undefined) {
350
+ throw new Error('Deterministic authority server command timeline is not configured');
351
+ }
352
+ return commandTimeline;
353
+ }
354
+ function createAuthorityChecksums(runtime, commandTimeline) {
355
+ const checksums = runtime.checksums();
356
+ return {
357
+ ...checksums,
358
+ ...(commandTimeline === undefined ? {} : { commandTimelineChecksum: commandTimeline.checksum() }),
359
+ };
360
+ }
361
+ function replayAuthorityCertificationHistory(players, targetFrame, inputHistory, lifecycleEvents) {
362
+ const replayRuntime = createDeterministicLocalAuthorityRuntime(players);
363
+ const orderedEvents = [
364
+ ...lifecycleEvents.map((event, index) => ({ kind: 'lifecycle', frame: event.frame, index, event })),
365
+ ...inputHistory.map((input, index) => ({ kind: 'input', frame: input.frame, index, input })),
366
+ ].sort(compareReplayEvents);
367
+ let mismatches = 0;
368
+ for (const entry of orderedEvents) {
369
+ while (replayRuntime.state.frame < entry.frame) {
370
+ replayRuntime.step();
371
+ }
372
+ if (entry.kind === 'lifecycle') {
373
+ applyReplayLifecycleEvent(replayRuntime, entry.event);
374
+ }
375
+ else if (replayRuntime.submitInput(entry.input) !== entry.input.accepted) {
376
+ mismatches += 1;
377
+ }
378
+ }
379
+ while (replayRuntime.state.frame < targetFrame) {
380
+ replayRuntime.step();
381
+ }
382
+ const checksums = replayRuntime.checksums();
383
+ return {
384
+ mismatches,
385
+ replayedAuthorityChecksum: checksums.authorityChecksum,
386
+ replayedProjectionChecksum: checksums.authorityProjectionChecksum,
387
+ };
388
+ }
389
+ function applyReplayLifecycleEvent(replayRuntime, event) {
390
+ if (event.kind === 'lateJoin') {
391
+ replayRuntime.lateJoin(event.slot);
392
+ return;
393
+ }
394
+ if (event.kind === 'spectator') {
395
+ replayRuntime.addSpectator(event.slot);
396
+ return;
397
+ }
398
+ if (event.kind === 'disconnect') {
399
+ replayRuntime.disconnect(event.slot);
400
+ return;
401
+ }
402
+ replayRuntime.reconnect(event.slot);
403
+ }
404
+ function clientRole(options) {
405
+ if (options.spectator === true) {
406
+ return 'spectator';
407
+ }
408
+ if (options.bot === true) {
409
+ return 'bot';
410
+ }
411
+ return 'player';
412
+ }
413
+ function compareNumbers(left, right) {
414
+ return left - right;
415
+ }
416
+ function compareReplayEvents(left, right) {
417
+ return left.frame - right.frame || replayEventKindOrder(left.kind) - replayEventKindOrder(right.kind) || left.index - right.index;
418
+ }
419
+ function replayEventKindOrder(kind) {
420
+ return kind === 'lifecycle' ? 0 : 1;
421
+ }
422
+ function sameNumberList(left, right) {
423
+ return left.length === right.length && left.every((value, index) => value === right[index]);
424
+ }
425
+ function assertClientSlot(players, slot) {
426
+ if (!Number.isInteger(slot) || slot < 0 || slot >= players) {
427
+ throw new Error(`Invalid deterministic multiplayer client slot ${slot}`);
428
+ }
429
+ }
@@ -0,0 +1,91 @@
1
+ export interface DeterministicNavNode {
2
+ readonly id: string;
3
+ readonly x: number;
4
+ readonly y: number;
5
+ readonly cost?: number;
6
+ readonly region?: string;
7
+ }
8
+ export interface DeterministicNavEdge {
9
+ readonly from: string;
10
+ readonly to: string;
11
+ readonly cost?: number;
12
+ readonly offMesh?: boolean;
13
+ readonly bidirectional?: boolean;
14
+ }
15
+ export interface DeterministicNavRegion {
16
+ readonly id: string;
17
+ readonly enabled: boolean;
18
+ }
19
+ export interface DeterministicNavigationGraphSource {
20
+ readonly id: string;
21
+ readonly nodes: readonly DeterministicNavNode[];
22
+ readonly edges: readonly DeterministicNavEdge[];
23
+ readonly regions?: readonly DeterministicNavRegion[];
24
+ }
25
+ export interface CookedDeterministicNavigationGraph {
26
+ readonly id: string;
27
+ readonly nodes: readonly DeterministicNavNode[];
28
+ readonly edges: readonly DeterministicNavEdge[];
29
+ readonly regions: readonly DeterministicNavRegion[];
30
+ readonly bytes: string;
31
+ readonly hash: string;
32
+ }
33
+ export interface DeterministicPathQuery {
34
+ readonly start: string;
35
+ readonly goal: string;
36
+ readonly disabledRegionIds?: readonly string[];
37
+ readonly blockedNodeIds?: readonly string[];
38
+ }
39
+ export interface DeterministicPathResult {
40
+ readonly found: boolean;
41
+ readonly nodes: readonly string[];
42
+ readonly cost: number;
43
+ readonly visitedNodeCount: number;
44
+ readonly offMeshTraversals: number;
45
+ readonly pathHash: string;
46
+ }
47
+ export interface DeterministicFlowField {
48
+ readonly goal: string;
49
+ readonly nextByNodeId: Readonly<Record<string, string>>;
50
+ readonly hash: string;
51
+ }
52
+ export interface DeterministicTilemapSource {
53
+ readonly id: string;
54
+ readonly width: number;
55
+ readonly height: number;
56
+ readonly blocked?: readonly string[];
57
+ readonly costs?: Readonly<Record<string, number>>;
58
+ readonly portals?: Readonly<Record<string, string>>;
59
+ }
60
+ export interface DeterministicNavigationAgent {
61
+ readonly id: string;
62
+ readonly nodeId: string;
63
+ readonly targetNodeId: string;
64
+ readonly path?: readonly string[];
65
+ readonly pathIndex?: number;
66
+ }
67
+ export interface DeterministicNavigationStepOptions {
68
+ readonly frame: number;
69
+ readonly disabledRegionIds?: readonly string[];
70
+ readonly blockedNodeIds?: readonly string[];
71
+ }
72
+ export interface DeterministicNavigationStepResult {
73
+ readonly agents: readonly DeterministicNavigationAgent[];
74
+ readonly replans: number;
75
+ readonly avoidanceCorrections: number;
76
+ readonly offMeshTraversals: number;
77
+ readonly visitedNodeCount: number;
78
+ readonly reciprocalAvoidanceCorrections: number;
79
+ readonly agentChecksum: string;
80
+ }
81
+ export declare function cookDeterministicNavigationGraph(source: DeterministicNavigationGraphSource): CookedDeterministicNavigationGraph;
82
+ export declare function findDeterministicPath(graph: CookedDeterministicNavigationGraph, query: DeterministicPathQuery): DeterministicPathResult;
83
+ export declare function createDeterministicFlowField(graph: CookedDeterministicNavigationGraph, goal: string, options?: Omit<DeterministicPathQuery, 'start' | 'goal'>): DeterministicFlowField;
84
+ export declare function smoothDeterministicPath(graph: CookedDeterministicNavigationGraph, path: readonly string[]): readonly string[];
85
+ export declare function applyDeterministicReciprocalAvoidance(agents: readonly DeterministicNavigationAgent[]): {
86
+ readonly agents: readonly DeterministicNavigationAgent[];
87
+ readonly corrections: number;
88
+ readonly hash: string;
89
+ };
90
+ export declare function cookTilemapNavigationGraph(source: DeterministicTilemapSource): CookedDeterministicNavigationGraph;
91
+ export declare function stepDeterministicNavigationAgents(graph: CookedDeterministicNavigationGraph, agents: readonly DeterministicNavigationAgent[], options: DeterministicNavigationStepOptions): DeterministicNavigationStepResult;