示例#1
0
        /// <summary>
        /// Starts the Login Server listeners, and begins accepting new connections
        /// </summary>
        public static void StartServers()
        {
            // Make sure we arent already running!
            if (isRunning)
            {
                return;
            }
            try
            {
                // Create debug log
                LogWriter DebugLog = new LogWriter(Path.Combine(Program.RootPath, "Logs", "MasterServer_Debug.log"), true, 1);

                // Start the DB Connection
                Console.Write("Connecting to Mysql... ");
                using (MasterDatabase Database = new MasterDatabase())
                {
                    Console.Write("Success!" + Environment.NewLine);
                }

                // Create our end point to bind to
                int       port    = Config.GetType <int>("Settings", "MasterServerUdpPort");
                IPAddress address = IPAddress.Parse(Config.GetValue("Settings", "ServerBindIp"));

                // Start Master Server, we write to console in the constructor because this
                // is actually 2 servers in 1
                Console.Write("<MSTR> Binding to UDP port {0}... ", port);
                MstrServer = new MasterServer(new IPEndPoint(address, port), DebugLog);
                Console.Write("Success!" + Environment.NewLine);

                // Start the CDKey server
                port = Config.GetType <int>("Settings", "CDKeyServerUdpPort");
                Console.Write("<CDKY> Binding to UDP port {0}... ", port);
                CDKeyServer = new CDKeyServer(new IPEndPoint(address, port), DebugLog);
                Console.Write("Success!" + Environment.NewLine);
            }
            catch
            {
                Console.Write("Failed!" + Environment.NewLine);
                throw;
            }

            // Let the client know we are ready for connections
            isRunning = true;
        }
        /// <summary>
        /// When a server connects, it needs to be validated. Once that happens, this
        /// method is called, and it allows the server to bee seen in the Serverlist
        /// </summary>
        /// <param name="remote">The remote IP of the server</param>
        private void ValidateServer(IPEndPoint remote)
        {
            string     key = String.Format("{0}:{1}", remote.Address, remote.Port);
            GameServer server;

            // try to fetch the existing server
            if (!Servers.TryGetValue(key, out server))
            {
                // If the key exists, then what gives?
                if (Servers.ContainsKey(key) && !Servers.TryGetValue(key, out server))
                {
                    Program.ErrorLog.Write("NOTICE: [MasterServer.ValidateServer] Unable to fetch a connected server.");
                }
                return;
            }

            // Server is valid
            server.IsValidated   = true;
            server.LastRefreshed = DateTime.Now;
            server.LastPing      = DateTime.Now;

            // Update or add the new server
            if (Debugging)
            {
                DebugLog.Write("Adding Validated Server to Serverlist: " + key);
            }
            Servers.AddOrUpdate(key, server, (k, old) => { return(server); });

            // Update the Dababase
            try
            {
                using (MasterDatabase Driver = new MasterDatabase())
                {
                    Driver.AddOrUpdateServer(server);
                }
            }
            catch (Exception e)
            {
                Program.ErrorLog.Write("ERROR: [MasterDatabase.AddOrUpdateServer] " + e.Message);
            }
        }
示例#3
0
        /// <summary>
        /// When a server connects, it needs to be validated. Once that happens, this
        /// method is called, and it allows the server to bee seen in the Serverlist
        /// </summary>
        /// <param name="remote">The remote IP of the server</param>
        private void ValidateServer(IPEndPoint remote)
        {
            string     key = String.Format("{0}:{1}", remote.Address, remote.Port);
            GameServer server;

            // try to fetch the existing server, if its not here... we have bigger problems
            if (!Servers.TryGetValue(key, out server))
            {
                Program.ErrorLog.Write("NOTICE: [MasterServer.ValidateServer] We encountered a strange error trying to fetch a connected server.");
                return;
            }

            // Server is valid
            server.IsValidated   = true;
            server.LastRefreshed = DateTime.Now;
            server.LastPing      = DateTime.Now;

            // Update or add the new server
            if (Debugging)
            {
                DebugLog.Write("Adding Validated Server to Serverlist: " + key);
            }
            Servers.AddOrUpdate(key, server, (k, old) => { return(server); });

            // Update the Dababase
            try
            {
                using (MasterDatabase Driver = new MasterDatabase())
                {
                    Driver.AddOrUpdateServer(server);
                }
            }
            catch (Exception e)
            {
                Program.ErrorLog.Write("ERROR: [MasterDatabase.AddOrUpdateServer] " + e.Message);
            }
        }
