update masks

This commit is contained in:
Mikhail 2024-10-31 14:46:21 +08:00
parent 7af468d4c5
commit 2e121381f7
7 changed files with 548 additions and 476 deletions

View File

@ -1,7 +1,6 @@
using DCFApixels.DragonECS.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
using System.Collections.Generic;
namespace DCFApixels.DragonECS
{
@ -9,62 +8,31 @@ namespace DCFApixels.DragonECS
{
#region Initialization Halpers
[ThreadStatic]
private static Stack<Builder> _constructorBuildersStack = null;
private static Stack<Builder> GetBuildersStack()
{
if (_constructorBuildersStack == null)
{
_constructorBuildersStack = new Stack<Builder>();
}
return _constructorBuildersStack;
}
private static Builder[] _constructorBuildersStack;
[ThreadStatic]
private static int _constructorBuildersStackIndex;
protected static Builder CurrentBuilder
{
get
{
var buildersStack = GetBuildersStack();
if (buildersStack.Count <= 0)
if (_constructorBuildersStack == null || _constructorBuildersStackIndex < 0)
{
Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(CurrentBuilder));
}
return buildersStack.Peek();
return _constructorBuildersStack[_constructorBuildersStackIndex];
}
}
protected static IncludeMarker Inc
{
get
{
var buildersStack = GetBuildersStack();
if (buildersStack.Count <= 0)
{
Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(Inc));
}
return buildersStack.Peek().Inc;
}
get { return CurrentBuilder.Inc; }
}
protected static ExcludeMarker Exc
{
get
{
var buildersStack = GetBuildersStack();
if (buildersStack.Count <= 0)
{
Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(Exc));
}
return buildersStack.Peek().Exc;
}
get { return CurrentBuilder.Exc; }
}
protected static OptionalMarker Opt
{
get
{
var buildersStack = GetBuildersStack();
if (buildersStack.Count <= 0)
{
Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(Opt));
}
return buildersStack.Peek().Opt;
}
get { return CurrentBuilder.Opt; }
}
#endregion
@ -99,7 +67,7 @@ namespace DCFApixels.DragonECS
public sealed class Builder
{
private EcsWorld _world;
private EcsMask.Builder _maskBuilder;
private EcsStaticMask.Builder _maskBuilder;
#region Properties
public IncludeMarker Inc
@ -121,29 +89,42 @@ namespace DCFApixels.DragonECS
#endregion
#region Constructors/New
private Builder(EcsWorld world)
private Builder() { }
private void Reset(EcsWorld world)
{
_maskBuilder = EcsStaticMask.New();
_world = world;
_maskBuilder = EcsMask.New(world);
}
internal static unsafe TAspect New<TAspect>(EcsWorld world) where TAspect : EcsAspect, new()
{
Builder builder = new Builder(world);
Type aspectType = typeof(TAspect);
EcsAspect newAspect;
if (_constructorBuildersStack == null)
{
_constructorBuildersStack = new Builder[4];
_constructorBuildersStackIndex = -1;
}
var buildersStack = GetBuildersStack();
_constructorBuildersStackIndex++;
if (_constructorBuildersStackIndex >= _constructorBuildersStack.Length)
{
Array.Resize(ref _constructorBuildersStack, _constructorBuildersStack.Length << 1);
}
Builder builder = _constructorBuildersStack[_constructorBuildersStackIndex];
if (builder == null)
{
builder = new Builder();
_constructorBuildersStack[_constructorBuildersStackIndex] = builder;
}
builder.Reset(world);
buildersStack.Push(builder);
newAspect = new TAspect();
TAspect newAspect = new TAspect();
newAspect.Init(builder);
buildersStack.Pop();
_constructorBuildersStackIndex--;
newAspect._source = world;
builder.Build(out newAspect._mask);
newAspect._isBuilt = true;
return (TAspect)newAspect;
return newAspect;
}
#endregion
@ -177,13 +158,13 @@ namespace DCFApixels.DragonECS
public TOtherAspect Combine<TOtherAspect>(int order = 0) where TOtherAspect : EcsAspect, new()
{
var result = _world.GetAspect<TOtherAspect>();
_maskBuilder.Combine(result.Mask);
_maskBuilder.Combine(result.Mask._staticMask);
return result;
}
public TOtherAspect Except<TOtherAspect>(int order = 0) where TOtherAspect : EcsAspect, new()
{
var result = _world.GetAspect<TOtherAspect>();
_maskBuilder.Except(result.Mask);
_maskBuilder.Except(result.Mask._staticMask);
return result;
}
#endregion
@ -191,7 +172,7 @@ namespace DCFApixels.DragonECS
#region Build
private void Build(out EcsMask mask)
{
mask = _maskBuilder.Build();
mask = _maskBuilder.Build().ToMask(_world);
}
#endregion

View File

@ -1,6 +1,5 @@
using DCFApixels.DragonECS.Internal;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@ -16,176 +15,6 @@ namespace DCFApixels.DragonECS
{
EcsMask ToMask(EcsWorld world);
}
#if ENABLE_IL2CPP
[Il2CppSetOption (Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
public sealed class EcsStaticMask : IEquatable<EcsStaticMask>, IEcsComponentMask
{
private static ConcurrentDictionary<Key, EcsStaticMask> _ids = new ConcurrentDictionary<Key, EcsStaticMask>();
private static IdDispenser _idDIspenser = new IdDispenser(nullID: 0);
private static object _lock = new object();
private readonly int _id;
/// <summary> Sorted </summary>
private readonly EcsTypeCode[] _inc;
/// <summary> Sorted </summary>
private readonly EcsTypeCode[] _exc;
#region Properties
public int ID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _id; }
}
/// <summary> Sorted set including constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> IncTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _inc; }
}
/// <summary> Sorted set excluding constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> ExcTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _exc; }
}
#endregion
#region Constrcutors
public static Builder New() { return new Builder(); }
public static Builder Inc<T>() { return new Builder().Inc<T>(); }
public static Builder Exc<T>() { return new Builder().Exc<T>(); }
private EcsStaticMask(int id, Key key)
{
_id = id;
_inc = key.inc;
_exc = key.exc;
}
#endregion
#region Methods
public EcsMask ToMask(EcsWorld world)
{
return EcsMask.FromStatic(world, this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(EcsStaticMask other) { return _id == other._id; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return _id; }
public override bool Equals(object obj) { return Equals((EcsStaticMask)obj); }
public override string ToString() { return $"s_mask({_id})"; }
#endregion
#region Builder
private readonly struct Key : IEquatable<Key>
{
public readonly EcsTypeCode[] inc;
public readonly EcsTypeCode[] exc;
public readonly int hash;
#region Constructors
public Key(EcsTypeCode[] inc, EcsTypeCode[] exc)
{
this.inc = inc;
this.exc = exc;
unchecked
{
hash = inc.Length + exc.Length;
for (int i = 0, iMax = inc.Length; i < iMax; i++)
{
hash = hash * EcsConsts.MAGIC_PRIME + (int)inc[i];
}
for (int i = 0, iMax = exc.Length; i < iMax; i++)
{
hash = hash * EcsConsts.MAGIC_PRIME - (int)exc[i];
}
}
}
#endregion
#region Object
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Key other)
{
if (inc.Length != other.inc.Length) { return false; }
if (exc.Length != other.exc.Length) { return false; }
for (int i = 0; i < inc.Length; i++)
{
if (inc[i] != other.inc[i]) { return false; }
}
for (int i = 0; i < exc.Length; i++)
{
if (exc[i] != other.exc[i]) { return false; }
}
return true;
}
public override bool Equals(object obj) { return Equals((Key)obj); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return hash; }
#endregion
}
public class Builder
{
private readonly HashSet<EcsTypeCode> _inc = new HashSet<EcsTypeCode>();
private readonly HashSet<EcsTypeCode> _exc = new HashSet<EcsTypeCode>();
#region Constrcutors
internal Builder() { }
#endregion
#region Include/Exclude/Combine
public Builder Inc<T>() { return Inc(EcsTypeCodeManager.Get<T>()); }
public Builder Exc<T>() { return Exc(EcsTypeCodeManager.Get<T>()); }
public Builder Inc(Type type) { return Inc(EcsTypeCodeManager.Get(type)); }
public Builder Exc(Type type) { return Exc(EcsTypeCodeManager.Get(type)); }
public Builder Inc(EcsTypeCode typeCode)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); }
#endif
_inc.Add(typeCode);
return this;
}
public Builder Exc(EcsTypeCode typeCode)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); }
#endif
_exc.Add(typeCode);
return this;
}
#endregion
#region Build
public EcsStaticMask Build()
{
HashSet<EcsTypeCode> combinedIncs = _inc;
HashSet<EcsTypeCode> combinedExcs = _exc;
var inc = combinedIncs.ToArray();
Array.Sort(inc);
var exc = combinedExcs.ToArray();
Array.Sort(exc);
var key = new Key(inc, exc);
if (_ids.TryGetValue(key, out EcsStaticMask result) == false)
{
lock (_lock)
{
if (_ids.TryGetValue(key, out result) == false)
{
result = new EcsStaticMask(_idDIspenser.UseFree(), key);
_ids[key] = result;
}
}
}
return result;
}
#endregion
}
#endregion
}
#if ENABLE_IL2CPP
[Il2CppSetOption (Option.NullChecks, false)]
@ -194,6 +23,7 @@ namespace DCFApixels.DragonECS
[DebuggerTypeProxy(typeof(DebuggerProxy))]
public sealed class EcsMask : IEquatable<EcsMask>, IEcsComponentMask
{
internal readonly EcsStaticMask _staticMask;
internal readonly int _id;
internal readonly short _worldID;
internal readonly EcsMaskChunck[] _incChunckMasks;
@ -245,24 +75,19 @@ namespace DCFApixels.DragonECS
#endregion
#region Constructors
[Obsolete("")]//TODO написать новый сопсоб создания
public static Builder New(EcsWorld world) { return new Builder(world); }
internal static EcsMask Create(int id, short worldID, int[] inc, int[] exc)
{
#if DEBUG
CheckConstraints(inc, exc);
#endif
return new EcsMask(id, worldID, inc, exc);
}
internal static EcsMask CreateEmpty(int id, short worldID)
{
return new EcsMask(id, worldID, new int[0], new int[0]);
return new EcsMask(EcsStaticMask.Empty, id, worldID, new int[0], new int[0]);
}
internal static EcsMask CreateBroken(int id, short worldID)
{
return new EcsMask(id, worldID, new int[1] { 1 }, new int[1] { 1 });
return new EcsMask(EcsStaticMask.Broken, id, worldID, new int[1] { 1 }, new int[1] { 1 });
}
private EcsMask(int id, short worldID, int[] inc, int[] exc)
private EcsMask(EcsStaticMask staticMask, int id, short worldID, int[] inc, int[] exc)
{
_staticMask = staticMask;
_id = id;
_inc = inc;
_exc = exc;
@ -415,33 +240,6 @@ namespace DCFApixels.DragonECS
#endregion
#region Debug utils
#if DEBUG
private static HashSet<int> _dummyHashSet = new HashSet<int>();
private static void CheckConstraints(int[] inc, int[] exc)
{
lock (_dummyHashSet)
{
if (CheckRepeats(inc)) { throw new EcsFrameworkException("The values in the Include constraints are repeated."); }
if (CheckRepeats(exc)) { throw new EcsFrameworkException("The values in the Exclude constraints are repeated."); }
_dummyHashSet.Clear();
_dummyHashSet.UnionWith(inc);
if (_dummyHashSet.Overlaps(exc)) { throw new EcsFrameworkException("Conflicting Include and Exclude constraints."); }
}
}
private static bool CheckRepeats(int[] array)
{
_dummyHashSet.Clear();
foreach (var item in array)
{
if (_dummyHashSet.Contains(item))
{
return true;
}
_dummyHashSet.Add(item);
}
return false;
}
#endif
private static string CreateLogString(short worldID, int[] inc, int[] exc)
{
#if (DEBUG && !DISABLE_DEBUG)
@ -558,7 +356,7 @@ namespace DCFApixels.DragonECS
#region StaticMask
public static EcsMask FromStatic(EcsWorld world, EcsStaticMask abstractMask)
{
return world.Get<WorldMaskComponent>().ConvertFromAbstract(abstractMask);
return world.Get<WorldMaskComponent>().ConvertFromStatic(abstractMask);
}
#endregion
@ -566,37 +364,34 @@ namespace DCFApixels.DragonECS
private readonly struct WorldMaskComponent : IEcsWorldComponent<WorldMaskComponent>
{
private readonly EcsWorld _world;
private readonly Dictionary<Key, EcsMask> _masks;
private readonly Dictionary<OpMaskKey, EcsMask> _opMasks;
private readonly SparseArray<EcsMask> _abstractMasks;
private readonly SparseArray<EcsMask> _staticMasks;
public readonly EcsMask EmptyMask;
public readonly EcsMask BrokenMask;
#region Constructor/Destructor
public WorldMaskComponent(EcsWorld world, Dictionary<Key, EcsMask> masks, EcsMask emptyMask, EcsMask brokenMask)
public WorldMaskComponent(EcsWorld world)
{
_world = world;
_masks = masks;
_opMasks = new Dictionary<OpMaskKey, EcsMask>(256);
_abstractMasks = new SparseArray<EcsMask>(256);
EmptyMask = emptyMask;
BrokenMask = brokenMask;
_staticMasks = new SparseArray<EcsMask>(256);
EmptyMask = CreateEmpty(_staticMasks.Count, world.id);
_staticMasks.Add(EmptyMask._staticMask.ID, EmptyMask);
BrokenMask = CreateBroken(_staticMasks.Count, world.id);
_staticMasks.Add(BrokenMask._staticMask.ID, BrokenMask);
}
public void Init(ref WorldMaskComponent component, EcsWorld world)
{
var masks = new Dictionary<Key, EcsMask>(256);
EcsMask emptyMask = CreateEmpty(0, world.id);
EcsMask brokenMask = CreateBroken(1, world.id);
masks.Add(new Key(emptyMask._inc, emptyMask._exc), emptyMask);
masks.Add(new Key(brokenMask._inc, brokenMask._exc), brokenMask);
component = new WorldMaskComponent(world, masks, emptyMask, brokenMask);
component = new WorldMaskComponent(world);
}
public void OnDestroy(ref WorldMaskComponent component, EcsWorld world)
{
component._masks.Clear();
component._opMasks.Clear();
component._abstractMasks.Clear();
component._staticMasks.Clear();
component = default;
}
#endregion
@ -611,7 +406,7 @@ namespace DCFApixels.DragonECS
{
return a.World.Get<WorldMaskComponent>().BrokenMask;
}
result = New(a.World).Combine(a).Combine(b).Build();
result = ConvertFromStatic(EcsStaticMask.New().Combine(a._staticMask).Combine(b._staticMask).Build());
_opMasks.Add(new OpMaskKey(a._id, b._id, operation), result);
}
return result;
@ -625,219 +420,69 @@ namespace DCFApixels.DragonECS
{
return a.World.Get<WorldMaskComponent>().BrokenMask;
}
result = New(a.World).Combine(a).Except(b).Build();
result = ConvertFromStatic(EcsStaticMask.New().Combine(a._staticMask).Except(b._staticMask).Build());
_opMasks.Add(new OpMaskKey(a._id, b._id, operation), result);
}
return result;
}
internal EcsMask ConvertFromAbstract(EcsStaticMask abstractMask)
internal EcsMask ConvertFromStatic(EcsStaticMask staticMask)
{
if (_abstractMasks.TryGetValue(abstractMask.ID, out EcsMask result) == false)
int[] ConvertTypeCodeToComponentTypeID(ReadOnlySpan<EcsTypeCode> from, EcsWorld world)
{
var b = New(_world);
foreach (var typeCode in abstractMask.IncTypeCodes)
int[] to = new int[from.Length];
for (int i = 0; i < to.Length; i++)
{
b.Inc(_world.DeclareOrGetComponentTypeID(typeCode));
to[i] = world.DeclareOrGetComponentTypeID(from[i]);
}
foreach (var typeCode in abstractMask.ExcTypeCodes)
{
b.Exc(_world.DeclareOrGetComponentTypeID(typeCode));
}
result = b.Build();
_abstractMasks.Add(abstractMask.ID, result);
Array.Sort(to);
return to;
}
return result;
}
internal EcsMask GetMask(Key maskKey)
{
if (_masks.TryGetValue(maskKey, out EcsMask result) == false)
if (_staticMasks.TryGetValue(staticMask.ID, out EcsMask result) == false)
{
result = Create(_masks.Count, _world.id, maskKey.inc, maskKey.exc);
_masks.Add(maskKey, result);
int[] incs = ConvertTypeCodeToComponentTypeID(staticMask.IncTypeCodes, _world);
int[] excs = ConvertTypeCodeToComponentTypeID(staticMask.ExcTypeCodes, _world);
result = new EcsMask(staticMask, _staticMasks.Count, _world.id, incs, excs);
_staticMasks.Add(staticMask.ID, result);
}
return result;
}
#endregion
}
private readonly struct Key : IEquatable<Key>
{
public readonly int[] inc;
public readonly int[] exc;
public readonly int hash;
#region Constructors
public Key(int[] inc, int[] exc)
{
this.inc = inc;
this.exc = exc;
unchecked
{
hash = inc.Length + exc.Length;
for (int i = 0, iMax = inc.Length; i < iMax; i++)
{
hash = hash * EcsConsts.MAGIC_PRIME + inc[i];
}
for (int i = 0, iMax = exc.Length; i < iMax; i++)
{
hash = hash * EcsConsts.MAGIC_PRIME - exc[i];
}
}
}
#endregion
#region Object
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Key other)
{
if (inc.Length != other.inc.Length) { return false; }
if (exc.Length != other.exc.Length) { return false; }
for (int i = 0; i < inc.Length; i++)
{
if (inc[i] != other.inc[i]) { return false; }
}
for (int i = 0; i < exc.Length; i++)
{
if (exc[i] != other.exc[i]) { return false; }
}
return true;
}
public override bool Equals(object obj) { return Equals((Key)obj); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return hash; }
#endregion
}
public class Builder
[Obsolete("")]//TODO написать новый сопсоб создания
public struct Builder
{
private readonly EcsStaticMask.Builder _builder;
private readonly EcsWorld _world;
private readonly HashSet<int> _inc = new HashSet<int>();
private readonly HashSet<int> _exc = new HashSet<int>();
private readonly List<Combined> _combineds = new List<Combined>();
private readonly List<Excepted> _excepteds = new List<Excepted>();
#region Constrcutors
internal Builder(EcsWorld world)
public Builder(EcsWorld world)
{
_world = world;
}
#endregion
#region Inc/Exc/Combine
[Obsolete("Use Inc<T>()")] public Builder Include<T>() { return Inc<T>(); }
[Obsolete("Use Exc<T>()")] public Builder Exclude<T>() { return Exc<T>(); }
[Obsolete("Use Inc(type)")] public Builder Include(Type type) { return Inc(type); }
[Obsolete("Use Exc(type)")] public Builder Exclude(Type type) { return Exc(type); }
public Builder Inc<T>() { return Inc(_world.GetComponentTypeID<T>()); }
public Builder Exc<T>() { return Exc(_world.GetComponentTypeID<T>()); }
public Builder Inc(Type type) { return Inc(_world.GetComponentTypeID(type)); }
public Builder Exc(Type type) { return Exc(_world.GetComponentTypeID(type)); }
public Builder Inc(int componentTypeID)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(componentTypeID) || _exc.Contains(componentTypeID)) { Throw.ConstraintIsAlreadyContainedInMask(_world.GetComponentType(componentTypeID)); }
#endif
_inc.Add(componentTypeID);
return this;
}
public Builder Exc(int componentTypeID)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(componentTypeID) || _exc.Contains(componentTypeID)) { Throw.ConstraintIsAlreadyContainedInMask(_world.GetComponentType(componentTypeID)); }
#endif
_exc.Add(componentTypeID);
return this;
_builder = EcsStaticMask.Builder.New();
}
public Builder Combine(EcsMask mask, int order = 0)
{
_combineds.Add(new Combined(mask, order));
return this;
}
public Builder Include<T>() { return Inc<T>(); }
public Builder Exclude<T>() { return Exc<T>(); }
public Builder Include(Type type) { return Inc(type); }
public Builder Exclude(Type type) { return Exc(type); }
public Builder Except(EcsMask mask, int order = 0)
{
_excepteds.Add(new Excepted(mask, order));
return this;
}
#endregion
public Builder Inc<T>() { _builder.Inc<T>(); return this; }
public Builder Exc<T>() { _builder.Exc<T>(); return this; }
public Builder Inc(Type type) { _builder.Inc(type); return this; }
public Builder Exc(Type type) { _builder.Exc(type); return this; }
public Builder Inc(EcsTypeCode typeCode) { _builder.Inc(typeCode); return this; }
public Builder Exc(EcsTypeCode typeCode) { _builder.Exc(typeCode); return this; }
public Builder Combine(EcsMask mask) { _builder.Combine(mask._staticMask); return this; }
public Builder Except(EcsMask mask) { _builder.Except(mask._staticMask); return this; }
#region Build
public EcsMask Build()
{
HashSet<int> combinedInc;
HashSet<int> combinedExc;
if (_combineds.Count > 0)
{
combinedInc = new HashSet<int>();
combinedExc = new HashSet<int>();
_combineds.Sort((a, b) => a.order - b.order);
foreach (var item in _combineds)
{
EcsMask submask = item.mask;
combinedInc.ExceptWith(submask._exc);//удаляю конфликтующие ограничения
combinedExc.ExceptWith(submask._inc);//удаляю конфликтующие ограничения
combinedInc.UnionWith(submask._inc);
combinedExc.UnionWith(submask._exc);
}
combinedInc.ExceptWith(_exc);//удаляю конфликтующие ограничения
combinedExc.ExceptWith(_inc);//удаляю конфликтующие ограничения
combinedInc.UnionWith(_inc);
combinedExc.UnionWith(_exc);
}
else
{
combinedInc = _inc;
combinedExc = _exc;
}
if (_excepteds.Count > 0)
{
foreach (var item in _excepteds)
{
if (combinedInc.Overlaps(item.mask._exc) || combinedExc.Overlaps(item.mask._inc))
{
_combineds.Clear();
_excepteds.Clear();
return _world.Get<WorldMaskComponent>().BrokenMask;
}
combinedInc.ExceptWith(item.mask._inc);
combinedExc.ExceptWith(item.mask._exc);
}
}
public Builder Inc(int componentTypeID) { Inc(_world.GetComponentType(componentTypeID)); return this; }
public Builder Exc(int componentTypeID) { Exc(_world.GetComponentType(componentTypeID)); return this; }
var inc = combinedInc.ToArray();
Array.Sort(inc);
var exc = combinedExc.ToArray();
Array.Sort(exc);
_combineds.Clear();
_excepteds.Clear();
return _world.Get<WorldMaskComponent>().GetMask(new Key(inc, exc));
}
#endregion
}
private readonly struct Combined
{
public readonly EcsMask mask;
public readonly int order;
public Combined(EcsMask mask, int order)
{
this.mask = mask;
this.order = order;
}
}
private readonly struct Excepted
{
public readonly EcsMask mask;
public readonly int order;
public Excepted(EcsMask mask, int order)
{
this.mask = mask;
this.order = order;
}
public EcsMask Build() { return _world.Get<WorldMaskComponent>().ConvertFromStatic(_builder.Build()); }
}
#endregion
}

