archetype-ecs-lib 0.4.2 → 0.6.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 +8 -173
- package/lib/ecs/Archetype.d.ts +1 -1
- package/lib/ecs/Archetype.js +1 -1
- package/lib/ecs/Commands.d.ts +7 -2
- package/lib/ecs/Commands.js +110 -0
- package/lib/ecs/EntityManager.d.ts +3 -1
- package/lib/ecs/EntityManager.js +89 -0
- package/lib/ecs/Events.d.ts +23 -0
- package/lib/ecs/Events.js +76 -0
- package/lib/ecs/Schedule.d.ts +129 -6
- package/lib/ecs/Schedule.js +387 -29
- package/lib/ecs/Signature.d.ts +1 -1
- package/lib/ecs/TypeRegistry.d.ts +1 -1
- package/lib/ecs/Types.d.ts +270 -5
- package/lib/ecs/World.d.ts +97 -7
- package/lib/ecs/World.js +715 -63
- package/lib/ecs/WorldSnapshotStore.d.ts +30 -0
- package/lib/ecs/WorldSnapshotStore.js +339 -0
- package/lib/ecs/stats/StatsOverlay.d.ts +72 -0
- package/lib/ecs/stats/StatsOverlay.js +548 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
1
|
+

|
|
2
2
|
|
|
3
3
|
# Archetype ECS Lib
|
|
4
4
|
|
|
@@ -7,66 +7,23 @@ A tiny **archetype-based ECS** (Entity Component System) for TypeScript.
|
|
|
7
7
|
- **Archetypes (tables)** store entities in a **SoA** layout (one column per component type).
|
|
8
8
|
- **Queries** iterate matching archetypes efficiently.
|
|
9
9
|
- **Commands** let you **defer structural changes** (spawn/despawn/add/remove) safely.
|
|
10
|
+
- **Resources (singletons)** store **global world state** (Input, Time, Config, Asset caches…) keyed by type, without using entities.
|
|
11
|
+
- **Events** transient messages (Hit happened, Click happened, Play sound).
|
|
10
12
|
- A minimal **Schedule** runs systems by phases and flushes commands between phases.
|
|
11
13
|
|
|
12
14
|
Exports are defined in `index.ts`:
|
|
13
15
|
- `Types`, `TypeRegistry`, `Commands`, `World`, `Schedule`
|
|
14
16
|
|
|
17
|
+
> :exclamation: The full documentation is at [https://piratejl.github.io/archetype-ecs-lib/](https://piratejl.github.io/archetype-ecs-lib/)
|
|
18
|
+
|
|
15
19
|
---
|
|
16
20
|
|
|
17
21
|
## Install
|
|
18
22
|
|
|
19
23
|
```bash
|
|
20
24
|
npm i archetype-ecs-lib
|
|
21
|
-
````
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Core concepts
|
|
26
|
-
|
|
27
|
-
### Entity
|
|
28
|
-
|
|
29
|
-
An entity is a lightweight handle:
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
type Entity = { id: number; gen: number };
|
|
33
25
|
```
|
|
34
26
|
|
|
35
|
-
The `gen` (generation) prevents using stale entity handles after despawn/reuse.
|
|
36
|
-
|
|
37
|
-
### Component
|
|
38
|
-
|
|
39
|
-
A component is any class used as a type key:
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
class Position { constructor(public x = 0, public y = 0) {} }
|
|
43
|
-
class Velocity { constructor(public x = 0, public y = 0) {} }
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Internally, constructors are mapped to a stable numeric `TypeId` via `typeId()`.
|
|
47
|
-
|
|
48
|
-
### World
|
|
49
|
-
|
|
50
|
-
`World` owns entities, archetypes, commands, and systems.
|
|
51
|
-
|
|
52
|
-
Structural operations:
|
|
53
|
-
|
|
54
|
-
* `spawn()`, `despawn(e)`
|
|
55
|
-
* `add(e, Ctor, value)`, `remove(e, Ctor)`
|
|
56
|
-
* `has(e, Ctor)`, `get(e, Ctor)`, `set(e, Ctor, value)`
|
|
57
|
-
* `query(...ctors)` to iterate entities with required components
|
|
58
|
-
* `cmd()` to enqueue deferred commands
|
|
59
|
-
* `flush()` applies queued commands
|
|
60
|
-
* `update(dt)` runs registered systems and flushes at the end
|
|
61
|
-
|
|
62
|
-
### Deferred structural changes (important)
|
|
63
|
-
|
|
64
|
-
While iterating a query (or while systems are running), doing structural changes directly can throw:
|
|
65
|
-
|
|
66
|
-
> “Cannot do structural change (…) while iterating. Use world.cmd() and flush …”
|
|
67
|
-
|
|
68
|
-
Use `world.cmd()` inside systems / loops, and let `world.flush()` apply changes safely.
|
|
69
|
-
|
|
70
27
|
---
|
|
71
28
|
|
|
72
29
|
## Quick start
|
|
@@ -85,7 +42,7 @@ world.add(e, Position, new Position(0, 0));
|
|
|
85
42
|
world.add(e, Velocity, new Velocity(1, 0));
|
|
86
43
|
|
|
87
44
|
// A simple system
|
|
88
|
-
world.addSystem((w
|
|
45
|
+
world.addSystem((w) => {
|
|
89
46
|
for (const { e, c1: pos, c2: vel } of w.query(Position, Velocity)) {
|
|
90
47
|
pos.x += vel.x * dt;
|
|
91
48
|
pos.y += vel.y * dt;
|
|
@@ -98,107 +55,7 @@ world.addSystem((w: any, dt: number) => {
|
|
|
98
55
|
world.update(1 / 60);
|
|
99
56
|
```
|
|
100
57
|
|
|
101
|
-
> Note: `SystemFn` is typed as `(world:
|
|
102
|
-
> In practice, you’ll typically use the concrete `World` API in systems (cast `world` or type your function accordingly).
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Query API
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
for (const row of world.query(Position, Velocity)) {
|
|
110
|
-
// row.e -> Entity
|
|
111
|
-
// row.c1 -> Position
|
|
112
|
-
// row.c2 -> Velocity
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
`query(...ctors)` yields objects shaped like:
|
|
117
|
-
|
|
118
|
-
* `e`: the entity
|
|
119
|
-
* `c1`, `c2`, `c3`, …: component values **in the same order** as the ctor arguments
|
|
120
|
-
|
|
121
|
-
So if you call `query(A, B, C)` you’ll get `{ e, c1: A, c2: B, c3: C }`.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Commands API (deferred ops)
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
const cmd = world.cmd();
|
|
129
|
-
|
|
130
|
-
cmd.spawn((e) => {
|
|
131
|
-
cmd.add(e, Position, new Position(0, 0));
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
cmd.add(entity, Velocity, new Velocity(1, 0));
|
|
135
|
-
cmd.remove(entity, Velocity);
|
|
136
|
-
cmd.despawn(entity);
|
|
137
|
-
|
|
138
|
-
// Apply them (World.update() also flushes automatically at end)
|
|
139
|
-
world.flush();
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Supported commands (`Commands.ts`):
|
|
143
|
-
|
|
144
|
-
* `spawn(init?)`
|
|
145
|
-
* `despawn(e)`
|
|
146
|
-
* `add(e, ctor, value)`
|
|
147
|
-
* `remove(e, ctor)`
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## Schedule (phases)
|
|
152
|
-
|
|
153
|
-
`Schedule` is a small phase runner:
|
|
154
|
-
|
|
155
|
-
```ts
|
|
156
|
-
import { World, Schedule } from "archetype-ecs-lib";
|
|
157
|
-
|
|
158
|
-
const world = new World();
|
|
159
|
-
const sched = new Schedule();
|
|
160
|
-
|
|
161
|
-
sched
|
|
162
|
-
.add("input", (w: any) => { /* read input, enqueue commands */ })
|
|
163
|
-
.add("sim", (w: any, dt) => { /* update movement */ })
|
|
164
|
-
.add("render",(w: any) => { /* build render data */ });
|
|
165
|
-
|
|
166
|
-
const phases = ["input", "sim", "render"];
|
|
167
|
-
|
|
168
|
-
// Runs each phase in order and calls world.flush() after each phase.
|
|
169
|
-
sched.run(world, 1/60, phases);
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
This is handy when you want deterministic ordering and command application points.
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## World API summary
|
|
177
|
-
|
|
178
|
-
### Entity lifecycle
|
|
179
|
-
|
|
180
|
-
* `spawn(): Entity`
|
|
181
|
-
* `despawn(e: Entity): void`
|
|
182
|
-
* `isAlive(e: Entity): boolean`
|
|
183
|
-
|
|
184
|
-
### Components
|
|
185
|
-
|
|
186
|
-
* `has(e, Ctor): boolean`
|
|
187
|
-
* `get(e, Ctor): T | undefined`
|
|
188
|
-
* `set(e, Ctor, value): void` *(requires the component to exist; otherwise throws)*
|
|
189
|
-
* `add(e, Ctor, value): void` *(structural: may move entity between archetypes)*
|
|
190
|
-
* `remove(e, Ctor): void` *(structural: may move entity between archetypes)*
|
|
191
|
-
|
|
192
|
-
### Systems / frame
|
|
193
|
-
|
|
194
|
-
* `addSystem(fn): this`
|
|
195
|
-
* `update(dt): void` *(runs systems in order, then flushes)*
|
|
196
|
-
* `cmd(): Commands`
|
|
197
|
-
* `flush(): void`
|
|
198
|
-
|
|
199
|
-
### Queries
|
|
200
|
-
|
|
201
|
-
* `query(...ctors): Iterable<{ e: Entity; c1?: any; c2?: any; ... }>`
|
|
58
|
+
> Note: `SystemFn` is typed as `(world: WorldApi, dt) => void`..
|
|
202
59
|
|
|
203
60
|
---
|
|
204
61
|
|
|
@@ -212,26 +69,4 @@ This is handy when you want deterministic ordering and command application point
|
|
|
212
69
|
|
|
213
70
|
## License
|
|
214
71
|
|
|
215
|
-
|
|
216
|
-
MIT License
|
|
217
|
-
|
|
218
|
-
Copyright (c) 2025 Jean-Laurent Duzant
|
|
219
|
-
|
|
220
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
221
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
222
|
-
in the Software without restriction, including without limitation the rights
|
|
223
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
224
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
225
|
-
furnished to do so, subject to the following conditions:
|
|
226
|
-
|
|
227
|
-
The above copyright notice and this permission notice shall be included in all
|
|
228
|
-
copies or substantial portions of the Software.
|
|
229
|
-
|
|
230
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
231
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
232
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
233
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
234
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
235
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
236
|
-
SOFTWARE.
|
|
237
|
-
```
|
|
72
|
+
This code is distributed under the terms and conditions of the [MIT license](https://github.com/PirateJL/archetype-ecs-lib/blob/master/LICENSE).
|
package/lib/ecs/Archetype.d.ts
CHANGED
package/lib/ecs/Archetype.js
CHANGED
|
@@ -74,7 +74,7 @@ var Archetype = /** @class */ (function () {
|
|
|
74
74
|
this.entities.pop();
|
|
75
75
|
try {
|
|
76
76
|
for (var _c = __values(this.cols), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
77
|
-
var _e = __read(_d.value, 2),
|
|
77
|
+
var _e = __read(_d.value, 2), col = _e[1];
|
|
78
78
|
col[row] = col[last];
|
|
79
79
|
col.pop();
|
|
80
80
|
}
|
package/lib/ecs/Commands.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentCtor, Entity } from "./Types";
|
|
1
|
+
import type { CommandsApi, ComponentCtor, ComponentCtorBundleItem, Entity } from "./Types";
|
|
2
2
|
export type Command = {
|
|
3
3
|
k: "spawn";
|
|
4
4
|
init?: (e: Entity) => void;
|
|
@@ -15,11 +15,16 @@ export type Command = {
|
|
|
15
15
|
e: Entity;
|
|
16
16
|
ctor: ComponentCtor<any>;
|
|
17
17
|
};
|
|
18
|
-
export declare class Commands {
|
|
18
|
+
export declare class Commands implements CommandsApi {
|
|
19
19
|
private q;
|
|
20
20
|
spawn(init?: (e: Entity) => void): void;
|
|
21
|
+
spawnBundle(...items: ComponentCtorBundleItem[]): void;
|
|
21
22
|
despawn(e: Entity): void;
|
|
23
|
+
despawnBundle(entities: Entity[]): void;
|
|
22
24
|
add<T>(e: Entity, ctor: ComponentCtor<T>, value: T): void;
|
|
25
|
+
addBundle(e: Entity, ...items: ComponentCtorBundleItem[]): void;
|
|
23
26
|
remove<T>(e: Entity, ctor: ComponentCtor<T>): void;
|
|
27
|
+
removeBundle(e: Entity, ...ctors: ComponentCtor<any>[]): void;
|
|
28
|
+
hasPending(): boolean;
|
|
24
29
|
drain(): Command[];
|
|
25
30
|
}
|
package/lib/ecs/Commands.js
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __values = (this && this.__values) || function(o) {
|
|
3
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
+
if (m) return m.call(o);
|
|
5
|
+
if (o && typeof o.length === "number") return {
|
|
6
|
+
next: function () {
|
|
7
|
+
if (o && i >= o.length) o = void 0;
|
|
8
|
+
return { value: o && o[i++], done: !o };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
+
};
|
|
13
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
14
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
15
|
+
if (!m) return o;
|
|
16
|
+
var i = m.call(o), r, ar = [], e;
|
|
17
|
+
try {
|
|
18
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
19
|
+
}
|
|
20
|
+
catch (error) { e = { error: error }; }
|
|
21
|
+
finally {
|
|
22
|
+
try {
|
|
23
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
24
|
+
}
|
|
25
|
+
finally { if (e) throw e.error; }
|
|
26
|
+
}
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
2
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
30
|
exports.Commands = void 0;
|
|
4
31
|
var Commands = /** @class */ (function () {
|
|
@@ -8,15 +35,98 @@ var Commands = /** @class */ (function () {
|
|
|
8
35
|
Commands.prototype.spawn = function (init) {
|
|
9
36
|
this.q.push({ k: "spawn", init: init });
|
|
10
37
|
};
|
|
38
|
+
Commands.prototype.spawnBundle = function () {
|
|
39
|
+
var _this = this;
|
|
40
|
+
var items = [];
|
|
41
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
42
|
+
items[_i] = arguments[_i];
|
|
43
|
+
}
|
|
44
|
+
this.spawn(function (e) {
|
|
45
|
+
var e_1, _a;
|
|
46
|
+
try {
|
|
47
|
+
// Applied during the same flush thanks to World.flush draining until empty.
|
|
48
|
+
for (var items_1 = __values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) {
|
|
49
|
+
var _b = __read(items_1_1.value, 2), ctor = _b[0], value = _b[1];
|
|
50
|
+
_this.add(e, ctor, value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
54
|
+
finally {
|
|
55
|
+
try {
|
|
56
|
+
if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
|
|
57
|
+
}
|
|
58
|
+
finally { if (e_1) throw e_1.error; }
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
11
62
|
Commands.prototype.despawn = function (e) {
|
|
12
63
|
this.q.push({ k: "despawn", e: e });
|
|
13
64
|
};
|
|
65
|
+
Commands.prototype.despawnBundle = function (entities) {
|
|
66
|
+
var e_2, _a;
|
|
67
|
+
try {
|
|
68
|
+
for (var entities_1 = __values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) {
|
|
69
|
+
var e = entities_1_1.value;
|
|
70
|
+
this.despawn(e);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
74
|
+
finally {
|
|
75
|
+
try {
|
|
76
|
+
if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
|
|
77
|
+
}
|
|
78
|
+
finally { if (e_2) throw e_2.error; }
|
|
79
|
+
}
|
|
80
|
+
};
|
|
14
81
|
Commands.prototype.add = function (e, ctor, value) {
|
|
15
82
|
this.q.push({ k: "add", e: e, ctor: ctor, value: value });
|
|
16
83
|
};
|
|
84
|
+
Commands.prototype.addBundle = function (e) {
|
|
85
|
+
var e_3, _a;
|
|
86
|
+
var items = [];
|
|
87
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
88
|
+
items[_i - 1] = arguments[_i];
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
for (var items_2 = __values(items), items_2_1 = items_2.next(); !items_2_1.done; items_2_1 = items_2.next()) {
|
|
92
|
+
var _b = __read(items_2_1.value, 2), ctor = _b[0], value = _b[1];
|
|
93
|
+
this.add(e, ctor, value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
97
|
+
finally {
|
|
98
|
+
try {
|
|
99
|
+
if (items_2_1 && !items_2_1.done && (_a = items_2.return)) _a.call(items_2);
|
|
100
|
+
}
|
|
101
|
+
finally { if (e_3) throw e_3.error; }
|
|
102
|
+
}
|
|
103
|
+
};
|
|
17
104
|
Commands.prototype.remove = function (e, ctor) {
|
|
18
105
|
this.q.push({ k: "remove", e: e, ctor: ctor });
|
|
19
106
|
};
|
|
107
|
+
Commands.prototype.removeBundle = function (e) {
|
|
108
|
+
var e_4, _a;
|
|
109
|
+
var ctors = [];
|
|
110
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
111
|
+
ctors[_i - 1] = arguments[_i];
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
for (var ctors_1 = __values(ctors), ctors_1_1 = ctors_1.next(); !ctors_1_1.done; ctors_1_1 = ctors_1.next()) {
|
|
115
|
+
var ctor = ctors_1_1.value;
|
|
116
|
+
this.remove(e, ctor);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
120
|
+
finally {
|
|
121
|
+
try {
|
|
122
|
+
if (ctors_1_1 && !ctors_1_1.done && (_a = ctors_1.return)) _a.call(ctors_1);
|
|
123
|
+
}
|
|
124
|
+
finally { if (e_4) throw e_4.error; }
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
Commands.prototype.hasPending = function () {
|
|
128
|
+
return this.q.length > 0;
|
|
129
|
+
};
|
|
20
130
|
Commands.prototype.drain = function () {
|
|
21
131
|
var out = this.q;
|
|
22
132
|
this.q = [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Entity, EntityMeta } from "./Types";
|
|
1
|
+
import type { Entity, EntityMeta, WorldSnapshotAllocator } from "./Types";
|
|
2
2
|
export declare class EntityManager {
|
|
3
3
|
private _nextId;
|
|
4
4
|
private _free;
|
|
@@ -6,4 +6,6 @@ export declare class EntityManager {
|
|
|
6
6
|
create(): Entity;
|
|
7
7
|
isAlive(e: Entity): boolean;
|
|
8
8
|
kill(e: Entity): void;
|
|
9
|
+
snapshotAllocator(): WorldSnapshotAllocator;
|
|
10
|
+
restoreAllocator(snapshot: WorldSnapshotAllocator): void;
|
|
9
11
|
}
|
package/lib/ecs/EntityManager.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __values = (this && this.__values) || function(o) {
|
|
3
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
+
if (m) return m.call(o);
|
|
5
|
+
if (o && typeof o.length === "number") return {
|
|
6
|
+
next: function () {
|
|
7
|
+
if (o && i >= o.length) o = void 0;
|
|
8
|
+
return { value: o && o[i++], done: !o };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
exports.EntityManager = void 0;
|
|
4
15
|
var EntityManager = /** @class */ (function () {
|
|
@@ -33,6 +44,84 @@ var EntityManager = /** @class */ (function () {
|
|
|
33
44
|
m.alive = false;
|
|
34
45
|
this._free.push(e.id);
|
|
35
46
|
};
|
|
47
|
+
EntityManager.prototype.snapshotAllocator = function () {
|
|
48
|
+
var generations = [];
|
|
49
|
+
for (var id = 1; id < this.meta.length; id++) {
|
|
50
|
+
var m = this.meta[id];
|
|
51
|
+
if (!m)
|
|
52
|
+
continue;
|
|
53
|
+
generations.push([id, m.gen]);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
nextId: this._nextId,
|
|
57
|
+
free: this._free.slice(),
|
|
58
|
+
generations: generations
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
EntityManager.prototype.restoreAllocator = function (snapshot) {
|
|
62
|
+
var e_1, _a, e_2, _b;
|
|
63
|
+
if (!Number.isInteger(snapshot.nextId) || snapshot.nextId < 1) {
|
|
64
|
+
throw new Error("Invalid snapshot allocator.nextId: ".concat(snapshot.nextId));
|
|
65
|
+
}
|
|
66
|
+
var seenGenerations = new Set();
|
|
67
|
+
this.meta.length = 0;
|
|
68
|
+
try {
|
|
69
|
+
for (var _c = __values(snapshot.generations), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
70
|
+
var entry = _d.value;
|
|
71
|
+
var id = entry[0];
|
|
72
|
+
var gen = entry[1];
|
|
73
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
74
|
+
throw new Error("Invalid snapshot allocator generations id: ".concat(id));
|
|
75
|
+
}
|
|
76
|
+
if (!Number.isInteger(gen) || gen <= 0) {
|
|
77
|
+
throw new Error("Invalid snapshot allocator generation for id ".concat(id, ": ").concat(gen));
|
|
78
|
+
}
|
|
79
|
+
if (id >= snapshot.nextId) {
|
|
80
|
+
throw new Error("Invalid snapshot allocator generation id ".concat(id, ": must be < nextId (").concat(snapshot.nextId, ")"));
|
|
81
|
+
}
|
|
82
|
+
if (seenGenerations.has(id)) {
|
|
83
|
+
throw new Error("Duplicate snapshot allocator generation entry for id ".concat(id));
|
|
84
|
+
}
|
|
85
|
+
seenGenerations.add(id);
|
|
86
|
+
this.meta[id] = { gen: gen, alive: false, arch: 0, row: 0 };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
90
|
+
finally {
|
|
91
|
+
try {
|
|
92
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
93
|
+
}
|
|
94
|
+
finally { if (e_1) throw e_1.error; }
|
|
95
|
+
}
|
|
96
|
+
var seenFree = new Set();
|
|
97
|
+
try {
|
|
98
|
+
for (var _e = __values(snapshot.free), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
99
|
+
var id = _f.value;
|
|
100
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
101
|
+
throw new Error("Invalid snapshot allocator free id: ".concat(id));
|
|
102
|
+
}
|
|
103
|
+
if (id >= snapshot.nextId) {
|
|
104
|
+
throw new Error("Invalid snapshot allocator free id ".concat(id, ": must be < nextId (").concat(snapshot.nextId, ")"));
|
|
105
|
+
}
|
|
106
|
+
if (!seenGenerations.has(id)) {
|
|
107
|
+
throw new Error("Invalid snapshot allocator free id ".concat(id, ": missing generation entry"));
|
|
108
|
+
}
|
|
109
|
+
if (seenFree.has(id)) {
|
|
110
|
+
throw new Error("Duplicate snapshot allocator free id ".concat(id));
|
|
111
|
+
}
|
|
112
|
+
seenFree.add(id);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
116
|
+
finally {
|
|
117
|
+
try {
|
|
118
|
+
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
|
119
|
+
}
|
|
120
|
+
finally { if (e_2) throw e_2.error; }
|
|
121
|
+
}
|
|
122
|
+
this._nextId = snapshot.nextId;
|
|
123
|
+
this._free = snapshot.free.slice();
|
|
124
|
+
};
|
|
36
125
|
return EntityManager;
|
|
37
126
|
}());
|
|
38
127
|
exports.EntityManager = EntityManager;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare class EventChannel<T> {
|
|
2
|
+
private _read;
|
|
3
|
+
private _write;
|
|
4
|
+
/** Emit an event into the current phase write buffer. */
|
|
5
|
+
emit(ev: T): void;
|
|
6
|
+
/**
|
|
7
|
+
* Drain readable events (emitted in the previous phase), then clears the read buffer.
|
|
8
|
+
* Zero allocations; fast for hot paths.
|
|
9
|
+
*/
|
|
10
|
+
drain(fn: (ev: T) => void): void;
|
|
11
|
+
/**
|
|
12
|
+
* Read-only view of readable events (previous phase).
|
|
13
|
+
* Valid until the next schedule boundary (swapEvents).
|
|
14
|
+
* Do not store this reference long-term.
|
|
15
|
+
*/
|
|
16
|
+
values(): readonly T[];
|
|
17
|
+
count(): number;
|
|
18
|
+
clear(): void;
|
|
19
|
+
/** Clears both buffers (rarely needed, but useful for resets). */
|
|
20
|
+
clearAll(): void;
|
|
21
|
+
/** @internal Called by World at phase boundaries. */
|
|
22
|
+
swapBuffers(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __values = (this && this.__values) || function(o) {
|
|
3
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
+
if (m) return m.call(o);
|
|
5
|
+
if (o && typeof o.length === "number") return {
|
|
6
|
+
next: function () {
|
|
7
|
+
if (o && i >= o.length) o = void 0;
|
|
8
|
+
return { value: o && o[i++], done: !o };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.EventChannel = void 0;
|
|
15
|
+
var EventChannel = /** @class */ (function () {
|
|
16
|
+
function EventChannel() {
|
|
17
|
+
this._read = [];
|
|
18
|
+
this._write = [];
|
|
19
|
+
}
|
|
20
|
+
/** Emit an event into the current phase write buffer. */
|
|
21
|
+
EventChannel.prototype.emit = function (ev) {
|
|
22
|
+
this._write.push(ev);
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Drain readable events (emitted in the previous phase), then clears the read buffer.
|
|
26
|
+
* Zero allocations; fast for hot paths.
|
|
27
|
+
*/
|
|
28
|
+
EventChannel.prototype.drain = function (fn) {
|
|
29
|
+
var e_1, _a;
|
|
30
|
+
var read = this._read;
|
|
31
|
+
try {
|
|
32
|
+
for (var read_1 = __values(read), read_1_1 = read_1.next(); !read_1_1.done; read_1_1 = read_1.next()) {
|
|
33
|
+
var r = read_1_1.value;
|
|
34
|
+
fn(r);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
38
|
+
finally {
|
|
39
|
+
try {
|
|
40
|
+
if (read_1_1 && !read_1_1.done && (_a = read_1.return)) _a.call(read_1);
|
|
41
|
+
}
|
|
42
|
+
finally { if (e_1) throw e_1.error; }
|
|
43
|
+
}
|
|
44
|
+
this.clear();
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Read-only view of readable events (previous phase).
|
|
48
|
+
* Valid until the next schedule boundary (swapEvents).
|
|
49
|
+
* Do not store this reference long-term.
|
|
50
|
+
*/
|
|
51
|
+
EventChannel.prototype.values = function () {
|
|
52
|
+
return this._read;
|
|
53
|
+
};
|
|
54
|
+
EventChannel.prototype.count = function () {
|
|
55
|
+
return this._read.length;
|
|
56
|
+
};
|
|
57
|
+
EventChannel.prototype.clear = function () {
|
|
58
|
+
this._read.length = 0;
|
|
59
|
+
};
|
|
60
|
+
/** Clears both buffers (rarely needed, but useful for resets). */
|
|
61
|
+
EventChannel.prototype.clearAll = function () {
|
|
62
|
+
this._read.length = 0;
|
|
63
|
+
this._write.length = 0;
|
|
64
|
+
};
|
|
65
|
+
/** @internal Called by World at phase boundaries. */
|
|
66
|
+
EventChannel.prototype.swapBuffers = function () {
|
|
67
|
+
if (this._write.length === 0 && this._read.length === 0)
|
|
68
|
+
return;
|
|
69
|
+
var tmp = this._read;
|
|
70
|
+
this._read = this._write;
|
|
71
|
+
this._write = tmp;
|
|
72
|
+
this._write.length = 0;
|
|
73
|
+
};
|
|
74
|
+
return EventChannel;
|
|
75
|
+
}());
|
|
76
|
+
exports.EventChannel = EventChannel;
|