
데디케이티드 서버(Dedicated Server) 구조에서 클라이언트끼리는 직접 대화할 수 없다. 채팅 시스템을 구현하려면 '클라이언트 -> 서버 -> 모든 클라이언트'로 이어지는 릴레이 방식이 필요하다. 오늘은 RPC를 활용한 채팅 및 접속 알림 구현과, 변수 값을 자동으로 동기화해주는 프로퍼티 레플리케이션(Property Replication)의 기초를 정리했다.
NetMulticast와 GameState를 이용해 접속 알림 띄우기지난 시간에 배운 것처럼 데디케이티드 서버 환경에서 클라이언트 A는 클라이언트 B의 존재를 직접적으로 알거나 통신할 수 없다. 따라서 채팅은 다음과 같은 3단계 릴레이로 구현해야 한다.
Server RPC로 서버에 보낸다. ("서버야, 나 이 말 했어.")Client RPC를 호출해 메시지를 띄운다. ("모두들 들어라, A가 이렇게 말했다.")❓ 왜 PlayerController에서 NetMulticast를 안 쓸까?
NetMulticast는 "해당 액터가 존재하는 모든 단말"에 함수를 실행한다. 하지만 PlayerController는 보안상 서버와 내 PC(Local)에만 존재하고, 남의 PC(Remote)에는 존재하지 않는다.
따라서 PlayerController에서 멀티캐스트를 쏘면 나랑 서버에만 채팅이 뜨고, 다른 친구 화면에는 뜨지 않는 참사가 벌어진다. 그래서 번거롭더라도 서버가 반복문(Iterator)을 돌며 일일이 Client RPC를 쏴주는 방식을 사용했다.
반면, "누군가 게임에 접속했습니다" 같은 알림은 모든 클라이언트에게 방송해야 한다. 이때는 NetMulticast가 아주 유용하다.
OnPostLogin)하면 이를 감지한다.따라서 흐름은 다음과 같다.
1. GameMode가 접속을 감지 GameState의 함수 호출.
2. GameState는 모든 클라이언트가 다 가지고 있으므로, 여기서 NetMulticast 함수를 실행.
3. 모든 클라이언트의 화면에 접속 알림 출력.
RPC가 "함수 실행"을 요청하는 것이라면, 레플리케이션은 "변수 값의 동기화"다.
서버에서 변수 값이 바뀌면, 연결된 클라이언트들의 해당 변수 값도 자동으로 갱신된다.
bReplicates = true여야 함.UPROPERTY(Replicated) 지정.GetLifetimeReplicatedProps 함수에서 DOREPLIFETIME 매크로 설정.// 서버가 받아서 -> 모든 클라이언트에게 뿌린다.
void ACXPlayerController::ServerRPCPrintChatMessageString_Implementation(const FString& InString)
{
// 월드에 있는 모든 플레이어 컨트롤러를 순회한다.
for (TActorIterator<ACXPlayerController> It(GetWorld()); It; ++It)
{
ACXPlayerController* PC = *It;
if (IsValid(PC))
{
// 각 PC에게 Client RPC를 쏴준다. (귓속말을 모두에게 하는 느낌)
PC->ClientRPCPrintChatMessageString(InString);
}
}
}
| 개념 | 설명 | 비고 |
|---|---|---|
| Server RPC | 클라이언트가 서버에게 요청 (채팅 전송) | Validation 필요 (보안) |
| Client RPC | 서버가 특정 클라이언트에게 요청 (채팅 수신) | 소유권(Ownership)이 중요함 |
| NetMulticast | 서버가 해당 액터를 가진 모든 단말에게 실행 | PlayerController에서는 사용 주의 |
| Property Replication | 서버의 변수 변경을 클라이언트에 자동 동기화 | DOREPLIFETIME 설정 필요 |
| 채팅 로직 | Client A Server Loop All Clients | PC는 남의 컴퓨터에 없으므로 이 방식 사용 |