@vgai/engine 0.5.41 → 0.5.44

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 (129) hide show
  1. package/dist/adapter/adapter-module.d.ts +28 -29
  2. package/dist/adapter/adapter-module.d.ts.map +1 -1
  3. package/dist/adapter/adapter-module.js +11 -34
  4. package/dist/adapter/authoring-seam-contract.d.ts +10 -0
  5. package/dist/adapter/authoring-seam-contract.d.ts.map +1 -1
  6. package/dist/adapter/authoring-seam-contract.js +2 -0
  7. package/dist/adapter/authoring.d.ts +31 -0
  8. package/dist/adapter/authoring.d.ts.map +1 -1
  9. package/dist/adapter/finders/finder-result.d.ts +2 -2
  10. package/dist/adapter/finders/finder-result.d.ts.map +1 -1
  11. package/dist/adapter/finders/index.d.ts +15 -2
  12. package/dist/adapter/finders/index.d.ts.map +1 -1
  13. package/dist/adapter/finders/index.js +38 -10
  14. package/dist/adapter/finders/prefabs-from-stories.d.ts.map +1 -1
  15. package/dist/adapter/finders/registry.d.ts +45 -0
  16. package/dist/adapter/finders/registry.d.ts.map +1 -0
  17. package/dist/adapter/finders/registry.js +56 -0
  18. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  19. package/dist/adapter/ingest/scene-capture.js +50 -20
  20. package/dist/adapter/system-adapter.d.ts +3 -1
  21. package/dist/adapter/system-adapter.d.ts.map +1 -1
  22. package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
  23. package/dist/canvas-react/pixi-react-root-factory.js +21 -11
  24. package/dist/data/vite-plugin-data.d.ts +1 -1
  25. package/dist/data/vite-plugin-data.d.ts.map +1 -1
  26. package/dist/data/vite-plugin-data.js +2 -2
  27. package/dist/ecs/user-data.d.ts +6 -0
  28. package/dist/ecs/user-data.d.ts.map +1 -1
  29. package/dist/ecs/user-data.js +4 -0
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/input/host-pointer.d.ts +18 -0
  34. package/dist/input/host-pointer.d.ts.map +1 -1
  35. package/dist/input/host-pointer.js +54 -5
  36. package/dist/input/input-manager.d.ts +83 -1
  37. package/dist/input/input-manager.d.ts.map +1 -1
  38. package/dist/input/input-manager.js +357 -8
  39. package/dist/manifest/configuration-kinds.d.ts +97 -0
  40. package/dist/manifest/configuration-kinds.d.ts.map +1 -0
  41. package/dist/manifest/configuration-kinds.js +148 -0
  42. package/dist/manifest/index.d.ts +2 -1
  43. package/dist/manifest/index.d.ts.map +1 -1
  44. package/dist/manifest/index.js +4 -3
  45. package/dist/manifest/kind-modules.d.ts +18 -0
  46. package/dist/manifest/kind-modules.d.ts.map +1 -0
  47. package/dist/manifest/kind-modules.js +58 -0
  48. package/dist/manifest/load-file.d.ts +3 -3
  49. package/dist/manifest/load-file.d.ts.map +1 -1
  50. package/dist/manifest/load-file.js +4 -4
  51. package/dist/manifest/load.d.ts +17 -12
  52. package/dist/manifest/load.d.ts.map +1 -1
  53. package/dist/manifest/load.js +29 -19
  54. package/dist/manifest/runtime-environment.d.ts +11 -0
  55. package/dist/manifest/runtime-environment.d.ts.map +1 -0
  56. package/dist/manifest/runtime-environment.js +44 -0
  57. package/dist/manifest/schema.d.ts +10 -9
  58. package/dist/manifest/schema.d.ts.map +1 -1
  59. package/dist/manifest/schema.js +32 -27
  60. package/dist/react/use-data.d.ts +1 -1
  61. package/dist/react/use-data.js +1 -1
  62. package/dist/render/environment-capture.d.ts +10 -1
  63. package/dist/render/environment-capture.d.ts.map +1 -1
  64. package/dist/render/environment-capture.js +25 -0
  65. package/dist/render/ibl-override-material.d.ts +11 -2
  66. package/dist/render/ibl-override-material.d.ts.map +1 -1
  67. package/dist/render/ibl-override-material.js +16 -2
  68. package/dist/render/matcap-texture.d.ts +5 -0
  69. package/dist/render/matcap-texture.d.ts.map +1 -0
  70. package/dist/render/matcap-texture.js +82 -0
  71. package/dist/render/soft-particle-depth.d.ts +14 -8
  72. package/dist/render/soft-particle-depth.d.ts.map +1 -1
  73. package/dist/render/soft-particle-depth.js +37 -14
  74. package/dist/render/viewport-shading.d.ts +11 -1
  75. package/dist/render/viewport-shading.d.ts.map +1 -1
  76. package/dist/render/viewport-shading.js +25 -0
  77. package/dist/runtime/create-runtime.js +6 -5
  78. package/dist/runtime/debug-bridge.d.ts.map +1 -1
  79. package/dist/runtime/debug-bridge.js +17 -3
  80. package/dist/runtime/mount-manifest.d.ts.map +1 -1
  81. package/dist/runtime/mount-manifest.js +11 -2
  82. package/dist/settings/schema.d.ts +99 -0
  83. package/dist/settings/schema.d.ts.map +1 -0
  84. package/dist/settings/schema.js +110 -0
  85. package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
  86. package/dist/world3d-react/r3f-root-factory.js +34 -36
  87. package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
  88. package/dist/world3d-react/rapier-physics-bridge.js +21 -6
  89. package/dist-config/config.js +1 -1
  90. package/dist-config/config.js.map +2 -2
  91. package/package.json +2 -1
  92. package/schemas/engine-capabilities.json +4 -4
  93. package/schemas/vgai-project.schema.json +41 -36
  94. package/schemas/vgai-settings.schema.json +112 -0
  95. package/src/adapter/adapter-module.ts +33 -51
  96. package/src/adapter/authoring-seam-contract.ts +2 -0
  97. package/src/adapter/authoring.ts +31 -0
  98. package/src/adapter/finders/finder-result.ts +2 -2
  99. package/src/adapter/finders/index.ts +68 -12
  100. package/src/adapter/finders/prefabs-from-stories.ts +2 -2
  101. package/src/adapter/finders/registry.ts +95 -0
  102. package/src/adapter/finders/scenes-from-entrypoint-selection.ts +4 -4
  103. package/src/adapter/ingest/scene-capture.ts +48 -19
  104. package/src/adapter/system-adapter.ts +3 -1
  105. package/src/canvas-react/pixi-react-root-factory.tsx +21 -11
  106. package/src/data/vite-plugin-data.ts +4 -2
  107. package/src/ecs/user-data.ts +6 -0
  108. package/src/index.ts +1 -1
  109. package/src/input/host-pointer.ts +84 -5
  110. package/src/input/input-manager.ts +404 -7
  111. package/src/manifest/configuration-kinds.ts +241 -0
  112. package/src/manifest/index.ts +4 -3
  113. package/src/manifest/kind-modules.ts +58 -0
  114. package/src/manifest/load-file.ts +11 -5
  115. package/src/manifest/load.ts +53 -22
  116. package/src/manifest/runtime-environment.ts +48 -0
  117. package/src/manifest/schema.ts +39 -33
  118. package/src/react/use-data.ts +1 -1
  119. package/src/render/environment-capture.ts +30 -1
  120. package/src/render/ibl-override-material.ts +32 -2
  121. package/src/render/matcap-texture.ts +92 -0
  122. package/src/render/soft-particle-depth.ts +43 -14
  123. package/src/render/viewport-shading.ts +33 -1
  124. package/src/runtime/create-runtime.ts +6 -5
  125. package/src/runtime/debug-bridge.ts +21 -3
  126. package/src/runtime/mount-manifest.ts +13 -2
  127. package/src/settings/schema.ts +127 -0
  128. package/src/world3d-react/r3f-root-factory.tsx +45 -39
  129. package/src/world3d-react/rapier-physics-bridge.tsx +22 -8
