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 dup = world:query()
dup.filter_with = table.clone(query.filter_with)
if query.filter_without then
if query.filter_without then
dup.filter_without = query.filter_without
end
return dup:cached()
@ -152,11 +152,6 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
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_removed: ((jecs.Entity) -> ())?
@ -176,7 +171,7 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
local r = jecs.entity_index_try_get_fast(
entity_index, entity :: any) :: jecs.Record
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
end
@ -193,13 +188,13 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
if callback_removed == nil then
return
end
local r = jecs.record(world, entity)
if not r then return end
local src = r.archetype
if not src then return end
if not archetypes[src.id] then return end
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
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
end
@ -253,10 +248,17 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
end
local r = jecs.record(world, entity)
if archetypes[r.archetype.id] then
last_old_archetype = nil
callback_removed(entity)
local src = r.archetype
if last_entity == entity and last_old_archetype == src then
return
end
if not archetypes[src.id] then
return
end
last_entity = entity
last_old_archetype = src
callback_removed(entity)
end)
table.insert(cleanup, onadded)
table.insert(cleanup, onremoved)
@ -294,7 +296,7 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
callback_removed(entity)
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
return
end
@ -310,7 +312,7 @@ local function monitors_new(query: jecs.Query<...any>): Monitor
if not archetype then
return
end
if last_old_archetype == archetype and terms_lookup[id] then
if last_old_archetype == archetype then
return
end

View file

@ -309,19 +309,19 @@ TEST("modules/ob::monitor", function()
local A = world:component()
local B = world:component()
local C = world:component()
local count = 0
ob.monitor(world:query(A, C)).removed(function()
ob.monitor(world:query(A, C)).removed(function()
count += 1
end)
local e = world:entity()
jecs.bulk_insert(world, e, {A, B}, {0,0})
CHECK(count==0)
world:remove(e, A)
CHECK(count==0)
end
do CASE [[should not invoke monitor.added callback multiple times in a bulk_move
]]
local A = world:component()
@ -838,27 +838,47 @@ TEST("modules/ob::monitor", function()
do CASE "monitor with wildcard pair should handle bulk_insert"
local A = world:component()
local B = world:component()
local e1 = world:entity()
local e2 = world:entity()
local e3 = world:entity()
local C = world:component()
local Relation = world:component()
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
monitor.added(function()
c += 1
end)
local e = world:entity()
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))
jecs.bulk_insert(world, entity, { A, B, C, jecs.pair(Relation, entity1) }, { 1, 2, 3, 4 })
CHECK(c == 1)
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"
local A = world:component()
local B = world:component()