A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Reflection; 5using UnityEngine; 6using UnityObject = UnityEngine.Object; 7 8namespace Unity.VisualScripting 9{ 10 [Descriptor(typeof(IUnit))] 11 public class UnitDescriptor<TUnit> : Descriptor<TUnit, UnitDescription>, IUnitDescriptor 12 where TUnit : class, IUnit 13 { 14 public UnitDescriptor(TUnit target) : base(target) 15 { 16 unitType = unit.GetType(); 17 } 18 19 protected Type unitType { get; } 20 21 public TUnit unit => target; 22 23 IUnit IUnitDescriptor.unit => unit; 24 25 private enum State 26 { 27 Defined, 28 29 NotDefined, 30 31 FailedToDefine 32 } 33 34 private State state 35 { 36 get 37 { 38 if (unit.isDefined) 39 { 40 return State.Defined; 41 } 42 else if (unit.failedToDefine) 43 { 44 return State.FailedToDefine; 45 } 46 else 47 { 48 return State.NotDefined; 49 } 50 } 51 } 52 53 54 #region Reflected Description 55 56 static UnitDescriptor() 57 { 58 XmlDocumentation.loadComplete += FreeReflectedDescriptions; 59 } 60 61 public static void FreeReflectedDescriptions() 62 { 63 reflectedDescriptions.Clear(); 64 reflectedInputDescriptions.Clear(); 65 reflectedOutputDescriptions.Clear(); 66 } 67 68 protected UnitDescription reflectedDescription 69 { 70 get 71 { 72 if (!reflectedDescriptions.TryGetValue(unitType, out var reflectedDescription)) 73 { 74 reflectedDescription = FetchReflectedDescription(unitType); 75 reflectedDescriptions.Add(unitType, reflectedDescription); 76 } 77 78 return reflectedDescription; 79 } 80 } 81 82 protected UnitPortDescription ReflectedPortDescription(IUnitPort port) 83 { 84 if (port is IUnitInvalidPort) 85 { 86 return null; 87 } 88 89 if (port is IUnitInputPort) 90 { 91 if (!reflectedInputDescriptions.TryGetValue(unitType, out var _reflectedInputDescriptions)) 92 { 93 _reflectedInputDescriptions = FetchReflectedPortDescriptions<IUnitInputPort>(unitType); 94 reflectedInputDescriptions.Add(unitType, _reflectedInputDescriptions); 95 } 96 97 if (_reflectedInputDescriptions.TryGetValue(port.key, out var portDescription)) 98 { 99 return portDescription; 100 } 101 } 102 else if (port is IUnitOutputPort) 103 { 104 if (!reflectedOutputDescriptions.TryGetValue(unitType, out var _reflectedOutputDescriptions)) 105 { 106 _reflectedOutputDescriptions = FetchReflectedPortDescriptions<IUnitOutputPort>(unitType); 107 reflectedOutputDescriptions.Add(unitType, _reflectedOutputDescriptions); 108 } 109 110 if (_reflectedOutputDescriptions.TryGetValue(port.key, out var portDescription)) 111 { 112 return portDescription; 113 } 114 } 115 116 return null; 117 } 118 119 private static readonly Dictionary<Type, UnitDescription> reflectedDescriptions = new Dictionary<Type, UnitDescription>(); 120 121 private static readonly Dictionary<Type, Dictionary<string, UnitPortDescription>> reflectedInputDescriptions = new Dictionary<Type, Dictionary<string, UnitPortDescription>>(); 122 123 private static readonly Dictionary<Type, Dictionary<string, UnitPortDescription>> reflectedOutputDescriptions = new Dictionary<Type, Dictionary<string, UnitPortDescription>>(); 124 125 private static UnitDescription FetchReflectedDescription(Type unitType) 126 { 127 var oldName = BoltFlowNameUtility.UnitPreviousTitle(unitType); 128 var prefix = string.IsNullOrEmpty(oldName) ? string.Empty : $"(Previously named {oldName}) "; 129 130 return new UnitDescription() 131 { 132 title = BoltFlowNameUtility.UnitTitle(unitType, false, true), 133 shortTitle = BoltFlowNameUtility.UnitTitle(unitType, true, true), 134 surtitle = unitType.GetAttribute<UnitSurtitleAttribute>()?.surtitle, 135 subtitle = unitType.GetAttribute<UnitSubtitleAttribute>()?.subtitle, 136 summary = prefix + unitType.Summary() 137 }; 138 } 139 140 private static Dictionary<string, UnitPortDescription> FetchReflectedPortDescriptions<T>(Type unitType) where T : IUnitPort 141 { 142 var descriptions = new Dictionary<string, UnitPortDescription>(); 143 144 foreach (var portMember in unitType.GetMembers().Where(member => typeof(T).IsAssignableFrom(member.GetAccessorType()))) 145 { 146 var key = portMember.GetAttribute<PortKeyAttribute>()?.key ?? portMember.Name; 147 148 if (descriptions.ContainsKey(key)) 149 { 150 Debug.LogWarning("Duplicate reflected port description for: " + key); 151 152 continue; 153 } 154 155 descriptions.Add(key, FetchReflectedPortDescription(portMember)); 156 } 157 158 return descriptions; 159 } 160 161 private static UnitPortDescription FetchReflectedPortDescription(MemberInfo portMember) 162 { 163 return new UnitPortDescription() 164 { 165 label = portMember.GetAttribute<PortLabelAttribute>()?.label ?? portMember.HumanName(), 166 showLabel = !(portMember.HasAttribute<PortLabelHiddenAttribute>() || (portMember.GetAttribute<PortLabelAttribute>()?.hidden ?? false)), 167 summary = portMember.Summary(), 168 getMetadata = (unitMetadata) => unitMetadata[portMember.Name] 169 }; 170 } 171 172 #endregion 173 174 175 #region Description 176 177 [Assigns] 178 public sealed override string Title() 179 { 180 switch (state) 181 { 182 case State.Defined: return DefinedTitle(); 183 case State.NotDefined: return DefaultTitle(); 184 case State.FailedToDefine: return ErrorTitle(unit.definitionException); 185 default: throw new UnexpectedEnumValueException<State>(state); 186 } 187 } 188 189 [Assigns] 190 public string ShortTitle() 191 { 192 switch (state) 193 { 194 case State.Defined: return DefinedShortTitle(); 195 case State.NotDefined: return DefaultShortTitle(); 196 case State.FailedToDefine: return ErrorShortTitle(unit.definitionException); 197 default: throw new UnexpectedEnumValueException<State>(state); 198 } 199 } 200 201 [Assigns] 202 public string Surtitle() 203 { 204 switch (state) 205 { 206 case State.Defined: return DefinedSurtitle(); 207 case State.NotDefined: return DefaultSurtitle(); 208 case State.FailedToDefine: return ErrorSurtitle(unit.definitionException); 209 default: throw new UnexpectedEnumValueException<State>(state); 210 } 211 } 212 213 [Assigns] 214 public string Subtitle() 215 { 216 switch (state) 217 { 218 case State.Defined: return DefinedSubtitle(); 219 case State.NotDefined: return DefaultSubtitle(); 220 case State.FailedToDefine: return ErrorSubtitle(unit.definitionException); 221 default: throw new UnexpectedEnumValueException<State>(state); 222 } 223 } 224 225 [Assigns] 226 public sealed override string Summary() 227 { 228 switch (state) 229 { 230 case State.Defined: return DefinedSummary(); 231 case State.NotDefined: return DefaultSummary(); 232 case State.FailedToDefine: return ErrorSummary(unit.definitionException); 233 default: throw new UnexpectedEnumValueException<State>(state); 234 } 235 } 236 237 [Assigns] 238 [RequiresUnityAPI] 239 public sealed override EditorTexture Icon() 240 { 241 switch (state) 242 { 243 case State.Defined: return DefinedIcon(); 244 case State.NotDefined: return DefaultIcon(); 245 case State.FailedToDefine: return ErrorIcon(unit.definitionException); 246 default: throw new UnexpectedEnumValueException<State>(state); 247 } 248 } 249 250 [Assigns] 251 [RequiresUnityAPI] 252 public IEnumerable<EditorTexture> Icons() 253 { 254 switch (state) 255 { 256 case State.Defined: return DefinedIcons(); 257 case State.NotDefined: return DefaultIcons(); 258 case State.FailedToDefine: return ErrorIcons(unit.definitionException); 259 default: throw new UnexpectedEnumValueException<State>(state); 260 } 261 } 262 263 protected virtual string DefinedTitle() 264 { 265 return reflectedDescription.title; 266 } 267 268 protected virtual string DefaultTitle() 269 { 270 return reflectedDescription.title; 271 } 272 273 protected virtual string ErrorTitle(Exception exception) 274 { 275 return reflectedDescription.title; 276 } 277 278 protected virtual string DefinedShortTitle() 279 { 280 return reflectedDescription.shortTitle; 281 } 282 283 protected virtual string DefaultShortTitle() 284 { 285 return reflectedDescription.shortTitle; 286 } 287 288 protected virtual string ErrorShortTitle(Exception exception) 289 { 290 return ErrorTitle(exception); 291 } 292 293 protected virtual string DefinedSurtitle() 294 { 295 return reflectedDescription.surtitle; 296 } 297 298 protected virtual string DefaultSurtitle() 299 { 300 return reflectedDescription.surtitle; 301 } 302 303 protected virtual string ErrorSurtitle(Exception exception) 304 { 305 return null; 306 } 307 308 protected virtual string DefinedSubtitle() 309 { 310 return reflectedDescription.subtitle; 311 } 312 313 protected virtual string DefaultSubtitle() 314 { 315 return reflectedDescription.subtitle; 316 } 317 318 protected virtual string ErrorSubtitle(Exception exception) 319 { 320 return null; 321 } 322 323 protected virtual string DefinedSummary() 324 { 325 return reflectedDescription.summary; 326 } 327 328 protected virtual string DefaultSummary() 329 { 330 return reflectedDescription.summary; 331 } 332 333 protected virtual string ErrorSummary(Exception exception) 334 { 335 return $"This node failed to define.\n\n{exception.DisplayName()}: {exception.Message}"; 336 } 337 338 protected virtual EditorTexture DefinedIcon() 339 { 340 return unit.GetType().Icon(); 341 } 342 343 protected virtual EditorTexture DefaultIcon() 344 { 345 return unit.GetType().Icon(); 346 } 347 348 protected virtual EditorTexture ErrorIcon(Exception exception) 349 { 350 return BoltCore.Icons.errorState; 351 } 352 353 protected virtual IEnumerable<EditorTexture> DefinedIcons() 354 { 355 return Enumerable.Empty<EditorTexture>(); 356 } 357 358 protected virtual IEnumerable<EditorTexture> DefaultIcons() 359 { 360 return Enumerable.Empty<EditorTexture>(); 361 } 362 363 protected virtual IEnumerable<EditorTexture> ErrorIcons(Exception exception) 364 { 365 return Enumerable.Empty<EditorTexture>(); 366 } 367 368 public void DescribePort(IUnitPort port, UnitPortDescription description) 369 { 370 description.getMetadata = (unitMetadata) => unitMetadata.StaticObject(port); 371 372 // Only defined nodes can have specific ports 373 if (state == State.Defined) 374 { 375 DefinedPort(port, description); 376 } 377 } 378 379 protected virtual void DefinedPort(IUnitPort port, UnitPortDescription description) 380 { 381 var reflectedPortDescription = ReflectedPortDescription(port); 382 383 if (reflectedPortDescription != null) 384 { 385 description.CopyFrom(reflectedPortDescription); 386 } 387 } 388 389 #endregion 390 } 391}