# Game Optimization Strategy

## Current Performance Analysis

### Polling Overhead (Every 5 seconds)
Current polling fetches **5 endpoints simultaneously**:
1. `/api/game/state` - ~1.3KB+ (inventory, equipment, dropped items, player data)
2. `/api/game/location` - Location data
3. `/api/game/profile` - Player profile
4. `/api/game/combat` - Combat status
5. `/api/game/pvp/status` - PvP combat status

**Problems:**
- Inventory sent every poll (~1.3KB) even though it rarely changes
- Equipment sent every poll even though it rarely changes
- Dropped items fetched every poll even though location doesn't change often
- Duplicate data across endpoints (player data in multiple responses)

---

## Optimization Strategy

### Phase 1: Smart Polling - Only Fetch What Changes

#### A. **Lightweight Polling Endpoint** (Every 5s)
Create `/api/game/state/minimal` that returns ONLY data that changes frequently:

```json
{
  "player": {
    "hp": 100,
    "stamina": 95,
    "location_id": "ruins_entrance",
    "movement_cooldown": 2
  },
  "in_combat": false,
  "in_pvp_combat": false,
  "location_changed": false,  // Flag if location changed
  "inventory_changed": false, // Flag if inventory changed
  "equipment_changed": false, // Flag if equipment changed
  "last_update_timestamp": 1699380123.456
}
```

**Size estimate:** ~200 bytes vs current 1.5KB+ = **87% reduction**

#### B. **On-Demand Fetching**
Only fetch full data when flags indicate changes or after specific actions:

**Inventory** - Fetch only when:
- `inventory_changed` flag is true
- After pickup/drop/craft/use actions
- After equipment changes
- On initial load

**Equipment** - Fetch only when:
- `equipment_changed` flag is true
- After equip/unequip actions
- On initial load

**Location Details** - Fetch only when:
- `location_changed` flag is true
- After movement
- On initial load

**Dropped Items** - Fetch only when:
- Location changes
- After dropping items
- After picking up items

#### C. **Change Tracking in Database**
Add columns to `players` table:
```sql
ALTER TABLE players ADD COLUMN inventory_version INTEGER DEFAULT 0;
ALTER TABLE players ADD COLUMN equipment_version INTEGER DEFAULT 0;
ALTER TABLE players ADD COLUMN last_state_change FLOAT DEFAULT 0;
```

Increment versions when:
- Inventory changes: `inventory_version++`
- Equipment changes: `equipment_version++`

Frontend caches versions and only re-fetches if version changed.

---

### Phase 2: Delta Updates (Advanced)

**Pros:**
- Even smaller payload (only changes)
- Extremely efficient for large inventories

**Cons:**
- More complex implementation
- Need to handle edge cases (out-of-sync states)
- Need full refresh mechanism as fallback

**Verdict:** **Not recommended initially**
- Current optimization (Phase 1) will give 85%+ bandwidth reduction
- Delta updates add complexity for diminishing returns
- Only consider if player base grows significantly (10k+ concurrent users)

---

### Phase 3: WebSocket for Real-Time Updates (Future)

Instead of polling, use WebSocket for:
- Push notifications when state changes
- Real-time PvP combat updates
- Instant location updates from other players
- Server broadcasts (events, announcements)

**Benefits:**
- Zero polling overhead
- True real-time experience
- Server can push updates only when needed

**Implementation complexity:** Medium-High

---

## Immediate Action Plan

### 1. Create Minimal State Endpoint (1-2 hours)

