archetype-ecs 1.2.0 → 2.0.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 +119 -40
- package/bench/allocations-1m.js +1 -1
- package/bench/multi-ecs-bench.js +1 -1
- package/bench/typed-vs-bitecs-1m.js +1 -1
- package/bench/typed-vs-untyped.js +81 -81
- package/bench/vs-bitecs.js +31 -56
- package/dist/src/ComponentRegistry.d.ts +13 -0
- package/dist/src/ComponentRegistry.js +29 -0
- package/dist/src/EntityManager.d.ts +52 -0
- package/dist/src/EntityManager.js +787 -0
- package/dist/src/Profiler.d.ts +12 -0
- package/dist/src/Profiler.js +38 -0
- package/dist/src/System.d.ts +37 -0
- package/dist/src/System.js +139 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +29 -0
- package/dist/tests/EntityManager.test.d.ts +1 -0
- package/dist/tests/EntityManager.test.js +499 -0
- package/dist/tests/System.test.d.ts +1 -0
- package/dist/tests/System.test.js +630 -0
- package/dist/tests/types.d.ts +1 -0
- package/dist/tests/types.js +129 -0
- package/package.json +8 -7
- package/src/ComponentRegistry.ts +45 -0
- package/src/EntityManager.ts +909 -0
- package/src/{Profiler.js → Profiler.ts} +18 -5
- package/src/System.ts +201 -0
- package/src/index.ts +38 -0
- package/tests/{EntityManager.test.js → EntityManager.test.ts} +182 -57
- package/tests/System.test.ts +546 -0
- package/tests/types.ts +69 -68
- package/tsconfig.json +8 -5
- package/.claude/settings.local.json +0 -32
- package/src/ComponentRegistry.js +0 -21
- package/src/EntityManager.js +0 -462
- package/src/index.d.ts +0 -111
- package/src/index.js +0 -37
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
36
|
+
import assert from 'node:assert/strict';
|
|
37
|
+
import { createEntityManager } from '../src/EntityManager.js';
|
|
38
|
+
import { createSystem, createSystems, System, OnAdded, OnRemoved } from '../src/System.js';
|
|
39
|
+
import { component } from '../src/index.js';
|
|
40
|
+
// ── Components (shared across all suites) ────────────────
|
|
41
|
+
const Position = component('Position', 'f32', ['x', 'y']);
|
|
42
|
+
const Velocity = component('Velocity', 'f32', ['vx', 'vy']);
|
|
43
|
+
const Health = component('Health', 'f32', ['hp']);
|
|
44
|
+
const Tag = component('Tag');
|
|
45
|
+
// ── Functional API ───────────────────────────────────────
|
|
46
|
+
describe('createSystem (functional)', () => {
|
|
47
|
+
let em;
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
em = createEntityManager();
|
|
50
|
+
});
|
|
51
|
+
it('onAdded fires callback after flush + run', () => {
|
|
52
|
+
const collected = [];
|
|
53
|
+
const sys = createSystem(em, (s) => {
|
|
54
|
+
s.onAdded(Health, (id) => collected.push(id));
|
|
55
|
+
});
|
|
56
|
+
const e1 = em.createEntity();
|
|
57
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
58
|
+
em.flushHooks();
|
|
59
|
+
sys();
|
|
60
|
+
assert.deepEqual(collected, [e1]);
|
|
61
|
+
sys.dispose();
|
|
62
|
+
});
|
|
63
|
+
it('onAdded with multiple types only fires when entity has ALL types', () => {
|
|
64
|
+
const collected = [];
|
|
65
|
+
const sys = createSystem(em, (s) => {
|
|
66
|
+
s.onAdded(Health, Position, (id) => collected.push(id));
|
|
67
|
+
});
|
|
68
|
+
const e1 = em.createEntity();
|
|
69
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
70
|
+
em.flushHooks();
|
|
71
|
+
sys();
|
|
72
|
+
assert.deepEqual(collected, []);
|
|
73
|
+
em.addComponent(e1, Position, { x: 0, y: 0 });
|
|
74
|
+
em.flushHooks();
|
|
75
|
+
sys();
|
|
76
|
+
assert.deepEqual(collected, [e1]);
|
|
77
|
+
sys.dispose();
|
|
78
|
+
});
|
|
79
|
+
it('onAdded deduplicates when multiple types trigger for same entity', () => {
|
|
80
|
+
const collected = [];
|
|
81
|
+
const sys = createSystem(em, (s) => {
|
|
82
|
+
s.onAdded(Health, Position, (id) => collected.push(id));
|
|
83
|
+
});
|
|
84
|
+
const e1 = em.createEntityWith(Health, { hp: 50 }, Position, { x: 0, y: 0 });
|
|
85
|
+
em.flushHooks();
|
|
86
|
+
sys();
|
|
87
|
+
assert.deepEqual(collected, [e1]);
|
|
88
|
+
sys.dispose();
|
|
89
|
+
});
|
|
90
|
+
it('onRemoved fires callback after flush + run', () => {
|
|
91
|
+
const collected = [];
|
|
92
|
+
const sys = createSystem(em, (s) => {
|
|
93
|
+
s.onRemoved(Health, (id) => collected.push(id));
|
|
94
|
+
});
|
|
95
|
+
const e1 = em.createEntity();
|
|
96
|
+
em.addComponent(e1, Health, { hp: 50 });
|
|
97
|
+
em.flushHooks();
|
|
98
|
+
sys();
|
|
99
|
+
em.removeComponent(e1, Health);
|
|
100
|
+
em.flushHooks();
|
|
101
|
+
sys();
|
|
102
|
+
assert.deepEqual(collected, [e1]);
|
|
103
|
+
sys.dispose();
|
|
104
|
+
});
|
|
105
|
+
it('onRemoved with multiple types deduplicates', () => {
|
|
106
|
+
const collected = [];
|
|
107
|
+
const sys = createSystem(em, (s) => {
|
|
108
|
+
s.onRemoved(Health, Position, (id) => collected.push(id));
|
|
109
|
+
});
|
|
110
|
+
const e1 = em.createEntityWith(Health, { hp: 50 }, Position, { x: 0, y: 0 });
|
|
111
|
+
em.flushHooks();
|
|
112
|
+
sys();
|
|
113
|
+
em.removeComponent(e1, Health);
|
|
114
|
+
em.removeComponent(e1, Position);
|
|
115
|
+
em.flushHooks();
|
|
116
|
+
sys();
|
|
117
|
+
assert.deepEqual(collected, [e1]);
|
|
118
|
+
sys.dispose();
|
|
119
|
+
});
|
|
120
|
+
it('buffers cleared after sys() — no double processing', () => {
|
|
121
|
+
const collected = [];
|
|
122
|
+
const sys = createSystem(em, (s) => {
|
|
123
|
+
s.onAdded(Health, (id) => collected.push(id));
|
|
124
|
+
});
|
|
125
|
+
const e1 = em.createEntity();
|
|
126
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
127
|
+
em.flushHooks();
|
|
128
|
+
sys();
|
|
129
|
+
assert.deepEqual(collected, [e1]);
|
|
130
|
+
collected.length = 0;
|
|
131
|
+
sys();
|
|
132
|
+
assert.deepEqual(collected, []);
|
|
133
|
+
sys.dispose();
|
|
134
|
+
});
|
|
135
|
+
it('dispose() unsubscribes hooks', () => {
|
|
136
|
+
const collected = [];
|
|
137
|
+
const sys = createSystem(em, (s) => {
|
|
138
|
+
s.onAdded(Health, (id) => collected.push(id));
|
|
139
|
+
});
|
|
140
|
+
sys.dispose();
|
|
141
|
+
const e1 = em.createEntity();
|
|
142
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
143
|
+
em.flushHooks();
|
|
144
|
+
sys();
|
|
145
|
+
assert.deepEqual(collected, []);
|
|
146
|
+
});
|
|
147
|
+
it('forEach wraps em.forEach correctly (include + exclude)', () => {
|
|
148
|
+
const e1 = em.createEntity();
|
|
149
|
+
em.addComponent(e1, Position, { x: 1, y: 2 });
|
|
150
|
+
em.addComponent(e1, Velocity, { vx: 3, vy: 4 });
|
|
151
|
+
const e2 = em.createEntity();
|
|
152
|
+
em.addComponent(e2, Position, { x: 5, y: 6 });
|
|
153
|
+
em.addComponent(e2, Tag);
|
|
154
|
+
let count = 0;
|
|
155
|
+
const sys = createSystem(em, (s) => {
|
|
156
|
+
return () => {
|
|
157
|
+
s.forEach([Position], (view) => { count += view.count; }, [Tag]);
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
sys();
|
|
161
|
+
assert.equal(count, 1);
|
|
162
|
+
sys.dispose();
|
|
163
|
+
});
|
|
164
|
+
it('hooks-only system (no tick) works', () => {
|
|
165
|
+
const added = [];
|
|
166
|
+
const sys = createSystem(em, (s) => {
|
|
167
|
+
s.onAdded(Tag, (id) => added.push(id));
|
|
168
|
+
});
|
|
169
|
+
const e1 = em.createEntity();
|
|
170
|
+
em.addComponent(e1, Tag);
|
|
171
|
+
em.flushHooks();
|
|
172
|
+
sys();
|
|
173
|
+
assert.deepEqual(added, [e1]);
|
|
174
|
+
sys.dispose();
|
|
175
|
+
});
|
|
176
|
+
it('query-only system (no hooks) works', () => {
|
|
177
|
+
const e1 = em.createEntity();
|
|
178
|
+
em.addComponent(e1, Position, { x: 10, y: 20 });
|
|
179
|
+
em.addComponent(e1, Velocity, { vx: 1, vy: 2 });
|
|
180
|
+
let totalCount = 0;
|
|
181
|
+
const sys = createSystem(em, (s) => {
|
|
182
|
+
return () => {
|
|
183
|
+
s.forEach([Position, Velocity], (view) => { totalCount += view.count; });
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
sys();
|
|
187
|
+
assert.equal(totalCount, 1);
|
|
188
|
+
sys.dispose();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
// ── Decorator-based class API ────────────────────────────
|
|
192
|
+
describe('System (class + decorators)', () => {
|
|
193
|
+
let em;
|
|
194
|
+
beforeEach(() => {
|
|
195
|
+
em = createEntityManager();
|
|
196
|
+
});
|
|
197
|
+
it('@OnAdded fires decorated method after flush + run', () => {
|
|
198
|
+
const collected = [];
|
|
199
|
+
let TestSys = (() => {
|
|
200
|
+
let _classSuper = System;
|
|
201
|
+
let _instanceExtraInitializers = [];
|
|
202
|
+
let _handleAdd_decorators;
|
|
203
|
+
return class TestSys extends _classSuper {
|
|
204
|
+
static {
|
|
205
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
206
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
207
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
208
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
209
|
+
}
|
|
210
|
+
handleAdd(id) { collected.push(id); }
|
|
211
|
+
constructor() {
|
|
212
|
+
super(...arguments);
|
|
213
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
})();
|
|
217
|
+
const sys = new TestSys(em);
|
|
218
|
+
const e1 = em.createEntity();
|
|
219
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
220
|
+
em.flushHooks();
|
|
221
|
+
sys.run();
|
|
222
|
+
assert.deepEqual(collected, [e1]);
|
|
223
|
+
sys.dispose();
|
|
224
|
+
});
|
|
225
|
+
it('@OnAdded with multiple types only fires when entity has ALL types', () => {
|
|
226
|
+
const collected = [];
|
|
227
|
+
let TestSys = (() => {
|
|
228
|
+
let _classSuper = System;
|
|
229
|
+
let _instanceExtraInitializers = [];
|
|
230
|
+
let _handleAdd_decorators;
|
|
231
|
+
return class TestSys extends _classSuper {
|
|
232
|
+
static {
|
|
233
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
234
|
+
_handleAdd_decorators = [OnAdded(Health, Position)];
|
|
235
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
236
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
237
|
+
}
|
|
238
|
+
handleAdd(id) { collected.push(id); }
|
|
239
|
+
constructor() {
|
|
240
|
+
super(...arguments);
|
|
241
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
})();
|
|
245
|
+
const sys = new TestSys(em);
|
|
246
|
+
// Only Health — should NOT trigger
|
|
247
|
+
const e1 = em.createEntity();
|
|
248
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
249
|
+
em.flushHooks();
|
|
250
|
+
sys.run();
|
|
251
|
+
assert.deepEqual(collected, []);
|
|
252
|
+
// Add Position — now matches
|
|
253
|
+
em.addComponent(e1, Position, { x: 0, y: 0 });
|
|
254
|
+
em.flushHooks();
|
|
255
|
+
sys.run();
|
|
256
|
+
assert.deepEqual(collected, [e1]);
|
|
257
|
+
sys.dispose();
|
|
258
|
+
});
|
|
259
|
+
it('@OnAdded deduplicates with createEntityWith', () => {
|
|
260
|
+
const collected = [];
|
|
261
|
+
let TestSys = (() => {
|
|
262
|
+
let _classSuper = System;
|
|
263
|
+
let _instanceExtraInitializers = [];
|
|
264
|
+
let _handleAdd_decorators;
|
|
265
|
+
return class TestSys extends _classSuper {
|
|
266
|
+
static {
|
|
267
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
268
|
+
_handleAdd_decorators = [OnAdded(Health, Position)];
|
|
269
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
270
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
271
|
+
}
|
|
272
|
+
handleAdd(id) { collected.push(id); }
|
|
273
|
+
constructor() {
|
|
274
|
+
super(...arguments);
|
|
275
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
})();
|
|
279
|
+
const sys = new TestSys(em);
|
|
280
|
+
const e1 = em.createEntityWith(Health, { hp: 50 }, Position, { x: 0, y: 0 });
|
|
281
|
+
em.flushHooks();
|
|
282
|
+
sys.run();
|
|
283
|
+
assert.deepEqual(collected, [e1]);
|
|
284
|
+
sys.dispose();
|
|
285
|
+
});
|
|
286
|
+
it('@OnRemoved fires decorated method after flush + run', () => {
|
|
287
|
+
const collected = [];
|
|
288
|
+
let TestSys = (() => {
|
|
289
|
+
let _classSuper = System;
|
|
290
|
+
let _instanceExtraInitializers = [];
|
|
291
|
+
let _handleRemove_decorators;
|
|
292
|
+
return class TestSys extends _classSuper {
|
|
293
|
+
static {
|
|
294
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
295
|
+
_handleRemove_decorators = [OnRemoved(Health)];
|
|
296
|
+
__esDecorate(this, null, _handleRemove_decorators, { kind: "method", name: "handleRemove", static: false, private: false, access: { has: obj => "handleRemove" in obj, get: obj => obj.handleRemove }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
297
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
298
|
+
}
|
|
299
|
+
handleRemove(id) { collected.push(id); }
|
|
300
|
+
constructor() {
|
|
301
|
+
super(...arguments);
|
|
302
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
})();
|
|
306
|
+
const sys = new TestSys(em);
|
|
307
|
+
const e1 = em.createEntity();
|
|
308
|
+
em.addComponent(e1, Health, { hp: 50 });
|
|
309
|
+
em.flushHooks();
|
|
310
|
+
sys.run();
|
|
311
|
+
em.removeComponent(e1, Health);
|
|
312
|
+
em.flushHooks();
|
|
313
|
+
sys.run();
|
|
314
|
+
assert.deepEqual(collected, [e1]);
|
|
315
|
+
sys.dispose();
|
|
316
|
+
});
|
|
317
|
+
it('@OnRemoved fires on destroyEntity for all component types', () => {
|
|
318
|
+
const removedHealth = [];
|
|
319
|
+
const removedPos = [];
|
|
320
|
+
let TestSys = (() => {
|
|
321
|
+
let _classSuper = System;
|
|
322
|
+
let _instanceExtraInitializers = [];
|
|
323
|
+
let _handleHealth_decorators;
|
|
324
|
+
let _handlePos_decorators;
|
|
325
|
+
return class TestSys extends _classSuper {
|
|
326
|
+
static {
|
|
327
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
328
|
+
_handleHealth_decorators = [OnRemoved(Health)];
|
|
329
|
+
_handlePos_decorators = [OnRemoved(Position)];
|
|
330
|
+
__esDecorate(this, null, _handleHealth_decorators, { kind: "method", name: "handleHealth", static: false, private: false, access: { has: obj => "handleHealth" in obj, get: obj => obj.handleHealth }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
331
|
+
__esDecorate(this, null, _handlePos_decorators, { kind: "method", name: "handlePos", static: false, private: false, access: { has: obj => "handlePos" in obj, get: obj => obj.handlePos }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
332
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
333
|
+
}
|
|
334
|
+
handleHealth(id) { removedHealth.push(id); }
|
|
335
|
+
handlePos(id) { removedPos.push(id); }
|
|
336
|
+
constructor() {
|
|
337
|
+
super(...arguments);
|
|
338
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
})();
|
|
342
|
+
const sys = new TestSys(em);
|
|
343
|
+
const e1 = em.createEntityWith(Health, { hp: 100 }, Position, { x: 0, y: 0 });
|
|
344
|
+
em.flushHooks();
|
|
345
|
+
sys.run();
|
|
346
|
+
em.destroyEntity(e1);
|
|
347
|
+
em.flushHooks();
|
|
348
|
+
sys.run();
|
|
349
|
+
assert.deepEqual(removedHealth, [e1]);
|
|
350
|
+
assert.deepEqual(removedPos, [e1]);
|
|
351
|
+
sys.dispose();
|
|
352
|
+
});
|
|
353
|
+
it('buffers cleared after run() — no double processing', () => {
|
|
354
|
+
const collected = [];
|
|
355
|
+
let TestSys = (() => {
|
|
356
|
+
let _classSuper = System;
|
|
357
|
+
let _instanceExtraInitializers = [];
|
|
358
|
+
let _handleAdd_decorators;
|
|
359
|
+
return class TestSys extends _classSuper {
|
|
360
|
+
static {
|
|
361
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
362
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
363
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
364
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
365
|
+
}
|
|
366
|
+
handleAdd(id) { collected.push(id); }
|
|
367
|
+
constructor() {
|
|
368
|
+
super(...arguments);
|
|
369
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
})();
|
|
373
|
+
const sys = new TestSys(em);
|
|
374
|
+
const e1 = em.createEntity();
|
|
375
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
376
|
+
em.flushHooks();
|
|
377
|
+
sys.run();
|
|
378
|
+
assert.deepEqual(collected, [e1]);
|
|
379
|
+
collected.length = 0;
|
|
380
|
+
sys.run();
|
|
381
|
+
assert.deepEqual(collected, []);
|
|
382
|
+
sys.dispose();
|
|
383
|
+
});
|
|
384
|
+
it('dispose() unsubscribes hooks', () => {
|
|
385
|
+
const collected = [];
|
|
386
|
+
let TestSys = (() => {
|
|
387
|
+
let _classSuper = System;
|
|
388
|
+
let _instanceExtraInitializers = [];
|
|
389
|
+
let _handleAdd_decorators;
|
|
390
|
+
return class TestSys extends _classSuper {
|
|
391
|
+
static {
|
|
392
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
393
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
394
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
395
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
396
|
+
}
|
|
397
|
+
handleAdd(id) { collected.push(id); }
|
|
398
|
+
constructor() {
|
|
399
|
+
super(...arguments);
|
|
400
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
})();
|
|
404
|
+
const sys = new TestSys(em);
|
|
405
|
+
sys.dispose();
|
|
406
|
+
const e1 = em.createEntity();
|
|
407
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
408
|
+
em.flushHooks();
|
|
409
|
+
sys.run();
|
|
410
|
+
assert.deepEqual(collected, []);
|
|
411
|
+
});
|
|
412
|
+
it('tick() is called after hook callbacks', () => {
|
|
413
|
+
const order = [];
|
|
414
|
+
let TestSys = (() => {
|
|
415
|
+
let _classSuper = System;
|
|
416
|
+
let _instanceExtraInitializers = [];
|
|
417
|
+
let _handleAdd_decorators;
|
|
418
|
+
return class TestSys extends _classSuper {
|
|
419
|
+
static {
|
|
420
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
421
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
422
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
423
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
424
|
+
}
|
|
425
|
+
handleAdd(_id) { order.push('hook'); }
|
|
426
|
+
tick() { order.push('tick'); }
|
|
427
|
+
constructor() {
|
|
428
|
+
super(...arguments);
|
|
429
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
})();
|
|
433
|
+
const sys = new TestSys(em);
|
|
434
|
+
const e1 = em.createEntity();
|
|
435
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
436
|
+
em.flushHooks();
|
|
437
|
+
sys.run();
|
|
438
|
+
assert.deepEqual(order, ['hook', 'tick']);
|
|
439
|
+
sys.dispose();
|
|
440
|
+
});
|
|
441
|
+
it('forEach available on System instance', () => {
|
|
442
|
+
const e1 = em.createEntity();
|
|
443
|
+
em.addComponent(e1, Position, { x: 1, y: 2 });
|
|
444
|
+
em.addComponent(e1, Velocity, { vx: 3, vy: 4 });
|
|
445
|
+
let count = 0;
|
|
446
|
+
class TestSys extends System {
|
|
447
|
+
tick() {
|
|
448
|
+
this.forEach([Position, Velocity], (view) => { count += view.count; });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const sys = new TestSys(em);
|
|
452
|
+
sys.run();
|
|
453
|
+
assert.equal(count, 1);
|
|
454
|
+
sys.dispose();
|
|
455
|
+
});
|
|
456
|
+
it('multiple decorated methods on same class', () => {
|
|
457
|
+
const added = [];
|
|
458
|
+
const removed = [];
|
|
459
|
+
let TestSys = (() => {
|
|
460
|
+
let _classSuper = System;
|
|
461
|
+
let _instanceExtraInitializers = [];
|
|
462
|
+
let _handleAdd_decorators;
|
|
463
|
+
let _handleRemove_decorators;
|
|
464
|
+
return class TestSys extends _classSuper {
|
|
465
|
+
static {
|
|
466
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
467
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
468
|
+
_handleRemove_decorators = [OnRemoved(Health)];
|
|
469
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
470
|
+
__esDecorate(this, null, _handleRemove_decorators, { kind: "method", name: "handleRemove", static: false, private: false, access: { has: obj => "handleRemove" in obj, get: obj => obj.handleRemove }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
471
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
472
|
+
}
|
|
473
|
+
handleAdd(id) { added.push(id); }
|
|
474
|
+
handleRemove(id) { removed.push(id); }
|
|
475
|
+
constructor() {
|
|
476
|
+
super(...arguments);
|
|
477
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
})();
|
|
481
|
+
const sys = new TestSys(em);
|
|
482
|
+
const e1 = em.createEntity();
|
|
483
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
484
|
+
em.flushHooks();
|
|
485
|
+
sys.run();
|
|
486
|
+
assert.deepEqual(added, [e1]);
|
|
487
|
+
assert.deepEqual(removed, []);
|
|
488
|
+
em.removeComponent(e1, Health);
|
|
489
|
+
em.flushHooks();
|
|
490
|
+
sys.run();
|
|
491
|
+
assert.deepEqual(removed, [e1]);
|
|
492
|
+
sys.dispose();
|
|
493
|
+
});
|
|
494
|
+
it('multiple instances have independent buffers', () => {
|
|
495
|
+
const collectedA = [];
|
|
496
|
+
const collectedB = [];
|
|
497
|
+
let TestSys = (() => {
|
|
498
|
+
let _classSuper = System;
|
|
499
|
+
let _instanceExtraInitializers = [];
|
|
500
|
+
let _handleAdd_decorators;
|
|
501
|
+
return class TestSys extends _classSuper {
|
|
502
|
+
static {
|
|
503
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
504
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
505
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
506
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
507
|
+
}
|
|
508
|
+
out = __runInitializers(this, _instanceExtraInitializers);
|
|
509
|
+
constructor(em, out) {
|
|
510
|
+
super(em);
|
|
511
|
+
this.out = out;
|
|
512
|
+
}
|
|
513
|
+
handleAdd(id) { this.out.push(id); }
|
|
514
|
+
};
|
|
515
|
+
})();
|
|
516
|
+
const sysA = new TestSys(em, collectedA);
|
|
517
|
+
const sysB = new TestSys(em, collectedB);
|
|
518
|
+
const e1 = em.createEntity();
|
|
519
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
520
|
+
em.flushHooks();
|
|
521
|
+
sysA.run();
|
|
522
|
+
sysB.run();
|
|
523
|
+
assert.deepEqual(collectedA, [e1]);
|
|
524
|
+
assert.deepEqual(collectedB, [e1]);
|
|
525
|
+
sysA.dispose();
|
|
526
|
+
sysB.dispose();
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
// ── createSystems (activator) ────────────────────────────
|
|
530
|
+
describe('createSystems', () => {
|
|
531
|
+
let em;
|
|
532
|
+
beforeEach(() => {
|
|
533
|
+
em = createEntityManager();
|
|
534
|
+
});
|
|
535
|
+
it('runs functional systems in order', () => {
|
|
536
|
+
const order = [];
|
|
537
|
+
function SysA() { return () => { order.push('A'); }; }
|
|
538
|
+
function SysB() { return () => { order.push('B'); }; }
|
|
539
|
+
const pipeline = createSystems(em, [SysA, SysB]);
|
|
540
|
+
pipeline();
|
|
541
|
+
assert.deepEqual(order, ['A', 'B']);
|
|
542
|
+
pipeline.dispose();
|
|
543
|
+
});
|
|
544
|
+
it('runs class-based systems in order', () => {
|
|
545
|
+
const order = [];
|
|
546
|
+
class SysA extends System {
|
|
547
|
+
tick() { order.push('A'); }
|
|
548
|
+
}
|
|
549
|
+
class SysB extends System {
|
|
550
|
+
tick() { order.push('B'); }
|
|
551
|
+
}
|
|
552
|
+
const pipeline = createSystems(em, [SysA, SysB]);
|
|
553
|
+
pipeline();
|
|
554
|
+
assert.deepEqual(order, ['A', 'B']);
|
|
555
|
+
pipeline.dispose();
|
|
556
|
+
});
|
|
557
|
+
it('mixes functional and class-based systems', () => {
|
|
558
|
+
const order = [];
|
|
559
|
+
function FuncSys() { return () => { order.push('func'); }; }
|
|
560
|
+
class ClassSys extends System {
|
|
561
|
+
tick() { order.push('class'); }
|
|
562
|
+
}
|
|
563
|
+
const pipeline = createSystems(em, [FuncSys, ClassSys]);
|
|
564
|
+
pipeline();
|
|
565
|
+
assert.deepEqual(order, ['func', 'class']);
|
|
566
|
+
pipeline.dispose();
|
|
567
|
+
});
|
|
568
|
+
it('dispose() disposes all systems', () => {
|
|
569
|
+
const collected = [];
|
|
570
|
+
let TestSys = (() => {
|
|
571
|
+
let _classSuper = System;
|
|
572
|
+
let _instanceExtraInitializers = [];
|
|
573
|
+
let _handleAdd_decorators;
|
|
574
|
+
return class TestSys extends _classSuper {
|
|
575
|
+
static {
|
|
576
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
577
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
578
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
579
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
580
|
+
}
|
|
581
|
+
handleAdd(id) { collected.push(id); }
|
|
582
|
+
constructor() {
|
|
583
|
+
super(...arguments);
|
|
584
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
})();
|
|
588
|
+
const pipeline = createSystems(em, [TestSys]);
|
|
589
|
+
pipeline.dispose();
|
|
590
|
+
const e1 = em.createEntity();
|
|
591
|
+
em.addComponent(e1, Health, { hp: 100 });
|
|
592
|
+
em.flushHooks();
|
|
593
|
+
pipeline();
|
|
594
|
+
assert.deepEqual(collected, []);
|
|
595
|
+
});
|
|
596
|
+
it('class hooks fire through pipeline', () => {
|
|
597
|
+
const added = [];
|
|
598
|
+
let moved = 0;
|
|
599
|
+
let HookSys = (() => {
|
|
600
|
+
let _classSuper = System;
|
|
601
|
+
let _instanceExtraInitializers = [];
|
|
602
|
+
let _handleAdd_decorators;
|
|
603
|
+
return class HookSys extends _classSuper {
|
|
604
|
+
static {
|
|
605
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
606
|
+
_handleAdd_decorators = [OnAdded(Health)];
|
|
607
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
608
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
609
|
+
}
|
|
610
|
+
handleAdd(id) { added.push(id); }
|
|
611
|
+
constructor() {
|
|
612
|
+
super(...arguments);
|
|
613
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
})();
|
|
617
|
+
class TickSys extends System {
|
|
618
|
+
tick() {
|
|
619
|
+
this.forEach([Position, Velocity], (view) => { moved += view.count; });
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const pipeline = createSystems(em, [HookSys, TickSys]);
|
|
623
|
+
const e1 = em.createEntityWith(Health, { hp: 100 }, Position, { x: 0, y: 0 }, Velocity, { vx: 1, vy: 1 });
|
|
624
|
+
em.flushHooks();
|
|
625
|
+
pipeline();
|
|
626
|
+
assert.deepEqual(added, [e1]);
|
|
627
|
+
assert.equal(moved, 1);
|
|
628
|
+
pipeline.dispose();
|
|
629
|
+
});
|
|
630
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|