참고 자료

Actor Owner and Owning Connection

  • Unreal Engine은 Server가 모든 권한을 가지는(server-authoritative) client-server model을 채용하고 있다.
    • 이 model에서 client는 중앙화 된 Server에 접속하게 된다.
  • Client가 Server에 접속을 하게 되면 그에 대응하는 NetConnection이 생성한다.
    • 이 때 NetConnection은 PlayerController의 Owner가 된다.
  • Client가 Server에서 Play를 시작하면, Player Controller은 Client가 Control 할 pawn을 possess한다.
    • 이 때 PlayerController는 Pawn의 Owner가 된다.
  • Actor의 Owning Connection은 Actor를 소유한 PlayerController의 Owning Connection과 연관되어 있다.
  • Owner와 Owning Connection은 어느 Client에서 Replicate와 RPC를 전달해야하는지 판단한다.

용처

Actor Replication

  • Actor의 변경사항을 어느 Connection에 Replicate하는지 판단이 필요.
  • PlayerController와 같이 Actor를 소유한 Connection에서만 Replicate를 받는 경우에는 
    bOnlyRelevantToOwner 트리거가 True로 설정되어 있다.
    • PlayerController의 경우에는 이 플래그가 기본적으로 설정되어 있다.

Property Replication

  • Owner에 따른 Property Replication 조건이 붙은 경우에 사용된다.

RPCs

  • Multicast RPC가 아니라면 Client RPC가 어느 Client에 전달되어야 하는지 판단이 필요

NetRole이 ROLE_AutonomousProxy인 Actor

  • 해당 Actor의 Owner가 아닌 NetConnection에 대한 Property Replication이 발생하면
    Role이 ROLE_SimulatedProxy로 변경된다.

Role

/** The network role of an actor on a local/remote network context */
UENUM(BlueprintType)
enum ENetRole : int
{
	/** No role at all. */
	ROLE_None UMETA(DisplayName = "None"),
	/** Locally simulated proxy of this actor. */
	ROLE_SimulatedProxy UMETA(DisplayName = "Simulated Proxy"),
	/** Locally autonomous proxy of this actor. */
	ROLE_AutonomousProxy UMETA(DisplayName = "Autonomous Proxy"),
	/** Authoritative control over the actor. */
	ROLE_Authority UMETA(DisplayName = "Authority"),
	ROLE_MAX UMETA(Hidden),
};

NetRole

ROLE_SimulatedProxy

  • 수동적인(Approximate) 프록시라는 의미
  • 클라이언트에서 모조(Simulated) 프록시로 동작한다.
    • 기본적인 물리 동작만 Simulation하고 자체적인 판단을 하지 않는다.

ROLE_AutonomousProxy

  • 능동적인(Autonomous) 프록시라는 의미
  • 클라이언트에서 Simulation이 아닌 Prediction 로직을 갖는다.

ROLE_Authority

  • 해당 Connection에서 Actor에 대한 완전하고 절대적인(Authoritative) 제어권을 갖는다는 뜻

Actor Role

Local Role

  • 현재 자신 PC에서의 NetRole을 지칭

Remote Role

  • 자신이 원격으로 연결되어 있는 PC에서의 NetRole을 지칭

Server/Client에서의 Role 값

Server

  • Unreal Engine은 Replicate Actor에 대해 모든 Authority를 Server가 갖는다.
    • 때문에 Server의 모든 Actor는 LocalRole이 ROLE_Authority가 된다.
  • 특정 PlayerController가 Actor에 대한 Ownership을 가진 경우,
    RemoteRole은 ROLE_AutonomousProxy가 된다.
  • 그 외의 Actor들에 대해서는 ROLE_SimulatedProxy가 된다.

Client

  • 해당 Client의 Connection이 Ownership을 갖는 Actor의 경우,
    Prediction이 가능하기 때문에 LocalRole이 ROLE_AutonomousProxy가 된다.
  • 반대로 자신이 OwnerShipe을 갖지 않는 Actor의 경우,
    Simulation만이 가능하기 때문에 LocalRole이 ROLE_SimulatedProxy가 된다.
  • 어느 Actor든, Replicate가 되면 Authority는 Server가 가지기 때문에 RemoteRole이 ROLE_Authority가 된다.
  • 단, Client에서만 존재하는 Local Actor의 경우에는 LocalRole이 ROLE_Authority,
    RemoteRole이 ROLE_None이다.

