From 45e9be43ef8f384f17c0059c03d1ab42813b0714 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 4 Apr 2020 15:11:28 +0200 Subject: [PATCH] Content now owns main menu and launcher connecting. --- Content.Client/EntryPoint.cs | 16 +- Content.Client/State/LauncherConnecting.cs | 230 ++++++++++++++ Content.Client/State/MainMenu.cs | 329 +++++++++++++++++++++ 3 files changed, 573 insertions(+), 2 deletions(-) create mode 100644 Content.Client/State/LauncherConnecting.cs create mode 100644 Content.Client/State/MainMenu.cs diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 5077657039..e1b50b11e8 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -1,4 +1,4 @@ -using System; +using System; using Content.Client.GameObjects.Components.Actor; using Content.Client.Input; using Content.Client.Interfaces; @@ -18,7 +18,7 @@ using Content.Shared.GameObjects.Components.VendingMachines; using Robust.Client.Interfaces; using Robust.Client.Interfaces.Graphics.Overlays; using Robust.Client.Interfaces.Input; -using Robust.Client.Interfaces.UserInterface; +using Robust.Client.Interfaces.State; using Robust.Client.Player; using Robust.Shared.ContentPack; using Robust.Shared.Interfaces.GameObjects; @@ -36,6 +36,8 @@ namespace Content.Client [Dependency] private readonly IPlayerManager _playerManager; [Dependency] private readonly IBaseClient _baseClient; [Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner; + [Dependency] private readonly IGameController _gameController; + [Dependency] private readonly IStateManager _stateManager; #pragma warning restore 649 public override void Init() @@ -223,6 +225,16 @@ namespace Content.Client IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); + + // Fire off into state dependent on launcher or not. + if (_gameController.LaunchState.FromLauncher) + { + _stateManager.RequestStateChange(); + } + else + { + _stateManager.RequestStateChange(); + } } public override void Update(ModUpdateLevel level, FrameEventArgs frameEventArgs) diff --git a/Content.Client/State/LauncherConnecting.cs b/Content.Client/State/LauncherConnecting.cs new file mode 100644 index 0000000000..df1af3c6dc --- /dev/null +++ b/Content.Client/State/LauncherConnecting.cs @@ -0,0 +1,230 @@ +using Content.Client.UserInterface.Controls; +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Robust.Client.Graphics.Drawing; +using Robust.Client.Interfaces; +using Robust.Client.Interfaces.UserInterface; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using static Content.Client.StaticIoC; + +namespace Content.Client.State +{ + public class LauncherConnecting : Robust.Client.State.State + { +#pragma warning disable 649 + [Dependency] private readonly IUserInterfaceManager _userInterfaceManager; + [Dependency] private readonly IStylesheetManager _stylesheetManager; + [Dependency] private readonly IClientNetManager _clientNetManager; + [Dependency] private readonly IGameController _gameController; +#pragma warning restore 649 + + private Control _control; + private Label _connectStatus; + + public override void Startup() + { + var panelTex = ResC.GetTexture("/Nano/button.svg.96dpi.png"); + var back = new StyleBoxTexture + { + Texture = panelTex, + Modulate = new Color(32, 32, 48), + }; + back.SetPatchMargin(StyleBox.Margin.All, 10); + + Button exitButton; + Label connectFailReason; + Control connectingStatus; + + var address = _gameController.LaunchState.Ss14Address ?? _gameController.LaunchState.ConnectAddress; + + _control = new Control + { + Stylesheet = _stylesheetManager.SheetSpace, + Children = + { + new PanelContainer + { + PanelOverride = back + }, + new VBoxContainer + { + SeparationOverride = 0, + CustomMinimumSize = (300, 200), + Children = + { + new HBoxContainer + { + Children = + { + new MarginContainer + { + MarginLeftOverride = 8, + Children = + { + new Label + { + Text = Loc.GetString("Space Station 14"), + StyleClasses = {StyleBase.StyleClassLabelHeading}, + VAlign = Label.VAlignMode.Center + }, + } + }, + + (exitButton = new Button + { + Text = Loc.GetString("Exit"), + SizeFlagsHorizontal = Control.SizeFlags.ShrinkEnd | Control.SizeFlags.Expand + }), + } + }, + + // Line + new HighDivider(), + + new MarginContainer + { + SizeFlagsVertical = Control.SizeFlags.FillExpand, + MarginLeftOverride = 4, + MarginRightOverride = 4, + MarginTopOverride = 4, + Children = + { + new VBoxContainer + { + SeparationOverride = 0, + Children = + { + new Control + { + Children = + { + (connectingStatus = new VBoxContainer + { + SeparationOverride = 0, + Children = + { + new Label + { + Text = Loc.GetString("Connecting to server..."), + Align = Label.AlignMode.Center, + }, + + (_connectStatus = new Label + { + StyleClasses = {StyleBase.StyleClassLabelSubText}, + Align = Label.AlignMode.Center, + }), + } + }), + (connectFailReason = new Label + { + Align = Label.AlignMode.Center + }) + } + }, + + // Padding. + new Control {CustomMinimumSize = (0, 8)}, + + new Label + { + Text = address, + StyleClasses = {StyleBase.StyleClassLabelSubText}, + SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter, + SizeFlagsVertical = + Control.SizeFlags.ShrinkEnd | Control.SizeFlags.Expand, + } + } + }, + } + }, + + // Line + new PanelContainer + { + PanelOverride = new StyleBoxFlat + { + BackgroundColor = Color.FromHex("#444"), + ContentMarginTopOverride = 2 + }, + }, + + new MarginContainer + { + MarginLeftOverride = 12, + MarginRightOverride = 4, + Children = + { + new HBoxContainer + { + SizeFlagsVertical = Control.SizeFlags.ShrinkEnd, + Children = + { + new Label + { + Text = Loc.GetString("Don't die!"), + StyleClasses = {StyleBase.StyleClassLabelSubText} + }, + new Label + { + Text = "ver 0.1", + SizeFlagsHorizontal = + Control.SizeFlags.Expand | Control.SizeFlags.ShrinkEnd, + StyleClasses = {StyleBase.StyleClassLabelSubText} + } + } + } + } + }, + } + }, + } + }; + + _userInterfaceManager.StateRoot.AddChild(_control); + + LayoutContainer.SetAnchorPreset(_control, LayoutContainer.LayoutPreset.Center); + LayoutContainer.SetGrowHorizontal(_control, LayoutContainer.GrowDirection.Both); + LayoutContainer.SetGrowVertical(_control, LayoutContainer.GrowDirection.Both); + + exitButton.OnPressed += args => + { + _gameController.Shutdown("Exit button pressed"); + }; + + _clientNetManager.ConnectFailed += (sender, args) => + { + connectFailReason.Text = Loc.GetString("Failed to connect to server:\n{0}", args.Reason); + connectingStatus.Visible = false; + connectFailReason.Visible = true; + }; + + _clientNetManager.ClientConnectStateChanged += ConnectStateChanged; + + ConnectStateChanged(_clientNetManager.ClientConnectState); + } + + private void ConnectStateChanged(ClientConnectionState state) + { + _connectStatus.Text = Loc.GetString(state switch + { + ClientConnectionState.NotConnecting => "Not connecting?", + ClientConnectionState.ResolvingHost => "Resolving server address...", + ClientConnectionState.EstablishingConnection => "Establishing initial connection...", + ClientConnectionState.Handshake => "Doing handshake...", + ClientConnectionState.Connected => "Synchronizing game state...", + _ => state.ToString() + }); + } + + public override void Shutdown() + { + _control.Dispose(); + } + } +} diff --git a/Content.Client/State/MainMenu.cs b/Content.Client/State/MainMenu.cs new file mode 100644 index 0000000000..1157e9907b --- /dev/null +++ b/Content.Client/State/MainMenu.cs @@ -0,0 +1,329 @@ +using System; +using System.Text.RegularExpressions; +using Robust.Client; +using Robust.Client.Interfaces; +using Robust.Client.Interfaces.ResourceManagement; +using Robust.Client.Interfaces.UserInterface; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Interfaces.Configuration; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Network; +using Robust.Shared.Utility; + +namespace Content.Client.State +{ + /// + /// Main menu screen that is the first screen to be displayed when the game starts. + /// + // Instantiated dynamically through the StateManager, Dependencies will be resolved. + public class MainScreen : Robust.Client.State.State + { + private const string PublicServerAddress = "server.spacestation14.io"; + +#pragma warning disable 649 + [Dependency] private readonly IBaseClient _client; + [Dependency] private readonly IClientNetManager _netManager; + [Dependency] private readonly IConfigurationManager _configurationManager; + [Dependency] private readonly IGameController _controllerProxy; + [Dependency] private readonly ILocalizationManager _loc; + [Dependency] private readonly IResourceCache _resourceCache; + [Dependency] private readonly IUserInterfaceManager userInterfaceManager; +#pragma warning restore 649 + + private MainMenuControl _mainMenuControl; + private OptionsMenu OptionsMenu; + private bool _isConnecting; + + // ReSharper disable once InconsistentNaming + private static readonly Regex IPv6Regex = new Regex(@"\[(.*:.*:.*)](?::(\d+))?"); + + /// + public override void Startup() + { + _mainMenuControl = new MainMenuControl(_resourceCache, _configurationManager); + userInterfaceManager.StateRoot.AddChild(_mainMenuControl); + + _mainMenuControl.QuitButton.OnPressed += QuitButtonPressed; + _mainMenuControl.OptionsButton.OnPressed += OptionsButtonPressed; + _mainMenuControl.DirectConnectButton.OnPressed += DirectConnectButtonPressed; + _mainMenuControl.JoinPublicServerButton.OnPressed += JoinPublicServerButtonPressed; + _mainMenuControl.AddressBox.OnTextEntered += AddressBoxEntered; + + _client.RunLevelChanged += RunLevelChanged; + + OptionsMenu = new OptionsMenu(_configurationManager); + } + + /// + public override void Shutdown() + { + _client.RunLevelChanged -= RunLevelChanged; + _netManager.ConnectFailed -= _onConnectFailed; + + _mainMenuControl.Dispose(); + OptionsMenu.Dispose(); + } + + private void QuitButtonPressed(BaseButton.ButtonEventArgs args) + { + _controllerProxy.Shutdown(); + } + + private void OptionsButtonPressed(BaseButton.ButtonEventArgs args) + { + OptionsMenu.OpenCentered(); + } + + private void DirectConnectButtonPressed(BaseButton.ButtonEventArgs args) + { + var input = _mainMenuControl.AddressBox; + TryConnect(input.Text); + } + + private void JoinPublicServerButtonPressed(BaseButton.ButtonEventArgs args) + { + TryConnect(PublicServerAddress); + } + + private void AddressBoxEntered(LineEdit.LineEditEventArgs args) + { + if (_isConnecting) + { + return; + } + + TryConnect(args.Text); + } + + private void TryConnect(string address) + { + var inputName = _mainMenuControl.UserNameBox.Text.Trim(); + var (nameValid, invalidReason) = UsernameHelpers.IsNameValid(inputName); + if (!nameValid) + { + invalidReason = _loc.GetString(invalidReason); + userInterfaceManager.Popup( + _loc.GetString("Invalid username:\n{0}", invalidReason), + _loc.GetString("Invalid Username")); + return; + } + + var configName = _configurationManager.GetCVar("player.name"); + if (_mainMenuControl.UserNameBox.Text != configName) + { + _configurationManager.SetCVar("player.name", inputName); + _configurationManager.SaveToFile(); + } + + _setConnectingState(true); + _netManager.ConnectFailed += _onConnectFailed; + try + { + ParseAddress(address, out var ip, out var port); + _client.ConnectToServer(ip, port); + } + catch (ArgumentException e) + { + userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error."); + Logger.Warning(e.ToString()); + _netManager.ConnectFailed -= _onConnectFailed; + } + } + + private void RunLevelChanged(object obj, RunLevelChangedEventArgs args) + { + if (args.NewLevel == ClientRunLevel.Initialize) + { + _setConnectingState(false); + _netManager.ConnectFailed -= _onConnectFailed; + } + } + + 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 _onConnectFailed(object _, NetConnectFailArgs args) + { + userInterfaceManager.Popup($"Failed to connect:\n{args.Reason}"); + _netManager.ConnectFailed -= _onConnectFailed; + _setConnectingState(false); + } + + private void _setConnectingState(bool state) + { + _isConnecting = state; + _mainMenuControl.DirectConnectButton.Disabled = state; +#if FULL_RELEASE + _mainMenuControl.JoinPublicServerButton.Disabled = state; +#endif + } + + private sealed class MainMenuControl : Control + { + private readonly IResourceCache _resourceCache; + private readonly IConfigurationManager _configurationManager; + + public LineEdit UserNameBox { get; private set; } + public Button JoinPublicServerButton { get; private set; } + public LineEdit AddressBox { get; private set; } + public Button DirectConnectButton { get; private set; } + public Button OptionsButton { get; private set; } + public Button QuitButton { get; private set; } + public Label VersionLabel { get; private set; } + + public MainMenuControl(IResourceCache resCache, IConfigurationManager configMan) + { + _resourceCache = resCache; + _configurationManager = configMan; + + PerformLayout(); + } + + private void PerformLayout() + { + LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide); + + var layout = new LayoutContainer(); + AddChild(layout); + + var vBox = new VBoxContainer + { + StyleIdentifier = "mainMenuVBox" + }; + + layout.AddChild(vBox); + LayoutContainer.SetAnchorPreset(vBox, LayoutContainer.LayoutPreset.TopRight); + LayoutContainer.SetMarginRight(vBox, -25); + LayoutContainer.SetMarginTop(vBox, 30); + LayoutContainer.SetGrowHorizontal(vBox, LayoutContainer.GrowDirection.Begin); + + var logoTexture = _resourceCache.GetResource("/Textures/Logo/logo.png"); + var logo = new TextureRect + { + Texture = logoTexture, + Stretch = TextureRect.StretchMode.KeepCentered, + }; + vBox.AddChild(logo); + + var userNameHBox = new HBoxContainer {SeparationOverride = 4}; + vBox.AddChild(userNameHBox); + userNameHBox.AddChild(new Label {Text = "Username:"}); + + var currentUserName = _configurationManager.GetCVar("player.name"); + UserNameBox = new LineEdit + { + Text = currentUserName, PlaceHolder = "Username", + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + + userNameHBox.AddChild(UserNameBox); + + JoinPublicServerButton = new Button + { + Text = "Join Public Server", + StyleIdentifier = "mainMenu", + TextAlign = Label.AlignMode.Center, +#if !FULL_RELEASE + Disabled = true, + ToolTip = "Cannot connect to public server with a debug build." +#endif + }; + + vBox.AddChild(JoinPublicServerButton); + + // Separator. + vBox.AddChild(new Control {CustomMinimumSize = (0, 2)}); + + AddressBox = new LineEdit + { + Text = "localhost", + PlaceHolder = "server address:port", + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + + vBox.AddChild(AddressBox); + + DirectConnectButton = new Button + { + Text = "Direct Connect", + TextAlign = Label.AlignMode.Center, + StyleIdentifier = "mainMenu", + }; + + vBox.AddChild(DirectConnectButton); + + // Separator. + vBox.AddChild(new Control {CustomMinimumSize = (0, 2)}); + + OptionsButton = new Button + { + Text = "Options", + TextAlign = Label.AlignMode.Center, + StyleIdentifier = "mainMenu", + }; + + vBox.AddChild(OptionsButton); + + QuitButton = new Button + { + Text = "Quit", + TextAlign = Label.AlignMode.Center, + StyleIdentifier = "mainMenu", + }; + + vBox.AddChild(QuitButton); + + VersionLabel = new Label + { + Text = $"v0.1" + }; + + LayoutContainer.SetAnchorPreset(VersionLabel, LayoutContainer.LayoutPreset.BottomRight); + LayoutContainer.SetGrowHorizontal(VersionLabel, LayoutContainer.GrowDirection.Begin); + LayoutContainer.SetGrowVertical(VersionLabel, LayoutContainer.GrowDirection.Begin); + layout.AddChild(VersionLabel); + } + } + } +}