@daneren2005/shared-memory-ecs 1.2.1 → 1.3.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 +86 -1
- package/dist/component-definition.d.ts +1 -1
- package/dist/create-component-worker-vbX3vEia.js +135 -0
- package/dist/create-component-worker-vbX3vEia.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +98 -190
- package/dist/index.js.map +1 -1
- package/dist/memory-component.d.ts +3 -1
- package/dist/systems/component-system.d.ts +15 -8
- package/dist/systems/system.d.ts +1 -1
- package/dist/systems/workers/component-web-worker.d.ts +5 -4
- package/dist/systems/workers/component-worker-message.d.ts +13 -1
- package/dist/systems/workers/create-component-worker.d.ts +1 -1
- package/dist/worker.d.ts +8 -0
- package/dist/worker.js +2 -0
- package/dist/world.d.ts +2 -1
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -170,7 +170,8 @@ use the accessors.
|
|
|
170
170
|
## ComponentSystem workers
|
|
171
171
|
|
|
172
172
|
`ComponentSystem` needs a `getWorker()` that returns a real `Worker`, and an `updateFunction`. Your
|
|
173
|
-
worker entry file calls `createComponentWorker(self, updateFunction)
|
|
173
|
+
worker entry file calls `createComponentWorker(self, updateFunction)`, importing it from the
|
|
174
|
+
[`/worker` subpath](#importing-in-workers) so the worker bundle stays small. When Web Workers or
|
|
174
175
|
`SharedArrayBuffer` are unavailable it transparently falls back to running the same update function on
|
|
175
176
|
the main thread. Attach any extra per-run data (the equivalent of the old faction/fog-of-war fields)
|
|
176
177
|
by overriding `addDataToWorld(world)`. Declare its shape with the `W` type parameter (an interface
|
|
@@ -189,6 +190,90 @@ class DamageSystem extends ComponentSystem<Components, { health: Int32Array }, D
|
|
|
189
190
|
}
|
|
190
191
|
```
|
|
191
192
|
|
|
193
|
+
### Update-function hooks: `init` and `preRun`
|
|
194
|
+
|
|
195
|
+
`addDataToWorld` runs on the main thread and re-sends its data every run. When the data instead needs to
|
|
196
|
+
*live in the worker* — computed once, or too big to ship each frame — attach hooks to the update function
|
|
197
|
+
itself. Both are optional properties on the `EntityUpdateFunction`.
|
|
198
|
+
|
|
199
|
+
**`init`** runs on `system.finishLoading()` — once at startup (`world.init()` calls it), and again on each
|
|
200
|
+
`world.load()` so a reused world re-seeds its workers. It receives whatever the system's `getInitData()` returned
|
|
201
|
+
(structured-cloned across the boundary) and returns a `Partial<W>` that is merged onto `world` on every
|
|
202
|
+
subsequent run. Use it for state that must be seeded from the main thread but then persist inside the worker
|
|
203
|
+
— a seeded RNG, a lookup table, a config object — without paying to re-send it each frame. Type the init
|
|
204
|
+
data with the `D` type parameter (the fourth on `EntityUpdateFunction` / `ComponentSystem`) so `getInitData`
|
|
205
|
+
and the `init` hook agree on its shape; it defaults to `unknown` when a system has no init data:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
interface DamageWorld extends ComponentSystemWorld {
|
|
209
|
+
damage: number
|
|
210
|
+
}
|
|
211
|
+
interface DamageInitData {
|
|
212
|
+
baseDamage: number
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const damageUpdate: EntityUpdateFunction<Components, { health: Int32Array }, DamageWorld, DamageInitData> =
|
|
216
|
+
(world, entityId, components) => { components.health[0] -= world.damage; };
|
|
217
|
+
|
|
218
|
+
// Runs once in the worker; its return is merged onto `world` before every run. `data` is typed as
|
|
219
|
+
// `DamageInitData | undefined` (undefined when the system supplies no getInitData).
|
|
220
|
+
damageUpdate.init = (data) => ({ damage: data?.baseDamage ?? 1 });
|
|
221
|
+
|
|
222
|
+
class DamageSystem extends ComponentSystem<Components, { health: Int32Array }, DamageWorld, DamageInitData> {
|
|
223
|
+
constructor(world: BaseWorld<Components>) {
|
|
224
|
+
super(world, {
|
|
225
|
+
name: 'DamageSystem',
|
|
226
|
+
required: ['health'],
|
|
227
|
+
updateFunction: damageUpdate,
|
|
228
|
+
getWorker: () => new Worker(/* your worker entry */),
|
|
229
|
+
// Runs on the main thread; its result is structured-cloned to the worker's `init`.
|
|
230
|
+
getInitData: () => ({ baseDamage: 5 }),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Unlike `addDataToWorld` (an overridable method on the system), `getInitData` is a config option — it lives in
|
|
237
|
+
the options passed to the `ComponentSystem` constructor, so a system used without a subclass can supply it
|
|
238
|
+
there directly.
|
|
239
|
+
|
|
240
|
+
**`preRun`** runs once per run, before any entity is updated, with the run's `world`, the full entity list,
|
|
241
|
+
the query results, and the same `callbacks`. Use it for setup that spans the whole batch — seeding a
|
|
242
|
+
spatial index, resetting an accumulator, or emitting a run-level event — that would be wasteful or wrong to
|
|
243
|
+
repeat inside the per-entity loop:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
damageUpdate.preRun = (world, entities, queries, callbacks) => {
|
|
247
|
+
// Runs before the per-entity pass; `entities` is everything this run will touch.
|
|
248
|
+
};
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
(An `entityRemoved(world, entityId, callbacks)` hook completes the set — it fires once per entity that left
|
|
252
|
+
the system this run, so a worker can release any per-entity state it was holding.)
|
|
253
|
+
|
|
254
|
+
### Importing in workers
|
|
255
|
+
|
|
256
|
+
Each worker entry file is bundled on its own, and a single-entry bundle cannot tree-shake this package's
|
|
257
|
+
barrel: importing `createComponentWorker` from `@daneren2005/shared-memory-ecs` drags the whole library -
|
|
258
|
+
`BaseWorld`, every system, their `@daneren2005/shared-memory-objects` dependencies - into the worker, even
|
|
259
|
+
though a worker never runs any of it (easily ~20kb of dead code per worker). Import worker-side helpers from
|
|
260
|
+
the `@daneren2005/shared-memory-ecs/worker` subpath instead. It exposes only what runs in a worker -
|
|
261
|
+
`createComponentWorker`, `createEntityWorker`, `killEntityWorker`, `DEAD_INDEX` (plus the worker-relevant
|
|
262
|
+
types) - so the bundle stays tiny:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// damage.worker.ts - the worker entry file
|
|
266
|
+
import { createComponentWorker } from '@daneren2005/shared-memory-ecs/worker';
|
|
267
|
+
import { damageUpdate } from './damage-update';
|
|
268
|
+
|
|
269
|
+
createComponentWorker(self, damageUpdate);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The same applies to any module the worker file pulls in: an update function that calls `createEntityWorker`
|
|
273
|
+
or `killEntityWorker` should import them from `/worker` too. Type-only imports (`EntityUpdateFunction`,
|
|
274
|
+
`ComponentSystemWorld`, ...) can come from either path since types are erased, and main-thread code
|
|
275
|
+
(`ComponentSystem`, `BaseWorld`, `EntityFactory`, ...) keeps importing from the package root.
|
|
276
|
+
|
|
192
277
|
### Reporting back to the main thread
|
|
193
278
|
|
|
194
279
|
An update function runs on shared memory, so anything it writes is already visible on the main thread. What
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TypedArrayConstructor } from '@daneren2005/shared-memory-objects';
|
|
1
|
+
import type { TypedArrayConstructor } from '@daneren2005/shared-memory-objects/interfaces/typed-array-constructor';
|
|
2
2
|
import type MemoryComponent from './memory-component';
|
|
3
3
|
import type { ComponentTypedArray } from './memory-component';
|
|
4
4
|
import type BaseEntity from './entity';
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//#region src/systems/workers/apply-query-delta.ts
|
|
2
|
+
function e(e, t) {
|
|
3
|
+
if (t.removed.length) {
|
|
4
|
+
let n = new Set(t.removed);
|
|
5
|
+
e = e.filter((e) => !n.has(e.entityId));
|
|
6
|
+
}
|
|
7
|
+
if (t.added.length) {
|
|
8
|
+
let n = /* @__PURE__ */ new Map();
|
|
9
|
+
e.forEach((e, t) => n.set(e.entityId, t));
|
|
10
|
+
for (let r of t.added) {
|
|
11
|
+
let t = n.get(r.entityId);
|
|
12
|
+
t === void 0 ? (n.set(r.entityId, e.length), e.push(r)) : e[t] = r;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return e;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/entity-component.ts
|
|
19
|
+
var t = 0, n = 1, r = {
|
|
20
|
+
type: Uint32Array,
|
|
21
|
+
size: 2,
|
|
22
|
+
loadProperties: ["type", "isStatic"],
|
|
23
|
+
load(e, t, n) {
|
|
24
|
+
let r = t.create([+!!n.dead, +!!n.isStatic]), i = t.getBlock(r);
|
|
25
|
+
return {
|
|
26
|
+
index: r,
|
|
27
|
+
type: n.type,
|
|
28
|
+
get dead() {
|
|
29
|
+
return i[0] === 1;
|
|
30
|
+
},
|
|
31
|
+
set dead(e) {
|
|
32
|
+
i[0] = +!!e;
|
|
33
|
+
},
|
|
34
|
+
get isStatic() {
|
|
35
|
+
return i[1] === 1;
|
|
36
|
+
},
|
|
37
|
+
set isStatic(e) {
|
|
38
|
+
i[1] = +!!e;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
save(e) {
|
|
43
|
+
let t = {};
|
|
44
|
+
return e.type && (t.type = e.type), e.dead && (t.dead = !0), t;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/actions/kill-entity-worker.ts
|
|
49
|
+
function i(e, t, n) {
|
|
50
|
+
let r = t.entity;
|
|
51
|
+
r && (r[0] = 1), n.entityDied(e);
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/actions/create-entity-worker.ts
|
|
55
|
+
function a(e, t) {
|
|
56
|
+
t.createEntity(e);
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/systems/workers/create-component-worker.ts
|
|
60
|
+
function o(t, n) {
|
|
61
|
+
let r = [], i = {}, a;
|
|
62
|
+
t.onmessage = function(o) {
|
|
63
|
+
let c = o.data;
|
|
64
|
+
if (c.type === "init") s(t, { type: "init-complete" });
|
|
65
|
+
else if (c.type === "load") a = n.init?.(c.data) ?? void 0, s(t, { type: "loaded" });
|
|
66
|
+
else if (c.type === "reset") {
|
|
67
|
+
r = [];
|
|
68
|
+
for (let e of Object.keys(i)) delete i[e];
|
|
69
|
+
a = void 0;
|
|
70
|
+
} else if (c.type === "run") {
|
|
71
|
+
a && Object.assign(c.world, a);
|
|
72
|
+
let o = performance.now(), l = [], u = {}, d = [];
|
|
73
|
+
r = e(r, c.entities);
|
|
74
|
+
let f = {};
|
|
75
|
+
Object.entries(c.queries).forEach(([t, n]) => {
|
|
76
|
+
let r = e(i[t] ?? [], n);
|
|
77
|
+
i[t] = r, f[t] = r;
|
|
78
|
+
});
|
|
79
|
+
let p = {
|
|
80
|
+
entityComponentChanged(e, t, n, r) {
|
|
81
|
+
l.push({
|
|
82
|
+
entityId: e,
|
|
83
|
+
event: "component-property-updated",
|
|
84
|
+
args: [
|
|
85
|
+
t,
|
|
86
|
+
n,
|
|
87
|
+
r
|
|
88
|
+
]
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
emitEntityEvent(e, t, ...n) {
|
|
92
|
+
l.push({
|
|
93
|
+
entityId: e,
|
|
94
|
+
event: t,
|
|
95
|
+
args: n
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
emitSystemEvent(e, t) {
|
|
99
|
+
(u[e] ?? (u[e] = [])).push(t);
|
|
100
|
+
},
|
|
101
|
+
entityDied(e) {
|
|
102
|
+
l.push({
|
|
103
|
+
entityId: e,
|
|
104
|
+
event: "death",
|
|
105
|
+
args: []
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
createEntity(e) {
|
|
109
|
+
d.push(e);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
n.preRun && n.preRun(c.world, r, f, p), r.forEach((e) => {
|
|
113
|
+
n(c.world, e.entityId, e.components, f, p);
|
|
114
|
+
}), n.entityRemoved && c.entities.removed.forEach((e) => {
|
|
115
|
+
n.entityRemoved(c.world, e, p);
|
|
116
|
+
});
|
|
117
|
+
let m = performance.now() - o;
|
|
118
|
+
s(t, {
|
|
119
|
+
type: "run-complete",
|
|
120
|
+
generation: c.generation,
|
|
121
|
+
runTime: m,
|
|
122
|
+
events: l,
|
|
123
|
+
systemEvents: u,
|
|
124
|
+
created: d
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function s(e, t) {
|
|
130
|
+
e.postMessage(t);
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { n as a, t as i, a as n, r as o, i as r, e as s, o as t };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=create-component-worker-vbX3vEia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-component-worker-vbX3vEia.js","names":[],"sources":["../src/systems/workers/apply-query-delta.ts","../src/entity-component.ts","../src/actions/kill-entity-worker.ts","../src/actions/create-entity-worker.ts","../src/systems/workers/create-component-worker.ts"],"sourcesContent":["import type { EntityUpdateComponents, QueryDelta, UpdateEntityConfigObject } from '../component-system';\n\n// Applies a run's delta to a query's persistent list: drop the entities that left, upsert those that joined or\n// changed. A steady-state run carries empty arrays and returns the list untouched. Returns the list (removals\n// rebuild it via filter), so callers must store the returned reference back.\nexport function applyQueryDelta<T extends EntityUpdateComponents>(\n\tlist: Array<UpdateEntityConfigObject<T>>,\n\tdelta: QueryDelta<T>,\n): Array<UpdateEntityConfigObject<T>> {\n\tif(delta.removed.length) {\n\t\tconst removedSet = new Set(delta.removed);\n\t\tlist = list.filter(entry => !removedSet.has(entry.entityId));\n\t}\n\n\tif(delta.added.length) {\n\t\t// Index existing members so a re-added entity replaces its entry in place instead of duplicating.\n\t\tconst indexByEid = new Map<number, number>();\n\t\tlist.forEach((entry, index) => indexByEid.set(entry.entityId, index));\n\n\t\tfor(let entity of delta.added) {\n\t\t\tconst existing = indexByEid.get(entity.entityId);\n\t\t\tif(existing !== undefined) {\n\t\t\t\tlist[existing] = entity;\n\t\t\t} else {\n\t\t\t\tindexByEid.set(entity.entityId, list.length);\n\t\t\t\tlist.push(entity);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn list;\n}\n","import type { ComponentDefinition } from './component-definition';\n\n// dead/isStatic live in the shared block (worker-visible); type is main-thread only.\nexport interface EntityComponent {\n\tindex: number\n\ttype: string\n\tdead: boolean\n\tisStatic: boolean\n}\n\nexport interface EntityComponentConfig {\n\ttype: string\n\tisStatic?: boolean\n}\n\nexport interface EntityComponentSerialization {\n\ttype?: string\n\tdead?: boolean\n}\n\nexport const DEAD_INDEX = 0;\nexport const STATIC_INDEX = 1;\n\nexport const entityDefinition: ComponentDefinition<EntityComponent, Uint32Array, EntityComponentConfig, EntityComponentSerialization> = {\n\ttype: Uint32Array,\n\tsize: 2,\n\tloadProperties: ['type', 'isStatic'],\n\tload(entity, memory, config) {\n\t\tconst index = memory.create([config.dead ? 1 : 0, config.isStatic ? 1 : 0]);\n\t\tconst block = memory.getBlock(index);\n\n\t\treturn {\n\t\t\tindex,\n\t\t\ttype: config.type,\n\t\t\tget dead() {\n\t\t\t\treturn block[DEAD_INDEX] === 1;\n\t\t\t},\n\t\t\tset dead(value: boolean) {\n\t\t\t\tblock[DEAD_INDEX] = value ? 1 : 0;\n\t\t\t},\n\t\t\tget isStatic() {\n\t\t\t\treturn block[STATIC_INDEX] === 1;\n\t\t\t},\n\t\t\tset isStatic(value: boolean) {\n\t\t\t\tblock[STATIC_INDEX] = value ? 1 : 0;\n\t\t\t},\n\t\t};\n\t},\n\tsave(component) {\n\t\tconst config: EntityComponentSerialization = {};\n\t\tif(component.type) {\n\t\t\tconfig.type = component.type;\n\t\t}\n\t\tif(component.dead) {\n\t\t\tconfig.dead = true;\n\t\t}\n\n\t\treturn config;\n\t},\n};\n","import type { ComponentTypedArray } from '../memory-component';\nimport type { ComponentSystemCallbacks, EntityUpdateComponents } from '../systems/component-system';\nimport { DEAD_INDEX } from '../entity-component';\n\n// Worker-side killEntity: flags the entity dead in its block and reports the death back via callbacks. The\n// entity component must be in the system's query for its block to be available here.\nexport default function killEntityWorker(entityId: number, components: EntityUpdateComponents, callbacks: ComponentSystemCallbacks): void {\n\tconst block = (components as { entity?: ComponentTypedArray }).entity;\n\tif(block) {\n\t\tblock[DEAD_INDEX] = 1;\n\t}\n\n\tcallbacks.entityDied(entityId);\n}\n","import type { ComponentSystemCallbacks, CreateEntityConfig } from '../systems/component-system';\n\n// Worker-side loadEntity: buffers the config through callbacks. The main thread builds it via world.loadEntity\n// once the run completes, so it first exists on the following frame.\nexport default function createEntityWorker(config: CreateEntityConfig, callbacks: ComponentSystemCallbacks): void {\n\tcallbacks.createEntity(config);\n}\n","import type ComponentWorkerMessage from './component-worker-message';\nimport type { EntityEvent, SystemEvents } from './component-worker-message';\nimport type { ComponentMap } from '../../component-definition';\nimport type { ComponentSystemCallbacks, ComponentSystemWorld, EntityQueryComponents, EntityUpdateComponents, EntityUpdateFunction, QueryDelta, UpdateEntityConfigObject } from '../component-system';\nimport { applyQueryDelta } from './apply-query-delta';\n\n// The slice of the worker global scope createComponentWorker touches. Passing `self` explicitly (rather than\n// using the global) lets runners like @vitest/web-worker, which inject `self` as a module local, drive it.\nexport interface ComponentWorkerScope {\n\tonmessage: ((e: MessageEvent) => void) | null\n\tpostMessage(message: ComponentWorkerMessage): void\n}\n\nexport default function createComponentWorker<\n\tC extends ComponentMap,\n\tT extends EntityUpdateComponents<C>,\n\tW extends ComponentSystemWorld = ComponentSystemWorld,\n\tD = unknown,\n>(scope: ComponentWorkerScope, updateFunction: EntityUpdateFunction<C, T, W, D>) {\n\t// Persistent lists, carried across runs and mutated by each run's delta (see applyQueryDelta).\n\tlet entities: Array<UpdateEntityConfigObject<T>> = [];\n\tconst queryEntities: { [key: string]: Array<UpdateEntityConfigObject<T>> } = {};\n\t// What updateFunction.init returned: persistent state (e.g. a seeded RNG) merged onto `world` each run.\n\tlet worldExtension: Partial<W> | undefined;\n\n\tscope.onmessage = function(e) {\n\t\tconst message = e.data as ComponentWorkerMessage<W, D>;\n\n\t\tif(message.type === 'init') {\n\t\t\tpostMessageTyped(scope, {\n\t\t\t\ttype: 'init-complete',\n\t\t\t});\n\t\t} else if(message.type === 'load') {\n\t\t\tworldExtension = updateFunction.init?.(message.data) ?? undefined;\n\t\t\tpostMessageTyped(scope, {\n\t\t\t\ttype: 'loaded',\n\t\t\t});\n\t\t} else if(message.type === 'reset') {\n\t\t\t// Drop the persistent lists so a reused world starts empty; worldExtension is refreshed by the next load.\n\t\t\tentities = [];\n\t\t\tfor(const key of Object.keys(queryEntities)) {\n\t\t\t\tdelete queryEntities[key];\n\t\t\t}\n\t\t\tworldExtension = undefined;\n\t\t} else if(message.type === 'run') {\n\t\t\tif(worldExtension) {\n\t\t\t\tObject.assign(message.world, worldExtension);\n\t\t\t}\n\t\t\tconst start = performance.now();\n\t\t\tlet entityEvents: Array<EntityEvent> = [];\n\t\t\tlet systemEvents: SystemEvents = {};\n\t\t\tlet createdEntities: Array<Record<string, unknown>> = [];\n\n\t\t\tentities = applyQueryDelta(entities, message.entities as QueryDelta<T>);\n\n\t\t\tlet queries: EntityQueryComponents<C> = {};\n\t\t\tObject.entries(message.queries).forEach(([queryKey, delta]) => {\n\t\t\t\tconst list = applyQueryDelta(queryEntities[queryKey] ?? [], delta as QueryDelta<T>);\n\t\t\t\tqueryEntities[queryKey] = list;\n\t\t\t\tqueries[queryKey] = list;\n\t\t\t});\n\n\t\t\tlet callbacks: ComponentSystemCallbacks<C> = {\n\t\t\t\tentityComponentChanged<K extends keyof C, P extends keyof C[K]>(entityId: number, componentName: K, prop: P, value: C[K][P]) {\n\t\t\t\t\tentityEvents.push({\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\tevent: 'component-property-updated',\n\t\t\t\t\t\targs: [componentName, prop, value],\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\temitEntityEvent(entityId: number, event: string, ...args: Array<unknown>) {\n\t\t\t\t\tentityEvents.push({\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\tevent,\n\t\t\t\t\t\targs,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\temitSystemEvent(event: string, entityId: number) {\n\t\t\t\t\t(systemEvents[event] ?? (systemEvents[event] = [])).push(entityId);\n\t\t\t\t},\n\t\t\t\tentityDied(entityId: number) {\n\t\t\t\t\tentityEvents.push({\n\t\t\t\t\t\tentityId,\n\t\t\t\t\t\tevent: 'death',\n\t\t\t\t\t\targs: [],\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tcreateEntity(config: Record<string, unknown>) {\n\t\t\t\t\tcreatedEntities.push(config);\n\t\t\t\t},\n\t\t\t};\n\t\t\tif(updateFunction.preRun) {\n\t\t\t\tupdateFunction.preRun(message.world, entities, queries, callbacks);\n\t\t\t}\n\n\t\t\tentities.forEach(entity => {\n\t\t\t\tupdateFunction(message.world, entity.entityId, entity.components, queries, callbacks);\n\t\t\t});\n\n\t\t\tif(updateFunction.entityRemoved) {\n\t\t\t\tmessage.entities.removed.forEach(entityId => {\n\t\t\t\t\tupdateFunction.entityRemoved!(message.world, entityId, callbacks);\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst runTime = performance.now() - start;\n\n\t\t\tpostMessageTyped(scope, {\n\t\t\t\ttype: 'run-complete',\n\t\t\t\tgeneration: message.generation,\n\t\t\t\trunTime,\n\t\t\t\tevents: entityEvents,\n\t\t\t\tsystemEvents,\n\t\t\t\tcreated: createdEntities,\n\t\t\t});\n\t\t}\n\t};\n}\n\nfunction postMessageTyped(scope: ComponentWorkerScope, message: ComponentWorkerMessage) {\n\tscope.postMessage(message);\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type { IterableSystemConfig } from './systems/iterable-system';
|
|
|
19
19
|
export { default as EntitySystem } from './systems/entity-system';
|
|
20
20
|
export type { EntitySystemConfig } from './systems/entity-system';
|
|
21
21
|
export { default as ComponentSystem } from './systems/component-system';
|
|
22
|
-
export type { ComponentSystemConfig, ComponentSystemQuery, ComponentSystemWorld, ComponentSystemCallbacks, CreateEntityConfig, EntityUpdateComponents, EntityQueryComponents, EntityUpdateFunction, EntityUpdatePreRunFunction, EntityRemovedFunction, UpdateEntityConfig, UpdateEntityConfigObject, } from './systems/component-system';
|
|
22
|
+
export type { ComponentSystemConfig, ComponentSystemQuery, ComponentSystemWorld, ComponentSystemCallbacks, CreateEntityConfig, EntityUpdateComponents, EntityQueryComponents, EntityUpdateFunction, EntityUpdateInitFunction, EntityUpdatePreRunFunction, EntityRemovedFunction, UpdateEntityConfig, UpdateEntityConfigObject, } from './systems/component-system';
|
|
23
23
|
export { default as WebWorker } from './systems/workers/web-worker';
|
|
24
24
|
export { default as ComponentWebWorker } from './systems/workers/component-web-worker';
|
|
25
25
|
export { default as createComponentWorker } from './systems/workers/create-component-worker';
|