From 4865b762628b72aa0021c9d4012c79e2f36516f8 Mon Sep 17 00:00:00 2001 From: Dawid Bepierszcz <41084667+daffyyyy@users.noreply.github.com> Date: Thu, 13 Nov 2025 01:33:38 +0100 Subject: [PATCH] Improve ban cache sync and update dependencies Enhanced CacheManager to use database time for ban updates, improving multi-server consistency and handling of status changes. Increased PlayerManager's semaphore limit and improved player/bans refresh logic to ensure status changes are detected even when the server is empty. Updated author and version metadata, commented out duplicate event registration, and bumped CounterStrikeSharp.API dependency to 1.0.346. --- CS2-SimpleAdmin/CS2-SimpleAdmin.cs | 5 +- .../Database/MysqlDatabaseProvider.cs | 2 +- CS2-SimpleAdmin/Events.cs | 2 +- CS2-SimpleAdmin/Managers/CacheManager.cs | 84 ++++++++++++++----- CS2-SimpleAdmin/Managers/PlayerManager.cs | 31 +++---- CS2-SimpleAdmin/VERSION | 2 +- .../CS2-SimpleAdmin_FunCommands.csproj | 2 +- 7 files changed, 82 insertions(+), 46 deletions(-) diff --git a/CS2-SimpleAdmin/CS2-SimpleAdmin.cs b/CS2-SimpleAdmin/CS2-SimpleAdmin.cs index 769fd51..03483b6 100644 --- a/CS2-SimpleAdmin/CS2-SimpleAdmin.cs +++ b/CS2-SimpleAdmin/CS2-SimpleAdmin.cs @@ -21,13 +21,12 @@ public partial class CS2_SimpleAdmin : BasePlugin, IPluginConfig "CS2-SimpleAdmin" + (Helper.IsDebugBuild ? " (DEBUG)" : " (RELEASE)"); public override string ModuleDescription => "Simple admin plugin for Counter-Strike 2 :)"; - public override string ModuleAuthor => "daffyy & Dliix66"; - public override string ModuleVersion => "1.7.8-beta-5"; + public override string ModuleAuthor => "daffyy"; + public override string ModuleVersion => "1.7.8-beta-6"; public override void Load(bool hotReload) { Instance = this; - if (hotReload) { ServerLoaded = false; diff --git a/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs b/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs index 79f0ce2..6d733b9 100644 --- a/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs +++ b/CS2-SimpleAdmin/Database/MysqlDatabaseProvider.cs @@ -15,7 +15,7 @@ public class MySqlDatabaseProvider(string connectionString) : IDatabaseProvider cmd.CommandText = "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';"; await cmd.ExecuteNonQueryAsync(); - cmd.CommandText = "SET time_zone = '+00:00';"; + // cmd.CommandText = "SET time_zone = '+00:00';"; await cmd.ExecuteNonQueryAsync(); return connection; diff --git a/CS2-SimpleAdmin/Events.cs b/CS2-SimpleAdmin/Events.cs index 4a3755a..954e544 100644 --- a/CS2-SimpleAdmin/Events.cs +++ b/CS2-SimpleAdmin/Events.cs @@ -23,7 +23,7 @@ public partial class CS2_SimpleAdmin { RegisterListener(OnMapStart); // RegisterListener(OnClientConnect); - RegisterListener(OnClientConnect); + // RegisterListener(OnClientConnect); RegisterListener(OnClientConnected); RegisterListener(OnGameServerSteamAPIActivated); if (Config.OtherSettings.UserMessageGagChatType) diff --git a/CS2-SimpleAdmin/Managers/CacheManager.cs b/CS2-SimpleAdmin/Managers/CacheManager.cs index 4e580c6..fe2a3c8 100644 --- a/CS2-SimpleAdmin/Managers/CacheManager.cs +++ b/CS2-SimpleAdmin/Managers/CacheManager.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using CS2_SimpleAdmin.Database; using CS2_SimpleAdmin.Models; using Dapper; +using Microsoft.Extensions.Logging; using ZLinq; namespace CS2_SimpleAdmin.Managers; @@ -16,6 +17,7 @@ internal class CacheManager: IDisposable private HashSet _cachedIgnoredIps = []; private DateTime _lastUpdateTime = DateTime.MinValue; + private DateTime? _lastDatabaseTime = null; // Track actual time from database private bool _isInitialized; private bool _disposed; @@ -156,13 +158,20 @@ internal class CacheManager: IDisposable await using var connection = await CS2_SimpleAdmin.DatabaseProvider.CreateConnectionAsync(); IEnumerable updatedBans; + // Get current time from database in local timezone (CURRENT_TIMESTAMP uses session timezone, not UTC) + var currentDatabaseTime = await connection.QueryFirstAsync("SELECT CURRENT_TIMESTAMP"); + // Optimization: Only get IDs for comparison if we need to check for deletions // Most of the time bans are just added/updated, not deleted HashSet? allIds = null; if (CS2_SimpleAdmin.Instance.Config.MultiServerMode) { - updatedBans = (await connection.QueryAsync( + // Use previous database time or start from far past if first run + var lastCheckTime = _lastDatabaseTime ?? DateTime.MinValue; + + // Get recently updated bans by timestamp (using database time to avoid timezone issues) + var updatedBans_Query = (await connection.QueryAsync( """ SELECT id AS Id, player_name AS PlayerName, @@ -171,33 +180,68 @@ internal class CacheManager: IDisposable status AS Status FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC """, - new { lastUpdate = _lastUpdateTime } - )); + new { lastUpdate = lastCheckTime } + )).ToList(); + + // Detect changes: new bans or status changes + var updatedList = new List(); + foreach (var ban in updatedBans_Query) + { + if (!_banCache.TryGetValue(ban.Id, out var cachedBan)) + { + // New ban + updatedList.Add(ban); + } + else if (cachedBan.Status != ban.Status) + { + // Status changed + updatedList.Add(ban); + } + } - // Optimization: Only fetch all IDs if there were updates - var updatedList = updatedBans.ToList(); if (updatedList.Count > 0) { allIds = (await connection.QueryAsync("SELECT id FROM sa_bans")).ToHashSet(); } updatedBans = updatedList; + + // Update last check time to current database time + _lastDatabaseTime = currentDatabaseTime; } else { - updatedBans = (await connection.QueryAsync( + // Use previous database time or start from far past if first run + var lastCheckTime = _lastDatabaseTime ?? DateTime.MinValue; + + // Get recently updated bans for this server by timestamp (using database time to avoid timezone issues) + var updatedBans_Query = (await connection.QueryAsync( """ SELECT id AS Id, player_name AS PlayerName, player_steamid AS PlayerSteamId, player_ip AS PlayerIp, status AS Status - FROM `sa_bans` WHERE (updated_at > @lastUpdate OR created > @lastUpdate) AND server_id = @serverId ORDER BY updated_at DESC + FROM `sa_bans` WHERE server_id = @serverId AND (updated_at > @lastUpdate OR created > @lastUpdate) ORDER BY updated_at DESC """, - new { lastUpdate = _lastUpdateTime, serverId = CS2_SimpleAdmin.ServerId } - )); + new { serverId = CS2_SimpleAdmin.ServerId, lastUpdate = lastCheckTime } + )).ToList(); + + // Detect changes: new bans or status changes + var updatedList = new List(); + foreach (var ban in updatedBans_Query) + { + if (!_banCache.TryGetValue(ban.Id, out var cachedBan)) + { + // New ban + updatedList.Add(ban); + } + else if (cachedBan.Status != ban.Status) + { + // Status changed + updatedList.Add(ban); + } + } - // Optimization: Only fetch all IDs if there were updates - var updatedList = updatedBans.ToList(); if (updatedList.Count > 0) { allIds = (await connection.QueryAsync( @@ -206,6 +250,9 @@ internal class CacheManager: IDisposable )).ToHashSet(); } updatedBans = updatedList; + + // Update last check time to current database time + _lastDatabaseTime = currentDatabaseTime; } // Optimization: Only process deletions if we have the full ID list @@ -276,16 +323,13 @@ internal class CacheManager: IDisposable } // Update cache with new/modified bans - var hasUpdates = false; foreach (var ban in updatedBans) { _banCache.AddOrUpdate(ban.Id, ban, (_, _) => ban); - hasUpdates = true; } - // Always rebuild indexes if there were any updates - // This ensures status changes (ACTIVE -> UNBANNED) are reflected - if (hasUpdates) + // Rebuild indexes if there were updates + if (updatedBans.Any()) { RebuildIndexes(); } @@ -441,12 +485,12 @@ internal class CacheManager: IDisposable { _ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress)); } - + return true; } } - if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) + if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0 || string.IsNullOrEmpty(ipAddress)) return false; if (string.IsNullOrEmpty(ipAddress) || @@ -547,14 +591,14 @@ internal class CacheManager: IDisposable var activeBan = steamBans.FirstOrDefault(b => b.StatusEnum == BanStatus.ACTIVE); if (activeBan != null) { - if (string.IsNullOrEmpty(activeBan.PlayerName) || string.IsNullOrEmpty(activeBan.PlayerIp)) + if (string.IsNullOrEmpty(activeBan.PlayerName) || string.IsNullOrEmpty(activeBan.PlayerIp) && !string.IsNullOrEmpty(ipAddress)) _ = Task.Run(() => UpdatePlayerData(playerName, steamId, ipAddress)); return true; } } - if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) + if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0 || string.IsNullOrEmpty(ipAddress)) return false; if (!_playerIpsCache.TryGetValue(steamId, out var ipData)) diff --git a/CS2-SimpleAdmin/Managers/PlayerManager.cs b/CS2-SimpleAdmin/Managers/PlayerManager.cs index 1378962..3974689 100644 --- a/CS2-SimpleAdmin/Managers/PlayerManager.cs +++ b/CS2-SimpleAdmin/Managers/PlayerManager.cs @@ -13,7 +13,7 @@ namespace CS2_SimpleAdmin.Managers; internal class PlayerManager { - private readonly SemaphoreSlim _loadPlayerSemaphore = new(5); + private readonly SemaphoreSlim _loadPlayerSemaphore = new(10); private readonly CS2_SimpleAdminConfig _config = CS2_SimpleAdmin.Instance.Config; /// @@ -51,7 +51,6 @@ internal class PlayerManager try { await _loadPlayerSemaphore.WaitAsync(); - if (!CS2_SimpleAdmin.PlayersInfo.ContainsKey(steamId)) { var isBanned = CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch @@ -284,18 +283,6 @@ internal class PlayerManager #endif if (CS2_SimpleAdmin.DatabaseProvider == null) return; - - // Optimization: Get players once and avoid allocating anonymous types - var validPlayers = Helper.GetValidPlayers(); - if (validPlayers.Count == 0) - return; - - // Use ValueTuple instead of anonymous type - better performance and less allocations - var tempPlayers = new List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)>(validPlayers.Count); - foreach (var p in validPlayers) - { - tempPlayers.Add((p.PlayerName, p.SteamID, p.IpAddress, p.UserId, p.Slot)); - } var pluginInstance = CS2_SimpleAdmin.Instance; var config = _config.OtherSettings; // Cache config access @@ -304,7 +291,8 @@ internal class PlayerManager { try { - // Run all expire tasks in parallel + // Always run cache and permission refresh, regardless of player count + // This ensures bans/mutes status changes are detected even when server is empty var expireTasks = new[] { pluginInstance.BanManager.ExpireOldBans(), @@ -332,6 +320,11 @@ internal class PlayerManager if (pluginInstance.CacheManager == null) return; + // Only check players if there are any online + var validPlayers = Helper.GetValidPlayers(); + if (validPlayers.Count == 0) + return; + // Optimization: Cache ban type and multi-account check to avoid repeated config access var banType = config.BanType; var checkMultiAccounts = config.CheckMultiAccountsByIp; @@ -339,7 +332,7 @@ internal class PlayerManager var bannedPlayers = new List<(string PlayerName, ulong SteamID, string? IpAddress, int? UserId, int Slot)>(); // Manual loop instead of LINQ - better performance - foreach (var player in tempPlayers) + foreach (var player in validPlayers) { var playerName = player.PlayerName; var steamId = player.SteamID; @@ -355,7 +348,7 @@ internal class PlayerManager if (isBanned) { - bannedPlayers.Add(player); + bannedPlayers.Add((playerName, steamId, ip, player.UserId, player.Slot)); } } @@ -376,8 +369,8 @@ internal class PlayerManager if (config.TimeMode == 0) { // Optimization: Manual projection instead of LINQ - var onlinePlayers = new List<(ulong, int?, int)>(tempPlayers.Count); - foreach (var player in tempPlayers) + var onlinePlayers = new List<(ulong, int?, int)>(validPlayers.Count); + foreach (var player in validPlayers) { onlinePlayers.Add((player.SteamID, player.UserId, player.Slot)); } diff --git a/CS2-SimpleAdmin/VERSION b/CS2-SimpleAdmin/VERSION index 9403fa9..7361075 100644 --- a/CS2-SimpleAdmin/VERSION +++ b/CS2-SimpleAdmin/VERSION @@ -1 +1 @@ -1.7.8-beta-5 \ No newline at end of file +1.7.8-beta-6 \ No newline at end of file diff --git a/Modules/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands.csproj b/Modules/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands.csproj index 3990512..85c6be5 100644 --- a/Modules/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands.csproj +++ b/Modules/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands/CS2-SimpleAdmin_FunCommands.csproj @@ -14,7 +14,7 @@ - +