refactor(netpacket): Simplify NetPacket functions for packet buffer reads#2463
refactor(netpacket): Simplify NetPacket functions for packet buffer reads#2463xezon wants to merge 6 commits intoTheSuperHackers:mainfrom
Conversation
82f6771 to
ea2d59c
Compare
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Include/GameNetwork/NetCommandMsg.h | Introduces NetCommandDataChunk RAII wrapper for network buffer ownership, adds readMessageData virtual method to the hierarchy, and converts setData/setFileData to consume a NetCommandDataChunk by non-const reference (via release()). |
| Core/GameEngine/Include/GameNetwork/NetPacketStructs.h | Introduces NetPacketBuf bounds-safe view and readObject/readBytes/readStringWithoutNull/readStringWithNull helpers. The getBufferForRead(0) guard and strnlen-based string advance fix previously flagged issues. |
| Core/GameEngine/Source/GameNetwork/NetPacket.cpp | Major simplification: removes ~700 lines of per-type readXMessage functions. The new getCommandList loop uses constructNetCommandRef which delegates to SmallNetPacketCommandBase::readMessage and then readMessageData virtual dispatch. Control flow on error correctly uses continue (not break) and i is always advanced by the return value, preventing infinite loops. |
| Core/GameEngine/Source/GameNetwork/NetPacketStructs.cpp | Implements all readMessage static methods for each command data type. The new SmallNetPacketCommandBase::readMessage centralises field parsing, and constructNetCommandMsg replaces the scattered switch on command type. The Repeat field type in the switch correctly falls through to default since that case is handled by the caller. |
| Core/GameEngine/Source/GameNetwork/ConnectionManager.cpp | Adopts NetCommandDataChunk for ownership transfer in sendFile. The len is correctly captured before readEntireAndClose() is called, and buffer lifetimes are managed cleanly via RAII. The getFilename → getPortableFilename fix is a correctness improvement. |
Sequence Diagram
sequenceDiagram
participant GCL as NetPacket::getCommandList
participant CNR as constructNetCommandRef
participant SBR as SmallNetPacketCommandBase::readMessage
participant CCM as constructNetCommandMsg
participant RMD as msg->readMessageData (virtual)
participant CDD as CommandData::readMessage
GCL->>GCL: NetPacketBuf buf(m_packet, m_packetLen)
loop while i < buf.size()
GCL->>GCL: isRepeat = (m_packet[i] == Repeat)?
alt not Repeat
GCL->>CNR: constructNetCommandRef(ref, commandBase, buf.offset(i))
CNR->>SBR: readMessage(ref, base, buf)
loop while size < buf.size()
SBR->>SBR: switch(buf[size]) on field type
alt CommandType / Relay / Frame / PlayerId / CommandId
SBR->>SBR: readObject into base field, advance size
else Data
SBR->>CCM: constructNetCommandMsg(base)
CCM-->>SBR: NetCommandMsg* (typed)
SBR->>SBR: ref = NEW_NETCOMMANDREF(msg), setRelay
SBR-->>CNR: return size
else Repeat / default
SBR-->>CNR: return size+1 (advance past unexpected byte)
end
end
CNR->>RMD: ref->getCommand()->readMessageData(*ref, buf.offset(size))
RMD->>CDD: CommandData::readMessage(ref, buf)
CDD-->>RMD: bytes consumed
RMD-->>CNR: bytes consumed
CNR-->>GCL: total bytes consumed (i advanced)
alt ref != nullptr
GCL->>GCL: retval->addMessage(ref)
GCL->>GCL: lastCommand = NEW_NETCOMMANDREF(msg)
else ref == nullptr
GCL->>GCL: DEBUG_CRASH + continue
end
else Repeat
GCL->>GCL: i += NetPacketRepeatCommand::getSize()
GCL->>GCL: construct repeated msg from lastCommand
GCL->>GCL: retval->addMessage(msg)
GCL->>GCL: lastCommand = NEW_NETCOMMANDREF(msg)
end
end
GCL-->>GCL: return retval (NetCommandList)
Comments Outside Diff (1)
-
Core/GameEngine/Source/GameNetwork/NetPacketStructs.cpp, line 2569-2613 (link)Unhandled argument data type silently desynchronises remaining arguments
The
switch (lastType)has nodefaultbranch. If a packet ever carries anARGUMENTDATATYPE_UNKNOWN(value0) or any future type not listed here,sizeis not advanced.sizeBeforeequalssize, so the guardif (size > sizeBefore)skipsaddArgument, but--argsLeftForTypestill runs. Every subsequent argument of a known type is then read from the same buffer position as the unknown type, silently corrupting the entire argument list for that command.This behaviour was present in the original
readGameMessageArgumentFromPacket, but this PR consolidates all argument reading here and makes it worth surfacing explicitly. Consider adding adefaultbranch that advancessizeby the expected size of the unknown type, or at minimum aDEBUG_CRASHso malformed packets don't produce silent desync:default: DEBUG_CRASH(("NetPacketGameCommandData::readMessage: Unknown argument type %d", lastType)); break;
Prompt To Fix With AI
This is a comment left during a code review. Path: Core/GameEngine/Source/GameNetwork/NetPacketStructs.cpp Line: 2569-2613 Comment: **Unhandled argument data type silently desynchronises remaining arguments** The `switch (lastType)` has no `default` branch. If a packet ever carries an `ARGUMENTDATATYPE_UNKNOWN` (value `0`) or any future type not listed here, `size` is not advanced. `sizeBefore` equals `size`, so the guard `if (size > sizeBefore)` skips `addArgument`, but `--argsLeftForType` still runs. Every subsequent argument of a known type is then read from the same buffer position as the unknown type, silently corrupting the entire argument list for that command. This behaviour was present in the original `readGameMessageArgumentFromPacket`, but this PR consolidates all argument reading here and makes it worth surfacing explicitly. Consider adding a `default` branch that advances `size` by the expected size of the unknown type, or at minimum a `DEBUG_CRASH` so malformed packets don't produce silent desync: ```cpp default: DEBUG_CRASH(("NetPacketGameCommandData::readMessage: Unknown argument type %d", lastType)); break; ``` How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: Core/GameEngine/Source/GameNetwork/NetPacketStructs.cpp
Line: 2569-2613
Comment:
**Unhandled argument data type silently desynchronises remaining arguments**
The `switch (lastType)` has no `default` branch. If a packet ever carries an `ARGUMENTDATATYPE_UNKNOWN` (value `0`) or any future type not listed here, `size` is not advanced. `sizeBefore` equals `size`, so the guard `if (size > sizeBefore)` skips `addArgument`, but `--argsLeftForType` still runs. Every subsequent argument of a known type is then read from the same buffer position as the unknown type, silently corrupting the entire argument list for that command.
This behaviour was present in the original `readGameMessageArgumentFromPacket`, but this PR consolidates all argument reading here and makes it worth surfacing explicitly. Consider adding a `default` branch that advances `size` by the expected size of the unknown type, or at minimum a `DEBUG_CRASH` so malformed packets don't produce silent desync:
```cpp
default:
DEBUG_CRASH(("NetPacketGameCommandData::readMessage: Unknown argument type %d", lastType));
break;
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: Core/GameEngine/Source/GameNetwork/NetPacket.cpp
Line: 447-452
Comment:
**Misleading comment about incrementing `i`**
The in-code comment reads "we have to increment i so we don't fall into an infinite loop", but `i` is already advanced by `i += constructNetCommandRef(ref, commandBase, buf.offset(i))` four lines above — *before* the `ref == nullptr` check. The `continue` itself does no additional incrementing. The comment is a remnant of the old `++i; break` pattern and may confuse future readers into thinking the `continue` is doing the work.
```suggestion
// i was already advanced by constructNetCommandRef above.
// The continue prevents stale commandBase state from being
// used to process subsequent commands.
DEBUG_CRASH(("Unrecognized packet entry, ignoring."));
```
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "Continue on unrecogn..."
ea2d59c to
86477bd
Compare
86477bd to
9323680
Compare
| deleteInstance(lastCommand); | ||
| lastCommand = newInstance(NetCommandRef)(msg); | ||
|
|
||
| msg->detach(); // Need to detach from new NetCommandMsg created by the "readXMessage" above. | ||
|
|
||
| // since the message is part of the list now, we don't have to keep track of it. So we'll just set it to null. | ||
| msg = nullptr; | ||
| break; | ||
| lastCommand = NEW_NETCOMMANDREF(msg); | ||
| msg->detach(); | ||
| } | ||
| else | ||
| { | ||
| i += NetPacketRepeatCommand::getSize(); | ||
|
|
There was a problem hiding this comment.
break abandons remaining commands; old code used continue + ++i
The old code had ++i; break; (which in the enclosing switch fell through and the outer while continued with the next byte), making packet parsing resilient to a single unknown byte. The new code breaks out of the outer while loop entirely, so every command that follows a parse failure in the same packet is silently lost.
In a deterministic game engine this asymmetry (sender sends N commands; receiver processes only the first K because of one bad byte) is a desync risk. If the packet-stop semantics are intentional, please add a comment explaining the rationale; otherwise, consider continuing past the failed entry by not breaking the outer loop:
Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngine/Source/GameNetwork/NetPacket.cpp
Line: 453-460
Comment:
**`break` abandons remaining commands; old code used `continue` + `++i`**
The old code had `++i; break;` (which in the enclosing `switch` fell through and the outer `while` continued with the next byte), making packet parsing resilient to a single unknown byte. The new code `break`s out of the outer `while` loop entirely, so every command that follows a parse failure in the same packet is silently lost.
In a deterministic game engine this asymmetry (sender sends N commands; receiver processes only the first K because of one bad byte) is a desync risk. If the packet-stop semantics are intentional, please add a comment explaining the rationale; otherwise, consider continuing past the failed entry by not breaking the outer loop:
How can I resolve this? If you propose a fix, please make it concise.
This change simplifies all NetPacket functions for packet buffer reads.
TODO