반응형

 

언리얼은 빌트인 된 멀티플레이 기능을 가지고 있다.
멀티플레이에선 여러개의 클라이언트가 동시에 실행되는데, 각 클라이언트가 서로 대화할 수 있고, 일관적인 하나의 세계를 공유해야 한다.

언리얼 네트워크 모델에서는, 플레이어는 서버에 접속하고, 서버는 권위있는 월드를 보유한다. 서버에서 무언가 변경되면, 복제Replicate라는 프로세스로 해당 변경 사항을 다른 클라이언트에 반영한다.

언리얼 복제 시스템 덕분에 소켓, 직렬화 등 복잡한 부분을 신경쓰지 않고 멀티플레이 게임을 쉽게 개발할 수 있다.


Net Mode

 

ENetMode UWorld::GetNetMode() const;


Net Mode는 월드(UWorld)의 프로퍼티이며, 다음 네가지 값 중 하나를 가진다.

 

NM_Standalone
NM_DedicatedServer
NM_ListenServer
NM_Client



다음과 같은 기준으로 넷 모드를 구별할 수 있다.

  1. 게임을 플레이할 수 있는가? 
    (GameInstance에 LocalPlayer이 있고, 플레이어의 입력을 처리하며 월드를 렌더링하는가?)

  2. 서버인가?
    (GameInstance에 GameMode 액터가 포함된, 권위있는 버전의 월드 복사본을 가지고 있는가?)
  3. 클라이언트에 대해 열려있는가?
     (자신이 서버이고, 다른 클라이언트가 자신에게 참여할 수 있는가?)


해당 기준에 대해 4가지 넷 모드는 각각 다음과 같다.

 

NM_Standalone  // 1, 2, x
NM_DedicatedServer  // x, 2, 3
NM_ListenServer  // 1, 2, 3
NM_Client  // 1, x, x

 


게임을 시작하면 게임의 생명 주기와 함께하는 게임 인스턴스 객체를 얻게 된다. 

이후, 게임은 서버 주소 혹은 맵 이름의 주소 정보를 담은 URL을 찾고, 

이를 이용하여 맵을 로드하여 월드(UWorld)를 생성한다.

월드의 NetMode는 게임 인스턴스가 시작된 방식에 따라 달라지는데, 이는 다음과 같다.

NM_Standalone

  • 게임 인스턴스가 로컬로 맵을 로드한 경우.
    게임 인스턴스는 서버이자 클라이언트지만, 단일 구성으로 실행되므로, 다른 클라이언트가 참여할 수 없다.
    URL은 맵 이름 혹은 경로

  • 로컬머신에서 실행 원격머신에서 클라이언트를 받지 않는 서버

 

NM_DedicatedServer

  • LocalPlayer나 뷰포트가 없는 게임의 인스턴스
    플레이어가 클라이언트로 연결할 수 있는 서버 전용 콘솔 응용 프로그램

  • 로컬 플레이어 없이 사운드, 그래픽, 사용자 입력, 기타 플레이어 관련 기능을 제거한 전용 서버


NM_ListenServer

  • 맵을 로컬로 연결하지만, 경로에 ?Listen 옵션을 추가하여 로드한 경우
    StandAlone과 동일하지만, 다른 클라이언트가 참여할 수 있다.
    URL은 맵 경로?Listen

  • 로컬 플레이어가 있는 서버로, 원격 플레이어에게도 접속을 허락함

 

NM_Client

  • 게임 인스턴스가 원격 서버에 연결된 경우.
    URL은 아이피 주소

  • Dedicated / Listen 서버로 접속되는 클라이언트. 서버 측 로직은 실행되지 않음

프레임 워크

 

① 서버에만 존재

✔ Game Mode

서버에만 존재하며 게임의 규칙을 설정.
굳이 클라이언트가 알 필요 없는 정보들을 저장하는데 유용.
ex) 게임 규칙

  • Event OnPostLogin : 서버에 새 플레이어가 추가될 시 제일 먼저 실행되는 이벤트

② 서버와 각 클라이언트에 존재

✔ Game State

모든 플레이어 간의 공유가 필요한 정보를 보관하고 모든 클라이언트에게 공유되는 클래스. (Replicated)
ex) 팀 점수, 접속된 플레이어 목록

 

✔ Player State

접속된 클라이언트마다 현재 상태값을 포함하고 있는 Player State 객체를 갖고 있음.
Pawn에 할당되지만, Pawn이 소멸되거나 리스폰되어도 사라지지 않음.
ex) K/D/A

 

