From afd64d215e890ae38aee12e207e559a431194053 Mon Sep 17 00:00:00 2001
From: taoria <445625470@qq.com>
Date: Mon, 18 Jul 2022 17:43:12 +0800
Subject: [PATCH] fix: allowing dependency walk
---
TNodeCore/Attribute/InternalModel.cs | 10 ++
TNodeCore/Attribute/InternalModel.cs.meta | 3 +
.../Editor/Resources/GraphViewBackground.uss | 7 +-
TNodeCore/Models/BlackboardDragNodeData.cs | 2 +
TNodeCore/Models/IModel.cs | 6 +-
TNodeCore/Models/NodeData.cs | 1 -
TNodeCore/Models/PortInfo.cs | 3 +-
TNodeCore/Runtime/RuntimeGraph.cs | 26 ++--
TNodeCore/Runtime/RuntimeNode.cs | 37 ++---
.../RuntimeCache/IModelPropertyAccessor.cs | 8 ++
.../IModelPropertyAccessor.cs.meta | 3 +
TNodeCore/RuntimeCache/RuntimeCache.cs | 133 ++++++++----------
.../Editor/Inspector/NodeInspectorInNode.cs | 5 +-
.../Editor/NodeGraphView/DataGraphView.cs | 10 +-
.../Editor/NodeViews/NodeView.cs | 5 +-
15 files changed, 132 insertions(+), 127 deletions(-)
create mode 100644 TNodeCore/Attribute/InternalModel.cs
create mode 100644 TNodeCore/Attribute/InternalModel.cs.meta
create mode 100644 TNodeCore/RuntimeCache/IModelPropertyAccessor.cs
create mode 100644 TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta
diff --git a/TNodeCore/Attribute/InternalModel.cs b/TNodeCore/Attribute/InternalModel.cs
new file mode 100644
index 0000000..f9b10cd
--- /dev/null
+++ b/TNodeCore/Attribute/InternalModel.cs
@@ -0,0 +1,10 @@
+namespace TNodeCore.Attribute{
+ ///
+ /// Internal use only. so that Editor Cache and Runtime cache could register it globally.
+ ///
+ public class InternalModel:System.Attribute{
+ public InternalModel(){
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/TNodeCore/Attribute/InternalModel.cs.meta b/TNodeCore/Attribute/InternalModel.cs.meta
new file mode 100644
index 0000000..16bb590
--- /dev/null
+++ b/TNodeCore/Attribute/InternalModel.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: bcd3a2f1670c4bb2b5ef98337ba785d6
+timeCreated: 1658132721
\ No newline at end of file
diff --git a/TNodeCore/Editor/Resources/GraphViewBackground.uss b/TNodeCore/Editor/Resources/GraphViewBackground.uss
index 6c9fbfc..c82adec 100644
--- a/TNodeCore/Editor/Resources/GraphViewBackground.uss
+++ b/TNodeCore/Editor/Resources/GraphViewBackground.uss
@@ -10,7 +10,7 @@ GridBackground{
left: 45%;
top: 45%;
- font-size: 14;
+ font-size: 24px;
}
#TopMenu{
@@ -20,4 +20,9 @@ GridBackground{
width: 100%;
height: 24px;
background-color: #171717;
+}
+.unity-text-element--inner-input-field-component{
+ width: 100%;
+ min-width: 70px;
+ max-width: 250px;
}
\ No newline at end of file
diff --git a/TNodeCore/Models/BlackboardDragNodeData.cs b/TNodeCore/Models/BlackboardDragNodeData.cs
index 773bcb5..0f1622f 100644
--- a/TNodeCore/Models/BlackboardDragNodeData.cs
+++ b/TNodeCore/Models/BlackboardDragNodeData.cs
@@ -1,10 +1,12 @@
using System;
+using TNodeCore.Attribute;
using TNodeCore.Attribute.Ports;
using TNodeCore.RuntimeCache;
using UnityEngine;
namespace TNodeCore.Models{
[Serializable]
+ [InternalModel]
public class BlackboardDragNodeData:NodeData{
public string blackDragData;
[SerializeReference]
diff --git a/TNodeCore/Models/IModel.cs b/TNodeCore/Models/IModel.cs
index c2261e3..b0a1e42 100644
--- a/TNodeCore/Models/IModel.cs
+++ b/TNodeCore/Models/IModel.cs
@@ -1,5 +1,7 @@
-namespace TNodeCore.Models{
+using System;
+
+namespace TNodeCore.Models{
public interface IModel{
-
+
}
}
\ No newline at end of file
diff --git a/TNodeCore/Models/NodeData.cs b/TNodeCore/Models/NodeData.cs
index 3e1565a..5701a69 100644
--- a/TNodeCore/Models/NodeData.cs
+++ b/TNodeCore/Models/NodeData.cs
@@ -22,7 +22,6 @@ namespace TNodeCore.Models{
public string nodeName;
public bool entryPoint;
-
public virtual void Process(){
}
diff --git a/TNodeCore/Models/PortInfo.cs b/TNodeCore/Models/PortInfo.cs
index dbd4e8c..1eb1ed8 100644
--- a/TNodeCore/Models/PortInfo.cs
+++ b/TNodeCore/Models/PortInfo.cs
@@ -1,9 +1,10 @@
using System;
+using UnityEngine.Serialization;
namespace TNodeCore.Models{
[Serializable]
public class PortInfo{
- public string portName;
+ public string portEntryName;
public string nodeDataId;
}
}
\ No newline at end of file
diff --git a/TNodeCore/Runtime/RuntimeGraph.cs b/TNodeCore/Runtime/RuntimeGraph.cs
index 771be99..235f792 100644
--- a/TNodeCore/Runtime/RuntimeGraph.cs
+++ b/TNodeCore/Runtime/RuntimeGraph.cs
@@ -33,8 +33,8 @@ namespace TNodeCore.Runtime{
//out node is node output data
//in node is node receive data
- var outValue = outNode.GetOutput(nodeLink.outPort.portName);
- inNode.SetInput(nodeLink.inPort.portName, outValue);
+ var outValue = outNode.GetOutput(nodeLink.outPort.portEntryName);
+ inNode.SetInput(nodeLink.inPort.portEntryName, outValue);
}
public GraphTool(List list, Dictionary graphNodes){
RuntimeNodes = graphNodes;
@@ -66,7 +66,7 @@ namespace TNodeCore.Runtime{
if(TopologicalOrder.Count!= list.Count){
throw new Exception("Topological sort failed,circular dependency detected");
}
- RuntimeNodes.Clear();
+
inDegreeCounterForTopologicalSort.Clear();
queue.Clear();
}
@@ -75,7 +75,7 @@ namespace TNodeCore.Runtime{
}
[SerializeReference]
public BlackboardData runtimeBlackboardData;
-
+ [NonSerialized]
private bool _build = false;
public void Build(){
@@ -85,6 +85,7 @@ namespace TNodeCore.Runtime{
ModifyOrCreateInNode(linkData);
ModifyOrCreateOutNode(linkData);
}
+ Debug.Log("hi");
var nodeList = RuntimeNodes.Values;
_graphTool = new GraphTool(nodeList.ToList(),RuntimeNodes);
_build = true;
@@ -93,7 +94,6 @@ namespace TNodeCore.Runtime{
public RuntimeNode Get(NodeData nodeData){
if(!_build)
Build();
-
if(RuntimeNodes.ContainsKey(nodeData.id)){
return RuntimeNodes[nodeData.id];
}
@@ -104,11 +104,12 @@ namespace TNodeCore.Runtime{
if (RuntimeNodes.ContainsKey(id)){
return RuntimeNodes[id];
}
-
return null;
}
//DFS search for resolving dependency
public bool ResolveDependency(NodeData startNode){
+ if(!_build)
+ Build();
if (_graphTool == null)
return false;
_graphTool.DependencyTraversal(Get(startNode));
@@ -140,8 +141,17 @@ namespace TNodeCore.Runtime{
runtimeBlackboardData = graphData.blackboardData.Clone() as BlackboardData;
}
}
-
-
+
+ public void OnDisable(){
+ RuntimeNodes.Clear();
+ _build = false;
+ }
+
+ public void OnDestroy(){
+ RuntimeNodes.Clear();
+ _build = false;
+ }
+
}
public enum ProcessingStrategy{
diff --git a/TNodeCore/Runtime/RuntimeNode.cs b/TNodeCore/Runtime/RuntimeNode.cs
index 34181fc..515f840 100644
--- a/TNodeCore/Runtime/RuntimeNode.cs
+++ b/TNodeCore/Runtime/RuntimeNode.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Reflection;
using Codice.Client.Common.TreeGrouper;
using TNodeCore.Attribute.Ports;
using TNodeCore.Models;
@@ -9,49 +10,33 @@ namespace TNodeCore.Runtime{
public class RuntimeNode{
public NodeData NodeData { get; set; }
//the link connect to node's in port
- public List InputLink;
+ public List InputLink = new List();
//the link connect to node's out port
- public List OutputLink;
+ public List OutputLink = new List();
//Cache node data type for fast access
private readonly Type _type;
public void SetInput(string portName,object value){
- NodeData.SetValue(portName, value);
+ _portAccessors[portName].SetValue(this.NodeData,value);
+
}
public object GetOutput(string portName){
- return NodeData.GetValue(portName);
+
+ return _portAccessors[portName].GetValue(this.NodeData);
}
+ private readonly Dictionary _portAccessors;
- private Dictionary _inputPorts = new();
- private Dictionary _outputPorts = new();
-
-
- private void CachingPorts(){
- var properties = _type.GetProperties();
- foreach (var propertyInfo in properties){
- //Check if the property is a port
- var attribute = propertyInfo.GetCustomAttributes(typeof(InputAttribute),true);
- if (attribute.Length > 0){
-
- _inputPorts.Add(propertyInfo.Name,NodeData.CacheSetProperty(propertyInfo.Name));
- }
-
- attribute = propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true);
- if (attribute.Length > 0){
- _outputPorts.Add(propertyInfo.Name,NodeData.CacheGetProperty(propertyInfo.Name));
- }
- }
- }
+
public RuntimeNode(NodeData nodeData){
NodeData = nodeData;
//Caching the type of the node
_type = nodeData.GetType();
var info = nodeData.GetType().GetProperties();
-
- CachingPorts();
+
+ _portAccessors = RuntimeCache.RuntimeCache.Instance.CachedPropertyAccessors[_type];
}
public List GetInputNodesId(){
List dependencies = new List();
diff --git a/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs
new file mode 100644
index 0000000..88a5cae
--- /dev/null
+++ b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace TNodeCore.RuntimeCache{
+ public interface IModelPropertyAccessor{
+ object GetValue(object model);
+ void SetValue(object model, object value);
+ }
+}
\ No newline at end of file
diff --git a/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta
new file mode 100644
index 0000000..29b0319
--- /dev/null
+++ b/TNodeCore/RuntimeCache/IModelPropertyAccessor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: bcaebfe910b84f5dbd81bc4330fe6f7b
+timeCreated: 1658131431
\ No newline at end of file
diff --git a/TNodeCore/RuntimeCache/RuntimeCache.cs b/TNodeCore/RuntimeCache/RuntimeCache.cs
index dfd88e8..5eb6068 100644
--- a/TNodeCore/RuntimeCache/RuntimeCache.cs
+++ b/TNodeCore/RuntimeCache/RuntimeCache.cs
@@ -8,6 +8,35 @@ using TNodeCore.Models;
using UnityEngine;
namespace TNodeCore.RuntimeCache{
+ public class PropAccessor:IModelPropertyAccessor{
+ public readonly Func Get;
+ public readonly Action Set;
+ public PropAccessor(string propName){
+ Type t = typeof(T1);
+ MethodInfo getter = t.GetMethod("get_" + propName);
+ MethodInfo setter = t.GetMethod("set_" + propName);
+ if(getter!=null)
+ Get = (Func)Delegate.CreateDelegate(typeof(Func), null, getter);
+ if(setter!=null)
+ Set = (Action)Delegate.CreateDelegate(typeof(Action), null, setter);
+ }
+ public static PropAccessor Create(string propName){
+ return new PropAccessor(propName);
+ }
+
+ public object GetValue(object model){
+ return Get((T1)model);
+ }
+
+ public void SetValue(object model, object value){
+ Set((T1)model,(T2)value);
+ }
+ }
+ public class PropertyNotFoundException : Exception{
+ public PropertyNotFoundException(string path):base("Property not found :"+path){
+
+ }
+ }
public class RuntimeCache{
//Singleton instance for the runtime cache
private static RuntimeCache _instance;
@@ -18,18 +47,16 @@ namespace TNodeCore.RuntimeCache{
public delegate object GetValueDelegate(IModel nodeData);
public delegate void SetValueDelegate(IModel nodeData,object value);
- public delegate object GetPropertyValueDelegate();
- public delegate void SetPropertyValueDelegate(object value);
+
public readonly Dictionary> CachedDelegatesForGettingValue =
new ();
public readonly Dictionary> CachedDelegatesForSettingValue =
new ();
- public readonly Dictionary> CachedDelegatesForGettingPropertyValue =
- new ();
- public readonly Dictionary> CachedDelegatesForSettingPropertyValue =
+ public readonly Dictionary> CachedPropertyAccessors =
new ();
+
private readonly Dictionary _graphBlackboardDictionary = new Dictionary();
private static readonly string[] ExcludedAssemblies = new string[]{"Microsoft", "UnityEngine","UnityEditor","mscorlib","System"};
@@ -46,23 +73,28 @@ namespace TNodeCore.RuntimeCache{
if (attribute is GraphUsageAttribute){
//if the type has GraphUsageAttribute, add it to the cache
AddTypeToCache(type,attribute as GraphUsageAttribute);
-
+ }
+
+ if (attribute is InternalModel){
+ AddTypeToCache(type,attribute as InternalModel);
}
}
}
+
+
}
- private void AddTypeToCache(Type type,GraphUsageAttribute attribute){
+ private void AddTypeToCache(Type type,System.Attribute attribute){
//Check if the type is a blackboard data type
if(typeof(BlackboardData).IsAssignableFrom(type)){
//if it is, add it to the cache
- AddBlackboardDataTypeToCache(type,attribute);
- RegisterRuntimeBlackboard(type);
+ AddBlackboardDataTypeToCache(type,(GraphUsageAttribute)attribute);
+ CacheRuntimeBlackboard(type);
}
//Check if the type is a node data type
if(typeof(NodeData).IsAssignableFrom(type)){
//if it is, add it to the cache
- RegisterRuntimeNodeData(type);
+ CacheRuntimeNodeData(type);
}
}
@@ -87,7 +119,7 @@ namespace TNodeCore.RuntimeCache{
}
return null;
}
- public void RegisterRuntimeBlackboard(Type type){
+ public void CacheRuntimeBlackboard(Type type){
if (type == null) return;
if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary());
@@ -114,29 +146,24 @@ namespace TNodeCore.RuntimeCache{
}
}
}
-
- public void RegisterRuntimeNodeData(Type type){
+ public static IModelPropertyAccessor Create(string propName,Type targetType,Type valueType){
+ var makeGenericType = typeof (PropAccessor<,>).MakeGenericType(targetType,valueType);
+ var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)});
+ var instance = constructor?.Invoke(new object[]{propName});
+ return (IModelPropertyAccessor) instance;
+ }
+ public void CacheRuntimeNodeData(Type type){
if (type == null) return;
if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary());
CachedDelegatesForSettingValue.Add(type,new Dictionary());
-
+ CachedPropertyAccessors.Add(type,new Dictionary());
var properties = type.GetProperties();
foreach(var property in properties){
- //if the property only has a setter ,skip
-
- // if(property.GetSetMethod(false) != null){
- // var setValueDelegate = SetValueDelegateForProperty(property);
- // CachedDelegatesForSettingPropertyValue[type].Add(property.Name,setValueDelegate);
- // }
- // if(property.GetMethod != null){
- // var getValueDelegate = GetValueDelegateForProperty(property);
- // CachedDelegatesForGettingPropertyValue[type].Add(property.Name,getValueDelegate);
- // }
-
-
+ var propertyAccessor = Create(property.Name,type,property.PropertyType);
+ CachedPropertyAccessors[type].Add(property.Name,propertyAccessor);
}
//register the fields
var fields = type.GetFields();
@@ -160,18 +187,9 @@ namespace TNodeCore.RuntimeCache{
return field.SetValue;
}
- private GetPropertyValueDelegate GetValueDelegateForProperty(PropertyInfo property){
- var getValueDelegate = (GetPropertyValueDelegate)Delegate.CreateDelegate(typeof(GetPropertyValueDelegate), property.GetGetMethod());
- return getValueDelegate;
- }
- private SetPropertyValueDelegate SetValueDelegateForProperty(PropertyInfo property){
- Debug.Log(property.GetSetMethod());
-
- var setValueDelegate = (SetPropertyValueDelegate)Delegate.CreateDelegate(typeof(SetPropertyValueDelegate), property.GetSetMethod());
- return setValueDelegate;
- }
-
+
}
+
public static class RuntimeExtension{
//todo latter on i will try some way caching reflection more efficiently
@@ -192,46 +210,5 @@ namespace TNodeCore.RuntimeCache{
var method = RuntimeCache.Instance.CachedDelegatesForSettingValue[type??data.GetType()][path];
method.Invoke(data,value);
}
-
- public static RuntimeCache.GetValueDelegate GetValueDelegate(this IModel blackboardData,string path){
- var method = RuntimeCache.Instance.CachedDelegatesForGettingValue[blackboardData.GetType()][path];
- return method;
- }
- ///
- /// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need
- ///
- ///
- ///
- ///
- ///
- public static RuntimeCache.GetPropertyValueDelegate CacheGetProperty(this IModel data,string path){
- var type = data.GetType();
- var property = type.GetProperty(path);
- if (property == null) throw new PropertyNotFoundException(path);
- var instance = Delegate.CreateDelegate(typeof(RuntimeCache.GetPropertyValueDelegate), data,
- property.GetGetMethod());
- return instance as RuntimeCache.GetPropertyValueDelegate;
- }
- ///
- /// it generate a delegate that can get the value fast,but it won't cache in runtime cache system,you should put it in somewhere you need
- ///
- ///
- ///
- ///
- ///
- public static RuntimeCache.SetPropertyValueDelegate CacheSetProperty(this IModel data,string path){
- var type = data.GetType();
- var property = type.GetProperty(path);
- if (property == null) throw new PropertyNotFoundException(path);
- var instance = Delegate.CreateDelegate(typeof(RuntimeCache.SetPropertyValueDelegate), data,
- property.GetSetMethod());
- return instance as RuntimeCache.SetPropertyValueDelegate;
- }
- }
-
- public class PropertyNotFoundException : Exception{
- public PropertyNotFoundException(string path):base("Property not found :"+path){
-
- }
}
}
\ No newline at end of file
diff --git a/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs b/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
index 702151d..1c03a31 100644
--- a/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
+++ b/TNodeGraphViewImpl/Editor/Inspector/NodeInspectorInNode.cs
@@ -57,7 +57,8 @@ namespace TNode.Editor.Inspector{
}
- var globalTest = GetFirstAncestorOfType()?.BaseDataGraphView?.TestMode;
+
+ var globalTest = GetFirstAncestorOfType()?.TestMode;
if(globalTest??false){
CreateTestButton();
}
@@ -74,7 +75,7 @@ namespace TNode.Editor.Inspector{
if(!test.IsRuntimeGraph) return;
var runtimeGraph = test.GetRuntimeGraph();
if (runtimeGraph != null){
- runtimeGraph.ResolveDependency(_data);
+ var res = runtimeGraph.ResolveDependency(_data);
}
_data.OnTest();
}
diff --git a/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs b/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
index f07e68c..216cb24 100644
--- a/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
+++ b/TNodeGraphViewImpl/Editor/NodeGraphView/DataGraphView.cs
@@ -71,12 +71,10 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
/// Probably reusable in later GTFs version
///
private void WaitingForAGraph(){
- Debug.Log("hello");
VisualElement visualElement = new VisualElement();
//Set background color to white
visualElement.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 1));
- Debug.Log("hello2");
visualElement.StretchToParentSize();
visualElement.name = "WaitingForAGraph";
Add(visualElement);
@@ -279,8 +277,8 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
var outputNodeView = _nodeDict[outputNode.id];
Edge newEdge = new Edge(){
- input = inputNodeView.inputContainer.Q(edge.inPort.portName),
- output = outputNodeView.outputContainer.Q(edge.outPort.portName)
+ input = inputNodeView.inputContainer.Q(edge.inPort.portEntryName),
+ output = outputNodeView.outputContainer.Q(edge.outPort.portEntryName)
};
newEdge.input?.Connect(newEdge);
@@ -378,11 +376,11 @@ namespace TNodeGraphViewImpl.Editor.NodeGraphView{
var outputNodeData = outputNode.GetNodeData();
var newNodeLink = new NodeLink(new PortInfo(){
nodeDataId = inputNodeData.id,
- portName = edge.input.portName,
+ portEntryName = edge.input.name,
}, new PortInfo(){
nodeDataId = outputNodeData.id,
- portName = edge.output.portName
+ portEntryName = edge.output.name
});
links.Add(newNodeLink);
}
diff --git a/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs b/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs
index 5a9c81e..a3417da 100644
--- a/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs
+++ b/TNodeGraphViewImpl/Editor/NodeViews/NodeView.cs
@@ -108,7 +108,8 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{
this.outputContainer.Add(port);
var portName = BuildPortName(attribute,propertyInfo);
port.portName = portName;
- port.name = portName;
+ port.name = propertyInfo.Name;
+
}
}
foreach (var propertyInfo in propertyInfos){
@@ -117,7 +118,7 @@ namespace TNodeGraphViewImpl.Editor.NodeViews{
this.inputContainer.Add(port);
var portName = BuildPortName(attribute,propertyInfo);
port.portName = portName;
- port.name = portName;
+ port.name = propertyInfo.Name;
}
}
}