@volter/editor-game 0.5.68 → 0.5.70
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 +2 -2
- 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 +11 -11
- 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 +4 -4
- 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 +3 -3
- 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 +11 -11
- 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 +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 +1 -1
- package/src/runtime/mount-manifest.ts +10 -10
- 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
|
@@ -31,8 +31,8 @@ import type { Game } from './game';
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* The minimal structural shape `ctx.debug.attachRoom` needs from a joined
|
|
34
|
-
* Colyseus room: just enough to send the reserved `
|
|
35
|
-
* message and listen for its `
|
|
34
|
+
* Colyseus room: just enough to send the reserved `__volter:debugCommand`
|
|
35
|
+
* message and listen for its `__volter:debugCommandResult` reply. Structural,
|
|
36
36
|
* not a Colyseus type import — the same seam-boundary style every other
|
|
37
37
|
* `DebugCtxSurface` member uses (no wrapper around Colyseus itself, D2).
|
|
38
38
|
* `onMessage`'s return type is `unknown` because Colyseus's own return shape
|
|
@@ -99,15 +99,15 @@ export interface DebugCtxSurface {
|
|
|
99
99
|
/**
|
|
100
100
|
* Task 2.2 — server-locus command routing (client leg). Call once your
|
|
101
101
|
* game code has joined its Colyseus room, so `locus: 'server'` commands
|
|
102
|
-
* have somewhere to send `
|
|
102
|
+
* have somewhere to send `__volter:debugCommand`. Returns a detach function
|
|
103
103
|
* (call on room leave/dispose) — game-scoped, last-attached room wins.
|
|
104
104
|
*/
|
|
105
105
|
attachRoom(room: DebugRoomHandle): () => void;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/** Engine-local error for the debug seam — mirrors the `code` + `data` shape
|
|
109
|
-
* `packages/
|
|
110
|
-
* depend on
|
|
109
|
+
* `packages/volter-sdk/src/errors.ts` uses (no import: the engine does not
|
|
110
|
+
* depend on the SDK). `code` is always machine-readable; nothing reading
|
|
111
111
|
* this error may key off `message` prose. */
|
|
112
112
|
export class DebugError extends Error {
|
|
113
113
|
readonly code: string;
|
|
@@ -129,7 +129,7 @@ const REGISTRATION_HINT =
|
|
|
129
129
|
* (`POST /__editor/command`) and `page.evaluate` both serialize the error's
|
|
130
130
|
* `data` bag — and an `Error` stringifies to `{}` there, so a command's own
|
|
131
131
|
* refusal text ("no such waypoint") vanished and the developer at
|
|
132
|
-
* `
|
|
132
|
+
* `volter-game-editor eval` saw only `debug: command "x" threw`. The text has to travel as
|
|
133
133
|
* plain strings, and in the `message` above all: that is the one field every
|
|
134
134
|
* client (`SessionError`, the CLI's own error print) actually surfaces. */
|
|
135
135
|
function describeCause(cause: unknown): {
|
|
@@ -145,12 +145,12 @@ function describeCause(cause: unknown): {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/** Task 2.2 — how long `invoke()` waits for a `locus: 'server'` command's
|
|
148
|
-
* `
|
|
148
|
+
* `__volter:debugCommandResult` reply before failing loudly. */
|
|
149
149
|
const SERVER_COMMAND_TIMEOUT_MS = 10_000;
|
|
150
150
|
|
|
151
151
|
/** The virtual-input surface the debug bridge (`runtime/debug-bridge.ts`)
|
|
152
152
|
* actuates through: a root's input door — its entry's `debug.input`
|
|
153
|
-
* (`adapter/native-debug-module.ts`) or a `
|
|
153
|
+
* (`adapter/native-debug-module.ts`) or a `volter.adapter.ts` input binding
|
|
154
154
|
* (`host/adapter-runtime-bindings.ts`), wired once per root when its bindings
|
|
155
155
|
* install, at the same spot as `setInputActionsSource` — absent until then. */
|
|
156
156
|
export interface DebugVirtualInputTarget {
|
|
@@ -200,7 +200,7 @@ export interface InputTraceSnapshot {
|
|
|
200
200
|
* (`runtime/game.ts`, D15/T-D15.3-.4) for full semantics. Named here (not
|
|
201
201
|
* re-declared per-caller) so the bridge (`runtime/debug-bridge.ts`), the
|
|
202
202
|
* editor relay (`command-listener.ts`'s `run-ticks` case), and
|
|
203
|
-
* `play.runTicks` (
|
|
203
|
+
* `play.runTicks` (the SDK) all reference the SAME type. */
|
|
204
204
|
export interface RunTicksOptions {
|
|
205
205
|
/** `'last'` (default) — skip `preRender`/`render` for every tick except
|
|
206
206
|
* the final one. `'all'` — render every tick. `'none'` — never render,
|
|
@@ -295,7 +295,7 @@ export interface DebugRegistry {
|
|
|
295
295
|
* Every world's target is kept, keyed by `worldId`, and
|
|
296
296
|
* {@link getVirtualInputTarget} resolves ONE of them via
|
|
297
297
|
* {@link resolveInputRootId} — the SAME resolution the debug bridge
|
|
298
|
-
* (`window.
|
|
298
|
+
* (`window.__volter.input.*`) and the editor relay both call through, so they
|
|
299
299
|
* can never disagree about which root an unaddressed actuation reaches. An
|
|
300
300
|
* explicit `worldId` reaches that world specifically. */
|
|
301
301
|
setVirtualInputTarget(worldId: string, target: DebugVirtualInputTarget): void;
|
|
@@ -318,7 +318,7 @@ export interface DebugRegistry {
|
|
|
318
318
|
* actuation target — called ONCE by `createGame`, immediately (unlike
|
|
319
319
|
* {@link setVirtualInputTarget}, which waits for a per-world mount, a
|
|
320
320
|
* `Game`'s own `runTicks` exists the instant the Game shell does). The
|
|
321
|
-
* SAME target backs `runtime/debug-bridge.ts`'s `window.
|
|
321
|
+
* SAME target backs `runtime/debug-bridge.ts`'s `window.__volter.runTicks`
|
|
322
322
|
* (door a) and the editor relay's `run-ticks` case → `play.runTicks`
|
|
323
323
|
* (door b) — one implementation, byte-identical semantics across doors
|
|
324
324
|
* (D17).
|
|
@@ -605,8 +605,8 @@ export function createDebugRegistry(opts: {
|
|
|
605
605
|
/**
|
|
606
606
|
* Task 2.2 — server-locus command routing (client leg). A `locus: 'server'`
|
|
607
607
|
* command never calls its own registered `fn` locally: `invoke()` instead
|
|
608
|
-
* sends the reserved room message `
|
|
609
|
-
* matching `
|
|
608
|
+
* sends the reserved room message `__volter:debugCommand` and resolves on the
|
|
609
|
+
* matching `__volter:debugCommandResult` reply, so a fixture mutates the
|
|
610
610
|
* AUTHORITATIVE (server) copy of state, not client prediction. Request ids
|
|
611
611
|
* are a monotonic per-registry counter (`dbg-<n>`), not `Math.random`/
|
|
612
612
|
* `Date.now`, so two in-flight commands never collide and correlation is
|
|
@@ -635,14 +635,14 @@ export function createDebugRegistry(opts: {
|
|
|
635
635
|
);
|
|
636
636
|
}, SERVER_COMMAND_TIMEOUT_MS);
|
|
637
637
|
pendingServerCommands.set(requestId, { resolve, reject, timer });
|
|
638
|
-
room.send('
|
|
638
|
+
room.send('__volter:debugCommand', { name, args, requestId });
|
|
639
639
|
});
|
|
640
640
|
}
|
|
641
641
|
|
|
642
642
|
// Defect 8 fix — the live detach() for whatever room is CURRENTLY attached
|
|
643
643
|
// (or null if none). `attachRoom` calls this itself before attaching a new
|
|
644
644
|
// room: without it, a second `attachRoom` call (no explicit `detach()` in
|
|
645
|
-
// between) left the first room's `
|
|
645
|
+
// between) left the first room's `__volter:debugCommandResult` subscription
|
|
646
646
|
// live forever (a leak — the old room keeps getting messages dispatched to
|
|
647
647
|
// a handler nothing reads anymore) and stranded any of ITS in-flight
|
|
648
648
|
// commands riding the full 10s timeout with no way to ever be answered.
|
|
@@ -652,7 +652,7 @@ export function createDebugRegistry(opts: {
|
|
|
652
652
|
* surface (game-scoped, last-attached room wins — and, since Defect 8,
|
|
653
653
|
* actually CLEANS UP the previous attachment rather than merely
|
|
654
654
|
* overwriting the pointer). Subscribes to the reserved
|
|
655
|
-
* `
|
|
655
|
+
* `__volter:debugCommandResult` reply and resolves/rejects the matching
|
|
656
656
|
* in-flight {@link invokeServerCommand} promise by `requestId`. */
|
|
657
657
|
function attachRoom(room: DebugRoomHandle): () => void {
|
|
658
658
|
// Last-wins, but with cleanup: detach whatever room was attached before
|
|
@@ -661,7 +661,7 @@ export function createDebugRegistry(opts: {
|
|
|
661
661
|
detachCurrentRoom?.();
|
|
662
662
|
|
|
663
663
|
attachedRoom = room;
|
|
664
|
-
const unsubscribe = room.onMessage('
|
|
664
|
+
const unsubscribe = room.onMessage('__volter:debugCommandResult', (message) => {
|
|
665
665
|
const reply = message as
|
|
666
666
|
| { requestId?: string; ok?: boolean; result?: unknown; error?: unknown }
|
|
667
667
|
| undefined;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* game's own camera writes never fight it because the game keeps rendering
|
|
22
22
|
* through its own canvas untouched. Per the non-redundancy rule that governs
|
|
23
23
|
* this list, a control the editor's own chrome furnishes gets no instrument;
|
|
24
|
-
* agents reach that camera through the editor half of `
|
|
24
|
+
* agents reach that camera through the editor half of `volter-game-editor eval`, not the
|
|
25
25
|
* game registry.
|
|
26
26
|
*
|
|
27
27
|
* ── THE DOOR ────────────────────────────────────────────────────────────────
|
|
@@ -266,7 +266,7 @@ export function createPerformanceProfiler(initiallyEnabled = false) {
|
|
|
266
266
|
startMs: existing?.startMs ?? phaseStart - frameStart,
|
|
267
267
|
});
|
|
268
268
|
if (recording)
|
|
269
|
-
performance.measure(`
|
|
269
|
+
performance.measure(`volter.phase.${name}`, { start: phaseStart, duration: elapsed });
|
|
270
270
|
},
|
|
271
271
|
systemObserver: {
|
|
272
272
|
beginSystem() {
|
|
@@ -344,7 +344,7 @@ export function createPerformanceProfiler(initiallyEnabled = false) {
|
|
|
344
344
|
frames.push(frame);
|
|
345
345
|
if (frames.length > 600) frames.splice(0, frames.length - 600);
|
|
346
346
|
if (recording)
|
|
347
|
-
performance.measure('
|
|
347
|
+
performance.measure('volter.frame', { start: frameStart, duration: frame.cpuMs });
|
|
348
348
|
publish();
|
|
349
349
|
},
|
|
350
350
|
subscribe(listener: () => void) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* game's own debug registry (`runtime/debug-registry.ts`), so a running game
|
|
4
4
|
* explains its own frame cost through the SAME seam every other reading and
|
|
5
5
|
* verb uses — `game.state('render.vitals')` / `game.command('render.census')`
|
|
6
|
-
* through `
|
|
6
|
+
* through `volter-game-editor eval`, and `game.providers()`/`game.commands()` to discover
|
|
7
7
|
* them without being told they exist.
|
|
8
8
|
*
|
|
9
9
|
* Engine-owned and first-party, seeded by BOTH three-root mount paths — a game
|
|
@@ -175,7 +175,7 @@ export function registerRenderVitals(
|
|
|
175
175
|
'Meshes grouped by shared (geometry, material) identity, largest group first — the ' +
|
|
176
176
|
'instancing shortlist for a draw-call diet. `structural` is the same scan keyed by ' +
|
|
177
177
|
'VALUE instead of object identity: what would collapse under a <Frozen> wrapper ' +
|
|
178
|
-
'(
|
|
178
|
+
'(volter-game-editor add static-batch) even when every mesh carries its own inline material.',
|
|
179
179
|
args: z.tuple([]),
|
|
180
180
|
locus: 'client',
|
|
181
181
|
},
|
|
@@ -138,7 +138,7 @@ export function decideStaticBatchAdvisory(
|
|
|
138
138
|
`are repeats of ${grouped(structural.familyCount)} structural families, ` +
|
|
139
139
|
`${where}. If that scenery is mount-static — nothing under it moves, re-colours or ` +
|
|
140
140
|
`unmounts after mount — one wrapper collapses it to a few draws: wrap it in ` +
|
|
141
|
-
`${fix}…</Frozen> (
|
|
141
|
+
`${fix}…</Frozen> (volter-game-editor add static-batch). Reactive scenery goes outside the wrapper, ` +
|
|
142
142
|
`and a subtree that must stay unbatched declares it: userData={{ staticBatch: false }}.`;
|
|
143
143
|
|
|
144
144
|
return { drawCalls, collapsible, subtree, fix, message };
|
|
@@ -150,14 +150,14 @@ export function decideStaticBatchAdvisory(
|
|
|
150
150
|
* along with everything else on the console. Same mechanism, and the same
|
|
151
151
|
* reason, as dev-tools' unconfigured-section warning.
|
|
152
152
|
*/
|
|
153
|
-
const WARNED_KEY = '
|
|
153
|
+
const WARNED_KEY = '__volterStaticBatchAdvised';
|
|
154
154
|
|
|
155
155
|
function alreadyWarned(): boolean {
|
|
156
156
|
return (globalThis as unknown as Record<string, boolean | undefined>)[WARNED_KEY] === true;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
/**
|
|
160
|
-
* The console IS this advisory's channel: `
|
|
160
|
+
* The console IS this advisory's channel: `volter-game-editor status` reports console
|
|
161
161
|
* warnings, which is where a building agent already looks. An in-editor
|
|
162
162
|
* banner would be one nobody opens, and a provider would be one nobody reads
|
|
163
163
|
* without already knowing to ask.
|
package/src/runtime/dev-build.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* case: it is a dev session by definition even when the editor SPA it
|
|
25
25
|
* runs inside is a production build.
|
|
26
26
|
* 2. A dev build — `import.meta.env.DEV`. The ordinary local/editor case.
|
|
27
|
-
* 3. A production build's EXPLICIT opt-in — `
|
|
27
|
+
* 3. A production build's EXPLICIT opt-in — `VITE_VOLTER_DEV_BUILD=true`.
|
|
28
28
|
* Instrumenting a production bundle is a real, legitimate choice (an
|
|
29
29
|
* internal playtest build, a QA build), and it must be an opt-in someone
|
|
30
30
|
* had to type, never something a default drifts into.
|
|
@@ -40,8 +40,8 @@ export function devBuildEnabled(override?: boolean | undefined): boolean {
|
|
|
40
40
|
// of the dev answer importable from ANY program rather than forcing every
|
|
41
41
|
// downstream tsconfig to adopt Vite's ambient types.
|
|
42
42
|
const env = (import.meta as unknown as { env?: unknown }).env as
|
|
43
|
-
| { DEV?: boolean | undefined;
|
|
43
|
+
| { DEV?: boolean | undefined; VITE_VOLTER_DEV_BUILD?: string | undefined }
|
|
44
44
|
| undefined;
|
|
45
45
|
if (env?.DEV === true) return true;
|
|
46
|
-
return env?.
|
|
46
|
+
return env?.VITE_VOLTER_DEV_BUILD === 'true';
|
|
47
47
|
}
|
package/src/runtime/game.ts
CHANGED
|
@@ -744,9 +744,9 @@ export interface GameInternal extends Game {
|
|
|
744
744
|
* call the SAME per-tick pipeline `runFrame` uses, `n` times in a tight
|
|
745
745
|
* loop, with `dt` fixed to the host loop's own fixed timestep
|
|
746
746
|
* (`this.loop.fixedDt` — `core/game-loop.ts`). This generalizes
|
|
747
|
-
* `render-control.ts`'s proven `
|
|
748
|
-
* the capture-only door (`?
|
|
749
|
-
* door can reach (the bridge's `window.
|
|
747
|
+
* `render-control.ts`'s proven `VolterRenderHarness. simulateSubsteps` from
|
|
748
|
+
* the capture-only door (`?volter-render=1`) to a Game-level primitive every
|
|
749
|
+
* door can reach (the bridge's `window.__volter.runTicks`, the editor relay's
|
|
750
750
|
* `run-ticks` case → `play.runTicks`) — `simulateSubsteps` itself is
|
|
751
751
|
* UNTOUCHED by this addition (it may later delegate to this method; not
|
|
752
752
|
* this unit's job).
|
|
@@ -816,7 +816,7 @@ export function createGame(opts: {
|
|
|
816
816
|
* on its own terms, whether or not the project's manifest DECLARES that
|
|
817
817
|
* reproducibility as a contract). The manifest-aware boot path
|
|
818
818
|
* (`mount-manifest.ts`'s `mountManifestRoots`) is what actually resolves
|
|
819
|
-
* `manifest.determinism.defaultSeed`/`?
|
|
819
|
+
* `manifest.determinism.defaultSeed`/`?volter-seed=`/explicit config and
|
|
820
820
|
* passes the result here, BEFORE any world's `mount()`/`setup()` runs —
|
|
821
821
|
* `createGameRuntime` always constructs the Game (this call) first (see
|
|
822
822
|
* this function's own doc comment below). */
|
|
@@ -1618,7 +1618,7 @@ export function createGame(opts: {
|
|
|
1618
1618
|
// D15/T-D15.4: wire the run-ticks target the instant the Game shell exists
|
|
1619
1619
|
// (unlike `setVirtualInputTarget`, which waits for a per-world mount, a
|
|
1620
1620
|
// Game's own `runTicks` needs nothing else) — this is what makes
|
|
1621
|
-
// `window.
|
|
1621
|
+
// `window.__volter.runTicks` (`debug-bridge.ts`) and the editor relay's
|
|
1622
1622
|
// `run-ticks` case reach the SAME implementation `game.runTicks` above is.
|
|
1623
1623
|
debugRegistry.setRunTicksTarget({ runTicks: gameInternal.runTicks });
|
|
1624
1624
|
// D15 (T-D15.1/.3) — same "file after the shell exists" ordering as the
|
|
@@ -160,7 +160,7 @@ export function createInputRouter(
|
|
|
160
160
|
if (claimant && claimant !== bottomId) {
|
|
161
161
|
const entry = byId.get(claimant);
|
|
162
162
|
if (entry) {
|
|
163
|
-
// The claim is exclusive: the world beneath never hears a press
|
|
163
|
+
// The claim is exclusive: the world beneath never hears a press the layer above took.
|
|
164
164
|
evt.stopPropagation();
|
|
165
165
|
forwardEvent(entry.canvas, evt);
|
|
166
166
|
}
|
|
@@ -130,7 +130,7 @@ export interface MountManifestOptions {
|
|
|
130
130
|
* never a real host. See `RootsRuntimeConfig.headless`. */
|
|
131
131
|
readonly headless?: boolean | undefined;
|
|
132
132
|
/**
|
|
133
|
-
* Task 2.1: overrides for the `?
|
|
133
|
+
* Task 2.1: overrides for the `?volter-debug=1` bridge `mountManifestRoots`
|
|
134
134
|
* installs at the tail of every mount — the engine-owned install point so
|
|
135
135
|
* every standalone project gets it with zero template edits. Omit both in
|
|
136
136
|
* a real host (defaults to `window.location`/ `window`); a headless test
|
|
@@ -145,7 +145,7 @@ export interface MountManifestOptions {
|
|
|
145
145
|
| undefined;
|
|
146
146
|
/**
|
|
147
147
|
* D15 (T-D15.1) — the "explicit config" leg of the boot-time seed
|
|
148
|
-
* precedence (`manifest.determinism.defaultSeed` → `?
|
|
148
|
+
* precedence (`manifest.determinism.defaultSeed` → `?volter-seed=` → this),
|
|
149
149
|
* highest-precedence, for a caller that already knows the exact seed it
|
|
150
150
|
* wants (a future CLI `--seed`/probe fixture `seed` option, T-D15.6).
|
|
151
151
|
* Ignored entirely unless the manifest declares
|
|
@@ -154,7 +154,7 @@ export interface MountManifestOptions {
|
|
|
154
154
|
readonly seed?: number | undefined;
|
|
155
155
|
/** Host identity for a private play run or coordinated Team Test. */
|
|
156
156
|
readonly playtest?: PlaytestContext | null | undefined;
|
|
157
|
-
/** Where to read `?
|
|
157
|
+
/** Where to read `?volter-seed=` from for the boot-time seed reader. Same
|
|
158
158
|
* override precedent as `debugBridge.url` (defaults to `window.location`
|
|
159
159
|
* when a real `window` exists; a headless caller with no override gets no
|
|
160
160
|
* query-param seed, same as `maybeInstallDebugBridge`'s own url default). */
|
|
@@ -166,13 +166,13 @@ export interface MountManifestOptions {
|
|
|
166
166
|
* `mountManifestRoots` threads into `createGameRuntime` (and therefore
|
|
167
167
|
* `createGame`, BEFORE any world's `mount()`/`setup()` runs). Returns
|
|
168
168
|
* `undefined` when the manifest doesn't declare `determinism.seededRandom`
|
|
169
|
-
* at all — `?
|
|
169
|
+
* at all — `?volter-seed=` and `defaultSeed` are both ignored in that case
|
|
170
170
|
* (§2.a: "the mount path seeds ctx.random ... iff declared"; an undeclared
|
|
171
171
|
* project's `ctx.random` still exists, just boots from `createGame`'s own
|
|
172
172
|
* fixed default, unaffected by the manifest or the URL).
|
|
173
173
|
*
|
|
174
174
|
* Precedence when declared (highest wins): `explicitSeed` (a caller-supplied
|
|
175
|
-
* config value) → `?
|
|
175
|
+
* config value) → `?volter-seed=<int>` on `url` → `manifest.determinism
|
|
176
176
|
* .defaultSeed`.
|
|
177
177
|
*/
|
|
178
178
|
export function resolveDeterminismSeed(opts: {
|
|
@@ -250,7 +250,7 @@ export function resolveManifest(raw: unknown): ResolvedGameManifest {
|
|
|
250
250
|
|
|
251
251
|
// ---------------------------------------------------------------------------
|
|
252
252
|
// Per-kind entry -> adapter resolution (mirrors `resolveRootBinding`'s
|
|
253
|
-
// per-identity dispatch, `packages/editor/src/binding-resolver.ts`)
|
|
253
|
+
// per-identity dispatch, `packages/editor-game/src/host/binding-resolver.ts`)
|
|
254
254
|
// ---------------------------------------------------------------------------
|
|
255
255
|
|
|
256
256
|
function resolveThreeAdapter(
|
|
@@ -341,7 +341,7 @@ function buildRootMountSpec(
|
|
|
341
341
|
zOrder: world.zOrder,
|
|
342
342
|
pausable: world.pausable,
|
|
343
343
|
// `root` is this file's own resolved manifest record; the rest — the
|
|
344
|
-
// parsed `
|
|
344
|
+
// parsed `volter.adapter.ts`, the entry namespace, the entry's declared
|
|
345
345
|
// debug/systems bindings — only the caller who loaded the entry can know.
|
|
346
346
|
...(entry?.declaration === undefined
|
|
347
347
|
? {}
|
|
@@ -376,7 +376,7 @@ function buildRootMountSpec(
|
|
|
376
376
|
// ---------------------------------------------------------------------------
|
|
377
377
|
|
|
378
378
|
/**
|
|
379
|
-
* Mount every world declared by a `
|
|
379
|
+
* Mount every world declared by a `volter.project.json` manifest onto `container`,
|
|
380
380
|
* standalone — no editor, no dev server. The generalization of
|
|
381
381
|
* `examples/tri-world/src/main.ts`'s hand-built `RootMountSpec[]` (D-Z2). Reuses
|
|
382
382
|
* the pure `loadGameManifest` + `createGameRuntime({ roots })` and mirrors
|
|
@@ -441,7 +441,7 @@ export async function mountManifestRoots(opts: MountManifestOptions): Promise<Ga
|
|
|
441
441
|
getGameplayRngTrapControl(session.game)?.setEnabled(true);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
// Task 2.1 — install the `?
|
|
444
|
+
// Task 2.1 — install the `?volter-debug=1` bridge at the TAIL of every
|
|
445
445
|
// manifest mount (engine-owned, so a standalone project gets it with zero
|
|
446
446
|
// template edits). `getDebugRegistry` returns `null` only for a bare
|
|
447
447
|
// `Game`-shaped test stand-in predating T7.1 — real mounts always have one.
|
|
@@ -458,7 +458,7 @@ export async function mountManifestRoots(opts: MountManifestOptions): Promise<Ga
|
|
|
458
458
|
debugRegistry.setRoomDeclared(true);
|
|
459
459
|
}
|
|
460
460
|
// Defect 5 fix: `maybeInstallDebugBridge` adds `window` listeners and
|
|
461
|
-
// publishes `window.
|
|
461
|
+
// publishes `window.__volter` with no way to undo either — wire its
|
|
462
462
|
// `uninstall()` into THIS session's own `stop()` so a caller that tears
|
|
463
463
|
// this mount down doesn't leave the bridge (and its listeners) live.
|
|
464
464
|
const bridgeHandle = maybeInstallDebugBridge({
|
|
@@ -188,7 +188,7 @@ function viewOwnerLabel(view: Container): string | undefined {
|
|
|
188
188
|
* on a 20 000-node `@pixi/react` world blocked the main thread for 199.5
|
|
189
189
|
* SECONDS in one synchronous run, 69% of all profiler samples inside this
|
|
190
190
|
* search, because `parentRenderGroup` reaches an array of every node in the
|
|
191
|
-
* tree and the search walked it once per unlabeled node. `
|
|
191
|
+
* tree and the search walked it once per unlabeled node. `volter-game-editor play`,
|
|
192
192
|
* `screenshot` and `stop` all timed out against a tab that was heartbeating
|
|
193
193
|
* normally the whole time.
|
|
194
194
|
*/
|
|
@@ -62,7 +62,7 @@ export interface IngestMount2D {
|
|
|
62
62
|
* `onWait` is the three lane's own seam (`ingest-root-adapter.ts` passes
|
|
63
63
|
* `setCaptureWait`): the capture window PARKS while the tab is hidden, and a
|
|
64
64
|
* parked wait is otherwise indistinguishable from a hung mount at every door.
|
|
65
|
-
* Threading it here is what lets `
|
|
65
|
+
* Threading it here is what lets `volter-game-editor status` say "waiting for the first
|
|
66
66
|
* visible frame — the tab is hidden" over a canvas ingest too.
|
|
67
67
|
*/
|
|
68
68
|
export async function mountIngestGame2D(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* runtime/presentation.ts — how a game with a DECLARED resolution is presented in a host panel.
|
|
3
3
|
*
|
|
4
|
-
* `
|
|
4
|
+
* `volter.project.json`'s `resolution` is the game's own LOGICAL size: the coordinate space its
|
|
5
5
|
* cameras frame, its DOM overlay lays out in, and its 2D worlds author positions against. A host
|
|
6
6
|
* panel is whatever size the user dragged it to, and the two are not the same number.
|
|
7
7
|
*
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Also does NOT install anything by default — a caller must explicitly call
|
|
19
19
|
* {@link installRenderControlHarness}, and even then it only actually
|
|
20
|
-
* publishes `window.
|
|
21
|
-
* `?
|
|
20
|
+
* publishes `window.__volterRender` when the page's URL opts in via
|
|
21
|
+
* `?volter-render=1` (AC 4: unavailable/protected in normal production
|
|
22
22
|
* gameplay unless explicitly enabled). See `render-seed.ts` for the
|
|
23
23
|
* matching `Math.random` determinism hardening, which must run BEFORE this
|
|
24
24
|
* module (or any other game module) is even imported.
|
|
25
25
|
*
|
|
26
26
|
* I5 (`--simulate` render, §15 I5) is implemented HERE, alongside `renderOnce`:
|
|
27
|
-
* {@link
|
|
27
|
+
* {@link VolterRenderHarness.simulateSubsteps} drives `game.runFrame(fixedDt)` a
|
|
28
28
|
* fixed integer number of times — the FULL fixed-step gameplay frame (every
|
|
29
29
|
* phase in `PHASE_ORDER`, including `physics`/`gameLogic`/`animation`), unlike
|
|
30
30
|
* `renderOnce`'s deliberately gameplay-free `preRender`+`render`-only pass.
|
|
@@ -109,11 +109,11 @@ export interface SeekableClock {
|
|
|
109
109
|
*/
|
|
110
110
|
export type FrameAdvancer = (dt: number) => void;
|
|
111
111
|
|
|
112
|
-
/** The `window.
|
|
112
|
+
/** The `window.__volterRender` surface (I2 AC, extended by I8's `advanceFrame`).
|
|
113
113
|
* Every method here is synchronous except the two that genuinely need to
|
|
114
114
|
* wait on the browser (composited render completion, async readiness
|
|
115
115
|
* probes). */
|
|
116
|
-
export interface
|
|
116
|
+
export interface VolterRenderHarness {
|
|
117
117
|
/** Evaluate every registered sequence/cinematic-participating evaluator at
|
|
118
118
|
* exact time `t` (seconds) via `clock.seek(t)` — never advances gameplay
|
|
119
119
|
* substeps. Synchronous: the clock's subscribed evaluators run inline,
|
|
@@ -129,7 +129,7 @@ export interface VgaiRenderHarness {
|
|
|
129
129
|
* by exactly `dt` seconds, in registration order. A render-mode capture
|
|
130
130
|
* loop calls this ONCE per OUTPUT frame, with `dt = 1 / fps`, BEFORE
|
|
131
131
|
* `seekFrame`/`renderOnce` for that frame — see
|
|
132
|
-
* `packages/
|
|
132
|
+
* `packages/volter-sdk/src/render/render-cinematic.ts`'s `captureFrames`, which
|
|
133
133
|
* drives exactly this sequence. `advanceFrame` is a genuinely
|
|
134
134
|
* FORWARD-ONLY, INCREMENTAL tick (it has no notion of "time" to seek
|
|
135
135
|
* to — it only knows "advance by `dt` more") — unlike {@link seek}/
|
|
@@ -213,7 +213,7 @@ export interface VgaiRenderHarness {
|
|
|
213
213
|
*/
|
|
214
214
|
simulateFixedDt(): number;
|
|
215
215
|
/**
|
|
216
|
-
* W3d (F11 perf regression gates) — the `
|
|
216
|
+
* W3d (F11 perf regression gates) — the `volter perf` sampling seam. Runs
|
|
217
217
|
* exactly `steps` FULL fixed-step gameplay frames (the same
|
|
218
218
|
* `game.runFrame(fixedDt)` path {@link simulateSubsteps} drives — every
|
|
219
219
|
* phase in `PHASE_ORDER`, including `render`, so `renderer.info`-backed
|
|
@@ -226,7 +226,7 @@ export interface VgaiRenderHarness {
|
|
|
226
226
|
* from the existing profiler (timings, render counters) or the live world
|
|
227
227
|
* roots themselves (counts).
|
|
228
228
|
*
|
|
229
|
-
* Honesty notes, recorded here because this seam is what `
|
|
229
|
+
* Honesty notes, recorded here because this seam is what `volter perf`
|
|
230
230
|
* reports: `gpuMs` is whatever the profiler's render reporter measured —
|
|
231
231
|
* `null` under headless SwiftShader (no usable GPU timer), never a
|
|
232
232
|
* fabricated 0; CPU timings include the profiler's own (small) phase
|
|
@@ -237,7 +237,7 @@ export interface VgaiRenderHarness {
|
|
|
237
237
|
perfSample(steps: number): PerfSampleReport;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
/** One measured fixed-step frame from {@link
|
|
240
|
+
/** One measured fixed-step frame from {@link VolterRenderHarness.perfSample}. */
|
|
241
241
|
export interface PerfFrameSample {
|
|
242
242
|
/** CPU time (ms) for the whole `runFrame` pass (profiler `cpuMs`). */
|
|
243
243
|
readonly cpuMs: number;
|
|
@@ -255,7 +255,7 @@ export interface PerfFrameSample {
|
|
|
255
255
|
};
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
/** Per-world structural counts from {@link
|
|
258
|
+
/** Per-world structural counts from {@link VolterRenderHarness.perfSample}. */
|
|
259
259
|
export interface PerfRootCount {
|
|
260
260
|
readonly id: string;
|
|
261
261
|
readonly kind: string;
|
|
@@ -279,7 +279,7 @@ export interface RenderControlHarnessOptions {
|
|
|
279
279
|
/**
|
|
280
280
|
* `GameInternal` (not just the game-facing `Game`) — this harness is
|
|
281
281
|
* itself a host (the render/capture driver), and I5's
|
|
282
|
-
* {@link
|
|
282
|
+
* {@link VolterRenderHarness.simulateSubsteps} needs the host-only
|
|
283
283
|
* `runFrame` surface (`runtime/game.ts`'s `GameInternal.runFrame` doc
|
|
284
284
|
* comment: "the host loop ... and `GameSession.step()` are the only
|
|
285
285
|
* callers"). Every real call site already holds a `GameInternal` (the
|
|
@@ -292,9 +292,9 @@ export interface RenderControlHarnessOptions {
|
|
|
292
292
|
/** Where to publish the harness. Defaults to the real `window` — override
|
|
293
293
|
* in a unit test to avoid touching the global object. */
|
|
294
294
|
readonly target?: Record<string, unknown>;
|
|
295
|
-
/** Where to read `?
|
|
295
|
+
/** Where to read `?volter-render=1` from. Defaults to `window.location`. */
|
|
296
296
|
readonly location?: { readonly search: string };
|
|
297
|
-
/** I8 render-integration seam — see {@link
|
|
297
|
+
/** I8 render-integration seam — see {@link VolterRenderHarness.advanceFrame}.
|
|
298
298
|
* Zero or more `FrameAdvancer`s invoked, in array order, on every
|
|
299
299
|
* `harness.advanceFrame(dt)` call. Default `[]` (no-op), which is exactly
|
|
300
300
|
* what a fixture with nothing gameplay-phase-driven (e.g. I0's fixture)
|
|
@@ -303,7 +303,7 @@ export interface RenderControlHarnessOptions {
|
|
|
303
303
|
readonly frameAdvancers?: readonly FrameAdvancer[];
|
|
304
304
|
/**
|
|
305
305
|
* I5 `--simulate` seam: the fixed substep `dt` (seconds)
|
|
306
|
-
* {@link
|
|
306
|
+
* {@link VolterRenderHarness.simulateSubsteps} passes to every
|
|
307
307
|
* `game.runFrame(fixedDt)` call. Default `1/60` — the same fixed timestep
|
|
308
308
|
* every real host's `createGameLoop` config uses (`create-runtime.ts`), so
|
|
309
309
|
* a `--simulate` fixture's Rapier world (whose own `integrationParameters.dt`
|
|
@@ -313,7 +313,7 @@ export interface RenderControlHarnessOptions {
|
|
|
313
313
|
*/
|
|
314
314
|
readonly simulateFixedDt?: number;
|
|
315
315
|
/**
|
|
316
|
-
* I5 AC 5 — see {@link
|
|
316
|
+
* I5 AC 5 — see {@link VolterRenderHarness.excludedFromDeterminism}. A
|
|
317
317
|
* static, page-declared list of subsystems this fixture/game has that do
|
|
318
318
|
* NOT participate in deterministic capture (e.g. live networking,
|
|
319
319
|
* non-seeded ambient audio) — named here as an explicit, reviewable
|
|
@@ -412,7 +412,7 @@ function warnUnrenderable(worldLabel: string, reason: string): void {
|
|
|
412
412
|
|
|
413
413
|
/**
|
|
414
414
|
* I5 AC 5 policy text for the two conditions {@link renderableRoots} skips —
|
|
415
|
-
* shared so {@link
|
|
415
|
+
* shared so {@link VolterRenderHarness.excludedFromDeterminism} names the SAME
|
|
416
416
|
* roots `renderOnce()` warns about, in the SAME words, rather than
|
|
417
417
|
* maintaining a second, driftable description of the same two checks.
|
|
418
418
|
*/
|
|
@@ -539,17 +539,17 @@ async function buildReadinessReport(hooks: RenderReadinessHooks): Promise<Readin
|
|
|
539
539
|
|
|
540
540
|
/**
|
|
541
541
|
* Build (and, when render mode is actually requested, publish) the
|
|
542
|
-
* `window.
|
|
542
|
+
* `window.__volterRender` harness. AC 4 — production protection — lives HERE,
|
|
543
543
|
* not just at the call site: even if a host application calls this
|
|
544
544
|
* unconditionally on every boot, the harness is only ever constructed *and*
|
|
545
545
|
* attached to `target` when `isRenderModeRequested(location)` is true;
|
|
546
546
|
* otherwise this is a no-op that returns `undefined`. A normal production
|
|
547
|
-
* gameplay page therefore never gets `window.
|
|
547
|
+
* gameplay page therefore never gets `window.__volterRender` no matter how
|
|
548
548
|
* this function is wired into its entry point.
|
|
549
549
|
*/
|
|
550
550
|
export function installRenderControlHarness(
|
|
551
551
|
opts: RenderControlHarnessOptions,
|
|
552
|
-
):
|
|
552
|
+
): VolterRenderHarness | undefined {
|
|
553
553
|
const location = opts.location ?? window.location;
|
|
554
554
|
if (!isRenderModeRequested(location)) return undefined;
|
|
555
555
|
|
|
@@ -559,7 +559,7 @@ export function installRenderControlHarness(
|
|
|
559
559
|
const simulateFixedDt = opts.simulateFixedDt ?? 1 / 60;
|
|
560
560
|
const declaredExclusions = opts.excludedFromDeterminism ?? [];
|
|
561
561
|
|
|
562
|
-
const harness:
|
|
562
|
+
const harness: VolterRenderHarness = {
|
|
563
563
|
seek(t: number): void {
|
|
564
564
|
clock.seek(t);
|
|
565
565
|
},
|
|
@@ -610,7 +610,7 @@ export function installRenderControlHarness(
|
|
|
610
610
|
},
|
|
611
611
|
};
|
|
612
612
|
|
|
613
|
-
target['
|
|
613
|
+
target['__volterRender'] = harness;
|
|
614
614
|
return harness;
|
|
615
615
|
}
|
|
616
616
|
|
|
@@ -54,14 +54,14 @@ export function installDeterministicRandom(seed: number): void {
|
|
|
54
54
|
* mode must be unavailable/protected in normal production gameplay unless
|
|
55
55
|
* explicitly enabled). Shared with `render-control.ts`'s own production
|
|
56
56
|
* guard so both checks agree on the same flag. */
|
|
57
|
-
export const RENDER_MODE_QUERY_PARAM = '
|
|
57
|
+
export const RENDER_MODE_QUERY_PARAM = 'volter-render';
|
|
58
58
|
/** Optional query param to pin a specific seed (default below when absent). */
|
|
59
|
-
export const RENDER_SEED_QUERY_PARAM = '
|
|
59
|
+
export const RENDER_SEED_QUERY_PARAM = 'volter-seed';
|
|
60
60
|
const DEFAULT_RENDER_SEED = 0x9e3779b9; // golden-ratio constant — an arbitrary but fixed default
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Seed `Math.random` iff the page's URL opts into render mode
|
|
64
|
-
* (`?
|
|
64
|
+
* (`?volter-render=1`), reading an optional `?volter-seed=<int>` override.
|
|
65
65
|
* Returns whether it seeded, purely for the caller's own diagnostics/log —
|
|
66
66
|
* the bootstrap entry doesn't need to branch on it.
|
|
67
67
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `runTicksWhenSettled` — the settled-aware tick driver behind BOTH session run-ticks doors
|
|
3
|
-
* (the bridge's `window.
|
|
3
|
+
* (the bridge's `window.__volter.runTicksSettled`, the editor relay's `run-ticks` case), one
|
|
4
4
|
* implementation with byte-identical semantics across doors, exactly as `runTicks` itself is
|
|
5
5
|
* shared (D17).
|
|
6
6
|
*
|
|
@@ -36,9 +36,9 @@ function resumeSuspended(): void {
|
|
|
36
36
|
|
|
37
37
|
/** Install once at editor boot (`audio.service.ts`). Idempotent. */
|
|
38
38
|
export function installGameAudio(): void {
|
|
39
|
-
const host = window as unknown as {
|
|
40
|
-
if (host.
|
|
41
|
-
host.
|
|
39
|
+
const host = window as unknown as { __volterAudioUnlock?: boolean } & typeof window;
|
|
40
|
+
if (host.__volterAudioUnlock) return;
|
|
41
|
+
host.__volterAudioUnlock = true;
|
|
42
42
|
const Orig = window.AudioContext;
|
|
43
43
|
if (typeof Orig !== 'function') return;
|
|
44
44
|
window.AudioContext = class extends Orig {
|
|
@@ -445,9 +445,9 @@ function observeMatchmaking(input: RequestInfo | URL, response: Response): void
|
|
|
445
445
|
|
|
446
446
|
/** Install once at editor boot (`network-observer.service.ts`). Idempotent. */
|
|
447
447
|
export function installGameNetwork(): void {
|
|
448
|
-
const host = window as unknown as {
|
|
449
|
-
if (host.
|
|
450
|
-
host.
|
|
448
|
+
const host = window as unknown as { __volterNetworkObserver?: boolean } & typeof window;
|
|
449
|
+
if (host.__volterNetworkObserver) return;
|
|
450
|
+
host.__volterNetworkObserver = true;
|
|
451
451
|
const Orig = window.WebSocket;
|
|
452
452
|
if (typeof Orig === 'function') {
|
|
453
453
|
const Observed = class extends Orig {
|
|
@@ -574,7 +574,7 @@ async function roomCall(method: string, args: unknown[], what: string, roomId?:
|
|
|
574
574
|
const query = new URLSearchParams({ roomId: roomId ?? mirror.roomId, method, args: JSON.stringify(args) });
|
|
575
575
|
const response = await fetch(`${monitorApi(mirror)}/room/call?${query}`);
|
|
576
576
|
if (response.status === 404) {
|
|
577
|
-
throw new Error(`The room server serves no Monitor view, so ${what} has nowhere to go (its \`server\` configuration sets
|
|
577
|
+
throw new Error(`The room server serves no Monitor view, so ${what} has nowhere to go (its \`server\` configuration sets VOLTER_ROOM_MONITOR=1).`);
|
|
578
578
|
}
|
|
579
579
|
if (!response.ok) throw new Error(`The room server refused ${what} (${response.status}).`);
|
|
580
580
|
}
|