@driftengine/entities 4.7.2 → 4.7.4
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/index.d.ts +1 -1
- package/dist/scene.js +3 -1
- package/dist/schedule.js +1 -1
- package/dist/store.d.ts +13 -1
- package/dist/store.js +26 -2
- package/package.json +2 -2
- package/src/index.ts +6 -1
- package/src/scene.ts +3 -1
- package/src/schedule.ts +1 -1
- package/src/store.ts +50 -3
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* and a future inspector all read **one** description of a component, so there is no second one to
|
|
17
17
|
* keep in sync.
|
|
18
18
|
*/
|
|
19
|
-
export type { ComponentStoreOptions, ComponentType, StoreSnapshot } from './store.ts';
|
|
19
|
+
export type { ComponentDefault, ComponentStoreOptions, ComponentType, StoreSnapshot, } from './store.ts';
|
|
20
20
|
export { ComponentStore, createStoreSnapshot, defineComponent } from './store.ts';
|
|
21
21
|
export type { ComponentView, ViewColumn } from './view.ts';
|
|
22
22
|
export type { CursorHost } from './query.ts';
|
package/dist/scene.js
CHANGED
|
@@ -108,9 +108,11 @@ export function deserializeWorld(world, scene, types) {
|
|
|
108
108
|
const instance = {};
|
|
109
109
|
for (const field of from.fields)
|
|
110
110
|
instance[field.name] = saved[field.id];
|
|
111
|
+
/* A field the file does not have takes the value the component declares for it, and zero or
|
|
112
|
+
nothing where it declares none. */
|
|
111
113
|
const defaults = {};
|
|
112
114
|
for (const field of type.schema.fields)
|
|
113
|
-
defaults[field.name] = field.type === 'String' ? null : 0;
|
|
115
|
+
defaults[field.name] = type.defaults[field.name] ?? (field.type === 'String' ? null : 0);
|
|
114
116
|
const result = migrate(instance, from, type.schema, defaults);
|
|
115
117
|
if (!result.migrated)
|
|
116
118
|
return { loaded: false, reason: result.reason };
|
package/dist/schedule.js
CHANGED
|
@@ -77,7 +77,7 @@ function group(order) {
|
|
|
77
77
|
const groups = [];
|
|
78
78
|
let current = [];
|
|
79
79
|
for (const system of order) {
|
|
80
|
-
if (current.
|
|
80
|
+
if (current.some((other) => conflicts(other, system))) {
|
|
81
81
|
groups.push(current);
|
|
82
82
|
current = [];
|
|
83
83
|
}
|
package/dist/store.d.ts
CHANGED
|
@@ -65,8 +65,20 @@ export interface ComponentType {
|
|
|
65
65
|
readonly id: number;
|
|
66
66
|
/** Fields typed `Entity`, which a scene load has to rewrite through its id map. */
|
|
67
67
|
readonly entityFields: readonly string[];
|
|
68
|
+
/**
|
|
69
|
+
* The value each field was declared with, by field name; a field with none is absent and starts
|
|
70
|
+
* at zero, `false` or nothing.
|
|
71
|
+
*
|
|
72
|
+
* **Read wherever a field is not given**: an entity added with some of its fields, a prefab that
|
|
73
|
+
* names a few, a saved scene from before the field existed. A DriftScript component's come from
|
|
74
|
+
* the module's metadata (`seed: u32 = 7`); before they were read, every one of those paths
|
|
75
|
+
* filled zero and nothing said so.
|
|
76
|
+
*/
|
|
77
|
+
readonly defaults: Readonly<Record<string, ComponentDefault>>;
|
|
68
78
|
}
|
|
69
|
-
|
|
79
|
+
/** A field's declared value: a number for every numeric and enum column, a flag, or a string. */
|
|
80
|
+
export type ComponentDefault = number | string | boolean;
|
|
81
|
+
export declare function defineComponent(schema: Schema, defaults?: Readonly<Record<string, ComponentDefault>>): ComponentType;
|
|
70
82
|
export declare function defineComponent(name: string, fields: Readonly<Record<string, string>>): ComponentType;
|
|
71
83
|
/**
|
|
72
84
|
* One store's contents, for a rewind. Reused between saves; see `AllocatorSnapshot`.
|
package/dist/store.js
CHANGED
|
@@ -105,7 +105,13 @@ let nextComponentId = 0;
|
|
|
105
105
|
* caller wanting a different id scheme, at which point the id belongs entirely to whoever declares
|
|
106
106
|
* the field and this function stops building one at all.
|
|
107
107
|
*/
|
|
108
|
-
export function defineComponent(first,
|
|
108
|
+
export function defineComponent(first, second) {
|
|
109
|
+
const fields = typeof first === 'string'
|
|
110
|
+
? second
|
|
111
|
+
: undefined;
|
|
112
|
+
const defaults = typeof first === 'string'
|
|
113
|
+
? {}
|
|
114
|
+
: (second ?? {});
|
|
109
115
|
const schema = typeof first === 'string'
|
|
110
116
|
? {
|
|
111
117
|
name: first,
|
|
@@ -125,10 +131,26 @@ export function defineComponent(first, fields) {
|
|
|
125
131
|
'about layout, so it is added here rather than assumed.');
|
|
126
132
|
}
|
|
127
133
|
}
|
|
134
|
+
/* A default names a field and fits its column, or the type is refused here, where a wrong one
|
|
135
|
+
is a fault in whoever declared it and not a value some later entity quietly starts with. */
|
|
136
|
+
for (const [name, value] of Object.entries(defaults)) {
|
|
137
|
+
const field = schema.fields.find((candidate) => candidate.name === name);
|
|
138
|
+
if (field === undefined) {
|
|
139
|
+
throw new Error(`\`${schema.name}\` has a default for \`${name}\`, which is not one of its fields: ` +
|
|
140
|
+
`${schema.fields.map((f) => `\`${f.name}\``).join(', ')}.`);
|
|
141
|
+
}
|
|
142
|
+
const inner = optionInner(field.type) ?? field.type;
|
|
143
|
+
const wanted = inner === 'String' ? 'string' : inner === 'bool' ? 'boolean' : 'number';
|
|
144
|
+
if (typeof value !== wanted) {
|
|
145
|
+
throw new Error(`\`${schema.name}.${name}\` is \`${field.type}\` and its default is the ${typeof value} ` +
|
|
146
|
+
`${JSON.stringify(value)}.`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
128
149
|
return {
|
|
129
150
|
name: schema.name,
|
|
130
151
|
id: nextComponentId++,
|
|
131
152
|
schema,
|
|
153
|
+
defaults: { ...defaults },
|
|
132
154
|
/* An optional handle is still a handle, so a scene load has to rewrite it through the id map
|
|
133
155
|
exactly as it rewrites a required one. Leaving it out would serialise a handle into a world
|
|
134
156
|
whose indices mean something else. */
|
|
@@ -242,7 +264,9 @@ export class ComponentStore {
|
|
|
242
264
|
const column = this.columns.get(field.name);
|
|
243
265
|
if (column === undefined)
|
|
244
266
|
continue;
|
|
245
|
-
|
|
267
|
+
/* Omitted takes the declared value; a `null` given for an optional field is its absence. */
|
|
268
|
+
const own = values[field.name];
|
|
269
|
+
const given = own === undefined ? this.type.defaults[field.name] : own;
|
|
246
270
|
if (Array.isArray(column))
|
|
247
271
|
column[at] = given ?? null;
|
|
248
272
|
else
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/entities",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.4",
|
|
4
4
|
"description": "Entities with generational identity, component storage, systems and scenes",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"driftscript": "1.
|
|
30
|
+
"driftscript": "1.14.0"
|
|
31
31
|
},
|
|
32
32
|
"author": "Drift Technologies",
|
|
33
33
|
"repository": {
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
* and a future inspector all read **one** description of a component, so there is no second one to
|
|
17
17
|
* keep in sync.
|
|
18
18
|
*/
|
|
19
|
-
export type {
|
|
19
|
+
export type {
|
|
20
|
+
ComponentDefault,
|
|
21
|
+
ComponentStoreOptions,
|
|
22
|
+
ComponentType,
|
|
23
|
+
StoreSnapshot,
|
|
24
|
+
} from './store.ts';
|
|
20
25
|
export { ComponentStore, createStoreSnapshot, defineComponent } from './store.ts';
|
|
21
26
|
|
|
22
27
|
export type { ComponentView, ViewColumn } from './view.ts';
|
package/src/scene.ts
CHANGED
|
@@ -136,9 +136,11 @@ export function deserializeWorld(
|
|
|
136
136
|
const instance: Record<string, unknown> = {};
|
|
137
137
|
for (const field of from.fields) instance[field.name] = saved[field.id];
|
|
138
138
|
|
|
139
|
+
/* A field the file does not have takes the value the component declares for it, and zero or
|
|
140
|
+
nothing where it declares none. */
|
|
139
141
|
const defaults: Record<string, unknown> = {};
|
|
140
142
|
for (const field of type.schema.fields)
|
|
141
|
-
defaults[field.name] = field.type === 'String' ? null : 0;
|
|
143
|
+
defaults[field.name] = type.defaults[field.name] ?? (field.type === 'String' ? null : 0);
|
|
142
144
|
|
|
143
145
|
const result = migrate(instance, from, type.schema, defaults);
|
|
144
146
|
if (!result.migrated) return { loaded: false, reason: result.reason };
|
package/src/schedule.ts
CHANGED
|
@@ -116,7 +116,7 @@ function group(order: readonly SystemDefinition[]): (readonly SystemDefinition[]
|
|
|
116
116
|
const groups: SystemDefinition[][] = [];
|
|
117
117
|
let current: SystemDefinition[] = [];
|
|
118
118
|
for (const system of order) {
|
|
119
|
-
if (current.
|
|
119
|
+
if (current.some((other) => conflicts(other, system))) {
|
|
120
120
|
groups.push(current);
|
|
121
121
|
current = [];
|
|
122
122
|
}
|
package/src/store.ts
CHANGED
|
@@ -139,11 +139,27 @@ export interface ComponentType {
|
|
|
139
139
|
readonly id: number;
|
|
140
140
|
/** Fields typed `Entity`, which a scene load has to rewrite through its id map. */
|
|
141
141
|
readonly entityFields: readonly string[];
|
|
142
|
+
/**
|
|
143
|
+
* The value each field was declared with, by field name; a field with none is absent and starts
|
|
144
|
+
* at zero, `false` or nothing.
|
|
145
|
+
*
|
|
146
|
+
* **Read wherever a field is not given**: an entity added with some of its fields, a prefab that
|
|
147
|
+
* names a few, a saved scene from before the field existed. A DriftScript component's come from
|
|
148
|
+
* the module's metadata (`seed: u32 = 7`); before they were read, every one of those paths
|
|
149
|
+
* filled zero and nothing said so.
|
|
150
|
+
*/
|
|
151
|
+
readonly defaults: Readonly<Record<string, ComponentDefault>>;
|
|
142
152
|
}
|
|
143
153
|
|
|
154
|
+
/** A field's declared value: a number for every numeric and enum column, a flag, or a string. */
|
|
155
|
+
export type ComponentDefault = number | string | boolean;
|
|
156
|
+
|
|
144
157
|
let nextComponentId = 0;
|
|
145
158
|
|
|
146
|
-
export function defineComponent(
|
|
159
|
+
export function defineComponent(
|
|
160
|
+
schema: Schema,
|
|
161
|
+
defaults?: Readonly<Record<string, ComponentDefault>>,
|
|
162
|
+
): ComponentType;
|
|
147
163
|
export function defineComponent(
|
|
148
164
|
name: string,
|
|
149
165
|
fields: Readonly<Record<string, string>>,
|
|
@@ -168,8 +184,16 @@ export function defineComponent(
|
|
|
168
184
|
*/
|
|
169
185
|
export function defineComponent(
|
|
170
186
|
first: Schema | string,
|
|
171
|
-
|
|
187
|
+
second?: Readonly<Record<string, string>> | Readonly<Record<string, ComponentDefault>>,
|
|
172
188
|
): ComponentType {
|
|
189
|
+
const fields =
|
|
190
|
+
typeof first === 'string'
|
|
191
|
+
? (second as Readonly<Record<string, string>> | undefined)
|
|
192
|
+
: undefined;
|
|
193
|
+
const defaults =
|
|
194
|
+
typeof first === 'string'
|
|
195
|
+
? {}
|
|
196
|
+
: ((second as Readonly<Record<string, ComponentDefault>> | undefined) ?? {});
|
|
173
197
|
const schema: Schema =
|
|
174
198
|
typeof first === 'string'
|
|
175
199
|
? {
|
|
@@ -194,10 +218,31 @@ export function defineComponent(
|
|
|
194
218
|
}
|
|
195
219
|
}
|
|
196
220
|
|
|
221
|
+
/* A default names a field and fits its column, or the type is refused here, where a wrong one
|
|
222
|
+
is a fault in whoever declared it and not a value some later entity quietly starts with. */
|
|
223
|
+
for (const [name, value] of Object.entries(defaults)) {
|
|
224
|
+
const field = schema.fields.find((candidate) => candidate.name === name);
|
|
225
|
+
if (field === undefined) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`\`${schema.name}\` has a default for \`${name}\`, which is not one of its fields: ` +
|
|
228
|
+
`${schema.fields.map((f) => `\`${f.name}\``).join(', ')}.`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const inner = optionInner(field.type) ?? field.type;
|
|
232
|
+
const wanted = inner === 'String' ? 'string' : inner === 'bool' ? 'boolean' : 'number';
|
|
233
|
+
if (typeof value !== wanted) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`\`${schema.name}.${name}\` is \`${field.type}\` and its default is the ${typeof value} ` +
|
|
236
|
+
`${JSON.stringify(value)}.`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
197
241
|
return {
|
|
198
242
|
name: schema.name,
|
|
199
243
|
id: nextComponentId++,
|
|
200
244
|
schema,
|
|
245
|
+
defaults: { ...defaults },
|
|
201
246
|
/* An optional handle is still a handle, so a scene load has to rewrite it through the id map
|
|
202
247
|
exactly as it rewrites a required one. Leaving it out would serialise a handle into a world
|
|
203
248
|
whose indices mean something else. */
|
|
@@ -354,7 +399,9 @@ export class ComponentStore {
|
|
|
354
399
|
for (const field of this.type.schema.fields) {
|
|
355
400
|
const column = this.columns.get(field.name);
|
|
356
401
|
if (column === undefined) continue;
|
|
357
|
-
|
|
402
|
+
/* Omitted takes the declared value; a `null` given for an optional field is its absence. */
|
|
403
|
+
const own = values[field.name];
|
|
404
|
+
const given = own === undefined ? this.type.defaults[field.name] : own;
|
|
358
405
|
if (Array.isArray(column)) column[at] = given ?? null;
|
|
359
406
|
else (column as NumericColumn)[at] = typeof given === 'number' ? given : Number(given ?? 0);
|
|
360
407
|
/*
|