From 8760b7f8387215b69a2efee7c8a3c2ed88e5ab24 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Mon, 8 Jan 2018 19:10:59 +0100 Subject: [PATCH] Some more plugin stuff, lovely commit messages ikr --- src/Artemis.Core/Artemis.Core.csproj | 6 +- src/Artemis.Core/Events/ModuleEventArgs.cs | 9 -- src/Artemis.Core/Events/PluginEventArgs.cs | 10 ++ .../Exceptions/ArtemisModuleException.cs | 25 ---- .../Exceptions/ArtemisPluginException.cs | 25 ++++ src/Artemis.Core/Models/ModuleInfo.cs | 17 --- src/Artemis.Core/Models/PluginInfo.cs | 40 +++++++ src/Artemis.Core/Services/CoreService.cs | 2 +- .../Services/Interfaces/IPluginService.cs | 36 +++++- src/Artemis.Core/Services/PluginService.cs | 111 ++++++++++-------- src/Artemis.Plugins/Artemis.Plugins.csproj | 3 + src/Artemis.Plugins/Interfaces/IModule.cs | 1 - src/Artemis.Plugins/Interfaces/IPlugin.cs | 13 +- src/Artemis.Plugins/packages.config | 2 +- src/Artemis.UI/Artemis.UI.csproj | 4 + src/Artemis.UI/ViewModels/HomeViewModel.cs | 20 ++++ 16 files changed, 216 insertions(+), 108 deletions(-) delete mode 100644 src/Artemis.Core/Events/ModuleEventArgs.cs create mode 100644 src/Artemis.Core/Events/PluginEventArgs.cs delete mode 100644 src/Artemis.Core/Exceptions/ArtemisModuleException.cs create mode 100644 src/Artemis.Core/Exceptions/ArtemisPluginException.cs delete mode 100644 src/Artemis.Core/Models/ModuleInfo.cs create mode 100644 src/Artemis.Core/Models/PluginInfo.cs diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 93bb7c259..399048bda 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -183,8 +183,8 @@ - - + + @@ -194,7 +194,7 @@ - + diff --git a/src/Artemis.Core/Events/ModuleEventArgs.cs b/src/Artemis.Core/Events/ModuleEventArgs.cs deleted file mode 100644 index d2b6fa813..000000000 --- a/src/Artemis.Core/Events/ModuleEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Artemis.Core.Plugins.Interfaces; - -namespace Artemis.Core.Events -{ - public class ModuleEventArgs : System.EventArgs - { - public IPlugin Plugin { get; set; } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Events/PluginEventArgs.cs b/src/Artemis.Core/Events/PluginEventArgs.cs new file mode 100644 index 000000000..a13e08df8 --- /dev/null +++ b/src/Artemis.Core/Events/PluginEventArgs.cs @@ -0,0 +1,10 @@ +using System; +using Artemis.Plugins.Interfaces; + +namespace Artemis.Core.Events +{ + public class PluginEventArgs : EventArgs + { + public IPlugin Plugin { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Exceptions/ArtemisModuleException.cs b/src/Artemis.Core/Exceptions/ArtemisModuleException.cs deleted file mode 100644 index a629d7126..000000000 --- a/src/Artemis.Core/Exceptions/ArtemisModuleException.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Artemis.Core.Models; - -namespace Artemis.Core.Exceptions -{ - public class ArtemisModuleException : Exception - { - public ArtemisModuleException(ModuleInfo moduleInfo) - { - ModuleInfo = moduleInfo; - } - - public ArtemisModuleException(ModuleInfo moduleInfo, string message) : base(message) - { - ModuleInfo = moduleInfo; - } - - public ArtemisModuleException(ModuleInfo moduleInfo, string message, Exception inner) : base(message, inner) - { - ModuleInfo = moduleInfo; - } - - public ModuleInfo ModuleInfo { get; } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Exceptions/ArtemisPluginException.cs b/src/Artemis.Core/Exceptions/ArtemisPluginException.cs new file mode 100644 index 000000000..0282b0295 --- /dev/null +++ b/src/Artemis.Core/Exceptions/ArtemisPluginException.cs @@ -0,0 +1,25 @@ +using System; +using Artemis.Core.Models; + +namespace Artemis.Core.Exceptions +{ + public class ArtemisPluginException : Exception + { + public ArtemisPluginException(PluginInfo pluginInfo) + { + PluginInfo = pluginInfo; + } + + public ArtemisPluginException(PluginInfo pluginInfo, string message) : base(message) + { + PluginInfo = pluginInfo; + } + + public ArtemisPluginException(PluginInfo pluginInfo, string message, Exception inner) : base(message, inner) + { + PluginInfo = pluginInfo; + } + + public PluginInfo PluginInfo { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/ModuleInfo.cs b/src/Artemis.Core/Models/ModuleInfo.cs deleted file mode 100644 index e47e98dfe..000000000 --- a/src/Artemis.Core/Models/ModuleInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using Artemis.Core.Plugins.Interfaces; -using Newtonsoft.Json; - -namespace Artemis.Core.Models -{ - public class ModuleInfo - { - public string Name { get; set; } - public string Version { get; set; } - public string MainFile { get; set; } - public IReadOnlyList SubFiles { get; set; } - - [JsonIgnore] - public IPlugin Plugin { get; set; } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Models/PluginInfo.cs b/src/Artemis.Core/Models/PluginInfo.cs new file mode 100644 index 000000000..1c8e2fa2b --- /dev/null +++ b/src/Artemis.Core/Models/PluginInfo.cs @@ -0,0 +1,40 @@ +using Artemis.Plugins.Interfaces; +using Newtonsoft.Json; + +namespace Artemis.Core.Models +{ + public class PluginInfo + { + /// + /// The name of the plugin + /// + public string Name { get; set; } + + /// + /// The version of the plugin + /// + public string Version { get; set; } + + /// + /// The file implementing IPlugin, loaded on startup + /// + public string Main { get; set; } + + /// + /// The file implementing IPluginViewModel, loaded when opened in the UI + /// + public string ViewModel { get; set; } + + /// + /// The instantiated plugin, available after successful load + /// + [JsonIgnore] + public IPlugin Plugin { get; set; } + + /// + /// Full path to the plugin's current folder + /// + [JsonIgnore] + public string Folder { get; set; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index ca61b1c68..434cb9ef4 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -22,7 +22,7 @@ namespace Artemis.Core.Services private async Task Initialize() { - await _pluginService.LoadModules(); + await _pluginService.LoadPlugins(); IsInitialized = true; } diff --git a/src/Artemis.Core/Services/Interfaces/IPluginService.cs b/src/Artemis.Core/Services/Interfaces/IPluginService.cs index ea83d4050..f086b1513 100644 --- a/src/Artemis.Core/Services/Interfaces/IPluginService.cs +++ b/src/Artemis.Core/Services/Interfaces/IPluginService.cs @@ -1,16 +1,44 @@ using System; +using System.Collections.ObjectModel; using System.Threading.Tasks; using Artemis.Core.Events; +using Artemis.Core.Models; using Artemis.Plugins.Interfaces; namespace Artemis.Core.Services.Interfaces { public interface IPluginService : IArtemisService, IDisposable { - Task LoadModules(); - Task ReloadModule(IPlugin plugin); + bool LoadingPlugins { get; } + ReadOnlyCollection Plugins { get; } - event EventHandler ModuleLoaded; - event EventHandler ModuleReloaded; + /// + /// Loads all installed plugins. If plugins already loaded this will reload them all + /// + /// + Task LoadPlugins(); + + Task ReloadPlugin(PluginInfo pluginInfo); + Task GetPluginViewModel(PluginInfo pluginInfo); + + /// + /// Occurs when a single plugin has loaded + /// + event EventHandler PluginLoaded; + + /// + /// Occurs when a single plugin has reloaded + /// + event EventHandler PluginReloaded; + + /// + /// Occurs when loading all plugins has started + /// + event EventHandler StartedLoadingPlugins; + + /// + /// Occurs when loading all plugins has finished + /// + event EventHandler FinishedLoadedPlugins; } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/PluginService.cs b/src/Artemis.Core/Services/PluginService.cs index 839dc12a9..475fbdace 100644 --- a/src/Artemis.Core/Services/PluginService.cs +++ b/src/Artemis.Core/Services/PluginService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; +using System.Linq; using System.Threading.Tasks; using Artemis.Core.Events; using Artemis.Core.Exceptions; @@ -10,109 +11,127 @@ using Artemis.Core.Services.Interfaces; using Artemis.Plugins.Interfaces; using CSScriptLibrary; using Newtonsoft.Json; +using Ninject; namespace Artemis.Core.Services { public class PluginService : IPluginService { - private readonly List _modules; + private readonly IKernel _kernel; + private readonly List _plugins; - public PluginService() + public PluginService(IKernel kernel) { - _modules = new List(); + _kernel = kernel; + _plugins = new List(); - if (!Directory.Exists(Constants.DataFolder + "modules")) - Directory.CreateDirectory(Constants.DataFolder + "modules"); + if (!Directory.Exists(Constants.DataFolder + "plugins")) + Directory.CreateDirectory(Constants.DataFolder + "plugins"); } - public bool LoadingModules { get; private set; } - public ReadOnlyCollection Modules => _modules.AsReadOnly(); + public bool LoadingPlugins { get; private set; } + public ReadOnlyCollection Plugins => _plugins.AsReadOnly(); /// - /// Loads all installed modules. If modules already loaded this will reload them all + /// Loads all installed plugins. If plugins already loaded this will reload them all /// /// - public async Task LoadModules() + public async Task LoadPlugins() { - if (LoadingModules) - throw new ArtemisCoreException("Cannot load modules while a previous load hasn't been completed yet."); + if (LoadingPlugins) + throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet."); - OnStartedLoadingModules(); + OnStartedLoadingPlugins(); - // Empty the list of modules - _modules.Clear(); - // Iterate all module folders - foreach (var directory in Directory.GetDirectories(Constants.DataFolder + "modules")) - // Load each module - _modules.Add(await LoadModuleFromFolder(directory)); + // Empty the list of plugins + _plugins.Clear(); - - OnFinishedLoadedModules(); + // Iterate all plugin folders and load each plugin + foreach (var directory in Directory.GetDirectories(Constants.DataFolder + "plugins")) + _plugins.Add(await LoadPluginFromFolder(directory)); + + OnFinishedLoadedPlugins(); } - public async Task ReloadModule(IPlugin plugin) + public async Task ReloadPlugin(PluginInfo pluginInfo) { } + public async Task GetPluginViewModel(PluginInfo pluginInfo) + { + // Compile the ViewModel and get the type + var compile = await Task.Run(() => CSScript.LoadFile(pluginInfo.Folder + pluginInfo.ViewModel)); + var vmType = compile.ExportedTypes.FirstOrDefault(t => typeof(IPluginViewModel).IsAssignableFrom(t)); + if (vmType == null) + throw new ArtemisPluginException(pluginInfo, "Cannot locate a view model for this plugin."); + + // Instantiate the ViewModel with Ninject + return (IPluginViewModel) _kernel.Get(vmType); + } + public void Dispose() { } - private async Task LoadModuleFromFolder(string folder) + private async Task LoadPluginFromFolder(string folder) { if (!folder.EndsWith("\\")) folder += "\\"; - if (!File.Exists(folder + "module.json")) - throw new ArtemisModuleException(null, "Failed to load module, no module.json found in " + folder); + if (!File.Exists(folder + "plugin.json")) + throw new ArtemisPluginException(null, "Failed to load plugin, no plugin.json found in " + folder); - var moduleInfo = JsonConvert.DeserializeObject(File.ReadAllText(folder + "module.json")); - // Load the main module which will contain a class implementing IModule - var module = await CSScript.Evaluator.LoadFileAsync(folder + moduleInfo.MainFile); - return module; + var pluginInfo = JsonConvert.DeserializeObject(File.ReadAllText(folder + "plugin.json")); + pluginInfo.Folder = folder; + + // Load the main plugin which will contain a class implementing IPlugin + var plugin = await CSScript.Evaluator.LoadFileAsync(folder + pluginInfo.Main); + pluginInfo.Plugin = plugin; + + return pluginInfo; } #region Events /// - /// Occurs when a single module has loaded + /// Occurs when a single plugin has loaded /// - public event EventHandler ModuleLoaded; + public event EventHandler PluginLoaded; /// - /// Occurs when a single module has reloaded + /// Occurs when a single plugin has reloaded /// - public event EventHandler ModuleReloaded; + public event EventHandler PluginReloaded; /// - /// Occurs when loading all modules has started + /// Occurs when loading all plugins has started /// - public event EventHandler StartedLoadingModules; + public event EventHandler StartedLoadingPlugins; /// - /// Occurs when loading all modules has finished + /// Occurs when loading all plugins has finished /// - public event EventHandler FinishedLoadedModules; + public event EventHandler FinishedLoadedPlugins; - private void OnModuleLoaded(ModuleEventArgs e) + private void OnPluginLoaded(PluginEventArgs e) { - ModuleLoaded?.Invoke(this, e); + PluginLoaded?.Invoke(this, e); } - private void OnModuleReloaded(ModuleEventArgs e) + private void OnPluginReloaded(PluginEventArgs e) { - ModuleReloaded?.Invoke(this, e); + PluginReloaded?.Invoke(this, e); } - private void OnStartedLoadingModules() + private void OnStartedLoadingPlugins() { - LoadingModules = true; - StartedLoadingModules?.Invoke(this, EventArgs.Empty); + LoadingPlugins = true; + StartedLoadingPlugins?.Invoke(this, EventArgs.Empty); } - private void OnFinishedLoadedModules() + private void OnFinishedLoadedPlugins() { - LoadingModules = false; - FinishedLoadedModules?.Invoke(this, EventArgs.Empty); + LoadingPlugins = false; + FinishedLoadedPlugins?.Invoke(this, EventArgs.Empty); } #endregion diff --git a/src/Artemis.Plugins/Artemis.Plugins.csproj b/src/Artemis.Plugins/Artemis.Plugins.csproj index 4bc6760f5..1f0dbc33a 100644 --- a/src/Artemis.Plugins/Artemis.Plugins.csproj +++ b/src/Artemis.Plugins/Artemis.Plugins.csproj @@ -30,6 +30,9 @@ 4 + + ..\packages\Ninject.3.3.4\lib\net45\Ninject.dll + ..\packages\Stylet.1.1.21\lib\net45\Stylet.dll diff --git a/src/Artemis.Plugins/Interfaces/IModule.cs b/src/Artemis.Plugins/Interfaces/IModule.cs index 377ad9c6e..c666ecc8e 100644 --- a/src/Artemis.Plugins/Interfaces/IModule.cs +++ b/src/Artemis.Plugins/Interfaces/IModule.cs @@ -2,6 +2,5 @@ { public interface IModule : IPlugin { - IPluginViewModel GetMainViewModel(); } } \ No newline at end of file diff --git a/src/Artemis.Plugins/Interfaces/IPlugin.cs b/src/Artemis.Plugins/Interfaces/IPlugin.cs index 8fdba572c..d9063dacc 100644 --- a/src/Artemis.Plugins/Interfaces/IPlugin.cs +++ b/src/Artemis.Plugins/Interfaces/IPlugin.cs @@ -1,6 +1,17 @@ -namespace Artemis.Plugins.Interfaces +using System.Threading.Tasks; + +namespace Artemis.Plugins.Interfaces { public interface IPlugin { + /// + /// Called when the plugin is loaded + /// + void LoadPlugin(); + + /// + /// Called when the plugin is unloaded + /// + void UnloadPlugin(); } } \ No newline at end of file diff --git a/src/Artemis.Plugins/packages.config b/src/Artemis.Plugins/packages.config index 0ef55cc6d..90d89f33c 100644 --- a/src/Artemis.Plugins/packages.config +++ b/src/Artemis.Plugins/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 5c0d7cb28..8158fc1d2 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -151,6 +151,10 @@ {9b811f9b-86b9-4771-87af-72bae7078a36} Artemis.Core + + {cd23bc5e-57f0-46ce-a007-24d031146219} + Artemis.Plugins + {e489e5e3-1a65-4af5-a1ea-f9805fd19a65} Artemis.Storage diff --git a/src/Artemis.UI/ViewModels/HomeViewModel.cs b/src/Artemis.UI/ViewModels/HomeViewModel.cs index 25d99189d..367ec1a12 100644 --- a/src/Artemis.UI/ViewModels/HomeViewModel.cs +++ b/src/Artemis.UI/ViewModels/HomeViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using Artemis.Core.Services.Interfaces; using Artemis.UI.ViewModels.Interfaces; using Stylet; @@ -7,6 +8,15 @@ namespace Artemis.UI.ViewModels { public class HomeViewModel : Screen, IHomeViewModel { + private readonly IPluginService _pluginService; + + public HomeViewModel(IPluginService pluginService) + { + _pluginService = pluginService; + + _pluginService.FinishedLoadedPlugins += PluginServiceOnFinishedLoadedPlugins; + } + public string Title => "Home"; public void OpenUrl(string url) @@ -15,5 +25,15 @@ namespace Artemis.UI.ViewModels if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) Process.Start(url); } + + /// + /// Populates the sidebar with plugins when they are finished loading + /// + /// + /// + private void PluginServiceOnFinishedLoadedPlugins(object sender, EventArgs eventArgs) + { + throw new NotImplementedException(); + } } } \ No newline at end of file