persist client count history data across reboots and allow for configurable timespan
This commit is contained in:
parent
02e5e78f67
commit
deff4f2947
@ -1002,10 +1002,13 @@ namespace IW4MAdmin
|
||||
LastMessage = DateTime.Now - start;
|
||||
lastCount = DateTime.Now;
|
||||
|
||||
var appConfig = _serviceProvider.GetService<ApplicationConfiguration>();
|
||||
// update the player history
|
||||
if ((lastCount - playerCountStart).TotalMinutes >= PlayerHistory.UpdateInterval)
|
||||
if (lastCount - playerCountStart >= appConfig.ServerDataCollectionInterval)
|
||||
{
|
||||
while (ClientHistory.Count > ((60 / PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
|
||||
var maxItems = Math.Ceiling(appConfig.MaxClientHistoryTime.TotalMinutes /
|
||||
appConfig.ServerDataCollectionInterval.TotalMinutes);
|
||||
while ( ClientHistory.Count > maxItems)
|
||||
{
|
||||
ClientHistory.Dequeue();
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Abstractions;
|
||||
using Data.Models.Client.Stats;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace IW4MAdmin.Application.Migration
|
||||
{
|
||||
|
@ -10,6 +10,7 @@ using Data.Models.Server;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
@ -21,14 +22,16 @@ namespace IW4MAdmin.Application.Misc
|
||||
private readonly ILogger _logger;
|
||||
private readonly IManager _manager;
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
private readonly ApplicationConfiguration _appConfig;
|
||||
|
||||
private bool _inProgress;
|
||||
private TimeSpan _period;
|
||||
|
||||
public ServerDataCollector(ILogger<ServerDataCollector> logger, IManager manager,
|
||||
IDatabaseContextFactory contextFactory)
|
||||
public ServerDataCollector(ILogger<ServerDataCollector> logger, ApplicationConfiguration appConfig,
|
||||
IManager manager, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_appConfig = appConfig;
|
||||
_manager = manager;
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
@ -42,7 +45,9 @@ namespace IW4MAdmin.Application.Misc
|
||||
|
||||
_logger.LogDebug("Initializing data collection with {Name}", nameof(ServerDataCollector));
|
||||
_inProgress = true;
|
||||
_period = period ?? TimeSpan.FromMinutes(Utilities.IsDevelopment ? 1 : 5);
|
||||
_period = period ?? (Utilities.IsDevelopment
|
||||
? TimeSpan.FromMinutes(1)
|
||||
: _appConfig.ServerDataCollectionInterval);
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -8,6 +9,8 @@ using Data.Models.Server;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
@ -19,16 +22,19 @@ namespace IW4MAdmin.Application.Misc
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDataValueCache<EFServerSnapshot, int> _snapshotCache;
|
||||
private readonly IDataValueCache<EFClient, (int, int)> _serverStatsCache;
|
||||
private readonly IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> _clientHistoryCache;
|
||||
|
||||
private readonly TimeSpan? _cacheTimeSpan =
|
||||
Utilities.IsDevelopment ? TimeSpan.FromSeconds(1) : (TimeSpan?) TimeSpan.FromMinutes(1);
|
||||
|
||||
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, int> snapshotCache,
|
||||
IDataValueCache<EFClient, (int, int)> serverStatsCache)
|
||||
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
||||
IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> clientHistoryCache)
|
||||
{
|
||||
_logger = logger;
|
||||
_snapshotCache = snapshotCache;
|
||||
_serverStatsCache = serverStatsCache;
|
||||
_clientHistoryCache = clientHistoryCache;
|
||||
}
|
||||
|
||||
public async Task<int> MaxConcurrentClientsAsync(long? serverId = null, TimeSpan? overPeriod = null,
|
||||
@ -45,14 +51,14 @@ namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
maxClients = await snapshots.Where(snapshot => snapshot.ServerId == serverId)
|
||||
.Where(snapshot => snapshot.CapturedAt >= oldestEntry)
|
||||
.MaxAsync(snapshot => (int?)snapshot.ClientCount, cancellationToken) ?? 0;
|
||||
.MaxAsync(snapshot => (int?) snapshot.ClientCount, cancellationToken) ?? 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
maxClients = await snapshots.Where(snapshot => snapshot.CapturedAt >= oldestEntry)
|
||||
.GroupBy(snapshot => snapshot.PeriodBlock)
|
||||
.Select(grp => grp.Sum(snapshot => (int?)snapshot.ClientCount))
|
||||
.Select(grp => grp.Sum(snapshot => (int?) snapshot.ClientCount))
|
||||
.MaxAsync(cancellationToken) ?? 0;
|
||||
}
|
||||
|
||||
@ -95,5 +101,43 @@ namespace IW4MAdmin.Application.Misc
|
||||
return (0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ClientHistoryInfo>> ClientHistoryAsync(TimeSpan? overPeriod = null, CancellationToken token = default)
|
||||
{
|
||||
_clientHistoryCache.SetCacheItem(async (set, cancellationToken) =>
|
||||
{
|
||||
var oldestEntry = overPeriod.HasValue
|
||||
? DateTime.UtcNow - overPeriod.Value
|
||||
: DateTime.UtcNow.AddHours(-12);
|
||||
|
||||
var history = await set.Where(snapshot => snapshot.CapturedAt >= oldestEntry)
|
||||
.Select(snapshot =>
|
||||
new
|
||||
{
|
||||
snapshot.ServerId,
|
||||
snapshot.CapturedAt,
|
||||
snapshot.ClientCount
|
||||
})
|
||||
.OrderBy(snapshot => snapshot.CapturedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return history.GroupBy(snapshot => snapshot.ServerId).Select(byServer => new ClientHistoryInfo
|
||||
{
|
||||
ServerId = byServer.Key,
|
||||
ClientCounts = byServer.Select(snapshot => new ClientCountSnapshot()
|
||||
{Time = snapshot.CapturedAt, ClientCount = snapshot.ClientCount}).ToList()
|
||||
}).ToList();
|
||||
}, nameof(_clientHistoryCache), TimeSpan.MaxValue);
|
||||
|
||||
try
|
||||
{
|
||||
return await _clientHistoryCache.GetCacheItem(nameof(_clientHistoryCache), token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Could not retrieve data for {Name}", nameof(ClientHistoryAsync));
|
||||
return Enumerable.Empty<ClientHistoryInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
<PackageId>RaidMax.IW4MAdmin.Data</PackageId>
|
||||
<Title>RaidMax.IW4MAdmin.Data</Title>
|
||||
<Authors />
|
||||
<PackageVersion>1.0.5</PackageVersion>
|
||||
<PackageVersion>1.0.6</PackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -22,33 +22,36 @@ namespace Data.Helpers
|
||||
public TimeSpan ExpirationTime { get; set; }
|
||||
public Func<DbSet<T>, CancellationToken, Task<V>> Getter { get; set; }
|
||||
public V Value { get; set; }
|
||||
public bool IsExpired => (DateTime.Now - LastRetrieval.Add(ExpirationTime)).TotalSeconds > 0;
|
||||
|
||||
public bool IsExpired => ExpirationTime != TimeSpan.MaxValue &&
|
||||
(DateTime.Now - LastRetrieval.Add(ExpirationTime)).TotalSeconds > 0;
|
||||
}
|
||||
|
||||
|
||||
public DataValueCache(ILogger<DataValueCache<T, V>> logger, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public void SetCacheItem(Func<DbSet<T>, CancellationToken, Task<V>> getter, string key, TimeSpan? expirationTime = null)
|
||||
|
||||
public void SetCacheItem(Func<DbSet<T>, CancellationToken, Task<V>> getter, string key,
|
||||
TimeSpan? expirationTime = null)
|
||||
{
|
||||
if (_cacheStates.ContainsKey(key))
|
||||
{
|
||||
_logger.LogDebug("Cache key {key} is already added", key);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var state = new CacheState()
|
||||
{
|
||||
Key = key,
|
||||
Getter = getter,
|
||||
ExpirationTime = expirationTime ?? TimeSpan.FromMinutes(DefaultExpireMinutes)
|
||||
};
|
||||
|
||||
|
||||
_cacheStates.Add(key, state);
|
||||
}
|
||||
|
||||
|
||||
public async Task<V> GetCacheItem(string keyName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_cacheStates.ContainsKey(keyName))
|
||||
@ -58,7 +61,7 @@ namespace Data.Helpers
|
||||
|
||||
var state = _cacheStates[keyName];
|
||||
|
||||
if (state.IsExpired)
|
||||
if (state.IsExpired || state.Value == null)
|
||||
{
|
||||
await RunCacheUpdate(state, cancellationToken);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.27.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.29.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.27.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.29.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -23,7 +23,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.27.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.29.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -19,7 +19,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.27.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.29.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -16,7 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.27.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.29.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -17,7 +17,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.27.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.29.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -20,7 +20,7 @@
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.27.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.29.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -141,7 +141,15 @@ namespace SharedLibraryCore.Configuration
|
||||
[LocalizedDisplayName(("WEBFRONT_CONFIGURATION_ENABLE_PRIVILEGED_USER_PRIVACY"))]
|
||||
public bool EnablePrivilegedUserPrivacy { get; set; }
|
||||
|
||||
[ConfigurationIgnore]
|
||||
public bool EnableImplicitAccountLinking { get; set; } = false;
|
||||
|
||||
[ConfigurationIgnore]
|
||||
public TimeSpan MaxClientHistoryTime { get; set; } = TimeSpan.FromHours(12);
|
||||
|
||||
[ConfigurationIgnore]
|
||||
public TimeSpan ServerDataCollectionInterval { get; set; } = TimeSpan.FromMinutes(5);
|
||||
|
||||
public Dictionary<Permission, string> OverridePermissionLevelNames { get; set; } = Enum
|
||||
.GetValues(typeof(Permission))
|
||||
.Cast<Permission>()
|
||||
|
18
SharedLibraryCore/Dtos/ClientHistoryInfo.cs
Normal file
18
SharedLibraryCore/Dtos/ClientHistoryInfo.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
public class ClientHistoryInfo
|
||||
{
|
||||
public long ServerId { get; set; }
|
||||
public List<ClientCountSnapshot> ClientCounts { get; set; }
|
||||
}
|
||||
|
||||
public class ClientCountSnapshot
|
||||
{
|
||||
public DateTime Time { get; set; }
|
||||
public string TimeString => Time.ToString("yyyy-MM-ddTHH:mm:ssZ");
|
||||
public int ClientCount { get; set; }
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ namespace SharedLibraryCore.Dtos
|
||||
public List<ChatInfo> ChatHistory { get; set; }
|
||||
public List<PlayerInfo> Players { get; set; }
|
||||
public Helpers.PlayerHistory[] PlayerHistory { get; set; }
|
||||
public List<ClientCountSnapshot> ClientCountHistory { get; set; }
|
||||
public long ID { get; set; }
|
||||
public bool Online { get; set; }
|
||||
public string ConnectProtocolUrl { get; set; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using SharedLibraryCore.Dtos;
|
||||
|
||||
namespace SharedLibraryCore.Helpers
|
||||
{
|
||||
@ -31,5 +32,14 @@ namespace SharedLibraryCore.Helpers
|
||||
/// Used by CanvasJS as a point on the y axis
|
||||
/// </summary>
|
||||
public int y { get; }
|
||||
|
||||
public ClientCountSnapshot ToClientCountSnapshot()
|
||||
{
|
||||
return new ClientCountSnapshot
|
||||
{
|
||||
ClientCount = y,
|
||||
Time = When
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Helpers;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
@ -22,8 +25,16 @@ namespace SharedLibraryCore.Interfaces
|
||||
/// Gets the total number of clients connected and total clients connected in the given time frame
|
||||
/// </summary>
|
||||
/// <param name="overPeriod">how far in the past to search</param>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="token">CancellationToken</param>
|
||||
/// <returns></returns>
|
||||
Task<(int, int)> ClientCountsAsync(TimeSpan? overPeriod = null, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the client count and history over the given period
|
||||
/// </summary>
|
||||
/// <param name="overPeriod">how far in the past to search</param>
|
||||
/// <param name="token">CancellationToken</param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<ClientHistoryInfo>> ClientHistoryAsync(TimeSpan? overPeriod = null, CancellationToken token = default);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
||||
<Version>2021.8.27.1</Version>
|
||||
<Version>2021.8.29.1</Version>
|
||||
<Authors>RaidMax</Authors>
|
||||
<Company>Forever None</Company>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
@ -19,7 +19,7 @@
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Description>Shared Library for IW4MAdmin</Description>
|
||||
<PackageVersion>2021.8.27.1</PackageVersion>
|
||||
<PackageVersion>2021.8.29.1</PackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
|
||||
@ -44,7 +44,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.Data" Version="1.0.5" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.Data" Version="1.0.6" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
@ -1,45 +1,94 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Data.Models.Client.Stats;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using static SharedLibraryCore.Server;
|
||||
|
||||
namespace WebfrontCore.ViewComponents
|
||||
{
|
||||
public class ServerListViewComponent : ViewComponent
|
||||
{
|
||||
private readonly IServerDataViewer _serverDataViewer;
|
||||
private readonly ApplicationConfiguration _appConfig;
|
||||
|
||||
public ServerListViewComponent(IServerDataViewer serverDataViewer,
|
||||
ApplicationConfiguration applicationConfiguration)
|
||||
{
|
||||
_serverDataViewer = serverDataViewer;
|
||||
_appConfig = applicationConfiguration;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(Game? game)
|
||||
{
|
||||
var servers = Program.Manager.GetServers().Where(_server => !game.HasValue || _server.GameName == game);
|
||||
var servers = Program.Manager.GetServers().Where(server => !game.HasValue || server.GameName == game);
|
||||
|
||||
var serverInfo = servers.Select(s => new ServerInfo()
|
||||
var serverInfo = new List<ServerInfo>();
|
||||
|
||||
foreach (var server in servers)
|
||||
{
|
||||
Name = s.Hostname,
|
||||
ID = s.EndPoint,
|
||||
Port = s.Port,
|
||||
Map = s.CurrentMap.Alias,
|
||||
ClientCount = s.ClientNum,
|
||||
MaxClients = s.MaxClients,
|
||||
GameType = s.Gametype,
|
||||
PlayerHistory = s.ClientHistory.ToArray(),
|
||||
Players = s.GetClientsAsList()
|
||||
.Select(p => new PlayerInfo()
|
||||
var serverId = server.GetIdForServer().Result;
|
||||
var clientHistory = _serverDataViewer.ClientHistoryAsync(_appConfig.MaxClientHistoryTime,
|
||||
CancellationToken.None).Result
|
||||
.FirstOrDefault(history => history.ServerId == serverId) ??
|
||||
new ClientHistoryInfo
|
||||
{
|
||||
ServerId = serverId
|
||||
};
|
||||
|
||||
var counts = clientHistory.ClientCounts?.AsEnumerable() ?? Enumerable.Empty<ClientCountSnapshot>();
|
||||
|
||||
if (server.ClientHistory.Count > 0)
|
||||
{
|
||||
Name = p.Name,
|
||||
ClientId = p.ClientId,
|
||||
Level = p.Level.ToLocalizedLevelName(),
|
||||
LevelInt = (int)p.Level,
|
||||
Tag = p.Tag,
|
||||
ZScore = p.GetAdditionalProperty<EFClientStatistics>(IW4MAdmin.Plugins.Stats.Helpers.StatManager.CLIENT_STATS_KEY)?.ZScore
|
||||
}).ToList(),
|
||||
ChatHistory = s.ChatHistory.ToList(),
|
||||
Online = !s.Throttled,
|
||||
IPAddress = $"{(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP)}:{s.Port}",
|
||||
ConnectProtocolUrl = s.EventParser.URLProtocolFormat.FormatExt(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP, s.Port)
|
||||
}).ToList();
|
||||
counts = counts.Union(server.ClientHistory
|
||||
.Select(history => history.ToClientCountSnapshot()).Where(history =>
|
||||
history.Time > clientHistory.ClientCounts.Last().Time));
|
||||
}
|
||||
|
||||
serverInfo.Add(new ServerInfo()
|
||||
{
|
||||
Name = server.Hostname,
|
||||
ID = server.EndPoint,
|
||||
Port = server.Port,
|
||||
Map = server.CurrentMap.Alias,
|
||||
ClientCount = server.ClientNum,
|
||||
MaxClients = server.MaxClients,
|
||||
GameType = server.Gametype,
|
||||
PlayerHistory = server.ClientHistory.ToArray(),
|
||||
Players = server.GetClientsAsList()
|
||||
.Select(p => new PlayerInfo()
|
||||
{
|
||||
Name = p.Name,
|
||||
ClientId = p.ClientId,
|
||||
Level = p.Level.ToLocalizedLevelName(),
|
||||
LevelInt = (int) p.Level,
|
||||
Tag = p.Tag,
|
||||
ZScore = p.GetAdditionalProperty<EFClientStatistics>(IW4MAdmin.Plugins.Stats.Helpers
|
||||
.StatManager
|
||||
.CLIENT_STATS_KEY)?.ZScore
|
||||
}).ToList(),
|
||||
ChatHistory = server.ChatHistory.ToList(),
|
||||
ClientCountHistory =
|
||||
counts.Where(history => history.Time >= DateTime.UtcNow - _appConfig.MaxClientHistoryTime)
|
||||
.ToList(),
|
||||
Online = !server.Throttled,
|
||||
IPAddress =
|
||||
$"{(server.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : server.IP)}:{server.Port}",
|
||||
ConnectProtocolUrl = server.EventParser.URLProtocolFormat.FormatExt(
|
||||
server.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : server.IP,
|
||||
server.Port)
|
||||
});
|
||||
}
|
||||
|
||||
return View("_List", serverInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -50,5 +50,8 @@
|
||||
</div>
|
||||
|
||||
<div class="row server-history mb-4">
|
||||
<div class="server-history-row" id="server_history_@Model.ID" data-serverid="@Model.ID" data-clienthistory='@Html.Raw(Json.Serialize(Model.PlayerHistory))' data-online="@Model.Online"></div>
|
||||
<div class="server-history-row" id="server_history_@Model.ID" data-serverid="@Model.ID"
|
||||
data-clienthistory='@Html.Raw(Json.Serialize(Model.PlayerHistory))'
|
||||
data-clienthistory-ex='@Html.Raw(Json.Serialize(Model.ClientCountHistory))'
|
||||
data-online="@Model.Online"></div>
|
||||
</div>
|
@ -2,7 +2,8 @@
|
||||
///////////////////////////////////////
|
||||
// thanks to canvasjs :(
|
||||
playerHistory.forEach(function (item, i) {
|
||||
playerHistory[i].x = new Date(playerHistory[i].x);
|
||||
playerHistory[i].x = new Date(playerHistory[i].timeString);
|
||||
playerHistory[i].y = playerHistory[i].clientCount;
|
||||
});
|
||||
|
||||
return new CanvasJS.Chart(`server_history_${i}`, {
|
||||
@ -84,7 +85,7 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
$('.server-history-row').each(function (index, element) {
|
||||
let clientHistory = $(this).data('clienthistory');
|
||||
let clientHistory = $(this).data('clienthistory-ex');
|
||||
let serverId = $(this).data('serverid');
|
||||
let maxClients = parseInt($('#server_header_' + serverId + ' .server-maxclients').text());
|
||||
let primaryColor = $('title').css('background-color');
|
||||
|
Loading…
Reference in New Issue
Block a user