mirror of
https://github.com/Ukendio/jecs.git
synced 2026-02-04 15:15:21 +00:00
Should be able to delete every archetype that is iterated
This commit is contained in:
parent
f543c06462
commit
3ebb2334da
2 changed files with 31 additions and 9 deletions
|
|
@ -3084,6 +3084,9 @@ local function world_new()
|
||||||
|
|
||||||
local archetype = record.archetype
|
local archetype = record.archetype
|
||||||
if archetype then
|
if archetype then
|
||||||
|
-- NOTE(marcus): It is important to remove the data and invoke
|
||||||
|
-- the hooks before the archetype and certain component records are
|
||||||
|
-- invalidated or else it will have a nasty runtime error.
|
||||||
for _, id in archetype.types do
|
for _, id in archetype.types do
|
||||||
local cr = component_index[id]
|
local cr = component_index[id]
|
||||||
local on_remove = cr.on_remove
|
local on_remove = cr.on_remove
|
||||||
|
|
@ -3103,6 +3106,29 @@ local function world_new()
|
||||||
local idr = component_index[entity]
|
local idr = component_index[entity]
|
||||||
local idr_r = component_index[rel]
|
local idr_r = component_index[rel]
|
||||||
|
|
||||||
|
--[[
|
||||||
|
It is important to note that `world_delete` uses a depth-first
|
||||||
|
traversal that prunes the children of the entity before their
|
||||||
|
parents. archetypes can be destroyed and removed from component
|
||||||
|
records while we're still iterating over those records. The
|
||||||
|
recursive nature of this function entails that archetype ids can be
|
||||||
|
removed from component records (idr_t.records, idr.records and
|
||||||
|
idr_r.records) while that collection is still being iterated over.
|
||||||
|
If we try to look up an archetype by ID after it has been destroyed,
|
||||||
|
we get nil. This is hard to debug because the removal happens deep
|
||||||
|
in the opaque call stack. Essentially the entry is removed on a
|
||||||
|
first come first serve basis.
|
||||||
|
|
||||||
|
The solution is to hold onto the borrowed references of the
|
||||||
|
archetypes in the lexical scope and make assumptions that the data
|
||||||
|
itself is not invalidated until `archetype_destroy` is called on it.
|
||||||
|
This is done since it is the only efficient way of doing it, however
|
||||||
|
we must veer on the edge of incorrectness. This is mostly acceptable
|
||||||
|
because it is not generally observable in the user-land but it has
|
||||||
|
caused subtle when something goes wrong.
|
||||||
|
|
||||||
|
- Marcus
|
||||||
|
]]
|
||||||
if idr then
|
if idr then
|
||||||
local flags = idr.flags
|
local flags = idr.flags
|
||||||
if (bit32.btest(flags, ECS_ID_DELETE) == true) then
|
if (bit32.btest(flags, ECS_ID_DELETE) == true) then
|
||||||
|
|
@ -3162,7 +3188,6 @@ 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,7 +3198,6 @@ 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)
|
||||||
|
|
@ -3199,11 +3223,9 @@ local function world_new()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if will_delete_archetype then
|
|
||||||
archetype_destroy(world, idr_t_archetype)
|
archetype_destroy(world, idr_t_archetype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if idr_r then
|
if idr_r then
|
||||||
local archetype_ids = idr_r.records
|
local archetype_ids = idr_r.records
|
||||||
|
|
@ -3245,10 +3267,8 @@ local function world_new()
|
||||||
local r = entity_index_try_get_unsafe(e) :: record
|
local r = entity_index_try_get_unsafe(e) :: record
|
||||||
inner_entity_move(e, r, node)
|
inner_entity_move(e, r, node)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
for archetype_id in archetype_ids do
|
archetype_destroy(world, idr_r_archetype)
|
||||||
archetype_destroy(world, archetypes[archetype_id])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,9 @@ TEST("reproduce idr_t nil archetype bug", function()
|
||||||
-- Removing this, but keeping Animator on the other 2 causes it to stop happening
|
-- Removing this, but keeping Animator on the other 2 causes it to stop happening
|
||||||
world:set(trackEntity1, cts.VelocitizeAnimationWeight, 0)
|
world:set(trackEntity1, cts.VelocitizeAnimationWeight, 0)
|
||||||
|
|
||||||
|
print("delete root")
|
||||||
world:delete(root)
|
world:delete(root)
|
||||||
|
print("---")
|
||||||
|
|
||||||
for entityId, info in trackedEntities do
|
for entityId, info in trackedEntities do
|
||||||
if world:contains(entityId) and not world:parent(entityId :: any) then
|
if world:contains(entityId) and not world:parent(entityId :: any) then
|
||||||
|
|
@ -105,7 +107,7 @@ TEST("reproduce idr_t nil archetype bug", function()
|
||||||
print(`batch = {batch}, i = {i}`)
|
print(`batch = {batch}, i = {i}`)
|
||||||
print("==========================================")
|
print("==========================================")
|
||||||
trackedEntities[entityId] = nil
|
trackedEntities[entityId] = nil
|
||||||
world:deletee(entityId)
|
world:delete(entityId)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue