개인 강의/Unreal Engine 멀티플레이어 게임 개발

Unreal Engine 멀티플레이어 게임 개발 1-5

jh009 2026. 7. 29. 16:04

1-5. NetRole

Authority와 Proxy

서버에 스폰 된 액터가 가진 NetRole 속성 값은 언제나 Authority.

서버에 스폰 된 액터에서 수행될 로직은 권한을 가지고 있음 뜻함.

게임에 중대한 영향을 끼치는 로직은 NetRole이 Authority일 때 수행해야 함.

 

NetRoleAuthority인 액터가 클라이언트로 복제되었을 때, 클라이언트에 복제된 액터의 NetRole 속성 값Proxy.

(허상 정도로 생각하면 편함)


로컬 롤과 리모트 롤

게임에 중대한 영향을 끼치는 로직을 작성하기 위해,

해당 액터가 현재 어느 PC에 스폰 되어서 로직이 돌고 있는지 구분해야 함.

 

로컬 롤(Local Role)

현재 동작하는 컴퓨터에서의 롤

 

리모트 롤(Remote Role)

커넥션으로 연결된, 반대편 컴퓨터에서의 롤


NetRole의 종류

 

None

  • 보통의 경우, 레플리케이션 되지 않는 액터를 뜻함. (무조건적이진 않음)
  • ex) LocalRole은 Authority이고 RemoteRole이 None이라면
    서버에서 스폰 되고, 클라쪽으로 레플리케이션 되지 않는 액터라고 볼 수 있음.

Authority

  • 게임에 중대한 영향을 끼칠 수 있는 권한을 가진 액터를 뜻함.
  • 서버에서 스폰된 액터가 LocalRole으로 Authority를 가질 수 있음.
  • ex) GameMode

Autonomous Proxy

  • Authority 액터의 복제본.
    서버로부터 데이터를 수신 받아서 동기화 가능, 서버로 송신도 가능.
  • ex) PlayerController

Simulated Proxy

  • Authority 액터의 복제본.
    서버로부터 수신 받아서 동기화 당하기만 함.
  • ex) 내 화면에 보이는 친구의 PlayerCharacter


매크로를 통한 NetRole 출력

New C++ 클래스 > Pawn 부모 클래스 > “CXPawn”

Path: Player (경로 뒤에 Player 추가)

 

BP_GameModeBase > Details > Default Pawn Class에 CXPawn 지정

// ChatX.h

...

class ChatXFunctionLibrary
{
public:
	...

	static FString GetRoleString(const AActor* InActor)
	{
		FString RoleString = TEXT("None");

		if (IsValid(InActor) == true)
		{
			FString LocalRoleString = UEnum::GetValueAsString(TEXT("Engine.ENetRole"), InActor->GetLocalRole());
			FString RemoteRoleString = UEnum::GetValueAsString(TEXT("Engine.ENetRole"), InActor->GetRemoteRole());
			
			RoleString = FString::Printf(TEXT("%s / %s"), *LocalRoleString, *RemoteRoleString);
		}

		return RoleString;
	}
	
};
// CXPawn.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "CXPawn.generated.h"

UCLASS()
class CHATX_API ACXPawn : public APawn
{
	GENERATED_BODY()
	
protected:
	virtual void BeginPlay() override;

	virtual void PossessedBy(AController* NewController) override;

};


// CXPawn.cpp


#include "CXPawn.h"
#include "ChatX.h"


void ACXPawn::BeginPlay()
{
	Super::BeginPlay();

	FString NetRoleString = ChatXFunctionLibrary::GetRoleString(this);
	FString CombinedString = FString::Printf(TEXT("CXPawn::BeginPlay() %s [%s]"), *ChatXFunctionLibrary::GetNetModeString(this), *NetRoleString);
	ChatXFunctionLibrary::MyPrintString(this, CombinedString, 10.f);
}

void ACXPawn::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	FString NetRoleString = ChatXFunctionLibrary::GetRoleString(this);
	FString CombinedString = FString::Printf(TEXT("CXPawn::PossessedBy() %s [%s]"), *ChatXFunctionLibrary::GetNetModeString(this), *NetRoleString);
	ChatXFunctionLibrary::MyPrintString(this, CombinedString, 10.f);
}

 

BP_GameModeBase > Details > Default Pawn Class에 CXPawn을 지정

 

로그를 확인했다면 BP_GameModeBase > Default Pawn Class는 None으로 돌려놓기


NetRole을 활용하여 정의된 중요 함수

게임에 중대한 영향을 끼치는 데미지나 스폰 같은 로직은 서버 컴퓨터에서 실행되어야 함.

이를 위해 언리얼은 AActor::HasAuthority() 함수를 제공

 

입력 관련 로직이나 UI는 Autonomous Proxy에서 수행되어야 함.

이를 위해 AController::IsLocalController()  APawn::IsLocallyControlled() 함수가 제공.

 

[참고] AActor::HasAuthority() 함수

// AActor.cpp

FORCEINLINE_DEBUGGABLE bool AActor::HasAuthority() const
{
	return (GetLocalRole() == ROLE_Authority);
}

HasAuthority → 서버 로직을 체크하기 위함.
주의사항
리슨서버 → 플레이어 임과 동시에 서버 역할도 겸함.
Standalone 상태에서도 Ture가 나옴.
HasAuthority → Ture라고 해서 무조건 멀티플레이 중인 서버는 아님.

 

AController::IsLocalController() 함수

// APawn.cpp

bool APawn::IsLocallyControlled() const
{
	return ( Controller && Controller->IsLocalController() );
}


// AController.cpp

bool AController::IsLocalController() const
{
	const ENetMode NetMode = GetNetMode();

	if (NetMode == NM_Standalone)
	{
		// Not networked.
		return true;
	}
	
	if (NetMode == NM_Client && GetLocalRole() == ROLE_AutonomousProxy)
	{
		// Networked client in control.
		return true;
	}

	if (GetRemoteRole() != ROLE_AutonomousProxy && GetLocalRole() == ROLE_Authority)
	{
		// Local authority in control.
		return true;
	}

	return false;
}

각 액터에 따른 특징

  • 게임 모드는 HasAuthority() 함수를 호출할 필요가 없음.
  • 폰은 Authority, Autonomous Proxy, Simulated Proxy가 혼재되어 있음.
  • UI 관련 로직은 클라이언트에서만 수행.

로컬 롤과 리모트 롤 2가지인 이유

 

로컬 롤만 있었다면 분간하기가 어려운 케이스

예시: 서버에 접속한 플레이어의 캐릭터 A와 서버에서 스폰 된 캐릭터 B가 있다고 가정. (A와 B의 로컬 롤은 Authority)

 

  • 로컬 롤만으로는 서버에 접속한 플레이어의 캐릭터인지, 서버에서 스폰된건지 구분 불가능.
  • 만약 리모트 롤까지 알게된다면, 서버에 접속한 플레이어의 캐릭터는 Autonomus Proxy.
  • 서버에서 스폰된 캐릭터는 None이므로 구분할 수가 있어짐.

 

구분이 가능해야 서버에서 호출되고 오너 클라이언트에서 실행될 RPC를 구현이 가능.

멀티플레이에서 각 액터들을 좀 더 정확하게 분류하게 위해 2가지로 나눔.