diff --git a/src/EcsPipeline.Builder.cs b/src/EcsPipeline.Builder.cs index 55c5be4..2966088 100644 --- a/src/EcsPipeline.Builder.cs +++ b/src/EcsPipeline.Builder.cs @@ -1,12 +1,11 @@ #if DISABLE_DEBUG #undef DEBUG #endif +using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Internal; using DCFApixels.DragonECS.RunnersCore; using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -25,7 +24,7 @@ namespace DCFApixels.DragonECS public sealed partial class EcsPipeline { - public class Builder : IEcsModule + public partial class Builder : IEcsModule { private SystemNode[] _systemNodes = new SystemNode[256]; private int _startIndex = -1; @@ -37,7 +36,7 @@ namespace DCFApixels.DragonECS private readonly Dictionary _layerLists = new Dictionary(8); private readonly List _initDeclaredRunners = new List(4); - public readonly LayerList Layers; + public readonly LayersMap Layers; public readonly Injector.Builder Injector; public readonly Configurator Configs; @@ -64,7 +63,7 @@ namespace DCFApixels.DragonECS Injector.AddNode(); Injector.AddNode(); - Layers = new LayerList(this, PRE_BEGIN_LAYER, BEGIN_LAYER, BASIC_LAYER, END_LAYER, POST_END_LAYER); + Layers = new LayersMap(this, PRE_BEGIN_LAYER, BEGIN_LAYER, BASIC_LAYER, END_LAYER, POST_END_LAYER); } #endregion @@ -245,7 +244,7 @@ namespace DCFApixels.DragonECS } Layers.MergeWith(other.Layers); - foreach (ref readonly SystemNode otherRecord in new LinkedListIterator(_systemNodes, _systemNodesCount, _startIndex)) + foreach (ref readonly SystemNode otherRecord in new LinkedListCountIterator(_systemNodes, _systemNodesCount, _startIndex)) { AddNode_Internal(otherRecord.system, otherRecord.layerName, otherRecord.sortOrder, otherRecord.isUnique); } @@ -309,7 +308,7 @@ namespace DCFApixels.DragonECS #if DEBUG _buildMarker.Begin(); #endif - var it = new LinkedListIterator(_systemNodes, _systemNodesCount, _startIndex); + var it = new LinkedListCountIterator(_systemNodes, _systemNodesCount, _startIndex); LayerSystemsList basicLayerList; if (_layerLists.TryGetValue(BASIC_LAYER, out basicLayerList) == false) @@ -354,7 +353,7 @@ namespace DCFApixels.DragonECS IEcsProcess[] allSystems = new IEcsProcess[allSystemsLength]; { int i = 0; - foreach (var item in Layers) + foreach (var item in Layers.Build()) { if (_layerLists.TryGetValue(item, out var list) && list.IsInit) { @@ -413,217 +412,10 @@ namespace DCFApixels.DragonECS return _builder; } } + #endregion - #region LayerList - public class LayerList : IEnumerable - { - private Builder _source; - private List _layers; - private string _basicLayerName; - private string _addLayerName; - - #region Properties - public int Count { get { return _layers.Count; } } - public object this[int index] { get { return _layers[index]; } } - #endregion - - #region Constructors - public LayerList(Builder source, string basicLayerName) - { - _source = source; - _layers = new List(16) { basicLayerName }; - _basicLayerName = basicLayerName; - _addLayerName = _basicLayerName; - } - public LayerList(Builder source, string preBeginlayer, string beginlayer, string basicLayer, string endLayer, string postEndLayer) - { - _source = source; - _layers = new List(16) { preBeginlayer, beginlayer, basicLayer, endLayer, postEndLayer }; - _basicLayerName = basicLayer; - _addLayerName = _basicLayerName; - } - #endregion - - #region Edit - - #region Single - public Builder Add(string newLayer) - { - InsertAfter(_addLayerName, newLayer); - _addLayerName = newLayer; - return _source; - } - public Builder Insert(string targetLayer, string newLayer) - { - if (Contains(newLayer)) { return _source; } - - int index = _layers.IndexOf(targetLayer); - if (index < 0) - { - throw new KeyNotFoundException($"Layer {targetLayer} not found"); - } - _layers.Insert(index, newLayer); - return _source; - } - public Builder InsertAfter(string targetLayer, string newLayer) - { - if (Contains(newLayer)) { return _source; } - - int index = _layers.IndexOf(targetLayer); - if (index < 0) - { - throw new KeyNotFoundException($"Layer {targetLayer} not found"); - } - - _layers.Insert(++index, newLayer); - return _source; - } - public Builder Move(string targetLayer, string movingLayer) - { - _layers.Remove(movingLayer); - return Insert(targetLayer, movingLayer); - } - public Builder MoveAfter(string targetLayer, string movingLayer) - { - _layers.Remove(movingLayer); - return InsertAfter(targetLayer, movingLayer); - } - #endregion - - #region Range - public Builder Add(params string[] newLayers) - { - InsertAfter(_addLayerName, newLayers); - _addLayerName = newLayers[newLayers.Length - 1]; - return _source; - } - public Builder Insert(string targetLayer, params string[] newLayers) - { - int index = _layers.IndexOf(targetLayer); - if (index < 0) - { - throw new KeyNotFoundException($"Layer {targetLayer} not found"); - } - _layers.InsertRange(index, newLayers.Where(o => !Contains(o))); - return _source; - } - public Builder InsertAfter(string targetLayer, params string[] newLayers) - { - int index = _layers.IndexOf(targetLayer); - if (index < 0) - { - throw new KeyNotFoundException($"Layer {targetLayer} not found"); - } - - _layers.InsertRange(++index, newLayers.Where(o => !Contains(o))); - return _source; - } - public Builder Move(string targetLayer, params string[] movingLayers) - { - foreach (var movingLayer in movingLayers) - { - _layers.Remove(movingLayer); - } - return Insert(targetLayer, movingLayers); - } - public Builder MoveAfter(string targetLayer, params string[] movingLayers) - { - foreach (var movingLayer in movingLayers) - { - _layers.Remove(movingLayer); - } - return InsertAfter(targetLayer, movingLayers); - } - #endregion - - #endregion - - #region MergeWith - private static bool CheckOverlapsOrder(List listA, IReadOnlyList listB) - { - int lastIndexof = 0; - for (int i = 0; i < listB.Count; i++) - { - var a = listB[i]; - int indexof = listA.IndexOf(a); - - if (indexof < 0) { continue; } - if (indexof < lastIndexof) - { - return false; - } - lastIndexof = indexof; - } - return true; - } - public void MergeWith(IReadOnlyList other) - { - List listA = _layers; - IReadOnlyList listB = other; - - if (CheckOverlapsOrder(listA, listB) == false) - { - //Для слияния списков слоев, нужно чтобы в пересечении порядок записей совпадал - Throw.Exception("To merge layer lists, the names of the layers present in both lists must appear in the same order in both lists."); - } - - HashSet seen = new HashSet(); - List result = new List(); - - foreach (string item in listA) - { - seen.Add(item); - } - foreach (string item in listB) - { - if (seen.Add(item) == false) - { - seen.Remove(item); - } - } - - int i = 0, j = 0; - while (i < listA.Count || j < listB.Count) - { - while (i < listA.Count && seen.Contains(listA[i])) - { - result.Add(listA[i]); - i++; - } - while (j < listB.Count && seen.Contains(listB[j])) - { - result.Add(listB[j]); - j++; - } - - if (i < listA.Count) { i++; } - if (j < listB.Count) - { - result.Add(listB[j]); - j++; - } - } - - _layers = result; - } - public void MergeWith(LayerList other) - { - MergeWith(other._layers); - } - #endregion - - #region Other - public bool Contains(string layer) { return _layers.Contains(layer); } - - public List.Enumerator GetEnumerator() { return _layers.GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() { return _layers.GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() { return _layers.GetEnumerator(); } - #endregion - } - #endregion - - #region SystemsList + #region LayerSystemsList private class LayerSystemsList { public int lasyInitSystemsCount = 0; @@ -726,7 +518,7 @@ namespace DCFApixels.DragonECS #region SerializableTemplate public EcsPipelineTemplate GenerateSerializableTemplate() { - var it = new LinkedListIterator(_systemNodes, _systemNodesCount, _startIndex); + var it = new LinkedListCountIterator(_systemNodes, _systemNodesCount, _startIndex); EcsPipelineTemplate result = new EcsPipelineTemplate(); result.layers = new string[Layers.Count]; result.records = new EcsPipelineTemplate.Record[it.Count]; @@ -768,6 +560,15 @@ namespace DCFApixels.DragonECS } } #endregion + + #region Obsolete + [Obsolete("Use LayersMap")] + public class LayerList : LayersMap + { + public LayerList(Builder source, string basicLayerName) : base(source, basicLayerName) { } + public LayerList(Builder source, string preBeginlayer, string beginlayer, string basicLayer, string endLayer, string postEndLayer) : base(source, preBeginlayer, beginlayer, basicLayer, endLayer, postEndLayer) { } + } + #endregion } } diff --git a/src/Utils/LayersMap.cs b/src/Utils/LayersMap.cs new file mode 100644 index 0000000..6952bc2 --- /dev/null +++ b/src/Utils/LayersMap.cs @@ -0,0 +1,585 @@ +using DCFApixels.DragonECS.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace DCFApixels.DragonECS.Core +{ + public partial class LayersMap : IEnumerable + { + private readonly Dictionary _layerIds = new Dictionary(32); + private StructList _layerInfos = new StructList(32); + private LayerID GetLayerID(string layer) + { + if (_layerIds.TryGetValue(layer, out LayerID layerID) == false) + { + layerID = (LayerID)_layerInfos.Count; + _layerInfos.Add(default); + + _layerIds[layer] = layerID; + ref var layerInfo = ref GetLayerInfo(layerID); + layerInfo.name = layer; + } + return layerID; + } + private ref LayerInfo GetLayerInfo(LayerID layerID) + { + return ref _layerInfos._items[(int)layerID]; + } + private struct LayerInfo + { + public string name; + public bool isLocked; + public bool isContained; + public int insertionIndex; + //build + public int inDegree; + public bool hasAnyDependency; + public LayerInfo(string name) : this() + { + this.name = name; + } + } + + private readonly EcsPipeline.Builder _source; + private StructList<(LayerID From, LayerID To)> _dependencies = new StructList<(LayerID, LayerID)>(16); + private readonly LayerID _basicLayerID; + private int _increment = 0; + private int _count; + + #region Properties + public int Count + { + get { return _count; } + } + #endregion + + #region Constructors + public LayersMap(EcsPipeline.Builder source = null) + { + GetLayerID(""); + _source = source; + } + public LayersMap(EcsPipeline.Builder source, string basicLayerName) + { + GetLayerID(""); + _source = source; + + Add(basicLayerName); + + _basicLayerID = GetLayerID(basicLayerName); + LockLayer(basicLayerName); + } + public LayersMap(EcsPipeline.Builder source, string preBeginlayer, string beginlayer, string basicLayer, string endLayer, string postEndLayer) + { + GetLayerID(""); + _source = source; + + Add(preBeginlayer) + .Before(beginlayer); + Add(beginlayer) + .After(preBeginlayer) + .Before(basicLayer); + Add(basicLayer) + .After(beginlayer) + .Before(endLayer); + Add(endLayer) + .After(basicLayer) + .Before(postEndLayer); + Add(postEndLayer) + .After(endLayer); + + _basicLayerID = GetLayerID(basicLayer); + LockLayer(preBeginlayer); + LockLayer(beginlayer); + LockLayer(basicLayer); + LockLayer(endLayer); + LockLayer(postEndLayer); + } + #endregion + + #region Methods + private void LockLayer(string layer) + { + GetLayerInfo(GetLayerID(layer)).isLocked = true; + } + private void Add_Internal(LayerID id) + { + ref var info = ref GetLayerInfo(id); + if (info.isLocked) { return; } + if (info.isContained == false) + { + _count++; + info.isContained = true; + } + info.insertionIndex = _increment++; + } + private void Remove_Internal(LayerID id) + { + ref var info = ref GetLayerInfo(id); + if (info.isLocked) { throw new Exception($"The {info.name} layer cannot be removed"); } + if (info.isContained) + { + _count--; + info.isContained = false; + } + info.insertionIndex = 0; + } + private void AddDependency_Internal(LayerID from, LayerID to) + { + GetLayerInfo(from).hasAnyDependency = true; + GetLayerInfo(to).hasAnyDependency = true; + _dependencies.Add((from, to)); + } + private void AddDependency_Internal(LayerID from, string to) + { + AddDependency_Internal(from, GetLayerID(to)); + } + private void AddDependency_Internal(string from, LayerID to) + { + AddDependency_Internal(GetLayerID(from), to); + } + private void AddDependency_Internal(string from, string to) + { + AddDependency_Internal(GetLayerID(from), GetLayerID(to)); + } + #endregion + + #region Add + public DependencyHandler Add(string layer) + { + var id = GetLayerID(layer); + Add_Internal(id); + return new DependencyHandler(this, (int)id); + } + public DependencyHandler Add(params string[] layers) + { + return Add(layersRange: layers); + } + public DependencyHandler Add(IEnumerable layersRange) + { + foreach (var layer in layersRange) + { + Add_Internal(GetLayerID(layer)); + } + return new DependencyHandler(this, layersRange); + } + #endregion + + #region Move + public DependencyHandler Move(string layer) + { + return new DependencyHandler(this, (int)GetLayerID(layer)); + } + public DependencyHandler Move(params string[] layers) + { + return new DependencyHandler(this, layers); + } + public DependencyHandler Move(IEnumerable layersRange) + { + return new DependencyHandler(this, layersRange); + } + #endregion + + #region Remove + public void Remove(string layer) + { + Remove_Internal(GetLayerID(layer)); + } + public void Remove(params string[] layers) + { + Remove(layersRange: layers); + } + public void Remove(IEnumerable layersRange) + { + foreach (var layer in layersRange) + { + Remove_Internal(GetLayerID(layer)); + } + } + #endregion + + #region MergeWith + public void MergeWith(LayersMap other) + { + foreach (var otherDependency in other._dependencies) + { + AddDependency_Internal(other.GetLayerInfo(otherDependency.From).name, other.GetLayerInfo(otherDependency.To).name); + } + for (int i = 0; i < other._layerInfos.Count; i++) + { + LayerID otherLayerID = (LayerID)i; + ref var otherLayerInfo = ref other.GetLayerInfo(otherLayerID); + Add(otherLayerInfo.name); + } + } + #endregion + + private enum LayerID : int { NULL = 0 } + public readonly ref struct DependencyHandler + { + private readonly LayersMap _source; + private readonly LayerID _id; + private readonly IEnumerable _layersRange; + + #region Properties + public EcsPipeline.Builder Back + { + get { return _source._source; } + } + #endregion + + #region Constructors + public DependencyHandler(LayersMap source, int id) + { + _source = source; + _id = (LayerID)id; + _layersRange = null; + } + public DependencyHandler(LayersMap source, IEnumerable layersRange) + { + _source = source; + _id = LayerID.NULL; + _layersRange = layersRange; + } + #endregion + + #region Before + public DependencyHandler Before(params string[] targets) + { + return Before(range: targets); + } + public DependencyHandler Before(IEnumerable range) + { + foreach (var target in range) + { + Before(target); + } + return this; + } + public DependencyHandler Before(string targetLayer) + { + if (_id != LayerID.NULL) + { + _source.AddDependency_Internal(_id, targetLayer); + } + if (_layersRange != null) + { + foreach (var layer in _layersRange) + { + _source.AddDependency_Internal(layer, targetLayer); + } + } + return this; + } + #endregion + + #region After + public DependencyHandler After(params string[] targets) + { + return After(range: targets); + } + public DependencyHandler After(IEnumerable range) + { + foreach (var target in range) + { + After(target); + } + return this; + } + public DependencyHandler After(string target) + { + if (_id != LayerID.NULL) + { + _source.AddDependency_Internal(target, _id); + } + if (_layersRange != null) + { + foreach (var id in _layersRange) + { + _source.AddDependency_Internal(target, id); + } + } + return this; + } + #endregion + } + + #region Build + public string[] Build() + { + LayerID[] nodes = new LayerID[_count]; + var adjacency = new List<(LayerID To, int DependencyIndex)>[_layerInfos.Count]; + + for (int i = 0, j = 0; i < _layerInfos.Count; i++) + { + LayerID layerID = (LayerID)i; + ref var info = ref GetLayerInfo(layerID); + adjacency[(int)layerID] = new List<(LayerID To, int DependencyIndex)>(); + _layerInfos._items[(int)layerID].inDegree = 0; + if (info.isContained) + { + nodes[j++] = layerID; + } + } + + for (int i = 0; i < _dependencies.Count; i++) + { + var (from, to) = _dependencies[i]; + ref var fromInfo = ref GetLayerInfo(from); + ref var toInfo = ref GetLayerInfo(to); + + if (fromInfo.isContained && toInfo.isContained) + { + adjacency[(int)from].Add((to, i)); + toInfo.inDegree += 1; + } + } + + if (_basicLayerID != LayerID.NULL) + { + int ind = _dependencies.Count; + var basicLayerAdjacencyList = adjacency[(int)_basicLayerID]; + for (int i = 0; i < _layerInfos.Count; i++) + { + ref var toInfo = ref _layerInfos._items[i]; + if(toInfo.isContained && toInfo.hasAnyDependency == false) + { + basicLayerAdjacencyList.Add(((LayerID)i, ind++)); + toInfo.inDegree += 1; + } + } + } + + + List zeroInDegree = new List(nodes.Length); + zeroInDegree.AddRange(nodes.Where(id => GetLayerInfo(id).inDegree == 0).OrderBy(id => GetLayerInfo(id).insertionIndex)); + + var result = new List(nodes.Length); + + while (zeroInDegree.Count > 0) + { + var current = zeroInDegree[0]; + zeroInDegree.RemoveAt(0); + result.Add(GetLayerInfo(current).name); + + foreach (var (neighbor, _) in adjacency[(int)current]) + { + ref var neighborInfo = ref GetLayerInfo(neighbor); + neighborInfo.inDegree--; + if (neighborInfo.inDegree == 0) + { + var neighborInsertionIndex = neighborInfo.insertionIndex; + int insertIndex = zeroInDegree.FindIndex(id => GetLayerInfo(id).insertionIndex < neighborInsertionIndex); + insertIndex = insertIndex < 0 ? 0 : insertIndex; + zeroInDegree.Insert(insertIndex, neighbor); + } + } + } + + if (result.Count != nodes.Length) + { + var cycle = FindCycle(adjacency, nodes); + string details = string.Empty; + if (cycle != null) + { + var cycleDependencies = GetCycleDependencies(cycle, adjacency); + details = $" Cycle edges path: {string.Join(", ", cycleDependencies)}"; + } + throw new InvalidOperationException("Cyclic dependency detected." + details); + } + + return result.ToArray(); + } + #endregion + + #region FindCycles + private List FindCycle( + List<(LayerID To, int DependencyIndex)>[] adjacency, + LayerID[] nodes) + { + var visited = new Dictionary(); + var recursionStack = new Stack(); + + foreach (var node in nodes) + { + if (FindCycleDFS(node, adjacency, visited, recursionStack)) + { + return recursionStack.Reverse().ToList(); + } + } + return null; + } + private bool FindCycleDFS( + LayerID node, + List<(LayerID To, int DependencyIndex)>[] adjacency, + Dictionary visited, + Stack recursionStack) + { + if (!visited.TryGetValue(node, out bool isVisited)) + { + visited[node] = true; + recursionStack.Push(node); + + foreach (var (neighbor, _) in adjacency[(int)node]) + { + if (!visited.ContainsKey(neighbor) && FindCycleDFS(neighbor, adjacency, visited, recursionStack)) + { + return true; + } + else if (recursionStack.Contains(neighbor)) + { + recursionStack.Push(neighbor); + return true; + } + } + + recursionStack.Pop(); + return false; + } + return isVisited && recursionStack.Contains(node); + } + + private string[] GetCycleDependencies( + List cycle, + List<(LayerID To, int DependencyIndex)>[] adjacency) + { + var cycleEdges = new HashSet<(LayerID, LayerID)>(); + for (int i = 0; i < cycle.Count - 1; i++) + { + cycleEdges.Add((cycle[i], cycle[i + 1])); + } + + var dependencies = new List(); + foreach (var from in cycle) + { + foreach (var (to, depIndex) in adjacency[(int)from]) + { + if (cycleEdges.Contains((from, to)) && _dependencies.Count > depIndex) + { + var dep = _dependencies[depIndex]; + dependencies.Add($"{GetLayerInfo(dep.From).name}->{GetLayerInfo(dep.To).name}"); + } + } + } + return dependencies.Distinct().ToArray(); + } + #endregion + + #region Other + public bool Contains(string layer) { return GetLayerInfo(GetLayerID(layer)).isContained; } + + public Enumerator GetEnumerator() { return new Enumerator(this); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public struct Enumerator : IEnumerator + { + private LayersMap _map; + private int _index; + public Enumerator(LayersMap map) + { + _map = map; + _index = -1; + } + public string Current + { + get { return _map._layerInfos._items[_index].name; } + } + object IEnumerator.Current { get { return Current; } } + public bool MoveNext() + { + if (_index++ >= _map._layerInfos.Count) { return false; } + ref var info = ref _map._layerInfos._items[_index]; + if(info.isContained == false) + { + return MoveNext(); + } + else + { + return true; + } + } + public void Reset() { _index = -1; } + public void Dispose() { } + } + #endregion + + #region Obsolete + [Obsolete("Use " + nameof(LayersMap) + ".Add(layer).Before(targetLayer).Back;")] + public EcsPipeline.Builder Insert(string targetLayer, string newLayer) + { + Add(newLayer).Before(targetLayer); + return _source; + } + [Obsolete("Use " + nameof(LayersMap) + ".Add(layer).After(targetLayer).Back;")] + public EcsPipeline.Builder InsertAfter(string targetLayer, string newLayer) + { + Add(newLayer).After(targetLayer); + return _source; + } + [Obsolete("Use " + nameof(LayersMap) + ".Move(layer).Before(targetLayer).Back;")] + public EcsPipeline.Builder Move(string targetLayer, string newLayer) + { + Move(newLayer).Before(targetLayer); + return _source; + } + [Obsolete("Use " + nameof(LayersMap) + ".Move(layer).After(targetLayer).Back;")] + public EcsPipeline.Builder MoveAfter(string targetLayer, string newLayer) + { + Move(newLayer).After(targetLayer); + return _source; + } + [Obsolete("Use " + nameof(LayersMap) + ".Add(layers).Before(targetLayer).Back;")] + public EcsPipeline.Builder Insert(string targetLayer, params string[] newLayers) + { + Add(newLayers).Before(targetLayer); + return _source; + } + [Obsolete("Use " + nameof(LayersMap) + ".Add(layers).After(targetLayer).Back;")] + public EcsPipeline.Builder InsertAfter(string targetLayer, params string[] newLayers) + { + Add(newLayers).After(targetLayer); + return _source; + } + [Obsolete("Use " + nameof(LayersMap) + ".Move(layers).Before(targetLayer).Back;")] + public EcsPipeline.Builder Move(string targetLayer, params string[] movingLayers) + { + Move(movingLayers).Before(targetLayer); + return _source; + } + [Obsolete("Use " + nameof(LayersMap) + ".Move(layers).After(targetLayer).Back;")] + public EcsPipeline.Builder MoveAfter(string targetLayer, params string[] movingLayers) + { + Move(movingLayers).After(targetLayer); + return _source; + } + + [Obsolete] + public object this[int index] + { + get + { + int i = 0; + foreach (var item in this) + { + if (i == index) + { + return item; + } + i++; + } + return null; + } + } + [Obsolete("Use MergeWith(LayersMap)")] + public void MergeWith(IReadOnlyList other) + { + foreach (var layer in other) + { + Add(layer); + } + } + #endregion + } +} \ No newline at end of file