Skip to content

ADR-0002: Ship on Steam; keep UDP direct-IP as the LAN/dev path

  • Date: 2026-08-22
  • Status: Accepted
  • Decision maker: Joao Miranda (product owner)
  • Ticket: RIOT-42

The shipping boot scene hardcodes loopback. InitializeScene.unity serialises a PurrNet.Transports.UDPTransport at :164 with _address: 127.0.0.1 (:171), _serverPort: 5000 (:169), _maxConnections: 100 (:170). UDPTransport.cs:197StartClientInternal() => Connect(_address, _serverPort) — dials that serialised value verbatim.

Two machines cannot currently connect by any means short of hand-editing the scene and rebuilding both halves. Every “Join” dials the joining client’s own loopback.

The Edgegap escape hatch is inert: InitializeScene.unity:168 sets _adaptToEdgegap: 1, but AutomaticCloudSetups.cs:15-19 resolves it to false unless EDGEGAP_PURRNET_SUPPORT && UNITY_SERVER && !UNITY_EDITOR, and none of those defines exist in ProjectSettings.asset.

Racoon Riot ships on Steam, using the Steam transport (SDR relay).

The store page is real, which removes the App ID prerequisite that would otherwise have made this premature.

  • Join code format: SteamID64 / Steam lobby ID. Players do not type a code in the shipped flow — they use Steam friend invites and the overlay, which is what TransportFront.InviteFriend and OpenInviteOverlay (TransportFront.cs:37-38) were stubbed for. The TransportFront abstraction does not change.
  • LAN / dev fallback: UDPTransport direct-IP stays, behind the same TransportFront seam. This is not optional — editor iteration under Steam otherwise requires the Steam client running with the correct App ID, which is unacceptable friction for day-to-day work and impossible on the CI runner. Selection becomes configuration-driven per RIOT-123 (Startup.cs:19 currently hard-wires the LAN dev transport into every build).

Steam was chosen over the vendored relay knowing it costs more setup. These are now required work, not caveats:

  1. Steamworks.NET is not installed. PurrNet.Steam.Runtime.asmdef:5 references GUID:68bd7fdb68ef2684e982e8a9825b18a5, which is owned by no .meta in Assets/, Packages/ or Library/PackageCache/. SteamClient.cs:5, SteamServer.cs:5 and SteamTransport.cs:5 all open with #if STEAMWORKS_NET, so the addon currently compiles to an empty assembly and the unresolved reference is silently dropped. Nothing about Steam works until this is installed and the define is set.
  2. MultiplayerEventManager.asmdef:5’s reference to the Steam addon is now legitimate. It was previously spurious — one grep hit project-wide, a comment at DevTransportFront.cs:110. It stays, and RIOT-181 must not remove it.
  3. Assets/PurrNet/Addons/Steam stays. RIOT-181 proposed deleting it along with UTP and Edgegap; that ticket is now partly superseded and needs re-scoping. UTP, Edgegap and PurrNet/Examples are still candidates for deletion.
  4. The dead Edgegap path should go (RIOT-160) — _adaptToEdgegap: 1 in the boot scene claims a capability that does not exist and now never will.

PurrTransport relay. PurrTransport.cs is vendored, implemented, and referenced by zero scenes and zero prefabs. Its alloc/join-by-room-name shape (:472 Alloc, :598 Join) already matches what TransportFront.CreateLobbyAsync / JoinLobbyByCodeAsync were designed around, so it would have been the cheapest path to working online play. Rejected in favour of Steam because the store page is real: Steam gives the same NAT-free relay via SDR at no per-seat cost, plus friend invites and the overlay, and shipping on Steam while running a second relay would mean paying for and operating infrastructure Steam already provides. It remains the fallback if Steam integration stalls — the TransportFront seam is what keeps that a swap rather than a rewrite.

UDP direct-IP alone. Works on LAN today with zero infrastructure and zero money, and DevTransportFront.cs already implements the IP parse and validation. Rejected as a shipping transport: it fails over the internet without host-side port forwarding, and “open UDP port 5000 on your router” is not a viable onboarding step for a party game. Retained as the LAN/dev path.

Unblocks: RIOT-48 (ApplyConnectionToken seam), RIOT-55 (rewrite MainMenu.OnStartClient), RIOT-123 (configuration-driven transport selection), RIOT-172 (two-peer PlayMode connect test), RIOT-135 (the dev join-code path).

Re-scopes: RIOT-181 (Steam addon now stays), RIOT-160 (Edgegap removal now unambiguous).

Changes: RIOT-66 asked for a join-code text field in MainMenu.unity. Under Steam the shipped flow is friend-invite-driven, so that field belongs to the LAN/dev path only. That ticket needs re-reading against this ADR.

Editor iteration now depends on either the Steam client running or the LAN/dev fallback being selected. The fallback is therefore load-bearing for CI, not a convenience.

Steam integration stalls on Steamworks.NET or App ID issues, or the game ships on a second platform that has its own transport. PurrTransport is the named fallback.