@codehz/ecs 0.2.4 → 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) {
@@ -791,7 +794,7 @@ class World {
791
794
  queryCache = new Map;
792
795
  systemScheduler = new SystemScheduler;
793
796
  commandBuffer = new CommandBuffer((entityId, commands) => this.executeEntityCommands(entityId, commands));
794
- lifecycleHooks = new Map;
797
+ hooks = new Map;
795
798
  exclusiveComponents = new Set;
796
799
  constructor(snapshot) {
797
800
  if (snapshot && typeof snapshot === "object") {
@@ -876,6 +879,7 @@ class World {
876
879
  const sourceArchetype = this.entityToArchetype.get(sourceEntityId);
877
880
  if (sourceArchetype) {
878
881
  const currentComponents = new Map;
882
+ let removedComponent = sourceArchetype.get(sourceEntityId, componentType);
879
883
  for (const archetypeComponentType of sourceArchetype.componentTypes) {
880
884
  if (archetypeComponentType !== componentType) {
881
885
  const componentData = sourceArchetype.get(sourceEntityId, archetypeComponentType);
@@ -890,7 +894,7 @@ class World {
890
894
  newArchetype.addEntity(sourceEntityId, currentComponents);
891
895
  this.entityToArchetype.set(sourceEntityId, newArchetype);
892
896
  this.untrackEntityReference(sourceEntityId, componentType, entityId);
893
- this.triggerLifecycleHooks(sourceEntityId, new Map, new Set([componentType]));
897
+ this.triggerLifecycleHooks(sourceEntityId, new Map, new Map([[componentType, removedComponent]]));
894
898
  }
895
899
  }
896
900
  this.entityReferences.delete(entityId);
@@ -917,7 +921,7 @@ class World {
917
921
  }
918
922
  this.commandBuffer.set(entityId, componentType, component2);
919
923
  }
920
- delete(entityId, componentType) {
924
+ remove(entityId, componentType) {
921
925
  if (!this.exists(entityId)) {
922
926
  throw new Error(`Entity ${entityId} does not exist`);
923
927
  }
@@ -927,7 +931,7 @@ class World {
927
931
  }
928
932
  this.commandBuffer.delete(entityId, componentType);
929
933
  }
930
- destroy(entityId) {
934
+ delete(entityId) {
931
935
  this.commandBuffer.destroy(entityId);
932
936
  }
933
937
  has(entityId, componentType) {
@@ -950,18 +954,29 @@ class World {
950
954
  registerSystem(system, additionalDeps = []) {
951
955
  this.systemScheduler.addSystem(system, additionalDeps);
952
956
  }
953
- registerLifecycleHook(componentType, hook) {
954
- if (!this.lifecycleHooks.has(componentType)) {
955
- 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
+ });
956
972
  }
957
- this.lifecycleHooks.get(componentType).add(hook);
958
973
  }
959
- unregisterLifecycleHook(componentType, hook) {
960
- const hooks = this.lifecycleHooks.get(componentType);
974
+ unhook(componentType, hook) {
975
+ const hooks = this.hooks.get(componentType);
961
976
  if (hooks) {
962
977
  hooks.delete(hook);
963
978
  if (hooks.size === 0) {
964
- this.lifecycleHooks.delete(componentType);
979
+ this.hooks.delete(componentType);
965
980
  }
966
981
  }
967
982
  }
@@ -1130,9 +1145,13 @@ class World {
1130
1145
  }
1131
1146
  }
1132
1147
  const finalComponentTypes = changeset.getFinalComponentTypes(currentArchetype.componentTypes);
1148
+ const removedCompoents = new Map;
1133
1149
  if (finalComponentTypes) {
1134
1150
  const newArchetype = this.ensureArchetype(finalComponentTypes);
1135
1151
  const currentComponents = currentArchetype.removeEntity(entityId);
1152
+ for (const componentType of changeset.removes) {
1153
+ removedCompoents.set(componentType, currentComponents.get(componentType));
1154
+ }
1136
1155
  newArchetype.addEntity(entityId, changeset.applyTo(currentComponents));
1137
1156
  this.entityToArchetype.set(entityId, newArchetype);
1138
1157
  } else {
@@ -1158,7 +1177,7 @@ class World {
1158
1177
  this.trackEntityReference(entityId, componentType, componentType);
1159
1178
  }
1160
1179
  }
1161
- this.triggerLifecycleHooks(entityId, changeset.adds, changeset.removes);
1180
+ this.triggerLifecycleHooks(entityId, changeset.adds, removedCompoents);
1162
1181
  return changeset;
1163
1182
  }
1164
1183
  ensureArchetype(componentTypes) {
@@ -1226,45 +1245,37 @@ class World {
1226
1245
  }
1227
1246
  triggerLifecycleHooks(entityId, addedComponents, removedComponents) {
1228
1247
  for (const [componentType, component2] of addedComponents) {
1229
- const directHooks = this.lifecycleHooks.get(componentType);
1248
+ const directHooks = this.hooks.get(componentType);
1230
1249
  if (directHooks) {
1231
1250
  for (const lifecycleHook of directHooks) {
1232
- if (lifecycleHook.onAdded) {
1233
- lifecycleHook.onAdded(entityId, componentType, component2);
1234
- }
1251
+ lifecycleHook.on_set?.(entityId, componentType, component2);
1235
1252
  }
1236
1253
  }
1237
1254
  const detailedType = getDetailedIdType(componentType);
1238
1255
  if (detailedType.type === "entity-relation" || detailedType.type === "component-relation" || detailedType.type === "wildcard-relation") {
1239
1256
  const wildcardRelationId = relation(detailedType.componentId, "*");
1240
- const wildcardHooks = this.lifecycleHooks.get(wildcardRelationId);
1257
+ const wildcardHooks = this.hooks.get(wildcardRelationId);
1241
1258
  if (wildcardHooks) {
1242
1259
  for (const lifecycleHook of wildcardHooks) {
1243
- if (lifecycleHook.onAdded) {
1244
- lifecycleHook.onAdded(entityId, componentType, component2);
1245
- }
1260
+ lifecycleHook.on_set?.(entityId, componentType, component2);
1246
1261
  }
1247
1262
  }
1248
1263
  }
1249
1264
  }
1250
- for (const componentType of removedComponents) {
1251
- const directHooks = this.lifecycleHooks.get(componentType);
1265
+ for (const [componentType, component2] of removedComponents) {
1266
+ const directHooks = this.hooks.get(componentType);
1252
1267
  if (directHooks) {
1253
1268
  for (const lifecycleHook of directHooks) {
1254
- if (lifecycleHook.onRemoved) {
1255
- lifecycleHook.onRemoved(entityId, componentType);
1256
- }
1269
+ lifecycleHook.on_remove?.(entityId, componentType, component2);
1257
1270
  }
1258
1271
  }
1259
1272
  const detailedType = getDetailedIdType(componentType);
1260
1273
  if (detailedType.type === "entity-relation" || detailedType.type === "component-relation" || detailedType.type === "wildcard-relation") {
1261
1274
  const wildcardRelationId = relation(detailedType.componentId, "*");
1262
- const wildcardHooks = this.lifecycleHooks.get(wildcardRelationId);
1275
+ const wildcardHooks = this.hooks.get(wildcardRelationId);
1263
1276
  if (wildcardHooks) {
1264
1277
  for (const hook of wildcardHooks) {
1265
- if (hook.onRemoved) {
1266
- hook.onRemoved(entityId, componentType);
1267
- }
1278
+ hook.on_remove?.(entityId, componentType, component2);
1268
1279
  }
1269
1280
  }
1270
1281
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.2.4",
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