Выделенный сервер: запуск, ENet и экспорт
Запуск без окна, серверная роль, UDP-порт, C#-пример и безопасный выбор Strip Visuals, Keep и Remove.
Что включает серверный режим
Выделенный сервер запускает игровую логику и сеть без отрисовки. Настройки запуска и экспорта решают разные задачи:
| Действие | Что меняется |
|---|---|
--headless | Драйвер окна становится headless, аудио — Dummy. Игровая логика сама по себе не становится серверной. |
-- --server | Пользовательский аргумент для вашего кода. В примере ниже запускает ENet-сервер. |
Экспорт с тегом dedicated_server | Движок автоматически выбирает headless и Dummy; пример включает серверную логику без --server. |
Export as dedicated server | Добавляет тег и позволяет заменить ненужные визуальные ресурсы заглушками. |
Добавление одного тега dedicated_server вручную не удаляет текстуры или меши из пакета. Запуск без окна также не означает нулевую нагрузку: физика, скрипты, импорт и сетевые обновления продолжают выполнять свою работу.
Первый запуск из проекта
Нужны C#-проект, настроенная главная сцена и работающая сборка проекта. Поместите оба файла ниже рядом с другими скриптами. Прикрепите DedicatedServerHost.cs к одной Entity в главной сцене. Этот пример владеет MultiplayerPeer дерева; не подключайте второй хост к тому же API Multiplayer.
Замените пути в команде на свои:
& 'C:\Tools\Reckfell\Reckfell Engine.console.exe' --headless --path 'D:\Games\MyGame' -- --server --port=7777 --bind=127.0.0.1-- отделяет аргументы движка от аргументов игры. --headless стоит до разделителя, --server, --port=... и --bind=... — после него. OS.GetCmdlineUserArgs() получает только вторую часть.
Ожидаемый вывод: SERVER_READY bind=127.0.0.1 port=7777. Для клиента адрес подключения — 127.0.0.1, порт — 7777. ENet использует UDP. Сообщение подтверждает создание локального сервера, а не доступность из интернета и не успешную авторизацию игроков.
По умолчанию пример слушает только текущий компьютер. Для сетевого хоста явно задайте --bind=0.0.0.0 или --bind=*; клиент подключается к действительному адресу сервера, а не к 0.0.0.0. Параметр --bind принимает IP-адрес или *, не доменное имя. Открывайте в межсетевом экране выбранный UDP-порт. Для внешних клиентов за NAT может понадобиться настройка маршрутизатора.
Без --server и без тега dedicated_server код не создаёт ENet-сервер, даже если процесс запущен с --headless. Это позволяет тем же проектом выполнять безоконные проверки без случайного открытия сетевого порта.
Полный код запуска
ServerOptions.cs выбирает роль и проверяет параметры. Допустим один --port=<1..65535> и один --bind=...; аргументы чувствительны к регистру. Нулевой порт намеренно запрещён этим примером, чтобы сервер не получил случайный порт вместо указанного.
using System;
using System.Globalization;
using System.Linq;
using System.Net;
public sealed record ServerOptions(bool Enabled, int Port, string BindAddress)
{
public static bool TryRead(string[] args, bool dedicatedBuild,
out ServerOptions options, out string error)
{
options = new(dedicatedBuild || args.Contains("--server"), 7777, "127.0.0.1");
error = "";
if (!options.Enabled) return true;
bool portSeen = false, bindSeen = false;
foreach (string arg in args)
{
if (arg.StartsWith("--port=", StringComparison.Ordinal))
{
if (portSeen || !int.TryParse(arg[7..], NumberStyles.None,
CultureInfo.InvariantCulture, out int port) || port is < 1 or > 65535)
{ error = "Use one --port=<1..65535>."; return false; }
portSeen = true;
options = options with { Port = port };
}
else if (arg.StartsWith("--bind=", StringComparison.Ordinal))
{
string address = arg[7..];
if (bindSeen || (address != "*" && !IPAddress.TryParse(address, out _)))
{ error = "Use one --bind=<IP address or *>; hostnames are not accepted."; return false; }
bindSeen = true;
options = options with { BindAddress = address };
}
}
return true;
}
}DedicatedServerHost.cs открывает порт, назначает транспорт и освобождает его при выходе Entity из дерева. StopServer() можно вызвать повторно. В примере установлен предел 32 клиента; это настройка ENet, а не подтверждённая производительность игры.
using Reckfell;
public partial class DedicatedServerHost : Entity
{
private ENetMultiplayerPeer? _peer;
public bool Listening { get; private set; }
public override void _Ready()
{
if (!ServerOptions.TryRead(OS.GetCmdlineUserArgs(), OS.HasFeature("dedicated_server"),
out var options, out string error))
{ ReckfellApi.PrintErr(error); GetTree().Quit(2); return; }
if (!options.Enabled) return;
_peer = new ENetMultiplayerPeer();
_peer.SetBindIP(options.BindAddress);
Error result = _peer.CreateServer(options.Port, maxClients: 32);
if (result != Error.Ok)
{
ReckfellApi.PrintErr($"SERVER_FAILED {result} bind={options.BindAddress} port={options.Port}");
_peer.Dispose(); _peer = null;
GetTree().Quit(3); return;
}
Multiplayer.MultiplayerPeer = _peer;
Listening = true;
ReckfellApi.Print($"SERVER_READY bind={options.BindAddress} port={options.Port}");
}
public void StopServer()
{
if (_peer is null) return;
Multiplayer.MultiplayerPeer = null!;
_peer.Close(); _peer.Dispose(); _peer = null;
Listening = false;
}
public override void _ExitTree() => StopServer();
}Это транспортный запуск. Репликация Entity, RPC, проверка полномочий и правила игры добавляются отдельно: сетевой игровой процесс. Не считайте клиентские команды достоверными только потому, что соединение установлено.
Экспорт без лишних ресурсов
- Убедитесь, что C#-проект имеет
.csprojи.sln. Обычная сборка.csprojещё не доказывает готовность к экспорту: C#-экспорт проверяет файл решения. - Установите экспортные шаблоны той же версии движка с поддержкой C# для целевой платформы и архитектуры. Для игры на сервере используйте экспортный исполняемый файл; редактор нужен для импорта и экспорта.
- Создайте отдельный профиль экспорта, например
Windows Server. Во вкладке ресурсов выберитеExport as dedicated server. - Для корня проекта выберите
Strip Visuals, затем переопределите отдельные файлы и каталоги по таблице ниже. Режим каталога наследуется дочерними файлами, пока для них не задан собственный режим. - Экспортируйте проект и перенесите всю папку результата, включая необходимые файлы C#. Не переносите только
.exeили один PCK наугад. - Запустите экспорт и проверьте
SERVER_READY. Включённый тег сам выбирает безоконные драйверы и серверную ветку примера;--portи--bindпо-прежнему передаются после--.
| Режим | Результат | Когда выбирать |
|---|---|---|
Strip Visuals | Поддерживаемые ресурсы заменяются заглушками. Например, Texture2D сохраняет размеры, Mesh — AABB, материал заменяется заглушкой. Исходные пиксели и геометрия не сохраняются как рабочие визуальные данные. | Когда серверу не нужны данные этого ресурса. |
Keep | Ресурс экспортируется с исходными данными. | Карта высот, пиксели для правил игры, геометрия для расчётов и другие реально читаемые сервером данные. |
Remove | Файл исключается из пакета. Ссылки на него не чинятся автоматически. | Только для файлов, которые серверная ветка не загружает и на которые не ссылаются необходимые сцены и ресурсы. |
Strip Visuals не является универсальным удалением всего графического содержимого: подмена зависит от поддержки create_placeholder у типа ресурса. Заглушка меша с AABB не заменяет его треугольники для ваших расчётов. Если форма столкновения уже хранится отдельно, сохраняйте нужную форму; если строите её из меша при запуске, сохраняйте и исходный меш через Keep. Аналогично, для анализа изображения нужен Keep, а не только сохранённые ширина и высота.
Не стройте серверную ветку с обязательной загрузкой клиентского меню, камеры или файла, помеченного Remove. Лучше иметь серверную главную сцену либо создавать клиентские Entity только в клиентской ветке. Файл ресурса Reckfell в текстовом формате имеет расширение .rdata; уровень — .rscene, префаб — .rprefab.
Подробности: теги возможностей, командная строка, экспорт проектов.
Запуск экспортного шаблона
PCK не является исполняемым файлом. Для ручной проверки используйте C#-экспортный шаблон той же версии, а не редактор: редактор не является проверкой загрузки экспортированных сборок C#.
В проверенном Windows debug-шаблоне 1.0.0 отключены переопределения пути: --path и --main-pack завершают запуск ошибкой. Эти параметры из команды запуска исходного проекта не нужно переносить в команду запуска экспортной игры.
Для отдельного пакета имена должны совпадать: Server.exe и Server.pck лежат рядом; консольная оболочка — Server.console.exe. При обычном экспорте сохраняйте созданные имена и всю структуру результата. Если пакет встроен в исполняемый файл, отдельный PCK не требуется.
Откройте терминал в папке экспорта и запустите:
& '.\Server.console.exe' -- --port=7777 --bind=127.0.0.1Ни --headless, ни --server здесь не нужны, если в экспортном проекте есть dedicated_server. После SERVER_READY подключите отдельный клиент. Сам серверный пакет не предоставляет меню для клиента.
Если сервер не работает
| Симптом | Что проверить |
|---|---|
Окно не открылось, но SERVER_READY отсутствует | Одного --headless недостаточно для выбора роли. Передайте -- --server или проверьте тег экспортированного проекта. |
Вывод Use one --port..., код завершения 2 | Порт должен быть целым числом 1..65535 без пробелов и повторного параметра. |
Вывод Use one --bind..., код 2 | Укажите один IP-адрес или *, не имя хоста. |
SERVER_FAILED, код 3 | Посмотрите имя ошибки, адрес и порт в той же строке. Проверьте, свободен ли UDP-порт и принадлежит ли адрес сетевому интерфейсу машины. |
| Локальный клиент подключается, внешний — нет | Пример по умолчанию слушает 127.0.0.1. Проверьте --bind, адрес клиента, UDP-правило межсетевого экрана и NAT. |
| После экспорта пропали данные изображения или меша | Проверьте наследуемый Strip Visuals; поставьте Keep для данных, которые сервер действительно читает. |
| Ошибка загрузки ресурса | Проверьте Remove, обязательные ссылки и полноту папки экспорта. |
| C#-экспорт сообщает об отсутствии решения | Восстановите правильный .sln рядом с проектом; успешная сборка одного .csproj эту проблему не устраняет. |
Пример проверен на Windows с установленной SDK 1.0.0: запуск без окна, ENet-подключение через loopback и работа экспортного debug-шаблона с отдельным PCK. В пакете проверены заглушки текстуры, меша и материала, а также Keep и Remove. Это не проверка Linux-развёртывания, release-шаблона, внешней сети или нагрузки.