Updated Filestore. Finished Auth and Install Services. Introduced GameOptions and BaseConfig. Adjusted Settings Service to reflect.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
namespace AlayaCore.Abstractions.Configuration
|
namespace AlayaCore.Abstractions.Configuration
|
||||||
{
|
{
|
||||||
public class BaseConfig
|
public abstract class BaseConfig
|
||||||
{
|
{
|
||||||
|
public abstract string FileName { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
|
||||||
namespace AlayaCore.Abstractions.Interfaces
|
namespace AlayaCore.Abstractions.Interfaces
|
||||||
{
|
{
|
||||||
public interface IFileStore
|
public interface IFileStore
|
||||||
{
|
{
|
||||||
|
string Get(FolderLocation location);
|
||||||
|
string GetOrCreate(FolderLocation location);
|
||||||
|
string Combine(FolderLocation location, params string[] paths);
|
||||||
|
bool Exists(FolderLocation location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AlayaCore.Models;
|
using AlayaCore.Models;
|
||||||
using AlayaCore.States;
|
using AlayaCore.States;
|
||||||
|
using CmlLib.Core;
|
||||||
|
using CmlLib.Core.Installers;
|
||||||
|
|
||||||
namespace AlayaCore.Abstractions.Interfaces
|
namespace AlayaCore.Abstractions.Interfaces
|
||||||
{
|
{
|
||||||
@@ -9,7 +12,11 @@ namespace AlayaCore.Abstractions.Interfaces
|
|||||||
{
|
{
|
||||||
Task<LaunchPlan> EvaluateAsync(CancellationToken cancellationToken = default);
|
Task<LaunchPlan> EvaluateAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task InstallOrUpdateAsync(CancellationToken cancellationToken = default);
|
Task InstallOrUpdateAsync(CancellationToken cancellationToken = default,
|
||||||
|
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null,
|
||||||
|
EventHandler<ByteProgress>? byteProgress = null,
|
||||||
|
IProgress<InstallerProgressChangedEventArgs>? neoForgeProgress = null,
|
||||||
|
IProgress<ByteProgress>? neoForgeByteProgress = null);
|
||||||
|
|
||||||
Task LaunchAsync(CancellationToken cancellationToken = default);
|
Task LaunchAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CmlLib.Core.Auth;
|
||||||
|
|
||||||
namespace AlayaCore.Abstractions.Interfaces.Services
|
namespace AlayaCore.Abstractions.Interfaces.Services
|
||||||
{
|
{
|
||||||
public interface IAuthService
|
public interface IAuthService
|
||||||
{
|
{
|
||||||
|
Task<bool> IsAuthenticatedAsync(CancellationToken cancellationToken = default);
|
||||||
|
Task AuthenticateAsync(CancellationToken cancellationToken = default);
|
||||||
|
Task SignOutAsync(CancellationToken cancellationToken = default);
|
||||||
|
Task<MSession> GetSessionAsync(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,31 @@
|
|||||||
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AlayaCore.Installation;
|
using AlayaCore.Installation;
|
||||||
using AlayaCore.Models.Manifests;
|
using AlayaCore.Models.Manifests;
|
||||||
|
using CmlLib.Core;
|
||||||
|
using CmlLib.Core.Installers;
|
||||||
|
|
||||||
namespace AlayaCore.Abstractions.Interfaces.Services
|
namespace AlayaCore.Abstractions.Interfaces.Services
|
||||||
{
|
{
|
||||||
public interface IMinecraftService
|
public interface IGameInstallService
|
||||||
{
|
{
|
||||||
Task EnsureMinecraftInstalledAsync(ManifestModel manifest, InstallEnvironment environment, CancellationToken cancellationToken);
|
Task EnsureMinecraftInstalledAsync(
|
||||||
Task EnsureNeoForgeInstalledAsync(ManifestModel manifest, InstallEnvironment environment, CancellationToken cancellationToken);
|
ManifestModel manifest,
|
||||||
|
InstallEnvironment environment,
|
||||||
|
CancellationToken cancellationToken = default,
|
||||||
|
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null,
|
||||||
|
EventHandler<ByteProgress>? byteProgress = null);
|
||||||
|
|
||||||
|
Task EnsureNeoForgeInstalledAsync(
|
||||||
|
ManifestModel manifest,
|
||||||
|
InstallEnvironment environment,
|
||||||
|
CancellationToken cancellationToken = default,
|
||||||
|
IProgress<InstallerProgressChangedEventArgs>? progress = null,
|
||||||
|
IProgress<ByteProgress>? byteProgress = null);
|
||||||
|
|
||||||
|
Task VerifyFilesAsync(
|
||||||
|
ManifestModel manifest,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,11 +7,18 @@ namespace AlayaCore.Abstractions.Interfaces.Services
|
|||||||
public interface ISettingsService
|
public interface ISettingsService
|
||||||
{
|
{
|
||||||
LauncherOptions LauncherOptions { get; }
|
LauncherOptions LauncherOptions { get; }
|
||||||
|
GameOptions GameOptions { get; }
|
||||||
|
|
||||||
Task SetForceReinstallAsync(bool value, CancellationToken cancellationToken = default);
|
Task SetForceReinstallAsync(bool value, CancellationToken cancellationToken = default);
|
||||||
|
Task UpdateLaunchVersionAsync(string newVersion, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task SaveLauncherOptionsAsync(CancellationToken cancellationToken = default);
|
Task SaveLauncherOptionsAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task LoadLauncherOptionsAsync(CancellationToken cancellationToken = default);
|
Task LoadLauncherOptionsAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task SaveGameOptionsAsync(CancellationToken cancellationToken = default);
|
||||||
|
Task LoadGameOptionsAsync(CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task LoadAllAsync(CancellationToken cancellationToken = default);
|
||||||
|
Task SaveAllAsync(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CmlLib.Core" Version="4.0.6" />
|
||||||
|
<PackageReference Include="CmlLib.Core.Auth.Microsoft" Version="3.3.1" />
|
||||||
|
<PackageReference Include="CmlLib.Core.Installer.NeoForge" Version="4.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
<PackageReference Include="XboxAuthNet.Game.Msal" Version="0.1.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ using AlayaCore.Installation;
|
|||||||
using AlayaCore.Models.Configuration;
|
using AlayaCore.Models.Configuration;
|
||||||
using AlayaCore.Models.Manifests;
|
using AlayaCore.Models.Manifests;
|
||||||
using AlayaCore.States;
|
using AlayaCore.States;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
using CmlLib.Core;
|
||||||
|
using CmlLib.Core.Installers;
|
||||||
|
|
||||||
namespace AlayaCore
|
namespace AlayaCore
|
||||||
{
|
{
|
||||||
@@ -20,12 +23,13 @@ namespace AlayaCore
|
|||||||
private readonly IJavaService _javaService;
|
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 ISettingsService _settingsService;
|
||||||
|
private readonly IAuthService _authService;
|
||||||
private readonly LauncherOptions _options;
|
private readonly LauncherOptions _options;
|
||||||
|
|
||||||
public bool CanRun { get; private set; }
|
public bool CanRun { get; private set; }
|
||||||
|
|
||||||
public bool NeedsUpdating { get; private set; }
|
public bool NeedsUpdating { get; private set; }
|
||||||
|
|
||||||
public LaunchPlan? CurrentPlan { get; private set; }
|
public LaunchPlan? CurrentPlan { get; private set; }
|
||||||
|
|
||||||
public LaunchDirector(
|
public LaunchDirector(
|
||||||
@@ -35,6 +39,9 @@ namespace AlayaCore
|
|||||||
IJavaService javaService,
|
IJavaService javaService,
|
||||||
IModService modService,
|
IModService modService,
|
||||||
IGameLaunchService gameLaunchService,
|
IGameLaunchService gameLaunchService,
|
||||||
|
IGameInstallService gameInstallService,
|
||||||
|
ISettingsService settingsService,
|
||||||
|
IAuthService authService,
|
||||||
LauncherOptions options)
|
LauncherOptions options)
|
||||||
{
|
{
|
||||||
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
|
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
|
||||||
@@ -43,6 +50,9 @@ namespace AlayaCore
|
|||||||
_javaService = javaService ?? throw new ArgumentNullException(nameof(javaService));
|
_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));
|
||||||
|
_settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService));
|
||||||
|
_authService = authService ?? throw new ArgumentNullException(nameof(authService));
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +71,8 @@ namespace AlayaCore
|
|||||||
javaNeedsInstallOrUpdate: false,
|
javaNeedsInstallOrUpdate: false,
|
||||||
minecraftNeedsInstallOrUpdate: false,
|
minecraftNeedsInstallOrUpdate: false,
|
||||||
neoforgeNeedsInstallOrUpdate: false,
|
neoforgeNeedsInstallOrUpdate: false,
|
||||||
modsNeedSync: false);
|
modsNeedSync: false,
|
||||||
|
needAuthenticating: false);
|
||||||
|
|
||||||
ApplyPlan(launcherUpdatePlan);
|
ApplyPlan(launcherUpdatePlan);
|
||||||
return launcherUpdatePlan;
|
return launcherUpdatePlan;
|
||||||
@@ -78,6 +89,10 @@ namespace AlayaCore
|
|||||||
!environment.JavaInstalled ||
|
!environment.JavaInstalled ||
|
||||||
!string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase);
|
!string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
bool needAuthenticating = !await _authService
|
||||||
|
.IsAuthenticatedAsync(cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
bool minecraftNeedsInstallOrUpdate =
|
bool minecraftNeedsInstallOrUpdate =
|
||||||
_options.ForceReinstall ||
|
_options.ForceReinstall ||
|
||||||
!environment.MinecraftInstalled ||
|
!environment.MinecraftInstalled ||
|
||||||
@@ -97,13 +112,18 @@ namespace AlayaCore
|
|||||||
javaNeedsInstallOrUpdate: javaNeedsInstallOrUpdate,
|
javaNeedsInstallOrUpdate: javaNeedsInstallOrUpdate,
|
||||||
minecraftNeedsInstallOrUpdate: minecraftNeedsInstallOrUpdate,
|
minecraftNeedsInstallOrUpdate: minecraftNeedsInstallOrUpdate,
|
||||||
neoforgeNeedsInstallOrUpdate: neoforgeNeedsInstallOrUpdate,
|
neoforgeNeedsInstallOrUpdate: neoforgeNeedsInstallOrUpdate,
|
||||||
modsNeedSync: modsNeedSync);
|
modsNeedSync: modsNeedSync,
|
||||||
|
needAuthenticating: needAuthenticating);
|
||||||
|
|
||||||
ApplyPlan(plan);
|
ApplyPlan(plan);
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstallOrUpdateAsync(CancellationToken cancellationToken = default)
|
public async Task InstallOrUpdateAsync(CancellationToken cancellationToken = default,
|
||||||
|
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null,
|
||||||
|
EventHandler<ByteProgress>? byteProgress = null,
|
||||||
|
IProgress<InstallerProgressChangedEventArgs>? neoForgeProgress = null,
|
||||||
|
IProgress<ByteProgress>? neoForgeByteProgress = null)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
@@ -113,8 +133,8 @@ namespace AlayaCore
|
|||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
ManifestModel? manifest = null;
|
ManifestModel? manifest;
|
||||||
InstallEnvironment? environment = null;
|
InstallEnvironment? environment;
|
||||||
|
|
||||||
switch (plan.State)
|
switch (plan.State)
|
||||||
{
|
{
|
||||||
@@ -135,7 +155,6 @@ namespace AlayaCore
|
|||||||
case LaunchState.InstallJava:
|
case LaunchState.InstallJava:
|
||||||
{
|
{
|
||||||
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
environment = await _installStateService
|
environment = await _installStateService
|
||||||
.GetCurrentEnvironmentAsync(cancellationToken)
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
@@ -147,20 +166,43 @@ namespace AlayaCore
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case LaunchState.NeedAuthenticating:
|
||||||
|
{
|
||||||
|
await _authService.AuthenticateAsync(cancellationToken);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case LaunchState.InstallMinecraft:
|
case LaunchState.InstallMinecraft:
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("Minecraft install/update flow has not been implemented yet.");
|
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
environment = await _installStateService
|
||||||
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
await _gameInstallService
|
||||||
|
.EnsureMinecraftInstalledAsync(manifest, environment, cancellationToken, minecraftProgess, byteProgress)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LaunchState.InstallNeoforge:
|
case LaunchState.InstallNeoforge:
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("NeoForge install/update flow has not been implemented yet.");
|
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
environment = await _installStateService
|
||||||
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
await _gameInstallService
|
||||||
|
.EnsureNeoForgeInstalledAsync(manifest, environment, cancellationToken, neoForgeProgress, neoForgeByteProgress)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LaunchState.SyncMods:
|
case LaunchState.SyncMods:
|
||||||
{
|
{
|
||||||
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
environment = await _installStateService
|
environment = await _installStateService
|
||||||
.GetCurrentEnvironmentAsync(cancellationToken)
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
@@ -185,6 +227,18 @@ namespace AlayaCore
|
|||||||
|
|
||||||
plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_options.ForceReinstall)
|
||||||
|
{
|
||||||
|
await _settingsService.SetForceReinstallAsync(false, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!plan.CanRun)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Install/update completed, but the launcher is still not in a runnable state.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LaunchAsync(CancellationToken cancellationToken = default)
|
public async Task LaunchAsync(CancellationToken cancellationToken = default)
|
||||||
@@ -250,7 +304,7 @@ namespace AlayaCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
var requiredMods = manifest.Files
|
var requiredMods = manifest.Files
|
||||||
.Where(file => file.Type == AlayaCore.Utilities.Enums.FileType.Mod)
|
.Where(file => file.Type == FileType.Mod)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var installedMods = environment.InstalledModsManifest.Mods;
|
var installedMods = environment.InstalledModsManifest.Mods;
|
||||||
|
|||||||
@@ -1,7 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
using CmlLib.Core;
|
||||||
|
|
||||||
namespace AlayaCore.Models
|
namespace AlayaCore.Models
|
||||||
{
|
{
|
||||||
public class AlayaPath
|
public class AlayaPath : MinecraftPath
|
||||||
{
|
{
|
||||||
|
public AlayaPath(IFileStore fileStore)
|
||||||
|
{
|
||||||
|
BasePath = NormalizePath(fileStore.Get(FolderLocation.Game));
|
||||||
|
|
||||||
|
Library = NormalizePath(BasePath + "/libraries");
|
||||||
|
Versions = NormalizePath(BasePath + "/versions");
|
||||||
|
Resource = NormalizePath(BasePath + "/resources");
|
||||||
|
|
||||||
|
Runtime = NormalizePath(fileStore.GetOrCreate(FolderLocation.JavaRuntime));
|
||||||
|
Assets = NormalizePath(BasePath + "/assets");
|
||||||
|
|
||||||
|
CreateDirs();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,30 @@
|
|||||||
|
using AlayaCore.Abstractions.Configuration;
|
||||||
|
|
||||||
namespace AlayaCore.Models.Configuration
|
namespace AlayaCore.Models.Configuration
|
||||||
{
|
{
|
||||||
public class GameOptions
|
public class GameOptions : BaseConfig
|
||||||
{
|
{
|
||||||
|
public override string FileName => "Game.json";
|
||||||
|
|
||||||
|
public string? LaunchVersion { get; set; }
|
||||||
|
|
||||||
|
public int MinimumRamMB { get; set; }
|
||||||
|
public int MaximumRamMB { get; set; }
|
||||||
|
|
||||||
|
public int ScreenWidth { get; set; }
|
||||||
|
public int ScreenHeight { get; set; }
|
||||||
|
|
||||||
|
public bool Fullscreen { get; set; }
|
||||||
|
|
||||||
|
public static GameOptions Default { get; } = new GameOptions
|
||||||
|
{
|
||||||
|
LaunchVersion = null,
|
||||||
|
MinimumRamMB = 1024,
|
||||||
|
MaximumRamMB = 2048,
|
||||||
|
ScreenWidth = 1920,
|
||||||
|
ScreenHeight = 1080,
|
||||||
|
Fullscreen = false,
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,16 @@
|
|||||||
|
using AlayaCore.Abstractions.Configuration;
|
||||||
|
|
||||||
namespace AlayaCore.Models.Configuration
|
namespace AlayaCore.Models.Configuration
|
||||||
{
|
{
|
||||||
public sealed class LauncherOptions
|
public sealed class LauncherOptions : BaseConfig
|
||||||
{
|
{
|
||||||
public bool ForceReinstall { get; set; }
|
public bool ForceReinstall { get; set; }
|
||||||
|
|
||||||
|
public override string FileName => "Launcher.json";
|
||||||
|
|
||||||
|
public static LauncherOptions Default { get; } = new LauncherOptions
|
||||||
|
{
|
||||||
|
ForceReinstall = false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,16 +23,15 @@ namespace AlayaCore.Models.Manifests.DTO
|
|||||||
[JsonProperty("minecraftVersion", Required = Required.Always)]
|
[JsonProperty("minecraftVersion", Required = Required.Always)]
|
||||||
public string MinecraftVersion { get; set; } = string.Empty;
|
public string MinecraftVersion { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("minecraftUrl", Required = Required.Always)]
|
|
||||||
[JsonConverter(typeof(UriConverter))]
|
|
||||||
public Uri MinecraftUrl { get; set; } = null!;
|
|
||||||
|
|
||||||
[JsonProperty("neoforgedVersion", Required = Required.Always)]
|
[JsonProperty("neoforgedVersion", Required = Required.Always)]
|
||||||
public string NeoforgedVersion { get; set; } = string.Empty;
|
public string NeoforgedVersion { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("neoforgedUrl", Required = Required.Always)]
|
[JsonProperty("serverUrl", Required = Required.Always)]
|
||||||
[JsonConverter(typeof(UriConverter))]
|
[JsonConverter(typeof(UriConverter))]
|
||||||
public Uri NeoforgedUrl { get; set; } = null!;
|
public Uri ServerUrl { get; set; } = null!;
|
||||||
|
|
||||||
|
[JsonProperty("serverPort", Required = Required.Always)]
|
||||||
|
public int ServerPort { get; }
|
||||||
|
|
||||||
[JsonProperty("files", Required = Required.Always)]
|
[JsonProperty("files", Required = Required.Always)]
|
||||||
public List<ModFileEntryDto> Files { get; set; } = new List<ModFileEntryDto>();
|
public List<ModFileEntryDto> Files { get; set; } = new List<ModFileEntryDto>();
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ namespace AlayaCore.Models.Manifests
|
|||||||
{
|
{
|
||||||
public Version AlayaVersion { get; }
|
public Version AlayaVersion { get; }
|
||||||
public string RequiredJavaVersion { get; }
|
public string RequiredJavaVersion { get; }
|
||||||
public Uri RequiredJavaUrl { get; }
|
public Uri RequiredJavaBaseUrl { get; }
|
||||||
public string MinecraftVersion { get; }
|
public string MinecraftVersion { get; }
|
||||||
public Uri MinecraftUrl { get; }
|
|
||||||
public string NeoforgedVersion { get; }
|
public string NeoforgedVersion { get; }
|
||||||
public Uri NeoforgedUrl { get; }
|
|
||||||
|
public Uri ServerUrl { get; }
|
||||||
|
public int ServerPort { get; }
|
||||||
public IReadOnlyList<ModFileEntry> Files { get; }
|
public IReadOnlyList<ModFileEntry> Files { get; }
|
||||||
|
|
||||||
public ManifestModel(
|
public ManifestModel(
|
||||||
@@ -22,20 +23,18 @@ namespace AlayaCore.Models.Manifests
|
|||||||
string requiredJavaVersion,
|
string requiredJavaVersion,
|
||||||
Uri requiredJavaUrl,
|
Uri requiredJavaUrl,
|
||||||
string minecraftVersion,
|
string minecraftVersion,
|
||||||
Uri minecraftUrl,
|
|
||||||
string neoforgedVersion,
|
string neoforgedVersion,
|
||||||
Uri neoforgedUrl,
|
Uri serverUrl,
|
||||||
|
int serverPort,
|
||||||
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));
|
RequiredJavaVersion = RequireNonEmpty(requiredJavaVersion, nameof(requiredJavaVersion));
|
||||||
RequiredJavaUrl = requiredJavaUrl ?? throw new ArgumentNullException(nameof(requiredJavaUrl));
|
RequiredJavaBaseUrl = requiredJavaUrl ?? throw new ArgumentNullException(nameof(requiredJavaUrl));
|
||||||
|
|
||||||
MinecraftVersion = RequireNonEmpty(minecraftVersion, nameof(minecraftVersion));
|
MinecraftVersion = RequireNonEmpty(minecraftVersion, nameof(minecraftVersion));
|
||||||
MinecraftUrl = minecraftUrl ?? throw new ArgumentNullException(nameof(minecraftUrl));
|
|
||||||
|
|
||||||
NeoforgedVersion = RequireNonEmpty(neoforgedVersion, nameof(neoforgedVersion));
|
NeoforgedVersion = RequireNonEmpty(neoforgedVersion, nameof(neoforgedVersion));
|
||||||
NeoforgedUrl = neoforgedUrl ?? throw new ArgumentNullException(nameof(neoforgedUrl));
|
|
||||||
|
|
||||||
if (files == null)
|
if (files == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
using CmlLib.Core.Auth;
|
||||||
|
using CmlLib.Core.Auth.Microsoft;
|
||||||
|
using Microsoft.Identity.Client;
|
||||||
|
using XboxAuthNet.Game.Msal;
|
||||||
|
using XboxAuthNet.Game.Msal.OAuth;
|
||||||
|
|
||||||
namespace AlayaCore.Services
|
namespace AlayaCore.Services
|
||||||
{
|
{
|
||||||
public class AuthService
|
public sealed class AuthService : IAuthService
|
||||||
{
|
{
|
||||||
|
private readonly IFileStore _fileStore;
|
||||||
|
|
||||||
|
private MSession? _session;
|
||||||
|
private IPublicClientApplication? _clientApp;
|
||||||
|
private JELoginHandler? _loginHandler;
|
||||||
|
|
||||||
|
public AuthService(IFileStore fileStore)
|
||||||
|
{
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> IsAuthenticatedAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
bool authenticated = _session != null && _session.CheckIsValid();
|
||||||
|
return Task.FromResult(authenticated);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AuthenticateAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
JELoginHandler loginHandler = await BuildHandlerAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_session = await loginHandler
|
||||||
|
.AuthenticateSilently(cancellationToken: cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_session = await loginHandler
|
||||||
|
.AuthenticateInteractively(cancellationToken: cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_session == null || !_session.CheckIsValid())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Authentication did not produce a valid Minecraft session.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SignOutAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
JELoginHandler loginHandler = await BuildHandlerAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
await loginHandler.Signout(cancellationToken).ConfigureAwait(false);
|
||||||
|
_session = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<MSession> GetSessionAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (_session != null && _session.CheckIsValid())
|
||||||
|
{
|
||||||
|
return _session;
|
||||||
|
}
|
||||||
|
|
||||||
|
await AuthenticateAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (_session == null || !_session.CheckIsValid())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No valid Minecraft session is available.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<JELoginHandler> BuildHandlerAsync()
|
||||||
|
{
|
||||||
|
if (_loginHandler != null)
|
||||||
|
{
|
||||||
|
return _loginHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
string accountDirectory = _fileStore.GetOrCreate(FolderLocation.Data);
|
||||||
|
string accountFilePath = Path.Combine(accountDirectory, "accounts.json");
|
||||||
|
|
||||||
|
_clientApp = await MsalClientHelper
|
||||||
|
.BuildApplicationWithCache("d91042d4-3eb5-43e4-b3ed-600e1d0760ff")
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
_loginHandler = new JELoginHandlerBuilder()
|
||||||
|
.WithOAuthProvider(new MsalCodeFlowProvider(_clientApp))
|
||||||
|
.WithAccountManager(accountFilePath)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return _loginHandler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,272 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
|
using AlayaCore.Installation;
|
||||||
|
using AlayaCore.Models;
|
||||||
|
using AlayaCore.Models.Manifests;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
using CmlLib.Core;
|
||||||
|
using CmlLib.Core.Installer.NeoForge;
|
||||||
|
using CmlLib.Core.Installer.NeoForge.Installers;
|
||||||
|
using CmlLib.Core.Installers;
|
||||||
|
using CmlLib.Core.Java;
|
||||||
|
using CmlLib.Core.VersionMetadata;
|
||||||
|
|
||||||
namespace AlayaCore.Services
|
namespace AlayaCore.Services
|
||||||
{
|
{
|
||||||
public class GameInstallService
|
public sealed class GameInstallService : IGameInstallService
|
||||||
{
|
{
|
||||||
|
private const string InstalledModsManifestFileName = "InstalledModsManifest.json";
|
||||||
|
|
||||||
|
private readonly IFileStore _fileStore;
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
|
|
||||||
|
private AlayaPath? _gamePath;
|
||||||
|
private MinecraftLauncher? _minecraftLauncher;
|
||||||
|
|
||||||
|
public GameInstallService(
|
||||||
|
IFileStore fileStore,
|
||||||
|
ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
|
_settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EnsureMinecraftInstalledAsync(
|
||||||
|
ManifestModel manifest,
|
||||||
|
InstallEnvironment environment,
|
||||||
|
CancellationToken cancellationToken = default,
|
||||||
|
EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null,
|
||||||
|
EventHandler<ByteProgress>? byteProgress = null)
|
||||||
|
{
|
||||||
|
if (manifest == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(manifest));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environment == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(environment));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(manifest.MinecraftVersion))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Minecraft version is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool alreadyInstalled =
|
||||||
|
environment.MinecraftInstalled &&
|
||||||
|
string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (alreadyInstalled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool versionMismatch =
|
||||||
|
environment.MinecraftInstalled &&
|
||||||
|
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (versionMismatch)
|
||||||
|
{
|
||||||
|
await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftLauncher launcher = GetOrCreateLauncher(minecraftProgess, byteProgress);
|
||||||
|
|
||||||
|
await launcher
|
||||||
|
.InstallAsync(manifest.MinecraftVersion, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EnsureNeoForgeInstalledAsync(
|
||||||
|
ManifestModel manifest,
|
||||||
|
InstallEnvironment environment,
|
||||||
|
CancellationToken cancellationToken = default,
|
||||||
|
IProgress<InstallerProgressChangedEventArgs>? progress = null,
|
||||||
|
IProgress<ByteProgress>? byteProgress = null)
|
||||||
|
{
|
||||||
|
if (manifest == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(manifest));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environment == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(environment));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(manifest.MinecraftVersion))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Minecraft version is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(manifest.NeoforgedVersion))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("NeoForge version is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool alreadyInstalled =
|
||||||
|
environment.NeoforgedInstalled &&
|
||||||
|
string.Equals(environment.NeoforgedVersion, manifest.NeoforgedVersion, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (alreadyInstalled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!environment.MinecraftInstalled ||
|
||||||
|
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
await EnsureMinecraftInstalledAsync(manifest, environment, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(environment.JavaPath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A valid Java installation is required before installing NeoForge.");
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftLauncher launcher = GetOrCreateLauncher();
|
||||||
|
|
||||||
|
await DownloadAndInstallNeoForgeAsync(
|
||||||
|
launcher,
|
||||||
|
manifest,
|
||||||
|
environment,
|
||||||
|
cancellationToken,
|
||||||
|
progress, byteProgress).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await launcher
|
||||||
|
.InstallAsync(manifest.MinecraftVersion, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task VerifyFilesAsync(
|
||||||
|
ManifestModel manifest,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (manifest == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(manifest));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(manifest.MinecraftVersion))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Minecraft version is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftLauncher launcher = GetOrCreateLauncher();
|
||||||
|
|
||||||
|
await launcher
|
||||||
|
.InstallAsync(manifest.MinecraftVersion, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CleanOldInstallAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
string gamePath = GetMinecraftPath();
|
||||||
|
|
||||||
|
if (Directory.Exists(gamePath))
|
||||||
|
{
|
||||||
|
Directory.Delete(gamePath, recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
string installedModsManifestPath = Path.Combine(
|
||||||
|
_fileStore.Get(FolderLocation.Manifests),
|
||||||
|
InstalledModsManifestFileName);
|
||||||
|
|
||||||
|
if (File.Exists(installedModsManifestPath))
|
||||||
|
{
|
||||||
|
File.Delete(installedModsManifestPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_gamePath = null;
|
||||||
|
_minecraftLauncher = null;
|
||||||
|
|
||||||
|
await _settingsService.UpdateLaunchVersionAsync(string.Empty, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MinecraftLauncher GetOrCreateLauncher(EventHandler<InstallerProgressChangedEventArgs>? minecraftProgess = null,
|
||||||
|
EventHandler<ByteProgress>? byteProgress = null)
|
||||||
|
{
|
||||||
|
if (_minecraftLauncher != null)
|
||||||
|
{
|
||||||
|
return _minecraftLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
|
_gamePath = new AlayaPath(_fileStore);
|
||||||
|
_minecraftLauncher = new MinecraftLauncher(_gamePath);
|
||||||
|
|
||||||
|
if(byteProgress != null)
|
||||||
|
_minecraftLauncher.ByteProgressChanged += byteProgress;
|
||||||
|
|
||||||
|
if(minecraftProgess != null)
|
||||||
|
_minecraftLauncher.FileProgressChanged += minecraftProgess;
|
||||||
|
|
||||||
|
|
||||||
|
return _minecraftLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadAndInstallNeoForgeAsync(
|
||||||
|
MinecraftLauncher launcher,
|
||||||
|
ManifestModel manifest,
|
||||||
|
InstallEnvironment environment,
|
||||||
|
CancellationToken cancellationToken,
|
||||||
|
IProgress<InstallerProgressChangedEventArgs>? progress = null,
|
||||||
|
IProgress<ByteProgress>? byteProgress = null)
|
||||||
|
{
|
||||||
|
if (launcher == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(launcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool neoForgeMismatch =
|
||||||
|
environment.NeoforgedInstalled &&
|
||||||
|
!string.Equals(environment.NeoforgedVersion, manifest.NeoforgedVersion, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (neoForgeMismatch)
|
||||||
|
{
|
||||||
|
await CleanOldInstallAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoForgeInstaller installer = new NeoForgeInstaller(launcher);
|
||||||
|
|
||||||
|
NeoForgeInstallOptions options = new NeoForgeInstallOptions
|
||||||
|
{
|
||||||
|
CancellationToken = cancellationToken,
|
||||||
|
JavaPath = environment.JavaPath,
|
||||||
|
SkipIfAlreadyInstalled = true
|
||||||
|
};
|
||||||
|
|
||||||
|
if(progress != null)
|
||||||
|
options.FileProgress = progress;
|
||||||
|
|
||||||
|
if(byteProgress != null)
|
||||||
|
options.ByteProgress = byteProgress;
|
||||||
|
|
||||||
|
string version = await installer
|
||||||
|
.Install(manifest.MinecraftVersion, manifest.NeoforgedVersion, options)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
await _settingsService
|
||||||
|
.UpdateLaunchVersionAsync(version, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetMinecraftPath()
|
||||||
|
{
|
||||||
|
return _fileStore.GetOrCreate(FolderLocation.Game);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
|
using AlayaCore.Installation;
|
||||||
|
using AlayaCore.Models;
|
||||||
|
using AlayaCore.Models.Configuration;
|
||||||
|
using AlayaCore.Models.Manifests;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
using CmlLib.Core;
|
||||||
|
using CmlLib.Core.Auth;
|
||||||
|
using CmlLib.Core.ProcessBuilder;
|
||||||
|
|
||||||
namespace AlayaCore.Services
|
namespace AlayaCore.Services
|
||||||
{
|
{
|
||||||
public class GameLaunchService
|
public sealed class GameLaunchService : IGameLaunchService
|
||||||
{
|
{
|
||||||
|
private readonly IAuthService _authService;
|
||||||
|
private readonly IFileStore _fileStore;
|
||||||
|
private readonly ILaunchDirector _director;
|
||||||
|
private readonly GameOptions _gameOptions;
|
||||||
|
|
||||||
|
private AlayaPath? _gamePath;
|
||||||
|
private MinecraftLauncher? _minecraftLauncher;
|
||||||
|
|
||||||
|
public GameLaunchService(
|
||||||
|
IAuthService authService,
|
||||||
|
IFileStore fileStore,
|
||||||
|
ILaunchDirector director,
|
||||||
|
GameOptions gameOptions)
|
||||||
|
{
|
||||||
|
_authService = authService ?? throw new ArgumentNullException(nameof(authService));
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
|
_director = director ?? throw new ArgumentNullException(nameof(director));
|
||||||
|
_gameOptions = gameOptions ?? throw new ArgumentNullException(nameof(gameOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LaunchAsync(
|
||||||
|
ManifestModel manifest,
|
||||||
|
InstallEnvironment environment,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (!_director.CanRun)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The launcher is not in a runnable state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manifest == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(manifest));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environment == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(environment));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_gameOptions.LaunchVersion))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("GameOptions.LaunchVersion is not configured.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(environment.JavaPath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A valid Java path is required to launch the game.");
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
MLaunchOption option = await BuildLaunchOptions(manifest, environment);
|
||||||
|
MinecraftLauncher launcher = GetOrCreateLauncher();
|
||||||
|
|
||||||
|
var process = await launcher
|
||||||
|
.CreateProcessAsync(_gameOptions.LaunchVersion, option)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
var processWrapper = new ProcessWrapper(process);
|
||||||
|
processWrapper.StartWithEvents();
|
||||||
|
|
||||||
|
await processWrapper.WaitForExitTaskAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MinecraftLauncher GetOrCreateLauncher()
|
||||||
|
{
|
||||||
|
if (_minecraftLauncher != null)
|
||||||
|
{
|
||||||
|
return _minecraftLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
|
_gamePath = new AlayaPath(_fileStore);
|
||||||
|
_minecraftLauncher = new MinecraftLauncher(_gamePath);
|
||||||
|
|
||||||
|
return _minecraftLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetMinecraftPath()
|
||||||
|
{
|
||||||
|
return _fileStore.GetOrCreate(FolderLocation.Game);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<MLaunchOption> BuildLaunchOptions(ManifestModel manifest, InstallEnvironment environment)
|
||||||
|
{
|
||||||
|
if (manifest.ServerUrl == null)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Manifest Server Url is not configured.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MLaunchOption
|
||||||
|
{
|
||||||
|
Session = await _authService.GetSessionAsync(),
|
||||||
|
JavaPath = environment.JavaPath,
|
||||||
|
MinimumRamMb = _gameOptions.MinimumRamMB,
|
||||||
|
MaximumRamMb = _gameOptions.MaximumRamMB,
|
||||||
|
ScreenWidth = _gameOptions.ScreenWidth,
|
||||||
|
ScreenHeight = _gameOptions.ScreenHeight,
|
||||||
|
ServerIp = manifest.ServerUrl.Host,
|
||||||
|
ServerPort = manifest.ServerPort,
|
||||||
|
DockName = "AlayaCraft"
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,13 +12,13 @@ using AlayaCore.Models.Results;
|
|||||||
|
|
||||||
namespace AlayaCore.Services
|
namespace AlayaCore.Services
|
||||||
{
|
{
|
||||||
public sealed class DownloadService : IDownloadService
|
public sealed class HttpDownloadService : IDownloadService
|
||||||
{
|
{
|
||||||
private const int BUFFER_SIZE = 81920;
|
private const int BUFFER_SIZE = 81920;
|
||||||
|
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
|
||||||
public DownloadService(IHttpClient httpClient)
|
public HttpDownloadService(IHttpClient httpClient)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
using AlayaCore.Abstractions.Interfaces.Services;
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
using AlayaCore.Installation;
|
using AlayaCore.Installation;
|
||||||
using AlayaCore.Models.Manifests;
|
using AlayaCore.Models.Manifests;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace AlayaCore.Services
|
namespace AlayaCore.Services
|
||||||
{
|
{
|
||||||
public sealed class InstallationStateService : IInstallStateService
|
public sealed class InstallationStateService : IInstallStateService
|
||||||
{
|
{
|
||||||
private const string JAVA_RUNTIME_FOLDER_NAME = "java-runtime-epsilon";
|
private const string VersionsFolderName = "versions";
|
||||||
|
|
||||||
|
private readonly IFileStore _fileStore;
|
||||||
private readonly IManifestService _manifestService;
|
private readonly IManifestService _manifestService;
|
||||||
|
|
||||||
public InstallationStateService(IManifestService manifestService)
|
public InstallationStateService(
|
||||||
|
IFileStore fileStore,
|
||||||
|
IManifestService manifestService)
|
||||||
{
|
{
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
|
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,26 +44,20 @@ namespace AlayaCore.Services
|
|||||||
javaVersion = GetJavaVersion(javaPath!);
|
javaVersion = GetJavaVersion(javaPath!);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool minecraftInstalled = IsMinecraftInstalled();
|
InstalledVersionState versionState = GetInstalledVersionState();
|
||||||
string? minecraftVersion = null;
|
|
||||||
|
|
||||||
bool neoforgeInstalled = IsNeoforgeInstalled();
|
|
||||||
string? neoforgeVersion = null;
|
|
||||||
|
|
||||||
|
|
||||||
InstalledModsManifestModel installedModsManifest =
|
InstalledModsManifestModel installedModsManifest =
|
||||||
await _manifestService.GetInstalledModsManifestAsync(cancellationToken).ConfigureAwait(false)
|
await _manifestService.GetInstalledModsManifestAsync(cancellationToken).ConfigureAwait(false);
|
||||||
?? new InstalledModsManifestModel();
|
|
||||||
|
|
||||||
return new InstallEnvironment(
|
return new InstallEnvironment(
|
||||||
osPlatform: platform,
|
osPlatform: platform,
|
||||||
javaInstalled: javaInstalled,
|
javaInstalled: javaInstalled,
|
||||||
javaPath: javaPath,
|
javaPath: javaPath,
|
||||||
javaVersion: javaVersion,
|
javaVersion: javaVersion,
|
||||||
minecraftInstalled: minecraftInstalled,
|
minecraftInstalled: !string.IsNullOrWhiteSpace(versionState.MinecraftVersion),
|
||||||
minecraftVersion: minecraftVersion,
|
minecraftVersion: versionState.MinecraftVersion,
|
||||||
neoforgedInstalled: neoforgeInstalled,
|
neoforgedInstalled: !string.IsNullOrWhiteSpace(versionState.NeoForgeVersion),
|
||||||
neoforgedVersion: neoforgeVersion,
|
neoforgedVersion: versionState.NeoForgeVersion,
|
||||||
installedModsManifest: installedModsManifest);
|
installedModsManifest: installedModsManifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,18 +133,14 @@ namespace AlayaCore.Services
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryGetJavaPath(out string? javaPath)
|
private bool TryGetJavaPath(out string? javaPath)
|
||||||
{
|
{
|
||||||
string executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
string executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||||
? "java.exe"
|
? "javaw.exe"
|
||||||
: "java";
|
: "javaw";
|
||||||
|
|
||||||
string fullPath = Path.Combine(
|
string runtimePath = _fileStore.GetOrCreate(FolderLocation.JavaRuntime);
|
||||||
AppContext.BaseDirectory,
|
string fullPath = Path.Combine(runtimePath, "bin", executableName);
|
||||||
"Java",
|
|
||||||
JAVA_RUNTIME_FOLDER_NAME,
|
|
||||||
"bin",
|
|
||||||
executableName);
|
|
||||||
|
|
||||||
if (!File.Exists(fullPath))
|
if (!File.Exists(fullPath))
|
||||||
{
|
{
|
||||||
@@ -154,14 +152,130 @@ namespace AlayaCore.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsMinecraftInstalled()
|
private InstalledVersionState GetInstalledVersionState()
|
||||||
{
|
{
|
||||||
return true;
|
string versionsPath = GetVersionsPath();
|
||||||
|
|
||||||
|
if (!Directory.Exists(versionsPath))
|
||||||
|
{
|
||||||
|
return InstalledVersionState.Empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsNeoforgeInstalled()
|
string[] versionDirectories = Directory.GetDirectories(versionsPath);
|
||||||
|
|
||||||
|
if (versionDirectories.Length == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return InstalledVersionState.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
string? minecraftVersion = null;
|
||||||
|
string? neoForgeVersion = null;
|
||||||
|
|
||||||
|
foreach (string versionDirectory in versionDirectories)
|
||||||
|
{
|
||||||
|
string? versionFolderName = Path.GetFileName(versionDirectory);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(versionFolderName))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string versionJsonPath = Path.Combine(versionDirectory, $"{versionFolderName}.json");
|
||||||
|
|
||||||
|
if (!File.Exists(versionJsonPath))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JObject versionJson = LoadJson(versionJsonPath);
|
||||||
|
|
||||||
|
string? id = versionJson.Value<string>("id");
|
||||||
|
string? inheritsFrom = versionJson.Value<string>("inheritsFrom");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(id))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNeoForgeVersion(id, inheritsFrom))
|
||||||
|
{
|
||||||
|
neoForgeVersion = id;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(minecraftVersion) && !string.IsNullOrWhiteSpace(inheritsFrom))
|
||||||
|
{
|
||||||
|
minecraftVersion = inheritsFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(minecraftVersion))
|
||||||
|
{
|
||||||
|
minecraftVersion = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InstalledVersionState(minecraftVersion, neoForgeVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetVersionsPath()
|
||||||
|
{
|
||||||
|
return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Game), VersionsFolderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNeoForgeVersion(string? id, string? inheritsFrom)
|
||||||
|
{
|
||||||
|
return ContainsNeoForgeMarker(id) || ContainsNeoForgeMarker(inheritsFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsNeoForgeMarker(string? value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.Contains("neoforge", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
value.Contains("neoforged", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JObject LoadJson(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Path cannot be null, empty, or whitespace.", nameof(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
string json = File.ReadAllText(path);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(json))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"File '{path}' was empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return JObject.Parse(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class InstalledVersionState
|
||||||
|
{
|
||||||
|
public string? MinecraftVersion { get; }
|
||||||
|
public string? NeoForgeVersion { get; }
|
||||||
|
|
||||||
|
public InstalledVersionState(string? minecraftVersion, string? neoForgeVersion)
|
||||||
|
{
|
||||||
|
MinecraftVersion = Normalize(minecraftVersion);
|
||||||
|
NeoForgeVersion = Normalize(neoForgeVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InstalledVersionState Empty()
|
||||||
|
{
|
||||||
|
return new InstalledVersionState(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? Normalize(string? value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? null : value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,30 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
using AlayaCore.Abstractions.Interfaces.Services;
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
using AlayaCore.Installation;
|
using AlayaCore.Installation;
|
||||||
using AlayaCore.Models.Manifests;
|
using AlayaCore.Models.Manifests;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
|
|
||||||
namespace AlayaCore.Services
|
namespace AlayaCore.Services
|
||||||
{
|
{
|
||||||
public sealed class JavaService : IJavaService
|
public sealed class JavaService : IJavaService
|
||||||
{
|
{
|
||||||
private const string DOWNLOAD_FILE_NAME = "java-runtime.download";
|
private const string JavaArchiveHashPlaceholder = "REPLACE_WITH_MANIFEST_HASH_SUPPORT";
|
||||||
private const string JAVA_INSTALL_FOLDER_NAME = "Java";
|
private const string BaseUrl = "https://aka.ms/download-jdk/";
|
||||||
private const string JAVA_ARCHIVE_HASH_PLACEHOLDER = "REPLACE_WITH_MANIFEST_HASH_SUPPORT";
|
|
||||||
|
|
||||||
private readonly IDownloadService _downloadService;
|
private readonly IDownloadService _downloadService;
|
||||||
|
private readonly IFileStore _fileStore;
|
||||||
|
|
||||||
public JavaService(IDownloadService downloadService)
|
public JavaService(IDownloadService downloadService, IFileStore fileStore)
|
||||||
{
|
{
|
||||||
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task EnsureValidJavaInstalledAsync(
|
public async Task EnsureValidJavaInstalledAsync(
|
||||||
@@ -38,26 +44,28 @@ namespace AlayaCore.Services
|
|||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
string installDirectory = GetJavaInstallDirectory();
|
||||||
|
|
||||||
if (environment.JavaInstalled &&
|
if (environment.JavaInstalled &&
|
||||||
|
Directory.Exists(installDirectory) &&
|
||||||
string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase))
|
string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (environment.JavaInstalled && !string.IsNullOrWhiteSpace(environment.JavaPath))
|
if (Directory.Exists(installDirectory))
|
||||||
{
|
{
|
||||||
string javaRootFolder = ResolveJavaRootFolder(environment.JavaPath);
|
await RemoveDirectoryAsync(installDirectory, cancellationToken).ConfigureAwait(false);
|
||||||
await RemoveJavaAsync(javaRootFolder, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string downloadPath = GetJavaDownloadPath();
|
Uri downloadUri = GetPlatformSpecificJavaUri(manifest.RequiredJavaVersion);
|
||||||
string installDirectory = GetJavaInstallDirectory();
|
string downloadPath = GetJavaDownloadPath(downloadUri);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(downloadPath) ?? AppContext.BaseDirectory);
|
Directory.CreateDirectory(Path.GetDirectoryName(downloadPath) ?? AppContext.BaseDirectory);
|
||||||
Directory.CreateDirectory(installDirectory);
|
Directory.CreateDirectory(installDirectory);
|
||||||
|
|
||||||
await DownloadJavaAsync(
|
await DownloadJavaAsync(
|
||||||
manifest.RequiredJavaUrl,
|
downloadUri,
|
||||||
downloadPath,
|
downloadPath,
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -67,52 +75,100 @@ namespace AlayaCore.Services
|
|||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetJavaInstallDirectory()
|
public Uri GetPlatformSpecificJavaUri(string javaVersion)
|
||||||
{
|
{
|
||||||
return Path.Combine(AppContext.BaseDirectory, JAVA_INSTALL_FOLDER_NAME);
|
if (string.IsNullOrWhiteSpace(javaVersion))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Java version must be provided.", nameof(javaVersion));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetJavaDownloadPath()
|
string os = GetOsSegment();
|
||||||
{
|
string arch = GetArchitectureSegment();
|
||||||
return Path.Combine(AppContext.BaseDirectory, "Temp", DOWNLOAD_FILE_NAME);
|
string extension = GetFileExtension(os);
|
||||||
|
|
||||||
|
string fileName = $"microsoft-jdk-{javaVersion}-{os}-{arch}.{extension}";
|
||||||
|
return new Uri($"{BaseUrl}{fileName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ResolveJavaRootFolder(string javaExecutablePath)
|
private string GetJavaInstallDirectory()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(javaExecutablePath))
|
return _fileStore.GetOrCreate(FolderLocation.Java);
|
||||||
{
|
|
||||||
throw new ArgumentException("Java executable path cannot be null, empty, or whitespace.", nameof(javaExecutablePath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string? binFolder = Path.GetDirectoryName(javaExecutablePath);
|
private string GetJavaDownloadPath(Uri downloadUri)
|
||||||
if (string.IsNullOrWhiteSpace(binFolder))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Could not resolve the Java bin directory.");
|
if (downloadUri == null)
|
||||||
}
|
|
||||||
|
|
||||||
string? javaRootFolder = Path.GetDirectoryName(binFolder);
|
|
||||||
if (string.IsNullOrWhiteSpace(javaRootFolder))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Could not resolve the Java installation directory.");
|
throw new ArgumentNullException(nameof(downloadUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
return javaRootFolder;
|
string fileName = Path.GetFileName(downloadUri.AbsolutePath);
|
||||||
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Could not determine Java archive file name from download URI.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task RemoveJavaAsync(
|
return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Downloads), fileName);
|
||||||
string oldJavaFolder,
|
}
|
||||||
|
|
||||||
|
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)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(oldJavaFolder))
|
if (string.IsNullOrWhiteSpace(directoryPath))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Old Java folder cannot be null, empty, or whitespace.", nameof(oldJavaFolder));
|
throw new ArgumentException("Directory path cannot be null, empty, or whitespace.", nameof(directoryPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
if (Directory.Exists(oldJavaFolder))
|
if (Directory.Exists(directoryPath))
|
||||||
{
|
{
|
||||||
Directory.Delete(oldJavaFolder, recursive: true);
|
Directory.Delete(directoryPath, recursive: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -140,22 +196,32 @@ namespace AlayaCore.Services
|
|||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Replace this when your manifest includes a Java archive/runtime hash.
|
string directory = Path.GetDirectoryName(destinationPath);
|
||||||
|
if (!string.IsNullOrWhiteSpace(directory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(destinationPath))
|
||||||
|
{
|
||||||
|
File.Delete(destinationPath);
|
||||||
|
}
|
||||||
|
|
||||||
await _downloadService.DownloadFileAsync(
|
await _downloadService.DownloadFileAsync(
|
||||||
javaUri,
|
javaUri,
|
||||||
destinationPath,
|
destinationPath,
|
||||||
JAVA_ARCHIVE_HASH_PLACEHOLDER,
|
JavaArchiveHashPlaceholder,
|
||||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task InstallJavaAsync(
|
private static async Task InstallJavaAsync(
|
||||||
string installerPath,
|
string archivePath,
|
||||||
string installDirectory,
|
string installDirectory,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(installerPath))
|
if (string.IsNullOrWhiteSpace(archivePath))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Installer path cannot be null, empty, or whitespace.", nameof(installerPath));
|
throw new ArgumentException("Archive path cannot be null, empty, or whitespace.", nameof(archivePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(installDirectory))
|
if (string.IsNullOrWhiteSpace(installDirectory))
|
||||||
@@ -165,23 +231,87 @@ namespace AlayaCore.Services
|
|||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
if (!File.Exists(installerPath))
|
if (!File.Exists(archivePath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException("Java installer or archive was not found.", installerPath);
|
throw new FileNotFoundException("Java archive was not found.", archivePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(installDirectory);
|
Directory.CreateDirectory(installDirectory);
|
||||||
|
|
||||||
// TODO:
|
if (archivePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||||
// Implement this based on the actual Java package format.
|
{
|
||||||
//
|
ZipFile.ExtractToDirectory(archivePath, installDirectory);
|
||||||
// Examples:
|
return;
|
||||||
// - If the file is a .zip, extract it into installDirectory
|
}
|
||||||
// - If it is a .tar.gz, unpack it appropriately
|
|
||||||
// - If it is an installer executable, launch it silently
|
if (archivePath.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
|
||||||
//
|
{
|
||||||
// For now this is intentionally left explicit rather than guessing the wrong install strategy.
|
await ExtractTarGzAsync(archivePath, installDirectory, cancellationToken).ConfigureAwait(false);
|
||||||
throw new NotImplementedException("InstallJavaAsync must be implemented for the actual Java package format.");
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,13 @@ using System.IO;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
using AlayaCore.Abstractions.Interfaces.Clients;
|
using AlayaCore.Abstractions.Interfaces.Clients;
|
||||||
using AlayaCore.Abstractions.Interfaces.Services;
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
using AlayaCore.Models.Configuration;
|
using AlayaCore.Models.Configuration;
|
||||||
using AlayaCore.Models.Manifests;
|
using AlayaCore.Models.Manifests;
|
||||||
using AlayaCore.Models.Manifests.DTO;
|
using AlayaCore.Models.Manifests.DTO;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
using AlayaCore.Utilities.Extensions;
|
using AlayaCore.Utilities.Extensions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@@ -21,15 +23,18 @@ namespace AlayaCore.Services
|
|||||||
|
|
||||||
private readonly IDownloadService _downloadService;
|
private readonly IDownloadService _downloadService;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IFileStore _fileStore;
|
||||||
private readonly ManifestServiceOptions _options;
|
private readonly ManifestServiceOptions _options;
|
||||||
|
|
||||||
public ManifestService(
|
public ManifestService(
|
||||||
IDownloadService downloadService,
|
IDownloadService downloadService,
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
|
IFileStore fileStore,
|
||||||
ManifestServiceOptions options)
|
ManifestServiceOptions options)
|
||||||
{
|
{
|
||||||
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
||||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,17 +173,17 @@ namespace AlayaCore.Services
|
|||||||
|
|
||||||
public string GetLauncherManifestPath()
|
public string GetLauncherManifestPath()
|
||||||
{
|
{
|
||||||
return Path.Combine(_options.ManifestDirectoryPath, LAUNCHER_MANIFEST_FILE_NAME);
|
return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Manifests), LAUNCHER_MANIFEST_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetCoreManifestPath()
|
public string GetCoreManifestPath()
|
||||||
{
|
{
|
||||||
return Path.Combine(_options.ManifestDirectoryPath, CORE_MANIFEST_FILE_NAME);
|
return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Manifests), CORE_MANIFEST_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetInstalledModsManifestPath()
|
public string GetInstalledModsManifestPath()
|
||||||
{
|
{
|
||||||
return Path.Combine(_options.ManifestDirectoryPath, INSTALLED_MODS_MANIFEST_FILE_NAME);
|
return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Manifests), INSTALLED_MODS_MANIFEST_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TModel?> LoadLocalManifestAsync<TDto, TModel>(
|
private async Task<TModel?> LoadLocalManifestAsync<TDto, TModel>(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
using AlayaCore.Abstractions.Interfaces.Clients;
|
using AlayaCore.Abstractions.Interfaces.Clients;
|
||||||
using AlayaCore.Abstractions.Interfaces.Services;
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
using AlayaCore.Installation;
|
using AlayaCore.Installation;
|
||||||
@@ -21,23 +22,23 @@ namespace AlayaCore.Services
|
|||||||
{
|
{
|
||||||
public sealed class ModService : IModService
|
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 IDownloadService _downloadService;
|
||||||
private readonly ModrinthConnectionOptions _options;
|
private readonly ModrinthConnectionOptions _options;
|
||||||
private readonly ManifestServiceOptions _manifestOptions;
|
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IFileStore _fileStore;
|
||||||
|
|
||||||
public ModService(
|
public ModService(
|
||||||
IDownloadService downloadService,
|
IDownloadService downloadService,
|
||||||
ModrinthConnectionOptions options,
|
ModrinthConnectionOptions options,
|
||||||
ManifestServiceOptions manifestOptions,
|
IHttpClient httpClient,
|
||||||
IHttpClient httpClient)
|
IFileStore fileStore)
|
||||||
{
|
{
|
||||||
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
_downloadService = downloadService ?? throw new ArgumentNullException(nameof(downloadService));
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
_manifestOptions = manifestOptions ?? throw new ArgumentNullException(nameof(manifestOptions));
|
|
||||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ProcessModsAsync(
|
public async Task ProcessModsAsync(
|
||||||
@@ -238,7 +239,7 @@ namespace AlayaCore.Services
|
|||||||
return $"{baseUrl}/version_file/{sha512Hash}";
|
return $"{baseUrl}/version_file/{sha512Hash}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetModDestinationPath(ModFileEntry fileEntry)
|
private string GetModDestinationPath(ModFileEntry fileEntry)
|
||||||
{
|
{
|
||||||
if (fileEntry == null)
|
if (fileEntry == null)
|
||||||
{
|
{
|
||||||
@@ -251,17 +252,16 @@ namespace AlayaCore.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
string modsDirectory = GetModsDirectoryPath();
|
string modsDirectory = GetModsDirectoryPath();
|
||||||
Directory.CreateDirectory(modsDirectory);
|
|
||||||
|
|
||||||
return Path.Combine(modsDirectory, fileEntry.FileName);
|
return Path.Combine(modsDirectory, fileEntry.FileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetModsDirectoryPath()
|
private string GetModsDirectoryPath()
|
||||||
{
|
{
|
||||||
return Path.Combine(AppContext.BaseDirectory, "Game", "mods");
|
return _fileStore.GetOrCreate(FolderLocation.Mods);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RemoveStaleMods(IEnumerable<ModFileEntry> requiredMods)
|
private void RemoveStaleMods(IEnumerable<ModFileEntry> requiredMods)
|
||||||
{
|
{
|
||||||
if (requiredMods == null)
|
if (requiredMods == null)
|
||||||
{
|
{
|
||||||
@@ -269,6 +269,7 @@ namespace AlayaCore.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
string modsDirectory = GetModsDirectoryPath();
|
string modsDirectory = GetModsDirectoryPath();
|
||||||
|
|
||||||
if (!Directory.Exists(modsDirectory))
|
if (!Directory.Exists(modsDirectory))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -302,10 +303,9 @@ namespace AlayaCore.Services
|
|||||||
List<ModFileEntry> entries = installedMods.ToList();
|
List<ModFileEntry> entries = installedMods.ToList();
|
||||||
InstalledModsManifestModel manifest = new InstalledModsManifestModel(entries);
|
InstalledModsManifestModel manifest = new InstalledModsManifestModel(entries);
|
||||||
|
|
||||||
string manifestsDirectory = _manifestOptions.ManifestDirectoryPath;
|
string manifestsDirectory = _fileStore.GetOrCreate(FolderLocation.Manifests);
|
||||||
Directory.CreateDirectory(manifestsDirectory);
|
|
||||||
|
|
||||||
string manifestPath = Path.Combine(manifestsDirectory, InstalledModsManifestFileName);
|
string manifestPath = Path.Combine(manifestsDirectory, INSTALLED_MODS_MANIFEST_FILE_NAME);
|
||||||
string temporaryManifestPath = manifestPath + ".tmp";
|
string temporaryManifestPath = manifestPath + ".tmp";
|
||||||
|
|
||||||
InstalledModsManifestDto dto = manifest.ToDto();
|
InstalledModsManifestDto dto = manifest.ToDto();
|
||||||
|
|||||||
@@ -2,21 +2,30 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AlayaCore.Abstractions.Configuration;
|
||||||
|
using AlayaCore.Abstractions.Interfaces;
|
||||||
using AlayaCore.Abstractions.Interfaces.Services;
|
using AlayaCore.Abstractions.Interfaces.Services;
|
||||||
using AlayaCore.Models.Configuration;
|
using AlayaCore.Models.Configuration;
|
||||||
|
using AlayaCore.Utilities.Enums;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace AlayaCore.Services
|
namespace AlayaCore.Services
|
||||||
{
|
{
|
||||||
public sealed class SettingsService : ISettingsService
|
public sealed class SettingsService : ISettingsService
|
||||||
{
|
{
|
||||||
private const string LauncherSettingsFileName = "Launcher.json";
|
private readonly IFileStore _fileStore;
|
||||||
|
|
||||||
public LauncherOptions LauncherOptions { get; }
|
public LauncherOptions LauncherOptions { get; }
|
||||||
|
public GameOptions GameOptions { get; }
|
||||||
|
|
||||||
public SettingsService(LauncherOptions launcherOptions)
|
public SettingsService(
|
||||||
|
LauncherOptions launcherOptions,
|
||||||
|
GameOptions gameOptions,
|
||||||
|
IFileStore fileStore)
|
||||||
{
|
{
|
||||||
LauncherOptions = launcherOptions ?? throw new ArgumentNullException(nameof(launcherOptions));
|
LauncherOptions = launcherOptions ?? throw new ArgumentNullException(nameof(launcherOptions));
|
||||||
|
GameOptions = gameOptions ?? throw new ArgumentNullException(nameof(gameOptions));
|
||||||
|
_fileStore = fileStore ?? throw new ArgumentNullException(nameof(fileStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetForceReinstallAsync(bool value, CancellationToken cancellationToken = default)
|
public async Task SetForceReinstallAsync(bool value, CancellationToken cancellationToken = default)
|
||||||
@@ -25,10 +34,16 @@ namespace AlayaCore.Services
|
|||||||
await SaveLauncherOptionsAsync(cancellationToken).ConfigureAwait(false);
|
await SaveLauncherOptionsAsync(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdateLaunchVersionAsync(string newVersion, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
GameOptions.LaunchVersion = newVersion ?? string.Empty;
|
||||||
|
await SaveGameOptionsAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SaveLauncherOptionsAsync(CancellationToken cancellationToken = default)
|
public async Task SaveLauncherOptionsAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await SaveAsync(
|
await SaveAsync(
|
||||||
LauncherSettingsFileName,
|
LauncherOptions.FileName,
|
||||||
LauncherOptions,
|
LauncherOptions,
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -36,7 +51,7 @@ namespace AlayaCore.Services
|
|||||||
public async Task LoadLauncherOptionsAsync(CancellationToken cancellationToken = default)
|
public async Task LoadLauncherOptionsAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
LauncherOptions? loadedOptions = await LoadAsync<LauncherOptions>(
|
LauncherOptions? loadedOptions = await LoadAsync<LauncherOptions>(
|
||||||
LauncherSettingsFileName,
|
LauncherOptions.FileName,
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (loadedOptions == null)
|
if (loadedOptions == null)
|
||||||
@@ -47,10 +62,49 @@ namespace AlayaCore.Services
|
|||||||
LauncherOptions.ForceReinstall = loadedOptions.ForceReinstall;
|
LauncherOptions.ForceReinstall = loadedOptions.ForceReinstall;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SaveGameOptionsAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await SaveAsync(
|
||||||
|
GameOptions.FileName,
|
||||||
|
GameOptions,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadGameOptionsAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
GameOptions? loadedOptions = await LoadAsync<GameOptions>(
|
||||||
|
GameOptions.FileName,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (loadedOptions == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameOptions.LaunchVersion = loadedOptions.LaunchVersion;
|
||||||
|
GameOptions.MinimumRamMB = loadedOptions.MinimumRamMB;
|
||||||
|
GameOptions.MaximumRamMB = loadedOptions.MaximumRamMB;
|
||||||
|
GameOptions.ScreenWidth = loadedOptions.ScreenWidth;
|
||||||
|
GameOptions.ScreenHeight = loadedOptions.ScreenHeight;
|
||||||
|
GameOptions.Fullscreen = loadedOptions.Fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadAllAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await LoadLauncherOptionsAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
await LoadGameOptionsAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveAllAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await SaveLauncherOptionsAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
await SaveGameOptionsAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveAsync<T>(
|
private async Task SaveAsync<T>(
|
||||||
string fileName,
|
string fileName,
|
||||||
T value,
|
T value,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken) where T : BaseConfig
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(fileName))
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
{
|
{
|
||||||
@@ -89,7 +143,7 @@ namespace AlayaCore.Services
|
|||||||
|
|
||||||
private async Task<T?> LoadAsync<T>(
|
private async Task<T?> LoadAsync<T>(
|
||||||
string fileName,
|
string fileName,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken) where T : BaseConfig
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(fileName))
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
{
|
{
|
||||||
@@ -124,9 +178,9 @@ namespace AlayaCore.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetFullPath(string fileName)
|
private string GetFullPath(string fileName)
|
||||||
{
|
{
|
||||||
return Path.Combine(AppContext.BaseDirectory, "Config", fileName);
|
return Path.Combine(_fileStore.GetOrCreate(FolderLocation.Config), fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace AlayaCore.States
|
|||||||
{
|
{
|
||||||
Ready,
|
Ready,
|
||||||
LauncherNeedsUpdate,
|
LauncherNeedsUpdate,
|
||||||
|
NeedAuthenticating,
|
||||||
InstallJava,
|
InstallJava,
|
||||||
InstallMinecraft,
|
InstallMinecraft,
|
||||||
InstallNeoforge,
|
InstallNeoforge,
|
||||||
@@ -17,35 +18,46 @@ namespace AlayaCore.States
|
|||||||
public bool MinecraftNeedsInstallOrUpdate { get; }
|
public bool MinecraftNeedsInstallOrUpdate { get; }
|
||||||
public bool NeoforgeNeedsInstallOrUpdate { get; }
|
public bool NeoforgeNeedsInstallOrUpdate { get; }
|
||||||
public bool ModsNeedSync { get; }
|
public bool ModsNeedSync { get; }
|
||||||
|
public bool NeedAuthenticating { get; }
|
||||||
|
|
||||||
public LaunchState State => ComputeState();
|
public LaunchState State => ComputeState();
|
||||||
|
|
||||||
public bool CanRun =>
|
public bool CanRun => State == LaunchState.Ready;
|
||||||
State == LaunchState.Ready;
|
|
||||||
|
|
||||||
public bool NeedsUpdating =>
|
public bool NeedsUpdating =>
|
||||||
State != LaunchState.Ready;
|
LauncherNeedsUpdate ||
|
||||||
|
JavaNeedsInstallOrUpdate ||
|
||||||
|
MinecraftNeedsInstallOrUpdate ||
|
||||||
|
NeoforgeNeedsInstallOrUpdate ||
|
||||||
|
ModsNeedSync;
|
||||||
|
|
||||||
|
public bool NeedsAttention =>
|
||||||
|
NeedsUpdating || NeedAuthenticating;
|
||||||
|
|
||||||
public LaunchPlan(
|
public LaunchPlan(
|
||||||
bool launcherNeedsUpdate,
|
bool launcherNeedsUpdate,
|
||||||
bool javaNeedsInstallOrUpdate,
|
bool javaNeedsInstallOrUpdate,
|
||||||
bool minecraftNeedsInstallOrUpdate,
|
bool minecraftNeedsInstallOrUpdate,
|
||||||
bool neoforgeNeedsInstallOrUpdate,
|
bool neoforgeNeedsInstallOrUpdate,
|
||||||
bool modsNeedSync)
|
bool modsNeedSync,
|
||||||
|
bool needAuthenticating)
|
||||||
{
|
{
|
||||||
LauncherNeedsUpdate = launcherNeedsUpdate;
|
LauncherNeedsUpdate = launcherNeedsUpdate;
|
||||||
JavaNeedsInstallOrUpdate = javaNeedsInstallOrUpdate;
|
JavaNeedsInstallOrUpdate = javaNeedsInstallOrUpdate;
|
||||||
MinecraftNeedsInstallOrUpdate = minecraftNeedsInstallOrUpdate;
|
MinecraftNeedsInstallOrUpdate = minecraftNeedsInstallOrUpdate;
|
||||||
NeoforgeNeedsInstallOrUpdate = neoforgeNeedsInstallOrUpdate;
|
NeoforgeNeedsInstallOrUpdate = neoforgeNeedsInstallOrUpdate;
|
||||||
ModsNeedSync = modsNeedSync;
|
ModsNeedSync = modsNeedSync;
|
||||||
|
NeedAuthenticating = needAuthenticating;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LaunchState ComputeState()
|
private LaunchState ComputeState()
|
||||||
{
|
{
|
||||||
// Priority order matters a LOT here
|
|
||||||
if (LauncherNeedsUpdate)
|
if (LauncherNeedsUpdate)
|
||||||
return LaunchState.LauncherNeedsUpdate;
|
return LaunchState.LauncherNeedsUpdate;
|
||||||
|
|
||||||
|
if (NeedAuthenticating)
|
||||||
|
return LaunchState.NeedAuthenticating;
|
||||||
|
|
||||||
if (JavaNeedsInstallOrUpdate)
|
if (JavaNeedsInstallOrUpdate)
|
||||||
return LaunchState.InstallJava;
|
return LaunchState.InstallJava;
|
||||||
|
|
||||||
@@ -68,7 +80,8 @@ namespace AlayaCore.States
|
|||||||
javaNeedsInstallOrUpdate: false,
|
javaNeedsInstallOrUpdate: false,
|
||||||
minecraftNeedsInstallOrUpdate: false,
|
minecraftNeedsInstallOrUpdate: false,
|
||||||
neoforgeNeedsInstallOrUpdate: false,
|
neoforgeNeedsInstallOrUpdate: false,
|
||||||
modsNeedSync: false);
|
modsNeedSync: false,
|
||||||
|
needAuthenticating: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace AlayaCore.Utilities.Converters
|
namespace AlayaCore.Utilities.Converters
|
||||||
@@ -12,9 +11,9 @@ namespace AlayaCore.Utilities.Converters
|
|||||||
{
|
{
|
||||||
writer.WriteNull();
|
writer.WriteNull();
|
||||||
}
|
}
|
||||||
else if (value is Uri)
|
else if (value is Uri uri)
|
||||||
{
|
{
|
||||||
writer.WriteValue(((Uri)value).AbsoluteUri);
|
writer.WriteValue(uri.AbsoluteUri);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -33,8 +32,7 @@ namespace AlayaCore.Utilities.Converters
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Uri uri = new Uri((string)reader.Value!);
|
return new Uri((string)reader.Value!, UriKind.Absolute);
|
||||||
return uri;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -47,7 +45,7 @@ namespace AlayaCore.Utilities.Converters
|
|||||||
|
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType == typeof(Uri);
|
return typeof(Uri).IsAssignableFrom(objectType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,17 @@
|
|||||||
namespace AlayaCore.Utilities.Enums
|
namespace AlayaCore.Utilities.Enums
|
||||||
{
|
{
|
||||||
public class FolderLocation
|
public enum FolderLocation
|
||||||
{
|
{
|
||||||
|
BaseDirectory,
|
||||||
|
Java,
|
||||||
|
JavaRuntime,
|
||||||
|
Game,
|
||||||
|
Mods,
|
||||||
|
ResourcePacks,
|
||||||
|
Config,
|
||||||
|
Downloads,
|
||||||
|
Manifests,
|
||||||
|
Plugins,
|
||||||
|
Data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,9 +50,9 @@ namespace AlayaCore.Utilities.Extensions
|
|||||||
dto.RequiredJavaVersion,
|
dto.RequiredJavaVersion,
|
||||||
dto.RequiredJavaUrl,
|
dto.RequiredJavaUrl,
|
||||||
dto.MinecraftVersion,
|
dto.MinecraftVersion,
|
||||||
dto.MinecraftUrl,
|
|
||||||
dto.NeoforgedVersion,
|
dto.NeoforgedVersion,
|
||||||
dto.NeoforgedUrl,
|
dto.ServerUrl,
|
||||||
|
dto.ServerPort,
|
||||||
dto.Files?.Select(file => file.ToModel()) ?? Array.Empty<ModFileEntry>());
|
dto.Files?.Select(file => file.ToModel()) ?? Array.Empty<ModFileEntry>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,11 +81,9 @@ namespace AlayaCore.Utilities.Extensions
|
|||||||
{
|
{
|
||||||
AlayaVersion = model.AlayaVersion,
|
AlayaVersion = model.AlayaVersion,
|
||||||
RequiredJavaVersion = model.RequiredJavaVersion,
|
RequiredJavaVersion = model.RequiredJavaVersion,
|
||||||
RequiredJavaUrl = model.RequiredJavaUrl,
|
RequiredJavaUrl = model.RequiredJavaBaseUrl,
|
||||||
MinecraftVersion = model.MinecraftVersion,
|
MinecraftVersion = model.MinecraftVersion,
|
||||||
MinecraftUrl = model.MinecraftUrl,
|
|
||||||
NeoforgedVersion = model.NeoforgedVersion,
|
NeoforgedVersion = model.NeoforgedVersion,
|
||||||
NeoforgedUrl = model.NeoforgedUrl,
|
|
||||||
Files = model.Files.Select(file => file.ToDto()).ToList()
|
Files = model.Files.Select(file => file.ToDto()).ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,86 @@ using AlayaCore.Utilities.Enums;
|
|||||||
|
|
||||||
namespace AlayaCore.Utilities.Stores
|
namespace AlayaCore.Utilities.Stores
|
||||||
{
|
{
|
||||||
public class FileStore : IFileStore
|
public sealed class LocalFileStore : IFileStore
|
||||||
{
|
{
|
||||||
private static readonly string _baseDirectory = AppContext.BaseDirectory;
|
private static readonly string BaseDirectoryPath = AppContext.BaseDirectory;
|
||||||
private static readonly string _javaDirectory = Path.Combine(_baseDirectory, "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 GameDirectoryPath = Path.Combine(BaseDirectoryPath, "Game");
|
||||||
|
private static readonly string ModsDirectoryPath = Path.Combine(GameDirectoryPath, "mods");
|
||||||
|
private static readonly string ResourcePacksDirectoryPath = Path.Combine(GameDirectoryPath, "resourcepacks");
|
||||||
|
private static readonly string ConfigDirectoryPath = Path.Combine(BaseDirectoryPath, "Config");
|
||||||
|
private static readonly string DownloadsDirectoryPath = Path.Combine(BaseDirectoryPath, "Temp");
|
||||||
|
private static readonly string ManifestsDirectoryPath = Path.Combine(BaseDirectoryPath, "Manifests");
|
||||||
|
private static readonly string PluginsDirectoryPath = Path.Combine(GameDirectoryPath, "plugins");
|
||||||
|
private static readonly string DataDirectoryPath = Path.Combine(BaseDirectoryPath, "Data");
|
||||||
|
|
||||||
private static readonly Dictionary<FolderLocation, string> _folders = new()
|
private static readonly IReadOnlyDictionary<FolderLocation, string> Folders =
|
||||||
|
new Dictionary<FolderLocation, string>
|
||||||
{
|
{
|
||||||
{ FolderLocation.BaseDirectory, _baseDirectory },
|
{ FolderLocation.BaseDirectory, BaseDirectoryPath },
|
||||||
{ FolderLocation.Java, _javaDirectory}
|
{ FolderLocation.Java, JavaDirectoryPath },
|
||||||
|
{ FolderLocation.JavaRuntime, JavaRuntimeDirectoryPath },
|
||||||
|
{ FolderLocation.Game, GameDirectoryPath },
|
||||||
|
{ FolderLocation.Mods, ModsDirectoryPath },
|
||||||
|
{ FolderLocation.ResourcePacks, ResourcePacksDirectoryPath },
|
||||||
|
{ FolderLocation.Config, ConfigDirectoryPath },
|
||||||
|
{ FolderLocation.Downloads, DownloadsDirectoryPath },
|
||||||
|
{ FolderLocation.Manifests, ManifestsDirectoryPath},
|
||||||
|
{ FolderLocation.Plugins, PluginsDirectoryPath },
|
||||||
|
{ FolderLocation.Data, DataDirectoryPath}
|
||||||
};
|
};
|
||||||
|
|
||||||
public string Get(FolderLocation location)
|
public string Get(FolderLocation location)
|
||||||
{
|
{
|
||||||
return _folders[location];
|
if (!Folders.TryGetValue(location, out string? path))
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(location), location, "Unknown folder location.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetOrCreate(FolderLocation location)
|
||||||
|
{
|
||||||
|
string path = Get(location);
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Combine(FolderLocation location, params string[] paths)
|
||||||
|
{
|
||||||
|
if (paths == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(paths));
|
||||||
|
}
|
||||||
|
|
||||||
|
string rootPath = Get(location);
|
||||||
|
|
||||||
|
if (paths.Length == 0)
|
||||||
|
{
|
||||||
|
return rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] combinedPaths = new string[paths.Length + 1];
|
||||||
|
combinedPaths[0] = rootPath;
|
||||||
|
|
||||||
|
for (int i = 0; i < paths.Length; i++)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(paths[i]))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Path segments cannot be null, empty, or whitespace.", nameof(paths));
|
||||||
|
}
|
||||||
|
|
||||||
|
combinedPaths[i + 1] = paths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.Combine(combinedPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(FolderLocation location)
|
||||||
|
{
|
||||||
|
return Directory.Exists(Get(location));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,9 +44,25 @@
|
|||||||
"netstandard2.1": {
|
"netstandard2.1": {
|
||||||
"targetAlias": "netstandard2.1",
|
"targetAlias": "netstandard2.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"CmlLib.Core": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[4.0.6, )"
|
||||||
|
},
|
||||||
|
"CmlLib.Core.Auth.Microsoft": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[3.3.1, )"
|
||||||
|
},
|
||||||
|
"CmlLib.Core.Installer.NeoForge": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[4.0.0, )"
|
||||||
|
},
|
||||||
"Newtonsoft.Json": {
|
"Newtonsoft.Json": {
|
||||||
"target": "Package",
|
"target": "Package",
|
||||||
"version": "[13.0.4, )"
|
"version": "[13.0.4, )"
|
||||||
|
},
|
||||||
|
"XboxAuthNet.Game.Msal": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[0.1.3, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"imports": [
|
"imports": [
|
||||||
|
|||||||
@@ -1,2 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<Import Project="$(NuGetPackageRoot)system.text.json/8.0.5/buildTransitive/netstandard2.0/System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json/8.0.5/buildTransitive/netstandard2.0/System.Text.Json.targets')" />
|
||||||
|
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions/8.0.1/buildTransitive/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions/8.0.1/buildTransitive/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("AlayaCore")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("AlayaCore")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+823ccf4b877ddf1fc1293f72c6b704d6a449ddaa")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("AlayaCore")]
|
[assembly: System.Reflection.AssemblyProductAttribute("AlayaCore")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("AlayaCore")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("AlayaCore")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
02d72db7e7301cd87568f68f39a003b2484df3c699c3ceed1d9bd04f25f6e3dd
|
c63b3bf6f0fd998ece1d911c58e61eb584840e2e19d535b62f67f54b1510e49e
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,40 @@
|
|||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"dgSpecHash": "iqHG7z//q/I=",
|
"dgSpecHash": "pXFuC0zr6Zg=",
|
||||||
"success": true,
|
"success": true,
|
||||||
"projectFilePath": "/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/AlayaCore.csproj",
|
"projectFilePath": "/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/AlayaCore.csproj",
|
||||||
"expectedPackageFiles": [
|
"expectedPackageFiles": [
|
||||||
|
"/Users/ryanmacham/.nuget/packages/cmllib.core/4.0.6/cmllib.core.4.0.6.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/cmllib.core.auth.microsoft/3.3.1/cmllib.core.auth.microsoft.3.3.1.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/cmllib.core.commons/4.0.0/cmllib.core.commons.4.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/cmllib.core.installer.neoforge/4.0.0/cmllib.core.installer.neoforge.4.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/htmlagilitypack/1.11.48/htmlagilitypack.1.11.48.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/lzma-sdk/19.0.0/lzma-sdk.19.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/microsoft.bcl.asyncinterfaces/8.0.0/microsoft.bcl.asyncinterfaces.8.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/microsoft.extensions.dependencyinjection.abstractions/8.0.1/microsoft.extensions.dependencyinjection.abstractions.8.0.1.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/microsoft.extensions.logging.abstractions/8.0.1/microsoft.extensions.logging.abstractions.8.0.1.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/microsoft.identity.client/4.61.3/microsoft.identity.client.4.61.3.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/microsoft.identity.client.extensions.msal/4.61.3/microsoft.identity.client.extensions.msal.4.61.3.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/microsoft.identitymodel.abstractions/6.35.0/microsoft.identitymodel.abstractions.6.35.0.nupkg.sha512",
|
||||||
"/Users/ryanmacham/.nuget/packages/newtonsoft.json/13.0.4/newtonsoft.json.13.0.4.nupkg.sha512",
|
"/Users/ryanmacham/.nuget/packages/newtonsoft.json/13.0.4/newtonsoft.json.13.0.4.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/sharpziplib/1.4.2/sharpziplib.1.4.2.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.buffers/4.5.1/system.buffers.4.5.1.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.diagnostics.diagnosticsource/6.0.1/system.diagnostics.diagnosticsource.6.0.1.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.io.filesystem.accesscontrol/5.0.0/system.io.filesystem.accesscontrol.5.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.memory/4.5.5/system.memory.4.5.5.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.net.http.json/8.0.0/system.net.http.json.8.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.numerics.vectors/4.4.0/system.numerics.vectors.4.4.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.runtime.compilerservices.unsafe/6.0.0/system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.security.accesscontrol/5.0.0/system.security.accesscontrol.5.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.security.cryptography.protecteddata/4.5.0/system.security.cryptography.protecteddata.4.5.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.security.principal.windows/5.0.0/system.security.principal.windows.5.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.text.encodings.web/8.0.0/system.text.encodings.web.8.0.0.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.text.json/8.0.5/system.text.json.8.0.5.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.threading.tasks.dataflow/8.0.1/system.threading.tasks.dataflow.8.0.1.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/system.threading.tasks.extensions/4.5.4/system.threading.tasks.extensions.4.5.4.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/xboxauthnet/3.0.4/xboxauthnet.3.0.4.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/xboxauthnet.game/1.4.1/xboxauthnet.game.1.4.1.nupkg.sha512",
|
||||||
|
"/Users/ryanmacham/.nuget/packages/xboxauthnet.game.msal/0.1.3/xboxauthnet.game.msal.0.1.3.nupkg.sha512",
|
||||||
"/Users/ryanmacham/.nuget/packages/netstandard.library.ref/2.1.0/netstandard.library.ref.2.1.0.nupkg.sha512"
|
"/Users/ryanmacham/.nuget/packages/netstandard.library.ref/2.1.0/netstandard.library.ref.2.1.0.nupkg.sha512"
|
||||||
],
|
],
|
||||||
"logs": []
|
"logs": []
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
"restore":{"projectUniqueName":"/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/AlayaCore.csproj","projectName":"AlayaCore","projectPath":"/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/AlayaCore.csproj","packagesPath":"","outputPath":"/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/obj/","projectStyle":"PackageReference","originalTargetFrameworks":["netstandard2.1"],"sources":{"https://api.nuget.org/v3/index.json":{}},"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"},"SdkAnalysisLevel":"10.0.200"}"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","dependencies":{"Newtonsoft.Json":{"target":"Package","version":"[13.0.4, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"downloadDependencies":[{"name":"NETStandard.Library.Ref","version":"[2.1.0, 2.1.0]"}],"frameworkReferences":{"NETStandard.Library":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"/usr/local/share/dotnet/sdk/10.0.201/RuntimeIdentifierGraph.json"}}
|
"restore":{"projectUniqueName":"/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/AlayaCore.csproj","projectName":"AlayaCore","projectPath":"/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/AlayaCore.csproj","packagesPath":"","outputPath":"/Users/ryanmacham/Documents/Coding/AlayaCore/AlayaCore/obj/","projectStyle":"PackageReference","originalTargetFrameworks":["netstandard2.1"],"sources":{"https://api.nuget.org/v3/index.json":{}},"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"},"SdkAnalysisLevel":"10.0.200"}"frameworks":{"netstandard2.1":{"targetAlias":"netstandard2.1","dependencies":{"CmlLib.Core":{"target":"Package","version":"[4.0.6, )"},"CmlLib.Core.Auth.Microsoft":{"target":"Package","version":"[3.3.1, )"},"CmlLib.Core.Installer.NeoForge":{"target":"Package","version":"[4.0.0, )"},"Newtonsoft.Json":{"target":"Package","version":"[13.0.4, )"},"XboxAuthNet.Game.Msal":{"target":"Package","version":"[0.1.3, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"downloadDependencies":[{"name":"NETStandard.Library.Ref","version":"[2.1.0, 2.1.0]"}],"frameworkReferences":{"NETStandard.Library":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"/usr/local/share/dotnet/sdk/10.0.201/RuntimeIdentifierGraph.json"}}
|
||||||
@@ -1 +1 @@
|
|||||||
17752277078247393
|
17754069520908862
|
||||||
@@ -1 +1 @@
|
|||||||
17752277078247393
|
17754069520908862
|
||||||
Reference in New Issue
Block a user