@codehz/ecs 0.8.2 → 0.10.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 (57) hide show
  1. package/README.en.md +26 -3
  2. package/README.md +41 -4
  3. package/dist/builder.d.mts +348 -83
  4. package/dist/index.d.mts +2 -2
  5. package/dist/index.mjs +2 -2
  6. package/dist/testing.d.mts +1 -1
  7. package/dist/testing.mjs +1 -1
  8. package/dist/world.mjs +1922 -1400
  9. package/dist/world.mjs.map +1 -1
  10. package/examples/debug-observability.ts +92 -0
  11. package/examples/inventory-system-relations.ts +1 -1
  12. package/examples/parent-child-hierarchy.ts +18 -38
  13. package/examples/spatial-grid.ts +1 -1
  14. package/package.json +1 -1
  15. package/skills/ecs/SKILL.md +4 -4
  16. package/src/__tests__/component/singleton.test.ts +116 -35
  17. package/src/__tests__/core/archetype.test.ts +155 -13
  18. package/src/__tests__/core/bitset.test.ts +12 -0
  19. package/src/__tests__/entity/entity.test.ts +33 -0
  20. package/src/__tests__/entity/id-system.test.ts +40 -0
  21. package/src/__tests__/perf/comprehensive.perf.test.ts +6 -9
  22. package/src/__tests__/perf/serialization.perf.test.ts +242 -0
  23. package/src/__tests__/perf/{dontfragment-wildcard.perf.test.ts → sparse-wildcard.perf.test.ts} +13 -16
  24. package/src/__tests__/query/caching.test.ts +62 -0
  25. package/src/__tests__/query/filter.test.ts +16 -22
  26. package/src/__tests__/query/perf.test.ts +3 -5
  27. package/src/__tests__/relations/hierarchy.test.ts +208 -0
  28. package/src/__tests__/relations/{dont-fragment → sparse}/basic.test.ts +64 -69
  29. package/src/__tests__/relations/{dont-fragment → sparse}/query-notification.test.ts +17 -9
  30. package/src/__tests__/serialization/bounds.test.ts +133 -1
  31. package/src/__tests__/world/commands.test.ts +337 -0
  32. package/src/__tests__/world/component-management.test.ts +6 -5
  33. package/src/__tests__/world/debug-stats.test.ts +206 -0
  34. package/src/__tests__/world/multi-component-hooks.test.ts +44 -0
  35. package/src/__tests__/world/serialize.test.ts +17 -0
  36. package/src/__tests__/world/wildcard-relation-hooks.test.ts +127 -0
  37. package/src/archetype/archetype.ts +96 -46
  38. package/src/archetype/helpers.ts +7 -29
  39. package/src/archetype/store.ts +35 -20
  40. package/src/commands/buffer.ts +5 -2
  41. package/src/commands/changeset.ts +0 -31
  42. package/src/component/registry.ts +64 -63
  43. package/src/entity/index.ts +6 -3
  44. package/src/index.ts +15 -0
  45. package/src/query/filter.ts +4 -10
  46. package/src/query/query.ts +12 -12
  47. package/src/storage/serialization.ts +29 -2
  48. package/src/types/index.ts +71 -0
  49. package/src/world/archetype-manager.ts +283 -0
  50. package/src/world/command-executor.ts +258 -0
  51. package/src/world/commands.ts +44 -56
  52. package/src/world/debug-stats.ts +147 -0
  53. package/src/world/hooks.ts +8 -0
  54. package/src/world/operations.ts +88 -0
  55. package/src/world/serialization.ts +32 -18
  56. package/src/world/singleton.ts +51 -0
  57. package/src/world/world.ts +429 -457
package/README.en.md CHANGED
@@ -78,7 +78,7 @@ const ChildOf = component({ exclusive: true, name: "ChildOf" });
78
78
  | `name` | `string` | Component name, used for serialization/debugging |
79
79
  | `exclusive` | `boolean` | Relation components only: an entity can have at most one relation of the same base component |
80
80
  | `cascadeDelete` | `boolean` | Entity relations only: when the target entity is deleted, the **entire referencing entity** is deleted. Differs from default behavior (default only cleans up the relation component, the entity survives). Supports transitive cascading. |
