diff --git a/Wabbajack.Common/WorkQueue.cs b/Wabbajack.Common/WorkQueue.cs index cf639bcf..37a74cee 100644 --- a/Wabbajack.Common/WorkQueue.cs +++ b/Wabbajack.Common/WorkQueue.cs @@ -23,17 +23,16 @@ namespace Wabbajack.Common private static readonly Subject _Status = new Subject(); public IObservable Status => _Status; - public static int ThreadCount { get; } = Environment.ProcessorCount; public static List Threads { get; private set; } - public WorkQueue() + public WorkQueue(int threadCount = 0) { - StartThreads(); + StartThreads(threadCount == 0 ? Environment.ProcessorCount : threadCount); } - private void StartThreads() + private void StartThreads(int threadCount) { - Threads = Enumerable.Range(0, ThreadCount) + Threads = Enumerable.Range(0, threadCount) .Select(idx => { var thread = new Thread(() => ThreadBody(idx)); diff --git a/Wabbajack.Lib/ABatchProcessor.cs b/Wabbajack.Lib/ABatchProcessor.cs new file mode 100644 index 00000000..212d8826 --- /dev/null +++ b/Wabbajack.Lib/ABatchProcessor.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reactive.Subjects; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using ReactiveUI; +using Wabbajack.Common; +using Wabbajack.VirtualFileSystem; + +namespace Wabbajack.Lib +{ + public abstract class ABatchProcessor : IBatchProcessor + { + public WorkQueue Queue { get; private set; } + private bool _configured = false; + + public void Dispose() + { + Queue?.Shutdown(); + } + + public Context VFS { get; private set; } + + protected StatusUpdateTracker UpdateTracker { get; private set; } + + private Subject _percentCompleted { get; } = new Subject(); + + /// + /// The current progress of the entire processing system on a scale of 0.0 to 1.0 + /// + public IObservable PercentCompleted => _percentCompleted; + + private Subject _textStatus { get; } = new Subject(); + + /// + /// The current status of the processor as a text string + /// + public IObservable TextStatus => _textStatus; + + private Subject _queueStatus { get; } = new Subject(); + public IObservable QueueStatus => _queueStatus; + + private Subject _isRunning { get; } = new Subject(); + public IObservable IsRunning => _isRunning; + + private Thread _processorThread { get; set; } + + protected void ConfigureProcessor(int steps, int threads = 0) + { + if (_configured) + throw new InvalidDataException("Can't configure a processor twice"); + Queue = new WorkQueue(threads); + UpdateTracker = new StatusUpdateTracker(steps); + Queue.Status.Subscribe(_queueStatus); + UpdateTracker.Progress.Subscribe(_percentCompleted); + UpdateTracker.StepName.Subscribe(_textStatus); + VFS = new Context(Queue) { UpdateTracker = UpdateTracker }; + _configured = true; + } + + protected abstract bool _Begin(); + public Task Begin() + { + _isRunning.OnNext(true); + var _tcs = new TaskCompletionSource(); + if (_processorThread != null) + { + throw new InvalidDataException("Can't start the processor twice"); + } + + _processorThread = new Thread(() => + { + try + { + _tcs.SetResult(_Begin()); + } + catch (Exception ex) + { + _tcs.SetException(ex); + } + finally + { + _isRunning.OnNext(false); + } + }); + _processorThread.Priority = ThreadPriority.BelowNormal; + _processorThread.Start(); + return _tcs.Task; + } + + public void Terminate() + { + Queue?.Shutdown(); + _processorThread?.Abort(); + _isRunning.OnNext(false); + } + } +} diff --git a/Wabbajack.Lib/ACompiler.cs b/Wabbajack.Lib/ACompiler.cs index d95b0732..89c3737d 100644 --- a/Wabbajack.Lib/ACompiler.cs +++ b/Wabbajack.Lib/ACompiler.cs @@ -17,15 +17,11 @@ using Path = Alphaleonis.Win32.Filesystem.Path; namespace Wabbajack.Lib { - public abstract class ACompiler + public abstract class ACompiler : ABatchProcessor { public string ModListName, ModListAuthor, ModListDescription, ModListImage, ModListWebsite, ModListReadme; public string WabbajackVersion; - public StatusUpdateTracker UpdateTracker { get; protected set; } - - public WorkQueue Queue { get; protected set; } - protected static string _vfsCacheName = "vfs_compile_cache.bin"; /// /// A stream of tuples of ("Update Title", 0.25) which represent the name of the current task @@ -34,8 +30,6 @@ namespace Wabbajack.Lib public IObservable<(string, float)> ProgressUpdates => _progressUpdates; protected readonly Subject<(string, float)> _progressUpdates = new Subject<(string, float)>(); - public Context VFS { get; internal set; } - public ModManager ModManager; public string GamePath; @@ -43,6 +37,8 @@ namespace Wabbajack.Lib public string ModListOutputFolder; public string ModListOutputFile; + public bool ShowReportWhenFinished { get; set; } = true; + public List SelectedArchives = new List(); public List InstallDirectives = new List(); public List AllFiles = new List(); @@ -128,7 +124,7 @@ namespace Wabbajack.Lib public void ShowReport() { - //if (!ShowReportWhenFinished) return; + if (!ShowReportWhenFinished) return; var file = Path.GetTempFileName() + ".html"; File.WriteAllText(file, ModList.ReportHTML); @@ -207,8 +203,6 @@ namespace Wabbajack.Lib return null; } - public abstract bool Compile(); - public Directive RunStack(IEnumerable stack, RawSourceFile source) { Utils.Status($"Compiling {source.Path}"); @@ -223,11 +217,5 @@ namespace Wabbajack.Lib public abstract IEnumerable GetStack(); public abstract IEnumerable MakeStack(); - - protected ACompiler() - { - ProgressUpdates.Subscribe(itm => Utils.Log($"{itm.Item2} - {itm.Item1}")); - Queue = new WorkQueue(); - } } } diff --git a/Wabbajack.Lib/AInstaller.cs b/Wabbajack.Lib/AInstaller.cs index 9ba3f525..c2833ea1 100644 --- a/Wabbajack.Lib/AInstaller.cs +++ b/Wabbajack.Lib/AInstaller.cs @@ -12,14 +12,10 @@ using Path = Alphaleonis.Win32.Filesystem.Path; namespace Wabbajack.Lib { - public abstract class AInstaller + public abstract class AInstaller : ABatchProcessor { public bool IgnoreMissingFiles { get; internal set; } = false; - public StatusUpdateTracker UpdateTracker { get; protected set; } - public WorkQueue Queue { get; protected set; } - public Context VFS { get; internal set; } - public string OutputFolder { get; set; } public string DownloadFolder { get; set; } @@ -29,13 +25,6 @@ namespace Wabbajack.Lib public ModList ModList { get; internal set; } public Dictionary HashedArchives { get; set; } - protected AInstaller() - { - Queue = new WorkQueue(); - } - - public abstract void Install(); - public void Info(string msg) { Utils.Log(msg); diff --git a/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs b/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs index 4f494e8c..c2dff378 100644 --- a/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs +++ b/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs @@ -13,11 +13,11 @@ namespace Wabbajack.Lib.CompilationSteps private readonly IEnumerable _include_directly; private readonly List _microstack; private readonly List _microstackWithInclude; - private readonly Compiler _mo2Compiler; + private readonly MO2Compiler _mo2Compiler; public DeconstructBSAs(ACompiler compiler) : base(compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; _include_directly = _mo2Compiler.ModInis.Where(kv => { var general = kv.Value.General; diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs b/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs index 4c01a8ed..e6fad045 100644 --- a/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs +++ b/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs @@ -9,11 +9,11 @@ namespace Wabbajack.Lib.CompilationSteps public class IgnoreDisabledMods : ACompilationStep { private readonly IEnumerable _allEnabledMods; - private readonly Compiler _mo2Compiler; + private readonly MO2Compiler _mo2Compiler; public IgnoreDisabledMods(ACompiler compiler) : base(compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; var alwaysEnabled = _mo2Compiler.ModInis.Where(f => IsAlwaysEnabled(f.Value)).Select(f => f.Key).ToHashSet(); _allEnabledMods = _mo2Compiler.SelectedProfiles diff --git a/Wabbajack.Lib/CompilationSteps/IncludeOtherProfiles.cs b/Wabbajack.Lib/CompilationSteps/IncludeOtherProfiles.cs index 096f87b9..fef548f1 100644 --- a/Wabbajack.Lib/CompilationSteps/IncludeOtherProfiles.cs +++ b/Wabbajack.Lib/CompilationSteps/IncludeOtherProfiles.cs @@ -8,11 +8,11 @@ namespace Wabbajack.Lib.CompilationSteps public class IgnoreOtherProfiles : ACompilationStep { private readonly IEnumerable _profiles; - private readonly Compiler _mo2Compiler; + private readonly MO2Compiler _mo2Compiler; public IgnoreOtherProfiles(ACompiler compiler) : base(compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; _profiles = _mo2Compiler.SelectedProfiles .Select(p => Path.Combine("profiles", p) + "\\") diff --git a/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs b/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs index 75ccee46..5cda9ece 100644 --- a/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs +++ b/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs @@ -7,11 +7,11 @@ namespace Wabbajack.Lib.CompilationSteps { public class IncludeStubbedConfigFiles : ACompilationStep { - private readonly Compiler _mo2Compiler; + private readonly MO2Compiler _mo2Compiler; public IncludeStubbedConfigFiles(ACompiler compiler) : base(compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; } public override Directive Run(RawSourceFile source) diff --git a/Wabbajack.Lib/CompilationSteps/IncludeTaggedMods.cs b/Wabbajack.Lib/CompilationSteps/IncludeTaggedMods.cs index 76611545..bb3a64e2 100644 --- a/Wabbajack.Lib/CompilationSteps/IncludeTaggedMods.cs +++ b/Wabbajack.Lib/CompilationSteps/IncludeTaggedMods.cs @@ -9,11 +9,11 @@ namespace Wabbajack.Lib.CompilationSteps { private readonly IEnumerable _includeDirectly; private readonly string _tag; - private readonly Compiler _mo2Compiler; + private readonly MO2Compiler _mo2Compiler; public IncludeTaggedMods(ACompiler compiler, string tag) : base(compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; _tag = tag; _includeDirectly = _mo2Compiler.ModInis.Where(kv => { diff --git a/Wabbajack.Lib/CompilationSteps/IncludeThisProfile.cs b/Wabbajack.Lib/CompilationSteps/IncludeThisProfile.cs index 858ca04b..923c24f9 100644 --- a/Wabbajack.Lib/CompilationSteps/IncludeThisProfile.cs +++ b/Wabbajack.Lib/CompilationSteps/IncludeThisProfile.cs @@ -9,11 +9,11 @@ namespace Wabbajack.Lib.CompilationSteps public class IncludeThisProfile : ACompilationStep { private readonly IEnumerable _correctProfiles; - private readonly Compiler _mo2Compiler; + private readonly MO2Compiler _mo2Compiler; public IncludeThisProfile(ACompiler compiler) : base(compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; _correctProfiles = _mo2Compiler.SelectedProfiles.Select(p => Path.Combine("profiles", p) + "\\").ToList(); } diff --git a/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs b/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs index 90c6239e..a820b49b 100644 --- a/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs +++ b/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs @@ -8,11 +8,11 @@ namespace Wabbajack.Lib.CompilationSteps { public class PatchStockESMs : ACompilationStep { - private readonly Compiler _mo2Compiler; + private readonly MO2Compiler _mo2Compiler; public PatchStockESMs(ACompiler compiler) : base(compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; } public override Directive Run(RawSourceFile source) diff --git a/Wabbajack.Lib/IBatchProcessor.cs b/Wabbajack.Lib/IBatchProcessor.cs new file mode 100644 index 00000000..f9625b8e --- /dev/null +++ b/Wabbajack.Lib/IBatchProcessor.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Wabbajack.Common; + +namespace Wabbajack.Lib +{ + /// + /// Wabbajack runs mostly as a batch processor of sorts, we have a list of tasks we need to perform + /// and the Compilers/Installers run throught those tasks one at a time. At any given moment the processor + /// will be using multiple threads to complete that task. This interface defines a common implementation of + /// all reporting functionality of both the compilers and installers. + /// + /// These processors are disposible because they contain WorkQueues which must be properly shutdown to keep + /// from leaking threads. + /// + public interface IBatchProcessor : IDisposable + { + /// + /// The current progress of the entire processing system on a scale of 0.0 to 1.0 + /// + IObservable PercentCompleted { get; } + + /// + /// The current status of the processor as a text string + /// + IObservable TextStatus { get; } + + /// + /// The status of the processor's work queue + /// + IObservable QueueStatus { get; } + + IObservable IsRunning { get; } + + /// + /// Begin processing + /// + Task Begin(); + + /// + /// Terminate any processing currently in progress by the processor. The processor should be disposed of + /// after calling this function as processing cannot be resumed and the tasks may be half completed. + /// Should only be called while IsRunning = true; + /// + void Terminate(); + } +} diff --git a/Wabbajack.Lib/Compiler.cs b/Wabbajack.Lib/MO2Compiler.cs similarity index 97% rename from Wabbajack.Lib/Compiler.cs rename to Wabbajack.Lib/MO2Compiler.cs index 0b66dcc9..9aa041d1 100644 --- a/Wabbajack.Lib/Compiler.cs +++ b/Wabbajack.Lib/MO2Compiler.cs @@ -16,21 +16,17 @@ using Path = Alphaleonis.Win32.Filesystem.Path; namespace Wabbajack.Lib { - public class Compiler : ACompiler + public class MO2Compiler : ACompiler { private string _mo2DownloadsFolder; - - public Dictionary> DirectMatchIndex; public string MO2Folder; public string MO2Profile; - public Compiler(string mo2_folder) + public MO2Compiler(string mo2_folder) { - UpdateTracker = new StatusUpdateTracker(10); - VFS = new Context(Queue) {UpdateTracker = UpdateTracker}; ModManager = ModManager.MO2; MO2Folder = mo2_folder; @@ -39,14 +35,10 @@ namespace Wabbajack.Lib ModListOutputFolder = "output_folder"; ModListOutputFile = MO2Profile + ExtensionManager.Extension; - VFS.ProgressUpdates.Debounce(new TimeSpan(0, 0, 0, 0, 100)) - .Subscribe(itm => _progressUpdates.OnNext(itm)); } public dynamic MO2Ini { get; } - public bool ShowReportWhenFinished { get; set; } = true; - public bool IgnoreMissingFiles { get; set; } public string MO2DownloadsFolder @@ -71,8 +63,9 @@ namespace Wabbajack.Lib public HashSet SelectedProfiles { get; set; } = new HashSet(); - public override bool Compile() + protected override bool _Begin() { + ConfigureProcessor(10); UpdateTracker.Reset(); UpdateTracker.NextStep("Gathering information"); Info("Looking for other profiles"); diff --git a/Wabbajack.Lib/Installer.cs b/Wabbajack.Lib/MO2Installer.cs similarity index 96% rename from Wabbajack.Lib/Installer.cs rename to Wabbajack.Lib/MO2Installer.cs index 8172427f..0620cfb4 100644 --- a/Wabbajack.Lib/Installer.cs +++ b/Wabbajack.Lib/MO2Installer.cs @@ -15,12 +15,10 @@ using Path = Alphaleonis.Win32.Filesystem.Path; namespace Wabbajack.Lib { - public class Installer : AInstaller + public class MO2Installer : AInstaller { - public Installer(string archive, ModList mod_list, string output_folder) + public MO2Installer(string archive, ModList mod_list, string output_folder) { - UpdateTracker = new StatusUpdateTracker(10); - VFS = new Context(Queue) {UpdateTracker = UpdateTracker}; ModManager = ModManager.MO2; ModListArchive = archive; OutputFolder = output_folder; @@ -30,8 +28,9 @@ namespace Wabbajack.Lib public string GameFolder { get; set; } - public override void Install() + protected override bool _Begin() { + ConfigureProcessor(10); var game = GameRegistry.Games[ModList.GameType]; if (GameFolder == null) @@ -44,7 +43,7 @@ namespace Wabbajack.Lib "game location up in the windows registry but were unable to find it, please make sure you launch the game once before running this installer. ", "Could not find game location", MessageBoxButton.OK); Utils.Log("Exiting because we couldn't find the game folder."); - return; + return false; } ValidateGameESMs(); @@ -63,7 +62,7 @@ namespace Wabbajack.Lib MessageBoxImage.Exclamation) == MessageBoxResult.No) { Utils.Log("Existing installation at the request of the user, existing mods folder found."); - return; + return false; } } @@ -97,6 +96,7 @@ namespace Wabbajack.Lib // Removed until we decide if we want this functionality // Nexus devs weren't sure this was a good idea, I (halgari) agree. //AskToEndorse(); + return true; } private void InstallIncludedDownloadMetas() diff --git a/Wabbajack.Lib/ReportBuilder.cs b/Wabbajack.Lib/ReportBuilder.cs index 4a5807c5..06a804a5 100644 --- a/Wabbajack.Lib/ReportBuilder.cs +++ b/Wabbajack.Lib/ReportBuilder.cs @@ -46,9 +46,9 @@ namespace Wabbajack.Lib public void Build(ACompiler c, ModList lst) { - Compiler compiler = null; + MO2Compiler compiler = null; if (lst.ModManager == ModManager.MO2) - compiler = (Compiler) c; + compiler = (MO2Compiler) c; Text($"### {lst.Name} by {lst.Author} - Installation Summary"); Text($"Build with Wabbajack Version {lst.WabbajackVersion}"); diff --git a/Wabbajack.Lib/VortexCompiler.cs b/Wabbajack.Lib/VortexCompiler.cs index ed8552c4..a3cac3e7 100644 --- a/Wabbajack.Lib/VortexCompiler.cs +++ b/Wabbajack.Lib/VortexCompiler.cs @@ -38,9 +38,6 @@ namespace Wabbajack.Lib public VortexCompiler(Game game, string gamePath, string vortexFolder, string downloadsFolder, string stagingFolder) { - UpdateTracker = new StatusUpdateTracker(10); - VFS = new Context(Queue) {UpdateTracker = UpdateTracker}; - ModManager = ModManager.Vortex; Game = game; @@ -54,8 +51,9 @@ namespace Wabbajack.Lib ActiveArchives = new List(); } - public override bool Compile() + protected override bool _Begin() { + ConfigureProcessor(10); if (string.IsNullOrEmpty(ModListName)) ModListName = $"Vortex ModList for {Game.ToString()}"; ModListOutputFile = $"{ModListName}{ExtensionManager.Extension}"; diff --git a/Wabbajack.Lib/VortexInstaller.cs b/Wabbajack.Lib/VortexInstaller.cs index 94f5f949..63e5dc39 100644 --- a/Wabbajack.Lib/VortexInstaller.cs +++ b/Wabbajack.Lib/VortexInstaller.cs @@ -13,8 +13,6 @@ namespace Wabbajack.Lib public VortexInstaller(string archive, ModList modList) { - UpdateTracker = new StatusUpdateTracker(10); - VFS = new Context(Queue) {UpdateTracker = UpdateTracker}; ModManager = ModManager.Vortex; ModListArchive = archive; ModList = modList; @@ -25,8 +23,9 @@ namespace Wabbajack.Lib GameInfo = GameRegistry.Games[ModList.GameType]; } - public override void Install() + protected override bool _Begin() { + ConfigureProcessor(10); Directory.CreateDirectory(DownloadFolder); HashArchives(); @@ -52,6 +51,7 @@ namespace Wabbajack.Lib //InstallIncludedDownloadMetas(); Info("Installation complete! You may exit the program."); + return true; } private void InstallIncludedFiles() diff --git a/Wabbajack.Lib/Wabbajack.Lib.csproj b/Wabbajack.Lib/Wabbajack.Lib.csproj index 8bcb678f..2a7182c4 100644 --- a/Wabbajack.Lib/Wabbajack.Lib.csproj +++ b/Wabbajack.Lib/Wabbajack.Lib.csproj @@ -78,6 +78,7 @@ + @@ -110,7 +111,7 @@ - + @@ -125,7 +126,8 @@ - + + diff --git a/Wabbajack.Lib/zEditIntegration.cs b/Wabbajack.Lib/zEditIntegration.cs index 7991a6f9..ce7b3d7a 100644 --- a/Wabbajack.Lib/zEditIntegration.cs +++ b/Wabbajack.Lib/zEditIntegration.cs @@ -14,11 +14,11 @@ namespace Wabbajack.Lib { public class zEditIntegration { - private static Compiler _mo2Compiler; + private static MO2Compiler _mo2Compiler; public static string FindzEditPath(ACompiler compiler) { - _mo2Compiler = (Compiler) compiler; + _mo2Compiler = (MO2Compiler) compiler; var executables = _mo2Compiler.MO2Ini.customExecutables; if (executables.size == null) return null; @@ -141,7 +141,7 @@ namespace Wabbajack.Lib public string dataFolder; } - public static void VerifyMerges(Compiler compiler) + public static void VerifyMerges(MO2Compiler compiler) { var by_name = compiler.InstallDirectives.ToDictionary(f => f.To); @@ -160,7 +160,7 @@ namespace Wabbajack.Lib } } - public static void GenerateMerges(Installer installer) + public static void GenerateMerges(MO2Installer installer) { installer.ModList .Directives diff --git a/Wabbajack.Test.ListValidation/ListValidation.cs b/Wabbajack.Test.ListValidation/ListValidation.cs index d281357a..c9194fe2 100644 --- a/Wabbajack.Test.ListValidation/ListValidation.cs +++ b/Wabbajack.Test.ListValidation/ListValidation.cs @@ -64,7 +64,7 @@ namespace Wabbajack.Test.ListValidation Log($"Loading {modlist_path}"); - var installer = Installer.LoadFromFile(modlist_path); + var installer = MO2Installer.LoadFromFile(modlist_path); Log($"{installer.Archives.Count} archives to validate"); diff --git a/Wabbajack.Test/ACompilerTest.cs b/Wabbajack.Test/ACompilerTest.cs index f7b7ac52..4197ad33 100644 --- a/Wabbajack.Test/ACompilerTest.cs +++ b/Wabbajack.Test/ACompilerTest.cs @@ -32,18 +32,18 @@ namespace Wabbajack.Test utils.Dispose(); } - protected Compiler ConfigureAndRunCompiler(string profile) + protected MO2Compiler ConfigureAndRunCompiler(string profile) { var compiler = MakeCompiler(); compiler.MO2Profile = profile; compiler.ShowReportWhenFinished = false; - Assert.IsTrue(compiler.Compile()); + Assert.IsTrue(compiler.Begin().Result); return compiler; } - protected Compiler MakeCompiler() + protected MO2Compiler MakeCompiler() { - var compiler = new Compiler(utils.MO2Folder); + var compiler = new MO2Compiler(utils.MO2Folder); return compiler; } protected ModList CompileAndInstall(string profile) @@ -53,13 +53,13 @@ namespace Wabbajack.Test return compiler.ModList; } - protected void Install(Compiler compiler) + protected void Install(MO2Compiler compiler) { - var modlist = Installer.LoadFromFile(compiler.ModListOutputFile); - var installer = new Installer(compiler.ModListOutputFile, modlist, utils.InstallFolder); + var modlist = MO2Installer.LoadFromFile(compiler.ModListOutputFile); + var installer = new MO2Installer(compiler.ModListOutputFile, modlist, utils.InstallFolder); installer.DownloadFolder = utils.DownloadsFolder; installer.GameFolder = utils.GameFolder; - installer.Install(); + installer.Begin().Wait(); } } } diff --git a/Wabbajack.Test/AVortexCompilerTest.cs b/Wabbajack.Test/AVortexCompilerTest.cs index e6e280e8..92aa7a83 100644 --- a/Wabbajack.Test/AVortexCompilerTest.cs +++ b/Wabbajack.Test/AVortexCompilerTest.cs @@ -37,7 +37,7 @@ namespace Wabbajack.Test vortexCompiler.DownloadsFolder = utils.DownloadsFolder; vortexCompiler.StagingFolder = utils.InstallFolder; Directory.CreateDirectory(utils.InstallFolder); - Assert.IsTrue(vortexCompiler.Compile()); + Assert.IsTrue(vortexCompiler.Begin().Result); return vortexCompiler; } @@ -60,13 +60,13 @@ namespace Wabbajack.Test protected void Install(VortexCompiler vortexCompiler) { - var modList = Installer.LoadFromFile(vortexCompiler.ModListOutputFile); - var installer = new Installer(vortexCompiler.ModListOutputFile, modList, utils.InstallFolder) + var modList = MO2Installer.LoadFromFile(vortexCompiler.ModListOutputFile); + var installer = new MO2Installer(vortexCompiler.ModListOutputFile, modList, utils.InstallFolder) { DownloadFolder = utils.DownloadsFolder, GameFolder = utils.GameFolder, }; - installer.Install(); + installer.Begin().Wait(); } } } diff --git a/Wabbajack.Test/EndToEndTests.cs b/Wabbajack.Test/EndToEndTests.cs index afe227f7..7f3229bb 100644 --- a/Wabbajack.Test/EndToEndTests.cs +++ b/Wabbajack.Test/EndToEndTests.cs @@ -73,11 +73,11 @@ namespace Wabbajack.Test if (Directory.Exists(loot_folder)) Directory.Delete(loot_folder, true); - var compiler = new Compiler(utils.InstallFolder); + var compiler = new MO2Compiler(utils.InstallFolder); compiler.MO2DownloadsFolder = Path.Combine(utils.DownloadsFolder); compiler.MO2Profile = profile; compiler.ShowReportWhenFinished = false; - Assert.IsTrue(compiler.Compile()); + Assert.IsTrue(compiler.Begin().Result); } @@ -144,21 +144,21 @@ namespace Wabbajack.Test return compiler.ModList; } - private void Install(Compiler compiler) + private void Install(MO2Compiler compiler) { - var modlist = Installer.LoadFromFile(compiler.ModListOutputFile); - var installer = new Installer(compiler.ModListOutputFile, modlist, utils.InstallFolder); + var modlist = MO2Installer.LoadFromFile(compiler.ModListOutputFile); + var installer = new MO2Installer(compiler.ModListOutputFile, modlist, utils.InstallFolder); installer.DownloadFolder = utils.DownloadsFolder; installer.GameFolder = utils.GameFolder; - installer.Install(); + installer.Begin().Wait(); } - private Compiler ConfigureAndRunCompiler(string profile) + private MO2Compiler ConfigureAndRunCompiler(string profile) { - var compiler = new Compiler(utils.MO2Folder); + var compiler = new MO2Compiler(utils.MO2Folder); compiler.MO2Profile = profile; compiler.ShowReportWhenFinished = false; - Assert.IsTrue(compiler.Compile()); + Assert.IsTrue(compiler.Begin().Result); return compiler; } } diff --git a/Wabbajack.Test/SanityTests.cs b/Wabbajack.Test/SanityTests.cs index 0d83b73d..fc49e42a 100644 --- a/Wabbajack.Test/SanityTests.cs +++ b/Wabbajack.Test/SanityTests.cs @@ -77,8 +77,8 @@ namespace Wabbajack.Test // Update the file and verify that it throws an error. utils.GenerateRandomFileData(game_file, 20); - var exception = Assert.ThrowsException(() => Install(compiler)); - Assert.AreEqual(exception.Message, "Game ESM hash doesn't match, is the ESM already cleaned? Please verify your local game files."); + var exception = Assert.ThrowsException(() => Install(compiler)); + Assert.AreEqual(exception.InnerExceptions.First().Message, "Game ESM hash doesn't match, is the ESM already cleaned? Please verify your local game files."); } diff --git a/Wabbajack/View Models/Compilers/ISubCompilerVM.cs b/Wabbajack/View Models/Compilers/ISubCompilerVM.cs index 4f16ee2b..fd44cc0d 100644 --- a/Wabbajack/View Models/Compilers/ISubCompilerVM.cs +++ b/Wabbajack/View Models/Compilers/ISubCompilerVM.cs @@ -13,6 +13,7 @@ namespace Wabbajack { IReactiveCommand BeginCommand { get; } bool Compiling { get; } + ModlistSettingsEditorVM ModlistSettings { get; } StatusUpdateTracker StatusTracker { get;} void Unload(); diff --git a/Wabbajack/View Models/Compilers/MO2CompilerVM.cs b/Wabbajack/View Models/Compilers/MO2CompilerVM.cs index cd34cfc1..e04cdaa9 100644 --- a/Wabbajack/View Models/Compilers/MO2CompilerVM.cs +++ b/Wabbajack/View Models/Compilers/MO2CompilerVM.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Text; using System.Threading.Tasks; using Wabbajack.Common; @@ -100,10 +101,10 @@ namespace Wabbajack .ObserveOnGuiThread(), execute: async () => { - Compiler compiler; + MO2Compiler compiler; try { - compiler = new Compiler(this.Mo2Folder) + compiler = new MO2Compiler(this.Mo2Folder) { MO2Profile = this.MOProfile, ModListName = this.ModlistSettings.ModListName, @@ -113,6 +114,10 @@ namespace Wabbajack ModListWebsite = this.ModlistSettings.Website, ModListReadme = this.ModlistSettings.ReadMeText.TargetPath, }; + // TODO: USE RX HERE + compiler.TextStatus.Subscribe(Utils.Log); + // TODO: Where do we bind this? + //compiler.QueueStatus.Subscribe(_cpuStatus); } catch (Exception ex) { @@ -120,23 +125,22 @@ namespace Wabbajack Utils.Log($"Compiler error: {ex.ExceptionToString()}"); return; } - await Task.Run(() => + + try { - try - { - this.StatusTracker = compiler.UpdateTracker; - compiler.Compile(); - } - catch (Exception ex) - { - while (ex.InnerException != null) ex = ex.InnerException; - Utils.Log($"Compiler error: {ex.ExceptionToString()}"); - } - finally - { - this.StatusTracker = null; - } - }); + await compiler.Begin(); + } + catch (Exception ex) + { + while (ex.InnerException != null) ex = ex.InnerException; + Utils.Log($"Compiler error: {ex.ExceptionToString()}"); + } + finally + { + this.StatusTracker = null; + compiler.Dispose(); + } + }); this._Compiling = this.BeginCommand.IsExecuting .ToProperty(this, nameof(this.Compiling)); @@ -191,7 +195,7 @@ namespace Wabbajack { try { - var tmp_compiler = new Compiler(this.Mo2Folder); + var tmp_compiler = new MO2Compiler(this.Mo2Folder); this.DownloadLocation.TargetPath = tmp_compiler.MO2DownloadsFolder; } catch (Exception ex) diff --git a/Wabbajack/View Models/Compilers/VortexCompilerVM.cs b/Wabbajack/View Models/Compilers/VortexCompilerVM.cs index f4a2cd9b..289f2528 100644 --- a/Wabbajack/View Models/Compilers/VortexCompilerVM.cs +++ b/Wabbajack/View Models/Compilers/VortexCompilerVM.cs @@ -106,12 +106,11 @@ namespace Wabbajack Utils.Log($"Compiler error: {ex.ExceptionToString()}"); return; } - await Task.Run(() => + await Task.Run(async () => { try { - this.StatusTracker = compiler.UpdateTracker; - compiler.Compile(); + await compiler.Begin(); } catch (Exception ex) { diff --git a/Wabbajack/View Models/InstallerVM.cs b/Wabbajack/View Models/InstallerVM.cs index 1bc4e49a..6fe0b8c4 100644 --- a/Wabbajack/View Models/InstallerVM.cs +++ b/Wabbajack/View Models/InstallerVM.cs @@ -9,6 +9,7 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Imaging; using Wabbajack.Common; @@ -131,7 +132,7 @@ namespace Wabbajack .Select(modListPath => { if (modListPath == null) return default(ModListVM); - var modList = Installer.LoadFromFile(modListPath); + var modList = MO2Installer.LoadFromFile(modListPath); if (modList == null) { MessageBox.Show("Invalid Modlist, or file not found.", "Invalid Modlist", MessageBoxButton.OK, @@ -291,15 +292,15 @@ namespace Wabbajack { this.Installing = true; this.InstallingMode = true; - var installer = new Installer(this.ModListPath, this.ModList.SourceModList, Location.TargetPath) + var installer = new MO2Installer(this.ModListPath, this.ModList.SourceModList, Location.TargetPath) { DownloadFolder = DownloadLocation.TargetPath }; - var th = new Thread(() => + Task.Run(async () => { try { - installer.Install(); + await installer.Begin(); } catch (Exception ex) { @@ -313,11 +314,7 @@ namespace Wabbajack this.Installing = false; } - }) - { - Priority = ThreadPriority.BelowNormal - }; - th.Start(); + }); } } } \ No newline at end of file diff --git a/Wabbajack/View Models/MainWindowVM.cs b/Wabbajack/View Models/MainWindowVM.cs index 2a38d367..3bcf52e3 100644 --- a/Wabbajack/View Models/MainWindowVM.cs +++ b/Wabbajack/View Models/MainWindowVM.cs @@ -83,9 +83,10 @@ namespace Wabbajack // Compile progress updates and populate ObservableCollection /* - WorkQueue.Status + _Compiler.WhenAny(c => c.Value.Compiler.) .ObserveOn(RxApp.TaskpoolScheduler) - .ToObservableChangeSet(x => x.ID) + .ToObservableChangeSet(x => x.) + /* .Batch(TimeSpan.FromMilliseconds(250), RxApp.TaskpoolScheduler) .EnsureUniqueChanges() .ObserveOn(RxApp.MainThreadScheduler)