@oasys/oecs 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +183 -155
  3. package/dist/archetype.d.ts +12 -9
  4. package/dist/archetype.d.ts.map +1 -1
  5. package/dist/component.d.ts +10 -0
  6. package/dist/component.d.ts.map +1 -1
  7. package/dist/ecs.d.ts +14 -14
  8. package/dist/ecs.d.ts.map +1 -1
  9. package/dist/entity.d.ts.map +1 -1
  10. package/dist/event.d.ts +6 -0
  11. package/dist/event.d.ts.map +1 -1
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.d.ts +8 -6
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +621 -459
  16. package/dist/query.d.ts +36 -16
  17. package/dist/query.d.ts.map +1 -1
  18. package/dist/ref.d.ts +4 -0
  19. package/dist/ref.d.ts.map +1 -1
  20. package/dist/resource.d.ts +21 -21
  21. package/dist/resource.d.ts.map +1 -1
  22. package/dist/schedule.d.ts +8 -6
  23. package/dist/schedule.d.ts.map +1 -1
  24. package/dist/store.d.ts +13 -6
  25. package/dist/store.d.ts.map +1 -1
  26. package/dist/system.d.ts.map +1 -1
  27. package/dist/type_primitives/assertions.d.ts +1 -0
  28. package/dist/type_primitives/assertions.d.ts.map +1 -1
  29. package/dist/type_primitives/binary_heap/binary_heap.d.ts +33 -0
  30. package/dist/type_primitives/binary_heap/binary_heap.d.ts.map +1 -0
  31. package/dist/type_primitives/bitset/bitset.d.ts +5 -0
  32. package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
  33. package/dist/type_primitives/error.d.ts +2 -1
  34. package/dist/type_primitives/error.d.ts.map +1 -1
  35. package/dist/type_primitives/index.d.ts +2 -0
  36. package/dist/type_primitives/index.d.ts.map +1 -1
  37. package/dist/type_primitives/topological_sort/topological_sort.d.ts +25 -0
  38. package/dist/type_primitives/topological_sort/topological_sort.d.ts.map +1 -0
  39. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +2 -0
  40. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
  41. package/dist/utils/arrays.d.ts.map +1 -1
  42. package/dist/utils/constants.d.ts +0 -8
  43. package/dist/utils/constants.d.ts.map +1 -1
  44. package/dist/utils/error.d.ts +4 -1
  45. package/dist/utils/error.d.ts.map +1 -1
  46. package/package.json +2 -2
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Oasys
3
+ Copyright (c) 2026 2khan
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,272 +1,300 @@
1
- # OECS
1
+ # oecs
2
2
 
3
- A fast, minimal archetype-based Entity Component System written in TypeScript.
3
+ A fast, minimal, archetype-based Entity Component System for TypeScript.
4
4
 
5
- - **Structure-of-Arrays (SoA)** — each component field is a contiguous typed array column (`Float64Array`, `Int32Array`, etc.), enabling cache-friendly inner loops.
6
- - **Phantom-typed components** — `ComponentDef<{x:"f64",y:"f64"}>` is just a number at runtime, but enforces field names and types at compile time.
7
- - **Batch iteration** `for..of` over a query yields non-empty archetypes. Access SoA columns via `get_column()` and write the inner loop.
8
- - **Single-entity refs** `ctx.ref(Pos, entity)` gives you a cached accessor with prototype-backed getters/setters.
9
- - **Resources** typed global singletons (time, input, config) with live readers.
10
- - **Events & signals** fire-and-forget SoA channels, auto-cleared each frame.
11
- - **Deferred structural changes** add/remove component and destroy entity are buffered during system execution and flushed between phases.
12
- - **Topological system ordering** systems within a phase are sorted by before/after constraints using Kahn's algorithm.
13
- - **Fixed timestep** configurable fixed update loop with accumulator and interpolation alpha.
5
+ ## Features
6
+
7
+ - **Archetype-based SoA storage.** Entities sharing a component set share contiguous typed-array columns cache-friendly loops, no per-entity object allocation.
8
+ - **Phantom-typed components.** `ComponentDef<{ x: "f64", y: "f64" }>` is a branded integer at runtime and a fully-typed schema at compile time. Misspelled fields are compile errors.
9
+ - **Callback iteration.** `query.for_each(arch => ...)` yields non-empty archetypes; you write the row loop over typed-array columns.
10
+ - **Tick-based change detection.** Each `(archetype, component)` tracks a change tick. `query.changed(Pos).for_each(...)` visits only archetypes mutated since the system's last run.
11
+ - **Key-based events and resources.** `event_key<F>` / `resource_key<T>` create module-scope symbol handles carrying their schema as a phantom import the key anywhere.
12
+ - **Cached single-entity refs.** `ctx.ref` / `ctx.ref_mut` give ergonomic `pos.x += vel.vx * dt` that compiles to a direct typed-array index op.
13
+ - **Deferred structural changes.** `ctx.add_component` / `ctx.remove_component` / `ctx.destroy_entity` buffer until the schedule flushes between phases, so iterators stay valid.
14
+ - **Topological scheduler.** Per-phase Kahn's-algorithm sort over a binary heap, with insertion order as a deterministic tiebreaker.
15
+ - **Fixed timestep.** Accumulator loop with configurable `fixed_timestep` and spiral-of-death protection.
16
+ - **Reusable primitives.** `BitSet`, `SparseSet`, `SparseMap`, `GrowableTypedArray`, `BinaryHeap`, `topological_sort` all exported.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pnpm add @oasys/oecs
22
+ ```
14
23
 
15
24
  ## Quick start
16
25
 
17
26
  ```ts
