Self contained changetracker

This commit is contained in:
Ukendio 2024-07-28 14:17:44 +02:00
parent 18ead3ed19
commit d5de1ad425
2 changed files with 136 additions and 106 deletions

View file

@ -935,7 +935,7 @@ do
setmetatable(it, it) setmetatable(it, it)
function world_query(world: World, ...: any): Query function world_query(world: World, ...: any): Query
-- breaking? -- breaking
if (...) == nil then if (...) == nil then
error("Missing components") error("Missing components")
end end
@ -1179,8 +1179,10 @@ function World.new()
self.ROOT_ARCHETYPE = archetype_of(self, {}) self.ROOT_ARCHETYPE = archetype_of(self, {})
for i = HI_COMPONENT_ID + 1, EcsRest do
-- Initialize built-in components -- Initialize built-in components
entity_index_new_id(self.entityIndex, EcsChildOf) entity_index_new_id(self.entityIndex, i)
end
return self return self
end end

View file

@ -564,34 +564,30 @@ TEST("world", function()
end) end)
type Tracker<T> = { track: (world: World, fn: (changes: {
added: () -> () -> (number, T),
removed: () -> () -> number,
changed: () -> () -> (number, T, T)
}) -> ()) -> ()
}
TEST("changetracker", function() type Entity<T = any> = number & { __nominal_type_dont_use: T }
local world = jecs.World.new()
local Previous = world:component()
local function shallowEq(a, b) local ChangeTracker: <T>(component: Entity<T>) -> Tracker<T>
for k, v in a do
if b[k] ~= v then
return false
end
end
return true
end
local function ChangeTracker(world, component) do
local addedComponents = {} local world: World
local removedComponents = {} local T
local previous = jecs.pair(Previous, component) local PreviousT
local isTrivial = nil local addedComponents
local removedComponents
local isTrivial
local added
local removed
local function track(fn) local function changes_added()
local added = false
local removed = false
local changes = {}
function changes.added()
added = true added = true
local q = world:query(component):without(previous) local q = world:query(T):without(PreviousT)
return function() return function()
local id, data = q:next() local id, data = q:next()
if not id then if not id then
@ -611,8 +607,17 @@ TEST("changetracker", function()
end end
end end
function changes.changed() local function shallowEq(a, b)
local q = world:query(component, previous) for k, v in a do
if b[k] ~= v then
return false
end
end
return true
end
local function changes_changed()
local q = world:query(T, PreviousT)
return function() return function()
local id, new, old = q:next() local id, new, old = q:next()
@ -638,10 +643,10 @@ TEST("changetracker", function()
end end
end end
function changes.removed() local function changes_removed()
removed = true removed = true
local q = world:query(previous):without(component) local q = world:query(PreviousT):without(T)
return function() return function()
local id = q:next() local id = q:next()
if id then if id then
@ -651,39 +656,62 @@ TEST("changetracker", function()
end end
end end
local changes = {
added = changes_added,
changed = changes_changed,
removed = changes_removed,
}
local function track(worldToTrack, fn)
world = worldToTrack
added = true
removed = true
fn(changes) fn(changes)
if not added then if not added then
for _ in changes.added() do for _ in changes_added() do
end end
end end
if not removed then if not removed then
for _ in changes.removed() do for _ in changes_removed() do
end end
end end
for e, data in addedComponents do for e, data in addedComponents do
world:set(e, previous, if isTrivial then data else table.clone(data)) world:set(e, PreviousT, if isTrivial then data else table.clone(data))
end end
for _, e in removedComponents do for _, e in removedComponents do
world:remove(e, previous) world:remove(e, PreviousT)
end end
end end
return { local tracker = { track = track }
track = track
} function ChangeTracker<T>(component: Entity<T>): Tracker<T>
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
TEST("changetracker", function()
local world = jecs.World.new()
do CASE "should allow change tracking" do CASE "should allow change tracking"
local Test = world:component() local Test = world:component() :: Entity<{ foo: number }>
local TestTracker = ChangeTracker(world, Test) local TestTracker = ChangeTracker(Test)
local e = world:entity() local e = world:entity()
world:set(e, Test, { foo = 11 }) world:set(e, Test, { foo = 11 })
TestTracker.track(function(changes) TestTracker.track(world, function(changes)
local added = 0 local added = 0
local changed = 0 local changed = 0
local removed = 0 local removed = 0
@ -705,7 +733,7 @@ TEST("changetracker", function()
test.foo = test.foo + 1 test.foo = test.foo + 1
end end
TestTracker.track(function(changes) TestTracker.track(world, function(changes)
local added = 0 local added = 0
local changed = 0 local changed = 0
local removed = 0 local removed = 0
@ -727,7 +755,7 @@ TEST("changetracker", function()
world:remove(e, Test) world:remove(e, Test)
TestTracker.track(function(changes) TestTracker.track(world, function(changes)
local added = 0 local added = 0
local changed = 0 local changed = 0
local removed = 0 local removed = 0