@vgai/engine 0.5.22 → 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/native-debug-module.d.ts +31 -5
- package/dist/adapter/native-debug-module.d.ts.map +1 -1
- package/dist/adapter/native-debug-module.js +25 -7
- 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 +2 -2
- package/dist/adapter/renderer-config.js +2 -2
- 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/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 +5 -6
- package/dist/core/sim-clock.d.ts.map +1 -1
- package/dist/core/sim-clock.js +4 -5
- 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/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 +18 -5
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +33 -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/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 +2 -2
- package/dist/runtime/game-input-seams.d.ts.map +1 -1
- package/dist/runtime/game.d.ts +90 -41
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +80 -54
- 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 +8 -39
- 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 +18 -18
- 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/r3f-root-factory.d.ts +7 -7
- package/dist/world3d-react/r3f-root-factory.js +7 -7
- package/dist/world3d-react/rapier-physics-bridge.d.ts +9 -8
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +100 -48
- 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/native-debug-module.ts +43 -7
- package/src/adapter/rapier-physics-adapter.ts +76 -20
- package/src/adapter/renderer-config.ts +2 -2
- 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/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 +5 -6
- package/src/core/types.ts +1 -3
- package/src/defaults.ts +6 -14
- 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 +34 -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/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 +2 -2
- package/src/runtime/game.ts +168 -80
- package/src/runtime/mount-game.ts +8 -56
- package/src/runtime/mount-manifest.ts +43 -34
- 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/r3f-root-factory.tsx +7 -7
- package/src/world3d-react/rapier-physics-bridge.tsx +107 -50
- 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/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/dist/runtime/game.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createDebugRegistry, DebugError, registerDebugRegistry, } from './debug
|
|
|
16
16
|
import { createGameplayRngTrap, registerGameplayRngTrapControl } from './gameplay-rng-trap';
|
|
17
17
|
import { createStateBridge } from './state-bridge';
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* The tail of `PHASE_ORDER` that presents rather than simulates, and
|
|
20
20
|
* therefore runs once per DISPLAY frame (`runRenderFrameImpl`) rather than
|
|
21
21
|
* once per fixed substep. The same two phases `runTicks`' `skipRenderPhases`
|
|
22
22
|
* fast-forward skips and `render-control.ts`'s `renderOnce()` drives — one
|
|
@@ -31,7 +31,7 @@ const DISPLAY_RATE_PHASES = PHASE_ORDER.filter((phase) => phase === SystemPhase.
|
|
|
31
31
|
* slice or later ones) gets identical throw behavior.
|
|
32
32
|
*/
|
|
33
33
|
export function createRootInstance(init) {
|
|
34
|
-
const { id, kind, pausable = true, adapter, mounted, scene, stage, container, physics, collisions, physics2d, camera, frame, } = init;
|
|
34
|
+
const { id, kind, pausable = true, adapter, mounted, scene, stage, container, physics, collisions, physics2d, camera, frame, binding = null, } = init;
|
|
35
35
|
// A three world with no scene has nothing for `threeScene()` to return —
|
|
36
36
|
// fail loudly HERE, at construction, rather than letting `threeScene()`
|
|
37
37
|
// throw its generic "not a three world" message later for a world whose
|
|
@@ -64,11 +64,11 @@ export function createRootInstance(init) {
|
|
|
64
64
|
// read, so this wraps `mounted.dispose` in place (mutating the SAME mount
|
|
65
65
|
// object every holder of `mounted` shares — calling `mounted.dispose()`
|
|
66
66
|
// directly, exactly like calling `world.mounted.dispose()`, trips this)
|
|
67
|
-
// to flip a
|
|
68
|
-
// once
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
67
|
+
// to flip a flag this instance exposes as `disposed`, and wraps the frame
|
|
68
|
+
// hooks so they silently no-op once it is set. `runFrame` reads `disposed`
|
|
69
|
+
// and skips the world outright, which is what also covers the OPAQUE
|
|
70
|
+
// `mounted.update` fallback — the path every three root takes now that a
|
|
71
|
+
// mount is a plain `RootAdapter` with no phase-partitioned entry point.
|
|
72
72
|
let disposed = false;
|
|
73
73
|
const originalDispose = mounted.dispose.bind(mounted);
|
|
74
74
|
mounted.dispose = () => {
|
|
@@ -100,6 +100,10 @@ export function createRootInstance(init) {
|
|
|
100
100
|
physics2d,
|
|
101
101
|
camera,
|
|
102
102
|
frame: guardedFrame,
|
|
103
|
+
binding,
|
|
104
|
+
get disposed() {
|
|
105
|
+
return disposed;
|
|
106
|
+
},
|
|
103
107
|
threeScene() {
|
|
104
108
|
if (kind !== 'three' || !scene) {
|
|
105
109
|
throw new Error(`RootInstance "${id}" (kind: ${kind}): threeScene() requested but this world is not ` +
|
|
@@ -130,22 +134,6 @@ export function createRootInstance(init) {
|
|
|
130
134
|
},
|
|
131
135
|
};
|
|
132
136
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Type guard for whether a `MountedThreeRoot` is a first-party
|
|
135
|
-
* `SetupThreeRootAdapter` mount (has a live `GameContext` at `.ctx`). Used to
|
|
136
|
-
* decide whether a world's `physics`/`collisions`/`camera` can be populated
|
|
137
|
-
* from it — an external
|
|
138
|
-
* adapter's mount has none of these first-party handles.
|
|
139
|
-
*
|
|
140
|
-
* Checks the `firstParty: true` brand (checklist item 1), NOT `'ctx' in
|
|
141
|
-
* mounted` — a foreign mount may carry an unrelated `ctx` key, so a
|
|
142
|
-
* structural `'ctx' in mounted` check would misfire. The probe is a plain property read, not an
|
|
143
|
-
* `instanceof`/value import of `setup-three-root-adapter.ts` — the
|
|
144
|
-
* `MountedSetupThreeRoot` import above stays type-only.
|
|
145
|
-
*/
|
|
146
|
-
export function isFirstPartyMounted(mounted) {
|
|
147
|
-
return mounted.firstParty === true;
|
|
148
|
-
}
|
|
149
137
|
/**
|
|
150
138
|
* Read an owner's Rapier handles, treating an absent pair as "this owner holds
|
|
151
139
|
* no rigid bodies" — the honest reading for a world (R3F, or a bare
|
|
@@ -162,15 +150,14 @@ function readRapierHandles(owner) {
|
|
|
162
150
|
* only *when*; this is the *what*, and it lives here because it is the runtime
|
|
163
151
|
* that knows about Rapier and shared geometry.
|
|
164
152
|
*
|
|
165
|
-
* The order mirrors `adapter/
|
|
153
|
+
* The order mirrors `adapter/world3d-react/r3f-root-factory.tsx`'s world teardown for
|
|
166
154
|
* ONE subtree. Within the physics step,
|
|
167
155
|
* `rapierWorld.removeRigidBody(body)` comes BEFORE `physics.remove(node)`: the
|
|
168
156
|
* registry's `remove()` only drops index entries, so reversing the two leaks
|
|
169
157
|
* the Rapier body.
|
|
170
158
|
*
|
|
171
|
-
* `owners` is every world that could own part of the subtree
|
|
172
|
-
*
|
|
173
|
-
* adapter mount passes its own ctx. `physics.get` is a no-op for a node the
|
|
159
|
+
* `owners` is every world that could own part of the subtree. `physics.get`
|
|
160
|
+
* is a no-op for a node the
|
|
174
161
|
* owner does not own, so offering the subtree to each is safe.
|
|
175
162
|
*
|
|
176
163
|
* Safe on an object already removed or already disposed: `removeFromParent` on
|
|
@@ -212,13 +199,17 @@ export function disposeDebrisSubtree(obj, owners) {
|
|
|
212
199
|
}
|
|
213
200
|
}
|
|
214
201
|
/**
|
|
215
|
-
* The {@link DebrisOwner}s one registered world contributes
|
|
216
|
-
*
|
|
202
|
+
* The {@link DebrisOwner}s one registered world contributes. Written as a
|
|
203
|
+
* standalone function
|
|
217
204
|
* so `createGame`'s clock disposer is one line and the "which mounts count"
|
|
218
205
|
* rule has exactly one home.
|
|
219
206
|
*/
|
|
220
|
-
function debrisOwnersOf(
|
|
221
|
-
|
|
207
|
+
function debrisOwnersOf(_world) {
|
|
208
|
+
// No mount carries first-party Rapier handles: physics is a declared
|
|
209
|
+
// system built inside the world's own tree, and the library owns its
|
|
210
|
+
// bodies' lifecycles there. The owner list stays as the disposal seam's
|
|
211
|
+
// shape; today it is always empty.
|
|
212
|
+
return [];
|
|
222
213
|
}
|
|
223
214
|
/**
|
|
224
215
|
* Construct the (host-internal) Game shell. Callers: `createGameRuntime`
|
|
@@ -233,7 +224,7 @@ export function createGame(opts) {
|
|
|
233
224
|
const input = new InputManager();
|
|
234
225
|
const stateBridge = createStateBridge();
|
|
235
226
|
// D15 (T-D15.1) — the game-scoped seeded-random surface every world's
|
|
236
|
-
// `ctx.random` aliases (see `
|
|
227
|
+
// `ctx.random` aliases (see `world3d-react/r3f-root-factory.tsx`'s `ctx.random =
|
|
237
228
|
// ...`, wired the same way `ctx.debug` is just below). Constructed
|
|
238
229
|
// unconditionally (cheap — a handful of closures) regardless of whether
|
|
239
230
|
// this project ever declares `determinism.seededRandom`; only the BOOT
|
|
@@ -266,7 +257,7 @@ export function createGame(opts) {
|
|
|
266
257
|
// the CURRENT values.
|
|
267
258
|
let tick = 0;
|
|
268
259
|
let simT = 0;
|
|
269
|
-
//
|
|
260
|
+
// The display-rate half. `renderAlpha` is the last alpha
|
|
270
261
|
// `runRenderFrameImpl` presented at (0 until a host drives one); the set is
|
|
271
262
|
// the `RenderStepped`-shaped registry `Game.onRenderStep` feeds. Deliberately
|
|
272
263
|
// NOT beside `tick`/`simT` in meaning: neither of these ever advances sim
|
|
@@ -282,7 +273,7 @@ export function createGame(opts) {
|
|
|
282
273
|
//
|
|
283
274
|
// The disposer is supplied HERE rather than inside the clock because
|
|
284
275
|
// `core/sim-clock.ts` deliberately knows nothing about Rapier or shared
|
|
285
|
-
// geometry. It mirrors `
|
|
276
|
+
// geometry. It mirrors `world3d-react/r3f-root-factory.tsx`'s world teardown
|
|
286
277
|
// ordering for ONE subtree.
|
|
287
278
|
const simClock = createSimClock({
|
|
288
279
|
// Every mount that could own part of the subtree is offered it:
|
|
@@ -341,6 +332,10 @@ export function createGame(opts) {
|
|
|
341
332
|
* component registration for a declared slot is shadowed (with the
|
|
342
333
|
* standard warning) rather than silently winning by running later. */
|
|
343
334
|
const declaredSystemAdapters = new Map();
|
|
335
|
+
/** Per-root POSITIVE ABSENCES from the same declaration — `absent(reason)`
|
|
336
|
+
* slots. Nothing binds them (there is nothing to bind); they are kept so a
|
|
337
|
+
* reader can tell an answered absence from an unasked question. */
|
|
338
|
+
const declaredAbsences = new Map();
|
|
344
339
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one cohesive merge-with-collision-report walk (per-world × per-source × per-key); splitting the collision-warn branch out would obscure that it's part of the same pass, not reduce real complexity
|
|
345
340
|
function computeSystemAdapters() {
|
|
346
341
|
// Debug is game-scoped: React hooks, probes, and the built-in time
|
|
@@ -425,6 +420,14 @@ export function createGame(opts) {
|
|
|
425
420
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: fans out TWO independent capability gates (loop, audio) with the same "call it, else report once" shape per world — splitting the two gates into separate loops would duplicate the fan-out, not reduce real complexity
|
|
426
421
|
function setRootGates(next) {
|
|
427
422
|
for (const world of roots) {
|
|
423
|
+
// A disposed world stays in `roots` (there is no `unregisterRoot`), and
|
|
424
|
+
// its adapter's `setPaused`/`systems.audio` reach a renderer, ticker or
|
|
425
|
+
// audio graph that `dispose()` already released — the same reason
|
|
426
|
+
// `runFrame`/`runRenderFrame`/`runTicks` skip it. Without this, a
|
|
427
|
+
// pause/resume after a partial teardown calls into freed resources, and
|
|
428
|
+
// the audio leg re-mutes a context that no longer exists.
|
|
429
|
+
if (world.disposed)
|
|
430
|
+
continue;
|
|
428
431
|
if (!world.pausable)
|
|
429
432
|
continue; // pausable:false roots are untouched by design
|
|
430
433
|
if (world.mounted.drivesOwnLoop) {
|
|
@@ -455,14 +458,14 @@ export function createGame(opts) {
|
|
|
455
458
|
if (rngTrapEnabled)
|
|
456
459
|
rngTrap.enable();
|
|
457
460
|
profiler.beginFrame();
|
|
458
|
-
//
|
|
461
|
+
// The render-phase skip. Two callers set it, for the
|
|
459
462
|
// same reason — this substep is not the thing that paints. `runTicks`'s
|
|
460
463
|
// `render: 'none'|'last'` fast-forward sets it on every tick it doesn't
|
|
461
464
|
// want to paint (see `runTicks`'s doc comment on `GameInternal`), and a
|
|
462
465
|
// DISPLAY-RATE host sets it on every substep because `runRenderFrame`
|
|
463
|
-
// paints once per real frame instead. Unlike `onlyFrozen` below it is
|
|
464
|
-
//
|
|
465
|
-
// signature, since an external host is
|
|
466
|
+
// paints once per real frame instead. Unlike `onlyFrozen` below it is not
|
|
467
|
+
// internal-only: it is part of the public `GameInternal.runFrame`
|
|
468
|
+
// signature, since an external host is one of those two callers.
|
|
466
469
|
const skipRenderPhases = frameOpts?.skipRenderPhases ?? false;
|
|
467
470
|
// `onlyFrozen` is internal-only (not part of the public `GameInternal.runFrame`
|
|
468
471
|
// signature — no external caller sets it) — `Game.play.step()` below is the
|
|
@@ -480,6 +483,8 @@ export function createGame(opts) {
|
|
|
480
483
|
inputFrameActive = false;
|
|
481
484
|
for (let i = 0; i < n; i++) {
|
|
482
485
|
const world = roots[i];
|
|
486
|
+
if (world.disposed)
|
|
487
|
+
continue;
|
|
483
488
|
if (onlyFrozen) {
|
|
484
489
|
if (paused && world.pausable && !world.mounted.drivesOwnLoop) {
|
|
485
490
|
inputFrameActive = true;
|
|
@@ -507,6 +512,8 @@ export function createGame(opts) {
|
|
|
507
512
|
if (!onlyFrozen) {
|
|
508
513
|
for (let i = 0; i < n; i++) {
|
|
509
514
|
const world = roots[i];
|
|
515
|
+
if (world.disposed)
|
|
516
|
+
continue;
|
|
510
517
|
if (!world.mounted.drivesOwnLoop)
|
|
511
518
|
continue;
|
|
512
519
|
if (!paused || !world.pausable || !world.mounted.setPaused) {
|
|
@@ -532,6 +539,8 @@ export function createGame(opts) {
|
|
|
532
539
|
systems.runPhase(phase, dt);
|
|
533
540
|
for (let i = 0; i < n; i++) {
|
|
534
541
|
const world = roots[i];
|
|
542
|
+
if (world.disposed)
|
|
543
|
+
continue;
|
|
535
544
|
if (world.mounted.drivesOwnLoop)
|
|
536
545
|
continue;
|
|
537
546
|
if (!(paused && world.pausable))
|
|
@@ -548,6 +557,8 @@ export function createGame(opts) {
|
|
|
548
557
|
}
|
|
549
558
|
for (let i = 0; i < n; i++) {
|
|
550
559
|
const world = roots[i];
|
|
560
|
+
if (world.disposed)
|
|
561
|
+
continue;
|
|
551
562
|
if (world.mounted.drivesOwnLoop)
|
|
552
563
|
continue;
|
|
553
564
|
if (!(paused && world.pausable))
|
|
@@ -583,6 +594,8 @@ export function createGame(opts) {
|
|
|
583
594
|
systems.runPhase(phase, dt);
|
|
584
595
|
for (let i = 0; i < n; i++) {
|
|
585
596
|
const world = roots[i];
|
|
597
|
+
if (world.disposed)
|
|
598
|
+
continue;
|
|
586
599
|
if (world.mounted.drivesOwnLoop)
|
|
587
600
|
continue;
|
|
588
601
|
// D10/T7.6: a `pausable` world under an active pause
|
|
@@ -610,6 +623,8 @@ export function createGame(opts) {
|
|
|
610
623
|
}
|
|
611
624
|
for (let i = 0; i < n; i++) {
|
|
612
625
|
const world = roots[i];
|
|
626
|
+
if (world.disposed)
|
|
627
|
+
continue;
|
|
613
628
|
if (world.mounted.drivesOwnLoop)
|
|
614
629
|
continue;
|
|
615
630
|
// A fully-frozen world gets no `endFrame`/opaque-`update` call either —
|
|
@@ -665,7 +680,7 @@ export function createGame(opts) {
|
|
|
665
680
|
rngTrap.disable();
|
|
666
681
|
}
|
|
667
682
|
/**
|
|
668
|
-
*
|
|
683
|
+
* One display frame's PRESENTATION pass. See
|
|
669
684
|
* `GameInternal.runRenderFrame`'s doc comment for the contract; this is the
|
|
670
685
|
* `preRender`+`render` slice of `runFrameImpl`'s phase loop, lifted out and
|
|
671
686
|
* driven by the loop's own per-real-frame callback instead of by the substep
|
|
@@ -713,6 +728,8 @@ export function createGame(opts) {
|
|
|
713
728
|
systems.runPhase(phase, displayDt);
|
|
714
729
|
for (let i = 0; i < n; i++) {
|
|
715
730
|
const world = roots[i];
|
|
731
|
+
if (world.disposed)
|
|
732
|
+
continue;
|
|
716
733
|
if (world.mounted.drivesOwnLoop)
|
|
717
734
|
continue;
|
|
718
735
|
// D10/T7.6, carried over verbatim in substance: a frozen world still
|
|
@@ -761,7 +778,10 @@ export function createGame(opts) {
|
|
|
761
778
|
for (const listener of systemAdapterListeners)
|
|
762
779
|
listener();
|
|
763
780
|
},
|
|
764
|
-
|
|
781
|
+
get declaredSystemAbsences() {
|
|
782
|
+
return [...declaredAbsences.values()].flat();
|
|
783
|
+
},
|
|
784
|
+
installDeclaredSystemAdapters(rootId, slots, absent = []) {
|
|
765
785
|
if (!roots.some((w) => w.id === rootId)) {
|
|
766
786
|
throw new Error(`installDeclaredSystemAdapters: no mounted root is named "${rootId}" ` +
|
|
767
787
|
`(mounted: ${roots.map((w) => `"${w.id}"`).join(', ') || 'none'}).`);
|
|
@@ -771,6 +791,11 @@ export function createGame(opts) {
|
|
|
771
791
|
'systems — a declaration is static and installs exactly once per mount.');
|
|
772
792
|
}
|
|
773
793
|
declaredSystemAdapters.set(rootId, slots);
|
|
794
|
+
// ALWAYS set, never merge: a re-install (an HMR of the entry's `systems`
|
|
795
|
+
// table) that dropped an `absent()` marker must also drop the recorded
|
|
796
|
+
// absence, or the coverage report keeps printing a game statement the
|
|
797
|
+
// game no longer makes.
|
|
798
|
+
declaredAbsences.set(rootId, absent);
|
|
774
799
|
gameInternal.notifySystemAdaptersChanged();
|
|
775
800
|
},
|
|
776
801
|
input,
|
|
@@ -801,6 +826,11 @@ export function createGame(opts) {
|
|
|
801
826
|
// not paused, nothing here runs — step() is a whole-call no-op.
|
|
802
827
|
if (paused) {
|
|
803
828
|
for (const world of roots) {
|
|
829
|
+
// Same disposed-world guard `runFrameImpl` (and `setRootGates`)
|
|
830
|
+
// make: a disposed world's `mounted.step` drives a loop whose
|
|
831
|
+
// resources are already freed.
|
|
832
|
+
if (world.disposed)
|
|
833
|
+
continue;
|
|
804
834
|
if (!world.pausable || !world.mounted.drivesOwnLoop)
|
|
805
835
|
continue;
|
|
806
836
|
if (!world.mounted.setPaused)
|
|
@@ -826,8 +856,8 @@ export function createGame(opts) {
|
|
|
826
856
|
// `endFrame` called once per registration) and double-dispose-wrapped
|
|
827
857
|
// (`createRootInstance` wraps `mounted.dispose` in place — a second
|
|
828
858
|
// wrap would flip `disposed` and call through on ITS OWN wrapped
|
|
829
|
-
// `originalDispose`, which
|
|
830
|
-
//
|
|
859
|
+
// `originalDispose`, which would be harmless only if the adapter's
|
|
860
|
+
// dispose happened to be idempotent; a foreign adapter
|
|
831
861
|
// has no such guarantee). Reject it outright instead.
|
|
832
862
|
if (roots.some((w) => w.mounted === world.mounted)) {
|
|
833
863
|
throw new Error(`Game.registerRoot: world "${world.id}" shares its \`mounted\` object with an ` +
|
|
@@ -837,14 +867,14 @@ export function createGame(opts) {
|
|
|
837
867
|
roots.push(world);
|
|
838
868
|
gameInternal.notifySystemAdaptersChanged();
|
|
839
869
|
// Notify state-bridge subscribers THE INSTANT the world list changes —
|
|
840
|
-
// not just at the next completed `runFrame`.
|
|
841
|
-
//
|
|
842
|
-
//
|
|
870
|
+
// not just at the next completed `runFrame`. Otherwise a UI that lists
|
|
871
|
+
// worlds hangs on an empty list: `mountAllRootSpecs`
|
|
872
|
+
// (`create-runtime.ts`) mounts roots
|
|
843
873
|
// SEQUENTIALLY, and a react world's `adapter.mount()`
|
|
844
874
|
// (`resolveDefaultReactAdapter`/`mountOneReactRoot`) renders its tree —
|
|
845
875
|
// synchronously in some React builds, but React 19's concurrent
|
|
846
876
|
// renderer does NOT guarantee a synchronous first commit (see
|
|
847
|
-
// `r3f-
|
|
877
|
+
// `r3f-root-factory.tsx`'s own doc comment on `onCreated`) — BEFORE the
|
|
848
878
|
// caller calls `registerRoot` for that very world. A `useWorldState`
|
|
849
879
|
// selector reading `g.roots` can therefore render for the first time
|
|
850
880
|
// while `roots` is still missing entries that register moments later.
|
|
@@ -888,15 +918,11 @@ export function createGame(opts) {
|
|
|
888
918
|
// world mount), eroding the signal for a genuinely un-observable
|
|
889
919
|
// ingested world.
|
|
890
920
|
// A HOST-DRIVEN mount (`drivesOwnLoop: false`, notably the R3F adapter)
|
|
891
|
-
// is observable through `Game.state`
|
|
892
|
-
//
|
|
893
|
-
// `runFrame`, so the frame-versioned bridge covers it. Requiring a
|
|
921
|
+
// is observable through `Game.state` when it ticks inside
|
|
922
|
+
// `runFrame` — the frame-versioned bridge covers it. Requiring a
|
|
894
923
|
// foreign-world `observe` bridge there produces a false warning while
|
|
895
924
|
// the canonical bridge is already live.
|
|
896
|
-
if (!
|
|
897
|
-
world.mounted.drivesOwnLoop &&
|
|
898
|
-
world.kind !== 'dom' &&
|
|
899
|
-
!world.mounted.observe) {
|
|
925
|
+
if (world.mounted.drivesOwnLoop && world.kind !== 'dom' && !world.mounted.observe) {
|
|
900
926
|
console.warn(`[game] world "${world.id}" (kind: ${world.kind}, adapter: "${world.adapter.id}"): ` +
|
|
901
927
|
'no state bridge — this mounted game has no `observe` (RootStateObserver); ' +
|
|
902
928
|
'react HUDs/useRootObservation cannot subscribe to its state.');
|
|
@@ -984,7 +1010,7 @@ export function createGame(opts) {
|
|
|
984
1010
|
// root's `mounted.dispose()`, whereas a per-root teardown may be ending
|
|
985
1011
|
// just one sub-session while sibling roots keep running (see the
|
|
986
1012
|
// disposed-world guard in `runFrame`). It used to be disposed from
|
|
987
|
-
// `
|
|
1013
|
+
// `world3d-react/r3f-root-factory.tsx`'s teardown, which meant disposing one of
|
|
988
1014
|
// two three roots froze `now()` for the whole Game, rejected the other
|
|
989
1015
|
// world's pending `delay`s and turned its `after()` calls into silent
|
|
990
1016
|
// no-ops. Note the asymmetry that gives the bug away: `seededRandom` and
|
|
@@ -80,22 +80,6 @@ export declare function isAdapterRegistered(kind: string): boolean;
|
|
|
80
80
|
* registry without cross-test leakage.
|
|
81
81
|
*/
|
|
82
82
|
export declare function __clearAdapterSurfaceRegistryForTests(): void;
|
|
83
|
-
/**
|
|
84
|
-
* A ready-to-register {@link AdapterSurfaceFactory} for `kind: 'three'` roots
|
|
85
|
-
* that follow the first-party entry-module convention (`export const
|
|
86
|
-
* adapter` and/or `export async function setup`). Threejs is safe to
|
|
87
|
-
* ship as an exported convenience (unlike canvas/react) because `three`/
|
|
88
|
-
* `SetupThreeRootAdapter` are already unconditional engine dependencies —
|
|
89
|
-
* see `mount-manifest.ts`'s header comment. Still never auto-registered:
|
|
90
|
-
* the registry itself stays zero-policy (see this file's header comment) —
|
|
91
|
-
* a caller opts in with `registerAdapter('three', defaultThreeAdapterFactory)`.
|
|
92
|
-
*
|
|
93
|
-
* Resolution mirrors `mount-manifest.ts`'s own `resolveThreeAdapter` branch
|
|
94
|
-
* order: an entry module's `adapter` export wins outright; else its `setup`
|
|
95
|
-
* export (wrapping via `fromSetup` happens inside `mountManifestRoots`
|
|
96
|
-
* itself — this factory just returns the `MountEntry` shape).
|
|
97
|
-
*/
|
|
98
|
-
export declare const defaultThreeAdapterFactory: AdapterSurfaceFactory;
|
|
99
83
|
export interface MountGameOptions {
|
|
100
84
|
/** Explicit per-world entries, same shape `mountManifestRoots` accepts —
|
|
101
85
|
* ALWAYS wins over a registered kind factory for that world id (backward
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-game.d.ts","sourceRoot":"","sources":["../../src/runtime/mount-game.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAuC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"mount-game.d.ts","sourceRoot":"","sources":["../../src/runtime/mount-game.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAuC,MAAM,kBAAkB,CAAC;AAOxF;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B;6DACyD;IACzD,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD;;;mFAG+E;IAC/E,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC;gEAC4D;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACzC;AAED,+EAA+E;AAC/E,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,4BAA4B,KAC9B,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAQtC;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAUlF;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,qCAAqC,IAAI,IAAI,CAE5D;AAMD,MAAM,WAAW,gBAAgB;IAC/B;;;kEAG8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC;CACrE;AA+BD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,qBAAqB,CACzC,aAAa,EAAE,OAAO,EACtB,IAAI,EAAE,YAAY,EAClB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAqDtB"}
|
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
// callers register, but it registers NONE itself. In particular there is no
|
|
13
13
|
// built-in 'canvas'/'react' registration here (that would require this file
|
|
14
14
|
// to value-import `pixi.js`/`react`/`react-dom`, exactly what `mount-manifest
|
|
15
|
-
// .ts` forbids); a project that has a canvas or react world MUST
|
|
16
|
-
// factory for that kind itself —
|
|
17
|
-
//
|
|
18
|
-
//
|
|
15
|
+
// .ts` forbids); a project that has a three, canvas, or react world MUST
|
|
16
|
+
// register a factory for that kind itself — the factories live with the
|
|
17
|
+
// modules that own their dependencies (`r3fRootFactory` in
|
|
18
|
+
// `@engine/world3d-react`, its Pixi twin in `@engine/canvas-react`), never
|
|
19
|
+
// wired up here automatically.
|
|
19
20
|
import { declaredRoots } from '../adapter/manifest-interpreter';
|
|
20
21
|
import { installNativeDebugBindings, installNativeSystemsBindings, nativeDebugBindingFromEntryModule, nativeSystemsBindingFromEntryModule, } from '../adapter/native-debug-module';
|
|
21
22
|
import { publishDevInstruments } from '../dev/instruments';
|
|
@@ -63,44 +64,12 @@ export function isAdapterRegistered(kind) {
|
|
|
63
64
|
export function __clearAdapterSurfaceRegistryForTests() {
|
|
64
65
|
registry.clear();
|
|
65
66
|
}
|
|
66
|
-
/**
|
|
67
|
-
* A ready-to-register {@link AdapterSurfaceFactory} for `kind: 'three'` roots
|
|
68
|
-
* that follow the first-party entry-module convention (`export const
|
|
69
|
-
* adapter` and/or `export async function setup`). Threejs is safe to
|
|
70
|
-
* ship as an exported convenience (unlike canvas/react) because `three`/
|
|
71
|
-
* `SetupThreeRootAdapter` are already unconditional engine dependencies —
|
|
72
|
-
* see `mount-manifest.ts`'s header comment. Still never auto-registered:
|
|
73
|
-
* the registry itself stays zero-policy (see this file's header comment) —
|
|
74
|
-
* a caller opts in with `registerAdapter('three', defaultThreeAdapterFactory)`.
|
|
75
|
-
*
|
|
76
|
-
* Resolution mirrors `mount-manifest.ts`'s own `resolveThreeAdapter` branch
|
|
77
|
-
* order: an entry module's `adapter` export wins outright; else its `setup`
|
|
78
|
-
* export (wrapping via `fromSetup` happens inside `mountManifestRoots`
|
|
79
|
-
* itself — this factory just returns the `MountEntry` shape).
|
|
80
|
-
*/
|
|
81
|
-
export const defaultThreeAdapterFactory = (world, ctx) => {
|
|
82
|
-
const mod = ctx.entryModule;
|
|
83
|
-
if (mod?.adapter)
|
|
84
|
-
return { kind: 'three', adapter: mod.adapter };
|
|
85
|
-
if (mod?.setup)
|
|
86
|
-
return { kind: 'three', setup: mod.setup };
|
|
87
|
-
if (world.entry !== undefined) {
|
|
88
|
-
throw new Error(`defaultThreeAdapterFactory: entry module "${world.entry}" for world "${world.id}" exports ` +
|
|
89
|
-
'neither `adapter` nor `setup` — every first-party entry module exports at least one ' +
|
|
90
|
-
'(export one from the declared entry module).');
|
|
91
|
-
}
|
|
92
|
-
throw new Error(`defaultThreeAdapterFactory: world "${world.id}" declares no \`entry\` — a three root is ` +
|
|
93
|
-
'authored as a TSX/R3F world module; point `entry` at it.');
|
|
94
|
-
};
|
|
95
67
|
/** True for the one shape `mountManifestRoots` can ALREADY mount with zero
|
|
96
68
|
* `entries[id]` at all.
|
|
97
69
|
*
|
|
98
|
-
*
|
|
99
|
-
* `default`-adapter, SCENE-driven, no-`entry` three world, which
|
|
100
|
-
* `resolveThreeAdapter`'s scene branch could mount from the manifest alone —
|
|
101
|
-
* died with the `.vscn.json` format. `load.ts` now rejects a three root with no
|
|
70
|
+
* NOTHING is self-sufficient: `load.ts` rejects a three root with no
|
|
102
71
|
* `entry` outright, so every world needs a registered factory or an explicit
|
|
103
|
-
* entry
|
|
72
|
+
* entry. This predicate is retained so the loud-degrade error stays the
|
|
104
73
|
* documented outcome rather than a silent skip. */
|
|
105
74
|
function worldIsSelfSufficient(_world) {
|
|
106
75
|
return false;
|
|
@@ -164,7 +133,7 @@ export async function mountGameFromManifest(manifestInput, host, opts = {}) {
|
|
|
164
133
|
throw new Error(`mountGameFromManifest: root "${world.id}" (adapter "${world.adapter.identity}") has no registered ` +
|
|
165
134
|
`adapter factory and no explicit entries["${world.id}"] — call ` +
|
|
166
135
|
`registerAdapter("${world.adapter.identity}", factory) before mounting (see ` +
|
|
167
|
-
'`
|
|
136
|
+
'`r3fRootFactory` in `@vgai/engine/world3d-react` for the worked three factory), or pass ' +
|
|
168
137
|
`opts.entries["${world.id}"] directly (see mount-manifest.ts's \`MountEntry\`).`);
|
|
169
138
|
}
|
|
170
139
|
const session = await mountManifestRoots({
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { RootDeclaration } from '../adapter/binding';
|
|
1
2
|
import type { RootAdapter } from '../adapter/root-adapter';
|
|
2
3
|
import { type ResolvedGameManifest } from '../manifest/load';
|
|
3
4
|
import { type GameSession } from './create-runtime';
|
|
4
5
|
import { type DebugBridgeWindowTarget } from './debug-bridge';
|
|
5
|
-
import type {
|
|
6
|
+
import type { PlaytestContext } from './playtest';
|
|
6
7
|
/**
|
|
7
8
|
* A caller-supplied entry for a `kind: 'three'` world — two ways to satisfy
|
|
8
9
|
* it, matching the two shapes `resolveDefaultThreeAdapter` supports (an
|
|
@@ -11,20 +12,27 @@ import type { GameSetupFn, PlaytestContext } from './types';
|
|
|
11
12
|
* - `{ adapter }` — a fully-constructed {@link RootAdapter} (first-party or
|
|
12
13
|
* not). Always wins if present, regardless of the world's `entry` field —
|
|
13
14
|
* full caller control, including for a `{ module }`-adapter world
|
|
14
|
-
* (mountManifestRoots cannot import an arbitrary module path itself
|
|
15
|
-
*
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
-
* `SetupThreeRootAdapter` here, mirroring `resolveDefaultThreeAdapter`'s
|
|
19
|
-
* entry branch exactly.
|
|
20
|
-
*
|
|
21
|
-
* The former third shape, `{ componentRegistry }` for a `scene`-declaring
|
|
22
|
-
* world, went with the `.vscn.json` format in WO-8.
|
|
15
|
+
* (mountManifestRoots cannot import an arbitrary module path itself —
|
|
16
|
+
* for a default-exported world, build the adapter with
|
|
17
|
+
* `resolveR3FEntryAdapter(entryModule, worldId)` from
|
|
18
|
+
* `@vgai/engine/world3d-react` and pass it here).
|
|
23
19
|
*/
|
|
24
|
-
export interface ThreeMountEntry {
|
|
20
|
+
export interface ThreeMountEntry extends MountEntryDeclaration {
|
|
25
21
|
readonly kind: 'three';
|
|
26
22
|
readonly adapter?: RootAdapter | undefined;
|
|
27
|
-
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The half of a root's binding declaration a CALLER holds — everything in
|
|
26
|
+
* `adapter/binding.ts`'s {@link RootDeclaration} except `root`, which this
|
|
27
|
+
* file already has from the manifest it just resolved.
|
|
28
|
+
*
|
|
29
|
+
* Optional throughout, and absent is the honest state for a caller who built
|
|
30
|
+
* an adapter by hand out of its own module graph: there is no entry namespace
|
|
31
|
+
* to report a static surface for, so the root registers with `binding: null`
|
|
32
|
+
* rather than one describing a module nobody loaded.
|
|
33
|
+
*/
|
|
34
|
+
export interface MountEntryDeclaration {
|
|
35
|
+
readonly declaration?: Omit<RootDeclaration, 'root'> | undefined;
|
|
28
36
|
}
|
|
29
37
|
/**
|
|
30
38
|
* A caller-supplied entry for a `kind: 'canvas'` world — ALWAYS a
|
|
@@ -34,7 +42,7 @@ export interface ThreeMountEntry {
|
|
|
34
42
|
* `pixi.js`, which it deliberately never does (see this file's header
|
|
35
43
|
* comment).
|
|
36
44
|
*/
|
|
37
|
-
export interface PixiMountEntry {
|
|
45
|
+
export interface PixiMountEntry extends MountEntryDeclaration {
|
|
38
46
|
readonly kind: 'canvas';
|
|
39
47
|
readonly adapter: RootAdapter<'canvas'>;
|
|
40
48
|
}
|
|
@@ -51,7 +59,7 @@ export interface PixiMountEntry {
|
|
|
51
59
|
* itself (see this file's header comment) — building the adapter is
|
|
52
60
|
* entirely the caller's job.
|
|
53
61
|
*/
|
|
54
|
-
export interface ReactMountEntry {
|
|
62
|
+
export interface ReactMountEntry extends MountEntryDeclaration {
|
|
55
63
|
readonly kind: 'dom';
|
|
56
64
|
readonly adapter: RootAdapter<'dom'>;
|
|
57
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-manifest.d.ts","sourceRoot":"","sources":["../../src/runtime/mount-manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mount-manifest.d.ts","sourceRoot":"","sources":["../../src/runtime/mount-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// The standalone manifest-mount helper: an ENGINE-side generalization of
|
|
2
2
|
// `examples/tri-world/src/main.ts`'s hand-built `RootMountSpec[]`.
|
|
3
3
|
//
|
|
4
|
-
// `resolveAllRoots`/`
|
|
5
|
-
//
|
|
4
|
+
// `resolveAllRoots`/`resolveRootBinding` (`packages/editor/src/
|
|
5
|
+
// binding-resolver.ts`) already do this translation for the EDITOR, but they
|
|
6
6
|
// are Vite-coupled by construction (`/@fs/` dynamic imports of project files
|
|
7
7
|
// through the dev server, §0.1/§0.5 of the design doc) — unusable from a
|
|
8
8
|
// plain static build. This module mirrors their per-kind dispatch
|
|
@@ -17,16 +17,12 @@
|
|
|
17
17
|
// type-only import): this file never value-imports
|
|
18
18
|
// `pixi.js`, `react`, or `react-dom` — a
|
|
19
19
|
// canvas or react world's adapter is ALWAYS supplied already-constructed via
|
|
20
|
-
// `entries[id].adapter` (the caller's own module graph built it)
|
|
21
|
-
//
|
|
22
|
-
// `SetupThreeRootAdapter` are already unconditional dependencies of every
|
|
23
|
-
// caller of `createGameRuntime`, so wrapping a `setup` with
|
|
24
|
-
// `fromSetup` here adds no NEW bundle weight — only pixi/react are avoided.
|
|
20
|
+
// `entries[id].adapter` (the caller's own module graph built it), so no
|
|
21
|
+
// surface's library is pulled into a game that has no world on it.
|
|
25
22
|
import { assertNever } from '../adapter/adapter-surface';
|
|
26
23
|
import { declaredRoots } from '../adapter/manifest-interpreter';
|
|
27
24
|
// Value-imported from the implementer's OWN path, not the type-only
|
|
28
25
|
// `../adapter` barrel (P-6).
|
|
29
|
-
import { fromSetup } from '../adapter/setup-three-root-adapter';
|
|
30
26
|
import { loadGameManifest, } from '../manifest/load';
|
|
31
27
|
import { createGameRuntime } from './create-runtime';
|
|
32
28
|
import { maybeInstallDebugBridge } from './debug-bridge';
|
|
@@ -104,8 +100,8 @@ export function resolveManifest(raw) {
|
|
|
104
100
|
return loadGameManifest(raw);
|
|
105
101
|
}
|
|
106
102
|
// ---------------------------------------------------------------------------
|
|
107
|
-
// Per-kind entry -> adapter resolution (mirrors `
|
|
108
|
-
// per-identity dispatch, `packages/editor/src/
|
|
103
|
+
// Per-kind entry -> adapter resolution (mirrors `resolveRootBinding`'s
|
|
104
|
+
// per-identity dispatch, `packages/editor/src/binding-resolver.ts`)
|
|
109
105
|
// ---------------------------------------------------------------------------
|
|
110
106
|
function resolveThreeAdapter(world, entry) {
|
|
111
107
|
if (entry?.adapter)
|
|
@@ -125,17 +121,15 @@ function resolveThreeAdapter(world, entry) {
|
|
|
125
121
|
}
|
|
126
122
|
// type === 'default'
|
|
127
123
|
if (world.entry !== undefined) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
return fromSetup(world.id, entry.setup);
|
|
124
|
+
throw new Error(`mountManifestRoots: world "${world.id}" (three) declares \`entry\` "${world.entry}" ` +
|
|
125
|
+
`— supply entries["${world.id}"] = { kind: 'three', adapter } (for a default-exported ` +
|
|
126
|
+
'world, build it with `resolveR3FEntryAdapter(entryModule, worldId)` from ' +
|
|
127
|
+
'`@vgai/engine/world3d-react`). Got ' +
|
|
128
|
+
`${entry === undefined ? 'no entry at all' : 'an entry with no `adapter`'}.`);
|
|
135
129
|
}
|
|
136
130
|
// Unreachable in practice: `load.ts`'s `checkAdapterSceneEntryRules` requires
|
|
137
131
|
// `entry` for every 'default' THREE adapter world at manifest-load time (and
|
|
138
|
-
// rejects
|
|
132
|
+
// rejects any other content declaration on one outright).
|
|
139
133
|
throw new Error(`mountManifestRoots: world "${world.id}" is default-three with no \`entry\` — this ` +
|
|
140
134
|
'should have been rejected by manifest validation.');
|
|
141
135
|
}
|
|
@@ -167,6 +161,12 @@ function buildRootMountSpec(world, entry) {
|
|
|
167
161
|
id: world.id,
|
|
168
162
|
zOrder: world.zOrder,
|
|
169
163
|
pausable: world.pausable,
|
|
164
|
+
// `root` is this file's own resolved manifest record; the rest — the
|
|
165
|
+
// parsed `vgai.adapter.ts`, the entry namespace, the entry's declared
|
|
166
|
+
// debug/systems bindings — only the caller who loaded the entry can know.
|
|
167
|
+
...(entry?.declaration === undefined
|
|
168
|
+
? {}
|
|
169
|
+
: { declaration: { root: world, ...entry.declaration } }),
|
|
170
170
|
};
|
|
171
171
|
// `entry`'s `kind` is guaranteed to agree with `world.kind` past the guard
|
|
172
172
|
// above (or `entry` is `undefined`) — TS can't narrow a `Record` lookup
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHO IS PLAYING, and under what identity — the host's own declaration for one
|
|
3
|
+
* private play run or coordinated team test.
|
|
4
|
+
*
|
|
5
|
+
* Its own module because it is a PUBLISHED path: a project's Colyseus
|
|
6
|
+
* networking adapter (the `colyseus` capability, copied into every game) names
|
|
7
|
+
* this type, so it must live somewhere that survives. The engine never joins a
|
|
8
|
+
* room and never couples this authoring identity to a networking library —
|
|
9
|
+
* games may pass `roomKey` as a Colyseus filter/join option, and that is the
|
|
10
|
+
* whole of the coupling.
|
|
11
|
+
*/
|
|
12
|
+
/** Host-supplied identity for one private play run or coordinated team test.
|
|
13
|
+
* Games may pass `roomKey` as a Colyseus filter/join option; the engine never
|
|
14
|
+
* joins a room or couples this authoring identity to a networking library. */
|
|
15
|
+
export interface PlaytestContext {
|
|
16
|
+
readonly mode: 'private' | 'team';
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly roomKey: string;
|
|
19
|
+
readonly revision: number | null;
|
|
20
|
+
readonly participantId: string | null;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=playtest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playtest.d.ts","sourceRoot":"","sources":["../../src/runtime/playtest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;8EAE8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC"}
|