diff --git a/GLOKON.Baiters.Core/Plugins/PluginLoader.cs b/GLOKON.Baiters.Core/Plugins/PluginLoader.cs index 7bda529..3b60b3d 100644 --- a/GLOKON.Baiters.Core/Plugins/PluginLoader.cs +++ b/GLOKON.Baiters.Core/Plugins/PluginLoader.cs @@ -1,5 +1,6 @@ using Serilog; using System.Reflection; +using System.Runtime.Loader; namespace GLOKON.Baiters.Core.Plugins { @@ -22,40 +23,42 @@ public static void LoadPlugins(GameManager gm) { try { - AssemblyName thisFile = AssemblyName.GetAssemblyName(pluginPath); ; pluginAssm.Add(Assembly.LoadFrom(pluginPath)); } catch (Exception ex) { - Log.Error(ex, "Failed to load plugin file ({PluginPath})", pluginPath); + Log.Error(ex, "Failed to load plugin file ({0})", pluginPath); } } + AssemblyLoadContext.Default.Resolving += Default_Resolving; + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + foreach (var assembly in pluginAssm) { - Type[] pluginTypes = assembly.GetTypes() - .Where((type) => type.IsClass && type.IsSubclassOf(typeof(BaitersPlugin))) - .ToArray(); - - foreach (Type pluginType in pluginTypes) + try { - if (Activator.CreateInstance(pluginType, gm) is BaitersPlugin plugin) + Type[] pluginTypes = assembly.GetTypes() + .Where((type) => type.IsClass && type.IsSubclassOf(typeof(BaitersPlugin))) + .ToArray(); + + foreach (Type pluginType in pluginTypes) { - try + if (Activator.CreateInstance(pluginType, gm) is BaitersPlugin plugin) { plugin.OnInit(); Plugins.Add(plugin); Log.Information("{0}:{1} plugin loaded by {2}", plugin.Name, plugin.Version, plugin.Author); } - catch (Exception ex) + else { - Log.Error(ex, "Failed to initialize plugin {0}:{1}", plugin.Name, plugin.Version); + Log.Error("Failed to instantiate plugin ({0})", pluginType.FullName); } } - else - { - Log.Error("Failed to create plugin ({0})", pluginType.FullName); - } + } + catch (Exception ex) + { + Log.Error(ex, "Failed to load plugin ({0})", assembly.FullName); } } } @@ -74,8 +77,37 @@ public static void UnloadPlugins() } } + AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve; + AssemblyLoadContext.Default.Resolving -= Default_Resolving; Plugins.Clear(); GC.Collect(); } + + private static Assembly? Default_Resolving(AssemblyLoadContext context, AssemblyName assembly) + { + Log.Verbose("Attempting to resolve {0}", assembly); + + string assemblyPath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"./plugins/{assembly.Name}.dll")); + return context.LoadFromAssemblyPath(assemblyPath); + } + + private static Assembly? CurrentDomain_AssemblyResolve(object? sender, ResolveEventArgs args) + { + Assembly? assembly = null; + + // Ignore version for internal dependencies + if (args.Name.Contains("GLOKON.Baiters.Core") || args.Name.Contains("GLOKON.Baiters.GodotInterop")) + { + var assemblyName = new AssemblyName(args.Name); + + // Prevents infinte loop, as args will contain the assembly FullName once resolved + if (assemblyName.Name != args.Name) + { + assembly = ((AppDomain)sender).Load(assemblyName.Name); + } + } + + return assembly; + } } }