jecs/demo/src/ReplicatedStorage/std/scheduler.luau

230 lines
6 KiB
Text
Raw Normal View History

--!native
--!optimize 2
2024-08-29 13:45:49 +00:00
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local jabby = require(ReplicatedStorage.Packages.jabby)
local jecs = require(ReplicatedStorage.ecs)
local components = require(ReplicatedStorage.std.components)
local pair = jecs.pair
type World = jecs.World
type Entity<T=nil> = jecs.Entity<T>
type System = {
2024-08-27 19:27:26 +00:00
callback: (world: World) -> (),
name: string,
rate: number?,
interval: number?
}
type Systems = { System }
type Events = {
RenderStepped: Systems,
Heartbeat: Systems
}
export type Scheduler = {
components: {
Disabled: Entity,
System: Entity<System>,
Phase: Entity,
DependsOn: Entity
},
collect: {
under_event: (event: Entity) -> Systems,
all: () -> Events
},
systems: {
2024-08-27 19:27:26 +00:00
begin: (events: Events) -> { [Entity]: thread },
new: (callback: (dt: number) -> (), phase: Entity) -> Entity
},
phases: {
RenderStepped: Entity,
Heartbeat: Entity
},
phase: (after: Entity) -> Entity
}
local scheduler_new: (w: World) -> Scheduler
2024-08-29 13:45:49 +00:00
do
local world
local Disabled
local System
local DependsOn
local Phase
local Event
local Name
2024-08-29 13:45:49 +00:00
local scheduler
local RenderStepped
local Heartbeat
local PreAnimation
local PreSimulation
2024-08-27 19:27:26 +00:00
local system: System
local dt
local function run()
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
2024-08-27 19:27:26 +00:00
local function begin(events: { Systems })
local threads = {}
for event, systems in events do
if not event then continue end
local event_name = tostring(event)
2024-08-27 19:27:26 +00:00
threads[event] = task.spawn(function()
while true do
dt = event:Wait()
debug.profilebegin(event_name)
for _, sys in systems do
2024-08-29 13:45:49 +00:00
scheduler:run(sys.id, sys.system, dt)
2024-08-27 19:27:26 +00:00
end
debug.profileend()
end
end)
end
2024-08-27 19:27:26 +00:00
return threads
end
local function scheduler_collect_systems_under_phase_recursive(systems, phase)
2024-08-29 13:45:49 +00:00
local phase_name = world:get(phase, Name)
for _, system in world:query(System):with(pair(DependsOn, phase)) do
2024-08-29 13:45:49 +00:00
table.insert(systems, {
id = scheduler:register_system({
name = system.name,
phase = phase_name
}),
system = system.callback
})
end
2024-08-27 19:27:26 +00:00
for after in world:query(Phase):with(pair(DependsOn, phase)) do
scheduler_collect_systems_under_phase_recursive(systems, after)
end
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()
2024-08-29 13:45:49 +00:00
local events = {}
for phase, event in world:query(Event):with(Phase) do
2024-08-29 13:45:49 +00:00
events[event] = scheduler_collect_systems_under_event(phase)
end
2024-08-29 13:45:49 +00:00
return events
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()
2024-08-29 13:45:49 +00:00
Name = 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
world:add(Heartbeat, Phase)
2024-08-29 13:45:49 +00:00
world:set(Heartbeat, Name)
world:set(Heartbeat, Event, RunService.Heartbeat)
world:add(PreSimulation, Phase)
world:set(PreSimulation, Event, RunService.PreSimulation)
world:add(PreAnimation, Phase)
world:set(PreAnimation, Event, RunService.PreAnimation)
2024-08-29 13:45:49 +00:00
jabby.public.updated = true
table.insert(jabby.public, {
class_name = "World",
name = "MyWorld",
world = world,
debug = Name,
entities = {}
})
for name, component in components do
world:set(component, Name, name)
print(Name, name)
end
scheduler = jabby.scheduler.create("scheduler")
table.insert(jabby.public, scheduler)
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 {
new = scheduler_new
}