mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 17:40:02 +00:00
Fix upvalues conflict
This commit is contained in:
parent
219d0e08c4
commit
00627c6eee
1 changed files with 69 additions and 147 deletions
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue