bugfixes and refactoring in game-server

This commit is contained in:
2026-10-27 15:55:48 +03:00
parent 3629411030
commit 31ef12b5f2
9 changed files with 732 additions and 66 deletions
+22 -13
View File
@@ -53,10 +53,10 @@ JWT_SECRET=... PUBLIC_ADDR=ws://localhost:8081/ws ./server
Проверки при JOIN:
- подпись (HS256) и срок действия обоих токенов;
- подпись (HS256) и срок действия обоих токенов (при первом подключении к партии);
- `game_server` строго равен `PUBLIC_ADDR` этого сервера;
- `player1_id != player2_id`;
- `user_id` из пользовательского токена совпадает с `player1_id` или
- `player1_id > 0` и `player2_id > 0`, а также `player1_id != player2_id`;
- `user_id > 0` из пользовательского токена совпадает с `player1_id` или
`player2_id` игрового токена;
- партия не находится в реестре завершённых (см. ниже).
@@ -108,19 +108,24 @@ JWT_SECRET=... PUBLIC_ADDR=ws://localhost:8081/ws ./server
| Код | Значение |
|------|----------|
| 1000 | нормальное завершение (партия окончена, END уже отправлен) |
| 1001 | сервер завершает работу (graceful shutdown) |
| 4000 | соединение заменено новым соединением того же игрока |
| 4001 | первое сообщение не JOIN |
| 4002 | сообщение не соответствует формату |
| 4003 | недействительный игровой токен / чужой `game_server` |
| 4004 | недействительный пользовательский токен / игрок не участник партии |
| 4003 | недействительный игровой токен / чужой `game_server` / неположительные id |
| 4004 | недействительный пользовательский токен / игрок не участник партии / неположительный id |
## Обработка краевых случаев
- **Переподключение.** Игрок идентифицируется токенами, а не соединением.
Новое JOIN от того же игрока закрывает старое соединение (код 4000) и
занимает его слот; очередь неподтверждённых сообщений переживает
разрыв и переотправляется. Одновременно существует не более одного
соединения на игрока.
Игровой токен запоминается в структуре матча при первом подключении:
повторное подключение с тем же игровым токеном полностью пропускает
его валидацию (включая проверку срока действия). Новое JOIN от того же
игрока закрывает старое соединение (код 4000) и занимает его слот;
очередь неподтверждённых сообщений переживает разрыв и переотправляется.
Одновременно существует не более одного соединения на игрока.
- **Повторный JOIN.** Если клиент присылает повторный JOIN на уже
установленном соединении, такое сообщение просто игнорируется без ошибки.
- **Повторный JOIN после завершения партии.** Завершённые партии хранятся
в реестре (`match_id` -> END-сообщения обоих игроков) до истечения срока
действия игрового токена. Поздний JOIN получает в ответ сохранённое END
@@ -131,8 +136,13 @@ JWT_SECRET=... PUBLIC_ADDR=ws://localhost:8081/ws ./server
токена: после истечения токена второй игрок всё равно не сможет войти.
- **JOIN с токеном, указывающим на другой сервер** (неверный
`game_server`) отклоняется с кодом 4003.
- **`player1_id == player2_id`** отклоняется (код 4003) — иначе один игрок
мог бы занять оба слота.
- **`player1_id == player2_id`** или неположительные ID отклоняются (код 4003).
- **Валидация ACK.** Поле `seq` от клиента проверяется на положительность и
ненахождение за пределами последнего отправленного `seq`. Некорректный `seq`
отклоняется с ошибкой.
- **Асинхронная отправка.** Запись сообщений в WebSocket делегирована
отдельной per-connection горутине, что предотвращает блокировку мьютексов
менеджера во время write timeout.
- Неизвестные опкоды после JOIN игнорируются (совместимость с будущими
версиями протокола); мусорные/не-JSON сообщения логируются и пропускаются.
- Keepalive: сервер пингует соединение каждые 25 с; размер входящего
@@ -166,5 +176,4 @@ go test -race ./...
- завершение партии по итогам игры с персонифицированными кодами
победы/поражения;
- репорт итогов партии на main-server (реестр завершённых партий уже
гарантирует, что партия завершается ровно один раз);
- очистка состояния начавшихся партий после их завершения.
гарантирует, что партия завершается ровно один раз).