diff --git a/core/src/cubos/core/ecs/world.cpp b/core/src/cubos/core/ecs/world.cpp index 6100ebea5..3ca2e074f 100644 --- a/core/src/cubos/core/ecs/world.cpp +++ b/core/src/cubos/core/ecs/world.cpp @@ -256,7 +256,34 @@ auto World::Components::add(const reflection::Type& type, void* value) -> Compon newTable.column(columnId).pushMove(value); // For each sparse relation table the entity is on, move its relations to the new corresponding one. - // TODO + for (const auto& [_, index] : mWorld.mTables.sparseRelation()) + { + // For each table where the entity's archetype is the 'from' archetype. + if (index.from().contains(oldArchetype)) + { + for (const auto& oldTableId : index.from().at(oldArchetype)) + { + // Move all occurrences of the entity in the 'from' column to the new table. + auto newTableId = oldTableId; + newTableId.from = newArchetype; + auto& newTable = mWorld.mTables.sparseRelation().create(newTableId, mWorld.mTypes); + mWorld.mTables.sparseRelation().at(oldTableId).moveFrom(mEntity.index, newTable); + } + } + + // For each table where the entity's archetype is the 'to' archetype. + if (index.to().contains(oldArchetype)) + { + for (const auto& oldTableId : index.to().at(oldArchetype)) + { + // Move all occurrences of the entity in the 'to' column to the new table. + auto newTableId = oldTableId; + newTableId.to = newArchetype; + auto& newTable = mWorld.mTables.sparseRelation().create(newTableId, mWorld.mTypes); + mWorld.mTables.sparseRelation().at(oldTableId).moveTo(mEntity.index, newTable); + } + } + } CUBOS_DEBUG("Added component {} to entity {}", type.name(), mEntity, mEntity); return *this; @@ -288,7 +315,34 @@ auto World::Components::remove(const reflection::Type& type) -> Components& oldTable.swapMove(mEntity.index, newTable); // For each sparse relation table the entity is on, move its relations to the new corresponding one. - // TODO + for (const auto& [_, index] : mWorld.mTables.sparseRelation()) + { + // For each table where the entity's archetype is the 'from' archetype. + if (index.from().contains(oldArchetype)) + { + for (const auto& oldTableId : index.from().at(oldArchetype)) + { + // Move all occurrences of the entity in the 'from' column to the new table. + auto newTableId = oldTableId; + newTableId.from = newArchetype; + auto& newTable = mWorld.mTables.sparseRelation().create(newTableId, mWorld.mTypes); + mWorld.mTables.sparseRelation().at(oldTableId).moveFrom(mEntity.index, newTable); + } + } + + // For each table where the entity's archetype is the 'to' archetype. + if (index.to().contains(oldArchetype)) + { + for (const auto& oldTableId : index.to().at(oldArchetype)) + { + // Move all occurrences of the entity in the 'to' column to the new table. + auto newTableId = oldTableId; + newTableId.to = newArchetype; + auto& newTable = mWorld.mTables.sparseRelation().create(newTableId, mWorld.mTypes); + mWorld.mTables.sparseRelation().at(oldTableId).moveTo(mEntity.index, newTable); + } + } + } CUBOS_DEBUG("Removed component {} from entity {}", type.name(), mEntity); return *this; diff --git a/core/tests/ecs/world.cpp b/core/tests/ecs/world.cpp index 60684f48f..410820635 100644 --- a/core/tests/ecs/world.cpp +++ b/core/tests/ecs/world.cpp @@ -209,4 +209,37 @@ TEST_CASE("ecs::World") CHECK(destroyed); } + + SUBCASE("relations are moved correctly between archetypes") + { + bool destroyed = false; + auto foo = world.create(); + auto bar = world.create(); + + // Add a relation between foo and bar. + REQUIRE_FALSE(world.related(foo, bar)); + world.relate(foo, bar, DetectDestructorRelation{{&destroyed}}); + REQUIRE(world.related(foo, bar)); + REQUIRE_FALSE(destroyed); + + // Change the archetype of foo. The entities should still be related. + world.components(foo).add(IntegerComponent{0}); + REQUIRE(world.related(foo, bar)); + REQUIRE_FALSE(destroyed); + + // Change the archetype of bar. The entities should still be related. + world.components(bar).add(IntegerComponent{0}); + REQUIRE(world.related(foo, bar)); + REQUIRE_FALSE(destroyed); + + // Change the archetypes once again. + world.components(foo).remove(); + world.components(bar).remove(); + REQUIRE(world.related(foo, bar)); + REQUIRE_FALSE(destroyed); + + // Destroy foo. The relation should be destroyed. + world.destroy(foo); + REQUIRE(destroyed); + } }