Appearance
Co-op Networking Architecture
Status: RESOLVED — July 19, 2026
Model: Turn-Passing State Sync
Turn-based game. No tick rate, no rollback, no latency compensation. At end of each turn: serialize state, send to partner.
Turn Flow:
Player 1 takes turn → End Turn → Serialize State → Send to P2
→ P2 receives state → P2 takes turn → End Turn → Serialize → Send to P1
→ Enemy AI resolves → Serialize → Both receive → Next roundTransport Layer
- Steamworks Networking (P2P reliable) with an abstraction layer on top.
- Abstraction wraps all network calls behind an interface (
INetworkTransport). Allows swapping to other transports (Epic Online Services, direct IP) without rewriting game logic. - Steam-only for MVP. No non-Steam fallback.
INetworkTransport
├── Connect(lobbyId)
├── Disconnect()
├── SendMessage(data, reliable)
├── OnMessageReceived(event)
└── ConnectionStateState Payload
TurnPayload
├── turnNumber: int
├── playerId: int // which player just ended their turn
├── units[]: List<UnitSnapshot>
├── gridChanges[]: List<TileChange>
├── timelineSnapshot: TimelineState
├── effects[]: List<ActiveEffect>
└── seed: int // for deterministic enemy AIDeterminism
- Enemy AI must be deterministic given the same seed + game state.
- Both clients compute enemy turns independently.
- Desync detection: CRC check at end of each full round. If mismatch: log + use host state as authoritative.
Connection Flow
1. Host: "Create Co-op Game" → Steamworks creates lobby (private, invite-only)
2. Host: "Invite Friend" → Steam overlay → friend list
3. Guest: Accepts → Steamworks joins lobby
4. Both: Draft phase (simultaneous picks from shared pool)
5. Host starts run → initial state sent
6. Turn loop beginsDisconnection
- No reconnection for MVP. Disconnect = run over for both players.
- "Player Disconnected" message. Return to main menu. No grace period, no timeout.
- Post-MVP: consider 60-second reconnect window.
Ghost Mode
When a player loses all units (not a disconnect — all their units permadead):
- Ghost player can: ping, emote, spectate (free camera).
- Ghost player cannot: take turns, control units, spend gold.
- Revival: surviving partner can use Phoenix Down at next Camp to revive one ghost unit.
- Both in Ghost Mode → run over.
Open Questions
- [ ] Abstraction layer scope: full interface or just the calls we use? Keep it minimal — only Connect, Disconnect, Send, OnReceive.
- [ ] NAT traversal: Steamworks handles this. No action needed.
- [ ] Serialization format for network payloads: JSON (debuggable) or binary (smaller)? For turn-based at ~5-20 KB/turn, JSON is fine.
- [ ] Latency tolerance: 200ms+ is imperceptible since turns are not real-time. No latency compensation needed.