View File

@ -299,7 +299,7 @@ namespace DCFApixels.DragonECS
#region Build
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
private EcsProfilerMarker _buildBarker = new EcsProfilerMarker("EcsPipeline.Build");
private static EcsProfilerMarker _buildBarker = new EcsProfilerMarker("EcsPipeline.Build");
#endif
public EcsPipeline Build()
{

View File

@ -40,7 +40,7 @@ namespace DCFApixels.DragonECS
private bool _isDestoryed = false;
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
private EcsProfilerMarker _initMarker = new EcsProfilerMarker("EcsPipeline.Init");
private static EcsProfilerMarker _initMarker = new EcsProfilerMarker("EcsPipeline.Init");
#endif
#region Properties
@ -305,16 +305,27 @@ namespace DCFApixels.DragonECS
#region EcsProcess
[DebuggerTypeProxy(typeof(DebuggerProxy))]
public readonly struct EcsProcessRaw : IEnumerable
public readonly struct EcsProcessRaw : IReadOnlyCollection<IEcsProcess>
{
public static readonly EcsProcessRaw Empty = new EcsProcessRaw(Array.Empty<IEcsProcess>());
private readonly Array _systems;
#region Properties
public bool IsNullOrEmpty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _systems == null || _systems.Length <= 0; }
}
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _systems.Length; }
}
int IReadOnlyCollection<IEcsProcess>.Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _systems.Length; }
}
public IEcsProcess this[int index]
{
get { return (IEcsProcess)_systems.GetValue(index); }
@ -330,6 +341,11 @@ namespace DCFApixels.DragonECS
#endregion
#region Enumerator
IEnumerator<IEcsProcess> IEnumerable<IEcsProcess>.GetEnumerator()
{
return ((IEnumerable<IEcsProcess>)(EcsProcess<IEcsProcess>)this).GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return _systems.GetEnumerator();
@ -371,7 +387,7 @@ namespace DCFApixels.DragonECS
public readonly struct EcsProcess<TProcess> : IReadOnlyCollection<TProcess>
where TProcess : IEcsProcess
{
public readonly static EcsProcess<TProcess> Empty = new EcsProcess<TProcess>(Array.Empty<TProcess>());
public static readonly EcsProcess<TProcess> Empty = new EcsProcess<TProcess>(Array.Empty<TProcess>());
private readonly TProcess[] _systems;
#region Properties

419
src/EcsStaticMask.cs Normal file
View File

@ -0,0 +1,419 @@
using DCFApixels.DragonECS.Internal;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
#endif
namespace DCFApixels.DragonECS
{
#if ENABLE_IL2CPP
[Il2CppSetOption (Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
[DebuggerTypeProxy(typeof(DebuggerProxy))]
public sealed class EcsStaticMask : IEquatable<EcsStaticMask>, IEcsComponentMask
{
public static readonly EcsStaticMask Empty;
public static readonly EcsStaticMask Broken;
private static readonly Stack<BuilderInstance> _buildersPool = new Stack<BuilderInstance>();
private static readonly ConcurrentDictionary<Key, EcsStaticMask> _ids = new ConcurrentDictionary<Key, EcsStaticMask>();
private static readonly IdDispenser _idDIspenser = new IdDispenser(nullID: 0);
private static readonly object _lock = new object();
static EcsStaticMask()
{
Empty = CreateMask(new Key(new EcsTypeCode[0], new EcsTypeCode[0]));
Broken = CreateMask(new Key(new EcsTypeCode[1] { (EcsTypeCode)1 }, new EcsTypeCode[1] { (EcsTypeCode)1 }));
}
private readonly int _id;
/// <summary> Sorted </summary>
private readonly EcsTypeCode[] _inc;
/// <summary> Sorted </summary>
private readonly EcsTypeCode[] _exc;
#region Properties
public int ID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _id; }
}
/// <summary> Sorted set including constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> IncTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _inc; }
}
/// <summary> Sorted set excluding constraints presented as global type codes. </summary>
public ReadOnlySpan<EcsTypeCode> ExcTypeCodes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _exc; }
}
public bool IsEmpty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _inc.Length == 0 && _exc.Length == 0; }
}
public bool IsBroken
{
get { return (_inc.Length & _exc.Length) == 1 && _inc[0] == _exc[0]; }
}
#endregion
#region Constrcutors
private static EcsStaticMask CreateMask(Key key)
{
if (_ids.TryGetValue(key, out EcsStaticMask result) == false)
{
lock (_lock)
{
if (_ids.TryGetValue(key, out result) == false)
{
result = new EcsStaticMask(_idDIspenser.UseFree(), key);
_ids[key] = result;
}
}
}
return result;
}
public static Builder New() { return Builder.New(); }
public static Builder Inc<T>() { return Builder.New().Inc<T>(); }
public static Builder Exc<T>() { return Builder.New().Exc<T>(); }
public static Builder Inc(Type type) { return Builder.New().Inc(type); }
public static Builder Exc(Type type) { return Builder.New().Exc(type); }
public static Builder Inc(EcsTypeCode typeCode) { return Builder.New().Inc(typeCode); }
public static Builder Exc(EcsTypeCode typeCode) { return Builder.New().Exc(typeCode); }
private EcsStaticMask(int id, Key key)
{
_id = id;
_inc = key.inc;
_exc = key.exc;
}
#endregion
#region Methods
public EcsMask ToMask(EcsWorld world)
{
return EcsMask.FromStatic(world, this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(EcsStaticMask other) { return _id == other._id; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return _id; }
public override bool Equals(object obj) { return Equals((EcsStaticMask)obj); }
public override string ToString() { return CreateLogString(_inc, _exc); }
#endregion
#region Builder
private readonly struct Key : IEquatable<Key>
{
public readonly EcsTypeCode[] inc;
public readonly EcsTypeCode[] exc;
public readonly int hash;
#region Constructors
public Key(EcsTypeCode[] inc, EcsTypeCode[] exc)
{
this.inc = inc;
this.exc = exc;
unchecked
{
hash = inc.Length + exc.Length;
for (int i = 0, iMax = inc.Length; i < iMax; i++)
{
hash = hash * EcsConsts.MAGIC_PRIME + (int)inc[i];
}
for (int i = 0, iMax = exc.Length; i < iMax; i++)
{
hash = hash * EcsConsts.MAGIC_PRIME - (int)exc[i];
}
}
}
#endregion
#region Object
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Key other)
{
if (inc.Length != other.inc.Length) { return false; }
if (exc.Length != other.exc.Length) { return false; }
for (int i = 0; i < inc.Length; i++)
{
if (inc[i] != other.inc[i]) { return false; }
}
for (int i = 0; i < exc.Length; i++)
{
if (exc[i] != other.exc[i]) { return false; }
}
return true;
}
public override bool Equals(object obj) { return Equals((Key)obj); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return hash; }
#endregion
}
public readonly struct Builder
{
private readonly BuilderInstance _builder;
private readonly int _version;
public static Builder New()
{
lock (_lock)
{
if (_buildersPool.TryPop(out BuilderInstance builderInstance) == false)
{
builderInstance = new BuilderInstance();
}
return new Builder(builderInstance);
}
}
private Builder(BuilderInstance builder)
{
_builder = builder;
_version = builder._version;
}
public Builder Inc<T>() { return Inc(EcsTypeCodeManager.Get<T>()); }
public Builder Exc<T>() { return Exc(EcsTypeCodeManager.Get<T>()); }
public Builder Inc(Type type) { return Inc(EcsTypeCodeManager.Get(type)); }
public Builder Exc(Type type) { return Exc(EcsTypeCodeManager.Get(type)); }
public Builder Inc(EcsTypeCode typeCode)
{
if (_version != _builder._version) { Throw.UndefinedException(); }
_builder.Inc(typeCode);
return this;
}
public Builder Exc(EcsTypeCode typeCode)
{
if (_version != _builder._version) { Throw.UndefinedException(); }
_builder.Exc(typeCode);
return this;
}
public Builder Combine(EcsStaticMask mask)
{
if (_version != _builder._version) { Throw.UndefinedException(); }
_builder.Combine(mask);
return this;
}
public Builder Except(EcsStaticMask mask)
{
if (_version != _builder._version) { Throw.UndefinedException(); }
_builder.Except(mask);
return this;
}
public EcsStaticMask Build()
{
if (_version != _builder._version) { Throw.UndefinedException(); }
lock (_lock)
{
_buildersPool.Push(_builder);
return _builder.Build();
}
}
}
private class BuilderInstance
{
private readonly HashSet<EcsTypeCode> _inc = new HashSet<EcsTypeCode>();
private readonly HashSet<EcsTypeCode> _exc = new HashSet<EcsTypeCode>();
private readonly List<Combined> _combineds = new List<Combined>();
private bool _sortedCombinedChecker = true;
private readonly List<Excepted> _excepteds = new List<Excepted>();
internal int _version;
#region Constrcutors
internal BuilderInstance() { }
#endregion
#region Inc/Exc/Combine/Except
public void Inc(EcsTypeCode typeCode)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); }
#endif
_inc.Add(typeCode);
}
public void Exc(EcsTypeCode typeCode)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); }
#endif
_exc.Add(typeCode);
}
public void Combine(EcsStaticMask mask, int order = 0)
{
if (_sortedCombinedChecker && order != 0)
{
_sortedCombinedChecker = false;
}
_combineds.Add(new Combined(mask, order));
}
public void Except(EcsStaticMask mask, int order = 0)
{
_excepteds.Add(new Excepted(mask, order));
}
#endregion
#region Build
public EcsStaticMask Build()
{
HashSet<EcsTypeCode> combinedIncs = _inc;
HashSet<EcsTypeCode> combinedExcs = _exc;
if (_combineds.Count > 0)
{
combinedIncs = new HashSet<EcsTypeCode>();
combinedExcs = new HashSet<EcsTypeCode>();
if (_sortedCombinedChecker == false)
{
_combineds.Sort((a, b) => a.order - b.order);
}
foreach (var item in _combineds)
{
EcsStaticMask submask = item.mask;
combinedIncs.ExceptWith(submask._exc);//удаляю конфликтующие ограничения
combinedExcs.ExceptWith(submask._inc);//удаляю конфликтующие ограничения
combinedIncs.UnionWith(submask._inc);
combinedExcs.UnionWith(submask._exc);
}
combinedIncs.ExceptWith(_exc);//удаляю конфликтующие ограничения
combinedExcs.ExceptWith(_inc);//удаляю конфликтующие ограничения
combinedIncs.UnionWith(_inc);
combinedExcs.UnionWith(_exc);
_combineds.Clear();
}
else
{
combinedIncs = _inc;
combinedExcs = _exc;
}
if (_excepteds.Count > 0)
{
foreach (var item in _excepteds)
{
//if (combinedIncs.Overlaps(item.mask._exc) || combinedExcs.Overlaps(item.mask._inc))
//{
// return _world.Get<WorldMaskComponent>().BrokenMask;
//}
combinedIncs.ExceptWith(item.mask._inc);
combinedExcs.ExceptWith(item.mask._exc);
}
_excepteds.Clear();
}
var inc = combinedIncs.ToArray();
Array.Sort(inc);
var exc = combinedExcs.ToArray();
Array.Sort(exc);
var key = new Key(inc, exc);
EcsStaticMask result = CreateMask(key);
_inc.Clear();
_exc.Clear();
_version++;
return result;
}
#endregion
#region Utils
private readonly struct Combined
{
public readonly EcsStaticMask mask;
public readonly int order;
public Combined(EcsStaticMask mask, int order) { this.mask = mask; this.order = order; }
}
private readonly struct Excepted
{
public readonly EcsStaticMask mask;
public readonly int order;
public Excepted(EcsStaticMask mask, int order) { this.mask = mask; this.order = order; }
}
#endregion
}
#endregion
#region Debug utils
private static string CreateLogString(EcsTypeCode[] inc, EcsTypeCode[] exc)
{
#if (DEBUG && !DISABLE_DEBUG)
string converter(EcsTypeCode o) { return EcsDebugUtility.GetGenericTypeName(EcsTypeCodeManager.FindTypeOfCode(o), 1); }
return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})";
#else
return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization
#endif
}
internal class DebuggerProxy
{
private EcsStaticMask _source;
public readonly int ID;
public readonly EcsTypeCode[] included;
public readonly EcsTypeCode[] excluded;
public readonly Type[] includedTypes;
public readonly Type[] excludedTypes;
public bool IsEmpty { get { return _source.IsEmpty; } }
public bool IsBroken { get { return _source.IsBroken; } }
public DebuggerProxy(EcsStaticMask mask)
{
_source = mask;
ID = mask._id;
included = mask._inc;
excluded = mask._exc;
Type converter(EcsTypeCode o) { return EcsTypeCodeManager.FindTypeOfCode(o); }
includedTypes = included.Select(converter).ToArray();
excludedTypes = excluded.Select(converter).ToArray();
}
public override string ToString()
{
return CreateLogString(included, excluded);
}
}
#if DEBUG
//TODO оптимизировать, так как списки сортированны, наверняка есть способ без хешсета пройтись и не локать треды
private static HashSet<int> _dummyHashSet = new HashSet<int>();
private static void CheckConstraints(int[] inc, int[] exc)
{
lock (_dummyHashSet)
{
if (CheckRepeats(inc)) { throw new EcsFrameworkException("The values in the Include constraints are repeated."); }
if (CheckRepeats(exc)) { throw new EcsFrameworkException("The values in the Exclude constraints are repeated."); }
_dummyHashSet.Clear();
_dummyHashSet.UnionWith(inc);
if (_dummyHashSet.Overlaps(exc)) { throw new EcsFrameworkException("Conflicting Include and Exclude constraints."); }
}
}
private static bool CheckRepeats(int[] array)
{
_dummyHashSet.Clear();
foreach (var item in array)
{
if (_dummyHashSet.Contains(item))
{
return true;
}
_dummyHashSet.Add(item);
}
return false;
}
#endif
#endregion
}
}

