3.6 KiB
3.6 KiB
ComponentRegistry
(no submodules)
Purpose
ComponentRegistry translates ComponentSchema references into stable integer IDs. These IDs are used as bit positions in archetype bitsets, and registering a schema is the first step before it can be added to any entity. The registry enforces three contracts: identity-stable registration (same schema → same ID), name-uniqueness (no two schemas may share a name), and a hard cap on how many distinct types can be registered (maxComponents, default 256, matching the bitset's typical 8-word allocation).
The registry is owned by World but exposed publicly so editors, profilers, and serialisation layers can iterate every registered type.
Features
- Idempotent
register— passing the same schema reference returns the same ID. - Throws structured
EcsErrorwith stable codes for every failure mode (COMPONENT_NAME_CONFLICT,COMPONENT_LIMIT_REACHED,COMPONENT_NOT_REGISTERED). - Bi-directional lookup: schema → ID, ID → schema, name → schema.
maxComponentscap configured at construction; validates positive integer.count,has, andschemas()accessors for inspection.
Usage: Basic Example
import { ComponentRegistry, defineComponent } from "@suaveplan/ecs";
const Position = defineComponent("Position", { x: 0, y: 0 });
const registry = new ComponentRegistry();
const positionId = registry.register(Position);
console.log(positionId); // 0
console.log(registry.register(Position) === positionId); // true
Usage: Advanced Example
import {
ComponentRegistry,
defineComponent,
ECS_CODES,
EcsError,
} from "@suaveplan/ecs";
const registry = new ComponentRegistry(4);
registry.register(defineComponent("A", {}));
registry.register(defineComponent("B", {}));
registry.register(defineComponent("C", {}));
registry.register(defineComponent("D", {}));
try {
registry.register(defineComponent("E", {}));
} catch (err) {
if (err instanceof EcsError && err.code === ECS_CODES.COMPONENT_LIMIT_REACHED) {
console.warn("raise maxComponents in WorldConfig");
}
}
API Reference
| Member | Description |
|---|---|
new ComponentRegistry(maxComponents?) |
Default maxComponents is 256. Rejects non-positive integers. |
register(schema) |
Return stable ID. Throws ECS.COMPONENT_NAME_CONFLICT / ECS.COMPONENT_LIMIT_REACHED. |
getId(schema) |
Return ID, or throw ECS.COMPONENT_NOT_REGISTERED. |
getSchema(id) / getSchemaByName(name) |
Lookups; return undefined when missing. |
has(schema) |
Boolean membership. |
count |
Number of registered schemas. |
schemas() |
Read-only snapshot in registration order. |
maxComponents |
Hard cap configured at construction. |
Implementation Notes
- Three maps back the registry — schema → id, id → schema, name → schema. The slight memory overhead pays off in O(1) lookups across all three directions.
- IDs are dense and monotonically increasing starting at
0. They are intentionally not stable across processes: the same schema registered second in process A and first in process B yields different IDs. Persistence layers useschema.name, never the ID. - Name uniqueness is enforced via the dedicated
nameToSchemamap; this catches the most common bug — twodefineComponent("Position", …)calls in different modules — at registration time rather than at the first archetype-create. - The
maxComponentscap is also the bitset size for archetype masks. Exceeding it would mean an archetype's mask runs off the end of itsUint32Array, so the limit is hard, not advisory.