This commit is contained in:
IsWaffle 2023-05-14 22:35:06 -04:00
parent 8331080d85
commit e01729554b
16 changed files with 137 additions and 44 deletions

View File

@ -1,4 +1,5 @@
using SharpCompress; using Serilog;
using SharpCompress;
using SPTInstaller.Interfaces; using SPTInstaller.Interfaces;
using SPTInstaller.Models; using SPTInstaller.Models;
using System; using System;
@ -22,6 +23,7 @@ namespace SPTInstaller.Controllers
public async Task<IResult> RunPreChecks() public async Task<IResult> RunPreChecks()
{ {
Log.Information("-<>--<>- Running PreChecks -<>--<>-");
var requiredResults = new List<IResult>(); var requiredResults = new List<IResult>();
_preChecks.ForEach(x => x.IsPending = true); _preChecks.ForEach(x => x.IsPending = true);
@ -30,6 +32,8 @@ namespace SPTInstaller.Controllers
{ {
var result = await check.RunCheck(); var result = await check.RunCheck();
Log.Information($"PreCheck: {check.Name} ({(check.IsRequired ? "Required" : "Optional")}) -> {(result.Succeeded ? "Passed" : "Failed")}");
if (check.IsRequired) if (check.IsRequired)
{ {
requiredResults.Add(result); requiredResults.Add(result);
@ -47,6 +51,8 @@ namespace SPTInstaller.Controllers
public async Task<IResult> RunTasks() public async Task<IResult> RunTasks()
{ {
Log.Information("-<>--<>- Running Installer Tasks -<>--<>-");
foreach (var task in _tasks) foreach (var task in _tasks)
{ {
TaskChanged?.Invoke(null, task); TaskChanged?.Invoke(null, task);

View File

@ -77,7 +77,7 @@ namespace SPTInstaller.CustomControls
for(; TaskProgress < progress;) for(; TaskProgress < progress;)
{ {
TaskProgress += 2; TaskProgress += 1;
await Task.Delay(1); await Task.Delay(1);
} }
}); });

View File

@ -28,11 +28,12 @@
<ProgressBar IsVisible="{Binding ShowProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" <ProgressBar IsVisible="{Binding ShowProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
IsIndeterminate="{Binding IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
/> />
<Label Grid.Column="1" <Label Grid.Column="1"
Content="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}, StringFormat='{}{0}%'}" Content="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}, StringFormat='{}{0}%'}"
IsVisible="{Binding ShowProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" IsVisible="{Binding !IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
/> />
</Grid> </Grid>
</Grid> </Grid>

View File

@ -45,5 +45,14 @@ namespace SPTInstaller.CustomControls
public static readonly StyledProperty<bool> ShowProgressProperty = public static readonly StyledProperty<bool> ShowProgressProperty =
AvaloniaProperty.Register<TaskDetails, bool>(nameof(ShowProgress)); AvaloniaProperty.Register<TaskDetails, bool>(nameof(ShowProgress));
public bool IndeterminateProgress
{
get => GetValue(IndeterminateProgressProperty);
set => SetValue(IndeterminateProgressProperty, value);
}
public static readonly StyledProperty<bool> IndeterminateProgressProperty =
AvaloniaProperty.Register<TaskDetails, bool>(nameof(IndeterminateProgress));
} }
} }

View File

@ -1,4 +1,5 @@
using ReactiveUI; using ReactiveUI;
using Serilog;
using SPTInstaller.Models; using SPTInstaller.Models;
using System; using System;
using System.IO; using System.IO;
@ -19,6 +20,7 @@ namespace SPTInstaller.Aki.Helper
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, "Error while creating directories");
return Result.FromError(ex.Message); return Result.FromError(ex.Message);
} }
} }
@ -42,6 +44,7 @@ namespace SPTInstaller.Aki.Helper
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, "Error while copying files");
return Result.FromError(ex.Message); return Result.FromError(ex.Message);
} }
} }
@ -65,6 +68,7 @@ namespace SPTInstaller.Aki.Helper
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error(ex, "Error during directory copy");
return Result.FromError(ex.Message); return Result.FromError(ex.Message);
} }
} }

View File

