내일배움캠프 75일차 - 언리얼 엔진 C++ 멀티플레이어: Remote Procedure Call

RPC (Remote Procedure Call)

평소 함수를 사용할 때에는 함수를 호출하는 PC와 실행되는 PC와 동일하였다.
즉, 로컬 PC에서 함수 호출과 실행을 같이 했었다.

멀티플레이에 사용되는 RPC는 간단히 말해,
"호출하는 PC실행하는 PC달라도 되게 끔 해주는 통신 기법" 이라고 할 수 있다.

(RPC 공식 설명 링크: https://dev.epicgames.com/documentation/unreal-engine/rpcs-in-unreal-engine?application_version=5.1)

'Hello'가 서버에서는 호출되지 않고, 클라이언트에서만 호출된다.

언리얼에서의 RPC의 용도

게임에 큰 영향을 끼치지 않는 일시적인 효과들(코스메틱: 사운드, 파티클 등)에 사용하는 편이다.

게임에 큰 영향을 끼치는 것들은 프로퍼티 레플리케이션을 사용하는 것이 좋다.

언리얼 RPC 호출표 (서버, 클라이언트)

Call과 Invoke

Call
 - 정적인 의미를 가지는 호출
 - 컴파일 타임에 어떤 함수인지, 호출하는 곳과 실행하는 곳이 정해져야 한다.
 - 지금까지 정의했던 일반적인 전역 함수, 멤버 함수가 이에 해당. (Direct: 직접적인 호출, 실행)

Invoke
 - 동적인 의미를 가지는 호출
 - 런타임에 어떤 함수인지, 호출하는 곳과 실행하는 곳이 어딘지 정해진다.
 - 함수 포인터, 동적 바인딩, RPC 등이 이에 해당. (Indirect: 간접적인 호출, 실행)
 - 위 사진처럼 RPC를 Invoke(호출)하다라고 표현함.

Actor Ownership ( ≈ Owning Connection)

네트워크 통신을 적용하려면, 액터는 서버에서 스폰되고 bReplicates가 true여야 한다.

예) 서버에서 스폰된 후, SetOwner(PlayerController) 함수를 호출해야( = Ownership을 체결해서 패밀리에 가입해야) Client-Owned Actor가 된다.

1) 서버에서 RPC 함수를 보는 시각

Client-Owned Actor (Client)
 : PlayerControllerLocal PlayerController같음.
 ex) 클라이언트가 소유한 플레이어 캐릭터

2) 클라이언트에서 RPC 함수를 보는 시각

Owned by invoking client (Invoking Client)
 : PlayerControllerLocal PlayerController같음.
 ex) 내 화면에 보이는 내 플레이어 캐릭터

Owned by different client (Different Client)
 : PlayerControllerLocal PlayerController다름.
 ex) 내 화면에 보이는 타인의 플레이어 캐릭터

3) 양쪽에서 각각 RPC 함수를 보는 시각

Server-Owned Actor (Server)
 : 서버에서 스폰되었지만, SetOwner() 함수 호출이 없음.
 ex) 보물상자 액터

Unowned Actor (None)
 : SetOwner() 함수 호출이 없음.
 : 단, 굳이 서버가 관리한다기보다 레벨에 배치되어 있는 액터들.
 ex) 나무 액터, 바위 액터, 문 액터, ...


NetMulticast, Server, Client - UFUNCTION() 키워드

UFUNCTION() 매크로와 함께 사용되는 키워드이다.
이는 "해당 원격 PC에서 RPC를 실행시켜 달라는 요청"을 뜻한다.

NetMulticast
 - "서버를 포함한 모든 클라이언트에서 해당 RPC를 실행시켜 주세요" 라는 요청이다.

NetMulticast RPC


Server
 - "서버에서 해당 RPC를 실행시켜주세요" 라는 요청이다.

Server RPC


Client
 - "클라이언트에서 해당 RPC를 실행시켜주세요" 라는 요청이다.

Client RPC

자주 쓰이는 케이스

1) RPC가 클라이언트에서 호출되고 서버에서 실행되어야 하는 경우에는 Server 키워드를 사용한다.
 - ClientConnection이 소유하고 있는 액터(Client-Owned)에서 RPC가 호출되어야 한다.
 - _Validate() 함수에서 RPC를 실행할지 말지 결정한다.

클라이언트 중 '내가 소유한 액터'만이 UFUNCTION(Server)로 서버에서 실행되고, 나머지는 반려된다.

2) RPC가 서버와 모든 클라이언트에서 실행되어야 하는 경우에는 NetMulticast 키워드를 사용한다.
 - 서버에서 호출해야 한다.
 - 부하가 심하므로, 빈번하게 호출되게 끔 코드를 작성하는 건 권장되지 않음.
 ex) Tick() 함수 내에서 NetMulticast RPC 호출

3) RPC가 서버에서 호출되고 클라이언트에서 실행되어야 하는 경우에는 Client 키워드를 사용한다.
 - 서버에서 호출해야 한다.

WithValidation - UFUNCTION() 키워드

서버에서 실행되는 RPC의 경우 작성이 권장되는 UFUNCTION() 매크로 키워드다.

이 키워드가 붙은 RPC를 구현할 때는 _Implementation() 함수와 _Validate() 함수로 나뉘어진다.
 : _Validate() 함수로 실행여부를 결정하고, 허가되면 _Implementation()가 실행된다.

서버 실행 로직은 무조건적으로 신뢰되므로, 위 키워드는 위변조를 막는 방벽이 되어준다.

Unreliable vs Reliable - UFUNCTION() 키워드

UFUNCTION() 매크로와 함께 사용되는 키워드이다.

Unreliable
 : 해당 RPC가 원격 PC에서 무조건 실행될 것이라는 보장이 없다.
 : RPC의 기본값이다. (키워드에 명시하지 않으면 Unreliable 고정)
 ex) 코스메틱(이펙트, 사운드 등) 처럼 게임에 큰 영향이 없는 로직

Reliable
 : 해당 RPC가 원격에서 무조건 실행되어야만 하는 로직일 때 사용한다.
 ex) 충돌, 대미지, 스폰 등 게임에 큰 영향을 끼치는 로직

RPC 활용 예제

RPC함수는 함수명에 _Implementation이 들어가야한다.

1) 내 클라이언트에서 컨트롤러가 메세지를 입력하고 Server측 RPC함수를 호출하면,

2) Server에서 함수를 실행하고 모든 컨트롤러를 순회해서 각 Client측 RPC함수를 호출하고,

3) Client에서 서버가 RPC함수와 함께 보낸 메세지 변수Print하는 것이다.

결과 화면


댓글

이 블로그의 인기 게시물

내일배움캠프 사전캠프 - 사전캠프설 연휴 커피 파밍 이벤트 작품 [ EXTREMITY ]

내일배움캠프 29일차 - 커리어데이 2일차 : 클라이언트 프로그래머로서 포트폴리오, 입사준비팁