✔ Player Controller

각 클라이언트는 하나의 Player Controller를 가지고 Pawn을 제어함
서버는 모든 클라이언트의 Player Controller를 가지고 있음.

 

 ③ 클라이언트에만 존재

✔ HUD, UMG Widget

User Interface를 표시

ex) UI


개념

✔ Replication (복제)

변수 및 함수의 동기화.
서버에서 Replication 해주지 않으면 클라이언트에 반영되지 않음.
중요한 로직은 서버에서 실행하고, 결과만 클라이언트에 뿌려주는 방식이 일반적.

Reliable : 반드시 실행되는 함수. 필수 로직이 아닌 이펙트 등에서는 꺼놔도 됨.
Multicast : 모든 클라이언트에게 뿌려줌.
Switch Has Authority : 서버인지 클라이언트인지 따지는 분기문
RepNotify : 변수가 Repicated 될 때마다 실행되는 함수를 생성
UFUNCTION(Server) : 서버에서만 실행되는 RPC 함수 지정자
UFUNCTION(Clinet) : 클라이언트에서만 실행되는 RPC 함수 지정자

 

✔Game Instance

엔진 시작 시 생성되고 종료될 때까지 소멸되지 않는 클래스.
따라서 지속성 데이터를 저장하기 좋지만, 서버와 각 클라이언트마다 따로 존재하고 통신하지 않는다는 단점이 있음.


복제 시스템의 기반 

 

언리얼의 서버와 모든 클라이언트는 각각의 게임 인스턴스와 월드를 가진다. 

이 각각의 클라이언트를 일관되게 만들기 위해 복제를 통해 동기화하는데, 이를 실현하기 위해 복제 시스템은

UNetDriverUNetConnectionUChannel이라는 세가지 중요한 클래스에 의존한다.

데디 케이트 서버와 두개의 클라이언트가 있다고 가정해보자. 

세 프로세스 각각에는 엔진 객체(UGameEngine)가 있다.
서버를 부팅하면 NetDriver가 생성되고, 원격 프로세스의 메세지 수신이 시작된다.(InitListen() 함수 호출)
클라이언트를 부팅하면 NetDriver가 생성되어 서버에 연결 요청을 보낸다.(InitConnect() 함수 호출)

서버와 클라이언트의 NetDriver가 연결되면, NetDriver 내의 NetConnection이 설정된다.
서버에는 연결된 각 클라이언트마다 하나씩 NetConnection을 가지고, (클라이언트가 3개면 NetConnection도 3개)
클라이언트는 서버에 대한 연결을 나타내는 하나의 NetConnection을 가진다.

각각의 NetConnection은 다양한 채널을 가지는데, 일반적으로 UControlChannel과 UVoiceChannel, 그리고 복제되는 모든 액터당 하나씩 가지게 되는 ActorChannel이 있다.
이는 복제가 엑터 단에서 이루어진다는 복제 시스템의 주요 특성을 보여준다.

액터를 모든 프로세스에 대해 동기화하기 위해, 해당 액터를 복제할 수 있도록 구성한다.(bReplicates = true)
복제할 수 있는 액터가 특정 플레이어와 연관된 것으로 간주되면,(AActor::IsNetRelevantFor() == true)
서버는 해당 플레이어의 NetConnection에서 ActorChannel을 열고, 서버와 클라이언트는 해당 채널을 통해 해당 액터에 대한 정보를 교환한다.

액터 복제

액터가 클라이언트에 복제되는 경우, 세가지 중요한 일이 발생할 수 있다.

Lifetime

액터의 수명이 서버와 클라이언트 간에 동기화되어 유지된다. 서버에서 복제되는 액터를 스폰하면 클라이언트에서도 생성되고, 액터가 서버에서 소멸하면, 클라이언트에서도 자동으로 소멸한다.

 

프로퍼티 복제

액터에 복제 플래그가 지정된 멤버 변수가 있는 경우, 해당 변수가 서버에서 변경되면 새로운 값이 클라이언트에 전파된다.

 

RPC

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/rpcs-in-unreal-engine?application_version=5.2

 

원격 프로시저 호출로, 다음 세가지 타입이 있다.

  • Multicast RPC
    서버에서 해당 함수를 호출하면 서버는 해당 액터가 현재 복제되고 있는 모든 클라이언트에 메세지를 보내, 클라이언트가 해당 함수를 호출하게 한다.

  • Server RPC
    서버에서 해당 클라이언트에게 함수를 호출하도록 한다.

  • Client RPC
    클라이언트에서 서버에게 해당 함수를 호출하도록 한다.

 

 

