archetype-ecs-lib 0.4.1 → 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 +2 -4
- package/lib/ecs/Commands.d.ts +1 -1
- package/lib/ecs/Commands.js +2 -2
- package/lib/ecs/Schedule.js +10 -1
- package/lib/ecs/World.d.ts +7 -1
- package/lib/ecs/World.js +79 -63
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# Archetype ECS Lib
|
|
2
4
|
|
|
3
5
|
A tiny **archetype-based ECS** (Entity Component System) for TypeScript.
|
|
@@ -16,10 +18,6 @@ Exports are defined in `index.ts`:
|
|
|
16
18
|
|
|
17
19
|
```bash
|
|
18
20
|
npm i archetype-ecs-lib
|
|
19
|
-
# or
|
|
20
|
-
pnpm add archetype-ecs-lib
|
|
21
|
-
# or
|
|
22
|
-
yarn add archetype-ecs-lib
|
|
23
21
|
````
|
|
24
22
|
|
|
25
23
|
---
|
package/lib/ecs/Commands.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type Command = {
|
|
|
16
16
|
ctor: ComponentCtor<any>;
|
|
17
17
|
};
|
|
18
18
|
export declare class Commands {
|
|
19
|
-
private
|
|
19
|
+
private q;
|
|
20
20
|
spawn(init?: (e: Entity) => void): void;
|
|
21
21
|
despawn(e: Entity): void;
|
|
22
22
|
add<T>(e: Entity, ctor: ComponentCtor<T>, value: T): void;
|
package/lib/ecs/Commands.js
CHANGED
|
@@ -18,8 +18,8 @@ var Commands = /** @class */ (function () {
|
|
|
18
18
|
this.q.push({ k: "remove", e: e, ctor: ctor });
|
|
19
19
|
};
|
|
20
20
|
Commands.prototype.drain = function () {
|
|
21
|
-
var out = this.q
|
|
22
|
-
this.q
|
|
21
|
+
var out = this.q;
|
|
22
|
+
this.q = [];
|
|
23
23
|
return out;
|
|
24
24
|
};
|
|
25
25
|
return Commands;
|
package/lib/ecs/Schedule.js
CHANGED
|
@@ -38,7 +38,16 @@ var Schedule = /** @class */ (function () {
|
|
|
38
38
|
try {
|
|
39
39
|
for (var list_1 = (e_2 = void 0, __values(list)), list_1_1 = list_1.next(); !list_1_1.done; list_1_1 = list_1.next()) {
|
|
40
40
|
var fn = list_1_1.value;
|
|
41
|
-
|
|
41
|
+
try {
|
|
42
|
+
fn(world, dt);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
var sysName = fn.name && fn.name.length > 0 ? fn.name : "<anonymous>";
|
|
46
|
+
var msg = error.message !== undefined && typeof 'string' ? error.message : JSON.stringify(error);
|
|
47
|
+
var e = new Error("[phase=".concat(phase, " system=").concat(sysName, "] ").concat(msg));
|
|
48
|
+
e.cause = error;
|
|
49
|
+
throw e;
|
|
50
|
+
}
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
package/lib/ecs/World.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare class World implements WorldI {
|
|
|
6
6
|
private readonly archByKey;
|
|
7
7
|
private readonly systems;
|
|
8
8
|
private readonly commands;
|
|
9
|
-
private
|
|
9
|
+
private _iterateDepth;
|
|
10
10
|
constructor();
|
|
11
11
|
/** Queue structural changes to apply safely after systems run. */
|
|
12
12
|
cmd(): Commands;
|
|
@@ -40,4 +40,10 @@ export declare class World implements WorldI {
|
|
|
40
40
|
*/
|
|
41
41
|
private _moveEntity;
|
|
42
42
|
private _apply;
|
|
43
|
+
private _formatEntity;
|
|
44
|
+
private _formatCtor;
|
|
45
|
+
/**
|
|
46
|
+
* Throws an error if the entity is not alive
|
|
47
|
+
*/
|
|
48
|
+
private _assertAlive;
|
|
43
49
|
}
|
package/lib/ecs/World.js
CHANGED
|
@@ -51,14 +51,16 @@ var World = /** @class */ (function () {
|
|
|
51
51
|
this.archByKey = new Map();
|
|
52
52
|
this.systems = [];
|
|
53
53
|
this.commands = new Commands_1.Commands();
|
|
54
|
-
this.
|
|
54
|
+
this._iterateDepth = 0;
|
|
55
55
|
// Archetype 0: empty signature
|
|
56
56
|
var archetype0 = new Archetype_1.Archetype(0, []);
|
|
57
57
|
this.archetypes[0] = archetype0;
|
|
58
58
|
this.archByKey.set("", archetype0);
|
|
59
59
|
}
|
|
60
60
|
/** Queue structural changes to apply safely after systems run. */
|
|
61
|
-
World.prototype.cmd = function () {
|
|
61
|
+
World.prototype.cmd = function () {
|
|
62
|
+
return this.commands;
|
|
63
|
+
};
|
|
62
64
|
World.prototype.addSystem = function (fn) {
|
|
63
65
|
this.systems.push(fn);
|
|
64
66
|
return this;
|
|
@@ -70,7 +72,7 @@ var World = /** @class */ (function () {
|
|
|
70
72
|
*/
|
|
71
73
|
World.prototype.update = function (dt) {
|
|
72
74
|
var e_1, _a;
|
|
73
|
-
this.
|
|
75
|
+
this._iterateDepth++;
|
|
74
76
|
try {
|
|
75
77
|
try {
|
|
76
78
|
for (var _b = __values(this.systems), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
@@ -87,12 +89,13 @@ var World = /** @class */ (function () {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
finally {
|
|
90
|
-
this.
|
|
92
|
+
this._iterateDepth--;
|
|
91
93
|
this.flush();
|
|
92
94
|
}
|
|
93
95
|
};
|
|
94
96
|
World.prototype.flush = function () {
|
|
95
97
|
var e_2, _a;
|
|
98
|
+
this._ensureNotIterating("flush");
|
|
96
99
|
var ops = this.commands.drain();
|
|
97
100
|
try {
|
|
98
101
|
for (var ops_1 = __values(ops), ops_1_1 = ops_1.next(); !ops_1_1.done; ops_1_1 = ops_1.next()) {
|
|
@@ -123,8 +126,7 @@ var World = /** @class */ (function () {
|
|
|
123
126
|
return this.entities.isAlive(e);
|
|
124
127
|
};
|
|
125
128
|
World.prototype.despawn = function (e) {
|
|
126
|
-
|
|
127
|
-
return;
|
|
129
|
+
this._assertAlive(e, 'despawn');
|
|
128
130
|
this._ensureNotIterating("despawn");
|
|
129
131
|
this._removeFromArchetype(e);
|
|
130
132
|
this.entities.kill(e);
|
|
@@ -149,18 +151,15 @@ var World = /** @class */ (function () {
|
|
|
149
151
|
return a.column(tid)[meta.row];
|
|
150
152
|
};
|
|
151
153
|
World.prototype.set = function (e, ctor, value) {
|
|
152
|
-
var meta = this.
|
|
153
|
-
if (!meta || !meta.alive || meta.gen !== e.gen)
|
|
154
|
-
return;
|
|
154
|
+
var meta = this._assertAlive(e, "set(".concat(this._formatCtor(ctor), ")"));
|
|
155
155
|
var tid = (0, TypeRegistry_1.typeId)(ctor);
|
|
156
156
|
var a = this.archetypes[meta.arch];
|
|
157
157
|
if (!a.has(tid))
|
|
158
|
-
throw new Error("set() requires component to exist; use add()");
|
|
158
|
+
throw new Error("set(".concat(this._formatCtor(ctor), ") requires component to exist on ").concat(this._formatEntity(e), "; use add()"));
|
|
159
159
|
a.column(tid)[meta.row] = value;
|
|
160
160
|
};
|
|
161
161
|
World.prototype.add = function (e, ctor, value) {
|
|
162
|
-
|
|
163
|
-
return;
|
|
162
|
+
this._assertAlive(e, "add(".concat(this._formatCtor(ctor), ")"));
|
|
164
163
|
this._ensureNotIterating("add");
|
|
165
164
|
var tid = (0, TypeRegistry_1.typeId)(ctor);
|
|
166
165
|
var srcMeta = this.entities.meta[e.id];
|
|
@@ -179,8 +178,7 @@ var World = /** @class */ (function () {
|
|
|
179
178
|
});
|
|
180
179
|
};
|
|
181
180
|
World.prototype.remove = function (e, ctor) {
|
|
182
|
-
|
|
183
|
-
return;
|
|
181
|
+
this._assertAlive(e, "remove(".concat(this._formatCtor(ctor), ")"));
|
|
184
182
|
this._ensureNotIterating("remove");
|
|
185
183
|
var tid = (0, TypeRegistry_1.typeId)(ctor);
|
|
186
184
|
var srcMeta = this.entities.meta[e.id];
|
|
@@ -205,79 +203,79 @@ var World = /** @class */ (function () {
|
|
|
205
203
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
206
204
|
ctors[_i] = arguments[_i];
|
|
207
205
|
}
|
|
208
|
-
|
|
209
|
-
var
|
|
206
|
+
// Preserve caller order for (c1,c2,c3,...) mapping.
|
|
207
|
+
var requested = new Array(ctors.length);
|
|
208
|
+
for (var i = 0; i < ctors.length; i++)
|
|
209
|
+
requested[i] = (0, TypeRegistry_1.typeId)(ctors[i]);
|
|
210
|
+
// Same ids, but sorted + deduped for signatureHasAll().
|
|
211
|
+
var needSorted = requested.slice();
|
|
212
|
+
needSorted.sort(function (a, b) { return a - b; });
|
|
213
|
+
var w = 0;
|
|
214
|
+
for (var i = 0; i < needSorted.length; i++) {
|
|
215
|
+
var v = needSorted[i];
|
|
216
|
+
if (i === 0 || v !== needSorted[w - 1])
|
|
217
|
+
needSorted[w++] = v;
|
|
218
|
+
}
|
|
219
|
+
needSorted.length = w;
|
|
210
220
|
function gen(world) {
|
|
211
|
-
var
|
|
221
|
+
var _a, _b, a, cols, i, row, e, out, i, e_3_1;
|
|
212
222
|
var e_3, _c;
|
|
213
223
|
return __generator(this, function (_d) {
|
|
214
224
|
switch (_d.label) {
|
|
215
225
|
case 0:
|
|
216
|
-
world.
|
|
226
|
+
world._iterateDepth++;
|
|
217
227
|
_d.label = 1;
|
|
218
228
|
case 1:
|
|
219
|
-
_d.trys.push([1, ,
|
|
220
|
-
_loop_1 = function (a) {
|
|
221
|
-
var cols, row, e, out, i;
|
|
222
|
-
return __generator(this, function (_e) {
|
|
223
|
-
switch (_e.label) {
|
|
224
|
-
case 0:
|
|
225
|
-
if (!a)
|
|
226
|
-
return [2 /*return*/, "continue"];
|
|
227
|
-
// if (!signatureHasAll(a.sig, need)) continue;
|
|
228
|
-
if (!(0, Signature_1.signatureHasAll)(a.sig, needSorted))
|
|
229
|
-
return [2 /*return*/, "continue"];
|
|
230
|
-
cols = requested.map(function (t) { return a.column(t); });
|
|
231
|
-
row = 0;
|
|
232
|
-
_e.label = 1;
|
|
233
|
-
case 1:
|
|
234
|
-
if (!(row < a.entities.length)) return [3 /*break*/, 4];
|
|
235
|
-
e = a.entities[row];
|
|
236
|
-
out = { e: e };
|
|
237
|
-
for (i = 0; i < cols.length; i++)
|
|
238
|
-
out["c".concat(i + 1)] = cols[i][row];
|
|
239
|
-
return [4 /*yield*/, out];
|
|
240
|
-
case 2:
|
|
241
|
-
_e.sent();
|
|
242
|
-
_e.label = 3;
|
|
243
|
-
case 3:
|
|
244
|
-
row++;
|
|
245
|
-
return [3 /*break*/, 1];
|
|
246
|
-
case 4: return [2 /*return*/];
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
};
|
|
229
|
+
_d.trys.push([1, , 12, 13]);
|
|
250
230
|
_d.label = 2;
|
|
251
231
|
case 2:
|
|
252
|
-
_d.trys.push([2,
|
|
232
|
+
_d.trys.push([2, 9, 10, 11]);
|
|
253
233
|
_a = __values(world.archetypes), _b = _a.next();
|
|
254
234
|
_d.label = 3;
|
|
255
235
|
case 3:
|
|
256
|
-
if (!!_b.done) return [3 /*break*/,
|
|
236
|
+
if (!!_b.done) return [3 /*break*/, 8];
|
|
257
237
|
a = _b.value;
|
|
258
|
-
|
|
238
|
+
if (!a)
|
|
239
|
+
return [3 /*break*/, 7];
|
|
240
|
+
if (!(0, Signature_1.signatureHasAll)(a.sig, needSorted))
|
|
241
|
+
return [3 /*break*/, 7];
|
|
242
|
+
cols = new Array(requested.length);
|
|
243
|
+
for (i = 0; i < requested.length; i++)
|
|
244
|
+
cols[i] = a.column(requested[i]);
|
|
245
|
+
row = 0;
|
|
246
|
+
_d.label = 4;
|
|
259
247
|
case 4:
|
|
260
|
-
|
|
261
|
-
|
|
248
|
+
if (!(row < a.entities.length)) return [3 /*break*/, 7];
|
|
249
|
+
e = a.entities[row];
|
|
250
|
+
out = { e: e };
|
|
251
|
+
for (i = 0; i < cols.length; i++)
|
|
252
|
+
out["c".concat(i + 1)] = cols[i][row];
|
|
253
|
+
return [4 /*yield*/, out];
|
|
262
254
|
case 5:
|
|
255
|
+
_d.sent();
|
|
256
|
+
_d.label = 6;
|
|
257
|
+
case 6:
|
|
258
|
+
row++;
|
|
259
|
+
return [3 /*break*/, 4];
|
|
260
|
+
case 7:
|
|
263
261
|
_b = _a.next();
|
|
264
262
|
return [3 /*break*/, 3];
|
|
265
|
-
case
|
|
266
|
-
case
|
|
263
|
+
case 8: return [3 /*break*/, 11];
|
|
264
|
+
case 9:
|
|
267
265
|
e_3_1 = _d.sent();
|
|
268
266
|
e_3 = { error: e_3_1 };
|
|
269
|
-
return [3 /*break*/,
|
|
270
|
-
case
|
|
267
|
+
return [3 /*break*/, 11];
|
|
268
|
+
case 10:
|
|
271
269
|
try {
|
|
272
270
|
if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
|
|
273
271
|
}
|
|
274
272
|
finally { if (e_3) throw e_3.error; }
|
|
275
273
|
return [7 /*endfinally*/];
|
|
276
|
-
case
|
|
277
|
-
case
|
|
278
|
-
world.
|
|
274
|
+
case 11: return [3 /*break*/, 13];
|
|
275
|
+
case 12:
|
|
276
|
+
world._iterateDepth--;
|
|
279
277
|
return [7 /*endfinally*/];
|
|
280
|
-
case
|
|
278
|
+
case 13: return [2 /*return*/];
|
|
281
279
|
}
|
|
282
280
|
});
|
|
283
281
|
}
|
|
@@ -286,7 +284,7 @@ var World = /** @class */ (function () {
|
|
|
286
284
|
//#endregion
|
|
287
285
|
//#region ---------- Internals ----------
|
|
288
286
|
World.prototype._ensureNotIterating = function (op) {
|
|
289
|
-
if (this.
|
|
287
|
+
if (this._iterateDepth > 0) {
|
|
290
288
|
throw new Error("Cannot do structural change (".concat(op, ") while iterating. Use world.cmd() and flush at end of frame."));
|
|
291
289
|
}
|
|
292
290
|
};
|
|
@@ -360,6 +358,24 @@ var World = /** @class */ (function () {
|
|
|
360
358
|
return this.remove(op.e, op.ctor);
|
|
361
359
|
}
|
|
362
360
|
};
|
|
361
|
+
World.prototype._formatEntity = function (e) {
|
|
362
|
+
return "e#".concat(e.id, "@").concat(e.gen);
|
|
363
|
+
};
|
|
364
|
+
World.prototype._formatCtor = function (ctor) {
|
|
365
|
+
var n = ctor === null || ctor === void 0 ? void 0 : ctor.name;
|
|
366
|
+
return n && n.length > 0 ? n : "<token>";
|
|
367
|
+
};
|
|
368
|
+
/**
|
|
369
|
+
* Throws an error if the entity is not alive
|
|
370
|
+
*/
|
|
371
|
+
World.prototype._assertAlive = function (e, op) {
|
|
372
|
+
var _a, _b;
|
|
373
|
+
var meta = this.entities.meta[e.id];
|
|
374
|
+
if (!this.entities.isAlive(e)) {
|
|
375
|
+
throw new Error("".concat(op, " on stale entity ").concat(this._formatEntity(e), " (alive=").concat((_a = meta === null || meta === void 0 ? void 0 : meta.alive) !== null && _a !== void 0 ? _a : false, ", gen=").concat((_b = meta === null || meta === void 0 ? void 0 : meta.gen) !== null && _b !== void 0 ? _b : "n/a", ")"));
|
|
376
|
+
}
|
|
377
|
+
return meta;
|
|
378
|
+
};
|
|
363
379
|
return World;
|
|
364
380
|
}());
|
|
365
381
|
exports.World = World;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archetype-ecs-lib",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "An Archetype Entity Component System (AECS)",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,10 +9,21 @@
|
|
|
9
9
|
"lint": "tslint -c tslint.json src/**/*.ts",
|
|
10
10
|
"reload-packages": "rm -Rf node_modules && npm cache clean --force && npm ci"
|
|
11
11
|
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/PirateJL/archetype-ecs-lib.git"
|
|
15
|
+
},
|
|
12
16
|
"keywords": [
|
|
13
|
-
"
|
|
17
|
+
"game",
|
|
18
|
+
"gamedev",
|
|
19
|
+
"threejs",
|
|
20
|
+
"game-development",
|
|
14
21
|
"ecs",
|
|
15
|
-
"
|
|
22
|
+
"entity-component-system",
|
|
23
|
+
"gameloop",
|
|
24
|
+
"ecs-framework",
|
|
25
|
+
"ecs-archetype",
|
|
26
|
+
"typescript"
|
|
16
27
|
],
|
|
17
28
|
"author": {
|
|
18
29
|
"name": "PirateJL"
|