@rbxts/planck 0.3.0-alpha.2 → 0.3.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/DependencyGraph.luau +45 -52
- package/src/Phase.luau +46 -41
- package/src/Pipeline.luau +105 -86
- package/src/Scheduler.d.ts +96 -3
- package/src/Scheduler.luau +713 -225
- 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,48 +1,86 @@
|
|
|
1
|
-
--!nonstrict
|
|
2
1
|
local DependencyGraph = require(script.Parent.DependencyGraph)
|
|
3
2
|
local Pipeline = require(script.Parent.Pipeline)
|
|
4
3
|
local Phase = require(script.Parent.Phase)
|
|
5
4
|
|
|
5
|
+
type DependencyGraph<T> = DependencyGraph.DependencyGraph<T>
|
|
6
|
+
type Pipeline = Pipeline.Pipeline
|
|
7
|
+
type Phase = Phase.Phase
|
|
8
|
+
|
|
6
9
|
local utils = require(script.Parent.utils)
|
|
7
|
-
local hooks = require(script.Parent.hooks)
|
|
8
10
|
local conditions = require(script.Parent.conditions)
|
|
9
11
|
|
|
10
12
|
local getSystem = utils.getSystem
|
|
11
13
|
local getSystemName = utils.getSystemName
|
|
14
|
+
local getEventIdentifier = utils.getEventIdentifier
|
|
12
15
|
|
|
16
|
+
local isSystem = utils.isSystem
|
|
13
17
|
local isPhase = utils.isPhase
|
|
14
18
|
local isPipeline = utils.isPipeline
|
|
15
|
-
|
|
16
19
|
local isValidEvent = utils.isValidEvent
|
|
17
|
-
|
|
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
|
|
18
79
|
|
|
19
80
|
-- Recent errors in Planks itself
|
|
20
81
|
local recentLogs = {}
|
|
21
82
|
local timeLastLogged = os.clock()
|
|
22
83
|
|
|
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
84
|
--- @class Scheduler
|
|
47
85
|
---
|
|
48
86
|
--- An Object which handles scheduling Systems to run within different
|
|
@@ -52,22 +90,270 @@ local timeLastLogged = os.clock()
|
|
|
52
90
|
local Scheduler = {}
|
|
53
91
|
Scheduler.__index = Scheduler
|
|
54
92
|
|
|
55
|
-
Scheduler.Hooks = hooks.Hooks
|
|
56
|
-
|
|
57
93
|
--- @method addPlugin
|
|
58
94
|
--- @within Scheduler
|
|
59
95
|
--- @param plugin PlanckPlugin
|
|
60
96
|
---
|
|
61
97
|
--- Initializes a plugin with the scheduler, see the [Plugin Docs](/docs/plugins) for more information.
|
|
62
|
-
function Scheduler
|
|
63
|
-
|
|
64
|
-
|
|
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)
|
|
65
106
|
return self
|
|
66
107
|
end
|
|
67
108
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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)
|
|
71
357
|
end
|
|
72
358
|
|
|
73
359
|
--- @method getDeltaTime
|
|
@@ -76,7 +362,7 @@ end
|
|
|
76
362
|
---
|
|
77
363
|
--- Returns the time since the system was ran last.
|
|
78
364
|
--- This must be used within a registered system.
|
|
79
|
-
function Scheduler
|
|
365
|
+
function Scheduler.getDeltaTime<U...>(self: Scheduler<U...>)
|
|
80
366
|
if self._currentSystem then
|
|
81
367
|
return self._currentSystem.deltaTime or 0
|
|
82
368
|
end
|
|
@@ -91,8 +377,35 @@ function Scheduler:getDeltaTime()
|
|
|
91
377
|
return self._systemInfo[systemFn].deltaTime or 0
|
|
92
378
|
end
|
|
93
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
|
+
|
|
94
404
|
-- Inspiration from https://github.com/matter-ecs/matter <3
|
|
95
|
-
function Scheduler
|
|
405
|
+
function Scheduler._handleLogs<U...>(
|
|
406
|
+
self: Scheduler<U...>,
|
|
407
|
+
systemInfo: SystemInfo<U...>
|
|
408
|
+
)
|
|
96
409
|
if not systemInfo.timeLastLogged then
|
|
97
410
|
systemInfo.timeLastLogged = os.clock()
|
|
98
411
|
end
|
|
@@ -101,6 +414,12 @@ function Scheduler:_handleLogs(systemInfo)
|
|
|
101
414
|
systemInfo.recentLogs = {}
|
|
102
415
|
end
|
|
103
416
|
|
|
417
|
+
-- selene: allow(incorrect_standard_library_use)
|
|
418
|
+
do
|
|
419
|
+
assert(systemInfo.timeLastLogged)
|
|
420
|
+
assert(systemInfo.recentLogs)
|
|
421
|
+
end
|
|
422
|
+
|
|
104
423
|
if os.clock() - systemInfo.timeLastLogged > 10 then
|
|
105
424
|
systemInfo.timeLastLogged = os.clock()
|
|
106
425
|
systemInfo.recentLogs = {}
|
|
@@ -108,20 +427,22 @@ function Scheduler:_handleLogs(systemInfo)
|
|
|
108
427
|
|
|
109
428
|
local name = systemInfo.name
|
|
110
429
|
|
|
111
|
-
for _,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
systemInfo.recentLogs[logMessage] = true
|
|
118
|
-
end
|
|
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
|
+
)
|
|
119
436
|
end
|
|
120
437
|
|
|
121
438
|
table.clear(systemInfo.logs)
|
|
122
439
|
end
|
|
123
440
|
|
|
124
|
-
function Scheduler
|
|
441
|
+
function Scheduler.runSystem<U...>(
|
|
442
|
+
self: Scheduler<U...>,
|
|
443
|
+
system: InternalSystem<U...>,
|
|
444
|
+
justInitialized: boolean
|
|
445
|
+
)
|
|
125
446
|
local systemInfo = self._systemInfo[system]
|
|
126
447
|
local now = os.clock()
|
|
127
448
|
|
|
@@ -133,6 +454,7 @@ function Scheduler:runSystem(system, justInitialized)
|
|
|
133
454
|
|
|
134
455
|
if justInitialized ~= true then
|
|
135
456
|
if self:_canRun(system) == false then
|
|
457
|
+
self:_systemTriedRun(systemInfo)
|
|
136
458
|
return
|
|
137
459
|
end
|
|
138
460
|
|
|
@@ -155,40 +477,54 @@ function Scheduler:runSystem(system, justInitialized)
|
|
|
155
477
|
coroutine.resume(self._thread)
|
|
156
478
|
end
|
|
157
479
|
|
|
480
|
+
-- selene: allow(incorrect_standard_library_use)
|
|
481
|
+
assert(self._thread)
|
|
482
|
+
|
|
158
483
|
local didYield = false
|
|
159
484
|
local hasSystem = false
|
|
160
485
|
|
|
161
486
|
local function systemCall()
|
|
162
487
|
local function noYield()
|
|
163
488
|
local success, errOrSys, cleanup
|
|
489
|
+
|
|
164
490
|
coroutine.resume(self._thread, function()
|
|
165
491
|
success, errOrSys, cleanup = xpcall(function()
|
|
166
492
|
return systemInfo.run(table.unpack(self._vargs))
|
|
167
493
|
end, function(e)
|
|
168
|
-
return
|
|
494
|
+
return {
|
|
495
|
+
e = e,
|
|
496
|
+
log = debug.traceback(tostring(e)),
|
|
497
|
+
trace = debug.traceback(),
|
|
498
|
+
}
|
|
169
499
|
end)
|
|
170
500
|
end)
|
|
171
501
|
|
|
172
502
|
if success == false then
|
|
173
503
|
didYield = true
|
|
504
|
+
|
|
505
|
+
-- selene: allow(incorrect_standard_library_use)
|
|
506
|
+
assert(errOrSys)
|
|
174
507
|
table.insert(systemInfo.logs, errOrSys)
|
|
175
|
-
|
|
508
|
+
self:_systemError(systemInfo, errOrSys)
|
|
509
|
+
|
|
176
510
|
return
|
|
177
511
|
end
|
|
178
512
|
|
|
179
513
|
if self._yielded then
|
|
180
514
|
didYield = true
|
|
515
|
+
|
|
181
516
|
local source, line = debug.info(self._thread, 1, "sl")
|
|
182
517
|
local errMessage = `{source}:{line}: System yielded`
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
self,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
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)
|
|
192
528
|
return
|
|
193
529
|
end
|
|
194
530
|
|
|
@@ -227,29 +563,32 @@ function Scheduler:runSystem(system, justInitialized)
|
|
|
227
563
|
end
|
|
228
564
|
end
|
|
229
565
|
|
|
230
|
-
local
|
|
231
|
-
"
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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)
|
|
240
579
|
systemInfo.initialized = true
|
|
241
580
|
end
|
|
242
581
|
end
|
|
243
582
|
|
|
244
|
-
|
|
583
|
+
self:_systemCall("SystemCall", systemInfo, noYield)
|
|
245
584
|
end
|
|
246
585
|
|
|
247
586
|
local function inner()
|
|
248
|
-
|
|
587
|
+
self:_systemCall("InnerSystemCall", systemInfo, systemCall)
|
|
249
588
|
end
|
|
250
589
|
|
|
251
590
|
local function outer()
|
|
252
|
-
|
|
591
|
+
self:_systemCall("OuterSystemCall", systemInfo, inner)
|
|
253
592
|
end
|
|
254
593
|
|
|
255
594
|
if os.clock() - timeLastLogged > 10 then
|
|
@@ -257,18 +596,18 @@ function Scheduler:runSystem(system, justInitialized)
|
|
|
257
596
|
recentLogs = {}
|
|
258
597
|
end
|
|
259
598
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
`
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
)
|
|
271
|
-
|
|
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`)
|
|
272
611
|
end
|
|
273
612
|
|
|
274
613
|
if didYield then
|
|
@@ -294,23 +633,23 @@ function Scheduler:runSystem(system, justInitialized)
|
|
|
294
633
|
end
|
|
295
634
|
end
|
|
296
635
|
|
|
297
|
-
function Scheduler
|
|
636
|
+
function Scheduler.runPhase<U...>(self: Scheduler<U...>, phase: Phase)
|
|
298
637
|
if self:_canRun(phase) == false then
|
|
299
638
|
return
|
|
300
639
|
end
|
|
301
640
|
|
|
302
|
-
|
|
641
|
+
self:_phaseBegan(phase)
|
|
303
642
|
|
|
304
643
|
if not self._phaseToSystems[phase] then
|
|
305
644
|
self._phaseToSystems[phase] = {}
|
|
306
645
|
end
|
|
307
646
|
|
|
308
647
|
for _, system in self._phaseToSystems[phase] do
|
|
309
|
-
self:runSystem(system)
|
|
648
|
+
self:runSystem(system, false)
|
|
310
649
|
end
|
|
311
650
|
end
|
|
312
651
|
|
|
313
|
-
function Scheduler
|
|
652
|
+
function Scheduler.runPipeline<U...>(self: Scheduler<U...>, pipeline: Pipeline)
|
|
314
653
|
if self:_canRun(pipeline) == false then
|
|
315
654
|
return
|
|
316
655
|
end
|
|
@@ -326,12 +665,12 @@ function Scheduler:runPipeline(pipeline)
|
|
|
326
665
|
end
|
|
327
666
|
end
|
|
328
667
|
|
|
329
|
-
function Scheduler
|
|
330
|
-
local
|
|
668
|
+
function Scheduler._canRun<U...>(self: Scheduler<U...>, dependent: Dependent<U...>)
|
|
669
|
+
local runConditions = self._runIfConditions[dependent :: any]
|
|
331
670
|
|
|
332
|
-
if
|
|
333
|
-
for _, runIf in
|
|
334
|
-
if runIf(table.unpack(self._vargs))
|
|
671
|
+
if runConditions then
|
|
672
|
+
for _, runIf in runConditions do
|
|
673
|
+
if runIf and not runIf(table.unpack(self._vargs)) then
|
|
335
674
|
return false
|
|
336
675
|
end
|
|
337
676
|
end
|
|
@@ -360,19 +699,20 @@ end
|
|
|
360
699
|
--- @return Scheduler
|
|
361
700
|
---
|
|
362
701
|
--- Runs the System, passing in the arguments of the Scheduler, `U...`.
|
|
363
|
-
function Scheduler
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
+
)
|
|
367
707
|
|
|
368
708
|
self:runPipeline(Pipeline.Startup)
|
|
369
709
|
|
|
370
|
-
if
|
|
371
|
-
self:runSystem(dependent)
|
|
710
|
+
if isSystem(dependent) then
|
|
711
|
+
self:runSystem(dependent :: any, false)
|
|
372
712
|
elseif isPhase(dependent) then
|
|
373
|
-
self:runPhase(dependent)
|
|
713
|
+
self:runPhase(dependent :: any)
|
|
374
714
|
elseif isPipeline(dependent) then
|
|
375
|
-
self:runPipeline(dependent)
|
|
715
|
+
self:runPipeline(dependent :: any)
|
|
376
716
|
else
|
|
377
717
|
error("Unknown dependent passed into Scheduler:run(unknown)")
|
|
378
718
|
end
|
|
@@ -398,7 +738,7 @@ end
|
|
|
398
738
|
--- Pipelines/Phases in these groups are still ordered by their dependencies
|
|
399
739
|
--- and by the order of insertion.
|
|
400
740
|
--- :::
|
|
401
|
-
function Scheduler
|
|
741
|
+
function Scheduler.runAll<U...>(self: Scheduler<U...>)
|
|
402
742
|
local orderedDefaults = self._defaultDependencyGraph:getOrderedList()
|
|
403
743
|
assert(
|
|
404
744
|
orderedDefaults,
|
|
@@ -412,9 +752,10 @@ function Scheduler:runAll()
|
|
|
412
752
|
for identifier, dependencyGraph in self._eventDependencyGraphs do
|
|
413
753
|
local orderedList = dependencyGraph:getOrderedList()
|
|
414
754
|
assert(
|
|
415
|
-
|
|
755
|
+
orderedList,
|
|
416
756
|
`Event Group '{identifier}' contains a circular dependency, check your Pipelines/Phases`
|
|
417
757
|
)
|
|
758
|
+
|
|
418
759
|
for _, dependency in orderedList do
|
|
419
760
|
self:run(dependency)
|
|
420
761
|
end
|
|
@@ -423,6 +764,52 @@ function Scheduler:runAll()
|
|
|
423
764
|
return self
|
|
424
765
|
end
|
|
425
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
|
+
|
|
426
813
|
--- @method insert
|
|
427
814
|
--- @within Scheduler
|
|
428
815
|
--- @param phase Phase
|
|
@@ -472,7 +859,12 @@ end
|
|
|
472
859
|
--- local myScheduler = Scheduler.new()
|
|
473
860
|
--- :insert(myPipeline, RunService, "Heartbeat")
|
|
474
861
|
--- ```
|
|
475
|
-
|
|
862
|
+
local insertFn: InsertFn = function<U...>(
|
|
863
|
+
self: Scheduler<U...>,
|
|
864
|
+
dependency: Phase | Pipeline,
|
|
865
|
+
instance: any,
|
|
866
|
+
event: any
|
|
867
|
+
): Scheduler<U...>
|
|
476
868
|
assert(
|
|
477
869
|
isPhase(dependency) or isPipeline(dependency),
|
|
478
870
|
"Unknown dependency passed to Scheduler:insert(unknown, _, _)"
|
|
@@ -492,13 +884,15 @@ function Scheduler:insert(dependency, instance, event)
|
|
|
492
884
|
end
|
|
493
885
|
|
|
494
886
|
if isPhase(dependency) then
|
|
495
|
-
self._phaseToSystems[dependency] = {}
|
|
496
|
-
|
|
887
|
+
self._phaseToSystems[dependency :: Phase] = {}
|
|
888
|
+
self:_phaseAdd(dependency :: Phase)
|
|
497
889
|
end
|
|
498
890
|
|
|
499
891
|
return self
|
|
500
892
|
end
|
|
501
893
|
|
|
894
|
+
Scheduler.insert = insertFn
|
|
895
|
+
|
|
502
896
|
--- @method insertAfter
|
|
503
897
|
--- @within Scheduler
|
|
504
898
|
--- @param phase Phase
|
|
@@ -517,7 +911,11 @@ end
|
|
|
517
911
|
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
518
912
|
--- ordering the Pipeline explicitly by setting the after Phase/Pipeline
|
|
519
913
|
--- as a dependent.
|
|
520
|
-
function Scheduler
|
|
914
|
+
function Scheduler.insertAfter<U...>(
|
|
915
|
+
self: Scheduler<U...>,
|
|
916
|
+
dependent: Dependency,
|
|
917
|
+
after: Dependency
|
|
918
|
+
)
|
|
521
919
|
assert(
|
|
522
920
|
isPhase(after) or isPipeline(after),
|
|
523
921
|
"Unknown dependency passed in Scheduler:insertAfter(_, unknown)"
|
|
@@ -531,8 +929,8 @@ function Scheduler:insertAfter(dependent, after)
|
|
|
531
929
|
dependencyGraph:insertAfter(dependent, after)
|
|
532
930
|
|
|
533
931
|
if isPhase(dependent) then
|
|
534
|
-
self._phaseToSystems[dependent] = {}
|
|
535
|
-
|
|
932
|
+
self._phaseToSystems[dependent :: Phase] = {}
|
|
933
|
+
self:_phaseAdd(dependent :: Phase)
|
|
536
934
|
end
|
|
537
935
|
|
|
538
936
|
return self
|
|
@@ -556,7 +954,11 @@ end
|
|
|
556
954
|
--- Initializes the Pipeline and it's Phases within the Scheduler,
|
|
557
955
|
--- ordering the Pipeline explicitly by setting the before Phase/Pipeline
|
|
558
956
|
--- as a dependency.
|
|
559
|
-
function Scheduler
|
|
957
|
+
function Scheduler.insertBefore<U...>(
|
|
958
|
+
self: Scheduler<U...>,
|
|
959
|
+
dependent: Dependency,
|
|
960
|
+
before: Dependency
|
|
961
|
+
)
|
|
560
962
|
assert(
|
|
561
963
|
isPhase(before) or isPipeline(before),
|
|
562
964
|
"Unknown dependency passed in Scheduler:insertBefore(_, unknown)"
|
|
@@ -570,8 +972,8 @@ function Scheduler:insertBefore(dependent, before)
|
|
|
570
972
|
dependencyGraph:insertBefore(dependent, before)
|
|
571
973
|
|
|
572
974
|
if isPhase(dependent) then
|
|
573
|
-
self._phaseToSystems[dependent] = {}
|
|
574
|
-
|
|
975
|
+
self._phaseToSystems[dependent :: Phase] = {}
|
|
976
|
+
self:_phaseAdd(dependent :: Phase)
|
|
575
977
|
end
|
|
576
978
|
|
|
577
979
|
return self
|
|
@@ -605,12 +1007,13 @@ end
|
|
|
605
1007
|
--- end
|
|
606
1008
|
--- end
|
|
607
1009
|
--- ```
|
|
608
|
-
function Scheduler
|
|
1010
|
+
function Scheduler.addSystem<U...>(
|
|
1011
|
+
self: Scheduler<U...>,
|
|
1012
|
+
system: System<U...>,
|
|
1013
|
+
phase: Phase?
|
|
1014
|
+
)
|
|
609
1015
|
local systemFn = getSystem(system)
|
|
610
|
-
|
|
611
|
-
if not systemFn then
|
|
612
|
-
error("Unknown system passed to Scheduler:addSystem(unknown, phase?)")
|
|
613
|
-
end
|
|
1016
|
+
assert(systemFn, "Unknown system passed to Scheduler:addSystem(unknown, _)")
|
|
614
1017
|
|
|
615
1018
|
local name = getSystemName(system)
|
|
616
1019
|
|
|
@@ -623,7 +1026,7 @@ function Scheduler:addSystem(system, phase)
|
|
|
623
1026
|
scheduledPhase = self._defaultPhase
|
|
624
1027
|
end
|
|
625
1028
|
|
|
626
|
-
local systemInfo = {
|
|
1029
|
+
local systemInfo: SystemInfo<U...> = {
|
|
627
1030
|
system = systemFn,
|
|
628
1031
|
run = systemFn,
|
|
629
1032
|
cleanup = nil,
|
|
@@ -633,15 +1036,18 @@ function Scheduler:addSystem(system, phase)
|
|
|
633
1036
|
initialized = false,
|
|
634
1037
|
}
|
|
635
1038
|
|
|
636
|
-
self._systemInfo[systemFn] = systemInfo
|
|
1039
|
+
self._systemInfo[systemFn] = systemInfo :: any
|
|
637
1040
|
|
|
638
1041
|
if not self._phaseToSystems[systemInfo.phase] then
|
|
639
1042
|
self._phaseToSystems[systemInfo.phase] = {}
|
|
640
1043
|
end
|
|
641
1044
|
|
|
642
|
-
table.insert(
|
|
1045
|
+
table.insert(
|
|
1046
|
+
self._phaseToSystems[systemInfo.phase],
|
|
1047
|
+
systemFn :: InternalSystem<...any>
|
|
1048
|
+
)
|
|
643
1049
|
|
|
644
|
-
|
|
1050
|
+
self:_systemAdd(systemInfo)
|
|
645
1051
|
|
|
646
1052
|
if type(system) == "table" and system.runConditions then
|
|
647
1053
|
for _, condition in system.runConditions do
|
|
@@ -662,10 +1068,15 @@ end
|
|
|
662
1068
|
---
|
|
663
1069
|
--- Adds the Systems to the Scheduler, scheduling them to be ran
|
|
664
1070
|
--- implicitly within the provided Phase or on the default Main phase.
|
|
665
|
-
function Scheduler
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
+
)
|
|
669
1080
|
|
|
670
1081
|
local foundSystem = false
|
|
671
1082
|
local n = 0
|
|
@@ -678,15 +1089,12 @@ function Scheduler:addSystems(systems, phase)
|
|
|
678
1089
|
end
|
|
679
1090
|
end
|
|
680
1091
|
|
|
681
|
-
|
|
682
|
-
error("Empty table passed to Scheduler:addSystems({ }, phase?)")
|
|
683
|
-
end
|
|
1092
|
+
assert(n ~= 0, "Empty table passed to Scheduler:addSystems({ }, _)")
|
|
684
1093
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
end
|
|
1094
|
+
assert(
|
|
1095
|
+
foundSystem,
|
|
1096
|
+
"Table containing unknown values passed to Scheduler:addSystems({ unknown }, _)"
|
|
1097
|
+
)
|
|
690
1098
|
|
|
691
1099
|
return self
|
|
692
1100
|
end
|
|
@@ -697,22 +1105,32 @@ end
|
|
|
697
1105
|
--- @param newPhase Phase
|
|
698
1106
|
---
|
|
699
1107
|
--- Changes the Phase that this system is scheduled on.
|
|
700
|
-
function Scheduler
|
|
1108
|
+
function Scheduler.editSystem<U...>(
|
|
1109
|
+
self: Scheduler<U...>,
|
|
1110
|
+
system: System<U...>,
|
|
1111
|
+
newPhase: Phase
|
|
1112
|
+
)
|
|
701
1113
|
local systemFn = getSystem(system)
|
|
1114
|
+
assert(
|
|
1115
|
+
systemFn,
|
|
1116
|
+
"Unknown system passed to Scheduler:editSystem(unknown, _)"
|
|
1117
|
+
)
|
|
1118
|
+
|
|
702
1119
|
local systemInfo = self._systemInfo[systemFn]
|
|
703
1120
|
assert(
|
|
704
1121
|
systemInfo,
|
|
705
|
-
"Attempt to edit a non-existent system in Scheduler:editSystem(_)"
|
|
1122
|
+
"Attempt to edit a non-existent system in Scheduler:editSystem(system, _)"
|
|
706
1123
|
)
|
|
707
1124
|
|
|
708
1125
|
assert(
|
|
709
1126
|
newPhase and self._phaseToSystems[newPhase] ~= nil or true,
|
|
710
|
-
"Phase never initialized before using Scheduler:editSystem(_,
|
|
1127
|
+
"Phase never initialized before using Scheduler:editSystem(_, phase)"
|
|
711
1128
|
)
|
|
712
1129
|
|
|
713
|
-
local
|
|
1130
|
+
local oldPhase = systemInfo.phase
|
|
1131
|
+
local systems = self._phaseToSystems[oldPhase]
|
|
714
1132
|
|
|
715
|
-
local index = table.find(systems, systemFn)
|
|
1133
|
+
local index = table.find(systems, systemFn :: InternalSystem<...any>)
|
|
716
1134
|
assert(index, "Unable to find system within phase")
|
|
717
1135
|
|
|
718
1136
|
table.remove(systems, index)
|
|
@@ -720,17 +1138,26 @@ function Scheduler:editSystem(system, newPhase)
|
|
|
720
1138
|
if not self._phaseToSystems[newPhase] then
|
|
721
1139
|
self._phaseToSystems[newPhase] = {}
|
|
722
1140
|
end
|
|
723
|
-
table.insert(
|
|
1141
|
+
table.insert(
|
|
1142
|
+
self._phaseToSystems[newPhase],
|
|
1143
|
+
systemFn :: InternalSystem<...any>
|
|
1144
|
+
)
|
|
724
1145
|
|
|
725
1146
|
systemInfo.phase = newPhase
|
|
1147
|
+
self:_systemEdited(systemInfo :: SystemInfo<U...>, oldPhase, newPhase)
|
|
1148
|
+
|
|
726
1149
|
return self
|
|
727
1150
|
end
|
|
728
1151
|
|
|
729
|
-
function Scheduler
|
|
730
|
-
self
|
|
1152
|
+
function Scheduler._removeCondition<U...>(
|
|
1153
|
+
self: Scheduler<U...>,
|
|
1154
|
+
dependent: Dependent<U...>,
|
|
1155
|
+
condition: Condition<U...>
|
|
1156
|
+
)
|
|
1157
|
+
self._runIfConditions[dependent :: any] = nil
|
|
731
1158
|
|
|
732
1159
|
for _, _conditions in self._runIfConditions do
|
|
733
|
-
if table.find(_conditions, condition) then
|
|
1160
|
+
if table.find(_conditions :: any, condition) then
|
|
734
1161
|
return
|
|
735
1162
|
end
|
|
736
1163
|
end
|
|
@@ -767,47 +1194,55 @@ end
|
|
|
767
1194
|
--- -- Later...
|
|
768
1195
|
--- scheduler:removeSystem(networkSystem) -- Cleanup executes here
|
|
769
1196
|
--- ```
|
|
770
|
-
function Scheduler
|
|
1197
|
+
function Scheduler.removeSystem<U...>(self: Scheduler<U...>, system: System<U...>)
|
|
771
1198
|
local systemFn = getSystem(system)
|
|
1199
|
+
assert(systemFn, "Invalid system passed to Scheduler:removeSystem(system)")
|
|
1200
|
+
|
|
772
1201
|
local systemInfo = self._systemInfo[systemFn]
|
|
773
1202
|
assert(
|
|
774
1203
|
systemInfo,
|
|
775
|
-
"Attempt to remove a non-existent system in Scheduler:removeSystem(
|
|
1204
|
+
"Attempt to remove a non-existent system in Scheduler:removeSystem(system)"
|
|
776
1205
|
)
|
|
777
1206
|
|
|
778
1207
|
if systemInfo.cleanup then
|
|
779
|
-
local success, err =
|
|
780
|
-
|
|
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
|
+
|
|
781
1221
|
if success then
|
|
782
|
-
|
|
1222
|
+
self:_systemCleanup(systemInfo, nil)
|
|
783
1223
|
else
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
systemInfo.name,
|
|
787
|
-
tostring(err)
|
|
788
|
-
)
|
|
789
|
-
hooks.systemError(self, systemInfo, errMsg)
|
|
790
|
-
hooks.systemCleanup(self, systemInfo, errMsg)
|
|
1224
|
+
self:_systemError(systemInfo, err)
|
|
1225
|
+
self:_systemCleanup(systemInfo, err)
|
|
791
1226
|
end
|
|
792
1227
|
end
|
|
793
1228
|
|
|
794
1229
|
local systems = self._phaseToSystems[systemInfo.phase]
|
|
795
1230
|
|
|
796
|
-
local index = table.find(systems, systemFn)
|
|
1231
|
+
local index = table.find(systems :: any, systemFn)
|
|
797
1232
|
assert(index, "Unable to find system within phase")
|
|
798
1233
|
|
|
799
1234
|
table.remove(systems, index)
|
|
800
1235
|
self._systemInfo[systemFn] = nil
|
|
801
1236
|
|
|
802
|
-
if self._runIfConditions[system] then
|
|
803
|
-
for _, condition in self._runIfConditions[system] do
|
|
1237
|
+
if self._runIfConditions[system :: any] then
|
|
1238
|
+
for _, condition in self._runIfConditions[system :: any] do
|
|
804
1239
|
self:_removeCondition(system, condition)
|
|
805
1240
|
end
|
|
806
1241
|
|
|
807
|
-
self._runIfConditions[system] = nil
|
|
1242
|
+
self._runIfConditions[system :: any] = nil
|
|
808
1243
|
end
|
|
809
1244
|
|
|
810
|
-
|
|
1245
|
+
self:_systemRemove(systemInfo :: SystemInfo<U...>)
|
|
811
1246
|
|
|
812
1247
|
return self
|
|
813
1248
|
end
|
|
@@ -818,33 +1253,49 @@ end
|
|
|
818
1253
|
--- @param new System
|
|
819
1254
|
---
|
|
820
1255
|
--- Replaces the System with a new System.
|
|
821
|
-
function Scheduler
|
|
822
|
-
|
|
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
|
+
|
|
823
1267
|
local oldSystemInfo = self._systemInfo[oldSystemFn]
|
|
824
1268
|
assert(
|
|
825
1269
|
oldSystemInfo,
|
|
826
|
-
"Attempt to replace a non-existent system in Scheduler:replaceSystem(
|
|
1270
|
+
"Attempt to replace a non-existent system in Scheduler:replaceSystem(system, _)"
|
|
827
1271
|
)
|
|
828
1272
|
|
|
829
|
-
local newSystemFn = getSystem(new)
|
|
1273
|
+
local newSystemFn = getSystem(new) :: InternalSystem<...any>
|
|
830
1274
|
assert(
|
|
831
1275
|
newSystemFn,
|
|
832
1276
|
"Attempt to pass non-system in Scheduler:replaceSystem(_, unknown)"
|
|
833
1277
|
)
|
|
834
1278
|
|
|
835
1279
|
if oldSystemInfo.cleanup then
|
|
836
|
-
|
|
837
|
-
|
|
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
|
+
|
|
838
1294
|
if success then
|
|
839
|
-
|
|
1295
|
+
self:_systemCleanup(oldSystemInfo, nil)
|
|
840
1296
|
else
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
oldSystemInfo.name,
|
|
844
|
-
tostring(err)
|
|
845
|
-
)
|
|
846
|
-
hooks.systemError(self, oldSystemInfo, errMsg)
|
|
847
|
-
hooks.systemCleanup(self, oldSystemInfo, errMsg)
|
|
1297
|
+
self:_systemError(oldSystemInfo, err)
|
|
1298
|
+
self:_systemCleanup(oldSystemInfo, err)
|
|
848
1299
|
end
|
|
849
1300
|
end
|
|
850
1301
|
|
|
@@ -864,7 +1315,10 @@ function Scheduler:replaceSystem(old, new)
|
|
|
864
1315
|
oldSystemInfo.initialized = false
|
|
865
1316
|
oldSystemInfo.name = getSystemName(new)
|
|
866
1317
|
|
|
867
|
-
|
|
1318
|
+
self:_systemReplace(
|
|
1319
|
+
copy :: SystemInfo<U...>,
|
|
1320
|
+
oldSystemInfo :: SystemInfo<U...>
|
|
1321
|
+
)
|
|
868
1322
|
|
|
869
1323
|
self._systemInfo[newSystemFn] = self._systemInfo[oldSystemFn]
|
|
870
1324
|
self._systemInfo[oldSystemFn] = nil
|
|
@@ -875,7 +1329,7 @@ end
|
|
|
875
1329
|
--- @method addRunCondition
|
|
876
1330
|
--- @within Scheduler
|
|
877
1331
|
--- @param system System
|
|
878
|
-
--- @param fn (U...) ->
|
|
1332
|
+
--- @param fn (U...) -> any
|
|
879
1333
|
---
|
|
880
1334
|
--- Adds a Run Condition which the Scheduler will check before
|
|
881
1335
|
--- this System is ran.
|
|
@@ -883,7 +1337,7 @@ end
|
|
|
883
1337
|
--- @method addRunCondition
|
|
884
1338
|
--- @within Scheduler
|
|
885
1339
|
--- @param phase Phase
|
|
886
|
-
--- @param fn (U...) ->
|
|
1340
|
+
--- @param fn (U...) -> any
|
|
887
1341
|
---
|
|
888
1342
|
--- Adds a Run Condition which the Scheduler will check before
|
|
889
1343
|
--- any Systems within this Phase are ran.
|
|
@@ -891,14 +1345,18 @@ end
|
|
|
891
1345
|
--- @method addRunCondition
|
|
892
1346
|
--- @within Scheduler
|
|
893
1347
|
--- @param pipeline Pipeline
|
|
894
|
-
--- @param fn (U...) ->
|
|
1348
|
+
--- @param fn (U...) -> any
|
|
895
1349
|
---
|
|
896
1350
|
--- Adds a Run Condition which the Scheduler will check before
|
|
897
1351
|
--- any Systems within any Phases apart of this Pipeline are ran.
|
|
898
|
-
function Scheduler
|
|
1352
|
+
function Scheduler.addRunCondition<U...>(
|
|
1353
|
+
self: Scheduler<U...>,
|
|
1354
|
+
dependent: Dependent<U...>,
|
|
1355
|
+
fn: Condition<U...>
|
|
1356
|
+
)
|
|
899
1357
|
fn = if typeof(fn) == "table" then fn[1] else fn
|
|
900
1358
|
|
|
901
|
-
local system = getSystem(dependent)
|
|
1359
|
+
local system: SystemFn<...any>? = getSystem(dependent :: any) :: any
|
|
902
1360
|
if system then
|
|
903
1361
|
dependent = system
|
|
904
1362
|
end
|
|
@@ -908,30 +1366,17 @@ function Scheduler:addRunCondition(dependent, fn)
|
|
|
908
1366
|
"Attempt to pass unknown dependent into Scheduler:addRunCondition(unknown, _)"
|
|
909
1367
|
)
|
|
910
1368
|
|
|
911
|
-
if not self._runIfConditions[dependent] then
|
|
912
|
-
self._runIfConditions[dependent] = {}
|
|
1369
|
+
if not self._runIfConditions[dependent :: any] then
|
|
1370
|
+
self._runIfConditions[dependent :: any] = {}
|
|
913
1371
|
end
|
|
914
1372
|
|
|
915
|
-
table.insert(self._runIfConditions[dependent], fn)
|
|
1373
|
+
table.insert(self._runIfConditions[dependent :: any], fn)
|
|
916
1374
|
|
|
917
1375
|
return self
|
|
918
1376
|
end
|
|
919
1377
|
|
|
920
|
-
function Scheduler:
|
|
921
|
-
|
|
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)
|
|
1378
|
+
function Scheduler._scheduleEvent<U...>(self: Scheduler<U...>, instance, event)
|
|
1379
|
+
local connect = utils.getConnectFn(instance, event)
|
|
935
1380
|
assert(
|
|
936
1381
|
connect,
|
|
937
1382
|
"Couldn't connect to event as no valid connect methods were found! Ensure the passed event has a 'Connect' or an 'on' method!"
|
|
@@ -948,24 +1393,32 @@ function Scheduler:_scheduleEvent(instance, event)
|
|
|
948
1393
|
local err =
|
|
949
1394
|
`Event Group '{identifier}' contains a circular dependency, check your Pipelines/Phases`
|
|
950
1395
|
if not recentLogs[err] then
|
|
951
|
-
|
|
1396
|
+
-- LUAU FUTURE: Inference bug
|
|
1397
|
+
task.spawn<<(string, number?)>>(error, err, 0)
|
|
952
1398
|
warn(
|
|
953
1399
|
`Planck: Error occurred while running event, this error will be ignored for 10 seconds`
|
|
954
1400
|
)
|
|
955
1401
|
recentLogs[err] = true
|
|
956
1402
|
end
|
|
1403
|
+
|
|
1404
|
+
return
|
|
957
1405
|
end
|
|
958
1406
|
|
|
959
1407
|
for _, dependency in orderedList do
|
|
960
|
-
self:run(dependency)
|
|
1408
|
+
self:run(dependency :: any)
|
|
961
1409
|
end
|
|
962
1410
|
end
|
|
963
1411
|
|
|
964
1412
|
self._connectedEvents[identifier] = connect(callback)
|
|
965
|
-
self._eventDependencyGraphs[identifier] =
|
|
1413
|
+
self._eventDependencyGraphs[identifier] =
|
|
1414
|
+
dependencyGraph :: DependencyGraph<Dependency>
|
|
966
1415
|
end
|
|
967
1416
|
|
|
968
|
-
function Scheduler
|
|
1417
|
+
function Scheduler._getEventDependencyGraph<U...>(
|
|
1418
|
+
self: Scheduler<U...>,
|
|
1419
|
+
instance,
|
|
1420
|
+
event
|
|
1421
|
+
)
|
|
969
1422
|
local identifier = getEventIdentifier(instance, event)
|
|
970
1423
|
|
|
971
1424
|
if not self._connectedEvents[identifier] then
|
|
@@ -975,7 +1428,10 @@ function Scheduler:_getEventDependencyGraph(instance, event)
|
|
|
975
1428
|
return self._eventDependencyGraphs[identifier]
|
|
976
1429
|
end
|
|
977
1430
|
|
|
978
|
-
function Scheduler
|
|
1431
|
+
function Scheduler._getGraphOfDependency<U...>(
|
|
1432
|
+
self: Scheduler<U...>,
|
|
1433
|
+
dependency: Dependency
|
|
1434
|
+
)
|
|
979
1435
|
if table.find(self._defaultDependencyGraph.nodes, dependency) then
|
|
980
1436
|
return self._defaultDependencyGraph
|
|
981
1437
|
end
|
|
@@ -1008,14 +1464,16 @@ end
|
|
|
1008
1464
|
--- These plugins are unable to properly be cleaned up, use
|
|
1009
1465
|
--- them with caution.
|
|
1010
1466
|
--- :::
|
|
1011
|
-
function Scheduler
|
|
1467
|
+
function Scheduler.cleanup<U...>(self: Scheduler<U...>)
|
|
1012
1468
|
for _, connection in self._connectedEvents do
|
|
1013
1469
|
utils.disconnectEvent(connection)
|
|
1014
1470
|
end
|
|
1015
1471
|
|
|
1016
1472
|
for _, plugin in self._plugins do
|
|
1017
|
-
|
|
1018
|
-
|
|
1473
|
+
-- LUAU FUTURE: Type solver doesn't play nice here
|
|
1474
|
+
local cleanup: ((self: Plugin) -> ())? = plugin.cleanup
|
|
1475
|
+
if cleanup then
|
|
1476
|
+
cleanup(plugin)
|
|
1019
1477
|
end
|
|
1020
1478
|
end
|
|
1021
1479
|
|
|
@@ -1036,34 +1494,64 @@ end
|
|
|
1036
1494
|
---
|
|
1037
1495
|
--- Creates a new Scheduler, the args passed will be passed to
|
|
1038
1496
|
--- any System anytime it is ran by the Scheduler.
|
|
1039
|
-
function Scheduler.new(...)
|
|
1040
|
-
local
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
self
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
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
|
|
1059
1522
|
if not self._hooks[hookName] then
|
|
1060
1523
|
self._hooks[hookName] = {}
|
|
1061
1524
|
end
|
|
1062
1525
|
end
|
|
1063
1526
|
|
|
1064
|
-
self:
|
|
1527
|
+
self:addRunCondition(Pipeline.Startup, conditions.runOnce())
|
|
1528
|
+
for _, phase in Pipeline.Startup.dependencyGraph.nodes do
|
|
1529
|
+
self:addRunCondition(phase, conditions.runOnce())
|
|
1530
|
+
end
|
|
1065
1531
|
|
|
1066
|
-
return self
|
|
1532
|
+
return self :: Scheduler<U...>
|
|
1067
1533
|
end
|
|
1068
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
|
+
|
|
1069
1557
|
return Scheduler
|