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

View File

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

View File

@ -28,11 +28,12 @@
<ProgressBar IsVisible="{Binding ShowProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}}"
HorizontalAlignment="Stretch"
IsIndeterminate="{Binding IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
<Label Grid.Column="1"
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>

View File

@ -45,5 +45,14 @@ namespace SPTInstaller.CustomControls
public static readonly StyledProperty<bool> ShowProgressProperty =
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 Serilog;
using SPTInstaller.Models;
using System;
using System.IO;
@ -19,6 +20,7 @@ namespace SPTInstaller.Aki.Helper
}
catch (Exception ex)
{
Log.Error(ex, "Error while creating directories");
return Result.FromError(ex.Message);
}
}
@ -42,6 +44,7 @@ namespace SPTInstaller.Aki.Helper
}
catch (Exception ex)
{
Log.Error(ex, "Error while copying files");
return Result.FromError(ex.Message);
}
}
@ -65,6 +68,7 @@ namespace SPTInstaller.Aki.Helper
}
catch (Exception ex)
{
Log.Error(ex, "Error during directory copy");
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.Models;
using System;
@ -18,12 +19,12 @@ namespace SPTInstaller.Installer_Tasks
public override async Task<IResult> TaskOperation()
{
SetStatus("Copying Client Files", 0);
SetStatus("Copying Client Files", "", 0);
var originalGameDirInfo = new DirectoryInfo(_data.OriginalGamePath);
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()
{
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);
@ -46,7 +46,7 @@ namespace SPTInstaller.Installer_Tasks
{
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 :)
if (mirror.Link.StartsWith("https://mega"))
@ -90,7 +90,7 @@ namespace SPTInstaller.Installer_Tasks
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)
{
@ -101,7 +101,7 @@ namespace SPTInstaller.Installer_Tasks
return buildResult;
}
SetStatus("", 0);
SetStatus("", "", 0);
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);

View File

@ -25,11 +25,11 @@ namespace SPTInstaller.Installer_Tasks
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");
SetStatus("Checking for Patches");
SetStatus("Checking for Patches", "", null, ProgressStyle.Indeterminate);
var patchRepoReleases = await repo.RepoListReleasesAsync("SPT-AKI", "Downgrade-Patches");
@ -75,6 +75,8 @@ namespace SPTInstaller.Installer_Tasks
status += " - Patch Available";
}
SetStatus("", status);
return Result.FromSuccess(status);
}
catch (Exception ex)

View File

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

View File

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

View File

@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.ReactiveUI;
using ReactiveUI;
using Serilog;
using Splat;
using SPTInstaller.Controllers;
using SPTInstaller.Helpers;
@ -9,6 +10,7 @@ using SPTInstaller.Installer_Tasks.PreChecks;
using SPTInstaller.Interfaces;
using SPTInstaller.Models;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
@ -34,13 +36,23 @@ namespace SPTInstaller
ServiceHelper.Register<PreCheckBase, NetFramework472PreCheck>();
ServiceHelper.Register<PreCheckBase, NetCore6PreCheck>();
#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, ReleaseCheckTask>();
ServiceHelper.Register<InstallerTaskBase, DownloadTask>();
ServiceHelper.Register<InstallerTaskBase, CopyClientTask>();
ServiceHelper.Register<InstallerTaskBase, SetupClientTask>();
#else
for(int i = 0; i < 5; i++)
for (int i = 0; i < 5; i++)
{
Locator.CurrentMutable.RegisterConstant<InstallerTaskBase>(TestTask.FromRandomName());
}

View File

@ -30,6 +30,7 @@
<PackageReference Include="FubarCoder.RestSharp.Portable.HttpClient" Version="4.0.8" />
<PackageReference Include="MegaApiClient" Version="1.10.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="XamlNameReferenceGenerator" Version="1.6.1" />
</ItemGroup>

View File

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

View File

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

View File

@ -30,7 +30,8 @@ namespace SPTInstaller.ViewModels
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;
@ -45,8 +46,13 @@ namespace SPTInstaller.ViewModels
Task.Run(async () =>
{
// TODO: if a required precheck fails, abort to message view
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}"
Details="{Binding CurrentTask.StatusDetails}"
Progress="{Binding CurrentTask.Progress}"
IndeterminateProgress="{Binding CurrentTask.IndeterminateProgress}"
ShowProgress="{Binding CurrentTask.ShowProgress}"
/>
</Grid>