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.

162 lines
6.5KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using DBC.ECS;
  5. using Svelto.Common;
  6. using Svelto.DataStructures;
  7. using Svelto.ECS.Hybrid;
  8. using Svelto.ECS.Internal;
  9. using Svelto.Utilities;
  10. namespace Svelto.ECS
  11. {
  12. struct ComponentBuilderComparer : IEqualityComparer<IComponentBuilder>
  13. {
  14. public bool Equals(IComponentBuilder x, IComponentBuilder y)
  15. {
  16. return x.GetEntityComponentType() == y.GetEntityComponentType();
  17. }
  18. public int GetHashCode(IComponentBuilder obj)
  19. {
  20. return obj.GetEntityComponentType().GetHashCode();
  21. }
  22. }
  23. public class ComponentBuilder<T> : IComponentBuilder
  24. where T : struct, IEntityComponent
  25. {
  26. internal static readonly Type ENTITY_COMPONENT_TYPE;
  27. public static readonly bool HAS_EGID;
  28. internal static readonly bool IS_ENTITY_VIEW_COMPONENT;
  29. static readonly T DEFAULT_IT;
  30. static readonly string ENTITY_COMPONENT_NAME;
  31. static readonly bool IS_UNMANAGED;
  32. public static bool HAS_REFERENCE;
  33. static ComponentBuilder()
  34. {
  35. ENTITY_COMPONENT_TYPE = typeof(T);
  36. DEFAULT_IT = default;
  37. IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  38. HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  39. HAS_REFERENCE = typeof(INeedEntityReference).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  40. ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
  41. IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanagedEx();
  42. if (IS_UNMANAGED)
  43. EntityComponentIDMap.Register<T>(new Filler<T>());
  44. SetEGIDWithoutBoxing<T>.Warmup();
  45. ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);
  46. if (IS_ENTITY_VIEW_COMPONENT)
  47. {
  48. EntityViewComponentCache.InitCache();
  49. }
  50. else
  51. {
  52. if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT
  53. && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false)
  54. throw new Exception(
  55. $"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
  56. }
  57. }
  58. public ComponentBuilder() { _initializer = DEFAULT_IT; }
  59. public ComponentBuilder(in T initializer) : this() { _initializer = initializer; }
  60. public bool isUnmanaged => IS_UNMANAGED;
  61. public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable<object> implementors)
  62. {
  63. var castedDic = dictionary as ITypeSafeDictionary<T>;
  64. T entityComponent = default;
  65. if (IS_ENTITY_VIEW_COMPONENT)
  66. {
  67. Check.Require(castedDic.ContainsKey(egid.entityID) == false
  68. , $"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}");
  69. this.SetEntityViewComponentImplementors(ref entityComponent, EntityViewComponentCache.cachedFields
  70. , implementors, EntityViewComponentCache.implementorsByType
  71. , EntityViewComponentCache.cachedTypes);
  72. castedDic.Add(egid.entityID, entityComponent);
  73. }
  74. else
  75. {
  76. Check.Require(!castedDic.ContainsKey(egid.entityID)
  77. , $"building an entity with already used entity id! id: '{egid.entityID}'");
  78. castedDic.Add(egid.entityID, _initializer);
  79. }
  80. }
  81. void IComponentBuilder.Preallocate(ITypeSafeDictionary dictionary, uint size) { Preallocate(dictionary, size); }
  82. public ITypeSafeDictionary CreateDictionary(uint size) { return TypeSafeDictionaryFactory<T>.Create(size); }
  83. public Type GetEntityComponentType() { return ENTITY_COMPONENT_TYPE; }
  84. public override int GetHashCode() { return _initializer.GetHashCode(); }
  85. static void Preallocate(ITypeSafeDictionary dictionary, uint size) { dictionary.ResizeTo(size); }
  86. readonly T _initializer;
  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. #if DEBUG && !PROFILE_SVELTO
  118. if (fields.Length == 0)
  119. Console.LogWarning($"No fields found in component {type}. Are you declaring only properties?");
  120. #endif
  121. cachedTypes = new Dictionary<Type, Type[]>();
  122. #if DEBUG && !PROFILE_SVELTO
  123. implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
  124. #else
  125. implementorsByType = new Dictionary<Type, object>();
  126. #endif
  127. }
  128. internal static void InitCache() { }
  129. }
  130. }
  131. }