46. 데디케이티드 서버 지원

이 문서는 클라이언트 엔진에서 제공하는 데디케이티드 서버 기능 과 아이펀 엔진, 그리고 클라이언트 플러그인 기능을 함께 사용하여 데디케이티드 서버 기반 게임을 만드는 방법을 설명합니다.

아이펀 엔진에서는 언리얼 엔진(Unreal Engine) 4, 그리고 유니티3D(Unity3D) 의 데디케이티드 서버 기능을 지원합니다.

데디케이티드 서버 연동 샘플은 깃허브 저장소 에서 확인할 수 있습니다.

46.1. 용어 설명

데디케이티드 서버: 언리얼 또는 유니티 게임 엔진에서 헤드리스(Headless) 모드로 빌드한 데디케이티드 서버 바이너리를 의미합니다.

매치(게임 세션): 데디케이티드 서버에서 유저들이 진행하는 게임을 뜻합니다.

데디서버 매니저: 아이펀 엔진 내부에서 동작하는 콤포넌트로 여러 호스트들을 관리하고 데디케이티드 서버에서 실행하는 매치(게임 세션)들을 관리하는 콤포넌트입니다. DedicatedServerManager 이름을 가진 인터페이스를 통해 매치를 관리할 수 있습니다.

게임 서버: 데디서버 매니저 콤포넌트를 활성화한 아이펀 엔진 기반 서버를 뜻합니다.

데디서버 호스트 매니저: 호스트(물리 또는 가상 머신) 에서 데디케이티드 서버 프로세스를 관리하고 호스트에서 진행 중인 매치(게임 세션)들을 관리하는 프로그램입니다. 윈도우, 리눅스를 사용할 수 있으며 맥OS 개발 환경으로 사용할 수 있습니다.

스폰(Spawn): 새로운 데디케이티드 서버 생성 요청을 뜻합니다.

난입(SendUser): 전에 생성했던 데디케이티드 서버로 유저를 보내는 행위를 뜻합니다.

리다이렉션: 스폰 요청이 성공한 경우 데디서버 매니저 에서 각 클라이이언트로 데디케이티드 서버 로 접속하라는 메시지를 전송합니다. 이후 클라이언트는 데디서버 매니저로부터 받은 주소와 포트 번호를 통해 데디케이티드 서버 로 접속한 후 게임을 진행하게 되는데 이러한 서버 이동 과정을 리다이렉션 이라고 합니다.

46.2. 레디스 서버 및 API 서비스 활성화

데디케이티드 서버 관련 기능(데디서버 매니저 및 데디서버 호스트 매니저)을 사용하기 위해서는 레디스(Redis) 서버와 API 서비스 포트 설정이 필요합니다. MANIFEST.json 파일 안에 있는 Redis 항목을 다음과 같이 설정해야 합니다.

 "Redis": {
   // 레디스 사용을 활성화합니다.
   "enable_redis": true,
   "redis_mode": "redis",
   "redis_servers": {
     "": {
       // 레디스 서버 주소
       "address": "127.0.0.1:6379",
       // 데이터베이스 번호.
       "database": 0,
       // auth 비밀 번호
       "auth_pass": ""
     }
   },
   ...
}

API 서비스는 기본적으로 활성화되어 있지만 데디서버 호스트 매니저 에서 이 포트로 접근할수 있는지 확인해야 합니다. 이 포트는 데디서버 호스트 매니저가 보내는 매치(게임 세션) 관련 데이터 또는 커스텀 콜백(CustomCallback) 등을 받기 위해 사용합니다.

"ApiService": {
  "api_service_port": 8014,
  "api_service_event_tags_size": 1,
  "api_service_logging_level": 2
},

46.3. 데디서버 매니저 콤포넌트 활성화

MANIFEST.json 파일 안에 DedicatedServerManager 항목을 추가하면 게임 서버를 시작할 때 데디서버 매니저를 활성화합니다.

"DedicatedServerManager": {
  // 상세 로그를 출력합니다. 개발 환경에서는 활성화하는 게 좋습니다.
  "dedicated_server_verbose_log": true,
  // 데디서버 매니저가 데디케이티드 서버 프로세스 생성 후 매치 준비까지
  // 대기할 시간(단위: 초)입니다. 이 시간 안에 프로세스 생성 및 매치(게임 세션)
  // 준비가 끝나지 않으면 스폰 실패로 간주합니다.
  "dedicated_server_spawn_timeout": 30
}

데디케이티드 서버 스폰 방법은 데디서버 호스트 매니저 설치 후 다시 설명하겠습니다.

46.4. 데디케이티드 서버 동작 구조

아이펀 엔진 데디케이티드 서버 구조도

다음은 클라이언트가 데디케이티드 서버 스폰 요청 후 데디케이티드 서버로 접속하는 일련의 과정을 설명합니다.

  1. 클라이언트 에서 게임 서버로 데디케이티드 서버 스폰을 요청합니다.
  2. 게임 서버클라이언트 와 함께 보낼 유저들 및 관련 데이터들을 한 곳에 묶어 데디케이티드 서버를 스폰합니다. 이 과정에서 여러 유저들을 일련의 규칙에 따라 모을 때 콘텐츠 지원 Part 2: 매치메이킹 기능을 사용할 수 있습니다.
  3. 게임 서버 는 현재 사용할 수 있는 호스트가 있는 지 확인한 후 호스트 위에서 실행 중인 데디케이티드 서버 호스트데디케이티드 서버 생성을 요청합니다.
  4. 데디케이티드 서버 는 스폰 요청 시 전달한 명령행 인자와 함께 초기화를 수행합니다. 이후 초기화 과정이 끝나면 {Post/Send}Ready() 함수를 호출해 게임 서버 로 준비 완료를 알립니다.
  5. 데디케이티드 서버 가 준비 완료를 알리면 게임 서버 는 스폰 요청 시 인자로 넣었던 유저들에게 리다이렉션 메시지를 전달합니다.
  6. 클라이언트리다이렉션 메시지를 받으면 서버 이동 처리를 진행합니다.
  7. 클라이언트 및 다른 유저들이 함께 게임을 진행합니다. 이 때 데디케이티드 서버 는 게임 도중 또는 게임이 끝난 후 게임 서버 로 게임 데이터를 전달할 수 있습니다. 데이터를 전달하는 방법은 아래에서 다시 설명하겠습니다.
  8. 게임 서버데디케이티드 서버 로부터 받은 게임 데이터를 받습니다. 개발자는 이를 적절히 가공하여 처리하거나 데이터베이스에 넣을 수 있습니다.

