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}