optimize player history retrieval

This commit is contained in:
RaidMax 2023-05-30 18:12:57 -05:00
parent 81e2a2f6d4
commit 84ed9c8d8f
5 changed files with 78 additions and 50 deletions

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace SharedLibraryCore.Dtos
{
@ -11,11 +12,17 @@ namespace SharedLibraryCore.Dtos
public class ClientCountSnapshot
{
[JsonIgnore]
public DateTime Time { get; set; }
[JsonPropertyName("ts")]
public string TimeString => Time.ToString("yyyy-MM-ddTHH:mm:ssZ");
[JsonPropertyName("cc")]
public int ClientCount { get; set; }
[JsonPropertyName("ci")]
public bool ConnectionInterrupted { get;set; }
[JsonIgnore]
public string Map { get; set; }
[JsonPropertyName("ma")]
public string MapAlias { get; set; }
}
}

View File

@ -1,8 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces;
using WebfrontCore.Controllers.API.Models;
@ -12,9 +16,14 @@ namespace WebfrontCore.Controllers.API
[Route("api/[controller]")]
public class Server : BaseController
{
public Server(IManager manager) : base(manager)
private readonly IServerDataViewer _serverDataViewer;
private readonly ApplicationConfiguration _applicationConfiguration;
public Server(IManager manager, IServerDataViewer serverDataViewer,
ApplicationConfiguration applicationConfiguration) : base(manager)
{
_serverDataViewer = serverDataViewer;
_applicationConfiguration = applicationConfiguration;
}
[HttpGet]
@ -110,5 +119,48 @@ namespace WebfrontCore.Controllers.API
completedEvent.Output
});
}
[HttpGet("{id}/history")]
public async Task<IActionResult> GetClientHistory(string id)
{
var foundServer = Manager.GetServers().FirstOrDefault(server => server.Id == id);
if (foundServer == null)
{
return new NotFoundResult();
}
var clientHistory = (await _serverDataViewer.ClientHistoryAsync(_applicationConfiguration.MaxClientHistoryTime,
CancellationToken.None))?
.FirstOrDefault(history => history.ServerId == foundServer.LegacyDatabaseId) ??
new ClientHistoryInfo
{
ServerId = foundServer.LegacyDatabaseId,
ClientCounts = new List<ClientCountSnapshot>()
};
var counts = clientHistory.ClientCounts?.AsEnumerable() ?? Enumerable.Empty<ClientCountSnapshot>();
if (foundServer.ClientHistory.ClientCounts.Any())
{
counts = counts.Union(foundServer.ClientHistory.ClientCounts.Where(history =>
history.Time > (clientHistory.ClientCounts?.LastOrDefault()?.Time ?? DateTime.MinValue)))
.Where(history => history.Time >= DateTime.UtcNow - _applicationConfiguration.MaxClientHistoryTime);
}
if (ViewBag.Maps?.Count == 0)
{
return Json(counts.ToList());
}
var clientCountSnapshots = counts.ToList();
foreach (var count in clientCountSnapshots)
{
count.MapAlias = foundServer.Maps.FirstOrDefault(map => map.Name == count.Map)?.Alias ??
count.Map;
}
return Json(clientCountSnapshots);
}
}
}

View File

