A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Runtime.Serialization;
5using Unity.VisualScripting.FullSerializer;
6#if UNITY_EDITOR
7using UnityEditor;
8#endif
9using Debug = UnityEngine.Debug;
10using UnityObject = UnityEngine.Object;
11
12namespace Unity.VisualScripting
13{
14 public static class Serialization
15 {
16 static Serialization()
17 {
18 freeOperations = new HashSet<SerializationOperation>();
19 busyOperations = new HashSet<SerializationOperation>();
20 }
21
22 public const string ConstructorWarning = "This parameterless constructor is only made public for serialization. Use another constructor instead.";
23
24 private static readonly HashSet<SerializationOperation> freeOperations;
25 private static readonly HashSet<SerializationOperation> busyOperations;
26
27 private static readonly object @lock = new object();
28
29 public static bool isUnitySerializing { get; set; }
30
31 public static bool isCustomSerializing => busyOperations.Count > 0;
32
33 public static bool isSerializing => isUnitySerializing || isCustomSerializing;
34
35 private static SerializationOperation StartOperation()
36 {
37 lock (@lock)
38 {
39 if (freeOperations.Count == 0)
40 {
41 freeOperations.Add(new SerializationOperation());
42 }
43
44 var operation = freeOperations.First();
45 freeOperations.Remove(operation);
46 busyOperations.Add(operation);
47 return operation;
48 }
49 }
50
51 private static void EndOperation(SerializationOperation operation)
52 {
53 lock (@lock)
54 {
55 if (!busyOperations.Contains(operation))
56 {
57 throw new InvalidOperationException("Trying to finish an operation that isn't started.");
58 }
59
60 operation.Reset();
61 busyOperations.Remove(operation);
62 freeOperations.Add(operation);
63 }
64 }
65
66 public static T CloneViaSerialization<T>(this T value, bool forceReflected = false)
67 {
68 return (T)Deserialize(Serialize(value, forceReflected), forceReflected);
69 }
70
71 public static void CloneViaSerializationInto<TSource, TDestination>(this TSource value, ref TDestination instance, bool forceReflected = false)
72 where TDestination : TSource
73 {
74 object _instance = instance;
75 DeserializeInto(Serialize(value, forceReflected), ref _instance, forceReflected);
76 }
77
78 public static SerializationData Serialize(this object value, bool forceReflected = false)
79 {
80 var operation = StartOperation();
81 try
82 {
83 var json = SerializeJson(operation.serializer, value, forceReflected);
84 var objectReferences = operation.objectReferences.ToArray();
85 var data = new SerializationData(json, objectReferences);
86
87#if DEBUG_SERIALIZATION
88 Debug.Log(data.ToString($"<color=#88FF00>Serialized: <b>{value?.GetType().Name ?? "null"} [{value?.GetHashCode().ToString() ?? "N/A"}]</b></color>"));
89#endif
90
91 return data;
92 }
93 catch (Exception ex)
94 {
95 throw new SerializationException($"Serialization of '{value?.GetType().ToString() ?? "null"}' failed.",
96 ex);
97 }
98 finally
99 {
100 EndOperation(operation);
101 }
102 }
103
104 public static void DeserializeInto(this SerializationData data, ref object instance, bool forceReflected = false)
105 {
106 try
107 {
108 if (string.IsNullOrEmpty(data.json))
109 {
110 instance = null;
111 return;
112 }
113
114#if DEBUG_SERIALIZATION
115 Debug.Log(data.ToString($"<color=#3388FF>Deserializing into: <b>{instance?.GetType().Name ?? "null"} [{instance?.GetHashCode().ToString() ?? "N/A"}]</b></color>"));
116#endif
117
118 var operation = StartOperation();
119 try
120 {
121 operation.objectReferences.AddRange(data.objectReferences);
122 DeserializeJson(operation.serializer, data.json, ref instance, forceReflected);
123 }
124 finally
125 {
126 EndOperation(operation);
127 }
128 }
129 catch (Exception ex)
130 {
131 try
132 {
133 Debug.LogWarning(data.ToString("Deserialization Failure Data"), instance as UnityObject);
134 }
135 catch (Exception ex2)
136 {
137 Debug.LogWarning("Failed to log deserialization failure data:\n" + ex2, instance as UnityObject);
138 }
139
140 throw new SerializationException($"Deserialization into '{instance?.GetType().ToString() ?? "null"}' failed.", ex);
141 }
142 }
143
144 public static object Deserialize(this SerializationData data, bool forceReflected = false)
145 {
146 object instance = null;
147 DeserializeInto(data, ref instance, forceReflected);
148 return instance;
149 }
150
151 private static string SerializeJson(fsSerializer serializer, object instance, bool forceReflected)
152 {
153 using (ProfilingUtility.SampleBlock("SerializeJson"))
154 {
155 fsData data;
156
157 fsResult result;
158
159 if (forceReflected)
160 {
161 result = serializer.TrySerialize(instance.GetType(), typeof(fsReflectedConverter), instance, out data);
162 }
163 else
164 {
165 result = serializer.TrySerialize(instance, out data);
166 }
167
168 HandleResult("Serialization", result, instance as UnityObject);
169
170 return fsJsonPrinter.CompressedJson(data);
171 }
172 }
173
174 private static fsResult DeserializeJsonUtil(fsSerializer serializer, string json, ref object instance, bool forceReflected)
175 {
176 var fsData = fsJsonParser.Parse(json);
177
178 fsResult result;
179
180 if (forceReflected)
181 {
182 result = serializer.TryDeserialize(fsData, instance.GetType(), typeof(fsReflectedConverter), ref instance);
183 }
184 else
185 {
186 result = serializer.TryDeserialize(fsData, ref instance);
187 }
188
189 return result;
190 }
191
192 private static void DeserializeJson(fsSerializer serializer, string json, ref object instance, bool forceReflected)
193 {
194 using (ProfilingUtility.SampleBlock("DeserializeJson"))
195 {
196 fsResult result = DeserializeJsonUtil(serializer, json, ref instance, forceReflected);
197
198 HandleResult("Deserialization", result, instance as UnityObject);
199 }
200 }
201
202 private static void HandleResult(string label, fsResult result, UnityObject context = null)
203 {
204 result.AssertSuccess();
205
206 if (result.HasWarnings)
207 {
208 foreach (var warning in result.RawMessages)
209 {
210 Debug.LogWarning($"[{label}] {warning}\n", context);
211 }
212 }
213 }
214
215 public static string PrettyPrint(string json)
216 {
217 return fsJsonPrinter.PrettyJson(fsJsonParser.Parse(json));
218 }
219
220 #region Dependencies
221
222 private static readonly HashSet<ISerializationDepender> awaitingDependers = new HashSet<ISerializationDepender>();
223
224 public static void AwaitDependencies(ISerializationDepender depender)
225 {
226 awaitingDependers.Add(depender);
227
228 CheckIfDependenciesMet(depender);
229 }
230
231 public static void NotifyDependencyDeserializing(ISerializationDependency dependency)
232 {
233 NotifyDependencyUnavailable(dependency);
234 }
235
236 public static void NotifyDependencyDeserialized(ISerializationDependency dependency)
237 {
238 NotifyDependencyAvailable(dependency);
239 }
240
241 public static void NotifyDependencyUnavailable(ISerializationDependency dependency)
242 {
243 dependency.IsDeserialized = false;
244 }
245
246 public static void NotifyDependencyAvailable(ISerializationDependency dependency)
247 {
248 dependency.IsDeserialized = true;
249
250 foreach (var awaitingDepender in awaitingDependers.ToArray())
251 {
252 if (!awaitingDependers.Contains(awaitingDepender))
253 {
254 // In case the depender was already handled by a recursive
255 // dependency via OnAfterDependenciesDeserialized,
256 // we skip it. This is necessary because we duplicated
257 // the set to safely iterate over it with removal.
258 //
259 // This should prevent OnAfterDependenciesDeserialized from
260 // running twice on any given depender in a single deserialization
261 // operation.
262 continue;
263 }
264
265 CheckIfDependenciesMet(awaitingDepender);
266 }
267 }
268
269 private static void CheckIfDependenciesMet(ISerializationDepender depender)
270 {
271 var areDependenciesMet = true;
272
273 foreach (var requiredDependency in depender.deserializationDependencies)
274 {
275 if (!requiredDependency.IsDeserialized)
276 {
277 areDependenciesMet = false;
278 break;
279 }
280 }
281
282 if (areDependenciesMet)
283 {
284 awaitingDependers.Remove(depender);
285
286 depender.OnAfterDependenciesDeserialized();
287 }
288 }
289
290 public static void LogStuckDependers()
291 {
292 if (awaitingDependers.Any())
293 {
294 var message = awaitingDependers.Count + " awaiting dependers: \n";
295
296 foreach (var depender in awaitingDependers)
297 {
298 HashSet<object> missingDependencies = new HashSet<object>();
299
300 foreach (var requiredDependency in depender.deserializationDependencies)
301 {
302 if (!requiredDependency.IsDeserialized)
303 {
304 missingDependencies.Add(requiredDependency);
305 break;
306 }
307 }
308
309 message += $"{depender} is missing {missingDependencies.ToCommaSeparatedString()}\n";
310 }
311
312 Debug.LogWarning(message);
313 }
314 else
315 {
316 Debug.Log("No stuck awaiting depender.");
317 }
318 }
319
320 #endregion
321 }
322}