본문 바로가기

Study/Unreal Engine

언리얼 엔진과 네트워크

언리얼 엔진의 네트워크 시스템을 공부하면서, 언리얼 엔진은 과연 TCP일까 UDP일까 문득 궁금해졌다.

찾아보며 다음과 같은 내용들을 찾았다.

 

우선 언리얼의 NetDriver.h의 내용은 다음과 같다.

// Copyright Epic Games, Inc. All Rights Reserved.

//
// Base class of a network driver attached to an active or pending level.
#pragma once

#include "CoreMinimal.h"
#include "Engine/EngineBaseTypes.h"
#include "Engine/EngineTypes.h"
#include "Net/NetworkMetricsDatabase.h"
#include "HAL/IConsoleManager.h"
#include "Math/RandomStream.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/Object.h"
#include "Misc/NetworkGuid.h"
#include "UObject/CoreNet.h"
#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#include "GameFramework/WorldSettings.h"
#include "PacketHandler.h"
#endif
#include "Channel.h"
#include "Net/Core/Misc/DDoSDetection.h"
#include "IPAddress.h"
#include "Net/NetAnalyticsTypes.h"
#include "Net/NetConnectionIdHandler.h"
#include "HAL/LowLevelMemTracker.h"
#if UE_WITH_IRIS
#include "Templates/PimplPtr.h"
#endif
#include "NetDriver.generated.h"

/**
 *****************************************************************************************
 * NetDrivers, NetConnections, and Channels
 *****************************************************************************************
 *
 *
 * UNetDrivers are responsible for managing sets of UNetConnections, and data that can be shared between them.
 * There is typically a relatively small number of UNetDrivers for a given game. These may include:
 *	- The Game NetDriver, responsible for standard game network traffic.
 *	- The Demo NetDriver, responsible for recording or playing back previously recorded game data. This is how Replays work.
 *	- The Beacon NetDriver, responsible for network traffic that falls outside of "normal" gameplay traffic.
 *
 * Custom NetDrivers can also be implemented by games or applications and used.
 * NetConnections represent individual clients that are connected to a game (or more generally, to a NetDriver).
 *
 * End point data isn't directly handled by NetConnections. Instead NetConnections will route data to Channels.
 * Every NetConnection will have its own set of channels.
 *
 * Common types of channels:
 *
 *	- A Control Channel is used to send information regarding state of a connection (whether or not the connection should close, etc.)
 *	- A Voice Channel may be used to send voice data between client and server.
 *	- A Unique Actor Channel will exist for every Actor replicated from the server to the client.
 *
 * Custom Channels can also be created and used for specialized purposes (although, this isn't very common).
 *
 *
 *****************************************************************************************
 * Game Net Drivers, Net Connections, and Channels
 *****************************************************************************************
 *
 *
 * Under normal circumstances, there will exist only a single NetDriver (created on Client and Server) for "standard" game traffic and connections.
 *
 * The Server NetDriver will maintain a list of NetConnections, each representing a player that is in the game. It is responsible for
 * replicating Actor Data.
 *
 * Client NetDrivers will have a single NetConnection representing the connection to the Server.
 *
 * On both Server and Client, the NetDriver is responsible for receiving Packets from the network and passing those to the appropriate
 * NetConnection (and establishing new NetConnections when necessary).
 *
 *
 *****************************************************************************************
 *****************************************************************************************
 *****************************************************************************************
 * Initiating Connections / Handshaking Flow.
 *****************************************************************************************
 *****************************************************************************************
 *****************************************************************************************
 *
 *
 * UIpNetDriver and UIpConnection (or derived classes) are the engine defaults for almost every platform, and everything below
 * describes how they establish and manage connections. These processes can differ between implementations of NetDriver, however.
 *
 * Both Server and Clients will have have their own NetDrivers, and all UE Replicated Game traffic will be sent by or Received
 * from the IpNetDriver. This traffic also includes logic for establishing connections, and re-establishing connections when
 * something goes wrong.
 *
 * Handshaking is split across a couple of different places: NetDriver, PendingNetGame, World, PacketHandlers, and maybe others.
 * The split is due to having separate needs, things such as: determining whether or not an incoming connection is sending data in "UE-Protocol",
 * determining whether or not an address appears to be malicious, whether or not a given client has the correct version of a game, etc.
 *
 *
 *****************************************************************************************
 * Startup and Handshaking
 *****************************************************************************************
 *
 *
 * Whenenever a server Loads a map (via UEngine::LoadMap), we will make a call into UWorld::Listen.
 * That code is responsible for creating the main Game Net Driver, parsing out settings, and calling UNetDriver::InitListen.
 * Ultimately, that code will be responsible for figuring out what how exactly we listen for client connections.
 * For example, in IpNetDriver, that is where we determine the IP / Port we will bind to by calls to our configured Socket Subsystem
 * (see ISocketSubsystem::GetLocalBindAddresses and ISocketSubsystem::BindNextPort).
 *
 * Once the server is listening, it's ready to start accepting client connections.
 *
 * Whenever a client wants to Join a server, they will first establish a new UPendingNetGame in UEngine::Browse with the server's IP.
 * UPendingNetGame::Initialize and UPendingNetGame::InitNetDriver are responsible for initializing settings and setting up the NetDriver respectively.
 * Clients will immediately setup a UNetConnection for the server as a part of this initialization, and will start sending data to the server on that connection,
 * initiating the handshaking process.
 *
 * On both Clients and Server, UNetDriver::TickDispatch is typically responsible for receiving network data.
 * Typically, when we receive a packet, we inspect its address and see whether or not it's from a connection we already know about.
 * We determine whether or not we've established a connection for a given source address by simply keeping a map from FInternetAddr to UNetConnection.
 *
 * If a packet is from a connection that's already established, we pass the packet along to the connection via UNetConnection::ReceivedRawPacket.
 * If a packet is not from a connection that's already established, we treat is as "connectionless" and begin the handshaking process.
 *
 * See StatelessConnectionHandlerComponent.cpp for details on how this handshaking works.
 *
 *
 *****************************************************************************************
 * UWorld / UPendingNetGame / AGameModeBase Startup and Handshaking
 *****************************************************************************************
 *
 *
 * After the UNetDriver and UNetConnection have completed their handshaking process on Client and Server,
 * UPendingNetGame::SendInitialJoin will be called on the Client to kick off game level handshaking.
 *
 * Game Level Handshaking is done through a more structured and involved set of FNetControlMessages.
 * The full set of control messages can be found in DataChannel.h.
 *
 * Most of the work for handling these control messages are done either in UWorld::NotifyControlMessage,
 * and UPendingNetGame::NotifyControlMessage. Briefly, the flow looks like this:
 *
 * Client's UPendingNetGame::SendInitialJoin sends NMT_Hello.
 *
 * Server's UWorld::NotifyControlMessage receives NMT_Hello, sends NMT_Challenge.
 *
 * Client's UPendingNetGame::NotifyControlMessage receives NMT_Challenge, and sends back data in NMT_Login.
 *
 * Server's UWorld::NotifyControlMessage receives NMT_Login, verifies challenge data, and then calls AGameModeBase::PreLogin.
 * If PreLogin doesn't report any errors, Server calls UWorld::WelcomePlayer, which call AGameModeBase::GameWelcomePlayer,
 * and send NMT_Welcome with map information.
 *
 * Client's UPendingNetGame::NotifyControlMessage receives NMT_Welcome, reads the map info (so it can start loading later),
 * and sends an NMT_NetSpeed message with the configured Net Speed of the client.
 *
 * Server's UWorld::NotifyControlMessage receives NMT_NetSpeed, and adjusts the connections Net Speed appropriately.
 *
 * At this point, the handshaking is considered to be complete, and the player is fully connected to the game.
 * Depending on how long it takes to load the map, the client could still receive some non-handshake control messages
 * on UPendingNetGame before control transitions to UWorld.
 *
 * There are also additional steps for handling Encryption when desired.
 *
 *
 *****************************************************************************************
 * Reestablishing Lost Connections
 *****************************************************************************************
 *
 *
 * Throughout the course of a game, it's possible for connections to be lost for a number of reasons.
 * Internet could drop out, users could switch from LTE to WIFI, they could leave a game, etc.
 *
 * If the server initiated one of these disconnects, or is otherwise aware of it (due to a timeout or error),
 * then the disconnect will be handled by closing the UNetConnection and notifying the game.
 * At that point, it's up to a game to decide whether or not they support Join In Progress or Rejoins.
 * If the game does support it, we will completely restart the handshaking flow as above.
 *
 * If something just briefly interrupts the client's connection, but the server is never made aware,
 * then the engine / game will typically recover automatically (albeit with some packet loss / lag spike).
 *
 * However, if the Client's IP Address or Port change for any reason, but the server isn't aware of this,
 * then we will begin a recovery process by redoing the low level handshake. In this case, game code
 * will not be alerted.
 *
 * This process is covered in StatlessConnectionHandlerComponent.cpp.
 *
 *
 *****************************************************************************************
 *****************************************************************************************
 *****************************************************************************************
 * Data Transmission
 *****************************************************************************************
 *****************************************************************************************
 *****************************************************************************************
 *
 *
 * Game NetConnections and NetDrivers are generally agnostic to the underlying communication method / technology used.
 * That is is left up to subclasses to decide (classes such as UIpConnection / UIpNetDriver or UWebSocketConnection / UWebSocketNetDriver).
 *
 * Instead, UNetDriver and UNetConnection work with Packets and Bunches.
 *
 * Packets are blobs of data that are sent between pairs of NetConnections on Host and Client.
 * Packets consist of meta data about the packet (such as header information and acknowledgments), and Bunches.
 *
 * Bunches are blobs of data that are sent between pairs of Channels on Host and Client.
 * When a Connection receives a Packet, that packet will be disassembled into individual bunches.
 * Those bunches are then passed along to individual Channels to be processed further.
 *
 * A Packet may contain no bunches, a single bunch, or multiple bunches.
 * Because size limits for bunches may be larger than the size limits of a single packet, UE supports
 * the notion of partial bunches.
 *
 * When a bunch is too large, before transmission we will slice it into a number of smaller bunches.
 * these bunches will be flagged as PartialInitial, Partial, or PartialFinal. Using this information,
 * we can reassemble the bunches on the receiving end.
 *
 *	Example: Client RPC to Server.
 *		- Client makes a call to Server_RPC.
 *		- That request is forwarded (via NetDriver and NetConnection) to the Actor Channel that owns the Actor on which the RPC was called.
 *		- The Actor Channel will serialize the RPC Identifier and parameters into a Bunch. The Bunch will also contain the ID of its Actor Channel.
 *		- The Actor Channel will then request the NetConnection send the Bunch.
 *		- Later, the NetConnection will assemble this (and other) data into a Packet which it will send to the server.
 *		- On the Server, the Packet will be received by the NetDriver.
 *		- The NetDriver will inspect the Address that sent the Packet, and hand the Packet over to the appropriate NetConnection.
 *		- The NetConnection will disassemble the Packet into its Bunches (one by one).
 *		- The NetConnection will use the Channel ID on the bunch to Route the bunch to the corresponding Actor Channel.
 *		- The ActorChannel will them disassemble the bunch, see it contains RPC data, and use the RPC ID and serialized parameters
 *			to call the appropriate function on the Actor.
 *
 *
 *****************************************************************************************
 * Reliability and Retransmission
 *****************************************************************************************
 *
 *
 * UE Networking typically assumes reliability isn't guaranteed by the underlying network protocol.
 * Instead, it implements its own reliability and retransmission of both packets and bunches.
 *
 * When a NetConnection is established, it will establish a Sequence Number for its packets and bunches.
 * These can either be fixed, or randomized (when randomized, the sequence will be sent by the server).
 *
 * The packet number is per NetConnection, incremented for every packet sent, every packet will include its packet number,
 * and we will never retransmit a packet with the same packet number.
 *
 * The bunch number is per Channel, incremented for every **reliable** bunch sent, and every **reliable** bunch will
 * include its bunch number. Unlike packets, though, exact (reliable) bunches may be retransmitted. This means we
 * will resend bunches with the same bunch number.
 *
 * Note, throughout the code what are described above as both bunch numbers and packet numbers are commonly referred to
 * just as sequence numbers. We make the distinction here for clearer understanding.
 *
 *	--- Detecting Incoming Dropped Packets ---
 *
 *
 *	By assigning packet numbers, we can easily detect when incoming packets are lost.
 *	This is done simply by taking the difference between the last successfully received packet number, and the
 *	packet number of the current packet being processed.
 *
 *	Under good conditions, all packets will be received in the order they are sent. This means that the difference will
 *	be +1.
 *
 *	If the difference is greater than 1, that indicates that we missed some packets. We will just
 *	assume that the missing packets were dropped, but consider the current packet to have been successfully received,
 *	and use its number going forward.
 *
 *	If the difference is negative (or 0), that indicates that we either received some packets out of order, or an external
 *	service is trying to resend data to us (remember, the engine will not reuse sequence numbers).
 *
 *	In either case, the engine will typically ignore the missing or invalid packets, and will not send ACKs for them.
 *
 *	We do have methods for "fixing" out of order packets that are received on the same frame.
 *	When enabled, if we detect missing packets (difference > 1), we won't process the current packet immediately.
 *	Instead, it will add it to a queue. The next time we receive a packet successfully (difference == 1), we will
 *	see if the head of our queue is properly ordered. If so, we will process it, otherwise we will continue
 *	receiving packets.
 *
 *	Once we've read all packets that are currently available, we will flush this queue processing any remaining packets.
 *	Anything that's missing at this point will be assumed to have been dropped.
 *
 *	Every packet successfully received will have its packet number sent back to the sender as an acknowledgment (ACK).
 *
 *
 *	--- Detecting Outgoing Dropped Packets ---
 *
 *
 *	As mentioned above, whenever a packet is received successfully the recipient will send back an ACK.
 *	These ACKs will contain the packet numbers of successfully received packets, in sequence order.
 *
 *	Similar to how the recipient tracks the packet number, the sender will track the highest ACKed packet number.
 *
 *	When ACKs are being processed, any ACK below our last received ACK is ignored and any gaps in packet
 *	numbers are considered Not Acknowledged (NAKed).
 *
 *	It is the sender's responsibility to handle these ACKs and NAKs and resend any missing data.
 *	The new data will be added to new outgoing packets (again, we will not resend packets we've already sent,
 *	or reuse packet sequence numbers).
 *
 *
 *	--- Resending Missing Data ---
 *
 *
 *	As mentioned above, packets alone don't contain useful game data. Instead, it's the bunches that comprise them
 *	that have meaningful data.
 *
 *	Bunches can either be marked as Reliable or Unreliable.
 *
 *	The engine will make no attempt at resending unreliable bunches if they are dropped. Therefore, if bunches
 *	are marked unreliable, the game / engine should be able to continue without them, or external retry
 *	mechanisms must be put in place, or the data must be sent redundantly. Therefore, everything below only
 *	applies to reliable bunches.
 *
 *	However, the engine will attempt to resend reliable bunches. Whenever a reliable bunch is sent, it will
 *	be added to a list of un-ACKed reliable bunches. If we receive a NAK for a packet that contained the bunch,
 *	the engine will retransmit an exact copy of that bunch. Note, because bunches may be partial, dropping even
 *	a single partial bunch will result in retransmission of the entire bunch. When all packets containing a bunch
 *	have been ACKed, we will remove it from the list.
 *
 *	Similar to packets, we will compare the bunch number for received reliable bunches to the last successfully
 *	received bunch. If we detect that the difference is negative, we simply ignore the bunch. If the difference
 *	is greater than one, we will assume we missed a bunch. Unlike packet handling, we will not discard this data.
 *	Instead, we will queue the bunch and pause processing of **any** bunches, reliable or unreliable.
 *	Processing will not be resumed until we detect have received the missing bunches, at which point we will process
 *	them, and then start processing our queued bunches.
 *	Any new bunches that are received while waiting for the missing bunches, or while we still have any bunches in our
 *	queue, will be added to the queue instead of being processed immediately.
 *
 */

