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.

177 lines
8.3KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using System.Text;
  7. using Svelto.ECS.Serialization;
  8. namespace Svelto.ECS
  9. {
  10. public static class GroupHashMap
  11. {
  12. /// <summary>
  13. /// c# Static constructors are guaranteed to be thread safe
  14. /// The runtime guarantees that a static constructor is only called once. So even if a type is called by multiple threads at the same time,
  15. /// the static constructor is always executed one time. To get a better understanding how this works, it helps to know what purpose it serves.
  16. ///
  17. /// Warmup the group hash map. This will call all the static constructors of the group types
  18. /// </summary>
  19. internal static void WarmUp()
  20. {
  21. List<Assembly> assemblies = AssemblyUtility.GetCompatibleAssemblies();
  22. foreach (Assembly assembly in assemblies)
  23. {
  24. var typeOfExclusiveGroup = typeof(ExclusiveGroup);
  25. var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct);
  26. var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup);
  27. var typesSafe = AssemblyUtility.GetTypesSafe(assembly);
  28. foreach (Type type in typesSafe)
  29. {
  30. CheckForGroupCompounds(type);
  31. //Search inside static types
  32. if (type != null && type.IsClass && type.IsSealed
  33. && type.IsAbstract) //IsClass and IsSealed and IsAbstract means only static classes
  34. {
  35. var subClasses = type.GetNestedTypes();
  36. foreach (var subclass in subClasses)
  37. {
  38. CheckForGroupCompounds(subclass);
  39. }
  40. var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
  41. foreach (var field in fields)
  42. {
  43. if ((typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)
  44. || typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)
  45. || typeOfExclusiveBuildGroup.IsAssignableFrom(field.FieldType)))
  46. {
  47. uint groupIDAndBitMask;
  48. if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType))
  49. {
  50. var group = (ExclusiveGroup)field.GetValue(null);
  51. groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
  52. }
  53. else if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType))
  54. {
  55. var group = (ExclusiveGroupStruct)field.GetValue(null);
  56. groupIDAndBitMask = @group.ToIDAndBitmask();
  57. }
  58. else
  59. {
  60. var group = (ExclusiveBuildGroup)field.GetValue(null);
  61. groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
  62. }
  63. {
  64. var bitMask = (byte)(groupIDAndBitMask >> 24);
  65. var groupID = groupIDAndBitMask & 0xFFFFFF;
  66. ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID, bitMask);
  67. #if DEBUG && !PROFILE_SVELTO
  68. if (GroupNamesMap.idToName.ContainsKey(@group) == false)
  69. GroupNamesMap.idToName[@group] =
  70. $"{type.FullName}.{field.Name} {@group.id})";
  71. #endif
  72. //The hashname is independent from the actual group ID. this is fundamental because it is want
  73. //guarantees the hash to be the same across different machines
  74. RegisterGroup(@group, $"{type.FullName}.{field.Name}");
  75. }
  76. }
  77. }
  78. }
  79. }
  80. }
  81. }
  82. static void CheckForGroupCompounds(Type type)
  83. {
  84. if (typeof(ITouchedByReflection).IsAssignableFrom(type))
  85. {
  86. //this calls the static constructor, but only once. Static constructors won't be called
  87. //more than once with this
  88. CallStaticConstructorsRecursively(type);
  89. }
  90. static void CallStaticConstructorsRecursively(Type type)
  91. {
  92. // Check if the current type has a static constructor
  93. // type.TypeInitializer.Invoke(null, null); //calling Invoke will force the static constructor to be called even if already called, this is a problem because GroupTag and Compound throw an exception if called multiple times
  94. RuntimeHelpers.RunClassConstructor(type.TypeHandle); //this will call the static constructor only once
  95. #if DEBUG && !PROFILE_SVELTO
  96. if (type.GetInterfaces().Contains(type) == false)
  97. {
  98. if (type.IsSealed == false)
  99. Svelto.Console.LogWarning(
  100. $"Group compound/tag {type} is not sealed. GroupCompounds and Tags cannot be inherited, consider marking it sealed");
  101. }
  102. #endif
  103. // Recursively check the base types
  104. Type baseType = type.BaseType;
  105. if (baseType != null && baseType != typeof(object)) //second if means we got the the end of the hierarchy
  106. {
  107. CallStaticConstructorsRecursively(baseType);
  108. }
  109. }
  110. }
  111. /// <summary>
  112. /// The hashname is independent from the actual group ID. this is fundamental because it is want
  113. /// guarantees the hash to be the same across different machines
  114. /// </summary>
  115. /// <param name="exclusiveGroupStruct"></param>
  116. /// <param name="name"></param>
  117. /// <exception cref="ECSException"></exception>
  118. internal static void RegisterGroup(ExclusiveGroupStruct exclusiveGroupStruct, string name)
  119. {
  120. //Group already registered by another field referencing the same group, can happen because
  121. //the group poked is a group compound which static constructor is already been called at this point
  122. if (_hashByGroups.ContainsKey(exclusiveGroupStruct))
  123. return;
  124. var nameHash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(name));
  125. if (_groupsByHash.ContainsKey(nameHash))
  126. throw new ECSException($"Group hash collision with {name} and {_groupsByHash[nameHash]}");
  127. Console.LogDebug($"Registering group {name} with ID {exclusiveGroupStruct.id} to {nameHash}");
  128. _groupsByHash.Add(nameHash, exclusiveGroupStruct);
  129. _hashByGroups.Add(exclusiveGroupStruct, nameHash);
  130. }
  131. public static uint GetHashFromGroup(ExclusiveGroupStruct groupStruct)
  132. {
  133. #if DEBUG && !PROFILE_SVELTO
  134. if (_hashByGroups.ContainsKey(groupStruct) == false)
  135. throw new ECSException($"Attempted to get hash from unregistered group {groupStruct}");
  136. #endif
  137. return _hashByGroups[groupStruct];
  138. }
  139. public static ExclusiveGroupStruct GetGroupFromHash(uint groupHash)
  140. {
  141. #if DEBUG && !PROFILE_SVELTO
  142. if (_groupsByHash.ContainsKey(groupHash) == false)
  143. throw new ECSException($"Attempted to get group from unregistered hash {groupHash}");
  144. #endif
  145. return _groupsByHash[groupHash];
  146. }
  147. static readonly Dictionary<uint, ExclusiveGroupStruct> _groupsByHash;
  148. static readonly Dictionary<ExclusiveGroupStruct, uint> _hashByGroups;
  149. static GroupHashMap()
  150. {
  151. _groupsByHash = new Dictionary<uint, ExclusiveGroupStruct>();
  152. _hashByGroups = new Dictionary<ExclusiveGroupStruct, uint>();
  153. }
  154. }
  155. }