/// <summary> /// Returns a bool stating whether the criteria for this award is met for a givin player /// </summary> /// <param name="Pid">The player ID</param> /// <param name="Level">The award level if the criteria is met</param> /// <returns></returns> public bool CriteriaMet(int Pid, StatsDatabase Driver, ref int Level) { // Get the award count (or level for badges) for this award string Query = "SELECT COALESCE(max(level), 0) FROM awards WHERE id=@P0 AND awd=@P1"; int AwardCount = Convert.ToInt32(Driver.ExecuteScalar(Query, Pid, AwardId)); bool IsRibbon = (AwardId > 3000000); // Can only recieve ribbons once in a lifetime, so return false if we have it already if (IsRibbon && AwardCount > 0) return false; // Medals and Badges can receive multiple times (Badges are award level, not count) if (!IsRibbon) Level = AwardCount + 1; // Loop through each criteria and see if we have met the criteria foreach (AwardCriteria Criteria in Criterias) { // Build the query. We always use a count() or sum() to return a sortof bool. string Where = Criteria.Where.Replace("###", Level.ToString()); Query = String.Format("SELECT {0} FROM {1} WHERE id={2} AND {3}", Criteria.Field, Criteria.Table, Pid, Where); // If we dont meet the expected result, the criteria is unmet, no use continuing if (Convert.ToInt32(Driver.ExecuteScalar(Query)) < Criteria.ExpectedResult) return false; } return true; }
public override void HandleRequest() { int Pid; // make sure we have a valid player ID if (!Request.QueryString.ContainsKey("pid") || !Int32.TryParse(Request.QueryString["pid"], out Pid) || !Request.QueryString.ContainsKey("nick")) { Response.WriteResponseStart(false); Response.WriteHeaderLine("asof", "err"); Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Invalid Syntax!"); Response.Send(); return; } // NOTE: The HttpServer will handle the DbConnectException using (Database = new StatsDatabase()) { // Fetch Player string PlayerNick = Request.QueryString["nick"].Replace("%20", " "); string CC = (Request.QueryString.ContainsKey("cid")) ? Client.Request.QueryString["cid"] : ""; if (Database.ExecuteScalar<int>("SELECT COUNT(*) FROM player WHERE id=@P0 OR name=@P1", Pid, PlayerNick) > 0) { Response.WriteResponseStart(false); Response.WriteFreeformLine("Player already Exists!"); Response.Send(); return; } // Create Player Database.Execute( "INSERT INTO player(id, name, country, joined, isbot) VALUES(@P0, @P1, @P2, @P3, 0)", Pid, PlayerNick, CC, DateTime.UtcNow.ToUnixTimestamp() ); // Confirm Response.WriteResponseStart(); Response.WriteFreeformLine("Player Created Successfully!"); Response.Send(); } }
/// <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); }
public override void HandleRequest(MvcRoute Route) { // Get our POST'ed parameters Dictionary<string, string> postParams = Client.Request.GetFormUrlEncodedPostVars(); // If we have a search value, run it if (postParams.ContainsKey("searchvalue")) { // NOTE: The HttpServer will handle the DbConnectException using (StatsDatabase Database = new StatsDatabase()) { Model.SearchValue = postParams["searchvalue"].Replace("+", " "); List<Dictionary<string, object>> Rows; // Do processing if (Validator.IsNumeric(Model.SearchValue)) { if (Validator.IsValidPID(Model.SearchValue)) { // If player PID exists, redirect there bool exists = Database.ExecuteScalar<bool>("SELECT COUNT(id) FROM player WHERE id=@P0", Model.SearchValue); if (exists) { Client.Response.Redirect("/bf2stats/player/" + Model.SearchValue); return; } } Rows = Database.Query( "SELECT id, name, score, time, country, rank, lastonline, kills, deaths FROM player WHERE id LIKE @P0 LIMIT 50", "%" + Model.SearchValue + "%" ); } else { // Check to see if player with this name exists Rows = Database.Query("SELECT id FROM player WHERE name=@P0 LIMIT 1", Model.SearchValue); if (Rows.Count > 0) { Client.Response.Redirect("/bf2stats/player/" + Rows[0]["id"]); return; } Rows = Database.Query( "SELECT id, name, score, time, country, rank, lastonline, kills, deaths FROM player WHERE name LIKE @P0 LIMIT 50", Model.SearchValue ); } // Loop through each result, and process foreach (Dictionary<string, object> Row in Rows) { // DO Kill Death Ratio double Kills = Int32.Parse(Row["kills"].ToString()); double Deaths = Int32.Parse(Row["deaths"].ToString()); double Kdr = (Deaths > 0) ? Math.Round(Kills / Deaths, 3) : Kills; // Get Score Per Min double Score = Int32.Parse(Row["score"].ToString()); double Mins = Int32.Parse(Row["time"].ToString()) / 60; double SPM = (Mins > 0) ? Math.Round(Score / Mins, 4) : Score; // Add Result Model.SearchResults.Add(new PlayerResult { Pid = Int32.Parse(Row["id"].ToString()), Name = Row["name"].ToString(), Score = (int)Score, Rank = Int32.Parse(Row["rank"].ToString()), TimePlayed = FormatTime(Int32.Parse(Row["time"].ToString())), LastOnline = FormatDate(Int32.Parse(Row["lastonline"].ToString())), Country = Row["country"].ToString().ToUpperInvariant(), Kdr = Kdr, Spm = SPM }); } } } // Send response base.SendTemplateResponse("search", typeof(SearchModel), Model); }
/// <summary> /// Start the ASP listener, and Connects to the stats database /// </summary> public static void Start() { // Can't start if we are already running! if (IsRunning) return; // === Try to connect to the database using (StatsDatabase Database = new StatsDatabase()) { if (!Database.TablesExist) { string message = "In order to use the Private Stats feature of this program, we need to setup a database. " + "You may choose to do this later by clicking \"Cancel\". Would you like to setup the database now?"; DialogResult R = MessageBox.Show(message, "Stats Database Setup", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (R == DialogResult.Yes) SetupManager.ShowDatabaseSetupForm(DatabaseMode.Stats, MainForm.Instance); // Call the stopped event to Re-enable the main form's buttons Stopped(null, EventArgs.Empty); return; } // Initialize the stats manager StatsManager.Load(Database); // Drop the SQLite ip2nation country tables (old table versions) if (Database.DatabaseEngine == DatabaseEngine.Sqlite) { string query = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='ip2nation'"; if (Database.ExecuteScalar<bool>(query)) // 0 count converts to false { Database.Execute("DROP TABLE IF EXISTS 'ip2nation';"); Database.Execute("DROP TABLE IF EXISTS 'ip2nationcountries';"); Database.Execute("VACUUM;"); } } } // === Compile our templates string path = Path.Combine(Program.RootPath, "Web", "Bf2Stats", "Views"); foreach (string file in Directory.EnumerateFiles(path, "*.cshtml")) { // If this template file is loaded already, then skip string fileName = Path.GetFileName(file); if (Engine.Razor.IsTemplateCached(fileName, ModelType)) continue; // Open the file, and compile it try { using (FileStream stream = File.OpenRead(file)) using (StreamReader reader = new StreamReader(stream)) Engine.Razor.Compile(reader.ReadToEnd(), fileName, ModelType); } catch (TemplateCompilationException e) { // Show the Exception form so the user can view DialogResult Res = ExceptionForm.ShowTemplateError(e, file); // If the user clicked "Quit", we stop if (Res == DialogResult.Abort) return; } } // === Load XML stats and awards files Bf2StatsData.Load(); BackendAwardData.BuildAwardData(); // Start the Listener and accept new connections try { Listener.Start(); Listener.BeginGetContext(HandleRequest, Listener); } catch (ObjectDisposedException) { // If we are disposed (happens when port 80 was in use already before, and we tried to start) // Then we need to start over with a new Listener CreateHttpListener(); Listener.Start(); Listener.BeginGetContext(HandleRequest, Listener); } // Fire Startup Event Started(null, EventArgs.Empty); }
public override void HandleRequest() { // Get player ID if (Request.QueryString.ContainsKey("pid")) Int32.TryParse(Request.QueryString["pid"], out Pid); // Prepare Output Response.WriteResponseStart(); Response.WriteHeaderLine("pid", "nick", "asof"); // Our ourput changes based on the selected Unlocks config setting switch (Program.Config.ASP_UnlocksMode) { // Player Based - Unlocks are earned case 0: // NOTE: The HttpServer will handle the DbConnectException using (Database = new StatsDatabase()) { // Make sure the player exists Rows = Database.Query("SELECT name, score, rank, availunlocks, usedunlocks FROM player WHERE id=@P0", Pid); if (Rows.Count == 0) goto case 2; // No Unlocks // Start Output Response.WriteDataLine(Pid, Rows[0]["name"].ToString().Trim(), DateTime.UtcNow.ToUnixTimestamp()); // Get total number of unlocks player is allowed to have givin his rank, and bonus unlocks Rank = Int32.Parse(Rows[0]["rank"].ToString()); int HasUsed = Int32.Parse(Rows[0]["usedunlocks"].ToString()); int Available = Int32.Parse(Rows[0]["availunlocks"].ToString()); int Earned = GetBonusUnlocks(); // Determine total unlocks available, based on what he has earned, minus what he has used already int Used = Database.ExecuteScalar<int>("SELECT COUNT(*) FROM unlocks WHERE id=@P0 AND state='s'", Pid); Earned -= Used; // Update database if the database is off if (Earned != Available || HasUsed != Used) Database.Execute("UPDATE player SET availunlocks=@P0, usedunlocks=@P1 WHERE id=@P2", Earned, Used, Pid); // Output more Response.WriteHeaderLine("enlisted", "officer"); Response.WriteDataLine(Earned, 0); Response.WriteHeaderLine("id", "state"); // Add each unlock's state Rows = Database.Query("SELECT kit, state FROM unlocks WHERE id=@P0 ORDER BY kit ASC", Pid); if (Rows.Count == 0) { // Create Player Unlock Data StringBuilder Query = new StringBuilder("INSERT INTO unlocks VALUES ", 350); // Normal unlocks for (int i = 11; i < 100; i += 11) { // 88 and above are Special Forces unlocks, and wont display at all if the base unlocks are not earned if (i < 78) Response.WriteDataLine(i, "n"); Query.AppendFormat("({0}, {1}, 'n'), ", Pid, i); } // Sf Unlocks, Dont display these because thats how Gamespy does it for (int i = 111; i < 556; i += 111) { Query.AppendFormat("({0}, {1}, 'n')", Pid, i); if (i != 555) Query.Append(", "); } // Do Insert Database.Execute(Query.ToString()); } else { Dictionary<string, bool> Unlocks = new Dictionary<string, bool>(7); foreach (Dictionary<string, object> Unlock in Rows) { // Add unlock to output if its a base unlock int Id = Int32.Parse(Unlock["kit"].ToString()); if (Id < 78) Response.WriteDataLine(Unlock["kit"], Unlock["state"]); // Add Unlock to list Unlocks.Add(Unlock["kit"].ToString(), (Unlock["state"].ToString() == "s")); } // Add SF Unlocks... We need the base class unlock unlocked first CheckUnlock(88, 22, Unlocks); CheckUnlock(99, 33, Unlocks); CheckUnlock(111, 44, Unlocks); CheckUnlock(222, 55, Unlocks); CheckUnlock(333, 66, Unlocks); CheckUnlock(444, 11, Unlocks); CheckUnlock(555, 77, Unlocks); } } break; // All Unlocked case 1: Response.WriteDataLine(Pid, "All_Unlocks", DateTime.UtcNow.ToUnixTimestamp()); Response.WriteHeaderLine("enlisted", "officer"); Response.WriteDataLine(0, 0); Response.WriteHeaderLine("id", "state"); for (int i = 11; i < 100; i += 11) Response.WriteDataLine(i, "s"); for (int i = 111; i < 556; i += 111) Response.WriteDataLine(i, "s"); break; // Unlocks Disabled case 2: default: Response.WriteDataLine(Pid, "No_Unlocks", DateTime.UtcNow.ToUnixTimestamp()); Response.WriteHeaderLine("enlisted", "officer"); Response.WriteDataLine(0, 0); Response.WriteHeaderLine("id", "state"); for (int i = 11; i < 78; i += 11) Response.WriteDataLine(i, "n"); break; } // Send Response Response.Send(); }
/// <summary> /// Initializes a new Snapshot, with the specified Date it was Posted /// </summary> /// <param name="Snapshot">The snapshot source</param> public Snapshot(string Snapshot, IPAddress ServerIp = null) { // Set some internal variables this.ServerIp = ServerIp ?? IPAddress.Loopback; this.Players = new List<Player>(); this.DataString = Snapshot.Trim(); string[] Data = DataString.Split('\\'); Snapshot = null; // Check for invalid snapshot string. All snapshots have at least 36 data pairs, // and has an Even number of data sectors. if (Data.Length < 36 || Data.Length % 2 != 0) throw new InvalidDataException("Snapshot does not contain at least 36 elements, or contains an odd number of elements"); // Assign server name and prefix this.ServerPrefix = Data[0]; this.ServerName = Data[1]; // Determine if we are central update. the "cdb_update" variable must be the LAST sector in snapshot int Mode; if (Data[Data.Length - 2] == "cdb_update" && Int32.TryParse(Data[Data.Length - 1], out Mode) && Mode.InRange(1, 2)) { this.SnapshotMode = (Mode == 2) ? SnapshotMode.Minimal : SnapshotMode.FullSync; } // Setup our data dictionary's. We use NiceDictionary so we can easily determine missing keys in the log file NiceDictionary<string, string> StandardData = new NiceDictionary<string, string>(16); NiceDictionary<string, string> PlayerData = new NiceDictionary<string, string>(); Dictionary<int, int> KillData = new Dictionary<int, int>(); // Wrap parsing Key/Value snapshot data in a try block! try { // Convert our standard data into key => value pairs for (int i = 2; i < Data.Length; i += 2) { // Format: "DataKey_PlayerIndex". PlayerIndex is NOT the Player Id string[] Parts = Data[i].Split('_'); if (Parts.Length == 1) { // Add to the Standard keys StandardData.Add(Data[i], Data[i + 1]); // Are we at the End of File? If so stop here if (Parts[0] == "EOF") { // Make sure to save that last players stats!! if (PlayerData.Count != 0) { AddPlayer(new Player(PlayerData, KillData)); PlayerData = null; KillData = null; } break; } } // If the item key is "pID", then we have a new player record else if (Parts[0] == "pID") { // If we have data, complete this player and start anew if (PlayerData.Count != 0) { AddPlayer(new Player(PlayerData, KillData)); PlayerData.Clear(); KillData.Clear(); } // Add new PID PlayerData.Add(Parts[0], Data[i + 1]); } else if (Parts[0] == "mvks") // Skip mvks... kill data only needs processed once (mvns) continue; else if (Parts[0] == "mvns") // Player kill data KillData.Add(Int32.Parse(Data[i + 1]), Int32.Parse(Data[i + 3])); else PlayerData.Add(Parts[0], Data[i + 1]); } } catch (Exception e) { throw new InvalidDataException("Error assigning Key => value pairs. See InnerException", e); } // Make sure we have a completed snapshot if (!StandardData.ContainsKey("EOF")) throw new InvalidDataException("No End of File element was found, Snapshot assumed to be incomplete."); // Try and set internal GameResult variables try { // Server data this.ServerPort = Int32.Parse(StandardData["gameport"]); this.QueryPort = Int32.Parse(StandardData["queryport"]); // Map Data this.MapName = StandardData["mapname"]; this.MapId = Int32.Parse(StandardData["mapid"]); this.RoundStartTime = (int)Convert.ToDouble(StandardData["mapstart"], CultureInfo.InvariantCulture.NumberFormat); this.RoundEndTime = (int)Convert.ToDouble(StandardData["mapend"], CultureInfo.InvariantCulture.NumberFormat); // Misc Data this.GameMode = Int32.Parse(StandardData["gm"]); this.Mod = StandardData["v"]; // bf2 mod replaced the version key, since we dont care the version anyways this.PlayersConnected = Int32.Parse(StandardData["pc"]); // Army Data... There is no RWA key if there was no winner... this.WinningTeam = Int32.Parse(StandardData["win"]); // Temp this.WinningArmyId = (StandardData.ContainsKey("rwa")) ? Int32.Parse(StandardData["rwa"]) : -1; this.Team1ArmyId = Int32.Parse(StandardData["ra1"]); this.Team1Tickets = Int32.Parse(StandardData["rs1"]); this.Team2ArmyId = Int32.Parse(StandardData["ra2"]); this.Team2Tickets = Int32.Parse(StandardData["rs2"]); } catch (Exception e) { throw new InvalidDataException("Error assigning GameResult variables. See InnerException", e); } // Wrap this in a try-catch block, because we want to be able to view GameResult // data, even if the database is offline try { // Dispose when done! using (StatsDatabase Driver = new StatsDatabase()) { // Check for custom map, with no ID (Not defined in Constants.py) if (this.MapId == 99) { // Check for existing map data var Rows = Driver.Query("SELECT id FROM mapinfo WHERE name=@P0", this.MapName); if (Rows.Count == 0) { // Create new MapId. Id's 700 - 1000 are reserved for unknown maps in the Constants.py file // There should never be more then 300 unknown map id's, considering 1001 is the start of KNOWN // Custom mod map id's. If we are at 1000 now, then we are in trouble :S this.MapId = Driver.ExecuteScalar<int>("SELECT COALESCE(MAX(id), 699) FROM mapinfo WHERE id BETWEEN 700 AND 999") + 1; if (this.MapId == 1000) throw new Exception("Maximum unknown custom mapid has been reached. Please add this map's mapid to the Constants.py"); // Insert map data, so we dont lose this mapid we generated Driver.Execute("INSERT INTO mapinfo(id, name, custom) VALUES (@P0, @P1, @P2)", this.MapId, this.MapName, 1); } else this.MapId = Int32.Parse(Rows[0]["id"].ToString()); } // Set whether or not this data is already been processed. The OR condition is because i goofed in early updates // and set the timestamp to the RoundStart instead of the RoundEnd like i should have this.IsProcessed = Driver.ExecuteScalar<int>( "SELECT COUNT(*) FROM round_history WHERE mapid=@P0 AND time=@P1 AND (timestamp=@P2 OR timestamp=@P3)", this.MapId, this.RoundTime.Seconds, this.RoundEndTime, this.RoundStartTime ) > 0; } } catch(DbConnectException) { this.IsProcessed = false; } // Indicate whether we are a custom map this.IsCustomMap = (this.MapId >= 700 || this.MapId == 99); }
/// <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; } } }