diff --git a/OpenRA.Game/Network/Handshake.cs b/OpenRA.Game/Network/Handshake.cs index 1fae372484..c11fd5c8de 100644 --- a/OpenRA.Game/Network/Handshake.cs +++ b/OpenRA.Game/Network/Handshake.cs @@ -20,19 +20,19 @@ namespace OpenRA.Network public string Version; public string Map; - public string Serialize() - { - var data = new List(); - data.Add(new MiniYamlNode("Handshake", FieldSaver.Save(this))); - return data.WriteToString(); - } - public static HandshakeRequest Deserialize(string data) { var handshake = new HandshakeRequest(); FieldLoader.Load(handshake, MiniYaml.FromString(data).First().Value); return handshake; } + + public string Serialize() + { + var data = new List(); + data.Add(new MiniYamlNode("Handshake", FieldSaver.Save(this))); + return data.WriteToString(); + } } public class HandshakeResponse @@ -42,16 +42,6 @@ namespace OpenRA.Network public string Password; [FieldLoader.Ignore] public Session.Client Client; - public string Serialize() - { - var data = new List(); - data.Add( new MiniYamlNode( "Handshake", null, - new string[]{ "Mod", "Version", "Password" }.Select( p => FieldSaver.SaveField(this, p) ).ToList() ) ); - data.Add(new MiniYamlNode("Client", FieldSaver.Save(Client))); - - return data.WriteToString(); - } - public static HandshakeResponse Deserialize(string data) { var handshake = new HandshakeResponse(); @@ -59,6 +49,7 @@ namespace OpenRA.Network var ys = MiniYaml.FromString(data); foreach (var y in ys) + { switch (y.Key) { case "Handshake": @@ -68,7 +59,19 @@ namespace OpenRA.Network FieldLoader.Load(handshake.Client, y.Value); break; } + } + return handshake; } + + public string Serialize() + { + var data = new List(); + data.Add(new MiniYamlNode("Handshake", null, + new string[] { "Mod", "Version", "Password" }.Select(p => FieldSaver.SaveField(this, p)).ToList())); + data.Add(new MiniYamlNode("Client", FieldSaver.Save(Client))); + + return data.WriteToString(); + } } } diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 8e5ad45553..15908ac281 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -18,6 +18,7 @@ namespace OpenRA.Network public class Session { public List Clients = new List(); + public List ClientPings = new List(); // Keyed by the PlayerReference id that the slot corresponds to public Dictionary Slots = new Dictionary(); @@ -30,23 +31,27 @@ namespace OpenRA.Network { var session = new Session(); - var ys = MiniYaml.FromString(data); - foreach (var y in ys) + var nodes = MiniYaml.FromString(data); + foreach (var node in nodes) { - var yy = y.Key.Split('@'); + var strings = node.Key.Split('@'); - switch (yy[0]) + switch (strings[0]) { + case "Client": + session.Clients.Add(Client.Deserialize(node.Value)); + break; + + case "ClientPing": + session.ClientPings.Add(ClientPing.Deserialize(node.Value)); + break; + case "GlobalSettings": - FieldLoader.Load(session.GlobalSettings, y.Value); + session.GlobalSettings = Global.Deserialize(node.Value); break; - case "Client": - session.Clients.Add(FieldLoader.Load(y.Value)); - break; - - case "Slot": - var s = FieldLoader.Load(y.Value); + case "Slot": + var s = Slot.Deserialize(node.Value); session.Slots.Add(s.PlayerReference, s); break; } @@ -107,9 +112,39 @@ namespace OpenRA.Network public bool IsReady { get { return State == ClientState.Ready; } } public bool IsInvalid { get { return State == ClientState.Invalid; } } public bool IsObserver { get { return Slot == null; } } + + public MiniYamlNode Serialize() + { + return new MiniYamlNode("Client@{0}".F(this.Index), FieldSaver.Save(this)); + } + + public static Client Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); + } + } + + public ClientPing PingFromClient(Client client) + { + return ClientPings.SingleOrDefault(p => p.Index == client.Index); + } + + public class ClientPing + { + public int Index; public int Latency = -1; public int LatencyJitter = -1; public int[] LatencyHistory = { }; + + public MiniYamlNode Serialize() + { + return new MiniYamlNode("ClientPing@{0}".F(this.Index), FieldSaver.Save(this)); + } + + public static ClientPing Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); + } } public class Slot @@ -123,6 +158,16 @@ namespace OpenRA.Network public bool LockTeam; public bool LockSpawn; public bool Required; + + public MiniYamlNode Serialize() + { + return new MiniYamlNode("Slot@{0}".F(this.PlayerReference), FieldSaver.Save(this)); + } + + public static Slot Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); + } } public class Global @@ -144,21 +189,34 @@ namespace OpenRA.Network public string StartingUnitsClass = "none"; public bool AllowVersionMismatch; public string GameUid; + + public MiniYamlNode Serialize() + { + return new MiniYamlNode("GlobalSettings", FieldSaver.Save(this)); + } + + public static Global Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); + } } public string Serialize() { - var clientData = new List(); + var sessionData = new List(); foreach (var client in Clients) - clientData.Add(new MiniYamlNode("Client@{0}".F(client.Index), FieldSaver.Save(client))); + sessionData.Add(client.Serialize()); + + foreach (var clientPing in ClientPings) + sessionData.Add(clientPing.Serialize()); foreach (var slot in Slots) - clientData.Add(new MiniYamlNode("Slot@{0}".F(slot.Key), FieldSaver.Save(slot.Value))); + sessionData.Add(slot.Value.Serialize()); - clientData.Add(new MiniYamlNode("GlobalSettings", FieldSaver.Save(GlobalSettings))); + sessionData.Add(GlobalSettings.Serialize()); - return clientData.WriteToString(); + return sessionData.WriteToString(); } } } diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 87b501bc90..5631435b5b 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -8,6 +8,7 @@ */ #endregion +using System.Collections.Generic; using System.Drawing; using System.Linq; using OpenRA.Traits; @@ -162,17 +163,76 @@ namespace OpenRA.Network case "SyncInfo": { orderManager.LobbyInfo = Session.Deserialize(order.TargetString); - - if (orderManager.FramesAhead != orderManager.LobbyInfo.GlobalSettings.OrderLatency - && !orderManager.GameStarted) - { - orderManager.FramesAhead = orderManager.LobbyInfo.GlobalSettings.OrderLatency; - Log.Write("server", "Order lag is now {0} frames.", orderManager.LobbyInfo.GlobalSettings.OrderLatency); - } + SetOrderLag(orderManager); Game.SyncLobbyInfo(); break; } + case "SyncLobbyClients": + { + var clients = new List(); + var nodes = MiniYaml.FromString(order.TargetString); + foreach (var node in nodes) + { + var strings = node.Key.Split('@'); + if (strings[0] == "Client") + clients.Add(Session.Client.Deserialize(node.Value)); + } + + orderManager.LobbyInfo.Clients = clients; + Game.SyncLobbyInfo(); + break; + } + + case "SyncLobbySlots": + { + var slots = new Dictionary(); + var nodes = MiniYaml.FromString(order.TargetString); + foreach (var node in nodes) + { + var strings = node.Key.Split('@'); + if (strings[0] == "Slot") + { + var slot = Session.Slot.Deserialize(node.Value); + slots.Add(slot.PlayerReference, slot); + } + } + + orderManager.LobbyInfo.Slots = slots; + Game.SyncLobbyInfo(); + break; + } + + case "SyncLobbyGlobalSettings": + { + var nodes = MiniYaml.FromString(order.TargetString); + foreach (var node in nodes) + { + var strings = node.Key.Split('@'); + if (strings[0] == "GlobalSettings") + orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value); + } + + SetOrderLag(orderManager); + Game.SyncLobbyInfo(); + break; + } + + case "SyncClientPings": + { + var pings = new List(); + var nodes = MiniYaml.FromString(order.TargetString); + foreach (var node in nodes) + { + var strings = node.Key.Split('@'); + if (strings[0] == "ClientPing") + pings.Add(Session.ClientPing.Deserialize(node.Value)); + } + + orderManager.LobbyInfo.ClientPings = pings; + break; + } + case "SetStance": { if (!Game.orderManager.LobbyInfo.GlobalSettings.FragileAlliances) @@ -227,5 +287,14 @@ namespace OpenRA.Network foreach (var nsc in w.ActorsWithTrait()) nsc.Trait.StanceChanged(nsc.Actor, p, target, oldStance, s); } + + static void SetOrderLag(OrderManager o) + { + if (o.FramesAhead != o.LobbyInfo.GlobalSettings.OrderLatency && !o.GameStarted) + { + o.FramesAhead = o.LobbyInfo.GlobalSettings.OrderLatency; + Log.Write("server", "Order lag is now {0} frames.", o.LobbyInfo.GlobalSettings.OrderLatency); + } + } } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index a07fc1361c..5b69dd77c7 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -322,6 +322,9 @@ namespace OpenRA.Server PreConns.Remove(newConn); Conns.Add(newConn); LobbyInfo.Clients.Add(client); + var clientPing = new Session.ClientPing(); + clientPing.Index = client.Index; + LobbyInfo.ClientPings.Add(clientPing); Log.Write("server", "Client {0}: Accepted connection from {1}.", newConn.PlayerIndex, newConn.socket.RemoteEndPoint); @@ -361,7 +364,7 @@ namespace OpenRA.Server else LobbyInfo.GlobalSettings.OrderLatency = 3; - SyncLobbyInfo(); + SyncLobbyGlobalSettings(); } public void UpdateInFlightFrames(Connection conn) @@ -442,6 +445,7 @@ namespace OpenRA.Server switch (so.Name) { case "Command": + { bool handled = false; foreach (var t in serverTraits.WithInterface()) if (handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data)) @@ -454,7 +458,8 @@ namespace OpenRA.Server } break; - + } + case "HandshakeResponse": ValidateClient(conn, so.Data); break; @@ -472,20 +477,22 @@ namespace OpenRA.Server break; } - var fromClient = GetClient(conn); - var history = fromClient.LatencyHistory.ToList(); + var pingFromClient = LobbyInfo.PingFromClient(GetClient(conn)); + if (pingFromClient == null) + return; + + var history = pingFromClient.LatencyHistory.ToList(); history.Add(Environment.TickCount - pingSent); // Cap ping history at 5 values (25 seconds) if (history.Count > 5) history.RemoveRange(0, history.Count - 5); - fromClient.Latency = history.Sum() / history.Count; - fromClient.LatencyJitter = (history.Max() - history.Min()) / 2; - fromClient.LatencyHistory = history.ToArray(); + pingFromClient.Latency = history.Sum() / history.Count; + pingFromClient.LatencyJitter = (history.Max() - history.Min()) / 2; + pingFromClient.LatencyHistory = history.ToArray(); - if (State == ServerState.WaitingPlayers) - SyncLobbyInfo(); + SyncClientPing(); break; } @@ -545,7 +552,7 @@ namespace OpenRA.Server } if (Conns.Any() || LobbyInfo.GlobalSettings.Dedicated) - SyncLobbyInfo(); + SyncLobbyClients(); if (!LobbyInfo.GlobalSettings.Dedicated && dropClient.IsAdmin) Shutdown(); @@ -570,6 +577,69 @@ namespace OpenRA.Server t.LobbyInfoSynced(this); } + public void SyncLobbyClients() + { + if (State != ServerState.WaitingPlayers) + return; + + // TODO: only need to sync the specific client that has changed to avoid conflicts + var clientData = new List(); + foreach (var client in LobbyInfo.Clients) + clientData.Add(client.Serialize()); + + DispatchOrders(null, 0, + new ServerOrder("SyncLobbyClients", clientData.WriteToString()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + + public void SyncLobbySlots() + { + if (State != ServerState.WaitingPlayers) + return; + + // TODO: don't sync all the slots if just one changed + var slotData = new List(); + foreach (var slot in LobbyInfo.Slots) + slotData.Add(slot.Value.Serialize()); + + DispatchOrders(null, 0, + new ServerOrder("SyncLobbySlots", slotData.WriteToString()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + + public void SyncLobbyGlobalSettings() + { + if (State != ServerState.WaitingPlayers) + return; + + var sessionData = new List(); + sessionData.Add(LobbyInfo.GlobalSettings.Serialize()); + + DispatchOrders(null, 0, + new ServerOrder("SyncLobbyGlobalSettings", sessionData.WriteToString()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + + public void SyncClientPing() + { + // TODO: split this further into per client ping orders + var clientPings = new List(); + foreach (var ping in LobbyInfo.ClientPings) + clientPings.Add(ping.Serialize()); + + DispatchOrders(null, 0, + new ServerOrder("SyncClientPings", clientPings.WriteToString()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + public void StartGame() { listener.Stop(); @@ -602,7 +672,7 @@ namespace OpenRA.Server foreach (var t in serverTraits.WithInterface()) t.GameStarted(this); - + // Check TimeOut if (Settings.TimeOut > 10000) { diff --git a/OpenRA.Mods.RA/Player/PlayerStatistics.cs b/OpenRA.Mods.RA/Player/PlayerStatistics.cs index 23550375cd..333e63e361 100644 --- a/OpenRA.Mods.RA/Player/PlayerStatistics.cs +++ b/OpenRA.Mods.RA/Player/PlayerStatistics.cs @@ -91,7 +91,13 @@ namespace OpenRA.Mods.RA case "Disconnected": case "ServerError": case "AuthenticationError": - case "SyncInfo": + case "SyncLobbyInfo": + case "SyncClientInfo": + case "SyncLobbySlots": + case "SyncLobbyGlobalSettings": + case "SyncClientPing": + case "Ping": + case "Pong": return; } if (order.OrderString.StartsWith("Dev")) diff --git a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs index bd1721f8e6..eaef9dc6ad 100644 --- a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs @@ -91,7 +91,7 @@ namespace OpenRA.Mods.RA.Server Log.Write("server", "Player @{0} is {1}", conn.socket.RemoteEndPoint, client.State); - server.SyncLobbyInfo(); + server.SyncLobbyClients(); CheckAutoStart(server, conn, client); @@ -106,7 +106,7 @@ namespace OpenRA.Mods.RA.Server return true; } - if (server.LobbyInfo.Slots.Any(sl => sl.Value.Required && + if (server.LobbyInfo.Slots.Any(sl => sl.Value.Required && server.LobbyInfo.ClientInSlot(sl.Key) == null)) { server.SendOrderTo(conn, "Message", "Unable to start the game until required slots are full."); @@ -130,8 +130,7 @@ namespace OpenRA.Mods.RA.Server client.Slot = s; S.SyncClientToPlayerReference(client, server.Map.Players[s]); - - server.SyncLobbyInfo(); + server.SyncLobbyClients(); CheckAutoStart(server, conn, client); return true; @@ -141,7 +140,7 @@ namespace OpenRA.Mods.RA.Server { if (bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllowSpectators)) { - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; } else @@ -158,7 +157,7 @@ namespace OpenRA.Mods.RA.Server client.Slot = null; client.SpawnPoint = 0; client.Color = HSLColor.FromRGB(255, 255, 255); - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; } else @@ -167,7 +166,7 @@ namespace OpenRA.Mods.RA.Server { "slot_close", s => { - if (!ValidateSlotCommand( server, conn, client, s, true )) + if (!ValidateSlotCommand(server, conn, client, s, true)) return false; // kick any player that's in the slot @@ -175,10 +174,14 @@ namespace OpenRA.Mods.RA.Server if (occupant != null) { if (occupant.Bot != null) + { server.LobbyInfo.Clients.Remove(occupant); + var ping = server.LobbyInfo.PingFromClient(occupant); + server.LobbyInfo.ClientPings.Remove(ping); + } else { - var occupantConn = server.Conns.FirstOrDefault( c => c.PlayerIndex == occupant.Index ); + var occupantConn = server.Conns.FirstOrDefault(c => c.PlayerIndex == occupant.Index); if (occupantConn != null) { server.SendOrderTo(occupantConn, "ServerError", "Your slot was closed by the host"); @@ -188,7 +191,7 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.Slots[s].Closed = true; - server.SyncLobbyInfo(); + server.SyncLobbySlots(); return true; }}, { "slot_open", @@ -199,13 +202,18 @@ namespace OpenRA.Mods.RA.Server var slot = server.LobbyInfo.Slots[s]; slot.Closed = false; + server.SyncLobbySlots(); // Slot may have a bot in it var occupant = server.LobbyInfo.ClientInSlot(s); if (occupant != null && occupant.Bot != null) + { server.LobbyInfo.Clients.Remove(occupant); + var ping = server.LobbyInfo.PingFromClient(occupant); + server.LobbyInfo.ClientPings.Remove(ping); + } + server.SyncLobbyClients(); - server.SyncLobbyInfo(); return true; }}, { "slot_bot", @@ -272,7 +280,8 @@ namespace OpenRA.Mods.RA.Server } S.SyncClientToPlayerReference(bot, server.Map.Players[parts[0]]); - server.SyncLobbyInfo(); + server.SyncLobbyClients(); + server.SyncLobbySlots(); return true; }}, { "map", @@ -290,6 +299,7 @@ namespace OpenRA.Mods.RA.Server return true; } server.LobbyInfo.GlobalSettings.Map = s; + var oldSlots = server.LobbyInfo.Slots.Keys.ToArray(); LoadMap(server); SetDefaultDifficulty(server); @@ -342,7 +352,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.FragileAlliances); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "allowcheats", @@ -361,7 +371,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllowCheats); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "shroud", @@ -380,7 +390,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.Shroud); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "fog", @@ -400,7 +410,7 @@ namespace OpenRA.Mods.RA.Server bool.TryParse(s, out server.LobbyInfo.GlobalSettings.Fog); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "assignteams", @@ -441,7 +451,7 @@ namespace OpenRA.Mods.RA.Server player.Team = assigned++ * teamCount / playerCount + 1; } - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "crates", @@ -460,7 +470,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.Crates); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "allybuildradius", @@ -479,7 +489,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllyBuildRadius); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "difficulty", @@ -499,7 +509,7 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.GlobalSettings.Difficulty = s; - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "startingunits", @@ -518,7 +528,7 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.GlobalSettings.StartingUnitsClass = s; - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "startingcash", @@ -537,7 +547,8 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.GlobalSettings.StartingCash = Exts.ParseIntegerInvariant(s); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); + return true; }}, { "kick", @@ -581,7 +592,8 @@ namespace OpenRA.Mods.RA.Server server.TempBans.Add(kickConnIP); } - server.SyncLobbyInfo(); + server.SyncLobbyClients(); + server.SyncLobbySlots(); return true; }}, { "name", @@ -589,7 +601,7 @@ namespace OpenRA.Mods.RA.Server { Log.Write("server", "Player@{0} is now known as {1}", conn.socket.RemoteEndPoint, s); client.Name = s; - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "race", @@ -607,7 +619,7 @@ namespace OpenRA.Mods.RA.Server return true; targetClient.Country = parts[1]; - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "team", @@ -632,7 +644,7 @@ namespace OpenRA.Mods.RA.Server } targetClient.Team = team; - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "spawn", @@ -668,7 +680,7 @@ namespace OpenRA.Mods.RA.Server } targetClient.SpawnPoint = spawnPoint; - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "color", @@ -687,7 +699,7 @@ namespace OpenRA.Mods.RA.Server var ci = parts[1].Split(',').Select(cc => Exts.ParseIntegerInvariant(cc)).ToArray(); targetClient.Color = targetClient.PreferredColor = new HSLColor((byte)ci[0], (byte)ci[1], (byte)ci[2]); - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }} }; diff --git a/OpenRA.Mods.RA/Widgets/Logic/ClientTooltipLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ClientTooltipLogic.cs index 1805c23fa0..1e17f43136 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ClientTooltipLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ClientTooltipLogic.cs @@ -71,8 +71,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic }; admin.IsVisible = () => orderManager.LobbyInfo.ClientWithIndex(clientIndex).IsAdmin; - latency.GetText = () => LobbyUtils.LatencyDescription(orderManager.LobbyInfo.ClientWithIndex(clientIndex).Latency); - latency.GetColor = () => LobbyUtils.LatencyColor(orderManager.LobbyInfo.ClientWithIndex(clientIndex).Latency); + var client = orderManager.LobbyInfo.ClientWithIndex(clientIndex); + var ping = orderManager.LobbyInfo.PingFromClient(client); + latency.GetText = () => LobbyUtils.LatencyDescription(ping); + latency.GetColor = () => LobbyUtils.LatencyColor(ping); var address = orderManager.LobbyInfo.ClientWithIndex(clientIndex).IpAddress; if (address == "127.0.0.1" && UPnP.NatDevice != null) address = UPnP.NatDevice.GetExternalIP().ToString(); diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs index 554679f1de..7b67c0a0fc 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs @@ -183,26 +183,32 @@ namespace OpenRA.Mods.RA.Widgets.Logic orderManager.IssueOrder(Order.Command("spawn {0} {1}".F((playerToMove ?? orderManager.LocalClient).Index, selectedSpawn))); } - public static Color LatencyColor(int latency) + public static Color LatencyColor(Session.ClientPing ping) { + if (ping == null) + return Color.Gray; + // Levels set relative to the default order lag of 3 net ticks (360ms) // TODO: Adjust this once dynamic lag is implemented - if (latency < 0) + if (ping.Latency < 0) return Color.Gray; - if (latency < 300) + if (ping.Latency < 300) return Color.LimeGreen; - if (latency < 600) + if (ping.Latency < 600) return Color.Orange; return Color.Red; } - public static string LatencyDescription(int latency) + public static string LatencyDescription(Session.ClientPing ping) { - if (latency < 0) + if (ping == null) return "Unknown"; - if (latency < 300) + + if (ping.Latency < 0) + return "Unknown"; + if (ping.Latency < 300) return "Good"; - if (latency < 600) + if (ping.Latency < 600) return "Moderate"; return "Poor"; } @@ -236,7 +242,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic block.IsVisible = () => visible; if (visible) - block.Get("LATENCY_COLOR").GetColor = () => LatencyColor(c.Latency); + block.Get("LATENCY_COLOR").GetColor = () => LatencyColor( + orderManager.LobbyInfo.PingFromClient(c)); var tooltip = parent.Get("CLIENT_REGION"); tooltip.IsVisible = () => visible;