1. 언리얼 네트웍 멀티플레이어 프레임웍 개요
언리얼 엔진은 무조건 서버에서 중앙집권적으로 처리한 뒤 클라이언트에게 내려주는 구조다. 클라이언트 간 통신은 없음.

멀티플레이어를 위한 조기 계획
언제든 프로젝트에 멀티플레이어 기능이 필요하게 될 수 있다면, 프로젝트 시작부터 멀티플레이어를 염두에 두고 모든 게임플레이를 빌드해야 합니다. 팀에서 멀티플레이어 게임을 만들기 위한 추가 단계를 일관되게 구현한다면, 싱글 플레이어 게임에 비해 게임플레이 구축에 그렇게 많은 시간이 걸리지는 않을 것입니다. 장기적으로 팀이 프로젝트를 디버그하고 서비스하는 것이 전체적으로 더 쉬워질 것입니다. 한편, 언리얼 엔진에서 멀티플레이어용으로 프로그래밍한 모든 게임플레이는 싱글 플레이어에서도 작동합니다.
하지만 네트워킹 없이 이미 구축한 코드베이스를 리팩터링하려면, 전체 프로젝트를 샅샅이 뒤져가며 거의 모든 게임플레이 함수를 다시 프로그래밍해야 합니다. 팀원들은 그 시점까지 당연하게 받아들이던 프로그래밍 모범 사례를 다시 배워야 합니다. 또한, 네트워크 속도와 안정성으로 인해 발생하는 기술적 병목 현상에도 대비할 수 없습니다.
프로젝트 후반에 네트워킹을 도입하는 것은 처음부터 계획하는 것보다 리소스가 많이 들고 번거로운 일입니다. 따라서, 멀티플레이어 기능이 절대 필요 없는 프로젝트라는 확신이 없는 한, 항상 멀티플레이어용으로 프로그램하는 것이 좋습니다.
네트워크 모드 및 서버 타입
네트워크 모드(Network mode) 는 네트워크 멀티플레이어 세션과 컴퓨터의 관계를 설명합니다. 게임 인스턴스는 다음 네트워크 모드 중 하나를 취할 수 있습니다:
| 네트워크 모드 | 설명 |
|---|---|
| 독립형(Standalone) | 게임이 원격 클라이언트 접속을 허용하지 않는 서버로 실행됩니다. 게임에 참여하는 모든 플레이어는 로컬 플레이어뿐입니다. 이 모드는 싱글 플레이어 및 로컬 멀티플레이 게임에 사용됩니다. 서버 측 로직과 클라이언트 측 로직 모두를 로컬 플레이어에 맞게 실행합니다. |
| 클라이언트(Client) | 게임이 네트워크 멀티플레이어 세션으로 서버에 접속된 클라이언트로 실행됩니다. 어떠한 서버 측 로직도 실행하지 않습니다. |
| 리슨 서버(Listen Server) | 게임이 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행됩니다. 원격 클라이언트의 접속을 허용하며, 서버에 바로 로컬 플레이어가 있습니다. 이 모드는 가벼운 협동 및 경쟁 멀티플레이어에 자주 사용됩니다. |
| 데디케이티드 서버(Dedicated Server) | 게임이 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행됩니다. 원격 클라이언트의 접속을 허용하지만, 로컬 플레이어는 없습니다. 따라서, 더 효율적인 운영을 위해 그래픽, 사운드, 입력 및 기타 플레이어 중심 기능을 버립니다. 이 모드는 높은 지속성이나 보안, 대규모 멀티플레이어가 필요한 게임에 자주 사용됩니다. |
액터 리플리케이션
리플리케이션은 네트워크 세션에 있는 다양한 컴퓨터 간에 게임 스테이트 정보를 재현하는 프로세스입니다. 리플리케이션이 올바로 설정되면, 다른 컴퓨터의 게임 인스턴스가 동기화됩니다. 기본적으로 대부분의 액터는 리플리케이션이 활성화되어 있지 않으며, 로컬에서 모든 함수를 수행합니다. C++ 액터 클래스에서 bReplicates 변수를 설정하거나 액터 블루프린트의 리플리케이트(Replicates) 세팅을 true 로 설정하여 특정 클래스의 액터에 대한 리플리케이션을 활성화할 수 있습니다.
다음은 네트워크 게임플레이 제작에 가장 많이 사용하는 리플리케이션 기능입니다:
| 리플리케이션 기능 | 설명 |
|---|---|
| 생성 및 소멸(Creation and Destruction) | 서버에서 오소리티 있는 버전의 액터가 스폰되면 접속된 모든 클라이언트에 자동으로 원격 프록시가 생성됩니다. 그런 다음, 정보가 그러한 원격 프록시에 리플리케이트됩니다. 오소리티 있는 액터를 소멸시키면 접속된 모든 클라이언트에 있는 원격 프록시가 자동으로 소멸됩니다. |
| 무브먼트 리플리케이션(Movement Replication) | 오소리티 있는 액터에 대해 무브먼트 리플리케이트(Replicate Movement) 가 활성화되어 있거나 C++에서 bReplicateMovement 가 true 로 설정되어 있으면, 자동으로 위치(Location)와 회전(Rotation), 속도(Velocity)가 리플리케이트됩니다. |
| 변수 리플리케이션(Variable Replication) | 리플리케이티드 변수로 지정된 변수는 값이 변경될 때마다 오소리티 있는 액터에서 원격 프록시로 자동으로 리플리케이트됩니다. |
| 컴포넌트 리플리케이션(Component Replication) | 액터 컴포넌트가 액터 컴포넌트를 소유한 액터의 일부로 리플리케이트됩니다. 리플리케이트되는 것으로 지정된 컴포넌트 내 모든 변수가 리플리케이트되며, 컴포넌트 내에서 호출된 모든 RPC가 액터 클래스에서 호출된 RPC와 일관되게 행동합니다. |
| 원격 프로시저 호출(RPC) | RPC는 네트워크 게임에서 특정 컴퓨터로 전송되는 특별 함수입니다. 처음 RPC를 호출한 컴퓨터가 무엇이든 지정된 컴퓨터에서만 RPC 구현이 실행됩니다. 서버(서버에서만 실행)나 클라이언트(액터 소유 클라이언트에서만 실행) 또는 NetMulticast(서버를 포함, 세션에 접속된 모든 컴퓨터에서 실행)로 지정할 수 있습니다. |
생성 및 소멸, 무브먼트 같은 일반적인 사용 사례는 자동으로 처리되지만, 리플리케이션을 활성화해도 다른 모든 게임플레이 피처는 기본적으로 자동으로 리플리케이트되지 않습니다. 게임에 적합하도록 리플리케이트하려는 변수와 함수를 정확하게 지정해야 합니다. 위의 모든 리플리케이션 기능에 대한 자세한 내용은 프로퍼티 리플리케이션 가이드에서 확인할 수 있습니다.
액터와 폰, 캐릭터의 몇 가지 일반적인 피처는 리플리케이트되지 않습니다.
- 스켈레탈 메시(Skeletal Mesh) 및 스태틱 메시(Static Mesh) 컴포넌트
- 머티리얼(Materials)
- 애니메이션 블루프린트(Animation Blueprints)
- 파티클 시스템(Particle Systems)
- 사운드 이미터(Sound Emitters)
- 피직스 오브젝트(Physics Objects)
이러한 각 요소는 모든 클라이언트에서 개별적으로 실행됩니다. 하지만, 이러한 시각적 요소를 구동하는 변수가 리플리케이트되면, 모든 클라이언트가 같은 정보를 보유하게 되어 거의 같은 방식으로 해당 요소가 시뮬레이션됩니다.
2. 게임모드와 로그인

