Skip to content

Commit

Permalink
Fix cycles
Browse files Browse the repository at this point in the history
  • Loading branch information
Ukendio committed Sep 20, 2024
1 parent 2ed869b commit 779bbb9
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 129 deletions.
159 changes: 67 additions & 92 deletions src/init.luau
Original file line number Diff line number Diff line change
Expand Up @@ -992,98 +992,6 @@ do
else
archetype_fast_delete(columns, column_count, row, types, delete)
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

function world_delete(world: World, entity: i53, destruct: boolean?)
Expand All @@ -1103,6 +1011,73 @@ do
archetype_delete(world, archetype, row, destruct)
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
entityIndex.sparse[entity] = nil

Expand Down
96 changes: 59 additions & 37 deletions test/tests.luau
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ local function debug_world_inspect(world)
}
end

local function name(world, e)
return world:get(e, jecs.Name)
end

TEST("world:entity()", function()
do CASE "unique IDs"
local world = jecs.World.new()
Expand Down Expand Up @@ -254,7 +258,6 @@ TEST("world:query()", function()
for x in q:iter() do
counter += 1
end
print(counter)
CHECK(counter == 2)
end
do CASE "tag"
Expand Down Expand Up @@ -764,32 +767,32 @@ TEST("world:clear()", function()
end)

TEST("world:has()", function()
do CASE "should find Tag on entity"
local world = jecs.World.new()
do CASE "should find Tag on entity"
local world = jecs.World.new()

local Tag = world:entity()
local Tag = world:entity()

local e = world:entity()
world:add(e, Tag)
local e = world:entity()
world:add(e, Tag)

CHECK(world:has(e, Tag))
end
CHECK(world:has(e, Tag))
end

do CASE "should return false when missing one tag"
local world = jecs.World.new()
do CASE "should return false when missing one tag"
local world = jecs.World.new()

local A = world:entity()
local B = world:entity()
local C = world:entity()
local D = world:entity()
local A = world:entity()
local B = world:entity()
local C = world:entity()
local D = world:entity()

local e = world:entity()
world:add(e, A)
world:add(e, C)
world:add(e, D)
local e = world:entity()
world:add(e, A)
world:add(e, C)
world:add(e, D)

CHECK(world:has(e, A, B, C, D) == false)
end
CHECK(world:has(e, A, B, C, D) == false)
end
end)

TEST("world:component()", function()
Expand Down Expand Up @@ -820,27 +823,46 @@ TEST("world:component()", function()
end)

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")
local world = jecs.World.new()
local world = jecs.World.new()

local Health = world:component()
local Poison = world:component()
local Health = world:component()
local Poison = world:component()

local id = world:entity()
world:set(id, Poison, 5)
world:set(id, Health, 50)
local id1 = world:entity()
world:set(id1, Poison, 500)
world:set(id1, Health, 50)
local id = world:entity()
world:set(id, Poison, 5)
world:set(id, Health, 50)
local id1 = world:entity()
world:set(id1, Poison, 500)
world:set(id1, Health, 50)

world:delete(id)
CHECK(not world:contains(id))
CHECK(world:get(id, Poison) == nil)
CHECK(world:get(id, Health) == nil)
world:delete(id)
CHECK(not world:contains(id))
CHECK(world:get(id, Poison) == nil)
CHECK(world:get(id, Health) == nil)

CHECK(world:get(id1, Poison) == 500)
CHECK(world:get(id1, Health) == 50)
end
CHECK(world:get(id1, Poison) == 500)
CHECK(world:get(id1, Health) == 50)
end

do CASE "delete entities using another Entity as component with Delete cleanup action"
local world = jecs.World.new()
Expand Down Expand Up @@ -918,7 +940,7 @@ TEST("world:delete", function()
end)

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))
end
end
Expand Down

0 comments on commit 779bbb9

Please sign in to comment.