@vworlds/vecs 1.0.61 → 1.0.62
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 +4 -1
- package/dist/component.d.ts +64 -3
- package/dist/component.js +84 -0
- package/dist/component.js.map +1 -1
- package/dist/component_meta.d.ts +21 -1
- package/dist/component_meta.js +34 -1
- package/dist/component_meta.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/reflection/component_members.d.ts +1 -0
- package/dist/reflection/component_members.js +42 -0
- package/dist/reflection/component_members.js.map +1 -0
- package/dist/reflection/decorators.d.ts +31 -0
- package/dist/reflection/decorators.js +129 -0
- package/dist/reflection/decorators.js.map +1 -0
- package/dist/reflection/defaults.d.ts +4 -0
- package/dist/reflection/defaults.js +149 -0
- package/dist/reflection/defaults.js.map +1 -0
- package/dist/reflection/member_types.d.ts +32 -0
- package/dist/reflection/member_types.js +86 -0
- package/dist/reflection/member_types.js.map +1 -0
- package/dist/reflection/vecs_struct.d.ts +31 -0
- package/dist/reflection/vecs_struct.js +259 -0
- package/dist/reflection/vecs_struct.js.map +1 -0
- package/dist/world/world.components.d.ts +52 -1
- package/dist/world/world.components.js +124 -1
- package/dist/world/world.components.js.map +1 -1
- package/docs/README.md +3 -0
- package/docs/components.md +14 -5
- package/docs/diagrams.md +13 -0
- package/docs/reflection.md +251 -0
- package/package.json +1 -1
- package/tsconfig.types.json +2 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { assertMemberType, elementType, formatMemberType, isBigIntType, isNumericType, memberTypesEqual, } from "./member_types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Core component holding a component class's member reflection metadata.
|
|
4
|
+
*
|
|
5
|
+
* `world.component(C)` attaches it to the **component entity** of `C` as soon
|
|
6
|
+
* as `C` or a base class declares a member, through decorators ({@link type},
|
|
7
|
+
* {@link range}, …) or the fluent {@link Component.member} API. Query it like
|
|
8
|
+
* any component:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* world.component(Position).get(VecsStruct)?.members.x; // { type: "i32", range: [-100, 100] }
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The definition is per class for the whole process: every world attaches the
|
|
15
|
+
* same frozen instance, so `getMut(VecsStruct, …)` on it throws. Members are
|
|
16
|
+
* inherited, whichever way the base class declared them, and are listed base
|
|
17
|
+
* class first, then in declaration order. A declaration that contradicts the
|
|
18
|
+
* definition throws, naming the class and the member.
|
|
19
|
+
*/
|
|
20
|
+
export class VecsStruct {
|
|
21
|
+
constructor() {
|
|
22
|
+
/** Member metadata keyed by member name. */
|
|
23
|
+
this.members = {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const definitions = new WeakMap();
|
|
27
|
+
/** Base class → definitions of its subclasses that declare members of their own. */
|
|
28
|
+
const descendants = new WeakMap();
|
|
29
|
+
/** Bumped by every change, so composed caches along any class chain stay correct. */
|
|
30
|
+
let version = 0;
|
|
31
|
+
/**
|
|
32
|
+
* @internal The class and its base classes, base class first.
|
|
33
|
+
*/
|
|
34
|
+
export function _classChain(Class) {
|
|
35
|
+
const chain = [];
|
|
36
|
+
for (let current = Class; current !== null && current !== Function.prototype; current = Object.getPrototypeOf(current)) {
|
|
37
|
+
chain.push(current);
|
|
38
|
+
}
|
|
39
|
+
return chain.reverse();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @internal The composed members of `Class`, base class first, or `undefined`
|
|
43
|
+
* when neither it nor a base class declares any. Reads the live definition.
|
|
44
|
+
*/
|
|
45
|
+
export function _structMembers(Class) {
|
|
46
|
+
const members = composed(Class);
|
|
47
|
+
return members.size === 0 ? undefined : members;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* @internal The frozen `VecsStruct` instance of `Class`, or `undefined` when it
|
|
51
|
+
* has no members.
|
|
52
|
+
*/
|
|
53
|
+
export function _structInstance(Class) {
|
|
54
|
+
if (_structMembers(Class) === undefined) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return (definitions.get(Class) ?? createDefinition(Class)).instance;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @internal Merge member declarations into the definition of `Class`.
|
|
61
|
+
*
|
|
62
|
+
* @returns `true` when the definition changed.
|
|
63
|
+
* @throws When a declaration contradicts the definition of `Class`, of a base
|
|
64
|
+
* class, or of a subclass, or is malformed.
|
|
65
|
+
*/
|
|
66
|
+
export function _declareMembers(Class, declarations) {
|
|
67
|
+
let changed = false;
|
|
68
|
+
for (const [name, declaration] of declarations) {
|
|
69
|
+
const current = composed(Class).get(name);
|
|
70
|
+
const merged = mergeMember(Class, name, current, declaration);
|
|
71
|
+
if (merged === current) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
for (const descendant of descendants.get(Class) ?? []) {
|
|
75
|
+
const own = descendant.own.get(name);
|
|
76
|
+
if (own !== undefined) {
|
|
77
|
+
mergeMember(descendant.Class, name, merged, own);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const definition = definitions.get(Class) ?? createDefinition(Class);
|
|
81
|
+
if (definition.own.size === 0) {
|
|
82
|
+
for (const base of _classChain(Class).slice(0, -1)) {
|
|
83
|
+
let set = descendants.get(base);
|
|
84
|
+
if (set === undefined) {
|
|
85
|
+
set = new Set();
|
|
86
|
+
descendants.set(base, set);
|
|
87
|
+
}
|
|
88
|
+
set.add(definition);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
definition.own.set(name, merged);
|
|
92
|
+
version++;
|
|
93
|
+
changed = true;
|
|
94
|
+
}
|
|
95
|
+
return changed;
|
|
96
|
+
}
|
|
97
|
+
function composed(Class) {
|
|
98
|
+
const definition = definitions.get(Class);
|
|
99
|
+
if (definition?.cache?.version === version) {
|
|
100
|
+
return definition.cache.members;
|
|
101
|
+
}
|
|
102
|
+
const members = new Map();
|
|
103
|
+
for (const C of _classChain(Class)) {
|
|
104
|
+
definitions.get(C)?.own.forEach((member, name) => {
|
|
105
|
+
const inherited = members.get(name);
|
|
106
|
+
members.set(name, inherited === undefined ? member : combine(inherited, member));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (definition !== undefined) {
|
|
110
|
+
definition.cache = { version, members, view: freezeMembers(members) };
|
|
111
|
+
}
|
|
112
|
+
return members;
|
|
113
|
+
}
|
|
114
|
+
/** Fill a subclass's member with fields a base class declared after it. */
|
|
115
|
+
function combine(inherited, member) {
|
|
116
|
+
const next = { ...member };
|
|
117
|
+
next.range ?? (next.range = inherited.range);
|
|
118
|
+
next.description ?? (next.description = inherited.description);
|
|
119
|
+
next.unit ?? (next.unit = inherited.unit);
|
|
120
|
+
next.values ?? (next.values = inherited.values);
|
|
121
|
+
return membersEqual(next, member) ? member : Object.freeze(next);
|
|
122
|
+
}
|
|
123
|
+
function createDefinition(Class) {
|
|
124
|
+
const instance = Object.create(VecsStruct.prototype);
|
|
125
|
+
const definition = { Class, own: new Map(), instance, cache: undefined };
|
|
126
|
+
definitions.set(Class, definition);
|
|
127
|
+
// One instance per class: worlds that attached it before a later declaration,
|
|
128
|
+
// here or on a base class, read the extended definition through it.
|
|
129
|
+
Object.defineProperty(instance, "members", {
|
|
130
|
+
enumerable: true,
|
|
131
|
+
get: () => {
|
|
132
|
+
composed(Class);
|
|
133
|
+
return definition.cache.view;
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
Object.freeze(instance);
|
|
137
|
+
return definition;
|
|
138
|
+
}
|
|
139
|
+
function freezeMembers(members) {
|
|
140
|
+
const view = {};
|
|
141
|
+
for (const [name, member] of members) {
|
|
142
|
+
view[name] = member;
|
|
143
|
+
}
|
|
144
|
+
return Object.freeze(view);
|
|
145
|
+
}
|
|
146
|
+
function mergeMember(Class, name, existing, declaration) {
|
|
147
|
+
const where = `VecsStruct ${Class.name || "anonymous class"}.${name}`;
|
|
148
|
+
const type = declaration.type ?? existing?.type;
|
|
149
|
+
if (type === undefined) {
|
|
150
|
+
throw new TypeError(`${where}: member metadata needs a type`);
|
|
151
|
+
}
|
|
152
|
+
assertMemberType(type, where);
|
|
153
|
+
const next = { ...existing, type };
|
|
154
|
+
if (existing !== undefined && !memberTypesEqual(existing.type, type)) {
|
|
155
|
+
throw contradiction(where, "type", formatMemberType(type), formatMemberType(existing.type));
|
|
156
|
+
}
|
|
157
|
+
if (declaration.range !== undefined) {
|
|
158
|
+
const range = normalizeRange(where, type, declaration.range);
|
|
159
|
+
if (existing?.range !== undefined) {
|
|
160
|
+
if (existing.range[0] !== range[0] || existing.range[1] !== range[1]) {
|
|
161
|
+
throw contradiction(where, "range", formatRange(range), formatRange(existing.range));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
next.range = range;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const field of ["description", "unit"]) {
|
|
169
|
+
const value = declaration[field];
|
|
170
|
+
if (value === undefined) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (typeof value !== "string") {
|
|
174
|
+
throw new TypeError(`${where}: ${field} must be a string`);
|
|
175
|
+
}
|
|
176
|
+
if (existing?.[field] !== undefined && existing[field] !== value) {
|
|
177
|
+
throw contradiction(where, field, JSON.stringify(value), JSON.stringify(existing[field]));
|
|
178
|
+
}
|
|
179
|
+
next[field] = value;
|
|
180
|
+
}
|
|
181
|
+
if (declaration.values !== undefined) {
|
|
182
|
+
const values = normalizeValues(where, type, declaration.values);
|
|
183
|
+
if (existing?.values !== undefined) {
|
|
184
|
+
if (!valuesEqual(existing.values, values)) {
|
|
185
|
+
throw contradiction(where, "values", formatValues(values), formatValues(existing.values));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
next.values = values;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (existing !== undefined && membersEqual(existing, next)) {
|
|
193
|
+
return existing;
|
|
194
|
+
}
|
|
195
|
+
return Object.freeze(next);
|
|
196
|
+
}
|
|
197
|
+
function normalizeRange(where, type, range) {
|
|
198
|
+
const element = elementType(type);
|
|
199
|
+
if (!isNumericType(element)) {
|
|
200
|
+
throw new TypeError(`${where}: range needs a numeric type, not ${formatMemberType(type)}`);
|
|
201
|
+
}
|
|
202
|
+
if (!Array.isArray(range) || range.length !== 2) {
|
|
203
|
+
throw new TypeError(`${where}: range must be [min, max]`);
|
|
204
|
+
}
|
|
205
|
+
let [min, max] = range;
|
|
206
|
+
if (isBigIntType(element)) {
|
|
207
|
+
min = toBigInt(where, min);
|
|
208
|
+
max = toBigInt(where, max);
|
|
209
|
+
}
|
|
210
|
+
else if (typeof min !== "number" || typeof max !== "number" || isNaN(min) || isNaN(max)) {
|
|
211
|
+
throw new TypeError(`${where}: range bounds of a ${formatMemberType(type)} must be numbers`);
|
|
212
|
+
}
|
|
213
|
+
if (min > max) {
|
|
214
|
+
throw new RangeError(`${where}: range min ${min} is above max ${max}`);
|
|
215
|
+
}
|
|
216
|
+
return Object.freeze([min, max]);
|
|
217
|
+
}
|
|
218
|
+
function toBigInt(where, value) {
|
|
219
|
+
if (typeof value === "bigint") {
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
223
|
+
throw new TypeError(`${where}: range bounds of a 64-bit member must be integers`);
|
|
224
|
+
}
|
|
225
|
+
return BigInt(value);
|
|
226
|
+
}
|
|
227
|
+
function normalizeValues(where, type, values) {
|
|
228
|
+
if (elementType(type) !== "enum") {
|
|
229
|
+
throw new TypeError(`${where}: values needs an enum type, not ${formatMemberType(type)}`);
|
|
230
|
+
}
|
|
231
|
+
if (!Array.isArray(values) || !values.every((value) => Number.isInteger(value))) {
|
|
232
|
+
throw new TypeError(`${where}: enum values must be integers`);
|
|
233
|
+
}
|
|
234
|
+
return Object.freeze([...values]);
|
|
235
|
+
}
|
|
236
|
+
function membersEqual(left, right) {
|
|
237
|
+
return (memberTypesEqual(left.type, right.type) &&
|
|
238
|
+
left.range?.[0] === right.range?.[0] &&
|
|
239
|
+
left.range?.[1] === right.range?.[1] &&
|
|
240
|
+
left.description === right.description &&
|
|
241
|
+
left.unit === right.unit &&
|
|
242
|
+
(left.values === right.values ||
|
|
243
|
+
(left.values !== undefined &&
|
|
244
|
+
right.values !== undefined &&
|
|
245
|
+
valuesEqual(left.values, right.values))));
|
|
246
|
+
}
|
|
247
|
+
function valuesEqual(left, right) {
|
|
248
|
+
return left.length === right.length && left.every((value, i) => value === right[i]);
|
|
249
|
+
}
|
|
250
|
+
function contradiction(where, field, declared, current) {
|
|
251
|
+
return new Error(`${where}: ${field} ${declared} contradicts the existing ${field} ${current}`);
|
|
252
|
+
}
|
|
253
|
+
function formatRange(range) {
|
|
254
|
+
return `[${range[0]}, ${range[1]}]`;
|
|
255
|
+
}
|
|
256
|
+
function formatValues(values) {
|
|
257
|
+
return `[${values.join(", ")}]`;
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=vecs_struct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vecs_struct.js","sourceRoot":"","sources":["../../src/reflection/vecs_struct.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Component, type ComponentClass, type ComponentType } from "../component.js";
|
|
1
|
+
import { Component, type ComponentClass, type ComponentInstance, type ComponentType } from "../component.js";
|
|
2
2
|
import { type EID } from "../entity/index.js";
|
|
3
|
+
import { type ValidationResult } from "../reflection/defaults.js";
|
|
3
4
|
import { Entities } from "./world.entities.js";
|
|
4
5
|
import { type World } from "./world.js";
|
|
5
6
|
export declare class Components extends Entities {
|
|
@@ -15,6 +16,14 @@ export declare class Components extends Entities {
|
|
|
15
16
|
* A newly registered definition inherits the current scope parent immediately.
|
|
16
17
|
* Scopes must be entered outside deferred mode. Existing lookups do not reparent it.
|
|
17
18
|
*
|
|
19
|
+
* On first registration the class's member decorators ({@link type},
|
|
20
|
+
* {@link range}, …) are read into its {@link VecsStruct} definition, which
|
|
21
|
+
* is attached to the component entity when the class declares any member.
|
|
22
|
+
* Static `vecs_<event>` methods (`vecs_add`, `vecs_set`, `vecs_remove`,
|
|
23
|
+
* `vecs_targetAdd`, `vecs_targetRemove`, `vecs_validate`, `vecs_normalize`)
|
|
24
|
+
* are registered as hooks for `<event>`, ahead of hooks added with `.on(…)`.
|
|
25
|
+
* A class without decorators or statics registers exactly as before.
|
|
26
|
+
*
|
|
18
27
|
* Pass an explicit numeric `ceid` to pin the component to a server-assigned
|
|
19
28
|
* stable id instead of auto-allocating one.
|
|
20
29
|
*
|
|
@@ -61,6 +70,48 @@ export declare class Components extends Entities {
|
|
|
61
70
|
* @returns The `Component` entity, for chaining.
|
|
62
71
|
*/
|
|
63
72
|
set<C extends ComponentClass>(this: World, Class: C, props: Partial<InstanceType<C>>): Component<C>;
|
|
73
|
+
/**
|
|
74
|
+
* Check `component` with its class's `validate` hooks.
|
|
75
|
+
*
|
|
76
|
+
* Hooks run in registration order and the first failure is returned. When
|
|
77
|
+
* the class has no explicit hook but declares ranges or integer member
|
|
78
|
+
* types, a default hook is created on first use: it checks each such member
|
|
79
|
+
* against its range and its type's bounds (for example `u8` is 0–255), that
|
|
80
|
+
* integers are whole, and that an `enum` with `values` holds one of them. It
|
|
81
|
+
* reads the live definition, inherited members included. A class with
|
|
82
|
+
* neither ranges nor integer types is valid.
|
|
83
|
+
*
|
|
84
|
+
* Nothing calls this on `set`; call it where your code accepts external data.
|
|
85
|
+
*
|
|
86
|
+
* @param component - An instance of a class registered in this world.
|
|
87
|
+
* @returns `true`, or the violations: one message or a list of them.
|
|
88
|
+
* @throws When `component.constructor` is not registered in this world.
|
|
89
|
+
*/
|
|
90
|
+
validate(this: World, component: ComponentInstance): ValidationResult;
|
|
91
|
+
/**
|
|
92
|
+
* Return whether {@link validate} accepts `component`.
|
|
93
|
+
*
|
|
94
|
+
* @throws When `component.constructor` is not registered in this world.
|
|
95
|
+
*/
|
|
96
|
+
isValid(this: World, component: ComponentInstance): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Modify `component` in place with its class's `normalize` hooks, so it
|
|
99
|
+
* becomes valid.
|
|
100
|
+
*
|
|
101
|
+
* Hooks run in registration order, each on the result of the previous one.
|
|
102
|
+
* When the class has no explicit hook but declares ranges or integer member
|
|
103
|
+
* types, a default hook is created on first use: it truncates integers and
|
|
104
|
+
* clamps each such member to its range and its type's bounds. Values of the
|
|
105
|
+
* wrong JavaScript type and `enum` values outside their `values` are left as
|
|
106
|
+
* they are; `NaN` becomes `0` first.
|
|
107
|
+
*
|
|
108
|
+
* @param component - An instance of a class registered in this world.
|
|
109
|
+
* @returns `component`, for chaining.
|
|
110
|
+
* @throws When `component.constructor` is not registered in this world.
|
|
111
|
+
*/
|
|
112
|
+
normalize<C extends ComponentInstance>(this: World, component: C): C;
|
|
64
113
|
/** Declare a group of mutually exclusive components. */
|
|
65
114
|
setExclusiveComponents(this: World, ...components: ComponentClass[]): void;
|
|
115
|
+
/** Resolve the meta of a component instance's class for the reflection API. */
|
|
116
|
+
private _reflectedMeta;
|
|
66
117
|
}
|
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import { Component } from "../component.js";
|
|
1
|
+
import { Component, } from "../component.js";
|
|
2
2
|
import { ComponentDef, Trait } from "../component_def.js";
|
|
3
|
+
import { Relationship, _slotHandlers } from "../component_meta.js";
|
|
4
|
+
import { _defaultNormalize, _defaultValidate, _hasDefaultChecks, } from "../reflection/defaults.js";
|
|
5
|
+
import { _ownDecoratedMembers } from "../reflection/decorators.js";
|
|
6
|
+
import { _attachVecsStruct } from "../reflection/component_members.js";
|
|
7
|
+
import { _classChain, _declareMembers } from "../reflection/vecs_struct.js";
|
|
3
8
|
import { Entities } from "./world.entities.js";
|
|
4
9
|
const RESERVED_EID = 0;
|
|
10
|
+
/** Hook events a class can implement as a static `vecs_<event>` method. */
|
|
11
|
+
const STATIC_HOOK_EVENTS = [
|
|
12
|
+
"add",
|
|
13
|
+
"set",
|
|
14
|
+
"remove",
|
|
15
|
+
"targetAdd",
|
|
16
|
+
"targetRemove",
|
|
17
|
+
"validate",
|
|
18
|
+
"normalize",
|
|
19
|
+
];
|
|
5
20
|
export class Components extends Entities {
|
|
6
21
|
component(ComponentClassOrName, ceid) {
|
|
7
22
|
if (typeof ComponentClassOrName === "string") {
|
|
@@ -41,6 +56,75 @@ export class Components extends Entities {
|
|
|
41
56
|
set(Class, props) {
|
|
42
57
|
return this.component(Class).set(Class, props);
|
|
43
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Check `component` with its class's `validate` hooks.
|
|
61
|
+
*
|
|
62
|
+
* Hooks run in registration order and the first failure is returned. When
|
|
63
|
+
* the class has no explicit hook but declares ranges or integer member
|
|
64
|
+
* types, a default hook is created on first use: it checks each such member
|
|
65
|
+
* against its range and its type's bounds (for example `u8` is 0–255), that
|
|
66
|
+
* integers are whole, and that an `enum` with `values` holds one of them. It
|
|
67
|
+
* reads the live definition, inherited members included. A class with
|
|
68
|
+
* neither ranges nor integer types is valid.
|
|
69
|
+
*
|
|
70
|
+
* Nothing calls this on `set`; call it where your code accepts external data.
|
|
71
|
+
*
|
|
72
|
+
* @param component - An instance of a class registered in this world.
|
|
73
|
+
* @returns `true`, or the violations: one message or a list of them.
|
|
74
|
+
* @throws When `component.constructor` is not registered in this world.
|
|
75
|
+
*/
|
|
76
|
+
validate(component) {
|
|
77
|
+
const meta = this._reflectedMeta(component, "validate");
|
|
78
|
+
if (meta._onValidate === undefined) {
|
|
79
|
+
if (!_hasDefaultChecks(meta.Class)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
meta._onValidate = _defaultValidate(meta.Class);
|
|
83
|
+
meta._validateIsDefault = true;
|
|
84
|
+
}
|
|
85
|
+
for (const hook of _slotHandlers(meta._onValidate)) {
|
|
86
|
+
const result = hook(component);
|
|
87
|
+
if (result !== true) {
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Return whether {@link validate} accepts `component`.
|
|
95
|
+
*
|
|
96
|
+
* @throws When `component.constructor` is not registered in this world.
|
|
97
|
+
*/
|
|
98
|
+
isValid(component) {
|
|
99
|
+
return this.validate(component) === true;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Modify `component` in place with its class's `normalize` hooks, so it
|
|
103
|
+
* becomes valid.
|
|
104
|
+
*
|
|
105
|
+
* Hooks run in registration order, each on the result of the previous one.
|
|
106
|
+
* When the class has no explicit hook but declares ranges or integer member
|
|
107
|
+
* types, a default hook is created on first use: it truncates integers and
|
|
108
|
+
* clamps each such member to its range and its type's bounds. Values of the
|
|
109
|
+
* wrong JavaScript type and `enum` values outside their `values` are left as
|
|
110
|
+
* they are; `NaN` becomes `0` first.
|
|
111
|
+
*
|
|
112
|
+
* @param component - An instance of a class registered in this world.
|
|
113
|
+
* @returns `component`, for chaining.
|
|
114
|
+
* @throws When `component.constructor` is not registered in this world.
|
|
115
|
+
*/
|
|
116
|
+
normalize(component) {
|
|
117
|
+
const meta = this._reflectedMeta(component, "normalize");
|
|
118
|
+
if (meta._onNormalize === undefined) {
|
|
119
|
+
if (!_hasDefaultChecks(meta.Class)) {
|
|
120
|
+
return component;
|
|
121
|
+
}
|
|
122
|
+
meta._onNormalize = _defaultNormalize(meta.Class);
|
|
123
|
+
meta._normalizeIsDefault = true;
|
|
124
|
+
}
|
|
125
|
+
meta._onNormalize(component);
|
|
126
|
+
return component;
|
|
127
|
+
}
|
|
44
128
|
/** Declare a group of mutually exclusive components. */
|
|
45
129
|
setExclusiveComponents(...components) {
|
|
46
130
|
const componentMetas = components.map((C) => this.entity(C).meta);
|
|
@@ -60,6 +144,12 @@ export class Components extends Entities {
|
|
|
60
144
|
}
|
|
61
145
|
throw new Error(`Component class ${ComponentClass.name} is already registered with eid ${existingComponent.eid}`);
|
|
62
146
|
}
|
|
147
|
+
// Validate declarations before allocating an id, so a throw leaves no
|
|
148
|
+
// half-registered class behind.
|
|
149
|
+
for (const Class of _classChain(ComponentClass)) {
|
|
150
|
+
_declareMembers(Class, _ownDecoratedMembers(Class));
|
|
151
|
+
}
|
|
152
|
+
const staticHooks = staticHooksOf(ComponentClass);
|
|
63
153
|
if (ceid === undefined) {
|
|
64
154
|
ceid = pool.alloc();
|
|
65
155
|
}
|
|
@@ -72,12 +162,16 @@ export class Components extends Entities {
|
|
|
72
162
|
const component = new Constructor(this, ceid, ComponentClass);
|
|
73
163
|
this._entities.set(ceid, component);
|
|
74
164
|
this._classRegistry.set(ComponentClass, component);
|
|
165
|
+
for (const [event, hook] of staticHooks) {
|
|
166
|
+
component.meta.on(event, hook);
|
|
167
|
+
}
|
|
75
168
|
// Publish the class before resolving core metadata: recursive registrations
|
|
76
169
|
// find themselves, so bootstrap needs neither special ids nor a second pass.
|
|
77
170
|
// Both metadata definitions exist before any module can claim their scope.
|
|
78
171
|
this.component(ComponentDef);
|
|
79
172
|
this.component(Trait);
|
|
80
173
|
component.add(ComponentDef);
|
|
174
|
+
_attachVecsStruct(component);
|
|
81
175
|
const parent = this._scope;
|
|
82
176
|
if (parent !== undefined) {
|
|
83
177
|
component.childOf(parent);
|
|
@@ -88,5 +182,34 @@ export class Components extends Entities {
|
|
|
88
182
|
this._notifyEntityCreated(component);
|
|
89
183
|
return component;
|
|
90
184
|
}
|
|
185
|
+
/** Resolve the meta of a component instance's class for the reflection API. */
|
|
186
|
+
_reflectedMeta(component, api) {
|
|
187
|
+
const Class = component?.constructor;
|
|
188
|
+
const registered = Class === undefined ? undefined : this._classRegistry.get(Class);
|
|
189
|
+
if (registered === undefined) {
|
|
190
|
+
throw new Error(`world.${api}: component class ${Class?.name || "<unknown>"} is not registered in this world`);
|
|
191
|
+
}
|
|
192
|
+
return registered.meta;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Collect a class's static `vecs_<event>` methods, bound to the class.
|
|
197
|
+
*
|
|
198
|
+
* @throws When a class that is not a relationship declares a `target*` static.
|
|
199
|
+
*/
|
|
200
|
+
function staticHooksOf(Class) {
|
|
201
|
+
const statics = Class;
|
|
202
|
+
const hooks = [];
|
|
203
|
+
for (const event of STATIC_HOOK_EVENTS) {
|
|
204
|
+
const hook = statics[`vecs_${event}`];
|
|
205
|
+
if (typeof hook !== "function") {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (event.startsWith("target") && !(Class.prototype instanceof Relationship)) {
|
|
209
|
+
throw new Error(`"${event}" hooks can only be registered on a Relationship component`);
|
|
210
|
+
}
|
|
211
|
+
hooks.push([event, hook.bind(Class)]);
|
|
212
|
+
}
|
|
213
|
+
return hooks;
|
|
91
214
|
}
|
|
92
215
|
//# sourceMappingURL=world.components.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"world.components.js","sourceRoot":"","sources":["../../src/world/world.components.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"world.components.js","sourceRoot":"","sources":["../../src/world/world.components.ts"],"names":[],"mappings":"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"}
|
package/docs/README.md
CHANGED
|
@@ -30,6 +30,8 @@ Read in this order the first time through:
|
|
|
30
30
|
- [Modules](./modules.md) — packaging features, composition, scopes and module provenance, built-in modules.
|
|
31
31
|
- [Subtree snapshots](./snapshots.md) — capturing, restoring, and moving an entity forest
|
|
32
32
|
between two worlds.
|
|
33
|
+
- [Component reflection](./reflection.md) — member types, `VecsStruct`, and the `validate` /
|
|
34
|
+
`normalize` hooks.
|
|
33
35
|
5. [Design guide](./design-guide.md) — opinionated patterns and anti-patterns distilled from real
|
|
34
36
|
designs. Read it once you know the machinery.
|
|
35
37
|
6. [Utilities](./utilities.md) — helper classes exported by `@vworlds/vecs` but not part of the ECS
|
|
@@ -60,6 +62,7 @@ after reading the execution-model and systems guides.
|
|
|
60
62
|
| [relationships.md](./relationships.md) | Relationships, target creation, projection and ownership, hierarchy, traversal, and cleanup. |
|
|
61
63
|
| [modules.md](./modules.md) | `Module<Config>`, `world.module`, `world.scope`, hierarchy provenance, built-in modules. |
|
|
62
64
|
| [snapshots.md](./snapshots.md) | `snapshotSubtree`, `restoreSubtree`, `moveSubtree`, and `OnSnapshot` capture policies. |
|
|
65
|
+
| [reflection.md](./reflection.md) | Member types and metadata, `VecsStruct`, `validate` / `normalize` hooks, `vecs_` static hooks. |
|
|
63
66
|
| [design-guide.md](./design-guide.md) | Patterns, anti-patterns, and a Phaser render-layer case study. |
|
|
64
67
|
| [diagrams.md](./diagrams.md) | Mermaid notation and copyable schema, instance, and systems templates for ECS designs. |
|
|
65
68
|
| [utilities.md](./utilities.md) | Public helper classes: `Events`, `ArrayMap`, `IterableArrayMap`, plus `Bitset` cross-ref. |
|
package/docs/components.md
CHANGED
|
@@ -154,6 +154,12 @@ world
|
|
|
154
154
|
| `set` | `entity.set(C, props)` applies ordinary data, `entity.attach(instance)` stores an instance, `entity.modified(C)` is called, or `getMut` / `ensure` marks it dirty. |
|
|
155
155
|
| `remove` | The component is detached (`entity.remove(C)`), or the owning entity is destroyed. |
|
|
156
156
|
|
|
157
|
+
Two more events, `validate` and `normalize`, check or fix a component instance. They run only
|
|
158
|
+
through `world.validate` and `world.normalize`, never on a mutation; see
|
|
159
|
+
[Component reflection](./reflection.md#validating-and-normalizing). A class can also declare any hook
|
|
160
|
+
as a static `vecs_<event>` method, which runs ahead of hooks added with `on`; see
|
|
161
|
+
[`vecs_` statics](./reflection.md#vecs_-statics).
|
|
162
|
+
|
|
157
163
|
Relationship component entities additionally expose the `targetAdd` and `targetRemove` events. Both
|
|
158
164
|
receive `(source, relationshipInstance, target)` after the edge and reverse index reach their new
|
|
159
165
|
state. An exclusive replacement fires `targetRemove` for the old edge before `targetAdd` for
|
|
@@ -450,17 +456,20 @@ interface markers for polymorphic dispatch — is in the
|
|
|
450
456
|
| `get(Class)` | Singleton read: `component(Class).get(Class)`. |
|
|
451
457
|
| `set(Class, props)` | Singleton write: `component(Class).set(Class, props)`. |
|
|
452
458
|
| `setExclusiveComponents(...classes)` | Declare one mutually exclusive group. |
|
|
459
|
+
| `validate(c)` / `isValid(c)` | Run the class's `validate` hooks; see [Component reflection](./reflection.md). |
|
|
460
|
+
| `normalize(c)` | Run the class's `normalize` hooks, modifying `c` in place. |
|
|
453
461
|
|
|
454
462
|
### `Component` entity (`lib/vecs/src/component.ts`)
|
|
455
463
|
|
|
456
464
|
A `Component<T>` is an `Entity` subclass, so the full [entity API](./entities.md#reference)
|
|
457
465
|
applies. On top of it:
|
|
458
466
|
|
|
459
|
-
| Member | Description
|
|
460
|
-
| ----------------------------- |
|
|
461
|
-
| `
|
|
462
|
-
| `
|
|
463
|
-
| `
|
|
467
|
+
| Member | Description |
|
|
468
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
469
|
+
| `member(name, type)` | Declare a member for [reflection](./reflection.md) and return the component; `.range` / `.description` / `.unit` / `.values` then apply to it. |
|
|
470
|
+
| `on(event, handler, insert?)` | Register a handler for `add` / `remove` / `set` / `targetAdd` / `targetRemove` / `validate` / `normalize` (plus the internal `policyAdd` / `policyRemove` / `targetPolicyAdd` / `targetPolicyRemove` events). `insert` defaults to `false` (append); `true` prepends. Rejects `target*` events on non-relationships. Returns the component. |
|
|
471
|
+
| `off(event, handler)` | Remove a previously-registered handler by reference. Idempotent. Returns the component. |
|
|
472
|
+
| `meta` | The `ComponentMeta` bookkeeping record. |
|
|
464
473
|
|
|
465
474
|
### `ComponentMeta` (`lib/vecs/src/component_meta.ts`)
|
|
466
475
|
|
package/docs/diagrams.md
CHANGED
|
@@ -43,6 +43,19 @@ Quote edge labels containing emoji. Only `ChildOf` uses a dotted relationship ed
|
|
|
43
43
|
(`child -. "1️⃣ChildOf" .-> parent`); every other relationship uses a solid edge
|
|
44
44
|
(`ship -- "1️⃣TowedBy" --> tug`). Do not use dotted edges for other parent-like relationships.
|
|
45
45
|
|
|
46
|
+
### Member types
|
|
47
|
+
|
|
48
|
+
When a design depends on what a component holds, list its members under the component in a
|
|
49
|
+
schema box, one indented line each. Use the [reflection member type](./reflection.md#member-types-libvecssrcreflectionmember_typests)
|
|
50
|
+
names and add a range or unit only when it matters to the design:
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
flowchart BT
|
|
54
|
+
ship["<b>Ship</b><br/>📦 Position<br/> x: i32 [-100, 100] m<br/> y: i32 [-100, 100] m<br/>📦 Cargo<br/> items: [u16]<br/>🏷️ Networked"]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Leave members out where the component's name says enough. Tags have no members.
|
|
58
|
+
|
|
46
59
|
## Schema template
|
|
47
60
|
|
|
48
61
|
Use a box for each entity kind: a **bold** name, then one line per component. Relationships live
|