46.5. 데디서버 호스트 매니저 설치 및 시작

데디서버 호스트 매니저는 리눅스, 윈도우, 맥OS 운영체제에서 동작합니다.

46.5.1. 리눅스 데디서버 호스트 매니저 설치

우분투 리눅스 환경

$ sudo apt-get install -y funapi1-dedicated-server-host

CentOS 환경

$ sudo yum install -y funapi1-dedicated-server-host

46.5.2. 윈도우 데디서버 호스트 매니저 설치

윈도우 10 (x64), 64bit 파이썬 2.7.14 버전을 기준으로 동작을 확인했습니다.

  1. 윈도우용 파이썬 2.7.14 버전(또는 2.7.x 최신 버전)을 다운로드 한 후 설치합니다.
  2. 파이썬을 기본 경로(c:\Python27) 에 설치한 경우, 명령행(윈도우 + R, cmd) 창을 연 후, 다음과 명령으로 필수 패키지들을 설치합니다.
C:\> C:\Python2.7\Script\pip.exe install flask gevent netifaces ^
    python-gflags requests redis six

Note

^ 문자는 MS-DOS 배치파일의 줄 이어짐 문자입니다. 한 줄로 입력하시면 됩니다.

  1. 데디서버 호스트 매니저를 다운로드 한 후 원하는 위치에 설치합니다.

46.5.3. 데디서버 호스트 매니저 설정하기

데디서버 호스트 매니저에서 사용할 수 있는 전체 플래그 및 설명은 funapi-dedicated-server-host.flag 파일 안에 있습니다. 경로는 다음과 같습니다. 리눅스 운영체제는 /etc/funapi-dedicated-server-host 경로를 사용하고, 윈도우는 다운로드 받은 데디서버 호스트 매니저 디렉토리 안에 있습니다.

데디서버 호스트 매니저에서 반드시 설정해야 하는 것들은 다음과 같습니다.

  • 레디스 서버 주소. 데디서버 매니저가 사용하는 주소와 같은 주소를 사용해야 합니다.
  • 데디케이티드 서버 엔진 타입 (ue4 혹은 unity)
  • 클라이언트가 데디케이티드 서버로 접속할 때 사용할 IP 주소 또는 네트워크 인터페이스
  • 데디서버 매니저의 스폰 요청을 받을 IP 주소 또는 네트워크 인터페이스
  • 데디케이티드 서버 실행 파일 위치
  • 개발 환경에서는 --verbose 플래그를 True 로 사용하는 게 좋습니다.

다음은 언리얼 엔진 4 기반 데디케이티드 서버 예제입니다. 만약 유니티 엔진 기반 데디케이티드 서버를 사용할 경우 --engine_type=unity 로 값을 변경해야 합니다.

46.5.3.1. 리눅스 환경 설정 파일 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# '#' 으로 시작하는 줄은 주석으로 처리합니다.
# 데디케이티드 서버 바이너리 경로를 이 곳에 지정하면 됩니다.
--binary_path=/var/lib/your/dedicated-server/binary

# 데디케이티드 서버 엔진 타입을 지정합니다. 'ue4' (언리얼 언젠4) 또는
# 'unity' (유니티3D) 를 사용할 수 있습니다.
--engine_type=ue4

# Redis 호스트 주소 및 호트 번호를 지정합니다.
--redis_host=127.0.0.1
--redis_port=6379

...

# 상세 로그를 출력합니다. (기본 값=False)
--verbose=True

...

# 특정 NIC, IP(또는 URL)를 바인딩할 때는 이 플래그를 사용해야 합니다.
# 내부 통신에 사용할 NIC 와 클라이언트 통신에 사용할 NIC 를 분리할 때 유용합니다.
# 예) eno1 인터페이스로 게임 클라이언트와 통신하고, eno2 를 내부 통신에 사용할 수
# 있습니다. 이 값은 리눅스 기반 운영체제에 따라 eth0, ens1, enp2s0 등으로 바뀔
# 수 있습니다.
# 만약 'game_ip' 값에 IP 또는 URL 주소를 설정하면 'game_interface' 값은
# 무시됩니다.
#--game_ip=10.0.0.7
--game_interface=eth0
--restful_interface=eth1

# 윈도우 운영체제를 사용할 때는 'game_interface' 와 'restful_interface' 를
# 사용할 수 없으므로 반드시 'game_ip' 와 'restful_ip' 플래그를 사용해야 합니다.
#--restful_ip=10.10.1.7

이 예제 파일에서는 /var/lib/your/dedicated-server/binary 를 데디케이티드 서버 바이너리 경로로 설정하고 레디스 서버 주소를 127.0.0.1:6379 로 지정했습니다.

데디케이티드 서버는 eth0 인터페이스에 지정한 IP로 통신하고, 데디서버 매니저와는 eth1 인터페이스에 지정한 IP로 로 통신합니다.

