3. 네트워크 환경 구성하기
C. NetMode를 이용한 나만의 라이브러리 함수 만들기
액터에는 GetNetMode라는 함수가 있는데 이를 통해 현재 실행중인 환경이 무엇인지 확인이 가능하다.
enum ENetMode
{
/** Standalone: a game without networking, with one or more local players. Still considered a server because it has all server functionality. */
NM_Standalone,
/** Dedicated server: server with no local players. */
NM_DedicatedServer,
/** Listen server: a server that also has a local player who is hosting the game, available to other players on the network. */
NM_ListenServer,
/**
* Network client: client connected to a remote server.
* Note that every mode less than this value is a kind of server, so checking NetMode < NM_Client is always some variety of server.
*/
NM_Client,
NM_MAX,
};
언리얼 엔진의 EngineBaseTypes.h에 해당 함수의 반환 타입인 ENetMode가 있다. 내용을 보면 스탠드얼론(서버 연결이 없는 싱글 플레이), 데디케이티드 서버, 리슨 서버, 클라이언트가 있다.
이를 이용하여 나만의 라이브러리 함수를 만든다.

라이브러리 함수를 만들 클래스를 만든다. C++코드 생성시 부모를 선택하지 않는다. 참고로 이 방식으로 코드 생성시 IDE에서는 표시가 되나 에디터의 콘텐츠 브라우저에는 표시가 안된다.
class BASEBALLGAME_API FCustomLibrary
{
public:
//클라이언트, 서버 여부에 따라 적절한 로그 출력을 한다.
static void PrintString(const AActor* InWorldContextActor, const FString& InString, float InTimeToDisplay = 1.f, FColor InColor = FColor::Cyan);
//지정한 액터를 통해 NetMode가 무엇인지 출력한다.
static FString GetNetModeString(const AActor* InWorldContextActor);
};
헤더에는 2개의 public static 함수를 선언하는데, 전자는 원래 쓰던 PrintString 함수 대신 필요한 기능만 사용하여 새로 만든 함수다. 후자는 현재 어느 환경에서 실행하는지를 String으로 반환하는 함수다.
FString FCustomLibrary::GetNetModeString(const AActor* InWorldContextActor)
{
FString NetModeString = TEXT("None");
if (IsValid(InWorldContextActor))
{
if (const ENetMode NetMode = InWorldContextActor->GetNetMode(); NetMode == NM_Client)
{
NetModeString = TEXT("Client");
}
else
{
if (NetMode == NM_Standalone)
{
NetModeString = TEXT("StandAlone");
}
else
{
NetModeString = TEXT("Server");
}
}
}
return NetModeString;
}
현재 실행하는 환경을 출력하는 함수를 먼저 보자면, GetNetMode 함수의 결과에 따라 적절한 이름을 FString으로 반환한다. 서버는 리슨 서버, 데이케이티드 서버 구분 없이 그냥 "Server"하나로 퉁쳤다.
void FCustomLibrary::PrintString(const AActor* InWorldContextActor, const FString& InString, float InTimeToDisplay, FColor InColor)
{
if (!IsValid(GEngine) || !IsValid(InWorldContextActor))
{
return;
}
if (const ENetMode NetMode = InWorldContextActor->GetNetMode(); NetMode == NM_Client || NetMode == NM_ListenServer)
{
if (InWorldContextActor->GetNetMode() == NM_Client || InWorldContextActor->GetNetMode() == NM_ListenServer)
{
//클라이언트 또는 리슨 서버는 GEngine으로 출력
GEngine->AddOnScreenDebugMessage(-1, InTimeToDisplay, InColor, InString);
}
else
{
//데디케이티드 서버는 UE_LOG로 출력
UE_LOG(LogTemp, Log, TEXT("%s"), *InString);
}
}
}
기존의 PrintString을 대체하는 함수의 내용은 다음과 같다.
- 로그용 텍스트를 출력하는 것이 전부다. 언리얼 엔진에서 제공하는 함수의 내용은 생각보다 내용이 많아서 이 중 불필요한 기능을 제거하고 출력하는 기능만 남겼다.
- GetNetMode 함수를 사용하여 클라이언트 또는 리슨 서버로 확인되면 화면에 띄운다.
- 만약 데이케이티드 서버로 확인되면 UE_LOG를 사용한다.
void ABaseballPlayerController::PrintChatMessageString(const FString& ChatMessage)
{
FCustomLibrary::PrintString(this, ChatMessage, 5.0f, FColor::Red);
}
플레이어 컨트롤러에서 출력하는 부분을 새로 만든 함수로 교체한다.
D. NetRole이 빠질 순 없지
액터는 GetLocalRole함수와 GetRemoteRole 함수가 있다. 두 함수 모두 같은 타입의 enum을 반환한다.
enum ENetRole : int
{
/** No role at all. */
ROLE_None UMETA(DisplayName = "None"),
/** Locally simulated proxy of this actor. */
ROLE_SimulatedProxy UMETA(DisplayName = "Simulated Proxy"),
/** Locally autonomous proxy of this actor. */
ROLE_AutonomousProxy UMETA(DisplayName = "Autonomous Proxy"),
/** Authoritative control over the actor. */
ROLE_Authority UMETA(DisplayName = "Authority"),
ROLE_MAX UMETA(Hidden),
};
그 enum은 언리얼 엔진의 EngineTypes.h에 있는데 각 타입은 다음과 같다.
- None : 오직 자기 자신에게만 존재하는 액터
- Authority : 주인이 자신이며 다른 클라이언트나 서버에 복제본이 있는 액터, 서버가 이 액터를 가지고 있으면 중요한 권한을 가졌다고 짐작할 수 있다.
- Simulated Proxy : 복제본인 액터, 오직 Authority에서의 모습을 반영만 할 뿐이다.
- Autonomous Proxy : 복제본인 액터, 서버로 부터 데이터를 받아 동기화도 되면서 서버로 송신하기도 한다.
//해당 액터의 NetRole이 무엇인지 출력한다.
static FString GetRoleString(const AActor* InActor);
나만의 라이브러리 함수를 1개 더 선언한다.
FString FCustomLibrary::GetRoleString(const AActor* InActor)
{
FString RoleString = TEXT("None");
if (IsValid(InActor))
{
const FString LocalRoleString = UEnum::GetValueAsString(TEXT("Engine.ENetRole"), InActor->GetLocalRole());
const FString RemoteRoleString = UEnum::GetValueAsString(TEXT("Engine.ENetRole"), InActor->GetRemoteRole());
RoleString = FString::Printf(TEXT("%s / %s"), *LocalRoleString, *RemoteRoleString);
}
return RoleString;
}
지정한 액터의 NetRole을 출력하되 "{Local Role} / {Remote Role}"형식으로 반환한다.
왜 Local과 Remote로 나눴는지 설명하자면, 일단 결론부터 말하면 액터의 조종 주체가 자신인지 구분하기 위함이다. 아래의 예시 상황을 보고 이해해보자
- 서버에 클라이언트 하나가 접속하여 캐릭터를 생성했다. 이 캐릭터인 액터를 A라고 한다.
- 그리고 서버에서 자체적으로 생성한 액터 B가 있다고 한다. B는 서버 내에서만 사용할 것이라서 접속한 클라이언트에게는 존재하지 않는다.
- 서버는 클라리언트의 상황을 알아야 하니 A가 서버에 존재한다. 이로써 서버는 A와 B 모두 존재한다.
- 여기서 서버 입장에서는 A와 B 모두 Local Role이 Authority다. 캐릭터인 A는 서버-클라이언트간 동작의 보안을 위해 실제 생성은 서버가 하고 클라이언트는 Autonomous Proxy인 복제본을 가지고 있다. 즉 서버에게 A의 Local Role은 Authorit 이고 Remote Role이 Autonomous Proxy다. 반면 B는 서버에서 생성했으니 동일하게 Local Role이 Authority다. 하지만 클라이언트에게 존재하지 않으니 Remote Role이 None이다.
- 이렇게 Local과 Remote가 발생하는 점을 이용하여 조종의 주체가 자신인지 구분할 수 있다.
'언리얼엔진 학습 및 과제' 카테고리의 다른 글
| 언리얼 네트워크 관련 사전 정리 (0) | 2025.11.26 |
|---|---|
| 온라인 채팅 구현하기 (4) (0) | 2025.11.25 |
| 온라인 채팅 구현하기 (2) (0) | 2025.11.20 |
| 온라인 채팅 구현하기 (1) (0) | 2025.11.19 |
| 팀 프로젝트 슈터 게임 (8) (0) | 2025.11.14 |