@downdraft/engine 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (908) hide show
  1. package/README.md +33 -0
  2. package/app/src/index.ts +10 -0
  3. package/app/src/renderer/bootstrap.ts +271 -0
  4. package/app/src/renderer/downdraft-base.css +93 -0
  5. package/app/src/renderer/feature-log.ts +200 -0
  6. package/app/src/renderer/game-module.ts +923 -0
  7. package/app/src/renderer/hooks.ts +88 -0
  8. package/app/src/renderer/hot-reload.ts +178 -0
  9. package/app/src/renderer/import-cache.ts +88 -0
  10. package/app/src/renderer/index.ts +206 -0
  11. package/app/src/renderer/mcp-harness.ts +144 -0
  12. package/app/src/renderer/save-store-factory.ts +239 -0
  13. package/app/src/renderer/sim-bridge.ts +183 -0
  14. package/app/src/renderer/standard-automation-tools.ts +771 -0
  15. package/app/src/shared/feature-log.ts +71 -0
  16. package/app/src/shared/gpu-info.ts +82 -0
  17. package/app/src/shared/import-cache-store.ts +142 -0
  18. package/app/src/shared/index.ts +6 -0
  19. package/app/src/shared/mcp-discovery.ts +47 -0
  20. package/app/src/shared/mcp-proxy.ts +82 -0
  21. package/app/src/shared/types.ts +324 -0
  22. package/asset-bake/src/bake-audio.ts +119 -0
  23. package/asset-bake/src/bake-gltf.ts +172 -0
  24. package/asset-bake/src/bake.ts +78 -0
  25. package/asset-bake/src/cache.ts +188 -0
  26. package/asset-bake/src/config.ts +186 -0
  27. package/asset-bake/src/index.ts +36 -0
  28. package/core/src/animation/animation-event.ts +41 -0
  29. package/core/src/animation/bone-mask.ts +122 -0
  30. package/core/src/animation/clip.ts +252 -0
  31. package/core/src/animation/index.ts +22 -0
  32. package/core/src/animation/mixamo.ts +113 -0
  33. package/core/src/animation/morph-target.ts +69 -0
  34. package/core/src/animation/player.ts +697 -0
  35. package/core/src/animation/retarget.ts +158 -0
  36. package/core/src/animation/skeleton-animator.ts +1399 -0
  37. package/core/src/animation/skeleton.ts +214 -0
  38. package/core/src/animation/state-machine.ts +280 -0
  39. package/core/src/assets/blob-store.ts +66 -0
  40. package/core/src/assets/cache.ts +186 -0
  41. package/core/src/assets/cubemap-converter.ts +155 -0
  42. package/core/src/assets/environment-manager.ts +121 -0
  43. package/core/src/assets/import-cache.ts +65 -0
  44. package/core/src/assets/import-settings.ts +72 -0
  45. package/core/src/assets/importer.ts +108 -0
  46. package/core/src/assets/index.ts +74 -0
  47. package/core/src/assets/irradiance-generator.ts +198 -0
  48. package/core/src/assets/loader-audio.ts +86 -0
  49. package/core/src/assets/loader-cubemap.ts +211 -0
  50. package/core/src/assets/loader-dds.ts +236 -0
  51. package/core/src/assets/loader-hdr.ts +474 -0
  52. package/core/src/assets/loader-mesh.ts +143 -0
  53. package/core/src/assets/loader-shader.ts +73 -0
  54. package/core/src/assets/loader-texture.ts +349 -0
  55. package/core/src/assets/lod.ts +198 -0
  56. package/core/src/assets/manager.ts +480 -0
  57. package/core/src/assets/manifest.ts +81 -0
  58. package/core/src/assets/material-bridge.ts +186 -0
  59. package/core/src/assets/model-importer.ts +72 -0
  60. package/core/src/assets/model-normalizer.ts +248 -0
  61. package/core/src/assets/model-to-gpu.ts +70 -0
  62. package/core/src/assets/model-to-mesh.ts +237 -0
  63. package/core/src/assets/prefilter-generator.ts +250 -0
  64. package/core/src/audio/engine.ts +177 -0
  65. package/core/src/audio/interface.ts +130 -0
  66. package/core/src/audio/listener.ts +32 -0
  67. package/core/src/audio/mixer.ts +141 -0
  68. package/core/src/audio/registry.ts +52 -0
  69. package/core/src/audio/sab.ts +87 -0
  70. package/core/src/audio/source.ts +89 -0
  71. package/core/src/change-detection/atomics.ts +1 -0
  72. package/core/src/change-detection/tracker.ts +47 -0
  73. package/core/src/debug-draw/lines.ts +64 -0
  74. package/core/src/debug-draw/points.ts +17 -0
  75. package/core/src/debug-draw/queue.ts +124 -0
  76. package/core/src/debug-draw/text.ts +25 -0
  77. package/core/src/ecs/archetype.ts +323 -0
  78. package/core/src/ecs/component.ts +188 -0
  79. package/core/src/ecs/entity.ts +25 -0
  80. package/core/src/ecs/events.ts +93 -0
  81. package/core/src/ecs/hierarchy.ts +124 -0
  82. package/core/src/ecs/hmr-swap-registry.ts +22 -0
  83. package/core/src/ecs/index.ts +25 -0
  84. package/core/src/ecs/query.ts +141 -0
  85. package/core/src/ecs/resource.ts +8 -0
  86. package/core/src/ecs/schedule.ts +181 -0
  87. package/core/src/ecs/system-params.ts +99 -0
  88. package/core/src/ecs/system.ts +42 -0
  89. package/core/src/ecs/world.ts +348 -0
  90. package/core/src/index.ts +352 -0
  91. package/core/src/input/context.ts +73 -0
  92. package/core/src/input/dom-handler.ts +380 -0
  93. package/core/src/input/local-player-manager.ts +218 -0
  94. package/core/src/input/mapping.ts +40 -0
  95. package/core/src/input/multi-sab-bridge.ts +276 -0
  96. package/core/src/input/multi-state.ts +54 -0
  97. package/core/src/input/sab-bridge.ts +71 -0
  98. package/core/src/input/state.ts +132 -0
  99. package/core/src/input/ui-router.ts +87 -0
  100. package/core/src/library/host.ts +317 -0
  101. package/core/src/library/index.ts +16 -0
  102. package/core/src/library/library.ts +292 -0
  103. package/core/src/material/compiler.ts +80 -0
  104. package/core/src/material/graph-bridge.ts +172 -0
  105. package/core/src/material/index.ts +9 -0
  106. package/core/src/material/library.ts +442 -0
  107. package/core/src/material/material.ts +156 -0
  108. package/core/src/material/variants.ts +61 -0
  109. package/core/src/math/mat4.ts +202 -0
  110. package/core/src/math/perlin-noise-2d.ts +77 -0
  111. package/core/src/math/perlin-noise-3d.ts +110 -0
  112. package/core/src/math/quat.ts +33 -0
  113. package/core/src/math/rng.ts +29 -0
  114. package/core/src/math/trig.ts +47 -0
  115. package/core/src/math/value-noise.ts +67 -0
  116. package/core/src/mesh/builder.ts +177 -0
  117. package/core/src/mesh/csg.ts +312 -0
  118. package/core/src/mesh/greased-line.ts +145 -0
  119. package/core/src/mesh/mesh.ts +17 -0
  120. package/core/src/mesh/parametric.ts +501 -0
  121. package/core/src/mesh/skinning.ts +67 -0
  122. package/core/src/mesh/vertex-layout.ts +91 -0
  123. package/core/src/module/cross-thread.ts +163 -0
  124. package/core/src/module/diagnostics.ts +123 -0
  125. package/core/src/module/host.ts +451 -0
  126. package/core/src/module/index.ts +22 -0
  127. package/core/src/module/module.ts +109 -0
  128. package/core/src/module/registry.ts +94 -0
  129. package/core/src/module/renderer-module.ts +247 -0
  130. package/core/src/module/ts-loader.ts +26 -0
  131. package/core/src/particles/compute-pass.ts +378 -0
  132. package/core/src/particles/emitter.ts +156 -0
  133. package/core/src/particles/index.ts +11 -0
  134. package/core/src/particles/particle-data.ts +47 -0
  135. package/core/src/particles/render-pass.ts +365 -0
  136. package/core/src/particles/simulator.ts +264 -0
  137. package/core/src/particles/system.ts +193 -0
  138. package/core/src/physics/body.ts +59 -0
  139. package/core/src/physics/ccd-heuristic.ts +55 -0
  140. package/core/src/physics/character.ts +282 -0
  141. package/core/src/physics/collider.ts +55 -0
  142. package/core/src/physics/collision-system.ts +91 -0
  143. package/core/src/physics/events.ts +133 -0
  144. package/core/src/physics/index.ts +34 -0
  145. package/core/src/physics/interface.ts +473 -0
  146. package/core/src/physics/interpolation-buffer.ts +192 -0
  147. package/core/src/physics/lifecycle.ts +87 -0
  148. package/core/src/physics/load-shedder.ts +164 -0
  149. package/core/src/physics/physics-accumulator.ts +101 -0
  150. package/core/src/physics/physics-system.ts +240 -0
  151. package/core/src/physics/ragdoll-presets.ts +272 -0
  152. package/core/src/physics/ragdoll-system.ts +220 -0
  153. package/core/src/physics/ragdoll.ts +182 -0
  154. package/core/src/physics/raycast.ts +143 -0
  155. package/core/src/physics/realm-manager.ts +499 -0
  156. package/core/src/physics/realm-worker-pool.ts +191 -0
  157. package/core/src/physics/realm-worker.ts +102 -0
  158. package/core/src/physics/realm.ts +189 -0
  159. package/core/src/physics/registry.ts +52 -0
  160. package/core/src/physics/safety.ts +247 -0
  161. package/core/src/physics/snapshot-manager.ts +141 -0
  162. package/core/src/physics/worker-protocol.ts +30 -0
  163. package/core/src/platform/bun-preload.ts +169 -0
  164. package/core/src/platform/css-stub.ts +4 -0
  165. package/core/src/platform/glob-polyfill.ts +124 -0
  166. package/core/src/platform/hidpi.ts +57 -0
  167. package/core/src/platform/render-surface.ts +77 -0
  168. package/core/src/platform/rpc.ts +98 -0
  169. package/core/src/platform/runtime.ts +132 -0
  170. package/core/src/plugin/context.ts +364 -0
  171. package/core/src/plugin/diagnostics.ts +61 -0
  172. package/core/src/plugin/discovery.ts +186 -0
  173. package/core/src/plugin/extension-loaders.ts +172 -0
  174. package/core/src/plugin/host.ts +659 -0
  175. package/core/src/plugin/index.ts +106 -0
  176. package/core/src/plugin/loader-asset.ts +124 -0
  177. package/core/src/plugin/loader-quickjs.ts +84 -0
  178. package/core/src/plugin/loader-wasm.ts +411 -0
  179. package/core/src/plugin/loader-worker.ts +261 -0
  180. package/core/src/plugin/manifest.ts +653 -0
  181. package/core/src/plugin/material-registry.ts +65 -0
  182. package/core/src/plugin/mcp-tools.ts +165 -0
  183. package/core/src/plugin/permissions.ts +175 -0
  184. package/core/src/plugin/quickjs-bridge.ts +294 -0
  185. package/core/src/plugin/registry.ts +107 -0
  186. package/core/src/plugin/sandbox-shim.ts +86 -0
  187. package/core/src/plugin/sandbox-worker.ts +215 -0
  188. package/core/src/plugin/wasm-abi.ts +152 -0
  189. package/core/src/plugin/wasm-worker.ts +229 -0
  190. package/core/src/plugin/workshop.ts +169 -0
  191. package/core/src/profiling/event-loop.ts +180 -0
  192. package/core/src/profiling/index.ts +69 -0
  193. package/core/src/profiling/iops/idb-patch.ts +242 -0
  194. package/core/src/profiling/iops/opfs-patch.ts +276 -0
  195. package/core/src/profiling/profiling-sab.ts +839 -0
  196. package/core/src/profiling/task-latency.ts +134 -0
  197. package/core/src/profiling/thread-metrics.ts +126 -0
  198. package/core/src/profiling/trace-event-writer.ts +205 -0
  199. package/core/src/profiling/warnings.ts +423 -0
  200. package/core/src/profiling/worker-prelude.ts +376 -0
  201. package/core/src/render/bind-group.ts +38 -0
  202. package/core/src/render/bindless/bindless-struct.ts +42 -0
  203. package/core/src/render/bindless/bindless.wgsl.ts +167 -0
  204. package/core/src/render/bindless/frame-bindings.ts +176 -0
  205. package/core/src/render/bindless/index.ts +10 -0
  206. package/core/src/render/bindless/material-manager.ts +257 -0
  207. package/core/src/render/bindless/texture-registry.ts +609 -0
  208. package/core/src/render/buffer.ts +82 -0
  209. package/core/src/render/camera-2d.ts +217 -0
  210. package/core/src/render/camera-controller.ts +440 -0
  211. package/core/src/render/camera.ts +409 -0
  212. package/core/src/render/canvas-resize-watcher.ts +46 -0
  213. package/core/src/render/compute-kernel.ts +172 -0
  214. package/core/src/render/constants.ts +2 -0
  215. package/core/src/render/device.ts +111 -0
  216. package/core/src/render/frame-graph.ts +768 -0
  217. package/core/src/render/frustum.ts +185 -0
  218. package/core/src/render/fullscreen-vs.ts +25 -0
  219. package/core/src/render/g-buffer.ts +186 -0
  220. package/core/src/render/game-renderer.ts +1452 -0
  221. package/core/src/render/gpu-driven/cull-pass.ts +394 -0
  222. package/core/src/render/gpu-driven/hzb.ts +215 -0
  223. package/core/src/render/gpu-driven/index.ts +7 -0
  224. package/core/src/render/gpu-driven/indirect-draw-pass.ts +324 -0
  225. package/core/src/render/gpu-driven/instance-buffer.ts +178 -0
  226. package/core/src/render/gpu-driven/mesh-table.ts +175 -0
  227. package/core/src/render/gpu-driven/passes/hzb-build-pass.ts +47 -0
  228. package/core/src/render/gpu-utils.ts +96 -0
  229. package/core/src/render/hdr.ts +88 -0
  230. package/core/src/render/hot-reload.ts +295 -0
  231. package/core/src/render/ibl-bind-group.ts +266 -0
  232. package/core/src/render/ibl.ts +180 -0
  233. package/core/src/render/index.ts +163 -0
  234. package/core/src/render/input-manager.ts +259 -0
  235. package/core/src/render/lighting/area-light.ts +196 -0
  236. package/core/src/render/lighting/cluster-grid.ts +288 -0
  237. package/core/src/render/lighting/cluster-types.ts +193 -0
  238. package/core/src/render/lighting/ies-parser.ts +162 -0
  239. package/core/src/render/lighting.ts +454 -0
  240. package/core/src/render/material-types/depth.wgsl +44 -0
  241. package/core/src/render/material-types/line.wgsl +50 -0
  242. package/core/src/render/material-types/matcap.wgsl +51 -0
  243. package/core/src/render/material-types/normal.wgsl +41 -0
  244. package/core/src/render/material-types/physical.wgsl +152 -0
  245. package/core/src/render/material-types/sprite.wgsl +46 -0
  246. package/core/src/render/material-types/sss.wgsl +79 -0
  247. package/core/src/render/material-types/toon.wgsl +79 -0
  248. package/core/src/render/passes/README.md +121 -0
  249. package/core/src/render/passes/atmosphere.ts +152 -0
  250. package/core/src/render/passes/cluster-lighting-pass.ts +476 -0
  251. package/core/src/render/passes/csm.ts +408 -0
  252. package/core/src/render/passes/cubemap-capture.ts +114 -0
  253. package/core/src/render/passes/debug-viz.ts +278 -0
  254. package/core/src/render/passes/debug.ts +538 -0
  255. package/core/src/render/passes/decal-mesh.ts +111 -0
  256. package/core/src/render/passes/decal-pass.ts +377 -0
  257. package/core/src/render/passes/deferred-lighting.ts +434 -0
  258. package/core/src/render/passes/depth-prepass.ts +163 -0
  259. package/core/src/render/passes/dome-360.ts +83 -0
  260. package/core/src/render/passes/fluid-render.ts +137 -0
  261. package/core/src/render/passes/gi-types.ts +118 -0
  262. package/core/src/render/passes/graph-compute.ts +300 -0
  263. package/core/src/render/passes/greased-line-pass.ts +232 -0
  264. package/core/src/render/passes/morph-target.ts +40 -0
  265. package/core/src/render/passes/opaque.ts +1028 -0
  266. package/core/src/render/passes/point-light-shadow.ts +266 -0
  267. package/core/src/render/passes/procedural-texture.ts +185 -0
  268. package/core/src/render/passes/reflection-probe.ts +140 -0
  269. package/core/src/render/passes/rsm-pass.ts +248 -0
  270. package/core/src/render/passes/sdf-text.ts +155 -0
  271. package/core/src/render/passes/shadow-map.ts +163 -0
  272. package/core/src/render/passes/shadow.ts +220 -0
  273. package/core/src/render/passes/skinning-vs.ts +121 -0
  274. package/core/src/render/passes/skinning.ts +326 -0
  275. package/core/src/render/passes/sky-dome.ts +404 -0
  276. package/core/src/render/passes/skybox.ts +162 -0
  277. package/core/src/render/passes/spot-light-shadow.ts +273 -0
  278. package/core/src/render/passes/terrain.ts +326 -0
  279. package/core/src/render/passes/transparent.ts +353 -0
  280. package/core/src/render/passes/underwater-fog.ts +185 -0
  281. package/core/src/render/passes/video-texture.ts +79 -0
  282. package/core/src/render/passes/volumetric-pass.ts +312 -0
  283. package/core/src/render/passes/volumetric-types.ts +171 -0
  284. package/core/src/render/passes/water.ts +753 -0
  285. package/core/src/render/pbr.ts +296 -0
  286. package/core/src/render/pipeline.ts +52 -0
  287. package/core/src/render/render-pass.ts +30 -0
  288. package/core/src/render/renderer-input-bus.ts +220 -0
  289. package/core/src/render/renderer-module-host.ts +623 -0
  290. package/core/src/render/resource-tracker.ts +67 -0
  291. package/core/src/render/sab-canvas-overlay.ts +122 -0
  292. package/core/src/render/shader-validator.ts +200 -0
  293. package/core/src/render/shaders/debug/debug-line.wgsl +27 -0
  294. package/core/src/render/shaders/debug/debug-point.wgsl +30 -0
  295. package/core/src/render/shaders/debug/debug-text.wgsl +42 -0
  296. package/core/src/render/shaders/debug-viz/aabb.wgsl +20 -0
  297. package/core/src/render/shaders/debug-viz/depth.wgsl +29 -0
  298. package/core/src/render/shaders/debug-viz/lod.wgsl +38 -0
  299. package/core/src/render/shaders/debug-viz/normals.wgsl +30 -0
  300. package/core/src/render/shaders/debug-viz/overdraw.wgsl +21 -0
  301. package/core/src/render/shaders/debug-viz/tangents.wgsl +29 -0
  302. package/core/src/render/shaders/debug-viz/wireframe.wgsl +33 -0
  303. package/core/src/render/shaders/shadow-chunks.ts +135 -0
  304. package/core/src/render/skybox.ts +146 -0
  305. package/core/src/render/splitscreen.ts +85 -0
  306. package/core/src/render/surface.ts +78 -0
  307. package/core/src/render/tonemap.ts +180 -0
  308. package/core/src/render/tracked-render-pass.ts +158 -0
  309. package/core/src/render/viewport.ts +109 -0
  310. package/core/src/render/wgsl-hmr.ts +70 -0
  311. package/core/src/sab/core-input-channel.ts +23 -0
  312. package/core/src/sab/define.ts +239 -0
  313. package/core/src/sab/errors.ts +76 -0
  314. package/core/src/sab/game-input.ts +343 -0
  315. package/core/src/sab/gamepad-devices.ts +259 -0
  316. package/core/src/sab/grid-buffer.ts +117 -0
  317. package/core/src/sab/input.ts +55 -0
  318. package/core/src/sab/layout.ts +219 -0
  319. package/core/src/sab/multi-input-channel.ts +50 -0
  320. package/core/src/sab/record-buffer.ts +155 -0
  321. package/core/src/sab/sab-polyfill.ts +56 -0
  322. package/core/src/sab/sim-channel.ts +453 -0
  323. package/core/src/sab/slot-buffer.ts +190 -0
  324. package/core/src/sab/types.ts +158 -0
  325. package/core/src/safety/bounds.ts +76 -0
  326. package/core/src/safety/json.ts +80 -0
  327. package/core/src/safety/path.ts +167 -0
  328. package/core/src/save/binary-format.ts +117 -0
  329. package/core/src/save/game-save-system.ts +161 -0
  330. package/core/src/save/grid-save-system.ts +158 -0
  331. package/core/src/save/migrate.ts +44 -0
  332. package/core/src/save/migration-registry.ts +94 -0
  333. package/core/src/save/persist-types.ts +208 -0
  334. package/core/src/save/schema.ts +46 -0
  335. package/core/src/save/serializer.ts +180 -0
  336. package/core/src/scene/builder.ts +65 -0
  337. package/core/src/scene/camera.ts +142 -0
  338. package/core/src/scene/checkpoint.ts +109 -0
  339. package/core/src/scene/game-loop.ts +198 -0
  340. package/core/src/scene/index.ts +24 -0
  341. package/core/src/scene/layer.ts +6 -0
  342. package/core/src/scene/prefab.ts +145 -0
  343. package/core/src/scene/scene-manager.ts +157 -0
  344. package/core/src/scene/scene.ts +192 -0
  345. package/core/src/scene/sky.ts +256 -0
  346. package/core/src/scene/spatial-grid.ts +176 -0
  347. package/core/src/scene/streaming.ts +304 -0
  348. package/core/src/scene/vegetation.ts +163 -0
  349. package/core/src/scene/world.ts +89 -0
  350. package/core/src/scripting/binding.ts +129 -0
  351. package/core/src/scripting/hot-reload.ts +144 -0
  352. package/core/src/scripting/script.ts +99 -0
  353. package/core/src/sim/hot-reload-pipeline.ts +49 -0
  354. package/core/src/sim/sim-state-helper.ts +47 -0
  355. package/core/src/sim/transient-state-registry.ts +77 -0
  356. package/core/src/sim/types.ts +113 -0
  357. package/core/src/sim/worker-loop.ts +222 -0
  358. package/core/src/stores/game-store.ts +158 -0
  359. package/core/src/stores/hot-reload-store.ts +44 -0
  360. package/core/src/telemetry/collector.ts +410 -0
  361. package/core/src/telemetry/gc-controller.ts +453 -0
  362. package/core/src/telemetry/gc-profiler.ts +126 -0
  363. package/core/src/telemetry/gc-tracker.ts +69 -0
  364. package/core/src/telemetry/gpu-profiler.ts +703 -0
  365. package/core/src/telemetry/gpu-resource-tracker.ts +285 -0
  366. package/core/src/telemetry/gpu-timer-pool.ts +234 -0
  367. package/core/src/telemetry/gpu-timer.ts +123 -0
  368. package/core/src/telemetry/gpu-timestamp-gate.ts +21 -0
  369. package/core/src/telemetry/reporter.ts +62 -0
  370. package/core/src/types/engine-types.ts +129 -0
  371. package/core/src/ui-font-scale.ts +112 -0
  372. package/core/src/util/broad-phase-grid.ts +82 -0
  373. package/core/src/util/feature-log.ts +220 -0
  374. package/core/src/util/index.ts +17 -0
  375. package/core/src/util/logger.ts +604 -0
  376. package/core/src/webgpu-destroy-augmentation.d.ts +25 -0
  377. package/core/src/worker/base-worker-host.ts +183 -0
  378. package/core/src/worker/buffer-sync.ts +550 -0
  379. package/core/src/worker/crash-recovery.ts +201 -0
  380. package/core/src/worker/entity-sim-worker-host.ts +190 -0
  381. package/core/src/worker/instrumented-worker-host.ts +171 -0
  382. package/core/src/worker/rpc.ts +253 -0
  383. package/core/src/worker/sim-worker-base.ts +913 -0
  384. package/core/src/worker/sim-worker-host.ts +358 -0
  385. package/core/src/worker/task-pool.ts +256 -0
  386. package/core/src/worker/task-worker.ts +83 -0
  387. package/libraries/asset-browser/src/index.ts +11 -0
  388. package/libraries/asset-browser/src/software-thumbnails.ts +949 -0
  389. package/libraries/audio-kira/README.md +17 -0
  390. package/libraries/audio-kira/native/Cargo.toml +14 -0
  391. package/libraries/audio-kira/native/src/engine.rs +27 -0
  392. package/libraries/audio-kira/native/src/lib.rs +185 -0
  393. package/libraries/audio-kira/native/src/mixer.rs +11 -0
  394. package/libraries/audio-kira/native/src/source.rs +80 -0
  395. package/libraries/audio-kira/src/backend.ts +469 -0
  396. package/libraries/audio-kira/src/index.ts +8 -0
  397. package/libraries/audio-kira/src/library.ts +82 -0
  398. package/libraries/blitz-ui/native/Cargo.toml +33 -0
  399. package/libraries/blitz-ui/native/assets/DejaVuSans.woff2 +0 -0
  400. package/libraries/blitz-ui/native/src/bridge.rs +100 -0
  401. package/libraries/blitz-ui/native/src/fonts.rs +20 -0
  402. package/libraries/blitz-ui/native/src/input.rs +80 -0
  403. package/libraries/blitz-ui/native/src/lib.rs +96 -0
  404. package/libraries/blitz-ui/native/src/shell.rs +301 -0
  405. package/libraries/blitz-ui/native-osr/Cargo.toml +24 -0
  406. package/libraries/blitz-ui/native-osr/src/events.rs +237 -0
  407. package/libraries/blitz-ui/native-osr/src/lib.rs +846 -0
  408. package/libraries/blitz-ui/native-osr/src/mutate.rs +385 -0
  409. package/libraries/blitz-ui/native-osr/src/resources.rs +45 -0
  410. package/libraries/blitz-ui/native-osr-spike.ts +70 -0
  411. package/libraries/blitz-ui/src/index.ts +15 -0
  412. package/libraries/blitz-ui/src/native-module.ts +221 -0
  413. package/libraries/blitz-ui/src/native-osr-ffi.ts +423 -0
  414. package/libraries/blitz-ui/src/router.ts +101 -0
  415. package/libraries/blitz-ui/src/shaders/ui-blit.wgsl.ts +35 -0
  416. package/libraries/blitz-ui/src/ui-blit-pass.ts +116 -0
  417. package/libraries/character/README.md +25 -0
  418. package/libraries/character/src/character-animator.ts +374 -0
  419. package/libraries/character/src/index.ts +24 -0
  420. package/libraries/character/src/loader.ts +375 -0
  421. package/libraries/character/src/locomotion.ts +124 -0
  422. package/libraries/character/src/preview.ts +287 -0
  423. package/libraries/devtools/README.md +51 -0
  424. package/libraries/devtools/native/Cargo.toml +12 -0
  425. package/libraries/devtools/native/src/lib.rs +1646 -0
  426. package/libraries/devtools/native/src/panels/console.rs +338 -0
  427. package/libraries/devtools/native/src/panels/dom_tree.rs +47 -0
  428. package/libraries/devtools/native/src/panels/generic.rs +317 -0
  429. package/libraries/devtools/native/src/panels/gpu.rs +107 -0
  430. package/libraries/devtools/native/src/panels/input.rs +88 -0
  431. package/libraries/devtools/native/src/panels/mod.rs +154 -0
  432. package/libraries/devtools/native/src/panels/perf_metrics.rs +91 -0
  433. package/libraries/devtools/native/src/panels/perf_recorder.rs +158 -0
  434. package/libraries/devtools/native/src/panels/scene.rs +72 -0
  435. package/libraries/devtools/native/src/state.rs +394 -0
  436. package/libraries/devtools/src/cdp-bridge.ts +349 -0
  437. package/libraries/devtools/src/collectors.ts +346 -0
  438. package/libraries/devtools/src/egui-ffi.ts +804 -0
  439. package/libraries/devtools/src/egui-renderer.ts +407 -0
  440. package/libraries/devtools/src/host.ts +363 -0
  441. package/libraries/devtools/src/index.ts +40 -0
  442. package/libraries/devtools/src/mirror.ts +794 -0
  443. package/libraries/devtools/src/native-providers.ts +744 -0
  444. package/libraries/devtools/src/verify.ts +167 -0
  445. package/libraries/devtools/src/web/browser.ts +102 -0
  446. package/libraries/devtools/src/web/host.ts +212 -0
  447. package/libraries/devtools/src/web/mirror.ts +430 -0
  448. package/libraries/devtools/src/web/server.ts +198 -0
  449. package/libraries/entities/README.md +17 -0
  450. package/libraries/entities/src/index.ts +4 -0
  451. package/libraries/entities/src/library.ts +64 -0
  452. package/libraries/entities/src/model-renderer.ts +1345 -0
  453. package/libraries/entities/src/shaders/model.wgsl +375 -0
  454. package/libraries/gamepad/src/feed-input.ts +83 -0
  455. package/libraries/gamepad/src/filters.ts +49 -0
  456. package/libraries/gamepad/src/glyphs.ts +93 -0
  457. package/libraries/gamepad/src/hub.ts +206 -0
  458. package/libraries/gamepad/src/index.ts +22 -0
  459. package/libraries/gamepad/src/library.ts +68 -0
  460. package/libraries/gamepad/src/source-sab.ts +70 -0
  461. package/libraries/gamepad/src/sysfs-enrich.ts +138 -0
  462. package/libraries/gaussian-splats/README.md +25 -0
  463. package/libraries/gaussian-splats/src/gpu-sort.ts +595 -0
  464. package/libraries/gaussian-splats/src/index.ts +31 -0
  465. package/libraries/gaussian-splats/src/library.ts +60 -0
  466. package/libraries/gaussian-splats/src/parser.ts +382 -0
  467. package/libraries/gaussian-splats/src/renderer.ts +427 -0
  468. package/libraries/gaussian-splats/src/sh-eval.ts +211 -0
  469. package/libraries/gaussian-splats/src/sorter.ts +110 -0
  470. package/libraries/gaussian-splats/src/tile-raster.ts +473 -0
  471. package/libraries/gpu-kernels/README.md +154 -0
  472. package/libraries/gpu-kernels/src/index.ts +11 -0
  473. package/libraries/gpu-kernels/src/kernel.ts +696 -0
  474. package/libraries/gpu-kernels/src/transpile.ts +958 -0
  475. package/libraries/html-ui-kit/src/behaviors.ts +250 -0
  476. package/libraries/html-ui-kit/src/components.ts +309 -0
  477. package/libraries/html-ui-kit/src/index.ts +30 -0
  478. package/libraries/html-ui-kit/src/nav/nav-controller.ts +238 -0
  479. package/libraries/html-ui-kit/src/nav/nav-input.ts +145 -0
  480. package/libraries/html-ui-kit/src/nav/osk.ts +153 -0
  481. package/libraries/html-ui-kit/src/nav/router.ts +167 -0
  482. package/libraries/html-ui-kit/src/nav/vcursor.ts +203 -0
  483. package/libraries/html-ui-kit/src/ten-foot.ts +182 -0
  484. package/libraries/html-ui-kit/src/theme.ts +238 -0
  485. package/libraries/lighting/README.md +20 -0
  486. package/libraries/lighting/src/index.ts +5 -0
  487. package/libraries/lighting/src/library.ts +66 -0
  488. package/libraries/lighting/src/light-system.ts +452 -0
  489. package/libraries/lighting/src/lighting-system.ts +68 -0
  490. package/libraries/lighting/src/shaders/light-debug.wgsl +30 -0
  491. package/libraries/marching-cubes/README.md +25 -0
  492. package/libraries/marching-cubes/src/chunked-field.ts +177 -0
  493. package/libraries/marching-cubes/src/deformation.ts +63 -0
  494. package/libraries/marching-cubes/src/generator.ts +407 -0
  495. package/libraries/marching-cubes/src/index.ts +32 -0
  496. package/libraries/marching-cubes/src/library.ts +80 -0
  497. package/libraries/marching-cubes/src/lod.ts +122 -0
  498. package/libraries/marching-cubes/src/marching-tetrahedra.ts +252 -0
  499. package/libraries/marching-cubes/src/sab.ts +45 -0
  500. package/libraries/marching-cubes/src/streaming-config.ts +48 -0
  501. package/libraries/marching-cubes/src/streaming-manager.ts +686 -0
  502. package/libraries/marching-cubes/src/tables.ts +323 -0
  503. package/libraries/marching-cubes/src/types.ts +18 -0
  504. package/libraries/models/README.md +25 -0
  505. package/libraries/models/src/bake-node-transforms.ts +171 -0
  506. package/libraries/models/src/codecs/basisu-codec.ts +180 -0
  507. package/libraries/models/src/codecs/draco-codec.ts +372 -0
  508. package/libraries/models/src/codecs/draco3dgltf.d.ts +40 -0
  509. package/libraries/models/src/codecs/index.ts +22 -0
  510. package/libraries/models/src/codecs/meshopt-codec.ts +91 -0
  511. package/libraries/models/src/codecs/registry.ts +221 -0
  512. package/libraries/models/src/dae.ts +753 -0
  513. package/libraries/models/src/default-part-selection.ts +56 -0
  514. package/libraries/models/src/dxf.ts +200 -0
  515. package/libraries/models/src/fbx/index.ts +457 -0
  516. package/libraries/models/src/fbx/interpreter/animation.ts +431 -0
  517. package/libraries/models/src/fbx/interpreter/blend-shapes.ts +323 -0
  518. package/libraries/models/src/fbx/interpreter/connections.ts +166 -0
  519. package/libraries/models/src/fbx/interpreter/diagnostics.ts +65 -0
  520. package/libraries/models/src/fbx/interpreter/geometry.ts +535 -0
  521. package/libraries/models/src/fbx/interpreter/global-settings.ts +92 -0
  522. package/libraries/models/src/fbx/interpreter/materials.ts +265 -0
  523. package/libraries/models/src/fbx/interpreter/nodes.ts +197 -0
  524. package/libraries/models/src/fbx/interpreter/skeleton.ts +476 -0
  525. package/libraries/models/src/fbx/parsers/binary-parser.ts +244 -0
  526. package/libraries/models/src/fbx/types.ts +119 -0
  527. package/libraries/models/src/gltf-extensions.ts +196 -0
  528. package/libraries/models/src/gltf.ts +935 -0
  529. package/libraries/models/src/index.ts +87 -0
  530. package/libraries/models/src/library.ts +75 -0
  531. package/libraries/models/src/loader.ts +265 -0
  532. package/libraries/models/src/material-adapter.ts +108 -0
  533. package/libraries/models/src/mini-xml.ts +174 -0
  534. package/libraries/models/src/normalize.ts +216 -0
  535. package/libraries/models/src/obj.ts +264 -0
  536. package/libraries/models/src/off.ts +95 -0
  537. package/libraries/models/src/ply.ts +348 -0
  538. package/libraries/models/src/pose-sampler.ts +219 -0
  539. package/libraries/models/src/sidecar/blender-extras.ts +37 -0
  540. package/libraries/models/src/sidecar/ddmeta.ts +114 -0
  541. package/libraries/models/src/sidecar/godot-import.ts +86 -0
  542. package/libraries/models/src/sidecar/resolver.ts +145 -0
  543. package/libraries/models/src/sidecar/types.ts +11 -0
  544. package/libraries/models/src/sidecar/unity-meta.ts +61 -0
  545. package/libraries/models/src/skeleton-synthesis.ts +132 -0
  546. package/libraries/models/src/stl.ts +149 -0
  547. package/libraries/models/src/threeds.ts +363 -0
  548. package/libraries/models/src/threemf.ts +201 -0
  549. package/libraries/models/src/types.ts +188 -0
  550. package/libraries/models/src/vtk.ts +218 -0
  551. package/libraries/models/test-fixtures/fbx/LICENSE-notice.txt +24 -0
  552. package/libraries/models/test-fixtures/fbx/README.md +46 -0
  553. package/libraries/models/test-fixtures/fbx/m01_cube_phong.fbx +0 -0
  554. package/libraries/models/test-fixtures/fbx/m02_geo_ngons.fbx +0 -0
  555. package/libraries/models/test-fixtures/fbx/m03_normals.fbx +0 -0
  556. package/libraries/models/test-fixtures/fbx/m04_material_properties.fbx +0 -0
  557. package/libraries/models/test-fixtures/fbx/m05_external_diffuse.png +0 -0
  558. package/libraries/models/test-fixtures/fbx/m05_textures.fbx +0 -0
  559. package/libraries/models/test-fixtures/fbx/m06_uv_transform.fbx +0 -0
  560. package/libraries/models/test-fixtures/fbx/m07_multimaterial.fbx +0 -0
  561. package/libraries/models/test-fixtures/fbx/m08_transforms.fbx +0 -0
  562. package/libraries/models/test-fixtures/fbx/m09_skinning.fbx +0 -0
  563. package/libraries/models/test-fixtures/fbx/m10_morph.fbx +0 -0
  564. package/libraries/models/test-fixtures/fbx/m11_node_anim.fbx +0 -0
  565. package/libraries/models/test-fixtures/fbx/m12_skeletal_anim.fbx +0 -0
  566. package/libraries/models/test-fixtures/fbx/m13_morph_anim.fbx +0 -0
  567. package/libraries/models/test-fixtures/fbx/m14_multiclip.fbx +0 -0
  568. package/libraries/models/test-fixtures/fbx/m15_camera_lights.fbx +0 -0
  569. package/libraries/models/test-fixtures/fbx/m16_axis_yup.fbx +0 -0
  570. package/libraries/models/test-fixtures/fbx/m16_axis_zup.fbx +0 -0
  571. package/libraries/models/test-fixtures/fbx/m16_units_254.fbx +0 -0
  572. package/libraries/navmesh/README.md +23 -0
  573. package/libraries/navmesh/src/crowd-system.ts +240 -0
  574. package/libraries/navmesh/src/debug-viz.ts +74 -0
  575. package/libraries/navmesh/src/index.ts +15 -0
  576. package/libraries/navmesh/src/library.ts +64 -0
  577. package/libraries/navmesh/src/navmesh-generator.ts +407 -0
  578. package/libraries/navmesh/src/navmesh.ts +159 -0
  579. package/libraries/navmesh/src/pathfinder.ts +252 -0
  580. package/libraries/navmesh/src/types.ts +63 -0
  581. package/libraries/networking/README.md +25 -0
  582. package/libraries/networking/src/authority.ts +121 -0
  583. package/libraries/networking/src/connection.ts +139 -0
  584. package/libraries/networking/src/discovery.ts +373 -0
  585. package/libraries/networking/src/enhanced-replication.ts +355 -0
  586. package/libraries/networking/src/epic-adapter.ts +129 -0
  587. package/libraries/networking/src/index.ts +45 -0
  588. package/libraries/networking/src/library.ts +69 -0
  589. package/libraries/networking/src/lobby.ts +108 -0
  590. package/libraries/networking/src/platform-adapter.ts +163 -0
  591. package/libraries/networking/src/rcs-adapter.ts +127 -0
  592. package/libraries/networking/src/remote-input.ts +188 -0
  593. package/libraries/networking/src/replication.ts +327 -0
  594. package/libraries/networking/src/rpc.ts +188 -0
  595. package/libraries/networking/src/session.ts +173 -0
  596. package/libraries/networking/src/transport.ts +201 -0
  597. package/libraries/networking/src/webrtc.ts +323 -0
  598. package/libraries/pathfinding-2d/README.md +15 -0
  599. package/libraries/pathfinding-2d/src/astar-grid.ts +312 -0
  600. package/libraries/pathfinding-2d/src/index.ts +11 -0
  601. package/libraries/persistence/README.md +25 -0
  602. package/libraries/persistence/src/autosave-manager.ts +112 -0
  603. package/libraries/persistence/src/binary-record-store.ts +244 -0
  604. package/libraries/persistence/src/browser.ts +51 -0
  605. package/libraries/persistence/src/file-save-store.ts +553 -0
  606. package/libraries/persistence/src/hash-utils.ts +59 -0
  607. package/libraries/persistence/src/index.ts +57 -0
  608. package/libraries/persistence/src/library.ts +74 -0
  609. package/libraries/persistence/src/mock-opfs.ts +141 -0
  610. package/libraries/persistence/src/node-fs-directory-handle.ts +161 -0
  611. package/libraries/persistence/src/opfs-save-store.ts +745 -0
  612. package/libraries/persistence/src/save-worker-proxy.ts +127 -0
  613. package/libraries/persistence/src/save-worker.ts +99 -0
  614. package/libraries/persistence/src/xxh3-ts.d.ts +10 -0
  615. package/libraries/physics-native/README.md +21 -0
  616. package/libraries/physics-native/native/Cargo.toml +11 -0
  617. package/libraries/physics-native/native/src/body.rs +403 -0
  618. package/libraries/physics-native/native/src/character.rs +226 -0
  619. package/libraries/physics-native/native/src/collider.rs +299 -0
  620. package/libraries/physics-native/native/src/joint.rs +81 -0
  621. package/libraries/physics-native/native/src/lib.rs +830 -0
  622. package/libraries/physics-native/native/src/query.rs +261 -0
  623. package/libraries/physics-native/native/src/realm.rs +33 -0
  624. package/libraries/physics-native/native/src/step.rs +54 -0
  625. package/libraries/physics-native/native/src/world.rs +148 -0
  626. package/libraries/physics-native/src/backend.ts +1046 -0
  627. package/libraries/physics-native/src/broadphase.ts +101 -0
  628. package/libraries/physics-native/src/ffi-spike-worker.ts +71 -0
  629. package/libraries/physics-native/src/index.ts +11 -0
  630. package/libraries/physics-native/src/library.ts +79 -0
  631. package/libraries/physics-native/src/narrowphase-shapes.ts +420 -0
  632. package/libraries/physics-native/src/narrowphase.ts +123 -0
  633. package/libraries/physics-native/src/solver.ts +141 -0
  634. package/libraries/physics-native/src/types.ts +76 -0
  635. package/libraries/physics-rapier/README.md +21 -0
  636. package/libraries/physics-rapier/bench/ffi-bench.ts +79 -0
  637. package/libraries/physics-rapier/bench/wasm-baseline.ts +90 -0
  638. package/libraries/physics-rapier/src/api.ts +452 -0
  639. package/libraries/physics-rapier/src/auto-backend.ts +59 -0
  640. package/libraries/physics-rapier/src/backend.ts +1207 -0
  641. package/libraries/physics-rapier/src/bulk-ops.ts +68 -0
  642. package/libraries/physics-rapier/src/ffi-backend.ts +21 -0
  643. package/libraries/physics-rapier/src/ffi-lib.ts +597 -0
  644. package/libraries/physics-rapier/src/index.ts +12 -0
  645. package/libraries/physics-rapier/src/library.ts +107 -0
  646. package/libraries/physics-rapier/src/rapier-backend.ts +1052 -0
  647. package/libraries/physics-rapier/src/realm-worker-script.ts +237 -0
  648. package/libraries/postfx/README.md +17 -0
  649. package/libraries/postfx/src/index.ts +19 -0
  650. package/libraries/postfx/src/library.ts +337 -0
  651. package/libraries/postfx/src/post-process-stack.ts +1752 -0
  652. package/libraries/postfx/src/shaders/post-process/afterimage.wgsl +14 -0
  653. package/libraries/postfx/src/shaders/post-process/ascii.wgsl +24 -0
  654. package/libraries/postfx/src/shaders/post-process/blit.wgsl +12 -0
  655. package/libraries/postfx/src/shaders/post-process/bloom-blur.wgsl +21 -0
  656. package/libraries/postfx/src/shaders/post-process/bloom-bright.wgsl +15 -0
  657. package/libraries/postfx/src/shaders/post-process/bloom-composite.wgsl +14 -0
  658. package/libraries/postfx/src/shaders/post-process/bloom-downsample.wgsl +60 -0
  659. package/libraries/postfx/src/shaders/post-process/bloom-soft.wgsl +46 -0
  660. package/libraries/postfx/src/shaders/post-process/bloom-upsample.wgsl +51 -0
  661. package/libraries/postfx/src/shaders/post-process/channel-mixer.wgsl +36 -0
  662. package/libraries/postfx/src/shaders/post-process/chromatic-aberration.wgsl +37 -0
  663. package/libraries/postfx/src/shaders/post-process/dithering.wgsl +64 -0
  664. package/libraries/postfx/src/shaders/post-process/dof.wgsl +131 -0
  665. package/libraries/postfx/src/shaders/post-process/edges.wgsl +45 -0
  666. package/libraries/postfx/src/shaders/post-process/fullscreen-vs.wgsl +15 -0
  667. package/libraries/postfx/src/shaders/post-process/fxaa.wgsl +102 -0
  668. package/libraries/postfx/src/shaders/post-process/gaussian-blur.wgsl +37 -0
  669. package/libraries/postfx/src/shaders/post-process/glow-blur.wgsl +33 -0
  670. package/libraries/postfx/src/shaders/post-process/glow-composite.wgsl +25 -0
  671. package/libraries/postfx/src/shaders/post-process/grain.wgsl +37 -0
  672. package/libraries/postfx/src/shaders/post-process/halftone.wgsl +80 -0
  673. package/libraries/postfx/src/shaders/post-process/highlight-blur.wgsl +33 -0
  674. package/libraries/postfx/src/shaders/post-process/highlight-composite.wgsl +32 -0
  675. package/libraries/postfx/src/shaders/post-process/lens-distortion.wgsl +43 -0
  676. package/libraries/postfx/src/shaders/post-process/lens-flare.wgsl +68 -0
  677. package/libraries/postfx/src/shaders/post-process/lut.wgsl +30 -0
  678. package/libraries/postfx/src/shaders/post-process/motion-blur.wgsl +55 -0
  679. package/libraries/postfx/src/shaders/post-process/occluder-chunk.wgsl +28 -0
  680. package/libraries/postfx/src/shaders/post-process/outline.wgsl +51 -0
  681. package/libraries/postfx/src/shaders/post-process/pixelation.wgsl +47 -0
  682. package/libraries/postfx/src/shaders/post-process/sharpen.wgsl +32 -0
  683. package/libraries/postfx/src/shaders/post-process/sobel.wgsl +26 -0
  684. package/libraries/postfx/src/shaders/post-process/split-tone.wgsl +36 -0
  685. package/libraries/postfx/src/shaders/post-process/ssao-blur.wgsl +38 -0
  686. package/libraries/postfx/src/shaders/post-process/ssao-composite.wgsl +25 -0
  687. package/libraries/postfx/src/shaders/post-process/ssao.wgsl +147 -0
  688. package/libraries/postfx/src/shaders/post-process/ssr.wgsl +155 -0
  689. package/libraries/postfx/src/shaders/post-process/taa.wgsl +98 -0
  690. package/libraries/postfx/src/shaders/post-process/tonemap.wgsl +40 -0
  691. package/libraries/postfx/src/shaders/post-process/watercolor.wgsl +82 -0
  692. package/libraries/postfx/src/shaders/post-process/white-balance.wgsl +41 -0
  693. package/libraries/recast/README.md +21 -0
  694. package/libraries/recast/src/backend.ts +150 -0
  695. package/libraries/recast/src/crowd-system.ts +352 -0
  696. package/libraries/recast/src/index.ts +12 -0
  697. package/libraries/recast/src/library.ts +91 -0
  698. package/libraries/recast/src/types.ts +86 -0
  699. package/libraries/sand/README.md +25 -0
  700. package/libraries/sand/src/cell.ts +43 -0
  701. package/libraries/sand/src/fields.ts +29 -0
  702. package/libraries/sand/src/fluid-grid.ts +261 -0
  703. package/libraries/sand/src/grid-dims.ts +74 -0
  704. package/libraries/sand/src/grid-sim-buffer.ts +271 -0
  705. package/libraries/sand/src/index.ts +81 -0
  706. package/libraries/sand/src/library.ts +92 -0
  707. package/libraries/sand/src/materials.ts +492 -0
  708. package/libraries/sand/src/palette.ts +93 -0
  709. package/libraries/sand/src/particles.ts +185 -0
  710. package/libraries/sand/src/render/sand-grid-pass.ts +430 -0
  711. package/libraries/sand/src/rng.ts +51 -0
  712. package/libraries/sand/src/rules/builtin-rules.ts +132 -0
  713. package/libraries/sand/src/rules/rule-compiler.ts +162 -0
  714. package/libraries/sand/src/rules/rule-engine.ts +231 -0
  715. package/libraries/sand/src/rules/rule-types.ts +87 -0
  716. package/libraries/sand/src/sand-step-pool.ts +484 -0
  717. package/libraries/sand/src/sand-step-worker.ts +97 -0
  718. package/libraries/sand/src/sand-world.ts +3227 -0
  719. package/libraries/stickman/README.md +25 -0
  720. package/libraries/stickman/src/index.ts +25 -0
  721. package/libraries/stickman/src/library.ts +61 -0
  722. package/libraries/stickman/src/proportions.ts +45 -0
  723. package/libraries/stickman/src/shader.ts +117 -0
  724. package/libraries/stickman/src/skeleton.ts +197 -0
  725. package/libraries/stickman/src/stickman-pass.ts +255 -0
  726. package/libraries/stickman/src/thick-line.ts +123 -0
  727. package/libraries/surface-nets/README.md +25 -0
  728. package/libraries/surface-nets/src/chunked-field.ts +14 -0
  729. package/libraries/surface-nets/src/deformation.ts +45 -0
  730. package/libraries/surface-nets/src/index.ts +36 -0
  731. package/libraries/surface-nets/src/library.ts +59 -0
  732. package/libraries/surface-nets/src/lod.ts +7 -0
  733. package/libraries/surface-nets/src/sab.ts +4 -0
  734. package/libraries/surface-nets/src/streaming-config.ts +5 -0
  735. package/libraries/surface-nets/src/streaming-manager.ts +15 -0
  736. package/libraries/surface-nets/src/surface-nets.ts +334 -0
  737. package/libraries/surface-nets/src/types.ts +23 -0
  738. package/libraries/water/README.md +25 -0
  739. package/libraries/water/src/buoyancy.ts +103 -0
  740. package/libraries/water/src/index.ts +43 -0
  741. package/libraries/water/src/library.ts +85 -0
  742. package/libraries/water/src/shore-damping.ts +93 -0
  743. package/libraries/water/src/water-buffer.ts +89 -0
  744. package/libraries/water/src/water-chunks.ts +13 -0
  745. package/libraries/water/src/water-physics.ts +376 -0
  746. package/libraries/water/src/water-renderer.ts +809 -0
  747. package/libraries/water/src/water-sab.ts +161 -0
  748. package/libraries/water/src/wave-sources.ts +84 -0
  749. package/libraries/weather/README.md +20 -0
  750. package/libraries/weather/src/index.ts +9 -0
  751. package/libraries/weather/src/library.ts +72 -0
  752. package/libraries/weather/src/types.ts +64 -0
  753. package/libraries/weather/src/weather-blend.ts +112 -0
  754. package/libraries/weather/src/weather-system.ts +271 -0
  755. package/libraries/weatherfx/README.md +24 -0
  756. package/libraries/weatherfx/src/cloud-provider.ts +31 -0
  757. package/libraries/weatherfx/src/cloud-system.ts +372 -0
  758. package/libraries/weatherfx/src/index.ts +9 -0
  759. package/libraries/weatherfx/src/library.ts +58 -0
  760. package/libraries/weatherfx/src/particle-system.ts +434 -0
  761. package/libraries/weatherfx/src/shaders/cloud.wgsl +111 -0
  762. package/libraries/weatherfx/src/shaders/particle-compute.wgsl +187 -0
  763. package/libraries/weatherfx/src/shaders/particle-render.wgsl +95 -0
  764. package/lint-plugin.mjs +41 -0
  765. package/mcp/src/client.ts +703 -0
  766. package/mcp/src/engine-context.ts +186 -0
  767. package/mcp/src/http-transport.ts +471 -0
  768. package/mcp/src/index.ts +13 -0
  769. package/mcp/src/prompts/add-entity.ts +42 -0
  770. package/mcp/src/prompts/create-scene.ts +44 -0
  771. package/mcp/src/prompts/debug-frame.ts +39 -0
  772. package/mcp/src/prompts/index.ts +12 -0
  773. package/mcp/src/resources/asset-list.ts +32 -0
  774. package/mcp/src/resources/checkpoint-list.ts +33 -0
  775. package/mcp/src/resources/entity-state.ts +55 -0
  776. package/mcp/src/resources/gpu-info.ts +103 -0
  777. package/mcp/src/resources/index.ts +57 -0
  778. package/mcp/src/resources/performance.ts +37 -0
  779. package/mcp/src/resources/scene-tree.ts +43 -0
  780. package/mcp/src/server.ts +375 -0
  781. package/mcp/src/tools/animation.ts +118 -0
  782. package/mcp/src/tools/asset.ts +161 -0
  783. package/mcp/src/tools/audio.ts +96 -0
  784. package/mcp/src/tools/build.ts +143 -0
  785. package/mcp/src/tools/camera.ts +167 -0
  786. package/mcp/src/tools/checkpoint.ts +100 -0
  787. package/mcp/src/tools/component.ts +199 -0
  788. package/mcp/src/tools/debug.ts +300 -0
  789. package/mcp/src/tools/entity.ts +361 -0
  790. package/mcp/src/tools/inspect.ts +210 -0
  791. package/mcp/src/tools/lighting.ts +263 -0
  792. package/mcp/src/tools/material.ts +243 -0
  793. package/mcp/src/tools/mesh.ts +239 -0
  794. package/mcp/src/tools/physics.ts +266 -0
  795. package/mcp/src/tools/scene.ts +153 -0
  796. package/mcp/src/tools/script.ts +122 -0
  797. package/mcp/src/types.ts +92 -0
  798. package/mcp/src/undo-redo.ts +77 -0
  799. package/modules/camera-controls/README.md +20 -0
  800. package/modules/camera-controls/src/index.ts +161 -0
  801. package/modules/controller-ui/src/index.ts +164 -0
  802. package/modules/devtools/README.md +25 -0
  803. package/modules/devtools/src/api.ts +461 -0
  804. package/modules/devtools/src/asset-utils.ts +105 -0
  805. package/modules/devtools/src/bind-debug-store.ts +48 -0
  806. package/modules/devtools/src/data-bridge.ts +286 -0
  807. package/modules/devtools/src/debug-overlay.ts +238 -0
  808. package/modules/devtools/src/debug-raycast.ts +328 -0
  809. package/modules/devtools/src/debug-store.ts +92 -0
  810. package/modules/devtools/src/debug-view-descriptors.ts +59 -0
  811. package/modules/devtools/src/doctor-panel.ts +182 -0
  812. package/modules/devtools/src/gizmo-module.ts +136 -0
  813. package/modules/devtools/src/graph-model.ts +509 -0
  814. package/modules/devtools/src/grid-renderer.ts +157 -0
  815. package/modules/devtools/src/height-ruler-renderer.ts +216 -0
  816. package/modules/devtools/src/index.ts +81 -0
  817. package/modules/devtools/src/init.ts +261 -0
  818. package/modules/devtools/src/label-overlay.ts +154 -0
  819. package/modules/devtools/src/material-stats-panel.ts +188 -0
  820. package/modules/devtools/src/profiling-bridge.ts +377 -0
  821. package/modules/devtools/src/profiling-hooks.ts +98 -0
  822. package/modules/devtools/src/renderer-adapter.ts +76 -0
  823. package/modules/devtools/src/scene-inspector.ts +637 -0
  824. package/modules/devtools/src/scene-store.ts +320 -0
  825. package/modules/devtools/src/scene-sync.ts +116 -0
  826. package/modules/devtools/src/shaders/transform-gizmo.wgsl +38 -0
  827. package/modules/devtools/src/sim-stats-panel.ts +272 -0
  828. package/modules/devtools/src/sim-stats-provider.ts +105 -0
  829. package/modules/devtools/src/skeleton-renderer.ts +351 -0
  830. package/modules/devtools/src/transform-gizmo.ts +928 -0
  831. package/modules/devtools/src/types.ts +290 -0
  832. package/modules/devtools/src/worker-expose.ts +67 -0
  833. package/modules/devtools/src/worker-sync.ts +137 -0
  834. package/modules/editor/src/commands/component.ts +156 -0
  835. package/modules/editor/src/commands/entity.ts +287 -0
  836. package/modules/editor/src/commands/hierarchy.ts +83 -0
  837. package/modules/editor/src/commands/index.ts +61 -0
  838. package/modules/editor/src/commands/prefab.ts +163 -0
  839. package/modules/editor/src/commands/registry.ts +331 -0
  840. package/modules/editor/src/commands/scene-io.ts +211 -0
  841. package/modules/editor/src/commands/selection.ts +73 -0
  842. package/modules/editor/src/commands/transform.ts +91 -0
  843. package/modules/editor/src/ddscene.ts +267 -0
  844. package/modules/editor/src/document.ts +123 -0
  845. package/modules/editor/src/editor-context.ts +172 -0
  846. package/modules/editor/src/index.ts +41 -0
  847. package/modules/editor/src/mcp.ts +103 -0
  848. package/modules/editor/src/module.ts +148 -0
  849. package/modules/editor/src/selection.ts +70 -0
  850. package/modules/editor/src/shell.ts +456 -0
  851. package/modules/editor/src/viewport.ts +199 -0
  852. package/modules/html-ui/src/composite.ts +135 -0
  853. package/modules/html-ui/src/create-html-ui.ts +188 -0
  854. package/modules/html-ui/src/doc-backend.ts +343 -0
  855. package/modules/html-ui/src/host.ts +646 -0
  856. package/modules/html-ui/src/index.ts +13 -0
  857. package/modules/html-ui/src/jsx-dev-runtime.ts +12 -0
  858. package/modules/html-ui/src/jsx-runtime.ts +126 -0
  859. package/modules/html-ui/src/protocol.ts +88 -0
  860. package/modules/html-ui/src/ui-worker.ts +16 -0
  861. package/modules/mcp/README.md +18 -0
  862. package/modules/mcp/src/index.ts +27 -0
  863. package/modules/movement-2d/README.md +19 -0
  864. package/modules/movement-2d/src/grid-character.ts +455 -0
  865. package/modules/movement-2d/src/index.ts +45 -0
  866. package/modules/movement-3d/README.md +21 -0
  867. package/modules/movement-3d/src/character-motor.ts +230 -0
  868. package/modules/movement-3d/src/index.ts +46 -0
  869. package/modules/native-osr/src/index.ts +22 -0
  870. package/modules/native-osr/src/input-router.ts +401 -0
  871. package/modules/native-osr/src/native-osr-manager.ts +247 -0
  872. package/modules/native-osr/src/native-texture-source.ts +86 -0
  873. package/modules/native-osr/src/types.ts +112 -0
  874. package/modules/native-osr/src/world-space-ui-pass.ts +351 -0
  875. package/modules/sailing/README.md +18 -0
  876. package/modules/sailing/src/index.ts +51 -0
  877. package/modules/terrain/README.md +18 -0
  878. package/modules/terrain/src/index.ts +78 -0
  879. package/modules/vitals/README.md +17 -0
  880. package/modules/vitals/src/index.ts +43 -0
  881. package/modules/vitals/src/vitals.ts +265 -0
  882. package/modules/xr/README.md +25 -0
  883. package/modules/xr/src/camera-rig.ts +146 -0
  884. package/modules/xr/src/debug.ts +59 -0
  885. package/modules/xr/src/frame-loop.ts +197 -0
  886. package/modules/xr/src/index.ts +16 -0
  887. package/modules/xr/src/input.ts +208 -0
  888. package/modules/xr/src/layer.ts +140 -0
  889. package/modules/xr/src/session.ts +156 -0
  890. package/modules/xr/src/sim-module.ts +23 -0
  891. package/modules/xr/src/types.ts +32 -0
  892. package/modules/xr/src/webxr.d.ts +182 -0
  893. package/modules/xr/src/xr-module.ts +179 -0
  894. package/package.json +232 -3
  895. package/shader-graph/src/chunks.ts +349 -0
  896. package/shader-graph/src/compiler.ts +732 -0
  897. package/shader-graph/src/compute-compiler.ts +453 -0
  898. package/shader-graph/src/compute-graph.ts +112 -0
  899. package/shader-graph/src/compute-profiles.ts +54 -0
  900. package/shader-graph/src/graph.ts +52 -0
  901. package/shader-graph/src/index.ts +53 -0
  902. package/shader-graph/src/profiles.ts +407 -0
  903. package/shader-graph/src/validator.ts +48 -0
  904. package/shader-graph/src/wgsl-binding-validator.ts +143 -0
  905. package/shader-graph/src/wgsl-struct-validator.ts +245 -0
  906. package/shader-graph/src/wgsl-struct.ts +397 -0
  907. package/test/src/index.ts +7 -0
  908. package/test/src/vision-test.ts +369 -0
