@vgai/engine 0.5.21 → 0.5.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/adapter/adapter-module.d.ts.map +1 -1
- package/dist/adapter/adapter-module.js +2 -4
- package/dist/adapter/adapter-surface.d.ts +1 -1
- package/dist/adapter/adapter-surface.js +1 -1
- package/dist/adapter/authoring.d.ts +19 -21
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +2 -2
- package/dist/adapter/binding.d.ts +341 -0
- package/dist/adapter/binding.d.ts.map +1 -0
- package/dist/adapter/binding.js +148 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -1
- package/dist/adapter/colyseus-networking-adapter.js +23 -4
- package/dist/adapter/entrypoint-selection-readers.d.ts +24 -0
- package/dist/adapter/entrypoint-selection-readers.d.ts.map +1 -0
- package/dist/adapter/entrypoint-selection-readers.js +63 -0
- package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -1
- package/dist/adapter/entrypoint-selection-source.js +1 -44
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +1 -52
- package/dist/adapter/host-context.d.ts +46 -15
- package/dist/adapter/host-context.d.ts.map +1 -1
- package/dist/adapter/host-context.js +2 -9
- package/dist/adapter/index.d.ts +11 -12
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +9 -10
- package/dist/adapter/ingest/game-contract.d.ts +0 -3
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +0 -3
- package/dist/adapter/ingest/scene-capture.d.ts +19 -23
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +143 -17
- package/dist/adapter/ingest/visible-capture-window.d.ts +34 -0
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
- package/dist/adapter/manifest-interpreter.d.ts +59 -0
- package/dist/adapter/manifest-interpreter.d.ts.map +1 -0
- package/dist/adapter/manifest-interpreter.js +50 -0
- package/dist/adapter/native-debug-module.d.ts +71 -3
- package/dist/adapter/native-debug-module.d.ts.map +1 -1
- package/dist/adapter/native-debug-module.js +146 -10
- package/dist/adapter/rapier-physics-adapter.d.ts +31 -1
- package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -1
- package/dist/adapter/rapier-physics-adapter.js +50 -36
- package/dist/adapter/renderer-config.d.ts +3 -3
- package/dist/adapter/renderer-config.js +3 -3
- package/dist/adapter/root-adapter.d.ts +7 -7
- package/dist/adapter/root-adapter.js +4 -4
- package/dist/adapter/system-adapter.d.ts +45 -7
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-seam-contract.d.ts +3 -3
- package/dist/adapter/system-seam-contract.js +3 -3
- package/dist/adapter/system-slot.d.ts +168 -0
- package/dist/adapter/system-slot.d.ts.map +1 -0
- package/dist/adapter/system-slot.js +229 -0
- package/dist/adapter/transform.d.ts +11 -1
- package/dist/adapter/transform.d.ts.map +1 -1
- package/dist/ai/navigation.d.ts +17 -1
- package/dist/ai/navigation.d.ts.map +1 -1
- package/dist/ai/navigation.js +26 -5
- package/dist/animation/anim-graph-types.d.ts +11 -3
- package/dist/animation/anim-graph-types.d.ts.map +1 -1
- package/dist/animation/theatre-clock-binding.d.ts +4 -4
- package/dist/animation/theatre-clock-binding.js +4 -4
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +2 -2
- package/dist/animation/xstate-animation-meta.d.ts +13 -0
- package/dist/animation/xstate-animation-meta.d.ts.map +1 -1
- package/dist/animation/xstate-animation-meta.js +14 -0
- package/dist/asset-formats/index.d.ts +4 -7
- package/dist/asset-formats/index.d.ts.map +1 -1
- package/dist/asset-formats/index.js +4 -7
- package/dist/asset-formats/render-env.d.ts +5 -8
- package/dist/asset-formats/render-env.d.ts.map +1 -1
- package/dist/asset-formats/render-env.js +3 -6
- package/dist/asset-parse-error.d.ts +2 -5
- package/dist/asset-parse-error.d.ts.map +1 -1
- package/dist/asset-parse-error.js +2 -5
- package/dist/audio/bus-mixer.d.ts +101 -0
- package/dist/audio/bus-mixer.d.ts.map +1 -0
- package/dist/audio/bus-mixer.js +115 -0
- package/dist/canvas-react/index.d.ts +5 -12
- package/dist/canvas-react/index.d.ts.map +1 -1
- package/dist/canvas-react/index.js +5 -12
- package/dist/canvas-react/pixi-react-root-factory.d.ts +34 -21
- package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-root-factory.js +274 -36
- package/dist/core/countdown-timer.d.ts +118 -0
- package/dist/core/countdown-timer.d.ts.map +1 -0
- package/dist/core/countdown-timer.js +137 -0
- package/dist/core/deferred-commands.d.ts +127 -0
- package/dist/core/deferred-commands.d.ts.map +1 -0
- package/dist/core/deferred-commands.js +132 -0
- package/dist/core/frame-pacing.d.ts +1 -1
- package/dist/core/frame-pacing.js +2 -2
- package/dist/core/game-loop.d.ts +1 -3
- package/dist/core/game-loop.d.ts.map +1 -1
- package/dist/core/game-loop.js +1 -3
- package/dist/core/seeded-random.d.ts +1 -1
- package/dist/core/sim-clock.d.ts +19 -8
- package/dist/core/sim-clock.d.ts.map +1 -1
- package/dist/core/sim-clock.js +18 -7
- package/dist/core/types.d.ts +1 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/defaults.d.ts +6 -14
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +6 -14
- package/dist/dev/instruments.d.ts +38 -11
- package/dist/dev/instruments.d.ts.map +1 -1
- package/dist/dev/instruments.js +81 -11
- package/dist/dev/register-render-vitals.d.ts +1 -4
- package/dist/dev/register-render-vitals.d.ts.map +1 -1
- package/dist/dev/register-render-vitals.js +1 -4
- package/dist/dev/render-debug-adapter.d.ts +2 -3
- package/dist/dev/render-debug-adapter.d.ts.map +1 -1
- package/dist/dev/render-debug-adapter.js +2 -3
- package/dist/dev/render-vitals.d.ts +3 -3
- package/dist/dev/render-vitals.js +3 -3
- package/dist/dev/webgl-frame-capture.d.ts +1 -1
- package/dist/dev/webgl-frame-capture.js +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/input/host-pointer.d.ts +122 -0
- package/dist/input/host-pointer.d.ts.map +1 -0
- package/dist/input/host-pointer.js +160 -0
- package/dist/input/input-manager.d.ts +33 -5
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +48 -13
- package/dist/input/rebind-controller.d.ts.map +1 -1
- package/dist/input/rebind-controller.js +6 -8
- package/dist/input/schema.d.ts.map +1 -1
- package/dist/input/schema.js +56 -28
- package/dist/manifest/filename.d.ts.map +1 -1
- package/dist/manifest/filename.js +8 -12
- package/dist/manifest/load.d.ts +10 -10
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +9 -6
- package/dist/manifest/schema.d.ts +17 -21
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +45 -35
- package/dist/physics/collider-dimensions.d.ts +8 -12
- package/dist/physics/collider-dimensions.d.ts.map +1 -1
- package/dist/physics/collider-dimensions.js +8 -12
- package/dist/pixi/scene-capture.d.ts +11 -13
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/react/world-state.d.ts +1 -1
- package/dist/react/world-state.d.ts.map +1 -1
- package/dist/render/directional-shadow-fit.d.ts +88 -0
- package/dist/render/directional-shadow-fit.d.ts.map +1 -0
- package/dist/render/directional-shadow-fit.js +133 -0
- package/dist/render/environment-capture.d.ts +68 -0
- package/dist/render/environment-capture.d.ts.map +1 -0
- package/dist/render/environment-capture.js +90 -0
- package/dist/render/ibl-override-material.d.ts +97 -0
- package/dist/render/ibl-override-material.d.ts.map +1 -0
- package/dist/render/ibl-override-material.js +114 -0
- package/dist/render/light-camera-factory.d.ts +5 -7
- package/dist/render/light-camera-factory.d.ts.map +1 -1
- package/dist/render/light-camera-factory.js +8 -11
- package/dist/render/material-factory.js +1 -1
- package/dist/render/render-batch-system.d.ts.map +1 -1
- package/dist/render/render-features.d.ts +17 -6
- package/dist/render/render-features.d.ts.map +1 -1
- package/dist/render/render-features.js +0 -28
- package/dist/runtime/create-runtime.d.ts +34 -25
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +218 -93
- package/dist/runtime/debug-registry.d.ts +77 -8
- package/dist/runtime/debug-registry.d.ts.map +1 -1
- package/dist/runtime/debug-registry.js +3 -3
- package/dist/runtime/dev-build.d.ts +2 -2
- package/dist/runtime/dev-build.js +2 -2
- package/dist/runtime/game-input-seams.d.ts +51 -0
- package/dist/runtime/game-input-seams.d.ts.map +1 -0
- package/dist/runtime/game-input-seams.js +86 -0
- package/dist/runtime/game.d.ts +104 -40
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +119 -61
- package/dist/runtime/mount-game.d.ts +0 -16
- package/dist/runtime/mount-game.d.ts.map +1 -1
- package/dist/runtime/mount-game.js +26 -41
- package/dist/runtime/mount-manifest.d.ts +22 -14
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +22 -20
- package/dist/runtime/playtest.d.ts +22 -0
- package/dist/runtime/playtest.d.ts.map +1 -0
- package/dist/runtime/playtest.js +12 -0
- package/dist/runtime/state-bridge.d.ts +2 -3
- package/dist/runtime/state-bridge.d.ts.map +1 -1
- package/dist/runtime/state-bridge.js +1 -0
- package/dist/setup/setup-audio.d.ts +12 -0
- package/dist/setup/setup-audio.d.ts.map +1 -1
- package/dist/setup/setup-audio.js +9 -10
- package/dist/setup/setup-renderer.d.ts +2 -25
- package/dist/setup/setup-renderer.d.ts.map +1 -1
- package/dist/setup/setup-renderer.js +2 -31
- package/dist/world3d-react/index.d.ts +15 -25
- package/dist/world3d-react/index.d.ts.map +1 -1
- package/dist/world3d-react/index.js +15 -25
- package/dist/world3d-react/r3f-root-factory.d.ts +28 -34
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
- package/dist/world3d-react/r3f-root-factory.js +403 -57
- package/dist/world3d-react/rapier-physics-bridge.d.ts +30 -17
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +143 -58
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +16 -14
- package/schemas/vgai-project.schema.json +35 -26
- package/src/adapter/adapter-module.ts +2 -4
- package/src/adapter/adapter-surface.ts +1 -1
- package/src/adapter/authoring.ts +19 -21
- package/src/adapter/binding.ts +468 -0
- package/src/adapter/colyseus-networking-adapter.ts +20 -5
- package/src/adapter/entrypoint-selection-readers.ts +66 -0
- package/src/adapter/entrypoint-selection-source.ts +1 -40
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +1 -48
- package/src/adapter/host-context.ts +45 -15
- package/src/adapter/index.ts +23 -11
- package/src/adapter/ingest/game-contract.ts +0 -3
- package/src/adapter/ingest/scene-capture.ts +190 -39
- package/src/adapter/ingest/visible-capture-window.ts +35 -0
- package/src/adapter/manifest-interpreter.ts +64 -0
- package/src/adapter/native-debug-module.ts +248 -15
- package/src/adapter/rapier-physics-adapter.ts +76 -20
- package/src/adapter/renderer-config.ts +3 -3
- package/src/adapter/root-adapter.ts +7 -7
- package/src/adapter/system-adapter.ts +43 -8
- package/src/adapter/system-seam-contract.ts +3 -3
- package/src/adapter/system-slot.ts +291 -0
- package/src/adapter/transform.ts +18 -1
- package/src/ai/navigation.ts +30 -3
- package/src/animation/anim-graph-types.ts +11 -3
- package/src/animation/theatre-clock-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +2 -2
- package/src/animation/xstate-animation-meta.ts +16 -0
- package/src/asset-formats/index.ts +4 -7
- package/src/asset-formats/render-env.ts +3 -6
- package/src/asset-parse-error.ts +2 -5
- package/src/audio/bus-mixer.ts +161 -0
- package/src/canvas-react/index.ts +5 -21
- package/src/canvas-react/pixi-react-root-factory.tsx +301 -39
- package/src/core/countdown-timer.ts +188 -0
- package/src/core/deferred-commands.ts +174 -0
- package/src/core/frame-pacing.ts +2 -2
- package/src/core/game-loop.ts +1 -3
- package/src/core/seeded-random.ts +1 -1
- package/src/core/sim-clock.ts +19 -8
- package/src/core/types.ts +1 -3
- package/src/defaults.ts +6 -14
- package/src/dev/instruments.ts +108 -11
- package/src/dev/register-render-vitals.ts +1 -4
- package/src/dev/render-debug-adapter.ts +2 -3
- package/src/dev/render-vitals.ts +3 -3
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/index.ts +2 -5
- package/src/input/host-pointer.ts +230 -0
- package/src/input/input-manager.ts +49 -13
- package/src/input/input-types.ts +1 -1
- package/src/input/rebind-controller.ts +6 -8
- package/src/input/schema.ts +216 -188
- package/src/manifest/filename.ts +8 -12
- package/src/manifest/load.ts +17 -13
- package/src/manifest/schema.ts +55 -47
- package/src/physics/collider-dimensions.ts +8 -12
- package/src/pixi/scene-capture.ts +12 -15
- package/src/react/world-state.tsx +1 -1
- package/src/render/directional-shadow-fit.ts +156 -0
- package/src/render/environment-capture.ts +102 -0
- package/src/render/ibl-override-material.ts +170 -0
- package/src/render/light-camera-factory.ts +8 -11
- package/src/render/material-factory.ts +1 -1
- package/src/render/render-batch-system.ts +14 -6
- package/src/render/render-features.ts +17 -35
- package/src/runtime/create-runtime.ts +270 -108
- package/src/runtime/debug-registry.ts +84 -8
- package/src/runtime/dev-build.ts +2 -2
- package/src/runtime/game-input-seams.ts +108 -0
- package/src/runtime/game.ts +231 -85
- package/src/runtime/mount-game.ts +31 -57
- package/src/runtime/mount-manifest.ts +47 -36
- package/src/runtime/playtest.ts +22 -0
- package/src/runtime/state-bridge.ts +3 -3
- package/src/setup/setup-audio.ts +21 -11
- package/src/setup/setup-renderer.ts +2 -61
- package/src/world3d-react/index.ts +15 -31
- package/src/world3d-react/r3f-root-factory.tsx +451 -60
- package/src/world3d-react/rapier-physics-bridge.tsx +154 -58
- package/dist/adapter/setup-three-root-adapter.d.ts +0 -86
- package/dist/adapter/setup-three-root-adapter.d.ts.map +0 -1
- package/dist/adapter/setup-three-root-adapter.js +0 -908
- package/dist/animation/clip-map.d.ts +0 -12
- package/dist/animation/clip-map.d.ts.map +0 -1
- package/dist/animation/clip-map.js +0 -31
- package/dist/asset-registry.d.ts +0 -38
- package/dist/asset-registry.d.ts.map +0 -1
- package/dist/asset-registry.js +0 -66
- package/dist/canvas-react/engine-bridge.d.ts +0 -45
- package/dist/canvas-react/engine-bridge.d.ts.map +0 -1
- package/dist/canvas-react/engine-bridge.js +0 -45
- package/dist/canvas-react/pixi-react-adapter.d.ts +0 -77
- package/dist/canvas-react/pixi-react-adapter.d.ts.map +0 -1
- package/dist/canvas-react/pixi-react-adapter.js +0 -294
- package/dist/canvas-react/world-context.d.ts +0 -98
- package/dist/canvas-react/world-context.d.ts.map +0 -1
- package/dist/canvas-react/world-context.js +0 -173
- package/dist/dev/debug-draw.d.ts +0 -24
- package/dist/dev/debug-draw.d.ts.map +0 -1
- package/dist/dev/debug-draw.js +0 -73
- package/dist/render/auto-batcher.d.ts +0 -34
- package/dist/render/auto-batcher.d.ts.map +0 -1
- package/dist/render/auto-batcher.js +0 -140
- package/dist/render/lod.d.ts +0 -13
- package/dist/render/lod.d.ts.map +0 -1
- package/dist/render/lod.js +0 -16
- package/dist/runtime/types.d.ts +0 -303
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- package/dist/world3d-react/engine-bridge.d.ts +0 -47
- package/dist/world3d-react/engine-bridge.d.ts.map +0 -1
- package/dist/world3d-react/engine-bridge.js +0 -73
- package/dist/world3d-react/r3f-adapter.d.ts +0 -58
- package/dist/world3d-react/r3f-adapter.d.ts.map +0 -1
- package/dist/world3d-react/r3f-adapter.js +0 -634
- package/dist/world3d-react/world-context.d.ts +0 -182
- package/dist/world3d-react/world-context.d.ts.map +0 -1
- package/dist/world3d-react/world-context.js +0 -235
- package/src/adapter/setup-three-root-adapter.ts +0 -1032
- package/src/animation/clip-map.ts +0 -34
- package/src/asset-registry.ts +0 -89
- package/src/canvas-react/engine-bridge.ts +0 -59
- package/src/canvas-react/pixi-react-adapter.tsx +0 -356
- package/src/canvas-react/world-context.ts +0 -253
- package/src/dev/debug-draw.ts +0 -80
- package/src/render/auto-batcher.ts +0 -168
- package/src/render/lod.ts +0 -17
- package/src/runtime/types.ts +0 -328
- package/src/world3d-react/engine-bridge.ts +0 -86
- package/src/world3d-react/r3f-adapter.tsx +0 -717
- package/src/world3d-react/world-context.ts +0 -358
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import ts from 'typescript';
|
|
19
|
+
import { indexingIdentifier, propertyKey, selectionTable } from './entrypoint-selection-readers';
|
|
19
20
|
|
|
20
21
|
export type EntrypointSelectionRewrite =
|
|
21
22
|
| { readonly ok: true; readonly source: string; readonly changed: boolean }
|
|
@@ -25,25 +26,6 @@ function parse(path: string, source: string): ts.SourceFile {
|
|
|
25
26
|
return ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
function selectionTable(sf: ts.SourceFile, name: string): ts.ObjectLiteralExpression | undefined {
|
|
29
|
-
for (const statement of sf.statements) {
|
|
30
|
-
if (!ts.isVariableStatement(statement)) continue;
|
|
31
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
32
|
-
if (!ts.isIdentifier(declaration.name) || declaration.name.text !== name) continue;
|
|
33
|
-
const initializer = declaration.initializer;
|
|
34
|
-
if (initializer && ts.isObjectLiteralExpression(initializer)) return initializer;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function propertyKey(property: ts.ObjectLiteralElementLike): string | undefined {
|
|
41
|
-
const name = property.name;
|
|
42
|
-
if (!name) return undefined;
|
|
43
|
-
if (ts.isIdentifier(name) || ts.isStringLiteral(name)) return name.text;
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
29
|
function tableKeys(table: ts.ObjectLiteralExpression): string[] {
|
|
48
30
|
const keys: string[] = [];
|
|
49
31
|
for (const property of table.properties) {
|
|
@@ -53,27 +35,6 @@ function tableKeys(table: ts.ObjectLiteralExpression): string[] {
|
|
|
53
35
|
return keys;
|
|
54
36
|
}
|
|
55
37
|
|
|
56
|
-
/** The identifier in `scenes[activeScene]`, or a failure to read one. */
|
|
57
|
-
function indexingIdentifier(sf: ts.SourceFile, selection: string): string | undefined | null {
|
|
58
|
-
const names = new Set<string>();
|
|
59
|
-
let unreadable = false;
|
|
60
|
-
const visit = (node: ts.Node): void => {
|
|
61
|
-
if (
|
|
62
|
-
ts.isElementAccessExpression(node) &&
|
|
63
|
-
ts.isIdentifier(node.expression) &&
|
|
64
|
-
node.expression.text === selection
|
|
65
|
-
) {
|
|
66
|
-
if (ts.isIdentifier(node.argumentExpression)) names.add(node.argumentExpression.text);
|
|
67
|
-
else unreadable = true;
|
|
68
|
-
}
|
|
69
|
-
ts.forEachChild(node, visit);
|
|
70
|
-
};
|
|
71
|
-
ts.forEachChild(sf, visit);
|
|
72
|
-
if (names.size === 0 && !unreadable) return undefined;
|
|
73
|
-
if (names.size !== 1 || unreadable) return null;
|
|
74
|
-
return [...names][0];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
38
|
function moduleLevelStringLiteral(sf: ts.SourceFile, name: string): ts.StringLiteral | undefined {
|
|
78
39
|
for (const statement of sf.statements) {
|
|
79
40
|
if (!ts.isVariableStatement(statement)) continue;
|
|
@@ -43,6 +43,7 @@ import type {
|
|
|
43
43
|
ScenesFromEntrypointSelectionParams,
|
|
44
44
|
} from '../adapter-module';
|
|
45
45
|
import { EXPORTED_COMPOSITION_REGIONS } from '../adapter-module';
|
|
46
|
+
import { indexingIdentifier, propertyKey, selectionTable } from '../entrypoint-selection-readers';
|
|
46
47
|
import type { FinderResult } from './finder-result';
|
|
47
48
|
|
|
48
49
|
/** One region's entrypoint, as the host reads it off the manifest + disk. */
|
|
@@ -136,54 +137,6 @@ function importedBindings(sf: ts.SourceFile): Map<string, ImportedBinding> {
|
|
|
136
137
|
return bindings;
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
/** The module-level `const <name> = { … }` initializer, if there is one. */
|
|
140
|
-
function selectionTable(sf: ts.SourceFile, name: string): ts.ObjectLiteralExpression | undefined {
|
|
141
|
-
for (const statement of sf.statements) {
|
|
142
|
-
if (!ts.isVariableStatement(statement)) continue;
|
|
143
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
144
|
-
if (!ts.isIdentifier(declaration.name) || declaration.name.text !== name) continue;
|
|
145
|
-
const initializer = declaration.initializer;
|
|
146
|
-
if (initializer && ts.isObjectLiteralExpression(initializer)) return initializer;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function propertyKey(property: ts.ObjectLiteralElementLike): string | undefined {
|
|
153
|
-
const name = property.name;
|
|
154
|
-
if (!name) return undefined;
|
|
155
|
-
if (ts.isIdentifier(name) || ts.isStringLiteral(name)) return name.text;
|
|
156
|
-
return undefined;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* The identifier the entrypoint INDEXES the selection table with — the
|
|
161
|
-
* `activeScene` in `scenes[activeScene]`.
|
|
162
|
-
*
|
|
163
|
-
* `undefined` when the module never indexes the table, or indexes it with
|
|
164
|
-
* anything other than one single identifier: both are honest "cannot be read",
|
|
165
|
-
* and the caller turns each into its own note.
|
|
166
|
-
*/
|
|
167
|
-
function indexingIdentifier(sf: ts.SourceFile, selection: string): string | undefined | null {
|
|
168
|
-
const names = new Set<string>();
|
|
169
|
-
let unreadable = false;
|
|
170
|
-
const visit = (node: ts.Node): void => {
|
|
171
|
-
if (
|
|
172
|
-
ts.isElementAccessExpression(node) &&
|
|
173
|
-
ts.isIdentifier(node.expression) &&
|
|
174
|
-
node.expression.text === selection
|
|
175
|
-
) {
|
|
176
|
-
if (ts.isIdentifier(node.argumentExpression)) names.add(node.argumentExpression.text);
|
|
177
|
-
else unreadable = true;
|
|
178
|
-
}
|
|
179
|
-
ts.forEachChild(node, visit);
|
|
180
|
-
};
|
|
181
|
-
ts.forEachChild(sf, visit);
|
|
182
|
-
if (names.size === 0 && !unreadable) return undefined; // never indexed at all
|
|
183
|
-
if (names.size !== 1 || unreadable) return null; // indexed, but not readably
|
|
184
|
-
return [...names][0];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
140
|
/**
|
|
188
141
|
* The string a module-level `const <name> = '…'` binds, or `undefined`.
|
|
189
142
|
*
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Host contexts — what the host hands a root when it mounts it, PER SURFACE.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* The host never assumes Rapier/Colyseus; those live behind `SystemAdapters`
|
|
7
|
-
* owned by the first-party implementer.
|
|
4
|
+
* The host never assumes Rapier/Colyseus; system choices live behind
|
|
5
|
+
* `SystemAdapters` owned by each root's own implementation.
|
|
8
6
|
*
|
|
9
7
|
* ## Why this file is a family, not one interface (P-8)
|
|
10
8
|
*
|
|
@@ -71,18 +69,50 @@
|
|
|
71
69
|
* hosts, with no caller anywhere, and its only real effect would have been to
|
|
72
70
|
* re-open the door this interface exists to close: the host handing a game a
|
|
73
71
|
* 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.
|
|
79
72
|
*/
|
|
80
73
|
|
|
81
74
|
import type * as THREE from 'three';
|
|
82
75
|
import type { AssetCache } from '../assets';
|
|
83
|
-
import type { Game
|
|
76
|
+
import type { Game } from '../runtime/game';
|
|
84
77
|
import type { AdapterSurface } from './adapter-surface';
|
|
85
78
|
|
|
79
|
+
/**
|
|
80
|
+
* THE FOUR DOORS a mounting root actually opens on the Game — and the whole
|
|
81
|
+
* reason `three`/`canvas` host contexts no longer name `GameInternal`.
|
|
82
|
+
*
|
|
83
|
+
* `GameInternal` is the HOST's own control surface: `runFrame`,
|
|
84
|
+
* `runRenderFrame`, `runTicks`, `registerRoot`, `notifySystemAdaptersChanged`,
|
|
85
|
+
* `dispose`. Handing it to an adapter's `mount()` published the loop driver to
|
|
86
|
+
* the thing being driven — nothing read those members through `host.game`, but
|
|
87
|
+
* the type said they could, so "what may a root do to its Game?" had no
|
|
88
|
+
* answer short of "everything".
|
|
89
|
+
*
|
|
90
|
+
* The four doors, each with its verified consumer:
|
|
91
|
+
*
|
|
92
|
+
* 1. **Input-map load** — `runtime/game-input-seams.ts` calls
|
|
93
|
+
* `game.loadInputMap(path)` (and reads `game.input`/`game.loop.fixedDt`)
|
|
94
|
+
* from every three and canvas mount.
|
|
95
|
+
* 2. **Debug-registry access** — `getDebugRegistry(host.game)`
|
|
96
|
+
* (`r3f-root-factory`, `pixi-react-root-factory`); the registry is keyed by
|
|
97
|
+
* Game IDENTITY, which is why this handle is the Game and not a projection
|
|
98
|
+
* of it.
|
|
99
|
+
* 3. **The Game handed to `<WorldProvider>`** — the react world-state seam
|
|
100
|
+
* (`r3f-root-factory`, `pixi-react-root-factory`, and both react mounts in
|
|
101
|
+
* the editor's resolver). `WorldProvider`'s prop is `Game`.
|
|
102
|
+
* 4. **The profiler toggle** — `host.game.profiler` (`r3f-root-factory`).
|
|
103
|
+
*
|
|
104
|
+
* Doors 2–4 are already on the PUBLIC {@link Game}. `loadInputMap` is the one
|
|
105
|
+
* member that was only on `GameInternal`, and it is explicitly an adapter-mount
|
|
106
|
+
* door (see its own doc comment in `runtime/game.ts`). So this handle is
|
|
107
|
+
* `Game` plus that one method — and a `GameInternal` satisfies it, so every
|
|
108
|
+
* host that already passes one keeps compiling unchanged.
|
|
109
|
+
*/
|
|
110
|
+
export interface HostGameHandle extends Game {
|
|
111
|
+
/** Load the game-owned input map ONCE, however many roots ask. See
|
|
112
|
+
* `GameInternal.loadInputMap` for the load-once/competing-paths contract. */
|
|
113
|
+
loadInputMap(path: string, options?: { optional?: boolean }): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
|
|
86
116
|
/** The canvas/container the game renders into, plus its size. */
|
|
87
117
|
export interface HostSurface {
|
|
88
118
|
readonly canvas: HTMLCanvasElement;
|
|
@@ -107,8 +137,8 @@ export interface HostContextBase {
|
|
|
107
137
|
* zero-break guarantee for this slice). Typed as the PUBLIC `Game` shell
|
|
108
138
|
* here because that is the weakest promise any surface makes (a react
|
|
109
139
|
* root's host has always carried a plain `Game`); the three and canvas
|
|
110
|
-
* contexts narrow it to
|
|
111
|
-
* pass.
|
|
140
|
+
* contexts narrow it to {@link HostGameHandle} — the four doors a mounting
|
|
141
|
+
* root actually opens, which is what their hosts really pass.
|
|
112
142
|
*/
|
|
113
143
|
readonly game?: Game | undefined;
|
|
114
144
|
/**
|
|
@@ -134,7 +164,7 @@ export interface ThreeHostContext extends HostContextBase {
|
|
|
134
164
|
readonly renderer: THREE.WebGLRenderer;
|
|
135
165
|
/** Shared GLTF/texture cache — three-flavored today; see the header. */
|
|
136
166
|
readonly assets: AssetCache;
|
|
137
|
-
readonly game?:
|
|
167
|
+
readonly game?: HostGameHandle | undefined;
|
|
138
168
|
}
|
|
139
169
|
|
|
140
170
|
/**
|
|
@@ -149,7 +179,7 @@ export interface CanvasHostContext extends HostContextBase {
|
|
|
149
179
|
readonly width: number;
|
|
150
180
|
readonly height: number;
|
|
151
181
|
/** Shared Game root when mounted by the universal multi-root host. */
|
|
152
|
-
readonly game?:
|
|
182
|
+
readonly game?: HostGameHandle | undefined;
|
|
153
183
|
/** DPR override (T6.1 slice 1, D5 §3 — "one DPR
|
|
154
184
|
* everywhere"): the roots-path host computes ONE dpr for every stacked
|
|
155
185
|
* surface and passes it here; omitted -> the canvas adapter's own
|
|
@@ -176,7 +206,7 @@ export interface CanvasHostContext extends HostContextBase {
|
|
|
176
206
|
* `game` (inherited, and deliberately still optional): a
|
|
177
207
|
* `default-react` sibling mounted BESIDE an ingest root (`packages/editor/src/
|
|
178
208
|
* ingest-siblings.ts`) has no native `Game` to hand it — there is no
|
|
179
|
-
* first-party
|
|
209
|
+
* first-party loop for the sibling to join, only the ingested
|
|
180
210
|
* root's own foreign runtime — so its host carries no `game` at all rather than
|
|
181
211
|
* fabricating an empty one (anti-shim rule); the sibling mounts its entry
|
|
182
212
|
* component bare, with no `<WorldProvider>` wrap. `mountOneReactRoot`
|
package/src/adapter/index.ts
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Adapter interfaces — the seams the engine host and the editor DEPEND ON.
|
|
3
3
|
*
|
|
4
|
-
* host → RootAdapter ← {
|
|
4
|
+
* host → RootAdapter ← { r3fRootFactory-built adapters, IngestRootAdapter, … }
|
|
5
5
|
* editor → AuthoringAdapter ← { LiveThreeAuthoringAdapter, ReactRootAuthoringAdapter, … }
|
|
6
6
|
* game → SystemAdapters (physics/networking/navigation/audio/camera/debug)
|
|
7
7
|
*
|
|
8
8
|
* The first-party Rapier/Colyseus stack is ONE implementer of these
|
|
9
|
-
* interfaces, not the engine's vocabulary
|
|
9
|
+
* interfaces, not the engine's vocabulary.
|
|
10
10
|
*
|
|
11
11
|
* **This module is TYPE-ONLY, and must stay that way (P-6).** It used to
|
|
12
|
-
* value-export its own implementers — `
|
|
13
|
-
* `
|
|
14
|
-
* `
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* path instead:
|
|
12
|
+
* value-export its own implementers — `createRapierPhysicsAdapter`,
|
|
13
|
+
* `createColyseusNetworkingAdapter`, `createNavigationAdapter` — so
|
|
14
|
+
* `import type { AuthoringAdapter } from '@engine/adapter'` was the only
|
|
15
|
+
* thing keeping the three/Rapier/Colyseus stack out of a Pixi or React
|
|
16
|
+
* consumer's module graph. A seam that ships its implementers is not a
|
|
17
|
+
* seam. Import an implementer from its OWN path instead:
|
|
19
18
|
*
|
|
20
|
-
* `@engine/adapter/setup-three-root-adapter` SetupThreeRootAdapter, fromSetup
|
|
21
19
|
* `@engine/adapter/rapier-physics-adapter` createRapierPhysicsAdapter
|
|
22
20
|
* `@engine/adapter/colyseus-networking-adapter` createColyseusNetworkingAdapter
|
|
23
21
|
* `@engine/adapter/first-party-systems` createNavigationAdapter
|
|
@@ -88,12 +86,27 @@ export {
|
|
|
88
86
|
AUTHORING_PROVIDER_SHAPES,
|
|
89
87
|
HIERARCHY_PROVIDER_SHAPE,
|
|
90
88
|
} from './authoring-seam-contract';
|
|
89
|
+
export type {
|
|
90
|
+
BundledEntrySubset,
|
|
91
|
+
EntryStaticSurface,
|
|
92
|
+
ObservationBinding,
|
|
93
|
+
ProjectBinding,
|
|
94
|
+
ProjectionBinding,
|
|
95
|
+
ProtocolFamily,
|
|
96
|
+
RootBinding,
|
|
97
|
+
RootBindingParts,
|
|
98
|
+
RootDeclaration,
|
|
99
|
+
SubstrateBinding,
|
|
100
|
+
TruthBinding,
|
|
101
|
+
} from './binding';
|
|
102
|
+
export { createRootBinding } from './binding';
|
|
91
103
|
export type { ColyseusNetworkingConfig } from './colyseus-networking-adapter';
|
|
92
104
|
export type {
|
|
93
105
|
CanvasHostContext,
|
|
94
106
|
DomHostContext,
|
|
95
107
|
HostContextBase,
|
|
96
108
|
HostContextFor,
|
|
109
|
+
HostGameHandle,
|
|
97
110
|
HostSurface,
|
|
98
111
|
ThreeHostContext,
|
|
99
112
|
} from './host-context';
|
|
@@ -134,7 +147,6 @@ export {
|
|
|
134
147
|
SeamEvidenceLedger,
|
|
135
148
|
seamStageAtLeast,
|
|
136
149
|
} from './seam-evidence';
|
|
137
|
-
export type { MountedSetupThreeRoot, SetupThreeRootConfig } from './setup-three-root-adapter';
|
|
138
150
|
export type {
|
|
139
151
|
AudioAdapter,
|
|
140
152
|
AudioDebugEvent,
|
|
@@ -14,9 +14,6 @@
|
|
|
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
|
-
*
|
|
18
|
-
* Origin (root), F17+F21 (lifecycle), WO-SYS1 (systems), WO-SYS2
|
|
19
|
-
* (`systems.systemAdapters`).
|
|
20
17
|
*/
|
|
21
18
|
|
|
22
19
|
import type {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* classes. A game that bundles its own copy cannot be captured this way (the
|
|
32
32
|
* module-identity gatekeeper).
|
|
33
33
|
*
|
|
34
|
-
*
|
|
34
|
+
* There is an OPTIONAL, ADDITIVE composer capture: a game rendering through
|
|
35
35
|
* its own three.js addon `EffectComposer`
|
|
36
36
|
* (`three/examples/jsm/postprocessing/EffectComposer.js`) still trips the `render`
|
|
37
37
|
* trap above (its `RenderPass` calls `renderer.render(scene,camera)` internally),
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
|
|
56
56
|
import type * as THREE from 'three';
|
|
57
57
|
import {
|
|
58
|
+
type CaptureWaitOptions,
|
|
58
59
|
documentVisibilityClock,
|
|
59
60
|
startVisibleCaptureWindow,
|
|
60
61
|
type VisibilityClock,
|
|
61
|
-
type VisibleCaptureWindow,
|
|
62
62
|
} from './visible-capture-window';
|
|
63
63
|
|
|
64
64
|
/** The renderer operations the ingest host may lawfully use after capture.
|
|
@@ -209,31 +209,28 @@ export interface RenderPassHooks {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
/**
|
|
212
|
-
* Options for {@link SceneCaptureHandle.waitForCapture}
|
|
213
|
-
*
|
|
212
|
+
* Options for {@link SceneCaptureHandle.waitForCapture} — declared with the
|
|
213
|
+
* window they configure (`visible-capture-window.ts`), because they configure
|
|
214
|
+
* the WAIT and not this surface, and re-exported here for the callers that
|
|
215
|
+
* look for them beside `waitForCapture`.
|
|
214
216
|
*/
|
|
215
|
-
export
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
* mount: nothing renders, nothing fails, and every door reports silence.
|
|
226
|
-
* This is the seam the editor publishes to `vgai status` so the answer is
|
|
227
|
-
* "waiting for the first visible frame — the tab is hidden", not a
|
|
228
|
-
* countdown that is not running.
|
|
229
|
-
*/
|
|
230
|
-
onWait?: ((wait: VisibleCaptureWindow | null) => void) | undefined;
|
|
231
|
-
}
|
|
217
|
+
export type { CaptureWaitOptions };
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* WHICH interception yielded the captured renderer. Not decoration: every
|
|
221
|
+
* coverage row the host prints names the mechanism it reached the game
|
|
222
|
+
* through, and the two are materially different — a `shared-three` game runs
|
|
223
|
+
* on the host's very `three` instance, while a `devtools-observer` game runs
|
|
224
|
+
* its OWN pinned revision and only its renderer is shared.
|
|
225
|
+
*/
|
|
226
|
+
export type CaptureMechanism = 'shared-three' | 'devtools-observer';
|
|
232
227
|
|
|
233
228
|
/** Handle returned by {@link installSceneCapture}. */
|
|
234
229
|
export interface SceneCaptureHandle {
|
|
235
230
|
/** The captured runtime, or null until the game renders its first frame. */
|
|
236
231
|
readonly captured: CapturedRuntime | null;
|
|
232
|
+
/** How {@link captured} was reached; `null` while nothing is captured. */
|
|
233
|
+
readonly capturedVia: CaptureMechanism | null;
|
|
237
234
|
/**
|
|
238
235
|
* Install (or clear, with `null`) hooks bracketing every `render()` the
|
|
239
236
|
* CAPTURED renderer makes. Set AFTER capture, because the consumer
|
|
@@ -259,7 +256,7 @@ export interface SceneCaptureHandle {
|
|
|
259
256
|
* host can pause (set null) and resume (re-set it) the game's own loop. */
|
|
260
257
|
getAnimationLoop(renderer: CapturedThreeRenderer): ((time: number) => void) | null;
|
|
261
258
|
/**
|
|
262
|
-
*
|
|
259
|
+
* Resize every captured `EffectComposer` that renders
|
|
263
260
|
* through the captured renderer to `w`×`h`, matching its pixel ratio to
|
|
264
261
|
* `renderer.getPixelRatio()` — the composer tracks whatever DPR policy the
|
|
265
262
|
* host already applies to the renderer; no separate knob. A no-op when no
|
|
@@ -278,6 +275,123 @@ interface ThreeLike {
|
|
|
278
275
|
WebGLRenderer: { prototype: Record<string, unknown> };
|
|
279
276
|
}
|
|
280
277
|
|
|
278
|
+
/**
|
|
279
|
+
* A three renderer instance reached through three's OWN devtools seam, rather
|
|
280
|
+
* than through a prototype the host shares with the game.
|
|
281
|
+
*
|
|
282
|
+
* Only the two constructor-assigned own methods this file already traps on the
|
|
283
|
+
* shared prototype are named — nothing else about a foreign instance is
|
|
284
|
+
* assumed, because nothing else about it is known.
|
|
285
|
+
*/
|
|
286
|
+
interface ForeignRendererLike {
|
|
287
|
+
render?: unknown;
|
|
288
|
+
setAnimationLoop?: unknown;
|
|
289
|
+
domElement?: unknown;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** What {@link observeForeignThreeRenderers} needs from the enclosing capture,
|
|
293
|
+
* passed in rather than closed over so this stays a top-level function. */
|
|
294
|
+
interface ForeignObserverPorts {
|
|
295
|
+
/** True when this renderer's `render` was already intercepted by the shared
|
|
296
|
+
* prototype trap — i.e. it belongs to the host's OWN `three`, which is
|
|
297
|
+
* already captured properly and must not be double-wrapped. */
|
|
298
|
+
alreadyTrapped(renderer: object): boolean;
|
|
299
|
+
isHostRenderer(renderer: unknown): boolean;
|
|
300
|
+
/** The same `(self, scene, camera)` observation the prototype trap makes. */
|
|
301
|
+
observeRender(self: unknown, real: (...args: unknown[]) => unknown, args: unknown[]): unknown;
|
|
302
|
+
/** Record a non-null animation-loop callback (hidden-tab pumping). */
|
|
303
|
+
recordLoop(renderer: object, callback: (time: number) => void): void;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* FOREIGN-INSTANCE CAPTURE — through three's own `__THREE_DEVTOOLS__` seam.
|
|
308
|
+
*
|
|
309
|
+
* The prototype trap above can only reach a game that resolves `three` to the
|
|
310
|
+
* host's instance ("the module-identity gatekeeper" in this file's header). A
|
|
311
|
+
* game vendored as a BUILT BUNDLE routinely does not: a webpack/CRA build
|
|
312
|
+
* inlines three outright, and a Vite build externalized to the game's own
|
|
313
|
+
* pinned `three-rNNN.module.js` loads a second ES module. Those games rendered
|
|
314
|
+
* a real picture the host could not see, and the mount failed by name.
|
|
315
|
+
*
|
|
316
|
+
* The canvas lane already answers this exact question and answers it the same
|
|
317
|
+
* way: `authoring/canvas-runtime-recognition.ts` recognizes Phaser and Babylon
|
|
318
|
+
* through THEIR OWN documented public registries (`Phaser.GAMES`,
|
|
319
|
+
* `BABYLON.Engine.Instances`) rather than by sharing a module instance. three's
|
|
320
|
+
* equivalent public registry is `__THREE_DEVTOOLS__`: every `WebGLRenderer`
|
|
321
|
+
* constructor since r118 ends with
|
|
322
|
+
*
|
|
323
|
+
* if ( typeof __THREE_DEVTOOLS__ !== 'undefined' )
|
|
324
|
+
* __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) );
|
|
325
|
+
*
|
|
326
|
+
* — a bare global lookup, so it is not affected by the game-globals lexical
|
|
327
|
+
* shadow, and it is present in every revision this repo vendors (r129, r155,
|
|
328
|
+
* r170) as well as the host's own r180. Setting that global before the game's
|
|
329
|
+
* modules evaluate therefore hands the host the game's renderer INSTANCE, and
|
|
330
|
+
* an own-property wrapper on that instance's `render` yields the identical
|
|
331
|
+
* `(scene, camera, renderer)` triple the prototype trap yields.
|
|
332
|
+
*
|
|
333
|
+
* This never changes what happens for a deduped game: such a renderer's own
|
|
334
|
+
* `render` assignment was already intercepted by the prototype setter, so
|
|
335
|
+
* `alreadyTrapped` skips it and there is exactly one interception per renderer.
|
|
336
|
+
*
|
|
337
|
+
* Returns its own teardown. If something else already installed
|
|
338
|
+
* `__THREE_DEVTOOLS__` (the real three.js devtools extension), we listen on it
|
|
339
|
+
* and leave it in place.
|
|
340
|
+
*/
|
|
341
|
+
function observeForeignThreeRenderers(ports: ForeignObserverPorts): () => void {
|
|
342
|
+
const host = globalThis as { __THREE_DEVTOOLS__?: EventTarget };
|
|
343
|
+
if (typeof EventTarget !== 'function') return () => undefined;
|
|
344
|
+
const weInstalled = host.__THREE_DEVTOOLS__ === undefined;
|
|
345
|
+
if (weInstalled) host.__THREE_DEVTOOLS__ = new EventTarget();
|
|
346
|
+
const target = host.__THREE_DEVTOOLS__;
|
|
347
|
+
if (!target) return () => undefined;
|
|
348
|
+
const restores: Array<() => void> = [];
|
|
349
|
+
|
|
350
|
+
function wrapOwn(instance: object, key: 'render' | 'setAnimationLoop', value: unknown): void {
|
|
351
|
+
const prior = (instance as Record<string, unknown>)[key];
|
|
352
|
+
Object.defineProperty(instance, key, {
|
|
353
|
+
configurable: true,
|
|
354
|
+
writable: true,
|
|
355
|
+
value,
|
|
356
|
+
});
|
|
357
|
+
restores.push(() => {
|
|
358
|
+
Object.defineProperty(instance, key, { configurable: true, writable: true, value: prior });
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const listener = (event: Event): void => {
|
|
363
|
+
const detail = (event as CustomEvent<unknown>).detail as ForeignRendererLike | null;
|
|
364
|
+
// `observe` also carries Scenes (r129/r155/r170 dispatch from the `Scene`
|
|
365
|
+
// constructor too). A renderer is the one that owns a canvas and a render
|
|
366
|
+
// method; anything else is not this observer's subject.
|
|
367
|
+
if (!detail || typeof detail !== 'object') return;
|
|
368
|
+
if (typeof detail.render !== 'function' || !detail.domElement) return;
|
|
369
|
+
if (ports.alreadyTrapped(detail)) return;
|
|
370
|
+
if (ports.isHostRenderer(detail)) return;
|
|
371
|
+
const realRender = detail.render as (...args: unknown[]) => unknown;
|
|
372
|
+
wrapOwn(detail, 'render', function (this: unknown, ...args: unknown[]) {
|
|
373
|
+
return ports.observeRender(detail, realRender, args);
|
|
374
|
+
});
|
|
375
|
+
const realSal = detail.setAnimationLoop;
|
|
376
|
+
if (typeof realSal === 'function') {
|
|
377
|
+
const sal = realSal as (...args: unknown[]) => unknown;
|
|
378
|
+
wrapOwn(detail, 'setAnimationLoop', function (this: unknown, callback: unknown) {
|
|
379
|
+
if (typeof callback === 'function') {
|
|
380
|
+
ports.recordLoop(detail, callback as (time: number) => void);
|
|
381
|
+
}
|
|
382
|
+
return sal.call(detail, callback);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
target.addEventListener('observe', listener);
|
|
388
|
+
return () => {
|
|
389
|
+
target.removeEventListener('observe', listener);
|
|
390
|
+
for (const restore of restores.splice(0)) restore();
|
|
391
|
+
if (weInstalled) delete host.__THREE_DEVTOOLS__;
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
281
395
|
// Minimal structural type for the bits of an `EffectComposer` instance we
|
|
282
396
|
// touch. This module never imports a concrete `three/examples/jsm/…` addon
|
|
283
397
|
// either — the caller passes the SAME class its ingested game imports, so the
|
|
@@ -300,7 +414,7 @@ interface PrototypeRendererCtorLike {
|
|
|
300
414
|
* Install the render accessor trap on `threeNamespace.WebGLRenderer.prototype`.
|
|
301
415
|
* Pass the host's `three` module so the game (which shares it) is trapped.
|
|
302
416
|
*
|
|
303
|
-
* `effectComposerCtor` is OPTIONAL
|
|
417
|
+
* `effectComposerCtor` is OPTIONAL: pass the host's
|
|
304
418
|
* `EffectComposer` class (`three/examples/jsm/postprocessing/EffectComposer.js`)
|
|
305
419
|
* to also trap composer construction/rendering on that shared addon, so
|
|
306
420
|
* `resizeComposers()` can reach it. Omitting it (or a game never constructing
|
|
@@ -380,14 +494,7 @@ export function installSceneCapture(
|
|
|
380
494
|
get(this: Record<symbol, unknown>) {
|
|
381
495
|
const self = this;
|
|
382
496
|
return function setAnimationLoop(this: unknown, cb: unknown) {
|
|
383
|
-
if (cb)
|
|
384
|
-
loopCallbacks.set(self as object, cb as (time: number) => void);
|
|
385
|
-
loopedRenderers.add(self as object);
|
|
386
|
-
// Hidden tabs park rAF. Play/eval still need a first frame, so when
|
|
387
|
-
// a waiter is parked we drive the game's own loop once — the same
|
|
388
|
-
// class of tick `waitSimTime` already uses for a hidden document.
|
|
389
|
-
queueMicrotask(pumpHiddenLoops);
|
|
390
|
-
}
|
|
497
|
+
if (cb) recordAnimationLoop(self as object, cb as (time: number) => void);
|
|
391
498
|
const real = self[REAL_SAL];
|
|
392
499
|
return typeof real === 'function'
|
|
393
500
|
? (real as (...a: unknown[]) => unknown).call(self, cb)
|
|
@@ -402,10 +509,13 @@ export function installSceneCapture(
|
|
|
402
509
|
* `after` runs in a `finally`, so a throwing game frame still restores
|
|
403
510
|
* whatever the hook armed.
|
|
404
511
|
*/
|
|
405
|
-
function
|
|
406
|
-
|
|
512
|
+
function bracketedRender(
|
|
513
|
+
self: unknown,
|
|
514
|
+
real: (...a: unknown[]) => unknown,
|
|
515
|
+
args: unknown[],
|
|
516
|
+
): unknown {
|
|
407
517
|
const hooks = renderPassHooks;
|
|
408
|
-
if (hooks === null || !captured ||
|
|
518
|
+
if (hooks === null || !captured || self !== captured.renderer) {
|
|
409
519
|
return real.apply(self, args);
|
|
410
520
|
}
|
|
411
521
|
hooks.before();
|
|
@@ -416,6 +526,20 @@ export function installSceneCapture(
|
|
|
416
526
|
}
|
|
417
527
|
}
|
|
418
528
|
|
|
529
|
+
function forwardRender(self: Record<symbol, unknown>, args: unknown[]): unknown {
|
|
530
|
+
return bracketedRender(self, self[REAL] as (...a: unknown[]) => unknown, args);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** Register a game's animation-loop callback, whichever trap saw it. */
|
|
534
|
+
function recordAnimationLoop(renderer: object, callback: (time: number) => void): void {
|
|
535
|
+
loopCallbacks.set(renderer, callback);
|
|
536
|
+
loopedRenderers.add(renderer);
|
|
537
|
+
// Hidden tabs park rAF. Play/eval still need a first frame, so when a
|
|
538
|
+
// waiter is parked we drive the game's own loop once — the same class of
|
|
539
|
+
// tick `waitSimTime` already uses for a hidden document.
|
|
540
|
+
queueMicrotask(pumpHiddenLoops);
|
|
541
|
+
}
|
|
542
|
+
|
|
419
543
|
/**
|
|
420
544
|
* Commit to a world, DECLARATION FIRST.
|
|
421
545
|
*
|
|
@@ -510,6 +634,21 @@ export function installSceneCapture(
|
|
|
510
634
|
},
|
|
511
635
|
});
|
|
512
636
|
|
|
637
|
+
// A game that does NOT share the host's `three` never reaches the prototype
|
|
638
|
+
// trap above. three's own devtools seam reaches it anyway — see
|
|
639
|
+
// {@link observeForeignThreeRenderers}.
|
|
640
|
+
const foreignRenderers = new WeakSet<object>();
|
|
641
|
+
const stopForeignObserver = observeForeignThreeRenderers({
|
|
642
|
+
alreadyTrapped: (renderer) => (renderer as Record<symbol, unknown>)[REAL] !== undefined,
|
|
643
|
+
isHostRenderer: (renderer) => opts?.isHostRenderer?.(renderer) === true,
|
|
644
|
+
observeRender: (self, real, args) => {
|
|
645
|
+
foreignRenderers.add(self as object);
|
|
646
|
+
observeRendererRender(self, args[0], args[1]);
|
|
647
|
+
return bracketedRender(self, real, args);
|
|
648
|
+
},
|
|
649
|
+
recordLoop: recordAnimationLoop,
|
|
650
|
+
});
|
|
651
|
+
|
|
513
652
|
// DOM-backed Three renderers (CSS3DRenderer is the first) use a normal
|
|
514
653
|
// prototype method rather than WebGLRenderer's constructor assignment. Trap
|
|
515
654
|
// only classes explicitly supplied by the host, and restore each byte-for-byte.
|
|
@@ -537,7 +676,7 @@ export function installSceneCapture(
|
|
|
537
676
|
});
|
|
538
677
|
}
|
|
539
678
|
|
|
540
|
-
// ----
|
|
679
|
+
// ---- (D-C2): additive composer capture, only when a composer ctor
|
|
541
680
|
// was passed. `EffectComposer` methods (including `render`) live on the
|
|
542
681
|
// PROTOTYPE (a plain ES class — the constructor never does `this.render =
|
|
543
682
|
// …`), so a direct method-wrapper suffices — no getter/setter indirection
|
|
@@ -563,7 +702,7 @@ export function installSceneCapture(
|
|
|
563
702
|
}
|
|
564
703
|
|
|
565
704
|
// Extracted so `uninstall()`'s own cognitive complexity doesn't grow with
|
|
566
|
-
// this additive
|
|
705
|
+
// this additive restore step (biome's noExcessiveCognitiveComplexity).
|
|
567
706
|
function restoreComposerTrap() {
|
|
568
707
|
if (!composerProto) return;
|
|
569
708
|
if (priorComposerRender) composerProto['render'] = priorComposerRender;
|
|
@@ -574,6 +713,12 @@ export function installSceneCapture(
|
|
|
574
713
|
get captured() {
|
|
575
714
|
return captured;
|
|
576
715
|
},
|
|
716
|
+
get capturedVia(): CaptureMechanism | null {
|
|
717
|
+
if (!captured) return null;
|
|
718
|
+
return foreignRenderers.has(captured.renderer as unknown as object)
|
|
719
|
+
? 'devtools-observer'
|
|
720
|
+
: 'shared-three';
|
|
721
|
+
},
|
|
577
722
|
getDrawCount() {
|
|
578
723
|
return drawCount;
|
|
579
724
|
},
|
|
@@ -618,8 +763,11 @@ export function installSceneCapture(
|
|
|
618
763
|
new Error(
|
|
619
764
|
`Scene capture timed out after ${timeoutMs}ms of VISIBLE time ` +
|
|
620
765
|
`(${Math.round(captureWindow.elapsedHiddenMs())}ms browser-suspended, which is ` +
|
|
621
|
-
'not counted because no frame can be presented) —
|
|
622
|
-
|
|
766
|
+
'not counted because no frame can be presented) — no three renderer of ANY ' +
|
|
767
|
+
"instance drew a scene: neither the host's own `three` (the shared prototype " +
|
|
768
|
+
"trap) nor a renderer announcing itself on three's `__THREE_DEVTOOLS__` seam, " +
|
|
769
|
+
'which is what reaches a game that bundles its own copy. The game never ' +
|
|
770
|
+
'rendered.',
|
|
623
771
|
),
|
|
624
772
|
);
|
|
625
773
|
},
|
|
@@ -675,10 +823,13 @@ export function installSceneCapture(
|
|
|
675
823
|
}
|
|
676
824
|
if (priorSAL) Object.defineProperty(proto, 'setAnimationLoop', priorSAL);
|
|
677
825
|
else delete proto['setAnimationLoop'];
|
|
678
|
-
//
|
|
826
|
+
// Fully restore the composer addon's `render` (a plain
|
|
679
827
|
// prototype method — no per-instance own-property to restore, unlike
|
|
680
828
|
// the renderer/loop traps above).
|
|
681
829
|
restoreComposerTrap();
|
|
830
|
+
// Stop listening on three's devtools seam and give every foreign
|
|
831
|
+
// renderer instance its own `render`/`setAnimationLoop` back.
|
|
832
|
+
stopForeignObserver();
|
|
682
833
|
for (const restore of additionalRendererRestores) restore();
|
|
683
834
|
},
|
|
684
835
|
};
|