@codehz/ecs 0.6.7 → 0.6.9
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/builder.d.mts +57 -10
- package/package.json +1 -1
- package/world.mjs +166 -135
- package/world.mjs.map +1 -1
package/world.mjs
CHANGED
|
@@ -98,16 +98,15 @@ function decodeRelationId(relationId) {
|
|
|
98
98
|
function getIdType(id) {
|
|
99
99
|
if (isComponentId(id)) return "component";
|
|
100
100
|
if (isEntityId(id)) return "entity";
|
|
101
|
-
if (id < 0)
|
|
102
|
-
const decoded =
|
|
103
|
-
if (decoded
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return "invalid";
|
|
101
|
+
if (id < 0) {
|
|
102
|
+
const decoded = decodeRelationRaw(id);
|
|
103
|
+
if (decoded === null) return "invalid";
|
|
104
|
+
const { componentId: rawComponentId, targetId: rawTargetId } = decoded;
|
|
105
|
+
if (!isValidComponentId(rawComponentId)) return "invalid";
|
|
106
|
+
if (rawTargetId === WILDCARD_TARGET_ID) return "wildcard-relation";
|
|
107
|
+
else if (isEntityId(rawTargetId)) return "entity-relation";
|
|
108
|
+
else if (isComponentId(rawTargetId)) return "component-relation";
|
|
109
|
+
else return "invalid";
|
|
111
110
|
}
|
|
112
111
|
return "invalid";
|
|
113
112
|
}
|
|
@@ -119,28 +118,29 @@ function getIdType(id) {
|
|
|
119
118
|
function getDetailedIdType(id) {
|
|
120
119
|
if (isComponentId(id)) return { type: "component" };
|
|
121
120
|
if (isEntityId(id)) return { type: "entity" };
|
|
122
|
-
if (id < 0)
|
|
123
|
-
const decoded =
|
|
124
|
-
if (decoded
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
type,
|
|
139
|
-
componentId: decoded.componentId,
|
|
140
|
-
targetId: decoded.targetId
|
|
121
|
+
if (id < 0) {
|
|
122
|
+
const decoded = decodeRelationRaw(id);
|
|
123
|
+
if (decoded === null) return { type: "invalid" };
|
|
124
|
+
const { componentId: rawComponentId, targetId: rawTargetId } = decoded;
|
|
125
|
+
if (!isValidComponentId(rawComponentId)) return { type: "invalid" };
|
|
126
|
+
const componentId = rawComponentId;
|
|
127
|
+
const targetId = rawTargetId;
|
|
128
|
+
if (targetId === WILDCARD_TARGET_ID) return {
|
|
129
|
+
type: "wildcard-relation",
|
|
130
|
+
componentId,
|
|
131
|
+
targetId
|
|
132
|
+
};
|
|
133
|
+
else if (isEntityId(targetId)) return {
|
|
134
|
+
type: "entity-relation",
|
|
135
|
+
componentId,
|
|
136
|
+
targetId
|
|
141
137
|
};
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
else if (isComponentId(targetId)) return {
|
|
139
|
+
type: "component-relation",
|
|
140
|
+
componentId,
|
|
141
|
+
targetId
|
|
142
|
+
};
|
|
143
|
+
else return { type: "invalid" };
|
|
144
144
|
}
|
|
145
145
|
return { type: "invalid" };
|
|
146
146
|
}
|
|
@@ -175,17 +175,18 @@ function isEntityRelation(id) {
|
|
|
175
175
|
*/
|
|
176
176
|
var EntityIdManager = class {
|
|
177
177
|
nextId = ENTITY_ID_START;
|
|
178
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Free list uses a stack (LIFO) for better memory locality when reusing IDs.
|
|
180
|
+
* We use an array instead of a Set for significantly better performance.
|
|
181
|
+
*/
|
|
182
|
+
freelist = [];
|
|
179
183
|
/**
|
|
180
184
|
* Allocate a new entity ID
|
|
181
185
|
* Uses freelist if available, otherwise increments counter
|
|
182
186
|
*/
|
|
183
187
|
allocate() {
|
|
184
|
-
if (this.freelist.
|
|
185
|
-
|
|
186
|
-
this.freelist.delete(id);
|
|
187
|
-
return id;
|
|
188
|
-
} else {
|
|
188
|
+
if (this.freelist.length > 0) return this.freelist.pop();
|
|
189
|
+
else {
|
|
189
190
|
const id = this.nextId;
|
|
190
191
|
this.nextId++;
|
|
191
192
|
if (this.nextId >= Number.MAX_SAFE_INTEGER) throw new Error("Entity ID overflow: reached maximum safe integer");
|
|
@@ -199,13 +200,13 @@ var EntityIdManager = class {
|
|
|
199
200
|
deallocate(id) {
|
|
200
201
|
if (!isEntityId(id)) throw new Error("Can only deallocate valid entity IDs");
|
|
201
202
|
if (id >= this.nextId) throw new Error("Cannot deallocate an ID that was never allocated");
|
|
202
|
-
this.freelist.
|
|
203
|
+
this.freelist.push(id);
|
|
203
204
|
}
|
|
204
205
|
/**
|
|
205
206
|
* Get the current freelist size (for debugging/monitoring)
|
|
206
207
|
*/
|
|
207
208
|
getFreelistSize() {
|
|
208
|
-
return this.freelist.
|
|
209
|
+
return this.freelist.length;
|
|
209
210
|
}
|
|
210
211
|
/**
|
|
211
212
|
* Get the next ID that would be allocated (for debugging)
|
|
@@ -230,7 +231,7 @@ var EntityIdManager = class {
|
|
|
230
231
|
deserializeState(state) {
|
|
231
232
|
if (typeof state.nextId !== "number") throw new Error("Invalid state for EntityIdManager.deserializeState");
|
|
232
233
|
this.nextId = state.nextId;
|
|
233
|
-
this.freelist =
|
|
234
|
+
this.freelist = state.freelist || [];
|
|
234
235
|
}
|
|
235
236
|
};
|
|
236
237
|
/**
|
|
@@ -627,10 +628,15 @@ var ComponentChangeset = class {
|
|
|
627
628
|
//#endregion
|
|
628
629
|
//#region src/commands/command-buffer.ts
|
|
629
630
|
/**
|
|
631
|
+
* Maximum number of command buffer execution iterations to prevent infinite loops
|
|
632
|
+
*/
|
|
633
|
+
const MAX_COMMAND_ITERATIONS = 100;
|
|
634
|
+
/**
|
|
630
635
|
* Command buffer for deferred structural changes
|
|
631
636
|
*/
|
|
632
637
|
var CommandBuffer = class {
|
|
633
638
|
commands = [];
|
|
639
|
+
swapBuffer = [];
|
|
634
640
|
executeEntityCommands;
|
|
635
641
|
/**
|
|
636
642
|
* Create a command buffer with an executor function
|
|
@@ -669,18 +675,19 @@ var CommandBuffer = class {
|
|
|
669
675
|
* Execute all commands and clear the buffer
|
|
670
676
|
*/
|
|
671
677
|
execute() {
|
|
672
|
-
const MAX_ITERATIONS = 100;
|
|
673
678
|
let iterations = 0;
|
|
674
679
|
while (this.commands.length > 0) {
|
|
675
|
-
if (iterations >=
|
|
680
|
+
if (iterations >= MAX_COMMAND_ITERATIONS) throw new Error("Command execution exceeded maximum iterations, possible infinite loop");
|
|
676
681
|
iterations++;
|
|
677
|
-
const currentCommands =
|
|
678
|
-
this.commands =
|
|
682
|
+
const currentCommands = this.commands;
|
|
683
|
+
this.commands = this.swapBuffer;
|
|
679
684
|
const entityCommands = /* @__PURE__ */ new Map();
|
|
680
685
|
for (const cmd of currentCommands) {
|
|
681
686
|
if (!entityCommands.has(cmd.entityId)) entityCommands.set(cmd.entityId, []);
|
|
682
687
|
entityCommands.get(cmd.entityId).push(cmd);
|
|
683
688
|
}
|
|
689
|
+
currentCommands.length = 0;
|
|
690
|
+
this.swapBuffer = currentCommands;
|
|
684
691
|
for (const [entityId, commands] of entityCommands) this.executeEntityCommands(entityId, commands);
|
|
685
692
|
}
|
|
686
693
|
}
|
|
@@ -721,8 +728,8 @@ function matchesComponentTypes(archetype, componentTypes) {
|
|
|
721
728
|
});
|
|
722
729
|
else if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && detailedType.componentId !== void 0 && isDontFragmentComponent(detailedType.componentId)) {
|
|
723
730
|
const wildcardMarker = relation(detailedType.componentId, "*");
|
|
724
|
-
return archetype.
|
|
725
|
-
} else return archetype.
|
|
731
|
+
return archetype.componentTypeSet.has(wildcardMarker);
|
|
732
|
+
} else return archetype.componentTypeSet.has(type);
|
|
726
733
|
});
|
|
727
734
|
}
|
|
728
735
|
/**
|
|
@@ -735,7 +742,7 @@ function matchesFilter(archetype, filter) {
|
|
|
735
742
|
if (!isRelationId(archetypeType)) return false;
|
|
736
743
|
return getComponentIdFromRelationId(archetypeType) === detailedType.componentId;
|
|
737
744
|
});
|
|
738
|
-
else return !archetype.
|
|
745
|
+
else return !archetype.componentTypeSet.has(type);
|
|
739
746
|
});
|
|
740
747
|
}
|
|
741
748
|
|
|
@@ -750,6 +757,8 @@ var Query = class {
|
|
|
750
757
|
filter;
|
|
751
758
|
cachedArchetypes = [];
|
|
752
759
|
isDisposed = false;
|
|
760
|
+
/** Cache key assigned by World for O(1) releaseQuery lookup */
|
|
761
|
+
_cacheKey;
|
|
753
762
|
/** Cached wildcard component types for faster entity filtering */
|
|
754
763
|
wildcardTypes;
|
|
755
764
|
/** Cached specific dontFragment relation types that need entity-level filtering */
|
|
@@ -903,10 +912,10 @@ var Query = class {
|
|
|
903
912
|
* Get a value from cache or compute and cache it if not present
|
|
904
913
|
* @param cache The cache map
|
|
905
914
|
* @param key The cache key
|
|
906
|
-
* @param compute Function to compute the value if not cached
|
|
915
|
+
* @param compute Function to compute the value if not cached (may have side effects)
|
|
907
916
|
* @returns The cached or computed value
|
|
908
917
|
*/
|
|
909
|
-
function
|
|
918
|
+
function getOrCompute(cache, key, compute) {
|
|
910
919
|
let value = cache.get(key);
|
|
911
920
|
if (value === void 0) {
|
|
912
921
|
value = compute();
|
|
@@ -915,20 +924,15 @@ function getOrComputeCache(cache, key, compute) {
|
|
|
915
924
|
return value;
|
|
916
925
|
}
|
|
917
926
|
/**
|
|
918
|
-
*
|
|
919
|
-
* @
|
|
920
|
-
* @param key The cache key
|
|
921
|
-
* @param create Function to create the value if not cached (can have side effects)
|
|
922
|
-
* @returns The cached or created value
|
|
927
|
+
* Alias for getOrCompute - maintained for backwards compatibility
|
|
928
|
+
* @deprecated Use getOrCompute instead
|
|
923
929
|
*/
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
return value;
|
|
931
|
-
}
|
|
930
|
+
const getOrComputeCache = getOrCompute;
|
|
931
|
+
/**
|
|
932
|
+
* Alias for getOrCompute - maintained for backwards compatibility
|
|
933
|
+
* @deprecated Use getOrCompute instead
|
|
934
|
+
*/
|
|
935
|
+
const getOrCreateWithSideEffect = getOrCompute;
|
|
932
936
|
|
|
933
937
|
//#endregion
|
|
934
938
|
//#region src/core/types.ts
|
|
@@ -939,6 +943,18 @@ function isOptionalEntityId(type) {
|
|
|
939
943
|
//#endregion
|
|
940
944
|
//#region src/core/archetype-helpers.ts
|
|
941
945
|
/**
|
|
946
|
+
* Check if a components map has any wildcard relations matching a component ID
|
|
947
|
+
* @param components - Component entity's components map
|
|
948
|
+
* @param wildcardComponentId - The component ID to match
|
|
949
|
+
* @returns True if at least one matching relation exists
|
|
950
|
+
*/
|
|
951
|
+
function hasWildcardRelation(components, wildcardComponentId) {
|
|
952
|
+
for (const relId of components.keys()) if (isRelationId(relId)) {
|
|
953
|
+
if (getComponentIdFromRelationId(relId) === wildcardComponentId) return true;
|
|
954
|
+
}
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
942
958
|
* Check if a detailed type represents a relation (entity or component)
|
|
943
959
|
*/
|
|
944
960
|
function isRelationType(detailedType) {
|
|
@@ -1036,6 +1052,10 @@ var Archetype = class {
|
|
|
1036
1052
|
*/
|
|
1037
1053
|
componentTypes;
|
|
1038
1054
|
/**
|
|
1055
|
+
* Set version of componentTypes for O(1) lookups in hot paths
|
|
1056
|
+
*/
|
|
1057
|
+
componentTypeSet;
|
|
1058
|
+
/**
|
|
1039
1059
|
* List of entities in this archetype
|
|
1040
1060
|
*/
|
|
1041
1061
|
entities = [];
|
|
@@ -1055,9 +1075,6 @@ var Archetype = class {
|
|
|
1055
1075
|
*/
|
|
1056
1076
|
dontFragmentRelations;
|
|
1057
1077
|
/**
|
|
1058
|
-
* Cache for pre-computed component data sources to avoid repeated calculations
|
|
1059
|
-
*/
|
|
1060
|
-
/**
|
|
1061
1078
|
* Multi-hooks that match this archetype
|
|
1062
1079
|
*/
|
|
1063
1080
|
matchingMultiHooks = /* @__PURE__ */ new Set();
|
|
@@ -1067,12 +1084,19 @@ var Archetype = class {
|
|
|
1067
1084
|
componentDataSourcesCache = /* @__PURE__ */ new Map();
|
|
1068
1085
|
constructor(componentTypes, dontFragmentRelations) {
|
|
1069
1086
|
this.componentTypes = [...componentTypes].sort((a, b) => a - b);
|
|
1087
|
+
this.componentTypeSet = new Set(this.componentTypes);
|
|
1070
1088
|
this.dontFragmentRelations = dontFragmentRelations;
|
|
1071
1089
|
for (const componentType of this.componentTypes) this.componentData.set(componentType, []);
|
|
1072
1090
|
}
|
|
1073
1091
|
get size() {
|
|
1074
1092
|
return this.entities.length;
|
|
1075
1093
|
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Check if the given component types match this archetype
|
|
1096
|
+
* @param componentTypes - Component types to check (can be in any order)
|
|
1097
|
+
* @returns true if the types match this archetype's component set
|
|
1098
|
+
* @note This method handles unsorted input by internally sorting for comparison
|
|
1099
|
+
*/
|
|
1076
1100
|
matches(componentTypes) {
|
|
1077
1101
|
if (this.componentTypes.length !== componentTypes.length) return false;
|
|
1078
1102
|
const sortedTypes = [...componentTypes].sort((a, b) => a - b);
|
|
@@ -1092,7 +1116,7 @@ var Archetype = class {
|
|
|
1092
1116
|
addDontFragmentRelations(entityId, componentData) {
|
|
1093
1117
|
const dontFragmentData = /* @__PURE__ */ new Map();
|
|
1094
1118
|
for (const [componentType, data] of componentData) {
|
|
1095
|
-
if (this.
|
|
1119
|
+
if (this.componentTypeSet.has(componentType)) continue;
|
|
1096
1120
|
const detailedType = getDetailedIdType(componentType);
|
|
1097
1121
|
if (isRelationType(detailedType) && isDontFragmentComponent(detailedType.componentId)) dontFragmentData.set(componentType, data);
|
|
1098
1122
|
}
|
|
@@ -1176,7 +1200,7 @@ var Archetype = class {
|
|
|
1176
1200
|
return relations;
|
|
1177
1201
|
}
|
|
1178
1202
|
getRegularComponent(entityId, index, componentType) {
|
|
1179
|
-
if (this.
|
|
1203
|
+
if (this.componentTypeSet.has(componentType)) {
|
|
1180
1204
|
const data = this.getComponentData(componentType)[index];
|
|
1181
1205
|
if (data === MISSING_COMPONENT) throw new Error(`Component type ${componentType} not found for entity ${entityId}`);
|
|
1182
1206
|
return data;
|
|
@@ -1188,7 +1212,7 @@ var Archetype = class {
|
|
|
1188
1212
|
getOptional(entityId, componentType) {
|
|
1189
1213
|
const index = this.entityToIndex.get(entityId);
|
|
1190
1214
|
if (index === void 0) throw new Error(`Entity ${entityId} is not in this archetype`);
|
|
1191
|
-
if (this.
|
|
1215
|
+
if (this.componentTypeSet.has(componentType)) {
|
|
1192
1216
|
const data = this.getComponentData(componentType)[index];
|
|
1193
1217
|
if (data === MISSING_COMPONENT) return void 0;
|
|
1194
1218
|
return { value: data };
|
|
@@ -1376,8 +1400,8 @@ function decodeSerializedId(sid) {
|
|
|
1376
1400
|
//#endregion
|
|
1377
1401
|
//#region src/core/world-commands.ts
|
|
1378
1402
|
function processCommands(entityId, currentArchetype, commands, changeset, handleExclusiveRelation) {
|
|
1379
|
-
for (const command of commands) if (command.type === "set"
|
|
1380
|
-
else if (command.type === "delete"
|
|
1403
|
+
for (const command of commands) if (command.type === "set") processSetCommand(entityId, currentArchetype, command.componentType, command.component, changeset, handleExclusiveRelation);
|
|
1404
|
+
else if (command.type === "delete") processDeleteCommand(entityId, currentArchetype, command.componentType, changeset);
|
|
1381
1405
|
}
|
|
1382
1406
|
function processSetCommand(entityId, currentArchetype, componentType, component$1, changeset, handleExclusiveRelation) {
|
|
1383
1407
|
const componentId = getComponentIdFromRelationId(componentType);
|
|
@@ -1385,7 +1409,7 @@ function processSetCommand(entityId, currentArchetype, componentType, component$
|
|
|
1385
1409
|
handleExclusiveRelation(entityId, currentArchetype, componentId);
|
|
1386
1410
|
if (isDontFragmentComponent(componentId)) {
|
|
1387
1411
|
const wildcardMarker = relation(componentId, "*");
|
|
1388
|
-
if (!currentArchetype.
|
|
1412
|
+
if (!currentArchetype.componentTypeSet.has(wildcardMarker)) changeset.set(wildcardMarker, void 0);
|
|
1389
1413
|
}
|
|
1390
1414
|
}
|
|
1391
1415
|
changeset.set(componentType, component$1);
|
|
@@ -1405,7 +1429,7 @@ function removeMatchingRelations(entityId, archetype, baseComponentId, changeset
|
|
|
1405
1429
|
}
|
|
1406
1430
|
const entityData = archetype.getEntity(entityId);
|
|
1407
1431
|
if (entityData) for (const [componentType] of entityData) {
|
|
1408
|
-
if (archetype.
|
|
1432
|
+
if (archetype.componentTypeSet.has(componentType)) continue;
|
|
1409
1433
|
if (getComponentIdFromRelationId(componentType) === baseComponentId) changeset.delete(componentType);
|
|
1410
1434
|
}
|
|
1411
1435
|
}
|
|
@@ -1579,11 +1603,11 @@ function triggerMultiComponentHooks(ctx, entityId, addedComponents, removedCompo
|
|
|
1579
1603
|
}
|
|
1580
1604
|
function entityHasAllComponents(ctx, entityId, requiredComponents) {
|
|
1581
1605
|
return requiredComponents.every((c) => {
|
|
1582
|
-
if (isWildcardRelationId(c))
|
|
1583
|
-
const
|
|
1606
|
+
if (isWildcardRelationId(c)) {
|
|
1607
|
+
const wildcardResult = ctx.getOptional(entityId, c);
|
|
1608
|
+
if (!wildcardResult) return false;
|
|
1609
|
+
const wildcardData = wildcardResult.value;
|
|
1584
1610
|
return Array.isArray(wildcardData) && wildcardData.length > 0;
|
|
1585
|
-
} catch {
|
|
1586
|
-
return false;
|
|
1587
1611
|
}
|
|
1588
1612
|
return ctx.has(entityId, c);
|
|
1589
1613
|
});
|
|
@@ -1591,11 +1615,11 @@ function entityHasAllComponents(ctx, entityId, requiredComponents) {
|
|
|
1591
1615
|
function entityHadAllComponentsBefore(ctx, entityId, requiredComponents, removedComponents) {
|
|
1592
1616
|
return requiredComponents.every((c) => {
|
|
1593
1617
|
if (anyComponentMatches(removedComponents, c)) return true;
|
|
1594
|
-
if (isWildcardRelationId(c))
|
|
1595
|
-
const
|
|
1618
|
+
if (isWildcardRelationId(c)) {
|
|
1619
|
+
const wildcardResult = ctx.getOptional(entityId, c);
|
|
1620
|
+
if (!wildcardResult) return false;
|
|
1621
|
+
const wildcardData = wildcardResult.value;
|
|
1596
1622
|
return Array.isArray(wildcardData) && wildcardData.length > 0;
|
|
1597
|
-
} catch {
|
|
1598
|
-
return false;
|
|
1599
1623
|
}
|
|
1600
1624
|
return ctx.has(entityId, c);
|
|
1601
1625
|
});
|
|
@@ -1879,8 +1903,9 @@ var World = class {
|
|
|
1879
1903
|
destroyEntityImmediate(entityId) {
|
|
1880
1904
|
const queue = [entityId];
|
|
1881
1905
|
const visited = /* @__PURE__ */ new Set();
|
|
1882
|
-
|
|
1883
|
-
|
|
1906
|
+
let queueIndex = 0;
|
|
1907
|
+
while (queueIndex < queue.length) {
|
|
1908
|
+
const cur = queue[queueIndex++];
|
|
1884
1909
|
if (visited.has(cur)) continue;
|
|
1885
1910
|
visited.add(cur);
|
|
1886
1911
|
const archetype = this.entityToArchetype.get(cur);
|
|
@@ -1915,33 +1940,40 @@ var World = class {
|
|
|
1915
1940
|
if (this.isComponentEntityId(entityId)) return true;
|
|
1916
1941
|
return this.entityToArchetype.has(entityId);
|
|
1917
1942
|
}
|
|
1918
|
-
set(entityId,
|
|
1919
|
-
if (
|
|
1943
|
+
set(entityId, componentTypeOrComponent, maybeComponent) {
|
|
1944
|
+
if (maybeComponent === void 0 && componentTypeOrComponent !== void 0) {
|
|
1945
|
+
const detailedType$1 = getDetailedIdType(entityId);
|
|
1946
|
+
if (detailedType$1.type === "component" || detailedType$1.type === "component-relation") {
|
|
1947
|
+
const componentId = entityId;
|
|
1948
|
+
const component$2 = componentTypeOrComponent;
|
|
1949
|
+
if (!this.exists(componentId)) throw new Error(`Component entity ${componentId} does not exist`);
|
|
1950
|
+
const detailedComponentType = getDetailedIdType(componentId);
|
|
1951
|
+
if (detailedComponentType.type === "invalid") throw new Error(`Invalid component type: ${componentId}`);
|
|
1952
|
+
if (detailedComponentType.type === "wildcard-relation") throw new Error(`Cannot directly add wildcard relation components: ${componentId}`);
|
|
1953
|
+
this.commandBuffer.set(componentId, componentId, component$2);
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
const entityIdArg = entityId;
|
|
1958
|
+
const componentType = componentTypeOrComponent;
|
|
1959
|
+
const component$1 = maybeComponent;
|
|
1960
|
+
if (!this.exists(entityIdArg)) throw new Error(`Entity ${entityIdArg} does not exist`);
|
|
1920
1961
|
const detailedType = getDetailedIdType(componentType);
|
|
1921
1962
|
if (detailedType.type === "invalid") throw new Error(`Invalid component type: ${componentType}`);
|
|
1922
1963
|
if (detailedType.type === "wildcard-relation") throw new Error(`Cannot directly add wildcard relation components: ${componentType}`);
|
|
1923
|
-
this.commandBuffer.set(
|
|
1964
|
+
this.commandBuffer.set(entityIdArg, componentType, component$1);
|
|
1924
1965
|
}
|
|
1925
|
-
/**
|
|
1926
|
-
* Removes a component from an entity.
|
|
1927
|
-
* The change is buffered and takes effect after calling `world.sync()`.
|
|
1928
|
-
* If the entity does not exist, throws an error.
|
|
1929
|
-
*
|
|
1930
|
-
* @template T - The component data type
|
|
1931
|
-
* @param entityId - The entity identifier
|
|
1932
|
-
* @param componentType - The component type to remove
|
|
1933
|
-
*
|
|
1934
|
-
* @throws {Error} If the entity does not exist
|
|
1935
|
-
* @throws {Error} If the component type is invalid
|
|
1936
|
-
*
|
|
1937
|
-
* @example
|
|
1938
|
-
* world.remove(entity, Position);
|
|
1939
|
-
* world.sync(); // Apply changes
|
|
1940
|
-
*/
|
|
1941
1966
|
remove(entityId, componentType) {
|
|
1942
|
-
if (
|
|
1967
|
+
if (componentType === void 0) {
|
|
1968
|
+
const componentId = entityId;
|
|
1969
|
+
if (!this.exists(componentId)) throw new Error(`Component entity ${componentId} does not exist`);
|
|
1970
|
+
this.commandBuffer.remove(componentId, componentId);
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
const entityIdArg = entityId;
|
|
1974
|
+
if (!this.exists(entityIdArg)) throw new Error(`Entity ${entityIdArg} does not exist`);
|
|
1943
1975
|
if (getDetailedIdType(componentType).type === "invalid") throw new Error(`Invalid component type: ${componentType}`);
|
|
1944
|
-
this.commandBuffer.remove(
|
|
1976
|
+
this.commandBuffer.remove(entityIdArg, componentType);
|
|
1945
1977
|
}
|
|
1946
1978
|
/**
|
|
1947
1979
|
* Deletes an entity and all its components from the world.
|
|
@@ -1957,35 +1989,24 @@ var World = class {
|
|
|
1957
1989
|
delete(entityId) {
|
|
1958
1990
|
this.commandBuffer.delete(entityId);
|
|
1959
1991
|
}
|
|
1960
|
-
/**
|
|
1961
|
-
* Checks if an entity has a specific component.
|
|
1962
|
-
* Immediately reflects the current state without waiting for `sync()`.
|
|
1963
|
-
*
|
|
1964
|
-
* @template T - The component data type
|
|
1965
|
-
* @param entityId - The entity identifier
|
|
1966
|
-
* @param componentType - The component type to check
|
|
1967
|
-
* @returns `true` if the entity has the component, `false` otherwise
|
|
1968
|
-
*
|
|
1969
|
-
* @example
|
|
1970
|
-
* if (world.has(entity, Position)) {
|
|
1971
|
-
* const pos = world.get(entity, Position);
|
|
1972
|
-
* }
|
|
1973
|
-
*/
|
|
1974
1992
|
has(entityId, componentType) {
|
|
1993
|
+
if (componentType === void 0) {
|
|
1994
|
+
const componentId = entityId;
|
|
1995
|
+
return this.componentEntityComponents.get(componentId)?.has(componentId) ?? false;
|
|
1996
|
+
}
|
|
1975
1997
|
if (this.isComponentEntityId(entityId)) {
|
|
1976
1998
|
if (isWildcardRelationId(componentType)) {
|
|
1977
1999
|
const componentId = getComponentIdFromRelationId(componentType);
|
|
1978
2000
|
if (componentId === void 0) return false;
|
|
1979
2001
|
const data = this.componentEntityComponents.get(entityId);
|
|
1980
2002
|
if (!data) return false;
|
|
1981
|
-
|
|
1982
|
-
return false;
|
|
2003
|
+
return hasWildcardRelation(data, componentId);
|
|
1983
2004
|
}
|
|
1984
2005
|
return this.componentEntityComponents.get(entityId)?.has(componentType) ?? false;
|
|
1985
2006
|
}
|
|
1986
2007
|
const archetype = this.entityToArchetype.get(entityId);
|
|
1987
2008
|
if (!archetype) return false;
|
|
1988
|
-
if (archetype.
|
|
2009
|
+
if (archetype.componentTypeSet.has(componentType)) return true;
|
|
1989
2010
|
if (isDontFragmentRelation(componentType)) return this.dontFragmentRelations.get(entityId)?.has(componentType) ?? false;
|
|
1990
2011
|
return false;
|
|
1991
2012
|
}
|
|
@@ -2009,8 +2030,8 @@ var World = class {
|
|
|
2009
2030
|
}
|
|
2010
2031
|
const archetype = this.entityToArchetype.get(entityId);
|
|
2011
2032
|
if (!archetype) throw new Error(`Entity ${entityId} does not exist`);
|
|
2012
|
-
if (componentType >= 0 || componentType %
|
|
2013
|
-
const inArchetype = archetype.
|
|
2033
|
+
if (componentType >= 0 || componentType % RELATION_SHIFT !== 0) {
|
|
2034
|
+
const inArchetype = archetype.componentTypeSet.has(componentType);
|
|
2014
2035
|
const hasDontFragment = isDontFragmentRelation(componentType);
|
|
2015
2036
|
if (!(inArchetype || hasDontFragment && this.dontFragmentRelations.get(entityId)?.has(componentType))) throw new Error(`Entity ${entityId} does not have component ${componentType}. Use has() to check component existence before calling get().`);
|
|
2016
2037
|
}
|
|
@@ -2178,6 +2199,7 @@ var World = class {
|
|
|
2178
2199
|
return cached.query;
|
|
2179
2200
|
}
|
|
2180
2201
|
const query = new Query(this, sortedTypes, filter);
|
|
2202
|
+
query._cacheKey = key;
|
|
2181
2203
|
this.queryCache.set(key, {
|
|
2182
2204
|
query,
|
|
2183
2205
|
refCount: 1
|
|
@@ -2243,14 +2265,15 @@ var World = class {
|
|
|
2243
2265
|
* world.releaseQuery(query); // Optional cleanup
|
|
2244
2266
|
*/
|
|
2245
2267
|
releaseQuery(query) {
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2268
|
+
const key = query._cacheKey;
|
|
2269
|
+
if (!key) return;
|
|
2270
|
+
const cached = this.queryCache.get(key);
|
|
2271
|
+
if (!cached || cached.query !== query) return;
|
|
2272
|
+
cached.refCount--;
|
|
2273
|
+
if (cached.refCount <= 0) {
|
|
2274
|
+
this.queryCache.delete(key);
|
|
2275
|
+
this._unregisterQuery(query);
|
|
2276
|
+
cached.query._disposeInternal();
|
|
2254
2277
|
}
|
|
2255
2278
|
}
|
|
2256
2279
|
/**
|
|
@@ -2282,8 +2305,16 @@ var World = class {
|
|
|
2282
2305
|
getArchetypesWithComponents(componentTypes) {
|
|
2283
2306
|
if (componentTypes.length === 0) return [...this.archetypes];
|
|
2284
2307
|
if (componentTypes.length === 1) return this.archetypesByComponent.get(componentTypes[0]) || [];
|
|
2285
|
-
const archetypeLists = componentTypes.map((type) => this.archetypesByComponent.get(type) || []);
|
|
2286
|
-
|
|
2308
|
+
const archetypeLists = componentTypes.map((type) => this.archetypesByComponent.get(type) || []).sort((a, b) => a.length - b.length);
|
|
2309
|
+
const shortest = archetypeLists[0];
|
|
2310
|
+
if (shortest.length === 0) return [];
|
|
2311
|
+
let result = new Set(shortest);
|
|
2312
|
+
for (let i = 1; i < archetypeLists.length; i++) {
|
|
2313
|
+
const listSet = new Set(archetypeLists[i]);
|
|
2314
|
+
for (const item of result) if (!listSet.has(item)) result.delete(item);
|
|
2315
|
+
if (result.size === 0) return [];
|
|
2316
|
+
}
|
|
2317
|
+
return Array.from(result);
|
|
2287
2318
|
}
|
|
2288
2319
|
query(componentTypes, includeComponents) {
|
|
2289
2320
|
const matchingArchetypes = this.getMatchingArchetypes(componentTypes);
|
|
@@ -2365,11 +2396,11 @@ var World = class {
|
|
|
2365
2396
|
for (const componentType of changeset.removes) if (isEntityRelation(componentType)) {
|
|
2366
2397
|
const targetId = getTargetIdFromRelationId(componentType);
|
|
2367
2398
|
untrackEntityReference(this.entityReferences, entityId, componentType, targetId);
|
|
2368
|
-
} else if (componentType >=
|
|
2399
|
+
} else if (componentType >= ENTITY_ID_START) untrackEntityReference(this.entityReferences, entityId, componentType, componentType);
|
|
2369
2400
|
for (const [componentType] of changeset.adds) if (isEntityRelation(componentType)) {
|
|
2370
2401
|
const targetId = getTargetIdFromRelationId(componentType);
|
|
2371
2402
|
trackEntityReference(this.entityReferences, entityId, componentType, targetId);
|
|
2372
|
-
} else if (componentType >=
|
|
2403
|
+
} else if (componentType >= ENTITY_ID_START) trackEntityReference(this.entityReferences, entityId, componentType, componentType);
|
|
2373
2404
|
}
|
|
2374
2405
|
ensureArchetype(componentTypes) {
|
|
2375
2406
|
const sortedTypes = filterRegularComponentTypes(componentTypes).sort((a, b) => a - b);
|
|
@@ -2398,7 +2429,7 @@ var World = class {
|
|
|
2398
2429
|
const componentId = getComponentIdFromRelationId(c);
|
|
2399
2430
|
return componentId !== void 0 && archetype.hasRelationWithComponentId(componentId);
|
|
2400
2431
|
}
|
|
2401
|
-
return archetype.
|
|
2432
|
+
return archetype.componentTypeSet.has(c) || isDontFragmentRelation(c);
|
|
2402
2433
|
});
|
|
2403
2434
|
}
|
|
2404
2435
|
archetypeReferencesEntity(archetype, entityId) {
|