using System; using System.Collections.Generic; using System.Threading; using Svelto.DataStructures; namespace Svelto.ECS { /// /// This mechanism is not for thread-safety but to be sure that all the permutations of group tags always /// point to the same group ID. /// A group compound can generate several permutation of tags, so that the order of the tag doesn't matter, /// but for this to work, each permutation must be identified by the same ID (generated by the unique combination) /// static class GroupCompoundInitializer { internal static readonly ThreadLocal skipStaticCompoundConstructorsWith4Tags = new ThreadLocal(); internal static readonly ThreadLocal skipStaticCompoundConstructorsWith3Tags = new ThreadLocal(); internal static readonly ThreadLocal skipStaticCompoundConstructorsWith2Tags = new ThreadLocal(); } public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag where G3 : GroupTag where G4 : GroupTag { static readonly FasterList _Groups; static readonly HashSet _GroupsHashSet; public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); static int isInitializing; static GroupCompound() { //avoid race conditions if compounds are using on multiple thread if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false) { _Groups = new FasterList(1); var group = new ExclusiveGroup(); _Groups.Add(group); var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint) group}"; #if DEBUG GroupNamesMap.idToName[(uint) group] = name; #endif GroupHashMap.RegisterGroup(group, name); _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true; //all the permutations must share the same group and group hashset. Warm them up, avoid call the //constructors again, set the desired value GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; //all the permutations are warmed up now GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = false; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound.Add(group); GroupCompound.Add(group); GroupCompound.Add(group); GroupCompound.Add(group); GroupCompound.Add(group); // and must share the same array GroupCompound.Add(group); GroupCompound.Add(group); GroupCompound.Add(group); GroupCompound.Add(group); GroupCompound.Add(group); //This is done here to be sure that the group is added once per group tag //(if done inside the previous group compound it would be added multiple times) GroupTag.Add(group); GroupTag.Add(group); GroupTag.Add(group); GroupTag.Add(group); } } internal static void Add(ExclusiveGroupStruct @group) { for (int i = 0; i < _Groups.count; ++i) if (_Groups[i] == group) throw new Exception("temporary must be transformed in unit test"); _Groups.Add(group); _GroupsHashSet.Add(group); } public static bool Includes(ExclusiveGroupStruct @group) { return _GroupsHashSet.Contains(@group); } } public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag where G3 : GroupTag { static readonly FasterList _Groups; static readonly HashSet _GroupsHashSet; public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); static int isInitializing; internal static void Add(ExclusiveGroupStruct group) { for (var i = 0; i < _Groups.count; ++i) if (_Groups[i] == group) throw new Exception("temporary must be transformed in unit test"); _Groups.Add(group); _GroupsHashSet.Add(group); } public static bool Includes(ExclusiveGroupStruct @group) { return _GroupsHashSet.Contains(@group); } static GroupCompound() { if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false) { _Groups = new FasterList(1); var group = new ExclusiveGroup(); _Groups.Add(group); var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint) group}"; #if DEBUG GroupNamesMap.idToName[(uint) group] = name; #endif GroupHashMap.RegisterGroup(group, name); _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true; //all the combinations must share the same group and group hashset GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; GroupCompound._Groups = _Groups; //all the constructor have been called now GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = false; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound._GroupsHashSet = _GroupsHashSet; GroupCompound.Add(group); // and must share the same array GroupCompound.Add(group); GroupCompound.Add(group); //This is done here to be sure that the group is added once per group tag //(if done inside the previous group compound it would be added multiple times) GroupTag.Add(group); GroupTag.Add(group); GroupTag.Add(group); } } } public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag { static readonly FasterList _Groups; static readonly HashSet _GroupsHashSet; public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); static int isInitializing; internal static void Add(ExclusiveGroupStruct group) { for (var i = 0; i < _Groups.count; ++i) if (_Groups[i] == group) throw new Exception("temporary must be transformed in unit test"); _Groups.Add(group); _GroupsHashSet.Add(group); } public static bool Includes(ExclusiveGroupStruct @group) { return _GroupsHashSet.Contains(@group); } static GroupCompound() { if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false) { var group = new ExclusiveGroup(); _Groups = new FasterList(1); _Groups.Add(group); var groupName = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {(uint) group}"; #if DEBUG GroupNamesMap.idToName[(uint) group] = groupName; #endif GroupHashMap.RegisterGroup(group, groupName); _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true; GroupCompound._Groups = _Groups; GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false; GroupCompound._GroupsHashSet = _GroupsHashSet; //every abstract group preemptively adds this group, it may or may not be empty in future GroupTag.Add(group); GroupTag.Add(group); } } } /// ///A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of ///combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities ///can use the same adjective together with other ones. However since I need to be able to iterate over all the ///groups with the same adjective, a group tag needs to hold all the groups sharing it. /// /// public abstract class GroupTag where T : GroupTag { static readonly FasterList _Groups = new FasterList(1); static readonly HashSet _GroupsHashSet; public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); static int isInitializing; static GroupTag() { if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0) { var group = new ExclusiveGroup(); _Groups.Add(group); var typeInfo = typeof(T); var name = $"Compound: {typeInfo.Name} ID {(uint) @group}"; #if DEBUG var typeInfoBaseType = typeInfo.BaseType; if (typeInfoBaseType.GenericTypeArguments[0] != typeInfo) throw new ECSException("Invalid Group Tag declared"); GroupNamesMap.idToName[(uint) group] = name; #endif GroupHashMap.RegisterGroup(group, name); _GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _)); } } //Each time a new combination of group tags is found a new group is added. internal static void Add(ExclusiveGroupStruct group) { for (var i = 0; i < _Groups.count; ++i) if (_Groups[i] == group) throw new Exception("temporary must be transformed in unit test"); _Groups.Add(group); _GroupsHashSet.Add(group); } public static bool Includes(ExclusiveGroupStruct @group) { return _GroupsHashSet.Contains(@group); } } }