@@ -150,19 +150,19 @@ function canvasWorldAdapter(id: string, component: ComponentType): RootAdapter<'
150
150
  const unmounted = new Promise<void>((resolve) => {
151
151
  resolveUnmounted = resolve;
152
152
  });
153
+ let resolveEffectsUnmounted!: () => void;
154
+ const effectsUnmounted = new Promise<void>((resolve) => {
155
+ resolveEffectsUnmounted = resolve;
156
+ });
153
157
  /**
154
- * Signals the tree's FIRST COMMIT and its LAST. A layout effect,
155
- * deliberately, and rendered as the LAST child, deliberately:
158
+ * Signals the tree's FIRST COMMIT and both unmount cleanup phases. Rendered as the LAST
159
+ * child deliberately:
156
160
  *
157
161
  * - mount: layout effects run synchronously inside the commit, before
158
162
  * any passive effect and before any tick can be scheduled;
159
- * - unmount: React commits deletion effects in tree order, each node
160
- * before its own children, so the LAST sibling's cleanup is the last
161
- * to run. That is what makes this a reliable "every component has
162
- * finished tearing down" signal — and it is load-bearing, because
163
- * `useTick`'s own cleanup calls `ticker.remove()`. Destroying the
164
- * Application before that cleanup lands throws inside React's commit
165
- * on every teardown of any world with a tick hook.
163
+ * - unmount: the layout cleanup fences `useTick` and the passive cleanup fences ordinary
164
+ * project effects. Native destruction waits for BOTH; resolving only from layout left
165
+ * passive listener/resource cleanup racing an Application whose renderer was null.
166
166
  */
