UFUNCTION(Server, Reliable, WithValidation)
void ServerRPC();
RPC는 Remote Procedure Call, 원격 함수 호출이다.
쉽게 말하면, 내 컴퓨터에서 함수를 호출했는데, 실제 함수의 실행은 다른 컴퓨터에서 하도록 요청하는 기능
ServerRPC();를 호출하면 Server에서 ServerRPC_Implementation()이 실행되도록 요청하는 기능이다.
Reliable은 상대방이 수신했다는 확인을 받을 때까지 재전송을 보장하는 RPC이다.
또 Reliable RPC는 순서도 보장된다.
반대로 Unreliable은 패킷이 유실되면 해당 호출이 실행되지 않을 수 있고 순서도 보장되지 않는다.
그렇지만 Reliable은 추가 네트워크 비용이 있으므로 남발하면 안 됩니다.
Reliable
- 반드시 전달되어야 하는 RPC에 사용한다.
- 수신 확인을 받을 때까지 재전송한다.
- 실행 순서를 보장한다.
- 네트워크 비용이 더 크므로 필요할 때만 사용한다.
Unreliable
- 패킷이 유실되면 해당 RPC가 실행되지 않을 수 있다.
- 재전송을 보장하지 않는다.
- 실행 순서를 보장하지 않는다.
WithValidation은 클라이언트가 보낸 Server RPC가 정상적인 요청인지 서버에서 검사하는 _Validate() 함수를 함께 사용하겠다
실행 흐름을 보면 왜 사용되는지 알 수 있다.
Client
ServerBuyItem(100)
↓
Server
ServerBuyItem_Validate(100)
↓
true인가?
/ \
true false
↓ ↓
Implementation | 클라이언트 연결 종료
이렇게 ServerRPC_Implementation()가 실행되기전 bool 값으로 유효성 검사를 해준다.
_Validate()는 클라이언트가 ServerRPC로 보낸 값이 서버가 예상한 정상적인 범위인지 검사하는 데 사용할 수 있다.
예를 들어 아이템 ID가 0 ~ MaxItemID - 1까지만 존재한다면,
bool ServerBuyItem_Validate(int32 ItemID)
{
return ItemID >= 0 && ItemID < MaxItemID;
}
처럼 검사할 수 있다.
클라이언트는 변조될 수 있기 때문에 -1, 999999처럼 게임에서 정상적으로 발생할 수 없는 값을 서버에 보낼 가능성이 있다. 따라서 서버는 클라이언트가 보낸 값을 그대로 신뢰하지 않고 유효한 값인지 검사해야 한다.
다만 _Validate()는 검증 실패 시 클라이언트 연결을 끊을 수 있으므로, 단순히 "골드가 부족하다", "쿨타임 중이다" 같은 정상적인 게임 실패 조건을 검사하는 용도로 사용하면 안 된다. 이런 조건은 _Implementation() 내부에서 검사하고 요청만 거절하는 것이 적절하다.
void ASPlayerCharacter::ServerRPC_Implementation(){}
bool ASPlayerCharacter::ServerRPC_Validate(){}
이런 식으로 RPC는 cpp에는 _Implementation()이 있어야하고 현재 WithValidation이므로 _Validate()도 필요하다
_Implemtation()는 말 그대로 현재 RPC인 Server에 구현될 구현부 라는 뜻이다.
Unreal은 Server나 Client RPC를 선언하면 _Implementation 이름을 사용하는 함수를 네트워크 처리와 연결하도록 코드를 생성하도록 되어있다.
스폰 흐름
1. 플레이어가 특정 키를 누른다.
2. 해당 클라이언트가 자신이 조종하는 PlayerCharacter에서
ServerSpawnActor() RPC를 호출한다.
3. Unreal 네트워크 시스템이
"어떤 RPC를 호출했는지 + RPC 매개변수"를
네트워크로 전송 가능한 형태로 Serialize하여 서버에 전송한다.
4. 서버가 네트워크 데이터를 수신하고 해석한다.
5. WithValidation을 사용했다면
ServerSpawnActor_Validate()를 먼저 실행한다.
6. 검증에 성공하면
ServerSpawnActor_Implementation()을 서버에서 실행한다.
7. 서버에서 SpawnActor()를 호출해 Actor를 생성한다.
8. 생성된 Actor가 bReplicates = true이면
Unreal의 Actor Replication을 통해 각 클라이언트에
해당 Actor가 복제된다.
Serialization은 메모리에 있는 데이터를 저장하거나 네트워크로 전송할 수 있는 형태로 변환하는 과정이다.
Deserialize는 그 반대이다 즉,
// Serialize
C++ 데이터
↓
전송 가능한 데이터
//Deserialize
전송된 데이터
↓
C++에서 사용할 수 있는 값으로 복원
코드 흐름으로 보면:
Client
키 입력
│
↓
ServerRPC()
│
└────────────── Network ────────────────┐
↓
Server
│
_Validate
│
_Implementation
│
SpawnActor
│
bReplicates
│
┌──────────────┬─────────────────┘
↓ ↓
Client 1 Client 2
Actor 생성 Actor 생성
SpawnedActor->SetOwner(this);
여기서 Owner는 이 Actor를 소유하는 다른 Actor가 누구인가를 설정한다.
언제 쓰이냐면, 예를 들어 PlayerCharacter가 총알Actor를 만들었다고하면 그때 SetOwner(this)를 설정해준다. 플레이어가 소유한 Actor는 PlayerController를 통해 해당 클라이언트의 connection과 연결된다.
나중에 누가 만든 Actor인지 직접 알기도 편함.