Update removal functions to handle more variants

This commit is contained in:
Ukendio 2025-03-27 21:49:44 +01:00
parent b81bd6eea8
commit 4eb15bbb55
2 changed files with 131 additions and 83 deletions

150
jecs.luau
View file

@ -1064,21 +1064,19 @@ local function archetype_delete(world: ecs_world_t, archetype: ecs_archetype_t,
end
local function world_clear(world: ecs_world_t, entity: i53)
--TODO: use sparse_get (stashed)
local record = entity_index_try_get(world.entity_index, entity)
if not record then
return
end
local component_index = world.component_index
local archetypes = world.archetypes
local tgt = ECS_PAIR(EcsWildcard, entity)
local idr_t = component_index[tgt]
local idr = component_index[entity]
local rel = ECS_PAIR(entity, EcsWildcard)
local idr_r = component_index[rel]
local archetype = record.archetype
local row = record.row
local idr = world.component_index[entity]
if idr then
local count = 0
local queue = {}
for archetype_id in idr.cache do
local idr_archetype = world.archetypes[archetype_id]
local idr_archetype = archetypes[archetype_id]
local entities = idr_archetype.entities
local n = #entities
count += n
@ -1089,14 +1087,79 @@ local function world_clear(world: ecs_world_t, entity: i53)
end
end
if archetype then
-- In the future should have a destruct mode for
-- deleting archetypes themselves. Maybe requires recycling
archetype_delete(world, archetype, row)
if idr_t then
local children
local ids
local count = 0
local archetype_ids = idr_t.cache
for archetype_id in archetype_ids do
local idr_t_archetype = archetypes[archetype_id]
local idr_t_types = idr_t_archetype.types
local entities = idr_t_archetype.entities
local removal_queued = false
for _, id in idr_t_types do
if not ECS_IS_PAIR(id) then
continue
end
local object = entity_index_get_alive(
entity_index, ECS_PAIR_SECOND(id))
if object ~= entity then
continue
end
if not ids then
ids = {}
end
ids[id] = true
removal_queued = true
end
record.archetype = nil :: any
record.row = nil :: any
if not removal_queued then
continue
end
if not queue then
queue = {}
end
local n = #entities
table.move(entities, 1, n, count + 1, queue)
count += n
end
for id in ids do
for _, child in queue do
world_remove(world, child, id)
end
end
end
if idr_r then
local count = 0
local archetype_ids = idr_r.cache
local ids = {}
local queue = {}
for archetype_id in archetype_ids do
local idr_r_archetype = archetypes[archetype_id]
local entities = idr_r_archetype.entities
local tr = idr_r_archetype.records[rel]
local tr_count = idr_r_archetype.counts[rel]
local types = idr_r_archetype.types
for i = tr, tr + tr_count - 1 do
ids[types[i]] = true
end
local n = #entities
table.move(entities, 1, n, count + 1, queue)
count += n
end
for _, e in queue do
for id in ids do
world_remove(world, e, id)
end
end
end
end
local function archetype_disconnect_edge(edge: ecs_graph_edge_t)
@ -1225,8 +1288,11 @@ local function world_delete(world: ecs_world_t, entity: i53)
local component_index = world.component_index
local archetypes = world.archetypes
local tgt = ECS_PAIR(EcsWildcard, delete)
local rel = ECS_PAIR(delete, EcsWildcard)
local idr_t = component_index[tgt]
local idr = component_index[delete]
local idr_r = component_index[rel]
if idr then
local flags = idr.flags
@ -1256,11 +1322,10 @@ local function world_delete(world: ecs_world_t, entity: i53)
end
end
local dense_array = entity_index.dense_array
if idr_t then
local children
local ids
local count = 0
local archetype_ids = idr_t.cache
for archetype_id in archetype_ids do
@ -1308,8 +1373,8 @@ local function world_delete(world: ecs_world_t, entity: i53)
end
if ids then
for id in ids do
for _, child in children do
for id in ids do
world_remove(world, child, id)
end
end
@ -1320,6 +1385,51 @@ local function world_delete(world: ecs_world_t, entity: i53)
end
end
if idr_r then
local count = 0
local archetype_ids = idr_r.cache
local flags = idr_r.flags
if bit32.band(flags, ECS_ID_DELETE) ~= 0 then
for archetype_id in archetype_ids do
local idr_r_archetype = archetypes[archetype_id]
local entities = idr_r_archetype.entities
local n = #entities
for i = n, 1, -1 do
world_delete(world, entities[i])
end
archetype_destroy(world, idr_r_archetype)
end
else
local children = {}
local count = 0
local ids = {}
for archetype_id in archetype_ids do
local idr_r_archetype = archetypes[archetype_id]
local entities = idr_r_archetype.entities
local tr = idr_r_achetype.records[rel]
local tr_count = idr_r_archetype.counts[rel]
local types = idr_r_achetype.types
for i = tr, tr_count - 1 do
ids[types[tr]] = true
end
local n = #entities
table.move(entities, 1, n, count + 1, children)
count += n
end
for _, child in children do
for id in ids do
world_remove(world, child, id)
end
end
for archetype_id in archetype_ids do
archetype_destroy(world, archetypes[archetype_id])
end
end
end
local dense_array = entity_index.dense_array
local index_of_deleted_entity = record.dense
local index_of_last_alive_entity = entity_index.alive_count
entity_index.alive_count = index_of_last_alive_entity - 1
@ -1350,8 +1460,6 @@ export type QueryInner = {
world: World,
}
local function query_iter_init(query: ecs_query_data_t): () -> (number, ...any)
local world_query_iter_next

View file

@ -1109,66 +1109,6 @@ TEST("world:clear()", function()
CHECK(world:get(e1, A) == nil)
CHECK(world:get(e1, B))
end
do CASE("should remove its components")
local world = jecs.World.new() :: World
local A = world:component()
local B = world:component()
local e = world:entity()
world:set(e, A, true)
world:set(e, B, true)
CHECK(world:get(e, A))
CHECK(world:get(e, B))
world:clear(e)
CHECK(world:get(e, A) == nil)
CHECK(world:get(e, B) == nil)
end
do CASE("should move last record")
local world = world_new()
local A = world:component()
local e = world:entity()
local e1 = world:entity()
world:add(e, A)
world:add(e1, A)
local archetype = world.archetype_index["1"]
local archetype_entities = archetype.entities
local _e = e :: number
local _e1 = e1 :: number
CHECK(archetype_entities[1] == _e)
CHECK(archetype_entities[2] == _e1)
local e_record: jecs.Record = entity_index_try_get_any(
world.entity_index, e) :: any
local e1_record: jecs.Record = entity_index_try_get_any(
world.entity_index, e1) :: any
CHECK(e_record.archetype == archetype)
CHECK(e1_record.archetype == archetype)
CHECK(e1_record.row == 2)
world:clear(e)
CHECK((e_record.archetype :: jecs.Archetype?) == nil)
CHECK((e_record.row :: number?) == nil)
CHECK(e1_record.archetype == archetype)
CHECK(e1_record.row == 1)
CHECK(archetype_entities[1] == _e1)
CHECK(archetype_entities[2] == nil)
CHECK(world:contains(e) == true)
CHECK(world:has(e, A) == false)
CHECK(world:contains(e1) == true)
CHECK(world:has(e1, A) == true)
end
end)
TEST("world:has()", function()