From 7f835cf4380917c06cba8a46335647c2cf3b3f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Sun, 7 Apr 2013 21:54:35 +0200 Subject: [PATCH 1/4] display ping when player joins lobby, closes #2124 --- OpenRA.Game/Server/Connection.cs | 36 ++++++++++++++++++++++++++++++-- OpenRA.Game/Server/Server.cs | 14 +++++++------ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/OpenRA.Game/Server/Connection.cs b/OpenRA.Game/Server/Connection.cs index b9d4b07b80..8ee7061b0e 100644 --- a/OpenRA.Game/Server/Connection.cs +++ b/OpenRA.Game/Server/Connection.cs @@ -12,6 +12,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; +using System.Net.NetworkInformation; +using System.Threading; namespace OpenRA.Server { @@ -22,8 +24,9 @@ namespace OpenRA.Server public ReceiveState State = ReceiveState.Header; public int ExpectLength = 8; public int Frame = 0; - public int MostRecentFrame = 0; + public string RemoteAddress; + public int Latency = -1; /* client data */ public int PlayerIndex; @@ -97,7 +100,36 @@ namespace OpenRA.Server } break; } } - }} + } + + 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(RemoteAddress, 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; + } + } + + } public enum ReceiveState { Header, Data }; } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 5ade506ef9..e9de70762a 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -251,6 +251,9 @@ namespace OpenRA.Server DispatchOrdersToClient(newConn, 0, 0, new ServerOrder("HandshakeRequest", request.Serialize()).Serialize()); } catch (Exception) { DropClient(newConn); } + + newConn.RemoteAddress = ((IPEndPoint)newConn.socket.RemoteEndPoint).Address.ToString(); + newConn.Ping(); } void ValidateClient(Connection newConn, string data) @@ -302,8 +305,7 @@ namespace OpenRA.Server // Check if IP is banned if (lobbyInfo.GlobalSettings.Ban != null) { - var remote_addr = ((IPEndPoint)newConn.socket.RemoteEndPoint).Address.ToString(); - if (lobbyInfo.GlobalSettings.Ban.Contains(remote_addr)) + if (lobbyInfo.GlobalSettings.Ban.Contains(newConn.RemoteAddress)) { Console.WriteLine("Rejected connection from "+client.Name+"("+newConn.socket.RemoteEndPoint+"); Banned."); Log.Write("server", "Rejected connection from {0}; Banned.", @@ -338,16 +340,16 @@ namespace OpenRA.Server foreach (var t in ServerTraits.WithInterface()) t.ClientJoined(this, newConn); + SendChat(newConn, "has joined the game. Ping: {0} ms".F(newConn.Latency)); SyncLobbyInfo(); - SendChat(newConn, "has joined the game."); - if ( File.Exists("{0}motd_{1}.txt".F(Platform.SupportDir, lobbyInfo.GlobalSettings.Mods[0])) ) + if (File.Exists("{0}motd_{1}.txt".F(Platform.SupportDir, lobbyInfo.GlobalSettings.Mods[0]))) { var motd = System.IO.File.ReadAllText("{0}motd_{1}.txt".F(Platform.SupportDir, lobbyInfo.GlobalSettings.Mods[0])); SendChatTo(newConn, motd); } - if ( lobbyInfo.GlobalSettings.Dedicated ) + if (lobbyInfo.GlobalSettings.Dedicated) { if (client.IsAdmin) SendChatTo(newConn, " You are admin now!"); @@ -578,7 +580,7 @@ namespace OpenRA.Server t.GameStarted(this); // Check TimeOut - if ( Settings.TimeOut > 10000 ) + if (Settings.TimeOut > 10000) { gameTimeout = new XTimer(Settings.TimeOut); gameTimeout.Elapsed += (_,e) => From f8e44b792f80a40c0cfbb98e2177b612516a780f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Sun, 7 Apr 2013 22:22:42 +0200 Subject: [PATCH 2/4] adjust order latency dynamically by client ping, closes #2990 --- OpenRA.Game/Network/Session.cs | 2 +- OpenRA.Game/Network/UnitOrders.cs | 3 +-- OpenRA.Game/Server/Server.cs | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index ffa6be46dc..ae65c4c277 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -82,7 +82,7 @@ namespace OpenRA.Network public string Map; public string[] Ban; public string[] Mods = { "ra" }; // mod names - public int OrderLatency = 3; + public int OrderLatency = 3; // x 40 = ms public int RandomSeed = 0; public bool FragileAlliances = false; // Allow diplomatic stance changes after game start. public bool AllowCheats = false; diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 24583688aa..8fda65b014 100755 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -164,8 +164,7 @@ namespace OpenRA.Network && !orderManager.GameStarted) { orderManager.FramesAhead = orderManager.LobbyInfo.GlobalSettings.OrderLatency; - Game.Debug( - "Order lag is now {0} frames.".F(orderManager.LobbyInfo.GlobalSettings.OrderLatency)); + Game.Debug("Order lag is now {0} frames.".F(orderManager.LobbyInfo.GlobalSettings.OrderLatency)); } Game.SyncLobbyInfo(); break; diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index e9de70762a..1b2a0633de 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -64,6 +64,8 @@ namespace OpenRA.Server public Map Map; XTimer gameTimeout; + int highestLatency; + protected volatile ServerState pState = new ServerState(); public ServerState State { @@ -341,6 +343,14 @@ namespace OpenRA.Server t.ClientJoined(this, newConn); SendChat(newConn, "has joined the game. Ping: {0} ms".F(newConn.Latency)); + + if (newConn.Latency > highestLatency) + highestLatency = newConn.Latency; + + lobbyInfo.GlobalSettings.OrderLatency = highestLatency / 40; // 1 frame is 40 ms + if (lobbyInfo.GlobalSettings.OrderLatency < 1) // should never be 0 + lobbyInfo.GlobalSettings.OrderLatency = 1; + SyncLobbyInfo(); if (File.Exists("{0}motd_{1}.txt".F(Platform.SupportDir, lobbyInfo.GlobalSettings.Mods[0]))) @@ -360,6 +370,8 @@ namespace OpenRA.Server if (mods.Any(m => m.Contains("{DEV_VERSION}"))) SendChat(newConn, "is running a non-versioned development build, "+ "and may cause desync if it contains any incompatible changes."); + + Game.Debug("Order lag has been adjusted to {0} frames.".F(lobbyInfo.GlobalSettings.OrderLatency)); } catch (Exception) { DropClient(newConn); } } @@ -532,7 +544,9 @@ namespace OpenRA.Server SendChat(toDrop, "Admin left! {0} is a new admin now!".F(lastClient.Name)); } } - + + //TODO: if (highestLatency == toDrop.Latency) then find the new highestLatency + DispatchOrders( toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf } ); if (conns.Count != 0 || lobbyInfo.GlobalSettings.Dedicated) From d2a6781e7e933321603dca2fb6182d54f56243cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Thu, 18 Apr 2013 17:09:51 +0200 Subject: [PATCH 3/4] set dynamic order lag when client with highest latency leaves hide Ping again from the players, write it to server.log instead just make everything silently work to not discriminate clients --- OpenRA.Game/Server/Server.cs | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 1b2a0633de..f04deb1b39 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -336,21 +336,15 @@ namespace OpenRA.Server OpenRA.Network.Session.Client clientAdmin = lobbyInfo.Clients.Where(c1 => c1.IsAdmin).Single(); - Log.Write("server", "Client {0}: Accepted connection from {1}", - newConn.PlayerIndex, newConn.socket.RemoteEndPoint); + Log.Write("server", "Client {0}: Accepted connection from {1} with {2} ms ping latency.", + newConn.PlayerIndex, newConn.socket.RemoteEndPoint, newConn.Latency); foreach (var t in ServerTraits.WithInterface()) t.ClientJoined(this, newConn); - SendChat(newConn, "has joined the game. Ping: {0} ms".F(newConn.Latency)); - - if (newConn.Latency > highestLatency) - highestLatency = newConn.Latency; - - lobbyInfo.GlobalSettings.OrderLatency = highestLatency / 40; // 1 frame is 40 ms - if (lobbyInfo.GlobalSettings.OrderLatency < 1) // should never be 0 - lobbyInfo.GlobalSettings.OrderLatency = 1; + SendChat(newConn, "has joined the game."); + SetDynamicOrderLag(); SyncLobbyInfo(); if (File.Exists("{0}motd_{1}.txt".F(Platform.SupportDir, lobbyInfo.GlobalSettings.Mods[0]))) @@ -370,8 +364,6 @@ namespace OpenRA.Server if (mods.Any(m => m.Contains("{DEV_VERSION}"))) SendChat(newConn, "is running a non-versioned development build, "+ "and may cause desync if it contains any incompatible changes."); - - Game.Debug("Order lag has been adjusted to {0} frames.".F(lobbyInfo.GlobalSettings.OrderLatency)); } catch (Exception) { DropClient(newConn); } } @@ -545,7 +537,8 @@ namespace OpenRA.Server } } - //TODO: if (highestLatency == toDrop.Latency) then find the new highestLatency + if (highestLatency == toDrop.Latency) + SetDynamicOrderLag(); DispatchOrders( toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf } ); @@ -562,6 +555,23 @@ namespace OpenRA.Server catch { } } + public void SetDynamicOrderLag() + { + foreach (var conn in conns) + { + if (conn.Latency > highestLatency) + highestLatency = conn.Latency; + } + + Log.Write("server", "Measured {0} ms as the highest connection round trip time.".F(highestLatency)); + + lobbyInfo.GlobalSettings.OrderLatency = highestLatency / 40; // 1 frame is 40 ms + if (lobbyInfo.GlobalSettings.OrderLatency < 1) // should never be 0 + lobbyInfo.GlobalSettings.OrderLatency = 1; + + Log.Write("server", "Order lag has been adjusted to {0} frames.".F(lobbyInfo.GlobalSettings.OrderLatency)); + } + public void SyncLobbyInfo() { if (State != ServerState.GameStarted) /* don't do this while the game is running, it breaks things. */ From ef20009310a6280270af6604120c7843b679463a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Thu, 18 Apr 2013 23:42:32 +0200 Subject: [PATCH 4/4] 1 net tick is 120 ms and good order latency should be 2-3 color code names in lobby accordingly --- OpenRA.Game/Network/Session.cs | 3 ++- OpenRA.Game/Server/Server.cs | 4 +++- OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs | 3 +++ OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs | 11 +++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index ae65c4c277..a57135b012 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -61,6 +61,7 @@ namespace OpenRA.Network public bool IsAdmin; public bool IsReady { get { return State == ClientState.Ready; } } public bool IsObserver { get { return Slot == null; } } + public int Ping = -1; } public class Slot @@ -82,7 +83,7 @@ namespace OpenRA.Network public string Map; public string[] Ban; public string[] Mods = { "ra" }; // mod names - public int OrderLatency = 3; // x 40 = ms + public int OrderLatency = 3; // net tick frames (x 120 = ms) public int RandomSeed = 0; public bool FragileAlliances = false; // Allow diplomatic stance changes after game start. public bool AllowCheats = false; diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index f04deb1b39..5815032491 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -322,6 +322,8 @@ namespace OpenRA.Server preConns.Remove(newConn); conns.Add(newConn); + client.Ping = newConn.Latency; + // Enforce correct PlayerIndex and Slot client.Index = newConn.PlayerIndex; client.Slot = lobbyInfo.FirstEmptySlot(); @@ -565,7 +567,7 @@ namespace OpenRA.Server Log.Write("server", "Measured {0} ms as the highest connection round trip time.".F(highestLatency)); - lobbyInfo.GlobalSettings.OrderLatency = highestLatency / 40; // 1 frame is 40 ms + lobbyInfo.GlobalSettings.OrderLatency = highestLatency / 120; if (lobbyInfo.GlobalSettings.OrderLatency < 1) // should never be 0 lobbyInfo.GlobalSettings.OrderLatency = 1; diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs index 9b142f6986..d65fd4594a 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs @@ -477,6 +477,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic template.Get("NAME").GetText = () => client.Name; if (client.IsAdmin) template.Get("NAME").Font = "Bold"; + if (client.Ping > -1) + template.Get("NAME").GetColor = () => LobbyUtils.GetPingColor(client.Ping); + var color = template.Get("COLOR"); color.GetColor = () => client.ColorRamp.GetColor(0); diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs index fa04fad141..452b522e6e 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs @@ -26,6 +26,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (c.IsAdmin) name.Font = "Bold"; name.Text = c.Name; + if (c.Ping > -1) + name.TextColor = GetPingColor(c.Ping); name.OnEnterKey = () => { name.Text = name.Text.Trim(); @@ -189,5 +191,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic Game.Renderer.Fonts["Bold"].DrawTextWithContrast(client.Name, position + new int2(5, 5), Color.White, Color.Black, 1); } } + + public static Color GetPingColor(int ping) + { + if (ping > 720) // OrderLag > 6 + return Color.Red; + if (ping > 360) // OrderLag > 3 + return Color.Orange; + return Color.LimeGreen; + } } }