Files
echoes-of-the-ash/old/ACCOUNT_PLAYER_SEPARATION_PLAN.md
2025-11-27 16:27:01 +01:00

703 lines
16 KiB
Markdown

# Account & Player Separation - Major Refactor Plan
## Overview
Separate authentication (accounts) from gameplay (characters/players) to support:
- Multiple characters per account
- Free tier: 1 character
- Premium tier: Up to 10 characters
- Character customization at creation
- Email-based login (no username)
---
## 1. New Database Schema
### Accounts Table (Authentication)
```sql
CREATE TABLE accounts (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255), -- NULL for Steam/OAuth
steam_id VARCHAR(255) UNIQUE, -- Steam integration
account_type VARCHAR(20) DEFAULT 'web', -- 'web', 'steam'
premium_expires_at TIMESTAMP, -- NULL = lifetime premium
email_verified BOOLEAN DEFAULT FALSE,
email_verification_token VARCHAR(255),
password_reset_token VARCHAR(255),
password_reset_expires TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login_at TIMESTAMP,
CONSTRAINT check_account_type CHECK (account_type IN ('web', 'steam'))
);
CREATE INDEX idx_accounts_email ON accounts(email);
CREATE INDEX idx_accounts_steam_id ON accounts(steam_id);
```
### Characters Table (Gameplay)
```sql
CREATE TABLE characters (
id SERIAL PRIMARY KEY,
account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
name VARCHAR(100) UNIQUE NOT NULL, -- Character name (unique across all players)
avatar_data TEXT, -- JSON for avatar customization
-- RPG Stats
level INTEGER DEFAULT 1,
xp INTEGER DEFAULT 0,
hp INTEGER DEFAULT 100,
max_hp INTEGER DEFAULT 100,
stamina INTEGER DEFAULT 100,
max_stamina INTEGER DEFAULT 100,
-- Base Attributes (start with 0, player allocates 20 points)
strength INTEGER DEFAULT 0,
agility INTEGER DEFAULT 0,
endurance INTEGER DEFAULT 0,
intellect INTEGER DEFAULT 0,
unspent_points INTEGER DEFAULT 20, -- Initial stat points to allocate
-- Game State
location_id VARCHAR(255) DEFAULT 'cabin',
is_dead BOOLEAN DEFAULT FALSE,
last_movement_time REAL DEFAULT 0,
-- Timestamps
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT check_unspent_points CHECK (unspent_points >= 0)
);
CREATE INDEX idx_characters_account_id ON characters(account_id);
CREATE INDEX idx_characters_name ON characters(name);
CREATE INDEX idx_characters_location_id ON characters(location_id);
```
### Character Limits
```sql
-- Enforce character limits via application logic:
-- Free accounts: MAX 1 character
-- Premium accounts: MAX 10 characters
```
---
## 2. Avatar System
### Avatar Data Structure (JSON)
```json
{
"preset": "warrior", // Optional preset
"body": {
"skin_tone": "#f5d6c6",
"build": "athletic" // slim, athletic, heavy
},
"hair": {
"style": "short",
"color": "#3d2817"
},
"face": {
"eyes": "blue",
"facial_hair": "none"
},
"equipped_display": {
"helmet": "iron_helmet", // Shows equipped items on avatar
"armor": "leather_chest",
"weapon": "iron_sword"
}
}
```
### Avatar Options
**Phase 1 (MVP):** Simple presets
- 10 preset avatars (warrior, mage, rogue, etc.)
- Color variations
**Phase 2 (Future):** Dynamic avatar
- Shows equipped armor/weapons
- Customizable features
- Level-based cosmetic unlocks
---
## 3. Migration Script
### `migrate_account_player_separation.py`
```python
"""
Major migration: Separate accounts from characters
1. Create accounts table
2. Create characters table
3. Migrate existing players to new structure
4. Update all foreign keys
5. Drop old players table (after backup)
"""
Steps:
1. Backup current players table
2. Create accounts table
3. For each existing player:
- Create account with email (generate if missing)
- Create character from player data
- Migrate inventory, equipment, stats, etc.
4. Update all foreign key references:
- inventory.player_id -> character_id
- equipment.player_id -> character_id
- dropped_items references
- combat references
- etc.
5. Test thoroughly
6. Drop old players table
```
---
## 4. Authentication Flow Changes
### Registration (Email-based)
```
POST /api/auth/register
{
"email": "player@example.com",
"password": "securepass"
}
Response:
{
"access_token": "...",
"account": {
"id": 1,
"email": "player@example.com",
"account_type": "web",
"is_premium": false,
"characters": [] // Empty on first register
},
"needs_character_creation": true
}
```
### Login (Email-based)
```
POST /api/auth/login
{
"email": "player@example.com",
"password": "securepass"
}
Response:
{
"access_token": "...",
"account": {...},
"characters": [
{
"id": 1,
"name": "Aragorn",
"level": 15,
"avatar_data": {...},
"last_played_at": "2025-11-09T..."
}
]
}
```
### Character Selection
```
POST /api/character/select
{
"character_id": 1
}
Response:
{
"character": {...full character data...},
"location": {...},
"inventory": [...],
"equipment": {...}
}
```
### Character Creation
```
POST /api/character/create
{
"name": "Aragorn",
"avatar": {
"preset": "warrior",
...
},
"stats": {
"strength": 8,
"agility": 5,
"endurance": 4,
"intellect": 3
} // Must total 20 points
}
Validation:
- Free users: Check character count < 1
- Premium users: Check character count < 10
- Name must be unique
- Stats must total exactly 20
```
---
## 5. JWT Token Structure
### Old (Current)
```json
{
"player_id": 1,
"exp": 1699564800
}
```
### New
```json
{
"account_id": 1,
"character_id": 5, // Set after character selection
"account_type": "web",
"is_premium": false,
"exp": 1699564800
}
```
**Flow:**
1. Login → Get token with `account_id`, no `character_id`
2. Select character → New token with `character_id`
3. All game endpoints require `character_id` in token
---
## 6. UI Changes Required
### A. Login/Register Screen Redesign
**Current:** Simple form
**New:** Modern authentication UI
```tsx
<AuthScreen>
<Tabs>
<Tab label="Login">
<EmailInput />
<PasswordInput />
<Button>Login</Button>
<Link>Forgot Password?</Link>
</Tab>
<Tab label="Register">
<EmailInput />
<PasswordInput />
<PasswordConfirmInput />
<Checkbox>I agree to Terms</Checkbox>
<Button>Create Account</Button>
</Tab>
</Tabs>
<Divider />
<SteamLoginButton /> // Future
</AuthScreen>
```
**Design:**
- Dark fantasy theme
- Animated background (subtle fire/ash effects)
- Elden Ring / Dark Souls inspired
- Responsive (mobile-first)
### B. Character Selection Screen
```tsx
<CharacterSelection>
<Header>
<AccountInfo email={account.email} />
<PremiumBadge if={isPremium} />
</Header>
<CharacterGrid>
{characters.map(char => (
<CharacterCard
key={char.id}
name={char.name}
level={char.level}
avatar={char.avatar_data}
lastPlayed={char.last_played_at}
onClick={() => selectCharacter(char.id)}
/>
))}
{canCreateMore && (
<CreateCharacterCard
onClick={() => setShowCreation(true)}
/>
)}
</CharacterGrid>
{!isPremium && characters.length >= 1 && (
<UpgradeBanner>
Upgrade to Premium for 9 more character slots!
</UpgradeBanner>
)}
</CharacterSelection>
```
### C. Character Creation Screen
```tsx
<CharacterCreation>
<Step1_Name>
<Input
placeholder="Enter character name"
validation={checkNameUnique}
/>
</Step1_Name>
<Step2_Avatar>
<AvatarPreview avatar={selectedAvatar} />
<AvatarPresets>
{presets.map(preset => (
<PresetCard
key={preset.id}
image={preset.thumbnail}
label={preset.name}
onClick={() => setAvatar(preset)}
/>
))}
</AvatarPresets>
</Step2_Avatar>
<Step3_Stats>
<StatAllocator
remaining={pointsRemaining}
stats={stats}
onAllocate={(stat, amount) => allocateStat(stat, amount)}
/>
<StatsPreview>
<Stat name="Strength" value={stats.strength} />
<Stat name="Agility" value={stats.agility} />
<Stat name="Endurance" value={stats.endurance} />
<Stat name="Intellect" value={stats.intellect} />
</StatsPreview>
<PointsRemaining>{pointsRemaining} / 20</PointsRemaining>
</Step3_Stats>
<Actions>
<Button onClick={handleBack}>Back</Button>
<Button
onClick={handleCreate}
disabled={!isValid}
primary
>
Create Character
</Button>
</Actions>
</CharacterCreation>
```
---
## 7. Steam Integration Specifics
### Do You Need Two Executables?
**Answer: NO, one executable with runtime detection**
```typescript
// At app startup
const config = {
isSteam: checkSteamRuntime(), // Detect Steam overlay
apiUrl: process.env.API_URL || 'https://api.game.com',
steamAppId: process.env.STEAM_APP_ID
};
if (config.isSteam) {
// Initialize Steamworks
await initSteamworks();
// Auto-login with Steam
const steamTicket = await getSteamAuthTicket();
const authResponse = await api.post('/api/auth/steam/login', {
steam_ticket: steamTicket
});
// Skip email/password login, go straight to character selection
} else {
// Show email/password login
}
```
**Build Configuration:**
```json
{
"builds": {
"web": {
"platform": "web",
"steamworks": false
},
"steam-windows": {
"platform": "windows",
"steamworks": true,
"steam_app_id": "1000000"
},
"steam-linux": {
"platform": "linux",
"steamworks": true
},
"standalone-windows": {
"platform": "windows",
"steamworks": false
}
}
}
```
---
## 8. Tauri Build Setup
### Project Structure
```
echoes-desktop/
├── src-tauri/
│ ├── src/
│ │ ├── main.rs
│ │ ├── steam.rs # Steamworks integration
│ │ ├── auth.rs # Authentication logic
│ │ └── storage.rs # Local storage/cache
│ ├── icons/
│ ├── Cargo.toml
│ └── tauri.conf.json
├── src/ # Frontend (React)
│ ├── components/
│ ├── screens/
│ │ ├── Auth.tsx # Login/Register
│ │ ├── CharacterSelect.tsx
│ │ ├── CharacterCreate.tsx
│ │ └── Game.tsx
│ └── main.tsx
├── assets/ # Bundled assets
└── package.json
```
### Installation Steps
```bash
# 1. Install Tauri CLI
cargo install tauri-cli
# 2. Create Tauri project
npm create tauri-app
# 3. Configure build
```
### tauri.conf.json
```json
{
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devPath": "http://localhost:5173",
"distDir": "../dist"
},
"package": {
"productName": "Echoes of the Ashes",
"version": "1.0.0"
},
"tauri": {
"allowlist": {
"all": false,
"fs": {
"scope": ["$APPDATA/echoes-of-ashes/*"]
},
"http": {
"scope": ["https://api.echoesoftheash.com/*"]
}
},
"bundle": {
"active": true,
"targets": ["msi", "app", "deb"], // Windows, Mac, Linux
"identifier": "com.echoesoftheash.game",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": ["assets/*"], // Bundle game assets
"externalBin": ["bin/steamworks"], // Steam DLL
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": "default-src 'self'; connect-src 'self' https://api.echoesoftheash.com"
},
"updater": {
"active": true,
"endpoints": [
"https://releases.echoesoftheash.com/{{target}}/{{current_version}}"
],
"dialog": true,
"pubkey": "YOUR_PUBLIC_KEY"
}
}
}
```
### Build Commands
```json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build",
"tauri:build:steam": "STEAM_ENABLED=true tauri build",
"tauri:build:standalone": "STEAM_ENABLED=false tauri build"
}
}
```
### Steamworks Integration (Rust)
```rust
// src-tauri/src/steam.rs
use steamworks::Client;
pub struct SteamManager {
client: Option<Client>,
}
impl SteamManager {
pub fn new(app_id: u32) -> Result<Self, String> {
match Client::init_app(app_id) {
Ok((client, _single)) => {
Ok(Self { client: Some(client) })
}
Err(e) => Err(format!("Failed to init Steam: {:?}", e))
}
}
pub fn get_steam_id(&self) -> Option<u64> {
self.client.as_ref().map(|c| {
c.user().steam_id().raw()
})
}
pub fn get_auth_session_ticket(&self) -> Option<Vec<u8>> {
// Implementation
None
}
}
```
---
## 9. Implementation Phases
### Phase 1: Database Refactor (Week 1)
- [ ] Create migration script
- [ ] Test migration on dev database
- [ ] Create accounts + characters tables
- [ ] Migrate existing data
- [ ] Update all FK references
- [ ] Test thoroughly
### Phase 2: Auth System (Week 1-2)
- [ ] Email-based login/register
- [ ] JWT with account_id + character_id
- [ ] Character selection endpoint
- [ ] Character creation endpoint
- [ ] Character limit enforcement
### Phase 3: UI Redesign (Week 2-3)
- [ ] New login/register screen
- [ ] Character selection screen
- [ ] Character creation screen
- [ ] Avatar system (presets)
- [ ] Stat allocation UI
### Phase 4: Steam Integration (Week 3-4)
- [ ] Set up Steamworks SDK
- [ ] Steam authentication backend
- [ ] Steam auto-login flow
- [ ] Test on Steam
### Phase 5: Tauri Desktop (Week 4-5)
- [ ] Set up Tauri project
- [ ] Asset bundling
- [ ] Build pipeline
- [ ] Steam runtime detection
- [ ] Auto-updater
- [ ] Test builds (Win/Mac/Linux)
### Phase 6: Testing & Polish (Week 5-6)
- [ ] End-to-end testing
- [ ] Performance optimization
- [ ] Bug fixes
- [ ] Documentation
- [ ] Beta release
---
## 10. Breaking Changes & Risks
### Database
- **MAJOR:** Complete schema change
- **Risk:** Data loss if migration fails
- **Mitigation:** Full backup before migration, rollback plan
### Authentication
- **MAJOR:** Login now uses email, not username
- **Risk:** Existing users can't login
- **Mitigation:** Send email to all users about change
### API
- **MAJOR:** Most endpoints change from player_id to character_id
- **Risk:** All API clients break
- **Mitigation:** Version API (v2), deprecate v1
### Frontend
- **MAJOR:** Complete auth flow redesign
- **Risk:** UX confusion
- **Mitigation:** Tutorial on first login after update
---
## 11. Rollback Plan
If migration fails:
1. Restore database from backup
2. Revert code changes
3. Restart containers with old version
4. Investigate issue
5. Fix and retry
**Backup Strategy:**
```bash
# Before migration
docker exec echoes_of_the_ashes_db pg_dump -U postgres gamedb > backup_$(date +%Y%m%d).sql
# Restore if needed
docker exec -i echoes_of_the_ashes_db psql -U postgres gamedb < backup_20251109.sql
```
---
## Next Steps
1. **Review this plan** - Confirm approach
2. **Create detailed migration script** - Handle all edge cases
3. **Set up dev environment** - Test migration there first
4. **Implement Phase 1** - Database refactor
5. **Update authentication** - Email-based login
6. **Build UI screens** - Character selection/creation
7. **Integrate Steam** - Steamworks SDK
8. **Create Tauri build** - Desktop client
**Estimated Timeline:** 6 weeks full-time
**Do you want me to start implementing Phase 1 (database refactor)?**