Привет.
Статья в помощь разработчикам мультиплеера на UnrealEngine, которые по тем или иным причинам испытывают сложности билда сервера локально (разрабатываете на Mac, или недостаток ресурсов, места, и так далее).
Начнем с проблематики. Хоть движок и мультиплатформенный, сам сервер логично иметь на Linux. Вы можете конечно хостить сервер на Mac, но вряд ли это оправданно. Поэтому, встает резонный вопрос — а как сбилдить сервер под Linux?
Далее проблематика билда сервер под Linux, находясь на другой ОС:
На Linux проблем нет, если есть ресурсы и место (а это минимум 200 гигабайт места и лучше от 8 ядер и 32Гб памяти).
На Windows есть танцы с бубном, но проблема может быть той же, что в п.1
На Mac, это как раз мой случай, я как не пробовал, у меня не получалось, а каждый такой эксперимент разгонял Mac, мешая остальной работе.
Выход? Я решил попробовать сбилдить это дело в облаке. И желательно так дешево, чтобы это можно было делать раз за разом.
AWS вроде предлагает нечто похожее, но только через заявку (не путать с GameLift), и скорее всего встанет в копеечку. Я нашел решение за 50 центов ;)
И о времени. В целом билд такого проекта процесс не быстрый на любом железе. Но все же предупрежу — на выбранном ниже конфиге у меня ушло 5-6 часов от входа на сервер и запуск собранного сервера.
Далее сухое todo для копипаста с некоторыми комментариями. Но поверьте, шишек на черновиках я набил достаточно.
Выбор сервера
Я выбирал между:
AWS, но за подходящую машинку я бы платил примерно те же 50-70 центов, но за час, если не дороже.
DigitalOcean, примерно в два раза дешевле Амазона, но у них очень неудобный скейл — то ресурсов не хватает, то места. Или докупать тома, что невыгодно.
Внезапно мой выбор пал на Hetzner Cloud. Только на их облачную инфраструктуру, где вы можете запустить сервер также на час. И стоит это всего 0.093 евро. Ну не круто ли?!
Тут не стоит жадничать (вы просто иначе не влезете) и брать сразу этот:

Потому что места вам потребуется примерно 200гб, да и на 8 ядрах работает сильно шустрее. Да-да, я экспериментировал и с предыдущим вариантом.
ОС Ubuntu
. И обязательно версию 22.04
, это официально поддерживаемая версия Эпиками. Не прочитав требования, я поставил сразу последнюю, билд на финальных нотах не получился, было обидно (ispc.generated
-файлы не билдились).
Подготовка и билд движка
Вам нужно создать пользователя в группе sudo
, так как билд под рутом запрещен. Так как сервер у нас сугубо под одну задачу на несколько часов, я не стал париться с какой-либо безопасностью и системой прав, просто создал пользователя builder
. Ну и обновить пакеты конечно тоже следует.
apt update && sudo apt upgrade -y
adduser builder
usermod -aG sudo builder
При создании пользователя спросит пароль, он вам в последующем пригодится.
Далее просто переходим под созданного пользователя и прямо там же можем клонировать репозиторий движка нужной версии (напомню, текущая статья про 5.5
). Для доступа к клонированию репозитория UE не забудьте, что ваш гитхаб-аккаунт должен быть привязан к аккаунту EpicGames.
su - builder
git clone --branch 5.5 https://github.com/EpicGames/UnrealEngine.git
Займет операция выше примерно полчаса. Далее переходим в директорию движка, выполняем сетап и генерацию, а также удаляем два ненужных серверу плагина, иначе они будут вставлять палки в колеса. На этапе сетапа вас как раз спросит пароль пользователя builder
.
cd UnrealEngine/
./Setup.sh
./GenerateProjectFiles.sh
rm -rf ./Engine/Plugins/Fab
rm -rf ./Engine/Plugins/Bridge
Вот мы подошли к самому жирному этапу — билду (примерно 3 часа). Для надежности запустим все через screen
, чтобы потом вернуться, если разлогинились (screen -r
).
screen -S unreal_build
make
Билд сервера игры
Итак, билд движка закончили, двигаемся дальше. Напомню, мы находимся в директории движка, поэтому поднимаемся выше и клонируем уже свой проект.
cd ../
git clone https://github.com/anton-ds/DedicateTest.git
После клонирования переходим в директорию движка и запускаем команду билда сервера. Это займет еще пару часов, но повторные ребилды сервера будут конечно быстрее.
cd UnrealEngine/
./Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \
-project="/home/builder/DedicateTest/DedicateTest.uproject" \
-noP4 -platform=Linux -server -build -cook -stage -pak -archive \
-archivedirectory="/home/builder/DedicateTest/BuildOutput"
Напомню, builder
- наш пользователь, под которым мы билдили, также его одноименная директория, куда мы склонировали проект (в моем случае это DedicateTest
). Итоговый результат мы получим в поддиректории BuildOutput
нашего проекта.
Запуск сервера и подключение клиента
Я намеренно в статье не упомянул как вообще правильно писать сетевые игры на Unreal Engine (на самом деле это все есть из коробки), так как заголовок предполагает, что вы читаете для конкретной цели – билд сервера, и знаете как все остальное устроено.
Тут следует заметить, что облачный сервер выполнил свою задачу и может быть удален. Вы просто копируете директорию BuildOutput/LinuxServer
на свой продакшен сервер и запускаете его уже там. Но разово рекомендую следующие шаги проверить на текущем.
На сервере переходим в директорию нашего игрового сервера и запускаем сервер (сначала открыв порт, по которому сервер будет слушать — в моем случае это 7777
). Напомню, мы все еще под пользователем builder
и под ним и остаемся. А код проекта — DedicateTest
.
sudo ufw allow 7777/udp
sudo ufw reload
cd ./LinuxServer
./DedicateTestServer.sh ThirdPersonMap -port=7777
Обратите также внимание, в параметрах запуска сервера я также передаю название карты, на которой запущен сервер. В моем случае так удобнее.
Ну все, предполагаем, что клиент у вас сбилжен на той же версии, запускаем игру. Возможно, у вас уже реализована логика подключения к серверу, но если нет, то вы можете открыть консоль в самой игре (~
) и ввести команду, указав IP сервера и порт.
open <ip>:7777
В логах самого сервера вы должны увидеть успешное подключение по IP вашего клиента. Подключившись еще одним клиентом, вы уже сможете взаимодействовать между клиентами посредством сервера.