@ -1,4 +1,5 @@
using SPTInstaller.Aki.Helper; using Serilog;
using SPTInstaller.Aki.Helper;
using SPTInstaller.Interfaces; using SPTInstaller.Interfaces;
using SPTInstaller.Models; using SPTInstaller.Models;
using System; using System;
@ -18,12 +19,12 @@ namespace SPTInstaller.Installer_Tasks
public override async Task<IResult> TaskOperation() public override async Task<IResult> TaskOperation()
{ {
SetStatus("Copying Client Files", 0); SetStatus("Copying Client Files", "", 0);
var originalGameDirInfo = new DirectoryInfo(_data.OriginalGamePath); var originalGameDirInfo = new DirectoryInfo(_data.OriginalGamePath);
var targetInstallDirInfo = new DirectoryInfo(_data.TargetInstallPath); var targetInstallDirInfo = new DirectoryInfo(_data.TargetInstallPath);
return FileHelper.CopyDirectoryWithProgress(originalGameDirInfo, targetInstallDirInfo, (message, progress) => { SetStatus($"Copying Client Files", message, progress); }); return FileHelper.CopyDirectoryWithProgress(originalGameDirInfo, targetInstallDirInfo, (message, progress) => { SetStatus($"Copying Client Files", message, progress, null, true); });
} }
} }
} }

View File

