@vgai/engine 0.5.39 → 0.5.41

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 (34) hide show
  1. package/dist/adapter/authoring.d.ts +6 -0
  2. package/dist/adapter/authoring.d.ts.map +1 -1
  3. package/dist/adapter/root-adapter.d.ts +16 -0
  4. package/dist/adapter/root-adapter.d.ts.map +1 -1
  5. package/dist/adapter/root-seam-contract.d.ts +5 -0
  6. package/dist/adapter/root-seam-contract.d.ts.map +1 -1
  7. package/dist/adapter/root-seam-contract.js +1 -0
  8. package/dist/ai/navigation.d.ts +30 -0
  9. package/dist/ai/navigation.d.ts.map +1 -1
  10. package/dist/ai/navigation.js +248 -15
  11. package/dist/ecs/user-data.d.ts +6 -0
  12. package/dist/ecs/user-data.d.ts.map +1 -1
  13. package/dist/ecs/user-data.js +2 -0
  14. package/dist/runtime/debug-registry.d.ts +2 -0
  15. package/dist/runtime/debug-registry.d.ts.map +1 -1
  16. package/dist/runtime/debug-registry.js +1 -0
  17. package/dist/runtime/game.d.ts +13 -6
  18. package/dist/runtime/game.d.ts.map +1 -1
  19. package/dist/runtime/game.js +42 -6
  20. package/dist/world3d-react/frame-delta.d.ts +15 -0
  21. package/dist/world3d-react/frame-delta.d.ts.map +1 -0
  22. package/dist/world3d-react/frame-delta.js +21 -0
  23. package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
  24. package/dist/world3d-react/r3f-root-factory.js +19 -6
  25. package/package.json +1 -1
  26. package/src/adapter/authoring.ts +6 -0
  27. package/src/adapter/root-adapter.ts +17 -0
  28. package/src/adapter/root-seam-contract.ts +1 -0
  29. package/src/ai/navigation.ts +298 -13
  30. package/src/ecs/user-data.ts +6 -0
  31. package/src/runtime/debug-registry.ts +3 -0
  32. package/src/runtime/game.ts +56 -11
  33. package/src/world3d-react/frame-delta.ts +25 -0
  34. package/src/world3d-react/r3f-root-factory.tsx +19 -6
@@ -89,8 +89,9 @@ export type AdapterSurface = AdapterSurfaceLeaf;
89
89
  * A world's per-phase frame hooks.
90
90
  * Populated on a `RootInstance` only for first-party mounts — an opaque/
91
91
  * foreign mount has no phase-partitioned entry point, so it stays
92
- * `undefined` and the Game's frame executor (`GameInternal.runFrame`) falls
93
- * back to calling its single `mounted.update(dt)` once per substep instead.
92
+ * `undefined` and the Game's frame executor falls back to its single
93
+ * `mounted.update(dt)`. The mount's `updateCadence` chooses fixed-substep
94
+ * (the default) or display-rate dispatch.
94
95
  */
