diff --git a/src/EcsArc.cs b/src/EcsArc.cs index 05629f2..3feb3ab 100644 --- a/src/EcsArc.cs +++ b/src/EcsArc.cs @@ -173,7 +173,7 @@ namespace DCFApixels.DragonECS return !_relEntityInfos[relEntityID].IsNull; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public StartEnd GetRelationInfo(int relEntityID) + public StartEnd GetRelationStartEnd(int relEntityID) { if (relEntityID <= 0 || relEntityID >= _relEntityInfos.Length) { @@ -182,14 +182,22 @@ namespace DCFApixels.DragonECS return new StartEnd(_relEntityInfos[relEntityID]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetRelStart(int relEntityID) + public int GetRelationStart(int relEntityID) { - return GetRelationInfo(relEntityID).start; + if (relEntityID <= 0 || relEntityID >= _relEntityInfos.Length) + { + Throw.UndefinedException(); + } + return _relEntityInfos[relEntityID].start; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetRelEnd(int relEntityID) + public int GetRelationEnd(int relEntityID) { - return GetRelationInfo(relEntityID).end; + if (relEntityID <= 0 || relEntityID >= _relEntityInfos.Length) + { + Throw.UndefinedException(); + } + return _relEntityInfos[relEntityID].end; } #endregion diff --git a/src/EcsWorldGraph.cs b/src/EcsWorldGraph.cs index 5d274aa..aeecc1a 100644 --- a/src/EcsWorldGraph.cs +++ b/src/EcsWorldGraph.cs @@ -80,23 +80,17 @@ namespace DCFApixels.DragonECS #region Extension public static bool IsRegistered(this EcsArcWorld self) { - if (self == null) - { - Throw.ArgumentNull(); - } + if (self == null) { Throw.ArgumentNull(); } int id = self.id; return id < _arcsMapping.Length && _arcsMapping[self.id] != null; } public static EcsArc GetRegisteredArc(this EcsArcWorld self) { - if (self == null) - { - Throw.ArgumentNull(); - } + if (self == null) { Throw.ArgumentNull(); } int id = self.id; if (id < _arcsMapping.Length && _arcsMapping[self.id] == null) { - throw new Exception(); + Throw.UndefinedException(); } return _arcsMapping[self.id]; } @@ -105,10 +99,7 @@ namespace DCFApixels.DragonECS public static EcsArc SetLoopArcAuto(this TWorld self, out EcsLoopArcWorld arcWorld, IConfigContainer config = null) where TWorld : EcsWorld { - if (self == null) - { - Throw.ArgumentNull(); - } + if (self == null) { Throw.ArgumentNull(); } if (typeof(TWorld) != self.GetType()) { EcsDebug.PrintWarning($"{nameof(TWorld)} is not {self.GetType().Name}"); @@ -120,10 +111,7 @@ namespace DCFApixels.DragonECS where TStartWorld : EcsWorld where TEndWorld : EcsWorld { - if (start == null || end == null) - { - Throw.ArgumentNull(); - } + if (start == null || end == null) { Throw.ArgumentNull(); } if (typeof(TStartWorld) == typeof(EcsWorld) && typeof(TEndWorld) == typeof(EcsWorld)) { EcsDebug.PrintWarning($"{nameof(TStartWorld)} is not {start.GetType().Name} or {nameof(TEndWorld)} is not {end.GetType().Name}"); @@ -143,49 +131,52 @@ namespace DCFApixels.DragonECS return SetArcAuto(start, end, out _, config); } - public static EcsArc SetLoopArc(this EcsWorld self, EcsArcWorld arc) => SetArc(self, self, arc); + public static EcsArc SetLoopArc(this EcsWorld self, EcsArcWorld arc) + { + return SetArc(self, self, arc); + } public static EcsArc SetArc(this EcsWorld start, EcsWorld end, EcsArcWorld arc) { - if (start == null || end == null || arc == null) - { - Throw.ArgumentNull(); - } + if (start == null || end == null || arc == null) { Throw.ArgumentNull(); } return Register(start, end, arc); } - public static bool HasLoopArc(this EcsWorld self) => HasArc(self, self); + public static bool HasLoopArc(this EcsWorld self) + { + return HasArc(self, self); + } public static bool HasArc(this EcsWorld start, EcsWorld end) { - if (start == null || end == null) - { - Throw.ArgumentNull(); - } + if (start == null || end == null) { Throw.ArgumentNull(); } return Has(start, end); } - public static EcsArc GetLoopArc(this EcsWorld self) => GetArc(self, self); + public static EcsArc GetLoopArc(this EcsWorld self) + { + return GetArc(self, self); + } public static EcsArc GetArc(this EcsWorld start, EcsWorld end) { - if (start == null || end == null) - { - Throw.ArgumentNull(); - } + if (start == null || end == null) { Throw.ArgumentNull(); } return Get(start, end); } - public static bool TryGetLoopArc(this EcsWorld self, out EcsArc arc) => TryGetArc(self, self, out arc); + public static bool TryGetLoopArc(this EcsWorld self, out EcsArc arc) + { + return TryGetArc(self, self, out arc); + } public static bool TryGetArc(this EcsWorld start, EcsWorld end, out EcsArc arc) { - if (start == null || end == null) - { - Throw.ArgumentNull(); - } + if (start == null || end == null) { Throw.ArgumentNull(); } bool result = Has(start, end); arc = result ? Get(start, end) : null; return result; } - public static void DestroyLoopArc(this EcsWorld self) => DestroyArcWith(self, self); + public static void DestroyLoopArc(this EcsWorld self) + { + DestroyArcWith(self, self); + } public static void DestroyArcWith(this EcsWorld start, EcsWorld end) { if (start == null || end == null) diff --git a/src/Executors/EcsJoinExecutor.cs b/src/Executors/EcsJoinExecutor.cs index 9b0ad23..8c4163f 100644 --- a/src/Executors/EcsJoinExecutor.cs +++ b/src/Executors/EcsJoinExecutor.cs @@ -1,124 +1,40 @@ -/* +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + namespace DCFApixels.DragonECS { - public class EcsJoinExecutor : EcsQueryExecutor, IEcsWorldEventListener - where TAspect : EcsAspect + public class EcsJoinExecutor : EcsQueryExecutor, IEcsWorldEventListener { - private TAspect _aspect; - //internal EcsGroup _filteredGroup; - - private IdsLinkedList _linkedBasket; - private int[] _mapping; - private int[] _counts; - - private long _executeVersion; - - private int _targetWorldCapacity = -1; - private EcsProfilerMarker _executeMarker = new EcsProfilerMarker("JoinAttach"); + private long _lastWorldVersion; #region Properties - public TAspect Aspect => _aspect; - internal long ExecuteVersion => _executeVersion; + public sealed override long Version + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _lastWorldVersion; + } #endregion - #region OnInitialize/OnDestroy + #region Callbacks protected override void OnInitialize() { - _linkedBasket = new IdsLinkedList(128); - World.AddListener(this); - _mapping = new int[World.Capacity]; - _counts = new int[World.Capacity]; } protected override void OnDestroy() { - World.RemoveListener(this); } - #endregion - - public void Clear() - { - _linkedBasket.Clear(); - ArrayUtility.Fill(_mapping, 0, 0, _mapping.Length); - ArrayUtility.Fill(_counts, 0, 0, _counts.Length); - } - - public IdsLinkedList.Span GetEntitiesFor(int entity) - { - ref var nodeIndex = ref _mapping[entity]; - if (nodeIndex <= 0) - return _linkedBasket.EmptySpan(); - else - return _linkedBasket.GetSpan(nodeIndex, _counts[entity]); - } - - #region Execute - public EcsJoinAttachResult Execute() => ExecuteFor(World.Entities); - public EcsJoinAttachResult ExecuteFor(EcsReadonlyGroup sourceGroup) - { - _executeMarker.Begin(); - var world = _aspect.World; -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (sourceGroup.IsNull) throw new ArgumentNullException();//TODO составить текст исключения. -#endif -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - else - if (World != sourceGroup.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир -#endif - - //Подготовка массивов - if (_targetWorldCapacity < World.Capacity) - { - _targetWorldCapacity = World.Capacity; - _mapping = new int[_targetWorldCapacity]; - _counts = new int[_targetWorldCapacity]; - } - else - { - ArrayUtility.Fill(_counts, 0); - ArrayUtility.Fill(_mapping, 0); - } - _linkedBasket.Clear(); - //Конец подготовки массивов - - EcsEdge edge = World.GetEdgeWithSelf(); - - var iterator = new EcsAspectIterator(_aspect, sourceGroup); - foreach (var arcEntityID in iterator) - { - var rel = edge.GetRelationTargets(arcEntityID); - - - int sorceEntityID = rel.entity; - //if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery - - ref int nodeIndex = ref _mapping[sorceEntityID]; - if (nodeIndex <= 0) - nodeIndex = _linkedBasket.Add(arcEntityID); - else - _linkedBasket.InsertAfter(nodeIndex, arcEntityID); - _counts[sorceEntityID]++; - } - - _executeVersion++; - _executeMarker.End(); - - return new EcsJoinAttachResult(_aspect, this, _executeVersion); - } - #endregion - - #region IEcsWorldEventListener - void IEcsWorldEventListener.OnWorldResize(int newSize) - { - Array.Resize(ref _mapping, newSize); - Array.Resize(ref _counts, newSize); - } - void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + public void OnWorldResize(int newSize) { } - void IEcsWorldEventListener.OnWorldDestroy() + public void OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + } + public void OnWorldDestroy() { } #endregion } } -*/ diff --git a/src/Executors/EcsJoinToGraphExecutor.cs b/src/Executors/EcsJoinToGraphExecutor.cs new file mode 100644 index 0000000..c68cd31 --- /dev/null +++ b/src/Executors/EcsJoinToGraphExecutor.cs @@ -0,0 +1,264 @@ +using DCFApixels.DragonECS.Graphs.Internal; +using DCFApixels.DragonECS.UncheckedCore; +using System; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS +{ + public abstract class EcsJoinToGraphExecutor : EcsQueryExecutor, IEcsWorldEventListener + { + private EcsAspect _aspect; + private EcsArcWorld _arcWorld; + + private EntityLinkedList _linkedList; + private Basket[] _baskets; + private int[] _startEntities; + private int _startEntitiesCount; + + private long _lastWorldVersion; + + private int _targetWorldCapacity = -1; + private EcsProfilerMarker _executeMarker = new EcsProfilerMarker("JoinAttach"); + + #region Properties + protected abstract EcsAspect AspectRaw { get; } + public sealed override long Version + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _lastWorldVersion; } + } + public EcsArcWorld ArcWorld + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _arcWorld; } + } + #endregion + + #region OnInitialize/OnDestroy + protected override void OnInitialize() + { + _linkedList = new EntityLinkedList(World.Capacity); + _baskets = new Basket[World.Capacity]; + World.AddListener(this); + _arcWorld = (EcsArcWorld)World; + } + protected override void OnDestroy() + { + World.RemoveListener(this); + } + #endregion + + #region Execute + public EcsGraph Execute() + { + return ExecuteFor(World.Entities); + } + public EcsGraph ExecuteFor(EcsSpan span) + { + _executeMarker.Begin(); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (span.IsNull) { Throw.ArgumentException(""); }//TODO составить текст исключения. + else if (World != span.World) { Throw.ArgumentException(""); } //TODO составить текст исключения. это проверка на то что пользователь использует правильный мир +#endif + + if (_lastWorldVersion != World.Version) + { + //Подготовка массивов + if (_targetWorldCapacity < World.Capacity) + { + _targetWorldCapacity = World.Capacity; + _baskets = new Basket[_targetWorldCapacity]; + _startEntities = new int[_targetWorldCapacity]; + } + else + { + ArrayUtility.Fill(_baskets, default); + } + _startEntitiesCount = 0; + _linkedList.Clear(); + //Конец подготовки массивов + + EcsArc arc = _arcWorld.GetRegisteredArc(); + + var iterator = _aspect.GetIteratorFor(span); + foreach (var relationEntityID in iterator) + { + int startEntityID = arc.GetRelationStart(relationEntityID); + if(startEntityID == 0) + { + continue; + } + _startEntities[_startEntitiesCount++] = startEntityID; + + ref var basket = ref _baskets[startEntityID]; + if (basket.index <= 0) + { + basket.index = _linkedList.Add(relationEntityID); + } + else + { + _linkedList.InsertAfter(basket.index, relationEntityID); + } + basket.count++; + } + + _lastWorldVersion = World.Version; + } + else + { + + } + + _executeMarker.End(); + return new EcsGraph(this, UncheckedCoreUtility.CreateSpan(WorldID, _startEntities, _startEntitiesCount)); + } + #endregion + + #region Internal result methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsGraphSpan GetNodes_Internal(int startEntityID) + { + Basket basket = _baskets[startEntityID]; + return new EcsGraphSpan(_linkedList._nodes, basket.index, basket.count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetNode_Internal(int startEntityID) + { + Basket basket = _baskets[startEntityID]; + return basket.count > 0 ? _linkedList.Get(basket.index) : 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetNodesCount_Internal(int startEntityID) + { + return _baskets[startEntityID].count; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int GetCount_Internal() + { + return _linkedList.Count; + } + #endregion + + #region IEcsWorldEventListener + void IEcsWorldEventListener.OnWorldResize(int newSize) + { + Array.Resize(ref _baskets, newSize); + } + void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { } + void IEcsWorldEventListener.OnWorldDestroy() { } + #endregion + + #region Basket + public struct Basket + { + public int index; + public int count; + } + #endregion + } + public sealed class EcsJoinExecutor : EcsJoinToGraphExecutor + where TAspect : EcsAspect + { + private TAspect _aspect; + + #region Properties + public TAspect Aspect + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _aspect; } + } + protected override EcsAspect AspectRaw + { + get { return _aspect; } + } + #endregion + } + + + #region EcsJoinedSpan/EcsJoined + public readonly ref struct EcsGraphSpan + { + public static EcsGraphSpan Empty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return new EcsGraphSpan(null, 0, 0); } + } + + private readonly EntityLinkedList.Node[] _nodes; + private readonly int _startNodeIndex; + private readonly int _count; + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _count; } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsGraphSpan(EntityLinkedList.Node[] nodes, int startNodeIndex, int count) + { + _nodes = nodes; + _startNodeIndex = startNodeIndex; + _count = count; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return new Enumerator(_nodes, _startNodeIndex, _count); + } + public struct Enumerator + { + private readonly EntityLinkedList.Node[] _nodes; + private int _index; + private int _count; + private int _next; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(EntityLinkedList.Node[] nodes, int startIndex, int count) + { + _nodes = nodes; + _index = -1; + _count = count; + _next = startIndex; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _nodes[_index].entityID; } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + _index = _next; + _next = _nodes[_next].next; + return _index > 0 && _count-- > 0; + } + } + } + + public readonly ref struct EcsGraph + { + private readonly EcsJoinToGraphExecutor _executer; + private readonly EcsSpan _startEntities; + public EcsSpan StartEntitiesSpan + { + get { return _startEntities; } + } + internal EcsGraph(EcsJoinToGraphExecutor executer, EcsSpan startEntites) + { + _executer = executer; + _startEntities = startEntites; + } + public EcsGraphSpan GetNodes(int startEntityID) + { + return _executer.GetNodes_Internal(startEntityID); + } + public int GetNode(int startEntityID) + { + return _executer.GetNode_Internal(startEntityID); + } + public int GetNodesCount(int startEntityID) + { + return _executer.GetNodesCount_Internal(startEntityID); + } + } + + #endregion +} diff --git a/src/Executors/EcsJoinExecutor.cs.meta b/src/Executors/EcsJoinToGraphExecutor.cs.meta similarity index 100% rename from src/Executors/EcsJoinExecutor.cs.meta rename to src/Executors/EcsJoinToGraphExecutor.cs.meta diff --git a/src/Internal/EntityLinkedList.cs b/src/Internal/EntityLinkedList.cs new file mode 100644 index 0000000..37b7509 --- /dev/null +++ b/src/Internal/EntityLinkedList.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS.Graphs.Internal +{ + internal class EntityLinkedList + { + public const int Enter = 0; + + internal Node[] _nodes; + private int _count; + private int _lastNodeIndex; + + #region Properties + public int Count => _count; + public int Capacity => _nodes.Length; + public int Last => _lastNodeIndex; + #endregion + + #region Constructors + public EntityLinkedList(int capacity) + { + _nodes = new Node[capacity + 10]; + Clear(); + } + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Resize(int newCapacity) + { + Array.Resize(ref _nodes, newCapacity + 10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + for (int i = 0; i < _nodes.Length; i++) + { + _nodes[i].next = 0; + } + _lastNodeIndex = Enter; + _count = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Set(int nodeIndex, int entityID) + { + _nodes[nodeIndex].entityID = entityID; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Get(int nodeIndex) + { + return _nodes[nodeIndex].entityID; + } + + /// Insert after + /// new node index + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int InsertAfter(int nodeIndex, int entityID) + { + _nodes[++_count].Set(entityID, _nodes[nodeIndex].next); + _nodes[nodeIndex].next = _count; + _lastNodeIndex = _count; + return _count; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Add(int entityID) + { + return InsertAfter(_lastNodeIndex, entityID); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return new Enumerator(_nodes); + } + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //public EcsJoinedSpan GetSpan(int startNodeIndex, int count) + //{ + // return new EcsJoinedSpan(_nodes, startNodeIndex, count); + //} + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //public EcsJoinedSpan GetEmptySpan() + //{ + // return new EcsJoinedSpan(_nodes, 0, 0); + //} + #region Utils + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] + public struct Node + { + public static readonly Node Empty = new Node() { entityID = 0, next = -1 }; + public int entityID; + /// next node index + public int next; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Set(int entityID, int next) + { + this.entityID = entityID; + this.next = next; + } + } + public struct Enumerator + { + private readonly Node[] _nodes; + private int _index; + private int _next; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(Node[] nodes) + { + _nodes = nodes; + _index = -1; + _next = Enter; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _nodes[_index].entityID; } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + _index = _next; + _next = _nodes[_next].next; + return _index > 0; + } + } + #endregion + } +}