A game about forced loneliness, made by TACStudios
1using System; 2using UnityEngine; 3 4namespace Unity.VisualScripting 5{ 6 /* Implementation note: 7 * Using an abstract base class works as a type unification workaround. 8 * https://stackoverflow.com/questions/22721763 9 * https://stackoverflow.com/a/7664919 10 * 11 * However, this forces us to use concrete classes for connections 12 * instead of interfaces. In other words, no IControlConnection / IValueConnection. 13 * If we did use interfaces, there would be ambiguity that needs to be resolved 14 * at every reference to the source or destination. 15 * 16 * However, using a disambiguator hack seems to confuse even recent Mono runtime versions of Unity 17 * and breaks its vtable. Sometimes, method pointers are just plain wrong. 18 * I'm guessing this is specifically due to InvalidConnection, which actually 19 * does unify the types; what the C# warning warned about. 20 * https://stackoverflow.com/q/50051657/154502 21 * 22 * THEREFORE, IUnitConnection has to be implemented at the concrete class level, 23 * because at that point the type unification warning is moot, because the type arguments are 24 * provided. 25 */ 26 27 public abstract class UnitConnection<TSourcePort, TDestinationPort> : GraphElement<FlowGraph>, IConnection<TSourcePort, TDestinationPort> 28 where TSourcePort : class, IUnitOutputPort 29 where TDestinationPort : class, IUnitInputPort 30 { 31 [Obsolete(Serialization.ConstructorWarning)] 32 protected UnitConnection() { } 33 34 protected UnitConnection(TSourcePort source, TDestinationPort destination) 35 { 36 Ensure.That(nameof(source)).IsNotNull(source); 37 Ensure.That(nameof(destination)).IsNotNull(destination); 38 39 if (source.unit.graph != destination.unit.graph) 40 { 41 throw new NotSupportedException("Cannot create connections across graphs."); 42 } 43 44 if (source.unit == destination.unit) 45 { 46 throw new InvalidConnectionException("Cannot create connections on the same unit."); 47 } 48 49 sourceUnit = source.unit; 50 sourceKey = source.key; 51 destinationUnit = destination.unit; 52 destinationKey = destination.key; 53 } 54 55 public virtual IGraphElementDebugData CreateDebugData() 56 { 57 return new UnitConnectionDebugData(); 58 } 59 60 #region Ports 61 62 [Serialize] 63 protected IUnit sourceUnit { get; private set; } 64 65 [Serialize] 66 protected string sourceKey { get; private set; } 67 68 [Serialize] 69 protected IUnit destinationUnit { get; private set; } 70 71 [Serialize] 72 protected string destinationKey { get; private set; } 73 74 [DoNotSerialize] 75 public abstract TSourcePort source { get; } 76 77 [DoNotSerialize] 78 public abstract TDestinationPort destination { get; } 79 80 #endregion 81 82 #region Dependencies 83 84 public override int dependencyOrder => 1; 85 86 public abstract bool sourceExists { get; } 87 88 public abstract bool destinationExists { get; } 89 90 protected void CopyFrom(UnitConnection<TSourcePort, TDestinationPort> source) 91 { 92 base.CopyFrom(source); 93 } 94 95 public override bool HandleDependencies() 96 { 97 // Replace the connection with an invalid connection if the ports are either missing or incompatible. 98 // If the ports are missing, create invalid ports if required. 99 100 var valid = true; 101 IUnitOutputPort source; 102 IUnitInputPort destination; 103 104 if (!sourceExists) 105 { 106 if (!sourceUnit.invalidOutputs.Contains(sourceKey)) 107 { 108 sourceUnit.invalidOutputs.Add(new InvalidOutput(sourceKey)); 109 } 110 111 source = sourceUnit.invalidOutputs[sourceKey]; 112 valid = false; 113 } 114 else 115 { 116 source = this.source; 117 } 118 119 if (!destinationExists) 120 { 121 if (!destinationUnit.invalidInputs.Contains(destinationKey)) 122 { 123 destinationUnit.invalidInputs.Add(new InvalidInput(destinationKey)); 124 } 125 126 destination = destinationUnit.invalidInputs[destinationKey]; 127 valid = false; 128 } 129 else 130 { 131 destination = this.destination; 132 } 133 134 if (!source.CanValidlyConnectTo(destination)) 135 { 136 valid = false; 137 } 138 139 if (!valid && source.CanInvalidlyConnectTo(destination)) 140 { 141 source.InvalidlyConnectTo(destination); 142 143 // Silence this warning if a unit with a missing type is involved (as it will not have any defined ports). 144 // This is to avoid drowning users in warning and error messages if a unit's script goes missing. 145 if (source.unit.GetType() != typeof(MissingType) && destination.unit.GetType() != typeof(MissingType)) 146 { 147 Debug.LogWarning($"Could not load connection between '{source.key}' of '{sourceUnit}' and '{destination.key}' of '{destinationUnit}'."); 148 } 149 } 150 151 return valid; 152 } 153 154 #endregion 155 156 #region Analytics 157 158 public override AnalyticsIdentifier GetAnalyticsIdentifier() 159 { 160 return null; 161 } 162 163 #endregion 164 } 165}