Перейти к содержимому

Справочник

Сетевой протокол

Это протокол между помощником лаунчера и сервером NodeMP, сетевой протокол v23. Ресурсы его не видят: они отправляют и получают события. С ним сталкиваются в фильтре ретрансляции нативного модуля, которому пакеты описываются категорией и подтипом, и при чтении дампа трафика или отладочного лога сервера. Здесь названы пакеты и их порядок; раскладка по полям — в include/net/Protocol.h сервера, точная копия которого есть у помощника.

У сессии два канала между помощником и сервером: одно TCP-соединение внутри TLS 1.3 для всего надёжного и, после привязки, один поток UDP для позиций машин.

  • По UDP идут только State::Pos, State::HeadPose, State::PosBatch и пакеты рукопожатия Ping, Pong, UdpHello и UdpHelloAck. Датаграммы клиента подписываются ключом сессии, который один раз приходит по TLS (UdpToken) и никогда не передаётся по UDP. Пока UDP не привязан, позиции идут по TCP.
  • Локальные каналы. Игра говорит с помощником по двум локальным TCP-портам: 4444 — канал команд, 4445 — ретрансляция. Первый кадр на каждом — Command::Auth с токеном помощника; с неверным токеном соединение закрывается.
  • Сжатие. Сжатое тело помечено битом FlagCompressed и несёт точный размер после распаковки, поэтому получатель сверяет его с пределом пакета до распаковки. Клиентский мод сжатых тел не видит: их распаковывает помощник.
  • Лимиты. Кадр — до 4 КБ до Welcome и до 1 МБ после; UDP-датаграмма — до 10 КБ. Конфигурация машины — до 768 КБ, чтобы её повтор при входе помещался в кадр. Эталонный манифест передаётся частями по IntegrityChunkBytes = 32 КиБ.

Каждый пакет назван двумя байтами: категорией и подтипом внутри неё. TCP-кадр — это [u32 length][category][subtype][flags][body], UDP-датаграмма — то же без длины. Существует восемь категорий:

Категория Байт Что в ней
Handshake 0x01 Установка соединения, проверка связи, привязка UDP.
Session 0x02 Приход и уход игроков, сообщения для всей сессии.
Content 0x03 Список модов, загрузка файлов, клиентские скрипты ресурсов.
Vehicle 0x04 Машины: спавн, места, авторитет.
State 0x05 Частое состояние машин.
Event 0x06 Именованные события: API плагинов и клиентских скриптов.
Command 0x07 Канал команд между игрой и помощником.
Module 0x08 Двоичные данные между нативными модулями и клиентами.

Подтип 0 недопустим во всех категориях, так что обнулённый буфер никогда не означает пакет. Числа — little-endian, строки — UTF-8 без нулевого байта, и без префикса длины может быть только одно поле переменной длины — последнее, оно занимает остаток кадра.

В таблицах L — помощник, S — сервер, G — клиентский мод в игре; T — TCP-канал сервера, U — его UDP-канал, R — порт ретрансляции, CC — порт команд.