18
- import { ECS, SCHEDULE } from "@oasys/oecs-typed2";
27
+ import { ECS, SCHEDULE, event_key, resource_key } from "@oasys/oecs";
28
+
29
+ // Keys — module scope, phantom-typed
30
+ const Time = resource_key<{ delta: number; elapsed: number }>("Time");
31
+ const DamageEvent = event_key<readonly ["target", "amount"]>("Damage");
19
32
 
20
33
  const world = new ECS();
21
34
 
22
- // Record syntax — per-field type control
35
+ // Components
23
36
  const Pos = world.register_component({ x: "f64", y: "f64" });
24
-
25
- // Array shorthand — uniform type, defaults to "f64"
26
37
  const Vel = world.register_component(["vx", "vy"] as const);
27
38
 
28
- // Tags have no fields
29
- const IsEnemy = world.register_tag();
30
-
31
- // Resources are global singletons
32
- const Time = world.register_resource(["delta", "elapsed"] as const, {
33
- delta: 0,
34
- elapsed: 0,
35
- });
36
-
37
- // Events are fire-and-forget messages
38
- const Damage = world.register_event(["target", "amount"] as const);
39
+ // Resources & events
40
+ world.register_resource(Time, { delta: 0, elapsed: 0 });
41
+ world.register_event(DamageEvent, ["target", "amount"] as const);
39
42
 
40
- // Create entities and attach components
43
+ // Entities
41
44
  const e = world.create_entity();
42
- world.add_component(e, Pos, { x: 0, y: 0 });
43
- world.add_component(e, Vel, { vx: 100, vy: 50 });
44
- world.add_component(e, IsEnemy);
45
+ world.add_components(e, [
46
+ { def: Pos, values: { x: 0, y: 0 } },
47
+ { def: Vel, values: { vx: 100, vy: 50 } },
48
+ ]);
45
49
 
