Unreal Engine/네트워크

[UE] #2. Network Role

코딩하는상후니 2022. 10. 28. 18:23

 

 


 

 
언리얼의 멀티플레이 게임 환경에서,
생성된 Actor, Pawn 과 같은 Object 들은 어떻게 동작시켜야할까 ??
 
앞전에 언리얼은 서버-클라이언트 모델을 기반으로 동작한다고 했다.
클라이언트는 서버에게 요청하고 서버는 해당 데이터들을 연결된 클라이언트에게로 전달해야한다.
 
이를 기반으로 우리는 Game Object 들을 원하는 동작을 하게끔 코드를 작성해야하는데
서버 위에 존재하는 Object 와 클라이언트에 존재하는 Object 는 다른 방식으로 동작할 것이다.
 
언리얼에서는 Actor 의 동기화 방식을 'Role' 이란 개념으로 구분지어놨다.
 

 

 

 

간단하게 말해서,
네트워크 동기화를 위해 해당 Actor 의 역할에 대해 정의한 Enum 항목이다.
 
이후에 언급되겠지만,
Local ( 현재 프로세스 )Remote ( 원격 ) 의 관점에서 각각 다르다.
 
'Authority' 는 우리가 가장 중요하게 볼 기준이 되는 역할이다.
만약
Local = Authority 라면 Remote 에 정보를 전달하는 주체, 동기화 부분에 권한이 있는 Actor 가 된다.
우리가 일반적으로 코드를 구현할 때의 모습이다.
 
Authority 가 아니라면 자체적으로 실행되는 정보에 대해서는,
해당 정보를 동기화시키기 위해 Authority 역할인 Actor 를 소유한 서버에게 요청한다.
따라서, 이 때의 Remote 는 Authority 가 되겠다.
 
 
넘어가서 자세히 살펴보도록 하자.
 
 
 
 
 
 
 
 
 
 

* ENetRole

 
 
 
 

* ENetRole::ROLE_Authority

 
관리자의 역할을 나타낸다.
Authority 일 때 진행되는 Replicating 은 연결된 모든 클라이언트들에게 전달된다.
단, 서버에서는 실행되지 않는다.
 
또한,서버 위에 존재하는 Actor 는 항상 Authority 역할을 가진다.
 
 
 
 

* ENetRole::ROLE_SimulatedProxy

 
Actor 가 해당 역할로 수행된다면 제어 ( Control ) 할 수 있는 방법이 없다.
즉,
Listen 서버가 아닐 때, 나의 화면에 보이는 상대방 캐릭터는 해당 역할로 수행된다.
 
 
 
 

* ENetRole::ROLE_AutonomousProxy

 
만약 Actor 에게 주어진 역할이 AutonomousProxy 라면,
로컬 ( Controller 를 소유한 플레이어 ) 은 해당 Actor 를 제어 ( Control ) 가능하다.
즉,
나의 화면에 내가 컨트롤하고 있는 캐릭터는 autonomous proxy 역할로 주어진다.
 
클라이언트 위에서 내가 조종하는 캐릭터는 autonomous proxy 이다.
만약 서버 위에서 내가 캐릭터를 조종하고 있다면 ( Listen Server ) authority 이다.
그 때의 RemoteRole 은 autonomous proxy 일 것이다.
 
 
 
 

* ENetRole::ROLE_None

 
역할이 없다.
 
Actor 를 Replicated 설정하지 않았다면 클라이언트 위의 Actor ROLE = None 이다.
따라서, Authority Actor 를 가진 서버로부터 정보를 받을 수도 줄 수도 없다.
 
하지만, 서버 위에서의 Actor 역할은 항상 Authority 이다.
 
 
 
 
 
 
 
 
 
 
 
 
 

* Autonomous  /  Simulated 개념

 
공식 문서에 따르면,
 
기본적으로 서버는 Replicate Actor 를 매번 업데이트하지 않고
따로 AActor::NetUpdateFrequency 를 이용해 Replicate 하게 된다.
 
이 때,
해당 움직임을 계산하는 Update 문과 실제로 Replicate 로 캐릭터가 움직이는 Update 가 맞아 떨어지지 않을 수 있다고 나와있다.
무슨 말이냐면 플레이어가 다른 방향의 속도로 2번 동안 프레임 계산이 이루어지는 동안
NetUpdate 는 1번 동작함으로써, 움직임이 끊겨보이는 현상이 나타날 수 있다. ( 순간이동 )
 
이러한 현상을 방지하고자 'Simulated' 라는 것을 이용하게 되는데
위에서 예시로 든 캐릭터 움직임의 속도 ( Velocity ) 를 최근 값을 기준으로 이후의 값을 예상해서
나의 화면에 보여지는 다른 캐릭터의 움직임을 보간한다는 이야기이다.
 
이러한 이유로 내가 제어( Control ) 할 수 없는 캐릭터들은 SimulatedProxy 의 역할을 가지게 된다.
 
 
반대로,
AutonomousProxy 는 PlayerController 를 가지고 있음으로 플레이어가 조종할 수 있으며,
더 많은 정보를 플레이어의 Input 값으로 받기 때문에 캐릭터의 움직임을 나타내기에 수월하다.
좀 더 상위 버전인 셈이다.
 
 
 
 
 
 
 
 
 
 

* GetLocalRole( )  /  GetRemoteRole( )

 

 

ENetRole LocalRole = InPawn->GetLocalRole();

ENetRole RemoteRole = InPawn->GetRemoteRole();

 

GetLocalRole  현재 로컬이 해당 Pawn 을 동기화할 수 있는 역할 수준을 보여줌.
GetRemoteRole  :  Replicated Data 를 전달할 원격 수신지의 역할 수준을 보여줌.
( Describes how much control the remote machine has over the actor. )

 

 

 

 
ListenServer 에서의 LocalRole 은 서버 위에 존재하기 때문에 모두가 Authority 이고
반대의 RemoteRole 은 Listen 서버의 호스트만 AutonomousProxy 가 되고 나머지는 SimulatedProxy 가 된다.
 
 
플레이하는 클라이언트로서, LocalRole 은 AutonomousProxy 가 되고 나머지는 SimulatedProxy 가 된다.
반대의 RemoteRole 은 다른 클라이언트 모두 Authority 가 된다.
 
 
이러한 정보들을 조합해 ( Dedicated Server /  Listen Server 등 )
현재 인스턴스가 현재 어느 위에서 어떤 역할로 동작하는지 알아볼 수 있다.
 
 
 
 
 
 
 
 
 
 

* IsLocallyControlled( )  /  HasAuthority( )

 
 
멀티 플레이 게임 환경을 구현하다보면 해당 권한과 로컬 지역의 컨트롤러를 확인해
로컬이 클라이언트인 상황에서 Server->Client 로 가는 RPC 를 호출하거나
Server 에서의 동작을 정의해야할 때가 존재하는데
 
이 때, 위 2가지 함수를 사용해서 구별할 수 있다.
물론 상황에 따라서 다르게 파악할 수 있다.
 
 
 

* HasAuthority( )

 

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

 

=> 권한을 가지고 있는지 확인 ( ROLE_Authority )
 
해당 Actor 가 가진 역할이 서버 위에 있어 권한이 있는지 ( =Authority ) 확인하는 식별 함수.

 

 
 
 
 
 
 

* IsLocallyControlled( )

 

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

 

=> 해당 폰에서 작업을 수행하는 주체가 로컬의 Controller 인지 확인할 수 있음.
즉,
실행하고 있는 주체의 Controller 인지 확인하는 함수 이다.
 
 
 

 

 

 

AController 의 마지막 if 문에 정의되어있는 것처럼,
만약 Server 위에서 Pawn 의 IsLocalController 를 수행한다고 가정할 때,
Local == ROLE_Authority && Remote == ROLE_SimulateProxy 를 확인해서 구별할 수 있다.
즉, Listen Server 일 때를 말한다.
 
