득이공간

[네트웍 멀티플레이 프레임웍의 이해] 1장. 언리얼 네트웍 멀티플레이어 프레임웍 기초 본문

GP/UE5

[네트웍 멀티플레이 프레임웍의 이해] 1장. 언리얼 네트웍 멀티플레이어 프레임웍 기초

쟁득 2024. 4. 17. 17:14
해당 게시물은 이득우 교수님의 '네트웍 멀티플레이 프레임웍의 이해' 강의를 수강하며
학습한 내용을 개인적으로 정리한 글입니다.

📌 목차 - 1장. 언리얼 네트웍 멀티플레이어 프레임웍 기초

1-1. 언리얼 네트웍 멀티플레이어 프레임웍 개요

1-2. 게임 모드와 로그인

1-3. 커넥션과 오너십

1-4. 액터의 역할과 커넥션 핸드셰이킹


📌 1-1. 언리얼 네트웍 멀티플레이어 프레임웍 개요

1. 향후 실습할 예제 프로젝트의 기본 설정
2. 클라이언트-서버 모델의 이해
3. 언리얼 네트웍 멀티플레이어 프레임웍을 구성하는 주요 개념의 이해
4. 앞으로 보강해야 할 기능의 확인

 

  • 언리얼 네트웍 멀티프레이어 프레임웍의 구성 요소
    • 어플리케이션: 게임 인스턴스, 월드, 네트웍 모드
    • 접속 플로우: 로그인, 커넥션, 오너십, 역할
    • 액터 리플리케이션: 연관성, 우선권, 빈도
    • RPC: 신뢰성, 검증
    • 움직임 리플리케이션: 캐릭터, 물리
    • 프로퍼티 리플리케이션: 상태, 최적화
  • 언리얼 공식 문서
    • 클라이언트-서버 구조
      • 클라이언트는 서버에서 일어나는 일의 근사치를 시뮬레이션한다.
    • 주요 과제
      • 모든 플레이어에게 일관된 경험을 제공
      • 네트워크 대역폭을 최대한 적게 사용
  • 싱글플레이어 게임의 구성
    • 스탠드 얼론: 게임 인스턴스( 게임 콘텐츠[ 월드 ] )
  • 멀티플레이어 게임의 구성 (클라이언트-서버 모델)
    • 게임 서비스: 서버 ↔ 클라이언트1, 클라이언트2, ..
    • 클라이언트
      • 독립적인 게임 인스턴스
      • 서버로부터 복제된 허상 (Proxy)
      • 접속 주체
  • C++ 프로그래밍으로 가능한 기능
    • 필수적으로 사용되는 가상 함수의 활용 가능
    • 제대로 된 리플리케이션 콜백의 호출 가능
    • 리플리케이션 조건의 세밀한 제어 가능
    • 네트워크 전송 최적화: 가벼운 데이터 설계

📌 1-2. 게임 모드와 로그인

