Browse Source

Removed old load sequence calculation and cleaned up warnings

pull/94/head
Anairkoen Schno 3 years ago
parent
commit
83c6897f1a
Signed by: DaNike GPG Key ID: BEFB74D5F3FC4387
3 changed files with 56 additions and 139 deletions
  1. +10
    -106
      IPA.Loader/Loader/Features/Feature.cs
  2. +34
    -22
      IPA.Loader/Loader/PluginLoader.cs
  3. +12
    -11
      IPA.Loader/Utilities/EnumerableExtensions.cs

+ 10
- 106
IPA.Loader/Loader/Features/Feature.cs View File

@ -1,9 +1,8 @@
using Mono.Cecil;
#nullable enable
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Diagnostics.CodeAnalysis;
#if NET3 #if NET3
using Net3_Proxy; using Net3_Proxy;
#endif #endif
@ -35,7 +34,7 @@ namespace IPA.Loader.Features
/// This should also be set whenever either <see cref="BeforeInit"/> returns false. /// This should also be set whenever either <see cref="BeforeInit"/> returns false.
/// </summary> /// </summary>
/// <value>the message to show when the feature is marked invalid</value> /// <value>the message to show when the feature is marked invalid</value>
public virtual string InvalidMessage { get; protected set; }
public virtual string? InvalidMessage { get; protected set; }
/// <summary> /// <summary>
/// Called before a plugin's `Init` method is called. This will not be called if there is no `Init` method. This should never throw an exception. An exception will abort the loading of the plugin with an error. /// Called before a plugin's `Init` method is called. This will not be called if there is no `Init` method. This should never throw an exception. An exception will abort the loading of the plugin with an error.
@ -65,6 +64,7 @@ namespace IPA.Loader.Features
// TODO: rework features to take arguments as JSON objects // TODO: rework features to take arguments as JSON objects
[SuppressMessage("Nullability", "CS8618", Justification = "Reset sets those fields.")]
static Feature() static Feature()
{ {
Reset(); Reset();
@ -72,18 +72,18 @@ namespace IPA.Loader.Features
internal static void Reset() internal static void Reset()
{ {
featureTypes = new Dictionary<string, Type>
featureTypes = new()
{ {
{ "IPA.DefineFeature", typeof(DefineFeature) } { "IPA.DefineFeature", typeof(DefineFeature) }
}; };
featureDelcarers = new Dictionary<string, PluginMetadata>
featureDelcarers = new()
{ {
{ "IPA.DefineFeature", null } { "IPA.DefineFeature", null }
}; };
} }
private static Dictionary<string, Type> featureTypes; private static Dictionary<string, Type> featureTypes;
private static Dictionary<string, PluginMetadata> featureDelcarers;
private static Dictionary<string, PluginMetadata?> featureDelcarers;
internal static bool HasFeature(string name) => featureTypes.ContainsKey(name); internal static bool HasFeature(string name) => featureTypes.ContainsKey(name);
@ -123,7 +123,7 @@ namespace IPA.Loader.Features
} }
} }
internal string FeatureName;
internal string FeatureName = null!;
internal class Instance internal class Instance
{ {
@ -139,8 +139,8 @@ namespace IPA.Loader.Features
type = null; type = null;
} }
private Type type;
public bool TryGetDefiningPlugin(out PluginMetadata plugin)
private Type? type;
public bool TryGetDefiningPlugin(out PluginMetadata? plugin)
{ {
return featureDelcarers.TryGetValue(Name, out plugin); return featureDelcarers.TryGetValue(Name, out plugin);
} }
@ -173,101 +173,5 @@ namespace IPA.Loader.Features
return result; return result;
} }
} }
/*
// returns false with both outs null for no such feature
internal static bool TryParseFeature(string featureString, PluginMetadata plugin,
out Feature feature, out Exception failException, out bool featureValid, out Instance parsed,
Instance? preParsed = null)
{
failException = null;
feature = null;
featureValid = false;
if (preParsed == null)
{
var builder = new StringBuilder();
string name = null;
var parameters = new List<string>();
bool escape = false;
int parens = 0;
bool removeWhitespace = true;
foreach (var chr in featureString)
{
if (escape)
{
builder.Append(chr);
escape = false;
}
else
{
switch (chr)
{
case '\\':
escape = true;
break;
case '(':
parens++;
if (parens != 1) goto default;
removeWhitespace = true;
name = builder.ToString();
builder.Clear();
break;
case ')':
parens--;
if (parens != 0) goto default;
goto case ',';
case ',':
if (parens > 1) goto default;
parameters.Add(builder.ToString());
builder.Clear();
removeWhitespace = true;
break;
default:
if (removeWhitespace && !char.IsWhiteSpace(chr))
removeWhitespace = false;
if (!removeWhitespace)
builder.Append(chr);
break;
}
}
}
if (name == null)
name = builder.ToString();
parsed = new Instance(name, parameters.ToArray());
if (parens != 0)
{
failException = new Exception("Malformed feature definition");
return false;
}
}
else
parsed = preParsed.Value;
if (!featureTypes.TryGetValue(parsed.Name, out var featureType))
return false;
try
{
if (!(Activator.CreateInstance(featureType) is Feature aFeature))
{
failException = new InvalidCastException("Feature type not a subtype of Feature");
return false;
}
featureValid = aFeature.Initialize(plugin, TODO);
feature = aFeature;
return true;
}
catch (Exception e)
{
failException = e;
return false;
}
}*/
} }
} }

