A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using JetBrains.Annotations; 5using UnityObject = UnityEngine.Object; 6 7namespace Unity.VisualScripting 8{ 9 public static class Cloning 10 { 11 static Cloning() 12 { 13 cloners.Add(arrayCloner); 14 cloners.Add(dictionaryCloner); 15 cloners.Add(enumerableCloner); 16 cloners.Add(listCloner); 17 cloners.Add(animationCurveCloner); 18 cloners.Add(gradientCloner); 19 } 20 21 // Cloning has to be really fast, and skippable takes a while. 22 private static readonly Dictionary<Type, bool> skippable = new Dictionary<Type, bool>(); 23 24 public static HashSet<ICloner> cloners { get; } = new HashSet<ICloner>(); 25 26 public static ArrayCloner arrayCloner { get; } = new ArrayCloner(); 27 public static DictionaryCloner dictionaryCloner { get; } = new DictionaryCloner(); 28 public static EnumerableCloner enumerableCloner { get; } = new EnumerableCloner(); 29 public static ListCloner listCloner { get; } = new ListCloner(); 30 public static AnimationCurveCloner animationCurveCloner { get; } = new AnimationCurveCloner(); 31 internal static GradientCloner gradientCloner { get; } = new GradientCloner(); 32 33 34 public static FieldsCloner fieldsCloner { get; } = new FieldsCloner(); 35 public static FakeSerializationCloner fakeSerializationCloner { get; } = new FakeSerializationCloner(); 36 37 public static object Clone(this object original, ICloner fallbackCloner, bool tryPreserveInstances) 38 { 39 using (var context = CloningContext.New(fallbackCloner, tryPreserveInstances)) 40 { 41 return Clone(context, original); 42 } 43 } 44 45 public static T Clone<T>(this T original, ICloner fallbackCloner, bool tryPreserveInstances) 46 { 47 return (T)Clone((object)original, fallbackCloner, tryPreserveInstances); 48 } 49 50 public static object CloneViaFakeSerialization(this object original) 51 { 52 return original.Clone(fakeSerializationCloner, true); 53 } 54 55 public static T CloneViaFakeSerialization<T>(this T original) 56 { 57 return (T)CloneViaFakeSerialization((object)original); 58 } 59 60 internal static object Clone(CloningContext context, object original) 61 { 62 object clone = null; 63 CloneInto(context, ref clone, original); 64 return clone; 65 } 66 67 internal static void CloneInto(CloningContext context, ref object clone, object original) 68 { 69 if (original == null) 70 { 71 clone = null; 72 return; 73 } 74 75 var type = original.GetType(); 76 77 if (Skippable(type)) 78 { 79 clone = original; 80 return; 81 } 82 83 if (context.clonings.ContainsKey(original)) 84 { 85 clone = context.clonings[original]; 86 return; 87 } 88 89 var cloner = GetCloner(original, type, context.fallbackCloner); 90 91 if (clone == null) 92 { 93 clone = cloner.ConstructClone(type, original); 94 } 95 96 context.clonings.Add(original, clone); 97 cloner.BeforeClone(type, original); 98 cloner.FillClone(type, ref clone, original, context); 99 cloner.AfterClone(type, clone); 100 context.clonings[original] = clone; // In case the reference changed, for example in arrays 101 } 102 103 [CanBeNull] 104 public static ICloner GetCloner(object original, Type type) 105 { 106 if (original is ISpecifiesCloner cloneableVia) 107 { 108 return cloneableVia.cloner; 109 } 110 111 return cloners.FirstOrDefault(cloner => cloner.Handles(type)); 112 } 113 114 private static ICloner GetCloner(object original, Type type, ICloner fallbackCloner) 115 { 116 var cloner = GetCloner(original, type); 117 118 if (cloner != null) 119 return cloner; 120 121 Ensure.That(nameof(fallbackCloner)).IsNotNull(fallbackCloner); 122 123 return fallbackCloner; 124 } 125 126 private static bool Skippable(Type type) 127 { 128 bool result; 129 130 if (!skippable.TryGetValue(type, out result)) 131 { 132 result = type.IsValueType || // Value types are copied on assignment, so no cloning is necessary 133 type == typeof(string) || // Strings have copy on write semantics as well, but aren't value types 134 typeof(Type).IsAssignableFrom(type) || // Types are guaranteed to be singletons. Using inheritance because MonoType/RuntimeType extend Type 135 typeof(UnityObject).IsAssignableFrom(type); // Unity objects act as pure references 136 137 skippable.Add(type, result); 138 } 139 140 return result; 141 } 142 } 143}