Transports

In short

INetTransport is a star network: one host (peer 0) and up to 3 clients (peers 1-3) that only talk to the host. LOCAL is UDP on a port (join code 7777, or address:port on a LAN) with its own resend-until-acked reliable channel. STEAM makes a friends-only Steam lobby (join code = lobby id) and connects over the Steam Datagram Relay. Steam code only compiles with the HOMERUNDERBY_STEAM define.

How it works

The interface (INetTransport): Host(), Join(code), Leave(), Send(peer, bytes, reliable), Broadcast(bytes, reliable), Poll(); state IsActive, IsHost, IsConnecting, LocalPeerId, JoinCode, Peers, LocalPlayerName, CanInvite / OpenInvite(); events PeerJoined, PeerLeft, Received, Log. Everything happens on the main thread inside Poll(), which NetRuntime.Update calls once a frame.

Choosing one. NetRuntime.Use(Backend.Local | Backend.Steam) (the LOCAL / STEAM buttons in the lobby and NET TEST) disposes the old transport, makes a new one and a new NetSession. It starts on Local. Without the define, asking for Steam logs “Steam is not compiled in…” and stays on Local.

LocalTransport (UDP)

  1. Host binds UDP on the port (default 7777) on all addresses. Join code = the port.
  2. Join parses the code: 7777 means 127.0.0.1:7777; 192.168.1.20:7777 for another PC. It sends Connect with the transport protocol HRD-LOCAL-1 every 0.5 s until Accept (with its peer id) or 6 s pass.
  3. The host refuses a different transport protocol (“different game version”) or a 4th client (“the game is full”). A repeated Connect from a known peer gets its Accept again.
  4. Reliable sends carry a sequence number, are resent every 0.2 s until acked, and are delivered in order (early ones are held, duplicates dropped). Unreliable sends are fire-and-forget.
  5. A heartbeat goes out after 1 s of silence. A peer silent for 10 s is dropped (“timed out”).
  6. Leave sends Disconnect to every peer.
  7. On Windows, SIO_UDP_CONNRESET is turned off so one client quitting doesn’t stop the host from reading.

SteamTransport (relay)

  1. SteamBootstrap.EnsureStarted initialises the Steam client once with app id 480 and the relay network (callbacks run on the main thread from Poll). Fails with “Steam is not available: … (is Steam running and signed in?)“.
  2. Host creates a lobby for 4 (MaxClients + 1), friends-only and joinable, with lobby data hrd_protocol = HRD-STEAM-1, then opens a relay socket (CreateRelaySocket<HostSocket>). Join code = the lobby id.
  3. Join parses the code as a lobby id, joins the lobby, checks hrd_protocol (“That lobby runs a different game version”), then connects over the relay to the lobby owner (ConnectRelay<HostLink>).
  4. On connection the host gives the client the next id (1, 2, 3) and sends [0xFF, id] first. A 4th client is closed at OnConnecting.
  5. Reliable → SendType.Reliable; unreliable → SendType.Unreliable | NoNagle.
  6. INVITE FRIENDS (OpenInvite) opens Steam’s invite overlay for the lobby (host only).
  7. SteamOverlayJoin listens for SteamFriends.OnGameLobbyJoinRequested from game start, whatever backend is selected; NetRuntime pumps Steam callbacks while Local is active. See Lobby and Session.
  8. LocalPlayerName is the Steam persona name.

The HOMERUNDERBY_STEAM define

  • SteamTransport.cs is wrapped in #if HOMERUNDERBY_STEAM. NetRuntime.SteamCompiled tells the UI whether it is in.
  • Tools > Home Run Derby > Steam > Enable Steam checks that Assets/Plugins/Steamworks/Facepunch.Steamworks.Win64.dll and steam_api64.dll exist, writes steam_appid.txt (= 480) next to the project, and adds the define to the Standalone target. Disable Steam removes the define.
  • Tools > Home Run Derby > Build > Windows Player (zip) copies steam_appid.txt next to the built .exe (it warns if the file is missing).
  • On main the define is on (ProjectSettings.asset: Standalone: HOMERUNDERBY_STEAM) and the Facepunch.Steamworks Win64 DLL, its XML docs and steam_api64.dll are committed under Assets/Plugins/Steamworks/.

Numbers

WhatValueWhere to tune
Max clients3 (4 players)LocalTransport.MaxClients, SteamTransport.MaxClients
Local default port7777LocalTransport.DefaultPort
Local connect retry / give up0.5 s / 6 sConnectRetrySeconds, ConnectTimeoutSeconds
Local heartbeat1 sHeartbeatSeconds
Local peer timeout10 sTimeoutSeconds
Local resend0.2 sResendSeconds
Local max message1200 bytesMaxPacket
Local transport protocolHRD-LOCAL-1LocalTransport.Protocol
Steam lobby protocolHRD-STEAM-1 (lobby key hrd_protocol)SteamTransport.Protocol
Steam app id480SteamBootstrap.AppId, SteamSetup.TestAppId, steam_appid.txt
Steam libraryFacepunch.Steamworks 2.5.2 (per the plan)Assets/Plugins/Steamworks/

In the code

FileWhat it does
Assets/_Project/Scripts/Net/INetTransport.csThe interface
Assets/_Project/Scripts/Net/LocalTransport.csUDP transport, reliable channel, heartbeats
Assets/_Project/Scripts/Net/SteamTransport.csSteamTransport (HostSocket, HostLink), SteamOverlayJoin, SteamBootstrap
Assets/_Project/Scripts/Net/NetRuntime.csUse(backend), Update polling, SteamCompiled
Assets/_Project/Editor/SteamSetup.csEnable / Disable Steam menu items
Assets/_Project/Editor/NetSelfTest.csTools > Home Run Derby > Net > Run Local Transport Self-Test: host + two clients, reliable round trips, in-order burst, unreliable, clean leave
steam_appid.txt480

Gotchas

Always call the base handlers in SteamTransport

HostSocket and HostLink override Facepunch’s SocketManager / ConnectionManager callbacks. Each override must call base first: base.OnConnected adds the connection to the poll group, and without it the host never hears a word from that client. That was the first Steam test’s bug, fixed in PR #27 (fix/steam-host-receive).

  • One Steam account can be signed in once per machine, so two instances on one PC cannot both use Steam. Use LOCAL.
  • There are three version strings: HRD-LOCAL-1 and HRD-STEAM-1 at the transport level, and NetSession.Protocol plus Application.version at the session level. See Protocol Versions.
  • SteamSetup’s class comment says Facepunch.Steamworks “is not committed”. It is committed on main; the comment is stale.
  • Only the Win64 Steam pieces are in the repo; the game targets Windows.
  • The Local port must be free: “Could not open UDP port … (is another instance already hosting on it?)“.