@vworlds/vecs 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +6 -2
- package/package.json +6 -2
- package/.claude/settings.json +0 -12
- package/.devcontainer/devcontainer.json +0 -22
- package/.github/workflows/publish.yml +0 -32
- package/src/component.ts +0 -180
- package/src/entity.ts +0 -276
- package/src/index.ts +0 -6
- package/src/phase.ts +0 -49
- package/src/system.ts +0 -693
- package/src/util/array_map.ts +0 -93
- package/src/util/bitset.ts +0 -199
- package/src/util/events.ts +0 -95
- package/src/util/ordered_set.ts +0 -82
- package/src/world.ts +0 -534
- package/tests/_helpers.ts +0 -30
- package/tests/array_map.test.ts +0 -68
- package/tests/bitset.test.ts +0 -127
- package/tests/component.test.ts +0 -104
- package/tests/entity.test.ts +0 -179
- package/tests/events.test.ts +0 -48
- package/tests/ordered_set.test.ts +0 -153
- package/tests/setup.ts +0 -6
- package/tests/system.test.ts +0 -800
- package/tests/world.test.ts +0 -174
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -9
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vworlds/vecs",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -21,5 +21,9 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"eventemitter3": "^4.0.7"
|
|
23
23
|
},
|
|
24
|
-
"license": "UNLICENSED"
|
|
24
|
+
"license": "UNLICENSED",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/vworlds/vecs"
|
|
28
|
+
}
|
|
25
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vworlds/vecs",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -21,5 +21,9 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"eventemitter3": "^4.0.7"
|
|
23
23
|
},
|
|
24
|
-
"license": "UNLICENSED"
|
|
24
|
+
"license": "UNLICENSED",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/vworlds/vecs"
|
|
28
|
+
}
|
|
25
29
|
}
|
package/.claude/settings.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vecs",
|
|
3
|
-
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20",
|
|
4
|
-
"remoteUser": "node",
|
|
5
|
-
"postCreateCommand": "yarn install",
|
|
6
|
-
"customizations": {
|
|
7
|
-
"vscode": {
|
|
8
|
-
"extensions": [
|
|
9
|
-
"dbaeumer.vscode-eslint",
|
|
10
|
-
"esbenp.prettier-vscode",
|
|
11
|
-
"anthropic.claude-code"
|
|
12
|
-
],
|
|
13
|
-
"settings": {
|
|
14
|
-
"claudeCode.allowDangerouslySkipPermissions": true
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"mounts": [
|
|
19
|
-
// Claude Code auth — mounts ~/.claude from the host so login carries over seamlessly
|
|
20
|
-
"source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind,consistency=cached"
|
|
21
|
-
]
|
|
22
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
name: Publish to npm
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- master
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
publish:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
permissions:
|
|
12
|
-
id-token: write
|
|
13
|
-
contents: read
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/checkout@v4
|
|
16
|
-
|
|
17
|
-
- uses: actions/setup-node@v4
|
|
18
|
-
with:
|
|
19
|
-
node-version: '20'
|
|
20
|
-
registry-url: 'https://registry.npmjs.org'
|
|
21
|
-
|
|
22
|
-
- name: Install dependencies
|
|
23
|
-
run: yarn install --frozen-lockfile
|
|
24
|
-
|
|
25
|
-
- name: Run tests
|
|
26
|
-
run: yarn test
|
|
27
|
-
|
|
28
|
-
- name: Build
|
|
29
|
-
run: yarn build
|
|
30
|
-
|
|
31
|
-
- name: Publish
|
|
32
|
-
run: yarn publish --access public
|
package/src/component.ts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { BitPtr, Bitset } from "./util/bitset.js";
|
|
2
|
-
import type { Entity } from "./entity.js";
|
|
3
|
-
import { type World } from "./world.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Lifecycle hook for a component type. Obtained via {@link World.hook}.
|
|
7
|
-
*
|
|
8
|
-
* Hooks let you react to component lifecycle events without building a full
|
|
9
|
-
* {@link System}. Each call returns the same `Hook` so the methods can be
|
|
10
|
-
* chained:
|
|
11
|
-
*
|
|
12
|
-
* ```ts
|
|
13
|
-
* world.hook(Sprite)
|
|
14
|
-
* .onAdd(c => initSprite(c))
|
|
15
|
-
* .onRemove(c => destroySprite(c))
|
|
16
|
-
* .onSet(c => syncSprite(c));
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* Callbacks are invoked synchronously during {@link World.runPhase} when
|
|
20
|
-
* archetype changes are flushed.
|
|
21
|
-
*
|
|
22
|
-
* @typeParam C - The `Component` subclass this hook is bound to.
|
|
23
|
-
*/
|
|
24
|
-
export interface Hook<C extends Component = Component> {
|
|
25
|
-
/**
|
|
26
|
-
* Register a callback that fires when a component of this type is added to
|
|
27
|
-
* an entity.
|
|
28
|
-
*
|
|
29
|
-
* @param handler - Receives the newly created component instance.
|
|
30
|
-
* @returns `this` for chaining.
|
|
31
|
-
*/
|
|
32
|
-
onAdd(handler: (c: C) => void): Hook<C>;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Register a callback that fires when a component of this type is removed
|
|
36
|
-
* from an entity (including when the entity is destroyed).
|
|
37
|
-
*
|
|
38
|
-
* @param handler - Receives the component instance being removed.
|
|
39
|
-
* @returns `this` for chaining.
|
|
40
|
-
*/
|
|
41
|
-
onRemove(handler: (c: C) => void): Hook<C>;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Register a callback that fires when {@link Component.modified} is called
|
|
45
|
-
* on a component of this type.
|
|
46
|
-
*
|
|
47
|
-
* @param handler - Receives the component instance that changed.
|
|
48
|
-
* @returns `this` for chaining.
|
|
49
|
-
*/
|
|
50
|
-
onSet(handler: (c: C) => void): Hook<C>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Internal bookkeeping record for a registered component class.
|
|
55
|
-
*
|
|
56
|
-
* Every component class that is passed to {@link World.registerComponent} gets
|
|
57
|
-
* a `ComponentMeta` that maps it to a numeric type id, a string name, and a
|
|
58
|
-
* pre-computed {@link BitPtr} used for fast archetype checks.
|
|
59
|
-
*
|
|
60
|
-
* `ComponentMeta` also implements {@link Hook}, so you can attach lifecycle
|
|
61
|
-
* callbacks directly on the meta object (as `World.hook()` returns it).
|
|
62
|
-
*/
|
|
63
|
-
export class ComponentMeta implements Hook<Component> {
|
|
64
|
-
/** The component class constructor. */
|
|
65
|
-
public readonly Class: typeof Component;
|
|
66
|
-
/** Numeric type id assigned at registration time. */
|
|
67
|
-
public readonly type: number;
|
|
68
|
-
/** Human-readable name used in logs and serialization lookups. */
|
|
69
|
-
public readonly componentName: string;
|
|
70
|
-
/** Pre-computed bit-pointer into the entity archetype {@link Bitset}. */
|
|
71
|
-
public readonly bitPtr: BitPtr;
|
|
72
|
-
private onAddHandler: ((c: Component) => void) | undefined;
|
|
73
|
-
private onRemoveHandler: ((c: Component) => void) | undefined;
|
|
74
|
-
private onSetHandler: ((c: Component) => void) | undefined;
|
|
75
|
-
|
|
76
|
-
constructor(Class: typeof Component, type: number, componentName: string) {
|
|
77
|
-
this.Class = Class;
|
|
78
|
-
this.type = type;
|
|
79
|
-
this.componentName = componentName;
|
|
80
|
-
this.bitPtr = new BitPtr(type);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** @inheritdoc */
|
|
84
|
-
public onAdd(handler: (c: Component) => void): ComponentMeta {
|
|
85
|
-
this.onAddHandler = handler;
|
|
86
|
-
return this;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** @inheritdoc */
|
|
90
|
-
public onRemove(handler: (c: Component) => void): ComponentMeta {
|
|
91
|
-
this.onRemoveHandler = handler;
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** @inheritdoc */
|
|
96
|
-
public onSet(handler: (c: Component) => void): ComponentMeta {
|
|
97
|
-
this.onSetHandler = handler;
|
|
98
|
-
return this;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** A component class constructor or its numeric type id. */
|
|
103
|
-
export type ComponentClassOrType = number | typeof Component;
|
|
104
|
-
|
|
105
|
-
/** An array of component class constructors or type ids. */
|
|
106
|
-
export type ComponentClassArray = ComponentClassOrType[];
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Base class for all ECS components.
|
|
110
|
-
*
|
|
111
|
-
* Extend this class to define data that can be attached to an {@link Entity}:
|
|
112
|
-
*
|
|
113
|
-
* ```ts
|
|
114
|
-
* class Position extends Component {
|
|
115
|
-
* x = 0;
|
|
116
|
-
* y = 0;
|
|
117
|
-
* }
|
|
118
|
-
*
|
|
119
|
-
* world.registerComponent(Position);
|
|
120
|
-
* const pos = entity.add(Position);
|
|
121
|
-
* pos.x = 100;
|
|
122
|
-
* pos.modified(); // notify watching systems
|
|
123
|
-
* ```
|
|
124
|
-
*
|
|
125
|
-
* A component instance is always bound to a single entity and is created by
|
|
126
|
-
* the world when {@link Entity.add} is called.
|
|
127
|
-
*/
|
|
128
|
-
export class Component {
|
|
129
|
-
private dirty: boolean = false;
|
|
130
|
-
|
|
131
|
-
constructor(
|
|
132
|
-
/** The entity this component belongs to. */
|
|
133
|
-
public readonly entity: Entity,
|
|
134
|
-
/** Registration metadata (type id, name, bit-pointer). */
|
|
135
|
-
public readonly meta: ComponentMeta
|
|
136
|
-
) {}
|
|
137
|
-
|
|
138
|
-
/** Numeric type id — shorthand for `this.meta.type`. */
|
|
139
|
-
public get type(): number {
|
|
140
|
-
return this.meta.type;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** Pre-computed bit-pointer — shorthand for `this.meta.bitPtr`. */
|
|
144
|
-
public get bitPtr(): BitPtr {
|
|
145
|
-
return this.meta.bitPtr;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Notify the world that this component's data has changed.
|
|
150
|
-
*
|
|
151
|
-
* Queues the component for delivery to all {@link System.update} callbacks
|
|
152
|
-
* that watch this component type. Call this after mutating the component's
|
|
153
|
-
* fields to ensure systems react to the new values.
|
|
154
|
-
*/
|
|
155
|
-
public modified() {
|
|
156
|
-
this.entity.world._queueUpdatedComponent(this);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** Returns the component's registered name, e.g. `"Position"`. */
|
|
160
|
-
public toString(): string {
|
|
161
|
-
return this.meta.componentName;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Compute a {@link Bitset} that has a bit set for every component class or
|
|
167
|
-
* type id in `classes`.
|
|
168
|
-
*
|
|
169
|
-
* @internal Used internally to build archetype masks for system queries.
|
|
170
|
-
*/
|
|
171
|
-
export function calculateComponentBitmask(
|
|
172
|
-
classes: ComponentClassArray,
|
|
173
|
-
world: World
|
|
174
|
-
) {
|
|
175
|
-
const bitmask = new Bitset();
|
|
176
|
-
classes.forEach((C) => {
|
|
177
|
-
bitmask.add(world.getComponentType(C));
|
|
178
|
-
});
|
|
179
|
-
return bitmask;
|
|
180
|
-
}
|
package/src/entity.ts
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
import { Component } from "./component.js";
|
|
2
|
-
import type { World } from "./world.js";
|
|
3
|
-
import { ArrayMap } from "./util/array_map.js";
|
|
4
|
-
import { type System } from "./system.js";
|
|
5
|
-
import { Events } from "./util/events.js";
|
|
6
|
-
import { Bitset } from "./util/bitset.js";
|
|
7
|
-
|
|
8
|
-
type EntityEvents = Events<{ destroy(): void }>;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A game object — a unique identifier with an arbitrary set of
|
|
12
|
-
* {@link Component | components} attached to it.
|
|
13
|
-
*
|
|
14
|
-
* You never construct an `Entity` directly. Use {@link World.createEntity} for
|
|
15
|
-
* locally-owned entities or {@link World.getOrCreateEntity} when the id is
|
|
16
|
-
* assigned by an external authority (e.g. the server):
|
|
17
|
-
*
|
|
18
|
-
* ```ts
|
|
19
|
-
* const e = world.createEntity();
|
|
20
|
-
* const pos = e.add(Position);
|
|
21
|
-
* pos.x = 100;
|
|
22
|
-
* pos.modified();
|
|
23
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* Entities support a parent–child hierarchy. When a parent is destroyed its
|
|
26
|
-
* children are destroyed recursively. The `children` set is created lazily.
|
|
27
|
-
*/
|
|
28
|
-
export class Entity {
|
|
29
|
-
private components = new ArrayMap<Component>(); //maps component types to Components
|
|
30
|
-
private deletedComponents = new ArrayMap<Component>(); //maps deleted component types to Components
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Bitmask representing the set of component types currently attached to this
|
|
34
|
-
* entity. Used by the world to efficiently match entities against system
|
|
35
|
-
* queries.
|
|
36
|
-
*/
|
|
37
|
-
public readonly componentBitmask = new Bitset();
|
|
38
|
-
private readonly systems = new Set<System>();
|
|
39
|
-
private readonly newSystems: System[] = [];
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* A free-form property bag that modules can use to associate arbitrary data
|
|
43
|
-
* with an entity without registering a component.
|
|
44
|
-
*/
|
|
45
|
-
public properties = new Map<string, any>();
|
|
46
|
-
public declare _events: EntityEvents;
|
|
47
|
-
|
|
48
|
-
/** Parent entity in the scene hierarchy, or `undefined` if root. */
|
|
49
|
-
public parent: Entity | undefined;
|
|
50
|
-
private _children: Set<Entity> | undefined;
|
|
51
|
-
public _archetypeChanged: boolean = false;
|
|
52
|
-
private destroyed = false;
|
|
53
|
-
|
|
54
|
-
constructor(
|
|
55
|
-
/** The {@link World} that owns this entity. */
|
|
56
|
-
public readonly world: World,
|
|
57
|
-
/** Unique numeric entity id assigned at creation time. */
|
|
58
|
-
public readonly eid: number
|
|
59
|
-
) {}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* The set of direct child entities in the scene hierarchy.
|
|
63
|
-
*
|
|
64
|
-
* The set is created lazily on first access. Mutate it only through
|
|
65
|
-
* {@link Entity.destroy} or by setting {@link Entity.parent} on a child —
|
|
66
|
-
* both will keep the parent–child links consistent.
|
|
67
|
-
*/
|
|
68
|
-
public get children(): Set<Entity> {
|
|
69
|
-
if (!this._children) this._children = new Set<Entity>();
|
|
70
|
-
return this._children;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Add a component of type `Class` to this entity and return the instance.
|
|
75
|
-
*
|
|
76
|
-
* If the component is already present the existing instance is returned and
|
|
77
|
-
* no callback is fired. Pass `markAsModified = false` to suppress the
|
|
78
|
-
* initial `onSet` / `update` notification (useful when bulk-loading
|
|
79
|
-
* network snapshots before systems are running).
|
|
80
|
-
*
|
|
81
|
-
* @param Class - The component class to instantiate.
|
|
82
|
-
* @param markAsModified - Whether to immediately queue an `update`
|
|
83
|
-
* notification. Defaults to `true`.
|
|
84
|
-
* @returns The new (or existing) component instance, typed as
|
|
85
|
-
* `InstanceType<Class>`.
|
|
86
|
-
*/
|
|
87
|
-
public add<C extends typeof Component>(
|
|
88
|
-
Class: C,
|
|
89
|
-
markAsModified?: boolean
|
|
90
|
-
): InstanceType<C>;
|
|
91
|
-
/**
|
|
92
|
-
* Add a component by its numeric type id.
|
|
93
|
-
*
|
|
94
|
-
* @param type - Numeric component type id (as returned by
|
|
95
|
-
* {@link World.getComponentType}).
|
|
96
|
-
* @param markAsModified - Whether to queue an update notification.
|
|
97
|
-
*/
|
|
98
|
-
public add(type: number, markAsModified?: boolean): Component;
|
|
99
|
-
public add(
|
|
100
|
-
typeOrClass: number | typeof Component,
|
|
101
|
-
markAsModified: boolean = true
|
|
102
|
-
) {
|
|
103
|
-
const type = this.world.getComponentType(typeOrClass);
|
|
104
|
-
|
|
105
|
-
let c = this.components.get(type);
|
|
106
|
-
if (c) {
|
|
107
|
-
return c;
|
|
108
|
-
}
|
|
109
|
-
c = this.world["getComponentInstance"](typeOrClass, this);
|
|
110
|
-
|
|
111
|
-
this.components.set(type, c);
|
|
112
|
-
this.componentBitmask.add(type);
|
|
113
|
-
this.world._notifyComponentAdded(this, c);
|
|
114
|
-
if (markAsModified) this.world._queueUpdatedComponent(c);
|
|
115
|
-
|
|
116
|
-
return c;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Remove the component of the given class from this entity.
|
|
121
|
-
*
|
|
122
|
-
* The `onRemove` hook and any `exit` callbacks on matching systems are
|
|
123
|
-
* called when archetype changes are flushed at the end of the next system
|
|
124
|
-
* run. Does nothing if the component is not present.
|
|
125
|
-
*
|
|
126
|
-
* @param Class - The component class to remove.
|
|
127
|
-
*/
|
|
128
|
-
public remove<C extends typeof Component>(Class: C): void;
|
|
129
|
-
/**
|
|
130
|
-
* Remove a component by its numeric type id.
|
|
131
|
-
*
|
|
132
|
-
* @param type - Numeric component type id.
|
|
133
|
-
*/
|
|
134
|
-
public remove(type: number): void;
|
|
135
|
-
public remove(typeOrClass: number | typeof Component): void {
|
|
136
|
-
const type = this.world.getComponentType(typeOrClass);
|
|
137
|
-
const c = this.components.get(type);
|
|
138
|
-
if (c) {
|
|
139
|
-
this.components.delete(type);
|
|
140
|
-
this.deletedComponents.set(type, c);
|
|
141
|
-
this.componentBitmask.delete(type);
|
|
142
|
-
this.world._notifyComponentRemoved(this, c);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** @internal Called by systems to deliver update notifications. */
|
|
147
|
-
public _notifyModified(component: Component) {
|
|
148
|
-
this.systems.forEach((s) => {
|
|
149
|
-
s.notifyModified(component);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Retrieve the component of type `Class`, or `undefined` if not present.
|
|
155
|
-
*
|
|
156
|
-
* @param typeOrClass - Component class or numeric type id.
|
|
157
|
-
* @param get_deleted - If `true`, also search components that were removed
|
|
158
|
-
* in the current frame but not yet garbage-collected. Useful inside
|
|
159
|
-
* `exit` callbacks to read final component values.
|
|
160
|
-
* @returns The component instance or `undefined`.
|
|
161
|
-
*/
|
|
162
|
-
public get<C extends typeof Component>(
|
|
163
|
-
typeOrClass: number | C,
|
|
164
|
-
get_deleted: boolean = false
|
|
165
|
-
): InstanceType<C> | undefined {
|
|
166
|
-
const type = this.world.getComponentType(typeOrClass);
|
|
167
|
-
|
|
168
|
-
const c = this.components.get(type);
|
|
169
|
-
if (!c && get_deleted) {
|
|
170
|
-
return this.deletedComponents.get(type) as InstanceType<C> | undefined;
|
|
171
|
-
}
|
|
172
|
-
return c as InstanceType<C> | undefined;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Typed event emitter for entity-level lifecycle events.
|
|
177
|
-
*
|
|
178
|
-
* Currently emits one event:
|
|
179
|
-
* - `"destroy"` — fired just before the entity is fully torn down.
|
|
180
|
-
*
|
|
181
|
-
* The emitter is created lazily on first access.
|
|
182
|
-
*/
|
|
183
|
-
public get events(): EntityEvents {
|
|
184
|
-
if (!this._events) {
|
|
185
|
-
this._events = new Events();
|
|
186
|
-
}
|
|
187
|
-
return this._events;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/** @internal */
|
|
191
|
-
public _hasSystem(s: System) {
|
|
192
|
-
return this.systems.has(s);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** @internal */
|
|
196
|
-
public _addSystem(s: System) {
|
|
197
|
-
if (!this.systems.has(s)) {
|
|
198
|
-
this.newSystems.push(s);
|
|
199
|
-
s._enter(this);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** @internal */
|
|
204
|
-
public _removeSystem(s: System) {
|
|
205
|
-
if (this.systems.delete(s)) {
|
|
206
|
-
s._exit(this);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** @internal */
|
|
211
|
-
public _updateSystems() {
|
|
212
|
-
this.newSystems.forEach((s) => {
|
|
213
|
-
this.systems.add(s);
|
|
214
|
-
});
|
|
215
|
-
this.newSystems.length = 0;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** `true` when the entity has no components attached. */
|
|
219
|
-
public get empty() {
|
|
220
|
-
return this.components.size == 0;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
private _destroy() {
|
|
224
|
-
if (this.destroyed) return;
|
|
225
|
-
this.destroyed = true;
|
|
226
|
-
this.systems.forEach((s) => {
|
|
227
|
-
s._exit(this);
|
|
228
|
-
});
|
|
229
|
-
this.systems.clear();
|
|
230
|
-
|
|
231
|
-
if (this._events) {
|
|
232
|
-
this._events.emit("destroy");
|
|
233
|
-
this._events.removeAllListeners("destroy");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Destroy this entity and recursively destroy all of its children.
|
|
239
|
-
*
|
|
240
|
-
* All components are removed (triggering `onRemove` hooks and `exit`
|
|
241
|
-
* callbacks), the entity is unregistered from the world, and the `"destroy"`
|
|
242
|
-
* event is emitted. The entity must not be used after calling this method.
|
|
243
|
-
*/
|
|
244
|
-
public destroy() {
|
|
245
|
-
this.world._notifyEntityDestroyed(this);
|
|
246
|
-
this.children.forEach((child) => {
|
|
247
|
-
child.destroy();
|
|
248
|
-
});
|
|
249
|
-
this.children.clear();
|
|
250
|
-
if (this.parent) {
|
|
251
|
-
this.parent.children.delete(this);
|
|
252
|
-
this.world.archetypeChanged(this.parent);
|
|
253
|
-
this.parent = undefined;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/** @internal */
|
|
258
|
-
public clearDeletedComponents() {
|
|
259
|
-
this.deletedComponents.clear();
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Iterate over every component currently attached to this entity.
|
|
264
|
-
*
|
|
265
|
-
* @param callback - Called with each component instance. Iteration order is
|
|
266
|
-
* not guaranteed.
|
|
267
|
-
*/
|
|
268
|
-
public forEachComponent(callback: (c: Component) => void) {
|
|
269
|
-
this.components.forEach(callback);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/** Returns `"EntityN"` where N is the entity id. */
|
|
273
|
-
public toString(): string {
|
|
274
|
-
return `Entity${this.eid}`;
|
|
275
|
-
}
|
|
276
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { type System } from "./system.js";
|
|
2
|
-
export { World } from "./world.js";
|
|
3
|
-
export { Component, type ComponentMeta } from "./component.js";
|
|
4
|
-
export { type Entity } from "./entity.js";
|
|
5
|
-
export { type IPhase } from "./phase.js";
|
|
6
|
-
export { Bitset } from "./util/bitset.js";
|
package/src/phase.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { type System } from "./system.js";
|
|
2
|
-
import { type World } from "./world.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A named, ordered bucket of {@link System | systems} within the world's
|
|
6
|
-
* update pipeline.
|
|
7
|
-
*
|
|
8
|
-
* Created internally by {@link World.addPhase}. The systems in a phase run in
|
|
9
|
-
* the order they were registered. Between each system run the world flushes
|
|
10
|
-
* pending archetype changes, so `enter` / `exit` callbacks are always
|
|
11
|
-
* delivered before the next system executes.
|
|
12
|
-
*
|
|
13
|
-
* @internal The concrete class is not part of the public API. Use
|
|
14
|
-
* {@link IPhase} to refer to phases in user code.
|
|
15
|
-
*/
|
|
16
|
-
export class Phase {
|
|
17
|
-
/** Systems that belong to this phase, in execution order. */
|
|
18
|
-
public systems: System[] = [];
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
/** Name used to look up the phase in the pipeline. */
|
|
22
|
-
public readonly name: string,
|
|
23
|
-
public world: World
|
|
24
|
-
) {}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Public interface for a pipeline phase returned by {@link World.addPhase}.
|
|
29
|
-
*
|
|
30
|
-
* Pass an `IPhase` to {@link System.phase} to assign a system to that phase,
|
|
31
|
-
* or to {@link World.runPhase} to execute it:
|
|
32
|
-
*
|
|
33
|
-
* ```ts
|
|
34
|
-
* const preUpdate = world.addPhase("preupdate");
|
|
35
|
-
* const send = world.addPhase("send");
|
|
36
|
-
*
|
|
37
|
-
* world.system("NetworkUpdate").phase(preUpdate).run(tick);
|
|
38
|
-
*
|
|
39
|
-
* // each frame:
|
|
40
|
-
* world.runPhase(preUpdate, now, delta);
|
|
41
|
-
* world.runPhase(send, now, delta);
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
export interface IPhase {
|
|
45
|
-
/** The name this phase was registered under. */
|
|
46
|
-
get name(): string;
|
|
47
|
-
/** The world that owns this phase. */
|
|
48
|
-
get world(): World;
|
|
49
|
-
}
|