using System; using System.Collections.Generic; using System.Reflection; using System.Security.Cryptography; using System.Text; using Artemis.Core.VisualScripting; using Artemis.Storage.Entities.Profile.Nodes; using Newtonsoft.Json; using Ninject; using SkiaSharp; namespace Artemis.Core.Services { internal class NodeService : INodeService { #region Constants private static readonly Type TYPE_NODE = typeof(INode); #endregion private readonly IKernel _kernel; #region Constructors public NodeService(IKernel kernel) { _kernel = kernel; } #endregion #region Properties & Fields public IEnumerable AvailableNodes => NodeTypeStore.GetAll(); #endregion #region Methods /// public TypeColorRegistration GetTypeColorRegistration(Type type) { TypeColorRegistration? match = NodeTypeStore.GetColor(type); if (match != null) return match; // 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); // Come up with a random color based on the type name that should be the same each time MD5 md5Hasher = MD5.Create(); byte[] hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(type.FullName!)); 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 (!TYPE_NODE.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; NodeData nodeData = new(plugin, nodeType, name, description, category, 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); } public string ExportScript(NodeScript nodeScript) { nodeScript.Save(); return JsonConvert.SerializeObject(nodeScript.Entity, IProfileService.ExportSettings); } public void ImportScript(string json, NodeScript target) { NodeScriptEntity? entity = JsonConvert.DeserializeObject(json); if (entity == null) throw new ArtemisCoreException("Failed to load node script"); target.LoadFromEntity(entity); } private INode CreateNode(INodeScript script, NodeEntity? entity, Type nodeType) { INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode"); if (entity != null) { node.X = entity.X; node.Y = entity.Y; try { if (node is Node nodeImplementation) nodeImplementation.DeserializeStorage(entity.Storage); } catch { // ignored } } node.Initialize(script); return node; } #endregion } /// /// A service that provides access to the node system /// public interface INodeService : IArtemisService { /// /// Gets all available nodes /// IEnumerable AvailableNodes { get; } /// /// Gets the best matching registration for the provided type /// 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. /// /// The node script to export. /// The resulting JSON. string ExportScript(NodeScript nodeScript); /// /// Imports the provided JSON onto the provided node script, overwriting any existing contents. /// /// The JSON to import. /// The target node script whose contents to overwrite. void ImportScript(string json, NodeScript target); } }