A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5namespace Unity.VisualScripting
6{
7 public sealed class UnitPreservation : IPoolable
8 {
9 private struct UnitPortPreservation
10 {
11 public readonly IUnit unit;
12
13 public readonly string key;
14
15 public UnitPortPreservation(IUnitPort port)
16 {
17 unit = port.unit;
18 key = port.key;
19 }
20
21 public UnitPortPreservation(IUnit unit, string key)
22 {
23 this.unit = unit;
24 this.key = key;
25 }
26
27 public IUnitPort GetOrCreateInput(out InvalidInput newInvalidInput)
28 {
29 var key = this.key;
30
31 if (!unit.inputs.Any(p => p.key == key))
32 {
33 newInvalidInput = new InvalidInput(key);
34 unit.invalidInputs.Add(newInvalidInput);
35 }
36 else
37 {
38 newInvalidInput = null;
39 }
40
41 return unit.inputs.Single(p => p.key == key);
42 }
43
44 public IUnitPort GetOrCreateOutput(out InvalidOutput newInvalidOutput)
45 {
46 var key = this.key;
47
48 if (!unit.outputs.Any(p => p.key == key))
49 {
50 newInvalidOutput = new InvalidOutput(key);
51 unit.invalidOutputs.Add(newInvalidOutput);
52 }
53 else
54 {
55 newInvalidOutput = null;
56 }
57
58 return unit.outputs.Single(p => p.key == key);
59 }
60 }
61
62 private readonly Dictionary<string, object> defaultValues = new Dictionary<string, object>();
63
64 private readonly Dictionary<string, List<UnitPortPreservation>> inputConnections = new Dictionary<string, List<UnitPortPreservation>>();
65
66 private readonly Dictionary<string, List<UnitPortPreservation>> outputConnections = new Dictionary<string, List<UnitPortPreservation>>();
67
68 private bool disposed;
69
70 void IPoolable.New()
71 {
72 disposed = false;
73 }
74
75 void IPoolable.Free()
76 {
77 disposed = true;
78
79 foreach (var inputConnection in inputConnections)
80 {
81 ListPool<UnitPortPreservation>.Free(inputConnection.Value);
82 }
83
84 foreach (var outputConnection in outputConnections)
85 {
86 ListPool<UnitPortPreservation>.Free(outputConnection.Value);
87 }
88
89 defaultValues.Clear();
90 inputConnections.Clear();
91 outputConnections.Clear();
92 }
93
94 private UnitPreservation() { }
95
96 public static UnitPreservation Preserve(IUnit unit)
97 {
98 var preservation = GenericPool<UnitPreservation>.New(() => new UnitPreservation());
99
100 foreach (var defaultValue in unit.defaultValues)
101 {
102 preservation.defaultValues.Add(defaultValue.Key, defaultValue.Value);
103 }
104
105 foreach (var input in unit.inputs)
106 {
107 if (input.hasAnyConnection)
108 {
109 preservation.inputConnections.Add(input.key, ListPool<UnitPortPreservation>.New());
110
111 foreach (var connectedPort in input.connectedPorts)
112 {
113 preservation.inputConnections[input.key].Add(new UnitPortPreservation(connectedPort));
114 }
115 }
116 }
117
118 foreach (var output in unit.outputs)
119 {
120 if (output.hasAnyConnection)
121 {
122 preservation.outputConnections.Add(output.key, ListPool<UnitPortPreservation>.New());
123
124 foreach (var connectedPort in output.connectedPorts)
125 {
126 preservation.outputConnections[output.key].Add(new UnitPortPreservation(connectedPort));
127 }
128 }
129 }
130
131 return preservation;
132 }
133
134 public void RestoreTo(IUnit unit)
135 {
136 if (disposed)
137 {
138 throw new ObjectDisposedException(ToString());
139 }
140
141 // Restore inline values if possible
142
143 foreach (var previousDefaultValue in defaultValues)
144 {
145 if (unit.defaultValues.ContainsKey(previousDefaultValue.Key) &&
146 unit.valueInputs.Contains(previousDefaultValue.Key) &&
147 unit.valueInputs[previousDefaultValue.Key].type.IsAssignableFrom(previousDefaultValue.Value))
148 {
149 unit.defaultValues[previousDefaultValue.Key] = previousDefaultValue.Value;
150 }
151 }
152
153 // Restore connections if possible
154
155 foreach (var previousInputConnections in inputConnections)
156 {
157 var previousInputPort = new UnitPortPreservation(unit, previousInputConnections.Key);
158 var previousOutputPorts = previousInputConnections.Value;
159
160 foreach (var previousOutputPort in previousOutputPorts)
161 {
162 RestoreConnection(previousOutputPort, previousInputPort);
163 }
164 }
165
166 foreach (var previousOutputConnections in outputConnections)
167 {
168 var previousOutputPort = new UnitPortPreservation(unit, previousOutputConnections.Key);
169 var previousInputPorts = previousOutputConnections.Value;
170
171 foreach (var previousInputPort in previousInputPorts)
172 {
173 RestoreConnection(previousOutputPort, previousInputPort);
174 }
175 }
176
177 GenericPool<UnitPreservation>.Free(this);
178 }
179
180 private void RestoreConnection(UnitPortPreservation sourcePreservation, UnitPortPreservation destinationPreservation)
181 {
182 InvalidOutput newInvalidSource;
183 InvalidInput newInvalidDestination;
184
185 var source = sourcePreservation.GetOrCreateOutput(out newInvalidSource);
186 var destination = destinationPreservation.GetOrCreateInput(out newInvalidDestination);
187
188 if (source.CanValidlyConnectTo(destination))
189 {
190 source.ValidlyConnectTo(destination);
191 }
192 else if (source.CanInvalidlyConnectTo(destination))
193 {
194 source.InvalidlyConnectTo(destination);
195 }
196 else
197 {
198 // In this case, we created invalid ports to attempt a connection,
199 // but even that failed (due to, for example, a cross-graph restoration).
200 // Therefore, we need to delete the invalid ports we created.
201
202 if (newInvalidSource != null)
203 {
204 sourcePreservation.unit.invalidOutputs.Remove(newInvalidSource);
205 }
206
207 if (newInvalidDestination != null)
208 {
209 destinationPreservation.unit.invalidInputs.Remove(newInvalidDestination);
210 }
211 }
212 }
213 }
214}