using System; using System.Net; using System.Text.RegularExpressions; using Robust.Client; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared; using Robust.Shared.AuthLib; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.Network; using Robust.Shared.Utility; namespace Robust.Client.UserInterface.Screens; [GenerateTypedNameReferences, ContentAccessAllowed] public sealed partial class DebugBuiltinConnectionScreen : UIScreen { [Dependency] private IBaseClient _client = default!; [Dependency] private IConfigurationManager _cfg = default!; [Dependency] private IUserInterfaceManager _userInterface = default!; [Dependency] private IClientNetManager _net = default!; // ReSharper disable once InconsistentNaming private static readonly Regex IPv6Regex = new(@"\[(.*:.*:.*)](?::(\d+))?"); private bool _isConnecting = false; public DebugBuiltinConnectionScreen() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); SetAnchorPreset(this, LayoutPreset.Wide); SetAnchorPreset(ConnectionBox, LayoutPreset.TopRight); SetMarginRight(ConnectionBox, -25); SetMarginTop(ConnectionBox, 30); SetGrowHorizontal(ConnectionBox, GrowDirection.Begin); UsernameEdit.Text = _cfg.GetCVar(CVars.PlayerName); _client.RunLevelChanged += RunLevelChanged; ConnectButton.OnPressed += ConnectButtonOnOnPressed; } private void ConnectButtonOnOnPressed(BaseButton.ButtonEventArgs obj) { var inputName = UsernameEdit.Text.Trim(); var configName = _cfg.GetCVar(CVars.PlayerName); if (!UsernameHelpers.IsNameValid(inputName, out var reason)) { var invalidReason = Loc.GetString(reason.ToText()); _userInterface.Popup( Loc.GetString("debug-builtin-connection-screen-invalid-username-with-reason", ("invalidReason", invalidReason)), Loc.GetString("debug-builtin-connection-screen-invalid-username")); return; } if (UsernameEdit.Text != configName) { _cfg.SetCVar(CVars.PlayerName, inputName); _cfg.SaveToFile(); } SetConnectingState(true); _net.ConnectFailed += OnConnectFailed; try { ParseAddress(ConnectionAddress.Text, out var ip, out var port); _client.ConnectToServer(ip, port); } catch (ArgumentException e) { _userInterface.Popup(Loc.GetString("debug-builtin-connection-screen-failed-to-connect", ("reason", e.Message))); Logger.Warning(e.ToString()); _net.ConnectFailed -= OnConnectFailed; SetConnectingState(false); } } private void ParseAddress(string address, out string ip, out ushort port) { var match6 = IPv6Regex.Match(address); if (match6 != Match.Empty) { ip = match6.Groups[1].Value; if (!match6.Groups[2].Success) { port = _client.DefaultPort; } else if (!ushort.TryParse(match6.Groups[2].Value, out port)) { throw new ArgumentException("Not a valid port."); } return; } // See if the IP includes a port. var split = address.Split(':'); ip = address; port = _client.DefaultPort; if (split.Length > 2) { throw new ArgumentException("Not a valid Address."); } // IP:port format. if (split.Length == 2) { ip = split[0]; if (!ushort.TryParse(split[1], out port)) { throw new ArgumentException("Not a valid port."); } } } private void RunLevelChanged(object? obj, RunLevelChangedEventArgs args) { switch (args.NewLevel) { case ClientRunLevel.Connecting: SetConnectingState(true); break; case ClientRunLevel.Connected: SetConnectingState(false); _net.ConnectFailed -= OnConnectFailed; break; } } private void OnConnectFailed(object? _, NetConnectFailArgs args) { _userInterface.Popup(Loc.GetString("debug-builtin-connection-screen-failed-to-connect",("reason", args.Reason))); _net.ConnectFailed -= OnConnectFailed; SetConnectingState(false); } private void SetConnectingState(bool state) { _isConnecting = state; ConnectButton.Disabled = state; } }