Skip to content

Справочник конфигурации игры ​

Конфигурация, которую игра-плагин добавляет к движку или читает у него: дефолты хоста движка, которые игра вправе переопределить, игровая половина конфига хоста, клиентский конфиг (clientDefaults плюс собственный конфиг игры), форма авторизации и каталог звуков игры, порты и опкоды движка, системные сообщения чата, часы и игровые данные. Переменные окружения и собственные конфиги платформы в контракт плагина не входят.

Дефолты хоста движка (vimp-engine/config/hostDefaults.js) ​

Источник: vimp-engine/src/config/hostDefaults.js. Движковая половина конфига хоста: лимиты, таймеры, кик-политики, спектаторский keyset (наблюдение — механизм движка). Worker хоста merge'ит её с HostPlugin.gameConfig активной игры-плагина и применяет поверх настройки комнаты.

ПараметрЗначениеОписание
isDevModefalseФлаг режима разработки: открывает dev-команды чата и отладочный рекордер в HostGame (debugging.md). Комната берёт его из room.isDevMode, который клиент выставляет из import.meta.env.DEV; в прод-бандле остаётся false
maxPlayers30Дефолтный лимит участников; комната хоста ограничивает его настройкой создателя (кламп к roomDefaults.maxPlayers игры), лимит считается по людям
chatMaxLength60Максимальная длина сообщения чата (авторитетно на хосте; должна совпадать с maxlength поля ввода чата)
spectatorKeysnextPlayer/prevPlayerКоманды наблюдателя и неактивного игрока (переключение наблюдаемого)

Таймеры (timers, мс) ​

ПараметрЗначениеОписание
timeStep1000/120Шаг физического тика ядра (~120 Гц)
networkSendRate4Снапшот отправляется каждый N-й тик (4 → 30 пакетов/сек)
roundTime120000Время раунда
mapTime600000Время карты
roomTimeMin / roomTimeMax10000 / 3600000Серверные границы клампа пользовательских roundTime/mapTime комнаты (форма лобби — не граница доверия)
voteTime10000Время жизни окна голосования
timeBlockedVote30000Кулдаун между голосованиями одной темы
teamChangeGracePeriod10000Окно смены команды в начале раунда
roundRestartDelay5000Пауза между раундами
mapChangeDelay2000Пауза перед сменой карты после голосования
rttPingInterval3000Интервал RTT-пингов
idleCheckInterval30000Периодичность проверки бездействия

Кики (rtt, idleKickTimeout) ​

  • rtt.maxMissedPings: 5 — количество подряд пропущенных pong-ответов до кика;
  • rtt.maxLatency: 1000 — сглаженная (EMA) задержка (мс), при превышении которой игрок кикается; порог рассчитан на P2P-хостинг с домашних каналов (реальный RTT 200–300 мс и спайки на смене карты — норма);
  • idleKickTimeout.player: 120000 — кик игрока за бездействие (2 минуты);
  • idleKickTimeout.spectator: null — null отключает кик (наблюдатели не кикаются).

Игровая половина конфига хоста ​

Игровая половина конфига хоста приходит в Worker полем gameConfig активной игры-плагина (Worker хоста грузит HostPlugin динамически по entries.host активного GameManifest) — параметры вроде friendlyFire, mapScale, teams, scripted, soundCues, схемы stat/panel/ playerKeys и playerState.defaultState. Это полностью игровые данные — их конкретные значения игра документирует сама. Движок синхронизирует rank/state с платформой (plugin-api.md); rank и state для движка непрозрачны — форму интерпретирует только игра.

coreParams — необязательный непрозрачный словарь собственных параметров ядра игры. buildCoreConfig вливает его в игровую половину конфига ядра (hostPlugin.createCore(...)), причём известные движку ключи (friendlyFire, models, weapons, playerKeys, panel) перекрывают одноимённые — подменить движковую часть контракта игра не может. Остальное движок не читает и не валидирует, он лишь довозит его до GameSim::new: именно поэтому новый параметр игрового ядра больше не требует релиза движка (@vimp-games/tanks, например, передаёт так параметры падения 2.5D).

