@downdraft/engine 0.1.2-alpha.5 → 0.3.0

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 (97) hide show
  1. package/app/src/renderer/bootstrap.ts +1 -1
  2. package/app/src/renderer/feature-log.ts +1 -1
  3. package/core/src/index.ts +2 -2
  4. package/core/src/input/local-player-manager.ts +2 -2
  5. package/core/src/platform/rpc.ts +4 -1
  6. package/core/src/render/game-renderer.ts +127 -64
  7. package/core/src/sab/grid-buffer.ts +55 -2
  8. package/core/src/sab/record-buffer.ts +48 -5
  9. package/core/src/sab/sim-channel.ts +181 -17
  10. package/core/src/sab/slot-buffer.ts +55 -1
  11. package/core/src/sab/types.ts +11 -0
  12. package/core/src/telemetry/gpu-resource-tracker.ts +24 -0
  13. package/core/src/util/index.ts +1 -0
  14. package/core/src/util/logger.ts +2 -2
  15. package/core/src/util/session-timer.ts +23 -0
  16. package/core/src/worker/base-worker-host.ts +2 -1
  17. package/core/src/worker/buffer-sync.ts +20 -13
  18. package/core/src/worker/rpc.ts +54 -5
  19. package/core/src/worker/sim-worker-base.ts +6 -3
  20. package/core/src/worker/task-pool.ts +4 -0
  21. package/dist/.dd-manifest.json +879 -869
  22. package/dist/app/src/renderer/bootstrap.js +1 -1
  23. package/dist/app/src/renderer/feature-log.js +1 -1
  24. package/dist/core/src/index.js +2 -2
  25. package/dist/core/src/input/local-player-manager.js +2 -2
  26. package/dist/core/src/platform/rpc.js +2 -1
  27. package/dist/core/src/render/game-renderer.js +125 -59
  28. package/dist/core/src/sab/grid-buffer.js +53 -1
  29. package/dist/core/src/sab/record-buffer.js +49 -5
  30. package/dist/core/src/sab/sim-channel.js +173 -17
  31. package/dist/core/src/sab/slot-buffer.js +54 -1
  32. package/dist/core/src/telemetry/gpu-resource-tracker.js +26 -0
  33. package/dist/core/src/util/index.js +1 -0
  34. package/dist/core/src/util/logger.js +2 -2
  35. package/dist/core/src/util/session-timer.js +22 -0
  36. package/dist/core/src/worker/base-worker-host.js +2 -1
  37. package/dist/core/src/worker/buffer-sync.js +16 -9
  38. package/dist/core/src/worker/rpc.js +46 -8
  39. package/dist/core/src/worker/sim-worker-base.js +6 -3
  40. package/dist/core/src/worker/task-pool.js +5 -0
  41. package/dist/libraries/devtools/src/backend.js +35 -9
  42. package/dist/libraries/devtools/src/collectors.js +73 -22
  43. package/dist/libraries/gamepad/src/feed-input.js +1 -1
  44. package/dist/libraries/gamepad/src/hub.js +2 -2
  45. package/dist/libraries/gamepad/src/source-sab.js +2 -2
  46. package/dist/libraries/gamepad/src/sysfs-enrich.js +2 -2
  47. package/dist/libraries/gpu-kernels/src/kernel.js +1 -1
  48. package/dist/libraries/gpu-kernels/src/transpile.js +1 -1
  49. package/dist/libraries/html-ui-kit/src/nav/nav-controller.js +1 -1
  50. package/dist/libraries/html-ui-kit/src/nav/nav-input.js +2 -2
  51. package/dist/libraries/networking/src/discovery.js +8 -8
  52. package/dist/libraries/persistence/src/file-save-store.js +5 -5
  53. package/dist/libraries/persistence/src/opfs-save-store.js +2 -2
  54. package/dist/modules/controller-ui/src/index.js +1 -1
  55. package/dist/modules/devtools/src/blitz/fonts.js +75 -0
  56. package/dist/modules/devtools/src/blitz/host.js +127 -33
  57. package/dist/modules/devtools/src/blitz/panels/console.js +19 -7
  58. package/dist/modules/devtools/src/blitz/panels/elements.js +217 -28
  59. package/dist/modules/devtools/src/blitz/panels/gpu.js +5 -2
  60. package/dist/modules/devtools/src/blitz/panels/performance.js +28 -14
  61. package/dist/modules/devtools/src/blitz/panels/snapshot.js +31 -8
  62. package/dist/modules/devtools/src/blitz/panels/types.js +2 -1
  63. package/dist/modules/devtools/src/blitz/theme.js +26 -4
  64. package/dist/modules/html-ui/src/doc-backend.js +52 -3
  65. package/dist/modules/html-ui/src/host.js +6 -3
  66. package/dist/modules/html-ui/src/jsx-runtime.js +1 -1
  67. package/libraries/blitz-ui/native/Cargo.toml +6 -0
  68. package/libraries/blitz-ui/native-osr/Cargo.toml +3 -1
  69. package/libraries/devtools/src/backend.ts +38 -12
  70. package/libraries/devtools/src/collectors.ts +75 -22
  71. package/libraries/gamepad/src/feed-input.ts +1 -1
  72. package/libraries/gamepad/src/hub.ts +2 -2
  73. package/libraries/gamepad/src/source-sab.ts +2 -2
  74. package/libraries/gamepad/src/sysfs-enrich.ts +2 -2
  75. package/libraries/gpu-kernels/src/kernel.ts +2 -2
  76. package/libraries/gpu-kernels/src/transpile.ts +1 -1
  77. package/libraries/html-ui-kit/src/nav/nav-controller.ts +1 -1
  78. package/libraries/html-ui-kit/src/nav/nav-input.ts +2 -2
  79. package/libraries/networking/src/discovery.ts +8 -8
  80. package/libraries/persistence/src/file-save-store.ts +5 -5
  81. package/libraries/persistence/src/opfs-save-store.ts +2 -2
  82. package/libraries/physics-rapier/src/realm-worker-script.ts +1 -1
  83. package/modules/controller-ui/src/index.ts +1 -1
  84. package/modules/devtools/src/blitz/fonts.ts +78 -0
  85. package/modules/devtools/src/blitz/host.ts +110 -34
  86. package/modules/devtools/src/blitz/panels/console.ts +20 -8
  87. package/modules/devtools/src/blitz/panels/elements.ts +212 -31
  88. package/modules/devtools/src/blitz/panels/gpu.ts +4 -2
  89. package/modules/devtools/src/blitz/panels/performance.ts +26 -14
  90. package/modules/devtools/src/blitz/panels/snapshot.ts +30 -8
  91. package/modules/devtools/src/blitz/panels/types.ts +2 -1
  92. package/modules/devtools/src/blitz/theme.ts +26 -4
  93. package/modules/html-ui/src/doc-backend.ts +67 -4
  94. package/modules/html-ui/src/host.ts +11 -3
  95. package/modules/html-ui/src/jsx-runtime.ts +1 -1
  96. package/modules/html-ui/src/protocol.ts +1 -1
  97. package/package.json +2 -2