만약 인터페이스가 아닌 특정 URL 이나 IP 로 통신하는 경우 --game_interface=<NIC> 대신 --game_ip=<URL/IP> 를 설정할 수 있습니다.

46.5.3.2. 리눅스 환경에서의 언리얼 엔진 4 권한 설정하기

Important

리눅스 운영체제에서 언리얼 엔진 4 로 개발한 데디케이티드 서버를 실행하기 위해서는 데디서버 호스트 매니저를 root 가 아닌 일반 사용자 권한으로 실행해야 합니다. 그렇지 않을 경우, 데디케이티드 서버 실행에 실패합니다.

데디케이티드 서버 매니저 설치로 생성된 서비스 파일을 복사하여 유저 권한의 서비스 목록에 추가합니다.

$ sudo cp /lib/systemd/system/funapi-dedicated-server-host.service \
/usr/lib/systemd/user/funapi-dedicated-server-host.service

Note

\ 문자는 bash 의 줄 이어짐 문자입니다. 한 줄로 입력하시면 됩니다.

그리고 복사된 서비스 파일을 열어 14~15 line 의 User=root, Group=root 를 제거합니다.

# NOTE: It's strongly recommended that one should use uid:gid other than root

User=root   # remove
Group=root  # remove

Type=simple
Restart=on-failure
RestartSec=5s

46.5.3.3. 윈도우 환경 설정 예제

윈도우 운영체제에서는 인터페이스(*_interface) 인자를 사용할 수 없으며 game_iprestful_ip 인자만 사용할 수 있습니다.

  • game_ip: 클라이언트가 데디케이티드 서버에 접속할 때 사용할 주소입니다. 따라서 외부에서 접근할 수 있는 IP 또는 URL 을 지정해야 합니다.
  • restful_ip: 데디서버 매니저가 데디서버 호스트 매니저와 통신할 때 사용하는 주소입니다. 내부 통신 전용 IP 가 있다면 해당 값을, 없다면 공인 IP 를 지정해야 합니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# '#' 으로 시작하는 줄은 주석으로 처리합니다.
# 데디케이티드 서버 바이너리 경로를 이 곳에 지정하면 됩니다.
--binary_path=D:\ShooterGame\ShooterGame.exe

# 데디케이티드 서버 엔진 타입을 지정합니다. 'ue4' (언리얼 언젠4) 또는
# 'unity' (유니티3D) 를 사용할 수 있습니다.
--engine_type=ue4

...

# 상세 로그를 출력합니다. (기본 값=False)
--verbose=True

...

# 특정 NIC, IP(또는 URL)를 바인딩할 때는 이 플래그를 사용해야 합니다.
# 내부 통신에 사용할 NIC 와 클라이언트 통신에 사용할 NIC 를 분리할 때 유용합니다.
# 예) eno1 인터페이스로 게임 클라이언트와 통신하고, eno2 를 내부 통신에 사용할 수
# 있습니다. 이 값은 리눅스 기반 운영체제에 따라 eth0, ens1, enp2s0 등으로 바뀔
# 수 있습니다.
# 만약 'game_ip' 값에 IP 또는 URL 주소를 설정하면 'game_interface' 값은
# 무시됩니다.
#--game_interface=eth0
#--restful_interface=eth1
--game_ip=10.0.0.7

# 윈도우 운영체제를 사용할 때는 'game_interface' 와 'restful_interface' 를
# 사용할 수 없으므로 반드시 'game_ip' 와 'restful_ip' 플래그를 사용해야 합니다.
# IP addresses used by game clients and game servers respectively.
--restful_ip=10.10.1.7

46.5.4. 데디케이티드 서버 추가 인자 지정

데디케이티드 서버 실행 시 추가 인자를 지정해야 하는 경우 --binary_path 값을 실행 파일 대신 스크립트(.sh 또는 .bat) 파일로 지정할 수 있습니다.

혹은 언리얼 엔진 에디터를 이용해서 패키징 없이 실행하게 설정할 수 있습니다. 예를 들면 다음과 같습니다(윈도우 운영체제에서 .bat 파일 이용).

REM example batch file, which utilizes UE4 editor to launch dedicated server

"C:\Program Files\Epic Games\4.14\Engine\Binaries\Win64\UE4Editor.exe" ^
  "C:\Work\apps-ue4-dedi-server-example\ShooterGame\ShooterGame.uproject" ^
  HighRise -skipcompile -server -log ^
  %*

UE4 에디터를 이용해서 데디케이티드 서버를 띄우며, 맵 이름인 HighRise 를 추가로 전달합니다.

맵 이름, 게임 모드 등 게임마다 달라질 수 있는 값을 전달할 때는 데디서버 매니저에서 스폰 요청(DedicatedServerManager::Spawn()) 시 추가 인자(dedicated_server_args) 를 지정할 수 있습니다.

static void Spawn(const fun::Uuid &match_id,
                  const fun::Json &match_data,
                  const std::vector<std::string> &dedicated_server_args,
                  const std::vector<std::string> &accounts,
                  const std::vector<fun::Json> &user_data,
                  const SendCallback &callback);

46.5.5. 데디서버 호스트 매니저 실행

Ubuntu 16.04 / CentOS 7 환경에 실행하기

우선 다음 명령어로 서비스를 활성화합니다.

# Unity

$ sudo systemctl daemon-reload
$ sudo systemctl enable funapi-dedicated-server-host

# Unreal Engine 4

$ systemctl --user daemon-reload
$ systemctl --user enable funapi-dedicated-server-host

그리고 서비스를 실행합니다.

# Unity

