diff --git a/NeosModLoader/NeosModLoader.csproj b/NeosModLoader/NeosModLoader.csproj
index c4ddc6e..a2ff56c 100644
--- a/NeosModLoader/NeosModLoader.csproj
+++ b/NeosModLoader/NeosModLoader.csproj
@@ -13,7 +13,7 @@
false
net462
512
- 9.0
+ 10.0
enable
true
true
diff --git a/NeosModLoader/Utility/GenericMethodInvoker.cs b/NeosModLoader/Utility/GenericMethodInvoker.cs
new file mode 100644
index 0000000..9266ac9
--- /dev/null
+++ b/NeosModLoader/Utility/GenericMethodInvoker.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NeosModLoader.Utility
+{
+ ///
+ /// Represents the base class for more specific generic method invokers.
+ ///
+ /// The type of the instances that the generic method gets invoked on.
+ /// The type of the generic method's return value. Use object if it depends on the generic type parameters of the method.
+ public abstract class BaseGenericMethodInvoker
+ {
+ private readonly Dictionary concreteMethods = new();
+
+ ///
+ /// Gets the generic who's concrete versions will be in invoked by this generic method invoker.
+ ///
+ public MethodInfo GenericMethod { get; }
+
+ ///
+ /// Creates a new generic method invoker for the given method which may not have generic type parameters.
+ ///
+ /// Use object for if it depends on the generic type parameters of the method.
+ ///
+ /// The generic method to invoke concrete version of.
+ /// Ignores the method lacking generic type parameters if true .
+ internal BaseGenericMethodInvoker(MethodInfo method, bool ignoreLackOfGenericParameters)
+ {
+ if (!ignoreLackOfGenericParameters && !method.ContainsGenericParameters)
+ throw new ArgumentException("Target method must have remaining open type parameters.", nameof(method));
+
+ GenericMethod = method;
+ }
+
+ ///
+ /// Creates a new generic method invoker for the given open generic method.
+ ///
+ /// Use object for if it depends on the generic type parameters of the method.
+ ///
+ /// The generic method to invoke concrete version of.
+ internal BaseGenericMethodInvoker(MethodInfo method) : this(method, false)
+ { }
+
+ ///
+ /// Invokes a concrete version of this invoker's GenericMethod using the given ,
+ /// type parameter and method .
+ ///
+ /// The object instance to invoke the method on. Use null for static methods.
+ /// The generic type parameter definition to create the concrete method with.
+ /// May be ignored if doesn't contain generic parameters.
+ /// The parameters to invoke the method with. May be empty or null if there is none.
+ /// The result of the method invocation.
+ internal TReturn InvokeInternal(TInstance? instance, TypeDefinition definition, params object[]? parameters)
+ {
+ if (!concreteMethods.TryGetValue(definition, out var method))
+ {
+ if (GenericMethod.ContainsGenericParameters)
+ method = GenericMethod.MakeGenericMethod(definition.types);
+ else
+ method = GenericMethod;
+
+ concreteMethods.Add(definition, method);
+ }
+
+ return (TReturn)method.Invoke(instance, parameters);
+ }
+ }
+
+ ///
+ /// Represents a generic method invoker that invokes instance methods with a return value.
+ ///
+ ///
+ public sealed class GenericInstanceMethodInvoker : BaseGenericMethodInvoker
+ {
+ ///
+ /// Creates a new generic method invoker for the given open generic instance method with a return value.
+ ///
+ /// Use object for if it depends on the generic type parameters of the method.
+ ///
+ /// The generic method to invoke concrete version of.
+ public GenericInstanceMethodInvoker(MethodInfo method) : base(method)
+ { }
+
+ internal GenericInstanceMethodInvoker(MethodInfo method, bool ignoreLackOfGenericParameters)
+ : base(method, ignoreLackOfGenericParameters)
+ { }
+
+ ///
+ /// Invokes a concrete version of this invoker's
+ /// GenericMethod
+ /// using the given , type parameter and method .
+ ///
+ /// The object instance to invoke the method on.
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with.
+ /// The result of the method invocation.
+ public TReturn Invoke(TInstance instance, TypeDefinition definition, params object[] parameters)
+ {
+ return InvokeInternal(instance, definition, parameters);
+ }
+ }
+
+ ///
+ /// Represents a generic method invoker that invokes static methods with a return value.
+ ///
+ ///
+ public sealed class GenericStaticMethodInvoker : BaseGenericMethodInvoker
+ {
+ ///
+ /// Creates a new generic method invoker for the given open generic static method with a return value.
+ ///
+ /// Use object for if it depends on the generic type parameters of the method.
+ ///
+ /// The generic method to invoke concrete version of.
+ public GenericStaticMethodInvoker(MethodInfo method) : base(method)
+ { }
+
+ ///
+ /// Invokes a concrete version of this invoker's static
+ /// GenericMethod
+ /// using the given type parameter and method .
+ ///
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with.
+ /// The result of the method invocation.
+ public TReturn Invoke(TypeDefinition definition, params object[] parameters)
+ {
+ return InvokeInternal(null, definition, parameters);
+ }
+ }
+
+ ///
+ /// Represents a generic method invoker that invokes static void methods.
+ ///
+ public sealed class GenericStaticVoidMethodInvoker : BaseGenericMethodInvoker
+ {
+ ///
+ /// Creates a new generic method invoker for the given open generic static void method.
+ ///
+ /// The generic method to invoke concrete version of.
+ public GenericStaticVoidMethodInvoker(MethodInfo method) : base(method)
+ { }
+
+ ///
+ /// Invokes a concrete version of this invoker's static void
+ /// GenericMethod
+ /// using the given type parameter and method .
+ ///
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with.
+ /// The result of the method invocation.
+ public void Invoke(TypeDefinition definition, params object[] parameters)
+ {
+ InvokeInternal(null, definition, parameters);
+ }
+ }
+
+ ///
+ /// Represents a generic method invoker that invokes instance void methods.
+ ///
+ ///
+ public sealed class GenericVoidMethodInvoker : BaseGenericMethodInvoker
+ {
+ ///
+ /// Creates a new generic method invoker for the given open generic instance void method.
+ ///
+ /// The generic method to invoke concrete version of.
+ public GenericVoidMethodInvoker(MethodInfo method) : base(method)
+ { }
+
+ ///
+ /// Invokes a concrete version of this invoker's void
+ /// GenericMethod
+ /// using the given , type parameter and method .
+ ///
+ /// The object instance to invoke the method on.
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with.
+ public void Invoke(TInstance instance, TypeDefinition definition, params object[] parameters)
+ {
+ InvokeInternal(instance, definition, parameters);
+ }
+ }
+}
diff --git a/NeosModLoader/Utility/GenericTypeMethodsInvoker.cs b/NeosModLoader/Utility/GenericTypeMethodsInvoker.cs
new file mode 100644
index 0000000..1069d67
--- /dev/null
+++ b/NeosModLoader/Utility/GenericTypeMethodsInvoker.cs
@@ -0,0 +1,295 @@
+using HarmonyLib;
+using NeosModLoader;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NeosModLoader.Utility
+{
+ ///
+ /// Represents a generic method invoker that invokes potentially generic methods on generic types.
+ ///
+ public sealed class GenericTypeMethodsInvoker
+ {
+ private readonly Dictionary concreteTypes = new();
+
+ ///
+ /// Gets the generic who's concrete versions will have concrete methods invoked by this invoker.
+ ///
+ public Type GenericType { get; }
+
+ ///
+ /// Gets the function that handles resolving passed in s of the GenericType to ones of the concrete one.
+ ///
+ public Func GetGenericMethodOfConcreteType { get; }
+
+ ///
+ /// Creates a new generic type methods invoker with the given generic type and the default concrete method resolving function.
+ ///
+ /// The generic type who's concrete versions will have concrete methods invoked by this invoker.
+ public GenericTypeMethodsInvoker(Type genericType)
+ : this(genericType, GetGenericMethodOfConcreteTypeDefault)
+ { }
+
+ ///
+ /// Creates a new generic type methods invoker with the given generic type and method resolving function.
+ ///
+ /// The generic type who's concrete versions will have concrete methods invoked by this invoker.
+ /// The function that handles resolving passed in s of the GenericType to ones of the concrete one.
+ public GenericTypeMethodsInvoker(Type genericType, Func getGenericMethodOfConcreteType)
+ {
+ GenericType = genericType;
+ GetGenericMethodOfConcreteType = getGenericMethodOfConcreteType;
+ }
+
+ ///
+ /// Invokes a concrete version of the given generic method using the given ,
+ /// type parameters and method on the
+ /// concrete version of this instance's GenericType created with the type parameters.
+ ///
+ /// The invoked method's return type.
+ /// The generic method of the generic type to invoke.
+ /// The generic type parameter definition to create the concrete instance type with.
+ /// The instance to invoke the constructed concrete method on.
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ public TReturn Invoke(MethodInfo method, TypeDefinition instanceDefinition, object instance, TypeDefinition methodDefinition, params object[] parameters)
+ {
+ return (TReturn)InvokeInternal(method, instanceDefinition, instance, methodDefinition, parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given generic method using the given ,
+ /// type parameters and method on the
+ /// concrete version of this instance's GenericType created with the type parameters.
+ ///
+ /// The generic method of the generic type to invoke.
+ /// The generic type parameter definition to create the concrete instance type with.
+ /// The instance to invoke the constructed concrete method on.
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ public object Invoke(MethodInfo method, TypeDefinition instanceDefinition, object instance, TypeDefinition methodDefinition, params object[] parameters)
+ {
+ return InvokeInternal(method, instanceDefinition, instance, methodDefinition, parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given method on the concrete version of this instance's
+ /// GenericType created with the type parameters.
+ ///
+ /// The invoked method's return type.
+ /// The generic method of the generic type to invoke.
+ /// The generic type parameter definition to create the concrete instance type with.
+ /// The instance to invoke the constructed concrete method on.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ public TReturn Invoke(MethodInfo method, TypeDefinition instanceDefinition, object instance, params object[] parameters)
+ {
+ return (TReturn)InvokeInternal(method, instanceDefinition, instance, new TypeDefinition(), parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given static method of the concrete version of this instance's
+ /// GenericType created with the type parameters.
+ ///
+ /// The invoked method's return type.
+ /// The generic method of the generic type to invoke.
+ /// The generic type parameter definition to create the concrete instance type with.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ public TReturn Invoke(MethodInfo method, TypeDefinition instanceDefinition, params object[] parameters)
+ {
+ return (TReturn)InvokeInternal(method, instanceDefinition, null, new TypeDefinition(), parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given method on the concrete version of this instance's
+ /// GenericType created with the type parameters.
+ ///
+ /// The generic method of the generic type to invoke.
+ /// The generic type parameter definition to create the concrete instance type with.
+ /// The instance to invoke the constructed concrete method on.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ public object Invoke(MethodInfo method, TypeDefinition instanceDefinition, object instance, params object[] parameters)
+ {
+ return InvokeInternal(method, instanceDefinition, instance, new TypeDefinition(), parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given static method of the concrete version of this instance's
+ /// GenericType created with the type parameters.
+ ///
+ /// The invoked method's return type.
+ /// The generic method of the generic type to invoke.
+ /// The generic type parameter definition to create the concrete instance type with.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ public object Invoke(MethodInfo method, TypeDefinition instanceDefinition, params object[] parameters)
+ {
+ return InvokeInternal(method, instanceDefinition, null, new TypeDefinition(), parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given method on the concrete version of this instance's
+ /// GenericType created with the given 's type parameters.
+ ///
+ /// The generic method of the generic type to invoke.
+ /// The instance to invoke the constructed concrete method on.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ /// The is not descended from the GenericType .
+ public object Invoke(MethodInfo method, object instance, params object[] parameters)
+ {
+ return InvokeInternal(method, GetMatchingGenericTypeArguments(instance), instance, new TypeDefinition(), parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given generic method using the given ,
+ /// type parameters and method on the
+ /// concrete version of this instance's GenericType created with the given 's type parameters.
+ ///
+ /// The invoked method's return type.
+ /// The generic method of the generic type to invoke.
+ /// The instance to invoke the constructed concrete method on.
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ /// The is not descended from the GenericType .
+ public object Invoke(MethodInfo method, object instance, TypeDefinition methodDefinition, params object[] parameters)
+ {
+ return InvokeInternal(method, GetMatchingGenericTypeArguments(instance), instance, methodDefinition, parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given generic method using the given ,
+ /// type parameters and method on the
+ /// concrete version of this instance's GenericType created with the given 's type parameters.
+ ///
+ /// The invoked method's return type.
+ /// The generic method of the generic type to invoke.
+ /// The instance to invoke the constructed concrete method on.
+ /// The generic type parameter definition to create the concrete method with.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ /// The is not descended from the GenericType .
+ public TReturn Invoke(MethodInfo method, object instance, TypeDefinition methodDefinition, params object[] parameters)
+ {
+ return (TReturn)InvokeInternal(method, GetMatchingGenericTypeArguments(instance), instance, methodDefinition, parameters);
+ }
+
+ ///
+ /// Invokes a concrete version of the given method on the concrete version of this instance's
+ /// GenericType created with the given 's type parameters.
+ ///
+ /// The invoked method's return type.
+ /// The generic method of the generic type to invoke.
+ /// The instance to invoke the constructed concrete method on.
+ /// The parameters to invoke the method with. May be empty if there is none.
+ /// The invoked method's return value.
+ /// The is not descended from the GenericType .
+ public TReturn Invoke(MethodInfo method, object instance, params object[] parameters)
+ {
+ return (TReturn)InvokeInternal(method, GetMatchingGenericTypeArguments(instance), instance, new TypeDefinition(), parameters);
+ }
+
+ private static MethodInfo GetGenericMethodOfConcreteTypeDefault(MethodInfo needleMethod, Type concreteType)
+ {
+ Logger.DebugFuncInternal(() => $"Looking for: {needleMethod.FullDescription()}");
+
+ return concreteType.GetMethods(AccessTools.all)
+ .Single(hayMethod =>
+ {
+ if (hayMethod.Name != needleMethod.Name)
+ return false;
+
+ Logger.DebugFuncInternal(() => $"Testing potential candidate: {hayMethod.FullDescription()}");
+
+ var needleParameters = needleMethod.GetParameters();
+ var hayParameters = hayMethod.GetParameters();
+
+ if (hayParameters.Length != needleParameters.Length)
+ return false;
+
+ for (var i = 0; i < needleParameters.Length; ++i)
+ {
+ //var needleParameter = needleParameters[i];
+ //var hayParameter = hayParameters[i];
+ //var checkType = (hayParameter.ParameterType.IsGenericParameter && needleParameter.ParameterType.IsGenericParameter)
+ // || (!hayParameter.ParameterType.IsGenericParameter && !needleParameter.ParameterType.IsGenericParameter);
+
+ //NeosMod.Msg($"Comparing: {hayParameter.ParameterType} to {needleParameter.ParameterType} => {hayParameter.ParameterType.FullName == needleParameter.ParameterType.FullName}");
+
+ //if (checkType && hayParameter.ParameterType.FullName != needleParameter.ParameterType.FullName)
+ // return false;
+
+ // TODO: Do a proper type check? lol
+ if (hayParameters[i].Name != needleParameters[i].Name)
+ return false;
+ }
+
+ return true;
+ });
+ }
+
+ private Type[] GetMatchingGenericTypeArguments(object instance)
+ {
+ var type = instance.GetType();
+
+ do
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == GenericType)
+ return type.GenericTypeArguments;
+
+ type = type.BaseType;
+ }
+ while (type != null);
+
+ throw new ArgumentException(
+ $"Provided instance [{instance.GetType().FullDescription()}] was not descended from {nameof(GenericType)} [{GenericType.FullDescription()}].",
+ nameof(instance));
+ }
+
+ private object InvokeInternal(MethodInfo method, TypeDefinition instanceTypes, object? instance, TypeDefinition methodTypes, object[] parameters)
+ {
+ if (!concreteTypes.TryGetValue(instanceTypes, out var concreteType))
+ {
+ concreteType = GenericType.MakeGenericType(instanceTypes.types);
+ concreteTypes.Add(instanceTypes, concreteType);
+ }
+
+ var methodInvoker = concreteType.GetMethodInvoker(method, GetGenericMethodOfConcreteType);
+
+ return methodInvoker.InvokeInternal(instance, methodTypes, parameters);
+ }
+
+ private readonly struct ConcreteType
+ {
+ public readonly Dictionary> MethodInvokers = new();
+ public readonly Type Type;
+
+ public ConcreteType(Type type)
+ {
+ Type = type;
+ }
+
+ public static implicit operator ConcreteType(Type type) => new(type);
+
+ public BaseGenericMethodInvoker GetMethodInvoker(MethodInfo genericMethod, Func getMethod)
+ {
+ if (!MethodInvokers.TryGetValue(genericMethod, out var methodInvoker))
+ {
+ methodInvoker = new GenericInstanceMethodInvoker(getMethod(genericMethod, Type), true);
+ MethodInvokers.Add(genericMethod, methodInvoker);
+ }
+
+ return methodInvoker;
+ }
+ }
+ }
+}
diff --git a/NeosModLoader/Utility/TypeDefinition.cs b/NeosModLoader/Utility/TypeDefinition.cs
new file mode 100644
index 0000000..14a2e2b
--- /dev/null
+++ b/NeosModLoader/Utility/TypeDefinition.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace NeosModLoader.Utility
+{
+ public readonly struct TypeDefinition : IEquatable, IEnumerable
+ {
+ internal readonly Type[] types;
+
+ public int Length => types.Length;
+
+ public TypeDefinition(params Type[] types)
+ => this.types = types ?? Array.Empty();
+
+ public static implicit operator TypeDefinition(Type[] types) => new(types);
+
+ public static implicit operator TypeDefinition(Type type)
+ {
+ if (type == null)
+ return new(Array.Empty());
+
+ return new(type);
+ }
+
+ public static bool operator !=(TypeDefinition left, TypeDefinition right) => !(left == right);
+
+ public static bool operator ==(TypeDefinition left, TypeDefinition right) => left.Equals(right);
+
+ public override bool Equals(object obj)
+ {
+ return obj is TypeDefinition definition && Equals(definition);
+ }
+
+ public bool Equals(TypeDefinition other)
+ {
+ return types.SequenceEqual(other.types);
+ }
+
+ public IEnumerator GetEnumerator()
+ => ((IEnumerable)types).GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => types.GetEnumerator();
+
+ public override int GetHashCode()
+ {
+ return unchecked(types.Aggregate(0, (acc, type) => (31 * acc) + type.GetHashCode()));
+ }
+ }
+}