@ -1,29 +1,21 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using SharedLibraryCore.Dtos;
using System.Linq;
using System.Threading;
using Data.Models;
using Data.Models.Client.Stats;
using IW4MAdmin.Plugins.Stats.Helpers;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
namespace WebfrontCore.ViewComponents
{
public class ServerListViewComponent : ViewComponent
{
private readonly IServerDataViewer _serverDataViewer;
private readonly ApplicationConfiguration _appConfig;
private readonly DefaultSettings _defaultSettings;
public ServerListViewComponent(IServerDataViewer serverDataViewer,
ApplicationConfiguration applicationConfiguration, DefaultSettings defaultSettings)
public ServerListViewComponent(DefaultSettings defaultSettings)
{
_serverDataViewer = serverDataViewer;
_appConfig = applicationConfiguration;
_defaultSettings = defaultSettings;
}
@ -46,25 +38,6 @@ namespace WebfrontCore.ViewComponents
foreach (var server in servers)
{
var serverId = server.GetIdForServer().Result;
var clientHistory = _serverDataViewer.ClientHistoryAsync(_appConfig.MaxClientHistoryTime,
CancellationToken.None).Result?
.FirstOrDefault(history => history.ServerId == serverId) ??
new ClientHistoryInfo
{
ServerId = serverId,
ClientCounts = new List<ClientCountSnapshot>()
};
var counts = clientHistory.ClientCounts?.AsEnumerable() ?? Enumerable.Empty<ClientCountSnapshot>();
if (server.ClientHistory.ClientCounts.Any())
{
counts = counts.Union(server.ClientHistory.ClientCounts.Where(history =>
history.Time > (clientHistory.ClientCounts?.LastOrDefault()?.Time ?? DateTime.MinValue)))
.Where(history => history.Time >= DateTime.UtcNow - _appConfig.MaxClientHistoryTime);
}
serverInfo.Add(new ServerInfo
{
Name = server.Hostname,
@ -76,11 +49,7 @@ namespace WebfrontCore.ViewComponents
MaxClients = server.MaxClients,
PrivateClientSlots = server.PrivateClientSlots,
GameType = server.GametypeName,
ClientHistory = new ClientHistoryInfo
{
ServerId = server.EndPoint,
ClientCounts = counts.ToList()
},
ClientHistory = new ClientHistoryInfo(),
Players = server.GetClientsAsList()
.Select(client => new PlayerInfo
{

View File

@ -82,8 +82,7 @@
}
<div class="server-history">
<div class="server-history-row m-auto bg-dark-dm bg-light-lm rounded-bottom" style="position:relative; width: 100%" id="server_history_@Model.ID" data-serverid="@Model.ID"
data-clienthistory='@Html.Raw(Json.Serialize(Model.ClientHistory))'
<div class="server-history-row m-auto bg-dark-dm bg-light-lm rounded-bottom" style="position:relative; width: 100%" id="server_history_@Model.ID" data-serverid="@Model.ID" data-server-endpoint="@Model.Endpoint"
data-clienthistory-ex='@Html.Raw(Json.Serialize(Model.ClientHistory.ClientCounts))'
data-online="@Model.Online">
<canvas id="server_history_canvas_@Model.ID" class="rounded-bottom" height="100"></canvas>

View File

@ -27,25 +27,25 @@ function getPlayerHistoryChart(playerHistory, i, width, maxClients) {
let lastMap = '';
playerHistory.forEach((elem, i) => {
if (elem.map !== lastMap) {
if (elem.ma !== lastMap) {
mapChange.push(i);
lastMap = elem.map;
lastMap = elem;
}
if (elem.connectionInterrupted) {
offlineTime.push({
clientCount: maxClients,
timeString: elem.timeString
timeString: elem.ts
});
onlineTime.push({
clientCount: 0,
timeString: elem.timeString
timeString: elem.ts
})
} else {
offlineTime.push({
clientCount: 0,
timeString: elem.timeString
timeString: elem.ts
});
onlineTime.push(elem)
@ -60,9 +60,9 @@ function getPlayerHistoryChart(playerHistory, i, width, maxClients) {
return new Chart(document.getElementById(`server_history_canvas_${i}`), {
type: 'line',
data: {
labels: playerHistory.map(history => history.timeString),
labels: playerHistory.map(history => history.ts),
datasets: [{
data: onlineTime.map(history => history.clientCount),
data: onlineTime.map(history => history.cc),
backgroundColor: fillColor,
borderColor: primaryColor,
borderWidth: 2,
@ -70,7 +70,7 @@ function getPlayerHistoryChart(playerHistory, i, width, maxClients) {
hoverBorderWidth: 2
},
{
data: offlineTime.map(history => history.clientCount),
data: offlineTime.map(history => history.cc),
backgroundColor: createDiagonalPattern(offlineFillColor),
borderColor: offlineFillColor,
borderWidth: 2,
@ -88,7 +88,7 @@ function getPlayerHistoryChart(playerHistory, i, width, maxClients) {
callbacks: {
// todo: localization at some point
title: context => moment(context[0].label).local().calendar(),
label: context => context.datasetIndex !== 1 ? `${context.value} ${_localization['WEBFRONT_SCRIPT_SERVER_PLAYERS']} | ${playerHistory[context.index].mapAlias}` : context.value === '0' ? '' : _localization['WEBFRONT_SCRIPT_SERVER_UNREACHABLE'],
label: context => context.datasetIndex !== 1 ? `${context.value} ${_localization['WEBFRONT_SCRIPT_SERVER_PLAYERS']} | ${playerHistory[context.index].ma}` : context.value === '0' ? '' : _localization['WEBFRONT_SCRIPT_SERVER_UNREACHABLE'],
},
mode: 'nearest',
intersect: false,
@ -153,13 +153,14 @@ $(document).ready(function () {
$(this).parent().parent().find('.server-header-ip-address').show();
});
$('.server-history-row').each(function (index, element) {
let clientHistory = $(this).data('clienthistory-ex');
$('.server-history-row').each(async function (index, element) {
const serverId = $(this).data('serverid');
const serverEp = $(this).data('server-endpoint');
setInterval(() => refreshClientActivity(serverId), 2000 + (index * 100));
let maxClients = parseInt($('#server_header_' + serverId + ' .server-maxclients').text());
let width = $('.server-header').first().width();
getPlayerHistoryChart(clientHistory, serverId, width, maxClients);
const clientHistory = await fetch(`/api/server/${serverEp}/history`);
getPlayerHistoryChart(await clientHistory.json(), serverId, width, maxClients);
});
$('.moment-date').each((index, element) => {