feat(core): add PluginLoader with collectible ALC
This commit is contained in:
parent
9dfbc05052
commit
a7f0732026
8 changed files with 226 additions and 0 deletions
11
src/AcDream.Core/Plugins/LoadedPlugin.cs
Normal file
11
src/AcDream.Core/Plugins/LoadedPlugin.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using AcDream.Plugin.Abstractions;
|
||||
|
||||
namespace AcDream.Core.Plugins;
|
||||
|
||||
public sealed record LoadedPlugin(
|
||||
PluginManifest Manifest,
|
||||
IAcDreamPlugin? Plugin,
|
||||
string? Error)
|
||||
{
|
||||
public bool Success => Plugin is not null && Error is null;
|
||||
}
|
||||
30
src/AcDream.Core/Plugins/PluginAssemblyLoadContext.cs
Normal file
30
src/AcDream.Core/Plugins/PluginAssemblyLoadContext.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace AcDream.Core.Plugins;
|
||||
|
||||
/// <summary>
|
||||
/// Collectible ALC for a single plugin. Resolves assemblies from the plugin's
|
||||
/// own directory EXCEPT for AcDream.Plugin.Abstractions, which must come from
|
||||
/// the default (host) context so type identity for IAcDreamPlugin is preserved.
|
||||
/// </summary>
|
||||
internal sealed class PluginAssemblyLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private readonly AssemblyDependencyResolver _resolver;
|
||||
|
||||
public PluginAssemblyLoadContext(string pluginDirectory, string pluginEntryPath)
|
||||
: base(name: pluginDirectory, isCollectible: true)
|
||||
{
|
||||
_resolver = new AssemblyDependencyResolver(pluginEntryPath);
|
||||
}
|
||||
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
{
|
||||
// Share the abstractions assembly with the host — do NOT reload it in the plugin ALC
|
||||
if (assemblyName.Name == "AcDream.Plugin.Abstractions")
|
||||
return null;
|
||||
|
||||
var path = _resolver.ResolveAssemblyToPath(assemblyName);
|
||||
return path is null ? null : LoadFromAssemblyPath(path);
|
||||
}
|
||||
}
|
||||
34
src/AcDream.Core/Plugins/PluginLoader.cs
Normal file
34
src/AcDream.Core/Plugins/PluginLoader.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System.Reflection;
|
||||
using AcDream.Plugin.Abstractions;
|
||||
|
||||
namespace AcDream.Core.Plugins;
|
||||
|
||||
public static class PluginLoader
|
||||
{
|
||||
public static LoadedPlugin Load(string pluginDirectory, PluginManifest manifest, IPluginHost host)
|
||||
{
|
||||
var dllPath = Path.Combine(pluginDirectory, manifest.EntryDll);
|
||||
if (!File.Exists(dllPath))
|
||||
return new LoadedPlugin(manifest, null, $"entry dll not found: {dllPath}");
|
||||
|
||||
try
|
||||
{
|
||||
var alc = new PluginAssemblyLoadContext(pluginDirectory, dllPath);
|
||||
var asm = alc.LoadFromAssemblyPath(dllPath);
|
||||
|
||||
var pluginType = asm.GetTypes()
|
||||
.FirstOrDefault(t => !t.IsAbstract && typeof(IAcDreamPlugin).IsAssignableFrom(t));
|
||||
|
||||
if (pluginType is null)
|
||||
return new LoadedPlugin(manifest, null, $"no IAcDreamPlugin implementation found in {manifest.EntryDll}");
|
||||
|
||||
var instance = (IAcDreamPlugin)Activator.CreateInstance(pluginType)!;
|
||||
instance.Initialize(host);
|
||||
return new LoadedPlugin(manifest, instance, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new LoadedPlugin(manifest, null, $"{ex.GetType().Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue