Core - Added node provider plugin feature type

Visual Scripting - Removed all nodes
This commit is contained in:
RobertBeekman authored and Robert committed 2024-02-22 19:42:25 +01:00
1 parent f0e9581fe1
commit 39d7d8132f
170 files changed
+193 -6434

No files matched your search

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using SkiaSharp;
namespace Artemis.Core.Nodes;
/// <summary>
/// Allows you to register one or more <see cref="INode" />s usable by node scripts.
/// </summary>
public abstract class NodeProvider : PluginFeature
{
private readonly List<NodeData> _nodeDescriptors;
/// <summary>
/// Creates a new instance of the <see cref="NodeProvider"/> class.
/// </summary>
public NodeProvider()
{
_nodeDescriptors = new List<NodeData>();
NodeDescriptors = new ReadOnlyCollection<NodeData>(_nodeDescriptors);
Disabled += OnDisabled;
}
/// <summary>
/// A read-only collection of all nodes added with <see cref="RegisterNodeType{T}" />
/// </summary>
public ReadOnlyCollection<NodeData> NodeDescriptors { get; set; }
/// <summary>
/// Adds a node descriptor for a given node, so that it appears in the UI.
/// <para>Note: You do not need to manually remove these on disable</para>
/// </summary>
/// <typeparam name="T">The type of the node you wish to register</typeparam>
protected void RegisterNodeType<T>() where T : INode
{
RegisterNodeType(typeof(T));
}
/// <summary>
/// Adds a node descriptor for a given node, so that it appears in the UI.
/// <para>Note: You do not need to manually remove these on disable</para>
/// </summary>
/// <param name="nodeType">The type of the node you wish to register</param>
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<NodeAttribute>();
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<T>(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();
}
}
+2 -94
View File
@@ -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<NodeData> AvailableNodes => NodeTypeStore.GetAll();
#endregion
#region Methods
/// <inheritdoc />
public List<Type> 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<NodeAttribute>();
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
}
/// <summary>
@@ -153,21 +76,6 @@ public interface INodeService : IArtemisService
/// </summary>
TypeColorRegistration GetTypeColorRegistration(Type type);
/// <summary>
/// Registers a node of the provided <paramref name="nodeType" />
/// </summary>
/// <param name="plugin">The plugin the node belongs to</param>
/// <param name="nodeType">The type of node to initialize</param>
NodeTypeRegistration RegisterNodeType(Plugin plugin, Type nodeType);
/// <summary>
/// Registers a type with a provided color for use in the node editor
/// </summary>
/// <param name="plugin">The plugin making the registration</param>
/// <param name="type">The type to associate the color with</param>
/// <param name="color">The color to display</param>
TypeColorRegistration RegisterTypeColor(Plugin plugin, Type type, SKColor color);
/// <summary>
/// Exports the provided node script to JSON.
/// </summary>
+3 -18
View File
@@ -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);
}
@@ -9,12 +9,12 @@ namespace Artemis.Core;
/// </summary>
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;
}
/// <summary>
@@ -23,9 +23,9 @@ public class NodeTypeRegistration
public NodeData NodeData { get; }
/// <summary>
/// Gets the plugin the node is associated with
/// Gets the plugin feature the node is associated with
/// </summary>
public Plugin Plugin { get; }
public PluginFeature PluginFeature { get; }
/// <summary>
/// Gets a boolean indicating whether the registration is in the internal Core store
@@ -39,12 +39,12 @@ public class NodeTypeRegistration
/// <returns><see langword="true" /> if the entity matches this registration; otherwise <see langword="false" />.</returns>
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
/// </summary>
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;
}
/// <summary>
@@ -80,9 +80,9 @@ public class TypeColorRegistration
public SKColor DarkenedColor => Color.Darken(0.35f);
/// <summary>
/// Gets the plugin type color is associated with
/// Gets the plugin feature this type color is associated with
/// </summary>
public Plugin Plugin { get; }
public PluginFeature PluginFeature { get; }
/// <summary>
/// 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);
}
@@ -14,6 +14,11 @@ public interface INode : INotifyPropertyChanged, IBreakableModel
/// Gets or sets the ID of the node.
/// </summary>
Guid Id { get; set; }
/// <summary>
/// Gets
/// </summary>
NodeData? NodeData { get; set; }
/// <summary>
/// Gets the name of the node
+24 -7
View File
@@ -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<INodeScript, NodeEntity?, INode> 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
/// <returns>The returning node of type <see cref="Type" /></returns>
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
/// <summary>
/// Gets the plugin that provided this node data
/// Gets the node provider that provided this node data
/// </summary>
public Plugin Plugin { get; }
public NodeProvider Provider { get; }
/// <summary>
/// Gets the type of <see cref="INode" /> this data represents
@@ -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,
@@ -41,6 +41,9 @@ public abstract class Node : BreakableModel, INode
set => SetAndNotify(ref _id, value);
}
/// <inheritdoc />
public NodeData NodeData { get; set; }
private string _name;
/// <inheritdoc />
@@ -104,8 +107,6 @@ public abstract class Node : BreakableModel, INode
/// <inheritdoc />
public override string BrokenDisplayName => Name;
internal IContainer Container { get; set; } = null!;
#endregion
#region Construtors
@@ -25,7 +25,7 @@ public abstract class Node<TStorage, TViewModel> : Node<TStorage>, ICustomViewMo
/// <param name="nodeScript"></param>
public virtual TViewModel GetViewModel(NodeScript nodeScript)
{
return Container.Resolve<TViewModel>(args: new object[] {this, nodeScript});
return NodeData.Provider.Plugin.Container.Resolve<TViewModel>(args: new object[] {this, nodeScript});
}
/// <summary>