-
Notifications
You must be signed in to change notification settings - Fork 229
refactor(network): Remove dynamic allocations for NetPacket #2866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,25 +71,22 @@ NetCommandRef *NetPacket::ConstructNetCommandMsgFromRawData(const UnsignedByte * | |
| return ref; | ||
| } | ||
|
|
||
| NetPacketList NetPacket::ConstructBigCommandPacketList(NetCommandRef *ref) { | ||
| NetCommandList *NetPacket::ConstructBigCommandList(NetCommandRef *ref) | ||
| { | ||
| // if we don't have a unique command ID, then the wrapped command cannot | ||
| // be identified. Therefore don't allow commands without a unique ID to | ||
| // be wrapped. | ||
| NetCommandMsg *msg = ref->getCommand(); | ||
|
|
||
| if (!DoesCommandRequireACommandID(msg->getNetCommandType())) { | ||
| DEBUG_CRASH(("Trying to wrap a command that doesn't have a unique command ID")); | ||
| return NetPacketList(); | ||
| return nullptr; | ||
| } | ||
|
|
||
| UnsignedInt bufferSize = GetBufferSizeNeededForCommand(msg); // need to implement. I have a drinking problem. | ||
| UnsignedByte *bigPacketData = nullptr; | ||
|
|
||
| NetPacketList packetList; | ||
| UnsignedByte* bigPacketData = NEW UnsignedByte[bufferSize]; | ||
|
|
||
| // create the buffer for the huge message and fill the buffer with that message. | ||
| UnsignedInt bigPacketCurrentOffset = 0; | ||
| bigPacketData = NEW UnsignedByte[bufferSize]; | ||
| ref->getCommand()->copyBytesForNetPacket(bigPacketData, *ref); | ||
|
|
||
| // create the wrapper command message we'll be using. | ||
|
|
@@ -102,55 +99,51 @@ NetPacketList NetPacket::ConstructBigCommandPacketList(NetCommandRef *ref) { | |
| if ((bufferSize % commandSizePerPacket) > 0) { | ||
| ++numChunks; | ||
| } | ||
|
|
||
| NetCommandList* commandList = newInstance(NetCommandList); | ||
|
|
||
| UnsignedInt currentChunk = 0; | ||
| UnsignedInt bigPacketCurrentOffset = 0; | ||
|
|
||
| // create the packets and the wrapper messages. | ||
| while (currentChunk < numChunks) { | ||
| NetPacket *packet = newInstance(NetPacket); | ||
|
|
||
| UnsignedInt dataSizeThisPacket = commandSizePerPacket; | ||
| if ((bufferSize - bigPacketCurrentOffset) < dataSizeThisPacket) { | ||
| dataSizeThisPacket = bufferSize - bigPacketCurrentOffset; | ||
| UnsignedInt dataSizeThisChunk = commandSizePerPacket; | ||
| if ((bufferSize - bigPacketCurrentOffset) < dataSizeThisChunk) { | ||
| dataSizeThisChunk = bufferSize - bigPacketCurrentOffset; | ||
| } | ||
| NetCommandDataChunk bigPacket(dataSizeThisPacket); | ||
| memcpy(bigPacket.data(), bigPacketData + bigPacketCurrentOffset, bigPacket.size()); | ||
|
|
||
| NetCommandDataChunk chunkData(dataSizeThisChunk); | ||
| memcpy(chunkData.data(), bigPacketData + bigPacketCurrentOffset, chunkData.size()); | ||
|
|
||
| if (DoesCommandRequireACommandID(wrapperMsg->getNetCommandType())) { | ||
| wrapperMsg->setID(GenerateNextCommandID()); | ||
| } | ||
|
|
||
| wrapperMsg->setPlayerID(msg->getPlayerID()); | ||
| wrapperMsg->setExecutionFrame(msg->getExecutionFrame()); | ||
|
|
||
| wrapperMsg->setChunkNumber(currentChunk); | ||
| wrapperMsg->setNumChunks(numChunks); | ||
| wrapperMsg->setDataOffset(bigPacketCurrentOffset); | ||
| wrapperMsg->setData(bigPacket); | ||
| wrapperMsg->setData(chunkData); | ||
| wrapperMsg->setTotalDataLength(bufferSize); | ||
| wrapperMsg->setWrappedCommandID(msg->getID()); | ||
|
|
||
| bigPacketCurrentOffset += dataSizeThisPacket; | ||
|
|
||
| NetCommandRef *ref = NEW_NETCOMMANDREF(wrapperMsg); | ||
| ref->setRelay(ref->getRelay()); | ||
|
|
||
| if (packet->addCommand(ref) == FALSE) { | ||
| DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("NetPacket::BeginBigCommandPacketList - failed to add a wrapper command to the packet")); // I still have a drinking problem. | ||
| } | ||
| bigPacketCurrentOffset += dataSizeThisChunk; | ||
|
|
||
| packetList.push_back(packet); | ||
|
|
||
| deleteInstance(ref); | ||
| ref = nullptr; | ||
| NetCommandRef* chunkRef = NEW_NETCOMMANDREF(wrapperMsg); | ||
| chunkRef->setRelay(ref->getRelay()); | ||
|
|
||
| commandList->addMessage(chunkRef); | ||
| ++currentChunk; | ||
| } | ||
|
Comment on lines
108
to
138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Every The old code worked because Any game command large enough to span two or more network chunks will be silently dropped to its last piece, breaking reassembly on the receiver. Prompt To Fix With AIThis is a comment left during a code review.
Path: Core/GameEngine/Source/GameNetwork/NetPacket.cpp
Line: 108-138
Comment:
**All chunk refs share the same mutable `wrapperMsg` — big commands silently truncated to one chunk**
Every `chunkRef` created in the loop points to the *same* `wrapperMsg` object. Because `wrapperMsg` is mutated in place on each iteration (new command ID, new chunk number, new data), all refs that were already inserted into `commandList` immediately observe the new state. The deduplication logic in `addMessage(NetCommandRef*&)` compares `m_lastMessageInserted->getCommand()` and `msg->getCommand()`, but both are literally the same pointer, so every chunk after the first is discarded as a "duplicate" and its `chunkRef` is deleted. The list ends up with a single entry holding the last chunk's data and the last generated command ID.
The old code worked because `addCommand` serialized each chunk's state into a raw byte buffer inside a per-chunk `NetPacket`, and the caller then called `getCommandList()` to deserialize independent `NetCommandMsg` objects from those bytes. The new path eliminates that serialization round-trip, but must instead allocate a distinct `NetWrapperCommandMsg` per chunk (so each ref owns its own snapshot of the state) to preserve correctness.
Any game command large enough to span two or more network chunks will be silently dropped to its last piece, breaking reassembly on the receiver.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| wrapperMsg->detach(); | ||
| wrapperMsg = nullptr; | ||
|
|
||
| delete[] bigPacketData; | ||
| bigPacketData = nullptr; | ||
|
|
||
| return packetList; | ||
| return commandList; | ||
|
Caball009 marked this conversation as resolved.
|
||
| } | ||
|
|
||
| UnsignedInt NetPacket::GetBufferSizeNeededForCommand(NetCommandMsg *msg) { | ||
|
|
@@ -173,13 +166,14 @@ NetPacket::NetPacket() { | |
| /** | ||
| * Constructor given raw transport data. | ||
| */ | ||
| NetPacket::NetPacket(TransportMessage *msg) { | ||
| NetPacket::NetPacket(const TransportMessage& msg) { | ||
| init(); | ||
| m_packetLen = msg->length; | ||
| memcpy(m_packet, msg->data, MAX_PACKET_SIZE); | ||
|
|
||
| m_packetLen = msg.length; | ||
| memcpy(m_packet, msg.data, MAX_PACKET_SIZE); | ||
| m_numCommands = -1; | ||
| m_addr = msg->addr; | ||
| m_port = msg->port; | ||
| m_addr = msg.addr; | ||
| m_port = msg.port; | ||
| } | ||
|
|
||
| /** | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.