@vgai/engine 0.5.0 → 0.5.2
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/package.json +1 -1
- package/schemas/engine-capabilities.json +4 -4
- package/schemas/vgai-game.schema.json +2 -2
- package/src/adapter/authoring.ts +42 -0
- package/src/adapter/index.ts +3 -0
- package/src/adapter/ingest/overlay-apply.ts +50 -6
- package/src/adapter/ingest/scene-capture.ts +51 -2
- package/src/adapter/vgai-scene-game-adapter.ts +24 -0
- package/src/manifest/schema.ts +7 -3
- package/src/runtime/create-runtime.ts +97 -0
- package/src/runtime/game.ts +11 -1
- package/src/scene/asset-ref-check.ts +1 -1
- package/src/scene/scene-loader.ts +2 -2
- package/src/scene/user-data.ts +18 -0
- package/src/world3d-react/behavior.tsx +19 -11
- package/src/world3d-react/r3f-adapter.tsx +8 -0
- package/dist/adapter/adapter-surface.d.ts +0 -30
- package/dist/adapter/adapter-surface.d.ts.map +0 -1
- package/dist/adapter/adapter-surface.js +0 -18
- package/dist/adapter/authoring.d.ts +0 -461
- package/dist/adapter/authoring.d.ts.map +0 -1
- package/dist/adapter/authoring.js +0 -22
- package/dist/adapter/colyseus-networking-adapter.d.ts +0 -43
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +0 -1
- package/dist/adapter/colyseus-networking-adapter.js +0 -38
- package/dist/adapter/first-party-systems.d.ts +0 -38
- package/dist/adapter/first-party-systems.d.ts.map +0 -1
- package/dist/adapter/first-party-systems.js +0 -77
- package/dist/adapter/game-adapter.d.ts +0 -133
- package/dist/adapter/game-adapter.d.ts.map +0 -1
- package/dist/adapter/game-adapter.js +0 -10
- package/dist/adapter/host-context.d.ts +0 -71
- package/dist/adapter/host-context.d.ts.map +0 -1
- package/dist/adapter/host-context.js +0 -16
- package/dist/adapter/index.d.ts +0 -22
- package/dist/adapter/index.d.ts.map +0 -1
- package/dist/adapter/index.js +0 -15
- package/dist/adapter/ingest/game-contract.d.ts +0 -53
- package/dist/adapter/ingest/game-contract.d.ts.map +0 -1
- package/dist/adapter/ingest/game-contract.js +0 -30
- package/dist/adapter/ingest/overlay-applier.d.ts +0 -118
- package/dist/adapter/ingest/overlay-applier.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-applier.js +0 -132
- package/dist/adapter/ingest/overlay-apply.d.ts +0 -60
- package/dist/adapter/ingest/overlay-apply.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-apply.js +0 -101
- package/dist/adapter/ingest/overlay-file.d.ts +0 -79
- package/dist/adapter/ingest/overlay-file.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-file.js +0 -66
- package/dist/adapter/ingest/overlay-report.d.ts +0 -113
- package/dist/adapter/ingest/overlay-report.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-report.js +0 -103
- package/dist/adapter/ingest/scene-capture.d.ts +0 -97
- package/dist/adapter/ingest/scene-capture.d.ts.map +0 -1
- package/dist/adapter/ingest/scene-capture.js +0 -248
- package/dist/adapter/ingest/upstream-pin.d.ts +0 -47
- package/dist/adapter/ingest/upstream-pin.d.ts.map +0 -1
- package/dist/adapter/ingest/upstream-pin.js +0 -50
- package/dist/adapter/loop-gate-report.d.ts +0 -34
- package/dist/adapter/loop-gate-report.d.ts.map +0 -1
- package/dist/adapter/loop-gate-report.js +0 -37
- package/dist/adapter/rapier-physics-adapter.d.ts +0 -12
- package/dist/adapter/rapier-physics-adapter.d.ts.map +0 -1
- package/dist/adapter/rapier-physics-adapter.js +0 -46
- package/dist/adapter/system-adapter.d.ts +0 -206
- package/dist/adapter/system-adapter.d.ts.map +0 -1
- package/dist/adapter/system-adapter.js +0 -13
- package/dist/adapter/transform.d.ts +0 -18
- package/dist/adapter/transform.d.ts.map +0 -1
- package/dist/adapter/transform.js +0 -1
- package/dist/adapter/vgai-scene-game-adapter.d.ts +0 -108
- package/dist/adapter/vgai-scene-game-adapter.d.ts.map +0 -1
- package/dist/adapter/vgai-scene-game-adapter.js +0 -762
- package/dist/ai/navigation.d.ts +0 -64
- package/dist/ai/navigation.d.ts.map +0 -1
- package/dist/ai/navigation.js +0 -133
- package/dist/animation/anim-graph-types.d.ts +0 -25
- package/dist/animation/anim-graph-types.d.ts.map +0 -1
- package/dist/animation/anim-graph-types.js +0 -1
- package/dist/animation/animation-clock.d.ts +0 -213
- package/dist/animation/animation-clock.d.ts.map +0 -1
- package/dist/animation/animation-clock.js +0 -301
- package/dist/animation/blend-node.d.ts +0 -22
- package/dist/animation/blend-node.d.ts.map +0 -1
- package/dist/animation/blend-node.js +0 -97
- package/dist/animation/camera-ownership.d.ts +0 -250
- package/dist/animation/camera-ownership.d.ts.map +0 -1
- package/dist/animation/camera-ownership.js +0 -169
- package/dist/animation/cinematic-cues.d.ts +0 -200
- package/dist/animation/cinematic-cues.d.ts.map +0 -1
- package/dist/animation/cinematic-cues.js +0 -213
- package/dist/animation/clip-map.d.ts +0 -12
- package/dist/animation/clip-map.d.ts.map +0 -1
- package/dist/animation/clip-map.js +0 -37
- package/dist/animation/gsap-registration.d.ts +0 -119
- package/dist/animation/gsap-registration.d.ts.map +0 -1
- package/dist/animation/gsap-registration.js +0 -92
- package/dist/animation/theatre-clock-binding.d.ts +0 -89
- package/dist/animation/theatre-clock-binding.d.ts.map +0 -1
- package/dist/animation/theatre-clock-binding.js +0 -29
- package/dist/animation/theatre-director.d.ts +0 -242
- package/dist/animation/theatre-director.d.ts.map +0 -1
- package/dist/animation/theatre-director.js +0 -235
- package/dist/animation/theatre-object-binding.d.ts +0 -305
- package/dist/animation/theatre-object-binding.d.ts.map +0 -1
- package/dist/animation/theatre-object-binding.js +0 -351
- package/dist/animation/xstate-animation-binding.d.ts +0 -56
- package/dist/animation/xstate-animation-binding.d.ts.map +0 -1
- package/dist/animation/xstate-animation-binding.js +0 -331
- package/dist/animation/xstate-animation-meta.d.ts +0 -266
- package/dist/animation/xstate-animation-meta.d.ts.map +0 -1
- package/dist/animation/xstate-animation-meta.js +0 -252
- package/dist/assets.d.ts +0 -26
- package/dist/assets.d.ts.map +0 -1
- package/dist/assets.js +0 -55
- package/dist/audio/index.d.ts +0 -4
- package/dist/audio/index.d.ts.map +0 -1
- package/dist/audio/index.js +0 -24
- package/dist/audio/tone-clock-binding.d.ts +0 -75
- package/dist/audio/tone-clock-binding.d.ts.map +0 -1
- package/dist/audio/tone-clock-binding.js +0 -29
- package/dist/audio/tone-context.d.ts +0 -114
- package/dist/audio/tone-context.d.ts.map +0 -1
- package/dist/audio/tone-context.js +0 -39
- package/dist/audio/tone-offline-render.d.ts +0 -90
- package/dist/audio/tone-offline-render.d.ts.map +0 -1
- package/dist/audio/tone-offline-render.js +0 -56
- package/dist/audio/wav-encode.d.ts +0 -38
- package/dist/audio/wav-encode.d.ts.map +0 -1
- package/dist/audio/wav-encode.js +0 -97
- package/dist/character/cloth-sim.d.ts +0 -179
- package/dist/character/cloth-sim.d.ts.map +0 -1
- package/dist/character/cloth-sim.js +0 -334
- package/dist/character/spring-chain.d.ts +0 -135
- package/dist/character/spring-chain.d.ts.map +0 -1
- package/dist/character/spring-chain.js +0 -165
- package/dist/core/game-loop.d.ts +0 -42
- package/dist/core/game-loop.d.ts.map +0 -1
- package/dist/core/game-loop.js +0 -167
- package/dist/core/seeded-random.d.ts +0 -69
- package/dist/core/seeded-random.d.ts.map +0 -1
- package/dist/core/seeded-random.js +0 -121
- package/dist/core/system-runner.d.ts +0 -120
- package/dist/core/system-runner.d.ts.map +0 -1
- package/dist/core/system-runner.js +0 -297
- package/dist/core/types.d.ts +0 -92
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -25
- package/dist/data/data-asset.d.ts +0 -101
- package/dist/data/data-asset.d.ts.map +0 -1
- package/dist/data/data-asset.js +0 -122
- package/dist/data/data-check-core.d.ts +0 -110
- package/dist/data/data-check-core.d.ts.map +0 -1
- package/dist/data/data-check-core.js +0 -178
- package/dist/data/data-ref.d.ts +0 -79
- package/dist/data/data-ref.d.ts.map +0 -1
- package/dist/data/data-ref.js +0 -130
- package/dist/data/vite-plugin-data.d.ts +0 -79
- package/dist/data/vite-plugin-data.d.ts.map +0 -1
- package/dist/data/vite-plugin-data.js +0 -217
- package/dist/dev/console-bridge.d.ts +0 -22
- package/dist/dev/console-bridge.d.ts.map +0 -1
- package/dist/dev/console-bridge.js +0 -71
- package/dist/dev/debug-draw.d.ts +0 -24
- package/dist/dev/debug-draw.d.ts.map +0 -1
- package/dist/dev/debug-draw.js +0 -73
- package/dist/dev/logger.d.ts +0 -30
- package/dist/dev/logger.d.ts.map +0 -1
- package/dist/dev/logger.js +0 -90
- package/dist/dev/performance-profiler.d.ts +0 -60
- package/dist/dev/performance-profiler.d.ts.map +0 -1
- package/dist/dev/performance-profiler.js +0 -181
- package/dist/dev/webgl-gpu-timer.d.ts +0 -8
- package/dist/dev/webgl-gpu-timer.d.ts.map +0 -1
- package/dist/dev/webgl-gpu-timer.js +0 -52
- package/dist/ecs/component-manager.d.ts +0 -205
- package/dist/ecs/component-manager.d.ts.map +0 -1
- package/dist/ecs/component-manager.js +0 -706
- package/dist/ecs/game-component.d.ts +0 -146
- package/dist/ecs/game-component.d.ts.map +0 -1
- package/dist/ecs/game-component.js +0 -141
- package/dist/ecs/hmr-swap-report.d.ts +0 -53
- package/dist/ecs/hmr-swap-report.d.ts.map +0 -1
- package/dist/ecs/hmr-swap-report.js +0 -55
- package/dist/humanoid/bake.operation.d.ts +0 -119
- package/dist/humanoid/bake.operation.d.ts.map +0 -1
- package/dist/humanoid/bake.operation.js +0 -228
- package/dist/humanoid/body.d.ts +0 -101
- package/dist/humanoid/body.d.ts.map +0 -1
- package/dist/humanoid/body.js +0 -530
- package/dist/humanoid/clips.d.ts +0 -62
- package/dist/humanoid/clips.d.ts.map +0 -1
- package/dist/humanoid/clips.js +0 -121
- package/dist/humanoid/compose.d.ts +0 -75
- package/dist/humanoid/compose.d.ts.map +0 -1
- package/dist/humanoid/compose.js +0 -176
- package/dist/humanoid/generate.d.ts +0 -92
- package/dist/humanoid/generate.d.ts.map +0 -1
- package/dist/humanoid/generate.js +0 -124
- package/dist/humanoid/index.d.ts +0 -24
- package/dist/humanoid/index.d.ts.map +0 -1
- package/dist/humanoid/index.js +0 -18
- package/dist/humanoid/schema.d.ts +0 -35
- package/dist/humanoid/schema.d.ts.map +0 -1
- package/dist/humanoid/schema.js +0 -87
- package/dist/humanoid/skeleton.d.ts +0 -103
- package/dist/humanoid/skeleton.d.ts.map +0 -1
- package/dist/humanoid/skeleton.js +0 -289
- package/dist/index.d.ts +0 -37
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -30
- package/dist/input/input-manager.d.ts +0 -945
- package/dist/input/input-manager.d.ts.map +0 -1
- package/dist/input/input-manager.js +0 -2207
- package/dist/input/input-types.d.ts +0 -174
- package/dist/input/input-types.d.ts.map +0 -1
- package/dist/input/input-types.js +0 -1
- package/dist/input/prompt-labels.d.ts +0 -21
- package/dist/input/prompt-labels.d.ts.map +0 -1
- package/dist/input/prompt-labels.js +0 -121
- package/dist/input/rebind-controller.d.ts +0 -59
- package/dist/input/rebind-controller.d.ts.map +0 -1
- package/dist/input/rebind-controller.js +0 -85
- package/dist/input/schema.d.ts +0 -16
- package/dist/input/schema.d.ts.map +0 -1
- package/dist/input/schema.js +0 -228
- package/dist/loader.d.ts +0 -43
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js +0 -59
- package/dist/manifest/index.d.ts +0 -5
- package/dist/manifest/index.d.ts.map +0 -1
- package/dist/manifest/index.js +0 -8
- package/dist/manifest/load-file.d.ts +0 -4
- package/dist/manifest/load-file.d.ts.map +0 -1
- package/dist/manifest/load-file.js +0 -14
- package/dist/manifest/load.d.ts +0 -144
- package/dist/manifest/load.d.ts.map +0 -1
- package/dist/manifest/load.js +0 -273
- package/dist/manifest/schema.d.ts +0 -230
- package/dist/manifest/schema.d.ts.map +0 -1
- package/dist/manifest/schema.js +0 -394
- package/dist/physics/collision-system.d.ts +0 -41
- package/dist/physics/collision-system.d.ts.map +0 -1
- package/dist/physics/collision-system.js +0 -64
- package/dist/physics/physics-registry.d.ts +0 -45
- package/dist/physics/physics-registry.d.ts.map +0 -1
- package/dist/physics/physics-registry.js +0 -65
- package/dist/physics/transform-writer.d.ts +0 -16
- package/dist/physics/transform-writer.d.ts.map +0 -1
- package/dist/physics/transform-writer.js +0 -38
- package/dist/physics/trigger-dispatch.d.ts +0 -24
- package/dist/physics/trigger-dispatch.d.ts.map +0 -1
- package/dist/physics/trigger-dispatch.js +0 -80
- package/dist/react/game-state.d.ts +0 -156
- package/dist/react/game-state.d.ts.map +0 -1
- package/dist/react/game-state.js +0 -222
- package/dist/react/root-adapter.d.ts +0 -11
- package/dist/react/root-adapter.d.ts.map +0 -1
- package/dist/react/root-adapter.js +0 -41
- package/dist/react/unmanaged-root-detector.d.ts +0 -12
- package/dist/react/unmanaged-root-detector.d.ts.map +0 -1
- package/dist/react/unmanaged-root-detector.js +0 -45
- package/dist/react/use-data.d.ts +0 -60
- package/dist/react/use-data.d.ts.map +0 -1
- package/dist/react/use-data.js +0 -101
- package/dist/react/use-selection.d.ts +0 -75
- package/dist/react/use-selection.d.ts.map +0 -1
- package/dist/react/use-selection.js +0 -90
- package/dist/render/auto-batcher.d.ts +0 -34
- package/dist/render/auto-batcher.d.ts.map +0 -1
- package/dist/render/auto-batcher.js +0 -140
- package/dist/render/render-batch-system.d.ts +0 -33
- package/dist/render/render-batch-system.d.ts.map +0 -1
- package/dist/render/render-batch-system.js +0 -230
- package/dist/render/render-features.d.ts +0 -47
- package/dist/render/render-features.d.ts.map +0 -1
- package/dist/render/render-features.js +0 -105
- package/dist/render/render-settings.d.ts +0 -36
- package/dist/render/render-settings.d.ts.map +0 -1
- package/dist/render/render-settings.js +0 -51
- package/dist/runtime/create-runtime.d.ts +0 -249
- package/dist/runtime/create-runtime.d.ts.map +0 -1
- package/dist/runtime/create-runtime.js +0 -641
- package/dist/runtime/debug-bridge.d.ts +0 -257
- package/dist/runtime/debug-bridge.d.ts.map +0 -1
- package/dist/runtime/debug-bridge.js +0 -224
- package/dist/runtime/debug-registry.d.ts +0 -288
- package/dist/runtime/debug-registry.d.ts.map +0 -1
- package/dist/runtime/debug-registry.js +0 -513
- package/dist/runtime/frame-selector-cache.d.ts +0 -41
- package/dist/runtime/frame-selector-cache.d.ts.map +0 -1
- package/dist/runtime/frame-selector-cache.js +0 -65
- package/dist/runtime/game.d.ts +0 -496
- package/dist/runtime/game.d.ts.map +0 -1
- package/dist/runtime/game.js +0 -741
- package/dist/runtime/gameplay-rng-trap.d.ts +0 -48
- package/dist/runtime/gameplay-rng-trap.d.ts.map +0 -1
- package/dist/runtime/gameplay-rng-trap.js +0 -96
- package/dist/runtime/input-router.d.ts +0 -78
- package/dist/runtime/input-router.d.ts.map +0 -1
- package/dist/runtime/input-router.js +0 -171
- package/dist/runtime/mount-game.d.ts +0 -129
- package/dist/runtime/mount-game.d.ts.map +0 -1
- package/dist/runtime/mount-game.js +0 -152
- package/dist/runtime/mount-manifest.d.ts +0 -173
- package/dist/runtime/mount-manifest.d.ts.map +0 -1
- package/dist/runtime/mount-manifest.js +0 -297
- package/dist/runtime/render-audio-control.d.ts +0 -110
- package/dist/runtime/render-audio-control.d.ts.map +0 -1
- package/dist/runtime/render-audio-control.js +0 -111
- package/dist/runtime/render-control.d.ts +0 -291
- package/dist/runtime/render-control.d.ts.map +0 -1
- package/dist/runtime/render-control.js +0 -264
- package/dist/runtime/render-seed.d.ts +0 -57
- package/dist/runtime/render-seed.d.ts.map +0 -1
- package/dist/runtime/render-seed.js +0 -74
- package/dist/runtime/state-bridge.d.ts +0 -61
- package/dist/runtime/state-bridge.d.ts.map +0 -1
- package/dist/runtime/state-bridge.js +0 -63
- package/dist/runtime/types.d.ts +0 -253
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- package/dist/scene/asset-loaders.d.ts +0 -79
- package/dist/scene/asset-loaders.d.ts.map +0 -1
- package/dist/scene/asset-loaders.js +0 -141
- package/dist/scene/asset-paths.d.ts +0 -11
- package/dist/scene/asset-paths.d.ts.map +0 -1
- package/dist/scene/asset-paths.js +0 -108
- package/dist/scene/asset-ref-check.d.ts +0 -88
- package/dist/scene/asset-ref-check.d.ts.map +0 -1
- package/dist/scene/asset-ref-check.js +0 -214
- package/dist/scene/asset-registry.d.ts +0 -38
- package/dist/scene/asset-registry.d.ts.map +0 -1
- package/dist/scene/asset-registry.js +0 -66
- package/dist/scene/collider-dimensions.d.ts +0 -63
- package/dist/scene/collider-dimensions.d.ts.map +0 -1
- package/dist/scene/collider-dimensions.js +0 -98
- package/dist/scene/component-registry.d.ts +0 -22
- package/dist/scene/component-registry.d.ts.map +0 -1
- package/dist/scene/component-registry.js +0 -37
- package/dist/scene/defaults.d.ts +0 -252
- package/dist/scene/defaults.d.ts.map +0 -1
- package/dist/scene/defaults.js +0 -151
- package/dist/scene/geometries/index.d.ts +0 -8
- package/dist/scene/geometries/index.d.ts.map +0 -1
- package/dist/scene/geometries/index.js +0 -7
- package/dist/scene/geometries/terrain.d.ts +0 -2
- package/dist/scene/geometries/terrain.d.ts.map +0 -1
- package/dist/scene/geometries/terrain.js +0 -37
- package/dist/scene/geometry-registry.d.ts +0 -24
- package/dist/scene/geometry-registry.d.ts.map +0 -1
- package/dist/scene/geometry-registry.js +0 -20
- package/dist/scene/instance-registry.d.ts +0 -36
- package/dist/scene/instance-registry.d.ts.map +0 -1
- package/dist/scene/instance-registry.js +0 -55
- package/dist/scene/instancers/grid.d.ts +0 -2
- package/dist/scene/instancers/grid.d.ts.map +0 -1
- package/dist/scene/instancers/grid.js +0 -36
- package/dist/scene/instancers/index.d.ts +0 -8
- package/dist/scene/instancers/index.d.ts.map +0 -1
- package/dist/scene/instancers/index.js +0 -7
- package/dist/scene/light-camera-factory.d.ts +0 -23
- package/dist/scene/light-camera-factory.d.ts.map +0 -1
- package/dist/scene/light-camera-factory.js +0 -68
- package/dist/scene/material-factory.d.ts +0 -17
- package/dist/scene/material-factory.d.ts.map +0 -1
- package/dist/scene/material-factory.js +0 -197
- package/dist/scene/material-registry.d.ts +0 -40
- package/dist/scene/material-registry.d.ts.map +0 -1
- package/dist/scene/material-registry.js +0 -44
- package/dist/scene/materials/index.d.ts +0 -8
- package/dist/scene/materials/index.d.ts.map +0 -1
- package/dist/scene/materials/index.js +0 -7
- package/dist/scene/materials/water.d.ts +0 -2
- package/dist/scene/materials/water.d.ts.map +0 -1
- package/dist/scene/materials/water.js +0 -54
- package/dist/scene/parse.d.ts +0 -23
- package/dist/scene/parse.d.ts.map +0 -1
- package/dist/scene/parse.js +0 -179
- package/dist/scene/particles-factory.d.ts +0 -33
- package/dist/scene/particles-factory.d.ts.map +0 -1
- package/dist/scene/particles-factory.js +0 -282
- package/dist/scene/scene-apply.d.ts +0 -109
- package/dist/scene/scene-apply.d.ts.map +0 -1
- package/dist/scene/scene-apply.js +0 -370
- package/dist/scene/scene-diff-schema.d.ts +0 -285
- package/dist/scene/scene-diff-schema.d.ts.map +0 -1
- package/dist/scene/scene-diff-schema.js +0 -92
- package/dist/scene/scene-diff-types.d.ts +0 -40
- package/dist/scene/scene-diff-types.d.ts.map +0 -1
- package/dist/scene/scene-diff-types.js +0 -14
- package/dist/scene/scene-loader.d.ts +0 -167
- package/dist/scene/scene-loader.d.ts.map +0 -1
- package/dist/scene/scene-loader.js +0 -1211
- package/dist/scene/scene-query.d.ts +0 -11
- package/dist/scene/scene-query.d.ts.map +0 -1
- package/dist/scene/scene-query.js +0 -63
- package/dist/scene/scene-types.d.ts +0 -10
- package/dist/scene/scene-types.d.ts.map +0 -1
- package/dist/scene/scene-types.js +0 -8
- package/dist/scene/scene-version.d.ts +0 -26
- package/dist/scene/scene-version.d.ts.map +0 -1
- package/dist/scene/scene-version.js +0 -34
- package/dist/scene/schema/animation.d.ts +0 -8
- package/dist/scene/schema/animation.d.ts.map +0 -1
- package/dist/scene/schema/animation.js +0 -42
- package/dist/scene/schema/audio.d.ts +0 -13
- package/dist/scene/schema/audio.d.ts.map +0 -1
- package/dist/scene/schema/audio.js +0 -22
- package/dist/scene/schema/camera.d.ts +0 -28
- package/dist/scene/schema/camera.d.ts.map +0 -1
- package/dist/scene/schema/camera.js +0 -17
- package/dist/scene/schema/collider.d.ts +0 -40
- package/dist/scene/schema/collider.d.ts.map +0 -1
- package/dist/scene/schema/collider.js +0 -55
- package/dist/scene/schema/entity-ref.d.ts +0 -31
- package/dist/scene/schema/entity-ref.d.ts.map +0 -1
- package/dist/scene/schema/entity-ref.js +0 -61
- package/dist/scene/schema/entity.d.ts +0 -1036
- package/dist/scene/schema/entity.d.ts.map +0 -1
- package/dist/scene/schema/entity.js +0 -153
- package/dist/scene/schema/environment.d.ts +0 -425
- package/dist/scene/schema/environment.d.ts.map +0 -1
- package/dist/scene/schema/environment.js +0 -357
- package/dist/scene/schema/index.d.ts +0 -37
- package/dist/scene/schema/index.d.ts.map +0 -1
- package/dist/scene/schema/index.js +0 -19
- package/dist/scene/schema/instances.d.ts +0 -14
- package/dist/scene/schema/instances.d.ts.map +0 -1
- package/dist/scene/schema/instances.js +0 -28
- package/dist/scene/schema/joint.d.ts +0 -23
- package/dist/scene/schema/joint.d.ts.map +0 -1
- package/dist/scene/schema/joint.js +0 -23
- package/dist/scene/schema/light.d.ts +0 -38
- package/dist/scene/schema/light.d.ts.map +0 -1
- package/dist/scene/schema/light.js +0 -26
- package/dist/scene/schema/material.d.ts +0 -199
- package/dist/scene/schema/material.d.ts.map +0 -1
- package/dist/scene/schema/material.js +0 -103
- package/dist/scene/schema/mesh.d.ts +0 -48
- package/dist/scene/schema/mesh.d.ts.map +0 -1
- package/dist/scene/schema/mesh.js +0 -82
- package/dist/scene/schema/particles.d.ts +0 -2178
- package/dist/scene/schema/particles.d.ts.map +0 -1
- package/dist/scene/schema/particles.js +0 -321
- package/dist/scene/schema/physics.d.ts +0 -56
- package/dist/scene/schema/physics.d.ts.map +0 -1
- package/dist/scene/schema/physics.js +0 -37
- package/dist/scene/schema/scene-file.d.ts +0 -303
- package/dist/scene/schema/scene-file.d.ts.map +0 -1
- package/dist/scene/schema/scene-file.js +0 -256
- package/dist/scene/schema/shadow.d.ts +0 -28
- package/dist/scene/schema/shadow.d.ts.map +0 -1
- package/dist/scene/schema/shadow.js +0 -20
- package/dist/scene/schema/spline.d.ts +0 -25
- package/dist/scene/schema/spline.d.ts.map +0 -1
- package/dist/scene/schema/spline.js +0 -15
- package/dist/scene/schema/tuples.d.ts +0 -12
- package/dist/scene/schema/tuples.d.ts.map +0 -1
- package/dist/scene/schema/tuples.js +0 -14
- package/dist/scene/user-data.d.ts +0 -185
- package/dist/scene/user-data.d.ts.map +0 -1
- package/dist/scene/user-data.js +0 -144
- package/dist/setup/setup-audio.d.ts +0 -20
- package/dist/setup/setup-audio.d.ts.map +0 -1
- package/dist/setup/setup-audio.js +0 -41
- package/dist/setup/setup-particles.d.ts +0 -17
- package/dist/setup/setup-particles.d.ts.map +0 -1
- package/dist/setup/setup-particles.js +0 -16
- package/dist/setup/setup-physics.d.ts +0 -25
- package/dist/setup/setup-physics.d.ts.map +0 -1
- package/dist/setup/setup-physics.js +0 -51
- package/dist/setup/setup-renderer.d.ts +0 -69
- package/dist/setup/setup-renderer.d.ts.map +0 -1
- package/dist/setup/setup-renderer.js +0 -363
- package/dist/tools/define-tool.d.ts +0 -130
- package/dist/tools/define-tool.d.ts.map +0 -1
- package/dist/tools/define-tool.js +0 -96
- package/dist/world2d/authoring-2d.d.ts +0 -80
- package/dist/world2d/authoring-2d.d.ts.map +0 -1
- package/dist/world2d/authoring-2d.js +0 -195
- package/dist/world2d/capture-to-scene2d.d.ts +0 -5
- package/dist/world2d/capture-to-scene2d.d.ts.map +0 -1
- package/dist/world2d/capture-to-scene2d.js +0 -49
- package/dist/world2d/collision-2d.d.ts +0 -52
- package/dist/world2d/collision-2d.d.ts.map +0 -1
- package/dist/world2d/collision-2d.js +0 -70
- package/dist/world2d/components-2d.d.ts +0 -47
- package/dist/world2d/components-2d.d.ts.map +0 -1
- package/dist/world2d/components-2d.js +0 -77
- package/dist/world2d/index.d.ts +0 -34
- package/dist/world2d/index.d.ts.map +0 -1
- package/dist/world2d/index.js +0 -32
- package/dist/world2d/ingest-iframe-2d.d.ts +0 -82
- package/dist/world2d/ingest-iframe-2d.d.ts.map +0 -1
- package/dist/world2d/ingest-iframe-2d.js +0 -166
- package/dist/world2d/ingest2d.d.ts +0 -97
- package/dist/world2d/ingest2d.d.ts.map +0 -1
- package/dist/world2d/ingest2d.js +0 -40
- package/dist/world2d/physics2d-registry.d.ts +0 -24
- package/dist/world2d/physics2d-registry.d.ts.map +0 -1
- package/dist/world2d/physics2d-registry.js +0 -38
- package/dist/world2d/pixi-game-adapter.d.ts +0 -107
- package/dist/world2d/pixi-game-adapter.d.ts.map +0 -1
- package/dist/world2d/pixi-game-adapter.js +0 -210
- package/dist/world2d/pixi-surface.d.ts +0 -39
- package/dist/world2d/pixi-surface.d.ts.map +0 -1
- package/dist/world2d/pixi-surface.js +0 -47
- package/dist/world2d/scene-capture-2d.d.ts +0 -43
- package/dist/world2d/scene-capture-2d.d.ts.map +0 -1
- package/dist/world2d/scene-capture-2d.js +0 -92
- package/dist/world2d/scene2d-loader.d.ts +0 -27
- package/dist/world2d/scene2d-loader.d.ts.map +0 -1
- package/dist/world2d/scene2d-loader.js +0 -275
- package/dist/world2d/schema/entity2d.d.ts +0 -102
- package/dist/world2d/schema/entity2d.d.ts.map +0 -1
- package/dist/world2d/schema/entity2d.js +0 -119
- package/dist/world2d/schema/physics2d.d.ts +0 -46
- package/dist/world2d/schema/physics2d.d.ts.map +0 -1
- package/dist/world2d/schema/physics2d.js +0 -45
- package/dist/world2d/schema/sprite.d.ts +0 -42
- package/dist/world2d/schema/sprite.d.ts.map +0 -1
- package/dist/world2d/schema/sprite.js +0 -63
- package/dist/world2d/schema/tilemap.d.ts +0 -15
- package/dist/world2d/schema/tilemap.d.ts.map +0 -1
- package/dist/world2d/schema/tilemap.js +0 -19
- package/dist/world2d/schema/tuples2d.d.ts +0 -20
- package/dist/world2d/schema/tuples2d.d.ts.map +0 -1
- package/dist/world2d/schema/tuples2d.js +0 -19
- package/dist/world2d/system-adapters-2d.d.ts +0 -16
- package/dist/world2d/system-adapters-2d.d.ts.map +0 -1
- package/dist/world2d/system-adapters-2d.js +0 -37
- package/dist/world2d/transform-writer-2d.d.ts +0 -16
- package/dist/world2d/transform-writer-2d.d.ts.map +0 -1
- package/dist/world2d/transform-writer-2d.js +0 -22
- package/dist/world2d/types.d.ts +0 -59
- package/dist/world2d/types.d.ts.map +0 -1
- package/dist/world2d/types.js +0 -1
package/dist/runtime/game.js
DELETED
|
@@ -1,741 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Game root — T7.1 slice 1 ("introduce Game internally") + slice 2 ("world
|
|
3
|
-
* registry + ordered frame algorithm"), extended in T7.3 slice 1 with real
|
|
4
|
-
* `'pixijs'`-kind `WorldInstance` support (`stage`/`physics2d`/`collisions2d`,
|
|
5
|
-
* a real `pixiRoot()`) so `world2d/pixi-game-adapter.ts` can register its
|
|
6
|
-
* world onto a real `Game` instead of driving its own loop, and in T7.3
|
|
7
|
-
* slice 2 with a construction-time stage check (symmetric with the
|
|
8
|
-
* threejs/scene check) and the two-world (threejs + pixijs) proof
|
|
9
|
-
* (`test/game-three-plus-pixi.test.ts`). T6.2 slice 1 adds real `'react'`-kind
|
|
10
|
-
* `WorldInstance` support (`container`, a real `reactRoot()`) — the DOM-root
|
|
11
|
-
* world surface `runtime/create-runtime.ts`'s roots path now mounts (see
|
|
12
|
-
* `docs/REACT-WORLD-DESIGN.md` §1.B); react roots host no `ComponentManager`
|
|
13
|
-
* (D8 — components attaching to a react-kind manager already throw,
|
|
14
|
-
* `ecs/component-manager.ts`), so `physics`/`collisions`/`camera`/`frame`
|
|
15
|
-
* stay `undefined` for them exactly like an opaque/foreign mount.
|
|
16
|
-
*
|
|
17
|
-
* See `docs/GAME-ROOT-DESIGN.md` (D6), particularly §4 (the frame algorithm)
|
|
18
|
-
* and §8 stage 2, for the full target shape. This file holds the Game/
|
|
19
|
-
* WorldInstance shell, the NEW game-scoped `SystemRunner`, and the
|
|
20
|
-
* host-facing `runFrame` frame executor. The public surface is NOT frozen
|
|
21
|
-
* yet — do not add these exports to a barrel/index; only the in-scope
|
|
22
|
-
* runtime/adapter files import this module directly. The `gpu` per-world
|
|
23
|
-
* cache is the one remaining slice-1 omission.
|
|
24
|
-
*/
|
|
25
|
-
import { formatAudioGateMessage, formatLoopGateMessage } from '../adapter/loop-gate-report';
|
|
26
|
-
import { createSeededRandom, DEFAULT_SEEDED_RANDOM_SEED, registerSeededRandom, } from '../core/seeded-random';
|
|
27
|
-
import { createSystemRunner } from '../core/system-runner';
|
|
28
|
-
import { PHASE_ORDER, SystemPhase } from '../core/types';
|
|
29
|
-
import { createPerformanceProfiler } from '../dev/performance-profiler';
|
|
30
|
-
import { createDebugRegistry, DebugError, registerDebugRegistry, } from './debug-registry';
|
|
31
|
-
import { createGameplayRngTrap, registerGameplayRngTrapControl } from './gameplay-rng-trap';
|
|
32
|
-
import { createStateBridge } from './state-bridge';
|
|
33
|
-
/**
|
|
34
|
-
* Build a `WorldInstance` whose kind-narrowed accessors throw descriptively
|
|
35
|
-
* on kind mismatch (GAME-ROOT-DESIGN §3). This is the one place that builds
|
|
36
|
-
* `threeRoot`/`pixiRoot`, so every world (however it's constructed, in this
|
|
37
|
-
* slice or later ones) gets identical throw behavior.
|
|
38
|
-
*/
|
|
39
|
-
export function createWorldInstance(init) {
|
|
40
|
-
const { id, kind, pausable = true, adapter, mounted, scene, stage, container, physics, collisions, physics2d, collisions2d, camera, frame, } = init;
|
|
41
|
-
// A threejs world with no scene has nothing for `threeRoot()` to return —
|
|
42
|
-
// fail loudly HERE, at construction, rather than letting `threeRoot()`
|
|
43
|
-
// throw its generic "not a threejs world" message later for a world whose
|
|
44
|
-
// kind IS threejs (checklist item 5; a misleading error for this case).
|
|
45
|
-
if (kind === 'threejs' && !scene) {
|
|
46
|
-
throw new Error(`WorldInstance "${id}" (kind: threejs): a threejs world requires a scene — ` +
|
|
47
|
-
'pass `scene` in WorldInstanceInit.');
|
|
48
|
-
}
|
|
49
|
-
// Symmetric check for pixijs (T7.3 slice 2) — a pixijs world with no stage
|
|
50
|
-
// has nothing for `pixiRoot()` to return; fail loudly here, at
|
|
51
|
-
// construction, same as the threejs/scene check above.
|
|
52
|
-
if (kind === 'pixijs' && !stage) {
|
|
53
|
-
throw new Error(`WorldInstance "${id}" (kind: pixijs): a pixijs world requires a stage — ` +
|
|
54
|
-
'pass `stage` in WorldInstanceInit.');
|
|
55
|
-
}
|
|
56
|
-
// Symmetric check for react (T6.2 slice 1, docs/REACT-WORLD-DESIGN.md
|
|
57
|
-
// §1.B) — a react world with no container has nothing for `reactRoot()`
|
|
58
|
-
// to return; fail loudly here, at construction, same as the two checks
|
|
59
|
-
// above.
|
|
60
|
-
if (kind === 'react' && !container) {
|
|
61
|
-
throw new Error(`WorldInstance "${id}" (kind: react): a react world requires a container — ` +
|
|
62
|
-
'pass `container` in WorldInstanceInit.');
|
|
63
|
-
}
|
|
64
|
-
// Disposed-world guard (GAME-ROOT-DESIGN §8 stage 3 / T7.1 slice 3). There
|
|
65
|
-
// is no `unregisterWorld` (decision 4 — roots are manifest-declared; a
|
|
66
|
-
// whole Game is disposed, not one world out of its registry), so a caller
|
|
67
|
-
// that disposes ONE world's `mounted` directly (e.g. ending a sub-session)
|
|
68
|
-
// leaves that `WorldInstance` sitting in `Game.roots` — and `runFrame`
|
|
69
|
-
// would otherwise keep invoking its (now-torn-down) frame hooks every
|
|
70
|
-
// subsequent frame. `MountedGame` has no public "am I disposed" flag to
|
|
71
|
-
// read, so this wraps `mounted.dispose` in place (mutating the SAME mount
|
|
72
|
-
// object every holder of `mounted` shares — calling `mounted.dispose()`
|
|
73
|
-
// directly, exactly like calling `world.mounted.dispose()`, trips this)
|
|
74
|
-
// to flip a private flag, and wraps the frame hooks so they silently no-op
|
|
75
|
-
// once that flag is set. `runFrame` itself needs no knowledge of disposal
|
|
76
|
-
// — a disposed world is skipped by construction from the next call
|
|
77
|
-
// onward. (The opaque-world `mounted.update` fallback path is unaffected
|
|
78
|
-
// by this guard — no opaque-mount two-world scenario exists yet to need it.)
|
|
79
|
-
let disposed = false;
|
|
80
|
-
const originalDispose = mounted.dispose.bind(mounted);
|
|
81
|
-
mounted.dispose = () => {
|
|
82
|
-
disposed = true;
|
|
83
|
-
originalDispose();
|
|
84
|
-
};
|
|
85
|
-
const guardedFrame = frame
|
|
86
|
-
? {
|
|
87
|
-
runPhase(phase, dt) {
|
|
88
|
-
if (disposed)
|
|
89
|
-
return;
|
|
90
|
-
frame.runPhase(phase, dt);
|
|
91
|
-
},
|
|
92
|
-
endFrame() {
|
|
93
|
-
if (disposed)
|
|
94
|
-
return;
|
|
95
|
-
frame.endFrame?.();
|
|
96
|
-
},
|
|
97
|
-
}
|
|
98
|
-
: undefined;
|
|
99
|
-
return {
|
|
100
|
-
id,
|
|
101
|
-
kind,
|
|
102
|
-
pausable,
|
|
103
|
-
adapter,
|
|
104
|
-
mounted,
|
|
105
|
-
physics,
|
|
106
|
-
collisions,
|
|
107
|
-
physics2d,
|
|
108
|
-
collisions2d,
|
|
109
|
-
camera,
|
|
110
|
-
frame: guardedFrame,
|
|
111
|
-
threeRoot() {
|
|
112
|
-
if (kind !== 'threejs' || !scene) {
|
|
113
|
-
throw new Error(`WorldInstance "${id}" (kind: ${kind}): threeRoot() requested but this world is not ` +
|
|
114
|
-
'a threejs world');
|
|
115
|
-
}
|
|
116
|
-
return scene;
|
|
117
|
-
},
|
|
118
|
-
pixiRoot() {
|
|
119
|
-
// The `!stage` branch a pixijs world could hit here is now unreachable
|
|
120
|
-
// (T7.3 slice 2): construction above throws for `kind === 'pixijs'`
|
|
121
|
-
// with no `stage`, symmetric with `threeRoot()`'s `scene` guard.
|
|
122
|
-
if (kind !== 'pixijs' || !stage) {
|
|
123
|
-
throw new Error(`WorldInstance "${id}" (kind: ${kind}): pixiRoot() requested but this world is not ` +
|
|
124
|
-
'a pixijs world');
|
|
125
|
-
}
|
|
126
|
-
return stage;
|
|
127
|
-
},
|
|
128
|
-
reactRoot() {
|
|
129
|
-
// The `!container` branch a react world could hit here is now
|
|
130
|
-
// unreachable (T6.2 slice 1): construction above throws for
|
|
131
|
-
// `kind === 'react'` with no `container`, symmetric with
|
|
132
|
-
// `threeRoot()`/`pixiRoot()`'s guards.
|
|
133
|
-
if (kind !== 'react' || !container) {
|
|
134
|
-
throw new Error(`WorldInstance "${id}" (kind: ${kind}): reactRoot() requested but this world is not ` +
|
|
135
|
-
'a react world');
|
|
136
|
-
}
|
|
137
|
-
return container;
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Type guard for whether a `MountedGame` is a first-party
|
|
143
|
-
* `VgaiSceneGameAdapter` mount (has a live `GameContext` at `.ctx`). Used to
|
|
144
|
-
* decide whether a world's `physics`/`collisions`/`camera` (and, on `Game`,
|
|
145
|
-
* `components`/`input`/`audio`) can be populated from it — an external
|
|
146
|
-
* adapter's mount has none of these first-party handles.
|
|
147
|
-
*
|
|
148
|
-
* Checks the `firstParty: true` brand (checklist item 1), NOT `'ctx' in
|
|
149
|
-
* mounted` — world2d's `MountedGame2D` (`world2d/pixi-game-adapter.ts`) also
|
|
150
|
-
* has a `ctx` key (a `World2DContext`, unrelated to `GameContext`), so a
|
|
151
|
-
* structural `'ctx' in mounted` check would misfire once T7.3 registers 2D
|
|
152
|
-
* roots onto the same `Game`. The probe is a plain property read, not an
|
|
153
|
-
* `instanceof`/value import of `vgai-scene-game-adapter.ts` — the
|
|
154
|
-
* `VgaiMountedGame` import above stays type-only.
|
|
155
|
-
*/
|
|
156
|
-
export function isFirstPartyMounted(mounted) {
|
|
157
|
-
return mounted.firstParty === true;
|
|
158
|
-
}
|
|
159
|
-
function describeMismatch(handle, world) {
|
|
160
|
-
return (`Game.${handle}: default world "${world.id}" (kind: ${world.kind}) is not a first-party ` +
|
|
161
|
-
"mount — hoisting these to true Game ownership is a later slice's work; available only " +
|
|
162
|
-
'via a first-party default world today');
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Construct the (host-internal) Game shell. Callers: `createGameRuntime`
|
|
166
|
-
* builds this BEFORE mounting its one adapter, then registers the default
|
|
167
|
-
* threejs world once mount resolves (see `registerThreeWorld` in
|
|
168
|
-
* `create-runtime.ts`).
|
|
169
|
-
*/
|
|
170
|
-
export function createGame(opts) {
|
|
171
|
-
const roots = [];
|
|
172
|
-
const profiler = createPerformanceProfiler();
|
|
173
|
-
const systems = createSystemRunner(profiler.systemObserver, 'game');
|
|
174
|
-
const stateBridge = createStateBridge();
|
|
175
|
-
// D15 (T-D15.1) — the game-scoped seeded-random surface every world's
|
|
176
|
-
// `ctx.random` aliases (see `vgai-scene-game-adapter.ts`'s `ctx.random =
|
|
177
|
-
// ...`, wired the same way `ctx.debug` is just below). Constructed
|
|
178
|
-
// unconditionally (cheap — a handful of closures) regardless of whether
|
|
179
|
-
// this project ever declares `determinism.seededRandom`; only the BOOT
|
|
180
|
-
// SEED and the enforcement (the burn-down scan, the trap right below) are
|
|
181
|
-
// conditional on that declaration.
|
|
182
|
-
const seededRandom = createSeededRandom(opts.seed ?? DEFAULT_SEEDED_RANDOM_SEED);
|
|
183
|
-
// D15 (T-D15.3) — the dev-mode `Math.random` phase trap. Constructed
|
|
184
|
-
// unconditionally too (disabled by default: `rngTrapEnabled` starts
|
|
185
|
-
// `false`, so `runFrameImpl`'s enable/disable calls below are no-ops) —
|
|
186
|
-
// the manifest-aware boot path flips `rngTrapControl.setEnabled(true)`
|
|
187
|
-
// once it resolves `manifest.determinism?.seededRandom` (mirrors
|
|
188
|
-
// `debugRegistry.setRoomDeclared` being flipped post-hoc from the same
|
|
189
|
-
// boot path for the very same "only the caller who read the manifest
|
|
190
|
-
// knows" reason).
|
|
191
|
-
const rngTrap = createGameplayRngTrap();
|
|
192
|
-
let rngTrapEnabled = false;
|
|
193
|
-
const rngTrapControl = {
|
|
194
|
-
setEnabled(enabled) {
|
|
195
|
-
rngTrapEnabled = enabled;
|
|
196
|
-
},
|
|
197
|
-
get enabled() {
|
|
198
|
-
return rngTrapEnabled;
|
|
199
|
-
},
|
|
200
|
-
};
|
|
201
|
-
// T1.2 (docs/SYNTHETIC-PLAYER-SPEC.md §3.1/§3.3): `tick` counts completed
|
|
202
|
-
// fixed substeps, `simT` accumulates their `dt` — both game-scoped, bumped
|
|
203
|
-
// ONLY where `stateBridge.bump()` is (guarded by `advanced`, `runFrame`'s
|
|
204
|
-
// tail below), so a paused/frozen frame never advances either. The debug
|
|
205
|
-
// registry reads them via suppliers (not by capturing the numbers now)
|
|
206
|
-
// so its built-in `time` provider always sees the CURRENT values.
|
|
207
|
-
let tick = 0;
|
|
208
|
-
let simT = 0;
|
|
209
|
-
const debugRegistry = createDebugRegistry({
|
|
210
|
-
getTick: () => tick,
|
|
211
|
-
getSimT: () => simT,
|
|
212
|
-
// D15/T-D15.5 — "the manifest's first/default world" for the per-world
|
|
213
|
-
// input-target resolution (`resolveInputWorldId`, `debug-registry.ts`):
|
|
214
|
-
// the SAME "first threejs world, else first world" rule
|
|
215
|
-
// `requireDefaultWorld` (declared just below — safe: this closure is
|
|
216
|
-
// only ever CALLED later, once at least one world has mounted) already
|
|
217
|
-
// defines for `Game.defaultWorld`/`Game.input` — so the debug bridge and
|
|
218
|
-
// the editor relay resolve the exact same world `Game.input` would.
|
|
219
|
-
getDefaultWorldId: () => (roots.length > 0 ? requireDefaultWorld().id : null),
|
|
220
|
-
// Issue #175 — the built-in `time` provider's `loopLiveness` field reads
|
|
221
|
-
// the REAL loop, not any UI-level play-state store: `opts.loop` is the
|
|
222
|
-
// SAME `GameLoop` this `Game`'s own `.loop` field exposes, so this
|
|
223
|
-
// registry can never disagree with `game.loop.liveness` about whether
|
|
224
|
-
// the loop is actually ticking.
|
|
225
|
-
getLoopLiveness: () => opts.loop.liveness,
|
|
226
|
-
});
|
|
227
|
-
function requireDefaultWorld() {
|
|
228
|
-
if (roots.length === 0) {
|
|
229
|
-
throw new Error('Game.defaultWorld: no roots registered yet');
|
|
230
|
-
}
|
|
231
|
-
return roots.find((w) => w.kind === 'threejs') ?? roots[0];
|
|
232
|
-
}
|
|
233
|
-
function requireFirstPartyCtx(handle) {
|
|
234
|
-
const world = requireDefaultWorld();
|
|
235
|
-
if (!isFirstPartyMounted(world.mounted)) {
|
|
236
|
-
throw new Error(describeMismatch(handle, world));
|
|
237
|
-
}
|
|
238
|
-
return world.mounted.ctx;
|
|
239
|
-
}
|
|
240
|
-
// --- Game.systemAdapters aggregation (§7.1-3, probe1) --------------------
|
|
241
|
-
// Warn-once-per-colliding-key state, scoped to this Game instance (a fresh
|
|
242
|
-
// Game gets a fresh warn history) — deliberately NOT reset by anything
|
|
243
|
-
// short of a new `createGame` call, matching `reportedGateShortfalls`
|
|
244
|
-
// above's "once per game instance" idiom.
|
|
245
|
-
const warnedSystemAdapterKeys = new Set();
|
|
246
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one cohesive merge-with-collision-report walk (per-world × per-key); splitting the collision-warn branch out would obscure that it's part of the same pass, not reduce real complexity
|
|
247
|
-
function computeSystemAdapters() {
|
|
248
|
-
// Debug is game-scoped: React hooks, probes, and the built-in time
|
|
249
|
-
// provider all register with the one registry created above, regardless
|
|
250
|
-
// of whether any mounted root happens to expose a `systems` object. A
|
|
251
|
-
// root may still publish this SAME adapter (the first-party Three/Pixi
|
|
252
|
-
// mounts do); the reference-equality branch below treats that as the
|
|
253
|
-
// intentional shared registration it is.
|
|
254
|
-
const result = { debug: debugRegistry.adapter };
|
|
255
|
-
const ownerWorldId = new Map([['debug', '(game)']]);
|
|
256
|
-
for (const world of roots) {
|
|
257
|
-
const adapters = world.mounted.systems;
|
|
258
|
-
if (!adapters)
|
|
259
|
-
continue;
|
|
260
|
-
for (const key of Object.keys(adapters)) {
|
|
261
|
-
if (adapters[key] === undefined)
|
|
262
|
-
continue;
|
|
263
|
-
const existingOwner = ownerWorldId.get(key);
|
|
264
|
-
if (existingOwner !== undefined) {
|
|
265
|
-
// Reference-equality short-circuit: two roots sharing the ONE
|
|
266
|
-
// game-scoped debug registry's adapter (T1.1) both expose the SAME
|
|
267
|
-
// object under `systems.debug` — that is by design, not a
|
|
268
|
-
// collision, so it must never warn.
|
|
269
|
-
if (result[key] === adapters[key])
|
|
270
|
-
continue;
|
|
271
|
-
if (!warnedSystemAdapterKeys.has(key)) {
|
|
272
|
-
warnedSystemAdapterKeys.add(key);
|
|
273
|
-
// biome-ignore lint/suspicious/noConsole: structured, greppable — mirrors this file's own reportGateShortfallOnce's deliberate direct console.warn just above
|
|
274
|
-
console.warn(`[game] systemAdapters: "${key}" is registered by both world "${existingOwner}" and ` +
|
|
275
|
-
`world "${world.id}" — the FIRST registration ("${existingOwner}") wins; the later ` +
|
|
276
|
-
'one is shadowed (docs/GAME-ROOT-DESIGN.md §3: game-scoped system adapters).');
|
|
277
|
-
}
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
// biome-ignore lint/suspicious/noExplicitAny: SystemAdapters is a plain optional-field record; the per-key copy is correct by construction (same key on both sides), just not expressible without a cast
|
|
281
|
-
result[key] = adapters[key];
|
|
282
|
-
ownerWorldId.set(key, world.id);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return result;
|
|
286
|
-
}
|
|
287
|
-
// --- D10/T7.6 play-state (Game.play) -------------------------------------
|
|
288
|
-
let paused = false;
|
|
289
|
-
// Dedupe loop-gate/audio-gate shortfall warnings to ONCE per (world, kind)
|
|
290
|
-
// — `pause()` fans out over every world every call; without this a
|
|
291
|
-
// multi-second play session would re-log the same "can't gate" shortfall
|
|
292
|
-
// every time the user hits Pause.
|
|
293
|
-
const reportedGateShortfalls = new Set();
|
|
294
|
-
function reportGateShortfallOnce(kind, worldId, reason) {
|
|
295
|
-
const key = `${kind}:${worldId}`;
|
|
296
|
-
if (reportedGateShortfalls.has(key))
|
|
297
|
-
return;
|
|
298
|
-
reportedGateShortfalls.add(key);
|
|
299
|
-
const message = kind === 'loop'
|
|
300
|
-
? formatLoopGateMessage({ worldId, reason })
|
|
301
|
-
: formatAudioGateMessage({ worldId, reason });
|
|
302
|
-
// Other native `console.warn`/`console.error` call sites in this file are
|
|
303
|
-
// unsuppressed and already counted in the lint baseline (see `runFrame`'s
|
|
304
|
-
// impl below); this one is a NEW site, so it's suppressed to keep this
|
|
305
|
-
// task's diff at zero NEW warnings (same reasoning as `overlay-report.ts`'s
|
|
306
|
-
// identical suppression).
|
|
307
|
-
// biome-ignore lint/suspicious/noConsole: see comment above
|
|
308
|
-
console.warn(message);
|
|
309
|
-
}
|
|
310
|
-
/** Fan out a loop-gate (self-driven roots) + audio-gate call over every
|
|
311
|
-
* `pausable` world, reporting honestly (once) wherever the capability is
|
|
312
|
-
* absent — shared by `pause()`/`resume()` below (same fan-out, opposite
|
|
313
|
-
* boolean). */
|
|
314
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: fans out TWO independent capability gates (loop, audio) with the same "call it, else report once" shape per world — splitting the two gates into separate loops would duplicate the fan-out, not reduce real complexity
|
|
315
|
-
function setWorldGates(next) {
|
|
316
|
-
for (const world of roots) {
|
|
317
|
-
if (!world.pausable)
|
|
318
|
-
continue; // pausable:false roots are untouched by design
|
|
319
|
-
if (world.mounted.drivesOwnLoop) {
|
|
320
|
-
if (world.mounted.setPaused) {
|
|
321
|
-
world.mounted.setPaused(next);
|
|
322
|
-
}
|
|
323
|
-
else if (next) {
|
|
324
|
-
reportGateShortfallOnce('loop', world.id, 'self-driven world, adapter declares no setPaused capability');
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
const audio = world.mounted.systems?.audio;
|
|
328
|
-
if (audio) {
|
|
329
|
-
audio.setMuted(next);
|
|
330
|
-
}
|
|
331
|
-
else if (next) {
|
|
332
|
-
reportGateShortfallOnce('audio', world.id, 'no SystemAdapters.audio on this world');
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the frame algorithm (GAME-ROOT-DESIGN §4, now with D10's per-world pause gate + the onlyFrozen step()-only mode) is one cohesive nested loop over phases/roots — splitting it would obscure the ordering contract documented on GameInternal.runFrame
|
|
337
|
-
function runFrameImpl(dt, frameOpts) {
|
|
338
|
-
// D15 (T-D15.3) — brackets the ENTIRE frame body (every phase, every
|
|
339
|
-
// world, both the `onlyFrozen` and normal branches below converge on the
|
|
340
|
-
// single `profiler.endFrame()` at the tail) with zero reordering of the
|
|
341
|
-
// phase algorithm itself — a no-op pair of calls while
|
|
342
|
-
// `rngTrapEnabled` is false (the common case: most projects never
|
|
343
|
-
// declare `determinism.seededRandom`).
|
|
344
|
-
if (rngTrapEnabled)
|
|
345
|
-
rngTrap.enable();
|
|
346
|
-
profiler.beginFrame();
|
|
347
|
-
const ignorePause = frameOpts?.ignorePause ?? false;
|
|
348
|
-
// D15/T-D15.4: internal-only render-phase skip (not part of the public
|
|
349
|
-
// `GameInternal.runFrame` signature — no external caller sets it, mirroring
|
|
350
|
-
// `onlyFrozen` below) — `runTicks`'s `render: 'none'|'last'` fast-forward
|
|
351
|
-
// mode sets this on every tick it doesn't want to paint. See `runTicks`'s
|
|
352
|
-
// doc comment on `GameInternal` for the full contract.
|
|
353
|
-
const skipRenderPhases = frameOpts?.skipRenderPhases ?? false;
|
|
354
|
-
// `onlyFrozen` is internal-only (not part of the public `GameInternal.runFrame`
|
|
355
|
-
// signature — no external caller sets it) — `Game.play.step()` below is the
|
|
356
|
-
// one and only caller. It ticks EXACTLY the currently-frozen set (host-driven,
|
|
357
|
-
// `pausable`, and `paused`) through every phase + `endFrame`, with the REAL
|
|
358
|
-
// `dt` (not the render-phase's forced `0`), and touches no other world at all
|
|
359
|
-
// — §7.1-2 fix, probe4: the previous `ignorePause` full-frame re-run ticked
|
|
360
|
-
// EVERY host-driven world, double-ticking every already-running
|
|
361
|
-
// `pausable: false` world (D10's ordinary loop never stops ticking them).
|
|
362
|
-
const onlyFrozen = frameOpts?.onlyFrozen ?? false;
|
|
363
|
-
// Checklist item 7: snapshot the world count ONCE at entry and iterate
|
|
364
|
-
// by index in both loops below. A world registered mid-frame (e.g. from
|
|
365
|
-
// a game-scoped system's side effect) joins at the NEXT `runFrame` call,
|
|
366
|
-
// not this one — `for (const world of roots)` would otherwise pick up
|
|
367
|
-
// a world pushed during this very frame. This also drops the two
|
|
368
|
-
// per-phase `for...of` iterator allocations.
|
|
369
|
-
const n = roots.length;
|
|
370
|
-
// §7.1-11 fix (probe5): `stateBridge.bump()` must fire iff at least one
|
|
371
|
-
// world actually advanced this call — not unconditionally. `advanced`
|
|
372
|
-
// covers rule (a) below (host-driven roots this call actually ticked);
|
|
373
|
-
// rule (b): a self-driven world that is RUNNING — which is every
|
|
374
|
-
// self-driven world while not paused, and, while paused, the ones the
|
|
375
|
-
// gate can't reach (`pausable: false`, or no `setPaused` capability) —
|
|
376
|
-
// checked once, up front, over the same `roots` array (no extra
|
|
377
|
-
// allocation, matching every other loop here). Rule (b) is skipped in
|
|
378
|
-
// `onlyFrozen` mode: a `step()` call bumps iff it ticked a frozen world
|
|
379
|
-
// — self-driven notifications belong to the loop's own `runFrame`s.
|
|
380
|
-
let advanced = false;
|
|
381
|
-
if (!onlyFrozen) {
|
|
382
|
-
for (let i = 0; i < n; i++) {
|
|
383
|
-
const world = roots[i];
|
|
384
|
-
if (!world.mounted.drivesOwnLoop)
|
|
385
|
-
continue;
|
|
386
|
-
if (!paused || !world.pausable || !world.mounted.setPaused) {
|
|
387
|
-
advanced = true;
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
if (onlyFrozen) {
|
|
393
|
-
// step(): the frozen set is host-driven + pausable + currently paused.
|
|
394
|
-
// While NOT paused this set is empty by construction, so this whole
|
|
395
|
-
// branch is a natural no-op — `Game.play.step()`'s "no-op while
|
|
396
|
-
// running" behavior falls straight out of this, no separate guard
|
|
397
|
-
// needed.
|
|
398
|
-
for (const phase of PHASE_ORDER) {
|
|
399
|
-
profiler.beginPhase();
|
|
400
|
-
for (let i = 0; i < n; i++) {
|
|
401
|
-
const world = roots[i];
|
|
402
|
-
if (world.mounted.drivesOwnLoop)
|
|
403
|
-
continue;
|
|
404
|
-
if (!(paused && world.pausable))
|
|
405
|
-
continue; // only the frozen set
|
|
406
|
-
try {
|
|
407
|
-
world.frame?.runPhase(phase, dt);
|
|
408
|
-
}
|
|
409
|
-
catch (err) {
|
|
410
|
-
console.error(`[game] world "${world.id}" (kind: ${world.kind}) runPhase("${phase}") threw ` +
|
|
411
|
-
'(step()):', err);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
profiler.endPhase(phase);
|
|
415
|
-
}
|
|
416
|
-
for (let i = 0; i < n; i++) {
|
|
417
|
-
const world = roots[i];
|
|
418
|
-
if (world.mounted.drivesOwnLoop)
|
|
419
|
-
continue;
|
|
420
|
-
if (!(paused && world.pausable))
|
|
421
|
-
continue;
|
|
422
|
-
advanced = true;
|
|
423
|
-
if (world.frame) {
|
|
424
|
-
try {
|
|
425
|
-
world.frame.endFrame?.();
|
|
426
|
-
}
|
|
427
|
-
catch (err) {
|
|
428
|
-
console.error(`[game] world "${world.id}" (kind: ${world.kind}) endFrame() threw (step()):`, err);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
try {
|
|
433
|
-
world.mounted.update?.(dt);
|
|
434
|
-
}
|
|
435
|
-
catch (err) {
|
|
436
|
-
console.error(`[game] world "${world.id}" (kind: ${world.kind}) update() threw (step()):`, err);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
for (const phase of PHASE_ORDER) {
|
|
443
|
-
profiler.beginPhase();
|
|
444
|
-
// D15/T-D15.4: `runTicks`'s fast-forward skip — `preRender`/`render`
|
|
445
|
-
// are the ONLY phases ever skipped this way (every earlier gameplay
|
|
446
|
-
// phase, and `endFrame` below, always run) — see `skipRenderPhases`'s
|
|
447
|
-
// declaration above and `runTicks`'s doc comment on `GameInternal`.
|
|
448
|
-
const skipThisPhase = skipRenderPhases && (phase === SystemPhase.PRE_RENDER || phase === SystemPhase.RENDER);
|
|
449
|
-
if (!skipThisPhase) {
|
|
450
|
-
systems.runPhase(phase, dt);
|
|
451
|
-
for (let i = 0; i < n; i++) {
|
|
452
|
-
const world = roots[i];
|
|
453
|
-
if (world.mounted.drivesOwnLoop)
|
|
454
|
-
continue;
|
|
455
|
-
// D10/T7.6: a `pausable` world under an active (non-ignored) pause
|
|
456
|
-
// skips every phase except `render` — its render still runs, every
|
|
457
|
-
// substep, but with `dt` forced to `0` (deterministic: no
|
|
458
|
-
// time-based render effect silently keeps animating a "frozen"
|
|
459
|
-
// scene). A `pausable: false` world (or any world while
|
|
460
|
-
// `ignorePause`) is unaffected.
|
|
461
|
-
const frozen = !ignorePause && paused && world.pausable;
|
|
462
|
-
if (frozen && phase !== SystemPhase.RENDER)
|
|
463
|
-
continue;
|
|
464
|
-
const phaseDt = frozen ? 0 : dt;
|
|
465
|
-
// Checklist item 2: isolate each world's per-phase work — one
|
|
466
|
-
// world's `runPhase` throwing must not starve sibling roots still
|
|
467
|
-
// due this phase, nor abort the frame. Mirrors `runOne`'s style in
|
|
468
|
-
// `core/system-runner.ts` (loud console.error, never swallowed).
|
|
469
|
-
try {
|
|
470
|
-
world.frame?.runPhase(phase, phaseDt);
|
|
471
|
-
}
|
|
472
|
-
catch (err) {
|
|
473
|
-
console.error(`[game] world "${world.id}" (kind: ${world.kind}) runPhase("${phase}") threw:`, err);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
profiler.endPhase(phase);
|
|
478
|
-
}
|
|
479
|
-
for (let i = 0; i < n; i++) {
|
|
480
|
-
const world = roots[i];
|
|
481
|
-
if (world.mounted.drivesOwnLoop)
|
|
482
|
-
continue;
|
|
483
|
-
// A fully-frozen world gets no `endFrame`/opaque-`update` call either —
|
|
484
|
-
// there is nothing to "end the frame" of when nothing ran this substep.
|
|
485
|
-
const frozen = !ignorePause && paused && world.pausable;
|
|
486
|
-
if (frozen)
|
|
487
|
-
continue;
|
|
488
|
-
advanced = true;
|
|
489
|
-
if (world.frame) {
|
|
490
|
-
try {
|
|
491
|
-
world.frame.endFrame?.();
|
|
492
|
-
}
|
|
493
|
-
catch (err) {
|
|
494
|
-
console.error(`[game] world "${world.id}" (kind: ${world.kind}) endFrame() threw:`, err);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
// Checklist item 2: same isolation for the opaque-world fallback —
|
|
499
|
-
// one foreign mount's `update` throwing must not starve its
|
|
500
|
-
// siblings' `endFrame`/`update` calls this same loop.
|
|
501
|
-
try {
|
|
502
|
-
world.mounted.update?.(dt);
|
|
503
|
-
}
|
|
504
|
-
catch (err) {
|
|
505
|
-
console.error(`[game] world "${world.id}" (kind: ${world.kind}) update() threw:`, err);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
// T7.4 slice 1 (REACT-STATE-BRIDGE.md §2): bump + notify LAST, after
|
|
511
|
-
// every phase of every world and every world's endFrame/update above —
|
|
512
|
-
// subscribers must only ever observe post-frame state. Bumped at most
|
|
513
|
-
// once per completed `runFrame`/`step()` call, never per phase/world —
|
|
514
|
-
// and, per §7.1-11's fix, only when `advanced` (see above) is true: a
|
|
515
|
-
// fully-gated paused game produces no notifications at all.
|
|
516
|
-
if (advanced) {
|
|
517
|
-
stateBridge.bump();
|
|
518
|
-
tick++;
|
|
519
|
-
simT += dt;
|
|
520
|
-
}
|
|
521
|
-
profiler.endFrame();
|
|
522
|
-
if (rngTrapEnabled)
|
|
523
|
-
rngTrap.disable();
|
|
524
|
-
}
|
|
525
|
-
const gameInternal = {
|
|
526
|
-
loop: opts.loop,
|
|
527
|
-
assets: opts.assets,
|
|
528
|
-
profiler,
|
|
529
|
-
systems,
|
|
530
|
-
get roots() {
|
|
531
|
-
return roots;
|
|
532
|
-
},
|
|
533
|
-
world(id) {
|
|
534
|
-
return roots.find((w) => w.id === id) ?? null;
|
|
535
|
-
},
|
|
536
|
-
get defaultWorld() {
|
|
537
|
-
return requireDefaultWorld();
|
|
538
|
-
},
|
|
539
|
-
get systemAdapters() {
|
|
540
|
-
return computeSystemAdapters();
|
|
541
|
-
},
|
|
542
|
-
get components() {
|
|
543
|
-
return requireFirstPartyCtx('components').components;
|
|
544
|
-
},
|
|
545
|
-
get input() {
|
|
546
|
-
return requireFirstPartyCtx('input').input;
|
|
547
|
-
},
|
|
548
|
-
get audio() {
|
|
549
|
-
return requireFirstPartyCtx('audio').audio;
|
|
550
|
-
},
|
|
551
|
-
state: stateBridge,
|
|
552
|
-
play: {
|
|
553
|
-
get paused() {
|
|
554
|
-
return paused;
|
|
555
|
-
},
|
|
556
|
-
pause() {
|
|
557
|
-
if (paused)
|
|
558
|
-
return;
|
|
559
|
-
paused = true;
|
|
560
|
-
setWorldGates(true);
|
|
561
|
-
},
|
|
562
|
-
resume() {
|
|
563
|
-
if (!paused)
|
|
564
|
-
return;
|
|
565
|
-
paused = false;
|
|
566
|
-
setWorldGates(false);
|
|
567
|
-
},
|
|
568
|
-
step(dt = 1 / 60) {
|
|
569
|
-
// Self-driven pausable roots advance via their adapter's `step()`
|
|
570
|
-
// capability — but only the ones that are actually FROZEN: the game
|
|
571
|
-
// must be paused, and the world must have been gate-able in the
|
|
572
|
-
// first place (`setPaused` present — a world the gate couldn't reach
|
|
573
|
-
// never stopped, so "stepping" it would double-tick a still-running
|
|
574
|
-
// loop, the same §7.1-2 class as the host-driven fix below). While
|
|
575
|
-
// not paused, nothing here runs — step() is a whole-call no-op.
|
|
576
|
-
if (paused) {
|
|
577
|
-
for (const world of roots) {
|
|
578
|
-
if (!world.pausable || !world.mounted.drivesOwnLoop)
|
|
579
|
-
continue;
|
|
580
|
-
if (!world.mounted.setPaused)
|
|
581
|
-
continue; // never gated — still running
|
|
582
|
-
if (world.mounted.step) {
|
|
583
|
-
world.mounted.step();
|
|
584
|
-
}
|
|
585
|
-
else {
|
|
586
|
-
reportGateShortfallOnce('loop', world.id, 'self-driven world, adapter declares no step capability');
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
// §7.1-2 fix (probe4): tick EXACTLY the frozen (host-driven, pausable,
|
|
591
|
-
// paused) set — not `{ ignorePause: true }`, which re-ran a FULL
|
|
592
|
-
// extra frame for every host-driven world (including already-running
|
|
593
|
-
// `pausable: false` ones — a double-tick, since D10's ordinary loop
|
|
594
|
-
// never stops ticking them). See `runFrameImpl`'s `onlyFrozen` mode.
|
|
595
|
-
runFrameImpl(dt, { onlyFrozen: true });
|
|
596
|
-
},
|
|
597
|
-
},
|
|
598
|
-
queryByComponent(cls, opts) {
|
|
599
|
-
// Checklist item 8a: a `worldId` naming a world that doesn't exist is a
|
|
600
|
-
// caller error (typo'd id, wrong manifest) — degrade loudly, matching
|
|
601
|
-
// repo habit, rather than silently returning `[]`. A worldId that DOES
|
|
602
|
-
// exist but isn't first-party legitimately answers "no components"
|
|
603
|
-
// below (the loop's `!isFirstPartyMounted` `continue`), which stays
|
|
604
|
-
// silent — that's a real, not a mistaken, empty result.
|
|
605
|
-
if (opts?.worldId !== undefined && !roots.some((w) => w.id === opts.worldId)) {
|
|
606
|
-
throw new Error(`Game.queryByComponent: unknown worldId "${opts.worldId}"`);
|
|
607
|
-
}
|
|
608
|
-
const result = [];
|
|
609
|
-
for (const world of roots) {
|
|
610
|
-
if (!isFirstPartyMounted(world.mounted))
|
|
611
|
-
continue;
|
|
612
|
-
if (opts?.worldId !== undefined && world.id !== opts.worldId)
|
|
613
|
-
continue;
|
|
614
|
-
if (opts?.kind !== undefined && world.kind !== opts.kind)
|
|
615
|
-
continue;
|
|
616
|
-
// Checklist item 8b: a plain for-loop push instead of
|
|
617
|
-
// `result.push(...arr)` — spread-as-arguments can hit engine/runtime
|
|
618
|
-
// argument-count limits once a world's instance count gets large.
|
|
619
|
-
const instances = world.mounted.ctx.components.queryByComponent(cls);
|
|
620
|
-
for (const inst of instances)
|
|
621
|
-
result.push(inst);
|
|
622
|
-
}
|
|
623
|
-
return result;
|
|
624
|
-
},
|
|
625
|
-
registerWorld(world) {
|
|
626
|
-
if (roots.some((w) => w.id === world.id)) {
|
|
627
|
-
throw new Error(`Game.registerWorld: duplicate world id "${world.id}"`);
|
|
628
|
-
}
|
|
629
|
-
// Checklist item 6: the SAME `mounted` object registered under two
|
|
630
|
-
// world ids would be double-ticked by `runFrame` (its `frame.runPhase`/
|
|
631
|
-
// `endFrame` called once per registration) and double-dispose-wrapped
|
|
632
|
-
// (`createWorldInstance` wraps `mounted.dispose` in place — a second
|
|
633
|
-
// wrap would flip `disposed` and call through on ITS OWN wrapped
|
|
634
|
-
// `originalDispose`, which is harmless today only by accident of
|
|
635
|
-
// `VgaiSceneGameAdapter.dispose` being idempotent; a foreign adapter
|
|
636
|
-
// has no such guarantee). Reject it outright instead.
|
|
637
|
-
if (roots.some((w) => w.mounted === world.mounted)) {
|
|
638
|
-
throw new Error(`Game.registerWorld: world "${world.id}" shares its \`mounted\` object with an ` +
|
|
639
|
-
`already-registered world ("${roots.find((w) => w.mounted === world.mounted).id}") — ` +
|
|
640
|
-
'the same mount cannot be registered twice.');
|
|
641
|
-
}
|
|
642
|
-
roots.push(world);
|
|
643
|
-
// Notify state-bridge subscribers THE INSTANT the world list changes —
|
|
644
|
-
// not just at the next completed `runFrame`. Root-cause fix for the
|
|
645
|
-
// "roots: " (empty) hang in `36-r3f-first-party.spec.ts` on CI
|
|
646
|
-
// (docs/R3F-FIRST-PARTY-DESIGN.md W3, gate 4): `mountAllWorldSpecs`
|
|
647
|
-
// (`create-runtime.ts`) mounts roots SEQUENTIALLY, and a react world's
|
|
648
|
-
// `adapter.mount()` (`resolveDefaultReactAdapter`/`mountOneReactWorld`)
|
|
649
|
-
// renders its tree — synchronously in some React builds, but React 19's
|
|
650
|
-
// concurrent renderer does NOT guarantee a synchronous first commit
|
|
651
|
-
// (see `r3f-adapter.tsx`'s own doc comment on `onCreated`) — BEFORE the
|
|
652
|
-
// caller calls `registerWorld` for that very world. A `useGameState`
|
|
653
|
-
// selector reading `g.roots` can therefore render for the first time
|
|
654
|
-
// while `roots` is still missing entries that register moments later.
|
|
655
|
-
// Previously the ONLY way such a subscriber ever saw the corrected
|
|
656
|
-
// value was the state bridge's next `bump()`, which fires exclusively
|
|
657
|
-
// from `GameInternal.runFrame`'s tail — i.e. only once the host's loop
|
|
658
|
-
// has actually ticked (`started = true; loop.start()` in
|
|
659
|
-
// `create-runtime.ts`, itself gated on EVERY world finishing its
|
|
660
|
-
// mount). On a slow/contended host (CI's 4-core SwiftShader runners)
|
|
661
|
-
// that first tick can be delayed well past a test's assertion window,
|
|
662
|
-
// or — if the tab is ever backgrounded — not fire at all for a long
|
|
663
|
-
// stretch; the subscriber's cached snapshot then sits on its stale
|
|
664
|
-
// (possibly fully empty) first render for that whole time, matching
|
|
665
|
-
// the observed CI symptom exactly ("the HUD element IS mounted; the
|
|
666
|
-
// roots list is EMPTY"). Proven red-then-green by
|
|
667
|
-
// `packages/engine/test/state-bridge.test.ts`'s "registerWorld notifies
|
|
668
|
-
// subscribers immediately" case: a subscriber registered before a
|
|
669
|
-
// world, with `runFrame` NEVER called, only saw the update after this
|
|
670
|
-
// fix. `bump()` also advances `frameVersion` (the only invalidation
|
|
671
|
-
// key `createFrameSelectorCache`/`useGameState` understand — see
|
|
672
|
-
// `frame-selector-cache.ts`), so this doubles as "frameVersion is
|
|
673
|
-
// bumped once per completed runFrame OR once per world registered",
|
|
674
|
-
// documented on `GameStateBridge.frameVersion` below.
|
|
675
|
-
stateBridge.bump();
|
|
676
|
-
// T7.4 slice 2 (REACT-STATE-BRIDGE.md §4): a NON-first-party mount
|
|
677
|
-
// (an ingested/foreign world — first-party mounts are exempt, they're
|
|
678
|
-
// observed via `Game.state`/`useGameState` instead) with no `observe`
|
|
679
|
-
// has no state bridge at all — react HUDs cannot subscribe to it, and
|
|
680
|
-
// silently returning `undefined` forever would hide that. Report ONCE
|
|
681
|
-
// per mount, at registration time, matching this file's existing
|
|
682
|
-
// `[game] world "<id>" (kind: <kind>) ...` console idiom (see
|
|
683
|
-
// `runFrame` below).
|
|
684
|
-
//
|
|
685
|
-
// §7.1-15: a `kind: 'react'` world is ALSO exempt — it has no `observe`
|
|
686
|
-
// BY DESIGN (D8/REACT-WORLD-DESIGN.md: a react world's own mounted tree
|
|
687
|
-
// reads state via `Game.state`/`useGameState`, the SAME first-party
|
|
688
|
-
// bridge a threejs/pixijs world's HUD uses, never `WorldStateObserver`
|
|
689
|
-
// — that hook is scoped to the ingested/foreign-world case). Without
|
|
690
|
-
// this exemption every production react world logged a false-positive
|
|
691
|
-
// "no state bridge" warning at registration (probe: every real react
|
|
692
|
-
// world mount), eroding the signal for a genuinely un-observable
|
|
693
|
-
// ingested world.
|
|
694
|
-
if (!isFirstPartyMounted(world.mounted) && world.kind !== 'react' && !world.mounted.observe) {
|
|
695
|
-
console.warn(`[game] world "${world.id}" (kind: ${world.kind}, adapter: "${world.adapter.id}"): ` +
|
|
696
|
-
'no state bridge — this mounted game has no `observe` (WorldStateObserver); ' +
|
|
697
|
-
'react HUDs/useWorldObservation cannot subscribe to its state (docs/REACT-STATE-BRIDGE.md §4).');
|
|
698
|
-
}
|
|
699
|
-
},
|
|
700
|
-
runFrame(dt, frameOpts) {
|
|
701
|
-
runFrameImpl(dt, frameOpts);
|
|
702
|
-
},
|
|
703
|
-
runTicks(n, ticksOpts) {
|
|
704
|
-
if (!Number.isInteger(n) || n < 0) {
|
|
705
|
-
throw new RangeError(`Game.runTicks: n must be a non-negative integer, got ${n}`);
|
|
706
|
-
}
|
|
707
|
-
// Refuse while paused (D15 §2.b): stepping the frozen set is
|
|
708
|
-
// `Game.play.step()`'s contract, not this one's — see `runTicks`'s doc
|
|
709
|
-
// comment on `GameInternal` above.
|
|
710
|
-
if (paused) {
|
|
711
|
-
throw new DebugError('RUN_TICKS_PAUSED', 'Game.runTicks: refused — Game.play.paused is true; stepping the frozen set is ' +
|
|
712
|
-
"Game.play.step()'s contract, not runTicks' (docs/D15-DETERMINISM-DESIGN.md §2.b)");
|
|
713
|
-
}
|
|
714
|
-
const render = ticksOpts?.render ?? 'last';
|
|
715
|
-
const fixedDt = opts.loop.fixedDt;
|
|
716
|
-
for (let i = 0; i < n; i++) {
|
|
717
|
-
const isFinalTick = i === n - 1;
|
|
718
|
-
const skipRenderPhases = render === 'all' ? false : render === 'none' ? true : !isFinalTick;
|
|
719
|
-
runFrameImpl(fixedDt, { skipRenderPhases });
|
|
720
|
-
}
|
|
721
|
-
},
|
|
722
|
-
};
|
|
723
|
-
// Filed AFTER the shell exists (the WeakMap keys on the Game object
|
|
724
|
-
// itself) so `getDebugRegistry(game)` — the react hooks' and any later
|
|
725
|
-
// consumer's reach-in — works from the moment `createGame` returns.
|
|
726
|
-
registerDebugRegistry(gameInternal, debugRegistry);
|
|
727
|
-
// D15/T-D15.4: wire the run-ticks target the instant the Game shell exists
|
|
728
|
-
// (unlike `setVirtualInputTarget`, which waits for a per-world mount, a
|
|
729
|
-
// Game's own `runTicks` needs nothing else) — this is what makes
|
|
730
|
-
// `window.__vgai.runTicks` (`debug-bridge.ts`) and the editor relay's
|
|
731
|
-
// `run-ticks` case reach the SAME implementation `game.runTicks` above is.
|
|
732
|
-
debugRegistry.setRunTicksTarget({ runTicks: gameInternal.runTicks });
|
|
733
|
-
// D15 (T-D15.1/.3) — same "file after the shell exists" ordering as the
|
|
734
|
-
// debug registry above: `getSeededRandom(game)`/`getGameplayRngTrapControl
|
|
735
|
-
// (game)` (per-world `ctx.random` wiring, and the manifest-aware boot
|
|
736
|
-
// path's post-hoc `setEnabled` call) both work from the moment
|
|
737
|
-
// `createGame` returns.
|
|
738
|
-
registerSeededRandom(gameInternal, seededRandom);
|
|
739
|
-
registerGameplayRngTrapControl(gameInternal, rngTrapControl);
|
|
740
|
-
return gameInternal;
|
|
741
|
-
}
|