@@ -5,6 +5,7 @@
5
5
  // ============================================================================
6
6
 
7
7
  import { createLogger } from "../util/logger";
8
+ import { untrackedTimeout } from "../util/session-timer";
8
9
 
9
10
  const log = createLogger();
10
11
 
@@ -91,6 +92,11 @@ interface EventMessage {
91
92
  data: any;
92
93
  }
93
94
 
95
+ interface EventBatchMessage {
96
+ __eventBatch: true;
97
+ events: { kind: string; data: any }[];
98
+ }
99
+
94
100
  function isRpcRequest(msg: any): msg is RpcRequest {
95
101
  return msg && msg.__rpc === true && typeof msg.method === "string";
96
102
  }
@@ -103,6 +109,10 @@ function isEventMessage(msg: any): msg is EventMessage {
103
109
  return msg && msg.__event === true;
104
110
  }
105
111
 
112
+ function isEventBatchMessage(msg: any): msg is EventBatchMessage {
113
+ return msg && msg.__eventBatch === true;
114
+ }
115
+
106
116
  // --- Worker side: expose an API object ---
107
117
 
108
118
  export type WorkerApi = Record<string, (...args: any[]) => any>;
@@ -145,6 +155,42 @@ export function exposeEvents(host?: WorkerHost): WorkerEventEmitter {
145
155
  };
146
156
  }
147
157
 
158
+ export interface BatchedWorkerEventEmitter extends WorkerEventEmitter {
159
+ /** Post any queued events immediately. */
160
+ flush: () => void;
161
+ }
162
+
163
+ /**
164
+ * Event emitter that coalesces emit() calls into a single `__eventBatch`
165
+ * postMessage per macrotask. A sim emitting per-tick or per-entity events
166
+ * otherwise produces one structured clone + one main-thread task per event,
167
+ * and the task burst competes with rAF. Events emitted during a tick batch
168
+ * (the worker never yields to timers mid-tick) all land in one message;
169
+ * events emitted between ticks flush on the next macrotask, so latency for
170
+ * isolated events is unchanged.
171
+ */
172
+ export function exposeBatchedEvents(host?: WorkerHost): BatchedWorkerEventEmitter {
173
+ const h = host ?? getWorkerHost();
174
+ let queue: { kind: string; data: any }[] = [];
175
+ let flushScheduled = false;
176
+ const flush = () => {
177
+ flushScheduled = false;
178
+ if (queue.length === 0) return;
179
+ h.postToHost({ __eventBatch: true, events: queue } as EventBatchMessage);
180
+ queue = [];
181
+ };
182
+ return {
183
+ emit: (kind: string, data: any) => {
184
+ queue.push({ kind, data });
185
+ if (!flushScheduled) {
186
+ flushScheduled = true;
187
+ setTimeout(flush, 0);
188
+ }
189
+ },
190
+ flush,
191
+ };
192
+ }
193
+
148
194
  // --- Main side: wrap a worker as a typed async proxy ---
