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.

211 lines
7.5KB

  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, IEntityComponent
  29. {
  30. public static readonly SharedStaticWrapper<int, ComponentID<T>> id;
  31. #if UNITY_BURST
  32. [Unity.Burst.BurstDiscard]
  33. //SharedStatic values must be initialized from not burstified code
  34. #endif
  35. public static void Init()
  36. {
  37. id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter);
  38. DBC.ECS.Check.Ensure(id.Data < ushort.MaxValue, "too many types registered, HOW :)");
  39. }
  40. }
  41. public class ComponentBuilder<T> : IComponentBuilder where T : struct, IEntityComponent
  42. {
  43. internal static readonly Type ENTITY_COMPONENT_TYPE;
  44. internal static readonly bool IS_ENTITY_VIEW_COMPONENT;
  45. static readonly T DEFAULT_IT;
  46. static readonly string ENTITY_COMPONENT_NAME;
  47. static readonly bool IS_UNMANAGED;
  48. #if SLOW_SVELTO_SUBMISSION
  49. public static readonly bool HAS_EGID;
  50. public static readonly bool HAS_REFERENCE;
  51. #endif
  52. static ComponentBuilder()
  53. {
  54. ENTITY_COMPONENT_TYPE = typeof(T);
  55. DEFAULT_IT = default;
  56. IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  57. #if SLOW_SVELTO_SUBMISSION
  58. HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  59. HAS_REFERENCE = typeof(INeedEntityReference).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  60. SetEGIDWithoutBoxing<T>.Warmup();
  61. #endif
  62. ComponentID<T>.Init();
  63. ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
  64. IS_UNMANAGED = TypeType.isUnmanaged<T>(); //attention this is important as it serves as warm up for Type<T>
  65. #if UNITY_NATIVE
  66. if (IS_UNMANAGED)
  67. EntityComponentIDMap.Register<T>(new Filler<T>());
  68. #endif
  69. ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);
  70. if (IS_ENTITY_VIEW_COMPONENT)
  71. {
  72. EntityViewComponentCache.InitCache();
  73. }
  74. else
  75. {
  76. if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT &&
  77. ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false)
  78. throw new Exception(
  79. $"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
  80. }
  81. }
  82. public ComponentBuilder()
  83. {
  84. _initializer = DEFAULT_IT;
  85. }
  86. public ComponentBuilder(in T initializer) : this()
  87. {
  88. _initializer = initializer;
  89. }
  90. public bool isUnmanaged => IS_UNMANAGED;
  91. public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable<object> implementors)
  92. {
  93. var castedDic = dictionary as ITypeSafeDictionary<T>;
  94. if (IS_ENTITY_VIEW_COMPONENT)
  95. {
  96. T entityComponent = default;
  97. Check.Require(castedDic.ContainsKey(egid.entityID) == false,
  98. $"building an entity with already used entity id! id: '{(ulong)egid}', {ENTITY_COMPONENT_NAME}");
  99. this.SetEntityViewComponentImplementors(ref entityComponent, EntityViewComponentCache.cachedFields,
  100. implementors, EntityViewComponentCache.implementorsByType, EntityViewComponentCache.cachedTypes);
  101. castedDic.Add(egid.entityID, entityComponent);
  102. }
  103. else
  104. {
  105. Check.Require(!castedDic.ContainsKey(egid.entityID),
  106. $"building an entity with already used entity id! id: '{egid.entityID}'");
  107. castedDic.Add(egid.entityID, _initializer);
  108. }
  109. }
  110. void IComponentBuilder.Preallocate(ITypeSafeDictionary dictionary, uint size)
  111. {
  112. Preallocate(dictionary, size);
  113. }
  114. public ITypeSafeDictionary CreateDictionary(uint size)
  115. {
  116. return TypeSafeDictionaryFactory<T>.Create(size);
  117. }
  118. public Type GetEntityComponentType()
  119. {
  120. return ENTITY_COMPONENT_TYPE;
  121. }
  122. public override int GetHashCode()
  123. {
  124. return _initializer.GetHashCode();
  125. }
  126. static void Preallocate(ITypeSafeDictionary dictionary, uint size)
  127. {
  128. dictionary.EnsureCapacity(size);
  129. }
  130. readonly T _initializer;
  131. /// <summary>
  132. /// Note: this static class will hold forever the references of the entities implementors. These references
  133. /// are not even cleared when the engines root is destroyed, as they are static references.
  134. /// It must be seen as an application-wide cache system. Honestly, I am not sure if this may cause leaking
  135. /// issues in some kind of applications. To remember.
  136. /// </summary>
  137. static class EntityViewComponentCache
  138. {
  139. internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
  140. internal static readonly Dictionary<Type, Type[]> cachedTypes;
  141. #if DEBUG && !PROFILE_SVELTO
  142. internal static readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
  143. #else
  144. internal static readonly Dictionary<Type, object> implementorsByType;
  145. #endif
  146. static 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. }