1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2024-10-17 03:42:50 +02:00
Minecraft-Phantom-Panel/Controller/Phantom.Controller.Minecraft/MinecraftVersions.cs

76 lines
3.0 KiB
C#

using System.Collections.Immutable;
using System.Diagnostics;
using Phantom.Common.Data.Minecraft;
using Phantom.Utils.Logging;
using Serilog;
namespace Phantom.Controller.Minecraft;
public sealed class MinecraftVersions : IDisposable {
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftVersions>();
private static readonly TimeSpan CacheRetentionTime = TimeSpan.FromMinutes(10);
private readonly MinecraftVersionApi api = new ();
private readonly Stopwatch cacheTimer = new ();
private readonly SemaphoreSlim cacheSemaphore = new (1, 1);
private bool IsCacheNotExpired => cacheTimer.IsRunning && cacheTimer.Elapsed < CacheRetentionTime;
private ImmutableArray<MinecraftVersion>? cachedVersions;
private readonly Dictionary<string, FileDownloadInfo?> cachedServerExecutables = new ();
public void Dispose() {
api.Dispose();
cacheSemaphore.Dispose();
}
public async Task<ImmutableArray<MinecraftVersion>> GetVersions(CancellationToken cancellationToken) {
return await GetCachedObject(() => cachedVersions != null, () => cachedVersions.GetValueOrDefault(), v => cachedVersions = v, LoadVersions, cancellationToken);
}
private async Task<ImmutableArray<MinecraftVersion>> LoadVersions(CancellationToken cancellationToken) {
ImmutableArray<MinecraftVersion> versions = await api.GetVersions(cancellationToken);
Logger.Information("Refreshed Minecraft version cache, {Versions} version(s) found.", versions.Length);
return versions;
}
public async Task<FileDownloadInfo?> GetServerExecutableInfo(string version, CancellationToken cancellationToken) {
var versions = await GetVersions(cancellationToken);
return await GetCachedObject(() => cachedServerExecutables.ContainsKey(version), () => cachedServerExecutables[version], v => cachedServerExecutables[version] = v, ct => LoadServerExecutableInfo(versions, version, ct), cancellationToken);
}
private async Task<FileDownloadInfo?> LoadServerExecutableInfo(ImmutableArray<MinecraftVersion> versions, string version, CancellationToken cancellationToken) {
var info = await api.GetServerExecutableInfo(versions, version, cancellationToken);
if (info == null) {
Logger.Information("Refreshed Minecraft {Version} server executable cache, no file found.", version);
}
else {
Logger.Information("Refreshed Minecraft {Version} server executable cache, found file: {Url}.", version, info.DownloadUrl);
}
return info;
}
private async Task<T> GetCachedObject<T>(Func<bool> isLoaded, Func<T> fieldGetter, Action<T> fieldSetter, Func<CancellationToken, Task<T>> fieldLoader, CancellationToken cancellationToken) {
if (IsCacheNotExpired && isLoaded()) {
return fieldGetter();
}
await cacheSemaphore.WaitAsync(cancellationToken);
try {
if (IsCacheNotExpired && isLoaded()) {
return fieldGetter();
}
T result = await fieldLoader(cancellationToken);
fieldSetter(result);
cacheTimer.Restart();
return result;
} finally {
cacheSemaphore.Release();
}
}
}