Real-Time Backend

Multiplayer Game Networking

Один игрок видит врага за углом и стреляет. Другой игрок уже ушёл за угол. Кто попал? Ответ зависит от того, как сервер обрабатывает latency - и именно это отличает Valorant от lag-riddled инди-шутеров.

  • **Valorant** (Riot Games) использует server-authoritative модель с 128 Hz tick rate и lag compensation до 200 мс. Сервер реконструирует прошлое состояние мира на момент выстрела и проверяет попадание в историческую позицию цели.
  • **Overwatch** (Blizzard) применяет client prediction для всех способностей и движения. Reconciliation обрабатывается в каждом server tick (64 Hz). Известный кейс: «Genji dash» часто выглядел иначе у разных игроков из-за агрессивного предсказания анимаций.
  • **Fortnite** синхронизирует state 20-30 Hz с delta compression и AoI фильтрацией для 100 игроков на карте. Постройки (уникальная механика) требуют отдельного state-канала с higher priority из-за критичности для геймплея.
  • **Apex Legends** использует собственный netcode (Evolution Engine) с client prediction и adaptive tick rate - при высоком пинге частота обновлений снижается, prioritizируя accuracy над частотой.

Client Prediction: локальный симулятор

При latency 100 мс игрок нажимает "вперёд" - и персонаж двигается через 100 мс. Это неприемлемо. **Client Prediction** решает проблему: клиент немедленно применяет входные данные локально, не ожидая подтверждения от сервера. Персонаж движется мгновенно, а сервер позже подтверждает или корректирует позицию.

Valve реализовала client prediction в Half-Life/Counter-Strike в 1999 году - это сделало шутеры с 100мс+ пингом играбельными. Современные шутеры (Valorant, Apex Legends, Overwatch) используют тот же принцип с нюансами в реализации физики и коллизий.

Предсказание работает только для детерминированной физики: одни и те же входные данные с тем же deltaTime дают одну и ту же позицию на клиенте и сервере. Любая недетерминированность (float rounding, random без сида) разрушает reconciliation.

Зачем клиент сохраняет очередь pendingInputs после отправки на сервер?

Server Reconciliation: исправление ошибок предсказания

Клиент предсказывает позицию, но сервер - авторитетный источник истины. Когда сервер возвращает авторитетное состояние (например, другой игрок стоял на пути), клиент должен исправить своё предсказание. Этот процесс называется **server reconciliation**.

Reconciliation без корректной реализации выглядит как телепортация: персонаж прыгает назад. Правильная реализация: получив ack на тик N, клиент откатывается к авторитетной позиции с тика N и переигрывает все несмотренные входные данные (N+1 до current). Если физика детерминирована - результат совпадёт с тем, что сервер отправит в следующем update.

В Overwatch reconciliation происходит каждый server tick (64 Hz). Среднее расхождение при 50 мс пинге - менее 2 пикселей. Если расхождение больше порогового значения (читерство или серьёзный рассинхрон) - сервер может форсировать телепорт.

Что произойдёт, если клиент получит server update с ackedTick, который уже был удалён из pendingInputs?

Interpolation: плавное движение других игроков

Client prediction применяется к локальному игроку. Для **других** игроков данных нет - сервер присылает обновления дискретно (20-64 раза в секунду). Без обработки движение выглядит как прыжки между позициями. **Entity Interpolation** сглаживает движение: клиент отображает позицию из прошлого (с задержкой 50-100 мс) и интерполирует между двумя известными серверными позициями.

Interpolation delay - это намеренная задержка рендеринга. Valve Source Engine использует 100 мс по умолчанию (cl_interp 0.1). Снижение до 50 мс ускоряет отображение, но при потере пакетов появляются телепортации. Counter-Strike 2 позволяет игрокам настраивать этот параметр.

Почему interpolation намеренно отображает прошлое состояние (с задержкой 50-100 мс), а не экстраполирует будущее?

Game State Synchronization: delta compression и priority

Сервер не может отправлять полный game state каждый тик - это слишком много данных. Battlefield 2042 имеет 128 игроков на карте: полный state включает позицию, ориентацию, состояние здоровья, анимации, предметы для каждого. При 64 Hz это тысячи объектов * 60 полей * 64 раза в секунду.

  • **Delta compression**: отправлять только изменения от последнего ACK-состояния, а не полный snapshot
  • **Area of Interest (AoI)**: не отправлять данные об игроках, которых клиент не видит (за пределами зоны видимости)
  • **Priority-based update**: ближайшие враги обновляются на 64 Hz, дальние - на 20 Hz, статичные объекты - только при изменении
  • **Bit-packing**: позиция кодируется в 24 бита вместо 96 (3x float), угол поворота - в 8-16 бит
  • **Snapshot interpolation**: сервер сохраняет историю состояний для lag compensation

Для честной игры сервер должен отправлять всем игрокам одинаковый game state

Сервер намеренно отправляет разным игрокам разные данные (AoI фильтрация) - это снижает читерство и трафик одновременно

Если клиент получает позиции всех игроков, включая скрытых за стеной - wallhack чит элементарен. AoI-фильтрация на сервере делает wallhack невозможным и уменьшает bandwidth на 60-90%. Valve реализовала это в CS:GO в 2019 году (Trusted Mode + AoI)

Что такое Area of Interest (AoI) в контексте game state synchronization?

Итоги

  • **Client Prediction** делает локальное управление мгновенным: клиент симулирует физику немедленно и хранит историю inputs для последующей коррекции
  • **Server Reconciliation** устраняет расхождение предсказания с авторитетным состоянием: при получении server ACK клиент откатывается и переигрывает непризнанные inputs
  • **Entity Interpolation** сглаживает движение других игроков за счёт намеренной задержки рендеринга на 50-100 мс - точность важнее мгновенности для чужих позиций

Связанные темы

Multiplayer networking строится на фундаменте realtime-транспорта и специфичных алгоритмов:

  • Game Netcode — Следующий уровень: tick rate, lag compensation и rollback netcode как продолжение темы
  • UDP Transport — Весь game netcode строится поверх UDP - понимание потерь пакетов и джиттера критично
  • CRDT Structures — Альтернативный подход к синхронизации state без авторитетного сервера - используется в коллаборативных инструментах

Вопросы для размышления

  • Что произойдёт, если сделать физику недетерминированной (например, использовать float без фиксированной точки)? Как это повлияет на reconciliation?
  • Как бы изменилась архитектура для игры с 1000 игроками на одной карте? Какие оптимизации state sync станут критически важными?
  • В каких ситуациях client prediction может навредить игровому опыту (создать видимые артефакты или нарушить честность)?

Связанные уроки

  • net-14-udp
Multiplayer Game Networking

0

1

Войти