Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin assembly loading fix #222

Open
wants to merge 6 commits into
base: 13.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 51 additions & 18 deletions NwPluginAPI/Loader/AssemblyLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,40 +114,69 @@ private static void LoadPlugins(PluginDirectory directory)

var loadedAssemblies = AppDomain.CurrentDomain
.GetAssemblies()
.Select(x =>
$"{x.GetName().Name}&r v&6{x.GetName().Version.ToString(3)}");
.ToDictionary(x => x.GetName().Name, x => x.GetName().Version);

var loadedPluginAssemblies = new List<PluginAssemblyInformation>();
var pluginsToInitialize = new List<PluginAssemblyInformation>();

foreach (string pluginPath in files)
{
if (!TryGetAssembly(pluginPath, out Assembly assembly))
continue;
loadedPluginAssemblies.Add(new PluginAssemblyInformation(pluginPath, assembly));
loadedAssemblies[assembly.GetName().Name] = assembly.GetName().Version;
}

Type[] types = null;

var missingDependencies = assembly
foreach (var pluginInfo in loadedPluginAssemblies)
{
var missingDependencies = pluginInfo.Assembly
.GetReferencedAssemblies()
.Where(x => !loadedAssemblies.ContainsKey(x.Name))
.ToDictionary(x => x.Name, x => x.Version);
var versionMismatch = pluginInfo.Assembly
.GetReferencedAssemblies()
.Select(x =>
$"{x.Name}&r v&6{x.Version.ToString(3)}")
.Where(x => !loadedAssemblies.Contains(x)).ToArray();
.Where(x => loadedAssemblies.ContainsKey(x.Name) && loadedAssemblies[x.Name] != x.Version)
.ToDictionary(x => x.Name, x => (Expected: x.Version, Actual: loadedAssemblies[x.Name]));

try
{
if (missingDependencies.Length != 0)
ResolveAssemblyEmbeddedResources(assembly);
types = assembly.GetTypes();
if (missingDependencies.Count != 0)
ResolveAssemblyEmbeddedResources(pluginInfo.Assembly, missingDependencies);
if (missingDependencies.Count != 0)
{
Log.Error($"Failed loading plugin &2{Path.GetFileNameWithoutExtension(pluginInfo.Path)}&r, missing dependencies\n&2{string.Join("\n", missingDependencies.Select(x => "&r - &2" + x.Key + " v" + x.Value.ToString(3) + "&r"))}", "Loader");
continue;
}

pluginInfo.Types = pluginInfo.Assembly.GetTypes();
pluginsToInitialize.Add(pluginInfo);
}
catch (Exception e)
{
if (missingDependencies.Length != 0)
if (missingDependencies.Count != 0)
{
Log.Error($"Failed loading plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r, missing dependencies\n&2{string.Join("\n", missingDependencies.Select(x => $"&r - &2{x}&r"))}\n\n{e}", "Loader");
continue;
Log.Error($"Failed loading plugin &2{Path.GetFileNameWithoutExtension(pluginInfo.Path)}&r, missing dependencies\n&2{string.Join("\n", missingDependencies.Select(x => "&r - &2" + x.Key + " v" + x.Value.ToString(3) + "&r"))}\n\n{e}", "Loader");
}
else
{
Log.Error("Failed loading plugin &2" + Path.GetFileNameWithoutExtension(pluginInfo.Path) + "&r, " + e, "Loader");
}

Log.Error($"Failed loading plugin &2{Path.GetFileNameWithoutExtension(pluginPath)}&r, {e.ToString()}");
continue;
}

if (versionMismatch.Count != 0)
{
Log.Warning($"Dependency version mismatch in plugin &2{Path.GetFileNameWithoutExtension(pluginInfo.Path)}&r\n&2{string.Join("\n", versionMismatch.Select(x => "&r - &2" + x.Key + " v" + x.Value.Actual.ToString(3) + " (expected version by plugin: " + x.Value.Expected.ToString(3) + ")" + "&r"))}", "Loader");
}
}

Log.Info($"Initializing &2{pluginsToInitialize.Count}&r plugins...");
foreach (var pluginInfo in pluginsToInitialize)
{
var pluginPath = pluginInfo.Path;
var assembly = pluginInfo.Assembly;
var types = pluginInfo.Types;
foreach (var entryType in types)
{
try
Expand Down Expand Up @@ -217,6 +246,7 @@ private static void LoadDependencies(string path)
Log.Error($"Failed loading dependency &2{Path.GetFileNameWithoutExtension(dependencyPath)}&r.\n{ex}");
continue;
}

successes++;
}

Expand Down Expand Up @@ -249,7 +279,8 @@ private static bool TryGetAssembly(string path, out Assembly assembly)
/// Attempts to load Embedded assemblies (compressed) from the target
/// </summary>
/// <param name="target">Assembly to check for embedded assemblies</param>
private static void ResolveAssemblyEmbeddedResources(Assembly target)
/// <param name="missingDependencies"></param>
private static void ResolveAssemblyEmbeddedResources(Assembly target, Dictionary<string, Version> missingDependencies)
{
Log.Debug($"Attempting to load embedded resources for {target.FullName}", Log.DebugMode);

Expand All @@ -271,7 +302,8 @@ private static void ResolveAssemblyEmbeddedResources(Assembly target)
}

dataStream.CopyTo(stream);
Assembly.Load(stream.ToArray());
var assemblyName = Assembly.Load(stream.ToArray()).GetName();
missingDependencies.Remove(assemblyName.Name);
Log.Debug($"Loaded {name}", Log.DebugMode);
}
}
Expand All @@ -289,7 +321,8 @@ private static void ResolveAssemblyEmbeddedResources(Assembly target)
{
Log.Debug($"Loading {name}", Log.DebugMode);
stream.CopyTo(memStream);
Assembly.Load(memStream.ToArray());
var assemblyName = Assembly.Load(memStream.ToArray()).GetName();
missingDependencies.Remove(assemblyName.Name);
Log.Debug($"Loaded {name}", Log.DebugMode);
}
}
Expand Down
24 changes: 24 additions & 0 deletions NwPluginAPI/Loader/PluginAssemblyInformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace PluginAPI.Loader
{

using System;
using System.Reflection;

internal sealed class PluginAssemblyInformation
{

public PluginAssemblyInformation(string path, Assembly assembly)
{
Path = path;
Assembly = assembly;
}

public Type[] Types = Array.Empty<Type>();

public readonly string Path;

public readonly Assembly Assembly;

}

}