This repository has been archived by the owner on Oct 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Mary <[email protected]>
- Loading branch information
Showing
7 changed files
with
315 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
SS14.Launcher/Models/EngineManager/EngineManagerDynamic.Manifest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Net.Http.Json; | ||
using System.Text.Json.Serialization; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Serilog; | ||
|
||
namespace SS14.Launcher.Models.EngineManager; | ||
|
||
public sealed partial class EngineManagerDynamic | ||
{ | ||
// This part of the code is responsible for downloading and caching the Robust build manifest. | ||
|
||
private readonly SemaphoreSlim _manifestSemaphore = new(1); | ||
private readonly Stopwatch _manifestStopwatch = Stopwatch.StartNew(); | ||
|
||
private Dictionary<string, VersionInfo>? _cachedRobustVersionInfo; | ||
private TimeSpan _robustCacheValidUntil; | ||
|
||
/// <summary> | ||
/// Look up information about an engine version. | ||
/// </summary> | ||
/// <param name="version">The version number to look up.</param> | ||
/// <param name="followRedirects">Follow redirections in version info.</param> | ||
/// <param name="cancel">Cancellation token.</param> | ||
/// <returns> | ||
/// Information about the version, or null if it could not be found. | ||
/// The returned version may be different than what was requested if redirects were followed. | ||
/// </returns> | ||
private async ValueTask<FoundVersionInfo?> GetVersionInfo( | ||
string version, | ||
bool followRedirects = true, | ||
CancellationToken cancel = default) | ||
{ | ||
await _manifestSemaphore.WaitAsync(cancel); | ||
try | ||
{ | ||
return await GetVersionInfoCore(version, followRedirects, cancel); | ||
} | ||
finally | ||
{ | ||
_manifestSemaphore.Release(); | ||
} | ||
} | ||
|
||
private async ValueTask<FoundVersionInfo?> GetVersionInfoCore( | ||
string version, | ||
bool followRedirects, | ||
CancellationToken cancel) | ||
{ | ||
// If we have a cached copy, and it's not expired, we check it. | ||
if (_cachedRobustVersionInfo != null && _robustCacheValidUntil > _manifestStopwatch.Elapsed) | ||
{ | ||
// Check the version. If this fails, we immediately re-request the manifest as it may have changed. | ||
// (Connecting to a freshly-updated server with a new Robust version, within the cache window.) | ||
if (FindVersionInfoInCached(version, followRedirects) is { } foundVersionInfo) | ||
return foundVersionInfo; | ||
} | ||
|
||
await UpdateBuildManifest(cancel); | ||
|
||
return FindVersionInfoInCached(version, followRedirects); | ||
} | ||
|
||
private async Task UpdateBuildManifest(CancellationToken cancel) | ||
{ | ||
// TODO: If-Modified-Since and If-None-Match request conditions. | ||
|
||
Log.Debug("Loading manifest from {manifestUrl}...", ConfigConstants.RobustBuildsManifest); | ||
_cachedRobustVersionInfo = | ||
await _http.GetFromJsonAsync<Dictionary<string, VersionInfo>>( | ||
ConfigConstants.RobustBuildsManifest, cancellationToken: cancel); | ||
|
||
_robustCacheValidUntil = _manifestStopwatch.Elapsed + ConfigConstants.RobustManifestCacheTime; | ||
} | ||
|
||
private FoundVersionInfo? FindVersionInfoInCached(string version, bool followRedirects) | ||
{ | ||
Debug.Assert(_cachedRobustVersionInfo != null); | ||
|
||
if (!_cachedRobustVersionInfo.TryGetValue(version, out var versionInfo)) | ||
return null; | ||
|
||
if (followRedirects) | ||
{ | ||
while (versionInfo.RedirectVersion != null) | ||
{ | ||
version = versionInfo.RedirectVersion; | ||
versionInfo = _cachedRobustVersionInfo[versionInfo.RedirectVersion]; | ||
} | ||
} | ||
|
||
return new FoundVersionInfo(version, versionInfo); | ||
} | ||
|
||
private sealed record FoundVersionInfo(string Version, VersionInfo Info); | ||
|
||
private sealed record VersionInfo( | ||
bool Insecure, | ||
[property: JsonPropertyName("redirect")] | ||
string? RedirectVersion, | ||
Dictionary<string, BuildInfo> Platforms); | ||
|
||
private sealed class BuildInfo | ||
{ | ||
[JsonInclude] [JsonPropertyName("url")] | ||
public string Url = default!; | ||
|
||
[JsonInclude] [JsonPropertyName("sha256")] | ||
public string Sha256 = default!; | ||
|
||
[JsonInclude] [JsonPropertyName("sig")] | ||
public string Signature = default!; | ||
} | ||
} |
Oops, something went wrong.