LLM_DECLARE_TAG_API(NetDriver, ENGINE_API);

class Error;
class FNetGUIDCache;
struct FNetSyncLoadReport;
class FNetworkNotify;
class FNetworkObjectList;
class FObjectReplicator;
class FRepChangedPropertyTracker;
class FRepLayout;
class FReplicationChangelistMgr;
class FVoicePacket;
class StatelessConnectHandlerComponent;
class UNetConnection;
class UReplicationDriver;
struct FNetworkObjectInfo;
class UChannel;
class IAnalyticsProvider;
class FNetAnalyticsAggregator;
class UNetDriver;
class UActorChannel;
class PacketHandler;
struct FReplicatedStaticActorDestructionInfo;

enum class ECreateReplicationChangelistMgrFlags;
enum class EEngineNetworkRuntimeFeatures : uint16;
#if UE_WITH_IRIS
class UReplicationSystem;
class UReplicationBridge;
namespace UE::Net
{
	class FNetObjectGroupHandle;
}
#endif // UE_WITH_IRIS

namespace UE::Net
{
	class FScopedIgnoreStaticActorDestruction
	{
	public:
		FScopedIgnoreStaticActorDestruction();
		~FScopedIgnoreStaticActorDestruction();

		UE_NONCOPYABLE(FScopedIgnoreStaticActorDestruction);

	private:
		bool bCachedValue = false;
	};

	bool ShouldIgnoreStaticActorDestruction();
}

using FConnectionMap = TMap<TSharedRef<const FInternetAddr>, TObjectPtr<UNetConnection>, FDefaultSetAllocator, FInternetAddrConstKeyMapFuncs<TObjectPtr<UNetConnection>>>;

extern ENGINE_API TAutoConsoleVariable<int32> CVarNetAllowEncryption;
extern ENGINE_API int32 GNumSaturatedConnections;
extern ENGINE_API int32 GNumSharedSerializationHit;
extern ENGINE_API int32 GNumSharedSerializationMiss;
extern ENGINE_API int32 GNumReplicateActorCalls;
extern ENGINE_API bool GReplicateActorTimingEnabled;
extern ENGINE_API bool GReceiveRPCTimingEnabled;
extern ENGINE_API double GReplicateActorTimeSeconds;
extern ENGINE_API uint32 GNetOutBytes;
extern ENGINE_API double GReplicationGatherPrioritizeTimeSeconds;
extern ENGINE_API double GServerReplicateActorTimeSeconds;
extern ENGINE_API int32 GNumClientConnections;
extern ENGINE_API int32 GNumClientUpdateLevelVisibility;

namespace UE::Net::Private
{
	/** Allow other internal systems to check this cvar */
	extern int32 SerializeNewActorOverrideLevel;
}

// Delegates

#if !UE_BUILD_SHIPPING
/**
 * Delegate for hooking ProcessRemoteFunction (used by NetcodeUnitTest)
 *
 * @param Actor				The actor the RPC will be called in
 * @param Function			The RPC to call
 * @param Parameters		The parameters data blob
 * @param OutParms			Out parameter information (irrelevant for RPC's)
 * @param Stack				The script stack
 * @param SubObject			The sub-object the RPC is being called in (if applicable)
 * @param bBlockSendRPC		Whether or not to block sending of the RPC (defaults to false)
 */
DECLARE_DELEGATE_SevenParams(FOnSendRPC, AActor* /*Actor*/, UFunction* /*Function*/, void* /*Parameters*/,
									FOutParmRec* /*OutParms*/, FFrame* /*Stack*/, UObject* /*SubObject*/, bool& /*bBlockSendRPC*/);

/**
 * Delegate for hooking ShouldSkipRepNotifies
 */
DECLARE_DELEGATE_RetVal(bool, FShouldSkipRepNotifies);

#endif

/**
 * The structure to pass to the OnConsiderListUpdate delegate 
 * 
 * @param DeltaSeconds     Time between the frames
 * @param Connection       NetConnection to process
 * @param bCPUSaturated    Not used by the engine at the moment but kept for compatibility
 */
struct ENGINE_API ConsiderListUpdateParams
{
	float DeltaSeconds = 0;
	UNetConnection* Connection = nullptr;
	bool bCPUSaturated = false;
};

DECLARE_DELEGATE_ThreeParams(FOnConsiderListUpdate, const ConsiderListUpdateParams& UpdateParams, int32& OutUpdated, const TArray<FNetworkObjectInfo*>& ConsiderList);

//
// Whether to support net lag and packet loss testing.
//
#define DO_ENABLE_NET_TEST !(UE_BUILD_SHIPPING)

#ifndef NET_DEBUG_RELEVANT_ACTORS
#define NET_DEBUG_RELEVANT_ACTORS !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
#endif 

/** Holds the packet simulation settings in one place */
USTRUCT()
struct FPacketSimulationSettings
{
	GENERATED_BODY()

	/**
	 * When set, will cause calls to FlushNet to drop packets.
	 * Value is treated as % of packets dropped (i.e. 0 = None, 100 = All).
	 * No general pattern / ordering is guaranteed.
	 * Clamped between 0 and 100.
	 *
	 * Works with all other settings.
	 */
	UPROPERTY(EditAnywhere, Category="Simulation Settings")
	int32	PktLoss = 0;

	/**
	* Sets the maximum size of packets in bytes that will be dropped
	* according to the PktLoss setting. Default is INT_MAX.
	*
	* Works with all other settings.
	*/
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktLossMaxSize = 0;

	/**
	* Sets the minimum size of packets in bytes that will be dropped
	* according to the PktLoss setting. Default is 0.
	*
	* Works with all other settings.
	*/
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktLossMinSize = 0;

	/**
	 * When set, will cause calls to FlushNet to change ordering of packets at random.
	 * Value is treated as a bool (i.e. 0 = False, anything else = True).
	 * This works by randomly selecting packets to be delayed until a subsequent call to FlushNet.
	 *
	 * Takes precedence over PktDup and PktLag.
	 */
	UPROPERTY(EditAnywhere, Category="Simulation Settings")
	int32	PktOrder = 0;

	/**
	 * When set, will cause calls to FlushNet to duplicate packets.
	 * Value is treated as % of packets duplicated (i.e. 0 = None, 100 = All).
	 * No general pattern / ordering is guaranteed.
	 * Clamped between 0 and 100.
	 *
	 * Cannot be used with PktOrder or PktLag.
	 */
	UPROPERTY(EditAnywhere, Category="Simulation Settings")
	int32	PktDup = 0;
	
	/**
	 * When set, will cause calls to FlushNet to delay packets.
	 * Value is treated as millisecond lag.
	 *
	 * Cannot be used with PktOrder.
	 */
	UPROPERTY(EditAnywhere, Category="Simulation Settings")
	int32	PktLag = 0;
	
	/**
	 * When set, will cause PktLag to use variable lag instead of constant.
	 * Value is treated as millisecond lag range (e.g. -GivenVariance <= 0 <= GivenVariance).
	 *
	 * Can only be used when PktLag is enabled.
	 */
	UPROPERTY(EditAnywhere, Category="Simulation Settings")
	int32	PktLagVariance = 0;

	/**
	 * If set lag values will randomly fluctuate between Min and Max.
	 * Ignored if PktLag value is set
	 */
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktLagMin = 0;
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktLagMax = 0;

	/**
	 * Set a value to add a minimum delay in milliseconds to incoming
	 * packets before they are processed.
	 */
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktIncomingLagMin = 0;
	
	/**
	 * The maximum delay in milliseconds to add to incoming
	 * packets before they are processed.
	 */
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktIncomingLagMax = 0;

	/**
	 * The ratio of incoming packets that will be dropped
	 * to simulate packet loss
	 */
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktIncomingLoss = 0;

	/**
	 * Causes sent packets to have a variable latency that fluctuates from [PktLagMin] to [PktLagMin+PktJitter]
	 * Note that this will cause packet loss on the receiving end.
	 */
	UPROPERTY(EditAnywhere, Category = "Simulation Settings")
	int32	PktJitter = 0;

	/** reads in settings from the .ini file 
	 * @note: overwrites all previous settings
	 */
	ENGINE_API void LoadConfig(const TCHAR* OptionalQualifier = nullptr);
	
	/** 
	 * Load a preconfigured emulation profile from the .ini
	 * Returns true if the given profile existed
	 */
	ENGINE_API bool LoadEmulationProfile(const TCHAR* ProfileName);

	/**
	 * Force new emulation settings and ignore config or cmdline values
	 */
	ENGINE_API void ApplySettings(const FPacketSimulationSettings& NewSettings);

	/**
	 * Ensure that settings have proper values
	 */
	ENGINE_API void ValidateSettings();
	ENGINE_API void ResetSettings();

	/**
	* Tells if a packet fits the size settings to potentially be dropped
	*/
	bool ShouldDropPacketOfSize(int32 NumBits) const
	{
		const bool bIsBigEnough = NumBits > PktLossMinSize * 8;
		const bool bIsSmallEnough = PktLossMaxSize == 0 || NumBits < PktLossMaxSize * 8;
		return bIsBigEnough && bIsSmallEnough;
	}

	/**
	 * Reads the settings from a string: command line or an exec
	 *
	 * @param Stream the string to read the settings from
	 * @Param OptionalQualifier: optional string to prepend to Pkt* settings. E.g, "GameNetDriverPktLoss=50"
	 */
	ENGINE_API bool ParseSettings(const TCHAR* Stream, const TCHAR* OptionalQualifier=nullptr);

