Exemplo n.º 1
0
        public static string LatencyDescription(Session.ClientPing ping)
        {
            if (ping == null)
            {
                return("Unknown");
            }

            if (ping.Latency < 0)
            {
                return("Unknown");
            }
            if (ping.Latency < 300)
            {
                return("Good");
            }
            if (ping.Latency < 600)
            {
                return("Moderate");
            }
            return("Poor");
        }
Exemplo n.º 2
0
        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 (ping.Latency < 0)
            {
                return(Color.Gray);
            }
            if (ping.Latency < 300)
            {
                return(Color.LimeGreen);
            }
            if (ping.Latency < 600)
            {
                return(Color.Orange);
            }
            return(Color.Red);
        }
Exemplo n.º 3
0
        void ValidateClient(Connection newConn, string data)
        {
            try
            {
                if (State == ServerState.GameStarted)
                {
                    Log.Write("server", "Rejected connection from {0}; game is already started.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "The game has already started");
                    DropClient(newConn);
                    return;
                }

                var handshake = HandshakeResponse.Deserialize(data);

                if (!string.IsNullOrEmpty(Settings.Password) && handshake.Password != Settings.Password)
                {
                    var message = string.IsNullOrEmpty(handshake.Password) ? "Server requires a password" : "Incorrect password";
                    SendOrderTo(newConn, "AuthenticationError", message);
                    DropClient(newConn);
                    return;
                }

                var client = new Session.Client()
                {
                    Name           = handshake.Client.Name,
                    IpAddress      = ((IPEndPoint)newConn.Socket.RemoteEndPoint).Address.ToString(),
                    Index          = newConn.PlayerIndex,
                    Slot           = LobbyInfo.FirstEmptySlot(),
                    PreferredColor = handshake.Client.Color,
                    Color          = handshake.Client.Color,
                    Race           = "Random",
                    SpawnPoint     = 0,
                    Team           = 0,
                    State          = Session.ClientState.Invalid,
                    IsAdmin        = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin)
                };

                if (client.IsObserver && !LobbyInfo.GlobalSettings.AllowSpectators)
                {
                    SendOrderTo(newConn, "ServerError", "The game is full");
                    DropClient(newConn);
                    return;
                }

                if (client.Slot != null)
                {
                    SyncClientToPlayerReference(client, MapPlayers.Players[client.Slot]);
                }
                else
                {
                    client.Color = HSLColor.FromRGB(255, 255, 255);
                }

                if (ModData.Manifest.Mod.Id != handshake.Mod)
                {
                    Log.Write("server", "Rejected connection from {0}; mods do not match.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible mod");
                    DropClient(newConn);
                    return;
                }

                if (ModData.Manifest.Mod.Version != handshake.Version && !LobbyInfo.GlobalSettings.AllowVersionMismatch)
                {
                    Log.Write("server", "Rejected connection from {0}; Not running the same version.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible version");
                    DropClient(newConn);
                    return;
                }

                // Check if IP is banned
                var bans = Settings.Ban.Union(TempBans);
                if (bans.Contains(client.IpAddress))
                {
                    Log.Write("server", "Rejected connection from {0}; Banned.", newConn.Socket.RemoteEndPoint);
                    SendOrderTo(newConn, "ServerError", "You have been {0} from the server".F(Settings.Ban.Contains(client.IpAddress) ? "banned" : "temporarily banned"));
                    DropClient(newConn);
                    return;
                }

                // Promote connection to a valid client
                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);

                foreach (var t in serverTraits.WithInterface <IClientJoined>())
                {
                    t.ClientJoined(this, newConn);
                }

                SyncLobbyInfo();
                SendMessage("{0} has joined the game.".F(client.Name));

                // Send initial ping
                SendOrderTo(newConn, "Ping", Game.RunTime.ToString());

                if (Settings.Dedicated)
                {
                    var motdFile = Platform.ResolvePath("^", "motd.txt");
                    if (!File.Exists(motdFile))
                    {
                        System.IO.File.WriteAllText(motdFile, "Welcome, have fun and good luck!");
                    }
                    var motd = System.IO.File.ReadAllText(motdFile);
                    if (!string.IsNullOrEmpty(motd))
                    {
                        SendOrderTo(newConn, "Message", motd);
                    }
                }

                if (handshake.Mod == "{DEV_VERSION}")
                {
                    SendMessage("{0} is running an unversioned development build, ".F(client.Name) +
                                "and may desynchronize the game state if they have incompatible rules.");
                }

                SetOrderLag();
            }
            catch (Exception) { DropClient(newConn); }
        }
