@vgai/engine 0.5.5 → 0.5.6
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/dist/adapter/adapter-surface.d.ts +30 -0
- package/dist/adapter/adapter-surface.d.ts.map +1 -0
- package/dist/adapter/adapter-surface.js +18 -0
- package/dist/adapter/authoring.d.ts +597 -0
- package/dist/adapter/authoring.d.ts.map +1 -0
- package/dist/adapter/authoring.js +22 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts +77 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -0
- package/dist/adapter/colyseus-networking-adapter.js +76 -0
- package/dist/adapter/first-party-systems.d.ts +23 -0
- package/dist/adapter/first-party-systems.d.ts.map +1 -0
- package/dist/adapter/first-party-systems.js +196 -0
- package/dist/adapter/host-context.d.ts +194 -0
- package/dist/adapter/host-context.d.ts.map +1 -0
- package/dist/adapter/host-context.js +80 -0
- package/dist/adapter/index.d.ts +33 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +25 -0
- package/dist/adapter/ingest/game-contract.d.ts +53 -0
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -0
- package/dist/adapter/ingest/game-contract.js +30 -0
- package/dist/adapter/ingest/scene-capture.d.ts +121 -0
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -0
- package/dist/adapter/ingest/scene-capture.js +270 -0
- package/dist/adapter/ingest/structural-ids.d.ts +64 -0
- package/dist/adapter/ingest/structural-ids.d.ts.map +1 -0
- package/dist/adapter/ingest/structural-ids.js +110 -0
- package/dist/adapter/ingest/upstream-pin.d.ts +44 -0
- package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -0
- package/dist/adapter/ingest/upstream-pin.js +47 -0
- package/dist/adapter/loop-gate-report.d.ts +34 -0
- package/dist/adapter/loop-gate-report.d.ts.map +1 -0
- package/dist/adapter/loop-gate-report.js +37 -0
- package/dist/adapter/rapier-physics-adapter.d.ts +69 -0
- package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -0
- package/dist/adapter/rapier-physics-adapter.js +135 -0
- package/dist/adapter/root-adapter.d.ts +195 -0
- package/dist/adapter/root-adapter.d.ts.map +1 -0
- package/dist/adapter/root-adapter.js +25 -0
- package/dist/adapter/setup-three-root-adapter.d.ts +87 -0
- package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -0
- package/dist/adapter/setup-three-root-adapter.js +791 -0
- package/dist/adapter/system-adapter.d.ts +453 -0
- package/dist/adapter/system-adapter.d.ts.map +1 -0
- package/dist/adapter/system-adapter.js +13 -0
- package/dist/adapter/transform.d.ts +18 -0
- package/dist/adapter/transform.d.ts.map +1 -0
- package/dist/adapter/transform.js +1 -0
- package/dist/ai/navigation.d.ts +78 -0
- package/dist/ai/navigation.d.ts.map +1 -0
- package/dist/ai/navigation.js +152 -0
- package/dist/animation/anim-graph-types.d.ts +25 -0
- package/dist/animation/anim-graph-types.d.ts.map +1 -0
- package/dist/animation/anim-graph-types.js +1 -0
- package/dist/animation/animation-clock.d.ts +213 -0
- package/dist/animation/animation-clock.d.ts.map +1 -0
- package/dist/animation/animation-clock.js +301 -0
- package/dist/animation/blend-node.d.ts +22 -0
- package/dist/animation/blend-node.d.ts.map +1 -0
- package/dist/animation/blend-node.js +97 -0
- package/dist/animation/camera-ownership.d.ts +249 -0
- package/dist/animation/camera-ownership.d.ts.map +1 -0
- package/dist/animation/camera-ownership.js +169 -0
- package/dist/animation/cinematic-cues.d.ts +200 -0
- package/dist/animation/cinematic-cues.d.ts.map +1 -0
- package/dist/animation/cinematic-cues.js +213 -0
- package/dist/animation/clip-map.d.ts +12 -0
- package/dist/animation/clip-map.d.ts.map +1 -0
- package/dist/animation/clip-map.js +31 -0
- package/dist/animation/cubic-spline-interpolant.d.ts +54 -0
- package/dist/animation/cubic-spline-interpolant.d.ts.map +1 -0
- package/dist/animation/cubic-spline-interpolant.js +111 -0
- package/dist/animation/theatre-clock-binding.d.ts +89 -0
- package/dist/animation/theatre-clock-binding.d.ts.map +1 -0
- package/dist/animation/theatre-clock-binding.js +29 -0
- package/dist/animation/theatre-director.d.ts +277 -0
- package/dist/animation/theatre-director.d.ts.map +1 -0
- package/dist/animation/theatre-director.js +248 -0
- package/dist/animation/theatre-object-binding.d.ts +305 -0
- package/dist/animation/theatre-object-binding.d.ts.map +1 -0
- package/dist/animation/theatre-object-binding.js +351 -0
- package/dist/animation/xstate-animation-binding.d.ts +119 -0
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -0
- package/dist/animation/xstate-animation-binding.js +349 -0
- package/dist/animation/xstate-animation-meta.d.ts +266 -0
- package/dist/animation/xstate-animation-meta.d.ts.map +1 -0
- package/dist/animation/xstate-animation-meta.js +252 -0
- package/dist/asset-formats/camera.d.ts +15 -0
- package/dist/asset-formats/camera.d.ts.map +1 -0
- package/dist/asset-formats/camera.js +15 -0
- package/dist/asset-formats/collider.d.ts +21 -0
- package/dist/asset-formats/collider.d.ts.map +1 -0
- package/dist/asset-formats/collider.js +53 -0
- package/dist/asset-formats/index.d.ts +46 -0
- package/dist/asset-formats/index.d.ts.map +1 -0
- package/dist/asset-formats/index.js +36 -0
- package/dist/asset-formats/instances.d.ts +17 -0
- package/dist/asset-formats/instances.d.ts.map +1 -0
- package/dist/asset-formats/instances.js +31 -0
- package/dist/asset-formats/light.d.ts +21 -0
- package/dist/asset-formats/light.d.ts.map +1 -0
- package/dist/asset-formats/light.js +35 -0
- package/dist/asset-formats/material.d.ts +129 -0
- package/dist/asset-formats/material.d.ts.map +1 -0
- package/dist/asset-formats/material.js +88 -0
- package/dist/asset-formats/mesh.d.ts +22 -0
- package/dist/asset-formats/mesh.d.ts.map +1 -0
- package/dist/asset-formats/mesh.js +64 -0
- package/dist/asset-formats/parse.d.ts +20 -0
- package/dist/asset-formats/parse.d.ts.map +1 -0
- package/dist/asset-formats/parse.js +34 -0
- package/dist/asset-formats/particles.d.ts +1390 -0
- package/dist/asset-formats/particles.d.ts.map +1 -0
- package/dist/asset-formats/particles.js +319 -0
- package/dist/asset-formats/render-env.d.ts +387 -0
- package/dist/asset-formats/render-env.d.ts.map +1 -0
- package/dist/asset-formats/render-env.js +300 -0
- package/dist/asset-formats/tuples.d.ts +12 -0
- package/dist/asset-formats/tuples.d.ts.map +1 -0
- package/dist/asset-formats/tuples.js +14 -0
- package/dist/asset-loaders.d.ts +97 -0
- package/dist/asset-loaders.d.ts.map +1 -0
- package/dist/asset-loaders.js +209 -0
- package/dist/asset-parse-error.d.ts +24 -0
- package/dist/asset-parse-error.d.ts.map +1 -0
- package/dist/asset-parse-error.js +29 -0
- package/dist/asset-registry.d.ts +38 -0
- package/dist/asset-registry.d.ts.map +1 -0
- package/dist/asset-registry.js +66 -0
- package/dist/assets.d.ts +26 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +55 -0
- package/dist/audio/wav-encode.d.ts +38 -0
- package/dist/audio/wav-encode.d.ts.map +1 -0
- package/dist/audio/wav-encode.js +97 -0
- package/dist/canvas-react/engine-bridge.d.ts +45 -0
- package/dist/canvas-react/engine-bridge.d.ts.map +1 -0
- package/dist/canvas-react/engine-bridge.js +45 -0
- package/dist/canvas-react/index.d.ts +42 -0
- package/dist/canvas-react/index.d.ts.map +1 -0
- package/dist/canvas-react/index.js +41 -0
- package/dist/canvas-react/pixi-primitive.d.ts +85 -0
- package/dist/canvas-react/pixi-primitive.d.ts.map +1 -0
- package/dist/canvas-react/pixi-primitive.js +181 -0
- package/dist/canvas-react/pixi-react-adapter.d.ts +71 -0
- package/dist/canvas-react/pixi-react-adapter.d.ts.map +1 -0
- package/dist/canvas-react/pixi-react-adapter.js +229 -0
- package/dist/canvas-react/pixi-react-root-factory.d.ts +56 -0
- package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -0
- package/dist/canvas-react/pixi-react-root-factory.js +73 -0
- package/dist/canvas-react/world-context.d.ts +88 -0
- package/dist/canvas-react/world-context.d.ts.map +1 -0
- package/dist/canvas-react/world-context.js +212 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +8 -0
- package/dist/core/frame-pacing.d.ts +90 -0
- package/dist/core/frame-pacing.d.ts.map +1 -0
- package/dist/core/frame-pacing.js +89 -0
- package/dist/core/game-loop.d.ts +62 -0
- package/dist/core/game-loop.d.ts.map +1 -0
- package/dist/core/game-loop.js +189 -0
- package/dist/core/seeded-random.d.ts +69 -0
- package/dist/core/seeded-random.d.ts.map +1 -0
- package/dist/core/seeded-random.js +121 -0
- package/dist/core/sim-clock.d.ts +199 -0
- package/dist/core/sim-clock.d.ts.map +1 -0
- package/dist/core/sim-clock.js +291 -0
- package/dist/core/system-runner.d.ts +96 -0
- package/dist/core/system-runner.d.ts.map +1 -0
- package/dist/core/system-runner.js +253 -0
- package/dist/core/types.d.ts +111 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +25 -0
- package/dist/data/data-asset.d.ts +101 -0
- package/dist/data/data-asset.d.ts.map +1 -0
- package/dist/data/data-asset.js +122 -0
- package/dist/data/data-check-core.d.ts +109 -0
- package/dist/data/data-check-core.d.ts.map +1 -0
- package/dist/data/data-check-core.js +177 -0
- package/dist/data/data-ref.d.ts +79 -0
- package/dist/data/data-ref.d.ts.map +1 -0
- package/dist/data/data-ref.js +130 -0
- package/dist/data/vite-plugin-data.d.ts +79 -0
- package/dist/data/vite-plugin-data.d.ts.map +1 -0
- package/dist/data/vite-plugin-data.js +217 -0
- package/dist/defaults.d.ts +233 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +137 -0
- package/dist/dev/chrome-trace.d.ts +45 -0
- package/dist/dev/chrome-trace.d.ts.map +1 -0
- package/dist/dev/chrome-trace.js +114 -0
- package/dist/dev/console-bridge.d.ts +22 -0
- package/dist/dev/console-bridge.d.ts.map +1 -0
- package/dist/dev/console-bridge.js +71 -0
- package/dist/dev/debug-draw.d.ts +24 -0
- package/dist/dev/debug-draw.d.ts.map +1 -0
- package/dist/dev/debug-draw.js +73 -0
- package/dist/dev/instruments.d.ts +133 -0
- package/dist/dev/instruments.d.ts.map +1 -0
- package/dist/dev/instruments.js +231 -0
- package/dist/dev/logger.d.ts +30 -0
- package/dist/dev/logger.d.ts.map +1 -0
- package/dist/dev/logger.js +90 -0
- package/dist/dev/performance-profiler.d.ts +111 -0
- package/dist/dev/performance-profiler.d.ts.map +1 -0
- package/dist/dev/performance-profiler.js +215 -0
- package/dist/dev/render-debug-adapter.d.ts +57 -0
- package/dist/dev/render-debug-adapter.d.ts.map +1 -0
- package/dist/dev/render-debug-adapter.js +141 -0
- package/dist/dev/render-memory.d.ts +53 -0
- package/dist/dev/render-memory.d.ts.map +1 -0
- package/dist/dev/render-memory.js +190 -0
- package/dist/dev/webgl-frame-capture.d.ts +134 -0
- package/dist/dev/webgl-frame-capture.d.ts.map +1 -0
- package/dist/dev/webgl-frame-capture.js +270 -0
- package/dist/dev/webgl-gpu-timer.d.ts +8 -0
- package/dist/dev/webgl-gpu-timer.d.ts.map +1 -0
- package/dist/dev/webgl-gpu-timer.js +52 -0
- package/dist/ecs/scene-index.d.ts +179 -0
- package/dist/ecs/scene-index.d.ts.map +1 -0
- package/dist/ecs/scene-index.js +331 -0
- package/dist/ecs/scene-query.d.ts +6 -0
- package/dist/ecs/scene-query.d.ts.map +1 -0
- package/dist/ecs/scene-query.js +40 -0
- package/dist/ecs/user-data.d.ts +193 -0
- package/dist/ecs/user-data.d.ts.map +1 -0
- package/dist/ecs/user-data.js +145 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/input/input-manager.d.ts +944 -0
- package/dist/input/input-manager.d.ts.map +1 -0
- package/dist/input/input-manager.js +2209 -0
- package/dist/input/input-types.d.ts +174 -0
- package/dist/input/input-types.d.ts.map +1 -0
- package/dist/input/input-types.js +1 -0
- package/dist/input/prompt-labels.d.ts +21 -0
- package/dist/input/prompt-labels.d.ts.map +1 -0
- package/dist/input/prompt-labels.js +121 -0
- package/dist/input/rebind-controller.d.ts +59 -0
- package/dist/input/rebind-controller.d.ts.map +1 -0
- package/dist/input/rebind-controller.js +85 -0
- package/dist/input/schema.d.ts +16 -0
- package/dist/input/schema.d.ts.map +1 -0
- package/dist/input/schema.js +228 -0
- package/dist/loader.d.ts +76 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +114 -0
- package/dist/manifest/editor-port.d.ts +53 -0
- package/dist/manifest/editor-port.d.ts.map +1 -0
- package/dist/manifest/editor-port.js +67 -0
- package/dist/manifest/filename.d.ts +14 -0
- package/dist/manifest/filename.d.ts.map +1 -0
- package/dist/manifest/filename.js +43 -0
- package/dist/manifest/index.d.ts +6 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +9 -0
- package/dist/manifest/load-file.d.ts +7 -0
- package/dist/manifest/load-file.d.ts.map +1 -0
- package/dist/manifest/load-file.js +23 -0
- package/dist/manifest/load.d.ts +161 -0
- package/dist/manifest/load.d.ts.map +1 -0
- package/dist/manifest/load.js +269 -0
- package/dist/manifest/locate.d.ts +24 -0
- package/dist/manifest/locate.d.ts.map +1 -0
- package/dist/manifest/locate.js +48 -0
- package/dist/manifest/schema.d.ts +293 -0
- package/dist/manifest/schema.d.ts.map +1 -0
- package/dist/manifest/schema.js +633 -0
- package/dist/physics/collider-dimensions.d.ts +63 -0
- package/dist/physics/collider-dimensions.d.ts.map +1 -0
- package/dist/physics/collider-dimensions.js +98 -0
- package/dist/physics/collision-system.d.ts +41 -0
- package/dist/physics/collision-system.d.ts.map +1 -0
- package/dist/physics/collision-system.js +64 -0
- package/dist/physics/physics-registry.d.ts +45 -0
- package/dist/physics/physics-registry.d.ts.map +1 -0
- package/dist/physics/physics-registry.js +65 -0
- package/dist/physics/transform-writer.d.ts +16 -0
- package/dist/physics/transform-writer.d.ts.map +1 -0
- package/dist/physics/transform-writer.js +38 -0
- package/dist/pixi/authoring.d.ts +93 -0
- package/dist/pixi/authoring.d.ts.map +1 -0
- package/dist/pixi/authoring.js +207 -0
- package/dist/pixi/index.d.ts +16 -0
- package/dist/pixi/index.d.ts.map +1 -0
- package/dist/pixi/index.js +15 -0
- package/dist/pixi/ingest-iframe.d.ts +82 -0
- package/dist/pixi/ingest-iframe.d.ts.map +1 -0
- package/dist/pixi/ingest-iframe.js +166 -0
- package/dist/pixi/ingest.d.ts +97 -0
- package/dist/pixi/ingest.d.ts.map +1 -0
- package/dist/pixi/ingest.js +40 -0
- package/dist/pixi/physics-registry.d.ts +24 -0
- package/dist/pixi/physics-registry.d.ts.map +1 -0
- package/dist/pixi/physics-registry.js +38 -0
- package/dist/pixi/scene-capture.d.ts +43 -0
- package/dist/pixi/scene-capture.d.ts.map +1 -0
- package/dist/pixi/scene-capture.js +92 -0
- package/dist/pixi/system-adapters.d.ts +16 -0
- package/dist/pixi/system-adapters.d.ts.map +1 -0
- package/dist/pixi/system-adapters.js +37 -0
- package/dist/react/unmanaged-root-detector.d.ts +18 -0
- package/dist/react/unmanaged-root-detector.d.ts.map +1 -0
- package/dist/react/unmanaged-root-detector.js +70 -0
- package/dist/react/use-data.d.ts +60 -0
- package/dist/react/use-data.d.ts.map +1 -0
- package/dist/react/use-data.js +101 -0
- package/dist/react/use-selection.d.ts +54 -0
- package/dist/react/use-selection.d.ts.map +1 -0
- package/dist/react/use-selection.js +71 -0
- package/dist/react/world-state.d.ts +154 -0
- package/dist/react/world-state.d.ts.map +1 -0
- package/dist/react/world-state.js +220 -0
- package/dist/render/auto-batcher.d.ts +34 -0
- package/dist/render/auto-batcher.d.ts.map +1 -0
- package/dist/render/auto-batcher.js +140 -0
- package/dist/render/instance-mesh.d.ts +5 -0
- package/dist/render/instance-mesh.d.ts.map +1 -0
- package/dist/render/instance-mesh.js +19 -0
- package/dist/render/light-camera-factory.d.ts +24 -0
- package/dist/render/light-camera-factory.d.ts.map +1 -0
- package/dist/render/light-camera-factory.js +71 -0
- package/dist/render/lod.d.ts +13 -0
- package/dist/render/lod.d.ts.map +1 -0
- package/dist/render/lod.js +16 -0
- package/dist/render/material-factory.d.ts +15 -0
- package/dist/render/material-factory.d.ts.map +1 -0
- package/dist/render/material-factory.js +186 -0
- package/dist/render/mesh-shadow.d.ts +11 -0
- package/dist/render/mesh-shadow.d.ts.map +1 -0
- package/dist/render/mesh-shadow.js +19 -0
- package/dist/render/particles-factory.d.ts +99 -0
- package/dist/render/particles-factory.d.ts.map +1 -0
- package/dist/render/particles-factory.js +361 -0
- package/dist/render/render-batch-system.d.ts +33 -0
- package/dist/render/render-batch-system.d.ts.map +1 -0
- package/dist/render/render-batch-system.js +223 -0
- package/dist/render/render-features.d.ts +47 -0
- package/dist/render/render-features.d.ts.map +1 -0
- package/dist/render/render-features.js +105 -0
- package/dist/render/render-settings.d.ts +35 -0
- package/dist/render/render-settings.d.ts.map +1 -0
- package/dist/render/render-settings.js +51 -0
- package/dist/render/spark-renderer-lifecycle.d.ts +18 -0
- package/dist/render/spark-renderer-lifecycle.d.ts.map +1 -0
- package/dist/render/spark-renderer-lifecycle.js +57 -0
- package/dist/render/viewport-shading.d.ts +17 -0
- package/dist/render/viewport-shading.d.ts.map +1 -0
- package/dist/render/viewport-shading.js +87 -0
- package/dist/runtime/create-runtime.d.ts +259 -0
- package/dist/runtime/create-runtime.d.ts.map +1 -0
- package/dist/runtime/create-runtime.js +666 -0
- package/dist/runtime/debug-bridge.d.ts +288 -0
- package/dist/runtime/debug-bridge.d.ts.map +1 -0
- package/dist/runtime/debug-bridge.js +277 -0
- package/dist/runtime/debug-registry.d.ts +287 -0
- package/dist/runtime/debug-registry.d.ts.map +1 -0
- package/dist/runtime/debug-registry.js +528 -0
- package/dist/runtime/dev-layers.d.ts +34 -0
- package/dist/runtime/dev-layers.d.ts.map +1 -0
- package/dist/runtime/dev-layers.js +40 -0
- package/dist/runtime/frame-selector-cache.d.ts +41 -0
- package/dist/runtime/frame-selector-cache.d.ts.map +1 -0
- package/dist/runtime/frame-selector-cache.js +65 -0
- package/dist/runtime/game.d.ts +576 -0
- package/dist/runtime/game.d.ts.map +1 -0
- package/dist/runtime/game.js +1003 -0
- package/dist/runtime/gameplay-rng-trap.d.ts +47 -0
- package/dist/runtime/gameplay-rng-trap.d.ts.map +1 -0
- package/dist/runtime/gameplay-rng-trap.js +95 -0
- package/dist/runtime/input-router.d.ts +78 -0
- package/dist/runtime/input-router.d.ts.map +1 -0
- package/dist/runtime/input-router.js +171 -0
- package/dist/runtime/mount-game.d.ts +126 -0
- package/dist/runtime/mount-game.d.ts.map +1 -0
- package/dist/runtime/mount-game.js +159 -0
- package/dist/runtime/mount-manifest.d.ts +174 -0
- package/dist/runtime/mount-manifest.d.ts.map +1 -0
- package/dist/runtime/mount-manifest.js +304 -0
- package/dist/runtime/presentation.d.ts +85 -0
- package/dist/runtime/presentation.d.ts.map +1 -0
- package/dist/runtime/presentation.js +92 -0
- package/dist/runtime/render-audio-control.d.ts +126 -0
- package/dist/runtime/render-audio-control.d.ts.map +1 -0
- package/dist/runtime/render-audio-control.js +116 -0
- package/dist/runtime/render-control.d.ts +347 -0
- package/dist/runtime/render-control.d.ts.map +1 -0
- package/dist/runtime/render-control.js +317 -0
- package/dist/runtime/render-seed.d.ts +56 -0
- package/dist/runtime/render-seed.d.ts.map +1 -0
- package/dist/runtime/render-seed.js +73 -0
- package/dist/runtime/state-bridge.d.ts +60 -0
- package/dist/runtime/state-bridge.d.ts.map +1 -0
- package/dist/runtime/state-bridge.js +62 -0
- package/dist/runtime/types.d.ts +285 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/unexported-game-trap.d.ts +36 -0
- package/dist/runtime/unexported-game-trap.d.ts.map +1 -0
- package/dist/runtime/unexported-game-trap.js +80 -0
- package/dist/setup/setup-audio.d.ts +20 -0
- package/dist/setup/setup-audio.d.ts.map +1 -0
- package/dist/setup/setup-audio.js +41 -0
- package/dist/setup/setup-particles.d.ts +17 -0
- package/dist/setup/setup-particles.d.ts.map +1 -0
- package/dist/setup/setup-particles.js +16 -0
- package/dist/setup/setup-physics.d.ts +25 -0
- package/dist/setup/setup-physics.d.ts.map +1 -0
- package/dist/setup/setup-physics.js +51 -0
- package/dist/setup/setup-renderer.d.ts +82 -0
- package/dist/setup/setup-renderer.d.ts.map +1 -0
- package/dist/setup/setup-renderer.js +381 -0
- package/dist/world3d-react/engine-bridge.d.ts +47 -0
- package/dist/world3d-react/engine-bridge.d.ts.map +1 -0
- package/dist/world3d-react/engine-bridge.js +48 -0
- package/dist/world3d-react/index.d.ts +47 -0
- package/dist/world3d-react/index.d.ts.map +1 -0
- package/dist/world3d-react/index.js +46 -0
- package/dist/world3d-react/r3f-adapter.d.ts +51 -0
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -0
- package/dist/world3d-react/r3f-adapter.js +368 -0
- package/dist/world3d-react/r3f-root-factory.d.ts +62 -0
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -0
- package/dist/world3d-react/r3f-root-factory.js +79 -0
- package/dist/world3d-react/rapier-physics-bridge.d.ts +76 -0
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -0
- package/dist/world3d-react/rapier-physics-bridge.js +120 -0
- package/dist/world3d-react/renderer-config.d.ts +58 -0
- package/dist/world3d-react/renderer-config.d.ts.map +1 -0
- package/dist/world3d-react/renderer-config.js +96 -0
- package/dist/world3d-react/world-context.d.ts +123 -0
- package/dist/world3d-react/world-context.d.ts.map +1 -0
- package/dist/world3d-react/world-context.js +204 -0
- package/dist-config/config.js +303 -0
- package/dist-config/config.js.map +7 -0
- package/package.json +14 -3
- package/src/adapter/rapier-physics-adapter.ts +77 -34
- package/src/config.ts +8 -0
- package/src/core/frame-pacing.ts +26 -0
- package/src/core/game-loop.ts +4 -3
- package/src/dev/instruments.ts +302 -0
- package/src/setup/setup-renderer.ts +2 -0
- package/src/world3d-react/index.ts +9 -0
- package/src/world3d-react/rapier-physics-bridge.tsx +137 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { clampTimeScale, paceFrame, TIME_SCALE_RANGE } from './frame-pacing';
|
|
2
|
+
/**
|
|
3
|
+
* Fixed-timestep game loop with accumulator pattern — SIM at a fixed rate,
|
|
4
|
+
* PRESENTATION at the display's rate (WO-P13, `docs/WORK.md` §2 P13, which
|
|
5
|
+
* flipped the former D1 "render is fixed-rate too" decision this comment used
|
|
6
|
+
* to state).
|
|
7
|
+
*
|
|
8
|
+
* Per real (rAF) frame:
|
|
9
|
+
*
|
|
10
|
+
* 1. the accumulator absorbs the frame gap (clamped, `timeScale`d) and
|
|
11
|
+
* `config.update(fixedDt)` runs once per WHOLE fixed substep it can
|
|
12
|
+
* consume, up to `maxSubSteps` — unchanged, and still the only thing that
|
|
13
|
+
* advances gameplay, physics, `tick`/`simT` and the sim clock;
|
|
14
|
+
* 2. `config.render(alpha, displayDt)` runs EXACTLY ONCE, whatever step 1
|
|
15
|
+
* did — including zero times. `alpha` is `accumulator / fixedDt` after
|
|
16
|
+
* step 1 (see `core/frame-pacing.ts`): how far presentation sits past the
|
|
17
|
+
* last completed fixed state, in `[0, 1]`.
|
|
18
|
+
*
|
|
19
|
+
* So a 120 Hz display draws 120 frames a second off a 60 Hz simulation, and a
|
|
20
|
+
* frame that consumes zero substeps still presents — which is the whole point:
|
|
21
|
+
* before this, motion presented at SIM rate, so half of every 120 Hz display's
|
|
22
|
+
* frames were duplicates and there was nowhere at all to host
|
|
23
|
+
* `RenderStepped`-shaped per-display-frame code (camera polish, procedural
|
|
24
|
+
* sway). `Game.onRenderStep` (`runtime/game.ts`) is that host, driven from
|
|
25
|
+
* this callback.
|
|
26
|
+
*
|
|
27
|
+
* `config.render` is OPTIONAL, and the hosts that don't pass it keep the old
|
|
28
|
+
* shape exactly: the `render` phase then still runs inside `config.update` as
|
|
29
|
+
* the last phase of `PHASE_ORDER` (`runtime/game.ts` only skips the render
|
|
30
|
+
* phases when its caller asks it to). That is deliberate, because two paths
|
|
31
|
+
* must stay frame-exact and neither goes through the rAF arm at all:
|
|
32
|
+
* `externalDrive` capture/offline export (see `GameLoopConfig.externalDrive`)
|
|
33
|
+
* and `Game.runTicks`, which drives `runFrame` directly.
|
|
34
|
+
*/
|
|
35
|
+
export function createGameLoop(config) {
|
|
36
|
+
const fixedDt = config.fixedTimestep ?? 1 / 60;
|
|
37
|
+
// Also the accumulator's hard ceiling — `paceFrame` derives
|
|
38
|
+
// `fixedDt * maxSubSteps` from these two and clamps against it.
|
|
39
|
+
const maxSubSteps = config.maxSubSteps ?? 8;
|
|
40
|
+
// I2 (render-control runtime mode, see `GameLoopConfig.externalDrive`'s
|
|
41
|
+
// doc comment): in this mode `start()` below deliberately skips both the
|
|
42
|
+
// `requestAnimationFrame` arm AND the `visibilitychange` listener install —
|
|
43
|
+
// no wall-clock timing source ever exists for this loop instance, so
|
|
44
|
+
// `config.update` can only ever be invoked by an external driver calling
|
|
45
|
+
// into the render-control seam, never by this file's own `frame()`.
|
|
46
|
+
const externalDrive = config.externalDrive ?? false;
|
|
47
|
+
let accumulator = 0;
|
|
48
|
+
let lastTime = 0;
|
|
49
|
+
let running = false;
|
|
50
|
+
let rafId = 0;
|
|
51
|
+
let timeScale = 1.0;
|
|
52
|
+
// Idle throttle (T2.1): a hidden tab stops the loop outright; becoming
|
|
53
|
+
// visible again restarts it with the accumulator clock resynced to "now" —
|
|
54
|
+
// deliberately no catch-up burst for the wall-clock time spent hidden.
|
|
55
|
+
let hiddenPaused = false;
|
|
56
|
+
let visibilityListenerAttached = false;
|
|
57
|
+
function handleVisibilityChange() {
|
|
58
|
+
if (typeof document === 'undefined')
|
|
59
|
+
return;
|
|
60
|
+
if (document.hidden) {
|
|
61
|
+
if (running) {
|
|
62
|
+
running = false;
|
|
63
|
+
cancelAnimationFrame(rafId);
|
|
64
|
+
hiddenPaused = true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (hiddenPaused) {
|
|
68
|
+
hiddenPaused = false;
|
|
69
|
+
running = true;
|
|
70
|
+
lastTime = performance.now();
|
|
71
|
+
accumulator = 0;
|
|
72
|
+
rafId = requestAnimationFrame(frame);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function frame(currentTime) {
|
|
76
|
+
if (!running)
|
|
77
|
+
return;
|
|
78
|
+
rafId = requestAnimationFrame(frame);
|
|
79
|
+
// Convert to seconds
|
|
80
|
+
const rawDt = (currentTime - lastTime) / 1000;
|
|
81
|
+
lastTime = currentTime;
|
|
82
|
+
// All of the pacing arithmetic — frame-gap clamp, timeScale, the
|
|
83
|
+
// spiral-of-death accumulator clamp, the substep count and the
|
|
84
|
+
// interpolation alpha — lives in `core/frame-pacing.ts`, pure and unit
|
|
85
|
+
// tested. The accumulator is written back BEFORE the substeps run so a
|
|
86
|
+
// throwing `update` cannot make the loop re-consume time it already
|
|
87
|
+
// charged for.
|
|
88
|
+
const paced = paceFrame(accumulator, rawDt, timeScale, { fixedDt, maxSubSteps });
|
|
89
|
+
accumulator = paced.accumulator;
|
|
90
|
+
// Fixed-rate: gameplay/physics, once per consumed substep. Zero times on
|
|
91
|
+
// a frame that could not fill one.
|
|
92
|
+
for (let i = 0; i < paced.steps; i++)
|
|
93
|
+
config.update(fixedDt);
|
|
94
|
+
// Display-rate: exactly once per real frame, whatever the substep count
|
|
95
|
+
// was — see the module doc comment. Optional; a host that never passes
|
|
96
|
+
// `render` keeps rendering inside `update`'s phase list.
|
|
97
|
+
config.render?.(paced.alpha, paced.displayDt);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
start() {
|
|
101
|
+
if (running)
|
|
102
|
+
return;
|
|
103
|
+
running = true;
|
|
104
|
+
// A direct start() call can land while the loop is auto-hidden-paused
|
|
105
|
+
// (`hiddenPaused` true, `running` false, no rAF pending). Without this
|
|
106
|
+
// reset, a later visibilitychange-to-visible would see stale
|
|
107
|
+
// `hiddenPaused === true` and spawn a SECOND rAF chain on top of the
|
|
108
|
+
// one this call is about to start (doubled update()/render() overhead
|
|
109
|
+
// until the next stop() — no sim-speed effect, but wasted work).
|
|
110
|
+
hiddenPaused = false;
|
|
111
|
+
lastTime = performance.now();
|
|
112
|
+
accumulator = 0;
|
|
113
|
+
// External-drive mode (I2): never arm rAF and never install the
|
|
114
|
+
// visibilitychange auto-stop handler. `running` still flips `true`
|
|
115
|
+
// above (so `isRunning`/`stop()` behave normally for a caller that
|
|
116
|
+
// treats this loop as "started"), but nothing will ever call
|
|
117
|
+
// `frame()` — a headless/backgrounded capture page cannot have this
|
|
118
|
+
// loop silently killed by `document.hidden`, because there is no
|
|
119
|
+
// listener to fire in the first place.
|
|
120
|
+
if (externalDrive)
|
|
121
|
+
return;
|
|
122
|
+
// A play session can be started while the editor tab is ALREADY
|
|
123
|
+
// hidden. In that case no future visibilitychange-to-hidden event will
|
|
124
|
+
// arrive, and arming rAF would leave `running=true` forever while the
|
|
125
|
+
// browser executes zero callbacks. Classify and park the loop before
|
|
126
|
+
// arming it so status is truthful and visibility restore can resume it.
|
|
127
|
+
if (!visibilityListenerAttached && typeof document !== 'undefined') {
|
|
128
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
129
|
+
visibilityListenerAttached = true;
|
|
130
|
+
}
|
|
131
|
+
if (typeof document !== 'undefined' && document.hidden) {
|
|
132
|
+
running = false;
|
|
133
|
+
hiddenPaused = true;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
rafId = requestAnimationFrame(frame);
|
|
137
|
+
},
|
|
138
|
+
stop() {
|
|
139
|
+
running = false;
|
|
140
|
+
hiddenPaused = false;
|
|
141
|
+
cancelAnimationFrame(rafId);
|
|
142
|
+
if (visibilityListenerAttached && typeof document !== 'undefined') {
|
|
143
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
144
|
+
visibilityListenerAttached = false;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
get isRunning() {
|
|
148
|
+
return running;
|
|
149
|
+
},
|
|
150
|
+
/**
|
|
151
|
+
* Truthful liveness (issue #175 — see `GameLoopLiveness`'s doc comment
|
|
152
|
+
* in `core/types.ts` for the full contract). Unlike `isRunning` above,
|
|
153
|
+
* this DISTINGUISHES a hidden-tab pause from an actually-stopped loop:
|
|
154
|
+
* `hiddenPaused` is only ever set by `handleVisibilityChange` (T2.1's
|
|
155
|
+
* idle throttle) and cleared by `start()`/`stop()`/visibility-restore,
|
|
156
|
+
* so it can never be true at the same time `running` is true. An
|
|
157
|
+
* `externalDrive` loop never installs the visibility listener at all
|
|
158
|
+
* (see `start()` above), so `hiddenPaused` stays permanently `false` for
|
|
159
|
+
* it — it only ever reports `'running'` or `'stopped'`.
|
|
160
|
+
*/
|
|
161
|
+
get liveness() {
|
|
162
|
+
if (hiddenPaused)
|
|
163
|
+
return 'hidden-paused';
|
|
164
|
+
return running ? 'running' : 'stopped';
|
|
165
|
+
},
|
|
166
|
+
set timeScale(value) {
|
|
167
|
+
const clamped = clampTimeScale(value);
|
|
168
|
+
if (clamped !== value) {
|
|
169
|
+
console.warn(`[game-loop] timeScale ${value} is out of range ` +
|
|
170
|
+
`[${TIME_SCALE_RANGE.min}, ${TIME_SCALE_RANGE.max}]; clamped to ${clamped}.`);
|
|
171
|
+
}
|
|
172
|
+
timeScale = clamped;
|
|
173
|
+
},
|
|
174
|
+
get timeScale() {
|
|
175
|
+
return timeScale;
|
|
176
|
+
},
|
|
177
|
+
/**
|
|
178
|
+
* The fixed substep timestep (seconds) this loop's accumulator consumes
|
|
179
|
+
* per `config.update()` call (`config.fixedTimestep ?? 1/60`) — read by
|
|
180
|
+
* `Game.runTicks` (D15, `runtime/game.ts`) so a synchronous fast-forward
|
|
181
|
+
* burst drives `game.runFrame` with the SAME `fixedDt` this loop's own
|
|
182
|
+
* rAF-driven accumulator would have used, without exposing (or
|
|
183
|
+
* `runTicks` needing) any other internal loop state.
|
|
184
|
+
*/
|
|
185
|
+
get fixedDt() {
|
|
186
|
+
return fixedDt;
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seeded-random core (T-D15.1). `ctx.random` is a game-scoped PRNG with
|
|
3
|
+
* NAMED STREAMS: calling the object itself (`ctx.random()`) draws from the
|
|
4
|
+
* `'gameplay'` stream; `ctx.random.stream('vfx')` (or any other name) derives
|
|
5
|
+
* an INDEPENDENT generator, so a cosmetic/VFX draw can never perturb the
|
|
6
|
+
* gameplay draw order — the classic replay-drift trap option B ("one
|
|
7
|
+
* game-scoped PRNG only") would fall into (see the design doc's §2.a option
|
|
8
|
+
* table).
|
|
9
|
+
*
|
|
10
|
+
* Each stream is seeded from `fnv1a(name) ^ rootSeed` — deterministic given
|
|
11
|
+
* the root seed, independent of draw order across streams (drawing from
|
|
12
|
+
* `'vfx'` never advances `'gameplay'`'s generator, since they are two
|
|
13
|
+
* separate mulberry32 instances). `reseed(seed)` re-derives every stream
|
|
14
|
+
* that has EVER been asked for via `.stream(name)` (including the implicit
|
|
15
|
+
* `'gameplay'` stream `ctx.random()` itself draws from) — future draws only:
|
|
16
|
+
* numbers already returned before a `reseed()` call are not (and cannot be)
|
|
17
|
+
* un-returned; this matches `play.seed.set`'s documented semantics (T-D15.6).
|
|
18
|
+
*
|
|
19
|
+
* Deliberately reuses {@link createMulberry32} FROM `runtime/render-seed.ts`
|
|
20
|
+
* (the render/capture door's own generator) rather than duplicating the
|
|
21
|
+
* algorithm — `render-seed.ts` stays a zero-import module itself (this file
|
|
22
|
+
* imports FROM it, never the reverse), so this is a dependency-clean
|
|
23
|
+
* direction: the render/capture door and the gameplay determinism door share
|
|
24
|
+
* one PRNG implementation without either depending on the other's door
|
|
25
|
+
* logic (§2.a: "Kept as-is for the render/capture door only" — the two doors
|
|
26
|
+
* stay orthogonal; only the generator function itself is shared).
|
|
27
|
+
*/
|
|
28
|
+
/** The name `ctx.random()` (called with no `.stream(...)`) draws from. */
|
|
29
|
+
export declare const GAMEPLAY_STREAM = "gameplay";
|
|
30
|
+
/** The fixed fallback seed a `SeededRandom` boots with when no explicit seed
|
|
31
|
+
* is supplied (mirrors `render-seed.ts`'s own `DEFAULT_RENDER_SEED` in
|
|
32
|
+
* spirit — an arbitrary but fixed golden-ratio-derived constant, never
|
|
33
|
+
* wall-clock/`Math.random`-derived, so an UNDECLARED project's `ctx.random`
|
|
34
|
+
* is still perfectly reproducible on its own terms even though nothing in
|
|
35
|
+
* the manifest asked for that — it just isn't a documented CONTRACT until
|
|
36
|
+
* `determinism.seededRandom` is declared, per T4.1's "no dead field" rule). */
|
|
37
|
+
export declare const DEFAULT_SEEDED_RANDOM_SEED = 2654435769;
|
|
38
|
+
/**
|
|
39
|
+
* The `ctx.random` surface (`GameContext.random`, `runtime/types.ts`).
|
|
40
|
+
* Callable (draws from the `'gameplay'` stream), plus:
|
|
41
|
+
* - `stream(name)` — an independent named generator, `[0, 1)` floats, same
|
|
42
|
+
* call signature as `Math.random`/the bare `SeededRandom` call itself.
|
|
43
|
+
* - `reseed(seed)` — re-derive every stream ever requested so far (future
|
|
44
|
+
* draws only) from a NEW root seed.
|
|
45
|
+
* - `seed` — the CURRENT root seed (reflects the last `reseed()` call, if
|
|
46
|
+
* any) — a live getter, not a value snapshotted at construction.
|
|
47
|
+
*/
|
|
48
|
+
export interface SeededRandom {
|
|
49
|
+
(): number;
|
|
50
|
+
stream(name: string): () => number;
|
|
51
|
+
reseed(seed: number): void;
|
|
52
|
+
readonly seed: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Construct a fresh, game-scoped seeded-random surface. `initialSeed` is
|
|
56
|
+
* coerced to an unsigned 32-bit integer the same way `render-seed.ts`'s
|
|
57
|
+
* `installDeterministicRandom` does (`>>> 0`), so any finite JS number is
|
|
58
|
+
* accepted.
|
|
59
|
+
*/
|
|
60
|
+
export declare function createSeededRandom(initialSeed: number): SeededRandom;
|
|
61
|
+
/** Called once by `createGame`, right after both the seeded-random surface
|
|
62
|
+
* and the Game shell object exist — mirrors `registerDebugRegistry`. */
|
|
63
|
+
export declare function registerSeededRandom(owner: object, random: SeededRandom): void;
|
|
64
|
+
/** The game-scoped `SeededRandom` backing every world's `ctx.random` — `null`
|
|
65
|
+
* for an owner built without one (there is always one for every real
|
|
66
|
+
* `createGame` call; `null` only for a hand-built `Game`-shaped stand-in a
|
|
67
|
+
* test constructs without going through `createGame`). */
|
|
68
|
+
export declare function getSeededRandom(owner: object): SeededRandom | null;
|
|
69
|
+
//# sourceMappingURL=seeded-random.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeded-random.d.ts","sourceRoot":"","sources":["../../src/core/seeded-random.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,0EAA0E;AAC1E,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C;;;;;;gFAMgF;AAChF,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,MAAM,CAAC;IACX,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CAAC;IACnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAsBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAgDpE;AAeD;yEACyE;AACzE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAE9E;AAED;;;2DAG2D;AAC3D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAElE"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seeded-random core (T-D15.1). `ctx.random` is a game-scoped PRNG with
|
|
3
|
+
* NAMED STREAMS: calling the object itself (`ctx.random()`) draws from the
|
|
4
|
+
* `'gameplay'` stream; `ctx.random.stream('vfx')` (or any other name) derives
|
|
5
|
+
* an INDEPENDENT generator, so a cosmetic/VFX draw can never perturb the
|
|
6
|
+
* gameplay draw order — the classic replay-drift trap option B ("one
|
|
7
|
+
* game-scoped PRNG only") would fall into (see the design doc's §2.a option
|
|
8
|
+
* table).
|
|
9
|
+
*
|
|
10
|
+
* Each stream is seeded from `fnv1a(name) ^ rootSeed` — deterministic given
|
|
11
|
+
* the root seed, independent of draw order across streams (drawing from
|
|
12
|
+
* `'vfx'` never advances `'gameplay'`'s generator, since they are two
|
|
13
|
+
* separate mulberry32 instances). `reseed(seed)` re-derives every stream
|
|
14
|
+
* that has EVER been asked for via `.stream(name)` (including the implicit
|
|
15
|
+
* `'gameplay'` stream `ctx.random()` itself draws from) — future draws only:
|
|
16
|
+
* numbers already returned before a `reseed()` call are not (and cannot be)
|
|
17
|
+
* un-returned; this matches `play.seed.set`'s documented semantics (T-D15.6).
|
|
18
|
+
*
|
|
19
|
+
* Deliberately reuses {@link createMulberry32} FROM `runtime/render-seed.ts`
|
|
20
|
+
* (the render/capture door's own generator) rather than duplicating the
|
|
21
|
+
* algorithm — `render-seed.ts` stays a zero-import module itself (this file
|
|
22
|
+
* imports FROM it, never the reverse), so this is a dependency-clean
|
|
23
|
+
* direction: the render/capture door and the gameplay determinism door share
|
|
24
|
+
* one PRNG implementation without either depending on the other's door
|
|
25
|
+
* logic (§2.a: "Kept as-is for the render/capture door only" — the two doors
|
|
26
|
+
* stay orthogonal; only the generator function itself is shared).
|
|
27
|
+
*/
|
|
28
|
+
import { createMulberry32 } from '../runtime/render-seed';
|
|
29
|
+
/** The name `ctx.random()` (called with no `.stream(...)`) draws from. */
|
|
30
|
+
export const GAMEPLAY_STREAM = 'gameplay';
|
|
31
|
+
/** The fixed fallback seed a `SeededRandom` boots with when no explicit seed
|
|
32
|
+
* is supplied (mirrors `render-seed.ts`'s own `DEFAULT_RENDER_SEED` in
|
|
33
|
+
* spirit — an arbitrary but fixed golden-ratio-derived constant, never
|
|
34
|
+
* wall-clock/`Math.random`-derived, so an UNDECLARED project's `ctx.random`
|
|
35
|
+
* is still perfectly reproducible on its own terms even though nothing in
|
|
36
|
+
* the manifest asked for that — it just isn't a documented CONTRACT until
|
|
37
|
+
* `determinism.seededRandom` is declared, per T4.1's "no dead field" rule). */
|
|
38
|
+
export const DEFAULT_SEEDED_RANDOM_SEED = 0x9e3779b9;
|
|
39
|
+
/** fnv1a-32, the standard non-cryptographic string hash — used only to mix a
|
|
40
|
+
* stream NAME into the root seed, never as a determinism primitive on its
|
|
41
|
+
* own (mulberry32 is still what actually produces the `[0,1)` sequence). */
|
|
42
|
+
function fnv1a32(str) {
|
|
43
|
+
let hash = 0x811c9dc5;
|
|
44
|
+
for (let i = 0; i < str.length; i++) {
|
|
45
|
+
hash ^= str.charCodeAt(i);
|
|
46
|
+
hash = Math.imul(hash, 0x01000193);
|
|
47
|
+
}
|
|
48
|
+
return hash >>> 0;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Construct a fresh, game-scoped seeded-random surface. `initialSeed` is
|
|
52
|
+
* coerced to an unsigned 32-bit integer the same way `render-seed.ts`'s
|
|
53
|
+
* `installDeterministicRandom` does (`>>> 0`), so any finite JS number is
|
|
54
|
+
* accepted.
|
|
55
|
+
*/
|
|
56
|
+
export function createSeededRandom(initialSeed) {
|
|
57
|
+
let rootSeed = initialSeed >>> 0;
|
|
58
|
+
const streams = new Map();
|
|
59
|
+
function deriveStreamSeed(name) {
|
|
60
|
+
return (fnv1a32(name) ^ rootSeed) >>> 0;
|
|
61
|
+
}
|
|
62
|
+
function getBox(name) {
|
|
63
|
+
let box = streams.get(name);
|
|
64
|
+
if (!box) {
|
|
65
|
+
box = { gen: createMulberry32(deriveStreamSeed(name)) };
|
|
66
|
+
streams.set(name, box);
|
|
67
|
+
}
|
|
68
|
+
return box;
|
|
69
|
+
}
|
|
70
|
+
function streamFn(name) {
|
|
71
|
+
const box = getBox(name);
|
|
72
|
+
return () => box.gen();
|
|
73
|
+
}
|
|
74
|
+
const random = (() => getBox(GAMEPLAY_STREAM).gen());
|
|
75
|
+
Object.defineProperties(random, {
|
|
76
|
+
stream: { value: streamFn, enumerable: true },
|
|
77
|
+
reseed: {
|
|
78
|
+
value: (seed) => {
|
|
79
|
+
rootSeed = seed >>> 0;
|
|
80
|
+
// Re-derive every stream ANYONE has ever asked for (including the
|
|
81
|
+
// implicit 'gameplay' stream, once `random()` or
|
|
82
|
+
// `.stream('gameplay')` has been called at least once) — a handle a
|
|
83
|
+
// caller stashed before this call keeps its identity but now reads
|
|
84
|
+
// from the freshly-seeded generator on its NEXT call (future draws
|
|
85
|
+
// only, per the design doc's `play.seed.set` semantics).
|
|
86
|
+
for (const [name, box] of streams) {
|
|
87
|
+
box.gen = createMulberry32(deriveStreamSeed(name));
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
enumerable: true,
|
|
91
|
+
},
|
|
92
|
+
seed: {
|
|
93
|
+
get: () => rootSeed,
|
|
94
|
+
enumerable: true,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
return random;
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Game-scoped registry — mirrors `runtime/debug-registry.ts`'s
|
|
101
|
+
// `registerDebugRegistry`/`getDebugRegistry` WeakMap pattern exactly, but
|
|
102
|
+
// keyed on a bare `object` (not `Game`) so this module never needs to import
|
|
103
|
+
// `runtime/game.ts` even as a type — `core/` stays independent of `runtime/`
|
|
104
|
+
// except for the one explicit, documented `render-seed.ts` reuse above.
|
|
105
|
+
// `createGame` (`runtime/game.ts`) is the one real registrant, passing itself
|
|
106
|
+
// (the `GameInternal` shell) as the key, exactly like it does for
|
|
107
|
+
// `registerDebugRegistry(gameInternal, debugRegistry)`.
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
const registryByOwner = new WeakMap();
|
|
110
|
+
/** Called once by `createGame`, right after both the seeded-random surface
|
|
111
|
+
* and the Game shell object exist — mirrors `registerDebugRegistry`. */
|
|
112
|
+
export function registerSeededRandom(owner, random) {
|
|
113
|
+
registryByOwner.set(owner, random);
|
|
114
|
+
}
|
|
115
|
+
/** The game-scoped `SeededRandom` backing every world's `ctx.random` — `null`
|
|
116
|
+
* for an owner built without one (there is always one for every real
|
|
117
|
+
* `createGame` call; `null` only for a hand-built `Game`-shaped stand-in a
|
|
118
|
+
* test constructs without going through `createGame`). */
|
|
119
|
+
export function getSeededRandom(owner) {
|
|
120
|
+
return registryByOwner.get(owner) ?? null;
|
|
121
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `SimClock` — P3, the sim-time scheduler (punchlist item P3, rung 4).
|
|
3
|
+
*
|
|
4
|
+
* `await` **is** the scheduler; JS already won that argument. The only thing
|
|
5
|
+
* missing from the engine was *clock binding*: a `delay(seconds)` that resolves
|
|
6
|
+
* on the fixed loop's own accumulator instead of on wall time, so a timer
|
|
7
|
+
* pauses when the game pauses, steps when the game steps, and reproduces
|
|
8
|
+
* exactly in an offline export. That, plus a timed-dispose helper for debris,
|
|
9
|
+
* is this whole module.
|
|
10
|
+
*
|
|
11
|
+
* ## This is NOT `AnimationClock`, and it never will be
|
|
12
|
+
*
|
|
13
|
+
* `animation/animation-clock.ts` is a seekable **cinematic** clock: it plays,
|
|
14
|
+
* pauses, loops, runs in reverse and is `seek()`ed to arbitrary times so
|
|
15
|
+
* Theatre/GSAP timelines and cue evaluators can be scrubbed. `SimClock` is the
|
|
16
|
+
* **monotonic gameplay clock**: it only ever moves forward, one fixed substep
|
|
17
|
+
* at a time, and there is no seek. Both live in one engine because they answer
|
|
18
|
+
* different questions — "where is the cinematic playhead" versus "how much
|
|
19
|
+
* gameplay time has actually elapsed".
|
|
20
|
+
*
|
|
21
|
+
* The reason they can never merge is a fact about Promises: **a Promise cannot
|
|
22
|
+
* un-resolve.** Scrub a seekable clock backwards past a `delay` that already
|
|
23
|
+
* fired and there is no correct behavior — you cannot un-await it. So `delay`
|
|
24
|
+
* binds to the clock that cannot rewind, and cinematic scrubbing stays with the
|
|
25
|
+
* clock that can.
|
|
26
|
+
*
|
|
27
|
+
* ## Two forms, because sync and async are genuinely different here
|
|
28
|
+
*
|
|
29
|
+
* `Game.runTicks` is **synchronous** (`runtime/game.ts`): it runs N substeps in
|
|
30
|
+
* one straight-line loop. A `delay` continuation is a microtask, so under
|
|
31
|
+
* `runTicks` — and under an offline export driven the same way — it does *not*
|
|
32
|
+
* interleave between ticks; it runs when the caller's stack unwinds, after the
|
|
33
|
+
* whole run. Under the ordinary rAF loop each frame yields, so the difference
|
|
34
|
+
* is invisible.
|
|
35
|
+
*
|
|
36
|
+
* Therefore:
|
|
37
|
+
*
|
|
38
|
+
* - `await clock.delay(n)` is the **ergonomic** form. Use it for gameplay.
|
|
39
|
+
* - `clock.after(n, fn)` is the **exact** form: `fn` is invoked inline, during
|
|
40
|
+
* the flush of the exact due tick, under every driver — rAF, `runTicks`,
|
|
41
|
+
* `step()`, offline export.
|
|
42
|
+
* - `clock.disposeAfter(obj, n)` is built on `after`, so debris removal is
|
|
43
|
+
* frame-exact in an offline export rather than "some time after the run".
|
|
44
|
+
*
|
|
45
|
+
* ## Firing rule (deterministic — pinned by `test/sim-clock.test.ts`)
|
|
46
|
+
*
|
|
47
|
+
* - The runtime calls `flush(simT)` immediately after it bumps `tick`/`simT`,
|
|
48
|
+
* inside the same `advanced` guard — so a paused or frozen world fires no
|
|
49
|
+
* timers at all, and `Game.play.step()` fires exactly the timers that one
|
|
50
|
+
* substep makes due.
|
|
51
|
+
* - A timer is due when `simT >= scheduledAt + seconds`.
|
|
52
|
+
* - Due timers run in due-time ascending order, insertion-sequence ascending
|
|
53
|
+
* on ties.
|
|
54
|
+
* - A timer scheduled *during* a flush is never due in that same flush, even
|
|
55
|
+
* at `seconds === 0`. That is what makes a self-rescheduling timer unable to
|
|
56
|
+
* hang the frame.
|
|
57
|
+
*
|
|
58
|
+
* ## Cancellation is `AbortSignal`
|
|
59
|
+
*
|
|
60
|
+
* The same rung-1 web primitive the rest of this program uses. An
|
|
61
|
+
* already-aborted signal makes `delay` reject immediately and `after` a no-op;
|
|
62
|
+
* rejection is a `DOMException` with `name === 'AbortError'`, matching `fetch`.
|
|
63
|
+
* Disposing the clock rejects every pending `delay` the same way — game code
|
|
64
|
+
* awaiting a delay is expected to tolerate rejection exactly as aborted `fetch`
|
|
65
|
+
* callers do.
|
|
66
|
+
*
|
|
67
|
+
* ## Ownership: the clock is GAME-scoped, and only the Game disposes it
|
|
68
|
+
*
|
|
69
|
+
* One clock per `Game`, shared by every world on it (`getSimClock(game)`); a
|
|
70
|
+
* mount with no Game shell builds a private, mount-local one instead. Whoever
|
|
71
|
+
* CREATED a clock disposes it: `GameInternal.dispose()` disposes the
|
|
72
|
+
* game-scoped one — `runtime/create-runtime.ts` calls that after every root has
|
|
73
|
+
* torn down — and a root adapter disposes only its own mount-local fallback.
|
|
74
|
+
* A per-root teardown must NEVER dispose the game-scoped clock: disposing one
|
|
75
|
+
* world's `mounted` is a supported way to end a sub-session while the Game
|
|
76
|
+
* keeps running, and doing so would freeze `now()`, reject every sibling
|
|
77
|
+
* world's pending `delay` and turn its later `after()` calls into silent
|
|
78
|
+
* no-ops. (`seededRandom` and `debugRegistry` are game-scoped the same way, and
|
|
79
|
+
* per-root teardown has never destroyed either — that asymmetry is what
|
|
80
|
+
* exposed the bug.)
|
|
81
|
+
*
|
|
82
|
+
* ## Cancelling your own timers is the GAME's job — warm restart will not
|
|
83
|
+
*
|
|
84
|
+
* A timer outlives the code that scheduled it unless something cancels it, and
|
|
85
|
+
* `hotReload` (warm restart) deliberately does not reach the game-scoped clock:
|
|
86
|
+
* it is per-ROOT, so cancelling here would kill a sibling root's live timers —
|
|
87
|
+
* the same ownership violation as above. There is deliberately no `reset()`.
|
|
88
|
+
*
|
|
89
|
+
* So a stale `after` closure surviving a warm restart is the same hazard class
|
|
90
|
+
* as a stale event listener, and it has the same owner and the same remedy: the
|
|
91
|
+
* game's own cleanup. `hotReload` runs the game's `dispose()` first for exactly
|
|
92
|
+
* this reason, and both `after` and `delay` take an `AbortSignal` so one
|
|
93
|
+
* controller cancels everything a `setup()` scheduled:
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* export async function setup(ctx: GameContext): Promise<GameCleanup> {
|
|
97
|
+
* const ac = new AbortController();
|
|
98
|
+
* ctx.clock.after(3, () => spawnWave(), { signal: ac.signal });
|
|
99
|
+
* return { dispose: () => ac.abort() }; // cancels timers AND listeners
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* This is the engine-wide "Examples must clean up" contract (CLAUDE.md), not a
|
|
104
|
+
* special rule for timers.
|
|
105
|
+
*
|
|
106
|
+
* ## Deliberately absent
|
|
107
|
+
*
|
|
108
|
+
* - **No `every`/`repeat`/interval.** Nothing needs one, and `after`
|
|
109
|
+
* re-arming itself is three lines the game owns.
|
|
110
|
+
* - **No fiber kernel, no coroutine emulation, no `task.spawn`.** Calling an
|
|
111
|
+
* `async function` *is* `task.spawn` — the language already has it.
|
|
112
|
+
* - **No wall clock.** This module reads no system timer and creates no
|
|
113
|
+
* host timeout; every time value comes from the runtime's own accumulator.
|
|
114
|
+
*/
|
|
115
|
+
import type * as THREE from 'three';
|
|
116
|
+
/** Cancel handle returned by {@link SimClock.after}/{@link SimClock.disposeAfter}. */
|
|
117
|
+
export interface SimTimerHandle {
|
|
118
|
+
/** Cancel the timer if it has not fired yet. Idempotent. */
|
|
119
|
+
cancel(): void;
|
|
120
|
+
}
|
|
121
|
+
/** Options accepted by every scheduling call. */
|
|
122
|
+
export interface SimScheduleOptions {
|
|
123
|
+
/** Cancel/reject when this signal aborts (already-aborted is honored too). */
|
|
124
|
+
readonly signal?: AbortSignal | undefined;
|
|
125
|
+
}
|
|
126
|
+
/** The sim clock as GAME CODE sees it — the whole public surface. */
|
|
127
|
+
export interface SimClock {
|
|
128
|
+
/** Sim seconds elapsed: the fixed loop's own accumulator. Monotonic. */
|
|
129
|
+
now(): number;
|
|
130
|
+
/** Completed fixed substeps — the same counter `Game`'s `tick` carries. */
|
|
131
|
+
tickNow(): number;
|
|
132
|
+
/**
|
|
133
|
+
* Resolve after `seconds` of SIM time. The ergonomic form; see the module
|
|
134
|
+
* header for why it is not frame-exact under a synchronous driver.
|
|
135
|
+
* Rejects with an `AbortError` `DOMException` if `opts.signal` aborts (or is
|
|
136
|
+
* already aborted), or if the clock is disposed while it is pending.
|
|
137
|
+
*/
|
|
138
|
+
delay(seconds: number, opts?: SimScheduleOptions): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Invoke `fn` inline during the flush of the exact due tick — the exact
|
|
141
|
+
* form. A no-op if `opts.signal` is already aborted.
|
|
142
|
+
*/
|
|
143
|
+
after(seconds: number, fn: () => void, opts?: SimScheduleOptions): SimTimerHandle;
|
|
144
|
+
/**
|
|
145
|
+
* Debris: dispose `obj` after `seconds` of sim time. Built on {@link after},
|
|
146
|
+
* so removal lands on an exact tick. The disposal itself is supplied by the
|
|
147
|
+
* runtime (see {@link SimClockOptions.dispose}) — this module knows nothing
|
|
148
|
+
* about physics or rendering.
|
|
149
|
+
*/
|
|
150
|
+
disposeAfter(obj: THREE.Object3D, seconds: number): SimTimerHandle;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The runtime-facing half — NOT for game code. Split off the public
|
|
154
|
+
* {@link SimClock} the same way `runtime/game.ts` splits `GameInternal` off
|
|
155
|
+
* `Game`: `GameContext.clock` is typed as `SimClock`, so game code cannot
|
|
156
|
+
* reach `flush`/`dispose`.
|
|
157
|
+
*/
|
|
158
|
+
export interface SimClockInternal extends SimClock {
|
|
159
|
+
/**
|
|
160
|
+
* Advance to `simT` and fire everything now due. Called by `runFrameImpl`
|
|
161
|
+
* immediately after the `tick`/`simT` bump, inside the `advanced` guard.
|
|
162
|
+
* One call === one completed substep, which is why {@link SimClock.tickNow}
|
|
163
|
+
* can simply count them.
|
|
164
|
+
*/
|
|
165
|
+
flush(simT: number): void;
|
|
166
|
+
/**
|
|
167
|
+
* Drop every pending timer and reject every pending `delay`. Called by
|
|
168
|
+
* whoever CREATED this clock and by nobody else — `GameInternal.dispose()`
|
|
169
|
+
* for the game-scoped one, the owning mount for a mount-local fallback. See
|
|
170
|
+
* the module header's ownership section.
|
|
171
|
+
*/
|
|
172
|
+
dispose(): void;
|
|
173
|
+
}
|
|
174
|
+
export interface SimClockOptions {
|
|
175
|
+
/**
|
|
176
|
+
* How {@link SimClock.disposeAfter} disposes an object. Supplied by the
|
|
177
|
+
* runtime, which is the only layer that knows about physics
|
|
178
|
+
* registries and shared geometry; the clock only knows *when*.
|
|
179
|
+
*/
|
|
180
|
+
readonly dispose: (obj: THREE.Object3D) => void;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Build the one sim clock a `Game` owns. See the module header for the
|
|
184
|
+
* contract; `runtime/game.ts` is the only production caller.
|
|
185
|
+
*/
|
|
186
|
+
export declare function createSimClock(options: SimClockOptions): SimClockInternal;
|
|
187
|
+
/** Called once by `createGame`, right after the clock and the Game shell exist. */
|
|
188
|
+
export declare function registerSimClock(owner: object, clock: SimClockInternal): void;
|
|
189
|
+
/** The game-scoped clock backing every world's `ctx.clock` — `null` for an
|
|
190
|
+
* owner built without one (a hand-built `Game`-shaped stand-in that never went
|
|
191
|
+
* through `createGame`).
|
|
192
|
+
*
|
|
193
|
+
* Returns the INTERNAL view because this registry is engine-only — but a
|
|
194
|
+
* caller that RESOLVES a clock here is not its owner and must not call
|
|
195
|
+
* `dispose()` on it (module header, ownership). Game code never reaches it at
|
|
196
|
+
* all: `GameContext.clock` is typed as the public {@link SimClock}, which has
|
|
197
|
+
* neither `flush` nor `dispose`, the same way `Game` hides `GameInternal`. */
|
|
198
|
+
export declare function getSimClock(owner: object): SimClockInternal | null;
|
|
199
|
+
//# sourceMappingURL=sim-clock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sim-clock.d.ts","sourceRoot":"","sources":["../../src/core/sim-clock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiHG;AAEH,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC3C;AAED,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,wEAAwE;IACxE,GAAG,IAAI,MAAM,CAAC;IACd,2EAA2E;IAC3E,OAAO,IAAI,MAAM,CAAC;IAClB;;;;;OAKG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,cAAc,CAAC;IAClF;;;;;OAKG;IACH,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC;CACpE;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAiB,SAAQ,QAAQ;IAChD;;;;;OAKG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC;CACjD;AAyBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,gBAAgB,CAiJzE;AAWD,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAE7E;AAED;;;;;;;;+EAQ+E;AAC/E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAElE"}
|