1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Plugins - Implemented features

Core - Removed Stylet dependency for #500
This commit is contained in:
SpoinkyNL 2020-11-11 23:53:52 +01:00
parent b56966c585
commit e812929215
139 changed files with 1635 additions and 1106 deletions

View File

@ -50,7 +50,6 @@
<PackageReference Include="Serilog.Sinks.Debug" Version="1.0.1" /> <PackageReference Include="Serilog.Sinks.Debug" Version="1.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="SkiaSharp" Version="1.68.3" /> <PackageReference Include="SkiaSharp" Version="1.68.3" />
<PackageReference Include="Stylet" Version="1.3.4" />
<PackageReference Include="System.Buffers" Version="4.5.0" /> <PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" /> <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.Reflection.Metadata" Version="1.8.0" /> <PackageReference Include="System.Reflection.Metadata" Version="1.8.0" />

View File

@ -42,6 +42,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayerproperties_005Ctypes/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayerproperties_005Ctypes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayershapes/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cprofile_005Clayershapes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Csurface/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Csurface/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mvvm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=placeholders/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=placeholders/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cdatamodelexpansions_005Cattributes/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins_005Cdatamodelexpansions_005Cattributes/@EntryIndexedValue">True</s:Boolean>

View File

@ -12,7 +12,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The full path to the Artemis application folder /// The full path to the Artemis application folder
/// </summary> /// </summary>
public static readonly string ApplicationFolder = Path.GetDirectoryName(typeof(Constants).Assembly.Location); public static readonly string ApplicationFolder = Path.GetDirectoryName(typeof(Constants).Assembly.Location)!;
/// <summary> /// <summary>
/// The full path to the Artemis executable /// The full path to the Artemis executable
@ -34,11 +34,16 @@ namespace Artemis.Core
/// </summary> /// </summary>
public static readonly PluginInfo CorePluginInfo = new PluginInfo public static readonly PluginInfo CorePluginInfo = new PluginInfo
{ {
Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core", IsEnabled = true Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"
}; };
internal static readonly CorePluginImplementation CorePluginImplementation = new CorePluginImplementation {PluginInfo = CorePluginInfo}; /// <summary>
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {PluginInfo = CorePluginInfo}; /// The plugin used by core components of Artemis
/// </summary>
public static readonly Plugin CorePlugin = new Plugin(CorePluginInfo, new DirectoryInfo(ApplicationFolder));
internal static readonly CorePluginFeature CorePluginFeature = new CorePluginFeature {Plugin = CorePlugin};
internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {Plugin = CorePlugin};
/// <summary> /// <summary>
/// A read-only collection containing all primitive numeric types /// A read-only collection containing all primitive numeric types

View File

@ -0,0 +1,18 @@
using System;
namespace Artemis.Core
{
public class PluginFeatureEventArgs : EventArgs
{
public PluginFeatureEventArgs()
{
}
public PluginFeatureEventArgs(PluginFeature pluginFeature)
{
PluginFeature = pluginFeature;
}
public PluginFeature PluginFeature { get; }
}
}

View File

@ -1,18 +0,0 @@
using System;
namespace Artemis.Core
{
public class PluginImplementationEventArgs : EventArgs
{
public PluginImplementationEventArgs()
{
}
public PluginImplementationEventArgs(PluginImplementation pluginImplementation)
{
PluginImplementation = pluginImplementation;
}
public PluginImplementation PluginImplementation { get; }
}
}

View File

@ -0,0 +1,24 @@
using System;
namespace Artemis.Core
{
public class ArtemisPluginFeatureException : Exception
{
public PluginFeature PluginFeature { get; }
public ArtemisPluginFeatureException(PluginFeature pluginFeature)
{
PluginFeature = pluginFeature;
}
public ArtemisPluginFeatureException(PluginFeature pluginFeature, string message) : base(message)
{
PluginFeature = pluginFeature;
}
public ArtemisPluginFeatureException(PluginFeature pluginFeature, string message, Exception inner) : base(message, inner)
{
PluginFeature = pluginFeature;
}
}
}

View File

@ -0,0 +1,72 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Artemis.Core
{
/// <summary>
/// Represents a basic bindable class which notifies when a property value changes.
/// </summary>
public abstract class CorePropertyChanged : INotifyPropertyChanged
{
#region Events
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
/// <summary>
/// Checks if the property already matches the desirec value or needs to be updated.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual bool RequiresUpdate<T>(ref T storage, T value)
{
return !Equals(storage, value);
}
/// <summary>
/// Checks if the property already matches the desired value and updates it if not.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute" />
/// .
/// </param>
/// <returns><c>true</c> if the value was changed, <c>false</c> if the existing value matched the desired value.</returns>
protected virtual bool SetAndNotify<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (!RequiresUpdate(ref storage, value)) return false;
storage = value;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Triggers the <see cref="PropertyChanged" />-event when a a property value has changed.
/// </summary>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute" />
/// .
/// </param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View File

@ -5,7 +5,6 @@ using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Artemis.Core.Properties; using Artemis.Core.Properties;
using SkiaSharp; using SkiaSharp;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -19,13 +18,13 @@ namespace Artemis.Core
/// </summary> /// </summary>
public ColorGradient() public ColorGradient()
{ {
Stops = new BindableCollection<ColorGradientStop>(); Stops = new List<ColorGradientStop>();
} }
/// <summary> /// <summary>
/// Gets a list of all the <see cref="ColorGradientStop" />s in the gradient /// Gets a list of all the <see cref="ColorGradientStop" />s in the gradient
/// </summary> /// </summary>
public BindableCollection<ColorGradientStop> Stops { get; } public List<ColorGradientStop> Stops { get; }
/// <summary> /// <summary>
/// Gets all the colors in the color gradient /// Gets all the colors in the color gradient

View File

@ -22,10 +22,10 @@ namespace Artemis.Core
public abstract string Icon { get; } public abstract string Icon { get; }
/// <summary> /// <summary>
/// Gets the plugin info this condition operator belongs to /// Gets the plugin this condition operator belongs to
/// <para>Note: Not set until after registering</para> /// <para>Note: Not set until after registering</para>
/// </summary> /// </summary>
public PluginInfo PluginInfo { get; internal set; } public Plugin Plugin { get; internal set; }
/// <summary> /// <summary>
/// Gets the left side type of this condition operator /// Gets the left side type of this condition operator

View File

@ -346,7 +346,7 @@ namespace Artemis.Core
if (Operator != null) if (Operator != null)
{ {
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid; Entity.OperatorPluginGuid = Operator.Plugin.Guid;
Entity.OperatorType = Operator.GetType().Name; Entity.OperatorType = Operator.GetType().Name;
} }
} }
@ -358,7 +358,7 @@ namespace Artemis.Core
private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e) private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e)
{ {
BaseConditionOperator conditionOperator = e.Registration.ConditionOperator; BaseConditionOperator conditionOperator = e.Registration.ConditionOperator;
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name) if (Entity.OperatorPluginGuid == conditionOperator.Plugin.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
UpdateOperator(conditionOperator); UpdateOperator(conditionOperator);
} }

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
{ {
internal EventPredicateWrapperDataModel() internal EventPredicateWrapperDataModel()
{ {
Implementation = Constants.CorePluginInfo; Feature = Constants.CorePluginFeature;
} }
[DataModelIgnore] [DataModelIgnore]

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
{ {
internal ListPredicateWrapperDataModel() internal ListPredicateWrapperDataModel()
{ {
Implementation = Constants.CorePluginInfo; Feature = Constants.CorePluginFeature;
} }
[DataModelIgnore] [DataModelIgnore]

View File

@ -12,10 +12,10 @@ namespace Artemis.Core
public abstract class BaseDataBindingModifierType public abstract class BaseDataBindingModifierType
{ {
/// <summary> /// <summary>
/// Gets the plugin info this data binding modifier belongs to /// Gets the plugin this data binding modifier belongs to
/// <para>Note: Not set until after registering</para> /// <para>Note: Not set until after registering</para>
/// </summary> /// </summary>
public PluginInfo PluginInfo { get; internal set; } public Plugin Plugin { get; internal set; }
/// <summary> /// <summary>
/// Gets the value type of this modifier type /// Gets the value type of this modifier type

View File

@ -238,7 +238,7 @@ namespace Artemis.Core
if (ModifierType != null) if (ModifierType != null)
{ {
Entity.ModifierType = ModifierType.GetType().Name; Entity.ModifierType = ModifierType.GetType().Name;
Entity.ModifierTypePluginGuid = ModifierType.PluginInfo.Guid; Entity.ModifierTypePluginGuid = ModifierType.Plugin.Guid;
} }
// General // General
@ -286,7 +286,7 @@ namespace Artemis.Core
return; return;
BaseDataBindingModifierType modifierType = e.TypeRegistration.DataBindingModifierType; BaseDataBindingModifierType modifierType = e.TypeRegistration.DataBindingModifierType;
if (modifierType.PluginInfo.Guid == Entity.ModifierTypePluginGuid && modifierType.GetType().Name == Entity.ModifierType) if (modifierType.Plugin.Guid == Entity.ModifierTypePluginGuid && modifierType.GetType().Name == Entity.ModifierType)
UpdateModifierType(modifierType); UpdateModifierType(modifierType);
} }

View File

@ -91,9 +91,9 @@ namespace Artemis.Core
public DataModel? Target { get; private set; } public DataModel? Target { get; private set; }
/// <summary> /// <summary>
/// Gets the data model GUID of the <see cref="Target" /> if it is a <see cref="DataModel" /> /// Gets the data model ID of the <see cref="Target" /> if it is a <see cref="DataModel" />
/// </summary> /// </summary>
public Guid? DataModelGuid => Target?.Implementation.Guid; public string? DataModelId => Target?.Feature.Id;
/// <summary> /// <summary>
/// Gets the point-separated path associated with this <see cref="DataModelPath" /> /// Gets the point-separated path associated with this <see cref="DataModelPath" />
@ -267,8 +267,8 @@ namespace Artemis.Core
{ {
Path = Entity.Path; Path = Entity.Path;
if (Target == null && Entity.DataModelGuid != null) if (Target == null && Entity.DataModelId != null)
Target = DataModelStore.Get(Entity.DataModelGuid.Value)?.DataModel; Target = DataModelStore.Get(Entity.DataModelId)?.DataModel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -279,7 +279,7 @@ namespace Artemis.Core
return; return;
Entity.Path = Path; Entity.Path = Path;
Entity.DataModelGuid = DataModelGuid; Entity.DataModelId = DataModelId;
Entity.WrapperType = Target switch Entity.WrapperType = Target switch
{ {
@ -295,7 +295,7 @@ namespace Artemis.Core
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e) private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
{ {
if (e.Registration.DataModel.Implementation.Guid != Entity.DataModelGuid) if (e.Registration.DataModel.Feature.Id != Entity.DataModelId)
return; return;
Target = e.Registration.DataModel; Target = e.Registration.DataModel;
@ -304,7 +304,7 @@ namespace Artemis.Core
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e) private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
{ {
if (e.Registration.DataModel.Implementation.Guid != Entity.DataModelGuid) if (e.Registration.DataModel.Feature.Id != Entity.DataModelId)
return; return;
Target = null; Target = null;

View File

@ -190,9 +190,9 @@ namespace Artemis.Core
typeof(PropertyGroupDescriptionAttribute) typeof(PropertyGroupDescriptionAttribute)
); );
General.GroupDescription = (PropertyGroupDescriptionAttribute) generalAttribute; General.GroupDescription = (PropertyGroupDescriptionAttribute) generalAttribute;
General.Initialize(this, "General.", Constants.CorePluginInfo); General.Initialize(this, "General.", Constants.CorePluginFeature);
Transform.GroupDescription = (PropertyGroupDescriptionAttribute) transformAttribute; Transform.GroupDescription = (PropertyGroupDescriptionAttribute) transformAttribute;
Transform.Initialize(this, "Transform.", Constants.CorePluginInfo); Transform.Initialize(this, "Transform.", Constants.CorePluginFeature);
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet; General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
ApplyShapeType(); ApplyShapeType();
@ -619,7 +619,7 @@ namespace Artemis.Core
BaseLayerBrush brush = LayerBrush; BaseLayerBrush brush = LayerBrush;
DeactivateLayerBrush(); DeactivateLayerBrush();
LayerEntity.PropertyEntities.RemoveAll(p => p.PluginGuid == brush.PluginInfo.Guid && p.Path.StartsWith("LayerBrush.")); LayerEntity.PropertyEntities.RemoveAll(p => p.FeatureId == brush.ProviderId && p.Path.StartsWith("LayerBrush."));
} }
internal void ActivateLayerBrush() internal void ActivateLayerBrush()
@ -628,7 +628,7 @@ namespace Artemis.Core
if (current == null) if (current == null)
return; return;
LayerBrushDescriptor descriptor = LayerBrushStore.Get(current.BrushPluginGuid, current.BrushType)?.LayerBrushDescriptor; LayerBrushDescriptor? descriptor = LayerBrushStore.Get(current.LayerBrushProviderId, current.BrushType)?.LayerBrushDescriptor;
descriptor?.CreateInstance(this); descriptor?.CreateInstance(this);
OnLayerBrushUpdated(); OnLayerBrushUpdated();
@ -662,7 +662,7 @@ namespace Artemis.Core
return; return;
LayerBrushReference current = General.BrushReference.CurrentValue; LayerBrushReference current = General.BrushReference.CurrentValue;
if (e.Registration.PluginImplementation.PluginInfo.Guid == current.BrushPluginGuid && if (e.Registration.PluginFeature.Id == current.LayerBrushProviderId &&
e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType) e.Registration.LayerBrushDescriptor.LayerBrushType.Name == current.BrushType)
ActivateLayerBrush(); ActivateLayerBrush();
} }

View File

@ -1,5 +1,4 @@
using System; using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerBrushes;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -8,24 +7,24 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class LayerBrushReference public class LayerBrushReference
{ {
/// <summary>
/// The GUID of the plugin the brush descriptor resides in
/// </summary>
public Guid BrushPluginGuid { get; set; }
/// <summary>
/// The full type name of the brush descriptor
/// </summary>
public string BrushType { get; set; }
public LayerBrushReference() public LayerBrushReference()
{ {
} }
public LayerBrushReference(LayerBrushDescriptor descriptor) public LayerBrushReference(LayerBrushDescriptor descriptor)
{ {
BrushPluginGuid = descriptor.LayerBrushProvider.PluginInfo.Guid; LayerBrushProviderId = descriptor.Provider.Id;
BrushType = descriptor.LayerBrushType.Name; BrushType = descriptor.LayerBrushType.Name;
} }
/// <summary>
/// The ID of the layer brush provided the brush was provided by
/// </summary>
public string LayerBrushProviderId { get; set; }
/// <summary>
/// The full type name of the brush descriptor
/// </summary>
public string BrushType { get; set; }
} }
} }

View File

@ -1,12 +1,11 @@
using System; using System;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents a keyframe on a <see cref="LayerProperty{T}" /> containing a value and a timestamp /// Represents a keyframe on a <see cref="LayerProperty{T}" /> containing a value and a timestamp
/// </summary> /// </summary>
public class LayerPropertyKeyframe<T> : PropertyChangedBase public class LayerPropertyKeyframe<T> : CorePropertyChanged
{ {
private LayerProperty<T> _layerProperty; private LayerProperty<T> _layerProperty;
private TimeSpan _position; private TimeSpan _position;

View File

@ -28,11 +28,11 @@ namespace Artemis.Core
/// Gets the description of this group /// Gets the description of this group
/// </summary> /// </summary>
public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; } public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; }
/// <summary> /// <summary>
/// Gets the info of the plugin this group is associated with /// Gets the plugin feature this group is associated with
/// </summary> /// </summary>
public PluginInfo PluginInfo { get; internal set; } public PluginFeature Feature { get; set; }
/// <summary> /// <summary>
/// Gets the profile element (such as layer or folder) this group is associated with /// Gets the profile element (such as layer or folder) this group is associated with
@ -145,18 +145,18 @@ namespace Artemis.Core
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty); PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
} }
internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginInfo pluginInfo) internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginFeature feature)
{ {
if (path == null) if (path == null)
throw new ArgumentNullException(nameof(path)); throw new ArgumentNullException(nameof(path));
if (pluginInfo == null) if (feature == null)
throw new ArgumentNullException(nameof(pluginInfo)); throw new ArgumentNullException(nameof(feature));
// Doubt this will happen but let's make sure // Doubt this will happen but let's make sure
if (PropertiesInitialized) if (PropertiesInitialized)
throw new ArtemisCoreException("Layer property group already initialized, wut"); throw new ArtemisCoreException("Layer property group already initialized, wut");
PluginInfo = pluginInfo; Feature = feature;
ProfileElement = profileElement; ProfileElement = profileElement;
Path = path.TrimEnd('.'); Path = path.TrimEnd('.');
@ -246,7 +246,7 @@ namespace Artemis.Core
instance.GroupDescription = propertyGroupDescription; instance.GroupDescription = propertyGroupDescription;
instance.LayerBrush = LayerBrush; instance.LayerBrush = LayerBrush;
instance.LayerEffect = LayerEffect; instance.LayerEffect = LayerEffect;
instance.Initialize(ProfileElement, $"{path}{propertyInfo.Name}.", PluginInfo); instance.Initialize(ProfileElement, $"{path}{propertyInfo.Name}.", Feature);
propertyInfo.SetValue(this, instance); propertyInfo.SetValue(this, instance);
_layerPropertyGroups.Add(instance); _layerPropertyGroups.Add(instance);
@ -254,11 +254,11 @@ namespace Artemis.Core
private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage) private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage)
{ {
PropertyEntity entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == PluginInfo.Guid && p.Path == path); PropertyEntity entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.FeatureId == Feature.Id && p.Path == path);
fromStorage = entity != null; fromStorage = entity != null;
if (entity == null) if (entity == null)
{ {
entity = new PropertyEntity {PluginGuid = PluginInfo.Guid, Path = path}; entity = new PropertyEntity {FeatureId = Feature.Id, Path = path};
profileElement.RenderElementEntity.PropertyEntities.Add(entity); profileElement.RenderElementEntity.PropertyEntities.Add(entity);
} }

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Core.Modules; using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Newtonsoft.Json;
using SkiaSharp; using SkiaSharp;
namespace Artemis.Core namespace Artemis.Core
@ -147,7 +146,7 @@ namespace Artemis.Core
throw new ObjectDisposedException("Profile"); throw new ObjectDisposedException("Profile");
ProfileEntity.Id = EntityId; ProfileEntity.Id = EntityId;
ProfileEntity.PluginGuid = Module.PluginInfo.Guid; ProfileEntity.ModuleId = Module.Id;
ProfileEntity.Name = Name; ProfileEntity.Name = Name;
ProfileEntity.IsActive = IsActivated; ProfileEntity.IsActive = IsActivated;

View File

