← Модуль 4/Таксономия нетокода
EN
Модуль 4 · Онлайн-миры (1997–2005)

Таксономия нетокода: три модели

Скрыть пинг можно тремя принципиально разными способами. Какой выбрать — диктует не вкус, а ответ на два вопроса: что у тебя на проводе (состояние или ввод) и можешь ли ты гарантировать детерминизм. Отсюда — авторитетный клиент-сервер MMO/шутеров, роллбэк файтингов и детерминированный локстеп RTS.
🏠 лаба~17 мин
Суть за 30 секунд
Пинг не обмануть — но его можно спрятать, и есть ровно три канонические стратегии. Авторитетный клиент-сервер (MMO, шутеры): сервер — истина, клиент предсказывает локально и сверяется (см. клиент-предсказание); шлём состояние мира, цена — трафик ∝ числу сущностей и борьба с читами. Детерминированный локстеп (RTS): шлём только команды, оба клиента крутят идентичную симуляцию; трафик микроскопический даже на 1500 юнитов, но появляется input delay и «ждём самого медленного». Роллбэк (файтинги): шлём только ввод, предсказываем ввод оппонента и при ошибке откатываем и пересчитываем кадры; свой ввод мгновенен, цена — детерминизм + сейв состояния каждый кадр. Выбор модели — это выбор, что класть на провод: состояние дорого, но гибко; ввод/команды дёшевы, но требуют детерминизма.

Механизм: что кладём на провод

Вся таксономия растёт из одного решения. Можно слать по сети состояние мира («игрок A в точке X с HP 80») — тогда клиенту не нужен детерминизм, он просто рисует присланное, но объём растёт с числом сущностей, и кто-то должен быть авторитетом, иначе клиент соврёт. Либо слать ввод/команды («нажал вперёд», «юниты 5–40 — атаковать сюда») и воспроизводить их одинаковой симуляцией на всех машинах — тогда трафик крошечный, но любое расхождение в вычислениях рушит всё (desync).

Стоимость канала

Грубая модель трафика на игрока. При рассылке состояния платим за каждую видимую сущность каждый снапшот:

Bstate≈ Nent· sent· fsnap

При локстепе/роллбэке — только за команды или биты ввода, и это не зависит от числа юнитов:

Bcmd≈ ccmd· scmd· ftick , ccmd≪Nent

Это и есть тезис статьи Бэттнера и Террано «1500 Archers on a 28.8» (GDC 2001, Ensemble, Age of Empires): попытка слать позиции юнитов упирала RTS в ~250 объектов на модеме 28.8k; рассылка команд при детерминированной симуляции сняла потолок до 1500+ — на проводе те же несколько команд за ход, хоть тысяча лучников на экране.

Стоимость задержки

Скрытие пинга у трёх моделей даёт разную ощущаемую задержку ввода (через сколько ты видишь реакцию на свою кнопку). Кадр при 60 fps = 16.67 мс; пусть RTT = 100 мс (one-way ≈ 50 мс ≈ 3 кадра).

Локстеп откладывает твой собственный ввод на input delay — столько кадров, чтобы команда успела дойти до пира до общего исполнения кадра:

dinput= ⌈ tone-wayΔt ⌉ = ⌈50/16.67⌉ =3 кадра

То есть ~50 мс лага — и его чувствуют оба игрока, всегда. Роллбэк и клиент-предсказание дают ощущаемую задержку ≈ 0 (свой ввод применяется в этом же кадре), а цену платят иначе: предсказанием чужого + откатом (роллбэк) или сверкой с сервером (клиент-сервер). Наивный авторитет без предсказания — это весь RTT, 100 мс, «управление по почте».

Ощущаемая задержка ввода · RTT 100 мс · 60 fps 0 50 мс 100 мс наивный авторитет ~100 мс (весь RTT) локстеп (RTS) ~50 мс input delay клиент-предсказание ≈0 + сверка/rubber-band роллбэк (файтинг) ≈0 + откат-«снап» Зелёные платят не лагом, а пересчётом/коррекцией; красный/жёлтый — лагом напрямую.

Три модели рядом

СвойствоКлиент-сервер + lag-compЛокстеп (детерм.)Роллбэк
На проводесостояние мира (дельты)командыбиты ввода
Авторитетвыделенный сервернет (все равны)нет (P2P, 2 игрока)
Детерминизмне обязателенобязателен, строгийобязателен
Трафик∝ числу сущностейкрошечныйкрошечный
Скрытие пингапредсказание + сверка + интерп.input delayпредсказание ввода + откат
Артефактrubber-banding, «смерть за углом»все ждут самого медленноговизуальный «снап»
Игроковот 2 до тысяч2–8 (масса юнитов)обычно 2
ЖанрMMO, BR, шутерыRTS, авто-баталерыфайтинги

