diff --git a/addons/ob.luau b/addons/ob.luau index 6661c47..5a01118 100755 --- a/addons/ob.luau +++ b/addons/ob.luau @@ -6,7 +6,6 @@ type Query = jecs.Query type Id = jecs.Id -type Entity = jecs.Entity export type Iter = (Observer) -> () -> (jecs.Entity, T...) @@ -22,7 +21,7 @@ export type Monitor = { local function observers_new( query: Query, - callback: ((Entity) -> ()) + callback: (jecs.Entity) -> () ): Observer query:cached() @@ -51,10 +50,10 @@ local function observers_new( archetypes[archetype.id] = true end - local function emplaced( - entity: jecs.Entity, + local function emplaced( + entity: jecs.Entity, id: jecs.Id, - value: a? + value: a ) local r = entity_index.sparse_array[jecs.ECS_ID(entity)] @@ -69,12 +68,26 @@ local function observers_new( for _, term in terms do if jecs.IS_PAIR(term) then - term = jecs.ECS_PAIR_FIRST(term) + local rel = jecs.ECS_PAIR_FIRST(term) + local tgt = jecs.ECS_PAIR_SECOND(term) + local wc = tgt == jecs.w + + local onadded = world:added(rel, function(entity, id) + if not wc and id ~= term then + return + end + local r = jecs.record(world, entity) + if archetypes[r.archetype.id] then + callback(entity) + end + end) + table.insert(cleanup, onadded) + else + local onadded = world:added(term, emplaced) + local onchanged = world:changed(term, emplaced) + table.insert(cleanup, onadded) + table.insert(cleanup, onchanged) end - local onadded = world:added(term, emplaced) - local onchanged = world:changed(term, emplaced) - table.insert(cleanup, onadded) - table.insert(cleanup, onchanged) end local without = query.filter_without @@ -138,7 +151,7 @@ local function observers_new( disconnect = disconnect, } - return (observer :: any) :: Observer + return observer end local function monitors_new(query: Query): Monitor @@ -169,10 +182,10 @@ local function monitors_new(query: Query): Monitor local callback_added: ((jecs.Entity) -> ())? local callback_removed: ((jecs.Entity) -> ())? - local function emplaced( - entity: jecs.Entity, + local function emplaced( + entity: jecs.Entity, id: jecs.Id, - value: a? + value: a ) if callback_added == nil then return @@ -203,12 +216,49 @@ local function monitors_new(query: Query): Monitor for _, term in terms do if jecs.IS_PAIR(term) then - term = jecs.ECS_PAIR_FIRST(term) + local rel = jecs.ECS_PAIR_FIRST(term) + local tgt = jecs.ECS_PAIR_SECOND(term) + local wc = tgt == jecs.w + + local onadded = world:added(rel, function(entity, id) + if callback_added == nil then + return + end + + if not wc and id ~= term then + return + end + + local r = jecs.entity_index_try_get_fast( + entity_index, entity :: any) :: jecs.Record + + local archetype = r.archetype + + if archetypes[archetype.id] then + callback_added(entity) + end + end) + local onremoved = world:removed(rel, function(entity, id) + if callback_removed == nil then + return + end + if not wc and id ~= term then + return + end + local r = jecs.record(world, entity) + if not archetypes[r.archetype.id] then + return + end + callback_removed(entity) + end) + table.insert(cleanup, onadded) + table.insert(cleanup, onremoved) + else + local onadded = world:added(term, emplaced) + local onremoved = world:removed(term, removed) + table.insert(cleanup, onadded) + table.insert(cleanup, onremoved) end - local onadded = world:added(term, emplaced) - local onremoved = world:removed(term, removed) - table.insert(cleanup, onadded) - table.insert(cleanup, onremoved) end local without = query.filter_without @@ -311,13 +361,13 @@ local function monitors_new(query: Query): Monitor callback_removed = callback end - local observer = { + local monitor = { disconnect = disconnect, added = monitor_added, removed = monitor_removed - } + } :: Monitor - return (observer :: any) :: Monitor + return monitor end return { diff --git a/test/addons/ob.luau b/test/addons/ob.luau index fbe0ccd..9bed301 100755 --- a/test/addons/ob.luau +++ b/test/addons/ob.luau @@ -7,6 +7,26 @@ local ob = require("@addons/ob") TEST("addons/ob::observer", function() local world = jecs.world() + do CASE [[should not invoke callbacks with a related but non-queried pair that + while the entity still matches against the query]] + local A = world:component() + local B = world:component() + local C = world:component() + + local c = 1 + + ob.observer(world:query(jecs.pair(A, B)), function() + c+=1 + end) + + local e = world:entity() + CHECK(c==1) + world:add(e, jecs.pair(A, B)) + CHECK(c==2) + world:add(e, jecs.pair(A, C)) + CHECK(c==2) + end + do CASE "should match against archetypes that were already created" local A = world:component() @@ -203,6 +223,28 @@ end) TEST("addons/ob::monitor", function() local world = jecs.world() + do CASE [[should not invoke callbacks with a related but non-queried pair that + while the entity still matches against the query]] + local A = world:component() + local B = world:component() + local C = world:component() + + local c = 1 + + local monitor = ob.monitor(world:query(jecs.pair(A, B))) + + monitor.added(function() + c += 1 + end) + + + local e = world:entity() + CHECK(c==1) + world:add(e, jecs.pair(A, B)) + CHECK(c==2) + world:add(e, jecs.pair(A, C)) + CHECK(c==2) + end do CASE "should match against archetypes that were already created" local A = world:component()