@downdraft/engine 0.3.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.
Files changed (82) hide show
  1. package/app/src/renderer/bootstrap.ts +0 -6
  2. package/app/src/renderer/feature-log.ts +1 -9
  3. package/app/src/renderer/mcp-harness.ts +8 -11
  4. package/app/src/renderer/standard-automation-tools.ts +8 -145
  5. package/core/src/module/host.ts +3 -6
  6. package/core/src/module/index.ts +1 -1
  7. package/core/src/module/module.ts +4 -47
  8. package/core/src/physics/interface.ts +5 -0
  9. package/core/src/platform/render-surface.ts +13 -0
  10. package/core/src/render/canvas-resize-watcher.ts +1 -10
  11. package/core/src/render/game-renderer.ts +40 -18
  12. package/core/src/render/index.ts +0 -2
  13. package/core/src/render/input-manager.ts +7 -7
  14. package/core/src/render/renderer-module-host.ts +2 -4
  15. package/core/src/ui-font-scale.ts +5 -38
  16. package/core/src/worker/rpc.ts +65 -0
  17. package/dist/.dd-manifest.json +859 -884
  18. package/dist/app/src/renderer/bootstrap.js +0 -4
  19. package/dist/app/src/renderer/feature-log.js +1 -10
  20. package/dist/app/src/renderer/mcp-harness.js +8 -11
  21. package/dist/app/src/renderer/standard-automation-tools.js +6 -138
  22. package/dist/core/src/module/host.js +3 -6
  23. package/dist/core/src/render/canvas-resize-watcher.js +1 -10
  24. package/dist/core/src/render/game-renderer.js +44 -18
  25. package/dist/core/src/render/index.js +0 -1
  26. package/dist/core/src/render/input-manager.js +7 -7
  27. package/dist/core/src/render/renderer-module-host.js +2 -4
  28. package/dist/core/src/ui-font-scale.js +5 -38
  29. package/dist/core/src/worker/rpc.js +59 -0
  30. package/dist/libraries/blitz-ui/src/native-module.js +8 -1
  31. package/dist/libraries/devtools/src/native-providers.js +0 -2
  32. package/dist/libraries/entities/src/model-renderer.js +87 -27
  33. package/dist/libraries/entities/src/shaders/model.wgsl +13 -4
  34. package/dist/libraries/networking/src/index.js +1 -0
  35. package/dist/libraries/networking/src/ws-server.js +387 -0
  36. package/dist/libraries/physics-rapier/src/api.js +15 -0
  37. package/dist/libraries/physics-rapier/src/backend.js +15 -0
  38. package/dist/libraries/physics-rapier/src/rapier-backend.js +5 -0
  39. package/dist/libraries/postfx/src/post-process-stack.js +1 -3
  40. package/dist/libraries/sand/src/materials.js +2 -32
  41. package/dist/libraries/sand/src/sand-world.js +0 -3
  42. package/dist/modules/devtools/src/api.js +8 -23
  43. package/dist/modules/devtools/src/data-bridge.js +0 -11
  44. package/dist/modules/devtools/src/index.js +0 -6
  45. package/dist/modules/devtools/src/init.js +8 -42
  46. package/dist/modules/devtools/src/scene-inspector.js +1 -4
  47. package/dist/modules/devtools/src/worker-sync.js +3 -11
  48. package/dist/test/src/vision-test.js +1 -3
  49. package/libraries/blitz-ui/src/native-module.ts +8 -1
  50. package/libraries/devtools/src/native-providers.ts +0 -2
  51. package/libraries/entities/src/model-renderer.ts +88 -27
  52. package/libraries/entities/src/shaders/model.wgsl +13 -4
  53. package/libraries/networking/src/index.ts +4 -0
  54. package/libraries/networking/src/ws-server.ts +397 -0
  55. package/libraries/physics-rapier/src/api.ts +15 -0
  56. package/libraries/physics-rapier/src/backend.ts +15 -0
  57. package/libraries/physics-rapier/src/rapier-backend.ts +6 -0
  58. package/libraries/postfx/src/post-process-stack.ts +1 -2
  59. package/libraries/sand/src/materials.ts +2 -32
  60. package/libraries/sand/src/sand-world.ts +0 -4
  61. package/modules/devtools/README.md +1 -3
  62. package/modules/devtools/src/api.ts +10 -34
  63. package/modules/devtools/src/data-bridge.ts +0 -16
  64. package/modules/devtools/src/index.ts +1 -11
  65. package/modules/devtools/src/init.ts +8 -51
  66. package/modules/devtools/src/scene-inspector.ts +2 -6
  67. package/modules/devtools/src/types.ts +5 -90
  68. package/modules/devtools/src/worker-sync.ts +3 -13
  69. package/package.json +4 -5
  70. package/test/src/vision-test.ts +2 -4
  71. package/core/src/render/sab-canvas-overlay.ts +0 -122
  72. package/dist/core/src/render/sab-canvas-overlay.js +0 -83
  73. package/dist/modules/devtools/src/debug-overlay.js +0 -181
  74. package/dist/modules/devtools/src/doctor-panel.js +0 -163
  75. package/dist/modules/devtools/src/label-overlay.js +0 -129
  76. package/dist/modules/devtools/src/material-stats-panel.js +0 -171
  77. package/dist/modules/devtools/src/sim-stats-panel.js +0 -250
  78. package/modules/devtools/src/debug-overlay.ts +0 -238
  79. package/modules/devtools/src/doctor-panel.ts +0 -182
  80. package/modules/devtools/src/label-overlay.ts +0 -154
  81. package/modules/devtools/src/material-stats-panel.ts +0 -188
  82. package/modules/devtools/src/sim-stats-panel.ts +0 -272
