@rbxts/planck 0.3.0-alpha.1 → 0.3.0-alpha.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.
- package/package.json +1 -1
- package/src/Phase.d.ts +2 -0
- package/src/Pipeline.d.ts +2 -0
- package/src/Scheduler.luau +1069 -1057
package/src/Scheduler.luau
CHANGED
|
@@ -1,1057 +1,1069 @@
|
|
|
1
|
-
--!nonstrict
|
|
2
|
-
local DependencyGraph = require(script.Parent.DependencyGraph)
|
|
3
|
-
local Pipeline = require(script.Parent.Pipeline)
|
|
4
|
-
local Phase = require(script.Parent.Phase)
|
|
5
|
-
|
|
6
|
-
local utils = require(script.Parent.utils)
|
|
7
|
-
local hooks = require(script.Parent.hooks)
|
|
8
|
-
local conditions = require(script.Parent.conditions)
|
|
9
|
-
|
|
10
|
-
local getSystem = utils.getSystem
|
|
11
|
-
local getSystemName = utils.getSystemName
|
|
12
|
-
|
|
13
|
-
local isPhase = utils.isPhase
|
|
14
|
-
local isPipeline = utils.isPipeline
|
|
15
|
-
|
|
16
|
-
local isValidEvent = utils.isValidEvent
|
|
17
|
-
local getEventIdentifier = utils.getEventIdentifier
|
|
18
|
-
|
|
19
|
-
-- Recent errors in Planks itself
|
|
20
|
-
local recentLogs = {}
|
|
21
|
-
local timeLastLogged = os.clock()
|
|
22
|
-
|
|
23
|
-
--- @type SystemFn ((U...) -> ())
|
|
24
|
-
--- @within Scheduler
|
|
25
|
-
--- Standard system function that runs every time it's scheduled
|
|
26
|
-
|
|
27
|
-
--- @type InitializerSystemFn ((U...) -> (SystemFn<U...> | (SystemFn<U...>, CleanupFn)))
|
|
28
|
-
--- @within Scheduler
|
|
29
|
-
--- Initializer system that returns the runtime function, optionally with cleanup
|
|
30
|
-
|
|
31
|
-
--- @type CleanupFn (() -> ())
|
|
32
|
-
--- @within Scheduler
|
|
33
|
-
--- Cleanup function called when system is removed
|
|
34
|
-
|
|
35
|
-
--- @interface SystemTable
|
|
36
|
-
--- @within Scheduler
|
|
37
|
-
--- .system SystemFn<U...> | InitializerSystemFn<U...>
|
|
38
|
-
--- .phase Phase?
|
|
39
|
-
--- .name string?
|
|
40
|
-
--- .runConditions {RunCondition}?
|
|
41
|
-
--- .[any] any
|
|
42
|
-
|
|
43
|
-
--- @type System SystemFn<U...> | SystemTable<U...>
|
|
44
|
-
--- @within Scheduler
|
|
45
|
-
|
|
46
|
-
--- @class Scheduler
|
|
47
|
-
---
|
|
48
|
-
--- An Object which handles scheduling Systems to run within different
|
|
49
|
-
--- Phases. The order of which Systems run will be defined either
|
|
50
|
-
--- implicitly by when it was added, or explicitly by tagging the system
|
|
51
|
-
--- with a Phase.
|
|
52
|
-
local Scheduler = {}
|
|
53
|
-
Scheduler.__index = Scheduler
|
|
54
|
-
|
|
55
|
-
Scheduler.Hooks = hooks.Hooks
|
|
56
|
-
|
|
57
|
-
--- @method addPlugin
|
|
58
|
-
--- @within Scheduler
|
|
59
|
-
--- @param plugin PlanckPlugin
|
|
60
|
-
---
|
|
61
|
-
--- Initializes a plugin with the scheduler, see the [Plugin Docs](/docs/plugins) for more information.
|
|
62
|
-
function Scheduler:addPlugin(plugin)
|
|
63
|
-
plugin:build(self)
|
|
64
|
-
table.insert(self._plugins, plugin)
|
|
65
|
-
return self
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
function Scheduler:_addHook(hook, fn)
|
|
69
|
-
assert(self._hooks[hook], `Unknown Hook: {hook}`)
|
|
70
|
-
table.insert(self._hooks[hook], fn)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
--- @method getDeltaTime
|
|
74
|
-
--- @within Scheduler
|
|
75
|
-
--- @return number
|
|
76
|
-
---
|
|
77
|
-
--- Returns the time since the system was ran last.
|
|
78
|
-
--- This must be used within a registered system.
|
|
79
|
-
function Scheduler:getDeltaTime()
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if not systemInfo.
|
|
97
|
-
systemInfo.
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
if
|
|
101
|
-
systemInfo.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if
|
|
135
|
-
self
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
if type(errOrSys) == "
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
coroutine.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if
|
|
293
|
-
self
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
local
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
---
|
|
344
|
-
|
|
345
|
-
--- @
|
|
346
|
-
--- @
|
|
347
|
-
---
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
error("
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
---
|
|
384
|
-
---
|
|
385
|
-
---
|
|
386
|
-
---
|
|
387
|
-
---
|
|
388
|
-
---
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
---
|
|
427
|
-
---
|
|
428
|
-
---
|
|
429
|
-
---
|
|
430
|
-
|
|
431
|
-
---
|
|
432
|
-
---
|
|
433
|
-
|
|
434
|
-
--- @
|
|
435
|
-
--- @
|
|
436
|
-
--- @
|
|
437
|
-
---
|
|
438
|
-
---
|
|
439
|
-
---
|
|
440
|
-
---
|
|
441
|
-
---
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
---
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
--- @
|
|
448
|
-
--- @
|
|
449
|
-
---
|
|
450
|
-
---
|
|
451
|
-
---
|
|
452
|
-
---
|
|
453
|
-
---
|
|
454
|
-
---
|
|
455
|
-
---
|
|
456
|
-
---
|
|
457
|
-
---
|
|
458
|
-
|
|
459
|
-
---
|
|
460
|
-
---
|
|
461
|
-
---
|
|
462
|
-
---
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
self.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
--- @
|
|
503
|
-
--- @
|
|
504
|
-
---
|
|
505
|
-
---
|
|
506
|
-
---
|
|
507
|
-
---
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
--- @
|
|
542
|
-
--- @
|
|
543
|
-
---
|
|
544
|
-
---
|
|
545
|
-
---
|
|
546
|
-
---
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
---
|
|
581
|
-
---
|
|
582
|
-
---
|
|
583
|
-
---
|
|
584
|
-
---
|
|
585
|
-
---
|
|
586
|
-
---
|
|
587
|
-
---
|
|
588
|
-
---
|
|
589
|
-
---
|
|
590
|
-
---
|
|
591
|
-
---
|
|
592
|
-
---
|
|
593
|
-
---
|
|
594
|
-
---
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
local
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
self._phaseToSystems[newPhase]
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
systemInfo.phase
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
end
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
---
|
|
742
|
-
---
|
|
743
|
-
---
|
|
744
|
-
---
|
|
745
|
-
---
|
|
746
|
-
---
|
|
747
|
-
---
|
|
748
|
-
---
|
|
749
|
-
---
|
|
750
|
-
---
|
|
751
|
-
---
|
|
752
|
-
---
|
|
753
|
-
---
|
|
754
|
-
---
|
|
755
|
-
---
|
|
756
|
-
---
|
|
757
|
-
---
|
|
758
|
-
function
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
---
|
|
876
|
-
---
|
|
877
|
-
---
|
|
878
|
-
|
|
879
|
-
---
|
|
880
|
-
---
|
|
881
|
-
---
|
|
882
|
-
|
|
883
|
-
---
|
|
884
|
-
---
|
|
885
|
-
---
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
if
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
self
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
---
|
|
993
|
-
---
|
|
994
|
-
---
|
|
995
|
-
---
|
|
996
|
-
---
|
|
997
|
-
---
|
|
998
|
-
---
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
end
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
self
|
|
1041
|
-
|
|
1042
|
-
self.
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
self
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1
|
+
--!nonstrict
|
|
2
|
+
local DependencyGraph = require(script.Parent.DependencyGraph)
|
|
3
|
+
local Pipeline = require(script.Parent.Pipeline)
|
|
4
|
+
local Phase = require(script.Parent.Phase)
|
|
5
|
+
|
|
6
|
+
local utils = require(script.Parent.utils)
|
|
7
|
+
local hooks = require(script.Parent.hooks)
|
|
8
|
+
local conditions = require(script.Parent.conditions)
|
|
9
|
+
|
|
10
|
+
local getSystem = utils.getSystem
|
|
11
|
+
local getSystemName = utils.getSystemName
|
|
12
|
+
|
|
13
|
+
local isPhase = utils.isPhase
|
|
14
|
+
local isPipeline = utils.isPipeline
|
|
15
|
+
|
|
16
|
+
local isValidEvent = utils.isValidEvent
|
|
17
|
+
local getEventIdentifier = utils.getEventIdentifier
|
|
18
|
+
|
|
19
|
+
-- Recent errors in Planks itself
|
|
20
|
+
local recentLogs = {}
|
|
21
|
+
local timeLastLogged = os.clock()
|
|
22
|
+
|
|
23
|
+
--- @type SystemFn ((U...) -> ())
|
|
24
|
+
--- @within Scheduler
|
|
25
|
+
--- Standard system function that runs every time it's scheduled
|
|
26
|
+
|
|
27
|
+
--- @type InitializerSystemFn ((U...) -> (SystemFn<U...> | (SystemFn<U...>, CleanupFn)))
|
|
28
|
+
--- @within Scheduler
|
|
29
|
+
--- Initializer system that returns the runtime function, optionally with cleanup
|
|
30
|
+
|
|
31
|
+
--- @type CleanupFn (() -> ())
|
|
32
|
+
--- @within Scheduler
|
|
33
|
+
--- Cleanup function called when system is removed
|
|
34
|
+
|
|
35
|
+
--- @interface SystemTable
|
|
36
|
+
--- @within Scheduler
|
|
37
|
+
--- .system SystemFn<U...> | InitializerSystemFn<U...>
|
|
38
|
+
--- .phase Phase?
|
|
39
|
+
--- .name string?
|
|
40
|
+
--- .runConditions {RunCondition}?
|
|
41
|
+
--- .[any] any
|
|
42
|
+
|
|
43
|
+
--- @type System SystemFn<U...> | SystemTable<U...>
|
|
44
|
+
--- @within Scheduler
|
|
45
|
+
|
|
46
|
+
--- @class Scheduler
|
|
47
|
+
---
|
|
48
|
+
--- An Object which handles scheduling Systems to run within different
|
|
49
|
+
--- Phases. The order of which Systems run will be defined either
|
|
50
|
+
--- implicitly by when it was added, or explicitly by tagging the system
|
|
51
|
+
--- with a Phase.
|
|
52
|
+
local Scheduler = {}
|
|
53
|
+
Scheduler.__index = Scheduler
|
|
54
|
+
|
|
55
|
+
Scheduler.Hooks = hooks.Hooks
|
|
56
|
+
|
|
57
|
+
--- @method addPlugin
|
|
58
|
+
--- @within Scheduler
|
|
59
|
+
--- @param plugin PlanckPlugin
|
|
60
|
+
---
|
|
61
|
+
--- Initializes a plugin with the scheduler, see the [Plugin Docs](/docs/plugins) for more information.
|
|
62
|
+
function Scheduler:addPlugin(plugin)
|
|
63
|
+
plugin:build(self)
|
|
64
|
+
table.insert(self._plugins, plugin)
|
|
65
|
+
return self
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
function Scheduler:_addHook(hook, fn)
|
|
69
|
+
assert(self._hooks[hook], `Unknown Hook: {hook}`)
|
|
70
|
+
table.insert(self._hooks[hook], fn)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
--- @method getDeltaTime
|
|
74
|
+
--- @within Scheduler
|
|
75
|
+
--- @return number
|
|
76
|
+
---
|
|
77
|
+
--- Returns the time since the system was ran last.
|
|
78
|
+
--- This must be used within a registered system.
|
|
79
|
+
function Scheduler:getDeltaTime()
|
|
80
|
+
if self._currentSystem then
|
|
81
|
+
return self._currentSystem.deltaTime or 0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
local systemFn = debug.info(2, "f")
|
|
85
|
+
if not systemFn or not self._systemInfo[systemFn] then
|
|
86
|
+
error(
|
|
87
|
+
"Scheduler:getDeltaTime() must be used within a registered system"
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
return self._systemInfo[systemFn].deltaTime or 0
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
-- Inspiration from https://github.com/matter-ecs/matter <3
|
|
95
|
+
function Scheduler:_handleLogs(systemInfo)
|
|
96
|
+
if not systemInfo.timeLastLogged then
|
|
97
|
+
systemInfo.timeLastLogged = os.clock()
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if not systemInfo.recentLogs then
|
|
101
|
+
systemInfo.recentLogs = {}
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
if os.clock() - systemInfo.timeLastLogged > 10 then
|
|
105
|
+
systemInfo.timeLastLogged = os.clock()
|
|
106
|
+
systemInfo.recentLogs = {}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
local name = systemInfo.name
|
|
110
|
+
|
|
111
|
+
for _, logMessage in systemInfo.logs do
|
|
112
|
+
if not systemInfo.recentLogs[logMessage] then
|
|
113
|
+
task.spawn(error, logMessage, 0)
|
|
114
|
+
warn(
|
|
115
|
+
`Planck: Error occurred in system{string.len(name) > 0 and ` '{name}'` or ""}, this error will be ignored for 10 seconds`
|
|
116
|
+
)
|
|
117
|
+
systemInfo.recentLogs[logMessage] = true
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
table.clear(systemInfo.logs)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
function Scheduler:runSystem(system, justInitialized)
|
|
125
|
+
local systemInfo = self._systemInfo[system]
|
|
126
|
+
local now = os.clock()
|
|
127
|
+
|
|
128
|
+
if not systemInfo then
|
|
129
|
+
error(
|
|
130
|
+
"Attempted to run a non-registered system, make sure it is added to the Scheduler"
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if justInitialized ~= true then
|
|
135
|
+
if self:_canRun(system) == false then
|
|
136
|
+
return
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
systemInfo.deltaTime = now - (systemInfo.lastTime or now)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
systemInfo.lastTime = now
|
|
143
|
+
self._currentSystem = systemInfo
|
|
144
|
+
|
|
145
|
+
if not self._thread then
|
|
146
|
+
self._thread = coroutine.create(function()
|
|
147
|
+
while true do
|
|
148
|
+
local fn = coroutine.yield()
|
|
149
|
+
self._yielded = true
|
|
150
|
+
fn()
|
|
151
|
+
self._yielded = false
|
|
152
|
+
end
|
|
153
|
+
end)
|
|
154
|
+
|
|
155
|
+
coroutine.resume(self._thread)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
local didYield = false
|
|
159
|
+
local hasSystem = false
|
|
160
|
+
|
|
161
|
+
local function systemCall()
|
|
162
|
+
local function noYield()
|
|
163
|
+
local success, errOrSys, cleanup
|
|
164
|
+
coroutine.resume(self._thread, function()
|
|
165
|
+
success, errOrSys, cleanup = xpcall(function()
|
|
166
|
+
return systemInfo.run(table.unpack(self._vargs))
|
|
167
|
+
end, function(e)
|
|
168
|
+
return debug.traceback(e)
|
|
169
|
+
end)
|
|
170
|
+
end)
|
|
171
|
+
|
|
172
|
+
if success == false then
|
|
173
|
+
didYield = true
|
|
174
|
+
table.insert(systemInfo.logs, errOrSys)
|
|
175
|
+
hooks.systemError(self, systemInfo, errOrSys)
|
|
176
|
+
return
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
if self._yielded then
|
|
180
|
+
didYield = true
|
|
181
|
+
local source, line = debug.info(self._thread, 1, "sl")
|
|
182
|
+
local errMessage = `{source}:{line}: System yielded`
|
|
183
|
+
table.insert(
|
|
184
|
+
systemInfo.logs,
|
|
185
|
+
debug.traceback(self._thread, errMessage, 2)
|
|
186
|
+
)
|
|
187
|
+
hooks.systemError(
|
|
188
|
+
self,
|
|
189
|
+
systemInfo,
|
|
190
|
+
debug.traceback(self._thread, errMessage, 2)
|
|
191
|
+
)
|
|
192
|
+
return
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
if not systemInfo.initialized then
|
|
196
|
+
if errOrSys == nil and cleanup == nil then
|
|
197
|
+
systemInfo.initialized = true
|
|
198
|
+
return
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
if type(errOrSys) == "function" then
|
|
202
|
+
systemInfo.run = errOrSys
|
|
203
|
+
systemInfo.initialized = true
|
|
204
|
+
if type(cleanup) == "function" then
|
|
205
|
+
systemInfo.cleanup = cleanup
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
hasSystem = true
|
|
209
|
+
return
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
if type(errOrSys) == "table" then
|
|
213
|
+
hasSystem = type(errOrSys.system) == "function"
|
|
214
|
+
local hasCleanup = type(errOrSys.cleanup) == "function"
|
|
215
|
+
|
|
216
|
+
if hasSystem or hasCleanup then
|
|
217
|
+
if hasSystem then
|
|
218
|
+
systemInfo.run = errOrSys.system
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
if hasCleanup then
|
|
222
|
+
systemInfo.cleanup = errOrSys.cleanup
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
systemInfo.initialized = true
|
|
226
|
+
return
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
local err = string.format(
|
|
231
|
+
"System '%s' initializer returned invalid type. "
|
|
232
|
+
.. "Expected: function, {system?, cleanup?}, or (function, function). "
|
|
233
|
+
.. "Got: %s, %s",
|
|
234
|
+
systemInfo.name,
|
|
235
|
+
type(errOrSys),
|
|
236
|
+
type(cleanup)
|
|
237
|
+
)
|
|
238
|
+
table.insert(systemInfo.logs, err)
|
|
239
|
+
hooks.systemError(self, systemInfo, err)
|
|
240
|
+
systemInfo.initialized = true
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
hooks.systemCall(self, "SystemCall", systemInfo, noYield)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
local function inner()
|
|
248
|
+
hooks.systemCall(self, "InnerSystemCall", systemInfo, systemCall)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
local function outer()
|
|
252
|
+
hooks.systemCall(self, "OuterSystemCall", systemInfo, inner)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
if os.clock() - timeLastLogged > 10 then
|
|
256
|
+
timeLastLogged = os.clock()
|
|
257
|
+
recentLogs = {}
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
local success, err: string? = pcall(outer)
|
|
261
|
+
if not success and not recentLogs[err] then
|
|
262
|
+
task.spawn(error, err, 0)
|
|
263
|
+
warn(
|
|
264
|
+
`Planck: Error occurred while running hooks, this error will be ignored for 10 seconds`
|
|
265
|
+
)
|
|
266
|
+
hooks.systemError(
|
|
267
|
+
self,
|
|
268
|
+
systemInfo,
|
|
269
|
+
`Error occurred while running hooks: {err}`
|
|
270
|
+
)
|
|
271
|
+
recentLogs[err] = true
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
if didYield then
|
|
275
|
+
coroutine.close(self._thread)
|
|
276
|
+
|
|
277
|
+
self._thread = coroutine.create(function()
|
|
278
|
+
while true do
|
|
279
|
+
local fn = coroutine.yield()
|
|
280
|
+
self._yielded = true
|
|
281
|
+
fn()
|
|
282
|
+
self._yielded = false
|
|
283
|
+
end
|
|
284
|
+
end)
|
|
285
|
+
|
|
286
|
+
coroutine.resume(self._thread)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
self:_handleLogs(systemInfo)
|
|
290
|
+
self._currentSystem = nil
|
|
291
|
+
|
|
292
|
+
if hasSystem and justInitialized ~= true then
|
|
293
|
+
self:runSystem(system, true)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
function Scheduler:runPhase(phase)
|
|
298
|
+
if self:_canRun(phase) == false then
|
|
299
|
+
return
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
hooks.phaseBegan(self, phase)
|
|
303
|
+
|
|
304
|
+
if not self._phaseToSystems[phase] then
|
|
305
|
+
self._phaseToSystems[phase] = {}
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
for _, system in self._phaseToSystems[phase] do
|
|
309
|
+
self:runSystem(system)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
function Scheduler:runPipeline(pipeline)
|
|
314
|
+
if self:_canRun(pipeline) == false then
|
|
315
|
+
return
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
local orderedList = pipeline.dependencyGraph:getOrderedList()
|
|
319
|
+
assert(
|
|
320
|
+
orderedList,
|
|
321
|
+
`Pipeline {pipeline} contains a circular dependency, check it's Phases`
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
for _, phase in orderedList do
|
|
325
|
+
self:runPhase(phase)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
function Scheduler:_canRun(dependent)
|
|
330
|
+
local conditions = self._runIfConditions[dependent]
|
|
331
|
+
|
|
332
|
+
if conditions then
|
|
333
|
+
for _, runIf in conditions do
|
|
334
|
+
if runIf(table.unpack(self._vargs)) == false then
|
|
335
|
+
return false
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
return true
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
--- @method run
|
|
344
|
+
--- @within Scheduler
|
|
345
|
+
--- @param phase Phase
|
|
346
|
+
--- @return Scheduler
|
|
347
|
+
---
|
|
348
|
+
--- Runs all Systems tagged with the Phase in order.
|
|
349
|
+
|
|
350
|
+
--- @method run
|
|
351
|
+
--- @within Scheduler
|
|
352
|
+
--- @param pipeline Pipeline
|
|
353
|
+
--- @return Scheduler
|
|
354
|
+
---
|
|
355
|
+
--- Runs all Systems tagged with any Phase within the Pipeline in order.
|
|
356
|
+
|
|
357
|
+
--- @method run
|
|
358
|
+
--- @within Scheduler
|
|
359
|
+
--- @param system System
|
|
360
|
+
--- @return Scheduler
|
|
361
|
+
---
|
|
362
|
+
--- Runs the System, passing in the arguments of the Scheduler, `U...`.
|
|
363
|
+
function Scheduler:run(dependent)
|
|
364
|
+
if not dependent then
|
|
365
|
+
error("No dependent specified in Scheduler:run(_)")
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
self:runPipeline(Pipeline.Startup)
|
|
369
|
+
|
|
370
|
+
if getSystem(dependent) then
|
|
371
|
+
self:runSystem(dependent)
|
|
372
|
+
elseif isPhase(dependent) then
|
|
373
|
+
self:runPhase(dependent)
|
|
374
|
+
elseif isPipeline(dependent) then
|
|
375
|
+
self:runPipeline(dependent)
|
|
376
|
+
else
|
|
377
|
+
error("Unknown dependent passed into Scheduler:run(unknown)")
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
return self
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
--- @method runAll
|
|
384
|
+
--- @within Scheduler
|
|
385
|
+
--- @return Scheduler
|
|
386
|
+
---
|
|
387
|
+
--- Runs all Systems within order.
|
|
388
|
+
---
|
|
389
|
+
--- :::note
|
|
390
|
+
--- When you add a Pipeline or Phase with an event, it will be grouped
|
|
391
|
+
--- with other Pipelines/Phases on that event. Otherwise, it will be
|
|
392
|
+
--- added to the default group.
|
|
393
|
+
---
|
|
394
|
+
--- When not running systems on Events, such as with the `runAll` method,
|
|
395
|
+
--- the Default group will be ran first, and then each Event Group in the
|
|
396
|
+
--- order created.
|
|
397
|
+
---
|
|
398
|
+
--- Pipelines/Phases in these groups are still ordered by their dependencies
|
|
399
|
+
--- and by the order of insertion.
|
|
400
|
+
--- :::
|
|
401
|
+
function Scheduler:runAll()
|
|
402
|
+
local orderedDefaults = self._defaultDependencyGraph:getOrderedList()
|
|
403
|
+
assert(
|
|
404
|
+
orderedDefaults,
|
|
405
|
+
"Default Group contains a circular dependency, check your Pipelines/Phases"
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
for _, dependency in orderedDefaults do
|
|
409
|
+
self:run(dependency)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
for identifier, dependencyGraph in self._eventDependencyGraphs do
|
|
413
|
+
local orderedList = dependencyGraph:getOrderedList()
|
|
414
|
+
assert(
|
|
415
|
+
orderedDefaults,
|
|
416
|
+
`Event Group '{identifier}' contains a circular dependency, check your Pipelines/Phases`
|
|
417
|
+
)
|
|
418
|
+
for _, dependency in orderedList do
|
|
419
|
+
self:run(dependency)
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
return self
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
--- @method insert
|
|
427
|
+
--- @within Scheduler
|
|
428
|
+
--- @param phase Phase
|
|
429
|
+
--- @return Scheduler
|
|
430
|
+
---
|
|
431
|
+
--- Initializes the Phase within the Scheduler, ordering it implicitly by
|
|
432
|
+
--- setting it as a dependent of the previous Phase/Pipeline.
|
|
433
|
+
|
|
434
|
+
--- @method insert
|
|
435
|
+
--- @within Scheduler
|
|
436
|
+
--- @param pipeline Pipeline
|
|
437
|
+
--- @return Scheduler
|
|
438
|
+
---
|
|
439
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
440
|
+
--- ordering the Pipeline implicitly by setting it as a dependent
|
|
441
|
+
--- of the previous Phase/Pipeline.
|
|
442
|
+
|
|
443
|
+
--- @method insert
|
|
444
|
+
--- @within Scheduler
|
|
445
|
+
--- @param phase Phase
|
|
446
|
+
--- @param instance Instance | EventLike
|
|
447
|
+
--- @param event string | EventLike
|
|
448
|
+
--- @return Scheduler
|
|
449
|
+
---
|
|
450
|
+
--- Initializes the Phase within the Scheduler, ordering it implicitly
|
|
451
|
+
--- by setting it as a dependent of the previous Phase/Pipeline, and
|
|
452
|
+
--- scheduling it to be ran on the specified event.
|
|
453
|
+
---
|
|
454
|
+
--- ```lua
|
|
455
|
+
--- local myScheduler = Scheduler.new()
|
|
456
|
+
--- :insert(myPhase, RunService, "Heartbeat")
|
|
457
|
+
--- ```
|
|
458
|
+
|
|
459
|
+
--- @method insert
|
|
460
|
+
--- @within Scheduler
|
|
461
|
+
--- @param pipeline Pipeline
|
|
462
|
+
--- @param instance Instance | EventLike
|
|
463
|
+
--- @param event string | EventLike
|
|
464
|
+
--- @return Scheduler
|
|
465
|
+
---
|
|
466
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
467
|
+
--- ordering the Pipeline implicitly by setting it as a dependent of
|
|
468
|
+
--- the previous Phase/Pipeline, and scheduling it to be ran on the
|
|
469
|
+
--- specified event.
|
|
470
|
+
---
|
|
471
|
+
--- ```lua
|
|
472
|
+
--- local myScheduler = Scheduler.new()
|
|
473
|
+
--- :insert(myPipeline, RunService, "Heartbeat")
|
|
474
|
+
--- ```
|
|
475
|
+
function Scheduler:insert(dependency, instance, event)
|
|
476
|
+
assert(
|
|
477
|
+
isPhase(dependency) or isPipeline(dependency),
|
|
478
|
+
"Unknown dependency passed to Scheduler:insert(unknown, _, _)"
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
if not instance then
|
|
482
|
+
local dependencyGraph = self._defaultDependencyGraph
|
|
483
|
+
dependencyGraph:insertBefore(dependency, self._defaultPhase)
|
|
484
|
+
else
|
|
485
|
+
assert(
|
|
486
|
+
isValidEvent(instance, event),
|
|
487
|
+
"Unknown instance/event passed to Scheduler:insert(_, instance, event)"
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
local dependencyGraph = self:_getEventDependencyGraph(instance, event)
|
|
491
|
+
dependencyGraph:insert(dependency)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
if isPhase(dependency) then
|
|
495
|
+
self._phaseToSystems[dependency] = {}
|
|
496
|
+
hooks.phaseAdd(self, dependency)
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
return self
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
--- @method insertAfter
|
|
503
|
+
--- @within Scheduler
|
|
504
|
+
--- @param phase Phase
|
|
505
|
+
--- @param after Phase | Pipeline
|
|
506
|
+
--- @return Scheduler
|
|
507
|
+
---
|
|
508
|
+
--- Initializes the Phase within the Scheduler, ordering it
|
|
509
|
+
--- explicitly by setting the after Phase/Pipeline as a dependent.
|
|
510
|
+
|
|
511
|
+
--- @method insertAfter
|
|
512
|
+
--- @within Scheduler
|
|
513
|
+
--- @param pipeline Pipeline
|
|
514
|
+
--- @param after Phase | Pipeline
|
|
515
|
+
--- @return Scheduler
|
|
516
|
+
---
|
|
517
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
518
|
+
--- ordering the Pipeline explicitly by setting the after Phase/Pipeline
|
|
519
|
+
--- as a dependent.
|
|
520
|
+
function Scheduler:insertAfter(dependent, after)
|
|
521
|
+
assert(
|
|
522
|
+
isPhase(after) or isPipeline(after),
|
|
523
|
+
"Unknown dependency passed in Scheduler:insertAfter(_, unknown)"
|
|
524
|
+
)
|
|
525
|
+
assert(
|
|
526
|
+
isPhase(dependent) or isPipeline(dependent),
|
|
527
|
+
"Unknown dependent passed in Scheduler:insertAfter(unknown, _)"
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
local dependencyGraph = self:_getGraphOfDependency(after)
|
|
531
|
+
dependencyGraph:insertAfter(dependent, after)
|
|
532
|
+
|
|
533
|
+
if isPhase(dependent) then
|
|
534
|
+
self._phaseToSystems[dependent] = {}
|
|
535
|
+
hooks.phaseAdd(self, dependent)
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
return self
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
--- @method insertBefore
|
|
542
|
+
--- @within Scheduler
|
|
543
|
+
--- @param phase Phase
|
|
544
|
+
--- @param before Phase | Pipeline
|
|
545
|
+
--- @return Scheduler
|
|
546
|
+
---
|
|
547
|
+
--- Initializes the Phase within the Scheduler, ordering it
|
|
548
|
+
--- explicitly by setting the before Phase/Pipeline as a dependency.
|
|
549
|
+
|
|
550
|
+
--- @method insertBefore
|
|
551
|
+
--- @within Scheduler
|
|
552
|
+
--- @param pipeline Pipeline
|
|
553
|
+
--- @param before Phase | Pipeline
|
|
554
|
+
--- @return Scheduler
|
|
555
|
+
---
|
|
556
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
557
|
+
--- ordering the Pipeline explicitly by setting the before Phase/Pipeline
|
|
558
|
+
--- as a dependency.
|
|
559
|
+
function Scheduler:insertBefore(dependent, before)
|
|
560
|
+
assert(
|
|
561
|
+
isPhase(before) or isPipeline(before),
|
|
562
|
+
"Unknown dependency passed in Scheduler:insertBefore(_, unknown)"
|
|
563
|
+
)
|
|
564
|
+
assert(
|
|
565
|
+
isPhase(dependent) or isPipeline(dependent),
|
|
566
|
+
"Unknown dependent passed in Scheduler:insertBefore(unknown, _)"
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
local dependencyGraph = self:_getGraphOfDependency(before)
|
|
570
|
+
dependencyGraph:insertBefore(dependent, before)
|
|
571
|
+
|
|
572
|
+
if isPhase(dependent) then
|
|
573
|
+
self._phaseToSystems[dependent] = {}
|
|
574
|
+
hooks.phaseAdd(self, dependent)
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
return self
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
--- @method addSystem
|
|
581
|
+
--- @within Scheduler
|
|
582
|
+
--- @param system System
|
|
583
|
+
--- @param phase Phase?
|
|
584
|
+
--- @return Scheduler
|
|
585
|
+
---
|
|
586
|
+
--- Adds the System to the Scheduler, scheduling it to be ran
|
|
587
|
+
--- implicitly within the provided Phase or on the default Main phase.
|
|
588
|
+
---
|
|
589
|
+
--- **Initializer Systems**: Systems can optionally return a function on their
|
|
590
|
+
--- first execution, which becomes the runtime system. This allows one-time
|
|
591
|
+
--- setup logic without creating separate initialization phases.
|
|
592
|
+
---
|
|
593
|
+
--- ```lua
|
|
594
|
+
--- local function renderSystem(world, state)
|
|
595
|
+
--- -- This runs once on first execution
|
|
596
|
+
--- local renderables = world:query(Transform, Model):cached()
|
|
597
|
+
---
|
|
598
|
+
--- -- This runs on each subsequent execution
|
|
599
|
+
--- return function(world, state)
|
|
600
|
+
--- for id, transform, model in renderables do
|
|
601
|
+
--- render(transform, model)
|
|
602
|
+
--- end
|
|
603
|
+
--- end, function()
|
|
604
|
+
--- -- Optional cleanup logic runs on removeSystem
|
|
605
|
+
--- end
|
|
606
|
+
--- end
|
|
607
|
+
--- ```
|
|
608
|
+
function Scheduler:addSystem(system, phase)
|
|
609
|
+
local systemFn = getSystem(system)
|
|
610
|
+
|
|
611
|
+
if not systemFn then
|
|
612
|
+
error("Unknown system passed to Scheduler:addSystem(unknown, phase?)")
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
local name = getSystemName(system)
|
|
616
|
+
|
|
617
|
+
local scheduledPhase
|
|
618
|
+
if phase then
|
|
619
|
+
scheduledPhase = phase
|
|
620
|
+
elseif type(system) == "table" and system.phase then
|
|
621
|
+
scheduledPhase = system.phase
|
|
622
|
+
else
|
|
623
|
+
scheduledPhase = self._defaultPhase
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
local systemInfo = {
|
|
627
|
+
system = systemFn,
|
|
628
|
+
run = systemFn,
|
|
629
|
+
cleanup = nil,
|
|
630
|
+
phase = scheduledPhase,
|
|
631
|
+
name = name,
|
|
632
|
+
logs = {},
|
|
633
|
+
initialized = false,
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
self._systemInfo[systemFn] = systemInfo
|
|
637
|
+
|
|
638
|
+
if not self._phaseToSystems[systemInfo.phase] then
|
|
639
|
+
self._phaseToSystems[systemInfo.phase] = {}
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
table.insert(self._phaseToSystems[systemInfo.phase], systemFn)
|
|
643
|
+
|
|
644
|
+
hooks.systemAdd(self, systemInfo)
|
|
645
|
+
|
|
646
|
+
if type(system) == "table" and system.runConditions then
|
|
647
|
+
for _, condition in system.runConditions do
|
|
648
|
+
condition = if typeof(condition) == "table"
|
|
649
|
+
then condition[1]
|
|
650
|
+
else condition
|
|
651
|
+
self:addRunCondition(systemFn, condition)
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
return self
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
--- @method addSystems
|
|
659
|
+
--- @within Scheduler
|
|
660
|
+
--- @param systems { System }
|
|
661
|
+
--- @param phase Phase?
|
|
662
|
+
---
|
|
663
|
+
--- Adds the Systems to the Scheduler, scheduling them to be ran
|
|
664
|
+
--- implicitly within the provided Phase or on the default Main phase.
|
|
665
|
+
function Scheduler:addSystems(systems, phase)
|
|
666
|
+
if type(systems) ~= "table" then
|
|
667
|
+
error("Unknown systems passed to Scheduler:addSystems(unknown, phase?)")
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
local foundSystem = false
|
|
671
|
+
local n = 0
|
|
672
|
+
|
|
673
|
+
for _, system in systems do
|
|
674
|
+
n += 1
|
|
675
|
+
if getSystem(system) then
|
|
676
|
+
foundSystem = true
|
|
677
|
+
self:addSystem(system, phase)
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
if n == 0 then
|
|
682
|
+
error("Empty table passed to Scheduler:addSystems({ }, phase?)")
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
if not foundSystem then
|
|
686
|
+
error(
|
|
687
|
+
"Unknown table passed to Scheduler:addSystems({ unknown }, phase?)"
|
|
688
|
+
)
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
return self
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
--- @method editSystem
|
|
695
|
+
--- @within Scheduler
|
|
696
|
+
--- @param system System
|
|
697
|
+
--- @param newPhase Phase
|
|
698
|
+
---
|
|
699
|
+
--- Changes the Phase that this system is scheduled on.
|
|
700
|
+
function Scheduler:editSystem(system, newPhase)
|
|
701
|
+
local systemFn = getSystem(system)
|
|
702
|
+
local systemInfo = self._systemInfo[systemFn]
|
|
703
|
+
assert(
|
|
704
|
+
systemInfo,
|
|
705
|
+
"Attempt to edit a non-existent system in Scheduler:editSystem(_)"
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
assert(
|
|
709
|
+
newPhase and self._phaseToSystems[newPhase] ~= nil or true,
|
|
710
|
+
"Phase never initialized before using Scheduler:editSystem(_, Phase)"
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
local systems = self._phaseToSystems[systemInfo.phase]
|
|
714
|
+
|
|
715
|
+
local index = table.find(systems, systemFn)
|
|
716
|
+
assert(index, "Unable to find system within phase")
|
|
717
|
+
|
|
718
|
+
table.remove(systems, index)
|
|
719
|
+
|
|
720
|
+
if not self._phaseToSystems[newPhase] then
|
|
721
|
+
self._phaseToSystems[newPhase] = {}
|
|
722
|
+
end
|
|
723
|
+
table.insert(self._phaseToSystems[newPhase], systemFn)
|
|
724
|
+
|
|
725
|
+
systemInfo.phase = newPhase
|
|
726
|
+
return self
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
function Scheduler:_removeCondition(dependent, condition)
|
|
730
|
+
self._runIfConditions[dependent] = nil
|
|
731
|
+
|
|
732
|
+
for _, _conditions in self._runIfConditions do
|
|
733
|
+
if table.find(_conditions, condition) then
|
|
734
|
+
return
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
conditions.cleanupCondition(condition)
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
--- @method removeSystem
|
|
742
|
+
--- @within Scheduler
|
|
743
|
+
--- @param system System
|
|
744
|
+
--- @return Scheduler
|
|
745
|
+
---
|
|
746
|
+
--- Removes the System from the Scheduler.
|
|
747
|
+
---
|
|
748
|
+
--- If the system provided a cleanup function during initialization,
|
|
749
|
+
--- that cleanup function will be executed before removal.
|
|
750
|
+
---
|
|
751
|
+
--- ```lua
|
|
752
|
+
--- -- System with cleanup
|
|
753
|
+
--- local function networkSystem(world, state)
|
|
754
|
+
--- local connection = Players.PlayerAdded:Connect(function(player)
|
|
755
|
+
--- -- Player joined logic
|
|
756
|
+
--- end)
|
|
757
|
+
---
|
|
758
|
+
--- return function(world, state)
|
|
759
|
+
--- -- Runtime logic
|
|
760
|
+
--- end, function()
|
|
761
|
+
--- -- Cleanup runs on removeSystem
|
|
762
|
+
--- connection:Disconnect()
|
|
763
|
+
--- end
|
|
764
|
+
--- end
|
|
765
|
+
---
|
|
766
|
+
--- scheduler:addSystem(networkSystem, Phase.Update)
|
|
767
|
+
--- -- Later...
|
|
768
|
+
--- scheduler:removeSystem(networkSystem) -- Cleanup executes here
|
|
769
|
+
--- ```
|
|
770
|
+
function Scheduler:removeSystem(system)
|
|
771
|
+
local systemFn = getSystem(system)
|
|
772
|
+
local systemInfo = self._systemInfo[systemFn]
|
|
773
|
+
assert(
|
|
774
|
+
systemInfo,
|
|
775
|
+
"Attempt to remove a non-existent system in Scheduler:removeSystem(_)"
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
if systemInfo.cleanup then
|
|
779
|
+
local success, err =
|
|
780
|
+
pcall(systemInfo.cleanup, table.unpack(self._vargs))
|
|
781
|
+
if success then
|
|
782
|
+
hooks.systemCleanup(self, systemInfo, nil)
|
|
783
|
+
else
|
|
784
|
+
local errMsg = string.format(
|
|
785
|
+
"Cleanup failed for system '%s': %s",
|
|
786
|
+
systemInfo.name,
|
|
787
|
+
tostring(err)
|
|
788
|
+
)
|
|
789
|
+
hooks.systemError(self, systemInfo, errMsg)
|
|
790
|
+
hooks.systemCleanup(self, systemInfo, errMsg)
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
local systems = self._phaseToSystems[systemInfo.phase]
|
|
795
|
+
|
|
796
|
+
local index = table.find(systems, systemFn)
|
|
797
|
+
assert(index, "Unable to find system within phase")
|
|
798
|
+
|
|
799
|
+
table.remove(systems, index)
|
|
800
|
+
self._systemInfo[systemFn] = nil
|
|
801
|
+
|
|
802
|
+
if self._runIfConditions[system] then
|
|
803
|
+
for _, condition in self._runIfConditions[system] do
|
|
804
|
+
self:_removeCondition(system, condition)
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
self._runIfConditions[system] = nil
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
hooks.systemRemove(self, systemInfo)
|
|
811
|
+
|
|
812
|
+
return self
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
--- @method replaceSystem
|
|
816
|
+
--- @within Scheduler
|
|
817
|
+
--- @param old System
|
|
818
|
+
--- @param new System
|
|
819
|
+
---
|
|
820
|
+
--- Replaces the System with a new System.
|
|
821
|
+
function Scheduler:replaceSystem(old, new)
|
|
822
|
+
local oldSystemFn = getSystem(old)
|
|
823
|
+
local oldSystemInfo = self._systemInfo[oldSystemFn]
|
|
824
|
+
assert(
|
|
825
|
+
oldSystemInfo,
|
|
826
|
+
"Attempt to replace a non-existent system in Scheduler:replaceSystem(unknown, _)"
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
local newSystemFn = getSystem(new)
|
|
830
|
+
assert(
|
|
831
|
+
newSystemFn,
|
|
832
|
+
"Attempt to pass non-system in Scheduler:replaceSystem(_, unknown)"
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
if oldSystemInfo.cleanup then
|
|
836
|
+
local success, err =
|
|
837
|
+
pcall(oldSystemInfo.cleanup, table.unpack(self._vargs))
|
|
838
|
+
if success then
|
|
839
|
+
hooks.systemCleanup(self, oldSystemInfo, nil)
|
|
840
|
+
else
|
|
841
|
+
local errMsg = string.format(
|
|
842
|
+
"Cleanup failed for system '%s': %s",
|
|
843
|
+
oldSystemInfo.name,
|
|
844
|
+
tostring(err)
|
|
845
|
+
)
|
|
846
|
+
hooks.systemError(self, oldSystemInfo, errMsg)
|
|
847
|
+
hooks.systemCleanup(self, oldSystemInfo, errMsg)
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
local systems = self._phaseToSystems[oldSystemInfo.phase]
|
|
852
|
+
|
|
853
|
+
local index = table.find(systems, oldSystemFn)
|
|
854
|
+
assert(index, "Unable to find system within phase")
|
|
855
|
+
|
|
856
|
+
table.remove(systems, index)
|
|
857
|
+
table.insert(systems, index, newSystemFn)
|
|
858
|
+
|
|
859
|
+
local copy = table.clone(oldSystemInfo)
|
|
860
|
+
|
|
861
|
+
oldSystemInfo.system = newSystemFn
|
|
862
|
+
oldSystemInfo.run = newSystemFn
|
|
863
|
+
oldSystemInfo.cleanup = nil
|
|
864
|
+
oldSystemInfo.initialized = false
|
|
865
|
+
oldSystemInfo.name = getSystemName(new)
|
|
866
|
+
|
|
867
|
+
hooks.systemReplace(self, copy, oldSystemInfo)
|
|
868
|
+
|
|
869
|
+
self._systemInfo[newSystemFn] = self._systemInfo[oldSystemFn]
|
|
870
|
+
self._systemInfo[oldSystemFn] = nil
|
|
871
|
+
|
|
872
|
+
return self
|
|
873
|
+
end
|
|
874
|
+
|
|
875
|
+
--- @method addRunCondition
|
|
876
|
+
--- @within Scheduler
|
|
877
|
+
--- @param system System
|
|
878
|
+
--- @param fn (U...) -> boolean
|
|
879
|
+
---
|
|
880
|
+
--- Adds a Run Condition which the Scheduler will check before
|
|
881
|
+
--- this System is ran.
|
|
882
|
+
|
|
883
|
+
--- @method addRunCondition
|
|
884
|
+
--- @within Scheduler
|
|
885
|
+
--- @param phase Phase
|
|
886
|
+
--- @param fn (U...) -> boolean
|
|
887
|
+
---
|
|
888
|
+
--- Adds a Run Condition which the Scheduler will check before
|
|
889
|
+
--- any Systems within this Phase are ran.
|
|
890
|
+
|
|
891
|
+
--- @method addRunCondition
|
|
892
|
+
--- @within Scheduler
|
|
893
|
+
--- @param pipeline Pipeline
|
|
894
|
+
--- @param fn (U...) -> boolean
|
|
895
|
+
---
|
|
896
|
+
--- Adds a Run Condition which the Scheduler will check before
|
|
897
|
+
--- any Systems within any Phases apart of this Pipeline are ran.
|
|
898
|
+
function Scheduler:addRunCondition(dependent, fn)
|
|
899
|
+
fn = if typeof(fn) == "table" then fn[1] else fn
|
|
900
|
+
|
|
901
|
+
local system = getSystem(dependent)
|
|
902
|
+
if system then
|
|
903
|
+
dependent = system
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
assert(
|
|
907
|
+
system or isPhase(dependent) or isPipeline(dependent),
|
|
908
|
+
"Attempt to pass unknown dependent into Scheduler:addRunCondition(unknown, _)"
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
if not self._runIfConditions[dependent] then
|
|
912
|
+
self._runIfConditions[dependent] = {}
|
|
913
|
+
end
|
|
914
|
+
|
|
915
|
+
table.insert(self._runIfConditions[dependent], fn)
|
|
916
|
+
|
|
917
|
+
return self
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
function Scheduler:_addBuiltins()
|
|
921
|
+
self._defaultPhase = Phase.new("Default")
|
|
922
|
+
self._defaultDependencyGraph = DependencyGraph.new()
|
|
923
|
+
|
|
924
|
+
self._defaultDependencyGraph:insert(Pipeline.Startup)
|
|
925
|
+
self._defaultDependencyGraph:insert(self._defaultPhase)
|
|
926
|
+
|
|
927
|
+
self:addRunCondition(Pipeline.Startup, conditions.runOnce())
|
|
928
|
+
for _, phase in Pipeline.Startup.dependencyGraph.nodes do
|
|
929
|
+
self:addRunCondition(phase, conditions.runOnce())
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
function Scheduler:_scheduleEvent(instance, event)
|
|
934
|
+
local connect = utils.getConnectFunction(instance, event)
|
|
935
|
+
assert(
|
|
936
|
+
connect,
|
|
937
|
+
"Couldn't connect to event as no valid connect methods were found! Ensure the passed event has a 'Connect' or an 'on' method!"
|
|
938
|
+
)
|
|
939
|
+
|
|
940
|
+
local identifier = getEventIdentifier(instance, event)
|
|
941
|
+
|
|
942
|
+
local dependencyGraph = DependencyGraph.new()
|
|
943
|
+
|
|
944
|
+
local callback = function()
|
|
945
|
+
local orderedList = dependencyGraph:getOrderedList()
|
|
946
|
+
|
|
947
|
+
if orderedList == nil then
|
|
948
|
+
local err =
|
|
949
|
+
`Event Group '{identifier}' contains a circular dependency, check your Pipelines/Phases`
|
|
950
|
+
if not recentLogs[err] then
|
|
951
|
+
task.spawn(error, err, 0)
|
|
952
|
+
warn(
|
|
953
|
+
`Planck: Error occurred while running event, this error will be ignored for 10 seconds`
|
|
954
|
+
)
|
|
955
|
+
recentLogs[err] = true
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
for _, dependency in orderedList do
|
|
960
|
+
self:run(dependency)
|
|
961
|
+
end
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
self._connectedEvents[identifier] = connect(callback)
|
|
965
|
+
self._eventDependencyGraphs[identifier] = dependencyGraph
|
|
966
|
+
end
|
|
967
|
+
|
|
968
|
+
function Scheduler:_getEventDependencyGraph(instance, event)
|
|
969
|
+
local identifier = getEventIdentifier(instance, event)
|
|
970
|
+
|
|
971
|
+
if not self._connectedEvents[identifier] then
|
|
972
|
+
self:_scheduleEvent(instance, event)
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
return self._eventDependencyGraphs[identifier]
|
|
976
|
+
end
|
|
977
|
+
|
|
978
|
+
function Scheduler:_getGraphOfDependency(dependency)
|
|
979
|
+
if table.find(self._defaultDependencyGraph.nodes, dependency) then
|
|
980
|
+
return self._defaultDependencyGraph
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
for _, dependencyGraph in self._eventDependencyGraphs do
|
|
984
|
+
if table.find(dependencyGraph.nodes, dependency) then
|
|
985
|
+
return dependencyGraph
|
|
986
|
+
end
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
error("Dependency does not belong to a DependencyGraph")
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
--- @within Scheduler
|
|
993
|
+
---
|
|
994
|
+
--- Disconnects all events, closes all threads, and performs
|
|
995
|
+
--- other cleanup work.
|
|
996
|
+
---
|
|
997
|
+
--- :::danger
|
|
998
|
+
--- Only use this if you intend to not use the associated
|
|
999
|
+
--- Scheduler anymore. It will not work as intended.
|
|
1000
|
+
---
|
|
1001
|
+
--- You should dereference the scheduler object so that
|
|
1002
|
+
--- it may be garbage collected.
|
|
1003
|
+
--- :::
|
|
1004
|
+
---
|
|
1005
|
+
--- :::warning
|
|
1006
|
+
--- If you're creating a "throwaway" scheduler, you should
|
|
1007
|
+
--- not add plugins like Jabby or the Matter Debugger to it.
|
|
1008
|
+
--- These plugins are unable to properly be cleaned up, use
|
|
1009
|
+
--- them with caution.
|
|
1010
|
+
--- :::
|
|
1011
|
+
function Scheduler:cleanup()
|
|
1012
|
+
for _, connection in self._connectedEvents do
|
|
1013
|
+
utils.disconnectEvent(connection)
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
for _, plugin in self._plugins do
|
|
1017
|
+
if plugin.cleanup then
|
|
1018
|
+
plugin:cleanup()
|
|
1019
|
+
end
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
if self._thread then
|
|
1023
|
+
coroutine.close(self._thread)
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
for _, _conditions in self._runIfConditions do
|
|
1027
|
+
for _, condition in _conditions do
|
|
1028
|
+
conditions.cleanupCondition(condition)
|
|
1029
|
+
end
|
|
1030
|
+
end
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
--- @function new
|
|
1034
|
+
--- @within Scheduler
|
|
1035
|
+
--- @param args U...
|
|
1036
|
+
---
|
|
1037
|
+
--- Creates a new Scheduler, the args passed will be passed to
|
|
1038
|
+
--- any System anytime it is ran by the Scheduler.
|
|
1039
|
+
function Scheduler.new(...)
|
|
1040
|
+
local self = {}
|
|
1041
|
+
|
|
1042
|
+
self._hooks = {}
|
|
1043
|
+
|
|
1044
|
+
self._vargs = { ... }
|
|
1045
|
+
|
|
1046
|
+
self._eventDependencyGraphs = {}
|
|
1047
|
+
self._connectedEvents = {}
|
|
1048
|
+
|
|
1049
|
+
self._phaseToSystems = {}
|
|
1050
|
+
self._systemInfo = {}
|
|
1051
|
+
|
|
1052
|
+
self._runIfConditions = {}
|
|
1053
|
+
|
|
1054
|
+
self._plugins = {}
|
|
1055
|
+
|
|
1056
|
+
setmetatable(self, Scheduler)
|
|
1057
|
+
|
|
1058
|
+
for _, hookName in hooks.Hooks do
|
|
1059
|
+
if not self._hooks[hookName] then
|
|
1060
|
+
self._hooks[hookName] = {}
|
|
1061
|
+
end
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
self:_addBuiltins()
|
|
1065
|
+
|
|
1066
|
+
return self
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
return Scheduler
|