Address code review

This commit is contained in:
aldelaro5 committed 2024-08-20 15:13:10 -04:00
1 parent 952c74f6fe
commit 4d47c3cd39
14 files changed
+97 -69

No files matched your search

+31 -14
View File
@@ -23,9 +23,10 @@ public abstract class BaseChainloader<TPlugin>
/// Analyzes the given type definition and attempts to convert it to a valid <see cref="PluginInfo" />
/// </summary>
/// <param name="type">Type definition to analyze.</param>
/// <param name="loadContext">The load context of the plugin</param>
/// <param name="assemblyLocation">The filepath of the assembly, to keep as metadata.</param>
/// <returns>If the type represent a valid plugin, returns a <see cref="PluginInfo" /> instance. Otherwise, return null.</returns>
public static PluginInfo ToPluginInfo<T>(TypeDefinition type, IPluginLoader loader, string assemblyLocation)
public static PluginInfo ToPluginInfo<T>(TypeDefinition type, IPluginLoadContext loadContext, string assemblyLocation)
{
if (type.IsInterface || type.IsAbstract)
return null;
@@ -42,11 +43,11 @@ public abstract class BaseChainloader<TPlugin>
}
if (type.IsSubtypeOf(typeof(BasePluginProvider)))
return GetPluginInfo(type, BepInPluginProvider.FromCecilType(type), loader, assemblyLocation);
return GetPluginInfo(type, BepInPlugin.FromCecilType(type), loader, assemblyLocation);
return GetPluginInfo(type, BepInPluginProvider.FromCecilType(type), loadContext, assemblyLocation);
return GetPluginInfo(type, BepInPlugin.FromCecilType(type), loadContext, assemblyLocation);
}
private static PluginInfo GetPluginInfo(TypeDefinition type, BepInPlugin metadata, IPluginLoader loader, string assemblyLocation)
private static PluginInfo GetPluginInfo(TypeDefinition type, BepInPlugin metadata, IPluginLoadContext loadContext, string assemblyLocation)
{
// Perform checks that will prevent the plugin from being loaded in ALL cases
if (metadata == null)
@@ -91,7 +92,7 @@ public abstract class BaseChainloader<TPlugin>
TypeName = type.FullName,
TargettedBepInExVersion = bepinVersion,
Location = assemblyLocation,
Loader = loader
LoadContext = loadContext
};
}
@@ -123,18 +124,22 @@ public abstract class BaseChainloader<TPlugin>
/// <summary>
/// List of all <see cref="PluginInfo" /> instances of all plugins loaded via the chainloader.
/// If a plugin fails to load, it is removed. This list only contains the plugins that were actually
/// loaded and have their dependencies satisfied.
/// </summary>
public Dictionary<string, PluginInfo> Plugins { get; } = new();
/// <summary>
/// List of all <see cref="PluginInfo" /> instances of all providers loaded via the chainloader.
/// If a provider fails to load, it is removed. This list only contains the providers that were actually
/// loaded and have their dependencies satisfied.
/// </summary>
public Dictionary<string, PluginInfo> Providers { get; } = new();
/// <summary>
/// List of all <see cref="IPluginLoader" /> discovered from all the providers
/// List of all <see cref="IPluginLoadContext" /> discovered from all the providers
/// </summary>
public List<IPluginLoader> Loaders { get; } = new();
public List<IPluginLoadContext> LoadContexts { get; } = new();
/// <summary>
/// Collection of error chainloader messages that occured during plugin loading.
@@ -142,6 +147,16 @@ public abstract class BaseChainloader<TPlugin>
/// </summary>
public List<string> DependencyErrors { get; } = new();
/// <summary>
/// Occurs after a plugin provider is loaded.
/// </summary>
public event Action<PluginInfo> ProviderLoaded;
/// <summary>
/// Occurs after all plugins providers are loaded.
/// </summary>
public event Action FinishedProviders;
/// <summary>
/// Occurs after a plugin is loaded.
/// </summary>
@@ -323,12 +338,13 @@ public abstract class BaseChainloader<TPlugin>
var builtinProviders = DiscoverPluginProvidersFrom(Paths.BepInExAssemblyDirectory);
var providers = DiscoverPluginProvidersFrom(Paths.PluginProviderPath);
Logger.Log(LogLevel.Info, $"{providers.Count + builtinProviders.Count} " +
$"meta plugin{(providers.Count + builtinProviders.Count == 1 ? "" : "s")} to load");
$"plugins provider{(providers.Count + builtinProviders.Count == 1 ? "" : "s")} to load");
LoadPlugins(builtinProviders, provider: true);
LoadPlugins(providers, provider: true);
FinishedProviders?.Invoke();
var plugins = TypeLoader.GetPluginsFromLoaders(Loaders, ToPluginInfo<TPlugin>, HasBepinPluginType<BepInPlugin>, "chainloader");
var plugins = TypeLoader.GetPluginsFromLoadContexts(LoadContexts, ToPluginInfo<TPlugin>, HasBepinPluginType<BepInPlugin>, "chainloader");
Logger.Log(LogLevel.Info, $"{plugins.Count} plugin{(plugins.Count == 1 ? "" : "s")} to load");
LoadPlugins(plugins, provider: false);
Finished?.Invoke();
@@ -373,10 +389,10 @@ public abstract class BaseChainloader<TPlugin>
ass = Assembly.LoadFrom(plugin.Location);
loadedAssemblies[plugin.Location] = ass;
}
else if (!provider && !loadedAssemblies.TryGetValue(plugin.Loader.AssemblyIdentifier, out ass))
else if (!provider && !loadedAssemblies.TryGetValue(plugin.LoadContext.AssemblyIdentifier, out ass))
{
ass = Assembly.Load(plugin.Loader.GetAssemblyData());
loadedAssemblies[plugin.Loader.AssemblyIdentifier] = ass;
ass = Assembly.Load(plugin.LoadContext.GetAssemblyData());
loadedAssemblies[plugin.LoadContext.AssemblyIdentifier] = ass;
}
existingPlugins[plugin.Metadata.GUID] = plugin;
@@ -388,13 +404,14 @@ public abstract class BaseChainloader<TPlugin>
plugin.Instance = Activator.CreateInstance(type);
AppDomain.CurrentDomain.AssemblyResolve += (_, args) => ((BasePluginProvider)plugin.Instance).ResolveAssembly(args.Name);
var requestedPluginsStream = ((BasePluginProvider)plugin.Instance).GetPlugins();
Loaders.AddRange(requestedPluginsStream);
LoadContexts.AddRange(requestedPluginsStream);
ProviderLoaded?.Invoke(plugin);
}
else
{
plugin.Instance = LoadPlugin(plugin, ass);
PluginLoaded?.Invoke(plugin);
}
PluginLoaded?.Invoke(plugin);
}
catch (Exception ex)
{
+18 -18
View File
@@ -128,7 +128,7 @@ public static class TypeLoader
/// selector.
/// </returns>
public static Dictionary<string, List<T>> FindPluginTypes<T>(string directory,
Func<TypeDefinition, IPluginLoader, string, T> typeSelector,
Func<TypeDefinition, IPluginLoadContext, string, T> typeSelector,
Func<AssemblyDefinition, bool> assemblyFilter = null,
string cacheName = null)
where T : ICacheable, new()
@@ -176,7 +176,7 @@ public static class TypeLoader
/// Looks up assemblies using the given loaders and locates all types that can be loaded and collects their metadata.
/// </summary>
/// <typeparam name="T">The specific base type to search for.</typeparam>
/// <param name="loaders">The loaders to obtain the assemblies from.</param>
/// <param name="loadContexts">The load contexts to obtain the assemblies from.</param>
/// <param name="typeSelector">A function to check if a type should be selected and to build the type metadata.</param>
/// <param name="assemblyFilter">A filter function to quickly determine if the assembly can be loaded.</param>
/// <param name="cacheName">The name of the cache to get cached types from.</param>
@@ -184,10 +184,10 @@ public static class TypeLoader
/// A dictionary of all assemblies in the directory and the list of type metadatas of types that match the
/// selector.
/// </returns>
public static List<T> GetPluginsFromLoaders<T>(IList<IPluginLoader> loaders,
Func<TypeDefinition, IPluginLoader, string, T> typeSelector,
Func<AssemblyDefinition, bool> assemblyFilter = null,
string cacheName = null)
public static List<T> GetPluginsFromLoadContexts<T>(IList<IPluginLoadContext> loadContexts,
Func<TypeDefinition, IPluginLoadContext, string, T> typeSelector,
Func<AssemblyDefinition, bool> assemblyFilter = null,
string cacheName = null)
where T : ICacheable, new()
{
var result = new Dictionary<string, List<T>>();
@@ -197,27 +197,27 @@ public static class TypeLoader
if (cacheName != null)
cache = LoadAssemblyCache<T>(cacheName);
foreach (IPluginLoader pluginLoader in loaders)
foreach (IPluginLoadContext loadContext in loadContexts)
{
IList<T> plugins;
if (cache != null && pluginLoader.AssemblyHash != null &&
cache.TryGetValue(pluginLoader.AssemblyIdentifier, out var cacheEntry) &&
pluginLoader.AssemblyHash == cacheEntry.Hash)
if (cache != null && loadContext.AssemblyHash != null &&
cache.TryGetValue(loadContext.AssemblyIdentifier, out var cacheEntry) &&
loadContext.AssemblyHash == cacheEntry.Hash)
{
plugins = cacheEntry.CacheItems;
}
else
{
var assemblyData = pluginLoader.GetAssemblyData();
var assemblyData = loadContext.GetAssemblyData();
using var memory = new MemoryStream(assemblyData);
plugins = ExamineStream(typeSelector, assemblyFilter, memory, pluginLoader, null);
plugins = ExamineStream(typeSelector, assemblyFilter, memory, loadContext, null);
}
foreach (T pluginInfo in plugins)
{
if (!result.ContainsKey(pluginLoader.AssemblyIdentifier))
result[pluginLoader.AssemblyIdentifier] = new();
result[pluginLoader.AssemblyIdentifier].Add(pluginInfo);
if (!result.ContainsKey(loadContext.AssemblyIdentifier))
result[loadContext.AssemblyIdentifier] = new();
result[loadContext.AssemblyIdentifier].Add(pluginInfo);
}
}
@@ -227,10 +227,10 @@ public static class TypeLoader
return result.SelectMany(x => x.Value).ToList();
}
private static List<T> ExamineStream<T>(Func<TypeDefinition, IPluginLoader, string, T> typeSelector,
private static List<T> ExamineStream<T>(Func<TypeDefinition, IPluginLoadContext, string, T> typeSelector,
Func<AssemblyDefinition, bool> assemblyFilter,
MemoryStream dllMs,
IPluginLoader loader,
IPluginLoadContext loadContext,
string location)
where T : ICacheable, new()
{
@@ -243,7 +243,7 @@ public static class TypeLoader
}
var matches = ass.MainModule.Types
.Select(t => typeSelector(t, loader, location))
.Select(t => typeSelector(t, loadContext, location))
.Where(t => t != null).ToList();
return matches;
}
+1 -1
View File
@@ -89,7 +89,7 @@ public class BepInPluginProvider : BepInPlugin
{
}
internal static BepInPluginProvider FromCecilType(TypeDefinition td)
internal new static BepInPluginProvider FromCecilType(TypeDefinition td)
{
var attr = MetadataHelper.GetCustomAttributes<BepInPluginProvider>(td, false).FirstOrDefault();
+2 -2
View File
@@ -55,8 +55,8 @@ public abstract class BasePluginProvider
/// <summary>
/// Obtains a list of assemblies containing plugins to load
/// </summary>
/// <returns>A list of loaders, one per assembly</returns>
public abstract IList<IPluginLoader> GetPlugins();
/// <returns>A list of load context, one per assembly</returns>
public abstract IList<IPluginLoadContext> GetPlugins();
/// <summary>
/// A custom assembly resolver that can be used by this provider to resolve assemblies
@@ -2,9 +2,9 @@
/// <summary>
/// An interface that allows to dynamically load and track an assembly
/// which can be obtained from a provided
/// which can be obtained from a provider
/// </summary>
public interface IPluginLoader
public interface IPluginLoadContext
{
/// <summary>
/// An identifier that uniquely identifies an assembly from a provider no matter its revision
+4 -4
View File
@@ -18,9 +18,9 @@ public class PluginInfo : ICacheable
public BepInPlugin Metadata { get; internal set; }
/// <summary>
/// The loader used to load this plugin, null if it is a provider
/// The load context used to load this plugin, null if it is a provider
/// </summary>
public IPluginLoader Loader { get; internal set; }
public IPluginLoadContext LoadContext { get; internal set; }
/// <summary>
/// Collection of <see cref="BepInProcess" /> attributes that describe what processes the plugin can run on.
@@ -52,7 +52,7 @@ public class PluginInfo : ICacheable
/// <inheritdoc />
public override string ToString() => $"{Metadata?.Name} {Metadata?.Version}";
public virtual void Save(BinaryWriter bw)
void ICacheable.Save(BinaryWriter bw)
{
bw.Write(TypeName);
@@ -78,7 +78,7 @@ public class PluginInfo : ICacheable
bw.Write(TargettedBepInExVersion.ToString(4));
}
public virtual void Load(BinaryReader br)
void ICacheable.Load(BinaryReader br)
{
TypeName = br.ReadString();
@@ -2,7 +2,7 @@
namespace BepInEx.PatcherProvider;
public class BepInExPatcherLoader : IPluginLoader
public class BepInExPatcherLoadContext : IPluginLoadContext
{
public string AssemblyIdentifier { get; internal set; }
public string AssemblyHash { get; internal set; }
@@ -12,15 +12,15 @@ public class BepInExPatcherProvider : BasePatcherProvider
{
private static readonly Dictionary<string, string> AssemblyLocationsByFilename = new();
public override IList<IPluginLoader> GetPatchers()
public override IList<IPluginLoadContext> GetPatchers()
{
var loaders = new List<IPluginLoader>();
var loadContexts = new List<IPluginLoadContext>();
foreach (var dll in Directory.GetFiles(Path.GetFullPath(Paths.PatcherPluginPath), "*.dll", SearchOption.AllDirectories))
{
try
{
AssemblyLocationsByFilename.Add(Path.GetFileNameWithoutExtension(dll), Path.GetDirectoryName(dll));
loaders.Add(new BepInExPatcherLoader
loadContexts.Add(new BepInExPatcherLoadContext
{
AssemblyHash = File.GetLastWriteTimeUtc(dll).ToString("O"),
AssemblyIdentifier = dll
@@ -32,7 +32,7 @@ public class BepInExPatcherProvider : BasePatcherProvider
}
}
return loaders;
return loadContexts;
}
public override Assembly ResolveAssembly(string name)
@@ -2,7 +2,7 @@
namespace BepInEx.PluginProvider;
public class BepInExPluginLoader : IPluginLoader
public class BepInExPluginLoadContext : IPluginLoadContext
{
public string AssemblyIdentifier { get; internal set; }
public string AssemblyHash { get; internal set; }
@@ -11,15 +11,15 @@ public class BepInExPluginProvider : BasePluginProvider
{
private static readonly Dictionary<string, string> AssemblyLocationsByFilename = new();
public override IList<IPluginLoader> GetPlugins()
public override IList<IPluginLoadContext> GetPlugins()
{
var loaders = new List<IPluginLoader>();
var loadContexts = new List<IPluginLoadContext>();
foreach (var dll in Directory.GetFiles(Path.GetFullPath(Paths.PluginPath), "*.dll", SearchOption.AllDirectories))
{
try
{
AssemblyLocationsByFilename.Add(Path.GetFileNameWithoutExtension(dll), Path.GetDirectoryName(dll));
loaders.Add(new BepInExPluginLoader
loadContexts.Add(new BepInExPluginLoadContext
{
AssemblyHash = File.GetLastWriteTimeUtc(dll).ToString("O"),
AssemblyIdentifier = dll
@@ -31,7 +31,7 @@ public class BepInExPluginProvider : BasePluginProvider
}
}
return loaders;
return loadContexts;
}
public override Assembly ResolveAssembly(string name)
@@ -25,7 +25,7 @@ public class AssemblyPatcher : IDisposable
private Func<byte[], string, Assembly> assemblyLoader;
private readonly List<IPluginLoader> loaders = new();
private readonly List<IPluginLoadContext> loadContexts = new();
private readonly List<BasePatcherProvider> providers = new();
public AssemblyPatcher(Func<byte[], string, Assembly> assemblyLoader)
@@ -69,7 +69,7 @@ public class AssemblyPatcher : IDisposable
AppDomain.CurrentDomain.AssemblyResolve -= PatcherProvidersAssemblyResolver;
}
private PatcherPluginMetadata ToPatcherPlugin<T>(TypeDefinition type, IPluginLoader loader, string assemblyPath)
private PatcherPluginMetadata ToPatcherPlugin<T>(TypeDefinition type, IPluginLoadContext loadContext, string assemblyPath)
{
if (type.IsInterface || type.IsAbstract && !type.IsSealed)
return null;
@@ -86,11 +86,11 @@ public class AssemblyPatcher : IDisposable
}
if (type.IsSubtypeOf(typeof(BasePatcherProvider)))
return GetPatcherInfo(type, loader, PatcherProviderPluginInfoAttribute.FromCecilType(type));
return GetPatcherInfo(type, loader, PatcherPluginInfoAttribute.FromCecilType(type));
return GetPatcherInfo(type, loadContext, PatcherProviderPluginInfoAttribute.FromCecilType(type));
return GetPatcherInfo(type, loadContext, PatcherPluginInfoAttribute.FromCecilType(type));
}
private PatcherPluginMetadata GetPatcherInfo(TypeDefinition type, IPluginLoader loader, PatcherPluginInfoAttribute metadata)
private PatcherPluginMetadata GetPatcherInfo(TypeDefinition type, IPluginLoadContext loadContext, PatcherPluginInfoAttribute metadata)
{
// Perform checks that will prevent the plugin from being loaded in ALL cases
if (metadata == null)
@@ -121,7 +121,7 @@ public class AssemblyPatcher : IDisposable
return new PatcherPluginMetadata
{
TypeName = type.FullName,
Loader = loader
LoadContext = loadContext
};
}
@@ -164,7 +164,7 @@ public class AssemblyPatcher : IDisposable
var instance = (BasePatcherProvider) Activator.CreateInstance(type);
providers.Add(instance);
loaders.AddRange(instance.GetPatchers());
loadContexts.AddRange(instance.GetPatchers());
}
catch (Exception e)
{
@@ -174,7 +174,7 @@ public class AssemblyPatcher : IDisposable
}
}
var patchers = TypeLoader.GetPluginsFromLoaders(loaders, ToPatcherPlugin<BasePatcher>, HasPatcherType<BasePatcher>);
var patchers = TypeLoader.GetPluginsFromLoadContexts(loadContexts, ToPatcherPlugin<BasePatcher>, HasPatcherType<BasePatcher>);
var sortedPatchers = new List<PatchDefinition>();
@@ -182,7 +182,7 @@ public class AssemblyPatcher : IDisposable
foreach (var patcherPlugin in patchers)
{
var ass = Assembly.Load(patcherPlugin.Loader.GetAssemblyData());
var ass = Assembly.Load(patcherPlugin.LoadContext.GetAssemblyData());
var patchDefinitions = LoadPatcherPlugin(ass, patcherPlugin);
sortedPatchers.AddRange(patchDefinitions);
@@ -196,11 +196,22 @@ public class AssemblyPatcher : IDisposable
private Assembly PatcherProvidersAssemblyResolver(object sender, ResolveEventArgs args)
{
foreach (BasePatcherProvider provider in providers)
BasePatcherProvider currentProvider = null;
try
{
var ass = provider.ResolveAssembly(args.Name);
if (ass != null)
return ass;
foreach (BasePatcherProvider provider in providers)
{
currentProvider = provider;
var ass = provider.ResolveAssembly(args.Name);
if (ass != null)
return ass;
}
}
catch (Exception e)
{
Logger.Log(LogLevel.Error, $"The assembly resolver of the patcher provider [{currentProvider?.Info.GUID}] " +
$"threw an exception while resolving {args.Name}: {e}");
return null;
}
return null;
@@ -91,7 +91,7 @@ public class PatcherProviderPluginInfoAttribute : PatcherPluginInfoAttribute {
{
}
internal static PatcherProviderPluginInfoAttribute FromCecilType(TypeDefinition td)
internal new static PatcherProviderPluginInfoAttribute FromCecilType(TypeDefinition td)
{
var attr = MetadataHelper.GetCustomAttributes<PatcherProviderPluginInfoAttribute>(td, false).FirstOrDefault();
@@ -39,8 +39,8 @@ public abstract class BasePatcherProvider
/// <summary>
/// Obtains a list of assemblies containing patchers to load
/// </summary>
/// <returns>A list of loaders, one per assembly</returns>
public abstract IList<IPluginLoader> GetPatchers();
/// <returns>A list of load context, one per assembly</returns>
public abstract IList<IPluginLoadContext> GetPatchers();
/// <summary>
/// A custom assembly resolver that can be used by this provider to resolve assemblies
@@ -14,9 +14,9 @@ internal class PatcherPluginMetadata : ICacheable
public string TypeName { get; set; } = string.Empty;
/// <summary>
/// The loader used to load this patcher, null if it is a provider
/// The load context used to load this patcher, null if it is a provider
/// </summary>
public IPluginLoader Loader { get; set; }
public IPluginLoadContext LoadContext { get; set; }
/// <inheritdoc />
public void Save(BinaryWriter bw) => bw.Write(TypeName);