Роллбэк-нетокод (GGPO)
Механизм: предсказать, играть, откатиться
Роллбэк — это спекулятивное исполнение детерминированной симуляции. Оба пира знают: одинаковый ввод на одинаковом кадре → побитово одинаковое состояние. На проводе — только биты ввода (см. таксономию). Проблема одна: ввод оппонента физически приходит с задержкой one-way. Делэй-нетокод решает её в лоб — ждёт ввод оппонента перед исполнением кадра (input delay всем, всегда). Роллбэк не ждёт.
Цикл кадра
Каждый кадр F клиент делает:
- читает свой ввод и применяет его сразу;
- предсказывает ввод оппонента — обычно «повтори последний известный» (в файтинге игрок чаще держит или ничего не жмёт, чем меняет ввод каждый кадр — прогноз верен в большинстве кадров);
- продвигает симуляцию на кадр вперёд;
- сохраняет состояние кадра
Fв кольцевой буфер.
Когда по сети приходит настоящий ввод оппонента за прошлый кадр F′:
- совпал с предсказанием → прогноз был верен, симуляция уже правильная, делать нечего;
- не совпал → откат: загрузить сохранённое состояние кадра
F′, подставить верный ввод и пересимулировать все кадрыF′…Fза один текущий кадр. Игрок видит короткий «снап» на 1–3 кадра.
Окно отката и его цена
Насколько глубоко придётся откатываться — это число кадров «в полёте», пока ввод оппонента летит к тебе. При one-way задержке и кадре :
(RTT 100 мс → one-way ≈ 50 мс ≈ 3 кадра при 60 fps.) Значит каждый кадр клиент должен уметь сохранить состояние и при мисспредикте пересчитать до кадров в пределах одного бюджета кадра:
При =3 это «прогнать симуляцию ~4× за кадр» (откат на кадров + текущий = +1 шагов) — нормально, если один шаг дёшев. Отсюда два жёстких требования: (1) сейв-стейт каждого кадра должен быть дешёвым (сериализация всего игрового состояния), (2) шаг симуляции — дёшев и детерминирован. В файтинге состояние крошечное (~10–50 КБ: два бойца, снаряды, таймеры) → сохранять и откатывать копейки. В RTS состояние — мегабайты тысяч юнитов → сейв каждого кадра и пересимуляция убьют и память, и CPU, поэтому RTS берут делэй (локстеп), а не роллбэк.
Делэй + роллбэк: одна ручка
Чистый роллбэк предсказывает всё окно . Можно добавить немного input delay кадров (свой ввод применяется не сразу, а через ) — тогда горизонт предсказания сокращается:
Меньше горизонт → реже и короче откаты → меньше визуальных «снапов», но добавляется постоянный input lag кадров всем. Это спектр между «делэй» ( большой, нет откатов, ровный лаг — как в локстепе) и «чистый роллбэк» (=0, нулевой свой лаг, максимум откатов). Хорошие реализации дают 1–3 кадра делэя как буфер на джиттер. Сравни: SFV ославился «восемью кадрами делэя» — это чистый делэй, без роллбэка, и потому ощущался вязким.
🕹 В какие игры поиграть — и что заметить
Роллбэк лучше всего «слышен» на контрасте с делэй-нетокодом: одна сигнатура — короткий рывок (снап), другая — ровный input lag и фризы. Сыграй по нарастающей и поймай разницу.
Первый крупный консольный файтинг с роллбэком (Double Helix → Iron Galaxy, GGPO-стиль, при участии автора GGPO). Доказал, что роллбэк работает на консоли в проде — и задал моду на жанр.
🎮 Сыграй: KI на Xbox/PC онлайн на среднем пинге — заметь, что свои комбо идут без запинки даже когда у оппонента скачет связь; «лагает» не твоё управление, а короткие подёргивания картинки оппонента. Это и есть откат вместо input lag.
Инди-файтинг прямо на GGPO; годами — витрина того, как должен ощущаться роллбэк. Маленькое состояние, чистый детерминизм, отличный онлайн на «плохой» сети.
🎮 Сыграй: Skullgirls онлайн и оффлайн подряд — разницы в отклике почти нет (в этом вся суть). На скачке пинга лови микро-«телепорт» оппонента на 1–3 кадра: это пересимуляция после мисспредикта.
Все трое — роллбэк из коробки (2021–2023). К началу 2020-х комьюнити отказывалось покупать файтинги без роллбэка; давление + open-source GGPO сделали его индустриальным стандартом.
🎮 Сыграй: в GGST/SF6 включи индикатор соединения (полоски/число кадров делэя) и сыграй на разном пинге. Заметь: при росте пинга растёт частота мелких снапов, но свой отклик не вязнет. Сравни с офлайном — почти неотличимо.
Обратный полюс. Smash Ultimate — делэй-нетокод (Сакураи: «побочки слишком велики», от роллбэка отказались; в 2020-м комьюнити взорвалось хэштегом #FixUltimateOnline). SFV запомнился «8 кадрами делэя». Сигнатура — не снапы, а плавающий input lag и фризы, когда ввод оппонента не успел.
🎮 Сыграй: Smash Ultimate онлайн на неидеальной сети — почувствуй, как само управление становится вязким и дёргается весь матч. Затем GGST — отклик мгновенный, артефакт другой (снап). А для контр-примера: Melee через Slippi (комьюнити-роллбэк поверх игры 2001 года) играет лучше «официального» делэя Ultimate.
Хардкор · инженерия: детерминизм, сейв-стейт и API GGPOможно пропустить
Роллбэк держится на побитовом детерминизме: одинаковый ввод на одинаковом кадре обязан давать побитово одинаковое состояние на обеих машинах — иначе пересимуляция разъедется (desync), и «согласованного» кадра не существует.
Что ломает детерминизм
- Float. Один и тот же код на разных CPU/компиляторах даёт разный результат (порядок операций, FMA, x87 80-бит vs SSE,
fast-math). Почти все роллбэк-движки берут fixed-point (целочисленную арифметику) для всей геймплейной симуляции. - Недетерминированный рандом. ГПСЧ с общим сидом и синхронным порядком вызовов; никакого «незаметного»
rand()в геймплее. - Неинициализированная память / хэш-итерация / указатели в состоянии. Сериализуемое состояние должно быть «плоским» и воспроизводимым.
API в трёх колбэках (форма GGPO)
GGPO абстрагирует сеть и просит у игры ровно три вещи:
save_game_state()— сериализовать всё геймплейное состояние в буфер (+ контрольную сумму);load_game_state(buf)— восстановить состояние из буфера (для отката);advance_frame(inputs)— продвинуть симуляцию на один кадр с заданным вводом.
Дальше GGPO сам предсказывает ввод, копит сейвы в кольцевом буфере, при приходе настоящего ввода зовёт load + многократный advance. Главный инструмент отладки — sync test: режим, где движок откатывается каждый кадр и сверяет контрольные суммы; любой источник недетерминизма всплывает сразу, а не «через 3 минуты матча».
Сколько хранить
Кольцевой буфер последних +запас состояний (обычно 7–8 кадров с запасом на джиттер). Сейв-стейт обязан быть быстрым: в файтинге это memcpy компактной структуры. Если состояние нельзя сделать дешёвым и плоским — роллбэк не ваш инструмент.
Хардкор · теория: почему «повтори последний ввод» — хороший предсказательможно пропустить
Качество роллбэка определяется частотой и глубиной откатов, а они — точностью предсказателя ввода оппонента. Наивная стратегия «оппонент на кадре сделал то же, что на » работает на удивление хорошо — и вот почему.
Статистика ввода файтинга
Геймплейный ввод сильно автокоррелирован: игрок держит направление, держит/отпускает кнопку, и меняет ввод лишь в редкие кадры (старт удара, смена направления). Если доля «кадров-изменений» равна , то на горизонте кадров вероятность хотя бы одного мисспредикта ≈ . При ≈0.1 и =3 это ~27% кадров с откатом — но каждый откат короткий (≤3 кадра) и почти всегда корректирует мелочь, поэтому визуально терпимо.
Граница худшего случая
Глубина отката ограничена сверху окном (нельзя откатиться дальше неподтверждённого ввода), значит и стоимость пересимуляции ограничена — алгоритм имеет жёсткий потолок работы за кадр. Чем выше пинг, тем больше : растёт и стоимость пересчёта, и длина «снапов». Поэтому роллбэк не «чинит» сколь угодно большой пинг — он делает малый пинг неотличимым от офлайна, а большой — играбельным, но с заметными рывками.
Почему откаты сходятся
Поскольку симуляция детерминирована, пересчёт с верным вводом даёт ровно то состояние, к которому позже придёт и оппонент. Откаты не накапливают ошибку: каждый «согласованный» кадр — общая истина обоих пиров, от которой отсчитывается следующее предсказание.
ML / AI: speculative decoding в инференсе LLM — это роллбэк дословно. Маленькая draft-модель предсказывает следующие токенов; большая модель проверяет их за один параллельный forward-pass; совпавший префикс принимается, на первом расхождении — «откат» к этой позиции и продолжение от неё. Предскажи дёшево → исполни спекулятивно → проверь → отбрось и пересчитай хвост. Выигрыш тот же, что у роллбэка: прячем латентность за предсказанием, когда оно часто верное и проверка/откат дёшевы.
Процессоры: branch prediction + спекулятивное исполнение конвейера; мисспредикт → pipeline flush (откат спекулятивных стадий) и перезапуск с верной ветки. Ровно цикл «предскажи / исполни / проверь / откатись».
Базы данных: optimistic concurrency control (OCC) и MVCC — транзакция исполняется в предположении «конфликта нет», на коммите валидируется; конфликт → abort + retry (= откат). Выгодно ровно когда конфликты редки.
Фронтенд / UX: optimistic UI — рисуем результат действия сразу (лайк, отправка), сверяемся с сервером, при ошибке откатываем. Тот же визуальный «снап», что у роллбэка.
Принцип: если round-trip — это потолок, а предсказание дёшево и обычно верно, а откат дёшев — действуй сейчас и исправляйся. Окупается тем сильнее, чем реже мисспредикт и чем дешевле откатить.
Почему «повтори последний ввод» вообще работает — игрок же постоянно жмёт кнопки?
Если роллбэк так хорош, почему RTS не используют его, а делэй?
memcpy и пересчёт нескольких кадров копеечны. RTS — тысячи юнитов, мегабайты состояния: сейв каждого кадра + пересимуляция нескольких кадров на каждый мисспредикт убьют память и CPU. Поэтому RTS терпят input delay (детерминированный локстеп — ничего не сохраняем/не откатываем), а файтинги, где state крошечный и важен каждый кадр, идут в роллбэк ради нулевого своего input lag.Откат «снапает» картинку — почему это считается лучше ровного input lag?
Детерминизм в роллбэке «легче», чем в локстепе RTS — почему, если оба его требуют?
Роллбэк «убирает» пинг — значит, на 250 мс будет идеально?
- GGPO — исходники (
github.com/pond3r/ggpo, Tony Cannon, MIT с 9 окт 2019) +ggpo.net. Три колбэка и sync test — там. - Infil, «Fighting Game Netcode» guide — лучший популярный разбор роллбэка vs делэй (с анимациями).
- Mike Z (Lab Zero) — доклады/посты о роллбэке в Skullgirls на GGPO.
- GGRS (
github.com/gschup/ggrs) — Rust-порт; Photon Quantum — коммерческий детерминированный роллбэк-движок. - Модуль 4 (
04-online-worlds-1997-2005.md), раздел «Networking model taxonomy → Rollback».