diff --git a/OpenRA.Game/Network/GameServer.cs b/OpenRA.Game/Network/GameServer.cs index fed810d5fb..d2cf4c918a 100644 --- a/OpenRA.Game/Network/GameServer.cs +++ b/OpenRA.Game/Network/GameServer.cs @@ -10,6 +10,8 @@ using System.Collections.Generic; using System.Linq; +using System.Net.NetworkInformation; +using System.Threading; namespace OpenRA.Network { @@ -65,5 +67,33 @@ namespace OpenRA.Network return UsefulMods.All(m => Game.CurrentMods.ContainsKey(m.Key) && AreVersionsCompatible(m.Value, Game.CurrentMods[m.Key].Version)); } + + public int Latency = -1; + bool hasBeenPinged; + public void Ping() + { + if (!hasBeenPinged) + { + hasBeenPinged = true; + var pingSender = new Ping(); + pingSender.PingCompleted += new PingCompletedEventHandler(pongRecieved); + AutoResetEvent waiter = new AutoResetEvent(false); + pingSender.SendAsync(Address.Split(':')[0], waiter); + } + } + + void pongRecieved(object sender, PingCompletedEventArgs e) + { + if (e.Cancelled || e.Error != null) + Latency = -1; + else + { + PingReply pong = e.Reply; + if (pong != null && pong.Status == IPStatus.Success) + Latency = (int)pong.RoundtripTime; + else + Latency = -1; + } + } } } diff --git a/OpenRA.Mods.RA/Widgets/Logic/ServerBrowserLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ServerBrowserLogic.cs index 8b738d2283..124375c205 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ServerBrowserLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ServerBrowserLogic.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Drawing; using OpenRA.FileFormats; using OpenRA.Network; +using OpenRA.Server; using OpenRA.Widgets; namespace OpenRA.Mods.RA.Widgets.Logic @@ -25,9 +26,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic Action OpenLobby; Action OnExit; - enum SearchStatus { Fetching, Failed, NoGames, Hidden } + enum SearchStatus { Fetching, Failed, NoGames, Hidden, Pinging } SearchStatus searchStatus = SearchStatus.Fetching; + bool showWaiting = true; + bool showEmpty = true; + bool showStarted = true; + bool showIncompatible = false; + public string ProgressLabelText() { switch (searchStatus) @@ -35,6 +41,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic case SearchStatus.Fetching: return "Fetching game list..."; case SearchStatus.Failed: return "Failed to contact master server."; case SearchStatus.NoGames: return "No games found."; + case SearchStatus.Pinging: return "Pinging compatible servers."; default: return ""; } } @@ -50,13 +57,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic // Menu buttons var refreshButton = panel.Get("REFRESH_BUTTON"); refreshButton.IsDisabled = () => searchStatus == SearchStatus.Fetching; - refreshButton.OnClick = () => + refreshButton.OnClick = () => ServerList.Query(games => RefreshServerList(panel, games)); + + var pingButton = panel.GetOrNull("PING_BUTTON"); + if (pingButton != null) { - searchStatus = SearchStatus.Fetching; - sl.RemoveChildren(); - currentServer = null; - ServerList.Query(games => RefreshServerList(panel, games)); - }; + pingButton.IsDisabled = () => searchStatus == SearchStatus.Pinging || + searchStatus == SearchStatus.Fetching || searchStatus == SearchStatus.Failed; + pingButton.OnClick = () => ServerList.Query(games => PingServerList(panel, games)); + } var join = panel.Get("JOIN_BUTTON"); join.IsDisabled = () => currentServer == null || !currentServer.CanJoin(); @@ -73,6 +82,34 @@ namespace OpenRA.Mods.RA.Widgets.Logic progressText.IsVisible = () => searchStatus != SearchStatus.Hidden; progressText.GetText = ProgressLabelText; + var showWaitingCheckbox = panel.GetOrNull("WAITING_FOR_PLAYERS"); + if (showWaitingCheckbox != null) + { + showWaitingCheckbox.IsChecked = () => showWaiting; + showWaitingCheckbox.OnClick = () => { showWaiting ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; + } + + var showEmptyCheckbox = panel.GetOrNull("EMPTY"); + if (showEmptyCheckbox != null) + { + showEmptyCheckbox.IsChecked = () => showEmpty; + showEmptyCheckbox.OnClick = () => { showEmpty ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; + } + + var showAlreadyStartedCheckbox = panel.GetOrNull("ALREADY_STARTED"); + if (showAlreadyStartedCheckbox != null) + { + showAlreadyStartedCheckbox.IsChecked = () => showStarted; + showAlreadyStartedCheckbox.OnClick = () => { showStarted ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; + } + + var showIncompatibleCheckbox = panel.GetOrNull("INCOMPATIBLE_VERSION"); + if (showIncompatibleCheckbox != null) + { + showIncompatibleCheckbox.IsChecked = () => showIncompatible; + showIncompatibleCheckbox.OnClick = () => { showIncompatible ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; + } + ServerList.Query(games => RefreshServerList(panel, games)); } @@ -104,9 +141,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (game == null) return ""; - if (game.State == 1) return "Waiting for players"; - if (game.State == 2) return "Playing"; - else return "Unknown"; + if (game.State == (int)ServerState.WaitingPlayers) + return "Waiting for players"; + if (game.State == (int)ServerState.GameStarted) + return "Playing"; + if (game.State == (int)ServerState.ShuttingDown) + return "Server shutting down"; + + return "Unknown server state"; } Map GetMapPreview(GameServer game) @@ -127,10 +169,56 @@ namespace OpenRA.Mods.RA.Widgets.Logic return s.UsefulMods.Select(m => GenerateModLabel(m)).JoinWith("\n"); } + static string GetPing(GameServer s) + { + if (s.Latency > -1) + return "Ping: {0} ms".F(s.Latency); + else + return "Ping: ? ms"; + } + + void PingServerList(Widget panel, IEnumerable games) + { + searchStatus = SearchStatus.Pinging; + + foreach (var loop in games.Where(g => g.CanJoin())) + { + var game = loop; + + if (game == null) + continue; + + game.Ping(); + } + + searchStatus = SearchStatus.Hidden; + + RefreshServerList(panel, games); + } + + bool Filtered(GameServer game) + { + if ((game.State == (int)ServerState.GameStarted) && !showStarted) + return true; + + if ((game.State == (int)ServerState.WaitingPlayers) && !showWaiting) + return true; + + if ((game.Players == 0) && !showEmpty) + return true; + + if (!game.CompatibleVersion() && !showIncompatible) + return true; + + return false; + } + public void RefreshServerList(Widget panel, IEnumerable games) { var sl = panel.Get("SERVER_LIST"); + searchStatus = SearchStatus.Fetching; + sl.RemoveChildren(); currentServer = null; @@ -186,6 +274,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic version.GetText = () => GenerateModsLabel(game); version.IsVisible = () => !game.CompatibleVersion(); + var ping = item.GetOrNull("PING"); + if (ping != null) + { + ping.GetText = () => GetPing(game); + ping.IsVisible = () => game.CompatibleVersion(); + } + if (!canJoin) { title.GetColor = () => Color.Gray; @@ -194,9 +289,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic state.GetColor = () => Color.Gray; ip.GetColor = () => Color.Gray; version.GetColor = () => Color.Gray; + if (ping != null) + ping.GetColor = () => Color.Gray; } - sl.AddChild(item); + if (!Filtered(game)) + sl.AddChild(item); } } } diff --git a/mods/ra/chrome/serverbrowser.yaml b/mods/ra/chrome/serverbrowser.yaml index 8e96dc2d90..18ce40484d 100644 --- a/mods/ra/chrome/serverbrowser.yaml +++ b/mods/ra/chrome/serverbrowser.yaml @@ -3,21 +3,52 @@ Background@JOINSERVER_BG: X:(WINDOW_RIGHT - WIDTH)/2 Y:(WINDOW_BOTTOM - HEIGHT)/2 Width:540 - Height:535 + Height:505 Children: Label@JOINSERVER_LABEL_TITLE: X:0 - Y:20 + Y:15 Width:PARENT_RIGHT Height:25 Text:Join Server Align:Center Font:Bold + Label@SHOW_LABEL_TITLE: + X:20 + Y:45 + Width:20 + Height:25 + Text:Show: + Font:Bold + Checkbox@WAITING_FOR_PLAYERS: + X:80 + Y:50 + Width:100 + Height:20 + Text:Waiting + Checkbox@EMPTY: + X:180 + Y:50 + Width:100 + Height:20 + Text:Empty + Checkbox@ALREADY_STARTED: + X:280 + Y:50 + Width:100 + Height:20 + Text:Started + Checkbox@INCOMPATIBLE_VERSION: + X:380 + Y:50 + Width:100 + Height:20 + Text:Incompatible ScrollPanel@SERVER_LIST: X:20 - Y:50 + Y:80 Width:500 - Height:425 + Height:355 Children: ScrollItem@SERVER_TEMPLATE: Width:PARENT_RIGHT-27 @@ -64,25 +95,37 @@ Background@JOINSERVER_BG: Y:40 Align:Right Height:25 - + Label@PING: + Width:140 + X:PARENT_RIGHT-150 + Y:40 + Align:Right + Height:25 Label@PROGRESS_LABEL: X:(PARENT_RIGHT - WIDTH) / 2 Y:PARENT_BOTTOM / 2 - HEIGHT Width:150 Height:30 Text:Fetching games... - Align:Center + Align:Center Button@REFRESH_BUTTON: X:20 Y:PARENT_BOTTOM - 45 - Width:120 + Width:100 Height:25 Text:Refresh Font:Bold + Button@PING_BUTTON: + X:140 + Y:PARENT_BOTTOM - 45 + Width:100 + Height:25 + Text:Ping + Font:Bold Button@JOIN_BUTTON: X:PARENT_RIGHT - 140 - 130 Y:PARENT_BOTTOM - 45 - Width:120 + Width:100 Height:25 Text:Join Font:Bold @@ -90,7 +133,7 @@ Background@JOINSERVER_BG: Button@BACK_BUTTON: X:PARENT_RIGHT - 140 Y:PARENT_BOTTOM - 45 - Width:120 + Width:100 Height:25 Text:Cancel Font:Bold