Only delete archetypes when completely invalidated

This commit is contained in:
Ukendio 2025-12-09 20:34:05 +01:00
parent 007097b791
commit 4db44476a9
2 changed files with 82 additions and 14 deletions

View file

@ -3084,17 +3084,6 @@ local function world_new()
local archetype = record.archetype local archetype = record.archetype
if archetype then
for _, id in archetype.types do
local idr = component_index[id]
local on_remove = idr.on_remove
if on_remove then
on_remove(entity, id, true)
end
end
archetype_delete(world, record.archetype, record.row)
end
local component_index = world.component_index local component_index = world.component_index
local archetypes = world.archetypes local archetypes = world.archetypes
local tgt = ECS_PAIR(EcsWildcard, entity) local tgt = ECS_PAIR(EcsWildcard, entity)
@ -3163,6 +3152,7 @@ local function world_new()
local idr_t_archetype = archetypes[archetype_id] local idr_t_archetype = archetypes[archetype_id]
local idr_t_types = idr_t_archetype.types local idr_t_types = idr_t_archetype.types
local entities = idr_t_archetype.entities local entities = idr_t_archetype.entities
local will_delete_archetype = false
for _, id in idr_t_types do for _, id in idr_t_types do
if not ECS_IS_PAIR(id) then if not ECS_IS_PAIR(id) then
@ -3173,6 +3163,7 @@ local function world_new()
if object ~= entity then if object ~= entity then
continue continue
end end
will_delete_archetype = true
local id_record = component_index[id] local id_record = component_index[id]
local flags = id_record.flags local flags = id_record.flags
local flags_delete_mask = bit32.btest(flags, ECS_ID_DELETE) local flags_delete_mask = bit32.btest(flags, ECS_ID_DELETE)
@ -3197,10 +3188,10 @@ local function world_new()
end end
end end
end end
end
for archetype_id in archetype_ids do if will_delete_archetype then
archetype_destroy(world, archetypes[archetype_id]) archetype_destroy(world, idr_t_archetype)
end
end end
end end
@ -3252,6 +3243,16 @@ local function world_new()
end end
end end
if archetype then
for _, id in archetype.types do
local cr = component_index[id]
local on_remove = cr.on_remove
if on_remove then
on_remove(entity, id, true)
end
end
archetype_delete(world, record.archetype, record.row)
end
local dense = record.dense local dense = record.dense
local i_swap = entity_index.alive_count local i_swap = entity_index.alive_count

View file

@ -24,6 +24,73 @@ type Id<T=unknown> = jecs.Id<T>
local entity_visualiser = require("@modules/entity_visualiser") local entity_visualiser = require("@modules/entity_visualiser")
local dwi = entity_visualiser.stringify local dwi = entity_visualiser.stringify
FOCUS()
TEST("reproduce idr_t nil archetype bug", function()
local world = jecs.world()
local cts = {
Humanoid = world:component(),
Animator = world:component(),
VelocitizeAnimationWeight = world:component(),
}
local char = world:entity()
-- REMOVING ONE OF THESE THESE OFFSETS i BY +1
world:set(char, cts.Humanoid, 0)
world:set(char, cts.Animator, 0)
--
world:added(cts.Humanoid, function() end) -- REMOVING THIS OFFSETS i BY +1 TOO
world:removed(cts.Animator, function(entity, id)
local r = jecs.record(world, entity)
local src = r.archetype
--REMOVING THIS jecs.archetype_traverse_remove CALL STOPS IT FROM HAPPENING
local dst = src and jecs.archetype_traverse_remove(world, id, src)
end)
local batches = 10
local batchSize = 20
local trackedEntities: { [number]: { parentId: number? } } = {}
for batch = 1, batches do
for i = 1, batchSize do
local root = world:entity()
world:add(root, jecs.pair(jecs.ChildOf, char))
-- Removing animator from trackEntity1 causes it to stop happening
local trackEntity1 = world:entity()
world:set(trackEntity1, cts.Animator, 0)
world:add(trackEntity1, jecs.pair(jecs.ChildOf, root))
trackedEntities[trackEntity1] = { parentId = root }
-- Removing animator from trackEntity2 causes it to happen less frequently
local trackEntity2 = world:entity()
world:set(trackEntity2, cts.Animator, 0)
world:add(trackEntity2, jecs.pair(jecs.ChildOf, root))
trackedEntities[trackEntity2] = { parentId = root }
-- Removing this, but keeping Animator on the other 2 causes it to stop happening
world:set(trackEntity1, cts.VelocitizeAnimationWeight, 0)
world:delete(root)
for entityId, info in trackedEntities do
if world:contains(entityId) and not world:parent(entityId :: any) then
print(`bugged entity found: {entityId}`)
print(`original parent: {info.parentId}`)
print(`batch = {batch}, i = {i}`)
print("==========================================")
trackedEntities[entityId] = nil
world:deletee(entityId)
end
end
end
end
end)
TEST("Ensure archetype edges get cleaned", function() TEST("Ensure archetype edges get cleaned", function()
local A = jecs.component() local A = jecs.component()
local B = jecs.component() local B = jecs.component()