Files
akadmin e9f5377e0a Phase 5: Complete Core Game Engine implementation
- Added MTGOnlineGame, GamePlayer, GameHistory models
- Implemented full game CRUD endpoints (create, list, get, join, leave, start, end)
- Integrated WebSocket game server with database persistence
- Added Commander deck validation service (100-card min, color identity, ban list)
- Created Alembic migration 006 for new game tables
- All game endpoints now database-backed (no more stubs)
2026-08-27 05:29:38 +00:00

118 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Add game models: mtgonline_games, game_players, game_history
Revision ID: 006
Revises: 005
Create Date: 2026-01-05 00:00:00.000000
This migration creates tables for the Core Game Engine:
- mtgonline_games: Active game sessions
- game_players: Player associations with games
- game_history: Historical game records
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '006'
down_revision: Union[str, None] = '005'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Create game engine tables."""
# ------------------------------------------------------------------
# 1. MTG Online Games Table (mtgonline_games)
# Mirrors: app/models/models.py – class MTGOnlineGame
# ------------------------------------------------------------------
op.create_table(
'mtgonline_games',
sa.Column('id', sa.Integer(), primary_key=True, index=True),
sa.Column('room_id', sa.Integer(),
sa.ForeignKey('mtgonline_rooms.id'),
nullable=False, index=True),
sa.Column('host_id', sa.Integer(),
sa.ForeignKey('mtgonline_users.id'),
nullable=False, index=True),
sa.Column('game_type', sa.String(50), nullable=False,
server_default='Commander'),
sa.Column('state', sa.String(20), nullable=False,
server_default='WAITING'),
sa.Column('is_password_protected', sa.Boolean(),
nullable=False, server_default='false'),
sa.Column('max_players', sa.Integer(), nullable=False,
server_default='4'),
sa.Column('current_players', sa.Integer(), nullable=False,
server_default='0'),
sa.Column('created_at', sa.DateTime(),
server_default=sa.func.now()),
sa.Column('started_at', sa.DateTime(), nullable=True),
sa.Column('finished_at', sa.DateTime(), nullable=True),
)
# ------------------------------------------------------------------
# 2. Game Players Table (game_players)
# Mirrors: app/models/models.py – class GamePlayer
# ------------------------------------------------------------------
op.create_table(
'game_players',
sa.Column('id', sa.Integer(), primary_key=True, index=True),
sa.Column('game_id', sa.Integer(),
sa.ForeignKey('mtgonline_games.id', ondelete='CASCADE'),
nullable=False, index=True),
sa.Column('user_id', sa.Integer(),
sa.ForeignKey('mtgonline_users.id'),
nullable=False, index=True),
sa.Column('seat_number', sa.Integer(), nullable=True),
sa.Column('life_total', sa.Integer(), nullable=False,
server_default='20'),
sa.Column('is_ready', sa.Boolean(), nullable=False,
server_default='false'),
sa.Column('is_host', sa.Boolean(), nullable=False,
server_default='false'),
sa.Column('joined_at', sa.DateTime(),
server_default=sa.func.now()),
)
# Unique constraint + indexes
op.create_unique_constraint(
'uq_game_player_unique', 'game_players',
['game_id', 'user_id'])
op.create_index('idx_game_players_game', 'game_players', ['game_id'])
op.create_index('idx_game_players_user', 'game_players', ['user_id'])
# ------------------------------------------------------------------
# 3. Game History Table (game_history)
# Mirrors: app/models/models.py – class GameHistory
# ------------------------------------------------------------------
op.create_table(
'game_history',
sa.Column('id', sa.Integer(), primary_key=True, index=True),
sa.Column('game_id', sa.Integer(),
sa.ForeignKey('mtgonline_games.id'),
nullable=True),
sa.Column('room_id', sa.Integer(),
sa.ForeignKey('mtgonline_rooms.id'),
nullable=True, index=True),
sa.Column('game_type', sa.String(50), nullable=False,
server_default='Commander'),
sa.Column('winner_id', sa.Integer(),
sa.ForeignKey('mtgonline_users.id'),
nullable=True, index=True),
sa.Column('duration_seconds', sa.Integer(), nullable=True),
sa.Column('result_type', sa.String(50), nullable=True),
sa.Column('replay_data', sa.JSON(), nullable=True),
sa.Column('created_at', sa.DateTime(),
server_default=sa.func.now()),
)
def downgrade() -> None:
"""Drop game engine tables in reverse dependency order."""
op.drop_table('game_history')
op.drop_table('game_players')
op.drop_table('mtgonline_games')