();
+
+ // get the latest name from the db
+ var playerData = await context.ReplayParticipants
+ .Where(p => p.PlayerGuid == guid)
+ .OrderByDescending(p => p.Id)
+ .FirstOrDefaultAsync();
+
+ if (playerData != null) return playerData.Username;
+
+ // ??? try to get using api
+ var playerDataApi = await _apiHelper.FetchPlayerDataFromGuid(guid);
+ return playerDataApi.Username;
+
+ }
+
private Guid GenerateRandomGuid()
{
var guidBytes = new byte[16];
From da28392a5097831b530f7b5553fc0ea4e4f34e9c Mon Sep 17 00:00:00 2001
From: Simon <63975668+Simyon264@users.noreply.github.com>
Date: Mon, 11 Nov 2024 03:52:45 +0100
Subject: [PATCH 2/3] Clarify message
---
ReplayBrowser/Pages/Leaderboard.razor | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ReplayBrowser/Pages/Leaderboard.razor b/ReplayBrowser/Pages/Leaderboard.razor
index 8f30a9b..41415b6 100644
--- a/ReplayBrowser/Pages/Leaderboard.razor
+++ b/ReplayBrowser/Pages/Leaderboard.razor
@@ -108,7 +108,7 @@ else
@if(LeaderboardData.Length == 0)
{
-
No data was found for the selected servers and time range. Data is probably still collected and calculated. Please try again later.
+
No data was found for the selected servers and time range. Data is probably still collected and calculated. Please try again later. This will take a long time.
}
From a2b82c6b64ab121de11381db7767e0bff29cd33f Mon Sep 17 00:00:00 2001
From: Simon <63975668+Simyon264@users.noreply.github.com>
Date: Mon, 11 Nov 2024 04:39:16 +0100
Subject: [PATCH 3/3] uuuhhh AMONG US
---
ReplayBrowser/Pages/Leaderboard.razor | 9 +++
ReplayBrowser/Services/LeaderboardService.cs | 79 +++++++++++++++-----
2 files changed, 70 insertions(+), 18 deletions(-)
diff --git a/ReplayBrowser/Pages/Leaderboard.razor b/ReplayBrowser/Pages/Leaderboard.razor
index 41415b6..d1d2547 100644
--- a/ReplayBrowser/Pages/Leaderboard.razor
+++ b/ReplayBrowser/Pages/Leaderboard.razor
@@ -1,4 +1,5 @@
@page "/leaderboard"
+@using Humanizer
@using Microsoft.AspNetCore.Components.Authorization
@using ReplayBrowser.Models
@using ReplayBrowser.Services
@@ -20,6 +21,14 @@
Leaderboard
Leaderboards
+Leaderboards update every 24 hours.
+@if(LeaderboardService.IsUpdating) {
+
+
The leaderboards are currently updating. This may take a while. Please be patient.
+
Update started: @LeaderboardService.UpdateStarted.Humanize(utcDate: true). Current progress: @LeaderboardService.UpdateProgress / @LeaderboardService.UpdateTotal
+
+}
+
@if(RequestedPrivate)
{
diff --git a/ReplayBrowser/Services/LeaderboardService.cs b/ReplayBrowser/Services/LeaderboardService.cs
index 61ac4d9..76dda83 100644
--- a/ReplayBrowser/Services/LeaderboardService.cs
+++ b/ReplayBrowser/Services/LeaderboardService.cs
@@ -27,7 +27,12 @@ public class LeaderboardService : IHostedService, IDisposable
private readonly AccountService _accountService;
private readonly IConfiguration _configuration;
- private List RedactedAccounts;
+ private const int MaxConcurrentUpdates = 10;
+
+ public static bool IsUpdating { get; private set; } = false;
+ public static DateTime UpdateStarted { get; private set; } = DateTime.MinValue;
+ public static int UpdateProgress { get; private set; } = 0;
+ public static int UpdateTotal { get; private set; } = 0;
public LeaderboardService(Ss14ApiHelper apiHelper, IServiceScopeFactory factory, AccountService accountService, IConfiguration configuration)
{
@@ -39,43 +44,62 @@ public LeaderboardService(Ss14ApiHelper apiHelper, IServiceScopeFactory factory,
public Task StartAsync(CancellationToken cancellationToken)
{
- _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromHours(6));
+ _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromHours(24));
return Task.CompletedTask;
}
private async void DoWork(object? state)
{
+ if (IsUpdating)
+ {
+ Log.Warning("Leaderboard update already in progress, skipping this update.");
+ return;
+ }
+
+ IsUpdating = true;
+ UpdateStarted = DateTime.UtcNow;
+
var sw = new Stopwatch();
sw.Start();
Log.Information("Updating leaderboards...");
- // Fetch all the redacted players, cache it
- // Yeah this ignores whether someone's an admin and doesn't let them bypass this
- // Better for performance though
-
- using (var scope = _scopeFactory.CreateScope()) {
- var context = scope.ServiceProvider.GetRequiredService();
- RedactedAccounts = await context.Accounts
- .Where(a => a.Settings.RedactInformation)
- .Select(a => a.Guid)
- .ToListAsync();
- }
-
var servers = _configuration.GetSection("ReplayUrls").Get()!.Select(x => x.FallBackServerName)
.Distinct().ToList();
var combinations = GetCombinations(servers).ToList();
+ Log.Information("Total combinations: {Combinations}", combinations.Count);
+
+ UpdateTotal = combinations.Count * Enum.GetValues().Length;
+ var semaphore = new SemaphoreSlim(MaxConcurrentUpdates);
+ var tasks = new List();
foreach (var serverArr in combinations)
{
- foreach (var rangeOption in Enum.GetValues())
+ var values = Enum.GetValues();
+ foreach (var rangeOption in values)
{
- await GenerateLeaderboard(rangeOption, serverArr.ToArray());
+ await semaphore.WaitAsync();
+ tasks.Add(Task.Run(async () =>
+ {
+ try
+ {
+ await GenerateLeaderboard(rangeOption, serverArr.ToArray());
+ UpdateProgress++;
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ }));
}
}
+ await Task.WhenAll(tasks);
+
sw.Stop();
Log.Information("Leaderboards updated in {Time}", sw.Elapsed);
+
+ IsUpdating = false;
}
static IEnumerable> GetCombinations(List list)
@@ -199,7 +223,7 @@ public void Dispose()
var returnList = new List();
foreach (var (key, value) in finalReturned)
{
- returnList.Add(await FinalizeLeaderboard(key, value.NameColumn, value, accountCaller?.Guid ?? Guid.Empty, entries));
+ returnList.Add(await FinalizeLeaderboard(key, value.NameColumn, value, accountCaller?.Guid ?? Guid.Empty, authenticationState, entries));
}
return returnList.ToArray();
@@ -531,6 +555,7 @@ private async Task FinalizeLeaderboard(
string columnName,
Leaderboard data,
Guid targetPlayer,
+ AuthenticationState authenticationState,
int limit = 10
)
{
@@ -541,7 +566,25 @@ private async Task FinalizeLeaderboard(
Data = new Dictionary()
};
- var players = data.Data.Values.Where(p => p.Player?.PlayerGuid is null || p.Player.PlayerGuid == Guid.Empty || !RedactedAccounts.Contains(p.Player?.PlayerGuid ?? Guid.Empty)).ToList();
+ var redactedAccounts = await _scopeFactory.CreateScope().ServiceProvider.GetRequiredService().Accounts
+ .Where(a => a.Settings.RedactInformation)
+ .Select(a => a.Guid)
+ .ToListAsync();
+
+ var account = await _accountService.GetAccount(authenticationState);
+ // Remove any redacted accounts
+ var players = data.Data.Values.ToList();
+ if (account == null || !account.IsAdmin)
+ {
+ players = players.Where(p =>
+ p.Player?.PlayerGuid == null
+ || (
+ !redactedAccounts.Contains((Guid)p.Player.PlayerGuid)
+ && account?.Guid != p.Player.PlayerGuid // Users can see their own data even if redacted
+ ))
+ .ToList();
+ }
+
players.Sort((a, b) => b.Count.CompareTo(a.Count));
for (var i = 0; i < players.Count; i++)