From d56c3c47c69969f4835a8aa8e874cc18f1e4ac9a Mon Sep 17 00:00:00 2001 From: Shachar Date: Tue, 16 Jan 2024 20:31:42 +0200 Subject: [PATCH 01/17] Fixed bot noclipin when no replay --- src/ST-Player/Replay/ReplayPlayer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ST-Player/Replay/ReplayPlayer.cs b/src/ST-Player/Replay/ReplayPlayer.cs index 8fe9436..5d7e529 100644 --- a/src/ST-Player/Replay/ReplayPlayer.cs +++ b/src/ST-Player/Replay/ReplayPlayer.cs @@ -46,7 +46,6 @@ public void Start() return; this.IsPlaying = true; - this.Controller.Pawn.Value!.MoveType = MoveType_t.MOVETYPE_NOCLIP; } public void Stop() @@ -111,11 +110,11 @@ public void LoadReplayData(TimerDatabase DB, Map current_map) string json = Compressor.Decompress(Encoding.UTF8.GetString((byte[])mapTimeReplay[0])); this.Frames = JsonSerializer.Deserialize>(json, options)!; } + FormatBotName(current_map); } mapTimeReplay.Close(); dbTask.Dispose(); - FormatBotName(current_map); } private void FormatBotName(Map current_map) From aceeaf1eba2bb608e33794b9fb6e548d2677e0f0 Mon Sep 17 00:00:00 2001 From: Shachar Date: Wed, 17 Jan 2024 15:46:21 +0200 Subject: [PATCH 02/17] Added saveloc/tele support --- src/ST-Commands/PlayerCommands.cs | 124 +++++++++++++++++++++++++- src/ST-Events/TriggerStartTouch.cs | 26 +++--- src/ST-Player/Player.cs | 4 + src/ST-Player/Saveloc/SavelocFrame.cs | 11 +++ 4 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 src/ST-Player/Saveloc/SavelocFrame.cs diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index a480ae1..59c22e2 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -84,7 +84,6 @@ public void PlayerGoToStage(CCSPlayerController? player, CommandInfo command) player.PrintToChat($"{PluginPrefix} {ChatColors.Red}Invalid stage provided. Usage: {ChatColors.Green}!s "); } - // Test command [ConsoleCommand("css_spec", "Moves a player automaticlly into spectator mode")] public void MovePlayerToSpectator(CCSPlayerController? player, CommandInfo command) { @@ -94,6 +93,11 @@ public void MovePlayerToSpectator(CCSPlayerController? player, CommandInfo comma player.ChangeTeam(CsTeam.Spectator); } + /* + ######################### + Reaplay Commands + ######################### + */ [ConsoleCommand("css_replaybotpause", "Pause the replay bot playback")] [ConsoleCommand("css_rbpause", "Pause the replay bot playback")] public void PauseReplay(CCSPlayerController? player, CommandInfo command) @@ -121,4 +125,122 @@ public void ReverseReplay(CCSPlayerController? player, CommandInfo command) CurrentMap.ReplayBot.FrameTickIncrement *= -1; } + + /* + ######################## + Saveloc Commands + ######################## + */ + [ConsoleCommand("css_saveloc", "Save current player location to be practiced")] + public void SavePlayerLocation(CCSPlayerController? player, CommandInfo command) + { + if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + return; + + Player p = playerList[player.UserId ?? 0]; + if (!p.Timer.IsRunning) + { + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Cannot save location while not in run"); + return; + } + + var player_pos = p.Controller.Pawn.Value!.AbsOrigin!; + var player_angle = p.Controller.PlayerPawn.Value!.EyeAngles; + var player_velocity = p.Controller.PlayerPawn.Value!.AbsVelocity; + + p.SavedLocations.Add(new SavelocFrame { + Pos = new Vector(player_pos.X, player_pos.Y, player_pos.Z), + Ang = new QAngle(player_angle.X, player_angle.Y, player_angle.Z), + Vel = new Vector(player_velocity.X, player_velocity.Y, player_velocity.Z), + Tick = p.Timer.Ticks + }); + p.CurrentSavedLocation = p.SavedLocations.Count-1; + + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Green}Saved location! {ChatColors.Default} use !tele #{p.SavedLocations.Count-1} to teleport to this location"); + } + + [ConsoleCommand("css_tele", "Save current player location to be practiced")] + public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo command) + { + if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + return; + + Player p = playerList[player.UserId ?? 0]; + + if(p.SavedLocations.Count == 0) + { + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}No saved locations"); + return; + } + + if (!p.Timer.IsPracticeMode) + { + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Timer now on practice"); + p.Timer.IsPracticeMode = true; + } + + SavelocFrame location = p.SavedLocations[p.CurrentSavedLocation]; + Server.NextFrame(() => { + p.Controller.PlayerPawn.Value!.Teleport(location.Pos, location.Ang, location.Vel); + p.Timer.Ticks = location.Tick; + }); + + p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}"); + } + + [ConsoleCommand("css_teleprev", "Save current player location to be practiced")] + public void TeleportPlayerLocationPrev(CCSPlayerController? player, CommandInfo command) + { + if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + return; + + Player p = playerList[player.UserId ?? 0]; + + if(p.SavedLocations.Count == 0) + { + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}No saved locations"); + return; + } + + if(p.CurrentSavedLocation == 0) + { + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Already at first location"); + } + else + { + p.CurrentSavedLocation--; + } + + TeleportPlayerLocation(player, command); + + p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}"); + } + + [ConsoleCommand("css_telenext", "Save current player location to be practiced")] + public void TeleportPlayerLocationNext(CCSPlayerController? player, CommandInfo command) + { + if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) + return; + + Player p = playerList[player.UserId ?? 0]; + + if(p.SavedLocations.Count == 0) + { + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}No saved locations"); + return; + } + + if(p.CurrentSavedLocation == p.SavedLocations.Count-1) + { + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Already at last location"); + } + else + { + p.CurrentSavedLocation++; + } + + TeleportPlayerLocation(player, command); + + p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}"); + } } \ No newline at end of file diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index 2f7633c..d1ffb2f 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -90,18 +90,20 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) #endif // 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) - 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 - CurrentMap.GetMapRecordAndTotals(DB); // Reload the Map record and totals for the HUD - - // Replay - Add end buffer for replay - AddTimer(1.5f, () => player.ReplayRecorder.SaveReplayData(player, DB)); - AddTimer(2f, () => { - CurrentMap.ReplayBot.LoadReplayData(DB!, CurrentMap); - CurrentMap.ReplayBot.ResetReplay(); - }); + if(!player.Timer.IsPracticeMode) { + 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) + 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 + CurrentMap.GetMapRecordAndTotals(DB); // Reload the Map record and totals for the HUD + + // Replay - Add end buffer for replay + AddTimer(1.5f, () => player.ReplayRecorder.SaveReplayData(player, DB)); + AddTimer(2f, () => { + CurrentMap.ReplayBot.LoadReplayData(DB!, CurrentMap); + CurrentMap.ReplayBot.ResetReplay(); + }); + } } #if DEBUG diff --git a/src/ST-Player/Player.cs b/src/ST-Player/Player.cs index 059d1cc..af124e5 100644 --- a/src/ST-Player/Player.cs +++ b/src/ST-Player/Player.cs @@ -12,6 +12,8 @@ internal class Player public PlayerStats Stats {get; set;} public PlayerHUD HUD {get; set;} public ReplayRecorder ReplayRecorder { get; set; } + public List SavedLocations { get; set; } + public int CurrentSavedLocation { get; set; } // Player information public PlayerProfile Profile {get; set;} @@ -30,6 +32,8 @@ public Player(CCSPlayerController Controller, CCSPlayer_MovementServices Movemen this.Timer = new PlayerTimer(); this.Stats = new PlayerStats(); this.ReplayRecorder = new ReplayRecorder(); + this.SavedLocations = new List(); + CurrentSavedLocation = 0; this.HUD = new PlayerHUD(this); this.CurrMap = CurrMap; diff --git a/src/ST-Player/Saveloc/SavelocFrame.cs b/src/ST-Player/Saveloc/SavelocFrame.cs new file mode 100644 index 0000000..a6bcd4c --- /dev/null +++ b/src/ST-Player/Saveloc/SavelocFrame.cs @@ -0,0 +1,11 @@ +using CounterStrikeSharp.API.Modules.Utils; + +namespace SurfTimer; + +internal class SavelocFrame +{ + public Vector Pos { get; set; } = new Vector(0, 0, 0); + public QAngle Ang { get; set; } = new QAngle(0, 0, 0); + public Vector Vel { get; set; } = new Vector(0, 0, 0); + public int Tick { get; set; } = 0; +} From 83285310bc3e35ca9e4a1692740ab9b24557c23c Mon Sep 17 00:00:00 2001 From: Shachar Date: Wed, 17 Jan 2024 16:01:05 +0200 Subject: [PATCH 03/17] Added practice mark to messages when in practice mode --- src/ST-Commands/PlayerCommands.cs | 6 +++--- src/ST-Events/TriggerEndTouch.cs | 4 ++++ src/ST-Events/TriggerStartTouch.cs | 10 +++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index 59c22e2..e4b5675 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -159,7 +159,7 @@ public void SavePlayerLocation(CCSPlayerController? player, CommandInfo command) p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Green}Saved location! {ChatColors.Default} use !tele #{p.SavedLocations.Count-1} to teleport to this location"); } - [ConsoleCommand("css_tele", "Save current player location to be practiced")] + [ConsoleCommand("css_tele", "Teleport player to current saved location")] public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo command) { if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) @@ -188,7 +188,7 @@ public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo comm p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}"); } - [ConsoleCommand("css_teleprev", "Save current player location to be practiced")] + [ConsoleCommand("css_teleprev", "Teleport player to previous saved location")] public void TeleportPlayerLocationPrev(CCSPlayerController? player, CommandInfo command) { if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) @@ -216,7 +216,7 @@ public void TeleportPlayerLocationPrev(CCSPlayerController? player, CommandInfo p.Controller.PrintToChat($"{PluginPrefix} Teleported #{p.CurrentSavedLocation}"); } - [ConsoleCommand("css_telenext", "Save current player location to be practiced")] + [ConsoleCommand("css_telenext", "Teleport player to next saved location")] public void TeleportPlayerLocationNext(CCSPlayerController? player, CommandInfo command) { if(player == null || !player.PawnIsAlive || !playerList.ContainsKey(player.UserId ?? 0)) diff --git a/src/ST-Events/TriggerEndTouch.cs b/src/ST-Events/TriggerEndTouch.cs index b330ee3..a41d4af 100644 --- a/src/ST-Events/TriggerEndTouch.cs +++ b/src/ST-Events/TriggerEndTouch.cs @@ -51,6 +51,10 @@ internal HookResult OnTriggerEndTouch(DynamicHook handler) // MAP START ZONE player.Timer.Start(); + // string PracticeString = ""; + // if (player.Timer.IsPracticeMode) + // PracticeString = $"({ChatColors.Grey}Practice{ChatColors.Default}) "; + /* Revisit // Wonky Prespeed check // To-do: make the teleportation a bit more elegant (method in a class or something) diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index d1ffb2f..3e266c0 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -57,18 +57,22 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) player.Stats.ThisRun.EndVelY = velocity_y; // End pre speed for the run player.Stats.ThisRun.EndVelZ = velocity_z; // End pre speed for the run + string PracticeString = ""; + if (player.Timer.IsPracticeMode) + PracticeString = $"({ChatColors.Grey}Practice{ChatColors.Default}) "; + // To-do: make Style (currently 0) be dynamic if (player.Stats.PB[style].Ticks <= 0) // Player first ever PersonalBest for the map { - Server.PrintToChatAll($"{PluginPrefix} {player.Controller.PlayerName} finished the map in {ChatColors.Gold}{PlayerHUD.FormatTime(player.Timer.Ticks)}{ChatColors.Default} ({player.Timer.Ticks})!"); + Server.PrintToChatAll($"{PluginPrefix} {PracticeString}{player.Controller.PlayerName} finished the map in {ChatColors.Gold}{PlayerHUD.FormatTime(player.Timer.Ticks)}{ChatColors.Default} ({player.Timer.Ticks})!"); } else if (player.Timer.Ticks < player.Stats.PB[style].Ticks) // Player beating their existing PersonalBest for the map { - Server.PrintToChatAll($"{PluginPrefix} {ChatColors.Lime}{player.Profile.Name}{ChatColors.Default} beat their PB in {ChatColors.Gold}{PlayerHUD.FormatTime(player.Timer.Ticks)}{ChatColors.Default} (Old: {ChatColors.BlueGrey}{PlayerHUD.FormatTime(player.Stats.PB[style].Ticks)}{ChatColors.Default})!"); + Server.PrintToChatAll($"{PluginPrefix} {PracticeString}{ChatColors.Lime}{player.Profile.Name}{ChatColors.Default} beat their PB in {ChatColors.Gold}{PlayerHUD.FormatTime(player.Timer.Ticks)}{ChatColors.Default} (Old: {ChatColors.BlueGrey}{PlayerHUD.FormatTime(player.Stats.PB[style].Ticks)}{ChatColors.Default})!"); } else // Player did not beat their existing PersonalBest for the map { - player.Controller.PrintToChat($"{PluginPrefix} You finished the map in {ChatColors.Yellow}{PlayerHUD.FormatTime(player.Timer.Ticks)}{ChatColors.Default}!"); + player.Controller.PrintToChat($"{PluginPrefix} {PracticeString}You finished the map in {ChatColors.Yellow}{PlayerHUD.FormatTime(player.Timer.Ticks)}{ChatColors.Default}!"); return HookResult.Continue; // Exit here so we don't write to DB } From f8f536f372db9130f7203e229f8a63a92768c1ad Mon Sep 17 00:00:00 2001 From: Shachar Date: Wed, 17 Jan 2024 16:16:01 +0200 Subject: [PATCH 04/17] Now even when time not running, When tele it will start playing in practice mode --- src/ST-Commands/PlayerCommands.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index e4b5675..c15bebb 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -173,6 +173,9 @@ public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo comm return; } + if(!p.Timer.IsRunning) + p.Timer.Start(); + if (!p.Timer.IsPracticeMode) { p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Red}Timer now on practice"); From caab5e836a4350e035f2be66053a144fd2f70302 Mon Sep 17 00:00:00 2001 From: Shachar Date: Wed, 17 Jan 2024 16:26:26 +0200 Subject: [PATCH 05/17] Forgot to add the tele args lol, Fixed now --- src/ST-Commands/PlayerCommands.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index c15bebb..0bcd1c3 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -156,7 +156,7 @@ public void SavePlayerLocation(CCSPlayerController? player, CommandInfo command) }); p.CurrentSavedLocation = p.SavedLocations.Count-1; - p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Green}Saved location! {ChatColors.Default} use !tele #{p.SavedLocations.Count-1} to teleport to this location"); + p.Controller.PrintToChat($"{PluginPrefix} {ChatColors.Green}Saved location! {ChatColors.Default} use !tele {p.SavedLocations.Count-1} to teleport to this location"); } [ConsoleCommand("css_tele", "Teleport player to current saved location")] @@ -182,6 +182,8 @@ public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo comm p.Timer.IsPracticeMode = true; } + if(command.ArgCount > 1) + p.CurrentSavedLocation = int.Parse(command.ArgByIndex(1)); SavelocFrame location = p.SavedLocations[p.CurrentSavedLocation]; Server.NextFrame(() => { p.Controller.PlayerPawn.Value!.Teleport(location.Pos, location.Ang, location.Vel); From 08e57cb34f89e61256abd930179e4af4d57a0828 Mon Sep 17 00:00:00 2001 From: Shachar Date: Thu, 18 Jan 2024 17:59:23 +0200 Subject: [PATCH 06/17] Fixed error when !tele larger than amount of teles or not a number --- src/ST-Commands/PlayerCommands.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index 0bcd1c3..b10397a 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -183,7 +183,13 @@ public void TeleportPlayerLocation(CCSPlayerController? player, CommandInfo comm } if(command.ArgCount > 1) - p.CurrentSavedLocation = int.Parse(command.ArgByIndex(1)); + try + { + int tele_n = int.Parse(command.ArgByIndex(1)); + if (tele_n < p.SavedLocations.Count) + p.CurrentSavedLocation = tele_n; + } + catch { } SavelocFrame location = p.SavedLocations[p.CurrentSavedLocation]; Server.NextFrame(() => { p.Controller.PlayerPawn.Value!.Teleport(location.Pos, location.Ang, location.Vel); From c1724f59903d512ec30cc4341c184f94aa270fec Mon Sep 17 00:00:00 2001 From: Shachar Date: Thu, 18 Jan 2024 18:08:05 +0200 Subject: [PATCH 07/17] Fixed Map stages counting. why wasn't this fix already :( --- src/ST-Commands/PlayerCommands.cs | 2 +- src/ST-Map/Map.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index b10397a..f646197 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -47,7 +47,7 @@ public void PlayerGoToStage(CCSPlayerController? player, CommandInfo command) return; int stage = Int32.Parse(command.ArgByIndex(1)) - 1; - if (stage > CurrentMap.Stages - 1) + if (stage > CurrentMap.Stages - 1 && CurrentMap.Stages > 0) stage = CurrentMap.Stages - 1; // Must be 1 argument diff --git a/src/ST-Map/Map.cs b/src/ST-Map/Map.cs index 272de00..01e36a3 100644 --- a/src/ST-Map/Map.cs +++ b/src/ST-Map/Map.cs @@ -150,6 +150,7 @@ internal Map(string Name, TimerDatabase DB) } } } + if (this.Stages > 0) this.Stages++; // You did not count the stages right :( Console.WriteLine($"[CS2 Surf] Identifying start zone: {this.StartZone.X},{this.StartZone.Y},{this.StartZone.Z}\nIdentifying end zone: {this.EndZone.X},{this.EndZone.Y},{this.EndZone.Z}"); // Gather map information OR create entry From 39aa8959da3c867b70a2d3fe6133eeef447d4fd5 Mon Sep 17 00:00:00 2001 From: Shachar Date: Thu, 18 Jan 2024 20:57:56 +0200 Subject: [PATCH 08/17] Added support for more than one replay bot + plugin no longer relies on Will's version of cs2fixes --- cfg/SurfTimer/server_settings.cfg | 2 +- src/ST-Commands/PlayerCommands.cs | 28 ++++++++++++---------- src/ST-Events/Players.cs | 36 +++++++++++++++++----------- src/ST-Events/Tick.cs | 6 +++-- src/ST-Events/TriggerStartTouch.cs | 4 ++-- src/ST-Map/Map.cs | 17 ++++++++++++- src/ST-Player/PlayerHUD.cs | 8 ++++--- src/ST-Player/Replay/ReplayPlayer.cs | 20 +++++++++++----- src/ST-UTILS/ConVar.cs | 15 ++++++++++++ src/SurfTimer.cs | 5 ++++ 10 files changed, 100 insertions(+), 41 deletions(-) create mode 100644 src/ST-UTILS/ConVar.cs diff --git a/cfg/SurfTimer/server_settings.cfg b/cfg/SurfTimer/server_settings.cfg index 074b2f0..65f5b6f 100644 --- a/cfg/SurfTimer/server_settings.cfg +++ b/cfg/SurfTimer/server_settings.cfg @@ -30,7 +30,7 @@ sv_staminalandcost 0 sv_timebetweenducks 0 // Some replay bot shit (took so fucking long to debug) -bot_quota 1 // This is gonna be used to change the amount of bots allowed (per stages/bonuses/etc) when stages/bonuses/etc added +// bot_quota 1 No nned for this, because the server handles it bot_quota_mode "normal" bot_join_after_player 1 bot_join_team CT diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index f646197..18bcd4c 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -102,28 +102,32 @@ Reaplay Commands [ConsoleCommand("css_rbpause", "Pause the replay bot playback")] public void PauseReplay(CCSPlayerController? player, CommandInfo command) { - if (player == null - || player.Team != CsTeam.Spectator - || CurrentMap.ReplayBot.Controller == null - || !CurrentMap.ReplayBot.IsPlaying - || CurrentMap.ReplayBot.Controller.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + if(player == null || player.Team != CsTeam.Spectator) return; - CurrentMap.ReplayBot.Pause(); + foreach(ReplayPlayer rb in CurrentMap.ReplayBots) + { + if(!rb.IsPlayable || !rb.IsPlaying || rb.Controller!.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + continue; + + rb.Pause(); + } } [ConsoleCommand("css_replaybotflip", "Flips the replay bot between Forward/Backward playback")] [ConsoleCommand("css_rbflip", "Flips the replay bot between Forward/Backward playback")] public void ReverseReplay(CCSPlayerController? player, CommandInfo command) { - if (player == null - || player.Team != CsTeam.Spectator - || CurrentMap.ReplayBot.Controller == null - || !CurrentMap.ReplayBot.IsPlaying - || CurrentMap.ReplayBot.Controller.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + if(player == null || player.Team != CsTeam.Spectator) return; - CurrentMap.ReplayBot.FrameTickIncrement *= -1; + foreach(ReplayPlayer rb in CurrentMap.ReplayBots) + { + if(!rb.IsPlayable || !rb.IsPlaying || rb.Controller!.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + continue; + + rb.FrameTickIncrement *= -1; + } } /* diff --git a/src/ST-Events/Players.cs b/src/ST-Events/Players.cs index 3d7e17b..f59670c 100644 --- a/src/ST-Events/Players.cs +++ b/src/ST-Events/Players.cs @@ -16,19 +16,26 @@ public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) if(!controller.IsValid) return HookResult.Continue; - if (controller.IsBot && CurrentMap.ReplayBot.Controller == null) + if(controller.IsBot) { - CurrentMap.ReplayBot.Controller = controller; - // CurrentMap.ReplayBot.Controller.PlayerName = $"[REPLAY] {CurrentMap.Name}"; - - Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); // WHY COLORS NOT WORKING AHHHHH!!!!! - AddTimer(2f, () => { - CurrentMap.ReplayBot.Controller.RemoveWeapons(); - - CurrentMap.ReplayBot.LoadReplayData(DB!, CurrentMap); - - CurrentMap.ReplayBot.Start(); - }); + for (int i = 0; i < CurrentMap.ReplayBots.Count; i++) + { + if(!CurrentMap.ReplayBots[i].IsPlayable) + { + // Currently only for Map + if(i == 0) { + CurrentMap.ReplayBots[0].SetController(controller); + Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); // WHY COLORS NOT WORKING AHHHHH!!!!! + AddTimer(2f, () => { + CurrentMap.ReplayBots[0].Controller!.RemoveWeapons(); + + CurrentMap.ReplayBots[0].LoadReplayData(DB!, CurrentMap); + + CurrentMap.ReplayBots[0].Start(); + }); + } + } + } } return HookResult.Continue; @@ -157,8 +164,9 @@ public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo { var player = @event.Userid; - if (CurrentMap.ReplayBot.Controller != null&& CurrentMap.ReplayBot.Controller.Equals(player)) - CurrentMap.ReplayBot.Reset(); + for (int i = 0; i < CurrentMap.ReplayBots.Count; i++) + if (CurrentMap.ReplayBots[i].IsPlayable && CurrentMap.ReplayBots[i].Controller!.Equals(player)) + CurrentMap.ReplayBots[i].Reset(); if (player.IsBot || !player.IsValid) { diff --git a/src/ST-Events/Tick.cs b/src/ST-Events/Tick.cs index a7140cc..51eaadd 100644 --- a/src/ST-Events/Tick.cs +++ b/src/ST-Events/Tick.cs @@ -14,7 +14,9 @@ public void OnTick() player.HUD.Display(); } - // Replay BOT Ticks - CurrentMap?.ReplayBot.Tick(); // When CurrentMap null the ? operator will terminate safely the operation + for(int i = 0; i < CurrentMap?.ReplayBots.Count; i++) + { + CurrentMap?.ReplayBots[i].Tick(); + } } } \ No newline at end of file diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index 3e266c0..08f6112 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -104,8 +104,8 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) // Replay - Add end buffer for replay AddTimer(1.5f, () => player.ReplayRecorder.SaveReplayData(player, DB)); AddTimer(2f, () => { - CurrentMap.ReplayBot.LoadReplayData(DB!, CurrentMap); - CurrentMap.ReplayBot.ResetReplay(); + CurrentMap.ReplayBots[0].LoadReplayData(DB!, CurrentMap); + CurrentMap.ReplayBots[0].ResetReplay(); }); } } diff --git a/src/ST-Map/Map.cs b/src/ST-Map/Map.cs index 01e36a3..cc4c20a 100644 --- a/src/ST-Map/Map.cs +++ b/src/ST-Map/Map.cs @@ -21,7 +21,7 @@ internal class Map public int LastPlayed {get; set;} = 0; public int TotalCompletions {get; set;} = 0; public Dictionary WR { get; set; } = new Dictionary(); - public ReplayPlayer ReplayBot { get; set; } = new ReplayPlayer(); + public List ReplayBots { get; set; } = new List { new ReplayPlayer() }; // Zone Origin Information // Map start/end zones @@ -216,6 +216,21 @@ internal Map(string Name, TimerDatabase DB) // Initiates getting the World Records for the map GetMapRecordAndTotals(DB); // To-do: Implement styles + + int bot_quota = 1; + if(this.Stages > 0) + { + this.ReplayBots.Add(new ReplayPlayer()); + bot_quota++; + } + if(this.Bonuses > 0) + { + this.ReplayBots.Add(new ReplayPlayer()); + bot_quota++; + } + Server.NextFrame(() => { + Server.ExecuteCommand($"bot_quota {bot_quota}"); + }); } public bool IsInZone(Vector zoneOrigin, float zoneCollisionRadius, Vector spawnOrigin) diff --git a/src/ST-Player/PlayerHUD.cs b/src/ST-Player/PlayerHUD.cs index dab8070..42edb24 100644 --- a/src/ST-Player/PlayerHUD.cs +++ b/src/ST-Player/PlayerHUD.cs @@ -102,12 +102,14 @@ public void Display() } else if (_player.Controller.Team == CsTeam.Spectator) { - if (_player.CurrMap.ReplayBot.Controller?.Pawn.SerialNum == _player.Controller.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + for (int i = 0; i < _player.CurrMap.ReplayBots.Count; i++) { - // Replay HUD Modules + if (_player.CurrMap.ReplayBots[i].Controller?.Pawn.SerialNum != _player.Controller.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + continue; + string replayModule = $"{FormatHUDElementHTML("", "REPLAY", "red", "large")}"; string nameModule = FormatHUDElementHTML($"{_player.CurrMap.WR[_player.Timer.Style].Name}", $"{FormatTime(_player.CurrMap.WR[_player.Timer.Style].Ticks)}", "#ffd500"); - string elapsed_ticks = FormatHUDElementHTML("Tick", $"{_player.CurrMap.ReplayBot.CurrentFrameTick}/{_player.CurrMap.ReplayBot.Frames.Count}", "#7882dd"); + string elapsed_ticks = FormatHUDElementHTML("Tick", $"{_player.CurrMap.ReplayBots[i].CurrentFrameTick}/{_player.CurrMap.ReplayBots[i].Frames.Count}", "#7882dd"); string hud = $"{replayModule}
{elapsed_ticks}
{nameModule}"; _player.Controller.PrintToCenterHtml(hud); diff --git a/src/ST-Player/Replay/ReplayPlayer.cs b/src/ST-Player/Replay/ReplayPlayer.cs index 2880f10..0d0a10a 100644 --- a/src/ST-Player/Replay/ReplayPlayer.cs +++ b/src/ST-Player/Replay/ReplayPlayer.cs @@ -12,6 +12,7 @@ internal class ReplayPlayer public bool IsPlaying { get; set; } = false; public bool IsPaused { get; set; } = false; public bool IsOnRepeat { get; set; } = true; // Currently should always repeat + public bool IsPlayable { get; set; } = false; // Tracking public List Frames { get; set; } = new List(); @@ -32,6 +33,7 @@ public void Reset() { this.IsPlaying = false; this.IsPaused = false; + this.IsPlayable = false; this.Frames.Clear(); @@ -40,6 +42,12 @@ public void Reset() this.Controller = null; } + public void SetController(CCSPlayerController c) + { + this.Controller = c; + this.IsPlayable = true; + } + public void Start() { if (this.Controller == null) @@ -61,11 +69,11 @@ public void Pause() public void Tick() { - if (!this.IsPlaying || this.Controller == null || this.Frames.Count == 0) + if (!this.IsPlaying || !this.IsPlayable || this.Frames.Count == 0) return; ReplayFrame current_frame = this.Frames[this.CurrentFrameTick]; - var current_pos = this.Controller.PlayerPawn.Value!.AbsOrigin!; + var current_pos = this.Controller!.PlayerPawn.Value!.AbsOrigin!; bool is_on_ground = (current_frame.Flags & (uint)PlayerFlags.FL_ONGROUND) != 0; bool is_ducking = (current_frame.Flags & (uint)PlayerFlags.FL_DUCKING) != 0; @@ -92,7 +100,7 @@ public void Tick() public void LoadReplayData(TimerDatabase DB, Map current_map) { - if (this.Controller == null) + if (!this.IsPlayable) return; // TODO: make query for wr too Task dbTask = DB.Query($@" @@ -121,12 +129,12 @@ public void LoadReplayData(TimerDatabase DB, Map current_map) private void FormatBotName(Map current_map) { - if (this.Controller == null) + if (!this.IsPlayable) return; - SchemaString bot_name = new SchemaString(this.Controller, "m_iszPlayerName"); + SchemaString bot_name = new SchemaString(this.Controller!, "m_iszPlayerName"); // Revisit, FORMAT CORECTLLY bot_name.Set($"[WR] {current_map.WR[0].Name} | {PlayerHUD.FormatTime(current_map.WR[0].Ticks)}"); - Utilities.SetStateChanged(this.Controller, "CBasePlayerController", "m_iszPlayerName"); + Utilities.SetStateChanged(this.Controller!, "CBasePlayerController", "m_iszPlayerName"); } } \ No newline at end of file diff --git a/src/ST-UTILS/ConVar.cs b/src/ST-UTILS/ConVar.cs new file mode 100644 index 0000000..55206ba --- /dev/null +++ b/src/ST-UTILS/ConVar.cs @@ -0,0 +1,15 @@ +using CounterStrikeSharp.API.Modules.Cvars; + +namespace SurfTimer; + +internal class ConVarHelper +{ + public static void RemoveCheatFlagFromConVar(string cv_name) + { + ConVar? cv = ConVar.Find(cv_name); + if (cv == null || (cv.Flags & CounterStrikeSharp.API.ConVarFlags.FCVAR_CHEAT) == 0) + return; + + cv.Flags &= ~CounterStrikeSharp.API.ConVarFlags.FCVAR_CHEAT; + } +} \ No newline at end of file diff --git a/src/SurfTimer.cs b/src/SurfTimer.cs index 2816032..b9032b9 100644 --- a/src/SurfTimer.cs +++ b/src/SurfTimer.cs @@ -77,6 +77,11 @@ public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) { // Load cvars/other configs here // Execute server_settings.cfg + + ConVarHelper.RemoveCheatFlagFromConVar("bot_stop"); + ConVarHelper.RemoveCheatFlagFromConVar("bot_freeze"); + ConVarHelper.RemoveCheatFlagFromConVar("bot_zombie"); + Server.ExecuteCommand("execifexists SurfTimer/server_settings.cfg"); Console.WriteLine("[CS2 Surf] Executed configuration: server_settings.cfg"); return HookResult.Continue; From 5d1d0fb1026f8eca8d50150e56a94d478be76af1 Mon Sep 17 00:00:00 2001 From: Shachar Date: Thu, 18 Jan 2024 21:44:40 +0200 Subject: [PATCH 09/17] Displaying time instead of ticks in bot replay now. would like for someone to check if there is a better way, but this works pretty well --- src/ST-Events/TriggerEndTouch.cs | 1 + src/ST-Events/TriggerStartTouch.cs | 1 + src/ST-Player/PlayerHUD.cs | 5 +-- src/ST-Player/Replay/ReplayFrame.cs | 10 ++++++ src/ST-Player/Replay/ReplayPlayer.cs | 46 +++++++++++++++++++++++++- src/ST-Player/Replay/ReplayRecorder.cs | 5 +++ 6 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/ST-Events/TriggerEndTouch.cs b/src/ST-Events/TriggerEndTouch.cs index a41d4af..7b0419b 100644 --- a/src/ST-Events/TriggerEndTouch.cs +++ b/src/ST-Events/TriggerEndTouch.cs @@ -50,6 +50,7 @@ internal HookResult OnTriggerEndTouch(DynamicHook handler) // MAP START ZONE player.Timer.Start(); + player.ReplayRecorder.CurrentSituation = ReplayFrameSituation.START_RUN; // string PracticeString = ""; // if (player.Timer.IsPracticeMode) diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index 08f6112..f022114 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -51,6 +51,7 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) if (player.Timer.IsRunning) { player.Timer.Stop(); + player.ReplayRecorder.CurrentSituation = ReplayFrameSituation.END_RUN; player.Stats.ThisRun.Ticks = player.Timer.Ticks; // End time for the run player.Stats.ThisRun.EndVelX = velocity_x; // End pre speed for the run diff --git a/src/ST-Player/PlayerHUD.cs b/src/ST-Player/PlayerHUD.cs index 42edb24..b7057c0 100644 --- a/src/ST-Player/PlayerHUD.cs +++ b/src/ST-Player/PlayerHUD.cs @@ -109,8 +109,9 @@ public void Display() string replayModule = $"{FormatHUDElementHTML("", "REPLAY", "red", "large")}"; string nameModule = FormatHUDElementHTML($"{_player.CurrMap.WR[_player.Timer.Style].Name}", $"{FormatTime(_player.CurrMap.WR[_player.Timer.Style].Ticks)}", "#ffd500"); - string elapsed_ticks = FormatHUDElementHTML("Tick", $"{_player.CurrMap.ReplayBots[i].CurrentFrameTick}/{_player.CurrMap.ReplayBots[i].Frames.Count}", "#7882dd"); - string hud = $"{replayModule}
{elapsed_ticks}
{nameModule}"; + + string elapsed_time = FormatHUDElementHTML("Time", $"{PlayerHUD.FormatTime(_player.CurrMap.ReplayBots[i].Stat_RunTick)}", "#7882dd"); + string hud = $"{replayModule}
{elapsed_time}
{nameModule}"; _player.Controller.PrintToCenterHtml(hud); } diff --git a/src/ST-Player/Replay/ReplayFrame.cs b/src/ST-Player/Replay/ReplayFrame.cs index 18174b3..f67de67 100644 --- a/src/ST-Player/Replay/ReplayFrame.cs +++ b/src/ST-Player/Replay/ReplayFrame.cs @@ -2,12 +2,22 @@ namespace SurfTimer; using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Core; +internal enum ReplayFrameSituation +{ + NONE, + START_RUN, + END_RUN, + TOUCH_CHECKPOINT, + START_STAGE, + END_STAGE +} [Serializable] internal class ReplayFrame { public Vector Pos { get; set; } = new Vector(0, 0, 0); public QAngle Ang { get; set; } = new QAngle(0, 0, 0); + public uint Situation { get; set; } = (uint)ReplayFrameSituation.NONE; public ulong Button { get; set; } public uint Flags { get; set; } public MoveType_t MoveType { get; set; } diff --git a/src/ST-Player/Replay/ReplayPlayer.cs b/src/ST-Player/Replay/ReplayPlayer.cs index 0d0a10a..6d2e867 100644 --- a/src/ST-Player/Replay/ReplayPlayer.cs +++ b/src/ST-Player/Replay/ReplayPlayer.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; using CounterStrikeSharp.API; @@ -14,6 +15,11 @@ internal class ReplayPlayer public bool IsOnRepeat { get; set; } = true; // Currently should always repeat public bool IsPlayable { get; set; } = false; + + // Stats for replay displaying + public bool Stat_IsRunning { get; set; } = false; + public int Stat_RunTick { get; set; } = 0; + // Tracking public List Frames { get; set; } = new List(); @@ -27,6 +33,9 @@ public void ResetReplay() { this.CurrentFrameTick = 0; this.FrameTickIncrement = 1; + + this.Stat_IsRunning = false; + this.Stat_RunTick = 0; } public void Reset() @@ -64,7 +73,10 @@ public void Stop() public void Pause() { if (this.IsPlaying) + { this.IsPaused = !this.IsPaused; + this.Stat_IsRunning = !this.Stat_IsRunning; + } } public void Tick() @@ -73,6 +85,35 @@ public void Tick() return; ReplayFrame current_frame = this.Frames[this.CurrentFrameTick]; + + + // SOME BLASHPEMY FOR YOU + if (this.FrameTickIncrement >= 0) + { + if (current_frame.Situation == (uint)ReplayFrameSituation.START_RUN) + { + this.Stat_IsRunning = true; + this.Stat_RunTick = 0; + } + else if (current_frame.Situation == (uint)ReplayFrameSituation.END_RUN) + { + this.Stat_IsRunning = false; + } + } + else + { + if (current_frame.Situation == (uint)ReplayFrameSituation.START_RUN) + { + this.Stat_IsRunning = false; + } + else if (current_frame.Situation == (uint)ReplayFrameSituation.END_RUN) + { + this.Stat_IsRunning = true; + this.Stat_RunTick = this.CurrentFrameTick - (64*2); // (64*2) counts for the 2 seconds before run actually starts + } + } + // END OF BLASPHEMY + var current_pos = this.Controller!.PlayerPawn.Value!.AbsOrigin!; bool is_on_ground = (current_frame.Flags & (uint)PlayerFlags.FL_ONGROUND) != 0; @@ -92,7 +133,11 @@ public void Tick() if (!this.IsPaused) + { this.CurrentFrameTick = Math.Max(0, this.CurrentFrameTick + this.FrameTickIncrement); + if (this.Stat_IsRunning) + this.Stat_RunTick = Math.Max(0, this.Stat_RunTick + this.FrameTickIncrement); + } if(this.CurrentFrameTick >= this.Frames.Count) this.ResetReplay(); @@ -124,7 +169,6 @@ public void LoadReplayData(TimerDatabase DB, Map current_map) } mapTimeReplay.Close(); dbTask.Dispose(); - } private void FormatBotName(Map current_map) diff --git a/src/ST-Player/Replay/ReplayRecorder.cs b/src/ST-Player/Replay/ReplayRecorder.cs index dd610e0..9ff60df 100644 --- a/src/ST-Player/Replay/ReplayRecorder.cs +++ b/src/ST-Player/Replay/ReplayRecorder.cs @@ -7,6 +7,7 @@ namespace SurfTimer; internal class ReplayRecorder { public bool IsRecording { get; set; } = false; + public ReplayFrameSituation CurrentSituation { get; set; } = ReplayFrameSituation.NONE; public List Frames { get; set; } = new List(); public void Reset() @@ -48,12 +49,16 @@ public void Tick(Player player) { Pos = new Vector(player_pos.X, player_pos.Y, player_pos.Z), Ang = new QAngle(player_angle.X, player_angle.Y, player_angle.Z), + Situation = (uint)this.CurrentSituation, Button = player_button, Flags = player_flags, MoveType = player_move_type, }; this.Frames.Add(frame); + + // Every Situation should last for at most, 1 tick + this.CurrentSituation = ReplayFrameSituation.NONE; } /// From e7f18e8870abd355e9cef80688f9bda454e6066d Mon Sep 17 00:00:00 2001 From: Shachar Date: Fri, 19 Jan 2024 19:11:08 +0200 Subject: [PATCH 10/17] Added support for Personal Best replays with !pbreplay --- src/ST-Commands/PlayerCommands.cs | 41 +++++++++++++++++++++++++ src/ST-Events/Players.cs | 32 ++++++++++--------- src/ST-Events/Tick.cs | 9 ++++-- src/ST-Events/TriggerStartTouch.cs | 27 +++++++++------- src/ST-Map/Map.cs | 26 ++++++++++++---- src/ST-Player/PlayerHUD.cs | 3 +- src/ST-Player/PlayerStats/CurrentRun.cs | 7 +++-- src/ST-Player/Replay/ReplayPlayer.cs | 30 +++++++++++++----- src/ST-Player/Replay/ReplayRecorder.cs | 8 +++++ 9 files changed, 137 insertions(+), 46 deletions(-) diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index 18bcd4c..0d89dbd 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -130,6 +130,47 @@ public void ReverseReplay(CCSPlayerController? player, CommandInfo command) } } + [ConsoleCommand("css_pbreplay", "Allows for replay of player's PB")] + public void PbReplay(CCSPlayerController? player, CommandInfo command) + { + if(player == null) + return; + + int maptime_id = playerList[player!.UserId ?? 0].Stats.PB[playerList[player.UserId ?? 0].Timer.Style].ID; + if (command.ArgCount > 1) + { + try + { + maptime_id = int.Parse(command.ArgByIndex(1)); + } + catch {} + } + + if(maptime_id == -1 || !CurrentMap.ConnectedMapTimes.Contains(maptime_id)) + { + player.PrintToChat($"{PluginPrefix} {ChatColors.Red}No time was found"); + return; + } + + for(int i = 0; i < CurrentMap.ReplayBots.Count; i++) + { + if(CurrentMap.ReplayBots[i].Stat_MapTimeID == maptime_id) + { + player.PrintToChat($"{PluginPrefix} {ChatColors.Red}A bot of this run already playing"); + return; + } + } + + CurrentMap.ReplayBots = CurrentMap.ReplayBots.Prepend(new ReplayPlayer() { + Stat_MapTimeID = maptime_id, + Stat_Prefix = "PB" + }).ToList(); + + Server.NextFrame(() => { + Server.ExecuteCommand($"bot_quota {CurrentMap.ReplayBots.Count}"); + }); + } + /* ######################## Saveloc Commands diff --git a/src/ST-Events/Players.cs b/src/ST-Events/Players.cs index f59670c..5f988c8 100644 --- a/src/ST-Events/Players.cs +++ b/src/ST-Events/Players.cs @@ -20,21 +20,23 @@ public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { for (int i = 0; i < CurrentMap.ReplayBots.Count; i++) { - if(!CurrentMap.ReplayBots[i].IsPlayable) - { - // Currently only for Map - if(i == 0) { - CurrentMap.ReplayBots[0].SetController(controller); - Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); // WHY COLORS NOT WORKING AHHHHH!!!!! - AddTimer(2f, () => { - CurrentMap.ReplayBots[0].Controller!.RemoveWeapons(); - - CurrentMap.ReplayBots[0].LoadReplayData(DB!, CurrentMap); - - CurrentMap.ReplayBots[0].Start(); - }); - } - } + if(CurrentMap.ReplayBots[i].IsPlayable) + continue; + + int repeats = -1; + if(CurrentMap.ReplayBots[i].Stat_Prefix == "PB") + repeats = 3; + + CurrentMap.ReplayBots[i].SetController(controller, repeats); + Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); + AddTimer(2f, () => { + CurrentMap.ReplayBots[i].Controller!.RemoveWeapons(); + + CurrentMap.ReplayBots[i].LoadReplayData(DB!); + + CurrentMap.ReplayBots[i].Start(); + }); + return HookResult.Continue; } } diff --git a/src/ST-Events/Tick.cs b/src/ST-Events/Tick.cs index 51eaadd..dde0d21 100644 --- a/src/ST-Events/Tick.cs +++ b/src/ST-Events/Tick.cs @@ -14,9 +14,14 @@ public void OnTick() player.HUD.Display(); } - for(int i = 0; i < CurrentMap?.ReplayBots.Count; i++) + if (CurrentMap == null) + return; + + for(int i = 0; i < CurrentMap!.ReplayBots.Count; i++) { - CurrentMap?.ReplayBots[i].Tick(); + CurrentMap.ReplayBots[i].Tick(); + if (CurrentMap.ReplayBots[i].RepeatCount == 0) + CurrentMap.KickReplayBot(i); } } } \ No newline at end of file diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index f022114..2b7a283 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -96,18 +96,23 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) // Add entry in DB for the run if(!player.Timer.IsPracticeMode) { - 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) - 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 - CurrentMap.GetMapRecordAndTotals(DB); // Reload the Map record and totals for the HUD - - // Replay - Add end buffer for replay - AddTimer(1.5f, () => player.ReplayRecorder.SaveReplayData(player, DB)); - AddTimer(2f, () => { - CurrentMap.ReplayBots[0].LoadReplayData(DB!, CurrentMap); - CurrentMap.ReplayBots[0].ResetReplay(); + AddTimer(1.5f, () => { + 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) + 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 + CurrentMap.GetMapRecordAndTotals(DB); // Reload the Map record and totals for the HUD }); + + // If run better than wr, load the replay time + if(player.Timer.Ticks < CurrentMap.WR[player.Timer.Style].Ticks) + { + AddTimer(2f, () => { + CurrentMap.ReplayBots[CurrentMap.ReplayBots.Count-1].Stat_MapTimeID = CurrentMap.WR[player.Timer.Style].ID; + CurrentMap.ReplayBots[CurrentMap.ReplayBots.Count-1].LoadReplayData(DB!); + CurrentMap.ReplayBots[CurrentMap.ReplayBots.Count-1].ResetReplay(); + }); + } } } diff --git a/src/ST-Map/Map.cs b/src/ST-Map/Map.cs index cc4c20a..38b53f5 100644 --- a/src/ST-Map/Map.cs +++ b/src/ST-Map/Map.cs @@ -21,6 +21,7 @@ internal class Map public int LastPlayed {get; set;} = 0; public int TotalCompletions {get; set;} = 0; public Dictionary WR { get; set; } = new Dictionary(); + public List ConnectedMapTimes { get; set; } = new List(); public List ReplayBots { get; set; } = new List { new ReplayPlayer() }; // Zone Origin Information @@ -217,22 +218,33 @@ internal Map(string Name, TimerDatabase DB) // Initiates getting the World Records for the map GetMapRecordAndTotals(DB); // To-do: Implement styles - int bot_quota = 1; + this.ReplayBots[0].Stat_MapTimeID = this.WR[0].ID; if(this.Stages > 0) { - this.ReplayBots.Add(new ReplayPlayer()); - bot_quota++; + this.ReplayBots = this.ReplayBots.Prepend(new ReplayPlayer()).ToList(); } if(this.Bonuses > 0) { - this.ReplayBots.Add(new ReplayPlayer()); - bot_quota++; + this.ReplayBots = this.ReplayBots.Prepend(new ReplayPlayer()).ToList(); } Server.NextFrame(() => { - Server.ExecuteCommand($"bot_quota {bot_quota}"); + Server.ExecuteCommand($"bot_quota {this.ReplayBots.Count}"); }); } + public void KickReplayBot(int index) + { + if (!this.ReplayBots[index].IsPlayable) + return; + + int? id_to_kick = this.ReplayBots[index].Controller!.UserId; + if(id_to_kick == null) + return; + + this.ReplayBots.RemoveAt(index); + Server.ExecuteCommand($"kickid {id_to_kick}; bot_quota {this.ReplayBots.Count}"); + } + public bool IsInZone(Vector zoneOrigin, float zoneCollisionRadius, Vector spawnOrigin) { if (spawnOrigin.X >= zoneOrigin.X - zoneCollisionRadius && spawnOrigin.X <= zoneOrigin.X + zoneCollisionRadius && @@ -261,6 +273,7 @@ FROM MapTimes { // To-do: Implement bonuses WR // To-do: Implement stages WR + this.ConnectedMapTimes.Clear(); while (mapWrData.Read()) { if (totalRows == 0) // We are sorting by `run_time ASC` so the first row is always the fastest run for the map and style combo :) @@ -276,6 +289,7 @@ FROM MapTimes this.WR[style].RunDate = mapWrData.GetInt32("run_date"); // Fastest run date for the Map and Style combo this.WR[style].Name = mapWrData.GetString("name"); // Fastest run player name for the Map and Style combo } + this.ConnectedMapTimes.Add(mapWrData.GetInt32("id")); totalRows++; } } diff --git a/src/ST-Player/PlayerHUD.cs b/src/ST-Player/PlayerHUD.cs index b7057c0..6685397 100644 --- a/src/ST-Player/PlayerHUD.cs +++ b/src/ST-Player/PlayerHUD.cs @@ -108,7 +108,8 @@ public void Display() continue; string replayModule = $"{FormatHUDElementHTML("", "REPLAY", "red", "large")}"; - string nameModule = FormatHUDElementHTML($"{_player.CurrMap.WR[_player.Timer.Style].Name}", $"{FormatTime(_player.CurrMap.WR[_player.Timer.Style].Ticks)}", "#ffd500"); + + string nameModule = FormatHUDElementHTML($"{_player.CurrMap.ReplayBots[i].Stat_PlayerName}", $"{FormatTime(_player.CurrMap.ReplayBots[i].Stat_RunTime)}", "#ffd500"); string elapsed_time = FormatHUDElementHTML("Time", $"{PlayerHUD.FormatTime(_player.CurrMap.ReplayBots[i].Stat_RunTick)}", "#7882dd"); string hud = $"{replayModule}
{elapsed_time}
{nameModule}"; diff --git a/src/ST-Player/PlayerStats/CurrentRun.cs b/src/ST-Player/PlayerStats/CurrentRun.cs index ddacc1a..4609dd1 100644 --- a/src/ST-Player/PlayerStats/CurrentRun.cs +++ b/src/ST-Player/PlayerStats/CurrentRun.cs @@ -53,13 +53,14 @@ public void SaveMapTime(Player player, TimerDatabase DB) // Add entry in DB for the run // To-do: add `type` int style = player.Timer.Style; + string replay_frames = player.ReplayRecorder.SerializeReplay(); Task updatePlayerRunTask = DB.Write($@" INSERT INTO `MapTimes` - (`player_id`, `map_id`, `style`, `type`, `stage`, `run_time`, `start_vel_x`, `start_vel_y`, `start_vel_z`, `end_vel_x`, `end_vel_y`, `end_vel_z`, `run_date`) + (`player_id`, `map_id`, `style`, `type`, `stage`, `run_time`, `start_vel_x`, `start_vel_y`, `start_vel_z`, `end_vel_x`, `end_vel_y`, `end_vel_z`, `run_date`, `replay_frames`) VALUES ({player.Profile.ID}, {player.CurrMap.ID}, {style}, 0, 0, {player.Stats.ThisRun.Ticks}, - {player.Stats.ThisRun.StartVelX}, {player.Stats.ThisRun.StartVelY}, {player.Stats.ThisRun.StartVelZ}, {player.Stats.ThisRun.EndVelX}, {player.Stats.ThisRun.EndVelY}, {player.Stats.ThisRun.EndVelZ}, {(int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()}) + {player.Stats.ThisRun.StartVelX}, {player.Stats.ThisRun.StartVelY}, {player.Stats.ThisRun.StartVelZ}, {player.Stats.ThisRun.EndVelX}, {player.Stats.ThisRun.EndVelY}, {player.Stats.ThisRun.EndVelZ}, {(int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()}, '{replay_frames}') ON DUPLICATE KEY UPDATE run_time=VALUES(run_time), start_vel_x=VALUES(start_vel_x), start_vel_y=VALUES(start_vel_y), - start_vel_z=VALUES(start_vel_z), end_vel_x=VALUES(end_vel_x), end_vel_y=VALUES(end_vel_y), end_vel_z=VALUES(end_vel_z), run_date=VALUES(run_date); + start_vel_z=VALUES(start_vel_z), end_vel_x=VALUES(end_vel_x), end_vel_y=VALUES(end_vel_y), end_vel_z=VALUES(end_vel_z), run_date=VALUES(run_date), replay_frames=VALUES(replay_frames); "); if (updatePlayerRunTask.Result <= 0) throw new Exception($"CS2 Surf ERROR >> internal class PersonalBest -> SaveMapTime -> Failed to insert/update player run in database. Player: {player.Profile.Name} ({player.Profile.SteamID})"); diff --git a/src/ST-Player/Replay/ReplayPlayer.cs b/src/ST-Player/Replay/ReplayPlayer.cs index 6d2e867..1d40c27 100644 --- a/src/ST-Player/Replay/ReplayPlayer.cs +++ b/src/ST-Player/Replay/ReplayPlayer.cs @@ -12,11 +12,16 @@ internal class ReplayPlayer { public bool IsPlaying { get; set; } = false; public bool IsPaused { get; set; } = false; - public bool IsOnRepeat { get; set; } = true; // Currently should always repeat public bool IsPlayable { get; set; } = false; + // Tracking for replay counting + public int RepeatCount { get; set; } = -1; // Stats for replay displaying + public int Stat_MapTimeID { get; set; } = -1; + public string Stat_Prefix { get; set; } = "WR"; + public string Stat_PlayerName { get; set; } = "None"; + public int Stat_RunTime { get; set; } = 0; public bool Stat_IsRunning { get; set; } = false; public int Stat_RunTick { get; set; } = 0; @@ -33,6 +38,8 @@ public void ResetReplay() { this.CurrentFrameTick = 0; this.FrameTickIncrement = 1; + if(this.RepeatCount > 0) + this.RepeatCount--; this.Stat_IsRunning = false; this.Stat_RunTick = 0; @@ -43,6 +50,7 @@ public void Reset() this.IsPlaying = false; this.IsPaused = false; this.IsPlayable = false; + this.RepeatCount = -1; this.Frames.Clear(); @@ -51,9 +59,10 @@ public void Reset() this.Controller = null; } - public void SetController(CCSPlayerController c) + public void SetController(CCSPlayerController c, int repeat_count = -1) { this.Controller = c; + this.RepeatCount = repeat_count; this.IsPlayable = true; } @@ -143,14 +152,16 @@ public void Tick() this.ResetReplay(); } - public void LoadReplayData(TimerDatabase DB, Map current_map) + public void LoadReplayData(TimerDatabase DB) { if (!this.IsPlayable) return; // TODO: make query for wr too Task dbTask = DB.Query($@" - SELECT `replay_frames` FROM MapTimeReplay - WHERE `map_id`={current_map.ID} AND `maptime_id`={current_map.WR[0].ID} + SELECT MapTimes.replay_frames, MapTimes.run_time, Player.name + FROM MapTimes + JOIN Player ON MapTimes.player_id = Player.id + WHERE MapTimes.id={this.Stat_MapTimeID} "); MySqlDataReader mapTimeReplay = dbTask.Result; if(!mapTimeReplay.HasRows) @@ -164,21 +175,24 @@ public void LoadReplayData(TimerDatabase DB, Map current_map) { string json = Compressor.Decompress(Encoding.UTF8.GetString((byte[])mapTimeReplay[0])); this.Frames = JsonSerializer.Deserialize>(json, options)!; + + this.Stat_RunTime = mapTimeReplay.GetInt32("run_time"); + this.Stat_PlayerName = mapTimeReplay.GetString("name"); } - FormatBotName(current_map); + FormatBotName(); } mapTimeReplay.Close(); dbTask.Dispose(); } - private void FormatBotName(Map current_map) + private void FormatBotName() { if (!this.IsPlayable) return; SchemaString bot_name = new SchemaString(this.Controller!, "m_iszPlayerName"); // Revisit, FORMAT CORECTLLY - bot_name.Set($"[WR] {current_map.WR[0].Name} | {PlayerHUD.FormatTime(current_map.WR[0].Ticks)}"); + bot_name.Set($"[{this.Stat_Prefix}] {this.Stat_PlayerName} | {PlayerHUD.FormatTime(this.Stat_RunTime)}"); Utilities.SetStateChanged(this.Controller!, "CBasePlayerController", "m_iszPlayerName"); } } \ No newline at end of file diff --git a/src/ST-Player/Replay/ReplayRecorder.cs b/src/ST-Player/Replay/ReplayRecorder.cs index 9ff60df..cdbae0c 100644 --- a/src/ST-Player/Replay/ReplayRecorder.cs +++ b/src/ST-Player/Replay/ReplayRecorder.cs @@ -61,7 +61,15 @@ public void Tick(Player player) this.CurrentSituation = ReplayFrameSituation.NONE; } + public string SerializeReplay() + { + JsonSerializerOptions options = new JsonSerializerOptions {WriteIndented = false, Converters = { new VectorConverter(), new QAngleConverter() }}; + string replay_frames = JsonSerializer.Serialize(Frames, options); + return Compressor.Compress(replay_frames); + } + /// + /// **Deprecated** /// [ player_id | maptime_id | replay_frames ] /// @ Adding a replay data for a run (PB/WR) /// @ Data saved can be accessed with `ReplayPlayer.LoadReplayData` From 5430f061b8d6f5bf0512552210a0dea161be1146 Mon Sep 17 00:00:00 2001 From: Shachar Date: Fri, 19 Jan 2024 19:34:26 +0200 Subject: [PATCH 11/17] Updated readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 04093ad..123870d 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Bold & Italics = being worked on. - [ ] Profile implementation (DB) - [ ] Points/Skill Groups (DB) - [ ] Player settings (DB) -- [ ] Run replays +- [X] Run replays +- [X] Saveloc/Tele - [ ] Style implementation (SW, HSW, BW) - [ ] Paint (?) From a5d2d1107ac572543f1c70c7d6f0295fadbfd739 Mon Sep 17 00:00:00 2001 From: Shachar Date: Fri, 19 Jan 2024 20:00:54 +0200 Subject: [PATCH 12/17] Removed not needed comments --- src/ST-Events/TriggerEndTouch.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ST-Events/TriggerEndTouch.cs b/src/ST-Events/TriggerEndTouch.cs index 7b0419b..a45fd3f 100644 --- a/src/ST-Events/TriggerEndTouch.cs +++ b/src/ST-Events/TriggerEndTouch.cs @@ -52,10 +52,6 @@ internal HookResult OnTriggerEndTouch(DynamicHook handler) player.Timer.Start(); player.ReplayRecorder.CurrentSituation = ReplayFrameSituation.START_RUN; - // string PracticeString = ""; - // if (player.Timer.IsPracticeMode) - // PracticeString = $"({ChatColors.Grey}Practice{ChatColors.Default}) "; - /* Revisit // Wonky Prespeed check // To-do: make the teleportation a bit more elegant (method in a class or something) From d47ec27ab0c19136a0997c583fd8219ca251c7bc Mon Sep 17 00:00:00 2001 From: Shachar Date: Sat, 20 Jan 2024 00:45:48 +0200 Subject: [PATCH 13/17] Works also when no wr exists --- src/ST-Events/TriggerStartTouch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index 2b7a283..0042b0a 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -105,7 +105,7 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) }); // If run better than wr, load the replay time - if(player.Timer.Ticks < CurrentMap.WR[player.Timer.Style].Ticks) + if(player.Timer.Ticks < CurrentMap.WR[player.Timer.Style].Ticks || CurrentMap.WR[player.Timer.Style].ID == -1) { AddTimer(2f, () => { CurrentMap.ReplayBots[CurrentMap.ReplayBots.Count-1].Stat_MapTimeID = CurrentMap.WR[player.Timer.Style].ID; From 7c71b78e7c6b8669ec148446afc5c7982d31d3cb Mon Sep 17 00:00:00 2001 From: Shachar Date: Sat, 20 Jan 2024 00:50:07 +0200 Subject: [PATCH 14/17] FormatTime correctly --- src/ST-Player/PlayerHUD.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ST-Player/PlayerHUD.cs b/src/ST-Player/PlayerHUD.cs index 6685397..1366f7d 100644 --- a/src/ST-Player/PlayerHUD.cs +++ b/src/ST-Player/PlayerHUD.cs @@ -44,10 +44,10 @@ public static string FormatTime(int ticks, PlayerTimer.TimeFormatStyle style = P { case PlayerTimer.TimeFormatStyle.Compact: return time.TotalMinutes < 1 - ? $"{time.Seconds:D1}.{millis:D3}" - : $"{time.Minutes:D1}:{time.Seconds:D1}.{millis:D3}"; + ? $"{time.Seconds:D1}:{millis:D3}" + : $"{time.Minutes:D2}:{time.Seconds:D2}.{millis:D3}"; case PlayerTimer.TimeFormatStyle.Full: - return $"{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}.{millis:D3}"; + return $"{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}:{millis:D3}"; case PlayerTimer.TimeFormatStyle.Verbose: return $"{time.Hours}h {time.Minutes}m {time.Seconds}s {millis}ms"; default: From d2e1082118a4cb3c542719e67f97f0c5511a5d9d Mon Sep 17 00:00:00 2001 From: Shachar Date: Sat, 20 Jan 2024 11:43:18 +0200 Subject: [PATCH 15/17] Reverted uneeded changes --- src/ST-Player/PlayerHUD.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ST-Player/PlayerHUD.cs b/src/ST-Player/PlayerHUD.cs index 1366f7d..a7a0dfa 100644 --- a/src/ST-Player/PlayerHUD.cs +++ b/src/ST-Player/PlayerHUD.cs @@ -45,9 +45,9 @@ public static string FormatTime(int ticks, PlayerTimer.TimeFormatStyle style = P case PlayerTimer.TimeFormatStyle.Compact: return time.TotalMinutes < 1 ? $"{time.Seconds:D1}:{millis:D3}" - : $"{time.Minutes:D2}:{time.Seconds:D2}.{millis:D3}"; + : $"{time.Minutes:D1}:{time.Seconds:D1}.{millis:D3}"; case PlayerTimer.TimeFormatStyle.Full: - return $"{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}:{millis:D3}"; + return $"{time.Hours:D1}:{time.Minutes:D1}:{time.Seconds:D1}:{millis:D3}"; case PlayerTimer.TimeFormatStyle.Verbose: return $"{time.Hours}h {time.Minutes}m {time.Seconds}s {millis}ms"; default: From 192ad573817f50cdf52adb0d900f435093a7aad1 Mon Sep 17 00:00:00 2001 From: Shachar Date: Sat, 20 Jan 2024 13:25:52 +0200 Subject: [PATCH 16/17] Temp patch for maps changing bot_quota, need to be addressed --- src/ST-Events/Players.cs | 4 +++- src/ST-Events/Tick.cs | 14 ++++++++++++++ src/ST-Player/PlayerHUD.cs | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ST-Events/Players.cs b/src/ST-Events/Players.cs index 5f988c8..eae88de 100644 --- a/src/ST-Events/Players.cs +++ b/src/ST-Events/Players.cs @@ -9,7 +9,7 @@ namespace SurfTimer; public partial class SurfTimer { - [GameEventHandler(HookMode.Post)] + [GameEventHandler] public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { var controller = @event.Userid; @@ -30,6 +30,8 @@ public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) CurrentMap.ReplayBots[i].SetController(controller, repeats); Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); AddTimer(2f, () => { + if(CurrentMap.ReplayBots[i].Controller == null) + return; CurrentMap.ReplayBots[i].Controller!.RemoveWeapons(); CurrentMap.ReplayBots[i].LoadReplayData(DB!); diff --git a/src/ST-Events/Tick.cs b/src/ST-Events/Tick.cs index dde0d21..1abdeab 100644 --- a/src/ST-Events/Tick.cs +++ b/src/ST-Events/Tick.cs @@ -1,4 +1,5 @@ using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Utils; namespace SurfTimer; @@ -17,6 +18,19 @@ public void OnTick() if (CurrentMap == null) return; + // Need to disable maps from executing their cfgs. Currently idk how (But seriusly it a security issue) + ConVar? bot_quota = ConVar.Find("bot_quota"); + if (bot_quota != null) + { + int cbq = bot_quota.GetPrimitiveValue(); + if(cbq != CurrentMap.ReplayBots.Count) + { + bot_quota.SetValue(CurrentMap.ReplayBots.Count); + } + } + + + for(int i = 0; i < CurrentMap!.ReplayBots.Count; i++) { CurrentMap.ReplayBots[i].Tick(); diff --git a/src/ST-Player/PlayerHUD.cs b/src/ST-Player/PlayerHUD.cs index a7a0dfa..4787ffb 100644 --- a/src/ST-Player/PlayerHUD.cs +++ b/src/ST-Player/PlayerHUD.cs @@ -47,7 +47,7 @@ public static string FormatTime(int ticks, PlayerTimer.TimeFormatStyle style = P ? $"{time.Seconds:D1}:{millis:D3}" : $"{time.Minutes:D1}:{time.Seconds:D1}.{millis:D3}"; case PlayerTimer.TimeFormatStyle.Full: - return $"{time.Hours:D1}:{time.Minutes:D1}:{time.Seconds:D1}:{millis:D3}"; + return $"{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}.{millis:D3}"; case PlayerTimer.TimeFormatStyle.Verbose: return $"{time.Hours}h {time.Minutes}m {time.Seconds}s {millis}ms"; default: From 392111c752537f7da9aade75a7a90cff797f4b7e Mon Sep 17 00:00:00 2001 From: Shachar Date: Sat, 20 Jan 2024 15:28:39 +0200 Subject: [PATCH 17/17] Cleaned code --- cfg/SurfTimer/server_settings.cfg | 2 +- src/ST-Commands/PlayerCommands.cs | 4 +-- src/ST-Events/Players.cs | 43 ++++++++++++------------- src/ST-Events/Tick.cs | 4 --- src/ST-Events/TriggerEndTouch.cs | 3 +- src/ST-Events/TriggerStartTouch.cs | 9 +++--- src/ST-Map/Map.cs | 14 +++----- src/ST-Player/Player.cs | 11 +++++++ src/ST-Player/PlayerHUD.cs | 2 +- src/ST-Player/PlayerStats/Checkpoint.cs | 2 -- src/ST-Player/Replay/ReplayPlayer.cs | 30 +++++++++-------- src/ST-Player/Replay/ReplayRecorder.cs | 23 ------------- src/SurfTimer.cs | 2 -- 13 files changed, 62 insertions(+), 87 deletions(-) diff --git a/cfg/SurfTimer/server_settings.cfg b/cfg/SurfTimer/server_settings.cfg index 65f5b6f..2ff13d0 100644 --- a/cfg/SurfTimer/server_settings.cfg +++ b/cfg/SurfTimer/server_settings.cfg @@ -30,7 +30,7 @@ sv_staminalandcost 0 sv_timebetweenducks 0 // Some replay bot shit (took so fucking long to debug) -// bot_quota 1 No nned for this, because the server handles it +// bot_quota 1 No need for this, because the server handles it bot_quota_mode "normal" bot_join_after_player 1 bot_join_team CT diff --git a/src/ST-Commands/PlayerCommands.cs b/src/ST-Commands/PlayerCommands.cs index 0d89dbd..3e51527 100644 --- a/src/ST-Commands/PlayerCommands.cs +++ b/src/ST-Commands/PlayerCommands.cs @@ -107,7 +107,7 @@ public void PauseReplay(CCSPlayerController? player, CommandInfo command) foreach(ReplayPlayer rb in CurrentMap.ReplayBots) { - if(!rb.IsPlayable || !rb.IsPlaying || rb.Controller!.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + if(!rb.IsPlayable || !rb.IsPlaying || !playerList[player.UserId ?? 0].IsSpectating(rb.Controller!)) continue; rb.Pause(); @@ -123,7 +123,7 @@ public void ReverseReplay(CCSPlayerController? player, CommandInfo command) foreach(ReplayPlayer rb in CurrentMap.ReplayBots) { - if(!rb.IsPlayable || !rb.IsPlaying || rb.Controller!.Pawn.SerialNum != player.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + if(!rb.IsPlayable || !rb.IsPlaying || !playerList[player.UserId ?? 0].IsSpectating(rb.Controller!)) continue; rb.FrameTickIncrement *= -1; diff --git a/src/ST-Events/Players.cs b/src/ST-Events/Players.cs index eae88de..a2162a2 100644 --- a/src/ST-Events/Players.cs +++ b/src/ST-Events/Players.cs @@ -13,33 +13,32 @@ public partial class SurfTimer public HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { var controller = @event.Userid; - if(!controller.IsValid) + if(!controller.IsValid || !controller.IsBot) return HookResult.Continue; - if(controller.IsBot) + for (int i = 0; i < CurrentMap.ReplayBots.Count; i++) { - for (int i = 0; i < CurrentMap.ReplayBots.Count; i++) - { - if(CurrentMap.ReplayBots[i].IsPlayable) - continue; + if(CurrentMap.ReplayBots[i].IsPlayable) + continue; + + int repeats = -1; + if(CurrentMap.ReplayBots[i].Stat_Prefix == "PB") + repeats = 3; + + CurrentMap.ReplayBots[i].SetController(controller, repeats); + Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); + AddTimer(2f, () => { + if(!CurrentMap.ReplayBots[i].IsPlayable) + return; - int repeats = -1; - if(CurrentMap.ReplayBots[i].Stat_Prefix == "PB") - repeats = 3; + CurrentMap.ReplayBots[i].Controller!.RemoveWeapons(); - CurrentMap.ReplayBots[i].SetController(controller, repeats); - Server.PrintToChatAll($"{ChatColors.Lime} Loading replay data..."); - AddTimer(2f, () => { - if(CurrentMap.ReplayBots[i].Controller == null) - return; - CurrentMap.ReplayBots[i].Controller!.RemoveWeapons(); - - CurrentMap.ReplayBots[i].LoadReplayData(DB!); - - CurrentMap.ReplayBots[i].Start(); - }); - return HookResult.Continue; - } + CurrentMap.ReplayBots[i].LoadReplayData(DB!); + + CurrentMap.ReplayBots[i].Start(); + }); + + return HookResult.Continue; } return HookResult.Continue; diff --git a/src/ST-Events/Tick.cs b/src/ST-Events/Tick.cs index 1abdeab..37703e4 100644 --- a/src/ST-Events/Tick.cs +++ b/src/ST-Events/Tick.cs @@ -1,6 +1,4 @@ -using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Cvars; -using CounterStrikeSharp.API.Modules.Utils; namespace SurfTimer; @@ -29,8 +27,6 @@ public void OnTick() } } - - for(int i = 0; i < CurrentMap!.ReplayBots.Count; i++) { CurrentMap.ReplayBots[i].Tick(); diff --git a/src/ST-Events/TriggerEndTouch.cs b/src/ST-Events/TriggerEndTouch.cs index a45fd3f..b98ed13 100644 --- a/src/ST-Events/TriggerEndTouch.cs +++ b/src/ST-Events/TriggerEndTouch.cs @@ -2,7 +2,6 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; -using CounterStrikeSharp.API; namespace SurfTimer; @@ -45,7 +44,7 @@ internal HookResult OnTriggerEndTouch(DynamicHook handler) if(player.ReplayRecorder.IsRecording) { // Saveing 2 seconds before leaving the start zone - player.ReplayRecorder.Frames.RemoveRange(0, Math.Max(0, player.ReplayRecorder.Frames.Count - (64*2))); // Would like for someone to fact check the math :) + player.ReplayRecorder.Frames.RemoveRange(0, Math.Max(0, player.ReplayRecorder.Frames.Count - (64*2))); // Todo make a plugin convar for the time saved before start of run } // MAP START ZONE diff --git a/src/ST-Events/TriggerStartTouch.cs b/src/ST-Events/TriggerStartTouch.cs index 0042b0a..3059d4e 100644 --- a/src/ST-Events/TriggerStartTouch.cs +++ b/src/ST-Events/TriggerStartTouch.cs @@ -104,13 +104,14 @@ internal HookResult OnTriggerStartTouch(DynamicHook handler) CurrentMap.GetMapRecordAndTotals(DB); // Reload the Map record and totals for the HUD }); - // If run better than wr, load the replay time + // This section checks if the PB is better than WR if(player.Timer.Ticks < CurrentMap.WR[player.Timer.Style].Ticks || CurrentMap.WR[player.Timer.Style].ID == -1) { + int WrIndex = CurrentMap.ReplayBots.Count-1; // As the ReplaysBot is set, WR Index will always be at the end of the List AddTimer(2f, () => { - CurrentMap.ReplayBots[CurrentMap.ReplayBots.Count-1].Stat_MapTimeID = CurrentMap.WR[player.Timer.Style].ID; - CurrentMap.ReplayBots[CurrentMap.ReplayBots.Count-1].LoadReplayData(DB!); - CurrentMap.ReplayBots[CurrentMap.ReplayBots.Count-1].ResetReplay(); + CurrentMap.ReplayBots[WrIndex].Stat_MapTimeID = CurrentMap.WR[player.Timer.Style].ID; + CurrentMap.ReplayBots[WrIndex].LoadReplayData(DB!); + CurrentMap.ReplayBots[WrIndex].ResetReplay(); }); } } diff --git a/src/ST-Map/Map.cs b/src/ST-Map/Map.cs index 38b53f5..c1fb166 100644 --- a/src/ST-Map/Map.cs +++ b/src/ST-Map/Map.cs @@ -218,18 +218,12 @@ internal Map(string Name, TimerDatabase DB) // Initiates getting the World Records for the map GetMapRecordAndTotals(DB); // To-do: Implement styles - this.ReplayBots[0].Stat_MapTimeID = this.WR[0].ID; - if(this.Stages > 0) - { + this.ReplayBots[0].Stat_MapTimeID = this.WR[0].ID; // Sets WrIndex to WR maptime_id + if(this.Stages > 0) // If stages map adds bot this.ReplayBots = this.ReplayBots.Prepend(new ReplayPlayer()).ToList(); - } - if(this.Bonuses > 0) - { + + if(this.Bonuses > 0) // If has bonuses adds bot this.ReplayBots = this.ReplayBots.Prepend(new ReplayPlayer()).ToList(); - } - Server.NextFrame(() => { - Server.ExecuteCommand($"bot_quota {this.ReplayBots.Count}"); - }); } public void KickReplayBot(int index) diff --git a/src/ST-Player/Player.cs b/src/ST-Player/Player.cs index af124e5..2bcfb83 100644 --- a/src/ST-Player/Player.cs +++ b/src/ST-Player/Player.cs @@ -38,4 +38,15 @@ public Player(CCSPlayerController Controller, CCSPlayer_MovementServices Movemen this.HUD = new PlayerHUD(this); this.CurrMap = CurrMap; } + + /// + /// Checks if current player is spcetating player

+ ///

+ public bool IsSpectating(CCSPlayerController p) + { + if(p == null || this.Controller == null || this.Controller.Team != CounterStrikeSharp.API.Modules.Utils.CsTeam.Spectator) + return false; + + return p.Pawn.SerialNum == this.Controller.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum; + } } diff --git a/src/ST-Player/PlayerHUD.cs b/src/ST-Player/PlayerHUD.cs index 4787ffb..4ed6c2b 100644 --- a/src/ST-Player/PlayerHUD.cs +++ b/src/ST-Player/PlayerHUD.cs @@ -104,7 +104,7 @@ public void Display() { for (int i = 0; i < _player.CurrMap.ReplayBots.Count; i++) { - if (_player.CurrMap.ReplayBots[i].Controller?.Pawn.SerialNum != _player.Controller.ObserverPawn.Value!.ObserverServices!.ObserverTarget.SerialNum) + if(!_player.CurrMap.ReplayBots[i].IsPlayable || !_player.IsSpectating(_player.CurrMap.ReplayBots[i].Controller!)) continue; string replayModule = $"{FormatHUDElementHTML("", "REPLAY", "red", "large")}"; diff --git a/src/ST-Player/PlayerStats/Checkpoint.cs b/src/ST-Player/PlayerStats/Checkpoint.cs index 30921f5..bf51165 100644 --- a/src/ST-Player/PlayerStats/Checkpoint.cs +++ b/src/ST-Player/PlayerStats/Checkpoint.cs @@ -1,5 +1,3 @@ -using MySqlConnector; - namespace SurfTimer; internal class Checkpoint : PersonalBest diff --git a/src/ST-Player/Replay/ReplayPlayer.cs b/src/ST-Player/Replay/ReplayPlayer.cs index 1d40c27..98a44c4 100644 --- a/src/ST-Player/Replay/ReplayPlayer.cs +++ b/src/ST-Player/Replay/ReplayPlayer.cs @@ -1,4 +1,3 @@ -using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; using CounterStrikeSharp.API; @@ -18,9 +17,9 @@ internal class ReplayPlayer public int RepeatCount { get; set; } = -1; // Stats for replay displaying - public int Stat_MapTimeID { get; set; } = -1; public string Stat_Prefix { get; set; } = "WR"; - public string Stat_PlayerName { get; set; } = "None"; + public string Stat_PlayerName { get; set; } = "N/A"; + public int Stat_MapTimeID { get; set; } = -1; public int Stat_RunTime { get; set; } = 0; public bool Stat_IsRunning { get; set; } = false; public int Stat_RunTick { get; set; } = 0; @@ -68,7 +67,7 @@ public void SetController(CCSPlayerController c, int repeat_count = -1) public void Start() { - if (this.Controller == null) + if (!this.IsPlayable) return; this.IsPlaying = true; @@ -81,11 +80,11 @@ public void Stop() public void Pause() { - if (this.IsPlaying) - { - this.IsPaused = !this.IsPaused; - this.Stat_IsRunning = !this.Stat_IsRunning; - } + if (!this.IsPlaying) + return; + + this.IsPaused = !this.IsPaused; + this.Stat_IsRunning = !this.Stat_IsRunning; } public void Tick() @@ -95,7 +94,6 @@ public void Tick() ReplayFrame current_frame = this.Frames[this.CurrentFrameTick]; - // SOME BLASHPEMY FOR YOU if (this.FrameTickIncrement >= 0) { @@ -126,7 +124,6 @@ public void Tick() var current_pos = this.Controller!.PlayerPawn.Value!.AbsOrigin!; bool is_on_ground = (current_frame.Flags & (uint)PlayerFlags.FL_ONGROUND) != 0; - bool is_ducking = (current_frame.Flags & (uint)PlayerFlags.FL_DUCKING) != 0; Vector velocity = (current_frame.Pos - current_pos) * 64; @@ -156,13 +153,14 @@ public void LoadReplayData(TimerDatabase DB) { if (!this.IsPlayable) return; - // TODO: make query for wr too + Task dbTask = DB.Query($@" SELECT MapTimes.replay_frames, MapTimes.run_time, Player.name FROM MapTimes JOIN Player ON MapTimes.player_id = Player.id WHERE MapTimes.id={this.Stat_MapTimeID} "); + MySqlDataReader mapTimeReplay = dbTask.Result; if(!mapTimeReplay.HasRows) { @@ -191,8 +189,12 @@ private void FormatBotName() return; SchemaString bot_name = new SchemaString(this.Controller!, "m_iszPlayerName"); - // Revisit, FORMAT CORECTLLY - bot_name.Set($"[{this.Stat_Prefix}] {this.Stat_PlayerName} | {PlayerHUD.FormatTime(this.Stat_RunTime)}"); + + string replay_name = $"[{this.Stat_Prefix}] {this.Stat_PlayerName} | {PlayerHUD.FormatTime(this.Stat_RunTime)}"; + if(this.Stat_RunTime <= 0) + replay_name = $"[{this.Stat_Prefix}] {this.Stat_PlayerName}"; + + bot_name.Set(replay_name); Utilities.SetStateChanged(this.Controller!, "CBasePlayerController", "m_iszPlayerName"); } } \ No newline at end of file diff --git a/src/ST-Player/Replay/ReplayRecorder.cs b/src/ST-Player/Replay/ReplayRecorder.cs index cdbae0c..fd20ed5 100644 --- a/src/ST-Player/Replay/ReplayRecorder.cs +++ b/src/ST-Player/Replay/ReplayRecorder.cs @@ -1,5 +1,4 @@ using System.Text.Json; -using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; namespace SurfTimer; @@ -67,26 +66,4 @@ public string SerializeReplay() string replay_frames = JsonSerializer.Serialize(Frames, options); return Compressor.Compress(replay_frames); } - - /// - /// **Deprecated** - /// [ player_id | maptime_id | replay_frames ] - /// @ Adding a replay data for a run (PB/WR) - /// @ Data saved can be accessed with `ReplayPlayer.LoadReplayData` - /// - public void SaveReplayData(Player player, TimerDatabase DB) - { - JsonSerializerOptions options = new JsonSerializerOptions {WriteIndented = false, Converters = { new VectorConverter(), new QAngleConverter() }}; - string replay_frames = JsonSerializer.Serialize(Frames, options); - string compressed_replay_frames = Compressor.Compress(replay_frames); - Task updatePlayerReplayTask = DB.Write($@" - INSERT INTO `MapTimeReplay` - (`player_id`, `maptime_id`, `map_id`, `replay_frames`) - VALUES ({player.Profile.ID}, {player.Stats.PB[0].ID}, {player.CurrMap.ID}, '{compressed_replay_frames}') - ON DUPLICATE KEY UPDATE replay_frames=VALUES(replay_frames) - "); - if (updatePlayerReplayTask.Result <= 0) - throw new Exception($"CS2 Surf ERROR >> internal class PlayerReplay -> SaveReplayData -> Failed to insert/update player run in database. Player: {player.Profile.Name} ({player.Profile.SteamID})"); - updatePlayerReplayTask.Dispose(); - } } \ No newline at end of file diff --git a/src/SurfTimer.cs b/src/SurfTimer.cs index b9032b9..846a225 100644 --- a/src/SurfTimer.cs +++ b/src/SurfTimer.cs @@ -32,8 +32,6 @@ You should have received a copy of the GNU Affero General Public License using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Utils; -using CounterStrikeSharp.API.Modules.Timers; -using CounterStrikeSharp.API.Modules.Cvars; namespace SurfTimer;