From 270b36fa2696d8347e1fe3f0b856a39dc6bee0fc Mon Sep 17 00:00:00 2001 From: Dawid Bepierszcz <41084667+daffyyyy@users.noreply.github.com> Date: Sat, 20 Apr 2024 18:56:41 +0200 Subject: [PATCH] 1.3.9a **MAJOR UPDATE** - New database schema - Added `css_admins_flags` table - Added `css_unmutes` table - Added `css_unbans` table --- CS2-SimpleAdmin.cs | 23 ++- CS2-SimpleAdmin.csproj | 4 +- Commands/basebans.cs | 9 +- Commands/basecommands.cs | 6 +- Commands/basecomms.cs | 53 +++--- Database/Database.cs | 71 +++++--- Database/Migration.cs | 64 +++++++ .../001_CreateTables.sql} | 8 +- Database/Migrations/002_CreateFlagsTable.sql | 9 + .../Migrations/003_ChangeColumnsPosition.sql | 4 + .../004_MoveOldFlagsToFlagsTable.sql | 30 ++++ Database/Migrations/005_CreateUnbansTable.sql | 29 +++ Events.cs | 33 ++-- Managers/AdminSQLManager.cs | 166 ++++++++++-------- Managers/BanManager.cs | 37 +++- Managers/MuteManager.cs | 165 ++++++++++------- Managers/PlayerPenaltyManager.cs | 24 +-- Menus/AdminMenu.cs | 4 +- Menus/ManageAdminsMenu.cs | 2 +- Menus/PlayersMenu.cs | 8 +- 20 files changed, 523 insertions(+), 226 deletions(-) create mode 100644 Database/Migration.cs rename Database/{database_setup.sql => Migrations/001_CreateTables.sql} (100%) create mode 100644 Database/Migrations/002_CreateFlagsTable.sql create mode 100644 Database/Migrations/003_ChangeColumnsPosition.sql create mode 100644 Database/Migrations/004_MoveOldFlagsToFlagsTable.sql create mode 100644 Database/Migrations/005_CreateUnbansTable.sql diff --git a/CS2-SimpleAdmin.cs b/CS2-SimpleAdmin.cs index fa62329..1e29e4c 100644 --- a/CS2-SimpleAdmin.cs +++ b/CS2-SimpleAdmin.cs @@ -3,7 +3,6 @@ using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Commands.Targeting; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; -using Dapper; using Discord.Webhook; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; @@ -38,7 +37,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig "CS2-SimpleAdmin"; public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)"; public override string ModuleAuthor => "daffyy & Dliix66"; - public override string ModuleVersion => "1.3.8b"; + public override string ModuleVersion => "1.3.9a"; public CS2_SimpleAdminConfig Config { get; set; } = new(); @@ -54,7 +53,6 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig _database.DatabaseMigration()); + + /* Task.Run(async () => { try @@ -109,6 +119,7 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig bag, int playerSlot) { - List tempList = new List(); + List tempList = []; while (!bag.IsEmpty) { if (bag.TryTake(out int item) && item != playerSlot) diff --git a/CS2-SimpleAdmin.csproj b/CS2-SimpleAdmin.csproj index 4a170d3..0f6119c 100644 --- a/CS2-SimpleAdmin.csproj +++ b/CS2-SimpleAdmin.csproj @@ -10,7 +10,7 @@ - + @@ -22,7 +22,7 @@ - + diff --git a/Commands/basebans.cs b/Commands/basebans.cs index 585e91b..c4cf433 100644 --- a/Commands/basebans.cs +++ b/Commands/basebans.cs @@ -371,12 +371,14 @@ namespace CS2_SimpleAdmin [ConsoleCommand("css_unban")] [RequiresPermissions("@css/unban")] - [CommandHelper(minArgs: 1, usage: "", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] + [CommandHelper(minArgs: 1, usage: " [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnUnbanCommand(CCSPlayerController? caller, CommandInfo command) { if (_database == null) return; - string callerName = caller == null ? "Console" : caller.PlayerName; + string callerName = caller?.PlayerName ?? "Console"; + string callerSteamId = caller?.SteamID.ToString() ?? "Console"; + if (command.GetArg(1).Length <= 1) { command.ReplyToCommand($"Too short pattern to search."); @@ -384,9 +386,10 @@ namespace CS2_SimpleAdmin } string pattern = command.GetArg(1); + string reason = command.GetArg(2); BanManager _banManager = new(_database, Config); - Task.Run(async () => await _banManager.UnbanPlayer(pattern)); + Task.Run(async () => await _banManager.UnbanPlayer(pattern, callerSteamId, reason)); Helper.SendDiscordLogMessage(caller, command, _discordWebhookClientLog, _localizer); Helper.LogCommand(caller, command); diff --git a/Commands/basecommands.cs b/Commands/basecommands.cs index 8eb1c26..95e18b4 100644 --- a/Commands/basecommands.cs +++ b/Commands/basecommands.cs @@ -134,11 +134,13 @@ namespace CS2_SimpleAdmin AddAdmin(caller, steamid, name, flags, immunity, time, globalAdmin, command); } - public void AddAdmin(CCSPlayerController? caller, string steamid, string name, string flags, int immunity, int time = 0, bool globalAdmin = false, CommandInfo? command = null) + public static void AddAdmin(CCSPlayerController? caller, string steamid, string name, string flags, int immunity, int time = 0, bool globalAdmin = false, CommandInfo? command = null) { if (_database == null) return; AdminSQLManager _adminManager = new(_database); - _ = _adminManager.AddAdminBySteamId(steamid, name, flags, immunity, time, globalAdmin); + + List flagsList = flags.Split(',').Select(flag => flag.Trim()).ToList(); + _ = _adminManager.AddAdminBySteamId(steamid, name, flagsList, immunity, time, globalAdmin); if (command != null) Helper.SendDiscordLogMessage(caller, command, _discordWebhookClientLog, _localizer); diff --git a/Commands/basecomms.cs b/Commands/basecomms.cs index d66a69b..f3c3816 100644 --- a/Commands/basecomms.cs +++ b/Commands/basecomms.cs @@ -77,7 +77,7 @@ namespace CS2_SimpleAdmin if (TagsDetected) Server.ExecuteCommand($"css_tag_mute {player!.SteamID}"); - playerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Gag, DateTime.Now.AddMinutes(time), time); + PlayerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Gag, DateTime.Now.AddMinutes(time), time); if (time == 0) { if (!player!.IsBot && !player.IsHLTV) @@ -230,7 +230,7 @@ namespace CS2_SimpleAdmin if (TagsDetected) Server.ExecuteCommand($"css_tag_mute {player!.SteamID}"); - playerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Gag, DateTime.Now.AddMinutes(time), time); + PlayerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Gag, DateTime.Now.AddMinutes(time), time); } Helper.SendDiscordPenaltyMessage(caller, player, reason, time, Helper.PenaltyType.Gag, _discordWebhookClientPenalty, _localizer); @@ -252,13 +252,16 @@ namespace CS2_SimpleAdmin [ConsoleCommand("css_ungag")] [RequiresPermissions("@css/chat")] - [CommandHelper(minArgs: 1, usage: "", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] + [CommandHelper(minArgs: 1, usage: " [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnUngagCommand(CCSPlayerController? caller, CommandInfo command) { if (_database == null) return; string callerName = caller == null ? "Console" : caller.PlayerName; + string callerSteamId = caller?.SteamID.ToString() ?? "Console"; + string foundPlayerName = string.Empty; string foundPlayerSteamId64 = string.Empty; + string reason = command.GetArg(2); if (command.GetArg(1).Length <= 1) { @@ -284,7 +287,7 @@ namespace CS2_SimpleAdmin CCSPlayerController? player = matches.FirstOrDefault(); if (player != null && player.IsValid) { - playerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Gag); + PlayerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Gag); if (TagsDetected) Server.ExecuteCommand($"css_tag_unmute {player!.SteamID}"); @@ -303,7 +306,7 @@ namespace CS2_SimpleAdmin CCSPlayerController? player = matches.FirstOrDefault(); if (player != null && player.IsValid) { - playerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Gag); + PlayerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Gag); if (TagsDetected) Server.ExecuteCommand($"css_tag_unmute {player!.SteamID.ToString()}"); @@ -319,12 +322,12 @@ namespace CS2_SimpleAdmin if (found) { - Task.Run(async () => { await _muteManager.UnmutePlayer(foundPlayerSteamId64, 0); }); // Unmute by type 0 (gag) + Task.Run(async () => { await _muteManager.UnmutePlayer(foundPlayerSteamId64, callerSteamId, reason, 0); }); // Unmute by type 0 (gag) command.ReplyToCommand($"Ungaged player {foundPlayerName}."); } else { - Task.Run(async () => { await _muteManager.UnmutePlayer(pattern, 0); }); // Unmute by type 0 (gag) + Task.Run(async () => { await _muteManager.UnmutePlayer(pattern, callerSteamId, reason, 0); }); // Unmute by type 0 (gag) command.ReplyToCommand($"Ungaged offline player with pattern {pattern}."); } @@ -424,7 +427,7 @@ namespace CS2_SimpleAdmin await muteManager.MutePlayer(playerInfo, adminInfo, reason, time, 1); }); - playerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Mute, DateTime.Now.AddMinutes(time), time); + PlayerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Mute, DateTime.Now.AddMinutes(time), time); if (time == 0) { @@ -526,7 +529,7 @@ namespace CS2_SimpleAdmin return; } - playerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Mute, DateTime.Now.AddMinutes(time), time); + PlayerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Mute, DateTime.Now.AddMinutes(time), time); if (time == 0) { @@ -594,8 +597,11 @@ namespace CS2_SimpleAdmin { if (_database == null) return; string callerName = caller == null ? "Console" : caller.PlayerName; + string callerSteamId = caller?.SteamID.ToString() ?? "Console"; + string foundPlayerName = string.Empty; string foundPlayerSteamId64 = string.Empty; + string reason = command.GetArg(2); if (command.GetArg(1).Length <= 1) { @@ -609,7 +615,7 @@ namespace CS2_SimpleAdmin string pattern = command.GetArg(1); bool found = false; MuteManager _muteManager = new(_database); - PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager(); + PlayerPenaltyManager playerPenaltyManager = new(); if (Helper.IsValidSteamID64(pattern)) { @@ -619,7 +625,7 @@ namespace CS2_SimpleAdmin CCSPlayerController? player = matches.FirstOrDefault(); if (player != null && player.IsValid) { - playerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Mute); + PlayerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Mute); player.VoiceFlags = VoiceFlags.Normal; found = true; foundPlayerName = player.PlayerName; @@ -635,7 +641,7 @@ namespace CS2_SimpleAdmin CCSPlayerController? player = matches.FirstOrDefault(); if (player != null && player.IsValid) { - playerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Mute); + PlayerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Mute); player.VoiceFlags = VoiceFlags.Normal; pattern = player.SteamID.ToString(); found = true; @@ -647,12 +653,12 @@ namespace CS2_SimpleAdmin if (found) { - Task.Run(async () => { await _muteManager.UnmutePlayer(foundPlayerSteamId64, 1); }); // Unmute by type 1 (mute) + Task.Run(async () => { await _muteManager.UnmutePlayer(foundPlayerSteamId64, callerSteamId, reason, 1); }); // Unmute by type 1 (mute) command.ReplyToCommand($"Unmuted player {foundPlayerName}."); } else { - Task.Run(async () => { await _muteManager.UnmutePlayer(pattern, 1); }); // Unmute by type 1 (mute) + Task.Run(async () => { await _muteManager.UnmutePlayer(pattern, callerSteamId, reason, 1); }); // Unmute by type 1 (mute) command.ReplyToCommand($"Unmuted offline player with pattern {pattern}."); } @@ -749,7 +755,7 @@ namespace CS2_SimpleAdmin Server.ExecuteCommand($"css_tag_mute {player!.SteamID}"); player!.VoiceFlags = VoiceFlags.Muted; - playerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Silence, DateTime.Now.AddMinutes(time), time); + PlayerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Silence, DateTime.Now.AddMinutes(time), time); if (time == 0) { @@ -859,7 +865,7 @@ namespace CS2_SimpleAdmin if (TagsDetected) Server.ExecuteCommand($"css_tag_mute {player!.SteamID}"); - playerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Silence, DateTime.Now.AddMinutes(time), time); + PlayerPenaltyManager.AddPenalty(player!.Slot, PenaltyType.Silence, DateTime.Now.AddMinutes(time), time); if (time == 0) { @@ -921,13 +927,16 @@ namespace CS2_SimpleAdmin [ConsoleCommand("css_unsilence")] [RequiresPermissions("@css/chat")] - [CommandHelper(minArgs: 1, usage: "", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] + [CommandHelper(minArgs: 1, usage: " [reason]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] public void OnUnsilenceCommand(CCSPlayerController? caller, CommandInfo command) { if (_database == null) return; string callerName = caller == null ? "Console" : caller.PlayerName; + string callerSteamId = caller?.SteamID.ToString() ?? "Console"; + string foundPlayerName = string.Empty; string foundPlayerSteamId64 = string.Empty; + string reason = command.GetArg(2); if (command.GetArg(1).Length <= 1) { @@ -941,7 +950,7 @@ namespace CS2_SimpleAdmin string pattern = command.GetArg(1); bool found = false; MuteManager _muteManager = new(_database); - PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager(); + PlayerPenaltyManager playerPenaltyManager = new(); if (Helper.IsValidSteamID64(pattern)) { @@ -954,7 +963,7 @@ namespace CS2_SimpleAdmin if (TagsDetected) Server.ExecuteCommand($"css_tag_unmute {player!.SteamID}"); - playerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Silence); + PlayerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Silence); player.VoiceFlags = VoiceFlags.Normal; found = true; foundPlayerName = player.PlayerName; @@ -973,7 +982,7 @@ namespace CS2_SimpleAdmin if (TagsDetected) Server.ExecuteCommand($"css_tag_unmute {player!.SteamID}"); - playerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Silence); + PlayerPenaltyManager.RemovePenaltiesByType(player!.Slot, PenaltyType.Silence); player.VoiceFlags = VoiceFlags.Normal; pattern = player.SteamID.ToString(); found = true; @@ -985,13 +994,13 @@ namespace CS2_SimpleAdmin if (found) { - Task.Run(async () => { await _muteManager.UnmutePlayer(foundPlayerSteamId64, 2); }); // Unmute by type 2 (silence) + Task.Run(async () => { await _muteManager.UnmutePlayer(foundPlayerSteamId64, callerSteamId, reason, 2); }); // Unmute by type 2 (silence) command.ReplyToCommand($"Unsilenced player {foundPlayerName}."); return; } else { - Task.Run(async () => { await _muteManager.UnmutePlayer(pattern, 2); }); // Unmute by type 2 (silence) + Task.Run(async () => { await _muteManager.UnmutePlayer(pattern, callerSteamId, reason, 2); }); // Unmute by type 2 (silence) command.ReplyToCommand($"Unsilenced offline player with pattern {pattern}."); return; } diff --git a/Database/Database.cs b/Database/Database.cs index 521c8ca..3a2addd 100644 --- a/Database/Database.cs +++ b/Database/Database.cs @@ -1,31 +1,60 @@ using Microsoft.Extensions.Logging; using MySqlConnector; -namespace CS2_SimpleAdmin +namespace CS2_SimpleAdmin; + +public class Database(string dbConnectionString) { - public class Database + private readonly string _dbConnectionString = dbConnectionString; + + public MySqlConnection GetConnection() { - private readonly string _dbConnectionString; - - public Database(string dbConnectionString) + try { - _dbConnectionString = dbConnectionString; + var connection = new MySqlConnection(_dbConnectionString); + connection.Open(); + return connection; } - - public async Task GetConnectionAsync() + catch (Exception ex) { - try - { - var connection = new MySqlConnection(_dbConnectionString); - await connection.OpenAsync(); - return connection; - } - catch (Exception ex) - { - if (CS2_SimpleAdmin._logger != null) - CS2_SimpleAdmin._logger.LogCritical($"Unable to connect to database: {ex.Message}"); - throw; - } + if (CS2_SimpleAdmin._logger != null) + CS2_SimpleAdmin._logger.LogCritical($"Unable to connect to database: {ex.Message}"); + throw; } } -} \ No newline at end of file + + public async Task GetConnectionAsync() + { + try + { + var connection = new MySqlConnection(_dbConnectionString); + await connection.OpenAsync(); + return connection; + } + catch (Exception ex) + { + CS2_SimpleAdmin._logger?.LogCritical($"Unable to connect to database: {ex.Message}"); + throw; + } + } + + public void DatabaseMigration() + { + Migration migrator = new(this); + migrator.ExecuteMigrations(); + } + + public bool CheckDatabaseConnection() + { + using MySqlConnection connection = GetConnection(); + + try + { + return connection.Ping(); + } + catch + { + return false; + } + } +} diff --git a/Database/Migration.cs b/Database/Migration.cs new file mode 100644 index 0000000..80ade5a --- /dev/null +++ b/Database/Migration.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Logging; +using MySqlConnector; + +namespace CS2_SimpleAdmin; + +public class Migration(Database database) +{ + private readonly Database _database = database; + + public void ExecuteMigrations() + { + string migrationsDirectory = CS2_SimpleAdmin.Instance.ModuleDirectory + "/Database/Migrations"; + + var files = Directory.GetFiles(migrationsDirectory, "*.sql") + .OrderBy(f => f); + + using MySqlConnection connection = _database.GetConnection(); + + // Create sa_migrations table if not exists + using var cmd = new MySqlCommand(@" + CREATE TABLE IF NOT EXISTS `sa_migrations` ( + `id` INT PRIMARY KEY AUTO_INCREMENT, + `version` VARCHAR(255) NOT NULL + );", connection); + + cmd.ExecuteNonQuery(); + + // Get the last applied migration version + var lastAppliedVersion = GetLastAppliedVersion(connection); + + foreach (var file in files) + { + var version = Path.GetFileNameWithoutExtension(file); + + // Check if the migration has already been applied + if (string.Compare(version, lastAppliedVersion, StringComparison.OrdinalIgnoreCase) > 0) + { + var sqlScript = File.ReadAllText(file); + + using var cmdMigration = new MySqlCommand(sqlScript, connection); + cmdMigration.ExecuteNonQuery(); + + // Update the last applied migration version + UpdateLastAppliedVersion(connection, version); + + CS2_SimpleAdmin._logger?.LogInformation($"Migration \"{version}\" successfully applied."); + } + } + } + + private static string GetLastAppliedVersion(MySqlConnection connection) + { + using var cmd = new MySqlCommand("SELECT `version` FROM `sa_migrations` ORDER BY `id` DESC LIMIT 1;", connection); + var result = cmd.ExecuteScalar(); + return result?.ToString() ?? string.Empty; + } + + private static void UpdateLastAppliedVersion(MySqlConnection connection, string version) + { + using var cmd = new MySqlCommand("INSERT INTO `sa_migrations` (`version`) VALUES (@Version);", connection); + cmd.Parameters.AddWithValue("@Version", version); + cmd.ExecuteNonQuery(); + } +} diff --git a/Database/database_setup.sql b/Database/Migrations/001_CreateTables.sql similarity index 100% rename from Database/database_setup.sql rename to Database/Migrations/001_CreateTables.sql index 8bda5f7..26c50b7 100644 --- a/Database/database_setup.sql +++ b/Database/Migrations/001_CreateTables.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS `sa_bans` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - `player_steamid` VARCHAR(64), `player_name` VARCHAR(128), + `player_steamid` VARCHAR(64), `player_ip` VARCHAR(128), `admin_steamid` VARCHAR(64) NOT NULL, `admin_name` VARCHAR(128) NOT NULL, @@ -15,8 +15,8 @@ CREATE TABLE IF NOT EXISTS `sa_bans` ( CREATE TABLE IF NOT EXISTS `sa_mutes` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `player_steamid` varchar(64) NOT NULL, `player_name` varchar(128) NULL, + `player_steamid` varchar(64) NOT NULL, `admin_steamid` varchar(64) NOT NULL, `admin_name` varchar(128) NOT NULL, `reason` varchar(255) NOT NULL, @@ -31,8 +31,8 @@ CREATE TABLE IF NOT EXISTS `sa_mutes` ( CREATE TABLE IF NOT EXISTS `sa_admins` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `player_steamid` varchar(64) NOT NULL, `player_name` varchar(128) NOT NULL, + `player_steamid` varchar(64) NOT NULL, `flags` TEXT NOT NULL, `immunity` varchar(64) NOT NULL DEFAULT '0', `server_id` INT NULL, @@ -43,8 +43,8 @@ CREATE TABLE IF NOT EXISTS `sa_admins` ( CREATE TABLE IF NOT EXISTS `sa_servers` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `address` varchar(64) NOT NULL, `hostname` varchar(128) NOT NULL, + `address` varchar(64) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `address` (`address`) ) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/Database/Migrations/002_CreateFlagsTable.sql b/Database/Migrations/002_CreateFlagsTable.sql new file mode 100644 index 0000000..3c9b75b --- /dev/null +++ b/Database/Migrations/002_CreateFlagsTable.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS `sa_admins_flags` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `admin_id` int(11) NOT NULL, + `flag` varchar(64) NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`admin_id`) REFERENCES `sa_admins` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +ALTER TABLE `sa_admins` CHANGE `flags` `flags` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL; diff --git a/Database/Migrations/003_ChangeColumnsPosition.sql b/Database/Migrations/003_ChangeColumnsPosition.sql new file mode 100644 index 0000000..d25915a --- /dev/null +++ b/Database/Migrations/003_ChangeColumnsPosition.sql @@ -0,0 +1,4 @@ +ALTER TABLE `sa_bans` CHANGE `player_name` `player_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`; +ALTER TABLE `sa_mutes` CHANGE `player_name` `player_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`; +ALTER TABLE `sa_admins` CHANGE `player_name` `player_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`; +ALTER TABLE `sa_servers` CHANGE `hostname` `hostname` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL AFTER `id`; \ No newline at end of file diff --git a/Database/Migrations/004_MoveOldFlagsToFlagsTable.sql b/Database/Migrations/004_MoveOldFlagsToFlagsTable.sql new file mode 100644 index 0000000..63d42c8 --- /dev/null +++ b/Database/Migrations/004_MoveOldFlagsToFlagsTable.sql @@ -0,0 +1,30 @@ +INSERT INTO sa_admins_flags (admin_id, flag) +WITH RECURSIVE numbers AS ( + SELECT 1 AS n + UNION ALL + SELECT n + 1 FROM numbers + WHERE n < (SELECT MAX(CHAR_LENGTH(flags) - CHAR_LENGTH(REPLACE(flags, ',', '')) + 1) FROM sa_admins) +) +SELECT + min_admins.admin_id, + TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(sa_admins.flags, ',', numbers.n), ',', -1)) AS flag +FROM numbers +JOIN ( + SELECT MIN(id) AS admin_id, player_steamid, server_id + FROM sa_admins + WHERE player_steamid != 'Console' + GROUP BY player_steamid, server_id +) AS min_admins ON 1=1 +JOIN sa_admins ON CHAR_LENGTH(sa_admins.flags) - CHAR_LENGTH(REPLACE(sa_admins.flags, ',', '')) >= numbers.n - 1 + AND min_admins.player_steamid = sa_admins.player_steamid + AND (min_admins.server_id = sa_admins.server_id OR (min_admins.server_id IS NULL AND sa_admins.server_id IS NULL)) + +UNION + +SELECT + (SELECT MAX(id) + 1 FROM sa_admins WHERE server_id IS NULL) AS admin_id, + TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(sa_admins.flags, ',', numbers.n), ',', -1)) AS flag +FROM numbers +JOIN sa_admins ON CHAR_LENGTH(sa_admins.flags) - CHAR_LENGTH(REPLACE(sa_admins.flags, ',', '')) >= numbers.n - 1 + AND sa_admins.server_id IS NULL +WHERE sa_admins.player_steamid != 'Console'; diff --git a/Database/Migrations/005_CreateUnbansTable.sql b/Database/Migrations/005_CreateUnbansTable.sql new file mode 100644 index 0000000..e005441 --- /dev/null +++ b/Database/Migrations/005_CreateUnbansTable.sql @@ -0,0 +1,29 @@ +CREATE TABLE IF NOT EXISTS `sa_unbans` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `ban_id` int(11) NOT NULL, + `admin_id` int(11) NOT NULL DEFAULT 0, + `reason` varchar(255) NOT NULL DEFAULT 'Unknown', + `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE IF NOT EXISTS `sa_unmutes` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `mute_id` int(11) NOT NULL, + `admin_id` int(11) NOT NULL DEFAULT 0, + `reason` varchar(255) NOT NULL DEFAULT 'Unknown', + `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +INSERT INTO `sa_admins` (`id`, `player_name`, `player_steamid`, `flags`, `immunity`, `server_id`, `ends`, `created`) +VALUES (-1, 'Console', 'Console', '', '0', NULL, NULL, '0000-00-00 00:00:00'); + +UPDATE `sa_admins` SET `id` = 0 WHERE `id` = -1; + +ALTER TABLE `sa_bans` ADD `unban_id` INT NULL AFTER `server_id`; +ALTER TABLE `sa_mutes` ADD `unmute_id` INT NULL AFTER `server_id`; +ALTER TABLE `sa_bans` ADD FOREIGN KEY (`unban_id`) REFERENCES `sa_unbans`(`id`) ON DELETE CASCADE; +ALTER TABLE `sa_mutes` ADD FOREIGN KEY (`unmute_id`) REFERENCES `sa_unmutes`(`id`) ON DELETE CASCADE; +ALTER TABLE `sa_unbans` ADD FOREIGN KEY (`admin_id`) REFERENCES `sa_admins`(`id`) ON DELETE CASCADE; +ALTER TABLE `sa_unmutes` ADD FOREIGN KEY (`admin_id`) REFERENCES `sa_admins`(`id`) ON DELETE CASCADE; diff --git a/Events.cs b/Events.cs index 27850e0..e100d6e 100644 --- a/Events.cs +++ b/Events.cs @@ -51,7 +51,7 @@ public partial class CS2_SimpleAdmin try { PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager(); - playerPenaltyManager.RemoveAllPenalties(player.Slot); + PlayerPenaltyManager.RemoveAllPenalties(player.Slot); if (TagsDetected) { @@ -167,7 +167,7 @@ public partial class CS2_SimpleAdmin // Apply mute penalty based on mute type if (muteType == "GAG") { - playerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Gag, ends, duration); + PlayerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Gag, ends, duration); Server.NextFrame(() => { if (TagsDetected) @@ -178,7 +178,7 @@ public partial class CS2_SimpleAdmin } else if (muteType == "MUTE") { - playerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Mute, ends, duration); + PlayerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Mute, ends, duration); Server.NextFrame(() => { player.VoiceFlags = VoiceFlags.Muted; @@ -186,7 +186,7 @@ public partial class CS2_SimpleAdmin } else { - playerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Silence, ends, duration); + PlayerPenaltyManager.AddPenalty(playerInfo.Slot, PenaltyType.Silence, ends, duration); Server.NextFrame(() => { player.VoiceFlags = VoiceFlags.Muted; @@ -234,7 +234,7 @@ public partial class CS2_SimpleAdmin PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager(); - if (playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) || playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) + if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) || PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) return HookResult.Handled; return HookResult.Continue; @@ -251,7 +251,7 @@ public partial class CS2_SimpleAdmin PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager(); - if (playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) || playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) + if (PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) || PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) return HookResult.Handled; if (info.GetArg(1).StartsWith("@")) @@ -294,7 +294,7 @@ public partial class CS2_SimpleAdmin silentPlayers.Clear(); PlayerPenaltyManager playerPenaltyManager = new PlayerPenaltyManager(); - playerPenaltyManager.RemoveAllPenalties(); + PlayerPenaltyManager.RemoveAllPenalties(); _database = new(dbConnectionString); @@ -317,6 +317,7 @@ public partial class CS2_SimpleAdmin AdminSQLManager _adminManager = new(_database); BanManager _banManager = new(_database, Config); MuteManager _muteManager = new(_database); + await _banManager.ExpireOldBans(); await _muteManager.ExpireOldMutes(); await _adminManager.DeleteOldAdmins(); @@ -335,21 +336,21 @@ public partial class CS2_SimpleAdmin { foreach (CCSPlayerController player in players) { - if (playerPenaltyManager.IsSlotInPenalties(player.Slot)) + if (PlayerPenaltyManager.IsSlotInPenalties(player.Slot)) { - if (!playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute) && !playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) + if (!PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute) && !PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) player.VoiceFlags = VoiceFlags.Normal; - if (!playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) && !playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) + if (!PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) && !PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence)) { if (TagsDetected) Server.ExecuteCommand($"css_tag_unmute {player!.SteamID}"); } if ( - !playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence) && - !playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute) && - !playerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) + !PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Silence) && + !PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Mute) && + !PlayerPenaltyManager.IsPenalized(player.Slot, PenaltyType.Gag) ) { player.VoiceFlags = VoiceFlags.Normal; @@ -360,14 +361,14 @@ public partial class CS2_SimpleAdmin } } - playerPenaltyManager.RemoveExpiredPenalties(); + PlayerPenaltyManager.RemoveExpiredPenalties(); } catch (Exception) { } }); }); }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT | CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); - AddTimer(2.5f, () => + AddTimer(2.0f, () => { string? address = $"{ConVar.Find("ip")!.StringValue}:{ConVar.Find("hostport")!.GetPrimitiveValue()}"; string? hostname = ConVar.Find("hostname")!.StringValue; @@ -391,7 +392,7 @@ public partial class CS2_SimpleAdmin else { await connection.ExecuteAsync( - "UPDATE `sa_servers` SET hostname = @hostname WHERE address = @address", + "UPDATE `sa_servers` SET `hostname` = @hostname, `id` = `id` WHERE `address` = @address", new { address, hostname }); } diff --git a/Managers/AdminSQLManager.cs b/Managers/AdminSQLManager.cs index 99b3b36..b26ee30 100644 --- a/Managers/AdminSQLManager.cs +++ b/Managers/AdminSQLManager.cs @@ -6,21 +6,15 @@ using System.Collections.Concurrent; namespace CS2_SimpleAdmin; -public class AdminSQLManager +public class AdminSQLManager(Database database) { - private readonly Database _database; + private readonly Database _database = database; // Unused for now //public static readonly ConcurrentDictionary> _adminCache = new ConcurrentDictionary>(); public static readonly ConcurrentDictionary _adminCache = new(); - //public static readonly ConcurrentDictionary _adminCacheTimestamps = new ConcurrentDictionary(); - - public AdminSQLManager(Database database) - { - _database = database; - } - + /* public async Task, int)>> GetAdminFlags(string steamId) { DateTime now = DateTime.UtcNow.ToLocalTime(); @@ -35,7 +29,7 @@ public class AdminSQLManager return new List<(List, int)>(); } - List<(List, int)> filteredFlagsWithImmunity = new List<(List, int)>(); + List<(List, int)> filteredFlagsWithImmunity = []; foreach (dynamic flags in activeFlags) { @@ -61,6 +55,7 @@ public class AdminSQLManager return filteredFlagsWithImmunity; } + */ public async Task, int, DateTime?)>> GetAllPlayersFlags() { @@ -70,61 +65,74 @@ public class AdminSQLManager { await using MySqlConnection connection = await _database.GetConnectionAsync(); - string sql = "SELECT player_steamid, flags, immunity, ends FROM sa_admins WHERE (ends IS NULL OR ends > @CurrentTime) AND (server_id IS NULL OR server_id = @serverid)"; + string sql = @" + SELECT sa_admins.player_steamid, sa_admins_flags.flag, sa_admins.immunity, sa_admins.ends + FROM sa_admins_flags + JOIN sa_admins ON sa_admins_flags.admin_id = sa_admins.id + WHERE (sa_admins.ends IS NULL OR sa_admins.ends > @CurrentTime) + AND (sa_admins.server_id IS NULL OR sa_admins.server_id = @serverid) + ORDER BY sa_admins.player_steamid"; + List? activeFlags = (await connection.QueryAsync(sql, new { CurrentTime = now, serverid = CS2_SimpleAdmin.ServerId }))?.ToList(); if (activeFlags == null) { - return new List<(string, List, int, DateTime?)>(); + return []; } - List<(string, List, int, DateTime?)> filteredFlagsWithImmunity = new List<(string, List, int, DateTime?)>(); + List<(string, List, int, DateTime?)> filteredFlagsWithImmunity = []; + string currentSteamId = string.Empty; + List currentFlags = []; + int immunityValue = 0; + DateTime? ends = null; - foreach (dynamic flags in activeFlags) + foreach (dynamic flagInfo in activeFlags) { - if (flags is not IDictionary flagsDict) + if (flagInfo is not IDictionary flagInfoDict) { continue; } - if (!flagsDict.TryGetValue("player_steamid", out var steamIdObj) || - !flagsDict.TryGetValue("flags", out var flagsValueObj) || - !flagsDict.TryGetValue("immunity", out var immunityValueObj) || - !flagsDict.TryGetValue("ends", out var endsObj)) + if (!flagInfoDict.TryGetValue("player_steamid", out var steamIdObj) || + !flagInfoDict.TryGetValue("flag", out var flagObj) || + !flagInfoDict.TryGetValue("immunity", out var immunityValueObj) || + !flagInfoDict.TryGetValue("ends", out var endsObj)) { - //Console.WriteLine("One or more required keys are missing."); continue; } - DateTime? ends = null; - - if (endsObj != null) // Check if "ends" is not null + if (steamIdObj is not string steamId || + flagObj is not string flag || + !int.TryParse(immunityValueObj.ToString(), out immunityValue)) { - if (!DateTime.TryParse(endsObj.ToString(), out var parsedEnds)) - { - //Console.WriteLine("Failed to parse 'ends' value."); - continue; - } + continue; + } + if (endsObj != null && DateTime.TryParse(endsObj.ToString(), out var parsedEnds)) + { ends = parsedEnds; } - if (!(steamIdObj is string steamId) || - !(flagsValueObj is string flagsValue) || - !int.TryParse(immunityValueObj.ToString(), out var immunityValue)) + if (currentSteamId != steamId && !string.IsNullOrEmpty(currentSteamId)) { - //Console.WriteLine("Failed to parse one or more values."); - continue; + filteredFlagsWithImmunity.Add((currentSteamId, currentFlags, immunityValue, ends)); + currentFlags = []; } - filteredFlagsWithImmunity.Add((steamId, flagsValue.Split(',').ToList(), immunityValue, ends)); + currentSteamId = steamId; + currentFlags.Add(flag); + } + + if (!string.IsNullOrEmpty(currentSteamId)) + { + filteredFlagsWithImmunity.Add((currentSteamId, currentFlags, immunityValue, ends)); } return filteredFlagsWithImmunity; } catch (Exception) { - return new List<(string, List, int, DateTime?)>(); + return []; } } @@ -161,52 +169,73 @@ public class AdminSQLManager //_adminCache.TryRemove(playerSteamId, out _); - await using MySqlConnection connection = await _database.GetConnectionAsync(); - - string sql = ""; - - if (globalDelete) + try { - sql = "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID"; - } - else - { - sql = "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId"; - } + await using MySqlConnection connection = await _database.GetConnectionAsync(); + string sql = ""; - await connection.ExecuteAsync(sql, new { PlayerSteamID = playerSteamId, ServerId = CS2_SimpleAdmin.ServerId }); + if (globalDelete) + { + sql = "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID"; + } + else + { + sql = "DELETE FROM sa_admins WHERE player_steamid = @PlayerSteamID AND server_id = @ServerId"; + } + + await connection.ExecuteAsync(sql, new { PlayerSteamID = playerSteamId, CS2_SimpleAdmin.ServerId }); + } + catch { }; } - public async Task AddAdminBySteamId(string playerSteamId, string playerName, string flags, int immunity = 0, int time = 0, bool globalAdmin = false) + public async Task AddAdminBySteamId(string playerSteamId, string playerName, List flagsList, int immunity = 0, int time = 0, bool globalAdmin = false) { - if (string.IsNullOrEmpty(playerSteamId)) return; - - flags = flags.Replace(" ", ""); + if (string.IsNullOrEmpty(playerSteamId) || flagsList == null || flagsList.Count == 0) return; DateTime now = DateTime.UtcNow.ToLocalTime(); DateTime? futureTime; + if (time != 0) futureTime = now.ToLocalTime().AddMinutes(time); else futureTime = null; - await using MySqlConnection connection = await _database.GetConnectionAsync(); - - var sql = "INSERT INTO `sa_admins` (`player_steamid`, `player_name`, `flags`, `immunity`, `ends`, `created`, `server_id`) " + - "VALUES (@playerSteamid, @playerName, @flags, @immunity, @ends, @created, @serverid)"; - - int? serverId = globalAdmin ? null : CS2_SimpleAdmin.ServerId; - - await connection.ExecuteAsync(sql, new + try { - playerSteamId, - playerName, - flags, - immunity, - ends = futureTime, - created = now, - serverid = serverId - }); + await using MySqlConnection connection = await _database.GetConnectionAsync(); + + // Insert admin into sa_admins table + var insertAdminSql = "INSERT INTO `sa_admins` (`player_steamid`, `player_name`, `immunity`, `ends`, `created`, `server_id`) " + + "VALUES (@playerSteamid, @playerName, @immunity, @ends, @created, @serverid); SELECT LAST_INSERT_ID();"; + + int adminId = await connection.ExecuteScalarAsync(insertAdminSql, new + { + playerSteamId, + playerName, + immunity, + ends = futureTime, + created = now, + serverid = globalAdmin ? null : CS2_SimpleAdmin.ServerId + }); + + // Insert flags into sa_admins_flags table + foreach (var flag in flagsList) + { + Console.WriteLine(flag); + var insertFlagsSql = "INSERT INTO `sa_admins_flags` (`admin_id`, `flag`) " + + "VALUES (@adminId, @flag)"; + + await connection.ExecuteAsync(insertFlagsSql, new + { + adminId, + flag + }); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } } public async Task DeleteOldAdmins() @@ -220,8 +249,7 @@ public class AdminSQLManager } catch (Exception) { - if (CS2_SimpleAdmin._logger != null) - CS2_SimpleAdmin._logger.LogCritical("Unable to remove expired admins"); + CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired admins"); } } } \ No newline at end of file diff --git a/Managers/BanManager.cs b/Managers/BanManager.cs index ee26efb..723c393 100644 --- a/Managers/BanManager.cs +++ b/Managers/BanManager.cs @@ -173,7 +173,7 @@ internal class BanManager(Database database, CS2_SimpleAdminConfig config) return 0; } - public async Task UnbanPlayer(string playerPattern) + public async Task UnbanPlayer(string playerPattern, string adminSteamId, string reason) { if (playerPattern == null || playerPattern.Length <= 1) { @@ -183,8 +183,43 @@ internal class BanManager(Database database, CS2_SimpleAdminConfig config) { await using MySqlConnection connection = await _database.GetConnectionAsync(); + string sqlRetrieveBans = "SELECT id FROM sa_bans WHERE (player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern) AND status = 'ACTIVE'"; + var bans = await connection.QueryAsync(sqlRetrieveBans, new { pattern = playerPattern }); + + if (!bans.Any()) + return; + + string sqlAdmin = "SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId"; + string sqlInsertUnban = "INSERT INTO sa_unbans (ban_id, admin_id, reason) VALUES (@banId, @adminId, @reason); SELECT LAST_INSERT_ID();"; + + int? sqlAdminId = await connection.ExecuteScalarAsync(sqlAdmin, new { adminSteamId }); + int adminId = sqlAdminId ?? 0; + + foreach (var ban in bans) + { + int banId = ban.id; + int? unbanId; + + // Insert into sa_unbans + if (reason != null) + { + unbanId = await connection.ExecuteScalarAsync(sqlInsertUnban, new { banId, adminId, reason }); + } + else + { + sqlInsertUnban = "INSERT INTO sa_unbans (ban_id, admin_id) VALUES (@banId, @adminId); SELECT LAST_INSERT_ID();"; + unbanId = await connection.ExecuteScalarAsync(sqlInsertUnban, new { banId, adminId }); + } + + // Update sa_bans to set unban_id + string sqlUpdateBan = "UPDATE sa_bans SET status = 'UNBANNED', unban_id = @unbanId WHERE id = @banId"; + await connection.ExecuteAsync(sqlUpdateBan, new { unbanId, banId }); + } + + /* string sqlUnban = "UPDATE sa_bans SET status = 'UNBANNED' WHERE player_steamid = @pattern OR player_name = @pattern OR player_ip = @pattern AND status = 'ACTIVE'"; await connection.ExecuteAsync(sqlUnban, new { pattern = playerPattern }); + */ } catch { } diff --git a/Managers/MuteManager.cs b/Managers/MuteManager.cs index cb27c41..d9d550d 100644 --- a/Managers/MuteManager.cs +++ b/Managers/MuteManager.cs @@ -4,20 +4,14 @@ using MySqlConnector; namespace CS2_SimpleAdmin; -internal class MuteManager +internal class MuteManager(Database database) { - private readonly Database _database; - - public MuteManager(Database database) - { - _database = database; - } + private readonly Database _database = database; public async Task MutePlayer(PlayerInfo player, PlayerInfo issuer, string reason, int time = 0, int type = 0) { if (player == null || player.SteamId == null) return; - await using MySqlConnection connection = await _database.GetConnectionAsync(); DateTime now = DateTime.UtcNow.ToLocalTime(); DateTime futureTime = now.AddMinutes(time).ToLocalTime(); @@ -28,29 +22,33 @@ internal class MuteManager else if (type == 2) muteType = "SILENCE"; - var sql = "INSERT INTO `sa_mutes` (`player_steamid`, `player_name`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`) " + - "VALUES (@playerSteamid, @playerName, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @type, @serverid)"; - - await connection.ExecuteAsync(sql, new + try { - playerSteamid = player.SteamId, - playerName = player.Name, - adminSteamid = issuer.SteamId == null ? "Console" : issuer.SteamId, - adminName = issuer.SteamId == null ? "Console" : issuer.Name, - banReason = reason, - duration = time, - ends = futureTime, - created = now, - type = muteType, - serverid = CS2_SimpleAdmin.ServerId - }); + await using MySqlConnection connection = await _database.GetConnectionAsync(); + var sql = "INSERT INTO `sa_mutes` (`player_steamid`, `player_name`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`) " + + "VALUES (@playerSteamid, @playerName, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid)"; + + await connection.ExecuteAsync(sql, new + { + playerSteamid = player.SteamId, + playerName = player.Name, + adminSteamid = issuer.SteamId ?? "Console", + adminName = issuer.SteamId == null ? "Console" : issuer.Name, + muteReason = reason, + duration = time, + ends = futureTime, + created = now, + type = muteType, + serverid = CS2_SimpleAdmin.ServerId + }); + } + catch { }; } public async Task AddMuteBySteamid(string playerSteamId, PlayerInfo issuer, string reason, int time = 0, int type = 0) { if (string.IsNullOrEmpty(playerSteamId)) return; - await using MySqlConnection connection = await _database.GetConnectionAsync(); DateTime now = DateTime.UtcNow.ToLocalTime(); DateTime futureTime = now.AddMinutes(time).ToLocalTime(); @@ -61,28 +59,33 @@ internal class MuteManager else if (type == 2) muteType = "SILENCE"; - var sql = "INSERT INTO `sa_mutes` (`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`) " + - "VALUES (@playerSteamid, @adminSteamid, @adminName, @banReason, @duration, @ends, @created, @type, @serverid)"; - - await connection.ExecuteAsync(sql, new + try { - playerSteamid = playerSteamId, - adminSteamid = issuer.SteamId == null ? "Console" : issuer.SteamId, - adminName = issuer.Name == null ? "Console" : issuer.Name, - banReason = reason, - duration = time, - ends = futureTime, - created = now, - type = muteType, - serverid = CS2_SimpleAdmin.ServerId - }); + await using MySqlConnection connection = await _database.GetConnectionAsync(); + var sql = "INSERT INTO `sa_mutes` (`player_steamid`, `admin_steamid`, `admin_name`, `reason`, `duration`, `ends`, `created`, `type`, `server_id`) " + + "VALUES (@playerSteamid, @adminSteamid, @adminName, @muteReason, @duration, @ends, @created, @type, @serverid)"; + + await connection.ExecuteAsync(sql, new + { + playerSteamid = playerSteamId, + adminSteamid = issuer.SteamId ?? "Console", + adminName = issuer.Name ?? "Console", + muteReason = reason, + duration = time, + ends = futureTime, + created = now, + type = muteType, + serverid = CS2_SimpleAdmin.ServerId + }); + } + catch { }; } public async Task> IsPlayerMuted(string steamId) { if (string.IsNullOrEmpty(steamId)) { - return new List(); + return []; } #if DEBUG @@ -102,49 +105,84 @@ internal class MuteManager } catch (Exception) { - return new List(); + return []; } } public async Task GetPlayerMutes(string steamId) { - await using MySqlConnection connection = await _database.GetConnectionAsync(); + try + { + await using MySqlConnection connection = await _database.GetConnectionAsync(); - int muteCount; - string sql = "SELECT COUNT(*) FROM sa_mutes WHERE player_steamid = @PlayerSteamID"; + int muteCount; + string sql = "SELECT COUNT(*) FROM sa_mutes WHERE player_steamid = @PlayerSteamID"; - muteCount = await connection.ExecuteScalarAsync(sql, new { PlayerSteamID = steamId }); - - return muteCount; + muteCount = await connection.ExecuteScalarAsync(sql, new { PlayerSteamID = steamId }); + return muteCount; + } + catch (Exception) + { + return 0; + } } - public async Task UnmutePlayer(string playerPattern, int type = 0) + public async Task UnmutePlayer(string playerPattern, string adminSteamId, string reason, int type = 0) { if (playerPattern == null || playerPattern.Length <= 1) { return; } - await using MySqlConnection connection = await _database.GetConnectionAsync(); - - if (type == 2) + try { - string _unbanSql = "UPDATE sa_mutes SET status = 'UNMUTED' WHERE (player_steamid = @pattern OR player_name = @pattern) AND status = 'ACTIVE'"; - await connection.ExecuteAsync(_unbanSql, new { pattern = playerPattern }); + await using MySqlConnection connection = await _database.GetConnectionAsync(); - return; + string muteType = "GAG"; + if (type == 1) + { + muteType = "MUTE"; + } + else if (type == 2) + muteType = "SILENCE"; + + string sqlRetrieveMutes = "SELECT id FROM sa_mutes WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'"; + var mutes = await connection.QueryAsync(sqlRetrieveMutes, new { pattern = playerPattern, muteType }); + + if (!mutes.Any()) + return; + + string sqlAdmin = "SELECT id FROM sa_admins WHERE player_steamid = @adminSteamId"; + string sqlInsertUnmute = "INSERT INTO sa_unmutes (mute_id, admin_id, reason) VALUES (@muteId, @adminId, @reason); SELECT LAST_INSERT_ID();"; + + int? sqlAdminId = await connection.ExecuteScalarAsync(sqlAdmin, new { adminSteamId }); + int adminId = sqlAdminId ?? 0; + + foreach (var mute in mutes) + { + int muteId = mute.id; + int? unmuteId; + + // Insert into sa_unmutes + if (reason != null) + { + unmuteId = await connection.ExecuteScalarAsync(sqlInsertUnmute, new { muteId, adminId, reason }); + } + else + { + sqlInsertUnmute = "INSERT INTO sa_unmutes (muteId, admin_id) VALUES (@muteId, @adminId); SELECT LAST_INSERT_ID();"; + unmuteId = await connection.ExecuteScalarAsync(sqlInsertUnmute, new { muteId, adminId }); + } + + // Update sa_mutes to set unmute_id + string sqlUpdateMute = "UPDATE sa_mutes SET status = 'UNMUTED', unmute_id = @unmuteId WHERE id = @muteId"; + await connection.ExecuteAsync(sqlUpdateMute, new { unmuteId, muteId }); + } } - - string muteType = "GAG"; - if (type == 1) + catch (Exception ex) { - muteType = "MUTE"; + Console.WriteLine(ex); } - else if (type == 2) - muteType = "SILENCE"; - - string sqlUnban = "UPDATE sa_mutes SET status = 'UNMUTED' WHERE (player_steamid = @pattern OR player_name = @pattern) AND type = @muteType AND status = 'ACTIVE'"; - await connection.ExecuteAsync(sqlUnban, new { pattern = playerPattern, muteType }); } public async Task ExpireOldMutes() @@ -158,8 +196,7 @@ internal class MuteManager } catch (Exception) { - if (CS2_SimpleAdmin._logger != null) - CS2_SimpleAdmin._logger.LogCritical("Unable to remove expired mutes"); + CS2_SimpleAdmin._logger?.LogCritical("Unable to remove expired mutes"); } } } \ No newline at end of file diff --git a/Managers/PlayerPenaltyManager.cs b/Managers/PlayerPenaltyManager.cs index 64333f7..5a9efb8 100644 --- a/Managers/PlayerPenaltyManager.cs +++ b/Managers/PlayerPenaltyManager.cs @@ -15,13 +15,15 @@ public class PlayerPenaltyManager new ConcurrentDictionary>>(); // Add a penalty for a player - public void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationSeconds) + public static void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationSeconds) { penalties.AddOrUpdate(slot, (_) => { - var dict = new Dictionary>(); - dict[penaltyType] = new List<(DateTime, int)>() { (endDateTime, durationSeconds) }; + var dict = new Dictionary> + { + [penaltyType] = [(endDateTime, durationSeconds)] + }; return dict; }, (_, existingDict) => @@ -35,7 +37,7 @@ public class PlayerPenaltyManager }); } - public bool IsPenalized(int slot, PenaltyType penaltyType) + public static bool IsPenalized(int slot, PenaltyType penaltyType) { //Console.WriteLine($"Checking penalties for player with slot {slot} and penalty type {penaltyType}"); @@ -78,23 +80,23 @@ public class PlayerPenaltyManager } // Get the end datetime and duration of penalties for a player and penalty type - public List<(DateTime EndDateTime, int Duration)> GetPlayerPenalties(int slot, PenaltyType penaltyType) + public static List<(DateTime EndDateTime, int Duration)> GetPlayerPenalties(int slot, PenaltyType penaltyType) { if (penalties.TryGetValue(slot, out Dictionary>? penaltyDict) && penaltyDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration)>? penaltiesList) && penaltiesList != null) { return penaltiesList; } - return new List<(DateTime EndDateTime, int Duration)>(); + return []; } - public bool IsSlotInPenalties(int slot) + public static bool IsSlotInPenalties(int slot) { return penalties.ContainsKey(slot); } // Remove all penalties for a player slot - public void RemoveAllPenalties(int slot) + public static void RemoveAllPenalties(int slot) { if (penalties.ContainsKey(slot)) { @@ -103,13 +105,13 @@ public class PlayerPenaltyManager } // Remove all penalties - public void RemoveAllPenalties() + public static void RemoveAllPenalties() { penalties.Clear(); } // Remove all penalties of a selected type from a specific player - public void RemovePenaltiesByType(int slot, PenaltyType penaltyType) + public static void RemovePenaltiesByType(int slot, PenaltyType penaltyType) { if (penalties.TryGetValue(slot, out Dictionary>? penaltyDict) && penaltyDict.ContainsKey(penaltyType)) @@ -119,7 +121,7 @@ public class PlayerPenaltyManager } // Remove all expired penalties for all players and penalty types - public void RemoveExpiredPenalties() + public static void RemoveExpiredPenalties() { DateTime now = DateTime.UtcNow.ToLocalTime(); foreach (var kvp in penalties.ToList()) // Use ToList to avoid modification while iterating diff --git a/Menus/AdminMenu.cs b/Menus/AdminMenu.cs index c3754cb..4384a53 100644 --- a/Menus/AdminMenu.cs +++ b/Menus/AdminMenu.cs @@ -8,7 +8,7 @@ namespace CS2_SimpleAdmin.Menus { public static BaseMenu CreateMenu(string title) { - return CS2_SimpleAdmin.Instance.Config.UseChatMenu ? new ChatMenu(title) : new CenterHtmlMenu(title); + return CS2_SimpleAdmin.Instance.Config.UseChatMenu ? new ChatMenu(title) : new CenterHtmlMenu(title, CS2_SimpleAdmin.Instance); } public static void OpenMenu(CCSPlayerController player, BaseMenu menu) @@ -44,7 +44,7 @@ namespace CS2_SimpleAdmin.Menus new ChatMenuOptionData("Manage Server", () => ManageServerMenu.OpenMenu(admin)), new ChatMenuOptionData("Fun actions", () => FunActionsMenu.OpenMenu(admin)), }; - + List customCommands = CS2_SimpleAdmin.Instance.Config.CustomServerCommands; if (customCommands.Count > 0) { diff --git a/Menus/ManageAdminsMenu.cs b/Menus/ManageAdminsMenu.cs index ac4127f..0fb1fd0 100644 --- a/Menus/ManageAdminsMenu.cs +++ b/Menus/ManageAdminsMenu.cs @@ -69,7 +69,7 @@ namespace CS2_SimpleAdmin.Menus private static void AddAdmin(CCSPlayerController admin, CCSPlayerController player, string flag) { // TODO: Change default immunity? - CS2_SimpleAdmin.Instance.AddAdmin(admin, player.SteamID.ToString(), player.PlayerName, flag, 10); + CS2_SimpleAdmin.AddAdmin(admin, player.SteamID.ToString(), player.PlayerName, flag, 10); } private static void RemoveAdmin(CCSPlayerController admin, CCSPlayerController player) diff --git a/Menus/PlayersMenu.cs b/Menus/PlayersMenu.cs index 7d0525f..dd7989d 100644 --- a/Menus/PlayersMenu.cs +++ b/Menus/PlayersMenu.cs @@ -1,7 +1,7 @@ -using System.Web; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Admin; using CounterStrikeSharp.API.Modules.Menu; +using System.Web; namespace CS2_SimpleAdmin.Menus { @@ -32,9 +32,13 @@ namespace CS2_SimpleAdmin.Menus BaseMenu menu = AdminMenu.CreateMenu(menuName); IEnumerable players = Helper.GetValidPlayersWithBots(); + string playerName = string.Empty; + foreach (CCSPlayerController player in players) { - string optionName = HttpUtility.HtmlEncode(player.PlayerName); + playerName = player.PlayerName.Length > 26 ? player.PlayerName[..26] : player.PlayerName; + + string optionName = HttpUtility.HtmlEncode(playerName); if (enableFilter != null && enableFilter(player) == false) continue;