@vgai/engine 0.5.15 → 0.5.16
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/dist/adapter/adapter-module.d.ts +228 -17
- package/dist/adapter/adapter-module.d.ts.map +1 -1
- package/dist/adapter/adapter-module.js +224 -42
- package/dist/adapter/authoring-seam-contract.d.ts +530 -0
- package/dist/adapter/authoring-seam-contract.d.ts.map +1 -0
- package/dist/adapter/authoring-seam-contract.js +162 -0
- package/dist/adapter/authoring.d.ts +237 -50
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +2 -1
- package/dist/adapter/entrypoint-selection-source.d.ts +35 -0
- package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -0
- package/dist/adapter/entrypoint-selection-source.js +151 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts +7 -1
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +142 -21
- package/dist/adapter/first-party-systems.d.ts +13 -3
- package/dist/adapter/first-party-systems.d.ts.map +1 -1
- package/dist/adapter/first-party-systems.js +10 -3
- package/dist/adapter/index.d.ts +7 -2
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +4 -0
- package/dist/adapter/ingest/contract-system-adapters.d.ts +1 -1
- package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -1
- package/dist/adapter/ingest/contract-system-adapters.js +13 -25
- package/dist/adapter/ingest/game-contract-seams.d.ts +214 -0
- package/dist/adapter/ingest/game-contract-seams.d.ts.map +1 -0
- package/dist/adapter/ingest/game-contract-seams.js +62 -0
- package/dist/adapter/ingest/game-contract.d.ts +5 -3
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +4 -1
- package/dist/adapter/ingest/merge-debug-adapters.d.ts +52 -0
- package/dist/adapter/ingest/merge-debug-adapters.d.ts.map +1 -0
- package/dist/adapter/ingest/merge-debug-adapters.js +141 -0
- package/dist/adapter/ingest/observation-debug-adapter.d.ts +56 -0
- package/dist/adapter/ingest/observation-debug-adapter.d.ts.map +1 -0
- package/dist/adapter/ingest/observation-debug-adapter.js +131 -0
- package/dist/adapter/ingest/scene-capture.d.ts +38 -13
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +101 -24
- package/dist/adapter/ingest/visible-capture-window.d.ts +22 -5
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
- package/dist/adapter/ingest/visible-capture-window.js +46 -7
- package/dist/adapter/native-debug-module.d.ts +43 -0
- package/dist/adapter/native-debug-module.d.ts.map +1 -0
- package/dist/adapter/native-debug-module.js +230 -0
- package/dist/adapter/object3d-authoring-subject.d.ts +32 -0
- package/dist/adapter/object3d-authoring-subject.d.ts.map +1 -0
- package/dist/adapter/object3d-authoring-subject.js +25 -0
- package/dist/adapter/root-adapter.d.ts +21 -56
- package/dist/adapter/root-adapter.d.ts.map +1 -1
- package/dist/adapter/root-adapter.js +4 -20
- package/dist/adapter/root-seam-contract.d.ts +124 -0
- package/dist/adapter/root-seam-contract.d.ts.map +1 -0
- package/dist/adapter/root-seam-contract.js +36 -0
- package/dist/adapter/seam-evidence.d.ts +118 -0
- package/dist/adapter/seam-evidence.d.ts.map +1 -0
- package/dist/adapter/seam-evidence.js +231 -0
- package/dist/adapter/setup-three-root-adapter.d.ts +2 -3
- package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -1
- package/dist/adapter/setup-three-root-adapter.js +2 -3
- package/dist/adapter/system-adapter.d.ts +6 -1
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-seam-contract.d.ts +354 -0
- package/dist/adapter/system-seam-contract.d.ts.map +1 -0
- package/dist/adapter/system-seam-contract.js +95 -0
- package/dist/animation/xstate-animation-binding.d.ts +6 -0
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +142 -4
- package/dist/asset-formats/collider.d.ts +1 -1
- package/dist/asset-formats/mesh.d.ts +2 -2
- package/dist/asset-formats/particles.d.ts +1 -1
- package/dist/canvas-react/engine-bridge.d.ts +6 -6
- package/dist/canvas-react/engine-bridge.d.ts.map +1 -1
- package/dist/canvas-react/engine-bridge.js +2 -2
- package/dist/canvas-react/index.d.ts +2 -2
- package/dist/canvas-react/index.d.ts.map +1 -1
- package/dist/canvas-react/index.js +1 -1
- package/dist/canvas-react/pixi-react-adapter.d.ts +15 -9
- package/dist/canvas-react/pixi-react-adapter.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-adapter.js +43 -29
- package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-root-factory.js +4 -0
- package/dist/canvas-react/world-context.d.ts +22 -12
- package/dist/canvas-react/world-context.d.ts.map +1 -1
- package/dist/canvas-react/world-context.js +12 -51
- package/dist/ecs/user-data.d.ts +5 -0
- package/dist/ecs/user-data.d.ts.map +1 -1
- package/dist/ecs/user-data.js +3 -0
- package/dist/input/input-manager.d.ts +49 -0
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +127 -0
- package/dist/manifest/schema.d.ts +9 -9
- package/dist/pixi/authoring.d.ts.map +1 -1
- package/dist/pixi/authoring.js +220 -1
- package/dist/pixi/ingest.d.ts +2 -1
- package/dist/pixi/ingest.d.ts.map +1 -1
- package/dist/pixi/ingest.js +4 -2
- package/dist/pixi/scene-capture.d.ts +9 -1
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/pixi/scene-capture.js +9 -6
- package/dist/render/particles-factory.d.ts +13 -6
- package/dist/render/particles-factory.d.ts.map +1 -1
- package/dist/render/particles-factory.js +6 -3
- package/dist/runtime/create-runtime.d.ts +23 -46
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +22 -67
- package/dist/runtime/game.d.ts +29 -71
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +15 -54
- package/dist/runtime/mount-game.d.ts.map +1 -1
- package/dist/runtime/mount-game.js +12 -1
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +4 -5
- package/dist/runtime/types.d.ts +18 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/setup/setup-renderer.d.ts +3 -4
- package/dist/setup/setup-renderer.d.ts.map +1 -1
- package/dist/setup/setup-renderer.js +3 -4
- package/dist/world3d-react/engine-bridge.d.ts +1 -1
- package/dist/world3d-react/engine-bridge.js +1 -1
- package/dist/world3d-react/r3f-adapter.d.ts +7 -0
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
- package/dist/world3d-react/r3f-adapter.js +50 -21
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
- package/dist/world3d-react/r3f-root-factory.js +4 -0
- package/dist/world3d-react/world-context.d.ts +9 -3
- package/dist/world3d-react/world-context.d.ts.map +1 -1
- package/dist/world3d-react/world-context.js +5 -18
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +7 -7
- package/src/adapter/adapter-module.ts +407 -50
- package/src/adapter/authoring-seam-contract.ts +211 -0
- package/src/adapter/authoring.ts +242 -50
- package/src/adapter/entrypoint-selection-source.ts +159 -0
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +159 -28
- package/src/adapter/first-party-systems.ts +17 -3
- package/src/adapter/index.ts +51 -1
- package/src/adapter/ingest/contract-system-adapters.ts +26 -30
- package/src/adapter/ingest/game-contract-seams.ts +85 -0
- package/src/adapter/ingest/game-contract.ts +9 -4
- package/src/adapter/ingest/merge-debug-adapters.ts +193 -0
- package/src/adapter/ingest/observation-debug-adapter.ts +158 -0
- package/src/adapter/ingest/scene-capture.ts +126 -23
- package/src/adapter/ingest/visible-capture-window.ts +63 -9
- package/src/adapter/native-debug-module.ts +307 -0
- package/src/adapter/object3d-authoring-subject.ts +50 -0
- package/src/adapter/root-adapter.ts +21 -56
- package/src/adapter/root-seam-contract.ts +56 -0
- package/src/adapter/seam-evidence.ts +336 -0
- package/src/adapter/setup-three-root-adapter.ts +4 -6
- package/src/adapter/system-adapter.ts +6 -1
- package/src/adapter/system-seam-contract.ts +121 -0
- package/src/animation/xstate-animation-binding.ts +151 -4
- package/src/canvas-react/engine-bridge.ts +6 -6
- package/src/canvas-react/index.ts +2 -1
- package/src/canvas-react/pixi-react-adapter.tsx +55 -34
- package/src/canvas-react/pixi-react-root-factory.tsx +4 -0
- package/src/canvas-react/world-context.ts +26 -101
- package/src/ecs/user-data.ts +5 -0
- package/src/input/input-manager.ts +122 -0
- package/src/pixi/authoring.ts +236 -1
- package/src/pixi/ingest.ts +9 -2
- package/src/pixi/scene-capture.ts +20 -5
- package/src/render/particles-factory.ts +16 -6
- package/src/runtime/create-runtime.ts +41 -106
- package/src/runtime/game.ts +41 -111
- package/src/runtime/mount-game.ts +19 -1
- package/src/runtime/mount-manifest.ts +4 -5
- package/src/runtime/types.ts +22 -0
- package/src/setup/setup-renderer.ts +3 -4
- package/src/world3d-react/engine-bridge.ts +1 -1
- package/src/world3d-react/r3f-adapter.tsx +64 -24
- package/src/world3d-react/r3f-root-factory.tsx +4 -0
- package/src/world3d-react/world-context.ts +11 -36
|
@@ -79,12 +79,24 @@ interface PixiLike {
|
|
|
79
79
|
Application: { prototype: Record<string, unknown> };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export interface SceneCapture2DOptions {
|
|
83
|
+
/**
|
|
84
|
+
* Select the runtime owned by this mount when other Pixi Applications share
|
|
85
|
+
* the page (for example editor prefab previews). Rejected draws still count,
|
|
86
|
+
* but cannot satisfy {@link SceneCapture2DHandle.waitForCapture}.
|
|
87
|
+
*/
|
|
88
|
+
accept?: ((runtime: CapturedRuntime2D) => boolean) | undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
/**
|
|
83
92
|
* Install the render trap on `pixiNamespace.Application.prototype.render`. Pass the
|
|
84
93
|
* host's `pixi.js` namespace so a shared-instance game is trapped. Install once per
|
|
85
94
|
* ingest session; `uninstall()` on teardown.
|
|
86
95
|
*/
|
|
87
|
-
export function installSceneCapture2D(
|
|
96
|
+
export function installSceneCapture2D(
|
|
97
|
+
pixiNamespace: unknown,
|
|
98
|
+
options: SceneCapture2DOptions = {},
|
|
99
|
+
): SceneCapture2DHandle {
|
|
88
100
|
const PIXI = pixiNamespace as PixiLike;
|
|
89
101
|
const proto = PIXI.Application.prototype;
|
|
90
102
|
|
|
@@ -102,8 +114,11 @@ export function installSceneCapture2D(pixiNamespace: unknown): SceneCapture2DHan
|
|
|
102
114
|
drawCount++;
|
|
103
115
|
const stage = this['stage'];
|
|
104
116
|
if (!captured && stage) {
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
const candidate = { stage, renderer: this['renderer'], app: this };
|
|
118
|
+
if (options.accept?.(candidate) ?? true) {
|
|
119
|
+
captured = candidate;
|
|
120
|
+
for (const resolve of waiters.splice(0)) resolve(captured);
|
|
121
|
+
}
|
|
107
122
|
}
|
|
108
123
|
return realRender?.apply(this, args);
|
|
109
124
|
},
|
|
@@ -138,8 +153,8 @@ export function installSceneCapture2D(pixiNamespace: unknown): SceneCapture2DHan
|
|
|
138
153
|
reject(
|
|
139
154
|
new Error(
|
|
140
155
|
`pixi scene capture timed out after ${timeoutMs}ms of VISIBLE time ` +
|
|
141
|
-
`(${Math.round(captureWindow.elapsedHiddenMs())}ms
|
|
142
|
-
'
|
|
156
|
+
`(${Math.round(captureWindow.elapsedHiddenMs())}ms browser-suspended, which is ` +
|
|
157
|
+
'not counted because no frame can be presented) — the game never rendered, or it bundles its own ' +
|
|
143
158
|
'(un-shared) copy of pixi.js.',
|
|
144
159
|
),
|
|
145
160
|
);
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ApplyForce,
|
|
14
14
|
ChangeEmitDirection,
|
|
15
15
|
CircleEmitter,
|
|
16
|
+
type ColorGenerator,
|
|
16
17
|
ColorGeneratorFromJSON,
|
|
17
18
|
ColorOverLife,
|
|
18
19
|
ConeEmitter,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
type EmitterShape,
|
|
21
22
|
ForceOverLife,
|
|
22
23
|
FrameOverLife,
|
|
24
|
+
type FunctionColorGenerator,
|
|
23
25
|
GravityForce,
|
|
24
26
|
GridEmitter,
|
|
25
27
|
HemisphereEmitter,
|
|
@@ -345,9 +347,9 @@ export interface ParticleSystemResult {
|
|
|
345
347
|
* This is how a caller with fidelity a plain descriptor cannot express reaches
|
|
346
348
|
* the SAME factory instead of hand-building its own `new ParticleSystem(...)`:
|
|
347
349
|
* a foreign-engine carry (e.g. Godot CPUParticles) supplies its own
|
|
348
|
-
* `EmitterShape
|
|
349
|
-
* else — lifecycle, start values, emission,
|
|
350
|
-
* JSON.
|
|
350
|
+
* `EmitterShape`, start-colour generator, and instancing geometry here, and
|
|
351
|
+
* expresses everything else — lifecycle, scalar start values, emission,
|
|
352
|
+
* behaviors, material — as descriptor JSON.
|
|
351
353
|
*/
|
|
352
354
|
export interface ParticleSystemObjects {
|
|
353
355
|
/**
|
|
@@ -356,6 +358,12 @@ export interface ParticleSystemObjects {
|
|
|
356
358
|
* present it REPLACES `data.shape`.
|
|
357
359
|
*/
|
|
358
360
|
shape?: EmitterShape;
|
|
361
|
+
/**
|
|
362
|
+
* A pre-built start-colour generator for a per-particle draw the descriptor's
|
|
363
|
+
* JSON generator vocabulary cannot express. When present it REPLACES
|
|
364
|
+
* `data.startColor`.
|
|
365
|
+
*/
|
|
366
|
+
startColor?: ColorGenerator | FunctionColorGenerator;
|
|
359
367
|
/**
|
|
360
368
|
* The geometry instanced per particle in `RenderMode.Mesh`. A
|
|
361
369
|
* `BufferGeometry` is not JSON-serializable, so `renderMode: 'mesh'` requires
|
|
@@ -377,8 +385,9 @@ export interface ParticleSystemObjects {
|
|
|
377
385
|
* Create a three.quarks ParticleSystem from ParticlesDescriptor JSON data.
|
|
378
386
|
*
|
|
379
387
|
* `objects` carries the pieces a JSON descriptor cannot: a plugin emitter
|
|
380
|
-
* shape, a mesh-mode instancing geometry, and the
|
|
381
|
-
* optional and additive — a call with only `data`
|
|
388
|
+
* shape, a start-colour generator, a mesh-mode instancing geometry, and the
|
|
389
|
+
* default-sprite toggle. It is optional and additive — a call with only `data`
|
|
390
|
+
* behaves exactly as before.
|
|
382
391
|
*/
|
|
383
392
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: straightforward field-by-field mapping
|
|
384
393
|
export function createParticleSystemFromData(
|
|
@@ -410,7 +419,8 @@ export function createParticleSystemFromData(
|
|
|
410
419
|
if (data.startSpeed) params['startSpeed'] = valueGen(data.startSpeed);
|
|
411
420
|
if (data.startSize) params['startSize'] = valueGen(data.startSize);
|
|
412
421
|
if (data.startRotation) params['startRotation'] = valueGen(data.startRotation);
|
|
413
|
-
if (
|
|
422
|
+
if (objects?.startColor) params['startColor'] = objects.startColor;
|
|
423
|
+
else if (data.startColor) params['startColor'] = colorGen(data.startColor);
|
|
414
424
|
if (data.startTileIndex) params['startTileIndex'] = valueGen(data.startTileIndex);
|
|
415
425
|
|
|
416
426
|
// Trail mode requires startLength inside rendererEmitterSettings, not as a top-level param
|
|
@@ -31,12 +31,11 @@ import type { PlaytestContext } from './types';
|
|
|
31
31
|
* by `createGameRuntime` and every headless test harness
|
|
32
32
|
* (`test/game-root.test.ts`, `test/frame-order.test.ts`,
|
|
33
33
|
* `test/game-two-roots.test.ts`) so there is exactly one code path for this
|
|
34
|
-
* wiring
|
|
35
|
-
* 3) — `physics`/`collisions`/`camera`/ `frame` are populated from the mount's
|
|
34
|
+
* wiring. `physics`/`collisions`/`camera`/`frame` are populated from the mount's
|
|
36
35
|
* first-party `GameContext`/ `MountedSetupThreeRoot` when available, left
|
|
37
36
|
* `undefined` otherwise (an external adapter's mount has none of these
|
|
38
37
|
* first-party handles — `frame` in particular is what makes
|
|
39
|
-
* `GameInternal.runFrame`
|
|
38
|
+
* `GameInternal.runFrame` fall back to a single opaque
|
|
40
39
|
* `mounted.update` call per substep for such a world).
|
|
41
40
|
*/
|
|
42
41
|
function firstPartyExtras(
|
|
@@ -53,10 +52,8 @@ function firstPartyExtras(
|
|
|
53
52
|
|
|
54
53
|
/**
|
|
55
54
|
* Register a three world onto the Game shell. `opts.id` defaults to
|
|
56
|
-
* `'main'
|
|
57
|
-
*
|
|
58
|
-
* real multi-world manifests later) pass an explicit id. This is the ONE
|
|
59
|
-
* registration code path for a three world, whatever its id.
|
|
55
|
+
* `'main'`; multi-root callers pass an explicit id. This is the one
|
|
56
|
+
* registration path for a three world, whatever its id.
|
|
60
57
|
*/
|
|
61
58
|
export function registerThreeRoot(
|
|
62
59
|
game: GameInternal,
|
|
@@ -104,13 +101,13 @@ export function registerPixiRoot(
|
|
|
104
101
|
}
|
|
105
102
|
|
|
106
103
|
/**
|
|
107
|
-
* Register a
|
|
104
|
+
* Register a React world onto the Game shell — the DOM
|
|
108
105
|
* analog of {@link registerThreeRoot}/{@link registerPixiRoot}. A react
|
|
109
106
|
* world has no `frame` hooks (react's own
|
|
110
107
|
* `createRoot` schedules its commits; `GameInternal.runFrame` correctly
|
|
111
108
|
* leaves a world with no `frame` untouched by its opaque-`update` fallback
|
|
112
109
|
* too, since `MountedReactGame` declares no `update`). `adapter` is typed
|
|
113
|
-
* as the real {@link ReactRootAdapter} shape (
|
|
110
|
+
* as the real {@link ReactRootAdapter} shape (same reasoning as
|
|
114
111
|
* `registerPixiRoot`'s doc comment above) — `RootInstance.adapter` only
|
|
115
112
|
* needs `.id`, so this passes through with zero cast.
|
|
116
113
|
*/
|
|
@@ -134,12 +131,12 @@ export function registerReactRoot(
|
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
/**
|
|
137
|
-
* One world to mount in the
|
|
138
|
-
*
|
|
139
|
-
*
|
|
134
|
+
* One world to mount in the universal host — the host-facing mirror of
|
|
135
|
+
* `manifest/load.ts`'s `ResolvedAdapterRoot` (same identity and presentation
|
|
136
|
+
* fields; the
|
|
140
137
|
* manifest-to-host translation itself is the editor's job, not this file's).
|
|
141
138
|
*
|
|
142
|
-
* `
|
|
139
|
+
* A `dom` root mounts as a DOM layer `<div>` in the same
|
|
143
140
|
* stack instead of a canvas — see {@link ReactRootMountSpec}/{@link
|
|
144
141
|
* ReactRootAdapter} below.
|
|
145
142
|
*/
|
|
@@ -168,12 +165,6 @@ export interface RootMountSpecBase {
|
|
|
168
165
|
* never sees DOM roots at all.
|
|
169
166
|
*/
|
|
170
167
|
readonly dev?: boolean | undefined;
|
|
171
|
-
/** `'gated'` (host-driven, default) or `'self-driven'` (this world drives
|
|
172
|
-
* its own loop — D5's "composited, unsynchronized" tier). Carried through
|
|
173
|
-
* for parity with `ResolvedAdapterRoot`; T6.1 slice 1 does not yet
|
|
174
|
-
* validate it against the mounted adapter's actual `drivesOwnLoop` (that
|
|
175
|
-
* cross-check, if ever needed, is T7.6's loop-gate surface). */
|
|
176
|
-
readonly loop?: 'gated' | 'self-driven' | undefined;
|
|
177
168
|
/**
|
|
178
169
|
* Optional claim predicate for the delegating input router (D5 §2a), over
|
|
179
170
|
* a point RELATIVE TO THE CONTAINER. Absent means: this world claims only
|
|
@@ -199,8 +190,8 @@ export interface PixiRootMountSpec extends RootMountSpecBase {
|
|
|
199
190
|
}
|
|
200
191
|
|
|
201
192
|
/**
|
|
202
|
-
* A live, mounted
|
|
203
|
-
*
|
|
193
|
+
* A live, mounted React world — the DOM analog of a mounted canvas world.
|
|
194
|
+
* React roots render from game state
|
|
204
195
|
* instead of a per-frame `update`, so this shape carries no `update`/`fixedUpdate`/
|
|
205
196
|
* `ctx`/`frame` — `drivesOwnLoop` is always `false` (react's `createRoot`
|
|
206
197
|
* schedules its OWN commits; the host's fixed-step loop never drives it,
|
|
@@ -217,10 +208,10 @@ export interface PixiRootMountSpec extends RootMountSpecBase {
|
|
|
217
208
|
* `kind: 'dom'` (§7.1-15): a react world has no `observe` BY DESIGN — that
|
|
218
209
|
* hook is scoped to `useRootObservation` (the ingested/foreign-world case),
|
|
219
210
|
* not `useWorldState` (`ui/game-state.tsx`), which a react world's own
|
|
220
|
-
* mounted tree uses instead — it reads `Game.state` directly
|
|
221
|
-
*
|
|
211
|
+
* mounted tree uses instead — it reads `Game.state` directly, never a
|
|
212
|
+
* per-world `observe`.
|
|
222
213
|
*
|
|
223
|
-
* `kind`/`container`
|
|
214
|
+
* `kind`/`container` satisfy `MountedReactRoot` (`adapter/
|
|
224
215
|
* root-adapter.ts`) — `container` is the SAME `DomHostContext.container` the
|
|
225
216
|
* adapter's `mount` was handed (identity matters, mirroring `threeScene()`/
|
|
226
217
|
* `pixiStage()`'s "same instance the adapter mounted" contract); every
|
|
@@ -236,7 +227,7 @@ export interface MountedReactGame extends MountedReactRoot {
|
|
|
236
227
|
* A react-shaped adapter — the structural contract `RootMountSpec`'s
|
|
237
228
|
* `react` variant requires. Deliberately
|
|
238
229
|
* NOT tied to a concrete implementer here so an editor-resolved adapter (the
|
|
239
|
-
* `default-react` resolver branch
|
|
230
|
+
* `default-react` resolver branch) can satisfy
|
|
240
231
|
* this shape without this file importing react-dom or any editor code.
|
|
241
232
|
*
|
|
242
233
|
* A genuine `RootAdapter<'dom'>` refinement (`HostContextFor<'dom'>` =
|
|
@@ -328,37 +319,23 @@ export interface RootsRuntimeConfig {
|
|
|
328
319
|
*
|
|
329
320
|
* This is the GENERIC host handle — it has no first-party concepts (no
|
|
330
321
|
* `GameContext`, no `GameSetupFn`). First-party features (the live `GameContext`,
|
|
331
|
-
* warm-restart hot reload) are reached
|
|
322
|
+
* warm-restart hot reload) are reached through `game.defaultRoot.mounted`
|
|
323
|
+
* and narrowed to `MountedSetupThreeRoot`
|
|
332
324
|
* (the editor does this for HMR/physics-sync — those are inherently first-party).
|
|
333
325
|
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
* world) by DECLARATION order, per `Game.defaultRoot`'s existing rule —
|
|
337
|
-
* independent of `zOrder`/canvas stacking, which is a rendering-only concern.
|
|
326
|
+
* Root access is explicit through `game.roots`, `game.world(id)`, and
|
|
327
|
+
* `game.defaultRoot`; the session does not project a surface-specific alias.
|
|
338
328
|
*/
|
|
339
329
|
export interface GameSession {
|
|
340
330
|
stop(): void;
|
|
341
331
|
pause(): void;
|
|
342
332
|
resume(): void;
|
|
343
333
|
step(): void;
|
|
344
|
-
/** Resize every world's render buffer. `pixelRatio`
|
|
345
|
-
*
|
|
346
|
-
* the mount-time ratio is left untouched (legacy behavior). */
|
|
334
|
+
/** Resize every world's render buffer. `pixelRatio` optionally re-pins the
|
|
335
|
+
* three renderers' DPR in the same pass. */
|
|
347
336
|
resize(width: number, height: number, pixelRatio?: number): void;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
/** The default world's mounted root, as the honest `MountedRoot` union —
|
|
351
|
-
* narrow on `.kind` before reaching for a surface's own members (the same
|
|
352
|
-
* discriminant `scene`/`camera` above use). This deliberately does NOT
|
|
353
|
-
* claim `MountedThreeRoot`: a roots-path session whose default world is
|
|
354
|
-
* canvas or dom returns that world's mount, and the old
|
|
355
|
-
* `as MountedThreeRoot` cast made every such value a type-level lie. For a
|
|
356
|
-
* first-party three mount, narrow to `'three'` and cast to
|
|
357
|
-
* `MountedSetupThreeRoot` for the first-party extras. */
|
|
358
|
-
readonly mounted: MountedRoot;
|
|
359
|
-
/** The Game root (T6.1 slice 1) — the multi-world entry point
|
|
360
|
-
* (`game.roots`/`game.world(id)`/`game.queryByComponent`) for callers
|
|
361
|
-
* that need more than the default-world aliases above. */
|
|
337
|
+
/** The multi-root entry point. Root access stays explicit through
|
|
338
|
+
* `game.roots`, `game.world(id)`, and `game.defaultRoot`. */
|
|
362
339
|
readonly game: Game;
|
|
363
340
|
}
|
|
364
341
|
|
|
@@ -374,7 +351,7 @@ export async function createGameRuntime(config: RootsRuntimeConfig): Promise<Gam
|
|
|
374
351
|
}
|
|
375
352
|
|
|
376
353
|
// ---------------------------------------------------------------------------
|
|
377
|
-
//
|
|
354
|
+
// Universal root host
|
|
378
355
|
// ---------------------------------------------------------------------------
|
|
379
356
|
|
|
380
357
|
/** A stand-in `THREE.WebGLRenderer` for headless (`headless:true`) three
|
|
@@ -397,7 +374,7 @@ function createHeadlessRendererStub(): THREE.WebGLRenderer {
|
|
|
397
374
|
|
|
398
375
|
/** One already-mounted world, tracked for disposal + the router. `element`
|
|
399
376
|
* is the world's stacked surface — an `HTMLCanvasElement` for three/
|
|
400
|
-
* canvas, or the DOM-root layer `<div>` for
|
|
377
|
+
* canvas, or the DOM-root layer `<div>` for React — kept
|
|
401
378
|
* under one field name so `fullCleanup`'s disposal loop stays kind-generic
|
|
402
379
|
* (`container.removeChild(entry.element)` needs no branch). */
|
|
403
380
|
interface MountedAdapterRoot {
|
|
@@ -477,11 +454,8 @@ async function mountOneThreeRoot(
|
|
|
477
454
|
// resize-staleness (the roots-path `resize()` below always calls
|
|
478
455
|
// `setSize(rw, rh, false)`, so nothing else was ever going to update this
|
|
479
456
|
// canvas' CSS after mount). `createHostRenderer`'s own default
|
|
480
|
-
// (`updateStyle:true`) is intentionally left alone
|
|
481
|
-
//
|
|
482
|
-
// relies on that construction-time stamp + its own later `updateStyle:true`
|
|
483
|
-
// resizes for byte-identical behavior; this fix touches only the
|
|
484
|
-
// roots-path canvas, after the fact.
|
|
457
|
+
// (`updateStyle:true`) is intentionally left alone; the host owns CSS layout
|
|
458
|
+
// and reasserts the container-relative size after renderer construction.
|
|
485
459
|
canvas.style.width = '100%';
|
|
486
460
|
canvas.style.height = '100%';
|
|
487
461
|
renderer.setPixelRatio(dpr);
|
|
@@ -559,7 +533,7 @@ async function mountOnePixiRoot(
|
|
|
559
533
|
}
|
|
560
534
|
|
|
561
535
|
/**
|
|
562
|
-
* Mount one
|
|
536
|
+
* Mount one React `RootMountSpec` — the DOM sibling of
|
|
563
537
|
* `mountOneThreeRoot`/`mountOnePixiRoot`. Unlike its canvas-backed siblings
|
|
564
538
|
* this returns NO `routerEntry`: a react world's DOM-root layer participates in
|
|
565
539
|
* D5's z-order/box stacking (the caller still creates and positions its `<div>`
|
|
@@ -592,7 +566,7 @@ async function mountOneReactRoot(
|
|
|
592
566
|
// An absolutely-positioned layer with no width/height collapses to zero
|
|
593
567
|
// content size, so a child's
|
|
594
568
|
// own position:absolute offsets resolve against a degenerate containing
|
|
595
|
-
// block
|
|
569
|
+
// block; otherwise clicks land outside the game.
|
|
596
570
|
layer.style.width = '100%';
|
|
597
571
|
layer.style.height = '100%';
|
|
598
572
|
const reactHost: DomHostContext = { container: layer, game };
|
|
@@ -613,8 +587,8 @@ async function mountOneReactRoot(
|
|
|
613
587
|
}
|
|
614
588
|
|
|
615
589
|
/**
|
|
616
|
-
* The
|
|
617
|
-
*
|
|
590
|
+
* The universal host implementer behind {@link createGameRuntime}. It builds
|
|
591
|
+
* one surface per world — a
|
|
618
592
|
* canvas for three/canvas, a DOM-root `<div>` layer for react — stacked
|
|
619
593
|
* per D5 §1, z-order/ties exactly matching
|
|
620
594
|
* `manifest/load.ts`'s sort, ONE `Game`, and registers every world onto it
|
|
@@ -628,16 +602,11 @@ async function mountOneReactRoot(
|
|
|
628
602
|
|
|
629
603
|
/**
|
|
630
604
|
* Dev/e2e-only `window.__vgaiScene`/`__vgaiCamera` exposure for the roots
|
|
631
|
-
* path's
|
|
632
|
-
* to keep that function's own cyclomatic complexity down.
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
* non-threejs default world (or none at all) publishes neither global,
|
|
637
|
-
* exactly like those getters return `undefined` in that case. Returns a
|
|
638
|
-
* retraction callback — identity-guarded (a newer session's globals must
|
|
639
|
-
* never be clobbered by an older session's stop, same reason the legacy
|
|
640
|
-
* path's own cleanup guards it) — that is a no-op when nothing was
|
|
605
|
+
* path's default world — split out of `createRootsGameRuntime` purely
|
|
606
|
+
* to keep that function's own cyclomatic complexity down. It uses the
|
|
607
|
+
* "first three world, else none" rule: a non-Three default world publishes
|
|
608
|
+
* neither global. Returns an identity-guarded retraction callback so an
|
|
609
|
+
* older session's stop cannot clobber a newer session's globals. It is a no-op when nothing was
|
|
641
610
|
* published (non-DEV build, or non-threejs default world).
|
|
642
611
|
*/
|
|
643
612
|
function installDefaultRootDevGlobals(game: GameInternal): () => void {
|
|
@@ -804,8 +773,7 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
804
773
|
// --- Surface stack (D5 §1): DOM/z-order follows zOrder, ties -> array order
|
|
805
774
|
// — computed FIRST (bottom -> top) so both the z-index assignment below and
|
|
806
775
|
// the router's default-claim rule share one definition. A react world's
|
|
807
|
-
// DOM-root layer shares this SAME stacking pass
|
|
808
|
-
// stacking model, no special case") even though it is a `<div>`, not a
|
|
776
|
+
// DOM-root layer shares this SAME stacking pass even though it is a `<div>`, not a
|
|
809
777
|
// canvas, and carries no `hitTest` (the router never sees react entries at
|
|
810
778
|
// all — see the dispatch loop below). ---
|
|
811
779
|
// Dev layers sit TOPMOST, above every non-dev root, whatever zOrder either
|
|
@@ -862,7 +830,7 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
862
830
|
// too (not just in `mountOneThreeRoot`) means every surface, whatever
|
|
863
831
|
// kind, starts container-relative from its very first paint, before any
|
|
864
832
|
// per-kind mount work has even run.
|
|
865
|
-
// `contain:layout paint` makes each surface the
|
|
833
|
+
// `contain:layout paint` makes each surface the containing block for
|
|
866
834
|
// `position:fixed` descendants (and clips overflow to the world's
|
|
867
835
|
// rectangle): full-screen game UI written the natural way (`fixed;
|
|
868
836
|
// inset:0`) then fills the WORLD, not the page. Without it, `fixed` UI
|
|
@@ -936,17 +904,7 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
936
904
|
started = true;
|
|
937
905
|
loop.start();
|
|
938
906
|
|
|
939
|
-
// Expose scene
|
|
940
|
-
// single-world path's identical dev-only exposure above, generalized to
|
|
941
|
-
// the roots path's DEFAULT world (E4): `mountGameFromManifest`/
|
|
942
|
-
// `mountManifestRoots` route every caller (including a
|
|
943
|
-
// single-threejs-world scaffold project) through THIS path, so a caller
|
|
944
|
-
// migrating off the legacy `{canvas, adapter}` call must not silently
|
|
945
|
-
// lose `window.__vgaiScene`/`__vgaiCamera` — real e2e/dev tooling
|
|
946
|
-
// depends on them
|
|
947
|
-
// (`packages/editor/e2e/tests/04-standalone-game.spec.ts`). Split into
|
|
948
|
-
// its own top-level helper (with its retraction counterpart below)
|
|
949
|
-
// purely to keep this function's own cyclomatic complexity down.
|
|
907
|
+
// Expose the default Three root's scene and camera for dev/e2e tooling.
|
|
950
908
|
const retractDevGlobals = installDefaultRootDevGlobals(game);
|
|
951
909
|
|
|
952
910
|
// G3/FT-11 — publish `window.__vgaiRender` for the deterministic capture
|
|
@@ -986,12 +944,7 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
986
944
|
|
|
987
945
|
return {
|
|
988
946
|
stop: fullCleanup,
|
|
989
|
-
//
|
|
990
|
-
// semantics itself now (see the legacy path's identical comment above) —
|
|
991
|
-
// a `pausable: false` world (a menu/HUD world) keeps ticking while every
|
|
992
|
-
// other world freezes, which the old blind `for (const entry of
|
|
993
|
-
// mountedEntries) entry.mounted.setPaused?.(true)` fan-out (with no
|
|
994
|
-
// `pausable` check at all) could never express.
|
|
947
|
+
// `Game.play` owns per-world pause, loop-gate, and audio-gate semantics.
|
|
995
948
|
pause() {
|
|
996
949
|
game.play.pause();
|
|
997
950
|
},
|
|
@@ -1014,24 +967,6 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
1014
967
|
entry.mounted.resize?.(safeWidth, safeHeight);
|
|
1015
968
|
}
|
|
1016
969
|
},
|
|
1017
|
-
// `GameSession.scene`/`.camera`/`.mounted` alias `Game.defaultRoot` (T6.1
|
|
1018
|
-
// slice 1's "first three world, else first world" rule) — a legacy
|
|
1019
|
-
// convenience shaped for the threejs-only past. T7.5 narrows the read via
|
|
1020
|
-
// the `kind` discriminant instead of a blind `.scene`/`.camera` cast
|
|
1021
|
-
// through a nonexistent property (identical behavior to before: still
|
|
1022
|
-
// `undefined` for a roots-path session whose default world isn't
|
|
1023
|
-
// three — real per-world surface routing for that case is T7.6's).
|
|
1024
|
-
get scene() {
|
|
1025
|
-
const m = game.defaultRoot.mounted;
|
|
1026
|
-
return (m.kind === 'three' ? m.scene : undefined) as THREE.Scene;
|
|
1027
|
-
},
|
|
1028
|
-
get camera() {
|
|
1029
|
-
const m = game.defaultRoot.mounted;
|
|
1030
|
-
return (m.kind === 'three' ? m.camera : undefined) as THREE.PerspectiveCamera;
|
|
1031
|
-
},
|
|
1032
|
-
get mounted() {
|
|
1033
|
-
return game.defaultRoot.mounted;
|
|
1034
|
-
},
|
|
1035
970
|
game,
|
|
1036
971
|
};
|
|
1037
972
|
}
|