feat(plugins): complete Campaign LA5 cross-host hosting

This commit is contained in:
Erik 2026-08-14 18:12:59 +02:00
parent 6c4cd2bbc6
commit 95f4be94db
26 changed files with 1630 additions and 99 deletions

View file

@ -0,0 +1,78 @@
using AcDream.Core.Plugins;
using AcDream.Platform;
using AcDream.Plugin.Abstractions;
using AcDream.Runtime.Session;
namespace AcDream.App.Plugins;
/// <summary>
/// Graphical-host composition for one plugin set. The shared
/// <see cref="PluginSession"/> owns discovery and collectible lifetimes; this
/// adapter supplies the graphical roots and translates outcomes into the
/// launcher status stream.
/// </summary>
internal sealed class GraphicalPluginSession : IDisposable
{
private readonly PluginSession _plugins;
private GraphicalPluginSession(PluginSession plugins)
{
_plugins = plugins;
}
internal int LoadedCount => _plugins.LoadedCount;
internal IReadOnlyList<WeakReference> CaptureLoadContextWeakReferences() =>
_plugins.CaptureLoadContextWeakReferences();
internal static GraphicalPluginSession Start(
ApplicationPathSet paths,
IReadOnlyList<string>? allowList,
string sessionId,
IPluginHost host,
SessionStatusWriter statusWriter)
{
ArgumentNullException.ThrowIfNull(paths);
ArgumentException.ThrowIfNullOrWhiteSpace(sessionId);
ArgumentNullException.ThrowIfNull(host);
ArgumentNullException.ThrowIfNull(statusWriter);
var plugins = new PluginSession(
host,
status => Report(statusWriter, sessionId, status));
try
{
plugins.Start(
[
Path.Combine(AppContext.BaseDirectory, "plugins"),
paths.PluginsDirectory,
],
allowList);
return new GraphicalPluginSession(plugins);
}
catch
{
plugins.Dispose();
throw;
}
}
public void Dispose() => _plugins.Dispose();
private static void Report(
SessionStatusWriter writer,
string sessionId,
PluginSessionStatus status)
{
if (status.Kind == PluginSessionStatusKind.Loaded)
{
writer.PluginLoaded(sessionId, status.Plugin);
return;
}
writer.PluginFailed(
sessionId,
status.Plugin,
status.Error ?? "plugin failed");
}
}