소유권(Ownership)


모든 액터는 소유자Owner로 지정된 다른 액터를 가질 수 있다.
일반적으로 생성 시 소유자를 지정하지만,(UWorld::SpawnActor() 호출 시, 해당 함수의 파라미터 지정) 

런타임에 지정할 수도 있다.(AActor::SetOwner())

플레이어 컨트롤러는 소유권과 관련하여 특히 중요한데, 일반적으로 각각의 UNetConnection은 플레이어를 나타내며(UNetConnection is UPlayer), 플레이어가 완전히 로그인 되면, 이와 연결된 플레이어 컨트롤러를 갖게 된다.
서버의 관점에서 보면, UNetConnection은 해당 플레이어 컨트롤러를 소유하고, 더 나아가 플레이어 컨트롤러로 추적할 수 있는 모든 액터를 소유한다.

예를 들어, 플레이어 컨트롤러는 APlayerState와 APawn객체를 자동으로 소유하는데, 만약 플레이어 컨트롤러가 소유하는 캐릭터가 무기를 소환하고, 서버가 해당 무기가 누구 소유인지 알고 싶다고 가정하면, 서버는 무기 액터의 소유자 참조 체인을 따라가며 플레이어 컨트롤러를 찾고, 최종적으로 플레이어 컨트롤러의 소유자인 UNetConnection 객체를 찾게 되고, 어느 Client Connection에 속해 있는지 알아내게 된다.

 

액터 복제 플래그 지정

액터를 복제하도록 등록하려면, 생성자에서 bReplicates = true를 지정해 주거나, AACtor::SetReplicates 함수를 호출하여 런타임에 복제를 켜거나 끌 수 있다.
bReplicates = true가 되면, 해당 액터가 복제에 적합해 지고, 서버는 해당 액터를 클라이언트에 복제하기 위해, 접속한 모든 플레이어의 NetConnection에서 해당 액터를 위한 새로운 ActorChannel을 열게 된다.

연관성 Relevancy(연관성)

https://docs.unrealengine.com/5.0/ko/actor-relevancy-and-priority-in-unreal-engine/

액터의 연관성에 따라, 어떤 연결이 언제 일어날지 결정된다.
액터가 복제에 적합할 때, 서버의 UNetDriver 객체는 해당 액터가 각각의 클라이언트와 연관이 있는지 확인한다.(AACtor::IsNetRelevantFor())
일부 액터는 bAlwaysRelevant == true인데, 복제 자격이 있는 한, 모든 클라이언트에 복제된다는 의미이다. 예를들어 GameState나 PlayerState는 bAlwaysRelevant == true이다.

소유권은 연관성에 있어 중요한 역할을 하는데, 특정 플레이어가 소유하거나 선동하는 액터는 항상 해당 클라이언트와 연관된 것으로 간주된다. (해당 액터가 어떤 플레이어에게 소유되는지는 AActor::GetNetOwner로 구할 수 있다.)
플레이어 컨트롤러와 같은 일부 액터는 소유자에게만 연관되도록 구성되어 있으므로, 소유자가 아닌 다른 클라이언트엔 복제되지 않는다.

소유자로부터 연관성을 상속하도록 액터를 구성할 수도 있다. (bNetUseOwnerRelevancy = true)

연관성 기본 동작 규칙

한 액터가 이러한 특수 플래그 중 어느 것도 설정되지 않고, 클라이언트가 해당 액터를 소유하지 않은 경우, 기본 동작은 연관성을 해당 클라이언트의 플레이어와의 거리를 기반으로 정한다.액터에서 플레이어까지의 제곱 거리가 NetCullDistanced보다 작으면 액터는 해당 플레이어와 연관이 있다.

거대한 레벨에서 한 플레이어는 해당 레벨의 작은 일부분만 볼 수 있는데, 레벨에 있는 다른 액터는 대부분 보이지도, 플레이어에게 중대한 영향을 끼치지 않는다. 따라서 서버가 보이는 것으로, 또는 클라이언트에 영향을 끼칠 수 있다고 여기는 액터 세트는 그 클라이언트에 대해 연관성이 있는 액터 세트라 여기게 된다.
언리얼 네트워크 코드의 대역폭 최적화는, 서버가 클라이언트에게 연관성이 있는 액터 세트에 대해서만 알려주는 것으로 이루어진다.
이것이 연관성을 설정하는 기본 동작 규칙이 거리에 따라 이루어지는 이유이다.

