diff --git a/libs/selection/EntitySelection.h b/libs/selection/EntitySelection.h index 54af8a8c0a..9b0e43602a 100644 --- a/libs/selection/EntitySelection.h +++ b/libs/selection/EntitySelection.h @@ -57,11 +57,6 @@ class EntitySelection final : ~SpawnargTracker() { - if (_node.expired()) - { - return; // cannot unsubscribe, the node is gone - } - // Call the onEntityRemoved method instead of relying on the onKeyErase() // invocations when detaching the observer. This allows us to keep some // assumptions about entity count in the CollectiveSpawnargs::onKeyErase method @@ -71,8 +66,11 @@ class EntitySelection final : auto entity = _entity; _entity = nullptr; - // Detaching the observer won't do anything here anymore - entity->detachObserver(this); + if (!_node.expired()) + { + // Detaching the observer won't do anything here anymore + entity->detachObserver(this); + } } scene::INodePtr getNode() const diff --git a/test/EntityInspector.cpp b/test/EntityInspector.cpp index d6a52cf287..1ab4caa3cf 100644 --- a/test/EntityInspector.cpp +++ b/test/EntityInspector.cpp @@ -7,6 +7,7 @@ #include "iselectable.h" #include "selection/EntitySelection.h" #include "algorithm/Entity.h" +#include "scenelib.h" namespace test { @@ -494,4 +495,32 @@ TEST_F(EntityInspectorTest, UndoRedoKeyValueAdditionRemoval) } } +TEST_F(EntityInspectorTest, DeletedEntitiesAreSafelyUntracked) +{ + KeyValueStore keyValueStore; + + // Create a single entity + auto cls = GlobalEntityClassManager().findClass("atdm:ai_builder_guard"); + auto guardNode = GlobalEntityModule().createEntity(cls); + + // Create a weak reference to check whether the entity is gone + std::weak_ptr weakGuardNode(guardNode); + + scene::addNodeToContainer(guardNode, GlobalMapModule().getRoot()); + Node_setSelected(guardNode, true); + + keyValueStore.rescanSelection(); + + EXPECT_FALSE(keyValueStore.store.empty()) << "No key values visible after selecting the entity"; + guardNode.reset(); + + // Close the existing map, this should release all references + GlobalCommandSystem().executeCommand("NewMap"); + + EXPECT_TRUE(weakGuardNode.expired()) << "Guard node is still alive after changing maps"; + keyValueStore.rescanSelection(); + + EXPECT_TRUE(keyValueStore.store.empty()) << "No key values should be visible after changing maps"; +} + }