Пошаговая игра после обрыва: как не повторить принятый ход
В пошаговой онлайн-игре переподключение должно восстанавливать принятое сервером состояние партии и судьбу незавершённой команды. Если сервер уже принял ход, а подтверждение потерялось, повтор отправки не должен создавать второй ход. Для этого нужны идентификатор команды, сохранённое решение и согласованная синхронизация, а не только восстановленный WebSocket.
Момент, в котором игрок не знает результата
- Игрок выбирает действие, клиент присваивает команде идентификатор и отправляет её в конкретную партию.
- Сервер проверяет игрока, состояние и правила, затем фиксирует принятое решение.
- Подтверждение направляется клиенту, но соединение обрывается до его получения.
- Игрок возвращается и видит прежний локальный экран без уверенности, был ли ход принят.
- Приложение должно выяснить судьбу прежней команды и текущее состояние, прежде чем разрешать следующее действие.
Это учебная последовательность, а не описание конкретного игрового сервера. Она показывает, почему отсутствие подтверждения не равнозначно непринятому ходу. Клиент не должен решать этот вопрос по анимации кнопки или тому, осталась ли команда в памяти вкладки. Решение должно сопоставляться с состоянием партии на стороне приложения.
Соединение и партия восстанавливаются на разных уровнях
В документации Socket.IO о доставке отдельно описано поведение по умолчанию при потере связи и необходимость дополнительных гарантий в приложении. А Connection State Recovery не обещает успешное восстановление во всех случаях и требует обработки повторной синхронизации. Эти возможности транспорта не заменяют правила принятия игрового хода.
Если выбран другой транспорт, проверьте его контракт отдельно. Для игры всё равно нужен ответ на прикладной вопрос: какую команду какой участник подал и какое решение по ней уже зафиксировано. Сам факт нового подключения не означает, что клиент получил все пропущенные события или вправе продолжить из прежнего локального состояния.
Таблица моментов разрыва
| Где произошёл обрыв | Что известно серверу | Что может знать клиент | Действие после возвращения | Проверяемый итог |
|---|---|---|---|---|
| До получения команды | Ход не зафиксирован | Отправка могла начаться | Сверить состояние и судьбу команды по принятому протоколу | Нет выдуманного принятого хода |
| После фиксации, до подтверждения | Есть решение по команде | Результат неизвестен | Получить ранее принятое решение без нового применения | Ход учтён один раз |
| После подтверждения, до обновления экрана | Состояние уже изменено | Локальное отображение могло отстать | Восстановить актуальные разрешённые данные | Экран соответствует принятой партии |
| Пока ходил другой участник | Есть более новое состояние | Старая версия партии | Синхронизировать доступное состояние и право хода | Старый экран не разрешает недопустимое действие |
| Во время окончания времени | Требуется решение по правилам таймера | Локальный отсчёт ненадёжен | Получить принятое сервером состояние и причину | Переподключение не создаёт случайную добавку времени |
Сохраняйте решение команды вместе с изменением партии
Мы советуем привязывать команду к партии, участнику и ожидаемой версии состояния. Идентификатор создаётся до первой отправки и сохраняет смысл при предусмотренном повторе. Новый идентификатор для той же неизвестной попытки не помогает выяснить судьбу старой. Приёмка должна проверить именно этот спорный момент, а не только повторное нажатие после явного успеха.
На сервере нужно согласовать целостность решения: когда ход считается принятым и где это можно проверить повторно. Два одновременно пришедших экземпляра не должны оба увидеть отсутствие результата и применить действие. Конкретное решение зависит от хранилища и архитектуры, но его результат должен быть наблюдаемым в контрольной партии.
Для отклонённого хода тоже нужен понятный ответ. Если состояние уже изменилось или право действия перешло другому игроку, приложение не должно автоматически трактовать возврат соединения как разрешение повторить всё заново. Покажите принятое решение и допустимый следующий шаг. При этом повтор с тем же идентификатором не следует использовать для другой по смыслу команды.
Что получает игрок при возвращении
Синхронизация должна дать текущую версию партии, доступное этому участнику содержимое и право действия. Не отправляйте игроку закрытую информацию соперника просто потому, что для отладки удобен полный серверный снимок. Представление для восстановления должно соблюдать те же игровые границы, что обычная выдача.
Можно использовать снимок состояния и предусмотренные события, но правила их совмещения надо определить заранее. Старое событие не должно повторно применяться поверх более нового снимка. Мы советуем обозначать версию и проверять порядок на согласованных учебных данных. При неудаче восстановления интерфейс лучше оставить в состоянии синхронизации, чем сразу разрешить ход из сомнительного экрана.
Таймер не должен зависеть от вкладки
Выберите игровое правило: продолжается ли время при разрыве, есть ли согласованная пауза и как учитывается попытка, поступившая у границы срока. Это решение владельца игры. Его нельзя оставлять побочным эффектом сетевого тайм-аута или локального счётчика браузера. После возвращения игрок получает принятое состояние времени, а не новый отсчёт по умолчанию.
Отдельно задайте момент, по которому принимается своевременность команды. Получение запроса и завершение его обработки могут происходить не одновременно. Для конкурентной игры это нужно согласовать и проверить на пограничных условиях. Статья не задаёт универсального правила победы: она требует сделать выбранное правило устойчивым к разрыву и повтору.
Как принять механику без случайной демонстрации
- Создайте учебную партию двух участников и запишите начальное состояние, правила хода и времени.
- Проверьте разрыв до получения команды и отдельно после её фиксации до подтверждения. Восстановление должно различать эти случаи по данным приложения.
- Повторите передачу одной команды по разрешённому сценарию. Подтвердите единственное рабочее изменение и одинаково понятный результат.
- Дайте второму участнику выполнить допустимое действие, пока первый отсутствует. После возвращения проверьте версию состояния и разрешённое право хода.
- Испытайте повторное подключение и обновление страницы у границы времени. Приёмка должна показать выбранное игровое правило без случайного продления.
- Проверьте недоступность восстановления и обычный выход из партии. У пользователя должны остаться понятные состояния ожидания, отказа и возвращения.
Проведение таких испытаний относится к собственной согласованной среде. Наблюдения сохраняйте по командам и версиям без лишних данных игроков. Для заказа можно использовать разработку многопользовательской игры, общий объём — разработку игр, а отдельную проверку производительности — материал о времени кадра браузерной игры. Восстановленный ход подтверждается состоянием партии, а не только исчезнувшим значком разрыва связи.
Вопросы и ответы
Что восстановить после socket.io reconnect в пошаговой игре?
Помимо соединения нужны актуальное доступное состояние партии и судьба команды с неизвестным результатом. Транспортное восстановление не подтверждает, что все игровые решения уже согласованы с клиентом.
Можно ли разрешать новый ход сразу после reconnect event?
Сначала приложение должно подтвердить версию партии и право действия участника. Иначе прежний локальный экран может позволить действие, которое уже не соответствует серверному состоянию.
Должен ли reconnect timeout останавливать игровой таймер?
Это не должно получаться случайно из настройки транспорта. Владелец игры выбирает правило времени и приёмки команды у границы срока, а приложение воспроизводимо применяет его после разрыва.