Обзор
Игра на VIMP — пакет-плагин: вы поставляете ядро симуляции (Rust, скомпилированный в Wasm), конфиги и контент (карты, картинки, звуки); движок поставляет правила матча (раунды, команды, голосования, чат, панель, статистика), клиент с отрисовкой, звуком и вводом, а платформа — лобби и каталог. Эта страница — модель, нужная, чтобы написать игру: что вы поставляете, номера версий, что комната делает с вашей игрой и в каких ограничениях работает ваш код.
Хост авторитетен: симуляция матча идёт в одном месте, а клиенты рисуют результат и маскируют сетевую задержку интерполяцией и предсказанием. Роль хоста во время матча может перейти к другому игроку.
Что вы поставляете
| Забота | Вы (плагин) | Движок |
|---|---|---|
| Сущности, движение, оружие, урон, AI | Rust-ядро | — |
| Отрисовка (спрайты, эффекты, частицы) | PixiJS-parts | полотна, камера, рендер-цикл |
| Карты, модели, оружие, звуки, текстуры | контент | загрузка и воспроизведение |
| Раунды, счёт, команды, чат, голосования, статистика, панель | конфиг, схемы и тексты | правила и механизмы |
| Форма авторизации | поля и тексты | каркас формы и личность |
| Боты («scripted-участники») | реализация | хуки жизненного цикла |
Плагин — пакет с dist/manifest.json, двумя бандлами (host и client) и Wasm-бинарём; движок загружает его в рантайме и никогда не импортирует плагин статически. Браузер хоста недоверенный: анти-чит вне области, и сегодня контрмер в движке нет.
Номера версий
Два независимых значения:
| Константа | Значение | Смысл | Где проверяется |
|---|---|---|---|
ENGINE_API_VERSION | 4, заморожена | Метка поколения контракта плагина (GameManifest, HostPlugin, ClientPlugin, WASM ABI, схема форм). Не шлагбаум: ни один плагин не отвергается за возраст | нигде в рантайме; правило контракта B2 проверяет согласованность внутри пакета |
SNAPSHOT_FORMAT_VERSION | 5 | Байтовая раскладка кадра состояния | внутри WASM-ядра, на обоих концах |
Плагин публикует engineApi в трёх местах, и они должны совпадать между собой (расхождение означает устаревший dist/): manifest.engineApi, hostPlugin.engineApi, clientPlugin.engineApi. Импортируйте ENGINE_API_VERSION из vimp-engine/config/opcodes.js, а не зашивайте 4. Совпадать с установленным движком не требуется — игра, собранная год назад, работает на сегодняшней сборке.
Совместимость согласуется по возможностям, а не по номеру: игра, которая не может работать без конкретной возможности движка, перечисляет её в необязательном manifest.requires (см. plugin-api.md). Движок отвергает плагин, только когда имя неизвестно его реестру возможностей (capabilities), т.е. игра новее движка; платформа показывает такую игру в лобби недоступной, а loadClientPlugin и standalone SDK бросают исключение. SDK читает список из HostPlugin.requires / ClientPlugin.requires (в соло-режиме манифеста нет), поэтому один и тот же список объявляется в трёх местах и держится равным — правило B2 отказывает пакету, у которого манифест и половины расходятся. Манифест без requires не требует ничего сверх базового контракта.
manifest.id — идентичность игры на платформе: он обязан совпадать с id, под которым игра зарегистрирована (submitting.md); несовпадение молча убирает игру из лобби.
Что комната делает с вашей игрой
- Создание. Вошедший пользователь заполняет форму комнаты (поля из
manifest.roomForm, значения по умолчанию изmanifest.roomDefaults);maxPlayersограничиваетсяroomDefaults.maxPlayersигры. - Старт. Загружается плагин хоста, проверяются обязательные поля
gameConfig, дефолты движка сливаются сgameConfigи переопределениями комнаты, из результата собирается ядро. - Вход. Игрок выбирает комнату и получает клиентский конфиг и первый снапшот (snapshot.md).
- Игра. Симуляция шагает с фиксированной частотой, каждый N-й тик уходит бинарный кадр состояния; мета-события (чат, голосования, панель, статистика, звуки, informs) идут JSON'ом.
- Ротация. Раунд заканчивается при уничтожении команды (истечение таймера запускает новый без результата); карта меняется по истечении таймера карты, следующую выбирает системное голосование (host-meta.md).
- Смена хоста. Комната живёт, пока в ней есть люди (лобби-режим): когда хост уходит, отстаёт, прячет вкладку или его переголосовали, комнату принимает другой участник. Игра с
gameConfig.migration.midRound: trueпродолжает с того же тика; любая другая мигрирует мягко — мета сохраняется, раунд начинается заново, физический мир не переносится. Dedicated- и standalone-хосты не меняются.
Ограничения потоков
Плагин хоста и всё, что он импортирует, работают в Web Worker: никаких window, document, DOM API и PixiJS — только изоморфные API (Date, Math, performance, setTimeout, queueMicrotask, fetch). Клиентский плагин работает в основном потоке и может использовать PixiJS, но PixiJS обязан оставаться единым общим экземпляром, поставляемым движком (plugin-api.md, правило контракта A1), и никогда не вшиваться в плагин.
Маскировка задержки
Сеть сглаживают три механизма; все живут в клиентском ядре — WASM-классе ClientCore из того же Rust-бинаря (подробности — client-api.md, ABI — core.md):
- Интерполяция (даёт
vimp-engine-core): кадры буферизуются, мир рисуется в прошлом (serverNow − 100 мс); события выдаются ровно один раз, позиции интерполируются. - Предсказание (ваше ядро, например
@vimp-games/tanks): локальная сущность симулируется репликой авторитетной модели движения (формулы общие с ядром игры); хост подтверждает ввод (lastInputSeq), reconciliation переигрывает неподтверждённый ввод, а расхождение плавно затухает. - Клиентский спавн выстрелов (ваше ядро): выстрел виден и слышен мгновенно; дубликаты от хоста подавляются по id автора.
Ключевые инварианты
- Источник истины по портам —
vimp-engine/src/config/wsports.js; по версии бинарного формата —vimp-engine/src/config/opcodes.js; по snapshot-ключам — собственная схема игры, передаваемая черезHostPlugin.gameConfig.snapshot(см. plugin-api.md). - Паритет реплики движения: авторитетное движение и клиентская реплика предсказания обязаны делить формулы тика — это забота игры (cargo-тест паритета над
client::predictorв crate игры); движок даёт только обобщённую машинериюPredictor<G>/интерполяции. - Единое числовое пространство id для людей и скриптовых участников (ботов); различаются через
isScripted/isNetworked. Ядро оперирует числовыми id, мета — строковыми ключами — конвертация происходит на границеGameCoreAdapter.
← Назад к оглавлению SDK · Следующая: Скаффолдинг игрового плагина (create-vimp-game) →