1. 디버그 로깅 매크로

// 프로그램명.h

#pragma once

#include "CoreMinimal.h"

#pragma region NetLogging

DEDICATEDX_API DECLARE_LOG_CATEGORY_EXTERN(LogDXNet, Log, All);

#define NETMODE_TCHAR ((GetNetMode() == ENetMode::NM_Client) ? *FString::Printf(TEXT("Client%02d"), UE::GetPlayInEditorID()) : ((GetNetMode() == ENetMode::NM_Standalone) ? TEXT("StandAlone") : TEXT("Server")))
#define FUNCTION_TCHAR (ANSI_TO_TCHAR(__FUNCTION__))
#define DX_LOG_NET(LogCategory, Verbosity, Format, ...) UE_LOG(LogCategory, Verbosity, TEXT("[%s] %s %s"), NETMODE_TCHAR, FUNCTION_TCHAR, *FString::Printf(Format, ##__VA_ARGS__))

#pragma endregion
// 프로그램명.cpp

#include "DedicatedX.h"
#include "Modules/ModuleManager.h"

IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, DedicatedX, "DedicatedX" );

#pragma region NetLogging

DEFINE_LOG_CATEGORY(LogDXNet);

#pragma endregion

 

이 매크로는 모든 로그 앞에 [Server], [Client01] 같은 표시와 함수명을 자동으로 붙여준다.

PIE에서 여러 클라이언트를 동시에 띄워 테스트해도 어느 쪽 로그인지 바로 구분할 수 있다는 점에서 멀티플레이 디버깅의 출발점이 되는 도구다

 

*FString 표기에 대해

*ClientConnection->GetName()

 

GetName()은 FString을 반환하는데, UE_LOG의 %s는 TCHAR*만 받을 수 있다.

FString 앞에 *를 붙이면 그 내부의 TCHAR 문자열을 꺼내주는 변환 연산자가 동작한다.

(포인터 역참조의 *와 모양은 같지만 다른 용도)

 

"FString을 로그에 찍을 땐 앞에 별표를 붙인다"는 약속으로 받아들이는 게 편하다

 

2. 접속 과정 디버깅: PreLogin → Login → PostLogin

void ADXGameModeBase::PostLogin(APlayerController* NewPlayer)
{
    Super::PostLogin(NewPlayer);

    UNetDriver* ServerNetDriver = GetNetDriver();
    if (IsValid(ServerNetDriver) == true)
    {
        for (const auto& ClientConnection : ServerNetDriver->ClientConnections)
        {
            if (IsValid(ClientConnection) == true)
            {
                DX_LOG_NET(LogDXNet, Log, TEXT("Client Connection: %s"), *ClientConnection->GetName());
            }
        }
    }
}

 

GameMode::PostLogin()은 서버에만 존재하는 액터의 함수라서 "서버가 클라이언트 접속을 인지하고 있는가"를 확인하기에 가장 적합한 위치다.

ServerNetDriver->ClientConnections를 순회하면 지금 서버에 붙어있는 모든 연결을 확인할 수 있다.

 

반대로 클라이언트 입장에서 "내가 서버에 잘 연결됐는가"를 확인하려면 PlayerController::PostNetInit()을 본다 PlayerController는 로컬 클라이언트 자신에게만 스폰되기 때문이다

 

이게 실제로 어떻게 쓰이는가

이 로그는 무언가를 직접 고치는 코드가 아니라 "문제가 어느 단계에서 발생했는지" 범위를 좁히는 용도다

예를 들어 "클라이언트가 접속했는데 캐릭터가 안 보인다"는 증상이 있을 때, PostLogin에서 ClientConnection이 찍히지 않는다면 네트워크 연결 자체의 문제(방화벽, 포트 등)다

ClientConnection은 찍히는데 캐릭터가 안 보인다면 연결은 정상이고 캐릭터 스폰 로직이나 그 이후 단계의 문제다.

 

3. Owner 속성

PlayerController가 Pawn을 Possess하면 그 Pawn의 Owner는 해당 PlayerController로 설정된다

일부 RPC와 Property Replication은 "이 액터의 Owner가 누구인가"에 따라 전송 대상이 달라지기 때문에, 캐릭터 관련 복제 버그를 만나면 "Owner가 제대로 잡혔는가"를 의심 목록에 넣어야 한다는 걸 배웠다

+ Recent posts