@daneren2005/shared-memory-ecs 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -28
- package/dist/actions/build-worker-entity.d.ts +1 -1
- package/dist/actions/create-entity-worker.d.ts +2 -2
- package/dist/actions/kill-entity-worker.d.ts +2 -2
- package/dist/component-definition.d.ts +2 -0
- package/dist/{create-component-worker-CaB32Dgn.js → create-system-worker-BM5E-S92.js} +53 -19
- package/dist/create-system-worker-BM5E-S92.js.map +1 -0
- package/dist/entity.d.ts +1 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.js +199 -94
- package/dist/index.js.map +1 -1
- package/dist/systems/entity-system.d.ts +1 -0
- package/dist/systems/{component-system.d.ts → entity-worker-system.d.ts} +22 -18
- package/dist/systems/iterable-system.d.ts +1 -0
- package/dist/systems/system.d.ts +11 -0
- package/dist/systems/worker-system.d.ts +22 -0
- package/dist/systems/workers/apply-query-delta.d.ts +1 -1
- package/dist/systems/workers/create-entity-system-worker.d.ts +8 -0
- package/dist/systems/workers/create-system-worker.d.ts +9 -0
- package/dist/systems/workers/entity-system-web-worker.d.ts +17 -0
- package/dist/systems/workers/{component-worker-message.d.ts → entity-system-worker-message.d.ts} +10 -4
- package/dist/worker.d.ts +7 -5
- package/dist/worker.js +2 -2
- package/dist/world.d.ts +5 -4
- package/package.json +3 -2
- package/dist/create-component-worker-CaB32Dgn.js.map +0 -1
- package/dist/systems/workers/component-web-worker.d.ts +0 -17
- package/dist/systems/workers/create-component-worker.d.ts +0 -8
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import type MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';
|
|
1
2
|
import type BaseWorld from '../world';
|
|
2
3
|
import type BaseEntity from '../entity';
|
|
3
4
|
import type { BaseComponent, ComponentDefinitionMap, ComponentMap } from '../component-definition';
|
|
4
5
|
import type { ComponentTypedArray } from '../memory-component';
|
|
5
6
|
import System, { type SystemConfig } from './system';
|
|
6
|
-
import
|
|
7
|
-
export default abstract class
|
|
7
|
+
import EntitySystemWebWorker from './workers/entity-system-web-worker';
|
|
8
|
+
export default abstract class EntityWorkerSystem<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> extends System<C> {
|
|
8
9
|
entities: Map<number, BaseEntity<C>>;
|
|
9
|
-
options:
|
|
10
|
-
worker: Worker |
|
|
10
|
+
options: EntityWorkerSystemConfig<C, T, W, D>;
|
|
11
|
+
worker: Worker | EntitySystemWebWorker<C, T, W, D>;
|
|
11
12
|
isWorkerThread: boolean;
|
|
12
13
|
private initialized;
|
|
13
14
|
private initPromise;
|
|
@@ -15,10 +16,12 @@ export default abstract class ComponentSystem<C extends ComponentMap, T extends
|
|
|
15
16
|
private runCompletePromise;
|
|
16
17
|
private isRunning;
|
|
17
18
|
private generation;
|
|
18
|
-
|
|
19
|
+
protected queryEntities: {
|
|
20
|
+
[key: string]: Map<number, BaseEntity<C>>;
|
|
21
|
+
};
|
|
19
22
|
private queryDeltas;
|
|
20
23
|
addDataToWorld?(world: W): void;
|
|
21
|
-
constructor(world: BaseWorld<ComponentDefinitionMap, C>, options:
|
|
24
|
+
constructor(world: BaseWorld<ComponentDefinitionMap, C>, options: EntityWorkerSystemConfig<C, T, W, D>);
|
|
22
25
|
private initWorker;
|
|
23
26
|
init(): Promise<void> | void;
|
|
24
27
|
finishLoading(): Promise<void>;
|
|
@@ -31,11 +34,11 @@ export default abstract class ComponentSystem<C extends ComponentMap, T extends
|
|
|
31
34
|
private buildQueryDelta;
|
|
32
35
|
private buildComponents;
|
|
33
36
|
isEntityInSystem(entity: BaseEntity<C>): boolean;
|
|
34
|
-
|
|
37
|
+
protected matchesQuery(entity: BaseEntity<C>, query: EntityWorkerSystemQuery<C>): boolean;
|
|
35
38
|
private getQueryDelta;
|
|
36
39
|
private markAdded;
|
|
37
40
|
private markRemoved;
|
|
38
|
-
|
|
41
|
+
protected updateEntityList(queryName: string, list: Map<number, BaseEntity<C>>, entity: BaseEntity<C>, shouldInclude: boolean): void;
|
|
39
42
|
checkAddEntity(entity: BaseEntity<C>): boolean;
|
|
40
43
|
removeEntity(entity: BaseEntity<C>): void;
|
|
41
44
|
recheckMembership(): void;
|
|
@@ -51,15 +54,15 @@ export type EntityQueryComponents<C extends ComponentMap = ComponentMap> = {
|
|
|
51
54
|
components: EntityUpdateComponents<C>;
|
|
52
55
|
}>;
|
|
53
56
|
};
|
|
54
|
-
type EntityUpdateFunctionImpl<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends
|
|
55
|
-
export type EntityUpdateFunction<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends
|
|
57
|
+
type EntityUpdateFunctionImpl<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld> = (world: W, entityId: number, components: T, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>) => void;
|
|
58
|
+
export type EntityUpdateFunction<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> = EntityUpdateFunctionImpl<C, T, W> & {
|
|
56
59
|
preRun?: EntityUpdatePreRunFunction<C, T, W>;
|
|
57
60
|
entityRemoved?: EntityRemovedFunction<C, W>;
|
|
58
61
|
init?: EntityUpdateInitFunction<W, D>;
|
|
59
62
|
};
|
|
60
|
-
export type EntityUpdateInitFunction<W extends
|
|
61
|
-
export type EntityUpdatePreRunFunction<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends
|
|
62
|
-
export type EntityRemovedFunction<C extends ComponentMap = ComponentMap, W extends
|
|
63
|
+
export type EntityUpdateInitFunction<W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> = (data: D | undefined) => Partial<W> | void;
|
|
64
|
+
export type EntityUpdatePreRunFunction<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld> = (world: W, entities: Array<UpdateEntityConfigObject<T>>, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>) => void;
|
|
65
|
+
export type EntityRemovedFunction<C extends ComponentMap = ComponentMap, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld> = (world: W, entityId: number, callbacks: EntityWorkerSystemCallbacks<C>) => void;
|
|
63
66
|
export type UpdateEntityConfig<T extends EntityUpdateComponents = EntityUpdateComponents> = number | UpdateEntityConfigObject<T>;
|
|
64
67
|
export type UpdateEntityConfigObject<T extends EntityUpdateComponents> = {
|
|
65
68
|
entityId: number;
|
|
@@ -73,10 +76,11 @@ export interface WorkerAllocator {
|
|
|
73
76
|
allocateEid(): number;
|
|
74
77
|
allocateComponentBlock(name: string, values: Array<number>): number;
|
|
75
78
|
}
|
|
76
|
-
export interface
|
|
79
|
+
export interface EntityWorkerSystemWorld {
|
|
77
80
|
gameTime: number;
|
|
78
81
|
elapsedTime: number;
|
|
79
82
|
getString(pointer: number): string;
|
|
83
|
+
heap?: MemoryHeap;
|
|
80
84
|
allocate?: WorkerAllocator;
|
|
81
85
|
buildEntityDescriptor?(config: WorkerCreateEntityConfig): WorkerCreatedEntity;
|
|
82
86
|
}
|
|
@@ -93,27 +97,27 @@ export interface WorkerCreatedEntity {
|
|
|
93
97
|
[name: string]: number;
|
|
94
98
|
};
|
|
95
99
|
}
|
|
96
|
-
export interface
|
|
100
|
+
export interface EntityWorkerSystemCallbacks<C extends ComponentMap = ComponentMap> {
|
|
97
101
|
entityComponentChanged<K extends keyof C, P extends keyof C[K]>(entityId: number, componentName: K, prop: P, value: C[K][P]): void;
|
|
98
102
|
emitEntityEvent(entityId: number, event: string, ...args: Array<unknown>): void;
|
|
99
103
|
emitSystemEvent(event: string, entityId: number): void;
|
|
100
104
|
entityDied(entityId: number): void;
|
|
101
105
|
createEntity(entity: WorkerCreatedEntity): void;
|
|
102
106
|
}
|
|
103
|
-
export interface
|
|
107
|
+
export interface EntityWorkerSystemQuery<C extends ComponentMap = ComponentMap> {
|
|
104
108
|
required: Array<keyof C>;
|
|
105
109
|
optional?: Array<keyof C>;
|
|
106
110
|
not?: Array<keyof C>;
|
|
107
111
|
filter?: (entity: BaseEntity<C>) => boolean;
|
|
108
112
|
}
|
|
109
|
-
export interface
|
|
113
|
+
export interface EntityWorkerSystemConfig<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> extends SystemConfig, EntityWorkerSystemQuery<C> {
|
|
110
114
|
updateFunction: EntityUpdateFunction<C, T, W, D>;
|
|
111
115
|
getWorker: () => Worker;
|
|
112
116
|
forceMainThread?: boolean;
|
|
113
117
|
getInitData?: () => D;
|
|
114
118
|
createsEntities?: boolean;
|
|
115
119
|
queries?: {
|
|
116
|
-
[key: string]:
|
|
120
|
+
[key: string]: EntityWorkerSystemQuery<C>;
|
|
117
121
|
};
|
|
118
122
|
}
|
|
119
123
|
export type { BaseComponent };
|
|
@@ -14,6 +14,7 @@ export default abstract class IterableSystem<C extends ComponentMap, T> extends
|
|
|
14
14
|
isCurrentlyRunning(): boolean;
|
|
15
15
|
runIterables(iterables: Array<T>, elapsedTime: number): void;
|
|
16
16
|
beforeRunIterables(): void;
|
|
17
|
+
protected getIterableEntityId(_iterable: T): number | undefined;
|
|
17
18
|
abstract getIterables(): Array<T>;
|
|
18
19
|
abstract updateIterable(iterable: T, elapsedTime: number): void;
|
|
19
20
|
}
|
package/dist/systems/system.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export default abstract class System<C extends ComponentMap = ComponentMap> exte
|
|
|
13
13
|
finishLoading(): void | Promise<void>;
|
|
14
14
|
update(elapsedTime: number): boolean;
|
|
15
15
|
abstract run(elapsedTime: number): void;
|
|
16
|
+
protected onError(error: Error, context?: {
|
|
17
|
+
entityId?: number;
|
|
18
|
+
phase?: SystemErrorPhase;
|
|
19
|
+
}): void;
|
|
16
20
|
protected onRunFinished(): void;
|
|
17
21
|
isCurrentlyRunning(): boolean;
|
|
18
22
|
waitForRunToComplete(): void | Promise<void>;
|
|
@@ -24,3 +28,10 @@ export interface SystemConfig {
|
|
|
24
28
|
deltaBetweenRuns?: number;
|
|
25
29
|
firstRun?: boolean;
|
|
26
30
|
}
|
|
31
|
+
export type SystemErrorPhase = 'preRun' | 'update' | 'entityRemoved' | 'run' | 'died';
|
|
32
|
+
export interface SystemError {
|
|
33
|
+
system: string;
|
|
34
|
+
error: Error;
|
|
35
|
+
entityId?: number;
|
|
36
|
+
phase?: SystemErrorPhase;
|
|
37
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type BaseWorld from '../world';
|
|
2
|
+
import type BaseEntity from '../entity';
|
|
3
|
+
import type { ComponentDefinitionMap, ComponentMap } from '../component-definition';
|
|
4
|
+
import type { SystemConfig } from './system';
|
|
5
|
+
import EntityWorkerSystem, { type EntityWorkerSystemQuery, type EntityWorkerSystemWorld, type EntityUpdateComponents } from './entity-worker-system';
|
|
6
|
+
import { type WorkerSystemRunFunction } from './workers/create-system-worker';
|
|
7
|
+
export default class WorkerSystem<C extends ComponentMap, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> extends EntityWorkerSystem<C, EntityUpdateComponents<C>, W, D> {
|
|
8
|
+
constructor(world: BaseWorld<ComponentDefinitionMap, C>, options: WorkerSystemConfig<C, W, D>);
|
|
9
|
+
checkAddEntity(entity: BaseEntity<C>): boolean;
|
|
10
|
+
shouldRun(): boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface WorkerSystemConfig<C extends ComponentMap, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> extends SystemConfig {
|
|
13
|
+
updateFunction: WorkerSystemRunFunction<C, W, D>;
|
|
14
|
+
getWorker: () => Worker;
|
|
15
|
+
forceMainThread?: boolean;
|
|
16
|
+
getInitData?: () => D;
|
|
17
|
+
createsEntities?: boolean;
|
|
18
|
+
queries?: {
|
|
19
|
+
[key: string]: EntityWorkerSystemQuery<C>;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export type { WorkerSystemRunFunction };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { EntityUpdateComponents, QueryDelta, UpdateEntityConfigObject } from '../
|
|
1
|
+
import type { EntityUpdateComponents, QueryDelta, UpdateEntityConfigObject } from '../entity-worker-system';
|
|
2
2
|
export declare function applyQueryDelta<T extends EntityUpdateComponents>(list: Array<UpdateEntityConfigObject<T>>, delta: QueryDelta<T>): Array<UpdateEntityConfigObject<T>>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type EntitySystemWorkerMessage from './entity-system-worker-message';
|
|
2
|
+
import type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';
|
|
3
|
+
import type { EntityWorkerSystemWorld, EntityUpdateComponents, EntityUpdateFunction } from '../entity-worker-system';
|
|
4
|
+
export interface EntitySystemWorkerScope {
|
|
5
|
+
onmessage: ((e: MessageEvent) => void) | null;
|
|
6
|
+
postMessage(message: EntitySystemWorkerMessage): void;
|
|
7
|
+
}
|
|
8
|
+
export default function createEntitySystemWorker<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown>(scope: EntitySystemWorkerScope, updateFunction: EntityUpdateFunction<C, T, W, D>, definitions?: ComponentDefinitionMap): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type EntitySystemWorkerScope } from './create-entity-system-worker';
|
|
2
|
+
import type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';
|
|
3
|
+
import type { EntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents, EntityUpdateFunction, EntityUpdateInitFunction, EntityRemovedFunction } from '../entity-worker-system';
|
|
4
|
+
export type WorkerSystemRunFunction<C extends ComponentMap, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> = ((world: W, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>) => void) & {
|
|
5
|
+
init?: EntityUpdateInitFunction<W, D>;
|
|
6
|
+
entityRemoved?: EntityRemovedFunction<C, W>;
|
|
7
|
+
};
|
|
8
|
+
export declare function toEntityUpdateFunction<C extends ComponentMap, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown>(run: WorkerSystemRunFunction<C, W, D>): EntityUpdateFunction<C, EntityUpdateComponents<C>, W, D>;
|
|
9
|
+
export default function createSystemWorker<C extends ComponentMap, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown>(scope: EntitySystemWorkerScope, runFunction: WorkerSystemRunFunction<C, W, D>, definitions?: ComponentDefinitionMap): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import WebWorker from './web-worker';
|
|
2
|
+
import type EntitySystemWorkerMessage from './entity-system-worker-message';
|
|
3
|
+
import type BaseWorld from '../../world';
|
|
4
|
+
import type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';
|
|
5
|
+
import type { EntityWorkerSystemWorld, EntityUpdateComponents, EntityUpdateFunction } from '../entity-worker-system';
|
|
6
|
+
export default class EntitySystemWebWorker<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> extends WebWorker {
|
|
7
|
+
private updateFunction;
|
|
8
|
+
private entities;
|
|
9
|
+
private queryEntities;
|
|
10
|
+
private worldExtension;
|
|
11
|
+
private world;
|
|
12
|
+
private allocator;
|
|
13
|
+
private createsEntities;
|
|
14
|
+
constructor(updateFunction: EntityUpdateFunction<C, T, W, D>, world: BaseWorld<ComponentDefinitionMap, C>, createsEntities?: boolean);
|
|
15
|
+
postMessage(message: EntitySystemWorkerMessage<W, D>): void;
|
|
16
|
+
onMessageTyped(message: EntitySystemWorkerMessage<W, D>): void;
|
|
17
|
+
}
|
package/dist/systems/workers/{component-worker-message.d.ts → entity-system-worker-message.d.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MemoryHeapMemory, GrowBufferData } from '@daneren2005/shared-memory-objects/memory-heap';
|
|
2
2
|
import type { WorldSharedMemory } from '../../world';
|
|
3
|
-
import type {
|
|
3
|
+
import type { EntityWorkerSystemWorld, QueryDelta, WorkerCreatedEntity } from '../entity-worker-system';
|
|
4
4
|
interface InitMessage {
|
|
5
5
|
type: 'init';
|
|
6
6
|
}
|
|
@@ -30,7 +30,7 @@ interface LoadedMessage {
|
|
|
30
30
|
interface ResetMessage {
|
|
31
31
|
type: 'reset';
|
|
32
32
|
}
|
|
33
|
-
interface RunUpdateMessage<W extends
|
|
33
|
+
interface RunUpdateMessage<W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld> {
|
|
34
34
|
type: 'run';
|
|
35
35
|
generation: number;
|
|
36
36
|
world: W;
|
|
@@ -44,6 +44,11 @@ export interface EntityEvent {
|
|
|
44
44
|
event: string;
|
|
45
45
|
args: Array<unknown>;
|
|
46
46
|
}
|
|
47
|
+
export interface WorkerRunError {
|
|
48
|
+
error: Error;
|
|
49
|
+
phase: 'preRun' | 'update' | 'entityRemoved';
|
|
50
|
+
entityId?: number;
|
|
51
|
+
}
|
|
47
52
|
export type SystemEvents = {
|
|
48
53
|
[event: string]: Array<number>;
|
|
49
54
|
};
|
|
@@ -54,6 +59,7 @@ interface EntityEventsMessage {
|
|
|
54
59
|
events: Array<EntityEvent>;
|
|
55
60
|
systemEvents: SystemEvents;
|
|
56
61
|
created: Array<WorkerCreatedEntity>;
|
|
62
|
+
errors: Array<WorkerRunError>;
|
|
57
63
|
}
|
|
58
|
-
type
|
|
59
|
-
export default
|
|
64
|
+
type EntitySystemWorkerMessage<W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld, D = unknown> = InitMessage | InitCompleteMessage | LoadMessage<D> | LoadedMessage | ResetMessage | GrowBufferMessage | GrowBufferFromWorkerMessage | RunUpdateMessage<W> | EntityEventsMessage;
|
|
65
|
+
export default EntitySystemWorkerMessage;
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
export { default as
|
|
2
|
-
export type {
|
|
1
|
+
export { default as createEntitySystemWorker } from './systems/workers/create-entity-system-worker';
|
|
2
|
+
export type { EntitySystemWorkerScope } from './systems/workers/create-entity-system-worker';
|
|
3
|
+
export { default as createSystemWorker } from './systems/workers/create-system-worker';
|
|
4
|
+
export type { WorkerSystemRunFunction } from './systems/workers/create-system-worker';
|
|
3
5
|
export { default as createEntityWorker } from './actions/create-entity-worker';
|
|
4
6
|
export { default as killEntityWorker } from './actions/kill-entity-worker';
|
|
5
7
|
export { DEAD_INDEX, TYPE_INDEX } from './entity-component';
|
|
6
|
-
export type { default as
|
|
7
|
-
export type { EntityEvent, SystemEvents } from './systems/workers/
|
|
8
|
-
export type {
|
|
8
|
+
export type { default as EntitySystemWorkerMessage } from './systems/workers/entity-system-worker-message';
|
|
9
|
+
export type { EntityEvent, SystemEvents } from './systems/workers/entity-system-worker-message';
|
|
10
|
+
export type { EntityWorkerSystemWorld, EntityWorkerSystemCallbacks, WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedEntity, EntityUpdateComponents, EntityQueryComponents, EntityUpdateFunction, EntityUpdateInitFunction, EntityUpdatePreRunFunction, EntityRemovedFunction, UpdateEntityConfigObject, } from './systems/entity-worker-system';
|
package/dist/worker.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { a as e, c as t, i as n, o as r, r as i, t as a } from "./create-system-worker-BM5E-S92.js";
|
|
2
|
+
export { r as DEAD_INDEX, t as TYPE_INDEX, i as createEntitySystemWorker, n as createEntityWorker, a as createSystemWorker, e as killEntityWorker };
|
package/dist/world.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import MemoryComponent from './memory-component';
|
|
|
7
7
|
import ConstantStringCache from './constant-string-cache';
|
|
8
8
|
import BaseEntity from './entity';
|
|
9
9
|
import type System from './systems/system';
|
|
10
|
-
import type { WorkerCreatedEntity } from './systems/
|
|
10
|
+
import type { WorkerCreatedEntity } from './systems/entity-worker-system';
|
|
11
11
|
import type { ComponentDefinitionMap, ComponentMap, ComponentsOf, EntityConfigOf, RegisteredComponentDefinition, RegisteredComponentRegistry } from './component-definition';
|
|
12
12
|
import { type EntityComponent } from './entity-component';
|
|
13
13
|
import EntityFactory from './entity-factory';
|
|
@@ -49,6 +49,7 @@ export default class BaseWorld<R extends ComponentDefinitionMap = ComponentDefin
|
|
|
49
49
|
allocateEid(): number;
|
|
50
50
|
getSharedComponentMemory(): WorldSharedMemory;
|
|
51
51
|
addGrownBuffer(data: GrowBufferData): void;
|
|
52
|
+
private get needsSpareBuffer();
|
|
52
53
|
init(): Promise<void>;
|
|
53
54
|
private finishLoadingSystems;
|
|
54
55
|
addEntity(entity: BaseEntity<C, Cfg>, created?: boolean): BaseEntity<C, Cfg>;
|
|
@@ -76,8 +77,8 @@ export default class BaseWorld<R extends ComponentDefinitionMap = ComponentDefin
|
|
|
76
77
|
private runUpdate;
|
|
77
78
|
pause(): void;
|
|
78
79
|
resume(): void;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
private
|
|
80
|
+
addEntityToEntityWorkerSystem(entity: BaseEntity<C>, component: keyof C): void;
|
|
81
|
+
removeEntityFromEntityWorkerSystem(entity: BaseEntity<C>, component: keyof C): void;
|
|
82
|
+
private componentAffectsEntityWorkerSystem;
|
|
82
83
|
destroy(): void;
|
|
83
84
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@daneren2005/shared-memory-ecs",
|
|
3
3
|
"description": "A small, reusable Entity/Component/System core backed by shared memory.",
|
|
4
4
|
"author": "Scott Jackson",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.6.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"repository": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"lint": "cross-env NODE_ENV=production oxlint .",
|
|
40
40
|
"lint:fix": "cross-env NODE_ENV=production oxlint --fix .",
|
|
41
41
|
"test": "vitest run",
|
|
42
|
+
"bench": "vitest bench --run --config vitest.benchmark.config.ts",
|
|
42
43
|
"release": "semantic-release",
|
|
43
44
|
"prepare": "husky"
|
|
44
45
|
},
|
|
@@ -61,6 +62,6 @@
|
|
|
61
62
|
"vitest": "^4.1.10"
|
|
62
63
|
},
|
|
63
64
|
"peerDependencies": {
|
|
64
|
-
"@daneren2005/shared-memory-objects": "^1.
|
|
65
|
+
"@daneren2005/shared-memory-objects": "^1.3.0"
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-component-worker-CaB32Dgn.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/systems/workers/create-component-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 '../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 { WorkerAllocator, WorkerCreateEntityConfig, WorkerCreatedEntity } from '../systems/component-system';\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): WorkerCreatedEntity {\n\tconst template = factoryConfigs[config.type] ?? {};\n\tconst merged: Record<string, unknown> = { ...template, ...config };\n\n\tconst eid = allocator.allocateEid();\n\tconst components: { [name: string]: number } = {};\n\tfor(let name of Object.keys(registry)) {\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\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\tisStatic: merged.isStatic as boolean | undefined,\n\t\tcomponents,\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\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;\nexport const TYPE_INDEX = 2;\n\n// `type` is stored as a pointer to an interned ConstantString, so the accessor needs the world's string cache\n// (passed in, not captured per instance) to resolve it.\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\tset type(value: string) {\n\t\tthis.block[TYPE_INDEX] = value ? this.cache.getOrCreate(value).pointer : 0;\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, EntityComponentSerialization> = {\n\ttype: Uint32Array,\n\tsize: 3,\n\tloadProperties: ['type', 'isStatic'],\n\t// The only component that reads the entity (never worker-created): it interns its type string 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\treturn [config.dead ? 1 : 0, config.isStatic ? 1 : 0, typePointer];\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 { 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, ComponentSystemWorld, WorkerCreateEntityConfig } from '../systems/component-system';\n\n// Worker-side entity creation from a factory config. Pass `{ type: 'ship', ...overrides }`: the worker merges the\n// ship template shipped from the factory, mints a unique id, and allocates + writes every triggered component's 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 there and wrapping the\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 createComponentWorker (so the worker has each component's toBlock).\nexport default function createEntityWorker(world: ComponentSystemWorld, config: WorkerCreateEntityConfig, callbacks: ComponentSystemCallbacks): 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 createComponentWorker');\n\t}\n\n\tcallbacks.createEntity(world.buildEntityDescriptor(config));\n}\n","import MemoryHeap from '@daneren2005/shared-memory-objects/memory-heap';\nimport type AllocatedMemory from '@daneren2005/shared-memory-objects/allocated-memory';\nimport type ComponentWorkerMessage from './component-worker-message';\nimport type { EntityEvent, SystemEvents } from './component-worker-message';\nimport ConstantStringCache from '../../constant-string-cache';\nimport MemoryComponent from '../../memory-component';\nimport type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';\nimport type {\n\tComponentSystemCallbacks, ComponentSystemWorld, EntityQueryComponents, EntityUpdateComponents,\n\tEntityUpdateFunction, QueryDelta, UpdateEntityConfigObject, WorkerCreatedEntity,\n} from '../component-system';\nimport { buildWorkerEntity, type FactoryConfigs } from '../../actions/build-worker-entity';\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>, 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\tconst getString = (pointer: number): string => stringCache?.getString(pointer) ?? '';\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\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\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\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);\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\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(entity: WorkerCreatedEntity) {\n\t\t\t\t\tcreatedEntities.push(entity);\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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"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import WebWorker from './web-worker';
|
|
2
|
-
import type ComponentWorkerMessage from './component-worker-message';
|
|
3
|
-
import type BaseWorld from '../../world';
|
|
4
|
-
import type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';
|
|
5
|
-
import type { ComponentSystemWorld, EntityUpdateComponents, EntityUpdateFunction } from '../component-system';
|
|
6
|
-
export default class ComponentWebWorker<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends ComponentSystemWorld = ComponentSystemWorld, D = unknown> extends WebWorker {
|
|
7
|
-
private updateFunction;
|
|
8
|
-
private entities;
|
|
9
|
-
private queryEntities;
|
|
10
|
-
private worldExtension;
|
|
11
|
-
private world;
|
|
12
|
-
private allocator;
|
|
13
|
-
private createsEntities;
|
|
14
|
-
constructor(updateFunction: EntityUpdateFunction<C, T, W, D>, world: BaseWorld<ComponentDefinitionMap, C>, createsEntities?: boolean);
|
|
15
|
-
postMessage(message: ComponentWorkerMessage<W, D>): void;
|
|
16
|
-
onMessageTyped(message: ComponentWorkerMessage<W, D>): void;
|
|
17
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type ComponentWorkerMessage from './component-worker-message';
|
|
2
|
-
import type { ComponentDefinitionMap, ComponentMap } from '../../component-definition';
|
|
3
|
-
import type { ComponentSystemWorld, EntityUpdateComponents, EntityUpdateFunction } from '../component-system';
|
|
4
|
-
export interface ComponentWorkerScope {
|
|
5
|
-
onmessage: ((e: MessageEvent) => void) | null;
|
|
6
|
-
postMessage(message: ComponentWorkerMessage): void;
|
|
7
|
-
}
|
|
8
|
-
export default function createComponentWorker<C extends ComponentMap, T extends EntityUpdateComponents<C>, W extends ComponentSystemWorld = ComponentSystemWorld, D = unknown>(scope: ComponentWorkerScope, updateFunction: EntityUpdateFunction<C, T, W, D>, definitions?: ComponentDefinitionMap): void;
|