Mirror of Svelto.ECS because we're a fan of it
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

166 行
6.7KB

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