📄 서버-클라이언트 구조
🧠 서버 (Server)
대부분의 작업을 처리하고 중요한 결정을 담당한다.
1. 월드 생성 및 관리
- 게임 모드에서 게임의 자체 인스턴스를 실행하여 연결된 모든 클라이언트 간에 공유되는 월드 역할을 담당한다.
- 레벨은 언제든 변경 가능하며, 연결된 모든 클라이언트를 모두 자동으로 다른 레벨로 이동시킬 수도 있다. (Server Travel)
2. 클라이언트 참가 및 퇴장 처리
- 클라이언트는 직접 IP 연결 또는 OSS(Online Subsystem)을 통해 서버에 참가 요청을 보낸다.
- 서버가 요청을 수락하면 플레이어 컨트롤러를 클라이언트에 할당하고 GameMode의 PostLogin 함수가 호출되어 다른 클라이언트와 상호작용할 수 있게 된다.
- 연결이 끊어지면 모든 클라이언트에게 알림을 요청하고 GameMode의 Logout 함수가 호출된다.
3. 모든 클라이언트가 알아야 하는 액터의 생성
- 클라이언트에게 액터의 인스턴스를 생성(Spawn)하도록 지시할 수 있는 권한은 서버에만 있다.
- 따라서 모든 클라이언트가 알아야하는 액터를 생성할 때는 서버에서 처리하는 것이 일반적이다.
4. 중요한 게임플레이 로직
- 승패를 결정하거나, 클라이언트의 체력을 깎는 등의 중요한 로직은 서버에서 처리한다.
5. 변수 리플리케이션 처리
- 리플리케이션이 필요한 변수는 서버에서 변경하여 모든 클라이언트의 값을 자동으로 업데이트한다. 부정행위를 방지하고, 모든 클라이언트가 동기화되도록 서버의 최신값으로 대체한다.
6. 클라이언트에서 RPC 처리
👥 클라이언트 (Client)
대부분의 액터에 대한 권한은 서버가 갖기 때문에, 작업은 서버에서 수행되고 클라이언트는 서버의 명령을 따른다.
1. 서버로부터 변수 리플리케이션 적용
- 리플레케이션 된 변수의 값이 서버에서 변경되면, 클라이언트도 변경된 값을 적용한다.
2. 서버에서 RPC 처리
- 클라이언트는 서버에서 전송한 RPC를 처리해야한다.
3. 시뮬레이션 시 움직임 예측
- 클라이언트가 액터를 시뮬레이션할 때 액터의 속도를 기반으로 로컬에서 예측해야한다.
4. 클라이언트만 알아야 하는 액터 생성
- 멀티 플레이 생존 게임의 크래프팅 시 미리보기용 액터 등
📄 동기화
- 동기화의 주인공은 Actor이다.
- Actor Replication을 활성화시키려면 BP의 디테일 탭에서 Replicates를 체크하거나 C++에서 AActor::SetReplicates(true)를 사용한다.
👯 리플리케이션 (Replication)
- 변수를 Replication(복제)해서 업데이트를 전송해 서버와 클라이언트를 동기화하는 방법
- 설정해놓으면 변수 값이 변경될 때마다 자동으로 Replicate 및 동기화된다.
✏️ Replicated
- 변수를 단순히 Replicate(복제)만 하고 싶은 경우 사용하는 지정자
UPROPERTY(Replicated)
float Health = 100.f;
✏️ RepNotify
- 변수가 업데이트될 때마다 함수를 호출하고 싶은 경우 사용하는 지정자
- ReplicatingUsing 지정자에서 사용되는 함수는 UFUNCTION()으로 선언해야한다.
UPROPERTY(ReplicatedUsing=On_Rep_Health)
float Health = 100.f;
void OnRep_Health()
{
UpdateHUD();
}
⚡ RPC (Remote Procedure Call)
- 로컬에서 호출하지만 (호출하는 장치와 다른) 원격 장치에서 실행되는 함수.
- 서버가 클라이언트의 함수를 실행하거나, 클라이언트가 서버의 함수를 실행해 동기화한다.
- 함수 구현 시 일반 함수와 다르게 _Implementation 버전만 구현해야한다.
✏️ Server RPC
- 중요한 로직을 서버에서 실행하도록 만들어 보안을 강화하고 동기화시킬 때 사용
- Server RPC를 실행하려면 액터 인스턴스를 가진 클라이언트에서 호출해야 한다.
UFUNCTION(Server, Relable, WithValidation)
void ServerRPCFunction(int32 InterParame, float FloatParam, AActor* ActorParam);
void ARPCTes::ServerRPCTest_Implementation(int32 IntegerParam, float FloatParam, AActor* ActorParm)
{
}
✏️ Multicast RPC
- 모든 클라이언트에서 함수를 호출하도록 지시할 때 사용
- 총알을 발사하는 캐릭터의 발사 애니메이션 재생 등
- Multicast RPC를 실행하려면 Multicast RPC를 정의한 액터 인스턴스의 서버에서 호출해야 한다.
UFUNCTION(NetMulticast)
void MulticastRPCFunction(int32 InterParam, float FloatParam, AActor* ActorParam);
void ARPCTest: :MulticastRPCTest_Implementation(int32 Integer Param, float FloatParam, AActor* ActorParam)
{
}
✏️ Client RPC
- RPC를 정의한 액터를 소유한 클라이언트에서만 함수를 실행 시 사용
- 발사체를 맞았을 때 해당 클라이언트에서만 들리는 피격 사운드 등
- Client RPC를 실행하려면 Client RPC를 정의한 액터 인스턴스의 서버에서 호출해야 한다.
UFUNCTION(NetMulticast)
void ClientRPCFunction(int32 Integer Param float FloatParam, AActor* ActorParam);
void ARPCTest::ClientRPCTest_Implementation(int32 IntegerParam, float FloatParam, AActor* ActorParam)
✏️ WithValidation
- RPC를 정의할 때, RPC를 호출하기 전 입력이 잘못된 경우 RPC를 호출하지 않도록 하기 위한 추가 보안 옵션으로 사용할 수 있다.
- WithValication 지정자를 사용하면 RPC 실행 여부를 나타내는 bool을 반환하는 함수의 _Validate 버전을 구현해야한다.
UFUNCTION(Server, WithValidation)
void ServerSetHealth(float NewHealth);
bool ARPCTest::ServerSetHealth_Validate(float NewHealth)
{
return NewHealth <= MaxHealth;
}
✏️ Reliable
- 원격 장치가 수신을 확일할 때까지 요청을 반복해 RPC가 실행됐는지를 확인하는 추가 옵션 지정자.
- 중요한 게임 플레이 로직을 실행하는 등의 매우 중요한 RPC에만 사용해야한다.
UFUNCTION(Server, Reliable)
void ServerReliableRPCFunction(int32 IntegerParameter);
📄 참고 자료