Перейти к содержанию

Модель сессий и игр

Ключевая идея: Session ≠ Game

Session (долгоживущая)
├── invite_code: "ABCD12"
├── participants: [P1, P2, P3]
├── status: Lobby | GameSelection | InGame | Closed
└── game_instances: [
      { id: g1, plugin: dice_board, status: finished },
      { id: g2, plugin: dice_board, status: active }  ← только один active
    ]

Session lifecycle

stateDiagram-v2
    [*] --> Lobby: createSession
    Lobby --> GameSelection: hostOpensCatalog
    GameSelection --> InGame: startGame
    InGame --> GameSelection: gameEnds
    GameSelection --> InGame: switchGame
    InGame --> Paused: pause
    Paused --> InGame: resume
    Lobby --> Closed: close
    GameSelection --> Closed: close
    InGame --> Closed: close
Статус Описание
Lobby Участники присоединяются, host настраивает
GameSelection Выбор игры, ожидание старта
InGame Активная GameInstance
Paused Пауза (опционально Phase 2+)
Closed Сессия архивирована

GameInstance

Поле Тип Описание
id UUID
session_id UUID FK
plugin_id string e.g. dice_board
status enum active, finished, aborted
state JSONB GameState от плагина
current_player_id UUID Participant
winner_id UUID? После finished
started_at timestamp
finished_at timestamp?

Participant

Поле Описание
id UUID
session_id FK
user_id nullable — для registered
guest_id nullable — для guest
telegram_id nullable
role host, player, spectator
display_name string
presence_web bool + last_seen_at
presence_telegram bool + last_seen_at
control_channel web | telegram | null

Смена игры (switch)

  1. Host вызывает POST /sessions/{id}/games с новым plugin_id
  2. Предыдущая GameInstance → finished или aborted
  3. Новая GameInstance → active, initial_state() плагина
  4. Session остаётся InGame; invite_code не меняется
  5. Событие game.started → все клиенты

Инварианты

  • INV-001: не более одного active GameInstance на сессию
  • Участники сохраняются между играми
  • История game_instances append-only

Связанные FR

FR-004, FR-005, FR-033, FR-034