archetype-ecs 1.3.1 → 1.3.2

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 (47) hide show
  1. package/README.md +104 -34
  2. package/bench/allocations-1m.js +1 -1
  3. package/bench/multi-ecs-bench.js +1 -1
  4. package/bench/typed-vs-bitecs-1m.js +1 -1
  5. package/bench/typed-vs-untyped.js +81 -81
  6. package/bench/vs-bitecs.js +31 -56
  7. package/dist/ComponentRegistry.d.ts +18 -0
  8. package/dist/ComponentRegistry.js +29 -0
  9. package/dist/EntityManager.d.ts +52 -0
  10. package/dist/EntityManager.js +891 -0
  11. package/dist/Profiler.d.ts +12 -0
  12. package/dist/Profiler.js +38 -0
  13. package/dist/System.d.ts +41 -0
  14. package/dist/System.js +159 -0
  15. package/dist/index.d.ts +12 -0
  16. package/dist/index.js +29 -0
  17. package/dist/src/ComponentRegistry.d.ts +18 -0
  18. package/dist/src/ComponentRegistry.js +29 -0
  19. package/dist/src/EntityManager.d.ts +49 -0
  20. package/dist/src/EntityManager.js +853 -0
  21. package/dist/src/Profiler.d.ts +12 -0
  22. package/dist/src/Profiler.js +38 -0
  23. package/dist/src/System.d.ts +37 -0
  24. package/dist/src/System.js +139 -0
  25. package/dist/src/index.d.ts +14 -0
  26. package/dist/src/index.js +29 -0
  27. package/dist/tests/EntityManager.test.d.ts +1 -0
  28. package/dist/tests/EntityManager.test.js +651 -0
  29. package/dist/tests/System.test.d.ts +1 -0
  30. package/dist/tests/System.test.js +630 -0
  31. package/dist/tests/types.d.ts +1 -0
  32. package/dist/tests/types.js +129 -0
  33. package/package.json +9 -7
  34. package/src/ComponentRegistry.ts +49 -0
  35. package/src/EntityManager.ts +1018 -0
  36. package/src/{Profiler.js → Profiler.ts} +18 -5
  37. package/src/System.ts +226 -0
  38. package/src/index.ts +44 -0
  39. package/tests/{EntityManager.test.js → EntityManager.test.ts} +338 -70
  40. package/tests/System.test.ts +730 -0
  41. package/tests/types.ts +67 -66
  42. package/tsconfig.json +8 -5
  43. package/tsconfig.test.json +13 -0
  44. package/src/ComponentRegistry.js +0 -21
  45. package/src/EntityManager.js +0 -578
  46. package/src/index.d.ts +0 -118
  47. package/src/index.js +0 -37