1. 게임 모드와 플레이어 컨트롤러를 활용한 로그인 플로우의 이해
2. 네트워크 모드의 확인
3. 네트웍 멀티플레이어용 로그 매크로 제작
4. 게임 플레이 시작 과정의 이해

 

  • 이번 강의에서 학습할 내용
    • 어플리케이션: 네트웍 모드
    • 접속 플로우: 로그인
  • 언리얼 공식 문서
    • 네트웍 모드
      • Standalone: 커넥션 x
      • Client: 서버에 연결된 클라이언트로 실행
      • Listen Server: 원격 클라이언트의 연결을 수락, 로컬 플레이어를 서버에 직접 배치
      • Dedicated Server: 원격 클라이언트의 연결을 허용, 로컬 플레이어가 없음
  • 게임 모드 기반의 로그인 플로우
    1. 서버의 초기화
      • 게임 서비스 준비( 스탠드얼론( 게임모드 ) )
    2. 플레이어의 로그인
      • 게임 서비스 준비( 스탠드얼론( 게임모드, 플레이어 컨트롤러 0 ) )
    3. 리슨 서버의 시작
      • 게임 서비스 시작( 리슨서버( 게임 모드, 플레이어 컨트롤러 0 ) )
    4. 클라이언트의 접속 시도
      • 게임 서비스 시작( 클라이언트( ) -접속요청→ 리슨서버( 게임 모드, 플레이어 컨트롤러 0 ) )
    5. 클라이언트의 접속 허용
      • 게임 서비스 시작( 클라이언트( ) 리슨서버( 게임 모드, 플레이어 컨트롤러 0, 플레이어 컨트롤러 1 ) )
    6. 클라이언트의 초기화
      • 게임 서비스 시작( 클라이언트( ‘플레이어 컨트롤러 0’ ) ←복제- 리슨서버( 게임 모드, 플레이어 컨트롤러 0, ‘플레이어 컨트롤러 1’ ) )
  • 네트웍 멀티플레이어용 로그 매크로 제작
    • ArenaBattle.h
    #define LOG_CALLINFO ANSI_TO_TCHAR(__FUNCTION__)
    #define AB_LOG(LogCat, Verbosity, Format, ...) \\
    UE_LOG(LogCat, Verbosity, TEXT("%s %s"), LOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__))
    
    DECLARE_LOG_CATEGORY_EXTERN(LogABNetwork, Log, All);
    
    • ArenaBattle.cpp
    DEFINE_LOG_CATEGORY(LogABNetwork);
    
  • 게임 모드의 주요 함수
    • PreLogin()
      • 클라이언트의 접속 요청을 처리
    • Login()
      • 접속을 허용한 클라이언트에 대응하는 플레이어 컨트롤러 생성
    • PostLogin()
      • 플레이어 입장을 위해 플레이어에 필요한 기본 설정을 모두 마무리
    • StartPlay()
      • 게임의 시작을 지시
    • BeginPlay()
      • 게임 모드의 StartPlay()를 통해 게임이 시작될 때 모든 액터에서 호출
  • 게임의 시작
    • 클라이언트에는 게임 모드가 없다.
    • 게임 모드는 게임 스테이트에게 게임을 시작(BeginPlay()를 호출)하도록 지시한다.
    • 게임 스테이트는 모든 게임 오브젝트의 BeginPlay()를 호출한다.

📌 1-3. 커넥션과 오너십