@ -3,11 +3,10 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using SkiaSharp; using SkiaSharp;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
public abstract class ProfileElement : PropertyChangedBase, IDisposable public abstract class ProfileElement : CorePropertyChanged, IDisposable
{ {
private bool _enabled; private bool _enabled;
private Guid _entityId; private Guid _entityId;

View File

@ -44,7 +44,7 @@ namespace Artemis.Core
LayerEffectEntity layerEffectEntity = new LayerEffectEntity LayerEffectEntity layerEffectEntity = new LayerEffectEntity
{ {
Id = layerEffect.EntityId, Id = layerEffect.EntityId,
PluginGuid = layerEffect.Descriptor.PlaceholderFor ?? layerEffect.PluginInfo.Guid, ProviderId = layerEffect.Descriptor.PlaceholderFor ?? layerEffect.ProviderId,
EffectType = layerEffect.GetEffectTypeName(), EffectType = layerEffect.GetEffectTypeName(),
Name = layerEffect.Name, Name = layerEffect.Name,
Enabled = layerEffect.Enabled, Enabled = layerEffect.Enabled,
@ -215,11 +215,11 @@ namespace Artemis.Core
foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects) foreach (LayerEffectEntity layerEffectEntity in RenderElementEntity.LayerEffects)
{ {
// If there is a non-placeholder existing effect, skip this entity // If there is a non-placeholder existing effect, skip this entity
BaseLayerEffect existing = _layerEffects.FirstOrDefault(e => e.EntityId == layerEffectEntity.Id); BaseLayerEffect? existing = _layerEffects.FirstOrDefault(e => e.EntityId == layerEffectEntity.Id);
if (existing != null && existing.Descriptor.PlaceholderFor == null) if (existing != null && existing.Descriptor.PlaceholderFor == null)
continue; continue;
LayerEffectDescriptor descriptor = LayerEffectStore.Get(layerEffectEntity.PluginGuid, layerEffectEntity.EffectType)?.LayerEffectDescriptor; LayerEffectDescriptor? descriptor = LayerEffectStore.Get(layerEffectEntity.ProviderId, layerEffectEntity.EffectType)?.LayerEffectDescriptor;
if (descriptor != null) if (descriptor != null)
{ {
// If a descriptor is found but there is an existing placeholder, remove the placeholder // If a descriptor is found but there is an existing placeholder, remove the placeholder
@ -235,7 +235,7 @@ namespace Artemis.Core
else if (existing == null) else if (existing == null)
{ {
// If no descriptor was found and there was no existing placeholder, create a placeholder // If no descriptor was found and there was no existing placeholder, create a placeholder
descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.PluginGuid); descriptor = PlaceholderLayerEffectDescriptor.Create(layerEffectEntity.ProviderId);
descriptor.CreateInstance(this, layerEffectEntity); descriptor.CreateInstance(this, layerEffectEntity);
} }
} }
@ -253,22 +253,22 @@ namespace Artemis.Core
private void LayerEffectStoreOnLayerEffectRemoved(object sender, LayerEffectStoreEvent e) private void LayerEffectStoreOnLayerEffectRemoved(object sender, LayerEffectStoreEvent e)
{ {
// If effects provided by the plugin are on the element, replace them with placeholders // If effects provided by the plugin are on the element, replace them with placeholders
List<BaseLayerEffect> pluginEffects = _layerEffects.Where(ef => ef.Descriptor.LayerEffectProvider != null && List<BaseLayerEffect> pluginEffects = _layerEffects.Where(ef => ef.Descriptor.Provider != null &&
ef.PluginInfo.Guid == e.Registration.PluginImplementation.PluginInfo.Guid).ToList(); ef.ProviderId == e.Registration.PluginFeature.Id).ToList();
foreach (BaseLayerEffect pluginEffect in pluginEffects) foreach (BaseLayerEffect pluginEffect in pluginEffects)
{ {
LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId); LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId);
_layerEffects.Remove(pluginEffect); _layerEffects.Remove(pluginEffect);
pluginEffect.Dispose(); pluginEffect.Dispose();
LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(pluginEffect.PluginInfo.Guid); LayerEffectDescriptor descriptor = PlaceholderLayerEffectDescriptor.Create(pluginEffect.ProviderId);
descriptor.CreateInstance(this, entity); descriptor.CreateInstance(this, entity);
} }
} }
private void LayerEffectStoreOnLayerEffectAdded(object sender, LayerEffectStoreEvent e) private void LayerEffectStoreOnLayerEffectAdded(object sender, LayerEffectStoreEvent e)
{ {
if (RenderElementEntity.LayerEffects.Any(ef => ef.PluginGuid == e.Registration.PluginImplementation.PluginInfo.Guid)) if (RenderElementEntity.LayerEffects.Any(ef => ef.ProviderId == e.Registration.PluginFeature.Id))
ActivateEffects(); ActivateEffects();
} }

View File

@ -3,14 +3,13 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents a timeline used by profile elements /// Represents a timeline used by profile elements
/// </summary> /// </summary>
public class Timeline : PropertyChangedBase, IStorageModel public class Timeline : CorePropertyChanged, IStorageModel
{ {
private const int MaxExtraTimelines = 15; private const int MaxExtraTimelines = 15;
@ -275,20 +274,20 @@ namespace Artemis.Core
if (segment <= TimelineSegment.End) if (segment <= TimelineSegment.End)
{ {
if (startUpdated || segment < TimelineSegment.End) if (startUpdated || segment < TimelineSegment.End)
NotifyOfPropertyChange(nameof(EndSegmentStartPosition)); OnPropertyChanged(nameof(EndSegmentStartPosition));
NotifyOfPropertyChange(nameof(EndSegmentEndPosition)); OnPropertyChanged(nameof(EndSegmentEndPosition));
} }
if (segment <= TimelineSegment.Main) if (segment <= TimelineSegment.Main)
{ {
if (startUpdated || segment < TimelineSegment.Main) if (startUpdated || segment < TimelineSegment.Main)
NotifyOfPropertyChange(nameof(MainSegmentStartPosition)); OnPropertyChanged(nameof(MainSegmentStartPosition));
NotifyOfPropertyChange(nameof(MainSegmentEndPosition)); OnPropertyChanged(nameof(MainSegmentEndPosition));
} }
if (segment <= TimelineSegment.Start) NotifyOfPropertyChange(nameof(StartSegmentEndPosition)); if (segment <= TimelineSegment.Start) OnPropertyChanged(nameof(StartSegmentEndPosition));
NotifyOfPropertyChange(nameof(Length)); OnPropertyChanged(nameof(Length));
} }
#endregion #endregion

View File

@ -4,20 +4,19 @@ using System.Linq;
using Artemis.Storage.Entities.Surface; using Artemis.Storage.Entities.Surface;
using RGB.NET.Core; using RGB.NET.Core;
using SkiaSharp; using SkiaSharp;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
public class ArtemisDevice : PropertyChangedBase public class ArtemisDevice : CorePropertyChanged
{ {
private ReadOnlyCollection<ArtemisLed> _leds; private ReadOnlyCollection<ArtemisLed> _leds;
private SKPath _renderPath; private SKPath _renderPath;
private SKRect _renderRectangle; private SKRect _renderRectangle;
internal ArtemisDevice(IRGBDevice rgbDevice, PluginImplementation pluginImplementation, ArtemisSurface surface) internal ArtemisDevice(IRGBDevice rgbDevice, PluginFeature pluginFeature, ArtemisSurface surface)
{ {
RgbDevice = rgbDevice; RgbDevice = rgbDevice;
PluginImplementation = pluginImplementation; PluginFeature = pluginFeature;
Surface = surface; Surface = surface;
DeviceEntity = new DeviceEntity(); DeviceEntity = new DeviceEntity();
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
@ -30,10 +29,10 @@ namespace Artemis.Core
CalculateRenderProperties(); CalculateRenderProperties();
} }
internal ArtemisDevice(IRGBDevice rgbDevice, PluginImplementation pluginImplementation, ArtemisSurface surface, DeviceEntity deviceEntity) internal ArtemisDevice(IRGBDevice rgbDevice, PluginFeature pluginFeature, ArtemisSurface surface, DeviceEntity deviceEntity)
{ {
RgbDevice = rgbDevice; RgbDevice = rgbDevice;
PluginImplementation = pluginImplementation; PluginFeature = pluginFeature;
Surface = surface; Surface = surface;
DeviceEntity = deviceEntity; DeviceEntity = deviceEntity;
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
@ -52,7 +51,7 @@ namespace Artemis.Core
} }
public IRGBDevice RgbDevice { get; } public IRGBDevice RgbDevice { get; }
public PluginImplementation PluginImplementation { get; } public PluginFeature PluginFeature { get; }
public ArtemisSurface Surface { get; } public ArtemisSurface Surface { get; }
public DeviceEntity DeviceEntity { get; } public DeviceEntity DeviceEntity { get; }
@ -68,7 +67,7 @@ namespace Artemis.Core
set set
{ {
DeviceEntity.X = value; DeviceEntity.X = value;
NotifyOfPropertyChange(nameof(X)); OnPropertyChanged(nameof(X));
} }
} }
@ -78,7 +77,7 @@ namespace Artemis.Core
set set
{ {
DeviceEntity.Y = value; DeviceEntity.Y = value;
NotifyOfPropertyChange(nameof(Y)); OnPropertyChanged(nameof(Y));
} }
} }
@ -88,7 +87,7 @@ namespace Artemis.Core
set set
{ {
DeviceEntity.Rotation = value; DeviceEntity.Rotation = value;
NotifyOfPropertyChange(nameof(Rotation)); OnPropertyChanged(nameof(Rotation));
} }
} }
@ -98,7 +97,7 @@ namespace Artemis.Core
set set
{ {
DeviceEntity.Scale = value; DeviceEntity.Scale = value;
NotifyOfPropertyChange(nameof(Scale)); OnPropertyChanged(nameof(Scale));
} }
} }
@ -108,7 +107,7 @@ namespace Artemis.Core
set set
{ {
DeviceEntity.ZIndex = value; DeviceEntity.ZIndex = value;
NotifyOfPropertyChange(nameof(ZIndex)); OnPropertyChanged(nameof(ZIndex));
} }
} }

View File

