UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

47 lines (46 loc) 2.24 kB
import type { Entity } from "./types"; import { type MatchHost } from "./query-match"; /** * Host interface that QueryCache uses to read EntityManager state. * Decouples QueryCache from EntityManager's private fields. */ export interface QueryCacheHost<ComponentTypes> extends MatchHost<ComponentTypes> { getChildren(parentId: number): readonly number[]; allEntities(): IterableIterator<Entity<ComponentTypes>>; componentIndex(component: keyof ComponentTypes): Set<number> | undefined; } /** * Maintains incrementally-updated Maps of entity id → Entity matching the * static portion of registered query shapes (with / without / parentHas). * EntityManager calls the on* hooks on component add/remove, entity * removal, and parent change. The `changed` filter is applied as a * post-pass by the caller, since its threshold advances each tick. */ export default class QueryCache<ComponentTypes> { private readonly host; private readonly caches; private readonly byComp; private readonly byParentComp; constructor(host: QueryCacheHost<ComponentTypes>); /** * Returns the Map of entity id → Entity matching the (with, without, * parentHas) shape. Caches are interned by canonical shape — identical * shapes share a single Map across systems. Cold-start populates by * iterating the smallest matching component index. */ getOrCreate(withC: ReadonlyArray<keyof ComponentTypes>, withoutC: ReadonlyArray<keyof ComponentTypes>, parentHas: ReadonlyArray<keyof ComponentTypes>): Map<number, Entity<ComponentTypes>>; /** Test-only: returns the number of distinct interned shapes. */ get cacheCount(): number; private populate; private matches; private reeval; onComponentChanged(entityId: number, componentName: keyof ComponentTypes): void; onParentChanged(childId: number): void; /** * Drops the entity from every cache. For parentHas caches, also drops * direct children — once this entity is gone, the parent-link severs * and children stop matching. Cascade-removed children fire their own * beforeRemoved first, so the extra delete is a harmless no-op there. */ onEntityRemoved(entityId: number): void; }