@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,791 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SetupThreeRootAdapter — the first-party implementer of {@link RootAdapter}
|
|
3
|
+
* that mounts ONE three root from an imperative `setup(ctx)` function.
|
|
4
|
+
*
|
|
5
|
+
* This is where Rapier lives now: the host no longer knows about it.
|
|
6
|
+
* `mount(host)` builds the full first-party runtime (the body
|
|
7
|
+
* that used to live inline in `create-runtime.ts`) from a neutral
|
|
8
|
+
* {@link ThreeHostContext}, runs the game's `setup`, and returns a
|
|
9
|
+
* {@link MountedThreeRoot}. First-party content is now just one implementer of
|
|
10
|
+
* the same interface an unmodified external game implements (Phase B).
|
|
11
|
+
*
|
|
12
|
+
* It was `VgaiSceneGameAdapter` until WO-9 tasks #6/#9. Both halves of that
|
|
13
|
+
* name had stopped being true: it loads no scene (the `.vscn.json` branches
|
|
14
|
+
* went with the format in WO-8 — `setup` is the only mount path left), and what
|
|
15
|
+
* it mounts is one three ROOT, not a game (a game is the manifest's whole set
|
|
16
|
+
* of roots). The R3F/TSX world root is a SEPARATE adapter; this one is
|
|
17
|
+
* specifically the `fromSetup` path.
|
|
18
|
+
*
|
|
19
|
+
* `GameSetupFn` / `GameContext` are imported ONLY here (and in the example
|
|
20
|
+
* `fromSetup` wrappers) — never by the host. That confinement is the inversion.
|
|
21
|
+
*/
|
|
22
|
+
import RAPIER from '@dimforge/rapier3d-compat';
|
|
23
|
+
import * as THREE from 'three';
|
|
24
|
+
import { clearAssetCaches } from '../asset-loaders';
|
|
25
|
+
import { createSeededRandom, DEFAULT_SEEDED_RANDOM_SEED, getSeededRandom, } from '../core/seeded-random';
|
|
26
|
+
import { createSimClock, getSimClock } from '../core/sim-clock';
|
|
27
|
+
import { createSystemRunner } from '../core/system-runner';
|
|
28
|
+
import { createDebugDraw } from '../dev/debug-draw';
|
|
29
|
+
import { createRenderDebugAdapter, frameCaptureContextFor, } from '../dev/render-debug-adapter';
|
|
30
|
+
import { collectRenderMemory } from '../dev/render-memory';
|
|
31
|
+
import { createWebGLFrameCapture } from '../dev/webgl-frame-capture';
|
|
32
|
+
import { createWebGLGpuTimer } from '../dev/webgl-gpu-timer';
|
|
33
|
+
import { createSceneIndex } from '../ecs/scene-index';
|
|
34
|
+
import { getUserData, hasUserData, setUserData } from '../ecs/user-data';
|
|
35
|
+
import { InputManager } from '../input/input-manager';
|
|
36
|
+
import { setAssetPrefix } from '../loader';
|
|
37
|
+
import { createCollisionSystem } from '../physics/collision-system';
|
|
38
|
+
import { createPhysicsRegistry } from '../physics/physics-registry';
|
|
39
|
+
import { createTransformWriter } from '../physics/transform-writer';
|
|
40
|
+
import { updateSceneLODs } from '../render/lod';
|
|
41
|
+
import { disposeSparkRendererWhenIdle, SPARK_DISCOVERY_INTERVAL_MS, sceneHasGaussianSplat, shouldDiscoverGaussianSplat, } from '../render/spark-renderer-lifecycle';
|
|
42
|
+
import { ViewportShadingRenderer } from '../render/viewport-shading';
|
|
43
|
+
import { createDebugRegistry, getDebugRegistry, } from '../runtime/debug-registry';
|
|
44
|
+
import { disposeDebrisSubtree } from '../runtime/game';
|
|
45
|
+
import { setupAudio } from '../setup/setup-audio';
|
|
46
|
+
import { setupParticles } from '../setup/setup-particles';
|
|
47
|
+
import { setupPhysics, updatePhysicsDebug } from '../setup/setup-physics';
|
|
48
|
+
import { createSceneView } from '../setup/setup-renderer';
|
|
49
|
+
import { createAudioSystemAdapter, releaseAudioMeters } from './first-party-systems';
|
|
50
|
+
import { createRapierPhysicsAdapter } from './rapier-physics-adapter';
|
|
51
|
+
/** Minimal stand-in for the GPU composer when mounting headlessly. */
|
|
52
|
+
function headlessComposer(renderer) {
|
|
53
|
+
return {
|
|
54
|
+
render: () => { },
|
|
55
|
+
setSize: () => { },
|
|
56
|
+
dispose: () => { },
|
|
57
|
+
addPass: () => { },
|
|
58
|
+
removeAllPasses: () => { },
|
|
59
|
+
passes: [],
|
|
60
|
+
getRenderer: () => renderer,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Wrap a first-party `setup` function as a {@link RootAdapter}. This is how every
|
|
65
|
+
* first-party example becomes an implementer of the same interface an external
|
|
66
|
+
* game implements — with no change to the setup's body.
|
|
67
|
+
*/
|
|
68
|
+
export function fromSetup(id, setup) {
|
|
69
|
+
return new SetupThreeRootAdapter({ id, setup });
|
|
70
|
+
}
|
|
71
|
+
export class SetupThreeRootAdapter {
|
|
72
|
+
config;
|
|
73
|
+
id;
|
|
74
|
+
constructor(config = {}) {
|
|
75
|
+
this.config = config;
|
|
76
|
+
this.id = config.id ?? 'setup-three';
|
|
77
|
+
}
|
|
78
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one cohesive first-party bootstrap (moved verbatim from create-runtime); splitting it would obscure the ordering contract
|
|
79
|
+
async mount(host) {
|
|
80
|
+
const { setup, editorPreview, inputMapPath = '/inputmaps/default.inputmap.json', assetPrefix = '/', } = this.config;
|
|
81
|
+
const headless = host.headless === true;
|
|
82
|
+
const { width, height } = host.surface;
|
|
83
|
+
const renderer = host.renderer;
|
|
84
|
+
setAssetPrefix(assetPrefix);
|
|
85
|
+
// --- Scene view (scene/camera/composer). Browser: the canonical builder, so
|
|
86
|
+
// behavior is byte-identical to the old create-runtime. Headless: plain
|
|
87
|
+
// scene/camera + a no-op composer (no GPU). ---
|
|
88
|
+
let scene;
|
|
89
|
+
let camera;
|
|
90
|
+
let composer;
|
|
91
|
+
if (headless) {
|
|
92
|
+
scene = new THREE.Scene();
|
|
93
|
+
scene.background = new THREE.Color(0x1a1a2e);
|
|
94
|
+
camera = new THREE.PerspectiveCamera(60, width / height || 1, 0.1, 1000);
|
|
95
|
+
camera.position.set(0, 5, 10);
|
|
96
|
+
camera.lookAt(0, 0, 0);
|
|
97
|
+
composer = headlessComposer(renderer);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const view = createSceneView(renderer, width, height);
|
|
101
|
+
scene = view.scene;
|
|
102
|
+
camera = view.camera;
|
|
103
|
+
composer = view.composer;
|
|
104
|
+
}
|
|
105
|
+
// --- Rapier physics ---
|
|
106
|
+
await RAPIER.init();
|
|
107
|
+
const physics = setupPhysics(RAPIER, scene);
|
|
108
|
+
const physicsRegistry = createPhysicsRegistry();
|
|
109
|
+
const writeTransforms = createTransformWriter(physicsRegistry, scene);
|
|
110
|
+
// --- Input (InputManager attaches window listeners; stub it headlessly) ---
|
|
111
|
+
const ownsInput = !host.game;
|
|
112
|
+
const input = host.game?.input ?? (headless ? headlessInput() : new InputManager());
|
|
113
|
+
if (!headless) {
|
|
114
|
+
if (host.game)
|
|
115
|
+
await host.game.loadInputMap(inputMapPath);
|
|
116
|
+
else
|
|
117
|
+
await input.loadMap(inputMapPath);
|
|
118
|
+
}
|
|
119
|
+
// --- Collision system ---
|
|
120
|
+
const collisions = createCollisionSystem(physics.rapierWorld, physics.eventQueue);
|
|
121
|
+
// --- Audio (Web Audio — browser only) ---
|
|
122
|
+
const audio = headless ? headlessAudio() : setupAudio(camera);
|
|
123
|
+
// --- Particles (three.quarks BatchedRenderer) ---
|
|
124
|
+
const particles = setupParticles(scene);
|
|
125
|
+
// --- Debug draw / assets ---
|
|
126
|
+
const debugDraw = createDebugDraw(scene);
|
|
127
|
+
const assets = host.assets;
|
|
128
|
+
// Test/headless adapters may provide the renderer surface needed by the
|
|
129
|
+
// runtime without a real WebGL context. GPU timing is optional diagnostics:
|
|
130
|
+
// degrade honestly when the renderer/context cannot expose extensions.
|
|
131
|
+
const rendererContext = typeof renderer.getContext === 'function' ? renderer.getContext() : undefined;
|
|
132
|
+
const gpuTimer = !headless && rendererContext && typeof rendererContext.getExtension === 'function'
|
|
133
|
+
? createWebGLGpuTimer(rendererContext)
|
|
134
|
+
: null;
|
|
135
|
+
// W4b (F11 frame debugger): first-party WebGL2 frame capture, constructed
|
|
136
|
+
// under the SAME real-context guard as gpuTimer — a headless/stub mount
|
|
137
|
+
// (`frameCaptureContextFor` returns null) never builds it and never
|
|
138
|
+
// registers `renderDebug`, so the capability is honestly absent there. The
|
|
139
|
+
// capture only patches the context WHILE a `captureFrame()` is armed (see
|
|
140
|
+
// beginPass/endPass in the render system below); every non-capture frame
|
|
141
|
+
// is zero-overhead.
|
|
142
|
+
const frameCaptureContext = frameCaptureContextFor(headless, rendererContext);
|
|
143
|
+
const frameCapture = frameCaptureContext ? createWebGLFrameCapture(frameCaptureContext) : null;
|
|
144
|
+
const renderDebugWiring = frameCapture
|
|
145
|
+
? createRenderDebugAdapter({
|
|
146
|
+
capture: frameCapture,
|
|
147
|
+
scene,
|
|
148
|
+
memory: () => collectRenderMemory(scene, renderer.info),
|
|
149
|
+
})
|
|
150
|
+
: null;
|
|
151
|
+
const viewportShading = new ViewportShadingRenderer();
|
|
152
|
+
let viewportShadingMode = 'solid';
|
|
153
|
+
// --- System runner (engine-level systems) ---
|
|
154
|
+
const systems = createSystemRunner(host.game?.profiler.systemObserver, 'three');
|
|
155
|
+
// D15/T-D15.3/.5: pass the LIVE shared game tick (not a locally-counted
|
|
156
|
+
// one) into every `poll()` call — `debugRegistry` is declared further
|
|
157
|
+
// down in this same `mount()` (it needs `ctx`/`systemAdapters` to exist
|
|
158
|
+
// first), but this callback only ever executes during real frame
|
|
159
|
+
// ticks, long after `mount()` has fully returned and `debugRegistry` is
|
|
160
|
+
// initialized (a safe closure-over-a-later-const, not a genuine
|
|
161
|
+
// use-before-init). `host.game` gates it: a bare mount with no `Game`
|
|
162
|
+
// shell behind it (test harnesses predating T7.1) has no shared tick to
|
|
163
|
+
// key off, so `InputManager.poll()` falls back to its own internal
|
|
164
|
+
// self-incrementing counter instead (unchanged pre-D15 behavior) rather
|
|
165
|
+
// than being pinned to a constant `0` forever.
|
|
166
|
+
if (ownsInput)
|
|
167
|
+
systems.add('input', () => input.poll(), { name: 'input.poll' });
|
|
168
|
+
systems.add('physics', () => physics.rapierWorld.step(physics.eventQueue), {
|
|
169
|
+
name: 'physics.step',
|
|
170
|
+
});
|
|
171
|
+
systems.add('postPhysics', () => {
|
|
172
|
+
collisions.drain();
|
|
173
|
+
writeTransforms();
|
|
174
|
+
}, { name: 'physics.sync' });
|
|
175
|
+
systems.add('preRender', (dt) => {
|
|
176
|
+
particles.batchedRenderer.update(dt);
|
|
177
|
+
updateSceneLODs(scene, camera);
|
|
178
|
+
sparkDiscoveryElapsedMs += dt * 1_000;
|
|
179
|
+
const shouldDiscover = shouldDiscoverGaussianSplat(0, sparkDiscoveryElapsedMs);
|
|
180
|
+
if (!sparkRenderer && !sparkRendererLoading && !sparkRendererFailed && shouldDiscover) {
|
|
181
|
+
sparkDiscoveryElapsedMs = 0;
|
|
182
|
+
if (!sceneHasGaussianSplat(scene))
|
|
183
|
+
return;
|
|
184
|
+
sparkRendererLoading = true;
|
|
185
|
+
void import('@sparkjsdev/spark')
|
|
186
|
+
.then(({ SparkRenderer }) => {
|
|
187
|
+
if (disposed || sparkRenderer)
|
|
188
|
+
return;
|
|
189
|
+
sparkRenderer = new SparkRenderer({ renderer, enableLod: false });
|
|
190
|
+
sparkRenderer.traverse((node) => setUserData(node, 'engineInternal', true));
|
|
191
|
+
})
|
|
192
|
+
.catch((error) => {
|
|
193
|
+
sparkRendererFailed = true;
|
|
194
|
+
// biome-ignore lint/suspicious/noConsole: A missing renderer chunk must fail once and loudly, not retry every frame.
|
|
195
|
+
console.error('Failed to install SparkRenderer for a live Gaussian splat.', error);
|
|
196
|
+
})
|
|
197
|
+
.finally(() => {
|
|
198
|
+
sparkRendererLoading = false;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}, { name: 'materials-and-particles' });
|
|
202
|
+
if (!headless) {
|
|
203
|
+
systems.add('render', (dt) => {
|
|
204
|
+
if (host.game?.profiler.enabled)
|
|
205
|
+
gpuTimer?.begin();
|
|
206
|
+
if (sparkRenderer)
|
|
207
|
+
scene.add(sparkRenderer);
|
|
208
|
+
// W4b: arm-gated frame capture wraps the real draw calls. beginPass
|
|
209
|
+
// is a no-op unless a captureFrame() armed it; afterRender delivers
|
|
210
|
+
// the assembled capture to a pending captureFrame() and restores the
|
|
211
|
+
// patched context — in the SAME finally as gpuTimer.end(). Spark is
|
|
212
|
+
// added before this so its draws fall inside the captured frame.
|
|
213
|
+
renderDebugWiring?.beforeRender();
|
|
214
|
+
try {
|
|
215
|
+
viewportShading.render(scene, viewportShadingMode, () => composer.render(dt),
|
|
216
|
+
// Scene authoring can temporarily attach editor infrastructure
|
|
217
|
+
// (gizmos/helpers on their own layer) to this same native scene.
|
|
218
|
+
// Only replace materials the actual game camera can render.
|
|
219
|
+
(mesh) => camera.layers.test(mesh.layers));
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
renderDebugWiring?.afterRender();
|
|
223
|
+
sparkRenderer?.removeFromParent();
|
|
224
|
+
gpuTimer?.end();
|
|
225
|
+
}
|
|
226
|
+
if (physics.debugEnabled)
|
|
227
|
+
updatePhysicsDebug(physics.rapierWorld, physics.debugMesh);
|
|
228
|
+
}, { name: 'three.render' });
|
|
229
|
+
}
|
|
230
|
+
// --- Debug toggle (browser only) ---
|
|
231
|
+
const onDebugToggle = (e) => {
|
|
232
|
+
if (e.code === 'KeyP') {
|
|
233
|
+
physics.debugEnabled = !physics.debugEnabled;
|
|
234
|
+
physics.debugMesh.visible = physics.debugEnabled;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
if (!headless)
|
|
238
|
+
window.addEventListener('keydown', onDebugToggle);
|
|
239
|
+
const postFrame = () => {
|
|
240
|
+
if (ownsInput)
|
|
241
|
+
input.endFrame();
|
|
242
|
+
if (host.game?.profiler.enabled) {
|
|
243
|
+
host.game.profiler.reportRender({
|
|
244
|
+
gpuMs: gpuTimer?.poll() ?? null,
|
|
245
|
+
drawCalls: renderer.info.render.calls,
|
|
246
|
+
triangles: renderer.info.render.triangles,
|
|
247
|
+
geometries: renderer.info.memory.geometries,
|
|
248
|
+
textures: renderer.info.memory.textures,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
// The mounted game's adapter surface (`mounted.systems`). Declared before
|
|
253
|
+
// ctx so `ctx.registerSystemAdapter` can close over it: the engine fills
|
|
254
|
+
// the first-party entries below; GAME-owned capabilities (networking,
|
|
255
|
+
// navigation) are registered by the game's setup through the ctx hook.
|
|
256
|
+
// The editor holds this same object reference (setActiveSystems), so
|
|
257
|
+
// late registrations are visible to its panels at read time.
|
|
258
|
+
const systemAdapters = {};
|
|
259
|
+
// P3 — `ctx.clock`. Same "ONE per Game root, shared across every world"
|
|
260
|
+
// reach-in as `ctx.debug`/`ctx.random` below: `getSimClock(host.game)`
|
|
261
|
+
// returns the clock `createGame` filed, and `runFrameImpl` is what flushes
|
|
262
|
+
// it. A bare mount with no Game shell (headless harness, foreign host) gets
|
|
263
|
+
// a private, mount-local clock so the field is always a real value — but
|
|
264
|
+
// nothing flushes that one, because a mount with no Game has no fixed loop
|
|
265
|
+
// to bind timers to and inventing a wall-clock frontier here is exactly the
|
|
266
|
+
// hazard `core/sim-clock.ts` exists to avoid. Its `disposeAfter` is
|
|
267
|
+
// therefore unreachable; the disposer is still the real one.
|
|
268
|
+
const gameClock = host.game ? getSimClock(host.game) : null;
|
|
269
|
+
const clock = gameClock ?? createSimClock({ dispose: (obj) => disposeDebrisSubtree(obj, [ctx]) });
|
|
270
|
+
/** Only a mount-local fallback is THIS mount's to dispose — see the
|
|
271
|
+
* `clock.dispose` teardown step below for why the game-scoped one is not. */
|
|
272
|
+
const ownsClock = gameClock === null;
|
|
273
|
+
// --- GameContext (the first-party runtime surface) ---
|
|
274
|
+
const ctx = {
|
|
275
|
+
scene,
|
|
276
|
+
camera,
|
|
277
|
+
rapierWorld: physics.rapierWorld,
|
|
278
|
+
rapier: RAPIER,
|
|
279
|
+
physics: physicsRegistry,
|
|
280
|
+
input,
|
|
281
|
+
collisions,
|
|
282
|
+
composer,
|
|
283
|
+
audio,
|
|
284
|
+
particles,
|
|
285
|
+
debugDraw,
|
|
286
|
+
assets,
|
|
287
|
+
systems,
|
|
288
|
+
clock,
|
|
289
|
+
// The live scene index for THIS root (P2 `observe`). Created before
|
|
290
|
+
// setup() runs so a game's own objects are indexed as they are added —
|
|
291
|
+
// it subscribes to three's `childadded`/`childremoved` on the scene, so
|
|
292
|
+
// everything the game builds arrives through it. Disposed in
|
|
293
|
+
// disposeGame() below (a warm restart keeps it: same `scene`, same ctx).
|
|
294
|
+
sceneIndex: createSceneIndex(scene),
|
|
295
|
+
registerSystemAdapter: (kind, adapter) => {
|
|
296
|
+
systemAdapters[kind] = adapter;
|
|
297
|
+
host.game?.notifySystemAdaptersChanged();
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
// Game root (T7.1 slice 1): only present once the host has constructed a
|
|
301
|
+
// Game shell (createGameRuntime does this before mount; headless/foreign
|
|
302
|
+
// hosts may omit it — ctx.game/ctx.roots simply stay undefined, which is
|
|
303
|
+
// the documented zero-break behavior). `ctx.roots` is assigned the SAME
|
|
304
|
+
// live array `host.game` mutates via `registerRoot` (not a copy) — the
|
|
305
|
+
// default world is registered AFTER mount returns, and this reference
|
|
306
|
+
// must observe that later push.
|
|
307
|
+
if (host.game) {
|
|
308
|
+
ctx.game = host.game;
|
|
309
|
+
ctx.roots = host.game.roots;
|
|
310
|
+
ctx.playtest = host.game.playtest ?? null;
|
|
311
|
+
}
|
|
312
|
+
// Everything registered above (the engine systems) is "engine" and must
|
|
313
|
+
// survive a warm
|
|
314
|
+
// restart. Everything a game's setup() (below) adds
|
|
315
|
+
// from here on is "game" content that `hotReload` bulk-removes via
|
|
316
|
+
// `systems.removeAllNonEngine()` on every restart (T1.7).
|
|
317
|
+
systems.markEngineBoundary();
|
|
318
|
+
// First-party System adapters over the real subsystems — seeded HERE,
|
|
319
|
+
// BEFORE setup()/scene load runs below, so that a game which registers any
|
|
320
|
+
// of these kinds during its own setup (via `ctx.registerSystemAdapter`)
|
|
321
|
+
// always wins over the engine's first-party entry (see
|
|
322
|
+
// registerSystemAdapter's docstring in runtime/types.ts). `navigation` is
|
|
323
|
+
// ALSO first-party now (R8 item 7): `createNavigationAdapter` wraps the
|
|
324
|
+
// resolves, not here (no NavMeshManager exists yet at this point in the
|
|
325
|
+
// mount). `networking` remains the one game-owned capability with no
|
|
326
|
+
// first-party implementer; it simply starts absent here.
|
|
327
|
+
// P-4: the physics seam is keyed by node id, so the first-party mount
|
|
328
|
+
// supplies the first-party `id → Object3D` map. `userData.entityId` is
|
|
329
|
+
// THIS stack's identity convention (the scene loader stamps it, and the
|
|
330
|
+
// editor's `objectMap`/`hierarchy.object3D` agree on it), which is exactly
|
|
331
|
+
// why the lookup belongs here and not inside `rapier-physics-adapter.ts`.
|
|
332
|
+
// Memoized because the gizmo/inspector ask repeatedly for one selection;
|
|
333
|
+
// a miss (or an object since detached) re-walks once and re-caches.
|
|
334
|
+
const nodeObjectCache = new Map();
|
|
335
|
+
const resolveNodeObject = (nodeId) => {
|
|
336
|
+
const cached = nodeObjectCache.get(nodeId);
|
|
337
|
+
if (cached?.parent)
|
|
338
|
+
return cached;
|
|
339
|
+
let found = null;
|
|
340
|
+
scene.traverse((o) => {
|
|
341
|
+
if (!found && getUserData(o, 'entityId') === nodeId)
|
|
342
|
+
found = o;
|
|
343
|
+
});
|
|
344
|
+
if (found)
|
|
345
|
+
nodeObjectCache.set(nodeId, found);
|
|
346
|
+
else
|
|
347
|
+
nodeObjectCache.delete(nodeId);
|
|
348
|
+
return found;
|
|
349
|
+
};
|
|
350
|
+
systemAdapters.physics = createRapierPhysicsAdapter(physicsRegistry, physics, resolveNodeObject);
|
|
351
|
+
// D10/T7.6: the audio seam `Game.play.pause()` silences on pause. Works
|
|
352
|
+
// against the headless stand-in too (`headlessAudio()`'s plain
|
|
353
|
+
// `masterGain.gain` object) — this world's `pause()` mutes it harmlessly.
|
|
354
|
+
systemAdapters.audio = createAudioSystemAdapter(audio);
|
|
355
|
+
// Debug/synthetic-player seam (T1.1): ONE registry per Game root, shared
|
|
356
|
+
// across every world that mounts onto it — `getDebugRegistry(host.game)`
|
|
357
|
+
// reaches the SAME accumulator `createGame` filed, so a second/third
|
|
358
|
+
// world's `ctx.debug` feeds it too (see debug-registry.ts's module doc
|
|
359
|
+
// for the provenance/id note). A mount with no Game shell at all (bare
|
|
360
|
+
// test harnesses predating T7.1) gets a private, mount-local registry
|
|
361
|
+
// instead — same absence precedent as `ctx.game`/`ctx.roots` above.
|
|
362
|
+
const debugRegistry = (host.game ? getDebugRegistry(host.game) : null) ??
|
|
363
|
+
createDebugRegistry({ getTick: () => 0, getSimT: () => 0 });
|
|
364
|
+
systemAdapters.debug = debugRegistry.adapter;
|
|
365
|
+
// W4b: the render-debug capability is engine-owned and first-party, seeded
|
|
366
|
+
// here (like physics/input/assets) BEFORE the engineAdapterKinds snapshot
|
|
367
|
+
// below so a warm restart / stop never mistakes it for game content. Only
|
|
368
|
+
// present when a real WebGL2 context backed the mount (renderDebugWiring).
|
|
369
|
+
if (renderDebugWiring)
|
|
370
|
+
systemAdapters.renderDebug = renderDebugWiring.adapter;
|
|
371
|
+
ctx.debug = debugRegistry.forRoot(this.id);
|
|
372
|
+
// T1.2: the InputManager is a first-party per-world handle the registry
|
|
373
|
+
// has no other way to reach — wired as a lazy supplier (not a direct
|
|
374
|
+
// read here), scoped to THIS world's id, so the built-in `input.actions`
|
|
375
|
+
// provider always reflects the CURRENT action set, not a snapshot taken
|
|
376
|
+
// at mount time. Per-world (not a single last-writer-wins slot) since
|
|
377
|
+
// D15/T-D15.5 — see `debug-registry.ts`'s `setInputActionsSource` doc.
|
|
378
|
+
debugRegistry.setInputActionsSource(this.id, () => typeof input.actionNames === 'function'
|
|
379
|
+
? input.actionNames().map((name) => ({ name, valueType: input.getActionValueType(name) }))
|
|
380
|
+
: []);
|
|
381
|
+
// D15/T-D15.5 — same lazy-supplier pattern (and same headless-stand-in
|
|
382
|
+
// guard) as `setInputActionsSource` immediately above: the built-in
|
|
383
|
+
// `input.trace` provider always reads the CURRENT recorded trace, never
|
|
384
|
+
// a snapshot taken at mount time. `seed`/`fixedDt` are assembled here
|
|
385
|
+
// (not inside `InputManager`, which has no business knowing about
|
|
386
|
+
// `ctx.random` or the loop) — the two replay-critical metadata fields
|
|
387
|
+
// the design doc's format sketch (§2.c) calls for beside the raw
|
|
388
|
+
// per-tick deltas; `null` only absent a Game/ctx.random behind this
|
|
389
|
+
// mount, matching every other "no Game" fallback in this file.
|
|
390
|
+
debugRegistry.setInputTraceSource(this.id, () => {
|
|
391
|
+
const raw = typeof input.getInputTrace === 'function'
|
|
392
|
+
? input.getInputTrace()
|
|
393
|
+
: { version: 1, ticks: [] };
|
|
394
|
+
const seed = (host.game ? getSeededRandom(host.game) : null)?.seed ?? null;
|
|
395
|
+
const fixedDt = host.game?.loop.fixedDt ?? null;
|
|
396
|
+
return { version: raw.version, seed, fixedDt, ticks: raw.ticks };
|
|
397
|
+
});
|
|
398
|
+
// Task 2.1 — the same InputManager instance, wired as the debug bridge's
|
|
399
|
+
// actuation target (`runtime/debug-bridge.ts`'s `input.*` methods) at the
|
|
400
|
+
// same seed spot as `setInputActionsSource` above. Per-world (D15 review
|
|
401
|
+
// objection 2 fix) — see `debug-registry.ts`'s `setVirtualInputTarget`
|
|
402
|
+
// doc for why this used to be a single last-writer-wins slot.
|
|
403
|
+
debugRegistry.setVirtualInputTarget(this.id, {
|
|
404
|
+
setVirtualAction: (action, value) => input.setVirtualAction(action, value),
|
|
405
|
+
tapVirtualAction: (action) => input.tapVirtualAction(action),
|
|
406
|
+
clearVirtualActions: () => input.clearVirtualActions(),
|
|
407
|
+
scheduleActionAtTick: (tick, action, value) => input.scheduleActionAtTick(tick, action, value),
|
|
408
|
+
startInputRecording: () => input.startInputRecording(),
|
|
409
|
+
stopInputRecording: () => input.stopInputRecording(),
|
|
410
|
+
isInputRecording: () => input.isInputRecording(),
|
|
411
|
+
injectAxis: (sourceId, value) => input.injectAxis(sourceId, value),
|
|
412
|
+
injectVector2: (sourceId, value) => input.injectVector2(sourceId, value),
|
|
413
|
+
injectPointerDelta: (sourceId, delta) => input.injectPointerDelta(sourceId, delta),
|
|
414
|
+
injectPointerPosition: (sourceId, value) => input.injectPointerPosition(sourceId, value),
|
|
415
|
+
});
|
|
416
|
+
// R4-class fix — wire the InputManager's `'input.schedule.dropped'` sink
|
|
417
|
+
// to THIS world's debug registry, same seed spot/precedent as
|
|
418
|
+
// `setInputActionsSource`/`setVirtualInputTarget` above (typeof-guarded:
|
|
419
|
+
// a headless `InputManager` stand-in may have no `setDebugEmit`).
|
|
420
|
+
if (!host.game && typeof input.setDebugEmit === 'function') {
|
|
421
|
+
input.setDebugEmit((event, detail) => ctx.debug?.emit(event, detail));
|
|
422
|
+
}
|
|
423
|
+
// D15 (T-D15.1) — same "ONE per Game root, shared across every world"
|
|
424
|
+
// pattern as the debug registry immediately above: `getSeededRandom
|
|
425
|
+
// (host.game)` reaches the SAME `SeededRandom` `createGame` filed (boot-
|
|
426
|
+
// seeded by the manifest-aware mount path, `mount-manifest.ts`, before
|
|
427
|
+
// ANY world's `mount()`/`setup()` runs — see `createGame`'s own doc
|
|
428
|
+
// comment), so every world's `ctx.random` is the identical instance, not
|
|
429
|
+
// a per-world copy. Same absence precedent as `ctx.debug`: a mount with
|
|
430
|
+
// no Game shell at all gets a private, mount-local surface instead.
|
|
431
|
+
ctx.random =
|
|
432
|
+
(host.game ? getSeededRandom(host.game) : null) ??
|
|
433
|
+
createSeededRandom(DEFAULT_SEEDED_RANDOM_SEED);
|
|
434
|
+
// Snapshot of the engine-owned adapter kinds, taken right after seeding
|
|
435
|
+
// and before any setup() has had a chance to run. `hotReload`/`disposeGame`
|
|
436
|
+
// use this to strip every GAME-registered kind (including an override of a
|
|
437
|
+
// first-party kind) left over from the outgoing game, so a warm restart or
|
|
438
|
+
// a full stop never exposes a disposed game's networking/etc. adapter to
|
|
439
|
+
// the next setup.
|
|
440
|
+
const engineAdapterKinds = new Set(Object.keys(systemAdapters));
|
|
441
|
+
// 'navigation' is engine-owned too (R8 item 7), but the scene load that
|
|
442
|
+
// populates it (below, `applySceneNavigation`) happens AFTER this
|
|
443
|
+
// snapshot — add it explicitly so the generic "strip every non-engine
|
|
444
|
+
// kind" step (hotReload/disposeGame) never mistakes a first-party
|
|
445
|
+
// navigation adapter for game-registered content and deletes it. (The
|
|
446
|
+
// adapter is still kept in sync with the CURRENT scene's NavMeshManager
|
|
447
|
+
// via the dedicated `clear navigation adapter` teardown step below, which
|
|
448
|
+
// runs independently of this snapshot.)
|
|
449
|
+
engineAdapterKinds.add('navigation');
|
|
450
|
+
// --- Scene / game setup ---
|
|
451
|
+
const engineSceneChildren = new Set(scene.children.slice());
|
|
452
|
+
// Tag engine-owned infra (particle BatchedRenderer, debug-draw + subtrees) so
|
|
453
|
+
// the editor's play-mode hierarchy skips them — they're not game entities.
|
|
454
|
+
for (const c of engineSceneChildren) {
|
|
455
|
+
c.traverse((n) => setUserData(n, 'engineInternal', true));
|
|
456
|
+
}
|
|
457
|
+
let currentCleanup = null;
|
|
458
|
+
let sparkRenderer = null;
|
|
459
|
+
let sparkRendererLoading = false;
|
|
460
|
+
let sparkRendererFailed = false;
|
|
461
|
+
// Discovery is render-loop scheduling, so advance it from the loop's `dt`
|
|
462
|
+
// instead of introducing a second wall-clock frontier into engine code.
|
|
463
|
+
// Start at the threshold so the first rendered frame discovers eager splats.
|
|
464
|
+
let sparkDiscoveryElapsedMs = SPARK_DISCOVERY_INTERVAL_MS;
|
|
465
|
+
if (!setup) {
|
|
466
|
+
throw new Error('SetupThreeRootAdapter: no `setup` supplied. The `.vscn.json` scene branches ' +
|
|
467
|
+
'(`scenePath` / `sceneData`) were removed with the format; a first-party three root ' +
|
|
468
|
+
'is either an imperative ' +
|
|
469
|
+
'`setup(ctx)` (wrap it with `fromSetup(id, setup)`) or a TSX/R3F world root ' +
|
|
470
|
+
'(`entry` pointing at a .tsx module — mounted by the R3F adapter, not this one).');
|
|
471
|
+
}
|
|
472
|
+
// Headless is legal here: `createSceneView` was skipped above, the composer
|
|
473
|
+
// is a no-op stand-in, and `setup` gets a real `THREE.Scene`/camera. It used
|
|
474
|
+
// to throw because headless existed only for the `.vscn` `sceneData` branch;
|
|
475
|
+
// with the format gone (WO-8) `setup` is the only mount path, and headless
|
|
476
|
+
// unit mounts are what exercise the Game/roots/frame machinery without WebGL.
|
|
477
|
+
currentCleanup = await setup(ctx, editorPreview);
|
|
478
|
+
// Warm the GPU program cache before the first visible frame. A mount gets
|
|
479
|
+
// a fresh WebGL context, so without this every scene material compiles
|
|
480
|
+
// lazily INSIDE the first draw calls — measured as ~0.5s of main-thread
|
|
481
|
+
// driver stalls on every editor play press. Gated on
|
|
482
|
+
// KHR_parallel_shader_compile: with the extension, compileAsync overlaps
|
|
483
|
+
// the compiles with the play transition; WITHOUT it (software
|
|
484
|
+
// rasterizers — SwiftShader in CI) compileAsync degenerates into one big
|
|
485
|
+
// synchronous compile that delays mount by whole seconds, strictly worse
|
|
486
|
+
// than the lazy first-draw path it was replacing. Best-effort: lazy
|
|
487
|
+
// compilation stays correct if it throws, and test hosts pass renderer
|
|
488
|
+
// stubs without these methods.
|
|
489
|
+
const glContext = typeof renderer.getContext === 'function' ? renderer.getContext() : undefined;
|
|
490
|
+
const parallelCompile = !!glContext &&
|
|
491
|
+
typeof glContext.getExtension === 'function' &&
|
|
492
|
+
!!glContext.getExtension('KHR_parallel_shader_compile');
|
|
493
|
+
if (!headless && parallelCompile && typeof renderer.compileAsync === 'function') {
|
|
494
|
+
try {
|
|
495
|
+
await renderer.compileAsync(scene, camera);
|
|
496
|
+
}
|
|
497
|
+
catch {
|
|
498
|
+
// Fall back to lazy compilation.
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Idempotency guard for disposeGame — see below. Declared here (not inside
|
|
502
|
+
// disposeGame) so hotReload can also refuse to run on an already-disposed
|
|
503
|
+
// adapter.
|
|
504
|
+
let disposed = false;
|
|
505
|
+
// Run one teardown step in isolation: a throwing step (e.g. a game's own
|
|
506
|
+
// cleanup misbehaving, or a WASM free() panicking) must
|
|
507
|
+
// not abort the remaining steps — otherwise a single bad step leaks
|
|
508
|
+
// the Rapier world and skips input.dispose() with no way to retry (a
|
|
509
|
+
// second call is a no-op once `disposed` is set). Logs and continues.
|
|
510
|
+
const safeStep = (label, fn) => {
|
|
511
|
+
try {
|
|
512
|
+
fn();
|
|
513
|
+
}
|
|
514
|
+
catch (err) {
|
|
515
|
+
console.error(`SetupThreeRootAdapter: teardown step "${label}" threw (continuing)`, err);
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
const disposeGame = () => {
|
|
519
|
+
// A second stop() must be a safe no-op — do NOT double-free the Rapier
|
|
520
|
+
// world/EventQueue (WASM handles panic/throw on a second free) or
|
|
521
|
+
// double-run teardown side effects.
|
|
522
|
+
if (disposed)
|
|
523
|
+
return;
|
|
524
|
+
disposed = true;
|
|
525
|
+
safeStep('currentCleanup.dispose', () => {
|
|
526
|
+
currentCleanup?.dispose();
|
|
527
|
+
currentCleanup = null;
|
|
528
|
+
});
|
|
529
|
+
safeStep('sparkRenderer.dispose', () => {
|
|
530
|
+
if (!sparkRenderer)
|
|
531
|
+
return;
|
|
532
|
+
sparkRenderer.removeFromParent();
|
|
533
|
+
disposeSparkRendererWhenIdle(sparkRenderer);
|
|
534
|
+
sparkRenderer = null;
|
|
535
|
+
});
|
|
536
|
+
// The scene instance's NavMeshManager (if any) was just disposed above
|
|
537
|
+
// (currentCleanup.dispose calls `inst.navMesh?.dispose(scene)`) — clear
|
|
538
|
+
// the wrapper too, or `mounted.systems.navigation` would keep pointing
|
|
539
|
+
// at a freed manager (R8 item 7). Unconditional: harmless if navigation
|
|
540
|
+
// was never set.
|
|
541
|
+
safeStep('clear navigation adapter', () => {
|
|
542
|
+
delete systemAdapters.navigation;
|
|
543
|
+
});
|
|
544
|
+
if (!headless) {
|
|
545
|
+
safeStep('removeEventListener(keydown)', () => window.removeEventListener('keydown', onDebugToggle));
|
|
546
|
+
}
|
|
547
|
+
// P3 — the MOUNT-LOCAL clock only, and only when this mount created one
|
|
548
|
+
// (a bare mount with no Game shell). The game-scoped clock is disposed by
|
|
549
|
+
// `GameInternal.dispose()`, which `create-runtime.ts` runs after every
|
|
550
|
+
// root has torn down — because disposing ONE world's mount is a supported
|
|
551
|
+
// way to end a sub-session while the Game keeps running, and destroying a
|
|
552
|
+
// shared clock there froze `now()` and killed the sibling worlds' timers.
|
|
553
|
+
// The asymmetry is the tell: `seededRandom` and `debugRegistry` are
|
|
554
|
+
// game-scoped too, and this teardown destroys neither (it strips only its
|
|
555
|
+
// OWN slice of the registry, a few steps below).
|
|
556
|
+
//
|
|
557
|
+
// Sim timers still go BEFORE the Rapier world they would touch: a pending
|
|
558
|
+
// `disposeAfter`/`after` fires into a live Rapier world, and once that is
|
|
559
|
+
// gone it would operate on freed WASM handles.
|
|
560
|
+
// Disposing cancels every timer and rejects every pending `delay` with an
|
|
561
|
+
// `AbortError` (which awaiting game code is documented to tolerate,
|
|
562
|
+
// exactly like an aborted `fetch`).
|
|
563
|
+
if (ownsClock)
|
|
564
|
+
safeStep('clock.dispose', () => clock.dispose());
|
|
565
|
+
// Before the scene is emptied below: dropping the listeners
|
|
566
|
+
// first means the teardown's ~N `scene.remove()` calls do no index
|
|
567
|
+
// bookkeeping, and — the failure mode this exists to prevent — no
|
|
568
|
+
// `childadded` listener is left behind on a disposed scene.
|
|
569
|
+
safeStep('sceneIndex.dispose', () => ctx.sceneIndex.dispose());
|
|
570
|
+
safeStep('remove rigid bodies', () => {
|
|
571
|
+
const handles = [];
|
|
572
|
+
physics.rapierWorld.forEachRigidBody((b) => handles.push(b.handle));
|
|
573
|
+
for (const h of handles) {
|
|
574
|
+
const b = physics.rapierWorld.getRigidBody(h);
|
|
575
|
+
if (b)
|
|
576
|
+
physics.rapierWorld.removeRigidBody(b);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
safeStep('rapierWorld.free', () => physics.rapierWorld.free());
|
|
580
|
+
safeStep('eventQueue.free', () => physics.eventQueue.free()); // WASM handle — must be freed manually
|
|
581
|
+
safeStep('physicsRegistry.clear', () => physicsRegistry.clear());
|
|
582
|
+
safeStep('scene children dispose', () => {
|
|
583
|
+
for (const obj of [...scene.children]) {
|
|
584
|
+
scene.remove(obj);
|
|
585
|
+
obj.traverse((node) => {
|
|
586
|
+
if (node instanceof THREE.Mesh) {
|
|
587
|
+
if (!hasUserData(node, '__sharedGeometry'))
|
|
588
|
+
node.geometry?.dispose();
|
|
589
|
+
if (Array.isArray(node.material))
|
|
590
|
+
node.material.forEach((m) => {
|
|
591
|
+
m.dispose();
|
|
592
|
+
});
|
|
593
|
+
else
|
|
594
|
+
node.material?.dispose();
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
scene.fog = null;
|
|
599
|
+
scene.background = null;
|
|
600
|
+
scene.environment = null;
|
|
601
|
+
});
|
|
602
|
+
safeStep('clearAssetCaches', () => clearAssetCaches());
|
|
603
|
+
safeStep('debugDraw.clear', () => debugDraw.clear());
|
|
604
|
+
// R8 item 7 (C) — disposeGame (Stop) used to skip both of these, unlike
|
|
605
|
+
// hotReload (warm restart), which already ran them: a game lifecycle
|
|
606
|
+
// system registered via `ctx.systems.register(...)` never had its
|
|
607
|
+
// `dispose()` hook called on a full Stop, and a game-registered
|
|
608
|
+
// systemAdapters kind (e.g. a `setup`-registered 'networking') survived
|
|
609
|
+
// a Stop and would be visible on a STALE `mounted.systems` even though
|
|
610
|
+
// the game that owned it is gone. Same two steps, same relative
|
|
611
|
+
// ordering as hotReload (before the
|
|
612
|
+
// engine-owned audio/input teardown below, which hotReload doesn't
|
|
613
|
+
// touch at all since it warm-restarts rather than tearing engine
|
|
614
|
+
// subsystems down).
|
|
615
|
+
safeStep('systems.removeAllNonEngine', () => systems.removeAllNonEngine());
|
|
616
|
+
safeStep('strip game-registered systemAdapters', () => {
|
|
617
|
+
for (const kind of Object.keys(systemAdapters)) {
|
|
618
|
+
if (!engineAdapterKinds.has(kind))
|
|
619
|
+
delete systemAdapters[kind];
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
// A game-hosted mount owns only its world-scoped registrations; sibling
|
|
623
|
+
// roots remain alive during root replacement. A bare standalone adapter
|
|
624
|
+
// owns the registry and can clear it globally.
|
|
625
|
+
safeStep('debugRegistry.strip', () => host.game ? debugRegistry.strip(this.id) : debugRegistry.strip());
|
|
626
|
+
// Release analyser taps BEFORE the audio teardown below. A consumer
|
|
627
|
+
// (the editor's meter poll) should dispose its own handle, but Stop must
|
|
628
|
+
// never leak taps regardless.
|
|
629
|
+
//
|
|
630
|
+
// This used to ride on the Tone introspection module's disposer, which
|
|
631
|
+
// meant a game that never touched Tone leaked them — the module was
|
|
632
|
+
// never loaded, so nothing ever called this. `acquireMeters` belongs to
|
|
633
|
+
// the base adapter (535d248dc), so its release does too, and the world
|
|
634
|
+
// that created the adapter is what disposes it.
|
|
635
|
+
safeStep('audio meters release', () => {
|
|
636
|
+
const audioAdapter = systemAdapters.audio;
|
|
637
|
+
if (audioAdapter)
|
|
638
|
+
releaseAudioMeters(audioAdapter);
|
|
639
|
+
});
|
|
640
|
+
if (!headless) {
|
|
641
|
+
safeStep('audio teardown', () => {
|
|
642
|
+
audio.masterGain.gain.value = 0;
|
|
643
|
+
audio.listener.context.suspend();
|
|
644
|
+
camera.remove(audio.listener);
|
|
645
|
+
composer.dispose();
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
if (ownsInput)
|
|
649
|
+
safeStep('input.dispose', () => input.dispose());
|
|
650
|
+
// W4b: restore any pending capture wrappers and reject a capture in
|
|
651
|
+
// flight (a Stop mid-capture must not leave the context patched).
|
|
652
|
+
safeStep('renderDebug.dispose', () => renderDebugWiring?.dispose());
|
|
653
|
+
safeStep('gpuTimer.dispose', () => gpuTimer?.dispose());
|
|
654
|
+
safeStep('viewportShading.dispose', () => viewportShading.dispose());
|
|
655
|
+
};
|
|
656
|
+
const hotReload = async (newSetup, newEditorPreview) => {
|
|
657
|
+
if (disposed) {
|
|
658
|
+
throw new Error('SetupThreeRootAdapter: cannot hotReload after dispose()/stop()');
|
|
659
|
+
}
|
|
660
|
+
// Full parity with disposeGame for the parts a warm restart rebuilds:
|
|
661
|
+
// game cleanup, component batch teardown (in that
|
|
662
|
+
// ordering, BEFORE bodies are pulled from the world — see disposeGame),
|
|
663
|
+
// scene-UI dispose (React root unmount), and bulk system removal. Each
|
|
664
|
+
// step isolated the same way as disposeGame — one throwing step (e.g. a
|
|
665
|
+
// component dispose) must not abort the rest of the warm restart.
|
|
666
|
+
safeStep('currentCleanup.dispose', () => {
|
|
667
|
+
currentCleanup?.dispose();
|
|
668
|
+
currentCleanup = null;
|
|
669
|
+
});
|
|
670
|
+
// Clear any navigation adapter a previous `setup` registered: `hotReload`'s
|
|
671
|
+
// `newSetup` (below) never auto-wires a replacement, so leaving the old
|
|
672
|
+
// wrapper would point at a disposed manager. A `newSetup` that wants navigation may
|
|
673
|
+
// register its own via `ctx.registerSystemAdapter('navigation', ...)`.
|
|
674
|
+
safeStep('clear navigation adapter', () => {
|
|
675
|
+
delete systemAdapters.navigation;
|
|
676
|
+
});
|
|
677
|
+
safeStep('remove rigid bodies', () => {
|
|
678
|
+
const handles = [];
|
|
679
|
+
physics.rapierWorld.forEachRigidBody((b) => handles.push(b.handle));
|
|
680
|
+
for (const h of handles) {
|
|
681
|
+
const b = physics.rapierWorld.getRigidBody(h);
|
|
682
|
+
if (b)
|
|
683
|
+
physics.rapierWorld.removeRigidBody(b);
|
|
684
|
+
}
|
|
685
|
+
physicsRegistry.clear();
|
|
686
|
+
});
|
|
687
|
+
safeStep('scene children dispose', () => {
|
|
688
|
+
for (const obj of [...scene.children]) {
|
|
689
|
+
if (engineSceneChildren.has(obj))
|
|
690
|
+
continue;
|
|
691
|
+
scene.remove(obj);
|
|
692
|
+
obj.traverse((node) => {
|
|
693
|
+
if (node instanceof THREE.Mesh) {
|
|
694
|
+
if (!hasUserData(node, '__sharedGeometry'))
|
|
695
|
+
node.geometry?.dispose();
|
|
696
|
+
if (Array.isArray(node.material))
|
|
697
|
+
node.material.forEach((m) => {
|
|
698
|
+
m.dispose();
|
|
699
|
+
});
|
|
700
|
+
else
|
|
701
|
+
node.material?.dispose();
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
scene.fog = null;
|
|
706
|
+
scene.background = null;
|
|
707
|
+
scene.environment = null;
|
|
708
|
+
});
|
|
709
|
+
safeStep('debugDraw.clear', () => debugDraw.clear());
|
|
710
|
+
// Bulk-remove every system the outgoing game registered (this restart's
|
|
711
|
+
// predecessor) so warm restarts never accumulate duplicate systems —
|
|
712
|
+
// engine systems registered before mount()'s boundary mark are untouched.
|
|
713
|
+
safeStep('systems.removeAllNonEngine', () => systems.removeAllNonEngine());
|
|
714
|
+
// Strip every GAME-registered systemAdapters kind left by the outgoing
|
|
715
|
+
// game (including any override of a first-party kind) so the disposed
|
|
716
|
+
// game's networking/etc. adapter is never exposed to the next setup —
|
|
717
|
+
// see registerSystemAdapter's docstring in runtime/types.ts. ('navigation'
|
|
718
|
+
// is engine-kind and is NOT touched here — it's a first-party adapter now,
|
|
719
|
+
// kept in sync with the current scene's NavMeshManager by the dedicated
|
|
720
|
+
// `clear navigation adapter` step above instead.) The new setup below
|
|
721
|
+
// either re-registers a kind or it stays absent.
|
|
722
|
+
safeStep('strip game-registered systemAdapters', () => {
|
|
723
|
+
for (const kind of Object.keys(systemAdapters)) {
|
|
724
|
+
if (!engineAdapterKinds.has(kind))
|
|
725
|
+
delete systemAdapters[kind];
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
// Hot-reload re-seed (spec §3.1): the incoming game's setup (below)
|
|
729
|
+
// re-registers its providers/commands under the SAME worldId — strip()
|
|
730
|
+
// clears the outgoing game's registrations first so that re-seed is
|
|
731
|
+
// silent (no stale duplicate-name warning) rather than "replacing" a
|
|
732
|
+
// dead game's entries. Defect 2 fix: scoped to THIS mount's worldId
|
|
733
|
+
// (`this.id`) — a blind global strip() here wiped every OTHER live
|
|
734
|
+
// world's registrations on every warm restart of just this one, plus
|
|
735
|
+
// every react-door registration (whose `useEffect` cleanup never
|
|
736
|
+
// re-fires to restore them after someone else's strip).
|
|
737
|
+
safeStep('debugRegistry.strip', () => debugRegistry.strip(this.id));
|
|
738
|
+
currentCleanup = await newSetup(ctx, newEditorPreview);
|
|
739
|
+
};
|
|
740
|
+
return {
|
|
741
|
+
kind: 'three',
|
|
742
|
+
scene,
|
|
743
|
+
camera,
|
|
744
|
+
firstParty: true,
|
|
745
|
+
drivesOwnLoop: false,
|
|
746
|
+
update: (dt) => {
|
|
747
|
+
systems.run(dt);
|
|
748
|
+
postFrame();
|
|
749
|
+
},
|
|
750
|
+
resize: (w, h) => {
|
|
751
|
+
const safeWidth = Math.max(1, w);
|
|
752
|
+
const safeHeight = Math.max(1, h);
|
|
753
|
+
composer.setSize(safeWidth, safeHeight);
|
|
754
|
+
camera.aspect = safeWidth / safeHeight;
|
|
755
|
+
camera.updateProjectionMatrix();
|
|
756
|
+
},
|
|
757
|
+
dispose: disposeGame,
|
|
758
|
+
systems: systemAdapters,
|
|
759
|
+
ctx,
|
|
760
|
+
composer: headless ? null : composer,
|
|
761
|
+
setViewportShadingMode: (mode) => {
|
|
762
|
+
viewportShadingMode = mode;
|
|
763
|
+
},
|
|
764
|
+
hotReload,
|
|
765
|
+
frame: {
|
|
766
|
+
runPhase: (phase, dt) => systems.runPhase(phase, dt),
|
|
767
|
+
endFrame: postFrame,
|
|
768
|
+
},
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
/** Headless input stand-in — `InputManager` attaches `window` listeners in its
|
|
773
|
+
* constructor, which Node lacks. A headless mount never reads input. */
|
|
774
|
+
function headlessInput() {
|
|
775
|
+
return {
|
|
776
|
+
poll: () => { },
|
|
777
|
+
endFrame: () => { },
|
|
778
|
+
dispose: () => { },
|
|
779
|
+
loadMap: async () => { },
|
|
780
|
+
isPressed: () => false,
|
|
781
|
+
getAxis: () => 0,
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
/** Headless audio stand-in (no Web Audio). Only the fields the mount/cleanup
|
|
785
|
+
* touch are present. */
|
|
786
|
+
function headlessAudio() {
|
|
787
|
+
return {
|
|
788
|
+
listener: undefined,
|
|
789
|
+
masterGain: { gain: { value: 1 } },
|
|
790
|
+
};
|
|
791
|
+
}
|