1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-20 17:26:02 +08:00

Merge pull request #2 from insthync/merge--siccity-master

Merge  siccity master
This commit is contained in:
Ittipon Teerapruettikulchai 2025-03-03 00:35:59 +07:00 committed by GitHub
commit a8ef96ce1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 71 additions and 46 deletions

View File

@ -0,0 +1,12 @@
using System;
/// <summary> Overrides the ValueType of the Port, to have a ValueType different from the type of its serializable field </summary>
/// <remarks> Especially useful in Dynamic Port Lists to create Value-Port Pairs with different type. </remarks>
[AttributeUsage(AttributeTargets.Field)]
public class PortTypeOverrideAttribute : Attribute {
public Type type;
/// <summary> Overrides the ValueType of the Port </summary>
/// <param name="type">ValueType of the Port</param>
public PortTypeOverrideAttribute(Type type) {
this.type = type;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1410c1437e863ab4fac7a7428aaca35b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEditor; using UnityEditor;
@ -12,8 +12,8 @@ namespace XNodeEditor {
/// <summary> Contains GUI methods </summary> /// <summary> Contains GUI methods </summary>
public partial class NodeEditorWindow { public partial class NodeEditorWindow {
public NodeGraphEditor graphEditor; public NodeGraphEditor graphEditor;
private List<UnityEngine.Object> selectionCache; private readonly HashSet<UnityEngine.Object> selectionCache = new();
private List<XNode.Node> culledNodes; private readonly HashSet<XNode.Node> culledNodes = new();
/// <summary> 19 if docked, 22 if not </summary> /// <summary> 19 if docked, 22 if not </summary>
private int topPadding { get { return isDocked() ? 19 : 22; } } private int topPadding { get { return isDocked() ? 19 : 22; } }
/// <summary> Executed after all other window GUI. Useful if Zoom is ruining your day. Automatically resets after being run.</summary> /// <summary> Executed after all other window GUI. Useful if Zoom is ruining your day. Automatically resets after being run.</summary>
@ -399,8 +399,13 @@ namespace XNodeEditor {
private void DrawNodes() { private void DrawNodes() {
Event e = Event.current; Event e = Event.current;
if (e.type == EventType.Layout) { if (e.type == EventType.Layout) {
selectionCache = new List<UnityEngine.Object>(Selection.objects); selectionCache.Clear();
var objs = Selection.objects;
selectionCache.EnsureCapacity(objs.Length);
foreach (var obj in objs)
selectionCache.Add(obj);
} }
System.Reflection.MethodInfo onValidate = null; System.Reflection.MethodInfo onValidate = null;
@ -432,7 +437,7 @@ namespace XNodeEditor {
List<XNode.NodePort> removeEntries = new List<XNode.NodePort>(); List<XNode.NodePort> removeEntries = new List<XNode.NodePort>();
if (e.type == EventType.Layout) culledNodes = new List<XNode.Node>(); if (e.type == EventType.Layout) culledNodes.Clear();
for (int n = 0; n < graph.nodes.Count; n++) { for (int n = 0; n < graph.nodes.Count; n++) {
// Skip null nodes. The user could be in the process of renaming scripts, so removing them at this point is not advisable. // Skip null nodes. The user could be in the process of renaming scripts, so removing them at this point is not advisable.
if (graph.nodes[n] == null) continue; if (graph.nodes[n] == null) continue;

View File

@ -399,6 +399,8 @@ namespace XNode {
public void OnBeforeSerialize() { public void OnBeforeSerialize() {
keys.Clear(); keys.Clear();
values.Clear(); values.Clear();
keys.Capacity = this.Count;
values.Capacity = this.Count;
foreach (KeyValuePair<string, NodePort> pair in this) { foreach (KeyValuePair<string, NodePort> pair in this) {
keys.Add(pair.Key); keys.Add(pair.Key);
values.Add(pair.Value); values.Add(pair.Value);
@ -407,6 +409,9 @@ namespace XNode {
public void OnAfterDeserialize() { public void OnAfterDeserialize() {
this.Clear(); this.Clear();
#if UNITY_2021_3_OR_NEWER
this.EnsureCapacity(keys.Count);
#endif
if (keys.Count != values.Count) if (keys.Count != values.Count)
throw new System.Exception("there are " + keys.Count + " keys and " + values.Count + " values after deserialization. Make sure that both key and value types are serializable."); throw new System.Exception("there are " + keys.Count + " keys and " + values.Count + " values after deserialization. Make sure that both key and value types are serializable.");

View File

@ -8,13 +8,24 @@ namespace XNode {
public static class NodeDataCache { public static class NodeDataCache {
private static PortDataCache portDataCache; private static PortDataCache portDataCache;
private static Dictionary<System.Type, Dictionary<string, string>> formerlySerializedAsCache; private static Dictionary<System.Type, Dictionary<string, string>> formerlySerializedAsCache;
private static Dictionary<System.Type, string> typeQualifiedNameCache;
private static bool Initialized { get { return portDataCache != null; } } private static bool Initialized { get { return portDataCache != null; } }
public static string GetTypeQualifiedName(System.Type type) {
if(typeQualifiedNameCache == null) typeQualifiedNameCache = new Dictionary<System.Type, string>();
string name;
if (!typeQualifiedNameCache.TryGetValue(type, out name)) {
name = type.AssemblyQualifiedName;
typeQualifiedNameCache.Add(type, name);
}
return name;
}
/// <summary> Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. </summary> /// <summary> Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. </summary>
public static void UpdatePorts(Node node, Dictionary<string, NodePort> ports) { public static void UpdatePorts(Node node, Dictionary<string, NodePort> ports) {
if (!Initialized) BuildCache(); if (!Initialized) BuildCache();
Dictionary<string, NodePort> staticPorts = new Dictionary<string, NodePort>();
Dictionary<string, List<NodePort>> removedPorts = new Dictionary<string, List<NodePort>>(); Dictionary<string, List<NodePort>> removedPorts = new Dictionary<string, List<NodePort>>();
System.Type nodeType = node.GetType(); System.Type nodeType = node.GetType();
@ -23,17 +34,15 @@ namespace XNode {
List<NodePort> dynamicListPorts = new List<NodePort>(); List<NodePort> dynamicListPorts = new List<NodePort>();
List<NodePort> typePortCache; Dictionary<string, NodePort> staticPorts;
if (portDataCache.TryGetValue(nodeType, out typePortCache)) { if (!portDataCache.TryGetValue(nodeType, out staticPorts)) {
for (int i = 0; i < typePortCache.Count; i++) { staticPorts = new Dictionary<string, NodePort>();
staticPorts.Add(typePortCache[i].fieldName, portDataCache[nodeType][i]); }
}
}
// Cleanup port dict - Remove nonexisting static ports - update static port types // Cleanup port dict - Remove nonexisting static ports - update static port types
// AND update dynamic ports (albeit only those in lists) too, in order to enforce proper serialisation. // AND update dynamic ports (albeit only those in lists) too, in order to enforce proper serialisation.
// Loop through current node ports // Loop through current node ports
foreach (NodePort port in ports.Values.ToList()) { foreach (NodePort port in ports.Values.ToArray()) {
// If port still exists, check it it has been changed // If port still exists, check it it has been changed
NodePort staticPort; NodePort staticPort;
if (staticPorts.TryGetValue(port.fieldName, out staticPort)) { if (staticPorts.TryGetValue(port.fieldName, out staticPort)) {
@ -84,7 +93,7 @@ namespace XNode {
foreach (NodePort listPort in dynamicListPorts) { foreach (NodePort listPort in dynamicListPorts) {
// At this point we know that ports here are dynamic list ports // At this point we know that ports here are dynamic list ports
// which have passed name/"backing port" checks, ergo we can proceed more safely. // which have passed name/"backing port" checks, ergo we can proceed more safely.
string backingPortName = listPort.fieldName.Split(' ')[0]; string backingPortName = listPort.fieldName.Substring(0, listPort.fieldName.IndexOf(' '));
NodePort backingPort = staticPorts[backingPortName]; NodePort backingPort = staticPorts[backingPortName];
// Update port constraints. Creating a new port instead will break the editor, mandating the need for setters. // Update port constraints. Creating a new port instead will break the editor, mandating the need for setters.
@ -147,6 +156,7 @@ namespace XNode {
// The following assemblies, and sub-assemblies (eg. UnityEngine.UI) are skipped // The following assemblies, and sub-assemblies (eg. UnityEngine.UI) are skipped
case "UnityEditor": case "UnityEditor":
case "UnityEngine": case "UnityEngine":
case "Unity":
case "System": case "System":
case "mscorlib": case "mscorlib":
case "Microsoft": case "Microsoft":
@ -154,11 +164,11 @@ namespace XNode {
default: default:
try try
{ {
nodeTypes.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray()); nodeTypes.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray());
} }
catch (System.Exception ex) catch (System.Exception ex)
{ {
Debug.LogError("Catched exception when building " + assemblyName + " caches"); Debug.LogError($"Catched exception when building {assemblyName} caches");
Debug.LogException(ex); Debug.LogException(ex);
} }
break; break;
@ -203,8 +213,9 @@ namespace XNode {
if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo[i].Name + " of type " + nodeType.FullName + " cannot be both input and output."); if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo[i].Name + " of type " + nodeType.FullName + " cannot be both input and output.");
else { else {
if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List<NodePort>()); if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new Dictionary<string, NodePort>());
portDataCache[nodeType].Add(new NodePort(fieldInfo[i])); NodePort port = new NodePort(fieldInfo[i]);
portDataCache[nodeType].Add(port.fieldName, port);
} }
if (formerlySerializedAsAttribute != null) { if (formerlySerializedAsAttribute != null) {
@ -218,30 +229,6 @@ namespace XNode {
} }
[System.Serializable] [System.Serializable]
private class PortDataCache : Dictionary<System.Type, List<NodePort>>, ISerializationCallbackReceiver { private class PortDataCache : Dictionary<System.Type, Dictionary<string, NodePort>> { }
[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.", keys.Count, values.Count));
for (int i = 0; i < keys.Count; i++)
this.Add(keys[i], values[i]);
}
}
} }
} }

View File

@ -47,7 +47,7 @@ namespace XNode {
public virtual void Clear() { public virtual void Clear() {
if (Application.isPlaying) { if (Application.isPlaying) {
for (int i = 0; i < nodes.Count; i++) { for (int i = 0; i < nodes.Count; i++) {
Destroy(nodes[i]); if (nodes[i] != null) Destroy(nodes[i]);
} }
} }
nodes.Clear(); nodes.Clear();

View File

@ -47,8 +47,9 @@ namespace XNode {
return valueType; return valueType;
} }
set { set {
if (valueType == value) return;
valueType = value; valueType = value;
if (value != null) _typeQualifiedName = value.AssemblyQualifiedName; if (value != null) _typeQualifiedName = NodeDataCache.GetTypeQualifiedName(value);
} }
} }
private Type valueType; private Type valueType;
@ -78,6 +79,10 @@ namespace XNode {
_connectionType = (attribs[i] as Node.OutputAttribute).connectionType; _connectionType = (attribs[i] as Node.OutputAttribute).connectionType;
_typeConstraint = (attribs[i] as Node.OutputAttribute).typeConstraint; _typeConstraint = (attribs[i] as Node.OutputAttribute).typeConstraint;
} }
// Override ValueType of the Port
if(attribs[i] is PortTypeOverrideAttribute) {
ValueType = (attribs[i] as PortTypeOverrideAttribute).type;
}
} }
} }