NodeGraphProcessor icon indicating copy to clipboard operation
NodeGraphProcessor copied to clipboard

Changing Port Order With OverrideFieldOrder Loses Port Edge Type

Open dannymate opened this issue 3 years ago • 0 comments

So I have a couple layers of inheritance first. It looks like this: BaseNode > DynamicNode<T> > SequenceNode

In Dynamic node I'm creating ports on the fly with CustomPortBehavior and CustomPortInput. Everything works how you would expect except I'd prefer if the ports were reordered so I did by implementing the OverrideFieldOrder method. Essentially all I'm doing is reversing the original order. i.e. return base.OverrideFieldOrder(fields).Reverse();.

image Also for some reason the property drawer for a float in DynamicNode is in the wrong place by default.

The order is reversed correctly but when creating a connection node in the menu it doesn't pickup up the correct type. Before (Dragging&Dropping an edge): image After (Dragging&Dropping an edge): image

The float itself does work before and after so I think it could be some kind of generic inheritance thing.

Code For DynamicNode<T>:

DynamicNode Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GraphProcessor;
using System.Linq;
using System.Reflection;
using System;

[System.Serializable]
public abstract class DynamicNode<T> : BaseNode
{
    [Input("Action Data")]
    public Dictionary<string, object> actionData = new Dictionary<string, object>();
    [Input("Float Val"), SerializeField] public float floatVal;

    [SerializeField, ExpandableSO, ValueChangedCallback(nameof(OnDataChanged))] public T data;

    protected override void Process()
    {
        UpdateActionWithCustomPortData();
    }

    protected virtual void UpdateActionWithCustomPortData()
    {
        // We clone due to reference issues
        Dictionary<string, object> actionDataClone = new Dictionary<string, object>(actionData);

        foreach (var field in GetInputFieldsOfType())
        {
            if (!actionDataClone.ContainsKey(field.fieldInfo.Name))
            {
                field.fieldInfo.SetValue(data, default);
            }
            else
            {
                field.fieldInfo.SetValue(data, actionDataClone[field.fieldInfo.Name]);
            }
        }

        actionData.Clear();
    }

    protected virtual void OnDataChanged(UnityEditor.SerializedProperty serializedProperty)
    {
        UpdatePortsForFieldLocal(nameof(actionData));
    }

    #region Reflection Generation Of Ports

    private List<FieldPortInfo> GetInputFieldsOfType()
    {
        List<FieldPortInfo> foundInputFields = new List<FieldPortInfo>();

        Type dataType = actionData != null ? data.GetType() : typeof(T);
        foreach (var field in dataType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
        {
            foreach (var attribute in field.GetCustomAttributes(typeof(InputAttribute), true))
            {
                if (attribute.GetType() != typeof(InputAttribute)) continue;

                foundInputFields.Add(new FieldPortInfo(field, attribute as InputAttribute));
                break;
            }
        }

        return foundInputFields;
    }

    [CustomPortInput(nameof(actionData), typeof(object))]
    protected void PullInputs(List<SerializableEdge> connectedEdges)
    {
        if (actionData == null) actionData = new Dictionary<string, object>();
        foreach (SerializableEdge t in connectedEdges)
        {
            actionData.Add(t.inputPortIdentifier, t.passThroughBuffer);
        }
    }

    [CustomPortBehavior(nameof(actionData))]
    protected IEnumerable<PortData> ActionDataBehaviour(List<SerializableEdge> edges)
    {
        foreach (var field in GetInputFieldsOfType())
        {
            yield return new PortData
            {
                displayName = field.inputAttribute.name,
                displayType = field.fieldInfo.FieldType,
                identifier = field.fieldInfo.Name,
                acceptMultipleEdges = false,
            };
        }
    }

    public override IEnumerable<FieldInfo> OverrideFieldOrder(IEnumerable<FieldInfo> fields)
    {
        return base.OverrideFieldOrder(fields).Reverse();

        // static long GetFieldInheritanceLevel(FieldInfo f)
        // {
        //     int level = 0;
        //     var t = f.DeclaringType;
        //     while (t != null)
        //     {
        //         t = t.BaseType;
        //         level++;
        //     }

        //     return level;
        // }

        // // Order by MetadataToken and inheritance level to sync the order with the port order (make sure FieldDrawers are next to the correct port)
        // return fields.OrderByDescending(f => (GetFieldInheritanceLevel(f) << 32) | (long)f.MetadataToken);

    }

    #endregion
}

public struct FieldPortInfo
{
    public FieldInfo fieldInfo;
    public InputAttribute inputAttribute;

    public FieldPortInfo(FieldInfo fieldInfo, InputAttribute inputAttribute)
    {
        this.fieldInfo = fieldInfo;
        this.inputAttribute = inputAttribute;
    }
}

dannymate avatar Jan 30 '22 17:01 dannymate