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
Context
Section titled “Context”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:197 —
StartClientInternal() => 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.
Decision
Section titled “Decision”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.InviteFriendandOpenInviteOverlay(TransportFront.cs:37-38) were stubbed for. TheTransportFrontabstraction does not change. - LAN / dev fallback:
UDPTransportdirect-IP stays, behind the sameTransportFrontseam. 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:19currently hard-wires the LAN dev transport into every build).
Prerequisites this decision creates
Section titled “Prerequisites this decision creates”Steam was chosen over the vendored relay knowing it costs more setup. These are now required work, not caveats:
- Steamworks.NET is not installed.
PurrNet.Steam.Runtime.asmdef:5referencesGUID:68bd7fdb68ef2684e982e8a9825b18a5, which is owned by no.metainAssets/,Packages/orLibrary/PackageCache/.SteamClient.cs:5,SteamServer.cs:5andSteamTransport.cs:5all 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. MultiplayerEventManager.asmdef:5’s reference to the Steam addon is now legitimate. It was previously spurious — one grep hit project-wide, a comment atDevTransportFront.cs:110. It stays, and RIOT-181 must not remove it.Assets/PurrNet/Addons/Steamstays. RIOT-181 proposed deleting it along with UTP and Edgegap; that ticket is now partly superseded and needs re-scoping. UTP, Edgegap andPurrNet/Examplesare still candidates for deletion.- The dead Edgegap path should go (RIOT-160) —
_adaptToEdgegap: 1in the boot scene claims a capability that does not exist and now never will.
Alternatives considered
Section titled “Alternatives considered”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.
Consequences
Section titled “Consequences”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.
Revisit if
Section titled “Revisit if”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.