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}