Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 1, 2026

Replaces in-memory storage with PostgreSQL to persist game state and user data across server restarts. Games and player statistics now survive crashes and deployments.

Database Layer

  • Schema: users (discord_id, wallet, stats), games (uuid, state, jsonb data), game_players join table
  • Connection: Pooled connections (size=5, overflow=10) with retry logic and exponential backoff
  • Migrations: Alembic with initial schema migration, migrate.py helper script for common operations
  • Session management: New session per operation with proper try/finally cleanup to prevent leaks

Game Persistence

  • Serialization: Blackjack state → JSONB (deck, hands, positions, timing, waiting players)
  • Triggers: Persist after player actions (join/leave/hit/stand) and game tick events
  • Recovery: Load active games on startup, restore complete state, resubscribe to Redis channels
# Game state survives restart
game.serialize_state()  # → {deck: [...], players: [...], current_player_idx: 2, ...}
game.restore_state(data, players)  # ← Full reconstruction from DB

Player Statistics

  • Track games played/won/lost/tied per user
  • Update and persist stats at end_hand()
  • Discord ID for cross-session identity

Configuration

  • PostgreSQL service in all compose files with persistent volumes
  • Environment: POSTGRES_* variables or DATABASE_URL connection string
  • Fallback: USE_DATABASE=false for memory-only mode (backward compatible)

Docker & Docs

  • Added postgres:16-alpine to compose files
  • README: setup, migrations, backup/restore procedures
  • .env.example with all configuration options
Original prompt

Goal

Add PostgreSQL persistence to preserve all game state and user data across server restarts, replacing the current in-memory storage.

Requirements

1. Database Schema

Create a PostgreSQL database schema with the following tables:

users table:

  • id (primary key, serial)
  • discord_id (text, unique, not null) - Discord user ID
  • name (text, not null) - Player display name
  • wallet (decimal, default 0) - Player's money balance
  • games_played (integer, default 0)
  • games_won (integer, default 0)
  • games_lost (integer, default 0)
  • games_tied (integer, default 0)
  • created_at (timestamp with time zone, default now())
  • updated_at (timestamp with time zone, default now())

games table:

  • id (primary key, serial)
  • game_id (uuid, unique, not null) - The game UUID currently used in code
  • guild_id (bigint) - Discord guild ID
  • channel_id (bigint) - Discord channel ID
  • state (text, not null) - Game state: 'waiting', 'active', 'finished'
  • game_data (jsonb) - Serialized game state including:
    • current_player_idx
    • time_last_hand_ended
    • time_last_event
    • players_waiting (list of player names)
    • dealer_hand (list of cards)
    • deck (list of cards)
    • discards (list of cards)
  • created_at (timestamp with time zone, default now())
  • updated_at (timestamp with time zone, default now())

game_players table: (join table for active players in a game)

  • id (primary key, serial)
  • game_id (integer, foreign key to games.id)
  • user_id (integer, foreign key to users.id)
  • position (integer) - Order in the players list
  • hand (jsonb) - Current hand as list of cards
  • joined_at (timestamp with time zone, default now())

