- Updated ModService->Resolve... to return Uri. - Removed ModrinthModInfoModel as no longer needed.
294 lines
11 KiB
C#
294 lines
11 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using AlayaCore.Abstractions.Interfaces;
|
|
using AlayaCore.Abstractions.Interfaces.Services;
|
|
using AlayaCore.Installation;
|
|
using AlayaCore.Models.Configuration;
|
|
using AlayaCore.Models.Manifests;
|
|
using AlayaCore.States;
|
|
|
|
namespace AlayaCore
|
|
{
|
|
public sealed class LaunchDirector : ILaunchDirector
|
|
{
|
|
private readonly IManifestService _manifestService;
|
|
private readonly IUpdateService _updateService;
|
|
private readonly IInstallStateService _installStateService;
|
|
private readonly IJavaService _javaService;
|
|
private readonly IModService _modService;
|
|
private readonly IGameLaunchService _gameLaunchService;
|
|
private readonly LauncherOptions _options;
|
|
|
|
public bool CanRun { get; private set; }
|
|
|
|
public bool NeedsUpdating { get; private set; }
|
|
|
|
public LaunchPlan? CurrentPlan { get; private set; }
|
|
|
|
public LaunchDirector(
|
|
IManifestService manifestService,
|
|
IUpdateService updateService,
|
|
IInstallStateService installStateService,
|
|
IJavaService javaService,
|
|
IModService modService,
|
|
IGameLaunchService gameLaunchService,
|
|
LauncherOptions options)
|
|
{
|
|
_manifestService = manifestService ?? throw new ArgumentNullException(nameof(manifestService));
|
|
_updateService = updateService ?? throw new ArgumentNullException(nameof(updateService));
|
|
_installStateService = installStateService ?? throw new ArgumentNullException(nameof(installStateService));
|
|
_javaService = javaService ?? throw new ArgumentNullException(nameof(javaService));
|
|
_modService = modService ?? throw new ArgumentNullException(nameof(modService));
|
|
_gameLaunchService = gameLaunchService ?? throw new ArgumentNullException(nameof(gameLaunchService));
|
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
}
|
|
|
|
public async Task<LaunchPlan> EvaluateAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
bool launcherNeedsUpdate = await _updateService
|
|
.DoesLauncherNeedUpdating(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
if (launcherNeedsUpdate)
|
|
{
|
|
LaunchPlan launcherUpdatePlan = new LaunchPlan(
|
|
launcherNeedsUpdate: true,
|
|
javaNeedsInstallOrUpdate: false,
|
|
minecraftNeedsInstallOrUpdate: false,
|
|
neoforgeNeedsInstallOrUpdate: false,
|
|
modsNeedSync: false);
|
|
|
|
ApplyPlan(launcherUpdatePlan);
|
|
return launcherUpdatePlan;
|
|
}
|
|
|
|
ManifestModel manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
InstallEnvironment environment = await _installStateService
|
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
bool javaNeedsInstallOrUpdate =
|
|
_options.ForceReinstall ||
|
|
!environment.JavaInstalled ||
|
|
!string.Equals(environment.JavaVersion, manifest.RequiredJavaVersion, StringComparison.OrdinalIgnoreCase);
|
|
|
|
bool minecraftNeedsInstallOrUpdate =
|
|
_options.ForceReinstall ||
|
|
!environment.MinecraftInstalled ||
|
|
!string.Equals(environment.MinecraftVersion, manifest.MinecraftVersion, StringComparison.OrdinalIgnoreCase);
|
|
|
|
bool neoforgeNeedsInstallOrUpdate =
|
|
_options.ForceReinstall ||
|
|
!environment.NeoforgedInstalled ||
|
|
!string.Equals(environment.NeoforgedVersion, manifest.NeoforgedVersion, StringComparison.OrdinalIgnoreCase);
|
|
|
|
bool modsNeedSync =
|
|
_options.ForceReinstall ||
|
|
DoModsNeedSync(manifest, environment);
|
|
|
|
LaunchPlan plan = new LaunchPlan(
|
|
launcherNeedsUpdate: false,
|
|
javaNeedsInstallOrUpdate: javaNeedsInstallOrUpdate,
|
|
minecraftNeedsInstallOrUpdate: minecraftNeedsInstallOrUpdate,
|
|
neoforgeNeedsInstallOrUpdate: neoforgeNeedsInstallOrUpdate,
|
|
modsNeedSync: modsNeedSync);
|
|
|
|
ApplyPlan(plan);
|
|
return plan;
|
|
}
|
|
|
|
public async Task InstallOrUpdateAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
LaunchPlan plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
while (!plan.CanRun)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
ManifestModel? manifest = null;
|
|
InstallEnvironment? environment = null;
|
|
|
|
switch (plan.State)
|
|
{
|
|
case LaunchState.LauncherNeedsUpdate:
|
|
{
|
|
LauncherManifestModel launcherManifest = await _manifestService
|
|
.GetLauncherManifestAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
await _updateService
|
|
.LaunchUpdaterAsync(launcherManifest, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
ApplyPlan(plan);
|
|
return;
|
|
}
|
|
|
|
case LaunchState.InstallJava:
|
|
{
|
|
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
environment = await _installStateService
|
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
await _javaService
|
|
.EnsureValidJavaInstalledAsync(manifest, environment, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
break;
|
|
}
|
|
|
|
case LaunchState.InstallMinecraft:
|
|
{
|
|
throw new NotImplementedException("Minecraft install/update flow has not been implemented yet.");
|
|
}
|
|
|
|
case LaunchState.InstallNeoforge:
|
|
{
|
|
throw new NotImplementedException("NeoForge install/update flow has not been implemented yet.");
|
|
}
|
|
|
|
case LaunchState.SyncMods:
|
|
{
|
|
manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
environment = await _installStateService
|
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
await _modService
|
|
.ProcessModsAsync(manifest, environment, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
break;
|
|
}
|
|
|
|
case LaunchState.Ready:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
throw new InvalidOperationException($"Unsupported launch state '{plan.State}'.");
|
|
}
|
|
}
|
|
|
|
plan = await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
public async Task LaunchAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
if (CurrentPlan == null)
|
|
{
|
|
await EvaluateAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
if (!CanRun)
|
|
{
|
|
throw new InvalidOperationException("Launcher cannot run because installation or updates are still required.");
|
|
}
|
|
|
|
InstallEnvironment environment = await _installStateService
|
|
.GetCurrentEnvironmentAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
ManifestModel manifest = await EnsureCurrentManifestAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
await _gameLaunchService
|
|
.LaunchAsync(manifest, environment, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
}
|
|
|
|
private async Task<ManifestModel> EnsureCurrentManifestAsync(CancellationToken cancellationToken)
|
|
{
|
|
ManifestModel? localManifest = await _manifestService
|
|
.GetLocalCoreManifestAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
Version remoteVersion = await _manifestService
|
|
.GetRemoteCoreManifestVersionAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
if (localManifest == null || localManifest.AlayaVersion != remoteVersion)
|
|
{
|
|
localManifest = await _manifestService
|
|
.GetCoreManifestAsync(cancellationToken)
|
|
.ConfigureAwait(false);
|
|
}
|
|
|
|
if (localManifest == null)
|
|
{
|
|
throw new FileNotFoundException("Local core manifest was not found after refresh.");
|
|
}
|
|
|
|
return localManifest;
|
|
}
|
|
|
|
private static bool DoModsNeedSync(ManifestModel manifest, InstallEnvironment environment)
|
|
{
|
|
if (manifest == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(manifest));
|
|
}
|
|
|
|
if (environment == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(environment));
|
|
}
|
|
|
|
var requiredMods = manifest.Files
|
|
.Where(file => file.Type == AlayaCore.Utilities.Enums.FileType.Mod)
|
|
.ToList();
|
|
|
|
var installedMods = environment.InstalledModsManifest.Mods;
|
|
|
|
if (requiredMods.Count != installedMods.Count)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
foreach (ModFileEntry requiredMod in requiredMods)
|
|
{
|
|
ModFileEntry? installedMod = installedMods.FirstOrDefault(
|
|
mod => string.Equals(mod.FileName, requiredMod.FileName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
if (installedMod == null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!string.Equals(installedMod.Sha512Hash, requiredMod.Sha512Hash, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (installedMod.Size != requiredMod.Size)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void ApplyPlan(LaunchPlan plan)
|
|
{
|
|
CurrentPlan = plan ?? throw new ArgumentNullException(nameof(plan));
|
|
NeedsUpdating = plan.NeedsUpdating;
|
|
CanRun = plan.CanRun;
|
|
}
|
|
}
|
|
} |