Mirror of Svelto.ECS because we're a fan of it
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
5.8KB

  1. #if !DEBUG || PROFILE_SVELTO
  2. #define DISABLE_CHECKS
  3. using System.Diagnostics;
  4. #endif
  5. using System;
  6. using System.Reflection;
  7. using Svelto.Common;
  8. namespace Svelto.ECS
  9. {
  10. static class ComponentBuilderUtilities
  11. {
  12. const string MSG = "Entity Components and Entity View Components fields cannot hold managed fields outside the Svelto rules.";
  13. #if DISABLE_CHECKS
  14. [Conditional("_CHECKS_DISABLED")]
  15. #endif
  16. public static void CheckFields(Type entityComponentType, bool needsReflection, bool isStringAllowed = false)
  17. {
  18. if (entityComponentType == ENTITY_INFO_COMPONENT || entityComponentType == EGIDType ||
  19. entityComponentType == EXCLUSIVEGROUPSTRUCTTYPE || entityComponentType == SERIALIZABLE_ENTITY_STRUCT)
  20. {
  21. return;
  22. }
  23. if (needsReflection == false)
  24. {
  25. if (entityComponentType.IsClass)
  26. {
  27. throw new ECSException("EntityComponents must be structs.", entityComponentType);
  28. }
  29. FieldInfo[] fields = entityComponentType.GetFields(BindingFlags.Public | BindingFlags.Instance);
  30. for (var i = fields.Length - 1; i >= 0; --i)
  31. {
  32. FieldInfo fieldInfo = fields[i];
  33. Type fieldType = fieldInfo.FieldType;
  34. SubCheckFields(fieldType, entityComponentType, isStringAllowed);
  35. }
  36. }
  37. else
  38. {
  39. FieldInfo[] fields = entityComponentType.GetFields(BindingFlags.Public | BindingFlags.Instance);
  40. if (fields.Length < 1)
  41. {
  42. ProcessError("No valid fields found in Entity View Components", entityComponentType);
  43. }
  44. for (int i = fields.Length - 1; i >= 0; --i)
  45. {
  46. FieldInfo fieldInfo = fields[i];
  47. if (fieldInfo.FieldType.IsInterfaceEx() == true)
  48. {
  49. PropertyInfo[] properties = fieldInfo.FieldType.GetProperties(
  50. BindingFlags.Public | BindingFlags.Instance
  51. | BindingFlags.DeclaredOnly);
  52. for (int j = properties.Length - 1; j >= 0; --j)
  53. {
  54. if (properties[j].PropertyType.IsGenericType)
  55. {
  56. Type genericTypeDefinition = properties[j].PropertyType.GetGenericTypeDefinition();
  57. if (genericTypeDefinition == DISPATCHONSETTYPE
  58. || genericTypeDefinition == DISPATCHONCHANGETYPE)
  59. {
  60. continue;
  61. }
  62. }
  63. Type propertyType = properties[j].PropertyType;
  64. //for EntityComponentStructs, component fields that are structs that hold strings
  65. //are allowed
  66. SubCheckFields(propertyType, entityComponentType, isStringAllowed: true);
  67. }
  68. }
  69. else
  70. if (fieldInfo.FieldType.IsUnmanagedEx() == false)
  71. {
  72. ProcessError("Entity View Components must hold only public interfaces, strings or unmanaged type fields.",
  73. entityComponentType);
  74. }
  75. }
  76. }
  77. }
  78. static bool IsString(Type type)
  79. {
  80. return type == STRINGTYPE || type == STRINGBUILDERTYPE;
  81. }
  82. /// <summary>
  83. /// This method checks the fields if it's an IEntityComponent, but checks all the properties if it's
  84. /// IEntityViewComponent
  85. /// </summary>
  86. /// <param name="fieldType"></param>
  87. /// <param name="entityComponentType"></param>
  88. /// <param name="isStringAllowed"></param>
  89. static void SubCheckFields(Type fieldType, Type entityComponentType, bool isStringAllowed = false)
  90. {
  91. //pass if it's Primitive or C# 8 unmanaged, or it's a string and string are allowed
  92. //this check must allow pointers are they are unmanaged types
  93. if ((isStringAllowed == true && IsString(fieldType) == true) || fieldType.IsValueTypeEx() == true)
  94. {
  95. //if it's a struct we have to check the fields recursively
  96. if (IsString(fieldType) == false)
  97. {
  98. CheckFields(fieldType, false, isStringAllowed);
  99. }
  100. return;
  101. }
  102. ProcessError(MSG, entityComponentType, fieldType);
  103. }
  104. static void ProcessError(string message, Type entityComponentType, Type fieldType = null)
  105. {
  106. if (fieldType != null)
  107. {
  108. throw new ECSException(message, entityComponentType, fieldType);
  109. }
  110. throw new ECSException(message, entityComponentType);
  111. }
  112. static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>);
  113. static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>);
  114. static readonly Type EGIDType = typeof(EGID);
  115. static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroupStruct);
  116. static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent);
  117. static readonly Type STRINGTYPE = typeof(string);
  118. static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder);
  119. internal static readonly Type ENTITY_INFO_COMPONENT = typeof(EntityInfoComponent);
  120. }
  121. }