@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,495 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cookDeterministicAnimation = cookDeterministicAnimation;
4
+ exports.createDeterministicAnimationRuntimeState = createDeterministicAnimationRuntimeState;
5
+ exports.stepDeterministicAnimation = stepDeterministicAnimation;
6
+ exports.runDeterministicAnimationFixture = runDeterministicAnimationFixture;
7
+ exports.reconstructDeterministicAnimationEvents = reconstructDeterministicAnimationEvents;
8
+ const canonical_js_1 = require("./canonical.js");
9
+ function cookDeterministicAnimation(descriptor) {
10
+ validateAnimationDescriptor(descriptor);
11
+ const cooked = {
12
+ id: descriptor.id,
13
+ initialState: descriptor.initialState,
14
+ clips: [...descriptor.clips].sort((left, right) => compareStrings(left.id, right.id)),
15
+ states: [...descriptor.states].sort((left, right) => compareStrings(left.id, right.id)),
16
+ transitions: [...descriptor.transitions].sort(compareTransitions),
17
+ layers: [...(descriptor.layers ?? [])].sort((left, right) => compareStrings(left.id, right.id)),
18
+ eventReconstruction: descriptor.eventReconstruction ?? 'synchronized',
19
+ };
20
+ const bytes = (0, canonical_js_1.canonicalStringify)(cooked);
21
+ return { id: descriptor.id, bytes, hash: (0, canonical_js_1.defaultChecksum)(bytes) };
22
+ }
23
+ function createDeterministicAnimationRuntimeState(descriptor, parameters = {}) {
24
+ validateAnimationDescriptor(descriptor);
25
+ return {
26
+ stateId: descriptor.initialState,
27
+ clipFrame: 0,
28
+ parameters: (0, canonical_js_1.cloneCanonical)(parameters),
29
+ consumedTriggers: [],
30
+ transitionBlend: undefined,
31
+ };
32
+ }
33
+ function stepDeterministicAnimation(descriptor, state, parameterUpdates = {}) {
34
+ const states = new Map(descriptor.states.map((animationState) => [animationState.id, animationState]));
35
+ const clips = new Map(descriptor.clips.map((clip) => [clip.id, clip]));
36
+ const parameters = { ...state.parameters, ...parameterUpdates };
37
+ const currentAnimationState = states.get(state.stateId);
38
+ if (currentAnimationState === undefined) {
39
+ throw new Error(`Unknown animation state ${state.stateId}`);
40
+ }
41
+ if (!clips.has(currentAnimationState.clipId)) {
42
+ throw new Error(`Unknown animation clip ${currentAnimationState.clipId}`);
43
+ }
44
+ const consumedTriggers = clearReleasedTriggers(descriptor, parameters, state.consumedTriggers);
45
+ const transition = descriptor.transitions
46
+ .filter((candidate) => candidate.from === state.stateId)
47
+ .sort(compareTransitions)
48
+ .find((candidate) => isAnimationTransitionEligible(candidate, parameters, state.clipFrame, consumedTriggers));
49
+ const nextStateId = transition?.to ?? state.stateId;
50
+ const animationState = states.get(nextStateId);
51
+ if (animationState === undefined) {
52
+ throw new Error(`Unknown animation state ${nextStateId}`);
53
+ }
54
+ const clip = clips.get(animationState.clipId);
55
+ if (clip === undefined) {
56
+ throw new Error(`Unknown animation clip ${animationState.clipId}`);
57
+ }
58
+ const clipFrame = transition === undefined ? advanceClipFrame(clip, state.clipFrame) : 0;
59
+ const nextConsumedTriggers = transition?.consumeTrigger === undefined ? consumedTriggers : [...consumedTriggers, transition.consumeTrigger].sort(compareStrings);
60
+ const transitionBlend = advanceAnimationBlend(state.transitionBlend, transition, state.clipFrame);
61
+ const previousPose = transition === undefined
62
+ ? sampleAnimationPose(descriptor, clips, currentAnimationState, state.clipFrame, parameters, undefined)
63
+ : {};
64
+ const sampledPose = sampleAnimationPose(descriptor, clips, animationState, clipFrame, parameters, transitionBlend);
65
+ const layerPoses = sampleAnimationLayers(descriptor, clips, clip, clipFrame, parameters);
66
+ const rootMotionDelta = rootMotionDeltaForState(descriptor, clips, animationState, previousPose, sampledPose);
67
+ const blendWeight = transitionBlend === undefined ? 1_000 : Math.trunc((transitionBlend.elapsedFrames * 1_000) / Math.max(1, transitionBlend.durationFrames));
68
+ const events = (clip.events ?? [])
69
+ .filter((event) => event.frame === clipFrame)
70
+ .sort((left, right) => compareStrings(left.id, right.id))
71
+ .map((event) => ({ id: event.id, clipId: clip.id, frame: clipFrame }));
72
+ const next = { stateId: nextStateId, clipFrame, parameters, consumedTriggers: nextConsumedTriggers, transitionBlend };
73
+ return {
74
+ state: next,
75
+ events,
76
+ transitionCount: transition === undefined ? 0 : 1,
77
+ exitFrameTransitionCount: transition?.exitFrame === undefined ? 0 : 1,
78
+ transitionBlendActive: transitionBlend !== undefined,
79
+ sampledPose,
80
+ layerPoses,
81
+ rootMotionDelta,
82
+ blendWeight,
83
+ checksum: (0, canonical_js_1.defaultChecksum)({ next, events, sampledPose, layerPoses, rootMotionDelta, blendWeight }),
84
+ };
85
+ }
86
+ function runDeterministicAnimationFixture(frames) {
87
+ const descriptor = defaultAnimationDescriptor();
88
+ const cooked = cookDeterministicAnimation(descriptor);
89
+ let state = createDeterministicAnimationRuntimeState(descriptor, { moving: false, fire: false });
90
+ let snapshot = state;
91
+ let transitions = 0;
92
+ let exitFrameTransitions = 0;
93
+ let transitionBlendFrames = 0;
94
+ let triggerLatchCount = 0;
95
+ let emittedEvents = 0;
96
+ const clipFrames = [];
97
+ const normalizedTimes = [];
98
+ const poses = [];
99
+ const layerPoses = [];
100
+ const blendWeights = [];
101
+ const rootMotionDeltas = [];
102
+ for (let frame = 0; frame < frames; frame += 1) {
103
+ if (frame === Math.floor(frames / 2)) {
104
+ snapshot = (0, canonical_js_1.cloneCanonical)(state);
105
+ }
106
+ const result = stepDeterministicAnimation(descriptor, state, animationFixtureParameters(frame));
107
+ state = result.state;
108
+ transitions += result.transitionCount;
109
+ exitFrameTransitions += result.exitFrameTransitionCount;
110
+ transitionBlendFrames += result.transitionBlendActive ? 1 : 0;
111
+ triggerLatchCount += result.state.consumedTriggers.length;
112
+ emittedEvents += result.events.length;
113
+ clipFrames.push(state.clipFrame);
114
+ normalizedTimes.push(normalizeClipTime(descriptor, state));
115
+ poses.push(result.sampledPose);
116
+ layerPoses.push(result.layerPoses);
117
+ blendWeights.push(result.blendWeight);
118
+ rootMotionDeltas.push(result.rootMotionDelta);
119
+ }
120
+ const checksum = (0, canonical_js_1.defaultChecksum)(state);
121
+ let restored = snapshot;
122
+ for (let frame = Math.floor(frames / 2); frame < frames; frame += 1) {
123
+ restored = stepDeterministicAnimation(descriptor, restored, animationFixtureParameters(frame)).state;
124
+ }
125
+ const reconstructed = reconstructDeterministicAnimationEvents(descriptor, snapshot, Math.floor(frames / 2), frames);
126
+ return {
127
+ checksum,
128
+ cookedAssetHash: cooked.hash,
129
+ transitions,
130
+ exitFrameTransitions,
131
+ transitionBlendFrames,
132
+ triggerLatchCount,
133
+ emittedEvents,
134
+ clipTimeHash: (0, canonical_js_1.defaultChecksum)(clipFrames),
135
+ normalizedTimeHash: (0, canonical_js_1.defaultChecksum)(normalizedTimes),
136
+ poseHash: (0, canonical_js_1.defaultChecksum)(poses),
137
+ blendWeightHash: (0, canonical_js_1.defaultChecksum)(blendWeights),
138
+ layerPoseHash: (0, canonical_js_1.defaultChecksum)(layerPoses),
139
+ rootMotionHash: (0, canonical_js_1.defaultChecksum)(rootMotionDeltas),
140
+ reconstructedEventHash: reconstructed.eventHash,
141
+ blendTreeFrames: poses.filter((pose) => Object.prototype.hasOwnProperty.call(pose, 'upper-aim')).length,
142
+ layerPoseFrames: layerPoses.filter((pose) => stableLayerIds(pose).length > 0).length,
143
+ rootMotionFrames: rootMotionDeltas.filter((delta) => delta !== 0).length,
144
+ reconstructedEvents: reconstructed.events,
145
+ rollbackEquivalent: checksum === (0, canonical_js_1.defaultChecksum)(restored),
146
+ };
147
+ }
148
+ function reconstructDeterministicAnimationEvents(descriptor, snapshot, startFrame, endFrame) {
149
+ let state = (0, canonical_js_1.cloneCanonical)(snapshot);
150
+ const events = [];
151
+ for (let frame = startFrame; frame < endFrame; frame += 1) {
152
+ const result = stepDeterministicAnimation(descriptor, state, animationFixtureParameters(frame));
153
+ state = result.state;
154
+ events.push(...result.events.map((event) => ({ frame, id: event.id, clipId: event.clipId })));
155
+ }
156
+ return {
157
+ events: events.length,
158
+ eventHash: (0, canonical_js_1.defaultChecksum)(events),
159
+ checksum: (0, canonical_js_1.defaultChecksum)({ state, events }),
160
+ };
161
+ }
162
+ function animationFixtureParameters(frame) {
163
+ return {
164
+ moving: frame % 40 > 5,
165
+ fire: frame % 31 <= 1,
166
+ aim: (frame * 7) % 101,
167
+ };
168
+ }
169
+ function defaultAnimationDescriptor() {
170
+ return {
171
+ id: 'humanoid.headless',
172
+ initialState: 'idle',
173
+ clips: [
174
+ { id: 'idle', durationFrames: 60, loop: true, events: [{ frame: 0, id: 'idle-loop' }], rootMotionTrackId: 'root-x', tracks: [{ id: 'root-x', keyframes: [{ frame: 0, value: 0 }, { frame: 59, value: 0 }] }, { id: 'upper-aim', keyframes: [{ frame: 0, value: 0 }, { frame: 59, value: 0 }] }] },
175
+ { id: 'run', durationFrames: 30, loop: true, events: [{ frame: 15, id: 'footstep' }], rootMotionTrackId: 'root-x', tracks: [{ id: 'root-x', keyframes: [{ frame: 0, value: 0 }, { frame: 29, value: 290 }] }, { id: 'upper-aim', keyframes: [{ frame: 0, value: 50 }, { frame: 29, value: 150 }] }] },
176
+ { id: 'fire', durationFrames: 12, loop: false, events: [{ frame: 4, id: 'muzzle' }], rootMotionTrackId: 'root-x', tracks: [{ id: 'root-x', keyframes: [{ frame: 0, value: 100 }, { frame: 11, value: 100 }] }, { id: 'upper-aim', keyframes: [{ frame: 0, value: 900 }, { frame: 11, value: 900 }] }] },
177
+ { id: 'aim-low', durationFrames: 30, loop: true, tracks: [{ id: 'upper-aim', keyframes: [{ frame: 0, value: 100 }, { frame: 29, value: 100 }] }] },
178
+ { id: 'aim-high', durationFrames: 30, loop: true, tracks: [{ id: 'upper-aim', keyframes: [{ frame: 0, value: 700 }, { frame: 29, value: 700 }] }] },
179
+ ],
180
+ states: [
181
+ { id: 'idle', clipId: 'idle' },
182
+ {
183
+ id: 'run',
184
+ clipId: 'run',
185
+ blendTree: {
186
+ parameter: 'aim',
187
+ clips: [
188
+ { clipId: 'aim-low', threshold: 0 },
189
+ { clipId: 'aim-high', threshold: 100 },
190
+ ],
191
+ },
192
+ },
193
+ { id: 'fire', clipId: 'fire' },
194
+ ],
195
+ transitions: [
196
+ { from: 'idle', to: 'run', when: { parameter: 'moving', equals: true }, priority: 1, durationFrames: 3 },
197
+ { from: 'run', to: 'idle', when: { parameter: 'moving', equals: false }, priority: 1, durationFrames: 3 },
198
+ { from: 'idle', to: 'fire', when: { parameter: 'fire', equals: true }, priority: 0, consumeTrigger: 'fire', durationFrames: 2 },
199
+ { from: 'run', to: 'fire', when: { parameter: 'fire', equals: true }, priority: 0, consumeTrigger: 'fire', durationFrames: 2 },
200
+ { from: 'fire', to: 'idle', when: { parameter: 'fire', equals: false }, priority: 9, exitFrame: 11, durationFrames: 1 },
201
+ ],
202
+ layers: [{ id: 'upper-body', stateId: 'run', weight: 500, additive: true }],
203
+ eventReconstruction: 'synchronized',
204
+ };
205
+ }
206
+ function validateAnimationDescriptor(descriptor) {
207
+ const clipIds = uniqueIds(descriptor.clips.map((clip) => clip.id), 'animation clip');
208
+ const stateIds = uniqueIds(descriptor.states.map((state) => state.id), 'animation state');
209
+ if (!stateIds.has(descriptor.initialState)) {
210
+ throw new Error(`Unknown initial animation state ${descriptor.initialState}`);
211
+ }
212
+ for (const clip of descriptor.clips) {
213
+ if (!Number.isInteger(clip.durationFrames) || clip.durationFrames <= 0) {
214
+ throw new Error(`Animation clip ${clip.id} has invalid duration`);
215
+ }
216
+ for (const event of clip.events ?? []) {
217
+ if (!Number.isInteger(event.frame) || event.frame < 0 || event.frame >= clip.durationFrames) {
218
+ throw new Error(`Animation clip ${clip.id} has event ${event.id} outside clip duration`);
219
+ }
220
+ }
221
+ uniqueIds((clip.events ?? []).map((event) => event.id), `animation clip ${clip.id} event`);
222
+ uniqueIds((clip.tracks ?? []).map((track) => track.id), `animation clip ${clip.id} track`);
223
+ for (const track of clip.tracks ?? []) {
224
+ if (track.keyframes.length === 0) {
225
+ throw new Error(`Animation track ${track.id} requires keyframes`);
226
+ }
227
+ uniqueIds(track.keyframes.map((keyframe) => String(keyframe.frame)), `animation track ${track.id} keyframe`);
228
+ for (const keyframe of track.keyframes) {
229
+ if (!Number.isInteger(keyframe.frame) || keyframe.frame < 0 || keyframe.frame >= clip.durationFrames) {
230
+ throw new Error(`Animation track ${track.id} has keyframe outside clip duration`);
231
+ }
232
+ if (!Number.isInteger(keyframe.value)) {
233
+ throw new Error(`Animation track ${track.id} has unquantized value`);
234
+ }
235
+ }
236
+ }
237
+ if (clip.rootMotionTrackId !== undefined && !(clip.tracks ?? []).some((track) => track.id === clip.rootMotionTrackId)) {
238
+ throw new Error(`Animation clip ${clip.id} references missing root motion track ${clip.rootMotionTrackId}`);
239
+ }
240
+ }
241
+ for (const state of descriptor.states) {
242
+ if (!clipIds.has(state.clipId)) {
243
+ throw new Error(`Animation state ${state.id} references missing clip ${state.clipId}`);
244
+ }
245
+ if (state.blendTree !== undefined) {
246
+ if (state.blendTree.clips.length < 2) {
247
+ throw new Error(`Animation state ${state.id} blend tree requires at least two clips`);
248
+ }
249
+ for (const entry of state.blendTree.clips) {
250
+ if (!clipIds.has(entry.clipId)) {
251
+ throw new Error(`Animation state ${state.id} blend tree references missing clip ${entry.clipId}`);
252
+ }
253
+ if (!Number.isInteger(entry.threshold)) {
254
+ throw new Error(`Animation state ${state.id} blend tree threshold must be quantized integer`);
255
+ }
256
+ }
257
+ }
258
+ }
259
+ for (const transition of descriptor.transitions) {
260
+ if (!stateIds.has(transition.from) || !stateIds.has(transition.to)) {
261
+ throw new Error(`Animation transition ${transition.from}->${transition.to} references missing state`);
262
+ }
263
+ if (transition.exitFrame !== undefined && !Number.isInteger(transition.exitFrame)) {
264
+ throw new Error(`Animation transition ${transition.from}->${transition.to} has invalid exit frame`);
265
+ }
266
+ const fromState = descriptor.states.find((state) => state.id === transition.from);
267
+ const fromClip = descriptor.clips.find((clip) => clip.id === fromState?.clipId);
268
+ if (transition.exitFrame !== undefined && (transition.exitFrame < 0 || fromClip === undefined || transition.exitFrame >= fromClip.durationFrames)) {
269
+ throw new Error(`Animation transition ${transition.from}->${transition.to} has invalid exit frame`);
270
+ }
271
+ if (transition.durationFrames !== undefined && (!Number.isInteger(transition.durationFrames) || transition.durationFrames < 0)) {
272
+ throw new Error(`Animation transition ${transition.from}->${transition.to} has invalid duration`);
273
+ }
274
+ }
275
+ uniqueIds((descriptor.layers ?? []).map((candidate) => candidate.id), 'animation layer');
276
+ for (const layer of descriptor.layers ?? []) {
277
+ if (!stateIds.has(layer.stateId)) {
278
+ throw new Error(`Animation layer ${layer.id} references missing state ${layer.stateId}`);
279
+ }
280
+ if (!Number.isInteger(layer.weight) || layer.weight < 0 || layer.weight > 1_000) {
281
+ throw new Error(`Animation layer ${layer.id} has invalid weight`);
282
+ }
283
+ }
284
+ rejectAmbiguousAnimationTransitions(descriptor.transitions);
285
+ }
286
+ function rejectAmbiguousAnimationTransitions(transitions) {
287
+ const seen = new Set();
288
+ for (const transition of [...transitions].sort(compareTransitions)) {
289
+ const key = [
290
+ transition.from,
291
+ String(transition.priority ?? 0),
292
+ transition.when.parameter,
293
+ transitionWhenValueKey(transition.when.equals),
294
+ String(transition.exitFrame ?? -1),
295
+ transition.consumeTrigger ?? '',
296
+ ].join('|');
297
+ if (seen.has(key)) {
298
+ throw new Error(`Ambiguous animation transition from ${transition.from} on ${transition.when.parameter}`);
299
+ }
300
+ seen.add(key);
301
+ }
302
+ }
303
+ function transitionWhenValueKey(value) {
304
+ return `${typeof value}:${String(value)}`;
305
+ }
306
+ function isAnimationTransitionEligible(transition, parameters, clipFrame, consumedTriggers) {
307
+ if (parameters[transition.when.parameter] !== transition.when.equals) {
308
+ return false;
309
+ }
310
+ if (transition.exitFrame !== undefined && clipFrame < transition.exitFrame) {
311
+ return false;
312
+ }
313
+ return transition.consumeTrigger === undefined || !consumedTriggers.includes(transition.consumeTrigger);
314
+ }
315
+ function uniqueIds(ids, label) {
316
+ const sorted = [...ids].sort(compareStrings);
317
+ const duplicate = sorted.find((id, index) => index > 0 && id === sorted[index - 1]);
318
+ if (duplicate !== undefined) {
319
+ throw new Error(`Duplicate ${label} id ${duplicate}`);
320
+ }
321
+ return new Set(sorted);
322
+ }
323
+ function clearReleasedTriggers(descriptor, parameters, consumedTriggers) {
324
+ return consumedTriggers
325
+ .filter((trigger) => descriptor.transitions.some((transition) => (transition.consumeTrigger === trigger && parameters[transition.when.parameter] === transition.when.equals)))
326
+ .sort(compareStrings);
327
+ }
328
+ function advanceAnimationBlend(current, transition, fromClipFrame) {
329
+ if (transition !== undefined) {
330
+ const durationFrames = transition.durationFrames ?? 0;
331
+ if (durationFrames <= 0) {
332
+ return undefined;
333
+ }
334
+ return {
335
+ from: transition.from,
336
+ to: transition.to,
337
+ fromClipFrame,
338
+ elapsedFrames: 0,
339
+ durationFrames,
340
+ };
341
+ }
342
+ if (current === undefined) {
343
+ return undefined;
344
+ }
345
+ const elapsedFrames = current.elapsedFrames + 1;
346
+ return elapsedFrames >= current.durationFrames ? undefined : { ...current, elapsedFrames };
347
+ }
348
+ function sampleAnimationPose(descriptor, clips, state, clipFrame, parameters, blend) {
349
+ const currentPose = state.blendTree === undefined
350
+ ? sampleClipPose(clips.get(state.clipId), clipFrame)
351
+ : sampleBlendTreePose(clips, state.blendTree, parameters, clipFrame);
352
+ if (blend === undefined) {
353
+ return currentPose;
354
+ }
355
+ const fromState = descriptor.states.find((candidate) => candidate.id === blend.from);
356
+ if (fromState === undefined) {
357
+ throw new Error(`Animation transition blend references missing state ${blend.from}`);
358
+ }
359
+ const fromClip = clips.get(fromState.clipId);
360
+ if (fromClip === undefined) {
361
+ throw new Error(`Animation transition blend references missing clip ${fromState.clipId}`);
362
+ }
363
+ const fromPose = sampleClipPose(fromClip, blend.fromClipFrame);
364
+ const weight = Math.trunc((blend.elapsedFrames * 1_000) / Math.max(1, blend.durationFrames));
365
+ const keys = [...new Set([...stablePoseKeys(fromPose), ...stablePoseKeys(currentPose)])].sort(compareStrings);
366
+ return Object.fromEntries(keys.map((key) => [
367
+ key,
368
+ Math.trunc(((fromPose[key] ?? 0) * (1_000 - weight) + (currentPose[key] ?? 0) * weight) / 1_000),
369
+ ]));
370
+ }
371
+ function sampleAnimationLayers(descriptor, clips, sourceClip, clipFrame, parameters) {
372
+ const states = new Map(descriptor.states.map((state) => [state.id, state]));
373
+ const entries = [...(descriptor.layers ?? [])]
374
+ .sort((left, right) => compareStrings(left.id, right.id))
375
+ .map((layer) => {
376
+ const state = states.get(layer.stateId);
377
+ if (state === undefined) {
378
+ throw new Error(`Animation layer ${layer.id} references missing state ${layer.stateId}`);
379
+ }
380
+ const layerClip = clips.get(state.clipId);
381
+ if (layerClip === undefined) {
382
+ throw new Error(`Animation layer ${layer.id} references missing clip ${state.clipId}`);
383
+ }
384
+ const layerFrame = mapClipFrameToLayer(sourceClip, layerClip, clipFrame);
385
+ const pose = sampleAnimationPose(descriptor, clips, state, layerFrame, parameters, undefined);
386
+ const weighted = Object.fromEntries(stablePoseKeys(pose).map((key) => [
387
+ key,
388
+ Math.trunc((pose[key] * layer.weight) / 1_000),
389
+ ]));
390
+ return [layer.id, weighted];
391
+ });
392
+ return Object.fromEntries(entries);
393
+ }
394
+ function mapClipFrameToLayer(sourceClip, layerClip, clipFrame) {
395
+ const sourceDuration = Math.max(1, sourceClip.durationFrames);
396
+ const layerDuration = Math.max(1, layerClip.durationFrames);
397
+ return Math.min(layerDuration - 1, Math.trunc((clipFrame * layerDuration) / sourceDuration));
398
+ }
399
+ function sampleBlendTreePose(clips, blendTree, parameters, clipFrame) {
400
+ const sorted = [...blendTree.clips].sort((left, right) => left.threshold - right.threshold || compareStrings(left.clipId, right.clipId));
401
+ const first = sorted[0];
402
+ const last = sorted.at(-1);
403
+ const rawParameter = parameters[blendTree.parameter];
404
+ if (typeof rawParameter === 'number' && !Number.isFinite(rawParameter)) {
405
+ throw new Error(`Animation blend parameter ${blendTree.parameter} must be finite`);
406
+ }
407
+ const parameterValue = typeof rawParameter === 'number'
408
+ ? Math.max(first.threshold, Math.min(last.threshold, rawParameter))
409
+ : first.threshold;
410
+ const upperIndex = sorted.findIndex((entry) => entry.threshold >= parameterValue);
411
+ const lower = upperIndex <= 0 ? first : sorted[upperIndex - 1];
412
+ const upper = sorted[upperIndex];
413
+ const weight = Math.trunc(((parameterValue - lower.threshold) * 1_000) / Math.max(1, upper.threshold - lower.threshold));
414
+ const lowerClip = clips.get(lower.clipId);
415
+ if (lowerClip === undefined) {
416
+ throw new Error(`Animation blend tree references missing clip ${lower.clipId}`);
417
+ }
418
+ const upperClip = clips.get(upper.clipId);
419
+ if (upperClip === undefined) {
420
+ throw new Error(`Animation blend tree references missing clip ${upper.clipId}`);
421
+ }
422
+ const lowerPose = sampleClipPose(lowerClip, clipFrame);
423
+ const upperPose = sampleClipPose(upperClip, clipFrame);
424
+ const keys = [...new Set([...stablePoseKeys(lowerPose), ...stablePoseKeys(upperPose)])].sort(compareStrings);
425
+ return Object.fromEntries(keys.map((key) => [
426
+ key,
427
+ Math.trunc(((lowerPose[key] ?? 0) * (1_000 - weight) + (upperPose[key] ?? 0) * weight) / 1_000),
428
+ ]));
429
+ }
430
+ function rootMotionDeltaForState(descriptor, clips, state, previousPose, currentPose) {
431
+ const clip = clips.get(state.clipId);
432
+ const trackId = clip?.rootMotionTrackId;
433
+ if (trackId === undefined) {
434
+ return 0;
435
+ }
436
+ const delta = (currentPose[trackId] ?? 0) - (previousPose[trackId] ?? currentPose[trackId] ?? 0);
437
+ return descriptor.eventReconstruction === 'non-synchronized' ? 0 : delta;
438
+ }
439
+ function stablePoseKeys(pose) {
440
+ return Reflect.ownKeys(pose)
441
+ .filter((key) => typeof key === 'string')
442
+ .sort(compareStrings);
443
+ }
444
+ function stableLayerIds(layers) {
445
+ return Reflect.ownKeys(layers)
446
+ .filter((key) => typeof key === 'string')
447
+ .sort(compareStrings);
448
+ }
449
+ function sampleClipPose(clip, clipFrame) {
450
+ const entries = [...(clip.tracks ?? [])]
451
+ .sort((left, right) => compareStrings(left.id, right.id))
452
+ .map((track) => [track.id, sampleTrack(track, clipFrame)]);
453
+ return Object.fromEntries(entries);
454
+ }
455
+ function sampleTrack(track, clipFrame) {
456
+ const keyframes = [...track.keyframes].sort((left, right) => left.frame - right.frame);
457
+ let previous = keyframes[0];
458
+ let next = keyframes.at(-1) ?? previous;
459
+ for (const keyframe of keyframes) {
460
+ if (keyframe.frame <= clipFrame) {
461
+ previous = keyframe;
462
+ }
463
+ if (keyframe.frame >= clipFrame) {
464
+ next = keyframe;
465
+ break;
466
+ }
467
+ }
468
+ if (previous.frame === next.frame) {
469
+ return previous.value;
470
+ }
471
+ const numerator = clipFrame - previous.frame;
472
+ const denominator = Math.max(1, next.frame - previous.frame);
473
+ return Math.trunc(previous.value + ((next.value - previous.value) * numerator) / denominator);
474
+ }
475
+ function normalizeClipTime(descriptor, state) {
476
+ const animationState = descriptor.states.find((candidate) => candidate.id === state.stateId);
477
+ const clip = descriptor.clips.find((candidate) => candidate.id === animationState?.clipId);
478
+ return Math.trunc((state.clipFrame * 1_000_000) / Math.max(1, clip.durationFrames));
479
+ }
480
+ function advanceClipFrame(clip, frame) {
481
+ const next = frame + 1;
482
+ if (next < clip.durationFrames) {
483
+ return next;
484
+ }
485
+ return clip.loop ? 0 : clip.durationFrames - 1;
486
+ }
487
+ function compareTransitions(left, right) {
488
+ return (left.priority ?? 0) - (right.priority ?? 0)
489
+ || compareStrings(left.from, right.from)
490
+ || compareStrings(left.to, right.to)
491
+ || compareStrings(left.when.parameter, right.when.parameter);
492
+ }
493
+ function compareStrings(left, right) {
494
+ return left < right ? -1 : left > right ? 1 : 0;
495
+ }