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.

EnginesRoot.Entities.cs 9.6KB

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
4 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
4 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
4 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
4 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
4 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
4 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
4 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
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. //#define PARANOID_CHECK
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Runtime.CompilerServices;
  5. using Svelto.DataStructures;
  6. using Svelto.ECS.Internal;
  7. namespace Svelto.ECS
  8. {
  9. public static class EGIDMultiMapperNBExtension
  10. {
  11. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  12. public static EGIDMultiMapper<T> QueryMappedEntities<T>(this EntitiesDB entitiesDb, LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  13. where T : struct, _IInternalEntityComponent
  14. {
  15. var dictionary = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary<T>>((uint) groups.count);
  16. foreach (var group in groups)
  17. {
  18. entitiesDb.QueryOrCreateEntityDictionary<T>(group, out var typeSafeDictionary);
  19. //if (typeSafeDictionary.count > 0) avoiding this allows these egidmappers to be precreated and stored
  20. dictionary.Add(group, typeSafeDictionary as ITypeSafeDictionary<T>);
  21. }
  22. return new EGIDMultiMapper<T>(dictionary);
  23. }
  24. }
  25. public partial class EnginesRoot: IDisposable, IUnitTestingInterface
  26. {
  27. ///--------------------------------------------
  28. ///
  29. public IEntityStreamConsumerFactory GenerateConsumerFactory()
  30. {
  31. return new GenericEntityStreamConsumerFactory(this);
  32. }
  33. public IEntityFactory GenerateEntityFactory()
  34. {
  35. return new GenericEntityFactory(this);
  36. }
  37. public IEntityFunctions GenerateEntityFunctions()
  38. {
  39. return new GenericEntityFunctions(this);
  40. }
  41. ///--------------------------------------------
  42. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  43. EntityInitializer BuildEntity(EGID entityID, IComponentBuilder[] componentsToBuild, Type descriptorType,
  44. IEnumerable<object> implementors, string caller)
  45. {
  46. CheckAddEntityID(entityID, descriptorType, caller);
  47. DBC.ECS.Check.Require(
  48. entityID.groupID.isInvalid == false,
  49. "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?");
  50. var reference = _entityLocator.ClaimReference();
  51. _entityLocator.SetReference(reference, entityID);
  52. var dic = EntityFactory.BuildGroupedEntities(
  53. entityID, _groupedEntityToAdd, componentsToBuild, implementors
  54. #if DEBUG && !PROFILE_SVELTO
  55. , descriptorType
  56. #endif
  57. );
  58. return new EntityInitializer(entityID, dic, reference);
  59. }
  60. /// <summary>
  61. /// Preallocate memory to avoid the impact to resize arrays when many entities are submitted at once
  62. /// </summary>
  63. internal void Preallocate(ExclusiveGroupStruct groupID, uint size, IComponentBuilder[] entityComponentsToBuild)
  64. {
  65. void PreallocateEntitiesToAdd()
  66. {
  67. _groupedEntityToAdd.Preallocate(groupID, size, entityComponentsToBuild);
  68. }
  69. void PreallocateDBGroup()
  70. {
  71. var numberOfEntityComponents = entityComponentsToBuild.Length;
  72. FasterDictionary<ComponentID, ITypeSafeDictionary> group = GetOrAddDBGroup(groupID);
  73. for (var index = 0; index < numberOfEntityComponents; index++)
  74. {
  75. var entityComponentBuilder = entityComponentsToBuild[index];
  76. var entityComponentType = entityComponentBuilder.getComponentID;
  77. var components = group.GetOrAdd(entityComponentType, () => entityComponentBuilder.CreateDictionary(size));
  78. if (components.count != 0)
  79. throw new ECSException("Entity already created in this group, cannot preallocate");
  80. entityComponentBuilder.Preallocate(components, size);
  81. if (_groupsPerEntity.TryGetValue(entityComponentType, out var groupedGroup) == false)
  82. groupedGroup = _groupsPerEntity[entityComponentType] =
  83. new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  84. groupedGroup[groupID] = components;
  85. }
  86. }
  87. PreallocateDBGroup();
  88. PreallocateEntitiesToAdd();
  89. _entityLocator.PreallocateReferenceMaps(groupID, size);
  90. }
  91. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  92. FasterDictionary<ComponentID, ITypeSafeDictionary> GetDBGroup(ExclusiveGroupStruct fromIdGroupId)
  93. {
  94. if (_groupEntityComponentsDB.TryGetValue(
  95. fromIdGroupId,
  96. out FasterDictionary<ComponentID, ITypeSafeDictionary> fromGroup) == false)
  97. throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId.ToName()));
  98. return fromGroup;
  99. }
  100. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  101. FasterDictionary<ComponentID, ITypeSafeDictionary> GetOrAddDBGroup(ExclusiveGroupStruct toGroupId)
  102. {
  103. return _groupEntityComponentsDB.GetOrAdd(
  104. toGroupId,
  105. () => new FasterDictionary<ComponentID, ITypeSafeDictionary>());
  106. }
  107. IComponentBuilder[] FindRealComponents<T>(EGID fromEntityGID) where T : IEntityDescriptor, new()
  108. {
  109. var fromGroup = GetDBGroup(fromEntityGID.groupID);
  110. if (fromGroup.TryGetValue(
  111. ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
  112. out var entityInfoDic) //<entity ID, EntityInfoComponent>
  113. && ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(
  114. fromEntityGID.entityID,
  115. out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
  116. {
  117. #if PARANOID_CHECK
  118. var hash = new HashSet<IComponentBuilder>(entityInfo.componentsToBuild,
  119. default(ComponentBuilderComparer));
  120. foreach (var component in EntityDescriptorTemplate<T>.descriptor.componentsToBuild)
  121. {
  122. if (hash.Contains(component) == false)
  123. throw new Exception(
  124. $"entityInfo.componentsToBuild must contain all the base components {fromEntityGID}," +
  125. $" missing component {component}");
  126. hash.Remove(component);
  127. }
  128. #endif
  129. return entityInfo.componentsToBuild;
  130. }
  131. return EntityDescriptorTemplate<T>.realDescriptor.componentsToBuild;
  132. }
  133. IComponentBuilder[] FindRealComponents(EGID fromEntityGID, IComponentBuilder[] baseComponents)
  134. {
  135. var fromGroup = GetDBGroup(fromEntityGID.groupID);
  136. if (fromGroup.TryGetValue(
  137. ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
  138. out var entityInfoDic) //<entity ID, EntityInfoComponent>
  139. && ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(
  140. fromEntityGID.entityID,
  141. out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
  142. {
  143. #if PARANOID_CHECK
  144. var hash = new HashSet<IComponentBuilder>(entityInfo.componentsToBuild,
  145. default(ComponentBuilderComparer));
  146. foreach (var component in baseComponents)
  147. {
  148. if (hash.Contains(component) == false)
  149. throw new Exception(
  150. $"entityInfo.componentsToBuild must contain all the base components {fromEntityGID}," +
  151. $" missing component {component}");
  152. hash.Remove(component);
  153. }
  154. #endif
  155. return entityInfo.componentsToBuild;
  156. }
  157. return baseComponents;
  158. }
  159. //one datastructure rule them all:
  160. //split by group
  161. //split by type per group. It's possible to get all the entities of a give type T per group thanks
  162. //to the FasterDictionary capabilities OR it's possible to get a specific entityComponent indexed by
  163. //ID. This ID doesn't need to be the EGID, it can be just the entityID
  164. //for each group id, save a dictionary indexed by entity type of entities indexed by id
  165. // group EntityComponentType entityID, EntityComponent
  166. internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  167. _groupEntityComponentsDB;
  168. //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
  169. //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
  170. //by _groupEntityComponentsDB
  171. // <EntityComponentType <groupID <entityID, EntityComponent>>>
  172. internal readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>
  173. _groupsPerEntity;
  174. readonly EntitiesDB _entitiesDB;
  175. EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB;
  176. }
  177. public interface IUnitTestingInterface
  178. {
  179. EntitiesDB entitiesForTesting { get; }
  180. }
  181. }