PART 9 - 강의 2/3
Host Migration
호스트 이탈 시 세션 유지 전략
01
Host Migration 개요
왜 Host Migration이 필요한가
문제 상황
┌─────────────────────────────────────────────────────────────┐
│ Host Migration이 필요한 이유 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [Host 이탈 시나리오] │
│ - 호스트 게임 종료 │
│ - 호스트 네트워크 연결 끊김 │
│ - 호스트 크래시 │
│ │
│ [Host Migration 없이] │
│ Host 이탈 → 모든 클라이언트 연결 끊김 → 세션 종료 │
│ → 진행 상황 손실! │
│ │
│ [Host Migration 있을 때] │
│ Host 이탈 → 새 Host 선출 → 상태 복원 → 게임 계속 │
│ │
│ [구현 복잡도] │
│ UE5는 Host Migration을 기본 제공하지 않음 │
│ 직접 구현 필요 (외부 서비스 활용 권장) │
│ │
└─────────────────────────────────────────────────────────────┘
02
상태 동기화 설계
Migration을 위한 상태 관리
C++
// 게임 상태 스냅샷 구조체
USTRUCT(BlueprintType)
struct FGameStateSnapshot
{
GENERATED_BODY()
// 게임 진행 상태
UPROPERTY()
float MatchTime;
UPROPERTY()
int32 CurrentRound;
UPROPERTY()
TArray<FTeamScore> TeamScores;
// 플레이어 상태
UPROPERTY()
TArray<FPlayerSnapshot> PlayerSnapshots;
// 월드 오브젝트 상태
UPROPERTY()
TArray<FActorSnapshot> ImportantActors;
};
USTRUCT()
struct FPlayerSnapshot
{
GENERATED_BODY()
UPROPERTY()
FUniqueNetIdRepl PlayerId;
UPROPERTY()
FVector Location;
UPROPERTY()
FRotator Rotation;
UPROPERTY()
float Health;
UPROPERTY()
TArray<FItemData> Inventory;
UPROPERTY()
int32 Score;
};
// 주기적 스냅샷 생성 (호스트에서)
void AMyGameState::CreateSnapshot()
{
if (!HasAuthority())
return;
FGameStateSnapshot Snapshot;
Snapshot.MatchTime = MatchTime;
Snapshot.CurrentRound = CurrentRound;
// 플레이어 상태 수집
for (APlayerState* PS : PlayerArray)
{
if (AMyPlayerState* MyPS = Cast<AMyPlayerState>(PS))
{
FPlayerSnapshot PlayerSnap;
PlayerSnap.PlayerId = MyPS->GetUniqueId();
PlayerSnap.Score = MyPS->Score;
// ... 추가 데이터
Snapshot.PlayerSnapshots.Add(PlayerSnap);
}
}
// 스냅샷 브로드캐스트
MulticastSnapshot(Snapshot);
}
03
Migration 프로세스
새 호스트 선출과 복원
C++
// 클라이언트에서 호스트 연결 끊김 감지
void AMyPlayerController::OnNetCleanup(UNetConnection* Connection)
{
Super::OnNetCleanup(Connection);
if (GetNetMode() == NM_Client)
{
// 호스트와 연결 끊김 - Migration 시작
StartHostMigration();
}
}
void UMySessionSubsystem::StartHostMigration()
{
// 1. 모든 클라이언트에게 Migration 시작 알림 (외부 서비스 통해)
NotifyMigrationStart();
// 2. 새 호스트 선출
// 기준: 가장 낮은 핑, 가장 오래 접속, 가장 좋은 하드웨어
FUniqueNetIdRepl NewHostId = ElectNewHost();
if (NewHostId == LocalPlayerId)
{
// 내가 새 호스트
BecomeNewHost();
}
else
{
// 새 호스트에 연결 대기
WaitForNewHost(NewHostId);
}
}
void UMySessionSubsystem::BecomeNewHost()
{
// 1. Listen Server로 맵 재로드
GetWorld()->ServerTravel(
GetWorld()->GetMapName() + "?listen",
true);
// 2. 저장된 스냅샷으로 상태 복원
RestoreGameState(LastSnapshot);
// 3. 다른 클라이언트들의 재접속 대기
WaitForClients();
}
void AMyGameMode::RestoreGameState(
const FGameStateSnapshot& Snapshot)
{
// GameState 복원
if (AMyGameState* GS = GetGameState<AMyGameState>())
{
GS->MatchTime = Snapshot.MatchTime;
GS->CurrentRound = Snapshot.CurrentRound;
}
// 플레이어 데이터는 재접속 시 복원
PendingPlayerRestores = Snapshot.PlayerSnapshots;
}
04
외부 서비스 활용
EOS/Photon을 통한 Migration
권장 접근법
┌─────────────────────────────────────────────────────────────┐
│ Host Migration 구현 전략 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [방법 1: 외부 상태 저장 (권장)] │
│ - EOS/PlayFab/Firebase에 게임 상태 주기적 저장 │
│ - 호스트 이탈 시 새 호스트가 상태 로드 │
│ - 장점: 신뢰성 높음, 구현 단순 │
│ │
│ [방법 2: P2P 상태 공유] │
│ - 모든 클라이언트가 최신 스냅샷 보유 │
│ - 호스트 이탈 시 로컬 스냅샷 사용 │
│ - 단점: 동기화 어려움, 치트 취약 │
│ │
│ [방법 3: Dedicated Server 폴백] │
│ - 평소 Listen Server 사용 │
│ - 호스트 이탈 시 Dedicated Server로 전환 │
│ - 장점: 안정적, 단점: 서버 비용 │
│ │
└─────────────────────────────────────────────────────────────┘
C++
// EOS를 통한 상태 저장 예시
void UMySessionSubsystem::SaveGameStateToCloud(
const FGameStateSnapshot& Snapshot)
{
// EOS Title Storage 또는 Player Data Storage 사용
IOnlineSubsystem* OSS = IOnlineSubsystem::Get("EOS");
IOnlineTitleFilePtr TitleFile = OSS->GetTitleFileInterface();
// 스냅샷 직렬화
TArray<uint8> Data;
FMemoryWriter Writer(Data);
Writer << Snapshot;
// 클라우드에 저장
FString FileName = FString::Printf(
TEXT("session_%s_snapshot.dat"),
*CurrentSessionId);
TitleFile->WriteUserFile(
GetLocalPlayer()->GetPreferredUniqueNetId().GetUniqueNetId(),
FileName,
Data);
}
실용적 조언
완벽한 Host Migration은 매우 복잡합니다. 코옵 게임에서는 "호스트 이탈 시 세션 종료 + 자동 저장/로드"가 더 현실적인 접근일 수 있습니다.
SUMMARY
핵심 요약
- Host Migration - 호스트 이탈 시 새 호스트로 세션 유지
- 상태 스냅샷 - 주기적으로 게임 상태 저장
- 호스트 선출 - 핑, 접속 시간, 하드웨어 기준
- 외부 서비스 - EOS/클라우드에 상태 저장이 가장 안정적
PRACTICE
도전 과제
배운 내용을 직접 실습해보세요
실습 1: 호스트 이탈 감지
AGameModeBase::Logout()와 NetworkFailure 델리게이트를 사용하여 호스트 연결 끊김을 감지하세요. 클라이언트에서 "호스트가 나갔습니다" UI를 표시하는 로직을 구현하세요.
실습 2: 게임 상태 직렬화
FMemoryWriter/FMemoryReader를 사용하여 현재 게임 상태(스코어, 남은 시간, 플레이어 위치)를 바이트 배열로 직렬화/역직렬화하는 시스템을 구현하세요.
심화 과제
EOS Lobby를 사용한 Host Migration 시스템을 구현하세요. 호스트 이탈 시 최저 핑 클라이언트를 새 호스트로 선출하고, 직렬화된 게임 상태를 전달하여 새 Listen Server에서 게임을 이어가는 전체 파이프라인을 완성하세요.