A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using UnityEngine;
3using UnityObject = UnityEngine.Object;
4
5namespace Unity.VisualScripting
6{
7 public abstract class Machine<TGraph, TMacro> : LudiqBehaviour, IMachine
8 where TGraph : class, IGraph, new()
9 where TMacro : Macro<TGraph>
10 {
11 protected Machine()
12 {
13 nest.nester = this;
14 nest.source = GraphSource.Macro;
15 }
16
17 [Serialize]
18 public GraphNest<TGraph, TMacro> nest { get; private set; } = new GraphNest<TGraph, TMacro>();
19
20 [DoNotSerialize]
21 IGraphNest IGraphNester.nest => nest;
22
23 // We use our own alive and enabled bools which are thread safe.
24 // Alive is true in Awake and OnEnable, and becomes false in OnDestroy.
25 // Enabled is not true during Awake, to avoid double OnEnable triggering due to GraphNest delegates
26 // See: https://support.ludiq.io/communities/5/topics/1971-a/
27 [DoNotSerialize]
28 private bool _alive;
29
30 [DoNotSerialize]
31 private bool _enabled;
32
33 [DoNotSerialize]
34 private GameObject threadSafeGameObject;
35
36 [DoNotSerialize]
37 GameObject IMachine.threadSafeGameObject => threadSafeGameObject;
38
39 [DoNotSerialize]
40 private bool isReferenceCached;
41
42 [DoNotSerialize]
43 private GraphReference _reference;
44
45 [DoNotSerialize]
46 protected GraphReference reference => isReferenceCached ? _reference : GraphReference.New(this, false);
47
48 [DoNotSerialize]
49 protected bool hasGraph => reference != null;
50
51 [DoNotSerialize]
52 public TGraph graph => nest.graph;
53
54 [DoNotSerialize]
55 public IGraphData graphData { get; set; }
56
57 [DoNotSerialize]
58 bool IGraphParent.isSerializationRoot => true;
59
60 [DoNotSerialize]
61 UnityObject IGraphParent.serializedObject
62 {
63 get
64 {
65 switch (nest.source)
66 {
67 case GraphSource.Macro: return nest.macro;
68 case GraphSource.Embed: return this;
69 default: throw new UnexpectedEnumValueException<GraphSource>(nest.source);
70 }
71 }
72 }
73
74 [DoNotSerialize]
75 IGraph IGraphParent.childGraph => graph;
76
77 public IEnumerable<object> GetAotStubs(HashSet<object> visited)
78 {
79 return nest.GetAotStubs(visited);
80 }
81
82 public bool isDescriptionValid
83 {
84 get => true;
85 set { }
86 }
87
88 protected virtual void Awake()
89 {
90 _alive = true;
91 threadSafeGameObject = gameObject;
92
93 nest.afterGraphChange += CacheReference;
94 nest.beforeGraphChange += ClearCachedReference;
95
96 CacheReference();
97
98 if (graph != null)
99 {
100 graph.Prewarm();
101 InstantiateNest();
102 }
103 }
104
105 protected virtual void OnEnable()
106 {
107 _enabled = true;
108 }
109
110 protected virtual void OnInstantiateWhileEnabled()
111 {
112 }
113
114 protected virtual void OnUninstantiateWhileEnabled()
115 {
116 }
117
118 protected virtual void OnDisable()
119 {
120 _enabled = false;
121 }
122
123 protected virtual void OnDestroy()
124 {
125 ClearCachedReference();
126
127 if (graph != null)
128 {
129 UninstantiateNest();
130 }
131
132 threadSafeGameObject = null;
133 _alive = false;
134 }
135
136 protected virtual void OnValidate()
137 {
138 threadSafeGameObject = gameObject;
139 }
140
141 public GraphPointer GetReference()
142 {
143 return reference;
144 }
145
146 private void CacheReference()
147 {
148 _reference = GraphReference.New(this, false);
149 isReferenceCached = true;
150 }
151
152 private void ClearCachedReference()
153 {
154 if (_reference != null)
155 {
156 _reference.Release();
157 _reference = null;
158 }
159 }
160
161 public virtual void InstantiateNest()
162 {
163 if (_alive)
164 {
165 GraphInstances.Instantiate(reference);
166 }
167
168 if (_enabled)
169 {
170 if (UnityThread.allowsAPI)
171 {
172 OnInstantiateWhileEnabled();
173 }
174 else
175 {
176 Debug.LogWarning($"Could not run instantiation events on {this.ToSafeString()} because the Unity API is not available.\nThis can happen when undoing / redoing a graph source change.", this);
177 }
178 }
179 }
180
181 public virtual void UninstantiateNest()
182 {
183 if (_enabled)
184 {
185 if (UnityThread.allowsAPI)
186 {
187 OnUninstantiateWhileEnabled();
188 }
189 else
190 {
191 Debug.LogWarning($"Could not run uninstantiation events on {this.ToSafeString()} because the Unity API is not available.\nThis can happen when undoing / redoing a graph source change.", this);
192 }
193 }
194
195 if (_alive)
196 {
197 var instances = GraphInstances.ChildrenOfPooled(this);
198
199 foreach (var instance in instances)
200 {
201 GraphInstances.Uninstantiate(instance);
202 }
203
204 instances.Free();
205 }
206 }
207
208#if MODULE_ANIMATION_EXISTS
209 // Should be in EventMachine logically, but kept here for legacy compatibility.
210 public virtual void TriggerAnimationEvent(AnimationEvent animationEvent)
211 {
212 }
213#endif
214
215 // Should be in EventMachine logically, but kept here for legacy compatibility.
216 public virtual void TriggerUnityEvent(string name)
217 {
218 }
219
220 public abstract TGraph DefaultGraph();
221
222 IGraph IGraphParent.DefaultGraph() => DefaultGraph();
223 }
224}