Switched to Modrinth Version_File with Hash rather than ID and Version.
Implemented SettingsService
This commit is contained in:
@@ -1,10 +1,21 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AlayaCore.Models;
|
||||
using AlayaCore.States;
|
||||
|
||||
namespace AlayaCore.Abstractions.Interfaces
|
||||
{
|
||||
public interface ILaunchDirector
|
||||
{
|
||||
Task RunAsync(CancellationToken cancellationToken = default);
|
||||
Task<LaunchPlan> EvaluateAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
Task InstallOrUpdateAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
Task LaunchAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
bool CanRun { get; }
|
||||
bool NeedsUpdating { get; }
|
||||
|
||||
LaunchPlan? CurrentPlan { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AlayaCore.Installation;
|
||||
using AlayaCore.Models.Manifests;
|
||||
|
||||
namespace AlayaCore.Abstractions.Interfaces.Services
|
||||
{
|
||||
public interface IGameLaunchServer
|
||||
public interface IGameLaunchService
|
||||
{
|
||||
|
||||
Task LaunchAsync(
|
||||
ManifestModel manifest,
|
||||
InstallEnvironment environment,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,17 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AlayaCore.Models.Configuration;
|
||||
|
||||
namespace AlayaCore.Abstractions.Interfaces.Services
|
||||
{
|
||||
public interface ISettingsService
|
||||
{
|
||||
LauncherOptions LauncherOptions { get; }
|
||||
|
||||
Task SetForceReinstallAsync(bool value, CancellationToken cancellationToken = default);
|
||||
|
||||
Task SaveLauncherOptionsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
Task LoadLauncherOptionsAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace AlayaCore.Installation
|
||||
{
|
||||
public sealed class InstallEnvironment
|
||||
{
|
||||
public OSPlatform OSPlatform { get; }
|
||||
public OSPlatform OsPlatform { get; }
|
||||
public bool JavaInstalled { get; }
|
||||
public string? JavaVersion { get; }
|
||||
public string? JavaPath { get; }
|
||||
@@ -42,7 +42,7 @@ namespace AlayaCore.Installation
|
||||
nameof(javaPath));
|
||||
}
|
||||
|
||||
OSPlatform = osPlatform;
|
||||
OsPlatform = osPlatform;
|
||||
JavaInstalled = javaInstalled;
|
||||
JavaPath = javaInstalled ? javaPath : null;
|
||||
JavaVersion = javaInstalled ? javaVersion : null;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AlayaCore.Abstractions.Interfaces;
|
||||
using AlayaCore.Abstractions.Interfaces.Services;
|
||||
using AlayaCore.Installation;
|
||||
using AlayaCore.Models.Configuration;
|
||||
using AlayaCore.Models.Manifests;
|
||||
using AlayaCore.States;
|
||||
|
||||
namespace AlayaCore
|
||||
{
|
||||
@@ -16,22 +19,34 @@ namespace AlayaCore
|
||||
private readonly IInstallStateService _installStateService;
|
||||
private readonly IJavaService _javaService;
|
||||
private readonly IModService _modService;
|
||||
private readonly IGameLaunchService _gameLaunchService;
|
||||
private readonly LauncherOptions _options;
|
||||
|
||||
public bool CanRun { get; private set; }
|
||||
|
||||
public bool NeedsUpdating { get; private set; }
|
||||
|
||||
public LaunchPlan? CurrentPlan { get; private set; }
|
||||
|
||||
public LaunchDirector(
|
||||
IManifestService manifestService,
|
||||
IUpdateService updateService,
|
||||
IInstallStateService installStateService,
|
||||
IJavaService javaService,
|
||||
IModService modService)
|
||||
IModService modService,
|
||||
IGameLaunchService gameLaunchService,
|
||||
LauncherOptions options)
|
||||
{
|
||||
_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));
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public async Task RunAsync(CancellationToken cancellationToken = default)
|
||||
public async Task<LaunchPlan> EvaluateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -41,17 +56,164 @@ namespace AlayaCore
|
||||
|
||||
if (launcherNeedsUpdate)
|
||||
{
|
||||
LauncherManifestModel launcherManifest = await _manifestService
|
||||
.GetLauncherManifestAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
LaunchPlan launcherUpdatePlan = new LaunchPlan(
|
||||
launcherNeedsUpdate: true,
|
||||
javaNeedsInstallOrUpdate: false,
|
||||
minecraftNeedsInstallOrUpdate: false,
|
||||
neoforgeNeedsInstallOrUpdate: false,
|
||||
modsNeedSync: false);
|
||||
|
||||
await _updateService
|
||||
.LaunchUpdaterAsync(launcherManifest, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
ApplyPlan(launcherUpdatePlan);
|
||||
return launcherUpdatePlan;
|
||||
}
|
||||
|
||||
ManifestModel manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
InstallEnvironment environment = await _installStateService
|
||||
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
bool javaNeedsInstallOrUpdate =
|
||||
_options.ForceReinstall ||
|
||||
!environment.JavaInstalled ||
|
||||
!string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
bool minecraftNeedsInstallOrUpdate =
|
||||
_options.ForceReinstall ||
|
||||
!environment.MinecraftInstalled ||
|
||||
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
bool neoforgeNeedsInstallOrUpdate =
|
||||
_options.ForceReinstall ||
|
||||
!environment.NeoforgedInstalled ||
|
||||
!string.Equals(environment.NeoforgedVersion, manifest.NeoforgedVersion, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
bool modsNeedSync =
|
||||
_options.ForceReinstall ||
|
||||
DoModsNeedSync(manifest, environment);
|
||||
|
||||
LaunchPlan plan = new LaunchPlan(
|
||||
launcherNeedsUpdate: false,
|
||||
javaNeedsInstallOrUpdate: javaNeedsInstallOrUpdate,
|
||||
minecraftNeedsInstallOrUpdate: minecraftNeedsInstallOrUpdate,
|
||||
neoforgeNeedsInstallOrUpdate: neoforgeNeedsInstallOrUpdate,
|
||||
modsNeedSync: modsNeedSync);
|
||||
|
||||
ApplyPlan(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
public async Task InstallOrUpdateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
LaunchPlan plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
while (!plan.CanRun)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ManifestModel? manifest = null;
|
||||
InstallEnvironment? environment = null;
|
||||
|
||||
switch (plan.State)
|
||||
{
|
||||
case LaunchState.LauncherNeedsUpdate:
|
||||
{
|
||||
LauncherManifestModel launcherManifest = await _manifestService
|
||||
.GetLauncherManifestAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await _updateService
|
||||
.LaunchUpdaterAsync(launcherManifest, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
ApplyPlan(plan);
|
||||
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.InstallMinecraft:
|
||||
{
|
||||
throw new NotImplementedException("Minecraft install/update flow has not been implemented yet.");
|
||||
}
|
||||
|
||||
case LaunchState.InstallNeoforge:
|
||||
{
|
||||
throw new NotImplementedException("NeoForge install/update flow has not been implemented yet.");
|
||||
}
|
||||
|
||||
case LaunchState.SyncMods:
|
||||
{
|
||||
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
environment = await _installStateService
|
||||
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await _modService
|
||||
.ProcessModsAsync(manifest, environment, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LaunchState.Ready:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException($"Unsupported launch state '{plan.State}'.");
|
||||
}
|
||||
}
|
||||
|
||||
plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LaunchAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (CurrentPlan == null)
|
||||
{
|
||||
await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!CanRun)
|
||||
{
|
||||
throw new InvalidOperationException("Launcher cannot run because installation or updates are still required.");
|
||||
}
|
||||
|
||||
InstallEnvironment environment = await _installStateService
|
||||
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
ManifestModel manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await _gameLaunchService
|
||||
.LaunchAsync(manifest, environment, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<ManifestModel> EnsureCurrentManifestAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
ManifestModel? localManifest = await _manifestService
|
||||
.GetLocalCoreManifestAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
@@ -72,48 +234,61 @@ namespace AlayaCore
|
||||
throw new FileNotFoundException("Local core manifest was not found after refresh.");
|
||||
}
|
||||
|
||||
await ProcessManifestAsync(localManifest, cancellationToken).ConfigureAwait(false);
|
||||
return localManifest;
|
||||
}
|
||||
|
||||
private async Task ProcessManifestAsync(
|
||||
ManifestModel manifest,
|
||||
CancellationToken cancellationToken)
|
||||
private static bool DoModsNeedSync(ManifestModel manifest, InstallEnvironment environment)
|
||||
{
|
||||
if (manifest == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(manifest));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
InstallEnvironment environment = await _installStateService
|
||||
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!environment.JavaInstalled || environment.JavaVersion != manifest.RequiredJavaVersion)
|
||||
if (environment == null)
|
||||
{
|
||||
await _javaService
|
||||
.EnsureValidJavaInstalledAsync(manifest, environment, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
throw new ArgumentNullException(nameof(environment));
|
||||
}
|
||||
|
||||
// Process Minecraft
|
||||
var requiredMods = manifest.Files
|
||||
.Where(file => file.Type == AlayaCore.Utilities.Enums.FileType.Mod)
|
||||
.ToList();
|
||||
|
||||
if (!environment.MinecraftInstalled || environment.MinecraftVersion != manifest.MinecraftVersion)
|
||||
var installedMods = environment.InstalledModsManifest.Mods;
|
||||
|
||||
if (requiredMods.Count != installedMods.Count)
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process Neoforge
|
||||
|
||||
if (!environment.NeoforgedInstalled || environment.NeoforgedVersion != manifest.NeoforgedVersion)
|
||||
foreach (ModFileEntry requiredMod in requiredMods)
|
||||
{
|
||||
InstalledModEntry? installedMod = installedMods.FirstOrDefault(
|
||||
mod => string.Equals(mod.FileName, requiredMod.FileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (installedMod == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!string.Equals(installedMod.Sha512Hash, requiredMod.Sha512Hash, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (installedMod.Size != requiredMod.Size)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process Mods
|
||||
return false;
|
||||
}
|
||||
|
||||
await _modService.ProcessModsAsync(manifest, environment, cancellationToken).ConfigureAwait(false);
|
||||
private void ApplyPlan(LaunchPlan plan)
|
||||
{
|
||||
CurrentPlan = plan ?? throw new ArgumentNullException(nameof(plan));
|
||||
NeedsUpdating = plan.NeedsUpdating;
|
||||
CanRun = plan.CanRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace AlayaCore.Models.Configuration
|
||||
{
|
||||
public class LauncherOptions
|
||||
public sealed class LauncherOptions
|
||||
{
|
||||
|
||||
public bool ForceReinstall { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,13 @@
|
||||
namespace AlayaCore.Models.Configuration
|
||||
{
|
||||
public class UpdateServiceOptions
|
||||
public sealed class LauncherUpdateServiceOptions
|
||||
{
|
||||
public LauncherUpdateServiceOptions(string alayaUpdaterPath, bool forceUpdate)
|
||||
{
|
||||
AlayaUpdaterPath = alayaUpdaterPath;
|
||||
ForceUpdate = forceUpdate;
|
||||
}
|
||||
|
||||
public string AlayaUpdaterPath { get; set; }
|
||||
public bool ForceUpdate { get; set; }
|
||||
}
|
||||
|
||||
@@ -13,13 +13,10 @@ namespace AlayaCore.Models.Manifests.DTO
|
||||
[JsonProperty("type", Required = Required.Always)]
|
||||
public FileType Type { get; set; }
|
||||
|
||||
[JsonProperty("modrinthId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string? ModrinthId { get; set; }
|
||||
|
||||
[JsonProperty("modrinthVersionId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string? ModrinthVersionId { get; set; }
|
||||
|
||||
[JsonProperty("sha512Hash", Required = Required.Always)]
|
||||
public string Sha512Hash { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("size", Required = Required.Always)]
|
||||
public long Size { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -6,36 +6,27 @@ namespace AlayaCore.Models.Manifests
|
||||
public sealed class InstalledModEntry
|
||||
{
|
||||
public string FileName { get; }
|
||||
public string Version { get; }
|
||||
|
||||
public string Sha512Hash { get; }
|
||||
public long Size { get; }
|
||||
|
||||
public long Size { get; set; }
|
||||
|
||||
public InstalledModEntry(string fileName, string version, string sha512Hash, long size)
|
||||
public InstalledModEntry(string fileName, string sha512Hash, long size)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
throw new ArgumentException("Name cannot be null, empty, or whitespace.", nameof(fileName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(version))
|
||||
{
|
||||
throw new ArgumentException("Version cannot be null, empty, or whitespace.", nameof(version));
|
||||
throw new ArgumentException("File name cannot be null, empty, or whitespace.", nameof(fileName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(sha512Hash))
|
||||
{
|
||||
throw new ArgumentException("SHA512Hash cannot be null, empty, or whitespace.", nameof(sha512Hash));
|
||||
throw new ArgumentException("SHA-512 hash cannot be null, empty, or whitespace.", nameof(sha512Hash));
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
if (size <= 0)
|
||||
{
|
||||
throw new ArgumentException("Size cannot be 0, empty, or whitespace.", nameof(size));
|
||||
throw new ArgumentOutOfRangeException(nameof(size), "Size must be greater than zero.");
|
||||
}
|
||||
|
||||
FileName = fileName;
|
||||
Version = version;
|
||||
Sha512Hash = sha512Hash;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
@@ -8,22 +8,20 @@ namespace AlayaCore.Models.Manifests
|
||||
{
|
||||
public string FileName { get; }
|
||||
public FileType Type { get; }
|
||||
public string? ModrinthId { get; }
|
||||
public string? ModrinthVersionId { get; }
|
||||
public string Sha512Hash { get; }
|
||||
|
||||
public long Size { get; }
|
||||
|
||||
public ModFileEntry(
|
||||
string name,
|
||||
FileType type,
|
||||
string sha512Hash,
|
||||
string? modrinthId = null,
|
||||
string? modrinthVersionId = null)
|
||||
long size)
|
||||
{
|
||||
FileName = RequireNonEmpty(name, nameof(name));
|
||||
Type = type;
|
||||
Sha512Hash = RequireNonEmpty(sha512Hash, nameof(sha512Hash));
|
||||
ModrinthId = NormalizeOptional(modrinthId);
|
||||
ModrinthVersionId = NormalizeOptional(modrinthVersionId);
|
||||
Size = size;
|
||||
}
|
||||
|
||||
private static string RequireNonEmpty(string value, string paramName)
|
||||
|
||||
@@ -161,6 +161,7 @@ namespace AlayaCore.Services
|
||||
await fileStream.FlushAsync(cancellationToken);
|
||||
|
||||
string actualHash = ConvertToLowerHex(sha512.Hash);
|
||||
|
||||
if (!string.Equals(actualHash, normalizedExpectedHash, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace AlayaCore.Services
|
||||
{
|
||||
public sealed class InstallationStateService : IInstallStateService
|
||||
{
|
||||
private const string JavaRuntimeFolderName = "java-runtime-epsilon";
|
||||
private const string JAVA_RUNTIME_FOLDER_NAME = "java-runtime-epsilon";
|
||||
|
||||
private readonly IManifestService _manifestService;
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace AlayaCore.Services
|
||||
string fullPath = Path.Combine(
|
||||
AppContext.BaseDirectory,
|
||||
"Java",
|
||||
JavaRuntimeFolderName,
|
||||
JAVA_RUNTIME_FOLDER_NAME,
|
||||
"bin",
|
||||
executableName);
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace AlayaCore.Services
|
||||
{
|
||||
public sealed class JavaService : IJavaService
|
||||
{
|
||||
private const string DownloadFileName = "java-runtime.download";
|
||||
private const string JavaInstallFolderName = "Java";
|
||||
private const string JavaArchiveHashPlaceholder = "REPLACE_WITH_MANIFEST_HASH_SUPPORT";
|
||||
private const string DOWNLOAD_FILE_NAME = "java-runtime.download";
|
||||
private const string JAVA_INSTALL_FOLDER_NAME = "Java";
|
||||
private const string JAVA_ARCHIVE_HASH_PLACEHOLDER = "REPLACE_WITH_MANIFEST_HASH_SUPPORT";
|
||||
|
||||
private readonly IDownloadService _downloadService;
|
||||
|
||||
@@ -69,12 +69,12 @@ namespace AlayaCore.Services
|
||||
|
||||
private static string GetJavaInstallDirectory()
|
||||
{
|
||||
return Path.Combine(AppContext.BaseDirectory, JavaInstallFolderName);
|
||||
return Path.Combine(AppContext.BaseDirectory, JAVA_INSTALL_FOLDER_NAME);
|
||||
}
|
||||
|
||||
private static string GetJavaDownloadPath()
|
||||
{
|
||||
return Path.Combine(AppContext.BaseDirectory, "Temp", DownloadFileName);
|
||||
return Path.Combine(AppContext.BaseDirectory, "Temp", DOWNLOAD_FILE_NAME);
|
||||
}
|
||||
|
||||
private static string ResolveJavaRootFolder(string javaExecutablePath)
|
||||
@@ -144,7 +144,7 @@ namespace AlayaCore.Services
|
||||
await _downloadService.DownloadFileAsync(
|
||||
javaUri,
|
||||
destinationPath,
|
||||
JavaArchiveHashPlaceholder,
|
||||
JAVA_ARCHIVE_HASH_PLACEHOLDER,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ namespace AlayaCore.Services
|
||||
public sealed class LauncherUpdateService : IUpdateService
|
||||
{
|
||||
private readonly IManifestService _manifestService;
|
||||
private readonly UpdateServiceOptions _options;
|
||||
private readonly LauncherUpdateServiceOptions _options;
|
||||
|
||||
public LauncherUpdateService(
|
||||
IManifestService manifestService,
|
||||
UpdateServiceOptions options)
|
||||
LauncherUpdateServiceOptions options)
|
||||
{
|
||||
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
@@ -15,9 +15,9 @@ namespace AlayaCore.Services
|
||||
{
|
||||
public sealed class ManifestService : IManifestService
|
||||
{
|
||||
private const string CoreManifestFileName = "CoreManifest.json";
|
||||
private const string LauncherManifestFileName = "LauncherManifest.json";
|
||||
private const string InstalledModsManifestFileName = "InstalledModsManifest.json";
|
||||
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 readonly IDownloadService _downloadService;
|
||||
private readonly IHttpClient _httpClient;
|
||||
@@ -168,17 +168,17 @@ namespace AlayaCore.Services
|
||||
|
||||
public string GetLauncherManifestPath()
|
||||
{
|
||||
return Path.Combine(_options.ManifestDirectoryPath, LauncherManifestFileName);
|
||||
return Path.Combine(_options.ManifestDirectoryPath, LAUNCHER_MANIFEST_FILE_NAME);
|
||||
}
|
||||
|
||||
public string GetCoreManifestPath()
|
||||
{
|
||||
return Path.Combine(_options.ManifestDirectoryPath, CoreManifestFileName);
|
||||
return Path.Combine(_options.ManifestDirectoryPath, CORE_MANIFEST_FILE_NAME);
|
||||
}
|
||||
|
||||
public string GetInstalledModsManifestPath()
|
||||
{
|
||||
return Path.Combine(_options.ManifestDirectoryPath, InstalledModsManifestFileName);
|
||||
return Path.Combine(_options.ManifestDirectoryPath, INSTALLED_MODS_MANIFEST_FILE_NAME);
|
||||
}
|
||||
|
||||
private async Task<TModel?> LoadLocalManifestAsync<TDto, TModel>(
|
||||
|
||||
@@ -19,19 +19,22 @@ namespace AlayaCore.Services
|
||||
{
|
||||
public sealed class ModService : IModService
|
||||
{
|
||||
private const string InstalledModsManifestFileName = "InstalledModsManifest.json";
|
||||
private const string INSTALLED_MODS_MANIFEST_FILE_NAME = "InstalledModsManifest.json";
|
||||
|
||||
private readonly IDownloadService _downloadService;
|
||||
private readonly ModrinthConnectionOptions _options;
|
||||
private readonly ManifestServiceOptions _manifestOptions;
|
||||
private readonly IHttpClient _httpClient;
|
||||
|
||||
public ModService(
|
||||
IDownloadService downloadService,
|
||||
ModrinthConnectionOptions options,
|
||||
ManifestServiceOptions manifestOptions,
|
||||
IHttpClient httpClient)
|
||||
{
|
||||
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_manifestOptions = manifestOptions ?? throw new ArgumentNullException(nameof(manifestOptions));
|
||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
}
|
||||
|
||||
@@ -58,9 +61,8 @@ namespace AlayaCore.Services
|
||||
.Where(file => file.Type == FileType.Mod)
|
||||
.ToList();
|
||||
|
||||
await RemoveStaleModsAsync(requiredMods, cancellationToken).ConfigureAwait(false);
|
||||
RemoveStaleMods(requiredMods);
|
||||
|
||||
List<ModFileEntry> missingOrOutdatedMods = new List<ModFileEntry>();
|
||||
List<InstalledModEntry> finalInstalledMods = new List<InstalledModEntry>();
|
||||
|
||||
foreach (ModFileEntry requiredMod in requiredMods)
|
||||
@@ -72,52 +74,30 @@ namespace AlayaCore.Services
|
||||
|
||||
string destinationPath = GetModDestinationPath(requiredMod);
|
||||
|
||||
if (installedMod == null)
|
||||
bool isValidInstalledMod =
|
||||
installedMod != null &&
|
||||
IsInstalledModUpToDate(installedMod, requiredMod) &&
|
||||
File.Exists(destinationPath) &&
|
||||
_downloadService.VerifyFileHash(destinationPath, requiredMod.Sha512Hash);
|
||||
|
||||
if (isValidInstalledMod)
|
||||
{
|
||||
missingOrOutdatedMods.Add(requiredMod);
|
||||
finalInstalledMods.Add(installedMod!);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsInstalledModUpToDate(installedMod, requiredMod))
|
||||
{
|
||||
missingOrOutdatedMods.Add(requiredMod);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!File.Exists(destinationPath))
|
||||
{
|
||||
missingOrOutdatedMods.Add(requiredMod);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hashMatches = _downloadService.VerifyFileHash(destinationPath, requiredMod.Sha512Hash);
|
||||
if (!hashMatches)
|
||||
{
|
||||
missingOrOutdatedMods.Add(requiredMod);
|
||||
continue;
|
||||
}
|
||||
|
||||
finalInstalledMods.Add(installedMod);
|
||||
}
|
||||
|
||||
foreach (ModFileEntry mod in missingOrOutdatedMods)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ModrinthModInfoModel model = await ResolveModUrlAsync(mod, cancellationToken).ConfigureAwait(false);
|
||||
string destinationPath = GetModDestinationPath(mod);
|
||||
ModrinthModInfoModel modInfo = await ResolveModUrlAsync(requiredMod, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await _downloadService.DownloadFileAsync(
|
||||
model.ModUrl,
|
||||
modInfo.ModUrl,
|
||||
destinationPath,
|
||||
mod.Sha512Hash,
|
||||
requiredMod.Sha512Hash,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
finalInstalledMods.Add(new InstalledModEntry(
|
||||
mod.FileName,
|
||||
mod.ModrinthVersionId ?? string.Empty,
|
||||
mod.Sha512Hash,
|
||||
model.Size));
|
||||
requiredMod.FileName,
|
||||
requiredMod.Sha512Hash,
|
||||
modInfo.Size));
|
||||
}
|
||||
|
||||
await FlushInstalledModsManifestAsync(finalInstalledMods, cancellationToken).ConfigureAwait(false);
|
||||
@@ -137,24 +117,16 @@ namespace AlayaCore.Services
|
||||
throw new ArgumentNullException(nameof(requiredMod));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(requiredMod.ModrinthVersionId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(requiredMod.Sha512Hash))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return string.Equals(
|
||||
installedMod.Version,
|
||||
requiredMod.ModrinthVersionId,
|
||||
StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(
|
||||
installedMod.Sha512Hash,
|
||||
requiredMod.Sha512Hash,
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
StringComparison.OrdinalIgnoreCase)
|
||||
&& installedMod.Size == requiredMod.Size;
|
||||
}
|
||||
|
||||
private async Task<ModrinthModInfoModel> ResolveModUrlAsync(
|
||||
@@ -166,17 +138,12 @@ namespace AlayaCore.Services
|
||||
throw new ArgumentNullException(nameof(fileEntry));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fileEntry.ModrinthId))
|
||||
if (string.IsNullOrWhiteSpace(fileEntry.Sha512Hash))
|
||||
{
|
||||
throw new InvalidOperationException($"Mod '{fileEntry.FileName}' does not contain a Modrinth ID.");
|
||||
throw new InvalidOperationException($"Mod '{fileEntry.FileName}' does not contain a SHA-512 hash.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fileEntry.ModrinthVersionId))
|
||||
{
|
||||
throw new InvalidOperationException($"Mod '{fileEntry.FileName}' does not contain a Modrinth version ID.");
|
||||
}
|
||||
|
||||
string versionEndpoint = BuildVersionEndpoint(fileEntry.ModrinthId, fileEntry.ModrinthVersionId);
|
||||
string versionEndpoint = BuildVersionEndpoint(fileEntry.Sha512Hash);
|
||||
|
||||
using HttpResponseMessage response = await _httpClient.GetAsync(
|
||||
new Uri(versionEndpoint, UriKind.Absolute),
|
||||
@@ -189,7 +156,7 @@ namespace AlayaCore.Services
|
||||
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
throw new InvalidDataException($"The Modrinth version response for '{fileEntry.FileName}' was empty.");
|
||||
throw new InvalidDataException($"The mod metadata response for '{fileEntry.FileName}' was empty.");
|
||||
}
|
||||
|
||||
JObject jsonObject = JObject.Parse(json);
|
||||
@@ -197,7 +164,7 @@ namespace AlayaCore.Services
|
||||
JArray? filesArray = jsonObject["files"] as JArray;
|
||||
if (filesArray == null || filesArray.Count == 0)
|
||||
{
|
||||
throw new InvalidDataException($"The Modrinth version response for '{fileEntry.FileName}' did not contain any files.");
|
||||
throw new InvalidDataException($"The mod metadata response for '{fileEntry.FileName}' did not contain any files.");
|
||||
}
|
||||
|
||||
JObject? selectedFile = filesArray
|
||||
@@ -207,35 +174,57 @@ namespace AlayaCore.Services
|
||||
|
||||
if (selectedFile == null)
|
||||
{
|
||||
throw new InvalidDataException($"The Modrinth version response for '{fileEntry.FileName}' did not contain a usable file entry.");
|
||||
throw new InvalidDataException($"The mod metadata response for '{fileEntry.FileName}' did not contain a usable file entry.");
|
||||
}
|
||||
|
||||
JObject? hashesObject = selectedFile["hashes"] as JObject;
|
||||
|
||||
if (hashesObject == null)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"The mod metadata response for '{fileEntry.FileName}' did not contain a hashes object.");
|
||||
}
|
||||
|
||||
string? remoteSha512Hash = hashesObject.Value<string>("sha512");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(remoteSha512Hash))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"The mod metadata response for '{fileEntry.FileName}' did not contain a valid SHA-512 hash.");
|
||||
}
|
||||
|
||||
if (!string.Equals(remoteSha512Hash, fileEntry.Sha512Hash, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"The mod metadata SHA-512 hash for '{fileEntry.FileName}' did not match the required manifest hash.");
|
||||
}
|
||||
|
||||
string? modUrl = selectedFile.Value<string>("url");
|
||||
if (string.IsNullOrWhiteSpace(modUrl))
|
||||
{
|
||||
throw new InvalidDataException($"The Modrinth version response for '{fileEntry.FileName}' did not contain a valid file URL.");
|
||||
throw new InvalidDataException($"The mod metadata response for '{fileEntry.FileName}' did not contain a valid file URL.");
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(modUrl, UriKind.Absolute, out Uri? result))
|
||||
{
|
||||
throw new InvalidDataException($"The Modrinth version response for '{fileEntry.FileName}' contained an invalid file URL.");
|
||||
throw new InvalidDataException($"The mod metadata response for '{fileEntry.FileName}' contained an invalid file URL.");
|
||||
}
|
||||
|
||||
long? size = selectedFile.Value<long?>("size");
|
||||
if (!size.HasValue || size.Value <= 0)
|
||||
{
|
||||
throw new InvalidDataException($"The Modrinth version response for '{fileEntry.FileName}' did not contain a valid file size.");
|
||||
throw new InvalidDataException($"The mod metadata response for '{fileEntry.FileName}' did not contain a valid file size.");
|
||||
}
|
||||
|
||||
return new ModrinthModInfoModel(result, size.Value);
|
||||
}
|
||||
|
||||
private string BuildVersionEndpoint(string modrinthId, string modrinthVersionId)
|
||||
private string BuildVersionEndpoint(string sha512Hash)
|
||||
{
|
||||
string baseUrl = _options.BaseApiUrl?.TrimEnd('/')
|
||||
?? throw new InvalidOperationException("Modrinth base API URL is not configured.");
|
||||
|
||||
return $"{baseUrl}/project/{modrinthId}/version/{modrinthVersionId}";
|
||||
return $"{baseUrl}/version_file/{sha512Hash}";
|
||||
}
|
||||
|
||||
private static string GetModDestinationPath(ModFileEntry fileEntry)
|
||||
@@ -261,17 +250,13 @@ namespace AlayaCore.Services
|
||||
return Path.Combine(AppContext.BaseDirectory, "Game", "mods");
|
||||
}
|
||||
|
||||
private static async Task RemoveStaleModsAsync(
|
||||
IEnumerable<ModFileEntry> requiredMods,
|
||||
CancellationToken cancellationToken = default)
|
||||
private static void RemoveStaleMods(IEnumerable<ModFileEntry> requiredMods)
|
||||
{
|
||||
if (requiredMods == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(requiredMods));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string modsDirectory = GetModsDirectoryPath();
|
||||
if (!Directory.Exists(modsDirectory))
|
||||
{
|
||||
@@ -285,8 +270,6 @@ namespace AlayaCore.Services
|
||||
|
||||
foreach (string filePath in Directory.GetFiles(modsDirectory))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string fileName = Path.GetFileName(filePath);
|
||||
|
||||
if (!requiredFileNames.Contains(fileName))
|
||||
@@ -294,11 +277,9 @@ namespace AlayaCore.Services
|
||||
File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async Task FlushInstalledModsManifestAsync(
|
||||
private async Task FlushInstalledModsManifestAsync(
|
||||
IEnumerable<InstalledModEntry> installedMods,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -308,13 +289,12 @@ namespace AlayaCore.Services
|
||||
}
|
||||
|
||||
List<InstalledModEntry> entries = installedMods.ToList();
|
||||
|
||||
InstalledModsManifestModel manifest = new InstalledModsManifestModel(entries);
|
||||
|
||||
string manifestsDirectory = Path.Combine(AppContext.BaseDirectory, "Manifests");
|
||||
string manifestsDirectory = _manifestOptions.ManifestDirectoryPath;
|
||||
Directory.CreateDirectory(manifestsDirectory);
|
||||
|
||||
string manifestPath = Path.Combine(manifestsDirectory, InstalledModsManifestFileName);
|
||||
string manifestPath = Path.Combine(manifestsDirectory, INSTALLED_MODS_MANIFEST_FILE_NAME);
|
||||
string temporaryManifestPath = manifestPath + ".tmp";
|
||||
|
||||
string json = JsonConvert.SerializeObject(
|
||||
@@ -323,7 +303,6 @@ namespace AlayaCore.Services
|
||||
mods = manifest.Mods.Select(mod => new
|
||||
{
|
||||
fileName = mod.FileName,
|
||||
version = mod.Version,
|
||||
sha512Hash = mod.Sha512Hash,
|
||||
size = mod.Size
|
||||
})
|
||||
@@ -332,8 +311,12 @@ namespace AlayaCore.Services
|
||||
|
||||
await File.WriteAllTextAsync(temporaryManifestPath, json, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
File.Copy(temporaryManifestPath, manifestPath, overwrite: true);
|
||||
File.Delete(temporaryManifestPath);
|
||||
if (File.Exists(manifestPath))
|
||||
{
|
||||
File.Delete(manifestPath);
|
||||
}
|
||||
|
||||
File.Move(temporaryManifestPath, manifestPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,132 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AlayaCore.Abstractions.Interfaces.Services;
|
||||
using AlayaCore.Models.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AlayaCore.Services
|
||||
{
|
||||
public class SettingsService
|
||||
public sealed class SettingsService : ISettingsService
|
||||
{
|
||||
private const string LauncherSettingsFileName = "Launcher.json";
|
||||
|
||||
public LauncherOptions LauncherOptions { get; }
|
||||
|
||||
public SettingsService(LauncherOptions launcherOptions)
|
||||
{
|
||||
LauncherOptions = launcherOptions ?? throw new ArgumentNullException(nameof(launcherOptions));
|
||||
}
|
||||
|
||||
public async Task SetForceReinstallAsync(bool value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
LauncherOptions.ForceReinstall = value;
|
||||
await SaveLauncherOptionsAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task SaveLauncherOptionsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await SaveAsync(
|
||||
LauncherSettingsFileName,
|
||||
LauncherOptions,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task LoadLauncherOptionsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
LauncherOptions? loadedOptions = await LoadAsync<LauncherOptions>(
|
||||
LauncherSettingsFileName,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (loadedOptions == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LauncherOptions.ForceReinstall = loadedOptions.ForceReinstall;
|
||||
}
|
||||
|
||||
private async Task SaveAsync<T>(
|
||||
string fileName,
|
||||
T value,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
throw new ArgumentException("File name cannot be null, empty, or whitespace.", nameof(fileName));
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string fullPath = GetFullPath(fileName);
|
||||
string? directoryPath = Path.GetDirectoryName(fullPath);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(directoryPath))
|
||||
{
|
||||
throw new InvalidOperationException("Could not resolve the settings directory path.");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
|
||||
string temporaryPath = fullPath + ".tmp";
|
||||
string json = JsonConvert.SerializeObject(value, Formatting.Indented);
|
||||
|
||||
await File.WriteAllTextAsync(temporaryPath, json, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
|
||||
File.Move(temporaryPath, fullPath);
|
||||
}
|
||||
|
||||
private async Task<T?> LoadAsync<T>(
|
||||
string fileName,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
throw new ArgumentException("File name cannot be null, empty, or whitespace.", nameof(fileName));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
string fullPath = GetFullPath(fileName);
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
string json = await File.ReadAllTextAsync(fullPath, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Failed to deserialize settings file '{fullPath}' to {typeof(T).Name}.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetFullPath(string fileName)
|
||||
{
|
||||
return Path.Combine(AppContext.BaseDirectory, "Config", fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,74 @@
|
||||
namespace AlayaCore.States
|
||||
{
|
||||
public class LaunchPlan
|
||||
public enum LaunchState
|
||||
{
|
||||
Ready,
|
||||
LauncherNeedsUpdate,
|
||||
InstallJava,
|
||||
InstallMinecraft,
|
||||
InstallNeoforge,
|
||||
SyncMods
|
||||
}
|
||||
|
||||
public sealed class LaunchPlan
|
||||
{
|
||||
public bool LauncherNeedsUpdate { get; }
|
||||
public bool JavaNeedsInstallOrUpdate { get; }
|
||||
public bool MinecraftNeedsInstallOrUpdate { get; }
|
||||
public bool NeoforgeNeedsInstallOrUpdate { get; }
|
||||
public bool ModsNeedSync { get; }
|
||||
|
||||
public LaunchState State => ComputeState();
|
||||
|
||||
public bool CanRun =>
|
||||
State == LaunchState.Ready;
|
||||
|
||||
public bool NeedsUpdating =>
|
||||
State != LaunchState.Ready;
|
||||
|
||||
public LaunchPlan(
|
||||
bool launcherNeedsUpdate,
|
||||
bool javaNeedsInstallOrUpdate,
|
||||
bool minecraftNeedsInstallOrUpdate,
|
||||
bool neoforgeNeedsInstallOrUpdate,
|
||||
bool modsNeedSync)
|
||||
{
|
||||
LauncherNeedsUpdate = launcherNeedsUpdate;
|
||||
JavaNeedsInstallOrUpdate = javaNeedsInstallOrUpdate;
|
||||
MinecraftNeedsInstallOrUpdate = minecraftNeedsInstallOrUpdate;
|
||||
NeoforgeNeedsInstallOrUpdate = neoforgeNeedsInstallOrUpdate;
|
||||
ModsNeedSync = modsNeedSync;
|
||||
}
|
||||
|
||||
private LaunchState ComputeState()
|
||||
{
|
||||
// Priority order matters a LOT here
|
||||
if (LauncherNeedsUpdate)
|
||||
return LaunchState.LauncherNeedsUpdate;
|
||||
|
||||
if (JavaNeedsInstallOrUpdate)
|
||||
return LaunchState.InstallJava;
|
||||
|
||||
if (MinecraftNeedsInstallOrUpdate)
|
||||
return LaunchState.InstallMinecraft;
|
||||
|
||||
if (NeoforgeNeedsInstallOrUpdate)
|
||||
return LaunchState.InstallNeoforge;
|
||||
|
||||
if (ModsNeedSync)
|
||||
return LaunchState.SyncMods;
|
||||
|
||||
return LaunchState.Ready;
|
||||
}
|
||||
|
||||
public static LaunchPlan EmptyReady()
|
||||
{
|
||||
return new LaunchPlan(
|
||||
launcherNeedsUpdate: false,
|
||||
javaNeedsInstallOrUpdate: false,
|
||||
minecraftNeedsInstallOrUpdate: false,
|
||||
neoforgeNeedsInstallOrUpdate: false,
|
||||
modsNeedSync: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace AlayaCore.Utilities.Extensions
|
||||
throw new ArgumentNullException(nameof(dto));
|
||||
}
|
||||
|
||||
return new InstalledModEntry(dto.FileName, dto.Version, dto.Sha512Hash, dto.Size);
|
||||
return new InstalledModEntry(dto.FileName, dto.Sha512Hash, dto.Size);
|
||||
}
|
||||
|
||||
public static LauncherManifestModel ToModel(this LauncherManifestDto dto)
|
||||
@@ -67,8 +67,7 @@ namespace AlayaCore.Utilities.Extensions
|
||||
dto.Name,
|
||||
dto.Type,
|
||||
dto.Sha512Hash,
|
||||
dto.ModrinthId,
|
||||
dto.ModrinthVersionId);
|
||||
dto.Size);
|
||||
}
|
||||
|
||||
public static ManifestDto ToDto(this ManifestModel model)
|
||||
@@ -102,9 +101,7 @@ namespace AlayaCore.Utilities.Extensions
|
||||
{
|
||||
Name = model.FileName,
|
||||
Type = model.Type,
|
||||
Sha512Hash = model.Sha512Hash,
|
||||
ModrinthId = model.ModrinthId,
|
||||
ModrinthVersionId = model.ModrinthVersionId
|
||||
Sha512Hash = model.Sha512Hash
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user