@ -1,10 +1,9 @@
using RGB.NET.Core; using RGB.NET.Core;
using SkiaSharp; using SkiaSharp;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
public class ArtemisLed : PropertyChangedBase public class ArtemisLed : CorePropertyChanged
{ {
private SKRect _absoluteRenderRectangle; private SKRect _absoluteRenderRectangle;
private SKRect _renderRectangle; private SKRect _renderRectangle;

View File

@ -3,11 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Artemis.Storage.Entities.Surface; using Artemis.Storage.Entities.Surface;
using RGB.NET.Core; using RGB.NET.Core;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
public class ArtemisSurface : PropertyChangedBase public class ArtemisSurface : CorePropertyChanged
{ {
private List<ArtemisDevice> _devices; private List<ArtemisDevice> _devices;
private bool _isActive; private bool _isActive;

View File

@ -6,6 +6,7 @@ using Ninject.Activation;
namespace Artemis.Core.Ninject namespace Artemis.Core.Ninject
{ {
// TODO: Investigate if this can't just be set as a constant on the plugin child kernel
internal class PluginSettingsProvider : Provider<PluginSettings> internal class PluginSettingsProvider : Provider<PluginSettings>
{ {
private static readonly List<PluginSettings> PluginSettings = new List<PluginSettings>(); private static readonly List<PluginSettings> PluginSettings = new List<PluginSettings>();
@ -25,21 +26,22 @@ namespace Artemis.Core.Ninject
throw new ArtemisCoreException("PluginSettings couldn't be injected, failed to get the injection parent request"); throw new ArtemisCoreException("PluginSettings couldn't be injected, failed to get the injection parent request");
// First try by PluginInfo parameter // First try by PluginInfo parameter
PluginInfo pluginInfo = parentRequest.Parameters.FirstOrDefault(p => p.Name == "PluginInfo")?.GetValue(context, null) as PluginInfo; Plugin? plugin = parentRequest.Parameters.FirstOrDefault(p => p.Name == "Plugin")?.GetValue(context, null) as Plugin;
if (pluginInfo == null)
pluginInfo = _pluginManagementService.GetPluginByAssembly(parentRequest.Service.Assembly)?.PluginInfo;
// Fall back to assembly based detection // Fall back to assembly based detection
if (pluginInfo == null) if (plugin == null)
plugin = _pluginManagementService.GetPluginByAssembly(parentRequest.Service.Assembly);
if (plugin == null)
throw new ArtemisCoreException("PluginSettings can only be injected with the PluginInfo parameter provided " + throw new ArtemisCoreException("PluginSettings can only be injected with the PluginInfo parameter provided " +
"or into a class defined in a plugin assembly"); "or into a class defined in a plugin assembly");
lock (PluginSettings) lock (PluginSettings)
{ {
PluginSettings? existingSettings = PluginSettings.FirstOrDefault(p => p.PluginInfo == pluginInfo); PluginSettings? existingSettings = PluginSettings.FirstOrDefault(p => p.Plugin == plugin);
if (existingSettings != null) if (existingSettings != null)
return existingSettings; return existingSettings;
PluginSettings? settings = new PluginSettings(pluginInfo, _pluginRepository); PluginSettings? settings = new PluginSettings(plugin, _pluginRepository);
PluginSettings.Add(settings); PluginSettings.Add(settings);
return settings; return settings;
} }

View File

@ -17,7 +17,7 @@ namespace Artemis.Core.Ninject
protected override ISettingsService CreateInstance(IContext context) protected override ISettingsService CreateInstance(IContext context)
{ {
IRequest parentRequest = context.Request.ParentRequest; IRequest parentRequest = context.Request.ParentRequest;
if (parentRequest == null || typeof(PluginImplementation).IsAssignableFrom(parentRequest.Service)) if (parentRequest == null || typeof(PluginFeature).IsAssignableFrom(parentRequest.Service))
throw new ArtemisPluginException($"SettingsService can not be injected into a plugin. Inject {nameof(PluginSettings)} instead."); throw new ArtemisPluginException($"SettingsService can not be injected into a plugin. Inject {nameof(PluginSettings)} instead.");
return _instance; return _instance;

View File

@ -14,10 +14,10 @@ namespace Artemis.Core.DataModelExpansions
private readonly Dictionary<string, DataModel> _dynamicDataModels = new Dictionary<string, DataModel>(); private readonly Dictionary<string, DataModel> _dynamicDataModels = new Dictionary<string, DataModel>();
/// <summary> /// <summary>
/// Gets the plugin implementation this data model belongs to /// Gets the plugin feature this data model belongs to
/// </summary> /// </summary>
[DataModelIgnore] [DataModelIgnore]
public DataModelPluginImplementation Implementation { get; internal set; } public DataModelPluginFeature Feature { get; internal set; }
/// <summary> /// <summary>
/// Gets the <see cref="DataModelPropertyAttribute" /> describing this data model /// Gets the <see cref="DataModelPropertyAttribute" /> describing this data model
@ -43,9 +43,9 @@ namespace Artemis.Core.DataModelExpansions
/// <returns></returns> /// <returns></returns>
public ReadOnlyCollection<PropertyInfo> GetHiddenProperties() public ReadOnlyCollection<PropertyInfo> GetHiddenProperties()
{ {
if (Implementation is ProfileModule profileModule) if (Feature is ProfileModule profileModule)
return profileModule.HiddenProperties; return profileModule.HiddenProperties;
if (Implementation is BaseDataModelExpansion dataModelExpansion) if (Feature is BaseDataModelExpansion dataModelExpansion)
return dataModelExpansion.HiddenProperties; return dataModelExpansion.HiddenProperties;
return new List<PropertyInfo>().AsReadOnly(); return new List<PropertyInfo>().AsReadOnly();
@ -81,7 +81,7 @@ namespace Artemis.Core.DataModelExpansions
throw new ArtemisCoreException($"Cannot add a dynamic data model with key '{key}' " + throw new ArtemisCoreException($"Cannot add a dynamic data model with key '{key}' " +
"because the key is already in use by a static property on this data model."); "because the key is already in use by a static property on this data model.");
dynamicDataModel.Implementation = Implementation; dynamicDataModel.Feature = Feature;
dynamicDataModel.DataModelDescription = new DataModelPropertyAttribute dynamicDataModel.DataModelDescription = new DataModelPropertyAttribute
{ {
Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name, Name = string.IsNullOrWhiteSpace(name) ? key.Humanize() : name,

View File

@ -45,7 +45,7 @@ namespace Artemis.Core.DataModelExpansions
internal override void InternalEnable() internal override void InternalEnable()
{ {
DataModel = Activator.CreateInstance<T>(); DataModel = Activator.CreateInstance<T>();
DataModel.Implementation = PluginInfo; DataModel.Feature = this;
DataModel.DataModelDescription = GetDataModelDescription(); DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable(); base.InternalEnable();
} }

View File

@ -8,7 +8,7 @@ namespace Artemis.Core.DataModelExpansions
/// For internal use only, to implement your own layer property type, extend <see cref="DataModelExpansion{T}" /> /// For internal use only, to implement your own layer property type, extend <see cref="DataModelExpansion{T}" />
/// instead. /// instead.
/// </summary> /// </summary>
public abstract class BaseDataModelExpansion : DataModelPluginImplementation public abstract class BaseDataModelExpansion : DataModelPluginFeature
{ {
/// <summary> /// <summary>
/// Gets a list of all properties ignored at runtime using <c>IgnoreProperty(x => x.y)</c> /// Gets a list of all properties ignored at runtime using <c>IgnoreProperty(x => x.y)</c>
@ -35,7 +35,7 @@ namespace Artemis.Core.DataModelExpansions
/// <returns></returns> /// <returns></returns>
public virtual DataModelPropertyAttribute GetDataModelDescription() public virtual DataModelPropertyAttribute GetDataModelDescription()
{ {
return new DataModelPropertyAttribute {Name = PluginInfo.Name, Description = PluginInfo.Description}; return new DataModelPropertyAttribute {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
} }
} }
} }

View File

@ -4,9 +4,9 @@ using System.Threading.Tasks;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents an implementation of a certain type provided by a plugin with support for data models /// Represents an feature of a certain type provided by a plugin with support for data models
/// </summary> /// </summary>
public abstract class DataModelPluginImplementation : PluginImplementation public abstract class DataModelPluginFeature : PluginFeature
{ {
/// <summary> /// <summary>
/// Registers a timed update that whenever the plugin is enabled calls the provided <paramref name="action" /> at the /// Registers a timed update that whenever the plugin is enabled calls the provided <paramref name="action" /> at the
@ -23,11 +23,11 @@ namespace Artemis.Core
{ {
if (action == null) if (action == null)
throw new ArgumentNullException(nameof(action)); throw new ArgumentNullException(nameof(action));
return new TimedUpdateRegistration(PluginInfo, interval, action); return new TimedUpdateRegistration(this, interval, action);
} }
/// <summary> /// <summary>
/// Registers a timed update that whenever the plugin is enabled calls the provided <paramref name="action" /> at the /// Registers a timed update that whenever the plugin is enabled calls the provided <paramref name="asyncAction" /> at the
/// provided /// provided
/// <paramref name="interval" /> /// <paramref name="interval" />
/// </summary> /// </summary>
@ -41,7 +41,7 @@ namespace Artemis.Core
{ {
if (asyncAction == null) if (asyncAction == null)
throw new ArgumentNullException(nameof(asyncAction)); throw new ArgumentNullException(nameof(asyncAction));
return new TimedUpdateRegistration(PluginInfo, interval, asyncAction); return new TimedUpdateRegistration(this, interval, asyncAction);
} }
} }
} }

View File

@ -10,7 +10,7 @@ namespace Artemis.Core.DeviceProviders
/// <summary> /// <summary>
/// Allows you to implement and register your own device provider /// Allows you to implement and register your own device provider
/// </summary> /// </summary>
public abstract class DeviceProvider : PluginImplementation public abstract class DeviceProvider : PluginFeature
{ {
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="DeviceProvider" /> class /// Creates a new instance of the <see cref="DeviceProvider" /> class
@ -50,9 +50,9 @@ namespace Artemis.Core.DeviceProviders
{ {
// Start from the plugin directory // Start from the plugin directory
if (e.RelativePart != null && e.FileName != null) if (e.RelativePart != null && e.FileName != null)
e.FinalPath = Path.Combine(PluginInfo.Directory.FullName, e.RelativePart, e.FileName); e.FinalPath = Path.Combine(Plugin.Directory.FullName, e.RelativePart, e.FileName);
else if (e.RelativePath != null) else if (e.RelativePath != null)
e.FinalPath = Path.Combine(PluginInfo.Directory.FullName, e.RelativePath); e.FinalPath = Path.Combine(Plugin.Directory.FullName, e.RelativePath);
IRGBDeviceInfo deviceInfo = ((IRGBDevice) sender).DeviceInfo; IRGBDeviceInfo deviceInfo = ((IRGBDevice) sender).DeviceInfo;
if (e.FileName != null && !File.Exists(e.FinalPath)) if (e.FileName != null && !File.Exists(e.FinalPath))

View File

@ -0,0 +1,20 @@
namespace Artemis.Core
{
/// <summary>
/// An optional entry point for your plugin
/// </summary>
public interface IPluginBootstrapper
{
/// <summary>
/// Called when the plugin is activated
/// </summary>
/// <param name="plugin">The plugin instance of your plugin</param>
void Enable(Plugin plugin);
/// <summary>
/// Called when the plugin is deactivated or when Artemis shuts down
/// </summary>
/// <param name="plugin">The plugin instance of your plugin</param>
void Disable(Plugin plugin);
}
}

View File

@ -0,0 +1,7 @@
namespace Artemis.Core
{
public interface IPluginConfigurationDialog
{
}
}

View File

@ -0,0 +1,10 @@
namespace Artemis.Core
{
/// <summary>
/// Represents a view model for a plugin configuration window
/// </summary>
public interface IPluginConfigurationViewModel
{
}
}

View File

@ -0,0 +1,9 @@
namespace Artemis.Core.LayerBrushes
{
/// <summary>
/// Represents the configuration dialog of a layer brush
/// </summary>
public interface ILayerBrushConfigurationDialog
{
}
}

View File

@ -1,17 +1,15 @@
using System; using System;
using Artemis.Core.Services;
using SkiaSharp; using SkiaSharp;
using Stylet;
namespace Artemis.Core.LayerBrushes namespace Artemis.Core.LayerBrushes
{ {
/// <summary> /// <summary>
/// For internal use only, please use <see cref="LayerBrush{T}" /> or <see cref="RgbNetLayerBrush{T}" /> or instead /// For internal use only, please use <see cref="LayerBrush{T}" /> or <see cref="RgbNetLayerBrush{T}" /> or instead
/// </summary> /// </summary>
public abstract class BaseLayerBrush : PropertyChangedBase, IDisposable public abstract class BaseLayerBrush : CorePropertyChanged, IDisposable
{ {
private LayerBrushType _brushType; private LayerBrushType _brushType;
private LayerBrushConfigurationDialog _configurationDialog; private ILayerBrushConfigurationDialog _configurationDialog;
private LayerBrushDescriptor _descriptor; private LayerBrushDescriptor _descriptor;
private Layer _layer; private Layer _layer;
private bool _supportsTransformation = true; private bool _supportsTransformation = true;
@ -37,7 +35,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary> /// <summary>
/// Gets or sets a configuration dialog complementing the regular properties /// Gets or sets a configuration dialog complementing the regular properties
/// </summary> /// </summary>
public LayerBrushConfigurationDialog ConfigurationDialog public ILayerBrushConfigurationDialog ConfigurationDialog
{ {
get => _configurationDialog; get => _configurationDialog;
protected set => SetAndNotify(ref _configurationDialog, value); protected set => SetAndNotify(ref _configurationDialog, value);
@ -53,9 +51,9 @@ namespace Artemis.Core.LayerBrushes
} }
/// <summary> /// <summary>
/// Gets the plugin info that defined this brush /// Gets the ID of the <see cref="LayerBrushProvider" /> that provided this effect
/// </summary> /// </summary>
public PluginInfo PluginInfo => Descriptor.LayerBrushProvider.PluginInfo; public string ProviderId => Descriptor?.Provider?.Id;
/// <summary> /// <summary>
/// Gets a reference to the layer property group without knowing it's type /// Gets a reference to the layer property group without knowing it's type
@ -72,21 +70,11 @@ namespace Artemis.Core.LayerBrushes
protected set protected set
{ {
if (value && BrushType == LayerBrushType.RgbNet) if (value && BrushType == LayerBrushType.RgbNet)
throw new ArtemisPluginException(PluginInfo, "An RGB.NET brush cannot support transformation"); throw new ArtemisPluginFeatureException(Descriptor.Provider, "An RGB.NET brush cannot support transformation");
_supportsTransformation = value; _supportsTransformation = value;
} }
} }
/// <inheritdoc />
public void Dispose()
{
DisableLayerBrush();
BaseProperties.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary> /// <summary>
/// Called when the layer brush is activated /// Called when the layer brush is activated
/// </summary> /// </summary>
@ -112,6 +100,16 @@ namespace Artemis.Core.LayerBrushes
internal virtual void Dispose(bool disposing) internal virtual void Dispose(bool disposing)
{ {
} }
/// <inheritdoc />
public void Dispose()
{
DisableLayerBrush();
BaseProperties.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
} }
/// <summary> /// <summary>

View File

@ -37,7 +37,7 @@ namespace Artemis.Core.LayerBrushes
Properties = Activator.CreateInstance<T>(); Properties = Activator.CreateInstance<T>();
Properties.GroupDescription ??= new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description}; Properties.GroupDescription ??= new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description};
Properties.LayerBrush = this; Properties.LayerBrush = this;
Properties.Initialize(Layer, "LayerBrush.", PluginInfo); Properties.Initialize(Layer, "LayerBrush.", Descriptor.Provider);
PropertiesInitialized = true; PropertiesInitialized = true;
EnableLayerBrush(); EnableLayerBrush();

View File

@ -1,5 +1,4 @@
using System; using System;
using Artemis.Core.Services;
using Ninject; using Ninject;
namespace Artemis.Core.LayerBrushes namespace Artemis.Core.LayerBrushes
@ -9,13 +8,13 @@ namespace Artemis.Core.LayerBrushes
/// </summary> /// </summary>
public class LayerBrushDescriptor public class LayerBrushDescriptor
{ {
internal LayerBrushDescriptor(string displayName, string description, string icon, Type layerBrushType, LayerBrushProvider layerBrushProvider) internal LayerBrushDescriptor(string displayName, string description, string icon, Type layerBrushType, LayerBrushProvider provider)
{ {
DisplayName = displayName; DisplayName = displayName;
Description = description; Description = description;
Icon = icon; Icon = icon;
LayerBrushType = layerBrushType; LayerBrushType = layerBrushType;
LayerBrushProvider = layerBrushProvider; Provider = provider;
} }
/// <summary> /// <summary>
@ -42,7 +41,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary> /// <summary>
/// The plugin that provided this <see cref="LayerBrushDescriptor" /> /// The plugin that provided this <see cref="LayerBrushDescriptor" />
/// </summary> /// </summary>
public LayerBrushProvider LayerBrushProvider { get; } public LayerBrushProvider Provider { get; }
/// <summary> /// <summary>
/// Determines whether the provided <paramref name="reference" /> references to a brush provided by this descriptor /// Determines whether the provided <paramref name="reference" /> references to a brush provided by this descriptor
@ -51,7 +50,8 @@ namespace Artemis.Core.LayerBrushes
{ {
if (reference == null) if (reference == null)
return false; return false;
return LayerBrushProvider.PluginInfo.Guid == reference.BrushPluginGuid && LayerBrushType.Name == reference.BrushType;
return Provider.Id == reference.LayerBrushProviderId && LayerBrushType.Name == reference.BrushType;
} }
/// <summary> /// <summary>
@ -62,7 +62,7 @@ namespace Artemis.Core.LayerBrushes
if (layer.LayerBrush != null) if (layer.LayerBrush != null)
throw new ArtemisCoreException("Layer already has an instantiated layer brush"); throw new ArtemisCoreException("Layer already has an instantiated layer brush");
BaseLayerBrush brush = (BaseLayerBrush) LayerBrushProvider.PluginInfo.Kernel.Get(LayerBrushType); BaseLayerBrush brush = (BaseLayerBrush) Provider.Plugin.Kernel!.Get(LayerBrushType);
brush.Layer = layer; brush.Layer = layer;
brush.Descriptor = this; brush.Descriptor = this;
brush.Initialize(); brush.Initialize();

View File

@ -7,7 +7,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary> /// <summary>
/// Allows you to create one or more <see cref="LayerBrush{T}" />s usable by profile layers. /// Allows you to create one or more <see cref="LayerBrush{T}" />s usable by profile layers.
/// </summary> /// </summary>
public abstract class LayerBrushProvider : PluginImplementation public abstract class LayerBrushProvider : PluginFeature
{ {
private readonly List<LayerBrushDescriptor> _layerBrushDescriptors; private readonly List<LayerBrushDescriptor> _layerBrushDescriptors;
@ -39,7 +39,7 @@ namespace Artemis.Core.LayerBrushes
protected void RegisterLayerBrushDescriptor<T>(string displayName, string description, string icon) where T : BaseLayerBrush protected void RegisterLayerBrushDescriptor<T>(string displayName, string description, string icon) where T : BaseLayerBrush
{ {
if (!IsEnabled) if (!IsEnabled)
throw new ArtemisPluginException(PluginInfo, "Can only add a layer brush descriptor when the plugin is enabled"); throw new ArtemisPluginException(Plugin, "Can only add a layer brush descriptor when the plugin is enabled");
LayerBrushDescriptor descriptor = new LayerBrushDescriptor(displayName, description, icon, typeof(T), this); LayerBrushDescriptor descriptor = new LayerBrushDescriptor(displayName, description, icon, typeof(T), this);
_layerBrushDescriptors.Add(descriptor); _layerBrushDescriptors.Add(descriptor);

View File

@ -0,0 +1,6 @@
namespace Artemis.Core.LayerEffects
{
public interface IEffectConfigurationViewModel
{
}
}

View File

@ -0,0 +1,6 @@
namespace Artemis.Core.LayerEffects
{
public interface ILayerEffectConfigurationDialog
{
}
}

View File

@ -1,16 +1,15 @@
using System; using System;
using Artemis.Core.Services; using Artemis.Core.Services;
using SkiaSharp; using SkiaSharp;
using Stylet;
namespace Artemis.Core.LayerEffects namespace Artemis.Core.LayerEffects
{ {
/// <summary> /// <summary>
/// For internal use only, please use <see cref="LayerEffect{T}" /> instead /// For internal use only, please use <see cref="LayerEffect{T}" /> instead
/// </summary> /// </summary>
public abstract class BaseLayerEffect : PropertyChangedBase, IDisposable public abstract class BaseLayerEffect : CorePropertyChanged, IDisposable
{ {
private LayerEffectConfigurationDialog _configurationDialog; private ILayerEffectConfigurationDialog _configurationDialog;
private LayerEffectDescriptor _descriptor; private LayerEffectDescriptor _descriptor;
private bool _enabled; private bool _enabled;
private Guid _entityId; private Guid _entityId;
@ -77,7 +76,7 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// Gets the <see cref="LayerEffectDescriptor" /> that registered this effect /// Gets the <see cref="LayerEffectDescriptor" /> that registered this effect
/// </summary> /// </summary>
public LayerEffectDescriptor Descriptor public LayerEffectDescriptor? Descriptor
{ {
get => _descriptor; get => _descriptor;
internal set => SetAndNotify(ref _descriptor, value); internal set => SetAndNotify(ref _descriptor, value);
@ -86,16 +85,16 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// Gets or sets a configuration dialog complementing the regular properties /// Gets or sets a configuration dialog complementing the regular properties
/// </summary> /// </summary>
public LayerEffectConfigurationDialog ConfigurationDialog public ILayerEffectConfigurationDialog ConfigurationDialog
{ {
get => _configurationDialog; get => _configurationDialog;
protected set => SetAndNotify(ref _configurationDialog, value); protected set => SetAndNotify(ref _configurationDialog, value);
} }
/// <summary> /// <summary>
/// Gets the plugin info that defined this effect /// Gets the ID of the <see cref="LayerEffectProvider"/> that provided this effect
/// </summary> /// </summary>
public PluginInfo PluginInfo => Descriptor.LayerEffectProvider?.PluginInfo; public string ProviderId => Descriptor?.Provider?.Id;
/// <summary> /// <summary>
/// Gets a reference to the layer property group without knowing it's type /// Gets a reference to the layer property group without knowing it's type

View File

@ -37,7 +37,7 @@ namespace Artemis.Core.LayerEffects
{ {
Properties = Activator.CreateInstance<T>(); Properties = Activator.CreateInstance<T>();
Properties.LayerEffect = this; Properties.LayerEffect = this;
Properties.Initialize(ProfileElement, PropertyRootPath, PluginInfo); Properties.Initialize(ProfileElement, PropertyRootPath, Descriptor.Provider);
PropertiesInitialized = true; PropertiesInitialized = true;
EnableLayerEffect(); EnableLayerEffect();

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using Artemis.Core.LayerEffects.Placeholder; using Artemis.Core.LayerEffects.Placeholder;
using Artemis.Core.Services;
using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile;
using Ninject; using Ninject;
@ -12,13 +11,13 @@ namespace Artemis.Core.LayerEffects
/// </summary> /// </summary>
public class LayerEffectDescriptor public class LayerEffectDescriptor
{ {
internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider layerEffectProvider) internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider provider)
{ {
DisplayName = displayName; DisplayName = displayName;
Description = description; Description = description;
Icon = icon; Icon = icon;
LayerEffectType = layerEffectType; LayerEffectType = layerEffectType;
LayerEffectProvider = layerEffectProvider; Provider = provider;
} }
/// <summary> /// <summary>
@ -45,12 +44,12 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// The plugin that provided this <see cref="LayerEffectDescriptor" /> /// The plugin that provided this <see cref="LayerEffectDescriptor" />
/// </summary> /// </summary>
public LayerEffectProvider LayerEffectProvider { get; } public LayerEffectProvider? Provider { get; }
/// <summary> /// <summary>
/// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder /// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder
/// </summary> /// </summary>
public Guid? PlaceholderFor { get; internal set; } public string? PlaceholderFor { get; internal set; }
/// <summary> /// <summary>
/// Creates an instance of the described effect and applies it to the render element /// Creates an instance of the described effect and applies it to the render element
@ -67,7 +66,10 @@ namespace Artemis.Core.LayerEffects
return; return;
} }
BaseLayerEffect effect = (BaseLayerEffect) LayerEffectProvider.PluginInfo.Kernel.Get(LayerEffectType); if (Provider == null)
throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its provider");
BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType);
effect.ProfileElement = renderElement; effect.ProfileElement = renderElement;
effect.EntityId = entity.Id; effect.EntityId = entity.Id;
effect.Order = entity.Order; effect.Order = entity.Order;
@ -83,7 +85,7 @@ namespace Artemis.Core.LayerEffects
private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity) private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity)
{ {
PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor.Value) {ProfileElement = renderElement, Descriptor = this}; PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor) {ProfileElement = renderElement, Descriptor = this};
effect.Initialize(); effect.Initialize();
renderElement.ActivateLayerEffect(effect); renderElement.ActivateLayerEffect(effect);
} }

View File

@ -7,7 +7,7 @@ namespace Artemis.Core.LayerEffects
/// <summary> /// <summary>
/// Allows you to register one or more <see cref="LayerEffect{T}" />s usable by profile layers. /// Allows you to register one or more <see cref="LayerEffect{T}" />s usable by profile layers.
/// </summary> /// </summary>
public abstract class LayerEffectProvider : PluginImplementation public abstract class LayerEffectProvider : PluginFeature
{ {
private readonly List<LayerEffectDescriptor> _layerEffectDescriptors; private readonly List<LayerEffectDescriptor> _layerEffectDescriptors;
@ -39,7 +39,7 @@ namespace Artemis.Core.LayerEffects
protected void RegisterLayerEffectDescriptor<T>(string displayName, string description, string icon) where T : BaseLayerEffect protected void RegisterLayerEffectDescriptor<T>(string displayName, string description, string icon) where T : BaseLayerEffect
{ {
if (!IsEnabled) if (!IsEnabled)
throw new ArtemisPluginException(PluginInfo, "Can only add a layer effect descriptor when the plugin is enabled"); throw new ArtemisPluginFeatureException(this, "Can only add a layer effect descriptor when the plugin is enabled");
LayerEffectDescriptor descriptor = new LayerEffectDescriptor(displayName, description, icon, typeof(T), this); LayerEffectDescriptor descriptor = new LayerEffectDescriptor(displayName, description, icon, typeof(T), this);
_layerEffectDescriptors.Add(descriptor); _layerEffectDescriptors.Add(descriptor);

View File

@ -1,5 +1,4 @@
using System; using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile;
using SkiaSharp; using SkiaSharp;
namespace Artemis.Core.LayerEffects.Placeholder namespace Artemis.Core.LayerEffects.Placeholder
@ -9,7 +8,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
/// </summary> /// </summary>
internal class PlaceholderLayerEffect : LayerEffect<PlaceholderProperties> internal class PlaceholderLayerEffect : LayerEffect<PlaceholderProperties>
{ {
internal PlaceholderLayerEffect(LayerEffectEntity originalEntity, Guid placeholderFor) internal PlaceholderLayerEffect(LayerEffectEntity originalEntity, string placeholderFor)
{ {
OriginalEntity = originalEntity; OriginalEntity = originalEntity;
PlaceholderFor = placeholderFor; PlaceholderFor = placeholderFor;
@ -21,8 +20,9 @@ namespace Artemis.Core.LayerEffects.Placeholder
HasBeenRenamed = OriginalEntity.HasBeenRenamed; HasBeenRenamed = OriginalEntity.HasBeenRenamed;
} }
public string PlaceholderFor { get; }
internal LayerEffectEntity OriginalEntity { get; } internal LayerEffectEntity OriginalEntity { get; }
public Guid PlaceholderFor { get; }
/// <inheritdoc /> /// <inheritdoc />
public override void EnableLayerEffect() public override void EnableLayerEffect()
@ -56,7 +56,7 @@ namespace Artemis.Core.LayerEffects.Placeholder
} }
/// <summary> /// <summary>
/// This is in place so that the UI has something to show /// This is in place so that the UI has something to show
/// </summary> /// </summary>
internal class PlaceholderProperties : LayerPropertyGroup internal class PlaceholderProperties : LayerPropertyGroup
{ {

View File

@ -1,14 +1,12 @@
using System; namespace Artemis.Core.LayerEffects.Placeholder
namespace Artemis.Core.LayerEffects.Placeholder
{ {
internal static class PlaceholderLayerEffectDescriptor internal static class PlaceholderLayerEffectDescriptor
{ {
public static LayerEffectDescriptor Create(Guid missingPluginGuid) public static LayerEffectDescriptor Create(string missingProviderId)
{ {
LayerEffectDescriptor descriptor = new LayerEffectDescriptor("Missing effect", "This effect could not be loaded", "FileQuestion", null, Constants.EffectPlaceholderPlugin) LayerEffectDescriptor descriptor = new LayerEffectDescriptor("Missing effect", "This effect could not be loaded", "FileQuestion", null, Constants.EffectPlaceholderPlugin)
{ {
PlaceholderFor = missingPluginGuid PlaceholderFor = missingProviderId
}; };
return descriptor; return descriptor;

View File

@ -0,0 +1,10 @@
namespace Artemis.Core.Modules
{
/// <summary>
/// The base class for any view model that belongs to a module
/// </summary>
public interface IModuleViewModel
{
}
}

View File

@ -42,13 +42,13 @@ namespace Artemis.Core.Modules
/// <returns></returns> /// <returns></returns>
public virtual DataModelPropertyAttribute GetDataModelDescription() public virtual DataModelPropertyAttribute GetDataModelDescription()
{ {
return new DataModelPropertyAttribute {Name = PluginInfo.Name, Description = PluginInfo.Description}; return new DataModelPropertyAttribute {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
} }
internal override void InternalEnable() internal override void InternalEnable()
{ {
DataModel = Activator.CreateInstance<T>(); DataModel = Activator.CreateInstance<T>();
DataModel.Implementation = PluginInfo; DataModel.Feature = this;
DataModel.DataModelDescription = GetDataModelDescription(); DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable(); base.InternalEnable();
} }
@ -64,7 +64,7 @@ namespace Artemis.Core.Modules
/// <summary> /// <summary>
/// Allows you to add support for new games/applications /// Allows you to add support for new games/applications
/// </summary> /// </summary>
public abstract class Module : DataModelPluginImplementation public abstract class Module : DataModelPluginFeature
{ {
/// <summary> /// <summary>
/// The modules display name that's shown in the menu /// The modules display name that's shown in the menu
@ -236,7 +236,7 @@ namespace Artemis.Core.Modules
if (Entity == null) if (Entity == null)
Entity = new ModuleSettingsEntity(); Entity = new ModuleSettingsEntity();
Entity.PluginGuid = PluginInfo.Guid; Entity.ModuleId = Id;
Entity.PriorityCategory = (int) PriorityCategory; Entity.PriorityCategory = (int) PriorityCategory;
Entity.Priority = Priority; Entity.Priority = Priority;
} }

View File

@ -3,7 +3,7 @@
namespace Artemis.Core.Modules namespace Artemis.Core.Modules
{ {
/// <inheritdoc /> /// <inheritdoc />
public class ModuleTab<T> : ModuleTab where T : ModuleViewModel public class ModuleTab<T> : ModuleTab where T : IModuleViewModel
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ModuleTab{T}" /> class /// Initializes a new instance of the <see cref="ModuleTab{T}" /> class

View File

@ -46,7 +46,7 @@ namespace Artemis.Core.Modules
/// <returns></returns> /// <returns></returns>
public virtual DataModelPropertyAttribute GetDataModelDescription() public virtual DataModelPropertyAttribute GetDataModelDescription()
{ {
return new DataModelPropertyAttribute {Name = PluginInfo.Name, Description = PluginInfo.Description}; return new DataModelPropertyAttribute {Name = Plugin.Info.Name, Description = Plugin.Info.Description};
} }
/// <summary> /// <summary>
@ -74,7 +74,7 @@ namespace Artemis.Core.Modules
internal override void InternalEnable() internal override void InternalEnable()
{ {
DataModel = Activator.CreateInstance<T>(); DataModel = Activator.CreateInstance<T>();
DataModel.Implementation = PluginInfo; DataModel.Feature = this;
DataModel.DataModelDescription = GetDataModelDescription(); DataModel.DataModelDescription = GetDataModelDescription();
base.InternalEnable(); base.InternalEnable();
} }
@ -184,7 +184,7 @@ namespace Artemis.Core.Modules
internal async Task ChangeActiveProfileAnimated(Profile profile, ArtemisSurface surface) internal async Task ChangeActiveProfileAnimated(Profile profile, ArtemisSurface surface)
{ {
if (profile != null && profile.Module != this) if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}."); throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
if (!IsActivated) if (!IsActivated)
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module"); throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");
@ -206,7 +206,7 @@ namespace Artemis.Core.Modules
internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface) internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface)
{ {
if (profile != null && profile.Module != this) if (profile != null && profile.Module != this)
throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}."); throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}.");
if (!IsActivated) if (!IsActivated)
throw new ArtemisCoreException("Cannot activate a profile on a deactivated module"); throw new ArtemisCoreException("Cannot activate a profile on a deactivated module");

View File

@ -7,16 +7,15 @@ using System.Reflection;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using McMaster.NETCore.Plugins; using McMaster.NETCore.Plugins;
using Ninject; using Ninject;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents a plugin /// Represents a plugin
/// </summary> /// </summary>
public class Plugin : PropertyChangedBase, IDisposable public class Plugin : CorePropertyChanged, IDisposable
{ {
private readonly List<PluginImplementation> _implementations; private readonly List<PluginFeature> _features;
private bool _isEnabled; private bool _isEnabled;
@ -25,7 +24,7 @@ namespace Artemis.Core
Info = info; Info = info;
Directory = directory; Directory = directory;
_implementations = new List<PluginImplementation>(); _features = new List<PluginFeature>();
} }
/// <summary> /// <summary>
@ -46,8 +45,8 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins /// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins
/// </summary> /// </summary>
public PluginConfigurationDialog? ConfigurationDialog { get; protected set; } public IPluginConfigurationDialog? ConfigurationDialog { get; set; }
/// <summary> /// <summary>
/// Indicates whether the user enabled the plugin or not /// Indicates whether the user enabled the plugin or not
/// </summary> /// </summary>
@ -58,19 +57,24 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Gets a read-only collection of all implementations this plugin provides /// Gets a read-only collection of all features this plugin provides
/// </summary> /// </summary>
public ReadOnlyCollection<PluginImplementation> Implementations => _implementations.AsReadOnly(); public ReadOnlyCollection<PluginFeature> Features => _features.AsReadOnly();
/// <summary> /// <summary>
/// The assembly the plugin code lives in /// The assembly the plugin code lives in
/// </summary> /// </summary>
internal Assembly? Assembly { get; set; } public Assembly? Assembly { get; internal set; }
/// <summary>
/// Gets the plugin bootstrapper
/// </summary>
public IPluginBootstrapper? Bootstrapper { get; internal set; }
/// <summary> /// <summary>
/// The Ninject kernel of the plugin /// The Ninject kernel of the plugin
/// </summary> /// </summary>
internal IKernel? Kernel { get; set; } public IKernel? Kernel { get; internal set; }
/// <summary> /// <summary>
/// The PluginLoader backing this plugin /// The PluginLoader backing this plugin
@ -80,7 +84,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// The entity representing the plugin /// The entity representing the plugin
/// </summary> /// </summary>
internal PluginEntity? Entity { get; set; } internal PluginEntity Entity { get; set; }
/// <summary> /// <summary>
/// Resolves the relative path provided in the <paramref name="path" /> parameter to an absolute path /// Resolves the relative path provided in the <paramref name="path" /> parameter to an absolute path
@ -92,25 +96,15 @@ namespace Artemis.Core
return path == null ? null : Path.Combine(Directory.FullName, path); return path == null ? null : Path.Combine(Directory.FullName, path);
} }
internal void ApplyToEntity() /// <summary>
/// Looks up the instance of the feature of type <typeparamref name="T" />
/// <para>Note: This method only returns instances of enabled features</para>
/// </summary>
/// <typeparam name="T">The type of feature to find</typeparam>
/// <returns>If found, the instance of the feature</returns>
public T? GetFeature<T>() where T : PluginFeature
{ {
Entity.Id = Guid; return _features.FirstOrDefault(i => i is T) as T;
Entity.IsEnabled = IsEnabled;
}
internal void AddImplementation(PluginImplementation implementation)
{
implementation.Plugin = this;
_implementations.Add(implementation);
}
public void SetEnabled(bool enable)
{
if (IsEnabled == enable)
return;
if (!enable && Implementations.Any(e => e.IsEnabled))
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled implementations");
} }
/// <inheritdoc /> /// <inheritdoc />
@ -119,13 +113,118 @@ namespace Artemis.Core
return Info.ToString(); return Info.ToString();
} }
internal void ApplyToEntity()
{
Entity.Id = Guid;
Entity.IsEnabled = IsEnabled;
}
internal void AddFeature(PluginFeature feature)
{
feature.Plugin = this;
_features.Add(feature);
OnFeatureAdded(new PluginFeatureEventArgs(feature));
}
internal void RemoveFeature(PluginFeature feature)
{
_features.Remove(feature);
feature.InternalDisable();
feature.Dispose();
OnFeatureRemoved(new PluginFeatureEventArgs(feature));
}
internal void SetEnabled(bool enable)
{
if (IsEnabled == enable)
return;
if (!enable && Features.Any(e => e.IsEnabled))
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled features");
IsEnabled = enable;
if (enable)
{
Bootstrapper?.Enable(this);
OnEnabled();
}
else
{
Bootstrapper?.Disable(this);
OnDisabled();
}
}
public void Dispose() public void Dispose()
{ {
foreach (PluginImplementation pluginImplementation in Implementations) foreach (PluginFeature feature in Features)
pluginImplementation.Dispose(); feature.Dispose();
Kernel?.Dispose(); Kernel?.Dispose();
PluginLoader?.Dispose(); PluginLoader?.Dispose();
_features.Clear();
SetEnabled(false);
} }
#region Events
/// <summary>
/// Occurs when the plugin is enabled
/// </summary>
public event EventHandler? Enabled;
/// <summary>
/// Occurs when the plugin is disabled
/// </summary>
public event EventHandler? Disabled;
/// <summary>
/// Occurs when an feature is loaded and added to the plugin
/// </summary>
public event EventHandler<PluginFeatureEventArgs>? FeatureAdded;
/// <summary>
/// Occurs when an feature is disabled and removed from the plugin
/// </summary>
public event EventHandler<PluginFeatureEventArgs>? FeatureRemoved;
/// <summary>
/// Invokes the Enabled event
/// </summary>
protected virtual void OnEnabled()
{
Enabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the Disabled event
/// </summary>
protected virtual void OnDisabled()
{
Disabled?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Invokes the FeatureAdded event
/// </summary>
protected virtual void OnFeatureAdded(PluginFeatureEventArgs e)
{
FeatureAdded?.Invoke(this, e);
}
/// <summary>
/// Invokes the FeatureRemoved event
/// </summary>
protected virtual void OnFeatureRemoved(PluginFeatureEventArgs e)
{
FeatureRemoved?.Invoke(this, e);
}
#endregion
} }
} }

View File

@ -1,24 +1,22 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// Represents an implementation of a certain type provided by a plugin /// Represents an feature of a certain type provided by a plugin
/// </summary> /// </summary>
public abstract class PluginImplementation : PropertyChangedBase, IDisposable public abstract class PluginFeature : CorePropertyChanged, IDisposable
{ {
private Exception? _loadException;
private bool _isEnabled; private bool _isEnabled;
private Exception? _loadException;
/// <summary> /// <summary>
/// Gets the plugin that provides this implementation /// Gets the plugin that provides this feature
/// </summary> /// </summary>
public Plugin? Plugin { get; internal set; } public Plugin Plugin { get; internal set; }
/// <summary> /// <summary>
/// Gets whether the plugin is enabled /// Gets whether the plugin is enabled
@ -38,15 +36,20 @@ namespace Artemis.Core
internal set => SetAndNotify(ref _loadException, value); internal set => SetAndNotify(ref _loadException, value);
} }
internal PluginImplementationEntity? Entity { get; set; } /// <summary>
/// Gets the identifier of this plugin feature
/// </summary>
public string Id => $"{GetType().FullName}-{Plugin.Guid.ToString().Substring(0, 8)}"; // Not as unique as a GUID but good enough and stays readable
internal PluginFeatureEntity Entity { get; set; }
/// <summary> /// <summary>
/// Called when the implementation is activated /// Called when the feature is activated
/// </summary> /// </summary>
public abstract void Enable(); public abstract void Enable();
/// <summary> /// <summary>
/// Called when the implementation is deactivated or when Artemis shuts down /// Called when the feature is deactivated or when Artemis shuts down
/// </summary> /// </summary>
public abstract void Disable(); public abstract void Disable();
@ -56,12 +59,12 @@ namespace Artemis.Core
return; return;
if (Plugin == null) if (Plugin == null)
throw new ArtemisCoreException("Cannot enable a plugin implementation that is not associated with a plugin"); throw new ArtemisCoreException("Cannot enable a plugin feature that is not associated with a plugin");
lock (Plugin) lock (Plugin)
{ {
if (!Plugin.IsEnabled) if (!Plugin.IsEnabled)
throw new ArtemisCoreException("Cannot enable a plugin implementation of a disabled plugin"); throw new ArtemisCoreException("Cannot enable a plugin feature of a disabled plugin");
if (!enable) if (!enable)
{ {
@ -72,7 +75,7 @@ namespace Artemis.Core
OnDisabled(); OnDisabled();
return; return;
} }
try try
{ {
if (isAutoEnable && GetLockFileCreated()) if (isAutoEnable && GetLockFileCreated())
@ -89,19 +92,10 @@ namespace Artemis.Core
// Allow up to 15 seconds for plugins to activate. // 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 // This means plugins that need more time should do their long running tasks in a background thread, which is intentional
ManualResetEvent wait = new ManualResetEvent(false); // This would've been a perfect match for Thread.Abort but that didn't make it into .NET Core
Thread work = new Thread(() => Task enableTask = Task.Run(InternalEnable);
{ if (!enableTask.Wait(TimeSpan.FromSeconds(15)))
InternalEnable();
wait.Set();
});
work.Start();
wait.WaitOne(TimeSpan.FromSeconds(15));
if (work.IsAlive)
{
work.Abort();
throw new ArtemisPluginException(Plugin, "Plugin load timeout"); throw new ArtemisPluginException(Plugin, "Plugin load timeout");
}
LoadException = null; LoadException = null;
OnEnabled(); OnEnabled();
@ -144,7 +138,7 @@ namespace Artemis.Core
internal void CreateLockFile() internal void CreateLockFile()
{ {
if (Plugin == null) if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin"); throw new ArtemisCoreException("Cannot lock a plugin feature that is not associated with a plugin");
File.Create(Plugin.ResolveRelativePath($"{GetType().FullName}.lock")).Close(); File.Create(Plugin.ResolveRelativePath($"{GetType().FullName}.lock")).Close();
} }
@ -152,7 +146,7 @@ namespace Artemis.Core
internal void DeleteLockFile() internal void DeleteLockFile()
{ {
if (Plugin == null) if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin"); throw new ArtemisCoreException("Cannot lock a plugin feature that is not associated with a plugin");
if (GetLockFileCreated()) if (GetLockFileCreated())
File.Delete(Plugin.ResolveRelativePath($"{GetType().FullName}.lock")); File.Delete(Plugin.ResolveRelativePath($"{GetType().FullName}.lock"));
@ -161,7 +155,7 @@ namespace Artemis.Core
internal bool GetLockFileCreated() internal bool GetLockFileCreated()
{ {
if (Plugin == null) if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin"); throw new ArtemisCoreException("Cannot lock a plugin feature that is not associated with a plugin");
return File.Exists(Plugin.ResolveRelativePath($"{GetType().FullName}.lock")); return File.Exists(Plugin.ResolveRelativePath($"{GetType().FullName}.lock"));
} }
@ -171,17 +165,17 @@ namespace Artemis.Core
#region Events #region Events
/// <summary> /// <summary>
/// Occurs when the implementation is enabled /// Occurs when the feature is enabled
/// </summary> /// </summary>
public event EventHandler? Enabled; public event EventHandler? Enabled;
/// <summary> /// <summary>
/// Occurs when the implementation is disabled /// Occurs when the feature is disabled
/// </summary> /// </summary>
public event EventHandler? Disabled; public event EventHandler? Disabled;
/// <summary> /// <summary>
/// Triggers the PluginEnabled event /// Triggers the Enabled event
/// </summary> /// </summary>
protected virtual void OnEnabled() protected virtual void OnEnabled()
{ {
@ -189,7 +183,7 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Triggers the PluginDisabled event /// Triggers the Disabled event
/// </summary> /// </summary>
protected virtual void OnDisabled() protected virtual void OnDisabled()
{ {

View File

@ -1,6 +1,5 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -8,7 +7,7 @@ namespace Artemis.Core
/// Represents basic info about a plugin and contains a reference to the instance of said plugin /// Represents basic info about a plugin and contains a reference to the instance of said plugin
/// </summary> /// </summary>
[JsonObject(MemberSerialization.OptIn)] [JsonObject(MemberSerialization.OptIn)]
public class PluginInfo : PropertyChangedBase public class PluginInfo : CorePropertyChanged
{ {
private string _description; private string _description;
private Guid _guid; private Guid _guid;

View File

@ -2,7 +2,6 @@
using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Entities.Plugins;
using Artemis.Storage.Repositories.Interfaces; using Artemis.Storage.Repositories.Interfaces;
using Newtonsoft.Json; using Newtonsoft.Json;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -10,17 +9,18 @@ namespace Artemis.Core
/// Represents a setting tied to a plugin of type <typeparamref name="T" /> /// Represents a setting tied to a plugin of type <typeparamref name="T" />
/// </summary> /// </summary>
/// <typeparam name="T">The value type of the setting</typeparam> /// <typeparam name="T">The value type of the setting</typeparam>
public class PluginSetting<T> : PropertyChangedBase public class PluginSetting<T> : CorePropertyChanged
{ {
// ReSharper disable once NotAccessedField.Local // TODO: Why? Should have included that...
private readonly PluginInfo _pluginInfo; // ReSharper disable once NotAccessedField.Local
private readonly Plugin _plugin;
private readonly IPluginRepository _pluginRepository; private readonly IPluginRepository _pluginRepository;
private readonly PluginSettingEntity _pluginSettingEntity; private readonly PluginSettingEntity _pluginSettingEntity;
private T _value; private T _value;
internal PluginSetting(PluginInfo pluginInfo, IPluginRepository pluginRepository, PluginSettingEntity pluginSettingEntity) internal PluginSetting(Plugin plugin, IPluginRepository pluginRepository, PluginSettingEntity pluginSettingEntity)
{ {
_pluginInfo = pluginInfo; _plugin = plugin;
_pluginRepository = pluginRepository; _pluginRepository = pluginRepository;
_pluginSettingEntity = pluginSettingEntity; _pluginSettingEntity = pluginSettingEntity;
@ -52,7 +52,7 @@ namespace Artemis.Core
_value = value; _value = value;
OnSettingChanged(); OnSettingChanged();
NotifyOfPropertyChange(nameof(Value)); OnPropertyChanged(nameof(Value));
if (AutoSave) if (AutoSave)
Save(); Save();

View File

@ -14,17 +14,17 @@ namespace Artemis.Core
private readonly IPluginRepository _pluginRepository; private readonly IPluginRepository _pluginRepository;
private readonly Dictionary<string, object> _settingEntities; private readonly Dictionary<string, object> _settingEntities;
internal PluginSettings(PluginInfo pluginInfo, IPluginRepository pluginRepository) internal PluginSettings(Plugin plugin, IPluginRepository pluginRepository)
{ {
PluginInfo = pluginInfo; Plugin = plugin;
_pluginRepository = pluginRepository; _pluginRepository = pluginRepository;
_settingEntities = new Dictionary<string, object>(); _settingEntities = new Dictionary<string, object>();
} }
/// <summary> /// <summary>
/// Gets the info of the plugin this setting belongs to /// Gets the plugin these settings belong to
/// </summary> /// </summary>
public PluginInfo PluginInfo { get; } public Plugin Plugin { get; }
/// <summary> /// <summary>
/// Gets the setting with the provided name. If the setting does not exist yet, it is created. /// Gets the setting with the provided name. If the setting does not exist yet, it is created.
@ -41,15 +41,15 @@ namespace Artemis.Core
if (_settingEntities.ContainsKey(name)) if (_settingEntities.ContainsKey(name))
return (PluginSetting<T>) _settingEntities[name]; return (PluginSetting<T>) _settingEntities[name];
// Try to find in database // Try to find in database
PluginSettingEntity settingEntity = _pluginRepository.GetSettingByNameAndGuid(name, PluginInfo.Guid); PluginSettingEntity settingEntity = _pluginRepository.GetSettingByNameAndGuid(name, Plugin.Guid);
// If not found, create a new one // If not found, create a new one
if (settingEntity == null) if (settingEntity == null)
{ {
settingEntity = new PluginSettingEntity {Name = name, PluginGuid = PluginInfo.Guid, Value = JsonConvert.SerializeObject(defaultValue)}; settingEntity = new PluginSettingEntity {Name = name, PluginGuid = Plugin.Guid, Value = JsonConvert.SerializeObject(defaultValue)};
_pluginRepository.AddSetting(settingEntity); _pluginRepository.AddSetting(settingEntity);
} }
PluginSetting<T> pluginSetting = new PluginSetting<T>(PluginInfo, _pluginRepository, settingEntity); PluginSetting<T> pluginSetting = new PluginSetting<T>(Plugin, _pluginRepository, settingEntity);
// This overrides null with the default value, I'm not sure if that's desirable because you // This overrides null with the default value, I'm not sure if that's desirable because you
// might expect something to go null and you might not // might expect something to go null and you might not

View File

@ -8,40 +8,41 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Represents a registration for a timed plugin update /// Represents a registration for a timed plugin update
/// </summary> /// </summary>
public class TimedUpdateRegistration public class TimedUpdateRegistration : IDisposable
{ {
private DateTime _lastEvent; private DateTime _lastEvent;
private Timer _timer; private Timer _timer;
private bool _disposed;
internal TimedUpdateRegistration(PluginInfo pluginInfo, TimeSpan interval, Action<double> action) internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Action<double> action)
{ {
PluginInfo = pluginInfo; Feature = feature;
Interval = interval; Interval = interval;
Action = action; Action = action;
PluginInfo.Plugin.Enabled += InstanceOnEnabled; Feature.Enabled += FeatureOnEnabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled; Feature.Disabled += FeatureOnDisabled;
if (PluginInfo.Plugin.IsEnabled) if (Feature.IsEnabled)
Start(); Start();
} }
internal TimedUpdateRegistration(PluginInfo pluginInfo, TimeSpan interval, Func<double, Task> asyncAction) internal TimedUpdateRegistration(PluginFeature feature, TimeSpan interval, Func<double, Task> asyncAction)
{ {
PluginInfo = pluginInfo; Feature = feature;
Interval = interval; Interval = interval;
AsyncAction = asyncAction; AsyncAction = asyncAction;
PluginInfo.Plugin.Enabled += InstanceOnEnabled; Feature.Enabled += FeatureOnEnabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled; Feature.Disabled += FeatureOnDisabled;
if (PluginInfo.Plugin.IsEnabled) if (Feature.IsEnabled)
Start(); Start();
} }
/// <summary> /// <summary>
/// Gets the plugin info of the plugin this registration is associated with /// Gets the plugin feature this registration is associated with
/// </summary> /// </summary>
public PluginInfo PluginInfo { get; } public PluginFeature Feature { get; }
/// <summary> /// <summary>
/// Gets the interval at which the update should occur /// Gets the interval at which the update should occur
@ -64,10 +65,13 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void Start() public void Start()
{ {
if (_disposed)
throw new ObjectDisposedException("TimedUpdateRegistration");
lock (this) lock (this)
{ {
if (!PluginInfo.Plugin.IsEnabled) if (!Feature.IsEnabled)
throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin"); throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin feature");
if (_timer != null) if (_timer != null)
return; return;
@ -85,6 +89,9 @@ namespace Artemis.Core
/// </summary> /// </summary>
public void Stop() public void Stop()
{ {
if (_disposed)
throw new ObjectDisposedException("TimedUpdateRegistration");
lock (this) lock (this)
{ {
if (_timer == null) if (_timer == null)
@ -99,7 +106,7 @@ namespace Artemis.Core
private void TimerOnElapsed(object sender, ElapsedEventArgs e) private void TimerOnElapsed(object sender, ElapsedEventArgs e)
{ {
if (!PluginInfo.Plugin.IsEnabled) if (!Feature.IsEnabled)
return; return;
lock (this) lock (this)
@ -108,7 +115,7 @@ namespace Artemis.Core
_lastEvent = DateTime.Now; _lastEvent = DateTime.Now;
// Modules don't always want to update, honor that // Modules don't always want to update, honor that
if (PluginInfo.Plugin is Module module && !module.IsUpdateAllowed) if (Feature is Module module && !module.IsUpdateAllowed)
return; return;
if (Action != null) if (Action != null)
@ -121,14 +128,25 @@ namespace Artemis.Core
} }
} }
private void InstanceOnEnabled(object sender, EventArgs e) private void FeatureOnEnabled(object sender, EventArgs e)
{ {
Start(); Start();
} }
private void InstanceOnDisabled(object sender, EventArgs e) private void FeatureOnDisabled(object sender, EventArgs e)
{ {
Stop(); Stop();
} }
/// <inheritdoc />
public void Dispose()
{
Stop();
Feature.Enabled -= FeatureOnEnabled;
Feature.Disabled -= FeatureOnDisabled;
_disposed = true;
}
} }
} }

View File

@ -40,7 +40,7 @@ namespace Artemis.Core.Services
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService) IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
{ {
Kernel = kernel; Kernel = kernel;
Constants.CorePluginInfo.Kernel = kernel; Constants.CorePlugin.Kernel = kernel;
_logger = logger; _logger = logger;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
@ -133,8 +133,8 @@ namespace Artemis.Core.Services
private void UpdatePluginCache() private void UpdatePluginCache()
{ {
_modules = _pluginManagementService.GetPluginsOfType<Module>().Where(p => p.IsEnabled).ToList(); _modules = _pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled).ToList();
_dataModelExpansions = _pluginManagementService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled).ToList(); _dataModelExpansions = _pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled).ToList();
} }
private void ConfigureJsonConvert() private void ConfigureJsonConvert()

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using Artemis.Core.DeviceProviders;
using RGB.NET.Core; using RGB.NET.Core;
namespace Artemis.Core.Services namespace Artemis.Core.Services
@ -33,69 +34,77 @@ namespace Artemis.Core.Services
void UnloadPlugins(); void UnloadPlugins();
/// <summary> /// <summary>
/// Loads the plugin defined in the provided <see cref="PluginInfo" /> /// Loads the plugin located in the provided <paramref name="directory" />
/// </summary> /// </summary>
/// <param name="pluginInfo">The plugin info defining the plugin to load</param> /// <param name="directory">The directory where the plugin is located</param>
void LoadPlugin(DirectoryInfo pluginInfo); Plugin LoadPlugin(DirectoryInfo directory);
/// <summary> /// <summary>
/// Unloads the plugin defined in the provided <see cref="PluginInfo" /> /// Enables the provided <paramref name="plugin" />
/// </summary> /// </summary>
/// <param name="pluginInfo">The plugin info defining the plugin to unload</param> /// <param name="plugin">The plugin to enable</param>
void UnloadPlugin(PluginInfo pluginInfo); void EnablePlugin(Plugin plugin, bool ignorePluginLock = false);
/// <summary> /// <summary>
/// Enables the provided plugin /// Unloads the provided <paramref name="plugin" />
/// </summary> /// </summary>
/// <param name="pluginImplementation"></param> /// <param name="plugin">The plugin to unload</param>
void UnloadPlugin(Plugin plugin);
/// <summary>
/// Disables the provided <paramref name="plugin" />
/// </summary>
/// <param name="plugin">The plugin to disable</param>
void DisablePlugin(Plugin plugin);
/// <summary>
/// Enables the provided plugin feature
/// </summary>
/// <param name="pluginFeature"></param>
/// <param name="isAutoEnable">If true, fails if there is a lock file present</param> /// <param name="isAutoEnable">If true, fails if there is a lock file present</param>
void EnablePluginImplementation(PluginImplementation pluginImplementation, bool isAutoEnable = false); void EnablePluginFeature(PluginFeature pluginFeature, bool isAutoEnable = false);
/// <summary> /// <summary>
/// Disables the provided plugin /// Disables the provided plugin feature
/// </summary> /// </summary>
/// <param name="pluginImplementation"></param> /// <param name="pluginFeature"></param>
void DisablePluginImplementation(PluginImplementation pluginImplementation); void DisablePluginFeature(PluginFeature pluginFeature);
/// <summary>
/// Finds the plugin info related to the plugin
/// </summary>
/// <param name="pluginImplementation">The plugin you want to find the plugin info for</param>
/// <returns>The plugins PluginInfo</returns>
PluginInfo GetPluginInfo(PluginImplementation pluginImplementation);
/// <summary> /// <summary>
/// Gets the plugin info of all loaded plugins /// Gets the plugin info of all loaded plugins
/// </summary> /// </summary>
/// <returns>A list containing all the plugin info</returns> /// <returns>A list containing all the plugin info</returns>
List<PluginInfo> GetAllPluginInfo(); List<Plugin> GetAllPlugins();
/// <summary> /// <summary>
/// Finds all enabled <see cref="PluginImplementation" /> instances of <typeparamref name="T" /> /// Finds all enabled <see cref="PluginFeature" /> instances of <typeparamref name="T" />
/// </summary> /// </summary>
/// <typeparam name="T">Either <see cref="PluginImplementation" /> or a plugin type implementing <see cref="PluginImplementation" /></typeparam> /// <typeparam name="T">
/// <returns>Returns a list of plugin instances of <typeparamref name="T" /></returns> /// Either <see cref="PluginFeature" /> or a plugin type implementing
List<T> GetPluginsOfType<T>() where T : PluginImplementation; /// <see cref="PluginFeature" />
/// </typeparam>
/// <returns>Returns a list of feature instances of <typeparamref name="T" /></returns>
List<T> GetFeaturesOfType<T>() where T : PluginFeature;
/// <summary> /// <summary>
/// Gets the plugin that provided the specified assembly /// Gets the plugin that provided the specified assembly
/// </summary> /// </summary>
/// <param name="assembly"></param> /// <param name="assembly"></param>
/// <returns></returns> /// <returns></returns>
PluginImplementation GetPluginByAssembly(Assembly assembly); Plugin GetPluginByAssembly(Assembly assembly);
/// <summary>
/// Returns the plugin info of the current call stack
/// </summary>
/// <returns>If the current call stack contains a plugin, the plugin. Otherwise null</returns>
Plugin? GetCallingPlugin();
/// <summary> /// <summary>
/// Gets the plugin that defined the specified device /// Gets the plugin that defined the specified device
/// </summary> /// </summary>
/// <param name="device"></param> /// <param name="device"></param>
/// <returns></returns> /// <returns></returns>
PluginImplementation GetPluginByDevice(IRGBDevice device); DeviceProvider GetDeviceProviderByDevice(IRGBDevice device);
/// <summary>
/// Returns the plugin info of the current call stack
/// </summary>
/// <returns>If the current call stack contains a plugin, the plugin. Otherwise null</returns>
PluginImplementation GetCallingPlugin();
#region Events #region Events
@ -134,6 +143,26 @@ namespace Artemis.Core.Services
/// </summary> /// </summary>
event EventHandler<PluginEventArgs> PluginDisabled; event EventHandler<PluginEventArgs> PluginDisabled;
/// <summary>
/// Occurs when a plugin feature is being enabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabling;
/// <summary>
/// Occurs when a plugin feature has been enabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabled;
/// <summary>
/// Occurs when a plugin feature could not be enabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnableFailed;
/// <summary>
/// Occurs when a plugin feature has been disabled
/// </summary>
public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled;
#endregion #endregion
} }
} }

View File

@ -26,16 +26,129 @@ namespace Artemis.Core.Services
_moduleRepository = moduleRepository; _moduleRepository = moduleRepository;
_pluginManagementService = pluginManagementService; _pluginManagementService = pluginManagementService;
_profileService = profileService; _profileService = profileService;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginManagementEnabled; _pluginManagementService.PluginFeatureEnabled += OnPluginFeatureEnabled;
Timer activationUpdateTimer = new Timer(2000); Timer activationUpdateTimer = new Timer(2000);
activationUpdateTimer.Start(); activationUpdateTimer.Start();
activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed; activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed;
foreach (Module module in _pluginManagementService.GetPluginsOfType<Module>()) foreach (Module module in _pluginManagementService.GetFeaturesOfType<Module>())
InitialiseOrApplyPriority(module); InitialiseOrApplyPriority(module);
} }
private async void ActivationUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
await UpdateModuleActivation();
}
private async Task ActivateModule(Module module)
{
try
{
module.Activate(false);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
await _profileService.ActivateLastProfileAnimated(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to activate module and last profile.", e), "Failed to activate module and last profile"
);
throw;
}
}
private async Task DeactivateModule(Module module)
{
try
{
// If this is a profile module, animate profile disable
// module.Deactivate would do the same but without animation
if (module.IsActivated && module is ProfileModule profileModule)
await profileModule.ChangeActiveProfileAnimated(null, null);
module.Deactivate(false);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile"
);
throw;
}
}
private void OverrideActivate(Module module)
{
try
{
if (module.IsActivated)
return;
// If activating while it should be deactivated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
module.Activate(!shouldBeActivated);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
_profileService.ActivateLastProfile(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to activate module and last profile.", e), "Failed to activate module and last profile"
);
throw;
}
}
private void OverrideDeactivate(Module module, bool clearingOverride)
{
try
{
if (!module.IsActivated)
return;
// If deactivating while it should be activated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
// No need to deactivate if it is not in an overridden state
if (shouldBeActivated && !module.IsActivatedOverride && !clearingOverride)
return;
module.Deactivate(true);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginFeatureException(
module, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile"
);
throw;
}
}
private void OnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
{
if (e.PluginFeature is Module module)
InitialiseOrApplyPriority(module);
}
private void InitialiseOrApplyPriority(Module module)
{
ModulePriorityCategory category = module.DefaultPriorityCategory;
int priority = 1;
module.Entity = _moduleRepository.GetByModuleId(module.Id);
if (module.Entity != null)
{
category = (ModulePriorityCategory) module.Entity.PriorityCategory;
priority = module.Entity.Priority;
}
UpdateModulePriority(module, category, priority);
}
public Module ActiveModuleOverride { get; private set; } public Module ActiveModuleOverride { get; private set; }
public async Task SetActiveModuleOverride(Module overrideModule) public async Task SetActiveModuleOverride(Module overrideModule)
@ -46,17 +159,19 @@ namespace Artemis.Core.Services
if (ActiveModuleOverride == overrideModule) if (ActiveModuleOverride == overrideModule)
return; return;
if (overrideModule != null) if (overrideModule != null)
{ {
OverrideActivate(overrideModule); OverrideActivate(overrideModule);
_logger.Information($"Setting active module override to {overrideModule.DisplayName}"); _logger.Information($"Setting active module override to {overrideModule.DisplayName}");
} }
else else
{
_logger.Information("Clearing active module override"); _logger.Information("Clearing active module override");
}
// Always deactivate all other modules whenever override is called // Always deactivate all other modules whenever override is called
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().ToList(); List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
foreach (Module module in modules.Where(m => m != overrideModule)) foreach (Module module in modules.Where(m => m != overrideModule))
OverrideDeactivate(module, overrideModule != null); OverrideDeactivate(module, overrideModule != null);
@ -83,13 +198,8 @@ namespace Artemis.Core.Services
// the principle is different for this service but not for the module // the principle is different for this service but not for the module
bool shouldBeActivated = ActiveModuleOverride.EvaluateActivationRequirements(); bool shouldBeActivated = ActiveModuleOverride.EvaluateActivationRequirements();
if (shouldBeActivated && ActiveModuleOverride.IsActivatedOverride) if (shouldBeActivated && ActiveModuleOverride.IsActivatedOverride)
{
ActiveModuleOverride.Reactivate(true, false); ActiveModuleOverride.Reactivate(true, false);
} else if (!shouldBeActivated && !ActiveModuleOverride.IsActivatedOverride) ActiveModuleOverride.Reactivate(false, true);
else if (!shouldBeActivated && !ActiveModuleOverride.IsActivatedOverride)
{
ActiveModuleOverride.Reactivate(false, true);
}
return; return;
} }
@ -97,10 +207,9 @@ namespace Artemis.Core.Services
Stopwatch stopwatch = new Stopwatch(); Stopwatch stopwatch = new Stopwatch();
stopwatch.Start(); stopwatch.Start();
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().ToList(); List<Module> modules = _pluginManagementService.GetFeaturesOfType<Module>().ToList();
List<Task> tasks = new List<Task>(); List<Task> tasks = new List<Task>();
foreach (Module module in modules) foreach (Module module in modules)
{
lock (module) lock (module)
{ {
bool shouldBeActivated = module.EvaluateActivationRequirements() && module.IsEnabled; bool shouldBeActivated = module.EvaluateActivationRequirements() && module.IsEnabled;
@ -109,7 +218,6 @@ namespace Artemis.Core.Services
else if (!shouldBeActivated && module.IsActivated) else if (!shouldBeActivated && module.IsActivated)
tasks.Add(DeactivateModule(module)); tasks.Add(DeactivateModule(module));
} }
}
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
@ -128,7 +236,12 @@ namespace Artemis.Core.Services
if (module.PriorityCategory == category && module.Priority == priority) if (module.PriorityCategory == category && module.Priority == priority)
return; return;
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().Where(m => m.PriorityCategory == category).OrderBy(m => m.Priority).ToList(); List<Module> modules = _pluginManagementService
.GetFeaturesOfType<Module>()
.Where(m => m.PriorityCategory == category)
.OrderBy(m => m.Priority)
.ToList();
if (modules.Contains(module)) if (modules.Contains(module))
modules.Remove(module); modules.Remove(module);
@ -149,110 +262,5 @@ namespace Artemis.Core.Services
} }
} }
} }
private async void ActivationUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
{
await UpdateModuleActivation();
}
private async Task ActivateModule(Module module)
{
try
{
module.Activate(false);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
await _profileService.ActivateLastProfileAnimated(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to activate module and last profile.", e), "Failed to activate module and last profile");
throw;
}
}
private async Task DeactivateModule(Module module)
{
try
{
// If this is a profile module, animate profile disable
// module.Deactivate would do the same but without animation
if (module.IsActivated && module is ProfileModule profileModule)
await profileModule.ChangeActiveProfileAnimated(null, null);
module.Deactivate(false);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile");
throw;
}
}
private void OverrideActivate(Module module)
{
try
{
if (module.IsActivated)
return;
// If activating while it should be deactivated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
module.Activate(!shouldBeActivated);
// If this is a profile module, activate the last active profile after module activation
if (module is ProfileModule profileModule)
_profileService.ActivateLastProfile(profileModule);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to activate module and last profile.", e), "Failed to activate module and last profile");
throw;
}
}
private void OverrideDeactivate(Module module, bool clearingOverride)
{
try
{
if (!module.IsActivated)
return;
// If deactivating while it should be activated, its an override
bool shouldBeActivated = module.EvaluateActivationRequirements();
// No need to deactivate if it is not in an overridden state
if (shouldBeActivated && !module.IsActivatedOverride && !clearingOverride)
return;
module.Deactivate(true);
}
catch (Exception e)
{
_logger.Error(new ArtemisPluginException(module.PluginInfo, "Failed to deactivate module and last profile.", e), "Failed to deactivate module and last profile");
throw;
}
}
private void PluginManagementServiceOnPluginManagementEnabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Plugin is Module module)
InitialiseOrApplyPriority(module);
}
private void InitialiseOrApplyPriority(Module module)
{
ModulePriorityCategory category = module.DefaultPriorityCategory;
int priority = 1;
module.Entity = _moduleRepository.GetByPluginGuid(module.PluginInfo.Guid);
if (module.Entity != null)
{
category = (ModulePriorityCategory) module.Entity.PriorityCategory;
priority = module.Entity.Priority;
}
UpdateModulePriority(module, category, priority);
}
} }
} }

View File

@ -41,6 +41,21 @@ namespace Artemis.Core.Services
Directory.CreateDirectory(Constants.DataFolder + "plugins"); Directory.CreateDirectory(Constants.DataFolder + "plugins");
} }
private void CopyBuiltInPlugin(FileInfo zipFileInfo, ZipArchive zipArchive)
{
DirectoryInfo pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins", Path.GetFileNameWithoutExtension(zipFileInfo.Name)));
bool createLockFile = File.Exists(Path.Combine(pluginDirectory.FullName, "artemis.lock"));
// Remove the old directory if it exists
if (Directory.Exists(pluginDirectory.FullName))
pluginDirectory.DeleteRecursively();
Directory.CreateDirectory(pluginDirectory.FullName);
zipArchive.ExtractToDirectory(pluginDirectory.FullName, true);
if (createLockFile)
File.Create(Path.Combine(pluginDirectory.FullName, "artemis.lock")).Close();
}
public bool LoadingPlugins { get; private set; } public bool LoadingPlugins { get; private set; }
#region Built in plugins #region Built in plugins
@ -72,7 +87,9 @@ namespace Artemis.Core.Services
// Find the matching plugin in the plugin folder // Find the matching plugin in the plugin folder
DirectoryInfo? match = pluginDirectory.EnumerateDirectories().FirstOrDefault(d => d.Name == Path.GetFileNameWithoutExtension(zipFile.Name)); DirectoryInfo? match = pluginDirectory.EnumerateDirectories().FirstOrDefault(d => d.Name == Path.GetFileNameWithoutExtension(zipFile.Name));
if (match == null) if (match == null)
{
CopyBuiltInPlugin(zipFile, archive); CopyBuiltInPlugin(zipFile, archive);
}
else else
{ {
string metadataFile = Path.Combine(match.FullName, "plugin.json"); string metadataFile = Path.Combine(match.FullName, "plugin.json");
@ -105,6 +122,48 @@ namespace Artemis.Core.Services
#endregion #endregion
public List<Plugin> GetAllPlugins()
{
return new List<Plugin>(_plugins);
}
public List<T> GetFeaturesOfType<T>() where T : PluginFeature
{
return _plugins.Where(p => p.IsEnabled).SelectMany(p => p.Features.Where(i => i.IsEnabled && i is T)).Cast<T>().ToList();
}
public Plugin? GetPluginByAssembly(Assembly assembly)
{
return _plugins.FirstOrDefault(p => p.Assembly == assembly);
}
// TODO: move to a more appropriate service
public DeviceProvider GetDeviceProviderByDevice(IRGBDevice rgbDevice)
{
return GetFeaturesOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
}
public Plugin? GetCallingPlugin()
{
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
foreach (StackFrame stackFrame in stackFrames)
{
Assembly assembly = stackFrame.GetMethod().DeclaringType.Assembly;
Plugin plugin = GetPluginByAssembly(assembly);
if (plugin != null)
return plugin;
}
return null;
}
public void Dispose()
{
UnloadPlugins();
}
#region Plugins #region Plugins
public void LoadPlugins(bool ignorePluginLock) public void LoadPlugins(bool ignorePluginLock)
@ -125,7 +184,8 @@ namespace Artemis.Core.Services
{ {
try try
{ {
LoadPlugin(subDirectory); Plugin plugin = LoadPlugin(subDirectory);
EnablePlugin(plugin, ignorePluginLock);
} }
catch (Exception e) catch (Exception e)
{ {
@ -133,22 +193,6 @@ namespace Artemis.Core.Services
} }
} }
// Activate plugins after they are all loaded
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
{
foreach (PluginImplementation pluginImplementation in plugin.Implementations.Where(i => i.Entity.IsEnabled))
{
try
{
EnablePluginImplementation(pluginImplementation, !ignorePluginLock);
}
catch (Exception)
{
// ignored, logged in EnablePlugin
}
}
}
LoadingPlugins = false; LoadingPlugins = false;
} }
} }
@ -165,29 +209,32 @@ namespace Artemis.Core.Services
} }
} }
public void LoadPlugin(DirectoryInfo pluginDirectory) public Plugin LoadPlugin(DirectoryInfo directory)
{ {
lock (_plugins) lock (_plugins)
{ {
_logger.Debug("Loading plugin from {directory}", pluginDirectory.FullName); _logger.Debug("Loading plugin from {directory}", directory.FullName);
// Load the metadata // Load the metadata
string metadataFile = Path.Combine(pluginDirectory.FullName, "plugin.json"); string metadataFile = Path.Combine(directory.FullName, "plugin.json");
if (!File.Exists(metadataFile)) if (!File.Exists(metadataFile))
_logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception"); _logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception");
// PluginInfo contains the ID which we need to move on // PluginInfo contains the ID which we need to move on
PluginInfo pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile)); PluginInfo pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
if (pluginInfo.Guid == Constants.CorePluginInfo.Guid)
throw new ArtemisPluginException($"Plugin cannot use reserved GUID {pluginInfo.Guid}");
// Ensure the plugin is not already loaded // Ensure the plugin is not already loaded
if (_plugins.Any(p => p.Guid == pluginInfo.Guid)) if (_plugins.Any(p => p.Guid == pluginInfo.Guid))
throw new ArtemisCoreException("Cannot load a plugin that is already loaded"); throw new ArtemisCoreException("Cannot load a plugin that is already loaded");
Plugin plugin = new Plugin(pluginInfo, pluginDirectory); Plugin plugin = new Plugin(pluginInfo, directory);
OnPluginLoading(new PluginEventArgs(plugin)); OnPluginLoading(new PluginEventArgs(plugin));
// Load the entity and fall back on creating a new one // Load the entity and fall back on creating a new one
plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity { Id = plugin.Guid, IsEnabled = true }; plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity {Id = plugin.Guid, IsEnabled = true};
// Locate the main assembly entry // Locate the main assembly entry
string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main); string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main);
@ -210,55 +257,94 @@ namespace Artemis.Core.Services
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e); throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
} }
// Get the Plugin implementation from the main assembly and if there is only one, instantiate it List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
List<Type> implementationTypes; if (bootstrappers.Count > 1)
try _logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
{ if (bootstrappers.Any())
implementationTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginImplementation).IsAssignableFrom(t)).ToList(); plugin.Bootstrapper = (IPluginBootstrapper?) Activator.CreateInstance(bootstrappers.First());
}
catch (ReflectionTypeLoadException e)
{
throw new ArtemisPluginException(plugin, "Failed to initialize the plugin assembly", new AggregateException(e.LoaderExceptions));
}
// Create the Ninject child kernel and load the module
plugin.Kernel = new ChildKernel(_kernel);
plugin.Kernel.Load(new PluginModule(pluginInfo));
if (!implementationTypes.Any())
_logger.Warning("Plugin {plugin} contains no implementations", plugin);
// Create instances of each implementation and add them to the plugin
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
foreach (Type implementationType in implementationTypes)
{
try
{
PluginImplementation instance = (PluginImplementation)plugin.Kernel.Get(implementationType);
plugin.AddImplementation(instance);
}
catch (Exception e)
{
throw new ArtemisPluginException(plugin, "Failed to load instantiate implementation", e);
}
}
_plugins.Add(plugin); _plugins.Add(plugin);
OnPluginLoaded(new PluginEventArgs(plugin)); OnPluginLoaded(new PluginEventArgs(plugin));
return plugin;
} }
} }
public void EnablePlugin(Plugin plugin, 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.SetEnabled(true);
// Get the Plugin feature from the main assembly and if there is only one, instantiate it
List<Type> featureTypes;
try
{
featureTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginFeature).IsAssignableFrom(t)).ToList();
}
catch (ReflectionTypeLoadException e)
{
throw new ArtemisPluginException(plugin, "Failed to initialize the plugin assembly", new AggregateException(e.LoaderExceptions));
}
if (!featureTypes.Any())
_logger.Warning("Plugin {plugin} contains no features", plugin);
// Create instances of each feature and add them to the plugin
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
foreach (Type featureType in featureTypes)
{
try
{
// Include Plugin as a parameter for the PluginSettingsProvider
IParameter[] parameters = {new Parameter("Plugin", plugin, false)};
PluginFeature instance = (PluginFeature) plugin.Kernel.Get(featureType, parameters);
plugin.AddFeature(instance);
// Load the enabled state and if not found, default to true
instance.Entity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ??
new PluginFeatureEntity {IsEnabled = true, Type = featureType.FullName};
}
catch (Exception e)
{
throw new ArtemisPluginException(plugin, "Failed to load instantiate feature", e);
}
}
// Activate plugins after they are all loaded
foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled))
{
try
{
EnablePluginFeature(pluginFeature, !ignorePluginLock);
}
catch (Exception)
{
// ignored, logged in EnablePluginFeature
}
}
OnPluginEnabled(new PluginEventArgs(plugin));
}
public void UnloadPlugin(Plugin plugin) public void UnloadPlugin(Plugin plugin)
{ {
lock (_plugins) lock (_plugins)
{ {
try try
{ {
plugin.SetEnabled(false); DisablePlugin(plugin);
} }
catch (Exception) catch (Exception e)
{ {
// TODO: Log these _logger.Warning(new ArtemisPluginException(plugin, "Exception during DisablePlugin call for UnloadPlugin", e), "Failed to unload plugin");
} }
finally finally
{ {
@ -267,38 +353,59 @@ namespace Artemis.Core.Services
plugin.Dispose(); plugin.Dispose();
_plugins.Remove(plugin); _plugins.Remove(plugin);
OnPluginUnloaded(new PluginEventArgs(plugin));
} }
} }
public void DisablePlugin(Plugin plugin)
{
if (!plugin.IsEnabled)
return;
while (plugin.Features.Any())
{
PluginFeature feature = plugin.Features[0];
plugin.RemoveFeature(feature);
OnPluginFeatureDisabled(new PluginFeatureEventArgs(feature));
}
plugin.SetEnabled(false);
plugin.Kernel.Dispose();
plugin.Kernel = null;
OnPluginDisabled(new PluginEventArgs(plugin));
}
#endregion #endregion
#region Features
#region Implementations public void EnablePluginFeature(PluginFeature pluginFeature, bool isAutoEnable = false)
public void EnablePluginImplementation(PluginImplementation pluginImplementation, bool isAutoEnable = false)
{ {
_logger.Debug("Enabling plugin implementation {implementation} - {plugin}", pluginImplementation, pluginImplementation.Plugin); _logger.Debug("Enabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
lock (_plugins) lock (_plugins)
{ {
OnPluginFeatureEnabling(new PluginFeatureEventArgs(pluginFeature));
try try
{ {
// A device provider may be queued for disable on next restart, this undoes that // A device provider may be queued for disable on next restart, this undoes that
if (pluginImplementation is DeviceProvider && pluginImplementation.IsEnabled && !pluginImplementation.PluginInfo.IsEnabled) if (pluginFeature is DeviceProvider && pluginFeature.IsEnabled && !pluginFeature.Entity.IsEnabled)
{ {
pluginImplementation.PluginInfo.IsEnabled = true; pluginFeature.Entity.IsEnabled = true;
pluginImplementation.PluginInfo.ApplyToEntity(); SavePlugin(pluginFeature.Plugin);
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity);
return; return;
} }
pluginImplementation.SetEnabled(true, isAutoEnable); pluginFeature.SetEnabled(true, isAutoEnable);
pluginFeature.Entity.IsEnabled = true;
} }
catch (Exception e) catch (Exception e)
{ {
_logger.Warning(new ArtemisPluginException(pluginImplementation.PluginInfo, "Exception during SetEnabled(true)", e), "Failed to enable plugin"); _logger.Warning(
new ArtemisPluginException(pluginFeature.Plugin, $"Exception during SetEnabled(true) on {pluginFeature}", e),
"Failed to enable plugin"
);
throw; throw;
} }
finally finally
@ -306,108 +413,83 @@ 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 // 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 // not affect the user's settings
if (isAutoEnable) if (isAutoEnable)
pluginImplementation.PluginInfo.IsEnabled = true; pluginFeature.Entity.IsEnabled = true;
pluginImplementation.PluginInfo.ApplyToEntity(); SavePlugin(pluginFeature.Plugin);
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity);
if (pluginImplementation.PluginInfo.IsEnabled) if (pluginFeature.IsEnabled)
_logger.Debug("Successfully enabled plugin {pluginInfo}", pluginImplementation.PluginInfo); {
_logger.Debug("Successfully enabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
OnPluginFeatureEnabled(new PluginFeatureEventArgs(pluginFeature));
}
else
{
OnPluginFeatureEnableFailed(new PluginFeatureEventArgs(pluginFeature));
}
} }
} }
OnPluginImplementationEnabled(new PluginImplementationEventArgs(pluginImplementation));
} }
public void DisablePluginImplementation(PluginImplementation pluginImplementation) public void DisablePluginFeature(PluginFeature pluginFeature)
{ {
lock (_plugins) lock (_plugins)
{ {
_logger.Debug("Disabling plugin {pluginInfo}", pluginImplementation.PluginInfo); try
// Device providers cannot be disabled at runtime simply queue a disable for next restart
if (pluginImplementation is DeviceProvider)
{ {
// Don't call SetEnabled(false) but simply update enabled state and save it _logger.Debug("Disabling plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
pluginImplementation.PluginInfo.IsEnabled = false;
pluginImplementation.PluginInfo.ApplyToEntity(); // Device providers cannot be disabled at runtime simply queue a disable for next restart
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity); if (pluginFeature is DeviceProvider)
return; {
// 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);
pluginImplementation.SetEnabled(false); if (!pluginFeature.IsEnabled)
pluginImplementation.PluginInfo.ApplyToEntity(); {
_pluginRepository.SavePlugin(pluginImplementation.PluginInfo.PluginEntity); _logger.Debug("Successfully disabled plugin feature {feature} - {plugin}", pluginFeature, pluginFeature.Plugin);
OnPluginFeatureDisabled(new PluginFeatureEventArgs(pluginFeature));
_logger.Debug("Successfully disabled plugin {pluginInfo}", pluginImplementation.PluginInfo); }
}
} }
OnPluginDisabled(new PluginEventArgs(pluginImplementation.PluginInfo));
} }
#endregion #endregion
#region Storage
public PluginInfo GetPluginInfo(PluginImplementation pluginImplementation) private void SavePlugin(Plugin plugin)
{ {
return _plugins.FirstOrDefault(p => p.Plugin == pluginImplementation); foreach (PluginFeature pluginFeature in plugin.Features)
}
public List<PluginInfo> GetAllPluginInfo()
{
return new List<PluginInfo>(_plugins);
}
public List<T> GetPluginsOfType<T>() where T : PluginImplementation
{
return _plugins.Where(p => p.IsEnabled && p.Plugin is T).Select(p => (T) p.Plugin).ToList();
}
public PluginImplementation GetPluginByAssembly(Assembly assembly)
{
return _plugins.FirstOrDefault(p => p.Assembly == assembly)?.Plugin;
}
public PluginImplementation GetPluginByDevice(IRGBDevice rgbDevice)
{
return GetPluginsOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
}
public PluginImplementation GetCallingPlugin()
{
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
foreach (StackFrame stackFrame in stackFrames)
{ {
Assembly assembly = stackFrame.GetMethod().DeclaringType.Assembly; if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName))
PluginImplementation pluginImplementation = GetPluginByAssembly(assembly); plugin.Entity.Features.Add(pluginFeature.Entity);
if (pluginImplementation != null)
return pluginImplementation;
} }
return null; _pluginRepository.SavePlugin(plugin.Entity);
} }
public void Dispose() private PluginFeatureEntity GetOrCreateFeatureEntity(PluginFeature feature)
{ {
UnloadPlugins(); return feature.Plugin.Entity.Features.FirstOrDefault(i => i.Type == feature.GetType().FullName) ??
new PluginFeatureEntity {IsEnabled = true, Type = feature.GetType().FullName};
} }
private void CopyBuiltInPlugin(FileInfo zipFileInfo, ZipArchive zipArchive) #endregion
{
DirectoryInfo pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins", Path.GetFileNameWithoutExtension(zipFileInfo.Name)));
bool createLockFile = File.Exists(Path.Combine(pluginDirectory.FullName, "artemis.lock"));
// Remove the old directory if it exists
if (Directory.Exists(pluginDirectory.FullName))
pluginDirectory.DeleteRecursively();
Directory.CreateDirectory(pluginDirectory.FullName);
zipArchive.ExtractToDirectory(pluginDirectory.FullName, true);
if (createLockFile)
File.Create(Path.Combine(pluginDirectory.FullName, "artemis.lock")).Close();
}
#region Events #region Events
@ -419,8 +501,10 @@ namespace Artemis.Core.Services
public event EventHandler<PluginEventArgs> PluginEnabled; public event EventHandler<PluginEventArgs> PluginEnabled;
public event EventHandler<PluginEventArgs> PluginDisabled; public event EventHandler<PluginEventArgs> PluginDisabled;
public event EventHandler<PluginImplementationEventArgs> PluginImplementationEnabled; public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabling;
public event EventHandler<PluginImplementationEventArgs> PluginImplementationDisabled; public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnabled;
public event EventHandler<PluginFeatureEventArgs> PluginFeatureDisabled;
public event EventHandler<PluginFeatureEventArgs> PluginFeatureEnableFailed;
protected virtual void OnCopyingBuildInPlugins() protected virtual void OnCopyingBuildInPlugins()
{ {
@ -457,14 +541,24 @@ namespace Artemis.Core.Services
PluginDisabled?.Invoke(this, e); PluginDisabled?.Invoke(this, e);
} }
protected virtual void OnPluginImplementationDisabled(PluginImplementationEventArgs e) protected virtual void OnPluginFeatureEnabling(PluginFeatureEventArgs e)
{ {
PluginImplementationDisabled?.Invoke(this, e); PluginFeatureEnabling?.Invoke(this, e);
} }
protected virtual void OnPluginImplementationEnabled(PluginImplementationEventArgs e) protected virtual void OnPluginFeatureEnabled(PluginFeatureEventArgs e)
{ {
PluginImplementationEnabled?.Invoke(this, e); PluginFeatureEnabled?.Invoke(this, e);
}
protected virtual void OnPluginFeatureDisabled(PluginFeatureEventArgs e)
{
PluginFeatureDisabled?.Invoke(this, e);
}
protected virtual void OnPluginFeatureEnableFailed(PluginFeatureEventArgs e)
{
PluginFeatureEnableFailed?.Invoke(this, e);
} }
#endregion #endregion

View File

@ -12,14 +12,14 @@ namespace Artemis.Core.Services
RegisterBuiltInConditionOperators(); RegisterBuiltInConditionOperators();
} }
public ConditionOperatorRegistration RegisterConditionOperator(PluginInfo pluginInfo, BaseConditionOperator conditionOperator) public ConditionOperatorRegistration RegisterConditionOperator(Plugin plugin, BaseConditionOperator conditionOperator)
{ {
if (pluginInfo == null) if (plugin == null)
throw new ArgumentNullException(nameof(pluginInfo)); throw new ArgumentNullException(nameof(plugin));
if (conditionOperator == null) if (conditionOperator == null)
throw new ArgumentNullException(nameof(conditionOperator)); throw new ArgumentNullException(nameof(conditionOperator));
conditionOperator.PluginInfo = pluginInfo; conditionOperator.Plugin = plugin;
return ConditionOperatorStore.Add(conditionOperator); return ConditionOperatorStore.Add(conditionOperator);
} }
@ -43,30 +43,30 @@ namespace Artemis.Core.Services
private void RegisterBuiltInConditionOperators() private void RegisterBuiltInConditionOperators()
{ {
// General usage for any type // General usage for any type
RegisterConditionOperator(Constants.CorePluginInfo, new EqualsConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new EqualsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new NotEqualConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new NotEqualConditionOperator());
// Numeric operators // Numeric operators
RegisterConditionOperator(Constants.CorePluginInfo, new NumberEqualsConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new NumberEqualsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new NumberNotEqualConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new NumberNotEqualConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new LessThanConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new GreaterThanConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanOrEqualConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new LessThanOrEqualConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanOrEqualConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new GreaterThanOrEqualConditionOperator());
// String operators // String operators
RegisterConditionOperator(Constants.CorePluginInfo, new StringEqualsConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringEqualsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotEqualConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringNotEqualConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringContainsConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringContainsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotContainsConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringNotContainsConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringStartsWithConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringStartsWithConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringEndsWithConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringEndsWithConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new StringMatchesRegexConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new StringMatchesRegexConditionOperator());
// Null checks, at the bottom // Null checks, at the bottom
// TODO: Implement a priority mechanism // TODO: Implement a priority mechanism
RegisterConditionOperator(Constants.CorePluginInfo, new NullConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new NullConditionOperator());
RegisterConditionOperator(Constants.CorePluginInfo, new NotNullConditionOperator()); RegisterConditionOperator(Constants.CorePlugin, new NotNullConditionOperator());
} }
} }
} }

