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.

158 lines
6.0KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using Svelto.Common;
  5. using Svelto.DataStructures;
  6. using Svelto.ECS.Hybrid;
  7. using Svelto.ECS.Internal;
  8. using Svelto.Utilities;
  9. namespace Svelto.ECS
  10. {
  11. public class ComponentBuilder<T> : IComponentBuilder where T : struct, IEntityComponent
  12. {
  13. public ComponentBuilder()
  14. {
  15. _initializer = DEFAULT_IT;
  16. }
  17. public ComponentBuilder(in T initializer) : this()
  18. {
  19. _initializer = initializer;
  20. }
  21. public bool isUnmanaged => IS_UNMANAGED;
  22. public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid,
  23. IEnumerable<object> implementors)
  24. {
  25. if (dictionary == null)
  26. dictionary = TypeSafeDictionaryFactory<T>.Create();
  27. var castedDic = dictionary as ITypeSafeDictionary<T>;
  28. T entityComponent = default;
  29. if (IS_ENTITY_VIEW_COMPONENT)
  30. {
  31. DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false,
  32. $"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}");
  33. this.FillEntityComponent(ref entityComponent, EntityViewComponentCache.cachedFields, implementors,
  34. EntityViewComponentCache.implementorsByType, EntityViewComponentCache.cachedTypes);
  35. castedDic.Add(egid.entityID, entityComponent);
  36. }
  37. else
  38. {
  39. DBC.ECS.Check.Require(!castedDic.ContainsKey(egid.entityID),
  40. $"building an entity with already used entity id! id: '{egid.entityID}'");
  41. castedDic.Add(egid.entityID, _initializer);
  42. }
  43. }
  44. ITypeSafeDictionary IComponentBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size)
  45. {
  46. return Preallocate(ref dictionary, size);
  47. }
  48. static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size)
  49. {
  50. if (dictionary == null)
  51. dictionary = TypeSafeDictionaryFactory<T>.Create(size);
  52. else
  53. dictionary.SetCapacity(size);
  54. return dictionary;
  55. }
  56. public Type GetEntityComponentType()
  57. {
  58. return ENTITY_COMPONENT_TYPE;
  59. }
  60. static ComponentBuilder()
  61. {
  62. ENTITY_COMPONENT_TYPE = typeof(T);
  63. DEFAULT_IT = default;
  64. IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  65. HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  66. ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
  67. IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanagedEx();
  68. if (IS_UNMANAGED)
  69. EntityComponentIDMap.Register<T>(new Filler<T>());
  70. SetEGIDWithoutBoxing<T>.Warmup();
  71. ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);
  72. if (IS_ENTITY_VIEW_COMPONENT)
  73. EntityViewComponentCache.InitCache();
  74. else
  75. {
  76. if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false)
  77. throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
  78. }
  79. }
  80. readonly T _initializer;
  81. internal static readonly Type ENTITY_COMPONENT_TYPE;
  82. public static readonly bool HAS_EGID;
  83. internal static readonly bool IS_ENTITY_VIEW_COMPONENT;
  84. static readonly T DEFAULT_IT;
  85. static readonly string ENTITY_COMPONENT_NAME;
  86. static bool IS_UNMANAGED;
  87. /// <summary>
  88. /// Note: this static class will hold forever the references of the entities implementors. These references
  89. /// are not even cleared when the engines root is destroyed, as they are static references.
  90. /// It must be seen as an application-wide cache system. Honestly, I am not sure if this may cause leaking
  91. /// issues in some kind of applications. To remember.
  92. /// </summary>
  93. static class EntityViewComponentCache
  94. {
  95. internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
  96. internal static readonly Dictionary<Type, Type[]> cachedTypes;
  97. #if DEBUG && !PROFILE_SVELTO
  98. internal static readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
  99. #else
  100. internal static readonly Dictionary<Type, object> implementorsByType;
  101. #endif
  102. static EntityViewComponentCache()
  103. {
  104. cachedFields = new FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>();
  105. var type = typeof(T);
  106. var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
  107. for (var i = fields.Length - 1; i >= 0; --i)
  108. {
  109. var field = fields[i];
  110. if (field.FieldType.IsInterface == true)
  111. {
  112. var setter = FastInvoke<T>.MakeSetter(field);
  113. //for each interface, cache the setter for this type
  114. cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter));
  115. }
  116. }
  117. cachedTypes = new Dictionary<Type, Type[]>();
  118. #if DEBUG && !PROFILE_SVELTO
  119. implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
  120. #else
  121. implementorsByType = new Dictionary<Type, object>();
  122. #endif
  123. }
  124. internal static void InitCache()
  125. {}
  126. }
  127. }
  128. }