mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-20 01:46:11 +00:00
Compare commits
2
Commits
v1.9.2
...
pluginmanager
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e857ed555 | ||
|
|
9d31752bf0 |
No files matched your search
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Plugins
|
||||
{
|
||||
public interface IPluginInformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique id of this plugin.
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the human readable name of this plugin.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the author of this plugin.
|
||||
/// </summary>
|
||||
string Author { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of this plugin.
|
||||
/// </summary>
|
||||
string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional startup object of this plugin.
|
||||
/// </summary>
|
||||
IPluginStartup? Startup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of this plugin.
|
||||
/// </summary>
|
||||
IPlugin? Instance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance type of this plugin.
|
||||
/// </summary>
|
||||
Type PluginType { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Api.Plugins
|
||||
{
|
||||
public interface IPluginManager
|
||||
{
|
||||
IReadOnlyList<IPluginInformation> Plugins { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
@@ -11,17 +12,21 @@ namespace Impostor.Plugins.Example
|
||||
{
|
||||
private readonly ILogger<ExamplePlugin> _logger;
|
||||
private readonly IGameManager _gameManager;
|
||||
private readonly IPluginManager _pluginManager;
|
||||
|
||||
public ExamplePlugin(ILogger<ExamplePlugin> logger, IGameManager gameManager)
|
||||
public ExamplePlugin(ILogger<ExamplePlugin> logger, IGameManager gameManager, IPluginManager pluginManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_gameManager = gameManager;
|
||||
_pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public override async ValueTask EnableAsync()
|
||||
{
|
||||
_logger.LogInformation("Example is being enabled.");
|
||||
|
||||
_logger.LogInformation("Following plugins are installed: {@plugins}", _pluginManager.Plugins.Select(x => $"{x.Id} ({x.Version})"));
|
||||
|
||||
var game = await _gameManager.CreateAsync(new GameOptionsData());
|
||||
if (game == null)
|
||||
{
|
||||
|
||||
+4
-2
@@ -1,9 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Server.Plugins
|
||||
namespace Impostor.Server.Config
|
||||
{
|
||||
public class PluginConfig
|
||||
public class PluginLoaderConfig
|
||||
{
|
||||
public const string Section = "PluginLoader";
|
||||
|
||||
public List<string> Paths { get; set; } = new List<string>();
|
||||
|
||||
public List<string> LibraryPaths { get; set; } = new List<string>();
|
||||
@@ -26,10 +26,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.7" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.1.2" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.333">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -8,17 +8,20 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
private Assembly? _assembly;
|
||||
|
||||
public AssemblyInformation(AssemblyName assemblyName, string path, bool isPlugin)
|
||||
public AssemblyInformation(AssemblyName assemblyName, string path, bool isPlugin, bool isDefaultAssembly)
|
||||
{
|
||||
AssemblyName = assemblyName;
|
||||
Path = path;
|
||||
IsPlugin = isPlugin;
|
||||
IsDefaultAssembly = isDefaultAssembly;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public bool IsPlugin { get; }
|
||||
|
||||
public bool IsDefaultAssembly { get; }
|
||||
|
||||
public AssemblyName AssemblyName { get; }
|
||||
|
||||
public Assembly Load(AssemblyLoadContext context)
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
bool IsPlugin { get; }
|
||||
|
||||
bool IsDefaultAssembly { get; }
|
||||
|
||||
Assembly Load(AssemblyLoadContext context);
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,19 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
private readonly Assembly _assembly;
|
||||
|
||||
public LoadedAssemblyInformation(Assembly assembly)
|
||||
public LoadedAssemblyInformation(Assembly assembly, bool isDefaultAssembly)
|
||||
{
|
||||
AssemblyName = assembly.GetName();
|
||||
_assembly = assembly;
|
||||
IsDefaultAssembly = isDefaultAssembly;
|
||||
}
|
||||
|
||||
public AssemblyName AssemblyName { get; }
|
||||
|
||||
public bool IsPlugin => false;
|
||||
|
||||
public bool IsDefaultAssembly { get; }
|
||||
|
||||
public Assembly Load(AssemblyLoadContext context)
|
||||
{
|
||||
return _assembly;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Impostor.Server.Plugins
|
||||
{
|
||||
public class PluginAssemblyLoadContext : AssemblyLoadContext, IDisposable
|
||||
{
|
||||
private readonly IDictionary<string, IAssemblyInformation> _assembliesByName;
|
||||
|
||||
public PluginAssemblyLoadContext(IDictionary<string, IAssemblyInformation> assembliesByName)
|
||||
: base(true)
|
||||
{
|
||||
_assembliesByName = assembliesByName;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_assembliesByName.Clear();
|
||||
this.Unload();
|
||||
}
|
||||
|
||||
protected override Assembly? Load(AssemblyName name)
|
||||
{
|
||||
return name.Name != null && _assembliesByName.TryGetValue(name.Name, out var assemblyInfo)
|
||||
? assemblyInfo.Load(this)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using Impostor.Api.Plugins;
|
||||
|
||||
namespace Impostor.Server.Plugins
|
||||
{
|
||||
public class PluginInformation
|
||||
public class PluginInformation : IPluginInformation
|
||||
{
|
||||
private readonly ImpostorPluginAttribute _attribute;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Plugins;
|
||||
@@ -13,20 +12,20 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
private readonly ILogger<PluginLoaderService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly List<PluginInformation> _plugins;
|
||||
private readonly PluginManager _pluginManager;
|
||||
|
||||
public PluginLoaderService(ILogger<PluginLoaderService> logger, IServiceProvider serviceProvider, List<PluginInformation> plugins)
|
||||
public PluginLoaderService(ILogger<PluginLoaderService> logger, IServiceProvider serviceProvider, PluginManager pluginManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
_plugins = plugins;
|
||||
_pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Loading plugins.");
|
||||
_logger.LogInformation("Enabling plugins.");
|
||||
|
||||
foreach (var plugin in _plugins)
|
||||
foreach (var plugin in _pluginManager.Plugins)
|
||||
{
|
||||
_logger.LogInformation("Enabling plugin {0}.", plugin);
|
||||
|
||||
@@ -38,10 +37,10 @@ namespace Impostor.Server.Plugins
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
_plugins.Count == 1
|
||||
? "Loaded {0} plugin."
|
||||
: "Loaded {0} plugins.",
|
||||
_plugins.Count
|
||||
_pluginManager.Plugins.Count == 1
|
||||
? "Enabled {0} plugin."
|
||||
: "Enabled {0} plugins.",
|
||||
_pluginManager.Plugins.Count
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
// Disable all plugins with a valid instance set.
|
||||
// In the case of a failed startup, some can be null.
|
||||
foreach (var plugin in _plugins)
|
||||
foreach (var plugin in _pluginManager.Plugins)
|
||||
{
|
||||
if (plugin.Instance != null)
|
||||
{
|
||||
@@ -57,6 +56,7 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
// Disable plugin.
|
||||
await plugin.Instance.DisableAsync();
|
||||
plugin.Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+150
-56
@@ -4,24 +4,72 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Plugins;
|
||||
using Impostor.Server.Config;
|
||||
using Impostor.Server.Plugins.Proxies;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileSystemGlobbing;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Plugins
|
||||
{
|
||||
public static class PluginLoader
|
||||
public class PluginManager : IPluginManager, IHostedService
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext(typeof(PluginLoader));
|
||||
private readonly ILogger<PluginManager> _logger;
|
||||
private readonly IOptions<PluginLoaderConfig> _pluginConfig;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ServiceProxyCollection _serviceProxyCollection;
|
||||
|
||||
public static IHostBuilder UsePluginLoader(this IHostBuilder builder, PluginConfig config)
|
||||
private PluginAssemblyLoadContext? _currentContext;
|
||||
|
||||
public PluginManager(ILogger<PluginManager> logger, IOptions<PluginLoaderConfig> pluginConfig, IConfiguration configuration, IServiceProvider serviceProvider, ServiceProxyCollection serviceProxyCollection)
|
||||
{
|
||||
var assemblyInfos = new List<IAssemblyInformation>();
|
||||
var context = AssemblyLoadContext.Default;
|
||||
_logger = logger;
|
||||
_pluginConfig = pluginConfig;
|
||||
_configuration = configuration;
|
||||
_serviceProvider = serviceProvider;
|
||||
_serviceProxyCollection = serviceProxyCollection;
|
||||
}
|
||||
|
||||
public IHost? CurrentHost { get; private set; }
|
||||
|
||||
public List<PluginInformation> Plugins { get; } = new List<PluginInformation>();
|
||||
|
||||
IReadOnlyList<IPluginInformation> IPluginManager.Plugins => Plugins.AsReadOnly();
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CurrentHost = CreatePluginHost();
|
||||
return CurrentHost.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (CurrentHost != null)
|
||||
{
|
||||
await CurrentHost.StopAsync(cancellationToken);
|
||||
|
||||
if (CurrentHost is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentHost.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<string, IAssemblyInformation> GetAssemblyInformation()
|
||||
{
|
||||
// Add the plugins and libraries.
|
||||
var config = _pluginConfig.Value;
|
||||
var pluginPaths = new List<string>(config.Paths);
|
||||
var libraryPaths = new List<string>(config.LibraryPaths);
|
||||
CheckPaths(pluginPaths);
|
||||
@@ -35,38 +83,70 @@ namespace Impostor.Server.Plugins
|
||||
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
|
||||
matcher.AddInclude("*.dll");
|
||||
matcher.AddExclude("Impostor.Api.dll");
|
||||
matcher.AddExclude("Impostor.Server.dll");
|
||||
|
||||
RegisterAssemblies(pluginPaths, matcher, assemblyInfos, true);
|
||||
RegisterAssemblies(libraryPaths, matcher, assemblyInfos, false);
|
||||
var assembliesByName = AssemblyLoadContext.Default.Assemblies
|
||||
.Where(a => a.GetName().Name != null)
|
||||
.ToDictionary(
|
||||
a => a.GetName().Name!,
|
||||
a => (IAssemblyInformation)new LoadedAssemblyInformation(a, true));
|
||||
|
||||
// Register the resolver to the current context.
|
||||
// TODO: Move this to a new context so we can unload/reload plugins.
|
||||
context.Resolving += (loadContext, name) =>
|
||||
RegisterAssemblies(assembliesByName, pluginPaths, matcher, true);
|
||||
RegisterAssemblies(assembliesByName, libraryPaths, matcher, false);
|
||||
|
||||
return assembliesByName;
|
||||
}
|
||||
|
||||
private IHost CreatePluginHost()
|
||||
{
|
||||
var assembliesByName = GetAssemblyInformation();
|
||||
_currentContext ??= new PluginAssemblyLoadContext(assembliesByName);
|
||||
|
||||
var pluginHostBuilder = new HostBuilder()
|
||||
.UseConsoleLifetime(options => options.SuppressStatusMessages = true)
|
||||
.UseContentRoot(_configuration.GetValue<string>(HostDefaults.ContentRootKey))
|
||||
.ConfigureHostConfiguration(builder => { builder.AddConfiguration(_configuration); })
|
||||
.UseServiceProviderFactory(_ => new DefaultServiceProviderFactory());
|
||||
|
||||
Plugins.Clear();
|
||||
Plugins.AddRange(GetPluginInformation(assembliesByName, _currentContext));
|
||||
|
||||
foreach (var plugin in Plugins)
|
||||
{
|
||||
Logger.Verbose("Loading assembly {0} v{1}", name.Name, name.Version);
|
||||
plugin.Startup?.ConfigureHost(pluginHostBuilder);
|
||||
}
|
||||
|
||||
// Some plugins may be referencing another Impostor.Api version and try to load it.
|
||||
// We want to only use the one shipped with the server.
|
||||
if (name.Name == "Impostor.Api")
|
||||
pluginHostBuilder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHostedService<PluginLoaderService>();
|
||||
|
||||
foreach (var (type, lifetime) in _serviceProxyCollection.ServiceTypes)
|
||||
{
|
||||
return typeof(IPlugin).Assembly;
|
||||
services.Add(new ServiceDescriptor(
|
||||
type,
|
||||
_ => _serviceProvider.GetService(type)!,
|
||||
lifetime));
|
||||
}
|
||||
|
||||
var info = assemblyInfos.FirstOrDefault(a => a.AssemblyName.Name == name.Name);
|
||||
foreach (var plugin in Plugins)
|
||||
{
|
||||
plugin.Startup?.ConfigureServices(services);
|
||||
}
|
||||
});
|
||||
|
||||
return info?.Load(loadContext);
|
||||
};
|
||||
return pluginHostBuilder.Build();
|
||||
}
|
||||
|
||||
// TODO: Catch uncaught exceptions.
|
||||
var assemblies = assemblyInfos
|
||||
private List<PluginInformation> GetPluginInformation(IDictionary<string, IAssemblyInformation> assembliesByName, PluginAssemblyLoadContext context)
|
||||
{
|
||||
var plugins = new List<PluginInformation>();
|
||||
var pluginAssemblies = assembliesByName
|
||||
.Values
|
||||
.Where(a => a.IsPlugin)
|
||||
.Select(a => context.LoadFromAssemblyName(a.AssemblyName))
|
||||
.ToList();
|
||||
|
||||
// Find all plugins.
|
||||
var plugins = new List<PluginInformation>();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
foreach (var assembly in pluginAssemblies)
|
||||
{
|
||||
// Find plugin startup.
|
||||
var pluginStartup = assembly
|
||||
@@ -76,7 +156,7 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
if (pluginStartup.Count > 1)
|
||||
{
|
||||
Logger.Warning("A plugin may only define zero or one IPluginStartup implementation ({0}).", assembly);
|
||||
_logger.LogWarning("A plugin may only define zero or one IPluginStartup implementation ({0}).", assembly);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -91,7 +171,7 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
if (plugin.Count != 1)
|
||||
{
|
||||
Logger.Warning("A plugin must define exactly one IPlugin or PluginBase implementation ({0}).", assembly);
|
||||
_logger.LogWarning("A plugin must define exactly one IPlugin or PluginBase implementation ({0}).", assembly);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -104,61 +184,75 @@ namespace Impostor.Server.Plugins
|
||||
plugin.Single()));
|
||||
}
|
||||
|
||||
var orderedPlugins = LoadOrderPlugins(plugins);
|
||||
|
||||
foreach (var plugin in orderedPlugins)
|
||||
{
|
||||
plugin.Startup?.ConfigureHost(builder);
|
||||
}
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHostedService(provider => ActivatorUtilities.CreateInstance<PluginLoaderService>(provider, orderedPlugins));
|
||||
|
||||
foreach (var plugin in orderedPlugins)
|
||||
{
|
||||
plugin.Startup?.ConfigureServices(services);
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
return OrderPlugins(plugins);
|
||||
}
|
||||
|
||||
private static void CheckPaths(IEnumerable<string> paths)
|
||||
private void CheckPaths(IEnumerable<string> paths)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Logger.Warning("Path {path} was specified in the PluginLoader configuration, but this folder doesn't exist!", path);
|
||||
_logger.LogWarning("Path {path} was specified in the PluginLoader configuration, but this folder doesn't exist!", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterAssemblies(
|
||||
private void RegisterAssemblies(
|
||||
IDictionary<string, IAssemblyInformation> assemblies,
|
||||
IEnumerable<string> paths,
|
||||
Matcher matcher,
|
||||
ICollection<IAssemblyInformation> assemblyInfos,
|
||||
bool isPlugin)
|
||||
{
|
||||
foreach (var path in paths.SelectMany(matcher.GetResultsInFullPath))
|
||||
{
|
||||
AssemblyName assemblyName;
|
||||
AssemblyName name;
|
||||
|
||||
try
|
||||
{
|
||||
assemblyName = AssemblyName.GetAssemblyName(path);
|
||||
name = AssemblyName.GetAssemblyName(path);
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
assemblyInfos.Add(new AssemblyInformation(assemblyName, path, isPlugin));
|
||||
if (name.Name == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Skipped {0} because the assembly name could not be resolved",
|
||||
path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (assemblies.TryGetValue(name.Name, out var loaded))
|
||||
{
|
||||
var version = name.Version;
|
||||
var loadedVersion = loaded.AssemblyName.Version;
|
||||
|
||||
if (loadedVersion != null && version != null && version.Major != loadedVersion.Major)
|
||||
{
|
||||
_logger.LogError(
|
||||
"Assembly {0} is already loaded with version {1} while loading version {2}",
|
||||
name.Name,
|
||||
loadedVersion,
|
||||
version);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Skipped assembly {0} because it's already loaded", name.Name);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogTrace("Registered assembly {name} v{version} ({path})", name.Name, name.Version, path);
|
||||
|
||||
assemblies.Add(name.Name, new AssemblyInformation(name, path, isPlugin, false));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<PluginInformation> LoadOrderPlugins(IEnumerable<PluginInformation> plugins)
|
||||
private List<PluginInformation> OrderPlugins(IEnumerable<PluginInformation> plugins)
|
||||
{
|
||||
var pluginDictionary = new Dictionary<string, PluginInformation>();
|
||||
var hardDependencies = new Dictionary<string, List<string>>();
|
||||
@@ -208,7 +302,7 @@ namespace Impostor.Server.Plugins
|
||||
return ordered;
|
||||
}
|
||||
|
||||
private static List<string> CheckHardDependencies(
|
||||
private List<string> CheckHardDependencies(
|
||||
List<string> plugins,
|
||||
IReadOnlyDictionary<string, List<string>> hardDependencies)
|
||||
{
|
||||
@@ -221,7 +315,7 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
foreach (var dependency in hardDependencies[plugin].Where(dependency => !plugins.Contains(dependency)))
|
||||
{
|
||||
Logger.Error(
|
||||
_logger.LogError(
|
||||
"The plugin {plugin} has defined the plugin {dependency} as a hard dependency but its not present! {plugin} will not loaded.",
|
||||
plugin,
|
||||
dependency,
|
||||
@@ -239,7 +333,7 @@ namespace Impostor.Server.Plugins
|
||||
return plugins;
|
||||
}
|
||||
|
||||
private static void RecursiveOrder(
|
||||
private void RecursiveOrder(
|
||||
string plugin,
|
||||
IReadOnlyDictionary<string, List<string>> dependencyGraph,
|
||||
ICollection<string> processed,
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Server.Plugins.Proxies
|
||||
{
|
||||
public class ServiceProxyCollection
|
||||
{
|
||||
public ServiceProxyCollection(IReadOnlyList<ServiceRegistration> serviceTypes)
|
||||
{
|
||||
ServiceTypes = serviceTypes;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ServiceRegistration> ServiceTypes { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Impostor.Server.Plugins.Proxies
|
||||
{
|
||||
public record ServiceRegistration(Type Type, ServiceLifetime Lifetime);
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Events;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Manager;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Plugins;
|
||||
using Impostor.Api.Utils;
|
||||
using Impostor.Hazel.Extensions;
|
||||
using Impostor.Server.Config;
|
||||
@@ -18,11 +21,13 @@ using Impostor.Server.Net.Manager;
|
||||
using Impostor.Server.Net.Messages;
|
||||
using Impostor.Server.Net.Redirector;
|
||||
using Impostor.Server.Plugins;
|
||||
using Impostor.Server.Plugins.Proxies;
|
||||
using Impostor.Server.Recorder;
|
||||
using Impostor.Server.Utils;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
@@ -31,38 +36,16 @@ namespace Impostor.Server
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static int Main(string[] args)
|
||||
private static async Task<int> Main(string[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
var logLevel = LogEventLevel.Debug;
|
||||
#else
|
||||
var logLevel = LogEventLevel.Information;
|
||||
#endif
|
||||
|
||||
if (args.Contains("--verbose"))
|
||||
{
|
||||
logLevel = LogEventLevel.Verbose;
|
||||
}
|
||||
else if (args.Contains("--errors-only"))
|
||||
{
|
||||
logLevel = LogEventLevel.Error;
|
||||
}
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Is(logLevel)
|
||||
#if DEBUG
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
|
||||
#else
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
#endif
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console()
|
||||
.CreateLogger();
|
||||
.CreateBootstrapLogger();
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Starting Impostor v{0}", DotnetUtils.Version);
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
await CreateHostBuilder(args).RunConsoleAsync();
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -76,25 +59,8 @@ namespace Impostor.Server
|
||||
}
|
||||
}
|
||||
|
||||
private static IConfiguration CreateConfiguration(string[] args)
|
||||
{
|
||||
var configurationBuilder = new ConfigurationBuilder();
|
||||
|
||||
configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
|
||||
configurationBuilder.AddJsonFile("config.json", true);
|
||||
configurationBuilder.AddJsonFile("config.Development.json", true);
|
||||
configurationBuilder.AddEnvironmentVariables(prefix: "IMPOSTOR_");
|
||||
configurationBuilder.AddCommandLine(args);
|
||||
|
||||
return configurationBuilder.Build();
|
||||
}
|
||||
|
||||
private static IHostBuilder CreateHostBuilder(string[] args)
|
||||
{
|
||||
var configuration = CreateConfiguration(args);
|
||||
var pluginConfig = configuration.GetSection("PluginLoader")
|
||||
.Get<PluginConfig>() ?? new PluginConfig();
|
||||
|
||||
return Host.CreateDefaultBuilder(args)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
#if DEBUG
|
||||
@@ -104,7 +70,10 @@ namespace Impostor.Server
|
||||
#endif
|
||||
.ConfigureAppConfiguration(builder =>
|
||||
{
|
||||
builder.AddConfiguration(configuration);
|
||||
builder.AddJsonFile("config.json", true);
|
||||
builder.AddJsonFile("config.Development.json", true);
|
||||
builder.AddEnvironmentVariables(prefix: "IMPOSTOR_");
|
||||
builder.AddCommandLine(args);
|
||||
})
|
||||
.ConfigureServices((host, services) =>
|
||||
{
|
||||
@@ -134,6 +103,7 @@ namespace Impostor.Server
|
||||
services.Configure<AnnouncementsServerConfig>(host.Configuration.GetSection(AnnouncementsServerConfig.Section));
|
||||
services.Configure<AuthServerConfig>(host.Configuration.GetSection(AuthServerConfig.Section));
|
||||
services.Configure<ServerRedirectorConfig>(host.Configuration.GetSection(ServerRedirectorConfig.Section));
|
||||
services.Configure<PluginLoaderConfig>(host.Configuration.GetSection(PluginLoaderConfig.Section));
|
||||
|
||||
if (redirector.Enabled)
|
||||
{
|
||||
@@ -228,10 +198,53 @@ namespace Impostor.Server
|
||||
{
|
||||
services.AddHostedService<AuthService>();
|
||||
}
|
||||
|
||||
services.AddSingleton<PluginManager>();
|
||||
services.AddSingleton<IPluginManager>(p => p.GetRequiredService<PluginManager>());
|
||||
services.AddHostedService(p => p.GetRequiredService<PluginManager>());
|
||||
})
|
||||
.UseSerilog()
|
||||
.UseConsoleLifetime()
|
||||
.UsePluginLoader(pluginConfig);
|
||||
.UseSerilog((context, loggerConfiguration) =>
|
||||
{
|
||||
#if DEBUG
|
||||
var logLevel = LogEventLevel.Debug;
|
||||
#else
|
||||
var logLevel = LogEventLevel.Information;
|
||||
#endif
|
||||
|
||||
if (args.Contains("--verbose"))
|
||||
{
|
||||
logLevel = LogEventLevel.Verbose;
|
||||
}
|
||||
else if (args.Contains("--errors-only"))
|
||||
{
|
||||
logLevel = LogEventLevel.Error;
|
||||
}
|
||||
|
||||
loggerConfiguration
|
||||
.MinimumLevel.Is(logLevel)
|
||||
#if DEBUG
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
|
||||
#else
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
#endif
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console()
|
||||
.ReadFrom.Configuration(context.Configuration);
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddTransient(serviceProvider => serviceProvider.GetRequiredService<PluginManager>().CurrentHost?.Services.GetServices<IEventListener>() ?? Enumerable.Empty<IEventListener>());
|
||||
|
||||
var apiAssembly = typeof(IPlugin).Assembly;
|
||||
var serilogAssembly = typeof(Serilog.ILogger).Assembly;
|
||||
|
||||
services.AddSingleton(new ServiceProxyCollection(
|
||||
services
|
||||
.Where(t => t.ServiceType.Assembly == apiAssembly || t.ServiceType.Assembly == serilogAssembly || t.ServiceType == typeof(PluginManager) || t.ServiceType == typeof(ILoggerFactory))
|
||||
.Select(t => new ServiceRegistration(t.ServiceType, t.Lifetime))
|
||||
.Distinct()
|
||||
.ToArray()));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user