From 6a68ec30a279c1942753a3cea9be95fc63f8781b Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sun, 17 Nov 2024 12:46:33 +0100 Subject: [PATCH] potential fix2 --- core/src/ecs/world.cpp | 47 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/core/src/ecs/world.cpp b/core/src/ecs/world.cpp index ce882307f8..72dc0634aa 100644 --- a/core/src/ecs/world.cpp +++ b/core/src/ecs/world.cpp @@ -176,6 +176,7 @@ void World::destroy(Entity entity) mEntityPool.destroy(entity.index); mTables.dense().at(archetype).swapErase(entity.index); + std::vector> delayedPropagateDepth{}; for (const auto& [type, index] : mTables.sparseRelation()) { // For each table where the entity's archetype is the 'from' archetype. @@ -184,11 +185,6 @@ void World::destroy(Entity entity) for (const auto& tableId : index.from().at(archetype)) { // Erase all occurrences of the entity in the 'from' column. - CUBOS_ASSERT(mTables.sparseRelation().contains(tableId), - "Sparse relation table {} {} {} {} in index does not exist. Obtained through relation " - "type {} for a 'from' archetype {} of entity {}", - tableId.from.inner, tableId.to.inner, tableId.dataType.inner, tableId.depth, type.inner, - archetype.inner, entity); mTables.sparseRelation().at(tableId).eraseFrom(entity.index); } } @@ -198,32 +194,41 @@ void World::destroy(Entity entity) { for (const auto& tableId : index.to().at(archetype)) { - CUBOS_ASSERT(mTables.sparseRelation().contains(tableId), - "Sparse relation table {} {} {} {} in index does not exist. Obtained through relation " - "type {} for a 'to' archetype {} of entity {}", - tableId.from.inner, tableId.to.inner, tableId.dataType.inner, tableId.depth, type.inner, - archetype.inner, entity); auto& table = mTables.sparseRelation().at(tableId); if (mTypes.isTreeRelation(type)) { // If the relation is tree-like, then we need to update the depth of the corresponding 'from' - // entities. - for (auto row = table.firstTo(entity.index); row != table.size(); row = table.nextTo(row)) - { - uint32_t fromIndex; - uint32_t toIndex; - table.indices(row, fromIndex, toIndex); - this->propagateDepth(fromIndex, type, 0); - } + // entities. We delay this to after the loop has finished, as we're iterating over all tables and + // propagateDepth() may create new tables, which can lead to UB. + // + // We won't erase the relation here, as we'll need to iterate over the table below. + delayedPropagateDepth.push_back(std::make_pair(type, &table)); + } + else + { + // Erase all occurrences of the entity in the 'to' column. + table.eraseTo(entity.index); } - - // Erase all occurrences of the entity in the 'to' column. - table.eraseTo(entity.index); } } } + // Process the delayed depth propagation. + for (auto [type, table] : delayedPropagateDepth) + { + for (auto row = table->firstTo(entity.index); row != table->size(); row = table->nextTo(row)) + { + uint32_t fromIndex; + uint32_t toIndex; + table->indices(row, fromIndex, toIndex); + this->propagateDepth(fromIndex, type, 0); + } + + // Now it's safe to erase the relations. + table->eraseTo(entity.index); + } + // Run commands from observers after entity is destroyed if (observed) {