diff --git a/Svelto.ECS/ComponentBuilder.CheckFields.cs b/Svelto.ECS/ComponentBuilder.CheckFields.cs index 6ff8372..e09f38d 100644 --- a/Svelto.ECS/ComponentBuilder.CheckFields.cs +++ b/Svelto.ECS/ComponentBuilder.CheckFields.cs @@ -4,23 +4,22 @@ using System.Diagnostics; #endif using System; using System.Reflection; +using System.Text; +using Svelto.Common; namespace Svelto.ECS { internal static class ComponentBuilderUtilities { - const string MSG = "Entity Structs field and Entity View Struct components must hold value types."; - + const string MSG = "Entity Components and Entity View Components fields cannot hold managed fields outside the Svelto rules."; #if DISABLE_CHECKS [Conditional("_CHECKS_DISABLED")] #endif public static void CheckFields(Type entityComponentType, bool needsReflection, bool isStringAllowed = false) { - if (entityComponentType == ENTITY_STRUCT_INFO_VIEW || - entityComponentType == EGIDType || - entityComponentType == EXCLUSIVEGROUPSTRUCTTYPE || - entityComponentType == SERIALIZABLE_ENTITY_STRUCT) + if (entityComponentType == ENTITY_STRUCT_INFO_VIEW || entityComponentType == EGIDType || + entityComponentType == EXCLUSIVEGROUPSTRUCTTYPE || entityComponentType == SERIALIZABLE_ENTITY_STRUCT) { return; } @@ -29,7 +28,7 @@ namespace Svelto.ECS { if (entityComponentType.IsClass) { - throw new EntityComponentException("EntityComponents must be structs.", entityComponentType); + throw new ECSException("EntityComponents must be structs.", entityComponentType); } FieldInfo[] fields = entityComponentType.GetFields(BindingFlags.Public | BindingFlags.Instance); @@ -48,51 +47,73 @@ namespace Svelto.ECS if (fields.Length < 1) { - ProcessError("No valid fields found in Entity View Struct", entityComponentType); + ProcessError("No valid fields found in Entity View Components", entityComponentType); } for (int i = fields.Length - 1; i >= 0; --i) { FieldInfo fieldInfo = fields[i]; - if (fieldInfo.FieldType.IsInterfaceEx() == false && fieldInfo.FieldType.IsValueTypeEx() == false) + if (fieldInfo.FieldType.IsInterfaceEx() == true) { - ProcessError("Entity View Structs must hold only public interfaces or value type fields.", - entityComponentType); - } - - PropertyInfo[] properties = fieldInfo.FieldType.GetProperties( - BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + PropertyInfo[] properties = fieldInfo.FieldType.GetProperties( + BindingFlags.Public | BindingFlags.Instance + | BindingFlags.DeclaredOnly); - for (int j = properties.Length - 1; j >= 0; --j) - { - if (properties[j].PropertyType.IsGenericType) + for (int j = properties.Length - 1; j >= 0; --j) { - Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition(); - if (genericTypeDefinition == DISPATCHONSETTYPE || - genericTypeDefinition == DISPATCHONCHANGETYPE) + if (properties[j].PropertyType.IsGenericType) { - continue; + Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition(); + if (genericTypeDefinition == DISPATCHONSETTYPE + || genericTypeDefinition == DISPATCHONCHANGETYPE) + { + continue; + } } - } - Type propertyType = properties[j].PropertyType; - if (propertyType != STRINGTYPE) - { + Type propertyType = properties[j].PropertyType; + //for EntityComponentStructs, component fields that are structs that hold strings //are allowed SubCheckFields(propertyType, entityComponentType, isStringAllowed: true); } } + else + if (fieldInfo.FieldType.IsUnmanagedEx() == true) + { + SubCheckFields(fieldInfo.FieldType, entityComponentType, isStringAllowed); + } + else + { + ProcessError("Entity View Components must hold only public interfaces, strings or unmanaged type fields.", + entityComponentType); + + } } } } + static bool IsString(Type type) + { + return type == STRINGTYPE || type == STRINGBUILDERTYPE; + } + + /// + /// This method checks the fields if it's an IEntityComponent, but checks all the properties if it's + /// IEntityViewComponent + /// + /// + /// + /// static void SubCheckFields(Type fieldType, Type entityComponentType, bool isStringAllowed = false) { - if (fieldType.IsPrimitive || fieldType.IsValueType || (isStringAllowed == true && fieldType == STRINGTYPE)) + //pass if it's Primitive or C# 8 unmanaged, or it's a string and string are allowed + //this check must allow pointers are they are unmanaged types + if ((isStringAllowed == true && IsString(fieldType) == true) || fieldType.IsUnmanagedEx() == true) { - if (fieldType.IsValueType && !fieldType.IsEnum && fieldType.IsPrimitive == false) + //if it's a struct we have to check the fields recursively + if (IsString(fieldType) == false && !fieldType.IsEnum && fieldType.IsPrimitive == false) { CheckFields(fieldType, false, isStringAllowed); } @@ -107,10 +128,10 @@ namespace Svelto.ECS { if (fieldType != null) { - throw new EntityComponentException(message, entityComponentType, fieldType); + throw new ECSException(message, entityComponentType, fieldType); } - throw new EntityComponentException(message, entityComponentType); + throw new ECSException(message, entityComponentType); } static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>); @@ -119,20 +140,8 @@ namespace Svelto.ECS static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroupStruct); static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent); static readonly Type STRINGTYPE = typeof(string); + static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder); internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoViewComponent); } - - public class EntityComponentException : Exception - { - public EntityComponentException(string message, Type entityComponentType, Type type) : - base(message.FastConcat(" entity view: '", entityComponentType.ToString(), "', field: '", type.ToString())) - { - } - - public EntityComponentException(string message, Type entityComponentType) : - base(message.FastConcat(" entity view: ", entityComponentType.ToString())) - { - } - } } \ No newline at end of file diff --git a/Svelto.ECS/ComponentBuilder.cs b/Svelto.ECS/ComponentBuilder.cs index f36c1c8..7089576 100644 --- a/Svelto.ECS/ComponentBuilder.cs +++ b/Svelto.ECS/ComponentBuilder.cs @@ -76,7 +76,7 @@ namespace Svelto.ECS IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE); HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE); ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString(); - var IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanaged(); + var IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanagedEx(); if (IS_UNMANAGED) EntityComponentIDMap.Register(new Filler()); @@ -89,7 +89,7 @@ namespace Svelto.ECS EntityViewComponentCache.InitCache(); else { - if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW && ENTITY_COMPONENT_TYPE.IsUnmanaged() == false) + if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false) throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}"); } } diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs index 2bc30e0..73a9229 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs @@ -13,7 +13,7 @@ namespace Svelto.ECS.Internal static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); internal static readonly bool _isUmanaged = - _type.IsUnmanaged() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false); + _type.IsUnmanagedEx() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false); internal SveltoDictionary>, ManagedStrategy> implMgd; internal SveltoDictionary>, NativeStrategy> implUnmgd; diff --git a/Svelto.ECS/ECSException.cs b/Svelto.ECS/ECSException.cs index b815f84..34951fe 100644 --- a/Svelto.ECS/ECSException.cs +++ b/Svelto.ECS/ECSException.cs @@ -9,5 +9,15 @@ namespace Svelto.ECS public ECSException(string message, Exception innerE):base("".FastConcat(message, ""), innerE) {} + + public ECSException(string message, Type entityComponentType, Type type) : + base(message.FastConcat(" entity view: '", entityComponentType.Name, "', field: '", type.Name)) + { + } + + public ECSException(string message, Type entityComponentType) : + base(message.FastConcat(" entity view: ", entityComponentType.Name)) + { + } } } \ No newline at end of file diff --git a/Svelto.ECS/GlobalTypeID.cs b/Svelto.ECS/GlobalTypeID.cs index 70d1fb7..b19f813 100644 --- a/Svelto.ECS/GlobalTypeID.cs +++ b/Svelto.ECS/GlobalTypeID.cs @@ -23,7 +23,7 @@ namespace Svelto.ECS { static Filler() { - DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanaged() == true, "invalid type used"); + DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanagedEx() == true, "invalid type used"); } //it's an internal interface