using System; using System.Collections.Generic; using System.Reflection; using System.Text; using Svelto.ECS.Serialization; namespace Svelto.ECS { public static class GroupHashMap { /// /// c# Static constructors are guaranteed to be thread safe /// internal static void Init() { List assemblies = AssemblyUtility.GetCompatibleAssemblies(); foreach (Assembly assembly in assemblies) { try { var typeOfExclusiveGroup = typeof(ExclusiveGroup); var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct); var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup); foreach (Type type in AssemblyUtility.GetTypesSafe(assembly)) { CheckForGroupCompounds(type); if (type != null && type.IsClass && type.IsSealed && type.IsAbstract) //IsClass and IsSealed and IsAbstract means only static classes { var subClasses = type.GetNestedTypes(); foreach (var subclass in subClasses) { CheckForGroupCompounds(subclass); } var fields = type.GetFields(); foreach (var field in fields) { if (field.IsStatic && (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType) || typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType) || typeOfExclusiveBuildGroup.IsAssignableFrom(field.FieldType))) { uint groupIDAndBitMask; if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)) { var group = (ExclusiveGroup)field.GetValue(null); groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask(); } else if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)) { var group = (ExclusiveGroupStruct)field.GetValue(null); groupIDAndBitMask = @group.ToIDAndBitmask(); } else { var group = (ExclusiveBuildGroup)field.GetValue(null); groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask(); } { var bitMask = (byte)(groupIDAndBitMask >> 24); var groupID = groupIDAndBitMask & 0xFFFFFF; ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID, bitMask); #if DEBUG && !PROFILE_SVELTO if (GroupNamesMap.idToName.ContainsKey(@group) == false) GroupNamesMap.idToName[@group] = $"{type.FullName}.{field.Name} {@group.id})"; #endif //The hashname is independent from the actual group ID. this is fundamental because it is want //guarantees the hash to be the same across different machines RegisterGroup(@group, $"{type.FullName}.{field.Name}"); } } } } } } catch { Console.LogDebugWarning( "something went wrong while gathering group names on the assembly: ".FastConcat( assembly.FullName)); } } } static void CheckForGroupCompounds(Type type) { if (typeof(ITouchedByReflection).IsAssignableFrom(type)) { //this wil call the static constructor, but only once. Static constructors won't be called //more than once with this System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(type.BaseType.TypeHandle); } } /// /// The hashname is independent from the actual group ID. this is fundamental because it is want /// guarantees the hash to be the same across different machines /// /// /// /// public static void RegisterGroup(ExclusiveGroupStruct exclusiveGroupStruct, string name) { //Group already registered by another field referencing the same group, can happen because //the group poked is a group compound which static constructor is already been called at this point if (_hashByGroups.ContainsKey(exclusiveGroupStruct)) return; var nameHash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(name)); if (_groupsByHash.ContainsKey(nameHash)) throw new ECSException($"Group hash collision with {name} and {_groupsByHash[nameHash]}"); Console.LogDebug($"Registering group {name} with ID {exclusiveGroupStruct.id} to {nameHash}"); _groupsByHash.Add(nameHash, exclusiveGroupStruct); _hashByGroups.Add(exclusiveGroupStruct, nameHash); } internal static uint GetHashFromGroup(ExclusiveGroupStruct groupStruct) { #if DEBUG && !PROFILE_SVELTO if (_hashByGroups.ContainsKey(groupStruct) == false) throw new ECSException($"Attempted to get hash from unregistered group {groupStruct}"); #endif return _hashByGroups[groupStruct]; } internal static ExclusiveGroupStruct GetGroupFromHash(uint groupHash) { #if DEBUG && !PROFILE_SVELTO if (_groupsByHash.ContainsKey(groupHash) == false) throw new ECSException($"Attempted to get group from unregistered hash {groupHash}"); #endif return _groupsByHash[groupHash]; } static readonly Dictionary _groupsByHash; static readonly Dictionary _hashByGroups; static GroupHashMap() { _groupsByHash = new Dictionary(); _hashByGroups = new Dictionary(); } } }