// Copyright Epic Games, Inc. All Rights Reserved. #include "PacketHandler.h" #include "Net/Core/Misc/PacketAudit.h" #include "EncryptionComponent.h" #include "Misc/ConfigCacheIni.h" #include "Modules/ModuleManager.h" #include "UObject/ObjectMacros.h" #include "UObject/Package.h" #include "HAL/ConsoleManager.h" #include "Net/Core/Misc/DDoSDetection.h" #include "HandlerComponentFactory.h" #include "ReliabilityHandlerComponent.h" #include "PacketHandlerProfileConfig.h" #include "SocketSubsystem.h" #include "Misc/StringBuilder.h" // @todo #JohnB: There is quite a lot of inefficient copying of packet data going on. // Redo the whole packet parsing/modification pipeline. IMPLEMENT_MODULE(FPacketHandlerComponentModuleInterface, PacketHandler); DEFINE_LOG_CATEGORY(PacketHandlerLog); DECLARE_CYCLE_STAT(TEXT("PacketHandler Incoming_Internal"), Stat_PacketHandler_Incoming_Internal, STATGROUP_Net); DECLARE_CYCLE_STAT(TEXT("PacketHandler Outgoing_Internal"), Stat_PacketHandler_Outgoing_Internal, STATGROUP_Net); // CVars #if !UE_BUILD_SHIPPING int32 GPacketHandlerCRCDump = 0; FAutoConsoleVariableRef CVarNetPacketHandlerCRCDump( TEXT("net.PacketHandlerCRCDump"), GPacketHandlerCRCDump, TEXT("Enables or disables dumping of packet CRC's for every HandlerComponent, Incoming and Outgoing, for debugging.")); static int32 GPacketHandlerTimeguardLimit = 20; static float GPacketHandlerTimeguardThresholdMS = 0.0f; bool GPacketHandlerDiscardTimeguardMeasurement = false; static FAutoConsoleVariableRef CVarNetPacketHandlerTimeguardThresholdMS( TEXT("net.PacketHandlerTimeguardThresholdMS"), GPacketHandlerTimeguardThresholdMS, TEXT("Threshold in milliseconds for the HandlerComponent timeguard, Incoming and Outgoing."), ECVF_Default); static FAutoConsoleVariableRef CVarNetPacketHandlerTimeguardLimit( TEXT("net.PacketHandlerTimeguardLimit"), GPacketHandlerTimeguardLimit, TEXT("Sets the maximum number of HandlerComponent timeguard logs.\n"), ECVF_Default ); // Lightweight time guard. Note: Threshold of 0 disables the timeguard #define NET_LIGHTWEIGHT_TIME_GUARD_BEGIN( Name, ThresholdMS ) \ double PREPROCESSOR_JOIN(__TimeGuard_ThresholdMS_, Name) = ThresholdMS; \ uint64 PREPROCESSOR_JOIN(__TimeGuard_StartCycles_, Name) = ( ThresholdMS > 0.0f && GPacketHandlerTimeguardLimit > 0 ) ? FPlatformTime::Cycles64() : 0; \ GPacketHandlerDiscardTimeguardMeasurement = false; #define NET_LIGHTWEIGHT_TIME_GUARD_END( Name, NameStringCode ) \ if ( PREPROCESSOR_JOIN(__TimeGuard_ThresholdMS_, Name) > 0.0f && GPacketHandlerTimeguardLimit > 0 && !GPacketHandlerDiscardTimeguardMeasurement ) \ {\ double PREPROCESSOR_JOIN(__TimeGuard_MSElapsed_,Name) = FPlatformTime::ToMilliseconds64( FPlatformTime::Cycles64() - PREPROCESSOR_JOIN(__TimeGuard_StartCycles_,Name) ); \ if ( PREPROCESSOR_JOIN(__TimeGuard_MSElapsed_,Name) > PREPROCESSOR_JOIN(__TimeGuard_ThresholdMS_, Name) ) \ { \ FString ReportName = NameStringCode; \ UE_LOG(PacketHandlerLog, Warning, TEXT("PacketHandler: %s - %s took %.2fms!"), TEXT(#Name), *ReportName, PREPROCESSOR_JOIN(__TimeGuard_MSElapsed_,Name)); \ GPacketHandlerTimeguardLimit--; \ } \ } #else // UE_BUILD_SHIPPING #define NET_LIGHTWEIGHT_TIME_GUARD_BEGIN( Name, ThresholdMS ) #define NET_LIGHTWEIGHT_TIME_GUARD_END( Name, NameStringCode ) #endif template OutType IntCastLog(InType In) { bool bFitsIn = IntFitsIn(In); ensureMsgf(bFitsIn, TEXT("PacketHandler: Loss of data caused by truncated cast")); UE_CLOG(!bFitsIn, PacketHandlerLog, Warning, TEXT("PacketHandler: Loss of data caused by truncated cast")); return static_cast(In); } /** * BufferedPacket */ BufferedPacket::~BufferedPacket() { delete [] Data; } /** * PacketHandler */ PacketHandler::PacketHandler(FDDoSDetection* InDDoS/*=nullptr*/) : Mode(UE::Handler::Mode::Client) , State(UE::Handler::State::Uninitialized) , bRawSend(false) , bBeganHandshaking(false) , bConnectionlessHandler(false) , MaxPacketBits(0) , DDoS(InDDoS) , LowLevelSendDel() , HandshakeCompleteDel() , OutgoingPacket(MAX_PACKET_SIZE * 8) , IncomingPacket() , HandlerComponents() , BufferedPackets() , QueuedPackets() , QueuedRawPackets() , QueuedHandlerPackets() , BufferedConnectionlessPackets() , QueuedConnectionlessPackets() , ReliabilityComponent(nullptr) , Provider() , Aggregator() { OutgoingPacket.SetAllowResize(true); OutgoingPacket.AllowAppend(true); } void PacketHandler::Tick(float DeltaTime) { for (const TSharedPtr& Component : HandlerComponents) { if (Component.IsValid()) { Component->Tick(DeltaTime); } } // Send off any queued handler packets BufferedPacket* QueuedPacket = nullptr; while (QueuedHandlerPackets.Dequeue(QueuedPacket)) { check(QueuedPacket->FromComponent != nullptr); FBitWriter OutPacket; OutPacket.SerializeBits(QueuedPacket->Data, QueuedPacket->CountBits); SendHandlerPacket(QueuedPacket->FromComponent, OutPacket, QueuedPacket->Traits); } } FPacketHandlerAddComponentByNameDelegate& PacketHandler::GetAddComponentByNameDelegate() { static FPacketHandlerAddComponentByNameDelegate AddComponentByNameDelegate; return AddComponentByNameDelegate; } FPacketHandlerAddComponentDelegate& PacketHandler::GetAddComponentDelegate() { static FPacketHandlerAddComponentDelegate AddComponentDelegate; return AddComponentDelegate; } void PacketHandler::Initialize(UE::Handler::Mode InMode, uint32 InMaxPacketBits, bool bConnectionlessOnly/*=false*/, TSharedPtr InProvider/*=nullptr*/, FDDoSDetection* InDDoS/*=nullptr*/, FName InDriverProfile/*=NAME_None*/) { Mode = InMode; MaxPacketBits = InMaxPacketBits; DDoS = InDDoS; bConnectionlessHandler = bConnectionlessOnly; // Only UNetConnection's will load the .ini components, for now. if (!bConnectionlessHandler) { TArray Components; FString DriverProfileCategory = FString::Printf(TEXT("%s PacketHandlerProfileConfig"), *InDriverProfile.GetPlainNameString()); GConfig->GetArray(*DriverProfileCategory, TEXT("Components"), Components, GEngineIni); // If we didn't get any matches, push in the regular components. if (Components.Num() == 0) { GConfig->GetArray(TEXT("PacketHandlerComponents"), TEXT("Components"), Components, GEngineIni); } // Users of this delegate can add components to the list by name and if necessary reorder them PRAGMA_DISABLE_DEPRECATION_WARNINGS GetAddComponentByNameDelegate().ExecuteIfBound(Components); PRAGMA_ENABLE_DEPRECATION_WARNINGS for (const FString& CurComponent : Components) { AddHandler(CurComponent, true); } // all of code below till end of scope can go away once deprecated GetAddComponentDelegate method is removed PRAGMA_DISABLE_DEPRECATION_WARNINGS // Users of this delegate can supply constructed additional components to be added after the named components TArray> AdditionalComponents; GetAddComponentDelegate().ExecuteIfBound(AdditionalComponents); for (TSharedPtr AdditionalComponent : AdditionalComponents) { AddHandler(AdditionalComponent, true); } PRAGMA_ENABLE_DEPRECATION_WARNINGS } // Add encryption component, if configured. FString EncryptionComponentName; if (GConfig->GetString(TEXT("PacketHandlerComponents"), TEXT("EncryptionComponent"), EncryptionComponentName, GEngineIni) && !EncryptionComponentName.IsEmpty()) { static IConsoleVariable* const AllowEncryptionCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.AllowEncryption")); if (AllowEncryptionCVar == nullptr || AllowEncryptionCVar->GetInt() != 0) { EncryptionComponent = StaticCastSharedPtr(AddHandler(EncryptionComponentName, true)); } else { UE_LOG(PacketHandlerLog, Warning, TEXT("PacketHandler encryption component is configured as %s, but it won't be used because the cvar net.AllowEncryption is false."), *EncryptionComponentName); } } bool bEnableReliability = false; GConfig->GetBool(TEXT("PacketHandlerComponents"), TEXT("bEnableReliability"), bEnableReliability, GEngineIni); if (bEnableReliability && !ReliabilityComponent.IsValid()) { UE_LOG(PacketHandlerLog, Warning, TEXT("bEnableReliability for PacketHandlerComponents is deprecated. For fully-reliable data, use reliable RPCs or a separate connection with a reliable protocol.")); PRAGMA_DISABLE_DEPRECATION_WARNINGS TSharedPtr NewComponent = MakeShareable(new ReliabilityHandlerComponent); ReliabilityComponent = StaticCastSharedPtr(NewComponent); AddHandler(NewComponent, true); PRAGMA_ENABLE_DEPRECATION_WARNINGS } } void PacketHandler::InitializeDelegates(FPacketHandlerLowLevelSendTraits InLowLevelSendDel, FPacketHandlerNotifyAddHandler InAddHandlerDel/*=FPacketHandlerNotifyAddHandler()*/) { LowLevelSendDel = InLowLevelSendDel; AddHandlerDel = InAddHandlerDel; } void PacketHandler::InitFaultRecovery(UE::Net::FNetConnectionFaultRecoveryBase* InFaultRecovery) { for (TSharedPtr& CurComponent : HandlerComponents) { CurComponent->InitFaultRecovery(InFaultRecovery); } } void PacketHandler::NotifyAnalyticsProvider(TSharedPtr InProvider, TSharedPtr InAggregator) { Provider = InProvider; Aggregator = InAggregator; if (State != UE::Handler::State::Uninitialized) { for (const TSharedPtr& CurComponent : HandlerComponents) { if (CurComponent->IsInitialized()) { CurComponent->NotifyAnalyticsProvider(); } } } } void PacketHandler::InitializeComponents() { if (State == UE::Handler::State::Uninitialized) { if (HandlerComponents.Num() > 0) { SetState(UE::Handler::State::InitializingComponents); } else { HandlerInitialized(); } } // Trigger delayed-initialization for HandlerComponents for (TSharedPtr& Component : HandlerComponents) { if (Component.IsValid() && !Component->IsInitialized()) { Component->Initialize(); Component->NotifyAnalyticsProvider(); } } // Called early, to ensure that all handlers report a valid reserved packet bits value (triggers an assert if not) GetTotalReservedPacketBits(); } void PacketHandler::BeginHandshaking(FPacketHandlerHandshakeComplete InHandshakeDel/*=FPacketHandlerHandshakeComplete()*/) { check(!bBeganHandshaking); bBeganHandshaking = true; HandshakeCompleteDel = InHandshakeDel; for (int32 i=HandlerComponents.Num() - 1; i>=0; --i) { HandlerComponent& CurComponent = *HandlerComponents[i]; if (CurComponent.RequiresHandshake() && !CurComponent.IsInitialized()) { CurComponent.NotifyHandshakeBegin(); break; } } } void PacketHandler::AddHandler(TSharedPtr& NewHandler, bool bDeferInitialize/*=false*/) { // This is never valid. Can end up silently changing maximum allow packet size, which could cause failure to send packets. if (State != UE::Handler::State::Uninitialized) { LowLevelFatalError(TEXT("Handler added during runtime.")); return; } // This should always be fatal, as an unexpectedly missing handler, may break net compatibility with the remote server/client if (!NewHandler.IsValid()) { LowLevelFatalError(TEXT("Failed to add handler - invalid instance.")); return; } // Warn if a component already exists with the same name. const bool bNameAlreadyExists = HandlerComponents.ContainsByPredicate([NewHandler](const TSharedPtr& Component) { return Component->GetName() == NewHandler->GetName(); }); if (bNameAlreadyExists) { UE_LOG(PacketHandlerLog, Warning, TEXT("Packet handler already contains a component with name %s."), *NewHandler->GetName().ToString()); return; } HandlerComponents.Add(NewHandler); NewHandler->Handler = this; AddHandlerDel.ExecuteIfBound(NewHandler); if (!bDeferInitialize) { NewHandler->Initialize(); } } TSharedPtr PacketHandler::AddHandler(const FString& ComponentStr, bool bDeferInitialize/*=false*/) { TSharedPtr ReturnVal = nullptr; if (!ComponentStr.IsEmpty()) { FString ComponentName; FString ComponentOptions; for (int32 i=0; i(*SingletonName, EFindFirstObjectOptions::NativeFirst | EFindFirstObjectOptions::EnsureIfAmbiguous); if (Factory == nullptr) { UClass* FactoryClass = StaticLoadClass(UHandlerComponentFactory::StaticClass(), nullptr, *ComponentName); if (FactoryClass != nullptr) { Factory = NewObject(GetTransientPackage(), FactoryClass, *SingletonName); } } if (Factory != nullptr) { ReturnVal = Factory->CreateComponentInstance(ComponentOptions); } else { UE_LOG(PacketHandlerLog, Warning, TEXT("Unable to load HandlerComponent factory: %s"), *ComponentName); } } // @todo #JohnB: Deprecate non-factory components eventually else { FPacketHandlerComponentModuleInterface* PacketHandlerInterface = FModuleManager::Get().LoadModulePtr(FName(*ComponentName)); if (PacketHandlerInterface != nullptr) { ReturnVal = PacketHandlerInterface->CreateComponentInstance(ComponentOptions); } if (!ReturnVal.IsValid()) { UE_LOG(PacketHandlerLog, Warning, TEXT("Unable to Load Module: %s"), *ComponentName); } } if (ReturnVal.IsValid()) { UE_LOG(PacketHandlerLog, Log, TEXT("Loaded PacketHandler component: %s (%s)"), *ComponentName, *ComponentOptions); AddHandler(ReturnVal, bDeferInitialize); } } else { UE_LOG(PacketHandlerLog, Warning, TEXT("PacketHandlerComponent 'ReliabilityHandlerComponent' is internal-only.")); } } return ReturnVal; } void PacketHandler::IncomingHigh(FBitReader& Reader) { // @todo #JohnB } void PacketHandler::OutgoingHigh(FBitWriter& Writer) { // @todo #JohnB } TSharedPtr PacketHandler::GetEncryptionComponent() { return EncryptionComponent; } TSharedPtr PacketHandler::GetComponentByName(FName ComponentName) const { for (const TSharedPtr& Component : HandlerComponents) { if (Component.IsValid() && Component->GetName() == ComponentName) { return Component; } } return nullptr; } void PacketHandler::CountBytes(FArchive& Ar) const { Ar.CountBytes(sizeof(*this), sizeof(*this)); OutgoingPacket.CountMemory(Ar); IncomingPacket.CountMemory(Ar); HandlerComponents.CountBytes(Ar); for (const TSharedPtr& Component : HandlerComponents) { if (HandlerComponent const * const LocalComponent = Component.Get()) { LocalComponent->CountBytes(Ar); } } // Don't handle EncryptionComponent, as it should be in our components array. BufferedPackets.CountBytes(Ar); for (BufferedPacket const * const LocalPacket : BufferedPackets) { if (LocalPacket) { LocalPacket->CountBytes(Ar); } } // Unfortunately, there's currently no way to safely calculate memory usage for TQueues. // so QueuedPackets, QueuedRawPackets, QueuedHandlerPackets, and QueuedConnectionlessPackets // can't be tracked without a rework. BufferedConnectionlessPackets.CountBytes(Ar); for (BufferedPacket const * const LocalPacket : BufferedConnectionlessPackets) { if (LocalPacket) { LocalPacket->CountBytes(Ar); } } // Don't handle ReliabilityComponent, since it should be in our components array. // Don't track AnalyticsProvider as that should be handled elsewhere. } void HandlerComponent::CountBytes(FArchive& Ar) const { Ar.CountBytes(sizeof(*this), sizeof(*this)); } EIncomingResult PacketHandler::Incoming_Internal(FReceivedPacketView& PacketView) { SCOPE_CYCLE_COUNTER(Stat_PacketHandler_Incoming_Internal); EIncomingResult ReturnVal = EIncomingResult::Success; FPacketDataView& DataView = PacketView.DataView; int32 CountBits = DataView.NumBits(); #if !UE_BUILD_SHIPPING uint32 SocketCRC = 0; if (UNLIKELY(!!GPacketHandlerCRCDump)) { SocketCRC = FCrc::MemCrc32(DataView.GetData(), DataView.NumBytes()); } #endif if (HandlerComponents.Num() > 0) { const uint8* DataPtr = DataView.GetData(); uint8 LastByte = (UNLIKELY(DataPtr == nullptr)) ? 0 : DataPtr[DataView.NumBytes() - 1]; if (LastByte != 0) { CountBits--; while (!(LastByte & 0x80)) { LastByte *= 2; CountBits--; } } else { PacketView.DataView = {nullptr, 0, ECountUnits::Bits}; ReturnVal = EIncomingResult::Error; #if !UE_BUILD_SHIPPING UE_CLOG((DDoS == nullptr || !DDoS->CheckLogRestrictions()), PacketHandlerLog, Error, TEXT("PacketHandler parsing packet with zero's in last byte.")); #endif } } #if !UE_BUILD_SHIPPING struct FHandlerCRC { uint32 CRC; bool bHasAlignedCRC; bool bError; }; TArray HandlerCRCs; uint32 NetConnectionCRC = 0; #endif if (ReturnVal == EIncomingResult::Success) { FBitReader ProcessedPacketReader(DataView.GetMutableData(), CountBits); FIncomingPacketRef PacketRef = {ProcessedPacketReader, PacketView.Address, PacketView.Traits}; FPacketAudit::CheckStage(TEXT("PostPacketHandler"), ProcessedPacketReader); if (State == UE::Handler::State::Uninitialized) { UpdateInitialState(); } for (int32 i=HandlerComponents.Num() - 1; i>=0; --i) { HandlerComponent& CurComponent = *HandlerComponents[i]; #if !UE_BUILD_SHIPPING if (UNLIKELY(!!GPacketHandlerCRCDump)) { if (ProcessedPacketReader.IsError()) { HandlerCRCs.Add({0, false, true}); } else if (ProcessedPacketReader.GetPosBits() == 0) { HandlerCRCs.Add({FCrc::MemCrc32(ProcessedPacketReader.GetData(), IntCastLog(ProcessedPacketReader.GetNumBytes())), true, false}); } else { HandlerCRCs.Add({0, false, false}); } } #endif if (CurComponent.IsActive() && !ProcessedPacketReader.IsError() && ProcessedPacketReader.GetBitsLeft() > 0) { // Realign the packet, so the packet data starts at position 0, if necessary if (ProcessedPacketReader.GetPosBits() != 0 && !CurComponent.CanReadUnaligned()) { RealignPacket(ProcessedPacketReader); #if !UE_BUILD_SHIPPING if (UNLIKELY(!!GPacketHandlerCRCDump)) { FHandlerCRC& CurCRC = HandlerCRCs[HandlerCRCs.Num() - 1]; CurCRC.CRC = FCrc::MemCrc32(ProcessedPacketReader.GetData(), IntCastLog(ProcessedPacketReader.GetNumBytes())); CurCRC.bHasAlignedCRC = true; } #endif } if (PacketView.Traits.bConnectionlessPacket) { CurComponent.IncomingConnectionless(PacketRef); } else { NET_LIGHTWEIGHT_TIME_GUARD_BEGIN(Incoming, GPacketHandlerTimeguardThresholdMS); CurComponent.Incoming(PacketRef); NET_LIGHTWEIGHT_TIME_GUARD_END(Incoming, CurComponent.GetName().ToString()); } } } if (!ProcessedPacketReader.IsError()) { ReplaceIncomingPacket(ProcessedPacketReader); if (IncomingPacket.GetBitsLeft() > 0) { FPacketAudit::CheckStage(TEXT("PrePacketHandler"), IncomingPacket, true); #if !UE_BUILD_SHIPPING if (UNLIKELY(!!GPacketHandlerCRCDump)) { NetConnectionCRC = FCrc::MemCrc32(IncomingPacket.GetData(), IntCastLog(IncomingPacket.GetBytesLeft())); } #endif } PacketView.DataView = {IncomingPacket.GetData(), (int32)IncomingPacket.GetBitsLeft(), ECountUnits::Bits}; } else { PacketView.DataView = {nullptr, 0, ECountUnits::Bits}; ReturnVal = EIncomingResult::Error; } } #if !UE_BUILD_SHIPPING if (UNLIKELY(!!GPacketHandlerCRCDump)) { TStringBuilder<2048> HandlerCRCStr; for (int32 i=HandlerCRCs.Num()-1; i>=0; i--) { FHandlerCRC& CurCRC = HandlerCRCs[i]; HandlerCRCStr.Appendf(TEXT("%s%i: "), (i != HandlerCRCs.Num()-1 ? TEXT(", ") : TEXT("")), (HandlerCRCs.Num() - 1) - i); if (CurCRC.bError) { HandlerCRCStr << TEXT("Error"); } else if (!CurCRC.bHasAlignedCRC) { HandlerCRCStr << TEXT("Unaligned"); } else { HandlerCRCStr << FString::Printf(TEXT("%08X"), CurCRC.CRC); } } UE_LOG(PacketHandlerLog, Log, TEXT("PacketHandler::Incoming: CRC Dump: NetConnection: %s, Component: %s, Socket: %08X"), (ReturnVal == EIncomingResult::Success ? *FString::Printf(TEXT("%08X"), NetConnectionCRC) : TEXT("Error")), *HandlerCRCStr, SocketCRC); } #endif return ReturnVal; } const ProcessedPacket PacketHandler::Outgoing_Internal(uint8* Packet, int32 CountBits, FOutPacketTraits& Traits, bool bConnectionless, const TSharedPtr& Address) { SCOPE_CYCLE_COUNTER(Stat_PacketHandler_Outgoing_Internal); ProcessedPacket ReturnVal; #if !UE_BUILD_SHIPPING uint32 NetConnectionCRC = 0; struct FHandlerCRC { uint32 CRC; bool bError; }; TArray HandlerCRCs; if (UNLIKELY(!!GPacketHandlerCRCDump)) { NetConnectionCRC = FCrc::MemCrc32(Packet, FMath::DivideAndRoundUp(CountBits, 8)); } #endif if (!bRawSend) { OutgoingPacket.Reset(); if (State == UE::Handler::State::Uninitialized) { UpdateInitialState(); } if (State == UE::Handler::State::Initialized) { OutgoingPacket.SerializeBits(Packet, CountBits); FPacketAudit::AddStage(TEXT("PrePacketHandler"), OutgoingPacket, true); for (int32 i=0; i(OutgoingPacket.GetNumBytes())), false}); } } #endif } // Add a termination bit, the same as the UNetConnection code does, if appropriate if (HandlerComponents.Num() > 0 && OutgoingPacket.GetNumBits() > 0) { FPacketAudit::AddStage(TEXT("PostPacketHandler"), OutgoingPacket); OutgoingPacket.WriteBit(1); } if (!bConnectionless && ReliabilityComponent.IsValid() && OutgoingPacket.GetNumBits() > 0) { PRAGMA_DISABLE_DEPRECATION_WARNINGS // Let the reliability handler know about all processed packets, so it can record them for resending if needed ReliabilityComponent->QueuePacketForResending(OutgoingPacket.GetData(), IntCastLog(OutgoingPacket.GetNumBits()), Traits); PRAGMA_ENABLE_DEPRECATION_WARNINGS } } // Buffer any packets being sent from game code until processors are initialized else if (State == UE::Handler::State::InitializingComponents && CountBits > 0) { if (bConnectionless) { BufferedConnectionlessPackets.Add(new BufferedPacket(Address, Packet, CountBits, Traits)); } else { BufferedPackets.Add(new BufferedPacket(Packet, CountBits, Traits)); } Packet = nullptr; CountBits = 0; } if (!OutgoingPacket.IsError()) { ReturnVal = {OutgoingPacket.GetData(), (int32)OutgoingPacket.GetNumBits()}; } else { ReturnVal = {nullptr, 0, true}; } } else { ReturnVal = {Packet, CountBits}; } #if !UE_BUILD_SHIPPING if (UNLIKELY(!!GPacketHandlerCRCDump)) { TStringBuilder<2048> HandlerCRCStr; for (int32 i=0; i 0 ? TEXT(", ") : TEXT("")), i); if (CurCRC.bError) { HandlerCRCStr << TEXT("Error"); } else { HandlerCRCStr.Appendf(TEXT("%08X"), CurCRC.CRC); } } TStringBuilder<32> SocketCRCStr; if (ReturnVal.bError) { SocketCRCStr << TEXT("Error"); } else { SocketCRCStr.Appendf(TEXT("%08X"), FCrc::MemCrc32(ReturnVal.Data, FMath::DivideAndRoundUp(ReturnVal.CountBits, 8))); } UE_LOG(PacketHandlerLog, Log, TEXT("PacketHandler::Outgoing: CRC Dump: NetConnection: %08X, Component: %s, Socket: %s"), NetConnectionCRC, *HandlerCRCStr, *SocketCRCStr); } #endif return ReturnVal; } void PacketHandler::ReplaceIncomingPacket(FBitReader& ReplacementPacket) { if (ReplacementPacket.GetPosBits() == 0 || ReplacementPacket.GetBitsLeft() == 0) { IncomingPacket = MoveTemp(ReplacementPacket); } else { // @todo #JohnB: Make this directly adjust and write into IncomingPacket's buffer, instead of copying - very inefficient TArray TempPacketData; TempPacketData.AddUninitialized(IntCastLog(ReplacementPacket.GetBytesLeft())); TempPacketData[TempPacketData.Num()-1] = 0; int64 NewPacketSizeBits = ReplacementPacket.GetBitsLeft(); ReplacementPacket.SerializeBits(TempPacketData.GetData(), NewPacketSizeBits); IncomingPacket.SetData(MoveTemp(TempPacketData), NewPacketSizeBits); } } void PacketHandler::RealignPacket(FBitReader& Packet) { if (Packet.GetPosBits() != 0) { const int32 BitsLeft = IntCastLog(Packet.GetBitsLeft()); if (BitsLeft > 0) { // @todo #JohnB: Based on above - when you optimize above, optimize this too TArray TempPacketData; TempPacketData.AddUninitialized(IntCastLog(Packet.GetBytesLeft())); TempPacketData[TempPacketData.Num()-1] = 0; Packet.SerializeBits(TempPacketData.GetData(), BitsLeft); Packet.SetData(MoveTemp(TempPacketData), BitsLeft); } } } void PacketHandler::SendHandlerPacket(HandlerComponent* InComponent, FBitWriter& Writer, FOutPacketTraits& Traits) { // @todo #JohnB: There is duplication between this function and others, it would be nice to reduce this. // Prevent any cases where a send happens before the handler is ready. check(State != UE::Handler::State::Uninitialized); if (LowLevelSendDel.IsBound()) { bool bEncounteredComponent = false; for (int32 i=0; i 0) { FPacketAudit::AddStage(TEXT("PostPacketHandler"), Writer); // Add a termination bit, the same as the UNetConnection code does, if appropriate Writer.WriteBit(1); if (ReliabilityComponent.IsValid()) { PRAGMA_DISABLE_DEPRECATION_WARNINGS // Let the reliability handler know about all processed packets, so it can record them for resending if needed ReliabilityComponent->QueueHandlerPacketForResending(InComponent, Writer.GetData(), IntCastLog(Writer.GetNumBits()), Traits); PRAGMA_ENABLE_DEPRECATION_WARNINGS } // Now finish off with a raw send (as we don't want to go through the PacketHandler chain again) bool bOldRawSend = bRawSend; bRawSend = true; LowLevelSendDel.ExecuteIfBound(Writer.GetData(), IntCastLog(Writer.GetNumBits()), Traits); bRawSend = bOldRawSend; } } else { LowLevelFatalError(TEXT("Called SendHandlerPacket when no LowLevelSend delegate is bound")); } } void PacketHandler::SetState(UE::Handler::State InState) { if (InState == State) { LowLevelFatalError(TEXT("Set new Packet Processor State to the state it is currently in.")); } else { State = InState; } } void PacketHandler::UpdateInitialState() { if (State == UE::Handler::State::Uninitialized) { if (HandlerComponents.Num() > 0) { InitializeComponents(); } else { HandlerInitialized(); } } } void PacketHandler::HandlerInitialized() { // Quickly verify that, if reliability is required, that it is enabled if (!ReliabilityComponent.IsValid()) { for(const TSharedPtr& Component : HandlerComponents) { if (Component.IsValid() && Component->RequiresReliability()) { // Don't allow this to be missed in shipping - but allow it during development, // as this is valid when developing new HandlerComponent's #if UE_BUILD_SHIPPING UE_LOG(PacketHandlerLog, Fatal, TEXT("Some HandlerComponents require bEnableReliability!!!")); #else UE_LOG(PacketHandlerLog, Warning, TEXT("Some HandlerComponents require bEnableReliability!!!")); #endif break; } } } // If any buffered packets, add to queue for (int32 i=0; i=0; --i) { HandlerComponent& CurComponent = *HandlerComponents[i]; if (!CurComponent.IsInitialized()) { bAllInitialized = false; } if (bEncounteredComponent) { // If the initialized component required a handshake, pass on notification to the next handshaking component // (components closer to the Socket, perform their handshake first) if (bBeganHandshaking && !CurComponent.IsInitialized() && InComponent->RequiresHandshake() && !bPassedHandshakeNotify && CurComponent.RequiresHandshake()) { CurComponent.NotifyHandshakeBegin(); bPassedHandshakeNotify = true; } } else { bEncounteredComponent = &CurComponent == InComponent; } } if (bAllInitialized) { HandlerInitialized(); } } } bool PacketHandler::DoesAnyProfileHaveComponent(const FString& InComponentName) { TArray ProfileSectionNames; if (GConfig->GetPerObjectConfigSections(GEngineIni, TEXT("PacketHandlerProfileConfig"), ProfileSectionNames)) { for (const FString& CurProfileSection : ProfileSectionNames) { FName CurNetDriver(*CurProfileSection.Left(CurProfileSection.Find(TEXT(" ")))); if (DoesProfileHaveComponent(CurNetDriver, InComponentName)) { return true; } } } return false; } bool PacketHandler::DoesProfileHaveComponent(const FName InNetDriverName, const FString& InComponentName) { TArray Components; FString DriverProfileCategory = FString::Printf(TEXT("%s PacketHandlerProfileConfig"), *InNetDriverName.GetPlainNameString()); GConfig->GetArray(*DriverProfileCategory, TEXT("Components"), Components, GEngineIni); for (const FString& Component : Components) { if (Component.Contains(InComponentName, ESearchCase::CaseSensitive)) { return true; } } return false; } BufferedPacket* PacketHandler::GetQueuedPacket() { BufferedPacket* QueuedPacket = nullptr; QueuedPackets.Dequeue(QueuedPacket); return QueuedPacket; } BufferedPacket* PacketHandler::GetQueuedRawPacket() { BufferedPacket* QueuedPacket = nullptr; QueuedRawPackets.Dequeue(QueuedPacket); return QueuedPacket; } BufferedPacket* PacketHandler::GetQueuedConnectionlessPacket() { BufferedPacket* QueuedConnectionlessPacket = nullptr; QueuedConnectionlessPackets.Dequeue(QueuedConnectionlessPacket); return QueuedConnectionlessPacket; } int32 PacketHandler::GetTotalReservedPacketBits() { int32 ReturnVal = 0; uint32 CurMaxOutgoingBits = MaxPacketBits; for (int32 i=HandlerComponents.Num()-1; i>=0; i--) { HandlerComponent* CurComponent = HandlerComponents[i].Get(); int32 CurReservedBits = CurComponent->GetReservedPacketBits(); // Specifying the reserved packet bits is mandatory, even if zero (as accidentally forgetting, leads to hard to trace issues). if (CurReservedBits == -1) { LowLevelFatalError(TEXT("Handler returned invalid 'GetReservedPacketBits' value.")); continue; } // Set the maximum Outgoing packet size for the HandlerComponent CurComponent->MaxOutgoingBits = CurMaxOutgoingBits; CurMaxOutgoingBits -= CurReservedBits; ReturnVal += CurReservedBits; } // Reserve space for the termination bit if (HandlerComponents.Num() > 0) { ReturnVal++; } return ReturnVal; } /** * HandlerComponent */ HandlerComponent::HandlerComponent() : Handler(nullptr) , State(UE::Handler::Component::State::UnInitialized) , MaxOutgoingBits(0) , bRequiresHandshake(false) , bRequiresReliability(false) , bActive(false) , bInitialized(false) { } HandlerComponent::HandlerComponent(FName InName) : Handler(nullptr) , State(UE::Handler::Component::State::UnInitialized) , MaxOutgoingBits(0) , bRequiresHandshake(false) , bRequiresReliability(false) , bActive(false) , bInitialized(false) , Name(InName) { } HandlerComponent::~HandlerComponent() = default; bool HandlerComponent::IsActive() const { return bActive; } void HandlerComponent::SetActive(bool Active) { bActive = Active; } void HandlerComponent::SetState(UE::Handler::Component::State InState) { State = InState; } void HandlerComponent::Initialized() { bInitialized = true; Handler->HandlerComponentInitialized(this); } bool HandlerComponent::IsInitialized() const { return bInitialized; } /** * FPacketHandlerComponentModuleInterface */ void FPacketHandlerComponentModuleInterface::StartupModule() { FPacketAudit::Init(); } void FPacketHandlerComponentModuleInterface::ShutdownModule() { FPacketAudit::Destruct(); }