diff --git a/src/init.luau b/src/init.luau index 65848cf..725e4c4 100644 --- a/src/init.luau +++ b/src/init.luau @@ -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?) @@ -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 diff --git a/test/tests.luau b/test/tests.luau index 6240e7a..3c6f8c5 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -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() @@ -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" @@ -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() @@ -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() @@ -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