Fix upvalues conflict

This commit is contained in:
Ukendio 2024-08-02 16:33:14 +02:00
parent 219d0e08c4
commit 00627c6eee

View file

@ -6,44 +6,44 @@ local jecs = require(game:GetService("ReplicatedStorage").ecs)
type World = jecs.WorldShim type World = jecs.WorldShim
type Entity<T = any> = jecs.Entity<T> type Entity<T = any> = jecs.Entity<T>
local function LOOP_ERROR(str) local function panic(str)
-- We don't want to interrupt the loop when we error -- We don't want to interrupt the loop when we error
task.spawn(error, str) task.spawn(error, str)
end end
local Scheduler: (World, ...ModuleScript) -> (number) -> () local function Scheduler(world, ...)
do local systems = { ... }
local systems local systemsNames = {}
local N local N = #systems
local w
local dt
local systemsNames
local system local system
local dt
local function profile() 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] local name = systemsNames[system]
debug.profilebegin(name) debug.profilebegin(name)
debug.setmemorycategory(name) debug.setmemorycategory(name)
system(w, dt) system(world, dt)
debug.profileend() debug.profileend()
end end
local function run(sys)
system = sys
return profile
end
local function loop(sinceLastFrame) local function loop(sinceLastFrame)
debug.profilebegin("loop()") debug.profilebegin("loop()")
for i = N, 1, -1 do for i = N, 1, -1 do
local system = systems[i] system = systems[i]
dt = sinceLastFrame dt = sinceLastFrame
local didNotYield, why = xpcall(function() local didNotYield, why = xpcall(function()
for _ in run(system) do end for _ in run do end
end, debug.traceback) end, debug.traceback)
if didNotYield then if didNotYield then
@ -52,15 +52,13 @@ do
if string.find(why, "thread is not yieldable") then if string.find(why, "thread is not yieldable") then
N -= 1 N -= 1
table.remove(systems, i) local name = table.remove(systems, i)
LOOP_ERROR("Not allowed to yield in the systems." panic("Not allowed to yield in the systems."
.. "\n" .. "\n"
.. "System: " .. `System: {name} has been ejected`
.. debug.info(system, "n")
.. " has been ejected"
) )
else else
LOOP_ERROR(why) panic(why)
end end
end end
@ -68,19 +66,7 @@ do
debug.resetmemorycategory() debug.resetmemorycategory()
end end
function Scheduler(world, ...) return loop
systems = { ... }
systemsNames = {}
N = #systems
w = world
for i, system in systems do
systems[i] = require(system)
systemsNames[system] = debug.info(system, "n")
end
return loop
end
end end
type Tracker<T> = { track: (world: World, fn: (changes: { type Tracker<T> = { track: (world: World, fn: (changes: {
@ -89,71 +75,78 @@ type Tracker<T> = { track: (world: World, fn: (changes: {
changed: () -> () -> (number, T, T) changed: () -> () -> (number, T, T)
}) -> ()) -> () }) -> ()) -> ()
} }
local ChangeTracker: <T>(world: any, component: Entity<T>) -> Tracker<T>
do type Entity<T = any> = number & { __nominal_type_dont_use: T }
local world: World
local T local function diff(a, b)
local PreviousT local size = 0
local addedComponents for k, v in a do
local removedComponents if b[k] ~= v then
local isTrivial return true
end
size += 1
end
for k, v in b do
size -= 1
end
if size ~= 0 then
return true
end
return false
end
local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
local PreviousT = jecs.pair(jecs.Rest, T)
local add = {}
local added local added
local removed local removed
local is_trivial
local function changes_added() local function changes_added()
added = true added = true
local q = world:query(T):without(PreviousT) local q = world:query(T):without(PreviousT):drain()
return function() return function()
local id, data = q:next() local id, data = q.next()
if not id then if not id then
return nil return nil
end end
if isTrivial == nil then is_trivial = typeof(data) ~= "table"
isTrivial = typeof(data) ~= "table"
end
if not isTrivial then add[id] = data
data = table.clone(data)
end
addedComponents[id] = data
return id, data return id, data
end end
end end
local function shallowEq(a, b)
for k, v in a do
if b[k] ~= v then
return false
end
end
return true
end
local function changes_changed() local function changes_changed()
local q = world:query(T, PreviousT) local q = world:query(T, PreviousT):drain()
return function() return function()
local id, new, old = q:next() local id, new, old = q.next()
while true do while true do
if not id then if not id then
return nil return nil
end end
if not isTrivial then if not is_trivial then
if not shallowEq(new, old) then if diff(new, old) then
break break
end end
elseif new ~= old then elseif new ~= old then
break break
end end
id, new, old = q:next() id, new, old = q.next()
end end
addedComponents[id] = new local record = world.entityIndex.sparse[id]
local archetype = record.archetype
local column = archetype.records[PreviousT].column
local data = if is_trivial then new else table.clone(new)
archetype.columns[column][record.row] = data
return id, old, new return id, old, new
end end
@ -162,11 +155,11 @@ do
local function changes_removed() local function changes_removed()
removed = true removed = true
local q = world:query(PreviousT):without(T) local q = world:query(PreviousT):without(T):drain()
return function() return function()
local id = q:next() local id = q.next()
if id then if id then
table.insert(removedComponents, id) world:remove(id, PreviousT)
end end
return id return id
end end
@ -179,8 +172,8 @@ do
} }
local function track(fn) local function track(fn)
added = true added = false
removed = true removed = false
fn(changes) fn(changes)
@ -194,88 +187,17 @@ do
end end
end end
for e, data in addedComponents do for e, data in add do
world:set(e, PreviousT, if isTrivial then data else table.clone(data)) world:set(e, PreviousT, if is_trivial then data else table.clone(data))
end
for _, e in removedComponents do
world:remove(e, PreviousT)
end end
end end
local tracker = { track = track } local tracker = { track = track }
function ChangeTracker<T>(worldToTrack: World, component: Entity<T>): Tracker<T> return tracker
world = worldToTrack
T = component
-- We just use jecs.Rest because people will probably not use it anyways
PreviousT = jecs.pair(jecs.Rest, T)
addedComponents = {}
removedComponents = {}
return tracker
end
end end
local Allocator: <T>(World, Entity<T>, (T) -> ()) -> { alloc: (Entity) -> (), free: (Entity) -> (), deinit: () -> () }
do
local arena: {}
local world
local cleanup
local id
local function alloc(entity: Entity)
table.insert(arena, entity)
end
local function deinit()
for _, e in arena do
cleanup(world:get(e, id), e)
world:clear(e)
end
end
local function free(entity: Entity)
local e = table.remove(arena, table.find(arena, entity))
if e then
cleanup(world:get(e, id), e)
world:clear(e)
end
end
local handle = {
alloc = alloc,
deinit = deinit,
free = free,
}
setmetatable(handle, handle)
function Allocator<T>(w: World, T: Entity<T>, fn: (T) -> ())
arena = {}
world = w
cleanup = fn
id = T
return handle
end
end
local world = jecs.World.new()
local Model = world:component() :: Entity<Model>
local ModelAllocator = Allocator(world,
Model, function(model) model:Destroy() end)
local e = world:entity()
world:set(e, Model, Instance.new("Model"))
ModelAllocator.alloc(e)
ModelAllocator.free(e)
return { return {
Scheduler = Scheduler, Scheduler = Scheduler,
ChangeTracker = ChangeTracker, ChangeTracker = ChangeTracker,
Allocator = Allocator
} }