2번째 if 문의 상황은 클라이언트 위에서 구별한다.

 

 

 

 

 

 

 

 

 
 
 
위에서 IsLocalController( ) / HasAuthority( ) 각각의 개념들은 살펴봤다.
 
이 함수들을 이용해서 서버-클라이언트들의 동작을 구별할 수 있다.
궁금한 것은 도대체 어느 상황일 때 구별해서 쓰느냐 이다.
 
 
그전에 먼저 몇 가지 예로 만든 Replicate 동작 과정을 살펴보고
해당 상황들에 맞춰서 위 함수들과 이전에 배운 Replicate 사용한다 가정하고 결과를 예측해보자.
 
 
 
 
 
 

* Server-Client Replicate 동작 과정

 
 
대략적으로 크게 2가지 정도로 구별할 수 있다.
동기화되야할 작업이 서버 위에서 동작하는 상황 or 그렇지 않은 상황 이 그것이다.
 
 
 

1. 동기화되어질 작업이 서버 위에서 동작할 때

 

 

 

 
기본적으로 처음 Load 되어 생성되는 Actor 는 Server 에서 Spawn 되어진다.
자주 언급하지만, 서버 위에 존재하는 Actor 의 Role 은 Authority 이다.
 
이 때 Replicated Actor 라면,
다른 클라이언트들의 Local Role 은 SimulatedProxy or AutonomousProxy 가 되고 아니라면 None 이 된다.
 
Player1 은 Server 에서 Control 되는 캐릭터, Player2 는 Client 에서 Control 되는 캐릭터라고 가정한다.

 

 
 
 
위 내용을 상기하며 그림을 참고하자.
글자 색별로 위에서부터 상황을 나누겠다.
 
 

 

* 보라색  :  1번 상황

1. Detect Collision -> 2. Overlap Event -> 3. Replicate
 
Player2 가 움직여서 Item 과 충돌이 발생했다.
이 때, Player2, Item 모두 Replicated 이고 Item 의 BeginOverlap 이 발동했다고 가정할 때
 
OverlapEvent 는 Authority 를 가진 Item 에 의해서 실행된다.
이 상황을 '서버 위에서 실행되고 있다' 라고 말한다. ( On Server )
 
Authority 를 가진 Item 이 다른 클라이언트에서 보여지는 Item 을 동기화하기 위해 Replicate 를 수행한다.
OverlapEvent 안에서 실행되는 함수 구현에 이전에 배운 Replicate 변수를 조작할 수 있고
해당 Replicate 의 값이 변화할 때 OnRep_Func 을 이용해 Replicate 를 수행할 때 실행되는 Callback 함수를 이용할 수 있다.
 
이 상황은 Authority 를 가진 Item 이 OverlapEvent 를 사용하기에 HasAuthority( ) == true 인 상황이고
IsLocalController( ) == false 인 상황이다.
왜냐하면 결국 충돌 자체는 Client 에서 일어났기 때문이다.
반대로 Item 과 Player1 이 충돌되었을 때가 IsLocalController( ) ==true 인 상황이다.
 
 
 
 
 
 
 
 

* 파랑색  :  2번 상황

1. Move -> 2. Replicate -> 3. Simulate
 
이 상황은 Player Character 는 Movement Replicate 가 되고 있다는 것을 말하고 있다.
 
어느 캐릭터를 움직이느냐에 따라 과정이 조금 다른데
지금처럼 Player1 ( Server ) 가 움직이는 경우, 바로 다른 클라이언트에게 Replicate 되겠고
 
만약 Player2 ( Client ) 가 움직이는 경우,
Server 에 있는 Authority 역할인 Player2 의 캐릭터가 다른 클라이언트로 Replicate 하면서 동기화된다.
이 때, LocalController 는 Player2 캐릭터를 소유하고 있는 Client 가 되겠다.
 
이런 미묘한 차이를 잘 이해해야한다.
 
 
 
 
 
 
 
 

* 금색  :  3번 상황

1. Overlap Event -> 2. Can't Replicate
 
