1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-20 01:06:01 +08:00
xNode/Scripts/NodeDataCache.cs
Thor Kramer Brigsted d3bb36fe0e Big update: Warning: Updating to this commit will break all node connections.
Internal NodePorts now uses dicts instead of lists. This is faster and more manageable.
Added instance ports.
Added Node.Ports, Node.Outputs, Node.Inputs, Node.InstanceOutputs, Node.InstanceInputs
Changed public GetInputByFieldName to GetInputValue and GetInputPort
2017-11-02 14:54:03 +01:00

102 lines
4.1 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
/// <summary> Precaches reflection data in editor so we won't have to do it runtime </summary>
public static class NodeDataCache {
private static PortDataCache portDataCache;
private static bool Initialized { get { return portDataCache != null; } }
/// <summary> Update static ports to reflect class fields. </summary>
public static void UpdatePorts(Node node, Dictionary<string, NodePort> ports) {
if (!Initialized) BuildCache();
Dictionary<string, NodePort> staticPorts = new Dictionary<string, NodePort>();
System.Type nodeType = node.GetType();
if (!portDataCache.ContainsKey(nodeType)) return;
for (int i = 0; i < portDataCache[nodeType].Count; i++) {
staticPorts.Add(portDataCache[nodeType][i].fieldName, portDataCache[nodeType][i]);
}
// Cleanup port dict - Remove nonexisting static ports - update static port types
foreach (NodePort port in ports.Values) {
if (staticPorts.ContainsKey(port.fieldName)) {
NodePort staticPort = staticPorts[port.fieldName];
if (port.IsDynamic || port.direction != staticPort.direction) ports.Remove(port.fieldName);
else port.type = staticPort.type;
} else {
ports.Remove(port.fieldName);
}
}
// Add missing ports
foreach (NodePort staticPort in staticPorts.Values) {
if (!ports.ContainsKey(staticPort.fieldName)) {
ports.Add(staticPort.fieldName, new NodePort(staticPort, node));
}
}
}
private static void BuildCache() {
portDataCache = new PortDataCache();
System.Type baseType = typeof(Node);
Assembly assembly = Assembly.GetAssembly(baseType);
System.Type[] nodeTypes = assembly.GetTypes().Where(t =>
!t.IsAbstract &&
baseType.IsAssignableFrom(t)
).ToArray();
for (int i = 0; i < nodeTypes.Length; i++) {
CachePorts(nodeTypes[i]);
}
}
private static void CachePorts(System.Type nodeType) {
System.Reflection.FieldInfo[] fieldInfo = nodeType.GetFields();
for (int i = 0; i < fieldInfo.Length; i++) {
//Get InputAttribute and OutputAttribute
object[] attribs = fieldInfo[i].GetCustomAttributes(false);
Node.InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is Node.InputAttribute) as Node.InputAttribute;
Node.OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is Node.OutputAttribute) as Node.OutputAttribute;
if (inputAttrib == null && outputAttrib == null) continue;
if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo + " cannot be both input and output.");
else {
if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List<NodePort>());
portDataCache[nodeType].Add(new NodePort(fieldInfo[i]));
}
}
}
[System.Serializable]
private class PortDataCache : Dictionary<System.Type, List<NodePort>>, ISerializationCallbackReceiver {
[SerializeField] private List<System.Type> keys = new List<System.Type>();
[SerializeField] private List<List<NodePort>> values = new List<List<NodePort>>();
// save the dictionary to lists
public void OnBeforeSerialize() {
keys.Clear();
values.Clear();
foreach (var pair in this) {
keys.Add(pair.Key);
values.Add(pair.Value);
}
}
// load dictionary from lists
public void OnAfterDeserialize() {
this.Clear();
if (keys.Count != values.Count)
throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
for (int i = 0; i < keys.Count; i++)
this.Add(keys[i], values[i]);
}
}
}