tweak(network): Increase message buffer and max packet sizes to reduce connection issues#2277
Conversation
Greptile Overview
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Include/Common/GameDefines.h | Added RETAIL_COMPATIBLE_NETWORKING feature flag to control packet size optimizations |
| Core/GameEngine/Include/GameNetwork/NetworkDefs.h | Increased MAX_MESSAGES from 128 to 256 and introduced conditional packet sizing; contains prologue violations and potential description/code mismatch |
| Core/GameEngine/Source/GameNetwork/Transport.cpp | Updated send/receive buffer operations to use MAX_NETWORK_MESSAGE_LEN; added clarifying comments about UDP payload sizing |
Sequence Diagram
sequenceDiagram
participant Game as Game Logic
participant Net as Network Layer
participant Transport as Transport Layer
participant UDP as UDP Socket
participant Remote as Remote Peer
Note over Game,Remote: Sending Messages (doSend)
Game->>Net: Queue game commands
Net->>Transport: queueSend(data, MAX_PACKET_SIZE)
Transport->>Transport: Store in m_outBuffer[MAX_MESSAGES]
Transport->>Transport: Add TransportMessageHeader (6 bytes)
Transport->>UDP: Write(bytesToSend = length + header)
UDP->>Remote: Send UDP packet (≤MAX_NETWORK_MESSAGE_LEN)
Note over Game,Remote: Receiving Messages (doRecv)
Remote->>UDP: Incoming UDP packet
UDP->>Transport: Read(buf, MAX_NETWORK_MESSAGE_LEN)
Transport->>Transport: Decrypt and validate (isGeneralsPacket)
Transport->>Transport: Store in m_inBuffer[MAX_MESSAGES]
Transport->>Net: Process received message
Net->>Game: Deliver game commands
Note over Transport: Buffer Sizes
Note over Transport: Retail: MAX_MESSAGES=256, MAX_PACKET_SIZE=476
Note over Transport: Non-retail: MAX_MESSAGES=256, MAX_PACKET_SIZE=1094
Last reviewed commit: 265fab3
188298b to
535b4db
Compare
| static const Int MAX_MESSAGE_LEN = MAX_UDP_PAYLOAD; | ||
| #endif | ||
|
|
||
| static const Int MAX_MESSAGES = 256; |
There was a problem hiding this comment.
Increases buffer footprint
Bumping MAX_MESSAGES from 128 to 256 doubles the size of Transport::m_inBuffer/m_outBuffer (and m_delayedInBuffer in debug) because these arrays are sized by MAX_MESSAGES (see Core/GameEngine/Include/GameNetwork/Transport.h:73-78). This is a behavioral change that can increase memory pressure (and potentially stack/heap usage depending on how Transport is instantiated) even when RETAIL_COMPATIBLE_NETWORKING is kept enabled; please confirm this is intended and safe for all builds, or gate it behind the same retail-compat switch.
Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngine/Include/GameNetwork/NetworkDefs.h
Line: 71:71
Comment:
**Increases buffer footprint**
Bumping `MAX_MESSAGES` from 128 to 256 doubles the size of `Transport::m_inBuffer`/`m_outBuffer` (and `m_delayedInBuffer` in debug) because these arrays are sized by `MAX_MESSAGES` (see `Core/GameEngine/Include/GameNetwork/Transport.h:73-78`). This is a behavioral change that can increase memory pressure (and potentially stack/heap usage depending on how `Transport` is instantiated) even when `RETAIL_COMPATIBLE_NETWORKING` is kept enabled; please confirm this is intended and safe for all builds, or gate it behind the same retail-compat switch.
How can I resolve this? If you propose a fix, please make it concise.
xezon
left a comment
There was a problem hiding this comment.
The presentation can be polished more.
535b4db to
66fef26
Compare
|
Tweaked and addressed comments |
Additional Comments (1)
Prompt To Fix With AIThis is a comment left during a code review.
Path: Core/GameEngine/Source/GameNetwork/NetPacket.cpp
Line: 960:964
Comment:
**Incorrect memcpy length**
`NetPacket::NetPacket(TransportMessage *msg)` copies `MAX_PACKET_SIZE` bytes (`memcpy(m_packet, msg->data, MAX_PACKET_SIZE)`) even though `msg->length` may be smaller. With this PR increasing `MAX_PACKET_SIZE` to 1200 in non-retail builds, this will read past the end of the received UDP payload for most packets and can cause out-of-bounds reads / crashes. This should copy `msg->length` (or `m_packetLen`) bytes instead of `MAX_PACKET_SIZE`.
How can I resolve this? If you propose a fix, please make it concise. |
66fef26 to
022692a
Compare
| // TheSuperHackers @info As we are not detecting for network fragmentation and adjusting max payload, we set 1200 bytes UDP payload as a safe upper limit for various networks | ||
| // We chose 1200 bytes as when taking mobile networks into account, maximum transmission unit sizes can vary from 1340 - 1500 bytes | ||
| // and when the packet headers for PPPOE, IPV6 and virtual network encapsulation are considered, we need a lower safe UDP payload to prevent fragmentation. | ||
| static constexpr const Int MAX_UDP_PAYLOAD = 1200; |
| static constexpr const Int MAX_UDP_PAYLOAD = 1200; | ||
| // UDP (8 bytes) + IP header (28 bytes) = 36 bytes total. We want a total packet size of 512, so 512 - 36 = 476 | ||
| static const Int MAX_PACKET_SIZE = 476; | ||
| static constexpr const Int RESTRICTED_UDP_PAYLOAD = 476; |
There was a problem hiding this comment.
Perhaps use RETAIL instead of RESTRICTED
| static constexpr const Int MAX_PACKET_SIZE = RESTRICTED_UDP_PAYLOAD; | ||
| static constexpr const Int MAX_MESSAGE_LEN = 1024; | ||
| #else | ||
| static constexpr const Int MAX_PACKET_SIZE = MAX_UDP_PAYLOAD; |
There was a problem hiding this comment.
After having another quick look, i actually need to tweak this.
MAX_MESSAGE_LEN is the maximum data that goes out on the wire in the end, MAX_PACKET_SIZE is the max size a game message packet can be, but it gets encapsulated into a NetworkMessage struct with a NetworkMessageHeader prepended to the data, which adds 6 bytes to the start of the data.
So the MAX_PACKET_SIZE actually needs to be MAX_MESSAGE_LEN - Sizeof(NetworkMessageHeader) to not cause bytes to be missed at the end of the data.
The networking code is a mess...
There was a problem hiding this comment.
To save the confusion again i am going to look at renaming these to better suit their purpose.
There was a problem hiding this comment.
Tweaked the naming and added some extra comments to affected places, the issue here is the entire handling of network data. It really needs refactoring but theres no clean way to do that while properly maintaining the retail mishandling for now.
It's something i can come back to in the future with more network api tweaking.
Dismissed because the Mauller wants to do another pass
|
After some talking with X64 i will need to adjust the packet size (data that goes onto the wire) down to 1100, the extra 100 bytes is to help with TURN relay headers and extra reliability layers That GO uses. this still gives 2.2-2.3x the packet size of retail. This means that data on the wire, UDP payload, needs to be a max of 1100, but game messages can only be a max of 1094 due to the 6 bytes of game packet header that i overlooked originally. We can always adjust these values in future when refactoring more of the networking. |
…e connection issues
022692a to
265fab3
Compare
|
Updated now with the tweaks |
|
In future, we can also introduce packet data compression to help reduce the traffic pressure. |
Closes: #203
This PR increases the send and receive buffer sizes, this can help to alleviate network pressure that can lead to the DC menu appearing when the game is waiting for more network data or has to wait for a resend.
The PR also implements a non retail change to increase the amount of data stored per UDP packet.
The retail game was designed with Dialup limitations in mind, which tended to limit the MTU side to 576 bytes.
Internally they chose 512 bytes as their packet size limit and then incorrectly calculated a lower max UDP payload than necessary on top of this.
The fix allows the game to use 1200 Bytes as the UDP payload for the greatest compatibility between different types of network connections. This should take into account overhead of PPPOE, VPN's and IPV6 on supporting networks without causing fragmentation.
At 1200 bytes per packets we are now handling ~2.5x the amount of data per packet compared to retail, this should allow a reduction of network traffic by up to 2/3's depending on how game commands are being generated.
When testing this on GO with a full 8 player Mayhem game, we did not see a single DC screen popup.
All players were creating large armies and using QQA repeatedly to move them.