@adobe/ecs 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/implementation/compiler/describe.d.ts +1 -1
- package/dist/implementation/compiler/describe.js +3 -3
- package/dist/implementation/compiler/describe.js.map +1 -1
- package/dist/implementation/compiler/signature.js +1 -1
- package/dist/implementation/compiler/transform.js +3 -3
- package/dist/implementation/compiler/transform.js.map +1 -1
- package/dist/implementation/world/commands.js +50 -1
- package/dist/implementation/world/commands.js.map +1 -1
- package/dist/implementation/world/entities.d.ts +2 -0
- package/dist/implementation/world/entities.js +6 -0
- package/dist/implementation/world/entities.js.map +1 -1
- package/dist/implementation/world/join.js +10 -2
- package/dist/implementation/world/join.js.map +1 -1
- package/dist/implementation/world/register.js +2 -2
- package/dist/implementation/world/rows.d.ts +1 -1
- package/dist/implementation/world/rows.js +5 -0
- package/dist/implementation/world/rows.js.map +1 -1
- package/dist/implementation/world/runner.js +3 -3
- package/dist/implementation/world/runner.js.map +1 -1
- package/dist/implementation/world/sharing.js +1 -1
- package/dist/implementation/world/staging.d.ts +4 -0
- package/dist/implementation/world/staging.js +15 -3
- package/dist/implementation/world/staging.js.map +1 -1
- package/dist/implementation/world/tables-read.d.ts +7 -0
- package/dist/implementation/world/tables-read.js +60 -0
- package/dist/implementation/world/tables-read.js.map +1 -0
- package/dist/implementation/world/tables.d.ts +9 -0
- package/dist/implementation/world/tables.js +355 -0
- package/dist/implementation/world/tables.js.map +1 -0
- 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 +1 -0
- package/dist/implementation/world/thread-systems.js.map +1 -1
- package/dist/index.d.ts +8 -28
- package/dist/index.js +4 -9
- package/dist/index.js.map +1 -1
- package/dist/interface/component/declarations.d.ts +4 -0
- package/dist/interface/component/declarations.js +8 -0
- package/dist/interface/component/declarations.js.map +1 -0
- package/dist/interface/component/declared.d.ts +12 -0
- package/dist/interface/component/declared.js +2 -0
- package/dist/interface/component/declared.js.map +1 -0
- package/dist/interface/component/index.d.ts +2 -0
- package/dist/interface/component/index.js +1 -1
- package/dist/interface/component/index.js.map +1 -1
- package/dist/interface/registry/names.d.ts +2 -0
- package/dist/interface/registry/names.js +9 -0
- package/dist/interface/registry/names.js.map +1 -1
- package/dist/interface/registry/schedule.d.ts +3 -3
- package/dist/interface/registry/schedule.js +3 -3
- package/dist/interface/registry/schedule.js.map +1 -1
- package/dist/interface/resource/declarations.d.ts +4 -0
- package/dist/interface/resource/declarations.js +8 -0
- package/dist/interface/resource/declarations.js.map +1 -0
- package/dist/interface/resource/index.d.ts +2 -0
- package/dist/interface/resource/index.js +1 -1
- package/dist/interface/resource/index.js.map +1 -1
- package/dist/interface/system/body.d.ts +11 -6
- package/dist/interface/system/check.d.ts +1 -1
- package/dist/interface/system/check.js +25 -9
- package/dist/interface/system/check.js.map +1 -1
- package/dist/interface/system/commands.d.ts +16 -0
- package/dist/interface/system/create.d.ts +21 -9
- package/dist/interface/system/create.js +3 -3
- package/dist/interface/system/create.js.map +1 -1
- package/dist/interface/system/declaration.d.ts +9 -2
- package/dist/interface/system/tables.d.ts +20 -0
- package/dist/interface/system/tables.js +2 -0
- package/dist/interface/system/tables.js.map +1 -0
- package/dist/interface/world/index.d.ts +1 -1
- package/dist/interface/world/table/index.d.ts +26 -0
- package/dist/interface/world/table/index.js +3 -0
- package/dist/interface/world/table/index.js.map +1 -0
- package/dist/interface/world/table/table.d.ts +11 -0
- package/dist/interface/world/table/table.js +2 -0
- package/dist/interface/world/table/table.js.map +1 -0
- package/dist/interface/world/world.d.ts +10 -10
- package/dist/world.d.ts +27 -0
- package/dist/world.js +27 -0
- package/dist/world.js.map +1 -0
- package/package.json +1 -1
- package/src/exports.test.ts +1 -1
- package/src/guide/00-keys.ts +1 -1
- package/src/guide/01-resources.md +1 -1
- package/src/guide/01-resources.ts +1 -1
- package/src/guide/02-entities.ts +2 -2
- package/src/guide/03-systems.md +2 -2
- package/src/guide/03-systems.out +1 -1
- package/src/guide/03-systems.ts +3 -3
- package/src/guide/04-ticks.md +1 -1
- package/src/guide/04-ticks.ts +3 -3
- package/src/guide/05-scheduling.ts +1 -1
- package/src/guide/06-sync-points.md +9 -2
- package/src/guide/06-sync-points.ts +2 -2
- package/src/guide/07-change-filters.ts +2 -2
- package/src/guide/08-input.ts +1 -1
- package/src/guide/09-rules.ts +1 -1
- package/src/guide/11-traits.ts +3 -3
- package/src/guide/12-lookups.ts +2 -2
- package/src/guide/13-indexes.ts +4 -4
- package/src/guide/14-kernels.ts +1 -1
- package/src/guide/15-reductions.ts +1 -1
- package/src/guide/16-gpu.ts +1 -1
- package/src/guide/17-hierarchies.ts +1 -1
- package/src/guide/18-identity.out +1 -1
- package/src/guide/19-threads.md +4 -2
- package/src/guide/19-threads.ts +2 -2
- package/src/guide/20-shared.ts +3 -3
- package/src/guide/21-dom.md +2 -2
- package/src/guide/21-dom.ts +1 -1
- package/src/guide/22-canvas.md +1 -1
- package/src/guide/22-canvas.ts +1 -1
- package/src/guide/CLAUDE.md +1 -1
- package/src/guide/guide.test.ts +7 -5
- package/src/implementation/compiler/describe.test-helper.ts +1 -1
- package/src/implementation/compiler/describe.test.ts +1 -1
- package/src/implementation/compiler/describe.ts +3 -3
- package/src/implementation/compiler/hygiene.test.ts +1 -1
- package/src/implementation/compiler/plugin.test.ts +2 -2
- package/src/implementation/compiler/signature.ts +1 -1
- package/src/implementation/compiler/transform.test.ts +3 -3
- package/src/implementation/compiler/transform.ts +3 -3
- package/src/implementation/world/change-blocks.test.ts +1 -1
- package/src/implementation/world/commands.ts +44 -1
- package/src/implementation/world/defaults.test.ts +1 -1
- package/src/implementation/world/entities.ts +6 -0
- package/src/implementation/world/finalizers.test.ts +1 -1
- package/src/implementation/world/index-changed.test.ts +2 -2
- package/src/implementation/world/index-joined.test.ts +5 -5
- package/src/implementation/world/index-order.test.ts +1 -1
- package/src/implementation/world/join.test.ts +4 -4
- package/src/implementation/world/join.ts +10 -2
- package/src/implementation/world/joined-observer.test.ts +1 -1
- package/src/implementation/world/joining-worker.fixture.ts +1 -1
- package/src/implementation/world/joining.fixture.ts +1 -1
- package/src/implementation/world/joining.test-helper.ts +1 -1
- package/src/implementation/world/kernel-spawns.test.ts +1 -1
- package/src/implementation/world/observe-factories.test.ts +2 -2
- package/src/implementation/world/observe-ticks-cases.test.ts +1 -1
- package/src/implementation/world/observe.test-helper.ts +1 -1
- package/src/implementation/world/observer-contained-worker.fixture.ts +1 -1
- package/src/implementation/world/observer-contained.test-helper.ts +1 -1
- package/src/implementation/world/observer-contained.test.ts +2 -2
- package/src/implementation/world/observer-joined.test-helper.ts +4 -4
- package/src/implementation/world/observer-joined.test.ts +23 -15
- package/src/implementation/world/observer.browser.test.ts +2 -2
- package/src/implementation/world/observer.test.ts +8 -8
- package/src/implementation/world/optional-workers.fixture.ts +1 -1
- package/src/implementation/world/optional.test.ts +7 -7
- package/src/implementation/world/reclaim.test.ts +3 -3
- package/src/implementation/world/register.ts +2 -2
- package/src/implementation/world/remote-objects.test-helper.ts +1 -1
- package/src/implementation/world/rows.ts +5 -1
- package/src/implementation/world/run-results.test.ts +10 -10
- package/src/implementation/world/runner.ts +3 -3
- package/src/implementation/world/shared-gpu.browser.test.ts +2 -2
- package/src/implementation/world/shared-kernels.test.ts +2 -2
- package/src/implementation/world/shared-workers.fixture.ts +2 -2
- package/src/implementation/world/shared.test.ts +8 -8
- package/src/implementation/world/sharing.ts +1 -1
- package/src/implementation/world/staging.ts +16 -3
- package/src/implementation/world/tables-read.ts +59 -0
- package/src/implementation/world/tables.test.ts +267 -0
- package/src/implementation/world/tables.ts +330 -0
- package/src/implementation/world/tails.test.ts +1 -1
- package/src/implementation/world/target.test.ts +2 -2
- package/src/implementation/world/thread-observer.ts +1 -1
- package/src/implementation/world/thread-systems.test-helper.ts +3 -3
- package/src/implementation/world/thread-systems.ts +1 -0
- package/src/implementation/world/where.test.ts +4 -4
- package/src/implementation/world/workers.fixture.ts +2 -2
- package/src/index.ts +4 -29
- package/src/interface/component/declarations.ts +11 -0
- package/src/interface/component/declared.ts +9 -0
- package/src/interface/component/index.ts +2 -0
- package/src/interface/index.type-test.ts +2 -2
- package/src/interface/indexes/index.type-test.ts +1 -1
- package/src/interface/registry/names.ts +8 -0
- package/src/interface/registry/schedule.ts +4 -4
- package/src/interface/resource/declarations.ts +11 -0
- package/src/interface/resource/index.ts +2 -0
- package/src/interface/system/body.ts +7 -5
- package/src/interface/system/check.ts +22 -8
- package/src/interface/system/commands.ts +14 -0
- package/src/interface/system/create.test.ts +13 -13
- package/src/interface/system/create.ts +15 -10
- package/src/interface/system/declaration.ts +9 -2
- package/src/interface/system/lists.type-test.ts +1 -1
- package/src/interface/system/optional.type-test.ts +1 -1
- package/src/interface/system/shared.type-test.ts +1 -1
- package/src/interface/system/system.type-test.ts +26 -20
- package/src/interface/system/tables.ts +21 -0
- package/src/interface/system/up.type-test.ts +3 -3
- package/src/interface/world/index.ts +2 -2
- package/src/interface/world/run.type-test.ts +3 -3
- package/src/interface/world/table/index.ts +19 -0
- package/src/interface/world/table/table.ts +12 -0
- package/src/interface/world/world.ts +10 -10
- package/src/observe/entity.ts +2 -2
- package/src/observe/resource.ts +2 -2
- package/src/observe/select.ts +4 -4
- package/src/observe/world-state.ts +3 -3
- package/src/perftest/benchmarks.ts +1 -1
- package/src/perftest/indexes.ts +3 -3
- package/src/perftest/loop.ts +1 -1
- package/src/perftest/observer.ts +4 -4
- package/src/perftest/reads.ts +2 -2
- package/src/perftest/remote.ts +5 -5
- package/src/perftest/strings.ts +1 -1
- package/src/perftest/suite.ts +1 -1
- package/src/world.test.ts +2 -2
- package/src/world.ts +32 -0
package/dist/world.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Create } from './interface/world/create.js';
|
|
2
|
+
import type { Join } from './interface/world/join.js';
|
|
3
|
+
export type { Table } from './interface/world/table/table.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a world on this thread, synchronously, with resources starting at the given values in place of their
|
|
6
|
+
* defaults. This thread is its `"world"` thread for the world's life: it runs the sync points, the ticks and the GPU
|
|
7
|
+
* device, and fills every logical thread no joined thread claims. Keys are the resources' symbols, built-in ones
|
|
8
|
+
* included (`{ [workers]: 0 }`); string keys are never a resource's, so they're ignored. Throws for a symbol that isn't
|
|
9
|
+
* a resource, or is one only the world writes. While a declaration's trait or default doesn't hold, each call fails to
|
|
10
|
+
* compile, once, naming each such key, its members and what's wrong with them.
|
|
11
|
+
* @function
|
|
12
|
+
*/
|
|
13
|
+
export declare const create: Create;
|
|
14
|
+
/**
|
|
15
|
+
* Joins, from another thread, a world that `share` let join through the other end of `port`, claiming the logical
|
|
16
|
+
* `threads` this thread fills: their objects live here, and the systems and finalizers reading them run here. Resolves,
|
|
17
|
+
* once the world accepted the claim, to the same world: its `run` runs systems there, and its `add` and `remove` take
|
|
18
|
+
* readonly systems, which run here after the world's sync points. Add them before awaiting anything else, so the
|
|
19
|
+
* world's `share` resolves with them in place. Its `dispose` disconnects this thread. Rejects with an `Error` saying
|
|
20
|
+
* why it didn't join: the world refused the claim, was disposed, or its thread ended first, as the platform reports it:
|
|
21
|
+
* the world's `Worker` failing to start or throwing first, a port closed, as in node, or, in a browser, the world's
|
|
22
|
+
* thread ending once it was shared through a port this thread listened on. A browser world's worker that the page ended
|
|
23
|
+
* before this thread heard from it isn't reported, so the join stays pending. Rejects too where memory can't be shared,
|
|
24
|
+
* as without cross-origin isolation.
|
|
25
|
+
* @function
|
|
26
|
+
*/
|
|
27
|
+
export declare const join: Join;
|
package/dist/world.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import { create as createWorld, join as joinWorld } from './implementation/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a world on this thread, synchronously, with resources starting at the given values in place of their
|
|
5
|
+
* defaults. This thread is its `"world"` thread for the world's life: it runs the sync points, the ticks and the GPU
|
|
6
|
+
* device, and fills every logical thread no joined thread claims. Keys are the resources' symbols, built-in ones
|
|
7
|
+
* included (`{ [workers]: 0 }`); string keys are never a resource's, so they're ignored. Throws for a symbol that isn't
|
|
8
|
+
* a resource, or is one only the world writes. While a declaration's trait or default doesn't hold, each call fails to
|
|
9
|
+
* compile, once, naming each such key, its members and what's wrong with them.
|
|
10
|
+
* @function
|
|
11
|
+
*/
|
|
12
|
+
export const create = createWorld;
|
|
13
|
+
/**
|
|
14
|
+
* Joins, from another thread, a world that `share` let join through the other end of `port`, claiming the logical
|
|
15
|
+
* `threads` this thread fills: their objects live here, and the systems and finalizers reading them run here. Resolves,
|
|
16
|
+
* once the world accepted the claim, to the same world: its `run` runs systems there, and its `add` and `remove` take
|
|
17
|
+
* readonly systems, which run here after the world's sync points. Add them before awaiting anything else, so the
|
|
18
|
+
* world's `share` resolves with them in place. Its `dispose` disconnects this thread. Rejects with an `Error` saying
|
|
19
|
+
* why it didn't join: the world refused the claim, was disposed, or its thread ended first, as the platform reports it:
|
|
20
|
+
* the world's `Worker` failing to start or throwing first, a port closed, as in node, or, in a browser, the world's
|
|
21
|
+
* thread ending once it was shared through a port this thread listened on. A browser world's worker that the page ended
|
|
22
|
+
* before this thread heard from it isn't reported, so the join stays pending. Rejects too where memory can't be shared,
|
|
23
|
+
* as without cross-origin isolation.
|
|
24
|
+
* @function
|
|
25
|
+
*/
|
|
26
|
+
export const join = joinWorld;
|
|
27
|
+
//# sourceMappingURL=world.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world.js","sourceRoot":"","sources":["../src/world.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,MAAM,IAAI,WAAW,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAMrF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAW,WAAW,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,IAAI,GAAS,SAAS,CAAC"}
|
package/package.json
CHANGED
package/src/exports.test.ts
CHANGED
|
@@ -50,7 +50,7 @@ export const world: World = World.create({ [workers]: 0 });
|
|
|
50
50
|
// Another thread joins it through the other end of a channel the world shares.
|
|
51
51
|
declare const port: MessagePort;
|
|
52
52
|
export const joined: Promise<World> = World.join(port, { threads: ["main"] });
|
|
53
|
-
export const read = System.create({
|
|
53
|
+
export const read = System.create({ readonly: true, components: { with: [position] }, lookup: { at: { with: [position] } } }, () => {});
|
|
54
54
|
export const added = world.add(read);
|
|
55
55
|
`;
|
|
56
56
|
|
package/src/guide/00-keys.ts
CHANGED
|
@@ -23,7 +23,7 @@ const award = System.create({ components: { with: [score] } }, (row, { resources
|
|
|
23
23
|
row[score] += resources[bonus];
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
const read = System.create({
|
|
26
|
+
const read = System.create({ readonly: true, components: { with: [score] } }, row => {
|
|
27
27
|
console.log("score", row[score]);
|
|
28
28
|
});
|
|
29
29
|
|
|
@@ -17,6 +17,6 @@ that isn't data, such as an `AudioContext`, is stored as it is; a change inside
|
|
|
17
17
|
written back. An `Entity` resource with no entity holds `Entity.none`, which is `0` rather than `null`, so it stays data
|
|
18
18
|
every thread can read.
|
|
19
19
|
|
|
20
|
-
A system reads resources by key. With `
|
|
20
|
+
A system reads resources by key. With `readonly: true` it only reads, and runs after the world's changes apply.
|
|
21
21
|
[`run`](01-resources.ts#L31-L32) runs a system once, and resolves to what the body returns, for a system without
|
|
22
22
|
components.
|
|
@@ -22,7 +22,7 @@ declare module "@adobe/ecs" {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const read = System.create({
|
|
25
|
+
const read = System.create({ readonly: true }, ({ resources }) =>
|
|
26
26
|
[resources[gravity], resources[floor], resources[names], resources[audio], resources[focused]]);
|
|
27
27
|
|
|
28
28
|
export const main = async (): Promise<void> => {
|
package/src/guide/02-entities.ts
CHANGED
|
@@ -25,10 +25,10 @@ export const defeat = System.create({ components: { with: [enemy] } }, (row, { c
|
|
|
25
25
|
commands.remove(row.entity, enemy);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
const readFoes = System.create({
|
|
28
|
+
const readFoes = System.create({ readonly: true, components: { with: [position, enemy] } }, row => {
|
|
29
29
|
console.log("a foe at", row[position]);
|
|
30
30
|
});
|
|
31
|
-
const readParticles = System.create({
|
|
31
|
+
const readParticles = System.create({ readonly: true, components: { with: [...Particle] } }, row => {
|
|
32
32
|
console.log("a particle moving at", row[velocity]);
|
|
33
33
|
});
|
|
34
34
|
|
package/src/guide/03-systems.md
CHANGED
|
@@ -8,8 +8,8 @@ it in parallel.
|
|
|
8
8
|
component in `with` and none in `not`, so the `frozen` one stays put. Values are replaced whole, never edited in
|
|
9
9
|
place.
|
|
10
10
|
- **Systems without components**, such as [`count`](03-systems.ts#L35-L37), run once per run.
|
|
11
|
-
- **
|
|
12
|
-
|
|
11
|
+
- **Readonly systems**, `readonly: true`, only read. Run one to read the world, as [`read`](03-systems.ts#L40-L42)
|
|
12
|
+
does, or add one to react to it. [Sync points](06-sync-points.md) says when and where they run.
|
|
13
13
|
- **Optional components**, `optional: [velocity]`, are in the row where the entity has them and `undefined` where it
|
|
14
14
|
doesn't. They never narrow which entities a system runs on; [`readSpeeds`](03-systems.ts#L44-L47) still reads the
|
|
15
15
|
entity without a velocity. Only commands add and remove them.
|
package/src/guide/03-systems.out
CHANGED
package/src/guide/03-systems.ts
CHANGED
|
@@ -37,16 +37,16 @@ const count = System.create({}, ({ resources }) => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
const places: number[] = [];
|
|
40
|
-
const read = System.create({
|
|
40
|
+
const read = System.create({ readonly: true, components: { with: [position] } }, row => {
|
|
41
41
|
places.push(row[position][0]);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
const speeds: string[] = [];
|
|
45
|
-
const readSpeeds = System.create({
|
|
45
|
+
const readSpeeds = System.create({ readonly: true, components: { with: [position], optional: [velocity] } }, row => {
|
|
46
46
|
speeds.push(row[velocity] === undefined ? "still" : `at ${row[velocity][0]}`);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
const frameCount = System.create({
|
|
49
|
+
const frameCount = System.create({ readonly: true }, ({ resources }) => resources[frames]);
|
|
50
50
|
|
|
51
51
|
const fail = System.create(() => {
|
|
52
52
|
throw new Error("broken");
|
package/src/guide/04-ticks.md
CHANGED
|
@@ -9,7 +9,7 @@ every tick from then on, and [`world.remove`](04-ticks.ts#L41) takes it off.
|
|
|
9
9
|
`step` milliseconds, each with that `delta`.
|
|
10
10
|
- A world ticks only after a run arrives, unless `always` is set, as for [the first world](04-ticks.ts#L36).
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
A readonly system with `resources: { changed: [elapsed] }` runs after each tick that changed `elapsed`:
|
|
13
13
|
[`watch`](04-ticks.ts#L18-L21) resolves once 30 ms have passed. Once `advance` is removed, `elapsed` stays put.
|
|
14
14
|
|
|
15
15
|
**Asking for a tick.** A system that needs another tick contributes a time to the `wake` reduction:
|
package/src/guide/04-ticks.ts
CHANGED
|
@@ -16,11 +16,11 @@ const advance = System.create({}, ({ resources }) => {
|
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
let reached: (elapsed: number) => void = () => {};
|
|
19
|
-
const watch = System.create({
|
|
19
|
+
const watch = System.create({ readonly: true, resources: { changed: [elapsed] } }, ({ resources }) => {
|
|
20
20
|
if (resources[elapsed] >= 30) reached(resources[elapsed]);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
const readTime = System.create({
|
|
23
|
+
const readTime = System.create({ readonly: true }, ({ resources }) => ({ elapsed: resources[elapsed], delta: resources[clock].delta }));
|
|
24
24
|
|
|
25
25
|
const step = System.create({}, ({ resources, reduce }) => {
|
|
26
26
|
resources[steps] += 1;
|
|
@@ -28,7 +28,7 @@ const step = System.create({}, ({ resources, reduce }) => {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
let finished: (steps: number) => void = () => {};
|
|
31
|
-
const watchSteps = System.create({
|
|
31
|
+
const watchSteps = System.create({ readonly: true, resources: { changed: [steps] } }, ({ resources }) => {
|
|
32
32
|
if (resources[steps] === 3) finished(resources[steps]);
|
|
33
33
|
});
|
|
34
34
|
|
|
@@ -20,7 +20,7 @@ const heal = System.create({ components: { with: [health] }, after: [hit] }, row
|
|
|
20
20
|
});
|
|
21
21
|
const frame = System.create([heal, hit]);
|
|
22
22
|
|
|
23
|
-
const read = System.create({
|
|
23
|
+
const read = System.create({ readonly: true, components: { with: [health] } }, row => {
|
|
24
24
|
console.log("health", row[health]);
|
|
25
25
|
});
|
|
26
26
|
|
|
@@ -10,5 +10,12 @@ A run ends at a *sync point*, where its commands apply.
|
|
|
10
10
|
A composite is one run with one sync point. In [`frame`](06-sync-points.ts#L25), `count` runs `after: [spawn]` but
|
|
11
11
|
before the spawned entities exist, so it counts none; run alone, after the sync point, it counts the hero.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
[`watch`](06-sync-points.ts#
|
|
13
|
+
**Readonly systems.** `readonly: true` places a system after the sync point, so it reads what the commands did. Added
|
|
14
|
+
to a world, [`watch`](06-sync-points.ts#L27-L29) runs after the sync point of every run and tick, and logs the hero
|
|
15
|
+
after the run that spawned it; `count`, which isn't readonly, runs before the sync point, in its tier.
|
|
16
|
+
|
|
17
|
+
- **Where:** a readonly system runs on the thread that added or ran it, so a [joined thread](19-threads.md) adds only
|
|
18
|
+
readonly systems.
|
|
19
|
+
- **Order:** none. It can't declare `before`, `after` or `during`.
|
|
20
|
+
- **Limits:** it can't write, use `commands` or `reduce`, or take input. Its row and resources are read-only, so each
|
|
21
|
+
is a type error.
|
|
@@ -24,10 +24,10 @@ const count = System.create({ components: { with: [health] }, after: [spawn] },
|
|
|
24
24
|
});
|
|
25
25
|
const frame = System.create([spawn, count]);
|
|
26
26
|
|
|
27
|
-
const watch = System.create({
|
|
27
|
+
const watch = System.create({ readonly: true, components: { with: [health] } }, row => {
|
|
28
28
|
console.log("health", row[health]);
|
|
29
29
|
});
|
|
30
|
-
const readSeen = System.create({
|
|
30
|
+
const readSeen = System.create({ readonly: true }, ({ resources }) => resources[seen]);
|
|
31
31
|
|
|
32
32
|
export const main = async (): Promise<void> => {
|
|
33
33
|
await using world = World.create();
|
|
@@ -36,7 +36,7 @@ const bump = System.create({ components: { with: [score, boss] } }, row => {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
let loaded = 0;
|
|
39
|
-
const countLoaded = System.create({
|
|
39
|
+
const countLoaded = System.create({ readonly: true, components: { with: [shown] } }, () => {
|
|
40
40
|
loaded += 1;
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -45,7 +45,7 @@ const step = System.create({ resources: { is: [map], not: [paused] } }, ({ resou
|
|
|
45
45
|
});
|
|
46
46
|
const pause = System.create({}, ({ resources }) => { resources[paused] = true; });
|
|
47
47
|
const open = System.create({}, ({ resources }) => { resources[map] = { width: 4 }; });
|
|
48
|
-
const readSteps = System.create({
|
|
48
|
+
const readSteps = System.create({ readonly: true }, ({ resources }) => resources[steps]);
|
|
49
49
|
|
|
50
50
|
export const main = async (): Promise<void> => {
|
|
51
51
|
await using world = World.create();
|
package/src/guide/08-input.ts
CHANGED
|
@@ -30,7 +30,7 @@ const heal = System.create({
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
const healths: number[] = [];
|
|
33
|
-
const read = System.create({
|
|
33
|
+
const read = System.create({ readonly: true, components: { with: [health] } }, row => {
|
|
34
34
|
healths.push(row[health]);
|
|
35
35
|
});
|
|
36
36
|
|
package/src/guide/09-rules.ts
CHANGED
|
@@ -31,7 +31,7 @@ const hide = System.create({ components: { with: [sprite] } }, (row, { commands
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
const drawing: string[] = [];
|
|
34
|
-
const read = System.create({
|
|
34
|
+
const read = System.create({ readonly: true, components: { with: [layer, drawn], optional: [mesh, sprite] } }, row => {
|
|
35
35
|
drawing.push(`${row[mesh] === undefined ? `sprite ${row[sprite]}` : `mesh ${row[mesh]}`} on layer ${row[layer]}`);
|
|
36
36
|
});
|
|
37
37
|
/** Logs what's drawn, in order. */
|
package/src/guide/11-traits.ts
CHANGED
|
@@ -33,9 +33,9 @@ const destroy = System.create({ components: { with: [ship] } }, (row, { commands
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
const counts = { outlined: 0, targeting: 0, entities: 0 };
|
|
36
|
-
const countOutlined = System.create({
|
|
37
|
-
const countTargeting = System.create({
|
|
38
|
-
const countAll = System.create({
|
|
36
|
+
const countOutlined = System.create({ readonly: true, components: { with: [outline] } }, () => { counts.outlined += 1; });
|
|
37
|
+
const countTargeting = System.create({ readonly: true, components: { with: [target] } }, () => { counts.targeting += 1; });
|
|
38
|
+
const countAll = System.create({ readonly: true, components: {} }, () => { counts.entities += 1; });
|
|
39
39
|
const count = System.create([countOutlined, countTargeting, countAll]);
|
|
40
40
|
|
|
41
41
|
export const main = async (): Promise<void> => {
|
package/src/guide/12-lookups.ts
CHANGED
|
@@ -25,7 +25,7 @@ const track = System.create({
|
|
|
25
25
|
|
|
26
26
|
const refreshed: boolean[] = [];
|
|
27
27
|
const watch = System.create({
|
|
28
|
-
|
|
28
|
+
readonly: true,
|
|
29
29
|
components: { with: [target] },
|
|
30
30
|
lookup: { enemy: { with: [health], changed: [health] } },
|
|
31
31
|
}, (row, { lookup }) => {
|
|
@@ -34,7 +34,7 @@ const watch = System.create({
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
const aims: number[] = [];
|
|
37
|
-
const readAims = System.create({
|
|
37
|
+
const readAims = System.create({ readonly: true, components: { with: [aim] } }, row => {
|
|
38
38
|
aims.push(row[aim]);
|
|
39
39
|
});
|
|
40
40
|
|
package/src/guide/13-indexes.ts
CHANGED
|
@@ -26,14 +26,14 @@ const count = System.create({ components: { with: [cell, crowded] }, lookup: { a
|
|
|
26
26
|
|
|
27
27
|
const byScore = Index.create({ key: [score], sorted: true, components: { with: [player] } });
|
|
28
28
|
|
|
29
|
-
const leaders = System.create({
|
|
29
|
+
const leaders = System.create({ readonly: true, lookup: { scores: { index: byScore } } }, ({ lookup }) => {
|
|
30
30
|
const winners: string[] = [];
|
|
31
31
|
const high = lookup.scores({ ">=": 90 });
|
|
32
32
|
for (let i = 0; i < high.length; i++) winners.push(high.at(i)[player]);
|
|
33
33
|
return winners;
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
const list = System.create({
|
|
36
|
+
const list = System.create({ readonly: true, components: { with: [player, score] }, order: byScore }, row => {
|
|
37
37
|
console.log(row[player], row[score]);
|
|
38
38
|
});
|
|
39
39
|
|
|
@@ -42,12 +42,12 @@ const find = System.create({ lookup: { byId: { index: byServerId } } }, ({ looku
|
|
|
42
42
|
lookup.byId(input.id)?.[player] ?? null);
|
|
43
43
|
|
|
44
44
|
const players = Index.create({ key: [], components: { with: [player] } });
|
|
45
|
-
const countPlayers = System.create({
|
|
45
|
+
const countPlayers = System.create({ readonly: true, lookup: { players: { index: players, changed: true } } }, ({ lookup }) => {
|
|
46
46
|
console.log("players", lookup.players().length);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
const crowds: number[] = [];
|
|
50
|
-
const readCrowds = System.create({
|
|
50
|
+
const readCrowds = System.create({ readonly: true, components: { with: [crowded] } }, row => {
|
|
51
51
|
crowds.push(row[crowded]);
|
|
52
52
|
});
|
|
53
53
|
|
package/src/guide/14-kernels.ts
CHANGED
|
@@ -29,7 +29,7 @@ const slow = System.create({ kernel: false, components: { with: [velocity] } },
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
const moved: string[] = [];
|
|
32
|
-
const read = System.create({
|
|
32
|
+
const read = System.create({ readonly: true, components: { with: [position, velocity] } }, row => {
|
|
33
33
|
moved.push(`at ${row[position][0]}, moving at ${row[velocity][0]}`);
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -23,7 +23,7 @@ const weigh = System.create({ components: { with: [mass] } }, (row, { reduce })
|
|
|
23
23
|
reduce[count](1);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
const report = System.create({
|
|
26
|
+
const report = System.create({ readonly: true }, ({ resources }) =>
|
|
27
27
|
`${resources[count]} weigh ${resources[total]}, at most ${resources[heaviest]}`);
|
|
28
28
|
|
|
29
29
|
const spawn = System.create(({ commands }) => {
|
package/src/guide/16-gpu.ts
CHANGED
|
@@ -32,7 +32,7 @@ const draw = System.render({ shader: { accessors: { dotPosition: position, dotSi
|
|
|
32
32
|
});
|
|
33
33
|
const shaderCode = Gpu.wgsl(draw);
|
|
34
34
|
|
|
35
|
-
const read = System.create({
|
|
35
|
+
const read = System.create({ readonly: true, components: { with: [position] } }, row => {
|
|
36
36
|
console.log("at", row[position]);
|
|
37
37
|
});
|
|
38
38
|
|
|
@@ -21,7 +21,7 @@ const place = System.create({ components: { with: [offset, placed] }, up: { thro
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
const where: string[] = [];
|
|
24
|
-
const read = System.create({
|
|
24
|
+
const read = System.create({ readonly: true, components: { with: [label, placed] } }, row => {
|
|
25
25
|
where.push(`${row[label]} at ${row[placed]}`);
|
|
26
26
|
});
|
|
27
27
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Error: System "explode" (@adobe/ecs@0.2.
|
|
1
|
+
Error: System "explode" (@adobe/ecs@0.2.3/src/guide/18-identity.ts#explode) failed: boom
|
package/src/guide/19-threads.md
CHANGED
|
@@ -11,6 +11,8 @@ A *logical thread* is where objects live and the code touching them runs: the pa
|
|
|
11
11
|
`World.join(port, { threads: ["audio"] })`, claiming the threads it fills. Usually the other end of the channel goes
|
|
12
12
|
to a worker; [here both ends](19-threads.ts#L44-L54) are on one thread.
|
|
13
13
|
|
|
14
|
-
- `share` resolves once the thread joined, with the
|
|
15
|
-
anything else. Those
|
|
14
|
+
- `share` resolves once the thread joined, with the readonly systems it added as `join` resolved, before awaiting
|
|
15
|
+
anything else. Those readonly systems run there after the world's sync points: `listen` hears what `mix` wrote.
|
|
16
|
+
- A joined thread adds only [readonly systems](03-systems.md), which run on it; a system that writes is added on the
|
|
17
|
+
world's thread.
|
|
16
18
|
- A joined world runs systems as the world does: [`audio.run`](19-threads.ts#L60-L61) spawns and mixes too.
|
package/src/guide/19-threads.ts
CHANGED
|
@@ -22,7 +22,7 @@ const mix = System.create({ components: { with: [volume] } }, (row, { resources
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
let heard = 0;
|
|
25
|
-
const listen = System.create({
|
|
25
|
+
const listen = System.create({ readonly: true }, ({ resources }) => {
|
|
26
26
|
heard = resources[levels].size;
|
|
27
27
|
});
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ const spawn = System.create(({ commands }) => {
|
|
|
31
31
|
commands.spawn([volume])({ [volume]: 0.3 });
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
const countVoices = System.create({
|
|
34
|
+
const countVoices = System.create({ readonly: true }, ({ resources }) => resources[levels].size);
|
|
35
35
|
|
|
36
36
|
/** Waits a turn of the event loop at a time until `done`, for two seconds at most. */
|
|
37
37
|
const until = async (done: () => boolean): Promise<void> => {
|
package/src/guide/20-shared.ts
CHANGED
|
@@ -56,7 +56,7 @@ let drawn = 0;
|
|
|
56
56
|
let left = Infinity;
|
|
57
57
|
let right = -Infinity;
|
|
58
58
|
const draw = System.create({
|
|
59
|
-
|
|
59
|
+
readonly: true,
|
|
60
60
|
components: { with: [position], where: { [cell]: (at, { resources }) => resources[camera]?.sees(at) === true } },
|
|
61
61
|
}, row => {
|
|
62
62
|
drawn += 1;
|
|
@@ -66,7 +66,7 @@ const draw = System.create({
|
|
|
66
66
|
|
|
67
67
|
let refreshed = 0;
|
|
68
68
|
const refresh = System.create({
|
|
69
|
-
|
|
69
|
+
readonly: true,
|
|
70
70
|
components: { with: [position], changed: [position], where: { [cell]: (at, { resources }) => resources[camera]?.sees(at) === true } },
|
|
71
71
|
}, () => {
|
|
72
72
|
refreshed += 1;
|
|
@@ -74,7 +74,7 @@ const refresh = System.create({
|
|
|
74
74
|
|
|
75
75
|
// Only chunk [4, 0], which no unit is in until one crosses into it.
|
|
76
76
|
let east = 0;
|
|
77
|
-
const countEast = System.create({
|
|
77
|
+
const countEast = System.create({ readonly: true, components: { with: [position], where: { [cell]: [4, 0] } } }, () => {
|
|
78
78
|
east += 1;
|
|
79
79
|
});
|
|
80
80
|
|
package/src/guide/21-dom.md
CHANGED
|
@@ -13,9 +13,9 @@ same.
|
|
|
13
13
|
Each tick runs one composite:
|
|
14
14
|
|
|
15
15
|
1. [`move`](21-dom.ts#L38-L44) moves every particle and turns it back from the walls.
|
|
16
|
-
2. [`create`](21-dom.ts#L46-L51) gives each particle without a dot an element, with `commands.add`.
|
|
16
|
+
2. [`create`](21-dom.ts#L46-L51) gives each particle without a dot an element, with `commands.add`. Readonly systems can't
|
|
17
17
|
issue commands, so it ticks, and its condition `is: [stage]` waits for a stage.
|
|
18
|
-
3. [`draw`](21-dom.ts#L52-L60),
|
|
18
|
+
3. [`draw`](21-dom.ts#L52-L60), a readonly system, sets each moved dot's `style.translate`, a transform, which the page
|
|
19
19
|
doesn't lay out again. `changed: [position]` skips the dots that didn't move.
|
|
20
20
|
|
|
21
21
|
[A finalizer](21-dom.ts#L61-L63) removes a particle's element when the particle goes, and every element when the
|
package/src/guide/21-dom.ts
CHANGED
|
@@ -51,7 +51,7 @@ const create = System.create({ components: { with: [position], not: [dot] }, res
|
|
|
51
51
|
});
|
|
52
52
|
// Moves each dot whose particle moved, by a transform, which the page doesn't lay out again.
|
|
53
53
|
const draw = System.create({
|
|
54
|
-
|
|
54
|
+
readonly: true,
|
|
55
55
|
components: { with: [position, dot], optional: [traced], changed: [position] },
|
|
56
56
|
}, (row, { resources }) => {
|
|
57
57
|
const [x, y] = row[position];
|
package/src/guide/22-canvas.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The same particles, drawn in immediate mode: each frame, the canvas is cleared and drawn again whole.
|
|
4
4
|
|
|
5
|
-
[`draw`](22-canvas.ts#L52-L80) is one
|
|
5
|
+
[`draw`](22-canvas.ts#L52-L80) is one readonly system without components, so it runs once per tick, not per particle.
|
|
6
6
|
|
|
7
7
|
- `changed: [clock]` runs it after every tick, and `is: [pen]` waits for a 2D context.
|
|
8
8
|
- It reads every particle through [an index without a key](22-canvas.ts#L50-L51), `key: []`, and the traced one
|
package/src/guide/22-canvas.ts
CHANGED
|
@@ -51,7 +51,7 @@ const particles = Index.create({ key: [], components: { with: [position] } });
|
|
|
51
51
|
const tracedOnes = Index.create({ key: [], components: { with: [position, traced] } });
|
|
52
52
|
// Clears the canvas and draws every particle, after each tick.
|
|
53
53
|
const draw = System.create({
|
|
54
|
-
|
|
54
|
+
readonly: true,
|
|
55
55
|
resources: { is: [pen], changed: [clock] },
|
|
56
56
|
lookup: { particles: { index: particles }, traced: { index: tracedOnes } },
|
|
57
57
|
}, ({ resources, lookup }) => {
|
package/src/guide/CLAUDE.md
CHANGED
|
@@ -45,5 +45,5 @@ it in `NN-name.type-test.ts`, under a `// @ts-expect-error` that the package's b
|
|
|
45
45
|
before they're printed.
|
|
46
46
|
- Keys are `Symbol()` with no description: the build's id names each by its binding.
|
|
47
47
|
- Archetypes are types, so their names are PascalCase: `const Particle = [position, velocity] as const`.
|
|
48
|
-
- Use the public API only, as a consumer imports it, not internal modules. Read a world through
|
|
48
|
+
- Use the public API only, as a consumer imports it, not internal modules. Read a world through readonly systems.
|
|
49
49
|
- Lines change as code does: the test of links fails on any that no longer covers whole code, so move them with it.
|
package/src/guide/guide.test.ts
CHANGED
|
@@ -69,12 +69,12 @@ const listed = [...read("README.md").matchAll(/^(?:- |(\d+)\. )\[([^\]]+)\]\(([^
|
|
|
69
69
|
/** What each chapter teaches, which its prose names as code and its program does; 0 is the unnumbered keys chapter's. */
|
|
70
70
|
const teaches: { readonly [chapter: number]: readonly string[] } = {
|
|
71
71
|
0: ["Symbol()", "declare module \"@adobe/ecs\"", "Components", "Resources", "row[score]", "resources[bonus]", "default"],
|
|
72
|
-
1: ["Resources", "default", "World.create", "
|
|
72
|
+
1: ["Resources", "default", "World.create", "readonly: true", "run", "ReadonlyMap", "Entity"],
|
|
73
73
|
2: ["Components", "type: true", "as const", "commands.spawn", "commands.remove", "with: [...Particle]"],
|
|
74
|
-
3: ["System.create", "with", "not", "
|
|
74
|
+
3: ["System.create", "with", "not", "readonly: true", "optional: [velocity]"],
|
|
75
75
|
4: ["clock", "delta", "cadence", "always", "resources: { changed: [elapsed] }", "wake", "reduce[wake]", "world.add", "world.remove"],
|
|
76
76
|
5: ["after", "during", "System.create([heal, hit])", "during: [update]"],
|
|
77
|
-
6: ["commands.spawn", "commands.add", "commands.despawn", "after: [spawn]"],
|
|
77
|
+
6: ["commands.spawn", "commands.add", "commands.despawn", "after: [spawn]", "readonly: true"],
|
|
78
78
|
7: ["resources: { changed: [level] }", "changed: [score]", "resources: { is: [map], not: [paused] }", "resources[map]"],
|
|
79
79
|
8: ["world.run(hit, { amount: 3 })", "target: \"entity\""],
|
|
80
80
|
9: ["Rule.create", "derived: true"],
|
|
@@ -106,10 +106,12 @@ const mutations: { readonly [chapter: number]: readonly (readonly [string, strin
|
|
|
106
106
|
],
|
|
107
107
|
1: [["World.create({ [gravity]: -1.6 })", "World.create()"], ["default: -9.8 ", ""], ["{ default: -Infinity }", "{}"]],
|
|
108
108
|
2: [["commands.remove(row.entity, enemy);", ""], ["{ [position]: [0, 10, 0] }", "{}"]],
|
|
109
|
-
3: [
|
|
109
|
+
3: [
|
|
110
|
+
["not: [frozen]", "not: []"], ["System.create([count, fail])", "System.create([fail])"],
|
|
111
|
+
],
|
|
110
112
|
4: [["world.remove(advance);", ""]],
|
|
111
113
|
5: [["after: [hit] ", ""], ["during: [update] ", ""]],
|
|
112
|
-
6: [["commands.add(hero, health, 5);", ""], ["commands.despawn(commands.spawn([health])({ [health]: 1 }));", "commands.spawn([health])({ [health]: 1 });"], ["watched.add(watch);", ""]],
|
|
114
|
+
6: [["commands.add(hero, health, 5);", ""], ["commands.despawn(commands.spawn([health])({ [health]: 1 }));", "commands.spawn([health])({ [health]: 1 });"], ["watched.add(watch);", ""], ["const watch = System.create({ readonly: true, ", "const watch = System.create({ "]],
|
|
113
115
|
7: [["resources: { changed: [level] } ", ""], [", changed: [score]", ""], [", not: [paused]", ""], ["is: [map], ", ""]],
|
|
114
116
|
8: [["target: \"entity\",", ""]],
|
|
115
117
|
9: [["with: [mesh], not: [hidden]", "with: [mesh]"], ["Rule.create({ components: { with: [sprite], not: [hidden] }, derive: [drawn] });", ""], ["Rule.create({ components: { with: [drawn] }, derive: [layer] });", ""]],
|
|
@@ -51,7 +51,7 @@ export const spawn = System.create(({ commands }) => {
|
|
|
51
51
|
commands.spawn([position, velocity])({ [position]: [0, 0, 0], [velocity]: [1, 0, 0] });
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
export const observe = System.create({
|
|
54
|
+
export const observe = System.create({ readonly: true, components: { with: [position] } }, row => {
|
|
55
55
|
console.log(row[position]);
|
|
56
56
|
});
|
|
57
57
|
|
|
@@ -109,7 +109,7 @@ describe("a module's systems described", () => {
|
|
|
109
109
|
it("says why systems that run as written have no code: per-run systems, observers, render systems, kernel: false and one declared in a function", () => {
|
|
110
110
|
expect(described("spawn")).toMatchObject({ kind: "per-run", runs: `Its body as written, on the "world" thread` });
|
|
111
111
|
for (const backend of ["js", "wasm", "wgsl"] as const) expect(reasonOf(described("spawn")[backend])).toBe("per-run systems run as written; only row systems compile");
|
|
112
|
-
expect(described("observe")).toMatchObject({ kind: "
|
|
112
|
+
expect(described("observe")).toMatchObject({ kind: "readonly", js: { kind: "none", reason: expect.stringMatching(/^a readonly system runs as written/) } });
|
|
113
113
|
expect(described("draw")).toMatchObject({ kind: "render", runs: expect.stringContaining("while the world has a GPU device"), wgsl: { kind: "none", reason: expect.stringMatching(/^a render system runs as written/) } });
|
|
114
114
|
const unflagged = described("unflagged");
|
|
115
115
|
expect(unflagged).toMatchObject({ kernel: false, js: { kind: "none", reason: "as written, because `kernel: false`" }, wasm: { kind: "none", reason: "as written, because `kernel: false`" }, wgsl: { kind: "eligible" } });
|
|
@@ -30,7 +30,7 @@ export type Backend =
|
|
|
30
30
|
* What runs a system: over every matched row, once per run, as an observer, on one entity its input names (`target`),
|
|
31
31
|
* or as a render system.
|
|
32
32
|
*/
|
|
33
|
-
export type SystemKind = "row" | "per-run" | "
|
|
33
|
+
export type SystemKind = "row" | "per-run" | "readonly" | "target" | "render";
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* A system of a module as the build compiles it: its call's offsets in the code transformed, the binding it's
|
|
@@ -427,7 +427,7 @@ export const describeSystems = async (sites: TransformedSites, format: (code: st
|
|
|
427
427
|
const kernel: boolean | "gpu" | null = literal === true || literal === false ? literal : literal === "gpu" ? "gpu" : null;
|
|
428
428
|
const kind: SystemKind = created === "render" ? "render"
|
|
429
429
|
: declaration === undefined ? "per-run"
|
|
430
|
-
: propertyOf(declaration, "
|
|
430
|
+
: propertyOf(declaration, "readonly") !== null ? "readonly"
|
|
431
431
|
: propertyOf(declaration, "target") !== null ? "target"
|
|
432
432
|
: propertyOf(declaration, "components") !== null ? "row" : "per-run";
|
|
433
433
|
const base = { start: node.start, end: node.end, binding: bindings.get(node) ?? null, kind, kernel };
|
|
@@ -442,7 +442,7 @@ export const describeSystems = async (sites: TransformedSites, format: (code: st
|
|
|
442
442
|
? threadOf(declaration, body, kind === "render" ? positionsOf("render", declaration) : positionsOf("system", declaration)) : "world";
|
|
443
443
|
if (kind === "render") return asWritten("a render system runs as written, recording GPU work with shaders of your own; `Gpu.wgsl` gives them their accessors", "world");
|
|
444
444
|
if (kind === "per-run") return asWritten("per-run systems run as written; only row systems compile", written());
|
|
445
|
-
if (kind === "
|
|
445
|
+
if (kind === "readonly") return asWritten("a readonly system runs as written, reading the settled state after the run's last sync point; only row systems that aren't readonly compile", written());
|
|
446
446
|
if (kind === "target") return asWritten("a system with `target` runs as written, once, on the entity its input names; only systems over every matched row compile", written());
|
|
447
447
|
if (declaration === undefined || body === undefined || body.type === "SpreadElement" || !isFunction(body)) {
|
|
448
448
|
return asWritten(declaration === undefined ? "its declaration isn't written in its call, so the build can't read whether it's a kernel" : "its body isn't a function written in place or bound at module scope, so the build can't read it", "world");
|
|
@@ -34,7 +34,7 @@ const spawn = System.create(({ commands }) => {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
const read: number[][] = [];
|
|
37
|
-
const readAll = System.create({
|
|
37
|
+
const readAll = System.create({ readonly: true, components: { with: [p, c, l, s, e, v0, v1] } }, row => {
|
|
38
38
|
read.push([row[p], row[c], row[l], row[s], row[e], row[v0], row[v1]]);
|
|
39
39
|
});
|
|
40
40
|
|