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

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

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++
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);

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