2. Database Connection Management

  • Add PostgreSQL connection configuration via environment variables:
    • DATABASE_URL (full connection string, e.g., postgresql://user:pass@host:port/dbname)
    • Or separate: POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD
  • Use psycopg2 or asyncpg for database connectivity
  • Implement connection pooling for efficiency
  • Add proper connection error handling and retries

3. Migration System

  • Create a simple migration system or use Alembic for schema migrations
  • Include an initial migration that creates all tables
  • Add a script or command to run migrations on startup or manually

4. Update Player Management

Modify cardgames/player.py:

  • Update PlayerRegistry to load/save players from PostgreSQL
  • Modify get_player() to query the database
  • Add methods to update player statistics after each hand
  • Ensure player wallet and stats are persisted

5. Update Game State Management

Modify cardgames/blackjack.py:

  • Add serialization methods to save complete game state to JSONB
  • Serialize: deck, discards, dealer hand, current_player_idx, timing info, players_waiting
  • Add deserialization/restoration methods to rebuild game state from database
  • Save game state after significant events (new hand, player action, hand end)

Modify cardgames/casino.py:

  • Update new_game() to persist game to database
  • Add method to load active games on startup
  • Update game state in database periodically or after key events

6. Update Bot Game Tracking

Modify bot.py:

  • Update BlackjackGame to store database game ID
  • Load active games from database on bot startup
  • Resume subscriptions to active games after restart

7. Game State Persistence Triggers

Automatically persist game state after:

  • New game created
  • Player joins/leaves
  • New hand starts
  • Player hits/stands
  • Hand ends
  • Game finishes

8. Startup Recovery

  • On server.py startup:
    • Load all active games from database
    • Reconstruct game objects with full state
    • Resume game loops
  • On bot.py startup:
    • Load active games for the bot's guilds
    • Resubscribe to game update channels

9. Statistics Tracking

Update cardgames/blackjack.py end_hand() method:

  • Track wins, losses, and ties
  • Update user statistics in database
  • Optionally: Store completed game history

10. Dependencies

Add to requirements.txt:

  • psycopg2-binary (or asyncpg if using async)
  • alembic (optional, for migrations)
  • sqlalchemy (optional, if using ORM approach)

11. Configuration

Add database configuration to compose files:

  • Add PostgreSQL service to compose.yml
  • Add environment variables for database connection
  • Update development compose files accordingly
  • Add volume for PostgreSQL data persistence

12. Documentation

Update README.md:

  • Document database setup requirements
  • Explain environment variables
  • Add instructions for running migrations
  • Document backup/restore procedures

Implementation Notes

  • Split into atomic commits where possible:
    1. Add database dependencies and configuration
    2. Create schema and migration system
    3. Implement database models/layer
    4. Update player persistence
    5. Update game state persistence...

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 5 commits January 1, 2026 06:01
- Add database dependencies: psycopg2-binary, alembic, SQLAlchemy
- Create database module with connection management and models
- Add Alembic migration system with initial schema
- Update Player class to support database fields (discord_id, stats)
- Update PlayerRegistry to load/save from PostgreSQL
- Add serialization methods to Blackjack for state persistence
- Update Casino to persist games and load on startup
- Update bot to load active games and resubscribe on restart
- Add PostgreSQL service to all Docker Compose files
- Update server.py to initialize database on startup

Co-authored-by: markrcote <131299+markrcote@users.noreply.github.com>
- Add comprehensive database documentation to README.md
- Document environment variables for PostgreSQL configuration
- Add migration, backup, and restore instructions
- Update development workflow documentation
- Fix flake8 linting issues (whitespace, variable scope)
- Update tests to disable database for unit testing
- Fix SQLAlchemy deprecation warning by using declarative_base

Co-authored-by: markrcote <131299+markrcote@users.noreply.github.com>
- Add migrate.py helper script for database management
- Create .env.example with all configuration options
- Update README with migrate.py documentation
- Fix SQLAlchemy deprecation by using orm.declarative_base
- Verify all imports and basic functionality work correctly

Co-authored-by: markrcote <131299+markrcote@users.noreply.github.com>
- Replace persistent session storage with proper session lifecycle
- Add session.close() in finally blocks to prevent connection leaks
- Remove manual updated_at assignment (handled by SQLAlchemy)
- Initialize global registry with USE_DATABASE environment variable
- Improve error handling for database operations
- Address all code review feedback

Co-authored-by: markrcote <131299+markrcote@users.noreply.github.com>
- Remove empty line in try block as flagged by code review

Co-authored-by: markrcote <131299+markrcote@users.noreply.github.com>
Copilot AI changed the title [WIP] Add PostgreSQL persistence for game state and user data Add PostgreSQL persistence for game state and player data Jan 1, 2026
Copilot AI requested a review from markrcote January 1, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants