@rbxts/planck 0.2.4 → 0.3.0-alpha.1

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.
@@ -1,86 +1,86 @@
1
- --!nonstrict
2
- local DependencyGraph = require("./DependencyGraph")
3
- local Phase = require("./Phase")
4
-
5
- --- @class Pipeline
6
- ---
7
- --- Pipelines represent a set of ordered Phases. Systems cannot be
8
- --- assigned to Pipelines themselves, but rather to Phases within
9
- --- those Pipelines.
10
- local Pipeline = {}
11
- Pipeline.__index = Pipeline
12
-
13
- function Pipeline:__tostring()
14
- return self._name
15
- end
16
-
17
- --- @method insert
18
- --- @within Pipeline
19
- --- @param phase Phase
20
- --- @return Pipeline
21
- ---
22
- --- Adds a Phase to the Pipeline, ordering it implicitly.
23
- function Pipeline:insert(phase)
24
- self.dependencyGraph:insert(phase)
25
- return self
26
- end
27
-
28
- --- @method insertAfter
29
- --- @within Pipeline
30
- --- @param phase Phase
31
- --- @param after Phase
32
- --- @return Pipeline
33
- ---
34
- --- Adds a Phase to the Pipeline after another Phase, ordering it explicitly.
35
- function Pipeline:insertAfter(phase, afterPhase)
36
- local i = table.find(self.dependencyGraph.nodes, afterPhase)
37
- assert(
38
- i,
39
- "Unknown Phase in Pipeline:insertAfter(_, unknown), try adding this Phase to the Pipeline."
40
- )
41
-
42
- self.dependencyGraph:insertAfter(phase, afterPhase)
43
- return self
44
- end
45
-
46
- --- @method insertBefore
47
- --- @within Pipeline
48
- --- @param phase Phase
49
- --- @param before Phase
50
- --- @return Pipeline
51
- ---
52
- --- Adds a Phase to the Pipeline before another Phase, ordering it explicitly.
53
- function Pipeline:insertBefore(phase, beforePhase)
54
- local i = table.find(self.dependencyGraph.nodes, beforePhase)
55
- assert(
56
- i,
57
- "Unknown Phase in Pipeline:insertBefore(_, unknown), try adding this Phase to the Pipeline."
58
- )
59
-
60
- self.dependencyGraph:insertBefore(phase, beforePhase)
61
- return self
62
- end
63
-
64
- --- @within Pipeline
65
- ---
66
- --- Creates a new Pipeline, with an optional name to use for debugging.
67
- --- When no name is provided, the script and line number will be used.
68
- function Pipeline.new(name: string?)
69
- name = name or debug.info(2, "sl")
70
- return setmetatable({
71
- _name = name,
72
- _type = "pipeline",
73
- dependencyGraph = DependencyGraph.new(),
74
- }, Pipeline)
75
- end
76
-
77
- --- @prop Startup Pipeline
78
- --- @within Pipeline
79
- ---
80
- --- A Pipeline containing the `PreStartup`, `Startup`, and `PostStartup` phases.
81
- Pipeline.Startup = Pipeline.new()
82
- :insert(Phase.PreStartup)
83
- :insert(Phase.Startup)
84
- :insert(Phase.PostStartup)
85
-
86
- return Pipeline
1
+ --!nonstrict
2
+ local DependencyGraph = require(script.Parent.DependencyGraph)
3
+ local Phase = require(script.Parent.Phase)
4
+
5
+ --- @class Pipeline
6
+ ---
7
+ --- Pipelines represent a set of ordered Phases. Systems cannot be
8
+ --- assigned to Pipelines themselves, but rather to Phases within
9
+ --- those Pipelines.
10
+ local Pipeline = {}
11
+ Pipeline.__index = Pipeline
12
+
13
+ function Pipeline:__tostring()
14
+ return self._name
15
+ end
16
+
17
+ --- @method insert
18
+ --- @within Pipeline
19
+ --- @param phase Phase
20
+ --- @return Pipeline
21
+ ---
22
+ --- Adds a Phase to the Pipeline, ordering it implicitly.
23
+ function Pipeline:insert(phase)
24
+ self.dependencyGraph:insert(phase)
25
+ return self
26
+ end
27
+
28
+ --- @method insertAfter
29
+ --- @within Pipeline
30
+ --- @param phase Phase
31
+ --- @param after Phase
32
+ --- @return Pipeline
33
+ ---
34
+ --- Adds a Phase to the Pipeline after another Phase, ordering it explicitly.
35
+ function Pipeline:insertAfter(phase, afterPhase)
36
+ local i = table.find(self.dependencyGraph.nodes, afterPhase)
37
+ assert(
38
+ i,
39
+ "Unknown Phase in Pipeline:insertAfter(_, unknown), try adding this Phase to the Pipeline."
40
+ )
41
+
42
+ self.dependencyGraph:insertAfter(phase, afterPhase)
43
+ return self
44
+ end
45
+
46
+ --- @method insertBefore
47
+ --- @within Pipeline
48
+ --- @param phase Phase
49
+ --- @param before Phase
50
+ --- @return Pipeline
51
+ ---
52
+ --- Adds a Phase to the Pipeline before another Phase, ordering it explicitly.
53
+ function Pipeline:insertBefore(phase, beforePhase)
54
+ local i = table.find(self.dependencyGraph.nodes, beforePhase)
55
+ assert(
56
+ i,
57
+ "Unknown Phase in Pipeline:insertBefore(_, unknown), try adding this Phase to the Pipeline."
58
+ )
59
+
60
+ self.dependencyGraph:insertBefore(phase, beforePhase)
61
+ return self
62
+ end
63
+
64
+ --- @within Pipeline
65
+ ---
66
+ --- Creates a new Pipeline, with an optional name to use for debugging.
67
+ --- When no name is provided, the script and line number will be used.
68
+ function Pipeline.new(name: string?)
69
+ name = name or debug.info(2, "sl")
70
+ return setmetatable({
71
+ _name = name,
72
+ _type = "pipeline",
73
+ dependencyGraph = DependencyGraph.new(),
74
+ }, Pipeline)
75
+ end
76
+
77
+ --- @prop Startup Pipeline
78
+ --- @within Pipeline
79
+ ---
80
+ --- A Pipeline containing the `PreStartup`, `Startup`, and `PostStartup` phases.
81
+ Pipeline.Startup = Pipeline.new()
82
+ :insert(Phase.PreStartup)
83
+ :insert(Phase.Startup)
84
+ :insert(Phase.PostStartup)
85
+
86
+ return Pipeline
@@ -0,0 +1,385 @@
1
+ import type { Plugin } from ".";
2
+ import type { Condition } from "./conditions";
3
+ import type { Phase } from "./Phase";
4
+ import type { Pipeline } from "./Pipeline";
5
+ import type { EventInstance, EventLike, ExtractEvents } from "./utils";
6
+
7
+ /**
8
+ * A system function that executes game logic with provided arguments. Runs each
9
+ * time the scheduler runs it. A system should not return a value.
10
+ *
11
+ * @example
12
+ *
13
+ * ```ts
14
+ * function movementSystem(world: World): void {
15
+ * // Update entity positions
16
+ * }
17
+ * ```
18
+ */
19
+ export type SystemFn<T extends unknown[]> = (...args: T) => void | undefined;
20
+
21
+ /**
22
+ * A cleanup function that executes when a system is removed from the scheduler.
23
+ * Used for resource cleanup when a system is unscheduled, or in hot-reload
24
+ * scenarios.
25
+ */
26
+ export type CleanupFn<T extends unknown[]> = (...args: T) => void | undefined;
27
+
28
+ /**
29
+ * The runtime system and optional cleanup function returned from
30
+ * initialization.
31
+ *
32
+ * At least one field must be present:
33
+ *
34
+ * - `system`: Runtime function that runs on each execution after initialization
35
+ * - `cleanup`: Cleanup function for hot-reload support.
36
+ *
37
+ * @example
38
+ *
39
+ * ```ts
40
+ * // Runtime system with cleanup
41
+ * return {
42
+ * system: (world) => updateLogic(world),
43
+ * cleanup: () => cleanupResources(),
44
+ * };
45
+ * ```
46
+ */
47
+ export type InitializerResult<T extends unknown[]> = {
48
+ /**
49
+ * Runtime system function that executes immediately after initialization and
50
+ * on all subsequent frames.
51
+ */
52
+ system?: SystemFn<T>;
53
+ /** Cleanup function that runs when the system is removed. */
54
+ cleanup?: CleanupFn<T>;
55
+ } & ({ system: SystemFn<T> } | { cleanup: CleanupFn<T> });
56
+
57
+ /**
58
+ * An initializer system that performs one-time setup and returns the runtime
59
+ * system. The initialization logic runs once on first execution, then the
60
+ * returned function executes immediately and on all subsequent executions.
61
+ *
62
+ * @example
63
+ *
64
+ * ```ts
65
+ * // Function return syntax
66
+ * function renderSystem(world: World): SystemFn<[World]> {
67
+ * const renderables = world.query(Transform, Model).cached();
68
+ *
69
+ * return (world: World) => {
70
+ * for (const [entity, transform, model] of renderables) {
71
+ * render(transform, model);
72
+ * }
73
+ * };
74
+ * }
75
+ *
76
+ * // With cleanup
77
+ * function renderSystem(world: World): InitializerResult<[World]> {
78
+ * const renderables = world.query(Transform, Model).cached();
79
+ * return {
80
+ * system: (world: World) => {
81
+ * for (const [entity, transform, model] of renderables) {
82
+ * render(transform, model);
83
+ * }
84
+ * },
85
+ * };
86
+ * }
87
+ * ```
88
+ */
89
+ export type InitializerSystemFn<T extends unknown[]> = (
90
+ ...args: T
91
+ ) => InitializerResult<T> | LuaTuple<[SystemFn<T>, CleanupFn<T>]> | SystemFn<T>;
92
+
93
+ /**
94
+ * Base configuration shared by all system table types. Contains optional
95
+ * metadata and execution conditions.
96
+ */
97
+ export interface BaseSystemTable<T extends unknown[]> {
98
+ /** Allow additional properties for plugin extensibility. */
99
+ [key: string]: unknown;
100
+ /** Human-readable name for debugging and profiling. */
101
+ name?: string;
102
+ /** The execution phase for this system. Defaults to Main phase. */
103
+ phase?: Phase;
104
+ /** Conditions that must be met for the system to execute. */
105
+ runConditions?: Condition<T>[] | LuaTuple<[Condition<T>, ...any[]]>;
106
+ }
107
+
108
+ /**
109
+ * Configuration table for systems. Supports standard systems, initializer
110
+ * systems, and initializers with cleanup.
111
+ */
112
+ export interface SystemTable<T extends unknown[]> extends BaseSystemTable<T> {
113
+ system: InitializerSystemFn<T> | SystemFn<T>;
114
+ }
115
+
116
+ /**
117
+ * A system can be either a function or a configuration table. Use a function
118
+ * for simple systems, or a table for systems that need additional configuration
119
+ * like phases, run conditions, or names.
120
+ *
121
+ * Systems can optionally perform one-time initialization by returning a
122
+ * function on their first execution. See {@link InitializerSystemFn} for
123
+ * initialization patterns.
124
+ *
125
+ * @example
126
+ *
127
+ * ```ts
128
+ * // Standard system (runs repeatedly)
129
+ * function updateSystem(world: World): void {
130
+ * // update entities
131
+ * }
132
+ *
133
+ * // Initializer system (setup runs once, then inner runs immediately)
134
+ * function movementSystem(world: World): SystemFn<[World]> {
135
+ * const entities = world.query(Transform, Velocity).cached();
136
+ * return (world) => {
137
+ * for (const [, transform, velocity] of entities) {
138
+ * transform.position.add(velocity);
139
+ * }
140
+ * };
141
+ * }
142
+ *
143
+ * // Simple function system
144
+ * export = updateSystem;
145
+ *
146
+ * // Table system with configuration
147
+ * export = {
148
+ * name: "Complex System",
149
+ * system: audioSystem,
150
+ * phase: Phases.Update,
151
+ * runConditions: [onlyWhenNeeded],
152
+ * };
153
+ * ```
154
+ */
155
+ export type System<T extends unknown[]> = InitializerSystemFn<T> | SystemFn<T> | SystemTable<T>;
156
+
157
+ /**
158
+ * An Object which handles scheduling Systems to run within different Phases.
159
+ * The order of which Systems run will be defined either implicitly by when it
160
+ * was added, or explicitly by tagging the system with a Phase.
161
+ */
162
+ export class Scheduler<T extends unknown[]> {
163
+ /**
164
+ * Creates a new Scheduler, the args passed will be passed to any System
165
+ * anytime it is ran by the Scheduler.
166
+ */
167
+ constructor(...args: T);
168
+
169
+ /**
170
+ * Initializes a plugin with the scheduler, see the [Plugin
171
+ * Docs](/docs/plugins) for more information.
172
+ */
173
+ addPlugin(plugin: Plugin<T>): this;
174
+
175
+ /**
176
+ * Adds the System to the Scheduler, scheduling it to be ran implicitly within
177
+ * the provided Phase or on the default Main phase.
178
+ */
179
+ addSystem(system: System<T>, phase?: Phase): this;
180
+
181
+ /**
182
+ * Adds the Systems to the Scheduler, scheduling them to be ran implicitly
183
+ * within the provided Phase or on the default Main phase.
184
+ */
185
+ addSystems(systems: System<T>[], phase?: Phase): this;
186
+
187
+ /** Changes the Phase that this system is scheduled on. */
188
+ editSystem(system: System<T>, newPhase: Phase): this;
189
+
190
+ /**
191
+ * Removes the System from the Scheduler.
192
+ *
193
+ * If the system provided a cleanup function, that cleanup executes before
194
+ * removal.
195
+ *
196
+ * @example
197
+ *
198
+ * ```ts
199
+ * const networkSystem = (world: World) => {
200
+ * const conn = connect();
201
+ * return {
202
+ * system: (world: World) => sync(conn),
203
+ * cleanup: () => conn.disconnect(), // Runs on removeSystem
204
+ * };
205
+ * };
206
+ *
207
+ * scheduler.addSystem(networkSystem, Phases.Update);
208
+ * // Later...
209
+ * scheduler.removeSystem(networkSystem); // Cleanup executes
210
+ * ```
211
+ */
212
+ removeSystem(system: System<T>): this;
213
+
214
+ /** Replaces the System with a new System. */
215
+ replaceSystem(system: System<T>, newSystem: System<T>): this;
216
+
217
+ /**
218
+ * Returns the time since the system was ran last. This must be used within a
219
+ * registered system.
220
+ */
221
+ getDeltaTime(): number;
222
+
223
+ /**
224
+ * Initializes the Phase within the Scheduler, ordering it implicitly by
225
+ * setting it as a dependent of the previous Phase/Pipeline.
226
+ */
227
+ insert(phase: Phase): this;
228
+ /**
229
+ * Initializes the Pipeline and it's Phases within the Scheduler, ordering the
230
+ * Pipeline implicitly by setting it as a dependent of the previous
231
+ * Phase/Pipeline.
232
+ */
233
+ insert(pipeline: Pipeline): this;
234
+ /**
235
+ * Initializes the Phase within the Scheduler, ordering it implicitly by
236
+ * setting it as a dependent of the previous Phase/Pipeline, and scheduling it
237
+ * to be ran on the specified event.
238
+ *
239
+ * ```ts
240
+ * const myScheduler = new Scheduler().insert(
241
+ * myPhase,
242
+ * RunService,
243
+ * "Heartbeat",
244
+ * );
245
+ * ```
246
+ */
247
+ insert<E extends EventInstance>(phase: Phase, instance: E, event: ExtractEvents<E>): this;
248
+ /**
249
+ * Initializes the Phase within the Scheduler, ordering it implicitly by
250
+ * setting it as a dependent of the previous Phase/Pipeline, and scheduling it
251
+ * to be ran on the specified event.
252
+ *
253
+ * ```ts
254
+ * const myScheduler = new Scheduler().insert(
255
+ * myPhase,
256
+ * RunService,
257
+ * "Heartbeat",
258
+ * );
259
+ * ```
260
+ */
261
+ insert(phase: Phase, instance: EventLike, event: string): this;
262
+ /**
263
+ * Initializes the Pipeline and it's Phases within the Scheduler, ordering the
264
+ * Pipeline implicitly by setting it as a dependent of the previous
265
+ * Phase/Pipeline, and scheduling it to be ran on the specified event.
266
+ *
267
+ * ```ts
268
+ * const myScheduler = new Scheduler().insert(
269
+ * myPipeline,
270
+ * RunService,
271
+ * "Heartbeat",
272
+ * );
273
+ * ```
274
+ */
275
+ insert<E extends EventInstance>(pipeline: Pipeline, instance: E, event: ExtractEvents<E>): this;
276
+ /**
277
+ * Initializes the Pipeline and it's Phases within the Scheduler, ordering the
278
+ * Pipeline implicitly by setting it as a dependent of the previous
279
+ * Phase/Pipeline, and scheduling it to be ran on the specified event.
280
+ *
281
+ * ```ts
282
+ * const myScheduler = new Scheduler().insert(
283
+ * myPipeline,
284
+ * RunService,
285
+ * "Heartbeat",
286
+ * );
287
+ * ```
288
+ */
289
+ insert(pipeline: Pipeline, instance: EventLike, event: string): this;
290
+
291
+ /**
292
+ * Initializes the Phase within the Scheduler, ordering it explicitly by
293
+ * setting the after Phase/Pipeline as a dependent.
294
+ */
295
+ insertAfter(phase: Phase, after: Phase | Pipeline): this;
296
+ /**
297
+ * Initializes the Pipeline and it's Phases within the Scheduler, ordering the
298
+ * Pipeline explicitly by setting the after Phase/Pipeline as a dependent.
299
+ */
300
+ insertAfter(pipeline: Pipeline, after: Phase | Pipeline): this;
301
+
302
+ /**
303
+ * Initializes the Phase within the Scheduler, ordering it explicitly by
304
+ * setting the before Phase/Pipeline as a dependency.
305
+ */
306
+ insertBefore(phase: Phase, before: Phase | Pipeline): this;
307
+ /**
308
+ * Initializes the Pipeline and it's Phases within the Scheduler, ordering the
309
+ * Pipeline explicitly by setting the before Phase/Pipeline as a dependency.
310
+ */
311
+ insertBefore(pipeline: Pipeline, before: Phase | Pipeline): this;
312
+
313
+ /**
314
+ * Adds a Run Condition which the Scheduler will check before this System is
315
+ * ran.
316
+ */
317
+ addRunCondition(
318
+ system: System<T>,
319
+ fn: Condition<T> | LuaTuple<[Condition<T>, ...any[]]>,
320
+ ...args: any
321
+ ): this;
322
+ /**
323
+ * Adds a Run Condition which the Scheduler will check before any Systems
324
+ * within this Phase are ran.
325
+ */
326
+ addRunCondition(
327
+ phase: Phase,
328
+ fn: Condition<T> | LuaTuple<[Condition<T>, ...any[]]>,
329
+ ...args: any
330
+ ): this;
331
+ /**
332
+ * Adds a Run Condition which the Scheduler will check before any Systems
333
+ * within any Phases apart of this Pipeline are ran.
334
+ */
335
+ addRunCondition(
336
+ pipeline: Pipeline,
337
+ fn: Condition<T> | LuaTuple<[Condition<T>, ...any[]]>,
338
+ ...args: any
339
+ ): this;
340
+
341
+ /** Runs all Systems tagged with the Phase in order. */
342
+ run(system: Phase): this;
343
+ /** Runs all Systems tagged with any Phase within the Pipeline in order. */
344
+ run(pipeline: Pipeline): this;
345
+ /** Runs the System, passing in the arguments of the Scheduler, `U...`. */
346
+ run(system: System<T>): this;
347
+
348
+ /**
349
+ * Runs all Systems within order.
350
+ *
351
+ * ### NOTE
352
+ *
353
+ * When you add a Pipeline or Phase with an event, it will be grouped with
354
+ * other Pipelines/Phases on that event. Otherwise, it will be added to the
355
+ * default group.
356
+ *
357
+ * When not running systems on Events, such as with the `runAll` method, the
358
+ * Default group will be ran first, and then each Event Group in the order
359
+ * created.
360
+ *
361
+ * Pipelines/Phases in these groups are still ordered by their dependencies
362
+ * and by the order of insertion.
363
+ */
364
+ runAll(): this;
365
+
366
+ /**
367
+ * Disconnects all events, closes all threads, and performs other cleanup
368
+ * work.
369
+ *
370
+ * ### Danger
371
+ *
372
+ * Only use this if you intend to not use the associated Scheduler anymore. It
373
+ * will not work as intended.
374
+ *
375
+ * You should dereference the scheduler object so that it may be garbage
376
+ * collected.
377
+ *
378
+ * ### Warning
379
+ *
380
+ * If you're creating a "throwaway" scheduler, you should not add plugins like
381
+ * Jabby or the Matter Debugger to it. These plugins are unable to properly be
382
+ * cleaned up, use them with caution.
383
+ */
384
+ cleanup(): void;
385
+ }