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}