MO2Compiler nullability enabled

This commit is contained in:
Justin Swanson 2020-04-09 16:05:07 -05:00
parent a29eb93caf
commit 4e1a32caac
5 changed files with 91 additions and 43 deletions

View File

@ -21,6 +21,9 @@ namespace Wabbajack
return ret;
}
/// <summary>
/// Adds the given values to the dictionary. If a key already exists, it will throw an exception
/// </summary>
public static void Add<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull
{
@ -30,6 +33,9 @@ namespace Wabbajack
}
}
/// <summary>
/// Adds the given values to the dictionary. If a key already exists, it will be replaced
/// </summary>
public static void Set<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull
{
@ -38,5 +44,15 @@ namespace Wabbajack
dict[val.Key] = val.Value;
}
}
/// <summary>
/// Clears the dictionary and adds the given values
/// </summary>
public static void SetTo<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
where K : notnull
{
dict.Clear();
dict.Set(vals);
}
}
}

View File

@ -33,5 +33,32 @@ namespace Wabbajack
yield return next;
foreach (var itm in coll) yield return itm;
}
/// <summary>
/// Converts and filters a nullable enumerable to a non-nullable enumerable
/// </summary>
public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> e)
where T : class
{
// Filter out nulls
return e.Where(e => e != null)
// Cast to non nullable type
.Select(e => e!);
}
/// <summary>
/// Selects items that are castable to the desired type
/// </summary>
/// <typeparam name="T">Type of the original enumerable to cast from</typeparam>
/// <typeparam name="R">Type to attempt casting to</typeparam>
/// <param name="e">Enumerable to process</param>
/// <returns>Enumerable with only objects that were castable</returns>
public static IEnumerable<R> WhereCastable<T, R>(this IEnumerable<T> e)
where T : class
where R : T
{
return e.Where(e => e is R)
.Select(e => (R)e);
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Wabbajack.Common
{
public static class ListExt
{
public static void SetTo<T>(this List<T> list, IEnumerable<T> rhs)
{
list.Clear();
list.AddRange(rhs);
}
}
}

View File

@ -43,9 +43,9 @@ namespace Wabbajack.Lib
public bool IgnoreMissingFiles { get; set; }
public ICollection<Archive> SelectedArchives = new List<Archive>();
public List<Directive> InstallDirectives = new List<Directive>();
public List<RawSourceFile> AllFiles = new List<RawSourceFile>();
public readonly List<Archive> SelectedArchives = new List<Archive>();
public readonly List<Directive> InstallDirectives = new List<Directive>();
public readonly List<RawSourceFile> AllFiles = new List<RawSourceFile>();
public ModList ModList = new ModList();
public List<IndexedArchive> IndexedArchives = new List<IndexedArchive>();
@ -222,7 +222,7 @@ namespace Wabbajack.Lib
.GroupBy(f => f.File.Hash)
.ToDictionary(f => f.Key, f => f.First());
SelectedArchives = await hashes.PMap(Queue, hash => ResolveArchive(hash, archives));
SelectedArchives.SetTo(await hashes.PMap(Queue, hash => ResolveArchive(hash, archives)));
}
public async Task<Archive> ResolveArchive(Hash hash, IDictionary<Hash, IndexedArchive> archives)

View File

