/// <summary>
        /// Converts a received parameter array from the client string to a keyValue pair dictionary
        /// </summary>
        /// <param name="parts">The array of data from the client</param>
        /// <returns></returns>
        private static Dictionary <string, string> ConvertToKeyValue(string[] parts)
        {
            Dictionary <string, string> Dic = new NiceDictionary <string, string>();

            try
            {
                for (int i = 0; i < parts.Length; i += 2)
                {
                    if (!Dic.ContainsKey(parts[i]))
                    {
                        Dic.Add(parts[i], parts[i + 1]);
                    }
                }
            }
            catch (IndexOutOfRangeException) { }

            return(Dic);
        }
        /// <summary>
        /// Queries the database, and returns a result set
        /// </summary>
        /// <param name="Sql">The SQL Statement to run on the database</param>
        /// <param name="Params">A list of sql params to add to the command</param>
        /// <returns></returns>
        public List <Dictionary <string, object> > Query(string Sql, List <DbParameter> Params)
        {
            // Create our Rows result
            List <Dictionary <string, object> > Rows = new List <Dictionary <string, object> >();

            // Increase Query Count
            NumQueries++;

            // Create the SQL Command
            using (DbCommand Command = this.CreateCommand(Sql))
            {
                // Add params
                foreach (DbParameter Param in Params)
                {
                    Command.Parameters.Add(Param);
                }

                // Execute the query
                using (DbDataReader Reader = Command.ExecuteReader())
                {
                    // If we have rows, add them to the list
                    if (Reader.HasRows)
                    {
                        // Add each row to the rows list
                        while (Reader.Read())
                        {
                            NiceDictionary <string, object> Row = new NiceDictionary <string, object>(Reader.FieldCount);
                            for (int i = 0; i < Reader.FieldCount; ++i)
                            {
                                Row.Add(Reader.GetName(i), Reader.GetValue(i));
                            }

                            Rows.Add(Row);
                        }
                    }

                    // Cleanup
                    Reader.Close();
                }
            }

            // Return Rows
            return(Rows);
        }
        /// <summary>
        /// Converts a received parameter array from the client string to a keyValue pair dictionary
        /// </summary>
        /// <param name="parts">The array of data from the client</param>
        /// <returns></returns>
        private static Dictionary<string, string> ConvertToKeyValue(string[] parts)
        {
            Dictionary<string, string> Dic = new NiceDictionary<string, string>();

            try
            {
                for (int i = 0; i < parts.Length; i += 2)
                {
                    if (!Dic.ContainsKey(parts[i]))
                        Dic.Add(parts[i], parts[i + 1]);
                }
            }
            catch (IndexOutOfRangeException) { }

            return Dic;
        }
