From d6d642d3dfd268a0d341ce07d27436d76a69fd2f Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 4 Jan 2026 18:44:42 +0100 Subject: [PATCH 01/11] bugfix(armorstore): Create overrides for ArmorTemplate data from custom maps to avoid CRC mismatch in the next multiplayer game session --- .../System/GameMemoryInitPools_GeneralsMD.inl | 2 + .../Code/GameEngine/Include/GameLogic/Armor.h | 20 ++++- .../Source/GameLogic/Object/Armor.cpp | 74 +++++++++++++++++-- 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index f9f5b5a0a18..3876e789798 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -20,6 +20,8 @@ // not const -- we might override from INI static PoolSizeRec PoolSizes[] = { + { "ArmorTemplatePool", 64, 8 }, + { "PartitionContactListNode", 2048, 512 }, { "BattleshipUpdate", 32, 32 }, { "FlyToDestAndDestroyUpdate", 32, 32 }, diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h index b98b2ba8d9e..8ceb50fd9cf 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h @@ -32,6 +32,7 @@ // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "Common/NameKeyGenerator.h" #include "Common/STLTypedefs.h" +#include "Common/Overridable.h" #include "GameLogic/Damage.h" // FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// @@ -43,8 +44,19 @@ class ArmorStore; to simulate different materials, and to help make game balance easier to adjust. */ //------------------------------------------------------------------------------------------------- -class ArmorTemplate +class ArmorTemplate : public Overridable { + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ArmorTemplate, "ArmorTemplatePool") + +#if defined(_MSC_VER) && _MSC_VER < 1300 + ArmorTemplate(const ArmorTemplate&) + { + DEBUG_CRASH(("This should never be called")); + } +#else + ArmorTemplate(const ArmorTemplate& that) = delete; +#endif + public: ArmorTemplate(); @@ -102,7 +114,7 @@ class ArmorStore : public SubsystemInterface ~ArmorStore(); void init() { } - void reset() { } + void reset(); void update() { } const ArmorTemplate* findArmorTemplate(NameKeyType namekey) const; @@ -121,7 +133,9 @@ class ArmorStore : public SubsystemInterface private: - typedef std::hash_map< NameKeyType, ArmorTemplate, rts::hash, rts::equal_to > ArmorTemplateMap; + ArmorTemplate* newOverride(ArmorTemplate *armorTemplate); + + typedef std::hash_map< NameKeyType, ArmorTemplate*, rts::hash, rts::equal_to > ArmorTemplateMap; ArmorTemplateMap m_armorTemplates; }; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp index 6d4578d1971..9423dc839cc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp @@ -54,6 +54,12 @@ ArmorTemplate::ArmorTemplate() clear(); } +//------------------------------------------------------------------------------------------------- +ArmorTemplate::~ArmorTemplate() +{ + +} + //------------------------------------------------------------------------------------------------- void ArmorTemplate::clear() { @@ -125,7 +131,7 @@ const ArmorTemplate* ArmorStore::findArmorTemplate(NameKeyType namekey) const } else { - return &(*it).second; + return (*it).second; } } @@ -149,11 +155,67 @@ const ArmorTemplate* ArmorStore::findArmorTemplate(const char* name) const { "Armor", ArmorTemplate::parseArmorCoefficients, NULL, 0 } }; - const char *c = ini->getNextToken(); - NameKeyType key = TheNameKeyGenerator->nameToKey(c); - ArmorTemplate& armorTmpl = TheArmorStore->m_armorTemplates[key]; - armorTmpl.clear(); - ini->initFromINI(&armorTmpl, myFieldParse); + const char *name = ini->getNextToken(); + NameKeyType key = TheNameKeyGenerator->nameToKey(name); + + // TheSuperHackers @bugfix Caball009 04/01/2025 Avoid mismatches by creating overrides instead of overwriting the default data. + // The code resembles the code of the ThingFactory. + ArmorTemplate *armorTmpl = TheArmorStore->m_armorTemplates[key]; + if (!armorTmpl) + { + DEBUG_ASSERTCRASH(ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES, ("Override without base template for armor templates is unexpected")); + + armorTmpl = newInstance(ArmorTemplate); + armorTmpl->clear(); + + TheArmorStore->m_armorTemplates[key] = armorTmpl; + } + else if (ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES) + { + DEBUG_CRASH(( "[LINE: %d in '%s'] Duplicate armor definition %s found!", ini->getLineNum(), ini->getFilename().str(), name )); + } + else + { + armorTmpl = TheArmorStore->newOverride(armorTmpl); + } + + ini->initFromINI(armorTmpl, myFieldParse); +} + +//------------------------------------------------------------------------------------------------- +ArmorTemplate* ArmorStore::newOverride( ArmorTemplate *armorTemplate ) +{ + // sanity + DEBUG_ASSERTCRASH( armorTemplate, ("newOverride(): NULL 'parent' armor template") ); + + // sanity just for debuging, the armor must be in the master list to do overrides + DEBUG_ASSERTCRASH( findArmorTemplate( armorTemplate->getName() ) != NULL, + ("newOverride(): Armor template '%s' not in master list", + armorTemplate->getName().str()) ); + + // find final override of the 'parent' template + ArmorTemplate *child = static_cast(armorTemplate->friend_getFinalOverride()); + + // allocate new template + ArmorTemplate *newTemplate = newInstance(ArmorTemplate); + + // copy data from final override to 'newTemplate' as a set of initial default values + *newTemplate = *child; + + newTemplate->markAsOverride(); + child->setNextOverride(newTemplate); + + // return the newly created override for us to set values with etc + return newTemplate; +} + +//------------------------------------------------------------------------------------------------- +void ArmorStore::reset() +{ + for (ArmorTemplateMap::iterator itr = m_armorTemplates.begin(); itr != m_armorTemplates.end(); ++itr) + { + itr->second->deleteOverrides(); + } } //------------------------------------------------------------------------------------------------- From c8874624a09de0cdd9c7dc036b73fa1839be2620 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 4 Jan 2026 21:19:09 +0100 Subject: [PATCH 02/11] Fixed indentation. --- GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h index 8ceb50fd9cf..2a50252da3b 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h @@ -49,7 +49,7 @@ class ArmorTemplate : public Overridable MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ArmorTemplate, "ArmorTemplatePool") #if defined(_MSC_VER) && _MSC_VER < 1300 - ArmorTemplate(const ArmorTemplate&) + ArmorTemplate(const ArmorTemplate&) { DEBUG_CRASH(("This should never be called")); } From bd559b8cc67ca16f7dcf10e675360f827702280b Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 4 Jan 2026 21:27:39 +0100 Subject: [PATCH 03/11] Fixed missing 'getName' function. --- GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h | 2 +- .../Code/GameEngine/Source/GameLogic/Object/Armor.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h index 2a50252da3b..028744aa0f7 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h @@ -133,7 +133,7 @@ class ArmorStore : public SubsystemInterface private: - ArmorTemplate* newOverride(ArmorTemplate *armorTemplate); + ArmorTemplate* newOverride(ArmorTemplate *armorTemplate, const char* name); typedef std::hash_map< NameKeyType, ArmorTemplate*, rts::hash, rts::equal_to > ArmorTemplateMap; ArmorTemplateMap m_armorTemplates; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp index 9423dc839cc..e8a9abfe32d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp @@ -176,22 +176,21 @@ const ArmorTemplate* ArmorStore::findArmorTemplate(const char* name) const } else { - armorTmpl = TheArmorStore->newOverride(armorTmpl); + armorTmpl = TheArmorStore->newOverride(armorTmpl, name); } ini->initFromINI(armorTmpl, myFieldParse); } //------------------------------------------------------------------------------------------------- -ArmorTemplate* ArmorStore::newOverride( ArmorTemplate *armorTemplate ) +ArmorTemplate* ArmorStore::newOverride(ArmorTemplate *armorTemplate, const char *name) { // sanity DEBUG_ASSERTCRASH( armorTemplate, ("newOverride(): NULL 'parent' armor template") ); // sanity just for debuging, the armor must be in the master list to do overrides - DEBUG_ASSERTCRASH( findArmorTemplate( armorTemplate->getName() ) != NULL, - ("newOverride(): Armor template '%s' not in master list", - armorTemplate->getName().str()) ); + DEBUG_ASSERTCRASH( findArmorTemplate( name ) != NULL, + ("newOverride(): Armor template '%s' not in master list", name) ); // find final override of the 'parent' template ArmorTemplate *child = static_cast(armorTemplate->friend_getFinalOverride()); From 413a6bc5797a65525e88423ae6df28bd4b00a226 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 4 Jan 2026 22:38:05 +0100 Subject: [PATCH 04/11] Added logic for new non-default armor templates. --- .../Source/GameLogic/Object/Armor.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp index e8a9abfe32d..5cb95f6eb4b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp @@ -156,18 +156,24 @@ const ArmorTemplate* ArmorStore::findArmorTemplate(const char* name) const }; const char *name = ini->getNextToken(); - NameKeyType key = TheNameKeyGenerator->nameToKey(name); + const NameKeyType key = TheNameKeyGenerator->nameToKey(name); // TheSuperHackers @bugfix Caball009 04/01/2025 Avoid mismatches by creating overrides instead of overwriting the default data. // The code resembles the code of the ThingFactory. ArmorTemplate *armorTmpl = TheArmorStore->m_armorTemplates[key]; if (!armorTmpl) { - DEBUG_ASSERTCRASH(ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES, ("Override without base template for armor templates is unexpected")); - + // no item is present, create a new one armorTmpl = newInstance(ArmorTemplate); armorTmpl->clear(); + if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) + { + // This ArmorTemplate is actually an override, so we will mark it as such so that it properly + // gets deleted on ::reset(). + armorTmpl->markAsOverride(); + } + TheArmorStore->m_armorTemplates[key] = armorTmpl; } else if (ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES) @@ -213,7 +219,13 @@ void ArmorStore::reset() { for (ArmorTemplateMap::iterator itr = m_armorTemplates.begin(); itr != m_armorTemplates.end(); ++itr) { - itr->second->deleteOverrides(); + const Overridable *stillValid = itr->second->deleteOverrides(); + + if (stillValid == NULL) + { + // Also needs to be removed from the Hash map. + m_armorTemplates.erase(itr->first); + } } } From 39455974eeef25ea615394be6eb8b13fa7804324 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Mon, 5 Jan 2026 11:42:05 +0100 Subject: [PATCH 05/11] Fixed iterator invalidation. --- .../Code/GameEngine/Source/GameLogic/Object/Armor.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp index 5cb95f6eb4b..b800078ff42 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp @@ -217,14 +217,20 @@ ArmorTemplate* ArmorStore::newOverride(ArmorTemplate *armorTemplate, const char //------------------------------------------------------------------------------------------------- void ArmorStore::reset() { - for (ArmorTemplateMap::iterator itr = m_armorTemplates.begin(); itr != m_armorTemplates.end(); ++itr) + ArmorTemplateMap::iterator itr = m_armorTemplates.begin(); + + while (itr != m_armorTemplates.end()) { const Overridable *stillValid = itr->second->deleteOverrides(); if (stillValid == NULL) { // Also needs to be removed from the Hash map. - m_armorTemplates.erase(itr->first); + itr = m_armorTemplates.erase(itr); + } + else + { + ++itr; } } } From 4c8a3c943c31e57b972c0bdb471b0b028ece29de Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Mon, 5 Jan 2026 11:42:35 +0100 Subject: [PATCH 06/11] Added deletion code in ArmorStore destructor. --- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp index b800078ff42..c48782b939c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp @@ -118,6 +118,11 @@ ArmorStore::ArmorStore() //------------------------------------------------------------------------------------------------- ArmorStore::~ArmorStore() { + for (ArmorTemplateMap::iterator itr = m_armorTemplates.begin(); itr != m_armorTemplates.end(); ++itr) + { + deleteInstance(itr->second); + } + m_armorTemplates.clear(); } From 72eacf0e0ece0df2e31f5154fd5cc7143c64945d Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Mon, 5 Jan 2026 21:03:55 +0100 Subject: [PATCH 07/11] Fixed STLport erase syntax. --- .../Code/GameEngine/Source/GameLogic/Object/Armor.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp index c48782b939c..e1b60ac5ea2 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp @@ -226,17 +226,18 @@ void ArmorStore::reset() while (itr != m_armorTemplates.end()) { + ArmorTemplateMap::iterator next = itr; + ++next; + const Overridable *stillValid = itr->second->deleteOverrides(); if (stillValid == NULL) { // Also needs to be removed from the Hash map. - itr = m_armorTemplates.erase(itr); - } - else - { - ++itr; + m_armorTemplates.erase(itr); } + + itr = next; } } From abab225a3ae0640b9cbd10443a05b9c789906508 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:14:39 +0100 Subject: [PATCH 08/11] Changed 'itr' to 'it'. --- .../GameEngine/Source/GameLogic/Object/Armor.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp index e1b60ac5ea2..f2987b9604f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Armor.cpp @@ -118,9 +118,9 @@ ArmorStore::ArmorStore() //------------------------------------------------------------------------------------------------- ArmorStore::~ArmorStore() { - for (ArmorTemplateMap::iterator itr = m_armorTemplates.begin(); itr != m_armorTemplates.end(); ++itr) + for (ArmorTemplateMap::iterator it = m_armorTemplates.begin(); it != m_armorTemplates.end(); ++it) { - deleteInstance(itr->second); + deleteInstance(it->second); } m_armorTemplates.clear(); @@ -222,22 +222,22 @@ ArmorTemplate* ArmorStore::newOverride(ArmorTemplate *armorTemplate, const char //------------------------------------------------------------------------------------------------- void ArmorStore::reset() { - ArmorTemplateMap::iterator itr = m_armorTemplates.begin(); + ArmorTemplateMap::iterator it = m_armorTemplates.begin(); - while (itr != m_armorTemplates.end()) + while (it != m_armorTemplates.end()) { - ArmorTemplateMap::iterator next = itr; + ArmorTemplateMap::iterator next = it; ++next; - const Overridable *stillValid = itr->second->deleteOverrides(); + const Overridable *stillValid = it->second->deleteOverrides(); if (stillValid == NULL) { // Also needs to be removed from the Hash map. - m_armorTemplates.erase(itr); + m_armorTemplates.erase(it); } - itr = next; + it = next; } } From eb5fbcd06380b20753b30d567712e5588033568b Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:17:00 +0100 Subject: [PATCH 09/11] Removed blank line. --- .../Source/Common/System/GameMemoryInitPools_GeneralsMD.inl | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index 3876e789798..3d27e808a26 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -21,7 +21,6 @@ static PoolSizeRec PoolSizes[] = { { "ArmorTemplatePool", 64, 8 }, - { "PartitionContactListNode", 2048, 512 }, { "BattleshipUpdate", 32, 32 }, { "FlyToDestAndDestroyUpdate", 32, 32 }, From c11ec256c6e12e4f480ca708231a0e9438eb09d2 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:17:15 +0100 Subject: [PATCH 10/11] Fixed indentation. --- GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h index 028744aa0f7..5758926e0ea 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h @@ -54,7 +54,7 @@ class ArmorTemplate : public Overridable DEBUG_CRASH(("This should never be called")); } #else - ArmorTemplate(const ArmorTemplate& that) = delete; + ArmorTemplate(const ArmorTemplate& that) = delete; #endif public: From 5d36015421fda0dcd086b2fac14e7cd259cb64cc Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:18:43 +0100 Subject: [PATCH 11/11] Made 'ArmorStore' destructor virtual. --- GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h index 5758926e0ea..2daf3747bef 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Armor.h @@ -111,7 +111,7 @@ class ArmorStore : public SubsystemInterface public: ArmorStore(); - ~ArmorStore(); + virtual ~ArmorStore(); void init() { } void reset();