@codehz/ecs 0.9.0 → 0.10.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/README.md +13 -1
- package/dist/builder.d.mts +59 -44
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1 -1
- package/dist/world.mjs +1136 -887
- package/dist/world.mjs.map +1 -1
- package/examples/spatial-grid.ts +1 -1
- package/package.json +1 -1
- package/src/__tests__/component/singleton.test.ts +76 -34
- package/src/__tests__/serialization/bounds.test.ts +2 -3
- package/src/__tests__/world/component-management.test.ts +6 -5
- package/src/index.ts +2 -0
- package/src/world/archetype-manager.ts +283 -0
- package/src/world/command-executor.ts +258 -0
- package/src/world/debug-stats.ts +147 -0
- package/src/world/operations.ts +88 -0
- package/src/world/singleton.ts +51 -0
- package/src/world/world.ts +178 -573
package/README.md
CHANGED
|
@@ -222,7 +222,7 @@ bun run examples/inventory-system-relations.ts
|
|
|
222
222
|
| `spawnMany(count, configure)` | 批量创建多个实体 |
|
|
223
223
|
| `exists(entity)` | 检查实体是否存在 |
|
|
224
224
|
| `set(entity, componentId, data?)` | 添加/更新组件(缓冲,`sync()` 后生效)。对 `void` 组件可不传 data |
|
|
225
|
-
| `
|
|
225
|
+
| `singleton(componentId)` | 获取单例组件句柄,推荐用 `world.singleton(Config).set(value)` |
|
|
226
226
|
| `get(entity, componentId?)` | 获取组件数据。**若组件不存在会抛出异常**,请先用 `has()` 检查或使用 `getOptional()` |
|
|
227
227
|
| `getOptional(entity, componentId?)` | 安全获取组件,返回 `{ value: T } \| undefined` |
|
|
228
228
|
| `has(entity, componentId?)` | 检查组件是否存在 |
|
|
@@ -236,6 +236,18 @@ bun run examples/inventory-system-relations.ts
|
|
|
236
236
|
| `serialize()` | 序列化世界状态为快照对象 |
|
|
237
237
|
| `sync()` | 执行所有延迟命令 |
|
|
238
238
|
|
|
239
|
+
单例组件推荐写法:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const config = world.singleton(GlobalConfig);
|
|
243
|
+
config.set({ debug: true });
|
|
244
|
+
world.sync();
|
|
245
|
+
|
|
246
|
+
if (config.has()) {
|
|
247
|
+
console.log(config.get());
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
239
251
|
### Query
|
|
240
252
|
|
|
241
253
|
查询通过 `world.createQuery()` 创建,应**跨帧复用**以获得最佳性能。
|
package/dist/builder.d.mts
CHANGED
|
@@ -1009,6 +1009,41 @@ declare class Query {
|
|
|
1009
1009
|
get disposed(): boolean;
|
|
1010
1010
|
}
|
|
1011
1011
|
//#endregion
|
|
1012
|
+
//#region src/world/singleton.d.ts
|
|
1013
|
+
interface SingletonHandleOps<T> {
|
|
1014
|
+
has(): boolean;
|
|
1015
|
+
get(): T;
|
|
1016
|
+
getOptional(): {
|
|
1017
|
+
value: T;
|
|
1018
|
+
} | undefined;
|
|
1019
|
+
remove(): void;
|
|
1020
|
+
set(value: T | undefined): void;
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Explicit handle for a singleton component (component-as-entity).
|
|
1024
|
+
*
|
|
1025
|
+
* This provides an explicit and concise API for singleton components without
|
|
1026
|
+
* overloading `world.set()` semantics.
|
|
1027
|
+
*
|
|
1028
|
+
* @example
|
|
1029
|
+
* const config = world.singleton(Config);
|
|
1030
|
+
* config.set({ debug: true });
|
|
1031
|
+
* world.sync();
|
|
1032
|
+
* console.log(config.get());
|
|
1033
|
+
*/
|
|
1034
|
+
declare class SingletonHandle<T = void> {
|
|
1035
|
+
readonly componentId: ComponentId<T>;
|
|
1036
|
+
private readonly ops;
|
|
1037
|
+
constructor(componentId: ComponentId<T>, ops: SingletonHandleOps<T>);
|
|
1038
|
+
has(): boolean;
|
|
1039
|
+
get(): T;
|
|
1040
|
+
getOptional(): {
|
|
1041
|
+
value: T;
|
|
1042
|
+
} | undefined;
|
|
1043
|
+
remove(): void;
|
|
1044
|
+
set(...args: T extends void ? [] : [value: NoInfer<T>]): void;
|
|
1045
|
+
}
|
|
1046
|
+
//#endregion
|
|
1012
1047
|
//#region src/world/world.d.ts
|
|
1013
1048
|
/**
|
|
1014
1049
|
* World class for ECS architecture
|
|
@@ -1016,32 +1051,22 @@ declare class Query {
|
|
|
1016
1051
|
*/
|
|
1017
1052
|
declare class World {
|
|
1018
1053
|
private entityIdManager;
|
|
1019
|
-
private archetypes;
|
|
1020
|
-
private archetypeBySignature;
|
|
1021
|
-
private entityToArchetype;
|
|
1022
|
-
private archetypesByComponent;
|
|
1023
1054
|
private entityReferences;
|
|
1024
|
-
/** Reverse index: entity ID → set of archetypes whose componentTypes include that entity ID */
|
|
1025
|
-
private entityToReferencingArchetypes;
|
|
1026
1055
|
/** Sparse relation storage (for components created with `sparse: true`), shared with all Archetype instances */
|
|
1027
1056
|
private readonly sparseStore;
|
|
1028
1057
|
/** Component entity (singleton) storage */
|
|
1029
1058
|
private readonly componentEntities;
|
|
1059
|
+
private archetypeManager;
|
|
1060
|
+
private get archetypes();
|
|
1061
|
+
private get entityToArchetype();
|
|
1062
|
+
private get archetypesByComponent();
|
|
1063
|
+
private get entityToReferencingArchetypes();
|
|
1030
1064
|
private readonly queryRegistry;
|
|
1031
1065
|
private hooks;
|
|
1032
|
-
private readonly
|
|
1033
|
-
private _debugMigrations;
|
|
1034
|
-
private _debugArchetypesCreated;
|
|
1035
|
-
private _debugArchetypesRemoved;
|
|
1066
|
+
private readonly debugStats;
|
|
1036
1067
|
private commandBuffer;
|
|
1037
|
-
private
|
|
1038
|
-
private readonly _removeChangeset;
|
|
1039
|
-
/** Cached command processor context to avoid per-entity object allocation */
|
|
1040
|
-
private readonly _commandCtx;
|
|
1041
|
-
/** Cached hooks context to avoid per-entity object allocation */
|
|
1042
|
-
private readonly _hooksCtx;
|
|
1068
|
+
private commandExecutor;
|
|
1043
1069
|
constructor(snapshot?: SerializedWorld);
|
|
1044
|
-
private createArchetypeSignature;
|
|
1045
1070
|
/**
|
|
1046
1071
|
* Creates a new entity.
|
|
1047
1072
|
* The entity is created with an empty component set and can be configured using `set()`.
|
|
@@ -1086,11 +1111,6 @@ declare class World {
|
|
|
1086
1111
|
* if (world.has(entity, Position)) { ... }
|
|
1087
1112
|
*/
|
|
1088
1113
|
exists(entityId: EntityId): boolean;
|
|
1089
|
-
private assertEntityExists;
|
|
1090
|
-
private assertComponentTypeValid;
|
|
1091
|
-
private assertSetComponentTypeValid;
|
|
1092
|
-
private resolveSetOperation;
|
|
1093
|
-
private resolveRemoveOperation;
|
|
1094
1114
|
/**
|
|
1095
1115
|
* Adds or updates a component on an entity (or marks void component as present).
|
|
1096
1116
|
* The change is buffered and takes effect after calling `world.sync()`.
|
|
@@ -1102,21 +1122,17 @@ declare class World {
|
|
|
1102
1122
|
* @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
|
|
1103
1123
|
* Adds or updates a component with data on the entity
|
|
1104
1124
|
*
|
|
1105
|
-
* @overload set<T>(componentId: ComponentId<T>, component: NoInfer<T>): void
|
|
1106
|
-
* Adds or updates a singleton component (shorthand for set(componentId, componentId, component))
|
|
1107
|
-
*
|
|
1108
1125
|
* @throws {Error} If the entity does not exist
|
|
1109
1126
|
* @throws {Error} If the component type is invalid or is a wildcard relation
|
|
1110
1127
|
*
|
|
1111
1128
|
* @example
|
|
1112
1129
|
* world.set(entity, Position, { x: 10, y: 20 });
|
|
1113
1130
|
* world.set(entity, Marker); // void component
|
|
1114
|
-
* world.set(
|
|
1131
|
+
* world.singleton(GlobalConfig).set({ debug: true }); // singleton component
|
|
1115
1132
|
* world.sync(); // Apply changes
|
|
1116
1133
|
*/
|
|
1117
1134
|
set(entityId: EntityId, componentType: EntityId<void>): void;
|
|
1118
1135
|
set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void;
|
|
1119
|
-
set<T>(componentId: ComponentId<T>, component: NoInfer<T>): void;
|
|
1120
1136
|
/**
|
|
1121
1137
|
* Removes a component from an entity.
|
|
1122
1138
|
* The change is buffered and takes effect after calling `world.sync()`.
|
|
@@ -1154,6 +1170,18 @@ declare class World {
|
|
|
1154
1170
|
* world.sync(); // Apply changes
|
|
1155
1171
|
*/
|
|
1156
1172
|
delete(entityId: EntityId): void;
|
|
1173
|
+
/**
|
|
1174
|
+
* Returns an explicit handle for a singleton component (component-as-entity).
|
|
1175
|
+
*
|
|
1176
|
+
* This is the preferred API for singleton components.
|
|
1177
|
+
*
|
|
1178
|
+
* @example
|
|
1179
|
+
* const config = world.singleton(GlobalConfig);
|
|
1180
|
+
* config.set({ debug: true });
|
|
1181
|
+
* world.sync();
|
|
1182
|
+
* console.log(config.get());
|
|
1183
|
+
*/
|
|
1184
|
+
singleton<T>(componentId: ComponentId<T>): SingletonHandle<T>;
|
|
1157
1185
|
/**
|
|
1158
1186
|
* Checks if a specific **component** is present on an entity.
|
|
1159
1187
|
*
|
|
@@ -1401,8 +1429,6 @@ declare class World {
|
|
|
1401
1429
|
* This is intended for development/debugging and leak detection.
|
|
1402
1430
|
*/
|
|
1403
1431
|
createDebugStatsCollector(callback: (stats: SyncDebugStats) => void): DebugStatsCollector;
|
|
1404
|
-
private _resetDebugActivityCounters;
|
|
1405
|
-
private _deliverDebugStats;
|
|
1406
1432
|
/**
|
|
1407
1433
|
* Synchronizes all buffered commands (set/remove/delete) to the world.
|
|
1408
1434
|
* This method must be called after making changes via `set()`, `remove()`, or `delete()` for them to take effect.
|
|
@@ -1498,7 +1524,6 @@ declare class World {
|
|
|
1498
1524
|
* @internal
|
|
1499
1525
|
*/
|
|
1500
1526
|
getMatchingArchetypes(componentTypes: EntityId<any>[]): Archetype[];
|
|
1501
|
-
private getArchetypesWithComponents;
|
|
1502
1527
|
/**
|
|
1503
1528
|
* Queries entities with specific components.
|
|
1504
1529
|
* For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
|
|
@@ -1531,21 +1556,9 @@ declare class World {
|
|
|
1531
1556
|
entity: EntityId;
|
|
1532
1557
|
components: ComponentTuple<T>;
|
|
1533
1558
|
}>;
|
|
1534
|
-
private executeEntityCommands;
|
|
1535
|
-
private applyEntityCommands;
|
|
1536
|
-
private createHooksContext;
|
|
1537
|
-
private removeComponentImmediate;
|
|
1538
|
-
private updateEntityReferences;
|
|
1539
1559
|
private ensureArchetype;
|
|
1540
|
-
/** Add componentType to the reverse index if it contains an entity ID */
|
|
1541
|
-
private addToReferencingIndex;
|
|
1542
|
-
/** Remove componentType from the reverse index */
|
|
1543
|
-
private removeFromReferencingIndex;
|
|
1544
|
-
private createNewArchetype;
|
|
1545
|
-
private updateArchetypeHookMatches;
|
|
1546
|
-
private archetypeMatchesHook;
|
|
1547
1560
|
private cleanupArchetypesReferencingEntity;
|
|
1548
|
-
private
|
|
1561
|
+
private archetypeMatchesHook;
|
|
1549
1562
|
/**
|
|
1550
1563
|
* Serializes the entire world state to a plain JavaScript object.
|
|
1551
1564
|
* This creates a "memory snapshot" that can be stored or transmitted.
|
|
@@ -1567,6 +1580,8 @@ declare class World {
|
|
|
1567
1580
|
* const savedData = JSON.parse(localStorage.getItem('save'));
|
|
1568
1581
|
* const newWorld = new World(savedData);
|
|
1569
1582
|
*/
|
|
1583
|
+
private removeComponentImmediate;
|
|
1584
|
+
private createHooksContext;
|
|
1570
1585
|
serialize(): SerializedWorld;
|
|
1571
1586
|
}
|
|
1572
1587
|
//#endregion
|
|
@@ -1643,5 +1658,5 @@ declare class EntityBuilder {
|
|
|
1643
1658
|
build(): EntityId;
|
|
1644
1659
|
}
|
|
1645
1660
|
//#endregion
|
|
1646
|
-
export {
|
|
1661
|
+
export { EntityRelationId as A, isSparseWildcard as C, ComponentId as D, relation as E, isRelationId as F, WildcardRelationId as M, isComponentId as N, ComponentRelationId as O, isEntityId as P, isSparseRelation as S, isWildcardRelationId as T, ComponentOptions as _, SingletonHandleOps as a, getComponentNameById as b, ComponentType as c, LifecycleHook as d, SyncDebugStats as f, SerializedWorld as g, SerializedEntityId as h, SingletonHandle as i, RelationId as j, EntityId as k, DebugStatsCollector as l, SerializedEntity as m, EntityBuilder as n, Query as o, SerializedComponent as p, World as r, ComponentTuple as s, ComponentDef as t, LifecycleCallback as u, component as v, decodeRelationId as w, isSparseComponent as x, getComponentIdByName as y };
|
|
1647
1662
|
//# sourceMappingURL=builder.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { type ComponentDef, type ComponentId, type ComponentOptions, type ComponentRelationId, type ComponentTuple, type ComponentType, type DebugStatsCollector, EntityBuilder, type EntityId, type EntityRelationId, type LifecycleCallback, type LifecycleHook, Query, type RelationId, type SerializedComponent, type SerializedEntity, type SerializedEntityId, type SerializedWorld, type SyncDebugStats, type WildcardRelationId, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
|
|
1
|
+
import { A as EntityRelationId, C as isSparseWildcard, D as ComponentId, E as relation, F as isRelationId, M as WildcardRelationId, N as isComponentId, O as ComponentRelationId, P as isEntityId, S as isSparseRelation, T as isWildcardRelationId, _ as ComponentOptions, a as SingletonHandleOps, b as getComponentNameById, c as ComponentType, d as LifecycleHook, f as SyncDebugStats, g as SerializedWorld, h as SerializedEntityId, i as SingletonHandle, j as RelationId, k as EntityId, l as DebugStatsCollector, m as SerializedEntity, n as EntityBuilder, o as Query, p as SerializedComponent, r as World, s as ComponentTuple, t as ComponentDef, u as LifecycleCallback, v as component, w as decodeRelationId, x as isSparseComponent, y as getComponentIdByName } from "./builder.mjs";
|
|
2
|
+
export { type ComponentDef, type ComponentId, type ComponentOptions, type ComponentRelationId, type ComponentTuple, type ComponentType, type DebugStatsCollector, EntityBuilder, type EntityId, type EntityRelationId, type LifecycleCallback, type LifecycleHook, Query, type RelationId, type SerializedComponent, type SerializedEntity, type SerializedEntityId, type SerializedWorld, SingletonHandle, type SingletonHandleOps, type SyncDebugStats, type WildcardRelationId, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { EntityBuilder, Query, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
|
|
1
|
+
import { a as component, c as isSparseComponent, d as decodeRelationId, f as isWildcardRelationId, g as isRelationId, h as isEntityId, i as EntityBuilder, l as isSparseRelation, m as isComponentId, n as Query, o as getComponentIdByName, p as relation, r as SingletonHandle, s as getComponentNameById, t as World, u as isSparseWildcard } from "./world.mjs";
|
|
2
|
+
export { EntityBuilder, Query, SingletonHandle, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as ComponentId, E as relation, M as WildcardRelationId, d as LifecycleHook, j as RelationId, k as EntityId, n as EntityBuilder, o as Query, r as World, t as ComponentDef, u as LifecycleCallback, v as component } from "./builder.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/testing/index.d.ts
|
|
4
4
|
/**
|
package/dist/testing.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as component, f as isWildcardRelationId, i as EntityBuilder, p as relation, t as World } from "./world.mjs";
|
|
2
2
|
//#region src/testing/index.ts
|
|
3
3
|
/**
|
|
4
4
|
* A test fixture that manages a World instance and provides convenient
|