149
195
 
150
196
  type AnyWorker = {
@@ -196,10 +242,13 @@ export function wrap<T extends WorkerApi>(worker: AnyWorker, options?: WrapOptio
196
242
  return;
197
243
  }
198
244
 
199
- if (isEventMessage(msg)) {
200
- for (const cb of eventListeners.values()) {
201
- try { cb(msg.kind, msg.data); } catch (err) {
202
- log.error("worker-proxy", `Event listener error: ${err}`);
245
+ if (isEventMessage(msg) || isEventBatchMessage(msg)) {
246
+ const events = isEventBatchMessage(msg) ? msg.events : [msg];
247
+ for (const ev of events) {
248
+ for (const cb of eventListeners.values()) {
249
+ try { cb(ev.kind, ev.data); } catch (err) {
250
+ log.error("worker-proxy", `Event listener error: ${err}`);
251
+ }
203
252
  }
204
253
  }
205
254
  }
@@ -248,7 +297,7 @@ export function wrap<T extends WorkerApi>(worker: AnyWorker, options?: WrapOptio
248
297
  const id = ++reqId;
249
298
  const entry: { resolve: (v: unknown) => void; reject: (e: Error) => void; timer?: ReturnType<typeof setTimeout> } = { resolve, reject };
250
299
  if (timeoutMs > 0) {
251
- entry.timer = setTimeout(() => {
300
+ entry.timer = untrackedTimeout(() => {
252
301
  if (pending.delete(id)) {
253
302
  reject(new Error(`RPC '${method}' timed out after ${timeoutMs}ms`));
254
303
  }
@@ -52,7 +52,7 @@ import type {
52
52
  GCController
53
53
  } from "../telemetry/gc-controller";
54
54
  import { createLogger } from "../util/logger";
55
- import { expose, exposeEvents, type WorkerApi } from "./rpc";
55
+ import { expose, exposeBatchedEvents, type WorkerApi } from "./rpc";
56
56
 
57
57
  const log = createLogger();
58
58
 
@@ -465,7 +465,7 @@ export function createSimWorker(opts: CreateSimWorkerOptions): SimWorkerControl
465
465
  // Serializes withLoopStopped critical sections.
466
466
  let stopChain: Promise<unknown> = Promise.resolve();
467
467
 
468
- const events = exposeEvents();
468
+ const events = exposeBatchedEvents();
469
469
 
470
470
  // Deterministic RNG — reseedable via control.setSeed(); the stable `rng`
471
471
  // closure is what ctx.rng and the Math.random shim draw from.
@@ -750,6 +750,9 @@ export function createSimWorker(opts: CreateSimWorkerOptions): SimWorkerControl
750
750
  syncWorker = null; // release sync worker reference
751
751
  saveStore = null;
752
752
  await opts.onShutdown?.();
753
+ // Drain any events queued during onShutdown so they aren't lost to
754
+ // a flush timer the terminating worker never runs.
755
+ events.flush();
753
756
  },
754
757
 
755
758
  async setSpeed(speed: number): Promise<void> {
@@ -898,7 +901,7 @@ export function createSimWorker(opts: CreateSimWorkerOptions): SimWorkerControl
898
901
  : {}),
899
902
  };
900
903
 
901
- const api: WorkerApi = { ...standardApi, ...(opts.extraApi ?? {}) };
904
+ const api: WorkerApi = { ...standardApi, ...opts.extraApi };
902
905
 
903
906
  // Optionally wrap the API before exposing (e.g. with exposeDevToolsApi
904
907
  // to add devtools + profiling RPC methods).
@@ -121,6 +121,10 @@ export class TaskPool {
121
121
  for (let i = 0; i < this.workerCount; i++) {
122
122
  const worker = this.createWorker();
123
123
  worker.onerror = (e: ErrorEvent) => {
124
+ // Session teardown tags tracked workers __ddExpectTerminate and
125
+ // fires synthetic error events to settle waiters — not faults
126
+ // (see session-tracker.ts terminateTrackedWorker).
127
+ if ((worker as any).__ddExpectTerminate || this.destroyed) return;
124
128
  if (this.onWorkerError) this.onWorkerError(i, e);
125
129
  else log.error("task-pool", `Worker ${i} error: ${e.message}`);
126
130
  };