167
167
  function Lifecycle(): null {
168
168
  useLayoutEffect(() => {
@@ -172,7 +172,10 @@ function canvasWorldAdapter(id: string, component: ComponentType): RootAdapter<'
172
172
  // This component is the last sibling. Reaching its passive effect
173
173
  // means the initial tree's provider/command effects have registered,
174
174
  // so mount() can truthfully back Play's ack.
175
- useEffect(resolveEffectsReady, []);
175
+ useEffect(() => {
176
+ resolveEffectsReady();
177
+ return resolveEffectsUnmounted;
178
+ }, []);
176
179
  return null;
177
180
  }
178
181
 
@@ -282,7 +285,14 @@ function canvasWorldAdapter(id: string, component: ComponentType): RootAdapter<'
282
285
  // Adapter surface: the host's shared debug registry. The game's own
283
286
  // capabilities arrive as the entry module's declared `systems`.
284
287
  const systemAdapters: SystemAdapters = registry ? { debug: registry.adapter } : {};
285
- const disposeComplete = unmounted.then(() => {
288
+ const disposeComplete = Promise.all([unmounted, effectsUnmounted]).then(() => {
289
+ // React can tear the root down itself after a fatal layout/effect error. In that path the
290
+ // host has not called dispose() yet, but Application.destroy() still clears Pixi's ticker
291
+ // and renderer. Wait for both layout and passive cleanup first: project effects may own
292
+ // window/document listeners and can still read the Pixi Application while React drains
293
+ // passive deletion effects. Fence the mounted root before clearing the native slots so
294
+ // subsequent host frames and the eventual host teardown cannot re-enter them.
295
+ disposed = true;
286
296
  mountedApp.destroy(false, { children: true });
287
297
  });
288
298
 
@@ -27,7 +27,7 @@
27
27
  * target file" hole, closed for declared refs — see
28
28
  * `collectDeclaredRefFields`/`findMissingRefTargets` in `data-check-core.ts`).
29
29
  * 3. **Tools never ship** (`generateBundle`): no emitted chunk may contain a
30
- * module from `src/tools/` or a `*.tool.*` file (§4: tester serves and
30
+ * module from `src/contributions/`, `src/tools/` or a `*.tool.*` file (§4: tester serves and
31
31
  * standalone builds strip tools entirely; W4 made this true by
32
32
  * construction — nothing imports tools — and this check PINS it against
33
33
  * the day some game module imports a tool "just for a helper").
@@ -268,7 +268,9 @@ export async function collectDataCheckProblems(options: VgaiDataCheckOptions): P
268
268
  /** True iff a bundled module id is tool code that must never ship (§4). Pure, unit-tested. */
269
269
  export function isToolModuleId(id: string): boolean {
270
270
  const normalized = id.split('\\').join('/');
271
- return /\/src\/tools\//.test(normalized) || /\.tool\.[tj]sx?(\?|$)/.test(normalized);
271
+ return (
272
+ /\/src\/(?:tools|contributions)\//.test(normalized) || /\.tool\.[tj]sx?(\?|$)/.test(normalized)
273
+ );
272
274
  }
273
275
 
274
276
  /**
@@ -82,6 +82,9 @@
82
82
  * association; unlike the label, never user-facing copy.
83
83
  * - `authoringDocument` — generic reference to the project-tool document
84
84
  * that owns this scene instance's authored asset.
85
+ * - `authoringHierarchyId`, `authoringHierarchyParentId`,
86
+ * `authoringHierarchyOrder` — source-owned semantic identity, parent identity,
87
+ * and sibling ordinal shared across native surfaces.
85
88
  * - `editorHelper` — `true` for editor-only helper objects (gizmos, wireframes).
86
89
  *
87
90
  * Hierarchy-presentation convention (written by GAME code, read by the editor —
@@ -195,6 +198,8 @@ export interface UserDataSchema {
195
198
  };
196
199
  /** Project-owned identity used to join authored hierarchy rows across adapter roots. */
197
200
  authoringHierarchyId: string;
201
+ /** Project-owned parent identity when the authored parent can live on another surface. */
202
+ authoringHierarchyParentId: string;
198
203
  /** Source-owned sibling ordinal used when hierarchy rows cross native roots. */
199
204
  authoringHierarchyOrder: number;
200
205
  editorHelper: boolean;
@@ -254,6 +259,7 @@ export const UserDataKeys = {
254
259
  authoringComponent: 'authoringComponent',
255
260
  authoringDocument: 'authoringDocument',
256
261
  authoringHierarchyId: 'authoringHierarchyId',
262
+ authoringHierarchyParentId: 'authoringHierarchyParentId',
257
263
  authoringHierarchyOrder: 'authoringHierarchyOrder',
258
264
  editorHelper: 'editorHelper',
259
265
  editorHelperType: 'editorHelperType',
package/src/index.ts CHANGED
@@ -32,7 +32,7 @@ export type {
32
32
  ResolvedGameManifest,
33
33
  RootAdapter,
34
34
  } from './manifest';
35
- export { DEFAULT_SERVER_MODULE, loadGameManifest } from './manifest';
35
+ export { loadGameManifest } from './manifest';
36
36
  export type { GameSession } from './runtime/create-runtime';
37
37
  export type {
38
38
  MountEntry,
@@ -72,6 +72,8 @@ export interface HostPointerTouch {
72
72
  * differently is holding a button nothing will ever release.
73
73
  */
74
74
  readonly phase: 'start' | 'move' | 'end';
75
+ /** True only when the browser ended this contact with `pointercancel`. */
76
+ readonly canceled: boolean;
75
77
  /**
76
78
  * DENSE finger index — the lowest slot free when this finger went down, and
77
79
  * free again the moment it lifts. NOT the browser's `pointerId`, which never
@@ -80,6 +82,22 @@ export interface HostPointerTouch {
80
82
  readonly index: number;
81
83
  /** Position in the design space (see {@link HostPointerOptions.designSize}). */
82
84
  readonly position: Vector2;
85
+ /** Position before design-resolution scaling, in surface CSS pixels. */
86
+ readonly screenPosition: Vector2;
87
+ /** Delta since this contact's preceding sample, in design pixels. */
88
+ readonly relative: Vector2;
89
+ /** Delta since this contact's preceding sample, in surface CSS pixels. */
90
+ readonly screenRelative: Vector2;
91
+ /** Design-pixel velocity derived from the browser event timestamps. */
92
+ readonly velocity: Vector2;
93
+ /** Surface CSS-pixel velocity derived from the browser event timestamps. */
94
+ readonly screenVelocity: Vector2;
95
+ /** Native PointerEvent pressure in the normalized [0, 1] range. */
96
+ readonly pressure: number;
97
+ /** Native PointerEvent tilt, in degrees. Touch contacts normally report zero. */
98
+ readonly tilt: Vector2;
99
+ /** Touch pointers are never an inverted pen; retained for Godot's event value contract. */
100
+ readonly penInverted: false;
83
101
  }
84
102
 
85
103
  /** What {@link createHostPointer} needs. */
@@ -141,6 +159,14 @@ export function createHostPointer(options: HostPointerOptions): HostPointer {
141
159
  const touches: HostPointerTouch[] = [];
142
160
  /** Browser `pointerId` -> dense finger index, for fingers currently down. */
143
161
  const fingerOfPointer = new Map<number, number>();
162
+ const sampleOfPointer = new Map<
163
+ number,
164
+ {
165
+ readonly position: Vector2;
166
+ readonly screenPosition: Vector2;
167
+ readonly timestamp: number;
168
+ }
169
+ >();
144
170
  const freeFinger = (): number => {
145
171
  const taken = new Set(fingerOfPointer.values());
146
172
  let index = 0;
@@ -152,11 +178,52 @@ export function createHostPointer(options: HostPointerOptions): HostPointer {
152
178
  if (touches.length > TOUCH_QUEUE_MAX) touches.shift();
153
179
  };
154
180
 
155
- const queueTouch = (event: PointerEvent, point: Vector2): void => {
181
+ const queueTouch = (event: PointerEvent, point: Vector2, screenPoint: Vector2): void => {
182
+ const previous = sampleOfPointer.get(event.pointerId);
183
+ const relative =
184
+ previous === undefined
185
+ ? { x: 0, y: 0 }
186
+ : { x: point.x - previous.position.x, y: point.y - previous.position.y };
187
+ const screenRelative =
188
+ previous === undefined
189
+ ? { x: 0, y: 0 }
190
+ : {
191
+ x: screenPoint.x - previous.screenPosition.x,
192
+ y: screenPoint.y - previous.screenPosition.y,
193
+ };
194
+ const elapsedSeconds =
195
+ previous === undefined ? 0 : Math.max(0, event.timeStamp - previous.timestamp) / 1000;
196
+ const velocity =
197
+ elapsedSeconds > 0
198
+ ? { x: relative.x / elapsedSeconds, y: relative.y / elapsedSeconds }
199
+ : { x: 0, y: 0 };
200
+ const screenVelocity =
201
+ elapsedSeconds > 0
202
+ ? { x: screenRelative.x / elapsedSeconds, y: screenRelative.y / elapsedSeconds }
203
+ : { x: 0, y: 0 };
204
+ const common = {
205
+ position: point,
206
+ screenPosition: screenPoint,
207
+ relative,
208
+ screenRelative,
209
+ velocity,
210
+ screenVelocity,
211
+ pressure: Number.isFinite(event.pressure) ? Math.max(0, Math.min(1, event.pressure)) : 0,
212
+ tilt: {
213
+ x: Number.isFinite(event.tiltX) ? event.tiltX : 0,
214
+ y: Number.isFinite(event.tiltY) ? event.tiltY : 0,
215
+ },
216
+ penInverted: false as const,
217
+ };
156
218
  if (event.type === 'pointerdown') {
157
219
  const index = freeFinger();
158
220
  fingerOfPointer.set(event.pointerId, index);
159
- queue({ phase: 'start', index, position: point });
221
+ sampleOfPointer.set(event.pointerId, {
222
+ position: point,
223
+ screenPosition: screenPoint,
224
+ timestamp: event.timeStamp,
225
+ });
226
+ queue({ phase: 'start', canceled: false, index, ...common });
160
227
  return;
161
228
  }
162
229
  // A move or a release from a pointer that never went down on this surface
@@ -165,10 +232,21 @@ export function createHostPointer(options: HostPointerOptions): HostPointer {
165
232
  if (index === undefined) return;
166
233
  if (event.type === 'pointerup' || event.type === 'pointercancel') {
167
234
  fingerOfPointer.delete(event.pointerId);
168
- queue({ phase: 'end', index, position: point });
235
+ sampleOfPointer.delete(event.pointerId);
236
+ queue({
237
+ phase: 'end',
238
+ canceled: event.type === 'pointercancel',
239
+ index,
240
+ ...common,
241
+ });
169
242
  return;
170
243
  }
171
- queue({ phase: 'move', index, position: point });
244
+ sampleOfPointer.set(event.pointerId, {
245
+ position: point,
246
+ screenPosition: screenPoint,
247
+ timestamp: event.timeStamp,
248
+ });
249
+ queue({ phase: 'move', canceled: false, index, ...common });
172
250
  };
173
251
 
174
252
  /** A client-space event as a point in the design space, via the surface rect
@@ -188,6 +266,7 @@ export function createHostPointer(options: HostPointerOptions): HostPointer {
188
266
  if (surface === null) return;
189
267
  const rect = surface.getBoundingClientRect();
190
268
  if (rect.width <= 0 || rect.height <= 0) return;
269
+ const screenPoint = { x: event.clientX - rect.left, y: event.clientY - rect.top };
191
270
  const point = toDesignSpace(event, rect);
192
271
 
193
272
  if (event.type === 'pointermove') {
@@ -199,7 +278,7 @@ export function createHostPointer(options: HostPointerOptions): HostPointer {
199
278
  }
200
279
  // A mouse or a pen is not a finger. A control that answered them here would
201
280
  // steal the clicks of a player who cannot see it.
202
- if (event.pointerType === 'touch') queueTouch(event, point);
281
+ if (event.pointerType === 'touch') queueTouch(event, point, screenPoint);
203
282
  };
204
283
 
205
284
  const KINDS = ['pointerdown', 'pointermove', 'pointerup', 'pointercancel'] as const;