View File

@ -85,20 +85,20 @@ namespace DCFApixels.DragonECS
return true;
}
long* ptr = _versions;
long* versionsPtr = _versions;
var slots = _world._poolSlots;
foreach (var slotIndex in _maskInc)
{
ptr++;
if (*ptr != slots[slotIndex].version)
versionsPtr++;
if (*versionsPtr != slots[slotIndex].version)
{
return false;
}
}
foreach (var slotIndex in _maskExc)
{
ptr++;
if (*ptr != slots[slotIndex].version)
versionsPtr++;
if (*versionsPtr != slots[slotIndex].version)
{
return false;
}

View File

@ -38,6 +38,17 @@ namespace DCFApixels.DragonECS.Internal
public static EcsTypeCode Get<T>() { return EcsTypeCodeCache<T>.code; }
public static bool Has(Type type) { return _codes.ContainsKey(type); }
public static bool Has<T>() { return _codes.ContainsKey(typeof(T)); }
public static Type FindTypeOfCode(EcsTypeCode typeCode)
{
foreach (var item in _codes)
{
if (item.Value == typeCode)
{
return item.Key;
}
}
return null;
}
public static IEnumerable<TypeCodeInfo> GetDeclaredTypes() { return _codes.Select(o => new TypeCodeInfo(o.Key, o.Value)); }
}
#if ENABLE_IL2CPP