ImitationTrigger — 멀티플레이에서 Server / Client / Role 문맥을 함께 남긴 이유
같은 함수가 찍혀도 의미는 같지 않았습니다
ImitationTrigger는 Unreal Engine 기반의 팀 멀티플레이 프로젝트였습니다.
싱글플레이에서는 로그에:
Text
OnDead called정도만 남겨도 함수가 호출됐다는 사실은 확인할 수 있습니다.
하지만 멀티플레이에서는 바로 다음 질문이 생깁니다.
Text
Server에서 실행됐는가?
Client에서 실행됐는가?
어떤 LocalRole을 가진 Actor인가?
AutonomousProxy인가?
SimulatedProxy인가?
어떤 RPC 문맥인가?같은 함수 이름이 보여도 어디서, 어떤 Role로 실행됐는가에 따라 의미가 달라질 수 있기 때문입니다.
그래서 ImitationTrigger에서는 이 실행 문맥을 한 줄에서 빠르게 읽을 수 있도록 프로젝트 전용 LogITNet과 IT_LOG_* 매크로를 구성했습니다.
목적은 로그를 많이 남기는 것이 아니었습니다
ITLogChannel의 목적은 한 문장으로 줄일 수 있습니다.
로그 메시지에 어느 Network Context에서 실행됐는가를 같이 남긴다.
직접 구현한 주요 구성은 다음과 같습니다.
Text
LogITNet
IT_LOG_NET
IT_LOG_ROLE
IT_LOG_PROXY
IT_LOG_CONN
IT_LOG_RPC각 매크로는 필요한 실행 문맥을 Prefix에 붙이는 역할을 합니다.
Text
IT_LOG_NET
= NetMode + Function
IT_LOG_ROLE
= NetMode + LocalRole + RemoteRole + Function
IT_LOG_PROXY
= NetMode + Proxy Type + Function
IT_LOG_CONN
= NetMode + Connection + Function
IT_LOG_RPC
= NetMode + RPC Type + Function즉 로그의 개수를 늘리는 것보다 한 줄을 봤을 때 실행 주체를 바로 알 수 있도록 형식을 통일하는 것이 목적이었습니다.
왜 일반 UE_LOG만으로는 부족했나
예를 들어 다음과 같은 로그가 있다고 하겠습니다.
Text
OnDead called함수가 실행됐다는 사실은 알 수 있습니다.
하지만 멀티플레이에서는:
Text
Server에서 호출됐는가?
Owning Client인가?
다른 Client의 SimulatedProxy인가?
Server와 Client 양쪽에서
각각 호출된 것인가?를 추가로 확인해야 합니다.
그래서 제가 원한 로그는 개념적으로 이런 형태였습니다.
Text
[Server]
[ROLE_Authority]
[OnDead]
called또는:
Text
[Client00]
[ROLE_SimulatedProxy]
[OnDead]
called중요한 점은 위 내용이 실제 Runtime Capture를 복사한 것이 아니라 의도한 Output Format을 설명하기 위한 예시라는 것입니다.
현재 Source / Git 구현 근거와 실제 Runtime Log Evidence는 분리해서 봅니다.
UE_LOG를 대체하는 별도 시스템은 아닙니다
ITLogChannel은 거대한 Logging Framework가 아닙니다.
기반은 기존 Unreal Logging입니다.
Text
UE_LOG
+
Project LogCategory
+
Network Context Prefix정도의 얇은 Utility입니다.
구조를 줄이면:
Text
Actor Function
→ IT_LOG_*(...)
→ UE_LOG
→ Output Log입니다.
즉 ITLogChannel 자체가 Gameplay State를 가지거나 Runtime을 관리하지 않습니다.
Text
Gameplay State Owner
= 아님
Runtime Manager
= 아님
Network System
= 아님
Passive Debug Utility
= 맞음이 경계를 명확히 두고 있습니다.
가장 먼저 구분한 것은 Server와 Client였습니다
멀티 PIE 환경에서는 같은 로그가 여러 Instance에서 동시에 보일 수 있습니다.
그래서 NetMode를 기준으로:
Text
Server
Client00
Client01
Standalone처럼 실행 위치를 구분할 수 있도록 했습니다.
이 정보만 있어도 다음 질문을 훨씬 빠르게 볼 수 있습니다.
Text
Server에서만 실행돼야 하는 코드가
Client에서도 실행되는가?
Owning Client에서 기대한 코드가
다른 Client에서도 실행되는가?그리고 함수명도 자동으로 Prefix에 포함해, 호출부마다 문자열을 반복해서 작성하는 일을 줄였습니다.
Text
NetMode
+
Function
+
Message가 가장 기본적인 형태입니다.
Actor Role도 같이 봤습니다
멀티플레이에서는 Server / Client 구분만으로 충분하지 않을 때가 있습니다.
같은 Client 안에서도 Actor가:
Text
ROLE_Authority
ROLE_AutonomousProxy
ROLE_SimulatedProxy중 어떤 Role을 가지는지에 따라 실행 의미가 달라질 수 있기 때문입니다.
그래서 Role을 확인할 때는:
Text
NetMode
+
LocalRole
+
RemoteRole
+
Function이 같이 보이도록 구성했습니다.
이를 통해 단순히:
Text
이 함수가 Client에서 실행됐다.에서 끝나는 것이 아니라:
Text
어떤 Client에서
어떤 Actor Role로
어떤 함수가 실행됐는가까지 한 번에 읽는 것을 목표로 했습니다.
Proxy / Connection / RPC도 필요한 문맥만 골라 남깁니다
모든 로그에 모든 정보를 넣지는 않았습니다.
확인하려는 문제에 따라 필요한 매크로를 선택합니다.
Text
Actor Role 확인
→ IT_LOG_ROLE
Proxy 구분
→ IT_LOG_PROXY
Connection 문맥
→ IT_LOG_CONN
RPC 실행 문맥
→ IT_LOG_RPC이렇게 한 이유는 Debug 정보가 많다고 항상 좋은 것은 아니기 때문입니다.
Text
필요한 Context만 남긴다.
형식은 일정하게 유지한다.
한 줄을 보고 실행 주체를 빠르게 판단한다.가 이 Utility의 사용 방향이었습니다.
작은 Utility인 만큼 한계도 있습니다
매크로 방식은 가볍고 빠르게 사용할 수 있지만 범용 Framework는 아닙니다.
Text
장점
- 반복 Prefix 코드 감소
- NetMode / Role 형식 통일
- 함수명 자동 포함
- Server / Client 로그 비교 용이
비용
- Macro 기반이라 Type-safe Interface가 아님
- Actor Context 의존성이 있음
- 많이 사용하면 로그가 과도해질 수 있음
- Feature State를 자동으로 이해하는 Tool은 아님프로젝트 규모가 더 커졌다면 Actor가 아닌 Object에서도 사용할 수 있도록 World Context 기반 Helper나 LogCategory 분리를 더 고민할 수 있었을 것입니다.
하지만 현재 프로젝트에서는 팀 멀티플레이 Debug에서 반복적으로 필요한 실행 문맥을 빠르게 읽기 위한 작은 Utility로 두는 것이 적절했습니다.
Debug Log는 Observation Surface입니다
이 부분도 중요하게 구분합니다.
Text
Gameplay State
= 실제 Gameplay System이 소유
ITLogChannel
= 그 Runtime 문맥을 관찰예를 들어 로그에:
Text
[Server]
[ROLE_Authority]가 찍혔다고 해서 ITLogChannel이 Authority State를 소유하는 것은 아닙니다.
로그는:
Text
현재 Runtime에서
어떤 Context가 관찰됐는가를 보여줄 뿐입니다.
그래서 Debug Utility를 설명할 때도 State Owner와 Observation Tool을 섞지 않습니다.
ADS Camera Core와도 분리해서 설명합니다
ImitationTrigger에서 제가 가장 중심으로 설명하는 Camera Flow는:
Text
CameraMode.ADS
→ CameraModeRules
→ CM_ThirdPerson_ADS
→ CameraModeStack입니다.
ITLogChannel은 이 Flow를 실행하는 시스템이 아닙니다.
Text
ITLogChannel
!=
ADS Camera Core정확한 위치는:
Text
Supporting Network Debug Utility입니다.
필요하다면 향후 ADS Runtime을 검증할 때 Input, Ability, CameraMode Selection 같은 Boundary에 임시 로그를 추가해 Network Context를 함께 볼 수 있습니다.
하지만:
Text
활용할 수 있다
!=
이미 ADS 문제를 이 Utility로 해결했다입니다.
현재 확보된 Evidence보다 Claim을 넓히지 않습니다.
제가 직접 맡은 범위
ITLogChannel에서 직접 구현으로 설명하는 부분은 다음입니다.
Text
LogITNet
IT_LOG_NET
IT_LOG_ROLE
IT_LOG_PROXY
IT_LOG_CONN
IT_LOG_RPC현재 main에서 실제 소비 근거는 제한적이며 대표적으로 ITForbiddenArea의 IT_LOG_ROLE 사용이 확인되어 있습니다.
반대로 다음을 제 구현으로 가져가지는 않습니다.
Text
Lobby / Matching / Dedicated Server 전체
Replication 전체
Character / Weapon Network 전체
ASC / Ability Framework 전체즉 이 작업은 네트워크 전체를 구현했다는 증거가 아니라, 멀티플레이 Runtime을 볼 때 필요한 Network Context를 공통 형식으로 만든 직접 구현 Debug 작업입니다.
현재 검증 범위
현재 안전하게 설명할 수 있는 범위는 다음입니다.
Text
ITLogChannel Source
= CONFIRMED
LogITNet / IT_LOG_* 구현
= Git Authorship Confirmed
ITForbiddenArea의 IT_LOG_ROLE Callsite
= Confirmed반면 다음은 아직 별도 Runtime Evidence가 필요합니다.
Text
Multi PIE에서
Server / Client Prefix 실제 출력
Authority / AutonomousProxy /
SimulatedProxy 실제 로그 비교
IT_LOG_RPC 실제 Runtime Output따라서:
Text
Macro Definition
!=
Runtime Use
Callsite
!=
Runtime Output를 계속 구분합니다.
정리
ImitationTrigger에서 ITLogChannel을 만든 이유는 많은 로그를 남기기 위해서가 아니었습니다.
같은 함수가 여러 Network Instance에서 실행되는 환경에서, 한 줄만 보고도 실행 문맥을 빠르게 구분하기 위해서였습니다.
Text
NetMode
Role
Proxy
Connection
RPC Type
Function중 필요한 정보를 같은 형식으로 붙여:
Text
누가 실행했는가?
어디에서 실행했는가?
어떤 Role인가?
어떤 함수인가?를 빠르게 읽을 수 있도록 구성했습니다.
이 Utility는 Gameplay Core System도, 전체 Networking Framework도 아닙니다.
대신 멀티플레이 Runtime에서 함수 호출 여부뿐 아니라 실행 주체와 Role까지 함께 관찰해야 한다는 경험을 작은 Debug Infrastructure로 구현한 작업입니다.
다음 글에서는 지금까지 추적한 ADS 구조를 실제 Runtime Claim으로 올리려면 무엇을 확인해야 하는지, 그리고 문제가 생겼을 때 Last Good Boundary / First Bad Boundary를 어떻게 찾을지 정리합니다.