using System.Collections.Concurrent; using CS2_SimpleAdmin.Models; using Dapper; using Microsoft.Extensions.Logging; namespace CS2_SimpleAdmin.Managers; internal class CacheManager { private readonly ConcurrentDictionary _banCache = new(); private readonly ConcurrentDictionary ips, DateTime used_at)> _playerIpsCache = new(); private DateTime _lastUpdateTime = DateTime.MinValue; private bool _isInitialized; public async Task InitializeCacheAsync() { if (CS2_SimpleAdmin.Database == null) return; if (!CS2_SimpleAdmin.ServerLoaded) return; if (_isInitialized) return; try { await using var connection = await CS2_SimpleAdmin.Database.GetConnectionAsync(); var 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 """); var ipHistory = await connection.QueryAsync<(ulong steamid, string address, DateTime used_at)>( "SELECT steamid, address, used_at FROM sa_players_ips"); foreach (var ban in bans) { _banCache.TryAdd(ban.Id, ban); } foreach (var group in ipHistory.GroupBy(x => x.steamid)) { var ips = new HashSet(group.Select(x => x.address)); var lastUsed = group.Max(x => x.used_at); _playerIpsCache[group.Key] = (ips, lastUsed); } _lastUpdateTime = DateTime.Now; _isInitialized = true; } catch (Exception e) { Console.WriteLine(e.ToString()); } } public async Task RefreshCacheAsync() { if (CS2_SimpleAdmin.Database == null) return; if (!_isInitialized) return; 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(); var ipHistory = await connection.QueryAsync<(ulong steamid, string address, DateTime used_at)>( "SELECT steamid, address, used_at FROM sa_players_ips"); // foreach (var group in ipHistory.GroupBy(x => x.steamid)) // { // var ips = new HashSet(group.Select(x => x.address)); // var lastUsed = group.Max(x => x.used_at); // _playerIpsCache[group.Key] = (ips, lastUsed); // } var groupedData = ipHistory.GroupBy(x => x.steamid).ToList(); Parallel.ForEach(groupedData, group => { var ips = new HashSet(group.Select(x => x.address)); var lastUsed = group.Max(x => x.used_at); _playerIpsCache.AddOrUpdate( group.Key, _ => (ips, lastUsed), (_, existing) => { existing.ips.UnionWith(ips); return (existing.ips, lastUsed > existing.used_at ? lastUsed : existing.used_at); }); }); if (updatedBans.Count == 0) return; foreach (var ban in updatedBans) { _banCache.AddOrUpdate(ban.Id, ban, (_, _) => ban); } _lastUpdateTime = DateTime.Now; } catch (Exception e) { // ignored } } 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(); private bool IsIpBanned(string ipAddress) { return _banCache.Values.Any(b => b.Status == "ACTIVE" && !string.IsNullOrEmpty(b.PlayerIp) && b.PlayerIp.Equals(ipAddress, StringComparison.OrdinalIgnoreCase)); } public bool IsPlayerBanned(string? steamId, string? ipAddress) => _banCache.Values.Any(b => b.Status == "ACTIVE" && ( (steamId != null && b.PlayerSteamId != null && b.PlayerSteamId.Equals(steamId, StringComparison.OrdinalIgnoreCase)) || (ipAddress != null && b.PlayerIp != null && b.PlayerIp.Equals(ipAddress, StringComparison.OrdinalIgnoreCase)) )); public bool IsPlayerOrAnyIpBanned(ulong steamId) { var steamIdStr = steamId.ToString(); if (_banCache.Values.Any(b => b.Status == "ACTIVE" && b.PlayerSteamId?.Equals(steamIdStr, StringComparison.OrdinalIgnoreCase) == true)) { return true; } return _playerIpsCache.TryGetValue(steamId, out var ipList) && ipList.ips.Any(IsIpBanned); } public bool HasIpForPlayer(ulong steamId, string ipAddress) { return _playerIpsCache.TryGetValue(steamId, out var ipList) && ipList.ips.Contains(ipAddress); } }