@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.
Files changed (302) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +23 -0
  3. package/contributions/asset-budget-asset.menu.ts +26 -0
  4. package/contributions/asset-budget.action.ts +18 -0
  5. package/contributions/asset-budget.document.tsx +21 -0
  6. package/contributions/asset-budget.menu.ts +22 -0
  7. package/contributions/audio-unlock.service.ts +17 -0
  8. package/contributions/audio.utility.tsx +15 -0
  9. package/contributions/autoplay.service.ts +48 -0
  10. package/contributions/bridge.command.ts +172 -0
  11. package/contributions/build-profiles.document.tsx +21 -0
  12. package/contributions/build-progress.status.tsx +45 -0
  13. package/contributions/build.action.ts +26 -0
  14. package/contributions/build.command.ts +27 -0
  15. package/contributions/build.header.tsx +37 -0
  16. package/contributions/build.menu.ts +31 -0
  17. package/contributions/build.service.ts +31 -0
  18. package/contributions/connection.status.tsx +43 -0
  19. package/contributions/coverage.service.ts +118 -0
  20. package/contributions/edit-mode-audio.service.ts +23 -0
  21. package/contributions/edit-mode-networking.service.ts +30 -0
  22. package/contributions/game-document.service.ts +25 -0
  23. package/contributions/game-eval.command.ts +210 -0
  24. package/contributions/game.layout.ts +19 -0
  25. package/contributions/gameplay.command.ts +255 -0
  26. package/contributions/generation.service.ts +96 -0
  27. package/contributions/generations.status.tsx +53 -0
  28. package/contributions/ingest.service.ts +80 -0
  29. package/contributions/instances.command.ts +75 -0
  30. package/contributions/navmesh.menu.ts +39 -0
  31. package/contributions/navmesh.service.ts +19 -0
  32. package/contributions/network.utility.tsx +17 -0
  33. package/contributions/play.command.ts +355 -0
  34. package/contributions/profiler.action.ts +16 -0
  35. package/contributions/profiler.menu.ts +22 -0
  36. package/contributions/profiler.utility.tsx +17 -0
  37. package/contributions/react/component-board.service.ts +21 -0
  38. package/contributions/react/design-time-mount.service.ts +60 -0
  39. package/contributions/react/pasteboard.action.ts +41 -0
  40. package/contributions/react/react-inspector.service.ts +85 -0
  41. package/contributions/react/story-documents.service.ts +44 -0
  42. package/contributions/scene-document.service.ts +32 -0
  43. package/contributions/state-watch.action.ts +17 -0
  44. package/contributions/state-watch.menu.ts +23 -0
  45. package/contributions/state-watch.utility.tsx +20 -0
  46. package/contributions/team-playtest.service.ts +124 -0
  47. package/contributions/three/camera-runtime.inspector.tsx +34 -0
  48. package/contributions/three/component-board.service.ts +24 -0
  49. package/contributions/three/component-verbs.command.ts +110 -0
  50. package/contributions/three/component-verbs.service.ts +92 -0
  51. package/contributions/three/constraints.inspector.tsx +34 -0
  52. package/contributions/three/model-asset-sections.service.ts +26 -0
  53. package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
  54. package/contributions/three/story-documents.service.ts +31 -0
  55. package/contributions/three/three-authoring.service.ts +66 -0
  56. package/contributions/transport.header.tsx +19 -0
  57. package/contributions/xstate-behavior.action.ts +42 -0
  58. package/contributions/xstate-behavior.document.tsx +73 -0
  59. package/contributions/xstate-behavior.inspector.tsx +28 -0
  60. package/contributions/xstate-behavior.menu.ts +23 -0
  61. package/package.json +144 -0
  62. package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
  63. package/src/asset-budget/asset-budget-model.ts +799 -0
  64. package/src/asset-budget/basis-encoder.ts +165 -0
  65. package/src/asset-budget/gltf-io.ts +154 -0
  66. package/src/asset-budget/gltf-optimize.ts +384 -0
  67. package/src/asset-budget/image-dims.ts +78 -0
  68. package/src/asset-budget/optimize-apply.ts +221 -0
  69. package/src/audio/AudioDebuggerPanel.tsx +457 -0
  70. package/src/audio/audio-debugger-model.ts +87 -0
  71. package/src/bridge/call.ts +60 -0
  72. package/src/bridge/dispatch.ts +459 -0
  73. package/src/bridge/live-frames.ts +25 -0
  74. package/src/bridge/screenshot.ts +316 -0
  75. package/src/build/BuildProfilesPanel.tsx +476 -0
  76. package/src/build/build-session.ts +247 -0
  77. package/src/build/format-bytes.ts +14 -0
  78. package/src/command-results.ts +36 -0
  79. package/src/coverage/live-authoring-surface.ts +30 -0
  80. package/src/coverage/live-project-verbs.ts +162 -0
  81. package/src/coverage/native-system-coverage.ts +143 -0
  82. package/src/coverage/root-coverage.ts +79 -0
  83. package/src/coverage/session-coverage.ts +193 -0
  84. package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
  85. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
  86. package/src/edit-mode/edit-mode-audio.ts +56 -0
  87. package/src/edit-mode/edit-mode-networking.ts +112 -0
  88. package/src/game-document/DevicePresetPicker.tsx +84 -0
  89. package/src/game-document/GameCaptureFrameButton.tsx +45 -0
  90. package/src/game-document/GameDocument.tsx +215 -0
  91. package/src/game-document/GamePanel.tsx +662 -0
  92. package/src/game-document/InstanceInspectorPicker.tsx +183 -0
  93. package/src/game-document/crowd-debug.ts +183 -0
  94. package/src/game-document/device-preview.ts +336 -0
  95. package/src/game-document/game-view-store.ts +107 -0
  96. package/src/game-document/physics-debug.ts +187 -0
  97. package/src/generation/GenerationActivity.tsx +421 -0
  98. package/src/generation/GenerationGallery.css +231 -0
  99. package/src/generation/generation-documents.tsx +338 -0
  100. package/src/generation/generation-jobs.ts +128 -0
  101. package/src/generation/generation-presentation.ts +257 -0
  102. package/src/host/adapter-reach.ts +445 -0
  103. package/src/host/adapter-runtime-bindings.ts +303 -0
  104. package/src/host/after-paint.ts +112 -0
  105. package/src/host/api/configurations.ts +66 -0
  106. package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
  107. package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
  108. package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
  109. package/src/host/authoring/contract-scenes-stories.ts +204 -0
  110. package/src/host/authoring/creation-site-related.ts +55 -0
  111. package/src/host/authoring/ephemeral-persistence.ts +29 -0
  112. package/src/host/authoring/gesture-persist.ts +84 -0
  113. package/src/host/authoring/ingest-data-writer.ts +232 -0
  114. package/src/host/authoring/ingest-source-persistence.ts +826 -0
  115. package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
  116. package/src/host/authoring/mounted-authoring.ts +41 -0
  117. package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
  118. package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
  119. package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
  120. package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
  121. package/src/host/authoring/pixi-isolation-assets.ts +25 -0
  122. package/src/host/authoring/pixi-live-write-target.ts +979 -0
  123. package/src/host/authoring/pixi-source-identity.ts +141 -0
  124. package/src/host/authoring/pixi-still-presentation.ts +71 -0
  125. package/src/host/authoring/pixi-structure-history.ts +237 -0
  126. package/src/host/authoring/pixi-transform-channels.ts +205 -0
  127. package/src/host/authoring/selection-remount-handoff.ts +23 -0
  128. package/src/host/authoring/source-persistence-backend.ts +373 -0
  129. package/src/host/authoring/source-refresh-revisions.ts +81 -0
  130. package/src/host/authoring/struct-write-pipe.ts +143 -0
  131. package/src/host/auto-frame-window.ts +89 -0
  132. package/src/host/binding-resolver.ts +393 -0
  133. package/src/host/browser-transpile.ts +631 -0
  134. package/src/host/canvas-entry-runtime.ts +95 -0
  135. package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
  136. package/src/host/components/HeaderTelemetry.tsx +341 -0
  137. package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
  138. package/src/host/components/ResolutionPicker.tsx +89 -0
  139. package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
  140. package/src/host/components/frame-debugger-model.ts +579 -0
  141. package/src/host/components/header-telemetry-model.ts +74 -0
  142. package/src/host/components/scene-document.tsx +447 -0
  143. package/src/host/components/utility-view-state.ts +87 -0
  144. package/src/host/components/world-root-stage-binding.tsx +133 -0
  145. package/src/host/components/world-root-stage.ts +1046 -0
  146. package/src/host/coverage/authoring-read-probe.ts +583 -0
  147. package/src/host/coverage/capability-coverage.ts +1587 -0
  148. package/src/host/coverage/coverage-accounting.ts +261 -0
  149. package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
  150. package/src/host/coverage/project-verb-coverage.ts +148 -0
  151. package/src/host/coverage/system-adapter-coverage.ts +373 -0
  152. package/src/host/design-system-stories/StoryLayout.tsx +104 -0
  153. package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
  154. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
  155. package/src/host/document-preview-three.ts +110 -0
  156. package/src/host/entry-adjudication.ts +89 -0
  157. package/src/host/game-location-guard.ts +150 -0
  158. package/src/host/game-module-access.ts +196 -0
  159. package/src/host/game-realm-page.ts +358 -0
  160. package/src/host/game-realm-reclaim.ts +55 -0
  161. package/src/host/game-realm-storage.ts +104 -0
  162. package/src/host/gameplay-export.ts +288 -0
  163. package/src/host/gameplay-recording.ts +717 -0
  164. package/src/host/gated-globals.ts +1511 -0
  165. package/src/host/history/json-history-resource.ts +254 -0
  166. package/src/host/ingest/registry.ts +261 -0
  167. package/src/host/instance-extract-actions.ts +120 -0
  168. package/src/host/instance-fork-actions.ts +120 -0
  169. package/src/host/play-control-hook.ts +26 -0
  170. package/src/host/playwright-shim.ts +479 -0
  171. package/src/host/projection/dom.ts +295 -0
  172. package/src/host/projection/pixi.ts +288 -0
  173. package/src/host/r3f-entry-runtime.ts +77 -0
  174. package/src/host/react-mount-runtime.ts +162 -0
  175. package/src/host/realm-services.ts +148 -0
  176. package/src/host/recording-preview.ts +106 -0
  177. package/src/host/roots/module-root.ts +203 -0
  178. package/src/host/roots/react-root.ts +181 -0
  179. package/src/host/same-realm-loop-gate.ts +544 -0
  180. package/src/host/scene-view-drawability.ts +69 -0
  181. package/src/host/sdk/tools.ts +31 -0
  182. package/src/host/served-bundle-runtime-modules.ts +331 -0
  183. package/src/host/server-log-bridge.ts +60 -0
  184. package/src/host/staged-projects.ts +24 -0
  185. package/src/host/stories/mounted-story-viewport-source.ts +64 -0
  186. package/src/host/stories/story-arg-descriptors.ts +50 -0
  187. package/src/host/stories/story-media-presence.ts +152 -0
  188. package/src/host/surface-content.ts +87 -0
  189. package/src/host/take-named-export.ts +23 -0
  190. package/src/host/three-ingest-runtime.ts +76 -0
  191. package/src/host/types-fastnoise-lite.d.ts +7 -0
  192. package/src/host/types-mikktspace.d.ts +20 -0
  193. package/src/host/types-troika-three-text.d.ts +7 -0
  194. package/src/host/use-active-performance-source.ts +53 -0
  195. package/src/host/viewport-pose-memory.ts +48 -0
  196. package/src/host/viewport-root-presentation.ts +40 -0
  197. package/src/ingest/active-ingest.ts +251 -0
  198. package/src/ingest/active-scene-navigation.ts +43 -0
  199. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
  200. package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
  201. package/src/ingest/capture-wait-report.ts +122 -0
  202. package/src/ingest/deferred-ingest-play.ts +216 -0
  203. package/src/ingest/deferred-ingest-session.ts +23 -0
  204. package/src/ingest/discovery-public-ingest.ts +242 -0
  205. package/src/ingest/dom-stub-mark.ts +7 -0
  206. package/src/ingest/entry-load.ts +56 -0
  207. package/src/ingest/game-contract-realm.ts +34 -0
  208. package/src/ingest/game-pointer-lock.ts +89 -0
  209. package/src/ingest/held-scene-repaint.ts +73 -0
  210. package/src/ingest/host-surface-box.ts +174 -0
  211. package/src/ingest/ingest-boot-viewport.ts +54 -0
  212. package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
  213. package/src/ingest/ingest-canvas-scene.ts +50 -0
  214. package/src/ingest/ingest-evidence-hook.ts +131 -0
  215. package/src/ingest/ingest-frame-snapshot.ts +183 -0
  216. package/src/ingest/ingest-play-commands.ts +134 -0
  217. package/src/ingest/ingest-play-control.ts +299 -0
  218. package/src/ingest/ingest-render-debug.ts +320 -0
  219. package/src/ingest/ingest-siblings.ts +487 -0
  220. package/src/ingest/ingest-status.ts +62 -0
  221. package/src/ingest/live-ingest-facet.ts +46 -0
  222. package/src/ingest/module-mode.ts +229 -0
  223. package/src/ingest/mount-canvas-ingest-root.ts +899 -0
  224. package/src/ingest/mount-coverage.ts +296 -0
  225. package/src/ingest/mount-dom-ingest-root.ts +282 -0
  226. package/src/ingest/mount-ingest-root.ts +744 -0
  227. package/src/ingest/mount-three-ingest-root.ts +366 -0
  228. package/src/ingest/resolve-canvas.ts +47 -0
  229. package/src/ingest/resolve-three.ts +123 -0
  230. package/src/ingest/served-bundle.ts +109 -0
  231. package/src/ingest/served-html-boot.ts +292 -0
  232. package/src/ingest/surface-canvas.ts +84 -0
  233. package/src/ingest/surface-dom.ts +84 -0
  234. package/src/ingest/surface-three.ts +128 -0
  235. package/src/ingest/types.ts +76 -0
  236. package/src/ingest/unmount-ingest-root.ts +224 -0
  237. package/src/navmesh/navmesh-actions.ts +27 -0
  238. package/src/navmesh/navmesh-handler.ts +237 -0
  239. package/src/navmesh/navmesh-workflow-store.ts +78 -0
  240. package/src/network/NetworkInspectorPanel.tsx +644 -0
  241. package/src/network/network-inspector-model.ts +225 -0
  242. package/src/play/play-boot-stall.ts +118 -0
  243. package/src/play/play-log-events.ts +26 -0
  244. package/src/play/play-mode.ts +2553 -0
  245. package/src/play/play-recording.ts +335 -0
  246. package/src/play/react-play-live-authoring.ts +165 -0
  247. package/src/play-bar/PlayBar.tsx +488 -0
  248. package/src/play-bar/PlayerCountPicker.tsx +100 -0
  249. package/src/profiler/FrameDebuggerPanel.tsx +458 -0
  250. package/src/profiler/PerformancePanel.tsx +1068 -0
  251. package/src/profiler/ProfilerPanel.tsx +53 -0
  252. package/src/profiler/frame-debugger-store.ts +97 -0
  253. package/src/profiler/main-thread-busy.ts +90 -0
  254. package/src/react/design-time-react-mount.ts +782 -0
  255. package/src/react/dom-authoring-adapter.ts +764 -0
  256. package/src/react/pasteboard-materialize.ts +154 -0
  257. package/src/react/react-inspector-section.tsx +2496 -0
  258. package/src/react/react-world-authoring-adapter.ts +3795 -0
  259. package/src/react/story-documents/story-args-section.ts +19 -0
  260. package/src/react/story-documents/story-documents.tsx +1380 -0
  261. package/src/react/story-paint-bounds.ts +94 -0
  262. package/src/react/ui-board-document.tsx +101 -0
  263. package/src/react/ui-board-title.ts +9 -0
  264. package/src/react/ui-component-board.ts +72 -0
  265. package/src/services/audio-pose-guard.ts +81 -0
  266. package/src/services/game-audio-unlock.ts +48 -0
  267. package/src/state-watch/StateWatchPanel.tsx +535 -0
  268. package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
  269. package/src/three/authoring/constraint-inspector-section.tsx +189 -0
  270. package/src/three/authoring/design-time-renderer.ts +189 -0
  271. package/src/three/authoring/model-asset-inspector-section.css +41 -0
  272. package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
  273. package/src/three/authoring/oid-source-persistence.ts +557 -0
  274. package/src/three/authoring/r3f-design-session.ts +1174 -0
  275. package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
  276. package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
  277. package/src/three/authoring/spatial-audio-handles.ts +215 -0
  278. package/src/three/authoring/spatial-collider-handles.ts +229 -0
  279. package/src/three/authoring/spatial-joint-handles.ts +114 -0
  280. package/src/three/authoring/spatial-light-handles.ts +178 -0
  281. package/src/three/authoring/spatial-lod-handles.ts +93 -0
  282. package/src/three/authoring/spatial-particle-handles.ts +368 -0
  283. package/src/three/authoring/three-authoring-adapter.ts +1953 -0
  284. package/src/three/authoring/three-scene-identity.ts +19 -0
  285. package/src/three/authoring/three-spatial-handles.ts +161 -0
  286. package/src/three/authoring/typed-three-inspector.ts +528 -0
  287. package/src/three/component-verbs/extract-menu.ts +71 -0
  288. package/src/three/component-verbs/fork-menu.ts +78 -0
  289. package/src/three/component-verbs/internals-menu.ts +91 -0
  290. package/src/three/story-documents/three-story-documents.tsx +607 -0
  291. package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
  292. package/src/three/three-board/board-framing.ts +412 -0
  293. package/src/three/three-board/board-layout.ts +401 -0
  294. package/src/three/three-board/board-scene.ts +901 -0
  295. package/src/three/three-board/three-component-board.ts +62 -0
  296. package/src/xstate/XStateBehaviorSection.tsx +130 -0
  297. package/src/xstate/XStateMachineInspector.tsx +566 -0
  298. package/src/xstate/character-animation-machine.fixture.ts +74 -0
  299. package/src/xstate/live-behaviors.ts +106 -0
  300. package/src/xstate/use-live-actor-state.ts +44 -0
  301. package/src/xstate/xstate-graph.ts +235 -0
  302. 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
+ }