From b6113c934a48edb6ea71918daa1902d53db44499 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:22:27 +0200 Subject: [PATCH 1/4] Refactored portable object. --- .../Source/GameLogic/Object/Contain/HelixContain.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp index aff9d91941c..a50a276fe0e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp @@ -250,10 +250,10 @@ void HelixContain::addToContainList( Object *obj ) if ( portable ) TheGameLogic->destroyObject( portable ); - m_portableStructureID = obj->getID(); - obj->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container - + portable = obj; + m_portableStructureID = portable->getID(); + portable->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container } else TransportContain::addToContainList( obj ); @@ -268,10 +268,10 @@ void HelixContain::addToContain( Object *obj ) if ( portable ) TheGameLogic->destroyObject( portable ); - m_portableStructureID = obj->getID(); - obj->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container - + portable = obj; + m_portableStructureID = portable->getID(); + portable->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container } else TransportContain::addToContain( obj ); From ae0d1be09349e1ecb0abba1890df9435279342a9 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 12 Jul 2026 23:05:20 +0200 Subject: [PATCH 2/4] Added code to set containedByID member variable. --- .../GameEngine/Include/GameLogic/Object.h | 4 +++ .../GameLogic/Object/Contain/HelixContain.cpp | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 29d391b81a3..49225bfd6e9 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -453,6 +453,10 @@ class Object : public Thing, public Snapshot const Object* getEnclosingContainedBy() const; ///< Find the first enclosing container in the containment chain. const Object* getOuterObject() const; ///< Get the top-level object +#if RETAIL_COMPATIBLE_CRC + void friend_setContainedByID(ObjectID id) { m_containedByID = id; } +#endif + // Special Powers ------------------------------------------------------------------------------- SpecialPowerModuleInterface *getSpecialPowerModule( const SpecialPowerTemplate *specialPowerTemplate ) const; void doSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, UnsignedInt commandOptions, Bool forced = false ); ///< execute power diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp index a50a276fe0e..6868066e00a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp @@ -254,6 +254,24 @@ void HelixContain::addToContainList( Object *obj ) m_portableStructureID = portable->getID(); portable->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container + +#if RETAIL_COMPATIBLE_CRC + Object* containedBy = getObject(); + + // TheSuperHackers @info Set INVALID_ID if the container object was destroyed + // to indicate that the pointer will become a dangling pointer in the next frame. + if (containedBy && !containedBy->isDestroyed()) + { + portable->friend_setContainedByID(containedBy->getID()); + } + else + { + portable->friend_setContainedByID(INVALID_ID); + } +#else + DEBUG_ASSERTCRASH(getObject() == nullptr || !getObject()->isDestroyed(), + ("HelixContain::addToContainList - Adding to a destroyed container")); +#endif } else TransportContain::addToContainList( obj ); @@ -272,6 +290,24 @@ void HelixContain::addToContain( Object *obj ) m_portableStructureID = portable->getID(); portable->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container + +#if RETAIL_COMPATIBLE_CRC + Object* containedBy = getObject(); + + // TheSuperHackers @info Set INVALID_ID if the container object was destroyed + // to indicate that the pointer will become a dangling pointer in the next frame. + if (containedBy && !containedBy->isDestroyed()) + { + portable->friend_setContainedByID(containedBy->getID()); + } + else + { + portable->friend_setContainedByID(INVALID_ID); + } +#else + DEBUG_ASSERTCRASH(getObject() == nullptr || !getObject()->isDestroyed(), + ("HelixContain::addToContain - Adding to a destroyed container")); +#endif } else TransportContain::addToContain( obj ); From e82aacefa0dc3681f291d82bbd68812d3cb03718 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 12 Jul 2026 23:19:37 +0200 Subject: [PATCH 3/4] Reset containedBy pointer and id on removal. --- GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h | 2 +- .../Source/GameLogic/Object/Contain/HelixContain.cpp | 8 +++++++- .../Code/GameEngine/Source/GameLogic/Object/Object.cpp | 9 +++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 49225bfd6e9..59045614419 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -449,7 +449,7 @@ class Object : public Thing, public Snapshot void onContainedBy( Object *containedBy ); void onRemovedFrom( Object *removedFrom ); Int getTransportSlotCount() const; - void friend_setContainedBy( Object *containedBy ) { m_containedBy = containedBy; } + void friend_setContainedBy( Object *containedBy ); const Object* getEnclosingContainedBy() const; ///< Find the first enclosing container in the containment chain. const Object* getOuterObject() const; ///< Get the top-level object diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp index 6868066e00a..c900e02d348 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp @@ -320,10 +320,16 @@ void HelixContain::removeFromContain( Object *obj, Bool exposeStealthUnits ) { Object *portable = getPortableStructure(); if ( portable ) + { +#if RETAIL_COMPATIBLE_CRC + portable->friend_setContainedByID(INVALID_ID); +#else + portable->friend_setContainedBy(nullptr); +#endif m_portableStructureID = INVALID_ID; //portable->kill(); - + } } else { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 0bd898aae75..a476abc66ac 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -749,6 +749,15 @@ Int Object::getTransportSlotCount() const return count; } +void Object::friend_setContainedBy(Object* containedBy) +{ + m_containedBy = containedBy; + +#if !RETAIL_COMPATIBLE_CRC + m_containedByFrame = containedBy ? TheGameLogic->getFrame() : 0; +#endif +} + const Object* Object::getEnclosingContainedBy() const { for (const Object* child = this, *container = getContainedBy(); container; child = container, container = container->getContainedBy()) From 5da1074284c691d2a23900228618afcdeaf5548a Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 14 Jul 2026 00:03:03 +0200 Subject: [PATCH 4/4] Tweaked older TSH comment. --- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index a476abc66ac..8ddeb128638 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -4299,8 +4299,9 @@ void Object::xfer( Xfer *xfer ) // No, the contain module is just going to friend_ reach in and set this for us. // Containers more complicated than Open (like Tunnel) can't do that. Our variable, // our responsibility. -#if !RETAIL_COMPATIBLE_CRC +#if RETAIL_COMPATIBLE_CRC // TheSuperHackers @tweak Contained by ID is already set with retail compatibility; don't overwrite it. +#else if( xfer->getXferMode() == XFER_SAVE ) { if( m_containedBy != nullptr )