**File:** `api/main.py`
```python
@app.get("/api/game/state/minimal")
async def get_minimal_state(current_user: dict = Depends(get_current_user)):
    """Lightweight endpoint for polling - returns only frequently changing data"""
    player = await db.get_player_by_id(current_user['id'])
    
    # Calculate movement cooldown
    import time
    current_time = time.time()
    last_movement = player.get('last_movement_time', 0)
    movement_cooldown = max(0, 5 - (current_time - last_movement))
    
    # Check if data changed since last poll
    # Client sends last known versions, server returns flags
    inventory_version = player.get('inventory_version', 0)
    equipment_version = player.get('equipment_version', 0)
    
    return {
        "player": {
            "hp": player['hp'],
            "max_hp": player['max_hp'],
            "stamina": player['stamina'],
            "max_stamina": player['max_stamina'],
            "location_id": player['location_id'],
            "movement_cooldown": int(movement_cooldown),
            "is_dead": player.get('is_dead', False)
        },
        "versions": {
            "inventory": inventory_version,
            "equipment": equipment_version
        },
        "timestamp": current_time
    }
```

### 2. Update Frontend Polling Logic (2-3 hours)

**File:** `pwa/src/components/Game.tsx`

```typescript
// Cache for full data
const [cachedInventory, setCachedInventory] = useState(null)
const [cachedEquipment, setCachedEquipment] = useState(null)
const [lastVersions, setLastVersions] = useState({ inventory: 0, equipment: 0 })

const fetchMinimalState = async () => {
  const res = await api.get('/api/game/state/minimal')
  
  // Update HP/Stamina/Location immediately
  setPlayerState(prev => ({
    ...prev,
    health: res.data.player.hp,
    stamina: res.data.player.stamina,
    location_id: res.data.player.location_id
  }))
  
  // Check if inventory changed
  if (res.data.versions.inventory !== lastVersions.inventory) {
    await fetchFullInventory()
    setLastVersions(prev => ({ ...prev, inventory: res.data.versions.inventory }))
  }
  
  // Check if equipment changed
  if (res.data.versions.equipment !== lastVersions.equipment) {
    await fetchFullEquipment()
    setLastVersions(prev => ({ ...prev, equipment: res.data.versions.equipment }))
  }
}
```

### 3. Add Version Tracking to Database Functions (1 hour)

Update these functions to increment versions:
- `add_item_to_inventory()` - increment `inventory_version`
- `remove_inventory_item()` - increment `inventory_version`
- `equip_item()` - increment `equipment_version`
- `unequip_item()` - increment `equipment_version`

### 4. Keep Full State Fetch for Actions (Already done!)

After actions (pickup, craft, equip, etc.), fetch full data:
```typescript
await handlePickup(itemId)
await fetchFullInventory() // Refresh immediately after action
```

---

## Expected Performance Gains

### Bandwidth Reduction
- **Current:** 1.5KB every 5s = **18KB/minute** per player
- **Optimized:** 200 bytes every 5s = **2.4KB/minute** per player
- **Savings:** **87% reduction** in polling bandwidth

### Server Load Reduction
- **Database queries per poll:**
  - Current: 8-12 queries (inventory items, equipment, dropped items, etc.)
  - Optimized: 1 query (player state only)
- **CPU usage:** ~80% reduction per poll
- **Memory:** Significant reduction from not loading/serializing inventory every poll

### Scalability
- **Current:** ~100 concurrent users max
- **Optimized:** ~800+ concurrent users with same resources

---

## Steam Integration & Monetization

### Can you release on Steam?
**YES!** Your game architecture is perfect for Steam:
- Progressive Web App can be packaged as desktop app
- Electron or Tauri wrapper around your PWA
- Steamworks SDK integration is straightforward

### Integration Steps

#### 1. **Package as Desktop App** (Choose one)

**Option A: Electron (Most common)**
```json
{
  "name": "echoes-of-the-ashes",
  "main": "main.js",
  "dependencies": {
    "electron": "^27.0.0"
  }
}
```

**Option B: Tauri (More efficient, Rust-based)**
- Smaller bundle size
- Better performance
- Rust backend integration

#### 2. **Integrate Steamworks**
```typescript
// Steam initialization
const steamworks = require('steamworks.js')

// Initialize Steam client
if (steamworks.init()) {
  const steamId = steamworks.localplayer.getSteamId()
  const username = steamworks.localplayer.getName()
  
  // Use Steam ID for authentication
  await api.post('/api/auth/steam', { 
    steamId, 
    username,
    ticket: steamworks.auth.getSessionTicket()
  })
}
```

