https://www.unrealengine.com/ko/tech-blog/replication-graph-overview-and-proper-replication-methods
- 일반적으로 Replicate되는 Actor들은 매 Tick마다 해당 Actor가 Replicate될지 말지 판단을 한다.
- 게임이 클수록, 동시접속자가 많을수록 CPU 병목현상이 발생하기 쉬워짐
- Replication Graph는 Actor 단위의 Replicate 판단 여부를 Node 단위로 묶어서 비용을 감소시킨다.
동작 방식
- 기본적인 동작 방식은 이전의 Actor Replicate와 크게 다르지 않다.
- 특정 공간 안의 Actor를 별도로 관리(Spatial Partitioning)
- 대상 클라이언트로부터 먼 거리의 Actor를 제외 (Relevancy Culling)
- Replicate 빈도 관리 (Frequency Control)
ReplicationGraphNode
- 동일한 조건으로 Replicate 되어야 할 Actor를 하나로 묶어주는 단위
- BaseClass인 UReplicationGraphNode는 Pure Virtual Function이 있어서 반드시 이를 상속 받은 Class를 써야 한다.
- 최소 아래 함수들은 반드시 Implement 되어야 한다.
/** Override this function to initialize the per-class data for replication */
//Initialize UReplicationGraph::GlobalActorReplicationInfoMap.
virtual void InitGlobalActorClassSettings();
/** Override this function to init/configure your project's Global Graph */
//Instantiate new UGraphNodes via ::CreateNewNode. Use ::AddGlobalGraphNode if they are global (for all connections).
virtual void InitGlobalGraphNodes();
//Route actor spawning/despawning to the right node. (Or your nodes can gather the actors themselves)
virtual void RouteAddNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo, FGlobalActorReplicationInfo& GlobalInfo);
virtual void RouteRemoveNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo);
/** Override this function to init/configure graph for a specific connection. Note they do not all have to be unique: connections can share nodes (e.g, 2 nodes for 2 teams) */
//Initialize per-connection nodes (or associate shared nodes with them via ::AddConnectionGraphNode)
virtual void InitConnectionGraphNodes(UNetReplicationGraphConnection* ConnectionManager);
- Node 안에 별도의 Child Node를 가질 수 있다.
ReplicationGraphNode_ActorList
- 간단한 Actor 목록을 관리
- 복잡한 Frequency나 Relevancy 로직 없이 관리 된다.
- 소규모 목록에서 사용할 때 유용
ReplicationGraphNode_ActorListFrequencyBuckets
- Replicate 빈도에 따라 Actor들을 Bucket 단위로 관리
- 다양한 간격으로 업데이트가 필요한 Actor를 관리할 때 유용
ReplicationGraphNode_AlwaysRelevant
- 위치, 상태와 관계 없이 모든 Client에게 항상 Replicate 되어야 하는 경우
- GameMode, Controller, GameState와 같은 GamePlay에 주요한 Actor에게 적합
ReplicationGraphNode_AlwaysRelevant_ForConnection
- AlwaysRelevant와 유사하지만 그 대상을 특정 Client에게만 제한할 수 있다.
- 특정 Player에게 필수적인 Actor를 Replicate할 때 적합
ReplicationGraphNode_ConnectionDormancyNode
- 비활성화 될 여지가 있는 Actor들에 적합
- 또는 배경의 Actor와 같이 업데이트를 제한하여 자원을 최적화 하는데 적합
ReplicationGraphNode_DormancyNode
- Actor가 활성화 될 때 모든 Client에 Replicate되어야 하는 경우
- 간헐적으로 활성화 되는 Actor들에게 유용
ReplicationGraphNode_DynamicSpatialFrequency
- 공간적 조건에 따라 Replicate 빈도를 동적으로 조절
- Player의 이동이나 게임 내 Event로 인해 Relevancy가 자주 변경되는 환경에 이상적
ReplicationGraphNode_GridSpatialization2D
- World를 Grid로 나누고, Grid 위치에 따라 Replicate를 관리
- 넓은 오픈월드 게임에서 플레이어 주변 Actor만 업데이트하여 네트워크 트래픽 최적화
ReplicationGraphNode_TearOff_ForConnnection
- Tear Off(서버에서 어느정도 분리된 상태)된 Actor들을 관리
- 해당 Actor들을 새로운 Client에 적절하게 Replicate 되도록 보장
- Actor가 급격하게 변경되는 빠른 페이스의 액션이나 파괴 시나리오에서 사용됨.
ReplicationGraph
- Replication Driver의 기능을 확장한 Class
- 다수의 ReplicationGraphNode를 관리한다.
작업 방식
Node 추가
- 기존에 만들었던 ReplicationGraphNode를 InitGlobalGraphNodes에서 생성, 추가한다.
void UBasicReplicationGraph::InitGlobalGraphNodes()
{
// -----------------------------------------------
// Spatial Actors
// -----------------------------------------------
GridNode = CreateNewNode<UReplicationGraphNode_GridSpatialization2D>();
GridNode->CellSize = 10000.f;
GridNode->SpatialBias = FVector2D(-UE_OLD_WORLD_MAX, -UE_OLD_WORLD_MAX);
AddGlobalGraphNode(GridNode);
// -----------------------------------------------
// Always Relevant (to everyone) Actors
// -----------------------------------------------
AlwaysRelevantNode = CreateNewNode<UReplicationGraphNode_ActorList>();
AddGlobalGraphNode(AlwaysRelevantNode);
}
Actor 추가
- AddNetworkActor에서 RouteAddNetworkActorToNodes를 호출
- RouteAddNetworkActorToNodes에서 등록된 Node를 통해 NotifyAddNetworkActor로 Actor 등록
void UReplicationGraph::AddNetworkActor(AActor* Actor)
{
LLM_SCOPE_BYTAG(NetRepGraph);
QUICK_SCOPE_CYCLE_COUNTER(UReplicationGraph_AddNetworkActor);
if (IsActorValidForReplicationGather(Actor) == false)
{
return;
}
if (NetDriver && !NetDriver->ShouldReplicateActor(Actor))
{
return;
}
bool bWasAlreadyThere = false;
ActiveNetworkActors.Add(Actor, &bWasAlreadyThere);
if (bWasAlreadyThere)
{
// Guarding against double adds
return;
}
ensureMsgf(!Actor->bNetTemporary, TEXT("ReplicationGraph does not support bNetTemporary. Actor: %s has bNetTemporary set."), *Actor->GetPathName());
// Create global rep info
FGlobalActorReplicationInfo& GlobalInfo = GlobalActorReplicationInfoMap.Get(Actor);
GlobalInfo.bWantsToBeDormant = Actor->NetDormancy > DORM_Awake;
RouteAddNetworkActorToNodes(FNewReplicatedActorInfo(Actor), GlobalInfo);
}
void UReplicationGraph::RouteAddNetworkActorToNodes(const FNewReplicatedActorInfo& ActorInfo, FGlobalActorReplicationInfo& GlobalInfo)
{
// The base implementation just routes to every global node. Subclasses will want a more direct routing function where possible.
for (UReplicationGraphNode* Node : GlobalGraphNodes)
{
Node->NotifyAddNetworkActor(ActorInfo);
}
}
Node 탐색
- 매 Tick마다 ServerReplicateActors를 호출
- 함수 내부에서 조건에 맞는 Node를 FNewReplicatedActorInfo로 검색
/** This is the struct we use to push new replication actors into the graph. "New" doesn't mean "newly spawned" it means "new to the graph". FIXME: Please suggest a better name! */
struct FNewReplicatedActorInfo
{
explicit FNewReplicatedActorInfo(const FActorRepListType& InActor) : Actor(InActor), Class(InActor->GetClass())
{
StreamingLevelName = GetStreamingLevelNameOfActor(Actor);
}
explicit FNewReplicatedActorInfo(const FActorRepListType& InActor, FName OverrideLevelName)
: Actor(InActor)
, StreamingLevelName(OverrideLevelName)
, Class(InActor->GetClass())
{
}
AActor* GetActor() const { return Actor; }
REPLICATIONGRAPH_API static FName GetStreamingLevelNameOfActor(const AActor* Actor);
FActorRepListType Actor;
FName StreamingLevelName;
UClass* Class;
};
설정 방법
ini 파일 설정
- Project의 Default.ini에서 아래와 같이 ReplicationDriverClassName 추가
[/Script/OnlineSubsystemUtils.IpNetDriver]
ReplicationDriverClassName="/Script/ProjectName.ClassName"
Instance 반환 함수 Bind
UReplicationDriver::CreateReplicationDriverDelegate().BindLambda([](UNetDriver* ForNetDriver, const FURL& URL, UWorld* World) -> UReplicationDriver*
{
return NewObject<UMyReplicationDriverClass>(GetTransientPackage());
});
NetReplicationGraphConnection
- 각 Client 연결에 대한 Replicate Data를 관리
- ReplicationGraph가 ReplicationDriver를 상속받아 NetDriver의 역할을 한다면,
NetReplicationGraphConnection은 ReplicationConnectionDriver를 상속받아 NetConnection의 역할을 한다.
- ReplicationGraph가 ReplicationDriver를 상속받아 NetDriver의 역할을 한다면,
- ReplcationGraph에서 Replication이 결정 된다면,
NetReplcationgraphConnection은 해당 Client에게 최적화 된 데이터를 전달해준다.
일반적인 사용 기준
- Actor의 위치에 따라 Group을 나눈다.
- 비활성화된 Actor를 식별하여 별도의 목록으로 관리한다.
- Character가 주워서 들고 다닐 수 있다면, 소유자와 같이 업데이트 한다.
- 모든 Client가 Replicate 받는 특수 목록을 만든다.
- 특정 Client에 Relevancy를 갖는 특수 목록을 만든다.
'UE5 > Network' 카테고리의 다른 글
[Network] Property Replication (1) | 2024.06.28 |
---|---|
[Network] Network Property (0) | 2024.06.25 |
[Network] Network Driver (0) | 2024.06.18 |
[Network] DemoNetDriver 및 Streamer (0) | 2024.06.17 |
[Network] Beacon (0) | 2024.06.17 |