From 0bf542d27de340a93f8b320d8946d97956b5ce72 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 00:04:25 +0300 Subject: [PATCH 01/12] perf: refactor message argument handling to use vector (#2630) Simpler and cleaner control. Improves loop performance significantly via cache hits and brings random index access down to O(1) --- .../GameEngine/Include/Common/MessageStream.h | 13 +++--- .../Source/Common/MessageStream.cpp | 43 +++---------------- .../GameEngine/Source/Common/Recorder.cpp | 12 ++---- 3 files changed, 15 insertions(+), 53 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h index 0a08d98486d..b42765edb97 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h @@ -32,6 +32,7 @@ #include "Common/SubsystemInterface.h" #include "Lib/BaseType.h" #include "Common/GameMemory.h" +#include enum { TRANSLATOR_ID_INVALID = -1 }; @@ -82,7 +83,6 @@ class GameMessageArgument : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(GameMessageArgument, "GameMessageArgument") public: - GameMessageArgument* m_next; ///< The next argument GameMessageArgumentType m_data; ///< The data storage of an argument GameMessageArgumentDataType m_type; ///< The type of the argument. }; @@ -639,7 +639,9 @@ class GameMessage : public MemoryPoolObject GameMessage *prev() { return m_prev; } ///< Return prev message in the stream Type getType() const { return m_type; } ///< Return the message type - UnsignedByte getArgumentCount() const { return m_argCount; } ///< Return the number of arguments for this msg + // TheSuperHackers @refactor RiQQ 11/05/2026 Return argument count from vector size + UnsignedByte getArgumentCount() const { return static_cast(m_argList.size()); } ///< Return the number of arguments for this msg + const std::vector& getArguments() const { return m_argList; } ///< Return the list of arguments const char *getCommandAsString() const; ///< returns a string representation of the command type. static const char *getCommandTypeAsString(GameMessage::Type t); @@ -662,7 +664,6 @@ class GameMessage : public MemoryPoolObject /** * Return the given argument union. - * @todo This should be a more list-like interface. Very inefficient. */ const GameMessageArgumentType *getArgument( Int argIndex ) const; GameMessageArgumentDataType getArgumentDataType( Int argIndex ) const; @@ -683,10 +684,8 @@ class GameMessage : public MemoryPoolObject Int m_playerIndex; ///< The Player who issued the command - /// @todo If a GameMessage needs more than 255 arguments, it needs to be split up into multiple GameMessage's. - UnsignedByte m_argCount; ///< The number of arguments of this message - - GameMessageArgument *m_argList, *m_argTail; ///< This message's arguments + // TheSuperHackers @refactor RiQQ 11/05/2026 Replaced linked list with std::vector for argument storage + std::vector m_argList; ///< This message's arguments /// allocate a new argument, add it to list, return pointer to its data GameMessageArgument *allocArg(); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp index 0addbe79dac..fde94dc2256 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp @@ -56,9 +56,6 @@ GameMessage::GameMessage( GameMessage::Type type ) { m_playerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex(); m_type = type; - m_argList = nullptr; - m_argTail = nullptr; - m_argCount = 0; m_list = nullptr; } @@ -69,11 +66,8 @@ GameMessage::GameMessage( GameMessage::Type type ) GameMessage::~GameMessage() { // free all arguments - GameMessageArgument *arg, *nextArg; - - for( arg = m_argList; arg; arg=nextArg ) + for( auto arg : m_argList ) { - nextArg = arg->m_next; deleteInstance(arg); } @@ -84,14 +78,11 @@ GameMessage::~GameMessage() /** * Return the given argument union. - * @todo This should be a more list-like interface. Very inefficient. */ const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const { - int i=0; - for( GameMessageArgument *a = m_argList; a; a=a->m_next, i++ ) - if (i == argIndex) - return &a->m_data; + if (argIndex >= 0 && static_cast(argIndex) < m_argList.size()) + return &m_argList[argIndex]->m_data; DEBUG_CRASH(("argument not found")); static const GameMessageArgumentType zero = { 0 }; @@ -103,17 +94,9 @@ const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const */ GameMessageArgumentDataType GameMessage::getArgumentDataType( Int argIndex ) const { - if (argIndex >= m_argCount) { - return ARGUMENTDATATYPE_UNKNOWN; - } - int i=0; - GameMessageArgument *a = m_argList; - for (; a && (i < argIndex); a=a->m_next, ++i ); + if (argIndex >= 0 && static_cast(argIndex) < m_argList.size()) + return m_argList[argIndex]->m_type; - if (a != nullptr) - { - return a->m_type; - } return ARGUMENTDATATYPE_UNKNOWN; } @@ -124,21 +107,7 @@ GameMessageArgument *GameMessage::allocArg() { // allocate a new argument GameMessageArgument *arg = newInstance(GameMessageArgument); - - // add to end of argument list - if (m_argTail) - m_argTail->m_next = arg; - else - { - m_argList = arg; - m_argTail = arg; - } - - arg->m_next = nullptr; - m_argTail = arg; - - m_argCount++; - + m_argList.push_back(arg); return arg; } diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 7e38c456b89..bc9c80cd750 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -743,15 +743,9 @@ void RecorderClass::writeToFile(GameMessage * msg) { argType = argType->getNext(); } -// UnsignedByte lasttype = (UnsignedByte)ARGUMENTDATATYPE_UNKNOWN; - Int numArgs = msg->getArgumentCount(); - for (Int i = 0; i < numArgs; ++i) { -// UnsignedByte type = (UnsignedByte)(msg->getArgumentDataType(i)); -// if (lasttype != type) { -// fwrite(&type, sizeof(type), 1, m_file); -// lasttype = type; -// } - writeArgument(msg->getArgumentDataType(i), *(msg->getArgument(i))); + for (auto arg : msg->getArguments()) + { + writeArgument(arg->m_type, arg->m_data); } deleteInstance(parser); From 572f477ae8158356227b693387bc2cf6cfe133e0 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 00:04:57 +0300 Subject: [PATCH 02/12] perf: adjust network messaging to use vectors (#2630) Only affects parts that relied on the linked list functionality of the GameMessageArguments --- .../Include/GameNetwork/NetCommandMsg.h | 8 +++-- .../Source/GameNetwork/NetCommandMsg.cpp | 32 +++---------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h index caf2302bc9b..ccfc3a3ec0f 100644 --- a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h +++ b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h @@ -32,6 +32,9 @@ #include "GameNetwork/NetworkDefs.h" #include "GameNetwork/NetPacketStructs.h" #include "Common/UnicodeString.h" +#include + +class GameMessageArgument; class NetCommandRef; @@ -117,10 +120,9 @@ class NetGameCommandMsg : public NetCommandMsgT m_argList; }; //----------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp index dd652a6cba3..176c85fb93f 100644 --- a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp +++ b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp @@ -88,12 +88,8 @@ Int NetCommandMsg::getSortNumber() const { * Constructor with no argument, sets everything to default values. */ NetGameCommandMsg::NetGameCommandMsg() { - m_argSize = 0; - m_numArgs = 0; m_type = (GameMessage::Type)0; m_commandType = NETCOMMANDTYPE_GAMECOMMAND; - m_argList = nullptr; - m_argTail = nullptr; } /** @@ -104,9 +100,8 @@ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { m_commandType = NETCOMMANDTYPE_GAMECOMMAND; m_type = msg->getType(); - Int count = msg->getArgumentCount(); - for (Int i = 0; i < count; ++i) { - addArgument(msg->getArgumentDataType(i), *(msg->getArgument(i))); + for (auto arg : msg->getArguments()) { + addArgument(arg->m_type, arg->m_data); } } @@ -114,11 +109,8 @@ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { * Destructor */ NetGameCommandMsg::~NetGameCommandMsg() { - GameMessageArgument *arg = m_argList; - while (arg != nullptr) { - m_argList = m_argList->m_next; + for (auto arg : m_argList) { deleteInstance(arg); - arg = m_argList; } } @@ -127,21 +119,10 @@ NetGameCommandMsg::~NetGameCommandMsg() { */ void NetGameCommandMsg::addArgument(const GameMessageArgumentDataType type, GameMessageArgumentType arg) { - if (m_argTail == nullptr) { - m_argList = newInstance(GameMessageArgument); - m_argTail = m_argList; - m_argList->m_data = arg; - m_argList->m_type = type; - m_argList->m_next = nullptr; - return; - } - GameMessageArgument *newArg = newInstance(GameMessageArgument); newArg->m_data = arg; newArg->m_type = type; - newArg->m_next = nullptr; - m_argTail->m_next = newArg; - m_argTail = newArg; + m_argList.push_back(newArg); } // here's where we figure out which slot corresponds to which player @@ -171,8 +152,7 @@ GameMessage *NetGameCommandMsg::constructGameMessage() const name.format("player%d", getPlayerID()); retval->friend_setPlayerIndex( ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(name))->getPlayerIndex()); - GameMessageArgument *arg = m_argList; - while (arg != nullptr) { + for (auto arg : m_argList) { switch (arg->m_type) { @@ -211,8 +191,6 @@ GameMessage *NetGameCommandMsg::constructGameMessage() const break; } - - arg = arg->m_next; } return retval; } From f627bbaf98f98922a489db8a7d92333ddcc3f6b1 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 00:27:51 +0300 Subject: [PATCH 03/12] fix: revert accidental todo comment delete and add debug assert (#2630) --- GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h index b42765edb97..e51a7e039cd 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h @@ -640,7 +640,8 @@ class GameMessage : public MemoryPoolObject Type getType() const { return m_type; } ///< Return the message type // TheSuperHackers @refactor RiQQ 11/05/2026 Return argument count from vector size - UnsignedByte getArgumentCount() const { return static_cast(m_argList.size()); } ///< Return the number of arguments for this msg + /// @todo If a GameMessage needs more than 255 arguments, it needs to be split up into multiple GameMessage's. + UnsignedByte getArgumentCount() const { DEBUG_ASSERTCRASH(m_argList.size() <= 255, ("GameMessage has more than 255 arguments")); return static_cast(m_argList.size()); } ///< Return the number of arguments for this msg const std::vector& getArguments() const { return m_argList; } ///< Return the list of arguments const char *getCommandAsString() const; ///< returns a string representation of the command type. From 84d7681d0633a091a36ac8bf5cec6c4d0150b093 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 16:11:19 +0300 Subject: [PATCH 04/12] refactor: refactor auto and range based loops into c++98 compatible versions (#2630) --- .../Source/GameNetwork/NetCommandMsg.cpp | 15 +++++++++------ .../GameEngine/Source/Common/MessageStream.cpp | 4 ++-- .../Code/GameEngine/Source/Common/Recorder.cpp | 5 +++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp index 176c85fb93f..24ce1c98da2 100644 --- a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp +++ b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp @@ -98,9 +98,11 @@ NetGameCommandMsg::NetGameCommandMsg() { */ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { m_commandType = NETCOMMANDTYPE_GAMECOMMAND; - m_type = msg->getType(); - for (auto arg : msg->getArguments()) { + + const std::vector args = msg->getArguments(); + for (size_t i = 0; i < args.size(); ++i) { + const GameMessageArgument* arg = args[i]; addArgument(arg->m_type, arg->m_data); } } @@ -109,8 +111,9 @@ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { * Destructor */ NetGameCommandMsg::~NetGameCommandMsg() { - for (auto arg : m_argList) { - deleteInstance(arg); + for (size_t i = 0; i < m_argList.size(); ++i) { + GameMessageArgument* argPtr = m_argList[i]; + deleteInstance(argPtr); } } @@ -152,8 +155,8 @@ GameMessage *NetGameCommandMsg::constructGameMessage() const name.format("player%d", getPlayerID()); retval->friend_setPlayerIndex( ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(name))->getPlayerIndex()); - for (auto arg : m_argList) { - + for (size_t i = 0; i < m_argList.size(); ++i) { + const GameMessageArgument* arg = m_argList[i]; switch (arg->m_type) { case ARGUMENTDATATYPE_INTEGER: diff --git a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp index fde94dc2256..d1e56e1c702 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp @@ -66,8 +66,8 @@ GameMessage::GameMessage( GameMessage::Type type ) GameMessage::~GameMessage() { // free all arguments - for( auto arg : m_argList ) - { + for( size_t i = 0; i < m_argList.size(); ++i ) { + GameMessageArgument* arg = m_argList[i]; deleteInstance(arg); } diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index bc9c80cd750..8cc765771aa 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -743,8 +743,9 @@ void RecorderClass::writeToFile(GameMessage * msg) { argType = argType->getNext(); } - for (auto arg : msg->getArguments()) - { + const std::vector args = msg->getArguments(); + for ( size_t i = 0; i < args.size(); ++i ) { + GameMessageArgument *arg = args[i]; writeArgument(arg->m_type, arg->m_data); } From 51bd44351cf10f90a362553fac1f8c3bf1cbcdb8 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 16:22:46 +0300 Subject: [PATCH 05/12] perf: reserve vector storage before iteratively populating and remove unnecessary test before uint conversion (#2630) --- Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp | 1 + GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp index 24ce1c98da2..b695d2e597c 100644 --- a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp +++ b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp @@ -101,6 +101,7 @@ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { m_type = msg->getType(); const std::vector args = msg->getArguments(); + m_argList.reserve(args.size()); for (size_t i = 0; i < args.size(); ++i) { const GameMessageArgument* arg = args[i]; addArgument(arg->m_type, arg->m_data); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp index d1e56e1c702..8f52f6a1a12 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp @@ -81,7 +81,7 @@ GameMessage::~GameMessage() */ const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const { - if (argIndex >= 0 && static_cast(argIndex) < m_argList.size()) + if (static_cast(argIndex) < m_argList.size()) return &m_argList[argIndex]->m_data; DEBUG_CRASH(("argument not found")); @@ -94,7 +94,7 @@ const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const */ GameMessageArgumentDataType GameMessage::getArgumentDataType( Int argIndex ) const { - if (argIndex >= 0 && static_cast(argIndex) < m_argList.size()) + if (static_cast(argIndex) < m_argList.size()) return m_argList[argIndex]->m_type; return ARGUMENTDATATYPE_UNKNOWN; From 8e5eda331d2f7d6d8a043a278540ceaceaeb4b41 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 16:59:47 +0300 Subject: [PATCH 06/12] refactor: Make minor code review changes(#2630) - Return GameMessageArguments as const - Style fixes (remove blank space, indent and space out too packed methods) - Remove vector includes; already included from precompiled header --- Core/GameEngine/Include/GameNetwork/NetCommandMsg.h | 3 --- .../GameEngine/Source/GameNetwork/NetCommandMsg.cpp | 2 +- .../Code/GameEngine/Include/Common/MessageStream.h | 13 ++++++++++--- .../Code/GameEngine/Source/Common/Recorder.cpp | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h index ccfc3a3ec0f..04df76c30f2 100644 --- a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h +++ b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h @@ -32,10 +32,7 @@ #include "GameNetwork/NetworkDefs.h" #include "GameNetwork/NetPacketStructs.h" #include "Common/UnicodeString.h" -#include - class GameMessageArgument; - class NetCommandRef; //----------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp index b695d2e597c..41e8675c4b4 100644 --- a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp +++ b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp @@ -100,7 +100,7 @@ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { m_commandType = NETCOMMANDTYPE_GAMECOMMAND; m_type = msg->getType(); - const std::vector args = msg->getArguments(); + const std::vector args = msg->getArguments(); m_argList.reserve(args.size()); for (size_t i = 0; i < args.size(); ++i) { const GameMessageArgument* arg = args[i]; diff --git a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h index e51a7e039cd..7147cc2eefb 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h @@ -32,7 +32,6 @@ #include "Common/SubsystemInterface.h" #include "Lib/BaseType.h" #include "Common/GameMemory.h" -#include enum { TRANSLATOR_ID_INVALID = -1 }; @@ -641,8 +640,16 @@ class GameMessage : public MemoryPoolObject Type getType() const { return m_type; } ///< Return the message type // TheSuperHackers @refactor RiQQ 11/05/2026 Return argument count from vector size /// @todo If a GameMessage needs more than 255 arguments, it needs to be split up into multiple GameMessage's. - UnsignedByte getArgumentCount() const { DEBUG_ASSERTCRASH(m_argList.size() <= 255, ("GameMessage has more than 255 arguments")); return static_cast(m_argList.size()); } ///< Return the number of arguments for this msg - const std::vector& getArguments() const { return m_argList; } ///< Return the list of arguments + UnsignedByte getArgumentCount() const { + DEBUG_ASSERTCRASH( + m_argList.size() <= 255, + ("GameMessage has more than 255 arguments") + ); + return static_cast(m_argList.size()); + } + const std::vector& getArguments() const { + return reinterpret_cast&>(m_argList); + } const char *getCommandAsString() const; ///< returns a string representation of the command type. static const char *getCommandTypeAsString(GameMessage::Type t); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 8cc765771aa..103ee31ad78 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -743,9 +743,9 @@ void RecorderClass::writeToFile(GameMessage * msg) { argType = argType->getNext(); } - const std::vector args = msg->getArguments(); + const std::vector args = msg->getArguments(); for ( size_t i = 0; i < args.size(); ++i ) { - GameMessageArgument *arg = args[i]; + const GameMessageArgument *arg = args[i]; writeArgument(arg->m_type, arg->m_data); } From 6ca883281a9a27c33287f1e32600613687a693c3 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 17:17:27 +0300 Subject: [PATCH 07/12] refactor: refactor GameMessageArguments into const to avoid reinterpret_cast (#2630) --- Core/GameEngine/Include/GameNetwork/NetCommandMsg.h | 2 +- Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp | 3 +-- GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h | 6 ++---- GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp | 3 +-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h index 04df76c30f2..7cc3156d1b4 100644 --- a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h +++ b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h @@ -119,7 +119,7 @@ class NetGameCommandMsg : public NetCommandMsgT m_argList; + std::vector m_argList; }; //----------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp index 41e8675c4b4..c168636e947 100644 --- a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp +++ b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp @@ -113,8 +113,7 @@ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { */ NetGameCommandMsg::~NetGameCommandMsg() { for (size_t i = 0; i < m_argList.size(); ++i) { - GameMessageArgument* argPtr = m_argList[i]; - deleteInstance(argPtr); + deleteInstance(const_cast(m_argList[i])); } } diff --git a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h index 7147cc2eefb..629848de8ba 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h @@ -647,9 +647,7 @@ class GameMessage : public MemoryPoolObject ); return static_cast(m_argList.size()); } - const std::vector& getArguments() const { - return reinterpret_cast&>(m_argList); - } + const std::vector& getArguments() const { return m_argList ; } const char *getCommandAsString() const; ///< returns a string representation of the command type. static const char *getCommandTypeAsString(GameMessage::Type t); @@ -693,7 +691,7 @@ class GameMessage : public MemoryPoolObject Int m_playerIndex; ///< The Player who issued the command // TheSuperHackers @refactor RiQQ 11/05/2026 Replaced linked list with std::vector for argument storage - std::vector m_argList; ///< This message's arguments + std::vector m_argList; ///< This message's arguments /// allocate a new argument, add it to list, return pointer to its data GameMessageArgument *allocArg(); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp index 8f52f6a1a12..66c4337aa9c 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp @@ -67,8 +67,7 @@ GameMessage::~GameMessage() { // free all arguments for( size_t i = 0; i < m_argList.size(); ++i ) { - GameMessageArgument* arg = m_argList[i]; - deleteInstance(arg); + deleteInstance(const_cast(m_argList[i])); } // detach message from list From be4126ee5e57674bc31c6921960cae1fbf827003 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Thu, 14 May 2026 15:07:20 +0300 Subject: [PATCH 08/12] fix: Get args by reference instead of copying (#2630) --- Core/GameEngine/Include/GameNetwork/NetCommandMsg.h | 1 + Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp | 3 ++- GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h index 7cc3156d1b4..611d9b5287a 100644 --- a/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h +++ b/Core/GameEngine/Include/GameNetwork/NetCommandMsg.h @@ -32,6 +32,7 @@ #include "GameNetwork/NetworkDefs.h" #include "GameNetwork/NetPacketStructs.h" #include "Common/UnicodeString.h" + class GameMessageArgument; class NetCommandRef; diff --git a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp index c168636e947..b7ab720ef46 100644 --- a/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp +++ b/Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp @@ -100,8 +100,9 @@ NetGameCommandMsg::NetGameCommandMsg(GameMessage *msg) { m_commandType = NETCOMMANDTYPE_GAMECOMMAND; m_type = msg->getType(); - const std::vector args = msg->getArguments(); + const std::vector& args = msg->getArguments(); m_argList.reserve(args.size()); + for (size_t i = 0; i < args.size(); ++i) { const GameMessageArgument* arg = args[i]; addArgument(arg->m_type, arg->m_data); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 103ee31ad78..d32b71fca8d 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -743,7 +743,7 @@ void RecorderClass::writeToFile(GameMessage * msg) { argType = argType->getNext(); } - const std::vector args = msg->getArguments(); + const std::vector& args = msg->getArguments(); for ( size_t i = 0; i < args.size(); ++i ) { const GameMessageArgument *arg = args[i]; writeArgument(arg->m_type, arg->m_data); From 763f61187149aaf716ab27cdddb15dd79132b545 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Tue, 12 May 2026 21:21:56 +0300 Subject: [PATCH 09/12] refactor(messaging): Mirror changes made to Zero Hour version (#2630) (cherry picked from commit 0c8438a88926ff2a2797854f9f17d94ce6daf177) --- .../GameEngine/Include/Common/MessageStream.h | 17 ++++--- .../Source/Common/MessageStream.cpp | 45 +++---------------- .../GameEngine/Source/Common/Recorder.cpp | 13 ++---- 3 files changed, 23 insertions(+), 52 deletions(-) diff --git a/Generals/Code/GameEngine/Include/Common/MessageStream.h b/Generals/Code/GameEngine/Include/Common/MessageStream.h index 2b04f40be2e..9edd57db125 100644 --- a/Generals/Code/GameEngine/Include/Common/MessageStream.h +++ b/Generals/Code/GameEngine/Include/Common/MessageStream.h @@ -605,7 +605,16 @@ class GameMessage : public MemoryPoolObject GameMessage *prev() { return m_prev; } ///< Return prev message in the stream Type getType() const { return m_type; } ///< Return the message type - UnsignedByte getArgumentCount() const { return m_argCount; } ///< Return the number of arguments for this msg + // TheSuperHackers @refactor RiQQ 12/05/2026 Return argument count from vector size + /// @todo If a GameMessage needs more than 255 arguments, it needs to be split up into multiple GameMessage's. + UnsignedByte getArgumentCount() const { + DEBUG_ASSERTCRASH( + m_argList.size() <= 255, + ("GameMessage has more than 255 arguments") + ); + return static_cast(m_argList.size()); + } + const std::vector& getArguments() const { return m_argList ; } const char *getCommandAsString() const; ///< returns a string representation of the command type. static const char *getCommandTypeAsString(GameMessage::Type t); @@ -649,10 +658,8 @@ class GameMessage : public MemoryPoolObject Int m_playerIndex; ///< The Player who issued the command - /// @todo If a GameMessage needs more than 255 arguments, it needs to be split up into multiple GameMessage's. - UnsignedByte m_argCount; ///< The number of arguments of this message - - GameMessageArgument *m_argList, *m_argTail; ///< This message's arguments + // TheSuperHackers @refactor RiQQ 12/05/2026 Replaced linked list with std::vector for argument storage + std::vector m_argList; ///< This message's arguments /// allocate a new argument, add it to list, return pointer to its data GameMessageArgument *allocArg(); diff --git a/Generals/Code/GameEngine/Source/Common/MessageStream.cpp b/Generals/Code/GameEngine/Source/Common/MessageStream.cpp index b970e457bf0..1e3b2f1f68d 100644 --- a/Generals/Code/GameEngine/Source/Common/MessageStream.cpp +++ b/Generals/Code/GameEngine/Source/Common/MessageStream.cpp @@ -56,9 +56,6 @@ GameMessage::GameMessage( GameMessage::Type type ) { m_playerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex(); m_type = type; - m_argList = nullptr; - m_argTail = nullptr; - m_argCount = 0; m_list = nullptr; } @@ -69,12 +66,8 @@ GameMessage::GameMessage( GameMessage::Type type ) GameMessage::~GameMessage() { // free all arguments - GameMessageArgument *arg, *nextArg; - - for( arg = m_argList; arg; arg=nextArg ) - { - nextArg = arg->m_next; - deleteInstance(arg); + for( size_t i = 0; i < m_argList.size(); ++i ) { + deleteInstance(const_cast(m_argList[i])); } // detach message from list @@ -88,10 +81,8 @@ GameMessage::~GameMessage() */ const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const { - int i=0; - for( GameMessageArgument *a = m_argList; a; a=a->m_next, i++ ) - if (i == argIndex) - return &a->m_data; + if (static_cast(argIndex) < m_argList.size()) + return &m_argList[argIndex]->m_data; DEBUG_CRASH(("argument not found")); static const GameMessageArgumentType zero = { 0 }; @@ -103,17 +94,9 @@ const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const */ GameMessageArgumentDataType GameMessage::getArgumentDataType( Int argIndex ) const { - if (argIndex >= m_argCount) { - return ARGUMENTDATATYPE_UNKNOWN; - } - int i=0; - GameMessageArgument *a = m_argList; - for (; a && (i < argIndex); a=a->m_next, ++i ); + if (static_cast(argIndex) < m_argList.size()) + return m_argList[argIndex]->m_type; - if (a != nullptr) - { - return a->m_type; - } return ARGUMENTDATATYPE_UNKNOWN; } @@ -124,21 +107,7 @@ GameMessageArgument *GameMessage::allocArg() { // allocate a new argument GameMessageArgument *arg = newInstance(GameMessageArgument); - - // add to end of argument list - if (m_argTail) - m_argTail->m_next = arg; - else - { - m_argList = arg; - m_argTail = arg; - } - - arg->m_next = nullptr; - m_argTail = arg; - - m_argCount++; - + m_argList.push_back(arg); return arg; } diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index 1e4fdd83238..359685bef48 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -741,15 +741,10 @@ void RecorderClass::writeToFile(GameMessage * msg) { argType = argType->getNext(); } -// UnsignedByte lasttype = (UnsignedByte)ARGUMENTDATATYPE_UNKNOWN; - Int numArgs = msg->getArgumentCount(); - for (Int i = 0; i < numArgs; ++i) { -// UnsignedByte type = (UnsignedByte)(msg->getArgumentDataType(i)); -// if (lasttype != type) { -// fwrite(&type, sizeof(type), 1, m_file); -// lasttype = type; -// } - writeArgument(msg->getArgumentDataType(i), *(msg->getArgument(i))); + const std::vector& args = msg->getArguments(); + for ( size_t i = 0; i < args.size(); ++i ) { + const GameMessageArgument *arg = args[i]; + writeArgument(arg->m_type, arg->m_data); } deleteInstance(parser); From 4e7c038611167fa596f1c3d86549ce5f1cd68feb Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Thu, 14 May 2026 20:42:16 +0300 Subject: [PATCH 10/12] refactor': remove unused m_next from GameMessageArgument (#2630) --- Generals/Code/GameEngine/Include/Common/MessageStream.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Generals/Code/GameEngine/Include/Common/MessageStream.h b/Generals/Code/GameEngine/Include/Common/MessageStream.h index 9edd57db125..6a2a0f1ddd1 100644 --- a/Generals/Code/GameEngine/Include/Common/MessageStream.h +++ b/Generals/Code/GameEngine/Include/Common/MessageStream.h @@ -82,7 +82,6 @@ class GameMessageArgument : public MemoryPoolObject { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(GameMessageArgument, "GameMessageArgument") public: - GameMessageArgument* m_next; ///< The next argument GameMessageArgumentType m_data; ///< The data storage of an argument GameMessageArgumentDataType m_type; ///< The type of the argument. }; From b06b8249251c031a852008d6617ca91dc294a90c Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Thu, 14 May 2026 23:59:07 +0300 Subject: [PATCH 11/12] fix: remove inconsistent todo leftover comment (#2630) --- Generals/Code/GameEngine/Include/Common/MessageStream.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Generals/Code/GameEngine/Include/Common/MessageStream.h b/Generals/Code/GameEngine/Include/Common/MessageStream.h index 6a2a0f1ddd1..e32173a4a33 100644 --- a/Generals/Code/GameEngine/Include/Common/MessageStream.h +++ b/Generals/Code/GameEngine/Include/Common/MessageStream.h @@ -636,7 +636,6 @@ class GameMessage : public MemoryPoolObject /** * Return the given argument union. - * @todo This should be a more list-like interface. Very inefficient. */ const GameMessageArgumentType *getArgument( Int argIndex ) const; GameMessageArgumentDataType getArgumentDataType( Int argIndex ) const; From 2d3dc31d5615fbf7b5234ad3aa93960a7da3a399 Mon Sep 17 00:00:00 2001 From: RiQQ <60820330+RikuAnt@users.noreply.github.com> Date: Fri, 15 May 2026 00:24:29 +0300 Subject: [PATCH 12/12] fix: remove inconsistent todo leftover comment (#2630) --- Generals/Code/GameEngine/Source/Common/MessageStream.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Generals/Code/GameEngine/Source/Common/MessageStream.cpp b/Generals/Code/GameEngine/Source/Common/MessageStream.cpp index 1e3b2f1f68d..a1c7b80af6b 100644 --- a/Generals/Code/GameEngine/Source/Common/MessageStream.cpp +++ b/Generals/Code/GameEngine/Source/Common/MessageStream.cpp @@ -77,7 +77,6 @@ GameMessage::~GameMessage() /** * Return the given argument union. - * @todo This should be a more list-like interface. Very inefficient. */ const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const {