mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Plugins - Implemented features enable/disable UI
This commit is contained in:
1 parent
e812929215
commit
c146479393
38 files changed
+571
-164
No files matched your search
@@ -7,6 +7,7 @@ using LiteDB;
|
||||
using Ninject.Activation;
|
||||
using Ninject.Extensions.Conventions;
|
||||
using Ninject.Modules;
|
||||
using Ninject.Planning.Bindings.Resolvers;
|
||||
using Serilog;
|
||||
|
||||
namespace Artemis.Core.Ninject
|
||||
@@ -22,6 +23,8 @@ namespace Artemis.Core.Ninject
|
||||
if (Kernel == null)
|
||||
throw new ArtemisCoreException("Failed to bind Ninject Core module, kernel is null.");
|
||||
|
||||
Kernel.Components.Remove<IMissingBindingResolver, SelfBindingResolver>();
|
||||
|
||||
// Bind all services as singletons
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
@@ -88,6 +91,7 @@ namespace Artemis.Core.Ninject
|
||||
|
||||
Kernel.Bind<PluginSettings>().ToProvider<PluginSettingsProvider>();
|
||||
Kernel.Bind<ILogger>().ToProvider<LoggerProvider>();
|
||||
Kernel.Bind<LoggerProvider>().ToSelf();
|
||||
}
|
||||
|
||||
private bool HasAccessToProtectedService(IRequest r)
|
||||
|
||||
@@ -1,19 +1,52 @@
|
||||
using System;
|
||||
using Artemis.Core.Services;
|
||||
using Ninject.Extensions.Conventions;
|
||||
using Ninject.Modules;
|
||||
using Ninject.Planning.Bindings.Resolvers;
|
||||
|
||||
namespace Artemis.Core.Ninject
|
||||
{
|
||||
internal class PluginModule : NinjectModule
|
||||
{
|
||||
public PluginModule(PluginInfo pluginInfo)
|
||||
public PluginModule(Plugin plugin)
|
||||
{
|
||||
PluginInfo = pluginInfo ?? throw new ArgumentNullException(nameof(pluginInfo));
|
||||
Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin));
|
||||
}
|
||||
|
||||
public PluginInfo PluginInfo { get; }
|
||||
public Plugin Plugin { get; }
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
if (Kernel == null)
|
||||
throw new ArtemisCoreException("Failed to bind plugin child module, kernel is null.");
|
||||
|
||||
Kernel.Components.Remove<IMissingBindingResolver, SelfBindingResolver>();
|
||||
|
||||
Kernel.Bind<Plugin>().ToConstant(Plugin);
|
||||
|
||||
// Bind plugin service interfaces
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.From(Plugin.Assembly)
|
||||
.IncludingNonPublicTypes()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<IPluginService>()
|
||||
.BindAllInterfaces()
|
||||
.Configure(c => c.InSingletonScope());
|
||||
});
|
||||
|
||||
// Plugin developers may not use an interface so bind the plugin services to themselves
|
||||
// Sadly if they do both, the kernel will treat the interface and the base type as two different singletons
|
||||
// perhaps we can avoid that, but I'm not sure how
|
||||
Kernel.Bind(x =>
|
||||
{
|
||||
x.From(Plugin.Assembly)
|
||||
.IncludingNonPublicTypes()
|
||||
.SelectAllClasses()
|
||||
.InheritedFrom<IPluginService>()
|
||||
.BindToSelf()
|
||||
.Configure(c => c.InSingletonScope());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Artemis.Core.Ninject
|
||||
private readonly IPluginRepository _pluginRepository;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
|
||||
internal PluginSettingsProvider(IPluginRepository pluginRepository, IPluginManagementService pluginManagementService)
|
||||
public PluginSettingsProvider(IPluginRepository pluginRepository, IPluginManagementService pluginManagementService)
|
||||
{
|
||||
_pluginRepository = pluginRepository;
|
||||
_pluginManagementService = pluginManagementService;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Artemis.Core.DataModelExpansions
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<PropertyInfo> HiddenProperties => HiddenPropertiesList.AsReadOnly();
|
||||
|
||||
internal DataModel InternalDataModel { get; set; }
|
||||
internal DataModel? InternalDataModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called each frame when the data model should update
|
||||
@@ -28,6 +28,12 @@ namespace Artemis.Core.DataModelExpansions
|
||||
/// <param name="deltaTime">Time in seconds since the last update</param>
|
||||
public abstract void Update(double deltaTime);
|
||||
|
||||
internal void InternalUpdate(double deltaTime)
|
||||
{
|
||||
if (InternalDataModel != null)
|
||||
Update(deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to provide your own data model description. By default this returns a description matching your plugin
|
||||
/// name and description
|
||||
|
||||
@@ -129,14 +129,15 @@ namespace Artemis.Core
|
||||
|
||||
internal void RemoveFeature(PluginFeature feature)
|
||||
{
|
||||
if (feature.IsEnabled)
|
||||
throw new ArtemisCoreException("Cannot remove an enabled feature from a plugin");
|
||||
|
||||
_features.Remove(feature);
|
||||
feature.InternalDisable();
|
||||
feature.Dispose();
|
||||
|
||||
OnFeatureRemoved(new PluginFeatureEventArgs(feature));
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal void SetEnabled(bool enable)
|
||||
{
|
||||
if (IsEnabled == enable)
|
||||
|
||||
@@ -87,8 +87,8 @@ namespace Artemis.Core
|
||||
throw new ArtemisPluginLockException(LoadException);
|
||||
}
|
||||
|
||||
IsEnabled = true;
|
||||
CreateLockFile();
|
||||
IsEnabled = true;
|
||||
|
||||
// Allow up to 15 seconds for plugins to activate.
|
||||
// This means plugins that need more time should do their long running tasks in a background thread, which is intentional
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
// Update all active modules, check Enabled status because it may go false before before the _dataModelExpansions list is updated
|
||||
foreach (BaseDataModelExpansion dataModelExpansion in _dataModelExpansions.Where(e => e.IsEnabled))
|
||||
dataModelExpansion.Update(args.DeltaTime);
|
||||
dataModelExpansion.InternalUpdate(args.DeltaTime);
|
||||
}
|
||||
|
||||
List<Module> modules;
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace Artemis.Core.Services
|
||||
/// Enables the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to enable</param>
|
||||
void EnablePlugin(Plugin plugin, bool ignorePluginLock = false);
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock = false);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads the provided <paramref name="plugin" />
|
||||
@@ -55,20 +56,23 @@ namespace Artemis.Core.Services
|
||||
/// Disables the provided <paramref name="plugin" />
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin to disable</param>
|
||||
void DisablePlugin(Plugin plugin);
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
void DisablePlugin(Plugin plugin, bool saveState);
|
||||
|
||||
/// <summary>
|
||||
/// Enables the provided plugin feature
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature"></param>
|
||||
/// <param name="pluginFeature">The feature to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
/// <param name="isAutoEnable">If true, fails if there is a lock file present</param>
|
||||
void EnablePluginFeature(PluginFeature pluginFeature, bool isAutoEnable = false);
|
||||
void EnablePluginFeature(PluginFeature pluginFeature, bool saveState, bool isAutoEnable = false);
|
||||
|
||||
/// <summary>
|
||||
/// Disables the provided plugin feature
|
||||
/// </summary>
|
||||
/// <param name="pluginFeature"></param>
|
||||
void DisablePluginFeature(PluginFeature pluginFeature);
|
||||
/// <param name="pluginFeature">The feature to enable</param>
|
||||
/// <param name="saveState">Whether or not to save the new enabled state</param>
|
||||
void DisablePluginFeature(PluginFeature pluginFeature, bool saveState);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin info of all loaded plugins
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for services provided by plugins.
|
||||
/// <para>
|
||||
/// Any service implementing this interface will be available inside the plugin as a singleton through dependency
|
||||
/// injection
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IPluginService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,8 @@ namespace Artemis.Core.Services
|
||||
try
|
||||
{
|
||||
Plugin plugin = LoadPlugin(subDirectory);
|
||||
EnablePlugin(plugin, ignorePluginLock);
|
||||
if (plugin.Entity.IsEnabled)
|
||||
EnablePlugin(plugin, false, ignorePluginLock);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -271,16 +272,13 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public void EnablePlugin(Plugin plugin, bool ignorePluginLock)
|
||||
public void EnablePlugin(Plugin plugin, bool saveState, bool ignorePluginLock)
|
||||
{
|
||||
if (plugin.Assembly == null)
|
||||
throw new ArtemisPluginException(plugin, "Cannot enable a plugin that hasn't successfully been loaded");
|
||||
|
||||
// Create the Ninject child kernel and load the module
|
||||
plugin.Kernel = new ChildKernel(_kernel);
|
||||
plugin.Kernel.Bind<Plugin>().ToConstant(plugin);
|
||||
plugin.Kernel.Load(new PluginModule(plugin.Info));
|
||||
|
||||
plugin.Kernel = new ChildKernel(_kernel, new PluginModule(plugin));
|
||||
plugin.SetEnabled(true);
|
||||
|
||||
// Get the Plugin feature from the main assembly and if there is only one, instantiate it
|
||||
@@ -303,6 +301,8 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
plugin.Kernel.Bind(featureType).ToSelf().InSingletonScope();
|
||||
|
||||
// Include Plugin as a parameter for the PluginSettingsProvider
|
||||
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
|
||||
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureType, parameters);
|
||||
@@ -314,7 +314,7 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ArtemisPluginException(plugin, "Failed to load instantiate feature", e);
|
||||
throw new ArtemisPluginException(plugin, "Failed to instantiate feature", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
EnablePluginFeature(pluginFeature, !ignorePluginLock);
|
||||
EnablePluginFeature(pluginFeature, false, !ignorePluginLock);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -331,6 +331,12 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
if (saveState)
|
||||
{
|
||||
plugin.Entity.IsEnabled = plugin.IsEnabled;
|
||||
SavePlugin(plugin);
|
||||
}
|
||||
|
||||
OnPluginEnabled(new PluginEventArgs(plugin));
|
||||
}
|
||||
|
||||
@@ -340,7 +346,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
DisablePlugin(plugin);
|
||||
DisablePlugin(plugin, false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -356,7 +362,7 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public void DisablePlugin(Plugin plugin)
|
||||
public void DisablePlugin(Plugin plugin, bool saveState)
|
||||
{
|
||||
if (!plugin.IsEnabled)
|
||||
return;
|
||||
@@ -364,14 +370,25 @@ namespace Artemis.Core.Services
|
||||
while (plugin.Features.Any())
|
||||
{
|
||||
PluginFeature feature = plugin.Features[0];
|
||||
if (feature.IsEnabled)
|
||||
DisablePluginFeature(feature, false);
|
||||
plugin.RemoveFeature(feature);
|
||||
OnPluginFeatureDisabled(new PluginFeatureEventArgs(feature));
|
||||
}
|
||||
|
||||
plugin.SetEnabled(false);
|
||||
|
||||
plugin.Kernel.Dispose();
|
||||
plugin.Kernel = null;
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
|
||||
if (saveState)
|
||||
{
|
||||
plugin.Entity.IsEnabled = plugin.IsEnabled;
|
||||
SavePlugin(plugin);
|
||||
}
|
||||
|
||||
OnPluginDisabled(new PluginEventArgs(plugin));
|
||||
}
|
||||
@@ -380,7 +397,7 @@ namespace Artemis.Core.Services
|
||||
|
||||
#region Features
|
||||
|
||||
public void EnablePluginFeature(PluginFeature pluginFeature, bool isAutoEnable = false)
|
||||
public void EnablePluginFeature(PluginFeature pluginFeature, bool saveState, bool isAutoEnable)
|
||||
{
|
||||
_logger.Debug("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
||||
|
||||
@@ -389,16 +406,9 @@ namespace Artemis.Core.Services
|
||||
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
|
||||
try
|
||||
{
|
||||
// A device provider may be queued for disable on next restart, this undoes that
|
||||
if (pluginFeature is DeviceProvider && pluginFeature.IsEnabled && !pluginFeature.Entity.IsEnabled)
|
||||
{
|
||||
pluginFeature.Entity.IsEnabled = true;
|
||||
SavePlugin(pluginFeature.Plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
pluginFeature.SetEnabled(true, isAutoEnable);
|
||||
pluginFeature.Entity.IsEnabled = true;
|
||||
if (saveState)
|
||||
pluginFeature.Entity.IsEnabled = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -412,10 +422,13 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
// On an auto-enable, ensure PluginInfo.Enabled is true even if enable failed, that way a failure on auto-enable does
|
||||
// not affect the user's settings
|
||||
if (isAutoEnable)
|
||||
pluginFeature.Entity.IsEnabled = true;
|
||||
if (saveState)
|
||||
{
|
||||
if (isAutoEnable)
|
||||
pluginFeature.Entity.IsEnabled = true;
|
||||
|
||||
SavePlugin(pluginFeature.Plugin);
|
||||
SavePlugin(pluginFeature.Plugin);
|
||||
}
|
||||
|
||||
if (pluginFeature.IsEnabled)
|
||||
{
|
||||
@@ -430,34 +443,22 @@ namespace Artemis.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public void DisablePluginFeature(PluginFeature pluginFeature)
|
||||
public void DisablePluginFeature(PluginFeature pluginFeature, bool saveState)
|
||||
{
|
||||
lock (_plugins)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Debug("Disabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
|
||||
|
||||
// Device providers cannot be disabled at runtime simply queue a disable for next restart
|
||||
if (pluginFeature is DeviceProvider)
|
||||
{
|
||||
// Don't call SetEnabled(false) but simply update enabled state and save it
|
||||
pluginFeature.Entity.IsEnabled = false;
|
||||
SavePlugin(pluginFeature.Plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
pluginFeature.SetEnabled(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
pluginFeature.Entity.IsEnabled = false;
|
||||
SavePlugin(pluginFeature.Plugin);
|
||||
if (saveState)
|
||||
{
|
||||
pluginFeature.Entity.IsEnabled = false;
|
||||
SavePlugin(pluginFeature.Plugin);
|
||||
}
|
||||
|
||||
if (!pluginFeature.IsEnabled)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Artemis.Core.Services
|
||||
{
|
||||
private readonly PluginSettings _pluginSettings;
|
||||
|
||||
internal SettingsService(IPluginRepository pluginRepository)
|
||||
public SettingsService(IPluginRepository pluginRepository)
|
||||
{
|
||||
_pluginSettings = new PluginSettings(Constants.CorePlugin, pluginRepository);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Artemis.Core.Services
|
||||
private readonly IProfileRepository _profileRepository;
|
||||
private readonly ISurfaceService _surfaceService;
|
||||
|
||||
internal ProfileService(ILogger logger,
|
||||
public ProfileService(ILogger logger,
|
||||
IPluginManagementService pluginManagementService,
|
||||
ISurfaceService surfaceService,
|
||||
IConditionOperatorService conditionOperatorService,
|
||||
|
||||
Reference in new issue
Block a user