diff --git a/src/Artemis.Core/Plugins/Nodes/NodeProvider.cs b/src/Artemis.Core/Plugins/Nodes/NodeProvider.cs
new file mode 100644
index 000000000..64acd5612
--- /dev/null
+++ b/src/Artemis.Core/Plugins/Nodes/NodeProvider.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Reflection;
+using SkiaSharp;
+
+namespace Artemis.Core.Nodes;
+
+///
+/// Allows you to register one or more s usable by node scripts.
+///
+public abstract class NodeProvider : PluginFeature
+{
+ private readonly List _nodeDescriptors;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public NodeProvider()
+ {
+ _nodeDescriptors = new List();
+ NodeDescriptors = new ReadOnlyCollection(_nodeDescriptors);
+ Disabled += OnDisabled;
+ }
+
+ ///
+ /// A read-only collection of all nodes added with
+ ///
+ public ReadOnlyCollection NodeDescriptors { get; set; }
+
+ ///
+ /// Adds a node descriptor for a given node, so that it appears in the UI.
+ /// Note: You do not need to manually remove these on disable
+ ///
+ /// The type of the node you wish to register
+ protected void RegisterNodeType() where T : INode
+ {
+ RegisterNodeType(typeof(T));
+ }
+
+ ///
+ /// Adds a node descriptor for a given node, so that it appears in the UI.
+ /// Note: You do not need to manually remove these on disable
+ ///
+ /// The type of the node you wish to register
+ protected void RegisterNodeType(Type nodeType)
+ {
+ if (!IsEnabled)
+ throw new ArtemisPluginFeatureException(this, "Can only add a node descriptor when the plugin is enabled");
+ if (nodeType == null)
+ throw new ArgumentNullException(nameof(nodeType));
+ if (!nodeType.IsAssignableTo(typeof(INode)))
+ throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
+
+ NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute();
+ string name = nodeAttribute?.Name ?? nodeType.Name;
+ string description = nodeAttribute?.Description ?? string.Empty;
+ string category = nodeAttribute?.Category ?? string.Empty;
+ string helpUrl = nodeAttribute?.HelpUrl ?? string.Empty;
+
+ NodeData nodeData = new(this, nodeType, name, description, category, helpUrl, nodeAttribute?.InputType, nodeAttribute?.OutputType);
+ _nodeDescriptors.Add(nodeData);
+ NodeTypeStore.Add(nodeData);
+ }
+
+ protected TypeColorRegistration RegisterTypeColor(SKColor color)
+ {
+ return NodeTypeStore.AddColor(typeof(T), color, this);
+ }
+
+ private void OnDisabled(object? sender, EventArgs e)
+ {
+ // The store will clean up the registrations by itself, the plugin feature just needs to clear its own list
+ _nodeDescriptors.Clear();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/NodeService.cs b/src/Artemis.Core/Services/NodeService.cs
index 7cae6709b..96a0b617b 100644
--- a/src/Artemis.Core/Services/NodeService.cs
+++ b/src/Artemis.Core/Services/NodeService.cs
@@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Artemis.Storage.Entities.Profile.Nodes;
-using DryIoc;
using Newtonsoft.Json;
using SkiaSharp;
@@ -13,31 +11,8 @@ namespace Artemis.Core.Services;
internal class NodeService : INodeService
{
- #region Constants
-
- private static readonly Type TypeNode = typeof(INode);
-
- #endregion
-
- private readonly IContainer _container;
-
- #region Constructors
-
- public NodeService(IContainer container)
- {
- _container = container;
- }
-
- #endregion
-
- #region Properties & Fields
-
public IEnumerable AvailableNodes => NodeTypeStore.GetAll();
- #endregion
-
- #region Methods
-
///
public List GetRegisteredTypes()
{
@@ -53,7 +28,7 @@ internal class NodeService : INodeService
// Objects represent an input that can take any type, these are hardcoded white
if (type == typeof(object))
- return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePlugin);
+ return new TypeColorRegistration(type, new SKColor(255, 255, 255, 255), Constants.CorePluginFeature);
// Come up with a random color based on the type name that should be the same each time
MD5 md5Hasher = MD5.Create();
@@ -61,32 +36,7 @@ internal class NodeService : INodeService
int hash = BitConverter.ToInt32(hashed, 0);
SKColor baseColor = SKColor.FromHsl(hash % 255, 50 + hash % 50, 50);
- return new TypeColorRegistration(type, baseColor, Constants.CorePlugin);
- }
-
- public NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType)
- {
- if (plugin == null) throw new ArgumentNullException(nameof(plugin));
- if (nodeType == null) throw new ArgumentNullException(nameof(nodeType));
-
- if (!TypeNode.IsAssignableFrom(nodeType)) throw new ArgumentException("Node has to be a base type of the Node-Type.", nameof(nodeType));
-
- NodeAttribute? nodeAttribute = nodeType.GetCustomAttribute();
- string name = nodeAttribute?.Name ?? nodeType.Name;
- string description = nodeAttribute?.Description ?? string.Empty;
- string category = nodeAttribute?.Category ?? string.Empty;
- string helpUrl = nodeAttribute?.HelpUrl ?? string.Empty;
-
- NodeData nodeData = new(plugin, nodeType, name, description, category, helpUrl, nodeAttribute?.InputType, nodeAttribute?.OutputType, (s, e) => CreateNode(s, e, nodeType));
- return NodeTypeStore.Add(nodeData);
- }
-
- public TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color)
- {
- if (plugin == null) throw new ArgumentNullException(nameof(plugin));
- if (type == null) throw new ArgumentNullException(nameof(type));
-
- return NodeTypeStore.AddColor(type, color, plugin);
+ return new TypeColorRegistration(type, baseColor, Constants.CorePluginFeature);
}
public string ExportScript(NodeScript nodeScript)
@@ -103,33 +53,6 @@ internal class NodeService : INodeService
target.LoadFromEntity(entity);
}
-
- private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType)
- {
- INode node = _container.Resolve(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
- if (node is Node concreteNode)
- concreteNode.Container = _container;
-
- if (entity != null)
- {
- node.X = entity.X;
- node.Y = entity.Y;
- try
- {
- if (node is Node nodeImplementation)
- nodeImplementation.DeserializeStorage(entity.Storage);
- }
- catch
- {
- // ignored
- }
- }
-
- node.TryInitialize(script);
- return node;
- }
-
- #endregion
}
///
@@ -153,21 +76,6 @@ public interface INodeService : IArtemisService
///
TypeColorRegistration GetTypeColorRegistration(Type type);
- ///
- /// Registers a node of the provided
- ///
- /// The plugin the node belongs to
- /// The type of node to initialize
- NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType);
-
- ///
- /// Registers a type with a provided color for use in the node editor
- ///
- /// The plugin making the registration
- /// The type to associate the color with
- /// The color to display
- TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color);
-
///
/// Exports the provided node script to JSON.
///
diff --git a/src/Artemis.Core/Stores/NodeTypeStore.cs b/src/Artemis.Core/Stores/NodeTypeStore.cs
index 052cac84c..05c31519b 100644
--- a/src/Artemis.Core/Stores/NodeTypeStore.cs
+++ b/src/Artemis.Core/Stores/NodeTypeStore.cs
@@ -13,16 +13,13 @@ internal class NodeTypeStore
public static NodeTypeRegistration Add(NodeData nodeData)
{
- if (nodeData.Plugin == null)
- throw new ArtemisCoreException("Cannot add a data binding modifier type that is not associated with a plugin");
-
NodeTypeRegistration typeRegistration;
lock (Registrations)
{
if (Registrations.Any(r => r.NodeData == nodeData))
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{nodeData.Name}'");
- typeRegistration = new NodeTypeRegistration(nodeData, nodeData.Plugin) {IsInStore = true};
+ typeRegistration = new NodeTypeRegistration(nodeData, nodeData.Provider) {IsInStore = true};
Registrations.Add(typeRegistration);
}
@@ -60,24 +57,12 @@ internal class NodeTypeStore
}
}
- public static Plugin? GetPlugin(INode node)
- {
- Type nodeType = node.GetType();
- lock (Registrations)
- {
- return Registrations.FirstOrDefault(r => r.NodeData.Type == nodeType)?.Plugin;
- }
- }
-
- public static TypeColorRegistration AddColor(Type type, SKColor color, Plugin plugin)
+ public static TypeColorRegistration AddColor(Type type, SKColor color, PluginFeature pluginFeature)
{
TypeColorRegistration typeColorRegistration;
lock (ColorRegistrations)
{
- if (ColorRegistrations.Any(r => r.Type == type))
- throw new ArtemisCoreException($"Node color store already contains a color for '{type.Name}'");
-
- typeColorRegistration = new TypeColorRegistration(type, color, plugin) {IsInStore = true};
+ typeColorRegistration = new TypeColorRegistration(type, color, pluginFeature) {IsInStore = true};
ColorRegistrations.Add(typeColorRegistration);
}
diff --git a/src/Artemis.Core/Stores/Registrations/NodeTypeRegistration.cs b/src/Artemis.Core/Stores/Registrations/NodeTypeRegistration.cs
index 659eb3503..bc5064c63 100644
--- a/src/Artemis.Core/Stores/Registrations/NodeTypeRegistration.cs
+++ b/src/Artemis.Core/Stores/Registrations/NodeTypeRegistration.cs
@@ -9,12 +9,12 @@ namespace Artemis.Core;
///
public class NodeTypeRegistration
{
- internal NodeTypeRegistration(NodeData nodeData, Plugin plugin)
+ internal NodeTypeRegistration(NodeData nodeData, PluginFeature pluginFeature)
{
NodeData = nodeData;
- Plugin = plugin;
+ PluginFeature = pluginFeature;
- Plugin.Disabled += OnDisabled;
+ PluginFeature.Disabled += OnDisabled;
}
///
@@ -23,9 +23,9 @@ public class NodeTypeRegistration
public NodeData NodeData { get; }
///
- /// Gets the plugin the node is associated with
+ /// Gets the plugin feature the node is associated with
///
- public Plugin Plugin { get; }
+ public PluginFeature PluginFeature { get; }
///
/// Gets a boolean indicating whether the registration is in the internal Core store
@@ -39,12 +39,12 @@ public class NodeTypeRegistration
/// if the entity matches this registration; otherwise .
public bool MatchesEntity(NodeEntity entity)
{
- return Plugin.Guid == entity.PluginId && NodeData.Type.Name == entity.Type;
+ return PluginFeature.Id == entity.ProviderId && NodeData.Type.Name == entity.Type;
}
private void OnDisabled(object? sender, EventArgs e)
{
- Plugin.Disabled -= OnDisabled;
+ PluginFeature.Disabled -= OnDisabled;
if (IsInStore)
NodeTypeStore.Remove(this);
}
@@ -55,13 +55,13 @@ public class NodeTypeRegistration
///
public class TypeColorRegistration
{
- internal TypeColorRegistration(Type type, SKColor color, Plugin plugin)
+ internal TypeColorRegistration(Type type, SKColor color, PluginFeature pluginFeature)
{
Type = type;
Color = color;
- Plugin = plugin;
+ PluginFeature = pluginFeature;
- Plugin.Disabled += OnDisabled;
+ PluginFeature.Disabled += OnDisabled;
}
///
@@ -80,9 +80,9 @@ public class TypeColorRegistration
public SKColor DarkenedColor => Color.Darken(0.35f);
///
- /// Gets the plugin type color is associated with
+ /// Gets the plugin feature this type color is associated with
///
- public Plugin Plugin { get; }
+ public PluginFeature PluginFeature { get; }
///
/// Gets a boolean indicating whether the registration is in the internal Core store
@@ -91,7 +91,7 @@ public class TypeColorRegistration
private void OnDisabled(object? sender, EventArgs e)
{
- Plugin.Disabled -= OnDisabled;
+ PluginFeature.Disabled -= OnDisabled;
if (IsInStore)
NodeTypeStore.RemoveColor(this);
}
diff --git a/src/Artemis.Core/VisualScripting/Interfaces/INode.cs b/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
index 3bc2521fb..abe7ecedd 100644
--- a/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
+++ b/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
@@ -14,6 +14,11 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
/// Gets or sets the ID of the node.
///
Guid Id { get; set; }
+
+ ///
+ /// Gets
+ ///
+ NodeData? NodeData { get; set; }
///
/// Gets the name of the node
diff --git a/src/Artemis.Core/VisualScripting/NodeData.cs b/src/Artemis.Core/VisualScripting/NodeData.cs
index 5b0c678e2..05359dba0 100644
--- a/src/Artemis.Core/VisualScripting/NodeData.cs
+++ b/src/Artemis.Core/VisualScripting/NodeData.cs
@@ -1,4 +1,5 @@
using System;
+using Artemis.Core.Nodes;
using Artemis.Storage.Entities.Profile.Nodes;
namespace Artemis.Core;
@@ -10,9 +11,9 @@ public class NodeData
{
#region Constructors
- internal NodeData(Plugin plugin, Type type, string name, string description, string category, string helpUrl, Type? inputType, Type? outputType, Func create)
+ internal NodeData(NodeProvider provider, Type type, string name, string description, string category, string helpUrl, Type? inputType, Type? outputType)
{
- Plugin = plugin;
+ Provider = provider;
Type = type;
Name = name;
Description = description;
@@ -20,7 +21,6 @@ public class NodeData
HelpUrl = helpUrl;
InputType = inputType;
OutputType = outputType;
- _create = create;
}
#endregion
@@ -35,14 +35,31 @@ public class NodeData
/// The returning node of type
public INode CreateNode(INodeScript script, NodeEntity? entity)
{
- INode node = _create(script, entity);
+ INode node = (INode) Provider.Plugin.Resolve(Type);
+ node.NodeData = this;
if (string.IsNullOrWhiteSpace(node.Name))
node.Name = Name;
if (string.IsNullOrWhiteSpace(node.Description))
node.Description = Description;
if (string.IsNullOrWhiteSpace(node.HelpUrl))
node.HelpUrl = HelpUrl;
+
+ if (entity != null)
+ {
+ node.X = entity.X;
+ node.Y = entity.Y;
+ try
+ {
+ if (node is Node nodeImplementation)
+ nodeImplementation.DeserializeStorage(entity.Storage);
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ node.TryInitialize(script);
return node;
}
@@ -91,11 +108,11 @@ public class NodeData
}
#region Properties & Fields
-
+
///
- /// Gets the plugin that provided this node data
+ /// Gets the node provider that provided this node data
///
- public Plugin Plugin { get; }
+ public NodeProvider Provider { get; }
///
/// Gets the type of this data represents
diff --git a/src/Artemis.Core/VisualScripting/NodeScript.cs b/src/Artemis.Core/VisualScripting/NodeScript.cs
index 7672f3504..e8ccddd4a 100644
--- a/src/Artemis.Core/VisualScripting/NodeScript.cs
+++ b/src/Artemis.Core/VisualScripting/NodeScript.cs
@@ -161,6 +161,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
{
foreach (INode node in _nodes)
{
+ // ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
if (node is IDisposable disposable)
disposable.Dispose();
}
@@ -181,6 +182,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
foreach (INode removeNode in removeNodes)
{
RemoveNode(removeNode);
+ // ReSharper disable once SuspiciousTypeConversion.Global - Provided by plugins
if (removeNode is IDisposable disposable)
disposable.Dispose();
}
@@ -312,7 +314,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
NodeEntity nodeEntity = new()
{
Id = node.Id,
- PluginId = NodeTypeStore.GetPlugin(node)?.Guid ?? Constants.CorePlugin.Guid,
+ ProviderId = node.NodeData?.Provider.Id ?? Constants.CorePluginFeature.Id,
Type = node.GetType().Name,
X = node.X,
Y = node.Y,
diff --git a/src/Artemis.Core/VisualScripting/Nodes/Node.cs b/src/Artemis.Core/VisualScripting/Nodes/Node.cs
index 5ff7dd254..3780e6e07 100644
--- a/src/Artemis.Core/VisualScripting/Nodes/Node.cs
+++ b/src/Artemis.Core/VisualScripting/Nodes/Node.cs
@@ -41,6 +41,9 @@ public abstract class Node : BreakableModel, INode
set => SetAndNotify(ref _id, value);
}
+ ///
+ public NodeData NodeData { get; set; }
+
private string _name;
///
@@ -104,8 +107,6 @@ public abstract class Node : BreakableModel, INode
///
public override string BrokenDisplayName => Name;
- internal IContainer Container { get; set; } = null!;
-
#endregion
#region Construtors
diff --git a/src/Artemis.Core/VisualScripting/Nodes/NodeTStorageTViewModel.cs b/src/Artemis.Core/VisualScripting/Nodes/NodeTStorageTViewModel.cs
index 98965c154..2b84a619e 100644
--- a/src/Artemis.Core/VisualScripting/Nodes/NodeTStorageTViewModel.cs
+++ b/src/Artemis.Core/VisualScripting/Nodes/NodeTStorageTViewModel.cs
@@ -25,7 +25,7 @@ public abstract class Node : Node, ICustomViewMo
///
public virtual TViewModel GetViewModel(NodeScript nodeScript)
{
- return Container.Resolve(args: new object[] {this, nodeScript});
+ return NodeData.Provider.Plugin.Container.Resolve(args: new object[] {this, nodeScript});
}
///
diff --git a/src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs b/src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs
index 5683e5c0f..63d98770b 100644
--- a/src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs
@@ -15,7 +15,7 @@ public class NodeEntity
{
Id = nodeEntity.Id;
Type = nodeEntity.Type;
- PluginId = nodeEntity.PluginId;
+ ProviderId = nodeEntity.ProviderId;
Name = nodeEntity.Name;
Description = nodeEntity.Description;
@@ -29,7 +29,7 @@ public class NodeEntity
public Guid Id { get; set; }
public string Type { get; set; }
- public Guid PluginId { get; set; }
+ public string ProviderId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
diff --git a/src/Artemis.Storage/Migrations/M0021GradientNodes.cs b/src/Artemis.Storage/Migrations/M0021GradientNodes.cs
index dba1e8956..cfb735f1c 100644
--- a/src/Artemis.Storage/Migrations/M0021GradientNodes.cs
+++ b/src/Artemis.Storage/Migrations/M0021GradientNodes.cs
@@ -22,7 +22,7 @@ public class M0021GradientNodes : IStorageMigration
{
Id = Guid.NewGuid(),
Type = "ColorGradientNode",
- PluginId = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"),
+ ProviderId = "Artemis.Plugins.Nodes.General.GeneralNodesProvider-d9e1ee78",
Name = "Color Gradient",
Description = "Outputs a color gradient with the given colors",
X = exitNode.X,
diff --git a/src/Artemis.Storage/Repositories/PluginRepository.cs b/src/Artemis.Storage/Repositories/PluginRepository.cs
index 6d659af47..c11673597 100644
--- a/src/Artemis.Storage/Repositories/PluginRepository.cs
+++ b/src/Artemis.Storage/Repositories/PluginRepository.cs
@@ -29,7 +29,6 @@ internal class PluginRepository : IPluginRepository
public void SavePlugin(PluginEntity pluginEntity)
{
_repository.Upsert(pluginEntity);
- _repository.Database.Checkpoint();
}
public void AddSetting(PluginSettingEntity pluginSettingEntity)
diff --git a/src/Artemis.UI.Shared/Services/DataModelUIService.cs b/src/Artemis.UI.Shared/Services/DataModelUIService.cs
index f7fe353f6..fc8569711 100644
--- a/src/Artemis.UI.Shared/Services/DataModelUIService.cs
+++ b/src/Artemis.UI.Shared/Services/DataModelUIService.cs
@@ -18,16 +18,20 @@ internal class DataModelUIService : IDataModelUIService
private readonly IContainer _container;
private readonly List _registeredDataModelDisplays;
private readonly List _registeredDataModelEditors;
+ private readonly PluginSetting _showFullPaths;
+ private readonly PluginSetting _showDataModelValues;
- public DataModelUIService(IDataModelService dataModelService, IContainer container)
+ public DataModelUIService(IDataModelService dataModelService, IContainer container, ISettingsService settingsService)
{
_dataModelService = dataModelService;
_container = container;
_registeredDataModelEditors = new List();
_registeredDataModelDisplays = new List();
-
+
RegisteredDataModelEditors = new ReadOnlyCollection(_registeredDataModelEditors);
RegisteredDataModelDisplays = new ReadOnlyCollection(_registeredDataModelDisplays);
+ ShowFullPaths = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
+ ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues", false);
}
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute? description, object? initialValue)
@@ -43,7 +47,9 @@ internal class DataModelUIService : IDataModelUIService
public IReadOnlyCollection RegisteredDataModelEditors { get; }
public IReadOnlyCollection RegisteredDataModelDisplays { get; }
-
+ public PluginSetting ShowFullPaths { get; }
+ public PluginSetting ShowDataModelValues { get; }
+
public DataModelPropertiesViewModel GetMainDataModelVisualization()
{
DataModelPropertiesViewModel viewModel = new(null, null, null);
diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs
index a91766a44..b70c5e8af 100644
--- a/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs
+++ b/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs
@@ -104,4 +104,14 @@ public interface IDataModelUIService : IArtemisSharedUIService
/// A function to call whenever the input was updated (submitted or not)
/// The most appropriate input view model for the provided
DataModelInputViewModel? GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute? description, object? initialValue, Action