프로젝트 회고 / / 2025. 12. 2. 23:13

[Dedicated Server] 비동기 폴링 기반 서버 접속 시도

반응형

🎮 구현 목표

플레이어가 "게임 참가" 버튼을 클릭하면 게임 세션 상태와 무관하게 자동으로 접속되도록 합니다. 게임 세션이 없으면 생성하고, 활성화 중이면 대기했다가 자동으로 접속하는 것이 목표입니다.

 

 

🚨 문제 상황

게임 세션 생성 타이밍 문제

 

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 개선: "연결 중..." 메시지로 진행 상황 피드백

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유