$ sudo systemctl start funapi-dedicated-server-host

# Unreal Engine 4

$ systemctl --user start funapi-dedicated-server-host

우분투 16.04 환경에서 실행하기

다음과 같이 서비스를 실행합니다.

$ sudo systemctl start funapi-dedicated-server-host

윈도우 환경에서 실행하기

funapi-dedicated-server-host.flag 에 있는 내용을 설정하신 후, funapi_dedicated_server 폴더가 보이는 디렉터리에서 아래 명령을 실행합니다.

여기서는 D:\DedicatedServerHost 디렉터리에 압축을 풀었다고 가정합니다.

D:\DedicatedServerHost> C:\Python2.7\python.exe -m funapi_dedicated_server ^
    --flagfile=funapi-dedicated-server-host.flag

Note

^ 문자는 MS-DOS 배치파일의 줄 이어짐 문자입니다. 한 줄로 입력해도 됩니다.

46.6. 매치(게임 세션) 시작하기

게임 서버에서는 다음과 같이 데디케이티드 서버를 스폰할 수 있습니다.

#include <funapi/service/dedicated_server_manager.h>

DedicatedServerManager::SendCallback spawn_callback =
    [](const fun::Uuid &match_id,
       const std::vector<std::string> &accounts,
       bool spawned) {
    //
    // 데디케이티드 서버 스폰 결과를 받는 콜백 핸들러입니다.
    //
    // 대기 중인 유후 서버가 있다면 게임 서버는 이 핸들러를 거의 즉시 호출합니다.
    // 대기 중인 서버가 없다면 최대 FLAGS_dedicated_server_spawn_timeout
    // 초 만큼 기다린 후 이 핸들러를 호출할 수 있습니다 (spawned=false).
    //
    // 게임 서버는 이 콜백 함수가 끝나는 즉시 클라이언트로 데디케이티드 서버 접속
    // 메시지를 보냅니다. 즉, 게임 서버는 이 콜백 함수를 호출하는 시점에서
    // 클라이언트가 데디케이티드 서버로 연결하기 전이라는 것을 보장합니다.
    //
    // 다음은 데디케이티드 서버 스폰 실패 시 확인해야 할 내용들입니다.
    //
    // - 데디서버 호스트 매니저가 실행 중인지 확인해주세요.
    // - 레디스에 ife-dedi-hosts 키가 있는지, 키에 호스트가 있는지 확인해주세요.
    // - AWS 를 사용할 경우 FLAGS_dedicated_server_spawn_timeout 이 충분히
    //   길어야 합니다. 그렇지 않으면 새 서버를 추가할 동안 타임 아웃이 발생할
    //   수 있습니다.
    // - 데디케이티드 서버 실행 시 인지를 제대로 지정했는지 확인해주세요.
    // - 유니티/언리얼 데디케이티드 서버는 정상적으로 실행했으나,
    //   데디서버 호스트 매니저로 초기화(Ready)를 보내지 못했거나,
    //   프로세스가 충돌했을 수 있습니다.
    //
  });

// 게임 데이터, 유저목록, 서버 실행 인자 (추가로 필요한 경우만) 를 넘겨서
// 데디케이티드 서버를 실행합니다.
DedicatedServerManager::Spawn(
    match_id,
    game_data,
    server_args,
    accounts,
    user_data,
    spawn_callback);

각 인자 설명은 다음과 같습니다.

  • fun::Uuid match_id: 매치(게임 세션)를 식별할 때 사용하는 ID 로 고유해야 합니다. 콘텐츠 지원 Part 2: 매치메이킹 기능을 사용할 때는 매치 성사 시 생성하는 ID 를 사용하면 됩니다. 데디서버 매니저 인터페이스(DedicatedServerManager) 에서 제공하는 모든 콜백은 이 매치 ID 를 태그로 하는 이벤트 위에서 실행합니다.
  • fun::Json match_data: 이 매치에서 사용할 데이터를 지정합니다. 데디케이티드 서버 실행 후 등록한 콜백 또는 함수를 통해 이 데이터를 가져올 수 있습니다. 이 곳에는 게임의 맵 정보, 규칙 등 게임 데디케이티드 서버 실행 후 적용할 전체적인 사항들을 넣으면 됩니다.
  • std::vector<std::string> server_args: 데디케이티드 서버 바이너리 실행 시 추가로 지정해야 할 인자를 설정합니다. 이 곳에는 데디케이티드 서버를 실행하기 전에 추가로 지정해야 할 게 있을 때 사용하면 됩니다(맵 변경 등). 예를 들면 맵을 변경하기 위해 map=<맵 정보>?opt1=1&opt2=2 값을 인자로 설정할 수 있습니다.
  • std::vector<std::string> accounts: AccountManager 로 로그인할 때 사용한 유저 ID 를 이곳에 넣습니다. 이 곳에 지정한 유저들은 이 함수로 생성한 데디케이티드 서버로 접속하게 됩니다.
  • std::vector<fun::Json> user_data: 각 유저 ID 가 사용할 데이터를 넣습니다. 유저 ID 인자 목록과 순서가 같아야 하고, accounts 와 같은 크기여야 합니다. 이 곳에는 개별 유저들의 장비, 아이템 등 유저별 데이터를 넣을 떄 사용하면 됩니다.
  • spawn_callback: 데디케이티드 서버 스폰 결과를 받는 콜백 함수입니다. 만약 성공적으로 데디케이티드 서버를 생성했다면 유저들이 서버로 접속하기 직전에 이 콜백 함수를 호출합니다. 즉, 게임 서버는 이 콜백 함수를 호출하는 시점에서 클라이언트가 데디케이티드 서버로 연결하기 전이라는 것을 보장합니다.

