@typeonce/ritmo 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/Clock.d.ts +19 -4
- package/dist/Clock.d.ts.map +1 -1
- package/dist/Clock.js +18 -2
- package/dist/Clock.js.map +1 -1
- package/dist/Command.d.ts +181 -40
- package/dist/Command.d.ts.map +1 -1
- package/dist/Command.js +31 -11
- package/dist/Command.js.map +1 -1
- package/dist/Condition.d.ts +12 -1
- package/dist/Condition.d.ts.map +1 -1
- package/dist/Condition.js.map +1 -1
- package/dist/Debug.d.ts +127 -13
- package/dist/Debug.d.ts.map +1 -1
- package/dist/Descriptor.d.ts +61 -10
- package/dist/Descriptor.d.ts.map +1 -1
- package/dist/Descriptor.js +22 -10
- package/dist/Descriptor.js.map +1 -1
- package/dist/Entity.d.ts +22 -7
- package/dist/Entity.d.ts.map +1 -1
- package/dist/Entity.js.map +1 -1
- package/dist/EntityScope.d.ts +20 -1
- package/dist/EntityScope.d.ts.map +1 -1
- package/dist/EntityScope.js +8 -2
- package/dist/EntityScope.js.map +1 -1
- package/dist/FixedLoop.d.ts +106 -0
- package/dist/FixedLoop.d.ts.map +1 -0
- package/dist/FixedLoop.js +97 -0
- package/dist/FixedLoop.js.map +1 -0
- package/dist/HostClock.d.ts +1 -1
- package/dist/HostClock.d.ts.map +1 -1
- package/dist/HostClock.js +2 -1
- package/dist/HostClock.js.map +1 -1
- package/dist/Machine.d.ts +59 -34
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +10 -3
- package/dist/Machine.js.map +1 -1
- package/dist/Query.d.ts +100 -14
- package/dist/Query.d.ts.map +1 -1
- package/dist/Query.js +34 -5
- package/dist/Query.js.map +1 -1
- package/dist/Relation.d.ts +5 -14
- package/dist/Relation.d.ts.map +1 -1
- package/dist/Relation.js +0 -32
- package/dist/Relation.js.map +1 -1
- package/dist/Runtime.d.ts +61 -15
- package/dist/Runtime.d.ts.map +1 -1
- package/dist/Runtime.js +594 -70
- package/dist/Runtime.js.map +1 -1
- package/dist/Schedule.d.ts +24 -15
- package/dist/Schedule.d.ts.map +1 -1
- package/dist/Schedule.js +2 -1
- package/dist/Schedule.js.map +1 -1
- package/dist/Schema.d.ts +233 -48
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +33 -7
- package/dist/Schema.js.map +1 -1
- package/dist/Snapshot.d.ts +90 -1
- package/dist/Snapshot.d.ts.map +1 -1
- package/dist/Snapshot.js +42 -1
- package/dist/Snapshot.js.map +1 -1
- package/dist/System.d.ts +132 -31
- package/dist/System.d.ts.map +1 -1
- package/dist/System.js +32 -0
- package/dist/System.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +2 -0
- package/dist/internal/cells.d.ts.map +1 -1
- package/dist/internal/cells.js +34 -8
- package/dist/internal/cells.js.map +1 -1
- package/dist/internal/debug.d.ts +10 -0
- package/dist/internal/debug.d.ts.map +1 -1
- package/dist/internal/debug.js +171 -12
- package/dist/internal/debug.js.map +1 -1
- package/dist/internal/equal.d.ts +8 -0
- package/dist/internal/equal.d.ts.map +1 -0
- package/dist/internal/equal.js +38 -0
- package/dist/internal/equal.js.map +1 -0
- package/dist/internal/fragments.d.ts +2 -6
- package/dist/internal/fragments.d.ts.map +1 -1
- package/dist/internal/fragments.js +36 -3
- package/dist/internal/fragments.js.map +1 -1
- package/dist/internal/game.d.ts +2 -0
- package/dist/internal/game.d.ts.map +1 -1
- package/dist/internal/game.js +48 -7
- package/dist/internal/game.js.map +1 -1
- package/dist/internal/order.d.ts.map +1 -1
- package/dist/internal/order.js +4 -2
- package/dist/internal/order.js.map +1 -1
- package/dist/internal/queries.d.ts.map +1 -1
- package/dist/internal/queries.js +57 -26
- package/dist/internal/queries.js.map +1 -1
- package/dist/internal/relations.d.ts +10 -0
- package/dist/internal/relations.d.ts.map +1 -0
- package/dist/internal/relations.js +32 -0
- package/dist/internal/relations.js.map +1 -0
- package/dist/internal/requirements.d.ts +26 -0
- package/dist/internal/requirements.d.ts.map +1 -0
- package/dist/internal/requirements.js +46 -0
- package/dist/internal/requirements.js.map +1 -0
- package/dist/internal/streams.d.ts +2 -0
- package/dist/internal/streams.d.ts.map +1 -1
- package/dist/internal/streams.js +10 -0
- package/dist/internal/streams.js.map +1 -1
- package/dist/internal/world.d.ts +7 -1
- package/dist/internal/world.d.ts.map +1 -1
- package/dist/internal/world.js +53 -3
- package/dist/internal/world.js.map +1 -1
- package/package.json +1 -1
- package/src/Clock.ts +29 -4
- package/src/Command.ts +281 -67
- package/src/Condition.ts +17 -1
- package/src/Debug.ts +124 -15
- package/src/Descriptor.ts +92 -21
- package/src/Entity.ts +23 -7
- package/src/EntityScope.ts +21 -2
- package/src/FixedLoop.ts +218 -0
- package/src/HostClock.ts +2 -1
- package/src/Machine.ts +72 -35
- package/src/Query.ts +118 -17
- package/src/Relation.ts +5 -67
- package/src/Runtime.ts +659 -85
- package/src/Schedule.ts +31 -19
- package/src/Schema.ts +297 -57
- package/src/Snapshot.ts +94 -2
- package/src/System.ts +151 -29
- package/src/index.ts +4 -0
- package/src/internal/cells.ts +42 -10
- package/src/internal/debug.ts +179 -14
- package/src/internal/equal.ts +31 -0
- package/src/internal/fragments.ts +35 -5
- package/src/internal/game.ts +69 -7
- package/src/internal/order.ts +8 -4
- package/src/internal/queries.ts +62 -12
- package/src/internal/relations.ts +52 -0
- package/src/internal/requirements.ts +71 -0
- package/src/internal/streams.ts +11 -0
- package/src/internal/world.ts +60 -4
package/src/Debug.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```ts
|
|
38
|
-
* const runtime = Game.Runtime.make({ services, resources, debug: { hostClock: HostClock.standard } })
|
|
38
|
+
* const runtime = Game.Runtime.make({ services, resources, debug: { hostClock: HostClock.standard } }).value
|
|
39
39
|
* runtime.debug.nameSchedules({ setup: setupSchedule, update: updateSchedule })
|
|
40
40
|
*
|
|
41
41
|
* const stop = runtime.debug.observe((event) => {
|
|
@@ -116,6 +116,10 @@ export type Storage = "constructed" | "transient" | "plain"
|
|
|
116
116
|
export interface ComponentDescription {
|
|
117
117
|
readonly name: string
|
|
118
118
|
readonly storage: Storage
|
|
119
|
+
/** Declared `immutable: true`: queries only read it, `commands.insert` replaces it. */
|
|
120
|
+
readonly immutable: boolean
|
|
121
|
+
/** The components it directly requires (`Schema.requires`). */
|
|
122
|
+
readonly requires: ReadonlyArray<string>
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
export interface ResourceDescription {
|
|
@@ -136,8 +140,22 @@ export interface RelationDescription {
|
|
|
136
140
|
export interface MachineDescription {
|
|
137
141
|
readonly name: string
|
|
138
142
|
readonly states: ReadonlyArray<Machine.StateValue>
|
|
139
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* The committed value, or `undefined` when the runtime does not provide the
|
|
145
|
+
* machine, or when it is a sub-state whose parent is in another state.
|
|
146
|
+
*/
|
|
140
147
|
readonly current: Machine.StateValue | undefined
|
|
148
|
+
/** Whether the runtime was given an initial value for the machine. */
|
|
149
|
+
readonly provided: boolean
|
|
150
|
+
/** For a sub-state, where it exists, as `Parent=state`. */
|
|
151
|
+
readonly within: string | undefined
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** An entity scope and the state changes that despawn it (`Game.EntityScope(name, { despawnOnExit, despawnOnEnter })`). */
|
|
155
|
+
export interface ScopeDescription {
|
|
156
|
+
readonly name: string
|
|
157
|
+
/** Triggers as `onExit(Flow=Playing)` / `onEnter(Flow=Menu)`; empty when only `commands.despawnScope` ends it. */
|
|
158
|
+
readonly despawnOn: ReadonlyArray<string>
|
|
141
159
|
}
|
|
142
160
|
|
|
143
161
|
export interface ServiceDescription {
|
|
@@ -155,6 +173,8 @@ export interface QueryDescription {
|
|
|
155
173
|
readonly without: ReadonlyArray<string>
|
|
156
174
|
readonly added: ReadonlyArray<string>
|
|
157
175
|
readonly changed: ReadonlyArray<string>
|
|
176
|
+
/** One entry per `anyOf` filter: the components of its `added` and `changed` members. */
|
|
177
|
+
readonly anyOf: ReadonlyArray<{ readonly added: ReadonlyArray<string>; readonly changed: ReadonlyArray<string> }>
|
|
158
178
|
/** Relation filters and reads, for example `with ChildOf` or `read Children`. */
|
|
159
179
|
readonly relations: ReadonlyArray<string>
|
|
160
180
|
}
|
|
@@ -165,7 +185,8 @@ export interface SystemDescription {
|
|
|
165
185
|
/** Where the system runs, as `schedule#step`. */
|
|
166
186
|
readonly placements: ReadonlyArray<string>
|
|
167
187
|
readonly queries: ReadonlyArray<QueryDescription>
|
|
168
|
-
|
|
188
|
+
/** Resource reads and writes. `changed` lists the reads declared with `changedResource` (also in `reads`). */
|
|
189
|
+
readonly resources: { readonly reads: ReadonlyArray<string>; readonly changed: ReadonlyArray<string>; readonly writes: ReadonlyArray<string> }
|
|
169
190
|
readonly events: { readonly reads: ReadonlyArray<string>; readonly writes: ReadonlyArray<string> }
|
|
170
191
|
readonly machines: {
|
|
171
192
|
readonly reads: ReadonlyArray<string>
|
|
@@ -185,7 +206,11 @@ export interface SystemDescription {
|
|
|
185
206
|
* names for despawnsScope, relation names for relates.
|
|
186
207
|
*/
|
|
187
208
|
readonly commands: CommandPermissions
|
|
188
|
-
/**
|
|
209
|
+
/**
|
|
210
|
+
* Run conditions, rendered, for example `stateChanged(Game/Flow)`. A system
|
|
211
|
+
* placed under several `Schedule.when` / `pauseWhen` groups is described
|
|
212
|
+
* once, with the conditions of every placement.
|
|
213
|
+
*/
|
|
189
214
|
readonly when: ReadonlyArray<string>
|
|
190
215
|
/** Pause conditions, rendered: while one fails the system waits and holds its stream entries. */
|
|
191
216
|
readonly pauseWhen: ReadonlyArray<string>
|
|
@@ -217,11 +242,19 @@ export interface ScheduleDescription {
|
|
|
217
242
|
* A finding about the described schedules. `warning` is almost certainly a
|
|
218
243
|
* bug; `info` is worth a look but can be intentional.
|
|
219
244
|
*
|
|
220
|
-
* Most lints read declarations only.
|
|
221
|
-
* so far: events sent with `runtime.send(...)` count as written,
|
|
245
|
+
* Most lints read declarations only. Some also use what the runtime has seen
|
|
246
|
+
* so far: events sent with `runtime.send(...)` count as written,
|
|
222
247
|
* `relation-failure-never-read` fires for a relation that applied commands
|
|
223
248
|
* have tried to relate or reorder (which can fail) while no described system
|
|
224
|
-
* reads its failures,
|
|
249
|
+
* reads its failures, and `requirement-violated` lists commands skipped
|
|
250
|
+
* because they would have broken a `Schema.requires` requirement,
|
|
251
|
+
* `next-state-replaced` lists next states one system queued and another
|
|
252
|
+
* replaced with a different value before the marker (the last wins),
|
|
253
|
+
* `stale-target` lists `despawn` / `remove` commands given a `SpawnedId`
|
|
254
|
+
* kept from an earlier run (skipped), `stream-loss-unread` lists stream
|
|
255
|
+
* readers that never check `lagged()` and lost entries (or are paused), so
|
|
256
|
+
* describe after the game has run for a while. `scope-trigger-never-applied`
|
|
257
|
+
* also reads which machines the runtime provides.
|
|
225
258
|
*/
|
|
226
259
|
export interface Lint {
|
|
227
260
|
readonly severity: "warning" | "info"
|
|
@@ -230,10 +263,16 @@ export interface Lint {
|
|
|
230
263
|
| "relation-failure-never-read"
|
|
231
264
|
| "event-never-written"
|
|
232
265
|
| "next-state-never-applied"
|
|
266
|
+
| "resource-changed-never-written"
|
|
233
267
|
| "component-never-read"
|
|
234
268
|
| "read-before-write"
|
|
235
269
|
| "this-tick-order"
|
|
236
270
|
| "command-permission-unused"
|
|
271
|
+
| "requirement-violated"
|
|
272
|
+
| "next-state-replaced"
|
|
273
|
+
| "stale-target"
|
|
274
|
+
| "stream-loss-unread"
|
|
275
|
+
| "scope-trigger-never-applied"
|
|
237
276
|
readonly message: string
|
|
238
277
|
readonly subject: string
|
|
239
278
|
/** What to change to resolve it (or, for `info`, when it is fine as it is). */
|
|
@@ -254,6 +293,8 @@ export interface Description {
|
|
|
254
293
|
readonly events: ReadonlyArray<{ readonly name: string }>
|
|
255
294
|
readonly relations: ReadonlyArray<RelationDescription>
|
|
256
295
|
readonly machines: ReadonlyArray<MachineDescription>
|
|
296
|
+
/** Scopes defined through the game, in definition order. */
|
|
297
|
+
readonly scopes: ReadonlyArray<ScopeDescription>
|
|
257
298
|
readonly services: ReadonlyArray<ServiceDescription>
|
|
258
299
|
readonly schedules: ReadonlyArray<ScheduleDescription>
|
|
259
300
|
readonly systems: ReadonlyArray<SystemDescription>
|
|
@@ -393,6 +434,78 @@ export type Effect =
|
|
|
393
434
|
| { readonly kind: "relate"; readonly entity: number; readonly relation: string; readonly target: number }
|
|
394
435
|
| { readonly kind: "unrelate"; readonly entity: number; readonly relation: string }
|
|
395
436
|
| { readonly kind: "relationFailure"; readonly entity: number; readonly relation: string; readonly error: string }
|
|
437
|
+
/** A command skipped because it would leave `component` without `missing` (`Schema.requires`). */
|
|
438
|
+
| {
|
|
439
|
+
readonly kind: "requirementViolated"
|
|
440
|
+
readonly entity: number
|
|
441
|
+
readonly command: RequirementViolation["command"]
|
|
442
|
+
readonly component: string
|
|
443
|
+
readonly missing: string
|
|
444
|
+
}
|
|
445
|
+
/** A `despawn` / `remove` skipped because it was given a `SpawnedId` from an earlier run. */
|
|
446
|
+
| { readonly kind: "staleTarget"; readonly entity: number; readonly command: StaleTarget["command"] }
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* A next state one system queued and another replaced with a different value
|
|
450
|
+
* before the same `applyStateTransitions(...)`: the `next-state-replaced`
|
|
451
|
+
* lint. Only the last value is committed.
|
|
452
|
+
*/
|
|
453
|
+
export interface NextStateReplacement {
|
|
454
|
+
readonly machine: string
|
|
455
|
+
readonly replaced: { readonly system: string; readonly value: Machine.StateValue }
|
|
456
|
+
readonly by: { readonly system: string; readonly value: Machine.StateValue }
|
|
457
|
+
/** How many times it happened. */
|
|
458
|
+
readonly count: number
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Commands of one system skipped because applying them would have left a
|
|
463
|
+
* component without one it requires (`Schema.requires`): the
|
|
464
|
+
* `requirement-violated` lint.
|
|
465
|
+
*/
|
|
466
|
+
export interface RequirementViolation {
|
|
467
|
+
readonly system: string
|
|
468
|
+
readonly command: "spawn" | "insert" | "remove"
|
|
469
|
+
/** The requirer. */
|
|
470
|
+
readonly component: string
|
|
471
|
+
/** The required component it would have lacked. */
|
|
472
|
+
readonly missing: string
|
|
473
|
+
/** The last entity it happened to. */
|
|
474
|
+
readonly entity: number
|
|
475
|
+
/** How many commands were skipped this way. */
|
|
476
|
+
readonly count: number
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Commands of one system skipped because they were given a `SpawnedId` kept
|
|
481
|
+
* from an earlier run, whose entity may since have been despawned and its id
|
|
482
|
+
* reused: the `stale-target` lint.
|
|
483
|
+
*/
|
|
484
|
+
export interface StaleTarget {
|
|
485
|
+
readonly system: string
|
|
486
|
+
readonly command: "despawn" | "remove"
|
|
487
|
+
/** The last entity id it happened to. */
|
|
488
|
+
readonly entity: number
|
|
489
|
+
/** How many commands were skipped this way. */
|
|
490
|
+
readonly count: number
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* One system's reads of one stream (events, transition events, removed,
|
|
495
|
+
* despawned, relation failures) on a debug runtime: the
|
|
496
|
+
* `stream-loss-unread` lint.
|
|
497
|
+
*/
|
|
498
|
+
export interface StreamLoss {
|
|
499
|
+
readonly system: string
|
|
500
|
+
/** The stream, as `events Hit`, `removed Sprite`, `despawned`, ... */
|
|
501
|
+
readonly stream: string
|
|
502
|
+
/** Runs that read it after entries were dropped before they were read. */
|
|
503
|
+
readonly lostRuns: number
|
|
504
|
+
/** Whether the system called `lagged()` on it. */
|
|
505
|
+
readonly checksLagged: boolean
|
|
506
|
+
/** Whether the system has `pauseWhen` conditions (a long pause can drop entries). */
|
|
507
|
+
readonly paused: boolean
|
|
508
|
+
}
|
|
396
509
|
|
|
397
510
|
export interface AppliedCommand {
|
|
398
511
|
readonly tag: string
|
|
@@ -486,8 +599,10 @@ export interface TransitionEvent {
|
|
|
486
599
|
readonly tick: number
|
|
487
600
|
readonly schedule: string
|
|
488
601
|
readonly machine: string
|
|
489
|
-
|
|
490
|
-
readonly
|
|
602
|
+
/** `undefined` when a sub-state starts existing (its parent entered its state). */
|
|
603
|
+
readonly from: Machine.StateValue | undefined
|
|
604
|
+
/** `undefined` when a sub-state stops existing (its parent left its state). */
|
|
605
|
+
readonly to: Machine.StateValue | undefined
|
|
491
606
|
/**
|
|
492
607
|
* `applied` committed the new value; `unchanged` was a `setIfChanged` to the
|
|
493
608
|
* current value; `failed` stopped in an exit or transition schedule and was
|
|
@@ -572,9 +687,3 @@ export interface Option {
|
|
|
572
687
|
export interface Member<S extends Schema.Any, Root> {
|
|
573
688
|
readonly debug: Handle<S, Root>
|
|
574
689
|
}
|
|
575
|
-
|
|
576
|
-
/** Resolves the extra runtime member for a `debug` option type. */
|
|
577
|
-
export type MemberFor<Enabled, S extends Schema.Any, Root> =
|
|
578
|
-
[Enabled] extends [true] ? Member<S, Root>
|
|
579
|
-
: [Enabled] extends [false | undefined] ? {}
|
|
580
|
-
: Partial<Member<S, Root>>
|
package/src/Descriptor.ts
CHANGED
|
@@ -166,6 +166,44 @@ export interface TransientDescriptor<
|
|
|
166
166
|
readonly transient: true
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Marks a component that is never edited in place: `Game.Query.write(...)`
|
|
171
|
+
* rejects it, and the only way to change its value is to replace it with
|
|
172
|
+
* `commands.insert(...)` (applied at `applyDeferred()`, seen by
|
|
173
|
+
* `Game.Query.changed(...)`).
|
|
174
|
+
*/
|
|
175
|
+
export interface Immutable {
|
|
176
|
+
readonly immutable: true
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Options of the component descriptor constructors (`Component`,
|
|
181
|
+
* `ConstructedComponent`, `TransientComponent`).
|
|
182
|
+
*/
|
|
183
|
+
export interface ComponentOptions {
|
|
184
|
+
/**
|
|
185
|
+
* `true` for data that says what an entity is (a kind, a team, a tile) and
|
|
186
|
+
* is replaced, never edited: queries can only read it, and
|
|
187
|
+
* `commands.insert(...)` replaces it. Leave it out for state that systems
|
|
188
|
+
* update every frame.
|
|
189
|
+
*/
|
|
190
|
+
readonly immutable?: true
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** The descriptor a component constructor returns for `Options`: `D`, plus `Immutable` when `immutable: true`. */
|
|
194
|
+
export type WithComponentOptions<D, Options extends ComponentOptions> =
|
|
195
|
+
Options extends { readonly immutable: true } ? D & Immutable : D
|
|
196
|
+
|
|
197
|
+
const applyComponentOptions = <D extends object>(descriptor: D, options: ComponentOptions | undefined): D =>
|
|
198
|
+
options?.immutable === true ? { ...descriptor, immutable: true } : descriptor
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Whether a component descriptor was declared `immutable: true` (readable by
|
|
202
|
+
* queries, replaced only with `commands.insert(...)`).
|
|
203
|
+
*/
|
|
204
|
+
export const isImmutable = (descriptor: Descriptor.Any): descriptor is Descriptor.Any & Immutable =>
|
|
205
|
+
"immutable" in descriptor && descriptor.immutable === true
|
|
206
|
+
|
|
169
207
|
/**
|
|
170
208
|
* Type-level helpers for working with descriptors.
|
|
171
209
|
*/
|
|
@@ -242,6 +280,22 @@ const makeConstructedDescriptor = <
|
|
|
242
280
|
[descriptorConstruction]: constructor
|
|
243
281
|
}) as ConstructedDescriptor<Kind, Name, Value, Raw, Error>
|
|
244
282
|
|
|
283
|
+
/**
|
|
284
|
+
* The message naming why `Value` cannot be the value of a descriptor of
|
|
285
|
+
* `Kind`, or `never` when it can. `any` turns off every check on the
|
|
286
|
+
* descriptor's reads and writes, and `never` erases the fragment it is listed in.
|
|
287
|
+
*/
|
|
288
|
+
type Article<Kind extends string> = Kind extends "event" ? "an" : "a"
|
|
289
|
+
|
|
290
|
+
type ValueRejection<Kind extends string, Value> = 0 extends 1 & Value
|
|
291
|
+
? `${Article<Kind>} ${Kind} value cannot be \`any\`: give it a type (use \`unknown\` for a value you never read)`
|
|
292
|
+
: [Value] extends [never] ? `${Article<Kind>} ${Kind} value cannot be \`never\`: give it a type (use \`{}\` for a marker)`
|
|
293
|
+
: never
|
|
294
|
+
|
|
295
|
+
/** `Make`, or, when `Value` is rejected, a function whose only parameter is the message. */
|
|
296
|
+
type CheckedValue<Kind extends string, Value, Make> = [ValueRejection<Kind, Value>] extends [never] ? Make
|
|
297
|
+
: (name: ValueRejection<Kind, Value>) => never
|
|
298
|
+
|
|
245
299
|
/**
|
|
246
300
|
* Defines a component descriptor.
|
|
247
301
|
*
|
|
@@ -251,14 +305,22 @@ const makeConstructedDescriptor = <
|
|
|
251
305
|
* Components are the only descriptor kind that can be queried directly with
|
|
252
306
|
* `Game.Query.read(...)`, `write(...)`, or `optional(...)`.
|
|
253
307
|
*
|
|
308
|
+
* The value is a real type: `any` and `never` do not compile (here and in
|
|
309
|
+
* every other descriptor constructor). Use `unknown` for a value no system
|
|
310
|
+
* reads, and `Descriptor.Tag` for a marker.
|
|
311
|
+
*
|
|
254
312
|
* @example
|
|
255
313
|
* ```ts
|
|
256
314
|
* const Position = Descriptor.Component<{ x: number; y: number }>()("Position")
|
|
315
|
+
* // What an entity is, replaced with commands.insert and never written by a query:
|
|
316
|
+
* const EnemyKind = Descriptor.Component<"grunt" | "boss">()("EnemyKind", { immutable: true })
|
|
257
317
|
* ```
|
|
258
318
|
*/
|
|
259
|
-
export const Component = <Value>()
|
|
260
|
-
name: Name
|
|
261
|
-
|
|
319
|
+
export const Component = <Value>(): CheckedValue<"component", Value, <const Name extends string, const Options extends ComponentOptions = {}>(
|
|
320
|
+
name: Name,
|
|
321
|
+
options?: Options
|
|
322
|
+
) => WithComponentOptions<Descriptor<"component", Name, Value>, Options>> =>
|
|
323
|
+
((name: string, options?: ComponentOptions) => applyComponentOptions(makeDescriptor("component", name), options)) as never
|
|
262
324
|
|
|
263
325
|
/**
|
|
264
326
|
* Defines a component descriptor that also knows how to validate raw values.
|
|
@@ -275,10 +337,11 @@ export const Component = <Value>() => <const Name extends string>(
|
|
|
275
337
|
*/
|
|
276
338
|
export const ConstructedComponent = <Value, Raw, Error, Constructor extends {} = {}>(
|
|
277
339
|
constructor: ResultConstructor<Value, Raw, Error> & Constructor
|
|
278
|
-
) => <const Name extends string>(
|
|
279
|
-
name: Name
|
|
280
|
-
|
|
281
|
-
|
|
340
|
+
) => <const Name extends string, const Options extends ComponentOptions = {}>(
|
|
341
|
+
name: Name,
|
|
342
|
+
options?: Options
|
|
343
|
+
): WithComponentOptions<ConstructedFor<"component", Name, Value, Raw, Error, Constructor>, Options> =>
|
|
344
|
+
applyComponentOptions(makeConstructedDescriptor("component", name, constructor), options) as WithComponentOptions<ConstructedFor<"component", Name, Value, Raw, Error, Constructor>, Options>
|
|
282
345
|
|
|
283
346
|
/**
|
|
284
347
|
* Defines a component descriptor that snapshots skip.
|
|
@@ -291,9 +354,11 @@ export const ConstructedComponent = <Value, Raw, Error, Constructor extends {} =
|
|
|
291
354
|
* const SpriteRef = Descriptor.TransientComponent<{ frame: number }>()("SpriteRef")
|
|
292
355
|
* ```
|
|
293
356
|
*/
|
|
294
|
-
export const TransientComponent = <Value>()
|
|
295
|
-
name: Name
|
|
296
|
-
|
|
357
|
+
export const TransientComponent = <Value>(): CheckedValue<"component", Value, <const Name extends string, const Options extends ComponentOptions = {}>(
|
|
358
|
+
name: Name,
|
|
359
|
+
options?: Options
|
|
360
|
+
) => WithComponentOptions<TransientDescriptor<"component", Name, Value>, Options>> =>
|
|
361
|
+
((name: string, options?: ComponentOptions) => applyComponentOptions(makeTransientDescriptor("component", name), options)) as never
|
|
297
362
|
|
|
298
363
|
/**
|
|
299
364
|
* Defines a marker component: no data, only presence (`[Player, {}]`).
|
|
@@ -401,9 +466,9 @@ export const isEntityState = (
|
|
|
401
466
|
* const Score = Descriptor.Resource<number>()("Score")
|
|
402
467
|
* ```
|
|
403
468
|
*/
|
|
404
|
-
export const Resource = <Value>()
|
|
469
|
+
export const Resource = <Value>(): CheckedValue<"resource", Value, <const Name extends string>(
|
|
405
470
|
name: Name
|
|
406
|
-
)
|
|
471
|
+
) => Descriptor<"resource", Name, Value>> => ((name: string) => makeDescriptor("resource", name)) as never
|
|
407
472
|
|
|
408
473
|
/**
|
|
409
474
|
* Defines a resource descriptor that also knows how to validate raw values.
|
|
@@ -424,9 +489,9 @@ export const ConstructedResource = <Value, Raw, Error, Constructor extends {} =
|
|
|
424
489
|
* const DeltaTime = Descriptor.TransientResource<number>()("DeltaTime")
|
|
425
490
|
* ```
|
|
426
491
|
*/
|
|
427
|
-
export const TransientResource = <Value>()
|
|
492
|
+
export const TransientResource = <Value>(): CheckedValue<"resource", Value, <const Name extends string>(
|
|
428
493
|
name: Name
|
|
429
|
-
)
|
|
494
|
+
) => TransientDescriptor<"resource", Name, Value>> => ((name: string) => makeTransientDescriptor("resource", name)) as never
|
|
430
495
|
|
|
431
496
|
/**
|
|
432
497
|
* Defines an event descriptor.
|
|
@@ -446,9 +511,9 @@ export const TransientResource = <Value>() => <const Name extends string>(
|
|
|
446
511
|
* const Hit = Descriptor.Event<{ target: number; amount: number }>()("Hit")
|
|
447
512
|
* ```
|
|
448
513
|
*/
|
|
449
|
-
export const Event = <Value>()
|
|
514
|
+
export const Event = <Value>(): CheckedValue<"event", Value, <const Name extends string>(
|
|
450
515
|
name: Name
|
|
451
|
-
)
|
|
516
|
+
) => Descriptor<"event", Name, Value>> => ((name: string) => makeDescriptor("event", name)) as never
|
|
452
517
|
|
|
453
518
|
/**
|
|
454
519
|
* Defines a service descriptor.
|
|
@@ -467,9 +532,9 @@ export const Event = <Value>() => <const Name extends string>(
|
|
|
467
532
|
* const Logger = Descriptor.Service<{ log: (message: string) => void }>()("Logger")
|
|
468
533
|
* ```
|
|
469
534
|
*/
|
|
470
|
-
export const Service = <Value>()
|
|
535
|
+
export const Service = <Value>(): CheckedValue<"service", Value, <const Name extends string>(
|
|
471
536
|
name: Name
|
|
472
|
-
)
|
|
537
|
+
) => Descriptor<"service", Name, Value>> => ((name: string) => makeDescriptor("service", name)) as never
|
|
473
538
|
|
|
474
539
|
/**
|
|
475
540
|
* Checks whether one descriptor is transient (skipped by snapshots).
|
|
@@ -580,7 +645,10 @@ export function constructorOf<D extends Descriptor.Any>(
|
|
|
580
645
|
* `Game.EntityScope(...)` instead when entities only share a lifetime (a
|
|
581
646
|
* level, a menu) and need no tree, order, or traversal.
|
|
582
647
|
*/
|
|
583
|
-
export const Hierarchy =
|
|
648
|
+
export const Hierarchy = <const Name extends string, const RelatedName extends string>(
|
|
649
|
+
name: Name,
|
|
650
|
+
relatedName: RelatedName
|
|
651
|
+
) => makePair("hierarchy", name, relatedName)
|
|
584
652
|
|
|
585
653
|
/**
|
|
586
654
|
* Defines a general relationship pair with direct edges and reverse lookups.
|
|
@@ -589,7 +657,10 @@ export const Hierarchy = RelationModule.Hierarchy
|
|
|
589
657
|
* reverse lookup, but not hierarchy-only behavior such as tree traversal or
|
|
590
658
|
* child reordering.
|
|
591
659
|
*/
|
|
592
|
-
export const Relation =
|
|
660
|
+
export const Relation = <const Name extends string, const RelatedName extends string>(
|
|
661
|
+
name: Name,
|
|
662
|
+
relatedName: RelatedName
|
|
663
|
+
) => makePair("relation", name, relatedName)
|
|
593
664
|
import * as DecodeModule from "./Decode.ts"
|
|
594
|
-
import
|
|
665
|
+
import { makePair } from "./internal/relations.ts"
|
|
595
666
|
import * as ResultModule from "./Result.ts"
|
package/src/Entity.ts
CHANGED
|
@@ -131,7 +131,7 @@ export namespace Handle {
|
|
|
131
131
|
* Drafts exist before the command queue is flushed. This is the place where the
|
|
132
132
|
* API can safely carry exact structural information.
|
|
133
133
|
*/
|
|
134
|
-
export interface EntityDraft<S extends Schema.Any, out P extends ComponentProof, Root = unknown> {
|
|
134
|
+
export interface EntityDraft<S extends Schema.Any, out P extends ComponentProof, Root = unknown, out Relates = never> {
|
|
135
135
|
/**
|
|
136
136
|
* Runtime tag for debugging and pattern matching.
|
|
137
137
|
*/
|
|
@@ -156,6 +156,11 @@ export interface EntityDraft<S extends Schema.Any, out P extends ComponentProof,
|
|
|
156
156
|
* Staged relationship edges attached to the entity before spawn.
|
|
157
157
|
*/
|
|
158
158
|
readonly relations: ReadonlyArray<StagedRelation<S, Root>>
|
|
159
|
+
/**
|
|
160
|
+
* The relations `Game.Command.relate(...)` staged on this draft: spawning
|
|
161
|
+
* it needs them in the system's `Game.System.relates(...)`. Type-level only.
|
|
162
|
+
*/
|
|
163
|
+
readonly __relates?: (_: never) => Relates
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
/**
|
|
@@ -168,17 +173,25 @@ export type StagedComponent = readonly [Descriptor<"component", string, any>, un
|
|
|
168
173
|
*
|
|
169
174
|
* Query execution is the main source of `EntityRef` values.
|
|
170
175
|
*/
|
|
171
|
-
export interface EntityRef<S extends Schema.Any, out P extends ComponentProof, Root = unknown,
|
|
176
|
+
export interface EntityRef<S extends Schema.Any, out P extends ComponentProof, Root = unknown, in Proves = never, in Lacks = never> {
|
|
172
177
|
/**
|
|
173
178
|
* Runtime tag for debugging and pattern matching.
|
|
174
179
|
*/
|
|
175
180
|
readonly kind: "EntityRef"
|
|
176
181
|
/**
|
|
177
182
|
* The component descriptors the query that produced this ref proves (its
|
|
178
|
-
* required slots and `with`): what `commands.despawn`
|
|
179
|
-
*
|
|
183
|
+
* required slots and `with`): what `commands.despawn`, `insert`, and
|
|
184
|
+
* `remove` check. Type-level only. Contravariant on purpose: a ref proving
|
|
185
|
+
* more is a subtype, so a union of refs (`cond ? a.entity : b.entity`)
|
|
186
|
+
* keeps only what both prove.
|
|
187
|
+
*/
|
|
188
|
+
readonly __proves?: (_: Proves) => void
|
|
189
|
+
/**
|
|
190
|
+
* The component descriptors the producing query proves absent (its
|
|
191
|
+
* `without`): what `commands.remove` checks against `Schema.requires`.
|
|
192
|
+
* Type-level only.
|
|
180
193
|
*/
|
|
181
|
-
readonly
|
|
194
|
+
readonly __lacks?: (_: Lacks) => void
|
|
182
195
|
/**
|
|
183
196
|
* The schema-bound entity identity.
|
|
184
197
|
*/
|
|
@@ -201,14 +214,17 @@ export interface EntityMut<
|
|
|
201
214
|
out P extends ComponentProof,
|
|
202
215
|
out W extends ComponentProof,
|
|
203
216
|
Root = unknown,
|
|
204
|
-
|
|
217
|
+
in Proves = never,
|
|
218
|
+
in Lacks = never
|
|
205
219
|
> {
|
|
206
220
|
/**
|
|
207
221
|
* Runtime tag for debugging and pattern matching.
|
|
208
222
|
*/
|
|
209
223
|
readonly kind: "EntityMut"
|
|
210
224
|
/** The component descriptors the producing query proves; see `EntityRef`. */
|
|
211
|
-
readonly __proves?: (_:
|
|
225
|
+
readonly __proves?: (_: Proves) => void
|
|
226
|
+
/** The component descriptors the producing query proves absent; see `EntityRef`. */
|
|
227
|
+
readonly __lacks?: (_: Lacks) => void
|
|
212
228
|
/**
|
|
213
229
|
* The schema-bound entity identity.
|
|
214
230
|
*/
|
package/src/EntityScope.ts
CHANGED
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
* parent-child tree: ordered children, `lookup.parent` / `descendants`
|
|
14
14
|
* traversal, and children despawned with their parent.
|
|
15
15
|
*
|
|
16
|
+
* A scope whose lifetime is a game state declares it:
|
|
17
|
+
* `Game.EntityScope(name, { despawnOnExit: [Flow, "Playing"] })` despawns
|
|
18
|
+
* every member when `applyStateTransitions(...)` commits that state change,
|
|
19
|
+
* with no system, permission, or marker to place.
|
|
20
|
+
*
|
|
16
21
|
* @module EntityScope
|
|
17
22
|
* @docGroup core
|
|
18
23
|
*/
|
|
@@ -25,9 +30,21 @@ export interface EntityScope<
|
|
|
25
30
|
readonly kind: "entityScope"
|
|
26
31
|
readonly name: Name
|
|
27
32
|
readonly key: symbol
|
|
33
|
+
/** State changes that despawn every member, applied by `applyStateTransitions(...)`. */
|
|
34
|
+
readonly despawnOn: ReadonlyArray<Trigger>
|
|
28
35
|
readonly __schemaRoot?: Root | undefined
|
|
29
36
|
}
|
|
30
37
|
|
|
38
|
+
/**
|
|
39
|
+
* One state change that despawns a scope: leaving (`exit`) or entering
|
|
40
|
+
* (`enter`) `state` of `machine`.
|
|
41
|
+
*/
|
|
42
|
+
export interface Trigger {
|
|
43
|
+
readonly phase: "enter" | "exit"
|
|
44
|
+
readonly machine: { readonly key: symbol; readonly name: string }
|
|
45
|
+
readonly state: string | number
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
/** Type-level helpers for entity scopes. */
|
|
32
49
|
export namespace EntityScope {
|
|
33
50
|
export type Any = EntityScope<string, any>
|
|
@@ -42,11 +59,13 @@ export namespace EntityScope {
|
|
|
42
59
|
* branded to that game's root.
|
|
43
60
|
*/
|
|
44
61
|
export const make = <const Name extends string, Root = unknown>(
|
|
45
|
-
name: Name
|
|
62
|
+
name: Name,
|
|
63
|
+
despawnOn: ReadonlyArray<Trigger> = []
|
|
46
64
|
): EntityScope<Name, Root> => ({
|
|
47
65
|
kind: "entityScope",
|
|
48
66
|
name,
|
|
49
|
-
key: keyOf(name)
|
|
67
|
+
key: keyOf(name),
|
|
68
|
+
despawnOn
|
|
50
69
|
})
|
|
51
70
|
|
|
52
71
|
const keyPrefix = "ritmo/entityScope/"
|