	ENGINE_API bool ParseHelper(const TCHAR* Cmd, const TCHAR* Name, int32& Value, const TCHAR* OptionalQualifier);

	ENGINE_API bool ConfigHelperInt(const TCHAR* Name, int32& Value, const TCHAR* OptionalQualifier);
	ENGINE_API bool ConfigHelperBool(const TCHAR* Name, bool& Value, const TCHAR* OptionalQualifier);
};

struct FActorDestructionInfo
{
public:
	FActorDestructionInfo()
		: Reason(EChannelCloseReason::Destroyed)
		, bIgnoreDistanceCulling(false) 
	{}

	TWeakObjectPtr<ULevel> Level; 
	TWeakObjectPtr<UObject> ObjOuter;
	FVector DestroyedPosition;
	FNetworkGUID NetGUID;
	FString PathName;
	FName StreamingLevelName;
	EChannelCloseReason Reason;

	/** When true the destruction info data will be sent even if the viewers are not close to the actor */
	bool bIgnoreDistanceCulling;

	void CountBytes(FArchive& Ar)
	{
		PathName.CountBytes(Ar);
	}
};

/** Used to configure the replication system default values */
USTRUCT()
struct FNetDriverReplicationSystemConfig
{
	GENERATED_USTRUCT_BODY()

	/** Override the max object count when running as a client. If 0 use the default system value. */
	UPROPERTY()
	uint32 MaxReplicatedObjectClientCount = 0;

	/** Override the max object count when running as a server. If 0 use the default system value. */
	UPROPERTY()
	uint32 MaxReplicatedObjectServerCount = 0;

	/** Override the number of pre-allocated objects when running as a client. */
	UPROPERTY()
	uint32 PreAllocatedReplicatedObjectClientCount = 0;

	/** Override the number of pre-allocated objects when running as a server. */
	UPROPERTY()
	uint32 PreAllocatedReplicatedObjectServerCount = 0;

	/** Override the number of pre-allocated objects in FReplicationWriter on the client. */
	UPROPERTY()
	uint32 MaxReplicatedWriterObjectClientCount = 0;
	
	/** Override the max compressed object count. If 0 use the default system value. */
	UPROPERTY()
	uint32 MaxDeltaCompressedObjectCount = 0;
	
	/** Override the max group count. If 0 use the default system value. */
	UPROPERTY()
	uint32 MaxNetObjectGroupCount = 0;
};

//
// Priority sortable list.
//
struct ENGINE_API FActorPriority
{
	int32						Priority;	// Update priority, higher = more important.
	
	FNetworkObjectInfo*			ActorInfo;	// Actor info.
	UActorChannel*		        Channel;	// Actor channel.

	FActorDestructionInfo *	DestructionInfo;	// Destroy an actor

	FActorPriority() : 
		Priority(0), ActorInfo(NULL), Channel(NULL), DestructionInfo(NULL)
	{}

	FActorPriority(class UNetConnection* InConnection, UActorChannel* InChannel, FNetworkObjectInfo* InActorInfo, const TArray<struct FNetViewer>& Viewers, bool bLowBandwidth);
	FActorPriority(class UNetConnection* InConnection, FActorDestructionInfo * DestructInfo, const TArray<struct FNetViewer>& Viewers );
};

struct FCompareFActorPriority
{
	FORCEINLINE bool operator()( const FActorPriority& A, const FActorPriority& B ) const
	{
		return B.Priority < A.Priority;
	}
};

/** Used to specify properties of a channel type */
USTRUCT()
struct FChannelDefinition
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	FName ChannelName;			// Channel type identifier

	UPROPERTY()
	FName ClassName;			// UClass name used to create the UChannel

	UPROPERTY()
	TObjectPtr<UClass> ChannelClass;		// UClass used to create the UChannel

	UPROPERTY()
	int32 StaticChannelIndex;	// Channel always uses this index, INDEX_NONE if dynamically chosen

	UPROPERTY()
	uint8 bTickOnCreate : 1;			// Whether to immediately begin ticking the channel after creation

	UPROPERTY()
	uint8 bServerOpen : 1;			// Channel opened by the server

	UPROPERTY()
	uint8 bClientOpen : 1;			// Channel opened by the client

	UPROPERTY()
	uint8 bInitialServer : 1;		// Channel created on server when connection is established

	UPROPERTY()
	uint8 bInitialClient : 1;		// Channel created on client before connecting

	FChannelDefinition() : 
		ChannelName(NAME_None),
		ClassName(NAME_None),
		ChannelClass(nullptr),
		StaticChannelIndex(INDEX_NONE),
		bTickOnCreate(false),
		bServerOpen(false),
		bClientOpen(false),
		bInitialServer(false),
		bInitialClient(false)
	{
	}
};

/**
 * Information about disconnected client NetConnection's
 */
struct FDisconnectedClient
{
	/** The address of the client */
	TSharedRef<const FInternetAddr>	Address;

	/** The time at which the client disconnected  */
	double						DisconnectTime;

	FDisconnectedClient(TSharedRef<const FInternetAddr>& InAddress, double InDisconnectTime)
		: Address(InAddress)
		, DisconnectTime(InDisconnectTime)
	{
	}
};

enum class EProcessRemoteFunctionFlags : uint32
{
	None = 0,
	ReplicatedActor = 1 << 0,	//! The owning actor has been replicated at least once
								//! while processing the remote function.
};
ENUM_CLASS_FLAGS(EProcessRemoteFunctionFlags);

/** A metrics listener that writes a metric to the 'Replication' CSV category. */
UCLASS()
class ENGINE_API UNetworkMetricsCSV_Replication : public UNetworkMetricsCSV
{
	GENERATED_BODY()

public:
	UNetworkMetricsCSV_Replication()
	{
		SetCategory("Replication");
	}

	virtual ~UNetworkMetricsCSV_Replication() = default;
};

UCLASS(Abstract, customConstructor, transient, MinimalAPI, config=Engine)
class UNetDriver : public UObject, public FExec
{
	GENERATED_UCLASS_BODY()

protected:

	ENGINE_API void InternalProcessRemoteFunction(
		class AActor* Actor,
		class UObject* SubObject,
		class UNetConnection* Connection,
		class UFunction* Function,
		void* Parms,
		FOutParmRec* OutParms,
		FFrame* Stack,
		bool bIsServer);

private:

	void InternalProcessRemoteFunctionPrivate(
		class AActor* Actor,
		class UObject* SubObject,
		class UNetConnection* Connection,
		class UFunction* Function,
		void* Parms,
		FOutParmRec* OutParms,
		FFrame* Stack,
		const bool bIsServer,
		EProcessRemoteFunctionFlags& RemoteFunctionFlags);

public:

	/** Destructor */
	ENGINE_API virtual ~UNetDriver();

	/** Used to specify the class to use for connections */
	UPROPERTY(Config)
	FString NetConnectionClassName;

	UPROPERTY(Config)
	FString ReplicationDriverClassName;

	/** Used to specify the class to use for ReplicationBridge */
	UPROPERTY(Config)
	FString ReplicationBridgeClassName;
	
	/** Can be used to configure settings for the ReplicationSystem */
	UPROPERTY(Config)
	FNetDriverReplicationSystemConfig ReplicationSystemConfig;

	/** @todo document */
	UPROPERTY(Config)
	int32 MaxDownloadSize;

	/** @todo document */
	UPROPERTY(Config)
	uint32 bClampListenServerTickRate:1;

	/** 
	* Limit the tick rate of the engine when running in dedicated server mode. 
	* @see UGameEngine::GetMaxTickRate 
	*/
	UE_DEPRECATED(5.3, "Variable will be made private. Use GetNetServerMaxTickRate and SetNetServerMaxTickRate instead.")
	UPROPERTY(Config)
	int32 NetServerMaxTickRate;

	/** The current max tick rate of the engine when running in dedicated server mode. */
	int32 GetNetServerMaxTickRate() const 
	{ 
		PRAGMA_DISABLE_DEPRECATION_WARNINGS
		return NetServerMaxTickRate;
		PRAGMA_ENABLE_DEPRECATION_WARNINGS
	}

	/** Override the configured server tick rate. Value is in ticks per second. */
	ENGINE_API void SetNetServerMaxTickRate(int32 InServerMaxTickRate);

	/** 
	* Delegate triggered when SetNetServerMaxTickRate is called and causes a change to the current max tick rate.
	* @param UNetDriver The netdriver that changed max tick rate.
	* @param int32 The new value of NetServerMaxTickRate
	* @param int32 The old value of NetServerMaxTickRate 
	*/
	DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnNetServerMaxTickRateChanged, UNetDriver*, int32, int32);
	FOnNetServerMaxTickRateChanged OnNetServerMaxTickRateChanged;

	/** Limit tick rate of replication to allow very high frame rates to still replicate data. A value less or equal to zero means use the engine tick rate. A value greater than zero will clamp the net tick rate to this value.  */
	UPROPERTY(Config)
	int32 MaxNetTickRate;

	/** @todo document */
	UPROPERTY(Config)
	int32 MaxInternetClientRate;

	/** @todo document */
	UPROPERTY(Config)
	int32 MaxClientRate;

	/** Amount of time a server will wait before traveling to next map, gives clients time to receive final RPCs on existing level @see NextSwitchCountdown */
	UPROPERTY(Config)
	float ServerTravelPause;

	/** @todo document */
	UPROPERTY(Config)
	float SpawnPrioritySeconds;

	/** @todo document */
	UPROPERTY(Config)
	float RelevantTimeout;

	/** @todo document */
	UPROPERTY(Config)
	float KeepAliveTime;

	/** Amount of time to wait for a new net connection to be established before destroying the connection */
	UPROPERTY(Config)
	float InitialConnectTimeout;

	/** 
	 * Amount of time to wait before considering an established connection timed out.  
	 * Typically shorter than the time to wait on a new connection because this connection
	 * should already have been setup and any interruption should be trapped quicker.
	 */
	UPROPERTY(Config)
	float ConnectionTimeout;

	/**
	* A multiplier that is applied to the above values when we are running with unoptimized builds (debug)
	* or data (uncooked). This allows us to retain normal timeout behavior while debugging without resorting
	* to the nuclear 'notimeouts' option or bumping the values above. If ==0 multiplier = 1
	*/
	UPROPERTY(Config)
	float TimeoutMultiplierForUnoptimizedBuilds;

	/** Connection to the server (this net driver is a client) */
	UPROPERTY()
	TObjectPtr<class UNetConnection> ServerConnection;

	/** Array of connections to clients (this net driver is a host) - unsorted, and ordering changes depending on actor replication */
	UPROPERTY()
	TArray<TObjectPtr<UNetConnection>> ClientConnections;

	/**
	 * Map of IP's to NetConnection's - for fast lookup, particularly under DDoS.
	 * Only valid IP's mapped (e.g. excludes DemoNetConnection). Recently disconnected clients remain mapped as nullptr connections.
	 */
	FConnectionMap MappedClientConnections;

	/** Tracks recently disconnected client IP's, and the disconnect time - so they can be cleaned from MappedClientConnections */
	TArray<FDisconnectedClient> RecentlyDisconnectedClients;

	/** The amount of time, in seconds, that recently disconnected clients should be tracked */
	UPROPERTY(Config)
	int32 RecentlyDisconnectedTrackingTime;


	/** Serverside PacketHandler for managing connectionless packets */
	TUniquePtr<PacketHandler> ConnectionlessHandler;

	/** Reference to the PacketHandler component, for managing stateless connection handshakes */
	TWeakPtr<StatelessConnectHandlerComponent> StatelessConnectComponent;

	/** The analytics provider used by the packet handler */
	TSharedPtr<IAnalyticsProvider> AnalyticsProvider;

	/** Special analytics aggregator tied to AnalyticsProvider - combines analytics from all NetConnections/PacketHandlers, in one event */
	TSharedPtr<FNetAnalyticsAggregator> AnalyticsAggregator;

	/** World this net driver is associated with */
	UPROPERTY()
	TObjectPtr<class UWorld> World;

	UPROPERTY()
	TObjectPtr<class UPackage> WorldPackage;

	/** @todo document */
	TSharedPtr< class FNetGUIDCache > GuidCache;

	TSharedPtr< class FClassNetCacheMgr >	NetCache;

	/** The loaded UClass of the net connection type to use */
	UPROPERTY()
	TObjectPtr<UClass> NetConnectionClass;

	UPROPERTY()
	TObjectPtr<UClass> ReplicationDriverClass;

	UPROPERTY(transient)
	TObjectPtr<UClass> ReplicationBridgeClass;

	/** @todo document */
	FProperty* RoleProperty;
	
	/** @todo document */
	FProperty* RemoteRoleProperty;

	/** Used to specify the net driver to filter actors with (NAME_None || NAME_GameNetDriver is the default net driver) */
	UPROPERTY(Config)
	FName NetDriverName;

	/** Used to specify available channel types and their associated UClass */
	UPROPERTY(Config)
	TArray<FChannelDefinition> ChannelDefinitions;

	/** Used for faster lookup of channel definitions by name. */
	UPROPERTY()
	TMap<FName, FChannelDefinition> ChannelDefinitionMap;

	/** @return true if the specified channel definition exists. */
	FORCEINLINE bool IsKnownChannelName(const FName& ChName) const
	{
		return ChannelDefinitionMap.Contains(ChName);
	}

	/** Creates a channel of each type that is set as bInitialClient. */
	ENGINE_API void CreateInitialClientChannels();

	/** Creates a channel of each type that is set as bIniitalServer for the given connection. */
	ENGINE_API void CreateInitialServerChannels(UNetConnection* ClientConnection);

