Ejemplo n.º 1
0
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;
		}
	}