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 может навредить игровому опыту (создать видимые артефакты или нарушить честность)?