Compare commits

..

2 Commits

21 changed files with 348 additions and 538 deletions

6
.idea/.idea.AlayaCore/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,3 +1,22 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultFileExtractors_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003Fe8_003F52aaf39a_003FDefaultFileExtractors_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003F5c_003Ff0b24cad_003FExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIGameInstaller_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003Fdf_003F3b38ca47_003FIGameInstaller_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIVersionMetadata_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003Ff2_003Fc2330846_003FIVersionMetadata_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIXboxGameAccountManager_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F101e700861b6410da498d4e79271a86112600_003Fc4_003F2054a44d_003FIXboxGameAccountManager_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJELoginHandlerBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2dce88ad0d604d86b0d410923cb59f4bb200_003Fb4_003F534fcf72_003FJELoginHandlerBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJELoginHandler_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F2dce88ad0d604d86b0d410923cb59f4bb200_003Fcd_003Ffe209c30_003FJELoginHandler_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMinecraftJavaPathResolver_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003F5f_003F3f4c19e9_003FMinecraftJavaPathResolver_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMinecraftLauncherParameters_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003F67_003Fd3bb8531_003FMinecraftLauncherParameters_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMinecraftLauncher_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003F93_003F1a552b3b_003FMinecraftLauncher_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMinecraftPath_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F75bf69b9634b4f328866dbcd8b90304a2600_003F3c_003Fef2111c9_003FMinecraftPath_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMinecraftProcessBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003Fc0_003F15c03583_003FMinecraftProcessBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMsalClientHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fee6114ec878e41eeb911687986e3a3fd6600_003F4b_003F0b7d25f6_003FMsalClientHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMSession_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F75bf69b9634b4f328866dbcd8b90304a2600_003Fa0_003F9cf699df_003FMSession_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANeoForgeInstaller_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F16599bff24b948d2b1e1d928222947e1de00_003F39_003F21a3bcfd_003FNeoForgeInstaller_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANeoForgeInstallOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F16599bff24b948d2b1e1d928222947e1de00_003Fa3_003F837206c4_003FNeoForgeInstallOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOperatingSystem_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb5d933e666c84a3394cfc49363e3e5bdd2b08_003Fb9_003Fd737a5e2_003FOperatingSystem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOperatingSystem_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb5d933e666c84a3394cfc49363e3e5bdd2b08_003Fb9_003Fd737a5e2_003FOperatingSystem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVersionConverter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FSourcesCache_003F346166506159999f4fec5f7c475ba964d2495ee825dd6e4c48dedef117f086_003FVersionConverter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AProcessWrapper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003F97_003F3a1ed5f7_003FProcessWrapper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APublicClientApplicationBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fe7dcae430a36a6030304d2dec7149bc9e40cb379e3a8e0efb762d5d19da5c_003FPublicClientApplicationBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVersionConverter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FSourcesCache_003F346166506159999f4fec5f7c475ba964d2495ee825dd6e4c48dedef117f086_003FVersionConverter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVersionMetadataCollection_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F11957ce69cb24b8a8e3979c0980ffeb33a400_003Ff7_003Fb6ecd842_003FVersionMetadataCollection_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@@ -11,5 +11,7 @@ namespace AlayaCore.Abstractions.Interfaces.Clients
Uri uri, Uri uri,
HttpCompletionOption completionOption, HttpCompletionOption completionOption,
CancellationToken cancellationToken); CancellationToken cancellationToken);
HttpClient HttpClient { get; }
} }
} }

View File