Dormancy

  • 가장 영향력이 강한 Network Optimize 중 하나
    • Project에서 자주 Replicate 되지 않는 Actor가 많을수록 효과적이다.
  • NetDriver는 Connection의 모든 Replicated Actor를 수집하고, 이를 Iterate하여 Replicate 대상을 결정한다.
    • Dormancy는 특정 Actor를 Dormant 상태로 두어 NetDriver로부터 Actor가 수집되지 않도록 한다.
    • Actor가 많을수록 Iterate 비용이 부담되기에 Actor 수집을 필터링하는 것은 매우 효과적이다.

사용법

  1. Actor의 Constructor에서 NetDormancy를 초기화
    • 보통은 DORM_DormantAll로 초기화
    • Actor가 Map에 배치된 경우, DORM_Initial로 초기화
  2. NetDormancy가 DORM_DormantAll/DORM_Initial일 때에는 Dormant 상태가 되어 Replicate가 되지 않는다.
    • Dormant 상태의 Actor의 값을 변경하더라도 Awake/Flush를 하면 변경사항이 보존되지 않는다.
  3. Replicated Property를 변경하기 전 FlushNetDormancy/ForceNetUpdate/SetNetDormancy 함수를 호출.
    • 일반적으로는 값을 변경 후 함수 호출을 해도 Replicate 될 수 있다.
    • 하지만 정식 스펙 상으로는 올바른 사용법도 아니니 이를 지양해야 한다.
    • 대표적으로 Fast Array의 값을 변경한 후 함수를 호출하면 변경사항이 Replicate되지 않는다.
  4. Replicated Property 값을 변경
  • 다시 한번 강조하지만 이는 값이 자주 변경되지 않는 Replicated Actor에 적합하다.
    • 너무 자주 값이 바뀌면 Dormant<->Awake/Flush 오버헤드가 발생.
  • Dormancy는 Relavancy Handling과 달리 Dormant 상태가 되더라도 Server/Client 양쪽에서 Actor가 존재한다.
    • Dormant Actor은 Relavancy 검증에서 제외된다.

ENetDormancy

/** Describes if an actor can enter a low network bandwidth dormant mode */
UENUM(BlueprintType)
enum ENetDormancy : int
{
	/** This actor can never go network dormant. */
	DORM_Never UMETA(DisplayName = "Never"),
	/** This actor can go dormant, but is not currently dormant. Game code will tell it when it go dormant. */
	DORM_Awake UMETA(DisplayName = "Awake"),
	/** This actor wants to go fully dormant for all connections. */
	DORM_DormantAll UMETA(DisplayName = "Dormant All"),
	/** This actor may want to go dormant for some connections, GetNetDormancy() will be called to find out which. */
	DORM_DormantPartial UMETA(DisplayName = "Dormant Partial"),
	/** This actor is initially dormant for all connection if it was placed in map. */
	DORM_Initial UMETA(DisplayName = "Initial"),

	DORM_MAX UMETA(Hidden),
};

Awake Method

  • NetDormancy 변수는 Public 접근자로 선언되어 있지만, 가급적 Awake Method 사용을 권장한다.
  • 변경사항을 NetDriver에 알리는 기능이 Awake Methond에 내장되어 있다.

SetNetDormancy

/** Puts actor in dormant networking state */
UFUNCTION(BlueprintAuthorityOnly, BlueprintCallable, Category = "Networking")
ENGINE_API void SetNetDormancy(ENetDormancy NewDormancy);

void AActor::SetNetDormancy(ENetDormancy NewDormancy)
{
	if (IsNetMode(NM_Client))
	{
		return;
	}

	if (IsPendingKillPending())
	{
		return;
	}

	ENetDormancy OldDormancy = NetDormancy;
	NetDormancy = NewDormancy;
	const bool bDormancyChanged = (OldDormancy != NewDormancy);

	if (UWorld* MyWorld = GetWorld())
	{
		if (FWorldContext* const Context = GEngine->GetWorldContextFromWorld(MyWorld))
		{
			// Tell driver about change
			if (bDormancyChanged)
			{
				for (FNamedNetDriver& Driver : Context->ActiveNetDrivers)
				{
					if (Driver.NetDriver != nullptr && Driver.NetDriver->ShouldReplicateActor(this))
					{
						Driver.NetDriver->NotifyActorDormancyChange(this, OldDormancy);
					}
				}
			}

			// If not dormant, flush actor from NetDriver's dormant list
			if (NewDormancy <= DORM_Awake)
			{
				// Since we are coming out of dormancy, make sure we are on the network actor list
				MyWorld->AddNetworkActor(this);

				for (FNamedNetDriver& Driver : Context->ActiveNetDrivers)
				{
					if (Driver.NetDriver != nullptr && Driver.NetDriver->ShouldReplicateActor(this))
					{
						Driver.NetDriver->FlushActorDormancy(this);
					}
				}
			}
		}
	}
}
  • Dormant Actor의 NetDormancy 값을 수정해 Awake/Dormant 상태를 변경할 수 있다.
    • Map에 배치된 Dormant Actor의 경우, Awake 이후 DORM_Initial 대신 DORM_DormantAll로 설정되어야 한다.
  • Dormant Actor가 매 Frame마다 움직일 때 사용하면 적합하다.

