@rbxts/planck 0.3.0-alpha.1 → 0.3.0-rc.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.
- package/package.json +1 -1
- package/src/DependencyGraph.luau +45 -52
- package/src/Phase.d.ts +2 -0
- package/src/Phase.luau +46 -41
- package/src/Pipeline.d.ts +2 -0
- package/src/Pipeline.luau +105 -86
- package/src/Scheduler.d.ts +96 -3
- package/src/Scheduler.luau +1557 -1057
- package/src/__tests__/systems.test.luau +196 -192
- package/src/conditions.d.ts +4 -1
- package/src/conditions.luau +23 -21
- package/src/init.luau +36 -207
- package/src/utils.luau +85 -30
- package/src/hooks.luau +0 -163
package/src/Scheduler.luau
CHANGED
|
@@ -1,1057 +1,1557 @@
|
|
|
1
|
-
|
|
2
|
-
local
|
|
3
|
-
local
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
local
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
local
|
|
14
|
-
local
|
|
15
|
-
|
|
16
|
-
local
|
|
17
|
-
local
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
self
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
end
|
|
678
|
-
|
|
679
|
-
return
|
|
680
|
-
end
|
|
681
|
-
|
|
682
|
-
--- @method
|
|
683
|
-
--- @within Scheduler
|
|
684
|
-
--- @param
|
|
685
|
-
--- @
|
|
686
|
-
---
|
|
687
|
-
---
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
assert(
|
|
924
|
-
|
|
925
|
-
"
|
|
926
|
-
)
|
|
927
|
-
|
|
928
|
-
local
|
|
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
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1
|
+
local DependencyGraph = require(script.Parent.DependencyGraph)
|
|
2
|
+
local Pipeline = require(script.Parent.Pipeline)
|
|
3
|
+
local Phase = require(script.Parent.Phase)
|
|
4
|
+
|
|
5
|
+
type DependencyGraph<T> = DependencyGraph.DependencyGraph<T>
|
|
6
|
+
type Pipeline = Pipeline.Pipeline
|
|
7
|
+
type Phase = Phase.Phase
|
|
8
|
+
|
|
9
|
+
local utils = require(script.Parent.utils)
|
|
10
|
+
local conditions = require(script.Parent.conditions)
|
|
11
|
+
|
|
12
|
+
local getSystem = utils.getSystem
|
|
13
|
+
local getSystemName = utils.getSystemName
|
|
14
|
+
local getEventIdentifier = utils.getEventIdentifier
|
|
15
|
+
|
|
16
|
+
local isSystem = utils.isSystem
|
|
17
|
+
local isPhase = utils.isPhase
|
|
18
|
+
local isPipeline = utils.isPipeline
|
|
19
|
+
local isValidEvent = utils.isValidEvent
|
|
20
|
+
|
|
21
|
+
type GenericTable = utils.GenericTable
|
|
22
|
+
type SignalLike<U...> = utils.SignalLike<U...>
|
|
23
|
+
type ConnectionLike = utils.ConnectionLike
|
|
24
|
+
type ConnectFn<T, U...> = utils.ConnectFn<T, U...>
|
|
25
|
+
type Callback<U...> = utils.Callback<U...>
|
|
26
|
+
|
|
27
|
+
export type SystemFn<U...> = utils.SystemFn<U...>
|
|
28
|
+
export type InitializerSystemFn<U...> = utils.InitializerSystemFn<U...>
|
|
29
|
+
export type InitializerResult<U...> = utils.InitializerResult<U...>
|
|
30
|
+
|
|
31
|
+
type InternalSystem<U...> = utils.InternalSystem<U...>
|
|
32
|
+
export type System<U...> = utils.System<U...>
|
|
33
|
+
export type SystemTable<U...> = utils.SystemTable<U...>
|
|
34
|
+
|
|
35
|
+
type Condition<U...> = conditions.Condition<U...>
|
|
36
|
+
|
|
37
|
+
type Vec<T> = { T }
|
|
38
|
+
type Map<K, V> = { [K]: V }
|
|
39
|
+
|
|
40
|
+
type BasePlugin<T> = {
|
|
41
|
+
build: (self: T, scheduler: Scheduler<...unknown>) -> (),
|
|
42
|
+
new: (...any) -> T,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type PluginImpl = {
|
|
46
|
+
__index: PluginImpl,
|
|
47
|
+
build: (self: Plugin, scheduler: Scheduler<...unknown>) -> (),
|
|
48
|
+
cleanup: ((self: Plugin) -> ())?,
|
|
49
|
+
new: (...any) -> Plugin,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type Plugin = setmetatable<{}, PluginImpl>
|
|
53
|
+
|
|
54
|
+
type SystemLog<E> = {
|
|
55
|
+
-- An Error object or message
|
|
56
|
+
e: E,
|
|
57
|
+
-- The string that will be printed
|
|
58
|
+
log: string,
|
|
59
|
+
-- The traceback as reported by the Scheduler
|
|
60
|
+
trace: string,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type SystemInfo<U...> = {
|
|
64
|
+
system: InternalSystem<U...>,
|
|
65
|
+
run: InternalSystem<U...>,
|
|
66
|
+
cleanup: SystemFn<U...>?,
|
|
67
|
+
initialized: boolean,
|
|
68
|
+
name: string,
|
|
69
|
+
deltaTime: number?,
|
|
70
|
+
lastTime: number?,
|
|
71
|
+
timeLastLogged: number?,
|
|
72
|
+
recentLogs: { [string]: boolean }?,
|
|
73
|
+
logs: { SystemLog<unknown> },
|
|
74
|
+
phase: Phase,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type Dependent<U...> = Pipeline | Phase | System<U...>
|
|
78
|
+
type Dependency = Pipeline | Phase
|
|
79
|
+
|
|
80
|
+
-- Recent errors in Planks itself
|
|
81
|
+
local recentLogs = {}
|
|
82
|
+
local timeLastLogged = os.clock()
|
|
83
|
+
|
|
84
|
+
--- @class Scheduler
|
|
85
|
+
---
|
|
86
|
+
--- An Object which handles scheduling Systems to run within different
|
|
87
|
+
--- Phases. The order of which Systems run will be defined either
|
|
88
|
+
--- implicitly by when it was added, or explicitly by tagging the system
|
|
89
|
+
--- with a Phase.
|
|
90
|
+
local Scheduler = {}
|
|
91
|
+
Scheduler.__index = Scheduler
|
|
92
|
+
|
|
93
|
+
--- @method addPlugin
|
|
94
|
+
--- @within Scheduler
|
|
95
|
+
--- @param plugin PlanckPlugin
|
|
96
|
+
---
|
|
97
|
+
--- Initializes a plugin with the scheduler, see the [Plugin Docs](/docs/plugins) for more information.
|
|
98
|
+
function Scheduler.addPlugin<T, U...>(
|
|
99
|
+
self: Scheduler<U...>,
|
|
100
|
+
plugin: setmetatable<{}, BasePlugin<T>>
|
|
101
|
+
)
|
|
102
|
+
local _plugin = plugin :: Plugin
|
|
103
|
+
_plugin:build(self)
|
|
104
|
+
|
|
105
|
+
table.insert(self._plugins, _plugin)
|
|
106
|
+
return self
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
type PartialHookContext = {
|
|
110
|
+
scheduler: Scheduler<...unknown>,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
type Hook<T> = {
|
|
114
|
+
__T: T,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
type Hooks = {
|
|
118
|
+
SystemAdd: Hook<(context: SystemHookContext) -> ()>,
|
|
119
|
+
SystemRemove: Hook<(context: SystemHookContext) -> ()>,
|
|
120
|
+
SystemReplace: Hook<(context: SystemReplaceContext) -> ()>,
|
|
121
|
+
SystemEdited: Hook<(context: SystemEditedContext) -> ()>,
|
|
122
|
+
SystemCleanup: Hook<(context: SystemErrorContext) -> ()>,
|
|
123
|
+
SystemError: Hook<(context: SystemErrorContext) -> ()>,
|
|
124
|
+
SystemTriedRun: Hook<(context: SystemHookContext) -> ()>,
|
|
125
|
+
|
|
126
|
+
OuterSystemCall: Hook<(context: SystemCallContext) -> SystemCallHookFn>,
|
|
127
|
+
InnerSystemCall: Hook<(context: SystemCallContext) -> SystemCallHookFn>,
|
|
128
|
+
SystemCall: Hook<(context: SystemCallContext) -> SystemCallHookFn>,
|
|
129
|
+
|
|
130
|
+
PhaseAdd: Hook<(context: PhaseContext) -> ()>,
|
|
131
|
+
PhaseBegan: Hook<(context: PhaseContext) -> ()>,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
--- @prop Hooks { [string]: string }
|
|
135
|
+
--- @within Scheduler
|
|
136
|
+
--- @since 0.3.0
|
|
137
|
+
---
|
|
138
|
+
--- See [creating plugins](/docs/plugins/creating).
|
|
139
|
+
Scheduler.Hooks = (
|
|
140
|
+
{
|
|
141
|
+
SystemAdd = "SystemAdd",
|
|
142
|
+
SystemRemove = "SystemRemove",
|
|
143
|
+
SystemReplace = "SystemReplace",
|
|
144
|
+
SystemEdited = "SystemEdited",
|
|
145
|
+
SystemCleanup = "SystemCleanup",
|
|
146
|
+
SystemError = "SystemError",
|
|
147
|
+
SystemTriedRun = "SystemTriedRun",
|
|
148
|
+
|
|
149
|
+
OuterSystemCall = "OuterSystemCall",
|
|
150
|
+
InnerSystemCall = "InnerSystemCall",
|
|
151
|
+
SystemCall = "SystemCall",
|
|
152
|
+
|
|
153
|
+
PhaseAdd = "PhaseAdd",
|
|
154
|
+
PhaseBegan = "PhaseBegan",
|
|
155
|
+
} :: any
|
|
156
|
+
) :: Hooks
|
|
157
|
+
|
|
158
|
+
type HookId = keyof<typeof(Scheduler.Hooks)>
|
|
159
|
+
|
|
160
|
+
--- @within Scheduler
|
|
161
|
+
--- @since 0.3.0
|
|
162
|
+
---
|
|
163
|
+
--- Allows for registering hooks, see [creating plugins](/docs/plugins/creating).
|
|
164
|
+
Scheduler.addHook = function<T, U...>(self: Scheduler<U...>, hook: Hook<T>, fn: T)
|
|
165
|
+
local hookId: HookId = hook :: any
|
|
166
|
+
|
|
167
|
+
assert(self._hooks[hookId], `Unknown Hook: {hook}`)
|
|
168
|
+
table.insert(self._hooks[hookId], fn)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
--- @method _addHook
|
|
172
|
+
--- @within Scheduler
|
|
173
|
+
--- @deprecated 0.3.0 -- Use `Scheduler:addHook()` instead
|
|
174
|
+
--- @private
|
|
175
|
+
---
|
|
176
|
+
--- Internal method for adding hooks maintained for backwards compatibility.
|
|
177
|
+
--- Use [Scheduler:addHook] instead.
|
|
178
|
+
Scheduler._addHook = Scheduler.addHook
|
|
179
|
+
|
|
180
|
+
local function callHooks<U...>(
|
|
181
|
+
hooks: Vec<(PartialHookContext) -> unknown>,
|
|
182
|
+
info: PartialHookContext
|
|
183
|
+
)
|
|
184
|
+
for _, hook in hooks do
|
|
185
|
+
local success, err = pcall(hook, info)
|
|
186
|
+
if not success then
|
|
187
|
+
warn("Unexpected error in hook:", err)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
export type SystemHookContext = PartialHookContext & {
|
|
193
|
+
system: SystemInfo<...any>,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function Scheduler._systemAdd<U...>(
|
|
197
|
+
self: Scheduler<U...>,
|
|
198
|
+
systemInfo: SystemInfo<U...>
|
|
199
|
+
)
|
|
200
|
+
local context = {
|
|
201
|
+
scheduler = self,
|
|
202
|
+
system = systemInfo,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
callHooks(self._hooks["SystemAdd"], context)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
function Scheduler._systemRemove<U...>(
|
|
209
|
+
self: Scheduler<U...>,
|
|
210
|
+
systemInfo: SystemInfo<U...>
|
|
211
|
+
)
|
|
212
|
+
local context: SystemHookContext = {
|
|
213
|
+
scheduler = self,
|
|
214
|
+
system = systemInfo :: any,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
callHooks(self._hooks["SystemRemove"], context)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
export type SystemReplaceContext = PartialHookContext & {
|
|
221
|
+
new: SystemInfo<...any>,
|
|
222
|
+
old: SystemInfo<...any>,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function Scheduler._systemReplace<U...>(
|
|
226
|
+
self: Scheduler<U...>,
|
|
227
|
+
oldSystemInfo: SystemInfo<U...>,
|
|
228
|
+
newSystemInfo: SystemInfo<U...>
|
|
229
|
+
)
|
|
230
|
+
local context: SystemReplaceContext = {
|
|
231
|
+
scheduler = self,
|
|
232
|
+
new = newSystemInfo :: any,
|
|
233
|
+
old = oldSystemInfo :: any,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
callHooks(self._hooks["SystemReplace"], context)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
export type SystemEditedContext = SystemHookContext & {
|
|
240
|
+
old: Phase,
|
|
241
|
+
new: Phase,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function Scheduler._systemEdited<U...>(
|
|
245
|
+
self: Scheduler<U...>,
|
|
246
|
+
systemInfo: SystemInfo<U...>,
|
|
247
|
+
old: Phase,
|
|
248
|
+
new: Phase
|
|
249
|
+
)
|
|
250
|
+
local context: SystemEditedContext = {
|
|
251
|
+
scheduler = self,
|
|
252
|
+
system = systemInfo :: any,
|
|
253
|
+
new = new,
|
|
254
|
+
old = old,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
callHooks(self._hooks["SystemEdited"], context)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
export type SystemErrorContext = PartialHookContext & {
|
|
261
|
+
system: SystemInfo<...any>,
|
|
262
|
+
error: SystemLog<unknown>?,
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function Scheduler._systemCleanup<E, U...>(
|
|
266
|
+
self: Scheduler<U...>,
|
|
267
|
+
systemInfo,
|
|
268
|
+
cleanupError: SystemLog<E>?
|
|
269
|
+
)
|
|
270
|
+
local context: SystemErrorContext = {
|
|
271
|
+
scheduler = self,
|
|
272
|
+
system = systemInfo,
|
|
273
|
+
error = cleanupError,
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
callHooks(self._hooks["SystemCleanup"], context)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
function Scheduler._systemError<E, U...>(
|
|
280
|
+
self: Scheduler<U...>,
|
|
281
|
+
systemInfo,
|
|
282
|
+
err: SystemLog<E>
|
|
283
|
+
)
|
|
284
|
+
local context: SystemErrorContext = {
|
|
285
|
+
scheduler = self,
|
|
286
|
+
system = systemInfo,
|
|
287
|
+
error = err,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
callHooks(self._hooks["SystemError"], context)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
function Scheduler._systemTriedRun<T, U...>(self: Scheduler<U...>, systemInfo)
|
|
294
|
+
local context: SystemHookContext = {
|
|
295
|
+
scheduler = self,
|
|
296
|
+
system = systemInfo,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
callHooks(self._hooks["SystemTriedRun"], context)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
type SystemHookId = "OuterSystemCall" | "InnerSystemCall" | "SystemCall"
|
|
303
|
+
|
|
304
|
+
type SystemCallHookFn = () -> ()
|
|
305
|
+
type SystemCallCallback = (SystemCallContext) -> SystemCallHookFn
|
|
306
|
+
export type SystemCallContext = PartialHookContext & {
|
|
307
|
+
system: SystemInfo<...any>,
|
|
308
|
+
nextFn: SystemCallHookFn,
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function Scheduler._systemCall<T, U...>(
|
|
312
|
+
self: Scheduler<U...>,
|
|
313
|
+
hookName: SystemHookId,
|
|
314
|
+
systemInfo: SystemInfo<U...>,
|
|
315
|
+
nextFn: SystemCallHookFn
|
|
316
|
+
)
|
|
317
|
+
for _, hook in self._hooks[hookName] :: Vec<SystemCallCallback> do
|
|
318
|
+
local context: SystemCallContext = {
|
|
319
|
+
scheduler = self,
|
|
320
|
+
system = systemInfo :: any,
|
|
321
|
+
nextFn = nextFn,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
nextFn = hook(context)
|
|
325
|
+
|
|
326
|
+
if not nextFn then
|
|
327
|
+
local source, line = debug.info(hook, "sl")
|
|
328
|
+
warn(
|
|
329
|
+
`{source}:{line}: Expected 'SystemCall' hook to return a function`
|
|
330
|
+
)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
nextFn()
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
export type PhaseContext = PartialHookContext & {
|
|
338
|
+
phase: Phase,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function Scheduler._phaseAdd<U...>(self: Scheduler<U...>, phase: Phase)
|
|
342
|
+
local context: PhaseContext = {
|
|
343
|
+
scheduler = self,
|
|
344
|
+
phase = phase,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
callHooks(self._hooks["PhaseAdd"], context)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
function Scheduler._phaseBegan<U...>(self: Scheduler<U...>, phase: Phase)
|
|
351
|
+
local context: PhaseContext = {
|
|
352
|
+
scheduler = self,
|
|
353
|
+
phase = phase,
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
callHooks(self._hooks["PhaseBegan"], context)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
--- @method getDeltaTime
|
|
360
|
+
--- @within Scheduler
|
|
361
|
+
--- @return number
|
|
362
|
+
---
|
|
363
|
+
--- Returns the time since the system was ran last.
|
|
364
|
+
--- This must be used within a registered system.
|
|
365
|
+
function Scheduler.getDeltaTime<U...>(self: Scheduler<U...>)
|
|
366
|
+
if self._currentSystem then
|
|
367
|
+
return self._currentSystem.deltaTime or 0
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
local systemFn = debug.info(2, "f")
|
|
371
|
+
if not systemFn or not self._systemInfo[systemFn] then
|
|
372
|
+
error(
|
|
373
|
+
"Scheduler:getDeltaTime() must be used within a registered system"
|
|
374
|
+
)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
return self._systemInfo[systemFn].deltaTime or 0
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
function Scheduler._reportLog<E, U...>(
|
|
381
|
+
_: Scheduler<U...>,
|
|
382
|
+
systemInfo: SystemInfo<U...>,
|
|
383
|
+
log: SystemLog<E>,
|
|
384
|
+
-- A warning after the error providing context
|
|
385
|
+
context: string
|
|
386
|
+
)
|
|
387
|
+
local recentLogs = systemInfo.recentLogs
|
|
388
|
+
local logMessage = log.log
|
|
389
|
+
|
|
390
|
+
assert(recentLogs)
|
|
391
|
+
|
|
392
|
+
if not recentLogs[logMessage] then
|
|
393
|
+
-- LUAU FUTURE: Inference bug
|
|
394
|
+
task.spawn<<(unknown, number)>>(error, logMessage, 0)
|
|
395
|
+
warn(
|
|
396
|
+
`Planck: {context},`
|
|
397
|
+
.. " "
|
|
398
|
+
.. `this error will be ignored for 10 seconds`
|
|
399
|
+
)
|
|
400
|
+
recentLogs[logMessage] = true
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
-- Inspiration from https://github.com/matter-ecs/matter <3
|
|
405
|
+
function Scheduler._handleLogs<U...>(
|
|
406
|
+
self: Scheduler<U...>,
|
|
407
|
+
systemInfo: SystemInfo<U...>
|
|
408
|
+
)
|
|
409
|
+
if not systemInfo.timeLastLogged then
|
|
410
|
+
systemInfo.timeLastLogged = os.clock()
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
if not systemInfo.recentLogs then
|
|
414
|
+
systemInfo.recentLogs = {}
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
-- selene: allow(incorrect_standard_library_use)
|
|
418
|
+
do
|
|
419
|
+
assert(systemInfo.timeLastLogged)
|
|
420
|
+
assert(systemInfo.recentLogs)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
if os.clock() - systemInfo.timeLastLogged > 10 then
|
|
424
|
+
systemInfo.timeLastLogged = os.clock()
|
|
425
|
+
systemInfo.recentLogs = {}
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
local name = systemInfo.name
|
|
429
|
+
|
|
430
|
+
for _, log in systemInfo.logs do
|
|
431
|
+
self:_reportLog(
|
|
432
|
+
systemInfo,
|
|
433
|
+
log,
|
|
434
|
+
`Error occurred in system{string.len(name) > 0 and ` '{name}'` or ""}`
|
|
435
|
+
)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
table.clear(systemInfo.logs)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
function Scheduler.runSystem<U...>(
|
|
442
|
+
self: Scheduler<U...>,
|
|
443
|
+
system: InternalSystem<U...>,
|
|
444
|
+
justInitialized: boolean
|
|
445
|
+
)
|
|
446
|
+
local systemInfo = self._systemInfo[system]
|
|
447
|
+
local now = os.clock()
|
|
448
|
+
|
|
449
|
+
if not systemInfo then
|
|
450
|
+
error(
|
|
451
|
+
"Attempted to run a non-registered system, make sure it is added to the Scheduler"
|
|
452
|
+
)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
if justInitialized ~= true then
|
|
456
|
+
if self:_canRun(system) == false then
|
|
457
|
+
self:_systemTriedRun(systemInfo)
|
|
458
|
+
return
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
systemInfo.deltaTime = now - (systemInfo.lastTime or now)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
systemInfo.lastTime = now
|
|
465
|
+
self._currentSystem = systemInfo
|
|
466
|
+
|
|
467
|
+
if not self._thread then
|
|
468
|
+
self._thread = coroutine.create(function()
|
|
469
|
+
while true do
|
|
470
|
+
local fn = coroutine.yield()
|
|
471
|
+
self._yielded = true
|
|
472
|
+
fn()
|
|
473
|
+
self._yielded = false
|
|
474
|
+
end
|
|
475
|
+
end)
|
|
476
|
+
|
|
477
|
+
coroutine.resume(self._thread)
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
-- selene: allow(incorrect_standard_library_use)
|
|
481
|
+
assert(self._thread)
|
|
482
|
+
|
|
483
|
+
local didYield = false
|
|
484
|
+
local hasSystem = false
|
|
485
|
+
|
|
486
|
+
local function systemCall()
|
|
487
|
+
local function noYield()
|
|
488
|
+
local success, errOrSys, cleanup
|
|
489
|
+
|
|
490
|
+
coroutine.resume(self._thread, function()
|
|
491
|
+
success, errOrSys, cleanup = xpcall(function()
|
|
492
|
+
return systemInfo.run(table.unpack(self._vargs))
|
|
493
|
+
end, function(e)
|
|
494
|
+
return {
|
|
495
|
+
e = e,
|
|
496
|
+
log = debug.traceback(tostring(e)),
|
|
497
|
+
trace = debug.traceback(),
|
|
498
|
+
}
|
|
499
|
+
end)
|
|
500
|
+
end)
|
|
501
|
+
|
|
502
|
+
if success == false then
|
|
503
|
+
didYield = true
|
|
504
|
+
|
|
505
|
+
-- selene: allow(incorrect_standard_library_use)
|
|
506
|
+
assert(errOrSys)
|
|
507
|
+
table.insert(systemInfo.logs, errOrSys)
|
|
508
|
+
self:_systemError(systemInfo, errOrSys)
|
|
509
|
+
|
|
510
|
+
return
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
if self._yielded then
|
|
514
|
+
didYield = true
|
|
515
|
+
|
|
516
|
+
local source, line = debug.info(self._thread, 1, "sl")
|
|
517
|
+
local errMessage = `{source}:{line}: System yielded`
|
|
518
|
+
local err = debug.traceback(self._thread, errMessage, 2)
|
|
519
|
+
|
|
520
|
+
local log = {
|
|
521
|
+
e = err,
|
|
522
|
+
log = err,
|
|
523
|
+
trace = debug.traceback(self._thread, nil, 2),
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
table.insert(systemInfo.logs, log :: SystemLog<unknown>)
|
|
527
|
+
self:_systemError(systemInfo, log)
|
|
528
|
+
return
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
if not systemInfo.initialized then
|
|
532
|
+
if errOrSys == nil and cleanup == nil then
|
|
533
|
+
systemInfo.initialized = true
|
|
534
|
+
return
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
if type(errOrSys) == "function" then
|
|
538
|
+
systemInfo.run = errOrSys
|
|
539
|
+
systemInfo.initialized = true
|
|
540
|
+
if type(cleanup) == "function" then
|
|
541
|
+
systemInfo.cleanup = cleanup
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
hasSystem = true
|
|
545
|
+
return
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
if type(errOrSys) == "table" then
|
|
549
|
+
hasSystem = type(errOrSys.system) == "function"
|
|
550
|
+
local hasCleanup = type(errOrSys.cleanup) == "function"
|
|
551
|
+
|
|
552
|
+
if hasSystem or hasCleanup then
|
|
553
|
+
if hasSystem then
|
|
554
|
+
systemInfo.run = errOrSys.system
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
if hasCleanup then
|
|
558
|
+
systemInfo.cleanup = errOrSys.cleanup
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
systemInfo.initialized = true
|
|
562
|
+
return
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
local errMessage = `System '{systemInfo.name}' initializer returned invalid type. `
|
|
567
|
+
.. "Expected: function, {system?, cleanup?}, or (function, function). "
|
|
568
|
+
.. `Got: {typeof(errOrSys)}, {typeof(cleanup)}`
|
|
569
|
+
|
|
570
|
+
local err = debug.traceback(errMessage)
|
|
571
|
+
local log = {
|
|
572
|
+
e = err,
|
|
573
|
+
log = err,
|
|
574
|
+
trace = debug.traceback(),
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
table.insert(systemInfo.logs, log :: SystemLog<unknown>)
|
|
578
|
+
self:_systemError(systemInfo, log)
|
|
579
|
+
systemInfo.initialized = true
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
self:_systemCall("SystemCall", systemInfo, noYield)
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
local function inner()
|
|
587
|
+
self:_systemCall("InnerSystemCall", systemInfo, systemCall)
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
local function outer()
|
|
591
|
+
self:_systemCall("OuterSystemCall", systemInfo, inner)
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
if os.clock() - timeLastLogged > 10 then
|
|
595
|
+
timeLastLogged = os.clock()
|
|
596
|
+
recentLogs = {}
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
-- LUAU FUTURE: Better types for pcalls
|
|
600
|
+
local success, err = xpcall(outer :: any, function(e)
|
|
601
|
+
return {
|
|
602
|
+
e = e,
|
|
603
|
+
log = debug.traceback(`Error occurred while running hooks: {e}`),
|
|
604
|
+
trace = debug.traceback(),
|
|
605
|
+
}
|
|
606
|
+
end)
|
|
607
|
+
|
|
608
|
+
if not success then
|
|
609
|
+
self:_systemError(systemInfo, err)
|
|
610
|
+
self:_reportLog(systemInfo, err, `Error occurred while running hooks`)
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
if didYield then
|
|
614
|
+
coroutine.close(self._thread)
|
|
615
|
+
|
|
616
|
+
self._thread = coroutine.create(function()
|
|
617
|
+
while true do
|
|
618
|
+
local fn = coroutine.yield()
|
|
619
|
+
self._yielded = true
|
|
620
|
+
fn()
|
|
621
|
+
self._yielded = false
|
|
622
|
+
end
|
|
623
|
+
end)
|
|
624
|
+
|
|
625
|
+
coroutine.resume(self._thread)
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
self:_handleLogs(systemInfo)
|
|
629
|
+
self._currentSystem = nil
|
|
630
|
+
|
|
631
|
+
if hasSystem and justInitialized ~= true then
|
|
632
|
+
self:runSystem(system, true)
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
function Scheduler.runPhase<U...>(self: Scheduler<U...>, phase: Phase)
|
|
637
|
+
if self:_canRun(phase) == false then
|
|
638
|
+
return
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
self:_phaseBegan(phase)
|
|
642
|
+
|
|
643
|
+
if not self._phaseToSystems[phase] then
|
|
644
|
+
self._phaseToSystems[phase] = {}
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
for _, system in self._phaseToSystems[phase] do
|
|
648
|
+
self:runSystem(system, false)
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
function Scheduler.runPipeline<U...>(self: Scheduler<U...>, pipeline: Pipeline)
|
|
653
|
+
if self:_canRun(pipeline) == false then
|
|
654
|
+
return
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
local orderedList = pipeline.dependencyGraph:getOrderedList()
|
|
658
|
+
assert(
|
|
659
|
+
orderedList,
|
|
660
|
+
`Pipeline {pipeline} contains a circular dependency, check it's Phases`
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
for _, phase in orderedList do
|
|
664
|
+
self:runPhase(phase)
|
|
665
|
+
end
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
function Scheduler._canRun<U...>(self: Scheduler<U...>, dependent: Dependent<U...>)
|
|
669
|
+
local runConditions = self._runIfConditions[dependent :: any]
|
|
670
|
+
|
|
671
|
+
if runConditions then
|
|
672
|
+
for _, runIf in runConditions do
|
|
673
|
+
if runIf and not runIf(table.unpack(self._vargs)) then
|
|
674
|
+
return false
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
return true
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
--- @method run
|
|
683
|
+
--- @within Scheduler
|
|
684
|
+
--- @param phase Phase
|
|
685
|
+
--- @return Scheduler
|
|
686
|
+
---
|
|
687
|
+
--- Runs all Systems tagged with the Phase in order.
|
|
688
|
+
|
|
689
|
+
--- @method run
|
|
690
|
+
--- @within Scheduler
|
|
691
|
+
--- @param pipeline Pipeline
|
|
692
|
+
--- @return Scheduler
|
|
693
|
+
---
|
|
694
|
+
--- Runs all Systems tagged with any Phase within the Pipeline in order.
|
|
695
|
+
|
|
696
|
+
--- @method run
|
|
697
|
+
--- @within Scheduler
|
|
698
|
+
--- @param system System
|
|
699
|
+
--- @return Scheduler
|
|
700
|
+
---
|
|
701
|
+
--- Runs the System, passing in the arguments of the Scheduler, `U...`.
|
|
702
|
+
function Scheduler.run<U...>(self: Scheduler<U...>, dependent: Dependent<U...>)
|
|
703
|
+
assert(
|
|
704
|
+
dependent ~= nil,
|
|
705
|
+
"No dependent specified in Scheduler:run(dependent?)"
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
self:runPipeline(Pipeline.Startup)
|
|
709
|
+
|
|
710
|
+
if isSystem(dependent) then
|
|
711
|
+
self:runSystem(dependent :: any, false)
|
|
712
|
+
elseif isPhase(dependent) then
|
|
713
|
+
self:runPhase(dependent :: any)
|
|
714
|
+
elseif isPipeline(dependent) then
|
|
715
|
+
self:runPipeline(dependent :: any)
|
|
716
|
+
else
|
|
717
|
+
error("Unknown dependent passed into Scheduler:run(unknown)")
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
return self
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
--- @method runAll
|
|
724
|
+
--- @within Scheduler
|
|
725
|
+
--- @return Scheduler
|
|
726
|
+
---
|
|
727
|
+
--- Runs all Systems within order.
|
|
728
|
+
---
|
|
729
|
+
--- :::note
|
|
730
|
+
--- When you add a Pipeline or Phase with an event, it will be grouped
|
|
731
|
+
--- with other Pipelines/Phases on that event. Otherwise, it will be
|
|
732
|
+
--- added to the default group.
|
|
733
|
+
---
|
|
734
|
+
--- When not running systems on Events, such as with the `runAll` method,
|
|
735
|
+
--- the Default group will be ran first, and then each Event Group in the
|
|
736
|
+
--- order created.
|
|
737
|
+
---
|
|
738
|
+
--- Pipelines/Phases in these groups are still ordered by their dependencies
|
|
739
|
+
--- and by the order of insertion.
|
|
740
|
+
--- :::
|
|
741
|
+
function Scheduler.runAll<U...>(self: Scheduler<U...>)
|
|
742
|
+
local orderedDefaults = self._defaultDependencyGraph:getOrderedList()
|
|
743
|
+
assert(
|
|
744
|
+
orderedDefaults,
|
|
745
|
+
"Default Group contains a circular dependency, check your Pipelines/Phases"
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
for _, dependency in orderedDefaults do
|
|
749
|
+
self:run(dependency)
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
for identifier, dependencyGraph in self._eventDependencyGraphs do
|
|
753
|
+
local orderedList = dependencyGraph:getOrderedList()
|
|
754
|
+
assert(
|
|
755
|
+
orderedList,
|
|
756
|
+
`Event Group '{identifier}' contains a circular dependency, check your Pipelines/Phases`
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
for _, dependency in orderedList do
|
|
760
|
+
self:run(dependency)
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
return self
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
type InsertFn =
|
|
768
|
+
& (<U...>(
|
|
769
|
+
self: Scheduler<U...>,
|
|
770
|
+
dependency: Phase | Pipeline
|
|
771
|
+
) -> Scheduler<U...>)
|
|
772
|
+
-- RBXScriptSignal & nil
|
|
773
|
+
& (<U...>(
|
|
774
|
+
self: Scheduler<U...>,
|
|
775
|
+
dependency: Phase | Pipeline,
|
|
776
|
+
signal: RBXScriptSignal<U...>
|
|
777
|
+
) -> Scheduler<U...>)
|
|
778
|
+
-- Instance & RBXScriptSignal
|
|
779
|
+
-- Instance & string
|
|
780
|
+
& (<U...>(
|
|
781
|
+
self: Scheduler<U...>,
|
|
782
|
+
dependency: Phase | Pipeline,
|
|
783
|
+
instance: Instance,
|
|
784
|
+
event: RBXScriptSignal<U...> | string
|
|
785
|
+
) -> Scheduler<U...>)
|
|
786
|
+
-- SignalLike & nil
|
|
787
|
+
& (<U...>(
|
|
788
|
+
self: Scheduler<U...>,
|
|
789
|
+
dependency: Phase | Pipeline,
|
|
790
|
+
signal: SignalLike<U...>
|
|
791
|
+
) -> Scheduler<U...>)
|
|
792
|
+
-- table & string
|
|
793
|
+
& (<U...>(
|
|
794
|
+
self: Scheduler<U...>,
|
|
795
|
+
dependency: Phase | Pipeline,
|
|
796
|
+
table: GenericTable,
|
|
797
|
+
event: string
|
|
798
|
+
) -> Scheduler<U...>)
|
|
799
|
+
-- table & connectable method
|
|
800
|
+
& (<T, U...>(
|
|
801
|
+
self: Scheduler<U...>,
|
|
802
|
+
dependency: Phase | Pipeline,
|
|
803
|
+
instance: GenericTable,
|
|
804
|
+
connectMethod: (GenericTable, Callback<U...>, ...any) -> T
|
|
805
|
+
) -> Scheduler<U...>)
|
|
806
|
+
-- connectable function
|
|
807
|
+
& (<T, U...>(
|
|
808
|
+
self: Scheduler<U...>,
|
|
809
|
+
dependency: Phase | Pipeline,
|
|
810
|
+
connectFn: (Callback<U...>, ...any) -> T
|
|
811
|
+
) -> Scheduler<U...>)
|
|
812
|
+
|
|
813
|
+
--- @method insert
|
|
814
|
+
--- @within Scheduler
|
|
815
|
+
--- @param phase Phase
|
|
816
|
+
--- @return Scheduler
|
|
817
|
+
---
|
|
818
|
+
--- Initializes the Phase within the Scheduler, ordering it implicitly by
|
|
819
|
+
--- setting it as a dependent of the previous Phase/Pipeline.
|
|
820
|
+
|
|
821
|
+
--- @method insert
|
|
822
|
+
--- @within Scheduler
|
|
823
|
+
--- @param pipeline Pipeline
|
|
824
|
+
--- @return Scheduler
|
|
825
|
+
---
|
|
826
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
827
|
+
--- ordering the Pipeline implicitly by setting it as a dependent
|
|
828
|
+
--- of the previous Phase/Pipeline.
|
|
829
|
+
|
|
830
|
+
--- @method insert
|
|
831
|
+
--- @within Scheduler
|
|
832
|
+
--- @param phase Phase
|
|
833
|
+
--- @param instance Instance | EventLike
|
|
834
|
+
--- @param event string | EventLike
|
|
835
|
+
--- @return Scheduler
|
|
836
|
+
---
|
|
837
|
+
--- Initializes the Phase within the Scheduler, ordering it implicitly
|
|
838
|
+
--- by setting it as a dependent of the previous Phase/Pipeline, and
|
|
839
|
+
--- scheduling it to be ran on the specified event.
|
|
840
|
+
---
|
|
841
|
+
--- ```lua
|
|
842
|
+
--- local myScheduler = Scheduler.new()
|
|
843
|
+
--- :insert(myPhase, RunService, "Heartbeat")
|
|
844
|
+
--- ```
|
|
845
|
+
|
|
846
|
+
--- @method insert
|
|
847
|
+
--- @within Scheduler
|
|
848
|
+
--- @param pipeline Pipeline
|
|
849
|
+
--- @param instance Instance | EventLike
|
|
850
|
+
--- @param event string | EventLike
|
|
851
|
+
--- @return Scheduler
|
|
852
|
+
---
|
|
853
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
854
|
+
--- ordering the Pipeline implicitly by setting it as a dependent of
|
|
855
|
+
--- the previous Phase/Pipeline, and scheduling it to be ran on the
|
|
856
|
+
--- specified event.
|
|
857
|
+
---
|
|
858
|
+
--- ```lua
|
|
859
|
+
--- local myScheduler = Scheduler.new()
|
|
860
|
+
--- :insert(myPipeline, RunService, "Heartbeat")
|
|
861
|
+
--- ```
|
|
862
|
+
local insertFn: InsertFn = function<U...>(
|
|
863
|
+
self: Scheduler<U...>,
|
|
864
|
+
dependency: Phase | Pipeline,
|
|
865
|
+
instance: any,
|
|
866
|
+
event: any
|
|
867
|
+
): Scheduler<U...>
|
|
868
|
+
assert(
|
|
869
|
+
isPhase(dependency) or isPipeline(dependency),
|
|
870
|
+
"Unknown dependency passed to Scheduler:insert(unknown, _, _)"
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
if not instance then
|
|
874
|
+
local dependencyGraph = self._defaultDependencyGraph
|
|
875
|
+
dependencyGraph:insertBefore(dependency, self._defaultPhase)
|
|
876
|
+
else
|
|
877
|
+
assert(
|
|
878
|
+
isValidEvent(instance, event),
|
|
879
|
+
"Unknown instance/event passed to Scheduler:insert(_, instance, event)"
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
local dependencyGraph = self:_getEventDependencyGraph(instance, event)
|
|
883
|
+
dependencyGraph:insert(dependency)
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
if isPhase(dependency) then
|
|
887
|
+
self._phaseToSystems[dependency :: Phase] = {}
|
|
888
|
+
self:_phaseAdd(dependency :: Phase)
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
return self
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
Scheduler.insert = insertFn
|
|
895
|
+
|
|
896
|
+
--- @method insertAfter
|
|
897
|
+
--- @within Scheduler
|
|
898
|
+
--- @param phase Phase
|
|
899
|
+
--- @param after Phase | Pipeline
|
|
900
|
+
--- @return Scheduler
|
|
901
|
+
---
|
|
902
|
+
--- Initializes the Phase within the Scheduler, ordering it
|
|
903
|
+
--- explicitly by setting the after Phase/Pipeline as a dependent.
|
|
904
|
+
|
|
905
|
+
--- @method insertAfter
|
|
906
|
+
--- @within Scheduler
|
|
907
|
+
--- @param pipeline Pipeline
|
|
908
|
+
--- @param after Phase | Pipeline
|
|
909
|
+
--- @return Scheduler
|
|
910
|
+
---
|
|
911
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
912
|
+
--- ordering the Pipeline explicitly by setting the after Phase/Pipeline
|
|
913
|
+
--- as a dependent.
|
|
914
|
+
function Scheduler.insertAfter<U...>(
|
|
915
|
+
self: Scheduler<U...>,
|
|
916
|
+
dependent: Dependency,
|
|
917
|
+
after: Dependency
|
|
918
|
+
)
|
|
919
|
+
assert(
|
|
920
|
+
isPhase(after) or isPipeline(after),
|
|
921
|
+
"Unknown dependency passed in Scheduler:insertAfter(_, unknown)"
|
|
922
|
+
)
|
|
923
|
+
assert(
|
|
924
|
+
isPhase(dependent) or isPipeline(dependent),
|
|
925
|
+
"Unknown dependent passed in Scheduler:insertAfter(unknown, _)"
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
local dependencyGraph = self:_getGraphOfDependency(after)
|
|
929
|
+
dependencyGraph:insertAfter(dependent, after)
|
|
930
|
+
|
|
931
|
+
if isPhase(dependent) then
|
|
932
|
+
self._phaseToSystems[dependent :: Phase] = {}
|
|
933
|
+
self:_phaseAdd(dependent :: Phase)
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
return self
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
--- @method insertBefore
|
|
940
|
+
--- @within Scheduler
|
|
941
|
+
--- @param phase Phase
|
|
942
|
+
--- @param before Phase | Pipeline
|
|
943
|
+
--- @return Scheduler
|
|
944
|
+
---
|
|
945
|
+
--- Initializes the Phase within the Scheduler, ordering it
|
|
946
|
+
--- explicitly by setting the before Phase/Pipeline as a dependency.
|
|
947
|
+
|
|
948
|
+
--- @method insertBefore
|
|
949
|
+
--- @within Scheduler
|
|
950
|
+
--- @param pipeline Pipeline
|
|
951
|
+
--- @param before Phase | Pipeline
|
|
952
|
+
--- @return Scheduler
|
|
953
|
+
---
|
|
954
|
+
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
955
|
+
--- ordering the Pipeline explicitly by setting the before Phase/Pipeline
|
|
956
|
+
--- as a dependency.
|
|
957
|
+
function Scheduler.insertBefore<U...>(
|
|
958
|
+
self: Scheduler<U...>,
|
|
959
|
+
dependent: Dependency,
|
|
960
|
+
before: Dependency
|
|
961
|
+
)
|
|
962
|
+
assert(
|
|
963
|
+
isPhase(before) or isPipeline(before),
|
|
964
|
+
"Unknown dependency passed in Scheduler:insertBefore(_, unknown)"
|
|
965
|
+
)
|
|
966
|
+
assert(
|
|
967
|
+
isPhase(dependent) or isPipeline(dependent),
|
|
968
|
+
"Unknown dependent passed in Scheduler:insertBefore(unknown, _)"
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
local dependencyGraph = self:_getGraphOfDependency(before)
|
|
972
|
+
dependencyGraph:insertBefore(dependent, before)
|
|
973
|
+
|
|
974
|
+
if isPhase(dependent) then
|
|
975
|
+
self._phaseToSystems[dependent :: Phase] = {}
|
|
976
|
+
self:_phaseAdd(dependent :: Phase)
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
return self
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
--- @method addSystem
|
|
983
|
+
--- @within Scheduler
|
|
984
|
+
--- @param system System
|
|
985
|
+
--- @param phase Phase?
|
|
986
|
+
--- @return Scheduler
|
|
987
|
+
---
|
|
988
|
+
--- Adds the System to the Scheduler, scheduling it to be ran
|
|
989
|
+
--- implicitly within the provided Phase or on the default Main phase.
|
|
990
|
+
---
|
|
991
|
+
--- **Initializer Systems**: Systems can optionally return a function on their
|
|
992
|
+
--- first execution, which becomes the runtime system. This allows one-time
|
|
993
|
+
--- setup logic without creating separate initialization phases.
|
|
994
|
+
---
|
|
995
|
+
--- ```lua
|
|
996
|
+
--- local function renderSystem(world, state)
|
|
997
|
+
--- -- This runs once on first execution
|
|
998
|
+
--- local renderables = world:query(Transform, Model):cached()
|
|
999
|
+
---
|
|
1000
|
+
--- -- This runs on each subsequent execution
|
|
1001
|
+
--- return function(world, state)
|
|
1002
|
+
--- for id, transform, model in renderables do
|
|
1003
|
+
--- render(transform, model)
|
|
1004
|
+
--- end
|
|
1005
|
+
--- end, function()
|
|
1006
|
+
--- -- Optional cleanup logic runs on removeSystem
|
|
1007
|
+
--- end
|
|
1008
|
+
--- end
|
|
1009
|
+
--- ```
|
|
1010
|
+
function Scheduler.addSystem<U...>(
|
|
1011
|
+
self: Scheduler<U...>,
|
|
1012
|
+
system: System<U...>,
|
|
1013
|
+
phase: Phase?
|
|
1014
|
+
)
|
|
1015
|
+
local systemFn = getSystem(system)
|
|
1016
|
+
assert(systemFn, "Unknown system passed to Scheduler:addSystem(unknown, _)")
|
|
1017
|
+
|
|
1018
|
+
local name = getSystemName(system)
|
|
1019
|
+
|
|
1020
|
+
local scheduledPhase
|
|
1021
|
+
if phase then
|
|
1022
|
+
scheduledPhase = phase
|
|
1023
|
+
elseif type(system) == "table" and system.phase then
|
|
1024
|
+
scheduledPhase = system.phase
|
|
1025
|
+
else
|
|
1026
|
+
scheduledPhase = self._defaultPhase
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
local systemInfo: SystemInfo<U...> = {
|
|
1030
|
+
system = systemFn,
|
|
1031
|
+
run = systemFn,
|
|
1032
|
+
cleanup = nil,
|
|
1033
|
+
phase = scheduledPhase,
|
|
1034
|
+
name = name,
|
|
1035
|
+
logs = {},
|
|
1036
|
+
initialized = false,
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
self._systemInfo[systemFn] = systemInfo :: any
|
|
1040
|
+
|
|
1041
|
+
if not self._phaseToSystems[systemInfo.phase] then
|
|
1042
|
+
self._phaseToSystems[systemInfo.phase] = {}
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
table.insert(
|
|
1046
|
+
self._phaseToSystems[systemInfo.phase],
|
|
1047
|
+
systemFn :: InternalSystem<...any>
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
self:_systemAdd(systemInfo)
|
|
1051
|
+
|
|
1052
|
+
if type(system) == "table" and system.runConditions then
|
|
1053
|
+
for _, condition in system.runConditions do
|
|
1054
|
+
condition = if typeof(condition) == "table"
|
|
1055
|
+
then condition[1]
|
|
1056
|
+
else condition
|
|
1057
|
+
self:addRunCondition(systemFn, condition)
|
|
1058
|
+
end
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
return self
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
--- @method addSystems
|
|
1065
|
+
--- @within Scheduler
|
|
1066
|
+
--- @param systems { System }
|
|
1067
|
+
--- @param phase Phase?
|
|
1068
|
+
---
|
|
1069
|
+
--- Adds the Systems to the Scheduler, scheduling them to be ran
|
|
1070
|
+
--- implicitly within the provided Phase or on the default Main phase.
|
|
1071
|
+
function Scheduler.addSystems<U...>(
|
|
1072
|
+
self: Scheduler<U...>,
|
|
1073
|
+
systems: { System<U...> },
|
|
1074
|
+
phase: Phase?
|
|
1075
|
+
)
|
|
1076
|
+
assert(
|
|
1077
|
+
type(systems) == "table",
|
|
1078
|
+
"Unknown value passed to Scheduler:addSystems(unknown, _). This value should be an array."
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
local foundSystem = false
|
|
1082
|
+
local n = 0
|
|
1083
|
+
|
|
1084
|
+
for _, system in systems do
|
|
1085
|
+
n += 1
|
|
1086
|
+
if getSystem(system) then
|
|
1087
|
+
foundSystem = true
|
|
1088
|
+
self:addSystem(system, phase)
|
|
1089
|
+
end
|
|
1090
|
+
end
|
|
1091
|
+
|
|
1092
|
+
assert(n ~= 0, "Empty table passed to Scheduler:addSystems({ }, _)")
|
|
1093
|
+
|
|
1094
|
+
assert(
|
|
1095
|
+
foundSystem,
|
|
1096
|
+
"Table containing unknown values passed to Scheduler:addSystems({ unknown }, _)"
|
|
1097
|
+
)
|
|
1098
|
+
|
|
1099
|
+
return self
|
|
1100
|
+
end
|
|
1101
|
+
|
|
1102
|
+
--- @method editSystem
|
|
1103
|
+
--- @within Scheduler
|
|
1104
|
+
--- @param system System
|
|
1105
|
+
--- @param newPhase Phase
|
|
1106
|
+
---
|
|
1107
|
+
--- Changes the Phase that this system is scheduled on.
|
|
1108
|
+
function Scheduler.editSystem<U...>(
|
|
1109
|
+
self: Scheduler<U...>,
|
|
1110
|
+
system: System<U...>,
|
|
1111
|
+
newPhase: Phase
|
|
1112
|
+
)
|
|
1113
|
+
local systemFn = getSystem(system)
|
|
1114
|
+
assert(
|
|
1115
|
+
systemFn,
|
|
1116
|
+
"Unknown system passed to Scheduler:editSystem(unknown, _)"
|
|
1117
|
+
)
|
|
1118
|
+
|
|
1119
|
+
local systemInfo = self._systemInfo[systemFn]
|
|
1120
|
+
assert(
|
|
1121
|
+
systemInfo,
|
|
1122
|
+
"Attempt to edit a non-existent system in Scheduler:editSystem(system, _)"
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
assert(
|
|
1126
|
+
newPhase and self._phaseToSystems[newPhase] ~= nil or true,
|
|
1127
|
+
"Phase never initialized before using Scheduler:editSystem(_, phase)"
|
|
1128
|
+
)
|
|
1129
|
+
|
|
1130
|
+
local oldPhase = systemInfo.phase
|
|
1131
|
+
local systems = self._phaseToSystems[oldPhase]
|
|
1132
|
+
|
|
1133
|
+
local index = table.find(systems, systemFn :: InternalSystem<...any>)
|
|
1134
|
+
assert(index, "Unable to find system within phase")
|
|
1135
|
+
|
|
1136
|
+
table.remove(systems, index)
|
|
1137
|
+
|
|
1138
|
+
if not self._phaseToSystems[newPhase] then
|
|
1139
|
+
self._phaseToSystems[newPhase] = {}
|
|
1140
|
+
end
|
|
1141
|
+
table.insert(
|
|
1142
|
+
self._phaseToSystems[newPhase],
|
|
1143
|
+
systemFn :: InternalSystem<...any>
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
systemInfo.phase = newPhase
|
|
1147
|
+
self:_systemEdited(systemInfo :: SystemInfo<U...>, oldPhase, newPhase)
|
|
1148
|
+
|
|
1149
|
+
return self
|
|
1150
|
+
end
|
|
1151
|
+
|
|
1152
|
+
function Scheduler._removeCondition<U...>(
|
|
1153
|
+
self: Scheduler<U...>,
|
|
1154
|
+
dependent: Dependent<U...>,
|
|
1155
|
+
condition: Condition<U...>
|
|
1156
|
+
)
|
|
1157
|
+
self._runIfConditions[dependent :: any] = nil
|
|
1158
|
+
|
|
1159
|
+
for _, _conditions in self._runIfConditions do
|
|
1160
|
+
if table.find(_conditions :: any, condition) then
|
|
1161
|
+
return
|
|
1162
|
+
end
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
conditions.cleanupCondition(condition)
|
|
1166
|
+
end
|
|
1167
|
+
|
|
1168
|
+
--- @method removeSystem
|
|
1169
|
+
--- @within Scheduler
|
|
1170
|
+
--- @param system System
|
|
1171
|
+
--- @return Scheduler
|
|
1172
|
+
---
|
|
1173
|
+
--- Removes the System from the Scheduler.
|
|
1174
|
+
---
|
|
1175
|
+
--- If the system provided a cleanup function during initialization,
|
|
1176
|
+
--- that cleanup function will be executed before removal.
|
|
1177
|
+
---
|
|
1178
|
+
--- ```lua
|
|
1179
|
+
--- -- System with cleanup
|
|
1180
|
+
--- local function networkSystem(world, state)
|
|
1181
|
+
--- local connection = Players.PlayerAdded:Connect(function(player)
|
|
1182
|
+
--- -- Player joined logic
|
|
1183
|
+
--- end)
|
|
1184
|
+
---
|
|
1185
|
+
--- return function(world, state)
|
|
1186
|
+
--- -- Runtime logic
|
|
1187
|
+
--- end, function()
|
|
1188
|
+
--- -- Cleanup runs on removeSystem
|
|
1189
|
+
--- connection:Disconnect()
|
|
1190
|
+
--- end
|
|
1191
|
+
--- end
|
|
1192
|
+
---
|
|
1193
|
+
--- scheduler:addSystem(networkSystem, Phase.Update)
|
|
1194
|
+
--- -- Later...
|
|
1195
|
+
--- scheduler:removeSystem(networkSystem) -- Cleanup executes here
|
|
1196
|
+
--- ```
|
|
1197
|
+
function Scheduler.removeSystem<U...>(self: Scheduler<U...>, system: System<U...>)
|
|
1198
|
+
local systemFn = getSystem(system)
|
|
1199
|
+
assert(systemFn, "Invalid system passed to Scheduler:removeSystem(system)")
|
|
1200
|
+
|
|
1201
|
+
local systemInfo = self._systemInfo[systemFn]
|
|
1202
|
+
assert(
|
|
1203
|
+
systemInfo,
|
|
1204
|
+
"Attempt to remove a non-existent system in Scheduler:removeSystem(system)"
|
|
1205
|
+
)
|
|
1206
|
+
|
|
1207
|
+
if systemInfo.cleanup then
|
|
1208
|
+
local success: boolean, err: SystemLog<unknown> = xpcall<<unknown, (nil), (
|
|
1209
|
+
SystemLog<unknown>
|
|
1210
|
+
)>>(systemInfo.cleanup, function(e)
|
|
1211
|
+
local errMessage =
|
|
1212
|
+
`Cleanup failed for system '{systemInfo.name}': {e}`
|
|
1213
|
+
|
|
1214
|
+
return {
|
|
1215
|
+
e = e,
|
|
1216
|
+
log = debug.traceback(errMessage),
|
|
1217
|
+
trace = debug.traceback(),
|
|
1218
|
+
}
|
|
1219
|
+
end, table.unpack(self._vargs))
|
|
1220
|
+
|
|
1221
|
+
if success then
|
|
1222
|
+
self:_systemCleanup(systemInfo, nil)
|
|
1223
|
+
else
|
|
1224
|
+
self:_systemError(systemInfo, err)
|
|
1225
|
+
self:_systemCleanup(systemInfo, err)
|
|
1226
|
+
end
|
|
1227
|
+
end
|
|
1228
|
+
|
|
1229
|
+
local systems = self._phaseToSystems[systemInfo.phase]
|
|
1230
|
+
|
|
1231
|
+
local index = table.find(systems :: any, systemFn)
|
|
1232
|
+
assert(index, "Unable to find system within phase")
|
|
1233
|
+
|
|
1234
|
+
table.remove(systems, index)
|
|
1235
|
+
self._systemInfo[systemFn] = nil
|
|
1236
|
+
|
|
1237
|
+
if self._runIfConditions[system :: any] then
|
|
1238
|
+
for _, condition in self._runIfConditions[system :: any] do
|
|
1239
|
+
self:_removeCondition(system, condition)
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
self._runIfConditions[system :: any] = nil
|
|
1243
|
+
end
|
|
1244
|
+
|
|
1245
|
+
self:_systemRemove(systemInfo :: SystemInfo<U...>)
|
|
1246
|
+
|
|
1247
|
+
return self
|
|
1248
|
+
end
|
|
1249
|
+
|
|
1250
|
+
--- @method replaceSystem
|
|
1251
|
+
--- @within Scheduler
|
|
1252
|
+
--- @param old System
|
|
1253
|
+
--- @param new System
|
|
1254
|
+
---
|
|
1255
|
+
--- Replaces the System with a new System.
|
|
1256
|
+
function Scheduler.replaceSystem<U...>(
|
|
1257
|
+
self: Scheduler<U...>,
|
|
1258
|
+
old: System<U...>,
|
|
1259
|
+
new: System<U...>
|
|
1260
|
+
)
|
|
1261
|
+
local oldSystemFn = getSystem(old) :: InternalSystem<...any>
|
|
1262
|
+
assert(
|
|
1263
|
+
oldSystemFn,
|
|
1264
|
+
"Attempt to pass non-system in Scheduler:replaceSystem(unknown, _)"
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
local oldSystemInfo = self._systemInfo[oldSystemFn]
|
|
1268
|
+
assert(
|
|
1269
|
+
oldSystemInfo,
|
|
1270
|
+
"Attempt to replace a non-existent system in Scheduler:replaceSystem(system, _)"
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
local newSystemFn = getSystem(new) :: InternalSystem<...any>
|
|
1274
|
+
assert(
|
|
1275
|
+
newSystemFn,
|
|
1276
|
+
"Attempt to pass non-system in Scheduler:replaceSystem(_, unknown)"
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
if oldSystemInfo.cleanup then
|
|
1280
|
+
-- LUAU FUTURE: Better types for xpcalls
|
|
1281
|
+
local success: boolean, err: SystemLog<unknown> = xpcall<<unknown, (nil), (
|
|
1282
|
+
SystemLog<unknown>
|
|
1283
|
+
)>>(oldSystemInfo.cleanup, function(e)
|
|
1284
|
+
local errMessage =
|
|
1285
|
+
`Cleanup failed for system '{oldSystemInfo.name}': {e}`
|
|
1286
|
+
|
|
1287
|
+
return {
|
|
1288
|
+
e = e,
|
|
1289
|
+
log = debug.traceback(errMessage),
|
|
1290
|
+
trace = debug.traceback(),
|
|
1291
|
+
}
|
|
1292
|
+
end, table.unpack(self._vargs))
|
|
1293
|
+
|
|
1294
|
+
if success then
|
|
1295
|
+
self:_systemCleanup(oldSystemInfo, nil)
|
|
1296
|
+
else
|
|
1297
|
+
self:_systemError(oldSystemInfo, err)
|
|
1298
|
+
self:_systemCleanup(oldSystemInfo, err)
|
|
1299
|
+
end
|
|
1300
|
+
end
|
|
1301
|
+
|
|
1302
|
+
local systems = self._phaseToSystems[oldSystemInfo.phase]
|
|
1303
|
+
|
|
1304
|
+
local index = table.find(systems, oldSystemFn)
|
|
1305
|
+
assert(index, "Unable to find system within phase")
|
|
1306
|
+
|
|
1307
|
+
table.remove(systems, index)
|
|
1308
|
+
table.insert(systems, index, newSystemFn)
|
|
1309
|
+
|
|
1310
|
+
local copy = table.clone(oldSystemInfo)
|
|
1311
|
+
|
|
1312
|
+
oldSystemInfo.system = newSystemFn
|
|
1313
|
+
oldSystemInfo.run = newSystemFn
|
|
1314
|
+
oldSystemInfo.cleanup = nil
|
|
1315
|
+
oldSystemInfo.initialized = false
|
|
1316
|
+
oldSystemInfo.name = getSystemName(new)
|
|
1317
|
+
|
|
1318
|
+
self:_systemReplace(
|
|
1319
|
+
copy :: SystemInfo<U...>,
|
|
1320
|
+
oldSystemInfo :: SystemInfo<U...>
|
|
1321
|
+
)
|
|
1322
|
+
|
|
1323
|
+
self._systemInfo[newSystemFn] = self._systemInfo[oldSystemFn]
|
|
1324
|
+
self._systemInfo[oldSystemFn] = nil
|
|
1325
|
+
|
|
1326
|
+
return self
|
|
1327
|
+
end
|
|
1328
|
+
|
|
1329
|
+
--- @method addRunCondition
|
|
1330
|
+
--- @within Scheduler
|
|
1331
|
+
--- @param system System
|
|
1332
|
+
--- @param fn (U...) -> any
|
|
1333
|
+
---
|
|
1334
|
+
--- Adds a Run Condition which the Scheduler will check before
|
|
1335
|
+
--- this System is ran.
|
|
1336
|
+
|
|
1337
|
+
--- @method addRunCondition
|
|
1338
|
+
--- @within Scheduler
|
|
1339
|
+
--- @param phase Phase
|
|
1340
|
+
--- @param fn (U...) -> any
|
|
1341
|
+
---
|
|
1342
|
+
--- Adds a Run Condition which the Scheduler will check before
|
|
1343
|
+
--- any Systems within this Phase are ran.
|
|
1344
|
+
|
|
1345
|
+
--- @method addRunCondition
|
|
1346
|
+
--- @within Scheduler
|
|
1347
|
+
--- @param pipeline Pipeline
|
|
1348
|
+
--- @param fn (U...) -> any
|
|
1349
|
+
---
|
|
1350
|
+
--- Adds a Run Condition which the Scheduler will check before
|
|
1351
|
+
--- any Systems within any Phases apart of this Pipeline are ran.
|
|
1352
|
+
function Scheduler.addRunCondition<U...>(
|
|
1353
|
+
self: Scheduler<U...>,
|
|
1354
|
+
dependent: Dependent<U...>,
|
|
1355
|
+
fn: Condition<U...>
|
|
1356
|
+
)
|
|
1357
|
+
fn = if typeof(fn) == "table" then fn[1] else fn
|
|
1358
|
+
|
|
1359
|
+
local system: SystemFn<...any>? = getSystem(dependent :: any) :: any
|
|
1360
|
+
if system then
|
|
1361
|
+
dependent = system
|
|
1362
|
+
end
|
|
1363
|
+
|
|
1364
|
+
assert(
|
|
1365
|
+
system or isPhase(dependent) or isPipeline(dependent),
|
|
1366
|
+
"Attempt to pass unknown dependent into Scheduler:addRunCondition(unknown, _)"
|
|
1367
|
+
)
|
|
1368
|
+
|
|
1369
|
+
if not self._runIfConditions[dependent :: any] then
|
|
1370
|
+
self._runIfConditions[dependent :: any] = {}
|
|
1371
|
+
end
|
|
1372
|
+
|
|
1373
|
+
table.insert(self._runIfConditions[dependent :: any], fn)
|
|
1374
|
+
|
|
1375
|
+
return self
|
|
1376
|
+
end
|
|
1377
|
+
|
|
1378
|
+
function Scheduler._scheduleEvent<U...>(self: Scheduler<U...>, instance, event)
|
|
1379
|
+
local connect = utils.getConnectFn(instance, event)
|
|
1380
|
+
assert(
|
|
1381
|
+
connect,
|
|
1382
|
+
"Couldn't connect to event as no valid connect methods were found! Ensure the passed event has a 'Connect' or an 'on' method!"
|
|
1383
|
+
)
|
|
1384
|
+
|
|
1385
|
+
local identifier = getEventIdentifier(instance, event)
|
|
1386
|
+
|
|
1387
|
+
local dependencyGraph = DependencyGraph.new()
|
|
1388
|
+
|
|
1389
|
+
local callback = function()
|
|
1390
|
+
local orderedList = dependencyGraph:getOrderedList()
|
|
1391
|
+
|
|
1392
|
+
if orderedList == nil then
|
|
1393
|
+
local err =
|
|
1394
|
+
`Event Group '{identifier}' contains a circular dependency, check your Pipelines/Phases`
|
|
1395
|
+
if not recentLogs[err] then
|
|
1396
|
+
-- LUAU FUTURE: Inference bug
|
|
1397
|
+
task.spawn<<(string, number?)>>(error, err, 0)
|
|
1398
|
+
warn(
|
|
1399
|
+
`Planck: Error occurred while running event, this error will be ignored for 10 seconds`
|
|
1400
|
+
)
|
|
1401
|
+
recentLogs[err] = true
|
|
1402
|
+
end
|
|
1403
|
+
|
|
1404
|
+
return
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
for _, dependency in orderedList do
|
|
1408
|
+
self:run(dependency :: any)
|
|
1409
|
+
end
|
|
1410
|
+
end
|
|
1411
|
+
|
|
1412
|
+
self._connectedEvents[identifier] = connect(callback)
|
|
1413
|
+
self._eventDependencyGraphs[identifier] =
|
|
1414
|
+
dependencyGraph :: DependencyGraph<Dependency>
|
|
1415
|
+
end
|
|
1416
|
+
|
|
1417
|
+
function Scheduler._getEventDependencyGraph<U...>(
|
|
1418
|
+
self: Scheduler<U...>,
|
|
1419
|
+
instance,
|
|
1420
|
+
event
|
|
1421
|
+
)
|
|
1422
|
+
local identifier = getEventIdentifier(instance, event)
|
|
1423
|
+
|
|
1424
|
+
if not self._connectedEvents[identifier] then
|
|
1425
|
+
self:_scheduleEvent(instance, event)
|
|
1426
|
+
end
|
|
1427
|
+
|
|
1428
|
+
return self._eventDependencyGraphs[identifier]
|
|
1429
|
+
end
|
|
1430
|
+
|
|
1431
|
+
function Scheduler._getGraphOfDependency<U...>(
|
|
1432
|
+
self: Scheduler<U...>,
|
|
1433
|
+
dependency: Dependency
|
|
1434
|
+
)
|
|
1435
|
+
if table.find(self._defaultDependencyGraph.nodes, dependency) then
|
|
1436
|
+
return self._defaultDependencyGraph
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
for _, dependencyGraph in self._eventDependencyGraphs do
|
|
1440
|
+
if table.find(dependencyGraph.nodes, dependency) then
|
|
1441
|
+
return dependencyGraph
|
|
1442
|
+
end
|
|
1443
|
+
end
|
|
1444
|
+
|
|
1445
|
+
error("Dependency does not belong to a DependencyGraph")
|
|
1446
|
+
end
|
|
1447
|
+
|
|
1448
|
+
--- @within Scheduler
|
|
1449
|
+
---
|
|
1450
|
+
--- Disconnects all events, closes all threads, and performs
|
|
1451
|
+
--- other cleanup work.
|
|
1452
|
+
---
|
|
1453
|
+
--- :::danger
|
|
1454
|
+
--- Only use this if you intend to not use the associated
|
|
1455
|
+
--- Scheduler anymore. It will not work as intended.
|
|
1456
|
+
---
|
|
1457
|
+
--- You should dereference the scheduler object so that
|
|
1458
|
+
--- it may be garbage collected.
|
|
1459
|
+
--- :::
|
|
1460
|
+
---
|
|
1461
|
+
--- :::warning
|
|
1462
|
+
--- If you're creating a "throwaway" scheduler, you should
|
|
1463
|
+
--- not add plugins like Jabby or the Matter Debugger to it.
|
|
1464
|
+
--- These plugins are unable to properly be cleaned up, use
|
|
1465
|
+
--- them with caution.
|
|
1466
|
+
--- :::
|
|
1467
|
+
function Scheduler.cleanup<U...>(self: Scheduler<U...>)
|
|
1468
|
+
for _, connection in self._connectedEvents do
|
|
1469
|
+
utils.disconnectEvent(connection)
|
|
1470
|
+
end
|
|
1471
|
+
|
|
1472
|
+
for _, plugin in self._plugins do
|
|
1473
|
+
-- LUAU FUTURE: Type solver doesn't play nice here
|
|
1474
|
+
local cleanup: ((self: Plugin) -> ())? = plugin.cleanup
|
|
1475
|
+
if cleanup then
|
|
1476
|
+
cleanup(plugin)
|
|
1477
|
+
end
|
|
1478
|
+
end
|
|
1479
|
+
|
|
1480
|
+
if self._thread then
|
|
1481
|
+
coroutine.close(self._thread)
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
for _, _conditions in self._runIfConditions do
|
|
1485
|
+
for _, condition in _conditions do
|
|
1486
|
+
conditions.cleanupCondition(condition)
|
|
1487
|
+
end
|
|
1488
|
+
end
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1491
|
+
--- @function new
|
|
1492
|
+
--- @within Scheduler
|
|
1493
|
+
--- @param args U...
|
|
1494
|
+
---
|
|
1495
|
+
--- Creates a new Scheduler, the args passed will be passed to
|
|
1496
|
+
--- any System anytime it is ran by the Scheduler.
|
|
1497
|
+
function Scheduler.new<U...>(...: U...): Scheduler<U...>
|
|
1498
|
+
local defaultPhase = Phase.new("Default")
|
|
1499
|
+
local defaultDependencyGraph: DependencyGraph<Dependency> =
|
|
1500
|
+
DependencyGraph.new() :: any
|
|
1501
|
+
|
|
1502
|
+
defaultDependencyGraph:insert(Pipeline.Startup)
|
|
1503
|
+
defaultDependencyGraph:insert(defaultPhase)
|
|
1504
|
+
|
|
1505
|
+
local self: Scheduler<U...> = setmetatable({
|
|
1506
|
+
_defaultPhase = defaultPhase,
|
|
1507
|
+
_defaultDependencyGraph = defaultDependencyGraph,
|
|
1508
|
+
_eventDependencyGraphs = {},
|
|
1509
|
+
_connectedEvents = {},
|
|
1510
|
+
_plugins = {},
|
|
1511
|
+
_phaseToSystems = {},
|
|
1512
|
+
_hooks = {},
|
|
1513
|
+
_runIfConditions = {},
|
|
1514
|
+
_systemInfo = {},
|
|
1515
|
+
_thread = nil :: any,
|
|
1516
|
+
_currentSystem = nil :: any,
|
|
1517
|
+
_yielded = false,
|
|
1518
|
+
_vargs = { ... },
|
|
1519
|
+
}, Scheduler)
|
|
1520
|
+
|
|
1521
|
+
for _, hookName in self.Hooks :: any do
|
|
1522
|
+
if not self._hooks[hookName] then
|
|
1523
|
+
self._hooks[hookName] = {}
|
|
1524
|
+
end
|
|
1525
|
+
end
|
|
1526
|
+
|
|
1527
|
+
self:addRunCondition(Pipeline.Startup, conditions.runOnce())
|
|
1528
|
+
for _, phase in Pipeline.Startup.dependencyGraph.nodes do
|
|
1529
|
+
self:addRunCondition(phase, conditions.runOnce())
|
|
1530
|
+
end
|
|
1531
|
+
|
|
1532
|
+
return self :: Scheduler<U...>
|
|
1533
|
+
end
|
|
1534
|
+
|
|
1535
|
+
export type Scheduler<U...> = setmetatable<
|
|
1536
|
+
{
|
|
1537
|
+
_defaultPhase: Phase,
|
|
1538
|
+
_defaultDependencyGraph: DependencyGraph<Dependency>,
|
|
1539
|
+
_eventDependencyGraphs: { [string]: DependencyGraph<Dependency> },
|
|
1540
|
+
_connectedEvents: { [string]: utils.ConnectionLike },
|
|
1541
|
+
_plugins: Vec<Plugin>,
|
|
1542
|
+
-- LUAU FIXME: Should be System<U...>
|
|
1543
|
+
_phaseToSystems: Map<Phase, Vec<InternalSystem<...any>>>,
|
|
1544
|
+
_hooks: Map<HookId, Vec<(PartialHookContext) -> unknown>>,
|
|
1545
|
+
-- LUAU FIXME: Should be <Dependent<U...>, Condition<U...>>
|
|
1546
|
+
_runIfConditions: Map<Dependent<...any>, Vec<Condition<...any>>>,
|
|
1547
|
+
-- LUAU FIXME: Should be SystemInfo<U...>
|
|
1548
|
+
_systemInfo: Map<InternalSystem<U...>, SystemInfo<...any>>,
|
|
1549
|
+
_currentSystem: SystemInfo<...any>,
|
|
1550
|
+
_thread: thread?,
|
|
1551
|
+
_yielded: boolean,
|
|
1552
|
+
_vargs: { any },
|
|
1553
|
+
},
|
|
1554
|
+
typeof(Scheduler)
|
|
1555
|
+
>
|
|
1556
|
+
|
|
1557
|
+
return Scheduler
|