@@ -10,6 +10,7 @@ namespace AlayaCore.Abstractions.Interfaces.Services
{ {
public interface IGameInstallService public interface IGameInstallService
{ {
Task EnsureMinecraftInstalledAsync( Task EnsureMinecraftInstalledAsync(
ManifestModel manifest, ManifestModel manifest,
InstallEnvironment environment, InstallEnvironment environment,

View File

@@ -1,15 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using AlayaCore.Installation;
using AlayaCore.Models.Manifests;
namespace AlayaCore.Abstractions.Interfaces.Services
{
public interface IJavaService
{
Task EnsureValidJavaInstalledAsync(
ManifestModel manifest,
InstallEnvironment environment,
CancellationToken cancellationToken = default);
}
}

View File

@@ -16,7 +16,7 @@ namespace AlayaCore.Abstractions.Interfaces.Services
Task<LauncherManifestModel?> GetLocalLauncherManifestAsync(CancellationToken cancellationToken = default); Task<LauncherManifestModel?> GetLocalLauncherManifestAsync(CancellationToken cancellationToken = default);
Task<string> GetRemoteLauncherManifestHashAsync(CancellationToken cancellationToken = default); Task<LauncherManifestModel> GetRemoteLauncherManifestAsync(CancellationToken cancellationToken = default);
Task<Version> GetRemoteCoreManifestVersionAsync(CancellationToken cancellationToken = default); Task<Version> GetRemoteCoreManifestVersionAsync(CancellationToken cancellationToken = default);
} }
} }

View File

@@ -20,7 +20,6 @@ namespace AlayaCore
private readonly IManifestService _manifestService; private readonly IManifestService _manifestService;
private readonly IUpdateService _updateService; private readonly IUpdateService _updateService;
private readonly IInstallStateService _installStateService; private readonly IInstallStateService _installStateService;
private readonly IJavaService _javaService;
private readonly IModService _modService; private readonly IModService _modService;
private readonly IGameLaunchService _gameLaunchService; private readonly IGameLaunchService _gameLaunchService;
private readonly IGameInstallService _gameInstallService; private readonly IGameInstallService _gameInstallService;
@@ -36,7 +35,6 @@ namespace AlayaCore
IManifestService manifestService, IManifestService manifestService,
IUpdateService updateService, IUpdateService updateService,
IInstallStateService installStateService, IInstallStateService installStateService,
IJavaService javaService,
IModService modService, IModService modService,
IGameLaunchService gameLaunchService, IGameLaunchService gameLaunchService,
IGameInstallService gameInstallService, IGameInstallService gameInstallService,
@@ -47,7 +45,6 @@ namespace AlayaCore
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService)); _manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
_updateService = updateService ?? throw new ArgumentNullException(nameof(updateService)); _updateService = updateService ?? throw new ArgumentNullException(nameof(updateService));
_installStateService = installStateService ?? throw new ArgumentNullException(nameof(installStateService)); _installStateService = installStateService ?? throw new ArgumentNullException(nameof(installStateService));
_javaService = javaService ?? throw new ArgumentNullException(nameof(javaService));
_modService = modService ?? throw new ArgumentNullException(nameof(modService)); _modService = modService ?? throw new ArgumentNullException(nameof(modService));
_gameLaunchService = gameLaunchService ?? throw new ArgumentNullException(nameof(gameLaunchService)); _gameLaunchService = gameLaunchService ?? throw new ArgumentNullException(nameof(gameLaunchService));
_gameInstallService = gameInstallService ?? throw new ArgumentNullException(nameof(gameInstallService)); _gameInstallService = gameInstallService ?? throw new ArgumentNullException(nameof(gameInstallService));
@@ -68,7 +65,6 @@ namespace AlayaCore
{ {
LaunchPlan launcherUpdatePlan = new LaunchPlan( LaunchPlan launcherUpdatePlan = new LaunchPlan(
launcherNeedsUpdate: true, launcherNeedsUpdate: true,
javaNeedsInstallOrUpdate: false,
minecraftNeedsInstallOrUpdate: false, minecraftNeedsInstallOrUpdate: false,
neoforgeNeedsInstallOrUpdate: false, neoforgeNeedsInstallOrUpdate: false,
modsNeedSync: false, modsNeedSync: false,
@@ -84,17 +80,13 @@ namespace AlayaCore
.GetCurrentEnvironmentAsync(cancellationToken) .GetCurrentEnvironmentAsync(cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
bool javaNeedsInstallOrUpdate =
_options.ForceReinstall ||
!environment.JavaInstalled ||
!string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase);
bool needAuthenticating = !await _authService bool needAuthenticating = !await _authService
.IsAuthenticatedAsync(cancellationToken) .IsAuthenticatedAsync(cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
bool minecraftNeedsInstallOrUpdate = bool minecraftNeedsInstallOrUpdate =
_options.ForceReinstall || _options.ForceReinstall ||
!environment.JavaInstalled ||
!environment.MinecraftInstalled || !environment.MinecraftInstalled ||
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase); !string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase);
@@ -109,7 +101,6 @@ namespace AlayaCore
LaunchPlan plan = new LaunchPlan( LaunchPlan plan = new LaunchPlan(
launcherNeedsUpdate: false, launcherNeedsUpdate: false,
javaNeedsInstallOrUpdate: javaNeedsInstallOrUpdate,
minecraftNeedsInstallOrUpdate: minecraftNeedsInstallOrUpdate, minecraftNeedsInstallOrUpdate: minecraftNeedsInstallOrUpdate,
neoforgeNeedsInstallOrUpdate: neoforgeNeedsInstallOrUpdate, neoforgeNeedsInstallOrUpdate: neoforgeNeedsInstallOrUpdate,
modsNeedSync: modsNeedSync, modsNeedSync: modsNeedSync,
@@ -119,8 +110,9 @@ namespace AlayaCore
return plan; return plan;
} }
public async Task InstallOrUpdateAsync(CancellationToken cancellationToken = default, public async Task InstallOrUpdateAsync(
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null, CancellationToken cancellationToken = default,
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgress = null,
EventHandler<ByteProgress>? byteProgress = null, EventHandler<ByteProgress>? byteProgress = null,
IProgress<InstallerProgressChangedEventArgs>? neoForgeProgress = null, IProgress<InstallerProgressChangedEventArgs>? neoForgeProgress = null,
IProgress<ByteProgress>? neoForgeByteProgress = null) IProgress<ByteProgress>? neoForgeByteProgress = null)
@@ -133,8 +125,8 @@ namespace AlayaCore
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
ManifestModel? manifest; ManifestModel manifest;
InstallEnvironment? environment; InstallEnvironment environment;
switch (plan.State) switch (plan.State)
{ {
@@ -152,23 +144,12 @@ namespace AlayaCore
return; 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: case LaunchState.NeedAuthenticating:
{ {
await _authService.AuthenticateAsync(cancellationToken); await _authService
.AuthenticateAsync(cancellationToken)
.ConfigureAwait(false);
break; break;
} }
@@ -180,7 +161,12 @@ namespace AlayaCore
.ConfigureAwait(false); .ConfigureAwait(false);
await _gameInstallService await _gameInstallService
.EnsureMinecraftInstalledAsync(manifest, environment, cancellationToken, minecraftProgess, byteProgress) .EnsureMinecraftInstalledAsync(
manifest,
environment,
cancellationToken,
minecraftProgress,
byteProgress)
.ConfigureAwait(false); .ConfigureAwait(false);
break; break;
@@ -194,8 +180,13 @@ namespace AlayaCore
.ConfigureAwait(false); .ConfigureAwait(false);
await _gameInstallService await _gameInstallService
.EnsureNeoForgeInstalledAsync(manifest, environment, cancellationToken, neoForgeProgress, neoForgeByteProgress) .EnsureNeoForgeInstalledAsync(
.ConfigureAwait(false); manifest,
environment,
cancellationToken,
neoForgeProgress,
neoForgeByteProgress)
.ConfigureAwait(false);
break; break;
} }
@@ -230,7 +221,9 @@ namespace AlayaCore
if (_options.ForceReinstall) if (_options.ForceReinstall)
{ {
await _settingsService.SetForceReinstallAsync(false, cancellationToken).ConfigureAwait(false); await _settingsService
.SetForceReinstallAsync(false, cancellationToken)
.ConfigureAwait(false);
} }
plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false); plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);

View File

@@ -19,6 +19,9 @@ namespace AlayaCore.Models.Manifests.DTO
[JsonProperty("javaUrl", Required = Required.Always)] [JsonProperty("javaUrl", Required = Required.Always)]
[JsonConverter(typeof(UriConverter))] [JsonConverter(typeof(UriConverter))]
public Uri RequiredJavaUrl { get; set; } = null!; public Uri RequiredJavaUrl { get; set; } = null!;
[JsonProperty("javaArchiveHash", Required = Required.Always)]
public string JavaArchiveHash { get; set; } = string.Empty;
[JsonProperty("minecraftVersion", Required = Required.Always)] [JsonProperty("minecraftVersion", Required = Required.Always)]
public string MinecraftVersion { get; set; } = string.Empty; public string MinecraftVersion { get; set; } = string.Empty;

View File

@@ -4,7 +4,7 @@ namespace AlayaCore.Models.Manifests
{ {
public sealed class LauncherManifestModel public sealed class LauncherManifestModel
{ {
public Version Version { get; } public Version? Version { get; }
public string Sha512Hash { get; } public string Sha512Hash { get; }
public Uri DownloadUri { get; } public Uri DownloadUri { get; }

View File

@@ -9,8 +9,6 @@ namespace AlayaCore.Models.Manifests
public sealed class ManifestModel public sealed class ManifestModel
{ {
public Version AlayaVersion { get; } public Version AlayaVersion { get; }
public string RequiredJavaVersion { get; }
public Uri RequiredJavaBaseUrl { get; }
public string MinecraftVersion { get; } public string MinecraftVersion { get; }
public string NeoforgedVersion { get; } public string NeoforgedVersion { get; }
@@ -20,8 +18,6 @@ namespace AlayaCore.Models.Manifests
public ManifestModel( public ManifestModel(
Version alayaVersion, Version alayaVersion,
string requiredJavaVersion,
Uri requiredJavaUrl,
string minecraftVersion, string minecraftVersion,
string neoforgedVersion, string neoforgedVersion,
Uri serverUrl, Uri serverUrl,
@@ -29,8 +25,6 @@ namespace AlayaCore.Models.Manifests
IEnumerable<ModFileEntry> files) IEnumerable<ModFileEntry> files)
{ {
AlayaVersion = alayaVersion ?? throw new ArgumentNullException(nameof(alayaVersion)); AlayaVersion = alayaVersion ?? throw new ArgumentNullException(nameof(alayaVersion));
RequiredJavaVersion = RequireNonEmpty(requiredJavaVersion, nameof(requiredJavaVersion));
RequiredJavaBaseUrl = requiredJavaUrl ?? throw new ArgumentNullException(nameof(requiredJavaUrl));
MinecraftVersion = RequireNonEmpty(minecraftVersion, nameof(minecraftVersion)); MinecraftVersion = RequireNonEmpty(minecraftVersion, nameof(minecraftVersion));

View File

@@ -26,12 +26,34 @@ namespace AlayaCore.Services
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore)); _fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
} }
public Task<bool> IsAuthenticatedAsync(CancellationToken cancellationToken = default) public async Task<bool> IsAuthenticatedAsync(CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
bool authenticated = _session != null && _session.CheckIsValid(); if (_session != null && _session.CheckIsValid())
return Task.FromResult(authenticated); {
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) public async Task AuthenticateAsync(CancellationToken cancellationToken = default)
@@ -46,6 +68,10 @@ namespace AlayaCore.Services
.AuthenticateSilently(cancellationToken: cancellationToken) .AuthenticateSilently(cancellationToken: cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
catch (OperationCanceledException)
{
throw;
}
catch catch
{ {
_session = await loginHandler _session = await loginHandler

View File

@@ -12,8 +12,6 @@ using CmlLib.Core;
using CmlLib.Core.Installer.NeoForge; using CmlLib.Core.Installer.NeoForge;
using CmlLib.Core.Installer.NeoForge.Installers; using CmlLib.Core.Installer.NeoForge.Installers;
using CmlLib.Core.Installers; using CmlLib.Core.Installers;
using CmlLib.Core.Java;
using CmlLib.Core.VersionMetadata;
namespace AlayaCore.Services namespace AlayaCore.Services
{ {
@@ -39,7 +37,7 @@ namespace AlayaCore.Services
ManifestModel manifest, ManifestModel manifest,
InstallEnvironment environment, InstallEnvironment environment,
CancellationToken cancellationToken = default, CancellationToken cancellationToken = default,
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null, EventHandler<InstallerProgressChangedEventArgs>? minecraftProgress = null,
EventHandler<ByteProgress>? byteProgress = null) EventHandler<ByteProgress>? byteProgress = null)
{ {
if (manifest == null) if (manifest == null)
@@ -77,7 +75,7 @@ namespace AlayaCore.Services
await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false); await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false);
} }
MinecraftLauncher launcher = GetOrCreateLauncher(minecraftProgess, byteProgress); MinecraftLauncher launcher = GetOrCreateLauncher(minecraftProgress, byteProgress);
await launcher await launcher
.InstallAsync(manifest.MinecraftVersion, cancellationToken) .InstallAsync(manifest.MinecraftVersion, cancellationToken)
@@ -122,6 +120,16 @@ namespace AlayaCore.Services
return; return;
} }
bool neoForgeMismatch =
environment.NeoforgedInstalled &&
!string.Equals(environment.NeoforgedVersion, manifest.NeoforgedVersion, StringComparison.OrdinalIgnoreCase);
if (neoForgeMismatch)
{
await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false);
return;
}
if (!environment.MinecraftInstalled || if (!environment.MinecraftInstalled ||
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase)) !string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase))
{ {
@@ -135,12 +143,13 @@ namespace AlayaCore.Services
MinecraftLauncher launcher = GetOrCreateLauncher(); MinecraftLauncher launcher = GetOrCreateLauncher();
await DownloadAndInstallNeoForgeAsync( await InstallNeoForgeAsync(
launcher, launcher,
manifest, manifest,
environment, environment,
cancellationToken, cancellationToken,
progress, byteProgress).ConfigureAwait(false); progress,
byteProgress).ConfigureAwait(false);
await launcher await launcher
.InstallAsync(manifest.MinecraftVersion, cancellationToken) .InstallAsync(manifest.MinecraftVersion, cancellationToken)
@@ -193,10 +202,13 @@ namespace AlayaCore.Services
_gamePath = null; _gamePath = null;
_minecraftLauncher = null; _minecraftLauncher = null;
await _settingsService.UpdateLaunchVersionAsync(string.Empty, cancellationToken).ConfigureAwait(false); await _settingsService
.UpdateLaunchVersionAsync(string.Empty, cancellationToken)
.ConfigureAwait(false);
} }
private MinecraftLauncher GetOrCreateLauncher(EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null, private MinecraftLauncher GetOrCreateLauncher(
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgress = null,
EventHandler<ByteProgress>? byteProgress = null) EventHandler<ByteProgress>? byteProgress = null)
{ {
if (_minecraftLauncher != null) if (_minecraftLauncher != null)
@@ -206,18 +218,21 @@ namespace AlayaCore.Services
_gamePath = new AlayaPath(_fileStore); _gamePath = new AlayaPath(_fileStore);
_minecraftLauncher = new MinecraftLauncher(_gamePath); _minecraftLauncher = new MinecraftLauncher(_gamePath);
if(byteProgress != null) if (byteProgress != null)
{
_minecraftLauncher.ByteProgressChanged += byteProgress; _minecraftLauncher.ByteProgressChanged += byteProgress;
}
if(minecraftProgess != null)
_minecraftLauncher.FileProgressChanged += minecraftProgess; if (minecraftProgress != null)
{
_minecraftLauncher.FileProgressChanged += minecraftProgress;
}
return _minecraftLauncher; return _minecraftLauncher;
} }
private async Task DownloadAndInstallNeoForgeAsync( private async Task InstallNeoForgeAsync(
MinecraftLauncher launcher, MinecraftLauncher launcher,
ManifestModel manifest, ManifestModel manifest,
InstallEnvironment environment, InstallEnvironment environment,
@@ -230,16 +245,6 @@ namespace AlayaCore.Services
throw new ArgumentNullException(nameof(launcher)); 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); NeoForgeInstaller installer = new NeoForgeInstaller(launcher);
NeoForgeInstallOptions options = new NeoForgeInstallOptions NeoForgeInstallOptions options = new NeoForgeInstallOptions
@@ -248,12 +253,16 @@ namespace AlayaCore.Services
JavaPath = environment.JavaPath, JavaPath = environment.JavaPath,
SkipIfAlreadyInstalled = true SkipIfAlreadyInstalled = true
}; };
if(progress != null) if (progress != null)
{
options.FileProgress = progress; options.FileProgress = progress;
}
if(byteProgress != null)
if (byteProgress != null)
{
options.ByteProgress = byteProgress; options.ByteProgress = byteProgress;
}
string version = await installer string version = await installer
.Install(manifest.MinecraftVersion, manifest.NeoforgedVersion, options) .Install(manifest.MinecraftVersion, manifest.NeoforgedVersion, options)

View File

@@ -8,9 +8,7 @@ using AlayaCore.Installation;
using AlayaCore.Models; using AlayaCore.Models;
using AlayaCore.Models.Configuration; using AlayaCore.Models.Configuration;
using AlayaCore.Models.Manifests; using AlayaCore.Models.Manifests;
using AlayaCore.Utilities.Enums;
using CmlLib.Core; using CmlLib.Core;
using CmlLib.Core.Auth;
using CmlLib.Core.ProcessBuilder; using CmlLib.Core.ProcessBuilder;
namespace AlayaCore.Services namespace AlayaCore.Services
@@ -22,7 +20,6 @@ namespace AlayaCore.Services
private readonly ILaunchDirector _director; private readonly ILaunchDirector _director;
private readonly GameOptions _gameOptions; private readonly GameOptions _gameOptions;
private AlayaPath? _gamePath;
private MinecraftLauncher? _minecraftLauncher; private MinecraftLauncher? _minecraftLauncher;
public GameLaunchService( public GameLaunchService(
@@ -69,7 +66,11 @@ namespace AlayaCore.Services
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
MLaunchOption option = await BuildLaunchOptions(manifest, environment); MLaunchOption option = await BuildLaunchOptionsAsync(
manifest,
environment,
cancellationToken).ConfigureAwait(false);
MinecraftLauncher launcher = GetOrCreateLauncher(); MinecraftLauncher launcher = GetOrCreateLauncher();
var process = await launcher var process = await launcher
@@ -89,27 +90,23 @@ namespace AlayaCore.Services
return _minecraftLauncher; return _minecraftLauncher;
} }
_gamePath = new AlayaPath(_fileStore); _minecraftLauncher = new MinecraftLauncher(new AlayaPath(_fileStore));
_minecraftLauncher = new MinecraftLauncher(_gamePath);
return _minecraftLauncher; return _minecraftLauncher;
} }
private string GetMinecraftPath() private async Task<MLaunchOption> BuildLaunchOptionsAsync(
{ ManifestModel manifest,
return _fileStore.GetOrCreate(FolderLocation.Game); InstallEnvironment environment,
} CancellationToken cancellationToken)
private async Task<MLaunchOption> BuildLaunchOptions(ManifestModel manifest, InstallEnvironment environment)
{ {
if (manifest.ServerUrl == null) if (manifest.ServerUrl == null)
{ {
throw new InvalidDataException("Manifest Server Url is not configured."); throw new InvalidDataException("Manifest ServerUrl is not configured.");
} }
return new MLaunchOption return new MLaunchOption
{ {
Session = await _authService.GetSessionAsync(), Session = await _authService.GetSessionAsync(cancellationToken).ConfigureAwait(false),
JavaPath = environment.JavaPath, JavaPath = environment.JavaPath,
MinimumRamMb = _gameOptions.MinimumRamMB, MinimumRamMb = _gameOptions.MinimumRamMB,
MaximumRamMb = _gameOptions.MaximumRamMB, MaximumRamMb = _gameOptions.MaximumRamMB,

View File

@@ -14,7 +14,7 @@ namespace AlayaCore.Services
{ {
public sealed class HttpDownloadService : IDownloadService public sealed class HttpDownloadService : IDownloadService
{ {
private const int BUFFER_SIZE = 81920; private const int BufferSize = 81920;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@@ -45,16 +45,10 @@ namespace AlayaCore.Services
throw new ArgumentException("Destination path cannot be null, empty, or whitespace.", nameof(destinationPath)); throw new ArgumentException("Destination path cannot be null, empty, or whitespace.", nameof(destinationPath));
} }
if (string.IsNullOrWhiteSpace(sha512Hash)) string normalizedExpectedHash = NormalizeHash(sha512Hash);
{
throw new ArgumentException("SHA-512 hash cannot be null, empty, or whitespace.", nameof(sha512Hash));
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
string normalizedExpectedHash = NormalizeHash(sha512Hash);
string fileName = Path.GetFileName(destinationPath); string fileName = Path.GetFileName(destinationPath);
if (string.IsNullOrWhiteSpace(fileName)) if (string.IsNullOrWhiteSpace(fileName))
{ {
throw new ArgumentException("Destination path must include a file name.", nameof(destinationPath)); throw new ArgumentException("Destination path must include a file name.", nameof(destinationPath));
@@ -69,7 +63,7 @@ namespace AlayaCore.Services
progress?.Report(new DownloadProgress( progress?.Report(new DownloadProgress(
fileName: fileName, fileName: fileName,
destinationPath: destinationPath, destinationPath: destinationPath,
bytesDownloaded: existingLength, bytesDownloaded: 0,
totalBytes: existingLength, totalBytes: existingLength,
bytesPerSecond: null, bytesPerSecond: null,
statusMessage: "File already present and valid.")); statusMessage: "File already present and valid."));
@@ -91,7 +85,7 @@ namespace AlayaCore.Services
using HttpResponseMessage response = await _httpClient.GetAsync( using HttpResponseMessage response = await _httpClient.GetAsync(
sourceUri, sourceUri,
HttpCompletionOption.ResponseHeadersRead, HttpCompletionOption.ResponseHeadersRead,
cancellationToken); cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
@@ -106,17 +100,21 @@ namespace AlayaCore.Services
bytesPerSecond: null, bytesPerSecond: null,
statusMessage: "Starting download...")); 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( await using FileStream fileStream = new FileStream(
tempFilePath, tempFilePath,
FileMode.Create, FileMode.Create,
FileAccess.Write, FileAccess.Write,
FileShare.None, FileShare.None,
BUFFER_SIZE, BufferSize,
useAsync: true); useAsync: true);
using SHA512 sha512 = SHA512.Create(); using SHA512 sha512 = SHA512.Create();
byte[] buffer = new byte[BUFFER_SIZE]; byte[] buffer = new byte[BufferSize];
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
while (true) while (true)
@@ -125,7 +123,7 @@ namespace AlayaCore.Services
buffer, buffer,
0, 0,
buffer.Length, buffer.Length,
cancellationToken); cancellationToken).ConfigureAwait(false);
if (bytesRead == 0) if (bytesRead == 0)
{ {
@@ -136,7 +134,7 @@ namespace AlayaCore.Services
buffer, buffer,
0, 0,
bytesRead, bytesRead,
cancellationToken); cancellationToken).ConfigureAwait(false);
sha512.TransformBlock(buffer, 0, bytesRead, null, 0); sha512.TransformBlock(buffer, 0, bytesRead, null, 0);
@@ -158,10 +156,9 @@ namespace AlayaCore.Services
} }
sha512.TransformFinalBlock(Array.Empty<byte>(), 0, 0); sha512.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
await fileStream.FlushAsync(cancellationToken); await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);
string actualHash = ConvertToLowerHex(sha512.Hash); string actualHash = ConvertToLowerHex(sha512.Hash);
if (!string.Equals(actualHash, normalizedExpectedHash, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(actualHash, normalizedExpectedHash, StringComparison.OrdinalIgnoreCase))
{ {
throw new InvalidDataException( throw new InvalidDataException(
@@ -204,18 +201,13 @@ namespace AlayaCore.Services
throw new ArgumentException("File path cannot be null, empty, or whitespace.", nameof(filePath)); throw new ArgumentException("File path cannot be null, empty, or whitespace.", nameof(filePath));
} }
if (string.IsNullOrWhiteSpace(sha512Hash)) string normalizedExpectedHash = NormalizeHash(sha512Hash);
{
throw new ArgumentException("SHA-512 hash cannot be null, empty, or whitespace.", nameof(sha512Hash));
}
if (!File.Exists(filePath)) if (!File.Exists(filePath))
{ {
return false; return false;
} }
string normalizedExpectedHash = NormalizeHash(sha512Hash);
using FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); using FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
using SHA512 sha512 = SHA512.Create(); using SHA512 sha512 = SHA512.Create();
@@ -228,6 +220,7 @@ namespace AlayaCore.Services
private static void EnsureDestinationDirectoryExists(string destinationPath) private static void EnsureDestinationDirectoryExists(string destinationPath)
{ {
string? directoryPath = Path.GetDirectoryName(destinationPath); string? directoryPath = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrWhiteSpace(directoryPath)) if (!string.IsNullOrWhiteSpace(directoryPath))
{ {
Directory.CreateDirectory(directoryPath); Directory.CreateDirectory(directoryPath);
@@ -250,7 +243,31 @@ namespace AlayaCore.Services
private static string NormalizeHash(string hash) 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) private static string ConvertToLowerHex(byte[]? hashBytes)

View File

@@ -137,9 +137,9 @@ namespace AlayaCore.Services
{ {
string executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) string executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "javaw.exe" ? "javaw.exe"
: "javaw"; : "java";
string runtimePath = _fileStore.GetOrCreate(FolderLocation.JavaRuntime); string runtimePath = _fileStore.Get(FolderLocation.JavaRuntime);
string fullPath = Path.Combine(runtimePath, "bin", executableName); string fullPath = Path.Combine(runtimePath, "bin", executableName);
if (!File.Exists(fullPath)) if (!File.Exists(fullPath))
@@ -161,7 +161,10 @@ namespace AlayaCore.Services
return InstalledVersionState.Empty(); return InstalledVersionState.Empty();
} }
string[] versionDirectories = Directory.GetDirectories(versionsPath); string[] versionDirectories = Directory
.GetDirectories(versionsPath)
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.ToArray();
if (versionDirectories.Length == 0) if (versionDirectories.Length == 0)
{ {
@@ -187,7 +190,10 @@ namespace AlayaCore.Services
continue; continue;
} }
JObject versionJson = LoadJson(versionJsonPath); if (!TryLoadJson(versionJsonPath, out JObject? versionJson))
{
continue;
}
string? id = versionJson.Value<string>("id"); string? id = versionJson.Value<string>("id");
string? inheritsFrom = versionJson.Value<string>("inheritsFrom"); string? inheritsFrom = versionJson.Value<string>("inheritsFrom");
@@ -199,7 +205,7 @@ namespace AlayaCore.Services
if (IsNeoForgeVersion(id, inheritsFrom)) if (IsNeoForgeVersion(id, inheritsFrom))
{ {
neoForgeVersion = id; neoForgeVersion ??= id;
if (string.IsNullOrWhiteSpace(minecraftVersion) && !string.IsNullOrWhiteSpace(inheritsFrom)) if (string.IsNullOrWhiteSpace(minecraftVersion) && !string.IsNullOrWhiteSpace(inheritsFrom))
{ {
@@ -209,10 +215,7 @@ namespace AlayaCore.Services
continue; continue;
} }
if (string.IsNullOrWhiteSpace(minecraftVersion)) minecraftVersion ??= id;
{
minecraftVersion = id;
}
} }
return new InstalledVersionState(minecraftVersion, neoForgeVersion); return new InstalledVersionState(minecraftVersion, neoForgeVersion);
@@ -220,7 +223,7 @@ namespace AlayaCore.Services
private string GetVersionsPath() 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) private static bool IsNeoForgeVersion(string? id, string? inheritsFrom)
@@ -239,21 +242,31 @@ namespace AlayaCore.Services
value.Contains("neoforged", StringComparison.OrdinalIgnoreCase); 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); string json = File.ReadAllText(path);
if (string.IsNullOrWhiteSpace(json)) 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 private sealed class InstalledVersionState

View File

@@ -1,317 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using AlayaCore.Abstractions.Interfaces;
using AlayaCore.Abstractions.Interfaces.Services;
using AlayaCore.Installation;
using AlayaCore.Models.Manifests;
using AlayaCore.Utilities.Enums;
namespace AlayaCore.Services
{
public sealed class JavaService : IJavaService
{
private const string JavaArchiveHashPlaceholder = "REPLACE_WITH_MANIFEST_HASH_SUPPORT";
private const string BaseUrl = "https://aka.ms/download-jdk/";
private readonly IDownloadService _downloadService;
private readonly IFileStore _fileStore;
public JavaService(IDownloadService downloadService, IFileStore fileStore)
{
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
}
public async Task EnsureValidJavaInstalledAsync(
ManifestModel manifest,
InstallEnvironment environment,
CancellationToken cancellationToken = default)
{
if (manifest == null)
{
throw new ArgumentNullException(nameof(manifest));
}
if (environment == null)
{
throw new ArgumentNullException(nameof(environment));
}
cancellationToken.ThrowIfCancellationRequested();
string installDirectory = GetJavaInstallDirectory();
if (environment.JavaInstalled &&
Directory.Exists(installDirectory) &&
string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase))
{
return;
}
if (Directory.Exists(installDirectory))
{
await RemoveDirectoryAsync(installDirectory, cancellationToken).ConfigureAwait(false);
}
Uri downloadUri = GetPlatformSpecificJavaUri(manifest.RequiredJavaVersion);
string downloadPath = GetJavaDownloadPath(downloadUri);
Directory.CreateDirectory(Path.GetDirectoryName(downloadPath) ?? AppContext.BaseDirectory);
Directory.CreateDirectory(installDirectory);
await DownloadJavaAsync(
downloadUri,
downloadPath,
cancellationToken).ConfigureAwait(false);
await InstallJavaAsync(
downloadPath,
installDirectory,
cancellationToken).ConfigureAwait(false);
}
public Uri GetPlatformSpecificJavaUri(string javaVersion)
{
if (string.IsNullOrWhiteSpace(javaVersion))
{
throw new ArgumentException("Java version must be provided.", nameof(javaVersion));
}
string os = GetOsSegment();
string arch = GetArchitectureSegment();
string extension = GetFileExtension(os);
string fileName = $"microsoft-jdk-{javaVersion}-{os}-{arch}.{extension}";
return new Uri($"{BaseUrl}{fileName}");
}
private string GetJavaInstallDirectory()
{
return _fileStore.GetOrCreate(FolderLocation.Java);
}
private string GetJavaDownloadPath(Uri downloadUri)
{
if (downloadUri == null)
{
throw new ArgumentNullException(nameof(downloadUri));
}
string fileName = Path.GetFileName(downloadUri.AbsolutePath);
if (string.IsNullOrWhiteSpace(fileName))
{
throw new InvalidOperationException("Could not determine Java archive file name from download URI.");
}
return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Downloads), fileName);
}
private static string GetOsSegment()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "windows";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "linux";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "macos";
}
throw new PlatformNotSupportedException("Unsupported operating system.");
}
private static string GetArchitectureSegment()
{
return RuntimeInformation.OSArchitecture switch
{
Architecture.X64 => "x64",
Architecture.Arm64 => "aarch64",
Architecture.X86 => throw new NotSupportedException("X86 is not supported."),
Architecture.Arm => throw new NotSupportedException("Arm32 is not supported."),
_ => throw new PlatformNotSupportedException(
$"Unsupported architecture: {RuntimeInformation.OSArchitecture}")
};
}
private static string GetFileExtension(string os)
{
return os switch
{
"windows" => "zip",
"linux" => "tar.gz",
"macos" => "tar.gz",
_ => throw new PlatformNotSupportedException($"Unsupported OS: {os}")
};
}
private static Task RemoveDirectoryAsync(
string directoryPath,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(directoryPath))
{
throw new ArgumentException("Directory path cannot be null, empty, or whitespace.", nameof(directoryPath));
}
cancellationToken.ThrowIfCancellationRequested();
if (Directory.Exists(directoryPath))
{
Directory.Delete(directoryPath, recursive: true);
}
return Task.CompletedTask;
}
private async Task DownloadJavaAsync(
Uri javaUri,
string destinationPath,
CancellationToken cancellationToken = default)
{
if (javaUri == null)
{
throw new ArgumentNullException(nameof(javaUri));
}
if (!javaUri.IsAbsoluteUri)
{
throw new ArgumentException("Java download URI must be absolute.", nameof(javaUri));
}
if (string.IsNullOrWhiteSpace(destinationPath))
{
throw new ArgumentException("Destination path cannot be null, empty, or whitespace.", nameof(destinationPath));
}
cancellationToken.ThrowIfCancellationRequested();
string directory = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrWhiteSpace(directory))
{
Directory.CreateDirectory(directory);
}
if (File.Exists(destinationPath))
{
File.Delete(destinationPath);
}
await _downloadService.DownloadFileAsync(
javaUri,
destinationPath,
JavaArchiveHashPlaceholder,
cancellationToken: cancellationToken).ConfigureAwait(false);
}
private static async Task InstallJavaAsync(
string archivePath,
string installDirectory,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(archivePath))
{
throw new ArgumentException("Archive path cannot be null, empty, or whitespace.", nameof(archivePath));
}
if (string.IsNullOrWhiteSpace(installDirectory))
{
throw new ArgumentException("Install directory cannot be null, empty, or whitespace.", nameof(installDirectory));
}
cancellationToken.ThrowIfCancellationRequested();
if (!File.Exists(archivePath))
{
throw new FileNotFoundException("Java archive was not found.", archivePath);
}
Directory.CreateDirectory(installDirectory);
if (archivePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
{
ZipFile.ExtractToDirectory(archivePath, installDirectory);
return;
}
if (archivePath.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
{
await ExtractTarGzAsync(archivePath, installDirectory, cancellationToken).ConfigureAwait(false);
return;
}
throw new NotSupportedException(
$"Unsupported Java archive format: {Path.GetFileName(archivePath)}");
}
private static async Task ExtractTarGzAsync(
string archivePath,
string destinationDirectory,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(archivePath))
{
throw new ArgumentException("Archive path cannot be null, empty, or whitespace.", nameof(archivePath));
}
if (string.IsNullOrWhiteSpace(destinationDirectory))
{
throw new ArgumentException("Destination directory cannot be null, empty, or whitespace.", nameof(destinationDirectory));
}
Directory.CreateDirectory(destinationDirectory);
var startInfo = new ProcessStartInfo
{
FileName = "tar",
Arguments = $"-xzf \"{archivePath}\" -C \"{destinationDirectory}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = new Process { StartInfo = startInfo };
await using (cancellationToken.Register(() =>
{
try
{
if (!process.HasExited)
{
process.Kill();
}
}
catch
{
// Ignore race conditions if the process exits while cancellation is being handled.
}
}))
{
process.Start();
string standardOutput = await process.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
string standardError = await process.StandardError.ReadToEndAsync().ConfigureAwait(false);
await Task.Run(() => process.WaitForExit(), cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
if (process.ExitCode != 0)
{
throw new InvalidOperationException(
$"tar extraction failed with exit code {process.ExitCode}. Error: {standardError}. Output: {standardOutput}");
}
}
}
}
}

