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.

ComponentBuilder.cs 9.1KB

Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
5 years ago
6 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
5 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
5 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
5 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
5 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using System.Threading;
  7. using DBC.ECS;
  8. using Svelto.Common;
  9. using Svelto.DataStructures;
  10. using Svelto.ECS.Hybrid;
  11. using Svelto.ECS.Internal;
  12. using Svelto.Utilities;
  13. namespace Svelto.ECS
  14. {
  15. struct ComponentBuilderComparer : IEqualityComparer<IComponentBuilder>
  16. {
  17. public bool Equals(IComponentBuilder x, IComponentBuilder y)
  18. {
  19. return x.GetEntityComponentType() == y.GetEntityComponentType();
  20. }
  21. public int GetHashCode(IComponentBuilder obj)
  22. {
  23. return obj.GetEntityComponentType().GetHashCode();
  24. }
  25. }
  26. public static class BurstCompatibleCounter
  27. {
  28. public static int counter;
  29. }
  30. static public class ComponentTypeMap
  31. {
  32. static readonly FasterDictionary<RefWrapper<Type>, ComponentID> _componentTypeMap = new FasterDictionary<RefWrapper<Type>, ComponentID>();
  33. static readonly FasterDictionary<ComponentID, Type> _reverseComponentTypeMap = new FasterDictionary<ComponentID, Type>();
  34. public static void Add(Type type, ComponentID idData)
  35. {
  36. _componentTypeMap.Add(type, idData);
  37. _reverseComponentTypeMap.Add(idData, type);
  38. }
  39. public static ComponentID FetchID(Type type)
  40. {
  41. return _componentTypeMap[type];
  42. }
  43. public static Type FetchType(ComponentID id)
  44. {
  45. return _reverseComponentTypeMap[id];
  46. }
  47. }
  48. public class ComponentTypeID<T> where T : struct, _IInternalEntityComponent
  49. {
  50. static readonly SharedStaticWrapper<ComponentID, ComponentTypeID<T>> _id;
  51. public static ComponentID id
  52. {
  53. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  54. get => _id.Data;
  55. }
  56. //todo: any reason to not do this? If I don't, I cannot Create filters in ready functions and
  57. //I have to remove the CreateFilter method
  58. static ComponentTypeID()
  59. {
  60. Init();
  61. }
  62. #if UNITY_BURST
  63. [Unity.Burst.BurstDiscard]
  64. //SharedStatic values must be initialized from not burstified code
  65. #endif
  66. static void Init()
  67. {
  68. _id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter);
  69. ComponentTypeMap.Add(typeof(T), id);
  70. }
  71. }
  72. sealed class ComponentIDDebugProxy
  73. {
  74. public ComponentIDDebugProxy(ComponentID id)
  75. {
  76. this._id = id;
  77. }
  78. public Type type => ComponentTypeMap.FetchType(_id);
  79. readonly ComponentID _id;
  80. }
  81. [DebuggerTypeProxy(typeof(ComponentIDDebugProxy))]
  82. public struct ComponentID: IEquatable<ComponentID>
  83. {
  84. public static implicit operator int(ComponentID id)
  85. {
  86. return id._id;
  87. }
  88. public static implicit operator uint(ComponentID id)
  89. {
  90. return (uint)id._id;
  91. }
  92. public static implicit operator ComponentID(int id)
  93. {
  94. return new ComponentID() {_id = id};
  95. }
  96. public bool Equals(ComponentID other)
  97. {
  98. return _id == other._id;
  99. }
  100. public override int GetHashCode()
  101. {
  102. return _id;
  103. }
  104. int _id;
  105. }
  106. public class ComponentBuilder<T> : IComponentBuilder where T : struct, _IInternalEntityComponent
  107. {
  108. internal static readonly Type ENTITY_COMPONENT_TYPE;
  109. internal static readonly bool IS_ENTITY_VIEW_COMPONENT;
  110. static readonly string ENTITY_COMPONENT_NAME;
  111. static readonly bool IS_UNMANAGED;
  112. #if SLOW_SVELTO_SUBMISSION
  113. public static readonly bool HAS_EGID;
  114. public static readonly bool HAS_REFERENCE;
  115. #endif
  116. static ComponentBuilder()
  117. {
  118. ENTITY_COMPONENT_TYPE = typeof(T);
  119. IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  120. #if SLOW_SVELTO_SUBMISSION
  121. HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  122. HAS_REFERENCE = typeof(INeedEntityReference).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  123. SetEGIDWithoutBoxing<T>.Warmup();
  124. #endif
  125. ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
  126. IS_UNMANAGED = TypeCache<T>.isUnmanaged; //attention this is important as it serves as warm up for Type<T>
  127. #if UNITY_NATIVE
  128. if (IS_UNMANAGED)
  129. EntityComponentIDMap.Register<T>(new Filler<T>());
  130. #endif
  131. ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);
  132. if (IS_ENTITY_VIEW_COMPONENT)
  133. {
  134. EntityViewComponentCache.InitCache();
  135. }
  136. else
  137. {
  138. if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT &&
  139. TypeCache<T>.isUnmanaged == false)
  140. throw new Exception(
  141. $"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
  142. }
  143. }
  144. public ComponentBuilder()
  145. {
  146. _initializer = default;
  147. }
  148. public ComponentBuilder(in T initializer) : this()
  149. {
  150. _initializer = initializer;
  151. }
  152. public bool isUnmanaged => IS_UNMANAGED;
  153. public ComponentID getComponentID => ComponentTypeID<T>.id;
  154. static readonly ThreadLocal<EntityViewComponentCache> _localCache = new ThreadLocal<EntityViewComponentCache>(() => new EntityViewComponentCache());
  155. public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable<object> implementors)
  156. {
  157. var castedDic = dictionary as ITypeSafeDictionary<T>;
  158. if (IS_ENTITY_VIEW_COMPONENT)
  159. {
  160. T entityComponent = default;
  161. Check.Require(castedDic.ContainsKey(egid.entityID) == false,
  162. $"building an entity with already used entity id! id: '{(ulong)egid}', {ENTITY_COMPONENT_NAME}");
  163. this.SetEntityViewComponentImplementors(ref entityComponent, implementors, _localCache.Value);
  164. castedDic.Add(egid.entityID, entityComponent);
  165. }
  166. else
  167. {
  168. Check.Require(!castedDic.ContainsKey(egid.entityID),
  169. $"building an entity with already used entity id! id: '{egid.entityID}'");
  170. castedDic.Add(egid.entityID, _initializer);
  171. }
  172. }
  173. void IComponentBuilder.Preallocate(ITypeSafeDictionary dictionary, uint size)
  174. {
  175. Preallocate(dictionary, size);
  176. }
  177. public ITypeSafeDictionary CreateDictionary(uint size)
  178. {
  179. return TypeSafeDictionaryFactory<T>.Create(size);
  180. }
  181. public Type GetEntityComponentType()
  182. {
  183. return ENTITY_COMPONENT_TYPE;
  184. }
  185. public override int GetHashCode()
  186. {
  187. return _initializer.GetHashCode();
  188. }
  189. static void Preallocate(ITypeSafeDictionary dictionary, uint size)
  190. {
  191. dictionary.EnsureCapacity(size);
  192. }
  193. readonly T _initializer;
  194. internal class EntityViewComponentCache
  195. {
  196. internal readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
  197. internal readonly Dictionary<Type, Type[]> cachedTypes;
  198. //this is just a local static cache that is cleared after every use
  199. #if DEBUG && !PROFILE_SVELTO
  200. internal readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
  201. #else
  202. internal readonly Dictionary<Type, object> implementorsByType;
  203. #endif
  204. internal EntityViewComponentCache()
  205. {
  206. cachedFields = new FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>();
  207. var type = typeof(T);
  208. var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
  209. for (var i = fields.Length - 1; i >= 0; --i)
  210. {
  211. var field = fields[i];
  212. if (field.FieldType.IsInterface == true)
  213. {
  214. var setter = FastInvoke<T>.MakeSetter(field);
  215. //for each interface, cache the setter for this type
  216. cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter));
  217. }
  218. }
  219. #if DEBUG && !PROFILE_SVELTO
  220. if (fields.Length == 0)
  221. Console.LogWarning($"No fields found in component {type}. Are you declaring only properties?");
  222. #endif
  223. cachedTypes = new Dictionary<Type, Type[]>();
  224. #if DEBUG && !PROFILE_SVELTO
  225. implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
  226. #else
  227. implementorsByType = new Dictionary<Type, object>();
  228. #endif
  229. }
  230. internal static void InitCache()
  231. {
  232. }
  233. }
  234. }
  235. }