@downdraft/engine 0.2.0 → 0.3.1
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/app/src/renderer/bootstrap.ts +0 -6
- package/app/src/renderer/feature-log.ts +1 -9
- package/app/src/renderer/mcp-harness.ts +8 -11
- package/app/src/renderer/standard-automation-tools.ts +8 -145
- package/core/src/index.ts +2 -2
- package/core/src/module/host.ts +3 -6
- package/core/src/module/index.ts +1 -1
- package/core/src/module/module.ts +4 -47
- package/core/src/physics/interface.ts +5 -0
- package/core/src/platform/render-surface.ts +13 -0
- package/core/src/platform/rpc.ts +4 -1
- package/core/src/render/canvas-resize-watcher.ts +1 -10
- package/core/src/render/game-renderer.ts +164 -79
- package/core/src/render/index.ts +0 -2
- package/core/src/render/input-manager.ts +7 -7
- package/core/src/render/renderer-module-host.ts +2 -4
- package/core/src/sab/grid-buffer.ts +55 -2
- package/core/src/sab/record-buffer.ts +48 -5
- package/core/src/sab/sim-channel.ts +181 -17
- package/core/src/sab/slot-buffer.ts +55 -1
- package/core/src/sab/types.ts +11 -0
- package/core/src/telemetry/gpu-resource-tracker.ts +24 -0
- package/core/src/ui-font-scale.ts +5 -38
- package/core/src/util/index.ts +1 -0
- package/core/src/util/session-timer.ts +23 -0
- package/core/src/worker/base-worker-host.ts +2 -1
- package/core/src/worker/buffer-sync.ts +20 -13
- package/core/src/worker/rpc.ts +119 -5
- package/core/src/worker/sim-worker-base.ts +5 -2
- package/core/src/worker/task-pool.ts +4 -0
- package/dist/.dd-manifest.json +893 -908
- package/dist/app/src/renderer/bootstrap.js +0 -4
- package/dist/app/src/renderer/feature-log.js +1 -10
- package/dist/app/src/renderer/mcp-harness.js +8 -11
- package/dist/app/src/renderer/standard-automation-tools.js +6 -138
- package/dist/core/src/index.js +2 -2
- package/dist/core/src/module/host.js +3 -6
- package/dist/core/src/platform/rpc.js +2 -1
- package/dist/core/src/render/canvas-resize-watcher.js +1 -10
- package/dist/core/src/render/game-renderer.js +166 -74
- package/dist/core/src/render/index.js +0 -1
- package/dist/core/src/render/input-manager.js +7 -7
- package/dist/core/src/render/renderer-module-host.js +2 -4
- package/dist/core/src/sab/grid-buffer.js +53 -1
- package/dist/core/src/sab/record-buffer.js +49 -5
- package/dist/core/src/sab/sim-channel.js +173 -17
- package/dist/core/src/sab/slot-buffer.js +54 -1
- package/dist/core/src/telemetry/gpu-resource-tracker.js +26 -0
- package/dist/core/src/ui-font-scale.js +5 -38
- package/dist/core/src/util/index.js +1 -0
- package/dist/core/src/util/session-timer.js +22 -0
- package/dist/core/src/worker/base-worker-host.js +2 -1
- package/dist/core/src/worker/buffer-sync.js +16 -9
- package/dist/core/src/worker/rpc.js +105 -8
- package/dist/core/src/worker/sim-worker-base.js +5 -2
- package/dist/core/src/worker/task-pool.js +5 -0
- package/dist/libraries/blitz-ui/src/native-module.js +8 -1
- package/dist/libraries/devtools/src/backend.js +35 -9
- package/dist/libraries/devtools/src/collectors.js +73 -22
- package/dist/libraries/devtools/src/native-providers.js +0 -2
- package/dist/libraries/entities/src/model-renderer.js +87 -27
- package/dist/libraries/entities/src/shaders/model.wgsl +13 -4
- package/dist/libraries/networking/src/index.js +1 -0
- package/dist/libraries/networking/src/ws-server.js +387 -0
- package/dist/libraries/physics-rapier/src/api.js +15 -0
- package/dist/libraries/physics-rapier/src/backend.js +15 -0
- package/dist/libraries/physics-rapier/src/rapier-backend.js +5 -0
- package/dist/libraries/postfx/src/post-process-stack.js +1 -3
- package/dist/libraries/sand/src/materials.js +2 -32
- package/dist/libraries/sand/src/sand-world.js +0 -3
- package/dist/modules/devtools/src/api.js +8 -23
- package/dist/modules/devtools/src/blitz/fonts.js +75 -0
- package/dist/modules/devtools/src/blitz/host.js +124 -30
- package/dist/modules/devtools/src/blitz/panels/console.js +19 -7
- package/dist/modules/devtools/src/blitz/panels/elements.js +217 -28
- package/dist/modules/devtools/src/blitz/panels/gpu.js +5 -2
- package/dist/modules/devtools/src/blitz/panels/performance.js +26 -12
- package/dist/modules/devtools/src/blitz/panels/snapshot.js +31 -8
- package/dist/modules/devtools/src/blitz/panels/types.js +2 -1
- package/dist/modules/devtools/src/blitz/theme.js +26 -4
- package/dist/modules/devtools/src/data-bridge.js +0 -11
- package/dist/modules/devtools/src/index.js +0 -6
- package/dist/modules/devtools/src/init.js +8 -42
- package/dist/modules/devtools/src/scene-inspector.js +1 -4
- package/dist/modules/devtools/src/worker-sync.js +3 -11
- package/dist/modules/html-ui/src/doc-backend.js +52 -3
- package/dist/modules/html-ui/src/host.js +6 -3
- package/dist/test/src/vision-test.js +1 -3
- package/libraries/blitz-ui/native/Cargo.toml +6 -0
- package/libraries/blitz-ui/native-osr/Cargo.toml +3 -1
- package/libraries/blitz-ui/src/native-module.ts +8 -1
- package/libraries/devtools/src/backend.ts +38 -12
- package/libraries/devtools/src/collectors.ts +75 -22
- package/libraries/devtools/src/native-providers.ts +0 -2
- package/libraries/entities/src/model-renderer.ts +88 -27
- package/libraries/entities/src/shaders/model.wgsl +13 -4
- package/libraries/networking/src/index.ts +4 -0
- package/libraries/networking/src/ws-server.ts +397 -0
- package/libraries/physics-rapier/src/api.ts +15 -0
- package/libraries/physics-rapier/src/backend.ts +15 -0
- package/libraries/physics-rapier/src/rapier-backend.ts +6 -0
- package/libraries/postfx/src/post-process-stack.ts +1 -2
- package/libraries/sand/src/materials.ts +2 -32
- package/libraries/sand/src/sand-world.ts +0 -4
- package/modules/devtools/README.md +1 -3
- package/modules/devtools/src/api.ts +10 -34
- package/modules/devtools/src/blitz/fonts.ts +78 -0
- package/modules/devtools/src/blitz/host.ts +107 -31
- package/modules/devtools/src/blitz/panels/console.ts +20 -8
- package/modules/devtools/src/blitz/panels/elements.ts +212 -31
- package/modules/devtools/src/blitz/panels/gpu.ts +4 -2
- package/modules/devtools/src/blitz/panels/performance.ts +24 -12
- package/modules/devtools/src/blitz/panels/snapshot.ts +30 -8
- package/modules/devtools/src/blitz/panels/types.ts +2 -1
- package/modules/devtools/src/blitz/theme.ts +26 -4
- package/modules/devtools/src/data-bridge.ts +0 -16
- package/modules/devtools/src/index.ts +1 -11
- package/modules/devtools/src/init.ts +8 -51
- package/modules/devtools/src/scene-inspector.ts +2 -6
- package/modules/devtools/src/types.ts +5 -90
- package/modules/devtools/src/worker-sync.ts +3 -13
- package/modules/html-ui/src/doc-backend.ts +67 -4
- package/modules/html-ui/src/host.ts +11 -3
- package/modules/html-ui/src/protocol.ts +1 -1
- package/package.json +4 -5
- package/test/src/vision-test.ts +2 -4
- package/core/src/render/sab-canvas-overlay.ts +0 -122
- package/dist/core/src/render/sab-canvas-overlay.js +0 -83
- package/dist/modules/devtools/src/debug-overlay.js +0 -181
- package/dist/modules/devtools/src/doctor-panel.js +0 -163
- package/dist/modules/devtools/src/label-overlay.js +0 -129
- package/dist/modules/devtools/src/material-stats-panel.js +0 -171
- package/dist/modules/devtools/src/sim-stats-panel.js +0 -250
- package/modules/devtools/src/debug-overlay.ts +0 -238
- package/modules/devtools/src/doctor-panel.ts +0 -182
- package/modules/devtools/src/label-overlay.ts +0 -154
- package/modules/devtools/src/material-stats-panel.ts +0 -188
- package/modules/devtools/src/sim-stats-panel.ts +0 -272
|
@@ -111,8 +111,6 @@ export interface BootstrapDevToolsOptions {
|
|
|
111
111
|
* Called after the renderer is initialized.
|
|
112
112
|
*/
|
|
113
113
|
createSimStatsProvider?: (renderer: any) => any;
|
|
114
|
-
/** DevTools panels (game-specific). Can be a factory or static array. */
|
|
115
|
-
panels?: any[] | ((renderer: any) => any[]);
|
|
116
114
|
/** Whether to enable scene inspector. Default: false. */
|
|
117
115
|
sceneInspector?: boolean;
|
|
118
116
|
/** Custom scene-inspector bridge class (extends BaseSceneInspector).
|
|
@@ -236,15 +234,11 @@ export async function bootstrapGame(opts: BootstrapGameOptions): Promise<void> {
|
|
|
236
234
|
if (!devtoolsMod) return;
|
|
237
235
|
const { initDevTools } = devtoolsMod;
|
|
238
236
|
const simStatsProvider = opts.devtools.createSimStatsProvider?.(renderer);
|
|
239
|
-
const panels = typeof opts.devtools.panels === "function"
|
|
240
|
-
? opts.devtools.panels(renderer)
|
|
241
|
-
: opts.devtools.panels ?? [];
|
|
242
237
|
const workerHosts = typeof opts.devtools.workerHosts === "function"
|
|
243
238
|
? opts.devtools.workerHosts(renderer)
|
|
244
239
|
: opts.devtools.workerHosts;
|
|
245
240
|
const devtoolsBridge = await importOrDead(initDevTools(renderer, {
|
|
246
241
|
simStatsProvider,
|
|
247
|
-
panels,
|
|
248
242
|
sceneInspector: opts.devtools.sceneInspector,
|
|
249
243
|
bridgeClass: opts.devtools.bridgeClass,
|
|
250
244
|
workerHosts,
|
|
@@ -86,20 +86,12 @@ export function collectRendererFeatureLog(opts: RendererFeatureLogOptions): Feat
|
|
|
86
86
|
if (nav?.deviceMemory) data.mem = `${nav.deviceMemory}G`;
|
|
87
87
|
} catch { /* navigator unavailable */ }
|
|
88
88
|
|
|
89
|
-
// --- SharedArrayBuffer
|
|
90
|
-
// there is no origin model on the native host) ---
|
|
89
|
+
// --- SharedArrayBuffer ---
|
|
91
90
|
try {
|
|
92
91
|
data.sab = typeof SharedArrayBuffer !== "undefined" ? 1 : 0;
|
|
93
92
|
} catch {
|
|
94
93
|
data.sab = 0;
|
|
95
94
|
}
|
|
96
|
-
if (getHostCapabilities().hasDom) {
|
|
97
|
-
try {
|
|
98
|
-
data.coi = (self as any).crossOriginIsolated ? 1 : 0;
|
|
99
|
-
} catch {
|
|
100
|
-
data.coi = 0;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
95
|
|
|
104
96
|
// --- Worker load status (best-effort; na = not checked) ---
|
|
105
97
|
data.wk = "na";
|
|
@@ -120,17 +120,14 @@ export function createMcpHarness(opts: McpHarnessOptions): void {
|
|
|
120
120
|
// Games import them instead of duplicating the implementation.
|
|
121
121
|
|
|
122
122
|
/** Convert a Blob to a base64 string (without the data: prefix). */
|
|
123
|
-
export function blobToBase64(blob: Blob): Promise<string> {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
reader.onerror = reject;
|
|
132
|
-
reader.readAsDataURL(blob);
|
|
133
|
-
});
|
|
123
|
+
export async function blobToBase64(blob: Blob): Promise<string> {
|
|
124
|
+
const bytes = new Uint8Array(await blob.arrayBuffer());
|
|
125
|
+
let bin = "";
|
|
126
|
+
const CHUNK = 0x8000;
|
|
127
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
128
|
+
bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
129
|
+
}
|
|
130
|
+
return btoa(bin);
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
/** MCP result helper: wrap data as a JSON content array. */
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
// });
|
|
27
27
|
// ============================================================================
|
|
28
28
|
|
|
29
|
-
import {
|
|
29
|
+
import { KEY, type RenderSurface } from "@downdraft/engine";
|
|
30
30
|
import type { InspectableRegistry } from "@downdraft/engine/libraries/inspectable";
|
|
31
31
|
import { createLogger } from "@downdraft/engine/util/logger";
|
|
32
32
|
import { downdraft } from "./index";
|
|
@@ -108,14 +108,6 @@ export interface StandardAutomationContext {
|
|
|
108
108
|
/** The game render surface (layer 0). Used by capture_screenshot + dispatch_click. */
|
|
109
109
|
surface: RenderSurface | (() => RenderSurface | null);
|
|
110
110
|
|
|
111
|
-
/**
|
|
112
|
-
* Extra canvases to composite between the game canvas and the DOM overlay
|
|
113
|
-
* in full-page screenshots (e.g. a 2D tile canvas). DOM hosts only —
|
|
114
|
-
* native capture returns the composited swapchain via captureFrame().
|
|
115
|
-
* Default: none.
|
|
116
|
-
*/
|
|
117
|
-
extraLayers?: () => HTMLCanvasElement[];
|
|
118
|
-
|
|
119
111
|
/**
|
|
120
112
|
* Render one frame when the render loop is paused (deterministic mode), so
|
|
121
113
|
* screenshots reflect current state. Called by capture_screenshot when the
|
|
@@ -422,20 +414,13 @@ export function createStandardAutomationTools(ctx: StandardAutomationContext): M
|
|
|
422
414
|
const rect = surface.getBoundingClientRect();
|
|
423
415
|
const clientX = rect.left + x;
|
|
424
416
|
const clientY = rect.top + y;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
//
|
|
428
|
-
//
|
|
429
|
-
const target = hasDom
|
|
430
|
-
? (document.elementFromPoint(clientX, clientY) ?? (surface as unknown as HTMLElement))
|
|
431
|
-
: (surface as unknown as EventTarget);
|
|
432
|
-
// MiniEventTarget (native event polyfill) has no capture/bubble —
|
|
433
|
-
// mirror real propagation by also dispatching on window. In a real
|
|
434
|
-
// DOM this would double-fire, so only do it under the polyfill.
|
|
435
|
-
const isNativeDom = !hasDom;
|
|
417
|
+
// There is no DOM tree — the surface itself is the input target.
|
|
418
|
+
const target = surface as unknown as EventTarget;
|
|
419
|
+
// MiniEventTarget (native event target) has no capture/bubble —
|
|
420
|
+
// mirror DOM propagation by also dispatching on window.
|
|
436
421
|
const fire = (ev: Event) => {
|
|
437
422
|
target.dispatchEvent(ev);
|
|
438
|
-
|
|
423
|
+
(window as any).dispatchEvent?.(ev);
|
|
439
424
|
};
|
|
440
425
|
if (type === "wheel") {
|
|
441
426
|
const deltaX = (params.deltaX as number) ?? 0;
|
|
@@ -474,10 +459,10 @@ export function createStandardAutomationTools(ctx: StandardAutomationContext): M
|
|
|
474
459
|
if (pointerType) fireOne(pointerType, true, buttons);
|
|
475
460
|
fireOne(type, false, buttons);
|
|
476
461
|
}
|
|
477
|
-
const targetEl = target as
|
|
462
|
+
const targetEl = target as { tagName?: string; id?: string; className?: unknown };
|
|
478
463
|
return jsonResult({
|
|
479
464
|
dispatched: true, x, y, type,
|
|
480
|
-
targetTag: targetEl.tagName ??
|
|
465
|
+
targetTag: targetEl.tagName ?? targetEl.id ?? "surface",
|
|
481
466
|
targetClass: targetEl.className?.toString().slice(0, 80),
|
|
482
467
|
});
|
|
483
468
|
},
|
|
@@ -617,128 +602,6 @@ export function createStandardAutomationTools(ctx: StandardAutomationContext): M
|
|
|
617
602
|
});
|
|
618
603
|
}
|
|
619
604
|
|
|
620
|
-
// ── get_element_bounds ──
|
|
621
|
-
if (want("get_element_bounds")) {
|
|
622
|
-
tools.push({
|
|
623
|
-
def: {
|
|
624
|
-
name: "get_element_bounds",
|
|
625
|
-
description: "Get the bounding box of a DOM element matching a CSS selector. Returns {x, y, width, height, top, left, bottom, right} or found:false.",
|
|
626
|
-
inputSchema: {
|
|
627
|
-
type: "object",
|
|
628
|
-
properties: { selector: { type: "string", description: "CSS selector" } },
|
|
629
|
-
required: ["selector"],
|
|
630
|
-
},
|
|
631
|
-
},
|
|
632
|
-
handler: (params: Record<string, unknown>) => {
|
|
633
|
-
const selector = params.selector as string;
|
|
634
|
-
try {
|
|
635
|
-
const el = document.querySelector(selector);
|
|
636
|
-
if (!el) return jsonResult({ found: false, selector });
|
|
637
|
-
const rect = el.getBoundingClientRect();
|
|
638
|
-
return jsonResult({
|
|
639
|
-
found: true, selector,
|
|
640
|
-
x: rect.x, y: rect.y, width: rect.width, height: rect.height,
|
|
641
|
-
top: rect.top, left: rect.left, bottom: rect.bottom, right: rect.right,
|
|
642
|
-
});
|
|
643
|
-
} catch (e) {
|
|
644
|
-
return errorResult(`Failed to query selector "${selector}": ${(e as Error).message}`);
|
|
645
|
-
}
|
|
646
|
-
},
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// ── inspect_dom ──
|
|
651
|
-
if (want("inspect_dom")) {
|
|
652
|
-
tools.push({
|
|
653
|
-
def: {
|
|
654
|
-
name: "inspect_dom",
|
|
655
|
-
description: "Inspect the main thread's DOM for debugging CSS/layout issues. 'stylesheets' lists all <style>/<link> tags; 'element' gets details for a selector.",
|
|
656
|
-
inputSchema: {
|
|
657
|
-
type: "object",
|
|
658
|
-
properties: {
|
|
659
|
-
action: { type: "string", enum: ["stylesheets", "element"] },
|
|
660
|
-
selector: { type: "string", description: "CSS selector (for action='element')" },
|
|
661
|
-
},
|
|
662
|
-
required: ["action"],
|
|
663
|
-
},
|
|
664
|
-
},
|
|
665
|
-
handler: (params: Record<string, unknown>) => {
|
|
666
|
-
if (!getHostCapabilities().hasDom) {
|
|
667
|
-
return errorResult("inspect_dom requires a real DOM — the native host's document is a canvas-compat facade, not a DOM tree");
|
|
668
|
-
}
|
|
669
|
-
const action = params.action as string;
|
|
670
|
-
if (action === "stylesheets") {
|
|
671
|
-
const styles = Array.from(document.querySelectorAll("style, link[rel='stylesheet']")).map((el) => {
|
|
672
|
-
const tag = el.tagName.toLowerCase();
|
|
673
|
-
if (tag === "style") {
|
|
674
|
-
const text = el.textContent ?? "";
|
|
675
|
-
return { tag, length: text.length, preview: text.slice(0, 200) };
|
|
676
|
-
}
|
|
677
|
-
return { tag, href: (el as HTMLLinkElement).href };
|
|
678
|
-
});
|
|
679
|
-
return jsonResult({ stylesheets: styles, count: styles.length });
|
|
680
|
-
}
|
|
681
|
-
if (action === "element" && params.selector) {
|
|
682
|
-
const el = document.querySelector(params.selector as string);
|
|
683
|
-
if (!el) return jsonResult({ found: false, selector: params.selector });
|
|
684
|
-
const cs = getComputedStyle(el);
|
|
685
|
-
return jsonResult({
|
|
686
|
-
found: true,
|
|
687
|
-
selector: params.selector,
|
|
688
|
-
tagName: el.tagName,
|
|
689
|
-
className: el.className,
|
|
690
|
-
id: el.id,
|
|
691
|
-
style: {
|
|
692
|
-
width: cs.width, height: cs.height,
|
|
693
|
-
maxWidth: cs.maxWidth, maxHeight: cs.maxHeight,
|
|
694
|
-
display: cs.display, position: cs.position,
|
|
695
|
-
top: cs.top, left: cs.left, overflow: cs.overflow,
|
|
696
|
-
},
|
|
697
|
-
bounds: el.getBoundingClientRect().toJSON(),
|
|
698
|
-
childCount: el.children.length,
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
return errorResult("Invalid action or missing selector");
|
|
702
|
-
},
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// ── get_element_style ──
|
|
707
|
-
if (want("get_element_style")) {
|
|
708
|
-
tools.push({
|
|
709
|
-
def: {
|
|
710
|
-
name: "get_element_style",
|
|
711
|
-
description: "Get computed style of a DOM element matching a CSS selector. Pass properties[] to read specific CSS properties.",
|
|
712
|
-
inputSchema: {
|
|
713
|
-
type: "object",
|
|
714
|
-
properties: {
|
|
715
|
-
selector: { type: "string", description: "CSS selector" },
|
|
716
|
-
properties: { type: "array", items: { type: "string" }, description: "CSS properties to read" },
|
|
717
|
-
},
|
|
718
|
-
required: ["selector"],
|
|
719
|
-
},
|
|
720
|
-
},
|
|
721
|
-
handler: (params: Record<string, unknown>) => {
|
|
722
|
-
if (!getHostCapabilities().hasDom) {
|
|
723
|
-
return errorResult("get_element_style requires a real DOM — no computed styles on the native host");
|
|
724
|
-
}
|
|
725
|
-
const selector = params.selector as string;
|
|
726
|
-
const props = (params.properties as string[]) ?? [];
|
|
727
|
-
try {
|
|
728
|
-
const el = document.querySelector(selector);
|
|
729
|
-
if (!el) return jsonResult({ found: false, selector });
|
|
730
|
-
const cs = getComputedStyle(el);
|
|
731
|
-
const result: Record<string, unknown> = { found: true, selector };
|
|
732
|
-
props.forEach((p) => { result[p] = cs.getPropertyValue(p);; });
|
|
733
|
-
result._className = el.className;
|
|
734
|
-
return jsonResult(result);
|
|
735
|
-
} catch (e) {
|
|
736
|
-
return errorResult(`Failed to query style for "${selector}": ${(e as Error).message}`);
|
|
737
|
-
}
|
|
738
|
-
},
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
|
|
742
605
|
// ── inspectable_list / inspectable_get / inspectable_set ──
|
|
743
606
|
const inspectables = ctx.inspectables;
|
|
744
607
|
if (want("inspectable_list") && inspectables) {
|
package/core/src/index.ts
CHANGED
|
@@ -56,7 +56,7 @@ export type { MultiInputChannelInstance, MultiInputChannelOptions } from "./sab/
|
|
|
56
56
|
export { INP, INP_FLAG, INP_HDR, INPUT_MAGIC, INPUT_VERSION, InputBufferReader, InputBufferWriter, InputChannel, KEY, MAX_INPUT_PLAYERS } from "./sab/game-input";
|
|
57
57
|
|
|
58
58
|
// Sim SAB Channel
|
|
59
|
-
export { allocateInputBuffer, allocateSimBuffer, ENT, MAX_ENTITIES, MAX_PLAYERS, PLR, PLR_FLAG, SIM_ENTITY_SLOT_SIZE, SIM_HDR, SIM_MAGIC, SIM_PLAYER_SLOT_SIZE, SIM_VERSION, SimBufferReader, SimBufferWriter, SimChannel } from "./sab/sim-channel";
|
|
59
|
+
export { allocateInputBuffer, allocateSimBuffer, ENT, MAX_ENTITIES, MAX_PLAYERS, PLR, PLR_FLAG, PREV, SIM_ENTITY_SLOT_SIZE, SIM_HDR, SIM_MAGIC, SIM_PLAYER_SLOT_SIZE, SIM_VERSION, SimBufferReader, SimBufferWriter, SimChannel } from "./sab/sim-channel";
|
|
60
60
|
export type { PhysicsTimingData } from "./sab/sim-channel";
|
|
61
61
|
|
|
62
62
|
// SAB Framework
|
|
@@ -90,7 +90,7 @@ export { EntitySimWorkerHost } from "./worker/entity-sim-worker-host";
|
|
|
90
90
|
export type { EntitySimApi, EntitySimHostOptions } from "./worker/entity-sim-worker-host";
|
|
91
91
|
export { exposeProfilingApi, InstrumentedWorkerHost } from "./worker/instrumented-worker-host";
|
|
92
92
|
export type { InstrumentedWorkerHostOptions } from "./worker/instrumented-worker-host";
|
|
93
|
-
export { expose, exposeEvents, getWorkerHost, wrap } from "./worker/rpc";
|
|
93
|
+
export { expose, exposeBatchedEvents, exposeEvents, getWorkerHost, wrap } from "./worker/rpc";
|
|
94
94
|
export type { ExposeOptions, HostMessageHandler, WorkerApi, WorkerEventEmitter, WorkerHost, WorkerProxy } from "./worker/rpc";
|
|
95
95
|
export { createSimWorker } from "./worker/sim-worker-base";
|
|
96
96
|
export type { CreateSimWorkerOptions, SimAfterTicksContext, SimTickContext, SimWorkerControl, SimWorkerStats } from "./worker/sim-worker-base";
|
package/core/src/module/host.ts
CHANGED
|
@@ -47,8 +47,8 @@ export class ModuleHost implements ModuleContext {
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Inject the DevTools API. Called by the game bootstrap after
|
|
50
|
-
* `initDevTools()` so plugins can self-register
|
|
51
|
-
* `ctx.devtools.
|
|
50
|
+
* `initDevTools()` so plugins can self-register data feeds via
|
|
51
|
+
* `ctx.devtools.registerDataFeed(...)` during their `register()` lifecycle.
|
|
52
52
|
*/
|
|
53
53
|
setDevToolsAPI(api: ModuleDevToolsAPI): void {
|
|
54
54
|
this._devtools = api;
|
|
@@ -56,7 +56,7 @@ export class ModuleHost implements ModuleContext {
|
|
|
56
56
|
|
|
57
57
|
get devtools(): ModuleDevToolsAPI {
|
|
58
58
|
// Return the injected API, or a no-op stub if not set (so plugins that
|
|
59
|
-
// call ctx.devtools.
|
|
59
|
+
// call ctx.devtools.register*() don't crash if devtools isn't wired).
|
|
60
60
|
return this._devtools ?? NoopDevToolsAPI;
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -488,10 +488,7 @@ export class ModuleHost implements ModuleContext {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
// No-op DevTools API stub — used when setDevToolsAPI() hasn't been called.
|
|
491
|
-
// Plugins that call ctx.devtools.registerPanel() will silently no-op.
|
|
492
491
|
const NoopDevToolsAPI: ModuleDevToolsAPI = {
|
|
493
|
-
registerPanel: () => {},
|
|
494
|
-
registerOverlayToggle: () => {},
|
|
495
492
|
registerDataFeed: () => {},
|
|
496
493
|
registerCommand: () => {},
|
|
497
494
|
registerSABStat: () => {},
|
package/core/src/module/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { buildCrossThreadReport, crossThreadToken } from "./cross-thread";
|
|
|
3
3
|
export type { CrossThreadReport, CrossThreadToken, ModuleThreadInfo, ThreadTag } from "./cross-thread";
|
|
4
4
|
export { DiagnosticError, isStrict, setStrict } from "./diagnostics";
|
|
5
5
|
export { ModuleHost } from "./host";
|
|
6
|
-
export type {
|
|
6
|
+
export type { Module, ModuleContext, ModuleDevToolsAPI, SABChannel } from "./module";
|
|
7
7
|
export { ModuleRegistry } from "./registry";
|
|
8
8
|
export type {
|
|
9
9
|
CameraControllerLike,
|
|
@@ -7,59 +7,16 @@ export interface SABChannel {
|
|
|
7
7
|
buffer: SharedArrayBuffer;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Panel registration payload for `ctx.devtools.registerPanel()`.
|
|
12
|
-
* Structurally compatible with `IDevToolsPanelExtension` from
|
|
13
|
-
* `@downdraft/engine/modules/devtools` — core declares the minimal shape
|
|
14
|
-
* so it doesn't depend on the devtools module (layering).
|
|
15
|
-
*/
|
|
16
|
-
export interface DevToolsPanelRegistration {
|
|
17
|
-
/** Unique id for this extension (e.g. "debug-info", "game-panel"). */
|
|
18
|
-
id: string;
|
|
19
|
-
/** Tab label shown in the tab bar. */
|
|
20
|
-
tabLabel: string;
|
|
21
|
-
/** Tab tooltip. */
|
|
22
|
-
tabTooltip?: string;
|
|
23
|
-
/** Order/priority for tab placement. Core tabs: 0-100, game tabs: 100+. Default: 100. */
|
|
24
|
-
order?: number;
|
|
25
|
-
/** __sceneInspector methods that must exist for this tab to be shown. */
|
|
26
|
-
requiredMethods?: string[];
|
|
27
|
-
/** HTML content for the panel body. */
|
|
28
|
-
html: string;
|
|
29
|
-
/** CSS to inject into the panel document. */
|
|
30
|
-
css?: string;
|
|
31
|
-
/** JS to eval in the panel context. */
|
|
32
|
-
script?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Overlay toggle registration payload for `ctx.devtools.registerOverlayToggle()`.
|
|
37
|
-
* Structurally compatible with `IDevToolsOverlayToggle` from
|
|
38
|
-
* `@downdraft/engine/modules/devtools`.
|
|
39
|
-
*/
|
|
40
|
-
export interface DevToolsOverlayToggleRegistration {
|
|
41
|
-
/** Unique id (e.g. "chunk-grid", "vel-arrows"). */
|
|
42
|
-
id: string;
|
|
43
|
-
/** Label shown next to the checkbox. */
|
|
44
|
-
label: string;
|
|
45
|
-
/** __sceneInspector methods that must exist for this toggle to be shown. */
|
|
46
|
-
requiredMethods?: string[];
|
|
47
|
-
/** JS to eval in the panel context. */
|
|
48
|
-
script?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
10
|
/**
|
|
52
11
|
* DevTools registration surface exposed on ModuleContext.
|
|
53
|
-
* Plugins call `ctx.devtools.
|
|
54
|
-
* debug
|
|
12
|
+
* Plugins call `ctx.devtools.registerDataFeed(...)` etc. to self-register
|
|
13
|
+
* debug data. The same interface works in both main and worker realms.
|
|
55
14
|
*
|
|
56
15
|
* The actual implementation is provided by @downdraft/engine/modules/devtools via
|
|
57
16
|
* the `devtools` singleton. Core defines the interface; the host injects
|
|
58
17
|
* the concrete object at construction time (see ModuleHost.setDevToolsAPI).
|
|
59
18
|
*/
|
|
60
19
|
export interface ModuleDevToolsAPI {
|
|
61
|
-
registerPanel(ext: DevToolsPanelRegistration): void;
|
|
62
|
-
registerOverlayToggle(toggle: DevToolsOverlayToggleRegistration): void;
|
|
63
20
|
registerDataFeed(name: string, fn: () => unknown, writeRateHz?: number): void;
|
|
64
21
|
registerCommand(name: string, fn: (...args: unknown[]) => unknown): void;
|
|
65
22
|
registerSABStat(name: string, offset: number, type: "u32" | "f32" | "i32"): void;
|
|
@@ -95,8 +52,8 @@ export interface ModuleContext {
|
|
|
95
52
|
registerMigration(fromVersion: number, fn: (data: unknown) => unknown): void;
|
|
96
53
|
onDispose(fn: () => void): void;
|
|
97
54
|
/**
|
|
98
|
-
* DevTools registration surface. Plugins self-register
|
|
99
|
-
*
|
|
55
|
+
* DevTools registration surface. Plugins self-register data feeds and
|
|
56
|
+
* commands via `ctx.devtools.register*(...)`.
|
|
100
57
|
* Works in both sim (worker) and renderer (main) plugin contexts.
|
|
101
58
|
*/
|
|
102
59
|
readonly devtools: ModuleDevToolsAPI;
|
|
@@ -399,6 +399,11 @@ export interface PhysicsBackend {
|
|
|
399
399
|
/** Set integration timestep on a realm. */
|
|
400
400
|
setIntegrationDt(realmId: number, dt: number): void;
|
|
401
401
|
|
|
402
|
+
/** Change a live realm's gravity vector. Optional — backends that can't
|
|
403
|
+
* mutate realm gravity simply omit it (callers fall back to per-body
|
|
404
|
+
* gravityScale). */
|
|
405
|
+
setRealmGravity?(realmId: number, gravity: [number, number, number]): void;
|
|
406
|
+
|
|
402
407
|
/** Per-realm sleep thresholds for graceful degradation (load shedding first line). */
|
|
403
408
|
setSleepThresholds(realmId: number, linearThreshold: number, angularThreshold: number): void;
|
|
404
409
|
/** Per-realm solver iteration count (Rapier `IntegrationParameters.numSolverIterations`). */
|
|
@@ -29,6 +29,15 @@ export interface RenderSurfaceContext {
|
|
|
29
29
|
* the swapchain can't produce a frame (resize in flight, occluded
|
|
30
30
|
* window) — callers must treat null as "skip this frame". */
|
|
31
31
|
getCurrentTexture(): GPUTexture | null;
|
|
32
|
+
/** The texture already acquired for the in-flight frame, if any —
|
|
33
|
+
* WITHOUT acquiring one. Null when nothing has acquired this frame.
|
|
34
|
+
* End-of-frame overlay passes (UI composites) should join a frame via
|
|
35
|
+
* peek rather than acquire: on hosts with frame retention (native),
|
|
36
|
+
* acquiring here fabricates a frame the game deliberately skipped and
|
|
37
|
+
* presents the overlay over a blank swapchain image. Optional — DOM
|
|
38
|
+
* GPUCanvasContext has no equivalent; callers fall back to
|
|
39
|
+
* getCurrentTexture() where acquire is idempotent within a frame. */
|
|
40
|
+
peekCurrentTexture?(): GPUTexture | null;
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
/** Listener signature — the native event target dispatches plain event
|
|
@@ -74,4 +83,8 @@ export interface RenderSurface {
|
|
|
74
83
|
* concept doesn't exist; presence is the feature check. */
|
|
75
84
|
requestPointerLock?(): Promise<void> | void;
|
|
76
85
|
exitPointerLock?(): void;
|
|
86
|
+
/** Current pointer-lock state. Optional — read instead of the DOM's
|
|
87
|
+
* `document.pointerLockElement` (there is no DOM on native). The surface
|
|
88
|
+
* also dispatches a `"pointerlockchange"` event on transitions. */
|
|
89
|
+
readonly pointerLocked?: boolean;
|
|
77
90
|
}
|
package/core/src/platform/rpc.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
import { untrackedTimeout } from "../util/session-timer";
|
|
3
|
+
|
|
1
4
|
export type RPCMessageType = "request" | "response" | "event";
|
|
2
5
|
|
|
3
6
|
export interface RPCMessage {
|
|
@@ -34,7 +37,7 @@ export class RPC {
|
|
|
34
37
|
}
|
|
35
38
|
const id = this.messageIdCounter = (this.messageIdCounter + 1) % 0xFFFFFFFF;
|
|
36
39
|
return new Promise((resolve, reject) => {
|
|
37
|
-
const timer =
|
|
40
|
+
const timer = untrackedTimeout(() => {
|
|
38
41
|
if (this.pending.has(id)) {
|
|
39
42
|
this.pending.delete(id);
|
|
40
43
|
reject(new Error(`[RPC] Call to "${channel}" timed out after ${timeoutMs}ms`));
|
|
@@ -5,7 +5,6 @@ export interface CanvasResizeHandler {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export class CanvasResizeWatcher {
|
|
8
|
-
private observer: ResizeObserver | null = null;
|
|
9
8
|
private dpr = 1;
|
|
10
9
|
private onSurfaceResize = () => this.resize();
|
|
11
10
|
|
|
@@ -14,13 +13,7 @@ export class CanvasResizeWatcher {
|
|
|
14
13
|
private handler: CanvasResizeHandler,
|
|
15
14
|
) {
|
|
16
15
|
this.dpr = (typeof window !== "undefined" ? window.devicePixelRatio : 0) || 1;
|
|
17
|
-
//
|
|
18
|
-
// aren't Elements — they push a "resize" event instead. Both paths feed
|
|
19
|
-
// the same resize() so either host drives layout updates.
|
|
20
|
-
if (typeof ResizeObserver !== "undefined") {
|
|
21
|
-
this.observer = new ResizeObserver(() => this.resize());
|
|
22
|
-
this.observer.observe(canvas as unknown as Element);
|
|
23
|
-
}
|
|
16
|
+
// Native surfaces push a "resize" event when the backing store changes.
|
|
24
17
|
canvas.addEventListener("resize", this.onSurfaceResize);
|
|
25
18
|
this.resize();
|
|
26
19
|
}
|
|
@@ -39,8 +32,6 @@ export class CanvasResizeWatcher {
|
|
|
39
32
|
}
|
|
40
33
|
|
|
41
34
|
destroy(): void {
|
|
42
|
-
this.observer?.disconnect();
|
|
43
|
-
this.observer = null;
|
|
44
35
|
this.canvas.removeEventListener("resize", this.onSurfaceResize);
|
|
45
36
|
}
|
|
46
37
|
}
|