Document offline fork support architecture
Add long-term design consideration for forking the RPG campaign as a standalone offline experience. ARCHITECTURE.md: - Add 'Offline Standalone Fork' section explaining: - Why offline support matters (single-player RPG focus) - Architecture principles for fork compatibility - Core engine independence requirements - Potential package structures and distribution options - What stays vs what goes in a fork AGENTS.md: - Add 'Core Engine Independence' section with: - Rules for keeping app/core/ decoupled - Import boundary examples (allowed vs forbidden) - Link to full architecture docs This ensures all contributors understand the design constraint: the game engine must remain completely independent of network, database, and authentication concerns.
This commit is contained in:
parent
91ad2cf0a5
commit
703bed07fb
40
AGENTS.md
40
AGENTS.md
@ -223,6 +223,46 @@ opponent_deck_count=len(opponent.deck) # ONLY count
|
|||||||
5. **Tests**: Include docstrings explaining "what" and "why" for each test.
|
5. **Tests**: Include docstrings explaining "what" and "why" for each test.
|
||||||
6. **Phaser in Vue**: Keep Phaser scenes focused on rendering. Game logic lives in backend.
|
6. **Phaser in Vue**: Keep Phaser scenes focused on rendering. Game logic lives in backend.
|
||||||
7. **Services**: Never bypass the service layer for business logic.
|
7. **Services**: Never bypass the service layer for business logic.
|
||||||
|
8. **Core Engine Independence**: Keep `app/core/` decoupled from DB/network (see below).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Engine Independence (Offline Fork Support)
|
||||||
|
|
||||||
|
> **Long-term goal**: The `backend/app/core/` module should remain forkable as a standalone offline game.
|
||||||
|
|
||||||
|
The game engine must stay **completely decoupled** from network and database concerns to enable a future offline/standalone version of the RPG campaign mode.
|
||||||
|
|
||||||
|
### Rules for `app/core/` Module
|
||||||
|
|
||||||
|
| DO | DON'T |
|
||||||
|
|----|-------|
|
||||||
|
| Accept `CardDefinition` objects as parameters | Import from `app.services` or `app.api` |
|
||||||
|
| Use `RandomProvider` protocol for RNG | Import database session types |
|
||||||
|
| Keep state self-contained in `GameState` | Make network calls or database queries |
|
||||||
|
| Use sync logic (async wrappers at service layer) | Require authentication or user sessions |
|
||||||
|
| Load configuration from `RulesConfig` objects | Hard-code database connection strings |
|
||||||
|
|
||||||
|
### Import Boundaries
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ALLOWED in app/core/
|
||||||
|
from app.core.models import CardDefinition, GameState
|
||||||
|
from app.core.config import RulesConfig
|
||||||
|
from app.core.rng import RandomProvider
|
||||||
|
|
||||||
|
# FORBIDDEN in app/core/
|
||||||
|
from app.services.card_service import CardService # NO - DB dependency
|
||||||
|
from app.api.deps import get_current_user # NO - Auth dependency
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession # NO - DB dependency
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why This Matters
|
||||||
|
|
||||||
|
See `docs/ARCHITECTURE.md#offline-standalone-fork` for full details. The core engine should be directly copyable to a standalone Python application with:
|
||||||
|
- Card definitions loaded from JSON files
|
||||||
|
- Save data stored locally
|
||||||
|
- No network or authentication requirements
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ This document provides a detailed technical overview of the Mantimon TCG archite
|
|||||||
6. [Game Engine](#game-engine)
|
6. [Game Engine](#game-engine)
|
||||||
7. [AI System](#ai-system)
|
7. [AI System](#ai-system)
|
||||||
8. [Security Considerations](#security-considerations)
|
8. [Security Considerations](#security-considerations)
|
||||||
|
9. [Offline Standalone Fork](#offline-standalone-fork)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -820,3 +821,115 @@ async def validate_action(game: GameState, player_id: str, action: dict) -> Vali
|
|||||||
|
|
||||||
return ValidationResult(False, "Unknown action type")
|
return ValidationResult(False, "Unknown action type")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Offline Standalone Fork
|
||||||
|
|
||||||
|
> **Design Consideration**: The architecture intentionally supports forking as a completely offline, standalone experience for the RPG/campaign mode.
|
||||||
|
|
||||||
|
### Why This Matters
|
||||||
|
|
||||||
|
The primary gameplay experience is a single-player RPG campaign. While the live service provides multiplayer and cloud saves, many players may prefer:
|
||||||
|
- Offline play without internet dependency
|
||||||
|
- Local save files they fully control
|
||||||
|
- A self-contained executable with no account requirements
|
||||||
|
- Modding and customization freedom
|
||||||
|
|
||||||
|
### Architecture Principles for Fork Compatibility
|
||||||
|
|
||||||
|
The `backend/app/core/` game engine is designed to be **completely decoupled** from network and database concerns:
|
||||||
|
|
||||||
|
| Component | Live Service | Offline Fork |
|
||||||
|
|-----------|--------------|--------------|
|
||||||
|
| Game Engine (`core/`) | Same | Same (copy directly) |
|
||||||
|
| Card Definitions | PostgreSQL | Embedded JSON files |
|
||||||
|
| Save Data | PostgreSQL + Redis | Local JSON/SQLite files |
|
||||||
|
| Authentication | OAuth/JWT | None needed |
|
||||||
|
| Multiplayer | WebSocket/Socket.io | Removed entirely |
|
||||||
|
| RNG | SecureRandom | SeededRandom (for replays) |
|
||||||
|
|
||||||
|
### Core Engine Independence
|
||||||
|
|
||||||
|
The game engine has **zero dependencies** on:
|
||||||
|
- Database connections
|
||||||
|
- Network I/O
|
||||||
|
- Authentication/sessions
|
||||||
|
- Redis caching
|
||||||
|
- WebSocket communication
|
||||||
|
|
||||||
|
All it needs:
|
||||||
|
- `RulesConfig` (can be hardcoded or loaded from JSON)
|
||||||
|
- `CardDefinition` objects (can be embedded or loaded from files)
|
||||||
|
- `RandomProvider` (SeededRandom works offline)
|
||||||
|
|
||||||
|
### Potential Offline Package Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
mantimon-offline/
|
||||||
|
├── game/
|
||||||
|
│ ├── core/ # Direct copy of backend/app/core/
|
||||||
|
│ ├── campaign/ # NPC definitions, dialog, progression
|
||||||
|
│ │ ├── clubs/ # Club data (NPCs, leaders, rewards)
|
||||||
|
│ │ ├── dialog/ # NPC dialog trees
|
||||||
|
│ │ └── progression.py # Campaign state machine
|
||||||
|
│ ├── cards/ # Card definitions as JSON
|
||||||
|
│ │ ├── base_set.json
|
||||||
|
│ │ └── expansion_1.json
|
||||||
|
│ └── saves/ # Local save directory
|
||||||
|
│ └── slot_1.json
|
||||||
|
├── ui/ # Could reuse Phaser or use PyGame/Godot
|
||||||
|
├── assets/ # Card images, sounds
|
||||||
|
└── main.py # Entry point
|
||||||
|
```
|
||||||
|
|
||||||
|
### Packaging Options for Distribution
|
||||||
|
|
||||||
|
| Approach | Bundle Size | Complexity | UI Reuse |
|
||||||
|
|----------|-------------|------------|----------|
|
||||||
|
| **Electron + Python** | ~150MB | Medium | Full web UI |
|
||||||
|
| **Tauri + Python** | ~30MB | High | Full web UI |
|
||||||
|
| **PyInstaller + PyGame** | ~50MB | Low | New UI needed |
|
||||||
|
| **Godot + GDScript port** | ~40MB | Medium | New UI needed |
|
||||||
|
| **Nuitka + embedded browser** | ~80MB | Medium | Phaser reuse |
|
||||||
|
|
||||||
|
### What Stays, What Goes
|
||||||
|
|
||||||
|
**Keep (copy to fork):**
|
||||||
|
- `backend/app/core/` - Entire game engine
|
||||||
|
- `frontend/src/game/` - Phaser scenes (if using web UI)
|
||||||
|
- Card definitions (export from DB to JSON)
|
||||||
|
- Campaign/NPC data
|
||||||
|
|
||||||
|
**Remove (not needed offline):**
|
||||||
|
- `backend/app/api/` - REST endpoints
|
||||||
|
- `backend/app/websocket/` - Multiplayer sync
|
||||||
|
- `backend/app/services/` - DB-backed services
|
||||||
|
- Authentication system
|
||||||
|
- Matchmaking/lobby
|
||||||
|
|
||||||
|
### Development Guidelines
|
||||||
|
|
||||||
|
To maintain fork compatibility, follow these rules when developing the core engine:
|
||||||
|
|
||||||
|
1. **No imports from `app.api`, `app.websocket`, or `app.services`** in `app.core`
|
||||||
|
2. **No database session dependencies** in core engine functions
|
||||||
|
3. **Card definitions passed in**, not fetched - `GameEngine.create_game()` receives a card registry
|
||||||
|
4. **RNG injected via protocol** - `RandomProvider` allows swapping implementations
|
||||||
|
5. **State is self-contained** - `GameState` includes everything needed to resume a game
|
||||||
|
6. **No async required** - Core logic is sync; async wrappers added at service layer
|
||||||
|
|
||||||
|
### Testing Offline Viability
|
||||||
|
|
||||||
|
The existing test suite validates offline compatibility:
|
||||||
|
- All `tests/core/` tests run without database
|
||||||
|
- `SeededRandom` enables deterministic testing
|
||||||
|
- Card fixtures are created in-memory, not loaded from DB
|
||||||
|
|
||||||
|
### Future: Official Offline Release
|
||||||
|
|
||||||
|
If demand exists, an official offline version could be released:
|
||||||
|
1. Export campaign data and card definitions to JSON
|
||||||
|
2. Bundle core engine with a lightweight UI wrapper
|
||||||
|
3. Distribute as standalone executable (itch.io, Steam, etc.)
|
||||||
|
4. Optional: Steam Workshop for community card sets
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user