Mirror of Svelto.ECS because we're a fan of it
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

157 行
6.3KB

  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.ECS.Hybrid;
  8. namespace Svelto.ECS
  9. {
  10. public 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("Entity View Components must have at least one interface declared as public field and not property", entityComponentType);
  43. }
  44. for (int i = fields.Length - 1; i >= 0; --i)
  45. {
  46. FieldInfo fieldInfo = fields[i];
  47. if (fieldInfo.FieldType.IsInterfaceEx() == false)
  48. {
  49. ProcessError("Entity View Components must hold only entity components interfaces."
  50. , entityComponentType);
  51. }
  52. PropertyInfo[] properties = fieldInfo.FieldType.GetProperties(
  53. BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  54. for (int j = properties.Length - 1; j >= 0; --j)
  55. {
  56. Type propertyType = properties[j].PropertyType;
  57. if (propertyType.IsGenericType)
  58. {
  59. Type genericTypeDefinition = propertyType.GetGenericTypeDefinition();
  60. if (genericTypeDefinition == RECATIVEVALUETYPE)
  61. {
  62. continue;
  63. }
  64. }
  65. // Special rules for ValueReference<T>
  66. if (IsValueReference(propertyType))
  67. {
  68. // Getters of ValueReference must be refs, which would cause a failure on the common check.
  69. if (properties[j].CanRead == true && propertyType.IsByRef == false)
  70. {
  71. ProcessError(MSG, entityComponentType, propertyType);
  72. }
  73. continue;
  74. }
  75. if (propertyType != STRINGTYPE)
  76. {
  77. //for EntityComponentStructs, component fields that are structs that hold strings
  78. //are allowed
  79. SubCheckFields(propertyType, entityComponentType, isStringAllowed: true);
  80. }
  81. }
  82. }
  83. }
  84. }
  85. static bool IsString(Type type)
  86. {
  87. return type == STRINGTYPE || type == STRINGBUILDERTYPE;
  88. }
  89. static bool IsValueReference(Type type)
  90. {
  91. var interfaces = type.GetInterfaces();
  92. return interfaces.Length == 1 && interfaces[0] == VALUE_REF_TYPE;
  93. }
  94. /// <summary>
  95. /// This method checks the fields if it's an IEntityComponent, but checks all the properties if it's
  96. /// IEntityViewComponent
  97. /// </summary>
  98. /// <param name="fieldType"></param>
  99. /// <param name="entityComponentType"></param>
  100. /// <param name="isStringAllowed"></param>
  101. static void SubCheckFields(Type fieldType, Type entityComponentType, bool isStringAllowed = false)
  102. {
  103. //pass if it's Primitive or C# 8 unmanaged, or it's a string and string are allowed
  104. //this check must allow pointers as they are unmanaged types
  105. if ((isStringAllowed == true && IsString(fieldType) == true) ||
  106. fieldType.IsValueTypeEx() == true)
  107. {
  108. //if it's a struct we have to check the fields recursively
  109. if (IsString(fieldType) == false)
  110. {
  111. CheckFields(fieldType, false, isStringAllowed);
  112. }
  113. return;
  114. }
  115. ProcessError(MSG, entityComponentType, fieldType);
  116. }
  117. static void ProcessError(string message, Type entityComponentType, Type fieldType = null)
  118. {
  119. if (fieldType != null)
  120. {
  121. throw new ECSException(message, entityComponentType, fieldType);
  122. }
  123. throw new ECSException(message, entityComponentType);
  124. }
  125. static readonly Type RECATIVEVALUETYPE = typeof(ReactiveValue<>);
  126. static readonly Type VALUE_REF_TYPE = typeof(IValueReferenceInternal);
  127. static readonly Type EGIDType = typeof(EGID);
  128. static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroupStruct);
  129. static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent);
  130. static readonly Type STRINGTYPE = typeof(string);
  131. static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder);
  132. internal static readonly Type ENTITY_INFO_COMPONENT = typeof(EntityInfoComponent);
  133. }
  134. }