@latticexyz/recs 2.0.0-next.17 → 2.0.0-next.18
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/CHANGELOG.md +10 -1
- package/dist/{chunk-YDQFEK6R.js → chunk-X4WOMEVS.js} +1 -1
- package/dist/chunk-X4WOMEVS.js.map +1 -0
- package/dist/deprecated/index.js +1 -1
- package/dist/deprecated/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/Component.ts +18 -17
- package/src/Entity.ts +1 -1
- package/src/Indexer.ts +1 -1
- package/src/Performance.spec.ts +1 -0
- package/src/Query.spec.ts +29 -29
- package/src/Query.ts +12 -13
- package/src/System.spec.ts +4 -4
- package/src/System.ts +7 -7
- package/src/deprecated/createActionSystem.spec.ts +1 -1
- package/src/deprecated/createActionSystem.ts +3 -3
- package/src/deprecated/defineActionComponent.ts +1 -1
- package/src/deprecated/waitForActionCompletion.ts +1 -1
- package/src/deprecated/waitForComponentValueIn.ts +3 -3
- package/src/utils.ts +2 -2
- package/dist/chunk-YDQFEK6R.js.map +0 -1
package/src/Component.ts
CHANGED
@@ -23,6 +23,7 @@ export type ComponentMutationOptions = {
|
|
23
23
|
skipUpdateStream?: boolean;
|
24
24
|
};
|
25
25
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
26
27
|
function getComponentName(component: Component<any, any, any>) {
|
27
28
|
return (
|
28
29
|
component.metadata?.componentName ??
|
@@ -55,7 +56,7 @@ function getComponentName(component: Component<any, any, any>) {
|
|
55
56
|
export function defineComponent<S extends Schema, M extends Metadata, T = unknown>(
|
56
57
|
world: World,
|
57
58
|
schema: S,
|
58
|
-
options?: { id?: string; metadata?: M; indexed?: boolean }
|
59
|
+
options?: { id?: string; metadata?: M; indexed?: boolean },
|
59
60
|
) {
|
60
61
|
if (Object.keys(schema).length === 0) throw new Error("Component schema must have at least one key");
|
61
62
|
const id = options?.id ?? uuid();
|
@@ -86,7 +87,7 @@ export function setComponent<S extends Schema, T = unknown>(
|
|
86
87
|
component: Component<S, Metadata, T>,
|
87
88
|
entity: Entity,
|
88
89
|
value: ComponentValue<S, T>,
|
89
|
-
options: ComponentMutationOptions = {}
|
90
|
+
options: ComponentMutationOptions = {},
|
90
91
|
) {
|
91
92
|
const entitySymbol = getEntitySymbol(entity);
|
92
93
|
const prevValue = getComponentValue(component, entity);
|
@@ -110,7 +111,7 @@ export function setComponent<S extends Schema, T = unknown>(
|
|
110
111
|
"for entity",
|
111
112
|
entity,
|
112
113
|
". Existing keys: ",
|
113
|
-
Object.keys(component.values)
|
114
|
+
Object.keys(component.values),
|
114
115
|
);
|
115
116
|
}
|
116
117
|
}
|
@@ -141,7 +142,7 @@ export function updateComponent<S extends Schema, T = unknown>(
|
|
141
142
|
entity: Entity,
|
142
143
|
value: Partial<ComponentValue<S, T>>,
|
143
144
|
initialValue?: ComponentValue<S, T>,
|
144
|
-
options: ComponentMutationOptions = {}
|
145
|
+
options: ComponentMutationOptions = {},
|
145
146
|
) {
|
146
147
|
const currentValue = getComponentValue(component, entity);
|
147
148
|
if (currentValue === undefined) {
|
@@ -163,7 +164,7 @@ export function updateComponent<S extends Schema, T = unknown>(
|
|
163
164
|
export function removeComponent<S extends Schema, M extends Metadata, T = unknown>(
|
164
165
|
component: Component<S, M, T>,
|
165
166
|
entity: Entity,
|
166
|
-
options: ComponentMutationOptions = {}
|
167
|
+
options: ComponentMutationOptions = {},
|
167
168
|
) {
|
168
169
|
const entitySymbol = getEntitySymbol(entity);
|
169
170
|
const prevValue = getComponentValue(component, entity);
|
@@ -184,7 +185,7 @@ export function removeComponent<S extends Schema, M extends Metadata, T = unknow
|
|
184
185
|
*/
|
185
186
|
export function hasComponent<S extends Schema, T = unknown>(
|
186
187
|
component: Component<S, Metadata, T>,
|
187
|
-
entity: Entity
|
188
|
+
entity: Entity,
|
188
189
|
): boolean {
|
189
190
|
const entitySymbol = getEntitySymbol(entity);
|
190
191
|
const map = Object.values(component.values)[0];
|
@@ -201,7 +202,7 @@ export function hasComponent<S extends Schema, T = unknown>(
|
|
201
202
|
*/
|
202
203
|
export function getComponentValue<S extends Schema, T = unknown>(
|
203
204
|
component: Component<S, Metadata, T>,
|
204
|
-
entity: Entity
|
205
|
+
entity: Entity,
|
205
206
|
): ComponentValue<S, T> | undefined {
|
206
207
|
const value: Record<string, unknown> = {};
|
207
208
|
const entitySymbol = getEntitySymbol(entity);
|
@@ -230,7 +231,7 @@ export function getComponentValue<S extends Schema, T = unknown>(
|
|
230
231
|
*/
|
231
232
|
export function getComponentValueStrict<S extends Schema, T = unknown>(
|
232
233
|
component: Component<S, Metadata, T>,
|
233
|
-
entity: Entity
|
234
|
+
entity: Entity,
|
234
235
|
): ComponentValue<S, T> {
|
235
236
|
const value = getComponentValue(component, entity);
|
236
237
|
if (!value) throw new Error(`No value for component ${getComponentName(component)} on entity ${entity}`);
|
@@ -253,7 +254,7 @@ export function getComponentValueStrict<S extends Schema, T = unknown>(
|
|
253
254
|
*/
|
254
255
|
export function componentValueEquals<S extends Schema, T = unknown>(
|
255
256
|
a?: Partial<ComponentValue<S, T>>,
|
256
|
-
b?: ComponentValue<S, T
|
257
|
+
b?: ComponentValue<S, T>,
|
257
258
|
): boolean {
|
258
259
|
if (!a && !b) return true;
|
259
260
|
if (!a || !b) return false;
|
@@ -276,7 +277,7 @@ export function componentValueEquals<S extends Schema, T = unknown>(
|
|
276
277
|
*/
|
277
278
|
export function withValue<S extends Schema, T = unknown>(
|
278
279
|
component: Component<S, Metadata, T>,
|
279
|
-
value: ComponentValue<S, T
|
280
|
+
value: ComponentValue<S, T>,
|
280
281
|
): [Component<S, Metadata, T>, ComponentValue<S, T>] {
|
281
282
|
return [component, value];
|
282
283
|
}
|
@@ -290,7 +291,7 @@ export function withValue<S extends Schema, T = unknown>(
|
|
290
291
|
*/
|
291
292
|
export function getEntitiesWithValue<S extends Schema>(
|
292
293
|
component: Component<S> | Indexer<S>,
|
293
|
-
value: Partial<ComponentValue<S
|
294
|
+
value: Partial<ComponentValue<S>>,
|
294
295
|
): Set<Entity> {
|
295
296
|
// Shortcut for indexers
|
296
297
|
if (isIndexer(component) && isFullComponentValue(component, value)) {
|
@@ -315,7 +316,7 @@ export function getEntitiesWithValue<S extends Schema>(
|
|
315
316
|
* @returns Set of all entities in the given component.
|
316
317
|
*/
|
317
318
|
export function getComponentEntities<S extends Schema, T = unknown>(
|
318
|
-
component: Component<S, Metadata, T
|
319
|
+
component: Component<S, Metadata, T>,
|
319
320
|
): IterableIterator<Entity> {
|
320
321
|
return component.entities();
|
321
322
|
}
|
@@ -335,7 +336,7 @@ export function getComponentEntities<S extends Schema, T = unknown>(
|
|
335
336
|
* @returns overridable component
|
336
337
|
*/
|
337
338
|
export function overridableComponent<S extends Schema, M extends Metadata, T = unknown>(
|
338
|
-
component: Component<S, M, T
|
339
|
+
component: Component<S, M, T>,
|
339
340
|
): OverridableComponent<S, M, T> {
|
340
341
|
let nonce = 0;
|
341
342
|
|
@@ -457,7 +458,7 @@ export function overridableComponent<S extends Schema, M extends Metadata, T = u
|
|
457
458
|
component.update$
|
458
459
|
.pipe(
|
459
460
|
filter((e) => !overriddenEntityValues.get(getEntitySymbol(e.entity))),
|
460
|
-
map((update) => ({ ...update, component: overriddenComponent }))
|
461
|
+
map((update) => ({ ...update, component: overriddenComponent })),
|
461
462
|
)
|
462
463
|
.subscribe(update$);
|
463
464
|
|
@@ -475,7 +476,7 @@ export function clearLocalCache(component: Component, uniqueWorldIdentifier?: st
|
|
475
476
|
// Note: Only proof of concept for now - use this only for component that do not update frequently
|
476
477
|
export function createLocalCache<S extends Schema, M extends Metadata, T = unknown>(
|
477
478
|
component: Component<S, M, T>,
|
478
|
-
uniqueWorldIdentifier?: string
|
479
|
+
uniqueWorldIdentifier?: string,
|
479
480
|
): Component<S, M, T> {
|
480
481
|
const { world, update$, values } = component;
|
481
482
|
const cacheId = getLocalCacheId(component as Component, uniqueWorldIdentifier);
|
@@ -509,7 +510,7 @@ export function createLocalCache<S extends Schema, M extends Metadata, T = unkno
|
|
509
510
|
const updateSub = update$.subscribe(() => {
|
510
511
|
numUpdates++;
|
511
512
|
const encoded = JSON.stringify(
|
512
|
-
Object.entries(mapObject(values, (m) => [...m.entries()].map((e) => [getEntityString(e[0]), e[1]])))
|
513
|
+
Object.entries(mapObject(values, (m) => [...m.entries()].map((e) => [getEntityString(e[0]), e[1]]))),
|
513
514
|
);
|
514
515
|
localStorage.setItem(cacheId, encoded);
|
515
516
|
if (numUpdates > 200) {
|
@@ -520,7 +521,7 @@ export function createLocalCache<S extends Schema, M extends Metadata, T = unkno
|
|
520
521
|
numUpdates,
|
521
522
|
"times since",
|
522
523
|
new Date(creation).toLocaleTimeString(),
|
523
|
-
"- the local cache is in an alpha state and should not be used with components that update frequently yet"
|
524
|
+
"- the local cache is in an alpha state and should not be used with components that update frequently yet",
|
524
525
|
);
|
525
526
|
}
|
526
527
|
});
|
package/src/Entity.ts
CHANGED
@@ -17,7 +17,7 @@ import { Component, ComponentValue, Entity, EntitySymbol, World } from "./types"
|
|
17
17
|
export function createEntity(
|
18
18
|
world: World,
|
19
19
|
components?: [Component, ComponentValue][],
|
20
|
-
options?: { id?: string } | { idSuffix?: string }
|
20
|
+
options?: { id?: string } | { idSuffix?: string },
|
21
21
|
): Entity {
|
22
22
|
const entity = world.registerEntity(options ?? {});
|
23
23
|
|
package/src/Indexer.ts
CHANGED
@@ -18,7 +18,7 @@ import { Component, ComponentValue, Entity, EntitySymbol, Indexer, Metadata, Sch
|
|
18
18
|
* @returns Indexed version of the component.
|
19
19
|
*/
|
20
20
|
export function createIndexer<S extends Schema, M extends Metadata, T = unknown>(
|
21
|
-
component: Component<S, M, T
|
21
|
+
component: Component<S, M, T>,
|
22
22
|
): Indexer<S, M, T> {
|
23
23
|
const valueToEntities = new Map<string, Set<EntitySymbol>>();
|
24
24
|
|
package/src/Performance.spec.ts
CHANGED
@@ -35,6 +35,7 @@ describe("V2", () => {
|
|
35
35
|
const Position = defineComponentV2(world, { x: TypeV2.Number, y: TypeV2.Number });
|
36
36
|
|
37
37
|
defineSystem(world, [HasValueV2(Position, { x: 1, y: 1 })], (update) => {
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
38
39
|
const e = update;
|
39
40
|
});
|
40
41
|
|
package/src/Query.spec.ts
CHANGED
@@ -98,27 +98,27 @@ describe("Query", () => {
|
|
98
98
|
expect(runQuery([HasValue(OwnedByEntity, { value: Player })])).toEqual(new Set([Depth1]));
|
99
99
|
|
100
100
|
expect(runQuery([ProxyExpand(OwnedByEntity, 0), HasValue(OwnedByEntity, { value: Player })])).toEqual(
|
101
|
-
new Set([Depth1])
|
101
|
+
new Set([Depth1]),
|
102
102
|
);
|
103
103
|
|
104
104
|
expect(runQuery([ProxyExpand(OwnedByEntity, 1), HasValue(OwnedByEntity, { value: Player })])).toEqual(
|
105
|
-
new Set([Depth1, Depth2])
|
105
|
+
new Set([Depth1, Depth2]),
|
106
106
|
);
|
107
107
|
|
108
108
|
expect(runQuery([ProxyExpand(OwnedByEntity, 2), HasValue(OwnedByEntity, { value: Player })])).toEqual(
|
109
|
-
new Set([Depth1, Depth2, Depth3])
|
109
|
+
new Set([Depth1, Depth2, Depth3]),
|
110
110
|
);
|
111
111
|
|
112
112
|
expect(runQuery([ProxyExpand(OwnedByEntity, 3), HasValue(OwnedByEntity, { value: Player })])).toEqual(
|
113
|
-
new Set([Depth1, Depth2, Depth3, Depth4])
|
113
|
+
new Set([Depth1, Depth2, Depth3, Depth4]),
|
114
114
|
);
|
115
115
|
|
116
116
|
expect(runQuery([ProxyExpand(OwnedByEntity, 4), HasValue(OwnedByEntity, { value: Player })])).toEqual(
|
117
|
-
new Set([Depth1, Depth2, Depth3, Depth4, Depth5])
|
117
|
+
new Set([Depth1, Depth2, Depth3, Depth4, Depth5]),
|
118
118
|
);
|
119
119
|
|
120
120
|
expect(
|
121
|
-
runQuery([ProxyExpand(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(OwnedByEntity, { value: Player })])
|
121
|
+
runQuery([ProxyExpand(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(OwnedByEntity, { value: Player })]),
|
122
122
|
).toEqual(new Set([Depth1, Depth2, Depth3, Depth4, Depth5]));
|
123
123
|
});
|
124
124
|
|
@@ -132,8 +132,8 @@ describe("Query", () => {
|
|
132
132
|
expect(
|
133
133
|
runQuery(
|
134
134
|
[ProxyRead(OwnedByEntity, 1), HasValue(Name, { name: "Alice" })],
|
135
|
-
new Set([Depth1, Depth2, Depth3]) // Provide an initial set of entities
|
136
|
-
)
|
135
|
+
new Set([Depth1, Depth2, Depth3]), // Provide an initial set of entities
|
136
|
+
),
|
137
137
|
).toEqual(new Set([Depth1]));
|
138
138
|
|
139
139
|
expect(
|
@@ -142,7 +142,7 @@ describe("Query", () => {
|
|
142
142
|
HasValue(Name, { name: "Alice" }), // Get all entities with name Alice or owned by Alice
|
143
143
|
ProxyExpand(OwnedByEntity, 0), // Turn off proxy expand
|
144
144
|
NotValue(Name, { name: "Alice" }), // Filter Alice, only keep entities owned by Alice
|
145
|
-
])
|
145
|
+
]),
|
146
146
|
).toEqual(new Set([Depth1]));
|
147
147
|
|
148
148
|
expect(
|
@@ -151,15 +151,15 @@ describe("Query", () => {
|
|
151
151
|
HasValue(Name, { name: "Alice" }), // Get all child entities of Alice (including alice)
|
152
152
|
ProxyExpand(OwnedByEntity, 0), // Turn off proxy expand
|
153
153
|
NotValue(Name, { name: "Alice" }), // Filter Alice, only keep entities owned by Alice
|
154
|
-
])
|
154
|
+
]),
|
155
155
|
).toEqual(new Set([Depth1, Depth2, Depth3, Depth4]));
|
156
156
|
|
157
157
|
// Get all entities from the initial set [Depth3] that have an indirect owner called Alice
|
158
158
|
expect(
|
159
159
|
runQuery(
|
160
160
|
[ProxyRead(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: "Alice" })],
|
161
|
-
new Set([Depth3]) // Provide an initial set of entities
|
162
|
-
)
|
161
|
+
new Set([Depth3]), // Provide an initial set of entities
|
162
|
+
),
|
163
163
|
).toEqual(new Set([Depth3]));
|
164
164
|
|
165
165
|
// Get all entities that have an indirect owner called Alice
|
@@ -171,8 +171,8 @@ describe("Query", () => {
|
|
171
171
|
ProxyRead(OwnedByEntity, 0),
|
172
172
|
NotValue(Name, { name: "Alice" }),
|
173
173
|
],
|
174
|
-
new Set([Player, Depth1, Depth2, Depth3, Depth4]) // Provide an initial set of entities
|
175
|
-
)
|
174
|
+
new Set([Player, Depth1, Depth2, Depth3, Depth4]), // Provide an initial set of entities
|
175
|
+
),
|
176
176
|
).toEqual(new Set([Depth1, Depth2, Depth3, Depth4]));
|
177
177
|
|
178
178
|
// Get all entities from the initial set [Depth3] that have an indirect owner called Alice and their direct child
|
@@ -183,8 +183,8 @@ describe("Query", () => {
|
|
183
183
|
ProxyExpand(OwnedByEntity, 1),
|
184
184
|
HasValue(Name, { name: "Alice" }),
|
185
185
|
],
|
186
|
-
new Set([Depth2]) // Provide an initial set of entities
|
187
|
-
)
|
186
|
+
new Set([Depth2]), // Provide an initial set of entities
|
187
|
+
),
|
188
188
|
).toEqual(new Set([Depth2, Depth3]));
|
189
189
|
});
|
190
190
|
|
@@ -204,15 +204,15 @@ describe("Query", () => {
|
|
204
204
|
createEntity(world, [withValue(Position, { x: 1, y: 1 })]);
|
205
205
|
|
206
206
|
expect(runQuery([ProxyExpand(FromPrototype, 1), Has(CanMove), Not(Prototype)])).toEqual(
|
207
|
-
new Set([instance1, instance2])
|
207
|
+
new Set([instance1, instance2]),
|
208
208
|
);
|
209
209
|
|
210
210
|
expect(runQuery([Has(Position), ProxyRead(FromPrototype, 1), Has(CanMove)])).toEqual(
|
211
|
-
new Set([instance1, instance2])
|
211
|
+
new Set([instance1, instance2]),
|
212
212
|
);
|
213
213
|
|
214
214
|
expect(runQuery([ProxyRead(FromPrototype, 1), Has(Position), Has(CanMove)])).toEqual(
|
215
|
-
new Set([instance1, instance2])
|
215
|
+
new Set([instance1, instance2]),
|
216
216
|
);
|
217
217
|
});
|
218
218
|
|
@@ -287,7 +287,7 @@ describe("Query", () => {
|
|
287
287
|
Has(CanMove), // ...have the CanMove component...
|
288
288
|
ProxyRead(OwnedByEntity, Number.MAX_SAFE_INTEGER), // ...and for whose owner holds...
|
289
289
|
NotValue(Name, { name: "Alice" }), // ...their name is not Alice
|
290
|
-
])
|
290
|
+
]),
|
291
291
|
).toEqual(new Set([Instance3, Entity8]));
|
292
292
|
});
|
293
293
|
|
@@ -343,14 +343,14 @@ describe("Query", () => {
|
|
343
343
|
entity: entities[0],
|
344
344
|
component: CanMove,
|
345
345
|
value: [{ value: true }, undefined],
|
346
|
-
})
|
346
|
+
}),
|
347
347
|
);
|
348
348
|
expect(mock).toHaveBeenCalledWith(
|
349
349
|
expect.objectContaining({
|
350
350
|
entity: entities[1],
|
351
351
|
component: CanMove,
|
352
352
|
value: [{ value: true }, undefined],
|
353
|
-
})
|
353
|
+
}),
|
354
354
|
);
|
355
355
|
expect(mock).toBeCalledTimes(2);
|
356
356
|
|
@@ -360,7 +360,7 @@ describe("Query", () => {
|
|
360
360
|
entity: entities[2],
|
361
361
|
component: CanMove,
|
362
362
|
value: [{ value: true }, undefined],
|
363
|
-
})
|
363
|
+
}),
|
364
364
|
);
|
365
365
|
expect(mock).toHaveBeenCalledTimes(3);
|
366
366
|
});
|
@@ -388,7 +388,7 @@ describe("Query", () => {
|
|
388
388
|
entity: entity1,
|
389
389
|
component: CanMove,
|
390
390
|
value: [undefined, { value: true }],
|
391
|
-
})
|
391
|
+
}),
|
392
392
|
);
|
393
393
|
|
394
394
|
removeComponent(CanMove, entity2);
|
@@ -398,7 +398,7 @@ describe("Query", () => {
|
|
398
398
|
entity: entity2,
|
399
399
|
component: CanMove,
|
400
400
|
value: [undefined, { value: true }],
|
401
|
-
})
|
401
|
+
}),
|
402
402
|
);
|
403
403
|
});
|
404
404
|
});
|
@@ -617,7 +617,7 @@ describe("Query", () => {
|
|
617
617
|
|
618
618
|
const query1 = defineQuery(
|
619
619
|
[ProxyRead(OwnedByEntity, 1), HasValue(Name, { name: "Alice" })],
|
620
|
-
{ initialSet: new Set([Depth1, Depth2, Depth3]) } // Provide an initial set of entities
|
620
|
+
{ initialSet: new Set([Depth1, Depth2, Depth3]) }, // Provide an initial set of entities
|
621
621
|
);
|
622
622
|
query1.update$.subscribe();
|
623
623
|
|
@@ -639,7 +639,7 @@ describe("Query", () => {
|
|
639
639
|
|
640
640
|
const query4 = defineQuery(
|
641
641
|
[ProxyRead(OwnedByEntity, Number.MAX_SAFE_INTEGER), HasValue(Name, { name: "Alice" })],
|
642
|
-
{ initialSet: new Set([Depth3]) } // Provide an initial set of entities
|
642
|
+
{ initialSet: new Set([Depth3]) }, // Provide an initial set of entities
|
643
643
|
);
|
644
644
|
query4.update$.subscribe();
|
645
645
|
|
@@ -650,7 +650,7 @@ describe("Query", () => {
|
|
650
650
|
ProxyRead(OwnedByEntity, 0),
|
651
651
|
NotValue(Name, { name: "Alice" }),
|
652
652
|
],
|
653
|
-
{ initialSet: new Set([Player, Depth1, Depth2, Depth3, Depth4]) } // Provide an initial set of entities
|
653
|
+
{ initialSet: new Set([Player, Depth1, Depth2, Depth3, Depth4]) }, // Provide an initial set of entities
|
654
654
|
);
|
655
655
|
query5.update$.subscribe();
|
656
656
|
|
@@ -660,7 +660,7 @@ describe("Query", () => {
|
|
660
660
|
ProxyExpand(OwnedByEntity, 1),
|
661
661
|
HasValue(Name, { name: "Alice" }),
|
662
662
|
],
|
663
|
-
{ initialSet: new Set([Depth2]) } // Provide an initial set of entities
|
663
|
+
{ initialSet: new Set([Depth2]) }, // Provide an initial set of entities
|
664
664
|
);
|
665
665
|
query6.update$.subscribe();
|
666
666
|
|
package/src/Query.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { filterNullish } from "@latticexyz/utils";
|
2
2
|
import { observable, ObservableSet } from "mobx";
|
3
|
-
import { concat, concatMap, filter, from, map, merge, Observable, of, share
|
3
|
+
import { concat, concatMap, filter, from, map, merge, Observable, of, share } from "rxjs";
|
4
4
|
import {
|
5
5
|
componentValueEquals,
|
6
6
|
getComponentEntities,
|
@@ -15,7 +15,6 @@ import {
|
|
15
15
|
ComponentValue,
|
16
16
|
Entity,
|
17
17
|
EntityQueryFragment,
|
18
|
-
EntitySymbol,
|
19
18
|
HasQueryFragment,
|
20
19
|
HasValueQueryFragment,
|
21
20
|
NotQueryFragment,
|
@@ -88,7 +87,7 @@ export function Not<T extends Schema>(component: Component<T>): NotQueryFragment
|
|
88
87
|
*/
|
89
88
|
export function HasValue<T extends Schema>(
|
90
89
|
component: Component<T>,
|
91
|
-
value: Partial<ComponentValue<T
|
90
|
+
value: Partial<ComponentValue<T>>,
|
92
91
|
): HasValueQueryFragment<T> {
|
93
92
|
return { type: QueryFragmentType.HasValue, component, value };
|
94
93
|
}
|
@@ -112,7 +111,7 @@ export function HasValue<T extends Schema>(
|
|
112
111
|
*/
|
113
112
|
export function NotValue<T extends Schema>(
|
114
113
|
component: Component<T>,
|
115
|
-
value: Partial<ComponentValue<T
|
114
|
+
value: Partial<ComponentValue<T>>,
|
116
115
|
): NotValueQueryFragment<T> {
|
117
116
|
return { type: QueryFragmentType.NotValue, component, value };
|
118
117
|
}
|
@@ -199,7 +198,7 @@ function passesQueryFragment<T extends Schema>(entity: Entity, fragment: EntityQ
|
|
199
198
|
* @returns True if the query fragment is positive, else false.
|
200
199
|
*/
|
201
200
|
function isPositiveFragment<T extends Schema>(
|
202
|
-
fragment: QueryFragment<T
|
201
|
+
fragment: QueryFragment<T>,
|
203
202
|
): fragment is HasQueryFragment<T> | HasValueQueryFragment<T> {
|
204
203
|
return fragment.type === QueryFragmentType.Has || fragment.type == QueryFragmentType.HasValue;
|
205
204
|
}
|
@@ -211,7 +210,7 @@ function isPositiveFragment<T extends Schema>(
|
|
211
210
|
* @returns True if the query fragment is negative, else false.
|
212
211
|
*/
|
213
212
|
function isNegativeFragment<T extends Schema>(
|
214
|
-
fragment: QueryFragment<T
|
213
|
+
fragment: QueryFragment<T>,
|
215
214
|
): fragment is NotQueryFragment<T> | NotValueQueryFragment<T> {
|
216
215
|
return fragment.type === QueryFragmentType.Not || fragment.type == QueryFragmentType.NotValue;
|
217
216
|
}
|
@@ -253,7 +252,7 @@ function isBreakingPassState(passes: boolean, fragment: EntityQueryFragment<Sche
|
|
253
252
|
function passesQueryFragmentProxy<T extends Schema>(
|
254
253
|
entity: Entity,
|
255
254
|
fragment: EntityQueryFragment<T>,
|
256
|
-
proxyRead: ProxyReadQueryFragment
|
255
|
+
proxyRead: ProxyReadQueryFragment,
|
257
256
|
): boolean | null {
|
258
257
|
let proxyEntity = entity;
|
259
258
|
let passes = false;
|
@@ -288,7 +287,7 @@ function passesQueryFragmentProxy<T extends Schema>(
|
|
288
287
|
export function getChildEntities(
|
289
288
|
entity: Entity,
|
290
289
|
component: Component<{ value: Type.Entity }>,
|
291
|
-
depth: number
|
290
|
+
depth: number,
|
292
291
|
): Set<Entity> {
|
293
292
|
if (depth === 0) return new Set();
|
294
293
|
|
@@ -416,7 +415,7 @@ export function runQuery(fragments: QueryFragment[], initialSet?: Set<Entity>):
|
|
416
415
|
*/
|
417
416
|
export function defineQuery(
|
418
417
|
fragments: QueryFragment[],
|
419
|
-
options?: { runOnInit?: boolean; initialSet?: Set<Entity> }
|
418
|
+
options?: { runOnInit?: boolean; initialSet?: Set<Entity> },
|
420
419
|
): {
|
421
420
|
update$: Observable<ComponentUpdate & { type: UpdateType }>;
|
422
421
|
matching: ObservableSet<Entity>;
|
@@ -503,7 +502,7 @@ export function defineQuery(
|
|
503
502
|
return { ...update, type: UpdateType.Enter };
|
504
503
|
}
|
505
504
|
}),
|
506
|
-
filterNullish()
|
505
|
+
filterNullish(),
|
507
506
|
);
|
508
507
|
|
509
508
|
return {
|
@@ -521,7 +520,7 @@ export function defineQuery(
|
|
521
520
|
*/
|
522
521
|
export function defineUpdateQuery(
|
523
522
|
fragments: QueryFragment[],
|
524
|
-
options?: { runOnInit?: boolean }
|
523
|
+
options?: { runOnInit?: boolean },
|
525
524
|
): Observable<ComponentUpdate & { type: UpdateType }> {
|
526
525
|
return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Update));
|
527
526
|
}
|
@@ -535,7 +534,7 @@ export function defineUpdateQuery(
|
|
535
534
|
*/
|
536
535
|
export function defineEnterQuery(
|
537
536
|
fragments: QueryFragment[],
|
538
|
-
options?: { runOnInit?: boolean }
|
537
|
+
options?: { runOnInit?: boolean },
|
539
538
|
): Observable<ComponentUpdate> {
|
540
539
|
return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Enter));
|
541
540
|
}
|
@@ -549,7 +548,7 @@ export function defineEnterQuery(
|
|
549
548
|
*/
|
550
549
|
export function defineExitQuery(
|
551
550
|
fragments: QueryFragment[],
|
552
|
-
options?: { runOnInit?: boolean }
|
551
|
+
options?: { runOnInit?: boolean },
|
553
552
|
): Observable<ComponentUpdate> {
|
554
553
|
return defineQuery(fragments, options).update$.pipe(filter((e) => e.type === UpdateType.Exit));
|
555
554
|
}
|
package/src/System.spec.ts
CHANGED
@@ -100,13 +100,13 @@ describe("System", () => {
|
|
100
100
|
|
101
101
|
expect(mock).toHaveBeenCalledTimes(1);
|
102
102
|
expect(mock).toHaveBeenCalledWith(
|
103
|
-
expect.objectContaining({ entity: entity1, component: CanMove, value: [{ value: true }, undefined] })
|
103
|
+
expect.objectContaining({ entity: entity1, component: CanMove, value: [{ value: true }, undefined] }),
|
104
104
|
);
|
105
105
|
|
106
106
|
const entity2 = createEntity(world, [withValue(CanMove, { value: true })]);
|
107
107
|
expect(mock).toHaveBeenCalledTimes(2);
|
108
108
|
expect(mock).toHaveBeenCalledWith(
|
109
|
-
expect.objectContaining({ entity: entity2, component: CanMove, value: [{ value: true }, undefined] })
|
109
|
+
expect.objectContaining({ entity: entity2, component: CanMove, value: [{ value: true }, undefined] }),
|
110
110
|
);
|
111
111
|
});
|
112
112
|
|
@@ -126,13 +126,13 @@ describe("System", () => {
|
|
126
126
|
|
127
127
|
expect(mock).toHaveBeenCalledTimes(1);
|
128
128
|
expect(mock).toHaveBeenCalledWith(
|
129
|
-
expect.objectContaining({ entity: entity1, component: CanMove, value: [undefined, { value: true }] })
|
129
|
+
expect.objectContaining({ entity: entity1, component: CanMove, value: [undefined, { value: true }] }),
|
130
130
|
);
|
131
131
|
|
132
132
|
removeComponent(CanMove, entity2);
|
133
133
|
expect(mock).toHaveBeenCalledTimes(2);
|
134
134
|
expect(mock).toHaveBeenCalledWith(
|
135
|
-
expect.objectContaining({ entity: entity2, component: CanMove, value: [undefined, { value: true }] })
|
135
|
+
expect.objectContaining({ entity: entity2, component: CanMove, value: [undefined, { value: true }] }),
|
136
136
|
);
|
137
137
|
});
|
138
138
|
});
|
package/src/System.ts
CHANGED
@@ -36,7 +36,7 @@ export function defineUpdateSystem(
|
|
36
36
|
world: World,
|
37
37
|
query: QueryFragment[],
|
38
38
|
system: (update: ComponentUpdate) => void,
|
39
|
-
options: { runOnInit?: boolean } = { runOnInit: true }
|
39
|
+
options: { runOnInit?: boolean } = { runOnInit: true },
|
40
40
|
) {
|
41
41
|
defineRxSystem(world, defineUpdateQuery(query, options), system);
|
42
42
|
}
|
@@ -56,7 +56,7 @@ export function defineEnterSystem(
|
|
56
56
|
world: World,
|
57
57
|
query: QueryFragment[],
|
58
58
|
system: (update: ComponentUpdate) => void,
|
59
|
-
options: { runOnInit?: boolean } = { runOnInit: true }
|
59
|
+
options: { runOnInit?: boolean } = { runOnInit: true },
|
60
60
|
) {
|
61
61
|
defineRxSystem(world, defineEnterQuery(query, options), system);
|
62
62
|
}
|
@@ -76,7 +76,7 @@ export function defineExitSystem(
|
|
76
76
|
world: World,
|
77
77
|
query: QueryFragment[],
|
78
78
|
system: (update: ComponentUpdate) => void,
|
79
|
-
options: { runOnInit?: boolean } = { runOnInit: true }
|
79
|
+
options: { runOnInit?: boolean } = { runOnInit: true },
|
80
80
|
) {
|
81
81
|
defineRxSystem(world, defineExitQuery(query, options), system);
|
82
82
|
}
|
@@ -96,7 +96,7 @@ export function defineSystem(
|
|
96
96
|
world: World,
|
97
97
|
query: QueryFragment[],
|
98
98
|
system: (update: ComponentUpdate & { type: UpdateType }) => void,
|
99
|
-
options: { runOnInit?: boolean } = { runOnInit: true }
|
99
|
+
options: { runOnInit?: boolean } = { runOnInit: true },
|
100
100
|
) {
|
101
101
|
defineRxSystem(world, defineQuery(query, options).update$, system);
|
102
102
|
}
|
@@ -116,7 +116,7 @@ export function defineComponentSystem<S extends Schema>(
|
|
116
116
|
world: World,
|
117
117
|
component: Component<S>,
|
118
118
|
system: (update: ComponentUpdate<S>) => void,
|
119
|
-
options: { runOnInit?: boolean } = { runOnInit: true }
|
119
|
+
options: { runOnInit?: boolean } = { runOnInit: true },
|
120
120
|
) {
|
121
121
|
const initial$ = options?.runOnInit ? from(getComponentEntities(component)).pipe(toUpdateStream(component)) : EMPTY;
|
122
122
|
defineRxSystem(world, concat(initial$, component.update$), system);
|
@@ -135,7 +135,7 @@ export function defineSyncSystem<T extends Schema>(
|
|
135
135
|
query: QueryFragment[],
|
136
136
|
component: (entity: Entity) => Component<T>,
|
137
137
|
value: (entity: Entity) => ComponentValue<T>,
|
138
|
-
options: { update?: boolean; runOnInit?: boolean } = { update: false, runOnInit: true }
|
138
|
+
options: { update?: boolean; runOnInit?: boolean } = { update: false, runOnInit: true },
|
139
139
|
) {
|
140
140
|
defineSystem(
|
141
141
|
world,
|
@@ -145,6 +145,6 @@ export function defineSyncSystem<T extends Schema>(
|
|
145
145
|
if (type === UpdateType.Exit) removeComponent(component(entity), entity);
|
146
146
|
if (options?.update && type === UpdateType.Update) setComponent(component(entity), entity, value(entity));
|
147
147
|
},
|
148
|
-
options
|
148
|
+
options,
|
149
149
|
);
|
150
150
|
}
|
@@ -177,7 +177,7 @@ describe("ActionSystem", () => {
|
|
177
177
|
expect(runQuery([HasValue(Action, { on: settlement1 })])).toEqual(new Set([entity1]));
|
178
178
|
expect(runQuery([HasValue(Action, { on: settlement2 })])).toEqual(new Set([entity2]));
|
179
179
|
expect(runQuery([HasValue(Action, { state: ActionState.Requested })])).toEqual(
|
180
|
-
new Set([entity1, entity2, entity3])
|
180
|
+
new Set([entity1, entity2, entity3]),
|
181
181
|
);
|
182
182
|
});
|
183
183
|
|
@@ -15,7 +15,7 @@ export type ActionSystem = ReturnType<typeof createActionSystem>;
|
|
15
15
|
export function createActionSystem<M = unknown>(
|
16
16
|
world: World,
|
17
17
|
txReduced$: Observable<string>,
|
18
|
-
waitForTransaction?: (tx: string) => Promise<void
|
18
|
+
waitForTransaction?: (tx: string) => Promise<void>,
|
19
19
|
) {
|
20
20
|
// Action component
|
21
21
|
const Action = defineActionComponent<M>(world);
|
@@ -40,7 +40,7 @@ export function createActionSystem<M = unknown>(
|
|
40
40
|
* @returns Components including pending updates
|
41
41
|
*/
|
42
42
|
function withOptimisticUpdates<S extends Schema, M extends Metadata, T>(
|
43
|
-
component: Component<S, M, T
|
43
|
+
component: Component<S, M, T>,
|
44
44
|
): OverridableComponent<S, M, T> {
|
45
45
|
const optimisticComponent = componentsWithOptimisticUpdates[component.id] || overridableComponent(component);
|
46
46
|
|
@@ -99,7 +99,7 @@ export function createActionSystem<M = unknown>(
|
|
99
99
|
// This subscriotion makes sure the action requirement is checked again every time
|
100
100
|
// one of the referenced components changes or the pending updates map changes
|
101
101
|
const subscription = merge(
|
102
|
-
...Object.values(action.componentsWithOptimisticUpdates).map((c) => c.update$)
|
102
|
+
...Object.values(action.componentsWithOptimisticUpdates).map((c) => c.update$),
|
103
103
|
).subscribe(() => checkRequirement(action));
|
104
104
|
checkRequirement(action);
|
105
105
|
disposer.set(action.id, { dispose: () => subscription?.unsubscribe() });
|
@@ -12,7 +12,7 @@ export function defineActionComponent<T = unknown>(world: World) {
|
|
12
12
|
overrides: Type.OptionalStringArray,
|
13
13
|
txHash: Type.OptionalString,
|
14
14
|
},
|
15
|
-
{ id: "Action" }
|
15
|
+
{ id: "Action" },
|
16
16
|
);
|
17
17
|
return Action as Component<SchemaOf<typeof Action>, Metadata, T>;
|
18
18
|
}
|
@@ -5,7 +5,7 @@ import { waitForComponentValueIn } from "./waitForComponentValueIn";
|
|
5
5
|
|
6
6
|
export async function waitForActionCompletion(
|
7
7
|
Action: ReturnType<typeof defineActionComponent>,
|
8
|
-
entity: Entity
|
8
|
+
entity: Entity,
|
9
9
|
): Promise<void> {
|
10
10
|
return waitForComponentValueIn(Action, entity, [
|
11
11
|
{ state: ActionState.Cancelled },
|
@@ -6,7 +6,7 @@ import { Component, Metadata, Entity, ComponentValue, Schema } from "../types";
|
|
6
6
|
export function waitForComponentValueIn<S extends Schema, T>(
|
7
7
|
component: Component<S, Metadata, T>,
|
8
8
|
entity: Entity,
|
9
|
-
values: Partial<ComponentValue<S>>[]
|
9
|
+
values: Partial<ComponentValue<S>>[],
|
10
10
|
): Promise<void> {
|
11
11
|
const [resolve, , promise] = deferred<void>();
|
12
12
|
|
@@ -19,13 +19,13 @@ export function waitForComponentValueIn<S extends Schema, T>(
|
|
19
19
|
|
20
20
|
const value$ = component.update$.pipe(
|
21
21
|
filter((update) => update.entity === entity), // Ignore updates of other entities
|
22
|
-
map((update) => update.value[0]) // Map the update to the current value
|
22
|
+
map((update) => update.value[0]), // Map the update to the current value
|
23
23
|
);
|
24
24
|
|
25
25
|
const subscription = value$
|
26
26
|
.pipe(
|
27
27
|
startWith(getComponentValue(component, entity)),
|
28
|
-
filter((currentValue) => Boolean(values.find((searchValue) => componentValueEquals(searchValue, currentValue))))
|
28
|
+
filter((currentValue) => Boolean(values.find((searchValue) => componentValueEquals(searchValue, currentValue)))),
|
29
29
|
)
|
30
30
|
.subscribe(() => {
|
31
31
|
resolve();
|