feat(plugins): complete Campaign LA5 cross-host hosting
This commit is contained in:
parent
6c4cd2bbc6
commit
95f4be94db
26 changed files with 1630 additions and 99 deletions
|
|
@ -18,6 +18,7 @@ public sealed class AppPluginHost : IPluginHost
|
|||
Ui = ui;
|
||||
}
|
||||
|
||||
public bool HasUi => true;
|
||||
public IPluginLogger Log { get; }
|
||||
public IGameState State { get; }
|
||||
public IEvents Events { get; }
|
||||
|
|
|
|||
78
src/AcDream.App/Plugins/GraphicalPluginSession.cs
Normal file
78
src/AcDream.App/Plugins/GraphicalPluginSession.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ using AcDream.App.Credentials;
|
|||
using AcDream.App.Plugins;
|
||||
using AcDream.App.Platform;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.Core.Plugins;
|
||||
using AcDream.Platform;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -162,76 +161,15 @@ var host = new AppPluginHost(
|
|||
worldEvents,
|
||||
window.Selection,
|
||||
uiRegistry);
|
||||
|
||||
var loaded = new List<LoadedPlugin>();
|
||||
var loadedPluginIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
StringComparer pathComparer =
|
||||
graphicalPlatform.OperatingSystem
|
||||
== GraphicalHostOperatingSystem.Windows
|
||||
? StringComparer.OrdinalIgnoreCase
|
||||
: StringComparer.Ordinal;
|
||||
string[] pluginRoots =
|
||||
[
|
||||
.. new[]
|
||||
{
|
||||
Path.Combine(AppContext.BaseDirectory, "plugins"),
|
||||
applicationPaths.PluginsDirectory,
|
||||
}.Distinct(pathComparer),
|
||||
];
|
||||
|
||||
foreach (string pluginsDir in pluginRoots)
|
||||
{
|
||||
Log.Information("scanning plugins in {PluginsDir}", pluginsDir);
|
||||
foreach (var result in PluginDiscovery.Scan(pluginsDir))
|
||||
{
|
||||
if (!result.Success)
|
||||
{
|
||||
Log.Warning(
|
||||
"plugin discovery failed for {Dir}: {Error}",
|
||||
result.PluginDirectory,
|
||||
result.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (loadedPluginIds.Contains(result.Manifest!.Id))
|
||||
{
|
||||
Log.Warning(
|
||||
"skipping duplicate plugin id {Id} from {Dir}",
|
||||
result.Manifest.Id,
|
||||
result.PluginDirectory);
|
||||
continue;
|
||||
}
|
||||
|
||||
var loadResult = PluginLoader.Load(
|
||||
result.PluginDirectory,
|
||||
result.Manifest,
|
||||
host);
|
||||
if (!loadResult.Success)
|
||||
{
|
||||
Log.Warning(
|
||||
"plugin load failed for {Id}: {Error}",
|
||||
result.Manifest.Id,
|
||||
loadResult.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
loadedPluginIds.Add(result.Manifest.Id);
|
||||
loaded.Add(loadResult);
|
||||
Log.Information(
|
||||
"loaded plugin {Id} ({DisplayName})",
|
||||
result.Manifest.Id,
|
||||
result.Manifest.DisplayName);
|
||||
}
|
||||
}
|
||||
using var pluginSession = GraphicalPluginSession.Start(
|
||||
applicationPaths,
|
||||
runtimeOptions.Plugins,
|
||||
runtimeOptions.SessionId ?? "app",
|
||||
host,
|
||||
window.StatusWriter);
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var plugin in loaded)
|
||||
{
|
||||
try { plugin.Plugin!.Enable(); }
|
||||
catch (Exception ex) { Log.Error(ex, "plugin enable failed: {Id}", plugin.Manifest.Id); }
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
window.Run();
|
||||
|
|
@ -244,11 +182,7 @@ try
|
|||
}
|
||||
finally
|
||||
{
|
||||
foreach (var plugin in loaded)
|
||||
{
|
||||
try { plugin.Plugin!.Disable(); }
|
||||
catch (Exception ex) { Log.Error(ex, "plugin disable failed: {Id}", plugin.Manifest.Id); }
|
||||
}
|
||||
pluginSession.Dispose();
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -362,6 +362,7 @@ public sealed class GameWindow :
|
|||
private RuntimeActionState _runtimeActions => _runtime.ActionOwner;
|
||||
public AcDream.Core.Selection.SelectionState Selection =>
|
||||
_runtimeActions.Selection;
|
||||
internal SessionStatusWriter StatusWriter => _statusWriter;
|
||||
public AcDream.Core.Chat.ChatLog Chat => _runtimeCommunication.Chat;
|
||||
public AcDream.Core.Chat.TurbineChatState TurbineChat =>
|
||||
_runtimeCommunication.TurbineChat;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue