From b97426313ba6ae8447247d73379fc0d82fed3ea9 Mon Sep 17 00:00:00 2001 From: Dawid Bepierszcz <41084667+daffyyyy@users.noreply.github.com> Date: Sun, 25 May 2025 01:00:14 +0200 Subject: [PATCH] 1.7.7-alpha - Fixed steamid only bans - Added missing multiservermode - Better caching --- .../013_AddNameColumnToSaPlayerIpsTable.sql | 3 +- CS2-SimpleAdmin/Events.cs | 5 +- CS2-SimpleAdmin/Helper.cs | 2 +- CS2-SimpleAdmin/Managers/CacheManager.cs | 225 +++++++++++++----- CS2-SimpleAdmin/Managers/PlayerManager.cs | 5 +- CS2-SimpleAdmin/Models/BanRecord.cs | 29 +-- 6 files changed, 173 insertions(+), 96 deletions(-) diff --git a/CS2-SimpleAdmin/Database/Migrations/013_AddNameColumnToSaPlayerIpsTable.sql b/CS2-SimpleAdmin/Database/Migrations/013_AddNameColumnToSaPlayerIpsTable.sql index 0c6bd63..17c9647 100644 --- a/CS2-SimpleAdmin/Database/Migrations/013_AddNameColumnToSaPlayerIpsTable.sql +++ b/CS2-SimpleAdmin/Database/Migrations/013_AddNameColumnToSaPlayerIpsTable.sql @@ -1,3 +1,4 @@ UPDATE `sa_players_ips` SET `address` = INET_ATON(address); ALTER TABLE `sa_players_ips` CHANGE `address` `address` INT UNSIGNED NOT NULL; -ALTER TABLE `sa_players_ips` ADD `name` VARCHAR(64) NULL DEFAULT NULL AFTER `steamid`; \ No newline at end of file +ALTER TABLE `sa_players_ips` ADD `name` VARCHAR(64) NULL DEFAULT NULL AFTER `steamid`; +ALTER TABLE `sa_players_ips` ADD INDEX(`used_at`); diff --git a/CS2-SimpleAdmin/Events.cs b/CS2-SimpleAdmin/Events.cs index d34129a..d872795 100644 --- a/CS2-SimpleAdmin/Events.cs +++ b/CS2-SimpleAdmin/Events.cs @@ -137,7 +137,10 @@ public partial class CS2_SimpleAdmin #if DEBUG Logger.LogCritical("[OnClientConnect]"); #endif - if (Config.OtherSettings.BanType == 1 && Instance.CacheManager != null && !Instance.CacheManager.IsPlayerBanned(null, ipaddress.Split(":")[0])) + if (Config.OtherSettings.BanType == 0) + return; + + if (Instance.CacheManager != null && !Instance.CacheManager.IsPlayerBanned(null, ipaddress.Split(":")[0])) return; Server.NextFrame((() => diff --git a/CS2-SimpleAdmin/Helper.cs b/CS2-SimpleAdmin/Helper.cs index a17ec62..8b81c8a 100644 --- a/CS2-SimpleAdmin/Helper.cs +++ b/CS2-SimpleAdmin/Helper.cs @@ -1001,4 +1001,4 @@ public static class IpHelper var bytes = BitConverter.GetBytes(ipAddress).Reverse().ToArray(); return new System.Net.IPAddress(bytes).ToString(); } -} +} \ No newline at end of file diff --git a/CS2-SimpleAdmin/Managers/CacheManager.cs b/CS2-SimpleAdmin/Managers/CacheManager.cs index 11fb12e..be097d0 100644 --- a/CS2-SimpleAdmin/Managers/CacheManager.cs +++ b/CS2-SimpleAdmin/Managers/CacheManager.cs @@ -8,6 +8,9 @@ namespace CS2_SimpleAdmin.Managers; internal class CacheManager: IDisposable { private readonly ConcurrentDictionary _banCache = []; + private readonly ConcurrentDictionary> _steamIdIndex = []; + private readonly ConcurrentDictionary> _ipIndex = []; + private readonly ConcurrentDictionary> _playerIpsCache = []; private HashSet _cachedIgnoredIps = []; @@ -29,24 +32,34 @@ internal class CacheManager: IDisposable .Select(IpHelper.IpToUint)); await using var connection = await CS2_SimpleAdmin.Database.GetConnectionAsync(); - var bans = await connection.QueryAsync( - """ - SELECT + List bans; + + if (CS2_SimpleAdmin.Instance.Config.MultiServerMode) + { + bans = (await connection.QueryAsync( + """ + SELECT id AS Id, - player_name AS PlayerName, player_steamid AS PlayerSteamId, player_ip AS PlayerIp, - admin_steamid AS AdminSteamId, - admin_name AS AdminName, - reason AS Reason, - duration AS Duration, - ends AS Ends, - created AS Created, - server_id AS ServerId, - status AS Status, - updated_at AS UpdatedAt - FROM sa_bans - """); + status AS Status + FROM sa_bans + """)).ToList(); + } + else + { + bans = (await connection.QueryAsync( + """ + SELECT + id AS Id, + player_steamid AS PlayerSteamId, + player_ip AS PlayerIp, + status AS Status + FROM sa_bans + WHERE server_id = @serverId + """, new {serverId = CS2_SimpleAdmin.ServerId})).ToList(); + } + var ipHistory = await connection.QueryAsync<(ulong steamid, string? name, uint address, DateTime used_at)>( "SELECT steamid, name, address, used_at FROM sa_players_ips ORDER BY used_at DESC"); @@ -89,7 +102,9 @@ internal class CacheManager: IDisposable return existingSet; }); } - + + RebuildIndexes(); + _lastUpdateTime = DateTime.Now.AddSeconds(-1); _isInitialized = true; } @@ -119,10 +134,61 @@ internal class CacheManager: IDisposable try { await using var connection = await CS2_SimpleAdmin.Database.GetConnectionAsync(); - var updatedBans = (await connection.QueryAsync( - "SELECT * FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC", - new { lastUpdate = _lastUpdateTime } - )).ToList(); + List updatedBans; + + var allIds = (await connection.QueryAsync("SELECT id FROM sa_bans")).ToHashSet(); + + if (CS2_SimpleAdmin.Instance.Config.MultiServerMode) + { + updatedBans = (await connection.QueryAsync( + """ + SELECT id AS Id, + player_steamid AS PlayerSteamId, + player_ip AS PlayerIp, + status AS Status + FROM `sa_bans` WHERE updated_at > @lastUpdate OR created > @lastUpdate ORDER BY updated_at DESC + """, + new { lastUpdate = _lastUpdateTime } + )).ToList(); + } + else + { + updatedBans = (await connection.QueryAsync( + """ + SELECT id AS Id, + 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 + """, + new { lastUpdate = _lastUpdateTime, serverId = CS2_SimpleAdmin.ServerId } + )).ToList(); + } + + foreach (var id in _banCache.Keys) + { + if (allIds.Contains(id) || !_banCache.TryRemove(id, out var ban)) continue; + + // Remove from steamIdIndex + if (!string.IsNullOrWhiteSpace(ban.PlayerSteamId) && + _steamIdIndex.TryGetValue(ban.PlayerSteamId, out var steamBans)) + { + steamBans.RemoveAll(b => b.Id == id); + if (steamBans.Count == 0) + _steamIdIndex.TryRemove(ban.PlayerSteamId, out _); + } + + // Remove from ipIndex + if (!string.IsNullOrWhiteSpace(ban.PlayerIp) && + IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt) && + _ipIndex.TryGetValue(ipUInt, out var ipBans)) + { + ipBans.RemoveAll(b => b.Id == id); + if (ipBans.Count == 0) + _ipIndex.TryRemove(ipUInt, out _); + } + } + var ipHistory = (await connection.QueryAsync<(ulong steamid, string? name, uint address, DateTime used_at)>( "SELECT steamid, name, address, used_at FROM sa_players_ips WHERE used_at >= @lastUpdate ORDER BY used_at DESC LIMIT 300", new {lastUpdate = _lastUpdateTime})).ToList(); @@ -166,7 +232,8 @@ internal class CacheManager: IDisposable { _banCache.AddOrUpdate(ban.Id, ban, (_, _) => ban); } - + + RebuildIndexes(); _lastUpdateTime = DateTime.Now.AddSeconds(-1); } catch (Exception e) @@ -174,10 +241,48 @@ internal class CacheManager: IDisposable // ignored } } + + private void RebuildIndexes() + { + _steamIdIndex.Clear(); + _ipIndex.Clear(); + + foreach (var ban in _banCache.Values) + { + if (ban.Status != "ACTIVE") + continue; + + if (!string.IsNullOrWhiteSpace(ban.PlayerSteamId)) + { + var steamId = ban.PlayerSteamId; + _steamIdIndex.AddOrUpdate( + steamId, + key => [ban], + (key, list) => + { + list.Add(ban); + return list; + }); + } + + if (ban.PlayerIp != null && + IpHelper.TryConvertIpToUint(ban.PlayerIp, out var ipUInt)) + { + _ipIndex.AddOrUpdate( + ipUInt, + key => [ban], + (key, list) => + { + list.Add(ban); + return list; + }); + } + } + } public List GetAllBans() => _banCache.Values.ToList(); public List GetActiveBans() => _banCache.Values.Where(b => b.Status == "ACTIVE").ToList(); - public List GetPlayerBansBySteamId(string steamId) => _banCache.Values.Where(b => b.PlayerSteamId == steamId).ToList(); + public List GetPlayerBansBySteamId(string steamId) => _steamIdIndex.TryGetValue(steamId, out var bans) ? bans : []; public List<(ulong SteamId, DateTime UsedAt, string PlayerName)> GetAccountsByIp(string ipAddress) { var ipAsUint = IpHelper.IpToUint(ipAddress); @@ -191,49 +296,37 @@ internal class CacheManager: IDisposable private bool IsIpBanned(string ipAddress) { + if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) return false; var ipUInt = IpHelper.IpToUint(ipAddress); - - return _banCache.Values.Any(b => - b is { Status: "ACTIVE", PlayerIp: not null } && - IpHelper.IpToUint(b.PlayerIp) == ipUInt && - !_cachedIgnoredIps.Contains(ipUInt)); + return !_cachedIgnoredIps.Contains(ipUInt) && _ipIndex.ContainsKey(ipUInt); } + public bool IsPlayerBanned(string? steamId, string? ipAddress) { + if (steamId != null && _steamIdIndex.ContainsKey(steamId)) + return true; + + if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) return false; + if (ipAddress == null) - return _banCache.Values.Any(b => - b.Status == "ACTIVE" && - steamId != null && - b.PlayerSteamId != null && - b.PlayerSteamId.Equals(steamId, StringComparison.OrdinalIgnoreCase)); + return false; if (!IpHelper.TryConvertIpToUint(ipAddress, out var ipUInt)) return false; - return _banCache.Values.Any(b => - b is { Status: "ACTIVE", PlayerIp: not null } && - ( - (steamId != null && - b.PlayerSteamId != null && - b.PlayerSteamId.Equals(steamId, StringComparison.OrdinalIgnoreCase)) - || - (IpHelper.TryConvertIpToUint(b.PlayerIp, out var bIpUint) && - bIpUint == ipUInt && - !_cachedIgnoredIps.Contains(ipUInt)) - ) - ); + return !_cachedIgnoredIps.Contains(ipUInt) && + _ipIndex.ContainsKey(ipUInt); } public bool IsPlayerOrAnyIpBanned(ulong steamId, string? ipAddress) { var steamIdStr = steamId.ToString(); - if (_banCache.Values.Any(b => - b.Status == "ACTIVE" && - b.PlayerSteamId?.Equals(steamIdStr, StringComparison.OrdinalIgnoreCase) == true)) - { + + if (_steamIdIndex.ContainsKey(steamIdStr)) return true; - } + + if (CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType == 0) return false; if (!_playerIpsCache.TryGetValue(steamId, out var ipData)) return false; @@ -245,21 +338,26 @@ internal class CacheManager: IDisposable { var ipAsUint = IpHelper.IpToUint(ipAddress); - ipData.Add(new IpRecord( - ipAsUint, - now.AddSeconds(-2), - CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown" - )); + if (!_cachedIgnoredIps.Contains(ipAsUint)) + { + ipData.Add(new IpRecord( + ipAsUint, + now.AddSeconds(-2), // artificially recent + CS2_SimpleAdmin._localizer?["sa_unknown"] ?? "Unknown" + )); + } } - return ipData.Any(x => - x.UsedAt >= cutoff && - !_cachedIgnoredIps.Contains(x.Ip) && - _banCache.Values.Any(b => - b is { Status: "ACTIVE", PlayerIp: not null } && - IpHelper.TryConvertIpToUint(b.PlayerIp, out var banIpUint) && - banIpUint == x.Ip - )); + foreach (var ipRecord in ipData) + { + if (ipRecord.UsedAt < cutoff || _cachedIgnoredIps.Contains(ipRecord.Ip)) + continue; + + if (_ipIndex.ContainsKey(ipRecord.Ip)) + return true; + } + + return false; } public bool HasIpForPlayer(ulong steamId, string ipAddress) @@ -273,6 +371,9 @@ internal class CacheManager: IDisposable private void Clear() { + _steamIdIndex.Clear(); + _ipIndex.Clear(); + _banCache.Clear(); _playerIpsCache.Clear(); _cachedIgnoredIps.Clear(); diff --git a/CS2-SimpleAdmin/Managers/PlayerManager.cs b/CS2-SimpleAdmin/Managers/PlayerManager.cs index 95f85fe..7b4c0de 100644 --- a/CS2-SimpleAdmin/Managers/PlayerManager.cs +++ b/CS2-SimpleAdmin/Managers/PlayerManager.cs @@ -313,9 +313,8 @@ public class PlayerManager return CS2_SimpleAdmin.Instance.CacheManager != null && CS2_SimpleAdmin.Instance.Config.OtherSettings.BanType switch { 0 => CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(player.SteamID.ToString(), null), - _ => CS2_SimpleAdmin.Instance.Config.OtherSettings.CheckMultiAccountsByIp - ? CS2_SimpleAdmin.Instance.CacheManager.IsPlayerOrAnyIpBanned(player.SteamID, player.IpAddress?.Split(":")[0]) - : CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(player.SteamID.ToString(), player.IpAddress?.Split(":")[0]) + _ => + CS2_SimpleAdmin.Instance.CacheManager.IsPlayerBanned(player.SteamID.ToString(), player.IpAddress?.Split(":")[0]) }; }) .ToList(); diff --git a/CS2-SimpleAdmin/Models/BanRecord.cs b/CS2-SimpleAdmin/Models/BanRecord.cs index dd7712a..479a26a 100644 --- a/CS2-SimpleAdmin/Models/BanRecord.cs +++ b/CS2-SimpleAdmin/Models/BanRecord.cs @@ -2,44 +2,17 @@ using System.ComponentModel.DataAnnotations.Schema; namespace CS2_SimpleAdmin.Models; -public record struct BanRecord +public record BanRecord { [Column("id")] public int Id { get; set; } - [Column("player_name")] - public string PlayerName { get; set; } - [Column("player_steamid")] public string? PlayerSteamId { get; set; } [Column("player_ip")] public string? PlayerIp { get; set; } - [Column("admin_steamid")] - public string AdminSteamId { get; set; } - - [Column("admin_name")] - public string AdminName { get; set; } - - [Column("reason")] - public string Reason { get; set; } - - [Column("duration")] - public int Duration { get; set; } - - [Column("ends")] - public DateTime? Ends { get; set; } - - [Column("created")] - public DateTime Created { get; set; } - - [Column("server_id")] - public int? ServerId { get; set; } - [Column("status")] public string Status { get; set; } - - [Column("updated_at")] - public DateTime UpdatedAt { get; set; } }