diff --git a/README.md b/README.md
index 3eadd3e..67a0426 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@
Support xNode on [Ko-fi](https://ko-fi.com/Z8Z5DYWA) or [Patreon](https://www.patreon.com/thorbrigsted)
+For full Odin support, consider using [KAJed82's fork](https://github.com/KAJed82/xNode)
+
### xNode
Thinking of developing a node-based plugin? Then this is for you. You can download it as an archive and unpack to a new unity project, or connect it as git submodule.
diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs
index 5d8d32b..a9147f2 100644
--- a/Scripts/Editor/NodeEditorAction.cs
+++ b/Scripts/Editor/NodeEditorAction.cs
@@ -221,7 +221,7 @@ namespace XNodeEditor {
//Port drag release
if (IsDraggingPort) {
// If connection is valid, save it
- if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) {
+ if (draggedOutputTarget != null && graphEditor.CanConnect(draggedOutput, draggedOutputTarget)) {
XNode.Node node = draggedOutputTarget.node;
if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
@@ -553,12 +553,9 @@ namespace XNodeEditor {
public void AutoConnect(XNode.Node node) {
if (autoConnectOutput == null) return;
- // Find input port of same type
- XNode.NodePort inputPort = node.Ports.FirstOrDefault(x => x.IsInput && x.ValueType == autoConnectOutput.ValueType);
- // Fallback to input port
- if (inputPort == null) inputPort = node.Ports.FirstOrDefault(x => x.IsInput);
- // Autoconnect if connection is compatible
- if (inputPort != null && inputPort.CanConnectTo(autoConnectOutput)) autoConnectOutput.Connect(inputPort);
+ // Find compatible input port
+ XNode.NodePort inputPort = node.Ports.FirstOrDefault(x => x.IsInput && graphEditor.CanConnect(autoConnectOutput, x));
+ if (inputPort != null) autoConnectOutput.Connect(inputPort);
// Save changes
EditorUtility.SetDirty(graph);
diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs
index 8c93cb2..a0e3358 100644
--- a/Scripts/Editor/NodeEditorGUILayout.cs
+++ b/Scripts/Editor/NodeEditorGUILayout.cs
@@ -394,6 +394,7 @@ namespace XNodeEditor {
};
list.onReorderCallback =
(ReorderableList rl) => {
+ serializedObject.Update();
bool hasRect = false;
bool hasNewRect = false;
Rect rect = Rect.zero;
diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs
index a304c2c..60b0cdb 100644
--- a/Scripts/Editor/NodeGraphEditor.cs
+++ b/Scripts/Editor/NodeGraphEditor.cs
@@ -59,6 +59,13 @@ namespace XNodeEditor {
return 0;
}
+ ///
+ /// Called before connecting two ports in the graph view to see if the output port is compatible with the input port
+ ///
+ public virtual bool CanConnect(XNode.NodePort output, XNode.NodePort input) {
+ return output.CanConnectTo(input);
+ }
+
///
/// Add items for the context menu when right-clicking this node.
/// Override to add custom menu items.
@@ -96,7 +103,7 @@ namespace XNodeEditor {
if (disallowed) menu.AddItem(new GUIContent(path), false, null);
else menu.AddItem(new GUIContent(path), false, () => {
XNode.Node node = CreateNode(type, pos);
- NodeEditorWindow.current.AutoConnect(node);
+ if (node != null) NodeEditorWindow.current.AutoConnect(node); // handle null nodes to avoid nullref exceptions
});
}
menu.AddSeparator("");
@@ -206,6 +213,7 @@ namespace XNodeEditor {
public virtual XNode.Node CreateNode(Type type, Vector2 position) {
Undo.RecordObject(target, "Create Node");
XNode.Node node = target.AddNode(type);
+ if (node == null) return null; // handle null nodes to avoid nullref exceptions
Undo.RegisterCreatedObjectUndo(node, "Create Node");
node.position = position;
if (node.name == null || node.name.Trim() == "") node.name = NodeEditorUtilities.NodeDefaultName(type);
@@ -221,7 +229,7 @@ namespace XNodeEditor {
XNode.Node node = target.CopyNode(original);
Undo.RegisterCreatedObjectUndo(node, "Duplicate Node");
node.name = original.name;
- AssetDatabase.AddObjectToAsset(node, target);
+ if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(target))) AssetDatabase.AddObjectToAsset(node, target);
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
return node;
}
diff --git a/Scripts/Editor/RenamePopup.cs b/Scripts/Editor/RenamePopup.cs
index a43837f..ca1ee15 100644
--- a/Scripts/Editor/RenamePopup.cs
+++ b/Scripts/Editor/RenamePopup.cs
@@ -53,8 +53,10 @@ namespace XNodeEditor {
if (GUILayout.Button("Revert to default") || (e.isKey && e.keyCode == KeyCode.Return)) {
target.name = NodeEditorUtilities.NodeDefaultName(target.GetType());
NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename();
- AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target));
- AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
+ if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(target))) {
+ AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target));
+ AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
+ }
Close();
target.TriggerOnValidate();
}
@@ -64,8 +66,10 @@ namespace XNodeEditor {
if (GUILayout.Button("Apply") || (e.isKey && e.keyCode == KeyCode.Return)) {
target.name = input;
NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename();
- AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target));
- AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
+ if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(target))) {
+ AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target));
+ AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
+ }
Close();
target.TriggerOnValidate();
}
diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs
index 9b55b9d..f28de98 100644
--- a/Scripts/NodeDataCache.cs
+++ b/Scripts/NodeDataCache.cs
@@ -70,20 +70,23 @@ namespace XNode {
for (int i = 0; i < reconnectConnections.Count; i++) {
NodePort connection = reconnectConnections[i];
if (connection == null) continue;
+ // CAVEAT: Ports connected under special conditions defined in graphEditor.CanConnect overrides will not auto-connect.
+ // To fix this, this code would need to be moved to an editor script and call graphEditor.CanConnect instead of port.CanConnectTo.
+ // This is only a problem in the rare edge case where user is using non-standard CanConnect overrides and changes port type of an already connected port
if (port.CanConnectTo(connection)) port.Connect(connection);
}
}
ports.Add(staticPort.fieldName, port);
}
}
-
+
// Finally, make sure dynamic list port settings correspond to the settings of their "backing port"
foreach (NodePort listPort in dynamicListPorts) {
// 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.
string backingPortName = listPort.fieldName.Split(' ')[0];
NodePort backingPort = staticPorts[backingPortName];
-
+
// Update port constraints. Creating a new port instead will break the editor, mandating the need for setters.
listPort.ValueType = GetBackingValueType(backingPort.ValueType);
listPort.direction = backingPort.direction;
@@ -95,7 +98,7 @@ namespace XNode {
///
/// Extracts the underlying types from arrays and lists, the only collections for dynamic port lists
/// currently supported. If the given type is not applicable (i.e. if the dynamic list port was not
- /// defined as an array or a list), returns the given type itself.
+ /// defined as an array or a list), returns the given type itself.
///
private static System.Type GetBackingValueType(System.Type portValType) {
if (portValType.HasElementType) {
@@ -114,10 +117,10 @@ namespace XNode {
// Thus, we need to check for attributes... (but at least we don't need to look at all fields this time)
string[] fieldNameParts = port.fieldName.Split(' ');
if (fieldNameParts.Length != 2) return false;
-
+
FieldInfo backingPortInfo = port.node.GetType().GetField(fieldNameParts[0]);
if (backingPortInfo == null) return false;
-
+
object[] attribs = backingPortInfo.GetCustomAttributes(true);
return attribs.Any(x => {
Node.InputAttribute inputAttribute = x as Node.InputAttribute;
@@ -126,7 +129,7 @@ namespace XNode {
outputAttribute != null && outputAttribute.dynamicPortList;
});
}
-
+
/// Cache node types
private static void BuildCache() {
portDataCache = new PortDataCache();
@@ -204,10 +207,10 @@ namespace XNode {
portDataCache[nodeType].Add(new NodePort(fieldInfo[i]));
}
- if(formerlySerializedAsAttribute != null) {
+ if (formerlySerializedAsAttribute != null) {
if (formerlySerializedAsCache == null) formerlySerializedAsCache = new Dictionary>();
if (!formerlySerializedAsCache.ContainsKey(nodeType)) formerlySerializedAsCache.Add(nodeType, new Dictionary());
-
+
if (formerlySerializedAsCache[nodeType].ContainsKey(formerlySerializedAsAttribute.oldName)) Debug.LogError("Another FormerlySerializedAs with value '" + formerlySerializedAsAttribute.oldName + "' already exist on this node.");
else formerlySerializedAsCache[nodeType].Add(formerlySerializedAsAttribute.oldName, fieldInfo[i].Name);
}