1. 네트웍 멀티플레이어 게임에서 진행되는 초기화 과정의 학습
2. 언리얼 엔진에서 네트웍 데이터가 로우레벨에서 전달되는 과정의 이해
3. 커넥션을 담당하는 액터의 오너십의 필요성과 이를 설정하는 방법의 이해

 

  • 이번 강의에서 학습할 내용
    • 접속 플로우: 커넥션, 오너십
  • 액터의 준비와 게임의 시작
  • 네트웍 통신을 담당하는 주요 클래스
    • PlayerController: 네트웍 통신에 접근 가능한 게임 내 대표 액터
    • UNetConnection: 주고 받는 패킷 데이터의 인코딩 디코딩, 네트웍 통신량 조절, 채널 관리
    • UNetDriver: 로우레벨에서의 소켓 관리와 패킷 처리, 네트웍 통신 설정
  • 서버의 네트워크 초기화 과정
    • 현재 월드에 넷드라이버가 있으면 클라이언트-서버, 아니면 스탠드얼론으로 판단한다.
    • 서버는 월드의 Listen 함수를 호출해 넷드라이버를 생성함으로 네트웍 기능을 시작한다.
    • UWorld::InternalGetNetMode()
    • UWorld::Listen()
  • 넷드라이버의 커넥션 관리
    • 넷드라이버는 다수의 커넥션을 관리하고 있으며, 서버와 클라이언트에 따라 다르게 동작한다.
    • 클라이언트에서 넷드라이버는 항상 하나의 서버 커넥션을 가진다.
    • 서버에서 넷드라이버는 다수의 클라이언트 커넥션을 가진다.
    • UNetDriver::IsServer()
  • 언리얼 엔진에서의 데이터 관리
    • 네트웍에서 주고 받는 데이터들은 다음과 같은 고도화 작업을 거친다.
      1. 커넥션(Connection): 모든 데이터를 전달하는 통로
      2. 패킷(Packet): 네트웍을 통해 전달되는 단위 데이터. 숫자 혹은 문자로 구성.
      3. 채널(Channel): 언리얼 엔진 아키텍처에 따라 구분된 데이터를 전달하는 논리적인 통로
      4. 번치(Bunch): 언리얼 엔진의 아키텍처에 사용되는 데이터. 하나의 명령에 대응하는 데이터 묶음
    • 데이터 통신을 관리하기 위한 대표 액터로 플레이어 컨트롤러가 주로 사용된다.
    • 커넥션을 담당하는 대표 액터는 커넥션에 대한 오너십을 가진다고 표현한다.
  • 언리얼 공식 문서 - 액터와 그 접속 공유
    • 접속 소유권의 중요성
      • RPC는 어느 클라이언트에서 클라이언트실행 RPC를 실행할지 결정해야 한다.
      • 액터 리플리케이션 및 접속 연관성 때문
      • 오너가 연관된 경우의 액터 프로퍼티 리플리케이션 조건 때문
  • 액터와 플레이어 컨트롤러의 넷커넥션
    • 어떤 액터가 통신을 하기 위해서는 자신을 소유한 액터가 커넥션을 소유하고 있어야 한다.
    • 일반적으로 플레이어 컨트롤러는 넷커넥션을 소유하고 있다.
    • 넷커넥션 역시 플레이어 컨트롤러를 소유하고 있다.
    • AActor::GetNetConnection()
    • APlayerController::GetNetConnection()
  • 플레이어 컨트롤러를 통한 설정 예시
    • 플레이어 컨트롤러가 캐릭터에 빙의하면 캐릭터의 오너로 설정된다.
      • 서버: 빙의(Possess) 함수를 호출해 오너십을 설정한다.
      • 클라이언트: 오너십이 설정된 캐릭터의 속성이 배포되면서 자신이 조종하는 캐릭터에 오너십이 설정된다.
    • 이 때 캐릭터가 무기 액터를 소유하면 무기 액터는 통신이 가능한 상태가 된다.
      • 플레이어 컨트롤러: 통신 가능
      • 플레이어 컨트롤러가 소유한 액터: 통신 가능
      • 플레이어 컨트롤러가 소유한 액터가 소유한 무기 액터: 통신 가능

📌 1-4. 액터의 역할과 커넥션 핸드셰이킹

