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.

157 lines
7.1KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Text;
  5. using Svelto.ECS.Serialization;
  6. namespace Svelto.ECS
  7. {
  8. public static class GroupHashMap
  9. {
  10. /// <summary>
  11. /// c# Static constructors are guaranteed to be thread safe
  12. /// </summary>
  13. internal static void Init()
  14. {
  15. List<Assembly> assemblies = AssemblyUtility.GetCompatibleAssemblies();
  16. foreach (Assembly assembly in assemblies)
  17. {
  18. try
  19. {
  20. var typeOfExclusiveGroup = typeof(ExclusiveGroup);
  21. var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct);
  22. var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup);
  23. foreach (Type type in AssemblyUtility.GetTypesSafe(assembly))
  24. {
  25. CheckForGroupCompounds(type);
  26. if (type != null && type.IsClass && type.IsSealed &&
  27. type.IsAbstract) //IsClass and IsSealed and IsAbstract means only static classes
  28. {
  29. var subClasses = type.GetNestedTypes();
  30. foreach (var subclass in subClasses)
  31. {
  32. CheckForGroupCompounds(subclass);
  33. }
  34. var fields = type.GetFields();
  35. foreach (var field in fields)
  36. {
  37. if (field.IsStatic
  38. && (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)
  39. || typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)
  40. || typeOfExclusiveBuildGroup.IsAssignableFrom(field.FieldType)))
  41. {
  42. uint groupIDAndBitMask;
  43. if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType))
  44. {
  45. var group = (ExclusiveGroup)field.GetValue(null);
  46. groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
  47. }
  48. else if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType))
  49. {
  50. var group = (ExclusiveGroupStruct)field.GetValue(null);
  51. groupIDAndBitMask = @group.ToIDAndBitmask();
  52. }
  53. else
  54. {
  55. var group = (ExclusiveBuildGroup)field.GetValue(null);
  56. groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
  57. }
  58. {
  59. var bitMask = (byte)(groupIDAndBitMask >> 24);
  60. var groupID = groupIDAndBitMask & 0xFFFFFF;
  61. ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID, bitMask);
  62. #if DEBUG && !PROFILE_SVELTO
  63. if (GroupNamesMap.idToName.ContainsKey(@group) == false)
  64. GroupNamesMap.idToName[@group] =
  65. $"{type.FullName}.{field.Name} {@group.id})";
  66. #endif
  67. //The hashname is independent from the actual group ID. this is fundamental because it is want
  68. //guarantees the hash to be the same across different machines
  69. RegisterGroup(@group, $"{type.FullName}.{field.Name}");
  70. }
  71. }
  72. }
  73. }
  74. }
  75. }
  76. catch
  77. {
  78. Console.LogDebugWarning(
  79. "something went wrong while gathering group names on the assembly: ".FastConcat(
  80. assembly.FullName));
  81. }
  82. }
  83. }
  84. static void CheckForGroupCompounds(Type type)
  85. {
  86. if (typeof(ITouchedByReflection).IsAssignableFrom(type))
  87. {
  88. //this wil call the static constructor, but only once. Static constructors won't be called
  89. //more than once with this
  90. System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.BaseType.TypeHandle);
  91. }
  92. }
  93. /// <summary>
  94. /// The hashname is independent from the actual group ID. this is fundamental because it is want
  95. /// guarantees the hash to be the same across different machines
  96. /// </summary>
  97. /// <param name="exclusiveGroupStruct"></param>
  98. /// <param name="name"></param>
  99. /// <exception cref="ECSException"></exception>
  100. public static void RegisterGroup(ExclusiveGroupStruct exclusiveGroupStruct, string name)
  101. {
  102. //Group already registered by another field referencing the same group, can happen because
  103. //the group poked is a group compound which static constructor is already been called at this point
  104. if (_hashByGroups.ContainsKey(exclusiveGroupStruct))
  105. return;
  106. var nameHash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(name));
  107. if (_groupsByHash.ContainsKey(nameHash))
  108. throw new ECSException($"Group hash collision with {name} and {_groupsByHash[nameHash]}");
  109. Console.LogDebug($"Registering group {name} with ID {exclusiveGroupStruct.id} to {nameHash}");
  110. _groupsByHash.Add(nameHash, exclusiveGroupStruct);
  111. _hashByGroups.Add(exclusiveGroupStruct, nameHash);
  112. }
  113. internal static uint GetHashFromGroup(ExclusiveGroupStruct groupStruct)
  114. {
  115. #if DEBUG && !PROFILE_SVELTO
  116. if (_hashByGroups.ContainsKey(groupStruct) == false)
  117. throw new ECSException($"Attempted to get hash from unregistered group {groupStruct}");
  118. #endif
  119. return _hashByGroups[groupStruct];
  120. }
  121. internal static ExclusiveGroupStruct GetGroupFromHash(uint groupHash)
  122. {
  123. #if DEBUG && !PROFILE_SVELTO
  124. if (_groupsByHash.ContainsKey(groupHash) == false)
  125. throw new ECSException($"Attempted to get group from unregistered hash {groupHash}");
  126. #endif
  127. return _groupsByHash[groupHash];
  128. }
  129. static readonly Dictionary<uint, ExclusiveGroupStruct> _groupsByHash;
  130. static readonly Dictionary<ExclusiveGroupStruct, uint> _hashByGroups;
  131. static GroupHashMap()
  132. {
  133. _groupsByHash = new Dictionary<uint, ExclusiveGroupStruct>();
  134. _hashByGroups = new Dictionary<ExclusiveGroupStruct, uint>();
  135. }
  136. }
  137. }