Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI changes to allow verification and modlist folder recognition #2616

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 162 additions & 27 deletions Wabbajack.App.Wpf/View Models/Installers/InstallerVM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
using Microsoft.Extensions.DependencyInjection;
using Wabbajack.CLI.Verbs;
using Wabbajack.VFS;
using SteamKit2.GC.Underlords.Internal;
using Wabbajack.Compiler;
using System.Windows.Controls.Primitives;

namespace Wabbajack;

Expand Down Expand Up @@ -138,8 +141,6 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
public LogStream LoggerProvider { get; }

private AbsolutePath LastInstallPath { get; set; }

[Reactive] public bool OverwriteFiles { get; set; }


// Command properties
Expand All @@ -155,7 +156,26 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
public ReactiveCommand<Unit, Unit> BeginCommand { get; }

public ReactiveCommand<Unit, Unit> VerifyCommand { get; }



private bool _isKeyPressed;
public bool IsKeyPressed
JanuarySnow marked this conversation as resolved.
Show resolved Hide resolved
{
get => _isKeyPressed;
set => this.RaiseAndSetIfChanged(ref _isKeyPressed, value);
}

private bool _UnrecognisedFilesPresent;
public bool UnrecognisedFilesPresent
JanuarySnow marked this conversation as resolved.
Show resolved Hide resolved
{
get => _UnrecognisedFilesPresent;
set => this.RaiseAndSetIfChanged(ref _UnrecognisedFilesPresent, value);
}

private string UnrecognisedFilesFoundErrorMessage = "Files found in the install folder, please choose a different folder, or if you are CERTAIN you want to install here " +
Environment.NewLine + " then hold down shift to enable the button. This will delete any unrecognised files in the folder!";


public InstallerVM(ILogger<InstallerVM> logger, DTOSerializer dtos, SettingsManager settingsManager, IServiceProvider serviceProvider,
SystemParametersConstructor parametersConstructor, IGameLocator gameLocator, LogStream loggerProvider, ResourceMonitor resourceMonitor,
Wabbajack.Services.OSIntegrated.Configuration configuration, HttpClient client, DownloadDispatcher dispatcher, IEnumerable<INeedsLogin> logins,
Expand Down Expand Up @@ -235,9 +255,6 @@ public InstallerVM(ILogger<InstallerVM> logger, DTOSerializer dtos, SettingsMana
UIUtils.OpenFolder(Installer.Location.TargetPath);
});

this.WhenAnyValue(x => x.OverwriteFiles)
.Subscribe(x => ConfirmOverwrite());

MessageBus.Current.Listen<LoadModlistForInstalling>()
.Subscribe(msg => LoadModlistFromGallery(msg.Path, msg.Metadata).FireAndForget())
.DisposeWith(CompositeDisposable);
Expand Down Expand Up @@ -271,8 +288,32 @@ public InstallerVM(ILogger<InstallerVM> logger, DTOSerializer dtos, SettingsMana
.Where(t => t.Failed)
.Concat(Validate())
.ToArray();
if (!errors.Any()) return ErrorResponse.Success;
return ErrorResponse.Fail(string.Join("\n", errors.Select(e => e.Reason)));
if (errors.Length == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be me, but I find the logic here pretty tricky to follow with the UnrecognisedFilesPresent bool. I'll have a look to see if I can lower the code complexity a bit here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its a casualty of working around the system that was really designed to return a "error or not error" binary paradigm to block activation of the start button.

I Didnt want to and couldnt even if I did want to, rewrite the entire validate() system , so just tacked on the special case of Unrecognised files being present as being distinct from recognised files being present.

to fit in the logic for when to enable the start button based on two boolean branches

 this.WhenAnyValue(x => x.ViewModel.ErrorState, x => x.ViewModel.IsShiftKeyPressed, x => x.ViewModel.UnrecognisedFilesPresent)
    .Select(v => (!v.Item1.Failed || v.Item1.Succeeded ) || (v.Item1.Failed && v.Item2 && v.Item3))

So that the shift key is only viable as a shortcut in that one error scenario.

Its ugly but yeah, im sure theres a better way.

{
UnrecognisedFilesPresent = false;
return ErrorResponse.Success;
} else
JanuarySnow marked this conversation as resolved.
Show resolved Hide resolved
{
if(errors.Length == 1)
{
if(errors[0].Reason == UnrecognisedFilesFoundErrorMessage)
{
UnrecognisedFilesPresent = true;
} else
{
UnrecognisedFilesPresent = false;
if (errors[0].Succeeded)
{
return ErrorResponse.Succeed(errors[0].Reason);
}
}
} else
{
//other errors too, not just this one particular error we want to handle for
UnrecognisedFilesPresent = false;
}
return ErrorResponse.Fail(string.Join("\n", errors.Select(e => e.Reason)));
}
})
.BindTo(this, vm => vm.ErrorState)
.DisposeWith(disposables);
Expand All @@ -286,14 +327,24 @@ private IEnumerable<ErrorResponse> Validate()
yield return ErrorResponse.Fail("Mod list source does not exist");

