Skip to content

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 round

Transport 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)
└── ConnectionState

State 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 AI

Determinism

  • 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 begins

Disconnection

  • 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.

Markdown is the canonical GDD source.