From 5591a3f5b14adef9122a29253c7bbfdc38b5e595 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 8 Sep 2024 19:39:43 +0800 Subject: [PATCH] Resolve TODO --- src/Debug/EcsDebug.cs | 2 +- src/Debug/EcsDebugUtility.cs | 1 - src/EcsPipeline.Builder.cs | 55 ++++++++---- src/EcsPipeline.cs | 2 + src/Injections/Graph/InjectionBranch.cs | 9 +- src/Injections/Graph/InjectionNode.cs | 16 +++- src/Injections/Injector.cs | 106 ++++++++++++------------ src/Injections/Utils/Interfaces.cs | 2 +- 8 files changed, 114 insertions(+), 79 deletions(-) diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index cc83a45..064b146 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -196,7 +196,7 @@ namespace DCFApixels.DragonECS { self.Print(""); } - //TODO PrintJson не возможно будет добавлено когда-то + //TODO PrintJson возможно будет добавлено когда-то } public sealed class DefaultDebugService : DebugService { diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 0106081..93ebdcb 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -66,7 +66,6 @@ namespace DCFApixels.DragonECS return AutoToString(self, typeof(T), isWriteName); } - //TODO сделать специальный вывод в виде названий констант для Enum-ов private static string AutoToString(object target, Type type, bool isWriteName) { #if (DEBUG && !DISABLE_DEBUG) || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает diff --git a/src/EcsPipeline.Builder.cs b/src/EcsPipeline.Builder.cs index 0b59a08..33415d6 100644 --- a/src/EcsPipeline.Builder.cs +++ b/src/EcsPipeline.Builder.cs @@ -39,6 +39,8 @@ namespace DCFApixels.DragonECS private AddParams _defaultAddParams = new AddParams(BASIC_LAYER, 0, false); + private HashSet _uniqueSystemsSet = new HashSet(); + #region Properties //private ReadOnlySpan SystemRecords //{ @@ -107,10 +109,14 @@ namespace DCFApixels.DragonECS { InsertAfterNode_Internal(_endIndex, system, layer, sortOrder, isUnique); } - int _DEBUG_COUNTER = 0; private void InsertAfterNode_Internal(int insertAfterIndex, IEcsProcess system, string layer, int sortOrder, bool isUnique) { - _DEBUG_COUNTER++; + //TODO нужно потестить + if (isUnique && _uniqueSystemsSet.Add(system.GetType()) == false) + { + EcsDebug.PrintWarning($"The pipeline already contains a unique instance of {system.GetType().Name}"); + } + SystemNode record = new SystemNode(system, layer, sortOrder, isUnique); int newIndex; if (_freeNodesCount <= 0) @@ -175,10 +181,7 @@ namespace DCFApixels.DragonECS _defaultAddParams = oldDefaultAddParams; } - if (module is IInjectionUnit injectionUnit) - { - Injector.Inject(injectionUnit); - } + Injector.Inject(module); return this; } #endregion @@ -251,6 +254,8 @@ namespace DCFApixels.DragonECS } public Builder Remove() { + _uniqueSystemsSet.Remove(typeof(TSystem)); + if (_systemNodesCount <= 1) { if (_systemNodesCount == 1 && _systemNodes[0].system is TSystem) @@ -293,9 +298,6 @@ namespace DCFApixels.DragonECS _layerLists.Add(BASIC_LAYER, basicLayerList); } - //ERROR Уникальные системы ломают работу подсчета систем - HashSet uniqueSystemsSet = new HashSet(); - int allSystemsLength = 0; foreach (var item in _layerLists) { @@ -320,10 +322,12 @@ namespace DCFApixels.DragonECS { list = basicLayerList; } - if (node.isUnique == false || uniqueSystemsSet.Add(node.system.GetType())) - { - list.Add(node.system, node.sortOrder, node.isUnique); - } + list.Add(node.system, node.sortOrder, node.isUnique); + + //if (node.isUnique == false || uniqueSystemsSet.Add(node.system.GetType())) + //{ + // list.Add(node.system, node.sortOrder, node.isUnique); + //} } @@ -525,15 +529,32 @@ namespace DCFApixels.DragonECS } return InsertAfter(targetLayer, movingLayers); } + + private static bool AreMatchingOrderIdentical(IReadOnlyList listA, IReadOnlyList listB) + { + int indexA = 0; + foreach (string itemB in listB) + { + if (indexA < listA.Count && listA[indexA] == itemB) + { + indexA++; + } + } + return indexA == listA.Count; + } public void MergeWith(IReadOnlyList other) { - //TODO добавить оишбку если порядок совпадающих слоев не совпадает - HashSet seen = new HashSet(); - List result = new List(); - List listA = _layers; IReadOnlyList listB = other; + if (AreMatchingOrderIdentical(listA, listB) == false) + { + Throw.Exception("Для слияния списков слоев, нужно чтобы названия слоев, присутствующие в обоих списках, появлялись в одном и том же порядке в обоих списках"); + } + + HashSet seen = new HashSet(); + List result = new List(); + foreach (string item in listA) { seen.Add(item); diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 5b05da0..0db3be1 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -224,6 +224,7 @@ namespace DCFApixels.DragonECS #endregion } + #region EcsModule public interface IEcsModule { void Import(EcsPipeline.Builder b); @@ -237,6 +238,7 @@ namespace DCFApixels.DragonECS void IInjectionUnit.InitInjectionNode(InjectionNodes nodes) { nodes.AddNode(); } public EcsModule() { if (GetType() != typeof(T)) { Throw.UndefinedException(); } } } + #endregion #region Extensions public static partial class EcsPipelineExtensions diff --git a/src/Injections/Graph/InjectionBranch.cs b/src/Injections/Graph/InjectionBranch.cs index 1ea6f74..feee2fd 100644 --- a/src/Injections/Graph/InjectionBranch.cs +++ b/src/Injections/Graph/InjectionBranch.cs @@ -7,20 +7,18 @@ namespace DCFApixels.DragonECS.Internal { private readonly Injector _source; private readonly Type _type; - private InjectionNodeBase[] _nodes = new InjectionNodeBase[2]; + private InjectionNodeBase[] _nodes = new InjectionNodeBase[4]; private int _nodesCount = 0; - private object _currentInjectedDependency; - public Type Type { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _type; } } - public object CurrentInjectedDependency + public ReadOnlySpan Nodes { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _currentInjectedDependency; } + get { return new ReadOnlySpan(_nodes, 0, _nodesCount); } } public InjectionBranch(Injector source, Type type) { @@ -29,7 +27,6 @@ namespace DCFApixels.DragonECS.Internal } public void Inject(object obj) { - _currentInjectedDependency = obj; for (int i = 0; i < _nodesCount; i++) { _nodes[i].Inject(obj); diff --git a/src/Injections/Graph/InjectionNode.cs b/src/Injections/Graph/InjectionNode.cs index fee555d..8851f71 100644 --- a/src/Injections/Graph/InjectionNode.cs +++ b/src/Injections/Graph/InjectionNode.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { @@ -9,6 +10,7 @@ namespace DCFApixels.DragonECS { get { return _type; } } + public abstract object CurrentInjectedDependencyRaw { get; } protected InjectionNodeBase(Type type) { _type = type; @@ -22,7 +24,18 @@ namespace DCFApixels.DragonECS.Internal internal sealed class InjectionNode : InjectionNodeBase { private EcsProcess> _process; - public InjectionNode(Type type) : base(type) { } + private T _currentInjectedDependency; + public sealed override object CurrentInjectedDependencyRaw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _currentInjectedDependency; } + } + public T CurrentInjectedDependency + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _currentInjectedDependency; } + } + public InjectionNode() : base(typeof(T)) { } public sealed override void Init(EcsPipeline pipeline) { _process = pipeline.GetProcess>(); @@ -30,6 +43,7 @@ namespace DCFApixels.DragonECS.Internal public sealed override void Inject(object raw) { T obj = (T)raw; + _currentInjectedDependency = obj; for (int i = 0; i < _process.Length; i++) { _process[i].Inject(obj); diff --git a/src/Injections/Injector.cs b/src/Injections/Injector.cs index 23c4cbe..48d11e2 100644 --- a/src/Injections/Injector.cs +++ b/src/Injections/Injector.cs @@ -1,6 +1,7 @@ using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS @@ -12,6 +13,10 @@ namespace DCFApixels.DragonECS private Dictionary _nodes = new Dictionary(32); private bool _isInit = false; +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + private HashSet _requiredInjectionTypes = new HashSet(); +#endif + public EcsPipeline Pipelie { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -24,43 +29,36 @@ namespace DCFApixels.DragonECS public void Inject(T obj) { object raw = obj; - Type type = obj.GetType(); - if (_branches.TryGetValue(type, out InjectionBranch branch) == false) + Type tType = typeof(T); + Type objType = obj.GetType(); + if (_branches.TryGetValue(objType, out InjectionBranch branch) == false) { - if (typeof(T) == type) + if (_nodes.ContainsKey(tType) == false) { - if (_nodes.ContainsKey(type) == false) - { - InitNode(new InjectionNode(type)); - } - branch = new InjectionBranch(this, type); - InitBranch(branch); + InitNode(new InjectionNode()); } - else + bool hasNode = _nodes.ContainsKey(objType); + if (hasNode == false && obj is IInjectionUnit unit) { - bool hasNode = _nodes.ContainsKey(type); - if (hasNode == false && obj is IInjectionUnit unit) - { - unit.InitInjectionNode(new InjectionNodes(this)); - hasNode = _nodes.ContainsKey(type); - } - if (hasNode) - { - branch = new InjectionBranch(this, type); - InitBranch(branch); - } - else - { - //TODO переработать это исключение - // идея следующая, в режиме дебага с помощью рефлекшена собрать информацию о системах в которых есть IEcsInject, собрать все типы которые принимают системы, - // потом при инициирующих инъекциях проверить что во все собранные типы были заинжектены. Если нет, то только тогда бросать исключение. - // Исключения можно заранее определять и собирать, а бросать на моменте. Например тут создать исключение, и если инхекции небыло то бросить его. - // Дополнительно обернуть все в #if DEBUG + unit.InitInjectionNode(new InjectionNodes(this)); + hasNode = _nodes.ContainsKey(objType); + } - //Другой вариант, тут добавить дополнительную проверку, если среди систем есть системы с IEcsInject где T это obj.GetType() то бросить исключение - throw new EcsInjectionException($"To create an injection branch, no injection node of {type.Name} was found. To create a node, use the AddNode<{type.Name}>() method directly in the injector or in the implementation of the IInjectionUnit for {type.Name}."); + branch = new InjectionBranch(this, objType); + InitBranch(branch); + +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + foreach (var requiredInjectionType in _requiredInjectionTypes) + { + if (requiredInjectionType.IsAssignableFrom(objType)) + { + if (_nodes.ContainsKey(requiredInjectionType) == false) + { + throw new EcsInjectionException($"A systems in the pipeline implements IEcsInject<{requiredInjectionType.Name}> interface, but no suitable injection node was found in the Injector. To create a node, use Injector.AddNode<{requiredInjectionType.Name}>() or implement the IInjectionUnit interface for type {objType.Name}."); + } } } +#endif } branch.Inject(raw); } @@ -68,26 +66,19 @@ namespace DCFApixels.DragonECS { return (T)Extract_Internal(typeof(T)); } - private object Extract_Internal(Type type) + private object Extract_Internal(Type type)//TODO проверить { - if (_branches.TryGetValue(type, out InjectionBranch branch)) + if (_nodes.TryGetValue(type, out InjectionNodeBase node)) { - return branch.CurrentInjectedDependency; + return node.CurrentInjectedDependencyRaw; } - return null; - - //if (_nodes.ContainsKey(type)) - //{ - // return null; - //} - //throw new EcsInjectionException($"The injection graph is missing a node for {type.Name} type. To create a node, use the AddNode<{type.Name}>() method directly in the injector or in the implementation of the IInjectionUnit for {type.Name}."); + throw new EcsInjectionException($"The injection graph is missing a node for {type.Name} type. To create a node, use the Injector.AddNode<{type.Name}>() method directly in the injector or in the implementation of the IInjectionUnit for {type.Name}."); } public void AddNode() { - Type type = typeof(T); - if (_nodes.ContainsKey(type) == false) + if (_nodes.ContainsKey(typeof(T)) == false) { - InitNode(new InjectionNode(type)); + InitNode(new InjectionNode()); } } #endregion @@ -132,16 +123,27 @@ namespace DCFApixels.DragonECS #region Build private void Init(EcsPipeline pipeline) { - if (_isInit) - { - throw new Exception("Already initialized"); - } + if (_isInit) { Throw.Exception("Already initialized"); } + _pipeline = pipeline; - foreach (var node in _nodes.Values) + foreach (var pair in _nodes) { - node.Init(pipeline); + pair.Value.Init(pipeline); } _isInit = true; + +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + var systems = _pipeline.AllSystems; + var injectType = typeof(IEcsInject<>); + foreach (var system in systems) + { + var type = system.GetType(); + foreach (var requiredInjectionType in type.GetInterfaces().Where(o => o.IsGenericType && o.GetGenericTypeDefinition() == injectType).Select(o => o.GenericTypeArguments[0])) + { + _requiredInjectionTypes.Add(requiredInjectionType); + } + } +#endif } private bool TryDeclare() { @@ -150,7 +152,7 @@ namespace DCFApixels.DragonECS { return false; } - InitNode(new InjectionNode(type)); + InitNode(new InjectionNode()); #if !REFLECTION_DISABLED if (IsCanInstantiated(type)) #endif @@ -180,13 +182,13 @@ namespace DCFApixels.DragonECS _initInjections.Add(new InitInject(obj)); return _source; } - public EcsPipeline.Builder Extract(ref T obj) + public EcsPipeline.Builder Extract(ref T obj)//TODO проверить { Type type = typeof(T); for (int i = _initInjections.Count - 1; i >= 0; i--) { var item = _initInjections[i]; - if (item.Type.IsAssignableFrom(type)) + if (type.IsAssignableFrom(item.Type)) { obj = (T)item.Raw; return _source; diff --git a/src/Injections/Utils/Interfaces.cs b/src/Injections/Utils/Interfaces.cs index c6fe01c..ca69da6 100644 --- a/src/Injections/Utils/Interfaces.cs +++ b/src/Injections/Utils/Interfaces.cs @@ -39,6 +39,6 @@ } public interface IInjectionUnit { - void InitInjectionNode(InjectionNodes nodes); + void InitInjectionNode(InjectionNodes graph); } }