81
- | `dontFragment` | `boolean` | Relation components only: relations with different target entities are stored in the same Archetype, preventing excessive fragmentation |
81
+ | `sparse` | `boolean` | Relation components only: relations with different target entities are stored in the same Archetype, preventing excessive fragmentation (legacy `dontFragment` alias remains fully supported) |
82
82
  | `merge` | `(prev, next) => T` | Merge strategy when `set()` is called multiple times on the same component within a single sync batch |
83
83
 
84
84
  ### Lifecycle Hooks
@@ -274,7 +274,7 @@ component<T>();
274
274
  // With a name
275
275
  component<T>("Name");
276
276
  // With options
277
- component<T>({ name?: string, exclusive?: boolean, cascadeDelete?: boolean, dontFragment?: boolean, merge?: (prev, next) => T });
277
+ component<T>({ name?: string, exclusive?: boolean, cascadeDelete?: boolean, sparse?: boolean, dontFragment?: boolean /* legacy alias, fully compatible */, merge?: (prev, next) => T });
278
278
  ```
279
279
 
280
280
  ### relation()
@@ -288,6 +288,29 @@ relation(componentId, "*");
288
288
  relation(componentId, otherComponentId);
289
289
  ```
290
290
 
291
+ ### Relation & Hierarchy Companion Tools (New)
292
+
293
+ To stop users from repeatedly hand-writing `buildChildrenByParent` + recursive descent for parent-child hierarchies and inventory systems, we now ship first-class helpers:
294
+
295
+ ```typescript
296
+ const ChildOf = component<void>({ exclusive: true, sparse: true });
297
+ const world = new World();
298
+ // ... build hierarchy ...
299
+
300
+ // Recommended usage (standalone functions removed to simplify API surface)
301
+ const kids = world.getChildren(parent, ChildOf);
302
+ const p = world.getParent(child, ChildOf);
303
+
304
+ for (const { entity, depth } of world.iterateDescendants(root, ChildOf)) { ... }
305
+
306
+ const items = world.getRelationTargets(player, InInventory);
307
+ const owners = world.getRelationSources(sword, InInventory);
308
+ ```
309
+
310
+ The same functionality is also available as methods on `World` instances. All helpers are fully compatible with data-bearing relations, exclusive/non-exclusive, and post-`sync()` semantics.
311
+
312
+ See `src/relations/hierarchy.ts` and the new test suite for details.
313
+
291
314
  ### Component / Entity ID Rules
292
315
 
293
316
  - Component ID: `1` – `1023`
@@ -391,7 +414,7 @@ src/
391
414
  │ ├── component-registry.ts # Component registry
392
415
  │ ├── component-entity-store.ts # Singleton component storage
393
416
  │ ├── component-type-utils.ts # Component type utilities
394
- │ ├── dont-fragment-store.ts # DontFragment storage
417
+ │ ├── store.ts # SparseStore (internal sparse storage)
395
418
  │ ├── entity.ts # Entity/component/relation type exports (aggregate)
396
419
  │ ├── entity-types.ts # Entity ID type definitions & constants
397
420
  │ ├── entity-relation.ts # Relation ID encoding/decoding
package/README.md CHANGED
@@ -78,7 +78,7 @@ const ChildOf = component({ exclusive: true, name: "ChildOf" });
78
78
  | `name` | `string` | 组件名称,用于序列化/调试 |
79
79
  | `exclusive` | `boolean` | 仅关系组件:同一实体对同一基础组件最多只能有一个关系 |
80
80
  | `cascadeDelete` | `boolean` | 仅实体关系:删除目标实体时,持有该关系的**整个实体**也会被删除。区别于默认行为(默认仅清理关系组件,实体保留)。支持传递级联。 |
81
- | `dontFragment` | `boolean` | 仅关系组件:不同目标实体的关系存放在同一 Archetype,防止因目标不同而过度碎片化 |
81
+ | `sparse` | `boolean` | 仅关系组件:不同目标实体的关系存放在同一 Archetype,防止因目标不同而过度碎片化(旧别名 `dontFragment` 仍完全兼容) |
82
82
  | `merge` | `(prev, next) => T` | 在同一 sync 批次中对同一组件反复 `set()` 时的合并策略 |
83
83
 
84
84
  ### 生命周期钩子
@@ -222,7 +222,7 @@ bun run examples/inventory-system-relations.ts
222
222
  | `spawnMany(count, configure)` | 批量创建多个实体 |
