Direct-Connect Server Join Flow
Direct-Connect UI Layer¶
Class: ServerListManager (line 46830)
File: Assembly-CSharp.decompiled.cs
The UI component owns the direct-connect input field and button. Key methods:
- OnDirectConnect() (line 47137): Button callback. Trims
_addressInputField.textand passes toNetworkClient.JoinClientFromMenu(). - OnInputValueChanged() (line 46913): Called on every keystroke via
_addressInputField.onValueChangedlistener. Enables/disables the "Connect" button based onIsValidIpAddressAndPort(). - IsValidIpAddressAndPort() (line 46918): Returns
ValidIpPattern.Match(address).Success.
Field: _addressInputField is a TMP_InputField (line 46836).
IP Address Validation (Hostname Blocker)¶
Static Regex: ValidIpPattern (line 46878)
private static readonly Regex ValidIpPattern = new Regex("^(?:[0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}\s*$");
Pattern matches only IPv4 dotted-quad format with colon-delimited port: ###.###.###.###:port. No hostname support. This regex blocks any domain name input: the button remains grayed out until the user types an IP.
This is the choke point for the hostname problem. Any Harmony patch enabling hostnames must either:
1. Patch IsValidIpAddressAndPort() to accept hostnames, or
2. Replace ValidIpPattern with a less restrictive regex, or
3. Patch the button-enabling logic upstream in OnInputValueChanged().
Connection Initiation¶
Method: NetworkClient.JoinClientFromMenu() (line 198051)
Flow:
1. Splits input on ':' to extract address and port.
2. Special case: if length == 1 and the string is 17 chars, treats it as a SteamId and calls JoinWithSteamP2P().
3. If length != 2, shows error dialog.
4. Parses port as ushort (line 198071).
5. Calls Assets.Scripts.Networking.NetworkManager.StartClient(address, port, localPort) (line 198073).
Method: NetworkManager.StartClient(string address, ushort port, ushort localPort) (line 255869)
- Uses RakNet directly: calls
Instance.rakNet.Connect(array, port, ReadOnlySpan<byte>.Empty, null)(line 255889). - Converts the address string to a null-terminated UTF-8 byte array (lines 255885-255888) and passes it to RakNet.
- On success, stores address and port in
NetworkClient.AddressandNetworkClient.Portstatic fields (lines 255898-255899) and logs"Connected to {address} on port {port}".
Whether RakNet performs DNS resolution on a hostname-shaped byte array is not visible in the C# decompile: RakPeerInterface.Connect is a managed wrapper over native RakNet code. The C# layer does not pre-resolve. See "Open questions" below.
DNS helper already in the game¶
Method: NetworkManager.ResolveIpAddress(string address) (line 255859, immediately above StartClient)
public static string ResolveIpAddress(string address)
{
IPAddress[] hostAddresses = Dns.GetHostAddresses(address);
if (hostAddresses.Length == 0)
{
return string.Empty;
}
return hostAddresses[0].ToString();
}
This static helper resolves a hostname to a dotted-quad string (or "" on failure). It is defined but not called from StartClient. A Harmony patch that needs to feed an IP to the game can call this helper directly instead of bundling its own System.Net.Dns invocation.
Harmony patch site #1: NetworkManager.StartClient() (prefix). Call ResolveIpAddress(address) when address is not dotted-quad, substitute the result. Defensive against unknown RakNet DNS behavior.
Harmony patch site #2: NetworkClient.JoinClientFromMenu() (prefix). Alternative interception site; can preprocess the input string before the colon split / port parse.
Server Persistence (Last Server Forgotten)¶
Finding: No PlayerPrefs entry for the last direct-connect address exists in ServerListManager or NetworkClient.
PlayerPrefs used in ServerListManager (line 46899-46901, 46927-46935, 46942-46962):
- FILTER_VERSION, FILTER_PASSWORD, FILTER_EMPTY (filter toggles)
- JoinSortBy (sort preference)
None of these store the address field. The _addressInputField text is not persisted anywhere. On app quit, it is lost.
Harmony patch site #3: ServerListManager.OnDisable() or application quit hook to save _addressInputField.text to PlayerPrefs (e.g., key LAST_DIRECT_CONNECT_ADDRESS).
Harmony patch site #4: ServerListManager.Awake() or Start() to restore the saved address and repopulate _addressInputField.text.
Port Handling¶
Port is extracted from the input string during parsing in JoinClientFromMenu() (line 198071):
The local port is automatically set to one higher than the server port. Both are passed to StartClient(). No separate port input field exists in the UI; port is baked into the "address:port" string format required by the regex.
Verification history¶
- 2026-05-14: Initial findings. Decompiled v0.2.6228.27061. Located ServerListManager, ValidIpPattern regex (IPv4-only), JoinClientFromMenu parsing, RakNet integration point, no persistence on address field.
- 2026-05-14: Added
NetworkManager.ResolveIpAddress(line 255859) as a ready-made DNS helper the game already ships. Walked back the unverified claim thatRakPeerInterface.Connectresolves hostnames internally; the C# decompile does not reveal native RakNet behavior, so a patch should not assume it.
Open questions¶
- Does the native
RakPeerInterface.Connectresolve hostnames internally, or does it require a numeric IP in the UTF-8 byte array? Not visible in the C# decompile. Resolved cheaply at implementation time by either (a) pre-resolving withNetworkManager.ResolveIpAddressdefensively or (b) testing with a hostname and inspecting RakNet's failure mode. - What does RakNet do if a hostname fails to resolve? Likely returns
ConnectionAttemptResult!=ConnectionAttemptStarted, whichStartClientalready surfaces viaConsoleWindow.PrintAction(...)(line 255892) andNetworkClient.OnJoinFailed(). Verify when implementing.