#### 3. **Two-Version Strategy (Free vs Premium)**

**Database Schema:**
```sql
ALTER TABLE players ADD COLUMN account_type VARCHAR(20) DEFAULT 'free';
-- Values: 'free', 'steam_premium', 'web_premium'

ALTER TABLE players ADD COLUMN steam_id VARCHAR(50) UNIQUE;
ALTER TABLE players ADD COLUMN premium_expires_at FLOAT;
```

**Backend Restrictions:**
```python
@app.post("/api/game/move")
async def move(req, current_user):
    player = await db.get_player_by_id(current_user['id'])
    
    # Check premium restrictions
    if player['account_type'] == 'free':
        if player['level'] >= 3:
            raise HTTPException(
                status_code=402,
                detail="Level 3 is the maximum for free accounts. Upgrade to premium to continue!"
            )
        
        # Check location restrictions
        if location_id in PREMIUM_LOCATIONS:
            raise HTTPException(
                status_code=402,
                detail="This area is only accessible to premium accounts."
            )
    
    # Continue with move logic...
```

**Monetization Options:**

1. **Steam Purchase (One-time)**
   - Buy on Steam = Permanent premium
   - Price: $9.99 - $19.99
   - Steam handles payment, you get 70%

2. **Web Subscription**
   - Stripe/PayPal integration
   - $4.99/month or $39.99/year
   - Separate from Steam version

3. **Hybrid Model**
   ```python
   def is_premium(player):
       # Steam premium (permanent)
       if player['account_type'] == 'steam_premium':
           return True
       
       # Web premium (subscription)
       if player['account_type'] == 'web_premium':
           if player['premium_expires_at'] > time.time():
               return True
       
       return False
   ```

### Steam Features You Can Use

1. **Achievements**
   ```typescript
   steamworks.achievement.activate('FIRST_COMBAT_WIN')
   ```

2. **Cloud Saves**
   - Sync player state across devices
   - Automatic backups

3. **Leaderboards**
   ```typescript
   steamworks.leaderboards.uploadScore('PLAYER_LEVEL', player.level)
   ```

4. **Friends/Multiplayer**
   - See Steam friends in-game
   - Invite to PvP combat
   - Group features

5. **Workshop**
   - User-created content
   - Custom locations/items
   - Community maps

### Legal Considerations

1. **Steam Agreement**
   - Need business entity (or individual) to sign Steamworks agreement
   - Need Tax ID (EIN for business, SSN for individual)

2. **Separate Versions**
   - ✅ **ALLOWED:** Selling Steam version + separate web subscription
   - ❌ **NOT ALLOWED:** Forcing Steam users to pay again on web
   - ✅ **ALLOWED:** Different pricing models
   - ✅ **ALLOWED:** Web has features Steam doesn't (and vice versa)

3. **Revenue Sharing**
   - Steam: 30% cut (you get 70%)
   - Web direct: 100% yours (minus payment processor ~3%)

### Recommended Strategy

**Phase 1: Optimize & Test** (Current)
- Implement polling optimizations
- Test with small player base
- Gather feedback

**Phase 2: Steam Preparation** (2-4 weeks)
- Package as Electron app
- Integrate Steamworks SDK
- Implement premium restrictions
- Add achievements/leaderboards

**Phase 3: Soft Launch** (Steam Early Access)
- Launch as Early Access ($14.99)
- Gather Steam community feedback
- Regular updates

**Phase 4: Dual Platform** (After Steam is stable)
- Keep Steam version
- Launch web version with subscription
- Cross-platform support (shared servers)

---

## Priority Order

1. **✅ Immediate:** Polling optimization (biggest impact, low effort)
2. **⏳ Short-term:** Premium restrictions system (prep for monetization)
3. **⏳ Medium-term:** Steam integration (packaging + Steamworks)
4. **🔮 Long-term:** WebSocket real-time updates (if needed)

Would you like me to start implementing the polling optimization now?