View File

@@ -40,30 +40,65 @@ namespace AlayaCore.Services
return true; return true;
} }
if (localManifest.Version == null)
{
return true;
}
if (string.IsNullOrWhiteSpace(localManifest.Sha512Hash)) if (string.IsNullOrWhiteSpace(localManifest.Sha512Hash))
{ {
return true; return true;
} }
string remoteHash = await _manifestService LauncherManifestModel remoteManifest = await _manifestService
.GetRemoteLauncherManifestHashAsync(cancellationToken) .GetRemoteLauncherManifestAsync(cancellationToken)
.ConfigureAwait(false); .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."); 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(), localManifest.Sha512Hash.Trim(),
remoteHash.Trim(), remoteManifest.Sha512Hash.Trim(),
StringComparison.OrdinalIgnoreCase); StringComparison.OrdinalIgnoreCase);
return versionMismatch || hashMismatch;
} }
public Task LaunchUpdaterAsync(LauncherManifestModel newManifest, CancellationToken cancellationToken = default) public Task LaunchUpdaterAsync(
LauncherManifestModel newManifest,
CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested(); 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; string updaterPath = _options.AlayaUpdaterPath;
if (string.IsNullOrWhiteSpace(updaterPath)) if (string.IsNullOrWhiteSpace(updaterPath))
@@ -84,11 +119,13 @@ namespace AlayaCore.Services
string workingDirectory = Path.GetDirectoryName(updaterPath) string workingDirectory = Path.GetDirectoryName(updaterPath)
?? throw new InvalidOperationException("Updater working directory could not be resolved."); ?? throw new InvalidOperationException("Updater working directory could not be resolved.");
string arguments = $"-url \"{newManifest.DownloadUri.AbsoluteUri}\"";
var startInfo = new ProcessStartInfo var startInfo = new ProcessStartInfo
{ {
FileName = updaterPath, FileName = updaterPath,
WorkingDirectory = workingDirectory, WorkingDirectory = workingDirectory,
Arguments = $@"-url {newManifest.DownloadUri}", Arguments = arguments,
UseShellExecute = false, UseShellExecute = false,
CreateNoWindow = true CreateNoWindow = true
}; };
@@ -102,7 +139,7 @@ namespace AlayaCore.Services
throw new InvalidOperationException("Failed to start updater process."); 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); throw new InvalidOperationException("Failed to launch updater process.", ex);
} }