Числовой итог при RTT 100 мс / 60 fps: наивный авторитет — 100 мс лага; локстеп — 50 мс, но всем; клиент-предсказание и роллбэк — ~0 мс лага, но первый ловит rubber-band при расхождении с сервером, второй — «снап» на 1–3 кадра при ошибке предсказания. Дальше каждую модель — отдельным уроком: клиент-предсказание (готов), роллбэк подробно.

🕹 В какие игры поиграть — и что заметить

Три модели нельзя прочувствовать в одной игре — у каждой свой жанр-носитель. Сыграй в три разные и поймай сигнатуру задержки каждой: что именно «лагает», когда сеть плохая.

Counter-Strike / любой шутер клиент-сервер + lag-comp

Выделенный сервер — истина, ты предсказываешь своё движение и стрельбу, чужих видишь чуть в прошлом (интерполяция), сервер отматывает время для попаданий. Сигнатура плохой сети — rubber-banding (тебя дёргает назад при расхождении прогноза) и «я умер уже за углом» (favor-the-shooter).

🎮 Сыграй: в CS введи net_graph 3, поиграй на высоком пинге. Лагает твоё движение резиной и чужие телепортируются — но мир не замирает целиком. Это состояние-на-проводе с предсказанием.

StarCraft / Age of Empires детерминированный локстеп

Шлются только команды, симуляцию крутят синхронно все машины. Поэтому сотни юнитов — без проблем по трафику, но твой клик исполняется через input delay (turn N+k), и при чьём-то лаге замирает вся игра, не отдельный юнит.

🎮 Сыграй: в старый StarCraft/AoE на плохой сети — заметь, что лагает весь мир разом (ждём всех), а юниты двигаются плавно и одинаково на всех экранах. Это команды-на-проводе + детерминизм. В PC-баге Кореи это было незаметно: пинг низкий.

Guilty Gear Strive / Killer Instinct роллбэк

P2P, 2 игрока, биты ввода на проводе. Свой ввод — мгновенно; ввод оппонента предсказывается («жмёт то же, что в прошлом кадре»), при ошибке — откат и пересчёт нескольких кадров. Сигнатура плохой сети — короткий «телепорт-снап» персонажа, но никогда input lag.

🎮 Сыграй: в GGST/KI онлайн при скачке пинга поймай микро-«тряску» оппонента — это видимый откат+пересимуляция. Сравни с делэй-нетокодом (ранний Smash Ultimate / старый SFV): там вместо снапа — ровный input lag на всё. Глубже — урок про роллбэк.

EVE Online клиент-сервер в экстриме · один шард

Та же клиент-серверная семья, но доведённая до предела: единый сервер (Tranquility), и при перегрузке вместо лага сервер замедляет время (Time Dilation) — все действия идут в 10–30% скорости, но согласованно. Авторитет сохранён ценой темпа.

🎮 Посмотри: зайди в EVE в момент крупного боя (или ролики B-R5RB 2014). Индикатор TiDi покажет «10%» — мир едет в slow-mo, но не рассыпается. Это выбор «консистентность важнее темпа». Подробно — в уроке «Персистентность и шардинг».

Хардкор · инженерия: детерминизм, float и почему локстеп страшнее роллбэкаможно пропустить

Детерминизм — это требование «одинаковый ввод → побитово одинаковый результат на всех машинах». Враг номер один — floating point: один и тот же код на разных CPU/компиляторах даёт разный результат (порядок операций, FMA, x87 80-бит vs SSE 64-бит, fast-math). Поэтому детерминированные движки берут fixed-point (целочисленную арифметику) или строго фиксируют float.

Почему локстеп — самый суровый

  • Расхождение фатально и тихо. В клиент-сервере сервер всё равно поправит клиента; в локстепе одно расхождение в одном юните из тысяч → desync, и дальше миры разъезжаются необратимо. Нужен побитовый детерминизм по всей симуляции.
  • Детерминированный рандом. ГПСЧ с общим сидом, синхронный порядок вызовов. Любой «незаметный» rand() вне синхросимуляции (партикл, звук) не должен влиять на геймплей.
  • Порядок команд. Команды всех игроков на кадр N сортируются детерминированно (по player id), иначе A→B и B→A дадут разный итог.
  • Контрольные суммы. Движки периодически шлют хэш состояния; разошёлся — ловим desync сразу, а не через 10 минут.

Роллбэк vs локстеп — общая ДНК