@ -21,7 +21,7 @@ namespace SPTInstaller.Installer_Tasks
private async Task<IResult> BuildMirrorList() private async Task<IResult> BuildMirrorList()
{ {
var progress = new Progress<double>((d) => { SetStatus("Downloading Mirror List", (int)Math.Floor(d));}); var progress = new Progress<double>((d) => { SetStatus("Downloading Mirror List", "", (int)Math.Floor(d));});
var file = await DownloadCacheHelper.GetOrDownloadFileAsync("mirrors.json", _data.PatcherMirrorsLink, progress); var file = await DownloadCacheHelper.GetOrDownloadFileAsync("mirrors.json", _data.PatcherMirrorsLink, progress);
@ -46,7 +46,7 @@ namespace SPTInstaller.Installer_Tasks
{ {
foreach (var mirror in _data.PatcherReleaseMirrors) foreach (var mirror in _data.PatcherReleaseMirrors)
{ {
SetStatus($"Downloading Patcher: {mirror.Link}"); SetStatus($"Downloading Patcher", mirror.Link);
// mega is a little weird since they use encryption, but thankfully there is a great library for their api :) // mega is a little weird since they use encryption, but thankfully there is a great library for their api :)
if (mirror.Link.StartsWith("https://mega")) if (mirror.Link.StartsWith("https://mega"))
@ -90,7 +90,7 @@ namespace SPTInstaller.Installer_Tasks
public override async Task<IResult> TaskOperation() public override async Task<IResult> TaskOperation()
{ {
var progress = new Progress<double>((d) => { SetStatus("", (int)Math.Floor(d)); }); var progress = new Progress<double>((d) => { SetStatus("", "", (int)Math.Floor(d)); });
if (_data.PatchNeeded) if (_data.PatchNeeded)
{ {
@ -101,7 +101,7 @@ namespace SPTInstaller.Installer_Tasks
return buildResult; return buildResult;
} }
SetStatus("", 0); SetStatus("", "", 0);
var patcherDownloadRresult = await DownloadPatcherFromMirrors(progress); var patcherDownloadRresult = await DownloadPatcherFromMirrors(progress);
@ -111,7 +111,7 @@ namespace SPTInstaller.Installer_Tasks
} }
} }
SetStatus("Downloading SPT-AKI", 0); SetStatus("Downloading SPT-AKI", "", 0);
_data.AkiZipInfo = await DownloadCacheHelper.GetOrDownloadFileAsync("sptaki.zip", _data.AkiReleaseDownloadLink, progress, _data.AkiReleaseHash); _data.AkiZipInfo = await DownloadCacheHelper.GetOrDownloadFileAsync("sptaki.zip", _data.AkiReleaseDownloadLink, progress, _data.AkiReleaseHash);

View File

@ -25,11 +25,11 @@ namespace SPTInstaller.Installer_Tasks
var repo = new RepositoryApi(Configuration.Default); var repo = new RepositoryApi(Configuration.Default);
SetStatus("Checking SPT Releases"); SetStatus("Checking SPT Releases", "", null, ProgressStyle.Indeterminate);
var akiRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Stable-releases"); var akiRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Stable-releases");
SetStatus("Checking for Patches"); SetStatus("Checking for Patches", "", null, ProgressStyle.Indeterminate);
var patchRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Downgrade-Patches"); var patchRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Downgrade-Patches");
@ -75,6 +75,8 @@ namespace SPTInstaller.Installer_Tasks
status += " - Patch Available"; status += " - Patch Available";
} }
SetStatus("", status);
return Result.FromSuccess(status); return Result.FromSuccess(status);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -25,13 +25,13 @@ namespace SPTInstaller.Installer_Tasks
var patcherEXE = new FileInfo(Path.Join(_data.TargetInstallPath, "patcher.exe")); var patcherEXE = new FileInfo(Path.Join(_data.TargetInstallPath, "patcher.exe"));
var progress = new Progress<double>((d) => { SetStatus("", (int)Math.Floor(d)); }); var progress = new Progress<double>((d) => { SetStatus("", "", (int)Math.Floor(d)); });
if (_data.PatchNeeded) if (_data.PatchNeeded)
{ {
// extract patcher files // extract patcher files
SetStatus("Extrating Patcher", 0); SetStatus("Extrating Patcher", "", 0);
var extractPatcherResult = ZipHelper.Decompress(_data.PatcherZipInfo, patcherOutputDir, progress); var extractPatcherResult = ZipHelper.Decompress(_data.PatcherZipInfo, patcherOutputDir, progress);
@ -41,7 +41,7 @@ namespace SPTInstaller.Installer_Tasks
} }
// copy patcher files to install directory // copy patcher files to install directory
SetStatus("Copying Patcher", 0); SetStatus("Copying Patcher", "", 0);
var patcherDirInfo = patcherOutputDir.GetDirectories("Patcher*", SearchOption.TopDirectoryOnly).First(); var patcherDirInfo = patcherOutputDir.GetDirectories("Patcher*", SearchOption.TopDirectoryOnly).First();
@ -53,10 +53,7 @@ namespace SPTInstaller.Installer_Tasks
} }
// run patcher // run patcher
SetStatus("Running Patcher"); SetStatus("Running Patcher", "", null, ProgressStyle.Indeterminate);
// TODO: indeterminate progress indicator
//StartDrawingIndeterminateProgress();
var patchingResult = ProcessHelper.PatchClientFiles(patcherEXE, targetInstallDirInfo); var patchingResult = ProcessHelper.PatchClientFiles(patcherEXE, targetInstallDirInfo);
@ -67,7 +64,7 @@ namespace SPTInstaller.Installer_Tasks
} }
// extract release files // extract release files
SetStatus("Extracting Release", 0); SetStatus("Extracting Release", "", 0);
var extractReleaseResult = ZipHelper.Decompress(_data.AkiZipInfo, targetInstallDirInfo, progress); var extractReleaseResult = ZipHelper.Decompress(_data.AkiZipInfo, targetInstallDirInfo, progress);
@ -77,10 +74,7 @@ namespace SPTInstaller.Installer_Tasks
} }
// cleanup temp files // cleanup temp files
SetStatus("Cleanup"); SetStatus("Cleanup", "almost done :)", null, ProgressStyle.Indeterminate);
// TODO: indeterminate progress indicator
//StartDrawingIndeterminateProgress();
if(_data.PatchNeeded) if(_data.PatchNeeded)
{ {

View File

@ -1,7 +1,10 @@
using Avalonia.Threading; using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
using Serilog;
using Splat;
using SPTInstaller.Interfaces; using SPTInstaller.Interfaces;
using System; using System;
using System.Security;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SPTInstaller.Models namespace SPTInstaller.Models
@ -57,6 +60,13 @@ namespace SPTInstaller.Models
private set => this.RaiseAndSetIfChanged(ref _showProgress, value); private set => this.RaiseAndSetIfChanged(ref _showProgress, value);
} }
private bool _indeterminateProgress;
public bool IndeterminateProgress
{
get => _indeterminateProgress;
private set => this.RaiseAndSetIfChanged(ref _indeterminateProgress, value);
}
private string _statusMessage; private string _statusMessage;
public string StatusMessage public string StatusMessage
{ {
@ -71,28 +81,65 @@ namespace SPTInstaller.Models
private set => this.RaiseAndSetIfChanged(ref _statusDetails, value); private set => this.RaiseAndSetIfChanged(ref _statusDetails, value);
} }
/// <summary> public enum ProgressStyle
/// Update the status message and optionally a progress bar value {
/// </summary> Hidden = 0,
/// <param name="message"></param> Shown,
/// <param name="progress"></param> Indeterminate,
/// <remarks>If message is empty, it isn't updated. If progress is null, the progress bar will be hidden. Details is not touched with this method</remarks> }
public void SetStatus(string message, int? progress = null) => SetStatus(message, "", progress);
/// <summary> /// <summary>
/// Update the status message, status details, and optionlly a progress bar value /// Update the status details of the task
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message">The main message to display. Not updated if empty</param>
/// <param name="progress"></param> /// <param name="details">The details of the task. Not updated if empty</param>
/// <remarks>If message or details are empty, it isn't updated. If progress is null, the progress bar will be hidden</remarks> /// <param name="progress">Progress of the task. Not empty if null. Overrides progressStyle if a non-null value is supplied</param>
public void SetStatus(string message, string details, int? progress = null) /// <param name="progressStyle">The style of the progress bar</param>
public void SetStatus(string message, string details, int? progress = null, ProgressStyle? progressStyle = null, bool noLog = false)
{ {
StatusMessage = String.IsNullOrWhiteSpace(message) ? StatusMessage : message; if(!string.IsNullOrWhiteSpace(message) && message != StatusMessage)
StatusDetails = String.IsNullOrWhiteSpace(details) ? StatusDetails : details; {
ShowProgress = progress != null; if (!noLog)
{
Log.Information($" <===> {message} <===>");
}
StatusMessage = message;
}
if(!string.IsNullOrWhiteSpace(details) && details != StatusDetails)
{
if (!noLog)
{
Log.Information(details);
}
StatusDetails = details;
}
if (progressStyle != null)
{
switch (progressStyle)
{
case ProgressStyle.Hidden:
ShowProgress = false;
IndeterminateProgress = false;
break;
case ProgressStyle.Shown:
ShowProgress = true;
IndeterminateProgress = false;
break;
case ProgressStyle.Indeterminate:
ShowProgress = true;
IndeterminateProgress = true;
break;
}
}
if (progress != null) if (progress != null)
{ {
ShowProgress = true;
IndeterminateProgress = false;
Progress = progress.Value; Progress = progress.Value;
} }
} }
@ -117,7 +164,9 @@ namespace SPTInstaller.Models
if (!result.Succeeded) if (!result.Succeeded)
{ {
// TODO: handle error state HasErrors = true;
return result;
} }
IsCompleted = true; IsCompleted = true;

