A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using UnityEngine;
3using UnityObject = UnityEngine.Object;
4
5namespace Unity.VisualScripting
6{
7 /// <remarks>
8 /// Does not support objects hidden with hide flags.
9 /// </remarks>
10 public static class Singleton<T> where T : MonoBehaviour, ISingleton
11 {
12 static Singleton()
13 {
14 awoken = new HashSet<T>();
15
16 attribute = typeof(T).GetAttribute<SingletonAttribute>();
17
18 if (attribute == null)
19 {
20 throw new InvalidImplementationException($"Missing singleton attribute for '{typeof(T)}'.");
21 }
22 }
23
24 private static readonly SingletonAttribute attribute;
25
26 private static bool persistent => attribute.Persistent;
27
28 private static bool automatic => attribute.Automatic;
29
30 private static string name => attribute.Name;
31
32 private static HideFlags hideFlags => attribute.HideFlags;
33
34 private static readonly object _lock = new object();
35
36 private static readonly HashSet<T> awoken;
37
38 private static T _instance;
39
40 public static bool instantiated
41 {
42 get
43 {
44 lock (_lock)
45 {
46 if (Application.isPlaying)
47 {
48 return _instance != null;
49 }
50 else
51 {
52 return FindInstances().Length == 1;
53 }
54 }
55 }
56 }
57
58 public static T instance
59 {
60 get
61 {
62 lock (_lock)
63 {
64 if (Application.isPlaying)
65 {
66 if (_instance == null)
67 {
68 Instantiate();
69 }
70
71 return _instance;
72 }
73 else
74 {
75 return Instantiate();
76 }
77 }
78 }
79 }
80
81 private static T[] FindObjectsOfType()
82 {
83#if UNITY_2023_1_OR_NEWER
84 return UnityObject.FindObjectsByType<T>(FindObjectsSortMode.None);
85#else
86 return UnityObject.FindObjectsOfType<T>();
87#endif
88 }
89
90 private static T[] FindInstances()
91 {
92 // Fails here on hidden hide flags
93 return FindObjectsOfType();
94 }
95
96 public static T Instantiate()
97 {
98 lock (_lock)
99 {
100 var instances = FindInstances();
101
102 if (instances.Length == 1)
103 {
104 _instance = instances[0];
105 }
106 else if (instances.Length == 0)
107 {
108 if (automatic)
109 {
110 // Create the parent game object with the proper hide flags
111 var singleton = new GameObject(name ?? typeof(T).Name);
112 singleton.hideFlags = hideFlags;
113
114 // Instantiate the component, letting Awake assign the real instance variable
115 var _instance = singleton.AddComponent<T>();
116 _instance.hideFlags = hideFlags;
117
118 // Sometimes in the editor, for example when creating a new scene,
119 // AddComponent seems to call Awake add a later frame, making this call
120 // fail for exactly one frame. We'll force-awake it if need be.
121 Awake(_instance);
122
123 // Make the singleton persistent if need be
124 if (persistent && Application.isPlaying)
125 {
126 UnityObject.DontDestroyOnLoad(singleton);
127 }
128 }
129 else
130 {
131 throw new UnityException($"Missing '{typeof(T)}' singleton in the scene.");
132 }
133 }
134 else if (instances.Length > 1)
135 {
136 throw new UnityException($"More than one '{typeof(T)}' singleton in the scene.");
137 }
138
139 return _instance;
140 }
141 }
142
143 public static void Awake(T instance)
144 {
145 Ensure.That(nameof(instance)).IsNotNull(instance);
146
147 if (awoken.Contains(instance))
148 {
149 return;
150 }
151
152 if (_instance != null)
153 {
154 throw new UnityException($"More than one '{typeof(T)}' singleton in the scene.");
155 }
156
157 _instance = instance;
158
159 awoken.Add(instance);
160 }
161
162 public static void OnDestroy(T instance)
163 {
164 Ensure.That(nameof(instance)).IsNotNull(instance);
165
166 if (_instance == instance)
167 {
168 _instance = null;
169 }
170 else
171 {
172 throw new UnityException($"Trying to destroy invalid instance of '{typeof(T)}' singleton.");
173 }
174 }
175 }
176}