示例#4
0
        /// <summary>
        /// Executed every 5 seconds or so... Removes all servers that haven't
        /// reported in awhile
        /// </summary>
        protected void CheckServers()
        {
            // Create a list of servers to update in the database
            List <GameServer> ServersToRemove = new List <GameServer>();

            // Remove servers that havent talked to us in awhile from the server list
            foreach (string key in Servers.Keys)
            {
                GameServer value;
                if (Servers.TryGetValue(key, out value))
                {
                    if (value.LastPing < DateTime.Now - TimeSpan.FromSeconds(ServerTTL))
                    {
                        if (Debugging)
                        {
                            DebugLog.Write("Removing Server for Expired Ping: " + key);
                        }
                        if (Servers.TryRemove(key, out value))
                        {
                            ServersToRemove.Add(value);
                        }
                        else
                        {
                            Program.ErrorLog.Write("ERROR: [MasterServer.CheckServers] Unable to remove server from server list: " + key);
                        }
                    }
                }
            }

            // If we have no servers to update, return
            if (ServersToRemove.Count == 0)
            {
                return;
            }

            // Update servers in database
            try
            {
                // Wrap this all in a database transaction, as this will speed
                // things up alot if there are alot of rows to update
                using (MasterDatabase Driver = new MasterDatabase())
                    using (DbTransaction Transaction = Driver.BeginTransaction())
                    {
                        try
                        {
                            foreach (GameServer server in ServersToRemove)
                            {
                                Driver.UpdateServerOffline(server);
                            }

                            Transaction.Commit();
                        }
                        catch
                        {
                            Transaction.Rollback();
                            throw;
                        }
                    }
            }
            catch (Exception e)
            {
                Program.ErrorLog.Write("ERROR: [MasterDatabase.UpdateServerStatus] Unable to update servers status: " + e.Message);
            }
        }
