Mirror of Svelto.ECS because we're a fan of it
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

214 lignes
7.3KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Threading;
  5. using DBC.ECS;
  6. using Svelto.Common;
  7. using Svelto.DataStructures;
  8. using Svelto.ECS.Hybrid;
  9. using Svelto.ECS.Internal;
  10. using Svelto.Utilities;
  11. namespace Svelto.ECS
  12. {
  13. struct ComponentBuilderComparer : IEqualityComparer<IComponentBuilder>
  14. {
  15. public bool Equals(IComponentBuilder x, IComponentBuilder y)
  16. {
  17. return x.GetEntityComponentType() == y.GetEntityComponentType();
  18. }
  19. public int GetHashCode(IComponentBuilder obj)
  20. {
  21. return obj.GetEntityComponentType().GetHashCode();
  22. }
  23. }
  24. public static class BurstCompatibleCounter
  25. {
  26. public static int counter;
  27. }
  28. public class ComponentID<T> where T : struct, _IInternalEntityComponent
  29. {
  30. public static readonly SharedStaticWrapper<int, ComponentID<T>> id;
  31. //todo: any reason to not do this? If I don't, I cannot Create filters in ready functions and
  32. //I have to remove the CreateFilter method
  33. static ComponentID()
  34. {
  35. Init();
  36. }
  37. #if UNITY_BURST
  38. [Unity.Burst.BurstDiscard]
  39. //SharedStatic values must be initialized from not burstified code
  40. #endif
  41. static void Init()
  42. {
  43. id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter);
  44. Check.Ensure(id.Data < ushort.MaxValue, "too many types registered, HOW :)");
  45. }
  46. }
  47. public class ComponentBuilder<T> : IComponentBuilder where T : struct, _IInternalEntityComponent
  48. {
  49. internal static readonly Type ENTITY_COMPONENT_TYPE;
  50. internal static readonly bool IS_ENTITY_VIEW_COMPONENT;
  51. static readonly T DEFAULT_IT;
  52. static readonly string ENTITY_COMPONENT_NAME;
  53. static readonly bool IS_UNMANAGED;
  54. #if SLOW_SVELTO_SUBMISSION
  55. public static readonly bool HAS_EGID;
  56. public static readonly bool HAS_REFERENCE;
  57. #endif
  58. static ComponentBuilder()
  59. {
  60. ENTITY_COMPONENT_TYPE = typeof(T);
  61. DEFAULT_IT = default;
  62. IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  63. #if SLOW_SVELTO_SUBMISSION
  64. HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  65. HAS_REFERENCE = typeof(INeedEntityReference).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  66. SetEGIDWithoutBoxing<T>.Warmup();
  67. #endif
  68. ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
  69. IS_UNMANAGED = TypeCache<T>.isUnmanaged; //attention this is important as it serves as warm up for Type<T>
  70. #if UNITY_NATIVE
  71. if (IS_UNMANAGED)
  72. EntityComponentIDMap.Register<T>(new Filler<T>());
  73. #endif
  74. ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);
  75. if (IS_ENTITY_VIEW_COMPONENT)
  76. {
  77. EntityViewComponentCache.InitCache();
  78. }
  79. else
  80. {
  81. if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT &&
  82. TypeCache<T>.isUnmanaged == false)
  83. throw new Exception(
  84. $"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
  85. }
  86. }
  87. public ComponentBuilder()
  88. {
  89. _initializer = DEFAULT_IT;
  90. }
  91. public ComponentBuilder(in T initializer) : this()
  92. {
  93. _initializer = initializer;
  94. }
  95. public bool isUnmanaged => IS_UNMANAGED;
  96. static readonly ThreadLocal<EntityViewComponentCache> _localCache = new ThreadLocal<EntityViewComponentCache>(() => new EntityViewComponentCache());
  97. public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable<object> implementors)
  98. {
  99. var castedDic = dictionary as ITypeSafeDictionary<T>;
  100. if (IS_ENTITY_VIEW_COMPONENT)
  101. {
  102. T entityComponent = default;
  103. Check.Require(castedDic.ContainsKey(egid.entityID) == false,
  104. $"building an entity with already used entity id! id: '{(ulong)egid}', {ENTITY_COMPONENT_NAME}");
  105. this.SetEntityViewComponentImplementors(ref entityComponent, implementors, _localCache.Value);
  106. castedDic.Add(egid.entityID, entityComponent);
  107. }
  108. else
  109. {
  110. Check.Require(!castedDic.ContainsKey(egid.entityID),
  111. $"building an entity with already used entity id! id: '{egid.entityID}'");
  112. castedDic.Add(egid.entityID, _initializer);
  113. }
  114. }
  115. void IComponentBuilder.Preallocate(ITypeSafeDictionary dictionary, uint size)
  116. {
  117. Preallocate(dictionary, size);
  118. }
  119. public ITypeSafeDictionary CreateDictionary(uint size)
  120. {
  121. return TypeSafeDictionaryFactory<T>.Create(size);
  122. }
  123. public Type GetEntityComponentType()
  124. {
  125. return ENTITY_COMPONENT_TYPE;
  126. }
  127. public override int GetHashCode()
  128. {
  129. return _initializer.GetHashCode();
  130. }
  131. static void Preallocate(ITypeSafeDictionary dictionary, uint size)
  132. {
  133. dictionary.EnsureCapacity(size);
  134. }
  135. readonly T _initializer;
  136. internal class EntityViewComponentCache
  137. {
  138. internal readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
  139. internal readonly Dictionary<Type, Type[]> cachedTypes;
  140. //this is just a local static cache that is cleared after every use
  141. #if DEBUG && !PROFILE_SVELTO
  142. internal readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
  143. #else
  144. internal readonly Dictionary<Type, object> implementorsByType;
  145. #endif
  146. internal EntityViewComponentCache()
  147. {
  148. cachedFields = new FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>();
  149. var type = typeof(T);
  150. var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
  151. for (var i = fields.Length - 1; i >= 0; --i)
  152. {
  153. var field = fields[i];
  154. if (field.FieldType.IsInterface == true)
  155. {
  156. var setter = FastInvoke<T>.MakeSetter(field);
  157. //for each interface, cache the setter for this type
  158. cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter));
  159. }
  160. }
  161. #if DEBUG && !PROFILE_SVELTO
  162. if (fields.Length == 0)
  163. Console.LogWarning($"No fields found in component {type}. Are you declaring only properties?");
  164. #endif
  165. cachedTypes = new Dictionary<Type, Type[]>();
  166. #if DEBUG && !PROFILE_SVELTO
  167. implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
  168. #else
  169. implementorsByType = new Dictionary<Type, object>();
  170. #endif
  171. }
  172. internal static void InitCache()
  173. {
  174. }
  175. }
  176. }
  177. }