View File

@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using ReactiveUI; using ReactiveUI;
using Serilog;
using Splat; using Splat;
using SPTInstaller.Controllers; using SPTInstaller.Controllers;
using SPTInstaller.Helpers; using SPTInstaller.Helpers;
@ -9,6 +10,7 @@ using SPTInstaller.Installer_Tasks.PreChecks;
using SPTInstaller.Interfaces; using SPTInstaller.Interfaces;
using SPTInstaller.Models; using SPTInstaller.Models;
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -34,13 +36,23 @@ namespace SPTInstaller
ServiceHelper.Register<PreCheckBase, NetFramework472PreCheck>(); ServiceHelper.Register<PreCheckBase, NetFramework472PreCheck>();
ServiceHelper.Register<PreCheckBase, NetCore6PreCheck>(); ServiceHelper.Register<PreCheckBase, NetCore6PreCheck>();
#if !TEST #if !TEST
string logPath = Path.Join(Environment.CurrentDirectory, "spt-aki-installer_.log");
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo
.File(path: logPath,
restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Debug,
rollingInterval: RollingInterval.Day)
.CreateLogger();
ServiceHelper.Register<InstallerTaskBase, InitializationTask>(); ServiceHelper.Register<InstallerTaskBase, InitializationTask>();
ServiceHelper.Register<InstallerTaskBase, ReleaseCheckTask>(); ServiceHelper.Register<InstallerTaskBase, ReleaseCheckTask>();
ServiceHelper.Register<InstallerTaskBase, DownloadTask>(); ServiceHelper.Register<InstallerTaskBase, DownloadTask>();
ServiceHelper.Register<InstallerTaskBase, CopyClientTask>(); ServiceHelper.Register<InstallerTaskBase, CopyClientTask>();
ServiceHelper.Register<InstallerTaskBase, SetupClientTask>(); ServiceHelper.Register<InstallerTaskBase, SetupClientTask>();
#else #else
for(int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
Locator.CurrentMutable.RegisterConstant<InstallerTaskBase>(TestTask.FromRandomName()); Locator.CurrentMutable.RegisterConstant<InstallerTaskBase>(TestTask.FromRandomName());
} }

