@volter/editor-game 0.5.67 → 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-player.document.tsx +95 -0
- 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 +9 -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 +19 -8
- package/src/build/build-session.ts +35 -0
- package/src/canvas/canvas-board/CanvasBoardDocument.tsx +78 -5
- 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 +206 -28
- package/src/host/authoring/pixi-creatable-kinds.ts +80 -0
- package/src/host/authoring/pixi-live-write-target.ts +6 -5
- package/src/host/authoring/pixi-source-write-target.ts +118 -20
- 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 +3 -3
- 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 +13 -1
- 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/served-bundle-runtime-modules.ts +2 -0
- 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 +3 -3
- 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 +521 -54
- package/src/network/network-inspector-model.ts +6 -0
- 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 +15 -15
- 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 +24 -11
- 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 +125 -15
- 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
package/NOTICE
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
Copyright 2026 Volter AI, Inc.
|
|
2
2
|
|
|
3
|
-
The files in this package were imported from volter-ai/
|
|
3
|
+
The files in this package were imported from volter-ai/volter-engine at commit
|
|
4
4
|
09c2749ce. LICENSE contains the AGPL-3.0-only license they retain.
|
|
5
5
|
|
|
6
|
-
packages/game/src, packages/game/contributions
|
|
6
|
+
packages/game/src, packages/game/contributions
|
|
7
7
|
-> src, contributions
|
|
8
|
-
packages/dom/src, packages/dom/contributions
|
|
8
|
+
packages/dom/src, packages/dom/contributions
|
|
9
9
|
-> src/react, contributions/react
|
|
10
10
|
packages/threejs/src except viewport/ and capture/, packages/threejs/contributions
|
|
11
|
-
|
|
12
|
-
host modules of packages/editor/src
|
|
11
|
+
-> src/three, contributions/three
|
|
12
|
+
host modules of packages/editor/src -> src/host, including
|
|
13
13
|
gameplay-export.ts and document-preview-three.ts, and the world-root stage
|
|
14
14
|
(components/world-root-stage.ts and the modules it draws with)
|
|
15
15
|
src/host/components/world-root-stage-binding.tsx is transcribed from the
|
|
@@ -18,6 +18,6 @@ The files in this package were imported from volter-ai/vgai-engine at commit
|
|
|
18
18
|
contributions/scene-document.service.ts from the Scene document half of
|
|
19
19
|
packages/editor/src/components/CenterDocuments.tsx
|
|
20
20
|
|
|
21
|
-
src/host/sdk/tools.ts was imported from
|
|
22
|
-
|
|
21
|
+
src/host/sdk/tools.ts was imported from src/tools.ts of the Apache-2.0 SDK
|
|
22
|
+
package of volter-ai/volter-engine, and retains that license. It re-exports the
|
|
23
23
|
Apache-2.0 tool contract of @volter/editor-sdk.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* which fires at once when one is already registered) rather than for a React
|
|
13
13
|
* mount.
|
|
14
14
|
*
|
|
15
|
-
* It reports its failure to the editor console — `
|
|
15
|
+
* It reports its failure to the editor console — `volter-game-editor console` is what reads
|
|
16
16
|
* it — rather than leaving a stopped surface with no reason on it.
|
|
17
17
|
*/
|
|
18
18
|
import { onShellStore } from '@volter/editor-sdk/kit/shell-store-door';
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BUILD PLAYER (`workspace.document`) — the project's last build, run as a
|
|
3
|
+
* player runs it: its own `dist/`, served by the editor server, in a frame
|
|
4
|
+
* with nothing of the editor inside it. Build Profiles' Build And Run opens
|
|
5
|
+
* it, as Unity's Build And Run launches the player and Godot's Web export
|
|
6
|
+
* runs in a browser; each run reloads the frame.
|
|
7
|
+
*
|
|
8
|
+
* What the build prints reaches the editor's console, prefixed
|
|
9
|
+
* `[Build Player]`, as a Godot export run with remote debug reports into the
|
|
10
|
+
* editor's debugger: its console calls, and its uncaught errors. The capture
|
|
11
|
+
* door photographs the build itself (`framed-document-capture.ts`).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ToolContributionProps } from '@volter/editor-sdk/contributions';
|
|
15
|
+
import { registerFramedCapture } from '@volter/editor-sdk/kit/framed-document-capture';
|
|
16
|
+
import { useSyncExternalStore } from 'react';
|
|
17
|
+
import { BUILD_PLAYER_URL, buildRunCount, subscribeBuildRuns } from '../src/build/build-session';
|
|
18
|
+
|
|
19
|
+
export const point = 'workspace.document';
|
|
20
|
+
export const title = 'Build Player';
|
|
21
|
+
|
|
22
|
+
const FORWARDED = ['log', 'info', 'warn', 'error'] as const;
|
|
23
|
+
|
|
24
|
+
const HOOKED = Symbol('build-player-output');
|
|
25
|
+
|
|
26
|
+
/** Hook the player's document once it exists: the build's module scripts run
|
|
27
|
+
* before `load`, after its HTML is parsed and while their bundle is fetched,
|
|
28
|
+
* so the frame is watched from insertion until its document is the build's. */
|
|
29
|
+
function watchFrame(frame: HTMLIFrameElement | null): void {
|
|
30
|
+
if (!frame) return;
|
|
31
|
+
const tick = () => {
|
|
32
|
+
if (!frame.isConnected) return;
|
|
33
|
+
const player = frame.contentWindow as (Window & typeof globalThis & { [HOOKED]?: true }) | null;
|
|
34
|
+
if (player?.location.pathname === new URL(BUILD_PLAYER_URL, location.href).pathname) {
|
|
35
|
+
if (!player[HOOKED]) {
|
|
36
|
+
forwardOutput(player);
|
|
37
|
+
registerFramedCapture(frame, {
|
|
38
|
+
container: () => player.document.getElementById('game-canvas') ?? player.document.body,
|
|
39
|
+
canvasFrame: (canvas) => copyAfterRender(player, canvas),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (player.document.readyState === 'complete') return;
|
|
43
|
+
}
|
|
44
|
+
setTimeout(tick, 0);
|
|
45
|
+
};
|
|
46
|
+
tick();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A copy of `canvas` taken in the player's next frame, after the game's own
|
|
50
|
+
* frame callbacks (registered a frame earlier) have drawn it. */
|
|
51
|
+
function copyAfterRender(player: Window & typeof globalThis, canvas: HTMLCanvasElement): Promise<CanvasImageSource | null> {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
player.requestAnimationFrame(() => {
|
|
54
|
+
const copy = player.document.createElement('canvas');
|
|
55
|
+
copy.width = canvas.width;
|
|
56
|
+
copy.height = canvas.height;
|
|
57
|
+
const context = copy.getContext('2d');
|
|
58
|
+
if (!context) return resolve(null);
|
|
59
|
+
context.drawImage(canvas, 0, 0);
|
|
60
|
+
resolve(copy);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function forwardOutput(player: Window & typeof globalThis & { [HOOKED]?: true }): void {
|
|
66
|
+
player[HOOKED] = true;
|
|
67
|
+
for (const level of FORWARDED) {
|
|
68
|
+
const own = player.console[level].bind(player.console);
|
|
69
|
+
player.console[level] = (...args: unknown[]) => {
|
|
70
|
+
own(...args);
|
|
71
|
+
console[level]('[Build Player]', ...args);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
player.addEventListener('error', (event) => {
|
|
75
|
+
console.error('[Build Player] Uncaught', event.error ?? event.message);
|
|
76
|
+
});
|
|
77
|
+
player.addEventListener('unhandledrejection', (event) => {
|
|
78
|
+
console.error('[Build Player] Unhandled rejection', event.reason);
|
|
79
|
+
});
|
|
80
|
+
console.info(`[Build Player] Running ${player.document.title || 'the build'}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default function BuildPlayerDocument(_props: ToolContributionProps) {
|
|
84
|
+
const run = useSyncExternalStore(subscribeBuildRuns, buildRunCount);
|
|
85
|
+
return (
|
|
86
|
+
<iframe
|
|
87
|
+
key={run}
|
|
88
|
+
title="Build Player"
|
|
89
|
+
data-build-player={run}
|
|
90
|
+
src={`${BUILD_PLAYER_URL}?run=${run}`}
|
|
91
|
+
ref={watchFrame}
|
|
92
|
+
style={{ width: '100%', height: '100%', border: 0, display: 'block', background: '#000' }}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* as a center document: the build-role targets, the manifest fields a build
|
|
4
4
|
* reads, and the Build trigger. A document rather than a dialog because the
|
|
5
5
|
* configuration is a persistent authored surface (it writes
|
|
6
|
-
* `
|
|
6
|
+
* `volter.project.json` through the history service) someone tunes and re-runs,
|
|
7
7
|
* and because a modal would block the viewport while a build streams.
|
|
8
8
|
*
|
|
9
9
|
* It ships with `@volter/editor-game`, so a folder of models has no Build Profiles tab
|
|
@@ -23,7 +23,7 @@ export default function BuildProgressStatus() {
|
|
|
23
23
|
if (session.phase === 'building') {
|
|
24
24
|
return (
|
|
25
25
|
<span
|
|
26
|
-
className="
|
|
26
|
+
className="volter-status-copy"
|
|
27
27
|
data-testid="status-build-progress"
|
|
28
28
|
data-build-phase="building"
|
|
29
29
|
data-status-tone="accent"
|
|
@@ -34,7 +34,7 @@ export default function BuildProgressStatus() {
|
|
|
34
34
|
}
|
|
35
35
|
return (
|
|
36
36
|
<span
|
|
37
|
-
className="
|
|
37
|
+
className="volter-status-copy"
|
|
38
38
|
data-testid="status-build-progress"
|
|
39
39
|
data-build-phase={session.result?.ok ? 'complete' : 'failed'}
|
|
40
40
|
data-status-tone={session.result?.ok ? 'success' : 'danger'}
|
|
@@ -25,7 +25,7 @@ export default function BuildHeader() {
|
|
|
25
25
|
aria-label="Export project"
|
|
26
26
|
disabled={playing}
|
|
27
27
|
title={playing ? 'Stop Play mode before exporting the project' : 'Export project'}
|
|
28
|
-
className="
|
|
28
|
+
className="volter-chrome-island volter-glass-island"
|
|
29
29
|
onClick={() => {
|
|
30
30
|
openBuildProfilesDocument();
|
|
31
31
|
}}
|
|
@@ -31,7 +31,7 @@ export default function ConnectionStatus() {
|
|
|
31
31
|
type="button"
|
|
32
32
|
variant="ghost"
|
|
33
33
|
size="compact"
|
|
34
|
-
className="
|
|
34
|
+
className="volter-status-action"
|
|
35
35
|
data-testid="status-connection"
|
|
36
36
|
data-connection-state={state}
|
|
37
37
|
onClick={() => editorHost().workspace.showUtility(NETWORK_UTILITY_ID)}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* ## The two doors, both the host's own, neither of them new vocabulary
|
|
17
17
|
*
|
|
18
|
-
* - `session.reportFacet(collect, { reusableKeys })` — the four `
|
|
18
|
+
* - `session.reportFacet(collect, { reusableKeys })` — the four `volter-game-editor status`
|
|
19
19
|
* facets. The keys are declared REUSABLE because grading a root re-walks
|
|
20
20
|
* its tree (76ms–1.3s, measured), which is exactly the cost
|
|
21
21
|
* `command-listener.ts`'s `REUSABLE_DERIVED_FACETS` exists to keep off the
|
|
@@ -86,7 +86,7 @@ function coverageFacets(reuse: Record<string, unknown> | null): Record<string, u
|
|
|
86
86
|
: null,
|
|
87
87
|
// The THIRD derived family, and the one that lives outside both adapter
|
|
88
88
|
// contracts: the native verbs whose subject is the PROJECT rather than a
|
|
89
|
-
// mounted root — its committed proof route, whether `
|
|
89
|
+
// mounted root — its committed proof route, whether `volter-game-editor add` can land a
|
|
90
90
|
// capability here, and whether it can produce a standalone build at all
|
|
91
91
|
// (`@editor/coverage/project-verb-coverage`). Without it a project could
|
|
92
92
|
// be green in the other two families and silently have none of the three.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The STEP verbs of the session wire (`@volter/editor-sdk/commands`, a
|
|
3
3
|
* `workspace.command` contribution): `game-eval`, the module lane behind
|
|
4
|
-
* `
|
|
4
|
+
* `volter-game-editor eval`'s `game.run(...)`, and `page-script`, the in-page Playwright
|
|
5
5
|
* shim behind `page(...)`. Both carry a step function's own SOURCE over the
|
|
6
6
|
* wire and reconstruct it here — closures do not survive the trip — so both
|
|
7
7
|
* are gated on a live game surface (a play session or an ingest mount) and
|
|
8
8
|
* rooted at the game container, never editor chrome.
|
|
9
9
|
*
|
|
10
|
-
* The module lane's page-side door (`window.
|
|
10
|
+
* The module lane's page-side door (`window.__volterGameEval`) and the two
|
|
11
11
|
* providers it needs are installed when this module loads, which is when the
|
|
12
12
|
* verbs themselves become available: one handler, two doors, registered
|
|
13
13
|
* together.
|
|
@@ -37,7 +37,7 @@ export const point = 'workspace.command';
|
|
|
37
37
|
* source in the editor page (same wire contract as `page-script`: closures do
|
|
38
38
|
* not survive) and runs it against a scope of { page, modules, instanceId } —
|
|
39
39
|
* `modules(path)` importing the RUNNING mount's own module instances
|
|
40
|
-
* (`game-module-access.ts`). This is what makes `
|
|
40
|
+
* (`game-module-access.ts`). This is what makes `volter-game-editor eval` able to touch the
|
|
41
41
|
* game's exported modules directly, in the browser, with no registry: the
|
|
42
42
|
* step is literal JS against the game's real functions.
|
|
43
43
|
*
|
|
@@ -51,12 +51,12 @@ export const point = 'workspace.command';
|
|
|
51
51
|
*/
|
|
52
52
|
declare global {
|
|
53
53
|
interface Window {
|
|
54
|
-
|
|
54
|
+
__volterGameEval?: (src: string, instance?: string) => Promise<unknown>;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
if (typeof window !== 'undefined') {
|
|
58
58
|
setFocusedInstanceProvider(focusedInstanceId);
|
|
59
|
-
window.
|
|
59
|
+
window.__volterGameEval = async (src: string, instance?: string) => {
|
|
60
60
|
const result = await handleGameEval({
|
|
61
61
|
type: 'game-eval',
|
|
62
62
|
src,
|
|
@@ -151,7 +151,7 @@ async function handlePageScript(cmd: EditorCommandMessage): Promise<EditorComman
|
|
|
151
151
|
// first-party `play-mode.ts` session. Keep the Playwright-style page door on
|
|
152
152
|
// the same reachability rule as bridge calls/screenshots: a real ingest
|
|
153
153
|
// container is sufficient, while the genuinely-unmounted case still
|
|
154
|
-
// refuses below. Without this, `
|
|
154
|
+
// refuses below. Without this, `volter-game-editor play` could start an ingested game and
|
|
155
155
|
// `game.commands()` would work, yet `page(...)` answered "not in play mode"
|
|
156
156
|
// against that very same visible game.
|
|
157
157
|
if (!isPlayModeActive() && !isIngestActive()) return notPlayingResult();
|
|
@@ -96,7 +96,7 @@ export const commands: CommandContribution['commands'] = {
|
|
|
96
96
|
}
|
|
97
97
|
// D15/T-D15.5 (review objection 2's fix) — resolved through the SAME
|
|
98
98
|
// per-world `DebugRegistry.getVirtualInputTarget(worldId?)` the debug
|
|
99
|
-
// bridge's `window.
|
|
99
|
+
// bridge's `window.__volter.input.*` calls, instead of the old
|
|
100
100
|
// `Game.input` (always the DEFAULT world only) — an explicit
|
|
101
101
|
// `cmd['worldId']` reaches that world specifically; omitted resolves
|
|
102
102
|
// to the same default both doors now share. Throws
|
|
@@ -217,7 +217,7 @@ export const commands: CommandContribution['commands'] = {
|
|
|
217
217
|
// session's `GameInternal.runTicks` through `getPlayRuntimeAccess()`'s
|
|
218
218
|
// `runTicks` accessor (itself backed by the SAME
|
|
219
219
|
// `DebugRegistry.getRunTicksTarget()` the bridge's
|
|
220
|
-
// `window.
|
|
220
|
+
// `window.__volter.runTicks` calls — one implementation, byte-identical
|
|
221
221
|
// behavior across doors, D17). `runTicks` itself throws a structured
|
|
222
222
|
// `DebugError` (`RUN_TICKS_PAUSED`) while paused — never a silent no-op —
|
|
223
223
|
// which `structuredErrorResult` below carries through as `data.code`.
|
|
@@ -38,7 +38,7 @@ export default function GenerationsStatus() {
|
|
|
38
38
|
type="button"
|
|
39
39
|
variant="ghost"
|
|
40
40
|
size="compact"
|
|
41
|
-
className="
|
|
41
|
+
className="volter-status-action"
|
|
42
42
|
data-testid="status-generations"
|
|
43
43
|
aria-label={`Generations, ${unseen} unseen, ${running} running`}
|
|
44
44
|
title={`${unseen} unseen generation results, ${running} running — open Generations`}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* registration, its `ingest`/`ingestCaptureWait` state facets and its
|
|
10
10
|
* `onShellStore` binding are module-load side effects of
|
|
11
11
|
* `mount-ingest-root.ts`. The static import below is therefore load-bearing
|
|
12
|
-
* — `
|
|
12
|
+
* — `volter-game-editor status` carries `ingest: null` / `ingestCaptureWait: null` before
|
|
13
13
|
* contribution pass. A product that composes no `@volter/editor-game` reports
|
|
14
14
|
* neither key, which is the honest state: no lane claims to mount
|
|
15
15
|
* unmodified games.
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* `isIngestActive()` is answered by a mount this hook already made.
|
|
25
25
|
*
|
|
26
26
|
* 3. REGISTERING THIS LANE'S ADAPTER DECLARATIONS. A repo-vendored game does
|
|
27
|
-
* not ship its own `
|
|
27
|
+
* not ship its own `volter.adapter.ts` — by the REALM rule its host-realm
|
|
28
28
|
* declaration lives in the in-tree registry (`@editor/ingest/registry`),
|
|
29
29
|
* which the host used to import directly and therefore carried in every
|
|
30
30
|
* editor boot, a `models` build that mounts no unmodified game included.
|
|
@@ -116,7 +116,7 @@ export const commands: CommandContribution['commands'] = {
|
|
|
116
116
|
// guard here: `play` on an already-playing (or still-booting) session
|
|
117
117
|
// is a RESTART, not a no-op, matching the SDK's own documented contract
|
|
118
118
|
// (`play.start`'s summary: "Start (or restart) play mode" —
|
|
119
|
-
// `packages/
|
|
119
|
+
// `packages/volter-sdk/src/play/lifecycle-operations.ts`). What used to
|
|
120
120
|
// make a restart unsafe was `enterPlayMode()` having no reentrancy
|
|
121
121
|
// guard for the async-boot window before it assigns `_session` — two
|
|
122
122
|
// overlapping calls ran their entire prologues concurrently and
|
|
@@ -127,11 +127,11 @@ export const commands: CommandContribution['commands'] = {
|
|
|
127
127
|
// SDK, a UI Play-button click racing a relayed command — gets a safe,
|
|
128
128
|
// clean restart for free without needing a guard here too.
|
|
129
129
|
try {
|
|
130
|
-
// D15/T-D15.6 (objection-4 fix) — `
|
|
130
|
+
// D15/T-D15.6 (objection-4 fix) — `volter-game-editor play --seed <n>` relays as
|
|
131
131
|
// `cmd['seed']`; `enterPlayMode`'s explicit-config seed leg (beats
|
|
132
|
-
// manifest.determinism.defaultSeed/?
|
|
132
|
+
// manifest.determinism.defaultSeed/?volter-seed=).
|
|
133
133
|
const seed = cmd['seed'];
|
|
134
|
-
// `
|
|
134
|
+
// `volter-game-editor play --name <text>` rides the same wire as `--seed`: a plain
|
|
135
135
|
// field on the relayed command, slugified server-side into this run's
|
|
136
136
|
// log filename and journal line. Absent, everything is unchanged.
|
|
137
137
|
const runName = cmd['name'];
|
|
@@ -173,11 +173,11 @@ export const commands: CommandContribution['commands'] = {
|
|
|
173
173
|
// EVERY PLAY RECORDS. There is no flag and no opt-in — see
|
|
174
174
|
// `play-recording.ts` for why the capability had to stop being one an
|
|
175
175
|
// agent must remember to switch on. `cmd['record']` only NAMES the file
|
|
176
|
-
// (`
|
|
176
|
+
// (`volter-game-editor play --record <name>`); its absence names the clip after the
|
|
177
177
|
// durable Gameplay Session.
|
|
178
178
|
//
|
|
179
179
|
// Started here rather than inside `enterPlayMode` on purpose: this
|
|
180
|
-
// handler is the RELAYED play (`
|
|
180
|
+
// handler is the RELAYED play (`volter-game-editor play`, the SDK's `play.start`),
|
|
181
181
|
// which is exactly the population the recording exists for. A human
|
|
182
182
|
// clicking the editor's own Play button is their own witness and gets
|
|
183
183
|
// play unchanged.
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
* the DOM projection and the CSS inspection estate
|
|
63
63
|
* (`components/RootSelectionOverlay.tsx`); the surgical JSX writer and the
|
|
64
64
|
* structural write pipe (`authoring/r3f-source-authoring-adapter.ts`,
|
|
65
|
-
* `@
|
|
65
|
+
* `@volter/canvas/pixi-source-write-target.ts`); the breakpoint state
|
|
66
66
|
* (`components/BoardRulers.tsx`); `authoring/react-story-board.ts`, the
|
|
67
67
|
* board's geometry, which three host surfaces read
|
|
68
68
|
* (`components/RootSelectionOverlay.tsx:98`,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@volter/editor-game",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.69",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"./host/stories/story-pixi-preview": "./src/host/stories/story-pixi-preview.ts",
|
|
31
31
|
"./host/stories/story-media-captures": "./src/host/stories/story-media-captures.ts"
|
|
32
32
|
},
|
|
33
|
-
"
|
|
33
|
+
"volter": {
|
|
34
34
|
"contributions": [
|
|
35
35
|
"./contributions/asset-budget-asset.menu.ts",
|
|
36
36
|
"./contributions/asset-budget.action.ts",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"./contributions/autoplay.service.ts",
|
|
43
43
|
"./contributions/bridge.command.ts",
|
|
44
44
|
"./contributions/build-profiles.document.tsx",
|
|
45
|
+
"./contributions/build-player.document.tsx",
|
|
45
46
|
"./contributions/build-progress.status.tsx",
|
|
46
47
|
"./contributions/build.action.ts",
|
|
47
48
|
"./contributions/build.command.ts",
|
|
@@ -100,11 +101,11 @@
|
|
|
100
101
|
"serving": "./dist-node/serving.mjs"
|
|
101
102
|
},
|
|
102
103
|
"dependencies": {
|
|
103
|
-
"@volter/editor-sdk": "0.5.
|
|
104
|
-
"@volter/editor-project": "0.5.
|
|
105
|
-
"@volter/editor-threejs": "0.5.
|
|
106
|
-
"@volter/threejs-runtime": "0.5.
|
|
107
|
-
"@volter/game-runtime": "0.5.
|
|
104
|
+
"@volter/editor-sdk": "0.5.69",
|
|
105
|
+
"@volter/editor-project": "0.5.69",
|
|
106
|
+
"@volter/editor-threejs": "0.5.69",
|
|
107
|
+
"@volter/threejs-runtime": "0.5.69",
|
|
108
|
+
"@volter/game-runtime": "0.5.69",
|
|
108
109
|
"@colyseus/msgpackr": "^1.11.3",
|
|
109
110
|
"@colyseus/schema": "^4.0.14",
|
|
110
111
|
"@colyseus/sdk": "^0.17.34",
|
|
@@ -137,7 +138,7 @@
|
|
|
137
138
|
"xstate": "^5.32.4",
|
|
138
139
|
"zod": "^4.3.6",
|
|
139
140
|
"@fortawesome/fontawesome-svg-core": "^7.2.0",
|
|
140
|
-
"@volter/editor-react": "0.5.
|
|
141
|
+
"@volter/editor-react": "0.5.69"
|
|
141
142
|
},
|
|
142
143
|
"peerDependencies": {
|
|
143
144
|
"@sparkjsdev/spark": "^2.1.0",
|
package/serving/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@volter/editor-game`'s server half (`package.json#
|
|
2
|
+
* `@volter/editor-game`'s server half (`package.json#volter.serving`): the one deep specifier a
|
|
3
3
|
* game's own contributions name, `@editor/game-module-access` (the template's
|
|
4
4
|
* `src/contributions/use-game-modules.ts`), resolved to this package's module. The page
|
|
5
5
|
* bundle maps the same specifier for its own copy (`src/host/served-bundle-runtime-modules.ts`);
|
|
@@ -882,7 +882,7 @@ function OptimizeActions({ entry, onApplied }: { entry: AssetBudgetEntry; onAppl
|
|
|
882
882
|
{lodCandidates.length > 0 ? (
|
|
883
883
|
<div style={{ display: 'flex', gap: spaceVar[2], marginTop: spaceVar[3] }}>
|
|
884
884
|
<Select
|
|
885
|
-
className="
|
|
885
|
+
className="volter-select"
|
|
886
886
|
aria-label="LOD base node"
|
|
887
887
|
data-testid="asset-budget-op-lod-node"
|
|
888
888
|
value={chosenLodNode}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - the project REFERENCE GRAPH: which manifest entries, code files, or
|
|
13
13
|
* sibling assets mention each asset ("what uses this", critique P5),
|
|
14
14
|
* the unused list derived from it, and duplicate groups by sha256
|
|
15
|
-
* - provenance stamps projected from `.
|
|
15
|
+
* - provenance stamps projected from `.volter/provenance.json`
|
|
16
16
|
*
|
|
17
17
|
* Anti-shim: anything unmeasurable is reported as absent/unknown with a
|
|
18
18
|
* reason (`inspectError`, `gpuBytes: null`, `unknownTextureCount`) — never a
|
|
@@ -190,7 +190,7 @@ export interface AssetBudgetSource {
|
|
|
190
190
|
* Reference-owner documents living OUTSIDE the walked snapshot, scanned for
|
|
191
191
|
* references but never budget rows. Server mode uses this for the project
|
|
192
192
|
* manifest: `HttpStorage` is hard-rooted at `public/` (its own doc comment;
|
|
193
|
-
* the T3.3 "two-`.
|
|
193
|
+
* the T3.3 "two-`.volter`-dirs" finding), so `volter.project.json` is unreachable
|
|
194
194
|
* through the seam and is fetched over the dev server instead — without it,
|
|
195
195
|
* every manifest-referenced asset (input maps, scenes) would be reported
|
|
196
196
|
* unused, a fabricated finding.
|
|
@@ -213,7 +213,7 @@ export interface AssetBudgetSource {
|
|
|
213
213
|
readonly codeReferencesScannable?: boolean;
|
|
214
214
|
readBytes(path: string): Promise<Uint8Array>;
|
|
215
215
|
readText(path: string): Promise<string>;
|
|
216
|
-
/** Parsed `.
|
|
216
|
+
/** Parsed `.volter/provenance.json` (or null/undefined when absent). */
|
|
217
217
|
readonly provenance?: unknown;
|
|
218
218
|
}
|
|
219
219
|
|
|
@@ -282,7 +282,7 @@ export function isEngineVendoredRuntimePath(path: string): boolean {
|
|
|
282
282
|
// --- Reference scanning ------------------------------------------------------
|
|
283
283
|
|
|
284
284
|
const CODE_OWNER_PATTERN = /\.(?:tsx?|jsx?|mjs|cjs)$/i;
|
|
285
|
-
const MANIFEST_NAME = '
|
|
285
|
+
const MANIFEST_NAME = 'volter.project.json';
|
|
286
286
|
|
|
287
287
|
/**
|
|
288
288
|
* The manifest is the ONLY JSON document that owns asset references. An
|
|
@@ -450,7 +450,7 @@ const IGNORED_DIRS = new Set([
|
|
|
450
450
|
'.git',
|
|
451
451
|
'dist',
|
|
452
452
|
'build',
|
|
453
|
-
'.
|
|
453
|
+
'.volter',
|
|
454
454
|
'.ci-scaffold',
|
|
455
455
|
'coverage',
|
|
456
456
|
'test-results',
|
|
@@ -476,21 +476,21 @@ export async function collectProjectBudgetSource(
|
|
|
476
476
|
await walk('');
|
|
477
477
|
|
|
478
478
|
// Server mode: `HttpStorage` is hard-rooted at the project's `public/`
|
|
479
|
-
// folder (its doc comment; the T3.3 "two-`.
|
|
479
|
+
// folder (its doc comment; the T3.3 "two-`.volter`-dirs" finding), so BOTH
|
|
480
480
|
// project-root documents the budget needs live outside the seam. Use the
|
|
481
481
|
// dev server's own idioms for each: the raw manifest served at
|
|
482
|
-
// `/
|
|
483
|
-
// `/__editor/
|
|
482
|
+
// `/volter.project.json` (`manifest-project.ts`) and the `.volter/`-scoped
|
|
483
|
+
// `/__editor/volter-file` route (`project-provenance.ts`). Anything
|
|
484
484
|
// unavailable stays honestly absent — no fabricated owners or stamps.
|
|
485
485
|
if (isPublicRootedBackend(backend)) {
|
|
486
486
|
const ownerFiles: AssetBudgetFile[] = [];
|
|
487
487
|
const virtualTexts = new Map<string, string>();
|
|
488
488
|
try {
|
|
489
|
-
const manifestRes = await fetch('/
|
|
489
|
+
const manifestRes = await fetch('/volter.project.json');
|
|
490
490
|
if (manifestRes.ok) {
|
|
491
491
|
const text = await manifestRes.text();
|
|
492
|
-
virtualTexts.set('
|
|
493
|
-
ownerFiles.push({ path: '
|
|
492
|
+
virtualTexts.set('volter.project.json', text);
|
|
493
|
+
ownerFiles.push({ path: 'volter.project.json', size: text.length });
|
|
494
494
|
}
|
|
495
495
|
} catch {
|
|
496
496
|
// No reachable manifest → no manifest-owned references.
|
|
@@ -501,7 +501,7 @@ export async function collectProjectBudgetSource(
|
|
|
501
501
|
// answers `text/plain` raw file bytes, which that JSON-only reader would
|
|
502
502
|
// reject as a non-answer. See `optimize-apply.ts`'s `readLedgerText`.
|
|
503
503
|
const provenanceRes = await fetch(
|
|
504
|
-
`/__editor/
|
|
504
|
+
`/__editor/volter-file?${new URLSearchParams({ path: '.volter/provenance.json' })}`,
|
|
505
505
|
);
|
|
506
506
|
if (provenanceRes.ok) provenance = JSON.parse(await provenanceRes.text());
|
|
507
507
|
} catch {
|
|
@@ -525,8 +525,8 @@ export async function collectProjectBudgetSource(
|
|
|
525
525
|
|
|
526
526
|
let provenance: unknown = null;
|
|
527
527
|
try {
|
|
528
|
-
if (await backend.exists('.
|
|
529
|
-
provenance = JSON.parse(await backend.read('.
|
|
528
|
+
if (await backend.exists('.volter/provenance.json')) {
|
|
529
|
+
provenance = JSON.parse(await backend.read('.volter/provenance.json'));
|
|
530
530
|
}
|
|
531
531
|
} catch {
|
|
532
532
|
provenance = null; // unreadable provenance = no stamps, never a crash
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Storage-side APPLY flow for Asset Budget optimizations (W4a M3).
|
|
3
3
|
*
|
|
4
4
|
* One place owns "optimized bytes land in the project": write through the
|
|
5
|
-
* storage seam, stamp central provenance (`.
|
|
5
|
+
* storage seam, stamp central provenance (`.volter/provenance.json`, the SAME
|
|
6
6
|
* document/idiom the generative-assets pipeline stamps and
|
|
7
7
|
* `project-provenance.ts` projects — one operation with an `outputs` entry
|
|
8
8
|
* carrying path/bytes/sha256), invalidate the engine's URL-keyed asset cache
|
|
@@ -27,7 +27,7 @@ import { handleProjectMutationFailure } from '@volter/editor-sdk/kit/source-conf
|
|
|
27
27
|
import { isPublicRootedBackend, type StorageBackend } from '@volter/editor-sdk/kit/storage-types';
|
|
28
28
|
import { invalidateCachedAsset } from '@volter/threejs-runtime/asset-loaders';
|
|
29
29
|
|
|
30
|
-
const PROVENANCE_PATH = '.
|
|
30
|
+
const PROVENANCE_PATH = '.volter/provenance.json';
|
|
31
31
|
|
|
32
32
|
/** Serving path (`/models/x.glb`) or bare path → storage path under public/. */
|
|
33
33
|
export function assetStoragePath(path: string): string {
|
|
@@ -74,10 +74,10 @@ function ledgerAssetPath(storagePath: string): string {
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Ledger IO honoring the backend's reach: server-mode `HttpStorage` is
|
|
77
|
-
* hard-rooted at `public/`, so writing `.
|
|
78
|
-
* would land at `public/.
|
|
79
|
-
* (the exact T3.3 "two-`.
|
|
80
|
-
* `.
|
|
77
|
+
* hard-rooted at `public/`, so writing `.volter/provenance.json` through it
|
|
78
|
+
* would land at `public/.volter/provenance.json` — a divergent second ledger
|
|
79
|
+
* (the exact T3.3 "two-`.volter`-dirs" bug class). The dev server's
|
|
80
|
+
* `.volter/`-scoped `/__editor/volter-file` GET/POST route pair is the recorded
|
|
81
81
|
* fix for that class; every other
|
|
82
82
|
* backend reaches the project root directly.
|
|
83
83
|
*/
|
|
@@ -88,7 +88,7 @@ async function readLedgerText(backend: StorageBackend): Promise<string | null> {
|
|
|
88
88
|
// file bytes, and that reader's whole rule is "the editor server answers
|
|
89
89
|
// JSON" — it would reject this route's successes.
|
|
90
90
|
const res = await fetch(
|
|
91
|
-
`/__editor/
|
|
91
|
+
`/__editor/volter-file?${new URLSearchParams({ path: PROVENANCE_PATH })}`,
|
|
92
92
|
);
|
|
93
93
|
if (res.status === 404) return null;
|
|
94
94
|
if (!res.ok) throw new Error(`read ${PROVENANCE_PATH}: HTTP ${res.status}`);
|
|
@@ -101,7 +101,7 @@ async function readLedgerText(backend: StorageBackend): Promise<string | null> {
|
|
|
101
101
|
async function writeLedgerText(backend: StorageBackend, content: string): Promise<void> {
|
|
102
102
|
if (isPublicRootedBackend(backend)) {
|
|
103
103
|
const write = async () => {
|
|
104
|
-
const res = await fetch('/__editor/
|
|
104
|
+
const res = await fetch('/__editor/volter-file', {
|
|
105
105
|
method: 'POST',
|
|
106
106
|
headers: { 'Content-Type': 'application/json' },
|
|
107
107
|
body: JSON.stringify({ path: PROVENANCE_PATH, content, ...sourceMutationAttribution() }),
|
package/src/bridge/call.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { dispatchBridgeMethod, hasLiveDebugPlane } from './dispatch';
|
|
|
17
17
|
* this package); everything else is `dispatchBridgeMethod`'s own structured
|
|
18
18
|
* failure, caught here and shipped back via `structuredErrorResult` so
|
|
19
19
|
* `code`/`data` survive the relay round trip byte-equivalent to the
|
|
20
|
-
* page-transport's `unwrap()` path (`packages/
|
|
20
|
+
* page-transport's `unwrap()` path (`packages/volter-live/src/game-client/client.ts`). */
|
|
21
21
|
export async function handleBridgeCall(cmd: EditorCommandMessage): Promise<EditorCommandResult> {
|
|
22
22
|
// The precondition is "no plane at all", not "not playing" — see
|
|
23
23
|
// `hasLiveDebugPlane`. The per-method preconditions inside
|
|
@@ -28,7 +28,7 @@ export async function handleBridgeCall(cmd: EditorCommandMessage): Promise<Edito
|
|
|
28
28
|
// the still edit world can be driven.
|
|
29
29
|
if (!isPlayModeActive() && !hasLiveDebugPlane()) {
|
|
30
30
|
// A LIVE INGEST that publishes no plane is a third state, and answering it
|
|
31
|
-
// with "not in play mode" sends the reader to `
|
|
31
|
+
// with "not in play mode" sends the reader to `volter-game-editor play` over a game that
|
|
32
32
|
// is already running and visible. MEASURED on the `flappy` canvas ingest:
|
|
33
33
|
// `game.waitSimTime(...)` refused with the play sentence while the mount's
|
|
34
34
|
// own play state read `playing`. The mount is what is missing a plane, so
|
|
@@ -43,7 +43,7 @@ export async function handleBridgeCall(cmd: EditorCommandMessage): Promise<Edito
|
|
|
43
43
|
'this ingest mount is live, but it publishes no debug plane — the mount installs no ' +
|
|
44
44
|
'SystemAdapters.debug, so there is nothing to enumerate, read or drive through the ' +
|
|
45
45
|
'game seam. Starting play will not change that; the game itself must declare the ' +
|
|
46
|
-
'
|
|
46
|
+
'volter game contract (or carry a contract shim beside it) for these calls to have an ' +
|
|
47
47
|
'answer.',
|
|
48
48
|
}
|
|
49
49
|
: notPlayingResult();
|