Подтип Направление Назначение
Hello L→S, T Версия протокола и имя, которое попросил игрок. При несовпадении версий — отказ с обеими версиями.
Welcome S→L, T Id игрока, токен для восстановления сессии, окно восстановления и биты возможностей сервера.
Ping L→S, T и U Проверка связи и времени отклика; необязательная метка возвращается в Pong.
Pong S→L, T и U Ответ на Ping.
UdpToken S→L, T 64-байтный ключ сессии для подписи UDP.
UdpHello L→S, U Привязывает UDP-адрес клиента нонсом и HMAC. Верный пакет привязывает с любого адреса — это нужно при раздельном туннелировании VPN.
MapInfo S→L, T Карта, которую должна загрузить игра.
JoinWorld L→S, T Карта загружена, присылай мир.
VerifyRequest S→L, T Насколько строго проверять установку игры ([integrity] verify), а для strict — id эталонного манифеста. Во время сессии его отправляет player:verify.
VerifyReport L→S, T Что лаунчер проверил и что нашёл: уровень, итог, число проблем и строка подробностей.
Identity L→S, T Билет на вход, всегда сразу после Hello; пустой — билета нет.
IntegrityManifestRequest L→S, T В кэше лаунчера нет эталонного манифеста с этим id, и он его просит.
IntegrityManifestChunk S→L, T Очередная часть сжатого манифеста.
IntegrityManifestDone S→L, T Все части отправлены; хеш, который лаунчер сверяет, прежде чем сохранить файл.
Resume L→S, T Идёт после Hello вместо Identity, чтобы вернуть оборванную сессию в пределах окна.
UdpHelloAck S→L, U Подтверждает принятый UdpHello на привязанный адрес.
Подтип Направление Назначение
Kick S→G, T и R Соединение отклонено или завершено, причина — текстом.
SelfInfo S→G, T и R Собственный id и итоговое имя вошедшего.
PlayerJoined S→G, T и R Id и имя другого игрока.
PlayerLeft S→G, T и R Игрок вышел.
PlayerList S→G, T и R Состав: сколько игроков, максимум, все id и имена.
SessionEnd L→G, R Помощник сообщает игре, что сессия закончилась, и почему.
ClientId L→G, R Помощник сообщает игре её id игрока.
Policy G→S, T и R Выбор игрока для его машин: кто может садиться, пользоваться триггерами, тянуть узлы.
Reconnecting L→G, R Связь оборвалась, помощник восстанавливает сессию: номер попытки и следующая пауза.
Resumed L→G, R Сервер вернул сессию.
Подтип Направление Назначение
ModsRequest L→S, T Запросить список модов сервера.
ModsInfo S→L, T Список модов в JSON.
FileRequest L→S, T Запросить один файл мода по имени.
FileBegin S→L, T Сейчас пойдёт файл: размер, затем сами байты.
FileDeny S→L, T Файла не будет: неизвестен, защищён или не читается.
SyncDone L→S, T У помощника есть все нужные файлы.
ResourceChunk S→G, T и R Часть клиентских скриптов одного ресурса, в JSON.
ResourceDone S→G, T и R Все ресурсы доставлены.
Подтип Направление Назначение
SpawnReq G→S, T и R Игрок просит заспавнить машину или заменить ту, в которой сидит.
Spawn S→G, T и R Машина есть: её id, кто заспавнил, повреждения и конфигурация.
SpawnDeny S→one, T и R Отказ в спавне с причиной; игрок убирает свою локальную машину.
Edit G↔S, T и R Новая конфигурация (детали, настройка).
Delete G↔S, T и R Машина удалена.
Reset G↔S, T и R Машину сбросили на позицию.
Coupler G↔S, T и R Изменилось сцепление или дверь.
Paint G↔S, T и R Новая покраска.
Camera G↔S, T и R За какой машиной следит камера игрока.
SeatClaim G→S, T и R Игрок просит место водителя, пассажира или выйти.
SeatVerdict S→one, T и R Ответ на SeatClaim, с причиной, если отказал плагин.
Driver S→G, T и R Кто теперь ведёт машину.
SeatFree S→G, T и R Место водителя освободилось.
Authority S→G, T и R Чья игра теперь считает машину.
AuthRevoke S→one, T и R Эта игра больше не считает машину.
PlayerVehicle S→G, T и R В какой машине игрок и в какой роли.
Resync S→G, T и R Полное сохранённое состояние машины заново.
ResyncReq G→S, T и R Клиент просит пересинхронизировать одну машину или все.
Trigger G↔S, T и R Нажатие триггера или двери, повторяется всем.
DamageStat G→S, T и R Счётчик повреждений от авторитета машины; остаётся на сервере.
DamageBlob G→S, T и R Сами данные о повреждениях, изредка; остаются на сервере для входа и пересинхронизации.
CouplerSet S→G, T и R Сервер открывает или закрывает сцепку или дверь.
Tag S→G, T и R Тег, который плагин поставил машине; пустое значение удаляет его.
Lock S→G, T и R Кто может садиться в машину.
ConfigHash G↔S, T и R Контрольная сумма сохранённой конфигурации; при расхождении — тихая пересинхронизация.
TriggerReq G→S, T и R Попросить авторитета машины выполнить триггер; плагины могут отказать.
NodeGrab G→S, T и R Запрос экспериментального захвата узлов; отклоняется, если плагин не разрешил.
NodeGrabSet S→one, T и R Разрешённый захват, переданный авторитету машины.
Grab G↔S, T и R Поток синхронизированного захвата узлов, пересылается остальным клиентам.
EditDeny S→one, T и R Отказ в правке или покраске с причиной от плагина, перед откатом.
Подтип Направление Назначение
Pos G↔S, U с откатом на T Положение, поворот, скорости и органы управления машины, в фиксированном двоичном виде.
Inputs G↔S, T Оси ввода сверх тех, что в Pos, при изменении.
Electrics G↔S, T Фары, поворотники, приборы — изменения.
Nodes G↔S, T Положения узлов: деформация.
BreakGroups G↔S, T Отвалившиеся детали.
Controller G↔S, T Один вызов контроллера.
Powertrain G↔S, T Режимы трансмиссии.
Engine G↔S, T Двигатель и зажигание.
HeadPose G↔S, U с откатом на T Положение камеры или головы игрока; не сохраняется.
PosBatch S→G, U Несколько снимков Pos в одной датаграмме. Отправляет только сервер.
Fire G↔S, T Огонь на машине от её авторитета, сохраняется как Electrics.
Подтип Направление Назначение
Event G↔S, T и R Именованное событие: имя и данные. node.emitServer на клиенте, player:send и node.broadcast на сервере.

