feature:method ports

main
taoria 3 years ago
parent 5e4c012a5e
commit 9504e69478
  1. 50
      Samples/New HelloGraph.asset
  2. 2
      TNodeCore/Runtime/Attributes/Ports/PortAttribute.cs
  3. 13
      TNodeCore/Runtime/Components/ConditionalGraph.cs
  4. 2
      TNodeCore/Runtime/ConditionalRuntimeNode.cs
  5. 3
      TNodeCore/Runtime/Models/NodeData.cs
  6. 109
      TNodeCore/Runtime/RuntimeCache/RuntimeCache.cs
  7. 1
      TNodeCore/Runtime/RuntimeNode.cs

@ -17,7 +17,6 @@ MonoBehaviour:
- id: 1 - id: 1
- id: 2 - id: 2
- id: 3 - id: 3
- id: 4
nodeLinks: nodeLinks:
- inPort: - inPort:
portEntryName: A portEntryName: A
@ -38,12 +37,12 @@ MonoBehaviour:
portEntryName: Value portEntryName: Value
nodeDataId: 06f5acb9-58d5-41b3-b7f5-bd90f7f9ab94 nodeDataId: 06f5acb9-58d5-41b3-b7f5-bd90f7f9ab94
blackboardData: blackboardData:
id: 5 id: 4
sceneReference: sceneReference:
editorModels: editorModels:
- id: 6 - id: 5
graphViewModel: graphViewModel:
id: 7 id: 6
references: references:
version: 1 version: 1
00000000: 00000000:
@ -52,7 +51,7 @@ MonoBehaviour:
positionInView: positionInView:
serializedVersion: 2 serializedVersion: 2
x: -345 x: -345
y: 21 y: -42
width: 0 width: 0
height: 0 height: 0
id: 23526615-f15c-4bdf-96c2-38a57d30b10d id: 23526615-f15c-4bdf-96c2-38a57d30b10d
@ -64,25 +63,12 @@ MonoBehaviour:
blackDragData: HelloGameObject blackDragData: HelloGameObject
isListElement: 0 isListElement: 0
00000001: 00000001:
type: {class: AddNode, ns: Samples, asm: Assembly-CSharp}
data:
positionInView:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
id: f33d73da-40c5-4b3a-ace7-40105e3489b6
nodeName: AddNode
entryPoint: 0
isTest: 0
00000002:
type: {class: BlackboardDragNode, ns: TNodeCore.Runtime.Models, asm: TNodeCore} type: {class: BlackboardDragNode, ns: TNodeCore.Runtime.Models, asm: TNodeCore}
data: data:
positionInView: positionInView:
serializedVersion: 2 serializedVersion: 2
x: -345 x: -345
y: 60 y: -84
width: 0 width: 0
height: 0 height: 0
id: ae519ad8-4bc8-46c5-a98a-dc95bc85b58a id: ae519ad8-4bc8-46c5-a98a-dc95bc85b58a
@ -93,13 +79,13 @@ MonoBehaviour:
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
blackDragData: HelloGameObject blackDragData: HelloGameObject
isListElement: 0 isListElement: 0
00000003: 00000002:
type: {class: BlackboardDragNode, ns: TNodeCore.Runtime.Models, asm: TNodeCore} type: {class: BlackboardDragNode, ns: TNodeCore.Runtime.Models, asm: TNodeCore}
data: data:
positionInView: positionInView:
serializedVersion: 2 serializedVersion: 2
x: -345 x: -345
y: -21 y: -126
width: 0 width: 0
height: 0 height: 0
id: 06f5acb9-58d5-41b3-b7f5-bd90f7f9ab94 id: 06f5acb9-58d5-41b3-b7f5-bd90f7f9ab94
@ -110,24 +96,20 @@ MonoBehaviour:
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
blackDragData: HelloGameObject blackDragData: HelloGameObject
isListElement: 0 isListElement: 0
00000004: 00000003:
type: {class: BlackboardDragNode, ns: TNodeCore.Runtime.Models, asm: TNodeCore} type: {class: AddNode, ns: Samples, asm: Assembly-CSharp}
data: data:
positionInView: positionInView:
serializedVersion: 2 serializedVersion: 2
x: 0 x: -17
y: 0 y: -18
width: 0 width: 0
height: 0 height: 0
id: a705abd9-5c22-4410-812f-d824ddfa60d8 id: f33d73da-40c5-4b3a-ace7-40105e3489b6
nodeName: nodeName: AddNode
entryPoint: 0 entryPoint: 0
isTest: 0 isTest: 0
blackboardDragTypeString: System.String, mscorlib, Version=4.0.0.0, Culture=neutral, 00000004:
PublicKeyToken=b77a5c561934e089
blackDragData: HelloString
isListElement: 0
00000005:
type: {class: HelloBlackboard, ns: TNode.Samples, asm: Assembly-CSharp} type: {class: HelloBlackboard, ns: TNode.Samples, asm: Assembly-CSharp}
data: data:
positionInView: positionInView:
@ -143,7 +125,7 @@ MonoBehaviour:
- {x: 0, y: 0, z: 0} - {x: 0, y: 0, z: 0}
V2S: V2S:
- {x: 0, y: 0} - {x: 0, y: 0}
00000006: 00000005:
type: {class: PlacematModel, ns: TNode.TNodeCore.Editor.Models, asm: TNodeCore} type: {class: PlacematModel, ns: TNode.TNodeCore.Editor.Models, asm: TNodeCore}
data: data:
positionInView: positionInView:
@ -156,7 +138,7 @@ MonoBehaviour:
hostModels: [] hostModels: []
zOrder: 0 zOrder: 0
title: Title title: Title
00000007: 00000006:
type: {class: GraphViewModel, ns: TNode.TNodeCore.Editor.Models, asm: TNodeCore} type: {class: GraphViewModel, ns: TNode.TNodeCore.Editor.Models, asm: TNodeCore}
data: data:
positionInView: positionInView:

