diff --git a/.idea/.idea.AlayaCore/.idea/vcs.xml b/.idea/.idea.AlayaCore/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/.idea.AlayaCore/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AlayaCore.sln.DotSettings.user b/AlayaCore.sln.DotSettings.user
index d83be9d..c9dd86c 100644
--- a/AlayaCore.sln.DotSettings.user
+++ b/AlayaCore.sln.DotSettings.user
@@ -1,3 +1,22 @@
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
ForceIncluded
- ForceIncluded
\ No newline at end of file
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
\ No newline at end of file
diff --git a/AlayaCore/Abstractions/Interfaces/Clients/IHttpClient.cs b/AlayaCore/Abstractions/Interfaces/Clients/IHttpClient.cs
index b34bb34..42d7bd0 100644
--- a/AlayaCore/Abstractions/Interfaces/Clients/IHttpClient.cs
+++ b/AlayaCore/Abstractions/Interfaces/Clients/IHttpClient.cs
@@ -11,5 +11,7 @@ namespace AlayaCore.Abstractions.Interfaces.Clients
Uri uri,
HttpCompletionOption completionOption,
CancellationToken cancellationToken);
+
+ HttpClient HttpClient { get; }
}
}
\ No newline at end of file
diff --git a/AlayaCore/Abstractions/Interfaces/Services/IGameInstallService.cs b/AlayaCore/Abstractions/Interfaces/Services/IGameInstallService.cs
index 8c78eea..5fffa91 100644
--- a/AlayaCore/Abstractions/Interfaces/Services/IGameInstallService.cs
+++ b/AlayaCore/Abstractions/Interfaces/Services/IGameInstallService.cs
@@ -10,6 +10,7 @@ namespace AlayaCore.Abstractions.Interfaces.Services
{
public interface IGameInstallService
{
+
Task EnsureMinecraftInstalledAsync(
ManifestModel manifest,
InstallEnvironment environment,
diff --git a/AlayaCore/Abstractions/Interfaces/Services/IManifestService.cs b/AlayaCore/Abstractions/Interfaces/Services/IManifestService.cs
index f6d8f31..b60a74e 100644
--- a/AlayaCore/Abstractions/Interfaces/Services/IManifestService.cs
+++ b/AlayaCore/Abstractions/Interfaces/Services/IManifestService.cs
@@ -16,7 +16,7 @@ namespace AlayaCore.Abstractions.Interfaces.Services
Task GetLocalLauncherManifestAsync(CancellationToken cancellationToken = default);
- Task GetRemoteLauncherManifestHashAsync(CancellationToken cancellationToken = default);
+ Task GetRemoteLauncherManifestAsync(CancellationToken cancellationToken = default);
Task GetRemoteCoreManifestVersionAsync(CancellationToken cancellationToken = default);
}
}
\ No newline at end of file
diff --git a/AlayaCore/LaunchDirector.cs b/AlayaCore/LaunchDirector.cs
index d8ff114..8e001e3 100644
--- a/AlayaCore/LaunchDirector.cs
+++ b/AlayaCore/LaunchDirector.cs
@@ -20,7 +20,6 @@ namespace AlayaCore
private readonly IManifestService _manifestService;
private readonly IUpdateService _updateService;
private readonly IInstallStateService _installStateService;
- private readonly IJavaService _javaService;
private readonly IModService _modService;
private readonly IGameLaunchService _gameLaunchService;
private readonly IGameInstallService _gameInstallService;
@@ -36,7 +35,6 @@ namespace AlayaCore
IManifestService manifestService,
IUpdateService updateService,
IInstallStateService installStateService,
- IJavaService javaService,
IModService modService,
IGameLaunchService gameLaunchService,
IGameInstallService gameInstallService,
@@ -47,7 +45,6 @@ namespace AlayaCore
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
_updateService = updateService ?? throw new ArgumentNullException(nameof(updateService));
_installStateService = installStateService ?? throw new ArgumentNullException(nameof(installStateService));
- _javaService = javaService ?? throw new ArgumentNullException(nameof(javaService));
_modService = modService ?? throw new ArgumentNullException(nameof(modService));
_gameLaunchService = gameLaunchService ?? throw new ArgumentNullException(nameof(gameLaunchService));
_gameInstallService = gameInstallService ?? throw new ArgumentNullException(nameof(gameInstallService));
@@ -68,7 +65,6 @@ namespace AlayaCore
{
LaunchPlan launcherUpdatePlan = new LaunchPlan(
launcherNeedsUpdate: true,
- javaNeedsInstallOrUpdate: false,
minecraftNeedsInstallOrUpdate: false,
neoforgeNeedsInstallOrUpdate: false,
modsNeedSync: false,
@@ -84,17 +80,13 @@ namespace AlayaCore
.GetCurrentEnvironmentAsync(cancellationToken)
.ConfigureAwait(false);
- bool javaNeedsInstallOrUpdate =
- _options.ForceReinstall ||
- !environment.JavaInstalled ||
- !string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase);
-
bool needAuthenticating = !await _authService
.IsAuthenticatedAsync(cancellationToken)
.ConfigureAwait(false);
-
+
bool minecraftNeedsInstallOrUpdate =
_options.ForceReinstall ||
+ !environment.JavaInstalled ||
!environment.MinecraftInstalled ||
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase);
@@ -109,7 +101,6 @@ namespace AlayaCore
LaunchPlan plan = new LaunchPlan(
launcherNeedsUpdate: false,
- javaNeedsInstallOrUpdate: javaNeedsInstallOrUpdate,
minecraftNeedsInstallOrUpdate: minecraftNeedsInstallOrUpdate,
neoforgeNeedsInstallOrUpdate: neoforgeNeedsInstallOrUpdate,
modsNeedSync: modsNeedSync,
@@ -119,8 +110,9 @@ namespace AlayaCore
return plan;
}
- public async Task InstallOrUpdateAsync(CancellationToken cancellationToken = default,
- EventHandler? minecraftProgess = null,
+ public async Task InstallOrUpdateAsync(
+ CancellationToken cancellationToken = default,
+ EventHandler? minecraftProgress = null,
EventHandler? byteProgress = null,
IProgress? neoForgeProgress = null,
IProgress? neoForgeByteProgress = null)
@@ -133,8 +125,8 @@ namespace AlayaCore
{
cancellationToken.ThrowIfCancellationRequested();
- ManifestModel? manifest;
- InstallEnvironment? environment;
+ ManifestModel manifest;
+ InstallEnvironment environment;
switch (plan.State)
{
@@ -152,23 +144,12 @@ namespace AlayaCore
return;
}
- case LaunchState.InstallJava:
- {
- manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
- environment = await _installStateService
- .GetCurrentEnvironmentAsync(cancellationToken)
- .ConfigureAwait(false);
-
- await _javaService
- .EnsureValidJavaInstalledAsync(manifest, environment, cancellationToken)
- .ConfigureAwait(false);
-
- break;
- }
-
case LaunchState.NeedAuthenticating:
{
- await _authService.AuthenticateAsync(cancellationToken);
+ await _authService
+ .AuthenticateAsync(cancellationToken)
+ .ConfigureAwait(false);
+
break;
}
@@ -180,7 +161,12 @@ namespace AlayaCore
.ConfigureAwait(false);
await _gameInstallService
- .EnsureMinecraftInstalledAsync(manifest, environment, cancellationToken, minecraftProgess, byteProgress)
+ .EnsureMinecraftInstalledAsync(
+ manifest,
+ environment,
+ cancellationToken,
+ minecraftProgress,
+ byteProgress)
.ConfigureAwait(false);
break;
@@ -194,8 +180,13 @@ namespace AlayaCore
.ConfigureAwait(false);
await _gameInstallService
- .EnsureNeoForgeInstalledAsync(manifest, environment, cancellationToken, neoForgeProgress, neoForgeByteProgress)
- .ConfigureAwait(false);
+ .EnsureNeoForgeInstalledAsync(
+ manifest,
+ environment,
+ cancellationToken,
+ neoForgeProgress,
+ neoForgeByteProgress)
+ .ConfigureAwait(false);
break;
}
@@ -230,7 +221,9 @@ namespace AlayaCore
if (_options.ForceReinstall)
{
- await _settingsService.SetForceReinstallAsync(false, cancellationToken).ConfigureAwait(false);
+ await _settingsService
+ .SetForceReinstallAsync(false, cancellationToken)
+ .ConfigureAwait(false);
}
plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
diff --git a/AlayaCore/Models/Manifests/DTO/ManifestDto.cs b/AlayaCore/Models/Manifests/DTO/ManifestDto.cs
index 14f99f2..f9a4231 100644
--- a/AlayaCore/Models/Manifests/DTO/ManifestDto.cs
+++ b/AlayaCore/Models/Manifests/DTO/ManifestDto.cs
@@ -19,6 +19,9 @@ namespace AlayaCore.Models.Manifests.DTO
[JsonProperty("javaUrl", Required = Required.Always)]
[JsonConverter(typeof(UriConverter))]
public Uri RequiredJavaUrl { get; set; } = null!;
+
+ [JsonProperty("javaArchiveHash", Required = Required.Always)]
+ public string JavaArchiveHash { get; set; } = string.Empty;
[JsonProperty("minecraftVersion", Required = Required.Always)]
public string MinecraftVersion { get; set; } = string.Empty;
diff --git a/AlayaCore/Models/Manifests/LauncherManifestModel.cs b/AlayaCore/Models/Manifests/LauncherManifestModel.cs
index fe8daae..d6b4034 100644
--- a/AlayaCore/Models/Manifests/LauncherManifestModel.cs
+++ b/AlayaCore/Models/Manifests/LauncherManifestModel.cs
@@ -4,7 +4,7 @@ namespace AlayaCore.Models.Manifests
{
public sealed class LauncherManifestModel
{
- public Version Version { get; }
+ public Version? Version { get; }
public string Sha512Hash { get; }
public Uri DownloadUri { get; }
diff --git a/AlayaCore/Models/Manifests/ManifestModel.cs b/AlayaCore/Models/Manifests/ManifestModel.cs
index 5e02235..40df915 100644
--- a/AlayaCore/Models/Manifests/ManifestModel.cs
+++ b/AlayaCore/Models/Manifests/ManifestModel.cs
@@ -9,8 +9,6 @@ namespace AlayaCore.Models.Manifests
public sealed class ManifestModel
{
public Version AlayaVersion { get; }
- public string RequiredJavaVersion { get; }
- public Uri RequiredJavaBaseUrl { get; }
public string MinecraftVersion { get; }
public string NeoforgedVersion { get; }
@@ -20,8 +18,6 @@ namespace AlayaCore.Models.Manifests
public ManifestModel(
Version alayaVersion,
- string requiredJavaVersion,
- Uri requiredJavaUrl,
string minecraftVersion,
string neoforgedVersion,
Uri serverUrl,
@@ -29,8 +25,6 @@ namespace AlayaCore.Models.Manifests
IEnumerable files)
{
AlayaVersion = alayaVersion ?? throw new ArgumentNullException(nameof(alayaVersion));
- RequiredJavaVersion = RequireNonEmpty(requiredJavaVersion, nameof(requiredJavaVersion));
- RequiredJavaBaseUrl = requiredJavaUrl ?? throw new ArgumentNullException(nameof(requiredJavaUrl));
MinecraftVersion = RequireNonEmpty(minecraftVersion, nameof(minecraftVersion));
diff --git a/AlayaCore/Services/AuthService.cs b/AlayaCore/Services/AuthService.cs
index 81cf3fb..3700e99 100644
--- a/AlayaCore/Services/AuthService.cs
+++ b/AlayaCore/Services/AuthService.cs
@@ -26,12 +26,34 @@ namespace AlayaCore.Services
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
}
- public Task IsAuthenticatedAsync(CancellationToken cancellationToken = default)
+ public async Task IsAuthenticatedAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
- bool authenticated = _session != null && _session.CheckIsValid();
- return Task.FromResult(authenticated);
+ if (_session != null && _session.CheckIsValid())
+ {
+ return true;
+ }
+
+ try
+ {
+ JELoginHandler loginHandler = await BuildHandlerAsync().ConfigureAwait(false);
+
+ _session = await loginHandler
+ .AuthenticateSilently(cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+
+ return _session != null && _session.CheckIsValid();
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch
+ {
+ _session = null;
+ return false;
+ }
}
public async Task AuthenticateAsync(CancellationToken cancellationToken = default)
@@ -46,6 +68,10 @@ namespace AlayaCore.Services
.AuthenticateSilently(cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
catch
{
_session = await loginHandler
diff --git a/AlayaCore/Services/GameInstallService.cs b/AlayaCore/Services/GameInstallService.cs
index fa4b9ff..db1aaef 100644
--- a/AlayaCore/Services/GameInstallService.cs
+++ b/AlayaCore/Services/GameInstallService.cs
@@ -12,8 +12,6 @@ using CmlLib.Core;
using CmlLib.Core.Installer.NeoForge;
using CmlLib.Core.Installer.NeoForge.Installers;
using CmlLib.Core.Installers;
-using CmlLib.Core.Java;
-using CmlLib.Core.VersionMetadata;
namespace AlayaCore.Services
{
@@ -39,7 +37,7 @@ namespace AlayaCore.Services
ManifestModel manifest,
InstallEnvironment environment,
CancellationToken cancellationToken = default,
- EventHandler? minecraftProgess = null,
+ EventHandler? minecraftProgress = null,
EventHandler? byteProgress = null)
{
if (manifest == null)
@@ -77,7 +75,7 @@ namespace AlayaCore.Services
await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false);
}
- MinecraftLauncher launcher = GetOrCreateLauncher(minecraftProgess, byteProgress);
+ MinecraftLauncher launcher = GetOrCreateLauncher(minecraftProgress, byteProgress);
await launcher
.InstallAsync(manifest.MinecraftVersion, cancellationToken)
@@ -122,6 +120,16 @@ namespace AlayaCore.Services
return;
}
+ bool neoForgeMismatch =
+ environment.NeoforgedInstalled &&
+ !string.Equals(environment.NeoforgedVersion, manifest.NeoforgedVersion, StringComparison.OrdinalIgnoreCase);
+
+ if (neoForgeMismatch)
+ {
+ await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false);
+ return;
+ }
+
if (!environment.MinecraftInstalled ||
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase))
{
@@ -135,12 +143,13 @@ namespace AlayaCore.Services
MinecraftLauncher launcher = GetOrCreateLauncher();
- await DownloadAndInstallNeoForgeAsync(
+ await InstallNeoForgeAsync(
launcher,
manifest,
environment,
cancellationToken,
- progress, byteProgress).ConfigureAwait(false);
+ progress,
+ byteProgress).ConfigureAwait(false);
await launcher
.InstallAsync(manifest.MinecraftVersion, cancellationToken)
@@ -193,10 +202,13 @@ namespace AlayaCore.Services
_gamePath = null;
_minecraftLauncher = null;
- await _settingsService.UpdateLaunchVersionAsync(string.Empty, cancellationToken).ConfigureAwait(false);
+ await _settingsService
+ .UpdateLaunchVersionAsync(string.Empty, cancellationToken)
+ .ConfigureAwait(false);
}
- private MinecraftLauncher GetOrCreateLauncher(EventHandler? minecraftProgess = null,
+ private MinecraftLauncher GetOrCreateLauncher(
+ EventHandler? minecraftProgress = null,
EventHandler? byteProgress = null)
{
if (_minecraftLauncher != null)
@@ -206,18 +218,21 @@ namespace AlayaCore.Services
_gamePath = new AlayaPath(_fileStore);
_minecraftLauncher = new MinecraftLauncher(_gamePath);
-
- if(byteProgress != null)
+
+ if (byteProgress != null)
+ {
_minecraftLauncher.ByteProgressChanged += byteProgress;
-
- if(minecraftProgess != null)
- _minecraftLauncher.FileProgressChanged += minecraftProgess;
-
+ }
+
+ if (minecraftProgress != null)
+ {
+ _minecraftLauncher.FileProgressChanged += minecraftProgress;
+ }
return _minecraftLauncher;
}
- private async Task DownloadAndInstallNeoForgeAsync(
+ private async Task InstallNeoForgeAsync(
MinecraftLauncher launcher,
ManifestModel manifest,
InstallEnvironment environment,
@@ -230,16 +245,6 @@ namespace AlayaCore.Services
throw new ArgumentNullException(nameof(launcher));
}
- bool neoForgeMismatch =
- environment.NeoforgedInstalled &&
- !string.Equals(environment.NeoforgedVersion, manifest.NeoforgedVersion, StringComparison.OrdinalIgnoreCase);
-
- if (neoForgeMismatch)
- {
- await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false);
- return;
- }
-
NeoForgeInstaller installer = new NeoForgeInstaller(launcher);
NeoForgeInstallOptions options = new NeoForgeInstallOptions
@@ -248,12 +253,16 @@ namespace AlayaCore.Services
JavaPath = environment.JavaPath,
SkipIfAlreadyInstalled = true
};
-
- if(progress != null)
+
+ if (progress != null)
+ {
options.FileProgress = progress;
-
- if(byteProgress != null)
+ }
+
+ if (byteProgress != null)
+ {
options.ByteProgress = byteProgress;
+ }
string version = await installer
.Install(manifest.MinecraftVersion, manifest.NeoforgedVersion, options)
diff --git a/AlayaCore/Services/GameLaunchService.cs b/AlayaCore/Services/GameLaunchService.cs
index d4998f7..8b71bd4 100644
--- a/AlayaCore/Services/GameLaunchService.cs
+++ b/AlayaCore/Services/GameLaunchService.cs
@@ -8,9 +8,7 @@ using AlayaCore.Installation;
using AlayaCore.Models;
using AlayaCore.Models.Configuration;
using AlayaCore.Models.Manifests;
-using AlayaCore.Utilities.Enums;
using CmlLib.Core;
-using CmlLib.Core.Auth;
using CmlLib.Core.ProcessBuilder;
namespace AlayaCore.Services
@@ -22,7 +20,6 @@ namespace AlayaCore.Services
private readonly ILaunchDirector _director;
private readonly GameOptions _gameOptions;
- private AlayaPath? _gamePath;
private MinecraftLauncher? _minecraftLauncher;
public GameLaunchService(
@@ -69,7 +66,11 @@ namespace AlayaCore.Services
cancellationToken.ThrowIfCancellationRequested();
- MLaunchOption option = await BuildLaunchOptions(manifest, environment);
+ MLaunchOption option = await BuildLaunchOptionsAsync(
+ manifest,
+ environment,
+ cancellationToken).ConfigureAwait(false);
+
MinecraftLauncher launcher = GetOrCreateLauncher();
var process = await launcher
@@ -89,27 +90,23 @@ namespace AlayaCore.Services
return _minecraftLauncher;
}
- _gamePath = new AlayaPath(_fileStore);
- _minecraftLauncher = new MinecraftLauncher(_gamePath);
-
+ _minecraftLauncher = new MinecraftLauncher(new AlayaPath(_fileStore));
return _minecraftLauncher;
}
- private string GetMinecraftPath()
- {
- return _fileStore.GetOrCreate(FolderLocation.Game);
- }
-
- private async Task BuildLaunchOptions(ManifestModel manifest, InstallEnvironment environment)
+ private async Task BuildLaunchOptionsAsync(
+ ManifestModel manifest,
+ InstallEnvironment environment,
+ CancellationToken cancellationToken)
{
if (manifest.ServerUrl == null)
{
- throw new InvalidDataException("Manifest Server Url is not configured.");
+ throw new InvalidDataException("Manifest ServerUrl is not configured.");
}
return new MLaunchOption
{
- Session = await _authService.GetSessionAsync(),
+ Session = await _authService.GetSessionAsync(cancellationToken).ConfigureAwait(false),
JavaPath = environment.JavaPath,
MinimumRamMb = _gameOptions.MinimumRamMB,
MaximumRamMb = _gameOptions.MaximumRamMB,
diff --git a/AlayaCore/Services/HttpDownloadService.cs b/AlayaCore/Services/HttpDownloadService.cs
index 2f68d16..7a1a65b 100644
--- a/AlayaCore/Services/HttpDownloadService.cs
+++ b/AlayaCore/Services/HttpDownloadService.cs
@@ -14,7 +14,7 @@ namespace AlayaCore.Services
{
public sealed class HttpDownloadService : IDownloadService
{
- private const int BUFFER_SIZE = 81920;
+ private const int BufferSize = 81920;
private readonly IHttpClient _httpClient;
@@ -45,16 +45,10 @@ namespace AlayaCore.Services
throw new ArgumentException("Destination path cannot be null, empty, or whitespace.", nameof(destinationPath));
}
- if (string.IsNullOrWhiteSpace(sha512Hash))
- {
- throw new ArgumentException("SHA-512 hash cannot be null, empty, or whitespace.", nameof(sha512Hash));
- }
-
+ string normalizedExpectedHash = NormalizeHash(sha512Hash);
cancellationToken.ThrowIfCancellationRequested();
- string normalizedExpectedHash = NormalizeHash(sha512Hash);
string fileName = Path.GetFileName(destinationPath);
-
if (string.IsNullOrWhiteSpace(fileName))
{
throw new ArgumentException("Destination path must include a file name.", nameof(destinationPath));
@@ -69,7 +63,7 @@ namespace AlayaCore.Services
progress?.Report(new DownloadProgress(
fileName: fileName,
destinationPath: destinationPath,
- bytesDownloaded: existingLength,
+ bytesDownloaded: 0,
totalBytes: existingLength,
bytesPerSecond: null,
statusMessage: "File already present and valid."));
@@ -91,7 +85,7 @@ namespace AlayaCore.Services
using HttpResponseMessage response = await _httpClient.GetAsync(
sourceUri,
HttpCompletionOption.ResponseHeadersRead,
- cancellationToken);
+ cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
@@ -106,17 +100,21 @@ namespace AlayaCore.Services
bytesPerSecond: null,
statusMessage: "Starting download..."));
- await using Stream responseStream = await response.Content.ReadAsStreamAsync();
+ await using Stream responseStream = await response.Content
+ .ReadAsStreamAsync()
+ .ConfigureAwait(false);
+
await using FileStream fileStream = new FileStream(
tempFilePath,
FileMode.Create,
FileAccess.Write,
FileShare.None,
- BUFFER_SIZE,
+ BufferSize,
useAsync: true);
+
using SHA512 sha512 = SHA512.Create();
- byte[] buffer = new byte[BUFFER_SIZE];
+ byte[] buffer = new byte[BufferSize];
Stopwatch stopwatch = Stopwatch.StartNew();
while (true)
@@ -125,7 +123,7 @@ namespace AlayaCore.Services
buffer,
0,
buffer.Length,
- cancellationToken);
+ cancellationToken).ConfigureAwait(false);
if (bytesRead == 0)
{
@@ -136,7 +134,7 @@ namespace AlayaCore.Services
buffer,
0,
bytesRead,
- cancellationToken);
+ cancellationToken).ConfigureAwait(false);
sha512.TransformBlock(buffer, 0, bytesRead, null, 0);
@@ -158,10 +156,9 @@ namespace AlayaCore.Services
}
sha512.TransformFinalBlock(Array.Empty(), 0, 0);
- await fileStream.FlushAsync(cancellationToken);
+ await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);
string actualHash = ConvertToLowerHex(sha512.Hash);
-
if (!string.Equals(actualHash, normalizedExpectedHash, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidDataException(
@@ -204,18 +201,13 @@ namespace AlayaCore.Services
throw new ArgumentException("File path cannot be null, empty, or whitespace.", nameof(filePath));
}
- if (string.IsNullOrWhiteSpace(sha512Hash))
- {
- throw new ArgumentException("SHA-512 hash cannot be null, empty, or whitespace.", nameof(sha512Hash));
- }
+ string normalizedExpectedHash = NormalizeHash(sha512Hash);
if (!File.Exists(filePath))
{
return false;
}
- string normalizedExpectedHash = NormalizeHash(sha512Hash);
-
using FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
using SHA512 sha512 = SHA512.Create();
@@ -228,6 +220,7 @@ namespace AlayaCore.Services
private static void EnsureDestinationDirectoryExists(string destinationPath)
{
string? directoryPath = Path.GetDirectoryName(destinationPath);
+
if (!string.IsNullOrWhiteSpace(directoryPath))
{
Directory.CreateDirectory(directoryPath);
@@ -250,7 +243,31 @@ namespace AlayaCore.Services
private static string NormalizeHash(string hash)
{
- return hash.Trim().Replace("-", string.Empty).ToLowerInvariant();
+ if (string.IsNullOrWhiteSpace(hash))
+ {
+ throw new ArgumentException("SHA-512 hash cannot be null, empty, or whitespace.", nameof(hash));
+ }
+
+ string normalized = hash.Trim().Replace("-", string.Empty).ToLowerInvariant();
+
+ if (normalized.Length != 128)
+ {
+ throw new ArgumentException("SHA-512 hash must be 128 hexadecimal characters long.", nameof(hash));
+ }
+
+ foreach (char c in normalized)
+ {
+ bool isHex =
+ (c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f');
+
+ if (!isHex)
+ {
+ throw new ArgumentException("SHA-512 hash contains invalid characters.", nameof(hash));
+ }
+ }
+
+ return normalized;
}
private static string ConvertToLowerHex(byte[]? hashBytes)
diff --git a/AlayaCore/Services/InstallStateService.cs b/AlayaCore/Services/InstallStateService.cs
index 253dc87..621c714 100644
--- a/AlayaCore/Services/InstallStateService.cs
+++ b/AlayaCore/Services/InstallStateService.cs
@@ -137,9 +137,9 @@ namespace AlayaCore.Services
{
string executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "javaw.exe"
- : "javaw";
+ : "java";
- string runtimePath = _fileStore.GetOrCreate(FolderLocation.JavaRuntime);
+ string runtimePath = _fileStore.Get(FolderLocation.JavaRuntime);
string fullPath = Path.Combine(runtimePath, "bin", executableName);
if (!File.Exists(fullPath))
@@ -161,7 +161,10 @@ namespace AlayaCore.Services
return InstalledVersionState.Empty();
}
- string[] versionDirectories = Directory.GetDirectories(versionsPath);
+ string[] versionDirectories = Directory
+ .GetDirectories(versionsPath)
+ .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
+ .ToArray();
if (versionDirectories.Length == 0)
{
@@ -187,7 +190,10 @@ namespace AlayaCore.Services
continue;
}
- JObject versionJson = LoadJson(versionJsonPath);
+ if (!TryLoadJson(versionJsonPath, out JObject? versionJson))
+ {
+ continue;
+ }
string? id = versionJson.Value("id");
string? inheritsFrom = versionJson.Value("inheritsFrom");
@@ -199,7 +205,7 @@ namespace AlayaCore.Services
if (IsNeoForgeVersion(id, inheritsFrom))
{
- neoForgeVersion = id;
+ neoForgeVersion ??= id;
if (string.IsNullOrWhiteSpace(minecraftVersion) && !string.IsNullOrWhiteSpace(inheritsFrom))
{
@@ -209,10 +215,7 @@ namespace AlayaCore.Services
continue;
}
- if (string.IsNullOrWhiteSpace(minecraftVersion))
- {
- minecraftVersion = id;
- }
+ minecraftVersion ??= id;
}
return new InstalledVersionState(minecraftVersion, neoForgeVersion);
@@ -220,7 +223,7 @@ namespace AlayaCore.Services
private string GetVersionsPath()
{
- return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Game), VersionsFolderName);
+ return Path.Combine(_fileStore.Get(FolderLocation.Game), VersionsFolderName);
}
private static bool IsNeoForgeVersion(string? id, string? inheritsFrom)
@@ -239,21 +242,31 @@ namespace AlayaCore.Services
value.Contains("neoforged", StringComparison.OrdinalIgnoreCase);
}
- private static JObject LoadJson(string path)
+ private static bool TryLoadJson(string path, out JObject? jsonObject)
{
- if (string.IsNullOrWhiteSpace(path))
+ jsonObject = null;
+
+ if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
- throw new ArgumentException("Path cannot be null, empty, or whitespace.", nameof(path));
+ return false;
}
string json = File.ReadAllText(path);
if (string.IsNullOrWhiteSpace(json))
{
- throw new InvalidDataException($"File '{path}' was empty.");
+ return false;
}
- return JObject.Parse(json);
+ try
+ {
+ jsonObject = JObject.Parse(json);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
}
private sealed class InstalledVersionState
diff --git a/AlayaCore/Services/LauncherUpdateService.cs b/AlayaCore/Services/LauncherUpdateService.cs
index 06a6a31..e736241 100644
--- a/AlayaCore/Services/LauncherUpdateService.cs
+++ b/AlayaCore/Services/LauncherUpdateService.cs
@@ -40,30 +40,65 @@ namespace AlayaCore.Services
return true;
}
+ if (localManifest.Version == null)
+ {
+ return true;
+ }
+
if (string.IsNullOrWhiteSpace(localManifest.Sha512Hash))
{
return true;
}
- string remoteHash = await _manifestService
- .GetRemoteLauncherManifestHashAsync(cancellationToken)
+ LauncherManifestModel remoteManifest = await _manifestService
+ .GetRemoteLauncherManifestAsync(cancellationToken)
.ConfigureAwait(false);
- if (string.IsNullOrWhiteSpace(remoteHash))
+ if (remoteManifest == null)
+ {
+ throw new InvalidOperationException("Remote launcher manifest could not be loaded.");
+ }
+
+ if (remoteManifest.Version == null)
+ {
+ throw new InvalidOperationException("Remote launcher manifest returned an invalid version.");
+ }
+
+ if (string.IsNullOrWhiteSpace(remoteManifest.Sha512Hash))
{
throw new InvalidOperationException("Remote launcher manifest returned an invalid SHA-512 hash.");
}
- return !string.Equals(
+ bool versionMismatch = localManifest.Version != remoteManifest.Version;
+ bool hashMismatch = !string.Equals(
localManifest.Sha512Hash.Trim(),
- remoteHash.Trim(),
+ remoteManifest.Sha512Hash.Trim(),
StringComparison.OrdinalIgnoreCase);
+
+ return versionMismatch || hashMismatch;
}
- public Task LaunchUpdaterAsync(LauncherManifestModel newManifest, CancellationToken cancellationToken = default)
+ public Task LaunchUpdaterAsync(
+ LauncherManifestModel newManifest,
+ CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
+ if (newManifest == null)
+ {
+ throw new ArgumentNullException(nameof(newManifest));
+ }
+
+ if (newManifest.DownloadUri == null)
+ {
+ throw new InvalidOperationException("Launcher manifest does not contain a download URI.");
+ }
+
+ if (!newManifest.DownloadUri.IsAbsoluteUri)
+ {
+ throw new InvalidOperationException("Launcher download URI must be absolute.");
+ }
+
string updaterPath = _options.AlayaUpdaterPath;
if (string.IsNullOrWhiteSpace(updaterPath))
@@ -84,11 +119,13 @@ namespace AlayaCore.Services
string workingDirectory = Path.GetDirectoryName(updaterPath)
?? throw new InvalidOperationException("Updater working directory could not be resolved.");
+ string arguments = $"-url \"{newManifest.DownloadUri.AbsoluteUri}\"";
+
var startInfo = new ProcessStartInfo
{
FileName = updaterPath,
WorkingDirectory = workingDirectory,
- Arguments = $@"-url {newManifest.DownloadUri}",
+ Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true
};
@@ -102,7 +139,7 @@ namespace AlayaCore.Services
throw new InvalidOperationException("Failed to start updater process.");
}
}
- catch (Exception ex) when (!(ex is OperationCanceledException))
+ catch (Exception ex) when (ex is not OperationCanceledException)
{
throw new InvalidOperationException("Failed to launch updater process.", ex);
}
diff --git a/AlayaCore/Services/ManifestService.cs b/AlayaCore/Services/ManifestService.cs
index 95287ab..3f249f3 100644
--- a/AlayaCore/Services/ManifestService.cs
+++ b/AlayaCore/Services/ManifestService.cs
@@ -17,9 +17,9 @@ namespace AlayaCore.Services
{
public sealed class ManifestService : IManifestService
{
- private const string CORE_MANIFEST_FILE_NAME = "CoreManifest.json";
- private const string LAUNCHER_MANIFEST_FILE_NAME = "LauncherManifest.json";
- private const string INSTALLED_MODS_MANIFEST_FILE_NAME = "InstalledModsManifest.json";
+ private const string CoreManifestFileName = "CoreManifest.json";
+ private const string LauncherManifestFileName = "LauncherManifest.json";
+ private const string InstalledModsManifestFileName = "InstalledModsManifest.json";
private readonly IDownloadService _downloadService;
private readonly IHttpClient _httpClient;
@@ -50,7 +50,8 @@ namespace AlayaCore.Services
cancellationToken);
}
- public async Task GetInstalledModsManifestAsync(CancellationToken cancellationToken = default)
+ public async Task GetInstalledModsManifestAsync(
+ CancellationToken cancellationToken = default)
{
string path = GetInstalledModsManifestPath();
@@ -66,11 +67,12 @@ namespace AlayaCore.Services
return InstalledModsManifestModel.Empty();
}
- InstalledModsManifestModel? manifest = DeserializeAndMapManifest(
- json,
- path,
- static dto => dto.ToModel(),
- swallowDeserializationErrors: true);
+ InstalledModsManifestModel? manifest =
+ DeserializeAndMapManifest(
+ json,
+ path,
+ static dto => dto.ToModel(),
+ swallowDeserializationErrors: true);
return manifest ?? InstalledModsManifestModel.Empty();
}
@@ -105,28 +107,10 @@ namespace AlayaCore.Services
public async Task GetRemoteCoreManifestVersionAsync(CancellationToken cancellationToken = default)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- using HttpResponseMessage response = await _httpClient.GetAsync(
+ ManifestModel remoteManifest = await GetRemoteManifestAsync(
_options.CoreManifestUri,
- HttpCompletionOption.ResponseContentRead,
- cancellationToken).ConfigureAwait(false);
-
- response.EnsureSuccessStatusCode();
-
- string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-
- if (string.IsNullOrWhiteSpace(json))
- {
- throw new InvalidDataException(
- $"Remote core manifest response from '{_options.CoreManifestUri}' was empty.");
- }
-
- ManifestModel remoteManifest = DeserializeAndMapManifest(
- json,
- _options.CoreManifestUri.ToString(),
static dto => dto.ToModel(),
- swallowDeserializationErrors: false)!;
+ cancellationToken).ConfigureAwait(false);
if (remoteManifest.AlayaVersion == null)
{
@@ -137,30 +121,13 @@ namespace AlayaCore.Services
return remoteManifest.AlayaVersion;
}
- public async Task GetRemoteLauncherManifestHashAsync(CancellationToken cancellationToken = default)
+ public async Task GetRemoteLauncherManifestAsync(
+ CancellationToken cancellationToken = default)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- using HttpResponseMessage response = await _httpClient.GetAsync(
+ LauncherManifestModel remoteManifest = await GetRemoteManifestAsync(
_options.LauncherManifestUri,
- HttpCompletionOption.ResponseContentRead,
- cancellationToken).ConfigureAwait(false);
-
- response.EnsureSuccessStatusCode();
-
- string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-
- if (string.IsNullOrWhiteSpace(json))
- {
- throw new InvalidDataException(
- $"Remote launcher manifest response from '{_options.LauncherManifestUri}' was empty.");
- }
-
- LauncherManifestModel remoteManifest = DeserializeAndMapManifest(
- json,
- _options.LauncherManifestUri.ToString(),
static dto => dto.ToModel(),
- swallowDeserializationErrors: false)!;
+ cancellationToken).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(remoteManifest.Sha512Hash))
{
@@ -168,22 +135,28 @@ namespace AlayaCore.Services
$"Remote launcher manifest from '{_options.LauncherManifestUri}' does not contain a valid SHA-512 hash.");
}
- return remoteManifest.Sha512Hash.Trim();
+ if (remoteManifest.DownloadUri == null || !remoteManifest.DownloadUri.IsAbsoluteUri)
+ {
+ throw new InvalidDataException(
+ $"Remote launcher manifest from '{_options.LauncherManifestUri}' does not contain a valid download URI.");
+ }
+
+ return remoteManifest;
}
public string GetLauncherManifestPath()
{
- return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Manifests), LAUNCHER_MANIFEST_FILE_NAME);
+ return Path.Combine(_fileStore.Get(FolderLocation.Manifests), LauncherManifestFileName);
}
public string GetCoreManifestPath()
{
- return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Manifests), CORE_MANIFEST_FILE_NAME);
+ return Path.Combine(_fileStore.Get(FolderLocation.Manifests), CoreManifestFileName);
}
public string GetInstalledModsManifestPath()
{
- return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Manifests), INSTALLED_MODS_MANIFEST_FILE_NAME);
+ return Path.Combine(_fileStore.Get(FolderLocation.Manifests), InstalledModsManifestFileName);
}
private async Task LoadLocalManifestAsync(
@@ -283,6 +256,51 @@ namespace AlayaCore.Services
swallowDeserializationErrors: false)!;
}
+ private async Task GetRemoteManifestAsync(
+ Uri manifestUri,
+ Func map,
+ CancellationToken cancellationToken)
+ where TDto : class
+ {
+ if (manifestUri == null)
+ {
+ throw new ArgumentNullException(nameof(manifestUri));
+ }
+
+ if (!manifestUri.IsAbsoluteUri)
+ {
+ throw new ArgumentException("Manifest URI must be absolute.", nameof(manifestUri));
+ }
+
+ if (map == null)
+ {
+ throw new ArgumentNullException(nameof(map));
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using HttpResponseMessage response = await _httpClient.GetAsync(
+ manifestUri,
+ HttpCompletionOption.ResponseContentRead,
+ cancellationToken).ConfigureAwait(false);
+
+ response.EnsureSuccessStatusCode();
+
+ string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ throw new InvalidDataException(
+ $"Remote manifest response from '{manifestUri}' was empty.");
+ }
+
+ return DeserializeAndMapManifest(
+ json,
+ manifestUri.ToString(),
+ map,
+ swallowDeserializationErrors: false)!;
+ }
+
private static TModel? DeserializeAndMapManifest(
string json,
string sourceName,
@@ -342,7 +360,7 @@ namespace AlayaCore.Services
{
return map(dto);
}
- catch (Exception ex) when (!(ex is OperationCanceledException))
+ catch (Exception ex) when (ex is not OperationCanceledException)
{
if (swallowDeserializationErrors)
{
diff --git a/AlayaCore/States/LaunchPlan.cs b/AlayaCore/States/LaunchPlan.cs
index 2062bef..cbbe5d7 100644
--- a/AlayaCore/States/LaunchPlan.cs
+++ b/AlayaCore/States/LaunchPlan.cs
@@ -5,7 +5,6 @@ namespace AlayaCore.States
Ready,
LauncherNeedsUpdate,
NeedAuthenticating,
- InstallJava,
InstallMinecraft,
InstallNeoforge,
SyncMods
@@ -14,7 +13,6 @@ namespace AlayaCore.States
public sealed class LaunchPlan
{
public bool LauncherNeedsUpdate { get; }
- public bool JavaNeedsInstallOrUpdate { get; }
public bool MinecraftNeedsInstallOrUpdate { get; }
public bool NeoforgeNeedsInstallOrUpdate { get; }
public bool ModsNeedSync { get; }
@@ -26,7 +24,6 @@ namespace AlayaCore.States
public bool NeedsUpdating =>
LauncherNeedsUpdate ||
- JavaNeedsInstallOrUpdate ||
MinecraftNeedsInstallOrUpdate ||
NeoforgeNeedsInstallOrUpdate ||
ModsNeedSync;
@@ -36,14 +33,12 @@ namespace AlayaCore.States
public LaunchPlan(
bool launcherNeedsUpdate,
- bool javaNeedsInstallOrUpdate,
bool minecraftNeedsInstallOrUpdate,
bool neoforgeNeedsInstallOrUpdate,
bool modsNeedSync,
bool needAuthenticating)
{
LauncherNeedsUpdate = launcherNeedsUpdate;
- JavaNeedsInstallOrUpdate = javaNeedsInstallOrUpdate;
MinecraftNeedsInstallOrUpdate = minecraftNeedsInstallOrUpdate;
NeoforgeNeedsInstallOrUpdate = neoforgeNeedsInstallOrUpdate;
ModsNeedSync = modsNeedSync;
@@ -58,9 +53,6 @@ namespace AlayaCore.States
if (NeedAuthenticating)
return LaunchState.NeedAuthenticating;
- if (JavaNeedsInstallOrUpdate)
- return LaunchState.InstallJava;
-
if (MinecraftNeedsInstallOrUpdate)
return LaunchState.InstallMinecraft;
@@ -77,7 +69,6 @@ namespace AlayaCore.States
{
return new LaunchPlan(
launcherNeedsUpdate: false,
- javaNeedsInstallOrUpdate: false,
minecraftNeedsInstallOrUpdate: false,
neoforgeNeedsInstallOrUpdate: false,
modsNeedSync: false,
diff --git a/AlayaCore/Utilities/Extensions/MappingExtensions.cs b/AlayaCore/Utilities/Extensions/MappingExtensions.cs
index c9dc425..6d86789 100644
--- a/AlayaCore/Utilities/Extensions/MappingExtensions.cs
+++ b/AlayaCore/Utilities/Extensions/MappingExtensions.cs
@@ -47,8 +47,6 @@ namespace AlayaCore.Utilities.Extensions
return new ManifestModel(
dto.AlayaVersion,
- dto.RequiredJavaVersion,
- dto.RequiredJavaUrl,
dto.MinecraftVersion,
dto.NeoforgedVersion,
dto.ServerUrl,
@@ -80,8 +78,6 @@ namespace AlayaCore.Utilities.Extensions
return new ManifestDto
{
AlayaVersion = model.AlayaVersion,
- RequiredJavaVersion = model.RequiredJavaVersion,
- RequiredJavaUrl = model.RequiredJavaBaseUrl,
MinecraftVersion = model.MinecraftVersion,
NeoforgedVersion = model.NeoforgedVersion,
Files = model.Files.Select(file => file.ToDto()).ToList()
diff --git a/AlayaCore/Utilities/Stores/LocalFileStore.cs b/AlayaCore/Utilities/Stores/LocalFileStore.cs
index ef2daab..daa245a 100644
--- a/AlayaCore/Utilities/Stores/LocalFileStore.cs
+++ b/AlayaCore/Utilities/Stores/LocalFileStore.cs
@@ -10,7 +10,7 @@ namespace AlayaCore.Utilities.Stores
{
private static readonly string BaseDirectoryPath = AppContext.BaseDirectory;
private static readonly string JavaDirectoryPath = Path.Combine(BaseDirectoryPath, "Java");
- private static readonly string JavaRuntimeDirectoryPath = Path.Combine(JavaDirectoryPath, "java-runtime-epsilon");
+ private static readonly string JavaRuntimeDirectoryPath = Path.Combine(JavaDirectoryPath, "runtime");
private static readonly string GameDirectoryPath = Path.Combine(BaseDirectoryPath, "Game");
private static readonly string ModsDirectoryPath = Path.Combine(GameDirectoryPath, "mods");
private static readonly string ResourcePacksDirectoryPath = Path.Combine(GameDirectoryPath, "resourcepacks");
@@ -36,6 +36,26 @@ namespace AlayaCore.Utilities.Stores
{ FolderLocation.Data, DataDirectoryPath}
};
+ public LocalFileStore()
+ {
+ CreateDirectories();
+ }
+
+ public void CreateDirectories()
+ {
+ Directory.CreateDirectory(BaseDirectoryPath);
+ Directory.CreateDirectory(JavaDirectoryPath);
+ Directory.CreateDirectory(JavaRuntimeDirectoryPath);
+ Directory.CreateDirectory(GameDirectoryPath);
+ Directory.CreateDirectory(ModsDirectoryPath);
+ Directory.CreateDirectory(ResourcePacksDirectoryPath);
+ Directory.CreateDirectory(ConfigDirectoryPath);
+ Directory.CreateDirectory(DownloadsDirectoryPath);
+ Directory.CreateDirectory(ManifestsDirectoryPath);
+ Directory.CreateDirectory(PluginsDirectoryPath);
+ Directory.CreateDirectory(DataDirectoryPath);
+ }
+
public string Get(FolderLocation location)
{
if (!Folders.TryGetValue(location, out string? path))