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.

136 lines
5.5KB

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