@ -32,7 +32,7 @@ namespace TNodeCore.Runtime.Attributes.Ports{
Path Path
} }
[MeansImplicitUse] [MeansImplicitUse]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = true)]
public class PortAttribute:System.Attribute{ public class PortAttribute:System.Attribute{
public readonly string Name; public readonly string Name;
public readonly PortNameHandling NameHandling; public readonly PortNameHandling NameHandling;

@ -17,6 +17,17 @@ namespace TNode.TNodeCore.Runtime.Components{
} }
EntryNode = entry.FirstOrDefault() as ConditionalRuntimeNode; EntryNode = entry.FirstOrDefault() as ConditionalRuntimeNode;
} }
public void Run(){
var res = StepForward();
while (StepForward().MoveNext()){
}
}
public IEnumerator StepForward(){ public IEnumerator StepForward(){
CurrentNode = EntryNode; CurrentNode = EntryNode;
@ -24,7 +35,7 @@ namespace TNode.TNodeCore.Runtime.Components{
//First let's process the node //First let's process the node
CurrentNode.NodeData.Process(); CurrentNode.NodeData.Process();
//Then check if there are conditional transitions //Then check if there are conditional transitions
var conditionalTransitions = CurrentNode.GetNextNodesId(); var conditionalTransitions = CurrentNode.GetConditionalNextIds();
var transitionNode = new List<RuntimeNode>(); var transitionNode = new List<RuntimeNode>();
foreach (var conditionalTransition in conditionalTransitions){ foreach (var conditionalTransition in conditionalTransitions){

@ -22,7 +22,7 @@ namespace TNodeCore.Runtime{
} }
} }
public string[] GetNextNodesId(){ public string[] GetConditionalNextIds(){
var ports = PossibleTransition.Where(x => x.Item2()); var ports = PossibleTransition.Where(x => x.Item2());
var portNames = ports.Select(x => x.Item1); var portNames = ports.Select(x => x.Item1);
//Search output links to found the link contains portNames as outport's name //Search output links to found the link contains portNames as outport's name

@ -26,6 +26,9 @@ namespace TNodeCore.Runtime.Models{
public virtual void Process(){ public virtual void Process(){
} }
public virtual IEnumerator AfterProcess(){ public virtual IEnumerator AfterProcess(){
yield return null; yield return null;
} }

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using TNodeCore.Runtime.Attributes; using TNodeCore.Runtime.Attributes;
using TNodeCore.Runtime.Attributes.Ports;
using TNodeCore.Runtime.Interfaces; using TNodeCore.Runtime.Interfaces;
using TNodeCore.Runtime.Models; using TNodeCore.Runtime.Models;
using UnityEngine; using UnityEngine;
@ -34,10 +35,43 @@ namespace TNodeCore.Runtime.RuntimeCache{
public void SetValue(object model, object value){ public void SetValue(object model, object value){
Set((T1)model,(T2)value); Set((T1)model,(T2)value);
} }
public Type Type{ get; set; } public Type Type{ get; set; }
} }
public class MethodAccessorInput<T1, T2>:IMethodAccessorInput{
public readonly Action<T1, T2> Set;
public MethodAccessorInput(string methodName){
Type t = typeof(T1);
MethodInfo setter = t.GetMethod(methodName);
if (setter != null)
Set = (Action<T1, T2>) Delegate.CreateDelegate(typeof(Action<T1, T2>), null, setter);
}
public void SetValue(object model,object value){
Set((T1)model,(T2)value);
}
}
public class MethodAccessorOutput<T1, T2> : IMethodAccessorOutput{
public readonly Func<T1, T2> Get;
public MethodAccessorOutput(string methodName){
Type t = typeof(T1);
MethodInfo getter = t.GetMethod(methodName);
if(getter!=null)
Get = (Func<T1, T2>)Delegate.CreateDelegate(typeof(Func<T1, T2>), null, getter);
}
public object GetValue(object model){
return Get((T1)model);
}
}
public interface IMethodAccessorInput{
public void SetValue(object model,object o);
}
internal class PortConverterHelper<T1,T2> : IPortConverterHelper{ internal class PortConverterHelper<T1,T2> : IPortConverterHelper{
private readonly PortTypeConversion<T1, T2> _converter; private readonly PortTypeConversion<T1, T2> _converter;
public PortConverterHelper(Type type){ public PortConverterHelper(Type type){
@ -78,6 +112,8 @@ namespace TNodeCore.Runtime.RuntimeCache{
} }
//delegate return a value from a nodedata //delegate return a value from a nodedata
public delegate object GetValueDelegate(Model nodeData); public delegate object GetValueDelegate(Model nodeData);
public delegate void CachedInputFunction(Model nodeData,object value);
public delegate object CachedOutputFunction(Model nodeData);
public delegate void SetValueDelegate(Model nodeData,object value); public delegate void SetValueDelegate(Model nodeData,object value);
@ -88,6 +124,11 @@ namespace TNodeCore.Runtime.RuntimeCache{
new Dictionary<Type,Dictionary<string,SetValueDelegate>>(); new Dictionary<Type,Dictionary<string,SetValueDelegate>>();
public readonly Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> CachedPropertyAccessors = public readonly Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> CachedPropertyAccessors =
new Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> (); new Dictionary<Type,Dictionary<string,IModelPropertyAccessor>> ();
public readonly Dictionary<Type,Dictionary<string,IMethodAccessorInput>> InputMethodPorts = new Dictionary<Type, Dictionary<string,IMethodAccessorInput>>();
public readonly Dictionary<Type, Dictionary<string, IMethodAccessorOutput>> OutputMethodPorts =
new Dictionary<Type, Dictionary<string, IMethodAccessorOutput>>();
/// <summary> /// <summary>
/// TODO: Converters now work globally, but it should be possible to specify a converter for a specific graph.but it will be too nested.so in current implementation, we will use a global converter. /// TODO: Converters now work globally, but it should be possible to specify a converter for a specific graph.but it will be too nested.so in current implementation, we will use a global converter.
/// </summary> /// </summary>
@ -179,6 +220,7 @@ namespace TNodeCore.Runtime.RuntimeCache{
} }
} }
private readonly Dictionary<Tuple<Type,Type>,bool> _possibleImplicitConversions = new Dictionary<Tuple<Type,Type>,bool> (); private readonly Dictionary<Tuple<Type,Type>,bool> _possibleImplicitConversions = new Dictionary<Tuple<Type,Type>,bool> ();
public bool HasImplicitConversion(Type baseType, Type targetType){ public bool HasImplicitConversion(Type baseType, Type targetType){
var tuple = new Tuple<Type, Type>(baseType, targetType); var tuple = new Tuple<Type, Type>(baseType, targetType);
if (_possibleImplicitConversions.ContainsKey(tuple)){ if (_possibleImplicitConversions.ContainsKey(tuple)){
@ -277,23 +319,39 @@ namespace TNodeCore.Runtime.RuntimeCache{
} }
} }
} }
public static IModelPropertyAccessor Create(string propName,Type targetType,Type valueType){ //TODO: CACHE IT AS FUNCTION
private static IModelPropertyAccessor CreatePropertyCache(string propName,Type targetType,Type valueType){
var makeGenericType = typeof (PropAccessor<,>).MakeGenericType(targetType,valueType); var makeGenericType = typeof (PropAccessor<,>).MakeGenericType(targetType,valueType);
var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)}); var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)});
var instance = constructor?.Invoke(new object[]{propName}); var instance = constructor?.Invoke(new object[]{propName});
return (IModelPropertyAccessor) instance; return (IModelPropertyAccessor) instance;
} }
public static IMethodAccessorInput CreateMethodInputCache(string methodName,Type targetType,Type inputTypes){
var makeGenericType = typeof (MethodAccessorInput<,>).MakeGenericType(targetType,inputTypes);
var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)});
var instance = constructor?.Invoke(new object[]{methodName});
return (IMethodAccessorInput) instance;
}
public static IMethodAccessorOutput CreateMethodOutputCache(string methodName,Type targetType,Type outputTypes){
var makeGenericType = typeof (MethodAccessorOutput<,>).MakeGenericType(targetType,outputTypes);
var constructor = makeGenericType.GetConstructor(new Type[]{typeof(string)});
var instance = constructor?.Invoke(new object[]{methodName});
return (IMethodAccessorOutput) instance;
}
public void CacheRuntimeNodeData(Type type){ public void CacheRuntimeNodeData(Type type){
if (type == null) return; if (type == null) return;
if(!CachedDelegatesForGettingValue.ContainsKey(type)){ if(!CachedDelegatesForGettingValue.ContainsKey(type)){
CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>()); CachedDelegatesForGettingValue.Add(type, new Dictionary<string, GetValueDelegate>());
CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>()); CachedDelegatesForSettingValue.Add(type,new Dictionary<string, SetValueDelegate>());
CachedPropertyAccessors.Add(type,new Dictionary<string, IModelPropertyAccessor>()); CachedPropertyAccessors.Add(type,new Dictionary<string, IModelPropertyAccessor>());
InputMethodPorts.Add(type,new Dictionary<string,IMethodAccessorInput>());
OutputMethodPorts.Add(type,new Dictionary<string,IMethodAccessorOutput>());
var properties = type.GetProperties(); var properties = type.GetProperties();
foreach(var property in properties){ foreach(var property in properties){
var propertyAccessor = Create(property.Name,type,property.PropertyType); var propertyAccessor = CreatePropertyCache(property.Name,type,property.PropertyType);
CachedPropertyAccessors[type].Add(property.Name,propertyAccessor); CachedPropertyAccessors[type].Add(property.Name,propertyAccessor);
} }
//register the fields //register the fields
@ -308,8 +366,46 @@ namespace TNodeCore.Runtime.RuntimeCache{
} }
} }
var methods = type.GetMethods();
foreach(var method in methods){
//Check if the method has an [Port] attribute
var portAttribute = method.GetCustomAttribute<PortAttribute>();
if(portAttribute != null){
//if the port is an input port. cached it as an function of setting value
if(portAttribute is InputAttribute){
var inputMethodAccessor = CreateMethodInputCache(method.Name, type,
method.GetParameters()[0].ParameterType);
InputMethodPorts[type].Add(method.Name,inputMethodAccessor);
}
if (portAttribute is OutputAttribute){
var outputMethodAccessor = CreateMethodOutputCache(method.Name, type,
method.ReturnType);
OutputMethodPorts[type].Add(method.Name,outputMethodAccessor);
}
}
}
}
}
private SetValueDelegate SetValueDelegateForMethod(MethodInfo method){
//Check if the method has not an return type
if(method.ReturnType == typeof(void)){
//check if the method has only one parameter
if(method.GetParameters().Length == 1){
//Cache the method
var methodDelegate = (SetValueDelegate) Delegate.CreateDelegate(typeof(SetValueDelegate),method);
return methodDelegate;
}
} }
return null;
} }
private GetValueDelegate GetValueDelegateForField(FieldInfo field){ private GetValueDelegate GetValueDelegateForField(FieldInfo field){
return field.GetValue; return field.GetValue;
@ -322,6 +418,11 @@ namespace TNodeCore.Runtime.RuntimeCache{
} }
public interface IMethodAccessorOutput{
object GetValue(object model);
}
public class ImplicitConversionHelper<T1,T2> : IPortConverterHelper{ public class ImplicitConversionHelper<T1,T2> : IPortConverterHelper{
public Func<T1, T2> ConvertFunc; public Func<T1, T2> ConvertFunc;
public ImplicitConversionHelper(){ public ImplicitConversionHelper(){

@ -34,6 +34,7 @@ namespace TNodeCore.Runtime{
public string[] GetPortsOfType<T> (){ public string[] GetPortsOfType<T> (){
var ports = new List<string>(); var ports = new List<string>();
foreach (var port in _portAccessors.Keys){ foreach (var port in _portAccessors.Keys){
if(_portAccessors[port].Type==typeof(T)){ if(_portAccessors[port].Type==typeof(T)){
ports.Add(port); ports.Add(port);
} }

Loading…
Cancel
Save