95
96
  export interface RootFrameHooks {
96
97
  /** Run this world's engine systems + component ticks + world-bound game
@@ -405,8 +406,8 @@ export interface Game {
405
406
  * sway, a cosmetic bob. It is NOT a gameplay hook: it can fire twice
406
407
  * between two fixed substeps (120 Hz display, 60 Hz sim) and zero times
407
408
  * across a `runTicks` fast-forward, so anything that must be deterministic
408
- * belongs in a fixed phase (`ctx.systems.add`, a `useFrame` hook, a
409
- * `useFrame`) instead.
409
+ * belongs in a fixed engine phase (`ctx.systems.add`) instead. Ecosystem
410
+ * presentation hooks such as R3F's `useFrame` share this display clock.
410
411
  *
411
412
  * Reached from game code as `ctx.game?.onRenderStep(...)`. Returns an
412
413
  * unsubscribe function; pass `opts.signal` to unsubscribe with the same
@@ -449,13 +450,17 @@ export interface GameInternal extends Game {
449
450
  * for world in roots: // after ALL phases
450
451
  * if world.mounted.drivesOwnLoop: continue
451
452
  * if world.frame: world.frame.endFrame?.()
452
- * else: world.mounted.update?.(dt) // opaque world fallback
453
+ * else if updateCadence != 'display' OR render phases are included:
454
+ * world.mounted.update?.(dt) // opaque world fallback
453
455
  * ```
454
456
  *
455
457
  * A single first-party world and its direct `mounted.update(dt)` entry run
456
458
  * the same `SystemRunner` and `postFrame` work. A `drivesOwnLoop` world is
457
459
  * never ticked here. An opaque host-driven world (no `frame`) gets exactly
458
- * one `update(dt)` call per substep, after the phase loop.
460
+ * one `update(dt)` call per substep after the phase loop by default. A
461
+ * display-cadence opaque world is withheld when `skipRenderPhases` is true;
462
+ * the matching host calls it once from {@link runRenderFrame}. A direct
463
+ * `runFrame` with render phases included still calls it once.
459
464
  *
460
465
  * D10/T7.6 play-state addendum: when `Game.play.paused` is true, every
461
466
  * `pausable` (and non-`drivesOwnLoop`) world skips every phase EXCEPT
@@ -495,6 +500,8 @@ export interface GameInternal extends Game {
495
500
  * for world in roots (declaration order):
496
501
  * if world.mounted.drivesOwnLoop: continue
497
502
  * world.frame?.runPhase(phase, frozen ? 0 : displayDt)
503
+ * for opaque world with updateCadence == 'display':
504
+ * world.mounted.update?.(frozen ? 0 : displayDt)
498
505
  * ```
499
506
  *
500
507
  * Advances NOTHING: no `tick`, no `simT`, no sim-clock flush, no state-bridge
@@ -1 +1 @@
1
- {"version":3,"file":"game.d.ts","sourceRoot":"","sources":["../../src/runtime/game.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"game.d.ts","sourceRoot":"","sources":["../../src/runtime/game.ts"],"names":[],"mappings":"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"}
@@ -282,6 +282,7 @@ export function createGame(opts) {
282
282
  const debugRegistry = createDebugRegistry({
283
283
  getTick: () => tick,
284
284
  getSimT: () => simT,
285
+ getFixedDt: () => opts.loop.fixedDt,
285
286
  // D15/T-D15.5 — "the manifest's first/default world" for the debug
286
287
  // registry's world-addressed input-target surface. Every
287
288
  // first-party root now registers the SAME game-owned InputManager, but the
@@ -435,7 +436,12 @@ export function createGame(opts) {
435
436
  reportGateShortfallOnce('loop', world.id, 'self-driven world, adapter declares no setPaused capability');
436
437
  }
437
438
  }
438
- const audio = world.mounted.systems?.audio;
439
+ // A native entry's static `systems` export is this root's primary
440
+ // declaration surface. `computeSystemAdapters()` already gives it
441
+ // precedence over a substrate-seeded mounted adapter; pause must read
442
+ // the same source or a valid entry-declared audio graph is visible to
443
+ // the editor yet paradoxically reported as ungateable here.
444
+ const audio = declaredSystemAdapters.get(world.id)?.audio ?? world.mounted.systems?.audio;
439
445
  if (audio) {
440
446
  audio.setMuted(next);
441
447
  }
@@ -643,11 +649,19 @@ export function createGame(opts) {
643
649
  // Checklist item 2: same isolation for the opaque-world fallback —
644
650
  // one foreign mount's `update` throwing must not starve its
645
651
  // siblings' `endFrame`/`update` calls this same loop.
646
- try {
647
- world.mounted.update?.(dt);
648
- }
649
- catch (err) {
650
- console.error(`[game] world "${world.id}" (kind: ${world.kind}) update() threw:`, err);
652
+ // A display-cadence opaque update is the root's indivisible native
653
+ // presentation (R3F `advance()` is callbacks + draw). The real
654
+ // rAF host withholds render phases from EVERY fixed catch-up step;
655
+ // withhold this update beside them, then run it once in
656
+ // `runRenderFrameImpl`. A direct/offline `runFrame` includes render
657
+ // phases and therefore still advances the root exactly once.
658
+ if (!(skipRenderPhases && world.mounted.updateCadence === 'display')) {
659
+ try {
660
+ world.mounted.update?.(dt);
661
+ }
662
+ catch (err) {
663
+ console.error(`[game] world "${world.id}" (kind: ${world.kind}) update() threw:`, err);
664
+ }
651
665
  }
652
666
  }
653
667
  }
@@ -746,6 +760,28 @@ export function createGame(opts) {
746
760
  }
747
761
  profiler.endPhase(phase);
748
762
  }
763
+ // An opaque display-cadence root cannot expose phase hooks because its
764
+ // native advance is inseparable. It still belongs to THIS presentation
765
+ // frame, once — never to every fixed catch-up substep that preceded it.
766
+ // A frozen world redraws at dt=0, preserving the established pause rule.
767
+ for (let i = 0; i < n; i++) {
768
+ const world = roots[i];
769
+ if (world.disposed)
770
+ continue;
771
+ if (world.mounted.drivesOwnLoop)
772
+ continue;
773
+ if (world.frame)
774
+ continue;
775
+ if (world.mounted.updateCadence !== 'display')
776
+ continue;
777
+ const frozen = paused && world.pausable;
778
+ try {
779
+ world.mounted.update?.(frozen ? 0 : displayDt);
780
+ }
781
+ catch (err) {
782
+ console.error(`[game] world "${world.id}" (kind: ${world.kind}) update() threw (display frame):`, err);
783
+ }
784
+ }
749
785
  profiler.endFrame();
750
786
  if (rngTrapEnabled)
751
787
  rngTrap.disable();
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The exact host-owned delta for the synchronous R3F frame currently being dispatched.
3
+ *
4
+ * Fiber's `frameloop: 'never'` API accepts an absolute timestamp and reconstructs the frame
5
+ * delta by subtracting its previous timestamp. Once that timestamp is large enough, ordinary
6
+ * floating-point cancellation can turn an exact host `1 / 60` into either adjacent double. The
7
+ * host still owns the original exact `dt`; adapter-boundary runtimes that must reproduce another
8
+ * engine's clock can read it here instead of mistaking Fiber's reconstruction residue for game
9
+ * time. The seat exists only for the synchronous `advance(...)` call and is cleared immediately
10
+ * afterwards, so an async consumer can never read a stale frame.
11
+ */
12
+ export declare function setHostFrameDelta(state: object, delta: number): void;
13
+ export declare function clearHostFrameDelta(state: object): void;
14
+ export declare function hostFrameDelta(state: object, fiberDelta: number): number;
15
+ //# sourceMappingURL=frame-delta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-delta.d.ts","sourceRoot":"","sources":["../../src/world3d-react/frame-delta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEpE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAExE"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The exact host-owned delta for the synchronous R3F frame currently being dispatched.
3
+ *
4
+ * Fiber's `frameloop: 'never'` API accepts an absolute timestamp and reconstructs the frame
5
+ * delta by subtracting its previous timestamp. Once that timestamp is large enough, ordinary
6
+ * floating-point cancellation can turn an exact host `1 / 60` into either adjacent double. The
7
+ * host still owns the original exact `dt`; adapter-boundary runtimes that must reproduce another
8
+ * engine's clock can read it here instead of mistaking Fiber's reconstruction residue for game
9
+ * time. The seat exists only for the synchronous `advance(...)` call and is cleared immediately
10
+ * afterwards, so an async consumer can never read a stale frame.
11
+ */
12
+ const hostFrameDeltas = new WeakMap();
13
+ export function setHostFrameDelta(state, delta) {
14
+ hostFrameDeltas.set(state, delta);
15
+ }
16
+ export function clearHostFrameDelta(state) {
17
+ hostFrameDeltas.delete(state);
18
+ }
19
+ export function hostFrameDelta(state, fiberDelta) {
20
+ return hostFrameDeltas.get(state) ?? fiberDelta;
21
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"r3f-root-factory.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-root-factory.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAWH,OAAO,KAAK,EAAoB,WAAW,EAAoB,MAAM,YAAY,CAAC;AAelF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAsZnE;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAI/F;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,qBAO5B,CAAC"}
1
+ {"version":3,"file":"r3f-root-factory.d.ts","sourceRoot":"","sources":["../../src/world3d-react/r3f-root-factory.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAWH,OAAO,KAAK,EAAoB,WAAW,EAAoB,MAAM,YAAY,CAAC;AAelF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAmanE;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAI/F;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,qBAO5B,CAAC"}
@@ -45,6 +45,7 @@ import { WorldProvider } from '../react/world-state';
45
45
  import { getDebugRegistry } from '../runtime/debug-registry';
46
46
  import { devBuildEnabled } from '../runtime/dev-build';
47
47
  import { DEFAULT_INPUT_MAP_PATH, wireGameInputSeams } from '../runtime/game-input-seams';
48
+ import { clearHostFrameDelta, setHostFrameDelta } from './frame-delta';
48
49
  /**
49
50
  * Build the `RootAdapter` for one default-exported R3F world. Mounts through
50
51
  * react-three-fiber, gated entirely by the host's own loop and rendering
@@ -291,11 +292,10 @@ function threeWorldAdapter(id, component) {
291
292
  if (renderDebugWiring)
292
293
  systemAdapters.renderDebug = renderDebugWiring.adapter;
293
294
  // The engine drives every `useFrame` through the mounted world's
294
- // `update(dt)` hook, never off a raw host-loop callback. That is the
295
- // whole pause story: `runFrameImpl` (`runtime/game.ts`) calls
296
- // `mounted.update?.(dt)` per substep for a host-driven world and SKIPS
297
- // it while that world is frozen, and `Game.play.step()` ticks it exactly
298
- // once.
295
+ // `update(dt)` hook, never off a raw Fiber loop. This mount declares
296
+ // that indivisible Fiber advance at DISPLAY cadence below: the real
297
+ // host calls it once per presentation, a paused world gets dt=0, and an
298
+ // explicit `Game.play.step()` still ticks it exactly once.
299
299
  //
300
300
  // `advance(timestamp, runGlobalEffects, state)`'s `timestamp` is
301
301
  // consumed as `THREE.Clock.elapsedTime` DIRECTLY when
@@ -327,6 +327,14 @@ function threeWorldAdapter(id, component) {
327
327
  return live().camera;
328
328
  },
329
329
  drivesOwnLoop: false,
330
+ // Fiber's `advance()` cannot separate its `useFrame` callbacks from
331
+ // its WebGL draw. Running this opaque update in every fixed catch-up
332
+ // substep renders N complete frames inside one rAF callback: once a
333
+ // scene costs more than 16.7ms, the accumulator asks for more draws,
334
+ // those draws make the next gap larger, and the loop spirals to its
335
+ // max-substep ceiling. The host's display pass is the one clock that
336
+ // must own this inseparable native advance.
337
+ updateCadence: 'display',
330
338
  // Adapter surface: `debug` (the shared game registry) and, when a real
331
339
  // WebGL2 context exists, `renderDebug` are engine-seeded. The game's
332
340
  // own capabilities arrive as the entry module's declared `systems`.
@@ -334,8 +342,12 @@ function threeWorldAdapter(id, component) {
334
342
  update(dt) {
335
343
  elapsed += dt;
336
344
  const current = live();
337
- renderDebugWiring?.beforeRender();
345
+ // Fiber receives an ABSOLUTE timestamp and reconstructs `delta` by subtraction. Publish
346
+ // the exact host-owned dt beside the synchronous advance so compatibility adapters that
347
+ // reproduce another engine's frame clock do not inherit one-ulp cancellation residue.
348
+ setHostFrameDelta(current, dt);
338
349
  try {
350
+ renderDebugWiring?.beforeRender();
339
351
  const profiler = host.game?.profiler;
340
352
  if (!profiler?.enabled) {
341
353
  advance(elapsed, true, current);
@@ -374,6 +386,7 @@ function threeWorldAdapter(id, component) {
374
386
  }
375
387
  }
376
388
  finally {
389
+ clearHostFrameDelta(current);
377
390
  renderDebugWiring?.afterRender();
378
391
  }
379
392
  },
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@vgai/engine",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "Apache-2.0",
5
- "version": "0.5.39",
5
+ "version": "0.5.41",
6
6
  "description": "Readable TypeScript game engine and universal host for Three.js, PixiJS, and React games.",
7
7
  "keywords": [
8
8
  "game-engine",
@@ -110,6 +110,12 @@ export interface EditorNode {
110
110
  typeLabel?: string;
111
111
  parentId: string | null;
112
112
  childIds: string[];
113
+ /** Opaque project-owned identity shared by authored rows across adapter roots. */
114
+ crossSurfaceId?: string;
115
+ /** Optional authored parent identity resolved against another row's cross-surface id. */
116
+ crossSurfaceParentId?: string;
117
+ /** Source-owned sibling ordinal used only when native rows are joined across surfaces. */
118
+ crossSurfaceOrder?: number;
113
119
  }
114
120
 
115
121
  export interface PropertyDescriptor {
@@ -65,6 +65,23 @@ export interface MountedRootBase {
65
65
  */
66
66
  readonly drivesOwnLoop: boolean;
67
67
 
68
+ /**
69
+ * Which host clock owns an opaque mounted root's single `update(dt)` hook.
70
+ *
71
+ * `fixed` (the default) preserves the adapter seam's original contract:
72
+ * the callback runs once per consumed simulation substep. `display` is for
73
+ * a reconciler whose one native advance is inseparably BOTH its callbacks
74
+ * and its draw (React Three Fiber's `advance()` is the measured case). The
75
+ * real host then withholds it from fixed catch-up substeps and invokes it
76
+ * exactly once from the display pass. A direct/offline `runFrame` that does
77
+ * not skip render phases still invokes it once, preserving capture and
78
+ * explicit-tick semantics.
79
+ *
80
+ * Absent means `fixed`. This changes no foreign/ingest adapter unless that
81
+ * adapter opts in explicitly.
82
+ */
83
+ readonly updateCadence?: 'fixed' | 'display';
84
+
68
85
  update?(dt: number): void; // host-driven only
69
86
  fixedUpdate?(dt: number): void;
70
87
  setPaused?(paused: boolean): void; // capability, not promise — see loop-model note above
@@ -18,6 +18,7 @@ export const ROOT_ADAPTER_SHAPE = defineSeamShape<RootAdapter<AdapterSurface>>()
18
18
 
19
19
  export const MOUNTED_ROOT_BASE_SHAPE = defineSeamShape<MountedRootBase>()({
20
20
  drivesOwnLoop: { optional: false, kind: 'value', required: 'shape' },
21
+ updateCadence: { optional: true, kind: 'value', required: 'shape' },
21
22
  update: { optional: true, kind: 'function', required: 'effect' },
22
23
  fixedUpdate: { optional: true, kind: 'function', required: 'effect' },
23
24
  setPaused: { optional: true, kind: 'function', required: 'effect' },