From fb3466e1028a47e2b1eb01d251f3abf29b10be6f Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Tue, 17 Nov 2020 22:50:38 +0100 Subject: [PATCH] Core - Nullable refactoring Core - Nullable refactoring Core - Nullable refactoring (finish) --- src/Artemis.Core/Constants.cs | 5 +- .../Extensions/ProcessExtensions.cs | 1 + src/Artemis.Core/Models/Profile/Folder.cs | 14 +--- src/Artemis.Core/Models/Profile/Layer.cs | 29 +++---- .../Profile/LayerProperties/LayerProperty.cs | 8 +- .../Models/Profile/LayerPropertyGroup.cs | 38 ++++----- .../Models/Profile/LayerShapes/LayerShape.cs | 2 +- src/Artemis.Core/Models/Profile/Profile.cs | 6 +- .../Models/Profile/ProfileElement.cs | 18 +++- .../Models/Profile/RenderProfileElement.cs | 15 ++-- src/Artemis.Core/Models/Profile/Renderer.cs | 4 +- .../Models/Surface/ArtemisDevice.cs | 8 +- src/Artemis.Core/Ninject/LoggerProvider.cs | 2 +- .../Attributes/DataModelProperty.cs | 12 +-- .../Plugins/DataModelExpansions/DataModel.cs | 14 +++- .../DataModelExpansions/DataModelExpansion.cs | 8 +- .../Plugins/DeviceProviders/DeviceProvider.cs | 2 +- .../LayerBrushes/Internal/BaseLayerBrush.cs | 14 +++- .../Internal/PropertiesLayerBrush.cs | 4 +- .../LayerBrushes/LayerBrushDescriptor.cs | 1 + .../Plugins/LayerBrushes/RgbNetLayerBrush.cs | 3 +- .../LayerEffects/Internal/BaseLayerEffect.cs | 19 +++-- .../Plugins/LayerEffects/LayerEffect.cs | 2 +- .../LayerEffects/LayerEffectDescriptor.cs | 18 ++-- .../ProcessActivationRequirement.cs | 6 +- src/Artemis.Core/Plugins/Modules/Module.cs | 8 +- src/Artemis.Core/Plugins/Modules/ModuleTab.cs | 13 +-- .../Plugins/Modules/ProfileModule.cs | 15 ++-- src/Artemis.Core/Plugins/Plugin.cs | 8 +- src/Artemis.Core/Plugins/PluginFeature.cs | 4 +- src/Artemis.Core/Plugins/PluginInfo.cs | 16 ++-- .../Plugins/Settings/PluginSetting.cs | 14 ++-- .../Plugins/TimedUpdateRegistration.cs | 6 +- src/Artemis.Core/Properties/Annotations.cs | 5 +- src/Artemis.Core/RGB.NET/BitmapBrush.cs | 8 +- src/Artemis.Core/Services/CoreService.cs | 28 +++---- .../Services/Interfaces/ICoreService.cs | 2 +- .../Services/Interfaces/IModuleService.cs | 4 +- .../Services/Interfaces/IRgbService.cs | 2 +- src/Artemis.Core/Services/ModuleService.cs | 4 +- .../Services/PluginManagementService.cs | 8 +- .../Registration/ConditionOperatorService.cs | 2 +- .../Registration/DataBindingService.cs | 2 +- .../Services/Registration/DataModelService.cs | 11 +-- .../Interfaces/IConditionOperatorService.cs | 2 +- .../Interfaces/IDataBindingService.cs | 2 +- .../Interfaces/IDataModelService.cs | 2 +- .../Registration/LayerBrushService.cs | 4 +- src/Artemis.Core/Services/RgbService.cs | 10 +-- src/Artemis.Core/Services/SettingsService.cs | 2 +- .../Services/Storage/ProfileService.cs | 82 ++++++++++--------- .../Services/Storage/SurfaceService.cs | 22 ++--- .../Stores/ConditionOperatorStore.cs | 7 +- src/Artemis.Core/Stores/DataModelStore.cs | 4 +- src/Artemis.Core/Stores/LayerBrushStore.cs | 4 +- src/Artemis.Core/Stores/LayerEffectStore.cs | 6 +- .../Utilities/CurrentProcessUtilities.cs | 6 +- .../Utilities/DeserializationLogger.cs | 6 +- .../Utilities/ExpressionUtilities.cs | 2 +- src/Artemis.Core/Utilities/IntroAnimation.cs | 14 ++-- .../Utilities/ReflectionUtilities.cs | 4 +- 61 files changed, 320 insertions(+), 272 deletions(-) diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs index 36f32cada..9a0ba95c4 100644 --- a/src/Artemis.Core/Constants.cs +++ b/src/Artemis.Core/Constants.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using Artemis.Storage.Entities.Plugins; namespace Artemis.Core { @@ -34,13 +35,13 @@ namespace Artemis.Core /// public static readonly PluginInfo CorePluginInfo = new PluginInfo { - Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core" + Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core", Version = new Version(2,0) }; /// /// The plugin used by core components of Artemis /// - public static readonly Plugin CorePlugin = new Plugin(CorePluginInfo, new DirectoryInfo(ApplicationFolder)); + public static readonly Plugin CorePlugin = new Plugin(CorePluginInfo, new DirectoryInfo(ApplicationFolder), null); internal static readonly CorePluginFeature CorePluginFeature = new CorePluginFeature {Plugin = CorePlugin}; internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new EffectPlaceholderPlugin {Plugin = CorePlugin}; diff --git a/src/Artemis.Core/Extensions/ProcessExtensions.cs b/src/Artemis.Core/Extensions/ProcessExtensions.cs index eb02a3c75..28685d781 100644 --- a/src/Artemis.Core/Extensions/ProcessExtensions.cs +++ b/src/Artemis.Core/Extensions/ProcessExtensions.cs @@ -8,6 +8,7 @@ namespace Artemis.Core /// /// A static class providing extensions /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1060:Move pinvokes to native methods class", Justification = "I don't care, piss off")] public static class ProcessExtensions { /// diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs index f764ca544..5b2c695d3 100644 --- a/src/Artemis.Core/Models/Profile/Folder.cs +++ b/src/Artemis.Core/Models/Profile/Folder.cs @@ -20,7 +20,7 @@ namespace Artemis.Core /// /// The parent of the folder /// The name of the folder - public Folder(ProfileElement parent, string name) + public Folder(ProfileElement parent, string name) : base(parent.Profile) { FolderEntity = new FolderEntity(); EntityId = Guid.NewGuid(); @@ -29,14 +29,11 @@ namespace Artemis.Core Profile = Parent.Profile; Name = name; Enabled = true; - - LayerEffectsList = new List(); - ExpandedPropertyGroups = new List(); - + Parent.AddChild(this); } - internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) + internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity) : base(parent.Profile) { FolderEntity = folderEntity; EntityId = folderEntity.Id; @@ -46,10 +43,7 @@ namespace Artemis.Core Name = folderEntity.Name; Enabled = folderEntity.Enabled; Order = folderEntity.Order; - - LayerEffectsList = new List(); - ExpandedPropertyGroups = new List(); - + Load(); } diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 89c91cf3e..259a13e03 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -28,7 +28,7 @@ namespace Artemis.Core /// /// The parent of the layer /// The name of the layer - public Layer(ProfileElement parent, string name) + public Layer(ProfileElement parent, string name) : base(parent.Profile) { LayerEntity = new LayerEntity(); EntityId = Guid.NewGuid(); @@ -39,16 +39,14 @@ namespace Artemis.Core Enabled = true; _general = new LayerGeneralProperties(); _transform = new LayerTransformProperties(); - - LayerEffectsList = new List(); + _leds = new List(); - ExpandedPropertyGroups = new List(); - + Initialize(); Parent.AddChild(this); } - internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) + internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) : base(parent.Profile) { LayerEntity = layerEntity; EntityId = layerEntity.Id; @@ -57,10 +55,8 @@ namespace Artemis.Core Parent = parent; _general = new LayerGeneralProperties(); _transform = new LayerTransformProperties(); - - LayerEffectsList = new List(); + _leds = new List(); - ExpandedPropertyGroups = new List(); Load(); Initialize(); @@ -263,17 +259,12 @@ namespace Artemis.Core private void ApplyShapeType() { - switch (General.ShapeType.CurrentValue) + LayerShape = General.ShapeType.CurrentValue switch { - case LayerShapeType.Ellipse: - LayerShape = new EllipseShape(this); - break; - case LayerShapeType.Rectangle: - LayerShape = new RectangleShape(this); - break; - default: - throw new ArgumentOutOfRangeException(); - } + LayerShapeType.Ellipse => new EllipseShape(this), + LayerShapeType.Rectangle => new RectangleShape(this), + _ => throw new ArgumentOutOfRangeException() + }; } #endregion diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 12edfed9c..67d653575 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -25,7 +25,13 @@ namespace Artemis.Core /// protected LayerProperty() { - // Cant define generic types as nullable ¯\_(ツ)_/¯ + // These are set right after construction to keep the constructor (and inherited constructs) clean + ProfileElement = null!; + LayerPropertyGroup = null!; + Path = null!; + Entity = null!; + PropertyDescription = null!; + CurrentValue = default!; DefaultValue = default!; diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs index 2e39f756c..b82052a89 100644 --- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs +++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Reflection; using Artemis.Core.LayerBrushes; using Artemis.Core.LayerEffects; -using Artemis.Core.Properties; using Artemis.Storage.Entities.Profile; using Humanizer; @@ -30,6 +29,12 @@ namespace Artemis.Core /// protected LayerPropertyGroup() { + // These are set right after construction to keep the constructor (and inherited constructs) clean + GroupDescription = null!; + Feature = null!; + ProfileElement = null!; + Path = null!; + _layerProperties = new List(); _layerPropertyGroups = new List(); } @@ -52,7 +57,7 @@ namespace Artemis.Core /// /// The parent group of this group /// - public LayerPropertyGroup Parent { get; internal set; } + public LayerPropertyGroup? Parent { get; internal set; } /// /// The path of this property group @@ -67,12 +72,12 @@ namespace Artemis.Core /// /// The layer brush this property group belongs to /// - public BaseLayerBrush LayerBrush { get; internal set; } + public BaseLayerBrush? LayerBrush { get; internal set; } /// /// The layer effect this property group belongs to /// - public BaseLayerEffect LayerEffect { get; internal set; } + public BaseLayerEffect? LayerEffect { get; internal set; } /// /// Gets or sets whether the property is hidden in the UI @@ -139,19 +144,16 @@ namespace Artemis.Core PropertyGroupInitialized?.Invoke(this, EventArgs.Empty); } - internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginFeature feature) + internal void Initialize(RenderProfileElement profileElement, string path, PluginFeature feature) { - if (path == null) - throw new ArgumentNullException(nameof(path)); - if (feature == null) - throw new ArgumentNullException(nameof(feature)); + if (path == null) throw new ArgumentNullException(nameof(path)); // Doubt this will happen but let's make sure if (PropertiesInitialized) throw new ArtemisCoreException("Layer property group already initialized, wut"); - Feature = feature; - ProfileElement = profileElement; + Feature = feature ?? throw new ArgumentNullException(nameof(feature)); + ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement)); Path = path.TrimEnd('.'); // Get all properties with a PropertyDescriptionAttribute @@ -209,8 +211,7 @@ namespace Artemis.Core if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType)) throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path}"); - ILayerProperty instance = (ILayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true); - if (instance == null) + if (!(Activator.CreateInstance(propertyInfo.PropertyType, true) is ILayerProperty instance)) throw new ArtemisPluginException($"Failed to create instance of layer property at {path}"); // Ensure the description has a name, if not this is a good point to set it based on the property info @@ -230,8 +231,7 @@ namespace Artemis.Core if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType)) throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup"); - LayerPropertyGroup instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType); - if (instance == null) + if (!(Activator.CreateInstance(propertyInfo.PropertyType) is LayerPropertyGroup instance)) throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}"); // Ensure the description has a name, if not this is a good point to set it based on the property info @@ -250,7 +250,7 @@ namespace Artemis.Core private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage) { - PropertyEntity entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.FeatureId == Feature.Id && p.Path == path); + PropertyEntity? entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.FeatureId == Feature.Id && p.Path == path); fromStorage = entity != null; if (entity == null) { @@ -298,18 +298,18 @@ namespace Artemis.Core /// /// Occurs when the property group has initialized all its children /// - public event EventHandler PropertyGroupInitialized; + public event EventHandler? PropertyGroupInitialized; /// /// Occurs when one of the current value of one of the layer properties in this group changes by some form of input /// Note: Will not trigger on properties in child groups /// - public event EventHandler LayerPropertyOnCurrentValueSet; + public event EventHandler? LayerPropertyOnCurrentValueSet; /// /// Occurs when the value of the layer property was updated /// - public event EventHandler VisibilityChanged; + public event EventHandler? VisibilityChanged; internal virtual void OnVisibilityChanged() { diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs index d9d6f7873..6b5f4e825 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs @@ -20,7 +20,7 @@ namespace Artemis.Core /// /// Gets a the path outlining the shape /// - public SKPath Path { get; protected set; } + public SKPath? Path { get; protected set; } /// /// Calculates the diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs index abadbc421..a8e21ce76 100644 --- a/src/Artemis.Core/Models/Profile/Profile.cs +++ b/src/Artemis.Core/Models/Profile/Profile.cs @@ -15,7 +15,7 @@ namespace Artemis.Core private bool _isActivated; private readonly object _lock = new object(); - internal Profile(ProfileModule module, string name) + internal Profile(ProfileModule module, string name) : base(null!) { ProfileEntity = new ProfileEntity(); EntityId = Guid.NewGuid(); @@ -30,7 +30,7 @@ namespace Artemis.Core Save(); } - internal Profile(ProfileModule module, ProfileEntity profileEntity) + internal Profile(ProfileModule module, ProfileEntity profileEntity) : base(null!) { Profile = this; ProfileEntity = profileEntity; @@ -149,7 +149,7 @@ namespace Artemis.Core ChildrenList.Clear(); // Populate the profile starting at the root, the rest is populated recursively - FolderEntity rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId); + FolderEntity? rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId); if (rootFolder == null) { Folder _ = new Folder(this, "Root folder"); diff --git a/src/Artemis.Core/Models/Profile/ProfileElement.cs b/src/Artemis.Core/Models/Profile/ProfileElement.cs index 29183378c..b9ee1b524 100644 --- a/src/Artemis.Core/Models/Profile/ProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/ProfileElement.cs @@ -13,7 +13,7 @@ namespace Artemis.Core { private bool _enabled; private Guid _entityId; - private string _name; + private string? _name; private int _order; private ProfileElement? _parent; private Profile _profile; @@ -21,8 +21,9 @@ namespace Artemis.Core internal List ChildrenList; internal bool Disposed; - internal ProfileElement() + internal ProfileElement(Profile profile) { + _profile = profile; ChildrenList = new List(); } @@ -56,7 +57,16 @@ namespace Artemis.Core /// /// The element's children /// - public ReadOnlyCollection Children => ChildrenList.AsReadOnly(); + public ReadOnlyCollection Children + { + get + { + lock (ChildrenList) + { + return ChildrenList.AsReadOnly(); + } + } + } /// /// The order in which this element appears in the update loop and editor @@ -70,7 +80,7 @@ namespace Artemis.Core /// /// The name which appears in the editor /// - public string Name + public string? Name { get => _name; set => SetAndNotify(ref _name, value); diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index 53b4cc9b6..9fc045777 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -16,10 +16,12 @@ namespace Artemis.Core /// public abstract class RenderProfileElement : ProfileElement { - internal RenderProfileElement() + internal RenderProfileElement(Profile profile) : base(profile) { Timeline = new Timeline(); Renderer = new Renderer(); + ExpandedPropertyGroups = new List(); + LayerEffectsList = new List(); LayerEffectStore.LayerEffectAdded += LayerEffectStoreOnLayerEffectAdded; LayerEffectStore.LayerEffectRemoved += LayerEffectStoreOnLayerEffectRemoved; @@ -68,7 +70,7 @@ namespace Artemis.Core LayerEffectEntity layerEffectEntity = new LayerEffectEntity { Id = layerEffect.EntityId, - ProviderId = layerEffect.Descriptor.PlaceholderFor ?? layerEffect.ProviderId, + ProviderId = layerEffect.Descriptor?.PlaceholderFor ?? layerEffect.ProviderId, EffectType = layerEffect.GetEffectTypeName(), Name = layerEffect.Name, Enabled = layerEffect.Enabled, @@ -76,7 +78,7 @@ namespace Artemis.Core Order = layerEffect.Order }; RenderElementEntity.LayerEffects.Add(layerEffectEntity); - layerEffect.BaseProperties.ApplyToEntity(); + layerEffect.BaseProperties?.ApplyToEntity(); } // Conditions @@ -290,8 +292,7 @@ namespace Artemis.Core private void LayerEffectStoreOnLayerEffectRemoved(object? sender, LayerEffectStoreEvent e) { // If effects provided by the plugin are on the element, replace them with placeholders - List pluginEffects = LayerEffectsList.Where(ef => ef.Descriptor.Provider != null && - ef.ProviderId == e.Registration.PluginFeature.Id).ToList(); + List pluginEffects = LayerEffectsList.Where(ef => ef.ProviderId == e.Registration.PluginFeature.Id).ToList(); foreach (BaseLayerEffect pluginEffect in pluginEffects) { LayerEffectEntity entity = RenderElementEntity.LayerEffects.First(en => en.Id == pluginEffect.EntityId); @@ -323,13 +324,13 @@ namespace Artemis.Core protected set => SetAndNotify(ref _displayConditionMet, value); } - private DataModelConditionGroup _displayCondition; + private DataModelConditionGroup? _displayCondition; private bool _displayConditionMet; /// /// Gets or sets the root display condition group /// - public DataModelConditionGroup DisplayCondition + public DataModelConditionGroup? DisplayCondition { get => _displayCondition; set => SetAndNotify(ref _displayCondition, value); diff --git a/src/Artemis.Core/Models/Profile/Renderer.cs b/src/Artemis.Core/Models/Profile/Renderer.cs index 03b4e1fb7..110974d05 100644 --- a/src/Artemis.Core/Models/Profile/Renderer.cs +++ b/src/Artemis.Core/Models/Profile/Renderer.cs @@ -26,7 +26,7 @@ namespace Artemis.Core if (IsOpen) throw new ArtemisCoreException("Cannot open render context because it is already open"); - if (!_valid) + if (!_valid || Canvas == null) { SKRect pathBounds = path.Bounds; int width = (int) pathBounds.Width; @@ -59,7 +59,7 @@ namespace Artemis.Core if (_disposed) throw new ObjectDisposedException("Renderer"); - Canvas.Restore(); + Canvas?.Restore(); Paint?.Dispose(); Paint = null; diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs index f99b35771..1bcb14540 100644 --- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs +++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs @@ -14,7 +14,7 @@ namespace Artemis.Core public class ArtemisDevice : CorePropertyChanged { private ReadOnlyCollection _leds; - private SKPath _renderPath; + private SKPath? _renderPath; private SKRect _renderRectangle; internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface) @@ -23,7 +23,7 @@ namespace Artemis.Core DeviceProvider = deviceProvider; Surface = surface; DeviceEntity = new DeviceEntity(); - Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); + _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); Rotation = 0; Scale = 1; @@ -39,7 +39,7 @@ namespace Artemis.Core DeviceProvider = deviceProvider; Surface = surface; DeviceEntity = deviceEntity; - Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); + _leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly(); } /// @@ -54,7 +54,7 @@ namespace Artemis.Core /// /// Gets the path surrounding the device, sized to match the render scale /// - public SKPath RenderPath + public SKPath? RenderPath { get => _renderPath; private set => SetAndNotify(ref _renderPath, value); diff --git a/src/Artemis.Core/Ninject/LoggerProvider.cs b/src/Artemis.Core/Ninject/LoggerProvider.cs index 9d1107b2d..e39a4a434 100644 --- a/src/Artemis.Core/Ninject/LoggerProvider.cs +++ b/src/Artemis.Core/Ninject/LoggerProvider.cs @@ -22,7 +22,7 @@ namespace Artemis.Core.Ninject protected override ILogger CreateInstance(IContext context) { - Type requestingType = context.Request.ParentContext?.Plan?.Type; + Type? requestingType = context.Request.ParentContext?.Plan?.Type; if (requestingType != null) return Logger.ForContext(requestingType); return Logger; diff --git a/src/Artemis.Core/Plugins/DataModelExpansions/Attributes/DataModelProperty.cs b/src/Artemis.Core/Plugins/DataModelExpansions/Attributes/DataModelProperty.cs index 6eae6b89b..3f6ee745a 100644 --- a/src/Artemis.Core/Plugins/DataModelExpansions/Attributes/DataModelProperty.cs +++ b/src/Artemis.Core/Plugins/DataModelExpansions/Attributes/DataModelProperty.cs @@ -11,32 +11,32 @@ namespace Artemis.Core.DataModelExpansions /// /// Gets or sets the user-friendly name for this property, shown in the UI. /// - public string Name { get; set; } + public string? Name { get; set; } /// /// Gets or sets the user-friendly description for this property, shown in the UI. /// - public string Description { get; set; } + public string? Description { get; set; } /// /// Gets or sets the an optional prefix to show before displaying elements in the UI. /// - public string Prefix { get; set; } + public string? Prefix { get; set; } /// /// Gets or sets an optional affix to show behind displaying elements in the UI. /// - public string Affix { get; set; } + public string? Affix { get; set; } /// /// Gets or sets an optional maximum value, this value is not enforced but used for percentage calculations. /// - public object MaxValue { get; set; } + public object? MaxValue { get; set; } /// /// Gets or sets an optional minimum value, this value is not enforced but used for percentage calculations. /// - public object MinValue { get; set; } + public object? MinValue { get; set; } /// /// Gets or sets whether this property resets the max depth of the data model, defaults to true diff --git a/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs b/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs index fd57053cb..3377bb081 100644 --- a/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs +++ b/src/Artemis.Core/Plugins/DataModelExpansions/DataModel.cs @@ -15,6 +15,16 @@ namespace Artemis.Core.DataModelExpansions { private readonly Dictionary _dynamicDataModels = new Dictionary(); + /// + /// Creates a new instance of the class + /// + protected DataModel() + { + // These are both set right after construction to keep the constructor of inherited classes clean + Feature = null!; + DataModelDescription = null!; + } + /// /// Gets the plugin feature this data model belongs to /// @@ -60,7 +70,7 @@ namespace Artemis.Core.DataModelExpansions /// The key of the child, must be unique to this data model /// An optional name, if not provided the key will be used in a humanized form /// An optional description - public T AddDynamicChild(T dynamicDataModel, string key, string name = null, string description = null) where T : DataModel + public T AddDynamicChild(T dynamicDataModel, string key, string? name = null, string? description = null) where T : DataModel { if (dynamicDataModel == null) throw new ArgumentNullException(nameof(dynamicDataModel)); @@ -140,7 +150,7 @@ namespace Artemis.Core.DataModelExpansions /// If found, the dynamic data model otherwise null public T? DynamicChild(string key) where T : DataModel { - _dynamicDataModels.TryGetValue(key, out DataModel value); + _dynamicDataModels.TryGetValue(key, out DataModel? value); return value as T; } diff --git a/src/Artemis.Core/Plugins/DataModelExpansions/DataModelExpansion.cs b/src/Artemis.Core/Plugins/DataModelExpansions/DataModelExpansion.cs index eca795b81..a0dd6b998 100644 --- a/src/Artemis.Core/Plugins/DataModelExpansions/DataModelExpansion.cs +++ b/src/Artemis.Core/Plugins/DataModelExpansions/DataModelExpansion.cs @@ -16,7 +16,7 @@ namespace Artemis.Core.DataModelExpansions /// public T DataModel { - get => (T) InternalDataModel; + get => InternalDataModel as T ?? throw new InvalidOperationException("Internal datamodel does not match the type of the data model"); internal set => InternalDataModel = value; } @@ -49,11 +49,5 @@ namespace Artemis.Core.DataModelExpansions DataModel.DataModelDescription = GetDataModelDescription(); base.InternalEnable(); } - - internal override void InternalDisable() - { - DataModel = null; - base.InternalDisable(); - } } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs index c59446c14..bfc020c52 100644 --- a/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs +++ b/src/Artemis.Core/Plugins/DeviceProviders/DeviceProvider.cs @@ -31,7 +31,7 @@ namespace Artemis.Core.DeviceProviders /// A logger used by the device provider internally, ignore this /// [Inject] - public ILogger Logger { get; set; } + public ILogger? Logger { get; set; } /// public override void Disable() diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs index 837f32142..c452384e3 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs @@ -10,10 +10,20 @@ namespace Artemis.Core.LayerBrushes { private LayerBrushType _brushType; private ILayerBrushConfigurationDialog? _configurationDialog; - private LayerBrushDescriptor? _descriptor; + private LayerBrushDescriptor _descriptor; private Layer _layer; private bool _supportsTransformation = true; + /// + /// Creates a new instance of the class + /// + protected BaseLayerBrush() + { + // Both are set right after construction to keep the constructor of inherited classes clean + _layer = null!; + _descriptor = null!; + } + /// /// Gets the layer this brush is applied to /// @@ -26,7 +36,7 @@ namespace Artemis.Core.LayerBrushes /// /// Gets the descriptor of this brush /// - public LayerBrushDescriptor? Descriptor + public LayerBrushDescriptor Descriptor { get => _descriptor; internal set => SetAndNotify(ref _descriptor, value); diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs index d8ede59ee..5b12849b8 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/PropertiesLayerBrush.cs @@ -7,7 +7,7 @@ namespace Artemis.Core.LayerBrushes /// public abstract class PropertiesLayerBrush : BaseLayerBrush where T : LayerPropertyGroup { - private T _properties; + private T _properties = null!; /// /// Gets whether all properties on this brush are initialized @@ -35,7 +35,7 @@ namespace Artemis.Core.LayerBrushes internal void InitializeProperties() { Properties = Activator.CreateInstance(); - Properties.GroupDescription ??= new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description}; + Properties.GroupDescription = new PropertyGroupDescriptionAttribute {Name = Descriptor.DisplayName, Description = Descriptor.Description}; Properties.LayerBrush = this; Properties.Initialize(Layer, "LayerBrush.", Descriptor.Provider); PropertiesInitialized = true; diff --git a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs index b8673a261..2f70a6d54 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrushDescriptor.cs @@ -59,6 +59,7 @@ namespace Artemis.Core.LayerBrushes /// internal void CreateInstance(Layer layer) { + if (layer == null) throw new ArgumentNullException(nameof(layer)); if (layer.LayerBrush != null) throw new ArtemisCoreException("Layer already has an instantiated layer brush"); diff --git a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs index c106a1c15..4028228a7 100644 --- a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs @@ -20,6 +20,7 @@ namespace Artemis.Core.LayerBrushes /// protected RgbNetLayerBrush() { + LedGroup = new ListLedGroup(); BrushType = LayerBrushType.RgbNet; SupportsTransformation = false; } @@ -75,7 +76,7 @@ namespace Artemis.Core.LayerBrushes // Not used in this effect type internal override void InternalRender(SKCanvas canvas, SKRect bounds, SKPaint paint) { - throw new NotImplementedException("RGB.NET layer effectes do not implement InternalRender"); + throw new NotImplementedException("RGB.NET layer effects do not implement InternalRender"); } private void LayerOnRenderPropertiesUpdated(object? sender, EventArgs e) diff --git a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs index f76014daf..87a10811d 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs @@ -9,7 +9,7 @@ namespace Artemis.Core.LayerEffects /// public abstract class BaseLayerEffect : CorePropertyChanged, IDisposable { - private ILayerEffectConfigurationDialog _configurationDialog; + private ILayerEffectConfigurationDialog? _configurationDialog; private LayerEffectDescriptor _descriptor; private bool _enabled; private Guid _entityId; @@ -18,6 +18,15 @@ namespace Artemis.Core.LayerEffects private int _order; private RenderProfileElement _profileElement; + /// + protected BaseLayerEffect() + { + // These are set right after construction to keep the constructor of inherited classes clean + _profileElement = null!; + _descriptor = null!; + _name = null!; + } + /// /// Gets the unique ID of this effect /// @@ -76,7 +85,7 @@ namespace Artemis.Core.LayerEffects /// /// Gets the that registered this effect /// - public LayerEffectDescriptor? Descriptor + public LayerEffectDescriptor Descriptor { get => _descriptor; internal set => SetAndNotify(ref _descriptor, value); @@ -85,7 +94,7 @@ namespace Artemis.Core.LayerEffects /// /// Gets or sets a configuration dialog complementing the regular properties /// - public ILayerEffectConfigurationDialog ConfigurationDialog + public ILayerEffectConfigurationDialog? ConfigurationDialog { get => _configurationDialog; protected set => SetAndNotify(ref _configurationDialog, value); @@ -94,12 +103,12 @@ namespace Artemis.Core.LayerEffects /// /// Gets the ID of the that provided this effect /// - public string ProviderId => Descriptor?.Provider?.Id; + public string ProviderId => Descriptor.Provider.Id; /// /// Gets a reference to the layer property group without knowing it's type /// - public virtual LayerPropertyGroup BaseProperties => null; + public virtual LayerPropertyGroup? BaseProperties => null; internal string PropertyRootPath => $"LayerEffect.{EntityId}.{GetType().Name}."; diff --git a/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs index 585fb8463..3a1b03e62 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/LayerEffect.cs @@ -8,7 +8,7 @@ namespace Artemis.Core.LayerEffects /// public abstract class LayerEffect : BaseLayerEffect where T : LayerPropertyGroup { - private T _properties; + private T _properties = null!; /// /// Gets whether all properties on this effect are initialized diff --git a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs index 33fafb542..0b742742a 100644 --- a/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs +++ b/src/Artemis.Core/Plugins/LayerEffects/LayerEffectDescriptor.cs @@ -11,7 +11,7 @@ namespace Artemis.Core.LayerEffects /// public class LayerEffectDescriptor { - internal LayerEffectDescriptor(string displayName, string description, string icon, Type layerEffectType, LayerEffectProvider provider) + internal LayerEffectDescriptor(string displayName, string description, string icon, Type? layerEffectType, LayerEffectProvider provider) { DisplayName = displayName; Description = description; @@ -39,12 +39,12 @@ namespace Artemis.Core.LayerEffects /// /// The type of the layer effect /// - public Type LayerEffectType { get; } + public Type? LayerEffectType { get; } /// /// The plugin that provided this /// - public LayerEffectProvider? Provider { get; } + public LayerEffectProvider Provider { get; } /// /// Gets the GUID this descriptor is acting as a placeholder for. If null, this descriptor is not a placeholder @@ -66,8 +66,8 @@ namespace Artemis.Core.LayerEffects return; } - 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"); + if (LayerEffectType == null) + throw new ArtemisCoreException("Cannot create an instance of a layer effect because this descriptor is not a placeholder but is still missing its LayerEffectType"); BaseLayerEffect effect = (BaseLayerEffect) Provider.Plugin.Kernel!.Get(LayerEffectType); effect.ProfileElement = renderElement; @@ -85,7 +85,13 @@ namespace Artemis.Core.LayerEffects private void CreatePlaceHolderInstance(RenderProfileElement renderElement, LayerEffectEntity entity) { - PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor) {ProfileElement = renderElement, Descriptor = this}; + if (PlaceholderFor == null) + throw new ArtemisCoreException("Cannot create a placeholder instance using a layer effect descriptor that is not a placeholder for anything"); + PlaceholderLayerEffect effect = new PlaceholderLayerEffect(entity, PlaceholderFor) + { + ProfileElement = renderElement, + Descriptor = this + }; effect.Initialize(); renderElement.ActivateLayerEffect(effect); } diff --git a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs index 51c471b0a..cd43a0c9a 100644 --- a/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs +++ b/src/Artemis.Core/Plugins/Modules/ActivationRequirements/ProcessActivationRequirement.cs @@ -16,7 +16,7 @@ namespace Artemis.Core.Modules /// /// The name of the process that must run /// The location of where the process must be running from (optional) - public ProcessActivationRequirement(string processName, string location = null) + public ProcessActivationRequirement(string? processName, string? location = null) { ProcessName = processName; Location = location; @@ -25,12 +25,12 @@ namespace Artemis.Core.Modules /// /// The name of the process that must run /// - public string ProcessName { get; set; } + public string? ProcessName { get; set; } /// /// The location of where the process must be running from /// - public string Location { get; set; } + public string? Location { get; set; } /// public bool Evaluate() diff --git a/src/Artemis.Core/Plugins/Modules/Module.cs b/src/Artemis.Core/Plugins/Modules/Module.cs index 89698c176..88a194349 100644 --- a/src/Artemis.Core/Plugins/Modules/Module.cs +++ b/src/Artemis.Core/Plugins/Modules/Module.cs @@ -18,7 +18,7 @@ namespace Artemis.Core.Modules /// public T DataModel { - get => (T) InternalDataModel; + get => InternalDataModel as T ?? throw new InvalidOperationException("Internal datamodel does not match the type of the data model"); internal set => InternalDataModel = value; } @@ -52,12 +52,6 @@ namespace Artemis.Core.Modules DataModel.DataModelDescription = GetDataModelDescription(); base.InternalEnable(); } - - internal override void InternalDisable() - { - DataModel = null; - base.InternalDisable(); - } } diff --git a/src/Artemis.Core/Plugins/Modules/ModuleTab.cs b/src/Artemis.Core/Plugins/Modules/ModuleTab.cs index 1c017964c..01c11411e 100644 --- a/src/Artemis.Core/Plugins/Modules/ModuleTab.cs +++ b/src/Artemis.Core/Plugins/Modules/ModuleTab.cs @@ -6,12 +6,11 @@ namespace Artemis.Core.Modules public class ModuleTab : ModuleTab where T : IModuleViewModel { /// - /// Initializes a new instance of the class + /// Creates a new instance of the class /// /// The title of the tab - public ModuleTab(string title) + public ModuleTab(string title) : base(title) { - Title = title; } /// @@ -24,9 +23,13 @@ namespace Artemis.Core.Modules public abstract class ModuleTab { /// - /// The module this tab belongs to + /// Creates a new instance of the class /// - internal Module Module { get; set; } + /// The title of the tab + protected ModuleTab(string title) + { + Title = title; + } /// /// The title of the tab diff --git a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs index a5b45926c..945521491 100644 --- a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs +++ b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs @@ -22,7 +22,7 @@ namespace Artemis.Core.Modules /// public T DataModel { - get => (T) InternalDataModel; + get => InternalDataModel as T ?? throw new InvalidOperationException("Internal datamodel does not match the type of the data model"); internal set => InternalDataModel = value; } @@ -83,7 +83,6 @@ namespace Artemis.Core.Modules { Deactivate(true); base.InternalDisable(); - DataModel = null; } } @@ -182,7 +181,7 @@ namespace Artemis.Core.Modules ProfileRendered(deltaTime, surface, canvas, canvasInfo); } - internal async Task ChangeActiveProfileAnimated(Profile profile, ArtemisSurface surface) + internal async Task ChangeActiveProfileAnimated(Profile? profile, ArtemisSurface? surface) { if (profile != null && profile.Module != this) throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}."); @@ -204,12 +203,14 @@ namespace Artemis.Core.Modules await Task.Delay(50); } - internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface) + internal void ChangeActiveProfile(Profile? profile, ArtemisSurface? surface) { if (profile != null && profile.Module != this) throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {this}."); if (!IsActivated) throw new ArtemisCoreException("Cannot activate a profile on a deactivated module"); + if (profile != null && surface == null) + throw new ArtemisCoreException("If changing the active profile to a non-null profile, a surface is required"); lock (_lock) { @@ -219,7 +220,7 @@ namespace Artemis.Core.Modules ActiveProfile?.Dispose(); ActiveProfile = profile; - ActiveProfile?.Activate(surface); + ActiveProfile?.Activate(surface!); } OnActiveProfileChanged(); @@ -229,7 +230,7 @@ namespace Artemis.Core.Modules { base.Deactivate(isOverride); - Profile profile = ActiveProfile; + Profile? profile = ActiveProfile; ActiveProfile = null; profile?.Dispose(); } @@ -249,7 +250,7 @@ namespace Artemis.Core.Modules /// /// Occurs when the has changed /// - public event EventHandler ActiveProfileChanged; + public event EventHandler? ActiveProfileChanged; /// /// Invokes the event diff --git a/src/Artemis.Core/Plugins/Plugin.cs b/src/Artemis.Core/Plugins/Plugin.cs index 2eb977b9d..853649b0b 100644 --- a/src/Artemis.Core/Plugins/Plugin.cs +++ b/src/Artemis.Core/Plugins/Plugin.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -19,10 +20,11 @@ namespace Artemis.Core private bool _isEnabled; - internal Plugin(PluginInfo info, DirectoryInfo directory) + internal Plugin(PluginInfo info, DirectoryInfo directory, PluginEntity? pluginEntity) { Info = info; Directory = directory; + Entity = pluginEntity ?? new PluginEntity {Id = Guid, IsEnabled = true}; _features = new List(); } @@ -42,6 +44,7 @@ namespace Artemis.Core /// public DirectoryInfo Directory { get; } + /// /// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins /// @@ -91,7 +94,8 @@ namespace Artemis.Core /// /// The path to resolve /// An absolute path pointing to the provided relative path - public string? ResolveRelativePath(string path) + [return: NotNullIfNotNull("path")] + public string? ResolveRelativePath(string? path) { return path == null ? null : Path.Combine(Directory.FullName, path); } diff --git a/src/Artemis.Core/Plugins/PluginFeature.cs b/src/Artemis.Core/Plugins/PluginFeature.cs index 3cc20c092..733c3ee9b 100644 --- a/src/Artemis.Core/Plugins/PluginFeature.cs +++ b/src/Artemis.Core/Plugins/PluginFeature.cs @@ -16,7 +16,7 @@ namespace Artemis.Core /// /// Gets the plugin that provides this feature /// - public Plugin Plugin { get; internal set; } + public Plugin Plugin { get; internal set; } = null!; // Will be set right after construction /// /// Gets whether the plugin is enabled @@ -41,7 +41,7 @@ namespace Artemis.Core /// 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; } + internal PluginFeatureEntity Entity { get; set; } = null!; // Will be set right after construction /// /// Called when the feature is activated diff --git a/src/Artemis.Core/Plugins/PluginInfo.cs b/src/Artemis.Core/Plugins/PluginInfo.cs index 26a3b7e1f..4323b2764 100644 --- a/src/Artemis.Core/Plugins/PluginInfo.cs +++ b/src/Artemis.Core/Plugins/PluginInfo.cs @@ -9,13 +9,13 @@ namespace Artemis.Core [JsonObject(MemberSerialization.OptIn)] public class PluginInfo : CorePropertyChanged { - private string _description; + private string? _description; private Guid _guid; - private string _icon; - private string _main; - private string _name; - private Plugin _plugin; - private Version _version; + private string? _icon; + private string _main = null!; + private string _name = null!; + private Plugin _plugin = null!; + private Version _version = null!; internal PluginInfo() { @@ -45,7 +45,7 @@ namespace Artemis.Core /// A short description of the plugin /// [JsonProperty] - public string Description + public string? Description { get => _description; set => SetAndNotify(ref _description, value); @@ -57,7 +57,7 @@ namespace Artemis.Core /// icons /// [JsonProperty] - public string Icon + public string? Icon { get => _icon; set => SetAndNotify(ref _icon, value); diff --git a/src/Artemis.Core/Plugins/Settings/PluginSetting.cs b/src/Artemis.Core/Plugins/Settings/PluginSetting.cs index d8738dd70..02508f220 100644 --- a/src/Artemis.Core/Plugins/Settings/PluginSetting.cs +++ b/src/Artemis.Core/Plugins/Settings/PluginSetting.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics.CodeAnalysis; +using Artemis.Core.Properties; using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Repositories.Interfaces; using Newtonsoft.Json; @@ -27,11 +29,11 @@ namespace Artemis.Core Name = pluginSettingEntity.Name; try { - Value = JsonConvert.DeserializeObject(pluginSettingEntity.Value); + _value = JsonConvert.DeserializeObject(pluginSettingEntity.Value); } catch (JsonReaderException) { - Value = default; + _value = default!; } } @@ -43,6 +45,8 @@ namespace Artemis.Core /// /// The value of the setting /// + [AllowNull] + [CanBeNull] public T Value { get => _value; @@ -50,7 +54,7 @@ namespace Artemis.Core { if (Equals(_value, value)) return; - _value = value; + _value = value!; OnSettingChanged(); OnPropertyChanged(nameof(Value)); @@ -94,12 +98,12 @@ namespace Artemis.Core /// /// Occurs when the value of the setting has been changed /// - public event EventHandler SettingChanged; + public event EventHandler? SettingChanged; /// /// Occurs when the value of the setting has been saved /// - public event EventHandler SettingSaved; + public event EventHandler? SettingSaved; /// public override string ToString() diff --git a/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs b/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs index cc77111b7..1297f6a71 100644 --- a/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs +++ b/src/Artemis.Core/Plugins/TimedUpdateRegistration.cs @@ -11,7 +11,7 @@ namespace Artemis.Core public class TimedUpdateRegistration : IDisposable { private DateTime _lastEvent; - private Timer _timer; + private Timer? _timer; private bool _disposed; private readonly object _lock = new object(); @@ -53,12 +53,12 @@ namespace Artemis.Core /// /// Gets the action that gets called each time the update event fires /// - public Action Action { get; } + public Action? Action { get; } /// /// Gets the task that gets called each time the update event fires /// - public Func AsyncAction { get; } + public Func? AsyncAction { get; } /// /// Starts calling the or at the configured diff --git a/src/Artemis.Core/Properties/Annotations.cs b/src/Artemis.Core/Properties/Annotations.cs index 74ad7d4bc..8e036ce4e 100644 --- a/src/Artemis.Core/Properties/Annotations.cs +++ b/src/Artemis.Core/Properties/Annotations.cs @@ -22,9 +22,10 @@ SOFTWARE. */ using System; -// ReSharper disable InheritdocConsiderUsage - #pragma warning disable 1591 +#pragma warning disable 8618 + +// ReSharper disable InheritdocConsiderUsage // ReSharper disable UnusedMember.Global // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedAutoPropertyAccessor.Global diff --git a/src/Artemis.Core/RGB.NET/BitmapBrush.cs b/src/Artemis.Core/RGB.NET/BitmapBrush.cs index 1ac02169d..a7d4a656b 100644 --- a/src/Artemis.Core/RGB.NET/BitmapBrush.cs +++ b/src/Artemis.Core/RGB.NET/BitmapBrush.cs @@ -60,7 +60,7 @@ namespace Artemis.Core /// /// Gets the bitmap used to sample the brush /// - public SKBitmap Bitmap { get; private set; } + public SKBitmap? Bitmap { get; private set; } #endregion @@ -94,6 +94,9 @@ namespace Artemis.Core private void TakeCenter(IEnumerable renderTargets) { + if (Bitmap == null) + return; + foreach (BrushRenderTarget renderTarget in renderTargets) { Point scaledLocation = renderTarget.Point * Scale; @@ -104,6 +107,9 @@ namespace Artemis.Core private void TakeSamples(IEnumerable renderTargets) { + if (Bitmap == null) + return; + int sampleSize = _sampleSizeSetting.Value; int sampleDepth = Math.Sqrt(sampleSize).RoundToInt(); diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index 4842b6880..782b83589 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -23,7 +23,7 @@ namespace Artemis.Core.Services /// internal class CoreService : ICoreService { - internal static IKernel Kernel; + internal static IKernel Kernel = null!; private readonly Stopwatch _frameStopWatch; private readonly ILogger _logger; @@ -32,10 +32,10 @@ namespace Artemis.Core.Services private readonly IProfileService _profileService; private readonly IRgbService _rgbService; private readonly ISurfaceService _surfaceService; - private List _dataModelExpansions; - private List _modules; + private List _dataModelExpansions = new List(); + private List _modules = new List(); - // ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else + // ReSharper disable UnusedParameter.Local - Storage migration and module service are injected early to ensure it runs before anything else public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginManagementService pluginManagementService, IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService) { @@ -60,10 +60,11 @@ namespace Artemis.Core.Services _pluginManagementService.PluginEnabled += (sender, args) => UpdatePluginCache(); _pluginManagementService.PluginDisabled += (sender, args) => UpdatePluginCache(); } + // ReSharper restore UnusedParameter.Local public TimeSpan FrameTime { get; private set; } public bool ModuleRenderingDisabled { get; set; } - public List StartupArguments { get; set; } + public List? StartupArguments { get; set; } public void Dispose() { @@ -86,13 +87,10 @@ namespace Artemis.Core.Services // Initialize the services _pluginManagementService.CopyBuiltInPlugins(); - _pluginManagementService.LoadPlugins(StartupArguments.Contains("--ignore-plugin-lock")); + _pluginManagementService.LoadPlugins(StartupArguments != null && StartupArguments.Contains("--ignore-plugin-lock")); ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface; - if (surfaceConfig != null) - _logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId); - else - _logger.Information("Initialized without an active surface entity"); + _logger.Information("Initialized with active surface entity {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.EntityId); PlayIntroAnimation(); OnInitialized(); @@ -121,7 +119,7 @@ namespace Artemis.Core.Services FrameRendering += DrawOverlay; // Stop rendering after the profile finishes (take 1 second extra in case of slow updates) - TimeSpan introLength = intro.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length); + TimeSpan introLength = intro.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length)!; Task.Run(async () => { await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1))); @@ -217,14 +215,14 @@ namespace Artemis.Core.Services if (_rgbService.IsRenderPaused) return; - OnFrameRendered(new FrameRenderedEventArgs(_rgbService.BitmapBrush, _rgbService.Surface)); + OnFrameRendered(new FrameRenderedEventArgs(_rgbService.BitmapBrush!, _rgbService.Surface)); } #region Events - public event EventHandler Initialized; - public event EventHandler FrameRendering; - public event EventHandler FrameRendered; + public event EventHandler? Initialized; + public event EventHandler? FrameRendering; + public event EventHandler? FrameRendered; private void OnInitialized() { diff --git a/src/Artemis.Core/Services/Interfaces/ICoreService.cs b/src/Artemis.Core/Services/Interfaces/ICoreService.cs index b506f18ac..cc99e4fc7 100644 --- a/src/Artemis.Core/Services/Interfaces/ICoreService.cs +++ b/src/Artemis.Core/Services/Interfaces/ICoreService.cs @@ -26,7 +26,7 @@ namespace Artemis.Core.Services /// /// Gets or sets a list of startup arguments /// - List StartupArguments { get; set; } + List? StartupArguments { get; set; } /// /// Initializes the core, only call once diff --git a/src/Artemis.Core/Services/Interfaces/IModuleService.cs b/src/Artemis.Core/Services/Interfaces/IModuleService.cs index aa61cd25c..e4748174a 100644 --- a/src/Artemis.Core/Services/Interfaces/IModuleService.cs +++ b/src/Artemis.Core/Services/Interfaces/IModuleService.cs @@ -12,13 +12,13 @@ namespace Artemis.Core.Services /// Gets the current active module override. If set, all other modules are deactivated and only the /// is active. /// - Module ActiveModuleOverride { get; } + Module? ActiveModuleOverride { get; } /// /// Changes the current and deactivates all other modules /// /// - Task SetActiveModuleOverride(Module overrideModule); + Task SetActiveModuleOverride(Module? overrideModule); /// /// Evaluates every enabled module's activation requirements and activates/deactivates modules accordingly diff --git a/src/Artemis.Core/Services/Interfaces/IRgbService.cs b/src/Artemis.Core/Services/Interfaces/IRgbService.cs index e5cd0c213..ad1d51644 100644 --- a/src/Artemis.Core/Services/Interfaces/IRgbService.cs +++ b/src/Artemis.Core/Services/Interfaces/IRgbService.cs @@ -17,7 +17,7 @@ namespace Artemis.Core.Services /// /// Gets the bitmap brush used to convert the rendered frame to LED-colors /// - BitmapBrush BitmapBrush { get; } + BitmapBrush? BitmapBrush { get; } /// /// Gets the scale the frames are rendered on, a scale of 1.0 means 1 pixel = 1mm diff --git a/src/Artemis.Core/Services/ModuleService.cs b/src/Artemis.Core/Services/ModuleService.cs index d5c543d82..68dd9d203 100644 --- a/src/Artemis.Core/Services/ModuleService.cs +++ b/src/Artemis.Core/Services/ModuleService.cs @@ -149,9 +149,9 @@ namespace Artemis.Core.Services 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) { try { diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index 8817ae644..e9b401081 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -242,12 +242,10 @@ namespace Artemis.Core.Services throw new ArtemisCoreException("Cannot load a plugin that is already loaded"); } - Plugin plugin = new Plugin(pluginInfo, directory); - OnPluginLoading(new PluginEventArgs(plugin)); - // 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 plugin = new Plugin(pluginInfo, directory, _pluginRepository.GetPluginByGuid(pluginInfo.Guid)); + OnPluginLoading(new PluginEventArgs(plugin)); + // Locate the main assembly entry string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main); if (!File.Exists(mainFile)) diff --git a/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs b/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs index bc3664a4a..f14a0d0af 100644 --- a/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs +++ b/src/Artemis.Core/Services/Registration/ConditionOperatorService.cs @@ -34,7 +34,7 @@ namespace Artemis.Core.Services return ConditionOperatorStore.GetForType(type, side).Select(r => r.ConditionOperator).ToList(); } - public BaseConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType) + public BaseConditionOperator? GetConditionOperator(Guid operatorPluginGuid, string operatorType) { return ConditionOperatorStore.Get(operatorPluginGuid, operatorType)?.ConditionOperator; } diff --git a/src/Artemis.Core/Services/Registration/DataBindingService.cs b/src/Artemis.Core/Services/Registration/DataBindingService.cs index b800a528c..6cf789fbc 100644 --- a/src/Artemis.Core/Services/Registration/DataBindingService.cs +++ b/src/Artemis.Core/Services/Registration/DataBindingService.cs @@ -34,7 +34,7 @@ namespace Artemis.Core.Services return DataBindingModifierTypeStore.GetForType(type, part).Select(r => r.DataBindingModifierType).ToList(); } - public BaseDataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType) + public BaseDataBindingModifierType? GetModifierType(Guid modifierTypePluginGuid, string modifierType) { return DataBindingModifierTypeStore.Get(modifierTypePluginGuid, modifierType)?.DataBindingModifierType; } diff --git a/src/Artemis.Core/Services/Registration/DataModelService.cs b/src/Artemis.Core/Services/Registration/DataModelService.cs index b583439be..c0fcc3a02 100644 --- a/src/Artemis.Core/Services/Registration/DataModelService.cs +++ b/src/Artemis.Core/Services/Registration/DataModelService.cs @@ -11,7 +11,7 @@ namespace Artemis.Core.Services public DataModelService(IPluginManagementService pluginManagementService) { // Add data models of already loaded plugins - foreach (Module module in pluginManagementService.GetFeaturesOfType().Where(p => p.IsEnabled)) + foreach (Module module in pluginManagementService.GetFeaturesOfType().Where(p => p.IsEnabled && p.InternalDataModel != null)) AddModuleDataModel(module); foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetFeaturesOfType().Where(p => p.IsEnabled)) AddDataModelExpansionDataModel(dataModelExpansion); @@ -40,9 +40,9 @@ namespace Artemis.Core.Services return DataModelStore.GetAll().Select(d => d.DataModel).ToList(); } - public T GetDataModel() where T : DataModel + public T? GetDataModel() where T : DataModel { - 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(PluginFeature pluginFeature) @@ -61,8 +61,7 @@ namespace Artemis.Core.Services private void AddModuleDataModel(Module module) { if (module.InternalDataModel == null) - return; - + throw new ArtemisCoreException("Cannot add module data model that is not enabled"); if (module.InternalDataModel.DataModelDescription == null) throw new ArtemisPluginFeatureException(module, "Module overrides GetDataModelDescription but returned null"); @@ -72,6 +71,8 @@ namespace Artemis.Core.Services private void AddDataModelExpansionDataModel(BaseDataModelExpansion dataModelExpansion) { + if (dataModelExpansion.InternalDataModel == null) + throw new ArtemisCoreException("Cannot add data model expansion that is not enabled"); if (dataModelExpansion.InternalDataModel.DataModelDescription == null) throw new ArtemisPluginFeatureException(dataModelExpansion, "Data model expansion overrides GetDataModelDescription but returned null"); diff --git a/src/Artemis.Core/Services/Registration/Interfaces/IConditionOperatorService.cs b/src/Artemis.Core/Services/Registration/Interfaces/IConditionOperatorService.cs index 359fec8d9..9b32ecc3f 100644 --- a/src/Artemis.Core/Services/Registration/Interfaces/IConditionOperatorService.cs +++ b/src/Artemis.Core/Services/Registration/Interfaces/IConditionOperatorService.cs @@ -32,6 +32,6 @@ namespace Artemis.Core.Services /// /// The operator's plugin GUID /// The type name of the operator - BaseConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType); + BaseConditionOperator? GetConditionOperator(Guid operatorPluginGuid, string operatorType); } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Registration/Interfaces/IDataBindingService.cs b/src/Artemis.Core/Services/Registration/Interfaces/IDataBindingService.cs index 27ca01ab3..36b73dcaf 100644 --- a/src/Artemis.Core/Services/Registration/Interfaces/IDataBindingService.cs +++ b/src/Artemis.Core/Services/Registration/Interfaces/IDataBindingService.cs @@ -33,6 +33,6 @@ namespace Artemis.Core.Services /// The modifier type's plugin GUID /// The type name of the modifier type /// - BaseDataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType); + BaseDataBindingModifierType? GetModifierType(Guid modifierTypePluginGuid, string modifierType); } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Registration/Interfaces/IDataModelService.cs b/src/Artemis.Core/Services/Registration/Interfaces/IDataModelService.cs index 2f07006b6..072c68601 100644 --- a/src/Artemis.Core/Services/Registration/Interfaces/IDataModelService.cs +++ b/src/Artemis.Core/Services/Registration/Interfaces/IDataModelService.cs @@ -29,7 +29,7 @@ namespace Artemis.Core.Services /// If found, returns the registered data model of type /// /// The type of the data model to find - T GetDataModel() where T : DataModel; + T? GetDataModel() where T : DataModel; /// /// If found, returns the data model of the provided plugin diff --git a/src/Artemis.Core/Services/Registration/LayerBrushService.cs b/src/Artemis.Core/Services/Registration/LayerBrushService.cs index de738d6c4..667ecde87 100644 --- a/src/Artemis.Core/Services/Registration/LayerBrushService.cs +++ b/src/Artemis.Core/Services/Registration/LayerBrushService.cs @@ -34,7 +34,7 @@ namespace Artemis.Core.Services return LayerBrushStore.GetAll().Select(r => r.LayerBrushDescriptor).ToList(); } - public LayerBrushDescriptor GetDefaultLayerBrush() + public LayerBrushDescriptor? GetDefaultLayerBrush() { PluginSetting defaultReference = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference { @@ -42,6 +42,8 @@ namespace Artemis.Core.Services BrushType = "ColorBrush" }); + defaultReference.Value.LayerBrushProviderId ??= "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba"; + defaultReference.Value.BrushType ??= "ColorBrush"; return LayerBrushStore.Get(defaultReference.Value.LayerBrushProviderId, defaultReference.Value.BrushType)?.LayerBrushDescriptor; } } diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs index 9091dbdd2..81031d862 100644 --- a/src/Artemis.Core/Services/RgbService.cs +++ b/src/Artemis.Core/Services/RgbService.cs @@ -17,7 +17,7 @@ namespace Artemis.Core.Services private readonly PluginSetting _renderScaleSetting; private readonly PluginSetting _sampleSizeSetting; private readonly PluginSetting _targetFrameRateSetting; - private ListLedGroup _surfaceLedGroup; + private ListLedGroup? _surfaceLedGroup; public RgbService(ILogger logger, ISettingsService settingsService) { @@ -41,7 +41,7 @@ namespace Artemis.Core.Services public RGBSurface Surface { get; set; } public TimerUpdateTrigger UpdateTrigger { get; } - public BitmapBrush BitmapBrush { get; private set; } + public BitmapBrush? BitmapBrush { get; private set; } public IReadOnlyCollection LoadedDevices => _loadedDevices.AsReadOnly(); public double RenderScale => _renderScaleSetting.Value; public bool IsRenderPaused { get; set; } @@ -109,7 +109,7 @@ namespace Artemis.Core.Services public void UpdateSurfaceLedGroup() { - if (_surfaceLedGroup == null) + if (_surfaceLedGroup == null || BitmapBrush == null) { // Apply the application wide brush and decorator BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting); @@ -126,10 +126,6 @@ namespace Artemis.Core.Services BitmapBrush.Scale = new Scale(_renderScaleSetting.Value); _surfaceLedGroup = new ListLedGroup(Surface.Leds) {Brush = BitmapBrush}; } - - lock (BitmapBrush) - { - } } private void OnDeviceLoaded(DeviceEventArgs e) diff --git a/src/Artemis.Core/Services/SettingsService.cs b/src/Artemis.Core/Services/SettingsService.cs index a5ca7d658..d58124c03 100644 --- a/src/Artemis.Core/Services/SettingsService.cs +++ b/src/Artemis.Core/Services/SettingsService.cs @@ -14,7 +14,7 @@ namespace Artemis.Core.Services public PluginSetting GetSetting(string name, T defaultValue = default) { - return _pluginSettings.GetSetting(name, defaultValue); + return _pluginSettings.GetSetting(name, defaultValue!); } } diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index c1c556618..098812196 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -36,6 +36,40 @@ namespace Artemis.Core.Services public JsonSerializerSettings MementoSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All}; public JsonSerializerSettings ExportSettings { get; set; } = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.All, Formatting = Formatting.Indented}; + public ProfileDescriptor? GetLastActiveProfile(ProfileModule module) + { + List moduleProfiles = _profileRepository.GetByModuleId(module.Id); + if (!moduleProfiles.Any()) + return CreateProfileDescriptor(module, "Default"); + + ProfileEntity? profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault(); + return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity); + } + + private void SaveActiveProfile(ProfileModule module) + { + if (module.ActiveProfile == null) + return; + + List profileEntities = _profileRepository.GetByModuleId(module.Id); + foreach (ProfileEntity profileEntity in profileEntities) + { + profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id; + _profileRepository.Save(profileEntity); + } + } + + /// + /// Populates all missing LEDs on all currently active profiles + /// + /// + private void ActiveProfilesPopulateLeds(ArtemisSurface surface) + { + List profileModules = _pluginManagementService.GetFeaturesOfType(); + foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList()) + profileModule.ActiveProfile?.PopulateLeds(surface); // Avoid race condition + } + public List GetProfileDescriptors(ProfileModule module) { List profileEntities = _profileRepository.GetByModuleId(module.Id); @@ -52,14 +86,14 @@ namespace Artemis.Core.Services public void ActivateLastProfile(ProfileModule profileModule) { - ProfileDescriptor activeProfile = GetLastActiveProfile(profileModule); + ProfileDescriptor? activeProfile = GetLastActiveProfile(profileModule); if (activeProfile != null) ActivateProfile(activeProfile); } public async Task ActivateLastProfileAnimated(ProfileModule profileModule) { - ProfileDescriptor activeProfile = GetLastActiveProfile(profileModule); + ProfileDescriptor? activeProfile = GetLastActiveProfile(profileModule); if (activeProfile != null) await ActivateProfileAnimated(activeProfile); } @@ -196,7 +230,8 @@ namespace Artemis.Core.Services string top = profile.UndoStack.Pop(); string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); profile.RedoStack.Push(memento); - profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings); + profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings) + ?? throw new InvalidOperationException("Failed to deserialize memento"); profile.Load(); InstantiateProfile(profile); @@ -220,7 +255,8 @@ namespace Artemis.Core.Services string top = profile.RedoStack.Pop(); string memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); profile.UndoStack.Push(memento); - profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings); + profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings) + ?? throw new InvalidOperationException("Failed to deserialize memento"); profile.Load(); InstantiateProfile(profile); @@ -246,7 +282,9 @@ namespace Artemis.Core.Services public ProfileDescriptor ImportProfile(string json, ProfileModule profileModule) { - ProfileEntity profileEntity = JsonConvert.DeserializeObject(json, ExportSettings); + ProfileEntity? profileEntity = JsonConvert.DeserializeObject(json, ExportSettings); + if (profileEntity == null) + throw new ArtemisCoreException("Failed to import profile but JSON.NET threw no error :("); // Assign a new GUID to make sure it is unique in case of a previous import of the same content profileEntity.UpdateGuid(Guid.NewGuid()); @@ -256,40 +294,6 @@ namespace Artemis.Core.Services return new ProfileDescriptor(profileModule, profileEntity); } - public ProfileDescriptor GetLastActiveProfile(ProfileModule module) - { - List moduleProfiles = _profileRepository.GetByModuleId(module.Id); - if (!moduleProfiles.Any()) - return CreateProfileDescriptor(module, "Default"); - - ProfileEntity profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault(); - return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity); - } - - private void SaveActiveProfile(ProfileModule module) - { - if (module.ActiveProfile == null) - return; - - List profileEntities = _profileRepository.GetByModuleId(module.Id); - foreach (ProfileEntity profileEntity in profileEntities) - { - profileEntity.IsActive = module.ActiveProfile.EntityId == profileEntity.Id; - _profileRepository.Save(profileEntity); - } - } - - /// - /// Populates all missing LEDs on all currently active profiles - /// - /// - private void ActiveProfilesPopulateLeds(ArtemisSurface surface) - { - List profileModules = _pluginManagementService.GetFeaturesOfType(); - foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList()) - profileModule.ActiveProfile.PopulateLeds(surface); - } - #region Event handlers private void OnActiveSurfaceConfigurationSelected(object? sender, SurfaceConfigurationEventArgs e) diff --git a/src/Artemis.Core/Services/Storage/SurfaceService.cs b/src/Artemis.Core/Services/Storage/SurfaceService.cs index af1b2a349..b7340bd56 100644 --- a/src/Artemis.Core/Services/Storage/SurfaceService.cs +++ b/src/Artemis.Core/Services/Storage/SurfaceService.cs @@ -28,6 +28,8 @@ namespace Artemis.Core.Services _surfaceConfigurations = new List(); _renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5); + // LoadFromRepository is guaranteed to set the ActiveSurface + ActiveSurface = null!; LoadFromRepository(); _rgbService.DeviceLoaded += RgbServiceOnDeviceLoaded; @@ -61,6 +63,7 @@ namespace Artemis.Core.Services public void SetActiveSurfaceConfiguration(ArtemisSurface surface) { + if (surface == null) throw new ArgumentNullException(nameof(surface)); if (ActiveSurface == surface) return; @@ -81,11 +84,8 @@ namespace Artemis.Core.Services } // Apply the active surface entity to the devices - if (ActiveSurface != null) - { - foreach (ArtemisDevice device in ActiveSurface.Devices) - device.ApplyToRgbDevice(); - } + foreach (ArtemisDevice device in ActiveSurface.Devices) + device.ApplyToRgbDevice(); // Update the RGB service's graphics decorator to work with the new surface entity _rgbService.UpdateSurfaceLedGroup(); @@ -134,7 +134,7 @@ namespace Artemis.Core.Services ArtemisSurface surfaceConfiguration = new ArtemisSurface(_rgbService.Surface, surfaceEntity, _renderScaleSetting.Value); foreach (DeviceEntity position in surfaceEntity.DeviceEntities) { - 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) { DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(device); @@ -150,7 +150,7 @@ namespace Artemis.Core.Services } // When all surface configs are loaded, apply the active surface config - ArtemisSurface active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive); + ArtemisSurface? active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive); if (active != null) SetActiveSurfaceConfiguration(active); else @@ -170,13 +170,13 @@ namespace Artemis.Core.Services private void AddDeviceIfMissing(IRGBDevice rgbDevice, ArtemisSurface surface) { string deviceIdentifier = rgbDevice.GetDeviceIdentifier(); - ArtemisDevice device = surface.Devices.FirstOrDefault(d => d.DeviceEntity.DeviceIdentifier == deviceIdentifier); + ArtemisDevice? device = surface.Devices.FirstOrDefault(d => d.DeviceEntity.DeviceIdentifier == deviceIdentifier); if (device != null) return; // Find an existing device config and use that - DeviceEntity existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier); + DeviceEntity? existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier); if (existingDeviceConfig != null) { DeviceProvider deviceProvider = _pluginManagementService.GetDeviceProviderByDevice(rgbDevice); @@ -225,8 +225,8 @@ namespace Artemis.Core.Services #region Events - public event EventHandler ActiveSurfaceConfigurationSelected; - public event EventHandler SurfaceConfigurationUpdated; + public event EventHandler? ActiveSurfaceConfigurationSelected; + public event EventHandler? SurfaceConfigurationUpdated; protected virtual void OnActiveSurfaceConfigurationChanged(SurfaceConfigurationEventArgs e) { diff --git a/src/Artemis.Core/Stores/ConditionOperatorStore.cs b/src/Artemis.Core/Stores/ConditionOperatorStore.cs index 67edee873..ae1437079 100644 --- a/src/Artemis.Core/Stores/ConditionOperatorStore.cs +++ b/src/Artemis.Core/Stores/ConditionOperatorStore.cs @@ -10,6 +10,9 @@ namespace Artemis.Core public static ConditionOperatorRegistration Add(BaseConditionOperator conditionOperator) { + if (conditionOperator.Plugin == null) + throw new ArtemisCoreException("Cannot add a condition operator to the store that is not related to a plugin"); + ConditionOperatorRegistration registration; lock (Registrations) { @@ -74,8 +77,8 @@ namespace Artemis.Core #region Events - public static event EventHandler ConditionOperatorAdded; - public static event EventHandler ConditionOperatorRemoved; + public static event EventHandler? ConditionOperatorAdded; + public static event EventHandler? ConditionOperatorRemoved; private static void OnConditionOperatorAdded(ConditionOperatorStoreEvent e) { diff --git a/src/Artemis.Core/Stores/DataModelStore.cs b/src/Artemis.Core/Stores/DataModelStore.cs index 02e7a9603..7a6e14d03 100644 --- a/src/Artemis.Core/Stores/DataModelStore.cs +++ b/src/Artemis.Core/Stores/DataModelStore.cs @@ -57,8 +57,8 @@ namespace Artemis.Core #region Events - public static event EventHandler DataModelAdded; - public static event EventHandler DataModelRemoved; + public static event EventHandler? DataModelAdded; + public static event EventHandler? DataModelRemoved; private static void OnDataModelAdded(DataModelStoreEvent e) { diff --git a/src/Artemis.Core/Stores/LayerBrushStore.cs b/src/Artemis.Core/Stores/LayerBrushStore.cs index fb05da802..b0897fdd8 100644 --- a/src/Artemis.Core/Stores/LayerBrushStore.cs +++ b/src/Artemis.Core/Stores/LayerBrushStore.cs @@ -58,8 +58,8 @@ namespace Artemis.Core #region Events - public static event EventHandler LayerBrushAdded; - public static event EventHandler LayerBrushRemoved; + public static event EventHandler? LayerBrushAdded; + public static event EventHandler? LayerBrushRemoved; private static void OnLayerBrushAdded(LayerBrushStoreEvent e) { diff --git a/src/Artemis.Core/Stores/LayerEffectStore.cs b/src/Artemis.Core/Stores/LayerEffectStore.cs index ac3060bd9..65f2b1289 100644 --- a/src/Artemis.Core/Stores/LayerEffectStore.cs +++ b/src/Artemis.Core/Stores/LayerEffectStore.cs @@ -51,14 +51,14 @@ namespace Artemis.Core { lock (Registrations) { - return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType.Name == typeName); + return Registrations.FirstOrDefault(d => d.PluginFeature.Id == providerId && d.LayerEffectDescriptor.LayerEffectType?.Name == typeName); } } #region Events - public static event EventHandler LayerEffectAdded; - public static event EventHandler LayerEffectRemoved; + public static event EventHandler? LayerEffectAdded; + public static event EventHandler? LayerEffectRemoved; private static void OnLayerEffectAdded(LayerEffectStoreEvent e) { diff --git a/src/Artemis.Core/Utilities/CurrentProcessUtilities.cs b/src/Artemis.Core/Utilities/CurrentProcessUtilities.cs index e04096b22..99aa6a3d0 100644 --- a/src/Artemis.Core/Utilities/CurrentProcessUtilities.cs +++ b/src/Artemis.Core/Utilities/CurrentProcessUtilities.cs @@ -23,7 +23,7 @@ namespace Artemis.Core string arguments = "-Command \"& {Start-Sleep -s " + delay + "; (Get-Process 'Artemis.UI').kill()}"; // If restart is required, start the executable again after the process was killed if (restart) - arguments = "-Command \"& {Start-Sleep -s " + delay + "; (Get-Process 'Artemis.UI').kill(); Start-Process -FilePath '" + Process.GetCurrentProcess().MainModule.FileName + "'}\""; + arguments = "-Command \"& {Start-Sleep -s " + delay + "; (Get-Process 'Artemis.UI').kill(); Start-Process -FilePath '" + Process.GetCurrentProcess().MainModule!.FileName + "'}\""; ProcessStartInfo info = new ProcessStartInfo { @@ -45,7 +45,7 @@ namespace Artemis.Core /// /// The URL to open /// The process created to open the URL - public static Process OpenUrl(string url) + public static Process? OpenUrl(string url) { ProcessStartInfo processInfo = new ProcessStartInfo { @@ -61,7 +61,7 @@ namespace Artemis.Core /// internal static string GetCurrentLocation() { - return Process.GetCurrentProcess().MainModule.FileName; + return Process.GetCurrentProcess().MainModule!.FileName!; } #region Events diff --git a/src/Artemis.Core/Utilities/DeserializationLogger.cs b/src/Artemis.Core/Utilities/DeserializationLogger.cs index de4424def..f75542c87 100644 --- a/src/Artemis.Core/Utilities/DeserializationLogger.cs +++ b/src/Artemis.Core/Utilities/DeserializationLogger.cs @@ -6,7 +6,7 @@ namespace Artemis.Core { internal static class DeserializationLogger { - private static ILogger _logger; + private static ILogger? _logger; public static void Initialize(IKernel kernel) { @@ -15,7 +15,7 @@ namespace Artemis.Core public static void LogPredicateDeserializationFailure(DataModelConditionPredicate dataModelConditionPredicate, JsonException exception) { - _logger.Warning( + _logger?.Warning( exception, "Failed to deserialize display condition predicate {left} {operator} {right}", dataModelConditionPredicate.Entity.LeftPath?.Path, @@ -26,7 +26,7 @@ namespace Artemis.Core public static void LogModifierDeserializationFailure(string modifierName, JsonSerializationException exception) { - _logger.Warning(exception, "Failed to deserialize static parameter for modifier {modifierName}", modifierName); + _logger?.Warning(exception, "Failed to deserialize static parameter for modifier {modifierName}", modifierName); } } } diff --git a/src/Artemis.Core/Utilities/ExpressionUtilities.cs b/src/Artemis.Core/Utilities/ExpressionUtilities.cs index 12a0a31a8..4db041961 100644 --- a/src/Artemis.Core/Utilities/ExpressionUtilities.cs +++ b/src/Artemis.Core/Utilities/ExpressionUtilities.cs @@ -19,7 +19,7 @@ namespace Artemis.Core { // Create an expression that checks every part of the path for null // In the same iteration, create the accessor - Expression condition = null; + Expression? condition = null; foreach (string memberName in path.Split('.')) { BinaryExpression notNull = Expression.NotEqual(source, Expression.Constant(null)); diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs index 8030b10fb..604191a94 100644 --- a/src/Artemis.Core/Utilities/IntroAnimation.cs +++ b/src/Artemis.Core/Utilities/IntroAnimation.cs @@ -21,21 +21,19 @@ namespace Artemis.Core _logger = logger; _profileService = profileService; _surfaceService = surfaceService; - CreateIntroProfile(); + + AnimationProfile = CreateIntroProfile(); } public Profile AnimationProfile { get; set; } public void Render(double deltaTime, SKCanvas canvas) { - if (AnimationProfile == null) - return; - AnimationProfile.Update(deltaTime); AnimationProfile.Render(canvas); } - private void CreateIntroProfile() + private Profile CreateIntroProfile() { try { @@ -44,24 +42,24 @@ namespace Artemis.Core ProfileEntity profileEntity = JsonConvert.DeserializeObject(json); // Inject every LED on the surface into each layer foreach (LayerEntity profileEntityLayer in profileEntity.Layers) - { profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity { DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(), LedName = l.RgbLed.Id.ToString() })); - } Profile profile = new Profile(new DummyModule(), profileEntity); profile.Activate(_surfaceService.ActiveSurface); _profileService.InstantiateProfile(profile); - AnimationProfile = profile; + return profile; } catch (Exception e) { _logger.Warning(e, "Failed to load intro profile"); } + + return new Profile(new DummyModule(), "Intro"); } } diff --git a/src/Artemis.Core/Utilities/ReflectionUtilities.cs b/src/Artemis.Core/Utilities/ReflectionUtilities.cs index 7816f36bb..8b914996d 100644 --- a/src/Artemis.Core/Utilities/ReflectionUtilities.cs +++ b/src/Artemis.Core/Utilities/ReflectionUtilities.cs @@ -10,11 +10,11 @@ namespace Artemis.Core { Type type = typeof(TSource); - MemberExpression member = propertyLambda.Body as MemberExpression; + MemberExpression? member = propertyLambda.Body as MemberExpression; if (member == null) throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", propertyLambda)); - PropertyInfo propInfo = member.Member as PropertyInfo; + PropertyInfo? propInfo = member.Member as PropertyInfo; if (propInfo == null) throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", propertyLambda));