@@ -15,46 +15,13 @@
15
15
  let cachedSystemScale: number | null = null;
16
16
 
17
17
  /**
18
- * Detect the system/browser default font size and return a scale factor
19
- * relative to 16px (the browser default). Always returns >= 1.0.
20
- *
21
- * Example: if the user set their browser default font to 20px, this returns
22
- * 1.25. If the browser default is 16px (normal), this returns 1.0.
23
- *
24
- * Must be called on the main thread (requires `document`).
18
+ * Detect the system default font size and return a scale factor relative to
19
+ * 16px. On the native host there is no CSS engine and no system font-size
20
+ * preference to measure — always 1.
25
21
  */
26
22
  export function detectSystemFontScale(): number {
27
- if (cachedSystemScale !== null) return cachedSystemScale;
28
- // On the native host the document is a canvas-compat facade — there is no
29
- // CSS engine and no getComputedStyle, so there is no "system font size"
30
- // to measure. Always 1.
31
- if (typeof document === "undefined" || !document.body || typeof getComputedStyle !== "function") {
32
- cachedSystemScale = 1;
33
- return 1;
34
- }
35
-
36
- const el = document.createElement("div");
37
- el.style.fontSize = "1rem";
38
- el.style.position = "absolute";
39
- el.style.visibility = "hidden";
40
- el.style.lineHeight = "0";
41
- el.style.margin = "0";
42
- el.style.padding = "0";
43
- document.body.appendChild(el);
44
-
45
- let scale = 1;
46
- try {
47
- const size = parseFloat(getComputedStyle(el).fontSize);
48
- // 16px is the browser default. Only scale up (never down).
49
- scale = size > 0 ? Math.max(1, size / 16) : 1;
50
- } catch {
51
- scale = 1;
52
- } finally {
53
- document.body.removeChild(el);
54
- }
55
-
56
- cachedSystemScale = scale;
57
- return scale;
23
+ cachedSystemScale = 1;
24
+ return 1;
58
25
  }
59
26
 
