mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Fix cycles (#129)
This commit is contained in:
parent
2ed869ba93
commit
b3da0745bc
2 changed files with 126 additions and 129 deletions
159
src/init.luau
159
src/init.luau
|
@ -992,98 +992,6 @@ do
|
||||||
else
|
else
|
||||||
archetype_fast_delete(columns, column_count, row, types, delete)
|
archetype_fast_delete(columns, column_count, row, types, delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
local component_index = world.componentIndex
|
|
||||||
|
|
||||||
local archetypes = world.archetypes
|
|
||||||
|
|
||||||
local idr = component_index[delete]
|
|
||||||
if idr then
|
|
||||||
local children = {}
|
|
||||||
for archetype_id in idr.cache do
|
|
||||||
local idr_archetype = archetypes[archetype_id]
|
|
||||||
|
|
||||||
for i, child in idr_archetype.entities do
|
|
||||||
table.insert(children, child)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local flags = idr.flags
|
|
||||||
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
|
|
||||||
for _, child in children do
|
|
||||||
-- Cascade deletion to children
|
|
||||||
world_delete(world, child)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for _, child in children do
|
|
||||||
world_remove(world, child, delete)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- TODO: iterate each linked record.
|
|
||||||
-- local r = ECS_PAIR(delete, EcsWildcard)
|
|
||||||
-- local idr_r = component_index[r]
|
|
||||||
-- if idr_r then
|
|
||||||
-- -- Doesn't work for relations atm
|
|
||||||
-- for archetype_id in idr_o.cache do
|
|
||||||
-- local children = {}
|
|
||||||
-- local idr_r_archetype = archetypes[archetype_id]
|
|
||||||
-- local idr_r_types = idr_r_archetype.types
|
|
||||||
|
|
||||||
-- for _, child in idr_r_archetype.entities do
|
|
||||||
-- table.insert(children, child)
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- for _, id in idr_r_types do
|
|
||||||
-- local relation = ECS_ENTITY_T_HI(id)
|
|
||||||
-- if world_target(world, child, relation) == delete then
|
|
||||||
-- world_remove(world, child, ECS_PAIR(relation, delete))
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
|
|
||||||
local o = ECS_PAIR(EcsWildcard, delete)
|
|
||||||
local idr_o = component_index[o]
|
|
||||||
|
|
||||||
if idr_o then
|
|
||||||
for archetype_id in idr_o.cache do
|
|
||||||
local children = {}
|
|
||||||
local idr_o_archetype = archetypes[archetype_id]
|
|
||||||
-- In the future, this needs to be optimized to only
|
|
||||||
-- look for linked records instead of doing this linearly
|
|
||||||
|
|
||||||
local idr_o_types = idr_o_archetype.types
|
|
||||||
|
|
||||||
for _, child in idr_o_archetype.entities do
|
|
||||||
table.insert(children, child)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, id in idr_o_types do
|
|
||||||
if not ECS_IS_PAIR(id) then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
local id_record = component_index[id]
|
|
||||||
|
|
||||||
if id_record then
|
|
||||||
local flags = id_record.flags
|
|
||||||
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
|
|
||||||
for _, child in children do
|
|
||||||
-- Cascade deletions of it has Delete as component trait
|
|
||||||
world_delete(world, child, destruct)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local object = ECS_ENTITY_T_LO(id)
|
|
||||||
if object == delete then
|
|
||||||
for _, child in children do
|
|
||||||
world_remove(world, child, id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function world_delete(world: World, entity: i53, destruct: boolean?)
|
function world_delete(world: World, entity: i53, destruct: boolean?)
|
||||||
|
@ -1103,6 +1011,73 @@ do
|
||||||
archetype_delete(world, archetype, row, destruct)
|
archetype_delete(world, archetype, row, destruct)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local delete = entity
|
||||||
|
local component_index = world.componentIndex
|
||||||
|
local archetypes = world.archetypes
|
||||||
|
local tgt = ECS_PAIR(EcsWildcard, delete)
|
||||||
|
local idr_t = component_index[tgt]
|
||||||
|
local idr = component_index[delete]
|
||||||
|
|
||||||
|
if idr then
|
||||||
|
local children = {}
|
||||||
|
for archetype_id in idr.cache do
|
||||||
|
local idr_archetype = archetypes[archetype_id]
|
||||||
|
|
||||||
|
for i, child in idr_archetype.entities do
|
||||||
|
table.insert(children, child)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local flags = idr.flags
|
||||||
|
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
|
||||||
|
for _, child in children do
|
||||||
|
-- Cascade deletion to children
|
||||||
|
world_delete(world, child)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _, child in children do
|
||||||
|
world_remove(world, child, delete)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if idr_t then
|
||||||
|
for archetype_id in idr_t.cache do
|
||||||
|
local children = {}
|
||||||
|
local idr_o_archetype = archetypes[archetype_id]
|
||||||
|
|
||||||
|
local idr_o_types = idr_o_archetype.types
|
||||||
|
|
||||||
|
for _, child in idr_o_archetype.entities do
|
||||||
|
table.insert(children, child)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, id in idr_o_types do
|
||||||
|
if not ECS_IS_PAIR(id) then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
local id_record = component_index[id]
|
||||||
|
|
||||||
|
if id_record then
|
||||||
|
local flags = id_record.flags
|
||||||
|
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
|
||||||
|
for _, child in children do
|
||||||
|
-- Cascade deletions of it has Delete as component trait
|
||||||
|
world_delete(world, child, destruct)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local object = ECS_ENTITY_T_LO(id)
|
||||||
|
if object == delete then
|
||||||
|
for _, child in children do
|
||||||
|
world_remove(world, child, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
record.archetype = nil :: any
|
record.archetype = nil :: any
|
||||||
entityIndex.sparse[entity] = nil
|
entityIndex.sparse[entity] = nil
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,10 @@ local function debug_world_inspect(world)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function name(world, e)
|
||||||
|
return world:get(e, jecs.Name)
|
||||||
|
end
|
||||||
|
|
||||||
TEST("world:entity()", function()
|
TEST("world:entity()", function()
|
||||||
do CASE "unique IDs"
|
do CASE "unique IDs"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
@ -254,7 +258,6 @@ TEST("world:query()", function()
|
||||||
for x in q:iter() do
|
for x in q:iter() do
|
||||||
counter += 1
|
counter += 1
|
||||||
end
|
end
|
||||||
print(counter)
|
|
||||||
CHECK(counter == 2)
|
CHECK(counter == 2)
|
||||||
end
|
end
|
||||||
do CASE "tag"
|
do CASE "tag"
|
||||||
|
@ -764,32 +767,32 @@ TEST("world:clear()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:has()", function()
|
TEST("world:has()", function()
|
||||||
do CASE "should find Tag on entity"
|
do CASE "should find Tag on entity"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Tag = world:entity()
|
local Tag = world:entity()
|
||||||
|
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:add(e, Tag)
|
world:add(e, Tag)
|
||||||
|
|
||||||
CHECK(world:has(e, Tag))
|
CHECK(world:has(e, Tag))
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "should return false when missing one tag"
|
do CASE "should return false when missing one tag"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local A = world:entity()
|
local A = world:entity()
|
||||||
local B = world:entity()
|
local B = world:entity()
|
||||||
local C = world:entity()
|
local C = world:entity()
|
||||||
local D = world:entity()
|
local D = world:entity()
|
||||||
|
|
||||||
local e = world:entity()
|
local e = world:entity()
|
||||||
world:add(e, A)
|
world:add(e, A)
|
||||||
world:add(e, C)
|
world:add(e, C)
|
||||||
world:add(e, D)
|
world:add(e, D)
|
||||||
|
|
||||||
CHECK(world:has(e, A, B, C, D) == false)
|
CHECK(world:has(e, A, B, C, D) == false)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:component()", function()
|
TEST("world:component()", function()
|
||||||
|
@ -820,27 +823,46 @@ TEST("world:component()", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("world:delete", function()
|
TEST("world:delete", function()
|
||||||
|
do CASE "bug: Empty entity does not respect cleanup policy"
|
||||||
|
local world = world_new()
|
||||||
|
local parent = world:entity()
|
||||||
|
local tag = world:entity()
|
||||||
|
|
||||||
|
local child = world:entity()
|
||||||
|
world:add(child, jecs.pair(jecs.ChildOf, parent))
|
||||||
|
world:delete(parent)
|
||||||
|
|
||||||
|
CHECK(not world:contains(parent))
|
||||||
|
CHECK(not world:contains(child))
|
||||||
|
|
||||||
|
local entity = world:entity()
|
||||||
|
world:add(entity, tag)
|
||||||
|
world:delete(tag)
|
||||||
|
CHECK(world:contains(entity))
|
||||||
|
CHECK(not world:contains(tag))
|
||||||
|
CHECK(not world:has(entity, tag)) -- => true
|
||||||
|
end
|
||||||
do CASE("should allow deleting components")
|
do CASE("should allow deleting components")
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
|
||||||
local Health = world:component()
|
local Health = world:component()
|
||||||
local Poison = world:component()
|
local Poison = world:component()
|
||||||
|
|
||||||
local id = world:entity()
|
local id = world:entity()
|
||||||
world:set(id, Poison, 5)
|
world:set(id, Poison, 5)
|
||||||
world:set(id, Health, 50)
|
world:set(id, Health, 50)
|
||||||
local id1 = world:entity()
|
local id1 = world:entity()
|
||||||
world:set(id1, Poison, 500)
|
world:set(id1, Poison, 500)
|
||||||
world:set(id1, Health, 50)
|
world:set(id1, Health, 50)
|
||||||
|
|
||||||
world:delete(id)
|
world:delete(id)
|
||||||
CHECK(not world:contains(id))
|
CHECK(not world:contains(id))
|
||||||
CHECK(world:get(id, Poison) == nil)
|
CHECK(world:get(id, Poison) == nil)
|
||||||
CHECK(world:get(id, Health) == nil)
|
CHECK(world:get(id, Health) == nil)
|
||||||
|
|
||||||
CHECK(world:get(id1, Poison) == 500)
|
CHECK(world:get(id1, Poison) == 500)
|
||||||
CHECK(world:get(id1, Health) == 50)
|
CHECK(world:get(id1, Health) == 50)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "delete entities using another Entity as component with Delete cleanup action"
|
do CASE "delete entities using another Entity as component with Delete cleanup action"
|
||||||
local world = jecs.World.new()
|
local world = jecs.World.new()
|
||||||
|
@ -918,7 +940,7 @@ TEST("world:delete", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
for i, friend in friends do
|
for i, friend in friends do
|
||||||
CHECK(not world:has(friends, pair(jecs.ChildOf, e)))
|
CHECK(not world:has(friend, pair(FriendsWith, e)))
|
||||||
CHECK(world:has(friend, Health))
|
CHECK(world:has(friend, Health))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue