@adobe/ecs 0.2.3 → 0.3.0
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/implementation/compiler/augmentations.js +8 -1
- package/dist/implementation/compiler/augmentations.js.map +1 -1
- package/dist/implementation/compiler/body-access.d.ts +18 -1
- package/dist/implementation/compiler/body-access.js +104 -15
- package/dist/implementation/compiler/body-access.js.map +1 -1
- package/dist/implementation/compiler/estree.d.ts +5 -2
- package/dist/implementation/compiler/estree.js +8 -5
- package/dist/implementation/compiler/estree.js.map +1 -1
- package/dist/implementation/compiler/inferred.js +5 -2
- package/dist/implementation/compiler/inferred.js.map +1 -1
- package/dist/implementation/compiler/transform.js +5 -4
- package/dist/implementation/compiler/transform.js.map +1 -1
- package/dist/implementation/compiler/type-schemas.d.ts +1 -0
- package/dist/implementation/compiler/type-schemas.js +35 -2
- package/dist/implementation/compiler/type-schemas.js.map +1 -1
- package/dist/implementation/world/activate.d.ts +14 -3
- package/dist/implementation/world/activate.js +28 -10
- package/dist/implementation/world/activate.js.map +1 -1
- package/dist/implementation/world/buffer.d.ts +7 -0
- package/dist/implementation/world/buffer.js +7 -0
- package/dist/implementation/world/buffer.js.map +1 -1
- package/dist/implementation/world/cascade.d.ts +10 -3
- package/dist/implementation/world/cascade.js +48 -23
- package/dist/implementation/world/cascade.js.map +1 -1
- package/dist/implementation/world/commands.js +41 -5
- package/dist/implementation/world/commands.js.map +1 -1
- package/dist/implementation/world/create.js +57 -6
- package/dist/implementation/world/create.js.map +1 -1
- package/dist/implementation/world/execute.d.ts +4 -4
- package/dist/implementation/world/execute.js +228 -93
- package/dist/implementation/world/execute.js.map +1 -1
- package/dist/implementation/world/finalizers.d.ts +10 -0
- package/dist/implementation/world/finalizers.js +60 -10
- package/dist/implementation/world/finalizers.js.map +1 -1
- package/dist/implementation/world/join.js +12 -2
- package/dist/implementation/world/join.js.map +1 -1
- package/dist/implementation/world/program.js +5 -1
- package/dist/implementation/world/program.js.map +1 -1
- package/dist/implementation/world/resource-changes.d.ts +18 -0
- package/dist/implementation/world/resource-changes.js +66 -0
- package/dist/implementation/world/resource-changes.js.map +1 -0
- package/dist/implementation/world/rows.js +21 -16
- package/dist/implementation/world/rows.js.map +1 -1
- package/dist/implementation/world/run-changes.d.ts +38 -0
- package/dist/implementation/world/run-changes.js +138 -0
- package/dist/implementation/world/run-changes.js.map +1 -0
- package/dist/implementation/world/runner.js +16 -12
- package/dist/implementation/world/runner.js.map +1 -1
- package/dist/implementation/world/state.d.ts +15 -0
- package/dist/implementation/world/state.js +22 -1
- package/dist/implementation/world/state.js.map +1 -1
- package/dist/implementation/world/synchronize.js +81 -24
- package/dist/implementation/world/synchronize.js.map +1 -1
- package/dist/implementation/world/tables-read.d.ts +5 -3
- package/dist/implementation/world/tables-read.js +16 -4
- package/dist/implementation/world/tables-read.js.map +1 -1
- package/dist/implementation/world/tables.d.ts +4 -2
- package/dist/implementation/world/tables.js +137 -38
- package/dist/implementation/world/tables.js.map +1 -1
- package/dist/implementation/world/thread-observer.js +1 -1
- package/dist/implementation/world/thread-observer.js.map +1 -1
- package/dist/implementation/world/thread-systems.js +10 -6
- package/dist/implementation/world/thread-systems.js.map +1 -1
- package/dist/implementation/world/up.js +7 -1
- package/dist/implementation/world/up.js.map +1 -1
- package/dist/implementation/world/where.d.ts +8 -0
- package/dist/implementation/world/where.js +40 -11
- package/dist/implementation/world/where.js.map +1 -1
- package/dist/interface/finalizer/body.d.ts +40 -4
- package/dist/interface/finalizer/create.d.ts +8 -6
- package/dist/interface/finalizer/create.js +45 -10
- package/dist/interface/finalizer/create.js.map +1 -1
- package/dist/interface/finalizer/declaration.d.ts +26 -4
- package/dist/interface/indexes/declaration-of.d.ts +17 -0
- package/dist/interface/indexes/declaration-of.js +20 -0
- package/dist/interface/indexes/declaration-of.js.map +1 -0
- package/dist/interface/indexes/index.d.ts +1 -0
- package/dist/interface/indexes/index.js +1 -0
- package/dist/interface/indexes/index.js.map +1 -1
- package/dist/interface/registry/index.d.ts +1 -1
- package/dist/interface/registry/index.js +1 -1
- package/dist/interface/registry/index.js.map +1 -1
- package/dist/interface/registry/names.d.ts +4 -0
- package/dist/interface/registry/names.js +20 -0
- package/dist/interface/registry/names.js.map +1 -1
- package/dist/interface/registry/schedule.d.ts +29 -1
- package/dist/interface/registry/schedule.js +261 -25
- package/dist/interface/registry/schedule.js.map +1 -1
- package/dist/interface/system/access.d.ts +15 -0
- package/dist/interface/system/check.d.ts +7 -0
- package/dist/interface/system/check.js +21 -3
- package/dist/interface/system/check.js.map +1 -1
- package/dist/interface/system/commands.d.ts +31 -5
- package/dist/interface/system/create-resolved.js +2 -1
- package/dist/interface/system/create-resolved.js.map +1 -1
- package/dist/interface/system/create.d.ts +10 -6
- package/dist/interface/system/create.js +5 -4
- package/dist/interface/system/create.js.map +1 -1
- package/dist/interface/system/declaration.d.ts +11 -4
- package/dist/interface/system/describe.d.ts +120 -0
- package/dist/interface/system/describe.js +116 -0
- package/dist/interface/system/describe.js.map +1 -0
- package/dist/interface/system/index.d.ts +1 -0
- package/dist/interface/system/index.js +1 -0
- package/dist/interface/system/index.js.map +1 -1
- package/dist/interface/system/leaf.d.ts +7 -0
- package/dist/interface/system/render.js +2 -1
- package/dist/interface/system/render.js.map +1 -1
- package/dist/interface/system/resolve.d.ts +14 -1
- package/dist/interface/system/resolve.js +50 -1
- package/dist/interface/system/resolve.js.map +1 -1
- package/dist/interface/system/shared-only.d.ts +7 -0
- package/dist/interface/system/shared-only.js +2 -0
- package/dist/interface/system/shared-only.js.map +1 -0
- package/dist/interface/system/tables.d.ts +9 -1
- package/dist/interface/world/table/index.d.ts +3 -2
- package/dist/interface/world/world.d.ts +15 -0
- package/package.json +11 -2
- package/src/guide/01-resources.md +4 -3
- package/src/guide/03-systems.out +1 -1
- package/src/guide/05-scheduling.md +12 -6
- package/src/guide/05-scheduling.out +1 -0
- package/src/guide/05-scheduling.ts +17 -0
- package/src/guide/06-sync-points.md +20 -8
- package/src/guide/06-sync-points.out +3 -2
- package/src/guide/06-sync-points.ts +24 -5
- package/src/guide/10-finalizers.md +5 -0
- package/src/guide/18-identity.out +1 -1
- package/src/guide/guide.test.ts +6 -153
- package/src/implementation/compiler/augmentations.test.ts +28 -1
- package/src/implementation/compiler/augmentations.ts +5 -1
- package/src/implementation/compiler/body-access.ts +102 -12
- package/src/implementation/compiler/estree.ts +9 -6
- package/src/implementation/compiler/inferred.test.ts +1 -1
- package/src/implementation/compiler/inferred.ts +5 -2
- package/src/implementation/compiler/transform.test.ts +41 -21
- package/src/implementation/compiler/transform.ts +5 -3
- package/src/implementation/compiler/type-schemas.ts +28 -2
- package/src/implementation/world/activate.ts +32 -10
- package/src/implementation/world/batches.test.ts +3 -3
- package/src/implementation/world/buffer.ts +7 -0
- package/src/implementation/world/cascade.ts +46 -22
- package/src/implementation/world/commands.ts +37 -5
- package/src/implementation/world/create.test.ts +8 -8
- package/src/implementation/world/create.ts +61 -6
- package/src/implementation/world/defaults.test.ts +24 -1
- package/src/implementation/world/despawn-where.test.ts +310 -0
- package/src/implementation/world/execute.ts +222 -86
- package/src/implementation/world/finalizers.ts +63 -11
- package/src/implementation/world/ids.test.ts +1 -1
- package/src/implementation/world/indexes.test.ts +2 -1
- package/src/implementation/world/join.test.ts +19 -0
- package/src/implementation/world/join.ts +16 -2
- package/src/implementation/world/lifetime.test-helper.ts +1 -1
- package/src/implementation/world/names.test.ts +1 -2
- package/src/implementation/world/passes.test.ts +61 -0
- package/src/implementation/world/perf.md +12 -1
- package/src/implementation/world/program.ts +4 -1
- package/src/implementation/world/resource-changes.test.ts +61 -0
- package/src/implementation/world/resource-changes.ts +67 -0
- package/src/implementation/world/rows.ts +21 -14
- package/src/implementation/world/rules.test.ts +37 -0
- package/src/implementation/world/run-changes.test.ts +88 -0
- package/src/implementation/world/run-changes.ts +160 -0
- package/src/implementation/world/run-results.test.ts +17 -0
- package/src/implementation/world/runner.ts +17 -13
- package/src/implementation/world/schedule.test.ts +42 -1
- package/src/implementation/world/shared.test.ts +2 -2
- package/src/implementation/world/state.ts +27 -0
- package/src/implementation/world/sync-points.test.ts +147 -0
- package/src/implementation/world/synchronize.ts +73 -20
- package/src/implementation/world/tables-read.ts +16 -5
- package/src/implementation/world/tables.test.ts +139 -4
- package/src/implementation/world/tables.ts +118 -35
- package/src/implementation/world/thread-observer.ts +1 -1
- package/src/implementation/world/thread-systems.ts +10 -6
- package/src/implementation/world/up.test.ts +13 -1
- package/src/implementation/world/up.ts +6 -1
- package/src/implementation/world/watch-traits-late.test-helper.ts +14 -0
- package/src/implementation/world/watch-traits.test-helper.ts +10 -0
- package/src/implementation/world/watch-traits.test.ts +62 -0
- package/src/implementation/world/where.ts +38 -10
- package/src/interface/finalizer/body.ts +33 -4
- package/src/interface/finalizer/create.test.ts +6 -1
- package/src/interface/finalizer/create.ts +45 -12
- package/src/interface/finalizer/declaration.ts +31 -4
- package/src/interface/indexes/create.test.ts +10 -0
- package/src/interface/indexes/declaration-of.ts +34 -0
- package/src/interface/indexes/index.ts +1 -0
- package/src/interface/registry/index.ts +1 -1
- package/src/interface/registry/names.ts +19 -0
- package/src/interface/registry/schedule.ts +275 -29
- package/src/interface/rule/rule.type-test.ts +23 -1
- package/src/interface/system/access.ts +16 -0
- package/src/interface/system/check.ts +17 -2
- package/src/interface/system/commands.ts +28 -4
- package/src/interface/system/commands.type-test.ts +31 -0
- package/src/interface/system/create-resolved.ts +2 -1
- package/src/interface/system/create.test.ts +10 -2
- package/src/interface/system/create.ts +9 -5
- package/src/interface/system/declaration.ts +10 -4
- package/src/interface/system/describe.test.ts +98 -0
- package/src/interface/system/describe.ts +122 -0
- package/src/interface/system/index.ts +1 -0
- package/src/interface/system/leaf.ts +7 -0
- package/src/interface/system/render.ts +2 -1
- package/src/interface/system/resolve.test.ts +17 -1
- package/src/interface/system/resolve.ts +60 -2
- package/src/interface/system/shared-only.ts +5 -0
- package/src/interface/system/system.type-test.ts +6 -0
- package/src/interface/system/tables.ts +7 -1
- package/src/interface/world/run.type-test.ts +11 -1
- package/src/interface/world/table/index.ts +3 -2
- package/src/interface/world/world.ts +13 -0
- package/src/perftest/benchmarks.ts +20 -0
- package/src/perftest/chained.ts +108 -0
- package/src/perftest/structural.ts +20 -0
- package/src/perftest/tables.ts +107 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { Entity } from "../../interface/entity/entity.js";
|
|
4
|
+
import { Finalizer } from "../../interface/finalizer/finalizer.js";
|
|
5
|
+
import { Rule } from "../../interface/rule/rule.js";
|
|
6
|
+
import type { Commands } from "../../interface/system/commands.js";
|
|
7
|
+
import { System } from "../../interface/system/system.js";
|
|
8
|
+
import type { World } from "../../interface/world/world.js";
|
|
9
|
+
import { workers } from "../../interface/resources/workers.js";
|
|
10
|
+
import { Observe } from "../../observe/index.js";
|
|
11
|
+
import { createWorld } from "./create.js";
|
|
12
|
+
import { runReported } from "./outcomes.test-helper.js";
|
|
13
|
+
import { count, read, readResource } from "./reads.test-helper.js";
|
|
14
|
+
|
|
15
|
+
const mark = Symbol("despawnWhereTest/mark");
|
|
16
|
+
const kept = Symbol("despawnWhereTest/kept");
|
|
17
|
+
const value = Symbol("despawnWhereTest/value");
|
|
18
|
+
const extra = Symbol("despawnWhereTest/extra");
|
|
19
|
+
const host = Symbol("despawnWhereTest/host");
|
|
20
|
+
const derivedFrom = Symbol("despawnWhereTest/derivedFrom");
|
|
21
|
+
const slot = Symbol("despawnWhereTest/slot");
|
|
22
|
+
const parent = Symbol("despawnWhereTest/parent");
|
|
23
|
+
const finalized = Symbol("despawnWhereTest/finalized");
|
|
24
|
+
const watched = Symbol("despawnWhereTest/watched");
|
|
25
|
+
const trigger = Symbol("despawnWhereTest/trigger");
|
|
26
|
+
const level = Symbol("despawnWhereTest/level");
|
|
27
|
+
const journal = Symbol("despawnWhereTest/journal");
|
|
28
|
+
const chunk = Symbol("despawnWhereTest/chunk");
|
|
29
|
+
// Never declared, so never registered.
|
|
30
|
+
const unregistered = Symbol("despawnWhereTest/unregistered");
|
|
31
|
+
|
|
32
|
+
declare module "@adobe/ecs" {
|
|
33
|
+
export interface Components {
|
|
34
|
+
[mark]: { type: true };
|
|
35
|
+
[kept]: { type: true };
|
|
36
|
+
[value]: { type: number };
|
|
37
|
+
[extra]: { type: true };
|
|
38
|
+
[host]: { type: true };
|
|
39
|
+
[derivedFrom]: { type: true };
|
|
40
|
+
[slot]: { type: number; derived: true };
|
|
41
|
+
[parent]: { type: Entity; reference: "owner" };
|
|
42
|
+
[finalized]: { type: number };
|
|
43
|
+
[watched]: { type: number };
|
|
44
|
+
[trigger]: { type: true };
|
|
45
|
+
[chunk]: { type: number; shared: true };
|
|
46
|
+
}
|
|
47
|
+
export interface Resources {
|
|
48
|
+
[level]: { type: number };
|
|
49
|
+
[journal]: { type: readonly string[]; default: [] };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Rule.create({ components: { with: [host, derivedFrom] }, derive: [slot] });
|
|
54
|
+
|
|
55
|
+
const seen: number[] = [];
|
|
56
|
+
Finalizer.create({ components: { with: [finalized] } }, row => { seen.push(row[finalized]); });
|
|
57
|
+
|
|
58
|
+
Finalizer.changes({ watch: [watched, mark] }, (changes, { resources }) => {
|
|
59
|
+
const lines = changes.groups.map(group => `${group.entities.map(entity => changes.previous(entity, watched)).join(",")}: +${group.added.length} -${group.removed.length}`);
|
|
60
|
+
resources[journal] = [...resources[journal], ...lines];
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/** A world, and a function running `body` once in a system with the world's commands, rejecting with what it threw. */
|
|
64
|
+
const withCommands = () => {
|
|
65
|
+
const world = createWorld({ [workers]: 0 });
|
|
66
|
+
let body: (commands: Commands) => void = () => {};
|
|
67
|
+
const issue = System.create({}, ({ commands }) => body(commands));
|
|
68
|
+
const run = async (next: (commands: Commands) => void): Promise<void> => {
|
|
69
|
+
body = next;
|
|
70
|
+
const [failure] = (await runReported(world, issue)).failures;
|
|
71
|
+
if (failure !== undefined) throw new Error(failure.message);
|
|
72
|
+
};
|
|
73
|
+
return { world, run };
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const alive = async (world: World, entity: Entity): Promise<boolean> =>
|
|
77
|
+
(await Observe.toPromise(Observe.entity(world, entity, { optional: [mark] }))) !== null;
|
|
78
|
+
|
|
79
|
+
const aliveEach = (world: World, entities: readonly Entity[]): Promise<boolean[]> => Promise.all(entities.map(entity => alive(world, entity)));
|
|
80
|
+
|
|
81
|
+
describe("despawnWhere", () => {
|
|
82
|
+
it("despawns every entity with each `with` component and no `not` one, across archetypes", async () => {
|
|
83
|
+
const { world, run } = withCommands();
|
|
84
|
+
const spawned: Entity[] = [];
|
|
85
|
+
await run(commands => {
|
|
86
|
+
for (let i = 0; i < 3; i++) {
|
|
87
|
+
spawned.push(commands.spawn([mark])());
|
|
88
|
+
spawned.push(commands.spawn([mark, value])({ [value]: i }));
|
|
89
|
+
spawned.push(commands.spawn([mark, value, extra])({ [value]: i }));
|
|
90
|
+
spawned.push(commands.spawn([value])({ [value]: i }));
|
|
91
|
+
spawned.push(commands.spawn([mark, kept])());
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
await run(commands => commands.despawnWhere({ with: [mark], not: [kept] }));
|
|
95
|
+
expect(await aliveEach(world, spawned)).toEqual([0, 1, 2].flatMap(() => [false, false, false, true, true]));
|
|
96
|
+
await run(commands => commands.despawnWhere({ with: [value, mark] }));
|
|
97
|
+
expect(await count(world, { required: [value] })).toBe(3);
|
|
98
|
+
await world.dispose();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("selects by the commands issued before it at the sync point; later ones to a despawned entity do nothing", async () => {
|
|
102
|
+
const { world, run } = withCommands();
|
|
103
|
+
let gains = Entity.none, loses = Entity.none, gainsAfter = Entity.none, changedAfter = Entity.none;
|
|
104
|
+
await run(commands => {
|
|
105
|
+
gains = commands.spawn([value])({ [value]: 1 });
|
|
106
|
+
loses = commands.spawn([mark, value])({ [value]: 2 });
|
|
107
|
+
gainsAfter = commands.spawn([value])({ [value]: 3 });
|
|
108
|
+
changedAfter = commands.spawn([mark, value])({ [value]: 4 });
|
|
109
|
+
});
|
|
110
|
+
await run(commands => {
|
|
111
|
+
commands.add(gains, mark);
|
|
112
|
+
commands.remove(loses, mark);
|
|
113
|
+
commands.despawnWhere({ with: [mark] });
|
|
114
|
+
commands.add(gainsAfter, mark);
|
|
115
|
+
commands.add(changedAfter, extra);
|
|
116
|
+
});
|
|
117
|
+
expect(await aliveEach(world, [gains, loses, gainsAfter, changedAfter])).toEqual([false, true, true, false]);
|
|
118
|
+
expect(await read(world, gainsAfter, mark)).toBe(true);
|
|
119
|
+
await world.dispose();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("keeps what the same sync point spawns, whenever spawned and whatever earlier commands give it", async () => {
|
|
123
|
+
const { world, run } = withCommands();
|
|
124
|
+
let alive = Entity.none;
|
|
125
|
+
await run(commands => { alive = commands.spawn([mark, value])({ [value]: 0 }); });
|
|
126
|
+
const spawned: Entity[] = [];
|
|
127
|
+
await run(commands => {
|
|
128
|
+
spawned.push(commands.spawn([mark, value])({ [value]: 1 }));
|
|
129
|
+
const tagged = commands.spawn([value])({ [value]: 2 });
|
|
130
|
+
commands.add(tagged, mark);
|
|
131
|
+
spawned.push(tagged);
|
|
132
|
+
commands.despawnWhere({ with: [mark] });
|
|
133
|
+
spawned.push(commands.spawn([mark, value])({ [value]: 3 }));
|
|
134
|
+
});
|
|
135
|
+
expect([await aliveEach(world, [alive]), await aliveEach(world, spawned)]).toEqual([[false], [true, true, true]]);
|
|
136
|
+
await world.dispose();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("clears the events a frame made before, keeping those it makes, in one system", async () => {
|
|
140
|
+
const { world, run } = withCommands();
|
|
141
|
+
let first = Entity.none, second = Entity.none;
|
|
142
|
+
await run(commands => {
|
|
143
|
+
commands.despawnWhere({ with: [mark] });
|
|
144
|
+
first = commands.spawn([mark, value])({ [value]: 1 });
|
|
145
|
+
});
|
|
146
|
+
expect(await aliveEach(world, [first])).toEqual([true]);
|
|
147
|
+
await run(commands => {
|
|
148
|
+
commands.despawnWhere({ with: [mark] });
|
|
149
|
+
second = commands.spawn([mark, value])({ [value]: 2 });
|
|
150
|
+
});
|
|
151
|
+
expect(await aliveEach(world, [first, second])).toEqual([false, true]);
|
|
152
|
+
await world.dispose();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("matches derived components, by where earlier commands leave an entity once derived", async () => {
|
|
156
|
+
const { world, run } = withCommands();
|
|
157
|
+
let derived = Entity.none, deriving = Entity.none, underived = Entity.none;
|
|
158
|
+
await run(commands => {
|
|
159
|
+
derived = commands.spawn([host, derivedFrom])();
|
|
160
|
+
deriving = commands.spawn([host])();
|
|
161
|
+
underived = commands.spawn([host, derivedFrom])();
|
|
162
|
+
});
|
|
163
|
+
expect(await read(world, derived, slot)).not.toBeNull();
|
|
164
|
+
await run(commands => {
|
|
165
|
+
commands.add(deriving, derivedFrom);
|
|
166
|
+
commands.remove(underived, derivedFrom);
|
|
167
|
+
commands.despawnWhere({ with: [slot] });
|
|
168
|
+
});
|
|
169
|
+
expect(await aliveEach(world, [derived, deriving, underived])).toEqual([false, false, true]);
|
|
170
|
+
await world.dispose();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("runs finalizers for each entity it despawns, reading its values", async () => {
|
|
174
|
+
const { world, run } = withCommands();
|
|
175
|
+
seen.length = 0;
|
|
176
|
+
await run(commands => {
|
|
177
|
+
for (let i = 0; i < 4; i++) commands.spawn(i % 2 === 0 ? [finalized, mark] : [finalized])({ [finalized]: i });
|
|
178
|
+
});
|
|
179
|
+
await run(commands => commands.despawnWhere({ with: [mark, finalized] }));
|
|
180
|
+
expect(seen.sort()).toEqual([0, 2]);
|
|
181
|
+
await world.dispose();
|
|
182
|
+
seen.length = 0;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("gives a changes finalizer the same changes as a despawn of each", async () => {
|
|
186
|
+
const journals: (readonly string[])[] = [];
|
|
187
|
+
for (const despawn of [
|
|
188
|
+
(commands: Commands, entities: readonly Entity[]) => { for (const entity of entities) commands.despawn(entity); },
|
|
189
|
+
(commands: Commands) => commands.despawnWhere({ with: [mark] }),
|
|
190
|
+
]) {
|
|
191
|
+
const { world, run } = withCommands();
|
|
192
|
+
const entities: Entity[] = [];
|
|
193
|
+
await run(commands => {
|
|
194
|
+
for (let i = 0; i < 3; i++) entities.push(commands.spawn([watched, mark])({ [watched]: i }));
|
|
195
|
+
entities.push(commands.spawn([watched, mark, extra])({ [watched]: 9 }));
|
|
196
|
+
commands.spawn([watched])({ [watched]: 5 });
|
|
197
|
+
});
|
|
198
|
+
await run(commands => despawn(commands, entities));
|
|
199
|
+
journals.push(await readResource(world, journal));
|
|
200
|
+
await world.dispose();
|
|
201
|
+
}
|
|
202
|
+
expect(journals[1]).toEqual(journals[0]);
|
|
203
|
+
expect(journals[1]).toContain("0,1,2: +0 -2");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("cascades owner references as a despawn does", async () => {
|
|
207
|
+
const { world, run } = withCommands();
|
|
208
|
+
let root = Entity.none, child = Entity.none, other = Entity.none;
|
|
209
|
+
await run(commands => {
|
|
210
|
+
root = commands.spawn([mark])();
|
|
211
|
+
child = commands.spawn([parent, value])({ [parent]: root, [value]: 1 });
|
|
212
|
+
other = commands.spawn([value])({ [value]: 2 });
|
|
213
|
+
});
|
|
214
|
+
await run(commands => commands.despawnWhere({ with: [mark] }));
|
|
215
|
+
expect(await aliveEach(world, [root, child, other])).toEqual([false, false, true]);
|
|
216
|
+
await world.dispose();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("never despawns the resource entity", async () => {
|
|
220
|
+
const { world, run } = withCommands();
|
|
221
|
+
const set = System.create({}, ({ resources, commands }) => {
|
|
222
|
+
resources[level] = 3;
|
|
223
|
+
commands.spawn([mark])();
|
|
224
|
+
commands.despawnWhere({ with: [mark] });
|
|
225
|
+
});
|
|
226
|
+
await world.run(set);
|
|
227
|
+
await run(commands => commands.despawnWhere({ with: [mark] }));
|
|
228
|
+
expect(await readResource(world, level)).toBe(3);
|
|
229
|
+
expect(await count(world, { required: [mark] })).toBe(0);
|
|
230
|
+
await world.dispose();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("throws in the issuing system, queuing nothing, for an empty `with` or a key that isn't a component", async () => {
|
|
234
|
+
const { world, run } = withCommands();
|
|
235
|
+
let entity = Entity.none;
|
|
236
|
+
await run(commands => { entity = commands.spawn([mark])(); });
|
|
237
|
+
// @ts-expect-error `with` names at least one component
|
|
238
|
+
await expect(run(commands => commands.despawnWhere({ with: [] }))).rejects.toThrow("names at least one component");
|
|
239
|
+
// @ts-expect-error never registered
|
|
240
|
+
await expect(run(commands => commands.despawnWhere({ with: [mark], not: [unregistered] }))).rejects.toThrow("no registered schema");
|
|
241
|
+
// @ts-expect-error a resource
|
|
242
|
+
await expect(run(commands => commands.despawnWhere({ with: [level] }))).rejects.toThrow("is a resource");
|
|
243
|
+
expect(await alive(world, entity)).toBe(true);
|
|
244
|
+
await world.dispose();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("runs in a system over rows, which isn't a kernel", async () => {
|
|
248
|
+
const { world, run } = withCommands();
|
|
249
|
+
const clear = System.create({ components: { with: [trigger] } }, (_row, { commands }) => {
|
|
250
|
+
commands.despawnWhere({ with: [mark] });
|
|
251
|
+
});
|
|
252
|
+
await run(commands => {
|
|
253
|
+
for (let i = 0; i < 5; i++) commands.spawn([mark, value])({ [value]: i });
|
|
254
|
+
commands.spawn([trigger])();
|
|
255
|
+
commands.spawn([trigger])();
|
|
256
|
+
});
|
|
257
|
+
const { failures } = await runReported(world, clear);
|
|
258
|
+
expect(failures).toEqual([]);
|
|
259
|
+
expect(await count(world, { required: [mark] })).toBe(0);
|
|
260
|
+
expect(await count(world, { required: [trigger] })).toBe(2);
|
|
261
|
+
await world.dispose();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("keeps to the archetypes holding where's shared values, each required as with requires it", async () => {
|
|
265
|
+
const { world, run } = withCommands();
|
|
266
|
+
const spawned: Entity[] = [];
|
|
267
|
+
await run(commands => {
|
|
268
|
+
spawned.push(commands.spawn([mark, chunk], { [chunk]: 1 })());
|
|
269
|
+
spawned.push(commands.spawn([mark, value, chunk], { [chunk]: 1 })({ [value]: 1 }));
|
|
270
|
+
spawned.push(commands.spawn([mark, chunk], { [chunk]: 2 })());
|
|
271
|
+
spawned.push(commands.spawn([mark])());
|
|
272
|
+
spawned.push(commands.spawn([value, chunk], { [chunk]: 1 })({ [value]: 2 }));
|
|
273
|
+
});
|
|
274
|
+
await run(commands => commands.despawnWhere({ with: [mark], where: { [chunk]: 1 } }));
|
|
275
|
+
expect(await aliveEach(world, spawned)).toEqual([false, false, true, true, true]);
|
|
276
|
+
await world.dispose();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("despawns by where what was alive before the sync point, keeping what it spawns, and none for a value no archetype holds", async () => {
|
|
280
|
+
const { world, run } = withCommands();
|
|
281
|
+
const spawned: Entity[] = [];
|
|
282
|
+
await run(commands => {
|
|
283
|
+
spawned.push(commands.spawn([mark, chunk], { [chunk]: 1 })());
|
|
284
|
+
spawned.push(commands.spawn([mark, chunk], { [chunk]: 2 })());
|
|
285
|
+
});
|
|
286
|
+
await run(commands => {
|
|
287
|
+
spawned.push(commands.spawn([mark, chunk], { [chunk]: 1 })());
|
|
288
|
+
commands.despawnWhere({ with: [mark], where: { [chunk]: 1 } });
|
|
289
|
+
commands.despawnWhere({ with: [mark], where: { [chunk]: 3 } });
|
|
290
|
+
});
|
|
291
|
+
expect(await aliveEach(world, spawned)).toEqual([false, true, true]);
|
|
292
|
+
await run(commands => commands.despawnWhere({ with: [mark], where: {} }));
|
|
293
|
+
expect(await aliveEach(world, spawned)).toEqual([false, false, false]);
|
|
294
|
+
await world.dispose();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("throws for a where that isn't values of shared components, queuing nothing", async () => {
|
|
298
|
+
const { world, run } = withCommands();
|
|
299
|
+
let entity = Entity.none;
|
|
300
|
+
await run(commands => { entity = commands.spawn([mark, chunk], { [chunk]: 1 })(); });
|
|
301
|
+
// @ts-expect-error where names only shared components
|
|
302
|
+
await expect(run(commands => commands.despawnWhere({ with: [mark], where: { [value]: 1 } }))).rejects.toThrow(/where names "despawnWhereTest\/value", which isn't a shared component/);
|
|
303
|
+
// @ts-expect-error where takes values, not predicates
|
|
304
|
+
await expect(run(commands => commands.despawnWhere({ with: [mark], where: { [chunk]: (given: number) => given === 1 } }))).rejects.toThrow(/gives "despawnWhereTest\/chunk" a predicate/);
|
|
305
|
+
// @ts-expect-error a value of the component's type
|
|
306
|
+
await expect(run(commands => commands.despawnWhere({ with: [mark], where: { [chunk]: "1" } }))).rejects.toThrow(/isn't one of its type's values/);
|
|
307
|
+
expect(await alive(world, entity)).toBe(true);
|
|
308
|
+
await world.dispose();
|
|
309
|
+
});
|
|
310
|
+
});
|