mapFallTime — сколько секунд длится падение на уровень высоты (по умолчанию 0.35). В отличие от coreParams это движковый ключ: buildCoreConfig кладёт его в половину engine, потому что траектория (FallModel в ядре) общая для всего, что роняет игра, и для динамических тел самой карты — их шагает движок. Тело, под которым не осталось плиты, падает на ближайшую плиту снизу.

У игры со своими уровнями это число обычно уже есть — в coreParams.levels.fallTime: по нему её ядро роняет танки и предсказывает на клиенте те же самые ящики. Два независимых поля роняли бы ящик у хоста и у его же клиента с разной скоростью, и разошлись бы они молча, поэтому buildCoreConfig берёт coreParams.levels.fallTime (если это конечное положительное число) ещё и как mapFallTime. Движковый ключ остаётся умолчанием для игр без уровней, а явный override по-прежнему сильнее обоих.

maps[].levelHeight — высота одного уровня в мировых единицах (без масштаба: масштаб карты умножает её ядро; по умолчанию — размер тайла). Она делает уклон рампы безразмерным — rise * levelHeight / span вместо «уровней на пиксель», — и константы подъёма (тяга, наклон корпуса, пыль) снова подбираются как уклон. Проверяют ядро и правило E4: конечное число больше нуля.

maps[].volumes (и maps[].levels[n].volumes) — необязательный словарь { "<ключ layers>": высота }: визуальная высота рендер-слоя в уровнях. Его проверяют ядро и правило контракта E4 (ключ обязан называть рендер-слой того же уровня, высота — лежать в (0, 8]), через хост он едет нетронутым и доезжает до рендер-парта полем volume; геометрию строит игра.

spectatorKeys — команды наблюдателя (nextPlayer/prevPlayer); набор движковый, живёт в vimp-engine/src/config/hostDefaults.js. playerKeys (команды игрока) — игровой конфиг, каждая клавиша имеет битовую маску key (1 << n, используется предиктором и ядром в истории ввода) и опциональный type:

  • type: 0 (по умолчанию) — многократное действие: начинается на keyDown, завершается на keyUp (движение, поворот башни);
  • type: 1 — срабатывает один раз на keyDown.

Клиентский конфиг: clientDefaults.js + собственный клиентский конфиг игры ​

Клиентский CONFIG_DATA собирается из двух половин: движковые дефолты — vimp-engine/src/config/clientDefaults.js (интерполяция, режимы/служебные клавиши управления, DOM-структуры движковых модулей, techInformList) и игровая половина, которую поставляет HostPlugin.buildClientGameConfig() активной игры-плагина (parts.*, канвасы, keyset игрока, схемы panel/stat, тексты chat/vote/gameInform, initIdList). Deep-merge выполняет buildClientConfig в Worker'е хоста; перед отправкой он дописывает:

  • modules.vote.params.time = game:timers:voteTime;
  • prediction — данные для клиентской реплики движения и стрельбы (timeStep, playerKeys, models, weapons, всё игровое).

Полная таблица, какие поля конфига движковые, а какие поставляет игра, — в plugin-api.md (раздел ClientPlugin API).

interpolation — snapshot-интерполяция (движок) ​

  • delay: 100 — мс; мир рендерится в прошлом (renderTime = serverNow − delay), ~3 кадра при 30 пакетах/сек;
  • maxFrameAge: 1000 — страховочная очистка старых кадров буфера.

divergence — детектор рассинхрона предикта (движок, опционально) ​

В боевом конфиге секции нет, и тогда путь кадра не делает ничего лишнего. Читается клиентским ядром, в headless-прогоне задаётся полем divergence сценария: thresholds (позиционно по player-блоку), defaultThreshold, capacity (кольцевой буфер), angles (индексы компонентов-углов, сравниваемых по окружности). См. debugging.md.

modules.canvasManager — полотна и камера ​

Общие параметры dynamicCamera — движковые; набор полотен canvases — игровой. Canvas-элементы генерирует клиент движка из этого конфига (ключ — id элемента; width/height — стартовый размер до первого resize):