private:

	/** List of channels that were previously used and can be used again */
	UPROPERTY()
	TArray<TObjectPtr<UChannel>> ActorChannelPool;

	/** Name of net driver definition used to create this driver */
	FName NetDriverDefinition;

	/** Cached copy of MaxChannelsOverride from the net driver definition to avoid extra lookups */
	int32 MaxChannelsOverride;

	/** A metrics database that holds statistics calcluated by the networking system. */
	UPROPERTY()
	TObjectPtr<UNetworkMetricsDatabase> NetworkMetricsDatabase;

	/** A cache of UNetworkMetricsBaseListener sub-class instances provided by the *.ini file (one instance per sub-class). */
	UPROPERTY()
	TMap<FName, TObjectPtr<UNetworkMetricsBaseListener>> NetworkMetricsListeners;

	/** Register each metric used by the networking system. */
	void SetupNetworkMetrics();

	/** Register metric listeners provided by the *.ini file. */
	void SetupNetworkMetricsListeners();

	/** Create an instance of UNetworkMetricsStats that is associated with a given Stat and cached with other listeners in NetworkMetricsListeners. */
	void RegisterStatsListener(const FName MetricName, const FName StatName);

	/** Reset any network metrics database values at the beginning of a frame. */
	void ResetNetworkMetrics();

public:
	/** Get the value of MaxChannelsOverride cached from the net driver definition */
	int32 GetMaxChannelsOverride() const { return MaxChannelsOverride; }

	/** Creates a new channel of the specified type name. If the type is pooled, it will return a pre-created channel */
	UChannel* GetOrCreateChannelByName(const FName& ChName);

	/** If the channel's type is pooled, this will add the channel to the pool. Otherwise, nothing will happen. */
	void ReleaseToChannelPool(UChannel* Channel);

	/** Change the NetDriver's NetDriverName. This will also reinit packet simulation settings so that settings can be qualified to a specific driver. */
	void SetNetDriverName(FName NewNetDriverNamed);

	/** Set the NetDriver's NetDriverDefintion. */
	void SetNetDriverDefinition(FName NewNetDriverDefinition);

	/** Get the NetDriver's NetDriverDefintion. */
	FName GetNetDriverDefinition() const { return NetDriverDefinition; }

	/** Callback after the engine created the NetDriver and set our name for the first time */
	void PostCreation(bool bInitializeWithIris);


	void InitPacketSimulationSettings();

	/** Returns true during the duration of a packet loss burst triggered by the net.pktlossburst command. */
#if DO_ENABLE_NET_TEST
	bool IsSimulatingPacketLossBurst() const;
#endif

	/** Interface for communication network state to others (ie World usually, but anything that implements FNetworkNotify) */
	class FNetworkNotify*		Notify;
	
	double GetElapsedTime() const { return ElapsedTime; }
	void ResetElapsedTime() { ElapsedTime = 0.0; }

	bool IsInTick() const { return bInTick; }

	bool GetPendingDestruction() const { return bPendingDestruction; }
	void SetPendingDestruction(bool bDestroy) { bPendingDestruction = bDestroy; }

private:
	double						ElapsedTime;

	/** Whether or not the NetDriver is ticking */
	bool bInTick;

	uint8 bPendingDestruction : 1;

#if DO_ENABLE_NET_TEST
	/** Dont load packet settings from config or cmdline when true*/
	uint8 bForcedPacketSettings : 1;
#endif 

	uint8 bDidHitchLastFrame : 1;

	/** cache whether or not we have a replay connection, updated when a connection is added or removed */
	uint8 bHasReplayConnection : 1;

protected:
	uint8 bMaySendProperties : 1;

	uint8 bSkipServerReplicateActors : 1;

	uint8 bSkipClearVoicePackets : 1;

public:
	/**
	 * If true, ignore timeouts completely.  Should be used only in development
	 */
	UPROPERTY(Config)
	uint8 bNoTimeouts : 1;

	/**
	 * If true this NetDriver will not apply the network emulation settings that simulate
	 * latency and packet loss in non-shippable builds
	 */
	UPROPERTY(Config)
	uint8 bNeverApplyNetworkEmulationSettings : 1;

	/** If true then client connections are to other client peers */
	uint8						bIsPeer : 1;
	/** @todo document */
	uint8						ProfileStats : 1;
	/** If true, it assumes the stats are being set by server data */
	uint8						bSkipLocalStats : 1;
	/** Collect net stats even if not FThreadStats::IsCollectingData(). */
	uint8 bCollectNetStats : 1;
	/** Used to determine if checking for standby cheats should occur */
	uint8						bIsStandbyCheckingEnabled : 1;
	/** Used to determine whether we've already caught a cheat or not */
	uint8						bHasStandbyCheatTriggered : 1;

	/** Last realtime a tick dispatch occurred. Used currently to try and diagnose timeout issues */
	double						LastTickDispatchRealtime;
	/** Timings for Socket::SendTo() */
	int32						SendCycles;
	/** Stats for network perf */
	uint32						InBytesPerSecond;
	/** todo document */
	uint32						OutBytesPerSecond;
	/** todo document */
	uint32						InBytes;
	/** Total bytes in packets received since the net driver's creation */
	uint32						InTotalBytes;
	/** todo document */
	uint32						OutBytes;
	/** Total bytes in packets sent since the net driver's creation */
	uint32						OutTotalBytes;
	/** Outgoing rate of NetGUID Bunches */
	uint32						NetGUIDOutBytes;
	/** Incoming rate of NetGUID Bunches */
	uint32						NetGUIDInBytes;
	/** todo document */
	uint32						InPackets;
	/** Total packets received since the net driver's creation  */
	uint32						InTotalPackets;
	/** todo document */
	uint32						OutPackets;
	/** Total packets sent since the net driver's creation  */
	uint32						OutTotalPackets;
	/** todo document */
	uint32						InBunches;
	/** todo document */
	uint32						OutBunches;
	/** Total bunches received since the net driver's creation  */
	uint32						InTotalBunches;
	/** Total bunches sent since the net driver's creation  */
	uint32						OutTotalBunches;
	/** Total number of outgoing reliable bunches */
	uint32						OutTotalReliableBunches;
	/** Total number of incoming reliable bunches */
	uint32						InTotalReliableBunches;
	/** todo document */
	uint32						InPacketsLost;
	/** Total packets lost that have been sent by clients since the net driver's creation  */
	uint32						InTotalPacketsLost;
	/** todo document */
	uint32						OutPacketsLost;
	/** Total packets lost that have been sent by the server since the net driver's creation  */
	uint32						OutTotalPacketsLost;
	/** Tracks the total number of voice packets sent */
	uint32						VoicePacketsSent;
	/** Tracks the total number of voice bytes sent */
	uint32						VoiceBytesSent;
	/** Tracks the total number of voice packets received */
	uint32						VoicePacketsRecv;
	/** Tracks the total number of voice bytes received */
	uint32						VoiceBytesRecv;
	/** Tracks the voice data percentage of in bound bytes */
	uint32						VoiceInPercent;
	/** Tracks the voice data percentage of out bound bytes */
	uint32						VoiceOutPercent;
	/** Time of last stat update */
	double						StatUpdateTime;
	/** Interval between gathering stats */
	float						StatPeriod;
	/** Total RPCs called since the net driver's creation  */
	uint32						TotalRPCsCalled;
	/** Total acks sent since the net driver's creation  */
	uint32						OutTotalAcks;

	/** Time of last netdriver cleanup pass */
	double						LastCleanupTime;
	/** The amount of time without packets before triggering the cheat code */
	float						StandbyRxCheatTime;
	/** todo document */
	float						StandbyTxCheatTime;
	/** The point we think the host is cheating or shouldn't be hosting due to crappy network */
	int32						BadPingThreshold;
	/** The number of clients missing data before triggering the standby code */
	float						PercentMissingForRxStandby;
	float						PercentMissingForTxStandby;
	/** The number of clients with bad ping before triggering the standby code */
	float						PercentForBadPing;
	/** The amount of time to wait before checking a connection for standby issues */
	float						JoinInProgressStandbyWaitTime;
	/** Used to track whether a given actor was replicated by the net driver recently */
	int32						NetTag;

#if NET_DEBUG_RELEVANT_ACTORS
	/** Dumps next net update's relevant actors when true*/
	bool						DebugRelevantActors;

	/** These are debug list of actors. They are using TWeakObjectPtr so that they do not affect GC performance since they are rarely in use (DebugRelevantActors) */
	TArray< TWeakObjectPtr<AActor> >	LastPrioritizedActors;
	TArray< TWeakObjectPtr<AActor> >	LastRelevantActors;
	TArray< TWeakObjectPtr<AActor> >	LastSentActors;
	TArray< TWeakObjectPtr<AActor> >	LastNonRelevantActors;

	void						PrintDebugRelevantActors();
#endif // NET_DEBUG_RELEVANT_ACTORS
	
	/** The server adds an entry into this map for every actor that is destroyed that join-in-progress
	 *  clients need to know about, that is, startup actors. Also, individual UNetConnections
	 *  need to keep track of FActorDestructionInfo for dormant and recently-dormant actors in addition
	 *  to startup actors (because they won't have an associated channel), and this map stores those
	 *  FActorDestructionInfos also.
	 */
	TMap<FNetworkGUID, TUniquePtr<FActorDestructionInfo>>	DestroyedStartupOrDormantActors;

private:

	/** Tracks the network guids in DestroyedStartupOrDormantActors above, but keyed on the streaming level name. */
	TMap<FName, TSet<FNetworkGUID>> DestroyedStartupOrDormantActorsByLevel;

	/** Cached list of analytic attributes that can be appended via FNetAnalyticsAggregator::AppendGameInstanceAttributes */
	TMap<FString, FString> CachedNetAnalyticsAttributes;

