@adobe/ecs 0.2.0 → 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/create.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/finalizers.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 -8
- package/dist/index.js +4 -9
- package/dist/index.js.map +1 -1
- package/dist/interface/component/component.d.ts +7 -1
- package/dist/interface/component/component.js +0 -1
- package/dist/interface/component/component.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 +3 -1
- package/dist/interface/component/index.js +1 -1
- package/dist/interface/component/index.js.map +1 -1
- package/dist/interface/component/value.d.ts +5 -5
- package/dist/interface/component/values.d.ts +2 -2
- package/dist/interface/finalizer/body.d.ts +5 -5
- 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/condition.d.ts +3 -3
- 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 -1
- package/dist/interface/resource/index.js +1 -1
- package/dist/interface/resource/index.js.map +1 -1
- package/dist/interface/resource/resource.d.ts +4 -1
- package/dist/interface/resource/resource.js +0 -1
- package/dist/interface/resource/resource.js.map +1 -1
- package/dist/interface/resource/value.d.ts +5 -5
- package/dist/interface/resources/cadence.d.ts +1 -1
- package/dist/interface/system/access.d.ts +1 -1
- package/dist/interface/system/body.d.ts +13 -8
- 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 +20 -4
- 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/struct.d.ts +2 -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/system/values-of.d.ts +4 -4
- package/dist/interface/system/values.d.ts +5 -5
- package/dist/interface/trait/types.d.ts +4 -4
- package/dist/interface/world/create.d.ts +3 -7
- package/dist/interface/world/declarations.d.ts +2 -2
- package/dist/interface/world/index.d.ts +1 -1
- package/dist/interface/world/join.d.ts +2 -10
- 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 +3 -3
- 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/11-traits.type-test.ts +1 -1
- 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/create.ts +3 -3
- 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/finalizers.ts +3 -3
- 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/pool-size.cases.ts +3 -1
- package/src/implementation/world/reads.test-helper.ts +6 -6
- 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 -6
- package/src/interface/component/component.ts +7 -1
- package/src/interface/component/declarations.ts +11 -0
- package/src/interface/component/declared.ts +9 -0
- package/src/interface/component/index.ts +3 -1
- package/src/interface/component/value.ts +5 -5
- package/src/interface/component/values.ts +2 -2
- package/src/interface/finalizer/body.ts +5 -5
- package/src/interface/index.type-test.ts +3 -3
- 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/condition.ts +3 -3
- package/src/interface/resource/declarations.ts +11 -0
- package/src/interface/resource/index.ts +2 -1
- package/src/interface/resource/resource.ts +4 -1
- package/src/interface/resource/value.ts +5 -5
- package/src/interface/resources/cadence.ts +1 -1
- package/src/interface/system/access.ts +1 -1
- package/src/interface/system/body.ts +9 -7
- package/src/interface/system/check.ts +22 -8
- package/src/interface/system/commands.ts +18 -4
- 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/struct.ts +2 -2
- 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/system/values-of.ts +4 -4
- package/src/interface/system/values.ts +5 -5
- package/src/interface/thread/thread.type-test.ts +1 -1
- package/src/interface/trait/trait.type-test.ts +4 -4
- package/src/interface/trait/types.ts +4 -4
- package/src/interface/world/create.ts +3 -7
- package/src/interface/world/declarations.ts +2 -2
- package/src/interface/world/index.ts +2 -2
- package/src/interface/world/join.ts +2 -10
- package/src/interface/world/loop.type-test.ts +6 -6
- 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/members.ts +3 -3
- package/src/observe/query.ts +1 -1
- package/src/observe/resource.ts +2 -2
- package/src/observe/select.ts +5 -5
- package/src/observe/values.ts +2 -2
- 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
|
@@ -6,8 +6,8 @@ import type { Resource } from '../resource/resource.js';
|
|
|
6
6
|
import type { Comparison } from '../world/filter.js';
|
|
7
7
|
import type { Listed, Property } from './property.js';
|
|
8
8
|
|
|
9
|
-
export type ComponentValues = { readonly [K in Component.Key]: Component
|
|
10
|
-
export type ResourceValues = { readonly [K in Resource.Key]: Resource
|
|
9
|
+
export type ComponentValues = { readonly [K in Component.Key]: Component<K> };
|
|
10
|
+
export type ResourceValues = { readonly [K in Resource.Key]: Resource<K> };
|
|
11
11
|
|
|
12
12
|
/** `Read` values are read-only, `Write` values writable. */
|
|
13
13
|
export type Values<Read, Write, Keys extends PropertyKey, Types> =
|
|
@@ -21,7 +21,7 @@ export type KeyOf<D> = D extends { readonly key: infer K extends readonly Compon
|
|
|
21
21
|
export type HeldBy<D> = KeyOf<D>[number] | Listed<Property<D, "components">, "with">;
|
|
22
22
|
|
|
23
23
|
/** The key's values, in key order. */
|
|
24
|
-
type KeyValues<K extends readonly Component.Key[]> = { readonly [I in keyof K]: Component
|
|
24
|
+
type KeyValues<K extends readonly Component.Key[]> = { readonly [I in keyof K]: Component<K[I] & Component.Key> };
|
|
25
25
|
|
|
26
26
|
/** A prefix of the key, possibly empty, whose last part may be a comparison. */
|
|
27
27
|
type Prefix<V extends readonly unknown[]> = V extends readonly [infer First, ...infer Rest]
|
|
@@ -29,7 +29,7 @@ type Prefix<V extends readonly unknown[]> = V extends readonly [infer First, ...
|
|
|
29
29
|
: readonly [];
|
|
30
30
|
|
|
31
31
|
/** A row an index lookup gives: its entity and the values of what every entity the index holds has, read-only. */
|
|
32
|
-
type IndexRow<D> = { readonly entity: Entity } & { readonly [K in HeldBy<D> & Component.Key]: Component
|
|
32
|
+
type IndexRow<D> = { readonly entity: Entity } & { readonly [K in HeldBy<D> & Component.Key]: Component<K> };
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* The rows a lookup found, in the index's order: by entity for a hash index, by key and then entity for a sorted one.
|
|
@@ -65,7 +65,7 @@ type IndexLookup<D> =
|
|
|
65
65
|
type EntityRow<L> = {
|
|
66
66
|
readonly [K in (Listed<L, "with"> & Component.Key) | (Exclude<Listed<L, "optional">, Listed<L, "with">> & Component.Key) | (L extends { readonly changed: readonly Component.Key[] } ? "changed" : never)]:
|
|
67
67
|
// Its `optional` components are their values where the entity has them, `undefined` where it doesn't.
|
|
68
|
-
K extends Component.Key ? K extends Listed<L, "with"> ? Component
|
|
68
|
+
K extends Component.Key ? K extends Listed<L, "with"> ? Component<K> : Component<K> | undefined : boolean
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
export type LookupFunction<L> = L extends { readonly index: Index<infer D> }
|
|
@@ -55,7 +55,7 @@ declare module '@adobe/ecs' {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
check<Equal<Component
|
|
58
|
+
check<Equal<Component<typeof path>, readonly number[]>>();
|
|
59
59
|
check<Equal<ThreadOf<{ readonly thread: "main" }, string>, never>>();
|
|
60
60
|
check<Equal<ThreadOf<{ readonly thread: "main" }, "edit" | "view">, never>>();
|
|
61
61
|
check<Equal<ThreadOf<{ readonly thread: "main" }, readonly string[]>, "main">>();
|
|
@@ -16,7 +16,7 @@ const gpuOnly = Symbol("traitTest/gpuOnly");
|
|
|
16
16
|
const nonPersistent = Symbol("traitTest/nonPersistent");
|
|
17
17
|
declare module '@adobe/ecs' {
|
|
18
18
|
export interface Traits<N> {
|
|
19
|
-
readonly [gpuOnly]: N extends Component.Key ? (IsStruct<Component
|
|
19
|
+
readonly [gpuOnly]: N extends Component.Key ? (IsStruct<Component<N>> extends true ? true : never) : never;
|
|
20
20
|
readonly [nonPersistent]: N extends Component.Key ? (Components[N] extends { readonly derived: true } ? never : true) : true;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -53,15 +53,15 @@ declare module '@adobe/ecs' {
|
|
|
53
53
|
|
|
54
54
|
// Types filter components by their traits and value types.
|
|
55
55
|
type Ours = typeof parent | typeof follows | typeof slot | typeof ready | typeof score | typeof link | typeof pair | typeof cache;
|
|
56
|
-
check<Equal<Component
|
|
57
|
-
check<Equal<Component
|
|
56
|
+
check<Equal<Component<typeof parent>, Entity>>();
|
|
57
|
+
check<Equal<Component<typeof link>, { readonly to: Entity; readonly weight: number }>>();
|
|
58
58
|
check<Equal<Extract<Component.With<"reference">, Ours>, typeof parent | typeof follows>>();
|
|
59
59
|
check<Equal<Extract<Component.With<"reference", "owner">, Ours>, typeof parent>>();
|
|
60
60
|
check<Equal<Extract<Derived, Ours>, typeof slot>>();
|
|
61
61
|
check<Equal<Extract<Component.With<typeof gpuOnly>, Ours>, typeof pair>>();
|
|
62
62
|
type Persisted = Exclude<Essential, Component.With<typeof nonPersistent>>;
|
|
63
63
|
check<Equal<Extract<Persisted, Ours>, Exclude<Ours, typeof slot | typeof cache>>>();
|
|
64
|
-
check<Equal<Resource
|
|
64
|
+
check<Equal<Resource<typeof time>, number>>();
|
|
65
65
|
|
|
66
66
|
// Commands add and remove only essential components.
|
|
67
67
|
export const issue = (commands: Commands, entity: Entity): void => {
|
|
@@ -9,7 +9,7 @@ import type { IsComparable, IsString, IsStruct } from '../schema/is-struct.js';
|
|
|
9
9
|
import type { Thread } from '../thread/thread.js';
|
|
10
10
|
|
|
11
11
|
/** The value type of the component or resource `N`. */
|
|
12
|
-
type ValueOf<N> = N extends Component.Key ? Component
|
|
12
|
+
type ValueOf<N> = N extends Component.Key ? Component<N> : N extends Resource.Key ? Resource<N> : never;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Whether each component's and resource's value is a struct, `"string"` for a string, computed once so a check is a
|
|
@@ -39,14 +39,14 @@ export interface Traits<N extends Component.Key | Resource.Key> {
|
|
|
39
39
|
* component. Not on a derived component, whose target only a command could set.
|
|
40
40
|
*/
|
|
41
41
|
readonly reference: N extends Component.Key
|
|
42
|
-
? Components[N] extends { readonly derived: true } | { readonly shared: true } ? never : [Component
|
|
42
|
+
? Components[N] extends { readonly derived: true } | { readonly shared: true } ? never : [Component<N>] extends [Entity] ? "owner" | "weak" : never
|
|
43
43
|
: never;
|
|
44
44
|
/**
|
|
45
45
|
* On a resource, the operator systems reduce into it with: each run that a contributor runs in starts it at the
|
|
46
46
|
* operator's identity and leaves it holding the fold of every contribution. Booleans fold bitwise.
|
|
47
47
|
*/
|
|
48
48
|
readonly reduce: N extends Resource.Key
|
|
49
|
-
? [Resource
|
|
49
|
+
? [Resource<N>] extends [boolean] ? "&" | "|" | "^" : Structs[N] extends true ? Reduction : never
|
|
50
50
|
: never;
|
|
51
51
|
/**
|
|
52
52
|
* The logical thread an object lives on, `"world"` without it. Not on a struct or a string, which live in shared
|
|
@@ -64,6 +64,6 @@ export interface Traits<N extends Component.Key | Resource.Key> {
|
|
|
64
64
|
* reference, and it has no thread: every thread reads it.
|
|
65
65
|
*/
|
|
66
66
|
readonly shared: N extends Component.Key
|
|
67
|
-
? Components[N] extends { readonly derived: true } | { readonly reference: unknown } ? never : IsComparable<Component
|
|
67
|
+
? Components[N] extends { readonly derived: true } | { readonly reference: unknown } ? never : IsComparable<Component<N>> extends true ? true : never
|
|
68
68
|
: never;
|
|
69
69
|
}
|
|
@@ -5,15 +5,11 @@ import type { InvalidDeclarations } from './declarations.js';
|
|
|
5
5
|
import type { World } from './world.js';
|
|
6
6
|
|
|
7
7
|
/** Values resources start at in place of their defaults: any but those only the world writes. */
|
|
8
|
-
export type InitialResources = { readonly [K in Exclude<Resource.Key, WorldWritten>]?: Resource
|
|
8
|
+
export type InitialResources = { readonly [K in Exclude<Resource.Key, WorldWritten>]?: Resource<K> };
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* device, and fills every logical thread no joined thread claims. Keys are the resources' symbols, built-in ones
|
|
14
|
-
* included (`{ [workers]: 0 }`); string keys are never a resource's, so they're ignored. Throws for a symbol that isn't
|
|
15
|
-
* a resource, or is one only the world writes. While a declaration's trait or default doesn't hold, each call fails to
|
|
16
|
-
* compile, once, naming each such key, its members and what's wrong with them.
|
|
11
|
+
* The type of `World.create`.
|
|
12
|
+
* @inline
|
|
17
13
|
*/
|
|
18
14
|
export type Create = (
|
|
19
15
|
// Unknown while every declaration holds; else what a call's `this` can't be, so the call reports it, printed whole.
|
|
@@ -30,5 +30,5 @@ type Reported<K, Members extends PropertyKey> = [Members] extends [never] ? neve
|
|
|
30
30
|
* `World.create` reports these.
|
|
31
31
|
*/
|
|
32
32
|
export type InvalidDeclarations =
|
|
33
|
-
| { readonly [K in Component.Key]: Reported<K, Invalid<K, Components[K], Component
|
|
34
|
-
| { readonly [K in Resource.Key]: Reported<K, Invalid<K, Resources[K], Resource
|
|
33
|
+
| { readonly [K in Component.Key]: Reported<K, Invalid<K, Components[K], Component<K>>> }[Component.Key]
|
|
34
|
+
| { readonly [K in Resource.Key]: Reported<K, Invalid<K, Resources[K], Resource<K>>> }[Resource.Key];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
-
// The `World` namespace's
|
|
3
|
-
export {};
|
|
2
|
+
// The `World` namespace's types.
|
|
3
|
+
export type { Table } from './table/table.js';
|
|
@@ -3,15 +3,7 @@ import type { Thread } from '../thread/thread.js';
|
|
|
3
3
|
import type { World } from './world.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* once the world accepted the claim, to the same world: its `run` runs systems there, and its `add` and `remove` take
|
|
9
|
-
* observer systems, which run here after the world's sync points. Add them before awaiting anything else, so the
|
|
10
|
-
* world's `share` resolves with them in place. Its `dispose` disconnects this thread. Rejects with an `Error` saying
|
|
11
|
-
* why it didn't join: the world refused the claim, was disposed, or its thread ended first, as the platform reports it:
|
|
12
|
-
* the world's `Worker` failing to start or throwing first, a port closed, as in node, or, in a browser, the world's
|
|
13
|
-
* thread ending once it was shared through a port this thread listened on. A browser world's worker that the page ended
|
|
14
|
-
* before this thread heard from it isn't reported, so the join stays pending. Rejects too where memory can't be shared,
|
|
15
|
-
* as without cross-origin isolation.
|
|
6
|
+
* The type of `World.join`.
|
|
7
|
+
* @inline
|
|
16
8
|
*/
|
|
17
9
|
export type Join = (port: Parameters<World["share"]>[0], options?: { readonly threads?: readonly Exclude<Thread, "world">[] }) => Promise<World>;
|
|
@@ -25,14 +25,14 @@ world.remove(move);
|
|
|
25
25
|
void removed;
|
|
26
26
|
|
|
27
27
|
// Built-in resources.
|
|
28
|
-
check<Equal<Resource
|
|
29
|
-
check<Equal<Resource
|
|
28
|
+
check<Equal<Resource<typeof gpuDevice>, GPUDevice | null>>();
|
|
29
|
+
check<Equal<Resource<typeof workers>, number | null>>();
|
|
30
30
|
// null is the platform's choice of pool size.
|
|
31
31
|
export const platformPool: World = create({ [workers]: null });
|
|
32
|
-
check<Equal<Resource
|
|
33
|
-
check<Equal<Resource
|
|
34
|
-
check<Equal<Resource
|
|
35
|
-
check<Equal<Resource
|
|
32
|
+
check<Equal<Resource<typeof always>, boolean>>();
|
|
33
|
+
check<Equal<Resource<typeof wake>, number>>();
|
|
34
|
+
check<Equal<Resource<typeof clock>, { readonly now: number; readonly delta: number; readonly tick: number }>>();
|
|
35
|
+
check<Equal<Resource<typeof cadence>, { readonly kind: "frame" } | { readonly kind: "fixed"; readonly step: number; readonly maxSteps?: number }>>();
|
|
36
36
|
// Built-in resources are symbols, so worlds start them at other values through their imported keys.
|
|
37
37
|
check<Equal<[typeof gpuDevice, typeof workers, typeof cadence, typeof always, typeof wake, typeof clock] extends readonly symbol[] ? true : false, true>>();
|
|
38
38
|
// @ts-expect-error a resource key is its imported symbol, never its description
|
|
@@ -19,7 +19,7 @@ declare module '@adobe/ecs' {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// A run resolves to its per-run body's value: a one-off read is an observer system returning it.
|
|
22
|
-
const count = System.create({
|
|
22
|
+
const count = System.create({ readonly: true }, ({ resources }) => resources[frames]);
|
|
23
23
|
check<Equal<typeof count, System<undefined, number>>>();
|
|
24
24
|
const counted: Promise<number> = world.run(count);
|
|
25
25
|
// @ts-expect-error a run of it resolves to a number
|
|
@@ -32,7 +32,7 @@ const scaled = System.create({}, ({ resources }, input: { readonly by: number })
|
|
|
32
32
|
const scaledValue: Promise<{ scaled: number }> = world.run(scaled, { by: 2 });
|
|
33
33
|
|
|
34
34
|
// A body that may be skipped, as one whose conditions or resource change filters can fail, may give undefined.
|
|
35
|
-
const changedFrames = System.create({
|
|
35
|
+
const changedFrames = System.create({ readonly: true, resources: { changed: [frames] } }, ({ resources }) => resources[frames]);
|
|
36
36
|
check<Equal<typeof changedFrames, System<undefined, number | undefined>>>();
|
|
37
37
|
const whileDevice = System.create({ resources: { is: [gpuDevice] } }, () => "drawn");
|
|
38
38
|
const drawn: Promise<string | undefined> = world.run(whileDevice);
|
|
@@ -68,7 +68,7 @@ void [counted, notText, nextValue, scaledValue, drawn, rows, composite, input, n
|
|
|
68
68
|
// @ts-expect-error a body returns no promise
|
|
69
69
|
System.create(() => Promise.resolve(1));
|
|
70
70
|
// @ts-expect-error a body returns no promise
|
|
71
|
-
System.create({
|
|
71
|
+
System.create({ readonly: true }, () => Promise.resolve(1));
|
|
72
72
|
// @ts-expect-error a body returns no promise, a row system's neither
|
|
73
73
|
System.create({ components: { with: [position] } }, () => Promise.resolve());
|
|
74
74
|
// @ts-expect-error a body returns no promise, a render system's neither
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One component's values in a table, copies taken as a readonly system reads `tables`:
|
|
5
|
+
* - `bytes`: a struct's or number's, `stride` bytes per row in the world's layout for it, which is a function of the
|
|
6
|
+
* component's schema alone;
|
|
7
|
+
* - `values`: a string's or an object's, one per row;
|
|
8
|
+
* - `value`: one value every row holds, for a shared component or one whose schema is a constant.
|
|
9
|
+
*/
|
|
10
|
+
export type Column =
|
|
11
|
+
| { readonly kind: "bytes"; readonly bytes: Uint8Array; readonly stride: number }
|
|
12
|
+
| { readonly kind: "values"; readonly values: readonly unknown[] }
|
|
13
|
+
| { readonly kind: "value"; readonly value: unknown };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* What `commands.spawnTables` spawned: each table's new entities in its row order, `Entity.none` for a row it didn't
|
|
17
|
+
* spawn, and how many rows it didn't, those owned by an entity outside the tables.
|
|
18
|
+
*/
|
|
19
|
+
export type Spawned = { readonly entities: readonly Float64Array[]; readonly dropped: number };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
import type { Component } from '../../component/component.js';
|
|
3
|
+
import type { Column } from './index.js';
|
|
4
|
+
|
|
5
|
+
/** The rows of one archetype: their entities in row order, and a column for each component asked for that it holds. */
|
|
6
|
+
export type Table = {
|
|
7
|
+
readonly count: number;
|
|
8
|
+
readonly entities: Float64Array;
|
|
9
|
+
readonly columns: { readonly [K in Component.Key]?: Column };
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type * as Table from './index.js';
|
|
@@ -3,23 +3,23 @@ import type { Input } from '../system/input.js';
|
|
|
3
3
|
import type { System } from '../system/system.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A world. Systems read and change it;
|
|
6
|
+
* A world. Systems read and change it; readonly systems read it after each run's and tick's sync point. Every member's
|
|
7
7
|
* type is named from the package root, so declarations a consumer emits for its results name no internal module.
|
|
8
8
|
*/
|
|
9
9
|
export type World = {
|
|
10
10
|
/**
|
|
11
11
|
* The one way to change state. Runs are sequential in call order; physically, any that don't conflict overlap. Each
|
|
12
|
-
* runs a system or composite, ending in a sync point, then
|
|
12
|
+
* runs a system or composite, ending in a sync point, then readonly systems, the run's and those added, and
|
|
13
13
|
* resolves to what a per-run body returns: a system without components resolves to its body's value, or
|
|
14
14
|
* `undefined` when its body was skipped, and any other system or composite to nothing. So a one-off read is a run
|
|
15
|
-
* of
|
|
16
|
-
* there before the run resolves, to its value as structured clone. A run of
|
|
15
|
+
* of a readonly system returning what it read; one that runs on a joined thread, as it reads objects there, runs
|
|
16
|
+
* there before the run resolves, to its value as structured clone. A run of readonly systems alone runs them once
|
|
17
17
|
* after an empty sync point, and doesn't wake the loop.
|
|
18
18
|
*
|
|
19
19
|
* When a system or finalizer of the run fails, later tiers are skipped, the sync point still applies what was done,
|
|
20
|
-
* and once its
|
|
20
|
+
* and once its readonly systems ran the run rejects with an `Error` naming each failure, its system's label and id,
|
|
21
21
|
* message and cause. It rejects too when the run can't happen or finish: registration fails, the world is disposed,
|
|
22
|
-
* its GPU device is lost, or its sync point can't apply a command's value. What fails after it,
|
|
22
|
+
* its GPU device is lost, or its sync point can't apply a command's value. What fails after it, a readonly system
|
|
23
23
|
* added or GPU work, is reported out of band, as ticks' failures are (see `add`).
|
|
24
24
|
*/
|
|
25
25
|
run<I, R>(system: System<I, R>, ...input: Input<NoInfer<I>>): Promise<R>;
|
|
@@ -27,9 +27,9 @@ export type World = {
|
|
|
27
27
|
* Adds a system to every tick, from the next one on, ordered with those already added by its constraints, as a
|
|
28
28
|
* composite's members are. A tick runs on a beat of the `cadence` resource where `always` is set, `wake` is due or
|
|
29
29
|
* a run arrived since the last tick, and only while some system is added. Throws for a system with no ordering path
|
|
30
|
-
* to one already added that conflicts with it.
|
|
30
|
+
* to one already added that conflicts with it. A readonly system runs after every run's and tick's sync point
|
|
31
31
|
* instead. It never conflicts and doesn't count as an added system for ticking. On a joined thread `add` takes only
|
|
32
|
-
*
|
|
32
|
+
* readonly systems, whose keys need ids: each runs on that thread after the world's sync points, once for the
|
|
33
33
|
* newest when the thread fell behind, reading structs and the objects of the threads it fills as of one sync point.
|
|
34
34
|
*
|
|
35
35
|
* What added systems throw, and what fails in ticks, is reported out of band on the world's thread, as an `Error`
|
|
@@ -43,14 +43,14 @@ export type World = {
|
|
|
43
43
|
* released. A system added both alone and within an added composite stays until both are removed; constraints
|
|
44
44
|
* naming a removed system are ignored. Removing a value that isn't added does nothing. Throws, removing nothing,
|
|
45
45
|
* when the systems left would conflict with no ordering path between them, as `add` does. On a joined thread it
|
|
46
|
-
* takes only
|
|
46
|
+
* takes only readonly systems.
|
|
47
47
|
*/
|
|
48
48
|
remove(system: System<void>): void;
|
|
49
49
|
/**
|
|
50
50
|
* Lets another thread join this world through `port`, one end of a channel to it, of the browser or node: a
|
|
51
51
|
* `MessagePort`, a `Worker`, or a worker's own scope, `self` or node's `parentPort`. That thread joins through the
|
|
52
52
|
* other end, claiming the logical threads it fills. Resolves once it has joined: the threads it claims are its own,
|
|
53
|
-
* and the
|
|
53
|
+
* and the readonly systems it added as its join resolved, before awaiting anything else, are added, so a run
|
|
54
54
|
* started afterwards runs on it and reaches them; one it adds later reaches the runs after its add arrived.
|
|
55
55
|
* Rejects with an `Error` saying why it didn't join: the world refused its claim, as of a thread claimed twice, the
|
|
56
56
|
* world was disposed, the port is shared already, or that thread ended first, as the platform reports it: a
|
package/src/observe/entity.ts
CHANGED
|
@@ -129,14 +129,14 @@ const createShape = (state: WorldState, byShape: Map<string, EntityShape>, id: s
|
|
|
129
129
|
let finders: Finders | null = null;
|
|
130
130
|
let source: object | null = null;
|
|
131
131
|
const system = optional.length === 0
|
|
132
|
-
? System.create(labelled({
|
|
132
|
+
? System.create(labelled({ readonly: true, lookup: { presence: { with: required, changed: required }, values: { with: required }, members: { index: membersOf(required), changed: required } } }, name, { lookup: { values: required } }), ({ lookup }) => {
|
|
133
133
|
if (source !== lookup || finders === null) {
|
|
134
134
|
source = lookup;
|
|
135
135
|
finders = { presence: lookup.presence, values: lookup.values, optional: noOptional };
|
|
136
136
|
}
|
|
137
137
|
pass(lookup.members().changed, finders);
|
|
138
138
|
})
|
|
139
|
-
: System.create(labelled({
|
|
139
|
+
: System.create(labelled({ readonly: true, lookup: optionalLookups(required, optional) }, name, { lookup: optionalReads(required, optional) }), ({ lookup }) => {
|
|
140
140
|
if (source !== lookup || finders === null) {
|
|
141
141
|
source = lookup;
|
|
142
142
|
finders = { presence: lookup.presence, values: lookup.values, optional: (index, entity) => lookup[index](entity) };
|
package/src/observe/members.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { Observe } from './observe.js';
|
|
|
5
5
|
import { observeCount, observeSelect } from './select.js';
|
|
6
6
|
import type { Query } from './query.js';
|
|
7
7
|
import { observeResource } from './resource.js';
|
|
8
|
-
import type {
|
|
8
|
+
import type { ReadonlyResourceType, ReadonlyType } from './values.js';
|
|
9
9
|
|
|
10
10
|
export { toPromise } from './to-promise.js';
|
|
11
11
|
export type { Query } from './query.js';
|
|
@@ -14,7 +14,7 @@ export type { Query } from './query.js';
|
|
|
14
14
|
* A resource's value, then a new one after each run or tick that changed it: one whose sync point stamped it, even with
|
|
15
15
|
* the same value, a reduced resource when its fold differs.
|
|
16
16
|
*/
|
|
17
|
-
export const resource = <R extends Resource.Key>(world: World, resource: R): Observe<
|
|
17
|
+
export const resource = <R extends Resource.Key>(world: World, resource: R): Observe<ReadonlyResourceType<R>> =>
|
|
18
18
|
observeResource(world, resource);
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -27,7 +27,7 @@ export const entity = <const R extends readonly Component.Key[] = [], const O ex
|
|
|
27
27
|
world: World,
|
|
28
28
|
entity: Entity,
|
|
29
29
|
components: { readonly required?: R; readonly optional?: O },
|
|
30
|
-
): Observe<({ readonly [K in keyof Pick<Components, R[number]>]:
|
|
30
|
+
): Observe<({ readonly [K in keyof Pick<Components, R[number]>]: ReadonlyType<K> } & { readonly [K in keyof Pick<Components, O[number]>]?: ReadonlyType<K> }) | null> =>
|
|
31
31
|
observeEntity(world, entity, components);
|
|
32
32
|
|
|
33
33
|
/**
|
package/src/observe/query.ts
CHANGED
|
@@ -10,7 +10,7 @@ type Comparison<T> = {
|
|
|
10
10
|
readonly ">="?: T;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
type ValuesOf<C extends readonly Component.Key[]> = { readonly [K in C[number]]: Component
|
|
13
|
+
type ValuesOf<C extends readonly Component.Key[]> = { readonly [K in C[number]]: Component<K> };
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Entities that have every component in `required`, every entity but the resource entity without it, and none in
|
package/src/observe/resource.ts
CHANGED
|
@@ -31,14 +31,14 @@ const createShape = (state: WorldState, byKey: Map<Resource.Key, ResourceShape>,
|
|
|
31
31
|
const label = `Observe.resource(${key.description ?? "?"})`;
|
|
32
32
|
// On a joined thread, first values come once a run of this system resolved, never in its pass: none to skip.
|
|
33
33
|
const { joined } = state;
|
|
34
|
-
const notifier = System.create(labelled({
|
|
34
|
+
const notifier = System.create(labelled({ readonly: true, resources: { changed: [key] } }, label, { resources: [key] }), ({ resources }) => {
|
|
35
35
|
passed(state);
|
|
36
36
|
entry.value = resources[key];
|
|
37
37
|
entry.valid = true;
|
|
38
38
|
if (entry.stopped > 0) sweepSubscribers(entry);
|
|
39
39
|
throwFailed(give(entry, 0, true, state.pass, joined ? -1 : state.pass, none));
|
|
40
40
|
});
|
|
41
|
-
const reader = System.create(labelled({
|
|
41
|
+
const reader = System.create(labelled({ readonly: true }, label, { resources: [key] }), ({ resources }) => {
|
|
42
42
|
const through = shape.called.shift() ?? 0;
|
|
43
43
|
entry.value = resources[key];
|
|
44
44
|
entry.valid = true;
|
package/src/observe/select.ts
CHANGED
|
@@ -161,7 +161,7 @@ const rangeOf = (condition: unknown): Range => {
|
|
|
161
161
|
return bounds.length === 0 ? unbounded : { range, bounds, exact };
|
|
162
162
|
};
|
|
163
163
|
|
|
164
|
-
type Value = Component
|
|
164
|
+
type Value = Component<Component.Key>;
|
|
165
165
|
|
|
166
166
|
/** A value or comparison a sorted index on one key part looks up by. */
|
|
167
167
|
type Bound = Value | { readonly "<="?: Value; readonly ">="?: Value; readonly "=="?: Value };
|
|
@@ -403,16 +403,16 @@ const createShape = (state: WorldState, byShape: Map<string, QueryShape>, id: st
|
|
|
403
403
|
};
|
|
404
404
|
|
|
405
405
|
const tracker = ordered !== null
|
|
406
|
-
? System.create(labelled({
|
|
406
|
+
? System.create(labelled({ readonly: true, lookup: { list: { index: ordered, changed: reads } } }, name, { lookup: { list: reads } }), ({ lookup }) => {
|
|
407
407
|
track(lookup.list(), null, fromSorted);
|
|
408
408
|
})
|
|
409
409
|
: byRange !== null
|
|
410
|
-
? System.create(labelled({
|
|
410
|
+
? System.create(labelled({ readonly: true, lookup: { list: { index: byRange, changed: reads } } }, name, { lookup: { list: reads } }), ({ lookup }) => {
|
|
411
411
|
// A range is of a `where` condition on the index's key, which the query's type checked against its
|
|
412
412
|
// values.
|
|
413
413
|
track(null, entry => ownRange(entry, range => lookup.list(range as Parameters<typeof lookup.list>[0]), () => lookup.list()), fromSorted);
|
|
414
414
|
})
|
|
415
|
-
: System.create(labelled({
|
|
415
|
+
: System.create(labelled({ readonly: true, lookup: { list: { index: keylessOf(has, not), changed: reads } } }, name, { lookup: { list: reads } }), ({ lookup }) => {
|
|
416
416
|
track(lookup.list(), null, fromKeyless);
|
|
417
417
|
});
|
|
418
418
|
|
|
@@ -466,7 +466,7 @@ const createShape = (state: WorldState, byShape: Map<string, QueryShape>, id: st
|
|
|
466
466
|
for (const entry of scan.entries) entry.gather.start();
|
|
467
467
|
accept = acceptOf(scan.entries);
|
|
468
468
|
};
|
|
469
|
-
const scanner = System.create(labelled({
|
|
469
|
+
const scanner = System.create(labelled({ readonly: true, components: { with: [...reads, ...required.filter(key => !reads.includes(key))], not } }, name, { components: reads }), row => {
|
|
470
470
|
if (accept === null) start();
|
|
471
471
|
if (direct === 0) {
|
|
472
472
|
accept?.(row);
|
package/src/observe/values.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { Component, Resource } from '../interface/index.js';
|
|
|
4
4
|
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
5
5
|
|
|
6
6
|
/** Component `K`'s value as observers give it: read-only, with branded primitives such as `Entity` as they are. */
|
|
7
|
-
export type
|
|
7
|
+
export type ReadonlyType<K extends Component.Key> = Component<K> extends Primitive ? Component<K> : Readonly<Component<K>>;
|
|
8
8
|
|
|
9
9
|
/** Resource `K`'s value as observers give it. */
|
|
10
|
-
export type
|
|
10
|
+
export type ReadonlyResourceType<K extends Resource.Key> = Resource<K> extends Primitive ? Resource<K> : Readonly<Resource<K>>;
|
|
@@ -116,7 +116,7 @@ const ignore = (): void => {
|
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
/** Runs nothing: a run of it resolves while its world lives. */
|
|
119
|
-
const alive = System.create(labelled({
|
|
119
|
+
const alive = System.create(labelled({ readonly: true }, "Observe/alive"), () => {});
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
122
|
* What a rejected run of the factories' own does: nothing for a disposed world, whose first values never come, and for
|
|
@@ -232,7 +232,7 @@ export const stateOf = (world: World): WorldState => {
|
|
|
232
232
|
deliveries: [],
|
|
233
233
|
idle: [],
|
|
234
234
|
rejected: rejectedOf(world),
|
|
235
|
-
deliver: System.create(labelled({
|
|
235
|
+
deliver: System.create(labelled({ readonly: true }, "Observe/deliver"), () => {
|
|
236
236
|
const delivery = created.deliveries.shift();
|
|
237
237
|
if (delivery === undefined) return;
|
|
238
238
|
created.delivered = delivery.batch;
|
|
@@ -243,7 +243,7 @@ export const stateOf = (world: World): WorldState => {
|
|
|
243
243
|
states.set(world, created);
|
|
244
244
|
if (joined) return created;
|
|
245
245
|
// Added before any shape's system, so in each pass it counts the pass before they run.
|
|
246
|
-
world.add(System.create(labelled({
|
|
246
|
+
world.add(System.create(labelled({ readonly: true }, "Observe/maintain"), () => {
|
|
247
247
|
created.pass++;
|
|
248
248
|
if (created.idle.length > 0) sweepShapes(created);
|
|
249
249
|
}));
|
|
@@ -505,7 +505,7 @@ export const benchmarks: readonly Benchmark[] = [
|
|
|
505
505
|
run: async () => {
|
|
506
506
|
const { world, roots } = await filteredForest();
|
|
507
507
|
const found: { child: Entity | null } = { child: null };
|
|
508
|
-
await world.run(System.create({
|
|
508
|
+
await world.run(System.create({ readonly: true, components: { with: [hierarchyParent] } }, row => {
|
|
509
509
|
if (found.child === null && row[hierarchyParent] === roots[0]) found.child = row.entity;
|
|
510
510
|
}));
|
|
511
511
|
const { child } = found;
|
package/src/perftest/indexes.ts
CHANGED
|
@@ -307,13 +307,13 @@ const seedFlags = System.create({}, ({ commands }) => {
|
|
|
307
307
|
for (let i = 0; i < flagged; i++) commands.spawn([flag])({ [flag]: i % 2 === 0 });
|
|
308
308
|
});
|
|
309
309
|
/** A run of observer systems alone, which passes every one added. */
|
|
310
|
-
const pass = System.create({
|
|
310
|
+
const pass = System.create({ readonly: true }, () => {});
|
|
311
311
|
/** A count of each bucket, written only when the index's entries changed: by `changed: true`, or by its lists' `changed`. */
|
|
312
312
|
const countsWhenChanged = (filtered: boolean): System => filtered
|
|
313
|
-
? System.create({
|
|
313
|
+
? System.create({ readonly: true, lookup: { byFlag: { index: byFlag, changed: true } } }, ({ lookup }) => {
|
|
314
314
|
found += lookup.byFlag(false).length + lookup.byFlag(true).length;
|
|
315
315
|
})
|
|
316
|
-
: System.create({
|
|
316
|
+
: System.create({ readonly: true, lookup: { byFlag: { index: byFlag } } }, ({ lookup }) => {
|
|
317
317
|
const off = lookup.byFlag(false);
|
|
318
318
|
const on = lookup.byFlag(true);
|
|
319
319
|
if (!off.changed && !on.changed) return;
|
package/src/perftest/loop.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const loopTicks = 10_000;
|
|
|
12
12
|
export const observerIdles = 100;
|
|
13
13
|
|
|
14
14
|
/** Observer systems no entity matches, each run after every tick. */
|
|
15
|
-
const idleObserver = Array.from({ length: observerIdles }, (_, i) => System.create({
|
|
15
|
+
const idleObserver = Array.from({ length: observerIdles }, (_, i) => System.create({ readonly: true, components: { with: [unused] } }, () => {}));
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* The fastest of `stretches` stretches of `loopTicks` ticks with nothing changed, after one to warm up: a world with
|
package/src/perftest/observer.ts
CHANGED
|
@@ -14,14 +14,14 @@ import type { World } from "../interface/world/world.js";
|
|
|
14
14
|
* first call only starts the stretch that warms up.
|
|
15
15
|
*/
|
|
16
16
|
export const eachTick = (world: World, notify: () => void): (() => void) => {
|
|
17
|
-
const counter = System.create({
|
|
17
|
+
const counter = System.create({ readonly: true, resources: { changed: [clock] } }, () => { notify(); });
|
|
18
18
|
world.add(counter);
|
|
19
19
|
return () => world.remove(counter);
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
/** The clock as an observer run last read it. */
|
|
23
23
|
const read = { tick: 0, now: 0 };
|
|
24
|
-
const readClock = System.create({
|
|
24
|
+
const readClock = System.create({ readonly: true }, ({ resources }) => {
|
|
25
25
|
read.tick = resources[clock].tick;
|
|
26
26
|
read.now = resources[clock].now;
|
|
27
27
|
});
|
|
@@ -36,7 +36,7 @@ export const ticksAfter = async (world: World, count: number): Promise<void> =>
|
|
|
36
36
|
let { tick, now } = read;
|
|
37
37
|
let seen = 0;
|
|
38
38
|
await new Promise<void>(resolve => {
|
|
39
|
-
const counter = System.create({
|
|
39
|
+
const counter = System.create({ readonly: true }, ({ resources }) => {
|
|
40
40
|
const current = resources[clock];
|
|
41
41
|
if (current.tick === tick && current.now === now) return;
|
|
42
42
|
tick = current.tick;
|
|
@@ -52,7 +52,7 @@ export const ticksAfter = async (world: World, count: number): Promise<void> =>
|
|
|
52
52
|
/** The entities with every component of `has` and none of `not`, in storage order, read by an observer run. */
|
|
53
53
|
export const entitiesWith = async (world: World, has: readonly Component.Key[], not: readonly Component.Key[] = []): Promise<Entity[]> => {
|
|
54
54
|
const found: Entity[] = [];
|
|
55
|
-
await world.run(System.create({
|
|
55
|
+
await world.run(System.create({ readonly: true, components: { with: [...has], not } }, row => { found.push(row.entity); }));
|
|
56
56
|
return found;
|
|
57
57
|
};
|
|
58
58
|
|
package/src/perftest/reads.ts
CHANGED
|
@@ -78,7 +78,7 @@ const lookups = async (system: System): Promise<{ readonly best: number; readonl
|
|
|
78
78
|
/** Every entity a seeded world holds. */
|
|
79
79
|
const entitiesOf = async (world: World): Promise<readonly Entity[]> => {
|
|
80
80
|
const found: Entity[] = [];
|
|
81
|
-
await world.run(System.create({
|
|
81
|
+
await world.run(System.create({ readonly: true, components: { with: [value] } }, row => { found.push(row.entity); }));
|
|
82
82
|
return found;
|
|
83
83
|
};
|
|
84
84
|
|
|
@@ -124,7 +124,7 @@ export const readBenchmarks: readonly Benchmark[] = [
|
|
|
124
124
|
run: async () => {
|
|
125
125
|
const world = await seeded(10_000);
|
|
126
126
|
copied = 0;
|
|
127
|
-
const best = await fastest(() => world.run(System.create({
|
|
127
|
+
const best = await fastest(() => world.run(System.create({ readonly: true }, ({ resources }) => { copied = resources[level]; })), 300);
|
|
128
128
|
check(copied === 3, "read run observer: wrong value");
|
|
129
129
|
await world.dispose();
|
|
130
130
|
return best;
|
package/src/perftest/remote.ts
CHANGED
|
@@ -49,7 +49,7 @@ export const reorder = System.create([flip, reverse]);
|
|
|
49
49
|
export const observerSeen = { last: 0, notified: 0 };
|
|
50
50
|
|
|
51
51
|
/** On a joined thread, after each sync point that changed it: the observed resource. */
|
|
52
|
-
export const observerCounter = System.create({
|
|
52
|
+
export const observerCounter = System.create({ readonly: true, resources: { changed: [counter] } }, ({ resources }) => {
|
|
53
53
|
observerSeen.last = resources[counter];
|
|
54
54
|
observerSeen.notified++;
|
|
55
55
|
});
|
|
@@ -61,7 +61,7 @@ const sortedKeys = { keys: new Float64Array(0), entities: new Float64Array(0), p
|
|
|
61
61
|
* On a joined thread, after each sync point: the entities in `order`, sorted from a scan of the index of every ranked
|
|
62
62
|
* entity into reused arrays.
|
|
63
63
|
*/
|
|
64
|
-
export const observerSelect = System.create({
|
|
64
|
+
export const observerSelect = System.create({ readonly: true, lookup: { all: { index: ranked } } }, ({ lookup }) => {
|
|
65
65
|
const list = lookup.all();
|
|
66
66
|
const count = list.length;
|
|
67
67
|
if (sortedKeys.keys.length < count) {
|
|
@@ -99,12 +99,12 @@ export const retext = System.create([count, recaption]);
|
|
|
99
99
|
export const reamounted = System.create([count, reamount]);
|
|
100
100
|
|
|
101
101
|
/** On a joined thread, after each sync point: every row's caption, out of its record. */
|
|
102
|
-
export const observerCaptions = System.create({
|
|
102
|
+
export const observerCaptions = System.create({ readonly: true, components: { with: [caption] } }, row => {
|
|
103
103
|
observerSeen.last += row[caption].length;
|
|
104
104
|
observerSeen.notified++;
|
|
105
105
|
});
|
|
106
106
|
/** On a joined thread, after each sync point: every row's amount. */
|
|
107
|
-
export const observerAmounts = System.create({
|
|
107
|
+
export const observerAmounts = System.create({ readonly: true, components: { with: [amount] } }, row => {
|
|
108
108
|
observerSeen.last += row[amount];
|
|
109
109
|
observerSeen.notified++;
|
|
110
110
|
});
|
|
@@ -129,7 +129,7 @@ const joinedFile = "./joined.js";
|
|
|
129
129
|
/** The observed resource, read by an observer run. */
|
|
130
130
|
const counterOf = async (world: World): Promise<number> => {
|
|
131
131
|
let value = 0;
|
|
132
|
-
await world.run(System.create({
|
|
132
|
+
await world.run(System.create({ readonly: true }, ({ resources }) => { value = resources[counter]; }));
|
|
133
133
|
return value;
|
|
134
134
|
};
|
|
135
135
|
|
package/src/perftest/strings.ts
CHANGED
|
@@ -80,7 +80,7 @@ const read = (system: System, name: string) => measure(system, async world => {
|
|
|
80
80
|
/** Each row's text, as the last write left it. */
|
|
81
81
|
const written = (name: string) => async (world: World): Promise<void> => {
|
|
82
82
|
let wrong = 0;
|
|
83
|
-
await world.run(System.create({
|
|
83
|
+
await world.run(System.create({ readonly: true, components: { with: [text, boxed] } }, row => {
|
|
84
84
|
if (row[text] !== row[boxed].text && row[text] !== labelOf(row.entity, round) && row[boxed].text !== labelOf(row.entity, round)) wrong++;
|
|
85
85
|
}));
|
|
86
86
|
check(wrong === 0, `${name}: wrong text`);
|