Compiler works, needs a lot of refinement

This commit is contained in:
Timothy Baldridge 2021-10-14 22:20:27 -06:00
parent 51015eaa81
commit c54e6249df
10 changed files with 135 additions and 25 deletions

View File

@ -6,6 +6,7 @@ using Avalonia.Markup.Xaml;
using CefNet; using CefNet;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
using Wabbajack.App.Controls; using Wabbajack.App.Controls;
@ -36,6 +37,10 @@ namespace Wabbajack.App
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()
{ {
var host = Host.CreateDefaultBuilder(Array.Empty<string>()) var host = Host.CreateDefaultBuilder(Array.Empty<string>())
.ConfigureLogging(c =>
{
c.ClearProviders();
})
.ConfigureServices((host, services) => .ConfigureServices((host, services) =>
{ {
services.AddAppServices(); services.AddAppServices();

View File

@ -21,7 +21,7 @@ public class LogViewModel : ViewModelBase, IActivatableViewModel
public LogViewModel(LoggerProvider provider) public LogViewModel(LoggerProvider provider)
{ {
_messages = new SourceCache<LoggerProvider.ILogMessage, long>(m => m.MessageId); _messages = new SourceCache<LoggerProvider.ILogMessage, long>(m => m.MessageId);
_messages.LimitSizeTo(100); //_messages.LimitSizeTo(100);
Activator = new ViewModelActivator(); Activator = new ViewModelActivator();
_provider = provider; _provider = provider;

View File

@ -46,9 +46,9 @@ public class SettingsManager
{ {
if (path.FileExists()) if (path.FileExists())
{ {
await using (var s = path.Open(FileMode.Create, FileAccess.Write)) await using (var s = path.Open(FileMode.Open))
{ {
await JsonSerializer.DeserializeAsync<T>(s, _dtos.Options); return (await JsonSerializer.DeserializeAsync<T>(s, _dtos.Options))!;
} }
} }
} }

View File

@ -1,3 +1,5 @@
using Avalonia.Controls.Mixins;
using ReactiveUI;
using Wabbajack.App.ViewModels; using Wabbajack.App.ViewModels;
using Wabbajack.App.Views; using Wabbajack.App.Views;
@ -8,5 +10,17 @@ public partial class CompilationView : ScreenBase<CompilationViewModel>
public CompilationView() public CompilationView()
{ {
InitializeComponent(); InitializeComponent();
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.StatusText, view => view.StatusText.Text)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.StepsProgress, view => view.StepsProgress.Value, p => p.Value * 1000)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.StepProgress, view => view.StepProgress.Value, p => p.Value * 10000)
.DisposeWith(disposables);
});
} }
} }

View File

@ -1,13 +1,16 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Threading;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Wabbajack.App.Messages; using Wabbajack.App.Messages;
using Wabbajack.App.ViewModels; using Wabbajack.App.ViewModels;
using Wabbajack.Common; using Wabbajack.Common;
using Wabbajack.Compiler; using Wabbajack.Compiler;
using Wabbajack.RateLimiter;
namespace Wabbajack.App.Screens; namespace Wabbajack.App.Screens;
@ -17,6 +20,11 @@ public class CompilationViewModel : ViewModelBase, IReceiverMarker, IReceiver<St
private ACompiler _compiler; private ACompiler _compiler;
private readonly ILogger<CompilationViewModel> _logger; private readonly ILogger<CompilationViewModel> _logger;
[Reactive] public string StatusText { get; set; } = "";
[Reactive] public Percent StepsProgress { get; set; } = Percent.Zero;
[Reactive] public Percent StepProgress { get; set; } = Percent.Zero;
public CompilationViewModel(ILogger<CompilationViewModel> logger, IServiceProvider provider) public CompilationViewModel(ILogger<CompilationViewModel> logger, IServiceProvider provider)
{ {
_logger = logger; _logger = logger;
@ -32,7 +40,15 @@ public class CompilationViewModel : ViewModelBase, IReceiverMarker, IReceiver<St
var compiler = _provider.GetService<MO2Compiler>()!; var compiler = _provider.GetService<MO2Compiler>()!;
compiler.Settings = mo2; compiler.Settings = mo2;
_compiler = compiler; _compiler = compiler;
_compiler.OnStatusUpdate += (sender, update) =>
{
Dispatcher.UIThread.InvokeAsync(() =>
{
StatusText = update.StatusText;
StepsProgress = update.StepsProgress;
StepProgress = update.StepProgress;
});
};
} }
Compile().FireAndForget(); Compile().FireAndForget();
} }
@ -46,6 +62,7 @@ public class CompilationViewModel : ViewModelBase, IReceiverMarker, IReceiver<St
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "During Compilation: {Message}", ex.Message); _logger.LogError(ex, "During Compilation: {Message}", ex.Message);
StatusText = $"ERRORED: {ex.Message}";
} }
} }
} }