public:

	const TSet<FNetworkGUID>& GetDestroyedStartupOrDormantActors(const FName& LevelName)
	{
		return DestroyedStartupOrDormantActorsByLevel.FindOrAdd(LevelName);
	}

	/** 
	 * Add or overwrite an analytics attribute that will be appended via FNetAnalyticsAggregator::AppendGameInstanceAttributes
	 * Useful to add game specific attributes to your analytics.
	 * @param AttributeKey The key of the attribute. Stored in a case-insensitive map
	 * @param AttributeValue Value of the attribute to store. Only supports strings.
	 */
	void SetNetAnalyticsAttributes(const FString& AttributeKey, const FString& AttributeValue) { CachedNetAnalyticsAttributes.Add(AttributeKey, AttributeValue); }

	/** The server adds an entry into this map for every startup actor that has been renamed, and will
	 *  always map from current name to original name
	 */
	TMap<FName, FName>	RenamedStartupActors;

	class UE_DEPRECATED(5.1, "No longer used.") FRepChangedPropertyTrackerWrapper
	{
	public:
		FRepChangedPropertyTrackerWrapper(UObject* Obj, const TSharedPtr<FRepChangedPropertyTracker>& InRepChangedPropertyTracker) : RepChangedPropertyTracker(InRepChangedPropertyTracker), WeakObjectPtr(Obj) {}

		const FRepChangedPropertyTracker* operator->() const { return RepChangedPropertyTracker.Get(); }
		FRepChangedPropertyTracker* operator->() { return RepChangedPropertyTracker.Get(); }
		
		const FRepChangedPropertyTracker* Get() const { return RepChangedPropertyTracker.Get(); }
		FRepChangedPropertyTracker* Get() { return RepChangedPropertyTracker.Get(); }

		bool IsValid() const { return RepChangedPropertyTracker.IsValid(); }
		bool IsObjectValid() const { return WeakObjectPtr.IsValid(); }

		TWeakObjectPtr<UObject> GetWeakObjectPtr() const { return WeakObjectPtr; }

		TSharedPtr<FRepChangedPropertyTracker> RepChangedPropertyTracker;

		void CountBytes(FArchive& Ar) const;

	private:
		TWeakObjectPtr<UObject> WeakObjectPtr;
	};

	PRAGMA_DISABLE_DEPRECATION_WARNINGS
	/** Maps FRepChangedPropertyTracker to active objects that are replicating properties */
	UE_DEPRECATED(5.1, "Property trackers have been moved to the NetCore module")
	TMap<UObject*, FRepChangedPropertyTrackerWrapper>	RepChangedPropertyTrackerMap;
	PRAGMA_ENABLE_DEPRECATION_WARNINGS

	/** Used to invalidate properties marked "unchanged" in FRepChangedPropertyTracker's */
	uint32 ReplicationFrame;

	/** Maps FRepLayout to the respective UClass */
	TMap<TWeakObjectPtr<UObject>, TSharedPtr<FRepLayout>, FDefaultSetAllocator, TWeakObjectPtrMapKeyFuncs<TWeakObjectPtr<UObject>, TSharedPtr<FRepLayout> > >	RepLayoutMap;

	class FReplicationChangelistMgrWrapper
	{
	public:
		FReplicationChangelistMgrWrapper(UObject* Obj, const TSharedPtr<FReplicationChangelistMgr>& InReplicationChangelistMgr) : ReplicationChangelistMgr(InReplicationChangelistMgr), WeakObjectPtr(Obj) {}

		const FReplicationChangelistMgr* operator->() const { return ReplicationChangelistMgr.Get(); }
		FReplicationChangelistMgr* operator->() { return ReplicationChangelistMgr.Get(); }

		bool IsValid() const { return ReplicationChangelistMgr.IsValid(); }
		bool IsObjectValid() const { return WeakObjectPtr.IsValid(); }

		TWeakObjectPtr<UObject> GetWeakObjectPtr() const { return WeakObjectPtr; }

		TSharedPtr<FReplicationChangelistMgr> ReplicationChangelistMgr;

		void CountBytes(FArchive& Ar) const;

	private:
		TWeakObjectPtr<UObject> WeakObjectPtr;
	};
	/** Maps an object to the respective FReplicationChangelistMgr */
	TMap< UObject*, FReplicationChangelistMgrWrapper >	ReplicationChangeListMap;

	/** Creates if necessary, and returns a FRepLayout that maps to the passed in UClass */
	TSharedPtr< FRepLayout >	GetObjectClassRepLayout( UClass * InClass );

	/** Creates if necessary, and returns a FRepLayout that maps to the passed in UFunction */
	ENGINE_API TSharedPtr<FRepLayout> GetFunctionRepLayout( UFunction * Function );

	/** Creates if necessary, and returns a FRepLayout that maps to the passed in UStruct */
	ENGINE_API TSharedPtr<FRepLayout> GetStructRepLayout( UStruct * Struct );

	/**
	 * Returns the FReplicationChangelistMgr that is associated with the passed in object,
	 * creating one if none exist.
	 *
	 * This should **never** be called on client NetDrivers!
	 */
	TSharedPtr< FReplicationChangelistMgr > GetReplicationChangeListMgr( UObject* Object );

	TMap< FNetworkGUID, TSet< FObjectReplicator* > >	GuidToReplicatorMap;
	int32												TotalTrackedGuidMemoryBytes;
	TSet< FObjectReplicator* >							UnmappedReplicators;
	TSet< FObjectReplicator* >							AllOwnedReplicators;

	/** Handles to various registered delegates */
	FDelegateHandle TickDispatchDelegateHandle;
	FDelegateHandle PostTickDispatchDelegateHandle;
	FDelegateHandle TickFlushDelegateHandle;
	FDelegateHandle PostTickFlushDelegateHandle;

#if !UE_BUILD_SHIPPING
	/** Delegate for hooking ProcessRemoteFunction */
	FOnSendRPC	SendRPCDel;

	/** Delegate for hooking ShouldSkipRepNotifies */
	FShouldSkipRepNotifies SkipRepNotifiesDel;
#endif

	/** Tracks the amount of time spent during the current frame processing queued bunches. */
	float ProcessQueuedBunchesCurrentFrameMilliseconds;

	/** DDoS detection management */
	FDDoSDetection DDoS;

	/** Local address this net driver is associated with */
	TSharedPtr<FInternetAddr> LocalAddr;

	/**
	* Updates the standby cheat information and
	 * causes the dialog to be shown/hidden as needed
	 */
	void UpdateStandbyCheatStatus(void);

	/** Sets the analytics provider */
	ENGINE_API virtual void SetAnalyticsProvider(TSharedPtr<IAnalyticsProvider> InProvider);

#if DO_ENABLE_NET_TEST
	FPacketSimulationSettings	PacketSimulationSettings;

	/**
	 * Modify the current emulation settings
	 */
	ENGINE_API void SetPacketSimulationSettings(const FPacketSimulationSettings& NewSettings);

	void OnPacketSimulationSettingsChanged();
