@latticexyz/recs 2.0.12-main-9be2bb86 → 2.0.12-main-e43c0938
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/package.json +6 -13
- package/CHANGELOG.md +0 -1248
- package/src/Component.spec.ts +0 -275
- package/src/Component.ts +0 -531
- package/src/Entity.spec.ts +0 -45
- package/src/Entity.ts +0 -46
- package/src/Indexer.spec.ts +0 -288
- package/src/Indexer.ts +0 -71
- package/src/Performance.spec.ts +0 -153
- package/src/Query.spec.ts +0 -811
- package/src/Query.ts +0 -554
- package/src/System.spec.ts +0 -139
- package/src/System.ts +0 -150
- package/src/World.spec.ts +0 -79
- package/src/World.ts +0 -102
- package/src/constants.ts +0 -54
- package/src/deprecated/constants.ts +0 -9
- package/src/deprecated/createActionSystem.spec.ts +0 -501
- package/src/deprecated/createActionSystem.ts +0 -236
- package/src/deprecated/defineActionComponent.ts +0 -18
- package/src/deprecated/index.ts +0 -2
- package/src/deprecated/types.ts +0 -45
- package/src/deprecated/waitForActionCompletion.ts +0 -15
- package/src/deprecated/waitForComponentValueIn.ts +0 -38
- package/src/index.ts +0 -9
- package/src/types.ts +0 -260
- package/src/utils.ts +0 -68
@@ -1,501 +0,0 @@
|
|
1
|
-
import { deferred, sleep } from "@latticexyz/utils";
|
2
|
-
import { ReplaySubject } from "rxjs";
|
3
|
-
import { defineComponent, getComponentValueStrict, withValue, setComponent } from "../Component";
|
4
|
-
import { createEntity } from "../Entity";
|
5
|
-
import { runQuery, HasValue } from "../Query";
|
6
|
-
import { createWorld } from "../World";
|
7
|
-
import { Type } from "../constants";
|
8
|
-
import { World, Component } from "../types";
|
9
|
-
import { waitForComponentValueIn } from "./waitForComponentValueIn";
|
10
|
-
import { ActionState } from "./constants";
|
11
|
-
import { createActionSystem } from "./createActionSystem";
|
12
|
-
import { waitForActionCompletion } from "./waitForActionCompletion";
|
13
|
-
|
14
|
-
describe("ActionSystem", () => {
|
15
|
-
let world: World;
|
16
|
-
let Resource: Component<{ amount: Type.Number }>;
|
17
|
-
let Action: Component<{
|
18
|
-
state: Type.String;
|
19
|
-
on: Type.OptionalEntity;
|
20
|
-
metadata: Type.OptionalT;
|
21
|
-
overrides: Type.OptionalStringArray;
|
22
|
-
txHash: Type.OptionalString;
|
23
|
-
}>;
|
24
|
-
let actions: ReturnType<typeof createActionSystem>;
|
25
|
-
let txReduced$: ReplaySubject<string>;
|
26
|
-
|
27
|
-
beforeEach(async () => {
|
28
|
-
world = createWorld();
|
29
|
-
txReduced$ = new ReplaySubject<string>();
|
30
|
-
actions = createActionSystem(world, txReduced$, async () => {
|
31
|
-
// mimic wait for tx
|
32
|
-
await sleep(100);
|
33
|
-
});
|
34
|
-
Action = actions.Action;
|
35
|
-
Resource = defineComponent(world, { amount: Type.Number });
|
36
|
-
});
|
37
|
-
|
38
|
-
afterEach(() => {
|
39
|
-
world.dispose();
|
40
|
-
});
|
41
|
-
|
42
|
-
it("should immediately execute actions if their requirement is met and set the Action component", async () => {
|
43
|
-
const mockFn = jest.fn();
|
44
|
-
const entity = actions.add({
|
45
|
-
id: "action",
|
46
|
-
components: {},
|
47
|
-
requirement: () => true,
|
48
|
-
updates: () => [],
|
49
|
-
execute: () => {
|
50
|
-
mockFn();
|
51
|
-
},
|
52
|
-
});
|
53
|
-
|
54
|
-
expect(mockFn).toHaveBeenCalledTimes(1);
|
55
|
-
expect(getComponentValueStrict(Action, entity).state).toBe(ActionState.Executing);
|
56
|
-
await waitForActionCompletion(Action, entity);
|
57
|
-
expect(getComponentValueStrict(Action, entity).state).toBe(ActionState.Complete);
|
58
|
-
});
|
59
|
-
|
60
|
-
it("should not execute actions if their requirement is not met and set the Action component", () => {
|
61
|
-
const mockFn = jest.fn();
|
62
|
-
const entity = actions.add({
|
63
|
-
id: "action",
|
64
|
-
components: {},
|
65
|
-
requirement: () => false,
|
66
|
-
updates: () => [],
|
67
|
-
execute: () => {
|
68
|
-
mockFn();
|
69
|
-
},
|
70
|
-
});
|
71
|
-
|
72
|
-
expect(mockFn).toHaveBeenCalledTimes(0);
|
73
|
-
expect(getComponentValueStrict(Action, entity).state).toBe(ActionState.Requested);
|
74
|
-
});
|
75
|
-
|
76
|
-
it("should set the Action component of failed actions", async () => {
|
77
|
-
const [, reject, promise] = deferred<void>();
|
78
|
-
const entity = actions.add({
|
79
|
-
id: "action",
|
80
|
-
components: {},
|
81
|
-
requirement: () => true,
|
82
|
-
updates: () => [],
|
83
|
-
execute: () => promise,
|
84
|
-
});
|
85
|
-
|
86
|
-
reject(new Error("action failed"));
|
87
|
-
|
88
|
-
await waitForActionCompletion(Action, entity);
|
89
|
-
|
90
|
-
expect(getComponentValueStrict(Action, entity).state).toBe(ActionState.Failed);
|
91
|
-
});
|
92
|
-
|
93
|
-
it("should set the Action component of cancelled actions", async () => {
|
94
|
-
const entity = actions.add({
|
95
|
-
id: "action",
|
96
|
-
components: {},
|
97
|
-
requirement: () => false,
|
98
|
-
updates: () => [],
|
99
|
-
execute: () => void 0,
|
100
|
-
});
|
101
|
-
|
102
|
-
const cancelled = actions.cancel("action");
|
103
|
-
await waitForActionCompletion(Action, entity);
|
104
|
-
|
105
|
-
expect(getComponentValueStrict(Action, entity).state).toBe(ActionState.Cancelled);
|
106
|
-
expect(cancelled).toBe(true);
|
107
|
-
});
|
108
|
-
|
109
|
-
it("should not be possible to cancel actions that are already executing", async () => {
|
110
|
-
const [resolve, , promise] = deferred<void>();
|
111
|
-
const entity = actions.add({
|
112
|
-
id: "action",
|
113
|
-
components: {},
|
114
|
-
requirement: () => true,
|
115
|
-
updates: () => [],
|
116
|
-
execute: () => promise,
|
117
|
-
});
|
118
|
-
|
119
|
-
const cancelled = actions.cancel("action");
|
120
|
-
resolve();
|
121
|
-
await waitForActionCompletion(Action, entity);
|
122
|
-
expect(getComponentValueStrict(Action, entity).state).toBe(ActionState.Complete);
|
123
|
-
expect(cancelled).toBe(false);
|
124
|
-
});
|
125
|
-
|
126
|
-
it("should execute actions if components it depends on changed and the requirement is met now", () => {
|
127
|
-
const mockFn = jest.fn();
|
128
|
-
const player = createEntity(world, [withValue(Resource, { amount: 0 })]);
|
129
|
-
|
130
|
-
actions.add({
|
131
|
-
id: "action",
|
132
|
-
components: { Resource },
|
133
|
-
requirement: ({ Resource }) => getComponentValueStrict(Resource, player).amount > 100,
|
134
|
-
updates: () => [],
|
135
|
-
execute: () => {
|
136
|
-
mockFn();
|
137
|
-
},
|
138
|
-
});
|
139
|
-
|
140
|
-
expect(mockFn).toHaveBeenCalledTimes(0);
|
141
|
-
setComponent(Resource, player, { amount: 99 });
|
142
|
-
expect(mockFn).toHaveBeenCalledTimes(0);
|
143
|
-
setComponent(Resource, player, { amount: 101 });
|
144
|
-
expect(mockFn).toHaveBeenCalledTimes(1);
|
145
|
-
});
|
146
|
-
|
147
|
-
it("should return all actions related to a given entity", () => {
|
148
|
-
const settlement1 = createEntity(world);
|
149
|
-
const settlement2 = createEntity(world);
|
150
|
-
|
151
|
-
const entity1 = actions.add({
|
152
|
-
id: "action1",
|
153
|
-
on: settlement1,
|
154
|
-
components: { Resource },
|
155
|
-
requirement: () => false,
|
156
|
-
updates: () => [],
|
157
|
-
execute: () => void 0,
|
158
|
-
});
|
159
|
-
|
160
|
-
const entity2 = actions.add({
|
161
|
-
id: "action2",
|
162
|
-
on: settlement2,
|
163
|
-
components: { Resource },
|
164
|
-
requirement: () => false,
|
165
|
-
updates: () => [],
|
166
|
-
execute: () => void 0,
|
167
|
-
});
|
168
|
-
|
169
|
-
const entity3 = actions.add({
|
170
|
-
id: "action3",
|
171
|
-
components: { Resource },
|
172
|
-
requirement: () => false,
|
173
|
-
updates: () => [],
|
174
|
-
execute: () => void 0,
|
175
|
-
});
|
176
|
-
|
177
|
-
expect(runQuery([HasValue(Action, { on: settlement1 })])).toEqual(new Set([entity1]));
|
178
|
-
expect(runQuery([HasValue(Action, { on: settlement2 })])).toEqual(new Set([entity2]));
|
179
|
-
expect(runQuery([HasValue(Action, { state: ActionState.Requested })])).toEqual(
|
180
|
-
new Set([entity1, entity2, entity3]),
|
181
|
-
);
|
182
|
-
});
|
183
|
-
|
184
|
-
it("should not remove pending update until all corresponding tx have been reduced", async () => {
|
185
|
-
const player = createEntity(world, [withValue(Resource, { amount: 100 })]);
|
186
|
-
|
187
|
-
const entity1 = actions.add({
|
188
|
-
id: "action1",
|
189
|
-
components: { Resource },
|
190
|
-
requirement: () => true,
|
191
|
-
updates: ({ Resource }) => [
|
192
|
-
{
|
193
|
-
component: Resource,
|
194
|
-
entity: player,
|
195
|
-
value: { amount: getComponentValueStrict(Resource, player).amount - 1 },
|
196
|
-
},
|
197
|
-
],
|
198
|
-
execute: async () => Promise.resolve("tx1"),
|
199
|
-
});
|
200
|
-
|
201
|
-
const entity2 = actions.add({
|
202
|
-
id: "action2",
|
203
|
-
components: { Resource },
|
204
|
-
// Resource needs to be 100 in order for this action to be executed
|
205
|
-
requirement: ({ Resource }) => getComponentValueStrict(Resource, player).amount === 100,
|
206
|
-
updates: () => [],
|
207
|
-
execute: () => void 0,
|
208
|
-
});
|
209
|
-
|
210
|
-
await waitForComponentValueIn(Action, entity1, [{ state: ActionState.WaitingForTxEvents }]);
|
211
|
-
// While action1 is waiting for tx, action 2 is not executed yet
|
212
|
-
expect(getComponentValueStrict(Action, entity1).state).toBe(ActionState.WaitingForTxEvents);
|
213
|
-
expect(getComponentValueStrict(Action, entity2).state).toBe(ActionState.Requested);
|
214
|
-
|
215
|
-
txReduced$.next("tx1");
|
216
|
-
// Now it's done
|
217
|
-
await waitForComponentValueIn(Action, entity1, [{ state: ActionState.Complete }]);
|
218
|
-
expect(getComponentValueStrict(Action, entity1).state).toBe(ActionState.Complete);
|
219
|
-
await sleep(0);
|
220
|
-
expect(getComponentValueStrict(Action, entity2).state).toBe(ActionState.Complete);
|
221
|
-
});
|
222
|
-
|
223
|
-
// TODO: get tests to pass
|
224
|
-
it.skip("should execute actions if the requirement is met while taking into account pending updates", async () => {
|
225
|
-
const requirementSpy1 = jest.fn();
|
226
|
-
const requirementSpy2 = jest.fn();
|
227
|
-
const requirementSpy3 = jest.fn();
|
228
|
-
|
229
|
-
const executeSpy1 = jest.fn();
|
230
|
-
const executeSpy2 = jest.fn();
|
231
|
-
const executeSpy3 = jest.fn();
|
232
|
-
|
233
|
-
const player = createEntity(world, [withValue(Resource, { amount: 0 })]);
|
234
|
-
|
235
|
-
let nonce = 0;
|
236
|
-
|
237
|
-
// First schedule action1
|
238
|
-
const [resolveAction1, , action1Promise] = deferred<void>();
|
239
|
-
const entity1 = actions.add({
|
240
|
-
id: "action1",
|
241
|
-
components: { Resource },
|
242
|
-
// This action requires a resource amount of 100 to be executed
|
243
|
-
requirement: ({ Resource }) => {
|
244
|
-
requirementSpy1();
|
245
|
-
return getComponentValueStrict(Resource, player).amount >= 100;
|
246
|
-
},
|
247
|
-
// When this action is executed it will subtract 100 from the resource amount
|
248
|
-
updates: ({ Resource }) => [
|
249
|
-
{
|
250
|
-
component: Resource,
|
251
|
-
entity: player,
|
252
|
-
value: { amount: getComponentValueStrict(Resource, player).amount - 100 },
|
253
|
-
},
|
254
|
-
],
|
255
|
-
execute: async () => {
|
256
|
-
executeSpy1(nonce++);
|
257
|
-
await action1Promise;
|
258
|
-
const { amount } = getComponentValueStrict(Resource, player);
|
259
|
-
setComponent(Resource, player, { amount: amount - 100 });
|
260
|
-
},
|
261
|
-
});
|
262
|
-
|
263
|
-
// Action1 is not executed yet because requirement is not met
|
264
|
-
expect(executeSpy1).toHaveBeenCalledTimes(0);
|
265
|
-
|
266
|
-
// The requirement was checked once when adding the action
|
267
|
-
expect(requirementSpy1).toHaveBeenCalledTimes(1);
|
268
|
-
|
269
|
-
// Then shedule action3
|
270
|
-
actions.add({
|
271
|
-
id: "action3",
|
272
|
-
components: { Resource },
|
273
|
-
// This action also requires a resource amount of 100 to be executed
|
274
|
-
requirement: ({ Resource }) => {
|
275
|
-
requirementSpy3();
|
276
|
-
const amount = getComponentValueStrict(Resource, player).amount;
|
277
|
-
return amount >= 100;
|
278
|
-
},
|
279
|
-
updates: ({ Resource }) => [
|
280
|
-
{
|
281
|
-
component: Resource,
|
282
|
-
entity: player,
|
283
|
-
value: { amount: getComponentValueStrict(Resource, player).amount - 100 },
|
284
|
-
},
|
285
|
-
],
|
286
|
-
execute: () => {
|
287
|
-
executeSpy3(nonce++);
|
288
|
-
},
|
289
|
-
});
|
290
|
-
|
291
|
-
// Action3 is also not executed yet because the requirement is not met
|
292
|
-
expect(executeSpy3).toHaveBeenCalledTimes(0);
|
293
|
-
|
294
|
-
// The requirement was cheecked once when adding the action
|
295
|
-
expect(requirementSpy3).toHaveBeenCalledTimes(1);
|
296
|
-
|
297
|
-
// Action 1's requirement was not checked again, because neither pending updates nor components changed.
|
298
|
-
expect(requirementSpy1).toHaveBeenCalledTimes(1);
|
299
|
-
|
300
|
-
// Now schedule action2.
|
301
|
-
// This action declares it will update the Resource component to be 100
|
302
|
-
const [resolveAction2, , action2Promise] = deferred<void>();
|
303
|
-
const entity2 = actions.add({
|
304
|
-
id: "action2",
|
305
|
-
components: { Resource },
|
306
|
-
requirement: () => {
|
307
|
-
requirementSpy2();
|
308
|
-
return true;
|
309
|
-
},
|
310
|
-
updates: () => [{ component: Resource, entity: player, value: { amount: 100 } }],
|
311
|
-
execute: async () => {
|
312
|
-
executeSpy2(nonce++);
|
313
|
-
await action2Promise;
|
314
|
-
const { amount } = getComponentValueStrict(Resource, player);
|
315
|
-
setComponent(Resource, player, { amount: amount + 100 });
|
316
|
-
},
|
317
|
-
});
|
318
|
-
|
319
|
-
// action2 is executed immediately
|
320
|
-
expect(executeSpy2).toHaveBeenCalledTimes(1);
|
321
|
-
expect(executeSpy2).toHaveBeenCalledWith(0);
|
322
|
-
|
323
|
-
// But it is not done yet, because the promise is not resolved
|
324
|
-
await waitForComponentValueIn(Action, entity2, [{ state: ActionState.Executing }]);
|
325
|
-
expect(getComponentValueStrict(Action, entity2).state).toBe(ActionState.Executing);
|
326
|
-
|
327
|
-
// action 2's requirement was checked only once
|
328
|
-
expect(requirementSpy2).toHaveBeenCalledTimes(1);
|
329
|
-
|
330
|
-
// Executing action 2 added pending updates and thereby triggered rechecking the requirements of action 1 and action 3
|
331
|
-
expect(requirementSpy1).toHaveBeenCalledTimes(2);
|
332
|
-
expect(requirementSpy3).toHaveBeenCalledTimes(2);
|
333
|
-
|
334
|
-
// Action 1 is already executed before action 2 resolves because it trusts action 2's update declaration
|
335
|
-
expect(executeSpy1).toHaveBeenCalledTimes(1);
|
336
|
-
|
337
|
-
// Action 1 should be executed after action 2
|
338
|
-
expect(executeSpy1).toHaveBeenCalledWith(1);
|
339
|
-
|
340
|
-
// action 3 should not have been executed, because action 1 declared it will reduce the resource amount, such that action3's requirement is not met
|
341
|
-
expect(executeSpy3).toHaveBeenCalledTimes(0);
|
342
|
-
|
343
|
-
// Now resolve action2
|
344
|
-
resolveAction2();
|
345
|
-
await waitForActionCompletion(Action, entity2);
|
346
|
-
|
347
|
-
// The real component amount should be at 100 now
|
348
|
-
expect(getComponentValueStrict(Resource, player).amount).toBe(100);
|
349
|
-
|
350
|
-
// Removing action 2's pending updates and modifying the component state should have triggered two requirement checks on action 3
|
351
|
-
expect(requirementSpy3).toHaveBeenCalledTimes(4);
|
352
|
-
|
353
|
-
// action3 should still not have been executed because action2 is not resolved yet and declared an update
|
354
|
-
expect(executeSpy3).toHaveBeenCalledTimes(0);
|
355
|
-
|
356
|
-
// Now resolve action1
|
357
|
-
resolveAction1();
|
358
|
-
await waitForActionCompletion(Action, entity1);
|
359
|
-
|
360
|
-
// The real component amount should be at 0 now
|
361
|
-
expect(getComponentValueStrict(Resource, player).amount).toBe(0);
|
362
|
-
|
363
|
-
// Removing action 1's pending updates and modifying the component state should have triggered two requirement checks on action 3
|
364
|
-
expect(requirementSpy3).toHaveBeenCalledTimes(6);
|
365
|
-
|
366
|
-
// action3 should still not have been executed
|
367
|
-
expect(executeSpy3).toHaveBeenCalledTimes(0);
|
368
|
-
|
369
|
-
// Setting the resource amount to 100 should trigger a requirement check on action 3
|
370
|
-
setComponent(Resource, player, { amount: 100 });
|
371
|
-
expect(requirementSpy3).toHaveBeenCalledTimes(7);
|
372
|
-
|
373
|
-
// Now action3 should finally have been executed
|
374
|
-
expect(executeSpy3).toHaveBeenCalledTimes(1);
|
375
|
-
expect(executeSpy3).toHaveBeenCalledWith(2);
|
376
|
-
|
377
|
-
// In total action 1's requirements should have been checked 2 times
|
378
|
-
expect(requirementSpy1).toHaveBeenCalledTimes(2);
|
379
|
-
|
380
|
-
// In total action 2's requirements should have been checked 1 time
|
381
|
-
expect(requirementSpy2).toHaveBeenCalledTimes(1);
|
382
|
-
|
383
|
-
// In total action 3's requirements should have been checked 7 times
|
384
|
-
expect(requirementSpy3).toHaveBeenCalledTimes(7);
|
385
|
-
});
|
386
|
-
|
387
|
-
it("declaring component updates should not modify real components", async () => {
|
388
|
-
const player = createEntity(world, [withValue(Resource, { amount: 0 })]);
|
389
|
-
|
390
|
-
expect(getComponentValueStrict(Resource, player)).toEqual({ amount: 0 });
|
391
|
-
|
392
|
-
const [resolve, , promise] = deferred<void>();
|
393
|
-
const entity = actions.add({
|
394
|
-
id: "action",
|
395
|
-
components: { Resource },
|
396
|
-
requirement: () => true,
|
397
|
-
updates: () => [{ component: Resource, entity: player, value: { amount: 1000 } }],
|
398
|
-
execute: async () => {
|
399
|
-
await promise;
|
400
|
-
},
|
401
|
-
});
|
402
|
-
|
403
|
-
expect(getComponentValueStrict(Resource, player)).toEqual({ amount: 0 });
|
404
|
-
|
405
|
-
resolve();
|
406
|
-
await waitForActionCompletion(Action, entity);
|
407
|
-
|
408
|
-
expect(getComponentValueStrict(Resource, player)).toEqual({ amount: 0 });
|
409
|
-
});
|
410
|
-
|
411
|
-
// TODO: get tests to pass
|
412
|
-
it.skip("should rerun the requirement function only if a component value accessed in the requirement changed", () => {
|
413
|
-
const player = createEntity(world, [withValue(Resource, { amount: 0 })]);
|
414
|
-
const requirementSpy = jest.fn();
|
415
|
-
|
416
|
-
actions.add({
|
417
|
-
id: "action",
|
418
|
-
components: { Resource },
|
419
|
-
requirement: ({ Resource }) => {
|
420
|
-
requirementSpy();
|
421
|
-
return getComponentValueStrict(Resource, player).amount >= 100;
|
422
|
-
},
|
423
|
-
updates: () => [],
|
424
|
-
execute: async () => void 0,
|
425
|
-
});
|
426
|
-
|
427
|
-
// The requirement should be checked once when adding the action
|
428
|
-
expect(requirementSpy).toHaveBeenCalledTimes(1);
|
429
|
-
|
430
|
-
// Setting unrelated values in the component should not retrigger a requirement check
|
431
|
-
const player2 = createEntity(world, [withValue(Resource, { amount: 0 })]);
|
432
|
-
setComponent(Resource, player2, { amount: 10 });
|
433
|
-
expect(requirementSpy).toHaveBeenCalledTimes(1);
|
434
|
-
|
435
|
-
// Setting a relevant value in the component should trigger a requirement check
|
436
|
-
setComponent(Resource, player, { amount: 10 });
|
437
|
-
expect(requirementSpy).toHaveBeenCalledTimes(2);
|
438
|
-
});
|
439
|
-
|
440
|
-
// TODO: get tests to pass
|
441
|
-
it.skip("should rerun the requirement function only if a pending update relevant to a value accessed in the requirement changed", () => {
|
442
|
-
const player1 = createEntity(world, [withValue(Resource, { amount: 0 })]);
|
443
|
-
const player2 = createEntity(world);
|
444
|
-
|
445
|
-
const requirementSpy = jest.fn();
|
446
|
-
|
447
|
-
actions.add({
|
448
|
-
id: "action1",
|
449
|
-
components: { Resource },
|
450
|
-
requirement: ({ Resource }) => {
|
451
|
-
requirementSpy();
|
452
|
-
return getComponentValueStrict(Resource, player1).amount >= 100;
|
453
|
-
},
|
454
|
-
updates: () => [],
|
455
|
-
execute: () => void 0,
|
456
|
-
});
|
457
|
-
|
458
|
-
// The requirement should be checked once when adding the action
|
459
|
-
expect(requirementSpy).toHaveBeenCalledTimes(1);
|
460
|
-
|
461
|
-
// Another action is executed, which does not declare any updates
|
462
|
-
actions.add({
|
463
|
-
id: "action2",
|
464
|
-
components: { Resource },
|
465
|
-
requirement: () => true,
|
466
|
-
updates: () => [],
|
467
|
-
execute: () => void 0,
|
468
|
-
});
|
469
|
-
|
470
|
-
// Executing actions with no pending updates that don't modify component states should not trigger a requirement check
|
471
|
-
expect(requirementSpy).toHaveBeenCalledTimes(1);
|
472
|
-
|
473
|
-
// Another action declares an update to the resource amount of player2, which is unrelated to action1's requirement
|
474
|
-
actions.add({
|
475
|
-
id: "action3",
|
476
|
-
components: { Resource },
|
477
|
-
requirement: () => true,
|
478
|
-
updates: () => [{ component: Resource, entity: player2, value: { amount: 1000 } }],
|
479
|
-
execute: () => void 0,
|
480
|
-
});
|
481
|
-
|
482
|
-
// Unrelated pending updates should not retrigger a requirement check
|
483
|
-
expect(requirementSpy).toHaveBeenCalledTimes(1);
|
484
|
-
|
485
|
-
const [resolve, , promise] = deferred<void>();
|
486
|
-
// Another action declares an update to the resource amount of player1, which is relevant to action1's requirement
|
487
|
-
actions.add({
|
488
|
-
id: "action4",
|
489
|
-
components: { Resource },
|
490
|
-
requirement: () => true,
|
491
|
-
updates: () => [{ component: Resource, entity: player1, value: { amount: 10000 } }],
|
492
|
-
execute: async () => {
|
493
|
-
await promise;
|
494
|
-
},
|
495
|
-
});
|
496
|
-
|
497
|
-
// Relevant pending updates should trigger a requirement check
|
498
|
-
expect(requirementSpy).toHaveBeenCalledTimes(2);
|
499
|
-
resolve();
|
500
|
-
});
|
501
|
-
});
|