@codehz/ecs 0.0.0 → 0.0.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.
package/README.md CHANGED
@@ -4,10 +4,11 @@
4
4
 
5
5
  ## 特性
6
6
 
7
- - 🚀 高性能:基于原型的组件存储和高效的查询系统
7
+ - 🚀 高性能:基于 Archetype 的组件存储和高效的查询系统
8
8
  - 🔧 类型安全:完整的 TypeScript 支持
9
9
  - 🏗️ 模块化:清晰的架构,支持自定义系统和组件
10
10
  - 📦 轻量级:零依赖,易于集成
11
+ - ⚡ 内存高效:连续内存布局,优化的迭代性能
11
12
 
12
13
  ## 安装
13
14
 
@@ -44,14 +45,48 @@ world.flushCommands();
44
45
 
45
46
  // 创建查询并更新
46
47
  const query = world.createQuery([PositionId, VelocityId]);
48
+ const deltaTime = 1.0 / 60.0; // 假设60FPS
47
49
  query.forEach([PositionId, VelocityId], (entity, position, velocity) => {
48
50
  position.x += velocity.x * deltaTime;
49
51
  position.y += velocity.y * deltaTime;
50
52
  });
51
53
  ```
52
54
 
55
+ ### 组件生命周期钩子
56
+
57
+ ECS 支持在组件添加或移除时执行回调函数:
58
+
59
+ ```typescript
60
+ // 注册组件生命周期钩子
61
+ world.registerComponentLifecycleHook(PositionId, {
62
+ onAdded: (entityId, componentType, component) => {
63
+ console.log(`组件 ${componentType} 被添加到实体 ${entityId}`);
64
+ },
65
+ onRemoved: (entityId, componentType) => {
66
+ console.log(`组件 ${componentType} 被从实体 ${entityId} 移除`);
67
+ }
68
+ });
69
+
70
+ // 你也可以只注册其中一个钩子
71
+ world.registerComponentLifecycleHook(VelocityId, {
72
+ onRemoved: (entityId, componentType) => {
73
+ console.log(`组件 ${componentType} 被从实体 ${entityId} 移除`);
74
+ }
75
+ });
76
+
77
+ // 添加组件时会触发钩子
78
+ world.addComponent(entity, PositionId, { x: 0, y: 0 });
79
+ world.flushCommands(); // 钩子在这里被调用
80
+ ```
81
+
53
82
  ### 运行示例
54
83
 
84
+ ```bash
85
+ bun run demo
86
+ ```
87
+
88
+ 或者直接运行:
89
+
55
90
  ```bash
56
91
  bun run examples/simple/demo.ts
57
92
  ```
@@ -65,6 +100,8 @@ bun run examples/simple/demo.ts
65
100
  - `removeComponent(entity, componentId)`: 从实体移除组件
66
101
  - `createQuery(componentIds)`: 创建查询
67
102
  - `registerSystem(system)`: 注册系统
103
+ - `registerComponentLifecycleHook(componentId, hook)`: 注册组件生命周期钩子
104
+ - `unregisterComponentLifecycleHook(componentId, hook)`: 注销组件生命周期钩子
68
105
  - `update(deltaTime)`: 更新世界
69
106
  - `flushCommands()`: 应用命令缓冲区
70
107
 
@@ -75,6 +112,8 @@ bun run examples/simple/demo.ts
75
112
  ### Query
76
113
 
77
114
  - `forEach(componentIds, callback)`: 遍历匹配的实体
115
+ - `getEntities()`: 获取所有匹配实体的ID列表
116
+ - `getEntitiesWithComponents(componentIds)`: 获取实体及其组件数据
78
117
 
79
118
  ### System
80
119
 
@@ -88,6 +127,13 @@ class MySystem implements System {
88
127
  }
89
128
  ```
90
129
 
