import type ECSpresso from './ecspresso'; import type { RemoveEntityOptions } from './types'; import type { WorldConfig, EmptyConfig } from './type-utils'; /** * CommandBuffer queues structural changes to be executed later. * This prevents ordering issues when modifying entities during system execution. * * Commands are executed in FIFO order when playback() is called. * * @example * ```typescript * // In a system * ecs.commands.removeEntity(entityId); * ecs.commands.spawn({ position: { x: 0, y: 0 } }); * * // Later (automatically at end of update()) * ecs.commands.playback(ecs); * ``` */ export default class CommandBuffer { private readonly parent?; private commands; private closed; /** * @param parent Owning ECS instance, used to read the active scope hint at * queue time so screen-gated spawns get auto-scoped on playback. Optional * so the buffer can be constructed standalone in tests. */ constructor(parent?: ECSpresso | undefined); /** * Queue an entity removal command * @param entityId The entity ID to remove * @param options Optional removal options (cascade, etc.) */ removeEntity(entityId: number, options?: RemoveEntityOptions): void; /** * Queue a component addition command * @param entityId The entity ID * @param componentName The name of the component to add * @param componentValue The component data */ addComponent(entityId: number, componentName: K, componentValue: Cfg['components'][K]): void; /** * Queue a component removal command * @param entityId The entity ID * @param componentName The name of the component to remove */ removeComponent(entityId: number, componentName: K): void; /** * Queue an entity spawn command * @param components The initial components for the new entity * @returns void (entity ID not available until playback) */ spawn(components: T & Record, never>, options?: { scope?: (keyof Cfg['screens'] & string) | null; }): void; /** * Queue a child entity spawn command * @param parentId The parent entity ID * @param components The initial components for the new child entity */ spawnChild(parentId: number, components: T & Record, never>, options?: { scope?: (keyof Cfg['screens'] & string) | null; }): void; /** * Snapshot the parent ECS's active scope hint at queue time when the * caller didn't pass an explicit scope. Without this, playback (after the * tick ends) would see a null hint and the gated system's screen intent * would be lost. */ private _resolveScope; /** * Queue multiple component additions * @param entityId The entity ID * @param components Object with component names as keys and component data as values */ addComponents(entityId: number, components: T & Record, never>): void; /** * Queue a parent assignment command * @param childId The child entity ID * @param parentId The parent entity ID */ setParent(childId: number, parentId: number): void; /** * Queue a component mutation command. * The mutator runs during playback, receiving the component for in-place mutation. * Automatically marks the component as changed. * @param entityId The entity ID * @param componentName The component to mutate * @param mutator A function that receives the component value for in-place mutation */ mutateComponent(entityId: number, componentName: K, mutator: (value: Cfg['components'][K]) => void): void; /** Queue a markChanged command for playback. */ markChanged(entityId: number, componentName: K): void; /** * Queue a parent removal command * @param childId The child entity ID */ removeParent(childId: number): void; /** * Execute all queued commands in FIFO order. * Errors from individual commands are caught and logged, but do not stop playback. * @param ecs The ECSpresso instance to execute commands on */ playback(ecs: ECSpresso): void; /** * Clear all queued commands without executing them */ clear(): void; /** @internal Discard queued commands and reject future queueing after world disposal. */ close(): void; private enqueue; /** * Get the number of queued commands * @returns The number of commands waiting to be executed */ get length(): number; }