mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-07 21:58:06 -05:00
Additional updates to support performance bucket
This commit is contained in:
parent
e1461582fa
commit
962abcf833
@ -2659,6 +2659,7 @@
|
||||
"gewehr43_upgraded": "G115 Compressor",
|
||||
"m1a1carbine_upgraded": "Widdershins RC-1",
|
||||
"m1garand_upgraded": "M1000",
|
||||
"m1garand_gl_upgraded": "The Imploder",
|
||||
"mg42_upgraded": "Barracuda FU-A11",
|
||||
"mp40_upgraded": "The Afterburner",
|
||||
"ppsh_upgraded": "The Reaper",
|
||||
|
@ -8,6 +8,7 @@ using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Client.Stats;
|
||||
using Data.Models.Server;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
@ -25,19 +26,21 @@ namespace IW4MAdmin.Application.Misc
|
||||
private readonly IDataValueCache<EFClient, (int, int)> _serverStatsCache;
|
||||
private readonly IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> _clientHistoryCache;
|
||||
private readonly IDataValueCache<EFClientRankingHistory, int> _rankedClientsCache;
|
||||
private readonly StatManager _statManager;
|
||||
|
||||
private readonly TimeSpan? _cacheTimeSpan =
|
||||
Utilities.IsDevelopment ? TimeSpan.FromSeconds(30) : (TimeSpan?) TimeSpan.FromMinutes(10);
|
||||
|
||||
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
||||
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache, IDataValueCache<EFClientRankingHistory, int> rankedClientsCache)
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache, IDataValueCache<EFClientRankingHistory, int> rankedClientsCache, StatManager statManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_snapshotCache = snapshotCache;
|
||||
_serverStatsCache = serverStatsCache;
|
||||
_clientHistoryCache = clientHistoryCache;
|
||||
_rankedClientsCache = rankedClientsCache;
|
||||
_statManager = statManager;
|
||||
}
|
||||
|
||||
public async Task<(int?, DateTime?)>
|
||||
@ -185,30 +188,31 @@ namespace IW4MAdmin.Application.Misc
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> RankedClientsCountAsync(long? serverId = null, CancellationToken token = default)
|
||||
public async Task<int> RankedClientsCountAsync(long? serverId = null, string performanceBucket = null, CancellationToken token = default)
|
||||
{
|
||||
_rankedClientsCache.SetCacheItem((set, ids, cancellationToken) =>
|
||||
{
|
||||
long? id = null;
|
||||
string bucket = null;
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
id = (long?)ids.First();
|
||||
}
|
||||
|
||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||
return set
|
||||
.Where(rating => rating.Newest)
|
||||
.Where(rating => rating.ServerId == id)
|
||||
.Where(rating => rating.CreatedDateTime >= fifteenDaysAgo)
|
||||
.Where(rating => rating.Client.Level != EFClient.Permission.Banned)
|
||||
.Where(rating => rating.Ranking != null)
|
||||
.CountAsync(cancellationToken);
|
||||
}, nameof(_rankedClientsCache), new object[] { serverId }, _cacheTimeSpan);
|
||||
|
||||
if (ids.Count() == 2)
|
||||
{
|
||||
bucket = (string)ids.Last();
|
||||
}
|
||||
|
||||
return _statManager.GetBucketConfig(serverId)
|
||||
.ContinueWith(result => _statManager.GetTotalRankedPlayers(id, bucket), cancellationToken).Result;
|
||||
|
||||
}, nameof(_rankedClientsCache), new object[] { serverId, performanceBucket }, _cacheTimeSpan);
|
||||
|
||||
try
|
||||
{
|
||||
return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), new object[] { serverId }, token);
|
||||
return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), new object[] { serverId, performanceBucket }, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -17,6 +17,7 @@ using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Events.Game;
|
||||
using SharedLibraryCore.Events.Game.GameScript.Zombie;
|
||||
using SharedLibraryCore.Events.Management;
|
||||
using Stats.Client.Abstractions;
|
||||
using Stats.Client.Game;
|
||||
@ -109,6 +110,39 @@ public class HitCalculator : IClientStatisticCalculator
|
||||
return;
|
||||
}
|
||||
|
||||
if (coreEvent is RoundEndEvent roundEndEvent)
|
||||
{
|
||||
foreach (var client in roundEndEvent.Server.ConnectedClients)
|
||||
{
|
||||
if (!_clientHitStatistics.TryGetValue(client.ClientId, out var state))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await state.OnTransaction.WaitAsync();
|
||||
await UpdateClientStatistics(client.ClientId, state);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not handle round end calculations for client {Client}",
|
||||
client.ToString());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (state.OnTransaction.CurrentCount == 0)
|
||||
{
|
||||
state.OnTransaction.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (coreEvent is ClientStateDisposeEvent clientStateDisposeEvent)
|
||||
{
|
||||
_clientHitStatistics.Remove(clientStateDisposeEvent.Client.ClientId, out var state);
|
||||
|
@ -47,7 +47,7 @@ namespace Stats.Client
|
||||
public async Task Initialize()
|
||||
{
|
||||
await LoadServers();
|
||||
|
||||
|
||||
_distributionCache.SetCacheItem(async (set, token) =>
|
||||
{
|
||||
var validPlayTime = _configuration.TopPlayersMinPlayTime;
|
||||
@ -71,14 +71,9 @@ namespace Stats.Client
|
||||
distributions.Add(serverId.ToString(), distributionParams);
|
||||
}
|
||||
|
||||
foreach (var performanceBucketGroup in _appConfig.Servers.GroupBy(server => server.PerformanceBucket))
|
||||
foreach (var performanceBucketGroup in _appConfig.Servers.Select(server => server.PerformanceBucket).Distinct())
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(performanceBucketGroup.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var performanceBucket = performanceBucketGroup.Key;
|
||||
var performanceBucket = performanceBucketGroup ?? "null";
|
||||
|
||||
var bucketConfig =
|
||||
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
||||
@ -99,19 +94,19 @@ namespace Stats.Client
|
||||
return distributions;
|
||||
}, DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromHours(1));
|
||||
|
||||
foreach (var server in _appConfig.Servers)
|
||||
foreach (var performanceBucket in _appConfig.Servers.Select(s => s.PerformanceBucket).Distinct())
|
||||
{
|
||||
_maxZScoreCache.SetCacheItem(async (set, ids, token) =>
|
||||
{
|
||||
var validPlayTime = _configuration.TopPlayersMinPlayTime;
|
||||
var oldestStat = TimeSpan.FromSeconds(_configuration.TopPlayersMinPlayTime);
|
||||
var performanceBucket = (string)ids.FirstOrDefault();
|
||||
var oldestStat = DateTime.UtcNow - Extensions.FifteenDaysAgo();
|
||||
var perfBucket = (string)ids.FirstOrDefault();
|
||||
|
||||
if (!string.IsNullOrEmpty(performanceBucket))
|
||||
if (!string.IsNullOrEmpty(perfBucket))
|
||||
{
|
||||
var bucketConfig =
|
||||
_configuration.PerformanceBuckets.FirstOrDefault(cfg =>
|
||||
cfg.Name == performanceBucket) ?? new PerformanceBucketConfiguration();
|
||||
cfg.Name == perfBucket) ?? new PerformanceBucketConfiguration();
|
||||
|
||||
validPlayTime = (int)bucketConfig.ClientMinPlayTime.TotalSeconds;
|
||||
oldestStat = bucketConfig.RankingExpiration;
|
||||
@ -120,19 +115,18 @@ namespace Stats.Client
|
||||
var zScore = await set
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime, oldestStat))
|
||||
.Where(s => s.Skill > 0)
|
||||
.Where(s => s.EloRating >= 1)
|
||||
.Where(stat =>
|
||||
performanceBucket == null || performanceBucket == stat.Server.PerformanceBucket)
|
||||
.Where(s => s.EloRating >= 0)
|
||||
.Where(stat => perfBucket == stat.Server.PerformanceBucket)
|
||||
.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, new[] { server.PerformanceBucket },
|
||||
}, MaxZScoreCacheKey, new[] { performanceBucket },
|
||||
Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromMinutes(30));
|
||||
|
||||
await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey, new[] { server.PerformanceBucket });
|
||||
await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey, new[] { performanceBucket });
|
||||
}
|
||||
|
||||
await _distributionCache.GetCacheItem(DistributionCacheKey, new CancellationToken());
|
||||
@ -140,7 +134,7 @@ namespace Stats.Client
|
||||
/*foreach (var serverId in _serverIds)
|
||||
{
|
||||
await using var ctx = _contextFactory.CreateContext(enableTracking: true);
|
||||
|
||||
|
||||
var a = await ctx.Set<EFClientStatistics>()
|
||||
.Where(s => s.ServerId == serverId)
|
||||
//.Where(s=> s.ClientId == 216105)
|
||||
@ -150,16 +144,16 @@ namespace Stats.Client
|
||||
.Where(s => s.TimePlayed >= 3600 * 3)
|
||||
.Where(s => s.UpdatedAt >= Extensions.FifteenDaysAgo())
|
||||
.ToListAsync();
|
||||
|
||||
|
||||
var b = a.Distinct();
|
||||
|
||||
|
||||
foreach (var item in b)
|
||||
{
|
||||
await Plugin.Manager.UpdateHistoricalRanking(item.ClientId, item, item.ServerId);
|
||||
//item.ZScore = await GetZScoreForServer(serverId, item.Performance);
|
||||
//item.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}*/
|
||||
}
|
||||
@ -186,7 +180,7 @@ namespace Stats.Client
|
||||
|
||||
var serverParams = await _distributionCache.GetCacheItem(DistributionCacheKey, new CancellationToken());
|
||||
Extensions.LogParams sdParams = null;
|
||||
|
||||
|
||||
if (serverId is not null && serverParams.TryGetValue(serverId.ToString(), out var sdParams1))
|
||||
{
|
||||
sdParams = sdParams1;
|
||||
@ -210,7 +204,7 @@ namespace Stats.Client
|
||||
|
||||
public async Task<double?> GetRatingForZScore(double? value, string performanceBucket)
|
||||
{
|
||||
var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey, new [] { performanceBucket });
|
||||
var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey, new[] { performanceBucket ?? "null" });
|
||||
return maxZScore == 0 ? null : value.GetRatingForZScore(maxZScore);
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,17 @@ using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Commands;
|
||||
using Stats.Dtos;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
public class ViewStatsCommand : Command
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly StatManager _statManager;
|
||||
private readonly IResourceQueryHelper<ClientRankingInfoRequest, ClientRankingInfo> _queryHelper;
|
||||
|
||||
public ViewStatsCommand(CommandConfiguration config, ITranslationLookup translationLookup,
|
||||
IDatabaseContextFactory contextFactory, StatManager statManager) : base(config, translationLookup)
|
||||
IDatabaseContextFactory contextFactory, IResourceQueryHelper<ClientRankingInfoRequest, ClientRankingInfo> queryHelper) : base(config, translationLookup)
|
||||
{
|
||||
Name = "stats";
|
||||
Description = translationLookup["PLUGINS_STATS_COMMANDS_VIEW_DESC"];
|
||||
@ -35,7 +36,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
};
|
||||
|
||||
_contextFactory = contextFactory;
|
||||
_statManager = statManager;
|
||||
_queryHelper = queryHelper;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||
@ -54,15 +55,19 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
}
|
||||
|
||||
var serverId = (gameEvent.Owner as IGameServer).LegacyDatabaseId;
|
||||
var totalRankedPlayers = await _statManager.GetTotalRankedPlayers(serverId);
|
||||
|
||||
// getting stats for a particular client
|
||||
if (gameEvent.Target != null)
|
||||
{
|
||||
var performanceRanking = await _statManager.GetClientOverallRanking(gameEvent.Target.ClientId, serverId);
|
||||
var performanceRankingString = performanceRanking == 0
|
||||
var performanceRanking = (await _queryHelper.QueryResource(new ClientRankingInfoRequest
|
||||
{
|
||||
ClientId = gameEvent.Target.ClientId,
|
||||
ServerEndpoint = gameEvent.Owner.Id
|
||||
})).Results.First();
|
||||
|
||||
var performanceRankingString = performanceRanking.CurrentRanking == 0
|
||||
? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"]
|
||||
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} (Color::Accent)#{performanceRanking}/{totalRankedPlayers}";
|
||||
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} (Color::Accent)#{performanceRanking.CurrentRanking}/{performanceRanking.TotalRankedClients}";
|
||||
|
||||
// target is currently connected so we want their cached stats if they exist
|
||||
if (gameEvent.Owner.GetClientsAsList().Any(client => client.Equals(gameEvent.Target)))
|
||||
@ -88,10 +93,15 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
// getting self stats
|
||||
else
|
||||
{
|
||||
var performanceRanking = await _statManager.GetClientOverallRanking(gameEvent.Origin.ClientId, serverId);
|
||||
var performanceRankingString = performanceRanking == 0
|
||||
var performanceRanking = (await _queryHelper.QueryResource(new ClientRankingInfoRequest
|
||||
{
|
||||
ClientId = gameEvent.Origin.ClientId,
|
||||
ServerEndpoint = gameEvent.Owner.Id
|
||||
})).Results.First();
|
||||
|
||||
var performanceRankingString = performanceRanking.CurrentRanking == 0
|
||||
? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"]
|
||||
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} (Color::Accent)#{performanceRanking}/{totalRankedPlayers}";
|
||||
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} (Color::Accent)#{performanceRanking.CurrentRanking}/{performanceRanking.TotalRankedClients}";
|
||||
|
||||
// check if current client is connected to the server
|
||||
if (gameEvent.Owner.GetClientsAsList().Any(client => client.Equals(gameEvent.Origin)))
|
||||
|
@ -27,5 +27,6 @@ namespace Stats.Dtos
|
||||
public List<EFClientRankingHistory> Ratings { get; set; }
|
||||
public List<EFClientStatistics> LegacyStats { get; set; }
|
||||
public List<EFMeta> CustomMetrics { get; set; } = new();
|
||||
public string PerformanceBucket { get; set; }
|
||||
}
|
||||
}
|
||||
|
3
Plugins/Stats/Dtos/ClientRankingInfo.cs
Normal file
3
Plugins/Stats/Dtos/ClientRankingInfo.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace Stats.Dtos;
|
||||
|
||||
public record ClientRankingInfo(int CurrentRanking, int TotalRankedClients, string PerformanceBucket);
|
3
Plugins/Stats/Dtos/ClientRankingInfoRequest.cs
Normal file
3
Plugins/Stats/Dtos/ClientRankingInfoRequest.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace Stats.Dtos;
|
||||
|
||||
public class ClientRankingInfoRequest : StatsInfoRequest;
|
@ -7,5 +7,6 @@
|
||||
/// </summary>
|
||||
public int? ClientId { get; set; }
|
||||
public string ServerEndpoint { get; set; }
|
||||
public string PerformanceBucket { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -7,40 +7,35 @@ using Data.Models;
|
||||
using Data.Models.Client;
|
||||
using Data.Models.Client.Stats;
|
||||
using IW4MAdmin.Plugins.Stats;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using Stats.Dtos;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Stats.Helpers
|
||||
{
|
||||
public class AdvancedClientStatsResourceQueryHelper : IResourceQueryHelper<StatsInfoRequest, AdvancedStatsInfo>
|
||||
public class AdvancedClientStatsResourceQueryHelper(
|
||||
ILogger<AdvancedClientStatsResourceQueryHelper> logger,
|
||||
IDatabaseContextFactory contextFactory,
|
||||
IServerDataViewer serverDataViewer,
|
||||
StatManager statManager
|
||||
)
|
||||
: IResourceQueryHelper<StatsInfoRequest, AdvancedStatsInfo>,
|
||||
IResourceQueryHelper<ClientRankingInfoRequest, ClientRankingInfo>
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IManager _manager;
|
||||
|
||||
public AdvancedClientStatsResourceQueryHelper(ILogger<AdvancedClientStatsResourceQueryHelper> logger,
|
||||
IDatabaseContextFactory contextFactory, IManager manager)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
_logger = logger;
|
||||
_manager = manager;
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<AdvancedStatsInfo>> QueryResource(StatsInfoRequest query)
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
await using var context = contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
long? serverId = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(query.ServerEndpoint))
|
||||
{
|
||||
serverId = (await context.Servers
|
||||
.Select(server => new {server.EndPoint, server.Id})
|
||||
.Select(server => new { server.EndPoint, server.Id })
|
||||
.FirstOrDefaultAsync(server => server.EndPoint == query.ServerEndpoint))
|
||||
?.Id;
|
||||
}
|
||||
@ -77,10 +72,18 @@ namespace Stats.Helpers
|
||||
.Where(r => r.ClientId == clientInfo.ClientId)
|
||||
.Where(r => r.ServerId == serverId)
|
||||
.Where(r => r.Ranking != null)
|
||||
.Where(r => r.PerformanceBucket == query.PerformanceBucket)
|
||||
.OrderByDescending(r => r.CreatedDateTime)
|
||||
.Take(250)
|
||||
.ToListAsync();
|
||||
|
||||
var rankingInfo = (await QueryResource(new ClientRankingInfoRequest
|
||||
{
|
||||
ClientId = query.ClientId,
|
||||
ServerEndpoint = query.ServerEndpoint,
|
||||
PerformanceBucket = query.PerformanceBucket
|
||||
})).Results.First();
|
||||
|
||||
var mostRecentRanking = ratings.FirstOrDefault(ranking => ranking.Newest);
|
||||
var ranking = mostRecentRanking?.Ranking + 1;
|
||||
|
||||
@ -90,7 +93,9 @@ namespace Stats.Helpers
|
||||
.Where(stat => serverId == null || stat.ServerId == serverId)
|
||||
.ToListAsync();
|
||||
|
||||
if (mostRecentRanking != null && mostRecentRanking.CreatedDateTime < Extensions.FifteenDaysAgo())
|
||||
var bucketConfig = await statManager.GetBucketConfig(serverId);
|
||||
|
||||
if (mostRecentRanking != null && mostRecentRanking.CreatedDateTime < DateTime.UtcNow - bucketConfig.RankingExpiration)
|
||||
{
|
||||
ranking = 0;
|
||||
}
|
||||
@ -100,7 +105,7 @@ namespace Stats.Helpers
|
||||
ranking = null;
|
||||
}
|
||||
|
||||
var hitInfo = new AdvancedStatsInfo()
|
||||
var hitInfo = new AdvancedStatsInfo
|
||||
{
|
||||
ServerId = serverId,
|
||||
Performance = mostRecentRanking?.PerformanceMetric,
|
||||
@ -111,9 +116,12 @@ namespace Stats.Helpers
|
||||
Level = clientInfo.Level,
|
||||
Rating = mostRecentRanking?.PerformanceMetric,
|
||||
All = hitStats,
|
||||
Servers = _manager.GetServers()
|
||||
Servers = Plugin.ServerManager.GetServers()
|
||||
.Select(server => new ServerInfo
|
||||
{Name = server.Hostname, IPAddress = server.ListenAddress, Port = server.ListenPort, Game = (Reference.Game)server.GameName})
|
||||
{
|
||||
Name = server.Hostname, IPAddress = server.ListenAddress, Port = server.ListenPort,
|
||||
Game = (Reference.Game)server.GameName
|
||||
})
|
||||
.Where(server => server.Game == clientInfo.GameName)
|
||||
.ToList(),
|
||||
Aggregate = hitStats.FirstOrDefault(hit =>
|
||||
@ -136,24 +144,82 @@ namespace Stats.Helpers
|
||||
Ratings = ratings,
|
||||
LegacyStats = legacyStats,
|
||||
Ranking = ranking,
|
||||
TotalRankedClients = rankingInfo.TotalRankedClients,
|
||||
PerformanceBucket = rankingInfo.PerformanceBucket
|
||||
};
|
||||
|
||||
// todo: when nothign found
|
||||
return new ResourceQueryHelperResult<AdvancedStatsInfo>()
|
||||
return new ResourceQueryHelperResult<AdvancedStatsInfo>
|
||||
{
|
||||
Results = new[] {hitInfo}
|
||||
Results = new[] { hitInfo }
|
||||
};
|
||||
}
|
||||
|
||||
public static Expression<Func<EFClientStatistics, bool>> GetRankingFunc(int minPlayTime, TimeSpan expiration, double? zScore = null,
|
||||
public static Expression<Func<EFClientStatistics, bool>> GetRankingFunc(int minPlayTime, TimeSpan expiration,
|
||||
double? zScore = null,
|
||||
long? serverId = null)
|
||||
{
|
||||
var oldestStat = DateTime.UtcNow.Subtract(expiration);
|
||||
return stats => (serverId == null || stats.ServerId == serverId) &&
|
||||
stats.UpdatedAt >= oldestStat &&
|
||||
stats.Client.Level != EFClient.Permission.Banned &&
|
||||
stats.TimePlayed >= minPlayTime
|
||||
&& (zScore == null || stats.ZScore > zScore);
|
||||
stats.UpdatedAt >= oldestStat &&
|
||||
stats.Client.Level != EFClient.Permission.Banned &&
|
||||
stats.TimePlayed >= minPlayTime
|
||||
&& (zScore == null || stats.ZScore > zScore);
|
||||
}
|
||||
|
||||
public async Task<ResourceQueryHelperResult<ClientRankingInfo>> QueryResource(ClientRankingInfoRequest query)
|
||||
{
|
||||
await using var context = contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
long? serverId = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(query.ServerEndpoint))
|
||||
{
|
||||
serverId = Plugin.ServerManager.Servers.FirstOrDefault(server => server.Id == query.ServerEndpoint)
|
||||
?.LegacyDatabaseId;
|
||||
}
|
||||
|
||||
var currentRanking = 0;
|
||||
int totalRankedClients;
|
||||
string performanceBucket;
|
||||
|
||||
if (string.IsNullOrEmpty(query.PerformanceBucket) && serverId is null)
|
||||
{
|
||||
var maxPerformance = await context.Set<EFClientRankingHistory>()
|
||||
.Where(r => r.ClientId == query.ClientId)
|
||||
.Where(r => r.Ranking != null)
|
||||
.Where(r => r.ServerId == serverId)
|
||||
.Where(rating => rating.Newest)
|
||||
.GroupBy(rating => rating.PerformanceBucket)
|
||||
.Select(grp => new { grp.Key, PerformanceMetric = grp.Max(rating => rating.Ranking) })
|
||||
.Where(grp => grp.PerformanceMetric != null)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (maxPerformance is null)
|
||||
{
|
||||
currentRanking = 0;
|
||||
totalRankedClients = 0;
|
||||
performanceBucket = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRanking =
|
||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, null, maxPerformance.Key);
|
||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(null, maxPerformance.Key);
|
||||
performanceBucket = maxPerformance.Key;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
performanceBucket = query.PerformanceBucket;
|
||||
currentRanking =
|
||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, serverId, performanceBucket);
|
||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(serverId, performanceBucket);
|
||||
}
|
||||
|
||||
return new ResourceQueryHelperResult<ClientRankingInfo>
|
||||
{
|
||||
Results = [new ClientRankingInfo(currentRanking, totalRankedClients, performanceBucket)]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,18 +75,23 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// gets a ranking across all servers for given client id
|
||||
/// </summary>
|
||||
/// <param name="clientId">client id of the player</param>
|
||||
/// <param name="serverId"></param>
|
||||
/// <param name="performanceBucket"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<int> GetClientOverallRanking(int clientId, long? serverId = null)
|
||||
public async Task<int> GetClientOverallRanking(int clientId, long? serverId = null, string performanceBucket = null)
|
||||
{
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
if (_config.EnableAdvancedMetrics)
|
||||
{
|
||||
var bucketConfig = await GetBucketConfig(null, performanceBucket);
|
||||
|
||||
var clientRanking = await context.Set<EFClientRankingHistory>()
|
||||
.Where(GetNewRankingFunc(bucketConfig.RankingExpiration, bucketConfig.ClientMinPlayTime, serverId, performanceBucket))
|
||||
.Where(r => r.ClientId == clientId)
|
||||
.Where(r => r.ServerId == serverId)
|
||||
.Where(r => r.Newest)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return clientRanking?.Ranking + 1 ?? 0;
|
||||
}
|
||||
|
||||
@ -110,7 +115,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Expression<Func<EFClientRankingHistory, bool>> GetNewRankingFunc(TimeSpan oldestStat, TimeSpan minPlayTime, long? serverId = null)
|
||||
private Expression<Func<EFClientRankingHistory, bool>> GetNewRankingFunc(TimeSpan oldestStat, TimeSpan minPlayTime, long? serverId = null, string performanceBucket = null)
|
||||
{
|
||||
var oldestDate = DateTime.UtcNow - oldestStat;
|
||||
return ranking => ranking.ServerId == serverId
|
||||
@ -119,12 +124,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
&& ranking.ZScore != null
|
||||
&& ranking.PerformanceMetric != null
|
||||
&& ranking.Newest
|
||||
&& ranking.PerformanceBucket == performanceBucket
|
||||
&& ranking.Client.TotalConnectionTime >= (int)minPlayTime.TotalSeconds;
|
||||
}
|
||||
|
||||
public async Task<int> GetTotalRankedPlayers(long serverId)
|
||||
public async Task<int> GetTotalRankedPlayers(long? serverId = null, string performanceBucket = null)
|
||||
{
|
||||
var bucketConfig = await GetBucketConfig(serverId);
|
||||
var bucketConfig = await GetBucketConfig(serverId, performanceBucket);
|
||||
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
|
||||
@ -150,7 +156,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
await using var context = _contextFactory.CreateContext(false);
|
||||
var clientIdsList = await context.Set<EFClientRankingHistory>()
|
||||
.Where(GetNewRankingFunc(bucketConfig.RankingExpiration, bucketConfig.ClientMinPlayTime, serverId: serverId))
|
||||
.Where(GetNewRankingFunc(bucketConfig.RankingExpiration, bucketConfig.ClientMinPlayTime, serverId: serverId, performanceBucket))
|
||||
.OrderByDescending(ranking => ranking.PerformanceMetric)
|
||||
.Select(ranking => ranking.ClientId)
|
||||
.Skip(start)
|
||||
@ -164,6 +170,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
var eachRank = await context.Set<EFClientRankingHistory>()
|
||||
.Where(ranking => ranking.ClientId == clientId)
|
||||
.Where(ranking => ranking.ServerId == serverId)
|
||||
.Where(ranking => ranking.PerformanceBucket == performanceBucket)
|
||||
.OrderByDescending(ranking => ranking.CreatedDateTime)
|
||||
.Select(ranking => new RankingSnapshot
|
||||
{
|
||||
@ -177,16 +184,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
})
|
||||
.Take(60)
|
||||
.ToListAsync();
|
||||
|
||||
if (rankingsDict.ContainsKey(clientId))
|
||||
|
||||
if (!rankingsDict.TryAdd(clientId, eachRank))
|
||||
{
|
||||
rankingsDict[clientId] = rankingsDict[clientId].Concat(eachRank).Distinct()
|
||||
.OrderByDescending(ranking => ranking.CreatedDateTime).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
rankingsDict.Add(clientId, eachRank);
|
||||
}
|
||||
}
|
||||
|
||||
var statsInfo = await context.Set<EFClientStatistics>()
|
||||
@ -224,7 +227,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
RatingChange = (rankingsDict[s.ClientId].Last().Ranking -
|
||||
rankingsDict[s.ClientId].First().Ranking) ?? 0,
|
||||
PerformanceHistory = rankingsDict[s.ClientId].Select(ranking => new PerformanceHistory
|
||||
{Performance = ranking.PerformanceMetric ?? 0, OccurredAt = ranking.CreatedDateTime})
|
||||
{ Performance = ranking.PerformanceMetric ?? 0, OccurredAt = ranking.CreatedDateTime })
|
||||
.ToList(),
|
||||
TimePlayed = Math.Round(s.TotalTimePlayed / 3600.0, 1).ToString("#,##0"),
|
||||
TimePlayedValue = TimeSpan.FromSeconds(s.TotalTimePlayed),
|
||||
@ -281,7 +284,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return finished;
|
||||
}
|
||||
|
||||
private async Task<PerformanceBucketConfiguration> GetBucketConfig(long? serverId)
|
||||
public async Task<PerformanceBucketConfiguration> GetBucketConfig(long? serverId = null,
|
||||
string bucketName = null)
|
||||
{
|
||||
var defaultConfig = new PerformanceBucketConfiguration
|
||||
{
|
||||
@ -289,11 +293,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
RankingExpiration = DateTime.UtcNow - Extensions.FifteenDaysAgo()
|
||||
};
|
||||
|
||||
if (serverId is null)
|
||||
if (serverId is null && bucketName is null)
|
||||
{
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
if (bucketName is not null)
|
||||
{
|
||||
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Name == bucketName) ??
|
||||
defaultConfig;
|
||||
}
|
||||
|
||||
var performanceBucket =
|
||||
(await _serverCache.FirstAsync(server => server.Id == serverId)).PerformanceBucket;
|
||||
|
||||
@ -306,11 +316,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
defaultConfig;
|
||||
}
|
||||
|
||||
public async Task<List<TopStatsInfo>> GetTopStats(int start, int count, long? serverId = null)
|
||||
public async Task<List<TopStatsInfo>> GetTopStats(int start, int count, long? serverId = null, string performanceBucket = null)
|
||||
{
|
||||
if (_config.EnableAdvancedMetrics)
|
||||
{
|
||||
return await GetNewTopStats(start, count, serverId);
|
||||
return await GetNewTopStats(start, count, serverId, performanceBucket);
|
||||
}
|
||||
|
||||
await using var context = _contextFactory.CreateContext(enableTracking: false);
|
||||
@ -355,7 +365,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
.Select(grp => new
|
||||
{
|
||||
grp.Key,
|
||||
Ratings = grp.Select(r => new {r.Performance, r.Ranking, r.When})
|
||||
Ratings = grp.Select(r => new { r.Performance, r.Ranking, r.When })
|
||||
});
|
||||
|
||||
var iqStatsInfo = (from stat in context.Set<EFClientStatistics>()
|
||||
@ -393,7 +403,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
ratingInfo.First(r => r.Key == s.ClientId).Ratings.Last().Ranking,
|
||||
PerformanceHistory = ratingInfo.First(r => r.Key == s.ClientId).Ratings.Count() > 1
|
||||
? ratingInfo.First(r => r.Key == s.ClientId).Ratings.OrderBy(r => r.When)
|
||||
.Select(r => new PerformanceHistory {Performance = r.Performance, OccurredAt = r.When})
|
||||
.Select(r => new PerformanceHistory { Performance = r.Performance, OccurredAt = r.When })
|
||||
.ToList()
|
||||
: new List<PerformanceHistory>
|
||||
{
|
||||
@ -1007,7 +1017,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
victimStats.LastScore = estimatedVictimScore;
|
||||
|
||||
// show encouragement/discouragement
|
||||
var streakMessage = attackerStats.ClientId != victimStats.ClientId
|
||||
var streakMessage = attacker.CurrentServer.IsZombieServer() ? string.Empty : attackerStats.ClientId != victimStats.ClientId
|
||||
? StreakMessage.MessageOnStreak(attackerStats.KillStreak, attackerStats.DeathStreak, _config)
|
||||
: StreakMessage.MessageOnStreak(-1, -1, _config);
|
||||
|
||||
@ -1258,6 +1268,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
.Include(stat => stat.Server)
|
||||
.Where(stat => stat.ClientId == clientId)
|
||||
.Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking
|
||||
.Where(stat => stat.Server.PerformanceBucket == bucketConfig.Name)
|
||||
.Where(stats => stats.UpdatedAt >= oldestStateDate)
|
||||
.Where(stats => stats.TimePlayed >= (int)bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||
.ToListAsync();
|
||||
@ -1278,11 +1289,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
private async Task UpdateAggregateForServerOrBucket(int clientId, EFClientStatistics clientStats, DatabaseContext context, List<EFClientStatistics> performances, PerformanceBucketConfiguration bucketConfig)
|
||||
{
|
||||
var aggregateZScore =
|
||||
performances.Where(performance => performance.Server.PerformanceBucket == bucketConfig.Name)
|
||||
.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), (int)bucketConfig.ClientMinPlayTime.TotalSeconds);
|
||||
performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), (int)bucketConfig.ClientMinPlayTime.TotalSeconds);
|
||||
|
||||
int? aggregateRanking = await context.Set<EFClientStatistics>()
|
||||
.Where(stat => stat.ClientId != clientId)
|
||||
.Where(stat => bucketConfig.Name == stat.Server.PerformanceBucket)
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc((int)bucketConfig.ClientMinPlayTime.TotalSeconds, bucketConfig.RankingExpiration))
|
||||
.GroupBy(stat => stat.ClientId)
|
||||
.Where(group =>
|
||||
|
@ -46,6 +46,7 @@ public class Plugin : IPluginV2
|
||||
private readonly IServerDataViewer _serverDataViewer;
|
||||
private readonly StatsConfiguration _statsConfig;
|
||||
private readonly StatManager _statManager;
|
||||
private readonly IResourceQueryHelper<ClientRankingInfoRequest, ClientRankingInfo> _queryHelper;
|
||||
|
||||
public static void RegisterDependencies(IServiceCollection serviceCollection)
|
||||
{
|
||||
@ -57,8 +58,9 @@ public class Plugin : IPluginV2
|
||||
ITranslationLookup translationLookup, IMetaServiceV2 metaService,
|
||||
IResourceQueryHelper<ChatSearchQuery, MessageResponse> chatQueryHelper,
|
||||
IEnumerable<IClientStatisticCalculator> statCalculators,
|
||||
IServerDistributionCalculator serverDistributionCalculator, IServerDataViewer serverDataViewer,
|
||||
StatsConfiguration statsConfig, StatManager statManager)
|
||||
IServerDistributionCalculator serverDistributionCalculator,
|
||||
StatsConfiguration statsConfig, StatManager statManager,
|
||||
IResourceQueryHelper<ClientRankingInfoRequest, ClientRankingInfo> queryHelper)
|
||||
{
|
||||
_databaseContextFactory = databaseContextFactory;
|
||||
_translationLookup = translationLookup;
|
||||
@ -70,6 +72,7 @@ public class Plugin : IPluginV2
|
||||
_serverDataViewer = serverDataViewer;
|
||||
_statsConfig = statsConfig;
|
||||
_statManager = statManager;
|
||||
_queryHelper = queryHelper;
|
||||
|
||||
IGameServerEventSubscriptions.MonitoringStopped +=
|
||||
async (monitorEvent, token) => await _statManager.Sync(monitorEvent.Server, token);
|
||||
@ -117,7 +120,7 @@ public class Plugin : IPluginV2
|
||||
}
|
||||
};
|
||||
IGameEventSubscriptions.MatchEnded += OnMatchEvent;
|
||||
IGameEventSubscriptions.RoundEnded += (roundEndedEvent, token) => _statManager.Sync(roundEndedEvent.Server, token);
|
||||
IGameEventSubscriptions.RoundEnded += OnRoundEnded;
|
||||
IGameEventSubscriptions.MatchStarted += OnMatchEvent;
|
||||
IGameEventSubscriptions.ScriptEventTriggered += OnScriptEvent;
|
||||
IGameEventSubscriptions.ClientKilled += OnClientKilled;
|
||||
@ -126,6 +129,16 @@ public class Plugin : IPluginV2
|
||||
IManagementEventSubscriptions.Load += OnLoad;
|
||||
}
|
||||
|
||||
private async Task OnRoundEnded(RoundEndEvent roundEndedEvent, CancellationToken token)
|
||||
{
|
||||
await _statManager.Sync(roundEndedEvent.Server, token);
|
||||
|
||||
foreach (var calculator in _statCalculators)
|
||||
{
|
||||
await calculator.CalculateForEvent(roundEndedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnClientKilled(ClientKillEvent killEvent, CancellationToken token)
|
||||
{
|
||||
if (!ShouldIgnoreEvent(killEvent.Attacker, killEvent.Victim))
|
||||
@ -258,7 +271,10 @@ public class Plugin : IPluginV2
|
||||
var performance =
|
||||
Math.Round(validPerformanceValues.Sum(c => c.Performance * c.TimePlayed / performancePlayTime), 2);
|
||||
var spm = Math.Round(clientStats.Sum(c => c.SPM) / clientStats.Count(c => c.SPM > 0), 1);
|
||||
var overallRanking = await _statManager.GetClientOverallRanking(request.ClientId);
|
||||
var ranking = (await _queryHelper.QueryResource(new ClientRankingInfoRequest
|
||||
{
|
||||
ClientId = request.ClientId,
|
||||
})).Results.First();
|
||||
|
||||
return new List<InformationResponse>
|
||||
{
|
||||
@ -267,12 +283,12 @@ public class Plugin : IPluginV2
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING"],
|
||||
Value = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING_FORMAT"]
|
||||
.FormatExt(
|
||||
(overallRanking == 0
|
||||
(ranking.CurrentRanking == 0
|
||||
? "--"
|
||||
: overallRanking.ToString("#,##0",
|
||||
: ranking.CurrentRanking.ToString("#,##0",
|
||||
new System.Globalization.CultureInfo(Utilities.CurrentLocalization
|
||||
.LocalizationName))),
|
||||
(await _serverDataViewer.RankedClientsCountAsync(token: token)).ToString("#,##0",
|
||||
ranking.TotalRankedClients.ToString("#,##0",
|
||||
new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))
|
||||
),
|
||||
Column = 0,
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
|
||||
namespace SharedLibraryCore.Events;
|
||||
|
||||
@ -31,8 +32,7 @@ public static class EventExtensions
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// todo: static logger
|
||||
Console.WriteLine("InvokeAsync: " + ex);
|
||||
Log.Error(ex, "InvokeAsync for event type {EventType} failed. Cancellation Token is None", typeof(TEventType).Name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,8 +46,8 @@ public static class EventExtensions
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// todo: static logger
|
||||
Console.WriteLine("InvokeAsync: " + ex);
|
||||
Log.Error(ex, "InvokeAsync for event type {EventType} failed. IsCancellationRequested is {TokenStatus}",
|
||||
typeof(TEventType).Name, tokenSource.Token.IsCancellationRequested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,9 @@ namespace SharedLibraryCore.Interfaces
|
||||
/// Retrieves the number of ranked clients for given server id
|
||||
/// </summary>
|
||||
/// <param name="serverId">ServerId to query on</param>
|
||||
/// <param name="performanceBucket"></param>
|
||||
/// <param name="token">CancellationToken</param>
|
||||
/// <returns></returns>
|
||||
Task<int> RankedClientsCountAsync(long? serverId = null, CancellationToken token = default);
|
||||
Task<int> RankedClientsCountAsync(long? serverId = null, string performanceBucket = null, CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
|
@ -219,9 +219,11 @@ namespace SharedLibraryCore
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsZombieServer(this Server server)
|
||||
public static bool IsZombieServer(this Server server) => (server as IGameServer).IsZombieServer();
|
||||
|
||||
public static bool IsZombieServer(this IGameServer server)
|
||||
{
|
||||
return new[] { Game.T4, Game.T5, Game.T6 }.Contains(server.GameName) &&
|
||||
return new[] { Reference.Game.T4, Reference.Game.T5, Reference.Game.T6 }.Contains(server.GameCode) &&
|
||||
ZmGameTypes.Contains(server.Gametype.ToLower());
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,6 @@ namespace WebfrontCore.Controllers
|
||||
{
|
||||
await statMetricFunc(new Dictionary<int, List<EFMeta>> { { id, hitInfo.CustomMetrics } }, matchedServerId, null, false);
|
||||
}
|
||||
|
||||
hitInfo.TotalRankedClients = await _serverDataViewer.RankedClientsCountAsync(matchedServerId, token);
|
||||
|
||||
return View("~/Views/Client/Statistics/Advanced.cshtml", hitInfo);
|
||||
}
|
||||
|
@ -1,25 +1,24 @@
|
||||
using IW4MAdmin.Plugins.Stats;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using Stats.Dtos;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using Data.Abstractions;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using Stats.Config;
|
||||
using Stats.Dtos;
|
||||
using WebfrontCore.QueryHelpers.Models;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
||||
namespace WebfrontCore.Controllers.Client.Legacy
|
||||
{
|
||||
public class StatsController : BaseController
|
||||
{
|
||||
@ -62,7 +61,7 @@ namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
||||
matchedServerId = server.LegacyDatabaseId;
|
||||
}
|
||||
|
||||
ViewBag.TotalRankedClients = await _serverDataViewer.RankedClientsCountAsync(matchedServerId, token);
|
||||
ViewBag.TotalRankedClients = await _serverDataViewer.RankedClientsCountAsync(matchedServerId, null, token);
|
||||
ViewBag.ServerId = matchedServerId;
|
||||
|
||||
return View("~/Views/Client/Statistics/Index.cshtml", _manager.GetServers()
|
||||
|
@ -113,6 +113,7 @@ namespace WebfrontCore
|
||||
services.AddTransient<IValidator<FindClientRequest>, FindClientRequestValidator>();
|
||||
services.AddSingleton<IResourceQueryHelper<FindClientRequest, FindClientResult>, ClientService>();
|
||||
services.AddSingleton<IResourceQueryHelper<StatsInfoRequest, StatsInfoResult>, StatsResourceQueryHelper>();
|
||||
services.AddSingleton<IResourceQueryHelper<ClientRankingInfoRequest, ClientRankingInfo>, AdvancedClientStatsResourceQueryHelper>();
|
||||
services
|
||||
.AddSingleton<IResourceQueryHelper<StatsInfoRequest, AdvancedStatsInfo>,
|
||||
AdvancedClientStatsResourceQueryHelper>();
|
||||
|
Loading…
x
Reference in New Issue
Block a user