From f365f9da2b5f7bfd762269cf0da301d9aea0cdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Wed, 16 Apr 2014 17:29:26 +0200 Subject: [PATCH 1/4] split lobby SyncInfo order into smaller chunks closes #4594 --- OpenRA.Game/Network/Session.cs | 31 ++++++++-- OpenRA.Game/Network/UnitOrders.cs | 62 ++++++++++++++++++-- OpenRA.Game/Server/Server.cs | 50 +++++++++++++++- OpenRA.Mods.RA/Player/PlayerStatistics.cs | 5 +- OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs | 58 +++++++++--------- 5 files changed, 166 insertions(+), 40 deletions(-) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 8e5ad45553..58d4242b2f 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -110,6 +110,13 @@ namespace OpenRA.Network public int Latency = -1; public int LatencyJitter = -1; public int[] LatencyHistory = { }; + + public string Serialize() + { + var clientData = new List(); + clientData.Add(new MiniYamlNode("Client@{0}".F(this.Index), FieldSaver.Save(this))); + return clientData.WriteToString(); + } } public class Slot @@ -123,6 +130,13 @@ namespace OpenRA.Network public bool LockTeam; public bool LockSpawn; public bool Required; + + public string Serialize() + { + var slotData = new List(); + slotData.Add(new MiniYamlNode("Slot@{0}".F(this.PlayerReference), FieldSaver.Save(this))); + return slotData.WriteToString(); + } } public class Global @@ -144,21 +158,28 @@ namespace OpenRA.Network public string StartingUnitsClass = "none"; public bool AllowVersionMismatch; public string GameUid; + + public string Serialize() + { + var globalData = new List(); + globalData.Add(new MiniYamlNode("GlobalSettings", FieldSaver.Save(this))); + return globalData.WriteToString(); + } } public string Serialize() { - var clientData = new List(); + var sessionData = new System.Text.StringBuilder(); foreach (var client in Clients) - clientData.Add(new MiniYamlNode("Client@{0}".F(client.Index), FieldSaver.Save(client))); + sessionData.Append(client.Serialize()); foreach (var slot in Slots) - clientData.Add(new MiniYamlNode("Slot@{0}".F(slot.Key), FieldSaver.Save(slot.Value))); + sessionData.Append(slot.Value.Serialize()); - clientData.Add(new MiniYamlNode("GlobalSettings", FieldSaver.Save(GlobalSettings))); + sessionData.Append(GlobalSettings.Serialize()); - return clientData.WriteToString(); + return sessionData.ToString(); } } } diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 87b501bc90..f7f629e800 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,13 +163,57 @@ namespace OpenRA.Network case "SyncInfo": { orderManager.LobbyInfo = Session.Deserialize(order.TargetString); + SetOrderLag(orderManager); + Game.SyncLobbyInfo(); + break; + } - if (orderManager.FramesAhead != orderManager.LobbyInfo.GlobalSettings.OrderLatency - && !orderManager.GameStarted) + case "SyncLobbyClients": + { + var clients = new List(); + var nodes = MiniYaml.FromString(order.TargetString); + foreach (var node in nodes) { - orderManager.FramesAhead = orderManager.LobbyInfo.GlobalSettings.OrderLatency; - Log.Write("server", "Order lag is now {0} frames.", orderManager.LobbyInfo.GlobalSettings.OrderLatency); + var strings = node.Key.Split('@'); + if (strings[0] == "Client") + clients.Add(FieldLoader.Load(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 = FieldLoader.Load(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") + FieldLoader.Load(orderManager.LobbyInfo.GlobalSettings, node.Value); + } + + SetOrderLag(orderManager); Game.SyncLobbyInfo(); break; } @@ -227,5 +272,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..614221da22 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -361,7 +361,7 @@ namespace OpenRA.Server else LobbyInfo.GlobalSettings.OrderLatency = 3; - SyncLobbyInfo(); + SyncLobbyGlobalSettings(); } public void UpdateInFlightFrames(Connection conn) @@ -485,7 +485,7 @@ namespace OpenRA.Server fromClient.LatencyHistory = history.ToArray(); if (State == ServerState.WaitingPlayers) - SyncLobbyInfo(); + SyncLobbyClients(); // TODO: SyncClientLatency break; } @@ -545,7 +545,7 @@ namespace OpenRA.Server } if (Conns.Any() || LobbyInfo.GlobalSettings.Dedicated) - SyncLobbyInfo(); + SyncLobbyClients(); if (!LobbyInfo.GlobalSettings.Dedicated && dropClient.IsAdmin) Shutdown(); @@ -570,6 +570,50 @@ namespace OpenRA.Server t.LobbyInfoSynced(this); } + public void SyncLobbyClients() + { + if (State != ServerState.WaitingPlayers) + return; + + var clientData = new System.Text.StringBuilder(); + foreach (var client in LobbyInfo.Clients) + clientData.Append(client.Serialize()); + + DispatchOrders(null, 0, + new ServerOrder("SyncLobbyClients", clientData.ToString()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + + public void SyncLobbySlots() + { + if (State != ServerState.WaitingPlayers) + return; + + var slotData = new System.Text.StringBuilder(); + foreach (var slot in LobbyInfo.Slots) + slotData.Append(slot.Value.Serialize()); + + DispatchOrders(null, 0, + new ServerOrder("SyncLobbySlots", slotData.ToString()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + + public void SyncLobbyGlobalSettings() + { + if (State != ServerState.WaitingPlayers) + return; + + DispatchOrders(null, 0, + new ServerOrder("SyncLobbyGlobalSettings", LobbyInfo.GlobalSettings.Serialize()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + public void StartGame() { listener.Stop(); diff --git a/OpenRA.Mods.RA/Player/PlayerStatistics.cs b/OpenRA.Mods.RA/Player/PlayerStatistics.cs index 23550375cd..71175c7bc7 100644 --- a/OpenRA.Mods.RA/Player/PlayerStatistics.cs +++ b/OpenRA.Mods.RA/Player/PlayerStatistics.cs @@ -91,7 +91,10 @@ namespace OpenRA.Mods.RA case "Disconnected": case "ServerError": case "AuthenticationError": - case "SyncInfo": + case "SyncLobbyInfo": + case "SyncClientInfo": + case "SyncLobbySlots": + case "SyncLobbyGlobalSettings": return; } if (order.OrderString.StartsWith("Dev")) diff --git a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs index bd1721f8e6..5b1866dec9 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 @@ -178,7 +177,7 @@ namespace OpenRA.Mods.RA.Server server.LobbyInfo.Clients.Remove(occupant); 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 +187,7 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.Slots[s].Closed = true; - server.SyncLobbyInfo(); + server.SyncLobbySlots(); return true; }}, { "slot_open", @@ -199,13 +198,14 @@ 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); + server.SyncLobbyClients(); - server.SyncLobbyInfo(); return true; }}, { "slot_bot", @@ -272,7 +272,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 +291,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 +344,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.FragileAlliances); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "allowcheats", @@ -361,7 +363,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllowCheats); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "shroud", @@ -380,7 +382,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.Shroud); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "fog", @@ -400,7 +402,7 @@ namespace OpenRA.Mods.RA.Server bool.TryParse(s, out server.LobbyInfo.GlobalSettings.Fog); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "assignteams", @@ -441,7 +443,7 @@ namespace OpenRA.Mods.RA.Server player.Team = assigned++ * teamCount / playerCount + 1; } - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "crates", @@ -460,7 +462,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.Crates); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "allybuildradius", @@ -479,7 +481,7 @@ namespace OpenRA.Mods.RA.Server } bool.TryParse(s, out server.LobbyInfo.GlobalSettings.AllyBuildRadius); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "difficulty", @@ -499,7 +501,7 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.GlobalSettings.Difficulty = s; - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "startingunits", @@ -518,7 +520,7 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.GlobalSettings.StartingUnitsClass = s; - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); return true; }}, { "startingcash", @@ -537,7 +539,8 @@ namespace OpenRA.Mods.RA.Server } server.LobbyInfo.GlobalSettings.StartingCash = Exts.ParseIntegerInvariant(s); - server.SyncLobbyInfo(); + server.SyncLobbyGlobalSettings(); + return true; }}, { "kick", @@ -581,7 +584,8 @@ namespace OpenRA.Mods.RA.Server server.TempBans.Add(kickConnIP); } - server.SyncLobbyInfo(); + server.SyncLobbyClients(); + server.SyncLobbySlots(); return true; }}, { "name", @@ -589,7 +593,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 +611,7 @@ namespace OpenRA.Mods.RA.Server return true; targetClient.Country = parts[1]; - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "team", @@ -632,7 +636,7 @@ namespace OpenRA.Mods.RA.Server } targetClient.Team = team; - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "spawn", @@ -668,7 +672,7 @@ namespace OpenRA.Mods.RA.Server } targetClient.SpawnPoint = spawnPoint; - server.SyncLobbyInfo(); + server.SyncLobbyClients(); return true; }}, { "color", @@ -687,7 +691,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; }} }; From f68a6bbd7691175d1def8c03262388b684f69ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Wed, 16 Apr 2014 19:35:43 +0200 Subject: [PATCH 2/4] split latency from Client into ClientPing closes #4282 --- OpenRA.Game/Network/Session.cs | 27 +++++++++++++- OpenRA.Game/Network/UnitOrders.cs | 15 ++++++++ OpenRA.Game/Server/Server.cs | 35 ++++++++++++++----- OpenRA.Mods.RA/Player/PlayerStatistics.cs | 3 ++ OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs | 8 +++++ .../Widgets/Logic/ClientTooltipLogic.cs | 6 ++-- OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs | 25 ++++++++----- 7 files changed, 99 insertions(+), 20 deletions(-) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 58d4242b2f..d60ea085f9 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(); @@ -45,6 +46,10 @@ namespace OpenRA.Network session.Clients.Add(FieldLoader.Load(y.Value)); break; + case "ClientPing": + session.ClientPings.Add(FieldLoader.Load(y.Value)); + break; + case "Slot": var s = FieldLoader.Load(y.Value); session.Slots.Add(s.PlayerReference, s); @@ -107,6 +112,23 @@ 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 string Serialize() + { + var clientData = new List(); + clientData.Add(new MiniYamlNode("Client@{0}".F(this.Index), FieldSaver.Save(this))); + return clientData.WriteToString(); + } + } + + 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 = { }; @@ -114,7 +136,7 @@ namespace OpenRA.Network public string Serialize() { var clientData = new List(); - clientData.Add(new MiniYamlNode("Client@{0}".F(this.Index), FieldSaver.Save(this))); + clientData.Add(new MiniYamlNode("ClientPing@{0}".F(this.Index), FieldSaver.Save(this))); return clientData.WriteToString(); } } @@ -174,6 +196,9 @@ namespace OpenRA.Network foreach (var client in Clients) sessionData.Append(client.Serialize()); + foreach (var clientPing in ClientPings) + sessionData.Append(clientPing.Serialize()); + foreach (var slot in Slots) sessionData.Append(slot.Value.Serialize()); diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index f7f629e800..94e7f95828 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -218,6 +218,21 @@ namespace OpenRA.Network 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(FieldLoader.Load(node.Value)); + } + + orderManager.LobbyInfo.ClientPings = pings; + break; + } + case "SetStance": { if (!Game.orderManager.LobbyInfo.GlobalSettings.FragileAlliances) diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 614221da22..80d34a19c9 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); @@ -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,7 @@ namespace OpenRA.Server } break; - + } case "HandshakeResponse": ValidateClient(conn, so.Data); break; @@ -472,20 +476,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) - SyncLobbyClients(); // TODO: SyncClientLatency + SyncClientPing(); break; } @@ -614,6 +620,19 @@ namespace OpenRA.Server t.LobbyInfoSynced(this); } + public void SyncClientPing() + { + var clientPings = new System.Text.StringBuilder(); + foreach (var ping in LobbyInfo.ClientPings) + clientPings.Append(ping.Serialize()); + + DispatchOrders(null, 0, + new ServerOrder("SyncClientPings", clientPings.ToString()).Serialize()); + + foreach (var t in serverTraits.WithInterface()) + t.LobbyInfoSynced(this); + } + public void StartGame() { listener.Stop(); diff --git a/OpenRA.Mods.RA/Player/PlayerStatistics.cs b/OpenRA.Mods.RA/Player/PlayerStatistics.cs index 71175c7bc7..333e63e361 100644 --- a/OpenRA.Mods.RA/Player/PlayerStatistics.cs +++ b/OpenRA.Mods.RA/Player/PlayerStatistics.cs @@ -95,6 +95,9 @@ namespace OpenRA.Mods.RA 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 5b1866dec9..eaef9dc6ad 100644 --- a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs @@ -174,7 +174,11 @@ 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); @@ -203,7 +207,11 @@ namespace OpenRA.Mods.RA.Server // 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(); 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 d16c28686e..4bfb250620 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs @@ -163,26 +163,32 @@ namespace OpenRA.Mods.RA.Widgets.Logic } } - 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"; } @@ -216,7 +222,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; From c7fad3a6933d1bbcd50d2f7f6c3ef12702911cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Thu, 15 May 2014 09:51:45 +0200 Subject: [PATCH 3/4] StyleCop and TODO --- OpenRA.Game/Network/Handshake.cs | 37 +++++++++++++++++--------------- OpenRA.Game/Network/Session.cs | 18 ++++++++-------- OpenRA.Game/Server/Server.cs | 4 ++++ 3 files changed, 33 insertions(+), 26 deletions(-) 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 d60ea085f9..7ee8d64bf5 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -38,19 +38,19 @@ namespace OpenRA.Network switch (yy[0]) { + case "Client": + session.Clients.Add(FieldLoader.Load(y.Value)); + break; + + case "ClientPing": + session.ClientPings.Add(FieldLoader.Load(y.Value)); + break; + case "GlobalSettings": FieldLoader.Load(session.GlobalSettings, y.Value); break; - case "Client": - session.Clients.Add(FieldLoader.Load(y.Value)); - break; - - case "ClientPing": - session.ClientPings.Add(FieldLoader.Load(y.Value)); - break; - - case "Slot": + case "Slot": var s = FieldLoader.Load(y.Value); session.Slots.Add(s.PlayerReference, s); break; diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 80d34a19c9..325f5f4c35 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -459,6 +459,7 @@ namespace OpenRA.Server break; } + case "HandshakeResponse": ValidateClient(conn, so.Data); break; @@ -581,6 +582,7 @@ namespace OpenRA.Server if (State != ServerState.WaitingPlayers) return; + // TODO: only need to sync the specific client that has changed to avoid conflicts var clientData = new System.Text.StringBuilder(); foreach (var client in LobbyInfo.Clients) clientData.Append(client.Serialize()); @@ -597,6 +599,7 @@ namespace OpenRA.Server if (State != ServerState.WaitingPlayers) return; + // TODO: don't sync all the slots if just one changed var slotData = new System.Text.StringBuilder(); foreach (var slot in LobbyInfo.Slots) slotData.Append(slot.Value.Serialize()); @@ -622,6 +625,7 @@ namespace OpenRA.Server public void SyncClientPing() { + // TODO: split this further into per client ping orders var clientPings = new System.Text.StringBuilder(); foreach (var ping in LobbyInfo.ClientPings) clientPings.Append(ping.Serialize()); From 86271c3dd16cda2ace4f32ae11439868502f49e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Thu, 15 May 2014 11:36:24 +0200 Subject: [PATCH 4/4] use List instead of System.Text.StringBuilder add Deserialize methods for everything in Network.Session --- OpenRA.Game/Network/Session.cs | 72 ++++++++++++++++++------------- OpenRA.Game/Network/UnitOrders.cs | 8 ++-- OpenRA.Game/Server/Server.cs | 25 ++++++----- 3 files changed, 60 insertions(+), 45 deletions(-) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 7ee8d64bf5..15908ac281 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -31,27 +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(FieldLoader.Load(y.Value)); + session.Clients.Add(Client.Deserialize(node.Value)); break; case "ClientPing": - session.ClientPings.Add(FieldLoader.Load(y.Value)); + session.ClientPings.Add(ClientPing.Deserialize(node.Value)); break; case "GlobalSettings": - FieldLoader.Load(session.GlobalSettings, y.Value); + session.GlobalSettings = Global.Deserialize(node.Value); break; case "Slot": - var s = FieldLoader.Load(y.Value); + var s = Slot.Deserialize(node.Value); session.Slots.Add(s.PlayerReference, s); break; } @@ -113,11 +113,14 @@ namespace OpenRA.Network public bool IsInvalid { get { return State == ClientState.Invalid; } } public bool IsObserver { get { return Slot == null; } } - public string Serialize() + public MiniYamlNode Serialize() { - var clientData = new List(); - clientData.Add(new MiniYamlNode("Client@{0}".F(this.Index), FieldSaver.Save(this))); - return clientData.WriteToString(); + return new MiniYamlNode("Client@{0}".F(this.Index), FieldSaver.Save(this)); + } + + public static Client Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); } } @@ -133,11 +136,14 @@ namespace OpenRA.Network public int LatencyJitter = -1; public int[] LatencyHistory = { }; - public string Serialize() + public MiniYamlNode Serialize() { - var clientData = new List(); - clientData.Add(new MiniYamlNode("ClientPing@{0}".F(this.Index), FieldSaver.Save(this))); - return clientData.WriteToString(); + return new MiniYamlNode("ClientPing@{0}".F(this.Index), FieldSaver.Save(this)); + } + + public static ClientPing Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); } } @@ -153,11 +159,14 @@ namespace OpenRA.Network public bool LockSpawn; public bool Required; - public string Serialize() + public MiniYamlNode Serialize() { - var slotData = new List(); - slotData.Add(new MiniYamlNode("Slot@{0}".F(this.PlayerReference), FieldSaver.Save(this))); - return slotData.WriteToString(); + return new MiniYamlNode("Slot@{0}".F(this.PlayerReference), FieldSaver.Save(this)); + } + + public static Slot Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); } } @@ -181,30 +190,33 @@ namespace OpenRA.Network public bool AllowVersionMismatch; public string GameUid; - public string Serialize() + public MiniYamlNode Serialize() { - var globalData = new List(); - globalData.Add(new MiniYamlNode("GlobalSettings", FieldSaver.Save(this))); - return globalData.WriteToString(); + return new MiniYamlNode("GlobalSettings", FieldSaver.Save(this)); + } + + public static Global Deserialize(MiniYaml data) + { + return FieldLoader.Load(data); } } public string Serialize() { - var sessionData = new System.Text.StringBuilder(); + var sessionData = new List(); foreach (var client in Clients) - sessionData.Append(client.Serialize()); + sessionData.Add(client.Serialize()); foreach (var clientPing in ClientPings) - sessionData.Append(clientPing.Serialize()); + sessionData.Add(clientPing.Serialize()); foreach (var slot in Slots) - sessionData.Append(slot.Value.Serialize()); + sessionData.Add(slot.Value.Serialize()); - sessionData.Append(GlobalSettings.Serialize()); + sessionData.Add(GlobalSettings.Serialize()); - return sessionData.ToString(); + return sessionData.WriteToString(); } } } diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 94e7f95828..5631435b5b 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -176,7 +176,7 @@ namespace OpenRA.Network { var strings = node.Key.Split('@'); if (strings[0] == "Client") - clients.Add(FieldLoader.Load(node.Value)); + clients.Add(Session.Client.Deserialize(node.Value)); } orderManager.LobbyInfo.Clients = clients; @@ -193,7 +193,7 @@ namespace OpenRA.Network var strings = node.Key.Split('@'); if (strings[0] == "Slot") { - var slot = FieldLoader.Load(node.Value); + var slot = Session.Slot.Deserialize(node.Value); slots.Add(slot.PlayerReference, slot); } } @@ -210,7 +210,7 @@ namespace OpenRA.Network { var strings = node.Key.Split('@'); if (strings[0] == "GlobalSettings") - FieldLoader.Load(orderManager.LobbyInfo.GlobalSettings, node.Value); + orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value); } SetOrderLag(orderManager); @@ -226,7 +226,7 @@ namespace OpenRA.Network { var strings = node.Key.Split('@'); if (strings[0] == "ClientPing") - pings.Add(FieldLoader.Load(node.Value)); + pings.Add(Session.ClientPing.Deserialize(node.Value)); } orderManager.LobbyInfo.ClientPings = pings; diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 325f5f4c35..5b69dd77c7 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -583,12 +583,12 @@ namespace OpenRA.Server return; // TODO: only need to sync the specific client that has changed to avoid conflicts - var clientData = new System.Text.StringBuilder(); + var clientData = new List(); foreach (var client in LobbyInfo.Clients) - clientData.Append(client.Serialize()); + clientData.Add(client.Serialize()); DispatchOrders(null, 0, - new ServerOrder("SyncLobbyClients", clientData.ToString()).Serialize()); + new ServerOrder("SyncLobbyClients", clientData.WriteToString()).Serialize()); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -600,12 +600,12 @@ namespace OpenRA.Server return; // TODO: don't sync all the slots if just one changed - var slotData = new System.Text.StringBuilder(); + var slotData = new List(); foreach (var slot in LobbyInfo.Slots) - slotData.Append(slot.Value.Serialize()); + slotData.Add(slot.Value.Serialize()); DispatchOrders(null, 0, - new ServerOrder("SyncLobbySlots", slotData.ToString()).Serialize()); + new ServerOrder("SyncLobbySlots", slotData.WriteToString()).Serialize()); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -616,8 +616,11 @@ namespace OpenRA.Server if (State != ServerState.WaitingPlayers) return; + var sessionData = new List(); + sessionData.Add(LobbyInfo.GlobalSettings.Serialize()); + DispatchOrders(null, 0, - new ServerOrder("SyncLobbyGlobalSettings", LobbyInfo.GlobalSettings.Serialize()).Serialize()); + new ServerOrder("SyncLobbyGlobalSettings", sessionData.WriteToString()).Serialize()); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -626,12 +629,12 @@ namespace OpenRA.Server public void SyncClientPing() { // TODO: split this further into per client ping orders - var clientPings = new System.Text.StringBuilder(); + var clientPings = new List(); foreach (var ping in LobbyInfo.ClientPings) - clientPings.Append(ping.Serialize()); + clientPings.Add(ping.Serialize()); DispatchOrders(null, 0, - new ServerOrder("SyncClientPings", clientPings.ToString()).Serialize()); + new ServerOrder("SyncClientPings", clientPings.WriteToString()).Serialize()); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -669,7 +672,7 @@ namespace OpenRA.Server foreach (var t in serverTraits.WithInterface()) t.GameStarted(this); - + // Check TimeOut if (Settings.TimeOut > 10000) {