이번 포스팅에서는 언리얼 엔진의 게임 모드 내 네트워크 호출 함수의 흐름을 파악해봅니다.
게임을 시작할 때 네트워크를 사용하는 게임이라면 PreLogin, Login, PostLogin, StartPlay, BeginPlay 함수를 거치게 되는데요.
눈으로 보고 싶다면 로그를 찍는 매크로를 만들어 확인해볼 수 있습니다.
로그 매크로 만들기
// Log Macro 사용
// GetNetMode : 게임의 인스턴스가 수행하는 역할을 정의하는 열거형타입
// GPlayInEditorID : Play In Editor (PIE) 는 각 에디터의 ID 반환, 그렇지 않은 경우는 -1을 반환
// NM_Client : NM은 NetMode의 약자. 게임을 하는 사람을 통해 서버에 접속된 게임의 인스턴스를 묘사
// NM_Standalone : 플레이어가 네트워크 접속을 수반하지 않는 단독 플레이어 게임에 사용되는 모드
#define LOG_NETMODEINFO ((GetNetMode() == ENetMode::NM_Client) ? *FString::Printf(TEXT("Client %d"), GPlayInEditorID) : ((GetNetMode() == ENetMode::NM_Standalone) ? TEXT("STANDALONE") : TEXT("SERVER")))
// __FUNCTION__ : 현재 함수명을 출력함
#define LOG_CALLINFO ANSI_TO_TCHAR(__FUNCTION__)
#define LYRA_TEMP_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(LogLyraNetwork, Log, All);
상속받은 네트워크 함수 내에 로그 매크로 추가
*예시
void ALyraGameMode::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
{
// 상속 함수 호출 전
LYRA_TEMP_LOG(LogLyraNetwork, Log, TEXT("%s"), TEXT("Begin"));
Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
// 상속 함수 호출 후
LYRA_TEMP_LOG(LogLyraNetwork, Log, TEXT("%s"), TEXT("End"));
}
게임 모드의 주요 함수
PreLogin
클라이언트의 접속 요청을 처리하는 함수
void AGameModeBase::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
Login
접속을 허용한 클라이언트에 대응하는 플레이어 컨트롤러를 만드는 함수
APlayerController* AGameModeBase::Login(UPlayer* NewPlayer,ENetRole InRemoteRole, const FString& Portal, const FString& Options, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage)
PostLogin
플레이어 입장을 위해 플레이어의 필요한 기본 설정을 마무리하는 함수
void AGameModeBase::PostLogin(APlayerController* NewPlayer)
StartPlay
게임의 시작을 지시하는 함수
void AGameModeBase::StartPlay()
BeginPlay
게임 모드의 StartPlay 를 통해 게임이 시작될 때 클라이언트 내 모든 액터에서 호출하는 함수
void AActor::BeginPlay()
StartPlay 함수에서 BeginPlay 함수까지의 흐름
GameModeBase 의 StartPlay 함수에서 각 클라이언트의 Actor 클래스 BeginPlay 함수까지, 호출이 어떻게 이어지는걸까요?
서버의 GameModeBase 가 StartPlay 를 지시하면, GameStateBase 의 HandleBeginPlay 를 호출합니다.
void AGameModeBase::StartPlay()
{
GameState->HandleBeginPlay();
}
현재 서버 내 월드에 있는 모든 액터들에게 클라이언트 내 게임 시작을 알립니다(Notify).
void AGameStateBase::HandleBeginPlay()
{
bReplicatedHasBegunPlay = true;
GetWorldSettings()->NotifyBeginPlay();
GetWorldSettings()->NotifyMatchStarted();
}
만약 클라이언트가 추가 생성 (클라이언트 창을 킨다거나) 되면, 해당 클라이언트에 맞는 PlayerController 가 같이 생성됩니다.
이 때의 클라이언트는 게임이 시작되었는지를 알 수 없는 상태로 시작되는데요.
같이 복사된 GameStateBase에 의해 bReplicatedHasBegunPlay 프로퍼티의 속성이 변경(true)되면서 OnRep_ReplicatedHasBegunPlay 함수가 호출됩니다.
UPROPERTY(Transient, ReplicatedUsing = OnRep_ReplicatedHasBegunPlay)
bool bReplicatedHasBegunPlay;
void AGameStateBase::OnRep_ReplicatedHasBegunPlay()
{
if (bReplicatedHasBegunPlay && GetLocalRole() != ROLE_Authority)
{
GetWorldSettings()->NotifyBeginPlay();
GetWorldSettings()->NotifyMatchStarted();
}
}
이 OnRep_ReplicatedHasBegunPlay 함수는GameState를 복제한 각각의 클라이언트의 BeginPlay 함수를 호출해 액터를 시작시킵니다.
언리얼 엔진 게임 플레이 시작 흐름
리슨서버 | 클라이언트 1 | 클라이언트 2 | |
게임모드 (GameMode) |
|||
플레이어 컨트롤러 0 (PlayerController) |
|||
플레이어 컨트롤러 1 (PlayerController) |
복제 ---------------------------> |
플레이어 컨트롤러 0 (PlayerController) |
|
플레이어 컨트롤러 2 (PlayerController) |
복제 -----------------------------------------------------------> |
플레이어 컨트롤러 0 (PlayerController) |
|
게임 스테이트 (GameState) |
복제 ---------------------------> |
게임 스테이트 (GameState) |
|
복제 -----------------------------------------------------------> |
게임 스테이트 (GameState) |
- 리슨 서버는 '나 자신' 의 컨트롤러 뿐만 아니라, 추가 생성되는 모든 클라이언트의 컨트롤러의 오리지널을 소유함
- 리슨 서버에 접속한 클라이언트들은 서버 속에 존재하는 자신의 컨트롤러와 게임 스테이트를 복제한 것을 소유함
'게임개발 > ue server' 카테고리의 다른 글
[UE] 언리얼 엔진의 커넥션 구성 (0) | 2024.06.16 |
---|---|
[UE] 네트워크 모드에서의 액터의 준비와 게임 시작 (1) | 2024.06.16 |
[UE] 어플리케이션의 네트워크 모드 파악하기 (0) | 2024.04.29 |
[UE] 언리얼 엔진의 멀티플레이어 게임 프레임워크 특징과 네트워크 프로그래밍에서 BP가 아닌 C++을 사용해야 하는 이유 (0) | 2024.04.28 |
[UE5] 언리얼 멀티플레이어 네트워킹 (0) | 2024.04.28 |