View File

@ -12,14 +12,14 @@ namespace Artemis.Core.Services
RegisterBuiltInModifiers(); RegisterBuiltInModifiers();
} }
public DataBindingModifierTypeRegistration RegisterModifierType(PluginInfo pluginInfo, BaseDataBindingModifierType dataBindingModifierType) public DataBindingModifierTypeRegistration RegisterModifierType(Plugin plugin, BaseDataBindingModifierType dataBindingModifierType)
{ {
if (pluginInfo == null) if (plugin == null)
throw new ArgumentNullException(nameof(pluginInfo)); throw new ArgumentNullException(nameof(plugin));
if (dataBindingModifierType == null) if (dataBindingModifierType == null)
throw new ArgumentNullException(nameof(dataBindingModifierType)); throw new ArgumentNullException(nameof(dataBindingModifierType));
dataBindingModifierType.PluginInfo = pluginInfo; dataBindingModifierType.Plugin = plugin;
return DataBindingModifierTypeStore.Add(dataBindingModifierType); return DataBindingModifierTypeStore.Add(dataBindingModifierType);
} }
@ -43,41 +43,41 @@ namespace Artemis.Core.Services
private void RegisterBuiltInModifiers() private void RegisterBuiltInModifiers()
{ {
// Numbers - General // Numbers - General
RegisterModifierType(Constants.CorePluginInfo, new SumModifierType()); RegisterModifierType(Constants.CorePlugin, new SumModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SubtractModifierType()); RegisterModifierType(Constants.CorePlugin, new SubtractModifierType());
RegisterModifierType(Constants.CorePluginInfo, new MultiplicationModifierType()); RegisterModifierType(Constants.CorePlugin, new MultiplicationModifierType());
RegisterModifierType(Constants.CorePluginInfo, new DivideModifierType()); RegisterModifierType(Constants.CorePlugin, new DivideModifierType());
RegisterModifierType(Constants.CorePluginInfo, new PercentageOfModifierType()); RegisterModifierType(Constants.CorePlugin, new PercentageOfModifierType());
// Numbers - Advanced // Numbers - Advanced
RegisterModifierType(Constants.CorePluginInfo, new MaxModifierType()); RegisterModifierType(Constants.CorePlugin, new MaxModifierType());
RegisterModifierType(Constants.CorePluginInfo, new MinModifierType()); RegisterModifierType(Constants.CorePlugin, new MinModifierType());
RegisterModifierType(Constants.CorePluginInfo, new ModuloModifierType()); RegisterModifierType(Constants.CorePlugin, new ModuloModifierType());
RegisterModifierType(Constants.CorePluginInfo, new AbsoluteModifierType()); RegisterModifierType(Constants.CorePlugin, new AbsoluteModifierType());
RegisterModifierType(Constants.CorePluginInfo, new PowerModifierType()); RegisterModifierType(Constants.CorePlugin, new PowerModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SquareRootModifierType()); RegisterModifierType(Constants.CorePlugin, new SquareRootModifierType());
// Numbers - Rounding // Numbers - Rounding
RegisterModifierType(Constants.CorePluginInfo, new FloorModifierType()); RegisterModifierType(Constants.CorePlugin, new FloorModifierType());
RegisterModifierType(Constants.CorePluginInfo, new RoundModifierType()); RegisterModifierType(Constants.CorePlugin, new RoundModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CeilingModifierType()); RegisterModifierType(Constants.CorePlugin, new CeilingModifierType());
// Numbers - Trigonometric // Numbers - Trigonometric
RegisterModifierType(Constants.CorePluginInfo, new SineModifierType()); RegisterModifierType(Constants.CorePlugin, new SineModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CosineModifierType()); RegisterModifierType(Constants.CorePlugin, new CosineModifierType());
RegisterModifierType(Constants.CorePluginInfo, new TangentModifierType()); RegisterModifierType(Constants.CorePlugin, new TangentModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CotangentModifierType()); RegisterModifierType(Constants.CorePlugin, new CotangentModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SecantModifierType()); RegisterModifierType(Constants.CorePlugin, new SecantModifierType());
RegisterModifierType(Constants.CorePluginInfo, new CosecantModifierType()); RegisterModifierType(Constants.CorePlugin, new CosecantModifierType());
// Colors // Colors
RegisterModifierType(Constants.CorePluginInfo, new SKColorSumModifierType()); RegisterModifierType(Constants.CorePlugin, new SKColorSumModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorSaturateModifierType()); RegisterModifierType(Constants.CorePlugin, new SKColorSaturateModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorDesaturateModifierType()); RegisterModifierType(Constants.CorePlugin, new SKColorDesaturateModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorBrightenModifierType()); RegisterModifierType(Constants.CorePlugin, new SKColorBrightenModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorDarkenModifierType()); RegisterModifierType(Constants.CorePlugin, new SKColorDarkenModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorRotateHueModifierType()); RegisterModifierType(Constants.CorePlugin, new SKColorRotateHueModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorInvertModifierType()); RegisterModifierType(Constants.CorePlugin, new SKColorInvertModifierType());
} }
} }
} }

