refactor(messaging): replace GameMessage argument linked list with std::vector#2700
Conversation
…rs#2630) Simpler and cleaner control. Improves loop performance significantly via cache hits and brings random index access down to O(1)
Only affects parts that relied on the linked list functionality of the GameMessageArguments
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h | Replaces m_argList/m_argTail/m_argCount linked-list fields with std::vector<const GameMessageArgument*>; adds getArguments() accessor and moves getArgumentCount() inline with a DEBUG_ASSERTCRASH guard for the 255-arg limit. Clean — no UB, const-correctness is consistent. |
| GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp | allocArg(), destructor, getArgument(), and getArgumentDataType() all updated to use the new vector; negative-index safety preserved via unsigned-cast trick in bounds checks; const_cast in destructor is correct since allocArg always allocates mutable objects. |
| Core/GameEngine/Include/GameNetwork/NetCommandMsg.h | m_numArgs/m_argSize/m_argList/m_argTail replaced with a single std::vector<const GameMessageArgument*> m_argList; forward declaration of GameMessageArgument added. Change is self-contained. |
| Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp | Constructor, copy-from-GameMessage constructor, destructor, addArgument, and constructGameMessage all updated; deep-copy semantics in copy constructor are correct. One unnecessary vector copy (const by value instead of const ref) noted. |
| GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp | Loop updated to iterate via getArguments(); same unnecessary vector copy as NetCommandMsg.cpp constructor — should bind by const reference. |
Sequence Diagram
sequenceDiagram
participant GMsg as GameMessage
participant ArgVec as m_argList (vector)
participant Net as NetGameCommandMsg
participant Rec as RecorderClass
GMsg->>ArgVec: allocArg() push_back(newArg)
Net->>GMsg: getArguments() returns const ref
Net->>Net: deep copy via addArgument()
Net->>ArgVec: push_back(newArg)
Rec->>GMsg: getArguments() returns const ref
Rec->>Rec: writeArgument per element
GMsg->>ArgVec: destructor deleteInstance each element
Net->>ArgVec: destructor deleteInstance each element
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp:746
`getArguments()` returns a `const` reference, but the variable is declared without `&`, so the vector is copied on every call. This triggers a heap allocation proportional to the argument count. Using a `const` reference avoids the copy entirely.
```suggestion
const std::vector<const GameMessageArgument*>& args = msg->getArguments();
```
### Issue 2 of 2
Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp:103
`getArguments()` returns a `const` reference, but `args` is declared by value, causing an unnecessary vector copy. Binding to a `const` reference avoids the extra heap allocation while keeping the same semantics since no modification is made to the vector.
```suggestion
const std::vector<const GameMessageArgument*>& args = msg->getArguments();
```
Reviews (3): Last reviewed commit: "refactor: refactor GameMessageArguments ..." | Re-trigger Greptile
| // TheSuperHackers @refactor RiQQ 11/05/2026 Replaced linked list with std::vector for argument storage | ||
| GameMessage::Type m_type; | ||
| GameMessageArgument *m_argList, *m_argTail; | ||
| std::vector<GameMessageArgument*> m_argList; |
There was a problem hiding this comment.
Can this be std::vector<const GameMessageArgument*>?
There was a problem hiding this comment.
This collides with the pooled object delete functionality deleteInstance in NetGameCommandMsg destructor. We can use I made the change in favor of const_cast over the reinterpret_cast that the AI first recommendedconst_cast if this is the preferred way?
There was a problem hiding this comment.
I see. The const_cast is not ideal, but that's price we've to pay, I suppose. I think the current way with <const T*> is preferable, but I'll let this conversation stay unresolved for now.
(const_cast is right here; I'm not sure whether reinterpret_cast would even compile here).
… unnecessary test before uint conversion (TheSuperHackers#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
| for (Int i = 0; i < count; ++i) { | ||
| addArgument(msg->getArgumentDataType(i), *(msg->getArgument(i))); | ||
|
|
||
| const std::vector<const GameMessageArgument*> args = msg->getArguments(); |
There was a problem hiding this comment.
const std::vector<const GameMessageArgument*>& args, we don't need to make a copy.
|
|
||
| const std::vector<const GameMessageArgument*> args = msg->getArguments(); | ||
| m_argList.reserve(args.size()); | ||
| for (size_t i = 0; i < args.size(); ++i) { |
There was a problem hiding this comment.
nit: Could use an empty line between reserve and the loop.
| // lasttype = type; | ||
| // } | ||
| writeArgument(msg->getArgumentDataType(i), *(msg->getArgument(i))); | ||
| const std::vector<const GameMessageArgument*> args = msg->getArguments(); |
| // TheSuperHackers @refactor RiQQ 11/05/2026 Replaced linked list with std::vector for argument storage | ||
| GameMessage::Type m_type; | ||
| GameMessageArgument *m_argList, *m_argTail; | ||
| std::vector<GameMessageArgument*> m_argList; |
There was a problem hiding this comment.
I see. The const_cast is not ideal, but that's price we've to pay, I suppose. I think the current way with <const T*> is preferable, but I'll let this conversation stay unresolved for now.
(const_cast is right here; I'm not sure whether reinterpret_cast would even compile here).
| #include "GameNetwork/NetPacketStructs.h" | ||
| #include "Common/UnicodeString.h" | ||
|
|
||
| class GameMessageArgument; |
There was a problem hiding this comment.
nit: Could use an empty line between includes and forward declarations.
|
Good effort. Generals fails to compile for now, but it's probably most convenient to wait for xezon's feedback before you replicate the changes from ZH to Gen. |
Fixes #2630
GameMessageandNetGameCommandMsgstored their arguments in a linked list (m_argList/m_argTail/m_argCount/m_next). Since arguments are only ever appended and iterated in order, this is replaced with astd::vector, removing the manual pointer threading and simplifying all related code. ADEBUG_ASSERTCRASHis added to guard the existing 255-argument limit.AI usage was minimal. All code has been reviewed and tested to best of my capability.
TODO: