@codehz/ecs 0.2.3 → 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.
package/archetype.d.ts CHANGED
@@ -102,6 +102,10 @@ export declare class Archetype {
102
102
  * Get all entities in this archetype
103
103
  */
104
104
  getEntities(): EntityId[];
105
+ /**
106
+ * Get the mapping of entities to their indices in this archetype
107
+ */
108
+ getEntityToIndexMap(): Map<EntityId, number>;
105
109
  /**
106
110
  * Get component data for all entities of a specific component type
107
111
  * @param componentType The component type
package/index.js CHANGED
@@ -389,6 +389,9 @@ class Archetype {
389
389
  getEntities() {
390
390
  return this.entities;
391
391
  }
392
+ getEntityToIndexMap() {
393
+ return this.entityToIndex;
394
+ }
392
395
  getComponentData(componentType) {
393
396
  const data = this.componentData.get(componentType);
394
397
  if (!data) {
@@ -528,6 +531,7 @@ class ComponentChangeset {
528
531
  let changed = false;
529
532
  for (const componentType of this.removes) {
530
533
  if (!finalComponentTypes.has(componentType)) {
534
+ this.removes.delete(componentType);
531
535
  continue;
532
536
  }
533
537
  changed = true;
@@ -790,7 +794,7 @@ class World {
790
794
  queryCache = new Map;
791
795
  systemScheduler = new SystemScheduler;
792
796
  commandBuffer = new CommandBuffer((entityId, commands) => this.executeEntityCommands(entityId, commands));
793
- lifecycleHooks = new Map;
797
+ hooks = new Map;
794
798
  exclusiveComponents = new Set;
795
799
  constructor(snapshot) {
796
800
  if (snapshot && typeof snapshot === "object") {
@@ -875,6 +879,7 @@ class World {
875
879
  const sourceArchetype = this.entityToArchetype.get(sourceEntityId);
876
880
  if (sourceArchetype) {
877
881
  const currentComponents = new Map;
882
+ let removedComponent = sourceArchetype.get(sourceEntityId, componentType);
878
883
  for (const archetypeComponentType of sourceArchetype.componentTypes) {
879
884
  if (archetypeComponentType !== componentType) {
880
885
  const componentData = sourceArchetype.get(sourceEntityId, archetypeComponentType);
@@ -889,7 +894,7 @@ class World {
889
894
  newArchetype.addEntity(sourceEntityId, currentComponents);
890
895
  this.entityToArchetype.set(sourceEntityId, newArchetype);
891
896
  this.untrackEntityReference(sourceEntityId, componentType, entityId);
892
- this.triggerLifecycleHooks(sourceEntityId, new Map, new Set([componentType]));
897
+ this.triggerLifecycleHooks(sourceEntityId, new Map, new Map([[componentType, removedComponent]]));
893
898
  }
894
899
  }
895
900
  this.entityReferences.delete(entityId);
@@ -916,7 +921,7 @@ class World {
916
921
  }
917
922
  this.commandBuffer.set(entityId, componentType, component2);
918
923
  }
919
- delete(entityId, componentType) {
924
+ remove(entityId, componentType) {
920
925
  if (!this.exists(entityId)) {
921
926
  throw new Error(`Entity ${entityId} does not exist`);
922
927
  }
@@ -926,7 +931,7 @@ class World {
926
931
  }
927
932
  this.commandBuffer.delete(entityId, componentType);
928
933
  }
929
- destroy(entityId) {
934
+ delete(entityId) {
930
935
  this.commandBuffer.destroy(entityId);
931
936
  }
932
937
  has(entityId, componentType) {
@@ -949,18 +954,29 @@ class World {
949
954
  registerSystem(system, additionalDeps = []) {
950
955
  this.systemScheduler.addSystem(system, additionalDeps);
951
956
  }
952
- registerLifecycleHook(componentType, hook) {
953
- if (!this.lifecycleHooks.has(componentType)) {
954
- this.lifecycleHooks.set(componentType, new Set);
957
+ hook(componentType, hook) {
958
+ if (!this.hooks.has(componentType)) {
959
+ this.hooks.set(componentType, new Set);
960
+ }
961
+ this.hooks.get(componentType).add(hook);
962
+ if (hook.on_init !== undefined) {
963
+ this.archetypesByComponent.get(componentType)?.forEach((archetype) => {
964
+ const entities = archetype.getEntityToIndexMap();
965
+ const componentData = archetype.getComponentData(componentType);
966
+ for (const [entity, index] of entities) {
967
+ const data = componentData[index];
968
+ const value = data === MISSING_COMPONENT ? undefined : data;
969
+ hook.on_init?.(entity, componentType, value);
970
+ }
971
+ });
955
972
  }
956
- this.lifecycleHooks.get(componentType).add(hook);
957
973
  }
958
- unregisterLifecycleHook(componentType, hook) {
959
- const hooks = this.lifecycleHooks.get(componentType);
974
+ unhook(componentType, hook) {
975
+ const hooks = this.hooks.get(componentType);
960
976
  if (hooks) {
961
977
  hooks.delete(hook);
962
978
  if (hooks.size === 0) {
963
- this.lifecycleHooks.delete(componentType);
979
+ this.hooks.delete(componentType);
964
980
  }
965
981
  }
966
982
  }
@@ -1129,9 +1145,13 @@ class World {
1129
1145
  }
1130
1146
  }
1131
1147
  const finalComponentTypes = changeset.getFinalComponentTypes(currentArchetype.componentTypes);
1148
+ const removedCompoents = new Map;
1132
1149
  if (finalComponentTypes) {
1133
1150
  const newArchetype = this.ensureArchetype(finalComponentTypes);
1134
1151
  const currentComponents = currentArchetype.removeEntity(entityId);
1152
+ for (const componentType of changeset.removes) {
1153
+ removedCompoents.set(componentType, currentComponents.get(componentType));
1154
+ }
1135
1155
  newArchetype.addEntity(entityId, changeset.applyTo(currentComponents));
1136
1156
  this.entityToArchetype.set(entityId, newArchetype);
1137
1157
  } else {
@@ -1157,7 +1177,7 @@ class World {
1157
1177
  this.trackEntityReference(entityId, componentType, componentType);
1158
1178
  }
1159
1179
  }
1160
- this.triggerLifecycleHooks(entityId, changeset.adds, changeset.removes);
1180
+ this.triggerLifecycleHooks(entityId, changeset.adds, removedCompoents);
1161
1181
  return changeset;
1162
1182
  }
1163
1183
  ensureArchetype(componentTypes) {
@@ -1225,45 +1245,37 @@ class World {
1225
1245
  }
1226
1246
  triggerLifecycleHooks(entityId, addedComponents, removedComponents) {
1227
1247
  for (const [componentType, component2] of addedComponents) {
1228
- const directHooks = this.lifecycleHooks.get(componentType);
1248
+ const directHooks = this.hooks.get(componentType);
1229
1249
  if (directHooks) {
1230
1250
  for (const lifecycleHook of directHooks) {
1231
- if (lifecycleHook.onAdded) {
1232
- lifecycleHook.onAdded(entityId, componentType, component2);
1233
- }
1251
+ lifecycleHook.on_set?.(entityId, componentType, component2);
1234
1252
  }
1235
1253
  }
1236
1254
  const detailedType = getDetailedIdType(componentType);
1237
1255
  if (detailedType.type === "entity-relation" || detailedType.type === "component-relation" || detailedType.type === "wildcard-relation") {
1238
1256
  const wildcardRelationId = relation(detailedType.componentId, "*");
1239
- const wildcardHooks = this.lifecycleHooks.get(wildcardRelationId);
1257
+ const wildcardHooks = this.hooks.get(wildcardRelationId);
1240
1258
  if (wildcardHooks) {
1241
1259
  for (const lifecycleHook of wildcardHooks) {
1242
- if (lifecycleHook.onAdded) {
1243
- lifecycleHook.onAdded(entityId, componentType, component2);
1244
- }
1260
+ lifecycleHook.on_set?.(entityId, componentType, component2);
1245
1261
  }
1246
1262
  }
1247
1263
  }
1248
1264
  }
1249
- for (const componentType of removedComponents) {
1250
- const directHooks = this.lifecycleHooks.get(componentType);
1265
+ for (const [componentType, component2] of removedComponents) {
1266
+ const directHooks = this.hooks.get(componentType);
1251
1267
  if (directHooks) {
1252
1268
  for (const lifecycleHook of directHooks) {
1253
- if (lifecycleHook.onRemoved) {
1254
- lifecycleHook.onRemoved(entityId, componentType);
1255
- }
1269
+ lifecycleHook.on_remove?.(entityId, componentType, component2);
1256
1270
  }
1257
1271
  }
1258
1272
  const detailedType = getDetailedIdType(componentType);
1259
1273
  if (detailedType.type === "entity-relation" || detailedType.type === "component-relation" || detailedType.type === "wildcard-relation") {
1260
1274
  const wildcardRelationId = relation(detailedType.componentId, "*");
1261
- const wildcardHooks = this.lifecycleHooks.get(wildcardRelationId);
1275
+ const wildcardHooks = this.hooks.get(wildcardRelationId);
1262
1276
  if (wildcardHooks) {
1263
1277
  for (const hook of wildcardHooks) {
1264
- if (hook.onRemoved) {
1265
- hook.onRemoved(entityId, componentType);
1266
- }
1278
+ hook.on_remove?.(entityId, componentType, component2);
1267
1279
  }
1268
1280
  }
1269
1281
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
package/types.d.ts CHANGED
@@ -6,11 +6,15 @@ export interface LifecycleHook<T = unknown> {
6
6
  /**
7
7
  * Called when a component is added to an entity
8
8
  */
9
- onAdded?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
9
+ on_init?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
10
10
  /**
11
- * Called when a component is removed from an entity
11
+ * Called when a component is added to an entity
12
+ */
13
+ on_set?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
14
+ /**
15
+ * Called when a component is deleted from an entity
12
16
  */
13
- onRemoved?: (entityId: EntityId, componentType: EntityId<T>) => void;
17
+ on_remove?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
14
18
  }
