A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine; 5using UnityObject = UnityEngine.Object; 6 7namespace Unity.VisualScripting 8{ 9 public sealed class GraphReference : GraphPointer 10 { 11 static GraphReference() 12 { 13 ReferenceCollector.onSceneUnloaded += FreeInvalidInterns; 14 } 15 16 #region Lifecycle 17 18 private GraphReference() { } 19 20 public static GraphReference New(IGraphRoot root, bool ensureValid) 21 { 22 if (!ensureValid && !IsValidRoot(root)) 23 { 24 return null; 25 } 26 27 var reference = new GraphReference(); 28 reference.Initialize(root); 29 reference.Hash(); 30 return reference; 31 } 32 33 public static GraphReference New(IGraphRoot root, IEnumerable<IGraphParentElement> parentElements, bool ensureValid) 34 { 35 if (!ensureValid && !IsValidRoot(root)) 36 { 37 return null; 38 } 39 40 var reference = new GraphReference(); 41 reference.Initialize(root, parentElements, ensureValid); 42 reference.Hash(); 43 return reference; 44 } 45 46 public static GraphReference New(UnityObject rootObject, IEnumerable<Guid> parentElementGuids, bool ensureValid) 47 { 48 if (!ensureValid && !IsValidRoot(rootObject)) 49 { 50 return null; 51 } 52 53 var reference = new GraphReference(); 54 reference.Initialize(rootObject, parentElementGuids, ensureValid); 55 reference.Hash(); 56 return reference; 57 } 58 59 private static GraphReference New(GraphPointer model) 60 { 61 var reference = new GraphReference(); 62 reference.CopyFrom(model); 63 return reference; 64 } 65 66 public override void CopyFrom(GraphPointer other) 67 { 68 base.CopyFrom(other); 69 70 if (other is GraphReference reference) 71 { 72 hashCode = reference.hashCode; 73 } 74 else 75 { 76 Hash(); 77 } 78 } 79 80 public GraphReference Clone() 81 { 82 return New(this); 83 } 84 85 #endregion 86 87 88 #region Conversion 89 90 public override GraphReference AsReference() 91 { 92 return this; 93 } 94 95 public GraphStack ToStackPooled() 96 { 97 return GraphStack.New(this); 98 } 99 100 internal void Release() 101 { 102 releaseDebugDataBinding?.Invoke(root); 103 } 104 105 #endregion 106 107 108 #region Instantiation 109 110 public void CreateGraphData() 111 { 112 if (_data != null) 113 { 114 throw new GraphPointerException("Graph data already exists.", this); 115 } 116 117 if (isRoot) 118 { 119 if (machine != null) 120 { 121 // Debug.Log($"Creating root graph data for {this}"); 122 123 _data = machine.graphData = graph.CreateData(); 124 } 125 else 126 { 127 throw new GraphPointerException("Root graph data can only be created on machines.", this); 128 } 129 } 130 else 131 { 132 if (_parentData == null) 133 { 134 throw new GraphPointerException("Child graph data can only be created from parent graph data.", this); 135 } 136 137 _data = _parentData.CreateChildGraphData(parentElement); 138 } 139 } 140 141 public void FreeGraphData() 142 { 143 if (_data == null) 144 { 145 throw new GraphPointerException("Graph data does not exist.", this); 146 } 147 148 if (isRoot) 149 { 150 if (machine != null) 151 { 152 // Debug.Log($"Freeing root graph data for {this}"); 153 154 _data = machine.graphData = null; 155 } 156 else 157 { 158 throw new GraphPointerException("Root graph data can only be freed on machines.", this); 159 } 160 } 161 else 162 { 163 if (_parentData == null) 164 { 165 throw new GraphPointerException("Child graph data can only be freed from parent graph data.", this); 166 } 167 168 _parentData.FreeChildGraphData(parentElement); 169 _data = null; 170 } 171 } 172 173 #endregion 174 175 176 #region Equality 177 178 [DoNotSerialize] 179 private int hashCode; 180 181 public override bool Equals(object obj) 182 { 183 if (!(obj is GraphReference other)) 184 { 185 return false; 186 } 187 188 return InstanceEquals(other); 189 } 190 191 private void Hash() 192 { 193 hashCode = ComputeHashCode(); 194 } 195 196 public override int GetHashCode() 197 { 198 return hashCode; 199 } 200 201 public static bool operator ==(GraphReference x, GraphReference y) 202 { 203 if (ReferenceEquals(x, y)) 204 { 205 return true; 206 } 207 208 if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) 209 { 210 return false; 211 } 212 213 return x.Equals(y); 214 } 215 216 public static bool operator !=(GraphReference x, GraphReference y) 217 { 218 return !(x == y); 219 } 220 221 #endregion 222 223 224 #region Traversal 225 226 public GraphReference ParentReference(bool ensureValid) 227 { 228 if (isRoot) 229 { 230 if (ensureValid) 231 { 232 throw new GraphPointerException("Trying to get parent graph reference of a root.", this); 233 } 234 else 235 { 236 return null; 237 } 238 } 239 240 var pointer = Clone(); 241 pointer.ExitParentElement(); 242 pointer.Hash(); 243 return pointer; 244 } 245 246 public GraphReference ChildReference(IGraphParentElement parentElement, bool ensureValid, int? maxRecursionDepth = null) 247 { 248 var pointer = Clone(); 249 250 if (!pointer.TryEnterParentElement(parentElement, out var error, maxRecursionDepth)) 251 { 252 if (ensureValid) 253 { 254 throw new GraphPointerException(error, this); 255 } 256 else 257 { 258 return null; 259 } 260 } 261 262 pointer.Hash(); 263 return pointer; 264 } 265 266 #endregion 267 268 269 #region Validation 270 271 public GraphReference Revalidate(bool ensureValid) 272 { 273 try 274 { 275 // Important to recreate by GUIDs to avoid serialization ghosting 276 return New(rootObject, parentElementGuids, ensureValid); 277 } 278 catch (Exception ex) 279 { 280 if (ensureValid) 281 { 282 throw; 283 } 284 285 Debug.LogWarning("Failed to revalidate graph pointer: \n" + ex); 286 return null; 287 } 288 } 289 290 #endregion 291 292 293 #region Navigation 294 295 public IEnumerable<GraphReference> GetBreadcrumbs() 296 { 297 for (int depth = 0; depth < this.depth; depth++) 298 { 299 yield return New(root, parentElementStack.Take(depth), true); 300 } 301 } 302 303 #endregion 304 305 306 #region Interning 307 308 private static readonly Dictionary<int, List<GraphReference>> internPool = new Dictionary<int, List<GraphReference>>(); 309 310 public static GraphReference Intern(GraphPointer pointer) 311 { 312 var hash = pointer.ComputeHashCode(); 313 314 if (internPool.TryGetValue(hash, out var interns)) 315 { 316 foreach (var intern in interns) 317 { 318 if (intern.InstanceEquals(pointer)) 319 { 320 return intern; 321 } 322 } 323 324 var reference = New(pointer); 325 interns.Add(reference); 326 return reference; 327 } 328 else 329 { 330 var reference = New(pointer); 331 internPool.Add(reference.hashCode, new List<GraphReference>() { reference }); 332 return reference; 333 } 334 } 335 336 internal static void ClearIntern(GraphPointer pointer) 337 { 338 var hash = pointer.ComputeHashCode(); 339 340 if (!internPool.TryGetValue(hash, out var interns)) return; 341 342 for (var i = interns.Count - 1; i >= 0; i--) 343 { 344 if (interns[i].InstanceEquals(pointer)) 345 { 346 interns.RemoveAt(i); 347 break; 348 } 349 } 350 351 if (interns.Count == 0) 352 { 353 internPool.Remove(hash); 354 } 355 } 356 357 public static void FreeInvalidInterns() 358 { 359 var invalidHashes = ListPool<int>.New(); 360 361 foreach (var internsByHash in internPool) 362 { 363 var hash = internsByHash.Key; 364 var interns = internsByHash.Value; 365 366 var invalidInterns = ListPool<GraphReference>.New(); 367 368 foreach (var intern in interns) 369 { 370 if (!intern.isValid) 371 { 372 invalidInterns.Add(intern); 373 } 374 } 375 376 foreach (var intern in invalidInterns) 377 { 378 interns.Remove(intern); 379 } 380 381 if (interns.Count == 0) 382 { 383 invalidHashes.Add(hash); 384 } 385 386 invalidInterns.Free(); 387 } 388 389 foreach (var hash in invalidHashes) 390 { 391 internPool.Remove(hash); 392 } 393 394 invalidHashes.Free(); 395 } 396 397 #endregion 398 } 399}