@forgeax/engine-scene 0.0.0-dev.8d955ade1c79
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/LICENSE +202 -0
- package/README.md +55 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/asset-owner.integration.test.d.ts +2 -0
- package/dist/__tests__/asset-owner.integration.test.d.ts.map +1 -0
- package/dist/__tests__/components.unit.test.d.ts +2 -0
- package/dist/__tests__/components.unit.test.d.ts.map +1 -0
- package/dist/__tests__/diagnostic-type-surface.test-d.d.ts +3 -0
- package/dist/__tests__/diagnostic-type-surface.test-d.d.ts.map +1 -0
- package/dist/__tests__/direct-child-of-import.unit.test.d.ts +2 -0
- package/dist/__tests__/direct-child-of-import.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts +8 -0
- package/dist/__tests__/fixtures/malformed-hierarchy-edge.d.ts.map +1 -0
- package/dist/__tests__/hierarchy-projection-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/hierarchy-projection-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/hierarchy-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/hierarchy-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/plugin.integration.test.d.ts +2 -0
- package/dist/__tests__/plugin.integration.test.d.ts.map +1 -0
- package/dist/__tests__/propagation.unit.test.d.ts +2 -0
- package/dist/__tests__/propagation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/transform-change-journal.unit.test.d.ts +2 -0
- package/dist/__tests__/transform-change-journal.unit.test.d.ts.map +1 -0
- package/dist/assets/scene-decoder.d.ts +8 -0
- package/dist/assets/scene-decoder.d.ts.map +1 -0
- package/dist/collect-subtree.d.ts +4 -0
- package/dist/collect-subtree.d.ts.map +1 -0
- package/dist/components/child-of.d.ts +31 -0
- package/dist/components/child-of.d.ts.map +1 -0
- package/dist/components/children.d.ts +48 -0
- package/dist/components/children.d.ts.map +1 -0
- package/dist/components/morph-weights.d.ts +5 -0
- package/dist/components/morph-weights.d.ts.map +1 -0
- package/dist/components/name.d.ts +4 -0
- package/dist/components/name.d.ts.map +1 -0
- package/dist/components/transform.d.ts +52 -0
- package/dist/components/transform.d.ts.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1731 -0
- package/dist/index.mjs.map +1 -0
- package/dist/instances/collect-profile.d.ts +7 -0
- package/dist/instances/collect-profile.d.ts.map +1 -0
- package/dist/instances/externalization.d.ts +14 -0
- package/dist/instances/externalization.d.ts.map +1 -0
- package/dist/instances/scene-instances.d.ts +350 -0
- package/dist/instances/scene-instances.d.ts.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/systems/hierarchy-projection.d.ts +16 -0
- package/dist/systems/hierarchy-projection.d.ts.map +1 -0
- package/dist/systems/index.d.ts +3 -0
- package/dist/systems/index.d.ts.map +1 -0
- package/dist/systems/propagate-transforms.d.ts +15 -0
- package/dist/systems/propagate-transforms.d.ts.map +1 -0
- package/package.json +58 -0
- package/src/__tests__/asset-owner.integration.test.ts +57 -0
- package/src/__tests__/components.unit.test.ts +26 -0
- package/src/__tests__/diagnostic-type-surface.test-d.ts +11 -0
- package/src/__tests__/direct-child-of-import.unit.test.ts +13 -0
- package/src/__tests__/fixtures/malformed-hierarchy-edge.ts +15 -0
- package/src/__tests__/hierarchy-projection-contract.unit.test.ts +45 -0
- package/src/__tests__/hierarchy-projection.unit.test.ts +92 -0
- package/src/__tests__/plugin.integration.test.ts +23 -0
- package/src/__tests__/propagation.unit.test.ts +163 -0
- package/src/__tests__/transform-change-journal.unit.test.ts +105 -0
- package/src/assets/scene-decoder.ts +153 -0
- package/src/collect-subtree.ts +30 -0
- package/src/components/child-of.ts +90 -0
- package/src/components/children.ts +113 -0
- package/src/components/morph-weights.ts +6 -0
- package/src/components/name.ts +22 -0
- package/src/components/transform.ts +89 -0
- package/src/errors.ts +34 -0
- package/src/index.ts +65 -0
- package/src/instances/collect-profile.ts +10 -0
- package/src/instances/externalization.ts +137 -0
- package/src/instances/scene-instances.ts +1564 -0
- package/src/plugin.ts +28 -0
- package/src/systems/hierarchy-projection.ts +134 -0
- package/src/systems/index.ts +11 -0
- package/src/systems/propagate-transforms.ts +376 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Immutable Scene collection policy shared by runtime collectors. */
|
|
2
|
+
export interface SceneCollectProfile {
|
|
3
|
+
readonly includeComponent: (componentName: string, transient: boolean) => boolean;
|
|
4
|
+
readonly includeField: (componentName: string, fieldName: string, transient: boolean) => boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const SCENE_COLLECT_PROFILE: SceneCollectProfile;
|
|
7
|
+
//# sourceMappingURL=collect-profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-profile.d.ts","sourceRoot":"","sources":["../../src/instances/collect-profile.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,gBAAgB,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,OAAO,CAAC;IAClF,QAAQ,CAAC,YAAY,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,OAAO,CAAC;CAClG;AAED,eAAO,MAAM,qBAAqB,EAAE,mBAGlC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AssetRef, SceneAsset } from '@forgeax/engine-types';
|
|
2
|
+
import { type Result } from '@forgeax/engine-types';
|
|
3
|
+
export type SceneComponentSchemaResolver = (componentName: string) => Readonly<Record<string, string>> | undefined;
|
|
4
|
+
export interface SceneExternalizationError {
|
|
5
|
+
readonly field: string;
|
|
6
|
+
readonly value: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface ExternalizedSceneAsset {
|
|
9
|
+
readonly payload: Record<string, unknown>;
|
|
10
|
+
readonly refs: readonly AssetRef[];
|
|
11
|
+
}
|
|
12
|
+
/** Project a SceneAsset's shared asset fields into a payload plus indexed refs. */
|
|
13
|
+
export declare function externalizeSceneAsset(scene: SceneAsset, resolveSchema: SceneComponentSchemaResolver): Result<ExternalizedSceneAsset, SceneExternalizationError>;
|
|
14
|
+
//# sourceMappingURL=externalization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"externalization.d.ts","sourceRoot":"","sources":["../../src/instances/externalization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAiB,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,4BAA4B,GAAG,CACzC,aAAa,EAAE,MAAM,KAClB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;CACpC;AA+BD,mFAAmF;AACnF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,aAAa,EAAE,4BAA4B,GAC1C,MAAM,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAsF3D"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { type Component, type ComponentData, type ComponentSchema, type EcsError, type EntityHandle, type ShapeOf, type World } from '@forgeax/engine-ecs';
|
|
2
|
+
import type { Handle, LocalEntityId, MountOverride, SceneAsset, SceneInstanceMount } from '@forgeax/engine-types';
|
|
3
|
+
import { type Result } from '@forgeax/engine-types';
|
|
4
|
+
/**
|
|
5
|
+
* C-R2 (feat-20260622-s5 / studio-issues): one structured, non-fatal record of
|
|
6
|
+
* a SceneAsset payload field that did NOT match the target component's schema.
|
|
7
|
+
*
|
|
8
|
+
* Scene data is loader-fed and may carry a stale / deprecated / typo'd field
|
|
9
|
+
* (an editor renames a field, an old `.pack.json` lags). `worldInstantiateScene`
|
|
10
|
+
* does NOT blank the whole scene over one such field (#478 lesson: a
|
|
11
|
+
* prod-silent strip re-introduced an invisible-entity class) and does NOT abort
|
|
12
|
+
* fatally. Instead it skips the unknown key (no write, no input mutation) and
|
|
13
|
+
* surfaces this record on the success value's `diagnostics[]` — observable in
|
|
14
|
+
* production (NOT NODE_ENV-gated), consumed by property access (no string parse):
|
|
15
|
+
*
|
|
16
|
+
* const r = worldInstantiateScene(world, handle);
|
|
17
|
+
* if (r.ok) for (const d of r.value.diagnostics)
|
|
18
|
+
* console.warn(`unknown field ${d.component}.${d.field} on localId ${d.localId}`);
|
|
19
|
+
*
|
|
20
|
+
* Direct `world.spawn` / `world.addComponent` / `Commands.spawn` stay fail-fast
|
|
21
|
+
* with `SpawnDataUnknownFieldError` — those are explicit API calls where a typo
|
|
22
|
+
* is a programming error, not loader-fed data.
|
|
23
|
+
*/
|
|
24
|
+
export type SceneInstantiateDiagnostic = {
|
|
25
|
+
/** Component name (schema key) the unknown field appeared under. */
|
|
26
|
+
readonly component: string;
|
|
27
|
+
/** The offending field name not declared in the component schema. */
|
|
28
|
+
readonly field: string;
|
|
29
|
+
/** LocalEntityId (within its owning SceneAsset) of the carrying entity. */
|
|
30
|
+
readonly localId: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Success value of `worldInstantiateScene`. `root` is the synthetic scene-root
|
|
34
|
+
* EntityHandle (carries `SceneInstance`); `diagnostics` is the (possibly empty)
|
|
35
|
+
* list of non-fatal unknown-field records aggregated across this scene and every
|
|
36
|
+
* recursively mounted sub-scene (C-R2). Empty array = no diagnostics.
|
|
37
|
+
*/
|
|
38
|
+
export type SceneInstantiateOk = {
|
|
39
|
+
readonly root: EntityHandle;
|
|
40
|
+
readonly diagnostics: readonly SceneInstantiateDiagnostic[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Success value of `worldInstantiateSceneFlat` — the "edit the scene itself"
|
|
44
|
+
* primitive. Unlike `instantiateScene`, NO synthetic SceneInstance root is
|
|
45
|
+
* minted and NO `ChildOf` is forced onto top-level members: the scene's own
|
|
46
|
+
* entities become plain top-level world entities whose hierarchy is exactly
|
|
47
|
+
* their authored `ChildOf` (an entity with no `ChildOf` stays a root). `roots`
|
|
48
|
+
* is the set of those top-level handles (own rootless entities + top-level
|
|
49
|
+
* mount carriers). Nested prefabs inside the scene STILL materialise as their
|
|
50
|
+
* own SceneInstance anchors (charter P4: instance == entity-with-SceneInstance)
|
|
51
|
+
* — only THIS scene is flat.
|
|
52
|
+
*/
|
|
53
|
+
export type SceneInstantiateFlatOk = {
|
|
54
|
+
readonly roots: EntityHandle[];
|
|
55
|
+
/**
|
|
56
|
+
* All mount carrier entities spawned while flattening this scene. These are
|
|
57
|
+
* separate from `roots`: carriers with an authored parent are not roots,
|
|
58
|
+
* but still delimit a nested prefab subtree for post-spawn hooks.
|
|
59
|
+
*/
|
|
60
|
+
readonly mountEntities: EntityHandle[];
|
|
61
|
+
readonly diagnostics: readonly SceneInstantiateDiagnostic[];
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* @internal Intermediate produced by `_spawnSceneMembers` and consumed by both
|
|
65
|
+
* the anchor finisher (`_instantiateSceneAsset`) and the flat finisher
|
|
66
|
+
* (`_instantiateSceneAssetFlat`). Holds everything the shared member-spawn
|
|
67
|
+
* (mounts recursion + own-entity spawn + deferred owned-parent wiring) computes,
|
|
68
|
+
* before either finisher decides whether to wrap the members in a synthetic
|
|
69
|
+
* SceneInstance root.
|
|
70
|
+
*/
|
|
71
|
+
export interface SceneMembersSpawn {
|
|
72
|
+
/** LocalEntityId → live Entity u32 (ENTITY_NULL_RAW for unspawned slots). */
|
|
73
|
+
readonly mapping: Uint32Array;
|
|
74
|
+
/** Reverse map live Entity → LocalEntityId for override / detach bookkeeping. */
|
|
75
|
+
readonly entityToLocalId: Map<EntityHandle, LocalEntityId>;
|
|
76
|
+
/** Own entities that carried no `ChildOf` — the scene's authored top-level roots. */
|
|
77
|
+
readonly rootEntities: EntityHandle[];
|
|
78
|
+
/** Mount carriers whose `mount.parent === undefined` (default-parented). */
|
|
79
|
+
readonly mountEntitiesNeedingRootParent: EntityHandle[];
|
|
80
|
+
/** Every mount carrier spawned by this scene, including explicitly parented carriers. */
|
|
81
|
+
readonly mountEntities: EntityHandle[];
|
|
82
|
+
/**
|
|
83
|
+
* The child anchor and mapping for each mount. Flat scene opening has no
|
|
84
|
+
* outer SceneInstance state to own parent-namespace mount overrides, so it
|
|
85
|
+
* records those overrides on this child anchor after the shared spawn pass.
|
|
86
|
+
*/
|
|
87
|
+
readonly mountInstances: readonly {
|
|
88
|
+
readonly mount: SceneInstanceMount;
|
|
89
|
+
readonly root: EntityHandle;
|
|
90
|
+
readonly mapping: Uint32Array;
|
|
91
|
+
}[];
|
|
92
|
+
/** `entities.length + mounts + Σ memberCount`, captured at instantiate-time. */
|
|
93
|
+
readonly totalSlots: number;
|
|
94
|
+
}
|
|
95
|
+
export type SceneAssetResolver = (source: number | string, parentHandle: Handle<'SceneAsset', 'shared'>) => Result<Handle<'SceneAsset', 'shared'>, unknown>;
|
|
96
|
+
/** @internal */
|
|
97
|
+
export declare function worldSetSceneAssetResolver(world: World, resolver: SceneAssetResolver): void;
|
|
98
|
+
/** @internal */
|
|
99
|
+
export declare function worldGetSceneAssetResolver(world: World): SceneAssetResolver | null;
|
|
100
|
+
/**
|
|
101
|
+
* Materialise a SceneAsset (and any nested SceneAsset references via
|
|
102
|
+
* `mounts[]`) into live entities. Returns the synthetic root Entity that
|
|
103
|
+
* carries the `SceneInstance` ECS component (charter P4: instance ==
|
|
104
|
+
* entity-with-SceneInstance).
|
|
105
|
+
*
|
|
106
|
+
* Recursion path is closed inside `_instantiateSceneRec(handle, parent,
|
|
107
|
+
* stack)` (D-3); cycle detection is fail-fast `pack-cyclic-reference +
|
|
108
|
+
* detail.kind:'mount-asset'` (D-1 mirror, plan-strategy §D-3). The
|
|
109
|
+
* caller-supplied `parent` flows to the synthetic root's `ChildOf` so the
|
|
110
|
+
* full sub-tree attaches under the AI user's host entity.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* const r = worldInstantiateScene(world, handle);
|
|
114
|
+
* if (!r.ok) return r;
|
|
115
|
+
* const { root, diagnostics } = r.value;
|
|
116
|
+
* for (const d of diagnostics) // C-R2: unknown-field records, non-fatal
|
|
117
|
+
* console.warn(`unknown field ${d.component}.${d.field} on localId ${d.localId}`);
|
|
118
|
+
* const inst = world.get(root, SceneInstance).value;
|
|
119
|
+
* const member = inst.mapping[0]; // first member entity
|
|
120
|
+
*/
|
|
121
|
+
export declare function worldInstantiateScene(world: World, handle: Handle<'SceneAsset', 'shared'>, parent?: EntityHandle): Result<SceneInstantiateOk, EcsError>;
|
|
122
|
+
/**
|
|
123
|
+
* Materialise a projected `SceneAsset` payload without exposing the temporary
|
|
124
|
+
* shared-ref handle to the caller. The World remains the owner of both the
|
|
125
|
+
* handle and the instantiated SceneInstance: the producer grant is released
|
|
126
|
+
* after the SceneInstance retains its source, including when instantiation
|
|
127
|
+
* fails. This is the payload-shaped counterpart to `worldInstantiateScene` for
|
|
128
|
+
* hosts that load a SceneAsset directly from the Engine AssetRegistry.
|
|
129
|
+
*/
|
|
130
|
+
export declare function worldInstantiateScenePayload(world: World, asset: SceneAsset, parent?: EntityHandle): Result<SceneInstantiateOk, EcsError>;
|
|
131
|
+
/**
|
|
132
|
+
* Materialise a SceneAsset FLAT — the "edit the scene itself" primitive.
|
|
133
|
+
* Unlike `instantiateScene`, this mints NO synthetic SceneInstance root and
|
|
134
|
+
* forces NO `ChildOf` onto top-level members: the scene's own entities become
|
|
135
|
+
* plain top-level world entities whose hierarchy is exactly their authored
|
|
136
|
+
* `ChildOf` (an entity with no `ChildOf` is a root). Use this to OPEN a scene
|
|
137
|
+
* for editing; use `instantiateScene` (anchor) at runtime / for nested
|
|
138
|
+
* prefabs where an instance boundary + override isolation is wanted.
|
|
139
|
+
*
|
|
140
|
+
* Nested prefabs referenced via `mounts[]` STILL materialise as their own
|
|
141
|
+
* SceneInstance anchors (charter P4 preserved) — only THIS top scene is flat.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* const r = worldInstantiateSceneFlat(world, handle);
|
|
145
|
+
* if (!r.ok) return r;
|
|
146
|
+
* const { roots, diagnostics } = r.value; // roots = top-level handles
|
|
147
|
+
*/
|
|
148
|
+
export declare function worldInstantiateSceneFlat(world: World, handle: Handle<'SceneAsset', 'shared'>): Result<SceneInstantiateFlatOk, EcsError>;
|
|
149
|
+
/**
|
|
150
|
+
* @internal Recursive helper carrying the cycle-detection stack. Sugar /
|
|
151
|
+
* other public callers must not see this mechanic — use `instantiateScene`
|
|
152
|
+
* (D-3 / charter P1).
|
|
153
|
+
*/
|
|
154
|
+
export declare function worldInstantiateSceneRec(world: World, handle: Handle<'SceneAsset', 'shared'>, parent: EntityHandle | undefined, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<EntityHandle, EcsError>;
|
|
155
|
+
/**
|
|
156
|
+
* @internal Resolve a SceneAsset handle through the SharedRefStore.
|
|
157
|
+
* The handle u32 is the SharedRefStore slot id (`world.allocSharedRef
|
|
158
|
+
* ('SceneAsset', asset)` is the producer; rc starts at 1, the SceneInstance
|
|
159
|
+
* spawn retains to rc=2 in M4 / w13). Errors propagate as EcsError so the
|
|
160
|
+
* instantiateScene chain returns a single closed union.
|
|
161
|
+
*/
|
|
162
|
+
export declare function worldResolveSceneAsset(world: World, handle: Handle<'SceneAsset', 'shared'>): Result<SceneAsset, EcsError>;
|
|
163
|
+
/**
|
|
164
|
+
* @internal Spawn one SceneAsset's members — the shared body of both scene
|
|
165
|
+
* finishers. Recurses into `mounts[]` (each nested prefab becomes its own
|
|
166
|
+
* SceneInstance anchor), spawns `entities[]` honouring their authored
|
|
167
|
+
* `ChildOf`, and wires deferred owned-parent mount edges. Does NOT create a
|
|
168
|
+
* synthetic root or force any `ChildOf` — that is the caller's (finisher's)
|
|
169
|
+
* job. `_instantiateSceneRec` owns cycle bookkeeping.
|
|
170
|
+
*/
|
|
171
|
+
export declare function worldSpawnSceneMembers(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<SceneMembersSpawn, EcsError>;
|
|
172
|
+
/**
|
|
173
|
+
* @internal Spawn one SceneAsset's entities + apply mounts recursively, then
|
|
174
|
+
* wrap them in a synthetic SceneInstance root (the anchor). This is the
|
|
175
|
+
* runtime / Play / nested-mount finisher (charter P4: instance ==
|
|
176
|
+
* entity-with-SceneInstance). Caller (`_instantiateSceneRec`) owns cycle
|
|
177
|
+
* bookkeeping.
|
|
178
|
+
*/
|
|
179
|
+
export declare function worldInstantiateSceneAsset(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, parent: EntityHandle | undefined, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<EntityHandle, EcsError>;
|
|
180
|
+
/**
|
|
181
|
+
* @internal Flat finisher — spawn one SceneAsset's members WITHOUT wrapping
|
|
182
|
+
* them in a synthetic SceneInstance root and WITHOUT forcing `ChildOf` onto
|
|
183
|
+
* top-level members. Used for "opening a scene to edit": the scene's own
|
|
184
|
+
* entities become plain top-level world entities whose hierarchy is exactly
|
|
185
|
+
* their authored `ChildOf`. Nested prefabs inside still materialise as their
|
|
186
|
+
* own SceneInstance anchors (the mount recursion in `_spawnSceneMembers` is
|
|
187
|
+
* always anchored). Returns the top-level handles (own rootless entities +
|
|
188
|
+
* top-level mount carriers).
|
|
189
|
+
*/
|
|
190
|
+
export declare function worldInstantiateSceneAssetFlat(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, stack: Set<number>, diagnostics: SceneInstantiateDiagnostic[]): Result<{
|
|
191
|
+
roots: EntityHandle[];
|
|
192
|
+
mountEntities: EntityHandle[];
|
|
193
|
+
}, EcsError>;
|
|
194
|
+
/** @internal Build ComponentData[] for one SceneEntity, remapping localIds.
|
|
195
|
+
*
|
|
196
|
+
* C-R2 (feat-20260622-s5 M6): unknown fields on a SceneAsset payload are NOT
|
|
197
|
+
* fatal. Unlike `world.spawn` (an explicit API call where a typo is a
|
|
198
|
+
* programming error -> `SpawnDataUnknownFieldError`), scene data is loader-fed
|
|
199
|
+
* and may carry a stale / deprecated / typo'd field. The remap below builds a
|
|
200
|
+
* fresh `remappedRaw` and simply SKIPS keys absent from the schema (no input
|
|
201
|
+
* mutation — the source `raw` is never deleted-from), recording each skipped
|
|
202
|
+
* key as a non-fatal `SceneInstantiateDiagnostic` into the passed accumulator.
|
|
203
|
+
* All known fields still write through, so one bad field cannot blank the
|
|
204
|
+
* entity or the scene (C-AC-02/03/04).
|
|
205
|
+
*/
|
|
206
|
+
export declare function worldBuildSceneEntityComponentDatas(world: World, node: import('@forgeax/engine-types').SceneEntity, mapping: Uint32Array, diagnostics: SceneInstantiateDiagnostic[]): Result<ComponentData[], EcsError>;
|
|
207
|
+
/**
|
|
208
|
+
* @internal feat-20260713 M2 / w8: apply one MountOverride to a live member
|
|
209
|
+
* entity column. The `field?` shape is the add-or-patch discriminant:
|
|
210
|
+
*
|
|
211
|
+
* - `field` present -> PATCH one field: `world.set(member, comp, {[field]:
|
|
212
|
+
* value})`. Omitted fields keep their authored / existing values.
|
|
213
|
+
* - `field` absent -> ADD/UPSERT the whole component: `value` is the
|
|
214
|
+
* per-field value map for `comp`. When the member already carries `comp`
|
|
215
|
+
* it is upserted (set-over each supplied field + schema defaults for the
|
|
216
|
+
* omitted ones — the whole component is rewritten from the value map +
|
|
217
|
+
* defaults, never a `component-already-present` error). When absent it is
|
|
218
|
+
* added fresh via `addComponent` (fillComponentDefaults fills omitted
|
|
219
|
+
* fields). The value-map is fed through `fillComponentDefaults` so the
|
|
220
|
+
* add and upsert paths write byte-identical rows.
|
|
221
|
+
*
|
|
222
|
+
* Component registration + value-key validation happened at
|
|
223
|
+
* `_validateMountOverrides` (fail-fast before any spawn); by this point the
|
|
224
|
+
* comp resolves through the World-local catalog and the value keys are schema-valid.
|
|
225
|
+
* still guards defensively (an unregistered comp is a no-op skip, matching
|
|
226
|
+
* the prior field-patch behaviour). Returns the underlying set / addComponent
|
|
227
|
+
* Result so a shared-field value gate (D-4) or any other write error
|
|
228
|
+
* propagates unchanged.
|
|
229
|
+
*/
|
|
230
|
+
export declare function worldApplyMountOverride(world: World, member: EntityHandle, ov: MountOverride): Result<void, EcsError>;
|
|
231
|
+
/**
|
|
232
|
+
* @internal R2/B-3 + R2/B-4: validate `mount.overrides[]` BEFORE any
|
|
233
|
+
* spawn so a malformed override fails fast with no observable side
|
|
234
|
+
* effects (charter P3 explicit-failure). Two checks:
|
|
235
|
+
*
|
|
236
|
+
* 1. `override.localId` must address a slot inside the parent-namespace
|
|
237
|
+
* member window `[memberFirst, memberFirst + memberCount)` (AC-06).
|
|
238
|
+
* 2. `override.field` must exist in the resolved component schema
|
|
239
|
+
* (AC-07). When the component is unregistered we cannot validate the
|
|
240
|
+
* field shape; let the existing fall-through path proceed (the
|
|
241
|
+
* catalog guard inside the override-application loop
|
|
242
|
+
* will skip the write).
|
|
243
|
+
*/
|
|
244
|
+
export declare function worldValidateMountOverrides(world: World, mount: SceneInstanceMount): Result<void, EcsError>;
|
|
245
|
+
/** @internal Spawn the mount-entity slot carrying mount.components (if any).
|
|
246
|
+
*
|
|
247
|
+
* R2/B-1: the mount entity is a structural intermediate in the ChildOf
|
|
248
|
+
* chain `cube -> innerSyntheticRoot -> mountEntity -> outerSyntheticRoot`,
|
|
249
|
+
* so it MUST carry Transform whenever Transform is registered (mirrors
|
|
250
|
+
* the D-V-0 synthetic-root invariant). Otherwise propagateTransforms
|
|
251
|
+
* walking the chain hits a Transform-less parent and emits per-frame
|
|
252
|
+
* `RhiError(hierarchy-broken)` (verify R1 root cause of the
|
|
253
|
+
* hello-scene-nesting demo black frames).
|
|
254
|
+
*/
|
|
255
|
+
export declare function worldSpawnMountEntity(world: World, mount: SceneInstanceMount, mapping: Uint32Array, diagnostics: SceneInstantiateDiagnostic[]): Result<EntityHandle, EcsError>;
|
|
256
|
+
/** @internal Resolve mount.source through the wired SceneAssetResolver. */
|
|
257
|
+
export declare function worldResolveMountSource(world: World, source: number | string, parentHandle: Handle<'SceneAsset', 'shared'>): Result<Handle<'SceneAsset', 'shared'>, EcsError>;
|
|
258
|
+
/** @internal Convert mount.overrides Map shape to the SceneInstanceState shape.
|
|
259
|
+
*
|
|
260
|
+
* feat-20260713 M1 / w4: `field` is optional (add-or-patch discriminant). In
|
|
261
|
+
* M1 only the field-patch form reaches this builder (the component-add form
|
|
262
|
+
* fails fast in the apply loops); the record type stays `field?: string` so
|
|
263
|
+
* the M2 add path can flow through untouched. `exactOptionalPropertyTypes`
|
|
264
|
+
* forbids writing an explicit `field: undefined`, so omit the key when absent.
|
|
265
|
+
*/
|
|
266
|
+
export declare function worldMountOverridesToStateMap(src: Map<LocalEntityId, Map<string, MountOverride>>): Map<LocalEntityId, Map<string, {
|
|
267
|
+
comp: string;
|
|
268
|
+
field?: string;
|
|
269
|
+
value: unknown;
|
|
270
|
+
}>>;
|
|
271
|
+
/** @internal Set the payload of an already-allocated SceneInstance state ref. */
|
|
272
|
+
export declare function worldSetUniqueRefPayload<T>(world: World, handle: Handle<string, 'unique'>, payload: T): void;
|
|
273
|
+
/**
|
|
274
|
+
* @internal Resolve the SceneInstanceState payload behind the
|
|
275
|
+
* `SceneInstance.state` ref column on `root`. Returns Err when `root`
|
|
276
|
+
* does not carry SceneInstance or the ref slot is dead.
|
|
277
|
+
*/
|
|
278
|
+
export declare function worldResolveSceneInstanceStatePayload(world: World, root: EntityHandle): Result<SceneInstanceStatePayload, EcsError>;
|
|
279
|
+
/**
|
|
280
|
+
* Public sugar — get the SceneInstanceState payload (Map / Set view) for
|
|
281
|
+
* `root`. Equivalent to `world.get(root, SceneInstance)` followed by a
|
|
282
|
+
* managed-ref resolution; provided so AI users do not have to learn the
|
|
283
|
+
* `ref<T>` slot resolution mechanic for the common read path.
|
|
284
|
+
*/
|
|
285
|
+
export declare function worldGetSceneInstanceState(world: World, root: EntityHandle): Result<SceneInstanceStatePayload, EcsError>;
|
|
286
|
+
/**
|
|
287
|
+
* Despawn a SceneInstance root + all its members. `opts.keepDetached`
|
|
288
|
+
* preserves members marked via `worldDetachSceneMember` (plan-strategy
|
|
289
|
+
* §D-5). Returns the count of entities actually despawned (root + each
|
|
290
|
+
* non-detached member).
|
|
291
|
+
*
|
|
292
|
+
* For a plain entity (no SceneInstance), behaviour matches
|
|
293
|
+
* `world.despawn(entity)` followed by `despawnDescendants(entity)` — i.e.
|
|
294
|
+
* `keepDetached` is a no-op.
|
|
295
|
+
*/
|
|
296
|
+
export declare function worldDespawnScene(world: World, root: EntityHandle, opts?: {
|
|
297
|
+
keepDetached?: boolean;
|
|
298
|
+
}): Result<number, EcsError>;
|
|
299
|
+
/**
|
|
300
|
+
* Despawn every descendant of `root` reachable through Children mirror /
|
|
301
|
+
* SceneInstance.mapping. `opts.keepDetached` is honoured only when `root`
|
|
302
|
+
* carries a SceneInstance (otherwise the option is ignored — there is no
|
|
303
|
+
* detached set on a plain entity).
|
|
304
|
+
*
|
|
305
|
+
* Returns the count of entities despawned. The `root` itself is NOT
|
|
306
|
+
* despawned (that is `despawnScene`'s extra step).
|
|
307
|
+
*/
|
|
308
|
+
export declare function worldDespawnDescendants(world: World, root: EntityHandle, opts?: {
|
|
309
|
+
keepDetached?: boolean;
|
|
310
|
+
}): Result<number, EcsError>;
|
|
311
|
+
/**
|
|
312
|
+
* Write a runtime override to a member entity belonging to `root`. Routes
|
|
313
|
+
* through `world.set(member, comp, { [field]: value })` after an entity-
|
|
314
|
+
* scope guard so cross-instance writes fail-fast. Type-mismatch surfaces
|
|
315
|
+
* `EcsErrorCode = 'scene-override-type-mismatch'` (D-9).
|
|
316
|
+
*/
|
|
317
|
+
export declare function worldSetSceneOverride<S extends ComponentSchema>(world: World, root: EntityHandle, member: EntityHandle, component: Component<string, S>, field: keyof ShapeOf<S> & string, value: unknown): Result<void, EcsError>;
|
|
318
|
+
/**
|
|
319
|
+
* Drop a runtime override (and any mount-time override for the same
|
|
320
|
+
* (member, comp, field) triple); roll the live column value back to the
|
|
321
|
+
* source SceneAsset's layer-1 explicit value (M2 v1 — M3+ widens to layer
|
|
322
|
+
* 2/3 defaults via fillComponentDefaults).
|
|
323
|
+
*/
|
|
324
|
+
export declare function worldRemoveSceneOverride<S extends ComponentSchema>(world: World, root: EntityHandle, member: EntityHandle, component: Component<string, S>, field: keyof ShapeOf<S> & string): Result<void, EcsError>;
|
|
325
|
+
/** Mark a member entity detached. Idempotent (set semantics). */
|
|
326
|
+
export declare function worldDetachSceneMember(world: World, root: EntityHandle, member: EntityHandle): Result<void, EcsError>;
|
|
327
|
+
/** Clear a detached mark. Idempotent (set semantics). */
|
|
328
|
+
export declare function worldReattachSceneMember(world: World, root: EntityHandle, member: EntityHandle): Result<void, EcsError>;
|
|
329
|
+
/**
|
|
330
|
+
* Get the SceneAsset handle a SceneInstance root was instantiated from.
|
|
331
|
+
* Returns Err on a plain entity (no SceneInstance component).
|
|
332
|
+
*/
|
|
333
|
+
export declare function worldGetSceneAssetForInstance(world: World, root: EntityHandle): Result<Handle<'SceneAsset', 'shared'>, EcsError>;
|
|
334
|
+
/** @internal Structural payload behind `SceneInstance.state` ref column. */
|
|
335
|
+
export interface SceneInstanceStatePayload {
|
|
336
|
+
readonly source: Handle<'SceneAsset', 'shared'>;
|
|
337
|
+
readonly entityToLocalId: Map<EntityHandle, LocalEntityId>;
|
|
338
|
+
readonly detachedLocalIds: Set<LocalEntityId>;
|
|
339
|
+
readonly overrides: Map<LocalEntityId, Map<string, {
|
|
340
|
+
readonly comp: string;
|
|
341
|
+
readonly field?: string;
|
|
342
|
+
readonly value: unknown;
|
|
343
|
+
}>>;
|
|
344
|
+
readonly rootEntities: EntityHandle[];
|
|
345
|
+
/** Synthetic roots of recursively mounted SceneAssets owned by this instance. */
|
|
346
|
+
readonly mountRoots: EntityHandle[];
|
|
347
|
+
readonly totalSlots: number;
|
|
348
|
+
readonly mountTimeOverrides: readonly MountOverride[];
|
|
349
|
+
}
|
|
350
|
+
//# sourceMappingURL=scene-instances.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-instances.d.ts","sourceRoot":"","sources":["../../src/instances/scene-instances.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAiBrD,wBAAgB,WAAW,IAAI,MAAM,CASpC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type EntityHandle, type World } from '@forgeax/engine-ecs';
|
|
2
|
+
import type { SceneErrorCode, SceneErrorDetail } from '../errors';
|
|
3
|
+
export interface SceneHierarchyDiagnostic {
|
|
4
|
+
readonly code: SceneErrorCode;
|
|
5
|
+
readonly expected: string;
|
|
6
|
+
readonly hint: string;
|
|
7
|
+
readonly detail: SceneErrorDetail;
|
|
8
|
+
}
|
|
9
|
+
export interface SceneHierarchySnapshot {
|
|
10
|
+
readonly parentOf: ReadonlyMap<EntityHandle, EntityHandle>;
|
|
11
|
+
readonly diagnostics: readonly SceneHierarchyDiagnostic[];
|
|
12
|
+
getParent(entity: EntityHandle): EntityHandle | undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Build the only World-local projection of ChildOf parent facts. */
|
|
15
|
+
export declare function projectHierarchy(world: World): SceneHierarchySnapshot;
|
|
16
|
+
//# sourceMappingURL=hierarchy-projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hierarchy-projection.d.ts","sourceRoot":"","sources":["../../src/systems/hierarchy-projection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,YAAY,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAC3D,QAAQ,CAAC,WAAW,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC1D,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;CAC3D;AAqCD,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,sBAAsB,CA+ErE"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { projectHierarchy, type SceneHierarchyDiagnostic, type SceneHierarchySnapshot, } from './hierarchy-projection';
|
|
2
|
+
export { PROPAGATE_TRANSFORMS_SYSTEM, propagateTransforms, registerPropagateTransforms, TransformSet, } from './propagate-transforms';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/systems/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,2BAA2B,EAC3B,YAAY,GACb,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type SystemHandle, type World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { type Result } from '@forgeax/engine-types';
|
|
3
|
+
import { SceneError } from '../errors';
|
|
4
|
+
import { type SceneHierarchySnapshot } from './hierarchy-projection';
|
|
5
|
+
export declare const PROPAGATE_TRANSFORMS_SYSTEM: "propagateTransforms";
|
|
6
|
+
export declare const PROPAGATE_TRANSFORMS_FIXED_SYSTEM: "propagateTransformsFixed";
|
|
7
|
+
export declare const TransformSet: import("@forgeax/engine-ecs").SystemSet;
|
|
8
|
+
export declare const TransformFixedSet: import("@forgeax/engine-ecs").SystemSet;
|
|
9
|
+
export declare function propagateTransforms(world: World, _hierarchy?: SceneHierarchySnapshot): Result<void, SceneError>;
|
|
10
|
+
export declare const PropagateTransforms: SystemHandle<readonly []>;
|
|
11
|
+
export declare const PropagateTransformsFixed: SystemHandle<readonly []>;
|
|
12
|
+
export declare function registerPropagateTransforms(world: World, options?: {
|
|
13
|
+
beforeSystemName?: string;
|
|
14
|
+
}): () => void;
|
|
15
|
+
//# sourceMappingURL=propagate-transforms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propagate-transforms.d.ts","sourceRoot":"","sources":["../../src/systems/propagate-transforms.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,YAAY,EAEjB,KAAK,KAAK,EACX,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEvF,eAAO,MAAM,2BAA2B,EAAG,qBAA8B,CAAC;AAC1E,eAAO,MAAM,iCAAiC,EAAG,0BAAmC,CAAC;AACrF,eAAO,MAAM,YAAY,yCAAyC,CAAC;AACnE,eAAO,MAAM,iBAAiB,yCAA+C,CAAC;AAgM9E,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,UAAU,CAAC,EAAE,sBAAsB,GAClC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,CA2F1B;AAED,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,SAAS,EAAE,CAOxD,CAAC;AAEH,eAAO,MAAM,wBAAwB,EAAE,YAAY,CAAC,SAAS,EAAE,CAI7D,CAAC;AAEH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,KAAK,EACZ,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,MAAM,IAAI,CA6CZ"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forgeax/engine-scene",
|
|
3
|
+
"version": "0.0.0-dev.8d955ade1c79",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"description": "Scene identity, hierarchy, and world transform propagation.",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.mjs",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@forgeax/engine-ecs": "0.0.0-dev.8d955ade1c79",
|
|
26
|
+
"@forgeax/engine-math": "0.0.0-dev.8d955ade1c79",
|
|
27
|
+
"@forgeax/engine-plugin": "0.0.0-dev.8d955ade1c79"
|
|
28
|
+
},
|
|
29
|
+
"forgeax": {
|
|
30
|
+
"metrics": {
|
|
31
|
+
"bundle-size": {
|
|
32
|
+
"enabled": true,
|
|
33
|
+
"path": "dist/index.mjs",
|
|
34
|
+
"compression": "gzip"
|
|
35
|
+
},
|
|
36
|
+
"fps": {
|
|
37
|
+
"enabled": false,
|
|
38
|
+
"reason": "headless scene package"
|
|
39
|
+
},
|
|
40
|
+
"bench": {
|
|
41
|
+
"enabled": false,
|
|
42
|
+
"reason": "scene propagation covered by unit tests"
|
|
43
|
+
},
|
|
44
|
+
"gate": {
|
|
45
|
+
"enabled": false,
|
|
46
|
+
"reason": "no package binary gate"
|
|
47
|
+
},
|
|
48
|
+
"spike-report": {
|
|
49
|
+
"enabled": false,
|
|
50
|
+
"reason": "not a spike package"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"test": "vitest run"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { defineComponent, World } from '@forgeax/engine-ecs';
|
|
2
|
+
import type { SceneAsset } from '@forgeax/engine-types';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { ChildOf, Children, worldDespawnScene, worldInstantiateScenePayload } from '../index';
|
|
5
|
+
|
|
6
|
+
const SceneInstance = defineComponent('SceneInstance', {
|
|
7
|
+
source: { type: 'shared<SceneAsset>' },
|
|
8
|
+
mapping: { type: 'array<entity>' },
|
|
9
|
+
state: { type: 'unique<SceneInstanceState>' },
|
|
10
|
+
});
|
|
11
|
+
const SceneMarker = defineComponent('ScenePayloadMarker', { value: 'i32' });
|
|
12
|
+
|
|
13
|
+
function registerSceneWorld(world: World): void {
|
|
14
|
+
for (const component of [SceneInstance, ChildOf, Children, SceneMarker]) {
|
|
15
|
+
world.components.register(component).unwrap();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function scene(): SceneAsset {
|
|
20
|
+
return {
|
|
21
|
+
kind: 'scene',
|
|
22
|
+
entities: [
|
|
23
|
+
{
|
|
24
|
+
localId: 0 as never,
|
|
25
|
+
components: { ScenePayloadMarker: { value: 7 } },
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('scene payload ownership', () => {
|
|
32
|
+
it('retains the payload for the mounted instance and releases it on despawn', () => {
|
|
33
|
+
const world = new World();
|
|
34
|
+
registerSceneWorld(world);
|
|
35
|
+
|
|
36
|
+
const result = worldInstantiateScenePayload(world, scene());
|
|
37
|
+
|
|
38
|
+
expect(result.ok).toBe(true);
|
|
39
|
+
expect(world.sharedRefs._liveCount()).toBe(1);
|
|
40
|
+
if (!result.ok) return;
|
|
41
|
+
expect(worldDespawnScene(world, result.value.root).ok).toBe(true);
|
|
42
|
+
expect(world.sharedRefs._liveCount()).toBe(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('releases the temporary producer grant when materialisation fails', () => {
|
|
46
|
+
const world = new World();
|
|
47
|
+
registerSceneWorld(world);
|
|
48
|
+
|
|
49
|
+
const result = worldInstantiateScenePayload(world, {
|
|
50
|
+
kind: 'scene',
|
|
51
|
+
entities: [{ localId: 0 as never, components: { MissingComponent: { value: 1 } } }],
|
|
52
|
+
} as SceneAsset);
|
|
53
|
+
|
|
54
|
+
expect(result.ok).toBe(false);
|
|
55
|
+
expect(world.sharedRefs._liveCount()).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { ChildOf, Children, Name, Transform } from '../index';
|
|
4
|
+
|
|
5
|
+
describe('scene component roster', () => {
|
|
6
|
+
it('exports exactly the four scene components', () => {
|
|
7
|
+
expect(Object.keys({ Name, Transform, ChildOf, Children }).sort()).toEqual([
|
|
8
|
+
'ChildOf',
|
|
9
|
+
'Children',
|
|
10
|
+
'Name',
|
|
11
|
+
'Transform',
|
|
12
|
+
]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('registers and spawns in a headless world', () => {
|
|
16
|
+
const world = new World();
|
|
17
|
+
const entity = world
|
|
18
|
+
.spawn(
|
|
19
|
+
{ component: Name, data: { value: 'root' } },
|
|
20
|
+
{ component: Transform, data: {} },
|
|
21
|
+
{ component: Children, data: {} },
|
|
22
|
+
)
|
|
23
|
+
.unwrap();
|
|
24
|
+
expect(world.get(entity, Name).unwrap().value).toBe('root');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type * as SceneSurface from '../index';
|
|
2
|
+
|
|
3
|
+
type DiagnosticSurface = Pick<SceneSurface.SceneHierarchyDiagnostic, 'code' | 'detail'>;
|
|
4
|
+
const diagnosticSurface: DiagnosticSurface | undefined = undefined;
|
|
5
|
+
void diagnosticSurface;
|
|
6
|
+
|
|
7
|
+
// These names were duplicate projections of the SceneError code/detail owners.
|
|
8
|
+
// @ts-expect-error hierarchy diagnostics use SceneErrorCode directly.
|
|
9
|
+
export type RemovedDiagnosticCode = SceneSurface['SceneHierarchyDiagnosticCode'];
|
|
10
|
+
// @ts-expect-error hierarchy diagnostics use SceneErrorDetail directly.
|
|
11
|
+
export type RemovedDiagnosticDetail = SceneSurface['SceneHierarchyDiagnosticDetail'];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { componentSchema } from '@forgeax/engine-ecs/internal';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('scene ChildOf module', () => {
|
|
5
|
+
it('registers its Children mirror when imported directly', async () => {
|
|
6
|
+
vi.resetModules();
|
|
7
|
+
const { ChildOf } = await import('../components/child-of');
|
|
8
|
+
const { Children } = await import('../components/children');
|
|
9
|
+
|
|
10
|
+
expect(ChildOf.name).toBe('ChildOf');
|
|
11
|
+
expect(componentSchema(Children).entities).toBe('array<entity>');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EntityHandle, World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { ChildOf } from '../../index';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scene-owned malformed-edge fixture. The public relationship write accepts
|
|
6
|
+
* stale handles as data; the projection then owns the liveness diagnostic.
|
|
7
|
+
* Keeping this fixture on `world.set` avoids a private ECS graph seam.
|
|
8
|
+
*/
|
|
9
|
+
export function setMalformedParentEdge(
|
|
10
|
+
world: World,
|
|
11
|
+
child: EntityHandle,
|
|
12
|
+
parent: EntityHandle,
|
|
13
|
+
): void {
|
|
14
|
+
world.set(child, ChildOf, { parent }).unwrap();
|
|
15
|
+
}
|