void UPacketLimitTest::ExecuteClientUnitTest() { bool bLowLevelSend = TestStage == ELimitTestStage::LTS_LowLevel_AtLimit || TestStage == ELimitTestStage::LTS_LowLevel_OverLimit; bool bBunchSend = TestStage == ELimitTestStage::LTS_Bunch_AtLimit || TestStage == ELimitTestStage::LTS_Bunch_OverLimit; // You can't access LowLevelSend from UNetConnection, but you can from UIpConnection, as it's exported there UIpConnection* IpConn = Cast<UIpConnection>(UnitConn); if (IpConn != nullptr) { int32 PacketLimit = UnitConn->MaxPacket; int32 SocketLimit = UnitConn->MaxPacket; TArray<uint8> PacketData; if (bBunchSend) { int64 FreeBits = UnitConn->SendBuffer.GetMaxBits() - MAX_BUNCH_HEADER_BITS + MAX_PACKET_TRAILER_BITS; PacketLimit = FreeBits / 8; check(PacketLimit > 0) } if (TestStage == ELimitTestStage::LTS_LowLevel_OverLimit || TestStage == ELimitTestStage::LTS_Bunch_OverLimit) { // Nudge the packet over the MaxPacket limit (accurate for LowLevel, approximate for Bunch) PacketLimit++; SocketLimit++; } PacketData.AddZeroed(PacketLimit); // Randomize the packet data (except for last byte), to thwart any compression, which would cause infinite recursion below // (e.g. recursively adding just zero's, would mean the same compressed size almost every time) for (int32 i=0; i<PacketData.Num()-1; i++) { PacketData[i] = FMath::Rand() % 255; } // Iteratively run 'test' sends, where the packet is passed through all the netcode but not sent, // unless the final (post-PacketHandler) packet size matches SocketLimit. bool bPacketAtLimit = false; int32 TryCount = 0; int32 SendDelta = 0; // Blocks all socket sends not matching SocketLimit TargetSocketSendSize = SocketLimit; while (!bPacketAtLimit && TryCount < 16) { if (bLowLevelSend) { IpConn->LowLevelSend(PacketData.GetData(), PacketData.Num(), PacketData.Num() * 8); } else if (bBunchSend) { UUnitTestNetConnection* UnitTestConn = CastChecked<UUnitTestNetConnection>(UnitConn); // If the bunch is to go over the limit, disable asserts bool bBunchOverLimit = TestStage == ELimitTestStage::LTS_Bunch_OverLimit; UnitTestConn->bDisableValidateSend = bBunchOverLimit; UnitConn->FlushNet(); int32 DummyControlBunchSequence = 0; FOutBunch* TestBunch = NUTNet::CreateChannelBunch(DummyControlBunchSequence, UnitConn, CHTYPE_Control, 0); TestBunch->Serialize(PacketData.GetData(), PacketData.Num()); UnitConn->SendRawBunch(*TestBunch, false); if (bBunchOverLimit) { // For a successful test, the bunch must cause a send error if (UnitConn->SendBuffer.IsError()) { bPacketAtLimit = true; UNIT_LOG(ELogType::StatusSuccess, TEXT("Detected successful bunch overflow. Moving to next test.")); NextTestStage(); } else { UNIT_LOG(ELogType::StatusFailure, TEXT("Failed to detect bunch overflow, when one was expected.")); VerificationState = EUnitTestVerification::VerifiedNeedsUpdate; } break; } UnitConn->FlushNet(); UnitTestConn->bDisableValidateSend = false; } // If PacketHandlers have increased/decreased final packet size away from SocketLimit, trim/pad the packet and retry SendDelta = FMath::Max(1, (SendDelta == 0 ? FMath::Abs(LastSocketSendSize - SocketLimit) : SendDelta / 2)); if (LastSocketSendSize > SocketLimit) { PacketData.RemoveAt(PacketData.Num()-1-SendDelta, SendDelta, false); } else if (LastSocketSendSize < SocketLimit) { PacketData.InsertZeroed(PacketData.Num()-1, SendDelta); for (int32 i=PacketData.Num()-1-SendDelta; i<SendDelta; i++) { PacketData[i] = FMath::Rand() % 255; } } else // if (LastSocketSendSize == SocketLimit) { // Packet successfully sent bPacketAtLimit = true; } TryCount++; } // Re-enable sending packets TargetSocketSendSize = 0; if (!bPacketAtLimit) { UNIT_LOG(ELogType::StatusFailure, TEXT("Failed to send packet - reached packet testing iteration limit.")); VerificationState = EUnitTestVerification::VerifiedUnreliable; } }