View File

@ -3,15 +3,11 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using DynamicData;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Wabbajack.App.Extensions;
using Wabbajack.App.Messages; using Wabbajack.App.Messages;
using Wabbajack.App.Models; using Wabbajack.App.Models;
using Wabbajack.App.ViewModels; using Wabbajack.App.ViewModels;
@ -82,18 +78,29 @@ public class CompilerConfigurationViewModel : ViewModelBase, IReceiverMarker
OutputFolder = KnownFolders.EntryPoint; OutputFolder = KnownFolders.EntryPoint;
this.WhenActivated((CompositeDisposable disposables) => this.WhenActivated(disposables =>
{ {
LoadLastCompilation().FireAndForget(); LoadLastCompilation().FireAndForget();
this.WhenAnyValue(v => v.SettingsFile)
.Subscribe( location =>
{
LoadNewSettingsFile(location).FireAndForget();
})
.DisposeWith(disposables);
}); });
} }
private async Task LoadNewSettingsFile(AbsolutePath location)
{
if (location == default) return;
if (location.FileExists()) await LoadSettings(location);
}
private async Task LoadLastCompilation() private async Task LoadLastCompilation()
{ {
var location = await _settingsManager.Load<AbsolutePath>("last_compilation"); var location = await _settingsManager.Load<AbsolutePath>("last_compilation");
if (location == default) return; SettingsFile = location;
if (location.FileExists()) await LoadSettings(location);
} }
private async Task BeginCompilation() private async Task BeginCompilation()

View File

