archetype-ecs 1.2.0 → 1.3.2
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 +126 -63
- package/bench/allocations-1m.js +1 -1
- package/bench/multi-ecs-bench.js +1 -1
- package/bench/typed-vs-bitecs-1m.js +1 -1
- package/bench/typed-vs-untyped.js +81 -81
- package/bench/vs-bitecs.js +31 -56
- package/dist/ComponentRegistry.d.ts +18 -0
- package/dist/ComponentRegistry.js +29 -0
- package/dist/EntityManager.d.ts +52 -0
- package/dist/EntityManager.js +891 -0
- package/dist/Profiler.d.ts +12 -0
- package/dist/Profiler.js +38 -0
- package/dist/System.d.ts +41 -0
- package/dist/System.js +159 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +29 -0
- package/dist/src/ComponentRegistry.d.ts +18 -0
- package/dist/src/ComponentRegistry.js +29 -0
- package/dist/src/EntityManager.d.ts +49 -0
- package/dist/src/EntityManager.js +853 -0
- package/dist/src/Profiler.d.ts +12 -0
- package/dist/src/Profiler.js +38 -0
- package/dist/src/System.d.ts +37 -0
- package/dist/src/System.js +139 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +29 -0
- package/dist/tests/EntityManager.test.d.ts +1 -0
- package/dist/tests/EntityManager.test.js +651 -0
- package/dist/tests/System.test.d.ts +1 -0
- package/dist/tests/System.test.js +630 -0
- package/dist/tests/types.d.ts +1 -0
- package/dist/tests/types.js +129 -0
- package/package.json +9 -7
- package/src/ComponentRegistry.ts +49 -0
- package/src/EntityManager.ts +1018 -0
- package/src/{Profiler.js → Profiler.ts} +18 -5
- package/src/System.ts +226 -0
- package/src/index.ts +44 -0
- package/tests/{EntityManager.test.js → EntityManager.test.ts} +338 -70
- package/tests/System.test.ts +730 -0
- package/tests/types.ts +67 -66
- package/tsconfig.json +8 -5
- package/tsconfig.test.json +13 -0
- package/.claude/settings.local.json +0 -32
- package/src/ComponentRegistry.js +0 -21
- package/src/EntityManager.js +0 -462
- package/src/index.d.ts +0 -111
- package/src/index.js +0 -37
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Compile-time type tests — run with: npx tsc --noEmit
|
|
2
|
+
// These tests validate TS types flow correctly through the API.
|
|
3
|
+
// No runtime execution needed; if this file compiles, the types are correct.
|
|
4
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
5
|
+
var useValue = arguments.length > 2;
|
|
6
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
7
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
8
|
+
}
|
|
9
|
+
return useValue ? value : void 0;
|
|
10
|
+
};
|
|
11
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
12
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
13
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
14
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
15
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
16
|
+
var _, done = false;
|
|
17
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
18
|
+
var context = {};
|
|
19
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
20
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
21
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
22
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
23
|
+
if (kind === "accessor") {
|
|
24
|
+
if (result === void 0) continue;
|
|
25
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
26
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
27
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
28
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
29
|
+
}
|
|
30
|
+
else if (_ = accept(result)) {
|
|
31
|
+
if (kind === "field") initializers.unshift(_);
|
|
32
|
+
else descriptor[key] = _;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
36
|
+
done = true;
|
|
37
|
+
};
|
|
38
|
+
import { createEntityManager, createSystem, createSystems, component, System, OnAdded, OnRemoved } from '../src/index.js';
|
|
39
|
+
// --- component() creates ComponentDef ---
|
|
40
|
+
const Position = component('Position', 'f32', ['x', 'y']);
|
|
41
|
+
const Velocity = component('Velocity', { vx: 'f32', vy: 'f32' });
|
|
42
|
+
const Tag = component('Tag');
|
|
43
|
+
const Name = component('Name', { name: 'string', title: 'string' });
|
|
44
|
+
const Label = component('Label', 'string', ['text', 'color']);
|
|
45
|
+
// component() returns ComponentDef
|
|
46
|
+
const _assertComp = Position;
|
|
47
|
+
const _assertTag = Tag;
|
|
48
|
+
// --- EntityManager typed methods ---
|
|
49
|
+
const em = createEntityManager();
|
|
50
|
+
const id = em.createEntity();
|
|
51
|
+
// addComponent: accepts data
|
|
52
|
+
em.addComponent(id, Position, { x: 1, y: 2 });
|
|
53
|
+
em.addComponent(id, Velocity, { vx: 0, vy: 0 });
|
|
54
|
+
em.addComponent(id, Name, { name: 'Hero', title: 'Sir' });
|
|
55
|
+
// getComponent: returns data or undefined
|
|
56
|
+
const pos = em.getComponent(id, Position);
|
|
57
|
+
// get/set: field descriptor access
|
|
58
|
+
const px = em.get(id, Position.x);
|
|
59
|
+
em.set(id, Position.x, 10);
|
|
60
|
+
// forEach + field: accepts FieldRef
|
|
61
|
+
em.forEach([Position, Velocity], (arch) => {
|
|
62
|
+
const count = arch.count;
|
|
63
|
+
const ids = arch.entityIds;
|
|
64
|
+
const arrX = arch.field(Position.x);
|
|
65
|
+
});
|
|
66
|
+
// createEntityWith: alternating type, data
|
|
67
|
+
em.createEntityWith(Position, { x: 0, y: 0 }, Velocity, { vx: 1, vy: 1 });
|
|
68
|
+
// --- createSystem returns FunctionalSystem ---
|
|
69
|
+
const sys = createSystem(em, (s) => {
|
|
70
|
+
// onAdded with 1 type
|
|
71
|
+
s.onAdded(Position, (entityId) => { });
|
|
72
|
+
// onAdded with 2 types
|
|
73
|
+
s.onAdded(Position, Velocity, (entityId) => { });
|
|
74
|
+
// onRemoved with 1 type
|
|
75
|
+
s.onRemoved(Position, (entityId) => { });
|
|
76
|
+
// onRemoved with 2 types
|
|
77
|
+
s.onRemoved(Position, Velocity, (entityId) => { });
|
|
78
|
+
return () => {
|
|
79
|
+
s.forEach([Position, Velocity], (view) => {
|
|
80
|
+
const count = view.count;
|
|
81
|
+
});
|
|
82
|
+
s.forEach([Position], (_view) => { }, [Velocity]);
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
sys();
|
|
86
|
+
sys.dispose();
|
|
87
|
+
// --- FunctionalSystemConstructor type ---
|
|
88
|
+
const ctor = (s) => {
|
|
89
|
+
s.onAdded(Position, (_id) => { });
|
|
90
|
+
};
|
|
91
|
+
// --- createSystems returns Pipeline ---
|
|
92
|
+
const pipeline = createSystems(em, [ctor]);
|
|
93
|
+
pipeline();
|
|
94
|
+
pipeline.dispose();
|
|
95
|
+
// --- Class-based System ---
|
|
96
|
+
let TestSystem = (() => {
|
|
97
|
+
let _classSuper = System;
|
|
98
|
+
let _instanceExtraInitializers = [];
|
|
99
|
+
let _handleAdd_decorators;
|
|
100
|
+
let _handleRemove_decorators;
|
|
101
|
+
return class TestSystem extends _classSuper {
|
|
102
|
+
static {
|
|
103
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
104
|
+
_handleAdd_decorators = [OnAdded(Position)];
|
|
105
|
+
_handleRemove_decorators = [OnRemoved(Position)];
|
|
106
|
+
__esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
107
|
+
__esDecorate(this, null, _handleRemove_decorators, { kind: "method", name: "handleRemove", static: false, private: false, access: { has: obj => "handleRemove" in obj, get: obj => obj.handleRemove }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
108
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
109
|
+
}
|
|
110
|
+
handleAdd(id) { }
|
|
111
|
+
handleRemove(id) { }
|
|
112
|
+
tick() {
|
|
113
|
+
this.forEach([Position], (view) => {
|
|
114
|
+
const count = view.count;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
constructor() {
|
|
118
|
+
super(...arguments);
|
|
119
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
})();
|
|
123
|
+
const testSys = new TestSystem(em);
|
|
124
|
+
testSys.run();
|
|
125
|
+
testSys.dispose();
|
|
126
|
+
// --- createSystems accepts mixed entries ---
|
|
127
|
+
const mixedPipeline = createSystems(em, [ctor, TestSystem]);
|
|
128
|
+
mixedPipeline();
|
|
129
|
+
mixedPipeline.dispose();
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archetype-ecs",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Lightweight archetype-based Entity Component System",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "
|
|
7
|
-
"
|
|
8
|
-
"types": "src/index.d.ts",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
9
8
|
"exports": {
|
|
10
9
|
".": {
|
|
11
|
-
"types": "./
|
|
12
|
-
"default": "./
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
13
12
|
}
|
|
14
13
|
},
|
|
15
14
|
"scripts": {
|
|
16
|
-
"
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"test": "tsc && tsc -p tsconfig.test.json && node --test dist-test/tests/EntityManager.test.js dist-test/tests/System.test.js",
|
|
17
17
|
"bench": "node --expose-gc bench/multi-ecs-bench.js"
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
@@ -26,9 +26,11 @@
|
|
|
26
26
|
],
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.2.3",
|
|
29
30
|
"bitecs": "^0.4.0",
|
|
30
31
|
"harmony-ecs": "^0.0.12",
|
|
31
32
|
"miniplex": "^2.0.0",
|
|
33
|
+
"typescript": "^5.9.3",
|
|
32
34
|
"wolf-ecs": "^2.0.0"
|
|
33
35
|
}
|
|
34
36
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const TYPED: unique symbol = Symbol('typed');
|
|
2
|
+
|
|
3
|
+
type TypedArrayConstructor =
|
|
4
|
+
| typeof Float32Array | typeof Float64Array
|
|
5
|
+
| typeof Int8Array | typeof Int16Array | typeof Int32Array
|
|
6
|
+
| typeof Uint8Array | typeof Uint16Array | typeof Uint32Array
|
|
7
|
+
| typeof Array;
|
|
8
|
+
|
|
9
|
+
export const TYPE_MAP: Record<string, TypedArrayConstructor> = {
|
|
10
|
+
'f32': Float32Array,
|
|
11
|
+
'f64': Float64Array,
|
|
12
|
+
'i8': Int8Array,
|
|
13
|
+
'i16': Int16Array,
|
|
14
|
+
'i32': Int32Array,
|
|
15
|
+
'u8': Uint8Array,
|
|
16
|
+
'u16': Uint16Array,
|
|
17
|
+
'u32': Uint32Array,
|
|
18
|
+
'string': Array,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type TypeSpec = TypedArrayConstructor | [TypedArrayConstructor, number];
|
|
22
|
+
|
|
23
|
+
export function parseTypeSpec(typeStr: string): TypeSpec {
|
|
24
|
+
const match = typeStr.match(/^(\w+)\[(\d+)\]$/);
|
|
25
|
+
if (match) {
|
|
26
|
+
const Ctor = TYPE_MAP[match[1]];
|
|
27
|
+
if (!Ctor) throw new Error(`Unknown base type "${match[1]}"`);
|
|
28
|
+
return [Ctor, parseInt(match[2])];
|
|
29
|
+
}
|
|
30
|
+
const Ctor = TYPE_MAP[typeStr];
|
|
31
|
+
if (!Ctor) throw new Error(`Unknown type "${typeStr}"`);
|
|
32
|
+
return Ctor;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const componentSchemas = new Map<symbol, Record<string, TypeSpec>>();
|
|
36
|
+
|
|
37
|
+
export interface FieldRef {
|
|
38
|
+
readonly _sym: symbol;
|
|
39
|
+
readonly _field: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type ComponentDef<F extends string = never> = {
|
|
43
|
+
readonly _sym: symbol;
|
|
44
|
+
readonly _name: string;
|
|
45
|
+
} & { readonly [K in F]: FieldRef };
|
|
46
|
+
|
|
47
|
+
export function toSym(type: ComponentDef | symbol): symbol {
|
|
48
|
+
return (type as ComponentDef)._sym || (type as symbol);
|
|
49
|
+
}
|