using System; using System.Collections.Generic; using System.Runtime.CompilerServices; #pragma warning disable 660,661 namespace Svelto.ECS { /// /// Exclusive Groups guarantee that the GroupID is unique. /// /// The best way to use it is like: /// /// public static class MyExclusiveGroups //(can be as many as you want) /// { /// public static ExclusiveGroup MyExclusiveGroup1 = new ExclusiveGroup(); /// /// public static ExclusiveGroup[] GroupOfGroups = { MyExclusiveGroup1, ...}; //for each on this! /// } /// ///To debug it use in your debug window: Svelto.ECS.Debugger.EGID.GetGroupNameFromId(groupID) public class ExclusiveGroup { public const uint MaxNumberOfExclusiveGroups = 2 << 20; public ExclusiveGroup() { _group = ExclusiveGroupStruct.Generate(); } public ExclusiveGroup(string recognizeAs) { _group = ExclusiveGroupStruct.Generate(); _knownGroups.Add(recognizeAs, _group); } public ExclusiveGroup(ushort range) { _group = new ExclusiveGroupStruct(range); #if DEBUG _range = range; #endif } public static implicit operator ExclusiveGroupStruct(ExclusiveGroup group) { return group._group; } public static explicit operator uint(ExclusiveGroup group) { return group._group; } public static ExclusiveGroupStruct operator+(ExclusiveGroup a, uint b) { #if DEBUG if (a._range == 0) throw new ECSException($"Adding values to a not ranged ExclusiveGroup: {(uint)a}"); if (b >= a._range) throw new ECSException($"Using out of range group: {(uint)a} + {b}"); #endif return a._group + b; } //todo document the use case for this method public static ExclusiveGroupStruct Search(string holderGroupName) { if (_knownGroups.ContainsKey(holderGroupName) == false) throw new Exception("Named Group Not Found ".FastConcat(holderGroupName)); return _knownGroups[holderGroupName]; } public override string ToString() { return _group.ToString(); } static readonly Dictionary _knownGroups = new Dictionary(); #if DEBUG readonly ushort _range; #endif readonly ExclusiveGroupStruct _group; } } #if future public static void ConstructStaticGroups() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); // Assemblies or types aren't guaranteed to be returned in the same order, // and I couldn't find proof that `GetTypes()` returns them in fixed order either, // even for builds made with the exact same source code. // So will sort reflection results by name before constructing groups. var groupFields = new List>(); foreach (Assembly assembly in assemblies) { Type[] types = GetTypesSafe(assembly); foreach (Type type in types) { if (type == null || !type.IsClass) { continue; } // Groups defined as static members in static classes if (type.IsSealed && type.IsAbstract) { FieldInfo[] fields = type.GetFields(); foreach(var field in fields) { if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType)) { groupFields.Add(new KeyValuePair($"{type.FullName}.{field.Name}", field)); } } } // Groups defined as classes else if (type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(ExclusiveGroup<>)) { FieldInfo field = type.GetField("Group", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); groupFields.Add(new KeyValuePair(type.FullName, field)); } } } groupFields.Sort((a, b) => string.CompareOrdinal(a.Key, b.Key)); for (int i = 0; i < groupFields.Count; ++i) { groupFields[i].Value.GetValue(null); #if DEBUG var group = (ExclusiveGroup) groupFields[i].Value.GetValue(null); groupNames[(uint) group] = groupFields[i].Key; #endif } } static Type[] GetTypesSafe(Assembly assembly) { try { Type[] types = assembly.GetTypes(); return types; } catch (ReflectionTypeLoadException e) { return e.Types; } } #if DEBUG static string[] groupNames = new string[ExclusiveGroup.MaxNumberOfExclusiveGroups]; #endif #endif