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}