🎮 구현 목표
플레이어가 "게임 참가" 버튼을 클릭하면 게임 세션 상태와 무관하게 자동으로 접속되도록 합니다. 게임 세션이 없으면 생성하고, 활성화 중이면 대기했다가 자동으로 접속하는 것이 목표입니다.
🚨 문제 상황
게임 세션 생성 타이밍 문제
AWS GameLift에서 게임 세션 생성 요청을 보내면 즉시 활성화되지 않습니다:
1. Lambda에 게임 세션 생성 요청 (CreateGameSession)
2. GameLift가 EC2 인스턴스에 게임 세션 할당
3. 게임 서버 프로세스 초기화
4. 상태: ACTIVATING (약 3~10초 소요)
5. 상태: ACTIVE (플레이어 접속 가능)
기존 흐름의 문제:
// 클라이언트: 버튼 클릭
void UGamePage::JoinGameButtonClicked()
{
GameSessionsManager->JoinGameSession(); // 게임 세션 찾기/생성
}
void UGameSessionsManager::FindOrCreateGameSession_Response(...)
{
// Lambda 응답: 새 게임 세션 생성됨
FDSGameSession GameSession;
if (GameSession.Status == "ACTIVATING")
{
// 아직 활성화 안 됨 → 플레이어 세션 생성 실패
TryCreatePlayerSession(...); // ❌ 실패!
}
}
플레이어 경험:
1. "게임 참가" 버튼 클릭
2. 게임 세션 생성 및 활성화 시작
3. "게임 세션을 찾을 수 없습니다" 메시지 ❌
4. 다시 버튼 클릭
5. 게임 세션 상태 검사 : 만약 (ACTIVATING)이면 실패
6. "게임 세션을 찾을 수 없습니다" 메시지 ❌
7. 게임 세션이 ACTIVE 상태가 될 때 까지 실패 반환
플레이어는 게임 세션이 어떤 상태인지 중요하지 않습니다. 입력에 따른 피드백(결과)만 중요합니다.
플레이어가 수동으로 재시도해야 하는 것은 매우 불편한 경험입니다. "왜 안 되지?" 하며 여러 번 클릭하는 것은 피로감을 줍니다.
따라서 내부적으로 어떻게 동작하는지 플레이어가 몰라도 의도한 결과를 받을 수 있도록 내부에서 타이머를 이용해 일정 시간 순환하는 방법을 고려했습니다.
💭 해결 방법
Timer 기반 자동 재시도 (폴링)
로직을 두 단계로 분리했습니다.
- ACTIVE 게임 세션이 존재하면 : 즉시 접속
- 게임 세션이 없다면 : 세션 생성을 요청하고 타이머 예약
핵심 아이디어: 게임 세션 상태를 확인하고, ACTIVATING 상태면 자동으로 재시도
void UGameSessionsManager::HandleGameSessionStatus(const FString& Status, const FString& SessionId)
{
// 플레이어 접속 처리
if (Status.Equals(TEXT("ACTIVE")))
{
// 활성화 완료 → 플레이어 세션 생성
BroadcastJoinGameSessionMessage.Broadcast(
TEXT("Found activate Game Session. Creating a Player Session..."), false);
if (UDS_LocalPlayerSubsystem* LocalPlayerSubsystem = GetDSLocalPlayerSubsystem())
{
TryCreatePlayerSession(LocalPlayerSubsystem->GetUsername(), SessionId);
}
}
else if (Status.Equals(TEXT("ACTIVATING")))
{
// 활성화 중 → 0.5초 후 자동 재시도
FTimerDelegate CreateSessionDelegate;
CreateSessionDelegate.BindUObject(this, &ThisClass::JoinGameSession);
APlayerController* LocalPlayerController = GEngine->GetFirstLocalPlayerController(GetWorld());
if (IsValid(LocalPlayerController))
{
LocalPlayerController->GetWorldTimerManager().SetTimer(
CreateSessionTimer,
CreateSessionDelegate,
0.5f, // 0.5초 간격
false // 1회만 실행 (재귀적으로 반복)
);
}
}
else
{
// 예외 상태
BroadcastJoinGameSessionMessage.Broadcast(
TEXT("GameSessionStatus is Not ACTIVE or ACTIVATING"), true);
}
}
실행 흐름:
1. 플레이어: "게임 참가" 버튼 클릭
2. JoinGameSession() 호출 → Lambda에 게임 세션 요청
3. Lambda 응답: Status = "ACTIVATING"
4. HandleGameSessionStatus() → Timer 설정 (0.5초 후)
5. 0.5초 후 JoinGameSession() 자동 재호출
6. Lambda 응답: 여전히 "ACTIVATING" → Timer 재설정
7. 0.5초 후 JoinGameSession() 자동 재호출
8. Lambda 응답: Status = "ACTIVE" → 플레이어 세션 생성
9. 서버 접속 성공 ✅
개선된 플레이어 경험:
1. "게임 참가" 버튼 클릭
2. "게임 세션을 찾는 중..." 메시지
3. (내부적으로 자동 재시도 중)
4. "플레이어 세션 생성 중..." 메시지
5. 로비 레벨로 자동 전환 ✅
플레이어는 한 번만 클릭하고, 내부적으로 알아서 처리됩니다.
Delegate로 UI 피드백
재시도 중에도 플레이어가 "멈춘 게 아니구나"를 알 수 있도록 BroadcastJoinGameSessionMessage Delegate로 상태 메시지를 전달:
// GameSessionsManager.h
UPROPERTY(BlueprintAssignable)
FAPIStatusMessgae BroadcastJoinGameSessionMessage;
// GamePage.cpp - UI가 Delegate 구독
void UGamePage::NativeConstruct()
{
GameSessionsManager->BroadcastJoinGameSessionMessage.AddDynamic(
JoinGameWidget, &UJoinGame::SetStatusMessage);
}
// 상태 변화마다 메시지 업데이트
BroadcastJoinGameSessionMessage.Broadcast(TEXT("Searching for Game Session..."), false);
BroadcastJoinGameSessionMessage.Broadcast(TEXT("Creating Player Session..."), false);
✅ 결과
✅ 원클릭 접속: 플레이어는 버튼 한 번만 클릭
✅ 자동 재시도: ACTIVATING 상태일 때 0.5초 간격으로 폴링
✅ UX 개선: "연결 중..." 메시지로 진행 상황 피드백
'프로젝트 회고' 카테고리의 다른 글
| [Dedicated Server] 콜백(Callback) 체인을 활용한 데이터 무결성 확보 (0) | 2025.12.02 |
|---|---|
| [Dedicated Server] SeamlessTravel: 레벨 전환 시 데이터 유실 방지 (0) | 2025.12.02 |
| [UE5 팀 프로젝트] 지연 초기화: 런타임 액터 스폰, BeginPlay 호출 전 DataTable 데이터 무결성 확보 (0) | 2025.12.02 |
| [UE5 액션] 델리게이트(Delegate)를 활용한 유연한 인벤토리 시스템 (0) | 2025.12.02 |
| [UE5 액션] 데이터 에셋 기반 무기별 전투 스타일 관리 (0) | 2025.12.02 |
