Lobby and Session

In short

The CO-OP LOBBY screen (LobbyView) is where players gather. NetSession runs 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):

  1. Refuse if protocol != Protocol or version != Application.version: “different game version (host X, you Y)“.
  2. Refuse if the roster already has 4: “the game is full”.
  3. Ignore a repeated Hello from the same peer.
  4. Name: the client’s name trimmed, or “Player {id+1}” if blank; made unique by adding ” 2”, ” 3”…
  5. Send Welcome, then broadcast the Roster.

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:

ReasonWhen
The host left the gameclient: host sent Leave or the transport lost peer 0
Lost the connection to the hostclient: transport went down after being live
The connection closedhost: 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 gamedefault 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

WhatValueWhere to tune
Max players4NetSession.MaxPlayers
Min players to START RUN2 (1 in the Editor)LobbyView.DrawPlayers
Session start timeout12 sNetSession.StartTimeoutSeconds
Log lines in the lobby6LobbyView.LogLines
Log lines kept60NetRuntime.MaxLogLines
COPIED! shown for1.5 sLobbyView.DrawConnection
Join code field max length40 charactersLobbyView GUI.TextField

In the code

FileWhat it does
Assets/_Project/Scripts/Net/NetSession.csHost, Join, Leave, Tick, SetMyBatter, StartRun, Handle, Admit; events RosterChanged, RunStarting, Ended, PlayerLeft, GameMessage; SendTo, SendToAll, SendToAllExcept
Assets/_Project/Scripts/Net/NetMessages.csPlayerInfo (id, name, batterId, cosmeticIds, connected) and the session messages
Assets/_Project/Scripts/Net/NetRuntime.csHostSession, JoinSession (both turn on Application.runInBackground), Named, JoinRequested
Assets/_Project/Scripts/UI/LobbyView.csThe screen; LocalPlayer, DrawMapPicker, CycleBatter
Assets/_Project/Scripts/Core/GameRoot.csEnterLobby, HookSession, JoinFromPlatform
Assets/_Project/Editor/NetSessionTest.csTools > 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. Welcome repeats the transport’s id.
  • PlayerInfo.connected exists, 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).
  • NetSession has no Unity calls, so NetSessionTest runs it headless.
  • LobbyNav is 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).