diff --git a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPredicate.cs index a800272d8..8424d3876 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPredicate.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DataModelConditionPredicate.cs @@ -115,7 +115,7 @@ namespace Artemis.Core try { - rightSideValue = JsonConvert.DeserializeObject(Entity.RightStaticValue, leftSideType, Constants.JsonConvertSettings); + rightSideValue = CoreJson.DeserializeObject(Entity.RightStaticValue, leftSideType); } // If deserialization fails, use the type's default catch (JsonSerializationException e) @@ -129,7 +129,7 @@ namespace Artemis.Core else { // Hope for the best... - UpdateRightSideStatic(JsonConvert.DeserializeObject(Entity.RightStaticValue, Constants.JsonConvertSettings)); + UpdateRightSideStatic(CoreJson.DeserializeObject(Entity.RightStaticValue)); } } catch (JsonReaderException e) @@ -349,7 +349,7 @@ namespace Artemis.Core RightPath?.Save(); Entity.RightPath = RightPath?.Entity; - Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue, Constants.JsonConvertSettings); + Entity.RightStaticValue = CoreJson.SerializeObject(RightStaticValue); if (Operator?.Plugin != null) { diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs index c726f261a..a494c1589 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Conditional/DataBindingCondition.cs @@ -1,6 +1,5 @@ using System; using Artemis.Storage.Entities.Profile.DataBindings; -using Newtonsoft.Json; namespace Artemis.Core { @@ -74,7 +73,7 @@ namespace Artemis.Core Entity.Condition = Condition.Entity; Condition.Save(); - Entity.Value = JsonConvert.SerializeObject(Value, Constants.JsonConvertSettings); + Entity.Value = CoreJson.SerializeObject(Value); Entity.Order = Order; } @@ -88,7 +87,7 @@ namespace Artemis.Core ? new DataModelConditionGroup(null, Entity.Condition) : new DataModelConditionGroup(null); - Value = (Entity.Value == null ? default : JsonConvert.DeserializeObject(Entity.Value, Constants.JsonConvertSettings))!; + Value = (Entity.Value == null ? default : CoreJson.DeserializeObject(Entity.Value))!; Order = Entity.Order; } diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs index ca14351a6..1b11249c9 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/Direct/DataBindingModifier.cs @@ -112,30 +112,6 @@ namespace Artemis.Core ValidateParameter(); } - private void ValidateParameter() - { - if (ModifierType == null) - return; - - if (ParameterType == ProfileRightSideType.Dynamic) - { - if (ParameterPath == null || !ParameterPath.IsValid) - return; - - Type parameterType = ParameterPath.GetPropertyType()!; - if (!ModifierType.SupportsType(parameterType, ModifierTypePart.Parameter)) - UpdateParameterDynamic(null); - } - else - { - if (ParameterStaticValue == null) - return; - - if (!ModifierType.SupportsType(ParameterStaticValue.GetType(), ModifierTypePart.Parameter)) - UpdateParameterStatic(null); - } - } - /// /// Updates the parameter of the modifier and makes the modifier dynamic /// @@ -181,6 +157,30 @@ namespace Artemis.Core ParameterStaticValue = null; } + private void ValidateParameter() + { + if (ModifierType == null) + return; + + if (ParameterType == ProfileRightSideType.Dynamic) + { + if (ParameterPath == null || !ParameterPath.IsValid) + return; + + Type parameterType = ParameterPath.GetPropertyType()!; + if (!ModifierType.SupportsType(parameterType, ModifierTypePart.Parameter)) + UpdateParameterDynamic(null); + } + else + { + if (ParameterStaticValue == null) + return; + + if (!ModifierType.SupportsType(ParameterStaticValue.GetType(), ModifierTypePart.Parameter)) + UpdateParameterStatic(null); + } + } + private void Initialize() { DataBindingModifierTypeStore.DataBindingModifierAdded += DataBindingModifierTypeStoreOnDataBindingModifierAdded; @@ -208,9 +208,9 @@ namespace Artemis.Core try { - staticValue = parameterType != null - ? JsonConvert.DeserializeObject(Entity.ParameterStaticValue, parameterType, Constants.JsonConvertSettings) - : JsonConvert.DeserializeObject(Entity.ParameterStaticValue, Constants.JsonConvertSettings); + staticValue = parameterType != null + ? CoreJson.DeserializeObject(Entity.ParameterStaticValue, parameterType) + : CoreJson.DeserializeObject(Entity.ParameterStaticValue); } // If deserialization fails, use the type's default catch (JsonSerializationException e) @@ -252,7 +252,7 @@ namespace Artemis.Core ParameterPath?.Save(); Entity.ParameterPath = ParameterPath?.Entity; - Entity.ParameterStaticValue = JsonConvert.SerializeObject(ParameterStaticValue, Constants.JsonConvertSettings); + Entity.ParameterStaticValue = CoreJson.SerializeObject(ParameterStaticValue); } /// diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs index c811b9b11..93867bd08 100644 --- a/src/Artemis.Core/Models/Profile/Folder.cs +++ b/src/Artemis.Core/Models/Profile/Folder.cs @@ -122,9 +122,7 @@ namespace Artemis.Core if (Parent == null) throw new ArtemisCoreException("Cannot create a copy of a folder without a parent"); - FolderEntity entityCopy = JsonConvert.DeserializeObject( - JsonConvert.SerializeObject(FolderEntity, Constants.JsonConvertTypedSettings), Constants.JsonConvertTypedSettings - )!; + FolderEntity entityCopy = CoreJson.DeserializeObject(CoreJson.SerializeObject(FolderEntity, true), true)!; entityCopy.Id = Guid.NewGuid(); entityCopy.Name += " - Copy"; diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index c776591b7..9064e03ef 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -123,9 +123,7 @@ namespace Artemis.Core if (Parent == null) throw new ArtemisCoreException("Cannot create a copy of a layer without a parent"); - LayerEntity entityCopy = JsonConvert.DeserializeObject( - JsonConvert.SerializeObject(LayerEntity, Constants.JsonConvertTypedSettings), Constants.JsonConvertTypedSettings - )!; + LayerEntity entityCopy = CoreJson.DeserializeObject(CoreJson.SerializeObject(LayerEntity, true), true)!; entityCopy.Id = Guid.NewGuid(); entityCopy.Name += " - Copy"; diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 0c17692a5..64a7e1f34 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -208,9 +208,8 @@ namespace Artemis.Core if (_disposed) throw new ObjectDisposedException("LayerProperty"); - string json = JsonConvert.SerializeObject(DefaultValue, Constants.JsonConvertTypedSettings); - - SetCurrentValue(JsonConvert.DeserializeObject(json), time); + string json = CoreJson.SerializeObject(DefaultValue, true); + SetCurrentValue(CoreJson.DeserializeObject(json)!, time); } private void ReapplyUpdate() @@ -512,7 +511,7 @@ namespace Artemis.Core try { if (Entity.Value != null) - BaseValue = JsonConvert.DeserializeObject(Entity.Value, Constants.JsonConvertSettings)!; + BaseValue = CoreJson.DeserializeObject(Entity.Value)!; } catch (JsonException) { @@ -528,7 +527,7 @@ namespace Artemis.Core _keyframes.AddRange(Entity.KeyframeEntities .Where(k => k.Position <= ProfileElement.Timeline.Length) .Select(k => new LayerPropertyKeyframe( - JsonConvert.DeserializeObject(k.Value, Constants.JsonConvertSettings)!, k.Position, (Easings.Functions) k.EasingFunction, this + CoreJson.DeserializeObject(k.Value)!, k.Position, (Easings.Functions) k.EasingFunction, this )) ); } @@ -557,12 +556,12 @@ namespace Artemis.Core if (!_isInitialized) throw new ArtemisCoreException("Layer property is not yet initialized"); - Entity.Value = JsonConvert.SerializeObject(BaseValue, Constants.JsonConvertSettings); + Entity.Value = CoreJson.SerializeObject(BaseValue); Entity.KeyframesEnabled = KeyframesEnabled; Entity.KeyframeEntities.Clear(); Entity.KeyframeEntities.AddRange(Keyframes.Select(k => new KeyframeEntity { - Value = JsonConvert.SerializeObject(k.Value, Constants.JsonConvertSettings), + Value = CoreJson.SerializeObject(k.Value), Position = k.Position, EasingFunction = (int) k.EasingFunction })); diff --git a/src/Artemis.Core/Plugins/Settings/PluginSetting.cs b/src/Artemis.Core/Plugins/Settings/PluginSetting.cs index 006df6970..eb8ec0433 100644 --- a/src/Artemis.Core/Plugins/Settings/PluginSetting.cs +++ b/src/Artemis.Core/Plugins/Settings/PluginSetting.cs @@ -29,7 +29,7 @@ namespace Artemis.Core Name = pluginSettingEntity.Name; try { - _value = JsonConvert.DeserializeObject(pluginSettingEntity.Value, Constants.JsonConvertSettings); + _value = CoreJson.DeserializeObject(pluginSettingEntity.Value)!; } catch (JsonReaderException) { @@ -66,7 +66,7 @@ namespace Artemis.Core /// /// Determines whether the setting has been changed /// - public bool HasChanged => JsonConvert.SerializeObject(Value, Constants.JsonConvertSettings) != _pluginSettingEntity.Value; + public bool HasChanged => CoreJson.SerializeObject(Value) != _pluginSettingEntity.Value; /// /// Gets or sets whether changes must automatically be saved @@ -79,7 +79,7 @@ namespace Artemis.Core /// public void RejectChanges() { - Value = JsonConvert.DeserializeObject(_pluginSettingEntity.Value, Constants.JsonConvertSettings); + Value = CoreJson.DeserializeObject(_pluginSettingEntity.Value); } /// @@ -90,7 +90,7 @@ namespace Artemis.Core if (!HasChanged) return; - _pluginSettingEntity.Value = JsonConvert.SerializeObject(Value, Constants.JsonConvertSettings); + _pluginSettingEntity.Value = CoreJson.SerializeObject(Value); _pluginRepository.SaveSetting(_pluginSettingEntity); OnSettingSaved(); } diff --git a/src/Artemis.Core/Plugins/Settings/PluginSettings.cs b/src/Artemis.Core/Plugins/Settings/PluginSettings.cs index 075a70d29..df940d21c 100644 --- a/src/Artemis.Core/Plugins/Settings/PluginSettings.cs +++ b/src/Artemis.Core/Plugins/Settings/PluginSettings.cs @@ -49,7 +49,7 @@ namespace Artemis.Core { Name = name, PluginGuid = Plugin.Guid, - Value = JsonConvert.SerializeObject(defaultValue, Constants.JsonConvertSettings) + Value = CoreJson.SerializeObject(defaultValue) }; _pluginRepository.AddSetting(settingEntity); } diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index d916d5d4d..df78d2a3b 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -5,10 +5,8 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; using Artemis.Core.DataModelExpansions; -using Artemis.Core.JsonConverters; using Artemis.Core.Ninject; using Artemis.Storage; -using Newtonsoft.Json; using Ninject; using RGB.NET.Core; using Serilog; @@ -114,7 +112,11 @@ namespace Artemis.Core.Services IntroAnimation intro = new IntroAnimation(_logger, _profileService, _surfaceService); // Draw a white overlay over the device - void DrawOverlay(object? sender, FrameRenderingEventArgs args) => intro.Render(args.DeltaTime, args.Canvas); + void DrawOverlay(object? sender, FrameRenderingEventArgs args) + { + intro.Render(args.DeltaTime, args.Canvas); + } + FrameRendering += DrawOverlay; // Stop rendering after the profile finishes (take 1 second extra in case of slow updates) @@ -181,11 +183,9 @@ namespace Artemis.Core.Services using SKCanvas canvas = new SKCanvas(_rgbService.BitmapBrush.Bitmap); canvas.Clear(new SKColor(0, 0, 0)); if (!ModuleRenderingDisabled) - { // While non-activated modules may be updated above if they expand the main data model, they may never render foreach (Module module in modules.Where(m => m.IsActivated)) module.InternalRender(args.DeltaTime, _surfaceService.ActiveSurface, canvas, _rgbService.BitmapBrush.Bitmap.Info); - } OnFrameRendering(new FrameRenderingEventArgs(canvas, args.DeltaTime, _rgbService.Surface)); } diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index 44ddf7f5f..6f3edc843 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -10,7 +10,6 @@ using Artemis.Core.Ninject; using Artemis.Storage.Entities.Plugins; using Artemis.Storage.Repositories.Interfaces; using McMaster.NETCore.Plugins; -using Newtonsoft.Json; using Ninject; using Ninject.Extensions.ChildKernel; using Ninject.Parameters; @@ -78,7 +77,7 @@ namespace Artemis.Core.Services throw new ArtemisPluginException("Couldn't find a plugin.json in " + zipFile.FullName); using StreamReader reader = new StreamReader(metaDataFileEntry.Open()); - PluginInfo builtInPluginInfo = JsonConvert.DeserializeObject(reader.ReadToEnd(), Constants.JsonConvertSettings)!; + PluginInfo builtInPluginInfo = CoreJson.DeserializeObject(reader.ReadToEnd())!; // Find the matching plugin in the plugin folder DirectoryInfo? match = pluginDirectory.EnumerateDirectories().FirstOrDefault(d => d.Name == Path.GetFileNameWithoutExtension(zipFile.Name)); @@ -99,7 +98,7 @@ namespace Artemis.Core.Services try { // Compare versions, copy if the same when debugging - PluginInfo pluginInfo = JsonConvert.DeserializeObject(File.ReadAllText(metadataFile), Constants.JsonConvertSettings)!; + PluginInfo pluginInfo = CoreJson.DeserializeObject(File.ReadAllText(metadataFile))!; if (builtInPluginInfo.Version > pluginInfo.Version) { @@ -186,7 +185,6 @@ namespace Artemis.Core.Services // Load the plugin assemblies into the plugin context DirectoryInfo pluginDirectory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins")); foreach (DirectoryInfo subDirectory in pluginDirectory.EnumerateDirectories()) - { try { Plugin plugin = LoadPlugin(subDirectory); @@ -197,7 +195,6 @@ namespace Artemis.Core.Services { _logger.Warning(new ArtemisPluginException("Failed to load plugin", e), "Plugin exception"); } - } LoadingPlugins = false; } @@ -226,7 +223,7 @@ namespace Artemis.Core.Services _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 pluginInfo = JsonConvert.DeserializeObject(File.ReadAllText(metadataFile), Constants.JsonConvertSettings)!; + PluginInfo pluginInfo = CoreJson.DeserializeObject(File.ReadAllText(metadataFile))!; if (pluginInfo.Guid == Constants.CorePluginInfo.Guid) throw new ArtemisPluginException($"Plugin cannot use reserved GUID {pluginInfo.Guid}"); @@ -300,7 +297,7 @@ namespace Artemis.Core.Services throw new ArtemisPluginException( plugin, "Failed to initialize the plugin assembly", - new AggregateException(e.LoaderExceptions.Where(le => le != null).Cast().ToArray()) + new AggregateException(e.LoaderExceptions.Where(le => le != null).ToArray()) ); } @@ -310,7 +307,6 @@ namespace Artemis.Core.Services // 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 { plugin.Kernel.Bind(featureType).ToSelf().InSingletonScope(); @@ -328,11 +324,9 @@ namespace Artemis.Core.Services { throw new ArtemisPluginException(plugin, "Failed to instantiate feature", e); } - } // Activate plugins after they are all loaded foreach (PluginFeature pluginFeature in plugin.Features.Where(i => i.Entity.IsEnabled)) - { try { EnablePluginFeature(pluginFeature, false, !ignorePluginLock); @@ -341,7 +335,6 @@ namespace Artemis.Core.Services { // ignored, logged in EnablePluginFeature } - } if (saveState) { @@ -483,10 +476,8 @@ namespace Artemis.Core.Services private void SavePlugin(Plugin plugin) { foreach (PluginFeature pluginFeature in plugin.Features) - { if (plugin.Entity.Features.All(i => i.Type != pluginFeature.GetType().FullName)) plugin.Entity.Features.Add(pluginFeature.Entity); - } _pluginRepository.SavePlugin(plugin.Entity); } diff --git a/src/Artemis.Core/Utilities/CoreJson.cs b/src/Artemis.Core/Utilities/CoreJson.cs new file mode 100644 index 000000000..b4e0ff0db --- /dev/null +++ b/src/Artemis.Core/Utilities/CoreJson.cs @@ -0,0 +1,69 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; + +namespace Artemis.Core +{ + internal static class CoreJson + { + #region Serialize + + /// + /// Serializes the specified object to a JSON string. + /// + /// The object to serialize. + /// If set to true sets TypeNameHandling to + /// A JSON string representation of the object. + [DebuggerStepThrough] + public static string SerializeObject(object? value, bool handleTypeNames = false) + { + return JsonConvert.SerializeObject(value, handleTypeNames ? Constants.JsonConvertTypedSettings : Constants.JsonConvertSettings); + } + + #endregion + + #region Deserialize + + /// + /// Deserializes the JSON to a .NET object. + /// + /// The JSON to deserialize. + /// If set to true sets TypeNameHandling to + /// The deserialized object from the JSON string. + [DebuggerStepThrough] + public static object? DeserializeObject(string value, bool handleTypeNames = false) + { + return JsonConvert.DeserializeObject(value, handleTypeNames ? Constants.JsonConvertTypedSettings : Constants.JsonConvertSettings); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The JSON to deserialize. + /// The of object being deserialized. + /// If set to true sets TypeNameHandling to + /// The deserialized object from the JSON string. + [DebuggerStepThrough] + public static object? DeserializeObject(string value, Type type, bool handleTypeNames = false) + { + return JsonConvert.DeserializeObject(value, type, handleTypeNames ? Constants.JsonConvertTypedSettings : Constants.JsonConvertSettings); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// If set to true sets TypeNameHandling to + /// The deserialized object from the JSON string. + [DebuggerStepThrough] + [return: MaybeNull] + public static T DeserializeObject(string value, bool handleTypeNames = false) + { + return JsonConvert.DeserializeObject(value, handleTypeNames ? Constants.JsonConvertTypedSettings : Constants.JsonConvertSettings); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs index 56777015f..128b6a864 100644 --- a/src/Artemis.Core/Utilities/IntroAnimation.cs +++ b/src/Artemis.Core/Utilities/IntroAnimation.cs @@ -4,7 +4,6 @@ using System.Linq; using Artemis.Core.Modules; using Artemis.Core.Services; using Artemis.Storage.Entities.Profile; -using Newtonsoft.Json; using Serilog; using SkiaSharp; @@ -39,7 +38,7 @@ namespace Artemis.Core { // Load the intro profile from JSON into a ProfileEntity string json = File.ReadAllText(Path.Combine(Constants.ApplicationFolder, "Resources", "intro-profile.json")); - ProfileEntity profileEntity = JsonConvert.DeserializeObject(json, Constants.JsonConvertSettings)!; + ProfileEntity profileEntity = CoreJson.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