@vgai/engine 0.5.21 → 0.5.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/adapter/adapter-module.d.ts.map +1 -1
- package/dist/adapter/adapter-module.js +2 -4
- package/dist/adapter/adapter-surface.d.ts +1 -1
- package/dist/adapter/adapter-surface.js +1 -1
- package/dist/adapter/authoring.d.ts +19 -21
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +2 -2
- package/dist/adapter/binding.d.ts +341 -0
- package/dist/adapter/binding.d.ts.map +1 -0
- package/dist/adapter/binding.js +148 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -1
- package/dist/adapter/colyseus-networking-adapter.js +23 -4
- package/dist/adapter/entrypoint-selection-readers.d.ts +24 -0
- package/dist/adapter/entrypoint-selection-readers.d.ts.map +1 -0
- package/dist/adapter/entrypoint-selection-readers.js +63 -0
- package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -1
- package/dist/adapter/entrypoint-selection-source.js +1 -44
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +1 -52
- package/dist/adapter/host-context.d.ts +46 -15
- package/dist/adapter/host-context.d.ts.map +1 -1
- package/dist/adapter/host-context.js +2 -9
- package/dist/adapter/index.d.ts +11 -12
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +9 -10
- package/dist/adapter/ingest/game-contract.d.ts +0 -3
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +0 -3
- package/dist/adapter/ingest/scene-capture.d.ts +19 -23
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +143 -17
- package/dist/adapter/ingest/visible-capture-window.d.ts +34 -0
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
- package/dist/adapter/manifest-interpreter.d.ts +59 -0
- package/dist/adapter/manifest-interpreter.d.ts.map +1 -0
- package/dist/adapter/manifest-interpreter.js +50 -0
- package/dist/adapter/native-debug-module.d.ts +71 -3
- package/dist/adapter/native-debug-module.d.ts.map +1 -1
- package/dist/adapter/native-debug-module.js +146 -10
- package/dist/adapter/rapier-physics-adapter.d.ts +31 -1
- package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -1
- package/dist/adapter/rapier-physics-adapter.js +50 -36
- package/dist/adapter/renderer-config.d.ts +3 -3
- package/dist/adapter/renderer-config.js +3 -3
- package/dist/adapter/root-adapter.d.ts +7 -7
- package/dist/adapter/root-adapter.js +4 -4
- package/dist/adapter/system-adapter.d.ts +45 -7
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-seam-contract.d.ts +3 -3
- package/dist/adapter/system-seam-contract.js +3 -3
- package/dist/adapter/system-slot.d.ts +168 -0
- package/dist/adapter/system-slot.d.ts.map +1 -0
- package/dist/adapter/system-slot.js +229 -0
- package/dist/adapter/transform.d.ts +11 -1
- package/dist/adapter/transform.d.ts.map +1 -1
- package/dist/ai/navigation.d.ts +17 -1
- package/dist/ai/navigation.d.ts.map +1 -1
- package/dist/ai/navigation.js +26 -5
- package/dist/animation/anim-graph-types.d.ts +11 -3
- package/dist/animation/anim-graph-types.d.ts.map +1 -1
- package/dist/animation/theatre-clock-binding.d.ts +4 -4
- package/dist/animation/theatre-clock-binding.js +4 -4
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +2 -2
- package/dist/animation/xstate-animation-meta.d.ts +13 -0
- package/dist/animation/xstate-animation-meta.d.ts.map +1 -1
- package/dist/animation/xstate-animation-meta.js +14 -0
- package/dist/asset-formats/index.d.ts +4 -7
- package/dist/asset-formats/index.d.ts.map +1 -1
- package/dist/asset-formats/index.js +4 -7
- package/dist/asset-formats/render-env.d.ts +5 -8
- package/dist/asset-formats/render-env.d.ts.map +1 -1
- package/dist/asset-formats/render-env.js +3 -6
- package/dist/asset-parse-error.d.ts +2 -5
- package/dist/asset-parse-error.d.ts.map +1 -1
- package/dist/asset-parse-error.js +2 -5
- package/dist/audio/bus-mixer.d.ts +101 -0
- package/dist/audio/bus-mixer.d.ts.map +1 -0
- package/dist/audio/bus-mixer.js +115 -0
- package/dist/canvas-react/index.d.ts +5 -12
- package/dist/canvas-react/index.d.ts.map +1 -1
- package/dist/canvas-react/index.js +5 -12
- package/dist/canvas-react/pixi-react-root-factory.d.ts +34 -21
- package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-root-factory.js +274 -36
- package/dist/core/countdown-timer.d.ts +118 -0
- package/dist/core/countdown-timer.d.ts.map +1 -0
- package/dist/core/countdown-timer.js +137 -0
- package/dist/core/deferred-commands.d.ts +127 -0
- package/dist/core/deferred-commands.d.ts.map +1 -0
- package/dist/core/deferred-commands.js +132 -0
- package/dist/core/frame-pacing.d.ts +1 -1
- package/dist/core/frame-pacing.js +2 -2
- package/dist/core/game-loop.d.ts +1 -3
- package/dist/core/game-loop.d.ts.map +1 -1
- package/dist/core/game-loop.js +1 -3
- package/dist/core/seeded-random.d.ts +1 -1
- package/dist/core/sim-clock.d.ts +19 -8
- package/dist/core/sim-clock.d.ts.map +1 -1
- package/dist/core/sim-clock.js +18 -7
- package/dist/core/types.d.ts +1 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/defaults.d.ts +6 -14
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +6 -14
- package/dist/dev/instruments.d.ts +38 -11
- package/dist/dev/instruments.d.ts.map +1 -1
- package/dist/dev/instruments.js +81 -11
- package/dist/dev/register-render-vitals.d.ts +1 -4
- package/dist/dev/register-render-vitals.d.ts.map +1 -1
- package/dist/dev/register-render-vitals.js +1 -4
- package/dist/dev/render-debug-adapter.d.ts +2 -3
- package/dist/dev/render-debug-adapter.d.ts.map +1 -1
- package/dist/dev/render-debug-adapter.js +2 -3
- package/dist/dev/render-vitals.d.ts +3 -3
- package/dist/dev/render-vitals.js +3 -3
- package/dist/dev/webgl-frame-capture.d.ts +1 -1
- package/dist/dev/webgl-frame-capture.js +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/input/host-pointer.d.ts +122 -0
- package/dist/input/host-pointer.d.ts.map +1 -0
- package/dist/input/host-pointer.js +160 -0
- package/dist/input/input-manager.d.ts +33 -5
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +48 -13
- package/dist/input/rebind-controller.d.ts.map +1 -1
- package/dist/input/rebind-controller.js +6 -8
- package/dist/input/schema.d.ts.map +1 -1
- package/dist/input/schema.js +56 -28
- package/dist/manifest/filename.d.ts.map +1 -1
- package/dist/manifest/filename.js +8 -12
- package/dist/manifest/load.d.ts +10 -10
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +9 -6
- package/dist/manifest/schema.d.ts +17 -21
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +45 -35
- package/dist/physics/collider-dimensions.d.ts +8 -12
- package/dist/physics/collider-dimensions.d.ts.map +1 -1
- package/dist/physics/collider-dimensions.js +8 -12
- package/dist/pixi/scene-capture.d.ts +11 -13
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/react/world-state.d.ts +1 -1
- package/dist/react/world-state.d.ts.map +1 -1
- package/dist/render/directional-shadow-fit.d.ts +88 -0
- package/dist/render/directional-shadow-fit.d.ts.map +1 -0
- package/dist/render/directional-shadow-fit.js +133 -0
- package/dist/render/environment-capture.d.ts +68 -0
- package/dist/render/environment-capture.d.ts.map +1 -0
- package/dist/render/environment-capture.js +90 -0
- package/dist/render/ibl-override-material.d.ts +97 -0
- package/dist/render/ibl-override-material.d.ts.map +1 -0
- package/dist/render/ibl-override-material.js +114 -0
- package/dist/render/light-camera-factory.d.ts +5 -7
- package/dist/render/light-camera-factory.d.ts.map +1 -1
- package/dist/render/light-camera-factory.js +8 -11
- package/dist/render/material-factory.js +1 -1
- package/dist/render/render-batch-system.d.ts.map +1 -1
- package/dist/render/render-features.d.ts +17 -6
- package/dist/render/render-features.d.ts.map +1 -1
- package/dist/render/render-features.js +0 -28
- package/dist/runtime/create-runtime.d.ts +34 -25
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +218 -93
- package/dist/runtime/debug-registry.d.ts +77 -8
- package/dist/runtime/debug-registry.d.ts.map +1 -1
- package/dist/runtime/debug-registry.js +3 -3
- package/dist/runtime/dev-build.d.ts +2 -2
- package/dist/runtime/dev-build.js +2 -2
- package/dist/runtime/game-input-seams.d.ts +51 -0
- package/dist/runtime/game-input-seams.d.ts.map +1 -0
- package/dist/runtime/game-input-seams.js +86 -0
- package/dist/runtime/game.d.ts +104 -40
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +119 -61
- package/dist/runtime/mount-game.d.ts +0 -16
- package/dist/runtime/mount-game.d.ts.map +1 -1
- package/dist/runtime/mount-game.js +26 -41
- package/dist/runtime/mount-manifest.d.ts +22 -14
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +22 -20
- package/dist/runtime/playtest.d.ts +22 -0
- package/dist/runtime/playtest.d.ts.map +1 -0
- package/dist/runtime/playtest.js +12 -0
- package/dist/runtime/state-bridge.d.ts +2 -3
- package/dist/runtime/state-bridge.d.ts.map +1 -1
- package/dist/runtime/state-bridge.js +1 -0
- package/dist/setup/setup-audio.d.ts +12 -0
- package/dist/setup/setup-audio.d.ts.map +1 -1
- package/dist/setup/setup-audio.js +9 -10
- package/dist/setup/setup-renderer.d.ts +2 -25
- package/dist/setup/setup-renderer.d.ts.map +1 -1
- package/dist/setup/setup-renderer.js +2 -31
- package/dist/world3d-react/index.d.ts +15 -25
- package/dist/world3d-react/index.d.ts.map +1 -1
- package/dist/world3d-react/index.js +15 -25
- package/dist/world3d-react/r3f-root-factory.d.ts +28 -34
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
- package/dist/world3d-react/r3f-root-factory.js +403 -57
- package/dist/world3d-react/rapier-physics-bridge.d.ts +30 -17
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +143 -58
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +16 -14
- package/schemas/vgai-project.schema.json +35 -26
- package/src/adapter/adapter-module.ts +2 -4
- package/src/adapter/adapter-surface.ts +1 -1
- package/src/adapter/authoring.ts +19 -21
- package/src/adapter/binding.ts +468 -0
- package/src/adapter/colyseus-networking-adapter.ts +20 -5
- package/src/adapter/entrypoint-selection-readers.ts +66 -0
- package/src/adapter/entrypoint-selection-source.ts +1 -40
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +1 -48
- package/src/adapter/host-context.ts +45 -15
- package/src/adapter/index.ts +23 -11
- package/src/adapter/ingest/game-contract.ts +0 -3
- package/src/adapter/ingest/scene-capture.ts +190 -39
- package/src/adapter/ingest/visible-capture-window.ts +35 -0
- package/src/adapter/manifest-interpreter.ts +64 -0
- package/src/adapter/native-debug-module.ts +248 -15
- package/src/adapter/rapier-physics-adapter.ts +76 -20
- package/src/adapter/renderer-config.ts +3 -3
- package/src/adapter/root-adapter.ts +7 -7
- package/src/adapter/system-adapter.ts +43 -8
- package/src/adapter/system-seam-contract.ts +3 -3
- package/src/adapter/system-slot.ts +291 -0
- package/src/adapter/transform.ts +18 -1
- package/src/ai/navigation.ts +30 -3
- package/src/animation/anim-graph-types.ts +11 -3
- package/src/animation/theatre-clock-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +2 -2
- package/src/animation/xstate-animation-meta.ts +16 -0
- package/src/asset-formats/index.ts +4 -7
- package/src/asset-formats/render-env.ts +3 -6
- package/src/asset-parse-error.ts +2 -5
- package/src/audio/bus-mixer.ts +161 -0
- package/src/canvas-react/index.ts +5 -21
- package/src/canvas-react/pixi-react-root-factory.tsx +301 -39
- package/src/core/countdown-timer.ts +188 -0
- package/src/core/deferred-commands.ts +174 -0
- package/src/core/frame-pacing.ts +2 -2
- package/src/core/game-loop.ts +1 -3
- package/src/core/seeded-random.ts +1 -1
- package/src/core/sim-clock.ts +19 -8
- package/src/core/types.ts +1 -3
- package/src/defaults.ts +6 -14
- package/src/dev/instruments.ts +108 -11
- package/src/dev/register-render-vitals.ts +1 -4
- package/src/dev/render-debug-adapter.ts +2 -3
- package/src/dev/render-vitals.ts +3 -3
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/index.ts +2 -5
- package/src/input/host-pointer.ts +230 -0
- package/src/input/input-manager.ts +49 -13
- package/src/input/input-types.ts +1 -1
- package/src/input/rebind-controller.ts +6 -8
- package/src/input/schema.ts +216 -188
- package/src/manifest/filename.ts +8 -12
- package/src/manifest/load.ts +17 -13
- package/src/manifest/schema.ts +55 -47
- package/src/physics/collider-dimensions.ts +8 -12
- package/src/pixi/scene-capture.ts +12 -15
- package/src/react/world-state.tsx +1 -1
- package/src/render/directional-shadow-fit.ts +156 -0
- package/src/render/environment-capture.ts +102 -0
- package/src/render/ibl-override-material.ts +170 -0
- package/src/render/light-camera-factory.ts +8 -11
- package/src/render/material-factory.ts +1 -1
- package/src/render/render-batch-system.ts +14 -6
- package/src/render/render-features.ts +17 -35
- package/src/runtime/create-runtime.ts +270 -108
- package/src/runtime/debug-registry.ts +84 -8
- package/src/runtime/dev-build.ts +2 -2
- package/src/runtime/game-input-seams.ts +108 -0
- package/src/runtime/game.ts +231 -85
- package/src/runtime/mount-game.ts +31 -57
- package/src/runtime/mount-manifest.ts +47 -36
- package/src/runtime/playtest.ts +22 -0
- package/src/runtime/state-bridge.ts +3 -3
- package/src/setup/setup-audio.ts +21 -11
- package/src/setup/setup-renderer.ts +2 -61
- package/src/world3d-react/index.ts +15 -31
- package/src/world3d-react/r3f-root-factory.tsx +451 -60
- package/src/world3d-react/rapier-physics-bridge.tsx +154 -58
- package/dist/adapter/setup-three-root-adapter.d.ts +0 -86
- package/dist/adapter/setup-three-root-adapter.d.ts.map +0 -1
- package/dist/adapter/setup-three-root-adapter.js +0 -908
- 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 -31
- package/dist/asset-registry.d.ts +0 -38
- package/dist/asset-registry.d.ts.map +0 -1
- package/dist/asset-registry.js +0 -66
- package/dist/canvas-react/engine-bridge.d.ts +0 -45
- package/dist/canvas-react/engine-bridge.d.ts.map +0 -1
- package/dist/canvas-react/engine-bridge.js +0 -45
- package/dist/canvas-react/pixi-react-adapter.d.ts +0 -77
- package/dist/canvas-react/pixi-react-adapter.d.ts.map +0 -1
- package/dist/canvas-react/pixi-react-adapter.js +0 -294
- package/dist/canvas-react/world-context.d.ts +0 -98
- package/dist/canvas-react/world-context.d.ts.map +0 -1
- package/dist/canvas-react/world-context.js +0 -173
- 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/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/lod.d.ts +0 -13
- package/dist/render/lod.d.ts.map +0 -1
- package/dist/render/lod.js +0 -16
- package/dist/runtime/types.d.ts +0 -303
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- package/dist/world3d-react/engine-bridge.d.ts +0 -47
- package/dist/world3d-react/engine-bridge.d.ts.map +0 -1
- package/dist/world3d-react/engine-bridge.js +0 -73
- package/dist/world3d-react/r3f-adapter.d.ts +0 -58
- package/dist/world3d-react/r3f-adapter.d.ts.map +0 -1
- package/dist/world3d-react/r3f-adapter.js +0 -634
- package/dist/world3d-react/world-context.d.ts +0 -182
- package/dist/world3d-react/world-context.d.ts.map +0 -1
- package/dist/world3d-react/world-context.js +0 -235
- package/src/adapter/setup-three-root-adapter.ts +0 -1032
- package/src/animation/clip-map.ts +0 -34
- package/src/asset-registry.ts +0 -89
- package/src/canvas-react/engine-bridge.ts +0 -59
- package/src/canvas-react/pixi-react-adapter.tsx +0 -356
- package/src/canvas-react/world-context.ts +0 -253
- package/src/dev/debug-draw.ts +0 -80
- package/src/render/auto-batcher.ts +0 -168
- package/src/render/lod.ts +0 -17
- package/src/runtime/types.ts +0 -328
- package/src/world3d-react/engine-bridge.ts +0 -86
- package/src/world3d-react/r3f-adapter.tsx +0 -717
- package/src/world3d-react/world-context.ts +0 -358
|
@@ -1,54 +1,55 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { assertNever } from '../adapter/adapter-surface';
|
|
3
|
+
import { createRootBinding } from '../adapter/binding';
|
|
3
4
|
import { createAssetCache } from '../assets';
|
|
4
5
|
import { createGameLoop } from '../core/game-loop';
|
|
5
6
|
import { createHostRenderer } from '../setup/setup-renderer';
|
|
7
|
+
import { getDebugRegistry } from './debug-registry';
|
|
6
8
|
// TYPE-ONLY: `create-runtime.ts` must never value-import `pixi.js` — a canvas
|
|
7
9
|
// `RootMountSpec`'s `adapter` is supplied ALREADY-CONSTRUCTED by the caller, so
|
|
8
10
|
// the host only ever needs the seam's types.
|
|
9
|
-
import { createGame, createRootInstance,
|
|
11
|
+
import { createGame, createRootInstance, } from './game';
|
|
10
12
|
import { createInputRouter, stackOrder } from './input-router';
|
|
11
13
|
import { installRenderControlHarness, isRenderModeRequested } from './render-control';
|
|
12
|
-
/**
|
|
13
|
-
* Register a three world for a freshly-mounted game onto the Game shell. Shared
|
|
14
|
-
* by `createGameRuntime` and every headless test harness
|
|
15
|
-
* (`test/game-root.test.ts`, `test/frame-order.test.ts`,
|
|
16
|
-
* `test/game-two-roots.test.ts`) so there is exactly one code path for this
|
|
17
|
-
* wiring. `physics`/`collisions`/`camera`/`frame` are populated from the mount's
|
|
18
|
-
* first-party `GameContext`/ `MountedSetupThreeRoot` when available, left
|
|
19
|
-
* `undefined` otherwise (an external adapter's mount has none of these
|
|
20
|
-
* first-party handles — `frame` in particular is what makes
|
|
21
|
-
* `GameInternal.runFrame` fall back to a single opaque
|
|
22
|
-
* `mounted.update` call per substep for such a world).
|
|
23
|
-
*/
|
|
24
|
-
function firstPartyExtras(mounted) {
|
|
25
|
-
if (!isFirstPartyMounted(mounted))
|
|
26
|
-
return {};
|
|
27
|
-
return {
|
|
28
|
-
physics: mounted.ctx.physics,
|
|
29
|
-
collisions: mounted.ctx.collisions,
|
|
30
|
-
camera: mounted.ctx.camera,
|
|
31
|
-
frame: mounted.frame,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
14
|
/**
|
|
35
15
|
* Register a three world onto the Game shell. `opts.id` defaults to
|
|
36
16
|
* `'main'`; multi-root callers pass an explicit id. This is the one
|
|
37
17
|
* registration path for a three world, whatever its id.
|
|
38
18
|
*/
|
|
39
19
|
export function registerThreeRoot(game, adapter, mounted, opts) {
|
|
20
|
+
const pausable = opts?.pausable ?? true;
|
|
40
21
|
const world = createRootInstance({
|
|
41
22
|
id: opts?.id ?? 'main',
|
|
42
23
|
kind: 'three',
|
|
43
|
-
pausable
|
|
24
|
+
pausable,
|
|
44
25
|
adapter,
|
|
45
26
|
mounted,
|
|
46
27
|
scene: mounted.scene,
|
|
47
|
-
|
|
28
|
+
binding: bindRoot(game, opts?.declaration, {
|
|
29
|
+
surface: 'three',
|
|
30
|
+
adapter: adapter,
|
|
31
|
+
}, mounted, pausable),
|
|
48
32
|
});
|
|
49
33
|
game.registerRoot(world);
|
|
50
34
|
return world;
|
|
51
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Complete the root's binding at the first moment it CAN be completed: the
|
|
38
|
+
* declaration is what the resolver knew, `mounted`/`adapter`/`pausable` are
|
|
39
|
+
* what mounting produced, and the debug registry is the game's own. Nothing
|
|
40
|
+
* here loads or fabricates — see `createRootBinding`.
|
|
41
|
+
*/
|
|
42
|
+
function bindRoot(game, declaration, adapter, mounted, pausable) {
|
|
43
|
+
if (!declaration)
|
|
44
|
+
return null;
|
|
45
|
+
return createRootBinding({
|
|
46
|
+
...declaration,
|
|
47
|
+
adapter,
|
|
48
|
+
mounted,
|
|
49
|
+
pausable,
|
|
50
|
+
debugRegistry: getDebugRegistry(game),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
52
53
|
/**
|
|
53
54
|
* Register a canvas world onto the Game shell — the pixi analog of
|
|
54
55
|
* {@link registerThreeRoot}. `adapter` is the seam's own
|
|
@@ -58,13 +59,15 @@ export function registerThreeRoot(game, adapter, mounted, opts) {
|
|
|
58
59
|
*/
|
|
59
60
|
export function registerPixiRoot(game, adapter, mounted, opts) {
|
|
60
61
|
const id = opts?.id ?? 'main';
|
|
62
|
+
const pausable = opts?.pausable ?? true;
|
|
61
63
|
const world = createRootInstance({
|
|
62
64
|
id,
|
|
63
65
|
kind: 'canvas',
|
|
64
|
-
pausable
|
|
66
|
+
pausable,
|
|
65
67
|
adapter,
|
|
66
68
|
mounted,
|
|
67
69
|
stage: mounted.stage,
|
|
70
|
+
binding: bindRoot(game, opts?.declaration, { surface: 'canvas', adapter }, mounted, pausable),
|
|
68
71
|
});
|
|
69
72
|
game.registerRoot(world);
|
|
70
73
|
return world;
|
|
@@ -81,13 +84,15 @@ export function registerPixiRoot(game, adapter, mounted, opts) {
|
|
|
81
84
|
* needs `.id`, so this passes through with zero cast.
|
|
82
85
|
*/
|
|
83
86
|
export function registerReactRoot(game, adapter, mounted, container, opts) {
|
|
87
|
+
const pausable = opts?.pausable ?? true;
|
|
84
88
|
const world = createRootInstance({
|
|
85
89
|
id: opts?.id ?? 'main',
|
|
86
90
|
kind: 'dom',
|
|
87
|
-
pausable
|
|
91
|
+
pausable,
|
|
88
92
|
adapter,
|
|
89
93
|
mounted,
|
|
90
94
|
container,
|
|
95
|
+
binding: bindRoot(game, opts?.declaration, { surface: 'dom', adapter }, mounted, pausable),
|
|
91
96
|
});
|
|
92
97
|
game.registerRoot(world);
|
|
93
98
|
return world;
|
|
@@ -106,9 +111,8 @@ export async function createGameRuntime(config) {
|
|
|
106
111
|
// Universal root host
|
|
107
112
|
// ---------------------------------------------------------------------------
|
|
108
113
|
/** A stand-in `THREE.WebGLRenderer` for headless (`headless:true`) three
|
|
109
|
-
* roots
|
|
110
|
-
*
|
|
111
|
-
* mount never calls a render-phase method on `host.renderer` at all, so
|
|
114
|
+
* roots: a headless mount never calls a render-phase method on
|
|
115
|
+
* `host.renderer` at all, so
|
|
112
116
|
* this only needs to satisfy the handful of calls THIS file itself makes
|
|
113
117
|
* (`setPixelRatio`/`setClearColor`/`setSize` at mount, `dispose`/
|
|
114
118
|
* `forceContextLoss` at teardown) — never a real GL call. Node conformance
|
|
@@ -122,6 +126,46 @@ function createHeadlessRendererStub() {
|
|
|
122
126
|
forceContextLoss() { },
|
|
123
127
|
};
|
|
124
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Reclaim ONE mounted root: its adapter, the renderer this file constructed for
|
|
131
|
+
* it (including the WebGL context — a browser keeps only a handful alive, so
|
|
132
|
+
* `dispose()` alone is not enough), and its stacked surface element.
|
|
133
|
+
*
|
|
134
|
+
* ONE body, TWO callers, deliberately: the session's `fullCleanup` (a normal
|
|
135
|
+
* stop) and `mountAllRootSpecs`'s partial-mount ROLLBACK (a later root's mount
|
|
136
|
+
* rejected, so the roots that already mounted must not survive the rejection).
|
|
137
|
+
* Those two must never drift — a rollback that reclaims less than a stop is how
|
|
138
|
+
* a failed play leaves a live renderer and a lost GL context behind with no
|
|
139
|
+
* handle anywhere able to reach it.
|
|
140
|
+
*
|
|
141
|
+
* Throws only what the adapter's own `dispose()` throws; every caller wraps it
|
|
142
|
+
* per entry so one bad root cannot abort the rest of the teardown.
|
|
143
|
+
*/
|
|
144
|
+
function disposeMountedRoot(entry, container) {
|
|
145
|
+
try {
|
|
146
|
+
entry.mounted.dispose();
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
// Renderer + surface release run even when the adapter's dispose threw:
|
|
150
|
+
// the GL context is the scarce resource, and the caller still sees the
|
|
151
|
+
// error (this rethrows through the `finally`).
|
|
152
|
+
entry.renderer?.dispose();
|
|
153
|
+
entry.renderer?.forceContextLoss();
|
|
154
|
+
// Remove from the element's CURRENT parent, not the mount-time
|
|
155
|
+
// `container`: the editor's Game panel re-parents the live surfaces
|
|
156
|
+
// when its mount element swaps (fill <-> device preset, W2c), and
|
|
157
|
+
// `container.removeChild` would throw NotFoundError after such a move.
|
|
158
|
+
// `container` remains the fallback for hosts whose element stand-ins
|
|
159
|
+
// never wire `parentNode` (headless unit fixtures); an element already
|
|
160
|
+
// detached by such a host is a no-op via the catch.
|
|
161
|
+
try {
|
|
162
|
+
(entry.element.parentNode ?? container).removeChild(entry.element);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
/* already detached — nothing to remove */
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
125
169
|
/** Mount one three `RootMountSpec` (canvas + renderer construction, per
|
|
126
170
|
* D5 §1/§4, then `registerThreeRoot`). Split out of
|
|
127
171
|
* `createRootsGameRuntime` purely to keep that function's own branching
|
|
@@ -175,7 +219,11 @@ async function mountOneThreeRoot(spec, ctx) {
|
|
|
175
219
|
game,
|
|
176
220
|
};
|
|
177
221
|
const mounted = await spec.adapter.mount(host);
|
|
178
|
-
registerThreeRoot(game, spec.adapter, mounted, {
|
|
222
|
+
registerThreeRoot(game, spec.adapter, mounted, {
|
|
223
|
+
id: spec.id,
|
|
224
|
+
pausable: spec.pausable,
|
|
225
|
+
declaration: spec.declaration,
|
|
226
|
+
});
|
|
179
227
|
return {
|
|
180
228
|
mountedEntry: { id: spec.id, kind: 'three', element: canvas, mounted, renderer },
|
|
181
229
|
routerEntry: { id: spec.id, zOrder: spec.zOrder ?? 0, canvas, hitTest: spec.hitTest },
|
|
@@ -186,7 +234,7 @@ async function mountOneThreeRoot(spec, ctx) {
|
|
|
186
234
|
* `mountOneThreeRoot` above.
|
|
187
235
|
*
|
|
188
236
|
* Unlike `mountOneThreeRoot`, this needs no explicit `canvas.style.width`/
|
|
189
|
-
* `.height` re-assertion
|
|
237
|
+
* `.height` re-assertion. The canvas surface constructs its
|
|
190
238
|
* `Application` with `autoDensity: true`, which makes PIXI ITSELF re-stamp
|
|
191
239
|
* `canvas.style.width`/`.height` (real CSS px, matching the LOGICAL
|
|
192
240
|
* width/height passed to `resize()`) on every `app.renderer.resize()` call —
|
|
@@ -215,6 +263,7 @@ async function mountOnePixiRoot(spec, ctx) {
|
|
|
215
263
|
registerPixiRoot(game, spec.adapter, mounted, {
|
|
216
264
|
id: spec.id,
|
|
217
265
|
pausable: spec.pausable,
|
|
266
|
+
declaration: spec.declaration,
|
|
218
267
|
});
|
|
219
268
|
return {
|
|
220
269
|
mountedEntry: {
|
|
@@ -271,6 +320,7 @@ layer) {
|
|
|
271
320
|
registerReactRoot(game, spec.adapter, mounted, layer, {
|
|
272
321
|
id: spec.id,
|
|
273
322
|
pausable: spec.pausable,
|
|
323
|
+
declaration: spec.declaration,
|
|
274
324
|
});
|
|
275
325
|
return {
|
|
276
326
|
mountedEntry: {
|
|
@@ -289,8 +339,8 @@ layer) {
|
|
|
289
339
|
* per D5 §1, z-order/ties exactly matching
|
|
290
340
|
* `manifest/load.ts`'s sort, ONE `Game`, and registers every world onto it
|
|
291
341
|
* via `registerThreeRoot`/`registerPixiRoot`/`registerReactRoot` — the
|
|
292
|
-
* SAME wiring `test/
|
|
293
|
-
* three/
|
|
342
|
+
* SAME wiring `test/create-runtime-worlds.test.ts` proves for the
|
|
343
|
+
* three/canvas pair. Worlds MOUNT in `roots` ARRAY order ("manifest
|
|
294
344
|
* declaration order" — the frame/registration axis), independent of
|
|
295
345
|
* `zOrder` (the canvas-stacking/rendering axis) — the two orders can differ
|
|
296
346
|
* and both are honored correctly.
|
|
@@ -344,37 +394,60 @@ function installDefaultRootDevGlobals(game) {
|
|
|
344
394
|
* comment).
|
|
345
395
|
*/
|
|
346
396
|
async function mountAllRootSpecs(mountSpecs, bottomId, inputs) {
|
|
347
|
-
const { surfacesById, ...shared } = inputs;
|
|
397
|
+
const { surfacesById, container, ...shared } = inputs;
|
|
348
398
|
const mountedEntries = [];
|
|
349
399
|
const routerEntries = [];
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
400
|
+
try {
|
|
401
|
+
for (const spec of mountSpecs) {
|
|
402
|
+
const isBottom = spec.id === bottomId;
|
|
403
|
+
if (spec.kind === 'dom') {
|
|
404
|
+
const layer = surfacesById.get(spec.id);
|
|
405
|
+
const { mountedEntry } = await mountOneReactRoot(spec, shared.game, layer);
|
|
406
|
+
mountedEntries.push(mountedEntry);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const canvas = surfacesById.get(spec.id);
|
|
410
|
+
const oneCtx = { ...shared, canvas, isBottom };
|
|
411
|
+
let mountedEntry;
|
|
412
|
+
let routerEntry;
|
|
413
|
+
if (spec.kind === 'three') {
|
|
414
|
+
({ mountedEntry, routerEntry } = await mountOneThreeRoot(spec, oneCtx));
|
|
415
|
+
}
|
|
416
|
+
else if (spec.kind === 'canvas') {
|
|
417
|
+
({ mountedEntry, routerEntry } = await mountOnePixiRoot(spec, oneCtx));
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
// Exhaustiveness guard (§7.4-2): 'react' was already handled by the
|
|
421
|
+
// early `continue` above, so only a hypothetical 4th `AdapterSurface` can
|
|
422
|
+
// reach here — fail loudly rather than silently defaulting. `spec`
|
|
423
|
+
// itself (not `spec.kind`) is what TS has narrowed to `never`, since
|
|
424
|
+
// `RootMountSpec` is a discriminated union at the object level.
|
|
425
|
+
assertNever(spec, 'create-runtime mount loop');
|
|
426
|
+
}
|
|
355
427
|
mountedEntries.push(mountedEntry);
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
const canvas = surfacesById.get(spec.id);
|
|
359
|
-
const oneCtx = { ...shared, canvas, isBottom };
|
|
360
|
-
let mountedEntry;
|
|
361
|
-
let routerEntry;
|
|
362
|
-
if (spec.kind === 'three') {
|
|
363
|
-
({ mountedEntry, routerEntry } = await mountOneThreeRoot(spec, oneCtx));
|
|
428
|
+
routerEntries.push(routerEntry);
|
|
364
429
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
// PARTIAL MOUNT ROLLBACK. Roots mount sequentially, so a rejection from
|
|
433
|
+
// root N leaves roots 1..N-1 fully live — renderer, WebGL context, R3F
|
|
434
|
+
// tree, adapter registrations — and NOTHING ever gets a handle to them:
|
|
435
|
+
// this function throws instead of returning, so no `GameSession` (and
|
|
436
|
+
// therefore no `stop()`) is ever constructed. Reclaim them here, in
|
|
437
|
+
// REVERSE mount order (the inverse of the order they were built in), then
|
|
438
|
+
// rethrow the ORIGINAL error — the mount failure is what the caller must
|
|
439
|
+
// see, never a teardown error raised while cleaning up after it.
|
|
440
|
+
for (let i = mountedEntries.length - 1; i >= 0; i--) {
|
|
441
|
+
const entry = mountedEntries[i];
|
|
442
|
+
try {
|
|
443
|
+
disposeMountedRoot(entry, container);
|
|
444
|
+
}
|
|
445
|
+
catch (disposeErr) {
|
|
446
|
+
// biome-ignore lint/suspicious/noConsole: a rollback failure must be visible; the original mount error is still what we rethrow
|
|
447
|
+
console.error(`createGameRuntime: rolling back root "${entry.id}" after a mount failure threw:`, disposeErr);
|
|
448
|
+
}
|
|
375
449
|
}
|
|
376
|
-
|
|
377
|
-
routerEntries.push(routerEntry);
|
|
450
|
+
throw err;
|
|
378
451
|
}
|
|
379
452
|
return { mountedEntries, routerEntries };
|
|
380
453
|
}
|
|
@@ -424,6 +497,34 @@ function installSessionRenderHarness(game, location, targetOverride) {
|
|
|
424
497
|
delete target['__vgaiRender'];
|
|
425
498
|
};
|
|
426
499
|
}
|
|
500
|
+
/**
|
|
501
|
+
* Reclaim what `createRootsGameRuntime` built for ITSELF when the mount
|
|
502
|
+
* rejects. `mountAllRootSpecs` has already rolled back every root that DID
|
|
503
|
+
* mount, so what is left is the Game shell (holding whatever registrations
|
|
504
|
+
* those roots made into it) and the stacked surface elements of the roots that
|
|
505
|
+
* never got to mount. Neither is reachable afterwards: the mount throws instead
|
|
506
|
+
* of returning, so no `GameSession` — and therefore no `stop()` — ever exists.
|
|
507
|
+
*
|
|
508
|
+
* Never throws: the MOUNT error is what the caller must see, so a failure while
|
|
509
|
+
* cleaning up after it is reported and swallowed.
|
|
510
|
+
*/
|
|
511
|
+
function reclaimAfterMountFailure(game, surfaces, container) {
|
|
512
|
+
try {
|
|
513
|
+
game.dispose();
|
|
514
|
+
}
|
|
515
|
+
catch (disposeErr) {
|
|
516
|
+
// biome-ignore lint/suspicious/noConsole: a failed rollback must be visible; the original mount error is what the caller gets
|
|
517
|
+
console.error('createGameRuntime: disposing the game after a mount failure threw:', disposeErr);
|
|
518
|
+
}
|
|
519
|
+
for (const surface of surfaces) {
|
|
520
|
+
try {
|
|
521
|
+
(surface.parentNode ?? container).removeChild(surface);
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
/* already detached by the per-root rollback */
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
427
528
|
async function createRootsGameRuntime(config) {
|
|
428
529
|
const { container, roots: specs, width, height, headless = false, seed, playtest } = config;
|
|
429
530
|
if (specs.length === 0) {
|
|
@@ -514,14 +615,14 @@ async function createRootsGameRuntime(config) {
|
|
|
514
615
|
// host's explicit `simulateSubsteps`/`renderOnce` calls — wall-clock time
|
|
515
616
|
// passing while a screenshot is taken must not move the world.
|
|
516
617
|
externalDrive: renderMode,
|
|
517
|
-
//
|
|
618
|
+
// Sim and presentation are wired to different callbacks here:
|
|
518
619
|
// `update` runs the gameplay phases once per consumed fixed substep with
|
|
519
620
|
// `preRender`/`render` withheld, and `render` runs those two once per real
|
|
520
621
|
// display frame with the interpolation alpha. Both closures are invoked
|
|
521
622
|
// only from the loop's own rAF arm, which `externalDrive` never arms — so
|
|
522
623
|
// a capture/offline-export page (`renderMode`) reaches neither, and its
|
|
523
624
|
// `simulateSubsteps` → `game.runFrame(fixedDt)` path stays frame-exact,
|
|
524
|
-
// rendering inside the substep
|
|
625
|
+
// rendering inside the substep.
|
|
525
626
|
update: (dt) => {
|
|
526
627
|
if (started)
|
|
527
628
|
game.runFrame(dt, { skipRenderPhases: true });
|
|
@@ -537,16 +638,25 @@ async function createRootsGameRuntime(config) {
|
|
|
537
638
|
// `createRootsGameRuntime`'s own cyclomatic complexity down (E4) — same
|
|
538
639
|
// reason `mountOneThreeRoot`/`mountOnePixiRoot` are already split out
|
|
539
640
|
// below.
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
641
|
+
let mounted;
|
|
642
|
+
try {
|
|
643
|
+
mounted = await mountAllRootSpecs(mountSpecs, bottomId, {
|
|
644
|
+
game,
|
|
645
|
+
surfacesById,
|
|
646
|
+
container,
|
|
647
|
+
w,
|
|
648
|
+
h,
|
|
649
|
+
dpr,
|
|
650
|
+
headless,
|
|
651
|
+
assets,
|
|
652
|
+
antialias: config.antialias,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
catch (err) {
|
|
656
|
+
reclaimAfterMountFailure(game, surfacesById.values(), container);
|
|
657
|
+
throw err;
|
|
658
|
+
}
|
|
659
|
+
const { mountedEntries, routerEntries } = mounted;
|
|
550
660
|
// --- Delegating input router (D5 §2a) ---
|
|
551
661
|
const router = createInputRouter(container, routerEntries);
|
|
552
662
|
started = true;
|
|
@@ -566,38 +676,53 @@ async function createRootsGameRuntime(config) {
|
|
|
566
676
|
resolveStopComplete = resolve;
|
|
567
677
|
});
|
|
568
678
|
let stopping = false;
|
|
679
|
+
/**
|
|
680
|
+
* Reclaim EVERYTHING this session owns, and let no single failure stop that.
|
|
681
|
+
*
|
|
682
|
+
* Teardown used to be one `try` over the whole body, so a throwing
|
|
683
|
+
* `entry.mounted.dispose()` on root 1 skipped root 2's disposal, both
|
|
684
|
+
* renderers' `forceContextLoss`, `game.dispose()` and the two global
|
|
685
|
+
* retractions — while `stopComplete` still resolved and the editor still
|
|
686
|
+
* reported the instance reclaimed. Every step is now isolated: a failure is
|
|
687
|
+
* COLLECTED and reported, never allowed to abort the steps after it.
|
|
688
|
+
*/
|
|
569
689
|
function fullCleanup() {
|
|
570
690
|
if (stopping)
|
|
571
691
|
return;
|
|
572
692
|
stopping = true;
|
|
693
|
+
const failures = [];
|
|
694
|
+
/** Run one teardown step; record its failure and keep going. */
|
|
695
|
+
const step = (name, run) => {
|
|
696
|
+
try {
|
|
697
|
+
run();
|
|
698
|
+
}
|
|
699
|
+
catch (error) {
|
|
700
|
+
failures.push({ step: name, error });
|
|
701
|
+
}
|
|
702
|
+
};
|
|
573
703
|
try {
|
|
574
|
-
loop.stop();
|
|
575
|
-
router.dispose();
|
|
704
|
+
step('loop.stop', () => loop.stop());
|
|
705
|
+
step('router.dispose', () => router.dispose());
|
|
576
706
|
for (const entry of mountedEntries) {
|
|
577
|
-
entry.
|
|
578
|
-
entry.renderer?.dispose();
|
|
579
|
-
entry.renderer?.forceContextLoss();
|
|
580
|
-
// Remove from the element's CURRENT parent, not the mount-time
|
|
581
|
-
// `container`: the editor's Game panel re-parents the live surfaces
|
|
582
|
-
// when its mount element swaps (fill <-> device preset, W2c), and
|
|
583
|
-
// `container.removeChild` would throw NotFoundError after such a move.
|
|
584
|
-
// `container` remains the fallback for hosts whose element stand-ins
|
|
585
|
-
// never wire `parentNode` (headless unit fixtures); an element already
|
|
586
|
-
// detached by such a host is a no-op via the catch.
|
|
587
|
-
try {
|
|
588
|
-
(entry.element.parentNode ?? container).removeChild(entry.element);
|
|
589
|
-
}
|
|
590
|
-
catch {
|
|
591
|
-
/* already detached — nothing to remove */
|
|
592
|
-
}
|
|
707
|
+
step(`root "${entry.id}"`, () => disposeMountedRoot(entry, container));
|
|
593
708
|
}
|
|
594
|
-
game.dispose();
|
|
595
|
-
retractDevGlobals();
|
|
596
|
-
retractRenderHarness?.();
|
|
709
|
+
step('game.dispose', () => game.dispose());
|
|
710
|
+
step('dev globals', () => retractDevGlobals());
|
|
711
|
+
step('render harness', () => retractRenderHarness?.());
|
|
597
712
|
}
|
|
598
713
|
finally {
|
|
599
714
|
void Promise.allSettled(mountedEntries.map((entry) => entry.mounted.disposeComplete ?? Promise.resolve())).then(() => resolveStopComplete());
|
|
600
715
|
}
|
|
716
|
+
if (failures.length > 0) {
|
|
717
|
+
// LOUD, and after everything else has been reclaimed. `stop()` is called
|
|
718
|
+
// from hosts that must not be left half-torn-down by an early throw, so
|
|
719
|
+
// the report comes last — but it IS a report: a silent partial teardown
|
|
720
|
+
// is the failure this whole restructure exists to end. The editor's
|
|
721
|
+
// `exitPlayMode` catches it and prints it to the editor console.
|
|
722
|
+
throw new AggregateError(failures.map((f) => f.error), `Game session teardown failed in ${failures.length} step(s): ${failures
|
|
723
|
+
.map((f) => f.step)
|
|
724
|
+
.join(', ')}. Every other step still ran.`);
|
|
725
|
+
}
|
|
601
726
|
}
|
|
602
727
|
return {
|
|
603
728
|
stop: fullCleanup,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The game-scoped debug/synthetic-player registry. ONE registry per `Game` root —
|
|
3
|
-
* every world's `ctx.debug` (see
|
|
3
|
+
* every world's `ctx.debug` (see {@link DebugCtxSurface} below) feeds the
|
|
4
4
|
* SAME registry via {@link DebugRegistry.forRoot}, so a name a game registers is
|
|
5
5
|
* visible (and name-collision-checked) across every world, not just the one that
|
|
6
6
|
* registered it. `createGame` (`runtime/game.ts`) constructs the registry
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* rather than threading it through every call site.
|
|
11
11
|
*
|
|
12
12
|
* Provenance note: a registration's "world" is the MOUNT's adapter id (the
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* id a project's root adapter already carries) rather than the
|
|
14
|
+
* `RootInstance.id` `registerThreeRoot`
|
|
15
15
|
* assigns — that id isn't known until AFTER `mount()` resolves (`create-
|
|
16
16
|
* runtime.ts` calls `registerThreeRoot` with it only once `mount()` returns),
|
|
17
17
|
* which is after every `setup()`-time registration has already run. For the
|
|
@@ -23,7 +23,76 @@ import { z } from 'zod';
|
|
|
23
23
|
import type { DebugAdapter } from '../adapter/system-adapter';
|
|
24
24
|
import type { GameLoopLiveness } from '../core/types';
|
|
25
25
|
import type { Game } from './game';
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The minimal structural shape `ctx.debug.attachRoom` needs from a joined
|
|
28
|
+
* Colyseus room: just enough to send the reserved `__vgai:debugCommand`
|
|
29
|
+
* message and listen for its `__vgai:debugCommandResult` reply. Structural,
|
|
30
|
+
* not a Colyseus type import — the same seam-boundary style every other
|
|
31
|
+
* `DebugCtxSurface` member uses (no wrapper around Colyseus itself, D2).
|
|
32
|
+
* `onMessage`'s return type is `unknown` because Colyseus's own return shape
|
|
33
|
+
* (void in some client versions, an unsubscribe function in others) isn't
|
|
34
|
+
* load-bearing here: the registry calls it defensively (invokes it on detach
|
|
35
|
+
* only if it's actually a function).
|
|
36
|
+
*/
|
|
37
|
+
export interface DebugRoomHandle {
|
|
38
|
+
send(type: string, message?: unknown): void;
|
|
39
|
+
onMessage(type: string, cb: (message: unknown) => void): unknown;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Infers a `registerCommand`/`registerReactCommand`/`useDebugCommand`
|
|
43
|
+
* handler's parameter tuple from its declared Zod `args` tuple (dry-run
|
|
44
|
+
* finding, ledger — "`registerCommand` fn typing forces `unknown[]` casts"):
|
|
45
|
+
* a real `z.ZodTuple` infers its element types (`z.tuple([z.number(),
|
|
46
|
+
* z.string()])` → `(n: number, s: string) => ...`), while omitting `args`
|
|
47
|
+
* (the generic's `undefined` default) resolves to the original permissive
|
|
48
|
+
* `(...args: unknown[]) => ...` shape every command had before this generic
|
|
49
|
+
* existed. That permissive fallback is deliberate, not just a placeholder —
|
|
50
|
+
* it keeps every existing no-schema registration (including ones whose
|
|
51
|
+
* handler reads positional args the runtime never validates, since
|
|
52
|
+
* `invoke()` only parses `args` against a schema when one is declared)
|
|
53
|
+
* compiling byte-for-byte unchanged.
|
|
54
|
+
*/
|
|
55
|
+
export type DebugCommandArgs<T extends z.ZodTuple | undefined = undefined> = T extends z.ZodTuple ? z.infer<T> : unknown[];
|
|
56
|
+
/**
|
|
57
|
+
* The authoring half of the debug/synthetic-player seam — the read/actuate half is
|
|
58
|
+
* `SystemAdapters.DebugAdapter` (`adapter/system-adapter.ts`). Callable from any
|
|
59
|
+
* `init` (one file per new provider/command), or from a react world's
|
|
60
|
+
* `useDebugProvider`/`useDebugCommand`/`useDebugEmit` hooks. Every registration feeds the ONE
|
|
61
|
+
* game-scoped registry (`runtime/debug-registry.ts`) — never a per-world accumulator.
|
|
62
|
+
*/
|
|
63
|
+
export interface DebugCtxSurface {
|
|
64
|
+
/** Register (or replace) a named, JSON-serializable state read. Duplicate
|
|
65
|
+
* names from the SAME world replace + warn once; the SAME name from a
|
|
66
|
+
* DIFFERENT world throws (`DebugError` code `DEBUG_NAME_COLLISION`). */
|
|
67
|
+
registerStateProvider(name: string, fn: () => unknown, opts?: {
|
|
68
|
+
tier?: 'observable' | 'assisted';
|
|
69
|
+
}): void;
|
|
70
|
+
/** Register (or replace) an invokable command. `locus` is REQUIRED once the
|
|
71
|
+
* project declares a Colyseus room (`DebugRegistry.setRoomDeclared`) — a
|
|
72
|
+
* fixture must say whether it mutates authoritative (server) or predicted
|
|
73
|
+
* (client) state.
|
|
74
|
+
*
|
|
75
|
+
* Generic over the declared `args` Zod tuple ({@link DebugCommandArgs}) so
|
|
76
|
+
* `fn`'s parameters INFER from it: `registerCommand('setHp', { args:
|
|
77
|
+
* z.tuple([z.number()]) }, (hp) => ...)` types `hp` as `number` with no
|
|
78
|
+
* cast. Leaving `args` off keeps `fn` typed `(...args: unknown[]) => ...`,
|
|
79
|
+
* same as before this generic existed. */
|
|
80
|
+
registerCommand<T extends z.ZodTuple | undefined = undefined>(name: string, spec: {
|
|
81
|
+
description?: string;
|
|
82
|
+
args?: T;
|
|
83
|
+
locus?: 'client' | 'server';
|
|
84
|
+
}, fn: (...args: DebugCommandArgs<T>) => unknown | Promise<unknown>): void;
|
|
85
|
+
/** Push a tick-stamped event onto the debug event ring (spec §3.3) — the
|
|
86
|
+
* engine stamps `tick`/`simT` at emission, not at read time. */
|
|
87
|
+
emit(event: string, detail?: unknown): void;
|
|
88
|
+
/**
|
|
89
|
+
* Task 2.2 — server-locus command routing (client leg). Call once your
|
|
90
|
+
* game code has joined its Colyseus room, so `locus: 'server'` commands
|
|
91
|
+
* have somewhere to send `__vgai:debugCommand`. Returns a detach function
|
|
92
|
+
* (call on room leave/dispose) — game-scoped, last-attached room wins.
|
|
93
|
+
*/
|
|
94
|
+
attachRoom(room: DebugRoomHandle): () => void;
|
|
95
|
+
}
|
|
27
96
|
/** Engine-local error for the debug seam — mirrors the `code` + `data` shape
|
|
28
97
|
* `packages/vgai-sdk/src/errors.ts` uses (no import: the engine does not
|
|
29
98
|
* depend on `@vgai/sdk`). `code` is always machine-readable; nothing reading
|
|
@@ -37,7 +106,7 @@ export declare class DebugError extends Error {
|
|
|
37
106
|
* actuates through — the SAME three methods `InputManager` exposes
|
|
38
107
|
* (`setVirtualAction`/`tapVirtualAction`/`clearVirtualActions`, Task 1.4),
|
|
39
108
|
* typed narrowly here so this module never imports `InputManager` itself.
|
|
40
|
-
* Wired once by the hosting adapter (`
|
|
109
|
+
* Wired once by the hosting adapter (`world3d-react/r3f-root-factory.tsx`, at the
|
|
41
110
|
* same seed spot as `setInputActionsSource`) — absent until then. */
|
|
42
111
|
export interface DebugVirtualInputTarget {
|
|
43
112
|
setVirtualAction(action: string, value: boolean | number | {
|
|
@@ -115,7 +184,7 @@ export interface RunTicksTarget {
|
|
|
115
184
|
}
|
|
116
185
|
/** What {@link createDebugRegistry} returns — the adapter half (`DebugAdapter`,
|
|
117
186
|
* for `SystemAdapters.debug`) plus the registration/lifecycle surface the
|
|
118
|
-
* hosting adapter (`
|
|
187
|
+
* hosting adapter (`world3d-react/r3f-root-factory.tsx`) and `createGame` drive. */
|
|
119
188
|
export interface DebugRegistry {
|
|
120
189
|
/** The `SystemAdapters.debug` implementer — one shared instance, seeded
|
|
121
190
|
* onto every world's adapter bag. */
|
|
@@ -169,7 +238,7 @@ export interface DebugRegistry {
|
|
|
169
238
|
* `{version: 1, seed: null, fixedDt: null, ticks: []}`. `seed`/`fixedDt`
|
|
170
239
|
* are the two replay-critical metadata fields the design doc's format
|
|
171
240
|
* sketch (§2.c) calls for beside the raw per-tick deltas — the wiring
|
|
172
|
-
* adapter (`
|
|
241
|
+
* adapter (`world3d-react/r3f-root-factory.tsx`) assembles them from
|
|
173
242
|
* `getSeededRandom(game)?.seed`/`host.game?.loop.fixedDt` alongside
|
|
174
243
|
* `InputManager.getInputTrace()`'s own `{version, ticks}`. An `engine`
|
|
175
244
|
* (package version) stamp remains a KNOWN GAP — no build-time version
|
|
@@ -228,7 +297,7 @@ export interface DebugRegistry {
|
|
|
228
297
|
/** D15/T-D15.3/.5 — the CURRENT shared game tick (the same counter the
|
|
229
298
|
* built-in `time` provider's `tick` field reads), for a per-world
|
|
230
299
|
* `InputManager.poll(tick)` call to key its `scheduleActionAtTick`
|
|
231
|
-
* numbering off — see `
|
|
300
|
+
* numbering off — see `world3d-react/r3f-root-factory.tsx`'s `systems.add('input',
|
|
232
301
|
* ...)` wiring. `0` for a bare `createDebugRegistry()` test stand-in with
|
|
233
302
|
* no real `Game`/tick counter behind it (matching `getTick`'s own
|
|
234
303
|
* constructor-supplied default in that case). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/debug-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAsC,MAAM,2BAA2B,CAAC;AAElG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"debug-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/debug-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAsC,MAAM,2BAA2B,CAAC;AAElG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC;CAClE;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,GAAG,SAAS,GAAG,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,GAC7F,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GACV,OAAO,EAAE,CAAC;AAEd;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;6EAEyE;IACzE,qBAAqB,CACnB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,EACjB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;KAAE,GAC1C,IAAI,CAAC;IACR;;;;;;;;;+CAS2C;IAC3C,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,GAAG,SAAS,GAAG,SAAS,EAC1D,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;KAAE,EACrE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAC/D,IAAI,CAAC;IACR;qEACiE;IACjE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,IAAI,CAAC;CAC/C;AAED;;;8CAG8C;AAC9C,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;gBAExC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAM1E;AA6BD;;;;;sEAKsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,gBAAgB,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GACjD;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,mBAAmB,IAAI,IAAI,CAAC;IAC5B;;4BAEwB;IACxB,oBAAoB,CAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GACjD,IAAI,CAAC;IACR,mBAAmB,IAAI,IAAI,CAAC;IAC5B,kBAAkB,IAAI,IAAI,CAAC;IAC3B,gBAAgB,IAAI,OAAO,CAAC;IAC5B;;;8EAG0E;IAC1E,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5E,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAChF;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED;;;;iEAIiE;AACjE,MAAM,WAAW,eAAe;IAC9B;;kCAE8B;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CAClC;AAED;;;kEAGkE;AAClE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CACnD;AA+CD;;qFAEqF;AACrF,MAAM,WAAW,aAAa;IAC5B;0CACsC;IACtC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B;iFAC6E;IAC7E,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC;IAC1C;;;sDAGkD;IAClD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACjD;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;kEAC8D;IAC9D,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IACzC;;;;;;6EAMyE;IACzE,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,IAAI,CAAC;IAC9F;;;;;;;;;;;;iFAY6E;IAC7E,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;IACzE;;;;;;;;;;;;;;;;gFAgB4E;IAC5E,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC9E;;;;;;;;;;;;OAYG;IACH,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,uBAAuB,GAAG,IAAI,CAAC;IACxE;;;;;;;;;OASG;IACH,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAChD;;;;iDAI6C;IAC7C,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAAC;IAC3C;;;;;;sDAMkD;IAClD,WAAW,IAAI,MAAM,CAAC;IACtB;;;;;;;;iBAQa;IACb,qBAAqB,CACnB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,EACjB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;KAAE,GACtD,MAAM,IAAI,CAAC;IACd;;sCAEkC;IAClC,oBAAoB,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,GAAG,SAAS,GAAG,SAAS,EAC/D,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;KAAE,EACrE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAC/D,MAAM,IAAI,CAAC;CACf;AAcD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,OAAO,IAAI,MAAM,CAAC;IAClB,OAAO,IAAI,MAAM,CAAC;IAClB,gBAAgB,CAAC,IAAI,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,IAAI,gBAAgB,CAAC;CACtC,GAAG,aAAa,CAwfhB;AAID;kFACkF;AAClF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAE/E;AAED;;8EAE8E;AAC9E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,aAAa,GAAG,IAAI,CAEjE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The game-scoped debug/synthetic-player registry. ONE registry per `Game` root —
|
|
3
|
-
* every world's `ctx.debug` (see
|
|
3
|
+
* every world's `ctx.debug` (see {@link DebugCtxSurface} below) feeds the
|
|
4
4
|
* SAME registry via {@link DebugRegistry.forRoot}, so a name a game registers is
|
|
5
5
|
* visible (and name-collision-checked) across every world, not just the one that
|
|
6
6
|
* registered it. `createGame` (`runtime/game.ts`) constructs the registry
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* rather than threading it through every call site.
|
|
11
11
|
*
|
|
12
12
|
* Provenance note: a registration's "world" is the MOUNT's adapter id (the
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* id a project's root adapter already carries) rather than the
|
|
14
|
+
* `RootInstance.id` `registerThreeRoot`
|
|
15
15
|
* assigns — that id isn't known until AFTER `mount()` resolves (`create-
|
|
16
16
|
* runtime.ts` calls `registerThreeRoot` with it only once `mount()` returns),
|
|
17
17
|
* which is after every `setup()`-time registration has already run. For the
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Ownership, stated in one place (the build rule):
|
|
11
11
|
* - OWNER: this function. It resolves the answer; nothing else derives it.
|
|
12
|
-
* - SHARERS: the
|
|
13
|
-
*
|
|
12
|
+
* - SHARERS: the three-root adapter (`world3d-react/r3f-root-factory.tsx`),
|
|
13
|
+
* which seeds live render vitals only
|
|
14
14
|
* under it, and the `static-batch` capability's mutation watch and `Frozen`
|
|
15
15
|
* warning. Any future dev-only instrument calls this too, with its own
|
|
16
16
|
* `override`.
|