FlushNetDormancy

/** Forces dormant actor to replicate but doesn't change NetDormancy state (i.e., they will go dormant again if left dormant) */
UFUNCTION(BlueprintAuthorityOnly, BlueprintCallable, Category="Networking")
ENGINE_API void FlushNetDormancy();

/** Removes the actor from the NetDriver's dormancy list: forcing at least one more update. */
void AActor::FlushNetDormancy()
{
	if (IsNetMode(NM_Client) || NetDormancy <= DORM_Awake || IsPendingKillPending())
	{
		return;
	}

	QUICK_SCOPE_CYCLE_COUNTER(NET_AActor_FlushNetDormancy);

	bool bWasDormInitial = false;
	if (NetDormancy == DORM_Initial)
	{
		// No longer initially dormant
		NetDormancy = DORM_DormantAll;
		bWasDormInitial = true;
	}

	// Don't proceed with network operations if not actually set to replicate
	if (!bReplicates)
	{
		return;
	}

	if (UWorld* const MyWorld = GetWorld())
	{
		// Add to network actors list if needed
		MyWorld->AddNetworkActor(this);
	
		if (FWorldContext* const Context = GEngine->GetWorldContextFromWorld(MyWorld))
		{
			for (FNamedNetDriver& Driver : Context->ActiveNetDrivers)
			{
				if (Driver.NetDriver != nullptr && Driver.NetDriver->ShouldReplicateActor(this))
				{
					Driver.NetDriver->FlushActorDormancy(this, bWasDormInitial);
				}
			}
		}
	}
}
  • Dormant Actor의 변경사항을 Replicate
    • Actor의 NetDormancy를 변경하지 않고도 연결된 Updates를 강제로 Replicate한다.
    • 단, NetDormancy가 DORM_Initial인 Actor는 NetDormancy가 DORM_DormantAll로 변경된다.
  • BP에서는 Dormant Actor의 Replicated Property를 수정하면 자동으로 FlushNetDormancy가 호출된다.
    • 5.4 버전 기준, ActorComponent 한정으로 Replicated Property를 수정해도 FlushNetDormancy가 호출되지 않는다.

ForceNetUpdate

/** Force actor to be updated to clients/demo net drivers */
UFUNCTION( BlueprintCallable, Category="Networking")
ENGINE_API virtual void ForceNetUpdate();