언리얼이 플레이어에 대해 연관성이 있는 액터 세트를 결정하는 데 사용하는 규칙은 다음과 같다.
이 검사는 순서대로 검사하며, AActor::IsNetRelevantFor에서 수행한다.

액터가 bAlwaysRelevant == true (항상 연관성이 있)거나, Pawn 이거나, 폰이 노이즈나 대미지같은 동작의 Instigator (유발자)인 경우, 연관성이 있다.

액터가 bNetUseOwnerRelevancy == true (네트 오너 연관성 사용)이고 Owner (오너)가 있는 경우, 오너의 연관성을 사용한다.

액터가 bOnlyRelevantToOwner == true (오너에게만 연관성이 있)고 첫 번째 검사를 통과하지 못한 경우, 연관성이 없다

액터가 다른 액터의 스켈레톤에 붙어있으면, 그 연관성은 조상의 연관성에 의해 결정된다.

액터가 숨겨져있고 (bHidden == true) 루트 컴포넌트가 충돌이 비활성화 되어 있으면 액터는 연관성이 없다.
(루트 컴포넌트가 없으면, AActor::IsNetRelevantFor() 는 경고를 출력하고 액터에 bAlwaysRelevant=true 설정을 할지 물어본다)

AGameNetworkManager (게임 네트워크 매니저)가 거리 기반 연관성을 사용하도록 설정되어 있고, 액터가 네트 컬 디스턴스보다 가깝다면 연관성이 있다

이 기본 동작 규칙은 모든 액터 클래스에 대해 IsNetRelevantFor 함수를 재정의함으로써 변경할 수 있다.

업데이트 빈도와 우선순위 Update Frequency & Priority

액터가 복제되면, 업데이트 빈도와 우선순위에 따라 서버가 접속한 클라이언트에 업데이트를 보내는 빈도가 결정된다.
NetUpdateFrequency를 설정하면 서버가 액터를 확인하고, 변동된 사항이 있을 경우, 클라이언트에 새 데이터를 보내는 초당 횟수가 결정된다.
실제 네트워크는 대기 시간이 매우 가변적이고, 대역폭이 제한 요소가 될 수 있으므로, 액터가 초당 60회 업데이트 되도록 설정되어 있어도, 반대편에선 항상 초당 60회 미만으로 작동될 수 없다는 점에 주의

서버의 NetDriver은 대역폭 포화를 완화하기 위해 몇 가지 간단한 로드 밸런싱을 사용한다. NetDriver가 작업할 수 있는 대역폭이 한정되어 있으므로, 우선 순위에 따라 관련 액터들을 정렬한 다음, 사용 가능한 대역폭을 모두 사용할 때까지 업데이트를 실행한다.
플레이어와 가까운 액터거나, 한동안 업데이트 되지 않은 액터에는 더 높은 우선순위 가중치가 부여되므로, 모든 액터는 결국에는 우선 순위의 맨 앞으로 작업하게 된다.

액터의 NetPriority 변수를 설정하면, 해당 가중치에 추가 값이 적용된다. 만약 NetPriority를 5.0으로 설정하면, 보통보다 5배 더 자주 업데이트 받게 된다.

RPC

앞서 언급한 정기적으로 업데이트되고, 대역폭이 제한되는 네트워크 업데이트 프로세스는 대부분의 복제되는 프로퍼티에 적용된다. 하지만 우선순위와 빈도를 무시하고, 즉시 전송하여 적용하고 싶은 메시지가 있는 경우를 처리하기 위해 RPC가 존재한다.

https://docs.unrealengine.com/4.27/ko/InteractiveExperiences/Networking/Actors/RPCs/

Server RPC

  • 서버에서 해당 클라이언트에게 함수를 호출하도록 한다.

Client RPC

  • 클라이언트에서 서버에게 해당 함수를 호출하도록 한다.

Multicast RPC

  • 서버에서 해당 함수를 호출하면 서버는 해당 액터가 현재 복제되고 있는 모든 클라이언트에 메세지를 보내, 클라이언트가 해당 함수를 호출하게 한다.
    서버와 클라이언트RPC와 달리, 멀티캐스트RPC는 연관성의 영향을 받는다. 소유하지 않은 클라이언트는 액터에 대한 공개 채널을 갖고 있지 않을 수 있기 때문, 이 경우 연관성이 없는 클라이언트에선 멀티캐스트 RPC가 호출되지 않고, 이는 지속적인 상태변화를 클라이언트에 복제하기 위해 멀티캐스트RPC에 의존해서는 안되는 이유이다.

 

