A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using UnityObject = UnityEngine.Object;
4
5namespace Unity.VisualScripting
6{
7 [DisableAnnotation]
8 public abstract class Macro<TGraph> : MacroScriptableObject, IMacro
9 where TGraph : class, IGraph, new()
10 {
11 [SerializeAs(nameof(graph))]
12 private TGraph _graph = new TGraph();
13
14 [DoNotSerialize]
15 public TGraph graph
16 {
17 get => _graph;
18 set
19 {
20 if (value == null)
21 {
22 throw new InvalidOperationException("Macros must have a graph.");
23 }
24
25 if (value == graph)
26 {
27 return;
28 }
29
30 _graph = value;
31 }
32 }
33
34 [DoNotSerialize]
35 IGraph IMacro.graph
36 {
37 get => graph;
38 set => graph = (TGraph)value;
39 }
40
41 [DoNotSerialize]
42 IGraph IGraphParent.childGraph => graph;
43
44 public IEnumerable<object> GetAotStubs(HashSet<object> visited)
45 {
46 return graph.GetAotStubs(visited);
47 }
48
49 [DoNotSerialize]
50 bool IGraphParent.isSerializationRoot => true;
51
52 [DoNotSerialize]
53 UnityObject IGraphParent.serializedObject => this;
54
55 [DoNotSerialize]
56 private GraphReference _reference = null;
57
58 [DoNotSerialize]
59 protected GraphReference reference => _reference == null ? GraphReference.New(this, false) : _reference;
60
61 public bool isDescriptionValid
62 {
63 get => true;
64 set { }
65 }
66
67 protected override void OnBeforeDeserialize()
68 {
69 base.OnBeforeDeserialize();
70
71 Serialization.NotifyDependencyDeserializing(this);
72 }
73
74 protected override void OnAfterDeserialize()
75 {
76 base.OnAfterDeserialize();
77
78 Serialization.NotifyDependencyDeserialized(this);
79 }
80
81 public abstract TGraph DefaultGraph();
82
83 IGraph IGraphParent.DefaultGraph()
84 {
85 return DefaultGraph();
86 }
87
88 // This seems to fix the legendary undo bug!
89 // https://support.ludiq.io/communities/5/topics/4434-undo-bug-isolated
90 // The issue seems to be that newly created assets don't receive OnAfterDeserialize,
91 // and therefore never notify the dependencies system that they became available.
92 // Therefore, if any graph relied on a macro dependency (super unit, super state, flow state, state unit)
93 // that was created before a deserialization of that dependency (usually enter/exit play mode, restart Unity),
94 // it would silently never load, not throwing any error or warning along the way.
95 // For example, creating a new flow macro, dragging it to create a super node in another graph,
96 // then undoing, would corrupt the parent graph.
97 // Note: this *could* go in Awake, but OnEnable seems to be more reliable and consistent. Awake
98 // doesn't get called in play mode entry for example (but that doesn't matter because OnAfterDeserialize does anyway).
99 protected virtual void OnEnable()
100 {
101 Serialization.NotifyDependencyAvailable(this);
102 }
103
104 // ScriptableObjects actually call OnDisable not OnDestroy when unloaded ("goes out of scope"),
105 // so we need to unregister the dependency here.
106 // https://forum.unity.com/threads/scriptableobject-behaviour-discussion-how-scriptable-objects-work.541212/
107 // The doc also guarantees it will be called before OnDestroy, so no need to repeat that in OnDestory.
108 protected virtual void OnDisable()
109 {
110 Serialization.NotifyDependencyUnavailable(this);
111 }
112
113 public GraphPointer GetReference()
114 {
115 return reference;
116 }
117
118 bool ISerializationDependency.IsDeserialized { get; set; }
119 }
120}