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.

155 lines
5.9KB

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