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.

265 lines
8.4KB

  1. using System;
  2. using System.Collections.Generic;
  3. #pragma warning disable 660,661
  4. namespace Svelto.ECS
  5. {
  6. /// <summary>
  7. /// Exclusive Groups guarantee that the GroupID is unique.
  8. ///
  9. /// The best way to use it is like:
  10. ///
  11. /// public static class MyExclusiveGroups //(can be as many as you want)
  12. /// {
  13. /// public static ExclusiveGroup MyExclusiveGroup1 = new ExclusiveGroup();
  14. ///
  15. /// public static ExclusiveGroup[] GroupOfGroups = { MyExclusiveGroup1, ...}; //for each on this!
  16. /// }
  17. /// </summary>
  18. ///
  19. ///use this like:
  20. /// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {}
  21. public abstract class NamedExclusiveGroup<T>:ExclusiveGroup
  22. {
  23. public static ExclusiveGroup Group = new ExclusiveGroup();
  24. public static string name = typeof(T).FullName;
  25. public NamedExclusiveGroup() { }
  26. public NamedExclusiveGroup(string recognizeAs) : base(recognizeAs)
  27. {}
  28. public NamedExclusiveGroup(ushort range) : base(range)
  29. {}
  30. }
  31. public class ExclusiveGroup
  32. {
  33. public ExclusiveGroup()
  34. {
  35. _group = ExclusiveGroupStruct.Generate();
  36. }
  37. public ExclusiveGroup(string recognizeAs)
  38. {
  39. _group = ExclusiveGroupStruct.Generate();
  40. _serialisedGroups.Add(recognizeAs, _group);
  41. }
  42. public ExclusiveGroup(ushort range)
  43. {
  44. _group = new ExclusiveGroupStruct(range);
  45. #if DEBUG
  46. _range = range;
  47. #endif
  48. }
  49. public static implicit operator ExclusiveGroupStruct(ExclusiveGroup group)
  50. {
  51. return group._group;
  52. }
  53. public static explicit operator uint(ExclusiveGroup group)
  54. {
  55. return group._group;
  56. }
  57. public static ExclusiveGroupStruct operator+(ExclusiveGroup a, uint b)
  58. {
  59. #if DEBUG
  60. if (a._range == 0)
  61. throw new ECSException("adding values to a not ranged ExclusiveGroup");
  62. if (b >= a._range)
  63. throw new ECSException("Using out of range group");
  64. #endif
  65. return a._group + b;
  66. }
  67. readonly ExclusiveGroupStruct _group;
  68. //I use this as parameter because it must not be possible to pass null Exclusive Groups.
  69. public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>,
  70. IEqualityComparer<ExclusiveGroupStruct>
  71. {
  72. public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
  73. {
  74. return c1.Equals(c2);
  75. }
  76. public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
  77. {
  78. return c1.Equals(c2) == false;
  79. }
  80. public bool Equals(ExclusiveGroupStruct other)
  81. {
  82. return other._id == _id;
  83. }
  84. public int CompareTo(ExclusiveGroupStruct other)
  85. {
  86. return other._id.CompareTo(_id);
  87. }
  88. public bool Equals(ExclusiveGroupStruct x, ExclusiveGroupStruct y)
  89. {
  90. return x._id == y._id;
  91. }
  92. public int GetHashCode(ExclusiveGroupStruct obj)
  93. {
  94. return _id.GetHashCode();
  95. }
  96. internal static ExclusiveGroupStruct Generate()
  97. {
  98. ExclusiveGroupStruct groupStruct;
  99. groupStruct._id = _globalId;
  100. DBC.ECS.Check.Require(_globalId + 1 < ushort.MaxValue, "too many exclusive groups created");
  101. _globalId++;
  102. return groupStruct;
  103. }
  104. /// <summary>
  105. /// Use this constructor to reserve N groups
  106. /// </summary>
  107. internal ExclusiveGroupStruct(ushort range)
  108. {
  109. _id = _globalId;
  110. DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created");
  111. _globalId += range;
  112. }
  113. internal ExclusiveGroupStruct(uint groupID)
  114. {
  115. _id = groupID;
  116. }
  117. public ExclusiveGroupStruct(byte[] data, uint pos)
  118. {
  119. _id = (uint)(
  120. data[pos++]
  121. | data[pos++] << 8
  122. | data[pos++] << 16
  123. | data[pos++] << 24
  124. );
  125. DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased");
  126. }
  127. public static implicit operator uint(ExclusiveGroupStruct groupStruct)
  128. {
  129. return groupStruct._id;
  130. }
  131. public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b)
  132. {
  133. var group = new ExclusiveGroupStruct();
  134. group._id = a._id + b;
  135. return group;
  136. }
  137. uint _id;
  138. static uint _globalId;
  139. }
  140. public static ExclusiveGroupStruct Search(string holderGroupName)
  141. {
  142. if (_serialisedGroups.ContainsKey(holderGroupName) == false)
  143. throw new Exception("Named Group Not Found ".FastConcat(holderGroupName));
  144. return _serialisedGroups[holderGroupName];
  145. }
  146. static readonly Dictionary<string, ExclusiveGroupStruct> _serialisedGroups = new Dictionary<string,
  147. ExclusiveGroupStruct>();
  148. #if DEBUG
  149. readonly ushort _range;
  150. #endif
  151. }
  152. }
  153. #if future
  154. public static void ConstructStaticGroups()
  155. {
  156. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
  157. // Assemblies or types aren't guaranteed to be returned in the same order,
  158. // and I couldn't find proof that `GetTypes()` returns them in fixed order either,
  159. // even for builds made with the exact same source code.
  160. // So will sort reflection results by name before constructing groups.
  161. var groupFields = new List<KeyValuePair<string, FieldInfo>>();
  162. foreach (Assembly assembly in assemblies)
  163. {
  164. Type[] types = GetTypesSafe(assembly);
  165. foreach (Type type in types)
  166. {
  167. if (type == null || !type.IsClass)
  168. {
  169. continue;
  170. }
  171. // Groups defined as static members in static classes
  172. if (type.IsSealed && type.IsAbstract)
  173. {
  174. FieldInfo[] fields = type.GetFields();
  175. foreach(var field in fields)
  176. {
  177. if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType))
  178. {
  179. groupFields.Add(new KeyValuePair<string, FieldInfo>($"{type.FullName}.{field.Name}", field));
  180. }
  181. }
  182. }
  183. // Groups defined as classes
  184. else if (type.BaseType != null
  185. && type.BaseType.IsGenericType
  186. && type.BaseType.GetGenericTypeDefinition() == typeof(ExclusiveGroup<>))
  187. {
  188. FieldInfo field = type.GetField("Group",
  189. BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
  190. groupFields.Add(new KeyValuePair<string, FieldInfo>(type.FullName, field));
  191. }
  192. }
  193. }
  194. groupFields.Sort((a, b) => string.CompareOrdinal(a.Key, b.Key));
  195. for (int i = 0; i < groupFields.Count; ++i)
  196. {
  197. groupFields[i].Value.GetValue(null);
  198. #if DEBUG
  199. var group = (ExclusiveGroup) groupFields[i].Value.GetValue(null);
  200. groupNames[(uint) group] = groupFields[i].Key;
  201. #endif
  202. }
  203. }
  204. static Type[] GetTypesSafe(Assembly assembly)
  205. {
  206. try
  207. {
  208. Type[] types = assembly.GetTypes();
  209. return types;
  210. }
  211. catch (ReflectionTypeLoadException e)
  212. {
  213. return e.Types;
  214. }
  215. }
  216. #if DEBUG
  217. static string[] groupNames = new string[ushort.MaxValue];
  218. #endif
  219. #endif