mirror of
https://github.com/Siccity/xNode.git
synced 2025-12-21 01:36:03 +08:00
Fix for the dynamic port "serialisation desync" problem. (#223)
* Exclude strings from array types manually to prevent them from being treated as char arrays.
This commit is contained in:
parent
ac7403b876
commit
7e93ffe4b7
@ -456,8 +456,8 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
XNode.Node newNodeIn, newNodeOut;
|
XNode.Node newNodeIn, newNodeOut;
|
||||||
if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) {
|
if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) {
|
||||||
newNodeIn.UpdateStaticPorts();
|
newNodeIn.UpdatePorts();
|
||||||
newNodeOut.UpdateStaticPorts();
|
newNodeOut.UpdatePorts();
|
||||||
inputPort = newNodeIn.GetInputPort(inputPort.fieldName);
|
inputPort = newNodeIn.GetInputPort(inputPort.fieldName);
|
||||||
outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
|
outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ namespace XNodeEditor {
|
|||||||
}).Where(x => x.port != null);
|
}).Where(x => x.port != null);
|
||||||
List<XNode.NodePort> dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList();
|
List<XNode.NodePort> dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList();
|
||||||
|
|
||||||
|
node.UpdatePorts();
|
||||||
|
|
||||||
ReorderableList list = null;
|
ReorderableList list = null;
|
||||||
Dictionary<string, ReorderableList> rlc;
|
Dictionary<string, ReorderableList> rlc;
|
||||||
if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) {
|
if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) {
|
||||||
@ -326,6 +328,7 @@ namespace XNodeEditor {
|
|||||||
}
|
}
|
||||||
list.list = dynamicPorts;
|
list.list = dynamicPorts;
|
||||||
list.DoLayoutList();
|
list.DoLayoutList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReorderableList CreateReorderableList(string fieldName, List<XNode.NodePort> dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action<ReorderableList> onCreation) {
|
private static ReorderableList CreateReorderableList(string fieldName, List<XNode.NodePort> dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action<ReorderableList> onCreation) {
|
||||||
@ -337,7 +340,7 @@ namespace XNodeEditor {
|
|||||||
list.drawElementCallback =
|
list.drawElementCallback =
|
||||||
(Rect rect, int index, bool isActive, bool isFocused) => {
|
(Rect rect, int index, bool isActive, bool isFocused) => {
|
||||||
XNode.NodePort port = node.GetPort(fieldName + " " + index);
|
XNode.NodePort port = node.GetPort(fieldName + " " + index);
|
||||||
if (hasArrayData) {
|
if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) {
|
||||||
if (arrayData.arraySize <= index) {
|
if (arrayData.arraySize <= index) {
|
||||||
EditorGUI.LabelField(rect, "Array[" + index + "] data out of range");
|
EditorGUI.LabelField(rect, "Array[" + index + "] data out of range");
|
||||||
return;
|
return;
|
||||||
@ -465,7 +468,7 @@ namespace XNodeEditor {
|
|||||||
EditorUtility.SetDirty(node);
|
EditorUtility.SetDirty(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasArrayData) {
|
if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) {
|
||||||
if (arrayData.arraySize <= index) {
|
if (arrayData.arraySize <= index) {
|
||||||
Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped");
|
Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped");
|
||||||
Debug.Log(rl.list[0]);
|
Debug.Log(rl.list[0]);
|
||||||
|
|||||||
@ -119,12 +119,12 @@ namespace XNode {
|
|||||||
protected void OnEnable() {
|
protected void OnEnable() {
|
||||||
if (graphHotfix != null) graph = graphHotfix;
|
if (graphHotfix != null) graph = graphHotfix;
|
||||||
graphHotfix = null;
|
graphHotfix = null;
|
||||||
UpdateStaticPorts();
|
UpdatePorts();
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Update static ports to reflect class fields. This happens automatically on enable. </summary>
|
/// <summary> Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. This happens automatically on enable or on redrawing a dynamic port list. </summary>
|
||||||
public void UpdateStaticPorts() {
|
public void UpdatePorts() {
|
||||||
NodeDataCache.UpdatePorts(this, ports);
|
NodeDataCache.UpdatePorts(this, ports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ namespace XNode {
|
|||||||
private static PortDataCache portDataCache;
|
private static PortDataCache portDataCache;
|
||||||
private static bool Initialized { get { return portDataCache != null; } }
|
private static bool Initialized { get { return portDataCache != null; } }
|
||||||
|
|
||||||
/// <summary> Update static ports 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();
|
||||||
|
|
||||||
@ -17,6 +17,8 @@ namespace XNode {
|
|||||||
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();
|
||||||
|
|
||||||
|
List<NodePort> dynamicListPorts = new List<NodePort>();
|
||||||
|
|
||||||
List<NodePort> typePortCache;
|
List<NodePort> typePortCache;
|
||||||
if (portDataCache.TryGetValue(nodeType, out typePortCache)) {
|
if (portDataCache.TryGetValue(nodeType, out typePortCache)) {
|
||||||
for (int i = 0; i < typePortCache.Count; i++) {
|
for (int i = 0; i < typePortCache.Count; i++) {
|
||||||
@ -25,6 +27,7 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
// Loop through current node ports
|
// Loop through current node ports
|
||||||
foreach (NodePort port in ports.Values.ToList()) {
|
foreach (NodePort port in ports.Values.ToList()) {
|
||||||
// If port still exists, check it it has been changed
|
// If port still exists, check it it has been changed
|
||||||
@ -43,6 +46,10 @@ namespace XNode {
|
|||||||
port.ClearConnections();
|
port.ClearConnections();
|
||||||
ports.Remove(port.fieldName);
|
ports.Remove(port.fieldName);
|
||||||
}
|
}
|
||||||
|
// If the port is dynamic and is managed by a dynamic port list, flag it for reference updates
|
||||||
|
else if (IsDynamicListPort(port)) {
|
||||||
|
dynamicListPorts.Add(port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Add missing ports
|
// Add missing ports
|
||||||
foreach (NodePort staticPort in staticPorts.Values) {
|
foreach (NodePort staticPort in staticPorts.Values) {
|
||||||
@ -60,8 +67,42 @@ namespace XNode {
|
|||||||
ports.Add(staticPort.fieldName, port);
|
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 = backingPort.ValueType;
|
||||||
|
listPort.direction = backingPort.direction;
|
||||||
|
listPort.connectionType = backingPort.connectionType;
|
||||||
|
listPort.typeConstraint = backingPort.typeConstraint;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns true if the given port is in a dynamic port list.</summary>
|
||||||
|
private static bool IsDynamicListPort(NodePort port) {
|
||||||
|
// Ports flagged as "dynamicPortList = true" end up having a "backing port" and a name with an index, but we have
|
||||||
|
// no guarantee that a dynamic port called "output 0" is an element in a list backed by a static "output" port.
|
||||||
|
// 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;
|
||||||
|
Node.OutputAttribute outputAttribute = x as Node.OutputAttribute;
|
||||||
|
return inputAttribute != null && inputAttribute.dynamicPortList ||
|
||||||
|
outputAttribute != null && outputAttribute.dynamicPortList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Cache node types </summary>
|
/// <summary> Cache node types </summary>
|
||||||
private static void BuildCache() {
|
private static void BuildCache() {
|
||||||
portDataCache = new PortDataCache();
|
portDataCache = new PortDataCache();
|
||||||
|
|||||||
@ -19,9 +19,18 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IO direction { get { return _direction; } }
|
public IO direction {
|
||||||
public Node.ConnectionType connectionType { get { return _connectionType; } }
|
get { return _direction; }
|
||||||
public Node.TypeConstraint typeConstraint { get { return _typeConstraint; } }
|
internal set { _direction = value; }
|
||||||
|
}
|
||||||
|
public Node.ConnectionType connectionType {
|
||||||
|
get { return _connectionType; }
|
||||||
|
internal set { _connectionType = value; }
|
||||||
|
}
|
||||||
|
public Node.TypeConstraint typeConstraint {
|
||||||
|
get { return _typeConstraint; }
|
||||||
|
internal set { _typeConstraint = value; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Is this port connected to anytihng? </summary>
|
/// <summary> Is this port connected to anytihng? </summary>
|
||||||
public bool IsConnected { get { return connections.Count != 0; } }
|
public bool IsConnected { get { return connections.Count != 0; } }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user