View File

@ -11,15 +11,16 @@ namespace Artemis.Core.Services
public DataModelService(IPluginManagementService pluginManagementService) public DataModelService(IPluginManagementService pluginManagementService)
{ {
// Add data models of already loaded plugins // Add data models of already loaded plugins
foreach (Module module in pluginManagementService.GetPluginsOfType<Module>().Where(p => p.IsEnabled)) foreach (Module module in pluginManagementService.GetFeaturesOfType<Module>().Where(p => p.IsEnabled))
AddModuleDataModel(module); AddModuleDataModel(module);
foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled)) foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
AddDataModelExpansionDataModel(dataModelExpansion); AddDataModelExpansionDataModel(dataModelExpansion);
// Add data models of new plugins when they get enabled // Add data models of new plugins when they get enabled
pluginManagementService.PluginEnabled += PluginServiceOnPluginEnabled; pluginManagementService.PluginFeatureEnabled += OnPluginFeatureEnabled;
} }
public DataModelRegistration RegisterDataModel(DataModel dataModel) public DataModelRegistration RegisterDataModel(DataModel dataModel)
{ {
if (dataModel == null) if (dataModel == null)
@ -44,21 +45,16 @@ namespace Artemis.Core.Services
return (T) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel; return (T) DataModelStore.GetAll().FirstOrDefault(d => d.DataModel is T)?.DataModel;
} }
public DataModel GetPluginDataModel(PluginImplementation pluginImplementation) public DataModel? GetPluginDataModel(PluginFeature pluginFeature)
{ {
return DataModelStore.Get(pluginImplementation.PluginInfo.Guid)?.DataModel; return DataModelStore.Get(pluginFeature.Id)?.DataModel;
} }
public DataModel GetPluginDataModel(Guid pluginGuid) private void OnPluginFeatureEnabled(object? sender, PluginFeatureEventArgs e)
{ {
return DataModelStore.Get(pluginGuid)?.DataModel; if (e.PluginFeature is Module module)
}
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Plugin is Module module)
AddModuleDataModel(module); AddModuleDataModel(module);
else if (e.PluginInfo.Plugin is BaseDataModelExpansion dataModelExpansion) else if (e.PluginFeature is BaseDataModelExpansion dataModelExpansion)
AddDataModelExpansionDataModel(dataModelExpansion); AddDataModelExpansionDataModel(dataModelExpansion);
} }
@ -68,7 +64,7 @@ namespace Artemis.Core.Services
return; return;
if (module.InternalDataModel.DataModelDescription == null) if (module.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginException(module.PluginInfo, "Module overrides GetDataModelDescription but returned null"); throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null");
module.InternalDataModel.IsExpansion = module.InternalExpandsMainDataModel; module.InternalDataModel.IsExpansion = module.InternalExpandsMainDataModel;
RegisterDataModel(module.InternalDataModel); RegisterDataModel(module.InternalDataModel);
@ -77,7 +73,7 @@ namespace Artemis.Core.Services
private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion) private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion)
{ {
if (dataModelExpansion.InternalDataModel.DataModelDescription == null) if (dataModelExpansion.InternalDataModel.DataModelDescription == null)
throw new ArtemisPluginException(dataModelExpansion.PluginInfo, "Data model expansion overrides GetDataModelDescription but returned null"); throw new ArtemisPluginFeatureException(dataModelExpansion, "Data model expansion overrides GetDataModelDescription but returned null");
dataModelExpansion.InternalDataModel.IsExpansion = true; dataModelExpansion.InternalDataModel.IsExpansion = true;
RegisterDataModel(dataModelExpansion.InternalDataModel); RegisterDataModel(dataModelExpansion.InternalDataModel);

View File

@ -12,9 +12,9 @@ namespace Artemis.Core.Services
/// <summary> /// <summary>
/// Registers a new condition operator for use in layer conditions /// Registers a new condition operator for use in layer conditions
/// </summary> /// </summary>
/// <param name="pluginInfo">The PluginInfo of the plugin this condition operator belongs to</param> /// <param name="plugin">The plugin this condition operator belongs to</param>
/// <param name="conditionOperator">The condition operator to register</param> /// <param name="conditionOperator">The condition operator to register</param>
ConditionOperatorRegistration RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] BaseConditionOperator conditionOperator); ConditionOperatorRegistration RegisterConditionOperator([NotNull] Plugin plugin, [NotNull] BaseConditionOperator conditionOperator);
/// <summary> /// <summary>
/// Removes a condition operator so it is no longer available for use in layer conditions /// Removes a condition operator so it is no longer available for use in layer conditions

