Skip to content

Conversation

@bobtista
Copy link

@bobtista bobtista commented Dec 31, 2025

Fixes latent bugs in network packet size calculations

Changes

  1. Fix struct sizes (NetPacketStructs.h)

    • Add missing commandId field to NetPacketLoadCompleteMessage and NetPacketTimeOutGameStartMessage
    • Change averageFps from UnsignedByte to UnsignedShort in NetPacketRunAheadMetricsCommand
  2. Fix isRoomFor* functions (NetPacket.cpp)

    • Add missing CommandId size calculation to isRoomForLoadCompleteMessage and isRoomForTimeOutGameStartMessage
  3. Fix wrong function call (NetPacket.cpp)

    • addTimeOutGameStartMessage now calls isRoomForTimeOutGameStartMessage instead of isRoomForLoadCompleteMessage (These are accidentally/coincidentally identical, but it's now using the right one)

@bobtista bobtista self-assigned this Dec 31, 2025
@xezon
Copy link

xezon commented Jan 1, 2026

Can you describe the implications of this change? What does it fix in practice and can it be user facing at all?

@bobtista
Copy link
Author

bobtista commented Jan 3, 2026

Can you describe the implications of this change? What does it fix in practice and can it be user facing at all?

There should be no practical change, and no user facing change. In theory, when a message is too large, the wrapper path uses sizeof(struct) which would cause problems when it's wrong.

  • GetBufferSizeNeededForCommand -> getPackedByteCount() -> sizeof(NetPacketLoadCompleteMessage) = 7 bytes
  • Allocate 7-byte buffer but then FillBufferWithLoadCompleteMessage writes 10 bytes
  • Also bufferSize would be wrong, so the receiver would get incomplete data
    BUT
    LoadComplete/TimeOutGameStart are ~10 bytes, MAX_PACKET_SIZE is 512+, so addCommand() should always succeed and this path should never be hit.

That said, keeping the bug makes the serialization /deserialization code messier and hacky. We have to write the struct and then manually add the commandId, instead of just writing the struct. #1680 will be simpler after this fix

@xezon
Copy link

xezon commented Jan 3, 2026

Are you very sure this causes no runtime issues?

UnsignedInt bufferSize = GetBufferSizeNeededForCommand(msg); // <---- expects good size
bigPacketData = NEW UnsignedByte[bufferSize]; // <---- allocates with size
FillBufferWithCommand(bigPacketData, ref); // <---- writes to sized buffer

Wouldn't the write overflow the buffer if it was too small?

@bobtista
Copy link
Author

bobtista commented Jan 3, 2026

Yes exactly. The reason there's no runtime issue is that ConstructBigCommandPacketList is only called when packet->addCommand() returns false (message too large for single packet). In other words, I think this is a problematic bug that went unnoticed because the message sizes are just never too big. Do you see any downsides to fixing it?

@xezon
Copy link

xezon commented Jan 4, 2026

Hmm ok maybe ConstructBigCommandPacketList is indeed seldom called. I would expect it to be called on file transfer, because file data certainly is bigger than single packet size.

I wonder how the isRoomFor* functions relate to this. Are these quasi duplicates of the getPackedByteCount functions?

Do you see any downsides to fixing it?

No. I am just trying to understand if this will fix anything user facing.

@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker Network Anything related to network, servers Gen Relates to Generals ZH Relates to Zero Hour Fix Is fixing something, but is not user facing Stability Concerns stability of the runtime labels Jan 4, 2026
@bobtista
Copy link
Author

bobtista commented Jan 4, 2026

Hmm ok maybe ConstructBigCommandPacketList is indeed seldom called. I would expect it to be called on file transfer, because file data certainly is bigger than single packet size.

I wonder how the isRoomFor* functions relate to this. Are these quasi duplicates of the getPackedByteCount functions?

Yes, file transfers use the big packet flow, but those sizes are correct - the bug was only in LoadComplete/TimeOutGameStart which never use the big packet path.

The isRoomFor* functions do similar work, but they handle the conditional inclusion of things like 'Did this change since the last message?' - while looking, I noticed they actually had the same bugs (missing CommandId counting), now fixed. Again, it never mattered for these messages because it's like 10 bytes being checked if it fits in 512 byte packets.

@xezon xezon added Bug Something is not working right, typically is user facing and removed Fix Is fixing something, but is not user facing labels Jan 7, 2026
@xezon xezon changed the title fix(network): correct packed struct field types and sizes bugfix(network): Fix packet size setup mistakes Jan 7, 2026
Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

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

I have promoted this to bug fix because in theory this can corrupt memory, but is probably very unlikely (or even impossible) in practice. Basically a packet would have to be filled just enough for one of the missized packets to pass the incorrect size check, while their written contents would then exceed the packet buffer.

I hope we can consolidate these packet size code paths because this setup is just awful.

@xezon xezon merged commit 3e8c472 into TheSuperHackers:main Jan 7, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker Network Anything related to network, servers Stability Concerns stability of the runtime ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants