diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index af3aae3..cf58594 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -37,6 +37,12 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void PrintWarning(object v) => Print(EcsConsts.DEBUG_WARNING_TAG, v); public static void PrintError(object v) => Print(EcsConsts.DEBUG_ERROR_TAG, v); + public static void Print() + { +#if !DISABLE_DRAGONECS_DEBUGGER + DebugService.Instance.Print(""); +#endif + } public static void Print(object v) { #if !DISABLE_DRAGONECS_DEBUGGER diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index a365fd8..83a8541 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -10,60 +10,6 @@ namespace DCFApixels.DragonECS { private const BindingFlags RFL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - private struct ProcessInterface - { - public Type interfaceType; - public string processName; - public ProcessInterface(Type interfaceType, string processName) - { - this.interfaceType = interfaceType; - this.processName = processName; - } - } - private static Dictionary _processes = new Dictionary(); - - static EcsDebugUtility() - { - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - var types = assembly.GetTypes(); - foreach (var type in types) - { - if (!type.IsInterface) - continue; - - if (type.GetInterface(nameof(IEcsProcess)) != null) - { - string name = type.Name; - if (name[0] == 'I' && name.Length > 1 && char.IsUpper(name[1])) - name = name.Substring(1); - name = Regex.Replace(name, @"\bEcs|Process\b", ""); - _processes.Add(type, new ProcessInterface(type, name)); - } - } - } - } - - #region Process - public static bool IsProcessInterface(Type type) - { - if (type.IsGenericType) type = type.GetGenericTypeDefinition(); - return _processes.ContainsKey(type); - } - public static string GetProcessInterfaceName(Type type) - { - if (type.IsGenericType) type = type.GetGenericTypeDefinition(); - return _processes[type].processName; - } - public static bool TryGetProcessInterfaceName(Type type, out string name) - { - if (type.IsGenericType) type = type.GetGenericTypeDefinition(); - bool result = _processes.TryGetValue(type, out ProcessInterface data); - name = data.processName; - return result; - } - #endregion - #region GetGenericTypeName public static string GetGenericTypeFullName(int maxDepth = 2) => GetGenericTypeFullName(typeof(T), maxDepth); public static string GetGenericTypeFullName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, true); diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index be990fd..704d3cd 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -6,6 +6,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; using static DCFApixels.DragonECS.EcsDebugUtility; namespace DCFApixels.DragonECS @@ -263,4 +264,85 @@ namespace DCFApixels.DragonECS } } #endregion + + public static class EcsProcessUtility + { + private struct ProcessInterface + { + public Type interfaceType; + public string processName; + public ProcessInterface(Type interfaceType, string processName) + { + this.interfaceType = interfaceType; + this.processName = processName; + } + } + private static Dictionary _processes = new Dictionary(); + private static HashSet _systems = new HashSet(); + + static EcsProcessUtility() + { + Type processBasicInterface = typeof(IEcsProcess); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var types = assembly.GetTypes(); + foreach (var type in types) + { + if (type.GetInterface(nameof(IEcsProcess)) != null || type == processBasicInterface) + { + if (type.IsInterface) + { + string name = type.Name; + if (name[0] == 'I' && name.Length > 1 && char.IsUpper(name[1])) + name = name.Substring(1); + name = Regex.Replace(name, @"\bEcs|Process\b", ""); + if (Regex.IsMatch(name, "`\\w{1,}$")) + { + var s = name.Split("`"); + name = s[0] + $"<{s[1]}>"; + } + _processes.Add(type, new ProcessInterface(type, name)); + } + else + { + _systems.Add(type); + } + } + } + } + } + + #region Systems + public static bool IsSystem(Type type) => _systems.Contains(type); + public static bool IsEcsSystem(this Type type) => _systems.Contains(type); + #endregion + + #region Process + public static bool IsProcessInterface(Type type) + { + if (type.IsGenericType) type = type.GetGenericTypeDefinition(); + return _processes.ContainsKey(type); + } + public static bool IsEcsProcessInterface(this Type type) => IsProcessInterface(type); + + public static string GetProcessInterfaceName(Type type) + { + if (type.IsGenericType) type = type.GetGenericTypeDefinition(); + return _processes[type].processName; + } + public static bool TryGetProcessInterfaceName(Type type, out string name) + { + if (type.IsGenericType) type = type.GetGenericTypeDefinition(); + bool result = _processes.TryGetValue(type, out ProcessInterface data); + name = data.processName; + return result; + } + + public static IEnumerable GetEcsProcessInterfaces(this Type self) + { + return self.GetInterfaces().Where(o=> o.IsEcsProcessInterface()); + } + #endregion + + } } diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index 1c83dff..c047cd2 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -73,7 +73,7 @@ namespace DCFApixels.DragonECS if (_poolIds.Contains(EcsTypeCode.Get())) throw new EcsFrameworkException("The pool has already been created."); - Type componentType = typeof(TPool).GetInterfaces().First(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IEcsPoolImplementation<>)); + Type componentType = typeof(TPool).GetInterfaces().First(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IEcsPoolImplementation<>)).GetGenericArguments()[0]; int componentTypeCode = EcsTypeCode.Get(componentType); if (_componentIds.TryGetValue(componentTypeCode, out int componentID)) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 3cbf10a..abdf0c3 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -1,13 +1,24 @@ -using DCFApixels.DragonECS.Utils; +using DCFApixels.DragonECS.Internal; using System; using System.Collections; using System.Collections.Generic; +using System.Reflection; using System.Runtime.CompilerServices; +using UnityEngine; +using static UnityEditor.Progress; namespace DCFApixels.DragonECS { + namespace Internal + { + public interface IEcsHybridPoolInternal : IEcsPool + { + void AddRefInternal(int entityID, object component, bool isAppend); + void DelInternal(int entityID, bool isAppend); + } + } /// Pool for IEcsHybridComponent components - public sealed class EcsHybridPool : IEcsPoolImplementation, IEcsHybridPool, IEnumerable //IEnumerable - IntelliSense hack + public sealed class EcsHybridPool : IEcsPoolImplementation, IEcsHybridPool, IEcsHybridPoolInternal, IEnumerable //IEnumerable - IntelliSense hack where T : IEcsHybridComponent { private EcsWorld _source; @@ -31,25 +42,12 @@ namespace DCFApixels.DragonECS public EcsWorld World => _source; #endregion - #region Init - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) - { - _source = world; - _componentID = componentID; - - const int capacity = 512; - - _mapping = new int[world.Capacity]; - _recycledItems = new int[128]; - _recycledItemsCount = 0; - _items = new T[capacity]; - _entities = new int[capacity]; - _itemsCount = 0; - } - #endregion - #region Methods - public void Add(int entityID, T component) + void IEcsHybridPoolInternal.AddRefInternal(int entityID, object component, bool isMain) + { + AddInternal(entityID, (T)component, isMain); + } + private void AddInternal(int entityID, T component, bool isMain) { ref int itemIndex = ref _mapping[entityID]; #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS @@ -71,40 +69,23 @@ namespace DCFApixels.DragonECS } this.IncrementEntityComponentCount(entityID); _listeners.InvokeOnAdd(entityID); - component.OnAddToPool(_source.GetEntityLong(entityID)); + if(isMain) + component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; _entities[itemIndex] = entityID; } + public void Add(int entityID, T component) + { + HybridMapping mapping = _source.GetHybridMapping(component.GetType()); + mapping.GetTargetTypePool().AddRefInternal(entityID, component, false); + foreach (var pool in mapping.GetPools()) + pool.AddRefInternal(entityID, component, true); + } public void Set(int entityID, T component) { - ref int itemIndex = ref _mapping[entityID]; - if(itemIndex <= 0) - {//null - if (_recycledItemsCount > 0) - { - itemIndex = _recycledItems[--_recycledItemsCount]; - _itemsCount++; - } - else - { - itemIndex = ++_itemsCount; - if (itemIndex >= _items.Length) - { - Array.Resize(ref _items, _items.Length << 1); - Array.Resize(ref _entities, _items.Length); - } - } - this.IncrementEntityComponentCount(entityID); - } - else - {//not null - _listeners.InvokeOnDel(entityID); - _items[itemIndex].OnDelFromPool(_source.GetEntityLong(entityID)); - } - _listeners.InvokeOnAdd(entityID); - component.OnAddToPool(_source.GetEntityLong(entityID)); - _items[itemIndex] = component; - _entities[itemIndex] = entityID; + if(Has(entityID)) + Del(entityID); + Add(entityID, component); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Get(int entityID) @@ -128,14 +109,19 @@ namespace DCFApixels.DragonECS { return _mapping[entityID] > 0; } - public void Del(int entityID) + void IEcsHybridPoolInternal.DelInternal(int entityID, bool isMain) + { + DelInternal(entityID, isMain); + } + private void DelInternal(int entityID, bool isMain) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif ref int itemIndex = ref _mapping[entityID]; T component = _items[itemIndex]; - component.OnDelFromPool(_source.GetEntityLong(entityID)); + if(isMain) + component.OnDelFromPool(_source.GetEntityLong(entityID)); if (_recycledItemsCount >= _recycledItems.Length) Array.Resize(ref _recycledItems, _recycledItems.Length << 1); _recycledItems[_recycledItemsCount++] = itemIndex; @@ -145,6 +131,14 @@ namespace DCFApixels.DragonECS this.DecrementEntityComponentCount(entityID); _listeners.InvokeOnDel(entityID); } + public void Del(int entityID) + { + var component = Get(entityID); + HybridMapping mapping = _source.GetHybridMapping(component.GetType()); + mapping.GetTargetTypePool().DelInternal(entityID, false); + foreach (var pool in mapping.GetPools()) + pool.DelInternal(entityID, true); + } public void TryDel(int entityID) { if (Has(entityID)) Del(entityID); @@ -175,6 +169,20 @@ namespace DCFApixels.DragonECS #endregion #region Callbacks + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) + { + _source = world; + _componentID = componentID; + + const int capacity = 512; + + _mapping = new int[world.Capacity]; + _recycledItems = new int[128]; + _recycledItemsCount = 0; + _items = new T[capacity]; + _entities = new int[capacity]; + _itemsCount = 0; + } void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); @@ -218,13 +226,11 @@ namespace DCFApixels.DragonECS void OnAddToPool(entlong entity); void OnDelFromPool(entlong entity); } - public static class IEcsHybridComponentExtensions + public static class EcsHybridPoolExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrNotAlive(this IEcsHybridComponent self) => self == null || self.IsAlive; - } - public static class EcsHybridPoolExt - { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool GetPool(this EcsWorld self) where T : IEcsHybridComponent { @@ -253,65 +259,94 @@ namespace DCFApixels.DragonECS } } - - - - - - - public static class InterfaceMatrix + public partial class EcsWorld { - private static SparseArray _edges = new SparseArray(); - private static SparseArray64 _matrix = new SparseArray64(); - public static bool HasEdge() - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!InterfaceIsDeclared() || !InterfaceIsDeclared()) - EcsDebug.PrintWarning($"{nameof(TParent)} or {nameof(TChild)} not declared."); -#endif - return _matrix.Contains(InterfaceId._id, InterfaceId._id); - } - public static bool InterfaceIsDeclared() => _edges.Contains(InterfaceId._id); + private Dictionary _mappings = new Dictionary(); - public static void DeclareInterfacesFromClass() + internal HybridMapping GetHybridMapping(Type type) { - Type type = typeof(T); - if (type.IsInterface) - throw new ArgumentException($"The argument {nameof(T)} cannot be an interface"); + if(!_mappings.TryGetValue(type, out HybridMapping mapping)) + { + mapping = new HybridMapping(this, type); + _mappings.Add(type, mapping); + } + return mapping; } } - internal class InterfaceMatrixEdge - { - private static int _increment = 0; - public readonly int id; - public readonly Type parentType; - public readonly Type childType; - public readonly int parentID; - public readonly int childID; - public static InterfaceMatrixEdge New() - { - return new InterfaceMatrixEdge( - typeof(TParent), - typeof(TChild), - InterfaceId._id, - InterfaceId._id); - } - public InterfaceMatrixEdge(Type parentType, Type childType, int parentID, int childID) - { - id = _increment++; - this.parentType = parentType; - this.childType = childType; - this.parentID = parentID; - this.childID = childID; - } - } - internal static class InterfaceId + internal class HybridMapping { - internal static int _increment; - } - internal static class InterfaceId - { - public static int _id = InterfaceId._increment++; + private EcsWorld _source; + private object[] _sourceForReflection; + private Type _type; + + private IEcsHybridPoolInternal _targetTypePool; + private List _relatedPools; + + private static Type hybridPoolType = typeof(EcsHybridPool<>); + private static MethodInfo getHybridPoolMethod = typeof(EcsHybridPoolExtensions).GetMethod($"{nameof(EcsHybridPoolExtensions.GetPool)}", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + + + private static HashSet _hybridComponents = new HashSet(); + static HybridMapping() + { + Type hybridComponentType = typeof(IEcsHybridComponent); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var types = assembly.GetTypes(); + foreach (var type in types) + { + if (type.GetInterface(nameof(IEcsHybridComponent)) != null && type != hybridComponentType) + { + _hybridComponents.Add(type); + } + } + } + } + public static bool IsEcsHybridComponentType(Type type) + { + return _hybridComponents.Contains(type); + } + + public HybridMapping(EcsWorld source, Type type) + { + if (!type.IsClass) + throw new ArgumentException(); + + _source = source; + _type = type; + _relatedPools = new List(); + _sourceForReflection = new object[]{ source }; + _targetTypePool = CreateHybridPool(type); + foreach (var item in type.GetInterfaces()) + { + if(IsEcsHybridComponentType(item)) + { + _relatedPools.Add(CreateHybridPool(item)); + } + } + Type baseType = type.BaseType; + while(baseType != typeof(object) && IsEcsHybridComponentType(baseType)) + { + _relatedPools.Add(CreateHybridPool(baseType)); + baseType = baseType.BaseType; + } + } + private IEcsHybridPoolInternal CreateHybridPool(Type componentType) + { + //var x = (IEcsHybridPoolInternal)getHybridPoolMethod.MakeGenericMethod(componentType).Invoke(null, _sourceForReflection); + //Debug.Log("_" + x.ComponentID + "_" +x.ComponentType.Name); + //return x; + return (IEcsHybridPoolInternal)getHybridPoolMethod.MakeGenericMethod(componentType).Invoke(null, _sourceForReflection); + } + + public IEcsHybridPoolInternal GetTargetTypePool() + { + return _targetTypePool; + } + public List GetPools() + { + return _relatedPools; + } } } diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 3f16701..8b058ec 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -31,22 +31,6 @@ namespace DCFApixels.DragonECS public EcsWorld World => _source; #endregion - #region Init - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) - { - _source = world; - _componentID = componentID; - - const int capacity = 512; - - _mapping = new int[world.Capacity]; - _recycledItems = new int[128]; - _recycledItemsCount = 0; - _items = new T[capacity]; - _itemsCount = 0; - } - #endregion - #region Methods public ref T Add(int entityID) { @@ -149,6 +133,19 @@ namespace DCFApixels.DragonECS #endregion #region Callbacks + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) + { + _source = world; + _componentID = componentID; + + const int capacity = 512; + + _mapping = new int[world.Capacity]; + _recycledItems = new int[128]; + _recycledItemsCount = 0; + _items = new T[capacity]; + _itemsCount = 0; + } void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 7e13bc8..496af44 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -26,17 +26,6 @@ namespace DCFApixels.DragonECS public EcsWorld World => _source; #endregion - #region Init - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) - { - _source = world; - _componentID = componentID; - - _mapping = new bool[world.Capacity]; - _count = 0; - } - #endregion - #region Method public void Add(int entityID) { @@ -114,6 +103,14 @@ namespace DCFApixels.DragonECS #endregion #region Callbacks + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) + { + _source = world; + _componentID = componentID; + + _mapping = new bool[world.Capacity]; + _count = 0; + } void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); diff --git a/src/Utils/EcsTypeCode.cs b/src/Utils/EcsTypeCode.cs index f90ac67..142e394 100644 --- a/src/Utils/EcsTypeCode.cs +++ b/src/Utils/EcsTypeCode.cs @@ -1,31 +1,47 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; -namespace DCFApixels.DragonECS +namespace DCFApixels.DragonECS.Internal { - namespace Internal + public static class EcsTypeCode { - internal static class EcsTypeCode + private static readonly Dictionary _codes = new Dictionary(); + private static int _incremetn = 1; + public static int Count => _codes.Count; + public static int Get(Type type) { - private static readonly Dictionary _codes = new Dictionary(); - private static int _incremetn = 1; - public static int Count => _codes.Count; - public static int Get(Type type) + if (!_codes.TryGetValue(type, out int code)) { - if (!_codes.TryGetValue(type, out int code)) - { - code = _incremetn++; - _codes.Add(type, code); - } - return code; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Get() => Cache.code; - private static class Cache - { - public static readonly int code = Get(typeof(T)); + code = _incremetn++; + _codes.Add(type, code); } + return code; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Get() => EcsTypeCodeCache.code; + public static bool Has(Type type) => _codes.ContainsKey(type); + public static bool Has() => _codes.ContainsKey(typeof(T)); + public static IEnumerable GetDeclared() => _codes.Select(o => new TypeCodeInfo(o.Key, o.Value)); + } + public static class EcsTypeCodeCache + { + public static readonly int code = EcsTypeCode.Get(typeof(T)); + } + public struct TypeCodeInfo + { + public Type type; + public int code; + public TypeCodeInfo(Type type, int code) + { + this.type = type; + this.code = code; + } + + public override string ToString() + { + return this.AutoToString(false) + "\n\r"; } } -} +} \ No newline at end of file