@typeonce/ritmo 0.6.0 → 0.8.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 +2 -0
- package/dist/Brand.d.ts +9 -4
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Brand.js +9 -4
- package/dist/Brand.js.map +1 -1
- package/dist/Clock.d.ts +186 -0
- package/dist/Clock.d.ts.map +1 -0
- package/dist/Clock.js +111 -0
- package/dist/Clock.js.map +1 -0
- package/dist/Command.d.ts +250 -41
- package/dist/Command.d.ts.map +1 -1
- package/dist/Command.js +59 -23
- 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 +163 -15
- package/dist/Debug.d.ts.map +1 -1
- package/dist/Debug.js.map +1 -1
- package/dist/Decode.d.ts +1 -1
- package/dist/Decode.js +1 -1
- package/dist/Descriptor.d.ts +67 -24
- package/dist/Descriptor.d.ts.map +1 -1
- package/dist/Descriptor.js +32 -15
- package/dist/Descriptor.js.map +1 -1
- package/dist/Entity.d.ts +22 -5
- package/dist/Entity.d.ts.map +1 -1
- package/dist/Entity.js +2 -2
- package/dist/Entity.js.map +1 -1
- package/dist/EntityScope.d.ts +26 -1
- package/dist/EntityScope.d.ts.map +1 -1
- package/dist/EntityScope.js +14 -2
- package/dist/EntityScope.js.map +1 -1
- package/dist/HostClock.d.ts +49 -0
- package/dist/HostClock.d.ts.map +1 -0
- package/dist/HostClock.js +46 -0
- package/dist/HostClock.js.map +1 -0
- package/dist/Inspector.d.ts +7 -1
- package/dist/Inspector.d.ts.map +1 -1
- package/dist/Inspector.js.map +1 -1
- package/dist/Machine.d.ts +67 -36
- 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 +74 -26
- package/dist/Query.d.ts.map +1 -1
- package/dist/Query.js +32 -28
- package/dist/Query.js.map +1 -1
- package/dist/Runtime.d.ts +39 -16
- package/dist/Runtime.d.ts.map +1 -1
- package/dist/Runtime.js +556 -87
- package/dist/Runtime.js.map +1 -1
- package/dist/Schedule.d.ts +217 -10
- package/dist/Schedule.d.ts.map +1 -1
- package/dist/Schedule.js +5 -0
- package/dist/Schedule.js.map +1 -1
- package/dist/Schema.d.ts +196 -44
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +29 -11
- package/dist/Schema.js.map +1 -1
- package/dist/Snapshot.d.ts +14 -0
- package/dist/Snapshot.d.ts.map +1 -1
- package/dist/Snapshot.js.map +1 -1
- package/dist/System.d.ts +212 -58
- package/dist/System.d.ts.map +1 -1
- package/dist/System.js +84 -6
- package/dist/System.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +3 -3
- package/dist/internal/cells.d.ts.map +1 -1
- package/dist/internal/cells.js +9 -11
- package/dist/internal/cells.js.map +1 -1
- package/dist/internal/debug.d.ts +13 -0
- package/dist/internal/debug.d.ts.map +1 -1
- package/dist/internal/debug.js +192 -7
- 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.map +1 -1
- package/dist/internal/fragments.js +11 -1
- package/dist/internal/fragments.js.map +1 -1
- package/dist/internal/game.d.ts.map +1 -1
- package/dist/internal/game.js +41 -6
- package/dist/internal/game.js.map +1 -1
- package/dist/internal/order.d.ts +10 -0
- package/dist/internal/order.d.ts.map +1 -0
- package/dist/internal/order.js +122 -0
- package/dist/internal/order.js.map +1 -0
- package/dist/internal/queries.d.ts.map +1 -1
- package/dist/internal/queries.js +32 -10
- package/dist/internal/queries.js.map +1 -1
- 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/package.json +1 -1
- package/src/Brand.ts +9 -4
- package/src/Clock.ts +311 -0
- package/src/Command.ts +407 -89
- package/src/Condition.ts +17 -1
- package/src/Debug.ts +172 -14
- package/src/Decode.ts +1 -1
- package/src/Descriptor.ts +92 -37
- package/src/Entity.ts +24 -5
- package/src/EntityScope.ts +27 -2
- package/src/HostClock.ts +64 -0
- package/src/Inspector.ts +7 -2
- package/src/Machine.ts +81 -37
- package/src/Query.ts +110 -38
- package/src/Runtime.ts +607 -111
- package/src/Schedule.ts +380 -13
- package/src/Schema.ts +249 -63
- package/src/Snapshot.ts +4 -0
- package/src/System.ts +248 -93
- package/src/index.ts +8 -7
- package/src/internal/cells.ts +10 -12
- package/src/internal/debug.ts +208 -12
- package/src/internal/equal.ts +31 -0
- package/src/internal/fragments.ts +12 -2
- package/src/internal/game.ts +61 -7
- package/src/internal/order.ts +144 -0
- package/src/internal/queries.ts +37 -13
- package/src/internal/requirements.ts +71 -0
- package/dist/Definition.d.ts +0 -99
- package/dist/Definition.d.ts.map +0 -1
- package/dist/Definition.js +0 -68
- package/dist/Definition.js.map +0 -1
- package/dist/Feature.d.ts +0 -106
- package/dist/Feature.d.ts.map +0 -1
- package/dist/Feature.js +0 -87
- package/dist/Feature.js.map +0 -1
- package/dist/Fx.d.ts +0 -128
- package/dist/Fx.d.ts.map +0 -1
- package/dist/Fx.js +0 -112
- package/dist/Fx.js.map +0 -1
- package/dist/internal/definition.d.ts +0 -17
- package/dist/internal/definition.d.ts.map +0 -1
- package/dist/internal/definition.js +0 -25
- package/dist/internal/definition.js.map +0 -1
- package/src/Definition.ts +0 -106
- package/src/Feature.ts +0 -241
- package/src/Fx.ts +0 -176
- package/src/internal/definition.ts +0 -56
package/src/Schedule.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import type { Condition, ConditionNeedsFromConditions, StateMachine } from "./Machine.ts"
|
|
28
28
|
import * as Requirement from "./Requirement.ts"
|
|
29
29
|
import type { Schema } from "./Schema.ts"
|
|
30
|
-
import type { FailureOf as SystemFailureOf, SystemDefinition, SystemFailure } from "./System.ts"
|
|
30
|
+
import type { FailureOf as SystemFailureOf, SystemDefinition, SystemFailure, ThisTick } from "./System.ts"
|
|
31
31
|
|
|
32
32
|
export interface ApplyDeferredStep {
|
|
33
33
|
readonly kind: "applyDeferred"
|
|
@@ -46,22 +46,375 @@ export type ScheduleMarkerStep =
|
|
|
46
46
|
| ApplyDeferredStep
|
|
47
47
|
| ApplyStateTransitionsStep<any, any>
|
|
48
48
|
|
|
49
|
-
type AnySystem = SystemDefinition<any, any, any, any, any
|
|
49
|
+
type AnySystem = SystemDefinition<any, any, any, any, any>
|
|
50
50
|
|
|
51
51
|
export type ScheduleStep = AnySystem | ScheduleMarkerStep
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* What a schedule carries for `thisTick` checks: its entries in order, and
|
|
55
|
+
* whether any of them has a `Game.System.thisTick` read. Building a schedule
|
|
56
|
+
* computes only the flag; `runtime.tick(...)` walks the entries, and only
|
|
57
|
+
* when some read exists. Schedules never report order errors themselves.
|
|
58
|
+
*
|
|
59
|
+
* `ScheduleOrder`'s defaults claim no `thisTick` reads: `Schema.BoundSchedule`
|
|
60
|
+
* (an annotation) uses it, so a schedule with a read cannot be widened into
|
|
61
|
+
* one and lose its check. Schedule types default to `AnyScheduleOrder`, and a
|
|
62
|
+
* schedule whose order was erased that way fails at `tick` as "not
|
|
63
|
+
* statically known", never silently.
|
|
64
|
+
*/
|
|
65
|
+
export interface ScheduleOrder<
|
|
66
|
+
out Entries extends ReadonlyArray<unknown> = ReadonlyArray<unknown>,
|
|
67
|
+
out Reads extends boolean = false
|
|
68
|
+
> {
|
|
69
|
+
readonly entries: Entries
|
|
70
|
+
readonly reads: Reads
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Accepts a schedule with any order summary (constraint positions only). */
|
|
74
|
+
export type AnyScheduleOrder = ScheduleOrder<ReadonlyArray<unknown>, boolean>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* One `thisTick` read whose writers do not all run earlier in the tick, as
|
|
78
|
+
* found at runtime by `tryTick` and the `this-tick-order` lint. `message` is
|
|
79
|
+
* the same text `runtime.tick(...)` reports at compile time.
|
|
80
|
+
*/
|
|
81
|
+
export interface OrderIssue {
|
|
82
|
+
/**
|
|
83
|
+
* `lateWriter`: the writer runs after the reader. `unflushedWriter`: a
|
|
84
|
+
* command writer runs before a query reader with no `applyDeferred()` in
|
|
85
|
+
* between.
|
|
86
|
+
*/
|
|
87
|
+
readonly kind: "lateWriter" | "unflushedWriter"
|
|
88
|
+
readonly reader: string
|
|
89
|
+
/** The event or resource name, or `queries.<slot>` for a query read. */
|
|
90
|
+
readonly data: string
|
|
91
|
+
/** The writer at fault. */
|
|
92
|
+
readonly writer: string
|
|
93
|
+
readonly message: string
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The message reported when the order of a tick cannot be known statically. */
|
|
97
|
+
export type UnknownOrderError =
|
|
98
|
+
"the order of this tick is not statically known (a schedule or tick argument spread from a widened array) and it contains Game.System.thisTick reads: list the systems and schedules as literal arguments or an `as const` tuple"
|
|
99
|
+
|
|
100
|
+
/** Plugin systems with an erased spec (`any`) take part in no order check. */
|
|
101
|
+
type IsErased<T> = 0 extends 1 & T ? true : false
|
|
102
|
+
|
|
103
|
+
type SpecAccessValues<Spec> =
|
|
104
|
+
Spec extends { readonly events: infer Events; readonly resources: infer Resources }
|
|
105
|
+
? Events[keyof Events] | Resources[keyof Resources]
|
|
106
|
+
: never
|
|
107
|
+
|
|
108
|
+
type SpecQueryValues<Spec> =
|
|
109
|
+
Spec extends { readonly queries: infer Queries } ? Queries[keyof Queries] : never
|
|
110
|
+
|
|
111
|
+
type CarriedOrder<Entry> =
|
|
112
|
+
"__order" extends keyof Entry
|
|
113
|
+
? Entry extends { readonly __order?: (_: never) => infer Order extends AnyScheduleOrder } ? Order : never
|
|
114
|
+
: never
|
|
115
|
+
|
|
116
|
+
/** The order an entry carries: a schedule's, or the bundle of an `applyStateTransitions(...)` step. */
|
|
117
|
+
type OrderOf<Entry> =
|
|
118
|
+
Entry extends { readonly kind: "applyStateTransitions"; readonly bundle?: infer Bundle }
|
|
119
|
+
? CarriedOrder<NonNullable<Bundle>>
|
|
120
|
+
: CarriedOrder<Entry>
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Whether one entry has a `thisTick` read: a cheap flag, computed once per
|
|
124
|
+
* schedule. `"unknown"` is a nested schedule whose type was widened.
|
|
125
|
+
*/
|
|
126
|
+
type EntryHasReads<Entry> =
|
|
127
|
+
Entry extends AnySystem
|
|
128
|
+
? IsErased<Entry["spec"]> extends true ? "no"
|
|
129
|
+
: [Extract<SpecAccessValues<Entry["spec"]> | SpecQueryValues<Entry["spec"]>, ThisTick>] extends [never] ? "no" : "yes"
|
|
130
|
+
: OrderOf<Entry>["reads"] extends infer Reads
|
|
131
|
+
? boolean extends Reads ? "unknown" : Reads extends true ? "yes" : "no"
|
|
132
|
+
: "no"
|
|
133
|
+
|
|
134
|
+
/** `true` when some entry reads `thisTick`, `boolean` when that cannot be known. */
|
|
135
|
+
type HasReads<Entries extends ReadonlyArray<unknown>> =
|
|
136
|
+
EntryHasReads<Entries[number]> extends infer Flags
|
|
137
|
+
? "unknown" extends Flags ? boolean : "yes" extends Flags ? true : false
|
|
138
|
+
: false
|
|
139
|
+
|
|
140
|
+
/** The order summary of entries run in this order. */
|
|
141
|
+
export type CompositionOrder<Entries extends ReadonlyArray<unknown>> = ScheduleOrder<Entries, HasReads<Entries>>
|
|
142
|
+
|
|
143
|
+
// The detailed check below runs only from `TickOrderErrors`, for ticks with a
|
|
144
|
+
// read. It never walks a tuple element by element: per read it finds the
|
|
145
|
+
// reader's position at each nesting level, slices the entries after it once,
|
|
146
|
+
// and looks for writers in the union of the systems there. A query read also
|
|
147
|
+
// walks back from the reader to the nearest marker, which is usually close.
|
|
148
|
+
|
|
149
|
+
/** An event or resource read. */
|
|
150
|
+
interface DataRead<Kind, Key, Reader> {
|
|
151
|
+
readonly kind: Kind
|
|
152
|
+
readonly key: Key
|
|
153
|
+
readonly reader: Reader
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* A query read: the components its matches must have (`Required`), and every
|
|
158
|
+
* component it names (`Named`: selection slots, optional ones included,
|
|
159
|
+
* `with`, `without`, and filters).
|
|
160
|
+
*/
|
|
161
|
+
interface QueryRead<Slot, Required, Named, Reader> {
|
|
162
|
+
readonly kind: "query"
|
|
163
|
+
readonly slot: Slot
|
|
164
|
+
readonly required: Required
|
|
165
|
+
readonly named: Named
|
|
166
|
+
readonly reader: Reader
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
type DescriptorKind<D> = D extends { readonly kind: infer Kind } ? Kind : never
|
|
170
|
+
|
|
171
|
+
type Names<D> = D extends { readonly name: infer Name extends string } ? Name : never
|
|
172
|
+
|
|
173
|
+
type DescriptorsOf<Access> = Access extends { readonly descriptor: infer D } ? D : never
|
|
174
|
+
|
|
175
|
+
/** Every system an entry runs, nested schedules and transition bundles included, as a union. */
|
|
176
|
+
type SystemsIn<Entry> =
|
|
177
|
+
Entry extends AnySystem ? Entry
|
|
178
|
+
: OrderOf<Entry> extends infer Order extends AnyScheduleOrder
|
|
179
|
+
? [Order] extends [never] ? never : SystemsIn<Order["entries"][number]>
|
|
180
|
+
: never
|
|
181
|
+
|
|
182
|
+
type QueryReadOf<Slot, Q, Reader> =
|
|
183
|
+
Q extends { readonly selection: infer Selection; readonly with: infer With extends ReadonlyArray<unknown>; readonly without: infer Without extends ReadonlyArray<unknown>; readonly filters: infer Filters extends ReadonlyArray<unknown> }
|
|
184
|
+
? QueryRead<
|
|
185
|
+
Slot,
|
|
186
|
+
Names<{ readonly [K in keyof Selection]: Selection[K] extends { readonly mode: "read" | "write"; readonly descriptor: infer D } ? D : never }[keyof Selection] | With[number]>,
|
|
187
|
+
Names<DescriptorsOf<Selection[keyof Selection]> | With[number] | Without[number] | DescriptorsOf<Filters[number]>>,
|
|
188
|
+
Reader
|
|
189
|
+
>
|
|
190
|
+
: never
|
|
191
|
+
|
|
192
|
+
type SystemReads<Sys> =
|
|
193
|
+
Sys extends AnySystem
|
|
194
|
+
? IsErased<Sys["spec"]> extends true ? never
|
|
195
|
+
:
|
|
196
|
+
| (Extract<SpecAccessValues<Sys["spec"]>, ThisTick> extends infer Access
|
|
197
|
+
? Access extends { readonly descriptor: infer D extends { readonly name: string } }
|
|
198
|
+
? DataRead<DescriptorKind<D>, D["name"], Sys["name"]>
|
|
199
|
+
: never
|
|
200
|
+
: never)
|
|
201
|
+
| (Sys["spec"] extends { readonly queries: infer Queries }
|
|
202
|
+
? { readonly [K in keyof Queries]: Queries[K] extends ThisTick ? QueryReadOf<K, Queries[K], Sys["name"]> : never }[keyof Queries]
|
|
203
|
+
: never)
|
|
204
|
+
: never
|
|
205
|
+
|
|
206
|
+
/** Names of the systems among `Systems` that write `Key` (other than `Reader`). */
|
|
207
|
+
type WritersOf<Systems, Kind, Key, Reader> =
|
|
208
|
+
Systems extends AnySystem
|
|
209
|
+
? IsErased<Systems["spec"]> extends true ? never
|
|
210
|
+
: Systems["name"] extends Reader ? never
|
|
211
|
+
: [Extract<SpecAccessValues<Systems["spec"]>, { readonly mode: "write"; readonly descriptor: { readonly kind: Kind; readonly name: Key } }>] extends [never]
|
|
212
|
+
? never
|
|
213
|
+
: Systems["name"]
|
|
214
|
+
: never
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Whether declared command permissions can change what a query read sees:
|
|
218
|
+
* `spawns` covering every component it requires, or `inserts` / `removes` of
|
|
219
|
+
* any component it names. `despawns` never count.
|
|
220
|
+
*/
|
|
221
|
+
type CommandsCount<Commands, Required, Named> =
|
|
222
|
+
CommandNames<Commands> extends readonly [infer Spawns, infer Changes]
|
|
223
|
+
? [Extract<Changes, Named>] extends [never]
|
|
224
|
+
? [Spawns] extends [never] ? false
|
|
225
|
+
: [Exclude<Required, Spawns>] extends [never] ? true : false
|
|
226
|
+
: true
|
|
227
|
+
: false
|
|
228
|
+
|
|
229
|
+
/** The component names a system may spawn, and may insert or remove: computed once per system, shared by every read. */
|
|
230
|
+
type CommandNames<Commands> =
|
|
231
|
+
Commands extends { readonly __permissions?: (_: never) => readonly [infer Spawns, infer Inserts, infer Removes, ...Array<unknown>] }
|
|
232
|
+
? readonly [Names<Spawns>, Names<Inserts | Removes>]
|
|
233
|
+
: readonly [never, never]
|
|
234
|
+
|
|
235
|
+
/** Names of the systems among `Systems` whose commands count for a query read (other than its reader). */
|
|
236
|
+
type CommandWritersOf<Systems, Required, Named, Reader> =
|
|
237
|
+
Systems extends AnySystem
|
|
238
|
+
? IsErased<Systems["spec"]> extends true ? never
|
|
239
|
+
: Systems["name"] extends Reader ? never
|
|
240
|
+
: Systems["spec"] extends { readonly commands: infer Commands }
|
|
241
|
+
? CommandsCount<Commands, Required, Named> extends true ? Systems["name"] : never
|
|
242
|
+
: never
|
|
243
|
+
: never
|
|
244
|
+
|
|
245
|
+
type WritersFor<Systems, R> =
|
|
246
|
+
R extends QueryRead<any, infer Required, infer Named, infer Reader> ? CommandWritersOf<Systems, Required, Named, Reader>
|
|
247
|
+
: R extends DataRead<infer Kind, infer Key, infer Reader> ? WritersOf<Systems, Kind, Key, Reader>
|
|
248
|
+
: never
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Some level of the entries is a widened array that may contain a read.
|
|
252
|
+
* `MayRead` is the flag carried by the schedule holding them: a widened array
|
|
253
|
+
* no longer shows its elements, so the flag decides (`boolean`: unknown).
|
|
254
|
+
*/
|
|
255
|
+
type UnknownIn<Entries extends ReadonlyArray<unknown>, MayRead extends boolean> =
|
|
256
|
+
Entries extends unknown ? UnknownInOne<Entries, MayRead> : never
|
|
257
|
+
|
|
258
|
+
type UnknownInOne<Entries extends ReadonlyArray<unknown>, MayRead extends boolean> =
|
|
259
|
+
number extends Entries["length"]
|
|
260
|
+
? [MayRead] extends [false] ? never : UnknownOrderError
|
|
261
|
+
: NestedOrders<Entries[number]> extends infer Order
|
|
262
|
+
? Order extends AnyScheduleOrder ? UnknownIn<Order["entries"], Order["reads"]> : never
|
|
263
|
+
: never
|
|
264
|
+
|
|
265
|
+
type NestedOrders<Entry> = Entry extends AnySystem ? never : OrderOf<Entry>
|
|
266
|
+
|
|
267
|
+
type NestedEntries<Entry> =
|
|
268
|
+
Entry extends AnySystem ? never
|
|
269
|
+
: OrderOf<Entry> extends infer Order extends AnyScheduleOrder
|
|
270
|
+
? [Order] extends [never] ? never : Order["entries"]
|
|
271
|
+
: never
|
|
272
|
+
|
|
273
|
+
type TupleOf<Length extends number, Out extends Array<unknown> = []> =
|
|
274
|
+
Out["length"] extends Length ? Out : TupleOf<Length, [...Out, unknown]>
|
|
275
|
+
|
|
276
|
+
/** Literal names of the systems an entry runs; erased (`string`) names identify no one. */
|
|
277
|
+
type NamesIn<Entry> =
|
|
278
|
+
SystemsIn<Entry> extends infer Sys
|
|
279
|
+
? Sys extends AnySystem ? (string extends Sys["name"] ? never : Sys["name"]) : never
|
|
280
|
+
: never
|
|
281
|
+
|
|
282
|
+
/** Indices (as numbers) of the entries that run the reader. */
|
|
283
|
+
type ReaderIndices<Entries extends ReadonlyArray<unknown>, Reader> = {
|
|
284
|
+
readonly [I in keyof Entries]: Reader extends NamesIn<Entries[I]> ? I : never
|
|
285
|
+
}[number] extends infer Index
|
|
286
|
+
? Index extends `${infer N extends number}` ? N : never
|
|
287
|
+
: never
|
|
288
|
+
|
|
289
|
+
type LateWriterError<R, Writer> =
|
|
290
|
+
Writer extends string
|
|
291
|
+
? R extends QueryRead<infer Slot extends string, any, any, infer Reader extends string>
|
|
292
|
+
? `${Writer} queues commands that ${Reader} needs this tick (queries.${Slot}), but it runs after ${Reader}: move ${Writer} before the applyDeferred() that precedes ${Reader}, or read queries.${Slot} without thisTick`
|
|
293
|
+
: R extends DataRead<any, infer Key extends string, infer Reader extends string>
|
|
294
|
+
? `${Reader} needs ${Key} from this tick, but its writer ${Writer} runs after it: move ${Writer} before ${Reader}, or read ${Key} without thisTick`
|
|
295
|
+
: never
|
|
296
|
+
: never
|
|
297
|
+
|
|
298
|
+
type UnflushedWriterError<R, Writer> =
|
|
299
|
+
Writer extends string
|
|
300
|
+
? R extends QueryRead<infer Slot extends string, any, any, infer Reader extends string>
|
|
301
|
+
? `${Reader} needs this tick's commands for queries.${Slot}, but ${Writer} queues them with no applyDeferred() in between: add Game.Schedule.applyDeferred() after ${Writer}`
|
|
302
|
+
: never
|
|
303
|
+
: never
|
|
304
|
+
|
|
305
|
+
/** Writers of the read's data that run after its reader, at this level and inside the reader's own entry. */
|
|
306
|
+
type LateWriters<Entries, R, Reader extends string> =
|
|
307
|
+
// Distributes over a union of schedules (a tick of one of several), then over each place the reader runs.
|
|
308
|
+
Entries extends ReadonlyArray<unknown> ? LateWritersAt<Entries, ReaderIndices<Entries, Reader>, R, Reader> : never
|
|
309
|
+
|
|
310
|
+
type LateWritersAt<Entries extends ReadonlyArray<unknown>, Index, R, Reader extends string> =
|
|
311
|
+
Index extends number
|
|
312
|
+
? Entries extends readonly [...TupleOf<Index>, infer At, ...infer Rest]
|
|
313
|
+
? | LateWriterError<R, WritersFor<SystemsIn<Rest[number]>, R>>
|
|
314
|
+
| LateWriters<NestedEntries<At>, R, Reader>
|
|
315
|
+
: never
|
|
316
|
+
: never
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The systems that run between the last marker and the reader (their commands
|
|
320
|
+
* are still queued when it runs), and whether a marker was found:
|
|
321
|
+
* `[Systems, Flushed]`, one per place the reader runs.
|
|
322
|
+
*/
|
|
323
|
+
type Segment<Entries, Reader> =
|
|
324
|
+
Entries extends ReadonlyArray<unknown> ? SegmentAt<Entries, ReaderIndices<Entries, Reader>, Reader> : never
|
|
325
|
+
|
|
326
|
+
type SegmentAt<Entries extends ReadonlyArray<unknown>, Index, Reader> =
|
|
327
|
+
Index extends number
|
|
328
|
+
? InnerSegment<Entries[Index], Reader> extends readonly [infer Inner, infer Flushed]
|
|
329
|
+
? Flushed extends true ? readonly [Inner, true]
|
|
330
|
+
: TailBefore<Entries, TupleOf<Index>> extends readonly [infer Outer, infer OuterFlushed]
|
|
331
|
+
? readonly [Inner | Outer, OuterFlushed]
|
|
332
|
+
: never
|
|
333
|
+
: never
|
|
334
|
+
: never
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* `TailSegment` of the entries before a position, walking back by index
|
|
338
|
+
* (`Count` holds one element per entry still to visit).
|
|
339
|
+
*/
|
|
340
|
+
type TailBefore<Entries extends ReadonlyArray<unknown>, Count extends ReadonlyArray<unknown>, Acc = never> =
|
|
341
|
+
Count extends readonly [unknown, ...infer Rest]
|
|
342
|
+
? Entries[Rest["length"]] extends infer Last
|
|
343
|
+
? Last extends ApplyDeferredStep ? readonly [Acc, true]
|
|
344
|
+
: Last extends AnySystem ? TailBefore<Entries, Rest, Acc | Last>
|
|
345
|
+
: Last extends { readonly kind: "applyStateTransitions" }
|
|
346
|
+
? TailSegment<NestedEntries<Last>> extends readonly [infer Inner, any] ? readonly [Acc | Inner, true] : readonly [Acc, true]
|
|
347
|
+
: TailSegment<NestedEntries<Last>> extends readonly [infer Inner, infer Flushed]
|
|
348
|
+
? Flushed extends true ? readonly [Acc | Inner, true] : TailBefore<Entries, Rest, Acc | Inner>
|
|
349
|
+
: TailBefore<Entries, Rest, Acc>
|
|
350
|
+
: never
|
|
351
|
+
: readonly [Acc, false]
|
|
352
|
+
|
|
353
|
+
/** The segment inside the entry that runs the reader: none for the reader itself; a transition step flushes first. */
|
|
354
|
+
type InnerSegment<At, Reader> =
|
|
355
|
+
At extends AnySystem ? readonly [never, false]
|
|
356
|
+
: At extends { readonly kind: "applyStateTransitions" }
|
|
357
|
+
? Segment<NestedEntries<At>, Reader> extends readonly [infer Inner, any] ? readonly [Inner, true] : readonly [never, true]
|
|
358
|
+
: Segment<NestedEntries<At>, Reader>
|
|
359
|
+
|
|
360
|
+
/** The systems after the last marker of `Entries` (walking back from the end), and whether a marker was found. */
|
|
361
|
+
type TailSegment<Entries, Acc = never> =
|
|
362
|
+
Entries extends readonly [...infer Init, infer Last]
|
|
363
|
+
? Last extends ApplyDeferredStep ? readonly [Acc, true]
|
|
364
|
+
: Last extends AnySystem ? TailSegment<Init, Acc | Last>
|
|
365
|
+
: Last extends { readonly kind: "applyStateTransitions" }
|
|
366
|
+
// Its bundle runs after its own flush; markers inside the bundle count too.
|
|
367
|
+
? TailSegment<NestedEntries<Last>> extends readonly [infer Inner, any] ? readonly [Acc | Inner, true] : readonly [Acc, true]
|
|
368
|
+
: TailSegment<NestedEntries<Last>> extends readonly [infer Inner, infer Flushed]
|
|
369
|
+
? Flushed extends true ? readonly [Acc | Inner, true] : TailSegment<Init, Acc | Inner>
|
|
370
|
+
: TailSegment<Init, Acc>
|
|
371
|
+
: readonly [Acc, false]
|
|
372
|
+
|
|
373
|
+
type UnflushedWriters<Entries, R, Reader extends string> =
|
|
374
|
+
R extends QueryRead<any, any, any, any>
|
|
375
|
+
? Segment<Entries, Reader> extends infer Found
|
|
376
|
+
? Found extends readonly [infer Systems, any] ? UnflushedWriterError<R, WritersFor<Systems, R>> : never
|
|
377
|
+
: never
|
|
378
|
+
: never
|
|
379
|
+
|
|
380
|
+
type ReadErrors<Entries extends ReadonlyArray<unknown>, Reads> =
|
|
381
|
+
Reads extends { readonly reader: infer Reader extends string }
|
|
382
|
+
? LateWriters<Entries, Reads, Reader> | UnflushedWriters<Entries, Reads, Reader>
|
|
383
|
+
: never
|
|
384
|
+
|
|
385
|
+
type CheckTick<Schedules extends ReadonlyArray<unknown>> =
|
|
386
|
+
[UnknownIn<Schedules, HasReads<Schedules>>] extends [never]
|
|
387
|
+
? ReadErrors<Schedules, SystemReads<SystemsIn<Schedules[number]>>>
|
|
388
|
+
: UnknownOrderError
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Every `thisTick` error of one `runtime.tick(...)` call over its arguments
|
|
392
|
+
* in order: writers that run after their reader, command writers with no
|
|
393
|
+
* `applyDeferred()` before a query reader, and a call whose order is not
|
|
394
|
+
* statically known. Free when nothing has a read.
|
|
395
|
+
*/
|
|
396
|
+
export type TickOrderErrors<Schedules extends ReadonlyArray<unknown>> =
|
|
397
|
+
HasReads<Schedules> extends false ? never : CheckTick<Schedules>
|
|
398
|
+
|
|
53
399
|
export interface TransitionBundleDefinition<
|
|
54
400
|
S extends Schema.Any = Schema.Any,
|
|
55
401
|
out Entries extends ReadonlyArray<StateMachine.AnyTransitionSchedule<S, any>> = ReadonlyArray<StateMachine.AnyTransitionSchedule<S, any>>,
|
|
56
402
|
out Needs extends Requirement.Requirement = Requirement.Requirement,
|
|
57
403
|
out Root = unknown,
|
|
58
404
|
out CarriedNeeds extends Requirement.Requirement = Needs,
|
|
59
|
-
out Failure extends SystemFailure = never
|
|
405
|
+
out Failure extends SystemFailure = never,
|
|
406
|
+
out Order extends AnyScheduleOrder = AnyScheduleOrder
|
|
60
407
|
> {
|
|
61
408
|
readonly kind: "transitionBundle"
|
|
62
409
|
readonly entries: Entries
|
|
63
410
|
readonly requirements: ReadonlyArray<CarriedNeeds>
|
|
64
411
|
readonly __failure?: (_: never) => Failure
|
|
412
|
+
/**
|
|
413
|
+
* The bundle's schedules in order, for `Game.System.thisTick` checks: they
|
|
414
|
+
* count at the position of the `applyStateTransitions(...)` step that
|
|
415
|
+
* applies the bundle, as if every one of them runs.
|
|
416
|
+
*/
|
|
417
|
+
readonly __order?: (_: never) => Order
|
|
65
418
|
readonly __schemaRoot?: Root | undefined
|
|
66
419
|
}
|
|
67
420
|
|
|
@@ -70,7 +423,9 @@ export interface ExecutableScheduleDefinition<
|
|
|
70
423
|
out Needs extends Requirement.Requirement = Requirement.Requirement,
|
|
71
424
|
out Root = unknown,
|
|
72
425
|
out CarriedNeeds extends Requirement.Requirement = Needs,
|
|
73
|
-
out Failure extends SystemFailure = never
|
|
426
|
+
out Failure extends SystemFailure = never,
|
|
427
|
+
// Any order by default, so a pattern that names five parameters (`infer S, any, ...`) matches every schedule.
|
|
428
|
+
out Order extends AnyScheduleOrder = AnyScheduleOrder
|
|
74
429
|
> {
|
|
75
430
|
readonly kind: "schedule"
|
|
76
431
|
readonly steps: ReadonlyArray<ScheduleStep>
|
|
@@ -78,6 +433,8 @@ export interface ExecutableScheduleDefinition<
|
|
|
78
433
|
readonly schema: S
|
|
79
434
|
readonly requirements: ReadonlyArray<CarriedNeeds>
|
|
80
435
|
readonly __failure?: (_: never) => Failure
|
|
436
|
+
/** Writes, `thisTick` reads, and order errors, checked by `runtime.tick(...)`. */
|
|
437
|
+
readonly __order?: (_: never) => Order
|
|
81
438
|
readonly __schemaRoot?: Root | undefined
|
|
82
439
|
}
|
|
83
440
|
|
|
@@ -86,8 +443,9 @@ export type ScheduleDefinition<
|
|
|
86
443
|
Needs extends Requirement.Requirement = Requirement.Requirement,
|
|
87
444
|
Root = unknown,
|
|
88
445
|
CarriedNeeds extends Requirement.Requirement = Needs,
|
|
89
|
-
Failure extends SystemFailure = never
|
|
90
|
-
|
|
446
|
+
Failure extends SystemFailure = never,
|
|
447
|
+
Order extends AnyScheduleOrder = AnyScheduleOrder
|
|
448
|
+
> = ExecutableScheduleDefinition<S, Needs, Root, CarriedNeeds, Failure, Order>
|
|
91
449
|
|
|
92
450
|
export namespace Schedule {
|
|
93
451
|
export type Definition<
|
|
@@ -114,11 +472,11 @@ export namespace Schedule {
|
|
|
114
472
|
*/
|
|
115
473
|
export type ScheduleEntry =
|
|
116
474
|
| ScheduleStep
|
|
117
|
-
| ScheduleDefinition<any, any, any, any, any>
|
|
475
|
+
| ScheduleDefinition<any, any, any, any, any, AnyScheduleOrder>
|
|
118
476
|
|
|
119
477
|
type EntrySchema<Entry> =
|
|
120
478
|
Entry extends { readonly schema: infer S extends Schema.Any } ? S
|
|
121
|
-
: Entry extends SystemDefinition<infer Spec, any, any, any, any
|
|
479
|
+
: Entry extends SystemDefinition<infer Spec, any, any, any, any> ? Spec["schema"]
|
|
122
480
|
: never
|
|
123
481
|
|
|
124
482
|
type MarkerNeeds<Step> =
|
|
@@ -164,7 +522,8 @@ export type AnonymousScheduleBuildFor<
|
|
|
164
522
|
CompositionExactRequirements<Entries>,
|
|
165
523
|
Root,
|
|
166
524
|
CompositionExactRequirements<Entries>,
|
|
167
|
-
CompositionFailure<Entries
|
|
525
|
+
CompositionFailure<Entries>,
|
|
526
|
+
CompositionOrder<Entries>
|
|
168
527
|
>
|
|
169
528
|
|
|
170
529
|
/**
|
|
@@ -181,7 +540,8 @@ export type ConditionalScheduleBuildFor<
|
|
|
181
540
|
CompositionExactRequirements<Entries> | ConditionNeedsFromConditions<Conditions>,
|
|
182
541
|
Root,
|
|
183
542
|
CompositionExactRequirements<Entries> | ConditionNeedsFromConditions<Conditions>,
|
|
184
|
-
CompositionFailure<Entries
|
|
543
|
+
CompositionFailure<Entries>,
|
|
544
|
+
CompositionOrder<Entries>
|
|
185
545
|
>
|
|
186
546
|
|
|
187
547
|
export type TransitionBundleInput<S extends Schema.Any = Schema.Any, Root = unknown> =
|
|
@@ -221,7 +581,8 @@ export const transitions = <
|
|
|
221
581
|
TransitionBundleRequirements<FlattenTransitionEntries<Entries>>,
|
|
222
582
|
unknown,
|
|
223
583
|
TransitionBundleRequirements<FlattenTransitionEntries<Entries>>,
|
|
224
|
-
TransitionBundleFailure<FlattenTransitionEntries<Entries
|
|
584
|
+
TransitionBundleFailure<FlattenTransitionEntries<Entries>>,
|
|
585
|
+
CompositionOrder<Entries>
|
|
225
586
|
> => {
|
|
226
587
|
const flattened = entries.flatMap((entry) =>
|
|
227
588
|
"kind" in entry && entry.kind === "transitionBundle" ? [...entry.entries] : [entry]
|
|
@@ -237,7 +598,8 @@ export const transitions = <
|
|
|
237
598
|
TransitionBundleRequirements<FlattenTransitionEntries<Entries>>,
|
|
238
599
|
unknown,
|
|
239
600
|
TransitionBundleRequirements<FlattenTransitionEntries<Entries>>,
|
|
240
|
-
TransitionBundleFailure<FlattenTransitionEntries<Entries
|
|
601
|
+
TransitionBundleFailure<FlattenTransitionEntries<Entries>>,
|
|
602
|
+
CompositionOrder<Entries>
|
|
241
603
|
>
|
|
242
604
|
}
|
|
243
605
|
|
|
@@ -287,6 +649,11 @@ export const make = <S extends Schema.Any, const Entries extends ReadonlyArray<S
|
|
|
287
649
|
* system's `when`, for gating many systems on one mode (playing, paused,
|
|
288
650
|
* a hit-stop) without repeating it on each.
|
|
289
651
|
*
|
|
652
|
+
* Use this when the gated systems should drop the events published while
|
|
653
|
+
* they were gated (a mode change makes them stale). Use `pauseWhen` instead
|
|
654
|
+
* when they must read that backlog on resume (hit-stop, pause menu). For one
|
|
655
|
+
* system, put the same conditions in its spec's `when` / `pauseWhen`.
|
|
656
|
+
*
|
|
290
657
|
* Conditions gate every system in `entries`, nested schedules included.
|
|
291
658
|
* Marker steps (`applyDeferred`, `applyStateTransitions`) in the group still
|
|
292
659
|
* run. A gated system is the same system as far as the runtime is concerned:
|
|
@@ -367,7 +734,7 @@ const gate = (system: AnySystem, when: ReadonlyArray<Condition>, pauseWhen: Read
|
|
|
367
734
|
export const isSystemStep = (step: ScheduleStep | ScheduleEntry): step is AnySystem =>
|
|
368
735
|
typeof step === "object" && step !== null && "spec" in step
|
|
369
736
|
|
|
370
|
-
const isScheduleEntry = (entry: ScheduleEntry): entry is ScheduleDefinition<any, any, any, any, any> =>
|
|
737
|
+
const isScheduleEntry = (entry: ScheduleEntry): entry is ScheduleDefinition<any, any, any, any, any, AnyScheduleOrder> =>
|
|
371
738
|
typeof entry === "object" && entry !== null && "kind" in entry && entry.kind === "schedule"
|
|
372
739
|
|
|
373
740
|
const describeValue = (value: unknown): string =>
|