SetField() public method

Sets a value for the specified field
public SetField ( string Field, object Value ) : void
Field string The column or field name
Value object The value to insert
return void
        /// <summary>
        /// Adds a server's posted snapshot into the Snapshot Processing Queue, which
        /// will process the snapshot as soon as possible. This method is Non-Blocking.
        /// </summary>
        /// <remarks>
        /// Any errors that occur during the actual import of the data will be
        /// logged inside the StatsDebug log
        /// </remarks>
        /// <param name="Data">The snapshot data provided by the server.</param>
        /// <param name="ServerAddress">The IP address of the server.</param>
        /// <exception cref="UnauthorizedAccessException">
        ///     Thrown if the Server IP is not authorized to post game data to this server
        /// </exception>
        /// <exception cref="InvalidDataException">
        ///     Thrown if the provided Snapshot data is not valid, and cannot be processed
        /// </exception>
        public static void QueueServerSnapshot(string Data, IPAddress ServerAddress)
        {
            // Make sure the server is authorized
            if (!IsAuthorizedGameServer(ServerAddress))
                throw new UnauthorizedAccessException("Un-Authorised Gameserver (Ip: " + ServerAddress + ")");

            // Create the Snapshot Object
            Snapshot Snap = new Snapshot(Data, ServerAddress);

            // Update this server in the Database
            using (StatsDatabase Database = new StatsDatabase())
            {
                // Try and grab the ID of this server
                int id = Database.ExecuteScalar<int>(
                    "SELECT COALESCE(id, -1), COUNT(id) FROM servers WHERE ip=@P0 AND port=@P1",
                    ServerAddress, Snap.ServerPort
                );

                // New server?
                if (id < 0)
                {
                    InsertQueryBuilder builder = new InsertQueryBuilder(Database);
                    builder.SetTable("servers");
                    builder.SetField("ip", ServerAddress);
                    builder.SetField("port", Snap.ServerPort);
                    builder.SetField("prefix", Snap.ServerPrefix);
                    builder.SetField("name", Snap.ServerName);
                    builder.SetField("queryport", Snap.QueryPort);
                    builder.SetField("lastupdate", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
                    builder.Execute();
                }
                else // existing
                {
                    UpdateQueryBuilder builder = new UpdateQueryBuilder(Database);
                    builder.SetTable("servers");
                    builder.SetField("prefix", Snap.ServerPrefix);
                    builder.SetField("name", Snap.ServerName);
                    builder.SetField("queryport", Snap.QueryPort);
                    builder.SetField("lastupdate", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
                    builder.AddWhere("id", Comparison.Equals, id);
                    builder.Execute();
                }
            }

            // Add snapshot to Queue
            SnapshotQueue.Enqueue(Snap);
        }
        /// <summary>
        /// Imports a Player XML Sheet from the specified path
        /// </summary>
        /// <param name="XmlPath">The full path to the XML file</param>
        public static void ImportPlayerXml(string XmlPath)
        {
            // Connect to database first!
            using (StatsDatabase Driver = new StatsDatabase())
            {
                // Load elements
                XDocument Doc = XDocument.Load(new FileStream(XmlPath, FileMode.Open, FileAccess.Read));
                XElement Info = Doc.Root.Element("Info");
                XElement TableData = Doc.Root.Element("TableData");

                // Make sure player doesnt already exist
                int Pid = Int32.Parse(Info.Element("Pid").Value);
                if (Driver.PlayerExists(Pid))
                    throw new Exception(String.Format("Player with PID {0} already exists!", Pid));

                // Begin Transaction
                using (DbTransaction Transaction = Driver.BeginTransaction())
                {
                    try
                    {
                        // Loop through tables
                        foreach (XElement Table in TableData.Elements())
                        {
                            // Loop through Rows
                            foreach (XElement Row in Table.Elements())
                            {
                                InsertQueryBuilder QueryBuilder = new InsertQueryBuilder(Table.Name.LocalName, Driver);
                                foreach (XElement Col in Row.Elements())
                                {
                                    if (Col.Name.LocalName == "name")
                                        QueryBuilder.SetField(Col.Name.LocalName, Col.Value.UnescapeXML());
                                    else
                                        QueryBuilder.SetField(Col.Name.LocalName, Col.Value);
                                }

                                QueryBuilder.Execute();
                            }
                        }

                        // Commit Transaction
                        Transaction.Commit();
                    }
                    catch
                    {
                        Transaction.Rollback();
                        throw;
                    }
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Processes the snapshot data, inserted and updating player data.
        /// </summary>
        /// <exception cref="InvalidDataException">Thrown if the snapshot data is invalid</exception>
        public void Process()
        {
            // Make sure we are valid, or throw exception!
            if (!IsValidSnapshot)
                throw new InvalidDataException("Invalid Snapshot data!");

            // Begin Logging
            Log(String.Format("Begin Processing ({0})...", MapName), LogLevel.Notice);
            if (IsCustomMap)
                Log(String.Format("Custom Map ({0})...", MapId), LogLevel.Notice);
            else
                Log(String.Format("Standard Map ({0})...", MapId), LogLevel.Notice);

            Log("Found " + PlayerData.Count + " Player(s)...", LogLevel.Notice);

            // Make sure we meet the minimum player requirement
            if (PlayerData.Count < MainForm.Config.ASP_MinRoundPlayers)
            {
                Log("Minimum round Player count does not meet the ASP requirement... Aborting", LogLevel.Warning);
                return;
            }

            // Start a timer!
            Stopwatch Clock = new Stopwatch();
            Clock.Start();

            // Setup some variables
            List<Dictionary<string, object>> Rows;
            InsertQueryBuilder InsertQuery;
            UpdateQueryBuilder UpdateQuery;
            WhereClause Where;

            // Temporary Map Data (For Round history and Map info tables)
            int MapScore = 0;
            int MapKills = 0;
            int MapDeaths = 0;
            int Team1Players = 0;
            int Team2Players = 0;
            int Team1PlayersEnd = 0;
            int Team2PlayersEnd = 0;

            // MySQL could throw a packet size error here, so we need will increase it!
            if (Driver.DatabaseEngine == DatabaseEngine.Mysql)
                Driver.Execute("SET GLOBAL max_allowed_packet=51200");

            // Begin Transaction
            DbTransaction Transaction = Driver.BeginTransaction();

            // To prevent half complete snapshots due to exceptions,
            // Put the whole thing in a try block, and rollback on error
            try
            {
                // Loop through each player, and process them
                int PlayerPosition = 0;
                foreach (Dictionary<string, string> Player in PlayerData)
                {
                    // Parse some player data
                    int Pid = Int32.Parse(Player["pID"]);
                    int Time = Int32.Parse(Player["ctime"]);
                    int SqlTime = Int32.Parse(Player["tsl"]);
                    int SqmTime = Int32.Parse(Player["tsm"]);
                    int LwTime = Int32.Parse(Player["tlw"]);
                    int Army = Int32.Parse(Player["a"]);
                    int RoundScore = Int32.Parse(Player["rs"]);
                    int CurRank = Int32.Parse(Player["rank"]);
                    int Kills = Int32.Parse(Player["kills"]);
                    int Deaths = Int32.Parse(Player["deaths"]);
                    int Ks = Int32.Parse(Player["ks"]);
                    int Ds = Int32.Parse(Player["ds"]);
                    bool IsAi = (Int32.Parse(Player["ai"]) != 0);
                    bool CompletedRound = (Int32.Parse(Player["c"]) == 1);
                    bool OnWinningTeam = (Int32.Parse(Player["t"]) == WinningTeam);
                    IPAddress PlayerIp = IPAddress.Loopback;

                    // Player meets min round time or are we ignoring AI?
                    if ((Time < MainForm.Config.ASP_MinRoundTime) || (MainForm.Config.ASP_IgnoreAI && IsAi))
                        continue;

                    // Add map data
                    MapScore += RoundScore;
                    MapKills += Kills;
                    MapDeaths += Deaths;

                    // Fix N/A Ip address
                    if (Player["ip"] == "N/A")
                        Player["ip"] = "127.0.0.1";

                    // Sometimes Squad times are negative.. idk why, but we need to fix that here
                    if (SqlTime < 0) SqlTime = 0;
                    if (SqmTime < 0) SqmTime = 0;
                    if (LwTime < 0) LwTime = 0;

                    // Log
                    Log(String.Format("Processing Player ({0})", Pid), LogLevel.Notice);

                    // Fetch the player
                    string Query;
                    Rows = Driver.Query("SELECT COUNT(id) AS count FROM player WHERE id=@P0", Pid);
                    if (int.Parse(Rows[0]["count"].ToString()) == 0)
                    {
                        // === New Player === //

                        // Log
                        Log(String.Format("Adding NEW Player ({0})", Pid), LogLevel.Notice);

                        // Get playres country code
                        IPAddress.TryParse(Player["ip"], out PlayerIp);
                        string CC = GetCountryCode(PlayerIp);

                        // Build insert data
                        InsertQuery = new InsertQueryBuilder("player", Driver);
                        InsertQuery.SetField("id", Pid);
                        InsertQuery.SetField("name", Player["name"]);
                        InsertQuery.SetField("country", CC);
                        InsertQuery.SetField("time", Time);
                        InsertQuery.SetField("rounds", Player["c"]);
                        InsertQuery.SetField("ip", Player["ip"]);
                        InsertQuery.SetField("score", Player["rs"]);
                        InsertQuery.SetField("cmdscore", Player["cs"]);
                        InsertQuery.SetField("skillscore", Player["ss"]);
                        InsertQuery.SetField("teamscore", Player["ts"]);
                        InsertQuery.SetField("kills", Player["kills"]);
                        InsertQuery.SetField("deaths", Player["deaths"]);
                        InsertQuery.SetField("captures", Player["cpc"]);
                        InsertQuery.SetField("captureassists", Player["cpa"]);
                        InsertQuery.SetField("defends", Player["cpd"]);
                        InsertQuery.SetField("damageassists", Player["ka"]);
                        InsertQuery.SetField("heals", Player["he"]);
                        InsertQuery.SetField("revives", Player["rev"]);
                        InsertQuery.SetField("ammos", Player["rsp"]);
                        InsertQuery.SetField("repairs", Player["rep"]);
                        InsertQuery.SetField("targetassists", Player["tre"]);
                        InsertQuery.SetField("driverspecials", Player["drs"]);
                        InsertQuery.SetField("teamkills", Player["tmkl"]);
                        InsertQuery.SetField("teamdamage", Player["tmdg"]);
                        InsertQuery.SetField("teamvehicledamage", Player["tmvd"]);
                        InsertQuery.SetField("suicides", Player["su"]);
                        InsertQuery.SetField("killstreak", Player["ks"]);
                        InsertQuery.SetField("deathstreak", Player["ds"]);
                        InsertQuery.SetField("rank", Player["rank"]);
                        InsertQuery.SetField("banned", Player["ban"]);
                        InsertQuery.SetField("kicked", Player["kck"]);
                        InsertQuery.SetField("cmdtime", Player["tco"]);
                        InsertQuery.SetField("sqltime", SqlTime);
                        InsertQuery.SetField("sqmtime", SqmTime);
                        InsertQuery.SetField("lwtime", LwTime);
                        InsertQuery.SetField("wins", OnWinningTeam);
                        InsertQuery.SetField("losses", !OnWinningTeam);
                        InsertQuery.SetField("availunlocks", 0);
                        InsertQuery.SetField("usedunlocks", 0);
                        InsertQuery.SetField("joined", TimeStamp);
                        InsertQuery.SetField("rndscore", Player["rs"]);
                        InsertQuery.SetField("lastonline", MapEnd);
                        InsertQuery.SetField("mode0", ((GameMode == 0) ? 1 : 0));
                        InsertQuery.SetField("mode1", ((GameMode == 1) ? 1 : 0));
                        InsertQuery.SetField("mode2", ((GameMode == 2) ? 1 : 0));
                        InsertQuery.SetField("isbot", Player["ai"]);

                        // Insert Player Data
                        InsertQuery.Execute();

                        // Create Player Unlock Data
                        Query = "INSERT INTO unlocks VALUES ";
                        for (int i = 11; i < 100; i += 11)
                            Query += String.Format("({0}, {1}, 'n'), ", Pid, i);
                        for (int i = 111; i < 556; i += 111)
                            Query += String.Format("({0}, {1}, 'n'), ", Pid, i);
                        Driver.Execute(Query.TrimEnd(new char[] { ',', ' ' }));
                    }
                    else
                    {
                        // Existing Player

                        // Log
                        Log(String.Format("Updating EXISTING Player ({0})", Pid), LogLevel.Notice);

                        // Fetch Player
                        Rows = Driver.Query("SELECT ip, country, rank, killstreak, deathstreak, rndscore FROM player WHERE id=@P0", Pid);
                        Dictionary<string, object> DataRow = Rows[0];

                        // Setup vars
                        string CC = DataRow["country"].ToString();
                        int DbRank = Int32.Parse(DataRow["rank"].ToString());

                        // Update country if the ip has changed
                        IPAddress.TryParse(Player["ip"], out PlayerIp);
                        if (DataRow["ip"].ToString() != Player["ip"])
                            CC = GetCountryCode(PlayerIp);

                        // Verify/Correct Rank
                        if (MainForm.Config.ASP_StatsRankCheck)
                        {
                            // Fail-safe in-case rank data was not obtained and reset to '0' in-game.
                            if (DbRank > CurRank)
                            {
                                Player["rank"] = DbRank.ToString();
                                DebugLog.Write("Rank Correction ({0}), Using database rank ({1})", Pid, DbRank);
                            }
                        }

                        // Calcuate best killstreak/deathstreak
                        int KillStreak = Int32.Parse(DataRow["killstreak"].ToString());
                        int DeathStreak = Int32.Parse(DataRow["deathstreak"].ToString());
                        if (Ks > KillStreak) KillStreak = Ks;
                        if (Ds > DeathStreak) DeathStreak = Ds;

                        // Calculate Best Round Score
                        int Brs = Int32.Parse(DataRow["rndscore"].ToString());
                        if (RoundScore > Brs) Brs = RoundScore;

                        // Calculate rank change
                        int chng = 0;
                        int decr = 0;
                        if (DbRank != CurRank)
                        {
                            if (CurRank > DbRank)
                                chng = 1;
                            else
                                decr = 1;
                        }

                        // Update Player Data
                        UpdateQuery = new UpdateQueryBuilder("player", Driver);
                        UpdateQuery.SetField("country", CC);
                        UpdateQuery.SetField("time", Time, ValueMode.Add);
                        UpdateQuery.SetField("rounds", Player["c"], ValueMode.Add);
                        UpdateQuery.SetField("ip", Player["ip"]);
                        UpdateQuery.SetField("score", Player["rs"], ValueMode.Add);
                        UpdateQuery.SetField("cmdscore", Player["cs"], ValueMode.Add);
                        UpdateQuery.SetField("skillscore", Player["ss"], ValueMode.Add);
                        UpdateQuery.SetField("teamscore", Player["ts"], ValueMode.Add);
                        UpdateQuery.SetField("kills", Player["kills"], ValueMode.Add);
                        UpdateQuery.SetField("deaths", Player["deaths"], ValueMode.Add);
                        UpdateQuery.SetField("captures", Player["cpc"], ValueMode.Add);
                        UpdateQuery.SetField("captureassists", Player["cpa"], ValueMode.Add);
                        UpdateQuery.SetField("defends", Player["cpd"], ValueMode.Add);
                        UpdateQuery.SetField("damageassists", Player["ks"], ValueMode.Add);
                        UpdateQuery.SetField("heals", Player["he"], ValueMode.Add);
                        UpdateQuery.SetField("revives", Player["rev"], ValueMode.Add);
                        UpdateQuery.SetField("ammos", Player["rsp"], ValueMode.Add);
                        UpdateQuery.SetField("repairs", Player["rep"], ValueMode.Add);
                        UpdateQuery.SetField("targetassists", Player["tre"], ValueMode.Add);
                        UpdateQuery.SetField("driverspecials", Player["drs"], ValueMode.Add);
                        UpdateQuery.SetField("teamkills", Player["tmkl"], ValueMode.Add);
                        UpdateQuery.SetField("teamdamage", Player["tmdg"], ValueMode.Add);
                        UpdateQuery.SetField("teamvehicledamage", Player["tmvd"], ValueMode.Add);
                        UpdateQuery.SetField("suicides", Player["su"], ValueMode.Add);
                        UpdateQuery.SetField("Killstreak", KillStreak, ValueMode.Set);
                        UpdateQuery.SetField("deathstreak", DeathStreak, ValueMode.Set);
                        UpdateQuery.SetField("rank", CurRank, ValueMode.Set);
                        UpdateQuery.SetField("banned", Player["ban"], ValueMode.Add);
                        UpdateQuery.SetField("kicked", Player["kck"], ValueMode.Add);
                        UpdateQuery.SetField("cmdtime", Player["tco"], ValueMode.Add);
                        UpdateQuery.SetField("sqltime", SqlTime, ValueMode.Add);
                        UpdateQuery.SetField("sqmtime", SqmTime, ValueMode.Add);
                        UpdateQuery.SetField("lwtime", LwTime, ValueMode.Add);
                        UpdateQuery.SetField("wins", ((OnWinningTeam) ? 1 : 0), ValueMode.Add);
                        UpdateQuery.SetField("losses", ((!OnWinningTeam) ? 1 : 0), ValueMode.Add);
                        UpdateQuery.SetField("rndscore", Brs, ValueMode.Set);
                        UpdateQuery.SetField("lastonline", TimeStamp, ValueMode.Set);
                        UpdateQuery.SetField("mode0", ((GameMode == 0) ? 1 : 0), ValueMode.Add);
                        UpdateQuery.SetField("mode1", ((GameMode == 1) ? 1 : 0), ValueMode.Add);
                        UpdateQuery.SetField("mode2", ((GameMode == 2) ? 1 : 0), ValueMode.Add);
                        UpdateQuery.SetField("chng", chng, ValueMode.Set);
                        UpdateQuery.SetField("decr", decr, ValueMode.Set);
                        UpdateQuery.SetField("isbot", Player["ai"], ValueMode.Set);
                        UpdateQuery.AddWhere("id", Comparison.Equals, Pid);
                        UpdateQuery.Execute();
                    }

                    // ********************************
                    // Insert Player history.
                    // ********************************
                    Driver.Execute(
                        "INSERT INTO player_history VALUES(@P0, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8, @P9)",
                        Pid, TimeStamp, Time, RoundScore, Player["cs"], Player["ss"], Player["ts"],
                        Kills, Deaths, CurRank
                    );

                    // ********************************
                    // Process Player Army Data
                    // ********************************
                    Log(String.Format("Processing Army Data ({0})", Pid), LogLevel.Notice);

                    // DO team counts
                    if(Army == Team1Army)
                    {
                        Team1Players++;
                        if (CompletedRound) // Completed round?
                            Team1PlayersEnd++;
                    }
                    else
                    {
                        Team2Players++;
                        if (CompletedRound) // Completed round?
                            Team2PlayersEnd++;
                    }

                    // Update player army times
                    Rows = Driver.Query("SELECT * FROM army WHERE id=" + Pid);
                    if (Rows.Count == 0)
                    {
                        InsertQuery = new InsertQueryBuilder("army", Driver);
                        InsertQuery.SetField("id", Pid);
                        InsertQuery.SetField("time0", Player["ta0"]);
                        InsertQuery.SetField("time1", Player["ta1"]);
                        InsertQuery.SetField("time2", Player["ta2"]);
                        InsertQuery.SetField("time3", Player["ta3"]);
                        InsertQuery.SetField("time4", Player["ta4"]);
                        InsertQuery.SetField("time5", Player["ta5"]);
                        InsertQuery.SetField("time6", Player["ta6"]);
                        InsertQuery.SetField("time7", Player["ta7"]);
                        InsertQuery.SetField("time8", Player["ta8"]);
                        InsertQuery.SetField("time9", Player["ta9"]);
                        InsertQuery.SetField("time10", Player["ta10"]);
                        InsertQuery.SetField("time11", Player["ta11"]);
                        InsertQuery.SetField("time12", Player["ta12"]);
                        InsertQuery.SetField("time13", Player["ta13"]);

                        // Make sure we arent playing an unsupported army
                        if (Army < 14)
                        {
                            InsertQuery.SetField("win" + Army, ((OnWinningTeam) ? 1 : 0));
                            InsertQuery.SetField("loss" + Army, ((!OnWinningTeam) ? 1 : 0));
                            InsertQuery.SetField("score" + Army, Player["rs"]);
                            InsertQuery.SetField("best" + Army, Player["rs"]);
                            InsertQuery.SetField("worst" + Army, Player["rs"]);
                        }

                        InsertQuery.Execute();
                    }
                    else
                    {
                        UpdateQuery = new UpdateQueryBuilder("army", Driver);
                        UpdateQuery.AddWhere("id", Comparison.Equals, Pid);
                        UpdateQuery.SetField("time0", Player["ta0"], ValueMode.Add);
                        UpdateQuery.SetField("time1", Player["ta1"], ValueMode.Add);
                        UpdateQuery.SetField("time2", Player["ta2"], ValueMode.Add);
                        UpdateQuery.SetField("time3", Player["ta3"], ValueMode.Add);
                        UpdateQuery.SetField("time4", Player["ta4"], ValueMode.Add);
                        UpdateQuery.SetField("time5", Player["ta5"], ValueMode.Add);
                        UpdateQuery.SetField("time6", Player["ta6"], ValueMode.Add);
                        UpdateQuery.SetField("time7", Player["ta7"], ValueMode.Add);
                        UpdateQuery.SetField("time8", Player["ta8"], ValueMode.Add);
                        UpdateQuery.SetField("time9", Player["ta9"], ValueMode.Add);
                        UpdateQuery.SetField("time10", Player["ta10"], ValueMode.Add);
                        UpdateQuery.SetField("time11", Player["ta11"], ValueMode.Add);
                        UpdateQuery.SetField("time12", Player["ta12"], ValueMode.Add);
                        UpdateQuery.SetField("time13", Player["ta13"], ValueMode.Add);

                        // Prevent database errors with custom army IDs
                        if (Army < 14)
                        {
                            string Best = (Int32.Parse(Rows[0]["best" + Army].ToString()) > RoundScore)
                                ? Rows[0]["best" + Army].ToString()
                                : Player["rs"];
                            string Worst = (Int32.Parse(Rows[0]["worst" + Army].ToString()) > RoundScore)
                                ? Rows[0]["worst" + Army].ToString()
                                : Player["rs"];

                            UpdateQuery.SetField("win" + Army, OnWinningTeam, ValueMode.Add);
                            UpdateQuery.SetField("loss" + Army, !OnWinningTeam, ValueMode.Add);
                            UpdateQuery.SetField("score" + Army, Player["rs"], ValueMode.Add);
                            UpdateQuery.SetField("best" + Army, Best, ValueMode.Set);
                            UpdateQuery.SetField("worst" + Army, Worst, ValueMode.Set);
                        }

                        UpdateQuery.Execute();
                    }

                    // ********************************
                    // Process Player Kills
                    // ********************************
                    Log(String.Format("Processing Kills Data ({0})", Pid), LogLevel.Notice);

                    foreach (KeyValuePair<string, string> Kill in KillData[PlayerPosition])
                    {
                        string Victim = Kill.Key;
                        int KillCount = Int32.Parse(Kill.Value);
                        Rows = Driver.Query("SELECT count FROM kills WHERE attacker=@P0 AND victim=@P1", Pid, Victim);
                        if (Rows.Count == 0)
                        {
                            InsertQuery = new InsertQueryBuilder("kills", Driver);
                            InsertQuery.SetField("attacker", Pid);
                            InsertQuery.SetField("victim", Victim);
                            InsertQuery.SetField("count", KillCount);
                            InsertQuery.Execute();
                        }
                        else
                        {
                            UpdateQuery = new UpdateQueryBuilder("kills", Driver);
                            UpdateQuery.SetField("count", KillCount, ValueMode.Add);
                            Where = UpdateQuery.AddWhere("attacker", Comparison.Equals, Pid);
                            Where.AddClause(LogicOperator.And, "victim", Comparison.Equals, Victim);
                            UpdateQuery.Execute();
                        }
                    }

                    // ********************************
                    // Process Player Kit Data
                    // ********************************
                    Log(String.Format("Processing Kit Data ({0})", Pid), LogLevel.Notice);

                    Rows = Driver.Query("SELECT time0 FROM kits WHERE id=" + Pid);
                    if (Rows.Count == 0)
                    {
                        InsertQuery = new InsertQueryBuilder("kits", Driver);
                        InsertQuery.SetField("id", Pid);
                        for (int i = 0; i < 7; i++)
                        {
                            InsertQuery.SetField("time" + i, Player["tk" + i]);
                            InsertQuery.SetField("kills" + i, Player["kk" + i]);
                            InsertQuery.SetField("deaths" + i, Player["dk" + i]);
                        }
                        InsertQuery.Execute();
                    }
                    else
                    {
                        UpdateQuery = new UpdateQueryBuilder("kits", Driver);
                        UpdateQuery.AddWhere("id", Comparison.Equals, Pid);
                        for (int i = 0; i < 7; i++)
                        {
                            UpdateQuery.SetField("time" + i, Player["tk" + i], ValueMode.Add);
                            UpdateQuery.SetField("kills" + i, Player["kk" + i], ValueMode.Add);
                            UpdateQuery.SetField("deaths" + i, Player["dk" + i], ValueMode.Add);
                        }
                        UpdateQuery.Execute();
                    }

                    // ********************************
                    // Process Player Vehicle Data
                    // ********************************
                    Log(String.Format("Processing Vehicle Data ({0})", Pid), LogLevel.Notice);

                    Rows = Driver.Query("SELECT time0 FROM vehicles WHERE id=" + Pid);
                    if (Rows.Count == 0)
                    {
                        InsertQuery = new InsertQueryBuilder("vehicles", Driver);
                        InsertQuery.SetField("id", Pid);
                        for (int i = 0; i < 7; i++)
                        {
                            InsertQuery.SetField("time" + i, Player["tv" + i]);
                            InsertQuery.SetField("kills" + i, Player["kv" + i]);
                            InsertQuery.SetField("deaths" + i, Player["bv" + i]);
                            InsertQuery.SetField("rk" + i, Player["kvr" + i]);
                        }
                        InsertQuery.SetField("timepara", Player["tvp"]);
                        InsertQuery.Execute();
                    }
                    else
                    {
                        UpdateQuery = new UpdateQueryBuilder("vehicles", Driver);
                        UpdateQuery.AddWhere("id", Comparison.Equals, Pid);
                        for (int i = 0; i < 7; i++)
                        {
                            UpdateQuery.SetField("time" + i, Player["tv" + i], ValueMode.Add);
                            UpdateQuery.SetField("kills" + i, Player["kv" + i], ValueMode.Add);
                            UpdateQuery.SetField("deaths" + i, Player["bv" + i], ValueMode.Add);
                            UpdateQuery.SetField("rk" + i, Player["kvr" + i], ValueMode.Add);
                        }
                        UpdateQuery.SetField("timepara", Player["tvp"], ValueMode.Add);
                        UpdateQuery.Execute();
                    }

                    // ********************************
                    // Process Player Weapon Data
                    // ********************************
                    Log(String.Format("Processing Weapon Data ({0})", Pid), LogLevel.Notice);

                    Rows = Driver.Query("SELECT time0 FROM weapons WHERE id=" + Pid);
                    if (Rows.Count == 0)
                    {
                        // Prepare Query
                        InsertQuery = new InsertQueryBuilder("weapons", Driver);
                        InsertQuery.SetField("id", Pid);

                        // Basic Weapon Data
                        for (int i = 0; i < 9; i++)
                        {
                            InsertQuery.SetField("time" + i, Player["tw" + i]);
                            InsertQuery.SetField("kills" + i, Player["kw" + i]);
                            InsertQuery.SetField("deaths" + i, Player["bw" + i]);
                            InsertQuery.SetField("fired" + i, Player["sw" + i]);
                            InsertQuery.SetField("hit" + i, Player["hw" + i]);
                        }

                        // Knife Data
                        InsertQuery.SetField("knifetime", Player["te0"]);
                        InsertQuery.SetField("knifekills", Player["ke0"]);
                        InsertQuery.SetField("knifedeaths", Player["be0"]);
                        InsertQuery.SetField("knifefired", Player["se0"]);
                        InsertQuery.SetField("knifehit", Player["he0"]);

                        // C4 Data
                        InsertQuery.SetField("c4time", Player["te1"]);
                        InsertQuery.SetField("c4kills", Player["ke1"]);
                        InsertQuery.SetField("c4deaths", Player["be1"]);
                        InsertQuery.SetField("c4fired", Player["se1"]);
                        InsertQuery.SetField("c4hit", Player["he1"]);

                        // Handgrenade
                        InsertQuery.SetField("handgrenadetime", Player["te3"]);
                        InsertQuery.SetField("handgrenadekills", Player["ke3"]);
                        InsertQuery.SetField("handgrenadedeaths", Player["be3"]);
                        InsertQuery.SetField("handgrenadefired", Player["se3"]);
                        InsertQuery.SetField("handgrenadehit", Player["he3"]);

                        // Claymore
                        InsertQuery.SetField("claymoretime", Player["te2"]);
                        InsertQuery.SetField("claymorekills", Player["ke2"]);
                        InsertQuery.SetField("claymoredeaths", Player["be2"]);
                        InsertQuery.SetField("claymorefired", Player["se2"]);
                        InsertQuery.SetField("claymorehit", Player["he2"]);

                        // Shockpad
                        InsertQuery.SetField("shockpadtime", Player["te4"]);
                        InsertQuery.SetField("shockpadkills", Player["ke4"]);
                        InsertQuery.SetField("shockpaddeaths", Player["be4"]);
                        InsertQuery.SetField("shockpadfired", Player["se4"]);
                        InsertQuery.SetField("shockpadhit", Player["he4"]);

                        // At Mine
                        InsertQuery.SetField("atminetime", Player["te5"]);
                        InsertQuery.SetField("atminekills", Player["ke5"]);
                        InsertQuery.SetField("atminedeaths", Player["be5"]);
                        InsertQuery.SetField("atminefired", Player["se5"]);
                        InsertQuery.SetField("atminehit", Player["he5"]);

                        // Tactical
                        InsertQuery.SetField("tacticaltime", Player["te6"]);
                        InsertQuery.SetField("tacticaldeployed", Player["de6"]);

                        // Grappling Hook
                        InsertQuery.SetField("grapplinghooktime", Player["te7"]);
                        InsertQuery.SetField("grapplinghookdeployed", Player["de7"]);
                        InsertQuery.SetField("grapplinghookdeaths", Player["be9"]);

                        // Zipline
                        InsertQuery.SetField("ziplinetime", Player["te8"]);
                        InsertQuery.SetField("ziplinedeployed", Player["de8"]);
                        InsertQuery.SetField("ziplinedeaths", Player["be8"]);

                        // Do Query
                        InsertQuery.Execute();
                    }
                    else
                    {
                        // Prepare Query
                        UpdateQuery = new UpdateQueryBuilder("weapons", Driver);
                        UpdateQuery.AddWhere("id", Comparison.Equals, Pid);

                        // Basic Weapon Data
                        for (int i = 0; i < 9; i++)
                        {
                            UpdateQuery.SetField("time" + i, Player["tw" + i], ValueMode.Add);
                            UpdateQuery.SetField("kills" + i, Player["kw" + i], ValueMode.Add);
                            UpdateQuery.SetField("deaths" + i, Player["bw" + i], ValueMode.Add);
                            UpdateQuery.SetField("fired" + i, Player["sw" + i], ValueMode.Add);
                            UpdateQuery.SetField("hit" + i, Player["hw" + i], ValueMode.Add);
                        }

                        // Knife Data
                        UpdateQuery.SetField("knifetime", Player["te0"], ValueMode.Add);
                        UpdateQuery.SetField("knifekills", Player["ke0"], ValueMode.Add);
                        UpdateQuery.SetField("knifedeaths", Player["be0"], ValueMode.Add);
                        UpdateQuery.SetField("knifefired", Player["se0"], ValueMode.Add);
                        UpdateQuery.SetField("knifehit", Player["he0"], ValueMode.Add);

                        // C4 Data
                        UpdateQuery.SetField("c4time", Player["te1"], ValueMode.Add);
                        UpdateQuery.SetField("c4kills", Player["ke1"], ValueMode.Add);
                        UpdateQuery.SetField("c4deaths", Player["be1"], ValueMode.Add);
                        UpdateQuery.SetField("c4fired", Player["se1"], ValueMode.Add);
                        UpdateQuery.SetField("c4hit", Player["he1"], ValueMode.Add);

                        // Handgrenade
                        UpdateQuery.SetField("handgrenadetime", Player["te3"], ValueMode.Add);
                        UpdateQuery.SetField("handgrenadekills", Player["ke3"], ValueMode.Add);
                        UpdateQuery.SetField("handgrenadedeaths", Player["be3"], ValueMode.Add);
                        UpdateQuery.SetField("handgrenadefired", Player["se3"], ValueMode.Add);
                        UpdateQuery.SetField("handgrenadehit", Player["he3"], ValueMode.Add);

                        // Claymore
                        UpdateQuery.SetField("claymoretime", Player["te2"], ValueMode.Add);
                        UpdateQuery.SetField("claymorekills", Player["ke2"], ValueMode.Add);
                        UpdateQuery.SetField("claymoredeaths", Player["be2"], ValueMode.Add);
                        UpdateQuery.SetField("claymorefired", Player["se2"], ValueMode.Add);
                        UpdateQuery.SetField("claymorehit", Player["he2"], ValueMode.Add);

                        // Shockpad
                        UpdateQuery.SetField("shockpadtime", Player["te4"], ValueMode.Add);
                        UpdateQuery.SetField("shockpadkills", Player["ke4"], ValueMode.Add);
                        UpdateQuery.SetField("shockpaddeaths", Player["be4"], ValueMode.Add);
                        UpdateQuery.SetField("shockpadfired", Player["se4"], ValueMode.Add);
                        UpdateQuery.SetField("shockpadhit", Player["he4"], ValueMode.Add);

                        // At Mine
                        UpdateQuery.SetField("atminetime", Player["te5"], ValueMode.Add);
                        UpdateQuery.SetField("atminekills", Player["ke5"], ValueMode.Add);
                        UpdateQuery.SetField("atminedeaths", Player["be5"], ValueMode.Add);
                        UpdateQuery.SetField("atminefired", Player["se5"], ValueMode.Add);
                        UpdateQuery.SetField("atminehit", Player["he5"], ValueMode.Add);

                        // Tactical
                        UpdateQuery.SetField("tacticaltime", Player["te6"], ValueMode.Add);
                        UpdateQuery.SetField("tacticaldeployed", Player["de6"], ValueMode.Add);

                        // Grappling Hook
                        UpdateQuery.SetField("grapplinghooktime", Player["te7"], ValueMode.Add);
                        UpdateQuery.SetField("grapplinghookdeployed", Player["de7"], ValueMode.Add);
                        UpdateQuery.SetField("grapplinghookdeaths", Player["be9"], ValueMode.Add);

                        // Zipline
                        UpdateQuery.SetField("ziplinetime", Player["te8"], ValueMode.Add);
                        UpdateQuery.SetField("ziplinedeployed", Player["de8"], ValueMode.Add);
                        UpdateQuery.SetField("ziplinedeaths", Player["be8"], ValueMode.Add);

                        // Do Query
                        UpdateQuery.Execute();
                    }

                    // ********************************
                    // Process Player Map Data
                    // ********************************
                    Log(String.Format("Processing Map Data ({0})", Pid), LogLevel.Notice);

                    Rows = Driver.Query("SELECT best, worst FROM maps WHERE id=@P0 AND mapid=@P1", Pid, MapId);
                    if (Rows.Count == 0)
                    {
                        // Prepare Query
                        InsertQuery = new InsertQueryBuilder("maps", Driver);
                        InsertQuery.SetField("id", Pid);
                        InsertQuery.SetField("mapid", MapId);
                        InsertQuery.SetField("time", Time);
                        InsertQuery.SetField("win", ((OnWinningTeam) ? 1 : 0));
                        InsertQuery.SetField("loss", ((!OnWinningTeam) ? 1 : 0));
                        InsertQuery.SetField("best", RoundScore);
                        InsertQuery.SetField("worst", RoundScore);
                        InsertQuery.Execute();
                    }
                    else
                    {
                        // Get best and worst round scores
                        string Best = ((Int32.Parse(Rows[0]["best"].ToString()) > RoundScore) ? Rows[0]["best"].ToString() : RoundScore.ToString());
                        string Worst = ((Int32.Parse(Rows[0]["worst"].ToString()) > RoundScore) ? Rows[0]["worst"].ToString() : RoundScore.ToString());

                        // Prepare Query
                        UpdateQuery = new UpdateQueryBuilder("maps", Driver);
                        Where = UpdateQuery.AddWhere("id", Comparison.Equals, Pid);
                        Where.AddClause(LogicOperator.And, "mapid", Comparison.Equals, MapId);
                        UpdateQuery.SetField("time", Time, ValueMode.Add);
                        UpdateQuery.SetField("win", ((OnWinningTeam) ? 1 : 0), ValueMode.Add);
                        UpdateQuery.SetField("loss", ((!OnWinningTeam) ? 1 : 0), ValueMode.Add);
                        UpdateQuery.SetField("best", Best, ValueMode.Add);
                        UpdateQuery.SetField("worst", Worst, ValueMode.Add);
                        UpdateQuery.Execute();
                    }

                    // ********************************
                    // Process Player Awards Data
                    // ********************************
                    Log(String.Format("Processing Award Data ({0})", Pid), LogLevel.Notice);

                    // Do we require round completion for award processing?
                    if (CompletedRound || !MainForm.Config.ASP_AwardsReqComplete)
                    {
                        // Get our list of awards we earned in the round
                        Dictionary<int, int> Awards = GetRoundAwards(Pid, Player);
                        foreach (KeyValuePair<int, int> Award in Awards)
                        {
                            int First = 0;
                            int AwardId = Award.Key;
                            int Level = Award.Value;

                            // If isMedal
                            if (AwardId > 2000000 && AwardId < 3000000)
                                Query = String.Format("SELECT level FROM awards WHERE id={0} AND awd={1}", Pid, AwardId);
                            else
                                Query = String.Format("SELECT level FROM awards WHERE id={0} AND awd={1} AND level={2}", Pid, AwardId, Level);

                            // Check for prior awarding of award
                            Rows = Driver.Query(Query);
                            if (Rows.Count == 0)
                            {
                                // Medals
                                if (AwardId > 2000000 && AwardId < 3000000)
                                    First = TimeStamp;

                                // Badges
                                else if(AwardId < 2000000)
                                {
                                    // Need to do extra work for Badges as more than one badge per round may have been awarded
                                    for (int j = 1; j < Level; j++)
                                    {
                                        Rows = Driver.Query("SELECT level FROM awards WHERE id=@P0 AND awd=@P1 AND level=@P2", Pid, AwardId, j);
                                        if (Rows.Count == 0)
                                        {
                                            // Prepare Query
                                            InsertQuery = new InsertQueryBuilder("awards", Driver);
                                            InsertQuery.SetField("id", Pid);
                                            InsertQuery.SetField("awd", AwardId);
                                            InsertQuery.SetField("level", j);
                                            InsertQuery.SetField("earned", (TimeStamp - 5) + j);
                                            InsertQuery.SetField("first", First);
                                            InsertQuery.Execute();
                                        }
                                    }
                                }

                                // Add the players award
                                InsertQuery = new InsertQueryBuilder("awards", Driver);
                                InsertQuery.SetField("id", Pid);
                                InsertQuery.SetField("awd", AwardId);
                                InsertQuery.SetField("level", Level);
                                InsertQuery.SetField("earned", TimeStamp);
                                InsertQuery.SetField("first", First);
                                InsertQuery.Execute();

                            }
                            else
                            {
                                // Player has recived this award prior //

                                // If award if a medal (Because ribbons and badges are only awarded once ever!)
                                if (AwardId > 2000000 && AwardId < 3000000)
                                {
                                    // Prepare Query
                                    UpdateQuery = new UpdateQueryBuilder("awards", Driver);
                                    Where = UpdateQuery.AddWhere("id", Comparison.Equals, Pid);
                                    Where.AddClause(LogicOperator.And, "awd", Comparison.Equals, AwardId);
                                    UpdateQuery.SetField("level", 1, ValueMode.Add);
                                    UpdateQuery.SetField("earned", TimeStamp, ValueMode.Set);
                                    UpdateQuery.Execute();
                                }
                            }

                            // Add best round count if player earned best round medal
                            if (OnWinningTeam && AwardId == 2051907)
                            {
                                // Prepare Query
                                UpdateQuery = new UpdateQueryBuilder("army", Driver);
                                UpdateQuery.AddWhere("id", Comparison.Equals, Pid);
                                UpdateQuery.SetField("brnd" + Army, 1, ValueMode.Add);
                                UpdateQuery.Execute();
                            }

                        } // End Foreach Award
                    }

                    // Increment player position
                    PlayerPosition++;
                } // End Foreach Player

                // Commit the transaction
                try
                {
                    Transaction.Commit();
                }
                catch (Exception E)
                {
                    try {
                        Transaction.Rollback();
                    }
                    catch { }

                    // Log error
                    Log("An error occured while commiting player changes: " + E.Message, LogLevel.Error);
                    return;
                }
            }
            catch(Exception E)
            {
                Log("An error occured while updating player stats: " + E.Message, LogLevel.Error);
                Transaction.Rollback();
            }

            // ********************************
            // Process ServerInfo
            // ********************************
            //Log("Processing Game Server", LogLevel.Notice);

            // ********************************
            // Process MapInfo
            // ********************************
            Log(String.Format("Processing Map Info ({0}:{1})", MapName, MapId), LogLevel.Notice);

            TimeSpan Timer = new TimeSpan(Convert.ToInt64(MapEnd - MapStart));
            Rows = Driver.Query("SELECT COUNT(id) AS count FROM mapinfo WHERE id=" + MapId);
            if(Int32.Parse(Rows[0]["count"].ToString()) == 0)
            {
                // Prepare Query
                InsertQuery = new InsertQueryBuilder("mapinfo", Driver);
                InsertQuery.SetField("id", MapId);
                InsertQuery.SetField("name", MapName);
                InsertQuery.SetField("score", MapScore);
                InsertQuery.SetField("time", Timer.Seconds);
                InsertQuery.SetField("times", 1);
                InsertQuery.SetField("kills", MapKills);
                InsertQuery.SetField("deaths", MapDeaths);
                InsertQuery.SetField("custom", (IsCustomMap) ? 1 : 0);
                InsertQuery.Execute();
            }
            else
            {
                UpdateQuery = new UpdateQueryBuilder("mapinfo", Driver);
                UpdateQuery.AddWhere("id", Comparison.Equals, MapId);
                UpdateQuery.SetField("score", MapScore, ValueMode.Add);
                UpdateQuery.SetField("time", Timer.Seconds, ValueMode.Add);
                UpdateQuery.SetField("times", 1, ValueMode.Add);
                UpdateQuery.SetField("kills", MapKills, ValueMode.Add);
                UpdateQuery.SetField("deaths", MapDeaths, ValueMode.Add);
                UpdateQuery.Execute();
            }

            // ********************************
            // Process RoundInfo
            // ********************************
            Log("Processing Round Info", LogLevel.Notice);
            InsertQuery = new InsertQueryBuilder("round_history", Driver);
            InsertQuery.SetField("timestamp", MapStart);
            InsertQuery.SetField("mapid", MapId);
            InsertQuery.SetField("time", Timer.Seconds);
            InsertQuery.SetField("team1", Team1Army);
            InsertQuery.SetField("team2", Team2Army);
            InsertQuery.SetField("tickets1", Team1Tickets);
            InsertQuery.SetField("tickets2", Team2Tickets);
            InsertQuery.SetField("pids1", Team1Players);
            InsertQuery.SetField("pids1_end", Team1PlayersEnd);
            InsertQuery.SetField("pids2", Team2Players);
            InsertQuery.SetField("pids2_end", Team2PlayersEnd);
            InsertQuery.Execute();

            // ********************************
            // Process Smoc And General Ranks
            // ********************************
            if (MainForm.Config.ASP_SmocCheck) SmocCheck();
            if (MainForm.Config.ASP_GeneralCheck) GenCheck();

            // Call our Finished Event
            Timer = new TimeSpan(Clock.ElapsedTicks);
            Log(String.Format("Snapshot ({0}) processed in {1} milliseconds", MapName, Timer.Milliseconds), LogLevel.Info);
            SnapshotProccessed();
        }
        /// <summary>
        /// Imports a players stats from the official gamespy ASP.
        /// This method is to be used in a background worker
        /// </summary>
        public void ImportEaStats(object sender, DoWorkEventArgs e)
        {
            // Setup variables
            BackgroundWorker Worker = (BackgroundWorker)sender;
            int Pid = Int32.Parse(e.Argument.ToString());

            // Make sure redirects are disabled
            if (MainForm.RedirectsEnabled)
                throw new Exception("Cant import player when Gamespy redirects are active");

            // Make sure the player doesnt exist!
            if(PlayerExists(Pid))
                throw new Exception(String.Format("Player with PID {0} already exists!", Pid));

            // Build variables
            Uri GsUrl;
            WebRequest Request;
            HttpWebResponse Response;
            List<string[]> PlayerLines;
            List<string[]> AwardLines;
            List<string[]> MapLines;
            InsertQueryBuilder Query;

            // Create Request
            string Url = String.Format(
                "getplayerinfo.aspx?pid={0}&info=per*,cmb*,twsc,cpcp,cacp,dfcp,kila,heal,rviv,rsup,rpar,tgte,dkas,dsab,cdsc,rank,cmsc,kick,kill,deth,suic,"
                + "ospm,klpm,klpr,dtpr,bksk,wdsk,bbrs,tcdr,ban,dtpm,lbtl,osaa,vrk,tsql,tsqm,tlwf,mvks,vmks,mvn*,vmr*,fkit,fmap,fveh,fwea,wtm-,wkl-,wdt-,"
                + "wac-,wkd-,vtm-,vkl-,vdt-,vkd-,vkr-,atm-,awn-,alo-,abr-,ktm-,kkl-,kdt-,kkd-",
                Pid);
            Worker.ReportProgress(1, "Requesting Player Stats");
            GsUrl = new Uri("http://bf2web.gamespy.com/ASP/" + Url);
            Request = WebRequest.Create(GsUrl);

            // Get response
            Response = (HttpWebResponse)Request.GetResponse();
            if (Response.StatusCode != HttpStatusCode.OK)
                throw new Exception("Unable to connect to the Gamespy ASP Webservice!");

            // Read response data
            Worker.ReportProgress(2, "Parsing Stats Response");
            PlayerLines = new List<string[]>();
            using (StreamReader Reader = new StreamReader(Response.GetResponseStream()))
                while(!Reader.EndOfStream)
                    PlayerLines.Add(Reader.ReadLine().Split('\t'));

            // Does the player exist?
            if (PlayerLines[0][0] != "O")
                throw new Exception("Player does not exist on the gamespy servers!");

            // Fetch player mapinfo
            Worker.ReportProgress(3, "Requesting Player Map Data");
            GsUrl = new Uri(String.Format("http://bf2web.gamespy.com/ASP/getplayerinfo.aspx?pid={0}&info=mtm-,mwn-,mls-", Pid));
            Request = WebRequest.Create(GsUrl);

            // Get response
            Response = (HttpWebResponse)Request.GetResponse();
            if (Response.StatusCode != HttpStatusCode.OK)
                throw new Exception("Unable to connect to the Gamespy ASP Webservice!");

            // Read response data
            Worker.ReportProgress(4, "Parsing Map Data Response");
            MapLines = new List<string[]>();
            using (StreamReader Reader = new StreamReader(Response.GetResponseStream()))
                while(!Reader.EndOfStream)
                    MapLines.Add(Reader.ReadLine().Split('\t'));

            // Fetch player awards
            Worker.ReportProgress(5, "Requesting Player Awards");
            GsUrl = new Uri(String.Format("http://bf2web.gamespy.com/ASP/getawardsinfo.aspx?pid={0}", Pid));
            Request = WebRequest.Create(GsUrl);

            // Get response
            Response = (HttpWebResponse)Request.GetResponse();
            if (Response.StatusCode != HttpStatusCode.OK)
                throw new Exception("Unable to connect to the Gamespy ASP Webservice!");

            // Read response data
            Worker.ReportProgress(6, "Parsing Player Awards Response");
            AwardLines = new List<string[]>();
            using (StreamReader Reader = new StreamReader(Response.GetResponseStream()))
                while(!Reader.EndOfStream)
                    AwardLines.Add(Reader.ReadLine().Split('\t'));

            // === Process Player Info === //

            // Parse Player Data
            Dictionary<string, string> PlayerInfo = StatsParser.ParseHeaderData(PlayerLines[3], PlayerLines[4]);
            int Rounds = Int32.Parse(PlayerInfo["mode0"]) + Int32.Parse(PlayerInfo["mode1"]) + Int32.Parse(PlayerInfo["mode2"]);

            // Begin database transaction
            DbTransaction Transaction = BeginTransaction();

            // Wrap all DB inserts into a try block so we can rollback on error
            try
            {
                // Insert Player Data
                Worker.ReportProgress(7, "Inserting Player Data Into Table: player");
                Query = new InsertQueryBuilder("player", this);
                Query.SetField("id", Pid);
                Query.SetField("name", " " + PlayerInfo["nick"].Trim()); // Online accounts always start with space in bf2stats
                Query.SetField("country", "xx");
                Query.SetField("time", PlayerInfo["time"]);
                Query.SetField("rounds", Rounds);
                Query.SetField("ip", "127.0.0.1");
                Query.SetField("score", PlayerInfo["scor"]);
                Query.SetField("cmdscore", PlayerInfo["cdsc"]);
                Query.SetField("skillscore", PlayerInfo["cmsc"]);
                Query.SetField("teamscore", PlayerInfo["twsc"]);
                Query.SetField("kills", PlayerInfo["kill"]);
                Query.SetField("deaths", PlayerInfo["deth"]);
                Query.SetField("captures", PlayerInfo["cpcp"]);
                Query.SetField("captureassists", PlayerInfo["cacp"]);
                Query.SetField("defends", PlayerInfo["dfcp"]);
                Query.SetField("damageassists", PlayerInfo["kila"]);
                Query.SetField("heals", PlayerInfo["heal"]);
                Query.SetField("revives", PlayerInfo["rviv"]);
                Query.SetField("ammos", PlayerInfo["rsup"]);
                Query.SetField("repairs", PlayerInfo["rpar"]);
                Query.SetField("driverspecials", PlayerInfo["dsab"]);
                Query.SetField("suicides", PlayerInfo["suic"]);
                Query.SetField("killstreak", PlayerInfo["bksk"]);
                Query.SetField("deathstreak", PlayerInfo["wdsk"]);
                Query.SetField("rank", PlayerInfo["rank"]);
                Query.SetField("banned", PlayerInfo["ban"]);
                Query.SetField("kicked", PlayerInfo["kick"]);
                Query.SetField("cmdtime", PlayerInfo["tcdr"]);
                Query.SetField("sqltime", PlayerInfo["tsql"]);
                Query.SetField("sqmtime", PlayerInfo["tsqm"]);
                Query.SetField("lwtime", PlayerInfo["tlwf"]);
                Query.SetField("wins", PlayerInfo["wins"]);
                Query.SetField("losses", PlayerInfo["loss"]);
                Query.SetField("joined", PlayerInfo["jond"]);
                Query.SetField("rndscore", PlayerInfo["bbrs"]);
                Query.SetField("lastonline", PlayerInfo["lbtl"]);
                Query.SetField("mode0", PlayerInfo["mode0"]);
                Query.SetField("mode1", PlayerInfo["mode1"]);
                Query.SetField("mode2", PlayerInfo["mode2"]);
                Query.Execute();

                // Insert Army Data
                Worker.ReportProgress(8, "Inserting Player Data Into Table: army");
                Query = new InsertQueryBuilder("army", this);
                Query.SetField("id", Pid);
                for (int i = 0; i < 10; i++)
                {
                    Query.SetField("time" + i, PlayerInfo["atm-" + i]);
                    Query.SetField("win" + i, PlayerInfo["awn-" + i]);
                    Query.SetField("loss" + i, PlayerInfo["alo-" + i]);
                    Query.SetField("best" + i, PlayerInfo["abr-" + i]);
                }
                Query.Execute();

                // Insert Kit Data
                Worker.ReportProgress(9, "Inserting Player Data Into Table: kits");
                Query = new InsertQueryBuilder("kits", this);
                Query.SetField("id", Pid);
                for (int i = 0; i < 7; i++)
                {
                    Query.SetField("time" + i, PlayerInfo["ktm-" + i]);
                    Query.SetField("kills" + i, PlayerInfo["kkl-" + i]);
                    Query.SetField("deaths" + i, PlayerInfo["kdt-" + i]);
                }
                Query.Execute();

                // Insert Vehicle Data
                Worker.ReportProgress(10, "Inserting Player Data Into Table: vehicles");
                Query = new InsertQueryBuilder("vehicles", this);
                Query.SetField("id", Pid);
                Query.SetField("timepara", 0);
                for (int i = 0; i < 7; i++)
                {
                    Query.SetField("time" + i, PlayerInfo["vtm-" + i]);
                    Query.SetField("kills" + i, PlayerInfo["vkl-" + i]);
                    Query.SetField("deaths" + i, PlayerInfo["vdt-" + i]);
                    Query.SetField("rk" + i, PlayerInfo["vkr-" + i]);
                }
                Query.Execute();

                // Insert Weapon Data
                Worker.ReportProgress(11, "Inserting Player Data Into Table: weapons");
                Query = new InsertQueryBuilder("weapons", this);
                Query.SetField("id", Pid);
                for (int i = 0; i < 9; i++)
                {
                    Query.SetField("time" + i, PlayerInfo["wtm-" + i]);
                    Query.SetField("kills" + i, PlayerInfo["wkl-" + i]);
                    Query.SetField("deaths" + i, PlayerInfo["wdt-" + i]);
                }

                // Knife
                Query.SetField("knifetime", PlayerInfo["wtm-9"]);
                Query.SetField("knifekills", PlayerInfo["wkl-9"]);
                Query.SetField("knifedeaths", PlayerInfo["wdt-9"]);
                // Shockpad
                Query.SetField("shockpadtime", PlayerInfo["wtm-10"]);
                Query.SetField("shockpadkills", PlayerInfo["wkl-10"]);
                Query.SetField("shockpaddeaths", PlayerInfo["wdt-10"]);
                // Claymore
                Query.SetField("claymoretime", PlayerInfo["wtm-11"]);
                Query.SetField("claymorekills", PlayerInfo["wkl-11"]);
                Query.SetField("claymoredeaths", PlayerInfo["wdt-11"]);
                // Handgrenade
                Query.SetField("handgrenadetime", PlayerInfo["wtm-12"]);
                Query.SetField("handgrenadekills", PlayerInfo["wkl-12"]);
                Query.SetField("handgrenadedeaths", PlayerInfo["wdt-12"]);
                // SF Weapn Data
                Query.SetField("tacticaldeployed", PlayerInfo["de-6"]);
                Query.SetField("grapplinghookdeployed", PlayerInfo["de-7"]);
                Query.SetField("ziplinedeployed", PlayerInfo["de-8"]);

                Query.Execute();

                // === Process Awards Data === //
                Worker.ReportProgress(12, "Inserting Player Awards");
                List<Dictionary<string, string>> Awards = StatsParser.ParseAwards(AwardLines);
                foreach (Dictionary<string, string> Award in Awards)
                {
                    Query = new InsertQueryBuilder("awards", this);
                    Query.SetField("id", Pid);
                    Query.SetField("awd", Award["id"]);
                    Query.SetField("level", Award["level"]);
                    Query.SetField("earned", Award["when"]);
                    Query.SetField("first", Award["first"]);
                    Query.Execute();
                }

                // === Process Map Data === //
                Worker.ReportProgress(13, "Inserting Player Map Data");
                PlayerInfo = StatsParser.ParseHeaderData(MapLines[3], MapLines[4]);
                int[] Maps = new int[] {
                    0, 1, 2, 3, 4, 5, 6, 100, 101, 102, 103, 105,
                    601, 300, 301, 302, 303, 304, 305, 306, 307,
                    10, 11, 110, 200, 201, 202, 12
                };
                foreach (int MapId in Maps)
                {
                    if (PlayerInfo.ContainsKey("mtm-" + MapId))
                    {
                        Query = new InsertQueryBuilder("maps", this);
                        Query.SetField("id", Pid);
                        Query.SetField("mapid", MapId);
                        Query.SetField("time", PlayerInfo["mtm-" + MapId]);
                        Query.SetField("win", PlayerInfo["mwn-" + MapId]);
                        Query.SetField("loss", PlayerInfo["mls-" + MapId]);
                        Query.SetField("best", 0);
                        Query.SetField("worst", 0);
                        Query.Execute();
                    }
                }

                // Commit transaction
                Transaction.Commit();
            }
            catch (Exception)
            {
                Transaction.Rollback();
                throw;
            }
            finally
            {
                // Dispose dispose the transaction
                Transaction.Dispose();
            }
        }
        /// <summary>
        /// Processes the snapshot data, inserted and updating player data in the gamespy database
        /// </summary>
        /// <exception cref="InvalidDataException">Thrown if the snapshot data is invalid</exception>
        public void ProcessData()
        {
            // Make sure we are processing the same data again
            if (this.IsProcessed)
                throw new Exception("Round data has already been processed!");

            // Begin Logging
            Log(String.Format("Begin Processing ({0})...", this.MapName), LogLevel.Notice);
            Log(String.Format((this.IsCustomMap) ? "Custom Map ({0})..." : "Standard Map ({0})...", this.MapId), LogLevel.Notice);
            Log("Found " + this.Players.Count + " Player(s)...", LogLevel.Notice);

            // Make sure we meet the minimum player requirement
            if (this.Players.Count < Program.Config.ASP_MinRoundPlayers)
            {
                Log("Minimum round Player count does not meet the ASP requirement... Aborting", LogLevel.Warning);
                throw new Exception("Minimum round Player count does not meet the ASP requirement");
            }

            // CDB update
            if (this.SnapshotMode == SnapshotMode.Minimal)
                Log("Snapshot mode set to CentralDatabase.Minimal. Rank and Award data will be ingnored", LogLevel.Notice);

            // Setup some variables
            Stopwatch Clock = Stopwatch.StartNew();
            List<Dictionary<string, object>> Rows;
            InsertQueryBuilder InsertQuery;
            UpdateQueryBuilder UpdateQuery;
            WhereClause Where;

            // Start the timer and Begin Transaction
            using (StatsDatabase Driver = new StatsDatabase())
            using (DbTransaction Transaction = Driver.BeginTransaction())
            {
                // To prevent half complete snapshots due to exceptions,
                // Put the whole thing in a try block, and rollback on error
                try
                {
                    // Loop through each player, and process them
                    foreach (Player Player in this.Players)
                    {
                        // Player meets min round time or are we ignoring AI?
                        if ((Player.RoundTime < Program.Config.ASP_MinRoundTime) || (Program.Config.ASP_IgnoreAI && Player.IsAI))
                            continue;

                        // Parse some player data
                        bool OnWinningTeam = (Player.Team == WinningTeam);
                        string CountryCode = Ip2nation.GetCountryCode(Player.IpAddress);
                        int Best, Worst;

                        // Log
                        Log(String.Format("Processing Player ({0})", Player.Pid), LogLevel.Notice);

                        // Fetch the player
                        Rows = Driver.Query("SELECT ip, country, rank, killstreak, deathstreak, rndscore FROM player WHERE id=@P0", Player.Pid);
                        if (Rows.Count == 0)
                        {
                            // === New Player === //
                            Log(String.Format("Adding NEW Player ({0})", Player.Pid), LogLevel.Notice);

                            // We dont save rank data on CentralDatabase.Minimal
                            if (this.SnapshotMode == SnapshotMode.Minimal)
                                Player.SetRank(0);

                            // Build insert data
                            InsertQuery = new InsertQueryBuilder("player", Driver);
                            InsertQuery.SetField("id", Player.Pid);
                            InsertQuery.SetField("name", Player.Name);
                            InsertQuery.SetField("country", CountryCode);
                            InsertQuery.SetField("time", Player.RoundTime);
                            InsertQuery.SetField("rounds", Player.CompletedRound);
                            InsertQuery.SetField("ip", Player.IpAddress);
                            InsertQuery.SetField("score", Player.RoundScore);
                            InsertQuery.SetField("cmdscore", Player.CommandScore);
                            InsertQuery.SetField("skillscore", Player.SkillScore);
                            InsertQuery.SetField("teamscore", Player.TeamScore);
                            InsertQuery.SetField("kills", Player.Stats.Kills);
                            InsertQuery.SetField("deaths", Player.Stats.Deaths);
                            InsertQuery.SetField("captures", Player.Stats.FlagCaptures);
                            InsertQuery.SetField("captureassists", Player.Stats.FlagCaptureAssists);
                            InsertQuery.SetField("defends", Player.Stats.FlagDefends);
                            InsertQuery.SetField("damageassists", Player.Stats.DamageAssists);
                            InsertQuery.SetField("heals", Player.Stats.Heals);
                            InsertQuery.SetField("revives", Player.Stats.Revives);
                            InsertQuery.SetField("ammos", Player.Stats.Ammos);
                            InsertQuery.SetField("repairs", Player.Stats.Repairs);
                            InsertQuery.SetField("targetassists", Player.Stats.TargetAssists);
                            InsertQuery.SetField("driverspecials", Player.Stats.DriverSpecials);
                            InsertQuery.SetField("teamkills", Player.Stats.TeamKills);
                            InsertQuery.SetField("teamdamage", Player.Stats.TeamDamage);
                            InsertQuery.SetField("teamvehicledamage", Player.Stats.TeamVehicleDamage);
                            InsertQuery.SetField("suicides", Player.Stats.Suicides);
                            InsertQuery.SetField("killstreak", Player.Stats.KillStreak);
                            InsertQuery.SetField("deathstreak", Player.Stats.DeathStreak);
                            InsertQuery.SetField("rank", Player.Rank);
                            InsertQuery.SetField("banned", Player.TimesBanned);
                            InsertQuery.SetField("kicked", Player.TimesKicked);
                            InsertQuery.SetField("cmdtime", Player.CmdTime);
                            InsertQuery.SetField("sqltime", Player.SqlTime);
                            InsertQuery.SetField("sqmtime", Player.SqmTime);
                            InsertQuery.SetField("lwtime", Player.LwTime);
                            InsertQuery.SetField("wins", OnWinningTeam);
                            InsertQuery.SetField("losses", !OnWinningTeam);
                            InsertQuery.SetField("availunlocks", 0);
                            InsertQuery.SetField("usedunlocks", 0);
                            InsertQuery.SetField("joined", this.RoundEndTime);
                            InsertQuery.SetField("rndscore", Player.RoundScore);
                            InsertQuery.SetField("lastonline", RoundEndTime);
                            InsertQuery.SetField("mode0", (GameMode == 0));
                            InsertQuery.SetField("mode1", (GameMode == 1));
                            InsertQuery.SetField("mode2", (GameMode == 2));
                            InsertQuery.SetField("isbot", Player.IsAI);

                            // Insert Player Data
                            InsertQuery.Execute();

                            // Double Check Unlocks
                            if (!Player.IsAI && Driver.ExecuteScalar<int>("SELECT COUNT(*) FROM unlocks WHERE id=@P0", Player.Pid) == 0)
                            {
                                // Create New Player Unlock Data
                                StringBuilder Q = new StringBuilder("INSERT INTO unlocks VALUES ", 350);

                                // Normal unlocks
                                for (int i = 11; i < 100; i += 11)
                                    Q.AppendFormat("({0}, {1}, 'n'), ", Player.Pid, i);

                                // Sf Unlocks
                                for (int i = 111; i < 556; i += 111)
                                {
                                    Q.AppendFormat("({0}, {1}, 'n')", Player.Pid, i);
                                    if (i != 555) Q.Append(", ");
                                }

                                // Execute query
                                Driver.Execute(Q.ToString());
                            }
                        }
                        else
                        {
                            // Existing Player
                            Log(String.Format("Updating EXISTING Player ({0})", Player.Pid), LogLevel.Notice);

                            // If rank is lower then the database rank, and the players old rank is not a special rank
                            // then we will be correct the rank here. We do this because sometimes stats are interupted
                            // while being fetched in the python (yay single threading!), and the players stats are reset
                            // during that round.
                            int DbRank = Int32.Parse(Rows[0]["rank"].ToString());
                            if (this.SnapshotMode == SnapshotMode.Minimal)
                            {
                                // On CDB mode, always use database rank
                                Player.SetRank(DbRank);
                            }
                            else if (DbRank > Player.Rank && DbRank != 11 && DbRank != 21)
                            {
                                // Fail-safe in-case rank data was not obtained and reset to '0' in-game.
                                Player.SetRank(DbRank);
                                DebugLog.Write("Rank Correction ({0}), Using database rank ({1})", Player.Pid, DbRank);
                            }

                            // Calcuate best killstreak/deathstreak
                            int KillStreak = Int32.Parse(Rows[0]["killstreak"].ToString());
                            int DeathStreak = Int32.Parse(Rows[0]["deathstreak"].ToString());
                            if (Player.Stats.KillStreak > KillStreak) KillStreak = Player.Stats.KillStreak;
                            if (Player.Stats.DeathStreak > DeathStreak) DeathStreak = Player.Stats.DeathStreak;

                            // Calculate Best Round Score
                            int Brs = Int32.Parse(Rows[0]["rndscore"].ToString());
                            if (Player.RoundScore > Brs) Brs = Player.RoundScore;

                            // Update Player Data
                            UpdateQuery = new UpdateQueryBuilder("player", Driver);
                            UpdateQuery.SetField("country", CountryCode, ValueMode.Set);
                            UpdateQuery.SetField("time", Player.RoundTime, ValueMode.Add);
                            UpdateQuery.SetField("rounds", Player.CompletedRound, ValueMode.Add);
                            UpdateQuery.SetField("ip", Player.IpAddress, ValueMode.Set);
                            UpdateQuery.SetField("score", Player.RoundScore, ValueMode.Add);
                            UpdateQuery.SetField("cmdscore", Player.CommandScore, ValueMode.Add);
                            UpdateQuery.SetField("skillscore", Player.SkillScore, ValueMode.Add);
                            UpdateQuery.SetField("teamscore", Player.TeamScore, ValueMode.Add);
                            UpdateQuery.SetField("kills", Player.Stats.Kills, ValueMode.Add);
                            UpdateQuery.SetField("deaths", Player.Stats.Deaths, ValueMode.Add);
                            UpdateQuery.SetField("captures", Player.Stats.FlagCaptures, ValueMode.Add);
                            UpdateQuery.SetField("captureassists", Player.Stats.FlagCaptureAssists, ValueMode.Add);
                            UpdateQuery.SetField("defends", Player.Stats.FlagDefends, ValueMode.Add);
                            UpdateQuery.SetField("damageassists", Player.Stats.DamageAssists, ValueMode.Add);
                            UpdateQuery.SetField("heals", Player.Stats.Heals, ValueMode.Add);
                            UpdateQuery.SetField("revives", Player.Stats.Revives, ValueMode.Add);
                            UpdateQuery.SetField("ammos", Player.Stats.Ammos, ValueMode.Add);
                            UpdateQuery.SetField("repairs", Player.Stats.Repairs, ValueMode.Add);
                            UpdateQuery.SetField("targetassists", Player.Stats.TargetAssists, ValueMode.Add);
                            UpdateQuery.SetField("driverspecials", Player.Stats.DriverSpecials, ValueMode.Add);
                            UpdateQuery.SetField("teamkills", Player.Stats.TeamKills, ValueMode.Add);
                            UpdateQuery.SetField("teamdamage", Player.Stats.TeamDamage, ValueMode.Add);
                            UpdateQuery.SetField("teamvehicledamage", Player.Stats.TeamVehicleDamage, ValueMode.Add);
                            UpdateQuery.SetField("suicides", Player.Stats.Suicides, ValueMode.Add);
                            UpdateQuery.SetField("Killstreak", KillStreak, ValueMode.Set);
                            UpdateQuery.SetField("deathstreak", DeathStreak, ValueMode.Set);
                            UpdateQuery.SetField("rank", Player.Rank, ValueMode.Set);
                            UpdateQuery.SetField("banned", Player.TimesBanned, ValueMode.Add);
                            UpdateQuery.SetField("kicked", Player.TimesKicked, ValueMode.Add);
                            UpdateQuery.SetField("cmdtime", Player.CmdTime, ValueMode.Add);
                            UpdateQuery.SetField("sqltime", Player.SqlTime, ValueMode.Add);
                            UpdateQuery.SetField("sqmtime", Player.SqmTime, ValueMode.Add);
                            UpdateQuery.SetField("lwtime", Player.LwTime, ValueMode.Add);
                            UpdateQuery.SetField("wins", OnWinningTeam, ValueMode.Add);
                            UpdateQuery.SetField("losses", !OnWinningTeam, ValueMode.Add);
                            UpdateQuery.SetField("rndscore", Brs, ValueMode.Set);
                            UpdateQuery.SetField("lastonline", this.RoundEndTime, ValueMode.Set);
                            UpdateQuery.SetField("mode0", (GameMode == 0), ValueMode.Add);
                            UpdateQuery.SetField("mode1", (GameMode == 1), ValueMode.Add);
                            UpdateQuery.SetField("mode2", (GameMode == 2), ValueMode.Add);
                            UpdateQuery.SetField("chng", (Player.Rank > DbRank), ValueMode.Set);
                            UpdateQuery.SetField("decr", (Player.Rank < DbRank), ValueMode.Set);
                            UpdateQuery.SetField("isbot", Player.IsAI, ValueMode.Set);
                            UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);
                            UpdateQuery.Execute();
                        }

                        // ********************************
                        // Insert Player history.
                        // ********************************
                        Driver.Execute(
                            "INSERT INTO player_history VALUES(@P0, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8, @P9)",
                            Player.Pid, this.RoundEndTime, Player.RoundTime, Player.RoundScore, Player.CommandScore,
                            Player.SkillScore, Player.TeamScore, Player.Stats.Kills, Player.Stats.Deaths, Player.Rank
                        );

                        // ********************************
                        // Process Player Army Data
                        // ********************************
                        Log(String.Format("Processing Army Data ({0})", Player.Pid), LogLevel.Notice);

                        // Update player army times
                        Rows = Driver.Query("SELECT * FROM army WHERE id=" + Player.Pid);
                        if (Rows.Count == 0)
                        {
                            // Build query
                            InsertQuery = new InsertQueryBuilder("army", Driver);
                            InsertQuery.SetField("id", Player.Pid);
                            for (int i = 0; i < 14; i++)
                                InsertQuery.SetField("time" + i, Player.TimeAsArmy[i]);

                            // Make sure we arent playing an unsupported army
                            if (Player.ArmyId < 14)
                            {
                                InsertQuery.SetField("win" + Player.ArmyId, OnWinningTeam);
                                InsertQuery.SetField("loss" + Player.ArmyId, !OnWinningTeam);
                                InsertQuery.SetField("score" + Player.ArmyId, Player.RoundScore);
                                InsertQuery.SetField("best" + Player.ArmyId, Player.RoundScore);
                                InsertQuery.SetField("worst" + Player.ArmyId, Player.RoundScore);
                            }

                            InsertQuery.Execute();
                        }
                        else
                        {
                            // Build query
                            UpdateQuery = new UpdateQueryBuilder("army", Driver);
                            UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);
                            for (int i = 0; i < 14; i++)
                                UpdateQuery.SetField("time" + i, Player.TimeAsArmy[i], ValueMode.Add);

                            // Prevent database errors with custom army IDs
                            if (Player.ArmyId < 14)
                            {
                                Best = Int32.Parse(Rows[0]["best" + Player.ArmyId].ToString());
                                Worst = Int32.Parse(Rows[0]["worst" + Player.ArmyId].ToString());
                                if (Player.RoundScore > Best) Best = Player.RoundScore;
                                if (Player.RoundScore < Worst) Worst = Player.RoundScore;

                                UpdateQuery.SetField("win" + Player.ArmyId, OnWinningTeam, ValueMode.Add);
                                UpdateQuery.SetField("loss" + Player.ArmyId, !OnWinningTeam, ValueMode.Add);
                                UpdateQuery.SetField("score" + Player.ArmyId, Player.RoundScore, ValueMode.Add);
                                UpdateQuery.SetField("best" + Player.ArmyId, Best, ValueMode.Set);
                                UpdateQuery.SetField("worst" + Player.ArmyId, Worst, ValueMode.Set);
                            }

                            UpdateQuery.Execute();
                        }

                        // ********************************
                        // Process Player Kills
                        // ********************************
                        Log(String.Format("Processing Kills Data ({0})", Player.Pid), LogLevel.Notice);
                        foreach (KeyValuePair<int, int> Kill in Player.Victims)
                        {
                            // Kill: VictimPid => KillCount
                            if (Driver.ExecuteScalar<int>("SELECT COUNT(*) FROM kills WHERE attacker=@P0 AND victim=@P1", Player.Pid, Kill.Key) == 0)
                            {
                                InsertQuery = new InsertQueryBuilder("kills", Driver);
                                InsertQuery.SetField("attacker", Player.Pid);
                                InsertQuery.SetField("victim", Kill.Key);
                                InsertQuery.SetField("count", Kill.Value);
                                InsertQuery.Execute();
                            }
                            else
                            {
                                UpdateQuery = new UpdateQueryBuilder("kills", Driver);
                                UpdateQuery.SetField("count", Kill.Value, ValueMode.Add);
                                Where = UpdateQuery.AddWhere("attacker", Comparison.Equals, Player.Pid);
                                Where.AddClause(LogicOperator.And, "victim", Comparison.Equals, Kill.Key);
                                UpdateQuery.Execute();
                            }
                        }

                        // ********************************
                        // Process Player Kit Data
                        // ********************************
                        Log(String.Format("Processing Kit Data ({0})", Player.Pid), LogLevel.Notice);

                        // Check for existing player data
                        if (Driver.ExecuteScalar<int>("SELECT COUNT(*) FROM kits WHERE id=" + Player.Pid) == 0)
                        {
                            InsertQuery = new InsertQueryBuilder("kits", Driver);
                            InsertQuery.SetField("id", Player.Pid);
                            for (int i = 0; i < 7; i++)
                            {
                                InsertQuery.SetField("time" + i, Player.KitData[i].Time);
                                InsertQuery.SetField("kills" + i, Player.KitData[i].Kills);
                                InsertQuery.SetField("deaths" + i, Player.KitData[i].Deaths);
                            }
                            InsertQuery.Execute();
                        }
                        else
                        {
                            UpdateQuery = new UpdateQueryBuilder("kits", Driver);
                            UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);
                            for (int i = 0; i < 7; i++)
                            {
                                UpdateQuery.SetField("time" + i, Player.KitData[i].Time, ValueMode.Add);
                                UpdateQuery.SetField("kills" + i, Player.KitData[i].Kills, ValueMode.Add);
                                UpdateQuery.SetField("deaths" + i, Player.KitData[i].Deaths, ValueMode.Add);
                            }
                            UpdateQuery.Execute();
                        }

                        // ********************************
                        // Process Player Vehicle Data
                        // ********************************
                        Log(String.Format("Processing Vehicle Data ({0})", Player.Pid), LogLevel.Notice);

                        // Check for existing player data
                        if (Driver.ExecuteScalar<int>("SELECT COUNT(*) FROM vehicles WHERE id=" + Player.Pid) == 0)
                        {
                            InsertQuery = new InsertQueryBuilder("vehicles", Driver);
                            InsertQuery.SetField("id", Player.Pid);
                            for (int i = 0; i < 7; i++)
                            {
                                InsertQuery.SetField("time" + i, Player.VehicleData[i].Time);
                                InsertQuery.SetField("kills" + i, Player.VehicleData[i].Kills);
                                InsertQuery.SetField("deaths" + i, Player.VehicleData[i].Deaths);
                                InsertQuery.SetField("rk" + i, Player.VehicleData[i].RoadKills);
                            }
                            InsertQuery.SetField("timepara", Player.VehicleData[7].Time);
                            InsertQuery.Execute();
                        }
                        else
                        {
                            UpdateQuery = new UpdateQueryBuilder("vehicles", Driver);
                            UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);
                            for (int i = 0; i < 7; i++)
                            {
                                UpdateQuery.SetField("time" + i, Player.VehicleData[i].Time, ValueMode.Add);
                                UpdateQuery.SetField("kills" + i, Player.VehicleData[i].Kills, ValueMode.Add);
                                UpdateQuery.SetField("deaths" + i, Player.VehicleData[i].Deaths, ValueMode.Add);
                                UpdateQuery.SetField("rk" + i, Player.VehicleData[i].RoadKills, ValueMode.Add);
                            }
                            UpdateQuery.SetField("timepara", Player.VehicleData[7].Time, ValueMode.Add);
                            UpdateQuery.Execute();
                        }

                        // ********************************
                        // Process Player Weapon Data
                        // ********************************
                        Log(String.Format("Processing Weapon Data ({0})", Player.Pid), LogLevel.Notice);

                        // Check for existing player data
                        if (Driver.ExecuteScalar<int>("SELECT COUNT(*) FROM weapons WHERE id=" + Player.Pid) == 0)
                        {
                            // Prepare Query
                            InsertQuery = new InsertQueryBuilder("weapons", Driver);
                            InsertQuery.SetField("id", Player.Pid);

                            // Basic Weapon Data
                            for (int i = 0; i < 15; i++)
                            {
                                if (i < 9)
                                {
                                    InsertQuery.SetField("time" + i, Player.WeaponData[i].Time);
                                    InsertQuery.SetField("kills" + i, Player.WeaponData[i].Kills);
                                    InsertQuery.SetField("deaths" + i, Player.WeaponData[i].Deaths);
                                    InsertQuery.SetField("fired" + i, Player.WeaponData[i].Fired);
                                    InsertQuery.SetField("hit" + i, Player.WeaponData[i].Hits);
                                }
                                else
                                {
                                    string Pfx = GetWeaponTblPrefix(i);
                                    InsertQuery.SetField(Pfx + "time", Player.WeaponData[i].Time);
                                    InsertQuery.SetField(Pfx + "kills", Player.WeaponData[i].Kills);
                                    InsertQuery.SetField(Pfx + "deaths", Player.WeaponData[i].Deaths);
                                    InsertQuery.SetField(Pfx + "fired", Player.WeaponData[i].Fired);
                                    InsertQuery.SetField(Pfx + "hit", Player.WeaponData[i].Hits);
                                }
                            }

                            // Tactical
                            InsertQuery.SetField("tacticaltime", Player.WeaponData[15].Time);
                            InsertQuery.SetField("tacticaldeployed", Player.WeaponData[15].Deployed);

                            // Grappling Hook
                            InsertQuery.SetField("grapplinghooktime", Player.WeaponData[16].Time);
                            InsertQuery.SetField("grapplinghookdeployed", Player.WeaponData[16].Deployed);
                            InsertQuery.SetField("grapplinghookdeaths", Player.WeaponData[16].Deaths);

                            // Zipline
                            InsertQuery.SetField("ziplinetime", Player.WeaponData[17].Time);
                            InsertQuery.SetField("ziplinedeployed", Player.WeaponData[17].Deployed);
                            InsertQuery.SetField("ziplinedeaths", Player.WeaponData[17].Deaths);

                            // Do Query
                            InsertQuery.Execute();
                        }
                        else
                        {
                            // Prepare Query
                            UpdateQuery = new UpdateQueryBuilder("weapons", Driver);
                            UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);

                            // Basic Weapon Data
                            for (int i = 0; i < 15; i++)
                            {
                                if (i < 9)
                                {
                                    UpdateQuery.SetField("time" + i, Player.WeaponData[i].Time, ValueMode.Add);
                                    UpdateQuery.SetField("kills" + i, Player.WeaponData[i].Kills, ValueMode.Add);
                                    UpdateQuery.SetField("deaths" + i, Player.WeaponData[i].Deaths, ValueMode.Add);
                                    UpdateQuery.SetField("fired" + i, Player.WeaponData[i].Fired, ValueMode.Add);
                                    UpdateQuery.SetField("hit" + i, Player.WeaponData[i].Hits, ValueMode.Add);
                                }
                                else
                                {
                                    string Pfx = GetWeaponTblPrefix(i);
                                    UpdateQuery.SetField(Pfx + "time", Player.WeaponData[i].Time, ValueMode.Add);
                                    UpdateQuery.SetField(Pfx + "kills", Player.WeaponData[i].Kills, ValueMode.Add);
                                    UpdateQuery.SetField(Pfx + "deaths", Player.WeaponData[i].Deaths, ValueMode.Add);
                                    UpdateQuery.SetField(Pfx + "fired", Player.WeaponData[i].Fired, ValueMode.Add);
                                    UpdateQuery.SetField(Pfx + "hit", Player.WeaponData[i].Hits, ValueMode.Add);
                                }
                            }

                            // Tactical
                            UpdateQuery.SetField("tacticaltime", Player.WeaponData[15].Time, ValueMode.Add);
                            UpdateQuery.SetField("tacticaldeployed", Player.WeaponData[15].Deployed, ValueMode.Add);

                            // Grappling Hook
                            UpdateQuery.SetField("grapplinghooktime", Player.WeaponData[16].Time, ValueMode.Add);
                            UpdateQuery.SetField("grapplinghookdeployed", Player.WeaponData[16].Deployed, ValueMode.Add);
                            UpdateQuery.SetField("grapplinghookdeaths", Player.WeaponData[16].Deaths, ValueMode.Add);

                            // Zipline
                            UpdateQuery.SetField("ziplinetime", Player.WeaponData[17].Time, ValueMode.Add);
                            UpdateQuery.SetField("ziplinedeployed", Player.WeaponData[17].Deployed, ValueMode.Add);
                            UpdateQuery.SetField("ziplinedeaths", Player.WeaponData[17].Deaths, ValueMode.Add);

                            // Do Query
                            UpdateQuery.Execute();
                        }

                        // ********************************
                        // Process Player Map Data
                        // ********************************
                        Log(String.Format("Processing Map Data ({0})", Player.Pid), LogLevel.Notice);

                        Rows = Driver.Query("SELECT best, worst FROM maps WHERE id=@P0 AND mapid=@P1", Player.Pid, MapId);
                        if (Rows.Count == 0)
                        {
                            // Prepare Query
                            InsertQuery = new InsertQueryBuilder("maps", Driver);
                            InsertQuery.SetField("id", Player.Pid);
                            InsertQuery.SetField("mapid", this.MapId);
                            InsertQuery.SetField("time", Player.RoundTime);
                            InsertQuery.SetField("win", OnWinningTeam);
                            InsertQuery.SetField("loss", !OnWinningTeam);
                            InsertQuery.SetField("best", Player.RoundScore);
                            InsertQuery.SetField("worst", Player.RoundScore);
                            InsertQuery.Execute();
                        }
                        else
                        {
                            // Get best and worst round scores
                            Best = Int32.Parse(Rows[0]["best"].ToString());
                            Worst = Int32.Parse(Rows[0]["worst"].ToString());
                            if (Player.RoundScore > Best) Best = Player.RoundScore;
                            if (Player.RoundScore < Worst) Worst = Player.RoundScore;

                            // Prepare Query
                            UpdateQuery = new UpdateQueryBuilder("maps", Driver);
                            Where = UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);
                            Where.AddClause(LogicOperator.And, "mapid", Comparison.Equals, this.MapId);
                            UpdateQuery.SetField("time", Player.RoundTime, ValueMode.Add);
                            UpdateQuery.SetField("win", OnWinningTeam, ValueMode.Add);
                            UpdateQuery.SetField("loss", !OnWinningTeam, ValueMode.Add);
                            UpdateQuery.SetField("best", Best, ValueMode.Set);
                            UpdateQuery.SetField("worst", Worst, ValueMode.Set);
                            UpdateQuery.Execute();
                        }

                        // Quit here on central database Min mode, since award data isnt allowed
                        if (this.SnapshotMode == SnapshotMode.Minimal) continue;

                        // ********************************
                        // Process Player Awards Data
                        // ********************************
                        Log(String.Format("Processing Award Data ({0})", Player.Pid), LogLevel.Notice);

                        // Do we require round completion for award processing?
                        if (Player.CompletedRound || !Program.Config.ASP_AwardsReqComplete)
                        {
                            // Prepare query
                            InsertQuery = new InsertQueryBuilder("awards", Driver);

                            // Add Backend awards too
                            foreach (BackendAward Award in BackendAwardData.BackendAwards)
                            {
                                int Level = 1;
                                if (Award.CriteriaMet(Player.Pid, Driver, ref Level))
                                    Player.EarnedAwards.Add(Award.AwardId, Level);
                            }

                            // Now we loop though each players earned award, and store them in the database
                            foreach (KeyValuePair<int, int> Award in Player.EarnedAwards)
                            {
                                // Get our award type. Award.Key is the ID, Award.Value is the level (or count)
                                bool IsMedal = Award.Key.InRange(2000000, 3000000);
                                bool IsBadge = (Award.Key < 2000000);

                                // Build our query
                                string Query = "SELECT COUNT(*) FROM awards WHERE id=@P0 AND awd=@P1";
                                if (IsBadge)
                                    Query += " AND level=" + Award.Value.ToString();

                                // Check for prior awarding of award
                                if (Driver.ExecuteScalar<int>(Query, Player.Pid, Award.Key) == 0)
                                {
                                    // Need to do extra work for Badges as more than one badge level may have been awarded.
                                    // The snapshot will only post the highest awarded level of a badge, so here we award
                                    // the lower level badges if the player does not have them.
                                    if (IsBadge)
                                    {
                                        // Check all prior badge levels, and make sure the player has them
                                        for (int j = 1; j < Award.Value; j++)
                                        {
                                            Query = "SELECT COUNT(*) FROM awards WHERE id=@P0 AND awd=@P1 AND level=@P2";
                                            if (Driver.ExecuteScalar<int>(Query, Player.Pid, Award.Key, j) == 0)
                                            {
                                                // Prepare Query
                                                InsertQuery.SetField("id", Player.Pid);
                                                InsertQuery.SetField("awd", Award.Key);
                                                InsertQuery.SetField("level", j);
                                                InsertQuery.SetField("earned", (this.RoundEndTime - 5) + j);
                                                InsertQuery.SetField("first", 0);
                                                InsertQuery.Execute();
                                            }
                                        }
                                    }

                                    // Add the players award
                                    InsertQuery.SetField("id", Player.Pid);
                                    InsertQuery.SetField("awd", Award.Key);
                                    InsertQuery.SetField("level", Award.Value);
                                    InsertQuery.SetField("earned", this.RoundEndTime);
                                    InsertQuery.SetField("first", ((IsMedal) ? this.RoundEndTime : 0));
                                    InsertQuery.Execute();

                                }
                                else // === Player has recived this award prior === //
                                {
                                    // Only update medals because ribbons and badges are only awarded once ever!
                                    if (IsMedal)
                                    {
                                        // Prepare Query
                                        UpdateQuery = new UpdateQueryBuilder("awards", Driver);
                                        Where = UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);
                                        Where.AddClause(LogicOperator.And, "awd", Comparison.Equals, Award.Key);
                                        UpdateQuery.SetField("level", 1, ValueMode.Add);
                                        UpdateQuery.SetField("earned", this.RoundEndTime, ValueMode.Set);
                                        UpdateQuery.Execute();
                                    }
                                }

                                // Add best round count if player earned best round medal
                                if (Award.Key == 2051907 && Player.ArmyId < 14)
                                {
                                    // Prepare Query
                                    UpdateQuery = new UpdateQueryBuilder("army", Driver);
                                    UpdateQuery.AddWhere("id", Comparison.Equals, Player.Pid);
                                    UpdateQuery.SetField("brnd" + Player.ArmyId, 1, ValueMode.Add);
                                    UpdateQuery.Execute();
                                }

                            } // End Foreach Award
                        } // End Award Processing
                    } // End Foreach Player

                    // ********************************
                    // Process ServerInfo
                    // ********************************
                    //Log("Processing Game Server", LogLevel.Notice);

                    // ********************************
                    // Process MapInfo
                    // ********************************
                    Log(String.Format("Processing Map Info ({0}:{1})", this.MapName, this.MapId), LogLevel.Notice);
                    if (Driver.ExecuteScalar<int>("SELECT COUNT(*) FROM mapinfo WHERE id=" + this.MapId) == 0)
                    {
                        // Prepare Query
                        InsertQuery = new InsertQueryBuilder("mapinfo", Driver);
                        InsertQuery.SetField("id", this.MapId);
                        InsertQuery.SetField("name", this.MapName);
                        InsertQuery.SetField("score", this.MapScore);
                        InsertQuery.SetField("time", this.RoundTime.Seconds);
                        InsertQuery.SetField("times", 1);
                        InsertQuery.SetField("kills", this.MapKills);
                        InsertQuery.SetField("deaths", this.MapDeaths);
                        InsertQuery.SetField("custom", (this.IsCustomMap) ? 1 : 0);
                        InsertQuery.Execute();
                    }
                    else
                    {
                        UpdateQuery = new UpdateQueryBuilder("mapinfo", Driver);
                        UpdateQuery.AddWhere("id", Comparison.Equals, this.MapId);
                        UpdateQuery.SetField("score", this.MapScore, ValueMode.Add);
                        UpdateQuery.SetField("time", this.RoundTime.Seconds, ValueMode.Add);
                        UpdateQuery.SetField("times", 1, ValueMode.Add);
                        UpdateQuery.SetField("kills", this.MapKills, ValueMode.Add);
                        UpdateQuery.SetField("deaths", this.MapDeaths, ValueMode.Add);
                        UpdateQuery.Execute();
                    }

                    // ********************************
                    // Process RoundInfo
                    // ********************************
                    Log("Processing Round Info", LogLevel.Notice);
                    InsertQuery = new InsertQueryBuilder("round_history", Driver);
                    InsertQuery.SetField("timestamp", this.RoundEndTime);
                    InsertQuery.SetField("mapid", this.MapId);
                    InsertQuery.SetField("time", this.RoundTime.Seconds);
                    InsertQuery.SetField("team1", this.Team1ArmyId);
                    InsertQuery.SetField("team2", this.Team2ArmyId);
                    InsertQuery.SetField("tickets1", this.Team1Tickets);
                    InsertQuery.SetField("tickets2", this.Team2Tickets);
                    InsertQuery.SetField("pids1", this.Team1Players);
                    InsertQuery.SetField("pids1_end", this.Team1PlayersEnd);
                    InsertQuery.SetField("pids2", this.Team2Players);
                    InsertQuery.SetField("pids2_end", this.Team2PlayersEnd);
                    InsertQuery.Execute();

                    // ********************************
                    // Process Smoc And General Ranks
                    // ********************************
                    if (Program.Config.ASP_SmocCheck) SmocCheck(Driver);
                    if (Program.Config.ASP_GeneralCheck) GenCheck(Driver);

                    // ********************************
                    // Commit the Transaction and Log
                    // ********************************
                    Transaction.Commit();
                    Clock.Stop();

                    // Log in the stats debug log, and call it
                    this.IsProcessed = true;
                    string logText = String.Format(
                        "Snapshot ({0}) processed in {1} milliseconds [{2} Queries]",
                        this.MapName, Clock.Elapsed.Milliseconds, Driver.NumQueries
                    );
                    Log(logText, LogLevel.Info);
                }
                catch (Exception E)
                {
                    Log("An error occured while updating player stats: " + E.Message, LogLevel.Error);
                    ExceptionHandler.GenerateExceptionLog(E);
                    Transaction.Rollback();
                    throw;
                }
            }
        }