1. 리슨 서버
- 리슨 서버(Listen Server)는 게임을 호스팅하면서 동시에 플레이어로 참여하는 서버
- 즉, 호스트(Host)는 서버 역할을 하면서 직접 게임도 플레이하고, 게스트(Guest)들은 이 서버에 접속하여 게임을 진행
2. 리슨 서버 컨트롤러 구조
Host:
- PlayerController00 (서버 컨트롤러 + 클라이언트 컨트롤러 역할)
- 서버에 존재하는 호스트 플레이어의 컨트롤러.
- 동시에 서버 관리 기능도 수행.
- 게스트 플레이어가 접속하면 해당 플레이어의 PlayerController를 추가로 생성함.
- PlayerController01 (게스트용 컨트롤러 생성)
- 게스트가 접속하면 서버에서 해당 플레이어를 위한 PlayerController를 생성.
- 클라이언트와 연결(Net Connection) 및 PlayerState를 관리.
- RPC 및 상태 동기화를 처리함.
Guest:
- PlayerController01 (클라이언트 컨트롤러)
- 게스트 플레이어의 입력을 처리.
- 서버의 PlayerController01과 연결되어 RPC 및 상태 동기화를 수행.
2. UI Widget을 활용한 채팅 프로그램 예시
이를 바탕으로 각 클라이언트에 채팅 위젯이 할당된 경우, 채팅을 주고 받는 과정을 살펴보겠습니다.
2-1. 리슨 서버 채팅 프로그램 구조

2-2. 리슨 서버 채팅 프로그램 클래스 역할
- 각 클라이언트의 컨트롤러에는 채팅을 주고 받는 기능이 있습니다.
//BaseBallPlayerController.h
UCLASS()
class BASEBALLGAME_API ABaseBallPlayerController : public APlayerController
{
GENERATED_BODY()
public:
UFUNCTION(Server, Reliable, WithValidation)
void ServerSendChatMessage(const FString& Message);
void ServerSendChatMessage_Implementation(const FString& Message);
bool ServerSendChatMessage_Validate(const FString& Message) { return true; }
void OnChatMessageReceived(const FString& Message);
virtual void BeginPlay() override;
private:
UChatWidget* PlayerChatWidget;
void InitializeChatWidget();
};
- GameState에서는 PlayerController의 호출을 받아 모든 클라이언트에게 채팅 메세지를 BroadCast(전파)합니다.
//BaseBallGameState.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "BaseBallGameState.generated.h"
UCLASS()
class BASEBALLGAME_API ABaseBallGameState : public AGameStateBase
{
GENERATED_BODY()
public:
ABaseBallGameState();
UPROPERTY()
TArray<FString> ChatMessages;
UFUNCTION(NetMulticast, Reliable)
void MulticastOnChatMessageReceived(const FString& Message);
void MulticastOnChatMessageReceived_Implementation(const FString& Message);
void ServerAddChatMessage(const FString& Message);
};
2-3. 리슨 서버 채팅 프로그램 동작
(1). 클라이언트에서 채팅을 입력하면 Client - Server RPC를 활용하여 GameState에 전달합니다.
//BaseBallPlayerController.cpp
void ABaseBallPlayerController::ServerSendChatMessage_Implementation(const FString& Message)
{
ABaseBallGameState* GameState = GetWorld()->GetGameState<ABaseBallGameState>();
if (GameState)
{
GameState->ServerAddChatMessage(Message);
}
}
(2). GameState는 서버에서 생성되며 클라이언트와 동기화되기 때문에, NetMultiCast RPC를 호출하면 Host와 Guest 모두 해당 함수를 실행하게 됩니다. 따라서 GameState에서 채팅 메시지를 받은 후 NetMultiCast를 사용해 각 클라이언트의 PlayerController를 찾아야 할 때, Host에서는 Host의 PlayerController만, Guest에서는 Guest의 PlayerController만 찾도록 처리하면 됩니다. 이를 통해 각 클라이언트에 적절한 위젯 업데이트가 이루어집니다.
//BaseBallGameState.cpp
void ABaseBallGameState::MulticastOnChatMessageReceived_Implementation(const FString& Message)
{
//각 클라이언트의 PlayerController에 캐스팅
ABaseBallPlayerController* LocalBPC = Cast<ABaseBallPlayerController>(GetWorld()->GetFirstPlayerController());
if (LocalBPC)
{
LocalBPC->OnChatMessageReceived(Message); // UI 업데이트
}
}
(3). 다시 각 클라이언트의 PlayerController로 돌아와서 실제로 UI Widget 업데이트를 하는 코드입니다.
//BaseBallPlayerController.cpp
void ABaseBallPlayerController::OnChatMessageReceived(const FString& Message)
{
if (PlayerChatWidget)
{
PlayerChatWidget->AddChatMessage(Message); // Update UI
}
}
3. HasAuthority(), IsLocalPlayerController 관련
| 구분 | HasAuthority() | IsLocalPlayerController() |
| 의미 | 서버에서 실행되는 객체인가? | 내 화면을 담당하는 컨트롤러인가? |
| 서버에서 실행되면? | ✅ true | ❌ false |
| 클라이언트에서 실행되면? | ❌ false | ✅ true |
| 사용 용도 | 서버/클라이언트 여부 판별 | UI 업데이트, 로컬 플레이어 판단 |
기본적으로 HasAuthority()와 IsLocalPlayerController()는 위와 같은 판단 기준으로 각 결과값을 가지게 됩니다.
그러나 리슨 서버의 Host 컨트롤러의 경우 서버 컨트롤러의 역할과 클라이언트 컨트롤러 역할을 둘다 가집니다. 그래서 서버와 클라이언트가 분리된 전용 서버(Dedicated Server)와는 다르게 아래와 같은 결과값을 가지게 됩니다.
| 리슨 서버 컨트롤러 유형 | HasAuthority() 값 | IsLocalPlayerController() 값 |
| Host 컨트롤러 | ✅ true | ✅ true |
| Guest 컨트롤러 | ❌ false | ✅ true |
✏️ 멀티플레이 개념은 PlayerController가 전부다..
'TIL > Unreal Engine' 카테고리의 다른 글
| AI ROV(Reciprocal Velocity Obstacles) (0) | 2025.04.24 |
|---|---|
| Unreal Engine에서 SFX 임포트 및 어테뉴에이션 세팅하기 (Feat. 발소리와 BGM) (0) | 2025.04.23 |
| Chaos Destruction (0) | 2025.03.26 |
| 언리얼 엔진 멀티플레이 게임의 GameMode와 GameState (0) | 2025.03.18 |
| Animation Retargeting (0) | 2025.02.14 |