ПараметрОписание
aspectRatioСоотношение сторон ('16:9'). Canvas заполняет максимум окна, сохраняя пропорцию. Без параметра — 100% окна
fixSizeФиксированный размер в px ('150' — квадрат, '200:100' — прямоугольник). Отключает aspectRatio и адаптивное масштабирование
baseScaleБазовый зум ('Числитель:Знаменатель'). Для адаптивных полотен — масштаб при эталонной ширине 1920px (итог = ширина/1920 × baseScale); для фиксированных — постоянный множитель
dynamicCameraВключает динамическую камеру (look-ahead + zoom от скорости)
shakeCameraРазрешает тряску камеры

Адаптивное масштабирование гарантирует одинаковый угол обзора на любых мониторах (эталон — Full HD 1920px).

dynamicCamera (общие параметры): lookAheadFactor (смещение камеры вперёд по движению), zoomOutFactor/maxZoomOut (отдаление от скорости), smoothnessPosition/smoothnessZoom/smoothnessVelocity (плавность).

pointerCanvas (игра, необязательный) — полотно, в систему координат которого пересчитывается канал указателя; по умолчанию первое объявленное.

Имена полотен, размеры и зум — данные игры; напр. @vimp-games/tanks задаёт vimp (16:9, зум 5:1, динамическая камера, тряска) и radar (150×150px, масштаб 1:8).

modules.controls — управление ​

  • keySetList (игра) — массив наборов keyCode: 'команда', целиком заданных игрой (напр. @vimp-games/tanks использует два: [0] — наблюдатель (n/p — переключение наблюдаемого игрока), [1] — игрок (w/s/a/d — движение, k/l/u — башня, j — огонь, n/p — смена оружия)). Какой набор активен, диктует хост через порт 17 (KEYSET_DATA).
  • pointer (игра, необязательный) — канал указателя (мышь/палец/стилус). Без этого ключа движок не вешает ни одного слушателя указателя. Поля: keySets (индексы наборов keySetList, в которых канал живой; по умолчанию все), doubleTapMs / doubleTapPx (пороги двойного тапа, по умолчанию 300 / 40), sendIntervalMs (минимум между двумя move, по умолчанию 50). Формат провода — "seq:aim:x:y:flags" с мировой точкой; см. client-api.md.
  • modes (движок) — режимы UI: c — чат, m — голосование, tab — статистика.
  • cmds (движок) — служебные клавиши (escape, enter), имеют высший приоритет и используются внутри режимов.

Прочие модули ​

DOM-структуры (elems) — движковые; тексты и схемы — игровые:

  • chat — id DOM-элементов, лимиты вывода (listLimit: 5 строк, lineTime: 15000 мс) и кэш — движок; шаблоны системных сообщений (messages, игра): реестр кодов групп, движковые группы s (статусы), v (голосования), m (карты), c (команды) плюс любые группы, которые регистрирует игра-плагин (напр. @vimp-games/tanks добавляет b для ботов). Хост шлёт только 'группа:номер:параметры', текст собирает клиент. У движковых групп есть английские умолчания в src/config/chatMessages.js (ниже); строка игры по тому же индексу их перекрывает, '' — не показывать сообщение, null/нет — умолчание. Игровая группа целиком на игре.
  • panel — контейнер containerId (движок); сопоставление серверных ключей (t, h, wa, w1, w2) полям (keys) и типизированная схема полей fields (игра): упорядоченный список { name, elem, type: 'bar'|'value'|'time'|'weapon', max?, blocks? } — PanelView генерирует DOM и поведение по типам, а не по именам полей.
  • stat — id контейнера (движок); подписи колонок columns, таблицы шапок/тел (heads, bodies) и sortList (игра) — DOM scoreboard генерирует StatView по схеме; sortList — параметры сортировки: массив пар [номер ячейки, по убыванию?]; при равенстве сравнение переходит к следующей паре.
  • vote — id/классы DOM (движок) и шаблоны голосований (templates, игра): [заголовок с плейсхолдерами {0}, варианты (массив — статичные, строка — запросить список у хоста), timeOff]. menu — пункты главного меню голосования.
  • gameInform / techInformList — шаблоны игровых сообщений (id элемента — движок, тексты list — игра) и технических экранов (движок): комната полна, кик за бездействие/задержку и т.д.
  • initIdList (игра) — какие модули/полотна инициализировать при старте (vimp, radar, panel, chat); механика инициализации — движковая. Эти DOM-id скрыты, пока не завершится авторизация; затем panel показывается как display: flex, все остальные id — как display: block.