@ -21,6 +21,7 @@ using Wabbajack.Installer;
using Wabbajack.Networking.WabbajackClientApi; using Wabbajack.Networking.WabbajackClientApi;
using Wabbajack.Paths; using Wabbajack.Paths;
using Wabbajack.Paths.IO; using Wabbajack.Paths.IO;
using Wabbajack.RateLimiter;
using Wabbajack.VFS; using Wabbajack.VFS;
namespace Wabbajack.Compiler namespace Wabbajack.Compiler
@ -50,6 +51,16 @@ namespace Wabbajack.Compiler
private readonly DTOSerializer _dtos; private readonly DTOSerializer _dtos;
public readonly IBinaryPatchCache _patchCache; public readonly IBinaryPatchCache _patchCache;
private long _maxStepProgress = 0;
private int _currentStep = 0;
private string _statusText;
private long _currentStepProgress;
private readonly Stopwatch _updateStopWatch = new();
protected long MaxSteps { get; set; }
public event EventHandler<StatusUpdate> OnStatusUpdate;
public ACompiler(ILogger logger, FileExtractor.FileExtractor extractor, FileHashCache hashCache, Context vfs, TemporaryFileManager manager, CompilerSettings settings, public ACompiler(ILogger logger, FileExtractor.FileExtractor extractor, FileHashCache hashCache, Context vfs, TemporaryFileManager manager, CompilerSettings settings,
ParallelOptions parallelOptions, DownloadDispatcher dispatcher, Client wjClient, IGameLocator locator, DTOSerializer dtos, ParallelOptions parallelOptions, DownloadDispatcher dispatcher, Client wjClient, IGameLocator locator, DTOSerializer dtos,
IBinaryPatchCache patchCache) IBinaryPatchCache patchCache)
@ -70,9 +81,42 @@ namespace Wabbajack.Compiler
_patchOptions = new(); _patchOptions = new();
_sourceFileLinks = new(); _sourceFileLinks = new();
_patchCache = patchCache; _patchCache = patchCache;
_updateStopWatch = new();
} }
public void NextStep(string statusText, long maxStepProgress = 1)
{
_updateStopWatch.Restart();
_maxStepProgress = maxStepProgress;
_currentStep += 1;
_statusText = statusText;
_logger.LogInformation("Compiler Step: {Step}", statusText);
if (OnStatusUpdate != null)
{
OnStatusUpdate(this, new StatusUpdate($"[{_currentStep}/{MaxSteps}] " + statusText, Percent.FactoryPutInRange(_currentStep, MaxSteps),
Percent.Zero));
}
}
public void UpdateProgress(long stepProgress)
{
Interlocked.Add(ref _currentStepProgress, stepProgress);
lock (_updateStopWatch)
{
if (_updateStopWatch.ElapsedMilliseconds < 100) return;
_updateStopWatch.Restart();
}
if (OnStatusUpdate != null)
{
OnStatusUpdate(this, new StatusUpdate(_statusText, Percent.FactoryPutInRange(_currentStep, MaxSteps),
Percent.FactoryPutInRange(_currentStepProgress, _maxStepProgress)));
}
}
public abstract Task<bool> Begin(CancellationToken token); public abstract Task<bool> Begin(CancellationToken token);
public CompilerSettings Settings public CompilerSettings Settings
@ -151,8 +195,10 @@ namespace Wabbajack.Compiler
public async Task<bool> GatherMetaData() public async Task<bool> GatherMetaData()
{ {
_logger.LogInformation("Getting meta data for {count} archives", SelectedArchives.Count); _logger.LogInformation("Getting meta data for {count} archives", SelectedArchives.Count);
NextStep("Gathering Metadata", SelectedArchives.Count);
await SelectedArchives.PDo(_parallelOptions, async a => await SelectedArchives.PDo(_parallelOptions, async a =>
{ {
UpdateProgress(1);
await _dispatcher.FillInMetadata(a); await _dispatcher.FillInMetadata(a);
}); });
@ -162,6 +208,7 @@ namespace Wabbajack.Compiler
protected async Task IndexGameFileHashes() protected async Task IndexGameFileHashes()
{ {
NextStep("Indexing Game Files", 1);
if (_settings.UseGamePaths) if (_settings.UseGamePaths)
{ {
//taking the games in Settings.IncludedGames + currently compiling game so you can eg //taking the games in Settings.IncludedGames + currently compiling game so you can eg
@ -215,6 +262,7 @@ namespace Wabbajack.Compiler
protected async Task CleanInvalidArchivesAndFillState() protected async Task CleanInvalidArchivesAndFillState()
{ {
NextStep("Cleaning Invalid Archives", 1);
var remove = await IndexedArchives.PMap(_parallelOptions, async a => var remove = await IndexedArchives.PMap(_parallelOptions, async a =>
{ {
try try
@ -248,6 +296,7 @@ namespace Wabbajack.Compiler
protected async Task InferMetas(CancellationToken token) protected async Task InferMetas(CancellationToken token)
{ {
async Task<bool> HasInvalidMeta(AbsolutePath filename) async Task<bool> HasInvalidMeta(AbsolutePath filename)
{ {
var metaName = filename.WithExtension(Ext.Meta); var metaName = filename.WithExtension(Ext.Meta);
@ -275,6 +324,7 @@ namespace Wabbajack.Compiler
.Where(f => f.FileExists()) .Where(f => f.FileExists())
.ToList(); .ToList();
NextStep("InferMetas", toFind.Count);
if (toFind.Count == 0) if (toFind.Count == 0)
{ {
return; return;
@ -284,6 +334,7 @@ namespace Wabbajack.Compiler
await toFind.PDoAll(async f => await toFind.PDoAll(async f =>
{ {
UpdateProgress(1);
var vf = _vfs.Index.ByRootPath[f]; var vf = _vfs.Index.ByRootPath[f];
var archives = await _wjClient.GetArchivesForHash(vf.Hash); var archives = await _wjClient.GetArchivesForHash(vf.Hash);
@ -315,6 +366,7 @@ namespace Wabbajack.Compiler
protected async Task ExportModList(CancellationToken token) protected async Task ExportModList(CancellationToken token)
{ {
NextStep("Exporting Modlist");
_logger.LogInformation("Exporting ModList to {location}", _settings.OutputFile); _logger.LogInformation("Exporting ModList to {location}", _settings.OutputFile);
// Modify readme and ModList image to relative paths if they exist // Modify readme and ModList image to relative paths if they exist
@ -382,8 +434,6 @@ namespace Wabbajack.Compiler
/// </summary> /// </summary>
protected async Task BuildPatches(CancellationToken token) protected async Task BuildPatches(CancellationToken token)
{ {
_logger.LogInformation("Gathering patch files");
var toBuild = InstallDirectives.OfType<PatchedFromArchive>() var toBuild = InstallDirectives.OfType<PatchedFromArchive>()
.Where(p => _patchOptions.GetValueOrDefault(p, Array.Empty<VirtualFile>()).Length > 0) .Where(p => _patchOptions.GetValueOrDefault(p, Array.Empty<VirtualFile>()).Length > 0)
.SelectMany(p => _patchOptions[p].Select(c => new PatchedFromArchive .SelectMany(p => _patchOptions[p].Select(c => new PatchedFromArchive
@ -395,6 +445,7 @@ namespace Wabbajack.Compiler
})) }))
.ToArray(); .ToArray();
NextStep("Generating Patches", toBuild.Length);
if (toBuild.Length == 0) if (toBuild.Length == 0)
{ {
return; return;
@ -406,6 +457,7 @@ namespace Wabbajack.Compiler
await _vfs.Extract( indexed.Keys.ToHashSet(), await _vfs.Extract( indexed.Keys.ToHashSet(),
async (vf, sf) => async (vf, sf) =>
{ {
UpdateProgress(1);
// For each, extract the destination // For each, extract the destination
var matches = indexed[vf]; var matches = indexed[vf];
foreach (var match in matches) foreach (var match in matches)
@ -484,6 +536,7 @@ namespace Wabbajack.Compiler
public async Task GenerateManifest() public async Task GenerateManifest()
{ {
NextStep("Generating Manifest");
var manifest = new Manifest(ModList); var manifest = new Manifest(ModList);
await using var of = _settings.OutputFile.Open(FileMode.Create, FileAccess.Write); await using var of = _settings.OutputFile.Open(FileMode.Create, FileAccess.Write);
await _dtos.Serialize(manifest, of); await _dtos.Serialize(manifest, of);
@ -491,6 +544,7 @@ namespace Wabbajack.Compiler
public async Task GatherArchives() public async Task GatherArchives()
{ {
NextStep("Gathering Archives");
_logger.LogInformation("Building a list of archives based on the files required"); _logger.LogInformation("Building a list of archives based on the files required");
var hashes = InstallDirectives.OfType<FromArchive>() var hashes = InstallDirectives.OfType<FromArchive>()
@ -502,7 +556,11 @@ namespace Wabbajack.Compiler
.ToDictionary(f => f.Key, f => f.First()); .ToDictionary(f => f.Key, f => f.First());
SelectedArchives.Clear(); SelectedArchives.Clear();
SelectedArchives.AddRange(await hashes.PMap(_parallelOptions, hash => ResolveArchive(hash, archives)).ToList()); SelectedArchives.AddRange(await hashes.PMap(_parallelOptions, hash =>
{
UpdateProgress(1);
return ResolveArchive(hash, archives);
}).ToList());
} }
public async Task<Archive> ResolveArchive(Hash hash, IDictionary<Hash, IndexedArchive> archives) public async Task<Archive> ResolveArchive(Hash hash, IDictionary<Hash, IndexedArchive> archives)
@ -596,11 +654,13 @@ namespace Wabbajack.Compiler
.GroupBy(f => _sourceFileLinks[f].File) .GroupBy(f => _sourceFileLinks[f].File)
.ToDictionary(k => k.Key); .ToDictionary(k => k.Key);
NextStep("Inlining Files");
if (grouped.Count == 0) return; if (grouped.Count == 0) return;
await _vfs.Extract(grouped.Keys.ToHashSet(), async (vf, sfn) => await _vfs.Extract(grouped.Keys.ToHashSet(), async (vf, sfn) =>
{ {
UpdateProgress(1);
await using var stream = await sfn.GetStream(); await using var stream = await sfn.GetStream();
var id = await IncludeFile(stream); var id = await IncludeFile(stream, token);
foreach (var file in grouped[vf]) foreach (var file in grouped[vf])
{ {
file.SourceDataID = id; file.SourceDataID = id;

View File

@ -35,7 +35,7 @@ namespace Wabbajack.Compiler
public static readonly HashSet<Extension> SupportedBSAs = new[] {".bsa", ".ba2"} public static readonly HashSet<Extension> SupportedBSAs = new[] {".bsa", ".ba2"}
.Select(s => new Extension(s)).ToHashSet(); .Select(s => new Extension(s)).ToHashSet();
public static HashSet<Extension> ConfigFileExtensions = new[]{".json", ".ini", ".yml", ".xml", ".yaml"}.Select(s => new Extension(s)).ToHashSet(); public static HashSet<Extension> ConfigFileExtensions = new[]{".json", ".ini", ".yml", ".xml", ".yaml", ".compiler_settings", ".mo2_compiler_settings"}.Select(s => new Extension(s)).ToHashSet();
public static HashSet<Extension> ESPFileExtensions = new []{ ".esp", ".esm", ".esl"}.Select(s => new Extension(s)).ToHashSet(); public static HashSet<Extension> ESPFileExtensions = new []{ ".esp", ".esm", ".esl"}.Select(s => new Extension(s)).ToHashSet();
public static HashSet<Extension> AssetFileExtensions = new[] {".dds", ".tga", ".nif", ".psc", ".pex"}.Select(s => new Extension(s)).ToHashSet(); public static HashSet<Extension> AssetFileExtensions = new[] {".dds", ".tga", ".nif", ".psc", ".pex"}.Select(s => new Extension(s)).ToHashSet();

View File

@ -29,6 +29,7 @@ namespace Wabbajack.Compiler
Client wjClient, IGameLocator locator, DTOSerializer dtos, IBinaryPatchCache patchCache) : Client wjClient, IGameLocator locator, DTOSerializer dtos, IBinaryPatchCache patchCache) :
base(logger, extractor, hashCache, vfs, manager, settings, parallelOptions, dispatcher, wjClient, locator, dtos, patchCache) base(logger, extractor, hashCache, vfs, manager, settings, parallelOptions, dispatcher, wjClient, locator, dtos, patchCache)
{ {
MaxSteps = 14;
} }
public AbsolutePath MO2ModsFolder => Settings.Source.Combine(Consts.MO2ModFolderName); public AbsolutePath MO2ModsFolder => Settings.Source.Combine(Consts.MO2ModFolderName);
@ -52,11 +53,13 @@ namespace Wabbajack.Compiler
var roots = new List<AbsolutePath> {Settings.Source, Settings.Downloads}; var roots = new List<AbsolutePath> {Settings.Source, Settings.Downloads};
roots.AddRange(Settings.OtherGames.Append(Settings.Game).Select(g => _locator.GameLocation(g))); roots.AddRange(Settings.OtherGames.Append(Settings.Game).Select(g => _locator.GameLocation(g)));
await _vfs.AddRoots(roots, token); NextStep("Add Roots", 1);
await _vfs.AddRoots(roots, token); // Step 1
await InferMetas(token); await InferMetas(token); // Step 2
await _vfs.AddRoot(Settings.Downloads, token); NextStep("Add Download Roots", 1);
await _vfs.AddRoot(Settings.Downloads, token); // Step 3
// Find all Downloads // Find all Downloads
IndexedArchives = await Settings.Downloads.EnumerateFiles() IndexedArchives = await Settings.Downloads.EnumerateFiles()
@ -69,7 +72,6 @@ namespace Wabbajack.Compiler
Meta = await f.WithExtension(Ext.Meta).ReadAllTextAsync() Meta = await f.WithExtension(Ext.Meta).ReadAllTextAsync()
}).ToList(); }).ToList();
await IndexGameFileHashes(); await IndexGameFileHashes();
IndexedArchives = IndexedArchives.DistinctBy(a => a.File.AbsoluteName).ToList(); IndexedArchives = IndexedArchives.DistinctBy(a => a.File.AbsoluteName).ToList();
@ -117,10 +119,15 @@ namespace Wabbajack.Compiler
var stack = MakeStack(); var stack = MakeStack();
var results = await AllFiles.PMap(_parallelOptions, f => RunStack(stack, f)).ToList(); NextStep("Running Compilation Stack", AllFiles.Count);
var results = await AllFiles.PMap(_parallelOptions, f =>
{
UpdateProgress(1);
return RunStack(stack, f);
}).ToList();
NextStep("Updating Extra files");
// Add the extra files that were generated by the stack // Add the extra files that were generated by the stack
results = results.Concat(ExtraFiles).ToList(); results = results.Concat(ExtraFiles).ToList();
var noMatch = results.OfType<NoMatch>().ToArray(); var noMatch = results.OfType<NoMatch>().ToArray();
@ -176,9 +183,11 @@ namespace Wabbajack.Compiler
private async Task RunValidation(ModList modList) private async Task RunValidation(ModList modList)
{ {
NextStep("Validating Archives", modList.Archives.Length);
var allowList = await _wjClient.LoadDownloadAllowList(); var allowList = await _wjClient.LoadDownloadAllowList();
foreach (var archive in modList.Archives) foreach (var archive in modList.Archives)
{ {
UpdateProgress(1);
if (!_dispatcher.IsAllowed(archive, allowList)) if (!_dispatcher.IsAllowed(archive, allowList))
{ {
_logger.LogCritical("Archive {name}, {primaryKeyString} is not allowed", archive.Name, _logger.LogCritical("Archive {name}, {primaryKeyString} is not allowed", archive.Name,

View File

@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Wabbajack.Common;
using Wabbajack.Compiler; using Wabbajack.Compiler;
using Wabbajack.Downloaders; using Wabbajack.Downloaders;
using Wabbajack.Downloaders.GameFile; using Wabbajack.Downloaders.GameFile;