From e4dc439724f6fd69cc3176a3da3f886a1772250b Mon Sep 17 00:00:00 2001 From: bobtista Date: Tue, 20 Jan 2026 18:01:12 -0600 Subject: [PATCH 1/4] bugfix(savegame): Restore object list order after load to match original save order Co-Authored-By: Claude Opus 4.5 --- .../Code/GameEngine/Include/GameLogic/Object.h | 3 +++ .../Source/GameLogic/System/GameLogic.cpp | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 5c2851ef7bd..b9acd2a7553 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -172,6 +172,9 @@ class Object : public Thing, public Snapshot Object* getNextObject() { return m_next; } const Object* getNextObject() const { return m_next; } + // TheSuperHackers @info bobtista 19/01/2026 Friend methods to allow GameLogic to fix object list order after load + void friend_setNextObject(Object* obj) { m_next = obj; } + void friend_setPrevObject(Object* obj) { m_prev = obj; } void updateObjValuesFromMapProperties(Dict* properties); ///< Brings in properties set in the editor. diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 569b01943c6..51421d0e3b4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -4924,6 +4924,23 @@ void GameLogic::xfer( Xfer *xfer ) } + // TheSuperHackers @fix bobtista 19/01/2026 + // Reverse the object list to restore the original order. + // Objects are prepended during registration, so loading them in save order + // (oldest first in original list) results in a reversed list. + Object *prev = nullptr; + Object *current = m_objList; + Object *next = nullptr; + while ( current != nullptr ) + { + next = current->getNextObject(); + current->friend_setNextObject( prev ); + current->friend_setPrevObject( next ); + prev = current; + current = next; + } + m_objList = prev; + } // campaign info From d34215786592966a1b284764616e7f100d789d16 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 27 Jan 2026 08:21:23 -0800 Subject: [PATCH 2/4] bugfix(savegame): Save objects in reverse order and fix load order for old saves --- .../GameEngine/Include/GameLogic/Object.h | 3 +- .../Source/GameLogic/System/GameLogic.cpp | 40 +++++++++++-------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index b9acd2a7553..78451396fcf 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -172,7 +172,8 @@ class Object : public Thing, public Snapshot Object* getNextObject() { return m_next; } const Object* getNextObject() const { return m_next; } - // TheSuperHackers @info bobtista 19/01/2026 Friend methods to allow GameLogic to fix object list order after load + Object* getPrevObject() { return m_prev; } + const Object* getPrevObject() const { return m_prev; } void friend_setNextObject(Object* obj) { m_next = obj; } void friend_setPrevObject(Object* obj) { m_prev = obj; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 51421d0e3b4..d4bfcea1c39 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -4801,13 +4801,14 @@ void GameLogic::prepareLogicForObjectLoad( void ) * 5: Added xfering the BuildAssistant's sell list. * 9: Added m_rankPointsToAddAtGameStart, or else on a load game, your RestartGame button will forget your exp * 10: xfer m_superweaponRestriction + * 11: Save objects in reverse order so they load in correct order */ // ------------------------------------------------------------------------------------------------ void GameLogic::xfer( Xfer *xfer ) { // version - const XferVersion currentVersion = 10; + const XferVersion currentVersion = 11; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -4840,8 +4841,13 @@ void GameLogic::xfer( Xfer *xfer ) ObjectTOCEntry *tocEntry; if( xfer->getXferMode() == XFER_SAVE ) { - + // TheSuperHackers @bugfix bobtista 27/01/2026 Save objects in reverse order (newest first) + // so they load in the correct order (oldest objects at head of list). + Object *lastObj = nullptr; for( obj = getFirstObject(); obj; obj = obj->getNextObject() ) + lastObj = obj; + + for( obj = lastObj; obj; obj = obj->getPrevObject() ) { // get the object TOC entry for this template @@ -4924,22 +4930,24 @@ void GameLogic::xfer( Xfer *xfer ) } - // TheSuperHackers @fix bobtista 19/01/2026 - // Reverse the object list to restore the original order. - // Objects are prepended during registration, so loading them in save order - // (oldest first in original list) results in a reversed list. - Object *prev = nullptr; - Object *current = m_objList; - Object *next = nullptr; - while ( current != nullptr ) + // TheSuperHackers @bugfix bobtista 27/01/2026 Reverse object list for old saves. + // Old saves stored objects oldest-first, which results in reversed order when loaded + // since objects are prepended during creation. Version 11+ saves in reverse order. + if ( version < 11 ) { - next = current->getNextObject(); - current->friend_setNextObject( prev ); - current->friend_setPrevObject( next ); - prev = current; - current = next; + Object *prev = nullptr; + Object *current = m_objList; + Object *next = nullptr; + while ( current != nullptr ) + { + next = current->getNextObject(); + current->friend_setNextObject( prev ); + current->friend_setPrevObject( next ); + prev = current; + current = next; + } + m_objList = prev; } - m_objList = prev; } From 61a136a8ed370c02a045c70d2e3bf2e6b9c91a93 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 27 Jan 2026 08:29:29 -0800 Subject: [PATCH 3/4] replicate to Generals --- .../GameEngine/Include/GameLogic/Object.h | 4 +++ .../Source/GameLogic/System/GameLogic.cpp | 28 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/Object.h b/Generals/Code/GameEngine/Include/GameLogic/Object.h index 28a5e2bfc55..39f60e21c87 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Object.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Object.h @@ -166,6 +166,10 @@ class Object : public Thing, public Snapshot Object* getNextObject() { return m_next; } const Object* getNextObject() const { return m_next; } + Object* getPrevObject() { return m_prev; } + const Object* getPrevObject() const { return m_prev; } + void friend_setNextObject(Object* obj) { m_next = obj; } + void friend_setPrevObject(Object* obj) { m_prev = obj; } void updateObjValuesFromMapProperties(Dict* properties); ///< Brings in properties set in the editor. diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 855d0942706..cdb77205226 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -4241,13 +4241,14 @@ void GameLogic::prepareLogicForObjectLoad( void ) * this version breaks compatibility with previous versions. (CBD) * 5: Added xfering the BuildAssistant's sell list. * 9: Added m_rankPointsToAddAtGameStart, or else on a load game, your RestartGame button will forget your exp + * 10: Save objects in reverse order so they load in correct order. Reverse object list for old saves. */ // ------------------------------------------------------------------------------------------------ void GameLogic::xfer( Xfer *xfer ) { // version - const XferVersion currentVersion = 9; + const XferVersion currentVersion = 10; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -4281,7 +4282,13 @@ void GameLogic::xfer( Xfer *xfer ) if( xfer->getXferMode() == XFER_SAVE ) { + // TheSuperHackers @bugfix bobtista 27/01/2026 Save objects in reverse order (newest first) + // so they load in the correct order (oldest objects at head of list). + Object *lastObj = nullptr; for( obj = getFirstObject(); obj; obj = obj->getNextObject() ) + lastObj = obj; + + for( obj = lastObj; obj; obj = obj->getPrevObject() ) { // get the object TOC entry for this template @@ -4364,6 +4371,25 @@ void GameLogic::xfer( Xfer *xfer ) } + // TheSuperHackers @bugfix bobtista 27/01/2026 Reverse object list for old saves. + // Old saves stored objects oldest-first, which results in reversed order when loaded + // since objects are prepended during creation. Version 10+ saves in reverse order. + if ( version < 10 ) + { + Object *prev = nullptr; + Object *current = m_objList; + Object *next = nullptr; + while ( current != nullptr ) + { + next = current->getNextObject(); + current->friend_setNextObject( prev ); + current->friend_setPrevObject( next ); + prev = current; + current = next; + } + m_objList = prev; + } + } // campaign info From 3d0f067aac3a19b246eb7a849975136bfe024ed1 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 28 Jan 2026 01:39:14 -0600 Subject: [PATCH 4/4] address review feedback --- .../Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | 6 +++--- .../Code/GameEngine/Source/GameLogic/System/GameLogic.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index cdb77205226..a64c1c2c55e 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -4241,7 +4241,7 @@ void GameLogic::prepareLogicForObjectLoad( void ) * this version breaks compatibility with previous versions. (CBD) * 5: Added xfering the BuildAssistant's sell list. * 9: Added m_rankPointsToAddAtGameStart, or else on a load game, your RestartGame button will forget your exp - * 10: Save objects in reverse order so they load in correct order. Reverse object list for old saves. + * 10: TheSuperHackers @tweak Save objects in reverse order so they load in correct order. Reverse object list for old saves. */ // ------------------------------------------------------------------------------------------------ void GameLogic::xfer( Xfer *xfer ) @@ -4371,10 +4371,10 @@ void GameLogic::xfer( Xfer *xfer ) } - // TheSuperHackers @bugfix bobtista 27/01/2026 Reverse object list for old saves. + // TheSuperHackers @fix bobtista 27/01/2026 Reverse object list for old saves. // Old saves stored objects oldest-first, which results in reversed order when loaded // since objects are prepended during creation. Version 10+ saves in reverse order. - if ( version < 10 ) + if ( version <= 9 ) { Object *prev = nullptr; Object *current = m_objList; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index d4bfcea1c39..3921f66c7db 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -4801,7 +4801,7 @@ void GameLogic::prepareLogicForObjectLoad( void ) * 5: Added xfering the BuildAssistant's sell list. * 9: Added m_rankPointsToAddAtGameStart, or else on a load game, your RestartGame button will forget your exp * 10: xfer m_superweaponRestriction - * 11: Save objects in reverse order so they load in correct order + * 11: TheSuperHackers @tweak Save objects in reverse order so they load in correct order */ // ------------------------------------------------------------------------------------------------ void GameLogic::xfer( Xfer *xfer ) @@ -4930,10 +4930,10 @@ void GameLogic::xfer( Xfer *xfer ) } - // TheSuperHackers @bugfix bobtista 27/01/2026 Reverse object list for old saves. + // TheSuperHackers @fix bobtista 27/01/2026 Reverse object list for old saves. // Old saves stored objects oldest-first, which results in reversed order when loaded // since objects are prepended during creation. Version 11+ saves in reverse order. - if ( version < 11 ) + if ( version <= 10 ) { Object *prev = nullptr; Object *current = m_objList;