mirror of
https://github.com/Ukendio/jecs.git
synced 2026-02-04 15:15:21 +00:00
escape for deletion
This commit is contained in:
parent
94f3eb0cc1
commit
4db046b925
7 changed files with 301 additions and 151 deletions
100
addons/ob.luau
100
addons/ob.luau
|
|
@ -137,9 +137,11 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
local callback_added: ((jecs.Entity) -> ())?
|
||||
local callback_removed: ((jecs.Entity) -> ())?
|
||||
|
||||
local function emplaced<T, a>(
|
||||
entity: jecs.Entity<T>,
|
||||
id: jecs.Id<a>
|
||||
local function emplaced<a>(
|
||||
entity: jecs.Entity,
|
||||
id: jecs.Id<a>,
|
||||
value: a,
|
||||
oldarchetype: jecs.Archetype
|
||||
)
|
||||
if callback_added == nil then
|
||||
return
|
||||
|
|
@ -148,23 +150,27 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
local r = jecs.entity_index_try_get_fast(
|
||||
entity_index, entity :: any) :: jecs.Record
|
||||
|
||||
local archetype = r.archetype
|
||||
|
||||
if archetypes[archetype.id] then
|
||||
if archetypes[r.archetype.id] then
|
||||
callback_added(entity)
|
||||
end
|
||||
end
|
||||
|
||||
local function removed(entity: jecs.Entity, component)
|
||||
local function removed(entity: jecs.Entity, component: jecs.Component, delete:boolean?)
|
||||
if delete then
|
||||
return
|
||||
end
|
||||
if callback_removed == nil then
|
||||
return
|
||||
end
|
||||
local r = jecs.record(world, entity)
|
||||
if not archetypes[r.archetype.id] then
|
||||
return
|
||||
end
|
||||
local src = r.archetype
|
||||
|
||||
local dst = jecs.archetype_traverse_remove(world, component, src)
|
||||
|
||||
if not archetypes[dst.id] then
|
||||
callback_removed(entity)
|
||||
end
|
||||
end
|
||||
|
||||
local cleanup = {}
|
||||
|
||||
|
|
@ -174,7 +180,7 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
local tgt = jecs.ECS_PAIR_SECOND(term)
|
||||
local wc = tgt == jecs.w
|
||||
|
||||
local onadded = world:added(rel, function(entity, id)
|
||||
local onadded = world:added(rel, function(entity, id, _, oldarchetype)
|
||||
if callback_added == nil then
|
||||
return
|
||||
end
|
||||
|
|
@ -186,9 +192,10 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
local r = jecs.entity_index_try_get_fast(
|
||||
entity_index, entity :: any) :: jecs.Record
|
||||
|
||||
local archetype = r.archetype
|
||||
|
||||
if archetypes[archetype.id] then
|
||||
if archetypes[oldarchetype.id] and archetypes[r.archetype.id] then
|
||||
print("???")
|
||||
end
|
||||
if not archetypes[oldarchetype.id] and archetypes[r.archetype.id] then
|
||||
callback_added(entity)
|
||||
end
|
||||
end)
|
||||
|
|
@ -199,11 +206,11 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
if not wc and id ~= term then
|
||||
return
|
||||
end
|
||||
|
||||
local r = jecs.record(world, entity)
|
||||
if not archetypes[r.archetype.id] then
|
||||
return
|
||||
end
|
||||
if archetypes[r.archetype.id] then
|
||||
callback_removed(entity)
|
||||
end
|
||||
end)
|
||||
table.insert(cleanup, onadded)
|
||||
table.insert(cleanup, onremoved)
|
||||
|
|
@ -223,7 +230,7 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
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)
|
||||
local onadded = world:added(rel, function(entity, id, _, oldarchetype)
|
||||
if callback_removed == nil then
|
||||
return
|
||||
end
|
||||
|
|
@ -232,14 +239,23 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
end
|
||||
local r = jecs.record(world, entity)
|
||||
local archetype = r.archetype
|
||||
if archetype then
|
||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
||||
if archetypes[dst.id] then
|
||||
if not archetype then
|
||||
return
|
||||
end
|
||||
|
||||
-- NOTE(marcus): This check that it was presently in
|
||||
-- the query but distinctively leaves is important as
|
||||
-- sometimes it could be too eager to report that it
|
||||
-- removed a component even though the entity is not
|
||||
-- apart of the monitor
|
||||
if archetypes[oldarchetype.id] and not archetypes[archetype.id] then
|
||||
callback_removed(entity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
local onremoved = world:removed(rel, function(entity, id)
|
||||
local onremoved = world:removed(rel, function(entity, id, delete)
|
||||
if delete then
|
||||
return
|
||||
end
|
||||
if callback_added == nil then
|
||||
return
|
||||
end
|
||||
|
|
@ -248,41 +264,59 @@ local function monitors_new(query: Query<...any>): Monitor
|
|||
end
|
||||
local r = jecs.record(world, entity)
|
||||
local archetype = r.archetype
|
||||
if archetype then
|
||||
if not archetype then
|
||||
return
|
||||
end
|
||||
|
||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
||||
|
||||
if archetypes[dst.id] then
|
||||
callback_added(entity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
table.insert(cleanup, onadded)
|
||||
table.insert(cleanup, onremoved)
|
||||
else
|
||||
local onadded = world:added(term, function(entity, id)
|
||||
local onadded = world:added(term, function(entity, id, _, oldarchetype)
|
||||
if callback_removed == nil then
|
||||
return
|
||||
end
|
||||
local r = jecs.record(world, entity)
|
||||
local archetype = r.archetype
|
||||
if archetype then
|
||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
||||
if archetypes[dst.id] then
|
||||
if not archetype then
|
||||
return
|
||||
end
|
||||
|
||||
-- NOTE(marcus): Sometimes OnAdd listeners for excluded
|
||||
-- terms are too eager to report that it is leaving the
|
||||
-- monitor even though the entity is not apart of it
|
||||
-- already.
|
||||
if archetypes[oldarchetype.id] and not archetypes[archetype.id] then
|
||||
callback_removed(entity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
local onremoved = world:removed(term, function(entity, id)
|
||||
local onremoved = world:removed(term, function(entity, id, delete)
|
||||
if delete then
|
||||
return
|
||||
end
|
||||
if callback_added == nil then
|
||||
return
|
||||
end
|
||||
local r = jecs.record(world, entity)
|
||||
local archetype = r.archetype
|
||||
if archetype then
|
||||
if not archetype then
|
||||
return
|
||||
end
|
||||
local dst = jecs.archetype_traverse_remove(world, id, archetype)
|
||||
|
||||
-- NOTE(marcus): Inversely with the opposite operation, you
|
||||
-- only need to check if it is going to enter the query once
|
||||
-- because world:remove already stipulates that it is
|
||||
-- idempotent so that this hook won't be invoked if it is
|
||||
-- was already removed.
|
||||
if archetypes[dst.id] then
|
||||
callback_added(entity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
table.insert(cleanup, onadded)
|
||||
table.insert(cleanup, onremoved)
|
||||
|
|
|
|||
37
benches/100k.luau
Executable file
37
benches/100k.luau
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
--!optimize 2
|
||||
--!native
|
||||
|
||||
local testkit = require("@testkit")
|
||||
local BENCH, START = testkit.benchmark()
|
||||
local function TITLE(title: string)
|
||||
print()
|
||||
print(testkit.color.white(title))
|
||||
end
|
||||
|
||||
local jecs = require("@jecs")
|
||||
local mirror = require("@mirror")
|
||||
|
||||
do
|
||||
TITLE(testkit.color.white_underline("Jecs query"))
|
||||
local world = jecs.world() :: jecs.World
|
||||
|
||||
local A = world:component()
|
||||
|
||||
for i = 1, 100_000 do
|
||||
local e = world:entity()
|
||||
world:set(e, A, true)
|
||||
end
|
||||
|
||||
local archetypes = world:query(A):archetypes()
|
||||
|
||||
BENCH("", function()
|
||||
for _, archetype in archetypes do
|
||||
local column = archetype.columns[1]
|
||||
for row, entity in archetype.entities do
|
||||
local data = column[row]
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
|
|
@ -4,9 +4,9 @@
|
|||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
|
||||
local jecs = require(ReplicatedStorage.Lib:Clone())
|
||||
local mirror = require(ReplicatedStorage.mirror:Clone())
|
||||
local mcs = mirror.world()
|
||||
local chrono = require(ReplicatedStorage.chronoecs:Clone())
|
||||
local ecs = jecs.world()
|
||||
local ccs = chrono.new()
|
||||
|
||||
local D1 = ecs:component()
|
||||
local D2 = ecs:component()
|
||||
|
|
@ -17,75 +17,75 @@ local D6 = ecs:component()
|
|||
local D7 = ecs:component()
|
||||
local D8 = ecs:component()
|
||||
|
||||
local E1 = mcs:component()
|
||||
local E2 = mcs:component()
|
||||
local E3 = mcs:component()
|
||||
local E4 = mcs:component()
|
||||
local E5 = mcs:component()
|
||||
local E6 = mcs:component()
|
||||
local E7 = mcs:component()
|
||||
local E8 = mcs:component()
|
||||
local E1 = ccs:component()
|
||||
local E2 = ccs:component()
|
||||
local E3 = ccs:component()
|
||||
local E4 = ccs:component()
|
||||
local E5 = ccs:component()
|
||||
local E6 = ccs:component()
|
||||
local E7 = ccs:component()
|
||||
local E8 = ccs:component()
|
||||
|
||||
local d_components = {}
|
||||
local e_components = {}
|
||||
|
||||
for i = 1, 150 do
|
||||
ecs:component()
|
||||
ccs:component()
|
||||
end
|
||||
|
||||
local function flip()
|
||||
return math.random() >= 0.3
|
||||
return math.random() >= 0.5
|
||||
end
|
||||
|
||||
local N = 2 ^ 16 - 2
|
||||
|
||||
for i = 1, N do
|
||||
local entity = ecs:entity()
|
||||
local m = mcs:entity()
|
||||
local m = ccs:entity()
|
||||
|
||||
if flip() then
|
||||
ecs:add(entity, entity)
|
||||
mcs:add(m, m)
|
||||
end
|
||||
if flip() then
|
||||
ecs:set(entity, D1, true)
|
||||
mcs:set(m, E1, true)
|
||||
ccs:add(m, E1)
|
||||
ccs:set(m, E1, true)
|
||||
end
|
||||
if flip() then
|
||||
ecs:set(entity, D2, true)
|
||||
mcs:set(m, E2, true)
|
||||
ccs:add(m, E2)
|
||||
ccs:set(m, E2, true)
|
||||
end
|
||||
if flip() then
|
||||
ecs:set(entity, D3, true)
|
||||
mcs:set(m, E3, true)
|
||||
ccs:add(m, E3)
|
||||
ccs:set(m, E3, true)
|
||||
end
|
||||
if flip() then
|
||||
ecs:set(entity, D4, true)
|
||||
mcs:set(m, E4, true)
|
||||
ccs:add(m, E4)
|
||||
ccs:set(m, E4, true)
|
||||
end
|
||||
if flip() then
|
||||
ecs:set(entity, D5, true)
|
||||
mcs:set(m, E5, true)
|
||||
ccs:add(m, E4)
|
||||
ccs:set(m, E5, true)
|
||||
end
|
||||
if flip() then
|
||||
ecs:set(entity, D6, true)
|
||||
mcs:set(m, E6, true)
|
||||
ccs:add(m, E6)
|
||||
ccs:set(m, E6, true)
|
||||
end
|
||||
if flip() then
|
||||
ecs:set(entity, D7, true)
|
||||
mcs:set(m, E7, true)
|
||||
ccs:add(m, E7)
|
||||
ccs:set(m, E7, true)
|
||||
end
|
||||
if flip() then
|
||||
ccs:add(m, E8)
|
||||
ecs:set(entity, D8, true)
|
||||
mcs:set(m, E8, true)
|
||||
ccs:set(m, E8, true)
|
||||
end
|
||||
end
|
||||
|
||||
local count = 0
|
||||
|
||||
for _, archetype in ecs:query(D2, D4, D6, D8):archetypes() do
|
||||
count += #archetype.entities
|
||||
end
|
||||
|
||||
|
||||
local mq = mcs:query(E2, E4, E6, E8):cached()
|
||||
local jq = ecs:query(D2, D4, D6, D8):cached()
|
||||
|
||||
print(count, #jq:archetypes())
|
||||
|
||||
return {
|
||||
ParameterGenerator = function()
|
||||
return
|
||||
|
|
@ -102,13 +102,13 @@ return {
|
|||
-- end
|
||||
-- end,
|
||||
--
|
||||
Mirror = function()
|
||||
for entityId, firstComponent in mq do
|
||||
chrono = function()
|
||||
for entityId, firstComponent in ccs:view(E2, E4, E6, E8) do
|
||||
end
|
||||
end,
|
||||
|
||||
Jecs = function()
|
||||
for entityId, firstComponent in jq do
|
||||
for entityId, firstComponent in ecs:query(D2, D4, D6, D8) do
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
|
|
|||
132
jecs.luau
132
jecs.luau
|
|
@ -63,37 +63,20 @@ end
|
|||
|
||||
type function ecs_id_t(first: type, second: type)
|
||||
local __T = types.singleton("__T")
|
||||
if second:is("nil") then
|
||||
|
||||
local p = ecs_pair_t(Entity(first), Entity(second))
|
||||
|
||||
-- Create component type that matches Component<T> structure exactly
|
||||
-- This should be structurally compatible with Component<T>
|
||||
local component_type = types.newtable()
|
||||
component_type:setproperty(__T, first)
|
||||
|
||||
-- Union order: component first, then pair
|
||||
-- This helps Luau recognize Component<T> as assignable
|
||||
-- local u = types.unionof(component_type, p)
|
||||
-- return u
|
||||
return component_type
|
||||
if second:is("nil") then
|
||||
return first
|
||||
end
|
||||
|
||||
local function entity(ty: type)
|
||||
local e = types.newtable()
|
||||
e:setproperty(__T, ty)
|
||||
return e
|
||||
end
|
||||
|
||||
local e1 = entity(first)
|
||||
local e2 = entity(second)
|
||||
return ecs_pair_t(e1, e2)
|
||||
return p
|
||||
end
|
||||
|
||||
export type Entity<T = nil> = { __T: T }
|
||||
export type Id2<T = any> = { __T: T }
|
||||
export type Id<T = any> = { __T: T }
|
||||
export type Pair<First=any, Second=any> = ecs_pair_t<Entity<First>, Entity<Second>>
|
||||
export type Component<T=any> = { __T: T }
|
||||
export type Id<First, Second=nil> = ecs_id_t<First, Second>
|
||||
export type Id2<First, Second=nil> = ecs_id_t<First, Second>
|
||||
|
||||
export type Item<T...> = (self: Query<T...>) -> (Entity, T...)
|
||||
export type Iter<T...> = (query: Query<T...>) -> () -> (Entity, T...)
|
||||
|
|
@ -110,8 +93,8 @@ export type CachedQuery<T...> = typeof(setmetatable(
|
|||
cached: (self: CachedQuery<T...>) -> CachedQuery<T...>,
|
||||
has: (CachedQuery<T...>, Entity) -> boolean,
|
||||
ids: { Id<any> },
|
||||
filter_with: { Id<any, any> }?,
|
||||
filter_without: { Id<any, any> }?,
|
||||
filter_with: { Id<any> }?,
|
||||
filter_without: { Id<any> }?,
|
||||
archetypes_map: { [number]: number },
|
||||
-- world: World
|
||||
},
|
||||
|
|
@ -129,8 +112,8 @@ export type Query<T...> = typeof(setmetatable(
|
|||
cached: (self: Query<T...>) -> CachedQuery<T...>,
|
||||
has: (Query<T...>, Entity) -> boolean,
|
||||
ids: { Id<any> },
|
||||
filter_with: { Id<any, any> }?,
|
||||
filter_without: { Id<any, any> }?,
|
||||
filter_with: { Id<any> }?,
|
||||
filter_without: { Id<any> }?,
|
||||
-- world: World
|
||||
},
|
||||
{} :: {
|
||||
|
|
@ -177,7 +160,7 @@ type componentrecord = {
|
|||
|
||||
on_add: ((entity: i53, id: i53, value: any, oldarchetype: archetype) -> ())?,
|
||||
on_change: ((entity: i53, id: i53, value: any, oldarchetype: archetype) -> ())?,
|
||||
on_remove: ((entity: i53, id: i53) -> ())?,
|
||||
on_remove: ((entity: i53, id: i53, delete: boolean?) -> ())?,
|
||||
|
||||
wildcard_pairs: { [number]: componentrecord },
|
||||
}
|
||||
|
|
@ -228,7 +211,7 @@ type world = {
|
|||
|
||||
added: (world, i53, (e: i53, id: i53, value: any?) -> ()) -> () -> (),
|
||||
changed: (world, i53, (e: i53, id: i53, value: any?) -> ()) -> () -> (),
|
||||
removed: (world, i53, (e: i53, id: i53) -> ()) -> () -> (),
|
||||
removed: (world, i53, (e: i53, id: i53, delete: boolean?) -> ()) -> () -> (),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -246,7 +229,7 @@ export type World = {
|
|||
observable: Map<Component, Map<Component, { Observer }>>,
|
||||
|
||||
added: <T>(World, Component<T>, (e: Entity, id: Id<T>, value: T, oldarchetype: Archetype) -> ()) -> () -> (),
|
||||
removed: <T>(World, Component<T>, (e: Entity, id: Id<T>) -> ()) -> () -> (),
|
||||
removed: <T>(World, Component<T>, (e: Entity, id: Id<T>, delete: boolean?) -> ()) -> () -> (),
|
||||
changed: <T>(World, Component<T>, (e: Entity, id: Id<T>, value: T, oldarchetype: Archetype) -> ()) -> () -> (),
|
||||
|
||||
--- Enforce a check on entities to be created within desired range
|
||||
|
|
@ -2248,21 +2231,22 @@ local function ecs_bulk_insert(world: world, entity: i53, ids: { i53 }, values:
|
|||
local to = archetype_ensure(world, dst_types)
|
||||
new_entity(entity, r, to)
|
||||
local ROOT_ARCHETYPE = world.ROOT_ARCHETYPE
|
||||
|
||||
for i, id in ids do
|
||||
local value = values[i]
|
||||
if value then
|
||||
r.archetype.columns_map[id][r.row] = value
|
||||
end
|
||||
end
|
||||
|
||||
for i, id in ids do
|
||||
local cdr = component_index[id]
|
||||
|
||||
local on_add = cdr.on_add
|
||||
if value then
|
||||
r.archetype.columns_map[id][r.row] = value
|
||||
if on_add then
|
||||
local value = values[i]
|
||||
on_add(entity, id, value, ROOT_ARCHETYPE)
|
||||
end
|
||||
else
|
||||
if on_add then
|
||||
on_add(entity, id, nil, ROOT_ARCHETYPE)
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
|
@ -2287,25 +2271,43 @@ local function ecs_bulk_insert(world: world, entity: i53, ids: { i53 }, values:
|
|||
|
||||
if from ~= to then
|
||||
entity_move(entity_index, entity, r, to)
|
||||
end
|
||||
|
||||
for i, set in emplaced do
|
||||
local id = ids[i]
|
||||
local idr = component_index[id]
|
||||
|
||||
for i, id in ids do
|
||||
local value = values[i] :: any
|
||||
|
||||
local on_add = idr.on_add
|
||||
|
||||
if value ~= nil then
|
||||
r.archetype.columns_map[id][r.row] = value
|
||||
local on_change = idr.on_change
|
||||
local hook = if set then on_change else on_add
|
||||
if hook then
|
||||
hook(entity, id, value :: any, from)
|
||||
end
|
||||
elseif on_add then
|
||||
on_add(entity, id, nil, from)
|
||||
end
|
||||
|
||||
for i, exists in emplaced do
|
||||
local value = values[i]
|
||||
local id = ids[i]
|
||||
local idr = component_index[id]
|
||||
if exists then
|
||||
local on_change = idr.on_change
|
||||
if on_change then
|
||||
on_change(entity, id, value, from)
|
||||
end
|
||||
else
|
||||
local on_add = idr.on_add
|
||||
if on_add then
|
||||
on_add(entity, id, value, from)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for i, id in ids do
|
||||
local value = values[i] :: any
|
||||
local idr = component_index[id]
|
||||
local on_change = idr.on_change
|
||||
if on_change then
|
||||
on_change(entity, id, value, from)
|
||||
end
|
||||
|
||||
if value ~= nil then
|
||||
r.archetype.columns_map[id][r.row] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2783,7 +2785,9 @@ local function world_new()
|
|||
end
|
||||
end
|
||||
|
||||
type Listener<T> = (e: i53, id: i53, value: T, oldarchetype: archetype) -> ()
|
||||
type Listener<T> =
|
||||
& ((e: i53, id: i53, value: T, oldarchetype: archetype) -> ())
|
||||
& ((e: i53, id: i53, delete: boolean?) -> ())
|
||||
|
||||
world.added = function<T>(_: world, component: i53, fn: Listener<T>)
|
||||
local listeners = signals.added[component]
|
||||
|
|
@ -2839,7 +2843,7 @@ local function world_new()
|
|||
listener(entity, id, value, oldarchetype)
|
||||
end
|
||||
end
|
||||
local existing_hook = world_get(world, component, EcsOnChange) :: Listener<T>
|
||||
local existing_hook = world_get(world, component, EcsOnChange) :: Listener<T>?
|
||||
if existing_hook then
|
||||
table.insert(listeners, existing_hook)
|
||||
end
|
||||
|
|
@ -2870,14 +2874,14 @@ local function world_new()
|
|||
end
|
||||
end
|
||||
|
||||
world.removed = function<T>(_: world, component: i53, fn: (i53, i53) -> ())
|
||||
world.removed = function<T>(_: world, component: i53, fn: (i53, i53, boolean?) -> ())
|
||||
local listeners = signals.removed[component]
|
||||
if not listeners then
|
||||
listeners = {}
|
||||
signals.removed[component] = listeners
|
||||
local function on_remove(entity, id)
|
||||
local function on_remove(entity, id, delete)
|
||||
for _, listener in listeners :: { (...any) -> () } do
|
||||
listener(entity, id)
|
||||
listener(entity, id, delete)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -3088,7 +3092,7 @@ local function world_new()
|
|||
local idr = component_index[id]
|
||||
local on_remove = idr.on_remove
|
||||
if on_remove then
|
||||
on_remove(entity, id)
|
||||
on_remove(entity, id, true)
|
||||
end
|
||||
end
|
||||
archetype_delete(world, record.archetype, record.row)
|
||||
|
|
@ -3096,11 +3100,11 @@ local function world_new()
|
|||
|
||||
local component_index = world.component_index
|
||||
local archetypes = world.archetypes
|
||||
local tgt = ECS_PAIR(EcsWildcard, entity::number)
|
||||
local rel = ECS_PAIR(entity::number, EcsWildcard)
|
||||
local tgt = ECS_PAIR(EcsWildcard, entity)
|
||||
local rel = ECS_PAIR(entity, EcsWildcard)
|
||||
|
||||
local idr_t = component_index[tgt]
|
||||
local idr = component_index[entity::number]
|
||||
local idr = component_index[entity]
|
||||
local idr_r = component_index[rel]
|
||||
|
||||
if idr then
|
||||
|
|
@ -3228,7 +3232,7 @@ local function world_new()
|
|||
local tr_count = counts[archetype_id]
|
||||
local idr_r_types = idr_r_archetype.types
|
||||
for i = tr, tr + tr_count - 1 do
|
||||
local id = types[i]
|
||||
local id = idr_r_types[i]
|
||||
node = archetype_traverse_remove(world, id, node)
|
||||
local on_remove = component_index[id].on_remove
|
||||
if on_remove then
|
||||
|
|
@ -3444,10 +3448,10 @@ return {
|
|||
pair = ECS_PAIR :: <P, O>(first: Entity<P>, second: Entity<O>) -> Pair<P, O>,
|
||||
|
||||
IS_PAIR = ECS_IS_PAIR :: (pair: Component) -> boolean,
|
||||
ECS_PAIR_FIRST = ECS_PAIR_FIRST :: <P, O>(pair: Id<P, O>) -> Component<P>,
|
||||
ECS_PAIR_SECOND = ECS_PAIR_SECOND :: <P, O>(pair: Id<P, O>) -> Component<O>,
|
||||
pair_first = ecs_pair_first :: <P, O>(world: World, pair: Id<P, O>) -> Component<P>,
|
||||
pair_second = ecs_pair_second :: <P, O>(world: World, pair: Id<P, O>) -> Component<O>,
|
||||
ECS_PAIR_FIRST = ECS_PAIR_FIRST :: <P, O>(pair: Id<P>) -> Component<P>,
|
||||
ECS_PAIR_SECOND = ECS_PAIR_SECOND :: <P, O>(pair: Id<P>) -> Component<O>,
|
||||
pair_first = ecs_pair_first :: <P, O>(world: World, pair: Id<P>) -> Component<P>,
|
||||
pair_second = ecs_pair_second :: <P, O>(world: World, pair: Id<P>) -> Component<O>,
|
||||
entity_index_get_alive = entity_index_get_alive,
|
||||
|
||||
archetype_append_to_records = archetype_append_to_records,
|
||||
|
|
|
|||
|
|
@ -222,8 +222,68 @@ TEST("addons/ob::observer", function()
|
|||
end
|
||||
end)
|
||||
|
||||
FOCUS()
|
||||
TEST("addons/ob::monitor", function()
|
||||
local world = jecs.world()
|
||||
|
||||
do CASE [[should not invoke monitor.added callback unless it wasn't apart
|
||||
of the monitor]]
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
local C = world:component()
|
||||
|
||||
local monitor = ob.monitor(world:query(A):without(B, C))
|
||||
local c = 0
|
||||
monitor.added(function()
|
||||
c += 1
|
||||
end)
|
||||
|
||||
local e = world:entity()
|
||||
world:add(e, A)
|
||||
CHECK(c==1)
|
||||
world:add(e, B)
|
||||
world:add(e, C)
|
||||
-- left
|
||||
CHECK(c==1)
|
||||
|
||||
world:remove(e, B)
|
||||
world:remove(e, C)
|
||||
-- re-enters once
|
||||
CHECK(c==2)
|
||||
|
||||
world:remove(e, B)
|
||||
CHECK(c==2)
|
||||
end
|
||||
|
||||
do CASE "deleted entity should only exit monitor once"
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
|
||||
local Destroy = world:entity()
|
||||
|
||||
local c = 0
|
||||
|
||||
local monitor = ob.monitor(world:query(A, B):without(Destroy))
|
||||
|
||||
monitor.added(function()
|
||||
print("enter")
|
||||
end)
|
||||
monitor.removed(function()
|
||||
c += 1
|
||||
end)
|
||||
|
||||
|
||||
local e = world:entity()
|
||||
world:add(e, A)
|
||||
world:add(e, B)
|
||||
|
||||
world:add(e, Destroy)
|
||||
|
||||
world:delete(e)
|
||||
print(c)
|
||||
CHECK(c==1)
|
||||
end
|
||||
|
||||
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()
|
||||
|
|
|
|||
11
test/ecr.luau
Executable file
11
test/ecr.luau
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
local function component()
|
||||
local id = 1
|
||||
local v
|
||||
local function instance()
|
||||
return id, v
|
||||
end
|
||||
return function(value)
|
||||
v = value
|
||||
return instance
|
||||
end
|
||||
end
|
||||
|
|
@ -292,14 +292,18 @@ TEST("bulk", function()
|
|||
world:changed(c3, counter)
|
||||
|
||||
local e = world:entity()
|
||||
jecs.bulk_insert(world, e, {c1,c2,c3}, {1,2,3})
|
||||
jecs.bulk_insert(world, e, {c1,c2}, {1,2,3})
|
||||
|
||||
jecs.bulk_insert(world, e, {c1,c2,c3}, {4,5,6})
|
||||
|
||||
CHECK(world:has(e, c1, c2, c3))
|
||||
CHECK(count == 3)
|
||||
CHECK(count == 2)
|
||||
|
||||
jecs.bulk_insert(world, e, {c1,c2,c3}, {7,8,9})
|
||||
CHECK(count == 2+3)
|
||||
end
|
||||
|
||||
|
||||
do CASE "Should bulk add with hooks moving archetypes without previous"
|
||||
local world = jecs.world()
|
||||
local c1, c2, c3 = world:component(), world:component(), world:component()
|
||||
|
|
|
|||
Loading…
Reference in a new issue