Exemplo n.º 4
0
        void ValidateClient(Connection newConn, string data)
        {
            try
            {
                if (State == ServerState.GameStarted)
                {
                    Log.Write("server", "Rejected connection from {0}; game is already started.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "The game has already started");
                    DropClient(newConn);
                    return;
                }

                var handshake = HandshakeResponse.Deserialize(data);

                if (!string.IsNullOrEmpty(Settings.Password) && handshake.Password != Settings.Password)
                {
                    var message = string.IsNullOrEmpty(handshake.Password) ? "Server requires a password" : "Incorrect password";
                    SendOrderTo(newConn, "AuthenticationError", message);
                    DropClient(newConn);
                    return;
                }

                var ipAddress = ((IPEndPoint)newConn.Socket.RemoteEndPoint).Address;
                var client    = new Session.Client
                {
                    Name                = OpenRA.Settings.SanitizedPlayerName(handshake.Client.Name),
                    IPAddress           = ipAddress.ToString(),
                    AnonymizedIPAddress = Type != ServerType.Local && Settings.ShareAnonymizedIPs ? Session.AnonymizeIP(ipAddress) : null,
                    Location            = GeoIP.LookupCountry(ipAddress),
                    Index               = newConn.PlayerIndex,
                    PreferredColor      = handshake.Client.PreferredColor,
                    Color               = handshake.Client.Color,
                    Faction             = "Random",
                    SpawnPoint          = 0,
                    Team                = 0,
                    State               = Session.ClientState.Invalid,
                };

                if (ModData.Manifest.Id != handshake.Mod)
                {
                    Log.Write("server", "Rejected connection from {0}; mods do not match.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible mod");
                    DropClient(newConn);
                    return;
                }

                if (ModData.Manifest.Metadata.Version != handshake.Version)
                {
                    Log.Write("server", "Rejected connection from {0}; Not running the same version.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible version");
                    DropClient(newConn);
                    return;
                }

                if (handshake.OrdersProtocol != ProtocolVersion.Orders)
                {
                    Log.Write("server", "Rejected connection from {0}; incompatible Orders protocol version {1}.",
                              newConn.Socket.RemoteEndPoint, handshake.OrdersProtocol);

                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible protocol");
                    DropClient(newConn);
                    return;
                }

                // Check if IP is banned
                var bans = Settings.Ban.Union(TempBans);
                if (bans.Contains(client.IPAddress))
                {
                    Log.Write("server", "Rejected connection from {0}; Banned.", newConn.Socket.RemoteEndPoint);
                    SendOrderTo(newConn, "ServerError", "You have been {0} from the server".F(Settings.Ban.Contains(client.IPAddress) ? "banned" : "temporarily banned"));
                    DropClient(newConn);
                    return;
                }

                Action completeConnection = () =>
                {
                    lock (LobbyInfo)
                    {
                        client.Slot    = LobbyInfo.FirstEmptySlot();
                        client.IsAdmin = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin);

                        if (client.IsObserver && !LobbyInfo.GlobalSettings.AllowSpectators)
                        {
                            SendOrderTo(newConn, "ServerError", "The game is full");
                            DropClient(newConn);
                            return;
                        }

                        if (client.Slot != null)
                        {
                            SyncClientToPlayerReference(client, Map.Players.Players[client.Slot]);
                        }
                        else
                        {
                            client.Color = Color.White;
                        }

                        // Promote connection to a valid client
                        PreConns.Remove(newConn);
                        Conns.Add(newConn);
                        LobbyInfo.Clients.Add(client);
                        newConn.Validated = true;

                        var clientPing = new Session.ClientPing {
                            Index = client.Index
                        };
                        LobbyInfo.ClientPings.Add(clientPing);

                        Log.Write("server", "Client {0}: Accepted connection from {1}.",
                                  newConn.PlayerIndex, newConn.Socket.RemoteEndPoint);

                        if (client.Fingerprint != null)
                        {
                            Log.Write("server", "Client {0}: Player fingerprint is {1}.",
                                      newConn.PlayerIndex, client.Fingerprint);
                        }

                        foreach (var t in serverTraits.WithInterface <IClientJoined>())
                        {
                            t.ClientJoined(this, newConn);
                        }

                        SyncLobbyInfo();

                        Log.Write("server", "{0} ({1}) has joined the game.",
                                  client.Name, newConn.Socket.RemoteEndPoint);

                        // Report to all other players
                        SendMessage("{0} has joined the game.".F(client.Name), newConn);

                        // Send initial ping
                        SendOrderTo(newConn, "Ping", Game.RunTime.ToString(CultureInfo.InvariantCulture));

                        if (Type == ServerType.Dedicated)
                        {
                            var motdFile = Platform.ResolvePath(Platform.SupportDirPrefix, "motd.txt");
                            if (!File.Exists(motdFile))
                            {
                                File.WriteAllText(motdFile, "Welcome, have fun and good luck!");
                            }

                            var motd = File.ReadAllText(motdFile);
                            if (!string.IsNullOrEmpty(motd))
                            {
                                SendOrderTo(newConn, "Message", motd);
                            }
                        }

                        if (Map.DefinesUnsafeCustomRules)
                        {
                            SendOrderTo(newConn, "Message", "This map contains custom rules. Game experience may change.");
                        }

                        if (!LobbyInfo.GlobalSettings.EnableSingleplayer)
                        {
                            SendOrderTo(newConn, "Message", TwoHumansRequiredText);
                        }
                        else if (Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots))
                        {
                            SendOrderTo(newConn, "Message", "Bots have been disabled on this map.");
                        }
                    }
                };

                if (Type == ServerType.Local)
                {
                    // Local servers can only be joined by the local client, so we can trust their identity without validation
                    client.Fingerprint = handshake.Fingerprint;
                    completeConnection();
                }
                else if (!string.IsNullOrEmpty(handshake.Fingerprint) && !string.IsNullOrEmpty(handshake.AuthSignature))
                {
                    waitingForAuthenticationCallback++;

                    Action <DownloadDataCompletedEventArgs> onQueryComplete = i =>
                    {
                        PlayerProfile profile = null;

                        if (i.Error == null)
                        {
                            try
                            {
                                var yaml = MiniYaml.FromString(Encoding.UTF8.GetString(i.Result)).First();
                                if (yaml.Key == "Player")
                                {
                                    profile = FieldLoader.Load <PlayerProfile>(yaml.Value);

                                    var publicKey  = Encoding.ASCII.GetString(Convert.FromBase64String(profile.PublicKey));
                                    var parameters = CryptoUtil.DecodePEMPublicKey(publicKey);
                                    if (!profile.KeyRevoked && CryptoUtil.VerifySignature(parameters, newConn.AuthToken, handshake.AuthSignature))
                                    {
                                        client.Fingerprint = handshake.Fingerprint;
                                        Log.Write("server", "{0} authenticated as {1} (UID {2})", newConn.Socket.RemoteEndPoint,
                                                  profile.ProfileName, profile.ProfileID);
                                    }
                                    else if (profile.KeyRevoked)
                                    {
                                        profile = null;
                                        Log.Write("server", "{0} failed to authenticate as {1} (key revoked)", newConn.Socket.RemoteEndPoint, handshake.Fingerprint);
                                    }
                                    else
                                    {
                                        profile = null;
                                        Log.Write("server", "{0} failed to authenticate as {1} (signature verification failed)",
                                                  newConn.Socket.RemoteEndPoint, handshake.Fingerprint);
                                    }
                                }
                                else
                                {
                                    Log.Write("server", "{0} failed to authenticate as {1} (invalid server response: `{2}` is not `Player`)",
                                              newConn.Socket.RemoteEndPoint, handshake.Fingerprint, yaml.Key);
                                }
                            }
                            catch (Exception ex)
                            {
                                Log.Write("server", "{0} failed to authenticate as {1} (exception occurred)",
                                          newConn.Socket.RemoteEndPoint, handshake.Fingerprint);
                                Log.Write("server", ex.ToString());
                            }
                        }
                        else
                        {
                            Log.Write("server", "{0} failed to authenticate as {1} (server error: `{2}`)",
                                      newConn.Socket.RemoteEndPoint, handshake.Fingerprint, i.Error);
                        }

                        delayedActions.Add(() =>
                        {
                            var notAuthenticated = Type == ServerType.Dedicated && profile == null && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Any());
                            var blacklisted      = Type == ServerType.Dedicated && profile != null && Settings.ProfileIDBlacklist.Contains(profile.ProfileID);
                            var notWhitelisted   = Type == ServerType.Dedicated && Settings.ProfileIDWhitelist.Any() &&
                                                   (profile == null || !Settings.ProfileIDWhitelist.Contains(profile.ProfileID));

                            if (notAuthenticated)
                            {
                                Log.Write("server", "Rejected connection from {0}; Not authenticated.", newConn.Socket.RemoteEndPoint);
                                SendOrderTo(newConn, "ServerError", "Server requires players to have an OpenRA forum account");
                                DropClient(newConn);
                            }
                            else if (blacklisted || notWhitelisted)
                            {
                                if (blacklisted)
                                {
                                    Log.Write("server", "Rejected connection from {0}; In server blacklist.", newConn.Socket.RemoteEndPoint);
                                }
                                else
                                {
                                    Log.Write("server", "Rejected connection from {0}; Not in server whitelist.", newConn.Socket.RemoteEndPoint);
                                }

                                SendOrderTo(newConn, "ServerError", "You do not have permission to join this server");
                                DropClient(newConn);
                            }
                            else
                            {
                                completeConnection();
                            }

                            waitingForAuthenticationCallback--;
                        }, 0);
                    };

                    new Download(playerDatabase.Profile + handshake.Fingerprint, _ => { }, onQueryComplete);
                }
                else
                {
                    if (Type == ServerType.Dedicated && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Any()))
                    {
                        Log.Write("server", "Rejected connection from {0}; Not authenticated.", newConn.Socket.RemoteEndPoint);
                        SendOrderTo(newConn, "ServerError", "Server requires players to have an OpenRA forum account");
                        DropClient(newConn);
                    }
                    else
                    {
                        completeConnection();
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Write("server", "Dropping connection {0} because an error occurred:", newConn.Socket.RemoteEndPoint);
                Log.Write("server", ex.ToString());
                DropClient(newConn);
            }
        }
