diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp index 0da2deb1da..f9a001519c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp @@ -247,9 +247,17 @@ void OpenContain::addOrRemoveObjFromWorld(Object* obj, Bool add) // remove rider from partition manager ThePartitionManager->unRegisterObject( obj ); - // hide the drawable associated with rider - if( obj->getDrawable() ) - obj->getDrawable()->setDrawableHidden( true ); + if (Drawable* drawable = obj->getDrawable()) + { +#if RETAIL_COMPATIBLE_CRC + // TheSuperHackers @tweak This shouldn't happen but don't make the occupant invisible if it does. + if (!(getObject()->isEffectivelyDead() || getObject()->isDestroyed())) +#endif + { + // hide the drawable associated with rider + drawable->setDrawableHidden(true); + } + } // remove object from pathfind map TheAI->pathfinder()->removeObjectFromPathfindMap( obj ); @@ -292,11 +300,26 @@ void OpenContain::addToContain( Object *rider ) if( rider == nullptr ) return; +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Caball009 25/05/2026 Ensure the occupant is only added to a non-destroyed + // container to avoid an invalid state and use-after-free bugs when accessing the contained by pointer. + if (getObject()->isDestroyed()) + { + DEBUG_CRASH(("'%s' is about to be added to '%s', which is destroyed", + rider->getTemplate()->getName().str(), getObject()->getTemplate()->getName().str())); + return; + } +#endif + // TheSuperHackers @bugfix Stubbjax 06/02/2026 Ensure the rider is not destroyed to prevent a // likely crash if it enters the container on the same frame. If this occurs with an unpatched // client present in a match, the game has a small chance to mismatch. if (rider->isDestroyed()) + { + DEBUG_CRASH(("'%s', which is destroyed, is about to be added to '%s'", + rider->getTemplate()->getName().str(), getObject()->getTemplate()->getName().str())); return; + } Drawable *riderDraw = rider->getDrawable(); Bool wasSelected = FALSE; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp index fe364eee86..cdadb37d56 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp @@ -469,8 +469,19 @@ UpdateSleepTime TransportContain::update() { const TransportContainModuleData *moduleData = getTransportContainModuleData(); - if( m_payloadCreated == FALSE ) + if (m_payloadCreated == FALSE) + { +#if RETAIL_COMPATIBLE_CRC createPayload(); +#else + // TheSuperHackers @bugfix Caball009 25/05/2026 Don't create payload + // for destroyed object to avoid leaving the payload in an invalid state. + if (!getObject()->isDestroyed()) + { + createPayload(); + } +#endif + } if( moduleData && moduleData->m_healthRegen ) {