130
+ ## 性能特点
131
+
132
+ - **Archetype 系统**:实体按组件组合分组,实现连续内存访问
133
+ - **缓存查询**:查询结果自动缓存,减少重复计算
134
+ - **命令缓冲区**:延迟执行组件添加/移除,提高批处理效率
135
+ - **类型安全**:编译时类型检查,无运行时开销
136
+
91
137
  ## 开发
92
138
 
93
139
  ### 运行测试
@@ -106,20 +152,28 @@ bunx tsc --noEmit
106
152
 
107
153
  ```
108
154
  src/
109
- ├── index.ts # 入口文件
110
- ├── entity.ts # 实体和组件管理
111
- ├── world.ts # 世界管理
112
- ├── archetype.ts # 原型系统
113
- ├── query.ts # 查询系统
114
- ├── system.ts # 系统接口
115
- ├── command-buffer.ts # 命令缓冲区
116
- ├── types.ts # 类型定义
117
- └── utils.ts # 工具函数
155
+ ├── index.ts # 入口文件
156
+ ├── entity.ts # 实体和组件管理
157
+ ├── world.ts # 世界管理
158
+ ├── archetype.ts # Archetype 系统(高效组件存储)
159
+ ├── query.ts # 查询系统
160
+ ├── query-filter.ts # 查询过滤器
161
+ ├── system.ts # 系统接口
162
+ ├── command-buffer.ts # 命令缓冲区
163
+ ├── types.ts # 类型定义
164
+ ├── utils.ts # 工具函数
165
+ ├── *.test.ts # 单元测试
166
+ ├── query.example.ts # 查询示例
167
+ └── *.perf.test.ts # 性能测试
118
168
 
119
169
  examples/
120
170
  └── simple/
121
- ├── demo.ts # 基本示例
122
- └── README.md # 示例说明
171
+ ├── demo.ts # 基本示例
172
+ └── README.md # 示例说明
173
+
174
+ scripts/
175
+ ├── build.ts # 构建脚本
176
+ └── release.ts # 发布脚本
123
177
  ```
124
178
 
125
179
  ## 许可证
package/archetype.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { EntityId } from "./entity";
1
+ import type { EntityId, WildcardRelationId } from "./entity";
2
2
  import type { ComponentTuple } from "./types";
3
3
  /**
4
4
  * Archetype class for ECS architecture
@@ -60,6 +60,13 @@ export declare class Archetype {
60
60
  * @param entityId The entity to check
61
61
  */
62
62
  hasEntity(entityId: EntityId): boolean;
