From 6646115e8340e5b9a8e5f1f7f18e452503869242 Mon Sep 17 00:00:00 2001 From: "Liam Z. Charles" Date: Tue, 9 Jan 2024 23:15:05 +1100 Subject: [PATCH] Make time loading asynchronous --- src/ST-Events/Players.cs | 4 +- src/ST-Events/TriggerStartTouch.cs | 4 +- src/ST-Player/PlayerStats/PlayerStats.cs | 192 ++++++++++++----------- 3 files changed, 103 insertions(+), 97 deletions(-) diff --git a/src/ST-Events/Players.cs b/src/ST-Events/Players.cs index 5d442ac..b46f139 100644 --- a/src/ST-Events/Players.cs +++ b/src/ST-Events/Players.cs @@ -113,8 +113,8 @@ public HookResult OnPlayerConnect(EventPlayerConnectFull @event, GameEventInfo i // To-do: hardcoded Style value // Load MapTimes for the player's PB and their Checkpoints - playerList[player.UserId ?? 0].Stats.LoadMapTimesData(playerList[player.UserId ?? 0], DB); // Will reload PB and Checkpoints for the player for all styles - playerList[player.UserId ?? 0].Stats.LoadCheckpointsData(DB); // To-do: This really should go inside `LoadMapTimesData` imo cuz here we hardcoding load for Style 0 + Task.Run(async () => await playerList[player.UserId ?? 0].Stats.LoadMapTimesData(playerList[player.UserId ?? 0], DB)); // Will reload PB and Checkpoints for the player for all styles + Task.Run(async () => await playerList[player.UserId ?? 0].Stats.LoadCheckpointsData(DB)); // To-do: This really should go inside `LoadMapTimesData` imo cuz here we hardcoding load for Style 0 // Print join messages Server.PrintToChatAll($"{PluginPrefix} {ChatColors.Green}{player.PlayerName}{ChatColors.Default} has connected from {ChatColors.Lime}{playerList[player.UserId ?? 0].Profile.Country}{ChatColors.Default}."); diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index a5ba595..f47faff 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -87,9 +87,9 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) // Add entry in DB for the run player.Stats.ThisRun.SaveMapTime(player, DB); // Save the MapTime PB data - player.Stats.LoadMapTimesData(player, DB); // Load the MapTime PB data again (will refresh the MapTime ID for the Checkpoints query) + Task.Run(async () => await player.Stats.LoadMapTimesData(player, DB)); // Load the MapTime PB data again (will refresh the MapTime ID for the Checkpoints query) player.Stats.ThisRun.SaveCurrentRunCheckpoints(player, DB); // Save this run's checkpoints - player.Stats.LoadCheckpointsData(DB); // Reload checkpoints for the run - we should really have this in `SaveMapTime` as well but we don't re-load PB data inside there so we need to do it here + Task.Run(async () => await player.Stats.LoadCheckpointsData(DB)); // Reload checkpoints for the run - we should really have this in `SaveMapTime` as well but we don't re-load PB data inside there so we need to do it here CurrentMap.GetMapRecordAndTotals(DB); // Reload the Map record and totals for the HUD } diff --git a/src/ST-Player/PlayerStats/PlayerStats.cs b/src/ST-Player/PlayerStats/PlayerStats.cs index 65dcc39..7121eac 100644 --- a/src/ST-Player/PlayerStats/PlayerStats.cs +++ b/src/ST-Player/PlayerStats/PlayerStats.cs @@ -26,123 +26,129 @@ public PlayerStats() /// `Checkpoints` are loaded separately because inside the while loop we cannot run queries. /// This can populate all the `style` stats the player has for the map - currently only 1 style is supported /// - public void LoadMapTimesData(Player player, TimerDatabase DB, int playerId = 0, int mapId = 0) + public async Task LoadMapTimesData(Player player, TimerDatabase DB, int playerId = 0, int mapId = 0) { - Task dbTask2 = DB.Query($"SELECT mainquery.*, (SELECT COUNT(*) FROM `MapTimes` AS subquery " + - $"WHERE subquery.`map_id` = mainquery.`map_id` AND subquery.`style` = mainquery.`style` " + - $"AND subquery.`run_time` <= mainquery.`run_time`) AS `rank` FROM `MapTimes` AS mainquery " + - $"WHERE mainquery.`player_id` = {player.Profile.ID} AND mainquery.`map_id` = {player.CurrMap.ID}; "); - MySqlDataReader playerStats = dbTask2.Result; - int style = 0; // To-do: implement styles - if (!playerStats.HasRows) + await Task.Run(async () => { - Console.WriteLine($"CS2 Surf DEBUG >> internal class PlayerStats -> LoadMapTimesData -> No MapTimes data found for Player."); - } - else - { - while (playerStats.Read()) + MySqlDataReader playerStats = await DB.Query($"SELECT mainquery.*, (SELECT COUNT(*) FROM `MapTimes` AS subquery " + + $"WHERE subquery.`map_id` = mainquery.`map_id` AND subquery.`style` = mainquery.`style` " + + $"AND subquery.`run_time` <= mainquery.`run_time`) AS `rank` FROM `MapTimes` AS mainquery " + + $"WHERE mainquery.`player_id` = {player.Profile.ID} AND mainquery.`map_id` = {player.CurrMap.ID}; "); + int style = 0; // To-do: implement styles + if (!playerStats.HasRows) { - // Load data into PersonalBest object - // style = playerStats.GetInt32("style"); // Uncomment when style is implemented - PB[style].ID = playerStats.GetInt32("id"); - PB[style].StartVelX = (float)playerStats.GetDouble("start_vel_x"); - PB[style].StartVelY = (float)playerStats.GetDouble("start_vel_y"); - PB[style].StartVelZ = (float)playerStats.GetDouble("start_vel_z"); - PB[style].EndVelX = (float)playerStats.GetDouble("end_vel_x"); - PB[style].EndVelY = (float)playerStats.GetDouble("end_vel_y"); - PB[style].EndVelZ = (float)playerStats.GetDouble("end_vel_z"); - PB[style].Ticks = playerStats.GetInt32("run_time"); - PB[style].RunDate = playerStats.GetInt32("run_date"); - PB[style].Rank = playerStats.GetInt32("rank"); - - Console.WriteLine($"============== CS2 Surf DEBUG >> LoadMapTimesData -> PlayerID: {player.Profile.ID} | Rank: {PB[style].Rank} | ID: {PB[style].ID} | RunTime: {PB[style].Ticks} | SVX: {PB[style].StartVelX} | SVY: {PB[style].StartVelY} | SVZ: {PB[style].StartVelZ} | EVX: {PB[style].EndVelX} | EVY: {PB[style].EndVelY} | EVZ: {PB[style].EndVelZ} | Run Date (UNIX): {PB[style].RunDate}"); - #if DEBUG - Console.WriteLine($"CS2 Surf DEBUG >> internal class PlayerStats -> LoadMapTimesData -> PlayerStats.PB (ID {PB[style].ID}) loaded from DB."); - #endif + Console.WriteLine($"CS2 Surf DEBUG >> internal class PlayerStats -> LoadMapTimesData -> No MapTimes data found for Player."); + } + else + { + while (playerStats.Read()) + { + // Load data into PersonalBest object + // style = playerStats.GetInt32("style"); // Uncomment when style is implemented + PB[style].ID = playerStats.GetInt32("id"); + PB[style].StartVelX = (float)playerStats.GetDouble("start_vel_x"); + PB[style].StartVelY = (float)playerStats.GetDouble("start_vel_y"); + PB[style].StartVelZ = (float)playerStats.GetDouble("start_vel_z"); + PB[style].EndVelX = (float)playerStats.GetDouble("end_vel_x"); + PB[style].EndVelY = (float)playerStats.GetDouble("end_vel_y"); + PB[style].EndVelZ = (float)playerStats.GetDouble("end_vel_z"); + PB[style].Ticks = playerStats.GetInt32("run_time"); + PB[style].RunDate = playerStats.GetInt32("run_date"); + PB[style].Rank = playerStats.GetInt32("rank"); + + Console.WriteLine($"============== CS2 Surf DEBUG >> LoadMapTimesData -> PlayerID: {player.Profile.ID} | Rank: {PB[style].Rank} | ID: {PB[style].ID} | RunTime: {PB[style].Ticks} | SVX: {PB[style].StartVelX} | SVY: {PB[style].StartVelY} | SVZ: {PB[style].StartVelZ} | EVX: {PB[style].EndVelX} | EVY: {PB[style].EndVelY} | EVZ: {PB[style].EndVelZ} | Run Date (UNIX): {PB[style].RunDate}"); + #if DEBUG + Console.WriteLine($"CS2 Surf DEBUG >> internal class PlayerStats -> LoadMapTimesData -> PlayerStats.PB (ID {PB[style].ID}) loaded from DB."); + #endif + } } - } - playerStats.Close(); + + playerStats.Close(); + }); } /// /// Executes the DB query to get all the checkpoints and store them in the Checkpoint dictionary /// - public void LoadCheckpointsData(TimerDatabase DB) + public async Task LoadCheckpointsData(TimerDatabase DB) { - Task dbTask = DB.Query($"SELECT * FROM `Checkpoints` WHERE `maptime_id` = {PB[0].ID};"); - MySqlDataReader results = dbTask.Result; - if (PB[0] == null) - { - #if DEBUG - Console.WriteLine("CS2 Surf ERROR >> internal class PlayerStats -> LoadCheckpointsData -> PersonalBest object is null."); - #endif - - results.Close(); - return; - } + await Task.Run(async () => + { + MySqlDataReader results = await DB.Query($"SELECT * FROM `Checkpoints` WHERE `maptime_id` = {PB[0].ID};"); + if (PB[0] == null) + { + #if DEBUG + Console.WriteLine("CS2 Surf ERROR >> internal class PlayerStats -> LoadCheckpointsData -> PersonalBest object is null."); + #endif - if (PB[0].Checkpoint == null) - { - #if DEBUG - Console.WriteLine($"CS2 Surf DEBUG >> internal class PlayerStats -> LoadCheckpointsData -> PB Checkpoints list is not initialized."); - #endif + results.Close(); + return; + } - PB[0].Checkpoint = new Dictionary(); // Initialize if null - } + if (PB[0].Checkpoint == null) + { + #if DEBUG + Console.WriteLine($"CS2 Surf DEBUG >> internal class PlayerStats -> LoadCheckpointsData -> PB Checkpoints list is not initialized."); + #endif - #if DEBUG - Console.WriteLine($"this.Checkpoint.Count {PB[0].Checkpoint.Count} "); - Console.WriteLine($"this.ID {PB[0].ID} "); - Console.WriteLine($"this.Ticks {PB[0].Ticks} "); - Console.WriteLine($"this.RunDate {PB[0].RunDate} "); - #endif + PB[0].Checkpoint = new Dictionary(); // Initialize if null + } - if (!results.HasRows) - { #if DEBUG - Console.WriteLine($"CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> No checkpoints found for this mapTimeId {PB[0].ID}."); + Console.WriteLine($"this.Checkpoint.Count {PB[0].Checkpoint.Count} "); + Console.WriteLine($"this.ID {PB[0].ID} "); + Console.WriteLine($"this.Ticks {PB[0].Ticks} "); + Console.WriteLine($"this.RunDate {PB[0].RunDate} "); #endif - results.Close(); - return; - } + if (!results.HasRows) + { + #if DEBUG + Console.WriteLine($"CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> No checkpoints found for this mapTimeId {PB[0].ID}."); + #endif - #if DEBUG - Console.WriteLine($"======== CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> Checkpoints found for this mapTimeId"); - #endif + results.Close(); + return; + } - while (results.Read()) - { #if DEBUG - Console.WriteLine($"cp {results.GetInt32("cp")} "); - Console.WriteLine($"run_time {results.GetFloat("run_time")} "); - Console.WriteLine($"sVelX {results.GetFloat("start_vel_x")} "); - Console.WriteLine($"sVelY {results.GetFloat("start_vel_y")} "); + Console.WriteLine($"======== CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> Checkpoints found for this mapTimeId"); #endif - Checkpoint cp = new(results.GetInt32("cp"), - results.GetInt32("run_time"), // To-do: what type of value we use here? DB uses DECIMAL but `.Tick` is int??? - results.GetFloat("start_vel_x"), - results.GetFloat("start_vel_y"), - results.GetFloat("start_vel_z"), - results.GetFloat("end_vel_x"), - results.GetFloat("end_vel_y"), - results.GetFloat("end_vel_z"), - results.GetFloat("end_touch"), - results.GetInt32("attempts")); - cp.ID = results.GetInt32("cp"); - // To-do: cp.ID = calculate Rank # from DB - - PB[0].Checkpoint[cp.CP] = cp; + while (results.Read()) + { + #if DEBUG + Console.WriteLine($"cp {results.GetInt32("cp")} "); + Console.WriteLine($"run_time {results.GetFloat("run_time")} "); + Console.WriteLine($"sVelX {results.GetFloat("start_vel_x")} "); + Console.WriteLine($"sVelY {results.GetFloat("start_vel_y")} "); + #endif + + Checkpoint cp = new(results.GetInt32("cp"), + results.GetInt32("run_time"), // To-do: what type of value we use here? DB uses DECIMAL but `.Tick` is int??? + results.GetFloat("start_vel_x"), + results.GetFloat("start_vel_y"), + results.GetFloat("start_vel_z"), + results.GetFloat("end_vel_x"), + results.GetFloat("end_vel_y"), + results.GetFloat("end_vel_z"), + results.GetFloat("end_touch"), + results.GetInt32("attempts")); + cp.ID = results.GetInt32("cp"); + // To-do: cp.ID = calculate Rank # from DB + + PB[0].Checkpoint[cp.CP] = cp; + + #if DEBUG + Console.WriteLine($"======= CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> Loaded CP {cp.CP} with RunTime {cp.Ticks}."); + #endif + } + + results.Close(); #if DEBUG - Console.WriteLine($"======= CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> Loaded CP {cp.CP} with RunTime {cp.Ticks}."); + Console.WriteLine($"======= CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> Checkpoints loaded from DB. Count: {PB[0].Checkpoint.Count}"); #endif - } - results.Close(); - - #if DEBUG - Console.WriteLine($"======= CS2 Surf DEBUG >> internal class Checkpoint : PersonalBest -> LoadCheckpointsData -> Checkpoints loaded from DB. Count: {PB[0].Checkpoint.Count}"); - #endif + }); } } \ No newline at end of file