void AActor::ForceNetUpdate()
{
	UNetDriver* NetDriver = GetNetDriver();

	if (GetLocalRole() == ROLE_Authority)
	{
		// ForceNetUpdate on the game net driver only if we are the authority...
		if (NetDriver && NetDriver->GetNetMode() < ENetMode::NM_Client) // ... and not a client
		{
			NetDriver->ForceNetUpdate(this);

			if (NetDormancy > DORM_Awake)
			{
				FlushNetDormancy();
			}
		}
	}

	// Even if not authority, other drivers (like the demo net driver) may need to ForceNetUpdate
	if (UWorld* MyWorld = GetWorld())
	{
		if (FWorldContext* const Context = GEngine->GetWorldContextFromWorld(MyWorld))
		{
			for (FNamedNetDriver& Driver : Context->ActiveNetDrivers)
			{
				if (Driver.NetDriver != nullptr && Driver.NetDriver != NetDriver && Driver.NetDriver->ShouldReplicateActor(this))
				{
					Driver.NetDriver->ForceNetUpdate(this);
				}
			}
		}
	}
}
  • FlushNetDormancy가 호출되고, 다음 NetUpdate에서 해당 Actor가 Replication 대상으로 고려된다.
    • Actor가 단일 Frame에서 간헐적으로 1회성 Update를 발생하는 경우에 유용
  • Actor가 Flush/Awake 된 후에 다시 Dormant 설정을 하더라도 즉시 Dormant 상태가 되지 않는다.
    • 때문에 여러 Update를 전송할 수 있다.
    • 해당 Actor와 SubObject에서 Replicate 되어야 할 Updates가 없을 때까지 Replicate 한다.
  • 또한 DormancyHysteresis가 활성화 된 경우에도 Dormant 상태가 즉각 적용되지 않는다.
  • UActorHannel::ReadyForDormancy
    • 더보기
      bool UActorChannel::ReadyForDormancy(bool suppressLogs)
      {
      	// We need to keep replicating the Actor and its subobjects until none of them have
      	// changes, and would otherwise go Dormant normally.
      	if (!bIsInDormancyHysteresis)
      	{
      		for (auto MapIt = ReplicationMap.CreateIterator(); MapIt; ++MapIt)
      		{
      			if (!MapIt.Value()->ReadyForDormancy(suppressLogs))
      			{
      				return false;
      			}
      		}
      	}
      
      	if (DormancyHysteresis > 0 && Connection && Connection->Driver)
      	{
      		bIsInDormancyHysteresis = true;
      		const double TimePassed = Connection->Driver->GetElapsedTime() - LastUpdateTime;
      		if (TimePassed < DormancyHysteresis)
      		{
      			return false;
      		}
      	}
      
      	return true;
      }
  • FObjectReplicator::ReadyForDormancy
    • 더보기
      bool FObjectReplicator::ReadyForDormancy(bool bSuppressLogs)
      {
      	if (GetObject() == nullptr)
      	{
      		UE_LOG(LogRep, Verbose, TEXT("ReadyForDormancy: Object == nullptr"));
      		return true;		// Technically, we don't want to hold up dormancy, but the owner needs to clean us up, so we warn
      	}
      
      	// Can't go dormant until last update produced no new property updates
      	if (!bLastUpdateEmpty)
      	{
      		if (!bSuppressLogs)
      		{
      			UE_LOG(LogRepTraffic, Verbose, TEXT("    [%d] Not ready for dormancy. bLastUpdateEmpty = false"), OwningChannel->ChIndex);
      		}
      
      		return false;
      	}
      
      	if (FSendingRepState* SendingRepState = RepState.IsValid() ? RepState->GetSendingRepState() : nullptr)
      	{
      		if (SendingRepState->HistoryStart != SendingRepState->HistoryEnd)
      		{
      			// We have change lists that haven't been acked
      			return false;
      		}
      
      		if (SendingRepState->NumNaks > 0)
      		{
      			return false;
      		}
      
      		if (!SendingRepState->bOpenAckedCalled)
      		{
      			return false;
      		}
      
      		if (SendingRepState->PreOpenAckHistory.Num() > 0)
      		{
      			return false;
      		}
      
      		// Can't go dormant if there are unAckd property updates
      		for (FPropertyRetirement& Retirement : SendingRepState->Retirement)
      		{
      			if (Retirement.Next != nullptr)
      			{
      				if (!bSuppressLogs)
      				{
      					UE_LOG(LogRepTraffic, Verbose, TEXT("    [%d] OutAckPacketId: %d First: %d Last: %d "), OwningChannel->ChIndex, OwningChannel->Connection->OutAckPacketId, Retirement.OutPacketIdRange.First, Retirement.OutPacketIdRange.Last);
      				}
      				return false;
      			}
      		}
      	}
      
      	return true;
      }

Awake Method를 사용해야 하는 경우

  • Actor가 Awake 되면 Replicated Property의 값을 Shadow State를 Reinitialize한다.
    • Shadow State는 변경된 Property와 Replicated Property를 비교하는데 사용한다.
    • 때문에 Domant Actor의 Replicated Property 값을 변경 하더라도 Awake 과정에서 변경사항이 탐지되지 않는다.

Dormancy with Replication Graph

  • ReplicationGraph를 사용하더라도 Dormancy는 Default NetDriver와 동일하게 동작해야 하기 때문에,
    Project에서는 Actor의 Dormant/Awake 세팅과 FlushNetDormancy 호출이 동일하게 이루어진다.
  • ReplicationGraphNode에서 Actor List를 수집할 때 Dormant Actor가 반환되더라도 아래 함수에서 건너뛴다.