View File

@ -12,9 +12,9 @@ namespace Artemis.Core.Services
/// <summary> /// <summary>
/// Registers a new modifier type for use in data bindings /// Registers a new modifier type for use in data bindings
/// </summary> /// </summary>
/// <param name="pluginInfo">The PluginInfo of the plugin this modifier type belongs to</param> /// <param name="plugin">The plugin this modifier type belongs to</param>
/// <param name="dataBindingModifierType">The modifier type to register</param> /// <param name="dataBindingModifierType">The modifier type to register</param>
DataBindingModifierTypeRegistration RegisterModifierType([NotNull] PluginInfo pluginInfo, [NotNull] BaseDataBindingModifierType dataBindingModifierType); DataBindingModifierTypeRegistration RegisterModifierType([NotNull] Plugin plugin, [NotNull] BaseDataBindingModifierType dataBindingModifierType);
/// <summary> /// <summary>
/// Removes a modifier type so it is no longer available for use in data bindings /// Removes a modifier type so it is no longer available for use in data bindings

View File

@ -34,13 +34,7 @@ namespace Artemis.Core.Services
/// <summary> /// <summary>
/// If found, returns the data model of the provided plugin /// If found, returns the data model of the provided plugin
/// </summary> /// </summary>
/// <param name="pluginImplementation">The plugin to find the data model of</param> /// <param name="pluginFeature">The plugin to find the data model of</param>
DataModel GetPluginDataModel(PluginImplementation pluginImplementation); DataModel? GetPluginDataModel(PluginFeature pluginFeature);
/// <summary>
/// If found, returns the data model of the provided plugin GUID
/// </summary>
/// <param name="pluginGuid">The GUID of the plugin to find the data model of</param>
DataModel GetPluginDataModel(Guid pluginGuid);
} }
} }