스폰 요청 이후 동작에 대해서는 데디케이티드 서버 동작 구조 부분을 참고해주세요.

46.6.1. 스폰한 데디케이티드 서버로 유저 보내기 (유저 난입)

유저 난입은 매치(게임 세션) 시작하기 에서 생성했던 데디케이티드 서버로 유저를 추가하는 기능입니다.

static void SendUsers(const fun::Uuid &match_id,
                      const fun::Json &match_data,
                      const std::vector<std::string> &accounts,
                      const std::vector<fun::Json> &user_data,
                      const SendCallback &callback);

함수 인자 설명은 DedicatedServerManager::Spawn 함수 인자 설명을 참고해주세요. 유저 난입은 server_args 를 제외한 나머지 항목들이 필요합니다.

46.7. 데디케이티드 서버 처리하기 (언리얼 엔진 4)

데디케이티드 서버에서 게임 서버와 통신하는데 필요한 부분은 언리얼 엔진 4 플러그인의 fun::FunapiDedicatedServer 클래스 멤버 함수로 제공합니다.

46.7.1. 데디케이티드 서버 시작

데디케이티드 서버 초기화를 시작합니다. 초기화 과정에는 버전 확인 및 프로세스 인자 확인 등을 포함합니다. 올바른 버전 형식(x.y.z.r)을 지정하지 않으면 초기화에 실패하며 데디케이티드 서버를 종료합니다.

bool Start(const FString &server_version);

fun::FunapiDedicatedServer::Start(FString("1.0.0.1"));

각 파라미터의 설명은 다음과 같습니다.

  • FString server_version: 데디서버 호스트 매니저에게 데디케이티드 서버 버전을 알려줍니다. 이 버전 정보는 데디서버 매니저 에서 데디서버 호스트 매니저 버전을 구분할 때 사용합니다.

Note

언리얼 엔진4 데디케이티드 서버 플러그인의 요청 응답 메세지 인터페이스 추가로 인해 1.19 버전 이후로 ParseConsoleCommand(), SetVersionInfo(), GetGameInfo(), Post(), PostHeartbeat() 의 공개 인터페이스가 제거되었습니다.

ParseConsoleCommand(), SetVersionInfo(), GetGameInfo()Start() 함수로 통합되었습니다.

Post() 함수는 SendXXX() 함수들로 대체되었습니다. PostHeartbeat() 는 외부로 공개되지 않고 플러그인 내부에서 처리됩니다.

46.7.2. 데디케이티드 서버가 준비되면 알려주기

데디케이티드 서버 초기화가 끝난 후 서버를 사용할 준비를 마치면 SendReady() 함수를 호출합니다. 이 함수를 호출하면 클라이언트가 접속을 시작할 수 있습니다.

fun::FunapiDedicatedServer::SendReady(const ResponseHandler &response_handler);

파라미터는 다음과 같은 의미입니다.

  • ResponseHandler response_handler 메시지 전송에 대한 결과 입니다. 전송 결과에 따라 재시도 또는 사용자 코드를 추가하여 전송 완료 후 동작을 추가할 수 있습니다.

Note

void PostReady() 함수는 void SendReady(const ResponseHandler &    response_handler) 와 동일한 기능을 하지만 콜백을 추가할 수 없어 메시지를 실제로 보냈는지 보장할 수 없습니다. 이와 같은 이유로 콜백이 없는 Post...() 함수들은 지원을 중단할 예정입니다.

46.7.3. 접속한 유저 인증처리

이 함수는 Start() 를 호출한 이후에만 유효합니다.

언리얼 엔진 4는 PreLogin 단계에서 유효한 유저인지 확인하기 위한 함수를 제공합니다. 즉, 클라이언트가 접속한 이후에 인증 처리를 위해 다음 함수를 호출합니다.

// 유저 인증 처리; 반환 값: 인증 성공 여부
bool AuthUser(const FString& uid,
              const FString& token,
              FString &error_message);

파라미터는 다음과 같은 의미입니다.

  • FString uid 유저의 uid 정보 입니다.
  • FString token 데디케이티드 서버 접속 권한을 가진 유저인지 확인할 때 사용하는 토큰입니다.
  • error_message 오류가 발생한 경우 오류 메시지 입니다.

예를 들어, 다음과 같이 호출하시면 됩니다.

PreLogin(Options, ...) {

  FString uid = UGameplayStatics::ParseOption(Options, "FunapiUID");
  FString token = UGameplayStatics::ParseOption(Options, "FunapiToken");

  if (!fun::FunapiDedicatedServer::AuthUser(uid, token, error_message)) {
    // 인증 실패한 경우 처리
  }

}

46.7.4. 게임 결과 보고하기

게임 서버에 게임 결과 메시지를 전송합니다.

void SendResult(const FString &json_string,  // 게임 결과에 대한 정보
                const ResponseHandler &response_handler);  // 요청 결과 콜백

예를 들어, 다음과 같이 호출해서 게임 결과를 보고합니다.

fun::FunapiDedicatedServer::SendResult(FString("{ \"foo\":\"bar\"}"),
  [this](fun::FunapiDedicatedServer::HttpResponse http_response) {
    // 데디케이티드 서버는 자동으로 종료되지 않으므로
    // 콜백 결과에 따라 종료해야 합니다.
    if (http_response.code == 0) {
      FGenericPlatformMisc::RequestExit(false);
    }
    else {
      // HttpResponse 응답 메세지 정보는 다음과 같습니다.
      // int code 에러 코드
      // FString desc 에러에 대한 설명.

      // 문서에서는 게임 결과 전송에 실패했을 때 재전송을 시도합니다.
      fun::FunapiDedicatedServer::SendResult(...);
    }
  });