46
- // Register a system with a typed query
50
+ // System query resolved once at registration
47
51
  const moveSys = world.register_system(
48
- (q, _ctx, dt) => {
49
- for (const arch of q) {
50
- const px = arch.get_column(Pos, "x");
51
- const py = arch.get_column(Pos, "y");
52
+ (q, ctx, dt) => {
53
+ q.for_each((arch) => {
54
+ const px = arch.get_column_mut(Pos, "x", ctx.world_tick);
55
+ const py = arch.get_column_mut(Pos, "y", ctx.world_tick);
52
56
  const vx = arch.get_column(Vel, "vx");
53
57
  const vy = arch.get_column(Vel, "vy");
54
- const n = arch.entity_count;
55
- for (let i = 0; i < n; i++) {
58
+ for (let i = 0; i < arch.entity_count; i++) {
56
59
  px[i] += vx[i] * dt;
57
60
  py[i] += vy[i] * dt;
58
61
  }
59
- }
62
+ });
60
63
  },
61
64
  (qb) => qb.every(Pos, Vel),
62
65
  );
63
66
 
64
- // Schedule the system
65
67
  world.add_systems(SCHEDULE.UPDATE, moveSys);
66
-
67
- // Initialize
68
68
  world.startup();
69
69
 
70
- // Game loop
71
- function frame(dt: number) {
72
- world.set_resource(Time, { delta: dt, elapsed: performance.now() / 1000 });
70
+ let last = performance.now();
71
+ function frame() {
72
+ const now = performance.now();
73
+ const dt = (now - last) / 1000;
74
+ last = now;
75
+ const t = world.resource(Time);
76
+ world.set_resource(Time, { delta: dt, elapsed: t.elapsed + dt });
73
77
  world.update(dt);
78
+ requestAnimationFrame(frame);
74
79
  }
80
+ requestAnimationFrame(frame);
75
81
  ```
76
82
 
77
- ## Components
78
-
79
- Components map field names to typed array tags. All field values are `number`, but storage uses the specified typed array (`Float64Array`, `Int32Array`, etc.) for cache-friendly iteration.
83
+ ## World options
80
84
 
81
85
  ```ts
82
- // Record syntax per-field type control
83
- const Position = world.register_component({ x: "f64", y: "f64" });
84
- const Health = world.register_component({ current: "i32", max: "i32" });
85
-
86
- // Array shorthand — all fields default to "f64"
87
- const Vel = world.register_component(["vx", "vy"] as const);
88
-
89
- // Tags — no fields
90
- const IsEnemy = world.register_tag();
86
+ const world = new ECS({
87
+ initial_capacity: 4096,
88
+ fixed_timestep: 1 / 50,
89
+ max_fixed_steps: 4,
90
+ });
91
91
  ```
92
92
 
93
- Supported typed array tags: `"f32"`, `"f64"`, `"i8"`, `"i16"`, `"i32"`, `"u8"`, `"u16"`, `"u32"`.
93
+ | Option | Type | Default | Description |
94
+ | ------------------ | -------- | ------- | ----------- |
95
+ | `initial_capacity` | `number` | `1024` | Starting size of each archetype's entity-ID and column buffers. Buffers double on overflow; pick close to your expected per-archetype entity count to avoid early reallocations. |
96
+ | `fixed_timestep` | `number` | `1/60` | Interval (seconds) at which `SCHEDULE.FIXED_UPDATE` systems run. |
97
+ | `max_fixed_steps` | `number` | `5` | Hard cap on fixed-update iterations per frame. Protects against spiral of death. |
98
+
99
+ ## Components
94
100
 
95
- Add components individually or in batch (single archetype transition):
101
+ Records give per-field type control; array shorthand defaults to `f64`. Tags have no fields.
96
102
 
97
103
  ```ts
98
- world.add_component(e, Position, { x: 10, y: 20 });
104
+ const Pos = world.register_component({ x: "f64", y: "f64" });
105
+ const Health = world.register_component({ current: "i32", max: "i32" });
106
+ const Vel = world.register_component(["vx", "vy"] as const);
107
+ const IsEnemy = world.register_tag();
108
+
99
109
  world.add_components(e, [
100
- { def: Position, values: { x: 10, y: 20 } },
101
- { def: Health, values: { current: 100, max: 100 } },
110
+ { def: Pos, values: { x: 0, y: 0 } },
111
+ { def: Vel, values: { vx: 1, vy: 0 } },
112
+ { def: IsEnemy },
102
113
  ]);
103
114
  ```
104
115
 
105
- See [docs/api/components.md](docs/api/components.md) for full API.
116
+ Supported tags: `f32`, `f64`, `i8`, `i16`, `i32`, `u8`, `u16`, `u32`.
117
+
118
+ See [docs/api/components.md](docs/api/components.md).
106
119
 
107
120
  ## Queries
108
121
 
109
- Queries are live views over all archetypes matching a component mask.
122
+ Live, cached views over matching archetypes. Iterate with `for_each`.
110
123
 
111
124
  ```ts
112
- const q = world.query(Position, Velocity);
113
-
114
- // Iterate non-empty archetypes, access SoA columns, write the inner loop
115
- for (const arch of q) {
116
- const px = arch.get_column(Position, "x");
117
- const py = arch.get_column(Position, "y");
118
- const vx = arch.get_column(Velocity, "vx");
119
- const vy = arch.get_column(Velocity, "vy");
120
- const n = arch.entity_count;
121
- for (let i = 0; i < n; i++) {
122
- px[i] += vx[i];
123
- py[i] += vy[i];
124
- }
125
- }
125
+ const q = world.query(Pos, Vel);
126
+
127
+ q.for_each((arch) => {
128
+ const px = arch.get_column(Pos, "x");
129
+ const py = arch.get_column(Pos, "y");
130
+ for (let i = 0; i < arch.entity_count; i++) { /* ... */ }
131
+ });
126
132
 
127
- // Chaining
128
- const targets = world.query(Position).and(Health).not(Shield).or(IsEnemy, IsBoss);
133
+ // Chaining returns new cached queries
134
+ const targets = world.query(Pos).and(Health).not(Shield).any_of(IsEnemy, IsBoss);
135
+
136
+ // Change detection — only archetypes whose Pos column changed since last run
137
+ q.changed(Pos).for_each((arch) => { /* ... */ });
129
138
  ```
130
139
 
131
- See [docs/api/queries.md](docs/api/queries.md) for full API.
140
+ See [docs/api/queries.md](docs/api/queries.md) and [docs/api/change-detection.md](docs/api/change-detection.md).
132
141
 
133
142
  ## Systems
134
143
 
135
- Systems are plain functions registered with a query and scheduled into lifecycle phases.
144
+ Systems are plain functions. Three registration shapes all return a `SystemDescriptor`.
136
145
 
137
146
  ```ts
138
- // With a typed query
147
+ // Bare function
148
+ const logSys = world.register_system((ctx, dt) => { /* ... */ });
149
+
150
+ // Function + query builder (query resolved once at registration)
139
151
  const moveSys = world.register_system(
140
- (q, ctx, dt) => { for (const arch of q) { /* ... */ } },
152
+ (q, ctx, dt) => { q.for_each((arch) => { /* ... */ }); },
141
153
  (qb) => qb.every(Pos, Vel),
142
154
  );
143
155
 
144
- // Without a query
145
- const logSys = world.register_system({
146
- fn(ctx, dt) { console.log("frame", dt); },
156
+ // Full config — lifecycle hooks, name
157
+ const spawnSys = world.register_system({
158
+ name: "spawn",
159
+ fn(ctx, dt) { /* every frame */ },
160
+ on_added(ctx) { /* once during world.startup() */ },
161
+ dispose() { /* during world.dispose() */ },
147
162
  });
148
163
  ```
149
164
 
150
- Inside systems, use `ctx` for deferred structural changes and per-entity access:
151
-
152
- ```ts
153
- const e = ctx.create_entity();
154
- ctx.add_component(e, Pos, { x: 0, y: 0 });
155
- ctx.destroy_entity(someEntity);
156
- ctx.remove_component(entity, Health);
157
- ```
165
+ `SystemContext` exposes deferred structural ops, per-entity access, events, resources, and tick bookkeeping (`ctx.world_tick`, `ctx.last_run_tick`).
158
166
 
159
- See [docs/api/systems.md](docs/api/systems.md) for full API.
167
+ See [docs/api/systems.md](docs/api/systems.md).
160
168
 
161
169
  ## Resources
162
170
 
163
- Resources are typed global singletonstime, input state, camera config.
171
+ Global singletons keyed by `ResourceKey<T>`. Values can be any type objects, typed arrays, class instances.
164
172
 
165
173
  ```ts
166
- const Time = world.register_resource(["delta", "elapsed"] as const, {
167
- delta: 0, elapsed: 0,
168
- });
174
+ import { resource_key } from "@oasys/oecs";
175
+
176
+ const Time = resource_key<{ delta: number; elapsed: number }>("Time");
177
+ const Assets = resource_key<Map<string, ImageBitmap>>("Assets");
169
178
 
170
- // Write
171
- world.set_resource(Time, { delta: dt, elapsed: total });
179
+ world.register_resource(Time, { delta: 0, elapsed: 0 });
180
+ world.register_resource(Assets, new Map());
172
181
 
173
- // Read scalar values, not arrays
174
- const time = world.resource(Time);
175
- time.delta; // number
176
- time.elapsed; // number
182
+ const t = world.resource(Time); // typed as { delta, elapsed }
183
+ world.set_resource(Time, { delta: 0.016, elapsed: 0 }); // swap in a new value
177
184
  ```
178
185
 
179
- See [docs/api/resources.md](docs/api/resources.md) for full API.
186
+ See [docs/api/resources.md](docs/api/resources.md).
180
187
 
181
- ## Events & Signals
188
+ ## Events
182
189
 
183
- Events are fire-and-forget SoA channels, auto-cleared each frame.
190
+ Fire-and-forget SoA channels. Data events carry typed fields; signals carry only a count. Cleared at the end of each `world.update(dt)`.
184
191
 
185
192
  ```ts
186
- // Data events carry fields
187
- const Damage = world.register_event(["target", "amount"] as const);
188
- ctx.emit(Damage, { target: entityId, amount: 50 });
193
+ import { event_key, signal_key } from "@oasys/oecs";
194
+
195
+ const DamageEvent = event_key<readonly ["target", "amount"]>("Damage");
196
+ const GameOver = signal_key("GameOver");
189
197
 
190
- const dmg = ctx.read(Damage);
198
+ world.register_event(DamageEvent, ["target", "amount"] as const);
199
+ world.register_signal(GameOver);
200
+
201
+ ctx.emit(DamageEvent, { target: victimId, amount: 25 });
202
+ ctx.emit(GameOver);
203
+
204
+ const dmg = ctx.read(DamageEvent);
191
205
  for (let i = 0; i < dmg.length; i++) {
192
- dmg.target[i]; // number
193
- dmg.amount[i]; // number
206
+ dmg.target[i]; dmg.amount[i]; // number columns
194
207
  }
195
-
196
- // Signals carry no data — just a count
197
- const OnReset = world.register_signal();
198
- ctx.emit(OnReset);
199
- if (ctx.read(OnReset).length > 0) { /* fired */ }
208
+ if (ctx.read(GameOver).length > 0) { /* fired */ }
200
209
  ```
201
210
 
202
- See [docs/api/events.md](docs/api/events.md) for full API.
211
+ See [docs/api/events.md](docs/api/events.md).
203
212
 
204
213
  ## Refs
205
214
 
206
- Refs provide cached single-entity field access faster than `get_field`/`set_field` for repeated access.
215
+ Cached single-entity handlesresolve archetype + row + column once, then read/write fields by name.
207
216
 
208
217
  ```ts
209
- const pos = ctx.ref(Pos, entity);
210
- const vel = ctx.ref(Vel, entity);
218
+ const pos = ctx.ref_mut(Pos, entity); // writable; bumps Pos change tick
219
+ const vel = ctx.ref(Vel, entity); // readonly
211
220
  pos.x += vel.vx * dt;
212
221
  pos.y += vel.vy * dt;
213
222
  ```
214
223
 
215
- See [docs/api/refs.md](docs/api/refs.md) for full API.
224
+ Prefer `ctx.ref` by default; reach for `ctx.ref_mut` at the point of mutation. Do not hold refs across archetype transitions or phase flushes.
225
+
226
+ See [docs/api/refs.md](docs/api/refs.md).
216
227
 
217
228
  ## Schedule
218
229
 
219
- Seven lifecycle phases, executed in order:
230
+ Seven phases run in a fixed order:
220
231
 
221
- | Phase | When | Use case |
222
- |---|---|---|
223
- | `PRE_STARTUP` | Once, before startup | Resource loading |
224
- | `STARTUP` | Once | Initial entity spawning |
225
- | `POST_STARTUP` | Once, after startup | Validation |
226
- | `FIXED_UPDATE` | Every tick (fixed dt) | Physics, simulation |
227
- | `PRE_UPDATE` | Every frame, first | Input handling |
228
- | `UPDATE` | Every frame | Game logic |
229
- | `POST_UPDATE` | Every frame, last | Rendering, cleanup |
232
+ | Phase | When | Typical use |
233
+ | -------------- | -------------------------------- | ----------------------- |
234
+ | `PRE_STARTUP` | Once, before `STARTUP` | Resource loading |
235
+ | `STARTUP` | Once | Initial entity spawning |
236
+ | `POST_STARTUP` | Once, after `STARTUP` | Validation |
237
+ | `FIXED_UPDATE` | Zero+ times per frame (fixed dt) | Physics, simulation |
238
+ | `PRE_UPDATE` | Every frame, first | Input, time |
239
+ | `UPDATE` | Every frame | Game logic, AI |
240
+ | `POST_UPDATE` | Every frame, last | Rendering, cleanup |
230
241
 
231
242
  ```ts
232
- world.add_systems(SCHEDULE.UPDATE, moveSys, physicsSys);
233
- world.add_systems(SCHEDULE.POST_UPDATE, renderSys);
234
-
235
- // Ordering constraints
236
- world.add_systems(SCHEDULE.UPDATE, moveSys, {
237
- system: physicsSys,
238
- ordering: { after: [moveSys] },
243
+ world.add_systems(SCHEDULE.UPDATE, moveSys, damageSys, {
244
+ system: deathSys,
245
+ ordering: { after: [damageSys] },
239
246
  });
240
247
  ```
241
248
 
242
- See [docs/api/schedule.md](docs/api/schedule.md) for full API.
249
+ Within a phase, systems are topologically sorted by `before` / `after` constraints. `ctx.flush()` runs automatically between phases.
250
+
251
+ See [docs/api/schedule.md](docs/api/schedule.md).
243
252
 
244
253
  ## Entity lifecycle
245
254
 
246
255
  ```ts
247
256
  const e = world.create_entity();
248
- world.is_alive(e); // true
249
- world.destroy_entity_deferred(e); // deferred
257
+ world.is_alive(e); // true
258
+ world.destroy_entity_deferred(e);
250
259
  world.flush();
251
- world.is_alive(e); // false
260
+ world.is_alive(e); // false
252
261
  ```
253
262
 
254
- Entity IDs are generational: destroying an entity increments its slot's generation, so stale IDs are detected as dead.
263
+ `EntityID` is a packed 31-bit integer (20-bit slot index, 11-bit generation). Destroying an entity bumps its slot's generation, so stale handles are detected as dead. Inside systems, use `ctx.create_entity()` (immediate) and `ctx.destroy_entity(e)` (deferred).
255
264
 
256
- ## Dev / Prod modes
265
+ See [docs/api/entities.md](docs/api/entities.md).
257
266
 
258
- `__DEV__` compile-time flags enable bounds checking, dead entity detection, and duplicate system detection. Circular dependency detection is always active (not tree-shaken). All other dev checks are tree-shaken in production builds.
267
+ ## Dev vs Prod modes
268
+
269
+ A compile-time `__DEV__` flag gates runtime sanity checks: bounds checks, liveness checks, duplicate-system detection, and registration validation. These are tree-shaken out of production bundles by the Vite build. Scheduler cycle detection is always active and throws `ECS_ERROR.CIRCULAR_SYSTEM_DEPENDENCY` on the first offending run.
259
270
 
260
271
  ## Development
261
272
 
262
273
  ```bash
263
274
  pnpm install
264
- pnpm test # vitest in watch mode
265
- pnpm bench # run benchmarks
266
- pnpm build # vite library build
267
- pnpm tsc --noEmit # type check
275
+ pnpm test # vitest
276
+ pnpm bench # vitest bench
277
+ pnpm build # vite library build
278
+ pnpm tsc --noEmit # type check
268
279
  ```
269
280
 
270
- ## Architecture
271
-
272
- See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for internal design details.
281
+ ## Guides
282
+
283
+ - [Getting Started](docs/GETTING_STARTED.md) step-by-step tutorial.
284
+ - [Best Practices](docs/BEST_PRACTICES.md) — component design, query patterns, pitfalls.
285
+ - [Architecture](docs/ARCHITECTURE.md) — data layout, flush model, cache invalidation.
286
+ - API reference:
287
+ [components](docs/api/components.md) ·
288
+ [entities](docs/api/entities.md) ·
289
+ [queries](docs/api/queries.md) ·
290
+ [systems](docs/api/systems.md) ·
291
+ [schedule](docs/api/schedule.md) ·
292
+ [resources](docs/api/resources.md) ·
293
+ [events](docs/api/events.md) ·
294
+ [refs](docs/api/refs.md) ·
295
+ [change detection](docs/api/change-detection.md) ·
296
+ [type primitives](docs/api/type-primitives.md)
297
+
298
+ ## License
299
+
300
+ MIT
@@ -1,5 +1,5 @@
1
1
  import { Brand, GrowableTypedArray, AnyTypedArray, TypedArrayTag, BitSet } from './type_primitives';
2
- import { ComponentID, ComponentDef, ComponentSchema, TagToTypedArray } from './component';
2
+ import { ComponentID, ComponentDef, ComponentSchema, TagToTypedArray, ReadonlyColumn, ReadonlyUint32Array } from './component';
3
3
  import { EntityID } from './entity';
4
4
  export type ArchetypeID = Brand<number, "archetype_id">;
5
5
  export declare const as_archetype_id: (value: number) => ArchetypeID;
@@ -35,21 +35,24 @@ export declare class Archetype {
35
35
  private readonly _field_names;
36
36
  readonly column_groups: (ArchetypeColumnGroup | undefined)[];
37
37
  readonly _column_ids: number[];
38
+ readonly _changed_tick: number[];
38
39
  constructor(id: ArchetypeID, mask: BitSet, layouts?: ArchetypeColumnLayout[], initial_capacity?: number);
39
40
  get entity_count(): number;
40
41
  /** Raw entity ID buffer. Valid data at indices 0..entity_count-1. */
41
- get entity_ids(): Uint32Array;
42
+ get entity_ids(): ReadonlyUint32Array;
42
43
  get entity_list(): Uint32Array;
43
44
  has_component(id: ComponentID): boolean;
44
45
  matches(required: BitSet): boolean;
45
- /** Get a single field's column. Valid data: indices 0..entity_count-1. */
46
- get_column<S extends ComponentSchema, K extends string & keyof S>(def: ComponentDef<S>, field: K): TagToTypedArray[S[K]];
47
- write_fields(row: number, component_id: ComponentID, values: Record<string, number>): void;
46
+ /** Get a single field's column (read-only). Valid data: indices 0..entity_count-1. */
47
+ get_column<S extends ComponentSchema, K extends string & keyof S>(def: ComponentDef<S>, field: K): ReadonlyColumn;
48
+ /** Get a single field's column (mutable). Marks the component as changed at the given tick. */
49
+ get_column_mut<S extends ComponentSchema, K extends string & keyof S>(def: ComponentDef<S>, field: K, tick: number): TagToTypedArray[S[K]];
50
+ write_fields(row: number, component_id: ComponentID, values: Record<string, number>, tick: number): void;
48
51
  /** Fast positional write: values[i] → field[i] in declaration order. No string lookup. */
49
- write_fields_positional(row: number, component_id: ComponentID, values: ArrayLike<number>): void;
52
+ write_fields_positional(row: number, component_id: ComponentID, values: ArrayLike<number>, tick: number): void;
50
53
  read_field(row: number, component_id: ComponentID, field: string): number;
51
54
  /** Copy all shared component columns from source archetype at src_row into dst_row. */
52
- copy_shared_from(source: Archetype, src_row: number, dst_row: number): void;
55
+ copy_shared_from(source: Archetype, src_row: number, dst_row: number, tick: number): void;
53
56
  /**
54
57
  * Add an entity. Pushes zeroes into all columns and returns the assigned row.
55
58
  * Store is responsible for tracking entity_index → row.
@@ -71,7 +74,7 @@ export declare class Archetype {
71
74
  * Uses a pre-computed transition map for branchless column copy.
72
75
  * Writes dst_row to _move_result[0], swapped entity index to _move_result[1].
73
76
  */
74
- move_entity_from(src: Archetype, src_row: number, entity_id: EntityID, transition_map: Int16Array): void;
77
+ move_entity_from(src: Archetype, src_row: number, entity_id: EntityID, transition_map: Int16Array, tick: number): void;
75
78
  /**
76
79
  * Move an entity from src into this archetype (tag-only: no columns to copy).
77
80
  * Writes dst_row to _move_result[0], swapped entity index to _move_result[1].
@@ -82,7 +85,7 @@ export declare class Archetype {
82
85
  * Much faster than per-entity move_entity_from when the entire source is moving.
83
86
  * After this call, src is empty. Returns the starting dst_row for the batch.
84
87
  */
85
- bulk_move_all_from(src: Archetype, transition_map: Int16Array): number;
88
+ bulk_move_all_from(src: Archetype, transition_map: Int16Array, tick: number): number;
86
89
  get_edge(component_id: ComponentID): ArchetypeEdge | undefined;
87
90
  set_edge(component_id: ComponentID, edge: ArchetypeEdge): void;
88
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"archetype.d.ts","sourceRoot":"","sources":["../src/archetype.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;KAmBK;AAEL,OAAO,EACL,KAAK,EAGL,kBAAkB,EAGlB,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,EAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAG3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAExD,eAAO,MAAM,eAAe,UAAW,MAAM,gBAK1C,CAAC;AAEJ,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,WAAW,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,wEAAwE;IACxE,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,8EAA8E;IAC9E,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,aAAa,EAAE,CAAC;CAC9B;AAED,UAAU,oBAAoB;IAC5B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,OAAO,EAAE,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC;CAC9C;AAED,qBAAa,SAAS;IACpB,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAC3C,MAAM,EAAE,MAAM,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAI7C,QAAQ,CAAC,aAAa,EAAE,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAM;IAEjE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAM;IAEpC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAM;IAErC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgC;IAE7D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAG/C,QAAQ,CAAC,aAAa,EAAE,CAAC,oBAAoB,GAAG,SAAS,CAAC,EAAE,CAAM;IAElE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAM;gBAGlC,EAAE,EAAE,WAAW,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,qBAAqB,EAAE,EACjC,gBAAgB,GAAE,MAAgC;IAgCpD,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,qEAAqE;IACrE,IAAW,UAAU,IAAI,WAAW,CAEnC;IAED,IAAW,WAAW,IAAI,WAAW,CAEpC;IAEM,aAAa,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO;IAIvC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIzC,0EAA0E;IACnE,UAAU,CAAC,CAAC,SAAS,eAAe,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,EACrE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,CAAC,GACP,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAsBjB,YAAY,CACjB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,WAAW,EACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,IAAI;IAWP,0FAA0F;IACnF,uBAAuB,CAC5B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,WAAW,EACzB,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,GACxB,IAAI;IAUA,UAAU,CACf,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,WAAW,EACzB,KAAK,EAAE,MAAM,GACZ,MAAM;IAST,uFAAuF;IAChF,gBAAgB,CACrB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,IAAI;IAkBP;;;OAGG;IACI,UAAU,CAAC,SAAS,EAAE,QAAQ,GAAG,MAAM;IAW9C;;;;OAIG;IACI,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAuBzC,uEAAuE;IAChE,cAAc,CAAC,SAAS,EAAE,QAAQ,GAAG,MAAM;IAOlD,4EAA4E;IACrE,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAe7C;;;;;OAKG;IACI,gBAAgB,CACrB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,QAAQ,EACnB,cAAc,EAAE,UAAU,GACzB,IAAI;IAwBP;;;OAGG;IACI,oBAAoB,CACzB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,QAAQ,GAClB,IAAI;IAWP;;;;OAIG;IACI,kBAAkB,CACvB,GAAG,EAAE,SAAS,EACd,cAAc,EAAE,UAAU,GACzB,MAAM;IAiCF,QAAQ,CAAC,YAAY,EAAE,WAAW,GAAG,aAAa,GAAG,SAAS;IAI9D,QAAQ,CAAC,YAAY,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI;CAGtE;AAED,iGAAiG;AACjG,eAAO,MAAM,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAgB,CAAC;AAE3D;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,UAAU,CA6B/E"}
1
+ {"version":3,"file":"archetype.d.ts","sourceRoot":"","sources":["../src/archetype.ts"],"names":[],"mappings":"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"}
@@ -31,5 +31,15 @@ declare const __schema: unique symbol;
31
31
  export type ComponentDef<S extends ComponentSchema = ComponentSchema> = ComponentID & {
32
32
  readonly [__schema]: S;
33
33
  };
34
+ /** Compile-time readonly view of a typed array column. Prevents index writes. */
35
+ export interface ReadonlyColumn {
36
+ readonly [index: number]: number;
37
+ readonly length: number;
38
+ }
39
+ /** Compile-time readonly view of a Uint32Array. Prevents index writes. */
40
+ export interface ReadonlyUint32Array {
41
+ readonly [index: number]: number;
42
+ readonly length: number;
43
+ }
34
44
  export {};
35
45
  //# sourceMappingURL=component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;KAuBK;AAEL,OAAO,EACL,KAAK,EAGL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACxD,eAAO,MAAM,eAAe,UAAW,MAAM,gBAK1C,CAAC;AAEJ,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAEtE,6CAA6C;AAC7C,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,YAAY,CAAC;IAClB,EAAE,EAAE,SAAS,CAAC;IACd,GAAG,EAAE,UAAU,CAAC;IAChB,GAAG,EAAE,UAAU,CAAC;IAChB,EAAE,EAAE,UAAU,CAAC;IACf,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,WAAW,CAAC;CAClB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,eAAe,IAAI;IACnD,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM;CAChC,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/C,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG,SAAS,MAAM,EAAE,CAAC;AAEhD,qFAAqF;AACrF,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,YAAY;CACxC,CAAC;AAMF,OAAO,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;AAEtC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAClE,WAAW,GAAG;IAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;KAuBK;AAEL,OAAO,EACL,KAAK,EAGL,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACxD,eAAO,MAAM,eAAe,UAAW,MAAM,gBAK1C,CAAC;AAEJ,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAEtE,6CAA6C;AAC7C,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,YAAY,CAAC;IAClB,EAAE,EAAE,SAAS,CAAC;IACd,GAAG,EAAE,UAAU,CAAC;IAChB,GAAG,EAAE,UAAU,CAAC;IAChB,EAAE,EAAE,UAAU,CAAC;IACf,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,WAAW,CAAC;CAClB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,eAAe,IAAI;IACnD,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM;CAChC,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/C,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG,SAAS,MAAM,EAAE,CAAC;AAEhD,qFAAqF;AACrF,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,YAAY;CACxC,CAAC;AAMF,OAAO,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;AAEtC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI,WAAW,GAAG;IACpF,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;CACxB,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB"}
package/dist/ecs.d.ts CHANGED
@@ -3,8 +3,8 @@ import { Archetype } from './archetype';
3
3
  import { SystemContext, Query, QueryBuilder, QueryResolver } from './query';
4
4
  import { EntityID } from './entity';
5
5
  import { ComponentDef, ComponentSchema, ComponentFields, FieldValues } from './component';
6
- import { EventDef } from './event';
7
- import { ResourceDef, ResourceReader } from './resource';
6
+ import { EventKey, EventReader } from './event';
7
+ import { ResourceKey } from './resource';
8
8
  import { SystemFn, SystemConfig, SystemDescriptor } from './system';
9
9
  import { BitSet, TypedArrayTag } from './type_primitives';
10
10
  export interface WorldOptions {
@@ -18,6 +18,7 @@ export declare class ECS implements QueryResolver {
18
18
  private readonly ctx;
19
19
  private readonly systems;
20
20
  private next_system_id;
21
+ private _tick;
21
22
  private _fixed_timestep;
22
23
  private _accumulator;
23
24
  private _max_fixed_steps;
@@ -32,15 +33,12 @@ export declare class ECS implements QueryResolver {
32
33
  readonly [K in F[number]]: T;
33
34
  }>;
34
35
  register_tag(): ComponentDef<Record<string, never>>;
35
- register_event<F extends readonly string[]>(fields: F): EventDef<F>;
36
- register_signal(): EventDef<readonly []>;
37
- register_resource<F extends readonly string[]>(fields: F, initial: {
38
- readonly [K in F[number]]: number;
39
- }): ResourceDef<F>;
40
- resource<F extends ComponentFields>(def: ResourceDef<F>): ResourceReader<F>;
41
- set_resource<F extends ComponentFields>(def: ResourceDef<F>, values: {
42
- readonly [K in F[number]]: number;
43
- }): void;
36
+ register_event<F extends readonly string[]>(key: EventKey<F>, fields: F): void;
37
+ register_signal(key: EventKey<readonly []>): void;
38
+ register_resource<T>(key: ResourceKey<T>, value: T): void;
39
+ resource<T>(key: ResourceKey<T>): T;
40
+ set_resource<T>(key: ResourceKey<T>, value: T): void;
41
+ has_resource<T>(key: ResourceKey<T>): boolean;
44
42
  create_entity(): EntityID;
45
43
  destroy_entity_deferred(id: EntityID): void;
46
44
  is_alive(id: EntityID): boolean;
@@ -67,11 +65,13 @@ export declare class ECS implements QueryResolver {
67
65
  batch_remove_component(src_arch: Archetype, def: ComponentDef): void;
68
66
  get_field<S extends ComponentSchema>(entity_id: EntityID, def: ComponentDef<S>, field: string & keyof S): number;
69
67
  set_field<S extends ComponentSchema>(entity_id: EntityID, def: ComponentDef<S>, field: string & keyof S, value: number): void;
70
- emit(def: EventDef<readonly []>): void;
71
- emit<F extends ComponentFields>(def: EventDef<F>, values: {
68
+ emit(key: EventKey<readonly []>): void;
69
+ emit<F extends ComponentFields>(key: EventKey<F>, values: {
72
70
  readonly [K in F[number]]: number;
73
71
  }): void;
72
+ read<F extends ComponentFields>(key: EventKey<F>): EventReader<F>;
74
73
  query<T extends ComponentDef[]>(...defs: T): Query<T>;
74
+ _get_last_run_tick(): number;
75
75
  /** QueryResolver implementation — creates or retrieves a cached Query. */
76
76
  _resolve_query(include: BitSet, exclude: BitSet | null, any_of: BitSet | null, defs: readonly ComponentDef[]): Query<any>;
77
77
  private _find_cached;
@@ -83,7 +83,7 @@ export declare class ECS implements QueryResolver {
83
83
  *
84
84
  * // Function + query builder
85
85
  * world.register_system(
86
- * (q, ctx, dt) => { for (const arch of q) { ... } },
86
+ * (q, ctx, dt) => { q.for_each((arch) => { ... }); },
87
87
  * (qb) => qb.every(Pos, Vel),
88
88
  * );
89
89
  *