# ADR-0004: FishNet Client-Hosted Authority Model

**Status:** Accepted  
**Date:** 2026-06-23  
**Deciders:** Mahmoud Aglan  
**Engine:** Unity 6 + FishNet v4  

---

## Context

IStalk is an asymmetric multiplayer game with 1 Eye player and 1-4 Runner players. The relay server is a dumb binary forwarder — it has no game logic. Someone must run the authoritative game simulation. The options are:

1. **Dedicated server** — a separate headless Unity instance runs the simulation.
2. **Client-hosted (one player is the server)** — one player's machine runs both their client and the FishNet server.

The relay architecture (SYS-20) already establishes that the room creator becomes `hostId`. The Eye role is always assigned to the first joiner (room creator). This creates a natural alignment: Eye = host = server.

---

## Decision

**Client-hosted: the Eye player's machine runs FishNet as server + client. All Runner players connect as clients only.**

```
Eye Player Machine:
    FishNet ServerManager (authoritative simulation)
    FishNet ClientManager (Eye's own visuals and input)
    RelayClient → WebSocket to relay

Runner Player Machines:
    FishNet ClientManager only
    RelayClient → WebSocket to relay
```

---

## Rationale

| Criterion | Client-Hosted (chosen) | Dedicated Server |
|-----------|----------------------|-----------------|
| Infrastructure cost | Zero — no server to run/pay for | Requires hosting, scaling, deployment |
| Latency for Eye | Zero — server is local | Same as Runners (network round-trip) |
| Latency for Runners | One hop through relay to Eye's machine | One hop to dedicated server |
| Cheating surface | Eye can theoretically cheat (they ARE the server) | Server is trusted neutral party |
| Complexity | Low — FishNet host mode is well-supported | High — needs headless build, deployment pipeline, matchmaking to server instances |
| MVP velocity | Fast — no infrastructure beyond the existing relay | Slow — new infrastructure to build and maintain |

**Why Eye as host specifically:**
1. **Authority alignment.** The Eye decides kills (laser hits), visibility (LoS), and match phase transitions. These are already server-authoritative by design. Having the Eye BE the server means these decisions have zero latency — the Eye never waits for a server round-trip to see if their kill registered.
2. **Asymmetric fairness.** In this game, Runners benefit from responsive movement (client-predicted). The Eye benefits from responsive observation and laser targeting (local server). Client-hosting gives each role its ideal latency profile.
3. **Relay architecture.** The relay's `hostId` already designates the room creator as the routing hub. This maps directly to FishNet's host concept.

**Cheating concern:**
The Eye player could theoretically modify their client to cheat (give themselves infinite Dread, instant laser, etc.). This is acceptable for MVP because:
- The game is designed for friend groups (private rooms with codes), not anonymous matchmaking.
- The Eye already has information advantage by design (sees the whole house).
- Anti-cheat is a Launch-tier concern, not MVP.

---

## Authority Rules

Every `NetworkBehaviour` must follow these rules:

### Server-Authoritative (Eye's machine decides)

| What | Why |
|------|-----|
| Runner positions (ground truth) | Prevents speed hacks; resolves prediction errors |
| Weight totals and tier | Cannot self-report carrying less weight |
| Loot pickup confirmation | Prevents two Runners claiming same item |
| Laser hit validation | Server performs SphereCast, not Eye's client input |
| Extraction channel timer | Server owns the countdown; client cannot speed it up |
| Match phase transitions | Single source of truth for game timeline |
| Runner death and despawn | Only server calls `ServerManager.Despawn()` |
| Dread economy (Alpha) | Eye cannot give themselves free Dread |

### Client-Predicted (Runner's machine predicts, server reconciles)

| What | Why |
|------|-----|
| Runner's own movement | Parkour requires sub-frame response (Pillar 1) |

### Client-Authoritative (acceptable for MVP)

| What | Why it's OK |
|------|-------------|
| Eye's camera pan/zoom input | Only affects Eye's own view; no gameplay impact on Runners |
| Eye's laser aim direction (cursor position) | Server re-validates with SphereCast anyway; aim input is just a request |

---

## Host Disconnect Behavior

**MVP:** If the host (Eye) disconnects, the match ends immediately.

- Relay detects disconnect (WebSocket close event).
- Relay broadcasts updated room state showing host gone.
- Runner clients detect `RelayClient.OnDisconnected` or FishNet `ClientManager.OnClientConnectionState(Stopped)`.
- `LobbyManager` triggers return-to-lobby flow.
- No host migration. No match salvage.

**Rationale:** Host migration in FishNet is complex (requires serializing all server state, transferring to a new host, reconnecting all clients). For a 2-5 player game with private rooms, the host disconnecting is rare and the fix is "host restarts and everyone rejoins." Proper host migration is a Beta/Launch consideration.

---

## Consequences

**Positive:**
- Zero infrastructure cost beyond the existing relay.
- Eye gets zero-latency server authority — kills, LoS, and phase transitions are instant for them.
- Simple architecture — one host mode, one authority model, no server deployment to manage.
- FishNet's host mode is well-tested and handles the server+client split cleanly.

**Negative:**
- Eye player can theoretically cheat. Acceptable for friend-group private rooms in MVP.
- Host disconnect kills the match. Acceptable for MVP.
- Eye's machine bears the simulation load (physics, LoS raycasts, all game logic). For 2-5 players with simple physics, this is trivial on any modern PC.
- Runners have slightly higher latency than Eye for server-validated actions (pickup, extraction confirm). Masked by client prediction for movement.

---

## GDD Requirements Addressed

- TR-NET-006: Client-hosted FishNet — host = room creator = server+client
- TR-NET-010: No host migration in MVP — host disconnect ends match
- TR-NET-005: Host election from `game_start.hostId`

---

## References

- `design/gdd/sys-33-fishnet-replication.md` — Section 2: Authority Model
- `design/gdd/sys-20-relay-transport.md` — Host election via `game_start.hostId`
- `design/gdd/sys-21-lobby-matchmaking.md` — Room creator = Eye = host
