import { type Component, type ComponentData, type ComponentSchema, type EcsError, type EntityHandle, type ShapeOf, type World } from '@forgeax/engine-ecs'; import type { Handle, LocalEntityId, SceneAsset, SceneEntityRef } from '@forgeax/engine-types'; import { type Result } from '@forgeax/engine-types'; import { type CompiledSceneAsset, type CompiledSceneEntity } from './keyed.js'; import type { MountOverride, SceneInstanceMount } from './runtime-types.js'; import { type SceneInstanceState } from './state.js'; export type { SceneInstanceOverrideRecord, SceneInstanceState } from './state.js'; /** * Legacy diagnostic shape retained on the scene-instantiation result for * non-blocking runtime observations. SceneAsset schema violations are * rejected by the keyed compiler before this result is produced; no authored * unknown-field record is emitted by the current path. * * const r = worldInstantiateScene(world, handle); * if (r.ok) for (const d of r.value.diagnostics) * console.warn('scene diagnostic', d); * * Direct `world.spawn` / `world.addComponent` / `Commands.spawn` remain * fail-fast with `SpawnDataUnknownFieldError`. */ export type SceneInstantiateDiagnostic = { /** Component name associated with the observation. */ readonly component: string; /** Field associated with the observation. */ readonly field: string; /** LocalEntityId within the owning SceneAsset, when applicable. */ readonly localId: number; }; /** * Success value of `worldInstantiateScene`. `root` is the synthetic scene-root * EntityHandle (carries `SceneInstance`); `diagnostics` contains only * non-blocking runtime observations. Schema-invalid authored fields fail before * an entity is created. */ export type SceneInstantiateOk = { readonly root: EntityHandle; readonly diagnostics: readonly SceneInstantiateDiagnostic[]; }; /** * Success value of `worldInstantiateSceneFlat` — the "edit the scene itself" * primitive. Unlike `instantiateScene`, NO synthetic SceneInstance root is * minted and NO `ChildOf` is forced onto top-level members: the scene's own * entities become plain top-level world entities whose hierarchy is exactly * their authored `ChildOf` (an entity with no `ChildOf` stays a root). `roots` * is the set of those top-level handles (own rootless entities + top-level * mount carriers). Nested prefabs inside the scene STILL materialise as their * own SceneInstance anchors (charter P4: instance == entity-with-SceneInstance) * — only THIS scene is flat. */ export type SceneInstantiateFlatOk = { readonly roots: EntityHandle[]; /** * All mount carrier entities spawned while flattening this scene. These are * separate from `roots`: carriers with an authored parent are not roots, * but still delimit a nested prefab subtree for post-spawn hooks. */ readonly mountEntities: EntityHandle[]; readonly diagnostics: readonly SceneInstantiateDiagnostic[]; }; /** * @internal Intermediate produced by `_spawnSceneMembers` and consumed by both * the anchor finisher (`_instantiateSceneAsset`) and the flat finisher * (`_instantiateSceneAssetFlat`). Holds everything the shared member-spawn * (mounts recursion + own-entity spawn + deferred owned-parent wiring) computes, * before either finisher decides whether to wrap the members in a synthetic * SceneInstance root. */ export interface SceneMembersSpawn { /** LocalEntityId → live Entity u32 (ENTITY_NULL_RAW for unspawned slots). */ readonly mapping: Uint32Array; /** Reverse map live Entity → LocalEntityId for override / detach bookkeeping. */ readonly entityToLocalId: Map; /** Own entities that carried no `ChildOf` — the scene's authored top-level roots. */ readonly rootEntities: EntityHandle[]; /** Mount carriers whose `mount.parent === undefined` (default-parented). */ readonly mountEntitiesNeedingRootParent: EntityHandle[]; /** Every mount carrier spawned by this scene, including explicitly parented carriers. */ readonly mountEntities: EntityHandle[]; /** * The child anchor and mapping for each mount. Flat scene opening has no * outer SceneInstance state to own parent-namespace mount overrides, so it * records those overrides on this child anchor after the shared spawn pass. */ readonly mountInstances: readonly { readonly mount: SceneInstanceMount; readonly root: EntityHandle; readonly mapping: Uint32Array; readonly key?: string; }[]; /** `entities.length + mounts + Σ memberCount`, captured at instantiate-time. */ readonly totalSlots: number; } export type SceneAssetResolver = (source: number | string, parentHandle: Handle<'SceneAsset', 'shared'>) => Result, unknown>; /** @internal */ export declare function worldSetSceneAssetResolver(world: World, resolver: SceneAssetResolver): void; /** @internal */ export declare function worldGetSceneAssetResolver(world: World): SceneAssetResolver | null; /** * Materialise a SceneAsset (and any nested SceneAsset references via * `mounts[]`) into live entities. Returns the synthetic root Entity that * carries the `SceneInstance` ECS component (charter P4: instance == * entity-with-SceneInstance). * * Recursion path is closed inside `_instantiateSceneRec(handle, parent, * stack)` (D-3); cycle detection is fail-fast `pack-cyclic-reference + * detail.kind:'mount-asset'` (D-1 mirror, plan-strategy §D-3). The * caller-supplied `parent` flows to the synthetic root's `ChildOf` so the * full sub-tree attaches under the AI user's host entity. * * @example * const r = worldInstantiateScene(world, handle); * if (!r.ok) return r; * const { root, diagnostics } = r.value; * for (const d of diagnostics) // non-blocking runtime observations * console.warn('scene diagnostic', d); * const inst = world.get(root, SceneInstance).value; * const member = inst.mapping[0]; // first member entity */ export declare function worldInstantiateScene(world: World, handle: Handle<'SceneAsset', 'shared'>, parent?: EntityHandle, sceneSourceKey?: string): Result; /** * Materialise a projected `SceneAsset` payload without exposing the temporary * shared-ref handle to the caller. The World remains the owner of both the * handle and the instantiated SceneInstance: the producer grant is released * after the SceneInstance retains its source, including when instantiation * fails. This is the payload-shaped counterpart to `worldInstantiateScene` for * hosts that load a SceneAsset directly from the Engine AssetRegistry. */ export declare function worldInstantiateScenePayload(world: World, asset: SceneAsset, parent?: EntityHandle): Result; /** * Materialise a SceneAsset FLAT — the "edit the scene itself" primitive. * Unlike `instantiateScene`, this mints NO synthetic SceneInstance root and * forces NO `ChildOf` onto top-level members: the scene's own entities become * plain top-level world entities whose hierarchy is exactly their authored * `ChildOf` (an entity with no `ChildOf` is a root). Use this to OPEN a scene * for editing; use `instantiateScene` (anchor) at runtime / for nested * prefabs where an instance boundary + override isolation is wanted. * * Nested prefabs referenced via `mounts[]` STILL materialise as their own * SceneInstance anchors (charter P4 preserved) — only THIS top scene is flat. * * @example * const r = worldInstantiateSceneFlat(world, handle); * if (!r.ok) return r; * const { roots, diagnostics } = r.value; // roots = top-level handles */ export declare function worldInstantiateSceneFlat(world: World, handle: Handle<'SceneAsset', 'shared'>): Result; /** * @internal Recursive helper carrying the cycle-detection stack. Sugar / * other public callers must not see this mechanic — use `instantiateScene` * (D-3 / charter P1). */ export declare function worldInstantiateSceneRec(world: World, handle: Handle<'SceneAsset', 'shared'>, parent: EntityHandle | undefined, stack: Set, diagnostics: SceneInstantiateDiagnostic[], instanceKey?: string, sceneSourceKey?: string): Result; /** * @internal Resolve a SceneAsset handle through the SharedRefStore. * The handle u32 is the SharedRefStore slot id (`world.allocSharedRef * ('SceneAsset', asset)` is the producer; rc starts at 1, the SceneInstance * spawn retains to rc=2 in M4 / w13). Errors propagate as EcsError so the * instantiateScene chain returns a single closed union. */ export declare function worldResolveSceneAsset(world: World, handle: Handle<'SceneAsset', 'shared'>): Result; /** * @internal Spawn one SceneAsset's members — the shared body of both scene * finishers. Recurses into `mounts[]` (each nested prefab becomes its own * SceneInstance anchor), spawns `entities[]` honouring their authored * `ChildOf`, and wires deferred owned-parent mount edges. Does NOT create a * synthetic root or force any `ChildOf` — that is the caller's (finisher's) * job. `_instantiateSceneRec` owns cycle bookkeeping. */ export declare function worldSpawnSceneMembers(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: CompiledSceneAsset, stack: Set, diagnostics: SceneInstantiateDiagnostic[], mountKeys?: ReadonlyMap): Result; /** * @internal Spawn one SceneAsset's entities + apply mounts recursively, then * wrap them in a synthetic SceneInstance root (the anchor). This is the * runtime / Play / nested-mount finisher (charter P4: instance == * entity-with-SceneInstance). Caller (`_instantiateSceneRec`) owns cycle * bookkeeping. */ export declare function worldInstantiateSceneAsset(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, parent: EntityHandle | undefined, stack: Set, diagnostics: SceneInstantiateDiagnostic[], instanceKey?: string, sceneSourceKey?: string): Result; /** * @internal Flat finisher — spawn one SceneAsset's members WITHOUT wrapping * them in a synthetic SceneInstance root and WITHOUT forcing `ChildOf` onto * top-level members. Used for "opening a scene to edit": the scene's own * entities become plain top-level world entities whose hierarchy is exactly * their authored `ChildOf`. Nested prefabs inside still materialise as their * own SceneInstance anchors (the mount recursion in `_spawnSceneMembers` is * always anchored). Returns the top-level handles (own rootless entities + * top-level mount carriers). */ export declare function worldInstantiateSceneAssetFlat(world: World, handle: Handle<'SceneAsset', 'shared'>, asset: SceneAsset, stack: Set, diagnostics: SceneInstantiateDiagnostic[]): Result<{ roots: EntityHandle[]; mountEntities: EntityHandle[]; }, EcsError>; /** @internal Build ComponentData[] for one SceneEntity, remapping localIds. * * SceneAsset payloads use the same schema contract as explicit ECS writes. * Unknown fields fail before the first entity is spawned, with the component * schema's structured error. The source object is never mutated. */ export declare function worldBuildSceneEntityComponentDatas(world: World, node: CompiledSceneEntity, mapping: Uint32Array, _diagnostics: SceneInstantiateDiagnostic[]): Result; /** * @internal feat-20260713 M2 / w8: apply one MountOverride to a live member * entity column. The `field?` shape is the add-or-patch discriminant: * * - `field` present -> PATCH one field: `world.set(member, comp, {[field]: * value})`. Omitted fields keep their authored / existing values. * - `field` absent -> ADD/UPSERT the whole component: `value` is the * per-field value map for `comp`. When the member already carries `comp` * it is upserted (set-over each supplied field + schema defaults for the * omitted ones — the whole component is rewritten from the value map + * defaults, never a `component-already-present` error). When absent it is * added fresh via `addComponent` (fillComponentDefaults fills omitted * fields). The value-map is fed through `fillComponentDefaults` so the * add and upsert paths write byte-identical rows. * * Component registration + value-key validation happened at * `_validateMountOverrides` (fail-fast before any spawn); by this point the * comp resolves through the World-local catalog and the value keys are schema-valid. * still guards defensively (an unregistered comp is a no-op skip, matching * the prior field-patch behaviour). Returns the underlying set / addComponent * Result so a shared-field value gate (D-4) or any other write error * propagates unchanged. */ export declare function worldApplyMountOverride(world: World, member: EntityHandle, ov: MountOverride): Result; /** * @internal R2/B-3 + R2/B-4: validate `mount.overrides[]` BEFORE any * spawn so a malformed override fails fast with no observable side * effects (charter P3 explicit-failure). Two checks: * * 1. `override.localId` must address a slot inside the parent-namespace * member window `[memberFirst, memberFirst + memberCount)` (AC-06). * 2. `override.field` must exist in the resolved component schema * (AC-07). When the component is unregistered we cannot validate the * field shape; let the existing fall-through path proceed (the * catalog guard inside the override-application loop * will skip the write). */ export declare function worldValidateMountOverrides(world: World, mount: SceneInstanceMount): Result; /** @internal Spawn the mount-entity slot carrying mount.components (if any). * * R2/B-1: the mount entity is a structural intermediate in the ChildOf * chain `cube -> innerSyntheticRoot -> mountEntity -> outerSyntheticRoot`, * so it MUST carry Transform whenever Transform is registered (mirrors * the D-V-0 synthetic-root invariant). Otherwise propagateTransforms * expanding the chain hits a Transform-less parent and emits per-frame * `RhiError(hierarchy-broken)` (verify R1 root cause of the * hello-scene-nesting demo black frames). */ export declare function worldSpawnMountEntity(world: World, mount: SceneInstanceMount, mapping: Uint32Array, diagnostics: SceneInstantiateDiagnostic[]): Result; /** @internal Resolve mount.source through the wired SceneAssetResolver. */ export declare function worldResolveMountSource(world: World, source: number | string, parentHandle: Handle<'SceneAsset', 'shared'>): Result, EcsError>; /** * Public sugar — get the SceneInstanceState (Map / Set view) for * `root`. Equivalent to `world.get(root, SceneInstance)` followed by a * managed-ref resolution; provided so AI users do not have to learn the * `ref` slot resolution mechanic for the common read path. * * Returns Err when `root` does not carry SceneInstance or the ref slot is dead. */ export declare function worldGetSceneInstanceState(world: World, root: EntityHandle): Result; /** Resolve a generated SceneEntityRef against one concrete SceneInstance. */ export declare function worldResolveSceneEntity(world: World, root: EntityHandle, ref: SceneEntityRef): Result; /** * Despawn a SceneInstance root + all its members. `opts.keepDetached` * preserves members marked via `worldDetachSceneMember` (plan-strategy * §D-5). Returns the count of entities actually despawned (root + each * non-detached member). * * For a plain entity (no SceneInstance), behaviour matches * `world.despawn(entity)` followed by `despawnDescendants(entity)` — i.e. * `keepDetached` is a no-op. */ export declare function worldDespawnScene(world: World, root: EntityHandle, opts?: { keepDetached?: boolean; }): Result; /** * Despawn every descendant of `root` reachable through Children mirror / * SceneInstance.mapping. `opts.keepDetached` is honoured only when `root` * carries a SceneInstance (otherwise the option is ignored — there is no * detached set on a plain entity). * * Returns the count of entities despawned. The `root` itself is NOT * despawned (that is `despawnScene`'s extra step). */ export declare function worldDespawnDescendants(world: World, root: EntityHandle, opts?: { keepDetached?: boolean; }): Result; /** * Write a runtime override to a member entity belonging to `root`. Routes * through `world.set(member, comp, { [field]: value })` after an entity- * scope guard so cross-instance writes fail-fast. Type-mismatch surfaces * `EcsErrorCode = 'scene-override-type-mismatch'` (D-9). */ export declare function worldSetSceneOverride(world: World, root: EntityHandle, member: EntityHandle, component: Component, field: keyof ShapeOf & string, value: unknown): Result; /** * Drop a runtime override (and any mount-time override for the same * (member, comp, field) triple); roll the live column value back to the * source SceneAsset's layer-1 explicit value (M2 v1 — M3+ widens to layer * 2/3 defaults via fillComponentDefaults). */ export declare function worldRemoveSceneOverride(world: World, root: EntityHandle, member: EntityHandle, component: Component, field: keyof ShapeOf & string): Result; /** Mark a member entity detached. Idempotent (set semantics). */ export declare function worldDetachSceneMember(world: World, root: EntityHandle, member: EntityHandle): Result; /** Clear a detached mark. Idempotent (set semantics). */ export declare function worldReattachSceneMember(world: World, root: EntityHandle, member: EntityHandle): Result; /** * Get the SceneAsset handle a SceneInstance root was instantiated from. * Returns Err on a plain entity (no SceneInstance component). */ export declare function worldGetSceneAssetForInstance(world: World, root: EntityHandle): Result, EcsError>; //# sourceMappingURL=scene-instances.d.ts.map