--- description: Server-authoritative architecture blueprint — authority boundaries, WS protocol contract, and gap priorities. Reference docs/blueprint_server_authoritative.md for full details. alwaysApply: true --- # Server-Authoritative Architecture Full blueprint: `docs/blueprint_server_authoritative.md` ## Authority Boundary **Server owns:** combat simulation, damage/crit rolls, HP changes, XP/gold awards, level-ups, loot generation, buff/debuff logic, enemy spawning, death detection. **Client owns:** rendering (PixiJS), camera, animations, walking visuals, UI overlays (HUD, HP bars, buff bars, popups), input (touch/click), sound/haptics triggered by server events. **Client must NOT:** compute damage, roll crits, award XP/gold, generate loot, run level-up calculations, or send hero stats to server. ## WS Message Envelope (mandatory) All messages (both directions) use `{"type": string, "payload": object}`. Text `"ping"`/`"pong"` for heartbeat (not inside envelope). ### Server → Client Events | Type | Key Payload Fields | |------|-------------------| | `hero_state` | Full hero JSON (sent on connect/revive) | | `combat_start` | `enemy`, `heroHp`, `heroMaxHp` | | `attack` | `source` (hero\|enemy), `damage`, `isCrit`, `heroHp`, `enemyHp`, `debuffApplied` | | `hero_died` | `heroHp`, `enemyHp`, `killedBy` | | `combat_end` | `xpGained`, `goldGained`, `newXp`, `newGold`, `newLevel`, `leveledUp`, `loot[]` | | `level_up` | `newLevel`, all stat fields | | `buff_applied` | `buffType`, `magnitude`, `durationMs`, `expiresAt` | | `debuff_applied` | `debuffType`, `magnitude`, `durationMs`, `expiresAt` | ### Client → Server Commands | Type | Payload | |------|---------| | `request_encounter` | `{}` | | `request_revive` | `{}` | | `activate_buff` | `{"buffType": "rage"}` | ## Critical Gaps (P0 — must fix) 1. **GAP-1:** `SaveHero` accepts arbitrary stats — **delete it**, replace with preferences-only endpoint 2. **GAP-2:** Combat is client-only — wire `engine.StartCombat()` into encounter handlers 3. **GAP-3:** XP/gold/level client-dictated — server awards on `handleEnemyDeath`, persists to DB 4. **GAP-4:** Auth middleware disabled — uncomment and enforce Telegram HMAC validation ## Backend Rules - Engine emits `WSMessage` (not raw `CombatEvent`) through `eventCh` - `handleEnemyDeath` must persist hero to DB after awarding rewards - WS handler extracts heroID from Telegram auth `initData`, not hardcoded - On client disconnect during combat: `StopCombat(heroID)` + persist state - Engine is the single writer for heroes in active combat (REST reads from engine, not DB) ## Frontend Rules - Remove `_simulateFighting`, `_onEnemyDefeated`, `_spawnEnemy`, `_requestEncounter` (client combat sim) - Remove `_serverAuthoritative` flag — always server-authoritative - Remove auto-save (`_triggerSave`, `sendBeacon`, `heroStateToSaveRequest`) - Walking requests encounters via WS every ~3s; server enforces cooldown - Revive and buff activation go through WS commands (REST as fallback) - On WS disconnect: show "Reconnecting..." overlay; server sends `hero_state` on reconnect ## Implementation Order 1. Enable auth middleware (blocks everything) 2. WS envelope + ping/pong + heroID from auth 3. Wire `StartCombat` into encounter handler 4. Engine events with rewards + DB persist on death 5. Delete `SaveHero` 6. Frontend: remove client combat sim, add server event handlers 7. Frontend: switch encounter/revive/buff to WS commands 8. Buff/debuff DB persistence 9. Server-side loot generation 10. CORS + WS `CheckOrigin` hardening Steps 1–5 (backend) and 6–7 (frontend) parallelize once WS contract is agreed.