Оба детерминированы и шлют ввод, но локстеп ждёт ввод перед исполнением (input delay), а роллбэк предсказывает и исполняет сразу, откатывая при ошибке. Роллбэк = «оптимистичный локстеп». Цена роллбэка — сейв состояния каждый кадр (для отката); поэтому он живёт там, где состояние крошечное (файтинг ~10–50 КБ), и почти не применим к RTS, где состояние огромно. См. отдельный урок.

Хардкор · хостинг: dedicated vs P2P, NAT и где это считатьможно пропустить
  • Кто хостит. Клиент-сервер MMO/шутеров требует выделенных серверов: античит, нет «host advantage», стабильность. Локстеп и роллбэк обычно P2P — нет состояния-истины, нечего хостить централизованно; дёшево, но падает при дисконнекте «хоста»/пира.
  • NAT и matchmaking. P2P упирается в NAT-traversal (STUN/TURN, hole punching); часть соединений всё равно идёт через relay. Поэтому даже «P2P» файтинги держат relay-серверы.
  • Авторитет ≠ топология. Можно P2P с одним пиром-авторитетом (listen-server, кооп вроде Helldivers/Deep Rock): дёшево, но хост видит мир без пинга и его сложнее защитить от читов.
  • Региональные серверы. Физику пинга не обмануть: RTT ≥ 2·расстояние/c. Все три модели выигрывают от близкого дата-центра — матчмейкинг привязывает к региону, чтобы снизить базовый RTT, на котором работает скрытие.
  • Гибриды. Современные кооп-игры берут клиент-сервер с ослабленным авторитетом (меньше игроков, социальный контекст, терпимость к лагу) — компромисс между ценой dedicated и защитой P2P.
Аналогия
Три способа синхронизировать встречу. Клиент-сервер — один диктор, который всем рассылает «вот текущая картина» (много слов, но никто не соврёт). Локстеп — оркестр по одной партитуре: дирижёр даёт только короткие команды «такт N+3 — вступаем», и все играют синхронно по нотам, что у них уже есть; но темп держит самый медленный. Роллбэк — два синхронных пианиста, каждый угадывает следующую ноту партнёра и играет сразу; ошибся — быстро переиграл последний такт. Состояние диктуют словами; команды/ввод — нотами.
Почему это важно
Это первый архитектурный выбор сетевой игры, и он необратим: модель определяет жанр, который ты вообще можешь сделать, бюджет трафика, требования к детерминизму и то, как игра ощущается на плохой сети. Перепутать модель с жанром (роллбэк для MMO, клиент-сервер для файтинга) — классическая дорогая ошибка. Понимание «что на проводе → что отсюда следует» отличает «работает в LAN» от «работает на 2000 игроков в одной системе EVE».
🔁 За пределами игр — куда это переносится
Это фундаментальный выбор распределённых систем: реплицировать состояние (state machine replication через лог) или пересылать состояние — и где разместить авторитет.

Распределённые системы: локстеп = детерминированный state-machine replication (Raft/Paxos: реплицируем лог команд, не снапшоты; все реплики применяют одинаковые команды в одинаковом порядке → одинаковое состояние). Клиент-сервер со снапшотами = primary-replica с пересылкой состояния. «Шлём команды, требуем детерминизм» vs «шлём состояние, детерминизм не нужен» — ровно эта дихотомия.

ML / AI: распределённый трейнинг — та же развилка. Data-parallel = «шлём состояние»: каждый воркер считает градиенты, all-reduce синхронизирует веса/градиенты (большой трафик, но воркеры независимы). Детерминированная пересборка из сида = «шлём команды»: храним сид+порядок данных и воспроизводим эпоху побитово вместо хранения чекпойнтов — дёшево по месту, но требует детерминизма (как локстеп). А off-policy-акторы (IMPALA), действующие устаревшей политикой с последующей коррекцией, — это «оптимистичный» путь, родственник роллбэка.

Бэкенд / БД: репликация через лог операций (event sourcing, WAL-shipping) vs через снимок (snapshot replication) — выбор «команды или состояние». Детерминизм реплики = воспроизводимость лога.

Принцип: реши, что дешевле двигать — состояние (гибко, дорого, не нужен детерминизм) или команды (дёшево, но всё держится на побитовой воспроизводимости). И отдельно реши, у кого авторитет.

