From 341c2716688e07ae40e4b2620846a523ad016af1 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Wed, 11 Mar 2026 09:45:06 +0100 Subject: [PATCH 1/2] bugfix(opencontain): Restore retail compatibility after crash fix in OpenContain::killAllContained. --- .../GameLogic/Object/Contain/OpenContain.cpp | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp index 3f3a245f703..29cf234ccdb 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp @@ -452,27 +452,41 @@ void OpenContain::killAllContained() // This scenario can happen if the killed occupant(s) apply deadly damage on death // to the host container, which then attempts to remove all remaining occupants // on the death of the host container. This is reproducible by shooting with - // Neutron Shells on a GLA Technical containing GLA Terrorists. + // Neutron Shells on a GLA Technical containing GLA Toxin Terrorists + // with the Chem_SuicideWeapon upgrade, which is automatically granted by the GLA Toxin Command Center. - ContainedItemsList list; - list.swap(m_containList); - m_containListSize = 0; - - ContainedItemsList::iterator it = list.begin(); - - while ( it != list.end() ) + ContainedItemsList::iterator it = m_containList.begin(); + while ( it != m_containList.end() ) { - Object *rider = *it++; + Object *rider = *it; DEBUG_ASSERTCRASH( rider, ("Contain list must not contain null element")); if ( rider ) { + // TheSuperHackers @bugfix Caball009 11/03/2026 The contain list must be updated while iterating over it. + // Swapping the list with a temporary one for safety reasons causes the GLA Demolition Suicide upgrade to apply damage to civilian buildings + // for all garrisoned units instead of the last one that's killed by Neutron Shells. + m_containList.erase(it); + --m_containListSize; + onRemoving( rider ); rider->onRemovedFrom( getObject() ); rider->kill(); + + // After Object::kill, the iterator may or may not be invalidated and the list may or may not be empty. + // Set the iterator to the beginning of the list. + it = m_containList.begin(); + } + else + { + ++it; } } + DEBUG_ASSERTCRASH(m_containList.empty(), ("killAllContained should have emptied the contain list")); + + m_containList.clear(); + m_containListSize = 0; } //-------------------------------------------------------------------------------------------------------- From 4f79b64bfe656a9d911960caaed8420478a7d0bb Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 20 Mar 2026 17:25:34 +0100 Subject: [PATCH 2/2] Updated comments. --- .../Source/GameLogic/Object/Contain/OpenContain.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp index 6bd349fe464..175829a0d2c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp @@ -447,13 +447,8 @@ void OpenContain::removeAllContained( Bool exposeStealthUnits ) //------------------------------------------------------------------------------------------------- void OpenContain::killAllContained() { - // TheSuperHackers @bugfix xezon 23/05/2025 Empty m_containList straight away - // to prevent a potential child call to catastrophically modify the m_containList as well. - // This scenario can happen if the killed occupant(s) apply deadly damage on death - // to the host container, which then attempts to remove all remaining occupants - // on the death of the host container. This is reproducible by shooting with - // Neutron Shells on a GLA Technical containing GLA Toxin Terrorists - // with the Chem_SuicideWeapon upgrade, which is automatically granted by the GLA Toxin Command Center. + // TheSuperHackers @bugfix Caball009 11/03/2026 The contain list must be updated while iterating over it, + // because e.g. GarrisonContain::onRemoving relies on that behavior for the team ownership of civilian buildings. ContainedItemsList::iterator it = m_containList.begin(); while ( it != m_containList.end() ) @@ -463,9 +458,6 @@ void OpenContain::killAllContained() DEBUG_ASSERTCRASH( rider, ("Contain list must not contain null element")); if ( rider ) { - // TheSuperHackers @bugfix Caball009 11/03/2026 The contain list must be updated while iterating over it. - // Swapping the list with a temporary one for safety reasons causes the GLA Demolition Suicide upgrade to apply damage to civilian buildings - // for all garrisoned units instead of the last one that's killed by Neutron Shells. m_containList.erase(it); --m_containListSize;