@volter/editor-game 0.5.68 → 0.5.69
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/NOTICE +7 -7
- package/contributions/autoplay.service.ts +1 -1
- package/contributions/build-profiles.document.tsx +1 -1
- package/contributions/build-progress.status.tsx +2 -2
- package/contributions/build.header.tsx +1 -1
- package/contributions/connection.status.tsx +1 -1
- package/contributions/coverage.service.ts +2 -2
- package/contributions/game-eval.command.ts +6 -6
- package/contributions/gameplay.command.ts +2 -2
- package/contributions/generations.status.tsx +1 -1
- package/contributions/ingest.service.ts +2 -2
- package/contributions/play.command.ts +6 -6
- package/contributions/react/react-inspector.service.ts +1 -1
- package/package.json +8 -8
- package/serving/index.ts +1 -1
- package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
- package/src/asset-budget/asset-budget-model.ts +14 -14
- package/src/asset-budget/optimize-apply.ts +8 -8
- package/src/bridge/call.ts +3 -3
- package/src/bridge/dispatch.ts +12 -12
- package/src/bridge/screenshot.ts +6 -6
- package/src/build/BuildProfilesPanel.tsx +8 -8
- package/src/canvas/canvas-board/CanvasBoardDocument.tsx +2 -2
- package/src/canvas/canvas-board/canvas-component-board.ts +1 -1
- package/src/canvas/canvas-design-mount.ts +1 -1
- package/src/coverage/live-project-verbs.ts +3 -3
- package/src/coverage/native-system-coverage.ts +3 -3
- package/src/coverage/root-coverage.ts +2 -2
- package/src/coverage/session-coverage.ts +2 -2
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +2 -2
- package/src/game-document/DevicePresetPicker.tsx +6 -6
- package/src/game-document/GameCaptureFrameButton.tsx +1 -1
- package/src/game-document/GameDocument.tsx +2 -2
- package/src/game-document/GamePanel.tsx +9 -9
- package/src/game-document/InstanceInspectorPicker.tsx +1 -1
- package/src/game-document/device-preview.ts +4 -4
- package/src/game-document/physics-debug.ts +2 -2
- package/src/generation/GenerationActivity.tsx +22 -22
- package/src/generation/GenerationGallery.css +79 -79
- package/src/generation/generation-documents.tsx +25 -25
- package/src/generation/generation-presentation.ts +1 -1
- package/src/host/adapter-reach.ts +1 -1
- package/src/host/adapter-runtime-bindings.ts +5 -5
- package/src/host/after-paint.ts +3 -3
- package/src/host/api/configurations.ts +1 -1
- package/src/host/authoring/canvas-runtime-recognition.ts +2 -2
- package/src/host/authoring/contract-hierarchy-authoring.ts +6 -6
- package/src/host/authoring/contract-scenes-stories.ts +5 -5
- package/src/host/authoring/ingest-data-writer.ts +1 -1
- package/src/host/authoring/ingest-source-persistence.ts +2 -2
- package/src/host/authoring/mount-isolated-pixi-screen.ts +1 -1
- package/src/host/authoring/pixi-authoring-adapter.ts +60 -4
- package/src/host/authoring/pixi-creatable-kinds.ts +18 -0
- package/src/host/authoring/pixi-live-write-target.ts +6 -5
- package/src/host/authoring/pixi-source-write-target.ts +5 -2
- package/src/host/authoring/pixi-structure-history.ts +3 -3
- package/src/host/binding-resolver.ts +6 -6
- package/src/host/browser-transpile.ts +10 -10
- package/src/host/canvas-preview-frames.ts +1 -1
- package/src/host/components/CameraAuthoringOverlay.tsx +14 -14
- package/src/host/components/HeaderTelemetry.tsx +11 -11
- package/src/host/components/PixiIsolationSceneContent.tsx +2 -2
- package/src/host/components/ResolutionPicker.tsx +4 -4
- package/src/host/components/scene-document.tsx +3 -3
- package/src/host/components/world-root-stage-binding.tsx +1 -1
- package/src/host/components/world-root-stage.ts +1 -1
- package/src/host/coverage/authoring-read-probe.ts +1 -1
- package/src/host/coverage/capability-coverage.ts +22 -22
- package/src/host/coverage/game-contract-seam-evidence.ts +8 -8
- package/src/host/coverage/project-verb-coverage.ts +1 -1
- package/src/host/coverage/system-adapter-coverage.ts +19 -6
- package/src/host/game-location-guard.ts +3 -3
- package/src/host/game-module-access.ts +6 -6
- package/src/host/game-realm-page.ts +3 -3
- package/src/host/game-realm-reclaim.ts +1 -1
- package/src/host/gameplay-recording.ts +5 -5
- package/src/host/gated-globals.ts +13 -13
- package/src/host/ingest/registry.ts +14 -14
- package/src/host/play-control-hook.ts +1 -1
- package/src/host/playwright-shim.ts +2 -2
- package/src/host/projection/pixi.ts +2 -2
- package/src/host/react-mount-runtime.ts +1 -1
- package/src/host/roots/canvas-root.tsx +2 -2
- package/src/host/roots/r3f-root.tsx +1 -1
- package/src/host/scene-view-drawability.ts +1 -1
- package/src/host/sdk/tools.ts +1 -1
- package/src/host/staged-projects.ts +1 -1
- package/src/host/stories/story-pixi-preview.ts +1 -1
- package/src/host/stories/story-three-preview.ts +1 -1
- package/src/host/three-ingest-runtime.ts +1 -1
- package/src/ingest/active-ingest.ts +1 -1
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
- package/src/ingest/authoring/ingest-root-adapter.ts +11 -11
- package/src/ingest/capture-wait-report.ts +4 -4
- package/src/ingest/discovery-public-ingest.ts +4 -4
- package/src/ingest/dom-stub-mark.ts +1 -1
- package/src/ingest/entry-load.ts +1 -1
- package/src/ingest/game-contract-realm.ts +3 -3
- package/src/ingest/game-pointer-lock.ts +1 -1
- package/src/ingest/ingest-canvas-scene-document.tsx +2 -2
- package/src/ingest/ingest-evidence-hook.ts +6 -6
- package/src/ingest/ingest-frame-snapshot.ts +2 -2
- package/src/ingest/ingest-play-commands.ts +1 -1
- package/src/ingest/ingest-play-control.ts +11 -11
- package/src/ingest/ingest-render-debug.ts +5 -5
- package/src/ingest/ingest-siblings.ts +3 -3
- package/src/ingest/module-mode.ts +2 -2
- package/src/ingest/mount-canvas-ingest-root.ts +10 -10
- package/src/ingest/mount-coverage.ts +3 -3
- package/src/ingest/mount-dom-ingest-root.ts +7 -7
- package/src/ingest/mount-ingest-root.ts +10 -10
- package/src/ingest/mount-three-ingest-root.ts +5 -5
- package/src/ingest/resolve-three.ts +2 -2
- package/src/ingest/served-bundle.ts +6 -6
- package/src/ingest/served-html-boot.ts +8 -8
- package/src/ingest/surface-dom.ts +9 -9
- package/src/ingest/surface-three.ts +1 -1
- package/src/ingest/types.ts +1 -1
- package/src/ingest/unmount-ingest-root.ts +3 -3
- package/src/network/NetworkInspectorPanel.tsx +2 -2
- package/src/play/play-boot-stall.ts +1 -1
- package/src/play/play-mode.ts +31 -30
- package/src/play/play-recording.ts +12 -12
- package/src/play-bar/PlayBar.tsx +8 -8
- package/src/play-bar/PlayerCountPicker.tsx +3 -3
- package/src/profiler/FrameDebuggerPanel.tsx +2 -2
- package/src/profiler/PerformancePanel.tsx +4 -4
- package/src/react/design-time-react-mount.ts +5 -5
- package/src/react/dom-authoring-adapter.ts +3 -3
- package/src/react/react-inspector-section.tsx +2 -2
- package/src/react/react-world-authoring-adapter.ts +20 -20
- package/src/react/story-documents/story-documents.tsx +4 -4
- package/src/react/ui-component-board.ts +1 -1
- package/src/runtime/adapter/audio-meter.ts +1 -1
- package/src/runtime/adapter/ingest/contract-debug-adapter.ts +4 -4
- package/src/runtime/adapter/ingest/contract-system-adapters.ts +9 -9
- package/src/runtime/adapter/ingest/merge-debug-adapters.ts +2 -2
- package/src/runtime/adapter/ingest/observation-debug-adapter.ts +1 -1
- package/src/runtime/adapter/native-debug-module.ts +5 -5
- package/src/runtime/audio/pose-guard.ts +1 -1
- package/src/runtime/core/game-scoped-slot.ts +1 -1
- package/src/runtime/create-runtime.ts +14 -14
- package/src/runtime/debug-bridge.ts +25 -25
- package/src/runtime/debug-registry.ts +17 -17
- package/src/runtime/dev/instruments.ts +1 -1
- package/src/runtime/dev/performance-profiler.ts +2 -2
- package/src/runtime/dev/register-render-vitals.ts +2 -2
- package/src/runtime/dev/static-batch-advisor.ts +3 -3
- package/src/runtime/dev-build.ts +3 -3
- package/src/runtime/game.ts +5 -5
- package/src/runtime/input-router.ts +1 -1
- package/src/runtime/mount-manifest.ts +9 -9
- package/src/runtime/pixi/authoring.ts +1 -1
- package/src/runtime/pixi/ingest.ts +1 -1
- package/src/runtime/presentation.ts +1 -1
- package/src/runtime/render-control.ts +21 -21
- package/src/runtime/render-seed.ts +3 -3
- package/src/runtime/run-ticks-settled.ts +1 -1
- package/src/services/game-audio.ts +3 -3
- package/src/services/game-network.ts +4 -4
- package/src/three/authoring/model-asset-inspector-section.css +15 -15
- package/src/three/authoring/model-asset-inspector-section.tsx +7 -7
- package/src/three/authoring/r3f-design-session.ts +10 -10
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5 -5
- package/src/three/authoring/three-authoring-adapter.ts +5 -4
- package/src/three/authoring/typed-three-inspector.ts +1 -1
- package/src/three/story-documents/three-story-documents.tsx +3 -3
- package/src/three/three-board/board-scene.ts +9 -9
- package/src/three/three-board/three-component-board.ts +1 -1
|
@@ -1052,7 +1052,7 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
1052
1052
|
this.matchedCssRules =
|
|
1053
1053
|
opts.matchedCssRules ??
|
|
1054
1054
|
((el) => getMatchedCssRules(el as MatchableElement) as CssRuleTarget[]);
|
|
1055
|
-
// Game CSS is SCOPED (`@scope ([data-
|
|
1055
|
+
// Game CSS is SCOPED (`@scope ([data-volter-game-styles])`, measured:
|
|
1056
1056
|
// a project stylesheet's `:root { --x }` never reaches the editor page's
|
|
1057
1057
|
// root), so the document root sees no game token — the adapter's OWN
|
|
1058
1058
|
// mounted root is inside the scope and inherits them all. Fall back to
|
|
@@ -1060,13 +1060,13 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
1060
1060
|
const tokenStyle = () => {
|
|
1061
1061
|
const root = this.root as unknown as Element;
|
|
1062
1062
|
// Tokens inherit DOWNWARD from each game-CSS scope root
|
|
1063
|
-
// (`[data-
|
|
1063
|
+
// (`[data-volter-game-styles]`), which sits BELOW this adapter's layer —
|
|
1064
1064
|
// so read the first scope root's computed style, not the layer's
|
|
1065
1065
|
// (measured: the layer sees none of the game's custom properties).
|
|
1066
1066
|
const el =
|
|
1067
1067
|
typeof Element !== 'undefined' && root instanceof Element
|
|
1068
|
-
? (root.querySelector('[data-
|
|
1069
|
-
root.closest?.('[data-
|
|
1068
|
+
? (root.querySelector('[data-volter-game-styles]') ??
|
|
1069
|
+
root.closest?.('[data-volter-game-styles]') ??
|
|
1070
1070
|
root)
|
|
1071
1071
|
: typeof document !== 'undefined'
|
|
1072
1072
|
? document.documentElement
|
|
@@ -1422,8 +1422,8 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
1422
1422
|
private elementLabel(n: OidNode, tree: OidTree): string {
|
|
1423
1423
|
// Preserve the explicit escape hatch, but make ordinary DOM semantics
|
|
1424
1424
|
// sufficient by default. Authored React should not need component splits
|
|
1425
|
-
// or `data-
|
|
1426
|
-
const editorLabel = normalizeElementText(n.el.getAttribute('data-
|
|
1425
|
+
// or `data-volter-name` merely to produce a legible hierarchy.
|
|
1426
|
+
const editorLabel = normalizeElementText(n.el.getAttribute('data-volter-name'));
|
|
1427
1427
|
if (editorLabel) return editorLabel;
|
|
1428
1428
|
|
|
1429
1429
|
const labelledBy = n.el.getAttribute('aria-labelledby');
|
|
@@ -1489,10 +1489,10 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
1489
1489
|
const parent = n.parentId ? tree.nodes.get(n.parentId) : null;
|
|
1490
1490
|
const parentComponent = parent ? this.componentName(parent) : null;
|
|
1491
1491
|
const isComponentBoundary = component !== null && component !== parentComponent;
|
|
1492
|
-
const crossSurfaceId = n.el.getAttribute('data-
|
|
1493
|
-
const crossSurfaceParentId = n.el.getAttribute('data-
|
|
1494
|
-
const crossSurfaceOrderAttribute = n.el.getAttribute('data-
|
|
1495
|
-
const crossSurfaceGroupLabel = n.el.getAttribute('data-
|
|
1492
|
+
const crossSurfaceId = n.el.getAttribute('data-volter-hierarchy-id');
|
|
1493
|
+
const crossSurfaceParentId = n.el.getAttribute('data-volter-hierarchy-parent-id');
|
|
1494
|
+
const crossSurfaceOrderAttribute = n.el.getAttribute('data-volter-hierarchy-order');
|
|
1495
|
+
const crossSurfaceGroupLabel = n.el.getAttribute('data-volter-hierarchy-group-label');
|
|
1496
1496
|
const crossSurfaceOrder =
|
|
1497
1497
|
crossSurfaceOrderAttribute === null ? undefined : Number(crossSurfaceOrderAttribute);
|
|
1498
1498
|
const node: EditorNode = {
|
|
@@ -1525,10 +1525,10 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
1525
1525
|
const semanticIds = new Set<string>();
|
|
1526
1526
|
for (const node of tree.nodes.values()) {
|
|
1527
1527
|
if (
|
|
1528
|
-
node.el.getAttribute('data-
|
|
1529
|
-
node.el.getAttribute('data-
|
|
1530
|
-
node.el.getAttribute('data-
|
|
1531
|
-
node.el.getAttribute('data-
|
|
1528
|
+
node.el.getAttribute('data-volter-hierarchy-id') !== null ||
|
|
1529
|
+
node.el.getAttribute('data-volter-hierarchy-parent-id') !== null ||
|
|
1530
|
+
node.el.getAttribute('data-volter-hierarchy-order') !== null ||
|
|
1531
|
+
node.el.getAttribute('data-volter-hierarchy-group-label') !== null
|
|
1532
1532
|
) {
|
|
1533
1533
|
semanticIds.add(node.id);
|
|
1534
1534
|
}
|
|
@@ -1545,10 +1545,10 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
1545
1545
|
[
|
|
1546
1546
|
node.id,
|
|
1547
1547
|
parentId,
|
|
1548
|
-
node.el.getAttribute('data-
|
|
1549
|
-
node.el.getAttribute('data-
|
|
1550
|
-
node.el.getAttribute('data-
|
|
1551
|
-
node.el.getAttribute('data-
|
|
1548
|
+
node.el.getAttribute('data-volter-hierarchy-id'),
|
|
1549
|
+
node.el.getAttribute('data-volter-hierarchy-parent-id'),
|
|
1550
|
+
node.el.getAttribute('data-volter-hierarchy-order'),
|
|
1551
|
+
node.el.getAttribute('data-volter-hierarchy-group-label'),
|
|
1552
1552
|
],
|
|
1553
1553
|
];
|
|
1554
1554
|
}),
|
|
@@ -2653,7 +2653,7 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
2653
2653
|
// boundary the scoped stylesheet paints — falling back to the document
|
|
2654
2654
|
// for a fixture element outside any scope.
|
|
2655
2655
|
const scope =
|
|
2656
|
-
(typeof el.closest === 'function' ? el.closest('[data-
|
|
2656
|
+
(typeof el.closest === 'function' ? el.closest('[data-volter-game-styles]') : null) ??
|
|
2657
2657
|
el.ownerDocument;
|
|
2658
2658
|
return tokens.map((name) => {
|
|
2659
2659
|
let count = 0;
|
|
@@ -3592,7 +3592,7 @@ export class ReactRootAuthoringAdapter implements AuthoringAdapter {
|
|
|
3592
3592
|
}
|
|
3593
3593
|
|
|
3594
3594
|
// A class GETTER, not a field initializer — see `ui-authoring-adapter.ts`/
|
|
3595
|
-
// `
|
|
3595
|
+
// `volter-scene-authoring-adapter.ts` for why (field initializers run before the
|
|
3596
3596
|
// constructor body assigns `this.writeBackend`).
|
|
3597
3597
|
get persistence(): PersistenceProvider {
|
|
3598
3598
|
const backend = this.writeBackend;
|
|
@@ -746,7 +746,7 @@ export function StoryDocsDocumentContent({ documentId }: WorkspaceDocumentConten
|
|
|
746
746
|
<article
|
|
747
747
|
data-testid="story-docs-document"
|
|
748
748
|
// §2.31 P2 amendment: docs prose reads over a local frost layer.
|
|
749
|
-
className="
|
|
749
|
+
className="volter-content-frost"
|
|
750
750
|
style={{
|
|
751
751
|
// §2.31: no root fill — the dock document surface shows through.
|
|
752
752
|
flex: 1,
|
|
@@ -975,7 +975,7 @@ function ArgRow({ id, name, value }: { id: string; name: string; value: unknown
|
|
|
975
975
|
</span>
|
|
976
976
|
{typeof value === 'boolean' ? (
|
|
977
977
|
<Checkbox
|
|
978
|
-
className="
|
|
978
|
+
className="volter-checkbox"
|
|
979
979
|
data-testid={`story-arg-${name}`}
|
|
980
980
|
checked={value}
|
|
981
981
|
onChange={(e) => setStoryArgOverride(id, name, e.target.checked)}
|
|
@@ -983,7 +983,7 @@ function ArgRow({ id, name, value }: { id: string; name: string; value: unknown
|
|
|
983
983
|
) : typeof value === 'number' ? (
|
|
984
984
|
<TextInput
|
|
985
985
|
type="number"
|
|
986
|
-
className="
|
|
986
|
+
className="volter-input"
|
|
987
987
|
data-testid={`story-arg-${name}`}
|
|
988
988
|
value={value}
|
|
989
989
|
onChange={(e) => {
|
|
@@ -1002,7 +1002,7 @@ function ArgRow({ id, name, value }: { id: string; name: string; value: unknown
|
|
|
1002
1002
|
) : typeof value === 'string' ? (
|
|
1003
1003
|
<TextInput
|
|
1004
1004
|
type="text"
|
|
1005
|
-
className="
|
|
1005
|
+
className="volter-input"
|
|
1006
1006
|
data-testid={`story-arg-${name}`}
|
|
1007
1007
|
value={value}
|
|
1008
1008
|
onChange={(e) => setStoryArgOverride(id, name, e.target.value)}
|
|
@@ -67,6 +67,6 @@ export const uiComponentBoard: ComponentBoard = {
|
|
|
67
67
|
`This project has no ${UI_COMPONENTS_TITLE} board (${UI_COMPONENTS_DOCUMENT_ID}): board ` +
|
|
68
68
|
"presence keys on STORIES, not on roots, and none of this project's stories declares the " +
|
|
69
69
|
'"dom" medium. Declare it — a manifest root entry naming the story\'s module, or a ' +
|
|
70
|
-
`\`
|
|
70
|
+
`\`volter.adapter.ts\` regionInclude that covers it — and the board appears. ${commandLine('console')} ` +
|
|
71
71
|
'names every story it could not place.',
|
|
72
72
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Meter arithmetic over an `AudioAdapter`'s `AudioMeterFrame.level` — the one
|
|
3
3
|
* place a linear RMS level becomes dBFS and a bar fill. Lives beside the
|
|
4
4
|
* adapter because both readers of a level (the editor's header meter and the
|
|
5
|
-
* `@
|
|
5
|
+
* `@volter/game` audio debugger) are consumers of the adapter, and neither may
|
|
6
6
|
* import the other.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Projects a game's DECLARED system surface (`window.
|
|
2
|
+
* Projects a game's DECLARED system surface (`window.volterGame.systems`, see
|
|
3
3
|
* `game-contract.ts`) onto the host's existing {@link DebugAdapter} — the one
|
|
4
4
|
* seam `game.commands()` / `game.state()` / `game.providers()` already read
|
|
5
5
|
* through for first-party content (`command-listener.ts`'s
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* game must not have to learn a parallel vocabulary. A first-party game
|
|
10
10
|
* registers verbs with `ctx.debug.registerCommand`; an ingested game declares
|
|
11
11
|
* them in its entry shim. Both arrive at the SAME `DebugAdapter`, so every
|
|
12
|
-
* consumer downstream — the CLI's `
|
|
12
|
+
* consumer downstream — the CLI's `volter-game-editor eval`, `@volter/e2e`'s `GameClient`, the
|
|
13
13
|
* editor's Debug Console and State Watch panels — works unchanged and unaware
|
|
14
14
|
* of the provenance.
|
|
15
15
|
*
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* emitted), not a stub standing in for a missing feature.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import type {
|
|
26
|
+
import type { VolterGameSystems } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
27
27
|
import type {
|
|
28
28
|
DebugAdapter,
|
|
29
29
|
DebugCommandInfo,
|
|
@@ -39,7 +39,7 @@ import { DebugError } from '../../debug-registry';
|
|
|
39
39
|
* when the truth is "this game declared nothing".
|
|
40
40
|
*/
|
|
41
41
|
export function createContractDebugAdapter(
|
|
42
|
-
systems:
|
|
42
|
+
systems: VolterGameSystems | undefined | null,
|
|
43
43
|
): DebugAdapter | null {
|
|
44
44
|
const commandList = systems?.commands ?? [];
|
|
45
45
|
const providerList = systems?.state ?? [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Projects a game's declared {@link
|
|
3
|
-
* (`window.
|
|
2
|
+
* Projects a game's declared {@link VolterGameSystemAdapters}
|
|
3
|
+
* (`window.volterGame.systems.systemAdapters`) onto the host's ordinary
|
|
4
4
|
* {@link SystemAdapters} bag — the ingest-realm counterpart of a first-party
|
|
5
5
|
* world's `ctx.registerSystemAdapter`, and the sibling of
|
|
6
6
|
* `contract-debug-adapter.ts` (which owns the `debug` slot).
|
|
@@ -55,9 +55,9 @@ export type {
|
|
|
55
55
|
} from '@volter/editor-project/adapter/ingest/contract-system-slots';
|
|
56
56
|
|
|
57
57
|
import type {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
VolterGameSystemAdapters,
|
|
59
|
+
VolterGameSystemEmpty,
|
|
60
|
+
VolterGameSystems,
|
|
61
61
|
} from '@volter/editor-project/adapter/ingest/game-contract';
|
|
62
62
|
import { GAME_SYSTEM_ADAPTERS_SHAPE } from '@volter/editor-project/adapter/ingest/game-contract-seams';
|
|
63
63
|
import {
|
|
@@ -70,7 +70,7 @@ import {
|
|
|
70
70
|
} from '@volter/editor-project/adapter/system-seam-contract';
|
|
71
71
|
|
|
72
72
|
/** The slots a game may declare through the contract (`debug` is projected from
|
|
73
|
-
* `commands`/`state` instead — see {@link
|
|
73
|
+
* `commands`/`state` instead — see {@link VolterGameSystemAdapters}). */
|
|
74
74
|
export type { ContractSystemSlot } from '@volter/editor-project/adapter/ingest/contract-system-slots';
|
|
75
75
|
|
|
76
76
|
/**
|
|
@@ -132,7 +132,7 @@ const EMPTY_PROJECTION: ContractSystemAdapterProjection = {
|
|
|
132
132
|
malformed: [],
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
-
function isEmptyRecord(value: object): value is
|
|
135
|
+
function isEmptyRecord(value: object): value is VolterGameSystemEmpty {
|
|
136
136
|
return (value as { present?: unknown }).present === false;
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -198,7 +198,7 @@ function classifyDeclaredSlot(
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
if (isEmptyRecord(value)) {
|
|
201
|
-
const evidence = (value as
|
|
201
|
+
const evidence = (value as VolterGameSystemEmpty).evidence;
|
|
202
202
|
// An empty claim with no evidence is exactly the "wrong empty claim" the
|
|
203
203
|
// bar calls a defect — it asserts an absence nobody can re-check.
|
|
204
204
|
if (typeof evidence !== 'string' || evidence.trim() === '') {
|
|
@@ -231,7 +231,7 @@ function classifyDeclaredSlot(
|
|
|
231
231
|
* header for what it buys and why its absence checks nothing.
|
|
232
232
|
*/
|
|
233
233
|
export function projectContractSystemAdapters(
|
|
234
|
-
systems:
|
|
234
|
+
systems: VolterGameSystems | undefined | null,
|
|
235
235
|
surface?: ContractSurface | undefined,
|
|
236
236
|
): ContractSystemAdapterProjection {
|
|
237
237
|
const declared = systems?.systemAdapters;
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Two feeders exist today and they are peers, not layers: the game's own
|
|
5
5
|
* declared system surface (`contract-debug-adapter.ts`, from
|
|
6
|
-
* `window.
|
|
6
|
+
* `window.volterGame.systems`) and its adapter module's observation table
|
|
7
7
|
* (`observation-debug-adapter.ts`). Both project onto the ONE runtime door the
|
|
8
|
-
* editor and `
|
|
8
|
+
* editor and `volter-game-editor eval` already read (`SystemAdapters.debug`), so the merge
|
|
9
9
|
* belongs here rather than in a second slot — a consumer must not be able to
|
|
10
10
|
* tell a name's provenance, which is the whole point of both projections.
|
|
11
11
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* This is the SIBLING of `contract-debug-adapter.ts` and deliberately the same
|
|
9
9
|
* shape: a projection rather than a second door. The two differ only in WHO
|
|
10
|
-
* declared the surface — the game itself, on `window.
|
|
10
|
+
* declared the surface — the game itself, on `window.volterGame.systems`, or the
|
|
11
11
|
* game's adapter module beside it — and a consumer downstream must not be able
|
|
12
12
|
* to tell them apart. `merge-debug-adapters.ts` is what folds both onto the one
|
|
13
13
|
* slot.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* export { debug } from './commands';
|
|
7
7
|
*
|
|
8
|
-
* The command module imports no
|
|
8
|
+
* The command module imports no volter API. Its functions are the application's
|
|
9
9
|
* own functions over its own stores; the native adapter merely projects that
|
|
10
10
|
* existing registry onto the session debug/input doors after the root mounts.
|
|
11
11
|
* Loading through the root entry is load-bearing: the functions close over the
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* copy of its stores.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import type {
|
|
16
|
+
import type { VolterGameSystemAdapters } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
17
17
|
import type {
|
|
18
18
|
NativeCommandEntry,
|
|
19
19
|
NativeDebugBinding,
|
|
@@ -407,12 +407,12 @@ export function installNativeDebugBindings(
|
|
|
407
407
|
// export { debug, systems } from './commands';
|
|
408
408
|
//
|
|
409
409
|
// — whose slots are the ONE declarable carrier both realms share,
|
|
410
|
-
// `
|
|
410
|
+
// `VolterGameSystemAdapters` (`ingest/game-contract.ts`; the native engine is
|
|
411
411
|
// the premade 100% implementation of that contract). Validation is the SAME
|
|
412
412
|
// projection the ingest realm uses (`ingest/contract-system-adapters.ts`), so
|
|
413
413
|
// there is one shape law, not two. This door is what retires
|
|
414
414
|
// `ctx.registerSystemAdapter` from component code (ARCHITECTURE-CORE §System
|
|
415
|
-
// adapters: "a project's `
|
|
415
|
+
// adapters: "a project's `volter.adapter.ts` binds app-owned systems through
|
|
416
416
|
// declared native exports … Components never call `registerSystemAdapter`").
|
|
417
417
|
//
|
|
418
418
|
// Native-realm difference from ingest: a malformed slot THROWS (this is our
|
|
@@ -437,7 +437,7 @@ export function nativeSystemsBindingFromEntryModule(
|
|
|
437
437
|
}
|
|
438
438
|
rejectUnknownKeys(raw, CONTRACT_SYSTEM_SLOTS, `Root "${rootId}" systems export`);
|
|
439
439
|
const projection = projectContractSystemAdapters(
|
|
440
|
-
{ systemAdapters: raw as
|
|
440
|
+
{ systemAdapters: raw as VolterGameSystemAdapters },
|
|
441
441
|
surface,
|
|
442
442
|
);
|
|
443
443
|
if (projection.malformed.length > 0) {
|
|
@@ -19,7 +19,7 @@ interface GuardState {
|
|
|
19
19
|
|
|
20
20
|
// The editor bundle and source-served runtime can load this helper separately
|
|
21
21
|
// while sharing Three. Installation belongs to the actual prototype identity.
|
|
22
|
-
const GUARD_STATE = Symbol.for('
|
|
22
|
+
const GUARD_STATE = Symbol.for('volter.audio-pose-guard');
|
|
23
23
|
type AudioPrototype = {
|
|
24
24
|
updateMatrixWorld: UpdateMatrixWorld;
|
|
25
25
|
[GUARD_STATE]?: GuardState;
|
|
@@ -11,7 +11,7 @@ export function createGameScopedSlot<T>(name: string): {
|
|
|
11
11
|
set(owner: object, value: T): void;
|
|
12
12
|
get(owner: object): T | null;
|
|
13
13
|
} {
|
|
14
|
-
const key = Symbol.for(`@
|
|
14
|
+
const key = Symbol.for(`@volter/game-runtime/game-scoped/${name}`);
|
|
15
15
|
|
|
16
16
|
return {
|
|
17
17
|
set(owner, value) {
|
|
@@ -299,7 +299,7 @@ export interface RootsRuntimeConfig {
|
|
|
299
299
|
*/
|
|
300
300
|
headless?: boolean | undefined;
|
|
301
301
|
/**
|
|
302
|
-
* `
|
|
302
|
+
* `volter.project.json`'s `rendering.antialias` — whether every three root's
|
|
303
303
|
* WebGL context is built with a multisampled drawing buffer.
|
|
304
304
|
*
|
|
305
305
|
* It is a RUNTIME-CONFIG option rather than a world's own
|
|
@@ -315,7 +315,7 @@ export interface RootsRuntimeConfig {
|
|
|
315
315
|
* world's `mount()`/`setup()` runs (this function constructs the Game
|
|
316
316
|
* first — see `createRootsGameRuntime`). The manifest-aware boot path
|
|
317
317
|
* (`mount-manifest.ts`'s `mountManifestRoots`) is the real caller that
|
|
318
|
-
* resolves this from `manifest.determinism`/`?
|
|
318
|
+
* resolves this from `manifest.determinism`/`?volter-seed=`/its own
|
|
319
319
|
* explicit-config leg; a caller building `RootsRuntimeConfig` by hand
|
|
320
320
|
* (a test, a bespoke host) may also set it directly. Omitting it falls
|
|
321
321
|
* back to `createGame`'s own fixed default. */
|
|
@@ -326,7 +326,7 @@ export interface RootsRuntimeConfig {
|
|
|
326
326
|
/**
|
|
327
327
|
* Test-only overrides for the render-control seam this host now wires up
|
|
328
328
|
* for EVERY session (see `createRootsGameRuntime`): `location` is where
|
|
329
|
-
* `?
|
|
329
|
+
* `?volter-render=1` is read from (defaults to the real `window.location`;
|
|
330
330
|
* a Node test has no `window` and must inject one to exercise the seam),
|
|
331
331
|
* `target` is where the harness publishes (defaults to the real `window`
|
|
332
332
|
* — override in a unit test to avoid touching the global object; mirrors
|
|
@@ -673,7 +673,7 @@ async function mountOneReactRoot(
|
|
|
673
673
|
*/
|
|
674
674
|
|
|
675
675
|
/**
|
|
676
|
-
* Dev/e2e-only `window.
|
|
676
|
+
* Dev/e2e-only `window.__volterScene`/`__volterCamera` exposure for the roots
|
|
677
677
|
* path's default world — split out of `createRootsGameRuntime` purely
|
|
678
678
|
* to keep that function's own cyclomatic complexity down. It uses the
|
|
679
679
|
* "first three world, else none" rule: a non-Three default world publishes
|
|
@@ -687,7 +687,7 @@ function installDefaultRootDevGlobals(game: GameInternal): () => void {
|
|
|
687
687
|
if (defaultMounted.kind !== 'three') return () => {};
|
|
688
688
|
const scene = defaultMounted.scene;
|
|
689
689
|
const w = window as unknown as Record<string, unknown>;
|
|
690
|
-
w['
|
|
690
|
+
w['__volterScene'] = scene;
|
|
691
691
|
// An ACCESSOR, not a value read once: a world may replace its camera after
|
|
692
692
|
// mount (fiber's `set({ camera })` — drei's `makeDefault`, or a translated
|
|
693
693
|
// Godot world installing the camera its `.tscn` authors), and a snapshot
|
|
@@ -697,15 +697,15 @@ function installDefaultRootDevGlobals(game: GameInternal): () => void {
|
|
|
697
697
|
// rule the value comparison was.
|
|
698
698
|
const readCamera = (): THREE.PerspectiveCamera =>
|
|
699
699
|
defaultMounted.camera as THREE.PerspectiveCamera;
|
|
700
|
-
Object.defineProperty(w, '
|
|
700
|
+
Object.defineProperty(w, '__volterCamera', {
|
|
701
701
|
get: readCamera,
|
|
702
702
|
configurable: true,
|
|
703
703
|
enumerable: true,
|
|
704
704
|
});
|
|
705
705
|
return () => {
|
|
706
|
-
if (w['
|
|
707
|
-
if (Object.getOwnPropertyDescriptor(w, '
|
|
708
|
-
delete w['
|
|
706
|
+
if (w['__volterScene'] === scene) delete w['__volterScene'];
|
|
707
|
+
if (Object.getOwnPropertyDescriptor(w, '__volterCamera')?.get === readCamera)
|
|
708
|
+
delete w['__volterCamera'];
|
|
709
709
|
};
|
|
710
710
|
}
|
|
711
711
|
|
|
@@ -845,7 +845,7 @@ function installSessionRenderHarness(
|
|
|
845
845
|
target,
|
|
846
846
|
});
|
|
847
847
|
return () => {
|
|
848
|
-
delete target['
|
|
848
|
+
delete target['__volterRender'];
|
|
849
849
|
};
|
|
850
850
|
}
|
|
851
851
|
|
|
@@ -920,14 +920,14 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
920
920
|
stacked.forEach((entry, i) => {
|
|
921
921
|
const isReact = kindById.get(entry.id) === 'dom';
|
|
922
922
|
const surface = document.createElement(isReact ? 'div' : 'canvas') as HTMLElement;
|
|
923
|
-
surface.dataset['
|
|
923
|
+
surface.dataset['volterRootSurface'] = 'true';
|
|
924
924
|
// WHICH ROOT THIS SURFACE PRESENTS — the host mounted it, so the host
|
|
925
925
|
// knows, and stamping it here is the difference between a declaration and
|
|
926
926
|
// the guess every late reader had to make instead ("the first <canvas> in
|
|
927
927
|
// DOM order is the game's"). Read by `packages/editor/src/presentation-
|
|
928
928
|
// surface.ts`, which is the one door the capture/staleness/screenshot
|
|
929
929
|
// sites now ask (ARCHITECTURE-CORE §The editor protocol, zero inference).
|
|
930
|
-
surface.dataset['
|
|
930
|
+
surface.dataset['volterRootId'] = entry.id;
|
|
931
931
|
if (!isReact) {
|
|
932
932
|
const canvas = surface as unknown as HTMLCanvasElement;
|
|
933
933
|
canvas.width = w;
|
|
@@ -961,7 +961,7 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
961
961
|
|
|
962
962
|
const assets = createAssetCache();
|
|
963
963
|
// Every manifest/host-mounted game is a deterministic-capture candidate —
|
|
964
|
-
// the render-control seam (`?
|
|
964
|
+
// the render-control seam (`?volter-render=1`, render-control.ts) is wired
|
|
965
965
|
// HERE, at the one host every boot path shares (mountManifestRoots and
|
|
966
966
|
// direct createGameRuntime callers both reach this function), instead of asking every project's entry page to install it
|
|
967
967
|
// the way the e2e fixtures do. Production-protected twice over:
|
|
@@ -1030,7 +1030,7 @@ async function createRootsGameRuntime(config: RootsRuntimeConfig): Promise<GameS
|
|
|
1030
1030
|
// Expose the default Three root's scene and camera for dev/e2e tooling.
|
|
1031
1031
|
const retractDevGlobals = installDefaultRootDevGlobals(game);
|
|
1032
1032
|
|
|
1033
|
-
// G3/FT-11 — publish `window.
|
|
1033
|
+
// G3/FT-11 — publish `window.__volterRender` for the deterministic capture
|
|
1034
1034
|
// host. Installed at the TAIL of the mount (same position as
|
|
1035
1035
|
// mount-manifest.ts's debug bridge): every world's `mount()`/`setup()` has
|
|
1036
1036
|
// resolved by now, so the harness's `scene` readiness hook reporting ready
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The standalone in-page debug bridge. Query-gated `window.
|
|
2
|
+
* The standalone in-page debug bridge. Query-gated `window.__volter`, the same
|
|
3
3
|
* production-protection-lives-in-the-installer pattern `render-control.ts`'s
|
|
4
|
-
* `installRenderControlHarness` established for `?
|
|
4
|
+
* `installRenderControlHarness` established for `?volter-render=1` (production
|
|
5
5
|
* protection lives HERE, not just at whatever call site invokes this — a
|
|
6
6
|
* caller that calls {@link maybeInstallDebugBridge} unconditionally on every
|
|
7
7
|
* boot still only ever gets a handle when the gate below actually passes).
|
|
8
8
|
*
|
|
9
9
|
* D17: this is door (a) of the seam's three doors — the SAME `DebugAdapter`
|
|
10
|
-
* names/JSON the editor relay (door b) and `@
|
|
10
|
+
* names/JSON the editor relay (door b) and `@volter/live` (door c, which
|
|
11
11
|
* drives door (a) itself over Playwright) all read. D18: the bridge installs
|
|
12
|
-
* only when the page opts in (`?
|
|
12
|
+
* only when the page opts in (`?volter-debug=1`) AND is either a dev build
|
|
13
13
|
* (`import.meta.env.DEV`) or the project's manifest explicitly opts a
|
|
14
14
|
* production build in (`manifest.debug.allowInProduction`, the described
|
|
15
15
|
* field whose runtime reader THIS module is).
|
|
@@ -23,7 +23,7 @@ import { runTicksWhenSettled } from './run-ticks-settled';
|
|
|
23
23
|
/** Query-param name that opts a standalone page into the debug bridge (D18) —
|
|
24
24
|
* mirrors `render-seed.ts`'s `RENDER_MODE_QUERY_PARAM` naming/shape, own
|
|
25
25
|
* const because this is a different call site with a different flag. */
|
|
26
|
-
export const DEBUG_MODE_QUERY_PARAM = '
|
|
26
|
+
export const DEBUG_MODE_QUERY_PARAM = 'volter-debug';
|
|
27
27
|
|
|
28
28
|
/** The subset of `ResolvedGameManifest` the D18 gate reads — typed narrowly
|
|
29
29
|
* (not imported from `manifest/load.ts`) so this module doesn't need the
|
|
@@ -40,7 +40,7 @@ export interface DebugBridgeManifest {
|
|
|
40
40
|
* resolution function, `debug-registry.ts`'s `resolveInputRootId`) — never
|
|
41
41
|
* "whichever root happened to register last". An explicit `worldId`
|
|
42
42
|
* reaches that root's door specifically. */
|
|
43
|
-
export interface
|
|
43
|
+
export interface VolterDebugInputHandle {
|
|
44
44
|
setVirtualAction(
|
|
45
45
|
action: string,
|
|
46
46
|
value: boolean | number | { x: number; y: number },
|
|
@@ -100,7 +100,7 @@ export interface VgaiDebugInputHandle {
|
|
|
100
100
|
* every field reflects the exact same instant (AC-B1.2's batched-read
|
|
101
101
|
* primitive: a probe polling this never sees `time` from one tick and
|
|
102
102
|
* `state` from another). */
|
|
103
|
-
export interface
|
|
103
|
+
export interface VolterDebugSnapshot {
|
|
104
104
|
/** `loopLiveness` (issue #175): the REAL `GameLoop.liveness` behind this
|
|
105
105
|
* session — `'loop-starved'` when no recent host rAF progress was observed,
|
|
106
106
|
* `null` only when no loop is wired at all (a bare debug-registry test
|
|
@@ -112,11 +112,11 @@ export interface VgaiDebugSnapshot {
|
|
|
112
112
|
pageErrors: string[];
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/** The frozen `window.
|
|
115
|
+
/** The frozen `window.__volter` shape (D18/spec §3.4) — version it if it ever
|
|
116
116
|
* needs a breaking change; `providers`/`state`/`stateAll`/`commands`/`events`
|
|
117
117
|
* are straight off the game's `DebugAdapter`, `invoke` re-checks D18 gating
|
|
118
118
|
* (belt-and-braces, AC-A1.7), and `input`/`snapshot` are bridge-only. */
|
|
119
|
-
export interface
|
|
119
|
+
export interface VolterDebugHandle {
|
|
120
120
|
readonly version: 1;
|
|
121
121
|
providers: DebugAdapter['providers'];
|
|
122
122
|
state: DebugAdapter['state'];
|
|
@@ -124,12 +124,12 @@ export interface VgaiDebugHandle {
|
|
|
124
124
|
commands: DebugAdapter['commands'];
|
|
125
125
|
invoke(name: string, args: unknown[]): Promise<unknown>;
|
|
126
126
|
events: DebugAdapter['events'];
|
|
127
|
-
input:
|
|
127
|
+
input: VolterDebugInputHandle;
|
|
128
128
|
/** See `DebugAdapter.events`'s doc comment for `sinceSeq`'s contract
|
|
129
129
|
* (run-4 friction #5) — `snapshot`'s `events` member is filtered the exact
|
|
130
130
|
* same way, just batched with `time`/`state`/`pageErrors` into one
|
|
131
131
|
* synchronous read. */
|
|
132
|
-
snapshot(sinceSeq?: number):
|
|
132
|
+
snapshot(sinceSeq?: number): VolterDebugSnapshot;
|
|
133
133
|
/**
|
|
134
134
|
* D15/T-D15.4 door (a): synchronously drive `n` fixed gameplay ticks via the
|
|
135
135
|
* live `Game`'s `GameInternal.runTicks` (`runtime/game.ts`) — reached through
|
|
@@ -148,7 +148,7 @@ export interface VgaiDebugHandle {
|
|
|
148
148
|
* (`DebugRegistry.registerWorldSettledProbe`, declared by the game's `debug.settled` entry
|
|
149
149
|
* export) — so a tick never races a scene remount's async commit, and WHICH tick first runs
|
|
150
150
|
* a freshly reloaded world is a function of the sim rather than wall timing. Session tick
|
|
151
|
-
* drivers (`@
|
|
151
|
+
* drivers (`@volter/live`'s fastForward) prefer this door; with no probe registered it behaves
|
|
152
152
|
* exactly like {@link runTicks}. Bounded: throws `WORLD_UNSETTLED_TIMEOUT` if a probe never
|
|
153
153
|
* settles.
|
|
154
154
|
*/
|
|
@@ -159,7 +159,7 @@ export interface VgaiDebugHandle {
|
|
|
159
159
|
* this is the honest human-input-path proof) → `input.clearVirtualActions()`
|
|
160
160
|
* into ONE async bridge call, as a TOP-LEVEL method (not under `input`)
|
|
161
161
|
* because it needs the sim clock, not just the input target. Was 15+
|
|
162
|
-
* transport round trips over the editor relay (`@
|
|
162
|
+
* transport round trips over the editor relay (`@volter/live`'s old
|
|
163
163
|
* `GameInput.hold`: set → a 150ms-interval `waitSimTime` snapshot poll loop
|
|
164
164
|
* → clear); now one `page.evaluate`/relay call that runs the wait
|
|
165
165
|
* in-process on the page.
|
|
@@ -185,7 +185,7 @@ export interface VgaiDebugHandle {
|
|
|
185
185
|
/**
|
|
186
186
|
* Defect 5 fix — undo everything THIS install did: remove the
|
|
187
187
|
* `error`/`unhandledrejection` window listeners it added, and delete
|
|
188
|
-
* `window.
|
|
188
|
+
* `window.__volter` so the registry is no longer reachable from the page.
|
|
189
189
|
* Idempotent (a second call is a harmless no-op). `mountManifestRoots`
|
|
190
190
|
* wires this into its session's `stop()`; a caller mounting more
|
|
191
191
|
* directly (a hand-rolled host) should call it on its own teardown path.
|
|
@@ -218,7 +218,7 @@ export interface MaybeInstallDebugBridgeOptions {
|
|
|
218
218
|
* its `getVirtualInputTarget()` backs `input.*`. */
|
|
219
219
|
readonly registry: DebugRegistry;
|
|
220
220
|
readonly manifest: DebugBridgeManifest;
|
|
221
|
-
/** Where to read `?
|
|
221
|
+
/** Where to read `?volter-debug=1` from. Defaults to `window.location` when a
|
|
222
222
|
* real `window` exists; a headless caller with no `window` at all (and no
|
|
223
223
|
* override) gets no bridge — there's nowhere to read a URL from. */
|
|
224
224
|
readonly url?: { readonly search: string } | undefined;
|
|
@@ -231,13 +231,13 @@ export interface MaybeInstallDebugBridgeOptions {
|
|
|
231
231
|
const PAGE_ERROR_CAP = 100;
|
|
232
232
|
|
|
233
233
|
/** `holdFor`'s in-process poll interval — this loop never leaves the page
|
|
234
|
-
* (no transport round trip per poll, unlike `@
|
|
234
|
+
* (no transport round trip per poll, unlike `@volter/live`'s old
|
|
235
235
|
* `waitSimTime`), so it can afford to be tighter than that loop's 150ms. */
|
|
236
236
|
const HOLD_FOR_POLL_MS = 50;
|
|
237
237
|
|
|
238
238
|
/** Consecutive `HOLD_FOR_POLL_MS` polls with the tick unchanged before
|
|
239
239
|
* `holdFor` gives up waiting and treats the game as stopped — mirrors
|
|
240
|
-
* `@
|
|
240
|
+
* `@volter/live`'s `WAIT_FOR_STALL_POLL_LIMIT` reasoning (`wait-for.ts`:
|
|
241
241
|
* a genuinely frozen sim clock must never poll forever), scaled to this
|
|
242
242
|
* faster in-process interval so the wall-clock grace period (~5s) lands in
|
|
243
243
|
* the same neighborhood. */
|
|
@@ -266,7 +266,7 @@ const STARVED_DRIVE_MAX_BATCHES = 2000;
|
|
|
266
266
|
* string. */
|
|
267
267
|
export const HOLD_STARVED_NO_DRIVER_REASON =
|
|
268
268
|
"the host loop reported liveness 'loop-starved' and this session has no way to drive " +
|
|
269
|
-
'ticks — no recent rAF progress was observed. Check `
|
|
269
|
+
'ticks — no recent rAF progress was observed. Check `volter-game-editor status` for the separate ' +
|
|
270
270
|
'visibility readings; foreground/reload the editor, or start play to wire the run-ticks ' +
|
|
271
271
|
'target, then retry.';
|
|
272
272
|
|
|
@@ -394,7 +394,7 @@ function buildDebugHandle(opts: {
|
|
|
394
394
|
registry: DebugRegistry;
|
|
395
395
|
manifest: DebugBridgeManifest;
|
|
396
396
|
window: DebugBridgeWindowTarget;
|
|
397
|
-
}):
|
|
397
|
+
}): VolterDebugHandle {
|
|
398
398
|
const { registry, manifest } = opts;
|
|
399
399
|
const adapter = registry.adapter;
|
|
400
400
|
|
|
@@ -474,7 +474,7 @@ function buildDebugHandle(opts: {
|
|
|
474
474
|
requireInputTarget('injectPointerPosition', worldId).injectPointerPosition(sourceId, value),
|
|
475
475
|
},
|
|
476
476
|
snapshot(sinceSeq) {
|
|
477
|
-
// One synchronous pass — see
|
|
477
|
+
// One synchronous pass — see VolterDebugSnapshot's doc comment.
|
|
478
478
|
return {
|
|
479
479
|
time: adapter.state('time') as {
|
|
480
480
|
simSeconds: number;
|
|
@@ -528,13 +528,13 @@ function buildDebugHandle(opts: {
|
|
|
528
528
|
uninstall() {
|
|
529
529
|
opts.window.removeEventListener?.('error', onWindowError);
|
|
530
530
|
opts.window.removeEventListener?.('unhandledrejection', onWindowRejection);
|
|
531
|
-
delete opts.window['
|
|
531
|
+
delete opts.window['__volter'];
|
|
532
532
|
},
|
|
533
533
|
};
|
|
534
534
|
}
|
|
535
535
|
|
|
536
536
|
/**
|
|
537
|
-
* Install `window.
|
|
537
|
+
* Install `window.__volter` iff the page opted in (`?volter-debug=1`) AND D18's
|
|
538
538
|
* gate passes (dev build, or manifest `debug.allowInProduction`). When the
|
|
539
539
|
* param is present but the gate fails, warns once (this single call IS the
|
|
540
540
|
* "once" — there is exactly one install attempt per boot) and installs
|
|
@@ -543,7 +543,7 @@ function buildDebugHandle(opts: {
|
|
|
543
543
|
*/
|
|
544
544
|
export function maybeInstallDebugBridge(
|
|
545
545
|
opts: MaybeInstallDebugBridgeOptions,
|
|
546
|
-
):
|
|
546
|
+
): VolterDebugHandle | undefined {
|
|
547
547
|
const url = opts.url ?? (hasRealWindow() ? window.location : undefined);
|
|
548
548
|
if (!url) return undefined;
|
|
549
549
|
if (!isDebugModeRequested(url)) return undefined;
|
|
@@ -555,7 +555,7 @@ export function maybeInstallDebugBridge(
|
|
|
555
555
|
if (!isDebugAllowed(opts.manifest)) {
|
|
556
556
|
// biome-ignore lint/suspicious/noConsole: structured, greppable D18 signal — mirrors debug-registry.ts's own console.warn idiom.
|
|
557
557
|
console.warn(
|
|
558
|
-
'[debug] ?
|
|
558
|
+
'[debug] ?volter-debug=1 ignored: production build without debug.allowInProduction (D18)',
|
|
559
559
|
);
|
|
560
560
|
return undefined;
|
|
561
561
|
}
|
|
@@ -565,6 +565,6 @@ export function maybeInstallDebugBridge(
|
|
|
565
565
|
manifest: opts.manifest,
|
|
566
566
|
window: windowTarget,
|
|
567
567
|
});
|
|
568
|
-
windowTarget['
|
|
568
|
+
windowTarget['__volter'] = handle;
|
|
569
569
|
return handle;
|
|
570
570
|
}
|