Commit
This commit is contained in:
564
old/STEAM_AND_PREMIUM_PLAN.md
Normal file
564
old/STEAM_AND_PREMIUM_PLAN.md
Normal file
@@ -0,0 +1,564 @@
|
||||
# Steam Integration & Premium System Plan
|
||||
|
||||
## Overview
|
||||
Transform Echoes of the Ashes into a premium game with multiple distribution channels:
|
||||
- **Web Version**: Free trial with level cap, upgrade to premium
|
||||
- **Steam Version**: Full game, integrated authentication
|
||||
- **Standalone Executable**: Bundled client for Windows/Mac/Linux
|
||||
|
||||
---
|
||||
|
||||
## 1. Account System
|
||||
|
||||
### Account Types
|
||||
```sql
|
||||
account_type ENUM('web', 'steam')
|
||||
```
|
||||
|
||||
- **web**: Email/password registration, optional premium upgrade
|
||||
- **steam**: Auto-premium via Steam ownership verification
|
||||
|
||||
### Premium System
|
||||
```sql
|
||||
premium_expires_at TIMESTAMP NULL
|
||||
```
|
||||
|
||||
- **NULL** = Lifetime premium (Steam users, purchased premium)
|
||||
- **Timestamp** = Free trial expires at this time
|
||||
- **Non-premium restrictions**:
|
||||
- Level cap at 10
|
||||
- No XP gain after level 10
|
||||
- Full map access (level-gated naturally by difficulty)
|
||||
- Can play with premium users
|
||||
|
||||
### Database Schema
|
||||
```sql
|
||||
CREATE TABLE players (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(255) UNIQUE NOT NULL,
|
||||
email VARCHAR(255), -- Required for web, NULL for steam
|
||||
password_hash VARCHAR(255), -- Required for web, NULL for steam
|
||||
steam_id VARCHAR(255) UNIQUE, -- Steam ID for steam users
|
||||
account_type VARCHAR(20) DEFAULT 'web',
|
||||
premium_expires_at TIMESTAMP NULL, -- NULL = premium forever
|
||||
-- ... existing fields ...
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Distribution Channels
|
||||
|
||||
### A. Web Version (Current)
|
||||
|
||||
**Registration Flow:**
|
||||
```
|
||||
POST /api/auth/register
|
||||
{
|
||||
"username": "player123",
|
||||
"email": "player@example.com", // NEW: Required
|
||||
"password": "securepass"
|
||||
}
|
||||
```
|
||||
|
||||
**Premium Upgrade:**
|
||||
```
|
||||
POST /api/payment/upgrade-premium
|
||||
{
|
||||
"payment_method": "stripe|paypal|crypto",
|
||||
"payment_token": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### B. Steam Version
|
||||
|
||||
**Architecture:**
|
||||
```
|
||||
[Steam Client] <-> [Steamworks API] <-> [Game Server]
|
||||
|
|
||||
[Steam Auth]
|
||||
```
|
||||
|
||||
**Steam Authentication Flow:**
|
||||
1. User launches game via Steam
|
||||
2. Game requests Steam session ticket via Steamworks SDK
|
||||
3. Client sends ticket to game server
|
||||
4. Server validates ticket with Steam API
|
||||
5. Server creates/logs in user with `steam_id`
|
||||
6. Premium status = automatic (owns game on Steam)
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
POST /api/auth/steam/login
|
||||
{
|
||||
"steam_ticket": "...",
|
||||
"steam_id": "76561198..."
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"access_token": "...",
|
||||
"player": {...},
|
||||
"is_premium": true, // Always true for Steam
|
||||
"account_type": "steam"
|
||||
}
|
||||
```
|
||||
|
||||
### C. Standalone Executable
|
||||
|
||||
**Two Approaches:**
|
||||
|
||||
#### Option 1: Electron/Tauri App (Recommended)
|
||||
**Pros:**
|
||||
- Web technologies (reuse existing PWA)
|
||||
- Easy to bundle assets
|
||||
- Cross-platform (Windows, Mac, Linux)
|
||||
- Auto-updates
|
||||
- Local caching
|
||||
|
||||
**Cons:**
|
||||
- Larger download size (~100-200MB with bundled assets)
|
||||
|
||||
**Tech Stack:**
|
||||
- **Tauri** (Rust + Web) - smaller, more secure
|
||||
- **Electron** (Node + Web) - more mature, larger
|
||||
|
||||
**Architecture:**
|
||||
```
|
||||
[Desktop App]
|
||||
├── Bundled Assets/
|
||||
│ ├── images/ (all icons, locations, NPCs)
|
||||
│ ├── data/ (items.json, npcs.json, etc.)
|
||||
│ └── sounds/ (future)
|
||||
├── Local Cache/
|
||||
│ └── user_data/
|
||||
└── API Client -> Game Server
|
||||
```
|
||||
|
||||
#### Option 2: Native Build (Godot/Unity)
|
||||
**Pros:**
|
||||
- True native performance
|
||||
- Better for future 3D/advanced graphics
|
||||
- Full offline capability
|
||||
|
||||
**Cons:**
|
||||
- Complete rewrite
|
||||
- Different tech stack from web
|
||||
- More maintenance
|
||||
|
||||
**Recommendation:** Start with **Tauri** for MVP
|
||||
|
||||
---
|
||||
|
||||
## 3. Asset Bundling Strategy
|
||||
|
||||
### Bundled Assets (Standalone)
|
||||
```
|
||||
app/assets/
|
||||
├── icons/
|
||||
│ ├── items/
|
||||
│ ├── ui/
|
||||
│ ├── status/
|
||||
│ └── actions/
|
||||
├── images/
|
||||
│ ├── locations/
|
||||
│ ├── npcs/
|
||||
│ └── interactables/
|
||||
├── data/
|
||||
│ ├── items.json
|
||||
│ ├── npcs.json
|
||||
│ ├── locations.json
|
||||
│ └── interactables.json
|
||||
└── sounds/ (future)
|
||||
```
|
||||
|
||||
### Caching Strategy
|
||||
|
||||
**On First Launch:**
|
||||
1. Check local cache version
|
||||
2. If outdated, download latest assets
|
||||
3. Store in local cache with version tag
|
||||
4. Future launches use cache
|
||||
|
||||
**Update Mechanism:**
|
||||
```json
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"assets": {
|
||||
"icons": "v1.2.0",
|
||||
"images": "v1.1.5",
|
||||
"data": "v1.2.0"
|
||||
},
|
||||
"cdn_url": "https://cdn.echoesoftheash.com"
|
||||
}
|
||||
```
|
||||
|
||||
**Hybrid Approach (Best):**
|
||||
1. **Bundle core assets** (~50MB):
|
||||
- Essential UI icons
|
||||
- Starting location images
|
||||
- Common item icons
|
||||
|
||||
2. **Lazy load on demand**:
|
||||
- High-level location images
|
||||
- Rare item icons
|
||||
- NPC portraits
|
||||
|
||||
3. **Cache everything locally**
|
||||
4. **Check for updates daily**
|
||||
|
||||
---
|
||||
|
||||
## 4. Steam Integration Technical Details
|
||||
|
||||
### Steamworks SDK Integration
|
||||
|
||||
**1. Setup:**
|
||||
```bash
|
||||
# Download Steamworks SDK
|
||||
# https://partner.steamgames.com/
|
||||
|
||||
# Install in project
|
||||
steamworks-sdk/
|
||||
├── sdk/
|
||||
│ ├── public/
|
||||
│ │ └── steam/
|
||||
│ │ └── steam_api.h
|
||||
│ └── redistributable_bin/
|
||||
│ ├── steam_api.dll (Windows)
|
||||
│ ├── libsteam_api.so (Linux)
|
||||
│ └── libsteam_api.dylib (Mac)
|
||||
└── tools/
|
||||
```
|
||||
|
||||
**2. Client-Side (Game Launch):**
|
||||
```cpp
|
||||
// Initialize Steam API
|
||||
if (!SteamAPI_Init()) {
|
||||
return ERROR_STEAM_NOT_RUNNING;
|
||||
}
|
||||
|
||||
// Get Steam ID
|
||||
CSteamID steamID = SteamUser()->GetSteamID();
|
||||
uint64 steamID64 = steamID.ConvertToUint64();
|
||||
|
||||
// Request auth ticket
|
||||
HAuthTicket hAuthTicket;
|
||||
uint8 rgubTicket[1024];
|
||||
uint32 pcbTicket;
|
||||
|
||||
hAuthTicket = SteamUser()->GetAuthSessionTicket(
|
||||
rgubTicket,
|
||||
sizeof(rgubTicket),
|
||||
&pcbTicket
|
||||
);
|
||||
|
||||
// Convert to hex string and send to server
|
||||
std::string ticket = BytesToHex(rgubTicket, pcbTicket);
|
||||
```
|
||||
|
||||
**3. Server-Side Validation:**
|
||||
```python
|
||||
import requests
|
||||
|
||||
async def validate_steam_ticket(steam_id: str, ticket: str) -> bool:
|
||||
"""Validate Steam auth ticket with Steam API"""
|
||||
url = "https://api.steampowered.com/ISteamUserAuth/AuthenticateUserTicket/v1/"
|
||||
params = {
|
||||
"key": STEAM_WEB_API_KEY, # Get from Steamworks Partner
|
||||
"appid": YOUR_STEAM_APP_ID,
|
||||
"ticket": ticket
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params)
|
||||
data = response.json()
|
||||
|
||||
if data['response']['params']['result'] == 'OK':
|
||||
validated_steam_id = data['response']['params']['steamid']
|
||||
return validated_steam_id == steam_id
|
||||
|
||||
return False
|
||||
```
|
||||
|
||||
### Steam Build Configuration
|
||||
|
||||
**1. Depot Configuration** (`depot_build_1001.vdf`):
|
||||
```vdf
|
||||
"DepotBuildConfig"
|
||||
{
|
||||
"DepotID" "1001"
|
||||
"ContentRoot" "..\build\steam\"
|
||||
"FileMapping"
|
||||
{
|
||||
"LocalPath" "*"
|
||||
"DepotPath" "."
|
||||
"recursive" "1"
|
||||
}
|
||||
"FileExclusion" "*.pdb"
|
||||
}
|
||||
```
|
||||
|
||||
**2. App Build Script** (`app_build_1000.vdf`):
|
||||
```vdf
|
||||
"AppBuild"
|
||||
{
|
||||
"AppID" "1000" // Your Steam App ID
|
||||
"Desc" "Echoes of the Ashes Build"
|
||||
"BuildOutput" "..\output\"
|
||||
"ContentRoot" "..\build\"
|
||||
"SetLive" "default"
|
||||
"Depots"
|
||||
{
|
||||
"1001" "depot_build_1001.vdf"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. Upload Script:**
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# build_and_upload_steam.sh
|
||||
|
||||
# Build the game
|
||||
npm run build:steam
|
||||
|
||||
# Upload to Steam
|
||||
steamcmd +login $STEAM_USERNAME +run_app_build app_build_1000.vdf +quit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Build Variants
|
||||
|
||||
### Configuration System
|
||||
|
||||
**config/builds.json:**
|
||||
```json
|
||||
{
|
||||
"web": {
|
||||
"api_url": "https://api.echoesoftheash.com",
|
||||
"bundled_assets": false,
|
||||
"steam_enabled": false,
|
||||
"premium_required": false
|
||||
},
|
||||
"steam": {
|
||||
"api_url": "https://api.echoesoftheash.com",
|
||||
"bundled_assets": true,
|
||||
"steam_enabled": true,
|
||||
"premium_required": false, // Validated by ownership
|
||||
"app_id": "1000000"
|
||||
},
|
||||
"standalone": {
|
||||
"api_url": "https://api.echoesoftheash.com",
|
||||
"bundled_assets": true,
|
||||
"steam_enabled": false,
|
||||
"premium_required": false, // Check via API
|
||||
"auto_update": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Build Commands
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build:web": "BUILD_TARGET=web vite build",
|
||||
"build:steam": "BUILD_TARGET=steam tauri build --target steam",
|
||||
"build:standalone": "BUILD_TARGET=standalone tauri build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Premium Enforcement
|
||||
|
||||
### XP Gain Restriction
|
||||
|
||||
```python
|
||||
async def award_xp(player_id: int, xp_amount: int):
|
||||
player = await db.get_player_by_id(player_id)
|
||||
|
||||
# Check premium status
|
||||
is_premium = await is_player_premium(player)
|
||||
|
||||
if not is_premium and player['level'] >= 10:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "Free trial limited to level 10. Upgrade to premium to continue!",
|
||||
"xp_gained": 0,
|
||||
"upgrade_url": "/premium/upgrade"
|
||||
}
|
||||
|
||||
# Award XP normally
|
||||
new_xp = player['xp'] + xp_amount
|
||||
await db.update_player(player_id, xp=new_xp)
|
||||
# ... level up logic ...
|
||||
```
|
||||
|
||||
### Helper Functions
|
||||
|
||||
```python
|
||||
async def is_player_premium(player: dict) -> bool:
|
||||
"""Check if player has premium access"""
|
||||
# Steam users are always premium
|
||||
if player['account_type'] == 'steam':
|
||||
return True
|
||||
|
||||
# NULL = lifetime premium
|
||||
if player['premium_expires_at'] is None:
|
||||
# Check if they ever had premium (distinguish from never-premium)
|
||||
# Could add a 'premium_granted_at' field
|
||||
return False # Or check another field
|
||||
|
||||
# Check if premium expired
|
||||
from datetime import datetime
|
||||
if player['premium_expires_at'] > datetime.utcnow():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def grant_premium(player_id: int, duration_days: int = None):
|
||||
"""Grant premium access to a player"""
|
||||
if duration_days is None:
|
||||
# Lifetime premium
|
||||
await db.update_player(player_id, premium_expires_at=None)
|
||||
else:
|
||||
# Time-limited premium
|
||||
from datetime import datetime, timedelta
|
||||
expires_at = datetime.utcnow() + timedelta(days=duration_days)
|
||||
await db.update_player(player_id, premium_expires_at=expires_at)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Implementation Roadmap
|
||||
|
||||
### Phase 1: Foundation (Current Sprint)
|
||||
- [x] Add database columns (steam_id, email, premium_expires_at, account_type)
|
||||
- [ ] Update registration to require email
|
||||
- [ ] Add premium check helper functions
|
||||
- [ ] Implement XP gain restriction for non-premium level 10+
|
||||
- [ ] Create icon folder structure
|
||||
|
||||
### Phase 2: Premium System (Next Sprint)
|
||||
- [ ] Payment integration (Stripe/PayPal)
|
||||
- [ ] Premium upgrade endpoint
|
||||
- [ ] Premium status UI indicators
|
||||
- [ ] Email verification system
|
||||
- [ ] Password reset flow
|
||||
|
||||
### Phase 3: Steam Integration
|
||||
- [ ] Set up Steamworks partner account
|
||||
- [ ] Integrate Steamworks SDK
|
||||
- [ ] Implement Steam authentication
|
||||
- [ ] Create Steam build pipeline
|
||||
- [ ] Test Steam authentication flow
|
||||
|
||||
### Phase 4: Standalone Client
|
||||
- [ ] Choose framework (Tauri recommended)
|
||||
- [ ] Set up project structure
|
||||
- [ ] Implement asset bundling
|
||||
- [ ] Add auto-update mechanism
|
||||
- [ ] Create installers (Windows, Mac, Linux)
|
||||
|
||||
### Phase 5: Polish & Launch
|
||||
- [ ] Replace emojis with custom icons
|
||||
- [ ] Optimize asset loading
|
||||
- [ ] Add caching layer
|
||||
- [ ] Performance testing
|
||||
- [ ] Beta testing
|
||||
- [ ] Official launch
|
||||
|
||||
---
|
||||
|
||||
## 8. Recommended Tech Stack
|
||||
|
||||
### For Standalone Executable: **Tauri**
|
||||
|
||||
**Why Tauri:**
|
||||
- Smaller bundle size (~3-5MB vs 100MB+ for Electron)
|
||||
- Uses system WebView (Chromium on Windows, Safari on Mac)
|
||||
- Rust backend = better security
|
||||
- Built-in auto-updater
|
||||
- Native system integration
|
||||
- Active development
|
||||
|
||||
**Project Structure:**
|
||||
```
|
||||
echoes-desktop/
|
||||
├── src-tauri/ # Rust backend
|
||||
│ ├── src/
|
||||
│ │ ├── main.rs # Entry point
|
||||
│ │ ├── steam.rs # Steam integration
|
||||
│ │ └── storage.rs # Local storage
|
||||
│ ├── icons/
|
||||
│ └── tauri.conf.json
|
||||
├── src/ # Frontend (reuse existing PWA)
|
||||
│ ├── components/
|
||||
│ ├── hooks/
|
||||
│ └── main.tsx
|
||||
├── assets/ # Bundled assets
|
||||
│ ├── icons/
|
||||
│ ├── images/
|
||||
│ └── data/
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
npm create tauri-app
|
||||
# Choose: React + TypeScript
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Cost Estimates
|
||||
|
||||
### Development
|
||||
- Steam Steamworks SDK: **Free** (requires $100 one-time app submission fee)
|
||||
- Payment Processing: **2.9% + $0.30 per transaction** (Stripe)
|
||||
- CDN for assets: **~$5-20/month** (CloudFlare, AWS CloudFront)
|
||||
|
||||
### Infrastructure
|
||||
- Current server: **Existing**
|
||||
- Steam bandwidth: **Free** (Valve covers)
|
||||
- Asset CDN: **$10-50/month** (depending on traffic)
|
||||
|
||||
---
|
||||
|
||||
## 10. Monetization Strategy
|
||||
|
||||
### Pricing Options
|
||||
|
||||
**Option A: One-time Purchase**
|
||||
- **$9.99** - Full game unlock
|
||||
- No subscription, lifetime access
|
||||
- Recommended for indie games
|
||||
|
||||
**Option B: Freemium with Optional Premium**
|
||||
- **Free**: Level 1-10, unlimited play
|
||||
- **$4.99**: Full unlock
|
||||
- Easier onboarding
|
||||
|
||||
**Option C: Steam-Only Paid**
|
||||
- **$14.99** on Steam (full game)
|
||||
- Free web version with level cap
|
||||
- Drive Steam sales
|
||||
|
||||
**Recommendation:** **Option C** - Premium on Steam, free trial on web
|
||||
|
||||
---
|
||||
|
||||
## Next Immediate Steps
|
||||
|
||||
1. **Update registration endpoint** (add email requirement)
|
||||
2. **Add premium helper functions** to codebase
|
||||
3. **Implement XP restriction** for level 10+ free users
|
||||
4. **Create payment integration** (Stripe)
|
||||
5. **Design icon system** and start replacing emojis
|
||||
6. **Set up Steamworks partner account** (long lead time)
|
||||
7. **Prototype Tauri desktop app** (weekend project)
|
||||
|
||||
Would you like me to start implementing any specific part of this plan?
|
||||
Reference in New Issue
Block a user