여기서 전달한 게임 결과는 게임 결과 처리하기 절에서 설명하는 서버 코드에서 처리합니다.

46.7.5. 메치 혹은 유저 데이터 얻기

// 유저 데이터를 얻습니다.
FString GetUserDataJsonString(const FString &uid);

// 유저 데이터가 변경되었을 때 콜백을 받습니다.
void SetUserDataCallback(const UserDataHandler &user_data_handler);

// 매치 데이터를 얻습니다.
FString GetMatchDataJsonString();

// 매치 데이터가 변경되었을 때 콜백을 받습니다.
void SetMatchDataCallback(const MatchDataHandler &match_data_handler);

46.7.6. 유저 상태 보고하기

언리얼 엔진 4의 Login, Logout 단계에서 유저 상태를 보고하는 함수를 제공합니다. 이를 통해 게임 서버는 데디케이티드 서버에 접속한 유저의 수를 관리할 수 있습니다.

void SendJoined(const FString &uid,  // 유저의 uid 정보
                const ResponseHandler &response_handler); // 요청 결과 콜백

void SendLeft(const FString &uid,  // 유저의 uid 정보
              const ResponseHandler &response_handler); // 요청 결과 콜백

예를 들어 다음과 같이 데디서버 매니저로 콜백을 전달할 수 있습니다.

Login(...) {

 // 유저가 로그인 했을때 Join 메시지를 데디서버 호스트 매니저에 보냅니다.
 fun::FunapiDedicatedServer::SendJoined(uid,
   [uid](fun::FunapiDedicatedServer::HttpResponse http_response) {
     if (http_response.code != 0) {
       // 재전송 시도를 합니다.
       fun::FunapiDedicatedServer::SendJoined(uid, ...);
     }
   });
}

Logout(...) {

 // 유저가 로그아웃 했을때 Left 메시지를 데디서버 호스트 매니저에 보냅니다.
 fun::FunapiDedicatedServer::SendLeft(uid,
   [uid](fun::FunapiDedicatedServer::HttpResponse http_response) {
     if (http_response.code != 0) {
       // 실패시 재전송 시도를 합니다.
       fun::FunapiDedicatedServer::SendLeft(uid, ...);
     }
   });
}

46.7.7. 사용자 메시지 보내기

데디케이티드 서버에서 게임 서버에게 사용자 메시지를 보냅니다.

void SendCustomCallback(
    const FString &json_string,  // 사용자 메시지
    const ResponseHandler &response_handler);  // 요청 결과 콜백

46.7.8. 게임 상태 저장하기

데디케이티드 서버는 현재 게임 상태를 데디서버 호스트 매니저로 전달할 수 있습니다. 데디서버 호스트 매니저는 전달 받은 게임 상태를 레디스 서버에 업데이트합니다.

void SendGameState(
    const FString &json_string,  // 게임 정보 메시지
    const ResponseHandler &response_handler);  // 요청 결과 콜백

46.7.9. 명령줄 실행인자 가져오기

게임이 진행되는 동안 변경되지 않는 특정 값을 데디케이티드 서버에 저장하는 기능을 제공합니다. 아래 함수를 통해 추가 인자들을 가져올 수 있습니다.

Note

명령줄 실행인자에 사용자 정보들을 추가할 수 있습니다. 추가 인자를 지정하는 방법은 데디케이티드 서버 추가 인자 지정 부분을 참고해 주세요.

TArray<FString> GetUserConsoleCommandOptions();

예를 들어 게임 최대 진행시간 값을 데디케이티드 서버에서 사용하는 방법입니다.

TArray<FString> user_options =
    fun::FunapiDedicatedServer::GetUserConsoleCommandOptions();

for(FString user_option : user_options){
  if(user_option.Contains(FString("MAX_GAME_PLAY_TIME"))) {
    // 데디서버 매니저에서는 아래와 같이 추가 인자를 넣었다고 가정합니다.
    // "-MAX_GAME_PLAY_TIME 300"

    float max_game_play_time_ = 0;
    FParse::Value(*user_option,
                  TEXT("MAX_GAME_PLAY_TIME"),
                  max_game_play_time_);
  }
}

46.8. 데디케이티드 서버 처리하기 (유니티3D)

데디케이티드 서버에서 게임 서버와 통신하는데 필요한 부분은 유니티3D 플러그인의 FunapiDedicatedServer 클래스 함수로 제공합니다.

Tip

유니티 데디케이티드 서버는 UDP 프로토콜을 사용하므로 방화벽에서 UDP 프로토콜을 허용하게 설정해야 해야 합니다. 유니티 네트워크에 대한 설명은 All about the Unity networking transport layer 글을 참고해주세요.

46.8.1. 명령행 인자 처리

bool FunapiDedicatedServer.Init();

해당함수를 호출해서 데디케이디드 서버를 시작할 때 명령행으로 전달한 인자를 처리합니다. 형식이 잘못되거나 필요한 인자가 없는 경우 false 를 반환합니다.

46.8.2. 데디케이티드 서버 시작

void FunapiDedicatedServer.Start(string version);

위 API를 호출하면 데디케이티드 서버 초기화를 시작합니다. 초기화 과정은 버전 확인 및 프로세스 인자 확인 등을 포함합니다. 올바른 버전 형식(x.y.z.r)을 지정하지 않으면 초기화에 실패하며 데디케이티드 서버를 종료합니다.

Note

