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.

221 lines
9.6KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS.Internal;
  6. namespace Svelto.ECS
  7. {
  8. public class EntityDescriptor
  9. {
  10. public void AddImplementors(params object[] componentsImplementor)
  11. {
  12. ProcessImplementors(componentsImplementor);
  13. }
  14. public void AddNodes(params INodeBuilder[] nodesWithID)
  15. {
  16. _nodesToBuild.AddRange(nodesWithID);
  17. }
  18. internal void BuildGroupedNodes
  19. (int entityID, int groupID,
  20. Dictionary<int, Dictionary<Type, ITypeSafeList>> groupNodesByType,
  21. ref BuildNodeCallbackStruct callBackstruct)
  22. {
  23. for (int index = 0; index < _nodesToBuild.Count; index++)
  24. {
  25. var nodeBuilder = _nodesToBuild[index];
  26. var nodeType = nodeBuilder.GetNodeType();
  27. Dictionary<Type, ITypeSafeList> groupedNodesTyped;
  28. if (groupNodesByType.TryGetValue(groupID, out groupedNodesTyped) == false)
  29. {
  30. groupedNodesTyped = new Dictionary<Type, ITypeSafeList>();
  31. groupNodesByType.Add(groupID, groupedNodesTyped);
  32. };
  33. BuildAndFillNode(entityID, groupedNodesTyped, nodeType, nodeBuilder);
  34. }
  35. SetupImplementors(ref callBackstruct, entityID);
  36. }
  37. internal void BuildNodes(int entityID,
  38. Dictionary<Type, ITypeSafeList> nodesByType,
  39. ref BuildNodeCallbackStruct callBackstruct)
  40. {
  41. int count = _nodesToBuild.Count;
  42. for (int index = 0; index < count; index++)
  43. {
  44. var nodeBuilder = _nodesToBuild[index];
  45. var nodeType = nodeBuilder.GetNodeType();
  46. BuildAndFillNode(entityID, nodesByType, nodeType, nodeBuilder);
  47. }
  48. SetupImplementors(ref callBackstruct, entityID);
  49. }
  50. void BuildAndFillNode(int entityID, Dictionary<Type, ITypeSafeList> groupedNodesTyped, Type nodeType, INodeBuilder nodeBuilder)
  51. {
  52. ITypeSafeList nodes;
  53. var nodesPoolWillBeCreated = groupedNodesTyped.TryGetValue(nodeType, out nodes) == false;
  54. var nodeObjectToFill = nodeBuilder.BuildNodeAndAddToList(ref nodes, entityID);
  55. if (nodesPoolWillBeCreated)
  56. groupedNodesTyped.Add(nodeType, nodes);
  57. //the semantic of this code must still be improved
  58. //but only classes can be filled, so I am aware
  59. //it's a NodeWithID
  60. if (nodeObjectToFill != null)
  61. FillNode(nodeObjectToFill as NodeWithID);
  62. }
  63. /// <summary>
  64. /// if you want to avoid allocation in run-time, you can prebuild
  65. /// EntityDescriptors and use them to build entities at different
  66. /// times
  67. /// </summary>
  68. protected EntityDescriptor(INodeBuilder[] nodesToBuild)
  69. {
  70. _nodesToBuild = new FasterList<INodeBuilder>(nodesToBuild);
  71. }
  72. protected EntityDescriptor(INodeBuilder[] nodesToBuild,
  73. params object[] componentsImplementor):this(nodesToBuild)
  74. {
  75. ProcessImplementors(componentsImplementor);
  76. }
  77. void ProcessImplementors(object[] implementors)
  78. {
  79. for (int index = 0; index < implementors.Length; index++)
  80. {
  81. var implementor = implementors[index];
  82. if (implementor != null)
  83. {
  84. if (implementor is IRemoveEntityComponent)
  85. _removingImplementors.Add(new DataStructures.WeakReference<IRemoveEntityComponent>(implementor as IRemoveEntityComponent));
  86. if (implementor is IDisableEntityComponent)
  87. _disablingImplementors.Add(new DataStructures.WeakReference<IDisableEntityComponent>(implementor as IDisableEntityComponent));
  88. if (implementor is IEnableEntityComponent)
  89. _enablingImplementors.Add(new DataStructures.WeakReference<IEnableEntityComponent>(implementor as IEnableEntityComponent));
  90. var interfaces = implementor.GetType().GetInterfaces();
  91. var weakReference = new DataStructures.WeakReference<object>(implementor);
  92. for (int iindex = 0; iindex < interfaces.Length; iindex++)
  93. {
  94. var componentType = interfaces[iindex];
  95. _implementorsByType[componentType] = weakReference;
  96. #if DEBUG && !PROFILER
  97. if (_implementorCounterByType.ContainsKey(componentType) == false)
  98. _implementorCounterByType[componentType] = 1;
  99. else
  100. _implementorCounterByType[componentType]++;
  101. #endif
  102. }
  103. }
  104. #if DEBUG && !PROFILER
  105. else
  106. Utility.Console.LogError(NULL_IMPLEMENTOR_ERROR.FastConcat(ToString()));
  107. #endif
  108. }
  109. }
  110. void SetupImplementors(
  111. ref BuildNodeCallbackStruct callBackstruct,
  112. int entityID)
  113. {
  114. var RemoveEntity = callBackstruct.internalRemove;
  115. var DisableEntity = callBackstruct.internalDisable;
  116. var EnableEntity = callBackstruct.internalEnable;
  117. int removingImplementorsCount = _removingImplementors.Count;
  118. if (removingImplementorsCount > 0)
  119. {
  120. Action removeEntityAction = () => RemoveEntity(_nodesToBuild, entityID);
  121. for (int index = 0; index < removingImplementorsCount; index++)
  122. _removingImplementors[index].Target.removeEntity = removeEntityAction;
  123. }
  124. int disablingImplementorsCount = _disablingImplementors.Count;
  125. if (disablingImplementorsCount > 0)
  126. {
  127. Action disableEntityAction = () => DisableEntity(_nodesToBuild, entityID);
  128. for (int index = 0; index < disablingImplementorsCount; index++)
  129. _disablingImplementors[index].Target.disableEntity = disableEntityAction;
  130. }
  131. int enablingImplementorsCount = _enablingImplementors.Count;
  132. if (enablingImplementorsCount > 0)
  133. {
  134. Action enableEntityAction = () => EnableEntity(_nodesToBuild, entityID);
  135. for (int index = 0; index < enablingImplementorsCount; index++)
  136. _enablingImplementors[index].Target.enableEntity = enableEntityAction;
  137. }
  138. }
  139. void FillNode<TNode>(TNode node) where TNode : NodeWithID
  140. {
  141. var fields = node.GetType().GetFields(BindingFlags.Public |
  142. BindingFlags.Instance);
  143. for (int i = fields.Length - 1; i >= 0; --i)
  144. {
  145. var field = fields[i];
  146. Type fieldType = field.FieldType;
  147. DataStructures.WeakReference<object> component;
  148. if (_implementorsByType.TryGetValue(fieldType, out component) == false)
  149. {
  150. Exception e =
  151. new Exception(NOT_FOUND_EXCEPTION +
  152. field.FieldType.Name + " - Node: " + node.GetType().Name +
  153. " - EntityDescriptor " + this);
  154. throw e;
  155. }
  156. else
  157. field.SetValue(node, component.Target);
  158. #if DEBUG && !PROFILER
  159. {
  160. if (_implementorCounterByType[fieldType] != 1)
  161. {
  162. Utility.Console.LogError(
  163. DUPLICATE_IMPLEMENTOR_ERROR.FastConcat("component: ", fieldType.ToString(),
  164. " implementor: ", component.Target.ToString()));
  165. }
  166. }
  167. #endif
  168. }
  169. }
  170. readonly FasterList<DataStructures.WeakReference<IDisableEntityComponent>> _disablingImplementors = new FasterList<DataStructures.WeakReference<IDisableEntityComponent>>();
  171. readonly FasterList<DataStructures.WeakReference<IRemoveEntityComponent>> _removingImplementors = new FasterList<DataStructures.WeakReference<IRemoveEntityComponent>>();
  172. readonly FasterList<DataStructures.WeakReference<IEnableEntityComponent>> _enablingImplementors = new FasterList<DataStructures.WeakReference<IEnableEntityComponent>>();
  173. readonly Dictionary<Type, DataStructures.WeakReference<object>> _implementorsByType = new Dictionary<Type, DataStructures.WeakReference<object>>();
  174. #if DEBUG && !PROFILER
  175. readonly Dictionary<Type, int> _implementorCounterByType = new Dictionary<Type, int>();
  176. #endif
  177. readonly FasterList<INodeBuilder> _nodesToBuild;
  178. const string DUPLICATE_IMPLEMENTOR_ERROR = "the same component is implemented with more than one implementor. This is considered an error and MUST be fixed. ";
  179. const string NULL_IMPLEMENTOR_ERROR = "Null implementor, are you using a wild GetComponents<Monobehaviour> to fetch it? ";
  180. const string NOT_FOUND_EXCEPTION = "Svelto.ECS: Implementor not found for a Node. Implementor Type: ";
  181. }
  182. }