@@ -0,0 +1,913 @@
1
+ // ============================================================================
2
+ // createSimWorker — helper for game sim worker entries.
3
+ //
4
+ // Handles the standard sim worker API: init, pause, resume, shutdown, setSpeed,
5
+ // step, getStats, resize. Games provide only onInit, onTick, and optional
6
+ // onResize/onAfterTicks/onShutdown.
7
+ //
8
+ // This enforces the standard API shape — consumers can't deviate. The helper
9
+ // handles:
10
+ // - Tick loop with fixed-step accumulator + speed multiplier
11
+ // - FPS tracking (per-second frame counter)
12
+ // - frameCount + tickCount
13
+ // - stepOnce (single-step mode: step once then pause)
14
+ // - startPaused option (for workers that need to wait for save data before simulating)
15
+ // - getStats() returning { fps, tick, frame }
16
+ // - expose() wiring with the standard API
17
+ // - "ready" event emission after onInit
18
+ // - Error recovery (logs error, retries after 100ms)
19
+ // - SAB polyfill buffer sync (when SharedArrayBuffer is unavailable, e.g.
20
+ // polyfilled — see sab-polyfill.ts + buffer-sync.ts)
21
+ //
22
+ // Usage in a game worker entry:
23
+ // createSimWorker({
24
+ // fixedDt: 1/30,
25
+ // onInit(sab, gridW, gridH) { ... },
26
+ // onTick(dt, ctx) { ... },
27
+ // onAfterTicks(ctx) { ... }, // once-per-frame writes (e.g. SAB upload)
28
+ // onResize(gridW, gridH) { ... },
29
+ // startPaused: true, // start paused (e.g. wait for save data)
30
+ // onSyncConfig(sab) { ... }, // buffer sync regions (SAB polyfill)
31
+ // extraApi: { clear: () => { ... }, loadGrid: (...) => { ... } },
32
+ // });
33
+ // ============================================================================
34
+
35
+ // Import the SAB polyfill FIRST — it must execute before any code that
36
+ // references SharedArrayBuffer. On hosts with real SABs this is a no-op.
37
+ import { flushProfilingTick, getWarningEngine, METRIC_TICK_LATENCY, recordTaskLatency } from "../profiling/worker-prelude";
38
+ import "../sab/sab-polyfill";
39
+ import { usingRealSAB } from "../sab/sab-polyfill";
40
+ import type { BufferSyncConfig, BufferSyncWorker } from "./buffer-sync";
41
+
42
+ import { mulberry32, type RngFn } from "../math/rng";
43
+ import type {
44
+ ComponentSection,
45
+ ISaveStore,
46
+ LoadOptions,
47
+ SaveMeta,
48
+ SaveOptions,
49
+ SaveState
50
+ } from "../save/persist-types";
51
+ import type {
52
+ GCController
53
+ } from "../telemetry/gc-controller";
54
+ import { createLogger, setThreadTag } from "../util/logger";
55
+ import { expose, exposeEvents, type WorkerApi } from "./rpc";
56
+
57
+ const log = createLogger();
58
+
59
+ export interface SimWorkerStats {
60
+ fps: number;
61
+ tick: number;
62
+ frame: number;
63
+ /** Cumulative onTick wall time in ms — diff with tick to get avg tick cost. */
64
+ tickMs: number;
65
+ }
66
+
67
+ /**
68
+ * Detect deterministic/test mode inside a worker. `DOWNDRAFT_DETERMINISTIC`
69
+ * reaches workers via `globalThis.__DOWNDRAFT_DETERMINISTIC__` (set by the
70
+ * host before worker creation) or `process.env` (Bun/Node workers).
71
+ */
72
+ function detectDeterministic(): boolean {
73
+ const g = globalThis as Record<string, unknown>;
74
+ if (g.__DOWNDRAFT_DETERMINISTIC__) return true;
75
+ try {
76
+ return (g.process as { env?: Record<string, string> } | undefined)?.env
77
+ ?.DOWNDRAFT_DETERMINISTIC === "1";
78
+ } catch {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Replace `Math.random` with a seeded-RNG-backed function that warns once per
85
+ * callsite. In deterministic mode this makes stray `Math.random()` calls in
86
+ * sim code deterministic (they draw from the same stream as `ctx.rng`) while
87
+ * surfacing migration targets. Only installed when deterministic mode is on.
88
+ */
89
+ function installDeterministicMathGuard(rng: RngFn): void {
90
+ const warned = new Set<string>();
91
+ const original = Math.random;
92
+ Math.random = () => {
93
+ const stack = new Error().stack ?? "";
94
+ if (!warned.has(stack)) {
95
+ warned.add(stack);
96
+ const line = stack.split("\n")[1]?.trim() ?? "unknown callsite";
97
+ log.warn(
98
+ "createSimWorker",
99
+ `Math.random() called in deterministic sim — use ctx.rng (from ${line})`,
100
+ );
101
+ }
102
+ return rng();
103
+ };
104
+ // Restore path for tests / unusual host reuse.
105
+ (Math.random as { __downdraftRestore?: () => void }).__downdraftRestore = () => {
106
+ Math.random = original;
107
+ };
108
+ }
109
+
110
+ export interface SimTickContext {
111
+ /** Current tick count (incremented before each onTick call). */
112
+ tickCount: number;
113
+ /** Current frame count (incremented after each tick batch). */
114
+ frameCount: number;
115
+ /** Current FPS (updated once per second). */
116
+ fps: number;
117
+ /**
118
+ * Seeded RNG (mulberry32) for deterministic randomness in sim code.
119
+ * Seeded by `CreateSimWorkerOptions.seed` or `control.setSeed()` — prefer
120
+ * this over `Math.random`, which is not deterministic across runs.
121
+ */
122
+ rng: RngFn;
123
+ }
124
+
125
+ export interface SimAfterTicksContext {
126
+ /** Number of ticks executed in this frame iteration. */
127
+ ticksThisFrame: number;
128
+ /** Current tick count after all ticks this frame. */
129
+ tickCount: number;
130
+ /** Current frame count (incremented after this callback). */
131
+ frameCount: number;
132
+ /** Current FPS (updated once per second). */
133
+ fps: number;
134
+ }
135
+
136
+ export interface SimWorkerControl {
137
+ /** Pause the sim loop (internal use, e.g. from extraApi methods). */
138
+ pause(): void;
139
+ /** Resume the sim loop (resets accumulator to prevent catch-up spike). */
140
+ resume(): void;
141
+ /**
142
+ * Stop the loop, wait for any in-flight onTick() to finish, then call the
143
+ * callback. After the callback resolves, restart the loop. Use this from
144
+ * onResize or extraApi methods that need to safely tear down + recreate
145
+ * resources (e.g. SandStepPool) without racing with the tick loop.
146
+ */
147
+ withLoopStopped<T>(fn: () => Promise<T> | T): Promise<T>;
148
+ /** Current speed multiplier (after min/max clamping). */
149
+ getSpeed(): number;
150
+ /** Set the speed multiplier (clamped to minSpeed/maxSpeed). */
151
+ setSpeed(speed: number): void;
152
+ /**
153
+ * Accumulated wall-clock ms spent inside onTick() since the last
154
+ * resetTickTimeAccum(). Use for sim-CPU% diagnostics.
155
+ */
156
+ getTickTimeAccum(): number;
157
+ /** Reset the tick-time accumulator (see getTickTimeAccum). */
158
+ resetTickTimeAccum(): void;
159
+ /** Current tick count. */
160
+ getTickCount(): number;
161
+ /**
162
+ * Attach a GCController to the loop — it is offered post-tick headroom
163
+ * for proactive collection each frame (same contract as
164
+ * SimWorkerLoop.setGCController).
165
+ */
166
+ setGCController(ctrl: GCController | null): void;
167
+ /**
168
+ * Stop the loop permanently (e.g. from an uncaught-error handler).
169
+ * After stop() the worker only responds to RPC — no more ticks.
170
+ */
171
+ stop(): void;
172
+ /** Get the events emitter for sending events to the host. */
173
+ events: { emit: (kind: string, data?: any) => void };
174
+ /**
175
+ * Reseed the deterministic RNG exposed as `ctx.rng`. Call from `onInit`
176
+ * once the game's seed is known (e.g. `control.setSeed(simConfig.seed)`).
177
+ * If `Math.random` was trapped (deterministic mode), the trap draws from
178
+ * the reseeded stream too.
179
+ */
180
+ setSeed(seed: number): void;
181
+ }
182
+
183
+ export interface CreateSimWorkerOptions {
184
+ /** Fixed simulation timestep in seconds (e.g., 1/30 for 30Hz, 1/60 for 60Hz). */
185
+ fixedDt: number;
186
+ /** Maximum ticks per loop iteration (prevents death spiral). Default: 5. */
187
+ maxStepsPerFrame?: number;
188
+
189
+ /** Minimum speed multiplier for setSpeed(). Default: 0. */
190
+ minSpeed?: number;
191
+ /** Maximum speed multiplier for setSpeed(). Default: unbounded. */
192
+ maxSpeed?: number;
193
+
194
+ /**
195
+ * If true, the worker starts in paused mode. The renderer must call
196
+ * resume() to begin simulation. Useful when save data must be loaded
197
+ * before the sim starts (e.g. mining-rpg). Default: false.
198
+ */
199
+ startPaused?: boolean;
200
+
201
+ /**
202
+ * Seed for the deterministic RNG exposed as `ctx.rng` in `onTick`.
203
+ * If the real seed only becomes known inside `onInit` (e.g. from
204
+ * simConfig args), call `control.setSeed(seed)` there instead.
205
+ */
206
+ seed?: number;
207
+
208
+ /**
209
+ * Deterministic/test mode. When true, `Math.random` is replaced by a
210
+ * seeded-RNG-backed shim that warns once per callsite — stray entropy in
211
+ * sim code becomes deterministic AND visible. Default: auto-detected via
212
+ * `__DOWNDRAFT_DETERMINISTIC__` / `process.env.DOWNDRAFT_DETERMINISTIC`.
213
+ * Set explicitly to force on/off.
214
+ */
215
+ deterministic?: boolean;
216
+
217
+ /**
218
+ * Called once at init time. Receives the SharedArrayBuffer and any
219
+ * additional args passed to the init() RPC. Set up the world, writer,
220
+ * input views, etc. here. Throw to signal init failure.
221
+ */
222
+ onInit: (sab: SharedArrayBuffer, control: SimWorkerControl, ...args: any[]) => Promise<void> | void;
223
+
224
+ /**
225
+ * Called once per simulation tick. Receives the fixed dt (in seconds) and a
226
+ * context object with current tickCount, frameCount, and fps.
227
+ * Perform the sim step here. Throw to signal a tick error (the loop will
228
+ * log it and retry after 100ms).
229
+ */
230
+ onTick: (dt: number, ctx: SimTickContext) => Promise<void> | void;
231
+
232
+ /**
233
+ * Called when the worker receives a resize() RPC. Optional.
234
+ * Receives the args passed to resize().
235
+ */
236
+ onResize?: (...args: any[]) => Promise<void> | void;
237
+
238
+ /**
239
+ * Called when the worker receives a shutdown() RPC. Optional.
240
+ * Clean up resources here.
241
+ */
242
+ onShutdown?: () => Promise<void> | void;
243
+
244
+ /**
245
+ * Called after each tick batch (all ticks for one frame iteration).
246
+ * Use for once-per-frame writes (e.g. uploading the full SAB after all
247
+ * catch-up ticks, rather than per-tick). Receives a context with
248
+ * ticksThisFrame, tickCount, frameCount, and fps.
249
+ *
250
+ * Note: frameCount is incremented AFTER this callback, so the value
251
+ * passed here is the count before this frame's increment.
252
+ */
253
+ onAfterTicks?: (ctx: SimAfterTicksContext) => void;
254
+
255
+ /**
256
+ * Called when the loop crashes with an unrecoverable error.
257
+ */
258
+ onError?: (err: Error) => void;
259
+
260
+ /**
261
+ * Returns the buffer sync config for the SAB polyfill (worker side).
262
+ * Called after onInit with the SAB. If provided and SharedArrayBuffer is
263
+ * unavailable, a BufferSyncWorker is created that syncs
264
+ * the declared write regions to the main thread after each tick batch.
265
+ * The regions should declare which regions the WORKER writes (sim data,
266
+ * stats, board) vs which it reads (input). See buffer-sync.ts.
267
+ */
268
+ onSyncConfig?: (sab: SharedArrayBuffer) => BufferSyncConfig;
269
+
270
+ /**
271
+ * SAB polyfill throttled sync: names of "fast" write regions that should be
272
+ * synced to the main thread EVERY tick batch (e.g. header, entities, drops).
273
+ * Large "slow" regions (e.g. grid data) are only synced every
274
+ * `onSyncSlowInterval` tick batches. This dramatically reduces per-tick copy
275
+ * overhead on mobile (e.g. 18KB/tick instead of 1.78MB/tick).
276
+ *
277
+ * If omitted, ALL write regions are synced every tick batch (original behavior).
278
+ */
279
+ onSyncFastRegions?: string[];
280
+
281
+ /**
282
+ * How often (in tick batches) to sync ALL write regions (including slow
283
+ * regions not listed in `onSyncFastRegions`). Default: 10. Only effective
284
+ * when `onSyncFastRegions` is provided.
285
+ */
286
+ onSyncSlowInterval?: number;
287
+
288
+ /**
289
+ * Additional API methods to expose beyond the standard set.
290
+ * e.g. { clear: () => { ... }, loadGrid: (grid, fields, w, h) => { ... } }
291
+ */
292
+ extraApi?: Record<string, (...args: any[]) => any>;
293
+
294
+ /**
295
+ * Save/load plumbing. When provided, the standard API gains the game
296
+ * persistence contract shared by the worker-host save flow:
297
+ *
298
+ * save(slotName, opts?) → { slotName, stateJson, success, gen?, meta? }
299
+ * load(slotName, stateJson?, opts?) → boolean
300
+ * initSaveStore(storeOpts) → creates + initializes the inline store
301
+ * restoreFromState(stateJson) → applies a serialized payload directly
302
+ *
303
+ * `capture()` returns the component's data payload; it is wrapped in the
304
+ * standard SaveState shape (components[componentName] = { v, data }) so
305
+ * all save backends store the same format. The computed
306
+ * SaveMeta (defaults merged with the `meta()` hook's extras) is returned
307
+ * as `meta` so the renderer-side store can persist real metadata.
308
+ * The emitted "saved" event carries `origin: "renderer"` — sim-initiated
309
+ * saves (Simulation.save) emit without an origin so game handlers can
310
+ * distinguish who must persist the payload.
311
+ */
312
+ save?: {
313
+ /**
314
+ * Serialize the sim's current state. When `componentName` is set this
315
+ * returns that component's data payload (wrapped as `{ v, data }` under
316
+ * `components[componentName]`); when omitted it returns the full
317
+ * components map directly (multi-component games).
318
+ *
319
+ * Optional — omit for push-model games where the renderer captures state
320
+ * itself (e.g. reads the SAB) and only pushes data in on load. When
321
+ * omitted, save()/captureState() are not exposed.
322
+ */
323
+ capture?: () => unknown | Promise<unknown>;
324
+ /**
325
+ * Binary payloads that accompany the JSON capture (typed-array state like
326
+ * grids/chunks that isn't JSON-expressible). Called immediately after
327
+ * capture() — with no sim tick able to interleave — so the two can share
328
+ * a captured snapshot via a worker-local variable. The blobs flow to
329
+ * ISaveStore.save() via SaveOptions.blobs (worker-owned stores) and are
330
+ * returned from captureState() (renderer-owned stores).
331
+ */
332
+ captureBlobs?: () => Record<string, ArrayBuffer> | Promise<Record<string, ArrayBuffer>>;
333
+ /**
334
+ * Apply a previously captured payload. Called with the loop stopped
335
+ * (withLoopStopped) so the sim can't tick mid-restore. Receives the
336
+ * component data (componentName set) or the full parsed payload, plus
337
+ * any binary blobs from the SaveState (grid/chunk payloads that aren't
338
+ * JSON-expressible).
339
+ */
340
+ restore?: (data: unknown, blobs?: Record<string, ArrayBuffer>) => void | Promise<void>;
341
+ /**
342
+ * Component key under SaveState.components (e.g. "sandbox"). Omit when
343
+ * capture() returns the whole components map.
344
+ */
345
+ componentName?: string;
346
+ /** Schema version stamped on the component section. Default: 1. */
347
+ version?: number;
348
+ /**
349
+ * Factory for an inline save store (e.g. `(o) => new OpfsSaveStore(o)`).
350
+ * Injectable because OpfsSaveStore lives in libraries/persistence —
351
+ * core must not import it. When omitted, save() still returns the
352
+ * stateJson for the caller's own store path.
353
+ */
354
+ createStore?: (opts: unknown) => ISaveStore & { init?: () => Promise<void> };
355
+ /**
356
+ * Extra SaveState.meta fields. Receives the captured payload and the
357
+ * caller's SaveOptions (e.g. to forward engineVersion from
358
+ * saveOpts.properties).
359
+ */
360
+ meta?: (data: unknown, saveOpts?: SaveOptions) => Partial<SaveMeta>;
361
+ };
362
+
363
+ /**
364
+ * Command dispatch. When provided, the standard API gains
365
+ * sendCommand(cmd) which forwards to this handler.
366
+ */
367
+ onCommand?: (cmd: unknown) => void | Promise<void>;
368
+
369
+ /**
370
+ * Wrap the exposed API with devtools + profiling RPC methods
371
+ * (__devtoolsGetManifest, __profilingAttach, etc.). When provided, the
372
+ * function is called with the assembled API and its return value is passed
373
+ * to expose(). Use this to add devtools + profiling RPC methods:
374
+ *
375
+ * wrapExpose: (api) => exposeDevToolsApi(api)
376
+ *
377
+ * Default: undefined (expose(api) is called directly).
378
+ */
379
+ wrapExpose?: (api: WorkerApi) => WorkerApi;
380
+ }
381
+
382
+ /**
383
+ * Creates and starts a sim worker with the standard API.
384
+ * Call this at the top level of a game's worker entry file.
385
+ *
386
+ * The exposed API is:
387
+ * init(sab, ...args) — calls onInit, starts the loop, emits "ready"
388
+ * resize(...args) — calls onResize
389
+ * pause() — pauses the loop
390
+ * resume() — resumes the loop (resets accumulator to prevent catch-up spike)
391
+ * shutdown() — stops the loop, calls onShutdown
392
+ * setSpeed(speed) — sets the speed multiplier (0 = paused)
393
+ * step() — single-steps (runs 1 tick then pauses)
394
+ * getStats() — returns { fps, tick, frame }
395
+ * save/load/initSaveStore/restoreFromState — when opts.save is provided
396
+ * sendCommand(cmd) — when opts.onCommand is provided
397
+ * ...extraApi — any additional methods
398
+ */
399
+ export function createSimWorker(opts: CreateSimWorkerOptions): SimWorkerControl {
400
+ // Thread tag for log prefixes — this file runs in the sim worker realm.
401
+ // Games that run multiple sims (e.g. falling-sand's per-layer workers)
402
+ // set their own tag (S0/S1/...) after this default.
403
+ if (!(globalThis as any).__ddThreadTag) setThreadTag("S0");
404
+
405
+ const tickMs = opts.fixedDt * 1000;
406
+ const maxStepsPerFrame = opts.maxStepsPerFrame ?? 5;
407
+
408
+ let running = false;
409
+ let loopActive = false;
410
+ let paused = false;
411
+ let stepOnce = false;
412
+ let speedMultiplier = 1;
413
+ let lastTick = 0;
414
+ let tickAccumulator = 0;
415
+ let tickCount = 0;
416
+ let tickTimeAccum = 0;
417
+ let frameCount = 0;
418
+ let fpsTimer = 0;
419
+ let fps = 0;
420
+
421
+ // SAB polyfill: buffer sync worker (only created when SAB is unavailable).
422
+ let syncWorker: BufferSyncWorker | null = null;
423
+ // SAB polyfill: throttled sync counter. When onSyncFastRegions is provided,
424
+ // fast regions are synced every tick batch, and ALL regions (including slow
425
+ // ones) are synced every onSyncSlowInterval tick batches.
426
+ let syncSlowCounter = 0;
427
+ const syncSlowInterval = opts.onSyncSlowInterval ?? 10;
428
+
429
+ // Inline save store (created by initSaveStore() via opts.save.createStore).
430
+ let saveStore: (ISaveStore & { init?: () => Promise<void> }) | null = null;
431
+
432
+ // Optional GC controller — offered post-tick headroom each frame.
433
+ let gcController: GCController | null = null;
434
+
435
+ /**
436
+ * Extract the restore payload from a serialized state. Handles all three
437
+ * wire forms: raw data, a components map (`{ name: { v, data } }`), and a
438
+ * full SaveState (`{ components: { name: { v, data } } }`). When
439
+ * componentName is omitted the components map / raw payload is returned
440
+ * as-is for multi-component games.
441
+ */
442
+ const extractRestoreData = (parsed: any): unknown => {
443
+ const name = opts.save!.componentName;
444
+ if (!name) return parsed?.components ?? parsed;
445
+ return (
446
+ parsed?.components?.[name]?.data ??
447
+ (parsed?.[name]?.data !== undefined ? parsed[name].data : parsed)
448
+ );
449
+ };
450
+ const syncFastRegions = opts.onSyncFastRegions;
451
+ // setTimeout truncates fractional milliseconds (e.g. 32.333 → 32), losing
452
+ // ~frac(tickMs) ms per iteration. For tickMs = 33.333 (30Hz), that's 0.333ms
453
+ // per iteration — after ~100 iterations (~3.3s) the deficit reaches one full
454
+ // tick, causing the accumulator to burst a 2-tick frame. This produces a
455
+ // slow, periodic oscillation in the effective tick rate (and thus in
456
+ // everything driven by dt: movement speed, drop spin, animation timing).
457
+ // We accumulate the sub-ms remainder and add it back when it exceeds 1ms,
458
+ // so the average setTimeout delay matches the desired interval exactly.
459
+ let timerRemainder = 0;
460
+
461
+ // The single armed loop timer — loop() schedules through scheduleLoop so
462
+ // withLoopStopped/stop/shutdown can disarm it. Without this, stopping the
463
+ // loop left a pending setTimeout that fired after the stop and spawned a
464
+ // second parallel loop chain.
465
+ let loopTimer: ReturnType<typeof setTimeout> | null = null;
466
+ // Promise for the in-flight onTick() call — the real barrier
467
+ // withLoopStopped waits on (the old code polled a flag for ~50ms then
468
+ // proceeded anyway, letting a suspended tick race the critical section).
469
+ let tickInFlight: Promise<void> | null = null;
470
+ // Serializes withLoopStopped critical sections.
471
+ let stopChain: Promise<unknown> = Promise.resolve();
472
+
473
+ const events = exposeEvents();
474
+
475
+ // Deterministic RNG — reseedable via control.setSeed(); the stable `rng`
476
+ // closure is what ctx.rng and the Math.random shim draw from.
477
+ let rngImpl = mulberry32(opts.seed ?? 0x9e3779b9);
478
+ const rng: RngFn = () => rngImpl();
479
+ const deterministic = opts.deterministic ?? detectDeterministic();
480
+ if (deterministic) {
481
+ installDeterministicMathGuard(rng);
482
+ }
483
+
484
+ const control: SimWorkerControl = {
485
+ pause: () => { paused = true; },
486
+ setSeed: (seed: number) => { rngImpl = mulberry32(seed); },
487
+ resume: () => {
488
+ paused = false;
489
+ lastTick = performance.now();
490
+ tickAccumulator = 0;
491
+ },
492
+ async withLoopStopped<T>(fn: () => Promise<T> | T): Promise<T> {
493
+ // Serialize concurrent callers — a second caller queues behind the
494
+ // first's stop → critical section → restart cycle.
495
+ const run = stopChain.then(async (): Promise<T> => {
496
+ // Stop the loop from scheduling another iteration AND disarm the
497
+ // pending one — an armed setTimeout that fires while loopActive was
498
+ // already restored used to spawn a second parallel loop chain.
499
+ loopActive = false;
500
+ clearLoopTimer();
501
+ // Hard barrier: if onTick is mid-flight, wait for it to actually
502
+ // return. Its continuation observes loopActive === false and exits
503
+ // before touching the accumulator or rescheduling. No timeout —
504
+ // running fn() while a tick still mutates state is exactly the
505
+ // race this exists to prevent.
506
+ if (tickInFlight) await tickInFlight.catch(() => {});
507
+ try {
508
+ return await fn();
509
+ } finally {
510
+ // Restart the loop — unless shutdown landed while we held the
511
+ // barrier (running === false is the permanent stop signal).
512
+ if (running) {
513
+ loopActive = true;
514
+ lastTick = performance.now();
515
+ tickAccumulator = 0;
516
+ void loop();
517
+ }
518
+ }
519
+ });
520
+ // Keep the chain alive across failures in fn().
521
+ stopChain = run.then(() => undefined, () => undefined);
522
+ return run;
523
+ },
524
+ getSpeed: () => speedMultiplier,
525
+ setSpeed: (speed: number) => {
526
+ speedMultiplier = Math.min(
527
+ opts.maxSpeed ?? Infinity,
528
+ Math.max(opts.minSpeed ?? 0, speed),
529
+ );
530
+ },
531
+ getTickTimeAccum: () => tickTimeAccum,
532
+ resetTickTimeAccum: () => { tickTimeAccum = 0; },
533
+ getTickCount: () => tickCount,
534
+ setGCController: (ctrl: GCController | null) => { gcController = ctrl; },
535
+ stop: () => { running = false; loopActive = false; clearLoopTimer(); },
536
+ events,
537
+ };
538
+
539
+ /** Arm the next loop iteration — the ONLY place a loop timer is created. */
540
+ function scheduleLoop(delay: number): void {
541
+ if (!loopActive) return;
542
+ if (loopTimer !== null) clearTimeout(loopTimer); // never stack timers
543
+ loopTimer = setTimeout(() => {
544
+ loopTimer = null;
545
+ void loop();
546
+ }, delay);
547
+ }
548
+
549
+ function clearLoopTimer(): void {
550
+ if (loopTimer !== null) {
551
+ clearTimeout(loopTimer);
552
+ loopTimer = null;
553
+ }
554
+ }
555
+
556
+ async function loop(): Promise<void> {
557
+ if (!running || !loopActive) return;
558
+
559
+ try {
560
+ const now = performance.now();
561
+ const elapsed = now - lastTick;
562
+
563
+ if (elapsed >= tickMs) {
564
+ lastTick = now - (elapsed % tickMs);
565
+
566
+ if (!paused || stepOnce) {
567
+ // Only accumulate sim time when actually stepping. While paused,
568
+ // lastTick still advances (above) so we don't get a huge elapsed
569
+ // spike on resume, but we must NOT let tickAccumulator build up.
570
+ //
571
+ // Credit only WHOLE tick periods: the fractional remainder stays
572
+ // owed in lastTick's phase (the snap above) and is re-measured as
573
+ // part of the next elapsed. Crediting elapsed/tickMs fractionally
574
+ // here AND keeping the remainder in lastTick counts it twice —
575
+ // every late-timer remainder leaks ~its size in extra sim time,
576
+ // so the sim ran measurably fast under timer jitter.
577
+ tickAccumulator += Math.floor(elapsed / tickMs) * speedMultiplier;
578
+
579
+ let steps = 0;
580
+ const maxSteps = stepOnce ? 1 : maxStepsPerFrame;
581
+
582
+ while (tickAccumulator >= 1 && steps < maxSteps) {
583
+ tickCount++;
584
+ const tickStart = performance.now();
585
+ const tick = Promise.resolve(
586
+ opts.onTick(opts.fixedDt, { tickCount, frameCount, fps, rng }),
587
+ );
588
+ tickInFlight = tick;
589
+ try {
590
+ await tick;
591
+ } finally {
592
+ tickInFlight = null;
593
+ }
594
+ const tickDurationUs = (performance.now() - tickStart) * 1000;
595
+ tickTimeAccum += tickDurationUs / 1000;
596
+ // Record task latency for the flame graph + histogram
597
+ recordTaskLatency("js", tickDurationUs, "tick");
598
+ // Fire instantaneous tick-latency warning (if configured)
599
+ getWarningEngine()?.checkInstant(METRIC_TICK_LATENCY, tickDurationUs);
600
+ if (!loopActive) return; // resize/withLoopStopped interrupted
601
+ tickAccumulator -= 1;
602
+ steps++;
603
+ }
604
+
605
+ // Clamp accumulator if we hit the step cap (death spiral prevention)
606
+ if (tickAccumulator > maxStepsPerFrame) {
607
+ tickAccumulator = 0;
608
+ }
609
+
610
+ if (stepOnce) {
611
+ stepOnce = false;
612
+ paused = true;
613
+ tickAccumulator = 0;
614
+ }
615
+
616
+ // Once-per-frame callback (after all ticks this frame)
617
+ if (steps > 0) {
618
+ opts.onAfterTicks?.({
619
+ ticksThisFrame: steps,
620
+ tickCount,
621
+ frameCount,
622
+ fps,
623
+ });
624
+
625
+ // SAB polyfill: sync written regions to the main thread after
626
+ // each tick batch. No-op when real SAB is available (desktop).
627
+ // When onSyncFastRegions is provided, use throttled sync:
628
+ // - Every tick batch: sync only fast regions (header, entities)
629
+ // - Every N tick batches: sync ALL regions (including grids)
630
+ if (syncWorker) {
631
+ if (syncFastRegions) {
632
+ syncWorker.syncToMain(syncFastRegions);
633
+ syncSlowCounter++;
634
+ if (syncSlowCounter >= syncSlowInterval) {
635
+ syncWorker.syncToMain();
636
+ syncSlowCounter = 0;
637
+ }
638
+ } else {
639
+ syncWorker.syncToMain();
640
+ }
641
+ }
642
+
643
+ // Flush profiling data (ThreadMetrics + EventLoop + instant warnings)
644
+ flushProfilingTick();
645
+ }
646
+ }
647
+
648
+ // Offer post-tick headroom to the GC controller for proactive
649
+ // collection (same contract as SimWorkerLoop).
650
+ if (gcController) {
651
+ const headroom = tickMs - (performance.now() - now);
652
+ if (headroom > 0) {
653
+ gcController.maybeCollect(headroom, tickMs);
654
+ }
655
+ }
656
+ }
657
+
658
+ frameCount++;
659
+ fpsTimer += elapsed;
660
+ if (fpsTimer >= 1000) {
661
+ fps = Math.round((frameCount * 1000) / fpsTimer);
662
+ frameCount = 0;
663
+ fpsTimer = 0;
664
+ }
665
+
666
+ // Sleep until the next tick is due instead of spinning with setTimeout(0).
667
+ // The deadline is phase-aligned: lastTick holds the fractional
668
+ // `elapsed % tickMs` remainder from the snap above, so the next tick is
669
+ // due at lastTick + tickMs — NOT a fresh tickMs from now. Sleeping a
670
+ // whole tickMs per iteration discards that remainder: whenever the
671
+ // remainder + timer jitter was smaller than this iteration's work, the
672
+ // loop woke before the deadline, did nothing, and slept another full
673
+ // tickMs — producing alternating normal/doubled tick intervals.
674
+ // If the accumulator still holds owed ticks (step cap was hit), they're
675
+ // spent on the next due iteration — the backlog is clamped to
676
+ // maxStepsPerFrame so a deadline wait is never more than ~tickMs.
677
+ const remaining = lastTick + tickMs - performance.now();
678
+ // Compensate for setTimeout's integer truncation: accumulate the sub-ms
679
+ // fractional part and add 1ms when it exceeds 1ms, so the average delay
680
+ // matches the desired interval exactly. Without this, a fractional tickMs
681
+ // (e.g. 33.333 for 30Hz) loses ~0.333ms per iteration to truncation,
682
+ // causing a ~3.3s periodic oscillation in the effective tick rate.
683
+ const intRemaining = Math.max(0, Math.floor(remaining));
684
+ timerRemainder += remaining - intRemaining;
685
+ let delay = intRemaining;
686
+ if (timerRemainder >= 1) {
687
+ delay += 1;
688
+ timerRemainder -= 1;
689
+ }
690
+ scheduleLoop(delay);
691
+ } catch (e) {
692
+ const err = e as Error;
693
+ log.error("createSimWorker", `Loop error: ${err.message}\n${err.stack}`);
694
+ opts.onError?.(err);
695
+ scheduleLoop(100);
696
+ }
697
+ }
698
+
699
+ const standardApi: WorkerApi = {
700
+ async init(sab: SharedArrayBuffer, ...args: any[]): Promise<void> {
701
+ await opts.onInit(sab, control, ...args);
702
+
703
+ // SAB polyfill: set up buffer sync if SAB is unavailable and the game
704
+ // provided a sync config. The BufferSyncWorker posts written regions
705
+ // to the main thread after each tick batch (see syncToMain() above).
706
+ if (!usingRealSAB && opts.onSyncConfig) {
707
+ const { BufferSyncWorker: BSW } = await import("./buffer-sync");
708
+ syncWorker = new BSW(opts.onSyncConfig(sab));
709
+ syncWorker.start();
710
+ // Sync initial state (e.g. board data written during onInit) to the
711
+ // main thread immediately — the sim may be paused at startup and
712
+ // syncToMain() won't be called until the first tick batch.
713
+ syncWorker.syncToMain();
714
+ }
715
+
716
+ running = true;
717
+ loopActive = true;
718
+ paused = opts.startPaused ?? false;
719
+ stepOnce = false;
720
+ speedMultiplier = 1;
721
+ lastTick = performance.now();
722
+ tickAccumulator = 0;
723
+ tickCount = 0;
724
+ frameCount = 0;
725
+ fpsTimer = 0;
726
+ fps = 0;
727
+ timerRemainder = 0;
728
+ clearLoopTimer();
729
+ events.emit("ready", {});
730
+ void loop();
731
+ },
732
+
733
+ async resize(...args: any[]): Promise<void> {
734
+ if (!opts.onResize) return;
735
+ // Use withLoopStopped to safely pause the loop during resize.
736
+ // This prevents races between onTick() and onResize() when both
737
+ // access shared resources (e.g. SandStepPool).
738
+ await control.withLoopStopped(() => opts.onResize!(...args));
739
+ },
740
+
741
+ async pause(): Promise<void> {
742
+ paused = true;
743
+ },
744
+
745
+ async resume(): Promise<void> {
746
+ paused = false;
747
+ lastTick = performance.now();
748
+ tickAccumulator = 0; // prevent catch-up spike after resume
749
+ },
750
+
751
+ async shutdown(): Promise<void> {
752
+ running = false;
753
+ loopActive = false;
754
+ clearLoopTimer();
755
+ syncWorker = null; // release sync worker reference
756
+ saveStore = null;
757
+ await opts.onShutdown?.();
758
+ },
759
+
760
+ async setSpeed(speed: number): Promise<void> {
761
+ speedMultiplier = Math.min(
762
+ opts.maxSpeed ?? Infinity,
763
+ Math.max(opts.minSpeed ?? 0, speed),
764
+ );
765
+ },
766
+
767
+ async step(): Promise<void> {
768
+ stepOnce = true;
769
+ paused = false;
770
+ lastTick = performance.now();
771
+ tickAccumulator = 0;
772
+ },
773
+
774
+ getStats(): SimWorkerStats {
775
+ return { fps, tick: tickCount, frame: frameCount, tickMs: tickTimeAccum };
776
+ },
777
+
778
+ ...(opts.save?.capture
779
+ ? {
780
+ async save(
781
+ slotName: string,
782
+ saveOpts?: SaveOptions,
783
+ ): Promise<{ slotName: string; stateJson: string; success: boolean; gen?: number; meta?: SaveMeta }> {
784
+ const data = await opts.save!.capture!();
785
+ const capturedBlobs = await opts.save!.captureBlobs?.();
786
+ // Always wrap in SaveState.components format so every backend
787
+ // stores the state in the expected component format —
788
+ // FileSaveStore.load() expects each component to have a
789
+ // { v, data } structure.
790
+ const name = opts.save!.componentName;
791
+ const components: Record<string, ComponentSection> = name
792
+ ? { [name]: { v: opts.save!.version ?? 1, data } }
793
+ : (data as Record<string, ComponentSection>);
794
+ const metaExtra = opts.save!.meta?.(data, saveOpts);
795
+ const saveStateObj: SaveState = {
796
+ components,
797
+ meta: {
798
+ engineVersion: "0.0.0",
799
+ timestamp: Date.now() / 1000,
800
+ entityCount: 0,
801
+ playerCount: 0,
802
+ ...metaExtra,
803
+ },
804
+ };
805
+ const stateJson = JSON.stringify(saveStateObj.components);
806
+ let gen: number | undefined;
807
+ let success = true;
808
+ if (saveStore) {
809
+ const result = await saveStore.save(slotName, saveStateObj, {
810
+ ...saveOpts,
811
+ blobs: capturedBlobs ?? saveOpts?.blobs,
812
+ });
813
+ success = result.success;
814
+ gen = result.gen;
815
+ }
816
+ events.emit("saved", { slotName, stateJson, success, gen, meta: saveStateObj.meta, origin: "renderer" });
817
+ return { slotName, stateJson, success, gen, meta: saveStateObj.meta };
818
+ },
819
+
820
+ /**
821
+ * Raw capture payload — no store interaction. Lets a
822
+ * renderer-owned save system (createGameSaveSystem) pull the
823
+ * worker's state and embed it in its own SaveState. Binary
824
+ * payloads (typed arrays) come back in `blobs`, transferred via
825
+ * structured clone.
826
+ */
827
+ async captureState(): Promise<{ stateJson: string; blobs?: Record<string, ArrayBuffer> }> {
828
+ const data = await opts.save!.capture!();
829
+ const blobs = await opts.save!.captureBlobs?.();
830
+ return { stateJson: JSON.stringify(data), blobs };
831
+ },
832
+ }
833
+ : {}),
834
+
835
+ ...(opts.save?.restore
836
+ ? {
837
+ async load(
838
+ slotName: string,
839
+ stateJson?: string,
840
+ loadOpts?: LoadOptions,
841
+ ): Promise<boolean> {
842
+ let json = stateJson;
843
+ let blobs: Record<string, ArrayBuffer> | undefined;
844
+ let gen: number | undefined;
845
+ if (!json && saveStore) {
846
+ const result = await saveStore.load(slotName, loadOpts);
847
+ const payload = opts.save!.componentName
848
+ ? result.state?.components?.[opts.save!.componentName]?.data
849
+ : result.state?.components;
850
+ if (payload !== undefined && payload !== null) {
851
+ json = JSON.stringify(payload);
852
+ blobs = result.blobs;
853
+ gen = result.gen;
854
+ }
855
+ }
856
+ if (!json) {
857
+ events.emit("loaded", { slotName, success: false });
858
+ return false;
859
+ }
860
+ let data: unknown = json;
861
+ try {
862
+ data = extractRestoreData(JSON.parse(json));
863
+ } catch {
864
+ // not JSON — pass through as-is
865
+ }
866
+ // Stop the loop during restore — mutating sim state while a tick
867
+ // is in flight is a data race.
868
+ await control.withLoopStopped(() => opts.save!.restore!(data, blobs));
869
+ events.emit("loaded", { slotName, success: true, gen });
870
+ return true;
871
+ },
872
+
873
+ async restoreFromState(
874
+ stateJson: string,
875
+ blobs?: Record<string, ArrayBuffer>,
876
+ ): Promise<void> {
877
+ let data: unknown = stateJson;
878
+ try {
879
+ data = extractRestoreData(JSON.parse(stateJson));
880
+ } catch {
881
+ // not JSON — pass through as-is
882
+ }
883
+ await control.withLoopStopped(() => opts.save!.restore!(data, blobs));
884
+ },
885
+ }
886
+ : {}),
887
+
888
+ ...(opts.save?.createStore
889
+ ? {
890
+ async initSaveStore(storeOpts: unknown): Promise<void> {
891
+ saveStore = opts.save!.createStore!(storeOpts);
892
+ await saveStore.init?.();
893
+ },
894
+ }
895
+ : {}),
896
+
897
+ ...(opts.onCommand
898
+ ? {
899
+ async sendCommand(cmd: unknown): Promise<void> {
900
+ await opts.onCommand!(cmd);
901
+ },
902
+ }
903
+ : {}),
904
+ };
905
+
906
+ const api: WorkerApi = { ...standardApi, ...(opts.extraApi ?? {}) };
907
+
908
+ // Optionally wrap the API before exposing (e.g. with exposeDevToolsApi
909
+ // to add devtools + profiling RPC methods).
910
+ expose(opts.wrapExpose ? opts.wrapExpose(api) : api);
911
+
912
+ return control;
913
+ }