View File

@ -30,6 +30,7 @@
<PackageReference Include="FubarCoder.RestSharp.Portable.HttpClient" Version="4.0.8" /> <PackageReference Include="FubarCoder.RestSharp.Portable.HttpClient" Version="4.0.8" />
<PackageReference Include="MegaApiClient" Version="1.10.3" /> <PackageReference Include="MegaApiClient" Version="1.10.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="SharpCompress" Version="0.33.0" /> <PackageReference Include="SharpCompress" Version="0.33.0" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" /> <PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
</ItemGroup> </ItemGroup>

View File

@ -1,5 +1,7 @@
using Avalonia; using Avalonia;
using ReactiveUI; using ReactiveUI;
using Serilog;
using System;
namespace SPTInstaller.ViewModels namespace SPTInstaller.ViewModels
{ {
@ -10,6 +12,9 @@ namespace SPTInstaller.ViewModels
public MainWindowViewModel() public MainWindowViewModel()
{ {
Log.Information("========= LAUNCHER STARTED =========");
Log.Information(Environment.OSVersion.VersionString);
Router.Navigate.Execute(new PreChecksViewModel(this)); Router.Navigate.Execute(new PreChecksViewModel(this));
} }

View File

@ -1,5 +1,6 @@
using Avalonia; using Avalonia;
using ReactiveUI; using ReactiveUI;
using Serilog;
using System.Windows.Input; using System.Windows.Input;
namespace SPTInstaller.ViewModels namespace SPTInstaller.ViewModels
@ -24,6 +25,7 @@ namespace SPTInstaller.ViewModels
public MessageViewModel(IScreen Host, string message) : base(Host) public MessageViewModel(IScreen Host, string message) : base(Host)
{ {
Message = message; Message = message;
Log.Information(message);
} }
} }
} }

View File

@ -30,7 +30,8 @@ namespace SPTInstaller.ViewModels
if(data == null || installer == null) if(data == null || installer == null)
{ {
// TODO: abort to message view NavigateTo(new MessageViewModel(HostScreen, "Failed to get required service for prechecks"));
return;
} }
data.TargetInstallPath = Environment.CurrentDirectory; data.TargetInstallPath = Environment.CurrentDirectory;
@ -45,8 +46,13 @@ namespace SPTInstaller.ViewModels
Task.Run(async () => Task.Run(async () =>
{ {
// TODO: if a required precheck fails, abort to message view
var result = await installer.RunPreChecks(); var result = await installer.RunPreChecks();
if(!result.Succeeded)
{
//if a required precheck fails, abort to message view
NavigateTo(new MessageViewModel(HostScreen ,result.Message));
}
}); });
} }
} }

View File

@ -18,6 +18,7 @@
Message="{Binding CurrentTask.StatusMessage}" Message="{Binding CurrentTask.StatusMessage}"
Details="{Binding CurrentTask.StatusDetails}" Details="{Binding CurrentTask.StatusDetails}"
Progress="{Binding CurrentTask.Progress}" Progress="{Binding CurrentTask.Progress}"
IndeterminateProgress="{Binding CurrentTask.IndeterminateProgress}"
ShowProgress="{Binding CurrentTask.ShowProgress}" ShowProgress="{Binding CurrentTask.ShowProgress}"
/> />
</Grid> </Grid>