first commit
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# ComponentRegistry
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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 `EcsError` with stable codes for every failure mode (`COMPONENT_NAME_CONFLICT`, `COMPONENT_LIMIT_REACHED`, `COMPONENT_NOT_REGISTERED`).
|
||||
- Bi-directional lookup: schema → ID, ID → schema, name → schema.
|
||||
- `maxComponents` cap configured at construction; validates positive integer.
|
||||
- `count`, `has`, and `schemas()` accessors for inspection.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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 use `schema.name`, never the ID.
|
||||
- Name uniqueness is enforced via the dedicated `nameToSchema` map; this catches the most common bug — two `defineComponent("Position", …)` calls in different modules — at registration time rather than at the first archetype-create.
|
||||
- The `maxComponents` cap is also the bitset size for archetype masks. Exceeding it would mean an archetype's mask runs off the end of its `Uint32Array`, so the limit is hard, not advisory.
|
||||
Reference in New Issue
Block a user