View File

@ -38,11 +38,11 @@ namespace Artemis.Core.Services
{ {
PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference PluginSetting<LayerBrushReference> defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference
{ {
BrushPluginGuid = Guid.Parse("92a9d6ba-6f7a-4937-94d5-c1d715b4141a"), LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba",
BrushType = "ColorBrush" BrushType = "ColorBrush"
}); });
return LayerBrushStore.Get(defaultReference.Value.BrushPluginGuid, defaultReference.Value.BrushType)?.LayerBrushDescriptor; return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor;
} }
} }
} }

View File

@ -9,7 +9,7 @@ namespace Artemis.Core.Services
internal SettingsService(IPluginRepository pluginRepository) internal SettingsService(IPluginRepository pluginRepository)
{ {
_pluginSettings = new PluginSettings(Constants.CorePluginInfo, pluginRepository); _pluginSettings = new PluginSettings(Constants.CorePlugin, pluginRepository);
} }
public PluginSetting<T> GetSetting<T>(string name, T defaultValue = default) public PluginSetting<T> GetSetting<T>(string name, T defaultValue = default)

View File

@ -38,13 +38,13 @@ namespace Artemis.Core.Services
public List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module) public List<ProfileDescriptor> GetProfileDescriptors(ProfileModule module)
{ {
List<ProfileEntity> profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
return profileEntities.Select(e => new ProfileDescriptor(module, e)).ToList(); return profileEntities.Select(e => new ProfileDescriptor(module, e)).ToList();
} }
public ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name) public ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name)
{ {
ProfileEntity profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, PluginGuid = module.PluginInfo.Guid}; ProfileEntity profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, ModuleId = module.Id};
_profileRepository.Add(profileEntity); _profileRepository.Add(profileEntity);
return new ProfileDescriptor(module, profileEntity); return new ProfileDescriptor(module, profileEntity);
@ -258,7 +258,7 @@ namespace Artemis.Core.Services
public ProfileDescriptor GetLastActiveProfile(ProfileModule module) public ProfileDescriptor GetLastActiveProfile(ProfileModule module)
{ {
List<ProfileEntity> moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); List<ProfileEntity> moduleProfiles = _profileRepository.GetByModuleId(module.Id);
if (!moduleProfiles.Any()) if (!moduleProfiles.Any())
return CreateProfileDescriptor(module, "Default"); return CreateProfileDescriptor(module, "Default");
@ -271,7 +271,7 @@ namespace Artemis.Core.Services
if (module.ActiveProfile == null) if (module.ActiveProfile == null)
return; return;
List<ProfileEntity> profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); List<ProfileEntity> profileEntities = _profileRepository.GetByModuleId(module.Id);
foreach (ProfileEntity profileEntity in profileEntities) foreach (ProfileEntity profileEntity in profileEntities)
{ {
profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id; profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id;
@ -285,7 +285,7 @@ namespace Artemis.Core.Services
/// <param name="surface"></param> /// <param name="surface"></param>
private void ActiveProfilesPopulateLeds(ArtemisSurface surface) private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
{ {
List<ProfileModule> profileModules = _pluginManagementService.GetPluginsOfType<ProfileModule>(); List<ProfileModule> profileModules = _pluginManagementService.GetFeaturesOfType<ProfileModule>();
foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList()) foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList())
profileModule.ActiveProfile.PopulateLeds(surface); profileModule.ActiveProfile.PopulateLeds(surface);
} }

View File

@ -44,8 +44,8 @@ namespace Artemis.Core.Services
// Add all current devices // Add all current devices
foreach (IRGBDevice rgbDevice in _rgbService.LoadedDevices) foreach (IRGBDevice rgbDevice in _rgbService.LoadedDevices)
{ {
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice); PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
configuration.Devices.Add(new ArtemisDevice(rgbDevice, pluginImplementation, configuration)); configuration.Devices.Add(new ArtemisDevice(rgbDevice, pluginFeature, configuration));
} }
lock (_surfaceConfigurations) lock (_surfaceConfigurations)
@ -136,8 +136,8 @@ namespace Artemis.Core.Services
IRGBDevice device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier); IRGBDevice device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier);
if (device != null) if (device != null)
{ {
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(device); PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(device);
surfaceConfiguration.Devices.Add(new ArtemisDevice(device, pluginImplementation, surfaceConfiguration, position)); surfaceConfiguration.Devices.Add(new ArtemisDevice(device, pluginFeature, surfaceConfiguration, position));
} }
} }
@ -178,8 +178,8 @@ namespace Artemis.Core.Services
DeviceEntity existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier); DeviceEntity existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier);
if (existingDeviceConfig != null) if (existingDeviceConfig != null)
{ {
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice); PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginImplementation, surface, existingDeviceConfig); device = new ArtemisDevice(rgbDevice, pluginFeature, surface, existingDeviceConfig);
} }
// Fall back on creating a new device // Fall back on creating a new device
else else
@ -189,8 +189,8 @@ namespace Artemis.Core.Services
rgbDevice.DeviceInfo, rgbDevice.DeviceInfo,
deviceIdentifier deviceIdentifier
); );
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice); PluginFeature pluginFeature = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginImplementation, surface); device = new ArtemisDevice(rgbDevice, pluginFeature, surface);
} }
surface.Devices.Add(device); surface.Devices.Add(device);

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.ConditionOperator == conditionOperator)) if (Registrations.Any(r => r.ConditionOperator == conditionOperator))
throw new ArtemisCoreException($"Condition operator store store already contains operator '{conditionOperator.Description}'"); throw new ArtemisCoreException($"Condition operator store store already contains operator '{conditionOperator.Description}'");
registration = new ConditionOperatorRegistration(conditionOperator, conditionOperator.PluginInfo.Plugin) {IsInStore = true}; registration = new ConditionOperatorRegistration(conditionOperator, conditionOperator.Plugin) {IsInStore = true};
Registrations.Add(registration); Registrations.Add(registration);
} }
@ -42,7 +42,7 @@ namespace Artemis.Core
{ {
lock (Registrations) lock (Registrations)
{ {
return Registrations.FirstOrDefault(r => r.PluginImplementation.PluginInfo.Guid == pluginGuid && r.ConditionOperator.GetType().Name == type); return Registrations.FirstOrDefault(r => r.Plugin.Guid == pluginGuid && r.ConditionOperator.GetType().Name == type);
} }
} }

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.DataBindingModifierType == modifierType)) if (Registrations.Any(r => r.DataBindingModifierType == modifierType))
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{modifierType.Name}'"); throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{modifierType.Name}'");
typeRegistration = new DataBindingModifierTypeRegistration(modifierType, modifierType.PluginInfo.Plugin) { IsInStore = true }; typeRegistration = new DataBindingModifierTypeRegistration(modifierType, modifierType.Plugin) { IsInStore = true };
Registrations.Add(typeRegistration); Registrations.Add(typeRegistration);
} }
@ -42,7 +42,7 @@ namespace Artemis.Core
{ {
lock (Registrations) lock (Registrations)
{ {
return Registrations.FirstOrDefault(r => r.PluginImplementation.PluginInfo.Guid == pluginGuid && r.DataBindingModifierType.GetType().Name == type); return Registrations.FirstOrDefault(r => r.Plugin.Guid == pluginGuid && r.DataBindingModifierType.GetType().Name == type);
} }
} }

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.DataModel == dataModel)) if (Registrations.Any(r => r.DataModel == dataModel))
throw new ArtemisCoreException($"Data model store already contains data model '{dataModel.DataModelDescription}'"); throw new ArtemisCoreException($"Data model store already contains data model '{dataModel.DataModelDescription}'");
registration = new DataModelRegistration(dataModel, dataModel.Implementation.Instance) {IsInStore = true}; registration = new DataModelRegistration(dataModel, dataModel.Feature) {IsInStore = true};
Registrations.Add(registration); Registrations.Add(registration);
} }
@ -47,11 +47,11 @@ namespace Artemis.Core
} }
} }
public static DataModelRegistration Get(Guid pluginGuid) public static DataModelRegistration Get(string id)
{ {
lock (Registrations) lock (Registrations)
{ {
return Registrations.FirstOrDefault(d => d.PluginImplementation.PluginInfo.Guid == pluginGuid); return Registrations.FirstOrDefault(d => d.PluginFeature.Id == id);
} }
} }

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.LayerBrushDescriptor == descriptor)) if (Registrations.Any(r => r.LayerBrushDescriptor == descriptor))
throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'"); throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'");
registration = new LayerBrushRegistration(descriptor, descriptor.LayerBrushProvider.PluginInfo.Plugin) {IsInStore = true}; registration = new LayerBrushRegistration(descriptor, descriptor.Provider) {IsInStore = true};
Registrations.Add(registration); Registrations.Add(registration);
} }
@ -47,11 +47,11 @@ namespace Artemis.Core
} }
} }
public static LayerBrushRegistration Get(Guid pluginGuid, string typeName) public static LayerBrushRegistration? Get(string id, string typeName)
{ {
lock (Registrations) lock (Registrations)
{ {
return Registrations.FirstOrDefault(d => d.PluginImplementation.PluginInfo.Guid == pluginGuid && return Registrations.FirstOrDefault(d => d.PluginFeature.Id == id &&
d.LayerBrushDescriptor.LayerBrushType.Name == typeName); d.LayerBrushDescriptor.LayerBrushType.Name == typeName);
} }
} }

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.LayerEffectDescriptor == descriptor)) if (Registrations.Any(r => r.LayerEffectDescriptor == descriptor))
throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'"); throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'");
registration = new LayerEffectRegistration(descriptor, descriptor.LayerEffectProvider.PluginInfo.Plugin) { IsInStore = true }; registration = new LayerEffectRegistration(descriptor, descriptor.Provider) { IsInStore = true };
Registrations.Add(registration); Registrations.Add(registration);
} }
@ -47,11 +47,11 @@ namespace Artemis.Core
} }
} }
public static LayerEffectRegistration Get(Guid pluginGuid, string typeName) public static LayerEffectRegistration? Get(string providerId, string typeName)
{ {
lock (Registrations) lock (Registrations)
{ {
return Registrations.FirstOrDefault(d => d.PluginImplementation.PluginInfo.Guid == pluginGuid && d.LayerEffectDescriptor.LayerEffectType.Name == typeName); return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType.Name == typeName);
} }
} }

