using System; using System.Collections.Generic; #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! /// } /// /// ///use this like: /// public class TriggersGroup : ExclusiveGroup {} public abstract class NamedExclusiveGroup:ExclusiveGroup { public static ExclusiveGroup Group = new ExclusiveGroup(); public static string name = typeof(T).FullName; public NamedExclusiveGroup() { } public NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {} public NamedExclusiveGroup(ushort range) : base(range) {} } public class ExclusiveGroup { public ExclusiveGroup() { _group = ExclusiveGroupStruct.Generate(); } public ExclusiveGroup(string recognizeAs) { _group = ExclusiveGroupStruct.Generate(); _serialisedGroups.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"); if (b >= a._range) throw new ECSException("Using out of range group"); #endif return a._group + b; } readonly ExclusiveGroupStruct _group; //I use this as parameter because it must not be possible to pass null Exclusive Groups. public struct ExclusiveGroupStruct : IEquatable, IComparable, IEqualityComparer { public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2) { return c1.Equals(c2); } public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2) { return c1.Equals(c2) == false; } public bool Equals(ExclusiveGroupStruct other) { return other._id == _id; } public int CompareTo(ExclusiveGroupStruct other) { return other._id.CompareTo(_id); } public bool Equals(ExclusiveGroupStruct x, ExclusiveGroupStruct y) { return x._id == y._id; } public int GetHashCode(ExclusiveGroupStruct obj) { return _id.GetHashCode(); } internal static ExclusiveGroupStruct Generate() { ExclusiveGroupStruct groupStruct; groupStruct._id = _globalId; DBC.ECS.Check.Require(_globalId + 1 < ushort.MaxValue, "too many exclusive groups created"); _globalId++; return groupStruct; } /// /// Use this constructor to reserve N groups /// internal ExclusiveGroupStruct(ushort range) { _id = _globalId; DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created"); _globalId += range; } internal ExclusiveGroupStruct(uint groupID) { _id = groupID; } public ExclusiveGroupStruct(byte[] data, uint pos) { _id = (uint)( data[pos++] | data[pos++] << 8 | data[pos++] << 16 | data[pos++] << 24 ); DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased"); } public static implicit operator uint(ExclusiveGroupStruct groupStruct) { return groupStruct._id; } public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b) { var group = new ExclusiveGroupStruct(); group._id = a._id + b; return group; } uint _id; static uint _globalId; } public static ExclusiveGroupStruct Search(string holderGroupName) { if (_serialisedGroups.ContainsKey(holderGroupName) == false) throw new Exception("Named Group Not Found ".FastConcat(holderGroupName)); return _serialisedGroups[holderGroupName]; } static readonly Dictionary _serialisedGroups = new Dictionary(); #if DEBUG readonly ushort _range; #endif } } #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[ushort.MaxValue]; #endif #endif