850: Пакет/Игровой Сервис
- Работает на:
- Совместим с:
Автономный, независящий от транспорта бэкенд-сервис, предназначенный для управления жизненным циклом пошаговых игр. Он действует как авторитетный сервер для состояния игры, обрабатывая действия игроков, развивая логику через подключаемые движки и сохраняя результаты.
Игровой Сервис (@idealic/game-service) — это модульный Node.js бэкенд, созданный для валидации и хостинга пошаговых игр. Изначально он был ориентирован на покер, но его архитектура является универсальной и служит безопасной песочницей для разработки и тестирования игровой логики в отрыве от проприетарной платформы клиента.
Основная архитектура
Сервис построен на модели API без сохранения состояния (Stateless API). Он взаимодействует с внешним миром через единую конечную точку, которая получает клиентскую версию состояния игры. Сервер объединяет её со своим авторитетным состоянием, проверяет и обрабатывает действия, а затем возвращает обновление.
Подключаемые игровые движки
Сервис строго отделён от игровой логики. Он функционирует как контейнер-хост для подключаемых движков.
- Паттерн Реестра: Сервис использует реестр для загрузки различных игровых движков.
- Единый интерфейс: Движки должны предоставлять стандартный интерфейс (
State.advance(),State.join()и т.д.). - Разделение ответственности:
- Игровой движок (например, покер): Отвечает за правила, валидацию и переходы состояний.
- Игровой Сервис (этот пакет): Управляет «метаигровой» логикой, такой как сетевое взаимодействие, сохранение данных, управление сессиями и таймаутами.
«Состояние» как источник истины
Сервис полагается на концепцию объекта состояния от базового движка. Это не просто снимок, а полная, сериализуемая запись истории (подобно шахматной нотации).
- Обмен данными без сохранения состояния: Клиенты и серверы обмениваются полным объектом состояния.
- Детерминизм: Состояние включает в себя случайное начальное число (random seed), что позволяет идеально восстановить историю игры для проверки и разрешения споров.
- Перспектива игрока: Состояние поддерживает «маскировку», при которой сервер генерирует персонализированное представление для каждого игрока (скрывая карманные карты противников) перед отправкой данных.
Слой абстракции ввода-вывода (I/O)
Сервис не зависит от транспорта. Все внешние взаимодействия абстрагированы в выделенный слой ввода-вывода (service.io.ts), что позволяет интегрировать сервис в любую среду (Express, WebSockets, Serverless) путем замены заглушек.
- Сохранение состояния:
saveGameиloadGameотвечают за хранение авторитетного состояния. - Управление сессиями:
fetchPlayerStacksиsavePlayerStacksинтегрируются с внешними системами кошельков. - Обмен данными в реальном времени:
broadcastToPlayersотвечает за отправку обновлений состояния клиентам (например, через WebSockets). - Фоновые процессы:
fetchTimedOutGamesпозволяет опрашивать сервис для контроля времени хода игроков.
Концепция сервиса столов
Сервис реализует модель сервиса столов, где столы — это не статические записи в базе данных, а динамически определяемые активные состояния игры.
- Динамическое выделение: Столы создаются по требованию. Если игрок ищет игру (например, «Техасский Холдем, $1/$2») и за столами нет свободных мест, создается новое состояние игры.
- Жизненный цикл:
- Поиск: Клиент запрашивает стол с определёнными параметрами. Система находит существующую игру или создаёт новую.
- Наблюдение: Игрок получает состояние игры, чтобы наблюдать за ходом действия.
- Присоединение: Игрок отправляет действие
join, чтобы занять место. - Игра: Игровой цикл продолжается.
- Завершение: Игроки удаляются по таймауту или из-за банкротства (недостаточно средств для следующей раздачи).
Как это работает
Операционный цикл сервиса выглядит так:
- Инициация: Внешнее событие (действие игрока) поступает в сервис.
- Загрузка: Авторитетное состояние загружается из хранилища.
- Слияние: Входящее клиентское состояние объединяется с состоянием сервера.
- Продвижение: Сервис вызывает
advance(), который циклически выполняет логику игрового движка до тех пор, пока не потребуется ввод от игрока. - Завершение: Если раунд заканчивается, сервис завершает раздачу, распределяет банки и начинает следующий раунд.
- Сохранение и рассылка: Обновлённое состояние сохраняется и отправляется всем игрокам.