@vgai/engine 0.5.15 → 0.5.17
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 +229 -33
- package/dist/adapter/adapter-module.d.ts.map +1 -1
- package/dist/adapter/adapter-module.js +222 -50
- 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 -3
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +145 -27
- 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 +25 -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 +129 -0
- package/dist/adapter/root-seam-contract.d.ts.map +1 -0
- package/dist/adapter/root-seam-contract.js +37 -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 +11 -12
- 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 +129 -64
- 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/data/curve.d.ts +1 -1
- package/dist/data/curve.js +2 -2
- package/dist/dev/instruments.d.ts +4 -4
- package/dist/dev/instruments.js +4 -4
- package/dist/dev/register-render-vitals.d.ts +2 -2
- package/dist/dev/register-render-vitals.js +2 -2
- package/dist/dev/static-batch-advisor.d.ts +1 -1
- package/dist/dev/static-batch-advisor.js +2 -2
- 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 +64 -0
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +149 -0
- package/dist/manifest/load.d.ts +0 -3
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +0 -4
- package/dist/manifest/schema.d.ts +9 -11
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +2 -23
- package/dist/pixi/authoring.d.ts +33 -0
- package/dist/pixi/authoring.d.ts.map +1 -1
- package/dist/pixi/authoring.js +301 -2
- 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/react/world-state.d.ts +12 -0
- package/dist/react/world-state.d.ts.map +1 -1
- package/dist/react/world-state.js +14 -0
- 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/render/soft-particle-depth.d.ts +3 -3
- package/dist/render/soft-particle-depth.d.ts.map +1 -1
- package/dist/render/soft-particle-depth.js +8 -13
- package/dist/runtime/create-runtime.d.ts +25 -62
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +58 -105
- package/dist/runtime/dev-build.d.ts +37 -0
- package/dist/runtime/dev-build.d.ts.map +1 -0
- package/dist/runtime/dev-build.js +49 -0
- package/dist/runtime/game.d.ts +38 -72
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +40 -55
- 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 +0 -13
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +5 -25
- 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.d.ts.map +1 -1
- package/dist/world3d-react/engine-bridge.js +29 -4
- package/dist/world3d-react/index.d.ts +1 -1
- package/dist/world3d-react/index.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 +132 -28
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
- package/dist/world3d-react/r3f-root-factory.js +14 -0
- package/dist/world3d-react/rapier-physics-bridge.d.ts +4 -4
- package/dist/world3d-react/rapier-physics-bridge.js +3 -3
- package/dist/world3d-react/world-context.d.ts +52 -3
- package/dist/world3d-react/world-context.d.ts.map +1 -1
- package/dist/world3d-react/world-context.js +94 -64
- package/dist-config/config.js.map +1 -1
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +8 -8
- package/schemas/vgai-project.schema.json +1 -6
- package/src/adapter/adapter-module.ts +405 -72
- 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 +162 -36
- 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 +25 -56
- package/src/adapter/root-seam-contract.ts +57 -0
- package/src/adapter/seam-evidence.ts +336 -0
- package/src/adapter/setup-three-root-adapter.ts +13 -15
- 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 +143 -77
- package/src/canvas-react/pixi-react-root-factory.tsx +4 -0
- package/src/canvas-react/world-context.ts +26 -101
- package/src/data/curve.ts +2 -2
- package/src/dev/instruments.ts +4 -4
- package/src/dev/register-render-vitals.ts +2 -2
- package/src/dev/static-batch-advisor.ts +2 -2
- package/src/ecs/user-data.ts +5 -0
- package/src/input/input-manager.ts +145 -0
- package/src/manifest/load.ts +0 -7
- package/src/manifest/schema.ts +2 -25
- package/src/pixi/authoring.ts +329 -2
- package/src/pixi/ingest.ts +9 -2
- package/src/pixi/scene-capture.ts +20 -5
- package/src/react/world-state.tsx +15 -0
- package/src/render/particles-factory.ts +16 -6
- package/src/render/soft-particle-depth.ts +8 -13
- package/src/runtime/create-runtime.ts +79 -158
- package/src/runtime/dev-build.ts +49 -0
- package/src/runtime/game.ts +72 -113
- package/src/runtime/mount-game.ts +19 -1
- package/src/runtime/mount-manifest.ts +5 -37
- package/src/runtime/types.ts +22 -0
- package/src/setup/setup-renderer.ts +3 -4
- package/src/world3d-react/engine-bridge.ts +29 -4
- package/src/world3d-react/index.ts +1 -1
- package/src/world3d-react/r3f-adapter.tsx +140 -31
- package/src/world3d-react/r3f-root-factory.tsx +14 -0
- package/src/world3d-react/rapier-physics-bridge.tsx +4 -4
- package/src/world3d-react/world-context.ts +111 -87
- package/dist/runtime/dev-layers.d.ts +0 -34
- package/dist/runtime/dev-layers.d.ts.map +0 -1
- package/dist/runtime/dev-layers.js +0 -46
- package/src/runtime/dev-layers.ts +0 -46
|
@@ -14,12 +14,11 @@ import { installRenderControlHarness, isRenderModeRequested } from './render-con
|
|
|
14
14
|
* by `createGameRuntime` and every headless test harness
|
|
15
15
|
* (`test/game-root.test.ts`, `test/frame-order.test.ts`,
|
|
16
16
|
* `test/game-two-roots.test.ts`) so there is exactly one code path for this
|
|
17
|
-
* wiring
|
|
18
|
-
* 3) — `physics`/`collisions`/`camera`/ `frame` are populated from the mount's
|
|
17
|
+
* wiring. `physics`/`collisions`/`camera`/`frame` are populated from the mount's
|
|
19
18
|
* first-party `GameContext`/ `MountedSetupThreeRoot` when available, left
|
|
20
19
|
* `undefined` otherwise (an external adapter's mount has none of these
|
|
21
20
|
* first-party handles — `frame` in particular is what makes
|
|
22
|
-
* `GameInternal.runFrame`
|
|
21
|
+
* `GameInternal.runFrame` fall back to a single opaque
|
|
23
22
|
* `mounted.update` call per substep for such a world).
|
|
24
23
|
*/
|
|
25
24
|
function firstPartyExtras(mounted) {
|
|
@@ -34,10 +33,8 @@ function firstPartyExtras(mounted) {
|
|
|
34
33
|
}
|
|
35
34
|
/**
|
|
36
35
|
* Register a three world onto the Game shell. `opts.id` defaults to
|
|
37
|
-
* `'main'
|
|
38
|
-
*
|
|
39
|
-
* real multi-world manifests later) pass an explicit id. This is the ONE
|
|
40
|
-
* registration code path for a three world, whatever its id.
|
|
36
|
+
* `'main'`; multi-root callers pass an explicit id. This is the one
|
|
37
|
+
* registration path for a three world, whatever its id.
|
|
41
38
|
*/
|
|
42
39
|
export function registerThreeRoot(game, adapter, mounted, opts) {
|
|
43
40
|
const world = createRootInstance({
|
|
@@ -73,13 +70,13 @@ export function registerPixiRoot(game, adapter, mounted, opts) {
|
|
|
73
70
|
return world;
|
|
74
71
|
}
|
|
75
72
|
/**
|
|
76
|
-
* Register a
|
|
73
|
+
* Register a React world onto the Game shell — the DOM
|
|
77
74
|
* analog of {@link registerThreeRoot}/{@link registerPixiRoot}. A react
|
|
78
75
|
* world has no `frame` hooks (react's own
|
|
79
76
|
* `createRoot` schedules its commits; `GameInternal.runFrame` correctly
|
|
80
77
|
* leaves a world with no `frame` untouched by its opaque-`update` fallback
|
|
81
78
|
* too, since `MountedReactGame` declares no `update`). `adapter` is typed
|
|
82
|
-
* as the real {@link ReactRootAdapter} shape (
|
|
79
|
+
* as the real {@link ReactRootAdapter} shape (same reasoning as
|
|
83
80
|
* `registerPixiRoot`'s doc comment above) — `RootInstance.adapter` only
|
|
84
81
|
* needs `.id`, so this passes through with zero cast.
|
|
85
82
|
*/
|
|
@@ -106,7 +103,7 @@ export async function createGameRuntime(config) {
|
|
|
106
103
|
return createRootsGameRuntime(config);
|
|
107
104
|
}
|
|
108
105
|
// ---------------------------------------------------------------------------
|
|
109
|
-
//
|
|
106
|
+
// Universal root host
|
|
110
107
|
// ---------------------------------------------------------------------------
|
|
111
108
|
/** A stand-in `THREE.WebGLRenderer` for headless (`headless:true`) three
|
|
112
109
|
* roots — mirrors `SetupThreeRootAdapter.mount`'s own `headlessComposer`
|
|
@@ -162,11 +159,8 @@ async function mountOneThreeRoot(spec, ctx) {
|
|
|
162
159
|
// resize-staleness (the roots-path `resize()` below always calls
|
|
163
160
|
// `setSize(rw, rh, false)`, so nothing else was ever going to update this
|
|
164
161
|
// canvas' CSS after mount). `createHostRenderer`'s own default
|
|
165
|
-
// (`updateStyle:true`) is intentionally left alone
|
|
166
|
-
//
|
|
167
|
-
// relies on that construction-time stamp + its own later `updateStyle:true`
|
|
168
|
-
// resizes for byte-identical behavior; this fix touches only the
|
|
169
|
-
// roots-path canvas, after the fact.
|
|
162
|
+
// (`updateStyle:true`) is intentionally left alone; the host owns CSS layout
|
|
163
|
+
// and reasserts the container-relative size after renderer construction.
|
|
170
164
|
canvas.style.width = '100%';
|
|
171
165
|
canvas.style.height = '100%';
|
|
172
166
|
renderer.setPixelRatio(dpr);
|
|
@@ -239,7 +233,7 @@ async function mountOnePixiRoot(spec, ctx) {
|
|
|
239
233
|
};
|
|
240
234
|
}
|
|
241
235
|
/**
|
|
242
|
-
* Mount one
|
|
236
|
+
* Mount one React `RootMountSpec` — the DOM sibling of
|
|
243
237
|
* `mountOneThreeRoot`/`mountOnePixiRoot`. Unlike its canvas-backed siblings
|
|
244
238
|
* this returns NO `routerEntry`: a react world's DOM-root layer participates in
|
|
245
239
|
* D5's z-order/box stacking (the caller still creates and positions its `<div>`
|
|
@@ -269,7 +263,7 @@ layer) {
|
|
|
269
263
|
// An absolutely-positioned layer with no width/height collapses to zero
|
|
270
264
|
// content size, so a child's
|
|
271
265
|
// own position:absolute offsets resolve against a degenerate containing
|
|
272
|
-
// block
|
|
266
|
+
// block; otherwise clicks land outside the game.
|
|
273
267
|
layer.style.width = '100%';
|
|
274
268
|
layer.style.height = '100%';
|
|
275
269
|
const reactHost = { container: layer, game };
|
|
@@ -289,8 +283,8 @@ layer) {
|
|
|
289
283
|
};
|
|
290
284
|
}
|
|
291
285
|
/**
|
|
292
|
-
* The
|
|
293
|
-
*
|
|
286
|
+
* The universal host implementer behind {@link createGameRuntime}. It builds
|
|
287
|
+
* one surface per world — a
|
|
294
288
|
* canvas for three/canvas, a DOM-root `<div>` layer for react — stacked
|
|
295
289
|
* per D5 §1, z-order/ties exactly matching
|
|
296
290
|
* `manifest/load.ts`'s sort, ONE `Game`, and registers every world onto it
|
|
@@ -303,16 +297,11 @@ layer) {
|
|
|
303
297
|
*/
|
|
304
298
|
/**
|
|
305
299
|
* Dev/e2e-only `window.__vgaiScene`/`__vgaiCamera` exposure for the roots
|
|
306
|
-
* path's
|
|
307
|
-
* to keep that function's own cyclomatic complexity down.
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* non-threejs default world (or none at all) publishes neither global,
|
|
312
|
-
* exactly like those getters return `undefined` in that case. Returns a
|
|
313
|
-
* retraction callback — identity-guarded (a newer session's globals must
|
|
314
|
-
* never be clobbered by an older session's stop, same reason the legacy
|
|
315
|
-
* path's own cleanup guards it) — that is a no-op when nothing was
|
|
300
|
+
* path's default world — split out of `createRootsGameRuntime` purely
|
|
301
|
+
* to keep that function's own cyclomatic complexity down. It uses the
|
|
302
|
+
* "first three world, else none" rule: a non-Three default world publishes
|
|
303
|
+
* neither global. Returns an identity-guarded retraction callback so an
|
|
304
|
+
* older session's stop cannot clobber a newer session's globals. It is a no-op when nothing was
|
|
316
305
|
* published (non-DEV build, or non-threejs default world).
|
|
317
306
|
*/
|
|
318
307
|
function installDefaultRootDevGlobals(game) {
|
|
@@ -451,26 +440,15 @@ async function createRootsGameRuntime(config) {
|
|
|
451
440
|
// --- Surface stack (D5 §1): DOM/z-order follows zOrder, ties -> array order
|
|
452
441
|
// — computed FIRST (bottom -> top) so both the z-index assignment below and
|
|
453
442
|
// the router's default-claim rule share one definition. A react world's
|
|
454
|
-
// DOM-root layer shares this SAME stacking pass
|
|
455
|
-
// stacking model, no special case") even though it is a `<div>`, not a
|
|
443
|
+
// DOM-root layer shares this SAME stacking pass even though it is a `<div>`, not a
|
|
456
444
|
// canvas, and carries no `hitTest` (the router never sees react entries at
|
|
457
445
|
// all — see the dispatch loop below). ---
|
|
458
|
-
// Dev layers sit TOPMOST, above every non-dev root, whatever zOrder either
|
|
459
|
-
// declares: a dev GUI the game can paint over is not a dev GUI. Expressed as
|
|
460
|
-
// two independent `stackOrder` passes concatenated rather than a magic
|
|
461
|
-
// zOrder value, so `stackOrder`'s one rule (ascending zOrder, ties -> array
|
|
462
|
-
// order) still governs WITHIN each band and nothing has to reason about what
|
|
463
|
-
// number is "high enough".
|
|
464
446
|
const claimEntries = mountSpecs.map((spec) => ({
|
|
465
447
|
id: spec.id,
|
|
466
448
|
zOrder: spec.zOrder ?? 0,
|
|
467
449
|
hitTest: spec.hitTest,
|
|
468
|
-
dev: spec.dev === true,
|
|
469
450
|
}));
|
|
470
|
-
const stacked =
|
|
471
|
-
...stackOrder(claimEntries.filter((e) => !e.dev)),
|
|
472
|
-
...stackOrder(claimEntries.filter((e) => e.dev)),
|
|
473
|
-
];
|
|
451
|
+
const stacked = stackOrder(claimEntries);
|
|
474
452
|
const bottomId = stacked[0]?.id;
|
|
475
453
|
const kindById = new Map(mountSpecs.map((spec) => [spec.id, spec.kind]));
|
|
476
454
|
const surfacesById = new Map();
|
|
@@ -485,12 +463,6 @@ async function createRootsGameRuntime(config) {
|
|
|
485
463
|
// surface.ts`, which is the one door the capture/staleness/screenshot
|
|
486
464
|
// sites now ask (ARCHITECTURE-CORE §The editor protocol, zero inference).
|
|
487
465
|
surface.dataset['vgaiRootId'] = entry.id;
|
|
488
|
-
// The capture's marker (`composite-screenshot.ts` reads exactly this
|
|
489
|
-
// attribute): a dev layer is excluded from a play capture unless the
|
|
490
|
-
// capture asks for it, because a contaminated "does the game look right"
|
|
491
|
-
// frame is worthless.
|
|
492
|
-
if (entry.dev)
|
|
493
|
-
surface.dataset['vgaiDevLayer'] = 'true';
|
|
494
466
|
if (!isReact) {
|
|
495
467
|
const canvas = surface;
|
|
496
468
|
canvas.width = w;
|
|
@@ -509,7 +481,7 @@ async function createRootsGameRuntime(config) {
|
|
|
509
481
|
// too (not just in `mountOneThreeRoot`) means every surface, whatever
|
|
510
482
|
// kind, starts container-relative from its very first paint, before any
|
|
511
483
|
// per-kind mount work has even run.
|
|
512
|
-
// `contain:layout paint` makes each surface the
|
|
484
|
+
// `contain:layout paint` makes each surface the containing block for
|
|
513
485
|
// `position:fixed` descendants (and clips overflow to the world's
|
|
514
486
|
// rectangle): full-screen game UI written the natural way (`fixed;
|
|
515
487
|
// inset:0`) then fills the WORLD, not the page. Without it, `fixed` UI
|
|
@@ -579,17 +551,7 @@ async function createRootsGameRuntime(config) {
|
|
|
579
551
|
const router = createInputRouter(container, routerEntries);
|
|
580
552
|
started = true;
|
|
581
553
|
loop.start();
|
|
582
|
-
// Expose scene
|
|
583
|
-
// single-world path's identical dev-only exposure above, generalized to
|
|
584
|
-
// the roots path's DEFAULT world (E4): `mountGameFromManifest`/
|
|
585
|
-
// `mountManifestRoots` route every caller (including a
|
|
586
|
-
// single-threejs-world scaffold project) through THIS path, so a caller
|
|
587
|
-
// migrating off the legacy `{canvas, adapter}` call must not silently
|
|
588
|
-
// lose `window.__vgaiScene`/`__vgaiCamera` — real e2e/dev tooling
|
|
589
|
-
// depends on them
|
|
590
|
-
// (`packages/editor/e2e/tests/04-standalone-game.spec.ts`). Split into
|
|
591
|
-
// its own top-level helper (with its retraction counterpart below)
|
|
592
|
-
// purely to keep this function's own cyclomatic complexity down.
|
|
554
|
+
// Expose the default Three root's scene and camera for dev/e2e tooling.
|
|
593
555
|
const retractDevGlobals = installDefaultRootDevGlobals(game);
|
|
594
556
|
// G3/FT-11 — publish `window.__vgaiRender` for the deterministic capture
|
|
595
557
|
// host. Installed at the TAIL of the mount (same position as
|
|
@@ -599,39 +561,48 @@ async function createRootsGameRuntime(config) {
|
|
|
599
561
|
const retractRenderHarness = renderMode && renderModeLocation !== undefined
|
|
600
562
|
? installSessionRenderHarness(game, renderModeLocation, config.renderControl?.target)
|
|
601
563
|
: undefined;
|
|
564
|
+
let resolveStopComplete;
|
|
565
|
+
const stopComplete = new Promise((resolve) => {
|
|
566
|
+
resolveStopComplete = resolve;
|
|
567
|
+
});
|
|
568
|
+
let stopping = false;
|
|
602
569
|
function fullCleanup() {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
570
|
+
if (stopping)
|
|
571
|
+
return;
|
|
572
|
+
stopping = true;
|
|
573
|
+
try {
|
|
574
|
+
loop.stop();
|
|
575
|
+
router.dispose();
|
|
576
|
+
for (const entry of mountedEntries) {
|
|
577
|
+
entry.mounted.dispose();
|
|
578
|
+
entry.renderer?.dispose();
|
|
579
|
+
entry.renderer?.forceContextLoss();
|
|
580
|
+
// Remove from the element's CURRENT parent, not the mount-time
|
|
581
|
+
// `container`: the editor's Game panel re-parents the live surfaces
|
|
582
|
+
// when its mount element swaps (fill <-> device preset, W2c), and
|
|
583
|
+
// `container.removeChild` would throw NotFoundError after such a move.
|
|
584
|
+
// `container` remains the fallback for hosts whose element stand-ins
|
|
585
|
+
// never wire `parentNode` (headless unit fixtures); an element already
|
|
586
|
+
// detached by such a host is a no-op via the catch.
|
|
587
|
+
try {
|
|
588
|
+
(entry.element.parentNode ?? container).removeChild(entry.element);
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
/* already detached — nothing to remove */
|
|
592
|
+
}
|
|
621
593
|
}
|
|
594
|
+
game.dispose();
|
|
595
|
+
retractDevGlobals();
|
|
596
|
+
retractRenderHarness?.();
|
|
597
|
+
}
|
|
598
|
+
finally {
|
|
599
|
+
void Promise.allSettled(mountedEntries.map((entry) => entry.mounted.disposeComplete ?? Promise.resolve())).then(() => resolveStopComplete());
|
|
622
600
|
}
|
|
623
|
-
game.dispose();
|
|
624
|
-
retractDevGlobals();
|
|
625
|
-
retractRenderHarness?.();
|
|
626
601
|
}
|
|
627
602
|
return {
|
|
628
603
|
stop: fullCleanup,
|
|
629
|
-
|
|
630
|
-
//
|
|
631
|
-
// a `pausable: false` world (a menu/HUD world) keeps ticking while every
|
|
632
|
-
// other world freezes, which the old blind `for (const entry of
|
|
633
|
-
// mountedEntries) entry.mounted.setPaused?.(true)` fan-out (with no
|
|
634
|
-
// `pausable` check at all) could never express.
|
|
604
|
+
stopComplete,
|
|
605
|
+
// `Game.play` owns per-world pause, loop-gate, and audio-gate semantics.
|
|
635
606
|
pause() {
|
|
636
607
|
game.play.pause();
|
|
637
608
|
},
|
|
@@ -655,24 +626,6 @@ async function createRootsGameRuntime(config) {
|
|
|
655
626
|
entry.mounted.resize?.(safeWidth, safeHeight);
|
|
656
627
|
}
|
|
657
628
|
},
|
|
658
|
-
// `GameSession.scene`/`.camera`/`.mounted` alias `Game.defaultRoot` (T6.1
|
|
659
|
-
// slice 1's "first three world, else first world" rule) — a legacy
|
|
660
|
-
// convenience shaped for the threejs-only past. T7.5 narrows the read via
|
|
661
|
-
// the `kind` discriminant instead of a blind `.scene`/`.camera` cast
|
|
662
|
-
// through a nonexistent property (identical behavior to before: still
|
|
663
|
-
// `undefined` for a roots-path session whose default world isn't
|
|
664
|
-
// three — real per-world surface routing for that case is T7.6's).
|
|
665
|
-
get scene() {
|
|
666
|
-
const m = game.defaultRoot.mounted;
|
|
667
|
-
return (m.kind === 'three' ? m.scene : undefined);
|
|
668
|
-
},
|
|
669
|
-
get camera() {
|
|
670
|
-
const m = game.defaultRoot.mounted;
|
|
671
|
-
return (m.kind === 'three' ? m.camera : undefined);
|
|
672
|
-
},
|
|
673
|
-
get mounted() {
|
|
674
|
-
return game.defaultRoot.mounted;
|
|
675
|
-
},
|
|
676
629
|
game,
|
|
677
630
|
};
|
|
678
631
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE one owner of "is this a development context". Every reader that has to
|
|
3
|
+
* answer that question calls {@link devBuildEnabled}; there is deliberately NO
|
|
4
|
+
* second source of truth — not a module-level cached boolean, not a
|
|
5
|
+
* `globalThis` flag, not a per-host copy of the `import.meta.env` read below.
|
|
6
|
+
* Instrumentation that ships to players because two places disagreed about
|
|
7
|
+
* what "dev" means is exactly the failure this single owner exists to make
|
|
8
|
+
* impossible.
|
|
9
|
+
*
|
|
10
|
+
* Ownership, stated in one place (the build rule):
|
|
11
|
+
* - OWNER: this function. It resolves the answer; nothing else derives it.
|
|
12
|
+
* - SHARERS: the two three-root adapters (`adapter/setup-three-root-adapter.ts`
|
|
13
|
+
* and `world3d-react/r3f-adapter.tsx`), which seed live render vitals only
|
|
14
|
+
* under it, and the `static-batch` capability's mutation watch and `Frozen`
|
|
15
|
+
* warning. Any future dev-only instrument calls this too, with its own
|
|
16
|
+
* `override`.
|
|
17
|
+
* - TEARDOWN: none. This is a pure predicate over build config and one
|
|
18
|
+
* caller-supplied argument — it owns no resource, allocates nothing, and
|
|
19
|
+
* has no lifecycle to end.
|
|
20
|
+
*
|
|
21
|
+
* The three inputs, highest precedence first:
|
|
22
|
+
* 1. `override` — the explicit per-call answer. A headless test, a capture
|
|
23
|
+
* harness, or a host that knows better passes `true`/`false` and gets
|
|
24
|
+
* exactly that. Passing `undefined` (or omitting it) means "decide for
|
|
25
|
+
* me" and falls through. The editor's own preview mount is the worked
|
|
26
|
+
* case: it is a dev session by definition even when the editor SPA it
|
|
27
|
+
* runs inside is a production build.
|
|
28
|
+
* 2. A dev build — `import.meta.env.DEV`. The ordinary local/editor case.
|
|
29
|
+
* 3. A production build's EXPLICIT opt-in — `VITE_VGAI_DEV_BUILD=true`.
|
|
30
|
+
* Instrumenting a production bundle is a real, legitimate choice (an
|
|
31
|
+
* internal playtest build, a QA build), and it must be an opt-in someone
|
|
32
|
+
* had to type, never something a default drifts into.
|
|
33
|
+
*
|
|
34
|
+
* Anything else — a production build with no opt-in — is `false`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function devBuildEnabled(override?: boolean | undefined): boolean;
|
|
37
|
+
//# sourceMappingURL=dev-build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-build.d.ts","sourceRoot":"","sources":["../../src/runtime/dev-build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAavE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE one owner of "is this a development context". Every reader that has to
|
|
3
|
+
* answer that question calls {@link devBuildEnabled}; there is deliberately NO
|
|
4
|
+
* second source of truth — not a module-level cached boolean, not a
|
|
5
|
+
* `globalThis` flag, not a per-host copy of the `import.meta.env` read below.
|
|
6
|
+
* Instrumentation that ships to players because two places disagreed about
|
|
7
|
+
* what "dev" means is exactly the failure this single owner exists to make
|
|
8
|
+
* impossible.
|
|
9
|
+
*
|
|
10
|
+
* Ownership, stated in one place (the build rule):
|
|
11
|
+
* - OWNER: this function. It resolves the answer; nothing else derives it.
|
|
12
|
+
* - SHARERS: the two three-root adapters (`adapter/setup-three-root-adapter.ts`
|
|
13
|
+
* and `world3d-react/r3f-adapter.tsx`), which seed live render vitals only
|
|
14
|
+
* under it, and the `static-batch` capability's mutation watch and `Frozen`
|
|
15
|
+
* warning. Any future dev-only instrument calls this too, with its own
|
|
16
|
+
* `override`.
|
|
17
|
+
* - TEARDOWN: none. This is a pure predicate over build config and one
|
|
18
|
+
* caller-supplied argument — it owns no resource, allocates nothing, and
|
|
19
|
+
* has no lifecycle to end.
|
|
20
|
+
*
|
|
21
|
+
* The three inputs, highest precedence first:
|
|
22
|
+
* 1. `override` — the explicit per-call answer. A headless test, a capture
|
|
23
|
+
* harness, or a host that knows better passes `true`/`false` and gets
|
|
24
|
+
* exactly that. Passing `undefined` (or omitting it) means "decide for
|
|
25
|
+
* me" and falls through. The editor's own preview mount is the worked
|
|
26
|
+
* case: it is a dev session by definition even when the editor SPA it
|
|
27
|
+
* runs inside is a production build.
|
|
28
|
+
* 2. A dev build — `import.meta.env.DEV`. The ordinary local/editor case.
|
|
29
|
+
* 3. A production build's EXPLICIT opt-in — `VITE_VGAI_DEV_BUILD=true`.
|
|
30
|
+
* Instrumenting a production bundle is a real, legitimate choice (an
|
|
31
|
+
* internal playtest build, a QA build), and it must be an opt-in someone
|
|
32
|
+
* had to type, never something a default drifts into.
|
|
33
|
+
*
|
|
34
|
+
* Anything else — a production build with no opt-in — is `false`.
|
|
35
|
+
*/
|
|
36
|
+
export function devBuildEnabled(override) {
|
|
37
|
+
if (override !== undefined)
|
|
38
|
+
return override;
|
|
39
|
+
// `import.meta` is cast whole, not just its `.env`: this module is reachable
|
|
40
|
+
// from programs whose tsconfig does not pull in `vite/client` (the session
|
|
41
|
+
// client's, for one, which reaches the three adapter transitively), and there
|
|
42
|
+
// `ImportMeta` has no declared `env` at all. The cast keeps the single owner
|
|
43
|
+
// of the dev answer importable from ANY program rather than forcing every
|
|
44
|
+
// downstream tsconfig to adopt Vite's ambient types.
|
|
45
|
+
const env = import.meta.env;
|
|
46
|
+
if (env?.DEV === true)
|
|
47
|
+
return true;
|
|
48
|
+
return env?.VITE_VGAI_DEV_BUILD === 'true';
|
|
49
|
+
}
|
package/dist/runtime/game.d.ts
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Game root
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* instead of driving its own loop, and in T7.3 slice 2 with a
|
|
7
|
-
* construction-time stage check (symmetric with the three/scene check).
|
|
8
|
-
* T6.2 slice 1 adds real `'react'`-kind
|
|
9
|
-
* `RootInstance` support (`container`, a real `reactRoot()`) — the DOM-root
|
|
10
|
-
* world surface `runtime/create-runtime.ts`'s roots path now mounts, with
|
|
11
|
-
* `physics`/`collisions`/`camera`/`frame` `undefined` for them exactly
|
|
12
|
-
* like an opaque/foreign mount.
|
|
13
|
-
*
|
|
14
|
-
* (D6), particularly §4 (the frame algorithm) and §8 stage 2, for the full
|
|
15
|
-
* target shape. This file holds the Game/ RootInstance shell, the NEW
|
|
16
|
-
* game-scoped `SystemRunner`, and the host-facing `runFrame` frame executor.
|
|
17
|
-
* The public surface is NOT frozen yet — do not add these exports to a
|
|
18
|
-
* barrel/index; only the in-scope runtime/adapter files import this module
|
|
19
|
-
* directly. The `gpu` per-world cache is the one remaining slice-1 omission.
|
|
2
|
+
* The surface-neutral Game root and its declaration-ordered root registry.
|
|
3
|
+
* Three, Canvas, and DOM roots share one game-scoped loop, input manager,
|
|
4
|
+
* system runner, state bridge, debug registry, clock, and seeded RNG. Each
|
|
5
|
+
* RootInstance retains its own mounted surface and optional capabilities.
|
|
20
6
|
*/
|
|
21
7
|
import type * as PIXI from 'pixi.js';
|
|
22
8
|
import type * as THREE from 'three';
|
|
@@ -33,7 +19,6 @@ import { InputManager } from '../input/input-manager';
|
|
|
33
19
|
import type { CollisionSystem } from '../physics/collision-system';
|
|
34
20
|
import type { PhysicsRegistry } from '../physics/physics-registry';
|
|
35
21
|
import type { Physics2DRegistry } from '../pixi/physics-registry';
|
|
36
|
-
import type { AudioContext as GameAudio } from '../setup/setup-audio';
|
|
37
22
|
import { type RunTicksOptions } from './debug-registry';
|
|
38
23
|
import { type GameStateBridge } from './state-bridge';
|
|
39
24
|
import type { GameContext, PlaytestContext } from './types';
|
|
@@ -54,10 +39,7 @@ export type GameLoop = ReturnType<typeof createGameLoop>;
|
|
|
54
39
|
* `runFrame`. `GameLoop.timeScale` (unaffected by this) remains the orthogonal
|
|
55
40
|
* "speed up/slow down" axis — pausing never touches it, so the host's
|
|
56
41
|
* accumulator/rAF loop keeps ticking at its normal cadence, which is what makes
|
|
57
|
-
* "paused still renders" possible
|
|
58
|
-
* implementation set `loop.timeScale = 0`, which starves the accumulator — and,
|
|
59
|
-
* before WO-P13, stopped rendering with it; that was the bug this control
|
|
60
|
-
* surface replaces, not a compatible behavior to keep).
|
|
42
|
+
* "paused still renders" possible.
|
|
61
43
|
*/
|
|
62
44
|
export interface PlayState {
|
|
63
45
|
/** Whether the game is currently paused (game-level — see the per-world
|
|
@@ -95,11 +77,9 @@ export interface PlayState {
|
|
|
95
77
|
}
|
|
96
78
|
/**
|
|
97
79
|
* The kinds of render surface a world can be. `'three'` and `'canvas'`
|
|
98
|
-
* roots are
|
|
99
|
-
* here so the type is already shaped for T7.4 and no caller has to widen a
|
|
100
|
-
* union later.
|
|
80
|
+
* roots are canvas-backed; `'dom'` roots own a DOM layer.
|
|
101
81
|
*
|
|
102
|
-
* Re-exported from `adapter/adapter-surface.ts`
|
|
82
|
+
* Re-exported from `adapter/adapter-surface.ts` so
|
|
103
83
|
* `adapter/root-adapter.ts`'s kind-tagged `MountedRoot` types can name it
|
|
104
84
|
* without an import cycle back to this file. This re-export keeps every
|
|
105
85
|
* existing `import type { AdapterSurface } from '../runtime/game'` call site
|
|
@@ -107,7 +87,7 @@ export interface PlayState {
|
|
|
107
87
|
*/
|
|
108
88
|
export type AdapterSurface = AdapterSurfaceLeaf;
|
|
109
89
|
/**
|
|
110
|
-
* A world's per-phase frame hooks
|
|
90
|
+
* A world's per-phase frame hooks.
|
|
111
91
|
* Populated on a `RootInstance` only for first-party mounts — an opaque/
|
|
112
92
|
* foreign mount has no phase-partitioned entry point, so it stays
|
|
113
93
|
* `undefined` and the Game's frame executor (`GameInternal.runFrame`) falls
|
|
@@ -116,7 +96,7 @@ export type AdapterSurface = AdapterSurfaceLeaf;
|
|
|
116
96
|
export interface RootFrameHooks {
|
|
117
97
|
/** Run this world's engine systems + component ticks + world-bound game
|
|
118
98
|
* systems for one phase. For a first-party world this delegates to the
|
|
119
|
-
*
|
|
99
|
+
* same `SystemRunner.runPhase` as its direct `mounted.update` entry. */
|
|
120
100
|
runPhase(phase: SystemPhaseName, dt: number): void;
|
|
121
101
|
/** Run once per substep, after ALL phases have run for ALL roots this
|
|
122
102
|
* substep (mirrors where `mounted.update`'s post-`systems.run` work sat
|
|
@@ -126,14 +106,12 @@ export interface RootFrameHooks {
|
|
|
126
106
|
}
|
|
127
107
|
/**
|
|
128
108
|
* A single world instance: the unit of adaptation.
|
|
129
|
-
* Slice-1 subset — `gpu` (per-world GPU resource cache, §6.2) is omitted
|
|
130
|
-
* until the slice that builds it.
|
|
131
109
|
*/
|
|
132
110
|
export interface RootInstance {
|
|
133
|
-
/** Manifest id
|
|
111
|
+
/** Manifest root id. */
|
|
134
112
|
readonly id: string;
|
|
135
113
|
readonly kind: AdapterSurface;
|
|
136
|
-
/** Per-world play/pause semantics
|
|
114
|
+
/** Per-world play/pause semantics. */
|
|
137
115
|
readonly pausable: boolean;
|
|
138
116
|
/** The RootAdapter that produced `mounted` — first-party or external.
|
|
139
117
|
* Deliberately narrower than `RootAdapter<K>` (T7.5): this field is only
|
|
@@ -157,7 +135,7 @@ export interface RootInstance {
|
|
|
157
135
|
* descriptively for a non-pixijs world, or a canvas world built without
|
|
158
136
|
* a `stage` (see `RootInstanceInit.stage`). */
|
|
159
137
|
pixiStage(): PIXI.Container;
|
|
160
|
-
/** Kind-narrowed accessor for
|
|
138
|
+
/** Kind-narrowed accessor for DOM roots: returns the
|
|
161
139
|
* DOM-root layer `<div>` the host mounted this world's react tree into
|
|
162
140
|
* (the SAME element passed as `container` to `createRootInstance` —
|
|
163
141
|
* identity matters, mirroring `threeScene()`/`pixiStage()`'s "same
|
|
@@ -176,7 +154,7 @@ export interface RootInstance {
|
|
|
176
154
|
readonly physics2d?: Physics2DRegistry | undefined;
|
|
177
155
|
/** Kind-typed via `mounted` in T7.5; `unknown` here deliberately. */
|
|
178
156
|
readonly camera?: unknown;
|
|
179
|
-
/** Phase-partitioned frame entry point
|
|
157
|
+
/** Phase-partitioned frame entry point — present only for
|
|
180
158
|
* first-party mounts. `undefined` for an opaque/foreign mount, which
|
|
181
159
|
* `GameInternal.runFrame` drives via its single `mounted.update` call
|
|
182
160
|
* instead (unless it `drivesOwnLoop`, in which case it isn't ticked at
|
|
@@ -229,8 +207,8 @@ export declare function createRootInstance(init: RootInstanceInit): RootInstance
|
|
|
229
207
|
/**
|
|
230
208
|
* Type guard for whether a `MountedThreeRoot` is a first-party
|
|
231
209
|
* `SetupThreeRootAdapter` mount (has a live `GameContext` at `.ctx`). Used to
|
|
232
|
-
* decide whether a world's `physics`/`collisions`/`camera`
|
|
233
|
-
*
|
|
210
|
+
* decide whether a world's `physics`/`collisions`/`camera` can be populated
|
|
211
|
+
* from it — an external
|
|
234
212
|
* adapter's mount has none of these first-party handles.
|
|
235
213
|
*
|
|
236
214
|
* Checks the `firstParty: true` brand (checklist item 1), NOT `'ctx' in
|
|
@@ -275,13 +253,9 @@ export interface DebrisOwner {
|
|
|
275
253
|
*/
|
|
276
254
|
export declare function disposeDebrisSubtree(obj: THREE.Object3D, owners: ReadonlyArray<DebrisOwner>): void;
|
|
277
255
|
/**
|
|
278
|
-
* The Game root. Owns the
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* additions: they delegate to the default world's first-party mount so
|
|
282
|
-
* existing single-world call sites keep working; hoisting them to true
|
|
283
|
-
* Game ownership (the target shape) is a later slice's
|
|
284
|
-
* work, not this one's.
|
|
256
|
+
* The Game root. Owns the loop, raw-asset cache, world registry, input,
|
|
257
|
+
* and game-scoped `SystemRunner`. Surface-specific capabilities remain on
|
|
258
|
+
* their mounted roots or in the aggregated `SystemAdapters` contract.
|
|
285
259
|
*/
|
|
286
260
|
export interface Game {
|
|
287
261
|
readonly loop: GameLoop;
|
|
@@ -291,18 +265,16 @@ export interface Game {
|
|
|
291
265
|
/** Per-game diagnostic store. Disabled by default; the editor enables it on demand. */
|
|
292
266
|
readonly profiler: PerformanceProfiler;
|
|
293
267
|
/**
|
|
294
|
-
* The game-scoped `SystemRunner
|
|
295
|
-
*
|
|
268
|
+
* The game-scoped `SystemRunner`, separate from any world's own runner.
|
|
269
|
+
* Within each phase,
|
|
296
270
|
* `GameInternal.runFrame` runs THIS runner's `runPhase` first, before any
|
|
297
271
|
* world's engine systems/component ticks/world-bound game systems (e.g.
|
|
298
272
|
* `ctx.systems.add`, which stays world-bound to the default world — see
|
|
299
273
|
* `runtime/types.ts`). Empty for every existing game (nothing registers
|
|
300
|
-
* against it
|
|
301
|
-
* unchanged by its presence.
|
|
274
|
+
* against it), so an empty runner has no frame cost beyond dispatch.
|
|
302
275
|
*/
|
|
303
276
|
readonly systems: SystemRunner;
|
|
304
|
-
/** Declaration-ordered.
|
|
305
|
-
* three world) — this is the SAME array reference `registerRoot`
|
|
277
|
+
/** Declaration-ordered. This is the same array reference `registerRoot`
|
|
306
278
|
* mutates, not a snapshot, so holders (e.g. `GameContext.roots`) observe
|
|
307
279
|
* later registrations. */
|
|
308
280
|
readonly roots: ReadonlyArray<RootInstance>;
|
|
@@ -338,9 +310,6 @@ export interface Game {
|
|
|
338
310
|
subscribeSystemAdapters?(listener: () => void): () => void;
|
|
339
311
|
/** The one game-owned `InputManager`, shared by every first-party root. */
|
|
340
312
|
readonly input: InputManager;
|
|
341
|
-
/** Delegates to the default world's first-party audio context. Throws
|
|
342
|
-
* when the default world is not a first-party mount. */
|
|
343
|
-
readonly audio: GameAudio;
|
|
344
313
|
/**
|
|
345
314
|
* Frame-versioned state bridge (T7.4 slice 1). Bumped once per completed
|
|
346
315
|
* `runFrame`, after all phases of all roots and all `endFrame` hooks (see
|
|
@@ -421,28 +390,18 @@ export interface GameInternal extends Game {
|
|
|
421
390
|
* else: world.mounted.update?.(dt) // opaque world fallback
|
|
422
391
|
* ```
|
|
423
392
|
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
* `update`
|
|
428
|
-
* `drivesOwnLoop` world is never ticked here at all — matching its
|
|
429
|
-
* exclusion from the legacy `!mountedRef.drivesOwnLoop` guard. An opaque
|
|
430
|
-
* host-driven world (no `frame`) gets exactly one `update(dt)` call per
|
|
431
|
-
* substep, after the phase loop — unchanged cadence from today.
|
|
393
|
+
* A single first-party world and its direct `mounted.update(dt)` entry run
|
|
394
|
+
* the same `SystemRunner` and `postFrame` work. A `drivesOwnLoop` world is
|
|
395
|
+
* never ticked here. An opaque host-driven world (no `frame`) gets exactly
|
|
396
|
+
* one `update(dt)` call per substep, after the phase loop.
|
|
432
397
|
*
|
|
433
398
|
* D10/T7.6 play-state addendum: when `Game.play.paused` is true, every
|
|
434
399
|
* `pausable` (and non-`drivesOwnLoop`) world skips every phase EXCEPT
|
|
435
400
|
* `render` (still called, every substep, with `dt` forced to `0`) and skips
|
|
436
401
|
* its `endFrame`/opaque-`update` call entirely — a `pausable: false` world
|
|
437
|
-
* is completely unaffected. `
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* none) but is no longer how `Game.play.step()` works (§7.1-2 fix,
|
|
441
|
-
* probe4: the old `ignorePause` full-frame re-run double-ticked every
|
|
442
|
-
* already-running `pausable: false` world, since D10's ordinary loop never
|
|
443
|
-
* stops ticking them). `Game.play.step()` now drives this function via the
|
|
444
|
-
* internal-only `onlyFrozen` mode instead (see `runFrameImpl` — not part of
|
|
445
|
-
* this public, host-facing signature): it ticks EXACTLY the currently
|
|
402
|
+
* is completely unaffected. `Game.play.step()` drives this function via the
|
|
403
|
+
* internal-only `onlyFrozen` mode (see `runFrameImpl` — not part of this
|
|
404
|
+
* public, host-facing signature): it ticks exactly the currently
|
|
446
405
|
* frozen set (host-driven, `pausable`, and `paused`) through every phase +
|
|
447
406
|
* `endFrame` with the real `dt` (not the render-phase's forced `0`), and
|
|
448
407
|
* touches no other world at all — a natural no-op while not paused, since
|
|
@@ -459,7 +418,6 @@ export interface GameInternal extends Game {
|
|
|
459
418
|
* `runTicks` sets it per tick for its `render: 'none' | 'last'` modes.
|
|
460
419
|
*/
|
|
461
420
|
runFrame(dt: number, opts?: {
|
|
462
|
-
ignorePause?: boolean;
|
|
463
421
|
skipRenderPhases?: boolean;
|
|
464
422
|
}): void;
|
|
465
423
|
/**
|
|
@@ -546,8 +504,16 @@ export interface GameInternal extends Game {
|
|
|
546
504
|
* the conventional `/inputmaps/default.inputmap.json` (or the adapter's
|
|
547
505
|
* configured `inputMapPath`); runtime additions go through
|
|
548
506
|
* `ctx.input.registerAction` instead.
|
|
507
|
+
*
|
|
508
|
+
* `{ optional: true }` is the CONVENTIONAL-PATH probe a root uses when the
|
|
509
|
+
* project never named a map: an absent file resolves quietly (a brand-new
|
|
510
|
+
* scaffold declares no actions yet), pins nothing — so a sibling root may
|
|
511
|
+
* still declare its own map — and yields to a map another root already
|
|
512
|
+
* owns. A map that exists but fails to parse is as loud either way.
|
|
549
513
|
*/
|
|
550
|
-
loadInputMap(path: string
|
|
514
|
+
loadInputMap(path: string, options?: {
|
|
515
|
+
optional?: boolean;
|
|
516
|
+
}): Promise<void>;
|
|
551
517
|
/** Release game-owned resources after every mounted root has disposed. */
|
|
552
518
|
dispose(): void;
|
|
553
519
|
}
|