게임 개발 메모장
[ UE5 ] RPC 본문
RPC의 기본 개념과 동작 원리
- 원격 프로시저(함수) 호출
- 원격 컴퓨터에 있는 함수를 호출할 수 있도록 만든 통신 프로토콜
- 네트워크 멀티플레이에서 서버와 클라이언트 간에 빠르게 행동을
명령하고 정보를 주고 받는데 사용
- 언리얼 엔진에서 클라이언트에서 서버로 통신하는 유일한 수단을 제공한다.
이 기능의 주요 용도의 속성상 장식이나 휘발성인 비신뢰성 게임플레이 이벤트를 위한것
이는 사운드 재생, 파티클 스폰, 액터의 핵심적인 기능과는 무관한 일시적 효과와
같은 작업을 하는 이벤트를 포함한다.
RPC 사용시 OwnerShip 작동 방식을 이해해둬야 한다.
대부분 RPC 실행 장소를 결정하기 때문이다.
RPC 사용하기
함수를 RPC 로 선언하려면 UFUNCTION 선언에 Server, Client, NetMulticast
키워드를 붙여주기만 하면 된다.
예를 들어, 함수를 서버에서 호출되지만
클라이언트에서 실행되는 RPC 로 선언하려면 이렇게 한다.
UFUNCTION( Client )
void ClientRPCFunction();
함수를 클라이언트에서 호출되지만 서버에서 실행되는 RPC 로 선언하는 것은
Server 키워드를 사용한다는 것 빼고는 매우 비슷하다.
UFUNCTION( Server )
void ServerRPCFunction();
Multicast 라 불리는 특수 유형 RPC 함수가 하나 더 있습니다.
Multicast RPC 는 서버에서 호출된 다음 서버는 물론 현재 연결된
모든 클라이언트에서도 실행되도록 고안된 것입니다.
멀티캐스트 함수를 선언하려면 그냥 NetMulticast 키워드를 사용하면 된다.
UFUNCTION( NetMulticast )
void MulticastRPCFunction();
멀티캐스트 RPC 는 클라이언트에서도 호출 가능하지만, 이 경우 로컬에서만 실행된다.
함수 초반에 Client, Server, Multicast 키워드를 앞쪽에 어떻게 붙였는지 보면.
내부적으로 합의한 규칙으로, 이 함수를 사용하는 프로그래머들에게
이 함수가 각각 클라이언트, 서버, 모든 클라이언트에서 호출된다는 것을 알리기 위한 것이다.
멀티플레이어 세션 도중 이 함수가 어느 머신에서 호출되는지
한 눈에 알아볼 수 있기에 매우 유용하다.
요건 및 주의사항
RPC 의 정상 작동을 위해 충족시켜야 하는 요건이 몇 가지 있다.
- Actor 에서 호출되어야 한다.
- Actor 는 빈드시 replicated 여야 한다.
- 서버에서 호출되고 클라이언트에서 실행되는 RPC 의 경우, 해당 Actor 를 실제 소유하고 있는 클라이언트에서만 함수가 실행된다.
- 클라이언트에서 호출되고 서버에서 실행되는 RPC 의 경우, 클라이언트는 RPC 가 호출되는 Actor 를 소유해야 한다.
- Multicast RPC 는 예외이다:
- 서버에서 호출되는 경우, 서버에서는 로컬에서 실행될 뿐만 아니라 현재 연결된 모든 클라이언트에서도 실행된다.
- 클라이언트에서 호출되는 경우, 로컬에서만 실행되며, 서버에서는 실행되지 않는다.
- 현재 멀티캐스트 이벤트에 대해 단순한 스로틀 조절 메카니즘이 있다. 멀티캐스트 함수는 주어진 액터의 네트워크 업데이트 기간동안 두 번 이상 리플리케이트되지 않는다. 장기적으로 크로스 채널 트래픽 관리 및 스로틀 조절 지원을 개선시킬 계획이다.
Client RPC 개요
- 서버에서 클라이언트로 호출하는 RPC
- 이를 활용해 특정 클라이언트에게만 명령을 보낼 수 있다.
- 서버에서 명령을 보낼 클라이언트의 커넥션을 소유한 액터를 사용해야한다.
(AActor::GetNetConnection)
* RPC는 반드시 전달되어야 하는 명령이 아니면 가급적이면
unreliable 키워드를 사용하는 것이 좋다.
Server RPC 개요
- 클라이언트에서 서버로 호출하는 RPC
- 언리얼 엔진 구조에서 유일하게 클라이언트가 서버의 함수를 호출할 수 있는 기능
- 서버쪽에서 클라이언트의 명령을 검증할 수 있는 함수를 구현할 수 있다.
- Client RPC와 동일하게 서버와의 커넥션을 소유한 액터를 사용해야 한다.
* 서버 RPC에는 WithValidation 이라고 하는 부가적인 키워드를 사용할 수가 있다.
이러한 기능은 클라이언트가 보낸 명령이 악의적인 것인지 판단하기 위함다.
만일 악의적인 유저가 데이터를 변조해 서버에 명령을 내리는데 서버가 이것을 그냥 조건 없이
모두 수용하게 되면 현재 진행중인 전반적인 게임 시스템이 무너질 수가 있다.
따라서 서버에 전달하는 데이터가 문제가 없는지 추가적인 함수를 구현해서 점검할 수가 있다.
NetMulticast RPC 개요
- 서버를 포함해 모든 플레이어에게 명령을 보내는 RPC
- 프로퍼티 리플리케이션과 유사하게 연관성 기반으로 동작함 (커넥션을 소유하지 않아도 동작)
- 프로퍼티 리플리케이션과 유사하지만 다른 용도로 사용 ( 이후에 설명 )
RPC 선언에 관련된 키워드
- Unreliable : RPC 호출을 보장하지 않는 옵션. 빠름
( 게임 플레이에 중요하지 않거나 매우 자주 호출되는 함수에 적합하다.!
액터의 움직임은 매 프레임마다 변경될 수 있기 때문에
이렇게 Unreliable한 RPC를 사용한다. )
- Reliable : RPC 호출을 보장해주는 추가 옵션. 정말 필요한 때만 호출
게임 플레이에 매우 중요하지만 자주 호출되지 않는 함수에 적합하다.
충돌 이벤트, 무기 발사, 종료, 액터 스폰 등이 이에 해당한다.
중요 : 과도하게 사용하면 해당 기능의 대기열이 넘칠 수가 있어서
강제로 연결이 끊어질 수가 있다.
프레임 단위로 리플리케이트 된 함수를 호출하는 경우 비신뢰성으로 설정해야 된다고
되어 있다. 예를들어서 Tick 함수
플레이어 입력도 굉장히 빈번하게 호출되는 함수이기 때문에 바인딩된 함수가 reliable인 경우에는
빈도를 제한해야 된다.
- WithValidation : 서버에서 검증 로직을 추가로 구현할 때 추가하는 옵션
예시
NetMulticast RPC 예시
플레이를 해보면 서버쪽에서 계속해서 분수대의 색상이 변한다.
클라이언트를 하나 추가를 하면 마찬가지로 색상이 동일하게 복제되어서 전달된다.
로그가 두 개씩 찍히고 있다.
클라이언트가 멀리 떨어지면 연관성을 잃는다.
멀티캐스트 RPC의 경우에는 연관성에 관련해 동작을 한다
그렇기 때문에 연관성을 잃게 되는 경우 클라이언트에는 해당 RPC가 호출되지않는다.(Log참조)
라이트 색상도 그대로 동일한 색상으로 유지
Server RPC 예시
서버 RPC를 클라이언트에서 호출을 해줘야한다. 클라이언트에서만 실행될 수 있게 만들어준다.
매초마다 클라이언트에서 서버로 명령을 보내는것 빛을 바꾸라고
그렇게 되면
서버RPC에서 구현된 로직인 멀티캐스트를 호출하기 때문에
이 멀티캐스트를 통해서 서버가 모든 클라이언트에게
바뀐 분수대 빛을 적용해라라고 명령을 내린다.
이것들을 받은 클라이언트들은 그때 색상을 바꾸는
분수대 빛의 색상을 바꾸는 형태로 로직이 구현될 것.
분수대의 빛 색상이 바뀌지 않고 워닝이 계속 뜬다.
단일 머신으로 전송하는 RPC인 경우에는 소유권 Ownership을 가져야 된다.
지금 분수대의 경우에는 플레이어 컨트롤러나 폰과 아무런 관련이 없기 때문에
Ownership 자체가 없다.
그래서 Ownership이 없는 액터인 분수대에 서버 IPC를 실행하라고
하니까 이런 에러가 뜨는것이다.
이것을 위해서 분수대에 Ownership을 설정해줘야된다.
에러는 잡았지만 분수대에 아무런 변화가 없다.
Authority가 없는 Client에서 Owner를 지정했다.
그런데 Client는 Proxy 즉, 서버의 내용물을 복제한 것에 불과하기 때문에 이렇게
클라이언트에다가 Owner를 설정하는 것은 사실상 의미가 없다.
왜냐하면 우리가 네트워크를 진행할 때는 서버에 있는 Actor의 Owner가 설정되어야 된다.
그렇기 때문에 이 코드는 그냥 클라이언트의 로컬 머신에서만 호출되는
네트워크 없는 LocalFunction을 호출하게 되는것이다.
이를 보완하기 위해서는 서버 코드에서 분수대의 Owner를 지정해줘야된다.
클라이언트 1번으로 지정해줘야 한다.
타이머에서 우리가 서버를 구동하고 서버가 구동될때는
클라이언트가 접속이 안되어있는 상황이기 때문에
BeginPlay를 시작하자마자 클라이언트 1번에 대한 Ownership 설정할 수가 없다.
왜냐하면 클라이언트가 접속을 안 했기 때문이다.
그래서 클라이언트가 접속할 충분한 10초의 시간을 주고
그 전에 클라이언트가 접속을 해서 서버에 해당 클라이언트 1번에 대한
플레이어 컨트롤러가 만들어질 때까지 시간적인 여유를
주고 이때 Ownership을 설정해야한다.
월드에서 자신이 가지고 있는 모든 플레이어 컨트롤러 이터레이터를 가져올 수 있다.
이터레이터를 통해서 플레이어 컨트롤러의 포인터를 가져올 수가 있다.
서버를 제외하고 서버 입장에서는 로컬 플레이어 컨트롤러가 아닌 애들이
클라이언트에 접속한 플레이어 컨트롤러이기 때문에 여기서 클라이언트 1번이 될 것이다.
그래서 해당 플레이어 컨트롤러를 Owner로 설정해주고 루프를 빠져나온다.
10초가 넘어가는 순간에 Ownership이 설정이 되면서 서로 간에
클라이언트가 서버에 명령을 보내고
서버는 랜덤한 색상을 가져온 다음에 멀티캐스트 RPC를 통해서
모든 클라이언트에 보내는 이런 로직이 잘 수행 된다.
WithValidation 키워드를 사용하는 경우에는 boolean 값으로
Validate라고 하는 함수를 구현을 해줘야한다.
return true로 지정해주면 검증에 통과를 했다는 것이고 false로 지정하면 검증에 실패 했다는 것이다.
false로 지정한다면 동작을 살펴보면
접속이 되었다가 검증에 실패하게되면 즉시 클라이언트의 접속을 끊는다.
그럼 접속을 끊은 상태에서 클라리언트가 대처를 해야하는데
대처를 안해주면 크래시가 난다.
검증에 실패하는 경우에는 클라이언트가 즉각적으로 차단이되고 해당 클라이언트는
StandAlone 모드로 돌아가게 된다.
Client RPC 예시
클라이언트 RPC의 경우에도 Ownership이 설정이 되어야 하기 때문에
10초가 되기 전까지는 클라이언트 RPC가 제대로 동작하지 않을 거라고 예측할 수 있다.
클라이언트 RPC에서 Ownership을 가지지 않으면 서버에서 당분간 호출이된다.
서버에서 우리가 클라이언트 RPC 로 호출하는데 해당하는 액터가 OwnerShip을 가지지 않는다.
그러면 그냥 서버에서 그 함수가 호출이 되도록 언리얼 엔진이 그렇게 설계가 되어있다.
그런데 10초 뒤에 Ownership을 가지게 되면 그때 클라이언트로 전송을 하게 된다.
10초 전까지는 서버의 색상이 변하다가 10초 이후에는 클라이언트에서만 색상이 변하도록
이렇게 구동이 된다.
클라이언트를 하나 더 추가하게되면 지금 이 새로운 클라이언트 같은 경우는 서버로부터
어떤 데이터를 받지도 못하기 때문에 초기 값의 라이트 색상으로 보여지는 것을 확인할 수 있고
클라이언트 1번만 분수대 색상이 변하는 것을 볼 수 있다.
https://docs.unrealengine.com/4.27/ko/InteractiveExperiences/Networking/Actors/RPCs/
'언리얼 엔진 > Network' 카테고리의 다른 글
[ UE5 ] 액터의 역할과 커넥션 핸드셰이킹 (0) | 2024.01.06 |
---|---|
[ UE5 ] 캐릭터 공격 구현 예시 (0) | 2023.12.30 |
[ UE5 ] 액터 리플리케이션 (0) | 2023.12.25 |
[ UE5 ] 커넥션과 오너십 (0) | 2023.12.25 |
[ UE5 ] 게임 모드와 로그인 (0) | 2023.12.25 |