@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
|
@@ -1,706 +0,0 @@
|
|
|
1
|
-
import { PHASE_ORDER } from '../core/types';
|
|
2
|
-
import { isFirstPartyMounted } from '../runtime/game';
|
|
3
|
-
import { GameComponent, linkGameComponentHmrClasses, } from './game-component';
|
|
4
|
-
import { logHmrSwapMiss } from './hmr-swap-report';
|
|
5
|
-
/**
|
|
6
|
-
* Find the `WorldInstance` (T7.1, `docs/GAME-ROOT-DESIGN.md`) that owns this
|
|
7
|
-
* manager's `ctx`, for wiring `GameComponent.world` at attach (T7.2, D8 —
|
|
8
|
-
* `docs/GAME-COMPONENT-GENERALIZATION.md` §2). Matches by `mounted.ctx ===
|
|
9
|
-
* ctx` identity (the same probe `isFirstPartyMounted` narrows for) rather
|
|
10
|
-
* than by id, so it works unmodified whether this manager belongs to the
|
|
11
|
-
* default world or a second/third registered world (`test/
|
|
12
|
-
* game-two-roots.test.ts`).
|
|
13
|
-
*
|
|
14
|
-
* WORLD-WIRING STATE (T7.2 slice 2 — was a KNOWN GAP in slice 1, now closed
|
|
15
|
-
* two ways): `ctx.game`/`ctx.roots` exist from the start of `mount()`, but
|
|
16
|
-
* the `WorldInstance` itself is constructed by `registerThreeWorld` in
|
|
17
|
-
* `create-runtime.ts` only AFTER `mount()` returns — i.e. AFTER
|
|
18
|
-
* scene-authored components have already been attached by the scene loader
|
|
19
|
-
* during that same `mount()` call. For those attaches, THIS function still
|
|
20
|
-
* resolves to `undefined` (the world genuinely doesn't exist yet — there is
|
|
21
|
-
* no way around that ordering; the `WorldInstance` wraps `mounted`, which
|
|
22
|
-
* only exists once `mount()` returns). What closes the gap is
|
|
23
|
-
* `adoptWorld()` below: `registerThreeWorld` calls it immediately after
|
|
24
|
-
* building the `WorldInstance`, and it backfills `instance.world` for every
|
|
25
|
-
* already-attached instance still missing one — a one-time wiring
|
|
26
|
-
* completion at registration, not a lazy getter and not a per-frame sync.
|
|
27
|
-
* Only a component attached AFTER that backfill and before the NEXT world's
|
|
28
|
-
* registration (there is no such window in the single-manager-per-world
|
|
29
|
-
* architecture) would still see a stale `undefined`; in practice every
|
|
30
|
-
* instance ends up with a defined, correct `world` by the time
|
|
31
|
-
* `createGameRuntime`/`registerThreeWorld` returns.
|
|
32
|
-
*/
|
|
33
|
-
function resolveWorldInstance(ctx) {
|
|
34
|
-
if (!ctx.game)
|
|
35
|
-
return undefined;
|
|
36
|
-
for (const w of ctx.game.roots) {
|
|
37
|
-
if (isFirstPartyMounted(w.mounted) && w.mounted.ctx === ctx)
|
|
38
|
-
return w;
|
|
39
|
-
}
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
/** Best-effort human label for an error message — a node's `label` (PIXI.Container,
|
|
43
|
-
* v8) or `name` (THREE.Object3D, and PIXI's deprecated alias), falling back to a
|
|
44
|
-
* generic placeholder when neither is set. */
|
|
45
|
-
function describeNode(node) {
|
|
46
|
-
const n = node;
|
|
47
|
-
const label = typeof n.label === 'string' && n.label
|
|
48
|
-
? n.label
|
|
49
|
-
: typeof n.name === 'string' && n.name
|
|
50
|
-
? n.name
|
|
51
|
-
: undefined;
|
|
52
|
-
return label ? `"${label}"` : '(unnamed entity)';
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Manages GameComponent instances attached to entities (Object3Ds).
|
|
56
|
-
*
|
|
57
|
-
* Tracks instances per Object3D and per phase. Registers one tick function per
|
|
58
|
-
* phase via `ctx.systems.setComponentTick(phase, ...)` (T7.1 slice 2) — a
|
|
59
|
-
* dedicated slot, not a bare `add()`ed system. `SystemRunner.runPhase` always
|
|
60
|
-
* runs the componentTick slot after every engine-bucket system and before
|
|
61
|
-
* every game-bucket system in that phase, so component updates interleave
|
|
62
|
-
* correctly with engine systems BY STRUCTURE: an engine system `add()`ed
|
|
63
|
-
* after this manager is constructed still runs before its tick (see
|
|
64
|
-
* `test/system-runs-before-component-tick.test.ts`).
|
|
65
|
-
*
|
|
66
|
-
* Kind (T7.2 slice 2, D8 §3): a manager is constructed for exactly ONE
|
|
67
|
-
* `AdapterSurface` (`opts.kind`, default `'threejs'` — every existing call site
|
|
68
|
-
* omits it and gets the same manager slice 1 built). `attach()` enforces two
|
|
69
|
-
* rules against that kind, loudly:
|
|
70
|
-
* - a `'react'`-kind manager throws on EVERY attach (react entities host no
|
|
71
|
-
* GameComponents — render from game state via the T7.4 state bridge);
|
|
72
|
-
* - any other kind throws when the component's `static declaredKind` is set
|
|
73
|
-
* and doesn't match this manager's kind (`'any'` is exempt — a
|
|
74
|
-
* kind-agnostic component may attach wherever components are legal).
|
|
75
|
-
* A `'pixijs'`-kind manager wires `rigidBody`/`collider` from `opts.physics2d`
|
|
76
|
-
* (a `Physics2DRegistry`) instead of the 3D `physics: PhysicsRegistry`
|
|
77
|
-
* parameter — the field names match by design (`world2d/types.ts`), so this
|
|
78
|
-
* is a retype of the wiring, not a new shape. `physics` is optional (T7.3
|
|
79
|
-
* slice 2) — a `'pixijs'`-kind manager never reads it (`resolvePhysicsRefs`
|
|
80
|
-
* below only reaches the 3D branch for a non-pixijs manager), so a caller
|
|
81
|
-
* building one no longer has to construct a throwaway 3D `PhysicsRegistry`
|
|
82
|
-
* just to satisfy this signature.
|
|
83
|
-
*/
|
|
84
|
-
export function createComponentManager(ctx, physics, opts) {
|
|
85
|
-
const kind = opts?.kind ?? 'threejs';
|
|
86
|
-
const physics2d = opts?.physics2d;
|
|
87
|
-
const byPhase = new Map();
|
|
88
|
-
const byEntity = new Map();
|
|
89
|
-
// --- HMR registry-keyed identity (T5.4) ----------------------------------
|
|
90
|
-
// `hotSwap` used to match live instances by `inst.constructor.name` — fragile
|
|
91
|
-
// under minification, a dev renaming a class, or two distinct classes that
|
|
92
|
-
// happen to share a runtime name. Both trees walked by `applyComponents`
|
|
93
|
-
// (`scene/component-registry.ts`) and `applyComponents2D`
|
|
94
|
-
// (`world2d/scene2d-loader.ts`) now pass the `componentRegistry` name the
|
|
95
|
-
// class was looked up under — plus the raw pre-`schema.parse` authored data —
|
|
96
|
-
// into `attach()`'s optional third argument, recorded here keyed by instance
|
|
97
|
-
// identity (a WeakMap: no explicit cleanup needed, entries die with the
|
|
98
|
-
// instance). An instance attached WITHOUT this info (an ad-hoc, code-attached
|
|
99
|
-
// component — `playground/main.ts`'s `ctx.components.attach(cube, new Cls())`,
|
|
100
|
-
// and this file's own `component-lifecycle.test.ts`) has no registry key on
|
|
101
|
-
// record; `hotSwap` falls back to the legacy `constructor.name` compare for
|
|
102
|
-
// those ONLY, preserving pre-T5.4 behavior for the ad-hoc path exactly (see
|
|
103
|
-
// `hotSwap` below).
|
|
104
|
-
const registryKeyOf = new WeakMap();
|
|
105
|
-
const authoredPropsOf = new WeakMap();
|
|
106
|
-
// --- Frame-boundary init flush (T1.11) -----------------------------------
|
|
107
|
-
// `pendingInit` holds every attached-but-not-yet-initialized instance. An
|
|
108
|
-
// instance is pushed into its phase's `byPhase` list immediately on attach
|
|
109
|
-
// (so getComponents()/hotSwap() see it right away), but the per-phase tick
|
|
110
|
-
// below FILTERS OUT anything still in `pendingInit` — so an instance can
|
|
111
|
-
// never receive update() before its init() has resolved, no matter how many
|
|
112
|
-
// frames init() takes (a real async load included).
|
|
113
|
-
//
|
|
114
|
-
// `initPromises` tracks in-flight init() calls (keyed by instance) so:
|
|
115
|
-
// (a) init() is called exactly once per instance, whether it was started
|
|
116
|
-
// by the automatic per-frame flush below or by an explicit
|
|
117
|
-
// `initAll()` call (they share the same `startInit` helper), and
|
|
118
|
-
// (b) `initAll()` can `await` an init() the automatic flush already
|
|
119
|
-
// kicked off, instead of double-invoking it.
|
|
120
|
-
//
|
|
121
|
-
// Semantics: init() is awaited (via a chained promise) before the instance
|
|
122
|
-
// is removed from `pendingInit`; only then is it eligible for update(). A
|
|
123
|
-
// throwing/rejecting init() is isolated the same way update()/dispose() are
|
|
124
|
-
// (T1.8) — logged loudly, never left to crash the frame loop or wedge the
|
|
125
|
-
// instance out of ticking forever (it's still removed from `pendingInit`
|
|
126
|
-
// after the error, matching "degrade loudly, don't hang").
|
|
127
|
-
const pendingInit = new Set();
|
|
128
|
-
const initPromises = new Map();
|
|
129
|
-
// --- Pending-init re-arm on hotSwap (§7.1-6, probe8) ---------------------
|
|
130
|
-
// `swapOneIfMatched` may land while an instance's init() is still in flight
|
|
131
|
-
// (`initPromises.has(inst)` — the OLD class's init hasn't settled yet). The
|
|
132
|
-
// live instance's prototype is swapped to the NEW class SYNCHRONOUSLY at
|
|
133
|
-
// swap time, but the in-flight promise chain still belongs to the OLD
|
|
134
|
-
// class's init call — its trailing `.then()` below is what clears
|
|
135
|
-
// `pendingInit`/`initPromises` once it settles, and nothing else re-triggers
|
|
136
|
-
// init() for the new class. `pendingReinit` records "a swap landed while
|
|
137
|
-
// this instance's init was in flight" so that SAME trailing `.then()` can,
|
|
138
|
-
// instead of clearing pendingInit, re-kick `startInit` for the (by-then
|
|
139
|
-
// already-swapped) instance — whose `.init` now resolves to the NEW class's
|
|
140
|
-
// method. `pendingInit` membership is deliberately left untouched across the
|
|
141
|
-
// whole handoff: the instance must stay ineligible for update() from the
|
|
142
|
-
// moment the OLD init started until the NEW init resolves, exactly as it
|
|
143
|
-
// was mid-old-init (T1.11's "never before init() resolves" invariant, now
|
|
144
|
-
// spanning a swap instead of being violated by one). A Set, not a Map to a
|
|
145
|
-
// specific class: by the time this fires, the CURRENT prototype (whichever
|
|
146
|
-
// class most recently swapped in) is what `inst.init` resolves to, so
|
|
147
|
-
// multiple swaps mid-flight collapse to exactly one re-kicked init for the
|
|
148
|
-
// LAST class — never a double-invoke, never a lost update.
|
|
149
|
-
const pendingReinit = new Set();
|
|
150
|
-
function startInit(inst) {
|
|
151
|
-
const existing = initPromises.get(inst);
|
|
152
|
-
if (existing)
|
|
153
|
-
return existing;
|
|
154
|
-
const p = Promise.resolve()
|
|
155
|
-
.then(() => inst.init?.(ctx))
|
|
156
|
-
.catch((err) => {
|
|
157
|
-
console.error(`[component-manager] ${inst.constructor.name}.init() threw:`, err);
|
|
158
|
-
})
|
|
159
|
-
.then(() => {
|
|
160
|
-
if (pendingReinit.has(inst)) {
|
|
161
|
-
// A hotSwap landed mid-init (probe8): don't clear pendingInit — the
|
|
162
|
-
// instance must stay un-ticked until the NEW class's init (kicked
|
|
163
|
-
// off below) resolves. Clear initPromises first so the re-kicked
|
|
164
|
-
// startInit doesn't see itself as "already in flight" and return
|
|
165
|
-
// this (already-settling) promise back out.
|
|
166
|
-
pendingReinit.delete(inst);
|
|
167
|
-
initPromises.delete(inst);
|
|
168
|
-
startInit(inst);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
pendingInit.delete(inst);
|
|
172
|
-
initPromises.delete(inst);
|
|
173
|
-
});
|
|
174
|
-
initPromises.set(inst, p);
|
|
175
|
-
return p;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Kick off init() for anything attached since the last flush that hasn't
|
|
179
|
-
* been started yet. Fire-and-forget (not awaited) — called at the start of
|
|
180
|
-
* every phase tick below, so a component attached mid-frame (e.g. from a
|
|
181
|
-
* trigger callback) gets its init() started as soon as possible without
|
|
182
|
-
* game code ever calling `initAll()` manually. Idempotent / cheap no-op
|
|
183
|
-
* when `pendingInit` is empty or everything in it is already in flight.
|
|
184
|
-
*/
|
|
185
|
-
function flushPendingInitAuto() {
|
|
186
|
-
for (const inst of pendingInit)
|
|
187
|
-
startInit(inst);
|
|
188
|
-
}
|
|
189
|
-
// --- In-tick structural-change deferral state ---------------------------
|
|
190
|
-
// A component's update() may call attach()/detach() (a Unity-MonoBehaviour
|
|
191
|
-
// style spawn/despawn-from-update pattern). Mutating `byPhase` mid-iteration
|
|
192
|
-
// would (a) skip a sibling when a splice shifts indices, and (b) tick a fresh
|
|
193
|
-
// instance before its init() ran. So while a phase loop is running we DEFER
|
|
194
|
-
// structural changes and apply them only after the loop completes.
|
|
195
|
-
let isTicking = false;
|
|
196
|
-
// Entities whose detach was requested during the current tick — applied
|
|
197
|
-
// (dispose + removal) after the phase loop completes.
|
|
198
|
-
const pendingDetach = [];
|
|
199
|
-
// Components whose attach was requested during the current tick — applied
|
|
200
|
-
// after the phase loop completes, so they start ticking next frame (never
|
|
201
|
-
// before their init()).
|
|
202
|
-
const pendingAttach = [];
|
|
203
|
-
// Live set of instances detached this tick. The running phase loop iterates a
|
|
204
|
-
// snapshot, so this guard ensures an instance detached earlier in the frame is
|
|
205
|
-
// not update()'d again (use-after-dispose protection).
|
|
206
|
-
const detachedThisTick = new Set();
|
|
207
|
-
for (const phase of PHASE_ORDER) {
|
|
208
|
-
byPhase.set(phase, []);
|
|
209
|
-
}
|
|
210
|
-
function flushPending() {
|
|
211
|
-
// Detaches win within a flush: an attach and a detach of the same entity
|
|
212
|
-
// issued in one tick resolves to detached, regardless of issue order
|
|
213
|
-
// (NOT "resolves in the order the caller issued the two kinds of op" —
|
|
214
|
-
// that was never actually true; see probe7 "attach-detach" finding).
|
|
215
|
-
// Detaches are applied first (as before), and every entity they touch is
|
|
216
|
-
// collected into `detachedEntities` so a same-tick pending attach whose
|
|
217
|
-
// target is in that set can be skipped below rather than landing a
|
|
218
|
-
// component on an entity the caller already despawned this tick.
|
|
219
|
-
let detachedEntities = null;
|
|
220
|
-
if (pendingDetach.length > 0) {
|
|
221
|
-
const batch = pendingDetach.splice(0);
|
|
222
|
-
detachedEntities = new Set(batch);
|
|
223
|
-
for (const node of batch)
|
|
224
|
-
performDetach(node);
|
|
225
|
-
}
|
|
226
|
-
if (pendingAttach.length > 0) {
|
|
227
|
-
const batch = pendingAttach.splice(0);
|
|
228
|
-
for (const { node, instance, registry } of batch) {
|
|
229
|
-
if (detachedEntities?.has(node)) {
|
|
230
|
-
// The target entity was detached in this same flush: the attach
|
|
231
|
-
// never actually happened (it was never pushed into byPhase/
|
|
232
|
-
// byEntity), so there is nothing to detach/dispose here — just
|
|
233
|
-
// purge it from init tracking so it never gets init()'d.
|
|
234
|
-
pendingInit.delete(instance);
|
|
235
|
-
initPromises.delete(instance);
|
|
236
|
-
pendingReinit.delete(instance);
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
performAttach(node, instance, registry);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
detachedThisTick.clear();
|
|
243
|
-
}
|
|
244
|
-
// Register the ONE component-tick function for each phase, via the
|
|
245
|
-
// dedicated slot (T7.1 slice 2) — NOT ctx.systems.add(). This is what
|
|
246
|
-
// makes "engine systems before component ticks" hold by structure: the
|
|
247
|
-
// slot always runs after the phase's entire engine bucket, regardless of
|
|
248
|
-
// when this manager was constructed relative to other systems.add() calls.
|
|
249
|
-
for (const phase of PHASE_ORDER) {
|
|
250
|
-
ctx.systems.setComponentTick(phase, (dt) => {
|
|
251
|
-
// Frame-boundary init flush (T1.11): every phase tick starts by kicking
|
|
252
|
-
// off init() for anything pending — no manual `initAll()` call required
|
|
253
|
-
// from game code. See `flushPendingInitAuto` above.
|
|
254
|
-
flushPendingInitAuto();
|
|
255
|
-
isTicking = true;
|
|
256
|
-
try {
|
|
257
|
-
// Snapshot the phase list: an in-loop attach must not be ticked this frame,
|
|
258
|
-
// and an in-loop detach (which only marks `detachedThisTick`) must not shift
|
|
259
|
-
// the indices we are iterating and skip a sibling.
|
|
260
|
-
const list = byPhase.get(phase).slice();
|
|
261
|
-
for (let i = 0; i < list.length; i++) {
|
|
262
|
-
const inst = list[i];
|
|
263
|
-
// A component detached earlier this frame must NOT be update()'d again.
|
|
264
|
-
if (detachedThisTick.has(inst))
|
|
265
|
-
continue;
|
|
266
|
-
// A component whose init() hasn't resolved yet must NOT be
|
|
267
|
-
// update()'d — this is the actual guarantee behind T1.11: never
|
|
268
|
-
// before init() resolves, regardless of how it got attached.
|
|
269
|
-
if (pendingInit.has(inst))
|
|
270
|
-
continue;
|
|
271
|
-
// Isolate per component: a throwing update() must not stop siblings
|
|
272
|
-
// in the same phase from ticking, and must not leave the manager
|
|
273
|
-
// wedged (isTicking stuck true, pending attach/detach never
|
|
274
|
-
// flushed). Failures are loud — logged with the component name —
|
|
275
|
-
// never swallowed silently.
|
|
276
|
-
try {
|
|
277
|
-
ctx.game?.profiler.beginComponent();
|
|
278
|
-
inst.update(dt, ctx);
|
|
279
|
-
}
|
|
280
|
-
catch (err) {
|
|
281
|
-
console.error(`[component-manager] ${inst.constructor.name}.update() threw:`, err);
|
|
282
|
-
}
|
|
283
|
-
finally {
|
|
284
|
-
ctx.game?.profiler.endComponent(inst.constructor.name);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
finally {
|
|
289
|
-
// Guaranteed even if something above throws unexpectedly (e.g. a
|
|
290
|
-
// future edit removes the per-component try/catch): isTicking must
|
|
291
|
-
// never get stuck true, and structural changes queued during this
|
|
292
|
-
// tick must still flush before the next tick runs.
|
|
293
|
-
isTicking = false;
|
|
294
|
-
flushPending();
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
function migratePhase(inst, from, to) {
|
|
299
|
-
if (from === to)
|
|
300
|
-
return;
|
|
301
|
-
const oldList = byPhase.get(from);
|
|
302
|
-
const idx = oldList.indexOf(inst);
|
|
303
|
-
if (idx !== -1)
|
|
304
|
-
oldList.splice(idx, 1);
|
|
305
|
-
byPhase.get(to).push(inst);
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* `hotSwap`'s per-instance identity check + swap (T5.4) — split out of
|
|
309
|
-
* `hotSwap` itself purely to keep that function's cognitive complexity
|
|
310
|
-
* down; matching semantics are documented on `hotSwap` above. Returns
|
|
311
|
-
* whether `inst` matched `name` (and was therefore swapped).
|
|
312
|
-
*/
|
|
313
|
-
function swapOneIfMatched(inst, name, NewClass) {
|
|
314
|
-
const trackedKey = registryKeyOf.get(inst);
|
|
315
|
-
const isMatch = trackedKey !== undefined ? trackedKey === name : inst.constructor.name === name;
|
|
316
|
-
if (!isMatch)
|
|
317
|
-
return false;
|
|
318
|
-
const oldPhase = inst.constructor.phase ?? 'gameLogic';
|
|
319
|
-
const newPhase = NewClass.phase ?? 'gameLogic';
|
|
320
|
-
linkGameComponentHmrClasses(inst.constructor, NewClass);
|
|
321
|
-
Object.setPrototypeOf(inst, NewClass.prototype);
|
|
322
|
-
migratePhase(inst, oldPhase, newPhase);
|
|
323
|
-
reparseSchemaOnSwap(inst, NewClass, name);
|
|
324
|
-
// §7.1-6 / probe8: if this instance's init() is still in flight (attached,
|
|
325
|
-
// startInit already called, promise not yet settled), record that a swap
|
|
326
|
-
// landed mid-init so `startInit`'s trailing `.then()` re-kicks a fresh
|
|
327
|
-
// init() for the NEW class once the OLD one settles, instead of silently
|
|
328
|
-
// clearing `pendingInit` and leaving the new init never invoked. An
|
|
329
|
-
// instance that is merely QUEUED (attached, `startInit` not yet called —
|
|
330
|
-
// `pendingInit.has(inst)` but no `initPromises` entry) needs no help here:
|
|
331
|
-
// its eventual first `startInit` call naturally reads the (already
|
|
332
|
-
// swapped) NEW prototype's `init`. An instance whose init already fully
|
|
333
|
-
// resolved (no `initPromises` entry, `pendingInit` doesn't have it either)
|
|
334
|
-
// is intentionally left alone — a swap is a live-patch, not a remount.
|
|
335
|
-
if (initPromises.has(inst)) {
|
|
336
|
-
pendingReinit.add(inst);
|
|
337
|
-
}
|
|
338
|
-
return true;
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Schema re-parse on swap (T5.4, docs/BACKBONE-TASKS.md's T5.4 detail note).
|
|
342
|
-
* `inst` just had its prototype swapped to `NewClass`; if `NewClass` declares
|
|
343
|
-
* a `static schema` AND `inst` was attached with recorded authored props
|
|
344
|
-
* (registry-keyed instances only — ad-hoc instances have none and are
|
|
345
|
-
* skipped, nothing to validate), re-validate those props against the NEW
|
|
346
|
-
* schema:
|
|
347
|
-
* - success: `next = schema.parse(authoredData)`. Merge ONLY fields
|
|
348
|
-
* `next` has that `inst` does NOT already own — i.e. fields the NEW
|
|
349
|
-
* schema added (which get their schema default/authored value) — the
|
|
350
|
-
* merge never touches a field already present on `inst`, so mutated
|
|
351
|
-
* runtime/gameplay state always wins over a re-derived authored value.
|
|
352
|
-
* Authored-VALUE edits (the user changing a field in the inspector)
|
|
353
|
-
* flow through the normal scene-edit path, never through this merge.
|
|
354
|
-
* - failure (old authored data no longer valid against the new schema,
|
|
355
|
-
* e.g. a field's type changed or a new field has no default): loud
|
|
356
|
-
* swap-miss warning, `inst`'s fields are left exactly as they were
|
|
357
|
-
* (last-good props) — never partially or invalidly overwritten.
|
|
358
|
-
*/
|
|
359
|
-
function reparseSchemaOnSwap(inst, NewClass, key) {
|
|
360
|
-
const schema = NewClass.schema;
|
|
361
|
-
if (!schema)
|
|
362
|
-
return;
|
|
363
|
-
const authored = authoredPropsOf.get(inst);
|
|
364
|
-
if (authored === undefined)
|
|
365
|
-
return;
|
|
366
|
-
const result = schema.safeParse(authored);
|
|
367
|
-
if (!result.success) {
|
|
368
|
-
logHmrSwapMiss({ key, reason: 'schema-reparse-failed', error: result.error.message });
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
const next = result.data;
|
|
372
|
-
const target = inst;
|
|
373
|
-
for (const [field, value] of Object.entries(next)) {
|
|
374
|
-
if (!Object.hasOwn(target, field)) {
|
|
375
|
-
target[field] = value;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Enforce the attach rules (T7.2 slice 2, D8 §3) BEFORE any structural
|
|
381
|
-
* change happens — called synchronously from the public `attach()`, ahead
|
|
382
|
-
* of the isTicking defer, so the throw always surfaces to the caller of
|
|
383
|
-
* `attach()` and never fires later out of a deferred `flushPending()` (mid
|
|
384
|
-
* -tick attaches are validated up front, at the moment they're requested).
|
|
385
|
-
*/
|
|
386
|
-
function validateAttach(node, instance) {
|
|
387
|
-
const ComponentClass = instance.constructor;
|
|
388
|
-
const declaredKind = ComponentClass.declaredKind ?? 'threejs';
|
|
389
|
-
const label = describeNode(node);
|
|
390
|
-
if (kind === 'react') {
|
|
391
|
-
throw new Error(`${ComponentClass.name}: cannot attach to entity ${label} — this manager's world is a ` +
|
|
392
|
-
"'react' world, and react entities host no GameComponents; render from game state " +
|
|
393
|
-
'via the T7.4 state bridge instead.');
|
|
394
|
-
}
|
|
395
|
-
if (declaredKind !== 'any' && declaredKind !== kind) {
|
|
396
|
-
throw new Error(`${ComponentClass.name}: declared kind '${declaredKind}' does not match entity ` +
|
|
397
|
-
`${label}'s world kind '${kind}' — set \`static declaredKind\` on ${ComponentClass.name} ` +
|
|
398
|
-
"if it is meant to attach in a non-default-kind world (or declare 'any' for a " +
|
|
399
|
-
'kind-agnostic component).');
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
/** Resolve rigidBody/collider refs for `node` from this manager's kind-appropriate
|
|
403
|
-
* registry: `Physics2DRegistry` for a 'pixijs' manager, `PhysicsRegistry` otherwise
|
|
404
|
-
* (`physics` is only optional for a 'pixijs' manager, which never reaches this
|
|
405
|
-
* branch — every real threejs call site still passes it). */
|
|
406
|
-
function resolvePhysicsRefs(node) {
|
|
407
|
-
if (kind === 'pixijs') {
|
|
408
|
-
return physics2d?.get(node);
|
|
409
|
-
}
|
|
410
|
-
return physics?.get(node);
|
|
411
|
-
}
|
|
412
|
-
function performAttach(node, instance, registry) {
|
|
413
|
-
instance.node = node;
|
|
414
|
-
instance.world = resolveWorldInstance(ctx);
|
|
415
|
-
const refs = resolvePhysicsRefs(node);
|
|
416
|
-
instance.rigidBody = refs?.body ?? null;
|
|
417
|
-
instance.collider = refs?.collider ?? null;
|
|
418
|
-
if (registry) {
|
|
419
|
-
registryKeyOf.set(instance, registry.key);
|
|
420
|
-
authoredPropsOf.set(instance, registry.props);
|
|
421
|
-
}
|
|
422
|
-
const phase = instance.constructor.phase ?? 'gameLogic';
|
|
423
|
-
byPhase.get(phase).push(instance);
|
|
424
|
-
let entityList = byEntity.get(node);
|
|
425
|
-
if (!entityList) {
|
|
426
|
-
entityList = [];
|
|
427
|
-
byEntity.set(node, entityList);
|
|
428
|
-
}
|
|
429
|
-
entityList.push(instance);
|
|
430
|
-
pendingInit.add(instance);
|
|
431
|
-
}
|
|
432
|
-
function performDetach(node) {
|
|
433
|
-
const instances = byEntity.get(node);
|
|
434
|
-
if (!instances)
|
|
435
|
-
return;
|
|
436
|
-
for (const inst of instances) {
|
|
437
|
-
// Isolate dispose() the same way as update(): one component's dispose
|
|
438
|
-
// throwing must not stop the rest of this entity's components (or the
|
|
439
|
-
// rest of a pending-detach batch) from being cleaned up and removed.
|
|
440
|
-
try {
|
|
441
|
-
inst.dispose?.(ctx);
|
|
442
|
-
}
|
|
443
|
-
catch (err) {
|
|
444
|
-
console.error(`[component-manager] ${inst.constructor.name}.dispose() threw:`, err);
|
|
445
|
-
}
|
|
446
|
-
const phase = inst.constructor.phase ?? 'gameLogic';
|
|
447
|
-
const phaseList = byPhase.get(phase);
|
|
448
|
-
const idx = phaseList.indexOf(inst);
|
|
449
|
-
if (idx !== -1)
|
|
450
|
-
phaseList.splice(idx, 1);
|
|
451
|
-
// Also drop it from the pending-init tracking so a not-yet-flushed
|
|
452
|
-
// init() never (re)starts on an already-disposed component (keeps
|
|
453
|
-
// detach symmetric with clear()). The in-flight promise, if any, is
|
|
454
|
-
// simply abandoned — its `.then()` continuation is now a no-op since
|
|
455
|
-
// both maps no longer reference this instance. `pendingReinit` too —
|
|
456
|
-
// a swap recorded mid-init on an instance now being detached must not
|
|
457
|
-
// re-kick an init() for a component that no longer exists.
|
|
458
|
-
pendingInit.delete(inst);
|
|
459
|
-
initPromises.delete(inst);
|
|
460
|
-
pendingReinit.delete(inst);
|
|
461
|
-
}
|
|
462
|
-
byEntity.delete(node);
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
/** Attach a component instance to an entity node (Object3D in a threejs
|
|
466
|
-
* world, PIXI.Container in a pixijs world). Validates the attach rules
|
|
467
|
-
* (§3: react-world throw, kind-mismatch throw) synchronously before any
|
|
468
|
-
* structural change, whether or not a phase tick is currently running.
|
|
469
|
-
* `registry` (T5.4, optional) records the `componentRegistry` name +
|
|
470
|
-
* pre-`schema.parse` authored data a scene-authored attach looked the
|
|
471
|
-
* class up under — `hotSwap` keys on it instead of the live
|
|
472
|
-
* `constructor.name`. Omit it for an ad-hoc, code-attached instance (no
|
|
473
|
-
* registry entry) — `hotSwap` falls back to matching those by class name,
|
|
474
|
-
* unchanged from pre-T5.4 behavior. */
|
|
475
|
-
attach(node, instance, registry) {
|
|
476
|
-
validateAttach(node, instance);
|
|
477
|
-
// Called from inside a phase loop (e.g. a spawn-from-update): defer so the
|
|
478
|
-
// new instance starts ticking next frame, after a subsequent initAll().
|
|
479
|
-
if (isTicking) {
|
|
480
|
-
pendingAttach.push({ node, instance, ...(registry ? { registry } : {}) });
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
performAttach(node, instance, registry);
|
|
484
|
-
},
|
|
485
|
-
/**
|
|
486
|
-
* Call init() on all components that haven't been initialized yet, and
|
|
487
|
-
* wait for them to resolve. Components attached since the last call are
|
|
488
|
-
* covered. Safe to call even when the automatic per-frame flush has
|
|
489
|
-
* already started some of them — `startInit` is idempotent per instance,
|
|
490
|
-
* so this awaits the same in-flight promise rather than double-invoking
|
|
491
|
-
* init(). Manual calls remain useful when subsequent code depends on an
|
|
492
|
-
* init()'s side effects having completed synchronously (the automatic
|
|
493
|
-
* flush only guarantees "ticking starts once init resolves", not
|
|
494
|
-
* "resolved by the time this function returns").
|
|
495
|
-
*/
|
|
496
|
-
async initAll() {
|
|
497
|
-
const batch = [...pendingInit];
|
|
498
|
-
await Promise.all(batch.map(startInit));
|
|
499
|
-
},
|
|
500
|
-
/** Remove all components from an entity node, calling dispose. */
|
|
501
|
-
detach(node) {
|
|
502
|
-
// Called from inside a phase loop (e.g. a despawn-from-update): mark the
|
|
503
|
-
// entity's instances so the running loop skips them, and defer the actual
|
|
504
|
-
// dispose + removal until the loop completes.
|
|
505
|
-
if (isTicking) {
|
|
506
|
-
const instances = byEntity.get(node);
|
|
507
|
-
if (!instances)
|
|
508
|
-
return;
|
|
509
|
-
for (const inst of instances)
|
|
510
|
-
detachedThisTick.add(inst);
|
|
511
|
-
pendingDetach.push(node);
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
performDetach(node);
|
|
515
|
-
},
|
|
516
|
-
/**
|
|
517
|
-
* Backfill `instance.world` for every already-attached instance whose
|
|
518
|
-
* world is still undefined (T7.2 slice 2 — closes the KNOWN GAP
|
|
519
|
-
* documented on `resolveWorldInstance` above). A ONE-TIME wiring
|
|
520
|
-
* completion, called exactly once by `registerThreeWorld`
|
|
521
|
-
* (`create-runtime.ts`) right after this manager's `WorldInstance` is
|
|
522
|
-
* constructed — NOT a lazy getter, NOT a per-frame sync: a plain
|
|
523
|
-
* backfill pass over the existing `byEntity` bookkeeping, with no
|
|
524
|
-
* ongoing cost once it returns. Idempotent and safe to call more than
|
|
525
|
-
* once (only ever touches instances still missing a world).
|
|
526
|
-
*/
|
|
527
|
-
adoptWorld(world) {
|
|
528
|
-
for (const [, instances] of byEntity) {
|
|
529
|
-
for (const inst of instances) {
|
|
530
|
-
if (inst.world === undefined)
|
|
531
|
-
inst.world = world;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
},
|
|
535
|
-
/** Get all component instances for an entity node. */
|
|
536
|
-
getComponents(node) {
|
|
537
|
-
return byEntity.get(node) ?? [];
|
|
538
|
-
},
|
|
539
|
-
/**
|
|
540
|
-
* Get the first attached instance of a given component class on an
|
|
541
|
-
* entity node — the typed single-component sibling of `getComponents`
|
|
542
|
-
* (issue #99: `onTriggerEnter`'s `other` is a raw node; this is how a
|
|
543
|
-
* handler asks "does my trigger partner have a `Collectible` (or
|
|
544
|
-
* whatever marker) component?" instead of an untyped `getComponents`
|
|
545
|
-
* walk or a fragile `other.name === '...'` display-name check).
|
|
546
|
-
* Matches via `instanceof`, so a subclass of `cls` is returned too
|
|
547
|
-
* (same subclass handling as `queryByComponent`). Same ordering as
|
|
548
|
-
* `getComponents` (attach order for that node) and, like
|
|
549
|
-
* `getComponents`, does NOT exclude instances pending-detach this tick
|
|
550
|
-
* — a caller inside a phase tick that cares should filter itself, same
|
|
551
|
-
* as any other `getComponents` consumer. `undefined` if the node has no
|
|
552
|
-
* attached instance of `cls`.
|
|
553
|
-
*/
|
|
554
|
-
getComponent(node, cls) {
|
|
555
|
-
const instances = byEntity.get(node);
|
|
556
|
-
if (!instances)
|
|
557
|
-
return undefined;
|
|
558
|
-
for (const inst of instances) {
|
|
559
|
-
if (inst instanceof cls)
|
|
560
|
-
return inst;
|
|
561
|
-
}
|
|
562
|
-
return undefined;
|
|
563
|
-
},
|
|
564
|
-
/**
|
|
565
|
-
* Query all live attached instances of a given component class within
|
|
566
|
-
* THIS manager — the per-world half of the §5 cross-world query
|
|
567
|
-
* (`docs/GAME-ROOT-DESIGN.md` §5; `Game.queryByComponent` in
|
|
568
|
-
* `runtime/game.ts` is the game-level aggregation over every world's
|
|
569
|
-
* manager).
|
|
570
|
-
*
|
|
571
|
-
* Returns instances (not nodes), `instanceof cls`, in stable attach
|
|
572
|
-
* order — walked from the existing `byEntity` bookkeeping (the same
|
|
573
|
-
* structure `hotSwap`/`clear` iterate), not a new index, so correctness
|
|
574
|
-
* tracks the manager's real attach/detach state rather than a
|
|
575
|
-
* maintained-in-parallel cache. Order note: this is attach order
|
|
576
|
-
* PER ENTITY (the order components were pushed for that Object3D),
|
|
577
|
-
* concatenated in `byEntity`'s insertion order (first-attach order of
|
|
578
|
-
* the entities themselves) — for the common case of each entity
|
|
579
|
-
* attached once this coincides with global chronological attach order;
|
|
580
|
-
* it would not for an entity re-attached to after other entities were
|
|
581
|
-
* attached in between, which this manager does not otherwise track a
|
|
582
|
-
* global order for.
|
|
583
|
-
*
|
|
584
|
-
* Excludes instances pending-detach this tick (`detachedThisTick`) —
|
|
585
|
-
* mirrors the same use-after-dispose guard the phase tick applies, so a
|
|
586
|
-
* query run mid-tick (e.g. from inside a component's `update`) never
|
|
587
|
-
* returns something already torn down this frame.
|
|
588
|
-
*/
|
|
589
|
-
queryByComponent(cls) {
|
|
590
|
-
const result = [];
|
|
591
|
-
for (const [, instances] of byEntity) {
|
|
592
|
-
for (const inst of instances) {
|
|
593
|
-
if (detachedThisTick.has(inst))
|
|
594
|
-
continue;
|
|
595
|
-
if (inst instanceof cls)
|
|
596
|
-
result.push(inst);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return result;
|
|
600
|
-
},
|
|
601
|
-
/**
|
|
602
|
-
* Hot-swap prototypes for every live instance registered under `name`
|
|
603
|
-
* (T5.4). State on `this` survives; method bodies update to the new
|
|
604
|
-
* class. `name` is matched against each instance's RECORDED registry key
|
|
605
|
-
* (`registryKeyOf`, set by `attach()`'s optional `registry` argument) —
|
|
606
|
-
* not the live `constructor.name`, which is fragile under minification, a
|
|
607
|
-
* class rename, or two distinct classes sharing one runtime name (a
|
|
608
|
-
* registry key is stable identity chosen at scene-author time,
|
|
609
|
-
* independent of what the class calls itself). An instance attached
|
|
610
|
-
* WITHOUT a registry key (ad-hoc/code-attached — no `ComponentRegistry`
|
|
611
|
-
* entry) has no such recorded identity, so it falls back to the legacy
|
|
612
|
-
* `constructor.name` compare — this is the ONLY place that fallback
|
|
613
|
-
* applies; a registry-keyed instance never matches by name, so two
|
|
614
|
-
* registry-keyed classes that happen to share a `constructor.name` (e.g.
|
|
615
|
-
* both minified to the same short name) swap independently.
|
|
616
|
-
*
|
|
617
|
-
* Zero matches is a loud swap-miss (`hmr-swap-report.ts`), never a silent
|
|
618
|
-
* no-op — this is "renamed class → loud miss" (docs/BACKBONE-TASKS.md's
|
|
619
|
-
* T5.4 detail note): if a dev renames a component's export, the hot
|
|
620
|
-
* update arrives keyed under the NEW name, no live instance is recorded
|
|
621
|
-
* under it yet (the registry/scene JSON hasn't been re-authored), so
|
|
622
|
-
* nothing matches and the warning fires — the OLD class stays in effect
|
|
623
|
-
* rather than the rename silently doing nothing.
|
|
624
|
-
*
|
|
625
|
-
* GameComponent-subclass guard (§7.1-5, probe7): every editor HMR call
|
|
626
|
-
* site passes any `typeof value === 'function'` module export — that used
|
|
627
|
-
* to include a plain helper function sharing a live component's registry
|
|
628
|
-
* key/name, which this method would happily `Object.setPrototypeOf` onto
|
|
629
|
-
* a live instance, leaving it with no `update()` (a caught-and-logged
|
|
630
|
-
* `TypeError` every frame, forever). `NewClass` is now required to have
|
|
631
|
-
* `GameComponent` somewhere in its prototype chain — anything else is
|
|
632
|
-
* refused OUTRIGHT, loudly, with NO instance touched. This is a second,
|
|
633
|
-
* independent line of defense behind the call-site filter
|
|
634
|
-
* (`play-mode.ts`'s HMR handlers now filter to `value.prototype
|
|
635
|
-
* instanceof GameComponent` before ever calling this) — belt AND
|
|
636
|
-
* suspenders, so a future caller that forgets to filter still can't
|
|
637
|
-
* corrupt a live instance.
|
|
638
|
-
*
|
|
639
|
-
* `opts.warnOnMiss` (default `true`, §7.1-4/probe2b): the multi-world HMR
|
|
640
|
-
* fan-out (`play-mode.ts`'s `hotSwapAcrossWorlds`) calls this once per
|
|
641
|
-
* first-party world and must not let a per-world zero-match here look
|
|
642
|
-
* like "renamed class" when the name simply lives in a DIFFERENT world's
|
|
643
|
-
* manager — the fan-out helper suppresses this method's own warning
|
|
644
|
-
* (`{ warnOnMiss: false }`) and emits the ONE real warning itself only
|
|
645
|
-
* when the total across every world is zero. Every other existing caller
|
|
646
|
-
* (`applyBrowserComponentEdit`, the dev HMR demos, every hotSwap unit
|
|
647
|
-
* test) omits `opts` and keeps today's per-call warn-on-miss behavior
|
|
648
|
-
* unchanged. Returns the number of live instances swapped, so a caller
|
|
649
|
-
* that needs to aggregate across several managers (the fan-out helper)
|
|
650
|
-
* can sum it without re-deriving it from a side-channel.
|
|
651
|
-
*/
|
|
652
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the GameComponent-subclass guard (§7.1-5) and the warnOnMiss option are both load-bearing early-exit branches ahead of the existing match loop — splitting them into separate helpers would obscure that hotSwap has exactly one guarded entry point
|
|
653
|
-
hotSwap(name, NewClass, opts) {
|
|
654
|
-
if (typeof NewClass !== 'function' || !(NewClass.prototype instanceof GameComponent)) {
|
|
655
|
-
// biome-ignore lint/suspicious/noConsole: structured, greppable — mirrors logHmrSwapMiss's own deliberate direct console.warn
|
|
656
|
-
console.warn(`[component-manager] hotSwap("${name}"): refused — ` +
|
|
657
|
-
`"${NewClass?.name ?? String(NewClass)}" does not ` +
|
|
658
|
-
'extend GameComponent (no GameComponent in its prototype chain), so no live instance ' +
|
|
659
|
-
'was touched. This guards against a plain function/class export sharing a registry ' +
|
|
660
|
-
'key/name with a real component (§7.1-5, T5.4).');
|
|
661
|
-
return 0;
|
|
662
|
-
}
|
|
663
|
-
let matched = 0;
|
|
664
|
-
for (const [, instances] of byEntity) {
|
|
665
|
-
for (const inst of instances) {
|
|
666
|
-
if (swapOneIfMatched(inst, name, NewClass))
|
|
667
|
-
matched++;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
const warnOnMiss = opts?.warnOnMiss ?? true;
|
|
671
|
-
if (matched === 0 && warnOnMiss) {
|
|
672
|
-
logHmrSwapMiss({ key: name, reason: 'no-live-instance' });
|
|
673
|
-
}
|
|
674
|
-
return matched;
|
|
675
|
-
},
|
|
676
|
-
/** Dispose all components and clear all maps. */
|
|
677
|
-
clear() {
|
|
678
|
-
// Isolate dispose() exactly like performDetach: one throwing dispose
|
|
679
|
-
// must not abort the rest of teardown (the remaining instances still
|
|
680
|
-
// get dispose()'d, and the maps/state below still get reset). A
|
|
681
|
-
// teardown path that aborts partway through is unrecoverable (a retry
|
|
682
|
-
// is a no-op since byEntity etc. are never cleared) — see the probe5
|
|
683
|
-
// "dispose-abort" finding this closes.
|
|
684
|
-
for (const [, instances] of byEntity) {
|
|
685
|
-
for (const inst of instances) {
|
|
686
|
-
try {
|
|
687
|
-
inst.dispose?.(ctx);
|
|
688
|
-
}
|
|
689
|
-
catch (err) {
|
|
690
|
-
console.error(`[component-manager] ${inst.constructor.name}.dispose() threw:`, err);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
byEntity.clear();
|
|
695
|
-
for (const phase of PHASE_ORDER) {
|
|
696
|
-
byPhase.set(phase, []);
|
|
697
|
-
}
|
|
698
|
-
pendingInit.clear();
|
|
699
|
-
initPromises.clear();
|
|
700
|
-
pendingReinit.clear();
|
|
701
|
-
pendingAttach.length = 0;
|
|
702
|
-
pendingDetach.length = 0;
|
|
703
|
-
detachedThisTick.clear();
|
|
704
|
-
},
|
|
705
|
-
};
|
|
706
|
-
}
|