@vgai/engine 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/adapter/authoring.d.ts +404 -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 +43 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -0
- package/dist/adapter/colyseus-networking-adapter.js +38 -0
- package/dist/adapter/first-party-systems.d.ts +38 -0
- package/dist/adapter/first-party-systems.d.ts.map +1 -0
- package/dist/adapter/first-party-systems.js +77 -0
- package/dist/adapter/game-adapter.d.ts +133 -0
- package/dist/adapter/game-adapter.d.ts.map +1 -0
- package/dist/adapter/game-adapter.js +10 -0
- package/dist/adapter/host-context.d.ts +73 -0
- package/dist/adapter/host-context.d.ts.map +1 -0
- package/dist/adapter/host-context.js +16 -0
- package/dist/adapter/index.d.ts +22 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +15 -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/overlay-applier.d.ts +118 -0
- package/dist/adapter/ingest/overlay-applier.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-applier.js +132 -0
- package/dist/adapter/ingest/overlay-apply.d.ts +60 -0
- package/dist/adapter/ingest/overlay-apply.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-apply.js +101 -0
- package/dist/adapter/ingest/overlay-file.d.ts +79 -0
- package/dist/adapter/ingest/overlay-file.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-file.js +66 -0
- package/dist/adapter/ingest/overlay-report.d.ts +113 -0
- package/dist/adapter/ingest/overlay-report.d.ts.map +1 -0
- package/dist/adapter/ingest/overlay-report.js +103 -0
- package/dist/adapter/ingest/scene-capture.d.ts +97 -0
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -0
- package/dist/adapter/ingest/scene-capture.js +248 -0
- package/dist/adapter/ingest/upstream-pin.d.ts +47 -0
- package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -0
- package/dist/adapter/ingest/upstream-pin.js +50 -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 +12 -0
- package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -0
- package/dist/adapter/rapier-physics-adapter.js +46 -0
- package/dist/adapter/system-adapter.d.ts +133 -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/adapter/vgai-scene-game-adapter.d.ts +116 -0
- package/dist/adapter/vgai-scene-game-adapter.d.ts.map +1 -0
- package/dist/adapter/vgai-scene-game-adapter.js +714 -0
- package/dist/adapter/world-kind.d.ts +30 -0
- package/dist/adapter/world-kind.d.ts.map +1 -0
- package/dist/adapter/world-kind.js +18 -0
- package/dist/ai/navigation.d.ts +64 -0
- package/dist/ai/navigation.d.ts.map +1 -0
- package/dist/ai/navigation.js +133 -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 +250 -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 +37 -0
- package/dist/animation/gsap-registration.d.ts +119 -0
- package/dist/animation/gsap-registration.d.ts.map +1 -0
- package/dist/animation/gsap-registration.js +92 -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 +242 -0
- package/dist/animation/theatre-director.d.ts.map +1 -0
- package/dist/animation/theatre-director.js +235 -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 +111 -0
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -0
- package/dist/animation/xstate-animation-binding.js +307 -0
- package/dist/animation/xstate-animation-meta.d.ts +228 -0
- package/dist/animation/xstate-animation-meta.d.ts.map +1 -0
- package/dist/animation/xstate-animation-meta.js +221 -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/index.d.ts +4 -0
- package/dist/audio/index.d.ts.map +1 -0
- package/dist/audio/index.js +24 -0
- package/dist/audio/tone-clock-binding.d.ts +75 -0
- package/dist/audio/tone-clock-binding.d.ts.map +1 -0
- package/dist/audio/tone-clock-binding.js +29 -0
- package/dist/audio/tone-context.d.ts +114 -0
- package/dist/audio/tone-context.d.ts.map +1 -0
- package/dist/audio/tone-context.js +39 -0
- package/dist/audio/tone-offline-render.d.ts +90 -0
- package/dist/audio/tone-offline-render.d.ts.map +1 -0
- package/dist/audio/tone-offline-render.js +56 -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/core/game-loop.d.ts +21 -0
- package/dist/core/game-loop.d.ts.map +1 -0
- package/dist/core/game-loop.js +130 -0
- package/dist/core/system-runner.d.ts +120 -0
- package/dist/core/system-runner.d.ts.map +1 -0
- package/dist/core/system-runner.js +286 -0
- package/dist/core/types.d.ts +58 -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 +110 -0
- package/dist/data/data-check-core.d.ts.map +1 -0
- package/dist/data/data-check-core.js +178 -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/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/logger.d.ts +30 -0
- package/dist/dev/logger.d.ts.map +1 -0
- package/dist/dev/logger.js +90 -0
- package/dist/ecs/component-manager.d.ts +189 -0
- package/dist/ecs/component-manager.d.ts.map +1 -0
- package/dist/ecs/component-manager.js +676 -0
- package/dist/ecs/game-component.d.ts +97 -0
- package/dist/ecs/game-component.d.ts.map +1 -0
- package/dist/ecs/game-component.js +80 -0
- package/dist/ecs/hmr-swap-report.d.ts +53 -0
- package/dist/ecs/hmr-swap-report.d.ts.map +1 -0
- package/dist/ecs/hmr-swap-report.js +55 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/input/input-manager.d.ts +596 -0
- package/dist/input/input-manager.d.ts.map +1 -0
- package/dist/input/input-manager.js +1564 -0
- package/dist/input/input-types.d.ts +171 -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 +43 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +59 -0
- package/dist/manifest/index.d.ts +5 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +8 -0
- package/dist/manifest/load-file.d.ts +4 -0
- package/dist/manifest/load-file.d.ts.map +1 -0
- package/dist/manifest/load-file.js +14 -0
- package/dist/manifest/load.d.ts +117 -0
- package/dist/manifest/load.d.ts.map +1 -0
- package/dist/manifest/load.js +255 -0
- package/dist/manifest/schema.d.ts +173 -0
- package/dist/manifest/schema.d.ts.map +1 -0
- package/dist/manifest/schema.js +302 -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/physics/trigger-dispatch.d.ts +24 -0
- package/dist/physics/trigger-dispatch.d.ts.map +1 -0
- package/dist/physics/trigger-dispatch.js +80 -0
- package/dist/react/game-state.d.ts +105 -0
- package/dist/react/game-state.d.ts.map +1 -0
- package/dist/react/game-state.js +144 -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 +96 -0
- package/dist/react/use-selection.d.ts +75 -0
- package/dist/react/use-selection.d.ts.map +1 -0
- package/dist/react/use-selection.js +90 -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/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 +230 -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 +36 -0
- package/dist/render/render-settings.d.ts.map +1 -0
- package/dist/render/render-settings.js +51 -0
- package/dist/runtime/create-runtime.d.ts +280 -0
- package/dist/runtime/create-runtime.d.ts.map +1 -0
- package/dist/runtime/create-runtime.js +795 -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 +433 -0
- package/dist/runtime/game.d.ts.map +1 -0
- package/dist/runtime/game.js +569 -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 +129 -0
- package/dist/runtime/mount-game.d.ts.map +1 -0
- package/dist/runtime/mount-game.js +152 -0
- package/dist/runtime/mount-manifest.d.ts +119 -0
- package/dist/runtime/mount-manifest.d.ts.map +1 -0
- package/dist/runtime/mount-manifest.js +204 -0
- package/dist/runtime/render-audio-control.d.ts +110 -0
- package/dist/runtime/render-audio-control.d.ts.map +1 -0
- package/dist/runtime/render-audio-control.js +111 -0
- package/dist/runtime/render-control.d.ts +291 -0
- package/dist/runtime/render-control.d.ts.map +1 -0
- package/dist/runtime/render-control.js +264 -0
- package/dist/runtime/render-seed.d.ts +57 -0
- package/dist/runtime/render-seed.d.ts.map +1 -0
- package/dist/runtime/render-seed.js +74 -0
- package/dist/runtime/scene-ui-bridge.d.ts +69 -0
- package/dist/runtime/scene-ui-bridge.d.ts.map +1 -0
- package/dist/runtime/scene-ui-bridge.js +23 -0
- package/dist/runtime/scene-ui-data.d.ts +13 -0
- package/dist/runtime/scene-ui-data.d.ts.map +1 -0
- package/dist/runtime/scene-ui-data.js +92 -0
- package/dist/runtime/state-bridge.d.ts +47 -0
- package/dist/runtime/state-bridge.d.ts.map +1 -0
- package/dist/runtime/state-bridge.js +53 -0
- package/dist/runtime/types.d.ts +179 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +1 -0
- package/dist/scene/asset-loaders.d.ts +79 -0
- package/dist/scene/asset-loaders.d.ts.map +1 -0
- package/dist/scene/asset-loaders.js +141 -0
- package/dist/scene/asset-paths.d.ts +11 -0
- package/dist/scene/asset-paths.d.ts.map +1 -0
- package/dist/scene/asset-paths.js +108 -0
- package/dist/scene/asset-ref-check.d.ts +88 -0
- package/dist/scene/asset-ref-check.d.ts.map +1 -0
- package/dist/scene/asset-ref-check.js +214 -0
- package/dist/scene/asset-registry.d.ts +30 -0
- package/dist/scene/asset-registry.d.ts.map +1 -0
- package/dist/scene/asset-registry.js +50 -0
- package/dist/scene/collider-dimensions.d.ts +63 -0
- package/dist/scene/collider-dimensions.d.ts.map +1 -0
- package/dist/scene/collider-dimensions.js +98 -0
- package/dist/scene/component-registry.d.ts +17 -0
- package/dist/scene/component-registry.d.ts.map +1 -0
- package/dist/scene/component-registry.js +28 -0
- package/dist/scene/defaults.d.ts +252 -0
- package/dist/scene/defaults.d.ts.map +1 -0
- package/dist/scene/defaults.js +151 -0
- package/dist/scene/geometries/index.d.ts +8 -0
- package/dist/scene/geometries/index.d.ts.map +1 -0
- package/dist/scene/geometries/index.js +7 -0
- package/dist/scene/geometries/terrain.d.ts +2 -0
- package/dist/scene/geometries/terrain.d.ts.map +1 -0
- package/dist/scene/geometries/terrain.js +37 -0
- package/dist/scene/geometry-registry.d.ts +24 -0
- package/dist/scene/geometry-registry.d.ts.map +1 -0
- package/dist/scene/geometry-registry.js +20 -0
- package/dist/scene/instance-registry.d.ts +36 -0
- package/dist/scene/instance-registry.d.ts.map +1 -0
- package/dist/scene/instance-registry.js +55 -0
- package/dist/scene/instancers/grid.d.ts +2 -0
- package/dist/scene/instancers/grid.d.ts.map +1 -0
- package/dist/scene/instancers/grid.js +36 -0
- package/dist/scene/instancers/index.d.ts +8 -0
- package/dist/scene/instancers/index.d.ts.map +1 -0
- package/dist/scene/instancers/index.js +7 -0
- package/dist/scene/light-camera-factory.d.ts +23 -0
- package/dist/scene/light-camera-factory.d.ts.map +1 -0
- package/dist/scene/light-camera-factory.js +68 -0
- package/dist/scene/material-factory.d.ts +17 -0
- package/dist/scene/material-factory.d.ts.map +1 -0
- package/dist/scene/material-factory.js +197 -0
- package/dist/scene/material-registry.d.ts +40 -0
- package/dist/scene/material-registry.d.ts.map +1 -0
- package/dist/scene/material-registry.js +44 -0
- package/dist/scene/materials/index.d.ts +8 -0
- package/dist/scene/materials/index.d.ts.map +1 -0
- package/dist/scene/materials/index.js +7 -0
- package/dist/scene/materials/water.d.ts +2 -0
- package/dist/scene/materials/water.d.ts.map +1 -0
- package/dist/scene/materials/water.js +54 -0
- package/dist/scene/parse.d.ts +23 -0
- package/dist/scene/parse.d.ts.map +1 -0
- package/dist/scene/parse.js +179 -0
- package/dist/scene/particles-factory.d.ts +33 -0
- package/dist/scene/particles-factory.d.ts.map +1 -0
- package/dist/scene/particles-factory.js +282 -0
- package/dist/scene/scene-apply.d.ts +109 -0
- package/dist/scene/scene-apply.d.ts.map +1 -0
- package/dist/scene/scene-apply.js +326 -0
- package/dist/scene/scene-diff-schema.d.ts +285 -0
- package/dist/scene/scene-diff-schema.d.ts.map +1 -0
- package/dist/scene/scene-diff-schema.js +92 -0
- package/dist/scene/scene-diff-types.d.ts +40 -0
- package/dist/scene/scene-diff-types.d.ts.map +1 -0
- package/dist/scene/scene-diff-types.js +14 -0
- package/dist/scene/scene-loader.d.ts +177 -0
- package/dist/scene/scene-loader.d.ts.map +1 -0
- package/dist/scene/scene-loader.js +1213 -0
- package/dist/scene/scene-query.d.ts +11 -0
- package/dist/scene/scene-query.d.ts.map +1 -0
- package/dist/scene/scene-query.js +63 -0
- package/dist/scene/scene-types.d.ts +10 -0
- package/dist/scene/scene-types.d.ts.map +1 -0
- package/dist/scene/scene-types.js +8 -0
- package/dist/scene/scene-version.d.ts +26 -0
- package/dist/scene/scene-version.d.ts.map +1 -0
- package/dist/scene/scene-version.js +34 -0
- package/dist/scene/schema/animation.d.ts +8 -0
- package/dist/scene/schema/animation.d.ts.map +1 -0
- package/dist/scene/schema/animation.js +42 -0
- package/dist/scene/schema/audio.d.ts +13 -0
- package/dist/scene/schema/audio.d.ts.map +1 -0
- package/dist/scene/schema/audio.js +22 -0
- package/dist/scene/schema/camera.d.ts +28 -0
- package/dist/scene/schema/camera.d.ts.map +1 -0
- package/dist/scene/schema/camera.js +17 -0
- package/dist/scene/schema/collider.d.ts +40 -0
- package/dist/scene/schema/collider.d.ts.map +1 -0
- package/dist/scene/schema/collider.js +55 -0
- package/dist/scene/schema/entity-ref.d.ts +31 -0
- package/dist/scene/schema/entity-ref.d.ts.map +1 -0
- package/dist/scene/schema/entity-ref.js +61 -0
- package/dist/scene/schema/entity.d.ts +1036 -0
- package/dist/scene/schema/entity.d.ts.map +1 -0
- package/dist/scene/schema/entity.js +153 -0
- package/dist/scene/schema/environment.d.ts +425 -0
- package/dist/scene/schema/environment.d.ts.map +1 -0
- package/dist/scene/schema/environment.js +357 -0
- package/dist/scene/schema/index.d.ts +39 -0
- package/dist/scene/schema/index.d.ts.map +1 -0
- package/dist/scene/schema/index.js +20 -0
- package/dist/scene/schema/instances.d.ts +14 -0
- package/dist/scene/schema/instances.d.ts.map +1 -0
- package/dist/scene/schema/instances.js +28 -0
- package/dist/scene/schema/joint.d.ts +23 -0
- package/dist/scene/schema/joint.d.ts.map +1 -0
- package/dist/scene/schema/joint.js +23 -0
- package/dist/scene/schema/light.d.ts +38 -0
- package/dist/scene/schema/light.d.ts.map +1 -0
- package/dist/scene/schema/light.js +26 -0
- package/dist/scene/schema/material.d.ts +199 -0
- package/dist/scene/schema/material.d.ts.map +1 -0
- package/dist/scene/schema/material.js +99 -0
- package/dist/scene/schema/mesh.d.ts +48 -0
- package/dist/scene/schema/mesh.d.ts.map +1 -0
- package/dist/scene/schema/mesh.js +82 -0
- package/dist/scene/schema/particles.d.ts +2178 -0
- package/dist/scene/schema/particles.d.ts.map +1 -0
- package/dist/scene/schema/particles.js +321 -0
- package/dist/scene/schema/physics.d.ts +56 -0
- package/dist/scene/schema/physics.d.ts.map +1 -0
- package/dist/scene/schema/physics.js +37 -0
- package/dist/scene/schema/scene-file.d.ts +304 -0
- package/dist/scene/schema/scene-file.d.ts.map +1 -0
- package/dist/scene/schema/scene-file.js +262 -0
- package/dist/scene/schema/shadow.d.ts +28 -0
- package/dist/scene/schema/shadow.d.ts.map +1 -0
- package/dist/scene/schema/shadow.js +20 -0
- package/dist/scene/schema/spline.d.ts +25 -0
- package/dist/scene/schema/spline.d.ts.map +1 -0
- package/dist/scene/schema/spline.js +15 -0
- package/dist/scene/schema/tuples.d.ts +12 -0
- package/dist/scene/schema/tuples.d.ts.map +1 -0
- package/dist/scene/schema/tuples.js +14 -0
- package/dist/scene/schema/ui.d.ts +880 -0
- package/dist/scene/schema/ui.d.ts.map +1 -0
- package/dist/scene/schema/ui.js +506 -0
- package/dist/scene/user-data.d.ts +171 -0
- package/dist/scene/user-data.d.ts.map +1 -0
- package/dist/scene/user-data.js +137 -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 +69 -0
- package/dist/setup/setup-renderer.d.ts.map +1 -0
- package/dist/setup/setup-renderer.js +363 -0
- package/dist/tools/define-tool.d.ts +105 -0
- package/dist/tools/define-tool.d.ts.map +1 -0
- package/dist/tools/define-tool.js +82 -0
- package/dist/world2d/authoring-2d.d.ts +76 -0
- package/dist/world2d/authoring-2d.d.ts.map +1 -0
- package/dist/world2d/authoring-2d.js +176 -0
- package/dist/world2d/capture-to-scene2d.d.ts +5 -0
- package/dist/world2d/capture-to-scene2d.d.ts.map +1 -0
- package/dist/world2d/capture-to-scene2d.js +49 -0
- package/dist/world2d/collision-2d.d.ts +52 -0
- package/dist/world2d/collision-2d.d.ts.map +1 -0
- package/dist/world2d/collision-2d.js +70 -0
- package/dist/world2d/components-2d.d.ts +47 -0
- package/dist/world2d/components-2d.d.ts.map +1 -0
- package/dist/world2d/components-2d.js +77 -0
- package/dist/world2d/index.d.ts +34 -0
- package/dist/world2d/index.d.ts.map +1 -0
- package/dist/world2d/index.js +32 -0
- package/dist/world2d/ingest-iframe-2d.d.ts +82 -0
- package/dist/world2d/ingest-iframe-2d.d.ts.map +1 -0
- package/dist/world2d/ingest-iframe-2d.js +166 -0
- package/dist/world2d/ingest2d.d.ts +97 -0
- package/dist/world2d/ingest2d.d.ts.map +1 -0
- package/dist/world2d/ingest2d.js +40 -0
- package/dist/world2d/physics2d-registry.d.ts +24 -0
- package/dist/world2d/physics2d-registry.d.ts.map +1 -0
- package/dist/world2d/physics2d-registry.js +38 -0
- package/dist/world2d/pixi-game-adapter.d.ts +107 -0
- package/dist/world2d/pixi-game-adapter.d.ts.map +1 -0
- package/dist/world2d/pixi-game-adapter.js +211 -0
- package/dist/world2d/pixi-surface.d.ts +39 -0
- package/dist/world2d/pixi-surface.d.ts.map +1 -0
- package/dist/world2d/pixi-surface.js +47 -0
- package/dist/world2d/scene-capture-2d.d.ts +43 -0
- package/dist/world2d/scene-capture-2d.d.ts.map +1 -0
- package/dist/world2d/scene-capture-2d.js +92 -0
- package/dist/world2d/scene2d-loader.d.ts +27 -0
- package/dist/world2d/scene2d-loader.d.ts.map +1 -0
- package/dist/world2d/scene2d-loader.js +274 -0
- package/dist/world2d/schema/entity2d.d.ts +102 -0
- package/dist/world2d/schema/entity2d.d.ts.map +1 -0
- package/dist/world2d/schema/entity2d.js +119 -0
- package/dist/world2d/schema/physics2d.d.ts +46 -0
- package/dist/world2d/schema/physics2d.d.ts.map +1 -0
- package/dist/world2d/schema/physics2d.js +45 -0
- package/dist/world2d/schema/sprite.d.ts +42 -0
- package/dist/world2d/schema/sprite.d.ts.map +1 -0
- package/dist/world2d/schema/sprite.js +63 -0
- package/dist/world2d/schema/tilemap.d.ts +15 -0
- package/dist/world2d/schema/tilemap.d.ts.map +1 -0
- package/dist/world2d/schema/tilemap.js +19 -0
- package/dist/world2d/schema/tuples2d.d.ts +20 -0
- package/dist/world2d/schema/tuples2d.d.ts.map +1 -0
- package/dist/world2d/schema/tuples2d.js +19 -0
- package/dist/world2d/system-adapters-2d.d.ts +16 -0
- package/dist/world2d/system-adapters-2d.d.ts.map +1 -0
- package/dist/world2d/system-adapters-2d.js +37 -0
- package/dist/world2d/transform-writer-2d.d.ts +16 -0
- package/dist/world2d/transform-writer-2d.d.ts.map +1 -0
- package/dist/world2d/transform-writer-2d.js +22 -0
- package/dist/world2d/types.d.ts +53 -0
- package/dist/world2d/types.d.ts.map +1 -0
- package/dist/world2d/types.js +1 -0
- package/package.json +18 -4
- package/src/adapter/authoring.ts +10 -0
- package/src/adapter/first-party-systems.ts +23 -34
- package/src/adapter/index.ts +1 -3
- package/src/adapter/system-adapter.ts +15 -22
- package/src/adapter/vgai-scene-game-adapter.ts +5 -14
- package/src/animation/anim-graph-types.ts +12 -43
- package/src/animation/animation-clock.ts +479 -0
- package/src/animation/camera-ownership.ts +467 -0
- package/src/animation/cinematic-cues.ts +451 -0
- package/src/animation/clip-map.ts +41 -0
- package/src/animation/gsap-registration.ts +184 -0
- package/src/animation/theatre-clock-binding.ts +111 -0
- package/src/animation/theatre-director.ts +347 -0
- package/src/animation/theatre-object-binding.ts +661 -0
- package/src/animation/xstate-animation-binding.ts +398 -0
- package/src/animation/xstate-animation-meta.ts +282 -0
- package/src/audio/index.ts +39 -7
- package/src/audio/tone-clock-binding.ts +98 -0
- package/src/audio/tone-context.ts +129 -0
- package/src/audio/tone-offline-render.ts +167 -0
- package/src/audio/wav-encode.ts +119 -0
- package/src/core/game-loop.ts +17 -0
- package/src/core/types.ts +13 -0
- package/src/data/data-asset.ts +167 -0
- package/src/data/data-check-core.ts +242 -0
- package/src/data/data-ref.ts +145 -0
- package/src/data/vite-plugin-data.ts +290 -0
- package/src/index.ts +48 -0
- package/src/input/input-manager.ts +1206 -32
- package/src/input/input-types.ts +155 -3
- package/src/input/prompt-labels.ts +122 -0
- package/src/input/rebind-controller.ts +105 -0
- package/src/input/schema.ts +206 -52
- package/src/react/use-data.ts +114 -0
- package/src/react/use-selection.tsx +135 -0
- package/src/runtime/create-runtime.ts +30 -9
- package/src/runtime/render-audio-control.ts +168 -0
- package/src/runtime/render-control.ts +522 -0
- package/src/runtime/render-seed.ts +79 -0
- package/src/runtime/types.ts +0 -5
- package/src/scene/asset-loaders.ts +10 -36
- package/src/scene/asset-paths.ts +0 -2
- package/src/scene/asset-ref-check.ts +248 -0
- package/src/scene/parse.ts +133 -0
- package/src/scene/scene-loader.ts +81 -96
- package/src/scene/schema/animation.ts +30 -79
- package/src/scene/schema/entity.ts +20 -0
- package/src/scene/schema/index.ts +2 -12
- package/src/scene/user-data.ts +8 -9
- package/src/setup/setup-renderer.ts +9 -2
- package/src/tools/define-tool.ts +152 -0
- package/src/animation/anim-graph.ts +0 -406
- package/src/animation/anim-system.ts +0 -28
- package/src/animation/property-track.ts +0 -178
- package/src/animation/schema.ts +0 -204
- package/src/audio/ambient.ts +0 -300
- package/src/audio/impacts.ts +0 -212
- package/src/audio/movement.ts +0 -140
- package/src/audio/musical.ts +0 -200
- package/src/audio/ui-sounds.ts +0 -171
- package/src/audio/vehicle.ts +0 -235
- package/src/audio/weapons.ts +0 -152
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { VgaiSceneGameAdapter } from '../adapter/vgai-scene-game-adapter';
|
|
3
|
+
import { assertNever } from '../adapter/world-kind';
|
|
4
|
+
import { createAssetCache } from '../assets';
|
|
5
|
+
import { createGameLoop } from '../core/game-loop';
|
|
6
|
+
import { createHostRenderer } from '../setup/setup-renderer';
|
|
7
|
+
import { createGame, createWorldInstance, isFirstPartyMounted, } from './game';
|
|
8
|
+
import { createInputRouter, stackOrder } from './input-router';
|
|
9
|
+
// Re-export the camera-precedence helper from its new home so existing importers
|
|
10
|
+
// (`import { adoptSceneCamera } from '@engine/runtime/create-runtime'`) keep working.
|
|
11
|
+
export { adoptSceneCamera } from '../adapter/vgai-scene-game-adapter';
|
|
12
|
+
/**
|
|
13
|
+
* Register a threejs world for a freshly-mounted game onto the Game shell.
|
|
14
|
+
* Shared by `createGameRuntime` and every headless test harness
|
|
15
|
+
* (`test/game-root.test.ts`, `test/frame-order.test.ts`,
|
|
16
|
+
* `test/game-two-worlds.test.ts`) so there is exactly one code path for this
|
|
17
|
+
* wiring (T7.1 slice 1, id-generalized in slice 3 for the two-world proof —
|
|
18
|
+
* `docs/GAME-ROOT-DESIGN.md` §8 stage 3) — `physics`/`collisions`/`camera`/
|
|
19
|
+
* `frame` are populated from the mount's first-party `GameContext`/
|
|
20
|
+
* `VgaiMountedGame` when available, left `undefined` otherwise (an external
|
|
21
|
+
* adapter's mount has none of these first-party handles — `frame` in
|
|
22
|
+
* particular is what makes `GameInternal.runFrame` (T7.1 slice 2) fall back
|
|
23
|
+
* to a single opaque `mounted.update` call per substep for such a world).
|
|
24
|
+
*/
|
|
25
|
+
function firstPartyExtras(mounted) {
|
|
26
|
+
if (!isFirstPartyMounted(mounted))
|
|
27
|
+
return {};
|
|
28
|
+
return {
|
|
29
|
+
physics: mounted.ctx.physics,
|
|
30
|
+
collisions: mounted.ctx.collisions,
|
|
31
|
+
camera: mounted.ctx.camera,
|
|
32
|
+
frame: mounted.frame,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Register a threejs world onto the Game shell. `opts.id` defaults to
|
|
37
|
+
* `'main'` — the single-world callers (`createGameRuntime`, slice-1/2 tests)
|
|
38
|
+
* are unaffected; a second/third world (T7.1 slice 3's two-world proof, and
|
|
39
|
+
* real multi-world manifests later) pass an explicit id. This is the ONE
|
|
40
|
+
* registration code path for a threejs world, whatever its id.
|
|
41
|
+
*/
|
|
42
|
+
export function registerThreeWorld(game, adapter, mounted, opts) {
|
|
43
|
+
const world = createWorldInstance({
|
|
44
|
+
id: opts?.id ?? 'main',
|
|
45
|
+
kind: 'threejs',
|
|
46
|
+
pausable: opts?.pausable ?? true,
|
|
47
|
+
adapter,
|
|
48
|
+
mounted,
|
|
49
|
+
scene: mounted.scene,
|
|
50
|
+
...firstPartyExtras(mounted),
|
|
51
|
+
});
|
|
52
|
+
// T7.2 slice 2 — one-time world-wiring backfill (closes the KNOWN GAP
|
|
53
|
+
// documented on `resolveWorldInstance` in `ecs/component-manager.ts`):
|
|
54
|
+
// scene-authored components were attached by the scene loader DURING the
|
|
55
|
+
// `mount()` call that just returned `mounted`, before this `WorldInstance`
|
|
56
|
+
// existed — so `instance.world` was left `undefined` for every one of
|
|
57
|
+
// them. `adoptWorld` backfills it now, once, for every already-attached
|
|
58
|
+
// instance still missing a world. Attaches made AFTER this point (e.g.
|
|
59
|
+
// runtime-spawned entities once the game is ticking) still resolve their
|
|
60
|
+
// world directly at attach time, unaffected by this call.
|
|
61
|
+
if (isFirstPartyMounted(mounted)) {
|
|
62
|
+
mounted.ctx.components.adoptWorld(world);
|
|
63
|
+
}
|
|
64
|
+
game.registerWorld(world);
|
|
65
|
+
return world;
|
|
66
|
+
}
|
|
67
|
+
/** Thin alias kept for existing single-world callers (`createGameRuntime`,
|
|
68
|
+
* `test/game-root.test.ts`, `test/frame-order.test.ts`) — registers the
|
|
69
|
+
* default `'main'` world via the SAME `registerThreeWorld` code path. */
|
|
70
|
+
export function registerDefaultThreeWorld(game, adapter, mounted) {
|
|
71
|
+
return registerThreeWorld(game, adapter, mounted);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Register a pixijs world onto the Game shell — the pixi analog of
|
|
75
|
+
* {@link registerThreeWorld}, mirroring the wiring
|
|
76
|
+
* `world2d/pixi-game-adapter.ts`'s `createWorld2DRuntime` performs for its
|
|
77
|
+
* OWN (separate) Game, and `test/game-three-plus-pixi.test.ts` performs by
|
|
78
|
+
* hand onto a SHARED Game. `createGameRuntime`'s worlds path
|
|
79
|
+
* (T6.1 slice 1) is the first PRODUCTION caller that registers a pixijs
|
|
80
|
+
* world alongside other worlds on one Game; this is its one wiring code
|
|
81
|
+
* path, so a second/third pixijs world (or a future caller) never
|
|
82
|
+
* re-derives it. `adapter` is typed as the real {@link Pixi2DGameAdapter}
|
|
83
|
+
* shape (T7.5 — previously the generic `GameAdapter`, which forced a cast at
|
|
84
|
+
* every call site since a pixi adapter's `mount` takes a `World2DHost`, not
|
|
85
|
+
* `HostContext`); `WorldInstance.adapter` itself only needs `.id`
|
|
86
|
+
* (`AdapterHandle`, `runtime/game.ts`), so this passes through with zero cast.
|
|
87
|
+
*
|
|
88
|
+
* Section 7.4-5 (design-gap catalog), UPDATED by wave 2 (D-Y1,
|
|
89
|
+
* docs/EXTERNAL-REACT-AND-MODULE-DESIGN.md §1/§4 slice S1): this guard used
|
|
90
|
+
* to THROW for a non-first-party mount, reasoning "today this can never
|
|
91
|
+
* actually fire (Pixi2DGameAdapter.mount is typed to always return a
|
|
92
|
+
* MountedGame2D, whose ctx/physics2d/collisions2d/frame are all REQUIRED
|
|
93
|
+
* fields, so there is no foreign-mount shape that could reach here)". Wave 2
|
|
94
|
+
* proved that reasoning stale: a `kind: 'pixijs'` `{ module }` adapter's mount
|
|
95
|
+
* result IS exactly such a foreign shape — `Pixi2DGameAdapter`'s TYPE promises
|
|
96
|
+
* `MountedGame2D`, but `adapter-resolver.ts`'s `resolveAllWorlds` reaches it
|
|
97
|
+
* only via `adapter as unknown as Pixi2DGameAdapter` (a custom module's actual
|
|
98
|
+
* return value is merely `MountedPixiWorld`-shaped, T7.5's cross-kind union),
|
|
99
|
+
* so the "never actually fire" premise no longer holds the moment a custom
|
|
100
|
+
* pixi module world reaches the worlds path. Rather than keep the throw (which
|
|
101
|
+
* would re-create exactly the kind of dead end D-X6 named and D-Y1 exists to
|
|
102
|
+
* remove), this now degrades EXACTLY like `registerThreeWorld`'s own foreign-
|
|
103
|
+
* mount handling above (`firstPartyExtras`): `physics2d`/`collisions2d`/
|
|
104
|
+
* `frame`/component-adoption are omitted for a non-first-party mount instead
|
|
105
|
+
* of read from a `.ctx` that doesn't exist — `stage` (the one field every
|
|
106
|
+
* `MountedPixiWorld`, first-party or not, actually carries) is always real.
|
|
107
|
+
*/
|
|
108
|
+
function pixiFirstPartyExtras(mounted) {
|
|
109
|
+
// Same structural check `firstPartyExtras`/`isFirstPartyMounted` use — see
|
|
110
|
+
// this function's own doc comment (above `registerPixiWorld`) for why this
|
|
111
|
+
// stays a plain boolean read rather than a type-guarded `isFirstPartyMounted`
|
|
112
|
+
// call (that guard's threejs-shaped return type would narrow `mounted` to
|
|
113
|
+
// `never` here).
|
|
114
|
+
if (mounted.firstParty !== true)
|
|
115
|
+
return {};
|
|
116
|
+
return {
|
|
117
|
+
physics2d: mounted.ctx.physics2d,
|
|
118
|
+
collisions2d: mounted.ctx.collisions2d,
|
|
119
|
+
frame: mounted.frame,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function registerPixiWorld(game, adapter, mounted, opts) {
|
|
123
|
+
const id = opts?.id ?? 'main';
|
|
124
|
+
const mountedIsFirstParty = mounted.firstParty === true;
|
|
125
|
+
const world = createWorldInstance({
|
|
126
|
+
id,
|
|
127
|
+
kind: 'pixijs',
|
|
128
|
+
pausable: opts?.pausable ?? true,
|
|
129
|
+
adapter,
|
|
130
|
+
mounted,
|
|
131
|
+
stage: mounted.stage,
|
|
132
|
+
...pixiFirstPartyExtras(mounted),
|
|
133
|
+
});
|
|
134
|
+
// Mirrors `registerThreeWorld`'s adoptWorld backfill above — the pixi
|
|
135
|
+
// world's scene-authored components attach during `mount()`, before this
|
|
136
|
+
// `WorldInstance` exists. Skipped for a non-first-party mount (a foreign
|
|
137
|
+
// `{ module }` adapter's `ComponentManager` doesn't exist to adopt into —
|
|
138
|
+
// same "nothing for a custom adapter to backfill" reasoning
|
|
139
|
+
// `registerReactWorld`'s doc comment records for react).
|
|
140
|
+
if (mountedIsFirstParty)
|
|
141
|
+
mounted.ctx.components.adoptWorld(world);
|
|
142
|
+
game.registerWorld(world);
|
|
143
|
+
return world;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Register a react world onto the Game shell (T6.2 slice 1,
|
|
147
|
+
* `docs/REACT-WORLD-DESIGN.md` §1.B) — the react analog of
|
|
148
|
+
* {@link registerThreeWorld}/{@link registerPixiWorld}. A react world has no
|
|
149
|
+
* `ComponentManager` to adopt (D8: react entities host no GameComponents —
|
|
150
|
+
* there is nothing for `adoptWorld` to backfill, unlike the three/pixi
|
|
151
|
+
* siblings above) and no `frame` hooks (react's own `createRoot` schedules
|
|
152
|
+
* its commits; `GameInternal.runFrame` correctly leaves a world with no
|
|
153
|
+
* `frame` untouched by its opaque-`update` fallback too, since
|
|
154
|
+
* `MountedReactGame` declares no `update`). `adapter` is typed as the real
|
|
155
|
+
* {@link ReactWorldAdapter} shape (T7.5, same reasoning as
|
|
156
|
+
* `registerPixiWorld`'s doc comment above) — `WorldInstance.adapter` only
|
|
157
|
+
* needs `.id`, so this passes through with zero cast.
|
|
158
|
+
*/
|
|
159
|
+
export function registerReactWorld(game, adapter, mounted, container, opts) {
|
|
160
|
+
const world = createWorldInstance({
|
|
161
|
+
id: opts?.id ?? 'main',
|
|
162
|
+
kind: 'react',
|
|
163
|
+
pausable: opts?.pausable ?? true,
|
|
164
|
+
adapter,
|
|
165
|
+
mounted,
|
|
166
|
+
container,
|
|
167
|
+
});
|
|
168
|
+
game.registerWorld(world);
|
|
169
|
+
return world;
|
|
170
|
+
}
|
|
171
|
+
function isWorldsConfig(config) {
|
|
172
|
+
return 'worlds' in config;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Bootstrap the game host and mount a game adapter.
|
|
176
|
+
*
|
|
177
|
+
* `createGameRuntime` is the HOST: it owns the canvas/renderer(s), the loop,
|
|
178
|
+
* the UI overlay, and the asset cache, and it mounts a {@link GameAdapter}
|
|
179
|
+
* (or several, via the worlds path — T6.1 slice 1) against that context. It
|
|
180
|
+
* knows nothing about `.vscn`, `GameComponent`, Rapier, or `GameSetupFn` —
|
|
181
|
+
* those live inside the first-party {@link VgaiSceneGameAdapter}, which is
|
|
182
|
+
* the default implementer when no explicit `adapter` is supplied on the
|
|
183
|
+
* legacy path. Dispatches on `config`'s shape: {@link WorldsRuntimeConfig}
|
|
184
|
+
* (`worlds` present) goes through {@link createWorldsGameRuntime};
|
|
185
|
+
* everything else is the byte-compatible legacy single-world path.
|
|
186
|
+
*/
|
|
187
|
+
export async function createGameRuntime(config) {
|
|
188
|
+
if (isWorldsConfig(config)) {
|
|
189
|
+
return createWorldsGameRuntime(config);
|
|
190
|
+
}
|
|
191
|
+
const { canvas, width, height, adapter, ...sceneConfig } = config;
|
|
192
|
+
const w = Math.max(1, width ?? canvas.width);
|
|
193
|
+
const h = Math.max(1, height ?? canvas.height);
|
|
194
|
+
// --- Host primitives (neutral — no first-party assumptions) ---
|
|
195
|
+
const renderer = createHostRenderer(canvas, w, h);
|
|
196
|
+
const assets = createAssetCache();
|
|
197
|
+
let uiContainer;
|
|
198
|
+
const existingUi = document.getElementById('example-ui');
|
|
199
|
+
if (existingUi instanceof HTMLDivElement) {
|
|
200
|
+
uiContainer = existingUi;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
uiContainer = document.createElement('div');
|
|
204
|
+
uiContainer.id = 'example-ui';
|
|
205
|
+
uiContainer.style.cssText =
|
|
206
|
+
'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:2;';
|
|
207
|
+
(canvas.parentElement ?? document.getElementById('ui-overlay') ?? document.body).appendChild(uiContainer);
|
|
208
|
+
}
|
|
209
|
+
const extraUpdaters = new Set();
|
|
210
|
+
const loopHandle = {
|
|
211
|
+
onUpdate(fn) {
|
|
212
|
+
extraUpdaters.add(fn);
|
|
213
|
+
return () => extraUpdaters.delete(fn);
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
// --- Game root (T7.1 slice 1 + slice 2) ---
|
|
217
|
+
// The loop's update callback used to close directly over `mounted`, which
|
|
218
|
+
// only exists after `game.mount(host)` resolves. The Game shell (and thus
|
|
219
|
+
// its `loop`) must exist BEFORE mount so the mount can see `host.game` — so
|
|
220
|
+
// the callback instead consults a mutable ref, set once mount resolves.
|
|
221
|
+
// Behavior is identical: `loop.start()` still only runs after mount, same
|
|
222
|
+
// pause/step/stop semantics below. `drivesOwnLoop` handling now lives
|
|
223
|
+
// INSIDE `game.runFrame` (T7.1 slice 2) rather than being checked here —
|
|
224
|
+
// for today's single first-party (or single opaque host-driven) world this
|
|
225
|
+
// is byte-identical to the old `if (!mountedRef.drivesOwnLoop) mountedRef
|
|
226
|
+
// .update?.(dt)` guard (see `runFrame`'s doc comment in `runtime/game.ts`).
|
|
227
|
+
// `game` is referenced here before its `const` below only textually — this
|
|
228
|
+
// closure isn't invoked until `loop.start()`, well after `game` exists.
|
|
229
|
+
let mountedRef = null;
|
|
230
|
+
const loop = createGameLoop({
|
|
231
|
+
fixedTimestep: 1 / 60,
|
|
232
|
+
maxSubSteps: 8,
|
|
233
|
+
update: (dt) => {
|
|
234
|
+
if (mountedRef)
|
|
235
|
+
game.runFrame(dt);
|
|
236
|
+
for (const fn of extraUpdaters)
|
|
237
|
+
fn(dt);
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
const game = createGame({ loop, assets });
|
|
241
|
+
const host = {
|
|
242
|
+
three: THREE,
|
|
243
|
+
surface: { canvas, width: w, height: h },
|
|
244
|
+
renderer,
|
|
245
|
+
loop: loopHandle,
|
|
246
|
+
assets,
|
|
247
|
+
ui: uiContainer,
|
|
248
|
+
game,
|
|
249
|
+
requestSystem: () => null,
|
|
250
|
+
};
|
|
251
|
+
// --- Mount the game adapter (first-party default, or an explicit adapter) ---
|
|
252
|
+
const gameAdapter = adapter ?? new VgaiSceneGameAdapter(sceneConfig);
|
|
253
|
+
const mounted = await gameAdapter.mount(host);
|
|
254
|
+
mountedRef = mounted;
|
|
255
|
+
// Register the default (and, in this slice, only) threejs world now that
|
|
256
|
+
// mount has produced a MountedGame — the world's `threeRoot()` must return
|
|
257
|
+
// the SAME scene instance the adapter mounted (identity is the contract).
|
|
258
|
+
registerDefaultThreeWorld(game, gameAdapter, mounted);
|
|
259
|
+
// Expose scene & camera for dev tools / e2e tests
|
|
260
|
+
if (import.meta.env?.DEV) {
|
|
261
|
+
const w = window;
|
|
262
|
+
w['__vgaiScene'] = mounted.scene;
|
|
263
|
+
w['__vgaiCamera'] = mounted.camera;
|
|
264
|
+
}
|
|
265
|
+
loop.start();
|
|
266
|
+
function fullCleanup() {
|
|
267
|
+
loop.stop();
|
|
268
|
+
mounted.dispose();
|
|
269
|
+
renderer.dispose();
|
|
270
|
+
renderer.forceContextLoss();
|
|
271
|
+
// Retract the dev/e2e globals THIS session published (identity-guarded so
|
|
272
|
+
// a newer session's globals are never clobbered by an older session's
|
|
273
|
+
// stop). Leaving them dangling let e2e waits pass vacuously against a
|
|
274
|
+
// stopped game's scene/camera.
|
|
275
|
+
if (import.meta.env?.DEV) {
|
|
276
|
+
const w = window;
|
|
277
|
+
if (w['__vgaiScene'] === mounted.scene)
|
|
278
|
+
delete w['__vgaiScene'];
|
|
279
|
+
if (w['__vgaiCamera'] === mounted.camera)
|
|
280
|
+
delete w['__vgaiCamera'];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
stop: fullCleanup,
|
|
285
|
+
// D10/T7.6: `Game.play` (`runtime/game.ts`) is the real control surface —
|
|
286
|
+
// it fans out over every `pausable` world (just the one default world on
|
|
287
|
+
// this legacy single-world path) via its loop-gate/audio-gate capability,
|
|
288
|
+
// reporting honestly wherever a world can't actually be gated, and keeps
|
|
289
|
+
// `render` running every substep while paused (the old `loop.timeScale =
|
|
290
|
+
// 0` here starved the accumulator entirely — per D1, zero substeps means
|
|
291
|
+
// zero renders too — which is the bug this replaces, not a behavior to
|
|
292
|
+
// preserve).
|
|
293
|
+
pause() {
|
|
294
|
+
game.play.pause();
|
|
295
|
+
},
|
|
296
|
+
resume() {
|
|
297
|
+
game.play.resume();
|
|
298
|
+
},
|
|
299
|
+
step() {
|
|
300
|
+
game.play.step();
|
|
301
|
+
},
|
|
302
|
+
resize(rw, rh) {
|
|
303
|
+
const safeWidth = Math.max(1, rw);
|
|
304
|
+
const safeHeight = Math.max(1, rh);
|
|
305
|
+
renderer.setSize(safeWidth, safeHeight);
|
|
306
|
+
mounted.resize?.(safeWidth, safeHeight);
|
|
307
|
+
},
|
|
308
|
+
get scene() {
|
|
309
|
+
return mounted.scene;
|
|
310
|
+
},
|
|
311
|
+
get camera() {
|
|
312
|
+
return mounted.camera;
|
|
313
|
+
},
|
|
314
|
+
get mounted() {
|
|
315
|
+
return mounted;
|
|
316
|
+
},
|
|
317
|
+
game,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// Worlds path (T6.1 slice 1 — docs/MULTI-WORLD-DESIGN.md §1.A/B/C)
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
/** A stand-in `THREE.WebGLRenderer` for headless (`headless:true`) threejs
|
|
324
|
+
* worlds — mirrors `VgaiSceneGameAdapter.mount`'s own `headlessComposer`
|
|
325
|
+
* pattern (`adapter/vgai-scene-game-adapter.ts`): a headless first-party
|
|
326
|
+
* mount never calls a render-phase method on `host.renderer` at all, so
|
|
327
|
+
* this only needs to satisfy the handful of calls THIS file itself makes
|
|
328
|
+
* (`setPixelRatio`/`setClearColor`/`setSize` at mount, `dispose`/
|
|
329
|
+
* `forceContextLoss` at teardown) — never a real GL call. Node conformance
|
|
330
|
+
* tests only; never used when `headless` is left `false`/absent. */
|
|
331
|
+
function createHeadlessRendererStub() {
|
|
332
|
+
return {
|
|
333
|
+
setPixelRatio() { },
|
|
334
|
+
setClearColor() { },
|
|
335
|
+
setSize() { },
|
|
336
|
+
dispose() { },
|
|
337
|
+
forceContextLoss() { },
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Derive a REAL default `hitTest` for a pixijs world from its own mounted
|
|
342
|
+
* stage (D5 §2a/COMPOSITION-DESIGN.md proof item 2: "pixi `EventBoundary
|
|
343
|
+
* .hitTest`") — used only when the caller's `WorldMountSpec` didn't declare
|
|
344
|
+
* an explicit `hitTest`. `(x, y)` are container-relative pixels, matching
|
|
345
|
+
* the coordinates the pixi surface renders at (the stage itself carries no
|
|
346
|
+
* pan/zoom — that lives on `pixi-surface.ts`'s child `world` container, per
|
|
347
|
+
* its own doc comment), so this is a correct global hit test with no extra
|
|
348
|
+
* coordinate mapping. Duck-typed against `mounted.ctx.app` (never a
|
|
349
|
+
* value-import of `pixi.js`) so this file stays type-only w.r.t. Pixi;
|
|
350
|
+
* returns `undefined` (no default claim) if the mount's `app`/event
|
|
351
|
+
* boundary isn't shaped as expected — degrade to "does not claim" rather
|
|
352
|
+
* than throw. A non-first-party mount (D-Y1, wave 2 — same finding
|
|
353
|
+
* `registerPixiWorld`'s doc comment records: a `{ module }` adapter's mount
|
|
354
|
+
* has no `.ctx` at all) has nothing to duck-type here either, so this checks
|
|
355
|
+
* the SAME `firstParty` brand FIRST and degrades identically — never claims,
|
|
356
|
+
* never throws.
|
|
357
|
+
*/
|
|
358
|
+
function derivePixiHitTest(mounted) {
|
|
359
|
+
if (mounted.firstParty !== true)
|
|
360
|
+
return undefined;
|
|
361
|
+
const app = mounted.ctx.app;
|
|
362
|
+
const rootBoundary = app?.renderer?.events?.rootBoundary;
|
|
363
|
+
if (!rootBoundary || typeof rootBoundary.hitTest !== 'function')
|
|
364
|
+
return undefined;
|
|
365
|
+
return (x, y) => rootBoundary.hitTest(x, y) != null;
|
|
366
|
+
}
|
|
367
|
+
/** Mount one threejs `WorldMountSpec` (canvas + renderer construction, per
|
|
368
|
+
* D5 §1/§4, then `registerThreeWorld`). Split out of
|
|
369
|
+
* `createWorldsGameRuntime` purely to keep that function's own branching
|
|
370
|
+
* simple — see `mountOnePixiWorld` for the pixijs sibling. */
|
|
371
|
+
async function mountOneThreeWorld(spec, ctx) {
|
|
372
|
+
const { game, canvas, w, h, dpr, isBottom, headless, uiContainer, loopHandle, assets } = ctx;
|
|
373
|
+
const renderer = headless
|
|
374
|
+
? createHeadlessRendererStub()
|
|
375
|
+
: createHostRenderer(canvas, w, h, undefined, {
|
|
376
|
+
alpha: !isBottom,
|
|
377
|
+
preserveDrawingBuffer: true,
|
|
378
|
+
});
|
|
379
|
+
renderer.setSize(w, h, false); // backing resolution only — CSS stacking owns layout size
|
|
380
|
+
// Defect E4.R1 (Fable review): `createHostRenderer`'s OWN construction-time
|
|
381
|
+
// `setSize` call (`setup-renderer.ts`, `updateStyle` defaulting `true` —
|
|
382
|
+
// unchanged there on purpose, see below) stamps `canvas.style.width`/
|
|
383
|
+
// `.height` to literal `${w}px`/`${h}px` the moment the renderer is built,
|
|
384
|
+
// OVERWRITING the container-relative `100%`/`100%` the surface-stacking
|
|
385
|
+
// loop above just set. The `renderer.setSize(w, h, false)` line right above
|
|
386
|
+
// this comment does NOT undo that stamp (updateStyle:false only skips
|
|
387
|
+
// TOUCHING style, it can't un-stamp a previous call) — so without this
|
|
388
|
+
// re-assertion, every worlds-path threejs canvas' on-screen size was
|
|
389
|
+
// permanently pinned to whatever `w`/`h` it happened to mount at (usually
|
|
390
|
+
// the manifest's `resolution`, since standalone builds mount before a real
|
|
391
|
+
// container size is known — see `mount-manifest.ts`). Re-asserting here
|
|
392
|
+
// makes the CSS layout size and the render-buffer resolution fully
|
|
393
|
+
// independent, exactly like the react DOM world layer already is: this
|
|
394
|
+
// fixes the template-standalone bug (broken at any viewport other than the
|
|
395
|
+
// manifest's `resolution`, which is also Playwright's DEFAULT viewport —
|
|
396
|
+
// why it went unnoticed) and, for free, tri-world's pre-existing
|
|
397
|
+
// resize-staleness (the worlds-path `resize()` below always calls
|
|
398
|
+
// `setSize(rw, rh, false)`, so nothing else was ever going to update this
|
|
399
|
+
// canvas' CSS after mount). `createHostRenderer`'s own default
|
|
400
|
+
// (`updateStyle:true`) is intentionally left alone — the LEGACY
|
|
401
|
+
// single-canvas path (editor play-mode's non-multi-world branch) still
|
|
402
|
+
// relies on that construction-time stamp + its own later `updateStyle:true`
|
|
403
|
+
// resizes for byte-identical behavior; this fix touches only the
|
|
404
|
+
// worlds-path canvas, after the fact.
|
|
405
|
+
canvas.style.width = '100%';
|
|
406
|
+
canvas.style.height = '100%';
|
|
407
|
+
renderer.setPixelRatio(dpr);
|
|
408
|
+
if (!isBottom)
|
|
409
|
+
renderer.setClearColor(0x000000, 0); // D5 §1: alpha-clear above the bottom layer
|
|
410
|
+
const host = {
|
|
411
|
+
three: THREE,
|
|
412
|
+
surface: { canvas, width: w, height: h },
|
|
413
|
+
renderer,
|
|
414
|
+
loop: loopHandle,
|
|
415
|
+
assets,
|
|
416
|
+
ui: uiContainer,
|
|
417
|
+
headless,
|
|
418
|
+
game,
|
|
419
|
+
requestSystem: () => null,
|
|
420
|
+
};
|
|
421
|
+
const mounted = await spec.adapter.mount(host);
|
|
422
|
+
registerThreeWorld(game, spec.adapter, mounted, { id: spec.id, pausable: spec.pausable });
|
|
423
|
+
return {
|
|
424
|
+
mountedEntry: { id: spec.id, kind: 'threejs', element: canvas, mounted, renderer },
|
|
425
|
+
routerEntry: { id: spec.id, zOrder: spec.zOrder ?? 0, canvas, hitTest: spec.hitTest },
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
/** Mount one pixijs `WorldMountSpec` (via its own `World2DHost`, per D5
|
|
429
|
+
* §1/§3/§4, then `registerPixiWorld`) — the pixi sibling of
|
|
430
|
+
* `mountOneThreeWorld` above. Derives a real default `hitTest` from the
|
|
431
|
+
* mounted stage when the spec didn't declare one (see `derivePixiHitTest`).
|
|
432
|
+
*
|
|
433
|
+
* Unlike `mountOneThreeWorld`, this needs no explicit `canvas.style.width`/
|
|
434
|
+
* `.height` re-assertion (E4.R1). `pixi-surface.ts` constructs its
|
|
435
|
+
* `Application` with `autoDensity: true`, which makes PIXI ITSELF re-stamp
|
|
436
|
+
* `canvas.style.width`/`.height` (real CSS px, matching the LOGICAL
|
|
437
|
+
* width/height passed to `resize()`) on every `app.renderer.resize()` call —
|
|
438
|
+
* including the one this world's `mounted.resize?.()` triggers from the
|
|
439
|
+
* worlds-path `resize()` below. So while a pixi world's canvas can start
|
|
440
|
+
* pinned to the mount-time `w`/`h` (same as threejs, until PIXI's own
|
|
441
|
+
* construction-time resize runs), it self-heals the moment ANY real
|
|
442
|
+
* `session.resize(rw, rh)` fires — every shipped standalone entry
|
|
443
|
+
* (`packages/editor/template/src/main.ts`, `examples/tri-world/src/main.ts`)
|
|
444
|
+
* already calls `session.resize()` unconditionally right after mount, so
|
|
445
|
+
* this never surfaces as a lasting bug the way threejs' buffer-only resize
|
|
446
|
+
* did (never self-healing, by design — see above). */
|
|
447
|
+
async function mountOnePixiWorld(spec, ctx) {
|
|
448
|
+
const { game, canvas, w, h, dpr, isBottom, uiContainer } = ctx;
|
|
449
|
+
const pixiHost = {
|
|
450
|
+
canvas,
|
|
451
|
+
width: w,
|
|
452
|
+
height: h,
|
|
453
|
+
ui: uiContainer,
|
|
454
|
+
dpr,
|
|
455
|
+
transparent: !isBottom,
|
|
456
|
+
preserveDrawingBuffer: true,
|
|
457
|
+
};
|
|
458
|
+
const mounted = await spec.adapter.mount(pixiHost);
|
|
459
|
+
registerPixiWorld(game, spec.adapter, mounted, {
|
|
460
|
+
id: spec.id,
|
|
461
|
+
pausable: spec.pausable,
|
|
462
|
+
});
|
|
463
|
+
return {
|
|
464
|
+
mountedEntry: {
|
|
465
|
+
id: spec.id,
|
|
466
|
+
kind: 'pixijs',
|
|
467
|
+
element: canvas,
|
|
468
|
+
mounted,
|
|
469
|
+
renderer: undefined,
|
|
470
|
+
},
|
|
471
|
+
routerEntry: {
|
|
472
|
+
id: spec.id,
|
|
473
|
+
zOrder: spec.zOrder ?? 0,
|
|
474
|
+
canvas,
|
|
475
|
+
hitTest: spec.hitTest ?? derivePixiHitTest(mounted),
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Mount one react `WorldMountSpec` (T6.2 slice 1, `docs/REACT-WORLD-DESIGN.md`
|
|
481
|
+
* §1.B/§1.C) — the react sibling of `mountOneThreeWorld`/`mountOnePixiWorld`.
|
|
482
|
+
* Unlike its canvas-backed siblings this returns NO `routerEntry`: a react
|
|
483
|
+
* world's DOM-root layer participates in D5's z-order/box stacking (the
|
|
484
|
+
* caller still creates and positions its `<div>` exactly like a canvas — see
|
|
485
|
+
* `createWorldsGameRuntime`'s stack-building loop) but needs no entry in the
|
|
486
|
+
* delegating router's hit-test loop (§1.C — "DOM layers need no entry in the
|
|
487
|
+
* router's hit-test loop"): the layer's own `pointer-events` discipline
|
|
488
|
+
* (this file sets `none` on the layer by default, mirroring the existing
|
|
489
|
+
* `uiContainer`/HUD rule; the mounted react tree opts specific elements back
|
|
490
|
+
* in with `pointer-events:auto`) is what lets its interactive elements claim
|
|
491
|
+
* events NATIVELY, via the real DOM, with zero router involvement — and lets
|
|
492
|
+
* a click over its non-interactive (transparent) area fall through to the
|
|
493
|
+
* canvas below it via the SAME native DOM hit-testing (a `pointer-events:none`
|
|
494
|
+
* element is invisible to hit-testing entirely, so the click lands on
|
|
495
|
+
* whatever real DOM element is beneath it — the router's normal
|
|
496
|
+
* canvas-vs-canvas forwarding, unaffected by this layer's presence).
|
|
497
|
+
*/
|
|
498
|
+
async function mountOneReactWorld(spec, game,
|
|
499
|
+
/** The ALREADY-created, already-stacked (position/z-index set, appended to
|
|
500
|
+
* `container`) DOM-root layer for this world — see
|
|
501
|
+
* `createWorldsGameRuntime`'s surface-stack loop, which builds a `<div>`
|
|
502
|
+
* for every react-kind spec up front, in the SAME pass that builds every
|
|
503
|
+
* other world's canvas. This function must reuse that exact element (never
|
|
504
|
+
* create its own) so `WorldInstance.reactRoot()` returns the SAME node
|
|
505
|
+
* that is actually positioned in the stack. */
|
|
506
|
+
layer) {
|
|
507
|
+
layer.style.pointerEvents = 'none';
|
|
508
|
+
// Size the layer like the shared uiContainer does: an absolutely-positioned
|
|
509
|
+
// layer with no width/height collapses to zero content size, so a child's
|
|
510
|
+
// own position:absolute offsets resolve against a degenerate containing
|
|
511
|
+
// block (found by the T6.2 slice-3 e2e — clicks landed outside the game).
|
|
512
|
+
layer.style.width = '100%';
|
|
513
|
+
layer.style.height = '100%';
|
|
514
|
+
const reactHost = { container: layer, game };
|
|
515
|
+
const mounted = await spec.adapter.mount(reactHost);
|
|
516
|
+
registerReactWorld(game, spec.adapter, mounted, layer, {
|
|
517
|
+
id: spec.id,
|
|
518
|
+
pausable: spec.pausable,
|
|
519
|
+
});
|
|
520
|
+
return {
|
|
521
|
+
mountedEntry: {
|
|
522
|
+
id: spec.id,
|
|
523
|
+
kind: 'react',
|
|
524
|
+
element: layer,
|
|
525
|
+
mounted,
|
|
526
|
+
renderer: undefined,
|
|
527
|
+
},
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* The worlds-path implementer behind {@link createGameRuntime} (T6.1 slice
|
|
532
|
+
* 1; react worlds added T6.2 slice 1). Builds ONE surface per world — a
|
|
533
|
+
* canvas for threejs/pixijs, a DOM-root `<div>` layer for react — stacked
|
|
534
|
+
* per COMPOSITION-DESIGN D5 §1, z-order/ties exactly matching
|
|
535
|
+
* `manifest/load.ts`'s sort, ONE `Game`, and registers every world onto it
|
|
536
|
+
* via `registerThreeWorld`/`registerPixiWorld`/`registerReactWorld` — the
|
|
537
|
+
* SAME wiring `test/game-three-plus-pixi.test.ts` proves by hand for the
|
|
538
|
+
* three/pixi pair. Worlds MOUNT in `worlds` ARRAY order (GAME-ROOT-DESIGN §4's
|
|
539
|
+
* "manifest declaration order" — the frame/registration axis), independent of
|
|
540
|
+
* `zOrder` (the canvas-stacking/rendering axis) — the two orders can differ
|
|
541
|
+
* and both are honored correctly.
|
|
542
|
+
*/
|
|
543
|
+
/**
|
|
544
|
+
* Dev/e2e-only `window.__vgaiScene`/`__vgaiCamera` exposure for the worlds
|
|
545
|
+
* path's DEFAULT world (E4) — split out of `createWorldsGameRuntime` purely
|
|
546
|
+
* to keep that function's own cyclomatic complexity down. Mirrors the
|
|
547
|
+
* legacy single-world path's identical exposure (above, in this same file),
|
|
548
|
+
* using the SAME "first threejs world, else none" default-world rule the
|
|
549
|
+
* `GameSession.scene`/`.camera` getters alias (`Game.defaultWorld`): a
|
|
550
|
+
* non-threejs default world (or none at all) publishes neither global,
|
|
551
|
+
* exactly like those getters return `undefined` in that case. Returns a
|
|
552
|
+
* retraction callback — identity-guarded (a newer session's globals must
|
|
553
|
+
* never be clobbered by an older session's stop, same reason the legacy
|
|
554
|
+
* path's own cleanup guards it) — that is a no-op when nothing was
|
|
555
|
+
* published (non-DEV build, or non-threejs default world).
|
|
556
|
+
*/
|
|
557
|
+
function installDefaultWorldDevGlobals(game) {
|
|
558
|
+
if (!import.meta.env?.DEV)
|
|
559
|
+
return () => { };
|
|
560
|
+
const defaultMounted = game.defaultWorld.mounted;
|
|
561
|
+
if (defaultMounted.kind !== 'threejs')
|
|
562
|
+
return () => { };
|
|
563
|
+
const scene = defaultMounted.scene;
|
|
564
|
+
const camera = defaultMounted.camera;
|
|
565
|
+
const w = window;
|
|
566
|
+
w['__vgaiScene'] = scene;
|
|
567
|
+
w['__vgaiCamera'] = camera;
|
|
568
|
+
return () => {
|
|
569
|
+
if (w['__vgaiScene'] === scene)
|
|
570
|
+
delete w['__vgaiScene'];
|
|
571
|
+
if (w['__vgaiCamera'] === camera)
|
|
572
|
+
delete w['__vgaiCamera'];
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Mount + register every world spec, in ARRAY (declaration) order — split
|
|
577
|
+
* out of `createWorldsGameRuntime` purely to keep that function's own
|
|
578
|
+
* cyclomatic complexity down (E4, same reason `mountOneThreeWorld`/
|
|
579
|
+
* `mountOnePixiWorld` are already split out). React worlds contribute NO
|
|
580
|
+
* router entry (docs/REACT-WORLD-DESIGN.md §1.C — "DOM layers need no entry
|
|
581
|
+
* in the router's hit-test loop"): their layer's own `pointer-events`
|
|
582
|
+
* discipline handles claim/fall-through natively, with zero router
|
|
583
|
+
* involvement (see `mountOneReactWorld`'s doc comment).
|
|
584
|
+
*/
|
|
585
|
+
async function mountAllWorldSpecs(mountSpecs, bottomId, inputs) {
|
|
586
|
+
const { surfacesById, ...shared } = inputs;
|
|
587
|
+
const mountedEntries = [];
|
|
588
|
+
const routerEntries = [];
|
|
589
|
+
for (const spec of mountSpecs) {
|
|
590
|
+
const isBottom = spec.id === bottomId;
|
|
591
|
+
if (spec.kind === 'react') {
|
|
592
|
+
const layer = surfacesById.get(spec.id);
|
|
593
|
+
const { mountedEntry } = await mountOneReactWorld(spec, shared.game, layer);
|
|
594
|
+
mountedEntries.push(mountedEntry);
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
const canvas = surfacesById.get(spec.id);
|
|
598
|
+
const oneCtx = { ...shared, canvas, isBottom };
|
|
599
|
+
let mountedEntry;
|
|
600
|
+
let routerEntry;
|
|
601
|
+
if (spec.kind === 'threejs') {
|
|
602
|
+
({ mountedEntry, routerEntry } = await mountOneThreeWorld(spec, oneCtx));
|
|
603
|
+
}
|
|
604
|
+
else if (spec.kind === 'pixijs') {
|
|
605
|
+
({ mountedEntry, routerEntry } = await mountOnePixiWorld(spec, oneCtx));
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
// Exhaustiveness guard (§7.4-2): 'react' was already handled by the
|
|
609
|
+
// early `continue` above, so only a hypothetical 4th `WorldKind` can
|
|
610
|
+
// reach here — fail loudly rather than silently defaulting. `spec`
|
|
611
|
+
// itself (not `spec.kind`) is what TS has narrowed to `never`, since
|
|
612
|
+
// `WorldMountSpec` is a discriminated union at the object level.
|
|
613
|
+
assertNever(spec, 'create-runtime mount loop');
|
|
614
|
+
}
|
|
615
|
+
mountedEntries.push(mountedEntry);
|
|
616
|
+
routerEntries.push(routerEntry);
|
|
617
|
+
}
|
|
618
|
+
return { mountedEntries, routerEntries };
|
|
619
|
+
}
|
|
620
|
+
async function createWorldsGameRuntime(config) {
|
|
621
|
+
const { container, worlds: specs, width, height, headless = false } = config;
|
|
622
|
+
if (specs.length === 0) {
|
|
623
|
+
throw new Error('createGameRuntime: `worlds` must contain at least one world.');
|
|
624
|
+
}
|
|
625
|
+
const mountSpecs = specs;
|
|
626
|
+
const w = Math.max(1, width ?? container.clientWidth ?? 0);
|
|
627
|
+
const h = Math.max(1, height ?? container.clientHeight ?? 0);
|
|
628
|
+
const dpr = headless
|
|
629
|
+
? 1
|
|
630
|
+
: Math.min(typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1, 2);
|
|
631
|
+
if (!container.style.position)
|
|
632
|
+
container.style.position = 'relative';
|
|
633
|
+
// --- Surface stack (D5 §1): DOM/z-order follows zOrder, ties -> array
|
|
634
|
+
// order — computed FIRST (bottom -> top) so both the z-index assignment
|
|
635
|
+
// below and the router's default-claim rule share one definition. A react
|
|
636
|
+
// world's DOM-root layer shares this SAME stacking pass (T6.2 slice 1,
|
|
637
|
+
// docs/REACT-WORLD-DESIGN.md §1.B — "one stacking model, no special case")
|
|
638
|
+
// even though it is a `<div>`, not a canvas, and carries no `hitTest` (the
|
|
639
|
+
// router never sees react entries at all — see the dispatch loop below). ---
|
|
640
|
+
const stacked = stackOrder(mountSpecs.map((spec) => ({ id: spec.id, zOrder: spec.zOrder ?? 0, hitTest: spec.hitTest })));
|
|
641
|
+
const bottomId = stacked[0]?.id;
|
|
642
|
+
const kindById = new Map(mountSpecs.map((spec) => [spec.id, spec.kind]));
|
|
643
|
+
const surfacesById = new Map();
|
|
644
|
+
stacked.forEach((entry, i) => {
|
|
645
|
+
const isReact = kindById.get(entry.id) === 'react';
|
|
646
|
+
const surface = document.createElement(isReact ? 'div' : 'canvas');
|
|
647
|
+
if (!isReact) {
|
|
648
|
+
const canvas = surface;
|
|
649
|
+
canvas.width = w;
|
|
650
|
+
canvas.height = h;
|
|
651
|
+
}
|
|
652
|
+
// Defect E4.R1 (Fable review): layout size is ALWAYS container-relative
|
|
653
|
+
// (`width:100%;height:100%`), fully decoupled from the surface's
|
|
654
|
+
// intrinsic buffer resolution (`w`/`h`, set on the canvas element's
|
|
655
|
+
// `width`/`height` ATTRIBUTES above — a device-pixel/render-resolution
|
|
656
|
+
// concern only). Without this, a canvas with no CSS size falls back to
|
|
657
|
+
// its `width`/`height` attribute as its CSS layout size too, so whatever
|
|
658
|
+
// stamped those attributes (`createHostRenderer`'s construction-time
|
|
659
|
+
// `setSize`, `mountOneThreeWorld` below) pins the ON-SCREEN size — see
|
|
660
|
+
// that function's doc comment for the concrete bug this caused (template
|
|
661
|
+
// standalone at any viewport ≠ the manifest resolution). Setting it HERE
|
|
662
|
+
// too (not just in `mountOneThreeWorld`) means every surface, whatever
|
|
663
|
+
// kind, starts container-relative from its very first paint, before any
|
|
664
|
+
// per-kind mount work has even run.
|
|
665
|
+
surface.style.cssText = `position:absolute;top:0;left:0;width:100%;height:100%;z-index:${i + 1};`;
|
|
666
|
+
container.appendChild(surface);
|
|
667
|
+
surfacesById.set(entry.id, surface);
|
|
668
|
+
});
|
|
669
|
+
// One shared UI overlay, above every canvas. I8 render-integration seam
|
|
670
|
+
// (docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md §15 I8, C1 review note):
|
|
671
|
+
// the legacy single-world path (above) reuses/stamps a stable `#example-ui`
|
|
672
|
+
// id so a capture host has a reliable selector for "the HUD/letterbox
|
|
673
|
+
// overlay" — this worlds path had no such id at all, so a render-mode page
|
|
674
|
+
// built on `createWorldsGameRuntime` (multi-world manifests, including a
|
|
675
|
+
// react-HUD-over-threejs cinematic) had no stable selector to composite-
|
|
676
|
+
// screenshot. `vgai-ui-overlay` is deliberately a DIFFERENT id than the
|
|
677
|
+
// legacy path's `example-ui` (not reused/aliased) — the two runtime paths
|
|
678
|
+
// mount genuinely different DOM subtrees, and collapsing them onto one id
|
|
679
|
+
// would make a caller's selector ambiguous about which mount produced it.
|
|
680
|
+
const uiContainer = document.createElement('div');
|
|
681
|
+
uiContainer.id = 'vgai-ui-overlay';
|
|
682
|
+
uiContainer.style.cssText =
|
|
683
|
+
`position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;` +
|
|
684
|
+
`z-index:${stacked.length + 1};`;
|
|
685
|
+
container.appendChild(uiContainer);
|
|
686
|
+
const assets = createAssetCache();
|
|
687
|
+
const extraUpdaters = new Set();
|
|
688
|
+
const loopHandle = {
|
|
689
|
+
onUpdate(fn) {
|
|
690
|
+
extraUpdaters.add(fn);
|
|
691
|
+
return () => extraUpdaters.delete(fn);
|
|
692
|
+
},
|
|
693
|
+
};
|
|
694
|
+
let started = false;
|
|
695
|
+
const loop = createGameLoop({
|
|
696
|
+
fixedTimestep: 1 / 60,
|
|
697
|
+
maxSubSteps: 8,
|
|
698
|
+
update: (dt) => {
|
|
699
|
+
if (started)
|
|
700
|
+
game.runFrame(dt);
|
|
701
|
+
for (const fn of extraUpdaters)
|
|
702
|
+
fn(dt);
|
|
703
|
+
},
|
|
704
|
+
});
|
|
705
|
+
const game = createGame({ loop, assets });
|
|
706
|
+
// --- Mount + register every world, in ARRAY (declaration) order. ---
|
|
707
|
+
// Split out into its own top-level function purely to keep
|
|
708
|
+
// `createWorldsGameRuntime`'s own cyclomatic complexity down (E4) — same
|
|
709
|
+
// reason `mountOneThreeWorld`/`mountOnePixiWorld` are already split out
|
|
710
|
+
// below.
|
|
711
|
+
const { mountedEntries, routerEntries } = await mountAllWorldSpecs(mountSpecs, bottomId, {
|
|
712
|
+
game,
|
|
713
|
+
surfacesById,
|
|
714
|
+
w,
|
|
715
|
+
h,
|
|
716
|
+
dpr,
|
|
717
|
+
headless,
|
|
718
|
+
uiContainer,
|
|
719
|
+
loopHandle,
|
|
720
|
+
assets,
|
|
721
|
+
});
|
|
722
|
+
// --- Delegating input router (D5 §2a) ---
|
|
723
|
+
const router = createInputRouter(container, routerEntries);
|
|
724
|
+
started = true;
|
|
725
|
+
loop.start();
|
|
726
|
+
// Expose scene & camera for dev tools / e2e tests — mirrors the legacy
|
|
727
|
+
// single-world path's identical dev-only exposure above, generalized to
|
|
728
|
+
// the worlds path's DEFAULT world (E4, docs/unified-world-editor/
|
|
729
|
+
// 27-visual-react-editing.md §7 E4): `mountGameFromManifest`/
|
|
730
|
+
// `mountManifestWorlds` route every caller (including a single-threejs-
|
|
731
|
+
// world scaffold project) through THIS path, so a caller migrating off
|
|
732
|
+
// the legacy `{canvas, adapter}` call must not silently lose
|
|
733
|
+
// `window.__vgaiScene`/`__vgaiCamera` — real e2e/dev tooling depends on
|
|
734
|
+
// them (`packages/editor/e2e/tests/04-standalone-game.spec.ts`). Split
|
|
735
|
+
// into its own top-level helper (with its retraction counterpart below)
|
|
736
|
+
// purely to keep this function's own cyclomatic complexity down.
|
|
737
|
+
const retractDevGlobals = installDefaultWorldDevGlobals(game);
|
|
738
|
+
function fullCleanup() {
|
|
739
|
+
loop.stop();
|
|
740
|
+
router.dispose();
|
|
741
|
+
for (const entry of mountedEntries) {
|
|
742
|
+
entry.mounted.dispose();
|
|
743
|
+
entry.renderer?.dispose();
|
|
744
|
+
entry.renderer?.forceContextLoss();
|
|
745
|
+
container.removeChild(entry.element);
|
|
746
|
+
}
|
|
747
|
+
container.removeChild(uiContainer);
|
|
748
|
+
retractDevGlobals();
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
stop: fullCleanup,
|
|
752
|
+
// D10/T7.6: `Game.play` fans out per-world pausable/loop-gate/audio-gate
|
|
753
|
+
// semantics itself now (see the legacy path's identical comment above) —
|
|
754
|
+
// a `pausable: false` world (a menu/HUD world) keeps ticking while every
|
|
755
|
+
// other world freezes, which the old blind `for (const entry of
|
|
756
|
+
// mountedEntries) entry.mounted.setPaused?.(true)` fan-out (with no
|
|
757
|
+
// `pausable` check at all) could never express.
|
|
758
|
+
pause() {
|
|
759
|
+
game.play.pause();
|
|
760
|
+
},
|
|
761
|
+
resume() {
|
|
762
|
+
game.play.resume();
|
|
763
|
+
},
|
|
764
|
+
step() {
|
|
765
|
+
game.play.step();
|
|
766
|
+
},
|
|
767
|
+
resize(rw, rh) {
|
|
768
|
+
const safeWidth = Math.max(1, rw);
|
|
769
|
+
const safeHeight = Math.max(1, rh);
|
|
770
|
+
for (const entry of mountedEntries) {
|
|
771
|
+
entry.renderer?.setSize(safeWidth, safeHeight, false);
|
|
772
|
+
entry.mounted.resize?.(safeWidth, safeHeight);
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
// `GameSession.scene`/`.camera`/`.mounted` alias `Game.defaultWorld` (T6.1
|
|
776
|
+
// slice 1's "first threejs world, else first world" rule) — a legacy
|
|
777
|
+
// convenience shaped for the threejs-only past. T7.5 narrows the read via
|
|
778
|
+
// the `kind` discriminant instead of a blind `.scene`/`.camera` cast
|
|
779
|
+
// through a nonexistent property (identical behavior to before: still
|
|
780
|
+
// `undefined` for a worlds-path session whose default world isn't
|
|
781
|
+
// threejs — real per-world surface routing for that case is T7.6's).
|
|
782
|
+
get scene() {
|
|
783
|
+
const m = game.defaultWorld.mounted;
|
|
784
|
+
return (m.kind === 'threejs' ? m.scene : undefined);
|
|
785
|
+
},
|
|
786
|
+
get camera() {
|
|
787
|
+
const m = game.defaultWorld.mounted;
|
|
788
|
+
return (m.kind === 'threejs' ? m.camera : undefined);
|
|
789
|
+
},
|
|
790
|
+
get mounted() {
|
|
791
|
+
return game.defaultWorld.mounted;
|
|
792
|
+
},
|
|
793
|
+
game,
|
|
794
|
+
};
|
|
795
|
+
}
|