63
+ /**
64
+ * Get component data for a specific entity and wildcard relation type
65
+ * Returns an array of all matching relation instances
66
+ * @param entityId The entity
67
+ * @param componentType The wildcard relation type
68
+ */
69
+ getComponent<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, any][] | undefined;
63
70
  /**
64
71
  * Get component data for a specific entity and component type
65
72
  * @param entityId The entity
package/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export * from "./world";
3
3
  export * from "./archetype";
4
4
  export * from "./query";
5
5
  export * from "./system";
6
+ export * from "./types";
package/index.js CHANGED
@@ -275,9 +275,29 @@ class Archetype {
275
275
  getComponent(entityId, componentType) {
276
276
  const index = this.entityToIndex.get(entityId);
277
277
  if (index === undefined) {
278
- return;
278
+ if (getIdType(componentType) === "wildcard-relation") {
279
+ return [];
280
+ } else {
281
+ return;
282
+ }
283
+ }
284
+ if (getIdType(componentType) === "wildcard-relation") {
285
+ const decoded = decodeRelationId(componentType);
286
+ const componentId = decoded.componentId;
287
+ const relations = [];
288
+ for (const relType of this.componentTypes) {
289
+ const relDecoded = decodeRelationId(relType);
290
+ if (relDecoded.componentId === componentId && (getIdType(relType) === "entity-relation" || getIdType(relType) === "component-relation")) {
291
+ const dataArray = this.componentData.get(relType);
292
+ if (dataArray && dataArray[index] !== undefined) {
293
+ relations.push([relDecoded.targetId, dataArray[index]]);
294
+ }
295
+ }
296
+ }
297
+ return relations;
298
+ } else {
299
+ return this.componentData.get(componentType)?.[index];
279
300
  }
280
- return this.componentData.get(componentType)?.[index];
281
301
  }
282
302
  setComponent(entityId, componentType, data) {
283
303
  const index = this.entityToIndex.get(entityId);
@@ -374,17 +394,26 @@ class CommandBuffer {
374
394
  this.commands.push({ type: "destroyEntity", entityId });
375
395
  }
376
396
  execute() {
377
- const entityCommands = new Map;
378
- for (const cmd of this.commands) {
379
- if (!entityCommands.has(cmd.entityId)) {
380
- entityCommands.set(cmd.entityId, []);
397
+ const MAX_ITERATIONS = 100;
398
+ let iterations = 0;
399
+ while (this.commands.length > 0) {
400
+ if (iterations >= MAX_ITERATIONS) {
401
+ throw new Error("Command execution exceeded maximum iterations, possible infinite loop");
402
+ }
403
+ iterations++;
404
+ const currentCommands = [...this.commands];
405
+ this.commands = [];
406
+ const entityCommands = new Map;
407
+ for (const cmd of currentCommands) {
408
+ if (!entityCommands.has(cmd.entityId)) {
409
+ entityCommands.set(cmd.entityId, []);
410
+ }
411
+ entityCommands.get(cmd.entityId).push(cmd);
412
+ }
413
+ for (const [entityId, commands] of entityCommands) {
414
+ this.executeEntityCommands(entityId, commands);
381
415
  }
382
- entityCommands.get(cmd.entityId).push(cmd);
383
- }
384
- for (const [entityId, commands] of entityCommands) {
385
- this.executeEntityCommands(entityId, commands);
386
416
  }
387
- this.commands = [];
388
417
  }
389
418
  getCommands() {
390
419
  return [...this.commands];
@@ -512,6 +541,9 @@ class World {
512
541
  queries = [];
513
542
  commandBuffer;
514
543
  componentToArchetypes = new Map;
544
+ componentLifecycleHooks = new Map;
545
+ wildcardRelationLifecycleHooks = new Map;
546
+ entityReverseIndex = new Map;
515
547
  constructor() {
516
548
  this.commandBuffer = new CommandBuffer((entityId, commands) => this.executeEntityCommands(entityId, commands));
517
549
  }
@@ -530,7 +562,36 @@ class World {
530
562
  if (!archetype) {
531
563
  return;
532
564
  }
565
+ const componentReferences = this.getComponentReferences(entityId);
566
+ for (const { sourceEntityId, componentType } of componentReferences) {
567
+ const sourceArchetype = this.entityToArchetype.get(sourceEntityId);
568
+ if (sourceArchetype) {
569
+ const currentComponents = new Map;
570
+ for (const compType of sourceArchetype.componentTypes) {
571
+ if (compType !== componentType) {
572
+ const data = sourceArchetype.getComponent(sourceEntityId, compType);
573
+ if (data !== undefined) {
574
+ currentComponents.set(compType, data);
575
+ }
576
+ }
577
+ }
578
+ const newComponentTypes = Array.from(currentComponents.keys()).sort((a, b) => a - b);
579
+ const newArchetype = this.getOrCreateArchetype(newComponentTypes);
580
+ sourceArchetype.removeEntity(sourceEntityId);
581
+ if (sourceArchetype.getEntities().length === 0) {
582
+ this.removeEmptyArchetype(sourceArchetype);
583
+ }
584
+ newArchetype.addEntity(sourceEntityId, currentComponents);
585
+ this.entityToArchetype.set(sourceEntityId, newArchetype);
586
+ this.removeComponentReference(sourceEntityId, componentType, entityId);
587
+ this.executeComponentLifecycleHooks(sourceEntityId, new Map, new Set([componentType]));
588
+ }
589
+ }
590
+ this.entityReverseIndex.delete(entityId);
533
591
  archetype.removeEntity(entityId);
592
+ if (archetype.getEntities().length === 0) {
593
+ this.removeEmptyArchetype(archetype);
594
+ }
534
595
  this.entityToArchetype.delete(entityId);
535
596
  this.entityIdManager.deallocate(entityId);
536
597
  }
@@ -541,12 +602,23 @@ class World {
541
602
  if (!this.hasEntity(entityId)) {
542
603
  throw new Error(`Entity ${entityId} does not exist`);
543
604
  }
605
+ const detailedType = getDetailedIdType(componentType);
606
+ if (detailedType.type === "invalid") {
607
+ throw new Error(`Invalid component type: ${componentType}`);
608
+ }
609
+ if (detailedType.type === "wildcard-relation") {
610
+ throw new Error(`Cannot directly add wildcard relation components: ${componentType}`);
611
+ }
544
612
  this.commandBuffer.addComponent(entityId, componentType, component);
545
613
  }
546
614
  removeComponent(entityId, componentType) {
547
615
  if (!this.hasEntity(entityId)) {
548
616
  throw new Error(`Entity ${entityId} does not exist`);
549
617
  }
618
+ const detailedType = getDetailedIdType(componentType);
619
+ if (detailedType.type === "invalid") {
620
+ throw new Error(`Invalid component type: ${componentType}`);
621
+ }
550
622
  this.commandBuffer.removeComponent(entityId, componentType);
551
623
  }
552
624
  destroyEntity(entityId) {
@@ -558,7 +630,14 @@ class World {
558
630
  }
559
631
  getComponent(entityId, componentType) {
560
632
  const archetype = this.entityToArchetype.get(entityId);
561
- return archetype ? archetype.getComponent(entityId, componentType) : undefined;
633
+ if (!archetype) {
634
+ if (getIdType(componentType) === "wildcard-relation") {
635
+ return [];
636
+ } else {
637
+ return;
638
+ }
639
+ }
640
+ return archetype.getComponent(entityId, componentType);
562
641
  }
563
642
  registerSystem(system) {
564
643
  this.systems.push(system);
@@ -569,6 +648,36 @@ class World {
569
648
  this.systems.splice(index, 1);
570
649
  }
571
650
  }
651
+ registerComponentLifecycleHook(componentType, hook) {
652
+ if (!this.componentLifecycleHooks.has(componentType)) {
653
+ this.componentLifecycleHooks.set(componentType, new Set);
654
+ }
655
+ this.componentLifecycleHooks.get(componentType).add(hook);
656
+ }
657
+ unregisterComponentLifecycleHook(componentType, hook) {
658
+ const hooks = this.componentLifecycleHooks.get(componentType);
659
+ if (hooks) {
660
+ hooks.delete(hook);
661
+ if (hooks.size === 0) {
662
+ this.componentLifecycleHooks.delete(componentType);
663
+ }
664
+ }
665
+ }
666
+ registerWildcardRelationLifecycleHook(baseComponentType, hook) {
667
+ if (!this.wildcardRelationLifecycleHooks.has(baseComponentType)) {
668
+ this.wildcardRelationLifecycleHooks.set(baseComponentType, new Set);
669
+ }
670
+ this.wildcardRelationLifecycleHooks.get(baseComponentType).add(hook);
671
+ }
672
+ unregisterWildcardRelationLifecycleHook(baseComponentType, hook) {
673
+ const hooks = this.wildcardRelationLifecycleHooks.get(baseComponentType);
674
+ if (hooks) {
675
+ hooks.delete(hook);
676
+ if (hooks.size === 0) {
677
+ this.wildcardRelationLifecycleHooks.delete(baseComponentType);
678
+ }
679
+ }
680
+ }
572
681
  update(...params) {
573
682
  for (const system of this.systems) {
574
683
  system.update(this, ...params);
@@ -687,8 +796,22 @@ class World {
687
796
  break;
688
797
  case "removeComponent":
689
798
  if (cmd.componentType) {
690
- removes.add(cmd.componentType);
691
- adds.delete(cmd.componentType);
799
+ const detailedType = getDetailedIdType(cmd.componentType);
800
+ if (detailedType.type === "wildcard-relation") {
801
+ const baseComponentId = detailedType.componentId;
802
+ for (const componentType of currentArchetype.componentTypes) {
803
+ const componentDetailedType = getDetailedIdType(componentType);
804
+ if (componentDetailedType.type === "entity-relation" || componentDetailedType.type === "component-relation") {
805
+ if (componentDetailedType.componentId === baseComponentId) {
806
+ removes.add(componentType);
807
+ adds.delete(componentType);
808
+ }
809
+ }
810
+ }
811
+ } else {
812
+ removes.add(cmd.componentType);
813
+ adds.delete(cmd.componentType);
814
+ }
692
815
  }
693
816
  break;
694
817
  }
@@ -713,6 +836,25 @@ class World {
713
836
  currentArchetype.setComponent(entityId, componentType, component);
714
837
  }
715
838
  }
839
+ for (const componentType of removes) {
840
+ const detailedType = getDetailedIdType(componentType);
841
+ if (detailedType.type === "entity-relation") {
842
+ const targetEntityId = detailedType.targetId;
843
+ this.removeComponentReference(entityId, componentType, targetEntityId);
844
+ } else if (detailedType.type === "entity") {
845
+ this.removeComponentReference(entityId, componentType, componentType);
846
+ }
847
+ }
848
+ for (const [componentType, component] of adds) {
849
+ const detailedType = getDetailedIdType(componentType);
850
+ if (detailedType.type === "entity-relation") {
851
+ const targetEntityId = detailedType.targetId;
852
+ this.addComponentReference(entityId, componentType, targetEntityId);
853
+ } else if (detailedType.type === "entity") {
854
+ this.addComponentReference(entityId, componentType, componentType);
855
+ }
856
+ }
857
+ this.executeComponentLifecycleHooks(entityId, adds, removes);
716
858
  }
717
859
  getOrCreateArchetype(componentTypes) {
718
860
  const sortedTypes = [...componentTypes].sort((a, b) => a - b);
@@ -731,6 +873,96 @@ class World {
731
873
  return newArchetype;
732
874
  });
733
875
  }
876
+ addComponentReference(sourceEntityId, componentType, targetEntityId) {
877
+ if (!this.entityReverseIndex.has(targetEntityId)) {
878
+ this.entityReverseIndex.set(targetEntityId, new Set);
879
+ }
880
+ this.entityReverseIndex.get(targetEntityId).add({ sourceEntityId, componentType });
881
+ }
882
+ removeComponentReference(sourceEntityId, componentType, targetEntityId) {
883
+ const references = this.entityReverseIndex.get(targetEntityId);
884
+ if (references) {
885
+ references.forEach((ref) => {
886
+ if (ref.sourceEntityId === sourceEntityId && ref.componentType === componentType) {
887
+ references.delete(ref);
888
+ }
889
+ });
890
+ if (references.size === 0) {
891
+ this.entityReverseIndex.delete(targetEntityId);
892
+ }
893
+ }
894
+ }
895
+ getComponentReferences(targetEntityId) {
896
+ const references = this.entityReverseIndex.get(targetEntityId);
897
+ return references ? Array.from(references) : [];
898
+ }
899
+ removeEmptyArchetype(archetype) {
900
+ if (archetype.getEntities().length > 0) {
901
+ return;
902
+ }
903
+ const index = this.archetypes.indexOf(archetype);
904
+ if (index !== -1) {
905
+ this.archetypes.splice(index, 1);
906
+ }
907
+ const hashKey = this.getComponentTypesHash(archetype.componentTypes);
908
+ this.archetypeMap.delete(hashKey);
909
+ for (const componentType of archetype.componentTypes) {
910
+ const archetypes = this.componentToArchetypes.get(componentType);
911
+ if (archetypes) {
912
+ const compIndex = archetypes.indexOf(archetype);
913
+ if (compIndex !== -1) {
914
+ archetypes.splice(compIndex, 1);
915
+ if (archetypes.length === 0) {
916
+ this.componentToArchetypes.delete(componentType);
917
+ }
918
+ }
919
+ }
920
+ }
921
+ }
922
+ executeComponentLifecycleHooks(entityId, addedComponents, removedComponents) {
923
+ for (const [componentType, component] of addedComponents) {
924
+ const hooks = this.componentLifecycleHooks.get(componentType);
925
+ if (hooks) {
926
+ for (const hook of hooks) {
927
+ if (hook.onAdded) {
928
+ hook.onAdded(entityId, componentType, component);
929
+ }
930
+ }
931
+ }
932
+ const detailedType = getDetailedIdType(componentType);
933
+ if (detailedType.type === "entity-relation" || detailedType.type === "component-relation" || detailedType.type === "wildcard-relation") {
934
+ const wildcardHooks = this.wildcardRelationLifecycleHooks.get(detailedType.componentId);
935
+ if (wildcardHooks) {
936
+ for (const hook of wildcardHooks) {
937
+ if (hook.onAdded) {
938
+ hook.onAdded(entityId, componentType, component);
939
+ }
940
+ }
941
+ }
942
+ }
943
+ }
944
+ for (const componentType of removedComponents) {
945
+ const hooks = this.componentLifecycleHooks.get(componentType);
946
+ if (hooks) {
947
+ for (const hook of hooks) {
948
+ if (hook.onRemoved) {
949
+ hook.onRemoved(entityId, componentType);
950
+ }
951
+ }
952
+ }
953
+ const detailedType = getDetailedIdType(componentType);
954
+ if (detailedType.type === "entity-relation" || detailedType.type === "component-relation" || detailedType.type === "wildcard-relation") {
955
+ const wildcardHooks = this.wildcardRelationLifecycleHooks.get(detailedType.componentId);
956
+ if (wildcardHooks) {
957
+ for (const hook of wildcardHooks) {
958
+ if (hook.onRemoved) {
959
+ hook.onRemoved(entityId, componentType);
960
+ }
961
+ }
962
+ }
963
+ }
964
+ }
965
+ }
734
966
  }
735
967
  export {
736
968
  isRelationId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
package/types.d.ts CHANGED
@@ -1,4 +1,17 @@
1
1
  import type { EntityId, WildcardRelationId } from "./entity";
2
+ /**
3
+ * Hook types for component lifecycle events
4
+ */
5
+ export interface LifecycleHook<T = unknown> {
6
+ /**
7
+ * Called when a component is added to an entity
8
+ */
9
+ onAdded?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
10
+ /**
11
+ * Called when a component is removed from an entity
12
+ */
13
+ onRemoved?: (entityId: EntityId, componentType: EntityId<T>) => void;
14
+ }
2
15
  /**
3
16
  * Type helper for component tuples extracted from EntityId array
4
17
  */
