From f220b95d2fd9905a81dfa4fb7496514b813b04d2 Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 21 Aug 2024 17:57:40 +0200 Subject: [PATCH] Add scheduler to demo (#106) * Add example to make scheduler with phases * Add more builtin phases --- demo.rbxl | Bin 0 -> 29228 bytes demo/src/ReplicatedStorage/std/init.luau | 7 +- demo/src/ReplicatedStorage/std/scheduler.luau | 237 ++++++++++++++---- demo/src/ServerScriptService/main.server.luau | 8 +- .../ServerScriptService/systems/mobsMove.luau | 29 ++- .../ServerScriptService/systems/players.luau | 5 +- .../systems/spawnMobs.luau | 5 +- .../StarterPlayerScripts/main.client.luau | 9 +- .../StarterPlayerScripts/systems/move.luau | 5 +- .../systems/syncMobs.luau | 6 +- .../systems/syncTransforms.luau | 5 +- test/tests.luau | 64 ++--- 12 files changed, 279 insertions(+), 101 deletions(-) create mode 100644 demo.rbxl diff --git a/demo.rbxl b/demo.rbxl new file mode 100644 index 0000000000000000000000000000000000000000..b0961d8f935239eb2ee6a75a7c7fa14b23b3e81b GIT binary patch literal 29228 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 = jecs.Entity + +type System = { + callback: (world: World) -> () +} + +type Systems = { System } + + +type Events = { + RenderStepped: Systems, + Heartbeat: Systems +} + +export type Scheduler = { + components: { + Disabled: Entity, + System: Entity, + Phase: Entity, + DependsOn: Entity + }, + + collect: { + under_event: (event: Entity) -> Systems, + all: () -> Events + }, + + systems: { + begin: (events: Events) -> (), + new: (callback: (dt: number) -> (), phase: Entity) -> Entity + }, + + phases: { + RenderStepped: Entity, + Heartbeat: Entity + }, + + phase: (after: Entity) -> Entity +} + +local scheduler_new: (w: World) -> Scheduler + +do + local world + local Disabled + local System + local DependsOn + local Phase + local Event + local Name + + local RenderStepped + local Heartbeat + local PreAnimation + local PreSimulation -local function Scheduler(...) - local systems = { ... } - local systemsNames = {} - local N = #systems local system local dt - - for i, module in systems do - local sys = require(module) - systems[i] = sys - local file, line = debug.info(2, "sl") - systemsNames[sys] = `{file}->::{line}::->{debug.info(sys, "n")}` - end - local function run() - local name = systemsNames[system] - - debug.profilebegin(name) - debug.setmemorycategory(name) - system(dt) + debug.profilebegin(system.name) + system.callback(dt) debug.profileend() end + local function panic(str) + -- We don't want to interrupt the loop when we error + task.spawn(error, str) + end + local function begin(events) + local connections = {} + for event, systems in events do - local function loop(sinceLastFrame) - debug.profilebegin("loop()") + if not event then continue end + local event_name = tostring(event) + connections[event] = event:Connect(function(last) + debug.profilebegin(event_name) + for _, sys in systems do + system = sys + dt = last - for i = N, 1, -1 do - system = systems[i] + local didNotYield, why = xpcall(function() + for _ in run do end + end, debug.traceback) - dt = sinceLastFrame + if didNotYield then + continue + end - local didNotYield, why = xpcall(function() - for _ in run do end - end, debug.traceback) + if string.find(why, "thread is not yieldable") then + panic("Not allowed to yield in the systems.") + else + panic(why) + end + end + debug.profileend() + end) + end + return connections + end - if didNotYield then - continue - end + local function scheduler_collect_systems_under_phase_recursive(systems, phase) + for _, system in world:query(System):with(pair(DependsOn, phase)) do + table.insert(systems, system) + end + for dependant in world:query(Phase):with(pair(DependsOn, phase)) do + scheduler_collect_systems_under_phase_recursive(systems, dependant) + end + end - if string.find(why, "thread is not yieldable") then - N -= 1 - local name = table.remove(systems, i) - panic("Not allowed to yield in the systems." - .. "\n" - .. `System: {name} has been ejected` - ) - else - panic(why) - end + local function scheduler_collect_systems_under_event(event) + local systems = {} + scheduler_collect_systems_under_phase_recursive(systems, event) + return systems + end + + local function scheduler_collect_systems_all() + local systems = {} + for phase, event in world:query(Event):with(Phase) do + systems[event] = scheduler_collect_systems_under_event(phase) + end + return systems + end + + local function scheduler_phase_new(after) + local phase = world:entity() + world:add(phase, Phase) + local dependency = pair(DependsOn, after) + world:add(phase, dependency) + return phase + end + + local function scheduler_systems_new(callback, phase) + local system = world:entity() + local name = debug.info(callback, "n") + world:set(system, System, { callback = callback, name = name }) + world:add(system, pair(DependsOn, phase)) + return system + end + + function scheduler_new(w) + world = w + Disabled = world:component() + System = world:component() + Phase = world:component() + DependsOn = world:component() + Event = world:component() + + RenderStepped = world:component() + Heartbeat = world:component() + PreSimulation = world:component() + PreAnimation = world:component() + + local RunService = game:GetService("RunService") + if RunService:IsClient() then + world:add(RenderStepped, Phase) + world:set(RenderStepped, Event, RunService.RenderStepped) end - debug.profileend() - debug.resetmemorycategory() - end + world:add(Heartbeat, Phase) + world:set(Heartbeat, Event, RunService.Heartbeat) - return loop + world:add(PreSimulation, Phase) + world:set(PreSimulation, Event, RunService.PreSimulation) + + world:add(PreAnimation, Phase) + world:set(PreAnimation, Event, RunService.PreAnimation) + + return { + phase = scheduler_phase_new, + + phases = { + RenderStepped = RenderStepped, + PreSimulation = PreSimulation, + Heartbeat = Heartbeat, + PreAnimation = PreAnimation + }, + + world = world, + + components = { + DependsOn = DependsOn, + Disabled = Disabled, + Phase = Phase, + System = System, + }, + + collect = { + under_event = scheduler_collect_systems_under_event, + all = scheduler_collect_systems_all + }, + + systems = { + new = scheduler_systems_new, + begin = begin + } + } + end end -return Scheduler + +return { + new = scheduler_new +} diff --git a/demo/src/ServerScriptService/main.server.luau b/demo/src/ServerScriptService/main.server.luau index 6a521b2..2cb8378 100644 --- a/demo/src/ServerScriptService/main.server.luau +++ b/demo/src/ServerScriptService/main.server.luau @@ -1,4 +1,8 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") local std = require(ReplicatedStorage.std) -local loop = std.Scheduler(unpack(script.Parent.systems:GetChildren())) -game:GetService("RunService").Heartbeat:Connect(loop) +local scheduler = std.Scheduler +for _, module in script.Parent.systems:GetChildren() do + require(module)(scheduler) +end +local events = scheduler.collect.all() +scheduler.systems.begin(events) diff --git a/demo/src/ServerScriptService/systems/mobsMove.luau b/demo/src/ServerScriptService/systems/mobsMove.luau index 89c1935..45b44dd 100644 --- a/demo/src/ServerScriptService/systems/mobsMove.luau +++ b/demo/src/ServerScriptService/systems/mobsMove.luau @@ -1,4 +1,7 @@ +--!optimize 2 +--!native --!strict + local ReplicatedStorage = game:GetService("ReplicatedStorage") local blink = require(game:GetService("ServerScriptService").net) local jecs = require(ReplicatedStorage.ecs) @@ -15,25 +18,22 @@ local Player = cts.Player local Character = cts.Character local function mobsMove(dt: number) - local players = world:query(Character):with(Player) + local targets = {} + for _, character in world:query(Character):with(Player):iter() do + table.insert(targets, (character.PrimaryPart :: Part).Position) + end for mob, cf, v in world:query(Transform, Velocity):with(Mob):iter() do local p = cf.Position local target + local closest - for playerId, character in players:iter() do - local pos = (character.PrimaryPart::Part).Position - if true then - target = pos - break - end - if not target then - target = pos - elseif (p - pos).Magnitude - < (p - target).Magnitude - then + for _, pos in targets do + local distance = (p - pos).Magnitude + if not target or distance < closest then target = pos + closest = distance end end @@ -47,4 +47,7 @@ local function mobsMove(dt: number) end end -return mobsMove +return function(scheduler: std.Scheduler) + return scheduler.systems.new(mobsMove, + scheduler.phases.Heartbeat) +end diff --git a/demo/src/ServerScriptService/systems/players.luau b/demo/src/ServerScriptService/systems/players.luau index beacce9..ab7dc4a 100644 --- a/demo/src/ServerScriptService/systems/players.luau +++ b/demo/src/ServerScriptService/systems/players.luau @@ -39,4 +39,7 @@ local function players() end end -return players +return function(scheduler: std.Scheduler) + return scheduler.systems.new(players, + scheduler.phases.Heartbeat) +end diff --git a/demo/src/ServerScriptService/systems/spawnMobs.luau b/demo/src/ServerScriptService/systems/spawnMobs.luau index 748a03d..9bbbb90 100644 --- a/demo/src/ServerScriptService/systems/spawnMobs.luau +++ b/demo/src/ServerScriptService/systems/spawnMobs.luau @@ -27,4 +27,7 @@ local function spawnMobs() end end -return spawnMobs +return function(scheduler: std.Scheduler) + return scheduler.systems.new(spawnMobs, + scheduler.phases.Heartbeat) +end diff --git a/demo/src/StarterPlayer/StarterPlayerScripts/main.client.luau b/demo/src/StarterPlayer/StarterPlayerScripts/main.client.luau index 1d7b555..9a6bb0d 100644 --- a/demo/src/StarterPlayer/StarterPlayerScripts/main.client.luau +++ b/demo/src/StarterPlayer/StarterPlayerScripts/main.client.luau @@ -1,6 +1,9 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage") local std = require(ReplicatedStorage.std) +local scheduler = std.Scheduler +for _, module in script.Parent:WaitForChild("systems"):GetChildren() do + require(module)(scheduler) +end +local events = scheduler.collect.all() -print(script.Parent:WaitForChild("systems"):GetChildren()) -local loop = std.Scheduler(unpack(script.Parent:WaitForChild("systems"):GetChildren())) -game:GetService("RunService").Heartbeat:Connect(loop) +scheduler.systems.begin(events) diff --git a/demo/src/StarterPlayer/StarterPlayerScripts/systems/move.luau b/demo/src/StarterPlayer/StarterPlayerScripts/systems/move.luau index a3270b9..84f9287 100644 --- a/demo/src/StarterPlayer/StarterPlayerScripts/systems/move.luau +++ b/demo/src/StarterPlayer/StarterPlayerScripts/systems/move.luau @@ -14,4 +14,7 @@ local function move(dt: number) end end -return move +return function(scheduler: std.Scheduler) + return scheduler.systems.new(move, + scheduler.phases.RenderStepped) +end diff --git a/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncMobs.luau b/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncMobs.luau index 0c6e89f..bf73e00 100644 --- a/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncMobs.luau +++ b/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncMobs.luau @@ -22,7 +22,9 @@ local function syncMobs() :set(cts.Model, model) :add(cts.Mob) end - end -return syncMobs +return function(scheduler: std.Scheduler) + return scheduler.systems.new(syncMobs, + scheduler.phases.RenderStepped) +end diff --git a/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncTransforms.luau b/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncTransforms.luau index 6fa3064..01ba86e 100644 --- a/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncTransforms.luau +++ b/demo/src/StarterPlayer/StarterPlayerScripts/systems/syncTransforms.luau @@ -13,4 +13,7 @@ local function syncTransforms() end end -return syncTransforms +return function(scheduler: std.Scheduler) + return scheduler.systems.new(syncTransforms, + scheduler.phases.RenderStepped) +end diff --git a/test/tests.luau b/test/tests.luau index 4c0692d..31f2614 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -1264,11 +1264,12 @@ TEST("scheduler", function() DependsOn: Entity }, + collect: { + under_event: (event: Entity) -> Systems, + all: () -> Events + }, + systems: { - collect: { - under_event: (event: Entity) -> Systems, - all: () -> Events - }, run: (events: Events) -> (), new: (callback: (world: World) -> (), phase: Entity) -> Entity }, @@ -1287,8 +1288,20 @@ TEST("scheduler", function() local System local DependsOn local Phase + local Event local RenderStepped local Heartbeat + local Name + + local function scheduler_systems_run(events) + for _, system in events[RenderStepped] do + system.callback() + end + print(Heartbeat, events[Heartbeat]) + for _, system in events[Heartbeat] do + system.callback() + end + end local function scheduler_collect_systems_under_phase_recursive(systems, phase) for _, system in world:query(System):with(pair(DependsOn, phase)) do @@ -1306,24 +1319,13 @@ TEST("scheduler", function() end local function scheduler_collect_systems_all() - local systems = { - RenderStepped = scheduler_collect_systems_under_event( - RenderStepped), - Heartbeat = scheduler_collect_systems_under_event( - Heartbeat) - } + local systems = {} + for phase in world:query(Phase, Event) do + systems[phase] = scheduler_collect_systems_under_event(phase) + end return systems end - local function scheduler_run_systems(events) - for _, system in events.RenderStepped do - system.callback(world) - end - for _, system in events.Heartbeat do - system.callback(world) - end - end - local function scheduler_phase_new(after) local phase = world:entity() world:add(phase, Phase) @@ -1345,12 +1347,15 @@ TEST("scheduler", function() System = world:component() Phase = world:component() DependsOn = world:component() + Event = world:component() RenderStepped = world:component() Heartbeat = world:component() world:add(RenderStepped, Phase) + world:add(RenderStepped, Event) world:add(Heartbeat, Phase) + world:add(Heartbeat, Event) return { phase = scheduler_phase_new, @@ -1371,13 +1376,14 @@ TEST("scheduler", function() System = System, }, + collect = { + under_event = scheduler_collect_systems_under_event, + all = scheduler_collect_systems_all + }, + systems = { - run = scheduler_run_systems, - collect = { - under_event = scheduler_collect_systems_under_event, - all = scheduler_collect_systems_all - }, new = scheduler_systems_new, + run = scheduler_systems_run } } end @@ -1432,7 +1438,7 @@ TEST("scheduler", function() createSystem(hit, Collisions) createSystem(move, Physics) - local events = scheduler.systems.collect.all() + local events = scheduler.collect.all() scheduler.systems.run(events) order ..= "->END" @@ -1465,20 +1471,20 @@ TEST("scheduler", function() createSystem(move, Physics) createSystem(camera, Render) - local systems = scheduler.systems.collect.under_event(Collisions) + local systems = scheduler.collect.under_event(Collisions) CHECK(#systems == 1) CHECK(systems[1].callback == hit) - systems = scheduler.systems.collect.under_event(Physics) + systems = scheduler.collect.under_event(Physics) CHECK(#systems == 2) - systems = scheduler.systems.collect.under_event(Heartbeat) + systems = scheduler.collect.under_event(Heartbeat) CHECK(#systems == 2) - systems = scheduler.systems.collect.under_event(Render) + systems = scheduler.collect.under_event(Render) CHECK(#systems == 1) CHECK(systems[1].callback == camera)