var downloadPath = Installer.DownloadLocation.TargetPath;


if (downloadPath.Depth <= 1)
{
yield return ErrorResponse.Fail("Download path isn't set to a folder");
}

var installPath = Installer.Location.TargetPath;


if (installPath.Depth <= 1)
{
yield return ErrorResponse.Fail("Install path isn't set to a folder");
}
if (installPath.InFolder(KnownFolders.Windows))
{
yield return ErrorResponse.Fail("Don't install modlists into your Windows folder");
}
if( installPath.ToString().Length > 0 && downloadPath.ToString().Length > 0 && installPath == downloadPath)
{
yield return ErrorResponse.Fail("Can't have identical install and download folders");
Expand All @@ -302,6 +353,10 @@ private IEnumerable<ErrorResponse> Validate()
{
yield return ErrorResponse.Fail("Can't put the install folder inside the download folder");
}
if (installPath.ToString().Length > 0 && Path.GetFileName(installPath.ToString().TrimEnd(Path.DirectorySeparatorChar)) == "downloads")
{
yield return ErrorResponse.Fail("You've selected a folder called 'downloads' as install folder, please check your paths");
}
foreach (var game in GameRegistry.Games)
{
if (!_gameLocator.TryFindLocation(game.Key, out var location))
Expand All @@ -325,25 +380,41 @@ private IEnumerable<ErrorResponse> Validate()
{
yield return ErrorResponse.Fail("Installing in this folder may overwrite Wabbajack");
}
bool OnlyDownloadFolderPresent = false;
JanuarySnow marked this conversation as resolved.
Show resolved Hide resolved
bool UpdatingExisting = false;
if (installPath.ToString().Length != 0 && AreThereFilesInInstallFolder(installPath)){
if (downloadPath.ToString().Length > 0)
{
if (IsSelectedDownloadFolderTheOnlyThingThere(installPath, downloadPath))
{
OnlyDownloadFolderPresent = true;
}
}
if (OnlyDownloadFolderPresent == false)
{
var listname = IsAModListDirectory(installPath);
if (listname == ModList.Name)
{
UpdatingExisting = true;
yield return ErrorResponse.Succeed("Existing install of this modlist found, installing to this folder will reset the modlist to its default state");
}
else
{
UnrecognisedFilesPresent = true;
yield return ErrorResponse.Fail(UnrecognisedFilesFoundErrorMessage);
}
}

if (installPath.ToString().Length != 0 && installPath != LastInstallPath && !OverwriteFiles &&
Directory.EnumerateFileSystemEntries(installPath.ToString()).Any())
{
yield return ErrorResponse.Fail("There are files in the install folder, please tick 'Overwrite Installation' to confirm you want to install to this folder " + Environment.NewLine +
"if you are updating an existing modlist, then this is expected and can be overwritten.");
}

if (KnownFolders.IsInSpecialFolder(installPath) || KnownFolders.IsInSpecialFolder(downloadPath))
{
yield return ErrorResponse.Fail("Can't install into Windows locations such as Documents etc, please make a new folder for the modlist - C:\\ModList\\ for example.");
}
// Disabled Because it was causing issues for people trying to update lists.
//if (installPath.ToString().Length > 0 && downloadPath.ToString().Length > 0 && !HasEnoughSpace(installPath, downloadPath)){
// yield return ErrorResponse.Fail("Can't install modlist due to lack of free hard drive space, please read the modlist Readme to learn more.");
//}
if (!UpdatingExisting && !OnlyDownloadFolderPresent && installPath.ToString().Length > 0 && downloadPath.ToString().Length > 0 && !HasEnoughSpace(installPath, downloadPath)){
yield return ErrorResponse.Fail("Can't install modlist due to lack of free hard drive space, please read the modlist Readme to learn more.");
}
}

/*
private bool HasEnoughSpace(AbsolutePath inpath, AbsolutePath downpath)
{
string driveLetterInPath = inpath.ToString().Substring(0,1);
Expand All @@ -370,8 +441,79 @@ private bool HasEnoughSpace(AbsolutePath inpath, AbsolutePath downpath)
}
}
return true;
}

private string IsAModListDirectory(AbsolutePath InstallPath)
{

var allFilenames = Directory.EnumerateFiles(InstallPath.ToString()).Select(p => Path.GetFileName(p));


var candidates = allFilenames.Where(fn => Path.GetExtension(fn) == ".compiler_settings")
.Select(fn => Path.GetFileName(fn));

CompilerSettings settings;
string jsonString = "";
foreach (string x in candidates)
{
var path = InstallPath.Combine(x);
try
{
jsonString = File.ReadAllText(path.ToString());
}
catch(IOException ex)
{
_logger.LogError(ex, "Trying to open existing compiler_settings file to find modlist in chosen folder, but can't open path: " + path);
return string.Empty;
}
finally
{
settings = _dtos.Deserialize<CompilerSettings>(jsonString);
}
return settings.ModListName;
}
return string.Empty;
}

private static bool AreThereFilesInInstallFolder(AbsolutePath InstallPath)
{
return Directory.EnumerateFileSystemEntries(InstallPath.ToString()).Any();
}

}*/
private bool IsSelectedDownloadFolderTheOnlyThingThere(AbsolutePath InstallPath, AbsolutePath DownloadPath)
{
//when parsing the install directory for things already there, if the thing thats already there
// is the download path we already want then dont count that as a thing thats already there!
List<string> dirs = new List<string>(Directory.EnumerateDirectories(InstallPath.ToString()));
string[] files = Directory.GetFiles(InstallPath.ToString(), "*.*", SearchOption.TopDirectoryOnly);
if (files.Length > 0)
{
return false;
}
bool DownloadFolderFound = false;
bool TempFolderFound = true;
foreach (string x in dirs)
{
if (x == DownloadPath.ToString())
{
DownloadFolderFound = true;
}
if(x == "__temp__")
{
TempFolderFound = true;
}
}
int DirectoryCount = Directory.GetDirectories(InstallPath.ToString()).Length;
if (DownloadFolderFound || TempFolderFound)
{

if ((DirectoryCount == 2 && DownloadFolderFound && TempFolderFound) || DirectoryCount == 1)
{
return true;
}
}
return false;
}

private async Task BeginSlideShow(CancellationToken token)
{
Expand Down Expand Up @@ -455,13 +597,6 @@ private async Task LoadModlist(AbsolutePath path, ModlistMetadata? metadata)
}
}

private void ConfirmOverwrite()
{
AbsolutePath prev = Installer.Location.TargetPath;
Installer.Location.TargetPath = "".ToAbsolutePath();
Installer.Location.TargetPath = prev;
}

private async Task Verify()
{
await Task.Run(async () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@
<Grid Background="{StaticResource WindowBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
Margin="5"
VerticalAlignment="Center"
Margin="2"
VerticalAlignment="Top"
Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="5" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="120" />
<ColumnDefinition Width="50" MinWidth="120" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="InstallConfigSpacer" Grid.Row="0" />
<TextBlock Grid.Row="1" Grid.Column="0"
Expand All @@ -40,12 +40,14 @@
Text="Target Modlist"
TextAlignment="Center" />
<TextBlock x:Name="errorTextBox"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Grid.Row="3"
FontFamily="Verdana" FontSize="10" FontWeight="ExtraBold"
FontFamily="Verdana" FontSize="11" FontWeight="ExtraBold"
Background="{StaticResource WindowBackgroundBrush}"
Foreground="Red"
Text=""
TextAlignment="Left" Margin="0,79,0,-45" Grid.ColumnSpan="3" />
TextAlignment="Left" Margin="0,79,0,-45" Grid.ColumnSpan="5" />
<local:FilePicker Grid.Row="1" Grid.Column="2"
x:Name="ModListLocationPicker"
Height="30"
Expand Down Expand Up @@ -104,24 +106,6 @@
VerticalAlignment="Top"
Foreground="{StaticResource WarningBrush}"
Kind="ExclamationTriangleSolid" />
<CheckBox Grid.Row="2" Grid.Column="2"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
x:Name="OverwriteCheckBox"
Content="Overwrite Installation"
IsChecked="False"
ToolTip="Confirm to overwrite files in install folder.">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Opacity" Value="0.6" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Opacity" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<Button Grid.Row="3" Grid.Column="2"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Expand Down
Loading