Example #4
0
        /// <summary>
        /// Initializes a new Snapshot, with the specified Date it was Posted
        /// </summary>
        /// <param name="Snapshot">The snapshot source</param>
        public Snapshot(string Snapshot, IPAddress ServerIp = null)
        {
            // Set some internal variables
            this.ServerIp = ServerIp ?? IPAddress.Loopback;
            this.Players = new List<Player>();
            this.DataString = Snapshot.Trim();
            string[] Data = DataString.Split('\\');
            Snapshot = null;

            // Check for invalid snapshot string. All snapshots have at least 36 data pairs,
            // and has an Even number of data sectors.
            if (Data.Length < 36 || Data.Length % 2 != 0)
                throw new InvalidDataException("Snapshot does not contain at least 36 elements, or contains an odd number of elements");

            // Assign server name and prefix
            this.ServerPrefix = Data[0];
            this.ServerName = Data[1];

            // Determine if we are central update. the "cdb_update" variable must be the LAST sector in snapshot
            int Mode;
            if (Data[Data.Length - 2] == "cdb_update" && Int32.TryParse(Data[Data.Length - 1], out Mode) && Mode.InRange(1, 2))
            {
                this.SnapshotMode = (Mode == 2) ? SnapshotMode.Minimal : SnapshotMode.FullSync;
            }

            // Setup our data dictionary's. We use NiceDictionary so we can easily determine missing keys in the log file
            NiceDictionary<string, string> StandardData = new NiceDictionary<string, string>(16);
            NiceDictionary<string, string> PlayerData = new NiceDictionary<string, string>();
            Dictionary<int, int> KillData = new Dictionary<int, int>();

            // Wrap parsing Key/Value snapshot data in a try block!
            try
            {
                // Convert our standard data into key => value pairs
                for (int i = 2; i < Data.Length; i += 2)
                {
                    // Format: "DataKey_PlayerIndex". PlayerIndex is NOT the Player Id
                    string[] Parts = Data[i].Split('_');
                    if (Parts.Length == 1)
                    {
                        // Add to the Standard keys
                        StandardData.Add(Data[i], Data[i + 1]);

                        // Are we at the End of File? If so stop here
                        if (Parts[0] == "EOF")
                        {
                            // Make sure to save that last players stats!!
                            if (PlayerData.Count != 0)
                            {
                                AddPlayer(new Player(PlayerData, KillData));
                                PlayerData = null;
                                KillData = null;
                            }
                            break;
                        }
                    }

                    // If the item key is "pID", then we have a new player record
                    else if (Parts[0] == "pID")
                    {
                        // If we have data, complete this player and start anew
                        if (PlayerData.Count != 0)
                        {
                            AddPlayer(new Player(PlayerData, KillData));
                            PlayerData.Clear();
                            KillData.Clear();
                        }

                        // Add new PID
                        PlayerData.Add(Parts[0], Data[i + 1]);
                    }
                    else if (Parts[0] == "mvks") // Skip mvks... kill data only needs processed once (mvns)
                        continue;
                    else if (Parts[0] == "mvns") // Player kill data
                        KillData.Add(Int32.Parse(Data[i + 1]), Int32.Parse(Data[i + 3]));
                    else
                        PlayerData.Add(Parts[0], Data[i + 1]);
                }
            }
            catch (Exception e)
            {
                throw new InvalidDataException("Error assigning Key => value pairs. See InnerException", e);
            }

            // Make sure we have a completed snapshot
            if (!StandardData.ContainsKey("EOF"))
                throw new InvalidDataException("No End of File element was found, Snapshot assumed to be incomplete.");

            // Try and set internal GameResult variables
            try
            {
                // Server data
                this.ServerPort = Int32.Parse(StandardData["gameport"]);
                this.QueryPort = Int32.Parse(StandardData["queryport"]);

                // Map Data
                this.MapName = StandardData["mapname"];
                this.MapId = Int32.Parse(StandardData["mapid"]);
                this.RoundStartTime = (int)Convert.ToDouble(StandardData["mapstart"], CultureInfo.InvariantCulture.NumberFormat);
                this.RoundEndTime = (int)Convert.ToDouble(StandardData["mapend"], CultureInfo.InvariantCulture.NumberFormat);

                // Misc Data
                this.GameMode = Int32.Parse(StandardData["gm"]);
                this.Mod = StandardData["v"]; // bf2 mod replaced the version key, since we dont care the version anyways
                this.PlayersConnected = Int32.Parse(StandardData["pc"]);

                // Army Data... There is no RWA key if there was no winner...
                this.WinningTeam = Int32.Parse(StandardData["win"]); // Temp
                this.WinningArmyId = (StandardData.ContainsKey("rwa")) ? Int32.Parse(StandardData["rwa"]) : -1;
                this.Team1ArmyId = Int32.Parse(StandardData["ra1"]);
                this.Team1Tickets = Int32.Parse(StandardData["rs1"]);
                this.Team2ArmyId = Int32.Parse(StandardData["ra2"]);
                this.Team2Tickets = Int32.Parse(StandardData["rs2"]);
            }
            catch (Exception e)
            {
                throw new InvalidDataException("Error assigning GameResult variables. See InnerException", e);
            }

            // Wrap this in a try-catch block, because we want to be able to view GameResult
            // data, even if the database is offline
            try
            {
                // Dispose when done!
                using (StatsDatabase Driver = new StatsDatabase())
                {
                    // Check for custom map, with no ID (Not defined in Constants.py)
                    if (this.MapId == 99)
                    {
                        // Check for existing map data
                        var Rows = Driver.Query("SELECT id FROM mapinfo WHERE name=@P0", this.MapName);
                        if (Rows.Count == 0)
                        {
                            // Create new MapId. Id's 700 - 1000 are reserved for unknown maps in the Constants.py file
                            // There should never be more then 300 unknown map id's, considering 1001 is the start of KNOWN
                            // Custom mod map id's. If we are at 1000 now, then we are in trouble :S
                            this.MapId = Driver.ExecuteScalar<int>("SELECT COALESCE(MAX(id), 699) FROM mapinfo WHERE id BETWEEN 700 AND 999") + 1;
                            if (this.MapId == 1000)
                                throw new Exception("Maximum unknown custom mapid has been reached. Please add this map's mapid to the Constants.py");

                            // Insert map data, so we dont lose this mapid we generated
                            Driver.Execute("INSERT INTO mapinfo(id, name, custom) VALUES (@P0, @P1, @P2)", this.MapId, this.MapName, 1);
                        }
                        else
                            this.MapId = Int32.Parse(Rows[0]["id"].ToString());
                    }

                    // Set whether or not this data is already been processed. The OR condition is because i goofed in early updates
                    // and set the timestamp to the RoundStart instead of the RoundEnd like i should have
                    this.IsProcessed = Driver.ExecuteScalar<int>(
                        "SELECT COUNT(*) FROM round_history WHERE mapid=@P0 AND time=@P1 AND (timestamp=@P2 OR timestamp=@P3)",
                        this.MapId, this.RoundTime.Seconds, this.RoundEndTime, this.RoundStartTime
                    ) > 0;
                }
            }
            catch(DbConnectException)
            {
                this.IsProcessed = false;
            }

            // Indicate whether we are a custom map
            this.IsCustomMap = (this.MapId >= 700 || this.MapId == 99);
        }