A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Reflection; 5using UnityEngine; 6 7namespace Unity.VisualScripting 8{ 9 /// <summary> 10 /// Exposes all members of the type. 11 /// </summary> 12 [SpecialUnit] 13 public sealed class Expose : Unit, IAotStubbable 14 { 15 public Expose() : base() { } 16 17 public Expose(Type type) : base() 18 { 19 this.type = type; 20 } 21 22 [Serialize, Inspectable, TypeFilter(Enums = false)] 23 public Type type { get; set; } 24 25 [Serialize, Inspectable, UnitHeaderInspectable("Instance"), InspectorToggleLeft] 26 public bool instance { get; set; } = true; 27 28 [Serialize, Inspectable, UnitHeaderInspectable("Static"), InspectorToggleLeft] 29 public bool @static { get; set; } = true; 30 31 /// <summary> 32 /// The instance of the exposed type. 33 /// </summary> 34 [DoNotSerialize] 35 [PortLabelHidden] 36 [NullMeansSelf] 37 public ValueInput target { get; private set; } 38 39 [DoNotSerialize] 40 public Dictionary<ValueOutput, Member> members { get; private set; } 41 42 public override bool canDefine => type != null; 43 44 public override IEnumerable<object> GetAotStubs(HashSet<object> visited) 45 { 46 if (members != null) 47 { 48 foreach (var member in members.Values) 49 { 50 if (member != null && member.isReflected) 51 { 52 yield return member.info; 53 } 54 } 55 } 56 } 57 58 protected override void Definition() 59 { 60 members = new Dictionary<ValueOutput, Member>(); 61 62 var requiresTarget = false; 63 64 foreach (var member in type.GetMembers() 65 .Where(m => m is FieldInfo || m is PropertyInfo) 66 .Select(m => m.ToManipulator(type)) 67 .DistinctBy(m => m.name) // To account for "new" duplicates 68 .Where(Include) 69 .OrderBy(m => m.requiresTarget ? 0 : 1) 70 .ThenBy(m => m.order)) 71 { 72 var memberPort = ValueOutput(member.type, member.name, (flow) => GetValue(flow, member)); 73 74 if (member.isPredictable) 75 { 76 memberPort.Predictable(); 77 } 78 79 members.Add(memberPort, member); 80 81 if (member.requiresTarget) 82 { 83 requiresTarget = true; 84 } 85 } 86 87 if (requiresTarget) 88 { 89 target = ValueInput(type, nameof(target)).NullMeansSelf(); 90 91 target.SetDefaultValue(type.PseudoDefault()); 92 93 foreach (var member in members.Keys) 94 { 95 if (members[member].requiresTarget) 96 { 97 Requirement(target, member); 98 } 99 } 100 } 101 } 102 103 private bool Include(Member member) 104 { 105 if (!instance && member.requiresTarget) 106 { 107 return false; 108 } 109 110 if (!@static && !member.requiresTarget) 111 { 112 return false; 113 } 114 115 if (!member.isPubliclyGettable) 116 { 117 return false; 118 } 119 120 if (member.info.HasAttribute<ObsoleteAttribute>()) 121 { 122 return false; 123 } 124 125 if (member.isIndexer) 126 { 127 return false; 128 } 129 130 // Pesky edit-mode only accessor that is only available in the editor, 131 // yet isn't marked by any special attribute to indicate it. 132 if (member.name == "runInEditMode" && member.declaringType == typeof(MonoBehaviour)) 133 { 134 return false; 135 } 136 137 return true; 138 } 139 140 private object GetValue(Flow flow, Member member) 141 { 142 var target = member.requiresTarget ? flow.GetValue(this.target, member.targetType) : null; 143 144 return member.Get(target); 145 } 146 } 147}