@rbxts/planck 0.1.0-rc.2

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.
@@ -0,0 +1,755 @@
1
+ local RunService = game:GetService("RunService")
2
+
3
+ local Pipeline = require(script.Parent:WaitForChild('Pipeline'))
4
+ local Phase = require(script.Parent:WaitForChild('Phase'))
5
+
6
+ local utils = require(script.Parent:WaitForChild('utils'))
7
+ local hooks = require(script.Parent:WaitForChild('hooks'))
8
+
9
+ local getSystem = utils.getSystem
10
+ local getSystemName = utils.getSystemName
11
+
12
+ local isPhase = utils.isPhase
13
+ local isPipeline = utils.isPipeline
14
+
15
+ local isValidEvent = utils.isValidEvent
16
+ local getEventIdentifier = utils.getEventIdentifier
17
+
18
+ -- Recent errors in Planks itself
19
+ local recentLogs = {}
20
+ local timeLastLogged = os.clock()
21
+
22
+ --- @type SystemFn ((U...) -> any)
23
+ --- @within Scheduler
24
+
25
+ --- @interface SystemTable
26
+ --- @within Scheduler
27
+ --- .system SystemFn<U...>
28
+ --- .phase Phase?
29
+ --- .[any] any
30
+
31
+ --- @type System SystemFn<U...> | SystemTable<U...>
32
+ --- @within Scheduler
33
+
34
+ --- @class Scheduler
35
+ ---
36
+ --- An Object which handles scheduling Systems to run within different
37
+ --- Phases. The order of which Systems run will be defined either
38
+ --- implicitly by when it was added, or explicitly by tagging the system
39
+ --- with a Phase.
40
+ local Scheduler = {}
41
+ Scheduler.__index = Scheduler
42
+
43
+ Scheduler.Hooks = hooks.Hooks
44
+
45
+ --- @method addPlugin
46
+ --- @within Scheduler
47
+ --- @param plugin PlanckPlugin
48
+ ---
49
+ --- Initializes a plugin with the scheduler, see the [Plugin Docs](/docs/plugins) for more information.
50
+ function Scheduler:addPlugin(plugin)
51
+ plugin:build(self)
52
+ return self
53
+ end
54
+
55
+ function Scheduler:_addHook(hook, fn)
56
+ assert(self._hooks[hook], `Unknown Hook: {hook}`)
57
+ table.insert(self._hooks[hook], fn)
58
+ end
59
+
60
+ -- Inspiration from https://github.com/matter-ecs/matter <3
61
+ function Scheduler:_handleLogs(systemInfo)
62
+ if not systemInfo.timeLastLogged then
63
+ systemInfo.timeLastLogged = os.clock()
64
+ end
65
+
66
+ if not systemInfo.recentLogs then
67
+ systemInfo.recentLogs = {}
68
+ end
69
+
70
+ if os.clock() - systemInfo.timeLastLogged > 10 then
71
+ systemInfo.timeLastLogged = os.clock()
72
+ systemInfo.recentLogs = {}
73
+ end
74
+
75
+ local name = debug.info(systemInfo.system, "n")
76
+
77
+ for _, logMessage in systemInfo.logs do
78
+ if not systemInfo.recentLogs[logMessage] then
79
+ task.spawn(error, logMessage, 0)
80
+ warn(
81
+ `Planck: Error occurred in system{string.len(name) > 0 and ` '{name}'` or ""}, this error will be ignored for 10 seconds`
82
+ )
83
+ systemInfo.recentLogs[logMessage] = true
84
+ end
85
+ end
86
+
87
+ table.clear(systemInfo.logs)
88
+ end
89
+
90
+ function Scheduler:runSystem(system)
91
+ local runIf = self._runIfConditions[system]
92
+ if runIf and runIf(table.unpack(self._vargs)) == false then
93
+ return
94
+ end
95
+
96
+ local systemInfo = self._systemInfo[system]
97
+
98
+ if not self._thread then
99
+ self._thread = coroutine.create(function()
100
+ while true do
101
+ local fn = coroutine.yield()
102
+ self._yielded = true
103
+ fn()
104
+ self._yielded = false
105
+ end
106
+ end)
107
+
108
+ coroutine.resume(self._thread)
109
+ end
110
+
111
+ local didYield = false
112
+
113
+ local function systemCall()
114
+ local function noYield()
115
+ local success, err = coroutine.resume(self._thread, function()
116
+ system(table.unpack(self._vargs))
117
+ end)
118
+
119
+ if not success then
120
+ didYield = true
121
+ table.insert(systemInfo.logs, err)
122
+ hooks.systemError(self, systemInfo, err)
123
+ return
124
+ end
125
+
126
+ if self._yielded then
127
+ didYield = true
128
+ local trace, line = debug.info(systemInfo.system, "sl")
129
+ table.insert(systemInfo.logs, `{trace}:{line}: System yielded`)
130
+ hooks.systemError(
131
+ self,
132
+ systemInfo,
133
+ `{trace}:{line}: System yielded`
134
+ )
135
+ end
136
+ end
137
+
138
+ hooks.systemCall(self, "SystemCall", systemInfo, noYield)
139
+ end
140
+
141
+ local function inner()
142
+ hooks.systemCall(self, "InnerSystemCall", systemInfo, systemCall)
143
+ end
144
+
145
+ local function outer()
146
+ hooks.systemCall(self, "OuterSystemCall", systemInfo, inner)
147
+ end
148
+
149
+ if os.clock() - timeLastLogged > 10 then
150
+ timeLastLogged = os.clock()
151
+ recentLogs = {}
152
+ end
153
+
154
+ local success, err: string? = pcall(outer)
155
+ if not success and not recentLogs[err] then
156
+ task.spawn(error, err, 0)
157
+ warn(
158
+ `Planck: Error occurred while running hooks, this error will be ignored for 10 seconds`
159
+ )
160
+ hooks.systemError(
161
+ self,
162
+ systemInfo,
163
+ `Error occurred while running hooks: {err}`
164
+ )
165
+ recentLogs[err] = true
166
+ end
167
+
168
+ if didYield then
169
+ coroutine.close(self._thread)
170
+
171
+ self._thread = coroutine.create(function()
172
+ while true do
173
+ local fn = coroutine.yield()
174
+ self._yielded = true
175
+ fn()
176
+ self._yielded = false
177
+ end
178
+ end)
179
+
180
+ coroutine.resume(self._thread)
181
+ end
182
+
183
+ self:_handleLogs(systemInfo)
184
+ end
185
+
186
+ function Scheduler:runPhase(phase)
187
+ local runIf = self._runIfConditions[phase]
188
+ if runIf and runIf(table.unpack(self._vargs)) == false then
189
+ return
190
+ end
191
+
192
+ hooks.phaseBegan(self, phase)
193
+
194
+ for _, system in self._phaseToSystems[phase] do
195
+ self:runSystem(system)
196
+ end
197
+ end
198
+
199
+ function Scheduler:runPipeline(pipeline)
200
+ local runIf = self._runIfConditions[pipeline]
201
+ if runIf and runIf(table.unpack(self._vargs)) == false then
202
+ return
203
+ end
204
+
205
+ for _, phase in pipeline._phases do
206
+ self:runPhase(phase)
207
+ end
208
+ end
209
+
210
+ --- @method run
211
+ --- @within Scheduler
212
+ --- @param phase Phase
213
+ --- @return Scheduler
214
+ ---
215
+ --- Runs all Systems tagged with the Phase in order.
216
+
217
+ --- @method run
218
+ --- @within Scheduler
219
+ --- @param pipeline Pipeline
220
+ --- @return Scheduler
221
+ ---
222
+ --- Runs all Systems tagged with any Phase within the Pipeline in order.
223
+
224
+ --- @method run
225
+ --- @within Scheduler
226
+ --- @param system System
227
+ --- @return Scheduler
228
+ ---
229
+ --- Runs the System, passing in the arguments of the Scheduler, `U...`.
230
+
231
+ function Scheduler:run(dependent)
232
+ if not dependent then
233
+ error("No dependent specified in Scheduler:run(_)")
234
+ end
235
+
236
+ self:runPipeline(Pipeline.Startup)
237
+
238
+ if getSystem(dependent) then
239
+ self:runSystem(dependent)
240
+ elseif isPhase(dependent) then
241
+ self:runPhase(dependent)
242
+ elseif isPipeline(dependent) then
243
+ self:runPipeline(dependent)
244
+ else
245
+ error("Unknown dependent passed into Scheduler:run(unknown)")
246
+ end
247
+
248
+ return self
249
+ end
250
+
251
+ --- @method runAll
252
+ --- @within Scheduler
253
+ --- @return Scheduler
254
+ ---
255
+ --- Runs all Systems within order.
256
+ function Scheduler:runAll()
257
+ for _, phase in self._orderedPhases do
258
+ self:run(phase)
259
+ end
260
+ end
261
+
262
+ function Scheduler:_insertPhaseAt(phase, index, instance, event)
263
+ assert(
264
+ not table.find(self._orderedPhases, phase),
265
+ "Phase already initialized"
266
+ )
267
+
268
+ table.insert(self._orderedPhases, index, phase)
269
+ self._phaseToSystems[phase] = {}
270
+
271
+ hooks.phaseAdd(self, phase)
272
+
273
+ if isValidEvent(instance, event) then
274
+ self:_schedulePhase(phase, instance, event)
275
+ end
276
+ end
277
+
278
+ function Scheduler:insertPhase(phase, instance, event)
279
+ local index = table.find(self._orderedPhases, Phase.Update)
280
+ self:_insertPhaseAt(phase, index, instance, event)
281
+ end
282
+
283
+ function Scheduler:insertPipeline(pipeline, instance, event)
284
+ for _, phase in pipeline._phases do
285
+ self:insertPhase(phase, instance, event)
286
+ end
287
+ end
288
+
289
+ --- @method insert
290
+ --- @within Scheduler
291
+ --- @param phase Phase
292
+ --- @return Scheduler
293
+ ---
294
+ --- Initializes the Phase within the Scheduler, ordering it implicitly.
295
+
296
+ --- @method insert
297
+ --- @within Scheduler
298
+ --- @param pipeline Pipeline
299
+ --- @return Scheduler
300
+ ---
301
+ --- Initializes all Phases within the Pipeline within the Scheduler,
302
+ --- ordering the Pipeline implicitly.
303
+
304
+ --- @method insert
305
+ --- @within Scheduler
306
+ --- @param phase Phase
307
+ --- @param instance Instance | EventLike
308
+ --- @param event string | EventLike
309
+ --- @return Scheduler
310
+ ---
311
+ --- Initializes the Phase within the Scheduler, ordering it implicitly
312
+ --- and scheduling it to be ran on the specified event.
313
+ ---
314
+ --- ```lua
315
+ --- local myScheduler = Scheduler.new()
316
+ --- :insert(myPhase, RunService, "Heartbeat")
317
+ --- ```
318
+
319
+ --- @method insert
320
+ --- @within Scheduler
321
+ --- @param pipeline Pipeline
322
+ --- @param instance Instance | EventLike
323
+ --- @param event string | EventLike
324
+ --- @return Scheduler
325
+ ---
326
+ --- Initializes all Phases within the Pipeline within the Scheduler,
327
+ --- ordering the Pipeline implicitly and scheduling it to be ran on
328
+ --- the specified event.
329
+ ---
330
+ --- ```lua
331
+ --- local myScheduler = Scheduler.new()
332
+ --- :insert(myPipeline, RunService, "Heartbeat")
333
+ --- ```
334
+
335
+ function Scheduler:insert(dependent, instance, event)
336
+ if isPhase(dependent) then
337
+ self:insertPhase(dependent, instance, event)
338
+ elseif isPipeline(dependent) then
339
+ self:insertPipeline(dependent, instance, event)
340
+ else
341
+ error("Unknown dependent passed to Scheduler:insert(unknown, _, _)")
342
+ end
343
+
344
+ return self
345
+ end
346
+
347
+ --- @method insertAfter
348
+ --- @within Scheduler
349
+ --- @param phase Phase
350
+ --- @param after Phase | Pipeline
351
+ --- @return Scheduler
352
+ ---
353
+ --- Initializes the Phase within the Scheduler, ordering it
354
+ --- explicitly after the Phase, or adding to the end of the
355
+ --- Pipeline provided.
356
+
357
+ --- @method insertAfter
358
+ --- @within Scheduler
359
+ --- @param pipeline Pipeline
360
+ --- @param after Phase | Pipeline
361
+ --- @return Scheduler
362
+ ---
363
+ --- Initializes all Phases within the Pipeline within the Scheduler,
364
+ --- ordering the Pipeline explicitly after the Phase, or adding
365
+ --- to the end of the Pipeline provided.
366
+
367
+ function Scheduler:insertAfter(dependent, after)
368
+ if isPhase(after) then
369
+ local index = table.find(self._orderedPhases, after)
370
+ assert(
371
+ index,
372
+ "Phase never initialized in Scheduler:insertAfter(_, phase)"
373
+ )
374
+
375
+ if isPhase(dependent) then
376
+ self:_insertPhaseAt(dependent, index + 1)
377
+ elseif isPipeline(dependent) then
378
+ for _, phase in dependent._phases do
379
+ index += 1
380
+ self:_insertPhaseAt(phase, index)
381
+ end
382
+ else
383
+ error(
384
+ "Unknown dependent passed in Scheduler:insertAfter(unknown, _)"
385
+ )
386
+ end
387
+ elseif isPipeline(after) then
388
+ local before = after._phases[#after._phases]
389
+
390
+ if isPhase(dependent) then
391
+ after:insert(dependent)
392
+ end
393
+
394
+ self:insertAfter(dependent, before)
395
+ else
396
+ error("Unknown dependency passed in Scheduler:insertAfter(_, unknown)")
397
+ end
398
+
399
+ return self
400
+ end
401
+
402
+ --- @method addSystems
403
+ --- @within Scheduler
404
+ --- @param systems System
405
+ --- @param phase Phase?
406
+ ---
407
+ --- Adds the System to the Scheduler, scheduling it to be ran
408
+ --- implicitly within the provided Phase or on the default Main phase.
409
+ function Scheduler:addSystem(system, phase)
410
+ local systemFn = getSystem(system)
411
+
412
+ if not systemFn then
413
+ error("Unknown system passed to Scheduler:addSystem(unknown, phase?)")
414
+ end
415
+
416
+ local systemInfo = {
417
+ system = systemFn,
418
+ phase = phase,
419
+ name = getSystemName(systemFn),
420
+ logs = {},
421
+ }
422
+
423
+ if not phase then
424
+ if type(system) == "table" and system.phase then
425
+ systemInfo.phase = system.phase
426
+ else
427
+ systemInfo.phase = Phase.Update
428
+ end
429
+ end
430
+
431
+ self._systemInfo[systemFn] = systemInfo
432
+ table.insert(self._phaseToSystems[systemInfo.phase], systemFn)
433
+
434
+ hooks.systemAdd(self, systemInfo)
435
+
436
+ return self
437
+ end
438
+
439
+ --- @method addSystems
440
+ --- @within Scheduler
441
+ --- @param systems { System }
442
+ --- @param phase Phase?
443
+ ---
444
+ --- Adds the Systems to the Scheduler, scheduling them to be ran
445
+ --- implicitly within the provided Phase or on the default Main phase.
446
+ function Scheduler:addSystems(systems, phase)
447
+ if type(systems) ~= "table" then
448
+ error("Unknown systems passed to Scheduler:addSystems(unknown, phase?)")
449
+ end
450
+
451
+ local foundSystem = false
452
+
453
+ for _, system in systems do
454
+ if getSystem(system) then
455
+ foundSystem = true
456
+ self:addSystem(system, phase)
457
+ end
458
+ end
459
+
460
+ if not foundSystem then
461
+ error(
462
+ "Unknown table passed to Scheduler:addSystems({ unknown }, phase?)"
463
+ )
464
+ end
465
+ end
466
+
467
+ --- @method editSystem
468
+ --- @within Scheduler
469
+ --- @param system System
470
+ --- @param newPhase Phase
471
+ ---
472
+ --- Changes the Phase that this system is scheduled on.
473
+ function Scheduler:editSystem(system, newPhase)
474
+ local systemFn = getSystem(system)
475
+ local systemInfo = self._systemInfo[systemFn]
476
+ assert(
477
+ systemInfo,
478
+ "Attempt to edit a non-exist system in Scheduler:editSystem(_)"
479
+ )
480
+
481
+ assert(
482
+ newPhase and self._phaseToSystems[newPhase] ~= nil or true,
483
+ "Phase never initialized before using Scheduler:editSystem(_, Phase)"
484
+ )
485
+
486
+ local systems = self._phaseToSystems[systemInfo.phase]
487
+
488
+ local index = table.find(systems, systemFn)
489
+ assert(index, "Unable to find system within phase")
490
+
491
+ table.remove(systems, index)
492
+ table.insert(self._phaseToSystems[newPhase], systemFn)
493
+
494
+ systemInfo.phase = newPhase
495
+ return self
496
+ end
497
+
498
+ --- @method removeSystem
499
+ --- @within Scheduler
500
+ --- @param system System
501
+ ---
502
+ --- Removes the System from the Scheduler.
503
+ function Scheduler:removeSystem(system)
504
+ local systemFn = getSystem(system)
505
+ local systemInfo = self._systemInfo[systemFn]
506
+ assert(
507
+ systemInfo,
508
+ "Attempt to remove a non-exist system in Scheduler:removeSystem(_)"
509
+ )
510
+
511
+ local systems = self._phaseToSystems[systemInfo.phase]
512
+
513
+ local index = table.find(systems, systemFn)
514
+ assert(index, "Unable to find system within phase")
515
+
516
+ table.remove(systems, index)
517
+ self._systemInfo[systemFn] = nil
518
+
519
+ hooks.systemRemove(self, systemInfo)
520
+
521
+ return self
522
+ end
523
+
524
+ --- @method replaceSystem
525
+ --- @within Scheduler
526
+ --- @param old System
527
+ --- @param new System
528
+ ---
529
+ --- Replaces the System with a new System.
530
+ function Scheduler:replaceSystem(old, new)
531
+ local oldSystemFn = getSystem(old)
532
+ local oldSystemInfo = self._systemInfo[oldSystemFn]
533
+ assert(
534
+ oldSystemInfo,
535
+ "Attempt to replace a non-existent system in Scheduler:replaceSystem(unknown, _)"
536
+ )
537
+
538
+ local newSystemFn = getSystem(new)
539
+ assert(
540
+ newSystemFn,
541
+ "Attempt to pass non-system in Scheduler:replaceSystem(_, unknown)"
542
+ )
543
+
544
+ local systems = self._phaseToSystems[oldSystemInfo.phase]
545
+
546
+ local index = table.find(systems, oldSystemFn)
547
+ assert(index, "Unable to find system within phase")
548
+
549
+ table.remove(systems, index)
550
+ table.insert(systems, index, newSystemFn)
551
+
552
+ local copy = table.clone(oldSystemInfo)
553
+
554
+ oldSystemInfo.system = newSystemFn
555
+ oldSystemInfo.name = getSystemName(newSystemFn)
556
+
557
+ hooks.systemReplace(self, copy, oldSystemInfo)
558
+
559
+ self._systemInfo[newSystemFn] = self._systemInfo[oldSystemFn]
560
+ self._systemInfo[oldSystemFn] = nil
561
+
562
+ return self
563
+ end
564
+
565
+ --- @method setRunCondition
566
+ --- @within Scheduler
567
+ --- @param system System
568
+ --- @param fn (U...) -> boolean
569
+ ---
570
+ --- Adds a Run Condition which the Scheduler will check before
571
+ --- this System is ran.
572
+
573
+ --- @method setRunCondition
574
+ --- @within Scheduler
575
+ --- @param phase Phase
576
+ --- @param fn (U...) -> boolean
577
+ ---
578
+ --- Adds a Run Condition which the Scheduler will check before
579
+ --- any Systems tagged with this Phase are ran.
580
+
581
+ --- @method setRunCondition
582
+ --- @within Scheduler
583
+ --- @param pipeline Pipeline
584
+ --- @param fn (U...) -> boolean
585
+ ---
586
+ --- Adds a Run Condition which the Scheduler will check before
587
+ --- any Systems within any Phases apart of this Pipeline are ran.\
588
+ --- \
589
+ --- This Run Condition will be applied to the Phases themselves.
590
+
591
+ function Scheduler:setRunCondition(dependent, fn)
592
+ local system = getSystem(dependent)
593
+ if system then
594
+ dependent = system
595
+ end
596
+
597
+ assert(
598
+ system or isPhase(dependent) or isPipeline(dependent),
599
+ "Attempt to pass unknown dependent into Scheduler:setRunCondition(unknown, _)"
600
+ )
601
+
602
+ self._runIfConditions[dependent] = fn
603
+ return self
604
+ end
605
+
606
+ function Scheduler:_addBuiltins()
607
+ local i = 0
608
+
609
+ for _, phase in Pipeline.Startup._phases do
610
+ i += 1
611
+ self:_insertPhaseAt(phase, i)
612
+ end
613
+
614
+ for _, phase in Pipeline.Main._phases do
615
+ i += 1
616
+ self:_insertPhaseAt(phase, i, RunService, "Heartbeat")
617
+ end
618
+
619
+ local runServiceEvents = {
620
+ "PreRender",
621
+ "PreAnimation",
622
+ "PreSimulation",
623
+ "PostSimulation",
624
+ }
625
+
626
+ for _, event in runServiceEvents do
627
+ i += 1
628
+ self:_insertPhaseAt(Phase[event], i, RunService, event)
629
+ end
630
+
631
+ local startupHasRan = {}
632
+
633
+ self:setRunCondition(Pipeline.Startup, function()
634
+ local phases = Pipeline.Startup._phases
635
+ local hasRan = startupHasRan[phases[#phases] ]
636
+
637
+ if not hasRan then
638
+ startupHasRan[phases[#phases] ] = true
639
+ end
640
+
641
+ return not hasRan
642
+ end)
643
+
644
+ for _, phase in Pipeline.Startup._phases do
645
+ self:setRunCondition(phase, function()
646
+ local hasRan = startupHasRan[phase]
647
+
648
+ if not hasRan then
649
+ startupHasRan[phase] = true
650
+ end
651
+
652
+ return not hasRan
653
+ end)
654
+ end
655
+ end
656
+
657
+ local EVENT_CONNECT_METHODS = { "Connect", "On", "on", "connect" }
658
+
659
+ -- This is a modified function from Matter by evaera (https://github.com/evaera)
660
+ -- License: Copyright (c) 2021 Eryn L. K., MIT License
661
+ -- Source: https://github.com/matter-ecs/matter/blob/main/lib/hooks/useEvent.luau
662
+ function Scheduler:_scheduleEvent(instance, event)
663
+ local identifier = getEventIdentifier(instance, event)
664
+
665
+ local callback = function()
666
+ for _, phase in self._eventToPhases[identifier] do
667
+ self:runPhase(phase)
668
+ end
669
+ end
670
+
671
+ local eventInstance = instance
672
+
673
+ if typeof(event) == "RBXScriptSignal" or type(event) == "table" then
674
+ eventInstance = event
675
+ elseif type(event) == "string" then
676
+ eventInstance = instance[event]
677
+ end
678
+
679
+ if type(eventInstance) == "function" then
680
+ self._connectedEvents[identifier] = eventInstance
681
+
682
+ return eventInstance(eventInstance, callback)
683
+ elseif typeof(eventInstance) == "RBXScriptSignal" then
684
+ self._connectedEvents[identifier] = eventInstance
685
+
686
+ return eventInstance:Connect(callback)
687
+ end
688
+
689
+ if type(eventInstance) == "table" then
690
+ for _, method in EVENT_CONNECT_METHODS do
691
+ if type(eventInstance[method]) ~= "function" then
692
+ continue
693
+ end
694
+
695
+ self._connectedEvents[identifier] = eventInstance
696
+ return eventInstance[method](eventInstance, callback)
697
+ end
698
+ end
699
+
700
+ error(
701
+ "Couldn't connect to event as no valid connect methods were found! Ensure the passed event has a 'Connect' or an 'on' method!"
702
+ )
703
+ end
704
+
705
+ function Scheduler:_schedulePhase(phase, instance, event)
706
+ local identifier = getEventIdentifier(instance, event)
707
+
708
+ if not self._eventToPhases[identifier] then
709
+ self._eventToPhases[identifier] = {}
710
+ end
711
+
712
+ table.insert(self._eventToPhases[identifier], phase)
713
+ if not self._connectedEvents[identifier] then
714
+ self:_scheduleEvent(instance, event)
715
+ end
716
+ end
717
+
718
+ --- @function new
719
+ --- @within Scheduler
720
+ --- @param args U...
721
+ ---
722
+ --- Creates a new Scheduler, the args passed will be passed to
723
+ --- any System anytime it is ran by the Scheduler.
724
+ function Scheduler.new(...)
725
+ local self = {}
726
+
727
+ self._hooks = {}
728
+
729
+ self._vargs = { ... }
730
+
731
+ self._orderedPhases = {}
732
+ self._orderedPipelines = {}
733
+
734
+ self._eventToPhases = {}
735
+ self._connectedEvents = {}
736
+
737
+ self._phaseToSystems = {}
738
+ self._systemInfo = {}
739
+
740
+ self._runIfConditions = {}
741
+
742
+ setmetatable(self, Scheduler)
743
+
744
+ for _, hookName in hooks.Hooks do
745
+ if not self._hooks[hookName] then
746
+ self._hooks[hookName] = {}
747
+ end
748
+ end
749
+
750
+ self:_addBuiltins()
751
+
752
+ return self
753
+ end
754
+
755
+ return Scheduler