@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `
|
|
2
|
+
* The `__volterIngest` / `__volterIngest2D` / `__volterIngestReact` dev hooks: the
|
|
3
3
|
* headless-readable evidence a live ingest mount publishes for automation,
|
|
4
|
-
* screenshots and
|
|
4
|
+
* screenshots and probes.
|
|
5
5
|
*
|
|
6
6
|
* Everything here is EVIDENCE, not control: names read off the live adapter,
|
|
7
7
|
* counters read off the live gate, the coverage report re-derived on demand.
|
|
@@ -18,7 +18,7 @@ import type { IngestMount } from './authoring/ingest-root-adapter';
|
|
|
18
18
|
import { ingestCoverageReport } from './mount-coverage';
|
|
19
19
|
|
|
20
20
|
/** The window key each surface's mount publishes its evidence under. */
|
|
21
|
-
export type IngestHookKey = '
|
|
21
|
+
export type IngestHookKey = '__volterIngest' | '__volterIngest2D' | '__volterIngestReact';
|
|
22
22
|
|
|
23
23
|
const AUTHORING_PROVIDER_KEYS = [
|
|
24
24
|
'hierarchy',
|
|
@@ -57,7 +57,7 @@ export function ingestHookEvidence(adapter: AuthoringAdapter): Record<string, un
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* The loop fields the `
|
|
60
|
+
* The loop fields the `__volterIngest` dev hook publishes — the measured verdict
|
|
61
61
|
* plus the gate's own counters, so automation can drive Play/Pause/Step through
|
|
62
62
|
* exactly the seam the editor's own buttons use rather than a parallel one.
|
|
63
63
|
*/
|
|
@@ -85,10 +85,10 @@ export function defineLoopHookFields(target: Record<string, unknown>, mount: Ing
|
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* D-L1 — the LIVE value behind
|
|
88
|
-
* `
|
|
88
|
+
* `__volterIngestReact.domEvidence()`, shared by both react ingest routes.
|
|
89
89
|
* Evaluated at scrape time (a function result, never memoized),
|
|
90
90
|
* same "read the live state fresh on every call" discipline as
|
|
91
|
-
* `
|
|
91
|
+
* `__volterSiblingMounts` (D-H1).
|
|
92
92
|
*
|
|
93
93
|
* `elementCount` prefers the DOM's real `querySelectorAll('*').length` — a
|
|
94
94
|
* real browser `HTMLElement` (what `reactRoot()` actually returns in
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SAME-FRAME PIXELS for an ingested game's canvas — the reason `
|
|
2
|
+
* SAME-FRAME PIXELS for an ingested game's canvas — the reason `volter-game-editor
|
|
3
3
|
* screenshot` no longer returns black over a running ingest.
|
|
4
4
|
*
|
|
5
5
|
* ## The failure
|
|
6
6
|
*
|
|
7
7
|
* `composite-screenshot.ts` reads a game's canvas with `drawImage`, and that
|
|
8
8
|
* only works on a WebGL canvas whose context was created with
|
|
9
|
-
* `preserveDrawingBuffer: true`. Every canvas the
|
|
9
|
+
* `preserveDrawingBuffer: true`. Every canvas the volter RUNTIME mounts sets it
|
|
10
10
|
* (`@volter/editor-game/runtime/create-runtime`), which is why the composite leg has always
|
|
11
11
|
* worked for first-party play. An INGESTED game creates its own canvas: the
|
|
12
12
|
* racing-game's `<Canvas>` passes no `gl` prop, so fiber's default
|
|
@@ -12,13 +12,13 @@ import { gameContractEpoch } from '../host/coverage/game-contract-seam-evidence'
|
|
|
12
12
|
import { recordLiveSeamEvidence } from '@volter/editor-sdk/kit/live-seam-evidence';
|
|
13
13
|
import { editorConsole } from '@volter/editor-sdk/kit/editor-console';
|
|
14
14
|
import { activateLiveDocument } from '@volter/editor-sdk/kit/live-document';
|
|
15
|
-
import type {
|
|
15
|
+
import type { VolterGameContract } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
16
16
|
import { activeIngest, type IngestLifecycleControl } from './active-ingest';
|
|
17
17
|
import { readIngestGameContract } from './game-contract-realm';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* The play-state control surface for the CURRENT ingest session (F17/F21):
|
|
21
|
-
* mounts are COLD by doctrine — the adapter sets `window.
|
|
21
|
+
* mounts are COLD by doctrine — the adapter sets `window.__volterMountCold`
|
|
22
22
|
* before the game's entry executes, and a session-driven game may defer its
|
|
23
23
|
* side-effects (backend connection, narrative, audio) until the first
|
|
24
24
|
* `play()`. Everything is contract-first (game-contract.ts): `lifecycle.start`
|
|
@@ -72,9 +72,9 @@ export function setIngestPlaying(playing: boolean): void {
|
|
|
72
72
|
*/
|
|
73
73
|
export function resetIngestPlaySurface(): void {
|
|
74
74
|
setIngestPlaying(false);
|
|
75
|
-
const w = window as unknown as {
|
|
76
|
-
delete w.
|
|
77
|
-
delete w.
|
|
75
|
+
const w = window as unknown as { __volterMountCold?: boolean; __volterIngestPlayed?: boolean };
|
|
76
|
+
delete w.__volterMountCold;
|
|
77
|
+
delete w.__volterIngestPlayed;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -90,7 +90,7 @@ export interface IngestLifecycleFallback {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function runContractLifecycleEffect(
|
|
93
|
-
contract:
|
|
93
|
+
contract: VolterGameContract,
|
|
94
94
|
member: 'start' | 'pause' | 'resume',
|
|
95
95
|
run: () => void,
|
|
96
96
|
): void {
|
|
@@ -132,7 +132,7 @@ export function bindIngestLifecycle(
|
|
|
132
132
|
runContractLifecycleEffect(contract, 'start', () => declared.start?.());
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
|
-
window.dispatchEvent(new CustomEvent('
|
|
135
|
+
window.dispatchEvent(new CustomEvent('volter:ingest-play'));
|
|
136
136
|
};
|
|
137
137
|
|
|
138
138
|
const hasPause = typeof declared?.pause === 'function';
|
|
@@ -209,7 +209,7 @@ function fanOutSiblingPause(paused: boolean): void {
|
|
|
209
209
|
* either from its own entry, or from the host-added module the manifest names
|
|
210
210
|
* in `ingest.contractShim`, which the mount imports first.
|
|
211
211
|
*/
|
|
212
|
-
export function activeIngestContract():
|
|
212
|
+
export function activeIngestContract(): VolterGameContract | null {
|
|
213
213
|
return readIngestGameContract();
|
|
214
214
|
}
|
|
215
215
|
|
|
@@ -247,9 +247,9 @@ export function getIngestPlayControl(): IngestPlayControl | null {
|
|
|
247
247
|
// Late-constructed game clients must not re-defer: the flag records
|
|
248
248
|
// that play already happened this session — and makes ▶-after-⏸ a
|
|
249
249
|
// RESUME, so `lifecycle.start` fires at most once per mount.
|
|
250
|
-
const w = window as unknown as {
|
|
251
|
-
if (w.
|
|
252
|
-
w.
|
|
250
|
+
const w = window as unknown as { __volterIngestPlayed?: boolean };
|
|
251
|
+
if (w.__volterIngestPlayed) return release();
|
|
252
|
+
w.__volterIngestPlayed = true;
|
|
253
253
|
lifecycle.start();
|
|
254
254
|
// A mount ends HELD (see `ingestContentTimeForMode`), so the first ▶ is
|
|
255
255
|
// ALSO the release of that hold — same path every later ▶ takes.
|
|
@@ -72,7 +72,7 @@ import { collectRenderMemory } from '../runtime/dev/render-memory';
|
|
|
72
72
|
import { createWebGLFrameCapture } from '../runtime/dev/webgl-frame-capture';
|
|
73
73
|
import { pixiRenderingContext } from '../runtime/pixi/render-pass-bracket';
|
|
74
74
|
import type { ObservationDeclaration } from '@volter/editor-project/adapter/adapter-module';
|
|
75
|
-
import type {
|
|
75
|
+
import type { VolterGameSystems } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
76
76
|
import type { SystemAdapters } from '@volter/editor-project/adapter/system-adapter';
|
|
77
77
|
import type { RenderPassHooks } from '@volter/threejs-runtime/adapter/ingest/scene-capture';
|
|
78
78
|
import type * as THREE from 'three';
|
|
@@ -223,7 +223,7 @@ export interface IngestSystemsWiring {
|
|
|
223
223
|
* accessor-trap for three, the renderer's runners for Pixi).
|
|
224
224
|
*/
|
|
225
225
|
export function wireIngestSystems(opts: {
|
|
226
|
-
readonly contractSystems:
|
|
226
|
+
readonly contractSystems: VolterGameSystems | undefined;
|
|
227
227
|
/**
|
|
228
228
|
* THIS MOUNT'S SURFACE, stated rather than inferred from which runtime is
|
|
229
229
|
* present — the recognized-Phaser/Babylon canvas branch captures neither
|
|
@@ -271,8 +271,8 @@ export function wireIngestSystems(opts: {
|
|
|
271
271
|
'game' in opts ? opts.game : typeof window === 'undefined' ? null : ingestGameRealmWindow(),
|
|
272
272
|
);
|
|
273
273
|
const { adapter: debug, collisions } = mergeDebugAdapters([
|
|
274
|
-
{ label: "the game's own contract (window.
|
|
275
|
-
{ label: "its adapter's observation table (
|
|
274
|
+
{ label: "the game's own contract (window.volterGame.systems)", adapter: contractDebug },
|
|
275
|
+
{ label: "its adapter's observation table (volter.adapter.ts)", adapter: observationDebug },
|
|
276
276
|
]);
|
|
277
277
|
const declared = projectContractSystemAdapters(opts.contractSystems, opts.surface);
|
|
278
278
|
const renderDebug = opts.runtime
|
|
@@ -284,7 +284,7 @@ export function wireIngestSystems(opts: {
|
|
|
284
284
|
// (last writer wins), so this is the only place that may install it, and
|
|
285
285
|
// anything needing the game's render pass multiplexes here rather than
|
|
286
286
|
// opening a second seam. The second consumer is the same-frame canvas
|
|
287
|
-
// snapshot (`ingest-frame-snapshot.ts`) that makes `
|
|
287
|
+
// snapshot (`ingest-frame-snapshot.ts`) that makes `volter-game-editor screenshot` readable
|
|
288
288
|
// over a game whose canvas has no `preserveDrawingBuffer`; it costs nothing
|
|
289
289
|
// until armed, and `deliverArmedIngestFrame` no-ops while no source is set —
|
|
290
290
|
// which is the canvas lane's state, because a Pixi ingest is already
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* the exact resolve+mount `module-mode.ts`'s `mountModuleRootRuntime`
|
|
21
21
|
* proves out, reused via `mountModuleRootRuntime`, but with NONE of that
|
|
22
22
|
* route's global side effects (no `setActiveAuthoring`/`setActiveSystems`,
|
|
23
|
-
* no viewport-tab switch, no `
|
|
23
|
+
* no viewport-tab switch, no `__volterModule` hook, no play-surface writes).
|
|
24
24
|
* Its `SiblingMount.setPaused` (D-B1) is wired straight from the scoped
|
|
25
25
|
* `GameSession.pause()`/`.resume()` `mountModuleRootRuntime` hands back —
|
|
26
26
|
* the SAME honest, already-existing capability `play-mode.ts`'s own
|
|
@@ -159,7 +159,7 @@ async function mountDefaultReactSibling(
|
|
|
159
159
|
const Entry = await resolveReactAdapterRootComponent(world, realm);
|
|
160
160
|
|
|
161
161
|
const layer = document.createElement('div');
|
|
162
|
-
layer.dataset['
|
|
162
|
+
layer.dataset['volterRootSurface'] = 'true';
|
|
163
163
|
styleSiblingLayer(layer, world, 'none');
|
|
164
164
|
gameContainer.appendChild(layer);
|
|
165
165
|
|
|
@@ -241,7 +241,7 @@ async function mountDefaultReactSibling(
|
|
|
241
241
|
* runtime `module-mode.ts`'s `mountModuleRootRuntime` provides — the exact
|
|
242
242
|
* resolve+mount the primary module route performs, reused here with none
|
|
243
243
|
* of that route's global side effects: no `setActiveAuthoring`/
|
|
244
|
-
* `setActiveSystems`, no viewport-tab switch, no `
|
|
244
|
+
* `setActiveSystems`, no viewport-tab switch, no `__volterModule` hook, no
|
|
245
245
|
* mount-failure-report clear. Mounts into its OWN layer div (never straight
|
|
246
246
|
* into the shared `gameContainer` the way the primary/single-world routes
|
|
247
247
|
* do) so its internal canvas/DOM stacking never collides with the ingest
|
|
@@ -74,7 +74,7 @@ export function exitModuleMode(): void {
|
|
|
74
74
|
} catch {
|
|
75
75
|
/* ignore */
|
|
76
76
|
}
|
|
77
|
-
delete (window as unknown as Record<string, unknown>)['
|
|
77
|
+
delete (window as unknown as Record<string, unknown>)['__volterModule'];
|
|
78
78
|
editorConsole.log('Module adapter mode stopped', 'adapter');
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -152,7 +152,7 @@ export async function enterModuleModeFromManifestRoot(
|
|
|
152
152
|
// Measured from the mounted adapter rather than declared by the route.
|
|
153
153
|
const reach = measureAdapterReach(mounted);
|
|
154
154
|
|
|
155
|
-
(window as unknown as Record<string, unknown>)['
|
|
155
|
+
(window as unknown as Record<string, unknown>)['__volterModule'] = {
|
|
156
156
|
worldId: world.id,
|
|
157
157
|
adapterId,
|
|
158
158
|
reach,
|
|
@@ -148,10 +148,10 @@ function publishCanvasIngestSystems(
|
|
|
148
148
|
setActiveSystems(systems);
|
|
149
149
|
editorConsole.log(
|
|
150
150
|
contractSystems
|
|
151
|
-
? `canvas ingest: "${gameName}" declares the
|
|
151
|
+
? `canvas ingest: "${gameName}" declares the volter game contract — ` +
|
|
152
152
|
`${contractSystems.commands?.length ?? 0} commands, ` +
|
|
153
153
|
`${contractSystems.state?.length ?? 0} state providers`
|
|
154
|
-
: `canvas ingest: "${gameName}" declares no
|
|
154
|
+
: `canvas ingest: "${gameName}" declares no volter game contract — game.commands()/` +
|
|
155
155
|
'game.state() have nothing to answer and will refuse by naming that',
|
|
156
156
|
'ingest',
|
|
157
157
|
);
|
|
@@ -361,7 +361,7 @@ function publishStructuralCanvasRuntime(
|
|
|
361
361
|
'ingest',
|
|
362
362
|
);
|
|
363
363
|
|
|
364
|
-
publishIngestHook('
|
|
364
|
+
publishIngestHook('__volterIngest2D', {
|
|
365
365
|
gameId: sessionId,
|
|
366
366
|
...ingestHookEvidence(adapter),
|
|
367
367
|
runtime: recognized.runtime,
|
|
@@ -481,7 +481,7 @@ async function mountCanvasIngestRootInner(
|
|
|
481
481
|
// F26: same cold-mount announcement the three adapter makes
|
|
482
482
|
// (ingest-root-adapter.ts) — a pixi game may declare the game contract and
|
|
483
483
|
// defer its session until ▶; games that ignore the flag behave as before.
|
|
484
|
-
(window as unknown as {
|
|
484
|
+
(window as unknown as { __volterMountCold?: boolean }).__volterMountCold = true;
|
|
485
485
|
// THE namespace the game's own modules will resolve `pixi.js` to.
|
|
486
486
|
//
|
|
487
487
|
// `installSceneCapture2D` wraps `Application.prototype.render` on the
|
|
@@ -518,7 +518,7 @@ async function mountCanvasIngestRootInner(
|
|
|
518
518
|
// unfocused editor window).
|
|
519
519
|
preferStructuralRuntime: async () => {
|
|
520
520
|
const realm = ingestGameRealmWindow() as unknown as PhaserRealmLike & BabylonRealmLike;
|
|
521
|
-
await realm.
|
|
521
|
+
await realm.__volterBabylon?.ready;
|
|
522
522
|
return recognizeStructuralCanvasRuntime(store, realm) !== null;
|
|
523
523
|
},
|
|
524
524
|
});
|
|
@@ -578,8 +578,8 @@ async function mountCanvasIngestRootInner(
|
|
|
578
578
|
const stage = mount.stage as Container;
|
|
579
579
|
// The game's async setup may add display objects after its first captured frame —
|
|
580
580
|
// wait for the stage to settle. `nextPaint()` rather than a bare rAF because
|
|
581
|
-
// this mount is awaited by `
|
|
582
|
-
// `
|
|
581
|
+
// this mount is awaited by `volter-game-editor play`: in a hidden tab (every agent-worktree
|
|
582
|
+
// `volter-game-editor edit` opens one — see `after-paint.ts`) no frame ever arrives, so a
|
|
583
583
|
// bare chain hangs this bounded loop on iteration 1 and play stalls in its
|
|
584
584
|
// boot phase with nothing to report.
|
|
585
585
|
for (let i = 0; i < 90 && (stage.children?.length ?? 0) === 0; i++) {
|
|
@@ -776,7 +776,7 @@ async function mountCanvasIngestRootInner(
|
|
|
776
776
|
// PD-3: the mount stood — but `contract-ready` may only be recorded when the
|
|
777
777
|
// declared promise actually ANSWERED. The three lane's `awaitWorldReady`
|
|
778
778
|
// (`authoring/ingest-root-adapter.ts`) is the precedent: a game that declares
|
|
779
|
-
// `window.
|
|
779
|
+
// `window.volterGame.ready` is awaited exactly once, and a declared signal that
|
|
780
780
|
// REJECTS does not fail the mount (the frame is already captured) — it
|
|
781
781
|
// degrades to the measured wait, LOUDLY, and the row then reports `measured`,
|
|
782
782
|
// which is the truth about which answer stood. Recording `contract-ready`
|
|
@@ -789,7 +789,7 @@ async function mountCanvasIngestRootInner(
|
|
|
789
789
|
readinessStood = 'declared';
|
|
790
790
|
} catch (err) {
|
|
791
791
|
editorConsole.error(
|
|
792
|
-
`Ingest game "${game.id}" declared \`window.
|
|
792
|
+
`Ingest game "${game.id}" declared \`window.volterGame.ready\` and it REJECTED ` +
|
|
793
793
|
`(${String(err)}) — the frame was already captured, so the host fell back to the ` +
|
|
794
794
|
'measured settle wait. This root now reports readiness as measured.',
|
|
795
795
|
'ingest',
|
|
@@ -828,7 +828,7 @@ async function mountCanvasIngestRootInner(
|
|
|
828
828
|
'ingest',
|
|
829
829
|
);
|
|
830
830
|
|
|
831
|
-
publishIngestHook('
|
|
831
|
+
publishIngestHook('__volterIngest2D', {
|
|
832
832
|
gameId: sessionId,
|
|
833
833
|
...ingestHookEvidence(adapter),
|
|
834
834
|
drawCount: () => mount.capture.getDrawCount(),
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Everything here is a measurement or the plumbing of one — the mount-time
|
|
6
6
|
* one-shot probe (the editor capabilities reached),
|
|
7
7
|
* the live re-read of the loop verdict, the once-per-mount
|
|
8
|
-
* console door, and the report the status facet and the `
|
|
8
|
+
* console door, and the report the status facet and the `__volterIngest` hook
|
|
9
9
|
* both serve. Mount PLUMBING lives in the `mount-*-ingest-root.ts` siblings
|
|
10
10
|
* and deliberately not here: a reader asking "what did the editor actually
|
|
11
11
|
* measure about this game, and where does that number come from" should have
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
type ContractSurface,
|
|
38
38
|
projectContractSystemAdapters,
|
|
39
39
|
} from '../runtime/adapter/ingest/contract-system-adapters';
|
|
40
|
-
import type {
|
|
40
|
+
import type { VolterGameContract } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
41
41
|
import type { SystemAdapters } from '@volter/editor-project/adapter/system-adapter';
|
|
42
42
|
import { activeIngest } from './active-ingest';
|
|
43
43
|
import type { IngestMount } from './authoring/ingest-root-adapter';
|
|
@@ -163,7 +163,7 @@ export const LOOP_PROBE_ABSENT =
|
|
|
163
163
|
*/
|
|
164
164
|
export function measureSystemAdapters(
|
|
165
165
|
active: SystemAdapters,
|
|
166
|
-
contract:
|
|
166
|
+
contract: VolterGameContract | null,
|
|
167
167
|
/** The mount's surface when the caller has one — see
|
|
168
168
|
* `contract-system-adapters.ts`'s header for the one slot it decides
|
|
169
169
|
* (`physics`, whose vocabulary follows the surface). */
|
|
@@ -72,7 +72,7 @@ async function mountDomIngestRootInner(store: EditorShellStore, folderId: string
|
|
|
72
72
|
const projectRoot = ingestGameDomProjectRoot(folderId);
|
|
73
73
|
if (!projectRoot) {
|
|
74
74
|
editorConsole.error(
|
|
75
|
-
`React ingest "${folderId}": no
|
|
75
|
+
`React ingest "${folderId}": no __VOLTER_ENGINE_ROOT__ available to resolve its entry`,
|
|
76
76
|
'ingest',
|
|
77
77
|
);
|
|
78
78
|
return;
|
|
@@ -95,7 +95,7 @@ async function mountDomIngestRootInner(store: EditorShellStore, folderId: string
|
|
|
95
95
|
// F26: same cold-mount announcement as the other ingest routes — the react
|
|
96
96
|
// world's entry executes in the editor realm and may declare the game
|
|
97
97
|
// contract to defer its session until ▶. Opt-in, never demanded.
|
|
98
|
-
(window as unknown as {
|
|
98
|
+
(window as unknown as { __volterMountCold?: boolean }).__volterMountCold = true;
|
|
99
99
|
const { createGameRuntime } = await import('../runtime/create-runtime');
|
|
100
100
|
const specs = await resolveAllRoots(game.manifest, projectRoot);
|
|
101
101
|
const w = gameContainer.clientWidth;
|
|
@@ -155,7 +155,7 @@ async function mountDomIngestRootInner(store: EditorShellStore, folderId: string
|
|
|
155
155
|
const coverage = domMountCoverage(adapter);
|
|
156
156
|
recordMountCoverage(coverage);
|
|
157
157
|
|
|
158
|
-
publishIngestHook('
|
|
158
|
+
publishIngestHook('__volterIngestReact', {
|
|
159
159
|
gameId: folderId,
|
|
160
160
|
worldId: game.worldId,
|
|
161
161
|
...ingestHookEvidence(adapter),
|
|
@@ -169,7 +169,7 @@ async function mountDomIngestRootInner(store: EditorShellStore, folderId: string
|
|
|
169
169
|
domEvidence: () => reactDomEvidence(root, adapter),
|
|
170
170
|
// D-L1: honest `typeof === 'function'` check on the REAL session
|
|
171
171
|
// control this route just installed above — never an independent
|
|
172
|
-
// literal `true` (mirrors `
|
|
172
|
+
// literal `true` (mirrors `__volterSiblingMounts`'s identical discipline).
|
|
173
173
|
setPausedPresent: typeof lifecycle.setPaused === 'function',
|
|
174
174
|
});
|
|
175
175
|
}
|
|
@@ -177,7 +177,7 @@ async function mountDomIngestRootInner(store: EditorShellStore, folderId: string
|
|
|
177
177
|
/**
|
|
178
178
|
* D-Y2 (slice S2) — the manifest-route sibling of {@link mountDomIngestRoot}: an
|
|
179
179
|
* EXTERNAL-FOLDER `ingest-react` world (the opened project's OWN
|
|
180
|
-
* `
|
|
180
|
+
* `volter.project.json`, not the in-tree `ingest/games/<id>` fixture registry)
|
|
181
181
|
* enters the same react ingest session. Exactly the same roots-path recipe
|
|
182
182
|
* (one-element `resolveAllRoots` + roots-path `createGameRuntime`) and the same
|
|
183
183
|
* `DomAuthoringAdapter` D-K4 wiring — only the SOURCE of
|
|
@@ -215,7 +215,7 @@ export async function mountDomIngestRootFromManifest(
|
|
|
215
215
|
|
|
216
216
|
// F26: same cold-mount announcement the vendored route makes — an
|
|
217
217
|
// external-folder game may declare the game contract too.
|
|
218
|
-
(window as unknown as {
|
|
218
|
+
(window as unknown as { __volterMountCold?: boolean }).__volterMountCold = true;
|
|
219
219
|
const { createGameRuntime } = await import('../runtime/create-runtime');
|
|
220
220
|
const specs = await resolveAllRoots({ ...manifest, roots: [world] }, projectRoot);
|
|
221
221
|
const w = gameContainer.clientWidth;
|
|
@@ -260,7 +260,7 @@ export async function mountDomIngestRootFromManifest(
|
|
|
260
260
|
const coverage = domMountCoverage(adapter);
|
|
261
261
|
recordMountCoverage(coverage);
|
|
262
262
|
|
|
263
|
-
publishIngestHook('
|
|
263
|
+
publishIngestHook('__volterIngestReact', {
|
|
264
264
|
worldId: world.id,
|
|
265
265
|
...ingestHookEvidence(adapter),
|
|
266
266
|
noAuthoring: false,
|
|
@@ -173,7 +173,7 @@ registerEditorStateFacet(() => ({
|
|
|
173
173
|
* mount is attempted (that flag is what swaps the viewport onto the live
|
|
174
174
|
* scene). When the mount then throws, that flag, plus any authoring
|
|
175
175
|
* adapter/adopted scene a partial mount installed, used to survive — so
|
|
176
|
-
* `
|
|
176
|
+
* `volter-game-editor status` reported `playing`, the hierarchy kept presenting whatever tree
|
|
177
177
|
* was underneath, and the only trace of the failure was one console line. This
|
|
178
178
|
* is the single teardown every failure path runs: drop the partial session,
|
|
179
179
|
* reset the play surface, and record + broadcast the failure so the status item
|
|
@@ -509,7 +509,7 @@ async function tryManifestIngestRoute2D(
|
|
|
509
509
|
* DOM sibling of {@link tryManifestIngestRoute}/{@link tryManifestIngestRoute2D}
|
|
510
510
|
* (D-Y2, slice S2): a manifest-backed project declaring an `ingest-react` root
|
|
511
511
|
* mounts the native-React ingest session straight from that root (external
|
|
512
|
-
* folder — `
|
|
512
|
+
* folder — `volter-game-editor edit <folder>` — not the in-tree `ingest/games/<id>` fixture
|
|
513
513
|
* registry). Same "applied or not" contract, same D-V2 composite scan/
|
|
514
514
|
* route-selection/sibling-mount shape as its three/canvas siblings above.
|
|
515
515
|
*/
|
|
@@ -689,7 +689,7 @@ async function autoLaunchIngestInner(store: EditorShellStore): Promise<void> {
|
|
|
689
689
|
// and several of that module's suites run under the Node vitest environment
|
|
690
690
|
// where a bare top-level `window` write is a load-time ReferenceError.
|
|
691
691
|
if (import.meta.env.DEV && typeof window !== 'undefined') {
|
|
692
|
-
(window as unknown as Record<string, unknown>)['
|
|
692
|
+
(window as unknown as Record<string, unknown>)['__volterIngestApi'] = {
|
|
693
693
|
enter: (gameId: string) => {
|
|
694
694
|
if (!_boundStore) throw new Error('ingest roots not bound');
|
|
695
695
|
return mountThreeIngestRootById(_boundStore, gameId);
|
|
@@ -700,7 +700,7 @@ if (import.meta.env.DEV && typeof window !== 'undefined') {
|
|
|
700
700
|
isActive: () => activeIngest()?.kind === 'three',
|
|
701
701
|
};
|
|
702
702
|
// Canvas sibling (T6.1 slice 4) — same shape, over the canvas ingest session.
|
|
703
|
-
(window as unknown as Record<string, unknown>)['
|
|
703
|
+
(window as unknown as Record<string, unknown>)['__volterIngestApi2D'] = {
|
|
704
704
|
enter: (gameId: string) => {
|
|
705
705
|
if (!_boundStore) throw new Error('ingest roots not bound');
|
|
706
706
|
return mountCanvasIngestRootById(_boundStore, gameId);
|
|
@@ -710,7 +710,7 @@ if (import.meta.env.DEV && typeof window !== 'undefined') {
|
|
|
710
710
|
};
|
|
711
711
|
// DOM sibling (Track N, N2) — same shape, over the native-React ingest
|
|
712
712
|
// session.
|
|
713
|
-
(window as unknown as Record<string, unknown>)['
|
|
713
|
+
(window as unknown as Record<string, unknown>)['__volterIngestApiReact'] = {
|
|
714
714
|
enter: (folderId: string) => {
|
|
715
715
|
if (!_boundStore) throw new Error('ingest roots not bound');
|
|
716
716
|
return mountDomIngestRoot(_boundStore, folderId);
|
|
@@ -722,19 +722,19 @@ if (import.meta.env.DEV && typeof window !== 'undefined') {
|
|
|
722
722
|
// `packages/editor/src/authoring/mount-failure-report.ts`'s multi-entry
|
|
723
723
|
// list is otherwise only ever read by the status bar's `mount-failure`
|
|
724
724
|
// contribution (a React component), invisible to `page.evaluate()`. This is
|
|
725
|
-
// a plain function returning the LIVE list —
|
|
725
|
+
// a plain function returning the LIVE list — a probe's browser
|
|
726
726
|
// phase is this surface's first consumer, but any other dev/e2e caller can
|
|
727
|
-
// poll it the same way `
|
|
728
|
-
(window as unknown as Record<string, unknown>)['
|
|
727
|
+
// poll it the same way `__volterIngestApi*` are already polled.
|
|
728
|
+
(window as unknown as Record<string, unknown>)['__volterMountFailureReports'] = () =>
|
|
729
729
|
getMountFailureReports();
|
|
730
730
|
// D-H1 (Slice B): headless-readable per-sibling signal — the live slot's
|
|
731
731
|
// `siblings` (D-V2's composite-sibling handles, `ingest-siblings.ts`'s
|
|
732
732
|
// `SiblingMount[]`) is otherwise invisible to `page.evaluate()`, same blind spot
|
|
733
|
-
// `
|
|
733
|
+
// `__volterMountFailureReports` closed for mount failures. A plain function
|
|
734
734
|
// returning a LIVE view (never a snapshot, re-reads the slot on every call) of
|
|
735
735
|
// honest per-sibling flags only — no capture/draw proof, see
|
|
736
736
|
// `DoctorSiblingEvidence`'s doc comment (`packages/editor/src/doctor/report.ts`).
|
|
737
|
-
(window as unknown as Record<string, unknown>)['
|
|
737
|
+
(window as unknown as Record<string, unknown>)['__volterSiblingMounts'] = () =>
|
|
738
738
|
(activeIngest()?.siblings ?? []).map((s) => ({
|
|
739
739
|
worldId: s.worldId,
|
|
740
740
|
setPausedPresent: typeof s.setPaused === 'function',
|
|
@@ -80,7 +80,7 @@ function captureMechanism(via: CaptureMechanism | null): string {
|
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Publish a captured three ingest to the whole editor: resize lifecycle,
|
|
83
|
-
* hierarchy/inspector adapter, viewport adoption and the `
|
|
83
|
+
* hierarchy/inspector adapter, viewport adoption and the `__volterIngest`
|
|
84
84
|
* proof hook.
|
|
85
85
|
*/
|
|
86
86
|
function activateCapturedThreeIngest(args: {
|
|
@@ -143,7 +143,7 @@ function activateCapturedThreeIngest(args: {
|
|
|
143
143
|
// physics/input/animation/etc.) → those are correctly N/A while ingested. Only
|
|
144
144
|
// the asset-path remap (done at mount) is a first-party asset-adapter behavior.
|
|
145
145
|
// The ONE exception is earned, never inferred: a game that DECLARES verbs and
|
|
146
|
-
// state in its contract (`window.
|
|
146
|
+
// state in its contract (`window.volterGame.systems`) gets a `debug` adapter
|
|
147
147
|
// projected from those declarations by `mountIngestGame`, so `game.commands()`
|
|
148
148
|
// /`game.state()` reach it through the ordinary bridge. A game that declares
|
|
149
149
|
// nothing still lands here with `{}` and the pre-contract floor stands.
|
|
@@ -211,7 +211,7 @@ function activateCapturedThreeIngest(args: {
|
|
|
211
211
|
...(import.meta.env.DEV ? { adapter: liveAuthoring, store } : {}),
|
|
212
212
|
};
|
|
213
213
|
defineLoopHookFields(hook, mount);
|
|
214
|
-
publishIngestHook('
|
|
214
|
+
publishIngestHook('__volterIngest', hook);
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
async function mountThreeIngestRootInner(
|
|
@@ -314,7 +314,7 @@ async function mountThreeIngestRootInner(
|
|
|
314
314
|
* {@link mountThreeIngestRootFromManifest} builds the descriptor straight from
|
|
315
315
|
* an arbitrary project's manifest instead (the CLI-on-folder route, for a game
|
|
316
316
|
* folder never registered in source) — both end up here. `sessionId` is the
|
|
317
|
-
* id this session is known by — the `
|
|
317
|
+
* id this session is known by — the `__volterIngest` dev-hook's reported
|
|
318
318
|
* `gameId` and the id every log line names; it is always the world/game id
|
|
319
319
|
* (`game.id`), passed explicitly rather than re-read off `game` so the two
|
|
320
320
|
* callers' "id" concept — a fixture's registry-era id vs. a manifest's world
|
|
@@ -350,7 +350,7 @@ export async function mountThreeIngestRootById(
|
|
|
350
350
|
/**
|
|
351
351
|
* Mount straight from a manifest's `ingest-three` world — the CLI-on-folder
|
|
352
352
|
* route (T3.3 slice 3): a game folder never registered in source, whose
|
|
353
|
-
* `
|
|
353
|
+
* `volter.project.json` declares an ingest-three world. Builds the descriptor via
|
|
354
354
|
* `resolveIngestDescriptor` (ingest/resolve-three.ts — parameterized by the
|
|
355
355
|
* manifest's ingest fields, no registry lookup) and mounts it exactly like any
|
|
356
356
|
* other {@link IngestGame}. The session is known by the world id — the same
|
|
@@ -32,8 +32,8 @@ import type { IngestGame } from './types';
|
|
|
32
32
|
*
|
|
33
33
|
* - A THREE ingest's teardown disposes the R3F root, so a remount MUST
|
|
34
34
|
* re-evaluate the entry — `remountEpochs: true` gives every `load()` its
|
|
35
|
-
* own `?
|
|
36
|
-
* it is deliberately NOT `
|
|
35
|
+
* own `?volter-ingest-remount=` buster (see `INGEST_REMOUNT_QUERY` for why
|
|
36
|
+
* it is deliberately NOT `volter-mount`, whose realm-keying and subgraph
|
|
37
37
|
* stamping the ingest readers cannot follow; an ingest's `load` IS its
|
|
38
38
|
* mount). A bare URL made stop → ▶ a NO-OP: browser module identity is
|
|
39
39
|
* per-URL, the already-evaluated entry's top-level boot did not re-run,
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `public/ingest/<id>/` holds games vendored as build output rather than as
|
|
5
5
|
* source (CLAUDE.md, "Repo layout"). Those folders carry their own
|
|
6
|
-
* `
|
|
6
|
+
* `volter.project.json`, so `volter-game-editor edit public/ingest/<id>` opens them like any
|
|
7
7
|
* other project — and the manifest ingest route then built the entry's import
|
|
8
8
|
* url with `fsImportPath`, i.e. `/@fs/<abs>/…`, putting a file that lives in
|
|
9
9
|
* VITE'S OWN `public/` DIRECTORY into Vite's module graph.
|
|
10
10
|
*
|
|
11
11
|
* That is the one thing Vite refuses outright. Measured on this route
|
|
12
|
-
* (2026-08-20, `
|
|
12
|
+
* (2026-08-20, `volter-game-editor edit public/ingest/tanks`, identically for `simcity`):
|
|
13
13
|
*
|
|
14
14
|
* [vite] Internal Server Error
|
|
15
15
|
* Cannot import non-asset file /ingest/tanks/three-r170.module.js which is
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* not stop being served because the door that opened it was a session rather
|
|
28
28
|
* than a `?project=` id, so the LOCAL route resolves the same way: by URL.
|
|
29
29
|
*
|
|
30
|
-
* The `
|
|
30
|
+
* The `__VOLTER_ENGINE_ROOT__` read is the exact inverse of
|
|
31
31
|
* `imported-gallery.ts`'s `importedProjectPath` (gallery card → absolute folder);
|
|
32
32
|
* this is absolute folder → served URL.
|
|
33
33
|
*/
|
|
@@ -36,7 +36,7 @@ import { importServedModule } from '../host/browser-transpile';
|
|
|
36
36
|
import { ensureServedBundleRuntimeModules } from '../host/served-bundle-runtime-modules';
|
|
37
37
|
import { bootDeclaredDocument, declaresClassicEntry, readDeclaredBoot } from './served-html-boot';
|
|
38
38
|
|
|
39
|
-
declare const
|
|
39
|
+
declare const __VOLTER_ENGINE_ROOT__: string;
|
|
40
40
|
|
|
41
41
|
function withoutTrailingSlash(path: string): string {
|
|
42
42
|
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
@@ -52,8 +52,8 @@ function withoutTrailingSlash(path: string): string {
|
|
|
52
52
|
* checkout, not about the game.
|
|
53
53
|
*/
|
|
54
54
|
export function servedProjectBaseUrl(projectRoot: string): string | undefined {
|
|
55
|
-
if (typeof
|
|
56
|
-
const publicRoot = `${withoutTrailingSlash(
|
|
55
|
+
if (typeof __VOLTER_ENGINE_ROOT__ !== 'string' || !__VOLTER_ENGINE_ROOT__) return undefined;
|
|
56
|
+
const publicRoot = `${withoutTrailingSlash(__VOLTER_ENGINE_ROOT__)}/public/`;
|
|
57
57
|
const dir = `${withoutTrailingSlash(projectRoot)}/`;
|
|
58
58
|
if (!dir.startsWith(publicRoot) || dir === publicRoot) return undefined;
|
|
59
59
|
return `/${dir.slice(publicRoot.length)}`;
|
|
@@ -166,10 +166,10 @@ export function declaresClassicEntry(boot: DeclaredBoot | null, entryUrl: string
|
|
|
166
166
|
*/
|
|
167
167
|
export async function stageDeclaredDom(boot: DeclaredBoot): Promise<void> {
|
|
168
168
|
const realmDocument = ingestGameRealmWindow().document;
|
|
169
|
-
realmDocument.querySelector('[data-
|
|
169
|
+
realmDocument.querySelector('[data-volter-declared-dom]')?.remove();
|
|
170
170
|
if (boot.bodyHtml.trim().length === 0 && boot.inlineCss.length === 0) return;
|
|
171
171
|
const holder = realmDocument.createElement('div');
|
|
172
|
-
holder.setAttribute('data-
|
|
172
|
+
holder.setAttribute('data-volter-declared-dom', '');
|
|
173
173
|
// `pointer-events:none` on the WRAPPER, restored per child: the holder is a
|
|
174
174
|
// host artifact, not part of the game's page, and it blanketed the game's
|
|
175
175
|
// canvas — a Babylon game whose whole UI is in-canvas (space-truckers' GUI
|
|
@@ -212,7 +212,7 @@ export async function stageDeclaredDom(boot: DeclaredBoot): Promise<void> {
|
|
|
212
212
|
for (const staged of holder.querySelectorAll('[id]')) {
|
|
213
213
|
if (surface.id === staged.id) surface.removeAttribute('id');
|
|
214
214
|
for (const stub of surface.querySelectorAll(
|
|
215
|
-
`[data-
|
|
215
|
+
`[data-volter-dom-stub][id="${CSS.escape(staged.id)}"]`,
|
|
216
216
|
)) {
|
|
217
217
|
if (stub !== staged) stub.remove();
|
|
218
218
|
}
|
|
@@ -278,15 +278,15 @@ export async function bootDeclaredDocument(
|
|
|
278
278
|
// violation ("trap returned falsish for property 'window'"), so the traps
|
|
279
279
|
// delegate to the captured realm window instead of proxying it.
|
|
280
280
|
const scope =
|
|
281
|
-
'var
|
|
282
|
-
'
|
|
283
|
-
'var
|
|
281
|
+
'var __volterPristine = new Set(Object.getOwnPropertyNames(globalThis));\n' +
|
|
282
|
+
'__volterPristine.add("__volterPristine"); __volterPristine.add("__volterRealmGlobals");\n' +
|
|
283
|
+
'var __volterRealmGlobals = (function (w) {\n' +
|
|
284
284
|
' return new Proxy({}, {\n' +
|
|
285
|
-
' has: function (_, k) { return typeof k === "string" && !
|
|
285
|
+
' has: function (_, k) { return typeof k === "string" && !__volterPristine.has(k) && k in w; },\n' +
|
|
286
286
|
' get: function (_, k) { return typeof k === "string" ? w[k] : undefined; },\n' +
|
|
287
287
|
' set: function (_, k, v) { w[k] = v; return true; },\n' +
|
|
288
288
|
' });\n' +
|
|
289
289
|
'})(window);\n';
|
|
290
|
-
const body = `${GAME_GLOBALS_PRELUDE}\n${scope}\nreturn (function(){ with (
|
|
290
|
+
const body = `${GAME_GLOBALS_PRELUDE}\n${scope}\nreturn (function(){ with (__volterRealmGlobals) {\n${sources.join('\n;\n')}\n} }).call(window);`;
|
|
291
291
|
gameRealmPage().withCurrentScript(entryUrl, () => new Function(body)());
|
|
292
292
|
}
|