Exemplo n.º 5
0
        void ValidateClient(Connection newConn, string data)
        {
            try
            {
                if (State == ServerState.GameStarted)
                {
                    Log.Write("server", "Rejected connection from {0}; game is already started.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "The game has already started");
                    DropClient(newConn);
                    return;
                }

                var handshake = HandshakeResponse.Deserialize(data);

                if (!string.IsNullOrEmpty(Settings.Password) && handshake.Password != Settings.Password)
                {
                    var message = string.IsNullOrEmpty(handshake.Password) ? "Server requires a password" : "Incorrect password";
                    SendOrderTo(newConn, "AuthenticationError", message);
                    DropClient(newConn);
                    return;
                }

                var client = new Session.Client
                {
                    Name           = OpenRA.Settings.SanitizedPlayerName(handshake.Client.Name),
                    IpAddress      = ((IPEndPoint)newConn.Socket.RemoteEndPoint).Address.ToString(),
                    Index          = newConn.PlayerIndex,
                    Slot           = LobbyInfo.FirstEmptySlot(),
                    PreferredColor = handshake.Client.PreferredColor,
                    Color          = handshake.Client.Color,
                    Faction        = "Random",
                    SpawnPoint     = 0,
                    Team           = 0,
                    State          = Session.ClientState.Invalid,
                    IsAdmin        = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin)
                };

                if (client.IsObserver && !LobbyInfo.GlobalSettings.AllowSpectators)
                {
                    SendOrderTo(newConn, "ServerError", "The game is full");
                    DropClient(newConn);
                    return;
                }

                if (client.Slot != null)
                {
                    SyncClientToPlayerReference(client, Map.Players.Players[client.Slot]);
                }
                else
                {
                    client.Color = HSLColor.FromRGB(255, 255, 255);
                }

                if (ModData.Manifest.Id != handshake.Mod)
                {
                    Log.Write("server", "Rejected connection from {0}; mods do not match.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible mod");
                    DropClient(newConn);
                    return;
                }

                if (ModData.Manifest.Metadata.Version != handshake.Version && !LobbyInfo.GlobalSettings.AllowVersionMismatch)
                {
                    Log.Write("server", "Rejected connection from {0}; Not running the same version.",
                              newConn.Socket.RemoteEndPoint);

                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible version");
                    DropClient(newConn);
                    return;
                }

                // Check if IP is banned
                var bans = Settings.Ban.Union(TempBans);
                if (bans.Contains(client.IpAddress))
                {
                    Log.Write("server", "Rejected connection from {0}; Banned.", newConn.Socket.RemoteEndPoint);
                    SendOrderTo(newConn, "ServerError", "You have been {0} from the server".F(Settings.Ban.Contains(client.IpAddress) ? "banned" : "temporarily banned"));
                    DropClient(newConn);
                    return;
                }

                // Promote connection to a valid client
                PreConns.Remove(newConn);
                Conns.Add(newConn);
                LobbyInfo.Clients.Add(client);
                newConn.Validated = true;

                var clientPing = new Session.ClientPing {
                    Index = client.Index
                };
                LobbyInfo.ClientPings.Add(clientPing);

                Log.Write("server", "Client {0}: Accepted connection from {1}.",
                          newConn.PlayerIndex, newConn.Socket.RemoteEndPoint);

                foreach (var t in serverTraits.WithInterface <IClientJoined>())
                {
                    t.ClientJoined(this, newConn);
                }

                SyncLobbyInfo();

                Log.Write("server", "{0} ({1}) has joined the game.",
                          client.Name, newConn.Socket.RemoteEndPoint);

                if (LobbyInfo.NonBotClients.Count() > 1)
                {
                    SendMessage("{0} has joined the game.".F(client.Name));
                }

                // Send initial ping
                SendOrderTo(newConn, "Ping", Game.RunTime.ToString(CultureInfo.InvariantCulture));

                if (Dedicated)
                {
                    var motdFile = Platform.ResolvePath(Platform.SupportDirPrefix, "motd.txt");
                    if (!File.Exists(motdFile))
                    {
                        File.WriteAllText(motdFile, "Welcome, have fun and good luck!");
                    }

                    var motd = File.ReadAllText(motdFile);
                    if (!string.IsNullOrEmpty(motd))
                    {
                        SendOrderTo(newConn, "Message", motd);
                    }
                }

                if (Map.DefinesUnsafeCustomRules)
                {
                    SendOrderTo(newConn, "Message", "This map contains custom rules. Game experience may change.");
                }

                if (!LobbyInfo.GlobalSettings.EnableSingleplayer)
                {
                    SendOrderTo(newConn, "Message", TwoHumansRequiredText);
                }
                else if (Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots))
                {
                    SendOrderTo(newConn, "Message", "Bots have been disabled on this map.");
                }

                if (handshake.Mod == "{DEV_VERSION}")
                {
                    SendMessage("{0} is running an unversioned development build, ".F(client.Name) +
                                "and may desynchronize the game state if they have incompatible rules.");
                }
            }
            catch (Exception ex)
            {
                Log.Write("server", "Dropping connection {0} because an error occurred:", newConn.Socket.RemoteEndPoint);
                Log.Write("server", ex.ToString());
                DropClient(newConn);
            }
        }