示例#5
0
        /// <summary>
        /// Executed every 60 seconds... Every 3rd ping, the BF2 server sends a full list
        /// of data that describes its current state, and this method is used to parse that
        /// data, and update the server in the Servers list
        /// </summary>
        /// <param name="remote">The servers remote address</param>
        /// <param name="data">The data we must parse, sent by the server</param>
        /// <returns>Returns whether or not the server needs to be validated, so it can be seen in the Server Browser</returns>
        private bool ParseServerDetails(IPEndPoint remote, byte[] data)
        {
            // Format key
            string key = String.Format("{0}:{1}", remote.Address, remote.Port);

            // split by 000 (info/player separator) and 002 (players/teams separator)
            // the players/teams separator is really 00, but because 00 may also be used elsewhere (an empty value for example), we hardcode it to 002
            // the 2 is the size of the teams, for BF2 this is always 2.
            string receivedData = Encoding.UTF8.GetString(data);

            string[] sections = receivedData.Split(new string[] { "\x00\x00\x00", "\x00\x00\x02" }, StringSplitOptions.None);
            if (sections.Length != 3 && !receivedData.EndsWith("\x00\x00"))
            {
                if (Debugging)
                {
                    DebugLog.Write("Invalid Server Data Received From {0} :: {1}", key, sections[0]);
                }
                return(true); // true means we don't send back a response
            }

            // We only care about the server section
            string serverVars = sections[0];

            string[] serverVarsSplit = serverVars.Split(new string[] { "\x00" }, StringSplitOptions.None);
            if (Debugging)
            {
                DebugLog.Write("Server Data Received From {0}", key);
                for (int i = 0; i < sections.Length; i++)
                {
                    DebugLog.Write("    DataString {0}: {1}", i, sections[i]);
                }
            }

            // Start a new Server Object
            GameServer server = new GameServer(remote);
            List <Dictionary <string, object> > Rows = null;

            // set the country based off ip address if its IPv4
            server.country = (remote.Address.AddressFamily == AddressFamily.InterNetwork)
                ? GeoIP.GetCountryCode(remote.Address).ToUpperInvariant()
                : "??";

            // Try and Fetch server and provider from the database
            try
            {
                using (MasterDatabase Driver = new MasterDatabase())
                {
                    string q = "SELECT s.*, p.authorized, p.plasma FROM server AS s JOIN stats_provider AS p ON s.provider_id = p.id WHERE ip=@P0 AND queryport=@P1";
                    Rows = Driver.Query(q, remote.Address, remote.Port);
                }
            }
            catch (Exception e)
            {
                Program.ErrorLog.Write("WARNING: [MasterServer.ParseServerDetails] " + e.Message);
            }

            // Set server vars
            for (int i = 0; i < serverVarsSplit.Length - 1; i += 2)
            {
                // Fetch the property
                PropertyInfo property = typeof(GameServer).GetProperty(serverVarsSplit[i]);
                if (property == null)
                {
                    continue;
                }
                else if (property.Name == "hostname")
                {
                    // strip consecutive whitespace from hostname
                    property.SetValue(server, Regex.Replace(serverVarsSplit[i + 1], @"\s+", " ").Trim(), null);
                }
                else if (property.Name == "bf2_plasma")
                {
                    // Does the server exist in the database?
                    if (Rows != null && Rows.Count > 0)
                    {
                        // For some damn reason, the driver is returning a bool instead of an integer?
                        if (Rows[0]["plasma"] is bool)
                        {
                            property.SetValue(server, Rows[0]["plasma"], null);
                        }
                        else
                        {
                            property.SetValue(server, Int32.Parse(Rows[0]["plasma"].ToString()) > 0, null);
                        }
                    }
                    else
                    {
                        property.SetValue(server, false, null);
                    }
                }
                else if (property.Name == "bf2_ranked")
                {
                    // Does the server exist in the database?
                    if (Rows != null && Rows.Count > 0)
                    {
                        // For some damn reason, the driver is returning a bool instead of an integer?
                        if (Rows[0]["authorized"] is bool)
                        {
                            property.SetValue(server, Rows[0]["authorized"], null);
                        }
                        else
                        {
                            property.SetValue(server, Int32.Parse(Rows[0]["authorized"].ToString()) > 0, null);
                        }
                    }
                    else
                    {
                        property.SetValue(server, false, null);
                    }
                }
                else if (property.Name == "bf2_pure")
                {
                    // we're always a pure server
                    property.SetValue(server, true, null);
                }
                else if (property.PropertyType == typeof(Boolean))
                {
                    // parse string to bool (values come in as 1 or 0)
                    int value;
                    if (Int32.TryParse(serverVarsSplit[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
                    {
                        property.SetValue(server, value != 0, null);
                    }
                }
                else if (property.PropertyType == typeof(Int32))
                {
                    // parse string to int
                    int value;
                    if (Int32.TryParse(serverVarsSplit[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
                    {
                        property.SetValue(server, value, null);
                    }
                }
                else if (property.PropertyType == typeof(Double))
                {
                    // parse string to double
                    double value;
                    if (Double.TryParse(serverVarsSplit[i + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out value))
                    {
                        property.SetValue(server, value, null);
                    }
                }
                else if (property.PropertyType == typeof(String))
                {
                    // parse string to string
                    property.SetValue(server, serverVarsSplit[i + 1], null);
                }
            }

            // you've got to have all these properties in order for your server to be valid
            if (!String.IsNullOrWhiteSpace(server.hostname) &&
                !String.IsNullOrWhiteSpace(server.gamevariant) &&
                !String.IsNullOrWhiteSpace(server.gamever) &&
                !String.IsNullOrWhiteSpace(server.gametype) &&
                !String.IsNullOrWhiteSpace(server.mapname) &&
                !String.IsNullOrWhiteSpace(server.gamename) &&
                server.gamename.Equals("battlefield2", StringComparison.InvariantCultureIgnoreCase) &&
                server.hostport > 1024 && server.hostport <= UInt16.MaxValue &&
                server.maxplayers > 0)
            {
                // Determine if we need to send a challenge key to the server for validation
                bool IsValidated = Servers.ContainsKey(key) && Servers[key].IsValidated;
                if (Debugging)
                {
                    DebugLog.Write("Server Data Parsed Successfully... Needs Validated: " + ((IsValidated) ? "false" : "true"));
                }

                // Add / Update Server
                server.IsValidated   = IsValidated;
                server.LastPing      = DateTime.Now;
                server.LastRefreshed = DateTime.Now;
                Servers.AddOrUpdate(key, server, (k, old) => { return(server); });

                // Tell the requester if we are good to go
                return(IsValidated);
            }

            // If we are here, the server information is invalid. Return true to ignore server
            return(true);
        }