void UReplicationGraph::ReplicateActorListsForConnections_Default(UNetReplicationGraphConnection* ConnectionManager, FGatheredReplicationActorLists& GatheredReplicationListsForConnection, FNetViewerArray& Viewers)
{				
//-------Skip-------//
    // Skip if dormant on this connection. We want this to always be the first/quickest check.
    if (ConnectionData.bDormantOnConnection)
    {
        DO_REPGRAPH_DETAILS(PrioritizedReplicationList.GetNextSkippedDebugDetails(Actor)->bWasDormant = true);
        if (bDoCulledOnConnectionCount)
        {
            DormancyClassAccumulator.Increment(Actor->GetClass());
        }
        continue;
    }
//-------Skip-------//
}
  • ReplicationGraphNode는 Dormant Actor에 대한 특별한 Handling이 포함될 수 있다.
    • 이를 통해 Node의 Dormant Actor 처리 시간, 메모리 뿐 아니라 Actor List의 크기도 줄일 수 있다.
    • 예를 들어 ReplicationGraphNode_GridSpatialization2D의 경우,
      Dormant Actor은 Static으로 취급하고 Awake Actor은 Dynamic으로 취급하는 Handling이 포함되어 있다.
    • 이러한 Handling은 보통은 정지 및 Dormant이지만 가끔 Grid를 통과하는 Actor에 유용하다.

Debug

Log

  • LogNetDormancy 로그 카테고리를 활성화하여 Dormant 정보를 가져올 수 있다.
    • 상세도를 높이면 Actor의 NetDormancy가 Flush될 때처럼 더 자세한 정보가 기록된다.

Console Command

NetPriority

  • Unreal Engine의 Network Update는 Bandwidth 제한으로 모든 Actor의 Replicate를 보장하지 않는다.
    • 만약 Update 용량이 Bandwidth를 초과하면 자체적인 Load Balancing을 통해 NetPriority를 할당한다.
  • NetPriority가 높을수록 더 중요한 Actor이므로 더 많은 Bandwidth가 할당된다.

Actor의 NetPriority 구하기

/** 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;
  • Actor의 Replicate 빈도 차이는 NetPriority의 비율과 일치하다.
    • NetPriority가 3.0인 Actor는 1,0인 Actor보다 3배의 빈도로 Update 된다.
  • 일반적으로 Actor는 1.0, Pawn과 PlayerController는 3.0의 초기값을 가진다.

Current NetPriority 구하기

/**
 * Function used to prioritize actors when deciding which to replicate
 * @param ViewPos		Position of the viewer
 * @param ViewDir		Vector direction of viewer
 * @param Viewer		"net object" owned by the client for whom net priority is being determined (typically player controller)
 * @param ViewTarget	The actor that is currently being viewed/controlled by Viewer, usually a pawn
 * @param InChannel		Channel on which this actor is being replicated.
 * @param Time			Time since actor was last replicated
 * @param bLowBandwidth True if low bandwidth of viewer
 * @return				Priority of this actor for replication, higher is more important
 */
ENGINE_API virtual float GetNetPriority(const FVector& ViewPos, const FVector& ViewDir, class AActor* Viewer, AActor* ViewTarget, UActorChannel* InChannel, float Time, bool bLowBandwidth);

/**
 * Defines in NetworkDistanceConstants.h
 * CLOSEPROXIMITY: 500
 * NEARSIGHTTHRESHOLD: 2000
 * MEDSIGHTTHREHOLD: 3162
 * FARSIGHTTHRESHOLD: 8000
 */
float AActor::GetNetPriority(const FVector& ViewPos, const FVector& ViewDir, AActor* Viewer, AActor* ViewTarget, UActorChannel* InChannel, float Time, bool bLowBandwidth)
{
	if (bNetUseOwnerRelevancy && Owner)
	{
		// If we should use our owner's priority, pass it through
		return Owner->GetNetPriority(ViewPos, ViewDir, Viewer, ViewTarget, InChannel, Time, bLowBandwidth);
	}

	if (ViewTarget && (this == ViewTarget || GetInstigator() == ViewTarget))
	{
		// If we're the view target or owned by the view target, use a high priority
		Time *= 4.f;
	}
	else if (!IsHidden() && GetRootComponent() != NULL)
	{
		// If this actor has a location, adjust priority based on location
		FVector Dir = GetActorLocation() - ViewPos;
		float DistSq = Dir.SizeSquared();

		// Adjust priority based on distance and whether actor is in front of viewer
		if ((ViewDir | Dir) < 0.f)
		{
			if (DistSq > NEARSIGHTTHRESHOLDSQUARED)
			{
				Time *= 0.2f;
			}
			else if (DistSq > CLOSEPROXIMITYSQUARED)
			{
				Time *= 0.4f;
			}
		}
		else if ((DistSq < FARSIGHTTHRESHOLDSQUARED) && (FMath::Square(ViewDir | Dir) > 0.5f * DistSq))
		{
			// Compute the amount of distance along the ViewDir vector. Dir is not normalized
			// Increase priority if we're being looked directly at
			Time *= 2.f;
		}
		else if (DistSq > MEDSIGHTTHRESHOLDSQUARED)
		{
			Time *= 0.4f;
		}
	}

	return NetPriority * Time;
}
  • Base NetPriority에 Viewer와의 거리, 마지막 Replicate 이후 시간 등을 복합적으로 판단해 결정된다.
  • 만약 NetPriority를 Customize하고 싶다면 이 함수를 Override해야 한다.
    • 단, 이는 매우 높은 숙련도와 이해도를 요구한다.