223
223
  | `exists(entity)` | 检查实体是否存在 |
224
224
  | `set(entity, componentId, data?)` | 添加/更新组件(缓冲,`sync()` 后生效)。对 `void` 组件可不传 data |
225
- | `set(componentId, data)` | 单例组件简写:`world.set(GlobalConfig, { ... })` |
225
+ | `singleton(componentId)` | 获取单例组件句柄,推荐用 `world.singleton(Config).set(value)` |
226
226
  | `get(entity, componentId?)` | 获取组件数据。**若组件不存在会抛出异常**,请先用 `has()` 检查或使用 `getOptional()` |
227
227
  | `getOptional(entity, componentId?)` | 安全获取组件,返回 `{ value: T } \| undefined` |
228
228
  | `has(entity, componentId?)` | 检查组件是否存在 |
@@ -236,6 +236,18 @@ bun run examples/inventory-system-relations.ts
236
236
  | `serialize()` | 序列化世界状态为快照对象 |
237
237
  | `sync()` | 执行所有延迟命令 |
238
238
 
239
+ 单例组件推荐写法:
240
+
241
+ ```ts
242
+ const config = world.singleton(GlobalConfig);
243
+ config.set({ debug: true });
244
+ world.sync();
245
+
246
+ if (config.has()) {
247
+ console.log(config.get());
248
+ }
249
+ ```
250
+
239
251
  ### Query
240
252
 
241
253
  查询通过 `world.createQuery()` 创建,应**跨帧复用**以获得最佳性能。
@@ -274,7 +286,7 @@ component<T>();
274
286
  // 指定名称
275
287
  component<T>("Name");
276
288
  // 带选项
277
- component<T>({ name?: string, exclusive?: boolean, cascadeDelete?: boolean, dontFragment?: boolean, merge?: (prev, next) => T });
289
+ component<T>({ name?: string, exclusive?: boolean, cascadeDelete?: boolean, sparse?: boolean, dontFragment?: boolean /* 旧别名,完全兼容 */, merge?: (prev, next) => T });
278
290
  ```
279
291
 
280
292
  ### relation()
@@ -288,6 +300,31 @@ relation(componentId, "*");
288
300
  relation(componentId, otherComponentId);
289
301
  ```
290
302
 
303
+ ### 关系/层级配套工具(新)
304
+
305
+ 为避免用户在父子层级(`ChildOf`)和库存系统(`InInventory`)中反复手写 `buildChildrenByParent` + 递归遍历逻辑,我们提供了配套工具:
306
+
307
+ ```typescript
308
+ const ChildOf = component<void>({ exclusive: true, sparse: true });
309
+ const world = new World();
310
+ // ... 创建层级 ...
311
+
312
+ // 推荐直接在 World 实例上使用(API 表面已简化)
313
+ const kids = world.getChildren(parent, ChildOf);
314
+ const p = world.getParent(child, ChildOf);
315
+
316
+ for (const { entity, depth, parent } of world.iterateDescendants(root, ChildOf)) {
317
+ // ...
318
+ }
319
+
320
+ const items = world.getRelationTargets(player, InInventory);
321
+ const owners = world.getRelationSources(sword, InInventory);
322
+ ```
323
+
324
+ 这些工具全部在 `world` 实例方法上也有对应(`world.getChildren(...)` 等),并完整支持数据负载关系、独占/非独占、删除后一致性。
325
+
326
+ 详见 `src/relations/hierarchy.ts` 和新增的测试。
327
+
291
328
  ### 组件 / 实体 ID 规则
292
329
 
293
330
  - 组件 ID:`1` ~ `1023`
@@ -391,7 +428,7 @@ src/
391
428
  │ ├── component-registry.ts # 组件注册表
392
429
  │ ├── component-entity-store.ts # 单例组件存储
393
430
  │ ├── component-type-utils.ts # 组件类型工具
394
- │ ├── dont-fragment-store.ts # DontFragment 存储
431
+ │ ├── store.ts # SparseStore (内部稀疏存储)
395
432
  │ ├── entity.ts # 实体/组件/关系类型导出(聚合)
396
433
  │ ├── entity-types.ts # 实体 ID 类型定义与常量
397
434
  │ ├── entity-relation.ts # 关系 ID 编码/解码