using System; using System.Collections.Generic; using System.Dynamic; using UnityEngine; using Object = System.Object; namespace FastScriptReload.Scripts.Runtime { public static class TemporaryNewFieldValues { public delegate object GetNewFieldInitialValue(Type forNewlyGeneratedType); public delegate Type GetNewFieldType(Type forNewlyGeneratedType); private static readonly Dictionary _existingObjectToFiledNameValueMap = new Dictionary(); private static readonly Dictionary> _existingObjectTypeToFieldNameToCreateDetaultValueFn = new Dictionary>(); private static readonly Dictionary> _existingObjectTypeToFieldNameToType = new Dictionary>(); //Unity by default will auto init some classes, like gradient, but those are not value types so need to be initialized manually private static Dictionary> ReferenceTypeToCreateDefaultValueFn = new Dictionary>() { [typeof(Gradient)] = () => new Gradient(), [typeof(AnimationCurve)] = () => new AnimationCurve(), }; public static void RegisterNewFields(Type existingType, Dictionary fieldNameToGenerateDefaultValueFn, Dictionary fieldNameToGetTypeFn) { _existingObjectTypeToFieldNameToCreateDetaultValueFn[existingType] = fieldNameToGenerateDefaultValueFn; _existingObjectTypeToFieldNameToType[existingType] = fieldNameToGetTypeFn; } public static dynamic ResolvePatchedObject(object original) { if (!_existingObjectToFiledNameValueMap.TryGetValue(original, out var existingExpandoToObjectTypePair)) { var patchedObject = new ExpandoObject(); var expandoForType = new ExpandoForType { ForType = typeof(TCreatedType), Object = patchedObject }; InitializeAdditionalFieldValues(original, patchedObject); _existingObjectToFiledNameValueMap[original] = expandoForType; return patchedObject; } else { if (existingExpandoToObjectTypePair.ForType != typeof(TCreatedType)) { InitializeAdditionalFieldValues(original, existingExpandoToObjectTypePair.Object); existingExpandoToObjectTypePair.ForType = typeof(TCreatedType); } return existingExpandoToObjectTypePair.Object; } } public static bool TryGetDynamicallyAddedFieldValues(object forObject, out IDictionary addedFieldValues) { if (_existingObjectToFiledNameValueMap.TryGetValue(forObject, out var expandoForType)) { addedFieldValues = expandoForType.Object; return true; } addedFieldValues = null; return false; } private static void InitializeAdditionalFieldValues(object original, ExpandoObject patchedObject) { var originalType = original.GetType(); //TODO: PERF: resolve via TOriginal, not getType var patchedObjectAsDict = patchedObject as IDictionary; foreach (var fieldNameToGenerateDefaultValueFn in _existingObjectTypeToFieldNameToCreateDetaultValueFn[originalType]) { if (!patchedObjectAsDict.ContainsKey(fieldNameToGenerateDefaultValueFn.Key)) { patchedObjectAsDict[fieldNameToGenerateDefaultValueFn.Key] = fieldNameToGenerateDefaultValueFn.Value(typeof(TCreatedType)); if (patchedObjectAsDict[fieldNameToGenerateDefaultValueFn.Key] == null) { var fieldType = _existingObjectTypeToFieldNameToType[originalType][fieldNameToGenerateDefaultValueFn.Key](typeof(TCreatedType)); if (ReferenceTypeToCreateDefaultValueFn.TryGetValue(fieldType, out var createValueFn)) { patchedObjectAsDict[fieldNameToGenerateDefaultValueFn.Key] = createValueFn(); } } } } } } public class ExpandoForType { public Type ForType; public ExpandoObject Object; } }