60
27
  /**
@@ -97,6 +97,61 @@ interface EventBatchMessage {
97
97
  events: { kind: string; data: any }[];
98
98
  }
99
99
 
100
+ // --- Event channel stats ---
101
+ // Cheap counters the DOWNDRAFT_EVENT_STATS logger (and QA tooling) read to
102
+ // verify batching is actually engaging: emitted events vs postMessage posts
103
+ // on the worker side, messages vs delivered events on the host side.
104
+ const eventChannelStats = {
105
+ /** Worker side: emit() calls. */
106
+ emitted: 0,
107
+ /** Worker side: postMessage calls carrying events (batch or single). */
108
+ posts: 0,
109
+ /** Host side: messages received carrying events. */
110
+ messages: 0,
111
+ /** Host side: events delivered to listeners. */
112
+ events: 0,
113
+ /** Host side: largest observed __eventBatch. */
114
+ maxBatch: 0,
115
+ };
116
+
117
+ export function getEventChannelStats(): Readonly<typeof eventChannelStats> {
118
+ return eventChannelStats;
119
+ }
120
+
121
+ function envFlag(name: string): boolean {
122
+ try {
123
+ return (
124
+ (globalThis as { process?: { env?: Record<string, string> } }).process?.env?.[name] === "1"
125
+ );
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+
131
+ let statsLoggerStarted = false;
132
+ function startEventStatsLogger(): void {
133
+ if (statsLoggerStarted || !envFlag("DOWNDRAFT_EVENT_STATS")) return;
134
+ statsLoggerStarted = true;
135
+ let prev = { emitted: 0, posts: 0, messages: 0, events: 0 };
136
+ const t = setInterval(() => {
137
+ const s = eventChannelStats;
138
+ const d = {
139
+ emitted: s.emitted - prev.emitted,
140
+ posts: s.posts - prev.posts,
141
+ messages: s.messages - prev.messages,
142
+ events: s.events - prev.events,
143
+ };
144
+ prev = { emitted: s.emitted, posts: s.posts, messages: s.messages, events: s.events };
145
+ if (d.emitted || d.posts || d.messages || d.events) {
146
+ log.info(
147
+ "rpc-events",
148
+ `5s Δ emitted=${d.emitted} posts=${d.posts} msgs=${d.messages} events=${d.events} maxBatch=${s.maxBatch}`
149
+ );
150
+ }
151
+ }, 5000);
152
+ (t as { unref?: () => void }).unref?.();
153
+ }
154
+
100
155
  function isRpcRequest(msg: any): msg is RpcRequest {
101
156
  return msg && msg.__rpc === true && typeof msg.method === "string";
102
157
  }
@@ -148,8 +203,11 @@ export interface WorkerEventEmitter {
148
203
 
149
204
  export function exposeEvents(host?: WorkerHost): WorkerEventEmitter {
150
205
  const h = host ?? getWorkerHost();
206
+ startEventStatsLogger();
151
207
  return {
152
208
  emit: (kind: string, data: any) => {
209
+ eventChannelStats.emitted++;
210
+ eventChannelStats.posts++;
153
211
  h.postToHost({ __event: true, kind, data } as EventMessage);
154
212
  },
155
213
  };
@@ -171,16 +229,19 @@ export interface BatchedWorkerEventEmitter extends WorkerEventEmitter {
171
229
  */
172
230
  export function exposeBatchedEvents(host?: WorkerHost): BatchedWorkerEventEmitter {
173
231
  const h = host ?? getWorkerHost();
232
+ startEventStatsLogger();
174
233
  let queue: { kind: string; data: any }[] = [];
175
234
  let flushScheduled = false;
176
235
  const flush = () => {
177
236
  flushScheduled = false;
178
237
  if (queue.length === 0) return;
238
+ eventChannelStats.posts++;
179
239
  h.postToHost({ __eventBatch: true, events: queue } as EventBatchMessage);
180
240
  queue = [];
181
241
  };
182
242
  return {
183
243
  emit: (kind: string, data: any) => {
244
+ eventChannelStats.emitted++;
184
245
  queue.push({ kind, data });
185
246
  if (!flushScheduled) {
186
247
  flushScheduled = true;
@@ -217,6 +278,7 @@ export interface WrapOptions {
217
278
  const DEFAULT_TIMEOUT_MS = 30_000;
218
279
 
219
280
  export function wrap<T extends WorkerApi>(worker: AnyWorker, options?: WrapOptions): WorkerProxy<T> {
281
+ startEventStatsLogger();
220
282
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
221
283
  // A worker nobody terminates must not pin the runtime after the host's
222
284
  // window closes — in-flight RPCs still hold the loop via their timeout.
@@ -244,6 +306,9 @@ export function wrap<T extends WorkerApi>(worker: AnyWorker, options?: WrapOptio
244
306
 
245
307
  if (isEventMessage(msg) || isEventBatchMessage(msg)) {
246
308
  const events = isEventBatchMessage(msg) ? msg.events : [msg];
309
+ eventChannelStats.messages++;
310
+ eventChannelStats.events += events.length;
311
+ if (events.length > eventChannelStats.maxBatch) eventChannelStats.maxBatch = events.length;
247
312
  for (const ev of events) {
248
313
  for (const cb of eventListeners.values()) {
249
314
  try { cb(ev.kind, ev.data); } catch (err) {