+ 34
- 22
IPA.Loader/Loader/PluginLoader.cs View File

@ -1,4 +1,5 @@
using IPA.Config;
#nullable enable
using IPA.Config;
using IPA.Loader.Features; using IPA.Loader.Features;
using IPA.Logging; using IPA.Logging;
using IPA.Utilities; using IPA.Utilities;
@ -32,7 +33,7 @@ namespace IPA.Loader
internal partial class PluginLoader internal partial class PluginLoader
{ {
internal static PluginMetadata SelfMeta;
internal static PluginMetadata SelfMeta = null!;
internal static Task LoadTask() => internal static Task LoadTask() =>
TaskEx.Run(() => TaskEx.Run(() =>
@ -40,6 +41,9 @@ namespace IPA.Loader
YeetIfNeeded(); YeetIfNeeded();
LoadMetadata(); LoadMetadata();
// old loader system
#if false
Resolve(); Resolve();
InitFeatures(); InitFeatures();
ComputeLoadOrder(); ComputeLoadOrder();
@ -47,6 +51,7 @@ namespace IPA.Loader
FilterWithoutFiles(); FilterWithoutFiles();
ResolveDependencies(); ResolveDependencies();
#endif
}); });
internal static void YeetIfNeeded() internal static void YeetIfNeeded()
@ -68,10 +73,10 @@ namespace IPA.Loader
} }
} }
internal static List<PluginMetadata> PluginsMetadata = new List<PluginMetadata>();
internal static List<PluginMetadata> DisabledPlugins = new List<PluginMetadata>();
internal static List<PluginMetadata> PluginsMetadata = new();
internal static List<PluginMetadata> DisabledPlugins = new();
private static readonly Regex embeddedTextDescriptionPattern = new Regex(@"#!\[(.+)\]", RegexOptions.Compiled | RegexOptions.Singleline);
private static readonly Regex embeddedTextDescriptionPattern = new(@"#!\[(.+)\]", RegexOptions.Compiled | RegexOptions.Singleline);
internal static void LoadMetadata() internal static void LoadMetadata()
{ {
@ -130,7 +135,7 @@ namespace IPA.Loader
foreach (var resource in pluginModule.Resources) foreach (var resource in pluginModule.Resources)
{ {
const string manifestSuffix = ".manifest.json"; const string manifestSuffix = ".manifest.json";
if (!(resource is EmbeddedResource embedded) ||
if (resource is not EmbeddedResource embedded ||
!embedded.Name.EndsWith(manifestSuffix)) continue; !embedded.Name.EndsWith(manifestSuffix)) continue;
pluginNs = embedded.Name.Substring(0, embedded.Name.Length - manifestSuffix.Length); pluginNs = embedded.Name.Substring(0, embedded.Name.Length - manifestSuffix.Length);
@ -297,6 +302,7 @@ namespace IPA.Loader
} }
} }
#region Ignore stuff
/// <summary> /// <summary>
/// An enum that represents several categories of ignore reasons that the loader may encounter. /// An enum that represents several categories of ignore reasons that the loader may encounter.
/// </summary> /// </summary>
@ -372,15 +378,15 @@ namespace IPA.Loader
/// Gets the textual description of the particular ignore reason. This will typically /// Gets the textual description of the particular ignore reason. This will typically
/// include details about why the plugin was ignored, if it is present. /// include details about why the plugin was ignored, if it is present.
/// </summary> /// </summary>
public string ReasonText { get; internal set; }
public string? ReasonText { get; internal set; }
/// <summary> /// <summary>
/// Gets the <see cref="Exception"/> that caused this plugin to be ignored, if any. /// Gets the <see cref="Exception"/> that caused this plugin to be ignored, if any.
/// </summary> /// </summary>
public Exception Error { get; internal set; }
public Exception? Error { get; internal set; }
/// <summary> /// <summary>
/// Gets the metadata of the plugin that this ignore was related to, if any. /// Gets the metadata of the plugin that this ignore was related to, if any.
/// </summary> /// </summary>
public PluginMetadata RelatedTo { get; internal set; }
public PluginMetadata? RelatedTo { get; internal set; }
/// <summary> /// <summary>
/// Initializes an <see cref="IgnoreReason"/> with the provided data. /// Initializes an <see cref="IgnoreReason"/> with the provided data.
/// </summary> /// </summary>
@ -388,7 +394,7 @@ namespace IPA.Loader
/// <param name="reasonText">the textual description of this ignore reason, if any</param> /// <param name="reasonText">the textual description of this ignore reason, if any</param>
/// <param name="error">the <see cref="Exception"/> that caused this <see cref="IgnoreReason"/>, if any</param> /// <param name="error">the <see cref="Exception"/> that caused this <see cref="IgnoreReason"/>, if any</param>
/// <param name="relatedTo">the <see cref="PluginMetadata"/> this reason is related to, if any</param> /// <param name="relatedTo">the <see cref="PluginMetadata"/> this reason is related to, if any</param>
public IgnoreReason(Reason reason, string reasonText = null, Exception error = null, PluginMetadata relatedTo = null)
public IgnoreReason(Reason reason, string? reasonText = null, Exception? error = null, PluginMetadata? relatedTo = null)
{ {
Reason = reason; Reason = reason;
ReasonText = reasonText; ReasonText = reasonText;
@ -412,10 +418,10 @@ namespace IPA.Loader
public override int GetHashCode() public override int GetHashCode()
{ {
int hashCode = 778404373; int hashCode = 778404373;
hashCode = hashCode * -1521134295 + Reason.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(ReasonText);
hashCode = hashCode * -1521134295 + EqualityComparer<Exception>.Default.GetHashCode(Error);
hashCode = hashCode * -1521134295 + EqualityComparer<PluginMetadata>.Default.GetHashCode(RelatedTo);
hashCode = (hashCode * -1521134295) + Reason.GetHashCode();
hashCode = (hashCode * -1521134295) + ReasonText?.GetHashCode() ?? 0;
hashCode = (hashCode * -1521134295) + Error?.GetHashCode() ?? 0;
hashCode = (hashCode * -1521134295) + RelatedTo?.GetHashCode() ?? 0;
return hashCode; return hashCode;
} }
@ -437,13 +443,15 @@ namespace IPA.Loader
public static bool operator !=(IgnoreReason left, IgnoreReason right) public static bool operator !=(IgnoreReason left, IgnoreReason right)
=> !(left == right); => !(left == right);
} }
#endregion
internal partial class PluginLoader internal partial class PluginLoader
{ {
// keep track of these for the updater; it should still be able to update mods not loaded // keep track of these for the updater; it should still be able to update mods not loaded
// the thing -> the reason // the thing -> the reason
internal static Dictionary<PluginMetadata, IgnoreReason> ignoredPlugins = new Dictionary<PluginMetadata, IgnoreReason>();
internal static Dictionary<PluginMetadata, IgnoreReason> ignoredPlugins = new();
#if false
internal static void Resolve() internal static void Resolve()
{ // resolves duplicates and conflicts, etc { // resolves duplicates and conflicts, etc
PluginsMetadata.Sort((a, b) => b.Version.CompareTo(a.Version)); PluginsMetadata.Sort((a, b) => b.Version.CompareTo(a.Version));
@ -706,6 +714,7 @@ namespace IPA.Loader
DisabledConfig.Instance.Changed(); DisabledConfig.Instance.Changed();
PluginsMetadata = metadata; PluginsMetadata = metadata;
} }
#endif
internal static void InitFeatures() internal static void InitFeatures()
{ {
@ -726,7 +735,7 @@ namespace IPA.Loader
} }
else else
{ // this is literally any other feature, so we want to delay its initialization { // this is literally any other feature, so we want to delay its initialization
meta.UnloadedFeatures.Add(feature);
_ = meta.UnloadedFeatures.Add(feature);
} }
} }
} }
@ -740,10 +749,13 @@ namespace IPA.Loader
{ {
if (plugin != meta) if (plugin != meta)
{ // if the feature is not applied to the defining feature { // if the feature is not applied to the defining feature
meta.LoadsAfter.Add(plugin);
_ = meta.LoadsAfter.Add(plugin);
} }
plugin.CreateFeaturesWhenLoaded.Add(feature);
if (plugin != null)
{
plugin.CreateFeaturesWhenLoaded.Add(feature);
}
} }
else else
{ {
@ -776,11 +788,11 @@ namespace IPA.Loader
internal static void Load(PluginMetadata meta) internal static void Load(PluginMetadata meta)
{ {
if (meta.Assembly == null && meta.PluginType != null)
if (meta is { Assembly: null, PluginType: not null })
meta.Assembly = Assembly.LoadFrom(meta.File.FullName); meta.Assembly = Assembly.LoadFrom(meta.File.FullName);
} }
internal static PluginExecutor InitPlugin(PluginMetadata meta, IEnumerable<PluginMetadata> alreadyLoaded)
internal static PluginExecutor? InitPlugin(PluginMetadata meta, IEnumerable<PluginMetadata> alreadyLoaded)
{ {
if (meta.Manifest.GameVersion != UnityGame.GameVersion) if (meta.Manifest.GameVersion != UnityGame.GameVersion)
Logger.loader.Warn($"Mod {meta.Name} developed for game version {meta.Manifest.GameVersion}, so it may not work properly."); Logger.loader.Warn($"Mod {meta.Name} developed for game version {meta.Manifest.GameVersion}, so it may not work properly.");
@ -870,7 +882,7 @@ namespace IPA.Loader
else else
{ {
feature.AppliedTo.InternalFeatures.Add(inst); feature.AppliedTo.InternalFeatures.Add(inst);
feature.AppliedTo.UnloadedFeatures.Remove(feature);
_ = feature.AppliedTo.UnloadedFeatures.Remove(feature);
} }
} }
meta.CreateFeaturesWhenLoaded.Clear(); // if a plugin is loaded twice, for the moment, we don't want to create the feature twice meta.CreateFeaturesWhenLoaded.Clear(); // if a plugin is loaded twice, for the moment, we don't want to create the feature twice
@ -905,7 +917,7 @@ namespace IPA.Loader
if (exec != null) if (exec != null)
{ {
list.Add(exec); list.Add(exec);
loaded.Add(meta);
_ = loaded.Add(meta);
} }
} }
catch (Exception e) catch (Exception e)


