From 81b3f68f1eb1c637255d7161047df2988543841a Mon Sep 17 00:00:00 2001 From: Mauller <26652186+Mauller@users.noreply.github.com> Date: Sat, 19 Jul 2025 18:02:07 +0100 Subject: [PATCH] [GEN][ZH] Fix tunnel contain crashes due to uninitialized tunnel systems --- .../Include/GameLogic/Module/TunnelContain.h | 2 + .../Object/Contain/TunnelContain.cpp | 45 +++++++++++-- .../Include/GameLogic/Module/TunnelContain.h | 3 + .../Object/Contain/TunnelContain.cpp | 65 +++++++++++++++++-- 4 files changed, 106 insertions(+), 9 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h b/Generals/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h index c76b2961ba..75ed560d6b 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h @@ -98,6 +98,8 @@ class TunnelContain : public OpenContain, public CreateModuleInterface virtual void onRemoving( Object *obj ); ///< object no longer contains 'obj' virtual void onSelling();///< Container is being sold. Tunnel responds by kicking people out if this is the last tunnel. + virtual void orderAllPassengersToExit( CommandSourceType commandSource ); ///< All of the smarts of exiting are in the passenger's AIExit. removeAllFrommContain is a last ditch system call, this is the game Evacuate + virtual Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const; virtual void addToContainList( Object *obj ); ///< The part of AddToContain that inheritors can override (Can't do whole thing because of all the private stuff involved) virtual void removeFromContain( Object *obj, Bool exposeStealthUnits = FALSE ); ///< remove 'obj' from contain list diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp index 35c42f740f..9c2ddb2141 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp @@ -65,6 +65,10 @@ TunnelContain::~TunnelContain() void TunnelContain::addToContainList( Object *obj ) { Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + owningPlayer->getTunnelSystem()->addToContainList( obj ); } @@ -97,6 +101,9 @@ void TunnelContain::removeFromContain( Object *obj, Bool exposeStealthUnits ) if( owningPlayer == NULL ) return; //game tear down. We do the onRemove* stuff first because this is allowed to fail but that still needs to be done + if(!owningPlayer->getTunnelSystem()) + return; + owningPlayer->getTunnelSystem()->removeFromContain( obj, exposeStealthUnits ); } @@ -106,8 +113,12 @@ void TunnelContain::removeFromContain( Object *obj, Bool exposeStealthUnits ) //------------------------------------------------------------------------------------------------- void TunnelContain::removeAllContained( Bool exposeStealthUnits ) { - ContainedItemsList list; Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + + ContainedItemsList list; owningPlayer->getTunnelSystem()->swapContainedItemsList(list); ContainedItemsList::iterator it = list.begin(); @@ -127,6 +138,10 @@ void TunnelContain::removeAllContained( Bool exposeStealthUnits ) void TunnelContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse ) { Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + owningPlayer->getTunnelSystem()->iterateContained( func, userData, reverse ); } @@ -190,7 +205,11 @@ void TunnelContain::onSelling() Bool TunnelContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const { Player *owningPlayer = getObject()->getControllingPlayer(); - return owningPlayer->getTunnelSystem()->isValidContainerFor( obj, checkCapacity ); + if( owningPlayer && owningPlayer->getTunnelSystem() ) + { + return owningPlayer->getTunnelSystem()->isValidContainerFor( obj, checkCapacity ); + } + return false; } UnsignedInt TunnelContain::getContainCount() const @@ -206,13 +225,21 @@ UnsignedInt TunnelContain::getContainCount() const Int TunnelContain::getContainMax( void ) const { Player *owningPlayer = getObject()->getControllingPlayer(); - return owningPlayer->getTunnelSystem()->getContainMax(); + if( owningPlayer && owningPlayer->getTunnelSystem() ) + { + return owningPlayer->getTunnelSystem()->getContainMax(); + } + return 0; } const ContainedItemsList* TunnelContain::getContainedItemsList() const { Player *owningPlayer = getObject()->getControllingPlayer(); - return owningPlayer->getTunnelSystem()->getContainedItemsList(); + if( owningPlayer && owningPlayer->getTunnelSystem() ) + { + return owningPlayer->getTunnelSystem()->getContainedItemsList(); + } + return NULL; } @@ -339,6 +366,16 @@ void TunnelContain::onBuildComplete( void ) m_isCurrentlyRegistered = TRUE; } +//------------------------------------------------------------------------------------------------- +void TunnelContain::orderAllPassengersToExit( CommandSourceType commandSource ) +{ + Player *owningPlayer = getObject()->getControllingPlayer(); + if( !owningPlayer || !owningPlayer->getTunnelSystem() ) + return; + + OpenContain::orderAllPassengersToExit( commandSource ); +} + // ------------------------------------------------------------------------------------------------ /** Per frame update */ // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h index cdb15252b1..e539ae26fd 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h @@ -102,6 +102,9 @@ class TunnelContain : public OpenContain, public CreateModuleInterface virtual void onSelling();///< Container is being sold. Tunnel responds by kicking people out if this is the last tunnel. virtual void onCapture( Player *oldOwner, Player *newOwner ); // Need to change who we are registered with. + virtual void orderAllPassengersToExit( CommandSourceType commandSource, Bool instantly ); ///< All of the smarts of exiting are in the passenger's AIExit. removeAllFrommContain is a last ditch system call, this is the game Evacuate + virtual void orderAllPassengersToIdle( CommandSourceType commandSource ); ///< Just like it sounds + virtual Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const; virtual void addToContainList( Object *obj ); ///< The part of AddToContain that inheritors can override (Can't do whole thing because of all the private stuff involved) virtual void removeFromContain( Object *obj, Bool exposeStealthUnits = FALSE ); ///< remove 'obj' from contain list diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp index 3ac0a0471a..81602f201b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp @@ -65,6 +65,10 @@ TunnelContain::~TunnelContain() void TunnelContain::addToContainList( Object *obj ) { Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + owningPlayer->getTunnelSystem()->addToContainList( obj ); } @@ -97,6 +101,9 @@ void TunnelContain::removeFromContain( Object *obj, Bool exposeStealthUnits ) if( owningPlayer == NULL ) return; //game tear down. We do the onRemove* stuff first because this is allowed to fail but that still needs to be done + if(!owningPlayer->getTunnelSystem()) + return; + owningPlayer->getTunnelSystem()->removeFromContain( obj, exposeStealthUnits ); } @@ -109,6 +116,10 @@ void TunnelContain::removeFromContain( Object *obj, Bool exposeStealthUnits ) void TunnelContain::harmAndForceExitAllContained( DamageInfo *info ) { Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + const ContainedItemsList *fullList = owningPlayer->getTunnelSystem()->getContainedItemsList(); Object *obj; @@ -146,8 +157,12 @@ void TunnelContain::killAllContained( void ) // on the death of the host container. This is reproducible by shooting with // Neutron Shells on a GLA Tunnel containing GLA Terrorists. - ContainedItemsList list; Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + + ContainedItemsList list; owningPlayer->getTunnelSystem()->swapContainedItemsList(list); ContainedItemsList::iterator it = list.begin(); @@ -167,8 +182,12 @@ void TunnelContain::killAllContained( void ) //------------------------------------------------------------------------------------------------- void TunnelContain::removeAllContained( Bool exposeStealthUnits ) { - ContainedItemsList list; Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + + ContainedItemsList list; owningPlayer->getTunnelSystem()->swapContainedItemsList(list); ContainedItemsList::iterator it = list.begin(); @@ -188,6 +207,10 @@ void TunnelContain::removeAllContained( Bool exposeStealthUnits ) void TunnelContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse ) { Player *owningPlayer = getObject()->getControllingPlayer(); + + if(!owningPlayer->getTunnelSystem()) + return; + owningPlayer->getTunnelSystem()->iterateContained( func, userData, reverse ); } @@ -261,7 +284,11 @@ void TunnelContain::onSelling() Bool TunnelContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const { Player *owningPlayer = getObject()->getControllingPlayer(); - return owningPlayer->getTunnelSystem()->isValidContainerFor( obj, checkCapacity ); + if( owningPlayer && owningPlayer->getTunnelSystem() ) + { + return owningPlayer->getTunnelSystem()->isValidContainerFor( obj, checkCapacity ); + } + return false; } UnsignedInt TunnelContain::getContainCount() const @@ -277,13 +304,21 @@ UnsignedInt TunnelContain::getContainCount() const Int TunnelContain::getContainMax( void ) const { Player *owningPlayer = getObject()->getControllingPlayer(); - return owningPlayer->getTunnelSystem()->getContainMax(); + if( owningPlayer && owningPlayer->getTunnelSystem() ) + { + return owningPlayer->getTunnelSystem()->getContainMax(); + } + return 0; } const ContainedItemsList* TunnelContain::getContainedItemsList() const { Player *owningPlayer = getObject()->getControllingPlayer(); - return owningPlayer->getTunnelSystem()->getContainedItemsList(); + if( owningPlayer && owningPlayer->getTunnelSystem() ) + { + return owningPlayer->getTunnelSystem()->getContainedItemsList(); + } + return NULL; } @@ -459,6 +494,26 @@ void TunnelContain::onCapture( Player *oldOwner, Player *newOwner ) OpenContain::onCapture( oldOwner, newOwner ); } +//------------------------------------------------------------------------------------------------- +void TunnelContain::orderAllPassengersToExit( CommandSourceType commandSource, Bool instantly ) +{ + Player *owningPlayer = getObject()->getControllingPlayer(); + if( !owningPlayer || !owningPlayer->getTunnelSystem() ) + return; + + OpenContain::orderAllPassengersToExit( commandSource, instantly ); +} + +//------------------------------------------------------------------------------------------------- +void TunnelContain::orderAllPassengersToIdle( CommandSourceType commandSource ) +{ + Player *owningPlayer = getObject()->getControllingPlayer(); + if( !owningPlayer || !owningPlayer->getTunnelSystem() ) + return; + + OpenContain::orderAllPassengersToIdle( commandSource ); +} + // ------------------------------------------------------------------------------------------------ /** Per frame update */ // ------------------------------------------------------------------------------------------------