@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/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 componentTypeRaw = componentEntry.type;
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
- const detailedType = getDetailedIdType(rawType);
2043
- let type = rawType;
2044
- let componentName;
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 { getComponentIdByName as a, isEntityId as c, relation as d, decodeRelationId as i, isRelationId as l, Query as n, getComponentNameById as o, component as r, isComponentId as s, World as t, isWildcardRelationId as u };
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