@aethervtt/shard-core 0.0.1
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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/ecs/commands.d.ts +35 -0
- package/dist/ecs/commands.js +96 -0
- package/dist/ecs/entity.d.ts +11 -0
- package/dist/ecs/entity.js +12 -0
- package/dist/ecs/events.d.ts +34 -0
- package/dist/ecs/events.js +65 -0
- package/dist/ecs/hierarchy.d.ts +7 -0
- package/dist/ecs/hierarchy.js +29 -0
- package/dist/ecs/index.d.ts +8 -0
- package/dist/ecs/index.js +38 -0
- package/dist/ecs/observers.d.ts +40 -0
- package/dist/ecs/observers.js +27 -0
- package/dist/ecs/query.d.ts +44 -0
- package/dist/ecs/query.js +67 -0
- package/dist/ecs/table.d.ts +91 -0
- package/dist/ecs/table.js +286 -0
- package/dist/ecs/world.d.ts +127 -0
- package/dist/ecs/world.js +513 -0
- package/dist/error.d.ts +26 -0
- package/dist/error.js +29 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -0
- package/dist/json.d.ts +15 -0
- package/dist/json.js +22 -0
- package/dist/math/aabb.d.ts +13 -0
- package/dist/math/aabb.js +78 -0
- package/dist/math/affine.d.ts +28 -0
- package/dist/math/affine.js +159 -0
- package/dist/math/affine64.d.ts +11 -0
- package/dist/math/affine64.js +58 -0
- package/dist/math/frustum.d.ts +11 -0
- package/dist/math/frustum.js +59 -0
- package/dist/math/hash.d.ts +14 -0
- package/dist/math/hash.js +38 -0
- package/dist/math/index.d.ts +14 -0
- package/dist/math/index.js +33 -0
- package/dist/math/mat3.d.ts +6 -0
- package/dist/math/mat3.js +37 -0
- package/dist/math/mat4.d.ts +23 -0
- package/dist/math/mat4.js +224 -0
- package/dist/math/quat.d.ts +22 -0
- package/dist/math/quat.js +233 -0
- package/dist/math/ray.d.ts +10 -0
- package/dist/math/ray.js +54 -0
- package/dist/math/rng.d.ts +41 -0
- package/dist/math/rng.js +102 -0
- package/dist/math/types.d.ts +8 -0
- package/dist/math/types.js +4 -0
- package/dist/math/vec2.d.ts +11 -0
- package/dist/math/vec2.js +52 -0
- package/dist/math/vec3.d.ts +23 -0
- package/dist/math/vec3.js +135 -0
- package/dist/math/vec4.d.ts +8 -0
- package/dist/math/vec4.js +47 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.js +39 -0
- package/dist/scheduler/profiler.d.ts +19 -0
- package/dist/scheduler/profiler.js +44 -0
- package/dist/scheduler/schedule.d.ts +52 -0
- package/dist/scheduler/schedule.js +235 -0
- package/dist/scheduler/system.d.ts +74 -0
- package/dist/scheduler/system.js +106 -0
- package/dist/schema/color.d.ts +7 -0
- package/dist/schema/color.js +34 -0
- package/dist/schema/component.d.ts +71 -0
- package/dist/schema/component.js +120 -0
- package/dist/schema/field.d.ts +145 -0
- package/dist/schema/field.js +591 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.js +40 -0
- package/dist/schema/names.d.ts +27 -0
- package/dist/schema/names.js +39 -0
- package/dist/schema/registry.d.ts +38 -0
- package/dist/schema/registry.js +58 -0
- package/dist/schema/resource.d.ts +48 -0
- package/dist/schema/resource.js +79 -0
- package/dist/test-env.d.ts +31 -0
- package/dist/test-env.js +37 -0
- package/package.json +38 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
const TYPED = {
|
|
2
|
+
f32: Float32Array,
|
|
3
|
+
f64: Float64Array,
|
|
4
|
+
i8: Int8Array,
|
|
5
|
+
i16: Int16Array,
|
|
6
|
+
i32: Int32Array,
|
|
7
|
+
u8: Uint8Array,
|
|
8
|
+
u16: Uint16Array,
|
|
9
|
+
u32: Uint32Array
|
|
10
|
+
};
|
|
11
|
+
function createColumn(storage, length) {
|
|
12
|
+
return storage === "object" ? new Array(length) : new TYPED[storage](length);
|
|
13
|
+
}
|
|
14
|
+
function growColumn(column, storage, length) {
|
|
15
|
+
if (storage === "object") return column;
|
|
16
|
+
const next = new TYPED[storage](length);
|
|
17
|
+
next.set(column);
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
function growTicks(ticks, length) {
|
|
21
|
+
const next = new Uint32Array(length);
|
|
22
|
+
next.set(ticks);
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
class ComponentStorage {
|
|
26
|
+
columns;
|
|
27
|
+
/** Columns by field name. Same arrays as `columns`. */
|
|
28
|
+
byName = /* @__PURE__ */ Object.create(null);
|
|
29
|
+
added;
|
|
30
|
+
changed;
|
|
31
|
+
/** The newest tick in `changed`, so a system can skip a table where nothing changed. */
|
|
32
|
+
lastChanged = 0;
|
|
33
|
+
def;
|
|
34
|
+
constructor(def, capacity) {
|
|
35
|
+
this.def = def;
|
|
36
|
+
this.columns = def.layout.map((c) => createColumn(c.storage, capacity * c.stride));
|
|
37
|
+
this.reindex();
|
|
38
|
+
this.added = new Uint32Array(capacity);
|
|
39
|
+
this.changed = new Uint32Array(capacity);
|
|
40
|
+
}
|
|
41
|
+
grow(capacity) {
|
|
42
|
+
const layout = this.def.layout;
|
|
43
|
+
for (let i = 0; i < layout.length; i++) {
|
|
44
|
+
const c = layout[i];
|
|
45
|
+
this.columns[i] = growColumn(this.columns[i], c.storage, capacity * c.stride);
|
|
46
|
+
}
|
|
47
|
+
this.reindex();
|
|
48
|
+
this.added = growTicks(this.added, capacity);
|
|
49
|
+
this.changed = growTicks(this.changed, capacity);
|
|
50
|
+
}
|
|
51
|
+
reindex() {
|
|
52
|
+
const layout = this.def.layout;
|
|
53
|
+
for (let i = 0; i < layout.length; i++) this.byName[layout[i].name] = this.columns[i];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
class Table {
|
|
57
|
+
count = 0;
|
|
58
|
+
capacity;
|
|
59
|
+
entities;
|
|
60
|
+
/** Component ids in this archetype, sorted. */
|
|
61
|
+
ids;
|
|
62
|
+
storages;
|
|
63
|
+
/** Cached archetype transitions: component id → table with it added / removed. */
|
|
64
|
+
addEdges = /* @__PURE__ */ new Map();
|
|
65
|
+
removeEdges = /* @__PURE__ */ new Map();
|
|
66
|
+
idSet;
|
|
67
|
+
byId = /* @__PURE__ */ new Map();
|
|
68
|
+
id;
|
|
69
|
+
/**
|
|
70
|
+
* The tick a row last moved in or out. Rows that move in keep their change ticks, so "nothing
|
|
71
|
+
* changed" needs this too.
|
|
72
|
+
*/
|
|
73
|
+
lastStructural = 0;
|
|
74
|
+
components;
|
|
75
|
+
clock;
|
|
76
|
+
constructor(id, components, clock, capacity = 16) {
|
|
77
|
+
this.id = id;
|
|
78
|
+
this.components = components;
|
|
79
|
+
this.clock = clock;
|
|
80
|
+
this.capacity = capacity;
|
|
81
|
+
this.entities = new Float64Array(capacity);
|
|
82
|
+
this.ids = components.map((c) => c.id);
|
|
83
|
+
this.idSet = new Set(this.ids);
|
|
84
|
+
this.storages = components.map((def) => {
|
|
85
|
+
const storage = new ComponentStorage(def, capacity);
|
|
86
|
+
this.byId.set(def.id, storage);
|
|
87
|
+
return storage;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
has(def) {
|
|
91
|
+
return this.idSet.has(def.id);
|
|
92
|
+
}
|
|
93
|
+
hasId(id) {
|
|
94
|
+
return this.idSet.has(id);
|
|
95
|
+
}
|
|
96
|
+
storage(def) {
|
|
97
|
+
return this.byId.get(def.id);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The raw column for one field. Numeric fields are TypedArrays with `stride` values per row
|
|
101
|
+
* (a vec3 at row `i` is at `[i * 3]`, `[i * 3 + 1]`, `[i * 3 + 2]`).
|
|
102
|
+
*/
|
|
103
|
+
column(def, field) {
|
|
104
|
+
return this.byId.get(def.id).byName[field];
|
|
105
|
+
}
|
|
106
|
+
addedTicks(def) {
|
|
107
|
+
return this.byId.get(def.id).added;
|
|
108
|
+
}
|
|
109
|
+
changedTicks(def) {
|
|
110
|
+
return this.byId.get(def.id).changed;
|
|
111
|
+
}
|
|
112
|
+
/** Marks one row, or every row, as changed at the current tick. */
|
|
113
|
+
markChanged(def, row) {
|
|
114
|
+
const storage = this.byId.get(def.id);
|
|
115
|
+
const tick = this.clock.tick;
|
|
116
|
+
if (row === void 0) storage.changed.fill(tick, 0, this.count);
|
|
117
|
+
else storage.changed[row] = tick;
|
|
118
|
+
storage.lastChanged = tick;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The newest change tick of any row of `def`: when it's not after `since`, no row changed. Code
|
|
122
|
+
* that writes `changedTicks()` directly must call `touch` too.
|
|
123
|
+
*/
|
|
124
|
+
lastChanged(def) {
|
|
125
|
+
return this.byId.get(def.id).lastChanged;
|
|
126
|
+
}
|
|
127
|
+
/** Records that rows of `def` changed this tick, after writing `changedTicks()` directly. */
|
|
128
|
+
touch(def) {
|
|
129
|
+
this.byId.get(def.id).lastChanged = this.clock.tick;
|
|
130
|
+
}
|
|
131
|
+
isAdded(def, row, since) {
|
|
132
|
+
return this.byId.get(def.id).added[row] > since;
|
|
133
|
+
}
|
|
134
|
+
isChanged(def, row, since) {
|
|
135
|
+
return this.byId.get(def.id).changed[row] > since;
|
|
136
|
+
}
|
|
137
|
+
// --- structural operations (used by the world) ----------------------------
|
|
138
|
+
pushRow(entity) {
|
|
139
|
+
if (this.count === this.capacity) this.grow(this.capacity * 2);
|
|
140
|
+
this.lastStructural = this.clock.tick;
|
|
141
|
+
const row = this.count++;
|
|
142
|
+
this.entities[row] = entity;
|
|
143
|
+
return row;
|
|
144
|
+
}
|
|
145
|
+
/** Swap-removes a row. Returns the entity moved into `row`, or -1 if it was the last row. */
|
|
146
|
+
removeRow(row) {
|
|
147
|
+
this.lastStructural = this.clock.tick;
|
|
148
|
+
const last = --this.count;
|
|
149
|
+
const storages = this.storages;
|
|
150
|
+
if (row === last) {
|
|
151
|
+
for (let s = 0; s < storages.length; s++) {
|
|
152
|
+
const storage = storages[s];
|
|
153
|
+
const layout = storage.def.layout;
|
|
154
|
+
for (let c = 0; c < layout.length; c++) {
|
|
155
|
+
if (layout[c].storage === "object") storage.columns[c][last] = void 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return -1;
|
|
159
|
+
}
|
|
160
|
+
const moved = this.entities[last];
|
|
161
|
+
this.entities[row] = moved;
|
|
162
|
+
for (let s = 0; s < storages.length; s++) {
|
|
163
|
+
const storage = storages[s];
|
|
164
|
+
copyRow(storage, last, storage, row);
|
|
165
|
+
const layout = storage.def.layout;
|
|
166
|
+
for (let c = 0; c < layout.length; c++) {
|
|
167
|
+
if (layout[c].storage === "object") storage.columns[c][last] = void 0;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return moved;
|
|
171
|
+
}
|
|
172
|
+
/** Copies the components both tables share from `row` here into `targetRow` there. */
|
|
173
|
+
copySharedTo(row, target, targetRow) {
|
|
174
|
+
const storages = this.storages;
|
|
175
|
+
for (let s = 0; s < storages.length; s++) {
|
|
176
|
+
const from = storages[s];
|
|
177
|
+
const to = target.byId.get(from.def.id);
|
|
178
|
+
if (to) copyRow(from, row, to, targetRow);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** Writes a full component value; fields missing from `init` take their defaults. */
|
|
182
|
+
initComponent(def, row, init) {
|
|
183
|
+
const storage = this.byId.get(def.id);
|
|
184
|
+
const layout = def.layout;
|
|
185
|
+
for (let c = 0; c < layout.length; c++) {
|
|
186
|
+
const { name, field } = layout[c];
|
|
187
|
+
const value = init?.[name];
|
|
188
|
+
field.write(storage.columns[c], row, value === void 0 ? field.defaultValue() : value);
|
|
189
|
+
}
|
|
190
|
+
const tick = this.clock.tick;
|
|
191
|
+
storage.added[row] = tick;
|
|
192
|
+
storage.changed[row] = tick;
|
|
193
|
+
storage.lastChanged = tick;
|
|
194
|
+
}
|
|
195
|
+
/** Writes only the fields present in `values`. */
|
|
196
|
+
writeComponent(def, row, values) {
|
|
197
|
+
const storage = this.byId.get(def.id);
|
|
198
|
+
const layout = def.layout;
|
|
199
|
+
for (let c = 0; c < layout.length; c++) {
|
|
200
|
+
const { name, field } = layout[c];
|
|
201
|
+
const value = values[name];
|
|
202
|
+
if (value !== void 0) field.write(storage.columns[c], row, value);
|
|
203
|
+
}
|
|
204
|
+
storage.changed[row] = this.clock.tick;
|
|
205
|
+
storage.lastChanged = this.clock.tick;
|
|
206
|
+
}
|
|
207
|
+
readComponent(def, row) {
|
|
208
|
+
const storage = this.byId.get(def.id);
|
|
209
|
+
const out = {};
|
|
210
|
+
const layout = def.layout;
|
|
211
|
+
for (let c = 0; c < layout.length; c++) {
|
|
212
|
+
const { name, field } = layout[c];
|
|
213
|
+
out[name] = field.read(storage.columns[c], row);
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Replaces a component's definition with one sharing its id (hot reload). Without `migrate`
|
|
219
|
+
* the layout must be the same and only the definition changes; with it, every row's value is
|
|
220
|
+
* rebuilt into new columns. Added ticks carry over; changed ticks become the current tick.
|
|
221
|
+
*/
|
|
222
|
+
redefine(def, migrate) {
|
|
223
|
+
const i = this.ids.indexOf(def.id);
|
|
224
|
+
if (i === -1) return;
|
|
225
|
+
const old = this.storages[i];
|
|
226
|
+
this.components[i] = def;
|
|
227
|
+
if (!migrate) {
|
|
228
|
+
old.def = def;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const values = [];
|
|
232
|
+
for (let row = 0; row < this.count; row++) {
|
|
233
|
+
values.push(migrate(this.readComponent(old.def, row), row));
|
|
234
|
+
}
|
|
235
|
+
const next = new ComponentStorage(def, this.capacity);
|
|
236
|
+
next.added.set(old.added);
|
|
237
|
+
this.storages[i] = next;
|
|
238
|
+
this.byId.set(def.id, next);
|
|
239
|
+
const tick = this.clock.tick;
|
|
240
|
+
for (let row = 0; row < this.count; row++) {
|
|
241
|
+
this.writeComponent(def, row, values[row]);
|
|
242
|
+
next.changed[row] = tick;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/** Approximate bytes used by TypedArray columns (object columns count as 8 bytes a slot). */
|
|
246
|
+
bytes() {
|
|
247
|
+
let total = this.entities.byteLength;
|
|
248
|
+
for (const storage of this.storages) {
|
|
249
|
+
total += storage.added.byteLength + storage.changed.byteLength;
|
|
250
|
+
for (const column of storage.columns) {
|
|
251
|
+
total += Array.isArray(column) ? this.capacity * 8 : column.byteLength;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return total;
|
|
255
|
+
}
|
|
256
|
+
grow(capacity) {
|
|
257
|
+
const entities = new Float64Array(capacity);
|
|
258
|
+
entities.set(this.entities);
|
|
259
|
+
this.entities = entities;
|
|
260
|
+
for (const storage of this.storages) storage.grow(capacity);
|
|
261
|
+
this.capacity = capacity;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function copyRow(from, fromRow, to, toRow) {
|
|
265
|
+
const layout = from.def.layout;
|
|
266
|
+
for (let c = 0; c < layout.length; c++) {
|
|
267
|
+
const stride = layout[c].stride;
|
|
268
|
+
const src = from.columns[c];
|
|
269
|
+
const dst = to.columns[c];
|
|
270
|
+
if (stride === 1) {
|
|
271
|
+
dst[toRow] = src[fromRow];
|
|
272
|
+
} else {
|
|
273
|
+
const a = fromRow * stride;
|
|
274
|
+
const b = toRow * stride;
|
|
275
|
+
for (let i = 0; i < stride; i++) dst[b + i] = src[a + i];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
to.added[toRow] = from.added[fromRow];
|
|
279
|
+
const changed = from.changed[fromRow];
|
|
280
|
+
to.changed[toRow] = changed;
|
|
281
|
+
if (changed > to.lastChanged) to.lastChanged = changed;
|
|
282
|
+
}
|
|
283
|
+
export {
|
|
284
|
+
ComponentStorage,
|
|
285
|
+
Table
|
|
286
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { ComponentDef } from '../schema/component.js';
|
|
2
|
+
import type { Fields, InferFields, InitFields } from '../schema/field.js';
|
|
3
|
+
import { Registry } from '../schema/registry.js';
|
|
4
|
+
import type { EventDef, ResourceDef } from '../schema/resource.js';
|
|
5
|
+
import { type Entity } from './entity.js';
|
|
6
|
+
import { EventReader } from './events.js';
|
|
7
|
+
import { type LifecycleObserver, type LifecycleTrigger, type TriggerObserver } from './observers.js';
|
|
8
|
+
import { Query, type QueryDescriptor } from './query.js';
|
|
9
|
+
import { Table, type TickSource } from './table.js';
|
|
10
|
+
/** A component to spawn/add with: the bare definition (defaults), or `[definition, values]`. */
|
|
11
|
+
export type ComponentInit<F extends Fields = Fields> = ComponentDef<F> | readonly [ComponentDef<F>, InitFields<F>];
|
|
12
|
+
export interface WorldStats {
|
|
13
|
+
entities: number;
|
|
14
|
+
archetypes: number;
|
|
15
|
+
tables: {
|
|
16
|
+
components: string[];
|
|
17
|
+
count: number;
|
|
18
|
+
capacity: number;
|
|
19
|
+
bytes: number;
|
|
20
|
+
}[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Entities, their components (in archetype tables), resources, events, and observers.
|
|
24
|
+
*
|
|
25
|
+
* Direct methods (`spawn`, `add`, `remove`, `despawn`) apply immediately and fire observers
|
|
26
|
+
* synchronously. Don't call them while iterating a query; record them on `Commands` instead.
|
|
27
|
+
*/
|
|
28
|
+
export declare class World implements TickSource {
|
|
29
|
+
/** Change-detection clock. The scheduler advances it once per system run. */
|
|
30
|
+
tick: number;
|
|
31
|
+
readonly registry: Registry;
|
|
32
|
+
private generations;
|
|
33
|
+
private tableOf;
|
|
34
|
+
private rowOf;
|
|
35
|
+
private nextIndex;
|
|
36
|
+
private readonly freeIndices;
|
|
37
|
+
private aliveCount;
|
|
38
|
+
private readonly tables;
|
|
39
|
+
private readonly tableByKey;
|
|
40
|
+
private readonly emptyTable;
|
|
41
|
+
private readonly queries;
|
|
42
|
+
private readonly resources;
|
|
43
|
+
private readonly eventQueues;
|
|
44
|
+
private readonly observers;
|
|
45
|
+
private readonly registered;
|
|
46
|
+
constructor();
|
|
47
|
+
incrementTick(): number;
|
|
48
|
+
spawn<const T extends readonly Fields[]>(...inits: {
|
|
49
|
+
[K in keyof T]: ComponentInit<T[K]>;
|
|
50
|
+
}): Entity;
|
|
51
|
+
/** Allocates an id that comes alive later via `spawnReserved`. Used by `Commands.spawn`. */
|
|
52
|
+
reserveEntity(): Entity;
|
|
53
|
+
spawnReserved(entity: Entity, inits: readonly ComponentInit[]): void;
|
|
54
|
+
isAlive(entity: Entity): boolean;
|
|
55
|
+
get entityCount(): number;
|
|
56
|
+
/** Despawns the entity and, if it has children, all of its descendants. */
|
|
57
|
+
despawn(entity: Entity): void;
|
|
58
|
+
/** Despawns only this entity. Its children lose their `ChildOf` and become roots. */
|
|
59
|
+
despawnSingle(entity: Entity): void;
|
|
60
|
+
/** Adds a component (defaults for missing fields). If present, replaces it and fires `onSet`. */
|
|
61
|
+
add<F extends Fields>(entity: Entity, component: ComponentDef<F>, value?: InitFields<F>): void;
|
|
62
|
+
/** Removes a component. Returns false if the entity didn't have it. */
|
|
63
|
+
remove(entity: Entity, component: ComponentDef): boolean;
|
|
64
|
+
has(entity: Entity, component: ComponentDef): boolean;
|
|
65
|
+
/** A copy of the component's value. Cold path: allocates. */
|
|
66
|
+
get<F extends Fields>(entity: Entity, component: ComponentDef<F>): InferFields<F>;
|
|
67
|
+
tryGet<F extends Fields>(entity: Entity, component: ComponentDef<F>): InferFields<F> | undefined;
|
|
68
|
+
/** Writes the given fields and marks the component changed. */
|
|
69
|
+
set<F extends Fields>(entity: Entity, component: ComponentDef<F>, values: InitFields<F>): void;
|
|
70
|
+
/** The table holding the entity's components. For system code that walks relationships. */
|
|
71
|
+
entityTable(entity: Entity): Table;
|
|
72
|
+
/** The entity's row in its table. Valid until the next structural change. */
|
|
73
|
+
entityRow(entity: Entity): number;
|
|
74
|
+
/**
|
|
75
|
+
* Like `entityTable` / `entityRow` but without the liveness check, for hot relationship walks
|
|
76
|
+
* where the caller already knows the entity is alive (e.g. entries of a `Children` list).
|
|
77
|
+
*/
|
|
78
|
+
entityTableUnchecked(entity: Entity): Table;
|
|
79
|
+
entityRowUnchecked(entity: Entity): number;
|
|
80
|
+
componentsOf(entity: Entity): readonly ComponentDef[];
|
|
81
|
+
/** Returns a cached query; the same descriptor always gives the same `Query`. */
|
|
82
|
+
query(desc: QueryDescriptor): Query;
|
|
83
|
+
insertResource<T>(def: ResourceDef<T>, value: T): void;
|
|
84
|
+
/** Inserts the resource from its `init` if it isn't present yet, and returns it. */
|
|
85
|
+
initResource<T>(def: ResourceDef<T>): T;
|
|
86
|
+
resource<T>(def: ResourceDef<T>): T;
|
|
87
|
+
tryResource<T>(def: ResourceDef<T>): T | undefined;
|
|
88
|
+
hasResource(def: ResourceDef<unknown>): boolean;
|
|
89
|
+
removeResource(def: ResourceDef<unknown>): boolean;
|
|
90
|
+
send<T>(def: EventDef<T>, ...data: T extends undefined ? [data?: T] : [data: T]): void;
|
|
91
|
+
reader<T>(def: EventDef<T>): EventReader<T>;
|
|
92
|
+
/** Advances every event queue by one frame. Called by the app at the start of each frame. */
|
|
93
|
+
updateEvents(): void;
|
|
94
|
+
private eventQueue;
|
|
95
|
+
/** Registers an observer. Returns a function that unregisters it. */
|
|
96
|
+
observe<F extends Fields>(trigger: LifecycleTrigger<F>, fn: LifecycleObserver<F>): () => void;
|
|
97
|
+
observe<T>(event: EventDef<T>, fn: TriggerObserver<T>): () => void;
|
|
98
|
+
/** Runs observers registered for `event`, synchronously. */
|
|
99
|
+
trigger<T>(event: EventDef<T>, data: T, entity?: Entity): void;
|
|
100
|
+
stats(): WorldStats;
|
|
101
|
+
/** All archetype tables, including empty ones. */
|
|
102
|
+
allTables(): readonly Table[];
|
|
103
|
+
private allocate;
|
|
104
|
+
private growIndex;
|
|
105
|
+
/** Returns the entity's index, or throws if it isn't alive. */
|
|
106
|
+
private locate;
|
|
107
|
+
private insert;
|
|
108
|
+
private moveEntity;
|
|
109
|
+
/** The archetype with `def` and everything `def` requires (transitively) added. Cached. */
|
|
110
|
+
private tableWith;
|
|
111
|
+
private tableWithout;
|
|
112
|
+
/**
|
|
113
|
+
* Hot reload: replaces a definition with one that reuses its id. Components keep their storage
|
|
114
|
+
* when `migrate` is omitted (same layout); otherwise every row is rebuilt through `migrate`.
|
|
115
|
+
* Resources and events keep their values and queues.
|
|
116
|
+
*/
|
|
117
|
+
redefine(def: ComponentDef | ResourceDef<unknown> | EventDef<unknown>, migrate?: (value: Record<string, unknown>, entity: Entity) => Record<string, unknown>): void;
|
|
118
|
+
private getOrCreateTable;
|
|
119
|
+
private ensureRegistered;
|
|
120
|
+
private hasObservers;
|
|
121
|
+
private fire;
|
|
122
|
+
/** Keeps `Children` in sync with `ChildOf`. */
|
|
123
|
+
private installHierarchy;
|
|
124
|
+
private childList;
|
|
125
|
+
private addChild;
|
|
126
|
+
private removeChild;
|
|
127
|
+
}
|