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;
|
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;
|
context.Entry(gameServer).Property(property => property.PerformanceBucket).IsModified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,11 +29,12 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
private readonly StatManager _statManager;
|
private readonly StatManager _statManager;
|
||||||
|
|
||||||
private readonly TimeSpan? _cacheTimeSpan =
|
private readonly TimeSpan? _cacheTimeSpan =
|
||||||
Utilities.IsDevelopment ? TimeSpan.FromSeconds(30) : (TimeSpan?) TimeSpan.FromMinutes(10);
|
Utilities.IsDevelopment ? TimeSpan.FromSeconds(30) : (TimeSpan?)TimeSpan.FromMinutes(10);
|
||||||
|
|
||||||
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
||||||
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
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;
|
_logger = logger;
|
||||||
_snapshotCache = snapshotCache;
|
_snapshotCache = snapshotCache;
|
||||||
@ -43,16 +44,17 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
_statManager = statManager;
|
_statManager = statManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(int?, DateTime?)>
|
public async Task<(int?, DateTime?)>
|
||||||
MaxConcurrentClientsAsync(long? serverId = null, Reference.Game? gameCode = null, TimeSpan? overPeriod = null,
|
MaxConcurrentClientsAsync(long? serverId = null, Reference.Game? gameCode = null, TimeSpan? overPeriod = null,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
_snapshotCache.SetCacheItem(async (snapshots, ids, cancellationToken) =>
|
_snapshotCache.SetCacheItem(async (snapshots, idsList, cancellationToken) =>
|
||||||
{
|
{
|
||||||
Reference.Game? game = null;
|
Reference.Game? game = null;
|
||||||
long? id = null;
|
long? id = null;
|
||||||
|
|
||||||
if (ids.Any())
|
var ids = idsList.ToList();
|
||||||
|
if (ids.Count is not 0)
|
||||||
{
|
{
|
||||||
game = (Reference.Game?)ids.First();
|
game = (Reference.Game?)ids.First();
|
||||||
id = (long?)ids.Last();
|
id = (long?)ids.Last();
|
||||||
@ -102,12 +104,11 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
_logger.LogDebug("Max concurrent clients since {Start} is {Clients}", oldestEntry, maxClients);
|
_logger.LogDebug("Max concurrent clients since {Start} is {Clients}", oldestEntry, maxClients);
|
||||||
|
|
||||||
return (maxClients, maxClientsTime);
|
return (maxClients, maxClientsTime);
|
||||||
}, nameof(MaxConcurrentClientsAsync), new object[] { gameCode, serverId }, _cacheTimeSpan, true);
|
}, nameof(MaxConcurrentClientsAsync), [gameCode, serverId], _cacheTimeSpan, true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await _snapshotCache.GetCacheItem(nameof(MaxConcurrentClientsAsync),
|
return await _snapshotCache.GetCacheItem(nameof(MaxConcurrentClientsAsync), [gameCode, serverId], token);
|
||||||
new object[] { gameCode, serverId }, token);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -116,22 +117,24 @@ 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) =>
|
_serverStatsCache.SetCacheItem(async (set, ids, cancellationToken) =>
|
||||||
{
|
{
|
||||||
Reference.Game? game = null;
|
Reference.Game? game = null;
|
||||||
|
|
||||||
if (ids.Any())
|
if (ids.Any())
|
||||||
{
|
{
|
||||||
game = (Reference.Game?)ids.First();
|
game = (Reference.Game?)ids.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = await set.CountAsync(item => game == null || item.GameName == game,
|
var count = await set.CountAsync(item => game == null || item.GameName == game,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
var startOfPeriod =
|
var startOfPeriod =
|
||||||
DateTime.UtcNow.AddHours(-overPeriod?.TotalHours ?? -24);
|
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);
|
cancellationToken);
|
||||||
|
|
||||||
return (count, recentCount);
|
return (count, recentCount);
|
||||||
@ -173,7 +176,10 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
{
|
{
|
||||||
ServerId = byServer.Key,
|
ServerId = byServer.Key,
|
||||||
ClientCounts = byServer.Select(snapshot => new ClientCountSnapshot
|
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();
|
}).ToList();
|
||||||
}, nameof(_clientHistoryCache), TimeSpan.MaxValue);
|
}, nameof(_clientHistoryCache), TimeSpan.MaxValue);
|
||||||
|
|
||||||
@ -184,35 +190,36 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Could not retrieve data for {Name}", nameof(ClientHistoryAsync));
|
_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;
|
long? id = null;
|
||||||
string bucket = null;
|
string bucket = null;
|
||||||
|
|
||||||
if (ids.Any())
|
var ids = idsList.ToList();
|
||||||
|
if (ids.Count is not 0)
|
||||||
{
|
{
|
||||||
id = (long?)ids.First();
|
id = (long?)ids.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ids.Count() == 2)
|
if (ids.Count is 2)
|
||||||
{
|
{
|
||||||
bucket = (string)ids.Last();
|
bucket = (string)ids.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _statManager.GetBucketConfig(serverId)
|
return _statManager.GetBucketConfig(serverId)
|
||||||
.ContinueWith(result => _statManager.GetTotalRankedPlayers(id, bucket), cancellationToken).Result;
|
.ContinueWith(result => _statManager.GetTotalRankedPlayers(id, bucket), cancellationToken).Result;
|
||||||
|
}, nameof(_rankedClientsCache), [serverId, performanceBucketCode], _cacheTimeSpan);
|
||||||
|
|
||||||
}, nameof(_rankedClientsCache), new object[] { serverId, performanceBucket }, _cacheTimeSpan);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), new object[] { serverId, performanceBucket }, token);
|
return await _rankedClientsCache.GetCacheItem(nameof(_rankedClientsCache), [serverId, performanceBucketCode], token);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,7 @@ namespace Data.Context
|
|||||||
|
|
||||||
#region STATS
|
#region STATS
|
||||||
|
|
||||||
|
public DbSet<EFPerformanceBucket> PerformanceBuckets { get; set; }
|
||||||
public DbSet<Models.Vector3> Vector3s { get; set; }
|
public DbSet<Models.Vector3> Vector3s { get; set; }
|
||||||
public DbSet<EFACSnapshotVector3> SnapshotVector3s { get; set; }
|
public DbSet<EFACSnapshotVector3> SnapshotVector3s { get; set; }
|
||||||
public DbSet<EFACSnapshot> ACSnapshots { get; set; }
|
public DbSet<EFACSnapshot> ACSnapshots { get; set; }
|
||||||
|
@ -4,12 +4,17 @@ namespace Data.Models.Client.Stats;
|
|||||||
|
|
||||||
public class EFPerformanceBucket
|
public class EFPerformanceBucket
|
||||||
{
|
{
|
||||||
[Key]
|
[Key] public int PerformanceBucketId { get; set; }
|
||||||
public int PerformanceBucketId { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifier for Bucket
|
||||||
|
/// </summary>
|
||||||
[MaxLength(256)]
|
[MaxLength(256)]
|
||||||
public string BucketCode { get; set; }
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Friendly name for Bucket
|
||||||
|
/// </summary>
|
||||||
[MaxLength(256)]
|
[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 matchingLocation = await GetOrAddHitLocation(hitInfo.Location, hitInfo.Game);
|
||||||
var meansOfDeath = await GetOrAddMeansOfDeath(hitInfo.MeansOfDeath, hitInfo.Game);
|
var meansOfDeath = await GetOrAddMeansOfDeath(hitInfo.MeansOfDeath, hitInfo.Game);
|
||||||
|
|
||||||
var baseTasks = new[]
|
List<Task<EFClientHitStatistic>> baseTasks =
|
||||||
{
|
[
|
||||||
// just the client
|
// just the client
|
||||||
GetOrAddClientHit(hitInfo.EntityId, null),
|
GetOrAddClientHit(hitInfo.EntityId),
|
||||||
// client and server
|
// client and server
|
||||||
GetOrAddClientHit(hitInfo.EntityId, serverId),
|
GetOrAddClientHit(hitInfo.EntityId, serverId),
|
||||||
// just the location
|
// just the location
|
||||||
GetOrAddClientHit(hitInfo.EntityId, null, matchingLocation.HitLocationId),
|
GetOrAddClientHit(hitInfo.EntityId, hitLocationId: matchingLocation.HitLocationId),
|
||||||
// location and server
|
// location and server
|
||||||
GetOrAddClientHit(hitInfo.EntityId, serverId, matchingLocation.HitLocationId),
|
GetOrAddClientHit(hitInfo.EntityId, serverId, hitLocationId: matchingLocation.HitLocationId),
|
||||||
// per weapon
|
// per weapon
|
||||||
GetOrAddClientHit(hitInfo.EntityId, null, null, weapon.WeaponId),
|
GetOrAddClientHit(hitInfo.EntityId, null, null, weapon.WeaponId),
|
||||||
// per weapon and server
|
// per weapon and server
|
||||||
@ -309,7 +309,7 @@ public class HitCalculator : IClientStatisticCalculator
|
|||||||
// means of death per server aggregate
|
// means of death per server aggregate
|
||||||
GetOrAddClientHit(hitInfo.EntityId, serverId,
|
GetOrAddClientHit(hitInfo.EntityId, serverId,
|
||||||
meansOfDeathId: meansOfDeath.MeansOfDeathId)
|
meansOfDeathId: meansOfDeath.MeansOfDeathId)
|
||||||
};
|
];
|
||||||
|
|
||||||
var allTasks = baseTasks.AsEnumerable();
|
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? hitLocationId = null, int? weaponId = null, int? attachmentComboId = null,
|
||||||
int? meansOfDeathId = null)
|
int? meansOfDeathId = null)
|
||||||
{
|
{
|
||||||
@ -425,7 +425,7 @@ public class HitCalculator : IClientStatisticCalculator
|
|||||||
&& hit.WeaponId == weaponId
|
&& hit.WeaponId == weaponId
|
||||||
&& hit.WeaponAttachmentComboId == attachmentComboId
|
&& hit.WeaponAttachmentComboId == attachmentComboId
|
||||||
&& hit.MeansOfDeathId == meansOfDeathId
|
&& 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)
|
if (hitStat != null)
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,7 @@ namespace Stats.Client
|
|||||||
{
|
{
|
||||||
var bucketConfig =
|
var bucketConfig =
|
||||||
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
||||||
bucket.Name == performanceBucket) ?? new PerformanceBucketConfiguration();
|
bucket.Code == performanceBucket) ?? new PerformanceBucketConfiguration();
|
||||||
|
|
||||||
var oldestPerf = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
var oldestPerf = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
||||||
var performances = await iqPerformances.Where(s => s.ServerId == serverId)
|
var performances = await iqPerformances.Where(s => s.ServerId == serverId)
|
||||||
@ -76,42 +76,43 @@ namespace Stats.Client
|
|||||||
distributions.Add(serverId.ToString(), distributionParams);
|
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 =
|
var bucketConfig =
|
||||||
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
_configuration.PerformanceBuckets.FirstOrDefault(bucket =>
|
||||||
bucket.Name == performanceBucket) ?? new PerformanceBucketConfiguration();
|
bucket.Code == performanceBucketCode) ?? new PerformanceBucketConfiguration();
|
||||||
|
|
||||||
var oldestPerf = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
var oldestPerf = DateTime.UtcNow - bucketConfig.RankingExpiration;
|
||||||
var performances = await iqPerformances
|
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.TimePlayed >= bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||||
.Where(perf => perf.UpdatedAt >= oldestPerf)
|
.Where(perf => perf.UpdatedAt >= oldestPerf)
|
||||||
.Where(perf => perf.Skill < 999999)
|
.Where(perf => perf.Skill < 999999)
|
||||||
.Select(s => s.EloRating * 1 / 3.0 + s.Skill * 2 / 3.0)
|
.Select(s => s.EloRating * 1 / 3.0 + s.Skill * 2 / 3.0)
|
||||||
.ToListAsync(token);
|
.ToListAsync(token);
|
||||||
var distributionParams = performances.GenerateDistributionParameters();
|
var distributionParams = performances.GenerateDistributionParameters();
|
||||||
distributions.Add(performanceBucket, distributionParams);
|
distributions.Add(performanceBucketCode, distributionParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
return distributions;
|
return distributions;
|
||||||
}, DistributionCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromHours(1));
|
}, 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) =>
|
_maxZScoreCache.SetCacheItem(async (set, ids, token) =>
|
||||||
{
|
{
|
||||||
var validPlayTime = _configuration.TopPlayersMinPlayTime;
|
var validPlayTime = _configuration.TopPlayersMinPlayTime;
|
||||||
var oldestStat = DateTime.UtcNow - Extensions.FifteenDaysAgo();
|
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 =
|
var bucketConfig =
|
||||||
_configuration.PerformanceBuckets.FirstOrDefault(cfg =>
|
_configuration.PerformanceBuckets.FirstOrDefault(cfg =>
|
||||||
cfg.Name == perfBucket) ?? new PerformanceBucketConfiguration();
|
cfg.Code == localPerformanceBucket) ?? new PerformanceBucketConfiguration();
|
||||||
|
|
||||||
validPlayTime = (int)bucketConfig.ClientMinPlayTime.TotalSeconds;
|
validPlayTime = (int)bucketConfig.ClientMinPlayTime.TotalSeconds;
|
||||||
oldestStat = bucketConfig.RankingExpiration;
|
oldestStat = bucketConfig.RankingExpiration;
|
||||||
@ -121,7 +122,7 @@ namespace Stats.Client
|
|||||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime, oldestStat))
|
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime, oldestStat))
|
||||||
.Where(s => s.Skill > 0)
|
.Where(s => s.Skill > 0)
|
||||||
.Where(s => s.EloRating >= 0)
|
.Where(s => s.EloRating >= 0)
|
||||||
.Where(stat => perfBucket == stat.Server.PerformanceBucket)
|
.Where(stat => localPerformanceBucket == stat.Server.PerformanceBucket.Code)
|
||||||
.GroupBy(stat => stat.ClientId)
|
.GroupBy(stat => stat.ClientId)
|
||||||
.Select(group =>
|
.Select(group =>
|
||||||
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed))
|
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);
|
await using var context = _contextFactory.CreateContext(false);
|
||||||
_serverIds.AddRange(await context.Servers
|
_serverIds.AddRange(await context.Servers
|
||||||
.Where(s => s.EndPoint != null && s.HostName != null)
|
.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());
|
.ToListAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ namespace Stats.Config;
|
|||||||
|
|
||||||
public class PerformanceBucketConfiguration
|
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 ClientMinPlayTime { get; set; } = Utilities.IsDevelopment ? TimeSpan.FromMinutes(1) : TimeSpan.FromHours(3);
|
||||||
public TimeSpan RankingExpiration { get; set; } = TimeSpan.FromDays(15);
|
public TimeSpan RankingExpiration { get; set; } = TimeSpan.FromDays(15);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? ClientId { get; set; }
|
public int? ClientId { get; set; }
|
||||||
public string ServerEndpoint { 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)
|
.ThenInclude(attachment => attachment.Attachment3)
|
||||||
.Where(stat => stat.ClientId == query.ClientId);
|
.Where(stat => stat.ClientId == query.ClientId);
|
||||||
|
|
||||||
iqHitStats = !string.IsNullOrEmpty(query.PerformanceBucket)
|
iqHitStats = !string.IsNullOrEmpty(query.PerformanceBucketCode)
|
||||||
? iqHitStats.Where(stat => stat.Server.PerformanceBucket == query.PerformanceBucket)
|
? iqHitStats.Where(stat => stat.Server.PerformanceBucket.Code == query.PerformanceBucketCode)
|
||||||
: iqHitStats.Where(stat => stat.ServerId == serverId);
|
: iqHitStats.Where(stat => stat.ServerId == serverId);
|
||||||
|
|
||||||
var hitStats = await iqHitStats.ToListAsync();
|
var hitStats = await iqHitStats.ToListAsync();
|
||||||
@ -76,7 +76,7 @@ namespace Stats.Helpers
|
|||||||
.Where(r => r.ClientId == clientInfo.ClientId)
|
.Where(r => r.ClientId == clientInfo.ClientId)
|
||||||
.Where(r => r.ServerId == serverId)
|
.Where(r => r.ServerId == serverId)
|
||||||
.Where(r => r.Ranking != null)
|
.Where(r => r.Ranking != null)
|
||||||
.Where(r => r.PerformanceBucket == query.PerformanceBucket)
|
.Where(r => r.PerformanceBucket.Code == query.PerformanceBucketCode)
|
||||||
.OrderByDescending(r => r.CreatedDateTime)
|
.OrderByDescending(r => r.CreatedDateTime)
|
||||||
.Take(250)
|
.Take(250)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@ -85,7 +85,7 @@ namespace Stats.Helpers
|
|||||||
{
|
{
|
||||||
ClientId = query.ClientId,
|
ClientId = query.ClientId,
|
||||||
ServerEndpoint = query.ServerEndpoint,
|
ServerEndpoint = query.ServerEndpoint,
|
||||||
PerformanceBucket = query.PerformanceBucket
|
PerformanceBucketCode = query.PerformanceBucketCode
|
||||||
})).Results.First();
|
})).Results.First();
|
||||||
|
|
||||||
var mostRecentRanking = ratings.FirstOrDefault(ranking => ranking.Newest);
|
var mostRecentRanking = ratings.FirstOrDefault(ranking => ranking.Newest);
|
||||||
@ -95,7 +95,7 @@ namespace Stats.Helpers
|
|||||||
var legacyStats = await context.Set<EFClientStatistics>()
|
var legacyStats = await context.Set<EFClientStatistics>()
|
||||||
.Where(stat => stat.ClientId == query.ClientId)
|
.Where(stat => stat.ClientId == query.ClientId)
|
||||||
.Where(stat => serverId == null || stat.ServerId == serverId)
|
.Where(stat => serverId == null || stat.ServerId == serverId)
|
||||||
.Where(stat => stat.Server.PerformanceBucket == query.PerformanceBucket)
|
.Where(stat => stat.Server.PerformanceBucket.Code == query.PerformanceBucketCode)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var bucketConfig = await statManager.GetBucketConfig(serverId);
|
var bucketConfig = await statManager.GetBucketConfig(serverId);
|
||||||
@ -126,12 +126,12 @@ namespace Stats.Helpers
|
|||||||
{
|
{
|
||||||
Name = server.Hostname, IPAddress = server.ListenAddress, Port = server.ListenPort,
|
Name = server.Hostname, IPAddress = server.ListenAddress, Port = server.ListenPort,
|
||||||
Game = (Reference.Game)server.GameName,
|
Game = (Reference.Game)server.GameName,
|
||||||
PerformanceBucket = server.PerformanceBucket
|
PerformanceBucket = server.PerformanceCode
|
||||||
})
|
})
|
||||||
.Where(server => server.Game == clientInfo.GameName)
|
.Where(server => server.Game == clientInfo.GameName)
|
||||||
.ToList(),
|
.ToList(),
|
||||||
Aggregate = hitStats.FirstOrDefault(hit =>
|
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),
|
hit.MeansOfDeathId == null),
|
||||||
ByHitLocation = hitStats
|
ByHitLocation = hitStats
|
||||||
.Where(hit => hit.HitLocationId != null)
|
.Where(hit => hit.HitLocationId != null)
|
||||||
@ -186,9 +186,9 @@ namespace Stats.Helpers
|
|||||||
|
|
||||||
var currentRanking = 0;
|
var currentRanking = 0;
|
||||||
int totalRankedClients;
|
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>()
|
var maxPerformance = await context.Set<EFClientRankingHistory>()
|
||||||
.Where(r => r.ClientId == query.ClientId)
|
.Where(r => r.ClientId == query.ClientId)
|
||||||
@ -204,27 +204,27 @@ namespace Stats.Helpers
|
|||||||
{
|
{
|
||||||
currentRanking = 0;
|
currentRanking = 0;
|
||||||
totalRankedClients = 0;
|
totalRankedClients = 0;
|
||||||
performanceBucket = null;
|
performanceBucketCode = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
currentRanking =
|
currentRanking =
|
||||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, null, maxPerformance.Key);
|
await statManager.GetClientOverallRanking(query.ClientId!.Value, null, maxPerformance.Key.Code);
|
||||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(null, maxPerformance.Key);
|
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(null, maxPerformance.Key.Code);
|
||||||
performanceBucket = maxPerformance.Key;
|
performanceBucketCode = maxPerformance.Key.Code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
performanceBucket = query.PerformanceBucket;
|
performanceBucketCode = query.PerformanceBucketCode;
|
||||||
currentRanking =
|
currentRanking =
|
||||||
await statManager.GetClientOverallRanking(query.ClientId!.Value, serverId, performanceBucket);
|
await statManager.GetClientOverallRanking(query.ClientId!.Value, serverId, performanceBucketCode);
|
||||||
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(serverId, performanceBucket);
|
totalRankedClients = await serverDataViewer.RankedClientsCountAsync(serverId, performanceBucketCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResourceQueryHelperResult<ClientRankingInfo>
|
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;
|
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;
|
var oldestDate = DateTime.UtcNow - oldestStat;
|
||||||
return ranking => ranking.ServerId == serverId
|
return ranking => ranking.ServerId == serverId
|
||||||
@ -124,7 +124,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
&& ranking.ZScore != null
|
&& ranking.ZScore != null
|
||||||
&& ranking.PerformanceMetric != null
|
&& ranking.PerformanceMetric != null
|
||||||
&& ranking.Newest
|
&& ranking.Newest
|
||||||
&& ranking.PerformanceBucket == performanceBucket
|
&& ranking.PerformanceBucket.Code == performanceBucketCode
|
||||||
&& ranking.Client.TotalConnectionTime >= (int)minPlayTime.TotalSeconds;
|
&& ranking.Client.TotalConnectionTime >= (int)minPlayTime.TotalSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,13 +150,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
public DateTime CreatedDateTime { get; set; }
|
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);
|
var bucketConfig = await GetBucketConfig(serverId);
|
||||||
|
|
||||||
await using var context = _contextFactory.CreateContext(false);
|
await using var context = _contextFactory.CreateContext(false);
|
||||||
var clientIdsList = await context.Set<EFClientRankingHistory>()
|
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)
|
.OrderByDescending(ranking => ranking.PerformanceMetric)
|
||||||
.Select(ranking => ranking.ClientId)
|
.Select(ranking => ranking.ClientId)
|
||||||
.Skip(start)
|
.Skip(start)
|
||||||
@ -170,7 +170,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
var eachRank = await context.Set<EFClientRankingHistory>()
|
var eachRank = await context.Set<EFClientRankingHistory>()
|
||||||
.Where(ranking => ranking.ClientId == clientId)
|
.Where(ranking => ranking.ClientId == clientId)
|
||||||
.Where(ranking => ranking.ServerId == serverId)
|
.Where(ranking => ranking.ServerId == serverId)
|
||||||
.Where(ranking => ranking.PerformanceBucket == performanceBucket)
|
.Where(ranking => ranking.PerformanceBucket.Code == performanceBucketCode)
|
||||||
.OrderByDescending(ranking => ranking.CreatedDateTime)
|
.OrderByDescending(ranking => ranking.CreatedDateTime)
|
||||||
.Select(ranking => new RankingSnapshot
|
.Select(ranking => new RankingSnapshot
|
||||||
{
|
{
|
||||||
@ -278,14 +278,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
foreach (var customMetricFunc in Plugin.ServerManager.CustomStatsMetrics)
|
foreach (var customMetricFunc in Plugin.ServerManager.CustomStatsMetrics)
|
||||||
{
|
{
|
||||||
await customMetricFunc(finished.ToDictionary(kvp => kvp.ClientId, kvp => kvp.Metrics), serverId,
|
await customMetricFunc(finished.ToDictionary(kvp => kvp.ClientId, kvp => kvp.Metrics), serverId,
|
||||||
performanceBucket, true);
|
performanceBucketCode, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return finished;
|
return finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PerformanceBucketConfiguration> GetBucketConfig(long? serverId = null,
|
public async Task<PerformanceBucketConfiguration> GetBucketConfig(long? serverId = null,
|
||||||
string bucketName = null)
|
string performanceBucketCode = null)
|
||||||
{
|
{
|
||||||
var defaultConfig = new PerformanceBucketConfiguration
|
var defaultConfig = new PerformanceBucketConfiguration
|
||||||
{
|
{
|
||||||
@ -293,26 +293,26 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
RankingExpiration = DateTime.UtcNow - Extensions.FifteenDaysAgo()
|
RankingExpiration = DateTime.UtcNow - Extensions.FifteenDaysAgo()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (serverId is null && bucketName is null)
|
if (serverId is null && performanceBucketCode is null)
|
||||||
{
|
{
|
||||||
return defaultConfig;
|
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;
|
defaultConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
var performanceBucket =
|
var performanceBucket =
|
||||||
(await _serverCache.FirstAsync(server => server.Id == serverId)).PerformanceBucket;
|
(await _serverCache.FirstAsync(server => server.Id == serverId))?.PerformanceBucket?.Code;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(performanceBucket))
|
if (string.IsNullOrEmpty(performanceBucket))
|
||||||
{
|
{
|
||||||
return defaultConfig;
|
return defaultConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Name == performanceBucket) ??
|
return _config.PerformanceBuckets.FirstOrDefault(bucket => bucket.Code == performanceBucket) ??
|
||||||
defaultConfig;
|
defaultConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1268,7 +1268,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
.Include(stat => stat.Server)
|
.Include(stat => stat.Server)
|
||||||
.Where(stat => stat.ClientId == clientId)
|
.Where(stat => stat.ClientId == clientId)
|
||||||
.Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking
|
.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.UpdatedAt >= oldestStateDate)
|
||||||
.Where(stats => stats.TimePlayed >= (int)bucketConfig.ClientMinPlayTime.TotalSeconds)
|
.Where(stats => stats.TimePlayed >= (int)bucketConfig.ClientMinPlayTime.TotalSeconds)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@ -1293,7 +1293,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
var aggregateRanking = await context.Set<EFClientStatistics>()
|
var aggregateRanking = await context.Set<EFClientStatistics>()
|
||||||
.Where(stat => stat.ClientId != clientId)
|
.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))
|
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc((int)bucketConfig.ClientMinPlayTime.TotalSeconds, bucketConfig.RankingExpiration))
|
||||||
.GroupBy(stat => stat.ClientId)
|
.GroupBy(stat => stat.ClientId)
|
||||||
.Where(group =>
|
.Where(group =>
|
||||||
@ -1302,7 +1302,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
.Select(c => c.Key)
|
.Select(c => c.Key)
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
|
|
||||||
var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore, bucketConfig.Name);
|
var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore, bucketConfig.Code);
|
||||||
|
|
||||||
if (newPerformanceMetric == null)
|
if (newPerformanceMetric == null)
|
||||||
{
|
{
|
||||||
@ -1311,19 +1311,23 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
return;
|
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
|
var aggregateRankingSnapshot = new EFClientRankingHistory
|
||||||
{
|
{
|
||||||
ClientId = clientId,
|
ClientId = clientId,
|
||||||
ZScore = aggregateZScore,
|
ZScore = aggregateZScore,
|
||||||
Ranking = aggregateRanking,
|
Ranking = aggregateRanking,
|
||||||
PerformanceMetric = newPerformanceMetric,
|
PerformanceMetric = newPerformanceMetric,
|
||||||
PerformanceBucket = bucketConfig.Name,
|
PerformanceBucketId = performanceBucketId,
|
||||||
Newest = true,
|
Newest = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
context.Add(aggregateRankingSnapshot);
|
context.Add(aggregateRankingSnapshot);
|
||||||
|
|
||||||
await PruneOldRankings(context, clientId, performanceBucket: bucketConfig.Name);
|
await PruneOldRankings(context, clientId, performanceBucketCode: bucketConfig.Code);
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1354,18 +1358,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
await context.SaveChangesAsync();
|
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>()
|
var totalRankingEntries = await context.Set<EFClientRankingHistory>()
|
||||||
.Where(r => r.ClientId == clientId)
|
.Where(r => r.ClientId == clientId)
|
||||||
.Where(r => r.ServerId == serverId)
|
.Where(r => r.ServerId == serverId)
|
||||||
.Where(r => r.PerformanceBucket == performanceBucket)
|
.Where(r => r.PerformanceBucket.Code == performanceBucketCode)
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
|
|
||||||
var mostRecent = await context.Set<EFClientRankingHistory>()
|
var mostRecent = await context.Set<EFClientRankingHistory>()
|
||||||
.Where(r => r.ClientId == clientId)
|
.Where(r => r.ClientId == clientId)
|
||||||
.Where(r => r.ServerId == serverId)
|
.Where(r => r.ServerId == serverId)
|
||||||
.Where(r => r.PerformanceBucket == performanceBucket)
|
.Where(r => r.PerformanceBucket.Code == performanceBucketCode)
|
||||||
.FirstOrDefaultAsync(r => r.Newest);
|
.FirstOrDefaultAsync(r => r.Newest);
|
||||||
|
|
||||||
if (mostRecent != null)
|
if (mostRecent != null)
|
||||||
@ -1381,7 +1385,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
var lastRating = await context.Set<EFClientRankingHistory>()
|
var lastRating = await context.Set<EFClientRankingHistory>()
|
||||||
.Where(r => r.ClientId == clientId)
|
.Where(r => r.ClientId == clientId)
|
||||||
.Where(r => r.ServerId == serverId)
|
.Where(r => r.ServerId == serverId)
|
||||||
.Where(r => r.PerformanceBucket == performanceBucket)
|
.Where(r => r.PerformanceBucket.Code == performanceBucketCode)
|
||||||
.OrderBy(r => r.CreatedDateTime)
|
.OrderBy(r => r.CreatedDateTime)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
@ -1397,6 +1401,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="attackerStats">Stats of the attacker</param>
|
/// <param name="attackerStats">Stats of the attacker</param>
|
||||||
/// <param name="victimStats">Stats of the victim</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,
|
public void CalculateKill(EFClientStatistics attackerStats, EFClientStatistics victimStats,
|
||||||
EFClient attacker, EFClient victim)
|
EFClient attacker, EFClient victim)
|
||||||
{
|
{
|
||||||
@ -1438,8 +1444,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
attackerStats.EloRating =
|
attackerStats.EloRating =
|
||||||
attackerEloRatingFunc?.Invoke(attacker, attackerStats) ?? attackerStats.EloRating;
|
attackerEloRatingFunc?.Invoke(attacker, attackerStats) ?? attackerStats.EloRating;
|
||||||
|
|
||||||
var victimEloRatingFunc =
|
// Unused? New code. TODO: Check if needed?
|
||||||
victim.GetAdditionalProperty<Func<EFClient, EFClientStatistics, double>>("EloRatingFunction");
|
//var victimEloRatingFunc =
|
||||||
|
// victim.GetAdditionalProperty<Func<EFClient, EFClientStatistics, double>>("EloRatingFunction");
|
||||||
|
|
||||||
victimStats.EloRating =
|
victimStats.EloRating =
|
||||||
attackerEloRatingFunc?.Invoke(victim, victimStats) ?? 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,
|
public async Task GetAdvancedStatsMetrics(Dictionary<int, List<EFMeta>> meta, long? serverId,
|
||||||
string performanceBucket,
|
string performanceBucketCode,
|
||||||
bool isTopStats)
|
bool isTopStats)
|
||||||
{
|
{
|
||||||
if (isTopStats || !meta.Any())
|
if (isTopStats || !meta.Any())
|
||||||
@ -634,8 +634,8 @@ public class ZombieClientStateManager(
|
|||||||
var iqStats = context.ZombieClientStatAggregates
|
var iqStats = context.ZombieClientStatAggregates
|
||||||
.Where(stat => stat.ClientId == clientId);
|
.Where(stat => stat.ClientId == clientId);
|
||||||
|
|
||||||
iqStats = !string.IsNullOrEmpty(performanceBucket)
|
iqStats = !string.IsNullOrEmpty(performanceBucketCode)
|
||||||
? iqStats.Where(stat => stat.Server.PerformanceBucket == performanceBucket)
|
? iqStats.Where(stat => stat.Server.PerformanceBucket.Code == performanceBucketCode)
|
||||||
: iqStats.Where(stat => stat.ServerId == serverId);
|
: iqStats.Where(stat => stat.ServerId == serverId);
|
||||||
|
|
||||||
var stats = await iqStats.Select(stat => new
|
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")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_CUSTOM_HOSTNAME")]
|
||||||
[ConfigurationOptional]
|
[ConfigurationOptional]
|
||||||
public string CustomHostname { get; set; }
|
public string CustomHostname { get; set; }
|
||||||
public string PerformanceBucket { get; set; }
|
public string PerformanceBucketCode { get; set; }
|
||||||
|
|
||||||
public IBaseConfiguration Generate()
|
public IBaseConfiguration Generate()
|
||||||
{
|
{
|
||||||
|
@ -45,9 +45,9 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// Retrieves the number of ranked clients for given server id
|
/// Retrieves the number of ranked clients for given server id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serverId">ServerId to query on</param>
|
/// <param name="serverId">ServerId to query on</param>
|
||||||
/// <param name="performanceBucket"></param>
|
/// <param name="performanceBucketCode"></param>
|
||||||
/// <param name="token">CancellationToken</param>
|
/// <param name="token">CancellationToken</param>
|
||||||
/// <returns></returns>
|
/// <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;
|
RConConnectionFactory = rconConnectionFactory;
|
||||||
ServerLogger = logger;
|
ServerLogger = logger;
|
||||||
DefaultSettings = serviceProvider.GetRequiredService<DefaultSettings>();
|
DefaultSettings = serviceProvider.GetRequiredService<DefaultSettings>();
|
||||||
PerformanceBucket = ServerConfig.PerformanceBucket;
|
PerformanceCode = ServerConfig.PerformanceBucketCode;
|
||||||
InitializeTokens();
|
InitializeTokens();
|
||||||
InitializeAutoMessages();
|
InitializeAutoMessages();
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ namespace SharedLibraryCore
|
|||||||
public bool IsInitialized { get; set; }
|
public bool IsInitialized { get; set; }
|
||||||
public int Port { get; protected set; }
|
public int Port { get; protected set; }
|
||||||
public int ListenPort => Port;
|
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 Kick(string reason, EFClient target, EFClient origin, EFPenalty originalPenalty);
|
||||||
public abstract Task<string[]> ExecuteCommandAsync(string command, CancellationToken token = default);
|
public abstract Task<string[]> ExecuteCommandAsync(string command, CancellationToken token = default);
|
||||||
public abstract Task SetDvarAsync(string name, object value, CancellationToken token = default);
|
public abstract Task SetDvarAsync(string name, object value, CancellationToken token = default);
|
||||||
|
@ -35,7 +35,7 @@ namespace WebfrontCore.Controllers
|
|||||||
{
|
{
|
||||||
ClientId = id,
|
ClientId = id,
|
||||||
ServerEndpoint = serverId,
|
ServerEndpoint = serverId,
|
||||||
PerformanceBucket = performanceBucket
|
PerformanceBucketCode = performanceBucket
|
||||||
}))?.Results?.First();
|
}))?.Results?.First();
|
||||||
|
|
||||||
if (hitInfo is null)
|
if (hitInfo is null)
|
||||||
|
@ -72,7 +72,7 @@ namespace WebfrontCore.Controllers.Client.Legacy
|
|||||||
IPAddress = selectedServer.ListenAddress,
|
IPAddress = selectedServer.ListenAddress,
|
||||||
Port = selectedServer.ListenPort,
|
Port = selectedServer.ListenPort,
|
||||||
Game = selectedServer.GameCode,
|
Game = selectedServer.GameCode,
|
||||||
PerformanceBucket = selectedServer.PerformanceBucket
|
PerformanceBucket = selectedServer.PerformanceCode
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user