mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-07 21:58:06 -05:00
Using Code from EFPerformanceBucket for references
This commit is contained in:
parent
79bd6ca8e1
commit
98e2be8623
@ -912,9 +912,9 @@ namespace IW4MAdmin
|
||||
context.Entry(gameServer).Property(property => property.HostName).IsModified = true;
|
||||
}
|
||||
|
||||
if (gameServer.PerformanceBucket != PerformanceBucket)
|
||||
if (gameServer.PerformanceBucket.Code != PerformanceCode)
|
||||
{
|
||||
gameServer.PerformanceBucket = PerformanceBucket;
|
||||
gameServer.PerformanceBucket.Code = PerformanceCode;
|
||||
context.Entry(gameServer).Property(property => property.PerformanceBucket).IsModified = true;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
||||
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache, IDataValueCache<EFClientRankingHistory, int> rankedClientsCache, StatManager statManager)
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache,
|
||||
IDataValueCache<EFClientRankingHistory, int> rankedClientsCache, StatManager statManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_snapshotCache = snapshotCache;
|
||||
@ -47,12 +48,13 @@ namespace IW4MAdmin.Application.Misc
|
||||
MaxConcurrentClientsAsync(long? serverId = null, Reference.Game? gameCode = null, TimeSpan? overPeriod = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
_snapshotCache.SetCacheItem(async (snapshots, ids, cancellationToken) =>
|
||||
_snapshotCache.SetCacheItem(async (snapshots, idsList, cancellationToken) =>
|
||||
{
|
||||
Reference.Game? game = null;
|
||||
long? id = null;
|
||||
|
||||
if (ids.Any())
|
||||
var ids = idsList.ToList();
|
||||
if (ids.Count is not 0)
|
||||
{
|
||||
game = (Reference.Game?)ids.First();
|
||||
id = (long?)ids.Last();
|
||||
@ -102,12 +104,11 @@ namespace IW4MAdmin.Application.Misc
|
||||
_logger.LogDebug("Max concurrent clients since {Start} is {Clients}", oldestEntry, maxClients);
|
||||
|
||||
return (maxClients, maxClientsTime);
|
||||
}, nameof(MaxConcurrentClientsAsync), new object[] { gameCode, serverId }, _cacheTimeSpan, true);
|
||||
}, nameof(MaxConcurrentClientsAsync), [gameCode, serverId], _cacheTimeSpan, true);
|
||||
|
||||
try
|
||||
{
|
||||
return await _snapshotCache.GetCacheItem(nameof(MaxConcurrentClientsAsync),
|
||||
new object[] { gameCode, serverId }, token);
|
||||
return await _snapshotCache.GetCacheItem(nameof(MaxConcurrentClientsAsync), [gameCode, serverId], token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -116,7 +117,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(int, int)> ClientCountsAsync(TimeSpan? overPeriod = null, Reference.Game? gameCode = null, CancellationToken token = default)
|
||||
public async Task<(int, int)> ClientCountsAsync(TimeSpan? overPeriod = null, Reference.Game? gameCode = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
_serverStatsCache.SetCacheItem(async (set, ids, cancellationToken) =>
|
||||
{
|
||||
@ -131,7 +133,8 @@ namespace IW4MAdmin.Application.Misc
|
||||
cancellationToken);
|
||||
var startOfPeriod =
|
||||
DateTime.UtcNow.AddHours(-overPeriod?.TotalHours ?? -24);
|
||||
var recentCount = await set.CountAsync(client => (game == null || client.GameName == game) && client.LastConnection >= startOfPeriod,
|
||||
var recentCount = await set.CountAsync(
|
||||
client => (game == null || client.GameName == game) && client.LastConnection >= startOfPeriod,
|
||||
cancellationToken);
|
||||
|
||||
return (count, recentCount);
|
||||
@ -173,7 +176,10 @@ namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
ServerId = byServer.Key,
|
||||
ClientCounts = byServer.Select(snapshot => new ClientCountSnapshot
|
||||
{ Time = snapshot.CapturedAt, ClientCount = snapshot.ClientCount, ConnectionInterrupted = snapshot.ConnectionInterrupted ?? false, Map = snapshot.MapName}).ToList()
|
||||
{
|
||||
Time = snapshot.CapturedAt, ClientCount = snapshot.ClientCount,
|
||||
ConnectionInterrupted = snapshot.ConnectionInterrupted ?? false, Map = snapshot.MapName
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
}, nameof(_clientHistoryCache), TimeSpan.MaxValue);
|
||||
|
||||
@ -184,35 +190,36 @@ namespace IW4MAdmin.Application.Misc
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not retrieve data for {Name}", nameof(ClientHistoryAsync));
|
||||
return Enumerable.Empty<ClientHistoryInfo>();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> RankedClientsCountAsync(long? serverId = null, string performanceBucket = null, CancellationToken token = default)
|
||||
public async Task<int> RankedClientsCountAsync(long? serverId = null, string performanceBucketCode = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
_rankedClientsCache.SetCacheItem((set, ids, cancellationToken) =>
|
||||
_rankedClientsCache.SetCacheItem((set, idsList, cancellationToken) =>
|
||||
{
|
||||
long? id = null;
|
||||
string bucket = null;
|
||||
|
||||
if (ids.Any())
|
||||
var ids = idsList.ToList();
|
||||
if (ids.Count is not 0)
|
||||
{
|
||||
id = (long?)ids.First();
|
||||
}
|
||||
|
||||
if (ids.Count() == 2)
|
||||
if (ids.Count is 2)
|
||||
{
|
||||
bucket = (string)ids.Last();
|
||||
}
|
||||
|
||||
return _statManager.GetBucketConfig(serverId)
|
||||
.ContinueWith(result => _statManager.GetTotalRankedPlayers(id, bucket), cancellationToken).Result;
|
||||
|
||||
}, nameof(_rankedClientsCache), new object[] { serverId, performanceBucket }, _cacheTimeSpan);
|
||||
}, nameof(_rankedClientsCache), [serverId, performanceBucketCode], _cacheTimeSpan);
|
||||
|
||||
try
|
||||
{
|
||||
return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), new object[] { serverId, performanceBucket }, token);
|
||||
return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), [serverId, performanceBucketCode], token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -25,6 +25,7 @@ namespace Data.Context
|
||||
|
||||
#region STATS
|
||||
|
||||
public DbSet<EFPerformanceBucket> PerformanceBuckets { get; set; }
|
||||
public DbSet<Models.Vector3> Vector3s { get; set; }
|
||||
public DbSet<EFACSnapshotVector3> SnapshotVector3s { get; set; }
|
||||
public DbSet<EFACSnapshot> ACSnapshots { get; set; }
|
||||
|
@ -4,12 +4,17 @@ namespace Data.Models.Client.Stats;
|
||||
|
||||
public class EFPerformanceBucket
|
||||
{
|
||||
[Key]
|
||||
public int PerformanceBucketId { get; set; }
|
||||
[Key] public int PerformanceBucketId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifier for Bucket
|
||||
/// </summary>
|
||||
[MaxLength(256)]
|
||||
public string BucketCode { get; set; }
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Friendly name for Bucket
|
||||
/// </summary>
|
||||
[MaxLength(256)]
|
||||
public string BucketName { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
@ -290,16 +290,16 @@ public class HitCalculator : IClientStatisticCalculator
|
||||
var matchingLocation = await GetOrAddHitLocation(hitInfo.Location, hitInfo.Game);
|
||||
var meansOfDeath = await GetOrAddMeansOfDeath(hitInfo.MeansOfDeath, hitInfo.Game);
|
||||
|
||||
var baseTasks = new[]
|
||||
{
|
||||
List<Task<EFClientHitStatistic>> baseTasks =
|
||||
[
|
||||
// just the client
|
||||
GetOrAddClientHit(hitInfo.EntityId, null),
|
||||
GetOrAddClientHit(hitInfo.EntityId),
|
||||
// client and server
|
||||
GetOrAddClientHit(hitInfo.EntityId, serverId),
|
||||
// just the location
|
||||
GetOrAddClientHit(hitInfo.EntityId, null, matchingLocation.HitLocationId),
|
||||
GetOrAddClientHit(hitInfo.EntityId, hitLocationId: matchingLocation.HitLocationId),
|
||||
// location and server
|
||||
GetOrAddClientHit(hitInfo.EntityId, serverId, matchingLocation.HitLocationId),
|
||||
GetOrAddClientHit(hitInfo.EntityId, serverId, hitLocationId: matchingLocation.HitLocationId),
|
||||
// per weapon
|
||||
GetOrAddClientHit(hitInfo.EntityId, null, null, weapon.WeaponId),
|
||||
// per weapon and server
|
||||
@ -309,7 +309,7 @@ public class HitCalculator : IClientStatisticCalculator
|
||||
// means of death per server aggregate
|
||||
GetOrAddClientHit(hitInfo.EntityId, serverId,
|
||||
meansOfDeathId: meansOfDeath.MeansOfDeathId)
|
||||
};
|
||||
];
|
||||
|
||||
var allTasks = baseTasks.AsEnumerable();
|
||||
|
||||
@ -413,7 +413,7 @@ public class HitCalculator : IClientStatisticCalculator
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<EFClientHitStatistic> GetOrAddClientHit(int clientId, long? serverId = null, string performanceBucket = null,
|
||||
private async Task<EFClientHitStatistic> GetOrAddClientHit(int clientId, long? serverId = null, string performanceBucketCode = null,
|
||||
int? hitLocationId = null, int? weaponId = null, int? attachmentComboId = null,
|
||||
int? meansOfDeathId = null)
|
||||
{
|
||||
@ -425,7 +425,7 @@ public class HitCalculator : IClientStatisticCalculator
|
||||
&& hit.WeaponId == weaponId
|
||||
&& hit.WeaponAttachmentComboId == attachmentComboId
|
||||
&& hit.MeansOfDeathId == meansOfDeathId
|
||||
&& (performanceBucket is not null && performanceBucket == hit.Server.PerformanceBucket || (performanceBucket is null && hit.ServerId == serverId)));
|
||||
&& (performanceBucketCode is not null && performanceBucketCode == hit.Server.PerformanceBucket.Code || (performanceBucketCode is null && hit.ServerId == serverId)));
|
||||
|
||||
if (hitStat != null)
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ namespace Stats.Client
|
||||
{
|
||||
var bucketConfig =
|
||||
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
||||
bucket.Name == performanceBucket) ?? new PerformanceBucketConfiguration();
|
||||
bucket.Code == performanceBucket) ?? new PerformanceBucketConfiguration();
|
||||
|
||||
var oldestPerf = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
||||
var performances = await iqPerformances.Where(s => s.ServerId == serverId)
|
||||
@ -76,42 +76,43 @@ namespace Stats.Client
|
||||
distributions.Add(serverId.ToString(), distributionParams);
|
||||
}
|
||||
|
||||
foreach (var performanceBucketGroup in _appConfig.Servers.Select(server => server.PerformanceBucket).Distinct())
|
||||
foreach (var performanceBucket in _appConfig.Servers.Select(server => server.PerformanceBucketCode).Distinct())
|
||||
{
|
||||
var performanceBucket = performanceBucketGroup ?? "null";
|
||||
// TODO: ?
|
||||
var performanceBucketCode = performanceBucket ?? "null";
|
||||
|
||||
var bucketConfig =
|
||||
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
||||
bucket.Name == performanceBucket) ?? new PerformanceBucketConfiguration();
|
||||
bucket.Code == performanceBucketCode) ?? new PerformanceBucketConfiguration();
|
||||
|
||||
var oldestPerf = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
||||
var performances = await iqPerformances
|
||||
.Where(perf => perf.Server.PerformanceBucket == performanceBucket)
|
||||
.Where(perf => perf.Server.PerformanceBucket.Code == performanceBucketCode)
|
||||
.Where(perf => perf.TimePlayed >= bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||
.Where(perf => perf.UpdatedAt >= oldestPerf)
|
||||
.Where(perf => perf.Skill < 999999)
|
||||
.Select(s => s.EloRating * 1 / 3.0 + s.Skill * 2 / 3.0)
|
||||
.ToListAsync(token);
|
||||
var distributionParams = performances.GenerateDistributionParameters();
|
||||
distributions.Add(performanceBucket, distributionParams);
|
||||
distributions.Add(performanceBucketCode, distributionParams);
|
||||
}
|
||||
|
||||
return distributions;
|
||||
}, DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromHours(1));
|
||||
|
||||
foreach (var performanceBucket in _appConfig.Servers.Select(s => s.PerformanceBucket).Distinct())
|
||||
foreach (var performanceBucket in _appConfig.Servers.Select(s => s.PerformanceBucketCode).Distinct())
|
||||
{
|
||||
_maxZScoreCache.SetCacheItem(async (set, ids, token) =>
|
||||
{
|
||||
var validPlayTime = _configuration.TopPlayersMinPlayTime;
|
||||
var oldestStat = DateTime.UtcNow - Extensions.FifteenDaysAgo();
|
||||
var perfBucket = (string)ids.FirstOrDefault();
|
||||
var localPerformanceBucket = (string)ids.FirstOrDefault();
|
||||
|
||||
if (!string.IsNullOrEmpty(perfBucket))
|
||||
if (!string.IsNullOrEmpty(localPerformanceBucket))
|
||||
{
|
||||
var bucketConfig =
|
||||
_configuration.PerformanceBuckets.FirstOrDefault(cfg =>
|
||||
cfg.Name == perfBucket) ?? new PerformanceBucketConfiguration();
|
||||
cfg.Code == localPerformanceBucket) ?? new PerformanceBucketConfiguration();
|
||||
|
||||
validPlayTime = (int)bucketConfig.ClientMinPlayTime.TotalSeconds;
|
||||
oldestStat = bucketConfig.RankingExpiration;
|
||||
@ -121,7 +122,7 @@ namespace Stats.Client
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime, oldestStat))
|
||||
.Where(s => s.Skill > 0)
|
||||
.Where(s => s.EloRating >= 0)
|
||||
.Where(stat => perfBucket == stat.Server.PerformanceBucket)
|
||||
.Where(stat => localPerformanceBucket == stat.Server.PerformanceBucket.Code)
|
||||
.GroupBy(stat => stat.ClientId)
|
||||
.Select(group =>
|
||||
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed))
|
||||
@ -170,7 +171,7 @@ namespace Stats.Client
|
||||
await using var context = _contextFactory.CreateContext(false);
|
||||
_serverIds.AddRange(await context.Servers
|
||||
.Where(s => s.EndPoint != null && s.HostName != null)
|
||||
.Select(s => new Tuple<long, string>(s.ServerId, s.PerformanceBucket))
|
||||
.Select(s => new Tuple<long, string>(s.ServerId, s.PerformanceBucket.Code))
|
||||
.ToListAsync());
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,8 @@ namespace Stats.Config;
|
||||
|
||||
public class PerformanceBucketConfiguration
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string FriendlyNameRENAME { get; set; }
|
||||
public string Code { get; set; }
|
||||
public TimeSpan ClientMinPlayTime { get; set; } = Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromHours(3);
|
||||
public TimeSpan RankingExpiration { get; set; } = TimeSpan.FromDays(15);
|
||||
}
|
||||
|
@ -7,6 +7,6 @@
|
||||
/// </summary>
|
||||
public int? ClientId { get; set; }
|
||||
public string ServerEndpoint { get; set; }
|
||||
public string PerformanceBucket { get; set; }
|
||||
public string PerformanceBucketCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -66,8 +66,8 @@ namespace Stats.Helpers
|
||||
.ThenInclude(attachment => attachment.Attachment3)
|
||||
.Where(stat => stat.ClientId == query.ClientId);
|
||||
|
||||
iqHitStats = !string.IsNullOrEmpty(query.PerformanceBucket)
|
||||
? iqHitStats.Where(stat => stat.Server.PerformanceBucket == query.PerformanceBucket)
|
||||
iqHitStats = !string.IsNullOrEmpty(query.PerformanceBucketCode)
|
||||
? iqHitStats.Where(stat => stat.Server.PerformanceBucket.Code == query.PerformanceBucketCode)
|
||||
: iqHitStats.Where(stat => stat.ServerId == serverId);
|
||||
|
||||
var hitStats = await iqHitStats.ToListAsync();
|
||||
@ -76,7 +76,7 @@ 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)
|
||||
.Where(r => r.PerformanceBucket.Code == query.PerformanceBucketCode)
|
||||
.OrderByDescending(r => r.CreatedDateTime)
|
||||
.Take(250)
|
||||
.ToListAsync();
|
||||
@ -85,7 +85,7 @@ namespace Stats.Helpers
|
||||
{
|
||||
ClientId = query.ClientId,
|
||||
ServerEndpoint = query.ServerEndpoint,
|
||||
PerformanceBucket = query.PerformanceBucket
|
||||
PerformanceBucketCode = query.PerformanceBucketCode
|
||||
})).Results.First();
|
||||
|
||||
var mostRecentRanking = ratings.FirstOrDefault(ranking => ranking.Newest);
|
||||
@ -95,7 +95,7 @@ namespace Stats.Helpers
|
||||
var legacyStats = await context.Set<EFClientStatistics>()
|
||||
.Where(stat => stat.ClientId == query.ClientId)
|
||||
.Where(stat => serverId == null || stat.ServerId == serverId)
|
||||
.Where(stat => stat.Server.PerformanceBucket == query.PerformanceBucket)
|
||||
.Where(stat => stat.Server.PerformanceBucket.Code == query.PerformanceBucketCode)
|
||||
.ToListAsync();
|
||||
|
||||
var bucketConfig = await statManager.GetBucketConfig(serverId);
|
||||
@ -126,12 +126,12 @@ namespace Stats.Helpers
|
||||
{
|
||||
Name = server.Hostname, IPAddress = server.ListenAddress, Port = server.ListenPort,
|
||||
Game = (Reference.Game)server.GameName,
|
||||
PerformanceBucket = server.PerformanceBucket
|
||||
PerformanceBucket = server.PerformanceCode
|
||||
})
|
||||
.Where(server => server.Game == clientInfo.GameName)
|
||||
.ToList(),
|
||||
Aggregate = hitStats.FirstOrDefault(hit =>
|
||||
hit.HitLocationId == null && (string.IsNullOrEmpty(query.PerformanceBucket) || hit.ServerId == serverId) && hit.WeaponId == null &&
|
||||
hit.HitLocationId == null && (string.IsNullOrEmpty(query.PerformanceBucketCode) || hit.ServerId == serverId) && hit.WeaponId == null &&
|
||||
hit.MeansOfDeathId == null),
|
||||
ByHitLocation = hitStats
|
||||
.Where(hit => hit.HitLocationId != null)
|
||||
@ -186,9 +186,9 @@ namespace Stats.Helpers
|
||||
|
||||
var currentRanking = 0;
|
||||
int totalRankedClients;
|
||||
string performanceBucket;
|
||||
string performanceBucketCode;
|
||||
|
||||
if (string.IsNullOrEmpty(query.PerformanceBucket) && serverId is null)
|
||||
if (string.IsNullOrEmpty(query.PerformanceBucketCode) && serverId is null)
|
||||
{
|
||||
var maxPerformance = await context.Set<EFClientRankingHistory>()
|
||||
.Where(r => r.ClientId == query.ClientId)
|
||||
@ -204,27 +204,27 @@ namespace Stats.Helpers
|
||||
{
|
||||
currentRanking = 0;
|
||||
totalRankedClients = 0;
|
||||
performanceBucket = null;
|
||||
performanceBucketCode = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRanking =
|
||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, null, maxPerformance.Key);
|
||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(null, maxPerformance.Key);
|
||||
performanceBucket = maxPerformance.Key;
|
||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, null, maxPerformance.Key.Code);
|
||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(null, maxPerformance.Key.Code);
|
||||
performanceBucketCode = maxPerformance.Key.Code;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
performanceBucket = query.PerformanceBucket;
|
||||
performanceBucketCode = query.PerformanceBucketCode;
|
||||
currentRanking =
|
||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, serverId, performanceBucket);
|
||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(serverId, performanceBucket);
|
||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, serverId, performanceBucketCode);
|
||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(serverId, performanceBucketCode);
|
||||
}
|
||||
|
||||
return new ResourceQueryHelperResult<ClientRankingInfo>
|
||||
{
|
||||
Results = [new ClientRankingInfo(currentRanking, totalRankedClients, performanceBucket)]
|
||||
Results = [new ClientRankingInfo(currentRanking, totalRankedClients, performanceBucketCode)]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Expression<Func<EFClientRankingHistory, bool>> GetNewRankingFunc(TimeSpan oldestStat, TimeSpan minPlayTime, long? serverId = null, string performanceBucket = null)
|
||||
private Expression<Func<EFClientRankingHistory, bool>> GetNewRankingFunc(TimeSpan oldestStat, TimeSpan minPlayTime, long? serverId = null, string performanceBucketCode = null)
|
||||
{
|
||||
var oldestDate = DateTime.UtcNow - oldestStat;
|
||||
return ranking => ranking.ServerId == serverId
|
||||
@ -124,7 +124,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
&& ranking.ZScore != null
|
||||
&& ranking.PerformanceMetric != null
|
||||
&& ranking.Newest
|
||||
&& ranking.PerformanceBucket == performanceBucket
|
||||
&& ranking.PerformanceBucket.Code == performanceBucketCode
|
||||
&& ranking.Client.TotalConnectionTime >= (int)minPlayTime.TotalSeconds;
|
||||
}
|
||||
|
||||
@ -150,13 +150,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
public DateTime CreatedDateTime { get; set; }
|
||||
}
|
||||
|
||||
public async Task<List<TopStatsInfo>> GetNewTopStats(int start, int count, long? serverId = null, string performanceBucket = null)
|
||||
public async Task<List<TopStatsInfo>> GetNewTopStats(int start, int count, long? serverId = null, string performanceBucketCode = null)
|
||||
{
|
||||
var bucketConfig = await GetBucketConfig(serverId);
|
||||
|
||||
await using var context = _contextFactory.CreateContext(false);
|
||||
var clientIdsList = await context.Set<EFClientRankingHistory>()
|
||||
.Where(GetNewRankingFunc(bucketConfig.RankingExpiration, bucketConfig.ClientMinPlayTime, serverId: serverId, performanceBucket))
|
||||
.Where(GetNewRankingFunc(bucketConfig.RankingExpiration, bucketConfig.ClientMinPlayTime, serverId: serverId, performanceBucketCode))
|
||||
.OrderByDescending(ranking => ranking.PerformanceMetric)
|
||||
.Select(ranking => ranking.ClientId)
|
||||
.Skip(start)
|
||||
@ -170,7 +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)
|
||||
.Where(ranking => ranking.PerformanceBucket.Code == performanceBucketCode)
|
||||
.OrderByDescending(ranking => ranking.CreatedDateTime)
|
||||
.Select(ranking => new RankingSnapshot
|
||||
{
|
||||
@ -278,14 +278,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
foreach (var customMetricFunc in Plugin.ServerManager.CustomStatsMetrics)
|
||||
{
|
||||
await customMetricFunc(finished.ToDictionary(kvp => kvp.ClientId, kvp => kvp.Metrics), serverId,
|
||||
performanceBucket, true);
|
||||
performanceBucketCode, true);
|
||||
}
|
||||
|
||||
return finished;
|
||||
}
|
||||
|
||||
public async Task<PerformanceBucketConfiguration> GetBucketConfig(long? serverId = null,
|
||||
string bucketName = null)
|
||||
string performanceBucketCode = null)
|
||||
{
|
||||
var defaultConfig = new PerformanceBucketConfiguration
|
||||
{
|
||||
@ -293,26 +293,26 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
RankingExpiration = DateTime.UtcNow - Extensions.FifteenDaysAgo()
|
||||
};
|
||||
|
||||
if (serverId is null && bucketName is null)
|
||||
if (serverId is null && performanceBucketCode is null)
|
||||
{
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
if (bucketName is not null)
|
||||
if (performanceBucketCode is not null)
|
||||
{
|
||||
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Name == bucketName) ??
|
||||
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Code == performanceBucketCode) ??
|
||||
defaultConfig;
|
||||
}
|
||||
|
||||
var performanceBucket =
|
||||
(await _serverCache.FirstAsync(server => server.Id == serverId)).PerformanceBucket;
|
||||
(await _serverCache.FirstAsync(server => server.Id == serverId))?.PerformanceBucket?.Code;
|
||||
|
||||
if (string.IsNullOrEmpty(performanceBucket))
|
||||
{
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Name == performanceBucket) ??
|
||||
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Code == performanceBucket) ??
|
||||
defaultConfig;
|
||||
}
|
||||
|
||||
@ -1268,7 +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(stat => stat.Server.PerformanceBucket.Code == bucketConfig.Code)
|
||||
.Where(stats => stats.UpdatedAt >= oldestStateDate)
|
||||
.Where(stats => stats.TimePlayed >= (int)bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||
.ToListAsync();
|
||||
@ -1293,7 +1293,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
var aggregateRanking = await context.Set<EFClientStatistics>()
|
||||
.Where(stat => stat.ClientId != clientId)
|
||||
.Where(stat => bucketConfig.Name == stat.Server.PerformanceBucket)
|
||||
.Where(stat => bucketConfig.Code == stat.Server.PerformanceBucket.Code)
|
||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc((int)bucketConfig.ClientMinPlayTime.TotalSeconds, bucketConfig.RankingExpiration))
|
||||
.GroupBy(stat => stat.ClientId)
|
||||
.Where(group =>
|
||||
@ -1302,7 +1302,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
.Select(c => c.Key)
|
||||
.CountAsync();
|
||||
|
||||
var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore, bucketConfig.Name);
|
||||
var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore, bucketConfig.Code);
|
||||
|
||||
if (newPerformanceMetric == null)
|
||||
{
|
||||
@ -1311,19 +1311,23 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: CACHE THE EFPERFORMANCEBUCKET SO WE DON'T NEED TO LOOK IT UP - THERE WILL BE A REALISTIC LIMIT TO HOW MANY BUCKETS SHOULD BE LIVE
|
||||
var performanceBucketId = (await context.PerformanceBuckets.FirstOrDefaultAsync(x => x.Code == bucketConfig.Code))
|
||||
?.PerformanceBucketId;
|
||||
|
||||
var aggregateRankingSnapshot = new EFClientRankingHistory
|
||||
{
|
||||
ClientId = clientId,
|
||||
ZScore = aggregateZScore,
|
||||
Ranking = aggregateRanking,
|
||||
PerformanceMetric = newPerformanceMetric,
|
||||
PerformanceBucket = bucketConfig.Name,
|
||||
PerformanceBucketId = performanceBucketId,
|
||||
Newest = true,
|
||||
};
|
||||
|
||||
context.Add(aggregateRankingSnapshot);
|
||||
|
||||
await PruneOldRankings(context, clientId, performanceBucket: bucketConfig.Name);
|
||||
await PruneOldRankings(context, clientId, performanceBucketCode: bucketConfig.Code);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@ -1354,18 +1358,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task PruneOldRankings(DatabaseContext context, int clientId, long? serverId = null, string performanceBucket = null)
|
||||
private async Task PruneOldRankings(DatabaseContext context, int clientId, long? serverId = null, string performanceBucketCode = null)
|
||||
{
|
||||
var totalRankingEntries = await context.Set<EFClientRankingHistory>()
|
||||
.Where(r => r.ClientId == clientId)
|
||||
.Where(r => r.ServerId == serverId)
|
||||
.Where(r => r.PerformanceBucket == performanceBucket)
|
||||
.Where(r => r.PerformanceBucket.Code == performanceBucketCode)
|
||||
.CountAsync();
|
||||
|
||||
var mostRecent = await context.Set<EFClientRankingHistory>()
|
||||
.Where(r => r.ClientId == clientId)
|
||||
.Where(r => r.ServerId == serverId)
|
||||
.Where(r => r.PerformanceBucket == performanceBucket)
|
||||
.Where(r => r.PerformanceBucket.Code == performanceBucketCode)
|
||||
.FirstOrDefaultAsync(r => r.Newest);
|
||||
|
||||
if (mostRecent != null)
|
||||
@ -1381,7 +1385,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
var lastRating = await context.Set<EFClientRankingHistory>()
|
||||
.Where(r => r.ClientId == clientId)
|
||||
.Where(r => r.ServerId == serverId)
|
||||
.Where(r => r.PerformanceBucket == performanceBucket)
|
||||
.Where(r => r.PerformanceBucket.Code == performanceBucketCode)
|
||||
.OrderBy(r => r.CreatedDateTime)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
@ -1397,6 +1401,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// </summary>
|
||||
/// <param name="attackerStats">Stats of the attacker</param>
|
||||
/// <param name="victimStats">Stats of the victim</param>
|
||||
/// <param name="attacker">Attacker</param>
|
||||
/// <param name="victim">Victim</param>
|
||||
public void CalculateKill(EFClientStatistics attackerStats, EFClientStatistics victimStats,
|
||||
EFClient attacker, EFClient victim)
|
||||
{
|
||||
@ -1438,8 +1444,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
attackerStats.EloRating =
|
||||
attackerEloRatingFunc?.Invoke(attacker, attackerStats) ?? attackerStats.EloRating;
|
||||
|
||||
var victimEloRatingFunc =
|
||||
victim.GetAdditionalProperty<Func<EFClient, EFClientStatistics, double>>("EloRatingFunction");
|
||||
// Unused? New code. TODO: Check if needed?
|
||||
//var victimEloRatingFunc =
|
||||
// victim.GetAdditionalProperty<Func<EFClient, EFClientStatistics, double>>("EloRatingFunction");
|
||||
|
||||
victimStats.EloRating =
|
||||
attackerEloRatingFunc?.Invoke(victim, victimStats) ?? victimStats.EloRating;
|
||||
|
@ -620,7 +620,7 @@ public class ZombieClientStateManager(
|
||||
}
|
||||
|
||||
public async Task GetAdvancedStatsMetrics(Dictionary<int, List<EFMeta>> meta, long? serverId,
|
||||
string performanceBucket,
|
||||
string performanceBucketCode,
|
||||
bool isTopStats)
|
||||
{
|
||||
if (isTopStats || !meta.Any())
|
||||
@ -634,8 +634,8 @@ public class ZombieClientStateManager(
|
||||
var iqStats = context.ZombieClientStatAggregates
|
||||
.Where(stat => stat.ClientId == clientId);
|
||||
|
||||
iqStats = !string.IsNullOrEmpty(performanceBucket)
|
||||
? iqStats.Where(stat => stat.Server.PerformanceBucket == performanceBucket)
|
||||
iqStats = !string.IsNullOrEmpty(performanceBucketCode)
|
||||
? iqStats.Where(stat => stat.Server.PerformanceBucket.Code == performanceBucketCode)
|
||||
: iqStats.Where(stat => stat.ServerId == serverId);
|
||||
|
||||
var stats = await iqStats.Select(stat => new
|
||||
|
29
Plugins/ZombieStats/TODO.md
Normal file
29
Plugins/ZombieStats/TODO.md
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
### Server reference PerformanceBucket
|
||||
|
||||
EFPerformanceBucket adding as DbSet to DatabaseContext
|
||||
|
||||
Create migration for EFClientStat
|
||||
|
||||
|
||||
### Leaderboard logic
|
||||
|
||||
Using individual stats - but groups them by the bucket
|
||||
|
||||
Determines what the performance is by all the stats in that bucket / per server and for global stats. (BUCKET, SERVER, GLOBAL)
|
||||
- Isn't properly calculating, when it goes to try and find where a person ranked in a performance bucket, it was returning null(?) so they never got a rank
|
||||
- Potentially resolved by using EFPerformanceBucketId instead of arbitrary string
|
||||
|
||||
|
||||
If new buckets are added to config, check on start and add to DB (PerformanceBucketConfgiuration)
|
||||
- Needs to update to use the code
|
||||
- CODE LOC `EnsureServerAdded()`
|
||||
|
||||
LOG OnPlayerRoundDataGameEvent ZOMBIES `CID - 848637`
|
||||
|
||||
### 2024-09-05
|
||||
- Fixed references
|
||||
- Added DbSet
|
||||
- MIGRATION NOT CREATED
|
||||
- StatManager Line 1313 needs checking.
|
||||
|
@ -47,7 +47,7 @@ namespace SharedLibraryCore.Configuration
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_CUSTOM_HOSTNAME")]
|
||||
[ConfigurationOptional]
|
||||
public string CustomHostname { get; set; }
|
||||
public string PerformanceBucket { get; set; }
|
||||
public string PerformanceBucketCode { get; set; }
|
||||
|
||||
public IBaseConfiguration Generate()
|
||||
{
|
||||
|
@ -45,9 +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="performanceBucketCode"></param>
|
||||
/// <param name="token">CancellationToken</param>
|
||||
/// <returns></returns>
|
||||
Task<int> RankedClientsCountAsync(long? serverId = null, string performanceBucket = null, CancellationToken token = default);
|
||||
Task<int> RankedClientsCountAsync(long? serverId = null, string performanceBucketCode = null, CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ namespace SharedLibraryCore
|
||||
RConConnectionFactory = rconConnectionFactory;
|
||||
ServerLogger = logger;
|
||||
DefaultSettings = serviceProvider.GetRequiredService<DefaultSettings>();
|
||||
PerformanceBucket = ServerConfig.PerformanceBucket;
|
||||
PerformanceCode = ServerConfig.PerformanceBucketCode;
|
||||
InitializeTokens();
|
||||
InitializeAutoMessages();
|
||||
}
|
||||
@ -164,7 +164,7 @@ namespace SharedLibraryCore
|
||||
public bool IsInitialized { get; set; }
|
||||
public int Port { get; protected set; }
|
||||
public int ListenPort => Port;
|
||||
public string PerformanceBucket { get; init; }
|
||||
public string PerformanceCode { get; init; }
|
||||
public abstract Task Kick(string reason, EFClient target, EFClient origin, EFPenalty originalPenalty);
|
||||
public abstract Task<string[]> ExecuteCommandAsync(string command, CancellationToken token = default);
|
||||
public abstract Task SetDvarAsync(string name, object value, CancellationToken token = default);
|
||||
|
@ -35,7 +35,7 @@ namespace WebfrontCore.Controllers
|
||||
{
|
||||
ClientId = id,
|
||||
ServerEndpoint = serverId,
|
||||
PerformanceBucket = performanceBucket
|
||||
PerformanceBucketCode = performanceBucket
|
||||
}))?.Results?.First();
|
||||
|
||||
if (hitInfo is null)
|
||||
|
@ -72,7 +72,7 @@ namespace WebfrontCore.Controllers.Client.Legacy
|
||||
IPAddress = selectedServer.ListenAddress,
|
||||
Port = selectedServer.ListenPort,
|
||||
Game = selectedServer.GameCode,
|
||||
PerformanceBucket = selectedServer.PerformanceBucket
|
||||
PerformanceBucket = selectedServer.PerformanceCode
|
||||
}));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user