diff --git a/src/Zirconium/Core/Plugins/Interfaces/IPluginAPI.cs b/src/Zirconium/Core/Plugins/Interfaces/IPluginAPI.cs index 932a7f6..8f19337 100644 --- a/src/Zirconium/Core/Plugins/Interfaces/IPluginAPI.cs +++ b/src/Zirconium/Core/Plugins/Interfaces/IPluginAPI.cs @@ -3,6 +3,8 @@ namespace Zirconium.Core.Plugins.Interfaces public interface IPluginAPI { void Initialize(IPluginHostAPI hostModuleAPI); + void PreInitialize(IPluginManager pluginManager); string GetPluginUniqueName(); + dynamic GetExportedAPI(); } } \ No newline at end of file diff --git a/src/Zirconium/Core/Plugins/Interfaces/IPluginManager.cs b/src/Zirconium/Core/Plugins/Interfaces/IPluginManager.cs new file mode 100644 index 0000000..7196d6c --- /dev/null +++ b/src/Zirconium/Core/Plugins/Interfaces/IPluginManager.cs @@ -0,0 +1,7 @@ +namespace Zirconium.Core.Plugins.Interfaces +{ + public interface IPluginManager + { + dynamic Depends(IPluginAPI currentPlugin, string pluginName); + } +} \ No newline at end of file diff --git a/src/Zirconium/Core/Plugins/PluginManager.cs b/src/Zirconium/Core/Plugins/PluginManager.cs index 8ada71e..89f9c07 100644 --- a/src/Zirconium/Core/Plugins/PluginManager.cs +++ b/src/Zirconium/Core/Plugins/PluginManager.cs @@ -6,20 +6,22 @@ using System.Threading; using McMaster.NETCore.Plugins; using Zirconium.Core.Models; using Zirconium.Core.Plugins.Interfaces; +using Zirconium.Utils; namespace Zirconium.Core.Plugins { // Class which responsible for plugin managing (loading, initializing) and plugin lifetime cycle - public class PluginManager + public class PluginManager : IPluginManager { - private IList _plugins; + private IDictionary _plugins; private IPluginHostAPI _pluginHostAPI; private Mutex _pluginsMutex; + private string _currentPluginFolderPath; public PluginManager(IPluginHostAPI hostModuleAPI) { _pluginHostAPI = hostModuleAPI; - _plugins = new List(); + _plugins = new Dictionary(); _pluginsMutex = new Mutex(); } @@ -32,48 +34,71 @@ namespace Zirconium.Core.Plugins return; } - // create module loaders + _currentPluginFolderPath = folderPath; + foreach (var dir in Directory.GetDirectories(folderPath)) { - var dirName = Path.GetFileName(dir); - if (enabledPlugins.Where(x => x == dirName).FirstOrDefault() == null) { + var pluginName = Path.GetFileName(dir); + if (enabledPlugins.Where(x => x == pluginName).FirstOrDefault() == null) + { continue; } - var pluginDll = Path.Combine(dir, dirName + ".dll"); - if (File.Exists(pluginDll)) - { - Logging.Log.Debug("found plugin " + dirName); - Logging.Log.Debug("try to init plugin " + dirName); - var loader = PluginLoader.CreateFromAssemblyFile( - pluginDll, - sharedTypes: new[] { + + var plugin = this.LoadPlugin(pluginName); + _pluginsMutex.WaitOne(); + _plugins[pluginName] = plugin; + _pluginsMutex.ReleaseMutex(); + } + } + + public IPluginAPI LoadPlugin(string pluginName) + { + PluginLoader loader; + var pluginDll = Path.Combine(_currentPluginFolderPath, pluginName + ".dll"); + if (File.Exists(pluginDll)) + { + Logging.Log.Debug("Found plugin " + pluginName); + Logging.Log.Debug("Try to initialize plugin " + pluginName); + loader = PluginLoader.CreateFromAssemblyFile( + pluginDll, + sharedTypes: new[] { typeof(IPluginAPI), typeof(IPluginHostAPI), + typeof(IPluginManager), typeof(IC2SMessageHandler), typeof(ICoreEventHandler), typeof(BaseMessage), typeof(CoreEvent) - } - ); - loaders.Add(loader); - } + } + ); + } + else + { + throw new Exception("specified plugin is not found"); } - // Create an instance of module types - foreach (var loader in loaders) - { - foreach (var pluginType in loader + IPluginAPI plugin = null; + foreach (var pluginType in loader .LoadDefaultAssembly() .GetTypes() .Where(t => typeof(IPluginAPI).IsAssignableFrom(t) && !t.IsAbstract)) - { - // This assumes the implementation of IPlugin has a parameterless constructor - IPluginAPI plugin = (IPluginAPI)Activator.CreateInstance(pluginType); - Logging.Log.Debug($"Created plugin instance '{plugin.GetPluginUniqueName()}'."); - plugin.Initialize(_pluginHostAPI); - _plugins.Add(plugin); - } + { + // This assumes the implementation of IPlugin has a parameterless constructor + plugin = (IPluginAPI)Activator.CreateInstance(pluginType); + Logging.Log.Debug($"Created plugin instance '{plugin.GetPluginUniqueName()}'."); + plugin.PreInitialize(this); + plugin.Initialize(_pluginHostAPI); } + return plugin; + } + + public dynamic Depends(IPluginAPI currentPlugin, string pluginName) + { + var dependantPlugin = _plugins.GetValueOrDefault(pluginName, null); + if (dependantPlugin != null) return dependantPlugin.GetExportedAPI(); + this.LoadPlugin(pluginName); + dependantPlugin = _plugins[pluginName]; + return dependantPlugin.GetExportedAPI(); } } } \ No newline at end of file diff --git a/src/ZirconiumPluginExamples/HelloWorldPlugin/HelloWorldPlugin.cs b/src/ZirconiumPluginExamples/HelloWorldPlugin/HelloWorldPlugin.cs index 0fbe7e7..1076657 100644 --- a/src/ZirconiumPluginExamples/HelloWorldPlugin/HelloWorldPlugin.cs +++ b/src/ZirconiumPluginExamples/HelloWorldPlugin/HelloWorldPlugin.cs @@ -15,13 +15,18 @@ namespace HelloWorldPlugin hostModuleAPI.Hook(handler); Log.Debug("plugin is initialized"); } + + public void PreInitialize(IPluginManager pluginManager) { } + + public dynamic GetExportedAPI() { return null; } } internal class C2SHandler : IC2SMessageHandler { private IPluginHostAPI hostModuleAPI; - public C2SHandler(IPluginHostAPI hostModuleAPI) { + public C2SHandler(IPluginHostAPI hostModuleAPI) + { this.hostModuleAPI = hostModuleAPI; }