@volter/editor-game 0.5.65
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/LICENSE +661 -0
- package/NOTICE +23 -0
- package/contributions/asset-budget-asset.menu.ts +26 -0
- package/contributions/asset-budget.action.ts +18 -0
- package/contributions/asset-budget.document.tsx +21 -0
- package/contributions/asset-budget.menu.ts +22 -0
- package/contributions/audio-unlock.service.ts +17 -0
- package/contributions/audio.utility.tsx +15 -0
- package/contributions/autoplay.service.ts +48 -0
- package/contributions/bridge.command.ts +172 -0
- package/contributions/build-profiles.document.tsx +21 -0
- package/contributions/build-progress.status.tsx +45 -0
- package/contributions/build.action.ts +26 -0
- package/contributions/build.command.ts +27 -0
- package/contributions/build.header.tsx +37 -0
- package/contributions/build.menu.ts +31 -0
- package/contributions/build.service.ts +31 -0
- package/contributions/connection.status.tsx +43 -0
- package/contributions/coverage.service.ts +118 -0
- package/contributions/edit-mode-audio.service.ts +23 -0
- package/contributions/edit-mode-networking.service.ts +30 -0
- package/contributions/game-document.service.ts +25 -0
- package/contributions/game-eval.command.ts +210 -0
- package/contributions/game.layout.ts +19 -0
- package/contributions/gameplay.command.ts +255 -0
- package/contributions/generation.service.ts +96 -0
- package/contributions/generations.status.tsx +53 -0
- package/contributions/ingest.service.ts +80 -0
- package/contributions/instances.command.ts +75 -0
- package/contributions/navmesh.menu.ts +39 -0
- package/contributions/navmesh.service.ts +19 -0
- package/contributions/network.utility.tsx +17 -0
- package/contributions/play.command.ts +355 -0
- package/contributions/profiler.action.ts +16 -0
- package/contributions/profiler.menu.ts +22 -0
- package/contributions/profiler.utility.tsx +17 -0
- package/contributions/react/component-board.service.ts +21 -0
- package/contributions/react/design-time-mount.service.ts +60 -0
- package/contributions/react/pasteboard.action.ts +41 -0
- package/contributions/react/react-inspector.service.ts +85 -0
- package/contributions/react/story-documents.service.ts +44 -0
- package/contributions/scene-document.service.ts +32 -0
- package/contributions/state-watch.action.ts +17 -0
- package/contributions/state-watch.menu.ts +23 -0
- package/contributions/state-watch.utility.tsx +20 -0
- package/contributions/team-playtest.service.ts +124 -0
- package/contributions/three/camera-runtime.inspector.tsx +34 -0
- package/contributions/three/component-board.service.ts +24 -0
- package/contributions/three/component-verbs.command.ts +110 -0
- package/contributions/three/component-verbs.service.ts +92 -0
- package/contributions/three/constraints.inspector.tsx +34 -0
- package/contributions/three/model-asset-sections.service.ts +26 -0
- package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
- package/contributions/three/story-documents.service.ts +31 -0
- package/contributions/three/three-authoring.service.ts +66 -0
- package/contributions/transport.header.tsx +19 -0
- package/contributions/xstate-behavior.action.ts +42 -0
- package/contributions/xstate-behavior.document.tsx +73 -0
- package/contributions/xstate-behavior.inspector.tsx +28 -0
- package/contributions/xstate-behavior.menu.ts +23 -0
- package/package.json +144 -0
- package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
- package/src/asset-budget/asset-budget-model.ts +799 -0
- package/src/asset-budget/basis-encoder.ts +165 -0
- package/src/asset-budget/gltf-io.ts +154 -0
- package/src/asset-budget/gltf-optimize.ts +384 -0
- package/src/asset-budget/image-dims.ts +78 -0
- package/src/asset-budget/optimize-apply.ts +221 -0
- package/src/audio/AudioDebuggerPanel.tsx +457 -0
- package/src/audio/audio-debugger-model.ts +87 -0
- package/src/bridge/call.ts +60 -0
- package/src/bridge/dispatch.ts +459 -0
- package/src/bridge/live-frames.ts +25 -0
- package/src/bridge/screenshot.ts +316 -0
- package/src/build/BuildProfilesPanel.tsx +476 -0
- package/src/build/build-session.ts +247 -0
- package/src/build/format-bytes.ts +14 -0
- package/src/command-results.ts +36 -0
- package/src/coverage/live-authoring-surface.ts +30 -0
- package/src/coverage/live-project-verbs.ts +162 -0
- package/src/coverage/native-system-coverage.ts +143 -0
- package/src/coverage/root-coverage.ts +79 -0
- package/src/coverage/session-coverage.ts +193 -0
- package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
- package/src/edit-mode/edit-mode-audio.ts +56 -0
- package/src/edit-mode/edit-mode-networking.ts +112 -0
- package/src/game-document/DevicePresetPicker.tsx +84 -0
- package/src/game-document/GameCaptureFrameButton.tsx +45 -0
- package/src/game-document/GameDocument.tsx +215 -0
- package/src/game-document/GamePanel.tsx +662 -0
- package/src/game-document/InstanceInspectorPicker.tsx +183 -0
- package/src/game-document/crowd-debug.ts +183 -0
- package/src/game-document/device-preview.ts +336 -0
- package/src/game-document/game-view-store.ts +107 -0
- package/src/game-document/physics-debug.ts +187 -0
- package/src/generation/GenerationActivity.tsx +421 -0
- package/src/generation/GenerationGallery.css +231 -0
- package/src/generation/generation-documents.tsx +338 -0
- package/src/generation/generation-jobs.ts +128 -0
- package/src/generation/generation-presentation.ts +257 -0
- package/src/host/adapter-reach.ts +445 -0
- package/src/host/adapter-runtime-bindings.ts +303 -0
- package/src/host/after-paint.ts +112 -0
- package/src/host/api/configurations.ts +66 -0
- package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
- package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
- package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
- package/src/host/authoring/contract-scenes-stories.ts +204 -0
- package/src/host/authoring/creation-site-related.ts +55 -0
- package/src/host/authoring/ephemeral-persistence.ts +29 -0
- package/src/host/authoring/gesture-persist.ts +84 -0
- package/src/host/authoring/ingest-data-writer.ts +232 -0
- package/src/host/authoring/ingest-source-persistence.ts +826 -0
- package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
- package/src/host/authoring/mounted-authoring.ts +41 -0
- package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
- package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
- package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
- package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
- package/src/host/authoring/pixi-isolation-assets.ts +25 -0
- package/src/host/authoring/pixi-live-write-target.ts +979 -0
- package/src/host/authoring/pixi-source-identity.ts +141 -0
- package/src/host/authoring/pixi-still-presentation.ts +71 -0
- package/src/host/authoring/pixi-structure-history.ts +237 -0
- package/src/host/authoring/pixi-transform-channels.ts +205 -0
- package/src/host/authoring/selection-remount-handoff.ts +23 -0
- package/src/host/authoring/source-persistence-backend.ts +373 -0
- package/src/host/authoring/source-refresh-revisions.ts +81 -0
- package/src/host/authoring/struct-write-pipe.ts +143 -0
- package/src/host/auto-frame-window.ts +89 -0
- package/src/host/binding-resolver.ts +393 -0
- package/src/host/browser-transpile.ts +631 -0
- package/src/host/canvas-entry-runtime.ts +95 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
- package/src/host/components/HeaderTelemetry.tsx +341 -0
- package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
- package/src/host/components/ResolutionPicker.tsx +89 -0
- package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
- package/src/host/components/frame-debugger-model.ts +579 -0
- package/src/host/components/header-telemetry-model.ts +74 -0
- package/src/host/components/scene-document.tsx +447 -0
- package/src/host/components/utility-view-state.ts +87 -0
- package/src/host/components/world-root-stage-binding.tsx +133 -0
- package/src/host/components/world-root-stage.ts +1046 -0
- package/src/host/coverage/authoring-read-probe.ts +583 -0
- package/src/host/coverage/capability-coverage.ts +1587 -0
- package/src/host/coverage/coverage-accounting.ts +261 -0
- package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
- package/src/host/coverage/project-verb-coverage.ts +148 -0
- package/src/host/coverage/system-adapter-coverage.ts +373 -0
- package/src/host/design-system-stories/StoryLayout.tsx +104 -0
- package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
- package/src/host/document-preview-three.ts +110 -0
- package/src/host/entry-adjudication.ts +89 -0
- package/src/host/game-location-guard.ts +150 -0
- package/src/host/game-module-access.ts +196 -0
- package/src/host/game-realm-page.ts +358 -0
- package/src/host/game-realm-reclaim.ts +55 -0
- package/src/host/game-realm-storage.ts +104 -0
- package/src/host/gameplay-export.ts +288 -0
- package/src/host/gameplay-recording.ts +717 -0
- package/src/host/gated-globals.ts +1511 -0
- package/src/host/history/json-history-resource.ts +254 -0
- package/src/host/ingest/registry.ts +261 -0
- package/src/host/instance-extract-actions.ts +120 -0
- package/src/host/instance-fork-actions.ts +120 -0
- package/src/host/play-control-hook.ts +26 -0
- package/src/host/playwright-shim.ts +479 -0
- package/src/host/projection/dom.ts +295 -0
- package/src/host/projection/pixi.ts +288 -0
- package/src/host/r3f-entry-runtime.ts +77 -0
- package/src/host/react-mount-runtime.ts +162 -0
- package/src/host/realm-services.ts +148 -0
- package/src/host/recording-preview.ts +106 -0
- package/src/host/roots/module-root.ts +203 -0
- package/src/host/roots/react-root.ts +181 -0
- package/src/host/same-realm-loop-gate.ts +544 -0
- package/src/host/scene-view-drawability.ts +69 -0
- package/src/host/sdk/tools.ts +31 -0
- package/src/host/served-bundle-runtime-modules.ts +331 -0
- package/src/host/server-log-bridge.ts +60 -0
- package/src/host/staged-projects.ts +24 -0
- package/src/host/stories/mounted-story-viewport-source.ts +64 -0
- package/src/host/stories/story-arg-descriptors.ts +50 -0
- package/src/host/stories/story-media-presence.ts +152 -0
- package/src/host/surface-content.ts +87 -0
- package/src/host/take-named-export.ts +23 -0
- package/src/host/three-ingest-runtime.ts +76 -0
- package/src/host/types-fastnoise-lite.d.ts +7 -0
- package/src/host/types-mikktspace.d.ts +20 -0
- package/src/host/types-troika-three-text.d.ts +7 -0
- package/src/host/use-active-performance-source.ts +53 -0
- package/src/host/viewport-pose-memory.ts +48 -0
- package/src/host/viewport-root-presentation.ts +40 -0
- package/src/ingest/active-ingest.ts +251 -0
- package/src/ingest/active-scene-navigation.ts +43 -0
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
- package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
- package/src/ingest/capture-wait-report.ts +122 -0
- package/src/ingest/deferred-ingest-play.ts +216 -0
- package/src/ingest/deferred-ingest-session.ts +23 -0
- package/src/ingest/discovery-public-ingest.ts +242 -0
- package/src/ingest/dom-stub-mark.ts +7 -0
- package/src/ingest/entry-load.ts +56 -0
- package/src/ingest/game-contract-realm.ts +34 -0
- package/src/ingest/game-pointer-lock.ts +89 -0
- package/src/ingest/held-scene-repaint.ts +73 -0
- package/src/ingest/host-surface-box.ts +174 -0
- package/src/ingest/ingest-boot-viewport.ts +54 -0
- package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
- package/src/ingest/ingest-canvas-scene.ts +50 -0
- package/src/ingest/ingest-evidence-hook.ts +131 -0
- package/src/ingest/ingest-frame-snapshot.ts +183 -0
- package/src/ingest/ingest-play-commands.ts +134 -0
- package/src/ingest/ingest-play-control.ts +299 -0
- package/src/ingest/ingest-render-debug.ts +320 -0
- package/src/ingest/ingest-siblings.ts +487 -0
- package/src/ingest/ingest-status.ts +62 -0
- package/src/ingest/live-ingest-facet.ts +46 -0
- package/src/ingest/module-mode.ts +229 -0
- package/src/ingest/mount-canvas-ingest-root.ts +899 -0
- package/src/ingest/mount-coverage.ts +296 -0
- package/src/ingest/mount-dom-ingest-root.ts +282 -0
- package/src/ingest/mount-ingest-root.ts +744 -0
- package/src/ingest/mount-three-ingest-root.ts +366 -0
- package/src/ingest/resolve-canvas.ts +47 -0
- package/src/ingest/resolve-three.ts +123 -0
- package/src/ingest/served-bundle.ts +109 -0
- package/src/ingest/served-html-boot.ts +292 -0
- package/src/ingest/surface-canvas.ts +84 -0
- package/src/ingest/surface-dom.ts +84 -0
- package/src/ingest/surface-three.ts +128 -0
- package/src/ingest/types.ts +76 -0
- package/src/ingest/unmount-ingest-root.ts +224 -0
- package/src/navmesh/navmesh-actions.ts +27 -0
- package/src/navmesh/navmesh-handler.ts +237 -0
- package/src/navmesh/navmesh-workflow-store.ts +78 -0
- package/src/network/NetworkInspectorPanel.tsx +644 -0
- package/src/network/network-inspector-model.ts +225 -0
- package/src/play/play-boot-stall.ts +118 -0
- package/src/play/play-log-events.ts +26 -0
- package/src/play/play-mode.ts +2553 -0
- package/src/play/play-recording.ts +335 -0
- package/src/play/react-play-live-authoring.ts +165 -0
- package/src/play-bar/PlayBar.tsx +488 -0
- package/src/play-bar/PlayerCountPicker.tsx +100 -0
- package/src/profiler/FrameDebuggerPanel.tsx +458 -0
- package/src/profiler/PerformancePanel.tsx +1068 -0
- package/src/profiler/ProfilerPanel.tsx +53 -0
- package/src/profiler/frame-debugger-store.ts +97 -0
- package/src/profiler/main-thread-busy.ts +90 -0
- package/src/react/design-time-react-mount.ts +782 -0
- package/src/react/dom-authoring-adapter.ts +764 -0
- package/src/react/pasteboard-materialize.ts +154 -0
- package/src/react/react-inspector-section.tsx +2496 -0
- package/src/react/react-world-authoring-adapter.ts +3795 -0
- package/src/react/story-documents/story-args-section.ts +19 -0
- package/src/react/story-documents/story-documents.tsx +1380 -0
- package/src/react/story-paint-bounds.ts +94 -0
- package/src/react/ui-board-document.tsx +101 -0
- package/src/react/ui-board-title.ts +9 -0
- package/src/react/ui-component-board.ts +72 -0
- package/src/services/audio-pose-guard.ts +81 -0
- package/src/services/game-audio-unlock.ts +48 -0
- package/src/state-watch/StateWatchPanel.tsx +535 -0
- package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
- package/src/three/authoring/constraint-inspector-section.tsx +189 -0
- package/src/three/authoring/design-time-renderer.ts +189 -0
- package/src/three/authoring/model-asset-inspector-section.css +41 -0
- package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
- package/src/three/authoring/oid-source-persistence.ts +557 -0
- package/src/three/authoring/r3f-design-session.ts +1174 -0
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
- package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
- package/src/three/authoring/spatial-audio-handles.ts +215 -0
- package/src/three/authoring/spatial-collider-handles.ts +229 -0
- package/src/three/authoring/spatial-joint-handles.ts +114 -0
- package/src/three/authoring/spatial-light-handles.ts +178 -0
- package/src/three/authoring/spatial-lod-handles.ts +93 -0
- package/src/three/authoring/spatial-particle-handles.ts +368 -0
- package/src/three/authoring/three-authoring-adapter.ts +1953 -0
- package/src/three/authoring/three-scene-identity.ts +19 -0
- package/src/three/authoring/three-spatial-handles.ts +161 -0
- package/src/three/authoring/typed-three-inspector.ts +528 -0
- package/src/three/component-verbs/extract-menu.ts +71 -0
- package/src/three/component-verbs/fork-menu.ts +78 -0
- package/src/three/component-verbs/internals-menu.ts +91 -0
- package/src/three/story-documents/three-story-documents.tsx +607 -0
- package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
- package/src/three/three-board/board-framing.ts +412 -0
- package/src/three/three-board/board-layout.ts +401 -0
- package/src/three/three-board/board-scene.ts +901 -0
- package/src/three/three-board/three-component-board.ts +62 -0
- package/src/xstate/XStateBehaviorSection.tsx +130 -0
- package/src/xstate/XStateMachineInspector.tsx +566 -0
- package/src/xstate/character-animation-machine.fixture.ts +74 -0
- package/src/xstate/live-behaviors.ts +106 -0
- package/src/xstate/use-live-actor-state.ts +44 -0
- package/src/xstate/xstate-graph.ts +235 -0
- package/src/xstate/xstate-layout.ts +76 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure data-path logic for the Network inspector (W3b) — no React/DOM, so the
|
|
3
|
+
* whole consumer side of the `NetworkingAdapter` inspector capabilities is
|
|
4
|
+
* headless-testable (`network-inspector.test.ts` drives it against a REAL
|
|
5
|
+
* loopback Colyseus server through the real arena adapter).
|
|
6
|
+
*
|
|
7
|
+
* The rendering component (`NetworkInspectorPanel.tsx`) owns only JSX; every
|
|
8
|
+
* behavior — capability detection, the seq-fenced message-log ring with
|
|
9
|
+
* pause/filter, the sparkline sample history, and the polyline geometry —
|
|
10
|
+
* lives here.
|
|
11
|
+
*
|
|
12
|
+
* Editor code never imports Colyseus (net-import-ban) — only the
|
|
13
|
+
* `NetworkingAdapter` interface.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type {
|
|
17
|
+
ConnectionState,
|
|
18
|
+
NetMessageEvent,
|
|
19
|
+
NetRates,
|
|
20
|
+
NetworkingAdapter,
|
|
21
|
+
ReplicationStats,
|
|
22
|
+
RoomInfo,
|
|
23
|
+
} from '@volter/editor-project/adapter';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The three headline readings the inspector's header shows.
|
|
27
|
+
*
|
|
28
|
+
* Every field is TRI-STATE, and the third state is the point: `undefined`
|
|
29
|
+
* means the adapter has no READER for it, which is not the same claim as the
|
|
30
|
+
* reader's own negative answer (`disconnected`, no room, zero counts). A row
|
|
31
|
+
* whose capability is absent is not rendered at all — never rendered as the
|
|
32
|
+
* negative, which would be a measurement the adapter never made.
|
|
33
|
+
*/
|
|
34
|
+
export interface NetworkView {
|
|
35
|
+
connectionState: ConnectionState | undefined;
|
|
36
|
+
roomInfo: RoomInfo | null | undefined;
|
|
37
|
+
stats: ReplicationStats | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Adapter -> header view model. Pure: no React, no DOM.
|
|
41
|
+
*
|
|
42
|
+
* Overloaded so a caller that has already established it HAS an adapter (the
|
|
43
|
+
* inspector panel returns the register-an-adapter notice before this point)
|
|
44
|
+
* is not handed a `| null` it would have to re-narrow with a branch that can
|
|
45
|
+
* never run. */
|
|
46
|
+
export function deriveNetworkView(adapter: NetworkingAdapter): NetworkView;
|
|
47
|
+
export function deriveNetworkView(
|
|
48
|
+
adapter: NetworkingAdapter | null | undefined,
|
|
49
|
+
): NetworkView | null;
|
|
50
|
+
export function deriveNetworkView(
|
|
51
|
+
adapter: NetworkingAdapter | null | undefined,
|
|
52
|
+
): NetworkView | null {
|
|
53
|
+
if (!adapter) return null;
|
|
54
|
+
return {
|
|
55
|
+
connectionState: adapter.getConnectionState?.(),
|
|
56
|
+
roomInfo: adapter.getRoomInfo?.(),
|
|
57
|
+
stats: adapter.getReplicationStats?.(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Which optional inspector capabilities the active adapter actually
|
|
62
|
+
* provides — the degradation ladder's per-section verdict. `null` input
|
|
63
|
+
* (no adapter registered) stays `null`: the panel renders the
|
|
64
|
+
* register-an-adapter notice instead of a fabricated all-false map. */
|
|
65
|
+
export interface NetworkCapabilityMap {
|
|
66
|
+
stateTree: boolean;
|
|
67
|
+
messageLog: boolean;
|
|
68
|
+
rates: boolean;
|
|
69
|
+
conditioning: boolean;
|
|
70
|
+
/** The adapter can read connection lifecycle state (`getConnectionState`).
|
|
71
|
+
* Absent ⇒ the link row is unsupported, NOT "disconnected". */
|
|
72
|
+
connection: boolean;
|
|
73
|
+
/** The adapter can name the active room (`getRoomInfo`). Absent ⇒ the room
|
|
74
|
+
* row is unsupported, NOT "connected to no room". */
|
|
75
|
+
room: boolean;
|
|
76
|
+
/** The adapter counts replication activity (`getReplicationStats`). Absent ⇒
|
|
77
|
+
* the stats row is unsupported, NOT a measured-quiet link. */
|
|
78
|
+
stats: boolean;
|
|
79
|
+
/** The adapter reports a local player identity (`getPlayerIdentity`) — the
|
|
80
|
+
* inspector shows the current name. */
|
|
81
|
+
identity: boolean;
|
|
82
|
+
/** The adapter can SET the local identity (`setPlayerIdentity`) — the
|
|
83
|
+
* inspector shows an EDITABLE name field. Implies `identity`. */
|
|
84
|
+
identitySettable: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function deriveNetworkCapabilities(
|
|
88
|
+
adapter: NetworkingAdapter | null | undefined,
|
|
89
|
+
): NetworkCapabilityMap | null {
|
|
90
|
+
if (!adapter) return null;
|
|
91
|
+
return {
|
|
92
|
+
stateTree: typeof adapter.getStateSnapshot === 'function',
|
|
93
|
+
messageLog: typeof adapter.messageEvents === 'function',
|
|
94
|
+
rates: typeof adapter.getRates === 'function',
|
|
95
|
+
// A conditioner needs BOTH the read and the write to render controls
|
|
96
|
+
// honestly (write-only would show state it can't verify; read-only
|
|
97
|
+
// couldn't apply anything).
|
|
98
|
+
conditioning:
|
|
99
|
+
typeof adapter.getConditioning === 'function' &&
|
|
100
|
+
typeof adapter.setConditioning === 'function',
|
|
101
|
+
connection: typeof adapter.getConnectionState === 'function',
|
|
102
|
+
room: typeof adapter.getRoomInfo === 'function',
|
|
103
|
+
stats: typeof adapter.getReplicationStats === 'function',
|
|
104
|
+
identity: typeof adapter.getPlayerIdentity === 'function',
|
|
105
|
+
identitySettable: typeof adapter.setPlayerIdentity === 'function',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Consumer-side message log over `NetworkingAdapter.messageEvents`.
|
|
111
|
+
*
|
|
112
|
+
* Pulls seq-fenced events from the adapter's ring into its own bounded ring.
|
|
113
|
+
* PAUSE stops consuming — the fence stays where it was, so resuming picks up
|
|
114
|
+
* whatever the adapter's ring still holds (a long pause loses only what the
|
|
115
|
+
* adapter itself evicted; nothing is fabricated to fill the gap).
|
|
116
|
+
*/
|
|
117
|
+
export class MessageLogModel {
|
|
118
|
+
private entries: NetMessageEvent[] = [];
|
|
119
|
+
private lastSeq = 0;
|
|
120
|
+
paused = false;
|
|
121
|
+
|
|
122
|
+
constructor(private readonly capacity = 500) {}
|
|
123
|
+
|
|
124
|
+
/** Consume new events (`seq > fence`) from the adapter. Returns how many
|
|
125
|
+
* were appended (0 while paused or when the capability is absent). */
|
|
126
|
+
pull(adapter: NetworkingAdapter | null | undefined): number {
|
|
127
|
+
if (this.paused || !adapter?.messageEvents) return 0;
|
|
128
|
+
const fresh = adapter.messageEvents(this.lastSeq);
|
|
129
|
+
if (fresh.length === 0) return 0;
|
|
130
|
+
for (const event of fresh) {
|
|
131
|
+
this.entries.push(event);
|
|
132
|
+
if (event.seq > this.lastSeq) this.lastSeq = event.seq;
|
|
133
|
+
}
|
|
134
|
+
if (this.entries.length > this.capacity) {
|
|
135
|
+
this.entries.splice(0, this.entries.length - this.capacity);
|
|
136
|
+
}
|
|
137
|
+
return fresh.length;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
get all(): readonly NetMessageEvent[] {
|
|
141
|
+
return this.entries;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
clear(): void {
|
|
145
|
+
// Deliberately NOT resetting the fence: clear empties the view; already-
|
|
146
|
+
// observed events don't come back.
|
|
147
|
+
this.entries = [];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Case-insensitive substring filter on `type` (the log's filter box). An
|
|
152
|
+
* empty/whitespace filter passes everything. */
|
|
153
|
+
export function filterMessages(
|
|
154
|
+
events: readonly NetMessageEvent[],
|
|
155
|
+
filter: string,
|
|
156
|
+
): NetMessageEvent[] {
|
|
157
|
+
const needle = filter.trim().toLowerCase();
|
|
158
|
+
if (!needle) return [...events];
|
|
159
|
+
return events.filter((e) => e.type.toLowerCase().includes(needle));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Fixed-capacity rolling series feeding one sparkline. */
|
|
163
|
+
export class RateSeries {
|
|
164
|
+
private values: number[] = [];
|
|
165
|
+
|
|
166
|
+
constructor(readonly capacity = 60) {}
|
|
167
|
+
|
|
168
|
+
push(value: number): void {
|
|
169
|
+
this.values.push(value);
|
|
170
|
+
if (this.values.length > this.capacity) this.values.shift();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
get data(): readonly number[] {
|
|
174
|
+
return this.values;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
get latest(): number | null {
|
|
178
|
+
return this.values.length > 0 ? (this.values[this.values.length - 1] as number) : null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Rolling history of `getRates()` samples driving the sparklines. A byte
|
|
184
|
+
* field the adapter OMITS pushes nothing — its series stays empty and the
|
|
185
|
+
* panel marks that direction "n/a" instead of drawing a fabricated zero line
|
|
186
|
+
* (the arena adapter omits `bytesInPerSec`: Colyseus patch bytes aren't
|
|
187
|
+
* observable through the Callbacks API).
|
|
188
|
+
*/
|
|
189
|
+
export class RateHistory {
|
|
190
|
+
readonly msgsIn = new RateSeries();
|
|
191
|
+
readonly msgsOut = new RateSeries();
|
|
192
|
+
readonly bytesIn = new RateSeries();
|
|
193
|
+
readonly bytesOut = new RateSeries();
|
|
194
|
+
|
|
195
|
+
sample(rates: NetRates): void {
|
|
196
|
+
this.msgsIn.push(rates.msgsInPerSec);
|
|
197
|
+
this.msgsOut.push(rates.msgsOutPerSec);
|
|
198
|
+
if (rates.bytesInPerSec !== undefined) this.bytesIn.push(rates.bytesInPerSec);
|
|
199
|
+
if (rates.bytesOutPerSec !== undefined) this.bytesOut.push(rates.bytesOutPerSec);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* SVG polyline `points` for a sparkline: newest sample at the right edge,
|
|
205
|
+
* y auto-scaled to the series max (min 1 so an all-zero series draws a flat
|
|
206
|
+
* baseline, not NaN). Fewer than 2 samples → `''` (nothing to draw yet).
|
|
207
|
+
*/
|
|
208
|
+
export function sparklinePoints(
|
|
209
|
+
values: readonly number[],
|
|
210
|
+
width: number,
|
|
211
|
+
height: number,
|
|
212
|
+
pad = 1,
|
|
213
|
+
): string {
|
|
214
|
+
if (values.length < 2) return '';
|
|
215
|
+
const max = Math.max(1, ...values);
|
|
216
|
+
const innerH = height - pad * 2;
|
|
217
|
+
const stepX = width / (values.length - 1);
|
|
218
|
+
return values
|
|
219
|
+
.map((v, i) => {
|
|
220
|
+
const x = i * stepX;
|
|
221
|
+
const y = pad + innerH * (1 - v / max);
|
|
222
|
+
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
223
|
+
})
|
|
224
|
+
.join(' ');
|
|
225
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The play boot's stall guard: a BACKGROUNDED editor tab must never turn a
|
|
3
|
+
* network-shaped play-start step into a silent timeout.
|
|
4
|
+
*
|
|
5
|
+
* ## The measurement this exists for
|
|
6
|
+
*
|
|
7
|
+
* Play start is frame-independent — it boots fine on a tab whose
|
|
8
|
+
* `requestAnimationFrame` never fires at all (measured: a hidden real-Chrome
|
|
9
|
+
* tab starts play in ~1s, and so does a tab with rAF stubbed dead). What is
|
|
10
|
+
* NOT independent is the network. A backgrounded tab's requests are
|
|
11
|
+
* deprioritized by the browser, so the fetch-shaped steps of
|
|
12
|
+
* `enterPlayMode` — the log-session POST, the manifest fetch, the dynamic
|
|
13
|
+
* imports that resolve each root's entry — queue behind whatever else that
|
|
14
|
+
* origin is loading. Measured in a hidden tab on a cold Vite graph:
|
|
15
|
+
*
|
|
16
|
+
* post-logSession @13125 (9.5s)
|
|
17
|
+
* post-fetchManifest @158274 (145s)
|
|
18
|
+
* pre-resolveAllRootEntries (still pending at 165s)
|
|
19
|
+
*
|
|
20
|
+
* The relay's own `play` budget is 120s (`server-utils.ts`'s
|
|
21
|
+
* `relayCommandTimeoutMs`), so the caller gets
|
|
22
|
+
* "Command timed out — editor connected but did not respond" — a message that
|
|
23
|
+
* names nothing, points at nothing, and hides the one remedy that works
|
|
24
|
+
* (foreground the tab; the very next `vgai play` then starts in seconds).
|
|
25
|
+
*
|
|
26
|
+
* ## What this does, and deliberately does not do
|
|
27
|
+
*
|
|
28
|
+
* A step that exceeds {@link PLAY_BOOT_STALL_MS} while `document.hidden` is
|
|
29
|
+
* reported as a NAMED failure that states the step, the elapsed budget, and
|
|
30
|
+
* the fix. A step that exceeds it while the tab is VISIBLE is not touched:
|
|
31
|
+
* a foregrounded tab is not being starved, so slow there means genuinely
|
|
32
|
+
* slow, and failing it would be a regression. The visibility is re-read when
|
|
33
|
+
* the timer fires, so a tab foregrounded mid-boot keeps its boot.
|
|
34
|
+
*
|
|
35
|
+
* Rejecting does not cancel the underlying request — it abandons it. Only
|
|
36
|
+
* steps whose abandoned result is inert (a fetch, a module import) are
|
|
37
|
+
* wrapped; the runtime MOUNT is not, because an abandoned mount would leak a
|
|
38
|
+
* live session.
|
|
39
|
+
*
|
|
40
|
+
* (`p-timeout` was considered and rejected: its contract is an unconditional
|
|
41
|
+
* deadline, and the whole point here is that the deadline applies only while
|
|
42
|
+
* the tab is hidden — plus the message is the deliverable.)
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/** How long one network-shaped play-boot step may sit while the tab is hidden.
|
|
46
|
+
* Generous by design: every such step measures in TENS OF MILLISECONDS on a
|
|
47
|
+
* healthy hidden tab, so this only fires on a genuinely starved one. */
|
|
48
|
+
export const PLAY_BOOT_STALL_MS = 15_000;
|
|
49
|
+
|
|
50
|
+
export interface PlayBootStallOptions {
|
|
51
|
+
/** Whether the editor tab is backgrounded right now. Re-read when the
|
|
52
|
+
* timer fires, never captured up front. */
|
|
53
|
+
readonly isHidden: () => boolean;
|
|
54
|
+
/** The editor page's own URL, so the message can name the tab to open. */
|
|
55
|
+
readonly editorUrl?: string | undefined;
|
|
56
|
+
readonly stallMs?: number | undefined;
|
|
57
|
+
readonly setTimer?: ((fn: () => void, ms: number) => unknown) | undefined;
|
|
58
|
+
readonly clearTimer?: ((handle: unknown) => void) | undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The one message. Names the step, the condition, and the remedy. */
|
|
62
|
+
export function playBootStallMessage(step: string, stallMs: number, editorUrl?: string): string {
|
|
63
|
+
const open = editorUrl ? ` (open ${editorUrl})` : '';
|
|
64
|
+
return (
|
|
65
|
+
`Play start stalled at "${step}" for ${Math.round(stallMs / 1000)}s and the editor tab is ` +
|
|
66
|
+
'HIDDEN — a backgrounded tab has its network requests deprioritized by the browser, so this ' +
|
|
67
|
+
'step is queued rather than broken. Bring the editor tab to the foreground' +
|
|
68
|
+
`${open} and run play again; the engine loop itself is hidden-safe, only the boot's ` +
|
|
69
|
+
'fetches are starved.'
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Run one network-shaped play-boot step under the hidden-tab stall guard.
|
|
75
|
+
*
|
|
76
|
+
* Resolves/rejects with `work`'s own outcome in every case except one: the
|
|
77
|
+
* step is still pending after the budget AND the tab is hidden, which
|
|
78
|
+
* rejects with {@link playBootStallMessage}.
|
|
79
|
+
*/
|
|
80
|
+
export function withPlayBootStallGuard<T>(
|
|
81
|
+
step: string,
|
|
82
|
+
work: Promise<T>,
|
|
83
|
+
options: PlayBootStallOptions,
|
|
84
|
+
): Promise<T> {
|
|
85
|
+
const stallMs = options.stallMs ?? PLAY_BOOT_STALL_MS;
|
|
86
|
+
const setTimer =
|
|
87
|
+
options.setTimer ?? ((fn: () => void, ms: number) => setTimeout(fn, ms) as unknown);
|
|
88
|
+
const clearTimer =
|
|
89
|
+
options.clearTimer ??
|
|
90
|
+
((handle: unknown) => clearTimeout(handle as ReturnType<typeof setTimeout>));
|
|
91
|
+
|
|
92
|
+
return new Promise<T>((resolve, reject) => {
|
|
93
|
+
let settled = false;
|
|
94
|
+
const handle = setTimer(() => {
|
|
95
|
+
if (settled) return;
|
|
96
|
+
// Re-read visibility HERE: a tab foregrounded during the wait is no
|
|
97
|
+
// longer starved, and must keep the boot it already has in flight.
|
|
98
|
+
if (!options.isHidden()) return;
|
|
99
|
+
settled = true;
|
|
100
|
+
reject(new Error(playBootStallMessage(step, stallMs, options.editorUrl)));
|
|
101
|
+
}, stallMs);
|
|
102
|
+
|
|
103
|
+
work.then(
|
|
104
|
+
(value) => {
|
|
105
|
+
if (settled) return;
|
|
106
|
+
settled = true;
|
|
107
|
+
clearTimer(handle);
|
|
108
|
+
resolve(value);
|
|
109
|
+
},
|
|
110
|
+
(error: unknown) => {
|
|
111
|
+
if (settled) return;
|
|
112
|
+
settled = true;
|
|
113
|
+
clearTimer(handle);
|
|
114
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LogEntry } from '@volter/editor-core/editor-api';
|
|
2
|
+
import type { TickStampedEvent } from '@volter/editor-project/adapter/system-adapter';
|
|
3
|
+
|
|
4
|
+
/** Convert newly observed game-authored debug events into persistent JSONL entries. */
|
|
5
|
+
export function debugEventsToLogEntries(
|
|
6
|
+
events: readonly TickStampedEvent[],
|
|
7
|
+
sinceSeq: number,
|
|
8
|
+
wallTime = Date.now(),
|
|
9
|
+
): { entries: LogEntry[]; lastSeq: number } {
|
|
10
|
+
const fresh = events.filter((event) => event.seq > sinceSeq).sort((a, b) => a.seq - b.seq);
|
|
11
|
+
return {
|
|
12
|
+
entries: fresh.map((event) => ({
|
|
13
|
+
t: wallTime,
|
|
14
|
+
level: 'info',
|
|
15
|
+
source: 'game-event',
|
|
16
|
+
msg: event.event,
|
|
17
|
+
tick: event.tick,
|
|
18
|
+
simT: event.simT,
|
|
19
|
+
meta: {
|
|
20
|
+
seq: event.seq,
|
|
21
|
+
...(event.detail !== undefined ? { detail: event.detail } : {}),
|
|
22
|
+
},
|
|
23
|
+
})),
|
|
24
|
+
lastSeq: fresh.at(-1)?.seq ?? sinceSeq,
|
|
25
|
+
};
|
|
26
|
+
}
|