public override void HandleRequest() { int Pid = 0; int Unlock = 0; List<Dictionary<string, object>> Rows; // Setup Params if (Request.QueryString.ContainsKey("pid")) Int32.TryParse(Request.QueryString["pid"], out Pid); if (Request.QueryString.ContainsKey("id")) Int32.TryParse(Request.QueryString["id"], out Unlock); // Make sure we have valid parameters if (Pid == 0 || Unlock == 0) { 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 Rows = Database.Query("SELECT availunlocks, usedunlocks FROM player WHERE id=@P0", Pid); if (Rows.Count == 0) { Response.WriteResponseStart(false); Response.WriteHeaderLine("asof", "err"); Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Player Doesnt Exist"); Response.Send(); return; } // Update Unlock, setting its state to 's' (unlocked) Database.Execute("UPDATE unlocks SET state = 's' WHERE id = @P0 AND kit = @P1", Pid, Unlock); // Update players used and avail unlock counts Database.Execute("UPDATE player SET availunlocks = @P0, usedunlocks = @P1 WHERE id = @P2", int.Parse(Rows[0]["availunlocks"].ToString()) - 1, int.Parse(Rows[0]["usedunlocks"].ToString()) + 1, Pid ); // Send Response Response.WriteResponseStart(); Response.WriteHeaderLine("response"); Response.WriteDataLine("OK"); Response.Send(); } }
public override void HandleRequest() { // NOTE: The HttpServer will handle the DbConnectException using (Database = new StatsDatabase()) { int Pid = 0; List<Dictionary<string, object>> Rows; // Setup Params if (Client.Request.QueryString.ContainsKey("pid")) Int32.TryParse(Client.Request.QueryString["pid"], out Pid); // Fetch Player Rows = Database.Query("SELECT rank FROM player WHERE id=@P0", Pid); if (Rows.Count == 0) { Response.WriteResponseStart(false); Response.WriteFreeformLine("Player Doesnt Exist!"); Client.Response.Send(); return; } // Reset Database.Execute("UPDATE player SET chng=0, decr=0 WHERE id=@P0", Pid); Response.WriteResponseStart(); Response.WriteFreeformLine(String.Format("Cleared rank notification {0}", Pid)); Response.Send(); } }
/// <summary> /// This request provides details on a particular players rank, and /// whether or not to show the user a promotion/demotion announcement /// </summary> /// <queryParam name="pid" type="int">The unique player ID</queryParam> /// <param name="Client">The HttpClient who made the request</param> /// <param name="Driver">The Stats Database Driver. Connection errors are handled in the calling object</param> public GetRankInfo(HttpClient Client, StatsDatabase Driver) { int Pid = 0; List<Dictionary<string, object>> Rows; // Setup Params if (Client.Request.QueryString.ContainsKey("pid")) Int32.TryParse(Client.Request.QueryString["pid"], out Pid); // Fetch Player Rows = Driver.Query("SELECT rank, chng, decr FROM player WHERE id=@P0", Pid); if (Rows.Count == 0) { Client.Response.WriteResponseStart(false); Client.Response.WriteHeaderLine("asof", "err"); Client.Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Player Doesnt Exist"); Client.Response.Send(); return; } // Output status... chng set to 1 shows the Promotion Announcement, whereas decr shows the Demotion Announcement Client.Response.WriteResponseStart(); Client.Response.WriteHeaderLine("rank", "chng", "decr"); Client.Response.WriteDataLine(Rows[0]["rank"], Rows[0]["chng"], Rows[0]["decr"]); Client.Response.Send(); // Reset Driver.Execute("UPDATE player SET chng=0, decr=0 WHERE id=@P0", Pid); }
public override void HandleRequest() { // NOTE: The HttpServer will handle the DbConnectException using (Database = new StatsDatabase()) { int Pid = 0; // Setup Params if (Client.Request.QueryString.ContainsKey("pid")) Int32.TryParse(Client.Request.QueryString["pid"], out Pid); // Fetch Player var Rows = Database.Query("SELECT rank, chng, decr FROM player WHERE id=@P0", Pid); if (Rows.Count == 0) { Response.WriteResponseStart(false); Response.WriteHeaderLine("asof", "err"); Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Player Doesnt Exist"); Response.Send(); return; } // Output status... chng set to 1 shows the Promotion Announcement, whereas decr shows the Demotion Announcement Response.WriteResponseStart(); Response.WriteHeaderLine("rank", "chng", "decr"); Response.WriteDataLine(Rows[0]["rank"], Rows[0]["chng"], Rows[0]["decr"]); Response.Send(); // Reset Database.Execute("UPDATE player SET chng=0, decr=0 WHERE id=@P0", Pid); } }
/// <summary> /// This request clears all rank announcements for a specific player /// </summary> /// <queryParam name="pid" type="int">The unique player ID</queryParam> /// <param name="Client">The HttpClient who made the request</param> /// <param name="Driver">The Stats Database Driver. Connection errors are handled in the calling object</param> public RankNotification(HttpClient Client, StatsDatabase Driver) { int Pid = 0; List<Dictionary<string, object>> Rows; // Setup Params if (Client.Request.QueryString.ContainsKey("pid")) Int32.TryParse(Client.Request.QueryString["pid"], out Pid); // Fetch Player Rows = Driver.Query("SELECT rank FROM player WHERE id=@P0", Pid); if (Rows.Count == 0) { Client.Response.WriteResponseStart(false); Client.Response.WriteFreeformLine("Player Doesnt Exist!"); Client.Response.Send(); return; } // Reset Driver.Execute("UPDATE player SET chng=0, decr=0 WHERE id=@P0", Pid); Client.Response.WriteResponseStart(); Client.Response.WriteFreeformLine(String.Format("Cleared rank notification {0}", Pid)); Client.Response.Send(); }
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> /// This request creates a player with the specified Pid when called /// </summary> /// <queryParam name="pid" type="int">The unique player ID</queryParam> /// <queryParam name ="nick" type="string">Unique player nickname</queryParam> /// <param name="Client">The HttpClient who made the request</param> /// <param name="Driver">The Stats Database Driver. Connection errors are handled in the calling object</param> public CreatePlayer(HttpClient Client, StatsDatabase Driver) { int Pid; // make sure we have a valid player ID if (!Client.Request.QueryString.ContainsKey("pid") || !Int32.TryParse(Client.Request.QueryString["pid"], out Pid) || !Client.Request.QueryString.ContainsKey("nick")) { Client.Response.WriteResponseStart(false); Client.Response.WriteHeaderLine("asof", "err"); Client.Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Invalid Syntax!"); Client.Response.Send(); return; } // Fetch Player string PlayerNick = Client.Request.QueryString["nick"].Replace("%20", " "); string CC = (Client.Request.QueryString.ContainsKey("cid")) ? Client.Request.QueryString["cid"] : ""; var Rows = Driver.Query("SELECT name FROM player WHERE id=@P0 OR name=@P1", Pid, PlayerNick); if (Rows.Count > 0) { Client.Response.WriteResponseStart(false); Client.Response.WriteFreeformLine("Player already Exists!"); Client.Response.Send(); return; } // Create Player Driver.Execute( "INSERT INTO player(id, name, country, joined, isbot) VALUES(@P0, @P1, @P2, @P3, 0)", Pid, PlayerNick, CC, DateTime.UtcNow.ToUnixTimestamp() ); // Confirm Client.Response.WriteResponseStart(); Client.Response.WriteFreeformLine("Player Created Successfully!"); Client.Response.Send(); }
/// <summary> /// Reset stats button click event /// </summary> private async void ResetStatsBtn_Click(object sender, EventArgs e) { if (MessageBox.Show("Are you sure you want to reset players stats?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { try { TaskForm.Show(this, "Reset Player Stats", "Reseting Player \"" + Player["name"] + "\"'s Stats", false); await Task.Run(() => { // Delete the players using (StatsDatabase Driver = new StatsDatabase()) { // Delete old player statistics Driver.DeletePlayer(Pid, TaskForm.Progress); // Insert a new player record Driver.Execute( "INSERT INTO player(id, name, country, joined, clantag, permban, isbot) VALUES(@P0, @P1, @P2, @P3, @P4, @P5, @P6)", Pid, Player["name"], Player["country"], Player["joined"], Player["clantag"], Player["permban"], Player["isbot"] ); } }); // Reload player LoadPlayer(); Notify.Show("Player Stats Reset Successfully!", "Operation Successful", AlertType.Success); } catch (DbConnectException Ex) { HttpServer.Stop(); ExceptionForm.ShowDbConnectError(Ex); TaskForm.CloseForm(); this.Close(); return; } catch (Exception E) { // Show exception error using (ExceptionForm Form = new ExceptionForm(E, false)) { Form.Message = String.Format("Failed to reset player stats!{1}{1}Error: {0}", E.Message, Environment.NewLine); Form.ShowDialog(); } } finally { // Close task form TaskForm.CloseForm(); } } }
/// <summary> /// Reset Unlocks Button Click Event /// </summary> private void ResetUnlocksBtn_Click(object sender, EventArgs e) { try { // Create New Player Unlock Data StringBuilder Query = new StringBuilder("INSERT INTO unlocks VALUES "); // Normal unlocks for (int i = 11; i < 100; i += 11) Query.AppendFormat("({0}, {1}, 'n'), ", Pid, i); // Sf Unlocks for (int i = 111; i < 556; i += 111) { Query.AppendFormat("({0}, {1}, 'n')", Pid, i); if (i != 555) Query.Append(", "); } // Do driver queries using (StatsDatabase Driver = new StatsDatabase()) using (DbTransaction T = Driver.BeginTransaction()) { try { // Perform queries Driver.Execute("DELETE FROM unlocks WHERE id = " + Pid); Driver.Execute("UPDATE player SET usedunlocks = 0 WHERE id = " + Pid); Driver.Execute(Query.ToString()); T.Commit(); // Notify user Notify.Show("Player Unlocks Have Been Reset", "This player will be able to select his new unlocks upon logging in.", AlertType.Success); } catch { T.Rollback(); throw; } } } catch (DbConnectException Ex) { HttpServer.Stop(); ExceptionForm.ShowDbConnectError(Ex); this.Close(); } }
/// <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); }
/// <summary> /// This request provides details on a particular players rank, and /// whether or not to show the user a promotion/demotion announcement /// </summary> /// <queryParam name="nick" type="string">The unique player's Name</queryParam> /// <queryParam name="ai" type="int">Defines whether the player is a bot (used by bf2server)</queryParam> /// <queryParam name="playerlist" type="int">Defines whether to list the players who's nick is similair to the Nick param</queryParam> /// <param name="Client">The HttpClient who made the request</param> /// <param name="Driver">The Stats Database Driver. Connection errors are handled in the calling object</param> public GetPlayerID(HttpClient Client, StatsDatabase Driver) { // Setup Variables List<Dictionary<string, object>> Rows; Dictionary<string, string> QueryString = Client.Request.QueryString; // Querystring vars int IsAI = 0; int ListPlayers = 0; string PlayerNick = ""; // Setup Params if (QueryString.ContainsKey("nick")) PlayerNick = QueryString["nick"].Replace("%20", " "); if (QueryString.ContainsKey("ai")) Int32.TryParse(QueryString["ai"], out IsAI); if (QueryString.ContainsKey("playerlist")) Int32.TryParse(QueryString["playerlist"], out ListPlayers); // Handle Request if (!String.IsNullOrWhiteSpace(PlayerNick)) { int Pid; // Create player if they donot exist Rows = Driver.Query("SELECT id FROM player WHERE name = @P0 LIMIT 1", PlayerNick); if (Rows.Count == 0) { // Grab new Player ID Pid = LowestPid--; // Create New Player Unlock Data StringBuilder Query = new StringBuilder("INSERT INTO unlocks VALUES "); // Normal unlocks for (int i = 11; i < 100; i += 11) Query.AppendFormat("({0}, {1}, 'n'), ", Pid, i); // Sf Unlocks for (int i = 111; i < 556; i += 111) { Query.AppendFormat("({0}, {1}, 'n')", Pid, i); if (i != 555) Query.Append(", "); } // Create Player Driver.Execute( "INSERT INTO player(id, name, joined, isbot) VALUES(@P0, @P1, @P2, @P3)", Pid, PlayerNick, DateTime.UtcNow.ToUnixTimestamp(), IsAI ); // Create player unlocks Driver.Execute(Query.ToString()); } else Pid = Int32.Parse(Rows[0]["id"].ToString()); // Send Response Client.Response.WriteResponseStart(); Client.Response.WriteHeaderLine("pid"); Client.Response.WriteDataLine(Pid); } else if (ListPlayers != 0) { // Prepare Response Client.Response.WriteResponseStart(); Client.Response.WriteHeaderLine("pid"); // Fetch Players Rows = Driver.Query("SELECT id FROM player WHERE ip <> '127.0.0.1'"); foreach (Dictionary<string, object> Player in Rows) Client.Response.WriteDataLine(Player["id"]); } else { Client.Response.WriteResponseStart(false); Client.Response.WriteHeaderLine("asof", "err"); Client.Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Invalid Syntax!"); } // Send Response Client.Response.Send(); }
/// <summary> /// Initializes a new Snapshot, with the specified Date it was Posted /// </summary> /// <param name="Snapshot">The snapshot source</param> /// <param name="Date">The original date in which this snapshot was created</param> public Snapshot(string Snapshot, DateTime Date, StatsDatabase Database) { // Load out database connection this.Driver = Database; this.Date = Date; this.TimeStamp = Date.ToUnixTimestamp(); // Get our snapshot key value pairs string[] Data = Snapshot.Split('\\'); Snapshot = null; // Check for invalid snapshot string. All snapshots have at least 36 data pairs, // and has an Even number of data sectors. We must also have an "End of File" Sector if (Data.Length < 36 || Data.Length % 2 != 0 || !Data.Contains("EOF")) { IsValid = false; return; } // Define if we are central update. the "cdb_update" variable must be the LAST sector in snapshot this.IsCentralUpdate = (Data[Data.Length - 2] == "cdb_update" && Data[Data.Length - 1] == "1"); // Server data this.ServerPrefix = Data[0]; this.ServerName = Data[1]; this.ServerPort = int.Parse(Data[3].ToString()); this.QueryPort = int.Parse(Data[5].ToString()); // Map Data this.MapName = Data[7]; this.MapId = int.Parse(Data[9]); this.MapStart = (int)Convert.ToDouble(Data[11], CultureInfo.InvariantCulture.NumberFormat); this.MapEnd = (int)Convert.ToDouble(Data[13], CultureInfo.InvariantCulture.NumberFormat); // Misc Data this.WinningTeam = int.Parse(Data[15]); this.GameMode = int.Parse(Data[17]); this.Mod = Data[21]; this.PlayersConnected = int.Parse(Data[23]); // Army Data this.WinningArmy = int.Parse(Data[25]); this.Team1Army = int.Parse(Data[27]); this.Team1Tickets = int.Parse(Data[29]); this.Team2Army = int.Parse(Data[31]); this.Team2Tickets = int.Parse(Data[33]); // Setup snapshot variables PlayerData = new Dictionary<int, Dictionary<string, string>>(); KillData = new Dictionary<int, Dictionary<string, string>>(); // Check for custom map, with no ID if (MapId == 99) { IsCustomMap = true; // Check for existing map data List<Dictionary<string, object>> Rows = Driver.Query("SELECT id FROM mapinfo WHERE name=@P0", 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 Rows = Driver.Query("SELECT COALESCE(MAX(id), 699) AS id FROM mapinfo WHERE id BETWEEN 700 AND 1000"); MapId = Int32.Parse(Rows[0]["id"].ToString()) + 1; // Insert map data, so we dont lose this mapid we generated Driver.Execute("INSERT INTO mapinfo(id, name) VALUES (@P0, @P1)", MapId, MapName); } else MapId = Int32.Parse(Rows[0]["id"].ToString()); } else IsCustomMap = (MapId >= 700); // Do player snapshots, sector 36 is first player for (int i = 36; i < Data.Length; i += 2) { // Format: "DataKey_PlayerId". PlayerId is not the PID, but rather // the player INDEX string[] Parts = Data[i].Split('_'); // Ignore uncomplete snapshots if (Parts.Length == 1) { // Unless we are at the end of file, IF there is no PID // Given for an item, the snapshot is invalid! if (Parts[0] == "EOF") break; else IsValid = false; return; } // If the item key is "pID", then we have a new player record int id = int.Parse(Parts[1]); if (Parts[0] == "pID") { PlayerData.Add(id, new Dictionary<string, string>()); KillData.Add(id, new Dictionary<string, string>()); } // Kill and death data has its own array key if (Parts[0] == "mvks") continue; if (Parts[0] == "mvns") KillData[id].Add(Data[i + 1], Data[i + 3]); else PlayerData[id].Add(Parts[0], Data[i + 1]); } // Set that we appear to be valid IsValid = true; }
/// <summary> /// Checks the rank tenure, and assigns a new General /// </summary> private void GenCheck(StatsDatabase Driver) { Log("Processing GENERAL Rank", LogLevel.Notice); // Vars List<Dictionary<string, object>> Rows; List<Dictionary<string, object>> Players; // Fetch all Sergeant Major's, Order by Score Players = Driver.Query("SELECT id, score FROM player WHERE rank=20 OR rank=21 ORDER BY score DESC LIMIT 1"); if (Players.Count == 1) { int Id = Int32.Parse(Players[0]["id"].ToString()); // Check for currently awarded Smoc Rows = Driver.Query("SELECT id, earned FROM awards WHERE awd=6666667 LIMIT 1"); if (Rows.Count > 0) { // Check for same and determine if minimum tenure servred int MinTenure = Program.Config.ASP_SpecialRankTenure * 86400; int Sid = Int32.Parse(Rows[0]["id"].ToString()); int Earned = Int32.Parse(Rows[0]["earned"].ToString()); // Assign new Smoc If the old SMOC's tenure is up, and the current SMOC is not the highest scoring SGM if (Id != Sid && (Earned + MinTenure) < this.RoundEndTime) { // Delete the GENERAL award Driver.Execute("DELETE FROM awards WHERE id=@P0 AND awd=6666667", Sid); // Change current GENERAL rank back to 3 Star Gen Driver.Execute("UPDATE player SET rank=20, chng=0, decr=1 WHERE id =" + Sid); // Award new GENERAL award Driver.Execute("INSERT INTO awards(id,awd,earned) VALUES(@P0,@P1,@P2)", Id, 6666667, this.RoundEndTime); // Update new GENERAL's rank Driver.Execute("UPDATE player SET rank=21, chng=1, decr=0 WHERE id =" + Id); } } else { // Award GENERAL award Driver.Execute("INSERT INTO awards(id,awd,earned) VALUES(@P0,@P1,@P2)", Id, 6666667, this.RoundEndTime); // Update GENERAL rank Driver.Execute("UPDATE player SET rank=21, chng=1, decr=0 WHERE id =" + Id); } } }
/// <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; } } }
public override void HandleRequest() { // Setup Variables List<Dictionary<string, object>> Rows; Dictionary<string, string> QueryString = Request.QueryString; // Querystring vars int IsAI = 0; int ListPlayers = 0; string PlayerNick = ""; // Setup Params if (QueryString.ContainsKey("nick")) PlayerNick = Uri.UnescapeDataString(QueryString["nick"].Replace("%20", " ")); if (QueryString.ContainsKey("ai")) Int32.TryParse(QueryString["ai"], out IsAI); if (QueryString.ContainsKey("playerlist")) Int32.TryParse(QueryString["playerlist"], out ListPlayers); // NOTE: The HttpServer will handle the DbConnectException using (Database = new StatsDatabase()) { // Handle Request if (!String.IsNullOrWhiteSpace(PlayerNick)) { int Pid; // Create player if they donot exist Rows = Database.Query("SELECT id FROM player WHERE name = @P0 LIMIT 1", PlayerNick); if (Rows.Count == 0) { // Grab new Player ID using thread safe methods Pid = (IsAI > 0) ? StatsManager.GenerateNewAIPid() : StatsManager.GenerateNewPlayerPid(); // Create Player Database.Execute( "INSERT INTO player(id, name, joined, isbot) VALUES(@P0, @P1, @P2, @P3)", Pid, PlayerNick, DateTime.UtcNow.ToUnixTimestamp(), IsAI ); } else Pid = Int32.Parse(Rows[0]["id"].ToString()); // Send Response Response.WriteResponseStart(); Response.WriteHeaderLine("pid"); Response.WriteDataLine(Pid); } else if (ListPlayers != 0) { // Prepare Response Response.WriteResponseStart(); Response.WriteHeaderLine("pid"); // Fetch Players Rows = Database.Query("SELECT id FROM player WHERE isbot=0 LIMIT 1000"); foreach (Dictionary<string, object> Player in Rows) Response.WriteDataLine(Player["id"]); } else { Response.WriteResponseStart(false); Response.WriteHeaderLine("asof", "err"); Response.WriteDataLine(DateTime.UtcNow.ToUnixTimestamp(), "Invalid Syntax!"); } // Send Response Response.Send(); } }
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> /// Imports ASP created BAK files (Mysql Out FILE) /// </summary> private void ImportASPBtn_Click(object sender, EventArgs e) { // Open File Select Dialog FolderSelectDialog Dialog = new FolderSelectDialog(); Dialog.Title = "Select ASP Database Backup Folder"; Dialog.InitialDirectory = Path.Combine(Paths.DocumentsFolder, "Backups"); if (Dialog.ShowDialog()) { // Get files list from path string path = Dialog.SelectedPath; string[] BakFiles = Directory.GetFiles(path, "*.bak"); if (BakFiles.Length > 0) { // Open the database connection StatsDatabase Database; try { Database = new StatsDatabase(); } catch (Exception Ex) { MessageBox.Show( "Unable to connect to database\r\n\r\nMessage: " + Ex.Message, "Database Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); // Stop the ASP server, and close this form ASP.ASPServer.Stop(); this.Close(); return; } // Show task dialog TaskForm.Show(this, "Importing Stats", "Importing ASP Stats Bak Files...", false); TaskForm.UpdateStatus("Removing old stats data"); // Clear old database records Database.Truncate(); Thread.Sleep(500); // Begin transaction DbTransaction Transaction = Database.BeginTransaction(); // import each table foreach (string file in BakFiles) { // Get table name string table = Path.GetFileNameWithoutExtension(file); // Update progress TaskForm.UpdateStatus("Processing stats table: " + table); // Import table data try { // Sqlite kinda sucks... no import methods if (Database.DatabaseEngine == DatabaseEngine.Sqlite) { string[] Lines = File.ReadAllLines(file); foreach (string line in Lines) { string[] Values = line.Split('\t'); Database.Execute( String.Format("INSERT INTO {0} VALUES({1})", table, "\"" + String.Join("\", \"", Values) + "\"") ); } } else Database.Execute(String.Format("LOAD DATA LOCAL INFILE '{0}' INTO TABLE {1};", file.Replace('\\', '/'), table)); } catch (Exception Ex) { // Show exception error ExceptionForm Form = new ExceptionForm(Ex, false); Form.Message = String.Format("Failed to import data into table {0}!{2}{2}Error: {1}", table, Ex.Message, Environment.NewLine); DialogResult Result = Form.ShowDialog(); // Rollback! TaskForm.UpdateStatus("Rolling back stats data"); Transaction.Rollback(); // Update message TaskForm.CloseForm(); return; } } // Commit the transaction, and alert the user Transaction.Commit(); TaskForm.CloseForm(); Notify.Show("Stats imported successfully!", "Operation Successful", AlertType.Success); // Displose Connection Database.Dispose(); } else { // Alert the user and tell them they failed MessageBox.Show( "Unable to locate any .bak files in this folder. Please select an ASP created database backup folder that contains backup files.", "Backup Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); } } }
/// <summary> /// This method imports a list of .Bak files into the database /// </summary> /// <param name="BakFiles">A list of Backfiles to import into the database</param> /// <param name="Database">The opened database connection</param> private void ImportFromBakup(string[] BakFiles, StatsDatabase Database) { // Clear old database records TaskForm.Progress.Report(new TaskProgressUpdate("Removing old stats data")); Database.Truncate(); // Let the database update itself Thread.Sleep(500); // Begin transaction using (DbTransaction Transaction = Database.BeginTransaction()) { // import each table foreach (string file in BakFiles) { // Get table name string table = Path.GetFileNameWithoutExtension(file); TaskForm.Progress.Report(new TaskProgressUpdate("Processing stats table: " + table)); // Import table data try { // Sqlite kinda sucks... no import methods if (Database.DatabaseEngine == DatabaseEngine.Sqlite) { string[] Lines = File.ReadAllLines(file); foreach (string line in Lines) { string[] Values = line.Split('\t'); Database.Execute( String.Format("INSERT INTO {0} VALUES({1})", table, "\"" + String.Join("\", \"", Values) + "\"") ); } } else Database.Execute(String.Format("LOAD DATA LOCAL INFILE '{0}' INTO TABLE {1};", file.Replace('\\', '/'), table)); } catch (Exception Ex) { // Show exception error using (ExceptionForm Form = new ExceptionForm(Ex, false)) { Form.Message = String.Format("Failed to import data into table {0}!{2}{2}Error: {1}", table, Ex.Message, Environment.NewLine); DialogResult Result = Form.ShowDialog(); // Rollback! TaskForm.Progress.Report(new TaskProgressUpdate("Rolling back stats data")); Transaction.Rollback(); return; } } } // Commit the transaction Transaction.Commit(); } }