using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Data.Abstractions; using Data.Models.Client; using Data.Models.Client.Stats; using Data.Models.Server; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using SharedLibraryCore; using SharedLibraryCore.Dtos; using SharedLibraryCore.Interfaces; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace IW4MAdmin.Application.Misc { /// public class ServerDataViewer : IServerDataViewer { private readonly ILogger _logger; private readonly IDataValueCache _snapshotCache; private readonly IDataValueCache _serverStatsCache; private readonly IDataValueCache> _clientHistoryCache; private readonly IDataValueCache _rankedClientsCache; private readonly TimeSpan? _cacheTimeSpan = Utilities.IsDevelopment ? TimeSpan.FromSeconds(30) : (TimeSpan?) TimeSpan.FromMinutes(10); public ServerDataViewer(ILogger logger, IDataValueCache snapshotCache, IDataValueCache serverStatsCache, IDataValueCache> clientHistoryCache, IDataValueCache rankedClientsCache) { _logger = logger; _snapshotCache = snapshotCache; _serverStatsCache = serverStatsCache; _clientHistoryCache = clientHistoryCache; _rankedClientsCache = rankedClientsCache; } public async Task<(int?, DateTime?)> MaxConcurrentClientsAsync(long? serverId = null, TimeSpan? overPeriod = null, CancellationToken token = default) { _snapshotCache.SetCacheItem(async (snapshots, cancellationToken) => { var oldestEntry = overPeriod.HasValue ? DateTime.UtcNow - overPeriod.Value : DateTime.UtcNow.AddDays(-1); int? maxClients; DateTime? maxClientsTime; if (serverId != null) { var clients = await snapshots.Where(snapshot => snapshot.ServerId == serverId) .Where(snapshot => snapshot.CapturedAt >= oldestEntry) .OrderByDescending(snapshot => snapshot.ClientCount) .Select(snapshot => new { snapshot.ClientCount, snapshot.CapturedAt }) .FirstOrDefaultAsync(cancellationToken); maxClients = clients?.ClientCount; maxClientsTime = clients?.CapturedAt; } else { var clients = await snapshots.Where(snapshot => snapshot.CapturedAt >= oldestEntry) .GroupBy(snapshot => snapshot.PeriodBlock) .Select(grp => new { ClientCount = grp.Sum(snapshot => (int?) snapshot.ClientCount), Time = grp.Max(snapshot => (DateTime?) snapshot.CapturedAt) }) .OrderByDescending(snapshot => snapshot.ClientCount) .FirstOrDefaultAsync(cancellationToken); maxClients = clients?.ClientCount; maxClientsTime = clients?.Time; } _logger.LogDebug("Max concurrent clients since {Start} is {Clients}", oldestEntry, maxClients); return (maxClients, maxClientsTime); }, nameof(MaxConcurrentClientsAsync), _cacheTimeSpan, true); try { return await _snapshotCache.GetCacheItem(nameof(MaxConcurrentClientsAsync), token); } catch (Exception ex) { _logger.LogError(ex, "Could not retrieve data for {Name}", nameof(MaxConcurrentClientsAsync)); return (null, null); } } public async Task<(int, int)> ClientCountsAsync(TimeSpan? overPeriod = null, CancellationToken token = default) { _serverStatsCache.SetCacheItem(async (set, cancellationToken) => { var count = await set.CountAsync(cancellationToken); var startOfPeriod = DateTime.UtcNow.AddHours(-overPeriod?.TotalHours ?? -24); var recentCount = await set.CountAsync(client => client.LastConnection >= startOfPeriod, cancellationToken); return (count, recentCount); }, nameof(_serverStatsCache), _cacheTimeSpan, true); try { return await _serverStatsCache.GetCacheItem(nameof(_serverStatsCache), token); } catch (Exception ex) { _logger.LogError(ex, "Could not retrieve data for {Name}", nameof(ClientCountsAsync)); return (0, 0); } } public async Task> ClientHistoryAsync(TimeSpan? overPeriod = null, CancellationToken token = default) { _clientHistoryCache.SetCacheItem(async (set, cancellationToken) => { var oldestEntry = overPeriod.HasValue ? DateTime.UtcNow - overPeriod.Value : DateTime.UtcNow.AddHours(-12); var history = await set.Where(snapshot => snapshot.CapturedAt >= oldestEntry) .Select(snapshot => new { snapshot.ServerId, snapshot.CapturedAt, snapshot.ClientCount, snapshot.ConnectionInterrupted, MapName = snapshot.Map.Name, }) .OrderBy(snapshot => snapshot.CapturedAt) .ToListAsync(cancellationToken); return history.GroupBy(snapshot => snapshot.ServerId).Select(byServer => new ClientHistoryInfo { ServerId = byServer.Key, ClientCounts = byServer.Select(snapshot => new ClientCountSnapshot { Time = snapshot.CapturedAt, ClientCount = snapshot.ClientCount, ConnectionInterrupted = snapshot.ConnectionInterrupted ?? false, Map = snapshot.MapName}).ToList() }).ToList(); }, nameof(_clientHistoryCache), TimeSpan.MaxValue); try { return await _clientHistoryCache.GetCacheItem(nameof(_clientHistoryCache), token); } catch (Exception ex) { _logger.LogError(ex, "Could not retrieve data for {Name}", nameof(ClientHistoryAsync)); return Enumerable.Empty(); } } public async Task RankedClientsCountAsync(long? serverId = null, CancellationToken token = default) { _rankedClientsCache.SetCacheItem(async (set, cancellationToken) => { var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15); return await set .Where(rating => rating.Newest) .Where(rating => rating.ServerId == serverId) .Where(rating => rating.CreatedDateTime >= fifteenDaysAgo) .Where(rating => rating.Client.Level != EFClient.Permission.Banned) .Where(rating => rating.Ranking != null) .CountAsync(cancellationToken); }, nameof(_rankedClientsCache), serverId is null ? null: new[] { (object)serverId }, _cacheTimeSpan, true); try { return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), serverId, token); } catch (Exception ex) { _logger.LogError(ex, "Could not retrieve data for {Name}", nameof(RankedClientsCountAsync)); return 0; } } } }