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.

172 lines
6.2KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. namespace Svelto.ECS
  6. {
  7. [StructLayout(LayoutKind.Explicit, Size = 4)]
  8. //the type doesn't implement IEqualityComparer, what implements it is a custom comparer
  9. public readonly struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>
  10. {
  11. public static readonly ExclusiveGroupStruct Invalid = default; //must stay here because of Burst
  12. public ExclusiveGroupStruct(byte[] data, uint pos):this()
  13. {
  14. _idInternal = (uint)(
  15. data[pos]
  16. | data[++pos] << 8
  17. | data[++pos] << 16
  18. );
  19. _bytemask = (byte) (data[++pos] << 24);
  20. DBC.ECS.Check.Ensure(id < _globalId, "Invalid group ID deserialiased");
  21. }
  22. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  23. public override bool Equals(object obj)
  24. {
  25. return obj is ExclusiveGroupStruct other && Equals(other);
  26. }
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. public override int GetHashCode()
  29. {
  30. return (int) id;
  31. }
  32. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  33. public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
  34. {
  35. return c1.Equals(c2);
  36. }
  37. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  38. public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
  39. {
  40. return c1.Equals(c2) == false;
  41. }
  42. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  43. public bool Equals(ExclusiveGroupStruct other)
  44. {
  45. #if DEBUG && !PROFILE_SVELTO
  46. if ((other.id != this.id || other._bytemask == this._bytemask) == false)
  47. throw new ECSException(
  48. "if the groups are correctly initialised, two groups with the same ID and different bitmask cannot exist");
  49. #endif
  50. return other.id == this.id;
  51. }
  52. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  53. public int CompareTo(ExclusiveGroupStruct other)
  54. {
  55. return other.id.CompareTo(id);
  56. }
  57. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  58. public bool IsEnabled()
  59. {
  60. return (_bytemask & (byte)ExclusiveGroupBitmask.DISABLED_BIT) == 0;
  61. }
  62. public override string ToString()
  63. {
  64. return this.ToName();
  65. }
  66. public bool isInvalid => this == Invalid;
  67. public uint id => _idInternal & 0xFFFFFF;
  68. public uint ToIDAndBitmask() => _idInternal;
  69. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  70. public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b)
  71. {
  72. var aID = a.id + b;
  73. #if DEBUG && !PROFILE_SVELTO
  74. if (aID >= 0xFFFFFF)
  75. throw new IndexOutOfRangeException();
  76. #endif
  77. var group = new ExclusiveGroupStruct(aID);
  78. return @group;
  79. }
  80. internal static ExclusiveGroupStruct Generate()
  81. {
  82. var newValue = Interlocked.Increment(ref _staticGlobalID);
  83. ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint) newValue - (uint) 1);
  84. DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");
  85. return groupStruct;
  86. }
  87. internal static ExclusiveGroupStruct Generate(byte bitmask)
  88. {
  89. var newValue = Interlocked.Increment(ref _staticGlobalID);
  90. ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint) newValue - (uint) 1, bitmask);
  91. DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");
  92. return groupStruct;
  93. }
  94. /// <summary>
  95. /// Use this to reserve N groups. We of course assign the current ID and then increment the index
  96. /// by range so that the next reserved index will take the range in consideration. This method is used
  97. /// internally by ExclusiveGroup.
  98. /// </summary>
  99. internal static ExclusiveGroupStruct GenerateWithRange(ushort range)
  100. {
  101. var newValue = Interlocked.Add(ref _staticGlobalID, (int)range);
  102. ExclusiveGroupStruct groupStruct = new ExclusiveGroupStruct((uint) newValue - (uint)range);
  103. DBC.ECS.Check.Require(_globalId < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");
  104. return groupStruct;
  105. }
  106. /// <summary>
  107. /// used internally only by the framework to convert uint in to groups. ID must be generated by the framework
  108. /// so only the framework can assure that this method is not being abused
  109. /// </summary>
  110. internal ExclusiveGroupStruct(uint groupID):this()
  111. {
  112. #if DEBUG && !PROFILE_SVELTO
  113. if (groupID >= 0xFFFFFF)
  114. throw new IndexOutOfRangeException();
  115. #endif
  116. _idInternal = groupID;
  117. }
  118. ExclusiveGroupStruct(uint groupID, byte bytemask):this()
  119. {
  120. #if DEBUG && !PROFILE_SVELTO
  121. if (groupID >= 0xFFFFFF)
  122. throw new IndexOutOfRangeException();
  123. #endif
  124. _idInternal = groupID;
  125. _bytemask = bytemask;
  126. }
  127. static ExclusiveGroupStruct()
  128. {
  129. _staticGlobalID = 1;
  130. }
  131. [FieldOffset(0)] readonly uint _idInternal;
  132. //byte mask can be used to add special flags to specific groups that can be checked for example when swapping groups
  133. //however at the moment we are not letting the user access it, because if we do so we should give access only to
  134. //4 bits are the other 4 bits will stay reserved for Svelto use (at the moment of writing using only the disable
  135. //bit)
  136. [FieldOffset(3)] readonly byte _bytemask;
  137. static int _staticGlobalID;
  138. static uint _globalId => (uint) _staticGlobalID;
  139. }
  140. }