Большая доля выделенных серверов, которые арендуют игроки, - Valheim, Rust, 7 Days to Die, Unturned, SCP: Secret Laboratory, The Forest, Sons of the Forest, V Rising, Core Keeper - это программы на Unity, запущенные без окна. У всех у них один и тот же скелет: исполняемый файл рядом с папкой <Name>_Data, два ключа (-batchmode -nographics), которые превращают игру в процесс без интерфейса, лог-файл, который Unity пишет в предсказуемое место, если не указать другое, и scripting backend (Mono или IL2CPP), от которого зависит, как работают моды. Разберитесь в этом скелете один раз - и любой сервер на Unity станет проще читать, запускать и отлаживать, какая бы студия его ни сделала.
Какие серверы сделаны на Unity и почему это важно#
Unity - это движок, а не серверный фреймворк. Каждая студия пишет поверх него свой сетевой код, свой формат конфигов и свою систему администрирования, так что знание того, что игра сделана на Unity, не подскажет, где лежат её настройки. Зато оно подскажет, как процесс ведёт себя на уровне операционной системы: как он запускается, как пишет логи, как расходует память и потоки и как в него попадают моды.
| Игра | Исполняемый файл сервера в Linux | Что в основном настраивает |
|---|---|---|
| Valheim | valheim_server.x86_64 | Аргументы запуска |
| Rust | RustDedicated | Аргументы +convar и server.cfg |
| 7 Days to Die | 7DaysToDieServer.x86_64 | serverconfig.xml |
| Unturned | Unturned_Headless.x86_64 | Commands.dat и Config.json |
| SCP: Secret Laboratory | Запускается через LocalAdmin | config_gameplay.txt |
| Sons of the Forest | Только Windows: SonsOfTheForestDS.exe | dedicatedserver.cfg |
Сервер на Unity обычно можно узнать по файлам. Ищите папку с именем <something>_Data, файл UnityPlayer.so (Linux) или UnityPlayer.dll (Windows) рядом с исполняемым файлом, а внутри папки данных - файлы globalgamemanagers и resources.assets. Если всё это на месте, всё, что написано в этой статье, к вашему серверу относится.
Некоторые игры, которые принято считать сделанными на Unity, на самом деле не на нём. Palworld, ARK, Satisfactory, Abiotic Factor и Squad сделаны на Unreal Engine (о них - в статье основы выделенного сервера на Unreal Engine); Terraria работает на XNA/FNA; Don't Starve Together и Factorio - на собственных движках студий; Space Engineers - на VRage. Разница практическая: расположение логов, модель потоков и загрузчики модов у них другие.
Batchmode, nographics и строка запуска#
Unity появился как движок для программ с окном. Выделенный сервер - это тот же исполняемый файл игрока, которому велено окно не открывать. Для этого есть два стандартных аргумента командной строки Unity:
-batchmode- запуск без интерактивного цикла, ожидающего пользователя: ни окна, ни ввода, ни диалогов. Всплывающие окна с ошибками, которые заблокировали бы десктопную сборку, подавляются.-nographics- вообще не инициализировать графическое устройство. Без этого ключа batchmode на машине без GPU всё равно может попытаться его создать и упасть или выделить память под рендеринг, которого никогда не будет.
Почти каждый стартовый скрипт сервера на Unity передаёт оба ключа. Поверх них каждая игра добавляет свои аргументы, которые Unity игнорирует, а читает код самой игры. Выглядят они по-разному:
# Valheim: plain dash arguments read by the game$ ./valheim_server.x86_64 -nographics -batchmode -name "Crew" -port 2456 \ -world "Midgard" -password "longship-42"# Rust: Unity flags, then +convar pairs$ ./RustDedicated -batchmode -nographics +server.port 28015 \ +server.identity "main" +server.hostname "My Rust server"# 7 Days to Die: a config file does most of the work$ ./7DaysToDieServer.x86_64 -batchmode -nographics -dedicated \ -configfile=serverconfig.xml -logfile output_log.txtНачиная с Unity 2021.2 есть ещё отдельная цель сборки "Dedicated Server". Игра, собранная так, лишена кода рендеринга и по умолчанию работает без интерфейса, поэтому ключи становятся подстраховкой, а не необходимостью. Старые игры и многие современные по-прежнему поставляются обычной сборкой игрока и полагаются на ключи, поэтому никогда не убирайте их из строки запуска, даже если сервер вроде бы работает и без них.
В панели вроде Pterodactyl строка запуска хранится в startup-команде egg, а игровые части вынесены в переменные на вкладке Startup. Вы меняете переменные, а ключи Unity остаются зафиксированными под ними. В статье параметры запуска игрового сервера разобрано, как эти переменные превращаются в командную строку на разных движках.
Частота кадров - это и есть tick rate
Сервер на Unity - это игровой цикл. Каждый кадр он обсчитывает физику, AI и сеть, а затем начинает заново. В batchmode его не сдерживает vsync, поэтому сервер, разработчик которого забыл ограничить частоту кадров, будет выдавать столько кадров в секунду, сколько потянет одно ядро, и держать CPU на 100%, даже когда никто не подключён. Большинство игр ограничивают её через Application.targetFrameRate и выносят лимит в настройки - в Rust, например, есть fps.limit. Если пустой сервер на Unity нагружает ядро полностью, сначала поищите эту настройку, а уже потом думайте, что что-то сломано. О компромиссе между кадрами и отзывчивостью - в статье что на самом деле значит tick rate.
Куда серверы на Unity пишут логи#
Unity ведёт собственный лог - сообщения движка плюс всё, что игра выводит через Debug.Log, - в файл Player.log. Где окажется этот файл, зависит от платформы и одного аргумента:
| Ситуация | Где лог |
|---|---|
Linux, без -logFile | ~/.config/unity3d/<Company>/<Product>/Player.log |
Windows, без -logFile | %USERPROFILE%\AppData\LocalLow\<Company>\<Product>\Player.log |
-logFile path/to/file.txt | Указанный файл, путь относительно рабочей папки |
-logFile - (где поддерживается) | Стандартный вывод, то есть лог появляется в консоли |
<Company> и <Product> - это строки, которые студия задала в проекте, например IronGate/Valheim. Лог предыдущего запуска обычно сохраняется как Player-prev.log в той же папке.
Это самая частая причина, по которой говорят, что у сервера на Unity "нет логов". Игра пишет логи как положено, просто в скрытую папку в домашнем каталоге того пользователя, от имени которого она запущена. Egg-и панелей почти всегда передают -logFile - либо с файлом внутри папки сервера, либо с дефисом, чтобы вывод попадал в консоль, ведь консоль - единственное окно, которое у вас есть.
Некоторые игры ведут свои логи помимо логов Unity. 7 Days to Die пишет файлы output_log с отметками времени, если её об этом попросить; Rust пишет логи для каждого identity, если передать -logfile; LocalAdmin у SCP: SL держит собственную папку с логами. Когда что-то ломается, проверяйте оба: лог игры - на ошибки игры, лог Unity - на ошибки движка и нативных библиотек. Карта логов по играм есть в статье логи игрового сервера.
Строки, которые выглядят страшно, но таковыми не являются
Логи серверов на Unity начинаются с блока служебных сообщений движка, который каждый раз пугает людей. На машине без интерфейса почти всё это безобидно:
Initialize engine version: 2022.3.xForcing GfxDevice: NullNullGfxDevice: Version: NULL 1.0 [1.0]Desktop is 0 x 0 @ 0 HzFallback handler could not load library .../Mono/libcЧто означает каждая из этих строк:
Initialize engine versionсообщает, какую версию Unity использует игра. Её стоит запомнить на случай, если о ней спросит загрузчик модов.Forcing GfxDevice: Nullи блокNullGfxDevice- это-nographics, работающий как задумано.Desktop is 0 x 0 @ 0 Hz- отсутствие дисплея, и это правильно.- Строки
Fallback handler could not load library- это Mono, который ищет нативные библиотеки под альтернативными именами. Они появляются почти на каждом сервере Unity под Linux и почти никогда ничего не значат. The referenced script on this Behaviour is missing- предупреждение о контенте, с которым студия выпустила игру. Раздражает, но не смертельно.
Строки, которые действительно важны, идут позже: NullReferenceException, повторяющийся каждый кадр, DllNotFoundException, всё, где упоминается неудачный SteamAPI_Init, и последние строки перед завершением процесса. Как читать лог вверх от места сбоя, разобрано в статье как читать консоль сервера.
Файлы: что лежит в папке _Data#
Установка сервера на Unity выглядит примерно так, имена зависят от игры:
server/ valheim_server.x86_64 the player binary UnityPlayer.so the engine valheim_server_Data/ Managed/ C# assemblies (Mono builds only) Assembly-CSharp.dll the game's own code MonoBleedingEdge/ the Mono runtime Plugins/ native libraries (Steamworks and others) globalgamemanagers engine settings baked at build time *.assets, *.resS game content linux64/steamclient.so Steam client library for the server steam_appid.txt which Steam app this isНичего внутри _Data не является конфигурацией, которую стоит править. Это сама игра, и SteamCMD перезапишет её при следующем обновлении (а с validate ещё и "починит" всё, что вы поменяли). Настройки, сохранения, бан-листы и конфиги модов живут в другом месте - в аргументах запуска, в конфиге, который игра читает из корня сервера, или в папке сохранений. Расположение сохранений зависит от игры и часто по умолчанию совпадает с тем же скрытым путём .config/unity3d или AppData\LocalLow, что и у лога, поэтому многие игры предлагают аргумент, чтобы его перенести (-savedir в Valheim, +server.identity в Rust, UserDataFolder в конфиге 7 Days to Die). Список есть в статье файлы сохранений игрового сервера.
Отдельного упоминания заслуживает steam_appid.txt. Steamworks читает его, чтобы понять, от имени какого приложения он работает. Если файла нет или в нём неверный id, SteamAPI_Init() завершается ошибкой, и сервер либо отказывается запускаться, либо запускается без функций Steam, так что игроки не могут его найти или подключиться. Стартовые скрипты часто создают этот файл или экспортируют SteamAppId перед запуском; start_server.sh у Valheim делает именно это, а заодно выставляет LD_LIBRARY_PATH=./linux64, чтобы нашёлся встроенный steamclient.so.
Mono или IL2CPP: почему от этого зависят моды#
Unity компилирует код игры на C# одним из двух способов, и для владельца сервера это самая значимая деталь из всех.
- Mono: код игры поставляется в виде .NET-сборок в
_Data/Managed, и Mono выполняет их при загрузке. Эти DLL можно загрузить, изучить и пропатчить. На этом держится каждая зрелая экосистема модов для Unity - BepInEx 5 для Valheim и многих других игр, Oxide/uMod и Carbon для Rust, RocketMod и OpenMod для Unturned, EXILED для SCP: SL. - IL2CPP: код на C# при сборке преобразуется в C++ и компилируется в нативный код. Папки
Managedнет, есть только большойGameAssembly.soилиGameAssembly.dllи папкаil2cpp_data. Для модов нужен более тяжёлый инструментарий (BepInEx 6, MelonLoader), который генерирует прокси-сборки, и при обновлениях они ломаются чаще.
Понять, что у вас, можно, просто посмотрев: папка Managed, полная DLL, означает Mono, GameAssembly - IL2CPP. Проверьте это до того, как планировать сервер с модами. Игра, которая на сервере Mono, а на клиенте IL2CPP (так бывает), всё равно может принимать серверные плагины, но для клиентских модов правила другие.
Обычные загрузчики модов для Mono внедряются через Doorstop - небольшую нативную библиотеку, которая запускается раньше кода игры. Именно поэтому BepInEx для Valheim поставляется с другим стартовым скриптом и папкой doorstop_libs: скрипт выставляет переменные окружения (LD_PRELOAD в Linux), чтобы загрузчик стартовал первым. Если сервер с модами запускается, но не загружает ни одного мода, команда запуска почти всегда осталась ванильной. Подробности для Valheim - в статье моды Valheim с BepInEx на сервере, а порядок загрузки модов поможет с зависимостями, когда загрузчик уже работает.
Память, CPU и потоки#
У серверов на Unity узнаваемый профиль потребления ресурсов, и знание его удерживает от покупки не того апгрейда.
Один загруженный поток. Unity выполняет игровую логику в главном потоке. Физика, jobs и часть сетевого кода могут использовать рабочие потоки, а несколько студий вынесли тяжёлую работу из главного потока, но на практике время кадра сервера на Unity определяет одно ядро. Частота важнее количества ядер. Если график в панели показывает тариф на 2 ядра загруженным на 50%, а игроки жалуются на лаги, значит одно ядро на 100%, а второе простаивает. Арифметика объяснена в статье как читать загрузку CPU игрового сервера.
Память, которая растёт. Mono использует сборщик мусора, который редко возвращает память операционной системе, а игры подгружают контент по мере того, как игроки исследуют мир. Сервер на Unity, который стартовал с 2 ГБ и через три дня занимает 4 ГБ, обычно ведёт себя нормально - хотя утечки в коде игр тоже частое дело. Стандартный ответ на оба случая - перезапуск по расписанию в тихий час. Как отличить одно от другого, разобрано в статье утечки памяти на игровом сервере.
Паузы сборщика мусора. Когда сборщик запускается, на большой куче он может остановить главный поток на десятки миллисекунд, и игроки ощущают это как подлагивания через равные промежутки. Инкрементальный сборщик Unity (доступен с 2019 года) распределяет эту работу, но включать ли его в игре, решает студия, а не вы.
Сохранения в главном потоке. Многие игры на Unity сериализуют мир в главном потоке, поэтому сервер замирает на время сохранения. Быстрый накопитель сокращает паузу; более длинный интервал автосохранения делает её реже, но при падении теряется больше. Этот компромисс взвешен в статье интервалы автосохранения игрового сервера.
| Симптом | Вероятная причина | Что проверить первым |
|---|---|---|
| Пустой сервер грузит одно ядро на 100% | Неограниченная частота кадров | Настройку лимита FPS или тиков в игре |
| Подлагивание каждые несколько минут | Автосохранение или GC | Совпадает ли оно со строкой "saving" в логе |
| Память растёт всю неделю | Нормальный рост или утечка | Возвращает ли перезапуск её к исходному уровню |
| Лаги растут вместе с постройками | Количество сущностей | Инструменты игры для сущностей |
Linux, библиотеки Steam и ошибки, с которыми вы столкнётесь#
Большинство серверов на Unity выходят с нативной сборкой для Linux, и это хорошая новость - Wine не нужен. Известные исключения - The Forest и Sons of the Forest: их серверы есть только под Windows и в Linux работают через Wine. Нативные сборки всё же зависят от нескольких нативных библиотек, и отсутствие какой-то из них порождает ошибки, которыми забиты ветки техподдержки.
dlopen failed trying to load: steamclient.so[S_API FAIL] SteamAPI_Init() failed; SteamAPI_IsSteamRunning() failed.Сервер не может найти клиентскую библиотеку Steam. Steamworks ищет её в ~/.steam/sdk64/steamclient.so (и в sdk32 для 32-битных сборок). Обычное решение - скопировать ту, что установил SteamCMD:
$ mkdir -p ~/.steam/sdk64$ cp ~/steamcmd/linux64/steamclient.so ~/.steam/sdk64/Некоторые стартовые скрипты вместо этого добавляют ./linux64 в LD_LIBRARY_PATH, и это работает, пока используется скрипт. Запустить исполняемый файл напрямую, в обход скрипта, - классический способ вернуть эту ошибку.
Другие ошибки, которые указывают на платформу, а не на игру:
- `DllNotFoundException` с именем библиотеки: не хватает нативной зависимости. На минимальном дистрибутиве это часто 32-битная библиотека или
libatomic,libpulseи подобные, с которыми игра слинкована даже без звука. То, что указано в сообщении, и нужно установить. - `Segmentation fault` при запуске: часто это загрузчик модов, собранный под другую версию Unity, или повреждённая установка. Запустите SteamCMD с
validateи попробуйте снова без модов. - Процесс сразу завершается без лога: лог ушёл туда, куда вы не смотрите. Добавьте
-logFileс явным путём, запустите снова и прочитайте этот файл.
В хостинговой панели эти библиотеки входят в образ, поэтому первые две ошибки редки; третья - та, с которой вы скорее всего встретитесь. Если у вас своя машина, в статье игровые серверы на Linux и Windows разобрано, что делать, когда сборки под Linux нет и нужен Wine.
Обновления и версии#
Серверы на Unity распространяются через SteamCMD, как и любые другие серверы из Steam, так что обновление - это app_update <appid> validate. Поверх этого есть два момента, специфичных для Unity.
Во-первых, версия движка может смениться вместе с обновлением игры. Когда студия переходит с одного релиза Unity на другой, обновлять нужно каждый нативный загрузчик модов и каждый прокси IL2CPP, и ломается всё сразу, а не по одному плагину. В патчноутах об этом пишут редко, а строка Initialize engine version в логе - всегда. Запомните её перед обновлением и сравните после.
Во-вторых, почти в каждой игре на Unity клиент и сервер должны совпадать точно, потому что сетевой протокол генерируется из кода самой игры. Разницы в одну сборку достаточно, чтобы соединение было отклонено. Когда выходит патч, сервер нужно обновить до того, как кто-то с новым клиентом сможет зайти, - а серверу с модами стоит дождаться своего загрузчика. Порядок действий описан в чек-листе на день обновления, а статья app id в SteamCMD и beta-ветки объясняет, как удержать сервер на старой сборке, если игра это позволяет.
FAQ#
Нужен ли GPU, чтобы запустить игровой сервер на Unity?
Нет. С -batchmode -nographics Unity создаёт пустое графическое устройство и ничего не рендерит. Серверы работают на обычных машинах только с CPU, и именно так их запускает любой хостинг. Если сервер жалуется на графику, значит не хватает одного из двух ключей.
Почему мой сервер на Unity занимает целое ядро, когда никого нет?
Либо частота кадров не ограничена и цикл крутится так быстро, как может, либо игра симулирует мир даже без игроков. Сначала поищите в настройках игры лимит кадров или тиков. Нормальная картина - сервер с лимитом, который в простое загружен на несколько процентов.
Можно ли использовать BepInEx на любом сервере на Unity?
BepInEx 5 работает на сборках Mono. Сборкам IL2CPP нужен BepInEx 6 или другой загрузчик, и поддержка там менее надёжная. Прежде чем на него рассчитывать, проверьте, есть ли папка Managed с DLL. У некоторых игр есть свой предпочтительный фреймворк (Oxide или Carbon для Rust, RocketMod или OpenMod для Unturned), и он лучше универсального загрузчика.
Где найти лог, если в консоли пусто?
Ищите Player.log в ~/.config/unity3d/<Company>/<Product>/ в Linux или в AppData\LocalLow\<Company>\<Product>\ в Windows либо добавьте -logFile с путём на ваш выбор. В панели egg обычно и так направляет вывод в консоль.
Сервер на Unity медленнее, чем на Unreal?
Ни один из движков не быстрее как сервер сам по себе. Важно то, как студия построила симуляцию: сколько работы идёт в главном потоке, как хранятся сущности, как устроено сохранение. Сравнивайте игры, а не движки.




Комментарии
Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.