+ 12
- 11
IPA.Loader/Utilities/EnumerableExtensions.cs View File

@ -1,4 +1,5 @@
using System;
#nullable enable
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -33,7 +34,7 @@ namespace IPA.Utilities
this.first = first; this.first = first;
} }
public PrependEnumerator GetEnumerator() => new PrependEnumerator(this);
public PrependEnumerator GetEnumerator() => new(this);
public struct PrependEnumerator : IEnumerator<T> public struct PrependEnumerator : IEnumerator<T>
{ {
@ -45,12 +46,12 @@ namespace IPA.Utilities
this.enumerable = enumerable; this.enumerable = enumerable;
restEnum = enumerable.rest.GetEnumerator(); restEnum = enumerable.rest.GetEnumerator();
state = 0; state = 0;
Current = default;
Current = default!;
} }
public T Current { get; private set; } public T Current { get; private set; }
object IEnumerator.Current => Current;
object? IEnumerator.Current => Current;
public void Dispose() => restEnum.Dispose(); public void Dispose() => restEnum.Dispose();
@ -109,7 +110,7 @@ namespace IPA.Utilities
this.last = last; this.last = last;
} }
public AppendEnumerator GetEnumerator() => new AppendEnumerator(this);
public AppendEnumerator GetEnumerator() => new(this);
public struct AppendEnumerator : IEnumerator<T> public struct AppendEnumerator : IEnumerator<T>
{ {
@ -121,12 +122,12 @@ namespace IPA.Utilities
this.enumerable = enumerable; this.enumerable = enumerable;
restEnum = enumerable.rest.GetEnumerator(); restEnum = enumerable.rest.GetEnumerator();
state = 0; state = 0;
Current = default;
Current = default!;
} }
public T Current { get; private set; } public T Current { get; private set; }
object IEnumerator.Current => Current;
object? IEnumerator.Current => Current;
public void Dispose() => restEnum.Dispose(); public void Dispose() => restEnum.Dispose();
@ -170,8 +171,8 @@ namespace IPA.Utilities
/// <typeparam name="T">the type of the enumeration</typeparam> /// <typeparam name="T">the type of the enumeration</typeparam>
/// <param name="self">the enumeration to filter</param> /// <param name="self">the enumeration to filter</param>
/// <returns>a filtered enumerable</returns> /// <returns>a filtered enumerable</returns>
public static IEnumerable<T> NonNull<T>(this IEnumerable<T> self) where T : class
=> self.Where(o => o != null);
public static IEnumerable<T> NonNull<T>(this IEnumerable<T?> self) where T : class
=> self.Where(o => o != null)!;
/// <summary> /// <summary>
/// LINQ-style extension method that filters <see langword="null"/> elements out of an enumeration based on a converter. /// LINQ-style extension method that filters <see langword="null"/> elements out of an enumeration based on a converter.
@ -181,7 +182,7 @@ namespace IPA.Utilities
/// <param name="self">the enumeration to filter</param> /// <param name="self">the enumeration to filter</param>
/// <param name="pred">the predicate to select for filtering</param> /// <param name="pred">the predicate to select for filtering</param>
/// <returns>a filtered enumerable</returns> /// <returns>a filtered enumerable</returns>
public static IEnumerable<T> NonNull<T, U>(this IEnumerable<T> self, Func<T, U> pred) where U : class
public static IEnumerable<T> NonNull<T, U>(this IEnumerable<T> self, Func<T, U?> pred) where U : class
=> self.Where(o => pred(o) != null); => self.Where(o => pred(o) != null);
/// <summary> /// <summary>
@ -191,7 +192,7 @@ namespace IPA.Utilities
/// <param name="self">the enumeration to filter</param> /// <param name="self">the enumeration to filter</param>
/// <returns>a filtered enumerable</returns> /// <returns>a filtered enumerable</returns>
public static IEnumerable<T> NonNull<T>(this IEnumerable<T?> self) where T : struct public static IEnumerable<T> NonNull<T>(this IEnumerable<T?> self) where T : struct
=> self.Where(o => o != null).Select(o => o.Value);
=> self.Where(o => o != null).Select(o => o!.Value);
/// <summary> /// <summary>
/// LINQ-style extension method that filters <see langword="null"/> elements out of an enumeration based on a converter to a nullable type. /// LINQ-style extension method that filters <see langword="null"/> elements out of an enumeration based on a converter to a nullable type.


Loading…
Cancel
Save