Но есть нюанс...
Совместимость
Есть логичное требование — и сервер и клиент должны быть собраны из одной версии кода. Ну вот только Unreal Engine в это вкладывает несколько больше — например, билд из исходников и билд из бинарной версии (той, что вы запускаете из Лаунчера) — разные по версиям билды. (причем ошибку совместимости может давать не всегда)
Самое простое и грамотное решение — это билдить еще и клиент из исходников, и это даже рекомендуется для мультиплеерных игр. Но мы исходили из того, что прямо здесь и сейчас мы этого сделать не можем или не хотим.
Итак, ошибка совместимости выражается в следующем — в момент подключения клиента сервер ругается ошибкой в логах:
LogHandshake: CheckVersion: Incompatible version. bValidHandshakeVersion: 1, bValidNetVersion: 0, GHandshakeEnforceNetworkCLVersion: 0, RemoteMinVersion: 3, RemoteCurVersion: 4, MinSupportedHandshakeVersion: 3, CurrentHandshakeVersion: 4, RemoteNetworkVersion: 2530911350, LocalNetworkVersion: 4263286266, RemoteNetworkFeatures: GenericReplication, LocalNetworkFeatures: GenericReplication
CheckVersion: Incompatible version ... RemoteNetworkVersion: 2530911350
, LocalNetworkVersion: 4263286266
.
Для чернового фикса, чтобы проверить работу вашего клиента с сервером, можно сделать следующее (важно! такой фикс НЕ для продакшена!). Либо вы можете написать свою логику проверки версий.
В файле Source/DedicateTest/DedicateTest.cpp
(название файла зависит от названия вашего проекта) добавляем следующее:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "DedicateTest.h"
#include "Modules/ModuleManager.h"
class FDedicateTestModule : public FDefaultGameModuleImpl
{
public:
virtual void StartupModule() override
{
FNetworkVersion::IsNetworkCompatibleOverride.BindStatic(&FDedicateTestModule::IsNetworkCompatible);
FNetworkVersion::GetLocalNetworkVersionOverride.BindStatic(&FDedicateTestModule::GetLocalNetworkVersion);
}
virtual void ShutdownModule() override
{
FNetworkVersion::IsNetworkCompatibleOverride.Unbind();
FNetworkVersion::GetLocalNetworkVersionOverride.Unbind();
}
static bool IsNetworkCompatible(uint32 LocalNetworkVersion, uint32 RemoteNetworkVersion)
{
// here you can add your own logic for checking network compatibility
return true;
}
static uint32 GetLocalNetworkVersion()
{
// must be equal to the server version
return 4263286266;
}
};
IMPLEMENT_PRIMARY_GAME_MODULE( FDedicateTestModule, DedicateTest, "DedicateTest" );
Вот число 4263286266
это как раз число сетевой версии из логов сервера. Пересобираем клиентскую сборку (не сервер), запускаем. Все должно заработать.
На этом все, не забудьте удалить сервер сборки ;-)
ntfshard
Анриал не собирал, но классический
make
без параметров собирает в 1 поток. Точно-j4
например не надо?anton_dolganin Автор
У них это учтено. Ну по крайней мере загружены были все CPU. А в целом да, спасибо за уточнение.