public partial class MultiplayerMenu : MarginContainer { enum Status { Disconnected, ConnectionFailed, HostingFailed, Connecting, Connected, Hosting, } Status _status = Status.Disconnected; [Export] public Label StatusLabel { get; set; } [ExportGroup("When Disconnected")] [Export] public Container WhenDisconnected { get; set; } [Export] public LineEdit AddressInput { get; set; } [Export] public SpinBox PortInput { get; set; } [Export] public Button ConnectButton { get; set; } [Export] public Button HostButton { get; set; } [ExportGroup("When Server")] [Export] public Container WhenServer { get; set; } [Export] public LineEdit PortDisplay { get; set; } [ExportGroup("When Connected")] [Export] public Container WhenConnected { get; set; } [Export] public Label PlayersLabel { get; set; } [Export] public Button DisconnectButton { get; set; } Game _game; public override void _Ready() { _game = GetNode("/root/Game"); PortDisplay.AddThemeColorOverride("font_uneditable_color", PortDisplay.GetThemeColor("font_color")); Multiplayer.ConnectedToServer += () => UpdateStatus(Status.Connected); Multiplayer.ConnectionFailed += () => UpdateStatus(Status.ConnectionFailed); Multiplayer.ServerDisconnected += () => UpdateStatus(Status.Disconnected); _game.MultiplayerManager.PlayerJoined += (_) => UpdatePlayerCount(); _game.MultiplayerManager.PlayerLeft += (_) => UpdatePlayerCount(); } void UpdateStatus(Status status) { _status = status; StatusLabel.Text = status switch { Status.Disconnected => "Disconnected", Status.ConnectionFailed => "Connection failed!", Status.HostingFailed => "Hosting failed!", Status.Connecting => "Connecting ...", Status.Connected => "Connected", Status.Hosting => "Hosting", _ => throw new InvalidOperationException(), }; StatusLabel.LabelSettings.FontColor = (status switch { Status.Disconnected => Colors.DarkGray, Status.ConnectionFailed => Colors.Red, Status.HostingFailed => Colors.Red, Status.Connecting => Colors.Yellow, Status.Connected => Colors.Green, Status.Hosting => Colors.Green, _ => throw new InvalidOperationException(), }).Lerp(StatusLabel.GetThemeColor("font_color"), 0.5f); WhenDisconnected.Visible = status < Status.Connecting; WhenServer.Visible = status == Status.Hosting; WhenConnected.Visible = status >= Status.Connecting; DisconnectButton.Text = status == Status.Hosting ? "Close Server" : "Disconnect"; UpdatePlayerCount(); } void UpdatePlayerCount() { PlayersLabel.Text = _status switch { < Status.Connecting => "Singleplayer", Status.Connecting => "??? Players", > Status.Connecting => ((Func)(() => { var players = _game.MultiplayerManager.Players.GetChildCount(); return $"{players} {(players != 1 ? "Players" : "Player")}"; }))(), }; } public void OnShowAddressToggled(bool toggledOn) { AddressInput.Secret = !toggledOn; AddressInput.VirtualKeyboardType = toggledOn ? LineEdit.VirtualKeyboardTypeEnum.Default : LineEdit.VirtualKeyboardTypeEnum.Password; } public void OnConnectPressed() { var address = AddressInput.Text; if (address == "") address = AddressInput.PlaceholderText; var port = (ushort)RoundToInt(PortInput.Value); _game.MultiplayerManager.Connect(address, port); UpdateStatus(Status.Connecting); } public void OnHostPressed() { var port = (ushort)RoundToInt(PortInput.Value); if (_game.MultiplayerManager.CreateServer(port)) { PortDisplay.Text = port.ToString(); UpdateStatus(Status.Hosting); } else UpdateStatus(Status.HostingFailed); } public void OnDisconnectPressed() { _game.MultiplayerManager.Disconnect(); UpdateStatus(Status.Disconnected); } }