@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.
- package/README.md +33 -0
- package/app/src/index.ts +10 -0
- package/app/src/renderer/bootstrap.ts +271 -0
- package/app/src/renderer/downdraft-base.css +93 -0
- package/app/src/renderer/feature-log.ts +200 -0
- package/app/src/renderer/game-module.ts +923 -0
- package/app/src/renderer/hooks.ts +88 -0
- package/app/src/renderer/hot-reload.ts +178 -0
- package/app/src/renderer/import-cache.ts +88 -0
- package/app/src/renderer/index.ts +206 -0
- package/app/src/renderer/mcp-harness.ts +144 -0
- package/app/src/renderer/save-store-factory.ts +239 -0
- package/app/src/renderer/sim-bridge.ts +183 -0
- package/app/src/renderer/standard-automation-tools.ts +771 -0
- package/app/src/shared/feature-log.ts +71 -0
- package/app/src/shared/gpu-info.ts +82 -0
- package/app/src/shared/import-cache-store.ts +142 -0
- package/app/src/shared/index.ts +6 -0
- package/app/src/shared/mcp-discovery.ts +47 -0
- package/app/src/shared/mcp-proxy.ts +82 -0
- package/app/src/shared/types.ts +324 -0
- package/asset-bake/src/bake-audio.ts +119 -0
- package/asset-bake/src/bake-gltf.ts +172 -0
- package/asset-bake/src/bake.ts +78 -0
- package/asset-bake/src/cache.ts +188 -0
- package/asset-bake/src/config.ts +186 -0
- package/asset-bake/src/index.ts +36 -0
- package/core/src/animation/animation-event.ts +41 -0
- package/core/src/animation/bone-mask.ts +122 -0
- package/core/src/animation/clip.ts +252 -0
- package/core/src/animation/index.ts +22 -0
- package/core/src/animation/mixamo.ts +113 -0
- package/core/src/animation/morph-target.ts +69 -0
- package/core/src/animation/player.ts +697 -0
- package/core/src/animation/retarget.ts +158 -0
- package/core/src/animation/skeleton-animator.ts +1399 -0
- package/core/src/animation/skeleton.ts +214 -0
- package/core/src/animation/state-machine.ts +280 -0
- package/core/src/assets/blob-store.ts +66 -0
- package/core/src/assets/cache.ts +186 -0
- package/core/src/assets/cubemap-converter.ts +155 -0
- package/core/src/assets/environment-manager.ts +121 -0
- package/core/src/assets/import-cache.ts +65 -0
- package/core/src/assets/import-settings.ts +72 -0
- package/core/src/assets/importer.ts +108 -0
- package/core/src/assets/index.ts +74 -0
- package/core/src/assets/irradiance-generator.ts +198 -0
- package/core/src/assets/loader-audio.ts +86 -0
- package/core/src/assets/loader-cubemap.ts +211 -0
- package/core/src/assets/loader-dds.ts +236 -0
- package/core/src/assets/loader-hdr.ts +474 -0
- package/core/src/assets/loader-mesh.ts +143 -0
- package/core/src/assets/loader-shader.ts +73 -0
- package/core/src/assets/loader-texture.ts +349 -0
- package/core/src/assets/lod.ts +198 -0
- package/core/src/assets/manager.ts +480 -0
- package/core/src/assets/manifest.ts +81 -0
- package/core/src/assets/material-bridge.ts +186 -0
- package/core/src/assets/model-importer.ts +72 -0
- package/core/src/assets/model-normalizer.ts +248 -0
- package/core/src/assets/model-to-gpu.ts +70 -0
- package/core/src/assets/model-to-mesh.ts +237 -0
- package/core/src/assets/prefilter-generator.ts +250 -0
- package/core/src/audio/engine.ts +177 -0
- package/core/src/audio/interface.ts +130 -0
- package/core/src/audio/listener.ts +32 -0
- package/core/src/audio/mixer.ts +141 -0
- package/core/src/audio/registry.ts +52 -0
- package/core/src/audio/sab.ts +87 -0
- package/core/src/audio/source.ts +89 -0
- package/core/src/change-detection/atomics.ts +1 -0
- package/core/src/change-detection/tracker.ts +47 -0
- package/core/src/debug-draw/lines.ts +64 -0
- package/core/src/debug-draw/points.ts +17 -0
- package/core/src/debug-draw/queue.ts +124 -0
- package/core/src/debug-draw/text.ts +25 -0
- package/core/src/ecs/archetype.ts +323 -0
- package/core/src/ecs/component.ts +188 -0
- package/core/src/ecs/entity.ts +25 -0
- package/core/src/ecs/events.ts +93 -0
- package/core/src/ecs/hierarchy.ts +124 -0
- package/core/src/ecs/hmr-swap-registry.ts +22 -0
- package/core/src/ecs/index.ts +25 -0
- package/core/src/ecs/query.ts +141 -0
- package/core/src/ecs/resource.ts +8 -0
- package/core/src/ecs/schedule.ts +181 -0
- package/core/src/ecs/system-params.ts +99 -0
- package/core/src/ecs/system.ts +42 -0
- package/core/src/ecs/world.ts +348 -0
- package/core/src/index.ts +352 -0
- package/core/src/input/context.ts +73 -0
- package/core/src/input/dom-handler.ts +380 -0
- package/core/src/input/local-player-manager.ts +218 -0
- package/core/src/input/mapping.ts +40 -0
- package/core/src/input/multi-sab-bridge.ts +276 -0
- package/core/src/input/multi-state.ts +54 -0
- package/core/src/input/sab-bridge.ts +71 -0
- package/core/src/input/state.ts +132 -0
- package/core/src/input/ui-router.ts +87 -0
- package/core/src/library/host.ts +317 -0
- package/core/src/library/index.ts +16 -0
- package/core/src/library/library.ts +292 -0
- package/core/src/material/compiler.ts +80 -0
- package/core/src/material/graph-bridge.ts +172 -0
- package/core/src/material/index.ts +9 -0
- package/core/src/material/library.ts +442 -0
- package/core/src/material/material.ts +156 -0
- package/core/src/material/variants.ts +61 -0
- package/core/src/math/mat4.ts +202 -0
- package/core/src/math/perlin-noise-2d.ts +77 -0
- package/core/src/math/perlin-noise-3d.ts +110 -0
- package/core/src/math/quat.ts +33 -0
- package/core/src/math/rng.ts +29 -0
- package/core/src/math/trig.ts +47 -0
- package/core/src/math/value-noise.ts +67 -0
- package/core/src/mesh/builder.ts +177 -0
- package/core/src/mesh/csg.ts +312 -0
- package/core/src/mesh/greased-line.ts +145 -0
- package/core/src/mesh/mesh.ts +17 -0
- package/core/src/mesh/parametric.ts +501 -0
- package/core/src/mesh/skinning.ts +67 -0
- package/core/src/mesh/vertex-layout.ts +91 -0
- package/core/src/module/cross-thread.ts +163 -0
- package/core/src/module/diagnostics.ts +123 -0
- package/core/src/module/host.ts +451 -0
- package/core/src/module/index.ts +22 -0
- package/core/src/module/module.ts +109 -0
- package/core/src/module/registry.ts +94 -0
- package/core/src/module/renderer-module.ts +247 -0
- package/core/src/module/ts-loader.ts +26 -0
- package/core/src/particles/compute-pass.ts +378 -0
- package/core/src/particles/emitter.ts +156 -0
- package/core/src/particles/index.ts +11 -0
- package/core/src/particles/particle-data.ts +47 -0
- package/core/src/particles/render-pass.ts +365 -0
- package/core/src/particles/simulator.ts +264 -0
- package/core/src/particles/system.ts +193 -0
- package/core/src/physics/body.ts +59 -0
- package/core/src/physics/ccd-heuristic.ts +55 -0
- package/core/src/physics/character.ts +282 -0
- package/core/src/physics/collider.ts +55 -0
- package/core/src/physics/collision-system.ts +91 -0
- package/core/src/physics/events.ts +133 -0
- package/core/src/physics/index.ts +34 -0
- package/core/src/physics/interface.ts +473 -0
- package/core/src/physics/interpolation-buffer.ts +192 -0
- package/core/src/physics/lifecycle.ts +87 -0
- package/core/src/physics/load-shedder.ts +164 -0
- package/core/src/physics/physics-accumulator.ts +101 -0
- package/core/src/physics/physics-system.ts +240 -0
- package/core/src/physics/ragdoll-presets.ts +272 -0
- package/core/src/physics/ragdoll-system.ts +220 -0
- package/core/src/physics/ragdoll.ts +182 -0
- package/core/src/physics/raycast.ts +143 -0
- package/core/src/physics/realm-manager.ts +499 -0
- package/core/src/physics/realm-worker-pool.ts +191 -0
- package/core/src/physics/realm-worker.ts +102 -0
- package/core/src/physics/realm.ts +189 -0
- package/core/src/physics/registry.ts +52 -0
- package/core/src/physics/safety.ts +247 -0
- package/core/src/physics/snapshot-manager.ts +141 -0
- package/core/src/physics/worker-protocol.ts +30 -0
- package/core/src/platform/bun-preload.ts +169 -0
- package/core/src/platform/css-stub.ts +4 -0
- package/core/src/platform/glob-polyfill.ts +124 -0
- package/core/src/platform/hidpi.ts +57 -0
- package/core/src/platform/render-surface.ts +77 -0
- package/core/src/platform/rpc.ts +98 -0
- package/core/src/platform/runtime.ts +132 -0
- package/core/src/plugin/context.ts +364 -0
- package/core/src/plugin/diagnostics.ts +61 -0
- package/core/src/plugin/discovery.ts +186 -0
- package/core/src/plugin/extension-loaders.ts +172 -0
- package/core/src/plugin/host.ts +659 -0
- package/core/src/plugin/index.ts +106 -0
- package/core/src/plugin/loader-asset.ts +124 -0
- package/core/src/plugin/loader-quickjs.ts +84 -0
- package/core/src/plugin/loader-wasm.ts +411 -0
- package/core/src/plugin/loader-worker.ts +261 -0
- package/core/src/plugin/manifest.ts +653 -0
- package/core/src/plugin/material-registry.ts +65 -0
- package/core/src/plugin/mcp-tools.ts +165 -0
- package/core/src/plugin/permissions.ts +175 -0
- package/core/src/plugin/quickjs-bridge.ts +294 -0
- package/core/src/plugin/registry.ts +107 -0
- package/core/src/plugin/sandbox-shim.ts +86 -0
- package/core/src/plugin/sandbox-worker.ts +215 -0
- package/core/src/plugin/wasm-abi.ts +152 -0
- package/core/src/plugin/wasm-worker.ts +229 -0
- package/core/src/plugin/workshop.ts +169 -0
- package/core/src/profiling/event-loop.ts +180 -0
- package/core/src/profiling/index.ts +69 -0
- package/core/src/profiling/iops/idb-patch.ts +242 -0
- package/core/src/profiling/iops/opfs-patch.ts +276 -0
- package/core/src/profiling/profiling-sab.ts +839 -0
- package/core/src/profiling/task-latency.ts +134 -0
- package/core/src/profiling/thread-metrics.ts +126 -0
- package/core/src/profiling/trace-event-writer.ts +205 -0
- package/core/src/profiling/warnings.ts +423 -0
- package/core/src/profiling/worker-prelude.ts +376 -0
- package/core/src/render/bind-group.ts +38 -0
- package/core/src/render/bindless/bindless-struct.ts +42 -0
- package/core/src/render/bindless/bindless.wgsl.ts +167 -0
- package/core/src/render/bindless/frame-bindings.ts +176 -0
- package/core/src/render/bindless/index.ts +10 -0
- package/core/src/render/bindless/material-manager.ts +257 -0
- package/core/src/render/bindless/texture-registry.ts +609 -0
- package/core/src/render/buffer.ts +82 -0
- package/core/src/render/camera-2d.ts +217 -0
- package/core/src/render/camera-controller.ts +440 -0
- package/core/src/render/camera.ts +409 -0
- package/core/src/render/canvas-resize-watcher.ts +46 -0
- package/core/src/render/compute-kernel.ts +172 -0
- package/core/src/render/constants.ts +2 -0
- package/core/src/render/device.ts +111 -0
- package/core/src/render/frame-graph.ts +768 -0
- package/core/src/render/frustum.ts +185 -0
- package/core/src/render/fullscreen-vs.ts +25 -0
- package/core/src/render/g-buffer.ts +186 -0
- package/core/src/render/game-renderer.ts +1452 -0
- package/core/src/render/gpu-driven/cull-pass.ts +394 -0
- package/core/src/render/gpu-driven/hzb.ts +215 -0
- package/core/src/render/gpu-driven/index.ts +7 -0
- package/core/src/render/gpu-driven/indirect-draw-pass.ts +324 -0
- package/core/src/render/gpu-driven/instance-buffer.ts +178 -0
- package/core/src/render/gpu-driven/mesh-table.ts +175 -0
- package/core/src/render/gpu-driven/passes/hzb-build-pass.ts +47 -0
- package/core/src/render/gpu-utils.ts +96 -0
- package/core/src/render/hdr.ts +88 -0
- package/core/src/render/hot-reload.ts +295 -0
- package/core/src/render/ibl-bind-group.ts +266 -0
- package/core/src/render/ibl.ts +180 -0
- package/core/src/render/index.ts +163 -0
- package/core/src/render/input-manager.ts +259 -0
- package/core/src/render/lighting/area-light.ts +196 -0
- package/core/src/render/lighting/cluster-grid.ts +288 -0
- package/core/src/render/lighting/cluster-types.ts +193 -0
- package/core/src/render/lighting/ies-parser.ts +162 -0
- package/core/src/render/lighting.ts +454 -0
- package/core/src/render/material-types/depth.wgsl +44 -0
- package/core/src/render/material-types/line.wgsl +50 -0
- package/core/src/render/material-types/matcap.wgsl +51 -0
- package/core/src/render/material-types/normal.wgsl +41 -0
- package/core/src/render/material-types/physical.wgsl +152 -0
- package/core/src/render/material-types/sprite.wgsl +46 -0
- package/core/src/render/material-types/sss.wgsl +79 -0
- package/core/src/render/material-types/toon.wgsl +79 -0
- package/core/src/render/passes/README.md +121 -0
- package/core/src/render/passes/atmosphere.ts +152 -0
- package/core/src/render/passes/cluster-lighting-pass.ts +476 -0
- package/core/src/render/passes/csm.ts +408 -0
- package/core/src/render/passes/cubemap-capture.ts +114 -0
- package/core/src/render/passes/debug-viz.ts +278 -0
- package/core/src/render/passes/debug.ts +538 -0
- package/core/src/render/passes/decal-mesh.ts +111 -0
- package/core/src/render/passes/decal-pass.ts +377 -0
- package/core/src/render/passes/deferred-lighting.ts +434 -0
- package/core/src/render/passes/depth-prepass.ts +163 -0
- package/core/src/render/passes/dome-360.ts +83 -0
- package/core/src/render/passes/fluid-render.ts +137 -0
- package/core/src/render/passes/gi-types.ts +118 -0
- package/core/src/render/passes/graph-compute.ts +300 -0
- package/core/src/render/passes/greased-line-pass.ts +232 -0
- package/core/src/render/passes/morph-target.ts +40 -0
- package/core/src/render/passes/opaque.ts +1028 -0
- package/core/src/render/passes/point-light-shadow.ts +266 -0
- package/core/src/render/passes/procedural-texture.ts +185 -0
- package/core/src/render/passes/reflection-probe.ts +140 -0
- package/core/src/render/passes/rsm-pass.ts +248 -0
- package/core/src/render/passes/sdf-text.ts +155 -0
- package/core/src/render/passes/shadow-map.ts +163 -0
- package/core/src/render/passes/shadow.ts +220 -0
- package/core/src/render/passes/skinning-vs.ts +121 -0
- package/core/src/render/passes/skinning.ts +326 -0
- package/core/src/render/passes/sky-dome.ts +404 -0
- package/core/src/render/passes/skybox.ts +162 -0
- package/core/src/render/passes/spot-light-shadow.ts +273 -0
- package/core/src/render/passes/terrain.ts +326 -0
- package/core/src/render/passes/transparent.ts +353 -0
- package/core/src/render/passes/underwater-fog.ts +185 -0
- package/core/src/render/passes/video-texture.ts +79 -0
- package/core/src/render/passes/volumetric-pass.ts +312 -0
- package/core/src/render/passes/volumetric-types.ts +171 -0
- package/core/src/render/passes/water.ts +753 -0
- package/core/src/render/pbr.ts +296 -0
- package/core/src/render/pipeline.ts +52 -0
- package/core/src/render/render-pass.ts +30 -0
- package/core/src/render/renderer-input-bus.ts +220 -0
- package/core/src/render/renderer-module-host.ts +623 -0
- package/core/src/render/resource-tracker.ts +67 -0
- package/core/src/render/sab-canvas-overlay.ts +122 -0
- package/core/src/render/shader-validator.ts +200 -0
- package/core/src/render/shaders/debug/debug-line.wgsl +27 -0
- package/core/src/render/shaders/debug/debug-point.wgsl +30 -0
- package/core/src/render/shaders/debug/debug-text.wgsl +42 -0
- package/core/src/render/shaders/debug-viz/aabb.wgsl +20 -0
- package/core/src/render/shaders/debug-viz/depth.wgsl +29 -0
- package/core/src/render/shaders/debug-viz/lod.wgsl +38 -0
- package/core/src/render/shaders/debug-viz/normals.wgsl +30 -0
- package/core/src/render/shaders/debug-viz/overdraw.wgsl +21 -0
- package/core/src/render/shaders/debug-viz/tangents.wgsl +29 -0
- package/core/src/render/shaders/debug-viz/wireframe.wgsl +33 -0
- package/core/src/render/shaders/shadow-chunks.ts +135 -0
- package/core/src/render/skybox.ts +146 -0
- package/core/src/render/splitscreen.ts +85 -0
- package/core/src/render/surface.ts +78 -0
- package/core/src/render/tonemap.ts +180 -0
- package/core/src/render/tracked-render-pass.ts +158 -0
- package/core/src/render/viewport.ts +109 -0
- package/core/src/render/wgsl-hmr.ts +70 -0
- package/core/src/sab/core-input-channel.ts +23 -0
- package/core/src/sab/define.ts +239 -0
- package/core/src/sab/errors.ts +76 -0
- package/core/src/sab/game-input.ts +343 -0
- package/core/src/sab/gamepad-devices.ts +259 -0
- package/core/src/sab/grid-buffer.ts +117 -0
- package/core/src/sab/input.ts +55 -0
- package/core/src/sab/layout.ts +219 -0
- package/core/src/sab/multi-input-channel.ts +50 -0
- package/core/src/sab/record-buffer.ts +155 -0
- package/core/src/sab/sab-polyfill.ts +56 -0
- package/core/src/sab/sim-channel.ts +453 -0
- package/core/src/sab/slot-buffer.ts +190 -0
- package/core/src/sab/types.ts +158 -0
- package/core/src/safety/bounds.ts +76 -0
- package/core/src/safety/json.ts +80 -0
- package/core/src/safety/path.ts +167 -0
- package/core/src/save/binary-format.ts +117 -0
- package/core/src/save/game-save-system.ts +161 -0
- package/core/src/save/grid-save-system.ts +158 -0
- package/core/src/save/migrate.ts +44 -0
- package/core/src/save/migration-registry.ts +94 -0
- package/core/src/save/persist-types.ts +208 -0
- package/core/src/save/schema.ts +46 -0
- package/core/src/save/serializer.ts +180 -0
- package/core/src/scene/builder.ts +65 -0
- package/core/src/scene/camera.ts +142 -0
- package/core/src/scene/checkpoint.ts +109 -0
- package/core/src/scene/game-loop.ts +198 -0
- package/core/src/scene/index.ts +24 -0
- package/core/src/scene/layer.ts +6 -0
- package/core/src/scene/prefab.ts +145 -0
- package/core/src/scene/scene-manager.ts +157 -0
- package/core/src/scene/scene.ts +192 -0
- package/core/src/scene/sky.ts +256 -0
- package/core/src/scene/spatial-grid.ts +176 -0
- package/core/src/scene/streaming.ts +304 -0
- package/core/src/scene/vegetation.ts +163 -0
- package/core/src/scene/world.ts +89 -0
- package/core/src/scripting/binding.ts +129 -0
- package/core/src/scripting/hot-reload.ts +144 -0
- package/core/src/scripting/script.ts +99 -0
- package/core/src/sim/hot-reload-pipeline.ts +49 -0
- package/core/src/sim/sim-state-helper.ts +47 -0
- package/core/src/sim/transient-state-registry.ts +77 -0
- package/core/src/sim/types.ts +113 -0
- package/core/src/sim/worker-loop.ts +222 -0
- package/core/src/stores/game-store.ts +158 -0
- package/core/src/stores/hot-reload-store.ts +44 -0
- package/core/src/telemetry/collector.ts +410 -0
- package/core/src/telemetry/gc-controller.ts +453 -0
- package/core/src/telemetry/gc-profiler.ts +126 -0
- package/core/src/telemetry/gc-tracker.ts +69 -0
- package/core/src/telemetry/gpu-profiler.ts +703 -0
- package/core/src/telemetry/gpu-resource-tracker.ts +285 -0
- package/core/src/telemetry/gpu-timer-pool.ts +234 -0
- package/core/src/telemetry/gpu-timer.ts +123 -0
- package/core/src/telemetry/gpu-timestamp-gate.ts +21 -0
- package/core/src/telemetry/reporter.ts +62 -0
- package/core/src/types/engine-types.ts +129 -0
- package/core/src/ui-font-scale.ts +112 -0
- package/core/src/util/broad-phase-grid.ts +82 -0
- package/core/src/util/feature-log.ts +220 -0
- package/core/src/util/index.ts +17 -0
- package/core/src/util/logger.ts +604 -0
- package/core/src/webgpu-destroy-augmentation.d.ts +25 -0
- package/core/src/worker/base-worker-host.ts +183 -0
- package/core/src/worker/buffer-sync.ts +550 -0
- package/core/src/worker/crash-recovery.ts +201 -0
- package/core/src/worker/entity-sim-worker-host.ts +190 -0
- package/core/src/worker/instrumented-worker-host.ts +171 -0
- package/core/src/worker/rpc.ts +253 -0
- package/core/src/worker/sim-worker-base.ts +913 -0
- package/core/src/worker/sim-worker-host.ts +358 -0
- package/core/src/worker/task-pool.ts +256 -0
- package/core/src/worker/task-worker.ts +83 -0
- package/libraries/asset-browser/src/index.ts +11 -0
- package/libraries/asset-browser/src/software-thumbnails.ts +949 -0
- package/libraries/audio-kira/README.md +17 -0
- package/libraries/audio-kira/native/Cargo.toml +14 -0
- package/libraries/audio-kira/native/src/engine.rs +27 -0
- package/libraries/audio-kira/native/src/lib.rs +185 -0
- package/libraries/audio-kira/native/src/mixer.rs +11 -0
- package/libraries/audio-kira/native/src/source.rs +80 -0
- package/libraries/audio-kira/src/backend.ts +469 -0
- package/libraries/audio-kira/src/index.ts +8 -0
- package/libraries/audio-kira/src/library.ts +82 -0
- package/libraries/blitz-ui/native/Cargo.toml +33 -0
- package/libraries/blitz-ui/native/assets/DejaVuSans.woff2 +0 -0
- package/libraries/blitz-ui/native/src/bridge.rs +100 -0
- package/libraries/blitz-ui/native/src/fonts.rs +20 -0
- package/libraries/blitz-ui/native/src/input.rs +80 -0
- package/libraries/blitz-ui/native/src/lib.rs +96 -0
- package/libraries/blitz-ui/native/src/shell.rs +301 -0
- package/libraries/blitz-ui/native-osr/Cargo.toml +24 -0
- package/libraries/blitz-ui/native-osr/src/events.rs +237 -0
- package/libraries/blitz-ui/native-osr/src/lib.rs +846 -0
- package/libraries/blitz-ui/native-osr/src/mutate.rs +385 -0
- package/libraries/blitz-ui/native-osr/src/resources.rs +45 -0
- package/libraries/blitz-ui/native-osr-spike.ts +70 -0
- package/libraries/blitz-ui/src/index.ts +15 -0
- package/libraries/blitz-ui/src/native-module.ts +221 -0
- package/libraries/blitz-ui/src/native-osr-ffi.ts +423 -0
- package/libraries/blitz-ui/src/router.ts +101 -0
- package/libraries/blitz-ui/src/shaders/ui-blit.wgsl.ts +35 -0
- package/libraries/blitz-ui/src/ui-blit-pass.ts +116 -0
- package/libraries/character/README.md +25 -0
- package/libraries/character/src/character-animator.ts +374 -0
- package/libraries/character/src/index.ts +24 -0
- package/libraries/character/src/loader.ts +375 -0
- package/libraries/character/src/locomotion.ts +124 -0
- package/libraries/character/src/preview.ts +287 -0
- package/libraries/devtools/README.md +51 -0
- package/libraries/devtools/native/Cargo.toml +12 -0
- package/libraries/devtools/native/src/lib.rs +1646 -0
- package/libraries/devtools/native/src/panels/console.rs +338 -0
- package/libraries/devtools/native/src/panels/dom_tree.rs +47 -0
- package/libraries/devtools/native/src/panels/generic.rs +317 -0
- package/libraries/devtools/native/src/panels/gpu.rs +107 -0
- package/libraries/devtools/native/src/panels/input.rs +88 -0
- package/libraries/devtools/native/src/panels/mod.rs +154 -0
- package/libraries/devtools/native/src/panels/perf_metrics.rs +91 -0
- package/libraries/devtools/native/src/panels/perf_recorder.rs +158 -0
- package/libraries/devtools/native/src/panels/scene.rs +72 -0
- package/libraries/devtools/native/src/state.rs +394 -0
- package/libraries/devtools/src/cdp-bridge.ts +349 -0
- package/libraries/devtools/src/collectors.ts +346 -0
- package/libraries/devtools/src/egui-ffi.ts +804 -0
- package/libraries/devtools/src/egui-renderer.ts +407 -0
- package/libraries/devtools/src/host.ts +363 -0
- package/libraries/devtools/src/index.ts +40 -0
- package/libraries/devtools/src/mirror.ts +794 -0
- package/libraries/devtools/src/native-providers.ts +744 -0
- package/libraries/devtools/src/verify.ts +167 -0
- package/libraries/devtools/src/web/browser.ts +102 -0
- package/libraries/devtools/src/web/host.ts +212 -0
- package/libraries/devtools/src/web/mirror.ts +430 -0
- package/libraries/devtools/src/web/server.ts +198 -0
- package/libraries/entities/README.md +17 -0
- package/libraries/entities/src/index.ts +4 -0
- package/libraries/entities/src/library.ts +64 -0
- package/libraries/entities/src/model-renderer.ts +1345 -0
- package/libraries/entities/src/shaders/model.wgsl +375 -0
- package/libraries/gamepad/src/feed-input.ts +83 -0
- package/libraries/gamepad/src/filters.ts +49 -0
- package/libraries/gamepad/src/glyphs.ts +93 -0
- package/libraries/gamepad/src/hub.ts +206 -0
- package/libraries/gamepad/src/index.ts +22 -0
- package/libraries/gamepad/src/library.ts +68 -0
- package/libraries/gamepad/src/source-sab.ts +70 -0
- package/libraries/gamepad/src/sysfs-enrich.ts +138 -0
- package/libraries/gaussian-splats/README.md +25 -0
- package/libraries/gaussian-splats/src/gpu-sort.ts +595 -0
- package/libraries/gaussian-splats/src/index.ts +31 -0
- package/libraries/gaussian-splats/src/library.ts +60 -0
- package/libraries/gaussian-splats/src/parser.ts +382 -0
- package/libraries/gaussian-splats/src/renderer.ts +427 -0
- package/libraries/gaussian-splats/src/sh-eval.ts +211 -0
- package/libraries/gaussian-splats/src/sorter.ts +110 -0
- package/libraries/gaussian-splats/src/tile-raster.ts +473 -0
- package/libraries/gpu-kernels/README.md +154 -0
- package/libraries/gpu-kernels/src/index.ts +11 -0
- package/libraries/gpu-kernels/src/kernel.ts +696 -0
- package/libraries/gpu-kernels/src/transpile.ts +958 -0
- package/libraries/html-ui-kit/src/behaviors.ts +250 -0
- package/libraries/html-ui-kit/src/components.ts +309 -0
- package/libraries/html-ui-kit/src/index.ts +30 -0
- package/libraries/html-ui-kit/src/nav/nav-controller.ts +238 -0
- package/libraries/html-ui-kit/src/nav/nav-input.ts +145 -0
- package/libraries/html-ui-kit/src/nav/osk.ts +153 -0
- package/libraries/html-ui-kit/src/nav/router.ts +167 -0
- package/libraries/html-ui-kit/src/nav/vcursor.ts +203 -0
- package/libraries/html-ui-kit/src/ten-foot.ts +182 -0
- package/libraries/html-ui-kit/src/theme.ts +238 -0
- package/libraries/lighting/README.md +20 -0
- package/libraries/lighting/src/index.ts +5 -0
- package/libraries/lighting/src/library.ts +66 -0
- package/libraries/lighting/src/light-system.ts +452 -0
- package/libraries/lighting/src/lighting-system.ts +68 -0
- package/libraries/lighting/src/shaders/light-debug.wgsl +30 -0
- package/libraries/marching-cubes/README.md +25 -0
- package/libraries/marching-cubes/src/chunked-field.ts +177 -0
- package/libraries/marching-cubes/src/deformation.ts +63 -0
- package/libraries/marching-cubes/src/generator.ts +407 -0
- package/libraries/marching-cubes/src/index.ts +32 -0
- package/libraries/marching-cubes/src/library.ts +80 -0
- package/libraries/marching-cubes/src/lod.ts +122 -0
- package/libraries/marching-cubes/src/marching-tetrahedra.ts +252 -0
- package/libraries/marching-cubes/src/sab.ts +45 -0
- package/libraries/marching-cubes/src/streaming-config.ts +48 -0
- package/libraries/marching-cubes/src/streaming-manager.ts +686 -0
- package/libraries/marching-cubes/src/tables.ts +323 -0
- package/libraries/marching-cubes/src/types.ts +18 -0
- package/libraries/models/README.md +25 -0
- package/libraries/models/src/bake-node-transforms.ts +171 -0
- package/libraries/models/src/codecs/basisu-codec.ts +180 -0
- package/libraries/models/src/codecs/draco-codec.ts +372 -0
- package/libraries/models/src/codecs/draco3dgltf.d.ts +40 -0
- package/libraries/models/src/codecs/index.ts +22 -0
- package/libraries/models/src/codecs/meshopt-codec.ts +91 -0
- package/libraries/models/src/codecs/registry.ts +221 -0
- package/libraries/models/src/dae.ts +753 -0
- package/libraries/models/src/default-part-selection.ts +56 -0
- package/libraries/models/src/dxf.ts +200 -0
- package/libraries/models/src/fbx/index.ts +457 -0
- package/libraries/models/src/fbx/interpreter/animation.ts +431 -0
- package/libraries/models/src/fbx/interpreter/blend-shapes.ts +323 -0
- package/libraries/models/src/fbx/interpreter/connections.ts +166 -0
- package/libraries/models/src/fbx/interpreter/diagnostics.ts +65 -0
- package/libraries/models/src/fbx/interpreter/geometry.ts +535 -0
- package/libraries/models/src/fbx/interpreter/global-settings.ts +92 -0
- package/libraries/models/src/fbx/interpreter/materials.ts +265 -0
- package/libraries/models/src/fbx/interpreter/nodes.ts +197 -0
- package/libraries/models/src/fbx/interpreter/skeleton.ts +476 -0
- package/libraries/models/src/fbx/parsers/binary-parser.ts +244 -0
- package/libraries/models/src/fbx/types.ts +119 -0
- package/libraries/models/src/gltf-extensions.ts +196 -0
- package/libraries/models/src/gltf.ts +935 -0
- package/libraries/models/src/index.ts +87 -0
- package/libraries/models/src/library.ts +75 -0
- package/libraries/models/src/loader.ts +265 -0
- package/libraries/models/src/material-adapter.ts +108 -0
- package/libraries/models/src/mini-xml.ts +174 -0
- package/libraries/models/src/normalize.ts +216 -0
- package/libraries/models/src/obj.ts +264 -0
- package/libraries/models/src/off.ts +95 -0
- package/libraries/models/src/ply.ts +348 -0
- package/libraries/models/src/pose-sampler.ts +219 -0
- package/libraries/models/src/sidecar/blender-extras.ts +37 -0
- package/libraries/models/src/sidecar/ddmeta.ts +114 -0
- package/libraries/models/src/sidecar/godot-import.ts +86 -0
- package/libraries/models/src/sidecar/resolver.ts +145 -0
- package/libraries/models/src/sidecar/types.ts +11 -0
- package/libraries/models/src/sidecar/unity-meta.ts +61 -0
- package/libraries/models/src/skeleton-synthesis.ts +132 -0
- package/libraries/models/src/stl.ts +149 -0
- package/libraries/models/src/threeds.ts +363 -0
- package/libraries/models/src/threemf.ts +201 -0
- package/libraries/models/src/types.ts +188 -0
- package/libraries/models/src/vtk.ts +218 -0
- package/libraries/models/test-fixtures/fbx/LICENSE-notice.txt +24 -0
- package/libraries/models/test-fixtures/fbx/README.md +46 -0
- package/libraries/models/test-fixtures/fbx/m01_cube_phong.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m02_geo_ngons.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m03_normals.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m04_material_properties.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m05_external_diffuse.png +0 -0
- package/libraries/models/test-fixtures/fbx/m05_textures.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m06_uv_transform.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m07_multimaterial.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m08_transforms.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m09_skinning.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m10_morph.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m11_node_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m12_skeletal_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m13_morph_anim.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m14_multiclip.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m15_camera_lights.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_axis_yup.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_axis_zup.fbx +0 -0
- package/libraries/models/test-fixtures/fbx/m16_units_254.fbx +0 -0
- package/libraries/navmesh/README.md +23 -0
- package/libraries/navmesh/src/crowd-system.ts +240 -0
- package/libraries/navmesh/src/debug-viz.ts +74 -0
- package/libraries/navmesh/src/index.ts +15 -0
- package/libraries/navmesh/src/library.ts +64 -0
- package/libraries/navmesh/src/navmesh-generator.ts +407 -0
- package/libraries/navmesh/src/navmesh.ts +159 -0
- package/libraries/navmesh/src/pathfinder.ts +252 -0
- package/libraries/navmesh/src/types.ts +63 -0
- package/libraries/networking/README.md +25 -0
- package/libraries/networking/src/authority.ts +121 -0
- package/libraries/networking/src/connection.ts +139 -0
- package/libraries/networking/src/discovery.ts +373 -0
- package/libraries/networking/src/enhanced-replication.ts +355 -0
- package/libraries/networking/src/epic-adapter.ts +129 -0
- package/libraries/networking/src/index.ts +45 -0
- package/libraries/networking/src/library.ts +69 -0
- package/libraries/networking/src/lobby.ts +108 -0
- package/libraries/networking/src/platform-adapter.ts +163 -0
- package/libraries/networking/src/rcs-adapter.ts +127 -0
- package/libraries/networking/src/remote-input.ts +188 -0
- package/libraries/networking/src/replication.ts +327 -0
- package/libraries/networking/src/rpc.ts +188 -0
- package/libraries/networking/src/session.ts +173 -0
- package/libraries/networking/src/transport.ts +201 -0
- package/libraries/networking/src/webrtc.ts +323 -0
- package/libraries/pathfinding-2d/README.md +15 -0
- package/libraries/pathfinding-2d/src/astar-grid.ts +312 -0
- package/libraries/pathfinding-2d/src/index.ts +11 -0
- package/libraries/persistence/README.md +25 -0
- package/libraries/persistence/src/autosave-manager.ts +112 -0
- package/libraries/persistence/src/binary-record-store.ts +244 -0
- package/libraries/persistence/src/browser.ts +51 -0
- package/libraries/persistence/src/file-save-store.ts +553 -0
- package/libraries/persistence/src/hash-utils.ts +59 -0
- package/libraries/persistence/src/index.ts +57 -0
- package/libraries/persistence/src/library.ts +74 -0
- package/libraries/persistence/src/mock-opfs.ts +141 -0
- package/libraries/persistence/src/node-fs-directory-handle.ts +161 -0
- package/libraries/persistence/src/opfs-save-store.ts +745 -0
- package/libraries/persistence/src/save-worker-proxy.ts +127 -0
- package/libraries/persistence/src/save-worker.ts +99 -0
- package/libraries/persistence/src/xxh3-ts.d.ts +10 -0
- package/libraries/physics-native/README.md +21 -0
- package/libraries/physics-native/native/Cargo.toml +11 -0
- package/libraries/physics-native/native/src/body.rs +403 -0
- package/libraries/physics-native/native/src/character.rs +226 -0
- package/libraries/physics-native/native/src/collider.rs +299 -0
- package/libraries/physics-native/native/src/joint.rs +81 -0
- package/libraries/physics-native/native/src/lib.rs +830 -0
- package/libraries/physics-native/native/src/query.rs +261 -0
- package/libraries/physics-native/native/src/realm.rs +33 -0
- package/libraries/physics-native/native/src/step.rs +54 -0
- package/libraries/physics-native/native/src/world.rs +148 -0
- package/libraries/physics-native/src/backend.ts +1046 -0
- package/libraries/physics-native/src/broadphase.ts +101 -0
- package/libraries/physics-native/src/ffi-spike-worker.ts +71 -0
- package/libraries/physics-native/src/index.ts +11 -0
- package/libraries/physics-native/src/library.ts +79 -0
- package/libraries/physics-native/src/narrowphase-shapes.ts +420 -0
- package/libraries/physics-native/src/narrowphase.ts +123 -0
- package/libraries/physics-native/src/solver.ts +141 -0
- package/libraries/physics-native/src/types.ts +76 -0
- package/libraries/physics-rapier/README.md +21 -0
- package/libraries/physics-rapier/bench/ffi-bench.ts +79 -0
- package/libraries/physics-rapier/bench/wasm-baseline.ts +90 -0
- package/libraries/physics-rapier/src/api.ts +452 -0
- package/libraries/physics-rapier/src/auto-backend.ts +59 -0
- package/libraries/physics-rapier/src/backend.ts +1207 -0
- package/libraries/physics-rapier/src/bulk-ops.ts +68 -0
- package/libraries/physics-rapier/src/ffi-backend.ts +21 -0
- package/libraries/physics-rapier/src/ffi-lib.ts +597 -0
- package/libraries/physics-rapier/src/index.ts +12 -0
- package/libraries/physics-rapier/src/library.ts +107 -0
- package/libraries/physics-rapier/src/rapier-backend.ts +1052 -0
- package/libraries/physics-rapier/src/realm-worker-script.ts +237 -0
- package/libraries/postfx/README.md +17 -0
- package/libraries/postfx/src/index.ts +19 -0
- package/libraries/postfx/src/library.ts +337 -0
- package/libraries/postfx/src/post-process-stack.ts +1752 -0
- package/libraries/postfx/src/shaders/post-process/afterimage.wgsl +14 -0
- package/libraries/postfx/src/shaders/post-process/ascii.wgsl +24 -0
- package/libraries/postfx/src/shaders/post-process/blit.wgsl +12 -0
- package/libraries/postfx/src/shaders/post-process/bloom-blur.wgsl +21 -0
- package/libraries/postfx/src/shaders/post-process/bloom-bright.wgsl +15 -0
- package/libraries/postfx/src/shaders/post-process/bloom-composite.wgsl +14 -0
- package/libraries/postfx/src/shaders/post-process/bloom-downsample.wgsl +60 -0
- package/libraries/postfx/src/shaders/post-process/bloom-soft.wgsl +46 -0
- package/libraries/postfx/src/shaders/post-process/bloom-upsample.wgsl +51 -0
- package/libraries/postfx/src/shaders/post-process/channel-mixer.wgsl +36 -0
- package/libraries/postfx/src/shaders/post-process/chromatic-aberration.wgsl +37 -0
- package/libraries/postfx/src/shaders/post-process/dithering.wgsl +64 -0
- package/libraries/postfx/src/shaders/post-process/dof.wgsl +131 -0
- package/libraries/postfx/src/shaders/post-process/edges.wgsl +45 -0
- package/libraries/postfx/src/shaders/post-process/fullscreen-vs.wgsl +15 -0
- package/libraries/postfx/src/shaders/post-process/fxaa.wgsl +102 -0
- package/libraries/postfx/src/shaders/post-process/gaussian-blur.wgsl +37 -0
- package/libraries/postfx/src/shaders/post-process/glow-blur.wgsl +33 -0
- package/libraries/postfx/src/shaders/post-process/glow-composite.wgsl +25 -0
- package/libraries/postfx/src/shaders/post-process/grain.wgsl +37 -0
- package/libraries/postfx/src/shaders/post-process/halftone.wgsl +80 -0
- package/libraries/postfx/src/shaders/post-process/highlight-blur.wgsl +33 -0
- package/libraries/postfx/src/shaders/post-process/highlight-composite.wgsl +32 -0
- package/libraries/postfx/src/shaders/post-process/lens-distortion.wgsl +43 -0
- package/libraries/postfx/src/shaders/post-process/lens-flare.wgsl +68 -0
- package/libraries/postfx/src/shaders/post-process/lut.wgsl +30 -0
- package/libraries/postfx/src/shaders/post-process/motion-blur.wgsl +55 -0
- package/libraries/postfx/src/shaders/post-process/occluder-chunk.wgsl +28 -0
- package/libraries/postfx/src/shaders/post-process/outline.wgsl +51 -0
- package/libraries/postfx/src/shaders/post-process/pixelation.wgsl +47 -0
- package/libraries/postfx/src/shaders/post-process/sharpen.wgsl +32 -0
- package/libraries/postfx/src/shaders/post-process/sobel.wgsl +26 -0
- package/libraries/postfx/src/shaders/post-process/split-tone.wgsl +36 -0
- package/libraries/postfx/src/shaders/post-process/ssao-blur.wgsl +38 -0
- package/libraries/postfx/src/shaders/post-process/ssao-composite.wgsl +25 -0
- package/libraries/postfx/src/shaders/post-process/ssao.wgsl +147 -0
- package/libraries/postfx/src/shaders/post-process/ssr.wgsl +155 -0
- package/libraries/postfx/src/shaders/post-process/taa.wgsl +98 -0
- package/libraries/postfx/src/shaders/post-process/tonemap.wgsl +40 -0
- package/libraries/postfx/src/shaders/post-process/watercolor.wgsl +82 -0
- package/libraries/postfx/src/shaders/post-process/white-balance.wgsl +41 -0
- package/libraries/recast/README.md +21 -0
- package/libraries/recast/src/backend.ts +150 -0
- package/libraries/recast/src/crowd-system.ts +352 -0
- package/libraries/recast/src/index.ts +12 -0
- package/libraries/recast/src/library.ts +91 -0
- package/libraries/recast/src/types.ts +86 -0
- package/libraries/sand/README.md +25 -0
- package/libraries/sand/src/cell.ts +43 -0
- package/libraries/sand/src/fields.ts +29 -0
- package/libraries/sand/src/fluid-grid.ts +261 -0
- package/libraries/sand/src/grid-dims.ts +74 -0
- package/libraries/sand/src/grid-sim-buffer.ts +271 -0
- package/libraries/sand/src/index.ts +81 -0
- package/libraries/sand/src/library.ts +92 -0
- package/libraries/sand/src/materials.ts +492 -0
- package/libraries/sand/src/palette.ts +93 -0
- package/libraries/sand/src/particles.ts +185 -0
- package/libraries/sand/src/render/sand-grid-pass.ts +430 -0
- package/libraries/sand/src/rng.ts +51 -0
- package/libraries/sand/src/rules/builtin-rules.ts +132 -0
- package/libraries/sand/src/rules/rule-compiler.ts +162 -0
- package/libraries/sand/src/rules/rule-engine.ts +231 -0
- package/libraries/sand/src/rules/rule-types.ts +87 -0
- package/libraries/sand/src/sand-step-pool.ts +484 -0
- package/libraries/sand/src/sand-step-worker.ts +97 -0
- package/libraries/sand/src/sand-world.ts +3227 -0
- package/libraries/stickman/README.md +25 -0
- package/libraries/stickman/src/index.ts +25 -0
- package/libraries/stickman/src/library.ts +61 -0
- package/libraries/stickman/src/proportions.ts +45 -0
- package/libraries/stickman/src/shader.ts +117 -0
- package/libraries/stickman/src/skeleton.ts +197 -0
- package/libraries/stickman/src/stickman-pass.ts +255 -0
- package/libraries/stickman/src/thick-line.ts +123 -0
- package/libraries/surface-nets/README.md +25 -0
- package/libraries/surface-nets/src/chunked-field.ts +14 -0
- package/libraries/surface-nets/src/deformation.ts +45 -0
- package/libraries/surface-nets/src/index.ts +36 -0
- package/libraries/surface-nets/src/library.ts +59 -0
- package/libraries/surface-nets/src/lod.ts +7 -0
- package/libraries/surface-nets/src/sab.ts +4 -0
- package/libraries/surface-nets/src/streaming-config.ts +5 -0
- package/libraries/surface-nets/src/streaming-manager.ts +15 -0
- package/libraries/surface-nets/src/surface-nets.ts +334 -0
- package/libraries/surface-nets/src/types.ts +23 -0
- package/libraries/water/README.md +25 -0
- package/libraries/water/src/buoyancy.ts +103 -0
- package/libraries/water/src/index.ts +43 -0
- package/libraries/water/src/library.ts +85 -0
- package/libraries/water/src/shore-damping.ts +93 -0
- package/libraries/water/src/water-buffer.ts +89 -0
- package/libraries/water/src/water-chunks.ts +13 -0
- package/libraries/water/src/water-physics.ts +376 -0
- package/libraries/water/src/water-renderer.ts +809 -0
- package/libraries/water/src/water-sab.ts +161 -0
- package/libraries/water/src/wave-sources.ts +84 -0
- package/libraries/weather/README.md +20 -0
- package/libraries/weather/src/index.ts +9 -0
- package/libraries/weather/src/library.ts +72 -0
- package/libraries/weather/src/types.ts +64 -0
- package/libraries/weather/src/weather-blend.ts +112 -0
- package/libraries/weather/src/weather-system.ts +271 -0
- package/libraries/weatherfx/README.md +24 -0
- package/libraries/weatherfx/src/cloud-provider.ts +31 -0
- package/libraries/weatherfx/src/cloud-system.ts +372 -0
- package/libraries/weatherfx/src/index.ts +9 -0
- package/libraries/weatherfx/src/library.ts +58 -0
- package/libraries/weatherfx/src/particle-system.ts +434 -0
- package/libraries/weatherfx/src/shaders/cloud.wgsl +111 -0
- package/libraries/weatherfx/src/shaders/particle-compute.wgsl +187 -0
- package/libraries/weatherfx/src/shaders/particle-render.wgsl +95 -0
- package/lint-plugin.mjs +41 -0
- package/mcp/src/client.ts +703 -0
- package/mcp/src/engine-context.ts +186 -0
- package/mcp/src/http-transport.ts +471 -0
- package/mcp/src/index.ts +13 -0
- package/mcp/src/prompts/add-entity.ts +42 -0
- package/mcp/src/prompts/create-scene.ts +44 -0
- package/mcp/src/prompts/debug-frame.ts +39 -0
- package/mcp/src/prompts/index.ts +12 -0
- package/mcp/src/resources/asset-list.ts +32 -0
- package/mcp/src/resources/checkpoint-list.ts +33 -0
- package/mcp/src/resources/entity-state.ts +55 -0
- package/mcp/src/resources/gpu-info.ts +103 -0
- package/mcp/src/resources/index.ts +57 -0
- package/mcp/src/resources/performance.ts +37 -0
- package/mcp/src/resources/scene-tree.ts +43 -0
- package/mcp/src/server.ts +375 -0
- package/mcp/src/tools/animation.ts +118 -0
- package/mcp/src/tools/asset.ts +161 -0
- package/mcp/src/tools/audio.ts +96 -0
- package/mcp/src/tools/build.ts +143 -0
- package/mcp/src/tools/camera.ts +167 -0
- package/mcp/src/tools/checkpoint.ts +100 -0
- package/mcp/src/tools/component.ts +199 -0
- package/mcp/src/tools/debug.ts +300 -0
- package/mcp/src/tools/entity.ts +361 -0
- package/mcp/src/tools/inspect.ts +210 -0
- package/mcp/src/tools/lighting.ts +263 -0
- package/mcp/src/tools/material.ts +243 -0
- package/mcp/src/tools/mesh.ts +239 -0
- package/mcp/src/tools/physics.ts +266 -0
- package/mcp/src/tools/scene.ts +153 -0
- package/mcp/src/tools/script.ts +122 -0
- package/mcp/src/types.ts +92 -0
- package/mcp/src/undo-redo.ts +77 -0
- package/modules/camera-controls/README.md +20 -0
- package/modules/camera-controls/src/index.ts +161 -0
- package/modules/controller-ui/src/index.ts +164 -0
- package/modules/devtools/README.md +25 -0
- package/modules/devtools/src/api.ts +461 -0
- package/modules/devtools/src/asset-utils.ts +105 -0
- package/modules/devtools/src/bind-debug-store.ts +48 -0
- package/modules/devtools/src/data-bridge.ts +286 -0
- package/modules/devtools/src/debug-overlay.ts +238 -0
- package/modules/devtools/src/debug-raycast.ts +328 -0
- package/modules/devtools/src/debug-store.ts +92 -0
- package/modules/devtools/src/debug-view-descriptors.ts +59 -0
- package/modules/devtools/src/doctor-panel.ts +182 -0
- package/modules/devtools/src/gizmo-module.ts +136 -0
- package/modules/devtools/src/graph-model.ts +509 -0
- package/modules/devtools/src/grid-renderer.ts +157 -0
- package/modules/devtools/src/height-ruler-renderer.ts +216 -0
- package/modules/devtools/src/index.ts +81 -0
- package/modules/devtools/src/init.ts +261 -0
- package/modules/devtools/src/label-overlay.ts +154 -0
- package/modules/devtools/src/material-stats-panel.ts +188 -0
- package/modules/devtools/src/profiling-bridge.ts +377 -0
- package/modules/devtools/src/profiling-hooks.ts +98 -0
- package/modules/devtools/src/renderer-adapter.ts +76 -0
- package/modules/devtools/src/scene-inspector.ts +637 -0
- package/modules/devtools/src/scene-store.ts +320 -0
- package/modules/devtools/src/scene-sync.ts +116 -0
- package/modules/devtools/src/shaders/transform-gizmo.wgsl +38 -0
- package/modules/devtools/src/sim-stats-panel.ts +272 -0
- package/modules/devtools/src/sim-stats-provider.ts +105 -0
- package/modules/devtools/src/skeleton-renderer.ts +351 -0
- package/modules/devtools/src/transform-gizmo.ts +928 -0
- package/modules/devtools/src/types.ts +290 -0
- package/modules/devtools/src/worker-expose.ts +67 -0
- package/modules/devtools/src/worker-sync.ts +137 -0
- package/modules/editor/src/commands/component.ts +156 -0
- package/modules/editor/src/commands/entity.ts +287 -0
- package/modules/editor/src/commands/hierarchy.ts +83 -0
- package/modules/editor/src/commands/index.ts +61 -0
- package/modules/editor/src/commands/prefab.ts +163 -0
- package/modules/editor/src/commands/registry.ts +331 -0
- package/modules/editor/src/commands/scene-io.ts +211 -0
- package/modules/editor/src/commands/selection.ts +73 -0
- package/modules/editor/src/commands/transform.ts +91 -0
- package/modules/editor/src/ddscene.ts +267 -0
- package/modules/editor/src/document.ts +123 -0
- package/modules/editor/src/editor-context.ts +172 -0
- package/modules/editor/src/index.ts +41 -0
- package/modules/editor/src/mcp.ts +103 -0
- package/modules/editor/src/module.ts +148 -0
- package/modules/editor/src/selection.ts +70 -0
- package/modules/editor/src/shell.ts +456 -0
- package/modules/editor/src/viewport.ts +199 -0
- package/modules/html-ui/src/composite.ts +135 -0
- package/modules/html-ui/src/create-html-ui.ts +188 -0
- package/modules/html-ui/src/doc-backend.ts +343 -0
- package/modules/html-ui/src/host.ts +646 -0
- package/modules/html-ui/src/index.ts +13 -0
- package/modules/html-ui/src/jsx-dev-runtime.ts +12 -0
- package/modules/html-ui/src/jsx-runtime.ts +126 -0
- package/modules/html-ui/src/protocol.ts +88 -0
- package/modules/html-ui/src/ui-worker.ts +16 -0
- package/modules/mcp/README.md +18 -0
- package/modules/mcp/src/index.ts +27 -0
- package/modules/movement-2d/README.md +19 -0
- package/modules/movement-2d/src/grid-character.ts +455 -0
- package/modules/movement-2d/src/index.ts +45 -0
- package/modules/movement-3d/README.md +21 -0
- package/modules/movement-3d/src/character-motor.ts +230 -0
- package/modules/movement-3d/src/index.ts +46 -0
- package/modules/native-osr/src/index.ts +22 -0
- package/modules/native-osr/src/input-router.ts +401 -0
- package/modules/native-osr/src/native-osr-manager.ts +247 -0
- package/modules/native-osr/src/native-texture-source.ts +86 -0
- package/modules/native-osr/src/types.ts +112 -0
- package/modules/native-osr/src/world-space-ui-pass.ts +351 -0
- package/modules/sailing/README.md +18 -0
- package/modules/sailing/src/index.ts +51 -0
- package/modules/terrain/README.md +18 -0
- package/modules/terrain/src/index.ts +78 -0
- package/modules/vitals/README.md +17 -0
- package/modules/vitals/src/index.ts +43 -0
- package/modules/vitals/src/vitals.ts +265 -0
- package/modules/xr/README.md +25 -0
- package/modules/xr/src/camera-rig.ts +146 -0
- package/modules/xr/src/debug.ts +59 -0
- package/modules/xr/src/frame-loop.ts +197 -0
- package/modules/xr/src/index.ts +16 -0
- package/modules/xr/src/input.ts +208 -0
- package/modules/xr/src/layer.ts +140 -0
- package/modules/xr/src/session.ts +156 -0
- package/modules/xr/src/sim-module.ts +23 -0
- package/modules/xr/src/types.ts +32 -0
- package/modules/xr/src/webxr.d.ts +182 -0
- package/modules/xr/src/xr-module.ts +179 -0
- package/package.json +232 -3
- package/shader-graph/src/chunks.ts +349 -0
- package/shader-graph/src/compiler.ts +732 -0
- package/shader-graph/src/compute-compiler.ts +453 -0
- package/shader-graph/src/compute-graph.ts +112 -0
- package/shader-graph/src/compute-profiles.ts +54 -0
- package/shader-graph/src/graph.ts +52 -0
- package/shader-graph/src/index.ts +53 -0
- package/shader-graph/src/profiles.ts +407 -0
- package/shader-graph/src/validator.ts +48 -0
- package/shader-graph/src/wgsl-binding-validator.ts +143 -0
- package/shader-graph/src/wgsl-struct-validator.ts +245 -0
- package/shader-graph/src/wgsl-struct.ts +397 -0
- package/test/src/index.ts +7 -0
- 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
|
+
}
|