/// <summary> /// Initializes the Entity with data loaded from a DataSet file. /// </summary> /// <param name="entityData"> /// The data loaded from a DataSet file. /// </param> /// <param name="initFunctions"> /// Helper functions to aid in initialization. /// </param> public void Initialize(Core.Entity entityData, EntityFactory.EntityInitializeFunctions initFunctions) { this.Address = entityData.Address; this.ReadStaticProperties(entityData.StaticProperties, initFunctions); foreach (var unused in entityData.DynamicProperties) { Debug.LogError($"Attempted to read dynamic property in an entity of type {entityData.ClassName} but dynamic properties are not yet supported. Report this."); } this.OnPropertiesLoaded(); }
private void ReadStaticProperties(Core.PropertyInfo[] properties, EntityFactory.EntityInitializeFunctions initFunctions) { var baseTypes = ReflectionUtils.GetParentTypes(this.GetType(), true); var fields = (from type in baseTypes from field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) let attribute = field.GetCustomAttribute <PropertyInfoAttribute>() where attribute != null && !attribute.IsAutoProperty select new { Field = field, PropertyInfo = attribute }).ToList(); foreach (var field in fields) { var loadedProperty = properties.FirstOrDefault(property => property.Name == field.Field.Name); if (loadedProperty == null) { Debug.LogError("Property " + field.Field.Name + " is null."); continue; } Assert.IsNotNull(loadedProperty); var propertyInfo = field.PropertyInfo; object value = null; switch (propertyInfo.Type) { case Core.PropertyInfoType.Int8: value = ExtractPropertyValue <sbyte, sbyte>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.UInt8: value = ExtractPropertyValue <byte, byte>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Int16: value = ExtractPropertyValue <short, short>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.UInt16: value = ExtractPropertyValue <ushort, ushort>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Int32: value = ExtractPropertyValue <int, int>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.UInt32: value = ExtractPropertyValue <uint, uint>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Int64: value = ExtractPropertyValue <long, long>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.UInt64: value = ExtractPropertyValue <ulong, ulong>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Float: value = ExtractPropertyValue <float, float>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Double: value = ExtractPropertyValue <double, double>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Bool: value = ExtractPropertyValue <bool, bool>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.String: value = ExtractPropertyValue <string, string>( loadedProperty, null, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Path: value = ExtractPropertyValue <string, string>( loadedProperty, FoxUtils.FoxPathToUnityPath, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.EntityPtr: /* Abandon all hope ye who enter * So what's going on here is that EntityPtr properties get Entities of varying types depending on their ptrType. * However, we don't know that type at compile time, so we can't call ExtractPropertyValue. * To do that, we must use reflection to dynamically invoke ExtractPropertyValue with the ptrType as a generic type argument. * To make matters worse, it requires a delegate as a parameter, and we need to dynamically create that delegate too since * its parameters are also unknown at compile time. We use the C# Expression API to create and compile a lambda expression at runtime. * * Last chance to turn back. */ var method = typeof(Entity).GetMethod(nameof(this.ExtractPropertyValue), BindingFlags.NonPublic | BindingFlags.Instance); var generic = method.MakeGenericMethod(typeof(ulong), propertyInfo.PtrType); var addressParameter = Expression.Parameter(typeof(ulong), "address"); var initFunctionsInstance = Expression.Constant(initFunctions.GetEntityFromAddress.Target); var getEntityFunction = initFunctions.GetEntityFromAddress.Method; var callGetEntityFunction = Expression.Call( initFunctionsInstance, getEntityFunction, addressParameter); var cast = Expression.Convert(callGetEntityFunction, propertyInfo.PtrType); var del = Expression.Lambda(cast, addressParameter).Compile(); value = generic.Invoke( this, new object[] { loadedProperty, del, propertyInfo.Container, propertyInfo.ArraySize }); break; case Core.PropertyInfoType.Vector3: value = ExtractPropertyValue <Core.Vector3, UnityEngine.Vector3>( loadedProperty, FoxUtils.FoxToUnity, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Vector4: value = ExtractPropertyValue <Core.Vector4, UnityEngine.Vector4>( loadedProperty, FoxUtils.FoxToUnity, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Quat: value = ExtractPropertyValue <Core.Quaternion, UnityEngine.Quaternion>( loadedProperty, FoxUtils.FoxToUnity, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Matrix3: value = ExtractPropertyValue <Core.Matrix3, Matrix3x3>( loadedProperty, FoxUtils.FoxToUnity, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Matrix4: value = ExtractPropertyValue <Core.Matrix4, UnityEngine.Matrix4x4>( loadedProperty, FoxUtils.FoxToUnity, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.Color: value = ExtractPropertyValue <Core.ColorRGBA, UnityEngine.Color>( loadedProperty, FoxUtils.FoxColorRGBAToUnityColor, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.FilePtr: value = ExtractPropertyValue <string, string>( loadedProperty, FoxUtils.FoxPathToUnityPath, propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.EntityHandle: value = ExtractPropertyValue <ulong, Entity>( loadedProperty, address => initFunctions.GetEntityFromAddress(address), propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.EntityLink: value = ExtractPropertyValue <Core.EntityLink, EntityLink>( loadedProperty, rawValue => initFunctions.MakeEntityLink(rawValue), propertyInfo.Container, propertyInfo.ArraySize); break; case Core.PropertyInfoType.PropertyInfo: Debug.LogError( "Property type PropertyInfo is not supported. I don't think it even exists. Is the DataSet file well-formed?"); break; case Core.PropertyInfoType.WideVector3: Debug.LogError("Property type WideVector3 is not supported. Is the DataSet file well-formed?"); break; } // If we're dealing with an enum, we need to cast to the enum type. if (field.PropertyInfo.Enum != null) { if (field.PropertyInfo.Container == Core.ContainerType.StaticArray && field.PropertyInfo.ArraySize > 1) { var typeOfList = typeof(List <>).MakeGenericType(field.PropertyInfo.Enum); var castedList = Activator.CreateInstance(typeOfList) as IList; foreach (var item in (value as IList)) { castedList.Add(item); } value = castedList; } else if (field.PropertyInfo.Container == Core.ContainerType.DynamicArray || field.PropertyInfo.Container == Core.ContainerType.List) { var typeOfList = typeof(List <>).MakeGenericType(field.PropertyInfo.Enum); var castedList = Activator.CreateInstance(typeOfList) as IList; foreach (var item in (value as IList)) { castedList.Add(item); } value = castedList; } else if (field.PropertyInfo.Container == Core.ContainerType.StringMap) { var typeOfList = typeof(Dictionary <,>).MakeGenericType(typeof(string), field.PropertyInfo.Enum); var oldDictionary = value as IDictionary; var castedDictionary = Activator.CreateInstance(typeOfList) as IDictionary; foreach (var item in oldDictionary.Keys) { castedDictionary.Add(item, oldDictionary[item]); } value = castedDictionary; } } // FilePtr and Path properties are unique in that we don't actually get the value at this stage. if (field.PropertyInfo.Type != Core.PropertyInfoType.FilePtr && field.PropertyInfo.Type != Core.PropertyInfoType.Path) { field.Field.SetValue(this, value); } } }