서버 초기화와 클라이언트의 접속 플로우의 이해

  • 어플리케이션의 네트웍 모드를 파악하는 방법의 학습
  • 네트웍 멀티플레이어 환경 분석을 위한 효율적인 로그 매크로(Log Macro) 제작
  • 게임 모드를 중심으로 진행되는 로그인 플로우의 학습

 

 

인프런 이득우님의 '언리얼 프로그래밍 Part3 - 네트웍 멀티플레이 프레임웍의 이해' 강의를 참고하였습니다. 
😎 [이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이 프레임웍의 이해] 강의 들으러 가기!

 

 

 

 

목차

     

     


     

     

     

    어플리케이션의 네트웍 모드


     

     

    학습 주제:  Network Mode & Login

     


     

     

    Listen Server vs. Dedicated Server

     

    2023.09.15 - [⭐ Unreal Engine/UE 개념정리] - [Unreal] 언리얼의 서버모델: 리슨서버, 데디케이티드 서버, 리플리케이션(Replication)

     

    [Unreal] 언리얼의 서버모델: 리슨서버, 데디케이티드 서버, 리플리케이션(Replication)

    리슨 서버는 사용자의 클라이언트 자체가 서버 호스트가 되는 서버모델이다. 클라이언트 중 한명이 호스트를 담당하여 서버의 역할을 수행한다. 데디케이트 서버는 호스트 역할만을 전담하는

    designerd.tistory.com


     

     

     

    게임 모드 기반의 로그인 플로우  -  Play In Editor (PIE)


     

     

    리슨서버 과정

     

    1. 서버의 초기화
    2. 플레이어 로그인
    3. 리슨 서버의 시작
    4. 클라이언트의 접속 허용
    5. 클라이언트의 초기화

     

     


     

     

     

    네트웍 멀티플레이용 로그 매크로 제작


     

     

    네트웍 멀티플레이용 로그 매크로를 구현한 코드

     

    매크로 헤더

    #pragma once
    #include "CoreMinimal.h"
    
    #define LOG_NETMODEINFO ((GetNetMode() == ENetMode::NM_Client) ? *FString::Printf(TEXT("CLIENT%d"), GPlayInEditorID) : ((GetNetMode() == ENetMode::NM_Standalone) ? TEXT("STANDALONE") : TEXT("SERVER"))) 
    #define LOG_CALLINFO ANSI_TO_TCHAR(__FUNCTION__)
    #define AB_LOG(LogCat, Verbosity, Format, ...) UE_LOG(LogCat, Verbosity, TEXT("[%s] %s %s"), LOG_NETMODEINFO, LOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__))
    
    DECLARE_LOG_CATEGORY_EXTERN(LogABNetwork, Log, All);

     

     

    매크로 헤더.cpp

    #include "ArenaBattle.h"
    #include "Modules/ModuleManager.h"
    
    DEFINE_LOG_CATEGORY(LogABNetwork);
    IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ArenaBattle, "ArenaBattle" );

     

     

    실행화면

     


     

     

     

     

    로그인 순서


     

     

    게임 모드의 주요 함수 + 로그인 순서

     

    게임 모드의 주요 함수 (로그인 순서대로 정리)

    1. PreLogin :  클라이언트의 접속 요청을 처리하는 함수
      1. 로그인이 아직 안 된 상태.
      2. PreLogin을 통과해야 비로서 로그인 됨.
      3. 접속 거부도 가능
      4. 서버 역할을 하는 컴퓨터는 이 단계를 거치지 않고 바로 Login 단계로 넘어감.
    2.  Login :  접속을 허용한 클라이언트에 대응하는 PlayerController를 만드는 함수
      1. 접속을 허용한 플레이어에 대한 객체를 생성하고 플레이어를 대변하는 객체에 대한 PlayerController를 만든다.
      2. return 값은 APlayerController
      3. 로그인 완료
    3.  PostLogin :  플레이어 입장을 위해 플레이어에 필요한 기본 설정을 모두 마무리하는 함수
      1. 해당 PlayerController가 빙의할 캐릭터 세팅까지 마치고 PostLogin에 보낸다.
      2. 플레이어의 기본 설정을 모두 마무리 하고 설정까지 하여 게임 시작 준비를 마친다.
      3. 게임 시작과 로그인은 별개의 개념이다.
      4. (리슨서버 기준) Standalone에서 PostLogin까지 끝낸 다음 클라이언트 접속이 가능하면, 서버 모드로 전환된다. 그 후, 서버에서 PreLogin이 실행된다.
    4. StartPlay :  게임의 시작을 지시하는 함수
      1. 서버 모드 전환 후 실행된다.
      2. 조건이 충족되면 서버에서 게임의 시작을 지시하는 함수다.
      3. StartPlay 함수가 호출되지 않으면, 게임이 시작되지 않은다.
    5.  BeginPlay :  GameMode의 StartPlay를 통해 게임이 시작될 때 모든 액터에서 호출하는 함수
      1. 레벨 위에 있는 모든 액터이 BeginPlay를 호출하여 게임 콘텐츠를 시작한다.

     


     

     

    ABGameMode

     

    ABGameMode.h

    #pragma once
    #include "CoreMinimal.h"
    #include "GameFramework/GameModeBase.h"
    #include "Interface/ABGameInterface.h"
    #include "ABGameMode.generated.h"
    
    UCLASS()
    class ARENABATTLE_API AABGameMode : public AGameModeBase, public IABGameInterface
    {
    	GENERATED_BODY()
    	
    public:
    	AABGameMode();
    	virtual void PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override;
    	virtual APlayerController* Login(UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override;
    	virtual void PostLogin(APlayerController* NewPlayer) override;
    	virtual void StartPlay() override;
    };

     

     

     

    ABGameMode.cpp

    #include "Game/ABGameMode.h"
    #include "ABGameMode.h"
    #include "Player/ABPlayerController.h"
    #include "ArenaBattle.h"
    #include "ABGameState.h"
    
    AABGameMode::AABGameMode()
    {
    	static ConstructorHelpers::FClassFinder<APawn> DefaultPawnClassRef(TEXT("/Script/Engine.Blueprint'/Game/ArenaBattle/Blueprint/BP_ABCharacterPlayer.BP_ABCharacterPlayer_C'"));
    	if (DefaultPawnClassRef.Class)
    	{
    		DefaultPawnClass = DefaultPawnClassRef.Class;
    	}
    	static ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerClassRef(TEXT("/Script/ArenaBattle.ABPlayerController"));
    	if (PlayerControllerClassRef.Class)
    	{
    		PlayerControllerClass = PlayerControllerClassRef.Class;
    	}
    
    	GameStateClass = AABGameState::StaticClass();
    }
    
    void AABGameMode::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
    {
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("============================================================"));
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
    
    	Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
    }
    
    APlayerController* AABGameMode::Login(UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
    {
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
    	APlayerController* NewPlayerController = Super::Login(NewPlayer, InRemoteRole, Portal, Options, UniqueId, ErrorMessage);
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
    
    	return NewPlayerController;
    }
    
    void AABGameMode::PostLogin(APlayerController* NewPlayer)
    {
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
    	Super::PostLogin(NewPlayer);
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
    }
    
    void AABGameMode::StartPlay()
    {
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
    	Super::StartPlay();
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
    }

     


     

     

    로그인 과정 로그

     

    서버

    • 서버는 PreLogin 단계가 없다. (자기 자신이 곧 법인데 굳이 로그인을 검사할 필요가 없다).

     

    클라이언트


     

     

     

    게임의 시작


     

     

    게임의 시작

     

     

    GameMode는 서버만 가지고 클라이언트에는 없다.

    반면에 GameState은 서버와 클라이언트 모두 존재한다.

     

    GameState라는 액터는 게임 정보를 알려주는 특별한 액터다. 서버에서 GameState에 게임 정보를 알려준 후 이 GameState는 클라이언트에 복제된다. 

     


     

    ABGameState

     

    ABGameState.h

    #pragma once
    #include "CoreMinimal.h"
    #include "GameFramework/GameStateBase.h"
    #include "ABGameState.generated.h"
    
    UCLASS()
    class ARENABATTLE_API AABGameState : public AGameStateBase
    {
    	GENERATED_BODY()
    	
    public:
    	virtual void HandleBeginPlay() override;
    	virtual void OnRep_ReplicatedHasBegunPlay() override;
    };

     

     

    ABGameState.cpp

    #include "Game/ABGameState.h"
    #include "ArenaBattle.h"
    
    void AABGameState::HandleBeginPlay()
    {
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
    	Super::HandleBeginPlay();
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
    }
    
    void AABGameState::OnRep_ReplicatedHasBegunPlay()
    {
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("Begin"));
    	Super::OnRep_ReplicatedHasBegunPlay();
    	AB_LOG(LogABNetwork, Log, TEXT("%s"), TEXT("End"));
    }