Keywords: UE4, Networking

Replication Cases

Disconnect Actor's replication at Run-time

Case:
A Character is replicated at first, then we want to disable replication when it dies.
E.g. Ragdoll, if we continue to keep replicating when character dies, it would increase overhead on server and server not care the result of Ragdoll, Ragdoll is just effect on client, so we need disconnect replication before perform Ragdoll computing.

Solution:
Execute AActor::ForceNetUpdate() to replicate current data to client before Ragdoll computing fired on server, then execute AActor::TearOff on server, from now on, Actor would not be replicaed forever.
AActor::TornOff() would be fired on client after executing AActor::TearOff on server, it means that Actor can be modified arbitrarily and it would not be corrected by server.

Replication of bullet path in shooting game

Suggestion:

  1. bReplicateMovement = true; in constructor of BulletActor;
  2. ProjectileMovement->InitialSpeed = 0.f; and ProjectileMovement->MaxSpeed = 0.f; in constructor of BulletActor;
  3. Set real time speed of BulletActor in Server.

If you set a large value for ProjectileMovement->InitialSpeed in bullet constructor, and then set Velocity in server, bullet path would jitter at the begining.

UE4 Networking Documents

Online Beacons Docs

Online Beacons are a special type of Actor that provide a lightweight way to contact a server and interact with it (via RPCs) without committing to a normal game connection. While the built-in classes can be used as they are in some cases, they are intended to be extended into custom classes that perform project-specific interactions, logic, and information requests.

Docs: Online Beacons
https://docs.unrealengine.com/en-US/Gameplay/Networking/OnlineBeacons/index.html

Online Beacons Usage:
e.g. If there's a requirement that make a decision to tell Client which Server to login, Client need to know the current player count of peer Server, now you can use Online Beacon to connect to Server before Replication connection created.

What are “online beacons” and how do they work?

The idea behind beacons was to provide a lightweight way to contact a server before actually connecting “for real”.

Ideally you make a connection, send an RPC, get a response, and disconnect. You can ask things like “who is in the game” or “please make room for me, I'm about to join”. Traditionally, connecting to a server involved a full map load which can be frustrating to users if they do all that work only to be denied entry to an almost full game because someone got into the game first. Now you can make contact while still in the main menu, for example, and test multiple servers to pick the best one before joining. Or to inquire about information about a game in progress to “spectate” without being actually connected with an APlayerController and all that entails.

Gears 3 used beacons to reserve space in game. That way we could test many servers for space and travel to only one, knowing that you were definitely going to get into the game. It avoided contention when thousands of users all received the same 20-50 search results but only 10 could join per match. However, in UE3, this was TCP/IP and the protocol had to be implemented manually not using any reflection by UObject.

In UE4, we decided that our already existing network code was so robust and powerful, why reinvent the wheel? So I made the AOnlineBeacon class to leverage all the RPC and replication power of AActor.

Traditionally, actor replication and spawning has always been server initiated, meaning that you had to connect before you got a copy of an actor that the server also knew about. I wanted clients to be able to create an actor that would initiate contact with the server and fire a delegate when a connection has been made so that higher level code could make an RPC. Otherwise you'd need to make a connection with the server, wait for the server to send you an actor you could RPC with, then switch to that. Unwieldy in my opinion.

Origin:
https://answers.unrealengine.com/questions/467973/what-are-online-beacons-and-how-do-they-work.html

Unreal Engine 4 Network Compendium

Unreal Engine 4 Network Compendium Made by: Cedric ‘eXi’ Neukirchen
http://cedric-neukirchen.net/Downloads/Compendium/UE4_Network_Compendium_by_Cedric_eXi_Neukirchen.pdf

Engine Source Analysis for Networking

Properties used for Replication in Common
/** Square of the max distance from the client's viewpoint that this actor is relevant and will be replicated. */
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category=Replication)
float NetCullDistanceSquared;   

/** Internal - used by UNetDriver */
UPROPERTY(Transient)
int32 NetTag;

/** How often (per second) this actor will be considered for replication, used to determine NetUpdateTime */
UPROPERTY(Category=Replication, EditDefaultsOnly, BlueprintReadWrite)
float NetUpdateFrequency;

/** Used to determine what rate to throttle down to when replicated properties are changing infrequently */
UPROPERTY(Category=Replication, EditDefaultsOnly, BlueprintReadWrite)
float MinNetUpdateFrequency;

/** Priority for this actor when checking for replication in a low bandwidth or saturated situation, higher priority means it is more likely to replicate */
UPROPERTY(Category=Replication, EditDefaultsOnly, BlueprintReadWrite)
float NetPriority;
How does engine check if Actor is relevant to connection

Critical Code:
Engine\Source\Runtime\Engine\Private\ActorReplication.cpp

