@codehz/ecs 0.4.0 → 0.4.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 +36 -17
- package/index.d.mts +2 -2
- package/index.mjs +2 -2
- package/package.json +5 -2
- package/testing.d.mts +7 -54
- package/testing.mjs +8 -96
- package/testing.mjs.map +1 -1
- package/world.d.mts +50 -12
- package/world.mjs +160 -57
- package/world.mjs.map +1 -1
package/world.mjs
CHANGED
|
@@ -1340,6 +1340,81 @@ var Query = class {
|
|
|
1340
1340
|
//#endregion
|
|
1341
1341
|
//#region src/world.ts
|
|
1342
1342
|
/**
|
|
1343
|
+
* Encode an internal EntityId into a SerializedEntityId for snapshots
|
|
1344
|
+
*/
|
|
1345
|
+
function encodeEntityId(id) {
|
|
1346
|
+
const detailed = getDetailedIdType(id);
|
|
1347
|
+
switch (detailed.type) {
|
|
1348
|
+
case "component": {
|
|
1349
|
+
const name = getComponentNameById(id);
|
|
1350
|
+
if (!name) console.warn(`Component ID ${id} has no registered name, serializing as number`);
|
|
1351
|
+
return name || id;
|
|
1352
|
+
}
|
|
1353
|
+
case "entity-relation": {
|
|
1354
|
+
const componentName = getComponentNameById(detailed.componentId);
|
|
1355
|
+
if (!componentName) console.warn(`Component ID ${detailed.componentId} in relation has no registered name`);
|
|
1356
|
+
return {
|
|
1357
|
+
component: componentName || detailed.componentId.toString(),
|
|
1358
|
+
target: detailed.targetId
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
case "component-relation": {
|
|
1362
|
+
const componentName = getComponentNameById(detailed.componentId);
|
|
1363
|
+
const targetName = getComponentNameById(detailed.targetId);
|
|
1364
|
+
if (!componentName) console.warn(`Component ID ${detailed.componentId} in relation has no registered name`);
|
|
1365
|
+
if (!targetName) console.warn(`Target component ID ${detailed.targetId} in relation has no registered name`);
|
|
1366
|
+
return {
|
|
1367
|
+
component: componentName || detailed.componentId.toString(),
|
|
1368
|
+
target: targetName || detailed.targetId
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
case "wildcard-relation": {
|
|
1372
|
+
const componentName = getComponentNameById(detailed.componentId);
|
|
1373
|
+
if (!componentName) console.warn(`Component ID ${detailed.componentId} in relation has no registered name`);
|
|
1374
|
+
return {
|
|
1375
|
+
component: componentName || detailed.componentId.toString(),
|
|
1376
|
+
target: "*"
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
default: return id;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Decode a SerializedEntityId back into an internal EntityId
|
|
1384
|
+
*/
|
|
1385
|
+
function decodeSerializedId(sid) {
|
|
1386
|
+
if (typeof sid === "number") return sid;
|
|
1387
|
+
if (typeof sid === "string") {
|
|
1388
|
+
const id = getComponentIdByName(sid);
|
|
1389
|
+
if (id === void 0) {
|
|
1390
|
+
const num = parseInt(sid, 10);
|
|
1391
|
+
if (!isNaN(num)) return num;
|
|
1392
|
+
throw new Error(`Unknown component name in snapshot: ${sid}`);
|
|
1393
|
+
}
|
|
1394
|
+
return id;
|
|
1395
|
+
}
|
|
1396
|
+
if (typeof sid === "object" && sid !== null && typeof sid.component === "string") {
|
|
1397
|
+
let compId = getComponentIdByName(sid.component);
|
|
1398
|
+
if (compId === void 0) {
|
|
1399
|
+
const num = parseInt(sid.component, 10);
|
|
1400
|
+
if (!isNaN(num)) compId = num;
|
|
1401
|
+
}
|
|
1402
|
+
if (compId === void 0) throw new Error(`Unknown component name in snapshot: ${sid.component}`);
|
|
1403
|
+
if (sid.target === "*") return relation(compId, "*");
|
|
1404
|
+
let targetId;
|
|
1405
|
+
if (typeof sid.target === "string") {
|
|
1406
|
+
const tid = getComponentIdByName(sid.target);
|
|
1407
|
+
if (tid === void 0) {
|
|
1408
|
+
const num = parseInt(sid.target, 10);
|
|
1409
|
+
if (!isNaN(num)) targetId = num;
|
|
1410
|
+
else throw new Error(`Unknown target component name in snapshot: ${sid.target}`);
|
|
1411
|
+
} else targetId = tid;
|
|
1412
|
+
} else targetId = sid.target;
|
|
1413
|
+
return relation(compId, targetId);
|
|
1414
|
+
}
|
|
1415
|
+
throw new Error(`Invalid ID in snapshot: ${JSON.stringify(sid)}`);
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1343
1418
|
* World class for ECS architecture
|
|
1344
1419
|
* Manages entities and components
|
|
1345
1420
|
*/
|
|
@@ -1375,27 +1450,12 @@ var World = class {
|
|
|
1375
1450
|
if (snapshot && typeof snapshot === "object") {
|
|
1376
1451
|
if (snapshot.entityManager) this.entityIdManager.deserializeState(snapshot.entityManager);
|
|
1377
1452
|
if (Array.isArray(snapshot.entities)) for (const entry of snapshot.entities) {
|
|
1378
|
-
const entityId = entry.id;
|
|
1453
|
+
const entityId = decodeSerializedId(entry.id);
|
|
1379
1454
|
const componentsArray = entry.components || [];
|
|
1380
1455
|
const componentMap = /* @__PURE__ */ new Map();
|
|
1381
1456
|
const componentTypes = [];
|
|
1382
1457
|
for (const componentEntry of componentsArray) {
|
|
1383
|
-
const
|
|
1384
|
-
let componentType;
|
|
1385
|
-
if (typeof componentTypeRaw === "number") componentType = componentTypeRaw;
|
|
1386
|
-
else if (typeof componentTypeRaw === "string") {
|
|
1387
|
-
const compId = getComponentIdByName(componentTypeRaw);
|
|
1388
|
-
if (compId === void 0) throw new Error(`Unknown component name in snapshot: ${componentTypeRaw}`);
|
|
1389
|
-
componentType = compId;
|
|
1390
|
-
} else if (typeof componentTypeRaw === "object" && componentTypeRaw !== null && typeof componentTypeRaw.component === "string") {
|
|
1391
|
-
const compId = getComponentIdByName(componentTypeRaw.component);
|
|
1392
|
-
if (compId === void 0) throw new Error(`Unknown component name in snapshot: ${componentTypeRaw.component}`);
|
|
1393
|
-
if (typeof componentTypeRaw.target === "string") {
|
|
1394
|
-
const targetCompId = getComponentIdByName(componentTypeRaw.target);
|
|
1395
|
-
if (targetCompId === void 0) throw new Error(`Unknown target component name in snapshot: ${componentTypeRaw.target}`);
|
|
1396
|
-
componentType = relation(compId, targetCompId);
|
|
1397
|
-
} else componentType = relation(compId, componentTypeRaw.target);
|
|
1398
|
-
} else throw new Error(`Invalid component type in snapshot: ${JSON.stringify(componentTypeRaw)}`);
|
|
1458
|
+
const componentType = decodeSerializedId(componentEntry.type);
|
|
1399
1459
|
componentMap.set(componentType, componentEntry.value);
|
|
1400
1460
|
componentTypes.push(componentType);
|
|
1401
1461
|
}
|
|
@@ -1558,6 +1618,27 @@ var World = class {
|
|
|
1558
1618
|
return query;
|
|
1559
1619
|
}
|
|
1560
1620
|
/**
|
|
1621
|
+
* Create an EntityBuilder for convenient entity creation.
|
|
1622
|
+
* @returns EntityBuilder
|
|
1623
|
+
*/
|
|
1624
|
+
spawn() {
|
|
1625
|
+
return new EntityBuilder(this);
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Spawn multiple entities using an EntityBuilder configuration callback
|
|
1629
|
+
* @param count number of entities
|
|
1630
|
+
* @param configure builder configuration callback
|
|
1631
|
+
* @returns Created entity IDs
|
|
1632
|
+
*/
|
|
1633
|
+
spawnMany(count, configure) {
|
|
1634
|
+
const entities = [];
|
|
1635
|
+
for (let i = 0; i < count; i++) {
|
|
1636
|
+
const builder = new EntityBuilder(this);
|
|
1637
|
+
entities.push(configure(builder, i).build());
|
|
1638
|
+
}
|
|
1639
|
+
return entities;
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1561
1642
|
* @internal Register a query for archetype update notifications
|
|
1562
1643
|
*/
|
|
1563
1644
|
_registerQuery(query) {
|
|
@@ -1958,16 +2039,6 @@ var World = class {
|
|
|
1958
2039
|
}
|
|
1959
2040
|
}
|
|
1960
2041
|
/**
|
|
1961
|
-
* Remove an empty archetype from all internal data structures
|
|
1962
|
-
*/
|
|
1963
|
-
cleanupEmptyArchetype(archetype) {
|
|
1964
|
-
if (archetype.getEntities().length > 0) return;
|
|
1965
|
-
this.removeArchetypeFromList(archetype);
|
|
1966
|
-
this.removeArchetypeFromSignatureMap(archetype);
|
|
1967
|
-
this.removeArchetypeFromComponentIndex(archetype);
|
|
1968
|
-
this.removeArchetypeFromQueries(archetype);
|
|
1969
|
-
}
|
|
1970
|
-
/**
|
|
1971
2042
|
* Remove archetype from the main archetypes list
|
|
1972
2043
|
*/
|
|
1973
2044
|
removeArchetypeFromList(archetype) {
|
|
@@ -2037,35 +2108,11 @@ var World = class {
|
|
|
2037
2108
|
for (const archetype of this.archetypes) {
|
|
2038
2109
|
const dumpedEntities = archetype.dump();
|
|
2039
2110
|
for (const { entity, components } of dumpedEntities) entities.push({
|
|
2040
|
-
id: entity,
|
|
2041
|
-
components: Array.from(components.entries()).map(([rawType, value]) => {
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
switch (detailedType.type) {
|
|
2046
|
-
case "component":
|
|
2047
|
-
type = getComponentNameById(rawType) || rawType;
|
|
2048
|
-
break;
|
|
2049
|
-
case "entity-relation":
|
|
2050
|
-
componentName = getComponentNameById(detailedType.componentId);
|
|
2051
|
-
if (componentName) type = {
|
|
2052
|
-
component: componentName,
|
|
2053
|
-
target: detailedType.targetId
|
|
2054
|
-
};
|
|
2055
|
-
break;
|
|
2056
|
-
case "component-relation":
|
|
2057
|
-
componentName = getComponentNameById(detailedType.componentId);
|
|
2058
|
-
if (componentName) type = {
|
|
2059
|
-
component: componentName,
|
|
2060
|
-
target: getComponentNameById(detailedType.targetId) || detailedType.targetId
|
|
2061
|
-
};
|
|
2062
|
-
break;
|
|
2063
|
-
}
|
|
2064
|
-
return {
|
|
2065
|
-
type,
|
|
2066
|
-
value: value === MISSING_COMPONENT ? void 0 : value
|
|
2067
|
-
};
|
|
2068
|
-
})
|
|
2111
|
+
id: encodeEntityId(entity),
|
|
2112
|
+
components: Array.from(components.entries()).map(([rawType, value]) => ({
|
|
2113
|
+
type: encodeEntityId(rawType),
|
|
2114
|
+
value: value === MISSING_COMPONENT ? void 0 : value
|
|
2115
|
+
}))
|
|
2069
2116
|
});
|
|
2070
2117
|
}
|
|
2071
2118
|
return {
|
|
@@ -2075,7 +2122,63 @@ var World = class {
|
|
|
2075
2122
|
};
|
|
2076
2123
|
}
|
|
2077
2124
|
};
|
|
2125
|
+
var EntityBuilder = class {
|
|
2126
|
+
world;
|
|
2127
|
+
components = [];
|
|
2128
|
+
constructor(world) {
|
|
2129
|
+
this.world = world;
|
|
2130
|
+
}
|
|
2131
|
+
with(componentId, value) {
|
|
2132
|
+
this.components.push({
|
|
2133
|
+
type: "component",
|
|
2134
|
+
id: componentId,
|
|
2135
|
+
value
|
|
2136
|
+
});
|
|
2137
|
+
return this;
|
|
2138
|
+
}
|
|
2139
|
+
withTag(componentId) {
|
|
2140
|
+
this.components.push({
|
|
2141
|
+
type: "component",
|
|
2142
|
+
id: componentId,
|
|
2143
|
+
value: void 0
|
|
2144
|
+
});
|
|
2145
|
+
return this;
|
|
2146
|
+
}
|
|
2147
|
+
withRelation(componentId, targetEntity, value) {
|
|
2148
|
+
this.components.push({
|
|
2149
|
+
type: "relation",
|
|
2150
|
+
componentId,
|
|
2151
|
+
targetId: targetEntity,
|
|
2152
|
+
value
|
|
2153
|
+
});
|
|
2154
|
+
return this;
|
|
2155
|
+
}
|
|
2156
|
+
withRelationTag(componentId, targetEntity) {
|
|
2157
|
+
this.components.push({
|
|
2158
|
+
type: "relation",
|
|
2159
|
+
componentId,
|
|
2160
|
+
targetId: targetEntity,
|
|
2161
|
+
value: void 0
|
|
2162
|
+
});
|
|
2163
|
+
return this;
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* Create an entity and enqueue components to be applied. This method
|
|
2167
|
+
* does NOT call `world.sync()` automatically; callers must invoke
|
|
2168
|
+
* `world.sync()` to apply deferred commands.
|
|
2169
|
+
* (Previously auto-synced; now a breaking change — buildDeferred() removed.)
|
|
2170
|
+
*/
|
|
2171
|
+
build() {
|
|
2172
|
+
const entity = this.world.new();
|
|
2173
|
+
for (const def of this.components) if (def.type === "component") this.world.set(entity, def.id, def.value);
|
|
2174
|
+
else {
|
|
2175
|
+
const relationId = relation(def.componentId, def.targetId);
|
|
2176
|
+
this.world.set(entity, relationId, def.value);
|
|
2177
|
+
}
|
|
2178
|
+
return entity;
|
|
2179
|
+
}
|
|
2180
|
+
};
|
|
2078
2181
|
|
|
2079
2182
|
//#endregion
|
|
2080
|
-
export {
|
|
2183
|
+
export { decodeRelationId as a, isComponentId as c, isWildcardRelationId as d, relation as f, component as i, isEntityId as l, World as n, getComponentIdByName as o, Query as r, getComponentNameById as s, EntityBuilder as t, isRelationId as u };
|
|
2081
2184
|
//# sourceMappingURL=world.mjs.map
|