bugfix(network): Prevent LAN lobby hang with long player names - #3039
bugfix(network): Prevent LAN lobby hang with long player names#3039bobtista wants to merge 2 commits into
Conversation
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Source/GameNetwork/GameInfo.cpp | Adds bounded UTF-8-aware player-name truncation, deterministic collision handling, and a second serialization pass for oversized LAN payloads. |
| Core/GameEngine/Source/GameNetwork/LANAPI.cpp | Correctly permits a payload of exactly 400 bytes, which fits the existing 401-byte null-terminated destination. |
| Core/GameEngine/Include/GameNetwork/LANAPI.h | Adds a compile-time assertion confirming that the LAN options buffer includes capacity beyond the payload limit. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Build UTF-8 player names] --> B[Serialize full GameInfo]
B --> C{Payload exceeds 400 bytes?}
C -- No --> D[Return payload]
C -- Yes --> E[Calculate required byte reduction]
E --> F{Enough removable name bytes?}
F -- No --> G[Return empty string]
F -- Yes --> H[Truncate names at UTF-8 boundaries]
H --> I[Resolve truncated-name collisions]
I --> J[Rebuild serialized GameInfo]
J --> D
Reviews (1): Last reviewed commit: "bugfix(network): Prevent LAN lobby hang ..." | Re-trigger Greptile
Skyaero42
left a comment
There was a problem hiding this comment.
This feels very complicated for what is eventually just a hack. FIxing the 400 byte gameinfo byte limit should be the true goal.
Something as simple as: count the number of available bytes for player names and divide that by the number of players - this gives the number of bytes each player name can have. Yes, it is not exact (if there a players with shorter names, that would also allow players with longer names than the threshold).
In general, there is a lot of stuff added in GameInfo that doesn't belong there. Specific byte counts of characters belong in Asciistring. Such function can probably also be generalized instead of using First and Last in functions.
| void SkirmishGameInfo::loadPostProcess() | ||
| { | ||
| } | ||
|
|
There was a problem hiding this comment.
removing these lines is out of scope imho. Let clang deal with that.
| Int remainingTruncatableByteCount = 0; | ||
|
|
||
| // Build truncatable byte count and player index pairs for the player names. | ||
| for (Int pi = 0; pi < MAX_SLOTS; ++pi) |
There was a problem hiding this comment.
pi could be confused with the number pi. Maybe just use i?
| return (static_cast<unsigned char>(c) & 0xC0) == 0x80; | ||
| } | ||
|
|
||
| static Int GetFirstUtf8CharacterByteLength(const AsciiString& string) |
There was a problem hiding this comment.
This should be an Asciistring function. It does not belong in GameInfo
| } | ||
| } | ||
|
|
||
| static Bool IsUtf8ContinuationByte(Char c) |
There was a problem hiding this comment.
This should be an Asciistring function. It does not belong in GameInfo
|
|
||
| static Int DivCeilPositive(Int numerator, Int denominator) | ||
| { | ||
| return (numerator + denominator - 1) / denominator; |
There was a problem hiding this comment.
- Requires a division by zero check
- Doesn't really fit GameInfo. Maybe WWMath?
- The name doesn't make it clear what it does.
| return true; | ||
| } | ||
|
|
||
| static void ReplaceLastUtf8Character(AsciiString& string, Char replacement) |
There was a problem hiding this comment.
Greptile has paused reviews on this repository — it used its 100 free open-source review credits for this billing period. Reviews resume automatically on August 26. To continue before then, an organization admin can keep reviews running past the free credits — those bill as normal usage.
7900833 to
85030a1
Compare
Agreed, but retail still needs something, even if it's hacky. The 400-byte limit is part of the packed retail LAN wire layout: LANMessage is sent by size and cast directly by receivers, so enlarging the options array would change field offsets and break retail compat. Removing that limit requires a versioned or chunked protocol extension and should be a separate change. This PR keeps the existing wire format and fixes the current infinite loop; it would remain necessary as the retail-compatible fallback even after an extended protocol is introduced. |
GameInfoToAsciiStringserializes the LAN lobby state into a string with a 400-byte limit. The existing code truncates each player name while appending its slot:Once the fixed portion of the options string consumes the remaining budget,
lenMaxbecomes negative. The loop removes the entire name, after whichAsciiString::removeLastCharbecomes a no-op. Because0 > lenMaxremains true, the host spins forever.Now the options string is built once with the full player names. Only when that string exceeds the limit is an exceptional second pass performed:
0through9if necessary.If the names cannot provide enough space while retaining one complete character each, serialization returns an empty string. Receivers reject that payload through the existing
ParseAsciiStringToGameInfofailure path instead of leaving the host in an infinite loop.Truncation affects only the serialized LAN payload. The host retains the full player names, while remote clients may display their truncated forms.
Supporting changes add a compile-time check that the
GameOptions.optionsbuffer exceedsm_lanMaxOptionsLengthand allow a payload of exactly 400 bytes, which fits in the 401-byte null-terminated buffer.Follow-up: cache each converted player name in
GameSlotso it is not rebuilt on every room refresh, as suggested in #1119.Todo:
CoreVerification