void FunapiDedicatedServer.Start(string version) 함수는 bool FunapiDedicatedServer.Init() 함수와 void FUnapiDedicatedServer.Start() 함수를 통합한 새로운 함수입니다(기존 함수들은 지원을 중단할 예정입니다). version 인자로는 FunapiDedicatedServer.version 에 지정했던 버전값을 다음과 같은 형태로 넣어주면 됩니다. 기존 : "{ \"version\": \"1.0.0.1\" }" -> 변경 : "1.0.0.1"

46.8.3. 콜백 함수 추가하기

데디케이티드 서버에서는 게임 상태를 관리할 수 있도록 여러 콜백 함수를 제공합니다.

// 데디케이티드서버 초기화가 정상적으로 진행되었을 때 호출됩니다.
event Action StartCallback;

// 새로운 유저가 이 데디케이티드 서버로 접속할 수 있을 때 호출됩니다.
// 유저 난입은 데디서버 매니저에서 처리할 수 있습니다.
// 여러 유저가 동시에 추가된 경우 추가된 순서대로 콜백을 호출합니다.
event Action<string /*uid*/, string /*json_string*/> UserDataCallback;

// 매치 데이터가 업데이트됐을 때 호출됩니다.
// 데디서버 매니저에서 유저 난입 시 match_data 를 업데이트한 경우 호출합니다.
// 여러 유저가 동시에 추가된 경우 추가된 순서대로 콜백을 호출합니다.
event Action<string /*json_string*/> MatchDataCallback;

// 데디케이티드서버와 데디서버 호스트 매니저의 연결이 끊겼을 때 호출됩니다.
event Action DisconnectedCallback;

다음과 같이 사용할 수 있습니다.

FunapiDedicateServer.StartCallback += delegate ()
{
    // 데디케이티드서버 시작 후 필요한 코드를 추가합니다.
    ...
};

FunapiDedicateServer.UserDataCallback +=
        delegate (string uid, string json_string)
{
    // 업데이트 된 유저 데이터에 대한 코드를 추가합니다.
    ...
};

FunapiDedicateServer.MatchDataCallback += delegate (string json_string)
{
    // 업데이트 된 매치 데이터에 대한 코드를 추가합니다.
    ...
};

FunapiDedicateServer.DisconnectedCallback += delegate ()
{
    // 게임 관련 정보들을 추후 반영하기 위해 파일로 저장하거나
    // 접속 중인 사용자들과 연결을 끊고 종료 처리를 할 수 있습니다.
    ...
};

46.8.4. 사용자 명령줄 실행인자 가져오기

데디케이티드 서버는 프로세스 실행 시 지정했던 인자들을 가져올 수 있습니다. 인자를 가져오는 방법은 다음과 같습니다.

Note

사용자 명령줄 실행인자는 데디서버 매니저에서 추가할 수 있습니다.

List<string> user_cmd_options;

위 API를 통해 추가 인자들을 가져올 수 있습니다. 다음과 같이 사용할 수 있습니다.

foreach (string cmd in FunapiDedicatedServer.user_cmd_options)
{
    // 각 커맨드에 대한 처리를 해줍니다.
    ...
}

46.8.5. 매치 혹은 유저 데이터 얻기

// 유저 데이터를 얻습니다.
string GetUserDataJsonString(string uid);

// 매치 데이터를 얻습니다.
string GetMatchDataJsonString();

uid 에 해당하는 유저 데이터와 매치 데이터를 JSON 형식으로 반환합니다.

46.8.6. 접속한 유저 인증 처리

bool AuthUser(string uid, string token);

uid 에 해당하는 유저가 인증 토큰 token 을 전달해온 경우 맞는 값인지 검사합니다.

46.8.7. 데디케이티드 서버가 준비되면 알려주기

void SendReady(
        Action<int /*response_code*/, string /*error_desc*/> callback);

데디케이티드 서버 초기화가 끝난 후 서버를 사용할 준비를 마치면 SendReady 함수를 호출합니다. 이 함수를 호출하면 클라이언트가 접속을 시작할 수 있습니다. 다음과 같이 사용할 수 있습니다.

SendReady("uid", delegate (int response_code, string error_desc)
{
    if (response_code == 0)
    {
        // 이 시점부터 클라이언트가 접속할 수 있습니다.
    }
    else
    {
        Debug.Log("error_desc={0}", error_desc);
        // 연결에 실패한 경우 0 이외의 응답 코드를 반환합니다.
        // 이 시점부터는 데디서버 호스트 매니저와 로컬 통신이 불가능하므로
        // 더 이상 게임 진행 상태를 게임 서버로 전달 할 수 없습니다.
        // 게임 관련 정보들을 추후 반영하기 위해 파일로 저장하거나
        // 접속 중인 사용자들과 연결을 끊고 종료 처리를 할 수 있습니다.
        ...
    }
});

Note

void Ready() 함수는 void SendReady(Action<int, string> callback) 와 동일한 기능을 하지만 콜백을 추가할 수 없어 메시지를 실제로 보냈는지 확인할 방법이 없습니다. 이와 같은 이유로 콜백이 없는 Ready() 함수와 Send...() 함수들은 지원을 중단할 예정입니다.

46.8.8. 유저 진입/이탈 전달하기

void SendJoined(
        string uid,
        Action<int /*response_code*/, string /*error_desc*/> callback);

void SendLeft(
        string uid,
        Action<int /*response_code*/, string /*error_desc*/> callback);

위 API를 사용해서 특정 유저 (uid) 가 진입/이탈했다는 정보를 게임 서버에 전달합니다. 다음과 같이 사용할 수 있습니다.

