first commit
This commit is contained in:
921
docs/ARCHITECTURE.md
Normal file
921
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,921 @@
|
||||
# Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
**Microservices Architecture** - Three independent, deployable components:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Frontend Layer (2 options) │
|
||||
│ │
|
||||
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||
│ │ Public Web │ │ Godot Client │ │
|
||||
│ │ (Flask + Jinja2) │ │ (Native Game) │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ - Browser-based UI │ │ - Desktop/Mobile │ │
|
||||
│ │ - HTMX interactions │ │ - Cross-platform │ │
|
||||
│ │ - Server-side │ │ - GDScript │ │
|
||||
│ │ rendering │ │ │ │
|
||||
│ └──────────────────────┘ └──────────────────────┘ │
|
||||
│ │ │ │
|
||||
│ └──────────────┬───────────────┘ │
|
||||
│ │ │
|
||||
│ │ HTTP/REST │
|
||||
└──────────────────────────┼─────────────────────────────────┘
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ API Backend (Flask) │
|
||||
│ │
|
||||
│ Single Source of Truth - All Business Logic │
|
||||
│ - Authentication (via Appwrite) │
|
||||
│ - Game logic & mechanics │
|
||||
│ - Character management │
|
||||
│ - Combat system │
|
||||
│ - Marketplace transactions │
|
||||
│ - Session management │
|
||||
│ - Data models & validation │
|
||||
│ - AI orchestration │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────────┼──────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ Appwrite │ │ Redis + RQ │ │ AI APIs │
|
||||
│ │ │ │ │ │
|
||||
│ - Auth │ │ - Job Queue │ │ - Replicate │
|
||||
│ - Database │ │ - Caching │ │ - Anthropic │
|
||||
│ - Storage │ │ - Sessions │ │ │
|
||||
│ - Realtime │ │ │ │ │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
### Component Responsibilities
|
||||
|
||||
**`/api` - API Backend (Port 5000)**
|
||||
- **Role:** Single source of truth, all business logic
|
||||
- **Tech:** Flask REST API + Appwrite + RQ + Redis
|
||||
- **Contains:** Models, services, game logic, AI integration
|
||||
- **Deployment:** Independent with own venv, config, tests
|
||||
- **Location:** `/api` directory
|
||||
|
||||
**`/public_web` - Web Frontend (Port 5001)**
|
||||
- **Role:** Thin UI layer for browsers
|
||||
- **Tech:** Flask + Jinja2 + HTMX
|
||||
- **Contains:** Views, templates, static assets
|
||||
- **Communication:** HTTP requests to API backend
|
||||
- **Deployment:** Independent with own venv, config
|
||||
- **Location:** `/public_web` directory
|
||||
|
||||
**`/godot_client` - Game Client**
|
||||
- **Role:** Native cross-platform game client
|
||||
- **Tech:** Godot 4.5 + GDScript
|
||||
- **Contains:** UI, scenes, client-side logic
|
||||
- **Communication:** HTTP requests to API backend
|
||||
- **Deployment:** Exports to Desktop/Mobile/Web
|
||||
- **Location:** `/godot_client` directory
|
||||
|
||||
---
|
||||
|
||||
## Core Design Principles
|
||||
|
||||
1. **Microservices Architecture:** Three independent, deployable components (API, Web, Godot)
|
||||
2. **API as Single Source of Truth:** All business logic centralized in API backend
|
||||
3. **AI-Driven Narrative:** Claude models generate story, NPC dialogue, combat descriptions
|
||||
4. **Code-Driven Mechanics:** All game mechanics (damage, stats, effects) use deterministic formulas (JRPG-style)
|
||||
5. **Turn-Based Gameplay:** Classic D&D style, not real-time
|
||||
6. **Scalable Architecture:** Independent services, horizontal scaling ready
|
||||
7. **Security First:** Developer is cybersecurity senior engineer/director - security is paramount
|
||||
8. **Monetization:** Tiered subscription model with free tier
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
### API Backend (`/api`)
|
||||
|
||||
| Component | Technology | Purpose |
|
||||
|-----------|-----------|---------|
|
||||
| **Framework** | Flask 3.x | REST API framework |
|
||||
| **Authentication** | Appwrite Auth | User management, session tokens |
|
||||
| **Database** | Appwrite Database | NoSQL document storage |
|
||||
| **Realtime** | Appwrite Realtime | WebSocket connections for multiplayer |
|
||||
| **Storage** | Appwrite Storage | User assets, logs |
|
||||
| **Job Queue** | RQ (Redis Queue) | Async AI calls, background tasks |
|
||||
| **Caching** | Redis | Session data, rate limiting |
|
||||
| **AI (Free)** | Replicate API | Free tier users (Llama 3 70B) |
|
||||
| **AI (Paid)** | Anthropic Claude | Paid tier users (Haiku/Sonnet) |
|
||||
| **Data Models** | Dataclasses | Typed domain models |
|
||||
| **Logging** | Structlog | Structured logging |
|
||||
| **Testing** | Pytest | Unit and integration tests |
|
||||
| **WSGI Server** | Gunicorn | Production WSGI server |
|
||||
|
||||
### Web Frontend (`/public_web`)
|
||||
|
||||
| Component | Technology | Purpose |
|
||||
|-----------|-----------|---------|
|
||||
| **Framework** | Flask 3.x | Lightweight web server |
|
||||
| **Templates** | Jinja2 | Server-side HTML rendering |
|
||||
| **UI Enhancement** | HTMX | Dynamic updates without heavy JS |
|
||||
| **Styling** | Vanilla CSS | Custom dark theme |
|
||||
| **API Client** | Python requests | HTTP calls to API backend |
|
||||
| **Logging** | Structlog | Structured logging |
|
||||
| **WSGI Server** | Gunicorn | Production WSGI server |
|
||||
|
||||
### Godot Client (`/godot_client`)
|
||||
|
||||
| Component | Technology | Purpose |
|
||||
|-----------|-----------|---------|
|
||||
| **Engine** | Godot 4.5 | Cross-platform game engine |
|
||||
| **Language** | GDScript | Game logic and UI |
|
||||
| **HTTP Client** | HTTPClient singleton | API communication |
|
||||
| **State Management** | StateManager singleton | Global game state |
|
||||
| **Fonts** | Cinzel + Lato | Display and body fonts |
|
||||
| **Exports** | Desktop/Mobile/Web | Multi-platform deployment |
|
||||
|
||||
### Shared Infrastructure
|
||||
|
||||
| Component | Technology | Purpose |
|
||||
|-----------|-----------|---------|
|
||||
| **Configuration** | YAML files | Environment-specific settings |
|
||||
| **Secrets** | .env files | API keys, credentials |
|
||||
| **Deployment** | Docker | Containerized services |
|
||||
| **Version Control** | Git | Source code management |
|
||||
|
||||
---
|
||||
|
||||
## AI Model Strategy
|
||||
|
||||
### Model Selection Philosophy
|
||||
- Use **Anthropic Claude models** for all paid tier interactions
|
||||
- Use **Replicate free models** only for free tier users
|
||||
- Model selection is **per-call based**, determined by context importance and user tier
|
||||
|
||||
### Model Tiers
|
||||
|
||||
| Tier | Provider | Model | Max Tokens | Temperature | Use Cases |
|
||||
|------|----------|-------|------------|-------------|-----------|
|
||||
| **FREE** | Replicate | meta-llama-3-70b-instruct | 256 | 0.7 | Free tier users only |
|
||||
| **STANDARD** | Anthropic | claude-3-5-haiku-20241022 | 512 | 0.8 | Most interactions (paid) |
|
||||
| **PREMIUM** | Anthropic | claude-3-5-sonnet-20241022 | 1024 | 0.9 | Important moments (paid) |
|
||||
|
||||
### Context-Based Model Selection
|
||||
|
||||
| Context | Model Tier | Examples |
|
||||
|---------|-----------|----------|
|
||||
| Simple acknowledgment | FREE | "You enter the room", "Item picked up" |
|
||||
| Item description | FREE | Basic item lore |
|
||||
| NPC dialogue | STANDARD | Merchant conversations, quest givers |
|
||||
| Combat narrative | STANDARD | Regular combat descriptions |
|
||||
| Story progression | PREMIUM | Major plot developments |
|
||||
| Boss encounter | PREMIUM | Epic boss fight narratives |
|
||||
| Character death | PREMIUM | Dramatic death scenes |
|
||||
|
||||
**Note:** Free tier users always get FREE tier models regardless of context.
|
||||
|
||||
### Prompt Management
|
||||
- Use **Jinja2 templates** for all AI prompts
|
||||
- Enable easy data injection and maintainability
|
||||
- Templates stored in `app/ai/prompt_templates.py`
|
||||
|
||||
---
|
||||
|
||||
## Appwrite Collections
|
||||
|
||||
### users
|
||||
Handled automatically by Appwrite Auth.
|
||||
|
||||
### characters
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `$id` | String | Unique ID |
|
||||
| `userId` | String | Owner user ID |
|
||||
| `characterData` | JSON String | Serialized Character dataclass |
|
||||
| `created_at` | ISO Timestamp | Creation time |
|
||||
| `updated_at` | ISO Timestamp | Last update |
|
||||
| `is_active` | Boolean | Active character flag |
|
||||
|
||||
### game_sessions
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `$id` | String | Session ID |
|
||||
| `party_member_ids` | Array[String] | Character IDs in party |
|
||||
| `config` | Object | Session configuration |
|
||||
| `combat_encounter` | JSON String | Current combat or null |
|
||||
| `conversation_history` | Array[Object] | Turn-by-turn history |
|
||||
| `game_state` | Object | Current location, quests, events |
|
||||
| `turn_order` | Array[String] | Character IDs in turn order |
|
||||
| `current_turn` | Integer | Index in turn_order |
|
||||
| `turn_number` | Integer | Global turn counter |
|
||||
| `created_at` | ISO Timestamp | Session start |
|
||||
| `last_activity` | ISO Timestamp | Last action |
|
||||
| `status` | String | active, completed, timeout |
|
||||
|
||||
**Session Config Structure:**
|
||||
```json
|
||||
{
|
||||
"min_players": 2,
|
||||
"timeout_minutes": 30,
|
||||
"auto_save_interval": 5
|
||||
}
|
||||
```
|
||||
|
||||
**Conversation History Entry:**
|
||||
```json
|
||||
{
|
||||
"turn": 1,
|
||||
"character_id": "char_id_1",
|
||||
"character_name": "Aragorn",
|
||||
"action": "I search for traps",
|
||||
"dm_response": "You notice...",
|
||||
"combat_log": []
|
||||
}
|
||||
```
|
||||
|
||||
### marketplace_listings
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `$id` | String | Listing ID |
|
||||
| `seller_id` | String | User ID |
|
||||
| `character_id` | String | Character ID |
|
||||
| `item_data` | Object | Full item details |
|
||||
| `listing_type` | String | "auction" or "fixed_price" |
|
||||
| `price` | Integer | For fixed_price |
|
||||
| `starting_bid` | Integer | For auction |
|
||||
| `current_bid` | Integer | For auction |
|
||||
| `buyout_price` | Integer | Optional instant buy |
|
||||
| `bids` | Array[Object] | Bid history |
|
||||
| `auction_end` | ISO Timestamp | For auction |
|
||||
| `status` | String | active, sold, expired, removed |
|
||||
| `created_at` | ISO Timestamp | Listing creation |
|
||||
|
||||
### transactions
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `$id` | String | Transaction ID |
|
||||
| `buyer_id` | String | User ID |
|
||||
| `seller_id` | String | User ID |
|
||||
| `listing_id` | String | Listing ID |
|
||||
| `item_data` | Object | Item details |
|
||||
| `price` | Integer | Final price |
|
||||
| `timestamp` | ISO Timestamp | Transaction time |
|
||||
| `transaction_type` | String | marketplace_sale, shop_purchase, etc. |
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
**Repository Root:**
|
||||
|
||||
```
|
||||
/coc
|
||||
├── api/ # API Backend Component
|
||||
├── public_web/ # Web Frontend Component
|
||||
├── godot_client/ # Godot Game Client Component
|
||||
├── docs/ # Project-wide documentation
|
||||
├── CLAUDE.md # Development guidelines
|
||||
└── README.md # Project overview
|
||||
```
|
||||
|
||||
### API Backend (`/api`)
|
||||
|
||||
**Single source of truth - All business logic**
|
||||
|
||||
```
|
||||
api/
|
||||
├── app/
|
||||
│ ├── __init__.py # Flask app factory
|
||||
│ ├── config.py # Configuration management
|
||||
│ ├── models/ # Data models (dataclasses)
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── enums.py # All enum types
|
||||
│ │ ├── stats.py # Stats dataclass
|
||||
│ │ ├── effects.py # Effect dataclass
|
||||
│ │ ├── abilities.py # Ability dataclass
|
||||
│ │ ├── items.py # Item dataclass
|
||||
│ │ ├── skills.py # SkillNode, SkillTree, PlayerClass
|
||||
│ │ ├── origins.py # Origin dataclass
|
||||
│ │ ├── character.py # Character with get_effective_stats()
|
||||
│ │ ├── combat.py # Combatant, CombatEncounter
|
||||
│ │ ├── session.py # GameSession, SessionConfig
|
||||
│ │ └── marketplace.py # Marketplace models
|
||||
│ ├── api/ # REST API endpoints
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── health.py # Health check
|
||||
│ │ ├── auth.py # Authentication endpoints
|
||||
│ │ ├── characters.py # Character CRUD
|
||||
│ │ ├── sessions.py # Session management (TODO)
|
||||
│ │ ├── combat.py # Combat actions (TODO)
|
||||
│ │ ├── marketplace.py # Marketplace operations (TODO)
|
||||
│ │ └── shop.py # NPC shop (TODO)
|
||||
│ ├── game_logic/ # Game mechanics
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── combat_engine.py # Combat calculations (TODO)
|
||||
│ │ ├── damage_calculator.py # Damage formulas (TODO)
|
||||
│ │ ├── effect_processor.py # Effect tick processing (TODO)
|
||||
│ │ ├── skill_manager.py # Skill unlock logic (TODO)
|
||||
│ │ └── loot_generator.py # Random loot generation (TODO)
|
||||
│ ├── ai/ # AI integration
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── ai_client.py # Replicate + Anthropic clients (TODO)
|
||||
│ │ ├── model_selector.py # Model tier selection (TODO)
|
||||
│ │ ├── prompt_templates.py # Jinja2 prompt templates (TODO)
|
||||
│ │ └── narrative_generator.py # AI narrative wrapper (TODO)
|
||||
│ ├── tasks/ # Background jobs (RQ)
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── ai_tasks.py # RQ jobs for AI calls (TODO)
|
||||
│ │ ├── combat_tasks.py # Async combat processing (TODO)
|
||||
│ │ └── marketplace_tasks.py # Auction processing (TODO)
|
||||
│ ├── services/ # Business logic & integrations
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── appwrite_service.py # Appwrite SDK wrapper
|
||||
│ │ ├── database_service.py # Database operations
|
||||
│ │ ├── database_init.py # Database initialization
|
||||
│ │ ├── character_service.py # Character management
|
||||
│ │ ├── class_loader.py # Load classes from YAML
|
||||
│ │ └── origin_service.py # Load origins from YAML
|
||||
│ ├── utils/ # Utilities
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── logging.py # Structlog setup
|
||||
│ │ ├── response.py # Standardized API responses
|
||||
│ │ └── auth.py # Auth decorators
|
||||
│ └── data/ # Game data (YAML)
|
||||
│ ├── abilities/ # Ability definitions
|
||||
│ ├── classes/ # Character class definitions
|
||||
│ ├── items/ # Item definitions (TODO)
|
||||
│ └── origins.yaml # Origin definitions
|
||||
├── config/ # Environment configs
|
||||
│ ├── development.yaml # Dev environment settings
|
||||
│ └── production.yaml # Production settings
|
||||
├── tests/ # Pytest test suite
|
||||
│ ├── __init__.py
|
||||
│ ├── test_character.py
|
||||
│ ├── test_stats.py
|
||||
│ ├── test_effects.py
|
||||
│ ├── test_combat_simulation.py
|
||||
│ ├── test_class_loader.py
|
||||
│ ├── test_origin_service.py
|
||||
│ ├── test_character_service.py
|
||||
│ └── test_api_characters_integration.py
|
||||
├── scripts/ # Utility scripts
|
||||
│ ├── init_database.py # Database initialization
|
||||
│ ├── setup.sh # Project setup
|
||||
│ └── README.md
|
||||
├── logs/ # Application logs
|
||||
│ └── app.log
|
||||
├── docs/ # API documentation
|
||||
│ ├── API_REFERENCE.md # API endpoint docs
|
||||
│ ├── API_TESTING.md # API testing guide
|
||||
│ ├── DATA_MODELS.md # Data model documentation
|
||||
│ ├── GAME_SYSTEMS.md # Game mechanics docs
|
||||
│ └── APPWRITE_SETUP.md # Database setup guide
|
||||
├── requirements.txt # Python dependencies (full stack)
|
||||
├── wsgi.py # WSGI entry point
|
||||
├── docker-compose.yml # Redis service
|
||||
├── .env.example # Environment variable template
|
||||
└── README.md # API backend README
|
||||
```
|
||||
|
||||
### Web Frontend (`/public_web`)
|
||||
|
||||
**Thin UI layer - Makes HTTP requests to API**
|
||||
|
||||
```
|
||||
public_web/
|
||||
├── app/
|
||||
│ ├── __init__.py # Flask app factory
|
||||
│ ├── config.py # Configuration loader
|
||||
│ ├── views/ # View blueprints (Flask routes)
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── auth_views.py # Auth pages (login, register, etc.)
|
||||
│ │ └── character_views.py # Character pages
|
||||
│ ├── services/ # (Stub) API client services
|
||||
│ │ ├── __init__.py # TODO: Refactor to HTTP client
|
||||
│ │ ├── character_service.py # Stub (raises NotImplementedError)
|
||||
│ │ ├── class_loader.py # Stub (raises NotImplementedError)
|
||||
│ │ └── origin_service.py # Stub (raises NotImplementedError)
|
||||
│ └── utils/ # Utilities
|
||||
│ ├── __init__.py
|
||||
│ ├── logging.py # Structlog setup
|
||||
│ └── auth.py # Auth helpers (stubs, need refactor)
|
||||
├── templates/ # Jinja2 HTML templates
|
||||
│ ├── base.html # Base layout
|
||||
│ ├── auth/ # Authentication pages
|
||||
│ │ ├── login.html
|
||||
│ │ ├── register.html
|
||||
│ │ ├── forgot_password.html
|
||||
│ │ ├── reset_password.html
|
||||
│ │ └── verify_email.html
|
||||
│ └── character/ # Character pages
|
||||
│ ├── list.html # Character list
|
||||
│ ├── detail.html # Character detail
|
||||
│ ├── create_origin.html # Creation step 1
|
||||
│ ├── create_class.html # Creation step 2
|
||||
│ ├── create_customize.html # Creation step 3
|
||||
│ └── create_confirm.html # Creation step 4
|
||||
├── static/ # CSS, JS, images
|
||||
│ └── css/
|
||||
│ └── main.css # Main stylesheet
|
||||
├── config/ # Web frontend configs
|
||||
│ ├── development.yaml # Dev settings (API URL, etc.)
|
||||
│ └── production.yaml # Production settings
|
||||
├── logs/ # Application logs
|
||||
├── docs/ # Frontend documentation
|
||||
├── requirements.txt # Python dependencies (minimal)
|
||||
├── wsgi.py # WSGI entry point
|
||||
├── .env.example # Environment template
|
||||
└── README.md # Web frontend README
|
||||
```
|
||||
|
||||
**Known Technical Debt:**
|
||||
- Views currently import stub services that raise `NotImplementedError`
|
||||
- Need to refactor views to make HTTP requests to API backend
|
||||
- Auth helpers need to validate sessions via API
|
||||
- See `/public_web/README.md` for details
|
||||
|
||||
### Godot Client (`/godot_client`)
|
||||
|
||||
**Native cross-platform game client**
|
||||
|
||||
```
|
||||
godot_client/
|
||||
├── project.godot # Godot project configuration
|
||||
├── scenes/ # Godot scene files (.tscn)
|
||||
│ ├── main.tscn # Entry point
|
||||
│ ├── auth/ # Authentication scenes
|
||||
│ │ └── login.tscn
|
||||
│ ├── character/ # Character scenes
|
||||
│ ├── combat/ # Combat scenes
|
||||
│ ├── world/ # World exploration scenes
|
||||
│ └── components/ # Reusable UI components
|
||||
├── scripts/ # GDScript code
|
||||
│ ├── main.gd # Main scene controller
|
||||
│ ├── services/ # Singleton autoloads
|
||||
│ │ ├── settings.gd # Settings management
|
||||
│ │ ├── http_client.gd # HTTP client for API calls
|
||||
│ │ └── state_manager.gd # Global state management
|
||||
│ ├── models/ # Data models
|
||||
│ │ └── api_response.gd # API response wrapper
|
||||
│ ├── components/ # Component scripts
|
||||
│ │ ├── form_field.gd # Form input component
|
||||
│ │ ├── card.gd # Card container
|
||||
│ │ └── custom_button.gd # Styled button
|
||||
│ └── utils/ # Helper utilities
|
||||
│ └── theme_colors.gd # Color constants
|
||||
├── assets/ # Game assets
|
||||
│ ├── fonts/ # Cinzel + Lato fonts
|
||||
│ ├── themes/ # UI theme files
|
||||
│ └── ui/ # UI assets
|
||||
├── docs/ # Godot client documentation
|
||||
│ ├── ARCHITECTURE.md # Client architecture
|
||||
│ ├── GETTING_STARTED.md # Setup guide
|
||||
│ ├── EXPORT.md # Export instructions
|
||||
│ └── THEME_SETUP.md # Theming guide
|
||||
├── ARCHITECTURE.md # Architecture overview
|
||||
├── README.md # Setup and usage
|
||||
└── EXPORT.md # Platform export guide
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RQ Job Processing
|
||||
|
||||
### Job Types
|
||||
|
||||
| Queue | Job Function | Purpose |
|
||||
|-------|--------------|---------|
|
||||
| **ai_tasks** | `generate_dm_response()` | Generate narrative responses |
|
||||
| | `generate_npc_dialogue()` | NPC conversations |
|
||||
| | `narrate_combat_action()` | Combat descriptions |
|
||||
| **combat_tasks** | `process_combat_round()` | Execute combat turn |
|
||||
| | `finalize_combat()` | Handle combat end |
|
||||
| **marketplace_tasks** | `process_ended_auctions()` | Periodic auction cleanup |
|
||||
| | `cleanup_old_session_logs()` | Periodic log cleanup |
|
||||
|
||||
### Worker Configuration
|
||||
|
||||
**Start RQ workers:**
|
||||
```bash
|
||||
# Single worker for all queues
|
||||
rq worker ai_tasks combat_tasks marketplace_tasks --url redis://localhost:6379
|
||||
|
||||
# Or separate workers
|
||||
rq worker ai_tasks --url redis://localhost:6379
|
||||
rq worker combat_tasks --url redis://localhost:6379
|
||||
rq worker marketplace_tasks --url redis://localhost:6379
|
||||
```
|
||||
|
||||
### Job Flow
|
||||
|
||||
1. User takes action in game
|
||||
2. Action queued to RQ with job ID
|
||||
3. API returns 202 Accepted with job ID
|
||||
4. Worker processes job asynchronously
|
||||
5. Worker updates Appwrite document
|
||||
6. Appwrite Realtime notifies all clients
|
||||
7. UI updates automatically
|
||||
|
||||
---
|
||||
|
||||
## Realtime Synchronization
|
||||
|
||||
**Use Appwrite Realtime for WebSocket connections:**
|
||||
|
||||
Frontend subscribes to session updates and receives automatic notifications when game state changes.
|
||||
|
||||
**Benefits:**
|
||||
- No polling required
|
||||
- Instant updates for all party members
|
||||
- Built-in connection management
|
||||
- Automatic reconnection
|
||||
|
||||
---
|
||||
|
||||
## Subscription Tiers
|
||||
|
||||
| Tier | Price | AI Calls/Day | AI Model | Marketplace | Log Retention | Max Chars | Party Size |
|
||||
|------|-------|--------------|----------|-------------|---------------|-----------|------------|
|
||||
| **FREE** | $0 | 50 | Replicate | ✗ | 7 days | 1 | Solo |
|
||||
| **BASIC** | $4.99 | 200 | Haiku | ✗ | 14 days | 3 | 2 |
|
||||
| **PREMIUM** | $9.99 | 1000 | Sonnet | ✓ | 30 days | 10 | 6 |
|
||||
| **ELITE** | $19.99 | Unlimited | Sonnet | ✓+ | 90 days | Unlimited | 10 |
|
||||
|
||||
**ELITE Perks:**
|
||||
- Priority marketplace listings
|
||||
- Early access to new classes
|
||||
- Exclusive items
|
||||
|
||||
---
|
||||
|
||||
## Security Architecture
|
||||
|
||||
### Authentication & Authorization
|
||||
- Appwrite Auth handles user authentication
|
||||
- HTTP-only cookies (`coc_session`) for session storage
|
||||
- Prevents XSS attacks (JavaScript cannot access)
|
||||
- HTTPS only in production (Secure flag)
|
||||
- SameSite=Lax for CSRF protection
|
||||
- 24-hour sessions (30 days with "remember me")
|
||||
- Email verification required before login
|
||||
- Password reset via email flow
|
||||
- Auth decorators for protected routes:
|
||||
- `@require_auth` - Enforce authentication
|
||||
- `@require_tier(tier)` - Enforce minimum subscription tier
|
||||
- `@require_email_verified` - Enforce verified email
|
||||
- User tier system (Free/Basic/Premium/Elite)
|
||||
- Session validation on every protected API call
|
||||
- User ID verification (users can only access their own data)
|
||||
|
||||
### Input Validation
|
||||
- Validate all JSON payloads against schemas
|
||||
- Sanitize user inputs (character names, chat messages)
|
||||
- Prevent injection attacks
|
||||
|
||||
### Rate Limiting
|
||||
- AI endpoint limits based on subscription tier
|
||||
- Marketplace actions (max listings per day)
|
||||
- Combat actions (prevent spam/automation)
|
||||
- Use Flask-Limiter with Redis backend
|
||||
|
||||
### API Security
|
||||
- CORS properly configured (only allow frontend domain)
|
||||
- API keys stored in environment variables
|
||||
- Appwrite permissions set correctly on all collections
|
||||
- HTTPS only in production
|
||||
|
||||
### Cost Control (AI)
|
||||
- Daily limits on AI calls per tier
|
||||
- Max tokens per request type
|
||||
- Cost logging for analytics and alerts
|
||||
- Graceful degradation if limits exceeded
|
||||
|
||||
### Data Protection
|
||||
|
||||
| Resource | Read Access | Write Access |
|
||||
|----------|-------------|--------------|
|
||||
| **Characters** | Owner only | Owner only |
|
||||
| **Sessions** | Party members | Active player |
|
||||
| **Marketplace** | All users | Listing owner |
|
||||
| **Transactions** | Buyer + Seller | System only |
|
||||
|
||||
---
|
||||
|
||||
## Why These Technologies?
|
||||
|
||||
### Flask over FastAPI
|
||||
- Team familiarity with Flask
|
||||
- Async handled by RQ (don't need FastAPI's native async)
|
||||
- Mature ecosystem for templates (Jinja2)
|
||||
- Can migrate specific endpoints later if needed
|
||||
|
||||
### RQ over Celery
|
||||
- Simpler setup and configuration
|
||||
- Adequate for current scale
|
||||
- Easier debugging
|
||||
- Redis already required for caching
|
||||
|
||||
### Appwrite
|
||||
- Reduces infrastructure overhead
|
||||
- Built-in auth, database, storage, realtime
|
||||
- Handles scaling of data layer
|
||||
- Real-time WebSocket support out of box
|
||||
- Self-hosted option available if needed later
|
||||
|
||||
### Dataclasses over ORM
|
||||
- Flexibility in data structure (easy to change)
|
||||
- No database migration headaches
|
||||
- JSON storage in Appwrite is schema-flexible
|
||||
- Easier to serialize/deserialize
|
||||
- Performance (no ORM overhead)
|
||||
|
||||
### Turn-Based
|
||||
- Simpler AI prompt construction
|
||||
- Better for multiplayer coordination
|
||||
- Classic D&D feel
|
||||
- Easier to balance than real-time
|
||||
- Less server load (no constant state updates)
|
||||
|
||||
### Microservices Architecture
|
||||
- **Independent Deployment:** Each component can be updated/scaled separately
|
||||
- **Technology Flexibility:** Can use different tech stacks per component (Flask + Godot)
|
||||
- **Team Scalability:** Different teams can work on different components
|
||||
- **Fault Isolation:** Failures in one component don't crash entire system
|
||||
- **API as Contract:** Clear interface between frontend and backend
|
||||
|
||||
---
|
||||
|
||||
## Microservices Communication
|
||||
|
||||
### API-First Design
|
||||
|
||||
All frontends communicate with the API backend exclusively via HTTP/REST:
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Public Web │──┐
|
||||
└─────────────┘ │
|
||||
│ HTTP/REST
|
||||
┌─────────────┐ │ (JSON)
|
||||
│ Godot Client│──┼──────────► ┌──────────────┐
|
||||
└─────────────┘ │ │ API Backend │
|
||||
│ │ (Port 5000) │
|
||||
│ └──────────────┘
|
||||
│
|
||||
Other │
|
||||
Clients │
|
||||
(Future) ────┘
|
||||
```
|
||||
|
||||
### Communication Patterns
|
||||
|
||||
**1. Request/Response (Synchronous)**
|
||||
- Standard CRUD operations
|
||||
- Character creation, updates
|
||||
- Authentication
|
||||
- Data retrieval
|
||||
|
||||
**Example:**
|
||||
```
|
||||
POST /api/v1/characters
|
||||
{
|
||||
"name": "Aragorn",
|
||||
"class_id": "vanguard",
|
||||
"origin_id": "noble_exile"
|
||||
}
|
||||
|
||||
→ API processes request
|
||||
→ Returns character data
|
||||
|
||||
Response:
|
||||
{
|
||||
"app": "Code of Conquest",
|
||||
"status": 200,
|
||||
"result": { character_data },
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
**2. Job Queue (Asynchronous)**
|
||||
- AI narrative generation
|
||||
- Combat processing
|
||||
- Long-running operations
|
||||
|
||||
**Example:**
|
||||
```
|
||||
POST /api/v1/sessions/{id}/action
|
||||
{
|
||||
"action": "I search the ancient library"
|
||||
}
|
||||
|
||||
→ API queues job to RQ
|
||||
→ Returns job ID immediately
|
||||
|
||||
Response:
|
||||
{
|
||||
"status": 202,
|
||||
"result": {
|
||||
"job_id": "abc123",
|
||||
"status": "queued"
|
||||
}
|
||||
}
|
||||
|
||||
→ Worker processes AI call
|
||||
→ Updates Appwrite document
|
||||
→ Appwrite Realtime notifies clients
|
||||
```
|
||||
|
||||
**3. Real-Time (WebSocket via Appwrite)**
|
||||
- Multiplayer session updates
|
||||
- Combat state changes
|
||||
- Party member actions
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Subscribe to session updates
|
||||
appwrite.subscribe('sessions.{sessionId}', callback);
|
||||
|
||||
// When another player acts:
|
||||
→ Worker updates session in Appwrite
|
||||
→ Appwrite broadcasts to all subscribers
|
||||
→ All clients update UI automatically
|
||||
```
|
||||
|
||||
### API Endpoints Structure
|
||||
|
||||
```
|
||||
/api/v1/
|
||||
├── health # Health check
|
||||
├── auth/ # Authentication
|
||||
│ ├── login
|
||||
│ ├── register
|
||||
│ ├── logout
|
||||
│ └── reset-password
|
||||
├── characters/ # Character management
|
||||
│ ├── GET / # List characters
|
||||
│ ├── POST / # Create character
|
||||
│ ├── GET /{id} # Get character
|
||||
│ ├── PUT /{id} # Update character
|
||||
│ └── DELETE /{id} # Delete character
|
||||
├── sessions/ # Game sessions (TODO)
|
||||
│ ├── POST / # Create session
|
||||
│ ├── GET /{id} # Get session
|
||||
│ └── POST /{id}/action # Take action
|
||||
├── combat/ # Combat system (TODO)
|
||||
│ └── POST /sessions/{id}/combat/action
|
||||
├── marketplace/ # Marketplace (TODO)
|
||||
│ ├── GET /listings
|
||||
│ └── POST /listings
|
||||
└── shop/ # NPC shop (TODO)
|
||||
└── GET /items
|
||||
```
|
||||
|
||||
### Frontend Responsibilities
|
||||
|
||||
**API Backend (`/api`):**
|
||||
- ✅ All business logic
|
||||
- ✅ Data validation
|
||||
- ✅ Database operations
|
||||
- ✅ Authentication & authorization
|
||||
- ✅ Game mechanics calculations
|
||||
- ✅ AI orchestration
|
||||
|
||||
**Web Frontend (`/public_web`):**
|
||||
- ❌ No business logic
|
||||
- ✅ Render HTML templates
|
||||
- ✅ Form validation (UI only)
|
||||
- ✅ Make HTTP requests to API
|
||||
- ✅ Display API responses
|
||||
- ⚠️ **Current:** Has stub services (needs refactoring)
|
||||
|
||||
**Godot Client (`/godot_client`):**
|
||||
- ❌ No business logic
|
||||
- ✅ Render game UI
|
||||
- ✅ Handle user input
|
||||
- ✅ Make HTTP requests to API
|
||||
- ✅ Display API responses
|
||||
- ✅ Client-side animations/effects
|
||||
|
||||
---
|
||||
|
||||
## Deployment Architecture
|
||||
|
||||
### Development Environment
|
||||
|
||||
```
|
||||
┌────────────────────┐
|
||||
│ Developer Machine │
|
||||
├────────────────────┤
|
||||
│ │
|
||||
│ API Backend │ ← Port 5000
|
||||
│ (Flask dev server)│
|
||||
│ │
|
||||
│ Web Frontend │ ← Port 5001
|
||||
│ (Flask dev server)│
|
||||
│ │
|
||||
│ Godot Editor │ ← F5 to test
|
||||
│ │
|
||||
│ Redis (Docker) │ ← Port 6379
|
||||
│ │
|
||||
└────────────────────┘
|
||||
│
|
||||
│ API calls
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Appwrite │
|
||||
│ (Cloud) │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
**Commands:**
|
||||
```bash
|
||||
# Terminal 1: API Backend
|
||||
cd api
|
||||
source venv/bin/activate
|
||||
python wsgi.py # → http://localhost:5000
|
||||
|
||||
# Terminal 2: Web Frontend
|
||||
cd public_web
|
||||
source venv/bin/activate
|
||||
python wsgi.py # → http://localhost:5001
|
||||
|
||||
# Terminal 3: Redis
|
||||
cd api
|
||||
docker-compose up
|
||||
|
||||
# Terminal 4: RQ Worker (optional)
|
||||
cd api
|
||||
source venv/bin/activate
|
||||
rq worker ai_tasks --url redis://localhost:6379
|
||||
|
||||
# Godot: Open project and press F5
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ Load Balancer / CDN │
|
||||
└────────────┬─────────────────────────┘
|
||||
│
|
||||
┌──────┴──────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ Web │ │ Web │ ← Port 8080 (internal)
|
||||
│ Frontend │ │ Frontend │
|
||||
│(Gunicorn)│ │(Gunicorn)│
|
||||
└────┬─────┘ └────┬─────┘
|
||||
│ │
|
||||
│ HTTP Requests
|
||||
└──────┬──────┘
|
||||
▼
|
||||
┌──────────┐
|
||||
│ API │ ← Port 5000 (internal)
|
||||
│ Backend │
|
||||
│(Gunicorn)│
|
||||
└────┬─────┘
|
||||
│
|
||||
┌────┴────┬──────────┬──────────┐
|
||||
▼ ▼ ▼ ▼
|
||||
┌──────────┐ ┌─────┐ ┌────────┐ ┌────────┐
|
||||
│ Appwrite │ │Redis│ │ RQ │ │ AI │
|
||||
│ │ │ │ │Workers │ │ APIs │
|
||||
└──────────┘ └─────┘ └────────┘ └────────┘
|
||||
```
|
||||
|
||||
**Deployment Strategy:**
|
||||
- Each component containerized with Docker
|
||||
- Independent scaling (more web servers if needed)
|
||||
- API backend as single deployment (multiple workers via Gunicorn)
|
||||
- Godot client exported to native apps (distributed separately)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
**API Backend:**
|
||||
```
|
||||
FLASK_ENV=production
|
||||
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
|
||||
APPWRITE_PROJECT_ID=...
|
||||
APPWRITE_API_KEY=...
|
||||
APPWRITE_DATABASE_ID=...
|
||||
ANTHROPIC_API_KEY=...
|
||||
REPLICATE_API_TOKEN=...
|
||||
REDIS_URL=redis://redis:6379
|
||||
SECRET_KEY=...
|
||||
```
|
||||
|
||||
**Web Frontend:**
|
||||
```
|
||||
FLASK_ENV=production
|
||||
SECRET_KEY=...
|
||||
API_BASE_URL=https://api.codeofconquest.com
|
||||
```
|
||||
|
||||
**Godot Client:**
|
||||
```gdscript
|
||||
# In settings.gd or environment config
|
||||
var api_base_url = "https://api.codeofconquest.com"
|
||||
```
|
||||
Reference in New Issue
Block a user