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에서 게임을 이어가는 전체 파이프라인을 완성하세요.