Детали, которые легко упустить:

  • Ячейки панели. bar рисует блочную полосу — blocks по умолчанию 30, max — 100, с цветовой рампой по блокам, миганием при низком значении и анимированным наполнением на старте раунда (500 мс); value — просто число; time — M:SS; weapon получает класс .active, когда это активное оружие. Ключ t движок жёстко задаёт как время раунда в секундах.
  • Колонки статистики. columns — подписи заголовков, позиционно сопоставляемые индексам key колонок хоста; команда, присутствующая в bodies, но не в heads (наблюдатели), получает строки, но не агрегатный заголовок; sortList сортирует численно (~~textContent), поэтому текстовая колонка сортируется как 0. Движок пишет свои пять имён в объявленные колонки и отбрасывает остальные, но его CSS раскладывает только пять (#stat …:nth-child(1)…(5), плюс классы строк .line1–.line3): у лишней колонки нет ширины, пока игра не повторит раскладку в styles (правило контракта C6 предупреждает об этом).
  • Режим статистики leaderboard. Игра, чьё табло — глобальный топ, объявляет modules.stat.params.mode: 'leaderboard' (с period, limit, columns) и statMode: 'leaderboard' в gameConfig — на обеих половинах, иначе хост рассылает таблицу комнаты, которую никто не рисует, либо клиент рисует таблицу, которую никто не наполняет (правило контракта C11 — ошибка в обоих случаях). Вид рисует один список .stat-leaderboard из .stat-row (место · ник · очки), присылаемый хостом через ACCOLADES_DATA; собственная строка вызывающего подсвечена (is-self), заменяет последнюю строку, когда он вне топа, и показывает —, когда за период он не ранжирован. CSS движка даёт лишь голый трёхколоночный каркас (правило C6 проверяет, что styles игры упоминают .stat-leaderboard).
  • UI голосований. Шаблон — [title, values?, timeOff?]; values — массив подписей или 'teams'/'maps' для списков, подставляемых движком; timeOff: true скрывает обратный отсчёт. menu — список голосований по инициативе игрока — движок в него ничего не добавляет (голосование «Сменить хост» стартует через /changehost, а его окно — движковое голосование @changeHost; имена с @ зарезервированы). params.time подставляется из timers.voteTime. Весь блок modules.vote опционален: игра, которой не о чем голосовать, может его опустить.

Игровой конфиг авторизации ​

Схема формы авторизации (HostPlugin.authSchema: id DOM-элементов, параметры формы, игровые валидаторы, тексты) — полностью игровые данные; движок лишь предоставляет нейтральный шаблон экрана авторизации (заголовок, справочные секции, кнопка Start — без поля name: ник берётся из проверенной личности игрока) и AuthView, которая подставляет заголовок/подсказки игры из texts. authSchema.params обычно объявляет только игровые поля (например, model у @vimp-games/tanks, валидируемый её же isValidModel); движковый валидатор isValidName (validators) существует для игры, которая захочет добавить в форму поле ника, но в дефолтной форме не используется — ник берётся из проверенного токена identity лобби, а не вводится пользователем. Валидация выполняется и на клиенте (валидаторы из бандла игры), и повторно хостом (Worker) как итоговым авторитетом; по проводу (AUTH_DATA, порт 1) уходят только elems/params/texts — код валидаторов не передаётся. Собственный конфиг авторизации игры документирован в её репозитории.

Игровой каталог звуков ​

Каталог звуков (имена файлов, приоритеты, громкость, флаги зацикливания, список кодеков) — игровые данные, отдаются под assetsBase игры. Механика воспроизведения (лимиты голосов, приоритеты) — движковая, см. client-api.md. Там же описан необязательный блок parts.sounds.spatial (профиль проекции, виртуальная высота, радиус рассеивания, атрибуты PannerNode): игра, которая его не объявила, получает движковые дефолты. Переменных .env задача не вводит.

Порты и опкоды (vimp-engine/config/wsports.js, opcodes.js) ​

  • wsports.js — реестр числовых портов игрового протокола (источник истины). Полные таблицы — в snapshot.md.
  • opcodes.js — версия бинарного snapshot-формата (SNAPSHOT_FORMAT_VERSION = 5), ENGINE_API_VERSION и HOT_FLAGS. Реестр снапшот-ключей — игровые данные, поставляемые через HostPlugin.gameConfig.snapshot (числовой id + kind на каждый ключ, задающий байтовую раскладку блока). Незарегистрированный ключ уронит упаковку кадра. Подробности — в snapshot.md.
  • gameCodes.js — коды сообщений GAME_INFORM_DATA (порт 7) (winnerTeam/roundStart/gameOver), источник истины, общий для хоста и клиента (код 1 запускает анимацию старта раунда на панели и логотипе).

Системные сообщения чата (vimp-engine/config/chatMessages.js) ​

Английские тексты движковых кодов чата по умолчанию, группы s, v, m, c (индекс = номер кода). Лежат отдельно от clientDefaults.js: при слиянии конфига массивы заменяются целиком, и массив s игры стёр бы умолчания. ChatModel сначала берёт строку игры по тому же индексу; '' — не показывать сообщение; null или нет индекса — умолчание. Новый код движка добавляется в ENGINE_MESSAGE_CODES, в эту таблицу и в диапазоны правила B8 одним изменением (согласованность проверяет tests/config/chatMessages.test.js).

ГруппаТексты по индексам
s0 "Team {0} is full. Your current team: {1}", 1 "Your team: {0}", 2 "Your new team: {0}", 3 "Your new status: spectator", 4 "{0} killed {1}", 5 "{0} joined the game", 6 "{0} left the game", 7 "Host changed", 8 "You are no longer the host (connection lost)", 9 "Host changed: the previous host was lagging", 10 "… went inactive", 11 "… had a poor connection"
v0 "A vote has been created", 1 "Voting has started", 2 "Your vote has been accepted", 3 "Voting is temporarily unavailable", 4 "Vote passed", 5 "Vote failed", 6 "Usage: /changehost", 7 "You are the host — use “Hand over host” in the room menu", 8 "No connection to the master server", 9 "A host vote was held recently", 10 "No other player can host", 11 "A host vote is already in progress", 12 "A host vote is not possible right now", 13 "Vote to change host passed ({0}/{1})", 14 "Vote to change host failed ({0}/{1})", 15 "Host vote cancelled"
m0 "Current map: {0}", 1 "Next map: {0}"
c0 "Command not found", 1 "Your rank: {0}"

s:8 и v:6–v:15 клиент добавляет сам, по проводу они не ходят. Плейсхолдеры — {0}, {1}, … — подставляются из массива params.

Часы (vimp-engine/lib/clock.js) ​

Источник: clock. Не конфиг, а точка подмены, делающая матч воспроизводимым: синглтон (в идиоме lib/config.js) с методами now() (эпоха, мс, Date.now), monotonic() (высокое разрешение, performance.now), random(), setTimeout/clearTimeout/setInterval/clearInterval плюс install(custom) (возвращает функцию отката) и reset().

Все таймеры хоста идут через lib/AbstractTimer.js, который берёт таймер-функции из clock; call-site хоста зовут clock.now()/clock.monotonic()/clock.random() вместо глобалов. Дефолты резолвят глобалы в момент вызова, поэтому поведение в проде (и vi.useFakeTimers() в тестах) не меняется, а headless-runner подставляет VirtualClock и прогоняет десятиминутный матч за секунды — детерминированно. См. debugging.md.

Игровые данные (модели, оружие, карты) ​

Параметры моделей/танков, определения оружия и карты — полностью игровые статические данные, см. доки активной игры-плагина (например, docs/ru/configuration.md в @vimp-games/tanks) для их конкретной формы и значений. Один сквозной инвариант, важный для контрибьютора движка: коэффициенты модели движения обычно общие между авторитетным ядром игры и репликой клиентского предикта, поэтому игры закрывают их изменение своими cargo-тестами на паритет.


← Предыдущая: Plugin API · Следующая: Схема снапшота и форматы передачи →

vimp-engine 0.38.1 · vimp-engine-core 0.23.2 · create-vimp-game 0.5.4