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 @@ - +