SendJoined("uid", delegate (int response_code, string error_desc)
{
    if (response_code == 0)
    {
        // 유저 진입 정보를 성공적으로 전달했습니다.
    }
    else
    {
        Debug.Log("error_desc={0}", error_desc);
        // 연결에 실패한 경우 0 이외의 응답 코드를 반환합니다.
        // 이 시점부터는 데디서버 호스트 매니저와 로컬 통신이 불가능하므로
        // 더 이상 게임 진행 상태를 게임 서버로 전달 할 수 없습니다.
        // 게임 관련 정보들을 추후 반영하기 위해 파일로 저장하거나
        // 접속 중인 사용자들과 연결을 끊고 종료 처리를 할 수 있습니다.
        ...
    }
});

SendLeft("uid", delegate (int response_code, string error_desc)
{
    if (response_code == 0)
    {
        // 유저 이탈 정보를 성공적으로 전달했습니다.
    }
    else
    {
        Debug.Log("error_desc={0}", error_desc);
        // 연결에 실패한 경우 0 이외의 응답 코드를 반환합니다.
        // 이 시점부터는 데디서버 호스트 매니저와 로컬 통신이 불가능하므로
        // 더 이상 게임 진행 상태를 게임 서버로 전달 할 수 없습니다.
        // 게임 관련 정보들을 추후 반영하기 위해 파일로 저장하거나
        // 접속 중인 사용자들과 연결을 끊고 종료 처리를 할 수 있습니다.
        ...
    }
});

46.8.9. 게임 상태 보내기

void SendGameState(
        string json_string,
        Action<int /*response_code*/, string /*error_desc*/> callback);

위 API를 사용해서 현재 게임 상태를 게임 서버에 전달합니다. 다음과 같이 사용할 수 있습니다.

SendGameState("state", delegate (int response_code, string error_desc)
{
    if (response_code == 0)
    {
        // 현재 게임 상태를 게임 서버로 성공적으로 전달했습니다.
    }
    else
    {
        Debug.Log("error_desc={0}", error_desc);
        // 연결에 실패한 경우 0 이외의 응답 코드를 반환합니다.
        // 이 시점부터는 데디서버 호스트 매니저와 로컬 통신이 불가능하므로
        // 더 이상 게임 진행 상태를 게임 서버로 전달 할 수 없습니다.
        // 게임 관련 정보들을 추후 반영하기 위해 파일로 저장하거나
        // 접속 중인 사용자들과 연결을 끊고 종료 처리를 할 수 있습니다.
        ...
    }
});

46.8.10. 사용자 정의 데이터 보내기

void SendCustomCallback(
        string json_string,
        Action<int /*response_code*/, string /*error_desc*/> callback);

위 API를 사용해서 사용자 정의 JSON 데이터를 게임 서버에 전달합니다. 다음과 같이 사용할 수 있습니다.

SendCustomCallback("custom", delegate (int response_code, string error_desc)
{
    if (response_code == 0)
    {
        // 사용자 정의 데이터를 성공적으로 전달했습니다.
    }
    else
    {
        Debug.Log("error_desc={0}", error_desc);
        // 연결에 실패한 경우 0 이외의 응답 코드를 반환합니다.
        // 이 시점부터는 데디서버 호스트 매니저와 로컬 통신이 불가능하므로
        // 더 이상 게임 진행 상태를 게임 서버로 전달 할 수 없습니다.
        // 게임 관련 정보들을 추후 반영하기 위해 파일로 저장하거나
        // 접속 중인 사용자들과 연결을 끊고 종료 처리를 할 수 있습니다.
        ...
    }
});

46.8.11. 게임 결과 보고하기

게임이 끝나면 JSON 형식으로 게임 서버에게 보낼 데이터를 만들어서 전송합니다. 아래 함수를 이용합니다.

void SendResult(
        string json_string,
        Action<int /*response_code*/, string /*error_desc*/> callback);

게임 서버에 보내는 데이터는 유효한 JSON 데이터여야 합니다. 다음과 같이 사용할 수 있습니다.

SendResult("result", delegate (int response_code, string error_desc)
{
    if (response_code == 0)
    {
        // 데디케이티드 서버는 자동으로 종료하지 않으므로 결과를 성공적으로
        // 보낸 후 종료해야 합니다.
#if UNITY_EDITOR
        // 에디터인 경우
        UnityEditor.EditorApplication.Exit(0);
#else
        // 에디터가 아닌 경우
        Application.Quit();
#endif
    }
    else
    {
        Debug.Log("error_desc={0}", error_desc);
        // 연결에 실패한 경우 0 이외의 응답 코드를 반환합니다.
        // 이 시점부터는 데디서버 호스트 매니저와 로컬 통신이 불가능하므로
        // 더 이상 게임 진행 상태를 게임 서버로 전달 할 수 없습니다.
        // 게임 관련 정보들을 추후 반영하기 위해 파일로 저장하거나
        // 접속 중인 사용자들과 연결을 끊고 종료 처리를 할 수 있습니다.
        ...
    }
});

46.9. 게임 결과 처리하기

게임 결과 보고하기 에서 보내는 PostResult / SendResult 결과는 게임 서버에서 등록한 콜백 함수에서 받아 처리할 수 있습니다.

#include <funapi/service/dedicated_server_manager.h>


// 게임 결과를 처리하는 콜백 함수입니다.
void result_callback(const fun::Uuid &match_id,
                     const fun::Json &match_data,
                     bool success) {
    // 게임 결과 처리를 수행합니다.
}

// 콜백을 등록합니다. 게임 서버의 ``Install()`` 또는 ``Start()`` 함수 등에서
// 등록하는 게 좋습니다.
DedicatedServerManager::RegisterMatchResultCallback(result_callback);

match_data 는 데디케이티드 서버가 전송한 JSON 데이터를 그대로 포함합니다. 매치(게임 세션) ID 와 이 값을 적절히 처리하거나, 데이터베이스에 저장할 수 있습니다.