@ste_tisci/ecs 0.1.5 → 0.1.6

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/dist/index.d.ts DELETED
@@ -1,97 +0,0 @@
1
- /**
2
- * Represents component data in Structure of Arrays (SoA) format.
3
- * Each property of the component is stored in a separate array,
4
- * enabling cache-efficient iteration and SIMD operations.
5
- * @template T - The component data type
6
- * @example
7
- * // For Position component: { x: number, y: number }
8
- * // ComponentDataArrays would be: { x: number[], y: number[] }
9
- */
10
- type ComponentDataArrays<T> = {
11
- [K in keyof T]: T[K][];
12
- };
13
-
14
- /**
15
- * Maps component names to their data arrays for direct access.
16
- * Used to expose component data to the user without store methods.
17
- * @template T - Record type defining all component types
18
- */
19
- type StoreDataMap<T> = {
20
- [K in keyof T]: ComponentDataArrays<T[K]>;
21
- };
22
-
23
- /**
24
- * Type helper for the query result.
25
- * Creates an object with eid and all required components typed correctly.
26
- * @template T - Record type with all component types.
27
- * @template C - Array of the component names requested in the query.
28
- */
29
- type QueryResult<T, C extends (keyof T)[]> = {
30
- [K in C[number]]: { id: number };
31
- };
32
-
33
- /**
34
- * Interface for the main ECS registry.
35
- * Provides high-level API for entity-component management,
36
- * coordinating between EntityManager and ComponentStores.
37
- * @template T - Record type defining all component types in the ECS
38
- */
39
- interface IECS<T> {
40
- /**
41
- * Initializes the ECS with component definitions.
42
- * Registers all components and creates their corresponding stores.
43
- * Must be called before any other operations.
44
- * @param names -component names
45
- */
46
- defineComponents<K extends readonly (keyof T)[]>(...names: K): void;
47
-
48
- /**
49
- * Creates a new entity in the ECS.
50
- * @returns The ID of the newly created entity
51
- */
52
- createEntity: () => number;
53
-
54
- /**
55
- * Completely removes an entity and all its components from the ECS.
56
- * Cleans up all component data and entity records.
57
- * @param eid - The entity ID to destroy
58
- */
59
- destroyEntity: (eid: number) => void;
60
-
61
- /**
62
- * Adds a component to an entity with the provided data.
63
- * Updates both the entity's bitmask and the component store.
64
- * @template K - The component name type (key of T)
65
- * @param eid - The entity ID
66
- * @param name - The component name
67
- * @param data - The component data to store
68
- */
69
- addComponent: <K extends keyof T>(eid: number, name: K, data: T[K]) => void;
70
-
71
- /**
72
- * Removes a component from an entity.
73
- * Updates both the entity's bitmask and the component store.
74
- * @template K - The component name type (key of T)
75
- * @param eid - The entity ID
76
- * @param name - The component name
77
- */
78
- removeComponent: <K extends keyof T>(eid: number, name: K) => void;
79
-
80
- /**
81
- * Retrieves all entities that contain the specified set of components.
82
- * Performs a filtered search across component stores and yields each matching entity.
83
- * The returned generator allows efficient iteration without allocating large arrays.
84
- *
85
- * @template C - Array of component names to query for
86
- * @param components - List of component names that the entity must include
87
- * @returns A generator that yields an object for each matching entity,
88
- * containing the entity ID and the related component data
89
- */
90
- query: <C extends (keyof T)[]>(...componentName: C) => Generator<QueryResult<T, C>>;
91
-
92
- components: StoreDataMap<T>;
93
- }
94
-
95
- declare function ECS<T extends Record<string, Record<string, any>>>(): IECS<T>;
96
-
97
- export { ECS };
package/dist/index.js DELETED
@@ -1,220 +0,0 @@
1
- // src/utils/SparseSet.ts
2
- function SparseSet() {
3
- const sparse = [];
4
- const dense = [];
5
- let size = 0;
6
- function has(eid) {
7
- const ID = getIndex(eid);
8
- const exist = ID !== void 0 && dense[ID] === eid;
9
- const isWhitinBounds = ID >= 0 && ID < size;
10
- return exist && isWhitinBounds;
11
- }
12
- function add(eid) {
13
- if (has(eid)) throw new Error(`ID ${eid} already present in the set`);
14
- sparse[eid] = size;
15
- dense[size] = eid;
16
- size++;
17
- }
18
- function remove(eid) {
19
- if (!has(eid)) throw new Error(`Cannot remove ID ${eid} because does not exists`);
20
- const ID = getIndex(eid);
21
- const last = dense[size - 1];
22
- dense[ID] = last;
23
- sparse[last] = ID;
24
- dense.pop();
25
- size--;
26
- delete sparse[eid];
27
- }
28
- function getIndex(eid) {
29
- return sparse[eid];
30
- }
31
- function getDense() {
32
- return dense;
33
- }
34
- function getSize() {
35
- return size;
36
- }
37
- return { has, add, remove, getIndex, getDense, getSize };
38
- }
39
-
40
- // src/ComponentStore.ts
41
- function ComponentStore() {
42
- const componentSet = SparseSet();
43
- const componentData = {};
44
- function add(eid, data) {
45
- if (componentSet.has(eid)) throw new Error(`Entity ${eid} already has this component`);
46
- const ID = componentSet.getSize();
47
- componentSet.add(eid);
48
- for (const key in data) {
49
- if (!componentData[key]) {
50
- componentData[key] = [];
51
- }
52
- componentData[key][ID] = data[key];
53
- }
54
- }
55
- function remove(eid) {
56
- if (!componentSet.has(eid)) throw new Error(`Entity ${eid} does not have this component`);
57
- const ID = componentSet.getIndex(eid);
58
- const lastID = componentSet.getSize() - 1;
59
- if (ID !== lastID) {
60
- for (const key of Object.keys(componentData)) {
61
- componentData[key][ID] = componentData[key][lastID];
62
- }
63
- }
64
- componentSet.remove(eid);
65
- for (const key in componentData) {
66
- componentData[key].pop();
67
- }
68
- }
69
- function getDense() {
70
- return componentSet.getDense();
71
- }
72
- function getData() {
73
- return componentData;
74
- }
75
- function getIndex(eid) {
76
- return componentSet.getIndex(eid);
77
- }
78
- function getSize() {
79
- return componentSet.getSize();
80
- }
81
- return { add, remove, getIndex, getDense, getData, getSize };
82
- }
83
-
84
- // src/EntityManager.ts
85
- function EntityManager(registry) {
86
- let nextID = 0;
87
- const recycledIDs = [];
88
- const bitMasks = [];
89
- function exists(eid) {
90
- const isWithinBounds = eid >= 0 && eid < nextID;
91
- const hasMask = bitMasks[eid] !== void 0;
92
- return isWithinBounds && hasMask;
93
- }
94
- function hasComponent(eid, name) {
95
- if (!exists(eid)) throw new Error(`Entity ${eid} does not exist`);
96
- const cid = registry.getID(name);
97
- return (bitMasks[eid] & 1n << BigInt(cid)) !== 0n;
98
- }
99
- function create() {
100
- const ID = recycledIDs.length > 0 ? recycledIDs.pop() : nextID++;
101
- bitMasks[ID] = 0n;
102
- return ID;
103
- }
104
- function remove(eid) {
105
- if (!exists(eid)) throw new Error(`Entity ${eid} does not exist`);
106
- bitMasks[eid] = void 0;
107
- recycledIDs.push(eid);
108
- }
109
- function addComponent(eid, name) {
110
- if (!exists(eid)) throw new Error(`Entity ${eid} does not exists`);
111
- if (hasComponent(eid, name)) throw new Error(`Entity ${eid} already has component ${name}`);
112
- const cid = registry.getID(name);
113
- bitMasks[eid] |= 1n << BigInt(cid);
114
- }
115
- function removeComponent(eid, name) {
116
- if (!exists(eid)) throw new Error(`Entity ${eid} does not exists`);
117
- if (!hasComponent(eid, name)) throw new Error(`Entity ${eid} does not have component ${name}`);
118
- const cid = registry.getID(name);
119
- bitMasks[eid] &= ~(1n << BigInt(cid));
120
- }
121
- function getMask(eid) {
122
- return bitMasks[eid];
123
- }
124
- return { exists, hasComponent, create, remove, addComponent, removeComponent, getMask };
125
- }
126
-
127
- // src/utils/ComponentRegistry.ts
128
- function createComponentRegistry() {
129
- const nameToID = /* @__PURE__ */ new Map();
130
- const IDtoName = /* @__PURE__ */ new Map();
131
- let nextID = 0;
132
- function register(name) {
133
- if (!nameToID.has(name)) {
134
- const eid = nextID++;
135
- nameToID.set(name, eid);
136
- IDtoName.set(eid, name);
137
- }
138
- }
139
- function getID(name) {
140
- const eid = nameToID.get(name);
141
- if (eid === void 0) throw new Error(`Component ${name} not registered`);
142
- return eid;
143
- }
144
- function getName(cid) {
145
- const name = IDtoName.get(cid);
146
- if (!name) throw new Error(`Component ID ${cid} not registered`);
147
- return name;
148
- }
149
- return { register, getID, getName };
150
- }
151
-
152
- // src/Ecs.ts
153
- function ECS() {
154
- const ComponentRegistry = createComponentRegistry();
155
- const Entities = EntityManager(ComponentRegistry);
156
- const internalStores = {};
157
- const components = {};
158
- function defineComponents(...names) {
159
- for (const name of names) {
160
- ComponentRegistry.register(name);
161
- internalStores[name] = ComponentStore();
162
- components[name] = internalStores[name].getData();
163
- }
164
- }
165
- function createEntity() {
166
- return Entities.create();
167
- }
168
- function destroyEntity(eid) {
169
- const entityMask = Entities.getMask(eid);
170
- let cid = 0;
171
- let tempMask = entityMask;
172
- while (tempMask !== 0n) {
173
- if ((tempMask & 1n) !== 0n) {
174
- const name = ComponentRegistry.getName(cid);
175
- internalStores[name].remove(eid);
176
- }
177
- tempMask >>= 1n;
178
- cid++;
179
- }
180
- Entities.remove(eid);
181
- }
182
- function addComponent(eid, name, data) {
183
- Entities.addComponent(eid, name);
184
- internalStores[name].add(eid, data);
185
- }
186
- function removeComponent(eid, name) {
187
- Entities.removeComponent(eid, name);
188
- internalStores[name].remove(eid);
189
- }
190
- function* query(...componentName) {
191
- if (componentName.length === 0) return;
192
- let smallestStore = internalStores[componentName[0]];
193
- let smallestSize = smallestStore.getSize();
194
- for (let i = 1; i < componentName.length; i++) {
195
- const store = internalStores[componentName[i]];
196
- const size = store.getSize();
197
- if (size < smallestSize) {
198
- smallestStore = store;
199
- smallestSize = size;
200
- }
201
- }
202
- let targetMask = 0n;
203
- for (const name of componentName) {
204
- targetMask |= 1n << BigInt(ComponentRegistry.getID(name));
205
- }
206
- for (const eid of smallestStore.getDense()) {
207
- if ((Entities.getMask(eid) & targetMask) !== targetMask) continue;
208
- const result = {};
209
- for (const name of componentName) {
210
- const id = internalStores[name].getIndex(eid);
211
- result[name] = { id };
212
- }
213
- yield result;
214
- }
215
- }
216
- return { defineComponents, createEntity, destroyEntity, addComponent, removeComponent, query, components };
217
- }
218
- export {
219
- ECS
220
- };