@@ -1,578 +0,0 @@
1
- import { TYPED, componentSchemas, toSym } from './ComponentRegistry.js';
2
-
3
- const INITIAL_CAPACITY = 64;
4
-
5
- function createSoAStore(schema, capacity) {
6
- const store = { [TYPED]: true, _schema: schema, _capacity: capacity };
7
- for (const [field, Ctor] of Object.entries(schema)) {
8
- store[field] = new Ctor(capacity);
9
- }
10
- return store;
11
- }
12
-
13
- function growSoAStore(store, newCapacity) {
14
- store._capacity = newCapacity;
15
- for (const [field, Ctor] of Object.entries(store._schema)) {
16
- const old = store[field];
17
- store[field] = new Ctor(newCapacity);
18
- if (Ctor === Array) {
19
- for (let i = 0; i < old.length; i++) store[field][i] = old[i];
20
- } else {
21
- store[field].set(old);
22
- }
23
- }
24
- }
25
-
26
- function soaWrite(store, idx, data) {
27
- for (const field in store._schema) {
28
- store[field][idx] = data[field];
29
- }
30
- }
31
-
32
- function soaRead(store, idx) {
33
- const obj = {};
34
- for (const field in store._schema) {
35
- obj[field] = store[field][idx];
36
- }
37
- return obj;
38
- }
39
-
40
- function soaSwap(store, idxA, idxB) {
41
- for (const field in store._schema) {
42
- const arr = store[field];
43
- const tmp = arr[idxA];
44
- arr[idxA] = arr[idxB];
45
- arr[idxB] = tmp;
46
- }
47
- }
48
-
49
- function createSnapshotStore(schema, capacity) {
50
- const snap = {};
51
- for (const [field, Ctor] of Object.entries(schema)) {
52
- snap[field] = new Ctor(capacity);
53
- }
54
- return snap;
55
- }
56
-
57
- function growSnapshotStore(snap, schema, newCapacity) {
58
- for (const [field, Ctor] of Object.entries(schema)) {
59
- const old = snap[field];
60
- snap[field] = new Ctor(newCapacity);
61
- if (Ctor === Array) {
62
- for (let i = 0; i < old.length; i++) snap[field][i] = old[i];
63
- } else {
64
- snap[field].set(old);
65
- }
66
- }
67
- }
68
-
69
- export function createEntityManager() {
70
- let nextId = 1;
71
- const allEntityIds = new Set();
72
-
73
- // Change tracking (opt-in via enableTracking)
74
- let trackFilter = 0; // bitmask — only track archetypes matching this
75
- let createdSet = null; // Set<EntityId>
76
- let destroyedSet = null; // Set<EntityId>
77
-
78
- // Double-buffered snapshots: tracked archetypes get back-buffer arrays
79
- // Game systems write to front (normal SoA arrays), flushSnapshots copies front→back
80
- const trackedArchetypes = []; // archetypes that match trackFilter
81
-
82
- // Component bit registry (symbol → bit index 0..31)
83
- const componentBitIndex = new Map();
84
- let nextBitIndex = 0;
85
-
86
- function getBit(type) {
87
- const sym = toSym(type);
88
- let bit = componentBitIndex.get(sym);
89
- if (bit === undefined) {
90
- bit = nextBitIndex++;
91
- componentBitIndex.set(sym, bit);
92
- }
93
- return bit;
94
- }
95
-
96
- function computeMask(types) {
97
- let mask = 0;
98
- for (const t of types) {
99
- mask |= (1 << getBit(t));
100
- }
101
- return mask;
102
- }
103
-
104
- // Archetype storage
105
- const archetypes = new Map(); // bitmask → Archetype
106
- const entityArchetype = new Map(); // entityId → Archetype
107
-
108
- // Query cache
109
- let queryCacheVersion = 0;
110
- const queryCache = new Map(); // queryKey → { version, archetypes[] }
111
-
112
- function getOrCreateArchetype(types) {
113
- const key = computeMask(types);
114
- let arch = archetypes.get(key);
115
- if (!arch) {
116
- const tracked = trackFilter !== 0 && (key & trackFilter) !== 0;
117
- arch = {
118
- key,
119
- types: new Set(types),
120
- entityIds: [],
121
- components: new Map(),
122
- snapshots: tracked ? new Map() : null,
123
- snapshotEntityIds: tracked ? [] : null,
124
- snapshotCount: 0,
125
- entityToIndex: new Map(),
126
- count: 0,
127
- capacity: INITIAL_CAPACITY
128
- };
129
- for (const t of types) {
130
- const schema = componentSchemas.get(t);
131
- const store = schema ? createSoAStore(schema, INITIAL_CAPACITY) : null;
132
- arch.components.set(t, store);
133
- if (tracked && store) {
134
- arch.snapshots.set(t, createSnapshotStore(schema, INITIAL_CAPACITY));
135
- }
136
- }
137
- archetypes.set(key, arch);
138
- if (tracked) trackedArchetypes.push(arch);
139
- queryCacheVersion++;
140
- }
141
- return arch;
142
- }
143
-
144
- function ensureCapacity(arch) {
145
- if (arch.count < arch.capacity) return;
146
- const newCap = arch.capacity * 2;
147
- arch.capacity = newCap;
148
- for (const [type, store] of arch.components) {
149
- if (store) {
150
- growSoAStore(store, newCap);
151
- if (arch.snapshots) {
152
- const snap = arch.snapshots.get(type);
153
- if (snap) growSnapshotStore(snap, store._schema, newCap);
154
- }
155
- }
156
- }
157
- }
158
-
159
- function addToArchetype(arch, entityId, componentMap) {
160
- ensureCapacity(arch);
161
- const idx = arch.count;
162
- arch.entityIds[idx] = entityId;
163
- for (const t of arch.types) {
164
- const store = arch.components.get(t);
165
- if (store) soaWrite(store, idx, componentMap[t]);
166
- }
167
- arch.entityToIndex.set(entityId, idx);
168
- arch.count++;
169
- entityArchetype.set(entityId, arch);
170
- }
171
-
172
- function removeFromArchetype(arch, entityId) {
173
- const idx = arch.entityToIndex.get(entityId);
174
- const lastIdx = arch.count - 1;
175
-
176
- if (idx !== lastIdx) {
177
- const lastEntity = arch.entityIds[lastIdx];
178
- arch.entityIds[idx] = lastEntity;
179
- for (const [type, store] of arch.components) {
180
- if (store) soaSwap(store, idx, lastIdx);
181
- }
182
- arch.entityToIndex.set(lastEntity, idx);
183
- }
184
-
185
- arch.entityIds.length = lastIdx;
186
- arch.entityToIndex.delete(entityId);
187
- arch.count--;
188
- entityArchetype.delete(entityId);
189
- }
190
-
191
- function readComponentData(arch, type, idx) {
192
- const store = arch.components.get(type);
193
- if (!store) return undefined;
194
- return soaRead(store, idx);
195
- }
196
-
197
- function getMatchingArchetypes(types, excludeTypes) {
198
- const includeMask = computeMask(types);
199
- const excludeMask = excludeTypes && excludeTypes.length > 0 ? computeMask(excludeTypes) : 0;
200
- const queryKey = `${includeMask}:${excludeMask}`;
201
-
202
- const cached = queryCache.get(queryKey);
203
- if (cached && cached.version === queryCacheVersion) {
204
- return cached.archetypes;
205
- }
206
-
207
- const matching = [];
208
- for (const arch of archetypes.values()) {
209
- if ((arch.key & includeMask) === includeMask &&
210
- (arch.key & excludeMask) === 0) {
211
- matching.push(arch);
212
- }
213
- }
214
-
215
- queryCache.set(queryKey, { version: queryCacheVersion, archetypes: matching });
216
- return matching;
217
- }
218
-
219
- return {
220
- createEntity() {
221
- const id = nextId++;
222
- allEntityIds.add(id);
223
- return id;
224
- },
225
-
226
- destroyEntity(id) {
227
- const arch = entityArchetype.get(id);
228
- if (arch) {
229
- if (destroyedSet && (arch.key & trackFilter)) destroyedSet.add(id);
230
- removeFromArchetype(arch, id);
231
- }
232
- allEntityIds.delete(id);
233
- },
234
-
235
- addComponent(entityId, comp, data) {
236
- const type = toSym(comp);
237
- const arch = entityArchetype.get(entityId);
238
-
239
- if (!arch) {
240
- const newArch = getOrCreateArchetype([type]);
241
- addToArchetype(newArch, entityId, { [type]: data });
242
- return;
243
- }
244
-
245
- if (arch.types.has(type)) {
246
- const store = arch.components.get(type);
247
- if (store) {
248
- const idx = arch.entityToIndex.get(entityId);
249
- soaWrite(store, idx, data);
250
- }
251
- return;
252
- }
253
-
254
- const newTypes = [...arch.types, type];
255
- const newArch = getOrCreateArchetype(newTypes);
256
-
257
- const idx = arch.entityToIndex.get(entityId);
258
- const map = { [type]: data };
259
- for (const t of arch.types) {
260
- map[t] = readComponentData(arch, t, idx);
261
- }
262
-
263
- removeFromArchetype(arch, entityId);
264
- addToArchetype(newArch, entityId, map);
265
- },
266
-
267
- removeComponent(entityId, comp) {
268
- const type = toSym(comp);
269
- const arch = entityArchetype.get(entityId);
270
- if (!arch || !arch.types.has(type)) return;
271
-
272
- // If entity is leaving a tracked archetype, treat as destroyed
273
- if (destroyedSet && (arch.key & trackFilter)) destroyedSet.add(entityId);
274
-
275
- if (arch.types.size === 1) {
276
- removeFromArchetype(arch, entityId);
277
- return;
278
- }
279
-
280
- const newTypes = [];
281
- for (const t of arch.types) {
282
- if (t !== type) newTypes.push(t);
283
- }
284
- const newArch = getOrCreateArchetype(newTypes);
285
-
286
- const idx = arch.entityToIndex.get(entityId);
287
- const map = {};
288
- for (const t of newTypes) {
289
- map[t] = readComponentData(arch, t, idx);
290
- }
291
-
292
- removeFromArchetype(arch, entityId);
293
- addToArchetype(newArch, entityId, map);
294
- },
295
-
296
- getComponent(entityId, comp) {
297
- const type = toSym(comp);
298
- const arch = entityArchetype.get(entityId);
299
- if (!arch) return undefined;
300
- const idx = arch.entityToIndex.get(entityId);
301
- if (idx === undefined) return undefined;
302
- return readComponentData(arch, type, idx);
303
- },
304
-
305
- get(entityId, fieldRef) {
306
- const arch = entityArchetype.get(entityId);
307
- if (!arch) return undefined;
308
- const store = arch.components.get(fieldRef._sym);
309
- if (!store) return undefined;
310
- const idx = arch.entityToIndex.get(entityId);
311
- return store[fieldRef._field][idx];
312
- },
313
-
314
- set(entityId, fieldRef, value) {
315
- const arch = entityArchetype.get(entityId);
316
- if (!arch) return;
317
- const store = arch.components.get(fieldRef._sym);
318
- if (!store) return;
319
- const idx = arch.entityToIndex.get(entityId);
320
- store[fieldRef._field][idx] = value;
321
- },
322
-
323
- hasComponent(entityId, comp) {
324
- const type = toSym(comp);
325
- const arch = entityArchetype.get(entityId);
326
- return arch ? arch.types.has(type) : false;
327
- },
328
-
329
- query(includeTypes, excludeTypes) {
330
- const matching = getMatchingArchetypes(includeTypes, excludeTypes);
331
- const result = [];
332
- for (let a = 0; a < matching.length; a++) {
333
- const arch = matching[a];
334
- const ids = arch.entityIds;
335
- for (let i = 0; i < arch.count; i++) {
336
- result.push(ids[i]);
337
- }
338
- }
339
- return result;
340
- },
341
-
342
- getAllEntities() {
343
- return [...allEntityIds];
344
- },
345
-
346
- createEntityWith(...args) {
347
- const id = nextId++;
348
- allEntityIds.add(id);
349
-
350
- const types = [];
351
- const map = {};
352
- for (let i = 0; i < args.length; i += 2) {
353
- const sym = toSym(args[i]);
354
- types.push(sym);
355
- map[sym] = args[i + 1];
356
- }
357
- const arch = getOrCreateArchetype(types);
358
- addToArchetype(arch, id, map);
359
-
360
- if (createdSet && (arch.key & trackFilter)) createdSet.add(id);
361
- return id;
362
- },
363
-
364
- count(includeTypes, excludeTypes) {
365
- const matching = getMatchingArchetypes(includeTypes, excludeTypes);
366
- let total = 0;
367
- for (let a = 0; a < matching.length; a++) {
368
- total += matching[a].count;
369
- }
370
- return total;
371
- },
372
-
373
- forEach(includeTypes, callback, excludeTypes) {
374
- const matching = getMatchingArchetypes(includeTypes, excludeTypes);
375
- for (let a = 0; a < matching.length; a++) {
376
- const arch = matching[a];
377
- if (arch.count === 0) continue;
378
- const snaps = arch.snapshots;
379
- const view = {
380
- id: arch.key,
381
- entityIds: arch.entityIds,
382
- count: arch.count,
383
- snapshotEntityIds: arch.snapshotEntityIds,
384
- snapshotCount: arch.snapshotCount,
385
- field(ref) {
386
- const sym = ref._sym || ref;
387
- const store = arch.components.get(sym);
388
- if (!store) return undefined;
389
- return store[ref._field];
390
- },
391
- snapshot(ref) {
392
- if (!snaps) return undefined;
393
- const sym = ref._sym || ref;
394
- const snap = snaps.get(sym);
395
- if (!snap) return undefined;
396
- return snap[ref._field];
397
- }
398
- };
399
- callback(view);
400
- }
401
- },
402
-
403
- enableTracking(filterComponent) {
404
- trackFilter = 1 << getBit(filterComponent);
405
- createdSet = new Set();
406
- destroyedSet = new Set();
407
- // Retroactively add snapshots to existing matching archetypes
408
- for (const arch of archetypes.values()) {
409
- if ((arch.key & trackFilter) !== 0 && !arch.snapshots) {
410
- arch.snapshots = new Map();
411
- arch.snapshotEntityIds = [];
412
- arch.snapshotCount = 0;
413
- for (const [t, store] of arch.components) {
414
- if (store) {
415
- arch.snapshots.set(t, createSnapshotStore(store._schema, arch.capacity));
416
- }
417
- }
418
- trackedArchetypes.push(arch);
419
- }
420
- }
421
- },
422
-
423
- flushChanges() {
424
- const result = { created: createdSet, destroyed: destroyedSet };
425
- createdSet = new Set();
426
- destroyedSet = new Set();
427
- return result;
428
- },
429
-
430
- flushSnapshots() {
431
- for (let a = 0; a < trackedArchetypes.length; a++) {
432
- const arch = trackedArchetypes[a];
433
- const count = arch.count;
434
- // Copy entityIds
435
- const eids = arch.entityIds;
436
- const snapEids = arch.snapshotEntityIds;
437
- for (let i = 0; i < count; i++) snapEids[i] = eids[i];
438
- arch.snapshotCount = count;
439
- // Copy all field arrays via .set() (one memcpy per field)
440
- for (const [type, store] of arch.components) {
441
- if (!store) continue;
442
- const snap = arch.snapshots.get(type);
443
- if (!snap) continue;
444
- for (const field in store._schema) {
445
- const src = store[field];
446
- const dst = snap[field];
447
- if (src.set) {
448
- // TypedArray — use .set() for memcpy, only copy active region
449
- dst.set(src.subarray(0, count));
450
- } else {
451
- // Regular Array (string fields)
452
- for (let i = 0; i < count; i++) dst[i] = src[i];
453
- }
454
- }
455
- }
456
- }
457
- },
458
-
459
- serialize(symbolToName, stripComponents = [], skipEntitiesWith = [], { serializers } = {}) {
460
- const stripSymbols = new Set(stripComponents.map(toSym));
461
- const skipSymbols = new Set(skipEntitiesWith.map(toSym));
462
- const skipEntityIds = new Set();
463
-
464
- if (skipSymbols.size > 0) {
465
- for (const arch of archetypes.values()) {
466
- let hasSkip = false;
467
- for (const sym of skipSymbols) {
468
- if (arch.types.has(sym)) { hasSkip = true; break; }
469
- }
470
- if (!hasSkip) continue;
471
- for (let i = 0; i < arch.count; i++) {
472
- skipEntityIds.add(arch.entityIds[i]);
473
- }
474
- }
475
- }
476
-
477
- const serializedComponents = {};
478
-
479
- for (const arch of archetypes.values()) {
480
- for (const [sym, store] of arch.components) {
481
- if (!store) continue;
482
- if (stripSymbols.has(sym) || skipSymbols.has(sym)) continue;
483
- const name = symbolToName.get(sym);
484
- if (!name) continue;
485
-
486
- if (!serializedComponents[name]) {
487
- serializedComponents[name] = {};
488
- }
489
- const entries = serializedComponents[name];
490
-
491
- const customSerializer = serializers && serializers.get(name);
492
-
493
- for (let i = 0; i < arch.count; i++) {
494
- const entityId = arch.entityIds[i];
495
- if (skipEntityIds.has(entityId)) continue;
496
- const value = soaRead(store, i);
497
- entries[entityId] = customSerializer ? customSerializer(value) : value;
498
- }
499
- }
500
- }
501
-
502
- for (const name of Object.keys(serializedComponents)) {
503
- if (Object.keys(serializedComponents[name]).length === 0) {
504
- delete serializedComponents[name];
505
- }
506
- }
507
-
508
- const serializedEntities = [];
509
- for (const id of allEntityIds) {
510
- if (!skipEntityIds.has(id)) serializedEntities.push(id);
511
- }
512
-
513
- return {
514
- nextId,
515
- entities: serializedEntities,
516
- components: serializedComponents
517
- };
518
- },
519
-
520
- deserialize(data, nameToSymbol, { deserializers } = {}) {
521
- allEntityIds.clear();
522
- archetypes.clear();
523
- entityArchetype.clear();
524
- queryCache.clear();
525
- queryCacheVersion = 0;
526
-
527
- nextId = data.nextId;
528
-
529
- const entityComponents = new Map();
530
-
531
- for (const id of data.entities) {
532
- allEntityIds.add(id);
533
- entityComponents.set(id, {});
534
- }
535
-
536
- for (const [name, store] of Object.entries(data.components)) {
537
- const entry = nameToSymbol[name];
538
- if (!entry) continue;
539
- const sym = toSym(entry);
540
-
541
- const customDeserializer = deserializers && deserializers.get(name);
542
-
543
- for (const [entityIdStr, compData] of Object.entries(store)) {
544
- const entityId = Number(entityIdStr);
545
- const obj = entityComponents.get(entityId);
546
- if (!obj) continue;
547
-
548
- if (customDeserializer) {
549
- obj[sym] = customDeserializer(compData);
550
- } else {
551
- obj[sym] = compData;
552
- }
553
- }
554
- }
555
-
556
- const groupedByKey = new Map();
557
- for (const [entityId, compMap] of entityComponents) {
558
- const types = Object.getOwnPropertySymbols(compMap);
559
- if (types.length === 0) continue;
560
-
561
- const key = computeMask(types);
562
- if (!groupedByKey.has(key)) {
563
- groupedByKey.set(key, []);
564
- }
565
- groupedByKey.get(key).push({ entityId, compMap });
566
- }
567
-
568
- for (const [key, entries] of groupedByKey) {
569
- const types = Object.getOwnPropertySymbols(entries[0].compMap);
570
- const arch = getOrCreateArchetype(types);
571
-
572
- for (const { entityId, compMap } of entries) {
573
- addToArchetype(arch, entityId, compMap);
574
- }
575
- }
576
- }
577
- };
578
- }
package/src/index.d.ts DELETED
@@ -1,118 +0,0 @@
1
- // === Basis types ===
2
- export type EntityId = number;
3
-
4
- // === Field reference descriptor ===
5
- export interface FieldRef<V = number> {
6
- readonly _sym: symbol;
7
- readonly _field: string;
8
- }
9
-
10
- // === Component definition ===
11
- declare const __phantom: unique symbol;
12
- export type ComponentDef<T = unknown> = {
13
- readonly _sym: symbol;
14
- readonly _name: string;
15
- readonly [__phantom]?: T;
16
- } & (T extends Record<string, number | string>
17
- ? { readonly [K in keyof T & string]: FieldRef<T[K]> }
18
- : {});
19
-
20
- /** @deprecated Use ComponentDef<T> instead */
21
- export type Component<T = unknown> = ComponentDef<T>;
22
- export type ComponentType = ComponentDef;
23
-
24
- // === TypedArray schema ===
25
- export type TypedArrayType = 'f32' | 'f64' | 'i8' | 'i16' | 'i32' | 'u8' | 'u16' | 'u32' | 'string';
26
-
27
- export type Schema = Record<string, TypedArrayType>;
28
-
29
- /** Maps a schema type to its runtime value type */
30
- type FieldToType<T extends TypedArrayType> = T extends 'string' ? string : number;
31
-
32
- /** Maps a schema definition to its runtime value type */
33
- type SchemaToType<S extends Schema> = { [K in keyof S]: FieldToType<S[K]> };
34
-
35
- export declare const TYPED: unique symbol;
36
-
37
- export declare const componentSchemas: Map<symbol, Record<string, Float32ArrayConstructor | Float64ArrayConstructor | Int8ArrayConstructor | Int16ArrayConstructor | Int32ArrayConstructor | Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | ArrayConstructor>>;
38
-
39
- // === TypedArray union ===
40
- type TypedArray = Float32Array | Float64Array | Int8Array | Int16Array | Int32Array | Uint8Array | Uint16Array | Uint32Array;
41
-
42
- // === ArchetypeView (forEach callback) ===
43
- export interface ArchetypeView {
44
- readonly id: number;
45
- readonly entityIds: EntityId[];
46
- readonly count: number;
47
- readonly snapshotEntityIds: EntityId[] | null;
48
- readonly snapshotCount: number;
49
- field(ref: FieldRef<any>): TypedArray | unknown[] | undefined;
50
- snapshot(ref: FieldRef<any>): TypedArray | unknown[] | undefined;
51
- }
52
-
53
- // === Serialize/Deserialize ===
54
- export interface SerializeOptions {
55
- serializers?: Map<string, (data: unknown) => unknown>;
56
- }
57
-
58
- export interface DeserializeOptions {
59
- deserializers?: Map<string, (data: unknown) => unknown>;
60
- }
61
-
62
- export interface SerializedData {
63
- nextId: number;
64
- entities: EntityId[];
65
- components: Record<string, Record<string, unknown>>;
66
- }
67
-
68
- // === EntityManager ===
69
- export interface EntityManager {
70
- createEntity(): EntityId;
71
- destroyEntity(id: EntityId): void;
72
- addComponent<T>(entityId: EntityId, type: ComponentDef<T>, data: T): void;
73
- removeComponent(entityId: EntityId, type: ComponentDef): void;
74
- getComponent<T>(entityId: EntityId, type: ComponentDef<T>): T | undefined;
75
- get<V>(entityId: EntityId, fieldRef: FieldRef<V>): V | undefined;
76
- set<V>(entityId: EntityId, fieldRef: FieldRef<V>, value: V): void;
77
- hasComponent(entityId: EntityId, type: ComponentDef): boolean;
78
- query(include: ComponentDef[], exclude?: ComponentDef[]): EntityId[];
79
- getAllEntities(): EntityId[];
80
- createEntityWith(...args: unknown[]): EntityId;
81
- count(include: ComponentDef[], exclude?: ComponentDef[]): number;
82
- forEach(include: ComponentDef[], callback: (view: ArchetypeView) => void, exclude?: ComponentDef[]): void;
83
- enableTracking(filterComponent: ComponentDef): void;
84
- flushChanges(): { created: Set<EntityId>; destroyed: Set<EntityId> };
85
- flushSnapshots(): void;
86
- serialize(
87
- symbolToName: Map<symbol, string>,
88
- stripComponents?: ComponentDef[],
89
- skipEntitiesWith?: ComponentDef[],
90
- options?: SerializeOptions
91
- ): SerializedData;
92
- deserialize(
93
- data: SerializedData,
94
- nameToSymbol: Record<string, ComponentDef>,
95
- options?: DeserializeOptions
96
- ): void;
97
- }
98
-
99
- // === Profiler ===
100
- export interface ProfilerEntry {
101
- avg: number;
102
- }
103
-
104
- export interface Profiler {
105
- readonly enabled: boolean;
106
- setEnabled(value: boolean): void;
107
- begin(): number;
108
- end(name: string, t0: number): void;
109
- record(name: string, ms: number): void;
110
- getData(): Map<string, ProfilerEntry>;
111
- }
112
-
113
- // === Exports ===
114
- export function createEntityManager(): EntityManager;
115
- export function component(name: string): ComponentDef;
116
- export function component<T extends TypedArrayType, F extends string>(name: string, type: T, fields: F[]): ComponentDef<Record<F, FieldToType<T>>>;
117
- export function component<S extends Schema>(name: string, schema: S): ComponentDef<SchemaToType<S>>;
118
- export const profiler: Profiler;