Lobby and Session
In short
The CO-OP LOBBY screen (
LobbyView) is where players gather.NetSessionruns on top of a transport: the host (always player 0) owns the roster and is the only one who changes it. A client says Hello, is checked for the same protocol and game version, gets a player id and then receives the whole roster on every change. Only the host can press START RUN.
How it works
Getting there. Title screen → switch the mode card to CO-OP (CHANGE MODE) → PLAY CO-OP
(GameRoot.EnterLobby, state GameState.Lobby). A Steam overlay invite also lands here (see below).
Left panel: CONNECTION.
- LOCAL / STEAM pick the backend (
NetRuntime.Use). Switching ends any session (“Switched connection type”). STEAM is greyed out when the game was compiled without Steam. See Transports. - Status line: HOSTING, IN THE LOBBY, CONNECTING… or NOT CONNECTED.
- Not in a session: HOST A GAME, or type a code under OR JOIN WITH A CODE, PASTE (from the clipboard) and
JOIN. On LOCAL an empty code field fills with
7777. - Hosting: SHARE THIS CODE shows the join code, COPY CODE puts it on the clipboard (shows COPIED! for 1.5 s), and on Steam INVITE FRIENDS opens the Steam invite overlay.
- LEAVE LOBBY while in a session. The last 6 lines of the net log sit at the bottom (why a join failed, who joined).
Right panel: PLAYERS n / 4.
- Four seats. A filled seat shows the seat number, name, batter, and pills YOU and HOST. Empty seats say OPEN SEAT. Seat order is the batting order.
- YOUR BATTER with
<>cycles your unlocked batters; the change goes to the host (SetBatter), which re-broadcasts the roster. Your batter stands on the right of the screen. - MAP: the host picks any unlocked map with
<>and a difficulty button (locked ones show LOCKED). It starts on the last unlocked map at its highest unlocked difficulty. Clients see THE HOST PICKS THE MAP. - BACK (or Esc) leaves the session and returns to the title.
- Host: START RUN needs at least 2 players (1 is allowed in the Editor); otherwise “Waiting for at least one more player.” Clients see HOST STARTS.
The handshake.
sequenceDiagram participant C as Client participant H as Host C->>H: transport connect (Local Connect / Steam relay) H-->>C: transport peer id (Local Accept / Steam [0xFF,id]) C->>H: Hello (protocol, version, PlayerInfo) alt protocol or version differs, or 4 players already H-->>C: Refuse (reason) else admitted H-->>C: Welcome (player id) H-->>C: Roster (everyone) end C->>H: SetBatter (when you change batter) H-->>C: Roster H-->>C: StartRun (seed, order, park id, difficulty)
Admitting a player (NetSession.Admit, host only):
- Refuse if
protocol != Protocolorversion != Application.version: “different game version (host X, you Y)“. - Refuse if the roster already has 4: “the game is full”.
- Ignore a repeated Hello from the same peer.
- Name: the client’s name trimmed, or “Player {id+1}” if blank; made unique by adding ” 2”, ” 3”…
- Send
Welcome, then broadcast theRoster.
Names. LobbyView.LocalPlayer uses the save’s playerName (blank if it is the default “You”), batter and
equipped cosmetics. NetRuntime.Named replaces the name with the Steam persona name when the transport has one.
Starting. NetSession.StartRun(seed, parkId, difficulty) locks the order (connected roster players, lobby order),
sends StartRun and raises RunStarting locally. Clients raise RunStarting when the message arrives. GameRoot
listens and calls CoopRun.Start. The seed is Random.Range(1, int.MaxValue) from the lobby.
Ending. NetSession.Tick (every frame, from NetRuntime.Update) ends the session when the connection drops or never
comes up within 12 s. The reason goes to the title screen as a notice. Reasons in the code:
| Reason | When |
|---|---|
| The host left the game | client: host sent Leave or the transport lost peer 0 |
| Lost the connection to the host | client: transport went down after being live |
| The connection closed | host: transport went down |
| Could not reach the host (check the code) | client: not connected after 12 s |
| Could not start hosting (see the log) | host: transport never came up in 12 s |
| The host refused: … | client got Refuse |
| You left the game | default reason for Leave() |
Steam invites. NetRuntime hooks SteamOverlayJoin at startup, whatever the backend. On “Join game” or an
accepted invite, GameRoot.JoinFromPlatform switches to STEAM, opens the lobby and joins, unless this player is busy
(already in a session, a co-op run, or batting): then it logs “Ignored a Steam join request: already in a game”.
Numbers
| What | Value | Where to tune |
|---|---|---|
| Max players | 4 | NetSession.MaxPlayers |
| Min players to START RUN | 2 (1 in the Editor) | LobbyView.DrawPlayers |
| Session start timeout | 12 s | NetSession.StartTimeoutSeconds |
| Log lines in the lobby | 6 | LobbyView.LogLines |
| Log lines kept | 60 | NetRuntime.MaxLogLines |
| COPIED! shown for | 1.5 s | LobbyView.DrawConnection |
| Join code field max length | 40 characters | LobbyView GUI.TextField |
In the code
| File | What it does |
|---|---|
Assets/_Project/Scripts/Net/NetSession.cs | Host, Join, Leave, Tick, SetMyBatter, StartRun, Handle, Admit; events RosterChanged, RunStarting, Ended, PlayerLeft, GameMessage; SendTo, SendToAll, SendToAllExcept |
Assets/_Project/Scripts/Net/NetMessages.cs | PlayerInfo (id, name, batterId, cosmeticIds, connected) and the session messages |
Assets/_Project/Scripts/Net/NetRuntime.cs | HostSession, JoinSession (both turn on Application.runInBackground), Named, JoinRequested |
Assets/_Project/Scripts/UI/LobbyView.cs | The screen; LocalPlayer, DrawMapPicker, CycleBatter |
Assets/_Project/Scripts/Core/GameRoot.cs | EnterLobby, HookSession, JoinFromPlatform |
Assets/_Project/Editor/NetSessionTest.cs | Tools > Home Run Derby > Net > Run Session Test: host + two clients over Local, unique names, batter change, wrong build refused, START reaches all, leaves |
Gotchas
- Player ids are network peer ids: host 0, clients 1 to 3 in connection order.
Welcomerepeats the transport’s id. PlayerInfo.connectedexists, but a player who leaves is removed from the roster, not marked disconnected (there is no rejoin).- The map picker only offers maps the host has unlocked. A team win counts toward everyone’s unlocks (see Co-op Run Flow).
NetSessionhas no Unity calls, soNetSessionTestruns it headless.LobbyNavis the title screen’s top bar (PLAY / CUSTOMIZE / PERKS / BATTING CAGE / INDEX), not part of the co-op lobby despite its name.- The version check now expects
HRD-COOP-7(Protocol Versions).
Related
- Co-op Overview: the big picture
- Transports: what the join code means on LOCAL and STEAM
- Protocol Versions: the version check
- Net Messages: Hello, Welcome, Refuse, Roster, SetBatter, StartRun, Leave
- Co-op Run Flow: what happens after START RUN
- Steam Test Guide: testing the lobby over Steam