@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 +4 -0
- package/index.js +41 -29
- package/package.json +1 -1
- package/types.d.ts +7 -3
- package/world.d.ts +5 -5
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
953
|
-
if (!this.
|
|
954
|
-
this.
|
|
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
|
-
|
|
959
|
-
const hooks = this.
|
|
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.
|
|
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,
|
|
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.
|
|
1248
|
+
const directHooks = this.hooks.get(componentType);
|
|
1229
1249
|
if (directHooks) {
|
|
1230
1250
|
for (const lifecycleHook of directHooks) {
|
|
1231
|
-
|
|
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.
|
|
1257
|
+
const wildcardHooks = this.hooks.get(wildcardRelationId);
|
|
1240
1258
|
if (wildcardHooks) {
|
|
1241
1259
|
for (const lifecycleHook of wildcardHooks) {
|
|
1242
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
1275
|
+
const wildcardHooks = this.hooks.get(wildcardRelationId);
|
|
1262
1276
|
if (wildcardHooks) {
|
|
1263
1277
|
for (const hook of wildcardHooks) {
|
|
1264
|
-
|
|
1265
|
-
hook.onRemoved(entityId, componentType);
|
|
1266
|
-
}
|
|
1278
|
+
hook.on_remove?.(entityId, componentType, component2);
|
|
1267
1279
|
}
|
|
1268
1280
|
}
|
|
1269
1281
|
}
|
package/package.json
CHANGED
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
|
-
|
|
9
|
+
on_init?: (entityId: EntityId, componentType: EntityId<T>, component: T) => void;
|
|
10
10
|
/**
|
|
11
|
-
* Called when a component is
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|