신뢰할 수 있음 플래그

 

Reliable 플래그를 넣어줌으로써, 신뢰할 수 있는 RPC 지정이 가능하다.

신뢰할 수 없는 RPC는 대역폭이 포화된 경우, 메시지가 반드시 도착하거나, 순서대로 도착한다는 보장이 없다.
신뢰할 수 있는 RPC는 도착하는것이 보장되며, 단일 액터 내에서는 호출된 순서대로 도착하는 것이 보장된다.
해당 함수가 중요한 함수라면 이러한 안정성이 필요하지만, 과도하게 사용하면 대역폭 포화가 악화되고 패킷 손실 시 병목 현상이 발생할 수 있음에 주의

_Implementation

RPC함수의 구현부의 함수 이름에는 _Implementation을 붙여줘야 한다. _Implementation이 붙은 함수 구현부는 실제로 메시지가 도착한 클라이언트 혹은 서버에서 실행되는 내용이고, _Implementation이 붙지 않고, 로컬에서 호출하는 함수는 네트워크를 통해 적절한 메시지를 보내는 자동 생성된 썽크이다.

_WithValidation

서버 RPC에 UFUNCTION 내부에 WithValidation 플래그를 지정할 수 있는데, 이 경우, 동일한 인수를 사용하고, 뒤에 _WithValidation를 붙인 함수를 생성할 수 있다. 해당 함수는 해당 값을 신뢰할 수 있는지 여부를 나타내는 부울을 반환한다. 이를 통해 클라이언트에서 전송된 데이터를 사용하는 경우, 치트를 감지할 수 있다. 서버 RPC가 이 검사에 실패하면, 해당 RPC를 보낸 클라이언트가 게임에서 추방된다는 점을 명심



RPC는 즉시 전송되며 신뢰할 수 있으고, 서버 RPC는 클라이언트의 값을 서버로 가져올 수 있는 유일한 방법이므로 매우 유용하다.
하지만 대체로 RPC는 일반적으로 캐릭터 무브먼트 등, 우선순위가 높고, 시간이 중요한 네트워크 코드용으로 예약되어 있다.
그 밖에 모든 경우엔 가능한 프로퍼티 레플리케이션에 의존해야 한다.

 

프로퍼티 레플리케이션

언리얼 복제 시스템의 핵심이며, 로드 밸런싱 및 우선순위 지정 기능을 통해 훨씬 더 확장성이 뛰어나다.

서버에서 복제되는 프로퍼티를 변경하면, 모든 클라이언트가 결국에는 서버와 동기화 될 것이라 기대할 수 있다.
플레이어가 너무 멀리 떨어져 있는 상태에서, 서버에서 액터가 변경되면, 변경사항은 업데이트 되지 않고 유지되다가, 액터가 해당 클라이언트와 다시 관련되면, 업데이트 된 값을 받게 된다.

프로퍼티 레플리케이션은 어떠한 경우에도 업데이트 빈도와 대역폭 제한을 준수한다.
서버에서 단일 프레임마다 복제되는 프로퍼티를 변경해도, 클라이언트는 업데이트 빈도에 따라, 업데이트될 때마다 가장 최근 값을 가져온다. 서버가 모든 단일 프레임마다 변경된 값을 보낼 의무가 없다는 뜻.

GetLifetimeReplicatedProps()

프로퍼티를 복제하고싶으면, UPROPERTY에 Replicate 플래그를 지정해주면 된다. 이후 복제하는 프로퍼티를 가진 모든 액터는 GetLifetimeReplicatedProps()함수를 오버라이드하고, DOREPLIFETIME(액터 포인터, 복제되는 프로퍼티 변수) 매크로로 복제하고자 등록하면 된다.
DOREPLIFETIME_CONDITION()으로 복제 조건을 지정할 수도 있는데, 예를 들어 이 액터를 소유하는 클라이언트에만 복제하고 싶다면, COND_OwnerOnly를 기입하면 된다.

 

프로퍼티 리플리케이션을 사용하기 위해서는 GetLifetimeReplicatedProps() 함수에 Replicated할 속성값을 등록해줘야만 정상적으로 빌드를 할 수 있다.