15
19
  export type ComponentType<T> = EntityId<T> | OptionalEntityId<T>;
16
20
  export type OptionalEntityId<T> = {
package/world.d.ts CHANGED
@@ -32,7 +32,7 @@ export declare class World<UpdateParams extends any[] = []> {
32
32
  /** Buffers structural changes for deferred execution */
33
33
  private commandBuffer;
34
34
  /** Stores lifecycle hooks for component and relation events */
35
- private lifecycleHooks;
35
+ private hooks;
36
36
  /** Set of component IDs marked as exclusive relations */
37
37
  private exclusiveComponents;
38
38
  /**
@@ -67,11 +67,11 @@ export declare class World<UpdateParams extends any[] = []> {
67
67
  /**
68
68
  * Remove a component from an entity (deferred)
69
69
  */
70
- delete<T>(entityId: EntityId, componentType: EntityId<T>): void;
70
+ remove<T>(entityId: EntityId, componentType: EntityId<T>): void;
71
71
  /**
72
72
  * Destroy an entity and remove all its components (deferred)
73
73
  */
74
- destroy(entityId: EntityId): void;
74
+ delete(entityId: EntityId): void;
75
75
  /**
76
76
  * Check if an entity has a specific component
77
77
  */
@@ -98,11 +98,11 @@ export declare class World<UpdateParams extends any[] = []> {
98
98
  /**
99
99
  * Register a lifecycle hook for component or wildcard relation events
100
100
  */
101
- registerLifecycleHook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
101
+ hook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
102
102
  /**
103
103
  * Unregister a lifecycle hook for component or wildcard relation events
104
104
  */
105
- unregisterLifecycleHook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
105
+ unhook<T>(componentType: EntityId<T>, hook: LifecycleHook<T>): void;
106
106
  /**
107
107
  * Mark a component as exclusive relation
108
108
  * For exclusive relations, an entity can have at most one relation per base component