Skip to content

refactor(messaging): replace GameMessage argument linked list with std::vector#2700

Open
RikuAnt wants to merge 7 commits into
TheSuperHackers:mainfrom
RikuAnt:feature/#2630_Refactor_message_arguments_to_vector
Open

refactor(messaging): replace GameMessage argument linked list with std::vector#2700
RikuAnt wants to merge 7 commits into
TheSuperHackers:mainfrom
RikuAnt:feature/#2630_Refactor_message_arguments_to_vector

Conversation

@RikuAnt
Copy link
Copy Markdown

@RikuAnt RikuAnt commented May 11, 2026

Fixes #2630

GameMessage and NetGameCommandMsg stored 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 a std::vector, removing the manual pointer threading and simplifying all related code. A DEBUG_ASSERTCRASH is 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:

  • Replicate in Generals.

RikuAnt added 3 commits May 12, 2026 00:04
…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
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 11, 2026

Greptile Summary

Replaces the manual linked-list argument storage (m_argList/m_argTail/m_argCount/m_next) in GameMessage and NetGameCommandMsg with std::vector<const GameMessageArgument*>, eliminating manual pointer threading throughout construction, destruction, and iteration. All callers (Recorder, NetGameCommandMsg copy constructor, constructGameMessage) are updated consistently and deep-copy semantics are preserved.

  • GameMessageArgument::m_next is removed; allocArg() and both destructors are simplified to a plain range-for with const_cast on delete — correct because all objects are originally allocated as mutable.
  • getArgumentCount() is now inlined in the header with a DEBUG_ASSERTCRASH to guard the existing 255-argument contract, and getArguments() exposes a const vector reference for bulk iteration.
  • getArgument(i) and getArgumentDataType(i) use an unsigned-cast bounds check that correctly handles out-of-range and negative indices without changing observable behaviour.

Confidence Score: 5/5

Safe to merge — the refactor is mechanical and consistent across all five files with no logic changes to argument handling.

Every code path that previously walked the linked list now iterates the vector with equivalent semantics. Memory ownership is correctly transferred (mutable allocation, const storage, const_cast on delete). The two vector-by-value copies are minor efficiency nits with no correctness impact.

No files require special attention.

Important Files Changed

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
Loading
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

Comment thread GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h Outdated
Comment thread Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp Outdated
Comment thread Core/GameEngine/Source/GameNetwork/NetCommandMsg.cpp Outdated
Comment thread Core/GameEngine/Include/GameNetwork/NetCommandMsg.h Outdated
Comment thread Core/GameEngine/Include/GameNetwork/NetCommandMsg.h Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/Common/MessageStream.cpp Outdated
// 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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be std::vector<const GameMessageArgument*>?

Copy link
Copy Markdown
Author

@RikuAnt RikuAnt May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This collides with the pooled object delete functionality deleteInstance in NetGameCommandMsg destructor. We can use const_cast if this is the preferred way? I made the change in favor of const_cast over the reinterpret_cast that the AI first recommended

Copy link
Copy Markdown

@Caball009 Caball009 May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

RikuAnt added 3 commits May 12, 2026 16:22
- Return GameMessageArguments as const
- Style fixes (remove blank space, indent and space out too packed methods)
- Remove vector includes; already included from precompiled header
Comment thread GeneralsMD/Code/GameEngine/Include/Common/MessageStream.h Outdated
@RikuAnt RikuAnt requested a review from Caball009 May 12, 2026 14:24
@L3-M L3-M added Minor Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour Refactor Edits the code with insignificant behavior changes, is never user facing Network Anything related to network, servers labels May 12, 2026
@L3-M L3-M added this to the Code foundation build up milestone May 12, 2026
for (Int i = 0; i < count; ++i) {
addArgument(msg->getArgumentDataType(i), *(msg->getArgument(i)));

const std::vector<const GameMessageArgument*> args = msg->getArguments();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also const T& to avoid a copy.

// 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;
Copy link
Copy Markdown

@Caball009 Caball009 May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could use an empty line between includes and forward declarations.

@Caball009
Copy link
Copy Markdown

Caball009 commented May 13, 2026

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Minor Severity: Minor < Major < Critical < Blocker Network Anything related to network, servers Refactor Edits the code with insignificant behavior changes, is never user facing ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inefficient lookups in GameMessage::getArgument, GameMessage::getArgumentDataType

3 participants