Remove the exact terms lookup set and detect bulk operation for removal of pairs

This commit is contained in:
Ukendio 2026-02-19 20:20:48 +01:00
parent 5f76674723
commit 6552a5d2d1
2 changed files with 54 additions and 32 deletions

View file

@ -9,7 +9,7 @@ local function duplicate(query: jecs.Query<...any>): jecs.CachedQuery<...any>
local world = (query :: jecs.Query<any> & { world: World }).world local world = (query :: jecs.Query<any> & { world: World }).world
local dup = world:query() local dup = world:query()
dup.filter_with = table.clone(query.filter_with) dup.filter_with = table.clone(query.filter_with)
if query.filter_without then if query.filter_without then
dup.filter_without = query.filter_without dup.filter_without = query.filter_without
end end
return dup:cached() return dup:cached()
@ -152,11 +152,6 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
local entity_index = world.entity_index :: any local entity_index = world.entity_index :: any
local terms_lookup: { [jecs.Id<any>]: boolean } = {}
for _, term in terms do
terms_lookup[term] = true
end
local callback_added: ((jecs.Entity) -> ())? local callback_added: ((jecs.Entity) -> ())?
local callback_removed: ((jecs.Entity) -> ())? local callback_removed: ((jecs.Entity) -> ())?
@ -176,7 +171,7 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
local r = jecs.entity_index_try_get_fast( local r = jecs.entity_index_try_get_fast(
entity_index, entity :: any) :: jecs.Record entity_index, entity :: any) :: jecs.Record
if not archetypes[oldarchetype.id] and archetypes[r.archetype.id] then if not archetypes[oldarchetype.id] and archetypes[r.archetype.id] then
if last_old_archetype == oldarchetype and last_entity == entity and terms_lookup[id] then if last_old_archetype == oldarchetype and last_entity == entity then
return return
end end
@ -193,13 +188,13 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
if callback_removed == nil then if callback_removed == nil then
return return
end end
local r = jecs.record(world, entity) local r = jecs.record(world, entity)
if not r then return end if not r then return end
local src = r.archetype local src = r.archetype
if not src then return end if not src then return end
if not archetypes[src.id] then return end if not archetypes[src.id] then return end
if last_entity == entity and last_old_archetype == src then if last_entity == entity and last_old_archetype == src then
@ -232,7 +227,7 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
entity_index, entity :: any) :: jecs.Record entity_index, entity :: any) :: jecs.Record
if not archetypes[oldarchetype.id] and archetypes[r.archetype.id] then if not archetypes[oldarchetype.id] and archetypes[r.archetype.id] then
if last_old_archetype == oldarchetype and last_entity == entity and terms_lookup[id] then if last_old_archetype == oldarchetype and last_entity == entity then
return return
end end
@ -253,10 +248,17 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
end end
local r = jecs.record(world, entity) local r = jecs.record(world, entity)
if archetypes[r.archetype.id] then local src = r.archetype
last_old_archetype = nil
callback_removed(entity) if last_entity == entity and last_old_archetype == src then
return
end end
if not archetypes[src.id] then
return
end
last_entity = entity
last_old_archetype = src
callback_removed(entity)
end) end)
table.insert(cleanup, onadded) table.insert(cleanup, onadded)
table.insert(cleanup, onremoved) table.insert(cleanup, onremoved)
@ -294,7 +296,7 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
callback_removed(entity) callback_removed(entity)
end end
end) end)
local onremoved = world:removed(rel, function(entity, id, delete) local onremoved = world:removed(rel, function(entity: jecs.Entity, id: jecs.Id, delete)
if delete then if delete then
return return
end end
@ -310,7 +312,7 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
if not archetype then if not archetype then
return return
end end
if last_old_archetype == archetype and terms_lookup[id] then if last_old_archetype == archetype then
return return
end end

View file

@ -309,19 +309,19 @@ TEST("modules/ob::monitor", function()
local A = world:component() local A = world:component()
local B = world:component() local B = world:component()
local C = world:component() local C = world:component()
local count = 0 local count = 0
ob.monitor(world:query(A, C)).removed(function() ob.monitor(world:query(A, C)).removed(function()
count += 1 count += 1
end) end)
local e = world:entity() local e = world:entity()
jecs.bulk_insert(world, e, {A, B}, {0,0}) jecs.bulk_insert(world, e, {A, B}, {0,0})
CHECK(count==0) CHECK(count==0)
world:remove(e, A) world:remove(e, A)
CHECK(count==0) CHECK(count==0)
end end
do CASE [[should not invoke monitor.added callback multiple times in a bulk_move do CASE [[should not invoke monitor.added callback multiple times in a bulk_move
]] ]]
local A = world:component() local A = world:component()
@ -838,27 +838,47 @@ TEST("modules/ob::monitor", function()
do CASE "monitor with wildcard pair should handle bulk_insert" do CASE "monitor with wildcard pair should handle bulk_insert"
local A = world:component() local A = world:component()
local B = world:component() local B = world:component()
local e1 = world:entity() local C = world:component()
local e2 = world:entity() local Relation = world:component()
local e3 = world:entity() local entity1 = world:entity()
local entity = world:entity()
local monitor = ob.monitor(world:query(A, jecs.pair(B, jecs.w))) local monitor = ob.monitor(world:query(A, B, C, jecs.pair(Relation, jecs.w)))
local c = 0 local c = 0
monitor.added(function() monitor.added(function()
c += 1 c += 1
end) end)
local e = world:entity() jecs.bulk_insert(world, entity, { A, B, C, jecs.pair(Relation, entity1) }, { 1, 2, 3, 4 })
world:add(e, A)
CHECK(c == 0)
world:add(e, jecs.pair(B, e1))
CHECK(c == 1)
world:add(e, jecs.pair(B, e2))
CHECK(c == 1)
world:add(e, jecs.pair(B, e3))
CHECK(c == 1) CHECK(c == 1)
end end
do CASE "monitor with wildcard pair: bulk_remove reports removed exactly once"
local A = world:component()
local B = world:component()
local C = world:component()
local Relation = world:component()
local entity1 = world:entity()
local entity = world:entity()
local monitor = ob.monitor(world:query(A, B, C, jecs.pair(Relation, jecs.w)))
local added_count = 0
local removed_count = 0
monitor.added(function()
added_count += 1
end)
monitor.removed(function()
removed_count += 1
end)
jecs.bulk_insert(world, entity, { A, B, C, jecs.pair(Relation, entity1) }, { 1, 2, 3, 4 })
CHECK(added_count == 1)
CHECK(removed_count == 0)
jecs.bulk_remove(world, entity, { A, B, C, jecs.pair(Relation, entity1) })
CHECK(removed_count == 1)
end
do CASE "monitor with multiple pairs should handle separate operations correctly" do CASE "monitor with multiple pairs should handle separate operations correctly"
local A = world:component() local A = world:component()
local B = world:component() local B = world:component()