Reference

NetRelevancy

  • Level 상의 Actor들 중 Server상에서 시야 안에 들어오는 Actor들이나 Client에 영향을 Actor들만 Replicate하는 방식
    • Runtime 중에 Spawn/Replicate 되는 Actor들은 Relevant 하지 않으면 Client에서 제거된다.
    • 제거된 Actor의 경우에는 Client에서 더이상 보이지 않는다.

Actor의 현재 Relevancy

/** 
 * Checks to see if this actor is relevant for a specific network connection
 *
 * @param RealViewer - is the "controlling net object" associated with the client for which network relevancy is being checked (typically player controller)
 * @param ViewTarget - is the Actor being used as the point of view for the RealViewer
 * @param SrcLocation - is the viewing location
 *
 * @return bool - true if this actor is network relevant to the client associated with RealViewer 
 */
ENGINE_API virtual bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const;

bool AActor::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
	if (bAlwaysRelevant || IsOwnedBy(ViewTarget) || IsOwnedBy(RealViewer) || this == ViewTarget || ViewTarget == GetInstigator())
	{
		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(IsHidden() && (!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);
}
  • Network Driver는 IsNetRelevantFor를 통해 Actor가 각 Connection과 Relevant한지를 판단한다.
    • Relevancy를 Customize하고 싶다면 이 함수를 Override해야 한다.
    • 다만 Override 작업은 높은 이해도와 난이도를 요구한다.
  • 참고로 Actor를 상속받은 Class 중 Pawn과 PlayerController는 위 함수를 Override하여 다른 로직으로 동작한다.

Actor Relevant 생성

/** Forces this actor to be net relevant if it is not already by default	 */
ENGINE_API virtual void ForceNetRelevant();

void AActor::ForceNetRelevant()
{
	if ( !NeedsLoadForClient() )
	{
		UE_LOG(LogSpawn, Warning, TEXT("ForceNetRelevant called for actor that doesn't load on client: %s" ), *GetFullName() );
		return;
	}

	if (RemoteRole == ROLE_None)
	{
		SetReplicates(true);
		bAlwaysRelevant = true;
		if (NetUpdateFrequency == 0.f)
		{
			NetUpdateFrequency = 0.1f;
		}
	}
	ForceNetUpdate();
}
  • Actor에서 ForceNetRelevant를 호출해 강제로 해당 Actor에 Relevancy를 부여할 수 있다.

Customize Relevancy Settings

  • Actor를 상속받은 Class는 사진의 옵션 또는 C++에서 Relevancy Setting을 Customize 할 수 있다.

bAlwaysRelevant

  • 모든 Client에서 조건 없이 Replicate 된다.

bNetUseOwnerRelevancy

  • 해당 Actor의 Owner에게 Relevant 할 때에 Replicate 된다.
  • 모든 Client에게 Replicate 되지 않고, Owner와 Relevant한 Client들에게만 Replicate 된다.

bOnlyRelevantToOwner

  • 해당 Actor의 Owner에게 Relevant 할 때에 Owner에게만 Replicate 된다.

Reference

'UE5 > Network' 카테고리의 다른 글

[Network] Remote Procedure Calls(RPCs)  (0) 2024.06.28
[Network] Property Replication  (1) 2024.06.28
[Network] Network Driver  (0) 2024.06.18
[Network] DemoNetDriver 및 Streamer  (0) 2024.06.17
[Network] Beacon  (0) 2024.06.17

+ Recent posts