@ -20,12 +20,12 @@ using File = Alphaleonis.Win32.Filesystem.File;
using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo;
using Game = Wabbajack.Common.Game;
using Path = Alphaleonis.Win32.Filesystem.Path;
#nullable enable
namespace Wabbajack.Lib
{
public class MO2Compiler : ACompiler
{
private AbsolutePath _mo2DownloadsFolder;
public AbsolutePath MO2Folder;
@ -38,7 +38,7 @@ namespace Wabbajack.Lib
public override AbsolutePath GamePath { get; }
public GameMetaData CompilingGame { get; set; }
public GameMetaData CompilingGame { get; }
public override AbsolutePath ModListOutputFolder => ((RelativePath)"output_folder").RelativeToEntryPoint();
@ -48,6 +48,17 @@ namespace Wabbajack.Lib
Consts.LocalAppDataPath.Combine(
$"vfs_compile_cache-{Path.Combine((string)MO2Folder ?? "Unknown", "ModOrganizer.exe").StringSha256Hex()}.bin");
public dynamic MO2Ini { get; }
public static AbsolutePath GetTypicalDownloadsFolder(AbsolutePath mo2Folder) => mo2Folder.Combine("downloads");
public AbsolutePath MO2ProfileDir => MO2Folder.Combine("profiles", MO2Profile);
public ConcurrentBag<Directive> ExtraFiles { get; } = new ConcurrentBag<Directive>();
public Dictionary<AbsolutePath, dynamic> ModInis { get; } = new Dictionary<AbsolutePath, dynamic>();
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
public MO2Compiler(AbsolutePath mo2Folder, string mo2Profile, AbsolutePath outputFile)
: base(steps: 20)
{
@ -60,8 +71,6 @@ namespace Wabbajack.Lib
ModListOutputFile = outputFile;
}
public dynamic MO2Ini { get; }
public AbsolutePath MO2DownloadsFolder
{
get
@ -76,16 +85,6 @@ namespace Wabbajack.Lib
set => _mo2DownloadsFolder = value;
}
public static AbsolutePath GetTypicalDownloadsFolder(AbsolutePath mo2Folder) => mo2Folder.Combine("downloads");
public AbsolutePath MO2ProfileDir => MO2Folder.Combine("profiles", MO2Profile);
internal UserStatus User { get; private set; }
public ConcurrentBag<Directive> ExtraFiles { get; private set; }
public Dictionary<AbsolutePath, dynamic> ModInis { get; private set; }
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
protected override async Task<bool> _Begin(CancellationToken cancel)
{
if (cancel.IsCancellationRequested) return false;
@ -127,6 +126,11 @@ namespace Wabbajack.Lib
if (lootPath.Exists)
{
if (CompilingGame.MO2Name == null)
{
throw new ArgumentException("Compiling game had no MO2 name specified.");
}
var lootGameDirs = new []
{
CompilingGame.MO2Name, // most of the games use the MO2 name
@ -217,10 +221,9 @@ namespace Wabbajack.Lib
.GroupBy(f => f.Hash)
.ToDictionary(f => f.Key, f => f.AsEnumerable());
AllFiles = mo2Files.Concat(gameFiles)
AllFiles.SetTo(mo2Files.Concat(gameFiles)
.Concat(lootFiles)
.DistinctBy(f => f.Path)
.ToList();
.DistinctBy(f => f.Path));
Info($"Found {AllFiles.Count} files to build into mod list");
@ -240,13 +243,10 @@ namespace Wabbajack.Lib
Error($"Found {dups.Count} duplicates, exiting");
}
ExtraFiles = new ConcurrentBag<Directive>();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Loading INIs");
ModInis = MO2Folder.Combine(Consts.MO2ModFolderName)
ModInis.SetTo(MO2Folder.Combine(Consts.MO2ModFolderName)
.EnumerateDirectories()
.Select(f =>
{
@ -255,7 +255,7 @@ namespace Wabbajack.Lib
return metaPath.Exists ? (mod_name: f, metaPath.LoadIniFile()) : default;
})
.Where(f => f.Item1 != default)
.ToDictionary(f => f.Item1, f => f.Item2);
.Select(f => new KeyValuePair<AbsolutePath, dynamic>(f.Item1, f.Item2)));
if (cancel.IsCancellationRequested) return false;
var stack = MakeStack();
@ -271,7 +271,7 @@ namespace Wabbajack.Lib
PrintNoMatches(noMatch);
if (CheckForNoMatchExit(noMatch)) return false;
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList();
InstallDirectives.SetTo(results.Where(i => !(i is IgnoredDirectly)));
Info("Getting Nexus api_key, please click authorize if a browser window appears");
@ -338,7 +338,7 @@ namespace Wabbajack.Lib
{
return a;
}
})).Where(a => a != null).ToHashSet();
})).NotNull().ToHashSet();
if (remove.Count == 0)
return;
@ -390,7 +390,6 @@ namespace Wabbajack.Lib
});
}
private async Task IncludeArchiveMetadata()
{
Utils.Log($"Including {SelectedArchives.Count} .meta files for downloads");
@ -412,13 +411,12 @@ namespace Wabbajack.Lib
/// </summary>
private void ResetMembers()
{
AllFiles = null;
InstallDirectives = null;
SelectedArchives = null;
ExtraFiles = null;
AllFiles.Clear();
InstallDirectives.Clear();
SelectedArchives.Clear();
ExtraFiles.Clear();
}
/// <summary>
/// Fills in the Patch fields in files that require them
/// </summary>
@ -488,8 +486,7 @@ namespace Wabbajack.Lib
return returnStream;
}
Error($"Couldn't load data for {to}");
return null;
throw new ArgumentException($"Couldn't load data for {to}");
}
public override IEnumerable<ICompilationStep> GetStack()
@ -568,12 +565,5 @@ namespace Wabbajack.Lib
new DropAll(this)
};
}
public class IndexedFileMatch
{
public IndexedArchive Archive;
public IndexedArchiveEntry Entry;
public DateTime LastModified;
}
}
}