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();
}
interface ITouchedByReflection
{
}
public abstract class GroupCompound : ITouchedByReflection where G1 : GroupTag
where G2 : GroupTag
where G3 : GroupTag
where G4 : GroupTag
{
static GroupCompound()
{
//avoid race conditions if compounds are using on multiple thread
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false)
{
var
group =
new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor
_Groups = new FasterList(1);
_Groups.Add(group);
#if DEBUG && !PROFILE_SVELTO
var name =
$"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)group.id}";
GroupNamesMap.idToName[group] = name;
#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
GroupHashMap.RegisterGroup(group, typeof(GroupCompound).FullName);
_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);
}
}
public static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static bool Includes(ExclusiveGroupStruct group)
{
return _GroupsHashSet.Contains(group);
}
internal static void Add(ExclusiveGroupStruct group)
{
#if DEBUG && !PROFILE_SVELTO
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new System.Exception("this test must be transformed in unit test");
#endif
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
static readonly FasterList _Groups;
static readonly HashSet _GroupsHashSet;
//we are changing this with Interlocked, so it cannot be readonly
static int isInitializing;
}
public abstract class GroupCompound : ITouchedByReflection where G1 : GroupTag
where G2 : GroupTag
where G3 : GroupTag
{
static GroupCompound()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false)
{
var
group =
new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor
_Groups = new FasterList(1);
_Groups.Add(group);
#if DEBUG && !PROFILE_SVELTO
var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint)group.id}";
GroupNamesMap.idToName[group] = name;
#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
GroupHashMap.RegisterGroup(group, typeof(GroupCompound).FullName);
_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 static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static bool Includes(ExclusiveGroupStruct group)
{
return _GroupsHashSet.Contains(group);
}
internal static void Add(ExclusiveGroupStruct group)
{
#if DEBUG && !PROFILE_SVELTO
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new System.Exception("this test must be transformed in unit test");
#endif
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
static readonly FasterList _Groups;
static readonly HashSet _GroupsHashSet;
//we are changing this with Interlocked, so it cannot be readonly
static int isInitializing;
}
public abstract class GroupCompound : ITouchedByReflection where G1 : GroupTag where G2 : GroupTag
{
static GroupCompound()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false)
{
var
group =
new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor
_Groups = new FasterList(1);
_Groups.Add(group);
#if DEBUG && !PROFILE_SVELTO
GroupNamesMap.idToName[group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {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
GroupHashMap.RegisterGroup(group, typeof(GroupCompound).FullName);
_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);
}
}
public static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static bool Includes(ExclusiveGroupStruct group)
{
return _GroupsHashSet.Contains(group);
}
internal static void Add(ExclusiveGroupStruct group)
{
#if DEBUG && !PROFILE_SVELTO
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new System.Exception("this test must be transformed in unit test");
#endif
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
static readonly FasterList _Groups;
static readonly HashSet _GroupsHashSet;
static int isInitializing;
}
///
/// 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 : ITouchedByReflection where T : GroupTag
{
static GroupTag()
{
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0)
{
var group = new ExclusiveGroup();
_Groups.Add(group);
#if DEBUG && !PROFILE_SVELTO
var typeInfo = typeof(T);
var name = $"Compound: {typeInfo.Name} ID {(uint)group.id}";
var typeInfoBaseType = typeInfo.BaseType;
if (typeInfoBaseType.GenericTypeArguments[0] !=
typeInfo) //todo: this should shield from using a pattern different than public class GROUP_NAME : GroupTag {} however I am not sure it's working
throw new ECSException("Invalid Group Tag declared");
GroupNamesMap.idToName[group] = name;
#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
GroupHashMap.RegisterGroup(group, typeof(GroupTag).FullName);
_GroupsHashSet = new HashSet(_Groups.ToArrayFast(out _));
}
}
public static FasterReadOnlyList Groups =>
new FasterReadOnlyList(_Groups);
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
public static bool Includes(ExclusiveGroupStruct group)
{
return _GroupsHashSet.Contains(group);
}
//Each time a new combination of group tags is found a new group is added.
internal static void Add(ExclusiveGroupStruct group)
{
#if DEBUG && !PROFILE_SVELTO
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new System.Exception("this test must be transformed in unit test");
#endif
_Groups.Add(group);
_GroupsHashSet.Add(group);
}
static readonly FasterList _Groups = new FasterList(1);
static readonly HashSet _GroupsHashSet;
//we are changing this with Interlocked, so it cannot be readonly
static int isInitializing;
}
}