From 686b297d3260121393b36870a324f8d6c4311701 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Mon, 24 Jan 2022 15:08:31 -0600 Subject: [PATCH] hopeful topstats fixes --- .../Client/ServerDistributionCalculator.cs | 24 +++++----- Plugins/Stats/Helpers/StatManager.cs | 45 ++++++++++++++----- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/Plugins/Stats/Client/ServerDistributionCalculator.cs b/Plugins/Stats/Client/ServerDistributionCalculator.cs index 02ed57fd3..b7c9bfc57 100644 --- a/Plugins/Stats/Client/ServerDistributionCalculator.cs +++ b/Plugins/Stats/Client/ServerDistributionCalculator.cs @@ -22,7 +22,7 @@ namespace Stats.Client private readonly IDataValueCache> _distributionCache; - + private readonly IDataValueCache _maxZScoreCache; @@ -51,9 +51,9 @@ namespace Stats.Client var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3; var distributions = new Dictionary(); - + await LoadServers(); - + foreach (var serverId in _serverIds) { var performance = await set @@ -63,14 +63,14 @@ namespace Stats.Client .Where(s => s.Client.Level != EFClient.Permission.Banned) .Where(s => s.TimePlayed >= validPlayTime) .Where(s => s.UpdatedAt >= Extensions.FifteenDaysAgo()) - .Select(s => s.EloRating * 1/3.0 + s.Skill * 2/3.0).ToListAsync(); + .Select(s => s.EloRating * 1 / 3.0 + s.Skill * 2 / 3.0).ToListAsync(); var distributionParams = performance.GenerateDistributionParameters(); distributions.Add(serverId, distributionParams); } return distributions; }), DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromHours(1)); - + _maxZScoreCache.SetCacheItem(async (set, token) => { var validPlayTime = _configurationHandler.Configuration()?.TopPlayersMinPlayTime ?? 3600 * 3; @@ -79,13 +79,16 @@ namespace Stats.Client .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime)) .Where(s => s.Skill > 0) .Where(s => s.EloRating > 0) - .MaxAsync(s => (double?)s.ZScore, token); + .GroupBy(stat => stat.ClientId) + .Select(group => + group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed)) + .MaxAsync(avgZScore => (double?) avgZScore, token); return zScore ?? 0; }, MaxZScoreCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30)); await _distributionCache.GetCacheItem(DistributionCacheKey); - await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey); - + await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey); + /*foreach (var serverId in _serverIds) { await using var ctx = _contextFactory.CreateContext(enableTracking: true); @@ -138,6 +141,7 @@ namespace Stats.Client { return 0.0; } + var zScore = (Math.Log(value) - sdParams.Mean) / sdParams.Sigma; return zScore; } @@ -145,7 +149,7 @@ namespace Stats.Client public async Task GetRatingForZScore(double? value) { var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey); - return maxZScore == 0 ? 0 : value.GetRatingForZScore(maxZScore); + return maxZScore == 0 ? null : value.GetRatingForZScore(maxZScore); } } -} \ No newline at end of file +} diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index b3e3ac5c9..4ec34fea6 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -121,7 +121,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { return (ranking) => ranking.ServerId == serverId && ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned - && ranking.Client.LastConnection >= Extensions.FifteenDaysAgo() + && ranking.CreatedDateTime >= Extensions.FifteenDaysAgo() && ranking.ZScore != null && ranking.PerformanceMetric != null && ranking.Newest @@ -895,7 +895,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers public async Task AddStandardKill(EFClient attacker, EFClient victim) { - long serverId = GetIdForServer(attacker.CurrentServer); + var serverId = GetIdForServer(attacker.CurrentServer); var attackerStats = attacker.GetAdditionalProperty(CLIENT_STATS_KEY); var victimStats = victim.GetAdditionalProperty(CLIENT_STATS_KEY); @@ -903,6 +903,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers // update the total stats _servers[serverId].ServerStatistics.TotalKills += 1; + if (attackerStats == null) + { + _log.LogWarning("Stats for {Client} are not yet initialized", attacker.ToString()); + return; + } + + if (victimStats == null) + { + _log.LogWarning("Stats for {Client} are not yet initialized", victim.ToString()); + return; + } + // this happens when the round has changed if (attackerStats.SessionScore == 0) { @@ -961,7 +973,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers // update their performance if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= - (Utilities.IsDevelopment ? 0.5 : _configHandler.Configuration().EnableAdvancedMetrics ? 10.0 : 2.5)) + (Utilities.IsDevelopment ? 0.5 : _configHandler.Configuration().EnableAdvancedMetrics ? 5.0 : 2.5)) { try { @@ -1178,16 +1190,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers public async Task UpdateHistoricalRanking(int clientId, EFClientStatistics clientStats, long serverId) { await using var context = _contextFactory.CreateContext(); + var minPlayTime = _configHandler.Configuration().TopPlayersMinPlayTime; var performances = await context.Set() .AsNoTracking() .Where(stat => stat.ClientId == clientId) .Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking .Where(stats => stats.UpdatedAt >= Extensions.FifteenDaysAgo()) - .Where(stats => stats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime) + .Where(stats => stats.TimePlayed >= minPlayTime) .ToListAsync(); - if (clientStats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime) + if (clientStats.TimePlayed >= minPlayTime) { clientStats.ZScore = await _serverDistributionCalculator.GetZScoreForServer(serverId, clientStats.Performance); @@ -1198,7 +1211,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers _configHandler.Configuration().TopPlayersMinPlayTime, clientStats.ZScore, serverId)) .CountAsync(); - var serverRankingSnapshot = new EFClientRankingHistory() + var serverRankingSnapshot = new EFClientRankingHistory { ClientId = clientId, ServerId = serverId, @@ -1215,27 +1228,35 @@ namespace IW4MAdmin.Plugins.Stats.Helpers performances.Add(clientStats); } - if (performances.Any(performance => performance.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime)) + if (performances.Any(performance => performance.TimePlayed >= minPlayTime)) { - var aggregateZScore = performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), _configHandler.Configuration().TopPlayersMinPlayTime); + var aggregateZScore = performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), minPlayTime); int? aggregateRanking = await context.Set() .Where(stat => stat.ClientId != clientId) - .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(_configHandler.Configuration() - .TopPlayersMinPlayTime)) + .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(minPlayTime)) .GroupBy(stat => stat.ClientId) .Where(group => group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed) > aggregateZScore) .Select(c => c.Key) .CountAsync(); + + var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore); + + if (newPerformanceMetric == null) + { + _log.LogWarning("Could not determine performance metric for {Client} {AggregateZScore}", + clientStats.Client?.ToString(), aggregateZScore); + return; + } - var aggregateRankingSnapshot = new EFClientRankingHistory() + var aggregateRankingSnapshot = new EFClientRankingHistory { ClientId = clientId, ZScore = aggregateZScore, Ranking = aggregateRanking, - PerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore), + PerformanceMetric = newPerformanceMetric, Newest = true, };