mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Replace OnSet hook with OnChange
This commit is contained in:
parent
a466ab151b
commit
cf88c259f8
3 changed files with 209 additions and 25 deletions
42
jecs.luau
42
jecs.luau
|
@ -61,8 +61,8 @@ type ecs_id_record_t = {
|
|||
flags: number,
|
||||
size: number,
|
||||
hooks: {
|
||||
on_add: ((entity: i53) -> ())?,
|
||||
on_set: ((entity: i53, data: any) -> ())?,
|
||||
on_add: ((entity: i53, data: any?) -> ())?,
|
||||
on_change: ((entity: i53, data: any) -> ())?,
|
||||
on_remove: ((entity: i53) -> ())?,
|
||||
},
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ local HI_COMPONENT_ID = _G.__JECS_HI_COMPONENT_ID or 256
|
|||
-- stylua: ignore start
|
||||
local EcsOnAdd = HI_COMPONENT_ID + 1
|
||||
local EcsOnRemove = HI_COMPONENT_ID + 2
|
||||
local EcsOnSet = HI_COMPONENT_ID + 3
|
||||
local EcsOnChange = HI_COMPONENT_ID + 3
|
||||
local EcsWildcard = HI_COMPONENT_ID + 4
|
||||
local EcsChildOf = HI_COMPONENT_ID + 5
|
||||
local EcsComponent = HI_COMPONENT_ID + 6
|
||||
|
@ -572,9 +572,11 @@ local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
|||
has_delete = true
|
||||
end
|
||||
|
||||
local on_add, on_set, on_remove = world_get(world, relation, EcsOnAdd, EcsOnSet, EcsOnRemove)
|
||||
local on_add, on_change, on_remove = world_get(world,
|
||||
relation, EcsOnAdd, EcsOnChange, EcsOnRemove)
|
||||
|
||||
local is_tag = not world_has_one_inline(world, relation, EcsComponent)
|
||||
local is_tag = not world_has_one_inline(world,
|
||||
relation, EcsComponent)
|
||||
|
||||
if is_tag and is_pair then
|
||||
is_tag = not world_has_one_inline(world, target, EcsComponent)
|
||||
|
@ -582,9 +584,6 @@ local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
|||
|
||||
flags = bit32.bor(
|
||||
flags,
|
||||
if on_add then ECS_ID_HAS_ON_ADD else 0,
|
||||
if on_remove then ECS_ID_HAS_ON_REMOVE else 0,
|
||||
if on_set then ECS_ID_HAS_ON_SET else 0,
|
||||
if has_delete then ECS_ID_DELETE else 0,
|
||||
if is_tag then ECS_ID_IS_TAG else 0
|
||||
)
|
||||
|
@ -596,7 +595,7 @@ local function id_record_ensure(world: ecs_world_t, id: number): ecs_id_record_t
|
|||
flags = flags,
|
||||
hooks = {
|
||||
on_add = on_add,
|
||||
on_set = on_set,
|
||||
on_change = on_change,
|
||||
on_remove = on_remove,
|
||||
},
|
||||
}
|
||||
|
@ -737,13 +736,13 @@ local function find_archetype_with(world: ecs_world_t, node: ecs_archetype_t, id
|
|||
-- them each time would be expensive. Instead this insertion sort can find the insertion
|
||||
-- point in the types array.
|
||||
|
||||
local dst = table.clone(node.types) :: { i53 }
|
||||
local at = find_insert(id_types, id)
|
||||
if at == -1 then
|
||||
-- If it finds a duplicate, it just means it is the same archetype so it can return it
|
||||
-- directly instead of needing to hash types for a lookup to the archetype.
|
||||
return node
|
||||
end
|
||||
local dst = table.clone(id_types) :: { i53 }
|
||||
table.insert(dst, at, id)
|
||||
|
||||
return archetype_ensure(world, dst)
|
||||
|
@ -933,14 +932,14 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
|
|||
if from == to then
|
||||
-- If the archetypes are the same it can avoid moving the entity
|
||||
-- and just set the data directly.
|
||||
local on_change = idr_hooks.on_change
|
||||
if on_change then
|
||||
on_change(entity, data)
|
||||
end
|
||||
|
||||
local tr = to.records[id]
|
||||
local column = from.columns[tr]
|
||||
column[record.row] = data
|
||||
local on_set = idr_hooks.on_set
|
||||
if on_set then
|
||||
on_set(entity, data)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -961,13 +960,10 @@ local function world_set(world: ecs_world_t, entity: i53, id: i53, data: unknown
|
|||
|
||||
local on_add = idr_hooks.on_add
|
||||
if on_add then
|
||||
on_add(entity)
|
||||
on_add(entity, data)
|
||||
end
|
||||
|
||||
local on_set = idr_hooks.on_set
|
||||
if on_set then
|
||||
on_set(entity, data)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local function world_component(world: World): i53
|
||||
|
@ -2471,7 +2467,7 @@ local function world_new()
|
|||
end
|
||||
|
||||
world_add(self, EcsName, EcsComponent)
|
||||
world_add(self, EcsOnSet, EcsComponent)
|
||||
world_add(self, EcsOnChange, EcsComponent)
|
||||
world_add(self, EcsOnAdd, EcsComponent)
|
||||
world_add(self, EcsOnRemove, EcsComponent)
|
||||
world_add(self, EcsWildcard, EcsComponent)
|
||||
|
@ -2479,7 +2475,7 @@ local function world_new()
|
|||
|
||||
world_set(self, EcsOnAdd, EcsName, "jecs.OnAdd")
|
||||
world_set(self, EcsOnRemove, EcsName, "jecs.OnRemove")
|
||||
world_set(self, EcsOnSet, EcsName, "jecs.OnSet")
|
||||
world_set(self, EcsOnChange, EcsName, "jecs.OnChange")
|
||||
world_set(self, EcsWildcard, EcsName, "jecs.Wildcard")
|
||||
world_set(self, EcsChildOf, EcsName, "jecs.ChildOf")
|
||||
world_set(self, EcsComponent, EcsName, "jecs.Component")
|
||||
|
@ -2612,7 +2608,7 @@ return {
|
|||
|
||||
OnAdd = EcsOnAdd :: Entity<(entity: Entity) -> ()>,
|
||||
OnRemove = EcsOnRemove :: Entity<(entity: Entity) -> ()>,
|
||||
OnSet = EcsOnSet :: Entity<(entity: Entity, data: any) -> ()>,
|
||||
OnChange = EcsOnChange :: Entity<(entity: Entity, data: any) -> ()>,
|
||||
ChildOf = EcsChildOf :: Entity,
|
||||
Component = EcsComponent :: Entity,
|
||||
Wildcard = EcsWildcard :: Entity,
|
||||
|
|
|
@ -1664,9 +1664,9 @@ TEST("Hooks", function()
|
|||
local Number = world:component()
|
||||
local e1 = world:entity()
|
||||
|
||||
world:set(Number, jecs.OnSet, function(entity, data)
|
||||
world:set(Number, jecs.OnChange, function(entity, data)
|
||||
CHECK(e1 == entity)
|
||||
CHECK(data == world:get(entity, Number))
|
||||
CHECK(world:get(entity, Number) == nil)
|
||||
CHECK(data == 1)
|
||||
end)
|
||||
world:set(e1, Number, 1)
|
||||
|
|
188
tools/observers.luau
Normal file
188
tools/observers.luau
Normal file
|
@ -0,0 +1,188 @@
|
|||
local jecs = require("@jecs")
|
||||
local testkit = require("@testkit")
|
||||
|
||||
local function observers_new(description)
|
||||
local event = description.event
|
||||
local query = description.query
|
||||
local callback = description.callback
|
||||
local world = query.world
|
||||
local terms = query.filter_with
|
||||
if not terms then
|
||||
local ids = query.ids
|
||||
query.filter_with = ids
|
||||
terms = ids
|
||||
end
|
||||
|
||||
local entity_index = world.entity_index
|
||||
local function emplaced(entity)
|
||||
local r = jecs.entity_index_try_get_fast(
|
||||
entity_index, entity)
|
||||
|
||||
local archetype = r.archetype
|
||||
|
||||
if jecs.query_match(query, archetype) then
|
||||
callback(entity)
|
||||
end
|
||||
end
|
||||
|
||||
for _, term in terms do
|
||||
if event == jecs.OnAdd then
|
||||
world:added(term, emplaced)
|
||||
elseif event == jecs.OnSet then
|
||||
world:emplaced(term, emplaced)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function world_track(world, ...)
|
||||
local entity_index = world.entity_index
|
||||
local terms = { ... }
|
||||
local q_shim = { filter_with = terms }
|
||||
|
||||
local n = 0
|
||||
local dense_array = {}
|
||||
local sparse_array = {}
|
||||
|
||||
local function emplaced(entity)
|
||||
local r = jecs.entity_index_try_get_fast(
|
||||
entity_index, entity)
|
||||
|
||||
local archetype = r.archetype
|
||||
|
||||
if jecs.query_match(q_shim, archetype) then
|
||||
n += 1
|
||||
dense_array[n] = entity
|
||||
sparse_array[entity] = n
|
||||
end
|
||||
end
|
||||
|
||||
local function removed(entity)
|
||||
local i = sparse_array[entity]
|
||||
if i ~= n then
|
||||
dense_array[i] = dense_array[n]
|
||||
end
|
||||
|
||||
dense_array[n] = nil
|
||||
end
|
||||
|
||||
for _, term in terms do
|
||||
world:added(term, emplaced)
|
||||
world:emplaced(term, emplaced)
|
||||
end
|
||||
|
||||
local function iter()
|
||||
local i = n
|
||||
print(i, "i")
|
||||
return function()
|
||||
local row = i
|
||||
if row == 0 then
|
||||
return nil
|
||||
end
|
||||
i -= 1
|
||||
return dense_array[row]
|
||||
end
|
||||
end
|
||||
|
||||
local it = {
|
||||
__iter = iter
|
||||
}
|
||||
return setmetatable(it, it)
|
||||
end
|
||||
|
||||
local function observers_init(world)
|
||||
local signals = {
|
||||
added = {},
|
||||
emplaced = {},
|
||||
removed = {}
|
||||
}
|
||||
world.added = function(_, component, fn)
|
||||
local listeners = signals.added[component]
|
||||
if not listeners then
|
||||
listeners = {}
|
||||
signals.added[component] = listeners
|
||||
local idr = jecs.id_record_ensure(world, component)
|
||||
idr.hooks.on_add = function(entity)
|
||||
for _, listener in listeners do
|
||||
listener(entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
end
|
||||
|
||||
world.emplaced = function(_, component, fn)
|
||||
local listeners = signals.emplaced[component]
|
||||
if not listeners then
|
||||
listeners = {}
|
||||
signals.emplaced[component] = listeners
|
||||
local idr = jecs.id_record_ensure(world, component)
|
||||
idr.hooks.on_set = function(entity, value)
|
||||
for _, listener in listeners do
|
||||
listener(entity, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
end
|
||||
|
||||
world.removed = function(_, component, fn)
|
||||
local listeners = signals.removed[component]
|
||||
if not listeners then
|
||||
listeners = {}
|
||||
signals.removed[component] = listeners
|
||||
local idr = jecs.id_record_ensure(world, component)
|
||||
idr.hooks.on_remove = function(entity)
|
||||
for _, listener in listeners do
|
||||
listener(entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
end
|
||||
|
||||
world.signals = signals
|
||||
|
||||
world.track = world_track
|
||||
return
|
||||
end
|
||||
|
||||
local world = jecs.world()
|
||||
observers_init(world)
|
||||
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
|
||||
world:added(A, print)
|
||||
world:added(A, function(entity)
|
||||
print(entity, 2)
|
||||
end)
|
||||
|
||||
local observer = observers_new({
|
||||
event = jecs.OnAdd,
|
||||
query = world:query(A, B),
|
||||
callback = function(entity)
|
||||
print(entity, 3)
|
||||
end
|
||||
})
|
||||
|
||||
local e = world:entity()
|
||||
local Test = world:track(A, B)
|
||||
for a in Test do
|
||||
print(a)
|
||||
assert(false)
|
||||
end
|
||||
world:add(e, A)
|
||||
|
||||
-- Output:
|
||||
-- 271
|
||||
-- 271, 2
|
||||
for _ in Test do
|
||||
assert(false)
|
||||
end
|
||||
world:add(e, B)
|
||||
for a in Test do
|
||||
print("lol")
|
||||
assert(true)
|
||||
end
|
||||
-- Output:
|
||||
-- 271, 3
|
Loading…
Reference in a new issue