Пакеты Command не покидают компьютер игрока.

Подтип Направление Назначение
Keepalive G↔L, CC Возвращается как есть.
VersionReq G→L, CC Узнать версию протокола помощника.
Version L→G, CC Версия протокола.
Connect G→L, CC Подключиться к серверу по адресу и порту.
Quit G→L, CC Выйти из сессии.
StatusReq G→L, CC Узнать состояние сессии.
Status L→G, CC Состояние сессии со строкой подробностей.
PingReq G→L, CC Узнать пинг до сервера.
Ping L→G, CC Пинг в миллисекундах, -1, если неизвестен.
MapReq G→L, CC Узнать карту сессии.
Map L→G, CC Карта; до сессии — пусто.
ModLoaded G→L, CC Игра загрузила файл мода.
ModList L→G, CC Файлы модов, нужные сессии.
ConnectFailed L→G, CC Вход не удался, с причиной.
Prompt L→G, CC Вопрос игроку.
PromptAnswer G→L, CC Ответ игрока: да или нет.
Auth G→L, CC и R Локальный токен; первый кадр на обоих каналах.
Server L→G, CC На каком сервере сессия, если вход начал лаунчер.
ClockPing G→L, CC Часы игры; помощник отвечает сразу.
ClockPong L→G, CC Метка игры и часы помощника, чтобы их согласовать.
ArrivalStamp L→G, R Когда следующая пересланная датаграмма пришла к помощнику.
ServerName L→G, CC Имя сервера, как в списке.
GameCaps G→L, CC Что умеет принимать этот клиентский мод; отправляется один раз при подключении.
Подтип Направление Назначение
Data G↔S, T и R Сырые байты на канале с номером, который выбирает модуль. На сервере — node.modules.on и node.modules.send.
  1. Помощник открывает TLS и отправляет Hello, затем Identity с билетом на вход (или Resume, чтобы вернуть оборванную сессию).
  2. Сервер проверяет баны, свободные места и билет, выдаёт игроку id и спрашивает плагины: playerConnectRequest, затем playerAuthenticated. Отказ — это Kick с причиной.
  3. Welcome и VerifyRequest. Если строгому серверу нужен эталонный манифест, помощник скачивает его, проверяет игру и отправляет VerifyReport. Проваленная проверка — Kick до любых загрузок.
  4. Моды: ModsRequest, ModsInfo, затем FileRequest и FileBegin или FileDeny для каждого недостающего файла, пока не придёт SyncDone.
  5. UdpToken и MapInfo, сразу за ними — клиентские скрипты ресурсов (ResourceChunk … ResourceDone). Сервер вызывает playerJoining, игра загружает карту.
  6. Помощник привязывает UDP через UdpHello и отправляет JoinWorld.
  7. Сервер отвечает SelfInfo вошедшему и PlayerJoined остальным, повторяет все машины с их состоянием и вызывает playerJoined.

Версия протокола растёт с каждым изменением на проводе и передаётся в Hello и Command::Version. Версии должны совпадать точно: лаунчер с другой версией получает отказ, в котором названы обе. Добавления, которые старые участники могут спокойно пропустить, вроде битов возможностей в конце Welcome, обходятся без новой версии.