그리고 DOREPLIFETIME은 [#include "Net/UnrealNetwork.h"]헤더를 사용하므로 까먹지말고 추가

 

RepNotify

복제되는 프로퍼티가 업데이트 될 때 일부 함수성을 실행해야 한다면 유용하다.
ReplicatedUsing=함수이름 플래그를 지정하여, RepNotify 함수를 등록하면, 복제 업데이트로 인해 값이 변경될 때마다 해당 함수가 클라이언트에서 호출된다.

주의
블루프린트에서는, 서버에서 복제되는 프로퍼티를 변경해도 RepNotify 함수가 자동으로 호출되지만,

 C++에서 RepNotify는 오직 클라이언트에서만 호출된다. 만약 해당 함수를 서버에서도 호출하고 싶다면, 명시적으로 해당 RepNotify함수를 호출해주자.

오소리티와 네트워크 롤Authority&Role

enum ENetRole
{
    ROLE_None,
    ROLE_SimulatedProxy,
    ROLE_AutonomousProxy,
    ROLE_Authority,
    ROLE_MAX,
}



대부분의 경우, 이 액터에 대한 권한이 나에게 있는지를 고려하면 된다.(HasAuthority() == true)
만약 HasAuthority() == true일 경우, 현재 해당 코드는 스탠드 얼론으로 실행되는 싱글 플레이거나, 서버이거나, 해당 액터가 클라이언트에만 존재하는 것이다.
HasAuthority() == false일 경우, 해당 코드는 클라이언트에서 실행되고, 액터는 서버에서 복제된 것이다. 이 경우, 클라이언트 액터 복제본은, 서버의 권위있는 버전의 프록시이다. 대부분의 경우, SimulatedProxy이지만, AutonomousProxy는 플레이어에 대해 이야기할 때만 적용된다.

플레이어 컨트롤러와 해당 플레이어 컨트롤러가 컨트롤하는 폰은, 해당 플레이어 컨트롤러를 소유한 클라이언트에 복제될 때 AutonomousProxy이다. 소유하지 않는 다른 모든 클라이언트에선 SimulatedProxy로 복제된다. AutonomousProxy는 클라이언트가 완전한 권한을 가지지 못하더라도, 액터의 움직임과 행동을 직접적으로 제어한다는 것을 의미한다.

IsLocallyControlled

폰이 로컬에 의해 제어받는 경우, 해당 플레이어는 IsLocallyControlled = true이다. 만약 IsLocallyControlled == false일 경우, 원격 클라이언트의 플레이어다.

 

언리얼 멀티플레이 팁

멀티플레이 게임을 만든다는 것은, 클래스의 멤버 함수의 기능만 생각하는 것이 아니라, 해당 코드가 실행되는 위치와, 데이터가 네트워크를 통해 서로 다른 시간에 흐르는 방식도 고려해야 한다. 추가로 고려해야 하는 고민거리가 하나 더 생기는 셈.
이를 보다 쉽게 배우기 위해 몇 가지 팁을 첨부한다.

 

  1. 액터가 복제되더라도, 해당 액터의 모든 부분이 네트워크와 관련될 필요가 없다.
    서버에서 동기화 되어야 하는 중요한 부분만 처리하고, 게임에 큰 영향을 주지 않는 다른 모든 것은 해당 클라이언트에게 맡겨버리자

  2. 대부분의 게임 코드는 멀티플레이용으로 설계된 언리얼 게임 프레임워크를 기반으로 구축된다. 자신만의 기능을 추가하기 위해 이러한 클래스의 멤버 함수를 재정의하려는 경우, 해당 함수가 서버에서만 실행되도록 설계되었는지, 혹은 클라, 혹은 모든 곳에서 실행되도록 설계되었는지 알고 싶을 것이다.
    어설션을 사용하면 코드가 어디에서 실행되도록 설계되었는지 명확하게 알 수 있다.

    checkf(HasAuthority(), TEXT("This Code Should only run on the server!")
    checkf(!HasAuthority(), TEXT("This Code Should only run on the client!")
    checkf(IsLocallyControlled(), TEXT("Pawn must be locally controlled!")

    또한, 이러한 함수에 접두사로 Auth등을 붙여주는 규칙을 사용한다면 유용할 것이다.
  3. 심도깊은 이해를 원한다면 엔진 코드를 보자
    NetDriver.h에는 복제 시스템 작동 방식에 대한 유용한 정보들을
    NetWorkDriver.cpp에는 유용한 콘솔 변수와 구현을 확인할 수 있다.
    NetDriver은 엔진 수준의 기본 클래스일 뿐, 게임 네트워크 드라이버에 사용되는 실제 구현은 IPNetDriver, IPConnection과 함께 OnlineSubsystemUnils 플러그인에서 구현된다.

 

 

 

반응형