CustomPortIO.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using System;
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. using System.Linq.Expressions;
  6. namespace GraphProcessor
  7. {
  8. public delegate void CustomPortIODelegate(BaseNode node, List< SerializableEdge > edges, NodePort outputPort = null);
  9. public static class CustomPortIO
  10. {
  11. class PortIOPerField : Dictionary< string, CustomPortIODelegate > {}
  12. class PortIOPerNode : Dictionary< Type, PortIOPerField > {}
  13. static Dictionary< Type, List< Type > > assignableTypes = new Dictionary< Type, List< Type > >();
  14. static PortIOPerNode customIOPortMethods = new PortIOPerNode();
  15. static CustomPortIO()
  16. {
  17. LoadCustomPortMethods();
  18. }
  19. static void LoadCustomPortMethods()
  20. {
  21. BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  22. foreach (var type in AppDomain.CurrentDomain.GetAllTypes())
  23. {
  24. if (type.IsAbstract || type.ContainsGenericParameters)
  25. continue ;
  26. if (!(type.IsSubclassOf(typeof(BaseNode))))
  27. continue ;
  28. var methods = type.GetMethods(bindingFlags);
  29. foreach (var method in methods)
  30. {
  31. var portInputAttr = method.GetCustomAttribute< CustomPortInputAttribute >();
  32. var portOutputAttr = method.GetCustomAttribute< CustomPortOutputAttribute >();
  33. if (portInputAttr == null && portOutputAttr == null)
  34. continue ;
  35. var p = method.GetParameters();
  36. bool nodePortSignature = false;
  37. // Check if the function can take a NodePort in optional param
  38. if (p.Length == 2 && p[1].ParameterType == typeof(NodePort))
  39. nodePortSignature = true;
  40. CustomPortIODelegate deleg;
  41. #if ENABLE_IL2CPP
  42. // IL2CPP doesn't support expression builders
  43. if (nodePortSignature)
  44. {
  45. deleg = new CustomPortIODelegate((node, edges, port) => {
  46. Debug.Log(port);
  47. method.Invoke(node, new object[]{ edges, port});
  48. });
  49. }
  50. else
  51. {
  52. deleg = new CustomPortIODelegate((node, edges, port) => {
  53. method.Invoke(node, new object[]{ edges });
  54. });
  55. }
  56. #else
  57. var p1 = Expression.Parameter(typeof(BaseNode), "node");
  58. var p2 = Expression.Parameter(typeof(List< SerializableEdge >), "edges");
  59. var p3 = Expression.Parameter(typeof(NodePort), "port");
  60. MethodCallExpression ex;
  61. if (nodePortSignature)
  62. ex = Expression.Call(Expression.Convert(p1, type), method, p2, p3);
  63. else
  64. ex = Expression.Call(Expression.Convert(p1, type), method, p2);
  65. deleg = Expression.Lambda< CustomPortIODelegate >(ex, p1, p2, p3).Compile();
  66. #endif
  67. if (deleg == null)
  68. {
  69. Debug.LogWarning("Can't use custom IO port function " + method + ": The method have to respect this format: " + typeof(CustomPortIODelegate));
  70. continue ;
  71. }
  72. string fieldName = (portInputAttr == null) ? portOutputAttr.fieldName : portInputAttr.fieldName;
  73. Type customType = (portInputAttr == null) ? portOutputAttr.outputType : portInputAttr.inputType;
  74. Type fieldType = type.GetField(fieldName, bindingFlags).FieldType;
  75. AddCustomIOMethod(type, fieldName, deleg);
  76. AddAssignableTypes(customType, fieldType);
  77. AddAssignableTypes(fieldType, customType);
  78. }
  79. }
  80. }
  81. public static CustomPortIODelegate GetCustomPortMethod(Type nodeType, string fieldName)
  82. {
  83. PortIOPerField portIOPerField;
  84. CustomPortIODelegate deleg;
  85. customIOPortMethods.TryGetValue(nodeType, out portIOPerField);
  86. if (portIOPerField == null)
  87. return null;
  88. portIOPerField.TryGetValue(fieldName, out deleg);
  89. return deleg;
  90. }
  91. static void AddCustomIOMethod(Type nodeType, string fieldName, CustomPortIODelegate deleg)
  92. {
  93. if (!customIOPortMethods.ContainsKey(nodeType))
  94. customIOPortMethods[nodeType] = new PortIOPerField();
  95. customIOPortMethods[nodeType][fieldName] = deleg;
  96. }
  97. static void AddAssignableTypes(Type fromType, Type toType)
  98. {
  99. if (!assignableTypes.ContainsKey(fromType))
  100. assignableTypes[fromType] = new List< Type >();
  101. assignableTypes[fromType].Add(toType);
  102. }
  103. public static bool IsAssignable(Type input, Type output)
  104. {
  105. if (assignableTypes.ContainsKey(input))
  106. return assignableTypes[input].Contains(output);
  107. return false;
  108. }
  109. }
  110. }