🏠 Лаба — симулятор лага и предсказания
Интерактивная лаба без кода: двигай аватар, крути пинг, потери пакетов и тикрейт, переключай режим (наивный авторитет / клиент-предсказание / локстеп) — и смотри своими глазами, как один платит лагом, другой rubber-band'ом, третий input delay'ем. Открыть лабу →
Лучший момент: на одном и том же пинге переключи «наивный» → «предсказание» — аватар перестаёт запаздывать; подними потери пакетов и увидишь rubber-band коррекции (сервер и клиент разошлись). Потом включи «локстеп» — лаг вернётся, но ровный и без снапов (зато при потерях он замирает).
🔧 Запусти и поковыряй — на домашнем компе
Во что поиграть по жанрам — выше (🕹). Здесь — пощупать различие моделей инструментами:
🔧 Поковырять (debug) ~40 мин, Godot/GGPO
Возьми готовый сетевой сэмпл: Godot high-level multiplayer (клиент-сервер, MultiplayerSynchronizer) и любой роллбэк-плагин (GodotSteam/SnapNet или GGRS на Rust). В клиент-серверном включи отрисовку серверной и предсказанной позиции; в роллбэке — счётчик rollback frames и предсказанный ввод. Покрути искусственный лаг и сравни поведение.
🧪 Потестить (глазами QA) ~15 мин
Спровоцируй сигнатурный артефакт каждой модели: rubber-band и «смерть за углом» (клиент-сервер, net_fakelag в Source), input delay и «вся игра ждёт» (локстеп RTS на плохой сети), визуальный «снап» (роллбэк-файтинг). Чеклист: один артефакт = одна модель, не перепутай.
Чеклист: завёл клиент-серверный и роллбэк-сэмпл; увидел rollback-счётчик; сопоставил три артефакта трём моделям.
Связи
основа
Клиент-предсказание — детальный разбор первой модели (prediction + reconciliation + lag-comp). Этот урок — карта, тот — одна её область вглубь.
дальше
Роллбэк подробно — третья модель в деталях: предсказание ввода, откат, фрейм-буфер, делэй vs роллбэк.
дальше
Персистентность и шардинг — где живёт авторитетное состояние, когда игроков тысячи (шарды WoW vs один шард EVE + TiDi).
Вопросы пытливого ума
Почему просто не слать состояние всегда — это же не требует детерминизма?
Потому что трафик. Состояние растёт с числом сущностей: RTS на 1500 юнитов = 1500 позиций × частота снапшотов — не лезет ни в модем 28.8k (отсюда «1500 Archers»), ни даже в современный канал на больших масштабах. Команды/ввод весят считанные байты независимо от числа юнитов. Платишь за это детерминизмом. Поэтому RTS исторически выбрали локстеп: дешёвый канал важнее, чем избавление от детерминизма. А MMO/шутеры выбрали состояние, потому что им нужен авторитет против читов и произвольный контент, где детерминизм недостижим.
Если роллбэк = «оптимистичный локстеп», почему RTS не используют роллбэк, а файтинги — да?
Из-за размера состояния, которое надо сохранять каждый кадр для возможного отката. Файтинг — два бойца, ~10–50 КБ: сериализовать и откатить дёшево. RTS — тысячи юнитов, мегабайты состояния: сейв каждого кадра + пересимуляция нескольких кадров на каждый мисспредикт убьёт и память, и CPU. Поэтому RTS терпят input delay (не надо ничего сохранять/откатывать), а файтинги, где state крошечный и важен каждый кадр, идут в роллбэк ради нулевого input lag.
«Ждём самого медленного» в локстепе — это можно обойти?
Не полностью — это следствие модели. Раз кадр N+k исполняется одинаково у всех и только после того, как все прислали ввод на N, то один лагающий пир тормозит всех. Смягчают: больший input delay (буфер на джиттер, но больше лага всем), адаптивный turn delay по худшему пингу, «speed-step» (динамически растягивать длительность хода). Роллбэк — это и есть радикальное «не ждём»: предсказываем и откатываем. Но он не масштабируется на RTS-состояние (см. выше).
Почему детерминизм в локстепе «страшнее», чем в роллбэке, если оба его требуют?
Объём симуляции и цена ошибки. В роллбэке два игрока и крошечное состояние — детерминизм проверяется и держится легче, а мисспредикт всё равно откатывается. В локстепе тысячи взаимодействующих юнитов, и любое расхождение в одном из них (порядок флоат-операций, незасинхроненный rand()) → desync всей игры без шанса на коррекцию (нет авторитета, чтобы поправить). Поэтому RTS-движки одержимы fixed-point, детерминированным ГПСЧ, порядком команд и контрольными суммами состояния.
Кооп на 4 игрока (Helldivers, Deep Rock) — какая это модель?
Гибрид: клиент-сервер с ослабленным авторитетом, часто listen-server (один игрок хостит). Игроков мало, контекст социальный (друзья, не рейтинг), толерантность к лагу высокая, читы менее критичны → можно не платить за выделенные серверы и строгий авторитет. Это сознательный выбор в середине спектра: не полный авторитет MMO и не детерминированный P2P RTS, а «дёшево и достаточно хорошо». Минус — падает, если хост вышел.
Что почитать