첫째 눈에 띄는 것은 Item 이 Replicated 되지 않았다.
동시에 Server 에서는 Authority 를 가지는 것을 볼 수 있다.
 
여기서 알 수 있는 것은 서버 위에 존재하는 Actor 는 Authority 를 가지고
Replicate 는 서버->클라이언트 만의 과정만을 가진다는 것이다.
 
따라서,
3번 상황에서 발생한 OverlapEvent 는 서버 위에서만 실행되고
다른 클라이언트들에게 전달할 방법이 없다.
 
그로 인해, 서버에서 발생된 충돌은 다른 클라이언트가 모른다.
Item 을 먹어 피를 채우거나 Item 을 장착하는 것이 다른 클라이언트들에게 보이지 않는다.
 
 
또한 한 가지 더 알아야하는 사실은
 
만약 Listen Server 일 경우,
Server 에는 Replicate 가 되지 않기 때문에 따로 처리를 해주어야한다는 의미가 된다.
 
이 때, IsLocalController == true 를 이용해서
Authority 역할인 객체를 통해서 작업이 수행될 때 Listen Server 의 Actor 의 동작을 정의할 수 있다.
 
 
 
 
 
 
 
 
 
 
 
 
 

2. 동기화되어질 작업이 서버가 아닌 곳에서 동작할 때

 
간단하게 말해서,
클라이언트의 개별 호출 작업들을 동기화해야하는 상황들을 말한다.
 
클라이언트의 상황을 서버에게 요청하는 대표적인 방법은 'RPC' 이다.

 

 

 

이전에 예시된 서버 위에서 동작할 때와 같은 환경이다.
다만, 상황이 약간 다른데
Client 의 Player 가 Replicated Item 을 키보드 'E' Key 를 눌러서 장착한다고 가정한다.
 
 
 
* 과정
1. Item Pick Up -> 2. RPC Request -> 3. RPC to Client By Server
 
 
Client 의 Player 가 아이템 장착을 위해 'E' 버튼을 누르면 Player3 에는 아이템이 장착된다.
 
이 때, RPC 를 사용하지 않으면 Player3 화면에만 장착됨이 보이고 나머지는 보이지 않는다.
 
서버도 알 수 없다.
왜냐하면,
아이템이 Replicated 라고 할지라도 Player3 에 장착된다는 사실 ( Socket Attach ) 은 알 수 없다.
 
어떤 것을 Replicate 지정하기에도 애매하다.
서버가 알아야하는 정보가 많을수록 Replicate 해야하는 것이 많아지기에 매우 낭비가 될 수 있다.
 
이 때, RPC 의 사용은 적절하다.
단지 일회성 이벤트를 수행하기만 하면 되는 과정이기 때문에
서버의 권한으로 연결되어있는 모든 클라이언트들에게 해당 Procedure 를 수행하는 요청을 보낸다.
 
결과적으로 보여지는 모든 Player3 캐릭터는 Item 을 장착한 상태가 된다.
 
 
이 때에도 HasAuthority == true 를 이용해 서버 위에서의 동작과
HasAuthority==false 를 이용해 클라이언트 위 일때에는 RPC 를 사용함으로써 동기화를 수행할 수 있다.
 
 
 
이렇게 개별적으로 발생할 수 있는 함수들을 실행할 때
서버에게도 알려줘 다른 클라이언트들에게 동기화해줄 수 있는 방법이 RPC 가 되겠다.
 
 
RPC 의 종류에도 NetMulticast, Server, Client 에 의한 상황으로 나뉘어진다.
또, RPC 사용 시 필요 조건은 해당 객체가 '반드시' Replicated 해야하는 것이다.
( RPC 에 관한 자세한 추후 다룰 예정이다. )

 

 

 

 

 

 

 

 

 


 

UE4-Network-Compendium-by-cedric-exi-neukirchen.pdf
2.11MB

 

 

 
 
 
 

 

 

 

 

 

 

 

 

'Unreal Engine > 네트워크' 카테고리의 다른 글

[UE] #1. Replication  (0) 2022.10.24