View File

@ -7,12 +7,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class ConditionOperatorRegistration public class ConditionOperatorRegistration
{ {
internal ConditionOperatorRegistration(BaseConditionOperator conditionOperator, PluginImplementation pluginImplementation) internal ConditionOperatorRegistration(BaseConditionOperator conditionOperator, Plugin plugin)
{ {
ConditionOperator = conditionOperator; ConditionOperator = conditionOperator;
PluginImplementation = pluginImplementation; Plugin = plugin;
PluginImplementation.Disabled += OnDisabled; Plugin.Disabled += OnDisabled;
} }
/// <summary> /// <summary>
@ -23,7 +23,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the plugin the condition operator is associated with /// Gets the plugin the condition operator is associated with
/// </summary> /// </summary>
public PluginImplementation PluginImplementation { get; } public Plugin Plugin { get; }
/// <summary> /// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store /// Gets a boolean indicating whether the registration is in the internal Core store
@ -32,7 +32,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object sender, EventArgs e)
{ {
PluginImplementation.Disabled -= OnDisabled; Plugin.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)
ConditionOperatorStore.Remove(this); ConditionOperatorStore.Remove(this);
} }

View File

@ -7,12 +7,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class DataBindingModifierTypeRegistration public class DataBindingModifierTypeRegistration
{ {
internal DataBindingModifierTypeRegistration(BaseDataBindingModifierType dataBindingModifierType, PluginImplementation pluginImplementation) internal DataBindingModifierTypeRegistration(BaseDataBindingModifierType dataBindingModifierType, Plugin plugin)
{ {
DataBindingModifierType = dataBindingModifierType; DataBindingModifierType = dataBindingModifierType;
PluginImplementation = pluginImplementation; Plugin = plugin;
PluginImplementation.Disabled += OnDisabled; Plugin.Disabled += OnDisabled;
} }
/// <summary> /// <summary>
@ -23,7 +23,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the plugin the data binding modifier is associated with /// Gets the plugin the data binding modifier is associated with
/// </summary> /// </summary>
public PluginImplementation PluginImplementation { get; } public Plugin Plugin { get; }
/// <summary> /// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store /// Gets a boolean indicating whether the registration is in the internal Core store
@ -32,7 +32,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object sender, EventArgs e)
{ {
PluginImplementation.Disabled -= OnDisabled; Plugin.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)
DataBindingModifierTypeStore.Remove(this); DataBindingModifierTypeStore.Remove(this);
} }

View File

@ -8,12 +8,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class DataModelRegistration public class DataModelRegistration
{ {
internal DataModelRegistration(DataModel dataModel, PluginImplementation pluginImplementation) internal DataModelRegistration(DataModel dataModel, PluginFeature pluginFeature)
{ {
DataModel = dataModel; DataModel = dataModel;
PluginImplementation = pluginImplementation; PluginFeature = pluginFeature;
PluginImplementation.Disabled += OnDisabled; PluginFeature.Disabled += OnDisabled;
} }
/// <summary> /// <summary>
@ -24,7 +24,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the plugin the data model is associated with /// Gets the plugin the data model is associated with
/// </summary> /// </summary>
public PluginImplementation PluginImplementation { get; } public PluginFeature PluginFeature { get; }
/// <summary> /// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store /// Gets a boolean indicating whether the registration is in the internal Core store
@ -33,7 +33,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object sender, EventArgs e)
{ {
PluginImplementation.Disabled -= OnDisabled; PluginFeature.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)
DataModelStore.Remove(this); DataModelStore.Remove(this);
} }

View File

@ -8,12 +8,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class LayerBrushRegistration public class LayerBrushRegistration
{ {
internal LayerBrushRegistration(LayerBrushDescriptor descriptor, PluginImplementation pluginImplementation) internal LayerBrushRegistration(LayerBrushDescriptor descriptor, PluginFeature pluginFeature)
{ {
LayerBrushDescriptor = descriptor; LayerBrushDescriptor = descriptor;
PluginImplementation = pluginImplementation; PluginFeature = pluginFeature;
PluginImplementation.Disabled += OnDisabled; PluginFeature.Disabled += OnDisabled;
} }
/// <summary> /// <summary>
@ -24,7 +24,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the plugin the layer brush is associated with /// Gets the plugin the layer brush is associated with
/// </summary> /// </summary>
public PluginImplementation PluginImplementation { get; } public PluginFeature PluginFeature { get; }
/// <summary> /// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store /// Gets a boolean indicating whether the registration is in the internal Core store
@ -33,7 +33,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object sender, EventArgs e)
{ {
PluginImplementation.Disabled -= OnDisabled; PluginFeature.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)
LayerBrushStore.Remove(this); LayerBrushStore.Remove(this);
} }

View File

@ -8,12 +8,12 @@ namespace Artemis.Core
/// </summary> /// </summary>
public class LayerEffectRegistration public class LayerEffectRegistration
{ {
internal LayerEffectRegistration(LayerEffectDescriptor descriptor, PluginImplementation pluginImplementation) internal LayerEffectRegistration(LayerEffectDescriptor descriptor, PluginFeature pluginFeature)
{ {
LayerEffectDescriptor = descriptor; LayerEffectDescriptor = descriptor;
PluginImplementation = pluginImplementation; PluginFeature = pluginFeature;
PluginImplementation.Disabled += OnDisabled; PluginFeature.Disabled += OnDisabled;
} }
/// <summary> /// <summary>
@ -24,7 +24,7 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the plugin the layer effect is associated with /// Gets the plugin the layer effect is associated with
/// </summary> /// </summary>
public PluginImplementation PluginImplementation { get; } public PluginFeature PluginFeature { get; }
/// <summary> /// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store /// Gets a boolean indicating whether the registration is in the internal Core store
@ -33,7 +33,7 @@ namespace Artemis.Core
private void OnDisabled(object sender, EventArgs e) private void OnDisabled(object sender, EventArgs e)
{ {
PluginImplementation.Disabled -= OnDisabled; PluginFeature.Disabled -= OnDisabled;
if (IsInStore) if (IsInStore)
LayerEffectStore.Remove(this); LayerEffectStore.Remove(this);
} }

View File

@ -3,13 +3,13 @@
namespace Artemis.Core namespace Artemis.Core
{ {
/// <summary> /// <summary>
/// An empty plugin used by <see cref="Constants.CorePluginInfo"/> /// An empty data model plugin feature used by <see cref="Constants.CorePlugin" />
/// </summary> /// </summary>
internal class CorePluginImplementation : PluginImplementation internal class CorePluginFeature : DataModelPluginFeature
{ {
public CorePluginImplementation() public CorePluginFeature()
{ {
Constants.CorePluginInfo.Plugin = this; Constants.CorePlugin.AddFeature(this);
IsEnabled = true; IsEnabled = true;
} }

View File

@ -1,6 +1,6 @@
using System.Diagnostics; using System;
using System.Diagnostics;
using System.Windows; using System.Windows;
using Stylet;
namespace Artemis.Core namespace Artemis.Core
{ {
@ -37,8 +37,8 @@ namespace Artemis.Core
if (!Debugger.IsAttached) if (!Debugger.IsAttached)
Process.Start(info); Process.Start(info);
// Also attempt a graceful shutdown on the UI thread // Request a graceful shutdown, whatever UI we're running can pick this up
Execute.OnUIThread(() => Application.Current.Shutdown()); OnShutdownRequested();
} }
/// <summary> /// <summary>
@ -64,5 +64,18 @@ namespace Artemis.Core
{ {
return Process.GetCurrentProcess().MainModule.FileName; return Process.GetCurrentProcess().MainModule.FileName;
} }
#region Events
public static event EventHandler ShutdownRequested;
private static void OnShutdownRequested()
{
ShutdownRequested?.Invoke(null, EventArgs.Empty);
}
#endregion
} }
} }

View File

@ -121,15 +121,6 @@
"System.Memory": "4.5.3" "System.Memory": "4.5.3"
} }
}, },
"Stylet": {
"type": "Direct",
"requested": "[1.3.4, )",
"resolved": "1.3.4",
"contentHash": "bCEdA+AIi+TM9SQQGLYMsFRIfzZcDUDg2Mznyr72kOkcC/cdBj01/jel4/v2aoKwbFcxVjiqmpgnbsFgMEZ4zQ==",
"dependencies": {
"System.Drawing.Common": "4.6.0"
}
},
"System.Buffers": { "System.Buffers": {
"type": "Direct", "type": "Direct",
"requested": "[4.5.0, )", "requested": "[4.5.0, )",
@ -192,8 +183,8 @@
}, },
"Microsoft.NETCore.Platforms": { "Microsoft.NETCore.Platforms": {
"type": "Transitive", "type": "Transitive",
"resolved": "3.0.0", "resolved": "1.1.0",
"contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
}, },
"Microsoft.NETCore.Targets": { "Microsoft.NETCore.Targets": {
"type": "Transitive", "type": "Transitive",
@ -210,14 +201,6 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "4.6.0",
"contentHash": "Edg+pFW5C8WJb680Za2kTV8TqUi6Ahl/WldRVoOVJ23UQLpDHFspa+umgFjkWZw24ETsU99Cg+ErZz683M4chg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "3.0.0"
}
},
"NETStandard.Library": { "NETStandard.Library": {
"type": "Transitive", "type": "Transitive",
"resolved": "1.6.1", "resolved": "1.6.1",
@ -557,15 +540,6 @@
"System.Runtime": "4.3.0" "System.Runtime": "4.3.0"
} }
}, },
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "4.6.0",
"contentHash": "2A3spjjoPZnvpVh/sDTzd+0H8ZqTdr+hH/6obB8MMfG81EJ85PmxCKDBxhBVQiA25PliKAZ1sKogDcq9mSnFEA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "3.0.0",
"Microsoft.Win32.SystemEvents": "4.6.0"
}
},
"System.Dynamic.Runtime": { "System.Dynamic.Runtime": {
"type": "Transitive", "type": "Transitive",
"resolved": "4.3.0", "resolved": "4.3.0",

View File

@ -10,7 +10,7 @@ namespace Artemis.Storage.Entities.Module
} }
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid PluginGuid { get; set; } public string ModuleId { get; set; }
public int PriorityCategory { get; set; } public int PriorityCategory { get; set; }
public int Priority { get; set; } public int Priority { get; set; }
} }

View File

@ -8,16 +8,21 @@ namespace Artemis.Storage.Entities.Plugins
/// </summary> /// </summary>
public class PluginEntity public class PluginEntity
{ {
public PluginEntity()
{
Features = new List<PluginFeatureEntity>();
}
public Guid Id { get; set; } public Guid Id { get; set; }
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
public List<PluginImplementationEntity> Implementations { get; set; } public List<PluginFeatureEntity> Features { get; set; }
} }
/// <summary> /// <summary>
/// Represents the configuration of a plugin implementation, each implementation has one configuration /// Represents the configuration of a plugin feature, each feature has one configuration
/// </summary> /// </summary>
public class PluginImplementationEntity public class PluginFeatureEntity
{ {
public string Type { get; set; } public string Type { get; set; }
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }

View File

@ -5,7 +5,7 @@ namespace Artemis.Storage.Entities.Profile
public class DataModelPathEntity public class DataModelPathEntity
{ {
public string Path { get; set; } public string Path { get; set; }
public Guid? DataModelGuid { get; set; } public string DataModelId { get; set; }
public PathWrapperType WrapperType { get; set; } public PathWrapperType WrapperType { get; set; }
} }

View File

@ -5,7 +5,7 @@ namespace Artemis.Storage.Entities.Profile
public class LayerEffectEntity public class LayerEffectEntity
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid PluginGuid { get; set; } public string ProviderId { get; set; }
public string EffectType { get; set; } public string EffectType { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; }

View File

@ -13,7 +13,7 @@ namespace Artemis.Storage.Entities.Profile
} }
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid PluginGuid { get; set; } public string ModuleId { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using Artemis.Storage.Entities.Profile.DataBindings; using Artemis.Storage.Entities.Profile.DataBindings;
namespace Artemis.Storage.Entities.Profile namespace Artemis.Storage.Entities.Profile
@ -12,7 +11,7 @@ namespace Artemis.Storage.Entities.Profile
DataBindingEntities = new List<DataBindingEntity>(); DataBindingEntities = new List<DataBindingEntity>();
} }
public Guid PluginGuid { get; set; } public string FeatureId { get; set; }
public string Path { get; set; } public string Path { get; set; }
public string Value { get; set; } public string Value { get; set; }

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using Artemis.Storage.Entities.Module; using Artemis.Storage.Entities.Module;
namespace Artemis.Storage.Repositories.Interfaces namespace Artemis.Storage.Repositories.Interfaces
@ -7,7 +6,7 @@ namespace Artemis.Storage.Repositories.Interfaces
public interface IModuleRepository : IRepository public interface IModuleRepository : IRepository
{ {
void Add(ModuleSettingsEntity moduleSettingsEntity); void Add(ModuleSettingsEntity moduleSettingsEntity);
ModuleSettingsEntity GetByPluginGuid(Guid guid); ModuleSettingsEntity GetByModuleId(string moduleId);
List<ModuleSettingsEntity> GetAll(); List<ModuleSettingsEntity> GetAll();
List<ModuleSettingsEntity> GetByCategory(int category); List<ModuleSettingsEntity> GetByCategory(int category);
void Save(ModuleSettingsEntity moduleSettingsEntity); void Save(ModuleSettingsEntity moduleSettingsEntity);

View File

@ -10,7 +10,7 @@ namespace Artemis.Storage.Repositories.Interfaces
void Remove(ProfileEntity profileEntity); void Remove(ProfileEntity profileEntity);
List<ProfileEntity> GetAll(); List<ProfileEntity> GetAll();
ProfileEntity Get(Guid id); ProfileEntity Get(Guid id);
List<ProfileEntity> GetByPluginGuid(Guid pluginGuid); List<ProfileEntity> GetByModuleId(string moduleId);
void Save(ProfileEntity profileEntity); void Save(ProfileEntity profileEntity);
} }
} }

View File

@ -13,7 +13,7 @@ namespace Artemis.Storage.Repositories
internal ModuleRepository(LiteRepository repository) internal ModuleRepository(LiteRepository repository)
{ {
_repository = repository; _repository = repository;
_repository.Database.GetCollection<ModuleSettingsEntity>().EnsureIndex(s => s.PluginGuid, true); _repository.Database.GetCollection<ModuleSettingsEntity>().EnsureIndex(s => s.ModuleId, true);
} }
public void Add(ModuleSettingsEntity moduleSettingsEntity) public void Add(ModuleSettingsEntity moduleSettingsEntity)
@ -21,9 +21,9 @@ namespace Artemis.Storage.Repositories
_repository.Insert(moduleSettingsEntity); _repository.Insert(moduleSettingsEntity);
} }
public ModuleSettingsEntity GetByPluginGuid(Guid guid) public ModuleSettingsEntity GetByModuleId(string moduleId)
{ {
return _repository.FirstOrDefault<ModuleSettingsEntity>(s => s.PluginGuid == guid); return _repository.FirstOrDefault<ModuleSettingsEntity>(s => s.ModuleId == moduleId);
} }
public List<ModuleSettingsEntity> GetAll() public List<ModuleSettingsEntity> GetAll()

View File

@ -36,12 +36,12 @@ namespace Artemis.Storage.Repositories
return _repository.FirstOrDefault<ProfileEntity>(p => p.Id == id); return _repository.FirstOrDefault<ProfileEntity>(p => p.Id == id);
} }
public List<ProfileEntity> GetByPluginGuid(Guid pluginGuid) public List<ProfileEntity> GetByModuleId(string moduleId)
{ {
return _repository.Query<ProfileEntity>() return _repository.Query<ProfileEntity>()
.Include(p => p.Folders) .Include(p => p.Folders)
.Include(p => p.Layers) .Include(p => p.Layers)
.Where(s => s.PluginGuid == pluginGuid) .Where(s => s.ModuleId == moduleId)
.ToList(); .ToList();
} }

View File

@ -8,6 +8,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plugins/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=propertyinput/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=propertyinput/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdatamodelvisualization/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdatamodelvisualization/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdialog/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cdialog/@EntryIndexedValue">True</s:Boolean>

View File

@ -11,22 +11,22 @@ namespace Artemis.UI.Shared
public DataModelVisualizationRegistration(IDataModelUIService dataModelUIService, public DataModelVisualizationRegistration(IDataModelUIService dataModelUIService,
RegistrationType registrationType, RegistrationType registrationType,
PluginInfo pluginInfo, Plugin plugin,
Type supportedType, Type supportedType,
Type viewModelType) Type viewModelType)
{ {
_dataModelUIService = dataModelUIService; _dataModelUIService = dataModelUIService;
RegistrationType = registrationType; RegistrationType = registrationType;
PluginInfo = pluginInfo; Plugin = plugin;
SupportedType = supportedType; SupportedType = supportedType;
ViewModelType = viewModelType; ViewModelType = viewModelType;
if (PluginInfo != Constants.CorePluginInfo) if (Plugin != Constants.CorePlugin)
PluginInfo.Plugin.Disabled += InstanceOnDisabled; Plugin.Disabled += InstanceOnDisabled;
} }
public RegistrationType RegistrationType { get; } public RegistrationType RegistrationType { get; }
public PluginInfo PluginInfo { get; } public Plugin Plugin { get; }
public Type SupportedType { get; } public Type SupportedType { get; }
public Type ViewModelType { get; } public Type ViewModelType { get; }
@ -34,8 +34,8 @@ namespace Artemis.UI.Shared
internal void Unsubscribe() internal void Unsubscribe()
{ {
if (PluginInfo != Constants.CorePluginInfo) if (Plugin != Constants.CorePlugin)
PluginInfo.Plugin.Disabled -= InstanceOnDisabled; Plugin.Disabled -= InstanceOnDisabled;
} }
private void InstanceOnDisabled(object sender, EventArgs e) private void InstanceOnDisabled(object sender, EventArgs e)

View File

@ -1,6 +1,7 @@
using Stylet; using Artemis.Core.LayerBrushes;
using Stylet;
namespace Artemis.Core.LayerBrushes namespace Artemis.UI.Shared.LayerBrushes
{ {
/// <summary> /// <summary>
/// Represents a view model for a brush configuration window /// Represents a view model for a brush configuration window

View File

@ -1,6 +1,7 @@
using System; using System;
using Artemis.Core.LayerBrushes;
namespace Artemis.Core.LayerBrushes namespace Artemis.UI.Shared.LayerBrushes
{ {
/// <inheritdoc /> /// <inheritdoc />
public class LayerBrushConfigurationDialog<T> : LayerBrushConfigurationDialog where T : BrushConfigurationViewModel public class LayerBrushConfigurationDialog<T> : LayerBrushConfigurationDialog where T : BrushConfigurationViewModel
@ -12,7 +13,7 @@ namespace Artemis.Core.LayerBrushes
/// <summary> /// <summary>
/// Describes a UI tab for a layer brush /// Describes a UI tab for a layer brush
/// </summary> /// </summary>
public abstract class LayerBrushConfigurationDialog public abstract class LayerBrushConfigurationDialog : ILayerBrushConfigurationDialog
{ {
/// <summary> /// <summary>
/// The layer brush this dialog belongs to /// The layer brush this dialog belongs to

Some files were not shown because too many files have changed in this diff Show More