Browse Source

modify entity component field rules

pull/50/head
sebas77 4 years ago
parent
commit
459204413b
5 changed files with 66 additions and 47 deletions
  1. +52
    -43
      Svelto.ECS/ComponentBuilder.CheckFields.cs
  2. +2
    -2
      Svelto.ECS/ComponentBuilder.cs
  3. +1
    -1
      Svelto.ECS/DataStructures/TypeSafeDictionary.cs
  4. +10
    -0
      Svelto.ECS/ECSException.cs
  5. +1
    -1
      Svelto.ECS/GlobalTypeID.cs

+ 52
- 43
Svelto.ECS/ComponentBuilder.CheckFields.cs View File

@@ -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;
}

/// <summary>
/// This method checks the fields if it's an IEntityComponent, but checks all the properties if it's
/// IEntityViewComponent
/// </summary>
/// <param name="fieldType"></param>
/// <param name="entityComponentType"></param>
/// <param name="isStringAllowed"></param>
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()))
{
}
}
}

+ 2
- 2
Svelto.ECS/ComponentBuilder.cs View File

@@ -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<T>(new Filler<T>());
@@ -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}");
}
}


+ 1
- 1
Svelto.ECS/DataStructures/TypeSafeDictionary.cs View File

@@ -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<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>> implMgd;
internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>> implUnmgd;


+ 10
- 0
Svelto.ECS/ECSException.cs View File

@@ -9,5 +9,15 @@ namespace Svelto.ECS
public ECSException(string message, Exception innerE):base("<color=red>".FastConcat(message, "</color>"), 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))
{
}
}
}

+ 1
- 1
Svelto.ECS/GlobalTypeID.cs View File

@@ -23,7 +23,7 @@ namespace Svelto.ECS
{
static Filler()
{
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanaged<T>() == true, "invalid type used");
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanagedEx<T>() == true, "invalid type used");
}

//it's an internal interface


Loading…
Cancel
Save