@daneren2005/shared-memory-ecs 1.6.0 → 1.7.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/README.md +121 -19
- package/dist/actions/add-component-worker.d.ts +3 -0
- package/dist/actions/build-worker-entity.d.ts +4 -2
- package/dist/actions/remove-component-worker.d.ts +3 -0
- package/dist/{create-system-worker-BM5E-S92.js → create-system-worker-DURlIMia.js} +121 -73
- package/dist/create-system-worker-DURlIMia.js.map +1 -0
- package/dist/entity-class.d.ts +51 -0
- package/dist/entity-component.d.ts +7 -2
- package/dist/entity-factory.d.ts +21 -5
- package/dist/entity.d.ts +10 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +352 -177
- package/dist/index.js.map +1 -1
- package/dist/node/node-worker-adapter.d.ts +11 -0
- package/dist/node/worker-self.d.ts +19 -0
- package/dist/node-worker-self.js +34 -0
- package/dist/node-worker-self.js.map +1 -0
- package/dist/node.js +20 -0
- package/dist/node.js.map +1 -0
- package/dist/systems/entity-worker-system.d.ts +19 -0
- package/dist/systems/workers/entity-system-web-worker.d.ts +2 -1
- package/dist/systems/workers/entity-system-worker-message.d.ts +5 -1
- package/dist/worker.d.ts +4 -2
- package/dist/worker.js +2 -2
- package/dist/world.d.ts +13 -13
- package/package.json +13 -1
- package/dist/create-system-worker-BM5E-S92.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A small, reusable Entity / Component / System core backed by shared memory
|
|
4
4
|
([`@daneren2005/shared-memory-objects`](https://www.npmjs.com/package/@daneren2005/shared-memory-objects)).
|
|
5
|
-
It contains only
|
|
6
|
-
|
|
5
|
+
It contains only engine-agnostic ECS concepts. Games supply their own components, entity classes, templates,
|
|
6
|
+
systems, and domain data.
|
|
7
7
|
|
|
8
8
|
## Concepts
|
|
9
9
|
|
|
@@ -23,14 +23,16 @@ fog of war, sub-classed entities, required components, etc).
|
|
|
23
23
|
- **`ComponentRegistry<C>`** – the map of all component definitions for a game.
|
|
24
24
|
- **`EntityFactory<C>`** – maps an entity `type` name to a base (template) config. Loading an entity layers
|
|
25
25
|
the caller's config over its type's template, so shared static data lives in one place and a save only
|
|
26
|
-
needs the `type` plus the entity's serialization.
|
|
26
|
+
needs the `type` plus the entity's serialization. Optionally, a template's immutable `class` selects a
|
|
27
|
+
game-provided `BaseEntity` subclass and restricts which components that template may load.
|
|
27
28
|
- **`BaseWorld<C>`** – builds one `MemoryComponent` per registered component (attached to that component's
|
|
28
29
|
definition as `world.registry[name].memoryComponent`) and runs systems. It is generic over your component
|
|
29
30
|
map `C`, so `world.registry`, `entity.components`, `setComponent`, etc. are fully typed. Its entities live
|
|
30
31
|
in `world.entities`, a `Map` keyed by `eid` — see [Iterating entities](#iterating-entities).
|
|
31
|
-
- **`BaseEntity<C>`** – an `eid`,
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
- **`BaseEntity<C>`** – an `eid`, its `id` alias, immutable `type`, `dead` state, and a bag of memory-backed
|
|
33
|
+
components. Every entity has an `entity` component whose immutable `type` and optional immutable `class`
|
|
34
|
+
are interned strings stored in shared memory. A game can subclass `BaseEntity` to expose direct convenience
|
|
35
|
+
getters for one entity class.
|
|
34
36
|
- **Systems** – `System`, `IterableSystem`, `EntitySystem` (main-thread iteration over entities with a
|
|
35
37
|
given set of components), `EntityWorkerSystem` (runs an update function over raw memory blocks,
|
|
36
38
|
off-thread when Web Workers + `SharedArrayBuffer` are available), and `WorkerSystem` (an `EntityWorkerSystem`
|
|
@@ -132,6 +134,64 @@ and the `entity` block stores only a pointer to it at `TYPE_INDEX`. Reading `ent
|
|
|
132
134
|
resolves that pointer back to the string through the world's cache; a worker can do the same (see
|
|
133
135
|
[Reading an entity's type in a worker](#reading-an-entitys-type-in-a-worker)).
|
|
134
136
|
|
|
137
|
+
### Entity classes and scoped templates
|
|
138
|
+
|
|
139
|
+
An entity `type` names one concrete template. Its optional `class` names the stable family that chooses a
|
|
140
|
+
wrapper constructor and the components that family permits. Callers still pass only `{ type, ...overrides }`:
|
|
141
|
+
the factory resolves class from the template, and neither identity can change after construction.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import {
|
|
145
|
+
BaseEntity,
|
|
146
|
+
BaseWorld,
|
|
147
|
+
EntityFactory,
|
|
148
|
+
defineEntityClasses,
|
|
149
|
+
defineEntityConfigs,
|
|
150
|
+
} from '@daneren2005/shared-memory-ecs';
|
|
151
|
+
import type { ComponentsOf, EntityConfigOf, EntityInstancesOf } from '@daneren2005/shared-memory-ecs';
|
|
152
|
+
|
|
153
|
+
const registry = {
|
|
154
|
+
health: healthDefinition,
|
|
155
|
+
value: valueDefinition,
|
|
156
|
+
};
|
|
157
|
+
type Components = ComponentsOf<typeof registry>;
|
|
158
|
+
type Config = EntityConfigOf<typeof registry>;
|
|
159
|
+
|
|
160
|
+
class ActorEntity extends BaseEntity<Components, Config> {
|
|
161
|
+
get health() { return this.components.health?.health; }
|
|
162
|
+
}
|
|
163
|
+
class ItemEntity extends BaseEntity<Components, Config> {
|
|
164
|
+
get value() { return this.components.value?.value; }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const classes = defineEntityClasses(registry, {
|
|
168
|
+
actor: { entity: ActorEntity, components: ['health'] },
|
|
169
|
+
item: { entity: ItemEntity, components: ['value'] },
|
|
170
|
+
} as const);
|
|
171
|
+
|
|
172
|
+
const templates = defineEntityConfigs(classes, {
|
|
173
|
+
guardian: { type: 'guardian', class: 'actor', maxHealth: 100 },
|
|
174
|
+
token: { type: 'token', class: 'item', value: 5 },
|
|
175
|
+
} as const);
|
|
176
|
+
|
|
177
|
+
type Entities = EntityInstancesOf<typeof classes>;
|
|
178
|
+
const factory = new EntityFactory<Components, Config, Entities>(templates, classes);
|
|
179
|
+
const world = new BaseWorld<typeof registry, Components, Config, Entities>(registry, { factory });
|
|
180
|
+
|
|
181
|
+
const item = world.loadEntity({ type: 'token' });
|
|
182
|
+
console.log(item instanceof ItemEntity); // true
|
|
183
|
+
console.log(item.components.entity.class); // "item"
|
|
184
|
+
console.log(item.id, item.type, item.dead); // BaseEntity identity accessors
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`defineEntityConfigs` checks each template against its class's component definitions, so adding a health-only
|
|
188
|
+
property to the item template is a type error. The factory repeats the boundary at runtime: loading or attaching
|
|
189
|
+
a component outside the class allowlist throws, protecting dynamically loaded data too. Client/editor metadata
|
|
190
|
+
that no component consumes should remain in a separate authoring type and be transformed into the scoped ECS
|
|
191
|
+
template.
|
|
192
|
+
|
|
193
|
+
Classless factories keep the original behavior: they create `BaseEntity` and scan the complete registry.
|
|
194
|
+
|
|
135
195
|
## Iterating entities
|
|
136
196
|
|
|
137
197
|
`world.entities` is a `Map` keyed by `eid`, not an array, and so is `entities` on `EntitySystem` and
|
|
@@ -307,7 +367,7 @@ class SpawnerSystem extends WorkerSystem<Components, SpawnerWorld> {
|
|
|
307
367
|
```
|
|
308
368
|
|
|
309
369
|
Its worker entry uses `createSystemWorker` (the run-function counterpart of `createEntitySystemWorker`),
|
|
310
|
-
passing the registry only when it creates entities:
|
|
370
|
+
passing the registry only when it creates entities or adds components:
|
|
311
371
|
|
|
312
372
|
```ts
|
|
313
373
|
import { createSystemWorker } from '@daneren2005/shared-memory-ecs/worker';
|
|
@@ -324,7 +384,8 @@ barrel: importing `createEntitySystemWorker` from `@daneren2005/shared-memory-ec
|
|
|
324
384
|
`BaseWorld`, every system, their `@daneren2005/shared-memory-objects` dependencies - into the worker, even
|
|
325
385
|
though a worker never runs any of it (easily ~20kb of dead code per worker). Import worker-side helpers from
|
|
326
386
|
the `@daneren2005/shared-memory-ecs/worker` subpath instead. It exposes only what runs in a worker -
|
|
327
|
-
`createEntitySystemWorker`, `createSystemWorker`, `createEntityWorker`, `
|
|
387
|
+
`createEntitySystemWorker`, `createSystemWorker`, `createEntityWorker`, `addComponentWorker`,
|
|
388
|
+
`removeComponentWorker`, `killEntityWorker`, `DEAD_INDEX`,
|
|
328
389
|
`TYPE_INDEX` (plus the worker-relevant types) - so the bundle stays tiny:
|
|
329
390
|
|
|
330
391
|
```ts
|
|
@@ -340,10 +401,9 @@ or `killEntityWorker` should import them from `/worker` too. Type-only imports (
|
|
|
340
401
|
`EntityWorkerSystemWorld`, ...) can come from either path since types are erased, and main-thread code
|
|
341
402
|
(`EntityWorkerSystem`, `BaseWorld`, `EntityFactory`, ...) keeps importing from the package root.
|
|
342
403
|
|
|
343
|
-
A worker that creates entities
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
whatever it imports) into that worker's bundle.
|
|
404
|
+
A worker that creates entities or adds components passes your component registry as the third argument -
|
|
405
|
+
`createEntitySystemWorker(self, shipUpdate, registry)` - so it has each component's `toBlock`. Only do this in
|
|
406
|
+
workers that need it; it pulls the registry (and whatever it imports) into that worker's bundle.
|
|
347
407
|
|
|
348
408
|
### Reading an entity's type in a worker
|
|
349
409
|
|
|
@@ -378,7 +438,8 @@ on the main thread) resolves the same way.
|
|
|
378
438
|
An update function runs on shared memory, so anything it writes is already visible on the main thread. What
|
|
379
439
|
it cannot do from there is touch the world, so the things that have to happen back on it go through
|
|
380
440
|
`callbacks`: `entityComponentChanged` (emitted on the entity as `component-property-updated`), `entityDied`
|
|
381
|
-
(as `death`),
|
|
441
|
+
(as `death`), `createEntity` (see [Creating entities from a worker](#creating-entities-from-a-worker)), and
|
|
442
|
+
component changes requested by `addComponentWorker` / `removeComponentWorker`.
|
|
382
443
|
All of them are collected during the run and applied once it completes.
|
|
383
444
|
|
|
384
445
|
`emitEntityEvent` is the escape hatch for an event of your own: name it whatever you like and give it
|
|
@@ -448,10 +509,12 @@ createEntityWorker(world, { type: 'ship', x: 100, y: 150 }, callbacks);
|
|
|
448
509
|
|
|
449
510
|
`createEntityWorker` layers your overrides over the `ship` template, mints a unique id from a shared atomic
|
|
450
511
|
counter (so it never collides with one the main thread or another worker hands out), and for each component
|
|
451
|
-
the merged config triggers, pushes a block into its pool and writes the values - all off-thread.
|
|
512
|
+
the merged config triggers, pushes a block into its pool and writes the values - all off-thread. With entity
|
|
513
|
+
classes registered, the worker also resolves the template's class and considers only its allowed components. It reports
|
|
452
514
|
back an id-plus-block-indexes descriptor; when the run completes the main thread *adopts* it, building the
|
|
453
|
-
always-present `entity` component there (interning
|
|
454
|
-
worker wrote - no block is copied or re-allocated. Like
|
|
515
|
+
always-present `entity` component there (interning `type` and `class` is a main-thread job), asking the factory
|
|
516
|
+
for the registered wrapper, and attaching each block the worker wrote - no block is copied or re-allocated. Like
|
|
517
|
+
every other worker report-back, the new entity first
|
|
455
518
|
exists on the following frame, so the system picks it up next run.
|
|
456
519
|
|
|
457
520
|
Two things make this work, both opt-in so only the workers that create entities pay for them:
|
|
@@ -483,9 +546,48 @@ const shipHealth: ComponentDefinition<HealthComponent, Int32Array, HealthConfig>
|
|
|
483
546
|
};
|
|
484
547
|
```
|
|
485
548
|
|
|
486
|
-
This runs identically on the main-thread fallback.
|
|
487
|
-
|
|
488
|
-
the
|
|
549
|
+
This runs identically on the main-thread fallback. A `loadInFinishLoading` component (one that reads other
|
|
550
|
+
entities) is skipped, since the worker has no world to read. A worker does not receive constructors; it returns
|
|
551
|
+
the serializable class key, and the main-thread factory constructs the wrapper during adoption.
|
|
552
|
+
|
|
553
|
+
### Adding and removing components at runtime
|
|
554
|
+
|
|
555
|
+
On the main thread, use the entity directly:
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
ship.loadComponent('shield', { capacity: 100 });
|
|
559
|
+
ship.removeComponent('shield');
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Both operations notify every system. Required and excluded (`not`) queries change membership as needed. An
|
|
563
|
+
entity already present through other required components is re-sent to worker systems too, so their cached
|
|
564
|
+
component bundle gains or loses an optional component on the next run.
|
|
565
|
+
|
|
566
|
+
Inside an update function, use the worker helpers instead. They record structural changes without changing the
|
|
567
|
+
component snapshot currently being iterated:
|
|
568
|
+
|
|
569
|
+
```ts
|
|
570
|
+
import { addComponentWorker, removeComponentWorker } from '@daneren2005/shared-memory-ecs/worker';
|
|
571
|
+
|
|
572
|
+
if(equippedShieldGenerator && !components.shield) {
|
|
573
|
+
addComponentWorker(world, entityId, 'shield', { capacity: 100 }, callbacks);
|
|
574
|
+
} else if(!equippedShieldGenerator && components.shield) {
|
|
575
|
+
removeComponentWorker(entityId, 'shield', callbacks);
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
The main thread applies those commands after the worker run completes, emits the normal component-added or
|
|
580
|
+
component-removed event, and queues query deltas. The originating run therefore keeps a stable snapshot and all
|
|
581
|
+
systems see the new structure no later than their next run. Removing a component uses the same deferred-free
|
|
582
|
+
safety described below.
|
|
583
|
+
|
|
584
|
+
Worker-side addition needs two opt-ins because it allocates a block:
|
|
585
|
+
|
|
586
|
+
- Set `addsComponents: true` on the `EntityWorkerSystem`.
|
|
587
|
+
- Pass the component registry to that system's `createEntitySystemWorker(self, update, registry)` call.
|
|
588
|
+
|
|
589
|
+
`addComponentWorker` calls the definition's worker-safe `toBlock(config)` and cannot add the always-present
|
|
590
|
+
`entity` component or a `loadInFinishLoading` component. Worker-side removal needs no allocation opt-in.
|
|
489
591
|
|
|
490
592
|
### Freeing component memory safely
|
|
491
593
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ComponentMap } from '../component-definition';
|
|
2
|
+
import type { EntityWorkerSystemCallbacks, EntityWorkerSystemWorld } from '../systems/entity-worker-system';
|
|
3
|
+
export default function addComponentWorker<C extends ComponentMap, K extends keyof C>(world: EntityWorkerSystemWorld, entityId: number, componentName: K, config: Record<string, unknown>, callbacks: EntityWorkerSystemCallbacks<C>): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedEntity } from '../systems/entity-worker-system';
|
|
1
|
+
import type { WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedComponent, WorkerCreatedEntity } from '../systems/entity-worker-system';
|
|
2
|
+
import type { WorkerEntityClassRegistry } from '../entity-class';
|
|
2
3
|
export interface WorkerCreatableComponent {
|
|
3
4
|
loadProperties: Array<string>;
|
|
4
5
|
loadInFinishLoading?: boolean;
|
|
@@ -10,4 +11,5 @@ export type WorkerCreateRegistry = {
|
|
|
10
11
|
export type FactoryConfigs = {
|
|
11
12
|
[type: string]: Record<string, unknown>;
|
|
12
13
|
};
|
|
13
|
-
export declare function buildWorkerEntity(config: WorkerCreateEntityConfig, factoryConfigs: FactoryConfigs, registry: WorkerCreateRegistry, allocator: WorkerAllocator): WorkerCreatedEntity;
|
|
14
|
+
export declare function buildWorkerEntity(config: WorkerCreateEntityConfig, factoryConfigs: FactoryConfigs, registry: WorkerCreateRegistry, allocator: WorkerAllocator, classes?: WorkerEntityClassRegistry): WorkerCreatedEntity;
|
|
15
|
+
export declare function buildWorkerComponent(name: string, config: Record<string, unknown>, registry: WorkerCreateRegistry, allocator: WorkerAllocator): WorkerCreatedComponent;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ComponentMap } from '../component-definition';
|
|
2
|
+
import type { EntityWorkerSystemCallbacks } from '../systems/entity-worker-system';
|
|
3
|
+
export default function removeComponentWorker<C extends ComponentMap, K extends keyof C>(entityId: number, componentName: K, callbacks: EntityWorkerSystemCallbacks<C>): void;
|
|
@@ -88,32 +88,53 @@ function o(e, t) {
|
|
|
88
88
|
}
|
|
89
89
|
//#endregion
|
|
90
90
|
//#region src/actions/build-worker-entity.ts
|
|
91
|
-
function s(e, t, n, r) {
|
|
92
|
-
let
|
|
93
|
-
|
|
91
|
+
function s(e, t, n, r, i) {
|
|
92
|
+
let a = t[e.type] ?? {};
|
|
93
|
+
if (i && Object.prototype.hasOwnProperty.call(e, "class")) throw Error("Entity class comes from its type template and cannot be supplied by a worker config");
|
|
94
|
+
let o = {
|
|
95
|
+
...a,
|
|
94
96
|
...e
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
+
}, s = typeof a.class == "string" ? a.class : void 0, c = Object.keys(n);
|
|
98
|
+
if (i) {
|
|
99
|
+
if (a.type !== e.type) throw Error(`Entity template ${e.type} must declare the same type`);
|
|
100
|
+
let t = s ? i[s] : void 0;
|
|
101
|
+
if (!t) throw Error(`Entity type ${e.type} has unknown class ${s ?? ""}`);
|
|
102
|
+
c = t.components;
|
|
103
|
+
}
|
|
104
|
+
let l = r.allocateEid(), u = {};
|
|
105
|
+
for (let e of c) {
|
|
97
106
|
if (e === "entity") continue;
|
|
98
107
|
let t = n[e];
|
|
99
|
-
t
|
|
108
|
+
if (!t) throw Error(`Entity class ${s ?? ""} contains unknown component ${e}`);
|
|
109
|
+
t.loadInFinishLoading || t.loadProperties.some((e) => e in o) && (u[e] = r.allocateComponentBlock(e, t.toBlock(o)));
|
|
100
110
|
}
|
|
101
111
|
return {
|
|
102
|
-
eid:
|
|
112
|
+
eid: l,
|
|
103
113
|
type: e.type,
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
class: s,
|
|
115
|
+
isStatic: o.isStatic,
|
|
116
|
+
components: u
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function c(e, t, n, r) {
|
|
120
|
+
if (e === "entity") throw Error("The entity component cannot be added at runtime");
|
|
121
|
+
let i = n[e];
|
|
122
|
+
if (!i) throw Error(`Unknown component: ${e}`);
|
|
123
|
+
if (i.loadInFinishLoading) throw Error(`Component ${e} requires main-thread finishLoading and cannot be added in a worker`);
|
|
124
|
+
return {
|
|
125
|
+
name: e,
|
|
126
|
+
index: r.allocateComponentBlock(e, i.toBlock(t))
|
|
106
127
|
};
|
|
107
128
|
}
|
|
108
129
|
//#endregion
|
|
109
130
|
//#region src/component.ts
|
|
110
|
-
var
|
|
131
|
+
var l = class {
|
|
111
132
|
index;
|
|
112
133
|
block;
|
|
113
134
|
constructor(e, t) {
|
|
114
135
|
this.block = e, this.index = t;
|
|
115
136
|
}
|
|
116
|
-
},
|
|
137
|
+
}, u = 0, d = 1, f = 2, p = 3, m = class extends l {
|
|
117
138
|
cache;
|
|
118
139
|
constructor(e, t, n) {
|
|
119
140
|
super(e, t), this.cache = n;
|
|
@@ -121,8 +142,8 @@ var c = class {
|
|
|
121
142
|
get type() {
|
|
122
143
|
return this.cache.getString(this.block[2]) ?? "";
|
|
123
144
|
}
|
|
124
|
-
|
|
125
|
-
|
|
145
|
+
get class() {
|
|
146
|
+
return this.cache.getString(this.block[3]) ?? "";
|
|
126
147
|
}
|
|
127
148
|
get dead() {
|
|
128
149
|
return this.block[0] === 1;
|
|
@@ -136,20 +157,21 @@ var c = class {
|
|
|
136
157
|
set isStatic(e) {
|
|
137
158
|
this.block[1] = +!!e;
|
|
138
159
|
}
|
|
139
|
-
},
|
|
160
|
+
}, h = {
|
|
140
161
|
type: Uint32Array,
|
|
141
|
-
size:
|
|
162
|
+
size: 4,
|
|
142
163
|
loadProperties: ["type", "isStatic"],
|
|
143
164
|
toBlock(e, t) {
|
|
144
|
-
let n = t.world.constantStrings, r = e.type ? n.getOrCreate(e.type).pointer : 0;
|
|
165
|
+
let n = t.world.constantStrings, r = e.type ? n.getOrCreate(e.type).pointer : 0, i = e.class ? n.getOrCreate(e.class).pointer : 0;
|
|
145
166
|
return [
|
|
146
167
|
+!!e.dead,
|
|
147
168
|
+!!e.isStatic,
|
|
148
|
-
r
|
|
169
|
+
r,
|
|
170
|
+
i
|
|
149
171
|
];
|
|
150
172
|
},
|
|
151
173
|
attach(e, t, n) {
|
|
152
|
-
return new
|
|
174
|
+
return new m(t.getBlock(n), n, e.world.constantStrings);
|
|
153
175
|
},
|
|
154
176
|
save(e) {
|
|
155
177
|
let t = {};
|
|
@@ -158,53 +180,64 @@ var c = class {
|
|
|
158
180
|
};
|
|
159
181
|
//#endregion
|
|
160
182
|
//#region src/actions/kill-entity-worker.ts
|
|
161
|
-
function
|
|
183
|
+
function g(e, t, n) {
|
|
162
184
|
let r = t.entity;
|
|
163
185
|
r && (r[0] = 1), n.entityDied(e);
|
|
164
186
|
}
|
|
165
187
|
//#endregion
|
|
166
188
|
//#region src/actions/create-entity-worker.ts
|
|
167
|
-
function
|
|
189
|
+
function _(e, t, n) {
|
|
168
190
|
if (!e.buildEntityDescriptor) throw Error("createEntityWorker requires the system to be registered with createsEntities: true and the component registry passed to createEntitySystemWorker");
|
|
169
191
|
n.createEntity(e.buildEntityDescriptor(t));
|
|
170
192
|
}
|
|
171
193
|
//#endregion
|
|
194
|
+
//#region src/actions/add-component-worker.ts
|
|
195
|
+
function v(e, t, n, r, i) {
|
|
196
|
+
if (!e.buildComponentDescriptor) throw Error("addComponentWorker requires addsComponents: true and the component registry passed to createEntitySystemWorker");
|
|
197
|
+
i.addComponent(t, e.buildComponentDescriptor(String(n), r));
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/actions/remove-component-worker.ts
|
|
201
|
+
function y(e, t, n) {
|
|
202
|
+
n.removeComponent(e, t);
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
172
205
|
//#region src/systems/workers/create-entity-system-worker.ts
|
|
173
|
-
function
|
|
174
|
-
let
|
|
175
|
-
t.onmessage = function(
|
|
176
|
-
let
|
|
177
|
-
if (
|
|
178
|
-
else if (
|
|
179
|
-
if (
|
|
206
|
+
function b(t, n, r) {
|
|
207
|
+
let l = [], u = {}, d, f, p, m = {}, h, g, _, v = !1, y = (e) => p?.getString(e) ?? "";
|
|
208
|
+
t.onmessage = function(b) {
|
|
209
|
+
let S = b.data;
|
|
210
|
+
if (S.type === "init") x(t, { type: "init-complete" });
|
|
211
|
+
else if (S.type === "load") {
|
|
212
|
+
if (S.heap && (f = new e(S.heap), p = new a(f), f.addOnGrowBufferHandlers((e) => x(t, {
|
|
180
213
|
type: "grow-buffer-from-worker",
|
|
181
214
|
buffer: e
|
|
182
|
-
}))),
|
|
183
|
-
|
|
184
|
-
for (let e of Object.keys(
|
|
185
|
-
|
|
215
|
+
}))), f && S.sharedMemory) {
|
|
216
|
+
m = {};
|
|
217
|
+
for (let e of Object.keys(S.sharedMemory.components)) m[e] = i.fromSharedMemory(f, S.sharedMemory.components[e]);
|
|
218
|
+
h = f.getSharedAlloc(S.sharedMemory.eidCounter);
|
|
186
219
|
}
|
|
187
|
-
|
|
188
|
-
} else if (
|
|
189
|
-
else if (
|
|
190
|
-
|
|
191
|
-
for (let e of Object.keys(
|
|
192
|
-
|
|
193
|
-
} else if (
|
|
194
|
-
|
|
220
|
+
g = S.factoryConfigs, _ = S.factoryClasses, v = !!S.addsComponents, d = n.init?.(S.data) ?? void 0, x(t, { type: "loaded" });
|
|
221
|
+
} else if (S.type === "grow-buffer") f && f.buffers[S.buffer.bufferPosition] === void 0 && f.addSharedBuffer(S.buffer);
|
|
222
|
+
else if (S.type === "reset") {
|
|
223
|
+
l = [];
|
|
224
|
+
for (let e of Object.keys(u)) delete u[e];
|
|
225
|
+
d = void 0;
|
|
226
|
+
} else if (S.type === "run") {
|
|
227
|
+
d && Object.assign(S.world, d), S.world.getString = y, S.world.heap = f;
|
|
195
228
|
let e = {
|
|
196
|
-
allocateEid: () =>
|
|
197
|
-
allocateComponentBlock: (e, t) =>
|
|
229
|
+
allocateEid: () => h ? Atomics.add(h.data, 0, 1) + 1 : 0,
|
|
230
|
+
allocateComponentBlock: (e, t) => m[e].create(t)
|
|
198
231
|
};
|
|
199
|
-
|
|
200
|
-
let i = performance.now(), a = [],
|
|
201
|
-
|
|
202
|
-
let
|
|
203
|
-
Object.entries(
|
|
204
|
-
let n = o(
|
|
205
|
-
|
|
232
|
+
S.world.allocate = e, g && r && (S.world.buildEntityDescriptor = (t) => s(t, g, r, e, _)), v && r && (S.world.buildComponentDescriptor = (t, n) => c(t, n, r, e));
|
|
233
|
+
let i = performance.now(), a = [], p = {}, b = [], C = [], w = [];
|
|
234
|
+
l = o(l, S.entities);
|
|
235
|
+
let T = {};
|
|
236
|
+
Object.entries(S.queries).forEach(([e, t]) => {
|
|
237
|
+
let n = o(u[e] ?? [], t);
|
|
238
|
+
u[e] = n, T[e] = n;
|
|
206
239
|
});
|
|
207
|
-
let
|
|
240
|
+
let E = {
|
|
208
241
|
entityComponentChanged(e, t, n, r) {
|
|
209
242
|
a.push({
|
|
210
243
|
entityId: e,
|
|
@@ -224,7 +257,7 @@ function g(t, n, r) {
|
|
|
224
257
|
});
|
|
225
258
|
},
|
|
226
259
|
emitSystemEvent(e, t) {
|
|
227
|
-
(
|
|
260
|
+
(p[e] ?? (p[e] = [])).push(t);
|
|
228
261
|
},
|
|
229
262
|
entityDied(e) {
|
|
230
263
|
a.push({
|
|
@@ -234,64 +267,79 @@ function g(t, n, r) {
|
|
|
234
267
|
});
|
|
235
268
|
},
|
|
236
269
|
createEntity(e) {
|
|
237
|
-
|
|
270
|
+
b.push(e);
|
|
271
|
+
},
|
|
272
|
+
addComponent(e, t) {
|
|
273
|
+
C.push({
|
|
274
|
+
type: "add",
|
|
275
|
+
entityId: e,
|
|
276
|
+
component: t
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
removeComponent(e, t) {
|
|
280
|
+
C.push({
|
|
281
|
+
type: "remove",
|
|
282
|
+
entityId: e,
|
|
283
|
+
componentName: String(t)
|
|
284
|
+
});
|
|
238
285
|
}
|
|
239
|
-
},
|
|
286
|
+
}, D = !1;
|
|
240
287
|
if (n.preRun) try {
|
|
241
|
-
n.preRun(
|
|
288
|
+
n.preRun(S.world, l, T, E);
|
|
242
289
|
} catch (e) {
|
|
243
|
-
|
|
290
|
+
D = !0, w.push({
|
|
244
291
|
error: e,
|
|
245
292
|
phase: "preRun"
|
|
246
293
|
});
|
|
247
294
|
}
|
|
248
|
-
|
|
295
|
+
D || l.forEach((e) => {
|
|
249
296
|
try {
|
|
250
|
-
n(
|
|
297
|
+
n(S.world, e.entityId, e.components, T, E);
|
|
251
298
|
} catch (t) {
|
|
252
|
-
|
|
299
|
+
w.push({
|
|
253
300
|
error: t,
|
|
254
301
|
phase: "update",
|
|
255
302
|
entityId: e.entityId
|
|
256
303
|
});
|
|
257
304
|
}
|
|
258
|
-
}), n.entityRemoved &&
|
|
305
|
+
}), n.entityRemoved && S.entities.removed.forEach((e) => {
|
|
259
306
|
try {
|
|
260
|
-
n.entityRemoved(
|
|
307
|
+
n.entityRemoved(S.world, e, E);
|
|
261
308
|
} catch (t) {
|
|
262
|
-
|
|
309
|
+
w.push({
|
|
263
310
|
error: t,
|
|
264
311
|
phase: "entityRemoved",
|
|
265
312
|
entityId: e
|
|
266
313
|
});
|
|
267
314
|
}
|
|
268
315
|
});
|
|
269
|
-
let
|
|
270
|
-
|
|
316
|
+
let O = performance.now() - i;
|
|
317
|
+
x(t, {
|
|
271
318
|
type: "run-complete",
|
|
272
|
-
generation:
|
|
273
|
-
runTime:
|
|
319
|
+
generation: S.generation,
|
|
320
|
+
runTime: O,
|
|
274
321
|
events: a,
|
|
275
|
-
systemEvents:
|
|
276
|
-
created:
|
|
277
|
-
|
|
322
|
+
systemEvents: p,
|
|
323
|
+
created: b,
|
|
324
|
+
componentChanges: C,
|
|
325
|
+
errors: w
|
|
278
326
|
});
|
|
279
327
|
}
|
|
280
328
|
};
|
|
281
329
|
}
|
|
282
|
-
function
|
|
330
|
+
function x(e, t) {
|
|
283
331
|
e.postMessage(t);
|
|
284
332
|
}
|
|
285
333
|
//#endregion
|
|
286
334
|
//#region src/systems/workers/create-system-worker.ts
|
|
287
|
-
function
|
|
335
|
+
function S(e) {
|
|
288
336
|
let t = (() => {});
|
|
289
337
|
return t.preRun = (t, n, r, i) => e(t, r, i), e.init && (t.init = e.init), e.entityRemoved && (t.entityRemoved = e.entityRemoved), t;
|
|
290
338
|
}
|
|
291
|
-
function
|
|
292
|
-
|
|
339
|
+
function C(e, t, n) {
|
|
340
|
+
b(e, S(t), n);
|
|
293
341
|
}
|
|
294
342
|
//#endregion
|
|
295
|
-
export {
|
|
343
|
+
export { a as _, v as a, p as c, f as d, h as f, o as g, s as h, y as i, u as l, c as m, S as n, _ as o, l as p, b as r, g as s, C as t, d as u, i as v };
|
|
296
344
|
|
|
297
|
-
//# sourceMappingURL=create-system-worker-
|
|
345
|
+
//# sourceMappingURL=create-system-worker-DURlIMia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-system-worker-DURlIMia.js","names":[],"sources":["../src/memory-component.ts","../src/constant-string-cache.ts","../src/systems/workers/apply-query-delta.ts","../src/actions/build-worker-entity.ts","../src/component.ts","../src/entity-component.ts","../src/actions/kill-entity-worker.ts","../src/actions/create-entity-worker.ts","../src/actions/add-component-worker.ts","../src/actions/remove-component-worker.ts","../src/systems/workers/create-entity-system-worker.ts","../src/systems/workers/create-system-worker.ts"],"sourcesContent":["import SharedPool from '@daneren2005/shared-memory-objects/shared-pool';\nimport type { SharedPoolMemory } from '@daneren2005/shared-memory-objects/shared-pool';\nimport type MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';\nimport type { TypedArrayConstructor } from '@daneren2005/shared-memory-objects/interfaces/typed-array-constructor';\n\nexport type ComponentTypedArray = Uint32Array | Int32Array | Float32Array | Float64Array;\n\n// Backed by a SharedPool: all bookkeeping lives in the shared heap, so a worker can reconstruct a handle over\n// the same pool (pass the owner's getSharedMemory() as `memory`) and allocate/read blocks off-thread.\nexport default class MemoryComponent<T extends ComponentTypedArray = ComponentTypedArray> {\n\theap: MemoryHeap;\n\tpool: SharedPool<T>;\n\n\tconstructor(heap: MemoryHeap, type: TypedArrayConstructor<T>, dataLength: number, memory?: SharedPoolMemory) {\n\t\tthis.heap = heap;\n\t\tthis.pool = memory\n\t\t\t? new SharedPool<T>(heap, memory)\n\t\t\t: new SharedPool<T>(heap, {\n\t\t\t\ttype,\n\t\t\t\tdataLength,\n\t\t\t});\n\t}\n\n\t// Reconstructs a handle over an existing pool (from the owner's getSharedMemory()) — used in a worker, where the\n\t// pool's type/dataLength are already recorded in the heap header, so no definition is needed.\n\tstatic fromSharedMemory<T extends ComponentTypedArray = ComponentTypedArray>(heap: MemoryHeap, memory: SharedPoolMemory): MemoryComponent<T> {\n\t\treturn new MemoryComponent<T>(heap, Uint32Array as unknown as TypedArrayConstructor<T>, 1, memory);\n\t}\n\n\tgetSharedMemory(): SharedPoolMemory {\n\t\treturn this.pool.getSharedMemory();\n\t}\n\n\tget length() {\n\t\treturn this.pool.length;\n\t}\n\tget rawLength() {\n\t\treturn this.pool.bufferLength;\n\t}\n\n\tcreate(values: Array<number>): number {\n\t\treturn this.pool.push(values);\n\t}\n\n\tgetBlock(index: number): T {\n\t\treturn this.pool.at(index);\n\t}\n\tget(index: number, dataIndex: number): number {\n\t\treturn this.pool.get(index, dataIndex);\n\t}\n\tset(index: number, dataIndex: number, value: number) {\n\t\tlet array = this.pool.at(index);\n\t\tarray[dataIndex] = value;\n\t}\n\n\tdelete(index: number) {\n\t\tthis.pool.deleteIndex(index);\n\t}\n\tclear() {\n\t\tthis.pool.clear();\n\t}\n}\n","import ConstantString from '@daneren2005/shared-memory-objects/constant-string';\nimport { getPointer } from '@daneren2005/shared-memory-objects/utils/pointer';\nimport type MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';\n\n// Interns immutable strings in the heap and resolves them back from a pointer. Every distinct value is allocated\n// once as a single ConstantString shared by all users (15 \"Space Ship\" entities point at one allocation), and a\n// pointer resolves to its string through a Map hit before ever rebuilding it from memory. Both sides of the\n// worker boundary hold one: the main thread creates + interns; a worker (with a heap reconstructed from the same\n// SharedArrayBuffers) only ever resolves pointers and caches the results.\nexport default class ConstantStringCache {\n\tprivate heap: MemoryHeap;\n\t// value -> the one interned allocation. Only the creating (main) thread populates this; it owns the memory.\n\tprivate byValue = new Map<string, ConstantString>();\n\t// pointer -> value: the fast lookup every thread checks before touching memory.\n\tprivate byPointer = new Map<number, string>();\n\n\tconstructor(heap: MemoryHeap) {\n\t\tthis.heap = heap;\n\t}\n\n\t// Dedupes on value, so repeated types share a single ConstantString. Main-thread only.\n\tgetOrCreate(value: string): ConstantString {\n\t\tlet existing = this.byValue.get(value);\n\t\tif(existing) {\n\t\t\treturn existing;\n\t\t}\n\n\t\tconst string = new ConstantString(this.heap, value);\n\t\tthis.byValue.set(value, string);\n\t\tthis.byPointer.set(string.pointer, value);\n\n\t\treturn string;\n\t}\n\n\t// pointer -> string, checking the cache before rebuilding from memory. A pointer of 0 is the empty string; a\n\t// pointer into a buffer that has not synced to this thread yet returns undefined.\n\tgetString(pointer: number): string | undefined {\n\t\tif(pointer === 0) {\n\t\t\treturn '';\n\t\t}\n\n\t\tlet cached = this.byPointer.get(pointer);\n\t\tif(cached !== undefined) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst memory = getPointer(pointer);\n\t\tif(this.heap.buffers[memory.bufferPosition] === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst value = new ConstantString(this.heap, memory).value;\n\t\tthis.byPointer.set(pointer, value);\n\n\t\treturn value;\n\t}\n\n\t// Frees every allocation this cache owns and empties the lookups. A worker's cache owns nothing (it only ever\n\t// resolved views), so this just drops its pointer lookups.\n\tclear() {\n\t\tthis.byValue.forEach(string => string.free());\n\t\tthis.byValue.clear();\n\t\tthis.byPointer.clear();\n\t}\n}\n","import type { EntityUpdateComponents, QueryDelta, UpdateEntityConfigObject } from '../entity-worker-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 { WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedComponent, WorkerCreatedEntity } from '../systems/entity-worker-system';\nimport type { WorkerEntityClassRegistry } from '../entity-class';\n\n// The slice of a component definition worker-side creation needs. Both the game's ComponentDefinitionMap and the\n// world's RegisteredComponentRegistry satisfy it.\nexport interface WorkerCreatableComponent {\n\tloadProperties: Array<string>\n\tloadInFinishLoading?: boolean\n\ttoBlock(config: Record<string, unknown>): Array<number>\n}\nexport type WorkerCreateRegistry = { [name: string]: WorkerCreatableComponent };\nexport type FactoryConfigs = { [type: string]: Record<string, unknown> };\n\n// Turns a factory-style config ({ type, ...overrides }) into a WorkerCreatedEntity, off-thread: layers the type's\n// factory template under the overrides, mints an id, and allocates + writes each triggered component's block via its\n// toBlock(). The always-present `entity` component is NOT built here - the main thread builds it on adopt (interning\n// the type is main-thread-only), reading `type`/`isStatic` off this descriptor. Shared by the real worker and the\n// main-thread fallback so both behave identically.\nexport function buildWorkerEntity(\n\tconfig: WorkerCreateEntityConfig,\n\tfactoryConfigs: FactoryConfigs,\n\tregistry: WorkerCreateRegistry,\n\tallocator: WorkerAllocator,\n\tclasses?: WorkerEntityClassRegistry,\n): WorkerCreatedEntity {\n\tconst template = factoryConfigs[config.type] ?? {};\n\tif(classes && Object.prototype.hasOwnProperty.call(config, 'class')) {\n\t\tthrow new Error('Entity class comes from its type template and cannot be supplied by a worker config');\n\t}\n\tconst merged: Record<string, unknown> = { ...template, ...config };\n\tconst entityClass = typeof template.class === 'string' ? template.class : undefined;\n\tlet componentNames = Object.keys(registry);\n\tif(classes) {\n\t\tif(template.type !== config.type) {\n\t\t\tthrow new Error(`Entity template ${config.type} must declare the same type`);\n\t\t}\n\t\tconst definition = entityClass ? classes[entityClass] : undefined;\n\t\tif(!definition) {\n\t\t\tthrow new Error(`Entity type ${config.type} has unknown class ${entityClass ?? ''}`);\n\t\t}\n\t\tcomponentNames = definition.components;\n\t}\n\n\tconst eid = allocator.allocateEid();\n\tconst components: { [name: string]: number } = {};\n\tfor(let name of componentNames) {\n\t\t// The entity component is always built on the main thread when the descriptor is adopted (interning the type),\n\t\t// never off-thread - skip it here whether or not the registry includes it.\n\t\tif(name === 'entity') {\n\t\t\tcontinue;\n\t\t}\n\t\tconst definition = registry[name];\n\t\tif(!definition) {\n\t\t\tthrow new Error(`Entity class ${entityClass ?? ''} contains unknown component ${name}`);\n\t\t}\n\t\t// Deferred components need the whole world (other entities) that a worker doesn't have; skip them.\n\t\tif(definition.loadInFinishLoading) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(definition.loadProperties.some(prop => prop in merged)) {\n\t\t\tcomponents[name] = allocator.allocateComponentBlock(name, definition.toBlock(merged));\n\t\t}\n\t}\n\n\treturn {\n\t\teid,\n\t\ttype: config.type,\n\t\tclass: entityClass,\n\t\tisStatic: merged.isStatic as boolean | undefined,\n\t\tcomponents,\n\t};\n}\n\nexport function buildWorkerComponent(\n\tname: string,\n\tconfig: Record<string, unknown>,\n\tregistry: WorkerCreateRegistry,\n\tallocator: WorkerAllocator,\n): WorkerCreatedComponent {\n\tif(name === 'entity') {\n\t\tthrow new Error('The entity component cannot be added at runtime');\n\t}\n\tconst definition = registry[name];\n\tif(!definition) {\n\t\tthrow new Error(`Unknown component: ${name}`);\n\t}\n\tif(definition.loadInFinishLoading) {\n\t\tthrow new Error(`Component ${name} requires main-thread finishLoading and cannot be added in a worker`);\n\t}\n\n\treturn {\n\t\tname,\n\t\tindex: allocator.allocateComponentBlock(name, definition.toBlock(config)),\n\t};\n}\n","import type { ComponentTypedArray } from './memory-component';\nimport type { BaseComponent } from './component-definition';\n\n// Base class for a memory-backed component accessor. Subclass it, declare prototype get/set accessors over\n// `this.block` indexed by the component's exported *_INDEX constants, and return `new YourComponent(block, index)`\n// from `attach`. Because the accessors live on one shared prototype - not fresh closures captured per entity -\n// reads off thousands of entities every frame stay monomorphic and inline, and constructing a component allocates\n// just the instance instead of a closure per accessor. Components that own no extra memory need nothing else; a\n// subclass that does can take more constructor args (the entity, another pool) and store them as fields.\nexport default abstract class Component<T extends ComponentTypedArray = ComponentTypedArray> implements BaseComponent {\n\treadonly index: number;\n\treadonly block: T;\n\n\tconstructor(block: T, index: number) {\n\t\tthis.block = block;\n\t\tthis.index = index;\n\t}\n}\n","import type { ComponentDefinition } from './component-definition';\nimport Component from './component';\nimport type ConstantStringCache from './constant-string-cache';\n\nexport interface EntityComponent {\n\tindex: number\n\treadonly type: string\n\treadonly class: string\n\tdead: boolean\n\tisStatic: boolean\n}\n\nexport interface EntityComponentConfig {\n\ttype: string\n\tisStatic?: boolean\n}\n\nexport interface EntityClassConfig {\n\treadonly class: string\n}\n\nexport interface EntityComponentSerialization {\n\ttype?: string\n\tdead?: boolean\n}\n\nexport const DEAD_INDEX = 0;\nexport const STATIC_INDEX = 1;\nexport const TYPE_INDEX = 2;\nexport const CLASS_INDEX = 3;\n\n// Identity strings are interned, so the accessor needs the world's cache to resolve their pointers.\nclass EntityComponentImpl extends Component<Uint32Array> implements EntityComponent {\n\tprivate cache: ConstantStringCache;\n\n\tconstructor(block: Uint32Array, index: number, cache: ConstantStringCache) {\n\t\tsuper(block, index);\n\t\tthis.cache = cache;\n\t}\n\n\tget type() {\n\t\treturn this.cache.getString(this.block[TYPE_INDEX]) ?? '';\n\t}\n\tget class() {\n\t\treturn this.cache.getString(this.block[CLASS_INDEX]) ?? '';\n\t}\n\tget dead() {\n\t\treturn this.block[DEAD_INDEX] === 1;\n\t}\n\tset dead(value: boolean) {\n\t\tthis.block[DEAD_INDEX] = value ? 1 : 0;\n\t}\n\tget isStatic() {\n\t\treturn this.block[STATIC_INDEX] === 1;\n\t}\n\tset isStatic(value: boolean) {\n\t\tthis.block[STATIC_INDEX] = value ? 1 : 0;\n\t}\n}\n\nexport const entityDefinition: ComponentDefinition<EntityComponent, Uint32Array, EntityComponentConfig & Partial<EntityClassConfig>, EntityComponentSerialization> = {\n\ttype: Uint32Array,\n\tsize: 4,\n\tloadProperties: ['type', 'isStatic'],\n\t// The only component that reads the entity (never worker-created): it interns its identity strings through the heap and\n\t// stores the pointer. `entity` is always passed here since loadComponent runs on the main thread.\n\ttoBlock(config, entity) {\n\t\tconst cache = entity!.world.constantStrings;\n\t\tconst typePointer = config.type ? cache.getOrCreate(config.type).pointer : 0;\n\t\tconst classPointer = config.class ? cache.getOrCreate(config.class).pointer : 0;\n\t\treturn [config.dead ? 1 : 0, config.isStatic ? 1 : 0, typePointer, classPointer];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new EntityComponentImpl(memory.getBlock(index), index, entity.world.constantStrings);\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 { EntityWorkerSystemCallbacks, EntityUpdateComponents } from '../systems/entity-worker-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: EntityWorkerSystemCallbacks): 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 { EntityWorkerSystemCallbacks, EntityWorkerSystemWorld, WorkerCreateEntityConfig } from '../systems/entity-worker-system';\n\n// Worker-side entity creation from a factory config. The worker merges the selected type template, resolves its\n// optional class, mints a unique id, and allocates + writes every allowed, triggered component block\n// directly into the shared pools (off-thread) via each component's toBlock(). It reports the descriptor back; the main\n// thread adopts it on run-complete (world.adoptEntity), building the `entity` component and class wrapper there\n// worker-written blocks, so the entity first exists on the following frame.\n//\n// Requires the system to be registered with `createsEntities: true` and its worker entry to pass the component\n// registry to createEntitySystemWorker (so the worker has each component's toBlock).\nexport default function createEntityWorker(world: EntityWorkerSystemWorld, config: WorkerCreateEntityConfig, callbacks: EntityWorkerSystemCallbacks): void {\n\tif(!world.buildEntityDescriptor) {\n\t\tthrow new Error('createEntityWorker requires the system to be registered with createsEntities: true and the component registry passed to createEntitySystemWorker');\n\t}\n\n\tcallbacks.createEntity(world.buildEntityDescriptor(config));\n}\n","import type { ComponentMap } from '../component-definition';\nimport type { EntityWorkerSystemCallbacks, EntityWorkerSystemWorld } from '../systems/entity-worker-system';\n\n// Allocates a component block in the worker and asks the main thread to attach it after the run. The entity's\n// current query snapshot is intentionally unchanged until the next run.\nexport default function addComponentWorker<C extends ComponentMap, K extends keyof C>(\n\tworld: EntityWorkerSystemWorld,\n\tentityId: number,\n\tcomponentName: K,\n\tconfig: Record<string, unknown>,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n): void {\n\tif(!world.buildComponentDescriptor) {\n\t\tthrow new Error('addComponentWorker requires addsComponents: true and the component registry passed to createEntitySystemWorker');\n\t}\n\n\tcallbacks.addComponent(entityId, world.buildComponentDescriptor(String(componentName), config));\n}\n","import type { ComponentMap } from '../component-definition';\nimport type { EntityWorkerSystemCallbacks } from '../systems/entity-worker-system';\n\n// Requests a main-thread detach after the worker run. Shared memory is freed only after every system that could\n// still hold the old block has completed a run.\nexport default function removeComponentWorker<C extends ComponentMap, K extends keyof C>(\n\tentityId: number,\n\tcomponentName: K,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n): void {\n\tcallbacks.removeComponent(entityId, componentName);\n}\n","import MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';\nimport type AllocatedMemory from '@daneren2005/shared-memory-objects/allocated-memory';\nimport type EntitySystemWorkerMessage from './entity-system-worker-message';\nimport type { EntityEvent, SystemEvents, WorkerRunError } from './entity-system-worker-message';\nimport ConstantStringCache from '../../constant-string-cache';\nimport MemoryComponent from '../../memory-component';\nimport type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';\nimport type {\n\tEntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents,\n\tEntityUpdateFunction, QueryDelta, UpdateEntityConfigObject, WorkerComponentChange, WorkerCreatedEntity,\n} from '../entity-worker-system';\nimport { buildWorkerComponent, buildWorkerEntity, type FactoryConfigs } from '../../actions/build-worker-entity';\nimport type { WorkerEntityClassRegistry } from '../../entity-class';\nimport { applyQueryDelta } from './apply-query-delta';\n\n// The slice of the worker global scope createEntitySystemWorker 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 EntitySystemWorkerScope {\n\tonmessage: ((e: MessageEvent) => void) | null\n\tpostMessage(message: EntitySystemWorkerMessage): void\n}\n\nexport default function createEntitySystemWorker<\n\tC extends ComponentMap,\n\tT extends EntityUpdateComponents<C>,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n>(scope: EntitySystemWorkerScope, updateFunction: EntityUpdateFunction<C, T, W, D>, definitions?: ComponentDefinitionMap) {\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\tlet heap: MemoryHeap | undefined;\n\tlet stringCache: ConstantStringCache | undefined;\n\t// Reconstructed pool handles over the world's shared state, plus the factory templates - for off-thread entity\n\t// allocation. `definitions` (passed by the game's worker entry) supplies each component's toBlock/loadProperties.\n\tlet pools: { [name: string]: MemoryComponent } = {};\n\tlet eidCounter: AllocatedMemory | undefined;\n\tlet factoryConfigs: FactoryConfigs | undefined;\n\tlet factoryClasses: WorkerEntityClassRegistry | undefined;\n\tlet addsComponents = false;\n\tconst getString = (pointer: number): string => stringCache?.getString(pointer) ?? '';\n\n\tscope.onmessage = function(e) {\n\t\tconst message = e.data as EntitySystemWorkerMessage<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\tif(message.heap) {\n\t\t\t\theap = new MemoryHeap(message.heap);\n\t\t\t\tstringCache = new ConstantStringCache(heap);\n\t\t\t\t// Report any buffer this worker grows (while allocating off-thread) back to the main thread, which adopts\n\t\t\t\t// it and fans it out to sibling workers.\n\t\t\t\theap.addOnGrowBufferHandlers(buffer => postMessageTyped(scope, { type: 'grow-buffer-from-worker', buffer }));\n\t\t\t}\n\t\t\tif(heap && message.sharedMemory) {\n\t\t\t\tpools = {};\n\t\t\t\tfor(const name of Object.keys(message.sharedMemory.components)) {\n\t\t\t\t\tpools[name] = MemoryComponent.fromSharedMemory(heap, message.sharedMemory.components[name]);\n\t\t\t\t}\n\t\t\t\teidCounter = heap.getSharedAlloc(message.sharedMemory.eidCounter);\n\t\t\t}\n\t\t\tfactoryConfigs = message.factoryConfigs;\n\t\t\tfactoryClasses = message.factoryClasses;\n\t\t\taddsComponents = !!message.addsComponents;\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 === 'grow-buffer') {\n\t\t\t// Never replace a buffer this worker already holds: the main thread fans a worker-grown buffer back out to\n\t\t\t// every worker (including the one that grew it), and overwriting our own live MemoryBuffer would discard its\n\t\t\t// allocation bookkeeping. The SharedArrayBuffer at that position is already the same one.\n\t\t\tif(heap && heap.buffers[message.buffer.bufferPosition] === undefined) {\n\t\t\t\theap.addSharedBuffer(message.buffer);\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\tmessage.world.getString = getString;\n\t\t\tmessage.world.heap = heap;\n\t\t\tconst allocator = {\n\t\t\t\tallocateEid: () => eidCounter ? Atomics.add(eidCounter.data, 0, 1) + 1 : 0,\n\t\t\t\tallocateComponentBlock: (name: string, values: Array<number>) => pools[name].create(values),\n\t\t\t};\n\t\t\tmessage.world.allocate = allocator;\n\t\t\t// Only a system registered with createsEntities (factoryConfigs shipped) whose worker entry passed the\n\t\t\t// component definitions can create entities from a config.\n\t\t\tif(factoryConfigs && definitions) {\n\t\t\t\tmessage.world.buildEntityDescriptor = config => buildWorkerEntity(config, factoryConfigs!, definitions, allocator, factoryClasses);\n\t\t\t}\n\t\t\tif(addsComponents && definitions) {\n\t\t\t\tmessage.world.buildComponentDescriptor = (name, config) => buildWorkerComponent(name, config, definitions, allocator);\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<WorkerCreatedEntity> = [];\n\t\t\tlet componentChanges: Array<WorkerComponentChange> = [];\n\t\t\tlet errors: Array<WorkerRunError> = [];\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: EntityWorkerSystemCallbacks<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(entity: WorkerCreatedEntity) {\n\t\t\t\t\tcreatedEntities.push(entity);\n\t\t\t\t},\n\t\t\t\taddComponent(entityId, component) {\n\t\t\t\t\tcomponentChanges.push({ type: 'add', entityId, component });\n\t\t\t\t},\n\t\t\t\tremoveComponent(entityId, componentName) {\n\t\t\t\t\tcomponentChanges.push({ type: 'remove', entityId, componentName: String(componentName) });\n\t\t\t\t},\n\t\t\t};\n\t\t\tlet preRunFailed = false;\n\t\t\tif(updateFunction.preRun) {\n\t\t\t\ttry {\n\t\t\t\t\tupdateFunction.preRun(message.world, entities, queries, callbacks);\n\t\t\t\t} catch(err) {\n\t\t\t\t\tpreRunFailed = true;\n\t\t\t\t\terrors.push({ error: err as Error, phase: 'preRun' });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// preRun sets up the run's state; if it threw, skip the entity loop rather than run over half-prepared data.\n\t\t\tif(!preRunFailed) {\n\t\t\t\tentities.forEach(entity => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tupdateFunction(message.world, entity.entityId, entity.components, queries, callbacks);\n\t\t\t\t\t} catch(err) {\n\t\t\t\t\t\t// One entity failing must not stop the rest of the run.\n\t\t\t\t\t\terrors.push({ error: err as Error, phase: 'update', entityId: entity.entityId });\n\t\t\t\t\t}\n\t\t\t\t});\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\ttry {\n\t\t\t\t\t\tupdateFunction.entityRemoved!(message.world, entityId, callbacks);\n\t\t\t\t\t} catch(err) {\n\t\t\t\t\t\terrors.push({ error: err as Error, phase: 'entityRemoved', entityId });\n\t\t\t\t\t}\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\tcomponentChanges,\n\t\t\t\terrors,\n\t\t\t});\n\t\t}\n\t};\n}\n\nfunction postMessageTyped(scope: EntitySystemWorkerScope, message: EntitySystemWorkerMessage) {\n\tscope.postMessage(message);\n}\n","import createEntitySystemWorker, { type EntitySystemWorkerScope } from './create-entity-system-worker';\nimport type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';\nimport type {\n\tEntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents,\n\tEntityUpdateFunction, EntityUpdateInitFunction, EntityRemovedFunction,\n} from '../entity-worker-system';\n\n// A WorkerSystem's single per-run function: called once per run over the named sub-queries, not once per entity.\n// Optional `init`/`entityRemoved` mirror EntityUpdateFunction's hooks.\nexport type WorkerSystemRunFunction<\n\tC extends ComponentMap,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n> = ((world: W, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>) => void) & {\n\tinit?: EntityUpdateInitFunction<W, D>\n\tentityRemoved?: EntityRemovedFunction<C, W>\n};\n\n// A WorkerSystem reuses the EntityWorkerSystem worker machinery: the single run function becomes `preRun` (which runs\n// once with query access) and the per-entity body is a no-op, since a WorkerSystem never populates its main query.\nexport function toEntityUpdateFunction<\n\tC extends ComponentMap,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n>(run: WorkerSystemRunFunction<C, W, D>): EntityUpdateFunction<C, EntityUpdateComponents<C>, W, D> {\n\tconst updateFunction = (() => {}) as EntityUpdateFunction<C, EntityUpdateComponents<C>, W, D>;\n\tupdateFunction.preRun = (world, _entities, queries, callbacks) => run(world, queries, callbacks);\n\tif(run.init) {\n\t\tupdateFunction.init = run.init;\n\t}\n\tif(run.entityRemoved) {\n\t\tupdateFunction.entityRemoved = run.entityRemoved;\n\t}\n\n\treturn updateFunction;\n}\n\n// Worker entry helper for a WorkerSystem (mirrors createEntitySystemWorker). Pass the component registry only for a\n// worker that creates entities.\nexport default function createSystemWorker<\n\tC extends ComponentMap,\n\tW extends EntityWorkerSystemWorld = EntityWorkerSystemWorld,\n\tD = unknown,\n>(scope: EntitySystemWorkerScope, runFunction: WorkerSystemRunFunction<C, W, D>, definitions?: ComponentDefinitionMap) {\n\tcreateEntitySystemWorker(scope, toEntityUpdateFunction(runFunction), definitions);\n}\n"],"mappings":"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"}
|