@vgai/engine 0.5.2 → 0.5.4
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 +18 -11
- package/package.json +10 -6
- package/schemas/engine-api.json +1 -68
- package/schemas/engine-api.md +1 -32
- package/schemas/engine-capabilities.json +30 -42
- package/schemas/{vgai-game.schema.json → vgai-project.schema.json} +52 -34
- package/src/adapter/adapter-surface.ts +5 -5
- package/src/adapter/authoring.ts +168 -226
- package/src/adapter/colyseus-networking-adapter.ts +44 -5
- package/src/adapter/first-party-systems.ts +156 -42
- package/src/adapter/host-context.ts +177 -46
- package/src/adapter/index.ts +47 -51
- package/src/adapter/ingest/game-contract.ts +2 -2
- package/src/adapter/ingest/scene-capture.ts +18 -19
- package/src/adapter/ingest/structural-ids.ts +127 -0
- package/src/adapter/ingest/upstream-pin.ts +9 -12
- package/src/adapter/loop-gate-report.ts +11 -11
- package/src/adapter/rapier-physics-adapter.ts +27 -9
- package/src/adapter/root-adapter.ts +217 -0
- package/src/adapter/{vgai-scene-game-adapter.ts → setup-three-root-adapter.ts} +173 -351
- package/src/adapter/system-adapter.ts +80 -63
- package/src/ai/navigation.ts +1 -1
- package/src/animation/animation-clock.ts +1 -1
- package/src/animation/camera-ownership.ts +1 -2
- package/src/animation/cubic-spline-interpolant.ts +132 -0
- package/src/animation/theatre-clock-binding.ts +2 -2
- package/src/animation/theatre-object-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +75 -5
- package/src/{scene/schema → asset-formats}/camera.ts +2 -5
- package/src/{scene/schema → asset-formats}/collider.ts +2 -5
- package/src/asset-formats/index.ts +54 -0
- package/src/{scene/schema → asset-formats}/instances.ts +10 -6
- package/src/{scene/schema → asset-formats}/light.ts +3 -6
- package/src/{scene/schema → asset-formats}/material.ts +4 -7
- package/src/{scene/schema → asset-formats}/mesh.ts +4 -7
- package/src/asset-formats/parse.ts +39 -0
- package/src/{scene/schema → asset-formats}/particles.ts +3 -6
- package/src/{scene/schema/environment.ts → asset-formats/render-env.ts} +23 -86
- package/src/{scene/schema → asset-formats}/tuples.ts +1 -1
- package/src/{scene/asset-loaders.ts → asset-loaders.ts} +8 -9
- package/src/asset-parse-error.ts +33 -0
- package/src/{scene/asset-registry.ts → asset-registry.ts} +1 -1
- package/src/assets.ts +1 -1
- package/src/audio/wav-encode.ts +9 -9
- package/src/canvas-react/engine-bridge.ts +59 -0
- package/src/canvas-react/index.ts +50 -0
- package/src/canvas-react/pixi-primitive.tsx +202 -0
- package/src/canvas-react/pixi-react-adapter.tsx +290 -0
- package/src/canvas-react/pixi-react-root-factory.tsx +88 -0
- package/src/canvas-react/world-context.ts +328 -0
- package/src/core/frame-pacing.ts +100 -0
- package/src/core/game-loop.ts +50 -28
- package/src/core/seeded-random.ts +7 -7
- package/src/core/sim-clock.ts +388 -0
- package/src/core/system-runner.ts +17 -63
- package/src/core/types.ts +34 -15
- package/src/data/data-asset.ts +3 -3
- package/src/data/data-check-core.ts +6 -7
- package/src/data/data-ref.ts +11 -11
- package/src/data/vite-plugin-data.ts +10 -10
- package/src/{scene/defaults.ts → defaults.ts} +18 -40
- package/src/dev/render-debug-adapter.ts +1 -1
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/ecs/scene-index.ts +439 -0
- package/src/ecs/scene-query.ts +43 -0
- package/src/{scene → ecs}/user-data.ts +17 -36
- package/src/index.ts +7 -9
- package/src/input/input-manager.ts +29 -32
- package/src/input/input-types.ts +2 -2
- package/src/input/schema.ts +5 -5
- package/src/loader.ts +57 -0
- package/src/manifest/editor-port.ts +69 -0
- package/src/manifest/filename.ts +49 -0
- package/src/manifest/index.ts +8 -2
- package/src/manifest/load-file.ts +11 -0
- package/src/manifest/load.ts +65 -77
- package/src/manifest/locate.ts +55 -0
- package/src/manifest/schema.ts +400 -233
- package/src/{scene → physics}/collider-dimensions.ts +3 -3
- package/src/physics/physics-registry.ts +1 -1
- package/src/{world2d/authoring-2d.ts → pixi/authoring.ts} +24 -11
- package/src/pixi/index.ts +43 -0
- package/src/{world2d/ingest-iframe-2d.ts → pixi/ingest-iframe.ts} +9 -9
- package/src/{world2d/ingest2d.ts → pixi/ingest.ts} +28 -28
- package/src/{world2d/physics2d-registry.ts → pixi/physics-registry.ts} +1 -1
- package/src/{world2d/scene-capture-2d.ts → pixi/scene-capture.ts} +3 -3
- package/src/{world2d/system-adapters-2d.ts → pixi/system-adapters.ts} +2 -2
- package/src/react/unmanaged-root-detector.ts +26 -1
- package/src/react/use-data.ts +5 -5
- package/src/react/use-selection.tsx +15 -42
- package/src/react/{game-state.tsx → world-state.tsx} +44 -46
- package/src/render/auto-batcher.ts +1 -2
- package/src/{scene → render}/instance-mesh.ts +1 -1
- package/src/{scene → render}/light-camera-factory.ts +14 -13
- package/src/render/lod.ts +17 -0
- package/src/{scene → render}/material-factory.ts +8 -7
- package/src/{scene → render}/particles-factory.ts +62 -12
- package/src/render/render-batch-system.ts +14 -41
- package/src/render/render-features.ts +1 -1
- package/src/render/render-settings.ts +1 -2
- package/src/render/spark-renderer-lifecycle.ts +1 -1
- package/src/runtime/create-runtime.ts +352 -463
- package/src/runtime/debug-bridge.ts +148 -93
- package/src/runtime/debug-registry.ts +79 -54
- package/src/runtime/dev-layers.ts +40 -0
- package/src/runtime/frame-selector-cache.ts +4 -4
- package/src/runtime/game.ts +618 -369
- package/src/runtime/gameplay-rng-trap.ts +6 -7
- package/src/runtime/input-router.ts +11 -11
- package/src/runtime/mount-game.ts +54 -55
- package/src/runtime/mount-manifest.ts +154 -150
- package/src/runtime/presentation.ts +141 -0
- package/src/runtime/render-audio-control.ts +64 -53
- package/src/runtime/render-control.ts +45 -51
- package/src/runtime/render-seed.ts +3 -4
- package/src/runtime/state-bridge.ts +17 -18
- package/src/runtime/types.ts +94 -61
- package/src/setup/setup-renderer.ts +11 -6
- package/src/world3d-react/engine-bridge.ts +46 -33
- package/src/world3d-react/index.ts +31 -26
- package/src/world3d-react/r3f-adapter.tsx +211 -96
- package/src/world3d-react/r3f-root-factory.tsx +91 -0
- package/src/world3d-react/renderer-config.ts +137 -0
- package/src/world3d-react/world-context.ts +141 -111
- package/schemas/entity2d.schema.json +0 -468
- package/schemas/prefab.schema.json +0 -9992
- package/schemas/scn2d.schema.json +0 -494
- package/schemas/vscn.schema.json +0 -10851
- package/src/adapter/game-adapter.ts +0 -164
- package/src/adapter/ingest/overlay-applier.ts +0 -207
- package/src/adapter/ingest/overlay-apply.ts +0 -168
- package/src/adapter/ingest/overlay-file.ts +0 -126
- package/src/adapter/ingest/overlay-report.ts +0 -176
- package/src/animation/gsap-registration.ts +0 -184
- package/src/audio/audio-introspection.ts +0 -290
- package/src/audio/index.ts +0 -39
- package/src/audio/tone-clock-binding.ts +0 -98
- package/src/audio/tone-context.ts +0 -175
- package/src/audio/tone-offline-render.ts +0 -167
- package/src/ecs/component-manager.ts +0 -814
- package/src/ecs/game-component.ts +0 -260
- package/src/ecs/hmr-swap-report.ts +0 -65
- package/src/physics/trigger-dispatch.ts +0 -97
- package/src/react/root-adapter.tsx +0 -49
- package/src/scene/asset-paths.ts +0 -121
- package/src/scene/asset-ref-check.ts +0 -248
- package/src/scene/component-registry.ts +0 -51
- package/src/scene/parse.ts +0 -204
- package/src/scene/scene-apply.ts +0 -407
- package/src/scene/scene-diff-schema.ts +0 -115
- package/src/scene/scene-diff-types.ts +0 -29
- package/src/scene/scene-loader.ts +0 -1526
- package/src/scene/scene-query.ts +0 -63
- package/src/scene/scene-types.ts +0 -33
- package/src/scene/scene-version.ts +0 -40
- package/src/scene/schema/animation.ts +0 -46
- package/src/scene/schema/audio.ts +0 -25
- package/src/scene/schema/entity-ref.ts +0 -78
- package/src/scene/schema/entity.ts +0 -189
- package/src/scene/schema/index.ts +0 -51
- package/src/scene/schema/joint.ts +0 -26
- package/src/scene/schema/physics.ts +0 -49
- package/src/scene/schema/scene-file.ts +0 -292
- package/src/scene/schema/shadow.ts +0 -24
- package/src/scene/schema/spline.ts +0 -21
- package/src/world2d/asset-paths2d.ts +0 -44
- package/src/world2d/capture-to-scene2d.ts +0 -52
- package/src/world2d/collision-2d.ts +0 -99
- package/src/world2d/entity2d-asset.ts +0 -22
- package/src/world2d/index.ts +0 -91
- package/src/world2d/physics2d-transform.ts +0 -173
- package/src/world2d/physics2d-units.ts +0 -10
- package/src/world2d/pixi-game-adapter.ts +0 -439
- package/src/world2d/pixi-surface.ts +0 -78
- package/src/world2d/scene2d-identity.ts +0 -49
- package/src/world2d/scene2d-loader.ts +0 -433
- package/src/world2d/schema/entity2d.ts +0 -163
- package/src/world2d/schema/physics2d.ts +0 -64
- package/src/world2d/schema/sprite.ts +0 -99
- package/src/world2d/schema/tilemap.ts +0 -39
- package/src/world2d/schema/tuples2d.ts +0 -25
- package/src/world2d/transform-writer-2d.ts +0 -42
- package/src/world2d/types.ts +0 -74
- package/src/world3d-react/behavior.tsx +0 -146
- /package/src/{scene → render}/mesh-shadow.ts +0 -0
|
@@ -1,60 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* First-party implementations of the remaining `SystemAdapters` —
|
|
2
|
+
* First-party implementations of the remaining `SystemAdapters` — audio and
|
|
3
3
|
* navigation — each a thin coordination/introspection boundary over the real
|
|
4
4
|
* engine subsystem (the original vision named these as first-class System
|
|
5
5
|
* adapters; physics + networking shipped earlier). The editor speaks only the
|
|
6
|
-
* interfaces; these wrap
|
|
7
|
-
*
|
|
6
|
+
* interfaces; these wrap the engine audio bus and `NavMeshManager` so an
|
|
7
|
+
* external game could supply its own equivalents. (The former
|
|
8
8
|
* `AnimationAdapter`/`createAnimationAdapter` over the `AnimGraph` map was
|
|
9
|
-
* removed by E5 — AnimGraph no longer exists
|
|
10
|
-
*
|
|
9
|
+
* removed by E5 — AnimGraph no longer exists. The former
|
|
10
|
+
* `createInputManagerAdapter`/`createVgaiAssetAdapter` went with
|
|
11
|
+
* `SystemAdapters.input`/`.assets` — registered, never read by anything.)
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import type * as THREE from 'three';
|
|
14
15
|
import type { NavMeshManager } from '../ai/navigation';
|
|
15
|
-
import type { InputManager } from '../input/input-manager';
|
|
16
|
-
import { resolveUrl } from '../loader';
|
|
17
16
|
import type { AudioContext as GameAudio } from '../setup/setup-audio';
|
|
18
17
|
import type {
|
|
19
|
-
ActionValueSnapshot,
|
|
20
|
-
AssetAdapter,
|
|
21
18
|
AudioAdapter,
|
|
22
|
-
|
|
19
|
+
AudioGraphNode,
|
|
20
|
+
AudioMeterFrame,
|
|
21
|
+
AudioMeterHandle,
|
|
23
22
|
NavigationAdapter,
|
|
24
23
|
NavPoint,
|
|
25
24
|
} from './system-adapter';
|
|
26
25
|
|
|
27
|
-
/** First-party `InputAdapter` over the engine `InputManager`. Reads each action through
|
|
28
|
-
* `InputManager.readAction`, dispatched on that action's OWN declared `valueType`
|
|
29
|
-
* (`getActionValueType`) — never a hardcoded getter — so a scalar/Vector2/pointer action
|
|
30
|
-
* surfaces its real typed value instead of being coerced through `isPressed` (F1-followup;
|
|
31
|
-
* `readAction` on the wrong type would throw, so dispatch must follow the declared type). */
|
|
32
|
-
export function createInputManagerAdapter(input: InputManager): InputAdapter {
|
|
33
|
-
return {
|
|
34
|
-
poll: () => input.poll(),
|
|
35
|
-
actions: () => {
|
|
36
|
-
const out: Record<string, ActionValueSnapshot> = {};
|
|
37
|
-
for (const name of input.actionNames()) {
|
|
38
|
-
const type = input.getActionValueType(name);
|
|
39
|
-
// `readAction`'s return type is inferred from a literal `expectedType`; with `type`
|
|
40
|
-
// widened to `ActionValueType` here it collapses to `boolean | number | Vector2`, so
|
|
41
|
-
// digital's `boolean` is normalized to the adapter's 1/0 the same way the old digital-
|
|
42
|
-
// only adapter did — every other value type already reads as `number | Vector2`.
|
|
43
|
-
const raw = input.readAction(name, type);
|
|
44
|
-
out[name] = { type, value: typeof raw === 'boolean' ? (raw ? 1 : 0) : raw };
|
|
45
|
-
}
|
|
46
|
-
return out;
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** First-party `AssetAdapter` — resolves URLs through the engine loader (which
|
|
52
|
-
* applies the project base + downloaded-asset remap). External/ingest games can
|
|
53
|
-
* supply their own to redirect relative paths. */
|
|
54
|
-
export function createVgaiAssetAdapter(): AssetAdapter {
|
|
55
|
-
return { resolve: (url) => resolveUrl(url) };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
26
|
/**
|
|
59
27
|
* First-party `AudioAdapter` over the engine's master-gain bus
|
|
60
28
|
* (`setup-audio.ts`'s `AudioContext.masterGain`) — the seam `Game.play.pause()`
|
|
@@ -64,10 +32,42 @@ export function createVgaiAssetAdapter(): AssetAdapter {
|
|
|
64
32
|
* (the editor's `MuteButton`, `PlayBar.tsx`): pausing while already
|
|
65
33
|
* user-muted resumes muted, exactly as a user would expect.
|
|
66
34
|
*/
|
|
35
|
+
function audioNodeTypeName(value: object): string {
|
|
36
|
+
const name = (value as { constructor?: { name?: string } }).constructor?.name;
|
|
37
|
+
return name && name !== 'Object' ? name : 'AudioNode';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** This world's real native context, when it has one (headless worlds do not). */
|
|
41
|
+
function nativeAudioContext(audio: GameAudio): (BaseAudioContext & Partial<AudioContext>) | null {
|
|
42
|
+
const context = (audio.listener as { context?: BaseAudioContext } | undefined)?.context;
|
|
43
|
+
return context && typeof context.state === 'string'
|
|
44
|
+
? (context as BaseAudioContext & Partial<AudioContext>)
|
|
45
|
+
: null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Live meter handles per adapter, so a world teardown can release analyser
|
|
50
|
+
* taps a consumer forgot to dispose.
|
|
51
|
+
*
|
|
52
|
+
* This registry follows `acquireMeters` out of `audio-introspection.ts`: the
|
|
53
|
+
* safety net has to live wherever the taps are created, and they are created
|
|
54
|
+
* here now. Keyed weakly so an adapter that is simply dropped takes its entry
|
|
55
|
+
* with it.
|
|
56
|
+
*/
|
|
57
|
+
const meterHandlesByAdapter = new WeakMap<AudioAdapter, Set<AudioMeterHandle>>();
|
|
58
|
+
|
|
59
|
+
/** Release every meter tap still open on `adapter` (world stop / teardown). */
|
|
60
|
+
export function releaseAudioMeters(adapter: AudioAdapter): void {
|
|
61
|
+
const handles = meterHandlesByAdapter.get(adapter);
|
|
62
|
+
if (!handles) return;
|
|
63
|
+
for (const handle of [...handles]) handle.dispose();
|
|
64
|
+
}
|
|
65
|
+
|
|
67
66
|
export function createAudioSystemAdapter(audio: GameAudio): AudioAdapter {
|
|
68
67
|
let muted = false;
|
|
69
68
|
let priorGain = audio.masterGain.gain.value;
|
|
70
|
-
|
|
69
|
+
const liveMeterHandles = new Set<AudioMeterHandle>();
|
|
70
|
+
const adapter: AudioAdapter = {
|
|
71
71
|
resume: () => audio.resume(),
|
|
72
72
|
setMuted(next: boolean) {
|
|
73
73
|
if (next === muted) return;
|
|
@@ -80,7 +80,121 @@ export function createAudioSystemAdapter(audio: GameAudio): AudioAdapter {
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
isMuted: () => muted,
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The engine's own bus hierarchy.
|
|
86
|
+
*
|
|
87
|
+
* This lived in `audio-introspection.ts` — the Tone-backed module — which
|
|
88
|
+
* meant the editor could only ever show an audio graph for a game that had
|
|
89
|
+
* loaded Tone. Nothing here needs Tone: it is the destination, the master
|
|
90
|
+
* gain, the three engine buses and the THREE listener. The Tone half (nodes
|
|
91
|
+
* a game routed through `connectToneBusToMasterGain`) is APPENDED by that
|
|
92
|
+
* module when it attaches, which is the honest split — Tone routes are
|
|
93
|
+
* Tone's to report.
|
|
94
|
+
*/
|
|
95
|
+
graphSnapshot(): AudioGraphNode[] {
|
|
96
|
+
const contextState = nativeAudioContext(audio)?.state;
|
|
97
|
+
const destination: AudioGraphNode = {
|
|
98
|
+
id: 'destination',
|
|
99
|
+
type: 'AudioDestinationNode',
|
|
100
|
+
label: 'Output',
|
|
101
|
+
outputs: [],
|
|
102
|
+
};
|
|
103
|
+
if (contextState !== undefined) destination.state = contextState;
|
|
104
|
+
const nodes: AudioGraphNode[] = [
|
|
105
|
+
destination,
|
|
106
|
+
{
|
|
107
|
+
id: 'master',
|
|
108
|
+
type: audioNodeTypeName(audio.masterGain),
|
|
109
|
+
label: 'Master',
|
|
110
|
+
outputs: ['destination'],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'bus-music',
|
|
114
|
+
type: audioNodeTypeName(audio.buses.music),
|
|
115
|
+
label: 'Music bus',
|
|
116
|
+
outputs: ['master'],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'bus-sfx',
|
|
120
|
+
type: audioNodeTypeName(audio.buses.sfx),
|
|
121
|
+
label: 'SFX bus',
|
|
122
|
+
outputs: ['master'],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 'bus-voice',
|
|
126
|
+
type: audioNodeTypeName(audio.buses.voice),
|
|
127
|
+
label: 'Voice bus',
|
|
128
|
+
outputs: ['master'],
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
if (audio.listener) {
|
|
132
|
+
nodes.push({
|
|
133
|
+
id: 'listener',
|
|
134
|
+
type: audioNodeTypeName(audio.listener),
|
|
135
|
+
label: '3D listener (THREE)',
|
|
136
|
+
outputs: ['master'],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return nodes;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Per-bus linear-RMS levels, via native `AnalyserNode` taps.
|
|
144
|
+
*
|
|
145
|
+
* Zero Tone — analysers work identically whether or not Tone is bridged,
|
|
146
|
+
* and disposal is a plain disconnect. It was in the Tone module purely by
|
|
147
|
+
* where it was written, so a game that never touches Tone had no meters.
|
|
148
|
+
*/
|
|
149
|
+
acquireMeters(): AudioMeterHandle | null {
|
|
150
|
+
const meterContext = nativeAudioContext(audio);
|
|
151
|
+
if (!meterContext || typeof meterContext.createAnalyser !== 'function') {
|
|
152
|
+
return null; // headless world — metering is impossible, and says so
|
|
153
|
+
}
|
|
154
|
+
const taps = (
|
|
155
|
+
[
|
|
156
|
+
['master', 'Master', audio.masterGain],
|
|
157
|
+
['bus-music', 'Music', audio.buses.music],
|
|
158
|
+
['bus-sfx', 'SFX', audio.buses.sfx],
|
|
159
|
+
['bus-voice', 'Voice', audio.buses.voice],
|
|
160
|
+
] as const
|
|
161
|
+
).map(([id, label, gain]) => {
|
|
162
|
+
const analyser = meterContext.createAnalyser();
|
|
163
|
+
analyser.fftSize = 1024;
|
|
164
|
+
gain.connect(analyser); // a TAP (analyser is a sink), not an insert
|
|
165
|
+
return { id, label, gain, analyser, buffer: new Float32Array(analyser.fftSize) };
|
|
166
|
+
});
|
|
167
|
+
let disposed = false;
|
|
168
|
+
const handle: AudioMeterHandle = {
|
|
169
|
+
read: (): AudioMeterFrame[] =>
|
|
170
|
+
taps.map(({ id, label, analyser, buffer }) => {
|
|
171
|
+
analyser.getFloatTimeDomainData(buffer);
|
|
172
|
+
let sum = 0;
|
|
173
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
174
|
+
const sample = buffer[i] as number;
|
|
175
|
+
sum += sample * sample;
|
|
176
|
+
}
|
|
177
|
+
return { id, label, level: Math.sqrt(sum / buffer.length) };
|
|
178
|
+
}),
|
|
179
|
+
dispose: (): void => {
|
|
180
|
+
if (disposed) return;
|
|
181
|
+
disposed = true;
|
|
182
|
+
for (const tap of taps) {
|
|
183
|
+
try {
|
|
184
|
+
tap.gain.disconnect(tap.analyser);
|
|
185
|
+
} catch {
|
|
186
|
+
// already torn down with the context — nothing to release
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
liveMeterHandles.delete(handle);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
liveMeterHandles.add(handle);
|
|
193
|
+
return handle;
|
|
194
|
+
},
|
|
83
195
|
};
|
|
196
|
+
meterHandlesByAdapter.set(adapter, liveMeterHandles);
|
|
197
|
+
return adapter;
|
|
84
198
|
}
|
|
85
199
|
|
|
86
200
|
/** First-party `NavigationAdapter` over the engine `NavMeshManager` — the
|
|
@@ -1,22 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Host contexts — what the host hands a root when it mounts it, PER SURFACE.
|
|
3
3
|
*
|
|
4
4
|
* This generalizes the old `GameContext`, which baked in first-party system
|
|
5
5
|
* choices (a Rapier world, the postprocessing composer, the InputManager, …).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
6
|
+
* The host never assumes Rapier/Colyseus; those live behind `SystemAdapters`
|
|
7
|
+
* owned by the first-party implementer.
|
|
8
|
+
*
|
|
9
|
+
* ## Why this file is a family, not one interface (P-8)
|
|
10
|
+
*
|
|
11
|
+
* There used to be a single "neutral" `HostContext` interface handed to EVERY
|
|
12
|
+
* root, with a NON-OPTIONAL `three: typeof THREE` and `renderer:
|
|
13
|
+
* THREE.WebGLRenderer` on it. That made the neutral layer three-LOCKED rather
|
|
14
|
+
* than merely three-named: a Pixi or React root would have been handed a WebGL
|
|
15
|
+
* renderer it cannot use. In practice nobody did hand it one — the production
|
|
16
|
+
* Pixi and React hosts have always built their own canvas/dom-shaped contexts
|
|
17
|
+
* instead — so the "one context for every surface"
|
|
18
|
+
* claim was already false in the tree; it just wasn't expressed in the types,
|
|
19
|
+
* which is why `RootAdapter<K>` could only parameterize its RETURN type and
|
|
20
|
+
* the Pixi/React adapters lived outside `RootAdapter` entirely.
|
|
21
|
+
*
|
|
22
|
+
* The split (ratified, docs/ARCHITECTURE-CORE.md "Seam"):
|
|
23
|
+
*
|
|
24
|
+
* {@link HostContextBase} what EVERY surface genuinely gets
|
|
25
|
+
* ├─ {@link ThreeHostContext} + `three` · `renderer` · `surface` · `assets`
|
|
26
|
+
* ├─ {@link CanvasHostContext} + `canvas`/`width`/`height` (+ dpr/compositing hints)
|
|
27
|
+
* └─ {@link DomHostContext} + `container`
|
|
28
|
+
*
|
|
29
|
+
* and {@link HostContextFor} maps an `AdapterSurface` to its context, so
|
|
30
|
+
* `RootAdapter<K>.mount`'s PARAMETER is K-typed too — the surface tag now
|
|
31
|
+
* picks the context type, which is the whole point of the tag.
|
|
32
|
+
*
|
|
33
|
+
* ### Where the line was drawn, and why
|
|
34
|
+
*
|
|
35
|
+
* The base holds exactly the members all three surfaces ALREADY carry today —
|
|
36
|
+
* nothing was invented to make the base look richer:
|
|
37
|
+
*
|
|
38
|
+
* - `game` — the root handle. Every surface has one (three: `ThreeHostContext.game`,
|
|
39
|
+
* canvas: `CanvasHostContext.game`, dom: `DomHostContext.game`), and it is the one
|
|
40
|
+
* genuinely surface-independent thing a host can hand any root.
|
|
41
|
+
* - `headless` — a property of the ENVIRONMENT (no GPU/DOM/audio), not of the
|
|
42
|
+
* render surface.
|
|
43
|
+
*
|
|
44
|
+
* Deliberately NOT in the base:
|
|
45
|
+
*
|
|
46
|
+
* - `renderer`/`three` — three's own objects; the defect this split exists to
|
|
47
|
+
* fix.
|
|
48
|
+
* - the render surface — its SHAPE is surface-specific and there is no honest
|
|
49
|
+
* common denominator: three nests `{canvas,width,height}` under `surface`,
|
|
50
|
+
* canvas passes those three flat (plus its own compositing hints), and a DOM
|
|
51
|
+
* root gets an `HTMLElement` container and no canvas at all. A shared
|
|
52
|
+
* `surface` field would have to be a union nobody can consume without
|
|
53
|
+
* re-branching on kind — i.e. the same three-shaped promise, one level down.
|
|
54
|
+
* - `assets` — `AssetCache`'s SIGNATURE reads neutral (`load<T>(url)`), but its
|
|
55
|
+
* one implementation is a GLTF/texture loader (`assets.ts` branches on
|
|
56
|
+
* `.glb`/`.gltf` and resolves THREE objects), and neither the canvas nor the
|
|
57
|
+
* dom host has ever been handed one: a canvas adapter builds its own
|
|
58
|
+
* cache inside `mount`. Promoting it to the base would be the anti-shim rule
|
|
59
|
+
* in miniature — a host promise nothing keeps. It stays on
|
|
60
|
+
* {@link ThreeHostContext} until a second surface genuinely receives one.
|
|
61
|
+
*
|
|
62
|
+
* These three names are the ONLY names for these shapes. The transitional
|
|
63
|
+
* aliases that carried the pre-split spellings were deleted; every implementer
|
|
64
|
+
* and call site names the surface it actually mounts on.
|
|
65
|
+
*
|
|
66
|
+
* It also used to carry `requestSystem<T extends keyof SystemRegistry>()` — an
|
|
67
|
+
* "ask the host for a first-party subsystem" hook — over an EMPTY
|
|
68
|
+
* `SystemRegistry`. That made `keyof SystemRegistry` = `never`, so no argument
|
|
69
|
+
* could legally be passed and the method was uncallable by construction. In
|
|
70
|
+
* practice it existed as 28 copies of `requestSystem: () => null` in tests and
|
|
71
|
+
* hosts, with no caller anywhere, and its only real effect would have been to
|
|
72
|
+
* re-open the door this interface exists to close: the host handing a game a
|
|
73
|
+
* subsystem it did not build. Deleted rather than kept as a promise.
|
|
74
|
+
*
|
|
75
|
+
* Migration note: `SetupThreeRootAdapter.mount` builds today's `GameContext`
|
|
76
|
+
* (Rapier world, composer, …) internally from a `ThreeHostContext`. New code
|
|
77
|
+
* targets these contexts; `GameContext` is now a first-party implementation
|
|
78
|
+
* detail.
|
|
15
79
|
*/
|
|
16
80
|
|
|
17
81
|
import type * as THREE from 'three';
|
|
18
82
|
import type { AssetCache } from '../assets';
|
|
19
|
-
import type { GameInternal } from '../runtime/game';
|
|
83
|
+
import type { Game, GameInternal } from '../runtime/game';
|
|
84
|
+
import type { AdapterSurface } from './adapter-surface';
|
|
20
85
|
|
|
21
86
|
/** The canvas/container the game renders into, plus its size. */
|
|
22
87
|
export interface HostSurface {
|
|
@@ -25,51 +90,117 @@ export interface HostSurface {
|
|
|
25
90
|
readonly height: number;
|
|
26
91
|
}
|
|
27
92
|
|
|
28
|
-
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
93
|
+
/**
|
|
94
|
+
* The surface-INDEPENDENT half of every host context — see this file's header
|
|
95
|
+
* for why only these two members earned a place here. Generic host code (a
|
|
96
|
+
* harness, a tier report, anything that must not care what it is mounting) can
|
|
97
|
+
* type against this; a real adapter always receives one of the per-surface
|
|
98
|
+
* contexts below.
|
|
99
|
+
*/
|
|
100
|
+
export interface HostContextBase {
|
|
101
|
+
/**
|
|
102
|
+
* The Game root (T7.1 slice 1 — D6). The host
|
|
103
|
+
* constructs the Game shell BEFORE mounting an adapter and hands it down
|
|
104
|
+
* here so an adapter can expose `ctx.game`/`ctx.roots` to the game it
|
|
105
|
+
* mounts. Absent in headless harnesses and foreign hosts that predate the
|
|
106
|
+
* Game root — everything must keep working when this is undefined (the
|
|
107
|
+
* zero-break guarantee for this slice). Typed as the PUBLIC `Game` shell
|
|
108
|
+
* here because that is the weakest promise any surface makes (a react
|
|
109
|
+
* root's host has always carried a plain `Game`); the three and canvas
|
|
110
|
+
* contexts narrow it to `GameInternal`, which is what their hosts really
|
|
111
|
+
* pass.
|
|
112
|
+
*/
|
|
113
|
+
readonly game?: Game | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* No GPU/DOM/audio available (Node conformance tests). A first-party adapter
|
|
116
|
+
* skips postprocessing/render/audio/input-map loading but still builds the
|
|
117
|
+
* scene + Rapier + components, so its scene/authoring/physics can be exercised
|
|
118
|
+
* headlessly. Browser hosts leave this false/undefined → full behavior.
|
|
119
|
+
*/
|
|
120
|
+
readonly headless?: boolean;
|
|
35
121
|
}
|
|
36
122
|
|
|
37
123
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* systems become host-provisioned.
|
|
124
|
+
* What a `three`-surface root is handed. This is the ONLY context carrying
|
|
125
|
+
* three's own objects — the split's entire point (see the header): a Pixi or
|
|
126
|
+
* React root is never handed a `WebGLRenderer` it cannot use.
|
|
42
127
|
*/
|
|
43
|
-
|
|
44
|
-
export interface SystemRegistry {}
|
|
45
|
-
|
|
46
|
-
export interface HostContext {
|
|
128
|
+
export interface ThreeHostContext extends HostContextBase {
|
|
47
129
|
/** The ONE shared three instance — identity matters for capture (see ingest). */
|
|
48
130
|
readonly three: typeof THREE;
|
|
49
131
|
/** Canvas/container + size; a self-driven game may take the surface over. */
|
|
50
132
|
readonly surface: HostSurface;
|
|
51
133
|
/** Host renderer — host-driven games render through it. */
|
|
52
134
|
readonly renderer: THREE.WebGLRenderer;
|
|
53
|
-
/**
|
|
54
|
-
readonly loop: LoopHandle;
|
|
55
|
-
/** Shared GLTF/texture cache. */
|
|
135
|
+
/** Shared GLTF/texture cache — three-flavored today; see the header. */
|
|
56
136
|
readonly assets: AssetCache;
|
|
57
|
-
/**
|
|
58
|
-
* No GPU/DOM/audio available (Node conformance tests). A first-party adapter
|
|
59
|
-
* skips postprocessing/render/audio/input-map loading but still builds the
|
|
60
|
-
* scene + Rapier + components, so its scene/authoring/physics can be exercised
|
|
61
|
-
* headlessly. Browser hosts leave this false/undefined → full behavior.
|
|
62
|
-
*/
|
|
63
|
-
readonly headless?: boolean;
|
|
64
|
-
/** Ask the host for a first-party subsystem; `null` if not provided. */
|
|
65
|
-
requestSystem<T extends keyof SystemRegistry>(kind: T): SystemRegistry[T] | null;
|
|
66
|
-
/**
|
|
67
|
-
* The Game root (T7.1 slice 1 — GAME-ROOT-DESIGN.md D6). The host
|
|
68
|
-
* constructs the Game shell BEFORE mounting a `GameAdapter` and hands it
|
|
69
|
-
* down here so an adapter can expose `ctx.game`/`ctx.roots` to the game it
|
|
70
|
-
* mounts. Absent in headless harnesses and foreign hosts that predate the
|
|
71
|
-
* Game root — everything must keep working when this is undefined (the
|
|
72
|
-
* zero-break guarantee for this slice).
|
|
73
|
-
*/
|
|
74
137
|
readonly game?: GameInternal | undefined;
|
|
75
138
|
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* What a `canvas`-surface root is handed: a raw canvas + its
|
|
142
|
+
* logical size, plus the
|
|
143
|
+
* compositing hints the multi-root host computes once for every stacked
|
|
144
|
+
* surface. No `three`, no `renderer`, no `assets` — a Pixi root builds its own
|
|
145
|
+
* `PIXI.Application` and its own asset cache from this.
|
|
146
|
+
*/
|
|
147
|
+
export interface CanvasHostContext extends HostContextBase {
|
|
148
|
+
readonly canvas: HTMLCanvasElement;
|
|
149
|
+
readonly width: number;
|
|
150
|
+
readonly height: number;
|
|
151
|
+
/** Shared Game root when mounted by the universal multi-root host. */
|
|
152
|
+
readonly game?: GameInternal | undefined;
|
|
153
|
+
/** DPR override (T6.1 slice 1, D5 §3 — "one DPR
|
|
154
|
+
* everywhere"): the roots-path host computes ONE dpr for every stacked
|
|
155
|
+
* surface and passes it here; omitted -> the canvas adapter's own
|
|
156
|
+
* default (`window.devicePixelRatio`, uncapped). */
|
|
157
|
+
readonly dpr?: number;
|
|
158
|
+
/** This root is stacked ABOVE another root (D5 §1): clear with alpha 0
|
|
159
|
+
* instead of an opaque background so the layer below shows through. */
|
|
160
|
+
readonly transparent?: boolean;
|
|
161
|
+
/** Capture tier cost (D5 §4) — the renderer's `preserveDrawingBuffer`. */
|
|
162
|
+
readonly preserveDrawingBuffer?: boolean;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* What a `dom`-surface root is handed (the shape `runtime/create-runtime.ts`
|
|
167
|
+
* used to spell `ReactRootHost`).
|
|
168
|
+
*
|
|
169
|
+
* `container` is the absolutely-positioned, z-ordered DOM-root layer `<div>`
|
|
170
|
+
* the host already created and stacked (same box/z-order rules as a canvas per
|
|
171
|
+
* D5 §1) — the adapter's `mount` renders its react tree INTO
|
|
172
|
+
* this exact element via `createRoot`; it must never create its own root
|
|
173
|
+
* element (the DOM analog of three's "the root's runtime renders into the
|
|
174
|
+
* surface it is handed" rule).
|
|
175
|
+
*
|
|
176
|
+
* `game` (inherited, and deliberately still optional): a
|
|
177
|
+
* `default-react` sibling mounted BESIDE an ingest root (`packages/editor/src/
|
|
178
|
+
* ingest-siblings.ts`) has no native `Game` to hand it — there is no
|
|
179
|
+
* first-party `GameContext`/loop for the sibling to join, only the ingested
|
|
180
|
+
* root's own foreign runtime — so its host carries no `game` at all rather than
|
|
181
|
+
* fabricating an empty one (anti-shim rule); the sibling mounts its entry
|
|
182
|
+
* component bare, with no `<WorldProvider>` wrap. `mountOneReactRoot`
|
|
183
|
+
* (`runtime/create-runtime.ts`, the NATIVE multi-root runtime path) still
|
|
184
|
+
* ALWAYS supplies a real `game` — this optionality is reached only by the
|
|
185
|
+
* composite sibling's own hand-built host, never by weakening the native path's
|
|
186
|
+
* guarantee.
|
|
187
|
+
*/
|
|
188
|
+
export interface DomHostContext extends HostContextBase {
|
|
189
|
+
readonly container: HTMLElement;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Map an {@link AdapterSurface} to the host context that surface's roots are
|
|
194
|
+
* handed — the mirror image of `MountedRootFor<K>` (`root-adapter.ts`), and
|
|
195
|
+
* what makes `RootAdapter<K>.mount`'s PARAMETER K-typed. A hypothetical 4th
|
|
196
|
+
* surface resolves to `never` here, so it cannot be mounted until it declares
|
|
197
|
+
* its own context (the same fail-to-compile discipline `assertNever` gives the
|
|
198
|
+
* dispatch sites).
|
|
199
|
+
*/
|
|
200
|
+
export type HostContextFor<K extends AdapterSurface> = K extends 'three'
|
|
201
|
+
? ThreeHostContext
|
|
202
|
+
: K extends 'canvas'
|
|
203
|
+
? CanvasHostContext
|
|
204
|
+
: K extends 'dom'
|
|
205
|
+
? DomHostContext
|
|
206
|
+
: never;
|
package/src/adapter/index.ts
CHANGED
|
@@ -1,38 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Adapter interfaces — the seams the engine host and the editor DEPEND ON.
|
|
3
3
|
*
|
|
4
|
-
* host →
|
|
5
|
-
* editor → AuthoringAdapter ← {
|
|
6
|
-
* game → SystemAdapters (physics/networking/
|
|
4
|
+
* host → RootAdapter ← { SetupThreeRootAdapter, IngestRootAdapter, … }
|
|
5
|
+
* editor → AuthoringAdapter ← { LiveThreeAuthoringAdapter, ReactRootAuthoringAdapter, … }
|
|
6
|
+
* game → SystemAdapters (physics/networking/navigation/audio/debug)
|
|
7
7
|
*
|
|
8
|
-
* The first-party
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The first-party Rapier/Colyseus stack is ONE implementer of these
|
|
9
|
+
* interfaces, not the engine's vocabulary..
|
|
10
|
+
*
|
|
11
|
+
* **This module is TYPE-ONLY, and must stay that way (P-6).** It used to
|
|
12
|
+
* value-export its own implementers — `SetupThreeRootAdapter`/`fromSetup`,
|
|
13
|
+
* `createRapierPhysicsAdapter`, `createColyseusNetworkingAdapter`,
|
|
14
|
+
* `createNavigationAdapter` — so `import type { AuthoringAdapter } from
|
|
15
|
+
* '@engine/adapter'` was the only thing keeping the three/Rapier/Colyseus/
|
|
16
|
+
* `.vscn` stack out of a Pixi or React consumer's module graph. A seam that
|
|
17
|
+
* ships its implementers is not a seam. Import an implementer from its OWN
|
|
18
|
+
* path instead:
|
|
19
|
+
*
|
|
20
|
+
* `@engine/adapter/setup-three-root-adapter` SetupThreeRootAdapter, fromSetup
|
|
21
|
+
* `@engine/adapter/rapier-physics-adapter` createRapierPhysicsAdapter
|
|
22
|
+
* `@engine/adapter/colyseus-networking-adapter` createColyseusNetworkingAdapter
|
|
23
|
+
* `@engine/adapter/first-party-systems` createNavigationAdapter
|
|
11
24
|
*/
|
|
12
25
|
|
|
13
26
|
export type { AdapterSurface } from './adapter-surface';
|
|
14
27
|
export type {
|
|
28
|
+
AssetDropContext,
|
|
15
29
|
AssetDropProvider,
|
|
30
|
+
AssetSubjectProvider,
|
|
16
31
|
AuthoringAdapter,
|
|
32
|
+
AuthoringAssetSubject,
|
|
17
33
|
AuthoringCapabilities,
|
|
18
34
|
AuthoringProvenance,
|
|
19
35
|
BoxEditProvider,
|
|
20
36
|
BoxEditReferencePoint,
|
|
21
37
|
ColorSampleProvider,
|
|
22
|
-
|
|
38
|
+
CreationSiteProvider,
|
|
23
39
|
DOMRectLike,
|
|
24
40
|
EditorNode,
|
|
25
41
|
EditorNodeRole,
|
|
26
|
-
EntityAssetProvider,
|
|
27
|
-
EntityAssetSaveDescriptor,
|
|
28
42
|
FileMapProvider,
|
|
29
43
|
HierarchyProvider,
|
|
30
44
|
InspectorProvider,
|
|
31
|
-
|
|
45
|
+
NodeCreationSite,
|
|
32
46
|
PersistenceProvider,
|
|
33
|
-
PhysicsShape2DHandle,
|
|
34
|
-
PhysicsShape2DProjection,
|
|
35
|
-
PhysicsShape2DProvider,
|
|
36
47
|
PickProvider,
|
|
37
48
|
PropertyDescriptor,
|
|
38
49
|
RectProvider,
|
|
@@ -40,46 +51,37 @@ export type {
|
|
|
40
51
|
RootGroupsProvider,
|
|
41
52
|
SelectionProvider,
|
|
42
53
|
SelectionResolution,
|
|
43
|
-
SpriteAtlas2D,
|
|
44
|
-
SpriteAtlasFrame2D,
|
|
45
|
-
SpriteFrames2DDescriptor,
|
|
46
|
-
SpriteFrames2DProvider,
|
|
47
54
|
StoriesProvider,
|
|
48
55
|
StoryRef,
|
|
49
56
|
StructureProvider,
|
|
50
57
|
TextProvider,
|
|
51
|
-
TilemapCell,
|
|
52
|
-
TilemapEditGrid,
|
|
53
|
-
TilemapEditProvider,
|
|
54
58
|
TransformChannel,
|
|
55
59
|
TransformEditability,
|
|
60
|
+
TransformObservation,
|
|
56
61
|
TransformProvider,
|
|
57
62
|
} from './authoring';
|
|
58
|
-
export {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
} from './
|
|
63
|
+
export type { ColyseusNetworkingConfig } from './colyseus-networking-adapter';
|
|
64
|
+
export type {
|
|
65
|
+
CanvasHostContext,
|
|
66
|
+
DomHostContext,
|
|
67
|
+
HostContextBase,
|
|
68
|
+
HostContextFor,
|
|
69
|
+
HostSurface,
|
|
70
|
+
ThreeHostContext,
|
|
71
|
+
} from './host-context';
|
|
67
72
|
export type {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
} from './
|
|
78
|
-
export type {
|
|
79
|
-
export { createRapierPhysicsAdapter } from './rapier-physics-adapter';
|
|
73
|
+
MountedPixiRoot,
|
|
74
|
+
MountedReactRoot,
|
|
75
|
+
MountedRoot,
|
|
76
|
+
MountedRootBase,
|
|
77
|
+
MountedThreeRoot,
|
|
78
|
+
RootAdapter,
|
|
79
|
+
RootStateObserver,
|
|
80
|
+
SurfaceAdapter,
|
|
81
|
+
SurfaceAdapterFor,
|
|
82
|
+
} from './root-adapter';
|
|
83
|
+
export type { MountedSetupThreeRoot, SetupThreeRootConfig } from './setup-three-root-adapter';
|
|
80
84
|
export type {
|
|
81
|
-
ActionValueSnapshot,
|
|
82
|
-
AssetAdapter,
|
|
83
85
|
AudioAdapter,
|
|
84
86
|
AudioDebugEvent,
|
|
85
87
|
AudioGraphNode,
|
|
@@ -87,15 +89,15 @@ export type {
|
|
|
87
89
|
AudioMeterHandle,
|
|
88
90
|
AudioTransportState,
|
|
89
91
|
ConnectionState,
|
|
90
|
-
InputAdapter,
|
|
91
|
-
NavBakeParams,
|
|
92
92
|
NavCrowdAgentState,
|
|
93
93
|
NavigationAdapter,
|
|
94
94
|
NavPoint,
|
|
95
95
|
NetConditioning,
|
|
96
96
|
NetMessageEvent,
|
|
97
97
|
NetPeer,
|
|
98
|
+
NetPlayerIdentity,
|
|
98
99
|
NetRates,
|
|
100
|
+
NetServerConfig,
|
|
99
101
|
NetworkingAdapter,
|
|
100
102
|
PhysicsAdapter,
|
|
101
103
|
RenderDebugAdapter,
|
|
@@ -105,9 +107,3 @@ export type {
|
|
|
105
107
|
Unsubscribe,
|
|
106
108
|
} from './system-adapter';
|
|
107
109
|
export type { Transform, TransformOwner } from './transform';
|
|
108
|
-
export {
|
|
109
|
-
fromSetup,
|
|
110
|
-
type VgaiMountedGame,
|
|
111
|
-
type VgaiSceneConfig,
|
|
112
|
-
VgaiSceneGameAdapter,
|
|
113
|
-
} from './vgai-scene-game-adapter';
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Relationship to the native engine: first-party content implements the SAME
|
|
12
12
|
* conceptual surface (mount root, lifecycle, loop gating) through
|
|
13
|
-
* `
|
|
13
|
+
* `RootAdapter`/`MountedThreeRoot` — the native engine is the premade 100%
|
|
14
14
|
* implementation of this contract. An ingested game climbs the same ladder
|
|
15
15
|
* endpoint by endpoint: capture infers what it can (the scene), the game
|
|
16
16
|
* declares what inference can't reach (its DOM root, its session lifecycle).
|
|
17
17
|
*
|
|
18
|
-
* Origin
|
|
18
|
+
* Origin (root), F17+F21 (lifecycle).
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
/**
|