package/world.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Archetype } from "./archetype";
2
2
  import { type Command } from "./command-buffer";
3
- import type { EntityId } from "./entity";
3
+ import type { EntityId, WildcardRelationId } from "./entity";
4
4
  import { Query } from "./query";
5
5
  import type { QueryFilter } from "./query-filter";
6
- import type { ComponentTuple } from "./types";
6
+ import type { ComponentTuple, LifecycleHook } from "./types";
7
7
  import type { System } from "./system";
8
8
  /**
9
9
  * World class for ECS architecture
@@ -18,6 +18,21 @@ export declare class World<ExtraParams extends any[] = [deltaTime: number]> {
18
18
  private queries;
19
19
  private commandBuffer;
20
20
  private componentToArchetypes;
21
+ /**
22
+ * Hook storage for component lifecycle events
23
+ */
24
+ private componentLifecycleHooks;
25
+ /**
26
+ * Hook storage for wildcard relation lifecycle events
27
+ * Maps base component type to set of wildcard relation hooks
28
+ */
29
+ private wildcardRelationLifecycleHooks;
30
+ /**
31
+ * Reverse index tracking which entities use each entity as a component type
32
+ * Maps entity ID to set of {sourceEntityId, componentType} pairs where componentType uses this entity
33
+ * This includes both relation components and direct usage of entities as component types
34
+ */
35
+ private entityReverseIndex;
21
36
  constructor();