View File

@@ -17,9 +17,9 @@ namespace AlayaCore.Services
{ {
public sealed class ManifestService : IManifestService public sealed class ManifestService : IManifestService
{ {
private const string CORE_MANIFEST_FILE_NAME = "CoreManifest.json"; private const string CoreManifestFileName = "CoreManifest.json";
private const string LAUNCHER_MANIFEST_FILE_NAME = "LauncherManifest.json"; private const string LauncherManifestFileName = "LauncherManifest.json";
private const string INSTALLED_MODS_MANIFEST_FILE_NAME = "InstalledModsManifest.json"; private const string InstalledModsManifestFileName = "InstalledModsManifest.json";
private readonly IDownloadService _downloadService; private readonly IDownloadService _downloadService;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@@ -50,7 +50,8 @@ namespace AlayaCore.Services
cancellationToken); cancellationToken);
} }
public async Task<InstalledModsManifestModel> GetInstalledModsManifestAsync(CancellationToken cancellationToken = default) public async Task<InstalledModsManifestModel> GetInstalledModsManifestAsync(
CancellationToken cancellationToken = default)
{ {
string path = GetInstalledModsManifestPath(); string path = GetInstalledModsManifestPath();
@@ -66,11 +67,12 @@ namespace AlayaCore.Services
return InstalledModsManifestModel.Empty(); return InstalledModsManifestModel.Empty();
} }
InstalledModsManifestModel? manifest = DeserializeAndMapManifest<InstalledModsManifestDto, InstalledModsManifestModel>( InstalledModsManifestModel? manifest =
json, DeserializeAndMapManifest<InstalledModsManifestDto, InstalledModsManifestModel>(
path, json,
static dto => dto.ToModel(), path,
swallowDeserializationErrors: true); static dto => dto.ToModel(),
swallowDeserializationErrors: true);
return manifest ?? InstalledModsManifestModel.Empty(); return manifest ?? InstalledModsManifestModel.Empty();
} }
@@ -105,28 +107,10 @@ namespace AlayaCore.Services
public async Task<Version> GetRemoteCoreManifestVersionAsync(CancellationToken cancellationToken = default) public async Task<Version> GetRemoteCoreManifestVersionAsync(CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested(); ManifestModel remoteManifest = await GetRemoteManifestAsync<ManifestDto, ManifestModel>(
using HttpResponseMessage response = await _httpClient.GetAsync(
_options.CoreManifestUri, _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<ManifestDto, ManifestModel>(
json,
_options.CoreManifestUri.ToString(),
static dto => dto.ToModel(), static dto => dto.ToModel(),
swallowDeserializationErrors: false)!; cancellationToken).ConfigureAwait(false);
if (remoteManifest.AlayaVersion == null) if (remoteManifest.AlayaVersion == null)
{ {
@@ -137,30 +121,13 @@ namespace AlayaCore.Services
return remoteManifest.AlayaVersion; return remoteManifest.AlayaVersion;
} }
public async Task<string> GetRemoteLauncherManifestHashAsync(CancellationToken cancellationToken = default) public async Task<LauncherManifestModel> GetRemoteLauncherManifestAsync(
CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested(); LauncherManifestModel remoteManifest = await GetRemoteManifestAsync<LauncherManifestDto, LauncherManifestModel>(
using HttpResponseMessage response = await _httpClient.GetAsync(
_options.LauncherManifestUri, _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<LauncherManifestDto, LauncherManifestModel>(
json,
_options.LauncherManifestUri.ToString(),
static dto => dto.ToModel(), static dto => dto.ToModel(),
swallowDeserializationErrors: false)!; cancellationToken).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(remoteManifest.Sha512Hash)) 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."); $"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() 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() 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() 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<TModel?> LoadLocalManifestAsync<TDto, TModel>( private async Task<TModel?> LoadLocalManifestAsync<TDto, TModel>(
@@ -283,6 +256,51 @@ namespace AlayaCore.Services
swallowDeserializationErrors: false)!; swallowDeserializationErrors: false)!;
} }
private async Task<TModel> GetRemoteManifestAsync<TDto, TModel>(
Uri manifestUri,
Func<TDto, TModel> 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<TDto, TModel>(
json,
manifestUri.ToString(),
map,
swallowDeserializationErrors: false)!;
}
private static TModel? DeserializeAndMapManifest<TDto, TModel>( private static TModel? DeserializeAndMapManifest<TDto, TModel>(
string json, string json,
string sourceName, string sourceName,
@@ -342,7 +360,7 @@ namespace AlayaCore.Services
{ {
return map(dto); return map(dto);
} }
catch (Exception ex) when (!(ex is OperationCanceledException)) catch (Exception ex) when (ex is not OperationCanceledException)
{ {
if (swallowDeserializationErrors) if (swallowDeserializationErrors)
{ {

View File

@@ -5,7 +5,6 @@ namespace AlayaCore.States
Ready, Ready,
LauncherNeedsUpdate, LauncherNeedsUpdate,
NeedAuthenticating, NeedAuthenticating,
InstallJava,
InstallMinecraft, InstallMinecraft,
InstallNeoforge, InstallNeoforge,
SyncMods SyncMods
@@ -14,7 +13,6 @@ namespace AlayaCore.States
public sealed class LaunchPlan public sealed class LaunchPlan
{ {
public bool LauncherNeedsUpdate { get; } public bool LauncherNeedsUpdate { get; }
public bool JavaNeedsInstallOrUpdate { get; }
public bool MinecraftNeedsInstallOrUpdate { get; } public bool MinecraftNeedsInstallOrUpdate { get; }
public bool NeoforgeNeedsInstallOrUpdate { get; } public bool NeoforgeNeedsInstallOrUpdate { get; }
public bool ModsNeedSync { get; } public bool ModsNeedSync { get; }
@@ -26,7 +24,6 @@ namespace AlayaCore.States
public bool NeedsUpdating => public bool NeedsUpdating =>
LauncherNeedsUpdate || LauncherNeedsUpdate ||
JavaNeedsInstallOrUpdate ||
MinecraftNeedsInstallOrUpdate || MinecraftNeedsInstallOrUpdate ||
NeoforgeNeedsInstallOrUpdate || NeoforgeNeedsInstallOrUpdate ||
ModsNeedSync; ModsNeedSync;
@@ -36,14 +33,12 @@ namespace AlayaCore.States
public LaunchPlan( public LaunchPlan(
bool launcherNeedsUpdate, bool launcherNeedsUpdate,
bool javaNeedsInstallOrUpdate,
bool minecraftNeedsInstallOrUpdate, bool minecraftNeedsInstallOrUpdate,
bool neoforgeNeedsInstallOrUpdate, bool neoforgeNeedsInstallOrUpdate,
bool modsNeedSync, bool modsNeedSync,
bool needAuthenticating) bool needAuthenticating)
{ {
LauncherNeedsUpdate = launcherNeedsUpdate; LauncherNeedsUpdate = launcherNeedsUpdate;
JavaNeedsInstallOrUpdate = javaNeedsInstallOrUpdate;
MinecraftNeedsInstallOrUpdate = minecraftNeedsInstallOrUpdate; MinecraftNeedsInstallOrUpdate = minecraftNeedsInstallOrUpdate;
NeoforgeNeedsInstallOrUpdate = neoforgeNeedsInstallOrUpdate; NeoforgeNeedsInstallOrUpdate = neoforgeNeedsInstallOrUpdate;
ModsNeedSync = modsNeedSync; ModsNeedSync = modsNeedSync;
@@ -58,9 +53,6 @@ namespace AlayaCore.States
if (NeedAuthenticating) if (NeedAuthenticating)
return LaunchState.NeedAuthenticating; return LaunchState.NeedAuthenticating;
if (JavaNeedsInstallOrUpdate)
return LaunchState.InstallJava;
if (MinecraftNeedsInstallOrUpdate) if (MinecraftNeedsInstallOrUpdate)
return LaunchState.InstallMinecraft; return LaunchState.InstallMinecraft;
@@ -77,7 +69,6 @@ namespace AlayaCore.States
{ {
return new LaunchPlan( return new LaunchPlan(
launcherNeedsUpdate: false, launcherNeedsUpdate: false,
javaNeedsInstallOrUpdate: false,
minecraftNeedsInstallOrUpdate: false, minecraftNeedsInstallOrUpdate: false,
neoforgeNeedsInstallOrUpdate: false, neoforgeNeedsInstallOrUpdate: false,
modsNeedSync: false, modsNeedSync: false,

View File

@@ -47,8 +47,6 @@ namespace AlayaCore.Utilities.Extensions
return new ManifestModel( return new ManifestModel(
dto.AlayaVersion, dto.AlayaVersion,
dto.RequiredJavaVersion,
dto.RequiredJavaUrl,
dto.MinecraftVersion, dto.MinecraftVersion,
dto.NeoforgedVersion, dto.NeoforgedVersion,
dto.ServerUrl, dto.ServerUrl,
@@ -80,8 +78,6 @@ namespace AlayaCore.Utilities.Extensions
return new ManifestDto return new ManifestDto
{ {
AlayaVersion = model.AlayaVersion, AlayaVersion = model.AlayaVersion,
RequiredJavaVersion = model.RequiredJavaVersion,
RequiredJavaUrl = model.RequiredJavaBaseUrl,
MinecraftVersion = model.MinecraftVersion, MinecraftVersion = model.MinecraftVersion,
NeoforgedVersion = model.NeoforgedVersion, NeoforgedVersion = model.NeoforgedVersion,
Files = model.Files.Select(file => file.ToDto()).ToList() Files = model.Files.Select(file => file.ToDto()).ToList()

View File

@@ -10,7 +10,7 @@ namespace AlayaCore.Utilities.Stores
{ {
private static readonly string BaseDirectoryPath = AppContext.BaseDirectory; private static readonly string BaseDirectoryPath = AppContext.BaseDirectory;
private static readonly string JavaDirectoryPath = Path.Combine(BaseDirectoryPath, "Java"); 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 GameDirectoryPath = Path.Combine(BaseDirectoryPath, "Game");
private static readonly string ModsDirectoryPath = Path.Combine(GameDirectoryPath, "mods"); private static readonly string ModsDirectoryPath = Path.Combine(GameDirectoryPath, "mods");
private static readonly string ResourcePacksDirectoryPath = Path.Combine(GameDirectoryPath, "resourcepacks"); private static readonly string ResourcePacksDirectoryPath = Path.Combine(GameDirectoryPath, "resourcepacks");
@@ -36,6 +36,26 @@ namespace AlayaCore.Utilities.Stores
{ FolderLocation.Data, DataDirectoryPath} { 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) public string Get(FolderLocation location)
{ {
if (!Folders.TryGetValue(location, out string? path)) if (!Folders.TryGetValue(location, out string? path))