Статус: Ready — РЕШЕНИЕ ЗАФИКСИРОВАНО 2026-06-19
Решение по сетевой архитектуре кооператива и его обоснование. Это главная развилка проекта по неткоду — здесь зафиксирован выбор, что из текущего кода keeper, что запарковано, и какая работа предстоит в Фазе MP.
Связано с 02. Детерминированная симуляция и тик, 6. Мультиплеер, 02. Архитектура реализации — Фаза 1 (Боевое ядро), 07. Техдолг, решения и changelog.
РЕШЕНИЕ
✅ Сетевая модель = host-authoritative + репликация состояния (Вариант A). ⛔ Детерминированный lockstep (Вариант B) — отклонён. Принято 2026-06-19 (Макс + анализ кода). Решающий фактор: игра — кооператив против ИИ (PvE), а текущая симуляция — на
float.
1. Простыми словами
Когда двое играют в один бой по сети, есть два способа держать их «в согласии»:
- Host-authoritative (выбрано). Бой считает только хост. Клиент — «тонкий экран»: получает позиции/HP юнитов по сети и рисует их, а свои действия (пауза, приказ) шлёт хосту. Один источник правды → рассинхрон невозможен в принципе.
- Lockstep (отклонено). Каждый игрок считает идентичный бой у себя; по сети летят только команды. Чтобы это работало, симуляции обязаны совпадать бит-в-бит на всех машинах.
2. Почему host-authoritative, а не lockstep
Решающий факт: это кооператив (PvE)
В соревновательном PvP нужна защита от читов и сокрытие задержки (каждый кадр критичен) — там lockstep оправдан (так делают RTS: StarCraft, AoE). У нас — кооп против ИИ: читы не угроза (вредишь только себе и друзьям), задержка в 100–200 мс на команду незаметна (юниты ходят сами, игрок изредка ставит юнита/жмёт паузу), игроков мало (до 4), юнитов — десятки.
Убийственный аргумент против lockstep: float
Lockstep требует, чтобы математика боя давала идентичный результат на всех машинах. Но вся симуляция — на float/Mathf/Vector2 (sqrt, normalize и т.п. разнятся между x86/ARM, Mono/IL2CPP, настройками FPU). Чтобы сделать lockstep честно, пришлось бы переписать всю боевую математику на fixed-point — самый большой возможный взрыв скоупа, ради бандвитча, который при 4 игроках нам не нужен.
Host-authoritative идёт по шерсти стека
NGO (Netcode for GameObjects) сделан для host-authoritative: NetworkVariable, NetworkTransform, RPC, дельта-сжатие. Lockstep на NGO — это плыть против течения.
Бандвитч — не проблема
~40 юнитов × (позиция ~4–8 б + HP ~2 б + флаги) ≈ 0.5 КБ/снапшот; при 15–20 Гц ≈ 8–13 КБ/с на клиента; на хост с 3 клиентами ≈ 25–40 КБ/с. Для брода — пыль. NGO ещё и дельта-сжимает простаивающих юнитов.
Сравнение
| Критерий | Host-authoritative (✅) | Lockstep (⛔) |
|---|---|---|
| Детерминизм между машинами | не нужен | обязателен бит-в-бит |
Текущий float-сим | подходит как есть | нужен рерайт на fixed-point |
| Сочетание с NGO | родное | против течения |
| Бандвитч | реплицируем состояние (для 4 игроков — пыль) | только команды (минимум) |
| Анти-чит / PvP | не нужен (кооп) | сильная сторона |
| Рассинхрон | невозможен by design | основной кошмар отладки |
| Реплеи | через запись состояния | бесплатно (команды+сид) |
3. Что это значит для существующего кода
Детерминизм не выбрасывается — он остаётся ценным для одиночных реплеев, отладки и «тот же сид → тот же бой». Просто перестаёт быть жёстким межмашинным требованием. XorShiftRng, тиковая модель, ComputeChecksum — всё остаётся.
| Кусок кода | Судьба | Почему |
|---|---|---|
CombatSimulation, все системы, эффекты, RNG, тик | Keeper | host считает этим же кодом |
ICombatCommand + очередь команд | Keeper | клиент шлёт интенты командами, хост применяет |
ComputeChecksum | Keeper (для SP) | тесты детерминизма, отладка реплеев |
NetworkCommandRelay (концепт intent→host) | Keeper, переработать обвязку | путь «клиент→хост» нужен; «все применяют на одном тике» — уберётся |
SimSyncProbe (сравнение checksum пиров) | ⛔ Запаркован → Net/_Parked/ | lockstep-инструмент, при host-auth второй симуляции нет |
FacepunchTransportBootstrap | Keeper | транспорт модель-независим |
Парковка
SimSyncProbe: файл перемещён вAssets/_Project/Scripts/Net/_Parked/и помечен баннером в коде. Не удалён намеренно — пригодится для SP-реплеев и как референс, если модель когда-нибудь пересмотрят. История в git.
4. Главная будущая таска: сетевой кооп (Фаза MP)
🎯 ГЛАВНАЯ ТАСКА НЕТКОДА. Реализуется в Фазе MP, не сейчас. В одиночке сетевой слой спит (NGO без
NetworkManagerнеактивен), поэтому он ничего не ломает и не требует немедленной работы.
Состав работы host-authoritative реализации:
- Репликация состояния юнитов хост→клиент через NGO (позиция, HP, щит, ключевые флаги; квантование + дельта-сжатие; ~15–20 Гц + интерполяция на клиенте).
- Клиент рендерит из реплик-состояния, а не из локального сима. Сейчас
CombatPresenterчитает живойCombatSimulation; нужен шов, где на клиенте источник — реплицированное состояние. Это главный архитектурный кусок. - Гейт тика: только хост (authority) гоняет
CombatLoopService.Tick. Сейчас гейтаIsServerнет — добавить при включении сети. - Релей команд для полного набора ввода (не только Pause/Resume): постановка юнита, способности, предметы. Переработать
NetworkCommandRelay: оставитьclient → ServerRpc(intent) → host enqueue, убрать lockstep-broadcast «все на одном тике». - Синхронизация паузы в кооп-планировании (любой ставит — у всех пауза; приказы во время паузы копятся и применяются на резюме).
- Late-join / реконнект: полный снапшот состояния присоединившемуся (NGO частично делает).
- Сид боя от хоста (сейчас
GenerateBattleSeedлокальный — для одиночки ок, для сети сид должен прийти в команде старта).
Связь с дизайн-доком 6. Мультиплеер и общим планом 7. Флоу забега и сессия (реконнект, репликация RunState).
5. Когда бы lockstep был правильным (честный контр-аргумент)
Чтобы решение было осознанным, а не «на веру»: lockstep выиграл бы, если бы это был соревновательный автобатлер с сотнями/тысячами юнитов и реплеями/анти-читом как фичами ядра. Тогда стоимость fixed-point-рерайта окупилась бы минимальным трафиком и бесплатными реплеями. Это не наш случай — поэтому host-authoritative.
Итог документа
- Решение: host-authoritative (Вариант A). Lockstep отклонён.
- Причины: кооп-PvE (мягкие требования) +
float-сим (несовместим с lockstep без рерайта) + NGO заточен под host-auth + бандвитч не проблема. - Детерминизм остаётся для SP (реплеи/тесты/дебаг), перестаёт быть межмашинным требованием.
- Keeper: сим, команды, checksum-для-SP, транспорт. Запарковано:
SimSyncProbe. Переработать обвязку:NetworkCommandRelay. - Главная будущая таска — реализация host-authoritative репликации в Фазе MP (см. §4).