22
37
  /**
23
38
  * Generate a hash key for component types array
@@ -51,6 +66,10 @@ export declare class World<ExtraParams extends any[] = [deltaTime: number]> {
51
66
  * Check if an entity has a specific component
52
67
  */
53
68
  hasComponent<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
69
+ /**
70
+ * Get wildcard relations from an entity
71
+ */
72
+ getComponent<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, any][] | undefined;
54
73
  /**
55
74
  * Get a component from an entity
56
75
  */
@@ -63,6 +82,23 @@ export declare class World<ExtraParams extends any[] = [deltaTime: number]> {
63
82
  * Unregister a system
64
83
  */
65
84
  unregisterSystem(system: System<ExtraParams>): void;
85
+ /**
86
+ * Register a lifecycle hook for component events
87
+ */
88
+ registerComponentLifecycleHook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
89
+ /**
90
+ * Unregister a lifecycle hook for component events
91
+ */
92
+ unregisterComponentLifecycleHook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
93
+ /**
94
+ * Register a lifecycle hook for wildcard relation events
95
+ * The hook will be triggered for any component that matches the wildcard relation pattern
96
+ */
97
+ registerWildcardRelationLifecycleHook<T>(baseComponentType: EntityId<T>, hook: LifecycleHook<T>): void;
98
+ /**
99
+ * Unregister a lifecycle hook for wildcard relation events
100
+ */
101
+ unregisterWildcardRelationLifecycleHook<T>(baseComponentType: EntityId<T>, hook: LifecycleHook<T>): void;
66
102
  /**
67
103
  * Update the world (run all systems)
68
104
  */
@@ -103,4 +139,32 @@ export declare class World<ExtraParams extends any[] = [deltaTime: number]> {
103
139
  * Get or create an archetype for the given component types
104
140
  */
105
141
  private getOrCreateArchetype;
142
+ /**
143
+ * Add a component reference to the reverse index when an entity is used as a component type
144
+ * @param sourceEntityId The entity that has the component
145
+ * @param componentType The component type (which may be an entity ID used as component type)
146
+ * @param targetEntityId The entity being used as component type
147
+ */
148
+ private addComponentReference;
149
+ /**
150
+ * Remove a component reference from the reverse index
151
+ * @param sourceEntityId The entity that has the component
152
+ * @param componentType The component type
153
+ * @param targetEntityId The entity being used as component type
154
+ */
155
+ private removeComponentReference;
156
+ /**
157
+ * Get all component references where a target entity is used as a component type
158
+ * @param targetEntityId The target entity
159
+ * @returns Array of {sourceEntityId, componentType} pairs
160
+ */
161
+ private getComponentReferences;
162
+ /**
163
+ * Remove an empty archetype from all internal data structures
164
+ */
165
+ private removeEmptyArchetype;
166
+ /**
167
+ * Execute component lifecycle hooks for added and removed components
168
+ */
169
+ private executeComponentLifecycleHooks;
106
170
  }