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.

CheckEntityUtilities.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
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
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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. //#define VERBOSE
  2. #if !DEBUG || PROFILE_SVELTO
  3. #define DONT_USE
  4. using System.Diagnostics;
  5. #endif
  6. using System;
  7. using System.Collections.Generic;
  8. using Svelto.DataStructures;
  9. namespace Svelto.ECS
  10. {
  11. /// <summary>
  12. /// Note: this check doesn't catch the case when an add and remove is done on the same entity before the next
  13. /// submission. Two operations on the same entity are not allowed between submissions.
  14. /// </summary>
  15. public partial class EnginesRoot
  16. {
  17. enum OperationType
  18. {
  19. Add,
  20. Remove,
  21. SwapFrom,
  22. SwapTo
  23. }
  24. #if DONT_USE
  25. [Conditional("MEANINGLESS")]
  26. #endif
  27. void InitDebugChecks()
  28. {
  29. _multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, OperationType>();
  30. _idChecker = new FasterDictionary<ExclusiveGroupStruct, HashSet<uint>>();
  31. }
  32. #if DONT_USE
  33. [Conditional("MEANINGLESS")]
  34. #endif
  35. void CheckSwapEntityID(EGID fromEgid, EGID toEgid, Type entityDescriptorType, string caller)
  36. {
  37. if (_multipleOperationOnSameEGIDChecker.TryGetValue(fromEgid, out var fromOperationType) == true)
  38. {
  39. var operationName = OperationName(fromOperationType);
  40. throw new ECSException(
  41. "Executing multiple structural changes (swapFrom) on the same entity is not supported "
  42. .FastConcat(" caller: ", caller, " ").FastConcat(fromEgid.entityID).FastConcat(" groupid: ")
  43. .FastConcat(fromEgid.groupID.ToName()).FastConcat(" type: ")
  44. .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
  45. .FastConcat(" previous operation was: ")
  46. .FastConcat(operationName));
  47. }
  48. if (_multipleOperationOnSameEGIDChecker.TryGetValue(toEgid, out var toOperationType) == true)
  49. {
  50. var operationName = OperationName(toOperationType);
  51. throw new ECSException(
  52. "Executing multiple structural changes (swapTo) on the same entity is not supported "
  53. .FastConcat(" caller: ", caller, " ").FastConcat(toEgid.entityID).FastConcat(" groupid: ")
  54. .FastConcat(toEgid.groupID.ToName()).FastConcat(" type: ")
  55. .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
  56. .FastConcat(" previous operation was: ")
  57. .FastConcat(operationName));
  58. }
  59. HashRemove(fromEgid, entityDescriptorType, false, caller);
  60. HashAdd(toEgid, entityDescriptorType, caller);
  61. _multipleOperationOnSameEGIDChecker.Add(fromEgid, OperationType.SwapFrom);
  62. _multipleOperationOnSameEGIDChecker.Add(toEgid, OperationType.SwapTo);
  63. }
  64. #if DONT_USE
  65. [Conditional("MEANINGLESS")]
  66. #endif
  67. void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller)
  68. {
  69. bool isAllowed = false;
  70. if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
  71. {
  72. //remove supersedes swap and remove operations, this means remove is allowed if the previous operation was swap or remove on the same submission
  73. isAllowed = operationType == OperationType.Remove || operationType == OperationType.SwapFrom;
  74. if (isAllowed)
  75. {
  76. #if VERBOSE
  77. var operationName = OperationName(operationType);
  78. Console.LogDebugWarning(
  79. "Executing multiple structural changes (remove) in one submission on the same entity. Remove supersedes swap and remove operations "
  80. .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
  81. .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
  82. .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
  83. .FastConcat(" previous operation was: ")
  84. .FastConcat(operationName));
  85. #endif
  86. }
  87. else
  88. throw new ECSException(
  89. "Executing multiple structural changes (remove) in one submission on the same entity is not supported "
  90. .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
  91. .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
  92. .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
  93. .FastConcat(" previous operation was: ")
  94. .FastConcat("add"));
  95. }
  96. HashRemove(egid, entityDescriptorType, isAllowed, caller);
  97. if (isAllowed == false)
  98. _multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Remove);
  99. else
  100. _multipleOperationOnSameEGIDChecker[egid] = OperationType.Remove;
  101. }
  102. #if DONT_USE
  103. [Conditional("MEANINGLESS")]
  104. #endif
  105. void CheckAddEntityID(EGID egid, Type entityDescriptorType, string caller)
  106. {
  107. if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
  108. {
  109. var operationName = OperationName(operationType);
  110. throw new ECSException(
  111. "Executing multiple structural changes (build) on the same entity is not supported "
  112. .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
  113. .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
  114. .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
  115. .FastConcat(" previous operation was: ")
  116. .FastConcat(operationName));
  117. }
  118. HashAdd(egid, entityDescriptorType, caller);
  119. _multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Add);
  120. }
  121. #if DONT_USE
  122. [Conditional("MEANINGLESS")]
  123. #endif
  124. void RemoveGroupID(ExclusiveBuildGroup groupID)
  125. {
  126. _idChecker.Remove(groupID);
  127. }
  128. #if DONT_USE
  129. [Conditional("MEANINGLESS")]
  130. #endif
  131. void ClearChecksForMultipleOperationsOnTheSameEgid()
  132. {
  133. _multipleOperationOnSameEGIDChecker.Clear();
  134. }
  135. void HashRemove(EGID egid, Type entityDescriptorType, bool isAllowed, string caller)
  136. {
  137. if (_idChecker.TryGetValue(egid.groupID, out HashSet<uint> hash))
  138. {
  139. if (hash.Contains(egid.entityID) == false && isAllowed == false)
  140. throw new ECSException(
  141. "Trying to remove an Entity not present in the database "
  142. .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
  143. .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
  144. .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
  145. }
  146. else
  147. {
  148. throw new ECSException(
  149. "Trying to remove an Entity with a group never used so far "
  150. .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
  151. .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
  152. .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
  153. }
  154. hash.Remove(egid.entityID);
  155. }
  156. void HashAdd(EGID egid, Type entityDescriptorType, string caller)
  157. {
  158. var hash = _idChecker.GetOrAdd(egid.groupID, () => new HashSet<uint>());
  159. if (hash.Contains(egid.entityID) == true)
  160. throw new ECSException(
  161. "Trying to add an Entity already present in the database "
  162. .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID)
  163. .FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
  164. .FastConcat(
  165. entityDescriptorType != null
  166. ? entityDescriptorType.Name
  167. : "not available"));
  168. hash.Add(egid.entityID);
  169. }
  170. string OperationName(OperationType operationType)
  171. {
  172. string operationName;
  173. switch (operationType)
  174. {
  175. case OperationType.Remove:
  176. operationName = "remove";
  177. break;
  178. case OperationType.Add:
  179. operationName = "add";
  180. break;
  181. case OperationType.SwapFrom:
  182. operationName = "swapFrom";
  183. break;
  184. default:
  185. operationName = "swapTo";
  186. break;
  187. }
  188. return operationName;
  189. }
  190. DataStructures.FasterDictionary<EGID, OperationType> _multipleOperationOnSameEGIDChecker;
  191. DataStructures.FasterDictionary<ExclusiveGroupStruct, HashSet<uint>> _idChecker;
  192. }
  193. }