GameState의 중요성: 클라이언트가 조종하는 컨트롤러는 사실 서버에 진짜가 생성되고 클라이언트 로컬의 컨트롤러는 가짜임. 게임모드는 서버에만 존재하고 클라이언트는 게임모드가 없음. 그래서 클라이언트는 AABPlayerController라는 클래스를 모름(?), 근데 어떻게 CLIENT1에서 BeginPlay를 할 수 있느냐? GameState가 값을 전달해줬기 때문. GameState는 통로임

GameMode::StartPlay()
↓
void AGameStateBase::HandleBeginPlay() { bReplicatedHasBegunPlay = true; GetWorldSettings()->NotifyBeginPlay(); GetWorldSettings()->NotifyMatchStarted(); }↓
UPROPERTY(Transient, ReplicatedUsing = OnRep_ReplicatedHasBegunPlay) bool bReplicatedHasBegunPlay;↓
void AGameStateBase::OnRep_ReplicatedHasBegunPlay() // 클라이언트용{ if (bReplicatedHasBegunPlay && GetLocalRole() != ROLE_Authority) { GetWorldSettings()->NotifyBeginPlay(); GetWorldSettings()->NotifyMatchStarted(); }}void AGameStateBase::HandleBeginPlay() // 서버용{ bReplicatedHasBegunPlay = true; GetWorldSettings()->NotifyBeginPlay(); GetWorldSettings()->NotifyMatchStarted(); }3. 커넥션과 오너십
- PreLogin에서 의도적으로 접속을 제약시키기 PreLogin의 ErrorMessage를 채워넣으면 오류로 간주되어 접속은 차단됨. 하지만 STANDALONE 형태로 게임이 실행은 됨
- PlayerController에는 PostInitializeComponents 뿐만 아니라 PostNetInit도 있음. 네트워크 연결 후에 초기화할 것이 있으면 이 조기함수에 설정할 것
- 의도적으로 게임 시작하지 않기(StartPlay 주석처리)
네트웍 통신을 담당하는 주요 클래스:
- PlayerController 클래스: 네트웍 통신에 접근가능한 게임 내 대표 액터. 하는 일이 많다
- UNetConnection 클래스: 주고받는 패킷 데이터의 인코딩/디코딩, 네트웍 통신량 조절, 채널 관리 담당
- UNetDriver 클래스: 로우레벨에서의 소켓 관리와 패킷 처리, 네트웍 통신 설정

넷드라이버의 커넥션 관리
- 넷드라이버는 다수의 커넥션을 관리하고 있으며, 서버와 클라이언트에 따라 다르게 동작함.
- 클라이언트에서 넷드라이버는 항상 하나의 서버 커넥션을 가진다.
- 서버에서 넷드라이버는 다수의 클라이언트 커넥션을 가진다.
언리얼 엔진에서의 데이터 관리
- 네트웍에서 주고 받는 데이터들은 다음과 같은 고도화 작업을 거침.
- 커넥션(Connection) : 모든 데이터를 전달하는 네트웍 통로
- 패킷(Packet) : 네트웍을 통해 전달되는 단위 데이터. 숫자 혹은 문자로 구성.
- 채널(Channel) : 언리얼 엔진 아키텍쳐에 따라 구분된 데이터를 전달하는 논리적인 통로
- 번치(Bunch) : 언리얼 엔진의 아키텍쳐에 사용되는 데이터. 하나의 명령에 대응하는 데이터 묶음
- 데이터 통신을 관리하기 위한 대표 액터로 플레이어 컨트롤러가 주로 사용됨.
- 커넥션을 담당하는 대표 액터는 커넥션에 대한 오너십을 가진다고 표현한다.