Exemplo n.º 6
0
        void ValidateClient(Connection newConn, string data)
        {
            try
            {
                if (State == ServerState.GameStarted)
                {
                    SendOrderTo(newConn, "ServerError", "The game has already started");
                    DropClient(newConn);
                    return;
                }

                var handshake = HandshakeResponse.Deserialize(data);

                if (!string.IsNullOrEmpty(Settings.Password) && handshake.Password != Settings.Password)
                {
                    var message = string.IsNullOrEmpty(handshake.Password) ? "Server requires a password" : "Incorrect password";
                    SendOrderTo(newConn, "AuthenticationError", message);
                    DropClient(newConn);
                    return;
                }

                var client = new Session.Client
                {
                    Name           = EW.Settings.SanitizedPlayerName(handshake.Client.Name),
                    IpAddress      = ((IPEndPoint)newConn.Socket.RemoteEndPoint).Address.ToString(),
                    Index          = newConn.PlayerIndex,
                    Slot           = LobbyInfo.FirstEmptySlot(),
                    PreferredColor = handshake.Client.PreferredColor,
                    Color          = handshake.Client.Color,
                    Faction        = "Random",
                    SpawnPoint     = 0,
                    Team           = 0,
                    State          = Session.ClientState.Invalid,
                    IsAdmin        = !LobbyInfo.Clients.Any(cl => cl.IsAdmin)
                };

                if (client.IsObserver && !LobbyInfo.GlobalSettings.AllowSpectators)
                {
                    SendOrderTo(newConn, "ServerError", "The game is full");
                    DropClient(newConn);
                    return;
                }

                if (client.Slot != null)
                {
                    SyncClientToPlayerReference(client, Map.Players.Players[client.Slot]);
                }
                else
                {
                    client.Color = HSLColor.FromRGB(255, 255, 255);
                }

                if (ModData.Manifest.Id != handshake.Mod)
                {
                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible mod");
                    DropClient(newConn);
                    return;
                }

                if (ModData.Manifest.Metadata.Version != handshake.Version && !LobbyInfo.GlobalSettings.AllowVersionMismatch)
                {
                    SendOrderTo(newConn, "ServerError", "Server is running an incompatible version");
                    DropClient(newConn);
                    return;
                }


                //Check if IP is banned
                var bans = Settings.Ban.Union(TempBans);
                if (bans.Contains(client.IpAddress))
                {
                    SendOrderTo(newConn, "ServerError", "You have been {0} from the server".F(Settings.Ban.Contains(client.IpAddress) ? "banned" : "temporarily banned"));
                    DropClient(newConn);
                    return;
                }

                //Promote connection to a valid client
                PreConns.Remove(newConn);
                Conns.Add(newConn);
                LobbyInfo.Clients.Add(client);
                newConn.Validated = true;


                var clientPing = new Session.ClientPing {
                    Index = client.Index
                };
                LobbyInfo.ClientPings.Add(clientPing);

                foreach (var t in serverTraits.WithInterface <IClientJoined>())
                {
                    t.ClientJoined(this, newConn);
                }

                SyncLobbyInfo();

                if (LobbyInfo.NonBotClients.Count() > 1)
                {
                    SendMessage("{0} has joined the game".F(client.Name));
                }

                SendOrderTo(newConn, "Ping", WarGame.RunTime.ToString(CultureInfo.InvariantCulture));

                if (Dedicated)
                {
                }

                if (Map.DefinesUnsafeCustomRules)
                {
                    SendOrderTo(newConn, "Message", "This map contains custom rules.Game experience may change.");
                }

                if (!LobbyInfo.GlobalSettings.EnableSinglePlayer)
                {
                    SendOrderTo(newConn, "Message", TwoHumansRequiredText);
                }
                else if (Map.Players.Players.Where(p => p.Value.Playable).All(p => !p.Value.AllowBots))
                {
                    SendOrderTo(newConn, "Message", "Bots have been disabled on this map");
                }
            }
            catch (Exception ex)
            {
                DropClient(newConn);
            }
        }