bool AActor::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
    if (bAlwaysRelevant || IsOwnedBy(ViewTarget) || IsOwnedBy(RealViewer) || this == ViewTarget || ViewTarget == Instigator)
    {
        return true;
    }
    else if ( bNetUseOwnerRelevancy && Owner)
    {
        return Owner->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
    }
    else if ( bOnlyRelevantToOwner )
    {
        return false;
    }
    else if ( RootComponent && RootComponent->GetAttachParent() && RootComponent->GetAttachParent()->GetOwner() && (Cast<USkeletalMeshComponent>(RootComponent->GetAttachParent()) || (RootComponent->GetAttachParent()->GetOwner() == Owner)) )
    {
        return RootComponent->GetAttachParent()->GetOwner()->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
    }
    else if( bHidden && (!RootComponent || !RootComponent->IsCollisionEnabled()) )
    {
        return false;
    }

    if (!RootComponent)
    {
        UE_LOG(LogNet, Warning, TEXT("Actor %s / %s has no root component in AActor::IsNetRelevantFor. (Make bAlwaysRelevant=true?)"), *GetClass()->GetName(), *GetName() );
        return false;
    }

    return !GetDefault<AGameNetworkManager>()->bUseDistanceBasedRelevancy ||
            IsWithinNetRelevancyDistance(SrcLocation);
}

Call Stack:

UE4Editor-Engine.dll!AActor::IsNetRelevantFor(const AActor * RealViewer, const AActor * ViewTarget, const FVector & SrcLocation) Line 320	C++
[Inline Frame] UE4Editor-Engine.dll!IsActorRelevantToConnection(const AActor *) Line 3661	C++
UE4Editor-Engine.dll!UNetDriver::ServerReplicateActors_PrioritizeActors(UNetConnection * Connection, const TArray<FNetViewer,FDefaultAllocator> & ConnectionViewers, const TArray<FNetworkObjectInfo *,FDefaultAllocator> ConsiderList, const bool bCPUSaturated, FActorPriority * & OutPriorityList, FActorPriority * * & OutPriorityActors) Line 3780	C++
UE4Editor-Engine.dll!UNetDriver::ServerReplicateActors(float DeltaSeconds) Line 4359	C++
UE4Editor-Engine.dll!UNetDriver::TickFlush(float DeltaSeconds) Line 506	C++
[Inline Frame] UE4Editor-Engine.dll!TMemberFunctionCaller<UNetDriver,void (__cdecl UNetDriver::*)(float)>::operator()(float &) Line 156	C++
[Inline Frame] UE4Editor-Engine.dll!UE4Tuple_Private::TTupleImpl<TIntegerSequence<unsigned int> >::ApplyAfter(TMemberFunctionCaller<UNetDriver,void (__cdecl UNetDriver::*)(float)> &&) Line 498	C++
[Inline Frame] UE4Editor-Engine.dll!TBaseUObjectMethodDelegateInstance<0,UNetDriver,TTypeWrapper<void> __cdecl(float)>::Execute(float) Line 617	C++
UE4Editor-Engine.dll!TBaseUObjectMethodDelegateInstance<0,UNetDriver,void __cdecl(float)>::ExecuteIfSafe(float <Params_0>) Line 679	C++
UE4Editor-Engine.dll!TBaseMulticastDelegate<void,float>::Broadcast(float <Params_0>) Line 977	C++
UE4Editor-Engine.dll!UWorld::Tick(ELevelTick TickType, float DeltaSeconds) Line 1720	C++
UE4Editor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1618	C++
UE4Editor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 403	C++
UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 3967	C++
[Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 62	C++
UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 168	C++
UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 261	C++

Related Properties:

/** Always relevant for network (overrides bOnlyRelevantToOwner). */
UPROPERTY(Category=Replication, EditDefaultsOnly, BlueprintReadWrite)
uint8 bAlwaysRelevant:1;
How does engine check if strip animation data

Strip Animation Data On Dedicated Server was used in UAnimSequence::Serialize():

const bool bCookingTargetNeedsCompressedData = bIsCooking 
    && (!UAnimationSettings::Get()->bStripAnimationDataOnDedicatedServer 
        || !bIsCookingForDedicatedServer 
        || bEnableRootMotion);

Functions:

/** Called on client when updated AttachmentReplication value is received for this actor. */
UFUNCTION()
virtual void OnRep_AttachmentReplication();

Properties:

//Priority for this actor when checking for replication in a low bandwidth or saturated situation, higher priority means it is more likely to replicate
UPROPERTY(Category=Replication, EditDefaultsOnly, BlueprintReadWrite)
float NetPriority
How to change RemoteRole when spawning Actor
/** This function should only be used in the constructor of classes that need to set the RemoteRole for backwards compatibility purposes */
void SetRemoteRoleForBackwardsCompat(const ENetRole InRemoteRole) { RemoteRole = InRemoteRole; }
Reference

Function call replication - Sending messages between server and client
https://wiki.beyondunreal.com/Everything_you_ever_wanted_to_know_about_replication_%28but_were_afraid_to_ask%29#Function_call_replication_-_Sending_messages_between_server_and_client

《Exploring in UE4》网络同步原理深入(上)[原理分析]
https://zhuanlan.zhihu.com/p/34723199

《Exploring in UE4》网络同步原理深入(下)[原理分析]
https://zhuanlan.zhihu.com/p/55596030


我是个过分认真的人,总想给生命一个交代。这种愚蠢的努力简直成了我的噩梦,当然也是最终的救赎。——《像我这样笨拙地生活》