using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using Impostor.Api.Plugins;
{
_attribute = pluginType.GetCustomAttribute<ImpostorPluginAttribute>()!;
+ Dependencies = pluginType.GetCustomAttributes<ImpostorDependencyAttribute>().Select(t => new DependencyInformation(t)).ToList();
Startup = startup;
PluginType = pluginType;
}
- public string Package => _attribute.Package;
+ public string Id => _attribute.Id;
public string Name => _attribute.Name;
public string Version => _attribute.Version;
+ public List<DependencyInformation> Dependencies { get; }
+
public IPluginStartup? Startup { get; }
public Type PluginType { get; }
public override string ToString()
{
- return $"{Package} {Name} ({Version}) by {Author}";
+ return $"{Id} {Name} ({Version}) by {Author}";
}
}
}
.Select(Activator.CreateInstance)
.Cast<IPluginStartup>()
.FirstOrDefault(),
- plugin.First()));
+ plugin.Single()));
}
- foreach (var plugin in plugins)
+ var orderedPlugins = LoadOrderPlugins(plugins);
+
+ foreach (var plugin in orderedPlugins)
{
plugin.Startup?.ConfigureHost(builder);
}
builder.ConfigureServices(services =>
{
- services.AddHostedService(provider => ActivatorUtilities.CreateInstance<PluginLoaderService>(provider, plugins));
+ services.AddHostedService(provider => ActivatorUtilities.CreateInstance<PluginLoaderService>(provider, orderedPlugins));
- foreach (var plugin in plugins)
+ foreach (var plugin in orderedPlugins)
{
plugin.Startup?.ConfigureServices(services);
}
assemblyInfos.Add(new AssemblyInformation(assemblyName, path, isPlugin));
}
}
+
+ private static List<PluginInformation> LoadOrderPlugins(IEnumerable<PluginInformation> plugins)
+ {
+ var pluginDictionary = new Dictionary<string, PluginInformation>();
+ var hardDependencies = new Dictionary<string, List<string>>();
+
+ foreach (var plugin in plugins)
+ {
+ pluginDictionary[plugin.Id] = plugin;
+ hardDependencies[plugin.Id] = plugin
+ .Dependencies
+ .Where(p => p.DependencyType == DependencyType.HardDependency)
+ .Select(p => p.Id)
+ .ToList();
+ }
+
+ var presentPlugins = pluginDictionary.Keys.ToList();
+
+ // Check whether the Hard Dependencies are present and remove those without.
+ var checkedPlugins = CheckHardDependencies(presentPlugins, hardDependencies);
+
+ var dependencyGraph = checkedPlugins.ToDictionary(p => p, _ => new List<string>());
+
+ foreach (var plugin in checkedPlugins)
+ {
+ foreach (var dependency in pluginDictionary[plugin].Dependencies.Where(d => checkedPlugins.Contains(d.Id)))
+ {
+ if (dependency.DependencyType == DependencyType.LoadBefore)
+ {
+ dependencyGraph[dependency.Id].Add(plugin);
+ }
+ else
+ {
+ dependencyGraph[plugin].Add(dependency.Id);
+ }
+ }
+ }
+
+ var processed = new List<string>();
+ var ordered = new List<PluginInformation>();
+ foreach (var plugin in checkedPlugins)
+ {
+ if (!processed.Contains(plugin))
+ {
+ RecursiveOrder(plugin, dependencyGraph, processed, ordered, pluginDictionary);
+ }
+ }
+
+ return ordered;
+ }
+
+ private static List<string> CheckHardDependencies(
+ List<string> plugins,
+ IReadOnlyDictionary<string, List<string>> hardDependencies)
+ {
+ foreach (var plugin in plugins)
+ {
+ if (!hardDependencies.ContainsKey(plugin))
+ {
+ continue;
+ }
+
+ foreach (var dependency in hardDependencies[plugin].Where(dependency => !plugins.Contains(dependency)))
+ {
+ Logger.Error(
+ "The plugin {plugin} has defined the plugin {dependency} as a hard dependency but its not present! {plugin} will not loaded.",
+ plugin,
+ dependency,
+ plugin
+ );
+
+ // Remove the plugin from the plugins to load.
+ plugins.Remove(plugin);
+
+ // Since other plugins might have defined the removed plugin as a hard dependency a recheck is necessary.
+ return CheckHardDependencies(plugins, hardDependencies);
+ }
+ }
+
+ return plugins;
+ }
+
+ private static void RecursiveOrder(
+ string plugin,
+ IReadOnlyDictionary<string, List<string>> dependencyGraph,
+ ICollection<string> processed,
+ ICollection<PluginInformation> ordered,
+ IReadOnlyDictionary<string, PluginInformation> pluginDictionary)
+ {
+ processed.Add(plugin);
+
+ foreach (var dependency in dependencyGraph[plugin])
+ {
+ // First add the dependencies using a recursive call before adding itself.
+ if (!processed.Contains(dependency))
+ {
+ RecursiveOrder(dependency, dependencyGraph, processed, ordered, pluginDictionary);
+ }
+ }
+
+ ordered.Add(pluginDictionary[plugin]);
+ }
}
}