#endif

	// Constructors.
	ENGINE_API UNetDriver(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

	ENGINE_API UNetDriver(FVTableHelper& Helper);


	//~ Begin UObject Interface.
	ENGINE_API virtual void PostInitProperties() override;
	ENGINE_API virtual void PostReloadConfig(FProperty* PropertyToLoad) override;
	ENGINE_API virtual void FinishDestroy() override;
	ENGINE_API virtual void Serialize( FArchive& Ar ) override;
	ENGINE_API static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
	//~ End UObject Interface.

	//~ Begin FExec Interface
protected:
	/**
	 * Handle exec commands
	 *
	 * @param InWorld	the world context
	 * @param Cmd		the exec command being executed
	 * @param Ar		the archive to log results to
	 *
	 * @return true if the handler consumed the input, false to continue searching handlers
	 */
	ENGINE_API virtual bool Exec_Dev(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar=*GLog) override;
//~ End FExec Interface.

public:

	ENGINE_API ENetMode	GetNetMode() const;

	/** 
	 * Returns true if this net driver is valid for the current configuration.
	 * Safe to call on a CDO if necessary
	 *
	 * @return true if available, false otherwise
	 */
	ENGINE_API virtual bool IsAvailable() const PURE_VIRTUAL( UNetDriver::IsAvailable, return false;)

	/**
	 * Common initialization between server and client connection setup
	 * 
	 * @param bInitAsClient are we a client or server
	 * @param InNotify notification object to associate with the net driver
	 * @param URL destination
	 * @param bReuseAddressAndPort whether to allow multiple sockets to be bound to the same address/port
	 * @param Error output containing an error string on failure
	 *
	 * @return true if successful, false otherwise (check Error parameter)
	 */
	ENGINE_API virtual bool InitBase(bool bInitAsClient, FNetworkNotify* InNotify, const FURL& URL, bool bReuseAddressAndPort, FString& Error);

	/**
	 * Initialize the net driver in client mode
	 *
	 * @param InNotify notification object to associate with the net driver
	 * @param ConnectURL remote ip:port of host to connect to
	 * @param Error resulting error string from connection attempt
	 * 
	 * @return true if successful, false otherwise (check Error parameter)
	 */
	ENGINE_API virtual bool InitConnect(class FNetworkNotify* InNotify, const FURL& ConnectURL, FString& Error ) PURE_VIRTUAL( UNetDriver::InitConnect, return true;);

	/**
	 * Initialize the network driver in server mode (listener)
	 *
	 * @param InNotify notification object to associate with the net driver
	 * @param ListenURL the connection URL for this listener
	 * @param bReuseAddressAndPort whether to allow multiple sockets to be bound to the same address/port
	 * @param Error out param with any error messages generated 
	 *
	 * @return true if successful, false otherwise (check Error parameter)
	 */
	ENGINE_API virtual bool InitListen(class FNetworkNotify* InNotify, FURL& ListenURL, bool bReuseAddressAndPort, FString& Error) PURE_VIRTUAL( UNetDriver::InitListen, return true;);

	/** Initialize the list of destroyed net startup actors from the current World */
	ENGINE_API virtual void InitDestroyedStartupActors();

	/**
	 * Initialize a PacketHandler for serverside net drivers, for handling connectionless packets
	 * NOTE: Only triggered by net driver subclasses that support it - from within InitListen.
	 */
	ENGINE_API virtual void InitConnectionlessHandler();

	/**
	 * Flushes all packets queued by the connectionless PacketHandler
	 * NOTE: This should be called shortly after all calls to PacketHandler::IncomingConnectionless, to minimize packet buffer buildup.
	 */
	ENGINE_API virtual void FlushHandler();

	/** Initializes the net connection class to use for new connections */
	ENGINE_API virtual bool InitConnectionClass(void);

	/** Initialized the replication driver class to use for this driver */
	ENGINE_API virtual bool InitReplicationDriverClass();

#if UE_WITH_IRIS
	/** Initialized the replication bridge class to use for this driver if using iris replication*/
	ENGINE_API virtual bool InitReplicationBridgeClass();
#endif
	/** Shutdown all connections managed by this net driver */
	ENGINE_API virtual void Shutdown();

	/* Close socket and Free the memory the OS allocated for this socket */
	ENGINE_API virtual void LowLevelDestroy();

	/* @return network number */
	ENGINE_API virtual FString LowLevelGetNetworkNumber();

	/* @return local addr of this machine if set */
	virtual TSharedPtr<const FInternetAddr> GetLocalAddr() { return LocalAddr; }

	/** Make sure this connection is in a reasonable state. */
	ENGINE_API virtual void AssertValid();

	/**
	 * Called to replicate any relevant actors to the connections contained within this net driver
	 *
	 * Process as many clients as allowed given Engine.NetClientTicksPerSecond, first building a list of actors to consider for relevancy checking,
	 * and then attempting to replicate each actor for each connection that it is relevant to until the connection becomes saturated.
	 *
	 * NetClientTicksPerSecond is used to throttle how many clients are updated each frame, hoping to avoid saturating the server's upstream bandwidth, although
	 * the current solution is far from optimal.  Ideally the throttling could be based upon the server connection becoming saturated, at which point each
	 * connection is reduced to priority only updates, and spread out amongst several ticks.  Also might want to investigate eliminating the redundant consider/relevancy
	 * checks for Actors that were successfully replicated for some channels but not all, since that would make a decent CPU optimization.
	 *
	 * @param DeltaSeconds elapsed time since last call
	 *
	 * @return the number of actors that were replicated
	 */
	ENGINE_API virtual int32 ServerReplicateActors(float DeltaSeconds);

	/**
	 * Process a remote function call on some actor destined for a remote location
	 *
	 * @param Actor actor making the function call
	 * @param Function function definition called
	 * @param Params parameters in a UObject memory layout
	 * @param Stack stack frame the UFunction is called in
	 * @param SubObject optional: sub object to actually call function on
	 */
	ENGINE_API virtual void ProcessRemoteFunction(class AActor* Actor, class UFunction* Function, void* Parameters, struct FOutParmRec* OutParms, struct FFrame* Stack, class UObject* SubObject = nullptr );

	/** Return a reference to the database that holds metrics calcluated by the networking system. */
	ENGINE_API TObjectPtr<UNetworkMetricsDatabase> GetMetrics() { return NetworkMetricsDatabase; };

	enum class ERemoteFunctionSendPolicy
	{		
		/** Unreliable multicast are queued. Everything else is send immediately */
		Default, 

		/** Bunch is send immediately no matter what */
		ForceSend,

		/** Bunch is queued until next actor replication, no matter what */
		ForceQueue,
	};
	UE_DEPRECATED(5.4, "Use fully scoped enum class value UNetDriver::ERemoteFunctionSendPolicy::Default")
	static constexpr ERemoteFunctionSendPolicy Default = ERemoteFunctionSendPolicy::Default;
	UE_DEPRECATED(5.4, "Use fully scoped enum class value UNetDriver::ERemoteFunctionSendPolicy::ForceSend")
	static constexpr ERemoteFunctionSendPolicy ForceSend = ERemoteFunctionSendPolicy::ForceSend;
	UE_DEPRECATED(5.4, "Use fully scoped enum class value UNetDriver::ERemoteFunctionSendPolicy::ForceQueue")
	static constexpr ERemoteFunctionSendPolicy ForceQueue = ERemoteFunctionSendPolicy::ForceQueue;

	/** Process a remote function on given actor channel. This is called by ::ProcessRemoteFunction.*/
	ENGINE_API void ProcessRemoteFunctionForChannel(
		UActorChannel* Ch,
		const class FClassNetCache* ClassCache,
		const FFieldNetCache* FieldCache,
		UObject* TargetObj,
		UNetConnection* Connection,
		UFunction* Function,
		void* Parms,
		FOutParmRec* OutParms,
		FFrame* Stack,
		const bool IsServer,
		const ERemoteFunctionSendPolicy SendPolicy = ERemoteFunctionSendPolicy::Default);

	ENGINE_API void ProcessRemoteFunctionForChannel(
		UActorChannel* Ch,
		const class FClassNetCache* ClassCache,
		const FFieldNetCache* FieldCache,
		UObject* TargetObj,
		UNetConnection* Connection,
		UFunction* Function,
		void* Parms,
		FOutParmRec* OutParms,
		FFrame* Stack,
		const bool IsServer,
		const ERemoteFunctionSendPolicy SendPolicy,
		EProcessRemoteFunctionFlags& RemoteFunctionFlags);

private:

	void ProcessRemoteFunctionForChannelPrivate(
		UActorChannel* Ch,
		const class FClassNetCache* ClassCache,
		const FFieldNetCache* FieldCache,
		UObject* TargetObj,
		UNetConnection* Connection,
		UFunction* Function,
		void* Parms,
		FOutParmRec* OutParms,
		FFrame* Stack,
		const bool bIsServer,
		const ERemoteFunctionSendPolicy SendPolicy,
		EProcessRemoteFunctionFlags& RemoteFunctionFlags);

public:

	/** handle time update: read and process packets */
	ENGINE_API virtual void TickDispatch( float DeltaTime );

	/** PostTickDispatch actions */
	ENGINE_API virtual void PostTickDispatch();

	/** ReplicateActors and Flush */
	ENGINE_API virtual void TickFlush(float DeltaSeconds);

	/** PostTick actions */
	ENGINE_API virtual void PostTickFlush();

	/**
	 * Sends a 'connectionless' (not associated with a UNetConection) packet, to the specified address.
	 * NOTE: Address is an abstract format defined by subclasses. Anything calling this, must use an address supplied by the net driver.
	 *
	 * @param Address		The address the packet should be sent to (format is abstract, determined by net driver subclasses)
	 * @param Data			The packet data
	 * @param CountBits		The size of the packet data, in bits
	 * @param Traits		Traits for the packet, if applicable
	 */
	ENGINE_API virtual void LowLevelSend(TSharedPtr<const FInternetAddr> Address, void* Data, int32 CountBits, FOutPacketTraits& Traits)
		PURE_VIRTUAL(UNetDriver::LowLevelSend,);

	/**
	 * Process any local talker packets that need to be sent to clients
	 */
	ENGINE_API virtual void ProcessLocalServerPackets();

	/**
	 * Process any local talker packets that need to be sent to the server
	 */
	ENGINE_API virtual void ProcessLocalClientPackets();

	/**
	 * Update the LagState based on a heuristic to determine if we are network lagging
	 */
	ENGINE_API virtual void UpdateNetworkLagState();

	/**
	 * Determines which other connections should receive the voice packet and
	 * queues the packet for those connections. Used for sending both local/remote voice packets.
	 *
	 * @param VoicePacket the packet to be queued
	 * @param CameFromConn the connection this packet came from (NULL if local)
	 */
	ENGINE_API virtual void ReplicateVoicePacket(TSharedPtr<class FVoicePacket> VoicePacket, class UNetConnection* CameFromConn);

#if !UE_BUILD_SHIPPING
	/**
	 * Exec command handlers
	 */
	bool HandleSocketsCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandlePackageMapCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandleNetFloodCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandleNetDebugTextCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandleNetDisconnectCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandleNetDumpServerRPCCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandleNetDumpDormancy(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandleDumpSubObjectsCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandleDumpRepLayoutFlagsCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandlePushModelMemCommand(const TCHAR* Cmd, FOutputDevice& Ar);
	bool HandlePropertyConditionsMemCommand(const TCHAR* Cmd, FOutputDevice& Ar);
#endif

	void HandlePacketLossBurstCommand( int32 DurationInMilliseconds );

	// ---------------------------------------------------------------
	//	Game code API for updating server Actor Replication State
	// ---------------------------------------------------------------

	ENGINE_API virtual void ForceNetUpdate(AActor* Actor);

	ENGINE_API void ForceAllActorsNetUpdateTime(float NetUpdateTimeOffset, TFunctionRef<bool(const AActor* const)> ValidActorTestFunc);

	/** Flushes actor from NetDriver's dormancy list, but does not change any state on the Actor itself */
	ENGINE_API void FlushActorDormancy(AActor *Actor, bool bWasDormInitial=false);

	//~ This probably doesn't need to be exported, since it's only called by AActor::SetNetDormancy.

	/** Notifies the NetDriver that the desired Dormancy state for this Actor has changed. */
	ENGINE_API void NotifyActorDormancyChange(AActor* Actor, ENetDormancy OldDormancyState);

	/** Called after an actor channel is opened on a client when the actor was previously dormant. */
	ENGINE_API virtual void NotifyActorClientDormancyChanged(AActor* Actor, ENetDormancy OldDormancyState);

	/** Forces properties on this actor to do a compare for one frame (rather than share shadow state) */
	ENGINE_API void ForcePropertyCompare( AActor* Actor );

	/** Force this actor to be relevant for at least one update */
	ENGINE_API void ForceActorRelevantNextUpdate(AActor* Actor);

	/** Tells the net driver about a networked actor that was spawned */
	ENGINE_API void AddNetworkActor(AActor* Actor);

	/** Called when a spawned actor is destroyed. */
	ENGINE_API virtual void NotifyActorDestroyed( AActor* Actor, bool IsSeamlessTravel=false );

	void NotifySubObjectDestroyed(UObject* SubObject);

	/** Called when an actor is renamed. */
	UE_DEPRECATED(5.4, "Replaced by overload that takes the PreviousOuter")
	ENGINE_API virtual void NotifyActorRenamed(AActor* Actor, FName PreviousName);
	
	/** Called when an actor is renamed. */
	ENGINE_API virtual void NotifyActorRenamed(AActor* Actor, UObject* PreviousOuter, FName PreviousName);

	ENGINE_API void RemoveNetworkActor(AActor* Actor);

	/** Called when an authoritative actor wants to delete a replicated subobject on the clients it was already replicated to */
	void DeleteSubObjectOnClients(AActor* Actor, UObject* SubObject);

	/** Called when an authoritative actor wants to tear off a subobject on the clients it was already replicated to */
	void TearOffSubObjectOnClients(AActor* Actor, UObject* SubObject);

	ENGINE_API virtual void NotifyActorLevelUnloaded( AActor* Actor );

	ENGINE_API virtual void NotifyActorTearOff(AActor* Actor);

	/** Called when an actor is about to be carried during a seamless travel */
	ENGINE_API void NotifyActorIsTraveling(AActor* TravelingActor);

	/** Set whether this actor should swap roles before replicating properties. */
	ENGINE_API void SetRoleSwapOnReplicate(AActor* Actor, bool bSwapRoles);

	// ---------------------------------------------------------------
	//
	// ---------------------------------------------------------------	

	ENGINE_API virtual void NotifyStreamingLevelUnload( ULevel* );

	/** creates a child connection and adds it to the given parent connection */
	ENGINE_API virtual class UChildConnection* CreateChild(UNetConnection* Parent);

	/** @return String that uniquely describes the net driver instance */
	FString GetDescription() const
	{ 
		return FString::Printf(TEXT("Name:%s Def:%s %s%s"), *NetDriverName.ToString(), *NetDriverDefinition.ToString(), *GetName(), bIsPeer ? TEXT("(PEER)") : TEXT(""));
	}

	/** @return true if this netdriver is handling accepting connections */
	ENGINE_API virtual bool IsServer() const;

	ENGINE_API virtual void CleanPackageMaps();

	void RemoveClassRepLayoutReferences(UClass* Class);

	ENGINE_API void CleanupWorldForSeamlessTravel();

	ENGINE_API void PreSeamlessTravelGarbageCollect();

	ENGINE_API void PostSeamlessTravelGarbageCollect();

	/**
	 * Get the socket subsytem appropriate for this net driver
	 */
	ENGINE_API virtual class ISocketSubsystem* GetSocketSubsystem() PURE_VIRTUAL(UNetDriver::GetSocketSubsystem, return NULL;);

	/**
	 * Associate a world with this net driver. 
	 * Disassociates any previous world first.
	 * 
	 * @param InWorld the world to associate with this netdriver
	 */
	ENGINE_API virtual void SetWorld(class UWorld* InWorld);

	/**
	 * Get the world associated with this net driver
	 */
	virtual class UWorld* GetWorld() const override final { return World; }

	class UPackage* GetWorldPackage() const { return WorldPackage; }

	/** Called during seamless travel to clear all state that was tied to the previous game world (actor lists, etc) */
	ENGINE_API virtual void ResetGameWorldState();

	/** @return true if the net resource is valid or false if it should not be used */
	ENGINE_API virtual bool IsNetResourceValid(void) PURE_VIRTUAL(UNetDriver::IsNetResourceValid, return false;);

	bool NetObjectIsDynamic(const UObject *Object) const;

	/** Draws debug markers in the world based on network state */
	void DrawNetDriverDebug();

	/** 
	 * Finds a FRepChangedPropertyTracker associated with an object.
	 * If not found, creates one.
	*/
	TSharedPtr<FRepChangedPropertyTracker> FindOrCreateRepChangedPropertyTracker(UObject *Obj);

	/** Finds a FRepChangedPropertyTracker associated with an object. */
	TSharedPtr<FRepChangedPropertyTracker> FindRepChangedPropertyTracker(UObject* Obj);

	/** Returns true if the client should destroy immediately any actor that becomes torn-off */
	virtual bool ShouldClientDestroyTearOffActors() const { return false; }

	/** Returns whether or not properties that are replicating using this driver should not call RepNotify functions. */
	ENGINE_API virtual bool ShouldSkipRepNotifies() const;

	/** Returns true if actor channels with InGUID should queue up bunches, even if they wouldn't otherwise be queued. */
	virtual bool ShouldQueueBunchesForActorGUID(FNetworkGUID InGUID) const { return false; }

	/** Returns whether or not RPCs processed by this driver should be ignored. */
	virtual bool ShouldIgnoreRPCs() const { return false; }

	/** Returns the existing FNetworkGUID of InActor, if it has one. */
	virtual FNetworkGUID GetGUIDForActor(const AActor* InActor) const { return FNetworkGUID(); }

	/** Returns the actor that corresponds to InGUID, if one can be found. */
	virtual AActor* GetActorForGUID(FNetworkGUID InGUID) const { return nullptr; }

	/** Returns true if RepNotifies should be checked and generated when receiving properties for the given object. */
	virtual bool ShouldReceiveRepNotifiesForObject(UObject* Object) const { return true; }

	/** Returns the object that manages the list of replicated UObjects. */
	FNetworkObjectList& GetNetworkObjectList() { return *NetworkObjects; }

	/** Returns the object that manages the list of replicated UObjects. */
	const FNetworkObjectList& GetNetworkObjectList() const { return *NetworkObjects; }

	/**
     *	Get the network object matching the given Actor.
	 *	If the Actor is not present in the NetworkObjectInfo list, it will be added.
	 */
	UE_DEPRECATED(5.3, "Will be made private in a future release")
	ENGINE_API FNetworkObjectInfo* FindOrAddNetworkObjectInfo(const AActor* InActor);

	/** Get the network object matching the given Actor. */
	UE_DEPRECATED(5.3, "Will be made private in a future release")
	ENGINE_API FNetworkObjectInfo* FindNetworkObjectInfo(const AActor* InActor);

	UE_DEPRECATED(5.3, "Will be made private in a future release")
	const FNetworkObjectInfo* FindNetworkObjectInfo(const AActor* InActor) const
	{
		PRAGMA_DISABLE_DEPRECATION_WARNINGS
		return const_cast<UNetDriver*>(this)->FindNetworkObjectInfo(InActor);
		PRAGMA_ENABLE_DEPRECATION_WARNINGS
	}

	/**
	 * Returns whether adaptive net frequency is enabled. If enabled, update frequency is allowed to ramp down to MinNetUpdateFrequency for an actor when no replicated properties have changed.
	 * This is currently controlled by the CVar "net.UseAdaptiveNetUpdateFrequency".
	 */
	ENGINE_API static bool IsAdaptiveNetUpdateFrequencyEnabled();

	/** Returns true if adaptive net update frequency is enabled and the given actor is having its update rate lowered from its standard rate. */
	ENGINE_API bool IsNetworkActorUpdateFrequencyThrottled(const AActor* InActor) const;

	/** Returns true if adaptive net update frequency is enabled and the given actor is having its update rate lowered from its standard rate. */
	UE_DEPRECATED(5.3, "Will be made private in a future release, please use version that takes an actor")
	ENGINE_API bool IsNetworkActorUpdateFrequencyThrottled(const FNetworkObjectInfo& InNetworkActor) const;

	/** Stop adaptive replication for the given actor if it's currently throttled. It maybe be allowed to throttle again later. */
	UE_DEPRECATED(5.3, "Will be made private in a future release, please use version that takes an actor")
	ENGINE_API void CancelAdaptiveReplication(FNetworkObjectInfo& InNetworkActor);

	ENGINE_API void CancelAdaptiveReplication(const AActor* InActor);

	/** Returns true if the driver's world time has exceeded the next replication update time for this actor, or if it is pending replication from a previous frame. */
	ENGINE_API bool IsPendingNetUpdate(const AActor* InActor) const;

	/** Returns the level ID/PIE instance ID for this netdriver to use. */
	int32 GetDuplicateLevelID() const { return DuplicateLevelID; }

	/** Sets the level ID/PIE instance ID for this netdriver to use. */
	void SetDuplicateLevelID(const int32 InDuplicateLevelID) { DuplicateLevelID = InDuplicateLevelID; }

	/** Explicitly sets the ReplicationDriver instance (you instantiate it and initialize it). Shouldn't be done during gameplay: ok to do in GameMode startup or via console commands for testing. Existing ReplicationDriver (if set) is destroyed when this is called.  */
	ENGINE_API void SetReplicationDriver(UReplicationDriver* NewReplicationManager);

	UReplicationDriver* GetReplicationDriver() const { return ReplicationDriver; }

	/** Returns if this netdriver is initialized to replicate using the Iris replication system or the Legacy replication system. */
	FORCEINLINE bool IsUsingIrisReplication() const
	{
#if UE_WITH_IRIS
		return bIsUsingIris;
#else
		return false;
#endif //UE_WITH_IRIS
	}

	/** Returns the bitflag telling which network features are activated for this NetDriver. */
	ENGINE_API EEngineNetworkRuntimeFeatures GetNetworkRuntimeFeatures() const;
	
#if UE_WITH_IRIS
	/** Remove references to the Iris bridge and system without deleting it */
	ENGINE_API void ClearIrisSystem();

	/** Set a previously initialized IrisSystem into this NetDriver */
	ENGINE_API void RestoreIrisSystem(UReplicationSystem* InReplicationSystem);

	/**
	 * Destroy and recreate the iris replication system for an active netdrive.
	 * This will re-add all existing replicated actors back in the system.
	 * Useful if you need to reapply hotfix configs downloaded post-initialization.
	 */
	ENGINE_API void RestartIrisSystem();
#endif // UE_WITH_IRIS

	template<class T>
	T* GetReplicationDriver() const { return Cast<T>(ReplicationDriver); }

#if UE_WITH_IRIS
	inline UReplicationSystem* GetReplicationSystem() { return ReplicationSystem; }
	inline UReplicationSystem* GetReplicationSystem() const { return ReplicationSystem; }

	void UpdateGroupFilterStatusForLevel(const ULevel* Level, UE::Net::FNetObjectGroupHandle LevelGroupHandle);
#endif // UE_WITH_IRIS

	ENGINE_API void RemoveClientConnection(UNetConnection* ClientConnectionToRemove);

	/** Adds (fully initialized, ready to go) client connection to the ClientConnections list + any other game related setup */
	ENGINE_API void	AddClientConnection(UNetConnection * NewConnection);

	//~ This method should only be called by internal networking systems.
	ENGINE_API void NotifyActorFullyDormantForConnection(AActor* Actor, UNetConnection* Connection);

	/** Returns true if this actor is considered to be in a loaded level */
	ENGINE_API virtual bool IsLevelInitializedForActor( const AActor* InActor, const UNetConnection* InConnection ) const;

	/** Called after processing RPC to track time spent */
	void NotifyRPCProcessed(UFunction* Function, UNetConnection* Connection, double ElapsedTimeSeconds);

	/** Returns true if this network driver will handle the remote function call for the given actor. */
	ENGINE_API virtual bool ShouldReplicateFunction(AActor* Actor, UFunction* Function) const;

	/** Returns true if this network driver will forward a received remote function call to other active net drivers. */
	ENGINE_API virtual bool ShouldForwardFunction(AActor* Actor, UFunction* Function, void* Parms) const;

	/** Returns true if this network driver will replicate the given actor. */
	ENGINE_API virtual bool ShouldReplicateActor(AActor* Actor) const;

	/** Returns true if this network driver should execute this remote call locally. */
	ENGINE_API virtual bool ShouldCallRemoteFunction(UObject* Object, UFunction* Function, const FReplicationFlags& RepFlags) const;

	/** Returns true if clients should destroy the actor when the channel is closed. */
	ENGINE_API virtual bool ShouldClientDestroyActor(AActor* Actor) const;

	/** Called when an actor channel is remotely opened for an actor. */
	ENGINE_API virtual void NotifyActorChannelOpen(UActorChannel* Channel, AActor* Actor);
	
	/** Called when an actor channel is cleaned up for an actor. */
	ENGINE_API virtual void NotifyActorChannelCleanedUp(UActorChannel* Channel, EChannelCloseReason CloseReason);

	ENGINE_API virtual void NotifyActorTornOff(AActor* Actor);

	/** Called on clients when an actor channel is closed because it went dormant. */
	ENGINE_API virtual void ClientSetActorDormant(AActor* Actor);

	/** Called on clients when an actor is torn off. */
	ENGINE_API virtual void ClientSetActorTornOff(AActor* Actor);

	/**
	 * Returns the current delinquency analytics and resets them.
	 * This would be similar to calls to Get and Reset separately, except that the caller
	 * will assume ownership of data in this case.
	 */
	ENGINE_API void ConsumeAsyncLoadDelinquencyAnalytics(FNetAsyncLoadDelinquencyAnalytics& Out);

	/** Returns the current delinquency analytics. */
	ENGINE_API const FNetAsyncLoadDelinquencyAnalytics& GetAsyncLoadDelinquencyAnalytics() const;

	/** Resets the current delinquency analytics. */
	ENGINE_API void ResetAsyncLoadDelinquencyAnalytics();

	inline uint32 AllocateConnectionId() { return ConnectionIdHandler.Allocate(); }
	inline void FreeConnectionId(uint32 Id) { return ConnectionIdHandler.Free(Id); };

	/** Returns the NetConnection associated with the ConnectionId. Slow. */
	ENGINE_API UNetConnection* GetConnectionById(uint32 ConnectionId) const;

	/** Returns identifier used for NetTrace */
	inline uint32 GetNetTraceId() const { return NetTraceId; }

	/** Sends a message to a client to destroy an actor to the client.  The actor may already be destroyed locally. */
	ENGINE_API int64 SendDestructionInfo(UNetConnection* Connection, FActorDestructionInfo* DestructionInfo);

	/**
	 * Creates and sends a destruction info with the LevelUnloaded reason, only if ThisActor is dormant or recently dormant on Connection.
	 * Returns true if the destruction info was sent, false if the actor isn't replicated or dormant/recently dormant.
	 */
	bool SendDestructionInfoForLevelUnloadIfDormant(AActor* ThisActor, UNetConnection* Connection);

protected:

	void SetIsInTick(bool bIsInTick) { bInTick = bIsInTick; }

	/** Register all TickDispatch, TickFlush, PostTickFlush to tick in World */
	ENGINE_API void RegisterTickEvents(class UWorld* InWorld);
	/** Unregister all TickDispatch, TickFlush, PostTickFlush to tick in World */
	ENGINE_API void UnregisterTickEvents(class UWorld* InWorld);

private:
	void InternalTickDispatch(float DeltaSeconds);
	void InternalTickFlush(float DeltaSeconds);

protected:
	/** Subclasses may override this to customize channel creation. Called by GetOrCreateChannel if the pool is exhausted and a new channel must be allocated. */
	ENGINE_API virtual UChannel* InternalCreateChannelByName(const FName& ChName);

	/** Update stats related to networking. */
	void UpdateNetworkStats();

#if WITH_SERVER_CODE
	/**
	* Helper functions for ServerReplicateActors
	*/
	int32 ServerReplicateActors_PrepConnections( const float DeltaSeconds );
	void ServerReplicateActors_BuildConsiderList( TArray<FNetworkObjectInfo*>& OutConsiderList, const float ServerTickTime );

	// Actor prioritization
	ENGINE_API int32 ServerReplicateActors_PrioritizeActors( UNetConnection* Connection, const TArray<FNetViewer>& ConnectionViewers, const TArray<FNetworkObjectInfo*>& ConsiderList, const bool bCPUSaturated, FActorPriority*& OutPriorityList, FActorPriority**& OutPriorityActors );
	
	UE_DEPRECATED(5.3, "This function has been deprecated. Please use ServerReplicateActors_ProcessPrioritizedActorsRange instead")
	int32 ServerReplicateActors_ProcessPrioritizedActors( UNetConnection* Connection, const TArray<FNetViewer>& ConnectionViewers, FActorPriority** PriorityActors, const int32 FinalSortedCount, int32& OutUpdated );
	
	// Actor relevancy processing within specified range
	ENGINE_API int32 ServerReplicateActors_ProcessPrioritizedActorsRange( UNetConnection* Connection, const TArray<FNetViewer>& ConnectionViewers, FActorPriority** PriorityActors, const TInterval<int32>& ActorsIndexRange, int32& OutUpdated, bool bIgnoreSaturation = false );
	
	// Relevant actors that could not be processed this frame are marked to be considered for next frame
	ENGINE_API void ServerReplicateActors_MarkRelevantActors( UNetConnection* Connection, const TArray<FNetViewer>& ConnectionViewers, int32 StartActorIndex, int32 EndActorIndex, FActorPriority** PriorityActors );
	
	/**
	* Delegate for overriding the method ServerReplicateActors
	* in the part that prepares prioritized actors list of
	* client connections
	*/
	FOnConsiderListUpdate OnPreConsiderListUpdateOverride;

	/**
	* Delegate that complements the method ServerReplicateActors
	* with the additional replication logic
	*/
	FOnConsiderListUpdate OnPostConsiderListUpdateOverride;

	/**
	* Delegate that allows to implement additional procedures after
	* main replication logic in the corresponding part of the method
	* ServerReplicateActors
	*/
	FOnConsiderListUpdate OnProcessConsiderListOverride;
#endif

	/** Used to handle any NetDriver specific cleanup once a level has been removed from the world. */
	ENGINE_API virtual void OnLevelRemovedFromWorld(class ULevel* Level, class UWorld* World);

	/** Used to handle any NetDriver specific setup when a level has been added to the world. */
	ENGINE_API virtual void OnLevelAddedToWorld(class ULevel* Level, class UWorld* World);

	/** Handles that track our LevelAdded / Removed delegates. */
	FDelegateHandle OnLevelRemovedFromWorldHandle;
	FDelegateHandle OnLevelAddedToWorldHandle;


public:

	/**
	 * Typically, properties will only ever be replicated / sent from Server net drivers.
	 * Therefore, on clients sending replication state won't be created.
	 * Setting this to true will force creation of sending replication state.
	 *
	 * Note, this doesn't imply the NetDriver *will* ever send properties and will not
	 * standard NetDrivers to send properties from clients.
	 */
	const bool MaySendProperties() const
	{
		return bMaySendProperties;
	}

	/**
	 * Get the current number of sent packets for which we have received a delivery notification
	 */
	uint32 GetOutTotalNotifiedPackets() const { return OutTotalNotifiedPackets; }

	/**
	 * Increase the current number of sent packets for which we have received a delivery notification
	 */
	inline void IncreaseOutTotalNotifiedPackets() { ++OutTotalNotifiedPackets; }

	/**
	 * Get the total number of out of order packets for all connections.
	 *
	 * @return The total number of out of order packets.
	 */
	int32 GetTotalOutOfOrderPackets() const
	{
		return TotalOutOfOrderPacketsLost + TotalOutOfOrderPacketsRecovered + TotalOutOfOrderPacketsDuplicate;
	}

	/**
	 * Get the total number of out of order packets lost for all connections.
	 *
	 * @return The total number of out of order packets lost.
	 */
	int32 GetTotalOutOfOrderPacketsLost() const
	{
		return TotalOutOfOrderPacketsLost;
	}

	/**
	 * Increase the value of TotalOutOfOrderPacketsLost.
	 *
	 * @param Count		The amount to add to TotalOutOfOrderPacketsLost
	 */
	void IncreaseTotalOutOfOrderPacketsLost(int32 Count=1)
	{
		TotalOutOfOrderPacketsLost += Count;
	}

	/**
	 * Get the total number of out of order packets recovered for all connections.
	 *
	 * @return The total number of out of order packets recovered.
	 */
	int32 GetTotalOutOfOrderPacketsRecovered() const
	{
		return TotalOutOfOrderPacketsRecovered;
	}

	/**
	 * Increase the value of TotalOutOfOrderPacketsRecovered.
	 *
	 * @param Count		The amount to add to TotalOutOfOrderPacketsRecovered
	 */
	void IncreaseTotalOutOfOrderPacketsRecovered(int32 Count=1)
	{
		TotalOutOfOrderPacketsRecovered += Count;
	}

	/**
	 * Get the total number of out of order packets that were duplicates for all connections.
	 *
	 * @return The total number of out of order packets that were duplicates.
	 */
	int32 GetTotalOutOfOrderPacketsDuplicate() const
	{
		return TotalOutOfOrderPacketsDuplicate;
	}

	/**
	 * Increase the value of TotalOutOfOrderPacketsDuplicate.
	 *
	 * @param Count		The amount to add to TotalOutOfOrderPacketsDuplicate
	 */
	void IncreaseTotalOutOfOrderPacketsDuplicate(int32 Count=1)
	{
		TotalOutOfOrderPacketsDuplicate += Count;
	}

	uint32 GetCachedGlobalNetTravelCount() const
	{
		return CachedGlobalNetTravelCount;
	}

	bool DidHitchLastFrame() const;

	static bool IsDormInitialStartupActor(AActor* Actor);

	/** Unmap all references to this object, so that if later we receive this object again, we can remap the original references */
	void MoveMappedObjectToUnmapped(const UObject* Object);

	/** Whether or not this driver has an IsReplay() connection, updated in Add/RemoveClientConnection */
	bool HasReplayConnection() const { return bHasReplayConnection; }

	/**
	 * Whether or not this NetDriver supports encryption. Does not signify that encryption is actually enabled, nor setup by the PacketHandler.
	 *
	 * @return		Whether or not this NetDriver supports encryption.
	 */
	virtual bool DoesSupportEncryption() const
	{
		return true;
	}

	/**
	 * Whether or not this NetDriver requires encryption. Does signify that encryption is enabled, but does not signify that it's setup properly.
	 *
	 * @return		Whether or not encryption is presently required for connections.
	 */
	ENGINE_API virtual bool IsEncryptionRequired() const;

	/** Returns the value of cvar net.ClientIncomingBunchFrameTimeLimitMS on clients, or 0 otherwise. 0 = no limit. */
	ENGINE_API float GetIncomingBunchFrameProcessingTimeLimit() const;

	/** Returns true if the cvar net.ClientIncomingBunchFrameTimeLimitMS is set and the limit was exceeded */
	ENGINE_API bool HasExceededIncomingBunchFrameProcessingTime() const;

	/** Called internally by channels to track processing time for net.ClientIncomingBunchFrameTimeLimitMS and HasExceededIncomingBunchFrameProcessingTime() */
	void AddBunchProcessingFrameTimeMS(float Milliseconds) { IncomingBunchProcessingElapsedFrameTimeMS += Milliseconds; }

	/** Called internally by channels to track how many hit net.QueuedBunchTimeFailsafeSeconds */
	void AddQueuedBunchFailsafeChannel() { ++QueuedBunchFailsafeNumChannels; }

protected:
	
	/** Stream of random numbers to be used by this instance of UNetDriver */
	FRandomStream UpdateDelayRandomStream;

	/** Creates a trace event that updates the name and properties of the associated Game Instance */
	void NotifyGameInstanceUpdated();

	/** Indicates whether ticking throttle is enabled for this instance of NetDriver */
	bool bTickingThrottleEnabled = true;
private:
	// Only for ForwardRemoteFunction
	friend FObjectReplicator;

	ENGINE_API virtual ECreateReplicationChangelistMgrFlags GetCreateReplicationChangelistMgrFlags() const;

	FDelegateHandle PostGarbageCollectHandle;
	void PostGarbageCollect();

	FActorDestructionInfo* CreateDestructionInfo(AActor* ThisActor, FActorDestructionInfo *DestructionInfo);

	void CreateReplicatedStaticActorDestructionInfo(ULevel* Level, const FReplicatedStaticActorDestructionInfo& Info);

	void FlushActorDormancyInternal(AActor *Actor);

	void LoadChannelDefinitions();

	/** Used with FNetDelegates::OnSyncLoadDetected to log sync loads */
	void ReportSyncLoad(const FNetSyncLoadReport& Report);

	enum class ECrashContextUpdate
	{
		Default,
		UpdateRepModel,
		ClearRepModel,
	};
	void UpdateCrashContext(ECrashContextUpdate UpdateType=ECrashContextUpdate::Default);

	void RemoveDestroyedGuidsByLevel(const ULevel* Level, const TArray<FNetworkGUID>& RemovedGUIDs);

	/** Handle to FNetDelegates::OnSyncLoadDetected delegate */
	FDelegateHandle ReportSyncLoadDelegateHandle;

#if UE_WITH_IRIS
	void InitIrisSettings(FName NewDriverName);
	void SetReplicationSystem(UReplicationSystem* ReplicationSystem);
	void CreateReplicationSystem(bool bInitAsClient);
	void UpdateIrisReplicationViews() const;
	void SendClientMoveAdjustments();
	void PostDispatchSendUpdate();
#endif

	/** Tell the registered NetAnalytics to send their analytics via the provider */
	void SendNetAnalytics();

	/** Description of the replication model used by this Driver (RepGraph, Iris or Generic) */
	FString GetReplicationModelName() const;

	void InitNetTraceId();

	/** Called from RPC processing code to forward RPC to other NetDrivers if ShouldForwardFunction returns true. */
	void ForwardRemoteFunction(UObject* RootObject, UObject* SubObject, UFunction* Function, void* Parms);

	/** Go over imported network guids and map them to the newly created object. */
	void UpdateUnmappedObjects();

	/** Periodically look for invalid dormant replicators tied to destroyed objects. */
	void CleanupStaleDormantReplicators();

private:

	UPROPERTY(transient)
	TObjectPtr<UReplicationDriver> ReplicationDriver;

#if UE_WITH_IRIS
	UReplicationSystem* ReplicationSystem = nullptr;

	/** When set this will skip registering all the network relevant actors when setting the World */
	bool bSkipBeginReplicationForWorld = false;

	/** True when the NetDriver has been configured to run with the Iris replication system.*/
	bool bIsUsingIris = false;

	// For FindOrAddNetworkObjectInfo
	TPimplPtr<FNetworkObjectInfo> DummyNetworkObjectInfo;
#endif

	/** Stores the list of objects to replicate into the replay stream. This should be a TUniquePtr, but it appears the generated.cpp file needs the full definition of the pointed-to type. */
	TSharedPtr<FNetworkObjectList> NetworkObjects;

	/** Set to "Lagging" on the server when all client connections are near timing out. We are lagging on the client when the server connection is near timed out. */
	ENetworkLagState::Type LagState;

	/** Duplicate level instance to use for playback (PIE instance ID) */
	int32 DuplicateLevelID;

	/** NetDriver time to end packet loss burst simulation. */
	double PacketLossBurstEndTime;

	/** Count the number of notified packets, i.e. packets that we know if they are delivered or not. Used to reliably measure outgoing packet loss */
	uint32 OutTotalNotifiedPackets;

	/** Assigns driver unique IDs to client connections */
	FNetConnectionIdHandler ConnectionIdHandler;

	/** Unique id used by NetTrace to identify driver */
	uint32 NetTraceId;

	/** Stat tracking for the total number of out of order packets lost */
	int32 TotalOutOfOrderPacketsLost = 0;

	/** Stat tracking for the total number of out of order packets recovered */
	int32 TotalOutOfOrderPacketsRecovered = 0;

	/** Stat tracking for the total number of out of order packets that were duplicates */
	int32 TotalOutOfOrderPacketsDuplicate = 0;

	/** Cached value for UEngine.GlobalNetTravelCount, at the time of NetDriver initialization */
	uint32 CachedGlobalNetTravelCount = 0;
	
	/** Accumulated number of frames in the current stat gathering period */
	uint32 StatUpdateFrames = 0;

	/** Milliseconds spent processing incoming bunches in the current frame, directly from the network and queued on channels. */
	float IncomingBunchProcessingElapsedFrameTimeMS = 0.0f;
	
	/** Accumulated number of frames in the current stat period for which HasExceededIncomingBunchFrameProcessingTime would return true */
	uint32 NumFramesOverIncomingBunchTimeLimit = 0;

	/** Accumulated number of channels that hit the net.QueuedBunchTimeFailsafeSeconds this frame */
	uint32 QueuedBunchFailsafeNumChannels = 0;

};

 

요약하면 다음과 같다.

 

1. NetDriver와 NetConnection의 역할

  • NetDriver: 게임의 네트워크 트래픽을 관리하는 핵심 요소로, 클라이언트-서버 간 데이터 교환을 담당.
    • Game NetDriver: 표준 게임 트래픽 담당.
    • Demo NetDriver: 리플레이 녹화 및 재생.
    • Beacon NetDriver: 게임 외부의 네트워크 트래픽 처리.
  • NetConnection: 클라이언트와 서버 간의 개별 연결을 나타내며, 데이터를 Channel로 라우팅.

2. 주요 Channel 유형

  • Control Channel: 연결 상태 정보 전송.
  • Voice Channel: 음성 데이터 전송.
  • Actor Channel: 서버에서 클라이언트로 복제된 Actor 관리.

3. 연결과 핸드셰이크

  • 핸드셰이크 과정: 클라이언트가 서버에 연결 요청 → NMT 메시지를 통해 인증 및 연결 설정.
  • 서버는 클라이언트가 올바른 버전을 사용 중인지, 악의적인 주소가 아닌지 확인 후 연결을 허용.

4. 데이터 전송

  • Packets와 Bunches:
    • Packet: 클라이언트-서버 간 전송되는 데이터 블록.
    • Bunch: Packet 내부에서 Channel로 라우팅되는 데이터 단위.
  • 대용량 Bunch는 부분 Bunch로 분할되어 전송됨.

5. 신뢰성 및 재전송

  • 패킷 손실 관리:
    • 클라이언트는 수신한 패킷 번호를 확인(ACK)하며 누락된 패킷을 감지.
    • 서버는 ACK를 통해 누락된 데이터를 확인하고, 재전송 처리.
  • Bunch 재전송:
    • 신뢰할 수 없는 Bunch는 재전송되지 않음.
    • 신뢰할 수 있는 Bunch는 손실 시 재전송하며, 누락된 데이터를 대기 큐에 보관.

6. 연결 복구

  • 클라이언트가 연결을 복구하려면:
    • IP/포트가 변경된 경우 로우 레벨 핸드셰이크 재시도.
    • 게임 진행 도중 연결이 끊기면 기존 핸드셰이크 절차를 반복하여 복구 가능.

7. 요약된 흐름

  • 서버:
    • 클라이언트의 연결 요청 수신 → 인증 및 핸드셰이크 → 데이터 전송 관리.
  • 클라이언트:
    • 서버에 요청 → 핸드셰이크 완료 후 게임 참여 → 지속적인 데이터 전송 및 ACK 처리.

이 구조는 안정적이고 유연한 네트워크 환경을 제공하며, 게임 트래픽 관리와 데이터 전달의 신뢰성을 보장한다.

 

그리고 다음 링크에서 언리얼 네트워킹 아키텍처에 대해 자세히 알아볼 수 있다.

https://sites.google.com/site/techaht/trans/unreal-net-arch%E3%85%87

 


언리얼 엔진은 기본적으로 UDP 기반으로 작동하며, 필요하다면 자체적으로 신뢰성을 보장하는 방식으로 패킷 전송 및 재전송을 관리한다. NetDriver.h에[서 보면, 핸드쉐이크와 같이 TCP에서만 사용하는 부분이 있어 헷갈릴 수도 있고, 위 링크에서 보면 알 수 있지만, 인터넷 드라이버의 이름 자체가 TcpNetDriver라 헷갈릴 수 있다.
TcpNetDriver는 애초에 UDP 네트워크 드라이브다. 또한 핸드 쉐이크의 경우, 언리얼 엔진은 Reliable UDP 방식을 사용하기 때문에 존재하는 것으로 볼 수 있다.

'Study > Unreal Engine' 카테고리의 다른 글

[Tip] 파일의 이름 길이  (0) 2024.10.20
[Tips] 이름 앞에 붙는 것  (0) 2024.10.20
[Tips] UFNCTION  (0) 2024.10.20
[Tips] Delegate  (0) 2024.10.20
[Tips] TSharedPtr  (0) 2024.10.20