1. 게임 로직을 구현하기 위해 알아야 하는 액터의 역할
2. 리슨 서버에서 액터 역할을 구분하기 위한 API와 이의 활용 방법
3. 클라이언트와 서버간의 접속이 맺어지고 게임이 준비되는 과정의 심층적인 이해

 

  • 이번 강의에서 학습할 내용
    • 접속 플로우: 커넥션, 역할
  • 서버와 클라이언트에 위치한 액터의 역할
    • 클라이언트-서버 모델에서는 항상 서버에 있는 액터만이 신뢰된다.
    • 이를 Authority를 가진다고 표현한다.
    • 클라이언트의 액터는 대부분 서버 액터를 복제한 허상에 불과하다.
    • 이러한 액터를 Proxy라고 표현한다.
  • 로컬 역할과 리모트 역할
    • 리슨서버의 경우 플레이어로서 게임에도 참여하므로, 어플리케이션의 게임 로직을 사용한다.
    • 어플리케이션의 게임 로직은 서버 액터에 대해서만 게임에 관련된 작업을 수행해야 한다.
    • 이를 구분하기 위해 현재 동작하는 어플리케이션에서의 역할을 로컬 역할(Local Role), 커넥션으로 연결된 어플리케이션에서의 역할을 리모트 역할(Remote Role)이라고 한다. = 상대적인 구조
  • 액터 역할의 종류
    • None: 액터가 존재하지 않음
    • Authority: 서비스를 대표하는 신뢰할 수 있는 역할. 게임 로직을 수행한다.
    • AuthonomousProxy: Authority를 가진 오브젝트의 복제품. 일부 게임 로직을 수행한다.
    • SumulatedProxy: Authority를 가진 오브젝트의 복제품. 게임 로직을 전혀 수행하지 않는다.
  • Autonomous Proxy와 Simulated Proxy
    • 클라이언트의 Proxy는 크게 Autonomous와 Simulated로 구분된다.
    • Autonomous는 클라이언트의 입력 정보를 서버에 보내는 능동적인 역할을 일부 수행한다.
    • Simulated는 일방적으로 서버로부터 데이터를 수신하고 이를 반영한다.
    • Autonomous 역할을 하는 액터로는 플레이어 컨트롤러와 폰이 있다.
  • 액터의 역할을 파악하는 API
    • Authority를 가진 액터만 게임 로직을 수행할 수 있다.
      • AActor::HasAuthority
    • Autonomous Proxy는 예외적으로 입력에 관련된 로직을 수행할 수 있다.
    • 따라서 입력에 관련된 게임 로직의 실행은 Authority와 Autonomous Proxy에서만 허용된다.
      • AController::IsLocalController
      • APawn::IsLocallyControlled
  • 넷모드에 따른 오브젝트 배치
    • 서버에만 존재하는 액터: 게임 모드
    • 서버와 모든 클라이언트에 존재하는 액터: 배경 액터와 폰
    • 서버와 소유하는 클라이언트에만 존재하는 액터: 플레이어 컨트롤러
    • 클라이언트에만 존재하는 오브젝트: 애니메이션 블루프린트 및 HUD
  • 오브젝트 배치에 따른 API의 사용
    • 게임 모드는 HasAuthority 함수를 호출할 필요가 없다.
    • 폰은 Autonomous와 Simulated가 혼재되어 있다. (API를 사용해 로직을 구분)
    • 애니메이션 재생이나 UI 관련 로직은 클라이언트에서만 사용한다. (서버는 변경된 속성을 전달하고, 변경된 속성에 따라 애니메이션과 UI를 바꾸도록 설계)
  • 커넥션 핸드셰이킹(Connection Handshaking)
    • 네트웍으로 접속하는 두 컴퓨터가 잘 연결되었는지 확인하는 과정
    • 언리얼 네트웍 멀티플레이 접속을 위한 핸드셰이킹 과정
  • 게임의 준비
    • 커넥션을 허용하면 게임을 시작할 수 있도록 클라이언트와 서버는 준비 과정을 거친다.
    • 클라이언트: 맵의 로딩
    • 서버: 클라이언트를 대표하는 플레이어 컨트롤러의 생성
  • 커넥션 핸드셰이킹의 확인
    • 관련 소스코드 1: DataChannel.h
    • 관련 소스코드 2: UWorld::NotifyControlMessage (서버)
    • 관련 소스코드 3: UPendingNetGame::NotifyControlMessage (클라이언트)
    • 패킷의 처리 콜스택 예시
  • 알아두면 좋은 언리얼 네트웍 시스템 구성
    • 관련 소스코드: NetDriver.h
    • 용도에 따라 패킷을 처리하는 다양한 NetDriver 클래스를 제공한다.
      • GameNetDriver: 게임 데이터를 처리하는데 사용하는 네트웍 드라이버
      • DemoNetDriver: 게임 리플레이 데이터를 처리하는데 사용하는 네트웍 드라이버
      • BeaconNetDriver: 게임 외 데이터를 처리하는데 사용하는 네트웍 드라이버
    • 언리얼 엔진은 게임 데이터를 처리하는 게임넷드라이버로 IpNetDriver를 사용한다.
    • 초기 접속에 관련된 데이터 패킷을 ControlChannel을 통해 분석된다.
    • 언리얼 엔진에서 번치(Bunch)를 처리하는데 사용하는 주요 채널
      • ControlChannel: 클라이언트 서버 간의 커넥션을 다룰 때 사용하는 채널
      • ActorChannel: 액터 리플리케이션 작업을 다룰 때 사용하는 채널
      • VoiceChannel: 음성 데이터를 전달할 때 사용
        •