From 29dc82568542a5e05bdbd10fe994da5064218bac Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:13:51 +0800 Subject: [PATCH 001/104] simple refactoring --- src/EcsAspect.cs | 6 +++--- src/Pools/EcsPool.cs | 23 +++++++++-------------- src/Pools/EcsPoolBase.cs | 7 ++++++- src/Pools/EcsTagPool.cs | 18 ++++++++---------- src/entlong.cs | 2 +- 5 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 3c47c0b..6a6a2fc 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -179,10 +179,10 @@ namespace DCFApixels.DragonECS } #endregion - private struct Combined + private readonly struct Combined { - public EcsAspect aspect; - public int order; + public readonly EcsAspect aspect; + public readonly int order; public Combined(EcsAspect aspect, int order) { this.aspect = aspect; diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index cc1cb6d..131b2ff 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -7,11 +7,11 @@ using static DCFApixels.DragonECS.EcsPoolThrowHalper; namespace DCFApixels.DragonECS { /// Pool for IEcsComponent components - public sealed class EcsPool : IEcsPoolImplementation, IEcsStructsPool, IEnumerable //IEnumerable - IntelliSense hack + public sealed class EcsPool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack where T : struct, IEcsComponent { private EcsWorld _source; - private int _id; + private int _componentID; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense @@ -19,15 +19,15 @@ namespace DCFApixels.DragonECS private int[] _recycledItems; private int _recycledItemsCount; - private IEcsComponentReset _componentResetHandler; - private IEcsComponentCopy _componentCopyHandler; + private IEcsComponentReset _componentResetHandler = EcsComponentResetHandler.instance; + private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; - private List _listeners; + private List _listeners = new List(); #region Properites public int Count => _itemsCount; public int Capacity => _items.Length; - public int ComponentID => _id; + public int ComponentID => _componentID; public Type ComponentType => typeof(T); public EcsWorld World => _source; #endregion @@ -36,7 +36,7 @@ namespace DCFApixels.DragonECS void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { _source = world; - _id = componentID; + _componentID = componentID; const int capacity = 512; @@ -45,11 +45,6 @@ namespace DCFApixels.DragonECS _recycledItemsCount = 0; _items = new T[capacity]; _itemsCount = 0; - - _listeners = new List(); - - _componentResetHandler = EcsComponentResetHandler.instance; - _componentCopyHandler = EcsComponentCopyHandler.instance; } #endregion @@ -171,8 +166,8 @@ namespace DCFApixels.DragonECS void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID) = (T)dataRaw; object IEcsPool.GetRaw(int entityID) => Read(entityID); void IEcsPool.SetRaw(int entityID, object dataRaw) => Get(entityID) = (T)dataRaw; - ref readonly T IEcsStructsPool.Read(int entityID) => ref Read(entityID); - ref T IEcsStructsPool.Get(int entityID) => ref Get(entityID); + ref readonly T IEcsStructPool.Read(int entityID) => ref Read(entityID); + ref T IEcsStructPool.Get(int entityID) => ref Get(entityID); #endregion #region Listeners diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index bb4d4cd..f07681f 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -30,12 +30,17 @@ namespace DCFApixels.DragonECS void RemoveListener(IEcsPoolEventListener listener); #endregion } - public interface IEcsStructsPool + public interface IEcsStructPool : IEcsPool { ref T Add(int entityID); ref readonly T Read(int entityID); ref T Get(int entityID); } + public interface IEcsClassPool : IEcsPool + { + T Add(int entityID); + T Get(int entityID); + } /// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool. public interface IEcsPoolImplementation : IEcsPool { diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 01c958f..a435a19 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -6,23 +6,23 @@ using static DCFApixels.DragonECS.EcsPoolThrowHalper; namespace DCFApixels.DragonECS { - public sealed class EcsTagPool : IEcsPoolImplementation, IEcsStructsPool, IEnumerable //IEnumerable - IntelliSense hack + public sealed class EcsTagPool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack where T : struct, IEcsTagComponent { private EcsWorld _source; - private int _id; + private int _componentID; private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private int _count; - private List _listeners; + private List _listeners = new List(); private T _fakeComponent; #region Properites public int Count => _count; int IEcsPool.Capacity => -1; - public int ComponentID => _id; + public int ComponentID => _componentID; public Type ComponentType => typeof(T); public EcsWorld World => _source; #endregion @@ -31,12 +31,10 @@ namespace DCFApixels.DragonECS void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { _source = world; - _id = componentID; + _componentID = componentID; _mapping = new bool[world.Capacity]; _count = 0; - - _listeners = new List(); } #endregion @@ -131,19 +129,19 @@ namespace DCFApixels.DragonECS #endregion #region Other - ref T IEcsStructsPool.Add(int entityID) + ref T IEcsStructPool.Add(int entityID) { Add(entityID); return ref _fakeComponent; } - ref readonly T IEcsStructsPool.Read(int entityID) + ref readonly T IEcsStructPool.Read(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(entityID)) ThrowNotHaveComponent(entityID); #endif return ref _fakeComponent; } - ref T IEcsStructsPool.Get(int entityID) + ref T IEcsStructPool.Get(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(entityID)) ThrowNotHaveComponent(entityID); diff --git a/src/entlong.cs b/src/entlong.cs index 904db21..2a4430a 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -33,7 +33,7 @@ namespace DCFApixels.DragonECS public bool IsNull { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this == NULL; + get => full == 0l; } public int ID { From 4b7649e80c00bdb19f4d94fe1c7367f1fe04f25c Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:22:01 +0800 Subject: [PATCH 002/104] split EcsWorld --- src/EcsWorld.cs | 92 --------------------------------- src/EcsWorld.static.cs | 100 ++++++++++++++++++++++++++++++++++++ src/EcsWorld.static.cs.meta | 11 ++++ 3 files changed, 111 insertions(+), 92 deletions(-) create mode 100644 src/EcsWorld.static.cs create mode 100644 src/EcsWorld.static.cs.meta diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 4d3b758..7ab71dc 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -3,101 +3,9 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { - [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)] - public struct EcsWorldCmp where T : struct - { - private int _worldID; - public EcsWorldCmp(int worldID) => _worldID = worldID; - public EcsWorld World => EcsWorld.GetWorld(_worldID); - public ref T Value => ref EcsWorld.GetData(_worldID); - } - public abstract partial class EcsWorld - { - private const short GEN_BITS = 0x7fff; - private const short DEATH_GEN_BIT = short.MinValue; - private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2; - - private static EcsWorld[] Worlds = new EcsWorld[4]; - private static IntDispenser _worldIdDispenser = new IntDispenser(0); - - private static List _dataReleaseres = new List(); - - static EcsWorld() - { - Worlds[0] = new EcsNullWorld(); - } - private static void ReleaseData(int worldID) - { - for (int i = 0, iMax = _dataReleaseres.Count; i < iMax; i++) - _dataReleaseres[i].Release(worldID); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsWorld GetWorld(int worldID) => Worlds[worldID]; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T GetData(int worldID) => ref WorldComponentPool.GetForWorld(worldID); - - private abstract class DataReleaser - { - public abstract void Release(int worldID); - } - private static class WorldComponentPool - { - private static T[] _items = new T[4]; - private static int[] _mapping = new int[4]; - private static int _count; - private static int[] _recycledItems = new int[4]; - private static int _recycledItemsCount; - private static IEcsWorldComponent _interface = EcsWorldComponentHandler.instance; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T Get(int itemIndex) => ref _items[itemIndex]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T GetForWorld(int worldID) => ref _items[GetItemIndex(worldID)]; - public static int GetItemIndex(int worldID) - { - if (_mapping.Length < Worlds.Length) - Array.Resize(ref _mapping, Worlds.Length); - - ref int itemIndex = ref _mapping[worldID]; - if (itemIndex <= 0) - { - if (_recycledItemsCount > 0) - { - _count++; - itemIndex = _recycledItems[--_recycledItemsCount]; - } - else - { - itemIndex = ++_count; - } - _interface.Init(ref _items[itemIndex], Worlds[worldID]); - _dataReleaseres.Add(new Releaser()); - } - return itemIndex; - } - private static void Release(int worldID) - { - ref int itemIndex = ref _mapping[worldID]; - if (itemIndex != 0) - { - _interface.OnDestroy(ref _items[itemIndex], Worlds[worldID]); - _recycledItems[_recycledItemsCount++] = itemIndex; - } - } - private sealed class Releaser : DataReleaser - { - public sealed override void Release(int worldID) - { - WorldComponentPool.Release(worldID); - } - } - } - } public abstract partial class EcsWorld { public readonly short id; diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs new file mode 100644 index 0000000..d5e69af --- /dev/null +++ b/src/EcsWorld.static.cs @@ -0,0 +1,100 @@ +using DCFApixels.DragonECS.Utils; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS +{ + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)] + public struct EcsWorldCmp where T : struct + { + private int _worldID; + public EcsWorldCmp(int worldID) => _worldID = worldID; + public EcsWorld World => EcsWorld.GetWorld(_worldID); + public ref T Value => ref EcsWorld.GetData(_worldID); + } + public abstract partial class EcsWorld + { + private const short GEN_BITS = 0x7fff; + private const short DEATH_GEN_BIT = short.MinValue; + private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2; + + private static EcsWorld[] Worlds = new EcsWorld[4]; + private static IntDispenser _worldIdDispenser = new IntDispenser(0); + + private static List _dataReleaseres = new List(); + + static EcsWorld() + { + Worlds[0] = new EcsNullWorld(); + } + private static void ReleaseData(int worldID) + { + for (int i = 0, iMax = _dataReleaseres.Count; i < iMax; i++) + _dataReleaseres[i].Release(worldID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsWorld GetWorld(int worldID) => Worlds[worldID]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T GetData(int worldID) => ref WorldComponentPool.GetForWorld(worldID); + + private abstract class DataReleaser + { + public abstract void Release(int worldID); + } + private static class WorldComponentPool + { + private static T[] _items = new T[4]; + private static int[] _mapping = new int[4]; + private static int _count; + private static int[] _recycledItems = new int[4]; + private static int _recycledItemsCount; + private static IEcsWorldComponent _interface = EcsWorldComponentHandler.instance; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Get(int itemIndex) => ref _items[itemIndex]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T GetForWorld(int worldID) => ref _items[GetItemIndex(worldID)]; + public static int GetItemIndex(int worldID) + { + if (_mapping.Length < Worlds.Length) + Array.Resize(ref _mapping, Worlds.Length); + + ref int itemIndex = ref _mapping[worldID]; + if (itemIndex <= 0) + { + if (_recycledItemsCount > 0) + { + _count++; + itemIndex = _recycledItems[--_recycledItemsCount]; + } + else + { + itemIndex = ++_count; + } + _interface.Init(ref _items[itemIndex], Worlds[worldID]); + _dataReleaseres.Add(new Releaser()); + } + return itemIndex; + } + private static void Release(int worldID) + { + ref int itemIndex = ref _mapping[worldID]; + if (itemIndex != 0) + { + _interface.OnDestroy(ref _items[itemIndex], Worlds[worldID]); + _recycledItems[_recycledItemsCount++] = itemIndex; + } + } + private sealed class Releaser : DataReleaser + { + public sealed override void Release(int worldID) + { + WorldComponentPool.Release(worldID); + } + } + } + } +} diff --git a/src/EcsWorld.static.cs.meta b/src/EcsWorld.static.cs.meta new file mode 100644 index 0000000..d99ff4b --- /dev/null +++ b/src/EcsWorld.static.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30c8fd4d7c5aeae4486e16024b4f50cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From f07a489d96e88d9ef03292984d4d92da5cb00758 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:57:50 +0800 Subject: [PATCH 003/104] update exceptions --- src/Builtin/InjectSystem.cs | 10 ++-- src/EcsAspect.cs | 15 ++---- src/EcsGroup.cs | 44 +++++------------ src/EcsPipeline.cs | 29 ++--------- src/EcsRunner.cs | 3 +- src/EcsWorld.cs | 8 ++-- src/EcsWorld.static.cs | 1 + src/Utils/Exceptions.cs | 96 +++++++++++++++++++++++++++++++++++++ src/entlong.cs | 26 +++------- 9 files changed, 132 insertions(+), 100 deletions(-) diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index e618f57..03c6a7c 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -2,7 +2,7 @@ using DCFApixels.DragonECS.RunnersCore; using System; using System.Linq; -using System.Runtime.CompilerServices; +using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { @@ -59,12 +59,10 @@ namespace DCFApixels.DragonECS private EcsBaseTypeInjectRunner _baseTypeInjectRunner; void IEcsInject.Inject(T obj) { - if (obj == null) ThrowArgumentNullException(); + if (obj == null) Throw.ArgumentNull(); _baseTypeInjectRunner.Inject(obj); foreach (var item in targets) item.Inject(obj); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ThrowArgumentNullException() => throw new ArgumentNullException(); protected override void OnSetup() { Type baseType = typeof(T).BaseType; @@ -112,7 +110,7 @@ namespace DCFApixels.DragonECS void IEcsInject.Inject(PreInitInjectController obj) => _injectController = obj; public InjectSystem(T injectedData) { - if (injectedData == null) throw new ArgumentNullException(); + if (injectedData == null) Throw.ArgumentNull(); _injectedData = injectedData; } public void PreInit(EcsPipeline pipeline) @@ -149,7 +147,7 @@ namespace DCFApixels.DragonECS { public static EcsPipeline.Builder Inject(this EcsPipeline.Builder self, T data) { - if (data == null) throw new ArgumentNullException(); + if (data == null) Throw.ArgumentNull(); return self.Add(new InjectSystem(data)); } public static EcsPipeline.Builder Inject(this EcsPipeline.Builder self, A a, B b) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 6a6a2fc..0ff7038 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -6,14 +6,13 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; +using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { public abstract class EcsAspect { - [EditorBrowsable(EditorBrowsableState.Always)] internal EcsWorld source; - [EditorBrowsable(EditorBrowsableState.Always)] internal EcsMask mask; private bool _isInit; @@ -85,7 +84,7 @@ namespace DCFApixels.DragonECS { int id = _world.GetComponentID(type); #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{type.Name} already in constraints list."); + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); #endif _inc.Add(id); } @@ -93,7 +92,7 @@ namespace DCFApixels.DragonECS { int id = _world.GetComponentID(type); #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{type.Name} already in constraints list."); + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); #endif _exc.Add(id); } @@ -279,14 +278,6 @@ namespace DCFApixels.DragonECS public override string ToString() => CreateLogString(worldTypeID, included, excluded); } #endregion - - #region ThrowHelper - internal static class ThrowHelper - { - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowArgumentDifferentWorldsException() => throw new ArgumentException("The groups belong to different worlds."); - } - #endregion } #endregion diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 7ec7c8c..5886d20 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -6,7 +6,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS -using static DCFApixels.DragonECS.EcsGroup.ThrowHelper; +using static DCFApixels.DragonECS.EcsThrowHalper; #endif namespace DCFApixels.DragonECS @@ -151,7 +151,7 @@ namespace DCFApixels.DragonECS get { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (index < 0 || index >= Count) ThrowArgumentOutOfRange(); + if (index < 0 || index >= Count) Throw.ArgumentOutOfRange(); #endif return _dense[++index]; } @@ -200,7 +200,7 @@ namespace DCFApixels.DragonECS internal void AddInternal(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (Has(entityID)) ThrowAlreadyContains(entityID); + if (Has(entityID)) Throw.Group_AlreadyContains(entityID); #endif if (++_count >= _dense.Length) Array.Resize(ref _dense, _dense.Length << 1); @@ -218,7 +218,7 @@ namespace DCFApixels.DragonECS internal void RemoveInternal(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowDoesNotContain(entityID); + if (!Has(entityID)) Throw.Group_DoesNotContain(entityID); #endif _dense[_sparse[entityID]] = _dense[_count]; _sparse[_dense[_count--]] = _sparse[entityID]; @@ -287,7 +287,7 @@ namespace DCFApixels.DragonECS public ReadOnlySpan ToSpan(int start, int length) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (start + length > _count) ThrowArgumentOutOfRangeException(); + if (start + length > _count) Throw.ArgumentOutOfRange(); #endif return new ReadOnlySpan(_dense, start, length); } @@ -301,7 +301,7 @@ namespace DCFApixels.DragonECS public void UnionWith(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_source != group.World) ThrowArgumentDifferentWorldsException(); + if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif foreach (var item in group) if (!Has(item)) @@ -315,7 +315,7 @@ namespace DCFApixels.DragonECS public void ExceptWith(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_source != group.World) ThrowArgumentDifferentWorldsException(); + if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif foreach (var item in this) if (group.Has(item)) @@ -329,7 +329,7 @@ namespace DCFApixels.DragonECS public void IntersectWith(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (World != group.World) ThrowArgumentDifferentWorldsException(); + if (World != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif foreach (var item in this) if (!group.Has(item)) @@ -343,7 +343,7 @@ namespace DCFApixels.DragonECS public void SymmetricExceptWith(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_source != group.World) ThrowArgumentDifferentWorldsException(); + if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif foreach (var item in group) if (Has(item)) @@ -367,7 +367,7 @@ namespace DCFApixels.DragonECS public static EcsGroup Union(EcsGroup a, EcsGroup b) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (a._source != b._source) ThrowArgumentDifferentWorldsException(); + if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException(); #endif EcsGroup result = a._source.GetFreeGroup(); foreach (var item in a) @@ -381,7 +381,7 @@ namespace DCFApixels.DragonECS public static EcsGroup Except(EcsGroup a, EcsGroup b) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (a._source != b._source) ThrowArgumentDifferentWorldsException(); + if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException(); #endif EcsGroup result = a._source.GetFreeGroup(); foreach (var item in a) @@ -394,7 +394,7 @@ namespace DCFApixels.DragonECS public static EcsGroup Intersect(EcsGroup a, EcsGroup b) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (a._source != b._source) ThrowArgumentDifferentWorldsException(); + if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException(); #endif EcsGroup result = a._source.GetFreeGroup(); foreach (var item in a) @@ -408,7 +408,7 @@ namespace DCFApixels.DragonECS public static EcsGroup SymmetricExcept(EcsGroup a, EcsGroup b) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (a._source != b._source) ThrowArgumentDifferentWorldsException(); + if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException(); #endif EcsGroup result = a._source.GetFreeGroup(); foreach (var item in a) @@ -519,24 +519,6 @@ namespace DCFApixels.DragonECS } #endregion - #region ThrowHalper -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - internal static class ThrowHelper - { - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowAlreadyContains(int entityID) => throw new EcsFrameworkException($"This group already contains entity {entityID}."); - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowArgumentOutOfRange() => throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count."); - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowDoesNotContain(int entityID) => throw new EcsFrameworkException($"This group does not contain entity {entityID}."); - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(); - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowArgumentDifferentWorldsException() => throw new ArgumentException("The groups belong to different worlds."); - } -#endif - #endregion - #region DebuggerProxy internal class DebuggerProxy { diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 5f915df..a06e1c5 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.CompilerServices; +using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { @@ -86,19 +87,19 @@ namespace DCFApixels.DragonECS public void Run() { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - CheckBeforeInitForMethod(nameof(Run)); - CheckAfterDestroyForMethod(nameof(Run)); + if (_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Run)); + if (_isDestoryed) Throw.Pipeline_MethodCalledAfterDestruction(nameof(Run)); #endif _runRunnerCache.Run(this); } public void Destroy() { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - CheckBeforeInitForMethod(nameof(Run)); + if (_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Destroy)); #endif if (_isDestoryed == true) { - EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been destroyed"); + EcsDebug.PrintWarning($"This {nameof(EcsPipeline)} has already been destroyed"); return; } _isDestoryed = true; @@ -106,26 +107,6 @@ namespace DCFApixels.DragonECS } #endregion - #region StateChecks -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - private void CheckBeforeInitForMethod(string methodName) - { - if (!_isInit) - throw new MethodAccessException($"It is forbidden to call {methodName}, before initialization {nameof(EcsPipeline)}"); - } - private void CheckAfterInitForMethod(string methodName) - { - if (_isInit) - throw new MethodAccessException($"It is forbidden to call {methodName}, after initialization {nameof(EcsPipeline)}"); - } - private void CheckAfterDestroyForMethod(string methodName) - { - if (_isDestoryed) - throw new MethodAccessException($"It is forbidden to call {methodName}, after destroying {nameof(EcsPipeline)}"); - } -#endif - #endregion - #region Builder public static Builder New() => new Builder(); public class Builder diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index a86185a..7e30725 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -73,8 +73,7 @@ namespace DCFApixels.DragonECS } if (delayedExceptions.Count > 0) { - foreach (var item in delayedExceptions) EcsDebug.Print(EcsConsts.DEBUG_ERROR_TAG, item.Message); - throw delayedExceptions[0]; + throw new AggregateException(delayedExceptions); } } diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 7ab71dc..e062288 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -3,6 +3,7 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { @@ -301,7 +302,7 @@ namespace DCFApixels.DragonECS var count = --_componentCounts[entityID]; if (count == 0 && _allEntites.Has(entityID)) DelEntity(entityID); #if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS - if (count < 0) throw new EcsFrameworkException("нарушен баланс инкремента/декремента компонентов"); + if (count < 0) Throw.World_InvalidIncrementComponentsBalance(); #endif } #endregion @@ -322,8 +323,7 @@ namespace DCFApixels.DragonECS internal void ReleaseGroup(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS - if (group.World != this) - throw new ArgumentException("groupFilter.WorldIndex != this"); + if (group.World != this) Throw.World_GroupDoesNotBelongWorld(); #endif group._isReleased = true; group.Clear(); @@ -365,8 +365,6 @@ namespace DCFApixels.DragonECS #endregion } - internal sealed class EcsNullWorld : EcsWorld { } - #region Callbacks Interface public interface IEcsWorldEventListener { diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index d5e69af..cf61e9f 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -97,4 +97,5 @@ namespace DCFApixels.DragonECS } } } + internal sealed class EcsNullWorld : EcsWorld { } } diff --git a/src/Utils/Exceptions.cs b/src/Utils/Exceptions.cs index a965542..7cd999d 100644 --- a/src/Utils/Exceptions.cs +++ b/src/Utils/Exceptions.cs @@ -1,8 +1,104 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.Serialization; namespace DCFApixels.DragonECS { + public class EcsThrowHalper + { + public static readonly EcsThrowHalper Throw = new EcsThrowHalper(); + private EcsThrowHalper() { } + } + + public static class EcsThrowHalper_Core + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Pool_AlreadyHasComponent(this EcsThrowHalper _, int entityID) + { + throw new EcsFrameworkException($"Entity({entityID}) already has component {EcsDebugUtility.GetGenericTypeName()}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Pool_NotHaveComponent(this EcsThrowHalper _, int entityID) + { + throw new EcsFrameworkException($"Entity({entityID}) has no component {EcsDebugUtility.GetGenericTypeName()}."); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ArgumentNull(this EcsThrowHalper _) + { + throw new ArgumentNullException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ConstraintIsAlreadyContainedInMask(this EcsThrowHalper _, Type type) + { + throw new EcsFrameworkException($"The {EcsDebugUtility.GetGenericTypeName(type)} constraint is already contained in the mask."); + } + + //[MethodImpl(MethodImplOptions.NoInlining)] + //public static void ArgumentDifferentWorldsException() + //{ + // throw new ArgumentException("The groups belong to different worlds."); + //} + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ArgumentOutOfRange(this EcsThrowHalper _) + { + throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count."); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Group_AlreadyContains(this EcsThrowHalper _, int entityID) + { + throw new EcsFrameworkException($"This group already contains entity {entityID}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Group_DoesNotContain(this EcsThrowHalper _, int entityID) + { + throw new EcsFrameworkException($"This group does not contain entity {entityID}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Group_ArgumentDifferentWorldsException(this EcsThrowHalper _) + { + throw new ArgumentException("The groups belong to different worlds."); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Pipeline_MethodCalledAfterInitialisation(this EcsThrowHalper _, string methodName) + { + throw new MethodAccessException($"It is forbidden to call {methodName}, after initialization {nameof(EcsPipeline)}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Pipeline_MethodCalledBeforeInitialisation(this EcsThrowHalper _, string methodName) + { + throw new MethodAccessException($"It is forbidden to call {methodName}, before initialization {nameof(EcsPipeline)}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Pipeline_MethodCalledAfterDestruction(this EcsThrowHalper _, string methodName) + { + throw new MethodAccessException($"It is forbidden to call {methodName}, after destroying {nameof(EcsPipeline)}."); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void World_InvalidIncrementComponentsBalance(this EcsThrowHalper _) + { + throw new MethodAccessException("Invalid increment components balance."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void World_GroupDoesNotBelongWorld(this EcsThrowHalper _) + { + throw new MethodAccessException("The Group does not belong in this world."); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Ent_ThrowIsNotAlive(this EcsThrowHalper _, entlong entity) + { + if (entity.IsNull) + throw new EcsFrameworkException($"The {entity} is null."); + else + throw new EcsFrameworkException($"The {entity} is not alive."); + } + } + [Serializable] public class EcsFrameworkException : Exception { diff --git a/src/entlong.cs b/src/entlong.cs index 2a4430a..ff49d76 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using static DCFApixels.DragonECS.entlong.ThrowHalper; +using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { @@ -33,7 +33,7 @@ namespace DCFApixels.DragonECS public bool IsNull { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => full == 0l; + get => full == 0L; } public int ID { @@ -41,7 +41,7 @@ namespace DCFApixels.DragonECS get { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!IsAlive) ThrowIsNotAlive(this); + if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this); #endif return id; } @@ -52,7 +52,7 @@ namespace DCFApixels.DragonECS get { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!IsAlive) ThrowIsNotAlive(this); + if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this); #endif return gen; } @@ -63,7 +63,7 @@ namespace DCFApixels.DragonECS get { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!IsAlive) ThrowIsNotAlive(this); + if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this); #endif return EcsWorld.GetWorld(world); } @@ -74,7 +74,7 @@ namespace DCFApixels.DragonECS get { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!IsAlive) ThrowIsNotAlive(this); + if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this); #endif return world; } @@ -143,20 +143,6 @@ namespace DCFApixels.DragonECS public static explicit operator entlong(in long a) => new entlong(a); #endregion - #region ThrowHalper - internal static class ThrowHalper - { - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowIsNotAlive(entlong entity) - { - if (entity.IsNull) - throw new EcsFrameworkException($"The {entity} is null."); - else - throw new EcsFrameworkException($"The {entity} is not alive."); - } - } - #endregion - #region DebuggerProxy internal class DebuggerProxy { From 9a6010efdbbec549fabc1699d415d18d4ff6a900 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 26 Jun 2023 02:53:55 +0800 Subject: [PATCH 004/104] update exceptions --- src/Builtin/InjectSystem.cs | 1 - src/EcsAspect.cs | 5 +- src/EcsGroup.cs | 8 +- src/EcsPipeline.cs | 18 ++--- src/EcsWorld.cs | 1 - src/Pools/EcsPool.cs | 13 ++- src/Pools/EcsPoolBase.cs | 5 +- src/Pools/EcsTagPool.cs | 17 ++-- src/Utils/Exceptions.cs | 156 ++++++++++++++++-------------------- src/entlong.cs | 2 +- 10 files changed, 100 insertions(+), 126 deletions(-) diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index 03c6a7c..fdac12e 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -2,7 +2,6 @@ using DCFApixels.DragonECS.RunnersCore; using System; using System.Linq; -using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 0ff7038..adb37b3 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -1,12 +1,11 @@ -using System; +using DCFApixels.DragonECS.Internal; +using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; -using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 5886d20..cd8e834 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -1,13 +1,11 @@ -using System; -using System.Linq; +using DCFApixels.DragonECS.Internal; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS -using static DCFApixels.DragonECS.EcsThrowHalper; -#endif namespace DCFApixels.DragonECS { diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index a06e1c5..6c39ded 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -6,21 +6,20 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.CompilerServices; -using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { public sealed class EcsPipeline { private IEcsProcess[] _allSystems; - private Dictionary _runners; + private Dictionary _runners = new Dictionary(); private IEcsRunProcess _runRunnerCache; private ReadOnlyCollection _allSystemsSealed; private ReadOnlyDictionary _allRunnersSealed; - private bool _isInit; - private bool _isDestoryed; + private bool _isInit = false; + private bool _isDestoryed = false; #region Properties public ReadOnlyCollection AllSystems => _allSystemsSealed; @@ -33,13 +32,8 @@ namespace DCFApixels.DragonECS private EcsPipeline(IEcsProcess[] systems) { _allSystems = systems; - _runners = new Dictionary(); - _allSystemsSealed = new ReadOnlyCollection(_allSystems); _allRunnersSealed = new ReadOnlyDictionary(_runners); - - _isInit = false; - _isDestoryed = false; } #endregion @@ -87,7 +81,7 @@ namespace DCFApixels.DragonECS public void Run() { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Run)); + if (!_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Run)); if (_isDestoryed) Throw.Pipeline_MethodCalledAfterDestruction(nameof(Run)); #endif _runRunnerCache.Run(this); @@ -95,9 +89,9 @@ namespace DCFApixels.DragonECS public void Destroy() { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Destroy)); + if (!_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Destroy)); #endif - if (_isDestoryed == true) + if (_isDestoryed) { EcsDebug.PrintWarning($"This {nameof(EcsPipeline)} has already been destroyed"); return; diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index e062288..dc9f5d6 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -3,7 +3,6 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 131b2ff..ac14c63 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using static DCFApixels.DragonECS.EcsPoolThrowHalper; namespace DCFApixels.DragonECS { @@ -53,7 +52,7 @@ namespace DCFApixels.DragonECS { ref int itemIndex = ref _mapping[entityID]; #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (itemIndex > 0) ThrowAlreadyHasComponent(entityID); + if (itemIndex > 0) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID); #endif if (_recycledItemsCount > 0) { @@ -74,7 +73,7 @@ namespace DCFApixels.DragonECS public ref T Get(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif _listeners.InvokeOnGet(entityID); return ref _items[_mapping[entityID]]; @@ -83,7 +82,7 @@ namespace DCFApixels.DragonECS public ref readonly T Read(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif return ref _items[_mapping[entityID]]; } @@ -117,7 +116,7 @@ namespace DCFApixels.DragonECS public void Del(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif ref int itemIndex = ref _mapping[entityID]; _componentResetHandler.Reset(ref _items[itemIndex]); @@ -136,14 +135,14 @@ namespace DCFApixels.DragonECS public void Copy(int fromEntityID, int toEntityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); #endif _componentCopyHandler.Copy(ref Get(fromEntityID), ref TryAddOrGet(toEntityID)); } public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); #endif _componentCopyHandler.Copy(ref Get(fromEntityID), ref toWorld.GetPool().TryAddOrGet(toEntityID)); } diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index f07681f..6237eda 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -1,4 +1,5 @@ using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -57,11 +58,11 @@ namespace DCFApixels.DragonECS { public static void ThrowAlreadyHasComponent(int entityID) { - throw new EcsFrameworkException($"Entity({entityID}) already has component {typeof(T).Name}."); + throw new EcsFrameworkException($"Entity({entityID}) already has component {EcsDebugUtility.GetGenericTypeName()}."); } public static void ThrowNotHaveComponent(int entityID) { - throw new EcsFrameworkException($"Entity({entityID}) has no component {typeof(T).Name}."); + throw new EcsFrameworkException($"Entity({entityID}) has no component {EcsDebugUtility.GetGenericTypeName()}."); } } public static class IEcsPoolImplementationExtensions diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index a435a19..69f45a2 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using static DCFApixels.DragonECS.EcsPoolThrowHalper; namespace DCFApixels.DragonECS { @@ -42,7 +41,7 @@ namespace DCFApixels.DragonECS public void Add(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (Has(entityID)) ThrowAlreadyHasComponent(entityID); + if (Has(entityID)) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID); #endif _count++; _mapping[entityID] = true; @@ -67,7 +66,7 @@ namespace DCFApixels.DragonECS public void Del(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif _mapping[entityID] = false; _count--; @@ -81,14 +80,14 @@ namespace DCFApixels.DragonECS public void Copy(int fromEntityID, int toEntityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); #endif TryAdd(toEntityID); } public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); #endif toWorld.GetPool().TryAdd(toEntityID); } @@ -137,14 +136,14 @@ namespace DCFApixels.DragonECS ref readonly T IEcsStructPool.Read(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif return ref _fakeComponent; } ref T IEcsStructPool.Get(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif return ref _fakeComponent; } @@ -152,14 +151,14 @@ namespace DCFApixels.DragonECS object IEcsPool.GetRaw(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif return _fakeComponent; } void IEcsPool.SetRaw(int entityID, object dataRaw) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif } #endregion diff --git a/src/Utils/Exceptions.cs b/src/Utils/Exceptions.cs index 7cd999d..ed4e955 100644 --- a/src/Utils/Exceptions.cs +++ b/src/Utils/Exceptions.cs @@ -4,98 +4,84 @@ using System.Runtime.Serialization; namespace DCFApixels.DragonECS { - public class EcsThrowHalper + namespace Internal { - public static readonly EcsThrowHalper Throw = new EcsThrowHalper(); - private EcsThrowHalper() { } - } + internal static class Throw + { + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ArgumentNull() + { + throw new ArgumentNullException(); + } - public static class EcsThrowHalper_Core - { - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Pool_AlreadyHasComponent(this EcsThrowHalper _, int entityID) - { - throw new EcsFrameworkException($"Entity({entityID}) already has component {EcsDebugUtility.GetGenericTypeName()}."); - } - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Pool_NotHaveComponent(this EcsThrowHalper _, int entityID) - { - throw new EcsFrameworkException($"Entity({entityID}) has no component {EcsDebugUtility.GetGenericTypeName()}."); - } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ConstraintIsAlreadyContainedInMask(Type type) + { + throw new EcsFrameworkException($"The {EcsDebugUtility.GetGenericTypeName(type)} constraint is already contained in the mask."); + } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void ArgumentNull(this EcsThrowHalper _) - { - throw new ArgumentNullException(); - } + //[MethodImpl(MethodImplOptions.NoInlining)] + //public static void ArgumentDifferentWorldsException() + //{ + // throw new ArgumentException("The groups belong to different worlds."); + //} + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ArgumentOutOfRange() + { + throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count."); + } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void ConstraintIsAlreadyContainedInMask(this EcsThrowHalper _, Type type) - { - throw new EcsFrameworkException($"The {EcsDebugUtility.GetGenericTypeName(type)} constraint is already contained in the mask."); - } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Group_AlreadyContains(int entityID) + { + throw new EcsFrameworkException($"This group already contains entity {entityID}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Group_DoesNotContain(int entityID) + { + throw new EcsFrameworkException($"This group does not contain entity {entityID}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Group_ArgumentDifferentWorldsException() + { + throw new ArgumentException("The groups belong to different worlds."); + } - //[MethodImpl(MethodImplOptions.NoInlining)] - //public static void ArgumentDifferentWorldsException() - //{ - // throw new ArgumentException("The groups belong to different worlds."); - //} - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void ArgumentOutOfRange(this EcsThrowHalper _) - { - throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count."); - } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Pipeline_MethodCalledAfterInitialisation(string methodName) + { + throw new MethodAccessException($"It is forbidden to call {methodName}, after initialization {nameof(EcsPipeline)}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Pipeline_MethodCalledBeforeInitialisation(string methodName) + { + throw new MethodAccessException($"It is forbidden to call {methodName}, before initialization {nameof(EcsPipeline)}."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Pipeline_MethodCalledAfterDestruction(string methodName) + { + throw new MethodAccessException($"It is forbidden to call {methodName}, after destroying {nameof(EcsPipeline)}."); + } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Group_AlreadyContains(this EcsThrowHalper _, int entityID) - { - throw new EcsFrameworkException($"This group already contains entity {entityID}."); - } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Group_DoesNotContain(this EcsThrowHalper _, int entityID) - { - throw new EcsFrameworkException($"This group does not contain entity {entityID}."); - } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Group_ArgumentDifferentWorldsException(this EcsThrowHalper _) - { - throw new ArgumentException("The groups belong to different worlds."); - } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void World_InvalidIncrementComponentsBalance() + { + throw new MethodAccessException("Invalid increment components balance."); + } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void World_GroupDoesNotBelongWorld() + { + throw new MethodAccessException("The Group does not belong in this world."); + } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Pipeline_MethodCalledAfterInitialisation(this EcsThrowHalper _, string methodName) - { - throw new MethodAccessException($"It is forbidden to call {methodName}, after initialization {nameof(EcsPipeline)}."); - } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Pipeline_MethodCalledBeforeInitialisation(this EcsThrowHalper _, string methodName) - { - throw new MethodAccessException($"It is forbidden to call {methodName}, before initialization {nameof(EcsPipeline)}."); - } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Pipeline_MethodCalledAfterDestruction(this EcsThrowHalper _, string methodName) - { - throw new MethodAccessException($"It is forbidden to call {methodName}, after destroying {nameof(EcsPipeline)}."); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void World_InvalidIncrementComponentsBalance(this EcsThrowHalper _) - { - throw new MethodAccessException("Invalid increment components balance."); - } - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void World_GroupDoesNotBelongWorld(this EcsThrowHalper _) - { - throw new MethodAccessException("The Group does not belong in this world."); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - internal static void Ent_ThrowIsNotAlive(this EcsThrowHalper _, entlong entity) - { - if (entity.IsNull) - throw new EcsFrameworkException($"The {entity} is null."); - else - throw new EcsFrameworkException($"The {entity} is not alive."); + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Ent_ThrowIsNotAlive(entlong entity) + { + if (entity.IsNull) + throw new EcsFrameworkException($"The {entity} is null."); + else + throw new EcsFrameworkException($"The {entity} is not alive."); + } } } diff --git a/src/entlong.cs b/src/entlong.cs index ff49d76..8f54c97 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -1,10 +1,10 @@ #pragma warning disable IDE1006 +using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using static DCFApixels.DragonECS.EcsThrowHalper; namespace DCFApixels.DragonECS { From f7a0495c874a1a726e3b067883b002e840b076cc Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 26 Jun 2023 04:04:00 +0800 Subject: [PATCH 005/104] remove useless --- src/Utils/WorldMetaStorage.cs | 50 ----------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/Utils/WorldMetaStorage.cs b/src/Utils/WorldMetaStorage.cs index da3b563..49e13a5 100644 --- a/src/Utils/WorldMetaStorage.cs +++ b/src/Utils/WorldMetaStorage.cs @@ -52,10 +52,6 @@ namespace DCFApixels.DragonECS public static int GetAspectID(int worldID) => Aspect.Get(worldID); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetExecutorID(int worldID) => Executor.Get(worldID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetWorldComponentID(int worldID) => WorldComponent.Get(worldID); - public static int GetWorldComponentID(Type type, int worldID) => _metas[worldID].GetWorldComponentID(type); - private abstract class ResizerBase { @@ -219,36 +215,6 @@ namespace DCFApixels.DragonECS } } } - private static class WorldComponent - { - public static int[] ids; - static WorldComponent() - { - ids = new int[_tokenCount]; - for (int i = 0; i < ids.Length; i++) - ids[i] = -1; - _resizers.Add(new Resizer()); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Get(int token) - { - ref int id = ref ids[token]; - if (id < 0) - id = _metas[token].GetWorldComponentID(typeof(T)); - return id; - } - private sealed class Resizer : ResizerBase - { - public override Type Type => typeof(T); - public override int[] IDS => ids; - public override void Resize(int size) - { - int oldSize = ids.Length; - Array.Resize(ref ids, size); - ArrayUtility.Fill(ids, -1, oldSize, size); - } - } - } #endregion private class WorldTypeMeta { @@ -260,7 +226,6 @@ namespace DCFApixels.DragonECS public int worldComponentCount; private Type[] _types = new Type[10]; private Dictionary _declaredComponentTypes = new Dictionary(); - private Dictionary _declaredWorldComponentTypes = new Dictionary(); public WorldTypeMeta(Type worldType) { @@ -279,21 +244,6 @@ namespace DCFApixels.DragonECS public bool IsDeclaredComponentType(Type type) => _declaredComponentTypes.ContainsKey(type); public Type GetComponentType(int componentID) => _types[componentID]; public int GetComponentID(Type type) => PoolComponentIdArrays.GetComponentID(type, id); - - - public int DeclareWorldComponentType(Type type) - { - int id = worldComponentCount++; - _declaredWorldComponentTypes.Add(type, id); - return id; - } - public bool IsDeclaredWorldComponentType(Type type) => _declaredWorldComponentTypes.ContainsKey(type); - public int GetWorldComponentID(Type type) - { - if (!_declaredWorldComponentTypes.TryGetValue(type, out int id)) - id = DeclareWorldComponentType(type); - return id; - } } } } From f3b7841ffe9b436230144cd8523b4d4e2979435b Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 01:21:52 +0800 Subject: [PATCH 006/104] hot fix --- src/DataInterfaces.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataInterfaces.cs b/src/DataInterfaces.cs index 474bffe..f64125c 100644 --- a/src/DataInterfaces.cs +++ b/src/DataInterfaces.cs @@ -20,7 +20,7 @@ namespace DCFApixels.DragonECS isHasHandler = targetType.GetInterfaces().Contains(typeof(IEcsWorldComponent<>).MakeGenericType(targetType)); if (isHasHandler) { - instance = (IEcsWorldComponent)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType)); + instance = (IEcsWorldComponent)Activator.CreateInstance(typeof(WorldComponentHandler<>).MakeGenericType(targetType)); } else { From 35d71245bc1de44a694f385fda24f4bce60b45ec Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 01:23:01 +0800 Subject: [PATCH 007/104] replace int with short for indexing world components --- src/EcsWorld.cs | 2 ++ src/EcsWorld.static.cs | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index dc9f5d6..6e8e334 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -142,6 +142,8 @@ namespace DCFApixels.DragonECS } return (TExecutor)result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get() where T : struct => ref WorldComponentPool.GetForWorld(id); #endregion diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index cf61e9f..c131c80 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -47,10 +47,10 @@ namespace DCFApixels.DragonECS private static class WorldComponentPool { private static T[] _items = new T[4]; - private static int[] _mapping = new int[4]; - private static int _count; - private static int[] _recycledItems = new int[4]; - private static int _recycledItemsCount; + private static short[] _mapping = new short[4]; + private static short _count; + private static short[] _recycledItems = new short[4]; + private static short _recycledItemsCount; private static IEcsWorldComponent _interface = EcsWorldComponentHandler.instance; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -62,7 +62,7 @@ namespace DCFApixels.DragonECS if (_mapping.Length < Worlds.Length) Array.Resize(ref _mapping, Worlds.Length); - ref int itemIndex = ref _mapping[worldID]; + ref short itemIndex = ref _mapping[worldID]; if (itemIndex <= 0) { if (_recycledItemsCount > 0) @@ -79,9 +79,10 @@ namespace DCFApixels.DragonECS } return itemIndex; } + private static void Release(int worldID) { - ref int itemIndex = ref _mapping[worldID]; + ref short itemIndex = ref _mapping[worldID]; if (itemIndex != 0) { _interface.OnDestroy(ref _items[itemIndex], Worlds[worldID]); From 1b693863078b27ab1917214adbb058a780770888 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 02:51:51 +0800 Subject: [PATCH 008/104] Update README-RU.md --- README-RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index 6395b63..6c2493a 100644 --- a/README-RU.md +++ b/README-RU.md @@ -14,7 +14,7 @@ | :--- | :--- | :--- | Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. -> **NOTICE:** Проект в стадии разработки. API может меняться. +> **Warning**: Проект в стадии разработки. API может меняться. > Readme еще не завершен ## Оглавление From 9ebf7bc023219b61cf27bd964d9fdc1cca5c2f03 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 02:52:11 +0800 Subject: [PATCH 009/104] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc8f4f2..fc32f17 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ | :--- | :--- | :--- | The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies -> **NOTICE:** The project is a work in progress, API may change. +> **Warning**: The project is a work in progress, API may change. > While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md). # Versioning From 51b409d5d65fd0b450304982a2f224ccc05164ff Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 05:09:41 +0800 Subject: [PATCH 010/104] GetPool optimisation GetPool almost 2x faster. --- src/EcsAspect.cs | 49 +++++++++++++++++------------------ src/EcsWorld.cache.cs | 40 ++++++++++++++++++++++++++++ src/EcsWorld.cs | 27 ++++++------------- src/Utils/EcsTypeCodeCache.cs | 28 ++++++++++++++++++++ 4 files changed, 100 insertions(+), 44 deletions(-) create mode 100644 src/EcsWorld.cache.cs create mode 100644 src/Utils/EcsTypeCodeCache.cs diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index adb37b3..a65d555 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -123,10 +123,10 @@ namespace DCFApixels.DragonECS foreach (var item in _combined) { EcsMask submask = item.aspect.mask; - maskInc.ExceptWith(submask._exc);//удаляю конфликтующие ограничения - maskExc.ExceptWith(submask._inc);//удаляю конфликтующие ограничения - maskInc.UnionWith(submask._inc); - maskExc.UnionWith(submask._exc); + maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения + maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения + maskInc.UnionWith(submask.inc); + maskExc.UnionWith(submask.exc); } maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения @@ -144,7 +144,7 @@ namespace DCFApixels.DragonECS var exc = maskExc.ToArray(); Array.Sort(exc); - mask = new EcsMask(_world.WorldTypeID, inc, exc); + mask = new EcsMask(_world.id, inc, exc); _world = null; _inc = null; _exc = null; @@ -202,24 +202,23 @@ namespace DCFApixels.DragonECS [DebuggerTypeProxy(typeof(DebuggerProxy))] public sealed class EcsMask { - internal readonly int _worldTypeID; + internal readonly int worldID; /// Including constraints - internal readonly int[] _inc; + internal readonly int[] inc; /// Excluding constraints - internal readonly int[] _exc; - internal EcsMask(int worldTypeID, int[] inc, int[] exc) + internal readonly int[] exc; + internal EcsMask(int worldID, int[] inc, int[] exc) { #if DEBUG - if (worldTypeID == 0) throw new ArgumentException(); CheckConstraints(inc, exc); #endif - _worldTypeID = worldTypeID; - _inc = inc; - _exc = exc; + this.worldID = worldID; + this.inc = inc; + this.exc = exc; } #region Object - public override string ToString() => CreateLogString(_worldTypeID, _inc, _exc); + public override string ToString() => CreateLogString(worldID, inc, exc); #endregion #region Debug utils @@ -247,10 +246,10 @@ namespace DCFApixels.DragonECS return false; } #endif - private static string CreateLogString(int worldTypeID, int[] inc, int[] exc) + private static string CreateLogString(int worldID, int[] inc, int[] exc) { #if (DEBUG && !DISABLE_DEBUG) - string converter(int o) => EcsDebugUtility.GetGenericTypeName(WorldMetaStorage.GetComponentType(worldTypeID, o), 1); + string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 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 @@ -259,22 +258,22 @@ namespace DCFApixels.DragonECS internal class DebuggerProxy { public readonly Type worldType; - public readonly int worldTypeID; + public readonly int worldID; public readonly int[] included; public readonly int[] excluded; public readonly Type[] includedTypes; public readonly Type[] excludedTypes; public DebuggerProxy(EcsMask mask) { - worldType = WorldMetaStorage.GetWorldType(mask._worldTypeID); - worldTypeID = mask._worldTypeID; - included = mask._inc; - excluded = mask._exc; - Type converter(int o) => WorldMetaStorage.GetComponentType(worldTypeID, o); + worldType = WorldMetaStorage.GetWorldType(mask.worldID); + worldID = mask.worldID; + included = mask.inc; + excluded = mask.exc; + Type converter(int o) => WorldMetaStorage.GetComponentType(worldID, o); includedTypes = included.Select(converter).ToArray(); excludedTypes = excluded.Select(converter).ToArray(); } - public override string ToString() => CreateLogString(worldTypeID, included, excluded); + public override string ToString() => CreateLogString(worldID, included, excluded); } #endregion } @@ -338,8 +337,8 @@ namespace DCFApixels.DragonECS public Enumerator(EcsReadonlyGroup sourceGroup, EcsMask mask) { _sourceGroup = sourceGroup.GetEnumerator(); - _inc = mask._inc; - _exc = mask._exc; + _inc = mask.inc; + _exc = mask.exc; _pools = sourceGroup.World._pools; } public int Current diff --git a/src/EcsWorld.cache.cs b/src/EcsWorld.cache.cs new file mode 100644 index 0000000..56ae21c --- /dev/null +++ b/src/EcsWorld.cache.cs @@ -0,0 +1,40 @@ +using DCFApixels.DragonECS.Utils; +using System; + +namespace DCFApixels.DragonECS +{ + public partial class EcsWorld + { + internal readonly struct PoolCache : IEcsWorldComponent> + where TPool : IEcsPoolImplementation, new() + { + public readonly TPool instance; + public PoolCache(TPool instance) => this.instance = instance; + void IEcsWorldComponent>.Init(ref PoolCache component, EcsWorld world) + { + component = new PoolCache(world.CreatePool()); + } + void IEcsWorldComponent>.OnDestroy(ref PoolCache component, EcsWorld world) + { + component = default; + } + } + private TPool CreatePool() where TPool : IEcsPoolImplementation, new() + { + int index = WorldMetaStorage.GetPoolID(_worldTypeID); + if (index >= _pools.Length) + { + int oldCapacity = _pools.Length; + Array.Resize(ref _pools, _pools.Length << 1); + ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length); + } + if (_pools[index] == _nullPool) + { + var pool = new TPool(); + _pools[index] = pool; + pool.OnInit(this, index); + } + return (TPool)_pools[index]; + } + } +} diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 6e8e334..d3f2c21 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -3,6 +3,7 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using typecode = System.Int32; namespace DCFApixels.DragonECS { @@ -102,22 +103,10 @@ namespace DCFApixels.DragonECS #if UNITY_2020_3_OR_NEWER [UnityEngine.Scripting.Preserve] #endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] public TPool GetPool() where TPool : IEcsPoolImplementation, new() { - int index = WorldMetaStorage.GetPoolID(_worldTypeID); - if (index >= _pools.Length) - { - int oldCapacity = _pools.Length; - Array.Resize(ref _pools, _pools.Length << 1); - ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length); - } - if (_pools[index] == _nullPool) - { - var pool = new TPool(); - _pools[index] = pool; - pool.OnInit(this, index); - } - return (TPool)_pools[index]; + return Get>().instance; } public TAspect GetAspect() where TAspect : EcsAspect { @@ -236,17 +225,17 @@ namespace DCFApixels.DragonECS public bool IsMatchesMask(EcsMask mask, int entityID) { #if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS - if (mask._worldTypeID != _worldTypeID) + if (mask.worldID != id) throw new EcsFrameworkException("The types of the target world of the mask and this world are different."); #endif - for (int i = 0, iMax = mask._inc.Length; i < iMax; i++) + for (int i = 0, iMax = mask.inc.Length; i < iMax; i++) { - if (!_pools[mask._inc[i]].Has(entityID)) + if (!_pools[mask.inc[i]].Has(entityID)) return false; } - for (int i = 0, iMax = mask._exc.Length; i < iMax; i++) + for (int i = 0, iMax = mask.exc.Length; i < iMax; i++) { - if (_pools[mask._exc[i]].Has(entityID)) + if (_pools[mask.exc[i]].Has(entityID)) return false; } return true; diff --git a/src/Utils/EcsTypeCodeCache.cs b/src/Utils/EcsTypeCodeCache.cs new file mode 100644 index 0000000..c60c241 --- /dev/null +++ b/src/Utils/EcsTypeCodeCache.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace DCFApixels.DragonECS +{ + namespace Internal + { + internal static class EcsTypeCode + { + private static readonly Dictionary _codes = new Dictionary(); + private static int _incremetn = 1; + public static int GetCode(Type type) + { + if (!_codes.TryGetValue(type, out int code)) + { + code = _incremetn++; + _codes.Add(type, code); + } + return code; + } + public static int Count => _codes.Count; + } + internal static class EcsTypeCodeCache + { + public static readonly int code = EcsTypeCode.GetCode(typeof(T)); + } + } +} From cd1da0253a71d2958c06e049970f1f952e286a30 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 05:30:45 +0800 Subject: [PATCH 011/104] optimisation --- src/EcsWorld.cache.cs | 45 +++++++++++++++++++++++++++++++++------- src/EcsWorld.cs | 29 ++++---------------------- src/Pools/EcsPoolBase.cs | 1 - 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/src/EcsWorld.cache.cs b/src/EcsWorld.cache.cs index 56ae21c..6862d0b 100644 --- a/src/EcsWorld.cache.cs +++ b/src/EcsWorld.cache.cs @@ -1,20 +1,21 @@ using DCFApixels.DragonECS.Utils; using System; +using System.Reflection; namespace DCFApixels.DragonECS { public partial class EcsWorld { - internal readonly struct PoolCache : IEcsWorldComponent> - where TPool : IEcsPoolImplementation, new() + internal readonly struct PoolCache : IEcsWorldComponent> + where T : IEcsPoolImplementation, new() { - public readonly TPool instance; - public PoolCache(TPool instance) => this.instance = instance; - void IEcsWorldComponent>.Init(ref PoolCache component, EcsWorld world) + public readonly T instance; + public PoolCache(T instance) => this.instance = instance; + void IEcsWorldComponent>.Init(ref PoolCache component, EcsWorld world) { - component = new PoolCache(world.CreatePool()); + component = new PoolCache(world.CreatePool()); } - void IEcsWorldComponent>.OnDestroy(ref PoolCache component, EcsWorld world) + void IEcsWorldComponent>.OnDestroy(ref PoolCache component, EcsWorld world) { component = default; } @@ -36,5 +37,35 @@ namespace DCFApixels.DragonECS } return (TPool)_pools[index]; } + internal readonly struct AspectCache : IEcsWorldComponent> + where T : EcsAspect + { + public readonly T instance; + public AspectCache(T instance) => this.instance = instance; + void IEcsWorldComponent>.Init(ref AspectCache component, EcsWorld world) + { + component = new AspectCache(EcsAspect.Builder.Build(world)); + } + void IEcsWorldComponent>.OnDestroy(ref AspectCache component, EcsWorld world) + { + component = default; + } + } + internal readonly struct ExcecutorCache : IEcsWorldComponent> + where T : EcsQueryExecutor, new() + { + public readonly T instance; + public ExcecutorCache(T instance) => this.instance = instance; + void IEcsWorldComponent>.Init(ref ExcecutorCache component, EcsWorld world) + { + T instance = new T(); + instance.Initialize(world); + component = new ExcecutorCache(instance); + } + void IEcsWorldComponent>.OnDestroy(ref ExcecutorCache component, EcsWorld world) + { + component = default; + } + } } } diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index d3f2c21..797abdb 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -27,9 +27,6 @@ namespace DCFApixels.DragonECS internal IEcsPoolImplementation[] _pools; private EcsNullPool _nullPool = EcsNullPool.instance; - private EcsAspect[] _aspects; - private EcsQueryExecutor[] _executors; - private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); @@ -73,9 +70,6 @@ namespace DCFApixels.DragonECS _delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET]; _allEntites = GetFreeGroup(); - - _aspects = new EcsAspect[128]; - _executors = new EcsQueryExecutor[128]; } public void Destroy() { @@ -83,8 +77,6 @@ namespace DCFApixels.DragonECS _gens = null; _pools = null; _nullPool = null; - _aspects = null; - _executors = null; Worlds[id] = null; ReleaseData(id); _worldIdDispenser.Release(id); @@ -108,28 +100,15 @@ namespace DCFApixels.DragonECS { return Get>().instance; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public TAspect GetAspect() where TAspect : EcsAspect { - int index = WorldMetaStorage.GetAspectID(_worldTypeID); - if (index >= _aspects.Length) - Array.Resize(ref _aspects, _aspects.Length << 1); - if (_aspects[index] == null) - _aspects[index] = EcsAspect.Builder.Build(this); - return (TAspect)_aspects[index]; + return Get>().instance; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public TExecutor GetExecutor() where TExecutor : EcsQueryExecutor, new() { - int index = WorldMetaStorage.GetExecutorID(_worldTypeID); - if (index >= _executors.Length) - Array.Resize(ref _executors, _executors.Length << 1); - var result = _executors[index]; - if (result == null) - { - result = new TExecutor(); - _executors[index] = result; - result.Initialize(this); - } - return (TExecutor)result; + return Get>().instance; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 6237eda..459c01f 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -1,5 +1,4 @@ using DCFApixels.DragonECS.Internal; -using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; From 09ce34223973afbb4ccb2f357aa640ef17064295 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 05:33:53 +0800 Subject: [PATCH 012/104] remove useless --- src/Utils/WorldMetaStorage.cs | 67 +---------------------------------- 1 file changed, 1 insertion(+), 66 deletions(-) diff --git a/src/Utils/WorldMetaStorage.cs b/src/Utils/WorldMetaStorage.cs index 49e13a5..85dd1a2 100644 --- a/src/Utils/WorldMetaStorage.cs +++ b/src/Utils/WorldMetaStorage.cs @@ -48,10 +48,6 @@ namespace DCFApixels.DragonECS public static Type GetComponentType(int worldID, int componentID) => _metas[worldID].GetComponentType(componentID); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetPoolID(int worldID) => Pool.Get(worldID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAspectID(int worldID) => Aspect.Get(worldID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetExecutorID(int worldID) => Executor.Get(worldID); private abstract class ResizerBase { @@ -155,74 +151,13 @@ namespace DCFApixels.DragonECS } } } - private static class Aspect - { - public static int[] ids; - static Aspect() - { - ids = new int[_tokenCount]; - for (int i = 0; i < ids.Length; i++) - ids[i] = -1; - _resizers.Add(new Resizer()); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Get(int token) - { - ref int id = ref ids[token]; - if (id < 0) - id = _metas[token].aspectsCount++; - return id; - } - private sealed class Resizer : ResizerBase - { - public override Type Type => typeof(T); - public override int[] IDS => ids; - public override void Resize(int size) - { - int oldSize = ids.Length; - Array.Resize(ref ids, size); - ArrayUtility.Fill(ids, -1, oldSize, size); - } - } - } - private static class Executor - { - public static int[] ids; - static Executor() - { - ids = new int[_tokenCount]; - for (int i = 0; i < ids.Length; i++) - ids[i] = -1; - _resizers.Add(new Resizer()); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Get(int token) - { - ref int id = ref ids[token]; - if (id < 0) - id = _metas[token].executorsCount++; - return id; - } - private sealed class Resizer : ResizerBase - { - public override Type Type => typeof(T); - public override int[] IDS => ids; - public override void Resize(int size) - { - int oldSize = ids.Length; - Array.Resize(ref ids, size); - ArrayUtility.Fill(ids, -1, oldSize, size); - } - } - } #endregion + private class WorldTypeMeta { public readonly Type worldType; public int id; public int componentCount; - public int aspectsCount; - public int executorsCount; public int worldComponentCount; private Type[] _types = new Type[10]; private Dictionary _declaredComponentTypes = new Dictionary(); From 709ecf9cf8773964ef2e21eea3c1bb97ec5dedf7 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 27 Jun 2023 06:01:30 +0800 Subject: [PATCH 013/104] add meta --- src/EcsWorld.cache.cs.meta | 11 +++++++++++ src/Utils/EcsTypeCodeCache.cs.meta | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/EcsWorld.cache.cs.meta create mode 100644 src/Utils/EcsTypeCodeCache.cs.meta diff --git a/src/EcsWorld.cache.cs.meta b/src/EcsWorld.cache.cs.meta new file mode 100644 index 0000000..63d2a9d --- /dev/null +++ b/src/EcsWorld.cache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5cae52accc835594f95c8d972e40c57e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Utils/EcsTypeCodeCache.cs.meta b/src/Utils/EcsTypeCodeCache.cs.meta new file mode 100644 index 0000000..8f99598 --- /dev/null +++ b/src/Utils/EcsTypeCodeCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be4c15a212b2a6941bdbe78f18b038b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 7a5aa6477d22c6a311b6cbd9e8e1061b7f2230ec Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:56:26 +0800 Subject: [PATCH 014/104] update entity copying/EcsTypeCode Api/add IdDispenser/import module with inject --- src/Builtin/InjectSystem.cs | 5 +- src/EcsWorld.cache.cs | 3 +- src/EcsWorld.cs | 23 +- .../{EcsTypeCodeCache.cs => EcsTypeCode.cs} | 8 +- ...eCodeCache.cs.meta => EcsTypeCode.cs.meta} | 0 src/Utils/IdDispenser.cs | 415 ++++++++++++++++++ src/Utils/IdDispenser.cs.meta | 11 + 7 files changed, 456 insertions(+), 9 deletions(-) rename src/Utils/{EcsTypeCodeCache.cs => EcsTypeCode.cs} (81%) rename src/Utils/{EcsTypeCodeCache.cs.meta => EcsTypeCode.cs.meta} (100%) create mode 100644 src/Utils/IdDispenser.cs create mode 100644 src/Utils/IdDispenser.cs.meta diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index fdac12e..47b8b50 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -147,7 +147,10 @@ namespace DCFApixels.DragonECS public static EcsPipeline.Builder Inject(this EcsPipeline.Builder self, T data) { if (data == null) Throw.ArgumentNull(); - return self.Add(new InjectSystem(data)); + self.Add(new InjectSystem(data)); + if (data is IEcsModule module) + self.AddModule(module); + return self; } public static EcsPipeline.Builder Inject(this EcsPipeline.Builder self, A a, B b) { diff --git a/src/EcsWorld.cache.cs b/src/EcsWorld.cache.cs index 6862d0b..2ba0f22 100644 --- a/src/EcsWorld.cache.cs +++ b/src/EcsWorld.cache.cs @@ -1,10 +1,9 @@ using DCFApixels.DragonECS.Utils; using System; -using System.Reflection; namespace DCFApixels.DragonECS { - public partial class EcsWorld + public abstract partial class EcsWorld { internal readonly struct PoolCache : IEcsWorldComponent> where T : IEcsPoolImplementation, new() diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 797abdb..f64521a 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -3,7 +3,6 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using typecode = System.Int32; namespace DCFApixels.DragonECS { @@ -14,6 +13,8 @@ namespace DCFApixels.DragonECS private Type _worldType; private int _worldTypeID; + private bool _isDestroyed; + private IntDispenser _entityDispenser; private int _entitiesCount; private int _entitesCapacity; @@ -34,6 +35,7 @@ namespace DCFApixels.DragonECS private List _entityListeners = new List(); #region Properties + public bool IsDestroyed => _isDestroyed; public int WorldTypeID => _worldTypeID; public int Count => _entitiesCount; public int Capacity => _entitesCapacity; //_denseEntities.Length; @@ -80,6 +82,7 @@ namespace DCFApixels.DragonECS Worlds[id] = null; ReleaseData(id); _worldIdDispenser.Release(id); + _isDestroyed = true; } #endregion @@ -242,7 +245,16 @@ namespace DCFApixels.DragonECS { foreach (var pool in _pools) { - if (pool.Has(fromEntityID)) pool.Copy(fromEntityID, toEntityID); + if (pool.Has(fromEntityID)) + pool.Copy(fromEntityID, toEntityID); + } + } + public void CopyEntity(int fromEntityID, EcsWorld toWorld, int toEntityID) + { + foreach (var pool in _pools) + { + if (pool.Has(fromEntityID)) + pool.Copy(fromEntityID, toWorld, toEntityID); } } public int CloneEntity(int fromEntityID) @@ -251,6 +263,12 @@ namespace DCFApixels.DragonECS CopyEntity(fromEntityID, newEntity); return newEntity; } + public int CloneEntity(int fromEntityID, EcsWorld toWorld) + { + int newEntity = NewEmptyEntity(); + CopyEntity(fromEntityID, toWorld, newEntity); + return newEntity; + } public void CloneEntity(int fromEntityID, int toEntityID) { CopyEntity(fromEntityID, toEntityID); @@ -260,6 +278,7 @@ namespace DCFApixels.DragonECS pool.Del(toEntityID); } } + //public void CloneEntity(int fromEntityID, EcsWorld toWorld, int toEntityID) #endregion #region Components Increment diff --git a/src/Utils/EcsTypeCodeCache.cs b/src/Utils/EcsTypeCode.cs similarity index 81% rename from src/Utils/EcsTypeCodeCache.cs rename to src/Utils/EcsTypeCode.cs index c60c241..77d26f2 100644 --- a/src/Utils/EcsTypeCodeCache.cs +++ b/src/Utils/EcsTypeCode.cs @@ -19,10 +19,10 @@ namespace DCFApixels.DragonECS return code; } public static int Count => _codes.Count; - } - internal static class EcsTypeCodeCache - { - public static readonly int code = EcsTypeCode.GetCode(typeof(T)); + internal static class Cache + { + public static readonly int code = EcsTypeCode.GetCode(typeof(T)); + } } } } diff --git a/src/Utils/EcsTypeCodeCache.cs.meta b/src/Utils/EcsTypeCode.cs.meta similarity index 100% rename from src/Utils/EcsTypeCodeCache.cs.meta rename to src/Utils/EcsTypeCode.cs.meta diff --git a/src/Utils/IdDispenser.cs b/src/Utils/IdDispenser.cs new file mode 100644 index 0000000..beaf822 --- /dev/null +++ b/src/Utils/IdDispenser.cs @@ -0,0 +1,415 @@ +// Sparse Set based ID dispenser, with the ability to reserve IDs. +// Warning! Release version omits error exceptions, incorrect use may lead to unstable state. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace DCFApixels +{ + [Serializable] + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public class IdDispenser : IEnumerable, IReadOnlyCollection + { + private const int MIN_SIZE = 4; + + private int[] _dense = Array.Empty(); + private int[] _sparse = Array.Empty(); + private IDState[] _sparseState = Array.Empty(); + + private int _usedCount; //[ |uuuu| ] + private int _reservedCount; //[rrr| | ] + private int _size; //[rrr|uuuu|ffffff] + + private int _nullID; + + #region Properties + /// Used Count + public int Count => _usedCount; + public int ReservedCount => _reservedCount; + public int Size => _size; + public int NullID => _nullID; + #endregion + + public IdDispenser(int capacity, int nullID = 0) + { + if (capacity % MIN_SIZE > 0) + capacity += MIN_SIZE; + Resize(capacity); + SetNullID(nullID); + + Reserved = new ReservedSpan(this); + Used = new UsedSpan(this); + } + + #region Use/Reserve/Release + /// Marks as used and returns next free id. + public int UseFree() + { + int count = _usedCount + _reservedCount; + CheckOrResize(count + 1); + int id = _dense[count]; + Add(id); + _sparseState[id] = IDState.Used; + return id; + } + public void UseFreeRange(ref int[] array, int range) + { + if (array.Length < range) + Array.Resize(ref array, range); + for (int i = 0; i < range; i++) + array[i] = UseFree(); + } + public void UseFreeRange(List list, int range) + { + for (int i = 0; i < range; i++) + list.Add(UseFree()); + } + /// Marks as used a free or reserved id, after this id cannot be retrieved via UseFree. + public void Use(int id) + { + CheckOrResize(id); +#if DEBUG + if (IsUsed(id) || IsReserved(id)) + { + if (IsUsed(id)) ThrowHalper.ThrowIsAlreadyInUse(id); + else ThrowHalper.ThrowIsHasBeenReserved(id); + } +#endif + if (IsFree(id)) + Add(id); + _sparseState[id] = IDState.Used; + } + public void UseRange(IEnumerable ids) + { + foreach (var item in ids) + Use(item); + } + /// Marks as reserved and returns next free id, after this id cannot be retrieved via UseFree. + public int ReserveFree() + { + int count = _usedCount + _reservedCount; + CheckOrResize(count + 1); + int id = _dense[count]; + _sparseState[id] = IDState.Reserved; + AddReserved(id); + return id; + } + /// Marks as reserved a free id, after this id cannot be retrieved via UseFree. + public void Reserve(int id) + { + CheckOrResize(id); +#if DEBUG + if (!IsFree(id)) ThrowHalper.ThrowIsNotAvailable(id); +#endif + _sparseState[id] = IDState.Reserved; + AddReserved(id); + } + public void ReserveRange(IEnumerable ids) + { + foreach (var item in ids) + Reserve(item); + } + public void Release(int id) + { + CheckOrResize(id); +#if DEBUG + if (IsFree(id) || IsNullID(id)) + { + if (IsFree(id)) ThrowHalper.ThrowIsNotUsed(id); + else ThrowHalper.ThrowIsNullID(id); + } +#endif + if (_sparseState[id] == IDState.Used) + Remove(id); + else + RemoveReserved(id); + _sparseState[id] = IDState.Free; + } + public void ReleaseRange(IEnumerable ids) + { + foreach (var item in ids) + Release(item); + } + public void ReleaseAll() + { + _usedCount = 0; + _reservedCount = 0; + for (int i = 0; i < _size;) + { + _sparse[i] = i; + _sparseState[i] = IDState.Free; + _dense[i] = i++; + _sparse[i] = i; + _sparseState[i] = IDState.Free; + _dense[i] = i++; + _sparse[i] = i; + _sparseState[i] = IDState.Free; + _dense[i] = i++; + _sparse[i] = i; + _sparseState[i] = IDState.Free; + _dense[i] = i++; + } + SetNullID(_nullID); + } + #endregion + + #region Checks + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsFree(int id) => _sparseState[id] == IDState.Free; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsReserved(int id) => _sparseState[id] == IDState.Reserved; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsUsed(int id) => _sparseState[id] == IDState.Used; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsNullID(int id) => id == _nullID; + + #endregion + + #region Sort + /// O(n) Sort. n = Size. Allows the UseFree method to return denser ids. + public void Sort() + { + int usedInc = _reservedCount; + int reservedInc = 0; + int freeInc = _reservedCount + _usedCount; + for (int i = 0; i < _size; i++) + { + switch (_sparseState[i]) + { + case IDState.Free: + _sparse[i] = freeInc; + _dense[freeInc++] = i; + break; + case IDState.Reserved: + _sparse[i] = reservedInc; + _dense[reservedInc++] = i; + break; + case IDState.Used: + _sparse[i] = usedInc; + _dense[usedInc++] = i; + break; + } + } + } + #endregion + + #region Other + private void SetNullID(int nullID) + { + _nullID = nullID; + if (nullID >= 0) + { + AddReserved(nullID); + _sparseState[nullID] = IDState.Reserved; + } + } + private bool IsValid() + { + for (int i = 0; i < _usedCount; i++) + { + if (_sparse[_dense[i]] != i || _dense[_sparse[i]] != i) + return false; + } + return true; + } + private void CheckOrResize(int id) + { + if (id > _size) + { + int leftBit = 0; + while (id != 0) + { + id >>= 1; + id &= int.MaxValue; + leftBit++; + } + if (leftBit >= 32) + Resize(int.MaxValue); + else + Resize(1 << leftBit); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Add(int value) + { + Swap(value, _reservedCount + _usedCount++); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Remove(int value) + { + Swap(value, _reservedCount + --_usedCount); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddReserved(int value) + { + Swap(value, _reservedCount + _usedCount); + Swap(value, _reservedCount++); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RemoveReserved(int value) + { + Swap(value, --_reservedCount); + Swap(value, _reservedCount + _usedCount); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Swap(int sparseIndex, int denseIndex) + { + int _dense_denseIndex_ = _dense[denseIndex]; + int _sparse_sparseIndex_ = _sparse[sparseIndex]; + _dense[denseIndex] = _dense[_sparse_sparseIndex_]; + _dense[_sparse_sparseIndex_] = _dense_denseIndex_; + _sparse[_dense_denseIndex_] = _sparse_sparseIndex_; + _sparse[sparseIndex] = denseIndex; + } + private void Resize(int newSize) + { + if (newSize < MIN_SIZE) + newSize = MIN_SIZE; + Array.Resize(ref _dense, newSize); + Array.Resize(ref _sparse, newSize); + Array.Resize(ref _sparseState, newSize); + for (int i = _size; i < newSize;) + { + _sparse[i] = i; + _dense[i] = i++; + _sparse[i] = i; + _dense[i] = i++; + _sparse[i] = i; + _dense[i] = i++; + _sparse[i] = i; + _dense[i] = i++; + } + _size = newSize; + Resized(newSize); + } + #endregion + + public delegate void ResizedHandler(int newSize); + public event ResizedHandler Resized = delegate { }; + + internal enum IDState : byte + { + Free, + Reserved, + Used, + } + + #region Enumerable + public UsedSpan Used; + public ReservedSpan Reserved; + public Enumerator GetEnumerator() => new Enumerator(_dense, _reservedCount, _usedCount); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private readonly int[] _dense; + private readonly int _count; + private int _index; + public int Current => _dense[_index]; + object IEnumerator.Current => Current; + public Enumerator(int[] dense, int startIndex, int count) + { + _dense = dense; + _count = startIndex + count; + _index = startIndex - 1; + } + public bool MoveNext() => ++_index < _count; + public void Dispose() { } + public void Reset() => _index = -1; + } + public readonly struct UsedSpan : IEnumerable + { + private readonly IdDispenser _instance; + public int Count => _instance._usedCount; + internal UsedSpan(IdDispenser instance) => _instance = instance; + public Enumerator GetEnumerator() => new Enumerator(_instance._dense, _instance._reservedCount, _instance._usedCount); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + public readonly struct ReservedSpan : IEnumerable + { + private readonly IdDispenser _instance; + public int Count => _instance._reservedCount; + internal ReservedSpan(IdDispenser instance) => _instance = instance; + public Enumerator GetEnumerator() => new Enumerator(_instance._dense, 0, _instance._reservedCount); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + #endregion + + #region Utils + private static class ThrowHalper + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowIsAlreadyInUse(int id) => throw new ArgumentException($"Id {id} is already in use."); + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowIsHasBeenReserved(int id) => throw new ArgumentException($"Id {id} has been reserved."); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowIsNotUsed(int id) => throw new ArgumentException($"Id {id} is not used."); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowIsNotAvailable(int id) => throw new ArgumentException($"Id {id} is not available."); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowIsNullID(int id) => throw new ArgumentException($"Id {id} cannot be released because it is used as a null id."); + } + + internal class DebuggerProxy + { + private IdDispenser _dispenser; + public DebuggerProxy(IdDispenser dispenser) => _dispenser = dispenser; +#if DEBUG + public IEnumerable Used => _dispenser.Used; + public IEnumerable Reserved => _dispenser.Reserved; + public Pair[] Pairs + { + get + { + Pair[] result = new Pair[_dispenser.Size]; + for (int i = 0; i < result.Length; i++) + result[i] = new Pair(_dispenser._dense[i], _dispenser._sparse[i]); + return result; + } + } + public ID[] All + { + get + { + ID[] result = new ID[_dispenser.Size]; + for (int i = 0; i < result.Length; i++) + { + int id = _dispenser._dense[i]; + result[i] = new ID(id, _dispenser._sparseState[id].ToString()); + } + return result; + } + } + public bool IsValid => _dispenser.IsValid(); + public int Count => _dispenser.ReservedCount; + public int Size => _dispenser.Size; + public int NullID => _dispenser._nullID; + internal readonly struct ID + { + public readonly int id; + public readonly string state; + public ID(int id, string state) { this.id = id; this.state = state; } + public override string ToString() => $"{id} - {state}"; + } + internal readonly struct Pair + { + public readonly int dense; + public readonly int sparse; + public Pair(int dense, int sparse) { this.dense = dense; this.sparse = sparse; } + public override string ToString() => $"{dense} - {sparse}"; + } +#endif + } + #endregion + } +} \ No newline at end of file diff --git a/src/Utils/IdDispenser.cs.meta b/src/Utils/IdDispenser.cs.meta new file mode 100644 index 0000000..eda80df --- /dev/null +++ b/src/Utils/IdDispenser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54c009cfa7ae0fd49938525468703c8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 26e45a6f16dccb541e87d58eedb84b8271cb9388 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:57:28 +0800 Subject: [PATCH 015/104] update debug utils/ add DebugGroup attribute --- src/Debug/Attributes/DebugColorAttribute.cs | 162 ++++++++++-------- src/Debug/Attributes/DebugGroupAttribute.cs | 33 ++++ .../Attributes/DebugGroupAttribute.cs.meta | 11 ++ src/Debug/EcsDebugUtility.cs | 80 +++++++-- src/Utils/EcsTypeCode.cs | 2 +- 5 files changed, 196 insertions(+), 92 deletions(-) create mode 100644 src/Debug/Attributes/DebugGroupAttribute.cs create mode 100644 src/Debug/Attributes/DebugGroupAttribute.cs.meta diff --git a/src/Debug/Attributes/DebugColorAttribute.cs b/src/Debug/Attributes/DebugColorAttribute.cs index fe603dc..5ec68dd 100644 --- a/src/Debug/Attributes/DebugColorAttribute.cs +++ b/src/Debug/Attributes/DebugColorAttribute.cs @@ -6,89 +6,99 @@ namespace DCFApixels.DragonECS [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public sealed class DebugColorAttribute : Attribute { - private Color color; + public readonly DebugColor color; public byte r => color.r; public byte g => color.g; public byte b => color.b; - public DebugColorAttribute(byte r, byte g, byte b) => color = new Color(r, g, b); - public DebugColorAttribute(DebugColor color) => this.color = new Color((int)color); - - [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)] - internal readonly struct Color - { - [FieldOffset(0)] public readonly int full; - [FieldOffset(3)] public readonly byte r; - [FieldOffset(2)] public readonly byte g; - [FieldOffset(1)] public readonly byte b; - public Color(byte r, byte g, byte b) : this() - { - this.r = r; - this.g = g; - this.b = b; - } - public Color(int full) : this() => this.full = full; - public (byte, byte, byte) ToTuple() => (r, g, b); - - public Color UpContrastColor() - { - byte minChannel = Math.Min(Math.Min(r, g), b); - byte maxChannel = Math.Max(Math.Max(r, g), b); - if (maxChannel == minChannel) - return default; - float factor = 255f / (maxChannel - minChannel); - return new Color((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor)); - } - - public static Color operator /(Color a, float b) - { - return new Color((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b)); - } - } + public DebugColorAttribute(byte r, byte g, byte b) => color = new DebugColor(r, g, b, 255); + public DebugColorAttribute(int colorCode) => color = new DebugColor(colorCode, true); } - public enum DebugColor + [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)] + public readonly struct DebugColor { - /// Red. RGB is (255, 0, 0) - Red = (255 << 24) + (000 << 16) + (000 << 8), - /// Green. RGB is (0, 255, 0) - Green = (000 << 24) + (255 << 16) + (000 << 8), - /// Blue. RGB is (0, 0, 255) - Blue = (000 << 24) + (000 << 16) + (255 << 8), + public static readonly DebugColor BlackColor = new DebugColor(Black); + /// color code Red. RGB is (255, 0, 0) + public const int Red = (255 << 24) | (000 << 16) | (000 << 8) | 255; + /// color code Green. RGB is (0, 255, 0) + public const int Green = (000 << 24) | (255 << 16) | (000 << 8) | 255; + /// color code Blue. RGB is (0, 0, 255) + public const int Blue = (000 << 24) | (000 << 16) | (255 << 8) | 255; - /// Yellow. RGB is (255, 255, 0) - Yellow = (255 << 24) + (255 << 16) + (000 << 8), - /// Cyan. RGB is (0, 255, 255) - Cyan = (000 << 24) + (255 << 16) + (255 << 8), - /// Magenta. RGB is (255, 0, 255) - Magenta = (255 << 24) + (000 << 16) + (255 << 8), + /// color code Yellow. RGB is (255, 255, 0) + public const int Yellow = (255 << 24) | (255 << 16) | (000 << 8) | 255; + /// color code Cyan. RGB is (0, 255, 255) + public const int Cyan = (000 << 24) | (255 << 16) | (255 << 8) | 255; + /// color code Magenta. RGB is (255, 0, 255) + public const int Magenta = (255 << 24) | (000 << 16) | (255 << 8) | 255; - /// Yellow. RGB is (255, 165, 0) - Orange = (255 << 24) + (165 << 16) + (000 << 8), - /// Yellow. RGB is (255, 69, 0) - OrangeRed = (255 << 24) + (69 << 16) + (000 << 8), - /// Lime. RGB is (125, 255, 0) - Lime = (125 << 24) + (255 << 16) + (000 << 8), - /// Lime. RGB is (127, 255, 212) - Aquamarine = (127 << 24) + (255 << 16) + (212 << 8), - /// Lime. RGB is (218, 165, 32) - Goldenrod = (218 << 24) + (165 << 16) + (32 << 8), - /// Yellow. RGB is (255, 105, 180) - DeepPink = (255 << 24) + (105 << 16) + (180 << 8), - /// Yellow. RGB is (220, 20, 60) - Crimson = (220 << 24) + (20 << 16) + (60 << 8), - /// Yellow. RGB is (138, 43, 226) - BlueViolet = (138 << 24) + (43 << 16) + (226 << 8), - /// Yellow. RGB is (255, 3, 62) - AmericanRose = (255 << 24) + (3 << 16) + (62 << 8), + /// color code Orange. RGB is (255, 165, 0) + public const int Orange = (255 << 24) | (165 << 16) | (000 << 8) | 255; + /// color code OrangeRed. RGB is (255, 69, 0) + public const int OrangeRed = (255 << 24) | (69 << 16) | (000 << 8) | 255; + /// color code Lime. RGB is (125, 255, 0) + public const int Lime = (125 << 24) | (255 << 16) | (000 << 8) | 255; + /// color code Aquamarine. RGB is (127, 255, 212) + public const int Aquamarine = (127 << 24) | (255 << 16) | (212 << 8) | 255; + /// color code Goldenrod. RGB is (218, 165, 32) + public const int Goldenrod = (218 << 24) | (165 << 16) | (32 << 8) | 255; + /// color code DeepPink. RGB is (255, 105, 180) + public const int DeepPink = (255 << 24) | (105 << 16) | (180 << 8) | 255; + /// color code Crimson. RGB is (220, 20, 60) + public const int Crimson = (220 << 24) | (20 << 16) | (60 << 8) | 255; + /// color code BlueViolet. RGB is (138, 43, 226) + public const int BlueViolet = (138 << 24) | (43 << 16) | (226 << 8) | 255; + /// color code AmericanRose. RGB is (255, 3, 62) + public const int AmericanRose = (255 << 24) | (3 << 16) | (62 << 8) | 255; - /// Grey/Gray. RGB is (127, 127, 127) - Gray = (127 << 24) + (127 << 16) + (127 << 8), - /// Grey/Gray. RGB is (127, 127, 127) - Grey = Gray, - /// Grey/Gray. RGB is (192, 192, 192) - Silver = (192 << 24) + (192 << 16) + (192 << 8), - /// White. RGB is (255, 255, 255) - White = -1, - /// Black. RGB is (0, 0, 0) - Black = 0, + /// color code Grey/Gray. RGB is (127, 127, 127) + public const int Gray = (127 << 24) | (127 << 16) | (127 << 8) | 255; + /// color code Grey/Gray. RGB is (127, 127, 127) + public const int Grey = Gray; + /// color code Silver. RGB is (192, 192, 192) + public const int Silver = (192 << 24) | (192 << 16) | (192 << 8) | 255; + /// color code White. RGB is (255, 255, 255) + public const int White = -1; + /// color code Black. RGB is (0, 0, 0) + public const int Black = 0; + + + [FieldOffset(0)] public readonly int colorCode; + [FieldOffset(3)] public readonly byte r; + [FieldOffset(2)] public readonly byte g; + [FieldOffset(1)] public readonly byte b; + [FieldOffset(0)] public readonly byte a; + public DebugColor(byte r, byte g, byte b) : this() + { + this.r = r; + this.g = g; + this.b = b; + a = 255; + } + public DebugColor(byte r, byte g, byte b, byte a) : this() + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + public DebugColor(int colorCode) : this() => this.colorCode = colorCode; + public DebugColor(int colorCode, bool withoutAlpha) : this() => this.colorCode = withoutAlpha ? colorCode | 255 : colorCode; + public (byte, byte, byte) ToTupleRGB() => (r, g, b); + public (byte, byte, byte, byte) ToTupleRGBA() => (r, g, b, a); + + public DebugColor UpContrastColor() + { + byte minChannel = Math.Min(Math.Min(r, g), b); + byte maxChannel = Math.Max(Math.Max(r, g), b); + if (maxChannel == minChannel) + return default; + float factor = 255f / (maxChannel - minChannel); + return new DebugColor((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor)); + } + public static DebugColor operator /(DebugColor a, float b) + { + return new DebugColor((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b)); + } + //public static explicit operator DebugColor(int colorCode) => new DebugColor(colorCode); } } \ No newline at end of file diff --git a/src/Debug/Attributes/DebugGroupAttribute.cs b/src/Debug/Attributes/DebugGroupAttribute.cs new file mode 100644 index 0000000..e0c7567 --- /dev/null +++ b/src/Debug/Attributes/DebugGroupAttribute.cs @@ -0,0 +1,33 @@ +using System; +using System.Text.RegularExpressions; + +namespace DCFApixels.DragonECS +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] + public sealed class DebugGroupAttribute : Attribute + { + public static readonly DebugGroupAttribute Empty = new DebugGroupAttribute(""); + public readonly string name; + public readonly string rootCategory; + public DebugGroupAttribute(string name) + { + name = Regex.Replace(name, @"^[/|\\]+|[/|\\]+$", ""); + rootCategory = Regex.Match(name, @"^(.*?)[/\\]").Groups[1].Value; + this.name = name; + } + public string[] SplitCategories() + { + return Regex.Split(name, @"[/|\\]"); + } + public DebugGroup GetData() => new DebugGroup(this); + } + public readonly struct DebugGroup + { + public static readonly DebugGroup Empty = new DebugGroup(DebugGroupAttribute.Empty); + private readonly DebugGroupAttribute _source; + public string Name => _source.name; + public string RootCategory => _source.rootCategory; + public DebugGroup(DebugGroupAttribute source) => _source = source; + public string[] SplitCategories() => _source.SplitCategories(); + } +} diff --git a/src/Debug/Attributes/DebugGroupAttribute.cs.meta b/src/Debug/Attributes/DebugGroupAttribute.cs.meta new file mode 100644 index 0000000..594d6fb --- /dev/null +++ b/src/Debug/Attributes/DebugGroupAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c447392c75f8b4a42a2e5c3eb49e5b82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 7985286..803c9b6 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -72,6 +72,22 @@ namespace DCFApixels.DragonECS } #endregion + #region GetGroup + public static DebugGroup GetGroup() => GetGroup(typeof(T)); + public static DebugGroup GetGroup(Type type) => type.TryGetCustomAttribute(out DebugGroupAttribute atr) ? atr.GetData() : DebugGroup.Empty; + public static bool TryGetGroup(out DebugGroup text) => TryGetGroup(typeof(T), out text); + public static bool TryGetGroup(Type type, out DebugGroup group) + { + if (type.TryGetCustomAttribute(out DebugGroupAttribute atr)) + { + group = atr.GetData(); + return true; + } + group = DebugGroup.Empty; + return false; + } + #endregion + #region GetDescription public static string GetDescription() => GetDescription(typeof(T)); public static string GetDescription(Type type) => type.TryGetCustomAttribute(out DebugDescriptionAttribute atr) ? atr.description : string.Empty; @@ -94,7 +110,7 @@ namespace DCFApixels.DragonECS private class WordColor { public int wordsCount; - public DebugColorAttribute.Color color; + public DebugColor color; } private class NameColor { @@ -107,7 +123,7 @@ namespace DCFApixels.DragonECS { color = new WordColor(); _words.Add(word, color); - color.color = new DebugColorAttribute.Color((byte)random.Next(), (byte)random.Next(), (byte)random.Next()).UpContrastColor() / 2; + color.color = new DebugColor((byte)random.Next(), (byte)random.Next(), (byte)random.Next()).UpContrastColor() / 2; } color.wordsCount++; colors.Add(color); @@ -122,7 +138,7 @@ namespace DCFApixels.DragonECS } return result; } - public DebugColorAttribute.Color CalcColor() + public DebugColor CalcColor() { float r = 0, g = 0, b = 0; int totalWordsCount = CalcTotalWordsColor(); @@ -134,11 +150,11 @@ namespace DCFApixels.DragonECS g += m * color.color.g; b += m * color.color.b; } - return new DebugColorAttribute.Color((byte)r, (byte)g, (byte)b); + return new DebugColor((byte)r, (byte)g, (byte)b); } } private static Dictionary _names = new Dictionary(); - private static DebugColorAttribute.Color CalcNameColorFor(Type type) + private static DebugColor CalcNameColorFor(Type type) { Type targetType = type.IsGenericType ? type.GetGenericTypeDefinition() : type; if (!_names.TryGetValue(targetType, out NameColor nameColor)) @@ -169,27 +185,27 @@ namespace DCFApixels.DragonECS return words; } - public static (byte, byte, byte) GetColorRGB() => GetColorRGB(typeof(T)); - public static (byte, byte, byte) GetColorRGB(Type type) + public static DebugColor GetColor() => GetColor(typeof(T)); + public static DebugColor GetColor(Type type) { var atr = type.GetCustomAttribute(); - return atr != null ? (atr.r, atr.g, atr.b) + return atr != null ? atr.color #if DEBUG //optimization for release build - : CalcNameColorFor(type).ToTuple(); -#else - : ((byte)0, (byte)0, (byte)0); + : CalcNameColorFor(type); +#else + : DebugColor.BlackColor; #endif } - public static bool TryGetColorRGB(out (byte, byte, byte) color) => TryGetColorRGB(typeof(T), out color); - public static bool TryGetColorRGB(Type type, out (byte, byte, byte) color) + public static bool TryGetColor(out DebugColor color) => TryGetColor(typeof(T), out color); + public static bool TryGetColor(Type type, out DebugColor color) { var atr = type.GetCustomAttribute(); if (atr != null) { - color = (atr.r, atr.g, atr.b); + color = atr.color; return true; } - color = ((byte)0, (byte)0, (byte)0); + color = DebugColor.BlackColor; return false; } #endregion @@ -199,6 +215,20 @@ namespace DCFApixels.DragonECS public static bool IsHidden(Type type) => type.TryGetCustomAttribute(out DebugHideAttribute _); #endregion + #region GenerateTypeDebugData + public static TypeDebugData GenerateTypeDebugData() => GenerateTypeDebugData(typeof(T)); + public static TypeDebugData GenerateTypeDebugData(Type type) + { + return new TypeDebugData( + type, + GetName(type), + GetGroup(type), + GetColor(type), + GetDescription(type), + IsHidden(type)); + } + #endregion + #region ReflectionExtensions internal static bool TryGetCustomAttribute(this Type self, out T attribute) where T : Attribute { @@ -212,4 +242,24 @@ namespace DCFApixels.DragonECS } #endregion } + + [Serializable] + public sealed class TypeDebugData + { + public readonly Type type; + public readonly string name; + public readonly DebugGroup group; + public readonly DebugColor color; + public readonly string description; + public readonly bool isHidden; + public TypeDebugData(Type type, string name, DebugGroup group, DebugColor color, string description, bool isHidden) + { + this.type = type; + this.name = name; + this.group = group; + this.color = color; + this.description = description; + this.isHidden = isHidden; + } + } } diff --git a/src/Utils/EcsTypeCode.cs b/src/Utils/EcsTypeCode.cs index 77d26f2..b18b4af 100644 --- a/src/Utils/EcsTypeCode.cs +++ b/src/Utils/EcsTypeCode.cs @@ -21,7 +21,7 @@ namespace DCFApixels.DragonECS public static int Count => _codes.Count; internal static class Cache { - public static readonly int code = EcsTypeCode.GetCode(typeof(T)); + public static readonly int code = GetCode(typeof(T)); } } } From 393c56a166fd2c4e6ec85b405b92c60ccfd179e3 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Thu, 29 Jun 2023 23:53:26 +0800 Subject: [PATCH 016/104] add hybrid components --- src/Debug/Attributes/DebugColorAttribute.cs | 2 - src/Pools/EcsHybridPool.cs | 223 ++++++++++++++++++++ src/Pools/EcsPoolBase.cs | 5 + 3 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 src/Pools/EcsHybridPool.cs diff --git a/src/Debug/Attributes/DebugColorAttribute.cs b/src/Debug/Attributes/DebugColorAttribute.cs index 5ec68dd..fda3aa5 100644 --- a/src/Debug/Attributes/DebugColorAttribute.cs +++ b/src/Debug/Attributes/DebugColorAttribute.cs @@ -61,7 +61,6 @@ namespace DCFApixels.DragonECS /// color code Black. RGB is (0, 0, 0) public const int Black = 0; - [FieldOffset(0)] public readonly int colorCode; [FieldOffset(3)] public readonly byte r; [FieldOffset(2)] public readonly byte g; @@ -99,6 +98,5 @@ namespace DCFApixels.DragonECS { return new DebugColor((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b)); } - //public static explicit operator DebugColor(int colorCode) => new DebugColor(colorCode); } } \ No newline at end of file diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs new file mode 100644 index 0000000..2653295 --- /dev/null +++ b/src/Pools/EcsHybridPool.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS +{ + /// Pool for IEcsComponent components + public sealed class EcsHybridPool : IEcsPoolImplementation, IEcsHybridPool, IEnumerable //IEnumerable - IntelliSense hack + where T : IEcsHybridComponent + { + private EcsWorld _source; + private int _componentID; + + private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID + private T[] _items; //dense + private int _itemsCount; + private int[] _recycledItems; + private int _recycledItemsCount; + + private IEcsComponentReset _componentResetHandler = EcsComponentResetHandler.instance; + private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; + + private List _listeners = new List(); + + #region Properites + public int Count => _itemsCount; + public int Capacity => _items.Length; + public int ComponentID => _componentID; + public Type ComponentType => typeof(T); + 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 void Add(int entityID, T component) + { + ref int itemIndex = ref _mapping[entityID]; +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (itemIndex > 0) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID); +#endif + if (_recycledItemsCount > 0) + { + itemIndex = _recycledItems[--_recycledItemsCount]; + _itemsCount++; + } + else + { + itemIndex = ++_itemsCount; + if (itemIndex >= _items.Length) + Array.Resize(ref _items, _items.Length << 1); + } + this.IncrementEntityComponentCount(entityID); + _listeners.InvokeOnAdd(entityID); + _items[itemIndex] = component; + } + 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); + } + this.IncrementEntityComponentCount(entityID); + } + else + {//not null + _listeners.InvokeOnDel(entityID); + if (_items[itemIndex] is IDisposable disposable) + disposable.Dispose(); + } + _listeners.InvokeOnAdd(entityID); + _items[itemIndex] = component; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Get(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); +#endif + _listeners.InvokeOnGet(entityID); + return _items[_mapping[entityID]]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); +#endif + return ref _items[_mapping[entityID]]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Has(int entityID) + { + return _mapping[entityID] > 0; + } + public void Del(int entityID) + { +#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]; + if (component is IDisposable disposable) + disposable.Dispose(); + if (_recycledItemsCount >= _recycledItems.Length) + Array.Resize(ref _recycledItems, _recycledItems.Length << 1); + _recycledItems[_recycledItemsCount++] = itemIndex; + _mapping[entityID] = 0; + _itemsCount--; + this.DecrementEntityComponentCount(entityID); + _listeners.InvokeOnDel(entityID); + } + public void TryDel(int entityID) + { + if (Has(entityID)) Del(entityID); + } + public void Copy(int fromEntityID, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); +#endif + Set(toEntityID, Get(fromEntityID)); + } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); +#endif + toWorld.GetPool().Set(toEntityID, Get(fromEntityID)); + } + #endregion + + #region Callbacks + void IEcsPoolImplementation.OnWorldResize(int newSize) + { + Array.Resize(ref _mapping, newSize); + } + void IEcsPoolImplementation.OnWorldDestroy() { } + void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + foreach (var entityID in buffer) + TryDel(entityID); + } + #endregion + + #region Other + void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID, (T)dataRaw); + object IEcsPool.GetRaw(int entityID) => Read(entityID); + void IEcsPool.SetRaw(int entityID, object dataRaw) => Set(entityID, (T)dataRaw); + #endregion + + #region Listeners + public void AddListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Add(listener); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Remove(listener); + } + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + #endregion + } + /// Hybrid component + public interface IEcsHybridComponent + { + bool IsAlive { get; } + entlong Entity { set; } + void OnAddToPool(); + void OnDelFromPool(); + } + public static class EcsHybridPoolExt + { + public static EcsHybridPool GetPool(this EcsWorld self) where T : IEcsHybridComponent + { + return self.GetPool>(); + } + + public static EcsHybridPool Include(this EcsAspectBuilderBase self) where T : IEcsHybridComponent + { + return self.Include>(); + } + public static EcsHybridPool Exclude(this EcsAspectBuilderBase self) where T : IEcsHybridComponent + { + return self.Exclude>(); + } + public static EcsHybridPool Optional(this EcsAspectBuilderBase self) where T : IEcsHybridComponent + { + return self.Optional>(); + } + } +} diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 459c01f..bcd8b90 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -41,6 +41,11 @@ namespace DCFApixels.DragonECS T Add(int entityID); T Get(int entityID); } + public interface IEcsHybridPool : IEcsPool + { + void Add(int entityID, T component); + T Get(int entityID); + } /// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool. public interface IEcsPoolImplementation : IEcsPool { From d674f2da8b5bb6e1f33edd1bfe2a1c0d50e54bfd Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Thu, 29 Jun 2023 23:59:45 +0800 Subject: [PATCH 017/104] Implement Add and Del callbacks for Hybrid components. --- src/Pools/EcsHybridPool.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 2653295..c620ed1 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -18,9 +18,6 @@ namespace DCFApixels.DragonECS private int[] _recycledItems; private int _recycledItemsCount; - private IEcsComponentReset _componentResetHandler = EcsComponentResetHandler.instance; - private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; - private List _listeners = new List(); #region Properites @@ -67,6 +64,7 @@ namespace DCFApixels.DragonECS } this.IncrementEntityComponentCount(entityID); _listeners.InvokeOnAdd(entityID); + component.OnAddToPool(); _items[itemIndex] = component; } public void Set(int entityID, T component) @@ -90,10 +88,10 @@ namespace DCFApixels.DragonECS else {//not null _listeners.InvokeOnDel(entityID); - if (_items[itemIndex] is IDisposable disposable) - disposable.Dispose(); + component.OnDelFromPool(); } _listeners.InvokeOnAdd(entityID); + component.OnAddToPool(); _items[itemIndex] = component; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -125,8 +123,7 @@ namespace DCFApixels.DragonECS #endif ref int itemIndex = ref _mapping[entityID]; T component = _items[itemIndex]; - if (component is IDisposable disposable) - disposable.Dispose(); + component.OnDelFromPool(); if (_recycledItemsCount >= _recycledItems.Length) Array.Resize(ref _recycledItems, _recycledItems.Length << 1); _recycledItems[_recycledItemsCount++] = itemIndex; From 49dd0eaa97c0c9cd7ea42862143039f0026cc64f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 30 Jun 2023 00:31:17 +0800 Subject: [PATCH 018/104] add EcsDebug.Break --- src/Debug/EcsDebug.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index 6dd1ac9..39eed53 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -80,7 +80,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Print(object v) => Print(null, v); public abstract void Print(string tag, object v); - + public abstract void Break(); public int RegisterMark(string name) { int id; @@ -123,6 +123,10 @@ namespace DCFApixels.DragonECS { Console.WriteLine($"[{tag}] {v}"); } + public override void Break() + { + Console.ReadLine(); + } public override void ProfilerMarkBegin(int id) { _stopwatchs[id].Start(); From 44e0792517f03469e0ada6a5e0e575bfc867edfb Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 30 Jun 2023 00:32:25 +0800 Subject: [PATCH 019/104] fix Break --- src/Debug/EcsDebug.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index 39eed53..af3aae3 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -50,6 +50,14 @@ namespace DCFApixels.DragonECS DebugService.Instance.Print(tag, v); #endif } + public static void Break() + { + { +#if !DISABLE_DRAGONECS_DEBUGGER + DebugService.Instance.Break(); +#endif + } + } } public abstract class DebugService From 7b30dc9e53568234a4fecc15f18daf612afaa7d3 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 30 Jun 2023 00:37:54 +0800 Subject: [PATCH 020/104] fix hybrid pool --- src/Pools/EcsHybridPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index c620ed1..45b6cca 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -88,7 +88,7 @@ namespace DCFApixels.DragonECS else {//not null _listeners.InvokeOnDel(entityID); - component.OnDelFromPool(); + _items[itemIndex].OnDelFromPool(); } _listeners.InvokeOnAdd(entityID); component.OnAddToPool(); From eddbe6b15071f4e5efb059728483068a485b605d Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 30 Jun 2023 00:59:49 +0800 Subject: [PATCH 021/104] fix EcsGroup.ToString() --- src/EcsGroup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index cd8e834..80c8f2a 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -467,7 +467,7 @@ namespace DCFApixels.DragonECS #endregion #region Object - public override string ToString() => string.Join(", ", _dense.Cast(), 0, _count); + public override string ToString() => $"group{{{string.Join(", ", _dense.Take(_count))}}}"; public override bool Equals(object obj) => obj is EcsGroup group && Equals(group); public bool Equals(EcsReadonlyGroup other) => Equals(other.GetGroupInternal()); public bool Equals(EcsGroup other) @@ -500,7 +500,7 @@ namespace DCFApixels.DragonECS private static bool StaticEquals(EcsGroup a, EcsReadonlyGroup b) => StaticEquals(a, b.GetGroupInternal()); private static bool StaticEquals(EcsGroup a, EcsGroup b) { - if (a is null) return false; + if (a is null || b is null) return false; return a.Equals(b); } public static bool operator ==(EcsGroup a, EcsGroup b) => StaticEquals(a, b); From ded1c5829684a48dfa763baf07e6ea96b2d8de5e Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 30 Jun 2023 01:01:19 +0800 Subject: [PATCH 022/104] change hybrid component interface --- src/Pools/EcsHybridPool.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 45b6cca..f6faac4 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -64,7 +64,7 @@ namespace DCFApixels.DragonECS } this.IncrementEntityComponentCount(entityID); _listeners.InvokeOnAdd(entityID); - component.OnAddToPool(); + component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; } public void Set(int entityID, T component) @@ -88,10 +88,10 @@ namespace DCFApixels.DragonECS else {//not null _listeners.InvokeOnDel(entityID); - _items[itemIndex].OnDelFromPool(); + _items[itemIndex].OnDelFromPool(_source.GetEntityLong(entityID)); } _listeners.InvokeOnAdd(entityID); - component.OnAddToPool(); + component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -123,7 +123,7 @@ namespace DCFApixels.DragonECS #endif ref int itemIndex = ref _mapping[entityID]; T component = _items[itemIndex]; - component.OnDelFromPool(); + component.OnDelFromPool(_source.GetEntityLong(entityID)); if (_recycledItemsCount >= _recycledItems.Length) Array.Resize(ref _recycledItems, _recycledItems.Length << 1); _recycledItems[_recycledItemsCount++] = itemIndex; @@ -193,9 +193,8 @@ namespace DCFApixels.DragonECS public interface IEcsHybridComponent { bool IsAlive { get; } - entlong Entity { set; } - void OnAddToPool(); - void OnDelFromPool(); + void OnAddToPool(entlong entity); + void OnDelFromPool(entlong entity); } public static class EcsHybridPoolExt { From d204971ba8a31008ed94299d2b2eee274b25ddb3 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 30 Jun 2023 01:11:27 +0800 Subject: [PATCH 023/104] Update README-RU.md --- README-RU.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README-RU.md b/README-RU.md index 6c2493a..84525c2 100644 --- a/README-RU.md +++ b/README-RU.md @@ -113,6 +113,7 @@ struct PlayerTag : IEcsTagComponent {} Встроенные виды компонентов: * `IEcsComponent` - Компоненты с данными. * `IEcsTagComponent` - Компоненты-теги. Без данных. +* `IEcsHybridComponent` - Гибридные компоненты. Предназначены для упрощения смешивания архитектурных подходов в проекте. Например можно MonoBehaviour-ы Unity прикреплять как обычные компоненты. > Компоненты-теги хоть и не имеют данных, само наличие или отсутствие компонента-тега у сущности уже несет информацию и может применяться для определения типа сущности. ## System From b04756c03d82181896ae64a01b88d5b98e2713c3 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 30 Jun 2023 01:19:03 +0800 Subject: [PATCH 024/104] Create EcsHybridPool.cs.meta --- src/Pools/EcsHybridPool.cs.meta | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/Pools/EcsHybridPool.cs.meta diff --git a/src/Pools/EcsHybridPool.cs.meta b/src/Pools/EcsHybridPool.cs.meta new file mode 100644 index 0000000..ec1c8be --- /dev/null +++ b/src/Pools/EcsHybridPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 511487e83f936f94780e572063b68a87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From aad94da19dd548285dd16b1d50e4c9f56ab84825 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 2 Jul 2023 16:17:13 +0800 Subject: [PATCH 025/104] add UncheckedGet for world component --- src/EcsWorld.cs | 2 ++ src/EcsWorld.static.cs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index f64521a..84a3375 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -116,6 +116,8 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get() where T : struct => ref WorldComponentPool.GetForWorld(id); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T UncheckedGet() where T : struct => ref WorldComponentPool.UncheckedGetForWorld(id); #endregion #region Where Query diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index c131c80..91b36be 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -57,6 +57,8 @@ namespace DCFApixels.DragonECS public static ref T Get(int itemIndex) => ref _items[itemIndex]; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetForWorld(int worldID) => ref _items[GetItemIndex(worldID)]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T UncheckedGetForWorld(int worldID) => ref _items[_mapping[worldID]]; public static int GetItemIndex(int worldID) { if (_mapping.Length < Worlds.Length) @@ -79,7 +81,6 @@ namespace DCFApixels.DragonECS } return itemIndex; } - private static void Release(int worldID) { ref short itemIndex = ref _mapping[worldID]; From 649037577d061fd50aceb04edca4e92a349e1f80 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 2 Jul 2023 16:45:40 +0800 Subject: [PATCH 026/104] add UncheckedGetPool --- src/EcsWorld.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 84a3375..c42513d 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -104,6 +104,11 @@ namespace DCFApixels.DragonECS return Get>().instance; } [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TPool UncheckedGetPool() where TPool : IEcsPoolImplementation, new() + { + return UncheckedGet>().instance; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public TAspect GetAspect() where TAspect : EcsAspect { return Get>().instance; From 43c5adc6cc5bd42955ddd4408973279bac4cccb6 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 2 Jul 2023 21:39:44 +0800 Subject: [PATCH 027/104] usafe optimise GetEntityLong --- src/EcsWorld.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index c42513d..415e8b0 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -201,7 +201,11 @@ namespace DCFApixels.DragonECS ReleaseDelEntityBuffer(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public entlong GetEntityLong(int entityID) => new entlong(entityID, _gens[entityID], id); //TODO придумать получше имя метода + public unsafe entlong GetEntityLong(int entityID) + { + long x = ((long)id << 48 | (long)_gens[entityID] << 32 | entityID); + return *(entlong*)&x; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsAlive(int entityID, short gen) => _gens[entityID] == gen; [MethodImpl(MethodImplOptions.AggressiveInlining)] From 8f76a1cf2ecccb525cfd18f85b616d76e120261e Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 2 Jul 2023 21:41:06 +0800 Subject: [PATCH 028/104] add entlongs iterator to EcsGroup --- src/EcsGroup.cs | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 80c8f2a..e7f354e 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -71,7 +71,10 @@ namespace DCFApixels.DragonECS public void Bake(List entities) => _source.Bake(entities); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan ToSpan() => _source.ToSpan(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan ToSpan(int start, int length) => _source.ToSpan(start, length); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsGroup.LongsIterator GetLongs() => _source.GetLongs(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public int First() => _source.First(); @@ -430,10 +433,7 @@ namespace DCFApixels.DragonECS #region Enumerator [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() - { - return new Enumerator(this); - } + public Enumerator GetEnumerator() => new Enumerator(this); IEnumerator IEnumerable.GetEnumerator() { for (int i = 0; i < _count; i++) @@ -444,6 +444,7 @@ namespace DCFApixels.DragonECS for (int i = 0; i < _count; i++) yield return _dense[i]; } + public LongsIterator GetLongs() => new LongsIterator(this); public ref struct Enumerator { private readonly int[] _dense; @@ -464,6 +465,45 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => ++_index <= _count && _count < _dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны } + public readonly struct LongsIterator : IEnumerable + { + private readonly EcsGroup _group; + public LongsIterator(EcsGroup group) => _group = group; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(_group); + IEnumerator IEnumerable.GetEnumerator() + { + for (int i = 0; i < _group._count; i++) + yield return _group.World.GetEntityLong(_group._dense[i]); + } + IEnumerator IEnumerable.GetEnumerator() + { + for (int i = 0; i < _group._count; i++) + yield return _group.World.GetEntityLong(_group._dense[i]); + } + public ref struct Enumerator + { + private readonly EcsWorld world; + private readonly int[] _dense; + private readonly int _count; + private int _index; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(EcsGroup group) + { + world = group.World; + _dense = group._dense; + _count = group._count; + _index = 0; + } + public entlong Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => world.GetEntityLong(_dense[_index]); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => ++_index <= _count && _count < _dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + } + } #endregion #region Object From 143faf24e0d3859caa8f703c0389c45eee6dea13 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 3 Jul 2023 02:40:13 +0800 Subject: [PATCH 029/104] add new unpack methods --- src/entlong.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/entlong.cs b/src/entlong.cs index 8f54c97..ea9883b 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -107,12 +107,33 @@ namespace DCFApixels.DragonECS world = EcsWorld.GetWorld(this.world); return IsAlive; } + public bool TryGetWorldID(out int worldID) + { + worldID = world; + return IsAlive; + } + public void Unpack(out EcsWorld world, out int id) + { + world = EcsWorld.GetWorld(this.world); + id = this.id; + } + public void Unpack(out int worldID, out int id) + { + worldID = world; + id = this.id; + } public bool TryUnpack(out EcsWorld world, out int id) { world = EcsWorld.GetWorld(this.world); id = this.id; return IsAlive; } + public bool TryUnpack(out int worldID, out int id) + { + worldID = world; + id = this.id; + return IsAlive; + } #endregion #region Equals From a11c7de42cb9e8a2a88fdc5d9233be80f01dc834 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 3 Jul 2023 02:44:35 +0800 Subject: [PATCH 030/104] add new getter methods to EcsWorld --- src/EcsWorld.cs | 15 +++++++++++++++ src/EcsWorld.static.cs | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 415e8b0..f36f4b1 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -108,6 +108,17 @@ namespace DCFApixels.DragonECS { return UncheckedGet>().instance; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPool GetPool(int worldID) where TPool : IEcsPoolImplementation, new() + { + return Get>(worldID).instance; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPool UncheckedGetPool(int worldID) where TPool : IEcsPoolImplementation, new() + { + return UncheckedGet>(worldID).instance; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public TAspect GetAspect() where TAspect : EcsAspect { @@ -123,6 +134,10 @@ namespace DCFApixels.DragonECS public ref T Get() where T : struct => ref WorldComponentPool.GetForWorld(id); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T UncheckedGet() where T : struct => ref WorldComponentPool.UncheckedGetForWorld(id); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Get(int worldID) where T : struct => ref WorldComponentPool.GetForWorld(worldID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T UncheckedGet(int worldID) where T : struct => ref WorldComponentPool.UncheckedGetForWorld(worldID); #endregion #region Where Query diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index 91b36be..8237000 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -39,6 +39,8 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetData(int worldID) => ref WorldComponentPool.GetForWorld(worldID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T UncheckedGetData(int worldID) => ref WorldComponentPool.UncheckedGetForWorld(worldID); private abstract class DataReleaser { From 5612fe047cec327b9b9d8ab52c17ce8773f7a1a0 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 3 Jul 2023 02:50:16 +0800 Subject: [PATCH 031/104] add simplified UncheckedGetPool for built-in pools --- src/Pools/EcsHybridPool.cs | 9 +++++++++ src/Pools/EcsPool.cs | 9 +++++++++ src/Pools/EcsTagPool.cs | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index f6faac4..7d11fe6 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -198,19 +198,28 @@ namespace DCFApixels.DragonECS } public static class EcsHybridPoolExt { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool GetPool(this EcsWorld self) where T : IEcsHybridComponent { return self.GetPool>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsHybridPool UncheckedGetPool(this EcsWorld self) where T : IEcsHybridComponent + { + return self.UncheckedGetPool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool Include(this EcsAspectBuilderBase self) where T : IEcsHybridComponent { return self.Include>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool Exclude(this EcsAspectBuilderBase self) where T : IEcsHybridComponent { return self.Exclude>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool Optional(this EcsAspectBuilderBase self) where T : IEcsHybridComponent { return self.Optional>(); diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index ac14c63..3f16701 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -191,19 +191,28 @@ namespace DCFApixels.DragonECS public interface IEcsComponent { } public static class EcsPoolExt { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool GetPool(this EcsWorld self) where TComponent : struct, IEcsComponent { return self.GetPool>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsPool UncheckedGetPool(this EcsWorld self) where TComponent : struct, IEcsComponent + { + return self.UncheckedGetPool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Include(this EcsAspectBuilderBase self) where TComponent : struct, IEcsComponent { return self.Include>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Exclude(this EcsAspectBuilderBase self) where TComponent : struct, IEcsComponent { return self.Exclude>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Optional(this EcsAspectBuilderBase self) where TComponent : struct, IEcsComponent { return self.Optional>(); diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 69f45a2..7e13bc8 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -186,19 +186,28 @@ namespace DCFApixels.DragonECS public interface IEcsTagComponent { } public static class EcsTagPoolExt { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsTagPool GetPool(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent { return self.GetPool>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTagPool UncheckedGetPool(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent + { + return self.UncheckedGetPool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsTagPool Include(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent { return self.Include>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsTagPool Exclude(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent { return self.Exclude>(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsTagPool Optional(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent { return self.Optional>(); From 112b96384d5c97918d3f4768ba09a0fb904412b0 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:00:25 +0800 Subject: [PATCH 032/104] Rework component ids Remove WorldMetaStorage --- src/EcsAspect.cs | 6 +- src/EcsWorld.cache.cs | 17 --- src/EcsWorld.cs | 43 +----- src/EcsWorld.pools.cs | 107 ++++++++++++++ src/EcsWorld.static.cs | 7 +- src/Utils/EcsTypeCode.cs | 11 +- src/Utils/IdDispenser.cs | 2 +- src/Utils/SparseArray.cs | 228 +++++++++++++++++++++++++++++ src/Utils/WorldMetaStorage.cs | 184 ----------------------- src/Utils/WorldMetaStorage.cs.meta | 11 -- 10 files changed, 354 insertions(+), 262 deletions(-) create mode 100644 src/EcsWorld.pools.cs create mode 100644 src/Utils/SparseArray.cs delete mode 100644 src/Utils/WorldMetaStorage.cs delete mode 100644 src/Utils/WorldMetaStorage.cs.meta diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index a65d555..dc386e0 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -257,7 +257,7 @@ namespace DCFApixels.DragonECS } internal class DebuggerProxy { - public readonly Type worldType; + public readonly EcsWorld world; public readonly int worldID; public readonly int[] included; public readonly int[] excluded; @@ -265,11 +265,11 @@ namespace DCFApixels.DragonECS public readonly Type[] excludedTypes; public DebuggerProxy(EcsMask mask) { - worldType = WorldMetaStorage.GetWorldType(mask.worldID); + world = EcsWorld.GetWorld(mask.worldID); worldID = mask.worldID; included = mask.inc; excluded = mask.exc; - Type converter(int o) => WorldMetaStorage.GetComponentType(worldID, o); + Type converter(int o) => world.GetComponentType(o); includedTypes = included.Select(converter).ToArray(); excludedTypes = excluded.Select(converter).ToArray(); } diff --git a/src/EcsWorld.cache.cs b/src/EcsWorld.cache.cs index 2ba0f22..487bc86 100644 --- a/src/EcsWorld.cache.cs +++ b/src/EcsWorld.cache.cs @@ -19,23 +19,6 @@ namespace DCFApixels.DragonECS component = default; } } - private TPool CreatePool() where TPool : IEcsPoolImplementation, new() - { - int index = WorldMetaStorage.GetPoolID(_worldTypeID); - if (index >= _pools.Length) - { - int oldCapacity = _pools.Length; - Array.Resize(ref _pools, _pools.Length << 1); - ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length); - } - if (_pools[index] == _nullPool) - { - var pool = new TPool(); - _pools[index] = pool; - pool.OnInit(this, index); - } - return (TPool)_pools[index]; - } internal readonly struct AspectCache : IEcsWorldComponent> where T : EcsAspect { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index f36f4b1..c2bc05d 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -10,9 +10,6 @@ namespace DCFApixels.DragonECS { public readonly short id; - private Type _worldType; - private int _worldTypeID; - private bool _isDestroyed; private IntDispenser _entityDispenser; @@ -25,9 +22,6 @@ namespace DCFApixels.DragonECS private int[] _delEntBuffer; private int _delEntBufferCount; - internal IEcsPoolImplementation[] _pools; - private EcsNullPool _nullPool = EcsNullPool.instance; - private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); @@ -36,7 +30,6 @@ namespace DCFApixels.DragonECS #region Properties public bool IsDestroyed => _isDestroyed; - public int WorldTypeID => _worldTypeID; public int Count => _entitiesCount; public int Capacity => _entitesCapacity; //_denseEntities.Length; public EcsReadonlyGroup Entities => _allEntites.Readonly; @@ -51,15 +44,12 @@ namespace DCFApixels.DragonECS if (isIndexable) { - id = (short)_worldIdDispenser.GetFree(); + id = (short)_worldIdDispenser.UseFree(); if (id >= Worlds.Length) Array.Resize(ref Worlds, Worlds.Length << 1); Worlds[id] = this; } - _worldType = this.GetType(); - _worldTypeID = WorldMetaStorage.GetWorldID(_worldType); - _entityDispenser = new IntDispenser(0); _pools = new IEcsPoolImplementation[512]; ArrayUtility.Fill(_pools, _nullPool); @@ -83,42 +73,15 @@ namespace DCFApixels.DragonECS ReleaseData(id); _worldIdDispenser.Release(id); _isDestroyed = true; + _poolIds = null; + _componentIds = null; } #endregion - #region ComponentInfo - public int GetComponentID() => WorldMetaStorage.GetComponentID(_worldTypeID); - public int GetComponentID(Type type) => WorldMetaStorage.GetComponentID(type, _worldTypeID); - public Type GetComponentType(int componentID) => WorldMetaStorage.GetComponentType(_worldTypeID, componentID); - public bool IsComponentTypeDeclared() => IsComponentTypeDeclared(typeof(T)); - public bool IsComponentTypeDeclared(Type type) => WorldMetaStorage.IsComponentTypeDeclared(_worldTypeID, type); - #endregion - #region Getters #if UNITY_2020_3_OR_NEWER [UnityEngine.Scripting.Preserve] #endif - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TPool GetPool() where TPool : IEcsPoolImplementation, new() - { - return Get>().instance; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TPool UncheckedGetPool() where TPool : IEcsPoolImplementation, new() - { - return UncheckedGet>().instance; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPool GetPool(int worldID) where TPool : IEcsPoolImplementation, new() - { - return Get>(worldID).instance; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPool UncheckedGetPool(int worldID) where TPool : IEcsPoolImplementation, new() - { - return UncheckedGet>(worldID).instance; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public TAspect GetAspect() where TAspect : EcsAspect { diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs new file mode 100644 index 0000000..1c83dff --- /dev/null +++ b/src/EcsWorld.pools.cs @@ -0,0 +1,107 @@ +using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.Utils; +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS +{ + public abstract partial class EcsWorld + { + private SparseArray _poolIds = new SparseArray(); + private SparseArray _componentIds = new SparseArray(); + private int _poolsCount; + internal IEcsPoolImplementation[] _pools; + private EcsNullPool _nullPool = EcsNullPool.instance; + + #region ComponentInfo + public int GetComponentID() => DeclareComponentType(EcsTypeCode.Get()); + public int GetComponentID(Type type) => DeclareComponentType(EcsTypeCode.Get(type)); + public bool IsComponentTypeDeclared() => _componentIds.Contains(EcsTypeCode.Get()); + public bool IsComponentTypeDeclared(Type type) => _componentIds.Contains(EcsTypeCode.Get(type)); + public Type GetComponentType(int componentID) => _pools[componentID].ComponentType; + #endregion + + #region Getters + +#if UNITY_2020_3_OR_NEWER + [UnityEngine.Scripting.Preserve] +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TPool GetPool() where TPool : IEcsPoolImplementation, new() + { + return Get>().instance; + } +#if UNITY_2020_3_OR_NEWER + [UnityEngine.Scripting.Preserve] +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TPool UncheckedGetPool() where TPool : IEcsPoolImplementation, new() + { + return UncheckedGet>().instance; + } +#if UNITY_2020_3_OR_NEWER + [UnityEngine.Scripting.Preserve] +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPool GetPool(int worldID) where TPool : IEcsPoolImplementation, new() + { + return Get>(worldID).instance; + } +#if UNITY_2020_3_OR_NEWER + [UnityEngine.Scripting.Preserve] +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPool UncheckedGetPool(int worldID) where TPool : IEcsPoolImplementation, new() + { + return UncheckedGet>(worldID).instance; + } + #endregion + + #region Declare/Create + private int DeclareComponentType(int typeCode) + { + if (!_componentIds.TryGetValue(typeCode, out int componentId)) + { + componentId = _poolsCount++; + _componentIds.Add(typeCode, componentId); + } + return componentId; + } + private TPool CreatePool() where TPool : IEcsPoolImplementation, new() + { + 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<>)); + int componentTypeCode = EcsTypeCode.Get(componentType); + + if (_componentIds.TryGetValue(componentTypeCode, out int componentID)) + { + _poolIds[componentTypeCode] = componentID; + } + else + { + componentID = _poolsCount++; + _poolIds[componentTypeCode] = componentID; + _componentIds[componentTypeCode] = componentID; + } + + if (_poolsCount >= _pools.Length) + { + int oldCapacity = _pools.Length; + Array.Resize(ref _pools, _pools.Length << 1); + ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length); + } + + if (_pools[componentID] == _nullPool) + { + var pool = new TPool(); + _pools[componentID] = pool; + pool.OnInit(this, componentID); + } + return (TPool)_pools[componentID]; + } + #endregion + } +} diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index 8237000..4b11c25 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -21,7 +21,7 @@ namespace DCFApixels.DragonECS private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2; private static EcsWorld[] Worlds = new EcsWorld[4]; - private static IntDispenser _worldIdDispenser = new IntDispenser(0); + private static IdDispenser _worldIdDispenser = new IdDispenser(0); private static List _dataReleaseres = new List(); @@ -101,5 +101,8 @@ namespace DCFApixels.DragonECS } } } - internal sealed class EcsNullWorld : EcsWorld { } + internal sealed class EcsNullWorld : EcsWorld + { + internal EcsNullWorld() : base(false) { } + } } diff --git a/src/Utils/EcsTypeCode.cs b/src/Utils/EcsTypeCode.cs index b18b4af..f90ac67 100644 --- a/src/Utils/EcsTypeCode.cs +++ b/src/Utils/EcsTypeCode.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { @@ -9,7 +10,8 @@ namespace DCFApixels.DragonECS { private static readonly Dictionary _codes = new Dictionary(); private static int _incremetn = 1; - public static int GetCode(Type type) + public static int Count => _codes.Count; + public static int Get(Type type) { if (!_codes.TryGetValue(type, out int code)) { @@ -18,10 +20,11 @@ namespace DCFApixels.DragonECS } return code; } - public static int Count => _codes.Count; - internal static class Cache + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Get() => Cache.code; + private static class Cache { - public static readonly int code = GetCode(typeof(T)); + public static readonly int code = Get(typeof(T)); } } } diff --git a/src/Utils/IdDispenser.cs b/src/Utils/IdDispenser.cs index beaf822..433edbb 100644 --- a/src/Utils/IdDispenser.cs +++ b/src/Utils/IdDispenser.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; -namespace DCFApixels +namespace DCFApixels.DragonECS.Utils { [Serializable] [DebuggerTypeProxy(typeof(DebuggerProxy))] diff --git a/src/Utils/SparseArray.cs b/src/Utils/SparseArray.cs new file mode 100644 index 0000000..9d83ae1 --- /dev/null +++ b/src/Utils/SparseArray.cs @@ -0,0 +1,228 @@ +//SparseArray. Analogous to Dictionary, but faster. +//Benchmark result of indexer.get speed test with 300 elements: +//[Dictinary: 5.786us] [SparseArray: 2.047us]. +using System; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS.Utils +{ + public class SparseArray + { + public const int MIN_CAPACITY_BITS_OFFSET = 4; + public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; + private const int EMPTY = -1; + + private int[] _buckets = Array.Empty(); + private Entry[] _entries = Array.Empty(); + + private int _count; + + private int _freeList; + private int _freeCount; + + private int _modBitMask; + + #region Properties + public TValue this[int keyX, int keyY] + { + get => _entries[FindEntry((keyX << 32) | keyY)].value; + set => Insert(keyX + (keyY << 32), value); + } + public TValue this[int key] + { + get => _entries[FindEntry(key)].value; + set => Insert(key, value); + } + public int Count => _count; + #endregion + + #region Constructors + public SparseArray(int minCapacity = MIN_CAPACITY) + { + minCapacity = NormalizeCapacity(minCapacity); + _buckets = new int[minCapacity]; + for (int i = 0; i < minCapacity; i++) + _buckets[i] = EMPTY; + _entries = new Entry[minCapacity]; + _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; + } + #endregion + + #region Add/Contains/Remove + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(int keyX, int keyY, TValue value) => Add((keyX << 32) | keyY, value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(int key, TValue value) + { +#if DEBUG + if (Contains(key)) + throw new ArgumentException("Contains(hashKey) is true"); +#endif + Insert(key, value); + } + + public bool Contains(int keyX, int keyY) => FindEntry((keyX << 32) | keyY) >= 0; + public bool Contains(int key) => FindEntry(key) >= 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(int keyX, int keyY) => Remove((keyX << 32) | keyY); + public bool Remove(int key) + { + int bucket = key & _modBitMask; + int last = -1; + for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next) + { + if (_entries[i].hashKey == key) + { + if (last < 0) + { + _buckets[bucket] = _entries[i].next; + } + else + { + _entries[last].next = _entries[i].next; + } + _entries[i].next = _freeList; + _entries[i].hashKey = -1; + _entries[i].value = default; + _freeList = i; + _freeCount++; + return true; + } + } + return false; + } + #endregion + + #region Find/Insert + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindEntry(int key) + { + for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next) + if (_entries[i].hashKey == key) return i; + return -1; + } + private void Insert(int key, TValue value) + { + int targetBucket = key & _modBitMask; + + for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashKey == key) + { + _entries[i].value = value; + return; + } + } + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; + } + else + { + if (_count == _entries.Length) + { + Resize(); + targetBucket = key & _modBitMask; + } + index = _count++; + } + + _entries[index].next = _buckets[targetBucket]; + _entries[index].hashKey = key; + _entries[index].value = value; + _buckets[targetBucket] = index; + } + #endregion + + #region TryGetValue + public bool TryGetValue(int keyX, int keyY, out TValue value) + { + int index = FindEntry((keyX << 32) | keyY); + if (index < 0) + { + value = default; + return false; + } + value = _entries[index].value; + return true; + } + public bool TryGetValue(int key, out TValue value) + { + int index = FindEntry(key); + if (index < 0) + { + value = default; + return false; + } + value = _entries[index].value; + return true; + } + #endregion + + #region Clear + public void Clear() + { + if (_count > 0) + { + for (int i = 0; i < _buckets.Length; i++) + { + _buckets[i] = -1; + } + Array.Clear(_entries, 0, _count); + _count = 0; + } + } + #endregion + + #region Resize + private void Resize() + { + int newSize = _buckets.Length << 1; + _modBitMask = (newSize - 1) & 0x7FFFFFFF; + + Contract.Assert(newSize >= _entries.Length); + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) + newBuckets[i] = EMPTY; + + Entry[] newEntries = new Entry[newSize]; + Array.Copy(_entries, 0, newEntries, 0, _count); + for (int i = 0; i < _count; i++) + { + if (newEntries[i].hashKey >= 0) + { + int bucket = newEntries[i].hashKey % newSize; + newEntries[i].next = newBuckets[bucket]; + newBuckets[bucket] = i; + } + } + _buckets = newBuckets; + _entries = newEntries; + } + + private int NormalizeCapacity(int capacity) + { + int result = MIN_CAPACITY; + while (result < capacity) result <<= 1; + return result; + } + #endregion + + #region Utils + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct Entry + { + public int next; // Index of next entry, -1 if last + public int hashKey; + public TValue value; + } + #endregion + } +} \ No newline at end of file diff --git a/src/Utils/WorldMetaStorage.cs b/src/Utils/WorldMetaStorage.cs deleted file mode 100644 index 85dd1a2..0000000 --- a/src/Utils/WorldMetaStorage.cs +++ /dev/null @@ -1,184 +0,0 @@ -using DCFApixels.DragonECS.Utils; -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace DCFApixels.DragonECS -{ - //TODO этот класс требует переработки, изначально такая конструкция имела хорошую производительность, но сейчас он слишком раздулся - internal static class WorldMetaStorage - { - private static int _tokenCount = 0; - private static List _resizers = new List(); - private static WorldTypeMeta[] _metas = new WorldTypeMeta[0]; - private static Dictionary _worldIds = new Dictionary(); - private static class WorldIndex - { - public static int id = GetWorldID(typeof(TWorldArchetype)); - } - private static int GetToken(Type worldType) - { - WorldTypeMeta meta = new WorldTypeMeta(worldType); - meta.id = _tokenCount; - Array.Resize(ref _metas, ++_tokenCount); - _metas[_tokenCount - 1] = meta; - - foreach (var item in _resizers) - item.Resize(_tokenCount); - return _tokenCount - 1; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetWorldID(Type worldType) - { - if (!_worldIds.TryGetValue(worldType, out int id)) - { - id = GetToken(worldType); - _worldIds.Add(worldType, id); - } - return id; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Type GetWorldType(int worldTypeID) => _metas[worldTypeID].worldType; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetWorldID() => WorldIndex.id; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetComponentID(int worldID) => Component.Get(worldID); - public static int GetComponentID(Type type, int worldID) => _metas[worldID].GetComponentID(type); - public static bool IsComponentTypeDeclared(int worldID, Type type) => _metas[worldID].IsDeclaredComponentType(type); - public static Type GetComponentType(int worldID, int componentID) => _metas[worldID].GetComponentType(componentID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetPoolID(int worldID) => Pool.Get(worldID); - - private abstract class ResizerBase - { - public abstract Type Type { get; } - public abstract int[] IDS { get; } - public abstract void Resize(int size); - } - - #region Containers - public static class PoolComponentIdArrays - { - private static Dictionary _componentTypeArrayPairs = new Dictionary(); - - public static int[] GetIdsArray(Type type) - { - int targetSize = _tokenCount; - if (!_componentTypeArrayPairs.TryGetValue(type, out int[] result)) - { - result = new int[targetSize]; - for (int i = 0; i < result.Length; i++) - result[i] = -1; - _componentTypeArrayPairs.Add(type, result); - } - else - { - if (result.Length < targetSize) - { - int oldSize = result.Length; - Array.Resize(ref result, targetSize); - ArrayUtility.Fill(result, -1, oldSize, targetSize); - _componentTypeArrayPairs[type] = result; - } - } - - return result; - } - - public static int GetComponentID(Type type, int token) - { - GetIdsArray(type); - ref int id = ref _componentTypeArrayPairs[type][token]; - if (id < 0) - id = _metas[token].DeclareComponentType(type); - return id; - } - } - private static class Pool - { - public static int[] ids; - private static Type componentType = typeof(T).GetGenericArguments()[0]; - static Pool() - { - ids = PoolComponentIdArrays.GetIdsArray(componentType); - _resizers.Add(new Resizer()); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Get(int token) - { - ref int id = ref ids[token]; - if (id < 0) - { - id = PoolComponentIdArrays.GetComponentID(componentType, token); - } - return id; - } - private sealed class Resizer : ResizerBase - { - public override Type Type => typeof(T); - public override int[] IDS => ids; - public override void Resize(int size) - { - ids = PoolComponentIdArrays.GetIdsArray(componentType); - } - } - } - private static class Component - { - public static int[] ids; - static Component() - { - ids = PoolComponentIdArrays.GetIdsArray(typeof(T)); - _resizers.Add(new Resizer()); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Get(int token) - { - ref int id = ref ids[token]; - if (id < 0) - { - id = PoolComponentIdArrays.GetComponentID(typeof(T), token); - } - return id; - } - private sealed class Resizer : ResizerBase - { - public override Type Type => typeof(T); - public override int[] IDS => ids; - public override void Resize(int size) - { - ids = PoolComponentIdArrays.GetIdsArray(typeof(T)); - } - } - } - #endregion - - private class WorldTypeMeta - { - public readonly Type worldType; - public int id; - public int componentCount; - public int worldComponentCount; - private Type[] _types = new Type[10]; - private Dictionary _declaredComponentTypes = new Dictionary(); - - public WorldTypeMeta(Type worldType) - { - this.worldType = worldType; - } - - public int DeclareComponentType(Type type) - { - int id = componentCount++; - if (_types.Length <= id) - Array.Resize(ref _types, id + 10); - _types[id] = type; - _declaredComponentTypes.Add(type, id); - return id; - } - public bool IsDeclaredComponentType(Type type) => _declaredComponentTypes.ContainsKey(type); - public Type GetComponentType(int componentID) => _types[componentID]; - public int GetComponentID(Type type) => PoolComponentIdArrays.GetComponentID(type, id); - } - } -} diff --git a/src/Utils/WorldMetaStorage.cs.meta b/src/Utils/WorldMetaStorage.cs.meta deleted file mode 100644 index ac629af..0000000 --- a/src/Utils/WorldMetaStorage.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5e06cf4352ab9414293b145eb27daba7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 1a5372b7c0429ddee267a52993d8235e7c600b38 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 4 Jul 2023 02:58:26 +0800 Subject: [PATCH 033/104] add explicit conversion from entlong to int --- src/entlong.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/entlong.cs b/src/entlong.cs index ea9883b..c8bd9a6 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -161,6 +161,8 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator long(in entlong a) => a.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator int(in entlong a) => a.ID; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator entlong(in long a) => new entlong(a); #endregion From a5951412bebade41ad45cae8d7d1b863727345c5 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 4 Jul 2023 02:59:09 +0800 Subject: [PATCH 034/104] add unity metas --- src/EcsWorld.pools.cs.meta | 11 +++++++++++ src/Utils/SparseArray.cs.meta | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/EcsWorld.pools.cs.meta create mode 100644 src/Utils/SparseArray.cs.meta diff --git a/src/EcsWorld.pools.cs.meta b/src/EcsWorld.pools.cs.meta new file mode 100644 index 0000000..59c9a1c --- /dev/null +++ b/src/EcsWorld.pools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2832746be4142a847b513bab5c276ba7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Utils/SparseArray.cs.meta b/src/Utils/SparseArray.cs.meta new file mode 100644 index 0000000..11d53db --- /dev/null +++ b/src/Utils/SparseArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25afb1113718f904c85dcbedd993d85e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 9b28349630804e8582bcc032f492797f9209f9c8 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 4 Jul 2023 04:05:27 +0800 Subject: [PATCH 035/104] Update README-RU.md --- README-RU.md | 51 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/README-RU.md b/README-RU.md index 84525c2..f687fd8 100644 --- a/README-RU.md +++ b/README-RU.md @@ -42,8 +42,9 @@ * [Пример для Unity](#Пример-для-Unity) * [Общий пример](#Общий-пример) * [Debug](#Debug) - * [Debug-Атрибуты](#Debug-Атрибуты) + * [Атрибуты](#Атрибуты) * [EcsDebug](#EcsDebug) + * [Профилирование](#Профилирование) * [Расширения](#Расширения) * [FAQ](#FAQ) @@ -558,14 +559,17 @@ public class EcsRoot # Debug Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. -## Debug-Атрибуты -В чистом виде дебаг-атрибуты не имеют применения, но используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах. +## Атрибуты +В чистом виде дебаг-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах. ``` c# using DCFApixels.DragonECS; // Задает пользовательское название типа, по умолчанию используется имя типа. -[DebugName("SomeComponent")] - +[DebugName("SomeComponent")] + +// Задает пользовательское название типа, по умолчанию используется имя типа. +[DebugGroup("Abilities/Passive/")] + // Задает цвет типа в системе rgb, где каждый канал принимает значение от 0 до 255, по умолчанию белый. [DebugColor(DebugColor.Red)] // или [DebugColor(255, 0, 0)] @@ -577,12 +581,47 @@ using DCFApixels.DragonECS; public struct Component { } ``` ## EcsDebug -Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы являются посредниками между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить его на другие движки, достаточно только реализовать специальный Debug-сервис. +Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить проект на другие движки, достаточно только реализовать специальный Debug-сервис. По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса. + +``` csharp +// Логирование. +EcsDebug.Print("Message"); + +// Логирование с тегом. +EcsDebug.Print("Tag", "Message"); + +// Прерывание игры. +EcsDebug.Break(); + +// Установка другого Debug-Сервиса. +EcsDebug.Set(); +``` + +## Профилирование +``` csharp +// Создание маркера с именем SomeMarker. +private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker"); + +... + +marker.Begin(); +// Код для которого замеряется скорость. +marker.End(); + +// или + +using (marker.Auto()) +{ + // Код для которого замеряется скорость. +} +``` + # Расширения * [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections) * [Поддержка классической C# многопоточности](https://github.com/DCFApixels/DragonECS-ClassicThreads) +* Отношения (Work in progress) * Интеграция с движком Unity (Work in progress) # FAQ From 04c474edf5c447d7fb37ac59983d9818663aea7e Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 4 Jul 2023 19:50:25 +0800 Subject: [PATCH 036/104] add properties to EcsMask --- src/EcsAspect.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index dc386e0..b082e5a 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -203,10 +203,13 @@ namespace DCFApixels.DragonECS public sealed class EcsMask { internal readonly int worldID; - /// Including constraints internal readonly int[] inc; - /// Excluding constraints internal readonly int[] exc; + public int WorldID => worldID; + /// Including constraints + public ReadOnlySpan Inc => inc; + /// Excluding constraints + public ReadOnlySpan Exc => exc; internal EcsMask(int worldID, int[] inc, int[] exc) { #if DEBUG From 60375bbe613f3afdbcae3b285f9ca3617d350b2f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 4 Jul 2023 19:56:24 +0800 Subject: [PATCH 037/104] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fc32f17..bc81d31 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,5 @@ DragonECS uses this versioning semantics: [Open](https://gist.github.com/DCFApix # Extensions * [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections) * [Classic C# multithreading support](https://github.com/DCFApixels/DragonECS-ClassicThreads) +* Relations (Work in progress) * Unity integration (Work in progress) From aba19f98b91728707a7d951fcce64a14d8303e93 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 17 Jul 2023 15:54:28 +0800 Subject: [PATCH 038/104] setup entlong serialization --- src/entlong.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/entlong.cs b/src/entlong.cs index c8bd9a6..dc1d730 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -12,16 +12,17 @@ namespace DCFApixels.DragonECS /// Strong identifier/Permanent entity identifier [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] [DebuggerTypeProxy(typeof(DebuggerProxy))] + [Serializable] public readonly struct entlong : IEquatable, IEquatable { public static readonly entlong NULL = default; [FieldOffset(0)] internal readonly long full; //Union - [FieldOffset(0)] + [FieldOffset(0), NonSerialized] internal readonly int id; - [FieldOffset(4)] + [FieldOffset(4), NonSerialized] internal readonly short gen; - [FieldOffset(6)] + [FieldOffset(6), NonSerialized] internal readonly short world; #region Properties @@ -154,16 +155,18 @@ namespace DCFApixels.DragonECS #region operators [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(in entlong a, in entlong b) => a.full == b.full; + public static bool operator ==(entlong a, entlong b) => a.full == b.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(in entlong a, in entlong b) => a.full != b.full; + public static bool operator !=(entlong a, entlong b) => a.full != b.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator long(in entlong a) => a.full; + public static explicit operator long(entlong a) => a.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator int(in entlong a) => a.ID; + public static explicit operator entlong(long a) => new entlong(a); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator entlong(in long a) => new entlong(a); + public static explicit operator int(entlong a) => a.ID; #endregion #region DebuggerProxy From 0c06d971aa64147b534f6d55ecf9e1acb5c0aa63 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 17 Jul 2023 15:55:23 +0800 Subject: [PATCH 039/104] update hybridpool / add clear not alive components --- src/Pools/EcsHybridPool.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 7d11fe6..8a68145 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -14,7 +14,9 @@ namespace DCFApixels.DragonECS private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense + private int[] _entities; private int _itemsCount; + private int[] _recycledItems; private int _recycledItemsCount; @@ -40,6 +42,7 @@ namespace DCFApixels.DragonECS _recycledItems = new int[128]; _recycledItemsCount = 0; _items = new T[capacity]; + _entities = new int[capacity]; _itemsCount = 0; } #endregion @@ -60,12 +63,16 @@ namespace DCFApixels.DragonECS { itemIndex = ++_itemsCount; if (itemIndex >= _items.Length) + { Array.Resize(ref _items, _items.Length << 1); + Array.Resize(ref _entities, _items.Length); + } } this.IncrementEntityComponentCount(entityID); _listeners.InvokeOnAdd(entityID); component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; + _entities[itemIndex] = entityID; } public void Set(int entityID, T component) { @@ -81,7 +88,10 @@ namespace DCFApixels.DragonECS { itemIndex = ++_itemsCount; if (itemIndex >= _items.Length) + { Array.Resize(ref _items, _items.Length << 1); + Array.Resize(ref _entities, _items.Length); + } } this.IncrementEntityComponentCount(entityID); } @@ -93,6 +103,7 @@ namespace DCFApixels.DragonECS _listeners.InvokeOnAdd(entityID); component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; + _entities[itemIndex] = entityID; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Get(int entityID) @@ -128,6 +139,7 @@ namespace DCFApixels.DragonECS Array.Resize(ref _recycledItems, _recycledItems.Length << 1); _recycledItems[_recycledItemsCount++] = itemIndex; _mapping[entityID] = 0; + _entities[itemIndex] = 0; _itemsCount--; this.DecrementEntityComponentCount(entityID); _listeners.InvokeOnDel(entityID); @@ -150,6 +162,15 @@ namespace DCFApixels.DragonECS #endif toWorld.GetPool().Set(toEntityID, Get(fromEntityID)); } + + public void ClearNotAliveComponents() + { + for (int i = _itemsCount - 1; i >= 0; i--) + { + if (!_items[i].IsAlive) + Del(_entities[i]); + } + } #endregion #region Callbacks From f8585293258b73f2d12368cb928b41dc383852ca Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 31 Oct 2023 03:03:13 +0800 Subject: [PATCH 040/104] update add IEcsDebugName add EcsDebugUtility.GetNameForObject fix warning add GC.Collect to EcsPipeline.Builder.Init fix bit shifts in SparseArray etc --- src/Debug/EcsDebugUtility.cs | 19 +++++++++++++++++++ src/Debug/Interfaces.meta | 8 ++++++++ src/Debug/Interfaces/IEcsDebugName.cs | 7 +++++++ src/Debug/Interfaces/IEcsDebugName.cs.meta | 11 +++++++++++ src/EcsPipeline.cs | 1 + src/EcsRunner.cs | 1 + src/EcsWorld.cs | 2 +- src/Pools/EcsHybridPool.cs | 8 +++++++- src/Utils/SparseArray.cs | 12 ++++++------ 9 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 src/Debug/Interfaces.meta create mode 100644 src/Debug/Interfaces/IEcsDebugName.cs create mode 100644 src/Debug/Interfaces/IEcsDebugName.cs.meta diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 803c9b6..937a838 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { @@ -57,8 +58,24 @@ namespace DCFApixels.DragonECS #endregion #region GetName + public static string GetNameForObject(IEcsDebugName obj) => obj.DebugName; + public static string GetNameForObject(object obj) => obj is IEcsDebugName dn ? dn.DebugName : GetName(obj.GetType()); public static string GetName() => GetName(typeof(T)); public static string GetName(Type type) => type.TryGetCustomAttribute(out DebugNameAttribute atr) ? atr.name : GetGenericTypeName(type); + public static bool TryGetCustomNameForObject(IEcsDebugName obj, out string name) + { + name = obj.DebugName; + return true; + } + public static bool TryGetCustomNameForObject(object obj, out string name) + { + if (obj is IEcsDebugName dn) + { + name = dn.DebugName; + return true; + } + return TryGetCustomName(obj.GetType(), out name); + } public static bool TryGetCustomName(out string name) => TryGetCustomName(typeof(T), out name); public static bool TryGetCustomName(Type type, out string name) { @@ -230,11 +247,13 @@ namespace DCFApixels.DragonECS #endregion #region ReflectionExtensions + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool TryGetCustomAttribute(this Type self, out T attribute) where T : Attribute { attribute = self.GetCustomAttribute(); return attribute != null; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool TryGetCustomAttribute(this MemberInfo self, out T attribute) where T : Attribute { attribute = self.GetCustomAttribute(); diff --git a/src/Debug/Interfaces.meta b/src/Debug/Interfaces.meta new file mode 100644 index 0000000..50388c5 --- /dev/null +++ b/src/Debug/Interfaces.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 50bc53c3762bf6b4ea127004a89a894e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Debug/Interfaces/IEcsDebugName.cs b/src/Debug/Interfaces/IEcsDebugName.cs new file mode 100644 index 0000000..90431f4 --- /dev/null +++ b/src/Debug/Interfaces/IEcsDebugName.cs @@ -0,0 +1,7 @@ +namespace DCFApixels.DragonECS +{ + public interface IEcsDebugName + { + string DebugName { get; } + } +} diff --git a/src/Debug/Interfaces/IEcsDebugName.cs.meta b/src/Debug/Interfaces/IEcsDebugName.cs.meta new file mode 100644 index 0000000..cdf6285 --- /dev/null +++ b/src/Debug/Interfaces/IEcsDebugName.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 170270e2ac54ab54a9dab57f58e25a9c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 6c39ded..7d7bb35 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -75,6 +75,7 @@ namespace DCFApixels.DragonECS _runRunnerCache = GetRunner(); _isInit = true; + GC.Collect(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 7e30725..be990fd 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -71,6 +71,7 @@ namespace DCFApixels.DragonECS Type interfaceType = item.BaseType.GenericTypeArguments[0]; _runnerHandlerTypes.Add(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, item); } + if (delayedExceptions.Count > 0) { throw new AggregateException(delayedExceptions); diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index c2bc05d..9bb781e 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -181,7 +181,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe entlong GetEntityLong(int entityID) { - long x = ((long)id << 48 | (long)_gens[entityID] << 32 | entityID); + long x = (long)id << 48 | (long)_gens[entityID] << 32 | (long)entityID; return *(entlong*)&x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 8a68145..df47d53 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -1,3 +1,4 @@ +using DCFApixels.DragonECS.Utils; using System; using System.Collections; using System.Collections.Generic; @@ -5,7 +6,7 @@ using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { - /// Pool for IEcsComponent components + /// Pool for IEcsHybridComponent components public sealed class EcsHybridPool : IEcsPoolImplementation, IEcsHybridPool, IEnumerable //IEnumerable - IntelliSense hack where T : IEcsHybridComponent { @@ -217,6 +218,11 @@ namespace DCFApixels.DragonECS void OnAddToPool(entlong entity); void OnDelFromPool(entlong entity); } + public static class IEcsHybridComponentExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNullOrNotAlive(this IEcsHybridComponent self) => self == null || self.IsAlive; + } public static class EcsHybridPoolExt { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Utils/SparseArray.cs b/src/Utils/SparseArray.cs index 9d83ae1..23fed2d 100644 --- a/src/Utils/SparseArray.cs +++ b/src/Utils/SparseArray.cs @@ -27,8 +27,8 @@ namespace DCFApixels.DragonECS.Utils #region Properties public TValue this[int keyX, int keyY] { - get => _entries[FindEntry((keyX << 32) | keyY)].value; - set => Insert(keyX + (keyY << 32), value); + get => _entries[FindEntry((keyX << 16) | keyY)].value; + set => Insert(keyX + (keyY << 16), value); } public TValue this[int key] { @@ -52,7 +52,7 @@ namespace DCFApixels.DragonECS.Utils #region Add/Contains/Remove [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(int keyX, int keyY, TValue value) => Add((keyX << 32) | keyY, value); + public void Add(int keyX, int keyY, TValue value) => Add((keyX << 16) | keyY, value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(int key, TValue value) { @@ -63,11 +63,11 @@ namespace DCFApixels.DragonECS.Utils Insert(key, value); } - public bool Contains(int keyX, int keyY) => FindEntry((keyX << 32) | keyY) >= 0; + public bool Contains(int keyX, int keyY) => FindEntry((keyX << 16) | keyY) >= 0; public bool Contains(int key) => FindEntry(key) >= 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Remove(int keyX, int keyY) => Remove((keyX << 32) | keyY); + public bool Remove(int keyX, int keyY) => Remove((keyX << 16) | keyY); public bool Remove(int key) { int bucket = key & _modBitMask; @@ -144,7 +144,7 @@ namespace DCFApixels.DragonECS.Utils #region TryGetValue public bool TryGetValue(int keyX, int keyY, out TValue value) { - int index = FindEntry((keyX << 32) | keyY); + int index = FindEntry((keyX << 16) | keyY); if (index < 0) { value = default; From f9b70c51b2a5528ac33d8138590cabff83d7d54b Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 1 Nov 2023 02:30:19 +0800 Subject: [PATCH 041/104] update debug utils remove IEcsDebugName add IEcsDebugMetaProvider update EcsDebugUtility --- src/Debug/EcsDebugUtility.cs | 148 +++++++++++++++--- src/Debug/Interfaces/IEcsDebugMetaProvider.cs | 7 + ....cs.meta => IEcsDebugMetaProvider.cs.meta} | 2 +- src/Debug/Interfaces/IEcsDebugName.cs | 7 - src/Pools/EcsHybridPool.cs | 62 ++++++++ 5 files changed, 200 insertions(+), 26 deletions(-) create mode 100644 src/Debug/Interfaces/IEcsDebugMetaProvider.cs rename src/Debug/Interfaces/{IEcsDebugName.cs.meta => IEcsDebugMetaProvider.cs.meta} (83%) delete mode 100644 src/Debug/Interfaces/IEcsDebugName.cs diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 937a838..a365fd8 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -2,11 +2,68 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; namespace DCFApixels.DragonECS { public static class EcsDebugUtility { + 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); @@ -46,7 +103,7 @@ namespace DCFApixels.DragonECS } private static string AutoToString(object target, Type type, bool isWriteName) { - var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var fields = type.GetFields(RFL_FLAGS); string[] values = new string[fields.Length]; for (int i = 0; i < fields.Length; i++) values[i] = (fields[i].GetValue(target) ?? "NULL").ToString(); @@ -58,25 +115,21 @@ namespace DCFApixels.DragonECS #endregion #region GetName - public static string GetNameForObject(IEcsDebugName obj) => obj.DebugName; - public static string GetNameForObject(object obj) => obj is IEcsDebugName dn ? dn.DebugName : GetName(obj.GetType()); - public static string GetName() => GetName(typeof(T)); - public static string GetName(Type type) => type.TryGetCustomAttribute(out DebugNameAttribute atr) ? atr.name : GetGenericTypeName(type); - public static bool TryGetCustomNameForObject(IEcsDebugName obj, out string name) + public static string GetName(object obj, int maxGenericDepth = 2) { - name = obj.DebugName; - return true; + return obj is IEcsDebugMetaProvider intr ? + GetName(intr.DebugMetaSource, maxGenericDepth) : + GetName(type: obj.GetType(), maxGenericDepth); } - public static bool TryGetCustomNameForObject(object obj, out string name) + public static string GetName(int maxGenericDepth = 2) => GetName(typeof(T), maxGenericDepth); + public static string GetName(Type type, int maxGenericDepth = 2) => type.TryGetCustomAttribute(out DebugNameAttribute atr) ? atr.name : GetGenericTypeName(type, maxGenericDepth); + public static bool TryGetCustomName(object obj, out string name) { - if (obj is IEcsDebugName dn) - { - name = dn.DebugName; - return true; - } - return TryGetCustomName(obj.GetType(), out name); + return obj is IEcsDebugMetaProvider intr ? + TryGetCustomName(intr.DebugMetaSource, out name) : + TryGetCustomName(type: obj.GetType(), out name); } - public static bool TryGetCustomName(out string name) => TryGetCustomName(typeof(T), out name); + public static bool TryGetCustomName(out string name) => TryGetCustomName(type: typeof(T), out name); public static bool TryGetCustomName(Type type, out string name) { if (type.TryGetCustomAttribute(out DebugNameAttribute atr)) @@ -90,8 +143,20 @@ namespace DCFApixels.DragonECS #endregion #region GetGroup + public static DebugGroup GetGroup(object obj) + { + return obj is IEcsDebugMetaProvider intr ? + GetGroup(intr.DebugMetaSource) : + GetGroup(type: obj.GetType()); + } public static DebugGroup GetGroup() => GetGroup(typeof(T)); public static DebugGroup GetGroup(Type type) => type.TryGetCustomAttribute(out DebugGroupAttribute atr) ? atr.GetData() : DebugGroup.Empty; + public static bool TryGetGroup(object obj, out DebugGroup group) + { + return obj is IEcsDebugMetaProvider intr ? + TryGetGroup(intr.DebugMetaSource, out group) : + TryGetGroup(type: obj.GetType(), out group); + } public static bool TryGetGroup(out DebugGroup text) => TryGetGroup(typeof(T), out text); public static bool TryGetGroup(Type type, out DebugGroup group) { @@ -106,8 +171,20 @@ namespace DCFApixels.DragonECS #endregion #region GetDescription + public static string GetDescription(object obj) + { + return obj is IEcsDebugMetaProvider intr ? + GetDescription(intr.DebugMetaSource) : + GetDescription(type: obj.GetType()); + } public static string GetDescription() => GetDescription(typeof(T)); public static string GetDescription(Type type) => type.TryGetCustomAttribute(out DebugDescriptionAttribute atr) ? atr.description : string.Empty; + public static bool TryGetDescription(object obj, out string text) + { + return obj is IEcsDebugMetaProvider intr ? + TryGetDescription(intr.DebugMetaSource, out text) : + TryGetDescription(type: obj.GetType(), out text); + } public static bool TryGetDescription(out string text) => TryGetDescription(typeof(T), out text); public static bool TryGetDescription(Type type, out string text) { @@ -122,7 +199,7 @@ namespace DCFApixels.DragonECS #endregion #region GetColor - private static Random random = new Random(); + private static Random random = new Random(100100100); private static Dictionary _words = new Dictionary(); private class WordColor { @@ -181,7 +258,7 @@ namespace DCFApixels.DragonECS } return nameColor.CalcColor(); } - public static List SplitString(string s) + private static List SplitString(string s) { string subs; List words = new List(); @@ -202,6 +279,12 @@ namespace DCFApixels.DragonECS return words; } + public static DebugColor GetColor(object obj) + { + return obj is IEcsDebugMetaProvider intr ? + GetColor(intr.DebugMetaSource) : + GetColor(type: obj.GetType()); + } public static DebugColor GetColor() => GetColor(typeof(T)); public static DebugColor GetColor(Type type) { @@ -213,6 +296,12 @@ namespace DCFApixels.DragonECS : DebugColor.BlackColor; #endif } + public static bool TryGetColor(object obj, out DebugColor color) + { + return obj is IEcsDebugMetaProvider intr ? + TryGetColor(intr.DebugMetaSource, out color) : + TryGetColor(type: obj.GetType(), out color); + } public static bool TryGetColor(out DebugColor color) => TryGetColor(typeof(T), out color); public static bool TryGetColor(Type type, out DebugColor color) { @@ -228,11 +317,34 @@ namespace DCFApixels.DragonECS #endregion #region IsHidden + public static bool IsHidden(object obj) + { + return obj is IEcsDebugMetaProvider intr ? + IsHidden(intr.DebugMetaSource) : + IsHidden(type: obj.GetType()); + } public static bool IsHidden() => IsHidden(typeof(T)); public static bool IsHidden(Type type) => type.TryGetCustomAttribute(out DebugHideAttribute _); #endregion + #region MetaSource + public static bool IsMetaSourceProvided(object obj) + { + return obj is IEcsDebugMetaProvider; + } + public static object GetMetaSource(object obj) + { + return obj is IEcsDebugMetaProvider intr ? intr.DebugMetaSource : obj; + } + #endregion + #region GenerateTypeDebugData + public static TypeDebugData GenerateTypeDebugData(object obj) + { + return obj is IEcsDebugMetaProvider intr ? + GenerateTypeDebugData(intr.DebugMetaSource) : + GenerateTypeDebugData(type: obj.GetType()); + } public static TypeDebugData GenerateTypeDebugData() => GenerateTypeDebugData(typeof(T)); public static TypeDebugData GenerateTypeDebugData(Type type) { diff --git a/src/Debug/Interfaces/IEcsDebugMetaProvider.cs b/src/Debug/Interfaces/IEcsDebugMetaProvider.cs new file mode 100644 index 0000000..706c8b9 --- /dev/null +++ b/src/Debug/Interfaces/IEcsDebugMetaProvider.cs @@ -0,0 +1,7 @@ +namespace DCFApixels.DragonECS +{ + public interface IEcsDebugMetaProvider + { + object DebugMetaSource { get; } + } +} diff --git a/src/Debug/Interfaces/IEcsDebugName.cs.meta b/src/Debug/Interfaces/IEcsDebugMetaProvider.cs.meta similarity index 83% rename from src/Debug/Interfaces/IEcsDebugName.cs.meta rename to src/Debug/Interfaces/IEcsDebugMetaProvider.cs.meta index cdf6285..793a2f6 100644 --- a/src/Debug/Interfaces/IEcsDebugName.cs.meta +++ b/src/Debug/Interfaces/IEcsDebugMetaProvider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 170270e2ac54ab54a9dab57f58e25a9c +guid: 2356ad2f91cd0a84db3d572a9f3c33f5 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/Debug/Interfaces/IEcsDebugName.cs b/src/Debug/Interfaces/IEcsDebugName.cs deleted file mode 100644 index 90431f4..0000000 --- a/src/Debug/Interfaces/IEcsDebugName.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DCFApixels.DragonECS -{ - public interface IEcsDebugName - { - string DebugName { get; } - } -} diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index df47d53..3cbf10a 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -252,4 +252,66 @@ namespace DCFApixels.DragonECS return self.Optional>(); } } + + + + + + + + public static class InterfaceMatrix + { + 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); + + public static void DeclareInterfacesFromClass() + { + Type type = typeof(T); + if (type.IsInterface) + throw new ArgumentException($"The argument {nameof(T)} cannot be an interface"); + } + } + 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 static int _increment; + } + internal static class InterfaceId + { + public static int _id = InterfaceId._increment++; + } } From b49be251a5f9257c0ba77679fa30c27e28e80582 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Thu, 2 Nov 2023 00:32:54 +0800 Subject: [PATCH 042/104] add SparseArray64 --- src/Utils/SparseArray64.cs | 236 ++++++++++++++++++++++++++++++++ src/Utils/SparseArray64.cs.meta | 11 ++ 2 files changed, 247 insertions(+) create mode 100644 src/Utils/SparseArray64.cs create mode 100644 src/Utils/SparseArray64.cs.meta diff --git a/src/Utils/SparseArray64.cs b/src/Utils/SparseArray64.cs new file mode 100644 index 0000000..69053a4 --- /dev/null +++ b/src/Utils/SparseArray64.cs @@ -0,0 +1,236 @@ +//SparseArray64. Analogous to Dictionary, but faster. +//Benchmark result of indexer.get speed test with 300 elements: +//[Dictinary: 6.705us] [SparseArray64: 2.512us]. +using System; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS.Utils +{ + internal class SparseArray64 + { + public const int MIN_CAPACITY_BITS_OFFSET = 4; + public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; + private const int EMPTY = -1; + + private int[] _buckets = Array.Empty(); + private Entry[] _entries = Array.Empty(); + + private int _count; + + private int _freeList; + private int _freeCount; + + private int _modBitMask; + + #region Properties + public TValue this[long keyX, long keyY] + { + get => _entries[FindEntry(keyX + (keyY << 32))].value; + set => Insert(keyX + (keyY << 32), value); + } + public TValue this[long key] + { + get => _entries[FindEntry(key)].value; + set => Insert(key, value); + } + + public int Count => _count; + #endregion + + #region Constructors + public SparseArray64(int minCapacity = MIN_CAPACITY) + { + minCapacity = NormalizeCapacity(minCapacity); + _buckets = new int[minCapacity]; + for (int i = 0; i < minCapacity; i++) + _buckets[i] = EMPTY; + _entries = new Entry[minCapacity]; + _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; + } + #endregion + + #region Add + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(long keyX, long keyY, TValue value) => Add(keyX + (keyY << 32), value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(long key, TValue value) + { +#if DEBUG + if (Contains(key)) + throw new ArgumentException("Contains(hashKey) is true"); +#endif + Insert(key, value); + } + #endregion + + #region Find/Insert/Remove + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindEntry(long key) + { + for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next) + if (_entries[i].hashKey == key) return i; + return -1; + } + private void Insert(long key, TValue value) + { + int targetBucket = unchecked((int)key & _modBitMask); + + for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashKey == key) + { + _entries[i].value = value; + return; + } + } + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; + } + else + { + if (_count == _entries.Length) + { + Resize(); + targetBucket = unchecked((int)key & _modBitMask); + } + index = _count++; + } + + _entries[index].next = _buckets[targetBucket]; + _entries[index].hashKey = key; + _entries[index].value = value; + _buckets[targetBucket] = index; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Remove(long keyX, long keyY) => Remove(keyX + (keyY << 32)); + public bool Remove(long key) + { + int bucket = unchecked((int)key & _modBitMask); + int last = -1; + for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next) + { + if (_entries[i].hashKey == key) + { + if (last < 0) + { + _buckets[bucket] = _entries[i].next; + } + else + { + _entries[last].next = _entries[i].next; + } + _entries[i].next = _freeList; + _entries[i].hashKey = -1; + _entries[i].value = default; + _freeList = i; + _freeCount++; + return true; + } + } + return false; + } + #endregion + + #region TryGetValue + public bool TryGetValue(long key, out TValue value) + { + int index = FindEntry(key); + if (index < 0) + { + value = default; + return false; + } + value = _entries[index].value; + return true; + } + public bool TryGetValue(long keyX, long keyY, out TValue value) + { + int index = FindEntry(keyX + (keyY << 32)); + if (index < 0) + { + value = default; + return false; + } + value = _entries[index].value; + return true; + } + #endregion + + #region Contains + public bool Contains(long keyX, long keyY) + { + return FindEntry(keyX + (keyY << 32)) >= 0; + } + public bool Contains(long key) + { + return FindEntry(key) >= 0; + } + #endregion + + #region Clear + public void Clear() + { + if (_count > 0) + { + for (int i = 0; i < _buckets.Length; i++) + { + _buckets[i] = -1; + } + Array.Clear(_entries, 0, _count); + _count = 0; + } + } + #endregion + + #region Resize + private void Resize() + { + int newSize = _buckets.Length << 1; + _modBitMask = (newSize - 1) & 0x7FFFFFFF; + + Contract.Assert(newSize >= _entries.Length); + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) + newBuckets[i] = EMPTY; + + Entry[] newEntries = new Entry[newSize]; + Array.Copy(_entries, 0, newEntries, 0, _count); + for (int i = 0; i < _count; i++) + { + if (newEntries[i].hashKey >= 0) + { + int bucket = unchecked((int)newEntries[i].hashKey & _modBitMask); + newEntries[i].next = newBuckets[bucket]; + newBuckets[bucket] = i; + } + } + _buckets = newBuckets; + _entries = newEntries; + } + + private int NormalizeCapacity(int capacity) + { + int result = MIN_CAPACITY; + while (result < capacity) result <<= 1; + return result; + } + #endregion + + #region Utils + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct Entry + { + public int next; // Index of next entry, -1 if last + public long hashKey; + public TValue value; + } + #endregion + } +} \ No newline at end of file diff --git a/src/Utils/SparseArray64.cs.meta b/src/Utils/SparseArray64.cs.meta new file mode 100644 index 0000000..623cbd0 --- /dev/null +++ b/src/Utils/SparseArray64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cab791e97df38274294035e89dc9de8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 6354d91769261209e495dec031ed7e7be10b8b4e Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:15:10 +0800 Subject: [PATCH 043/104] update Hybridity implementation --- src/Debug/EcsDebug.cs | 6 + src/Debug/EcsDebugUtility.cs | 54 -------- src/EcsRunner.cs | 82 ++++++++++++ src/EcsWorld.pools.cs | 2 +- src/Pools/EcsHybridPool.cs | 251 ++++++++++++++++++++--------------- src/Pools/EcsPool.cs | 29 ++-- src/Pools/EcsTagPool.cs | 19 ++- src/Utils/EcsTypeCode.cs | 56 +++++--- 8 files changed, 289 insertions(+), 210 deletions(-) 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 From 32c218f852db0a003fc267d50db9aad956fae891 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 16:37:41 +0800 Subject: [PATCH 044/104] Update README-RU.md --- README-RU.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index f687fd8..455e117 100644 --- a/README-RU.md +++ b/README-RU.md @@ -41,6 +41,7 @@ * [Корень ECS](#Корень-ECS) * [Пример для Unity](#Пример-для-Unity) * [Общий пример](#Общий-пример) + * [Гибридность](#Гибридность) * [Debug](#Debug) * [Атрибуты](#Атрибуты) * [EcsDebug](#EcsDebug) @@ -556,7 +557,78 @@ public class EcsRoot } } ``` - +## Гибридность +Для смешивания архитектурных подходов классического OOP и ECS используется специальный пул `EcsHybridPool`. Принцип работы этого пула несколько отличается от других, он упрощает поддержу наследования и полиморфизма. + +
+Как это работает? + +При добавлении элемента в пул, пул сканирует его иерархию наследования и реализуемые интерфейсы в поиске типов у которых есть интерфес `IEcsHybridComponent` и автоматически добавляет компонент в соответсвующие этим типам пулы. Таким же образом происходит удаление. Сканирвоание просиходит не для типа T а для типа экземпляра, поэтому в примере ниже строчка в `_world.GetPool().Add(entity, _rigidbody);` добавляет не только в пул `EcsHybridPool` но и в остальные. + +
+ +Пример использования: +``` csharp +public interface ITransform : IEcsHybridComponent +{ + Vector3 Position { get; set; } + // ... +} +public class Transform : ITransform +{ + public Vector3 Position { get; set; } + // ... +} +public class Rigidbody : ITransform +{ + public Vector3 Position { get; set; } + public float Mass { get; set; } + // ... +} +public class Camera : ITransform +{ + Vector3 Position { get; set; } + // ... +} +// ... + +EcsWorld _world; +Rigidbody _rigidbody; +// ... + +// Создадим пустую сущность. +int entity = _world.NewEmptyEntity(); +// Получаем пул EcsHybridPool и добавляем в него для сущности компонент _rigidbody. +// Если вместо ITransform подставить Transform или Rigidbody, то результат будет одинаковый +_world.GetPool().Add(entity, _rigidbody); +// ... + +//Все эти строчки вернут экземпляр _rigidbody. +ITransform iTransform = _world.GetPool().Get(entity); +Transform transform = _world.GetPool().Get(entity); +Rigidbody rigidbody = _world.GetPool().Get(entity); +//Исключение - отсутсвует компонент. Camera не является наследником или наследуемым классом для _rigidbody. +Camera camera = _world.GetPool().Get(entity); + +//Все эти строчки вернут True. +bool isITransform = _world.GetPool().Has(entity); +bool isTransform = _world.GetPool().Has(entity); +bool isRigidbody = _world.GetPool().Has(entity); +//Эта строчка вернет False. +bool isCamera = _world.GetPool().Has(entity); +// ... + +// Удалим у сущности компонент. +_world.GetPool().Del(entity); +// ... +//Все эти строчки вернут False. +bool isITransform = _world.GetPool().Has(entity); +bool isTransform = _world.GetPool().Has(entity); +bool isRigidbody = _world.GetPool().Has(entity); +bool isCamera = _world.GetPool().Has(entity); +// ... +``` + # Debug Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. ## Атрибуты From bfe60eece6e03c2c4decea81e42548b46b48ca75 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 16:51:35 +0800 Subject: [PATCH 045/104] Update README-RU.md --- README-RU.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index 455e117..9de693a 100644 --- a/README-RU.md +++ b/README-RU.md @@ -579,7 +579,7 @@ public class Transform : ITransform public Vector3 Position { get; set; } // ... } -public class Rigidbody : ITransform +public class Rigidbody : Transform { public Vector3 Position { get; set; } public float Mass { get; set; } @@ -592,6 +592,16 @@ public class Camera : ITransform } // ... +pubcli TransformAspect : EcsAspect +{ + public EcsPool transforms; + public Aspect(Builder b) + { + transforms = b.Include(); + } +} +// ... + EcsWorld _world; Rigidbody _rigidbody; // ... @@ -610,6 +620,9 @@ Rigidbody rigidbody = _world.GetPool().Get(entity); //Исключение - отсутсвует компонент. Camera не является наследником или наследуемым классом для _rigidbody. Camera camera = _world.GetPool().Get(entity); +//Вернет True. +bool isMatches = _world.GetAspect().IsMatches(entity); + //Все эти строчки вернут True. bool isITransform = _world.GetPool().Has(entity); bool isTransform = _world.GetPool().Has(entity); From cf1301208bc6081eaf788695a32c8e704a12e263 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 16:55:14 +0800 Subject: [PATCH 046/104] Update README-RU.md --- README-RU.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README-RU.md b/README-RU.md index 9de693a..c5c4ad3 100644 --- a/README-RU.md +++ b/README-RU.md @@ -590,11 +590,9 @@ public class Camera : ITransform Vector3 Position { get; set; } // ... } -// ... - pubcli TransformAspect : EcsAspect { - public EcsPool transforms; + public EcsHybridPool transforms; public Aspect(Builder b) { transforms = b.Include(); From db7db981f1477cde72a1637c64b8a36fc73808d8 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 16:55:47 +0800 Subject: [PATCH 047/104] Update README-RU.md --- README-RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index c5c4ad3..a8013b8 100644 --- a/README-RU.md +++ b/README-RU.md @@ -590,7 +590,7 @@ public class Camera : ITransform Vector3 Position { get; set; } // ... } -pubcli TransformAspect : EcsAspect +public TransformAspect : EcsAspect { public EcsHybridPool transforms; public Aspect(Builder b) From 362b0bb89aac08ce4fb8a73515f681c61d14ff88 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 16:56:25 +0800 Subject: [PATCH 048/104] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d4e1240..3fe128b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "displayName": "DragonECS", "description": "C# Entity Component System Framework", "unity": "2020.3", - "version": "0.7.8", + "version": "0.8.0", "repository": { "type": "git", "url": "https://github.com/DCFApixels/DragonECS.git" From cf60cc3de4f53cdabbfb13779f24edf18ad3d8be Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 16:58:54 +0800 Subject: [PATCH 049/104] Update README-RU.md --- README-RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index a8013b8..7c876e7 100644 --- a/README-RU.md +++ b/README-RU.md @@ -618,7 +618,7 @@ Rigidbody rigidbody = _world.GetPool().Get(entity); //Исключение - отсутсвует компонент. Camera не является наследником или наследуемым классом для _rigidbody. Camera camera = _world.GetPool().Get(entity); -//Вернет True. +//Вернет True. Поэтому фишка гибридных пулов будет работать и в запросах сущностей bool isMatches = _world.GetAspect().IsMatches(entity); //Все эти строчки вернут True. From 6707618293b3b1aefa9bc328e778f80b5087a2cc Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:04:58 +0800 Subject: [PATCH 050/104] Update README-RU.md --- README-RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index 7c876e7..a5578f3 100644 --- a/README-RU.md +++ b/README-RU.md @@ -650,7 +650,7 @@ using DCFApixels.DragonECS; // Задает пользовательское название типа, по умолчанию используется имя типа. [DebugName("SomeComponent")] -// Задает пользовательское название типа, по умолчанию используется имя типа. +// Используется для группировки типов. [DebugGroup("Abilities/Passive/")] // Задает цвет типа в системе rgb, где каждый канал принимает значение от 0 до 255, по умолчанию белый. From 2026373308c9c8577e0d712b9a038b8e9414a26d Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:13:29 +0800 Subject: [PATCH 051/104] Update README-RU.md --- README-RU.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index a5578f3..4df167d 100644 --- a/README-RU.md +++ b/README-RU.md @@ -192,13 +192,14 @@ const string SOME_LAYER = nameof(SOME_LAYER); EcsPipelone pipeline = EcsPipeline.New() //... .Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER) // Вставляет новый слой перед конечным слоем EcsConsts.END_LAYER + .Add(New SomeSystem(), SOME_LAYER) // Система SomeSystem будет вставлена в слой SOME_LAYER //... .BuildAndInit(); ``` Встроенные слои расположены в следующем порядке: * `EcsConst.PRE_BEGIN_LAYER` * `EcsConst.BEGIN_LAYER` -* `EcsConst.BASIC_LAYER` (Если при добавблении системы не казать слой, то она будет доавблена сюда) +* `EcsConst.BASIC_LAYER` (Если при добавблении системы не указать слой, то она будет доавблена сюда) * `EcsConst.END_LAYER` * `EcsConst.POST_END_LAYER` From 15aca8a4e4dcdb7dcfa36030e65ad0ff1bec998c Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:40:42 +0800 Subject: [PATCH 052/104] Update README-RU.md --- README-RU.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README-RU.md b/README-RU.md index 4df167d..a389c7b 100644 --- a/README-RU.md +++ b/README-RU.md @@ -49,6 +49,8 @@ * [Расширения](#Расширения) * [FAQ](#FAQ) +

+ # Установка * ### Unity-модуль Поддерживается установка в виде Unity-модуля в при помощи добавления git-URL [в PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) или ручного добавления в `Packages/manifest.json`: @@ -61,6 +63,8 @@ https://github.com/DCFApixels/DragonECS.git ### Версионирование В DragonECS применяется следующая семантика версионирования: [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md) +

+ # Основные концепции ## Entity **Сущности** - это то к чему крепятся данные. Реализованы в виде идентификаторов, которых есть 2 вида: @@ -641,6 +645,8 @@ bool isCamera = _world.GetPool().Has(entity); // ... ``` +

+ # Debug Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. ## Атрибуты @@ -702,11 +708,15 @@ using (marker.Auto()) } ``` +

+ # Расширения * [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections) * [Поддержка классической C# многопоточности](https://github.com/DCFApixels/DragonECS-ClassicThreads) * Отношения (Work in progress) * Интеграция с движком Unity (Work in progress) + +

# FAQ ## 'ReadOnlySpan<>' could not be found @@ -715,3 +725,6 @@ using (marker.Auto()) The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?) ``` Чтобы починить добавте директиву `ENABLE_DUMMY_SPAN` в `Project Settings/Player/Other Settings/Scripting Define Symbols`. + +


+ From 68f002056f261eb5cf5080fcdb2f1ec94a1e6a67 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 18:35:58 +0800 Subject: [PATCH 053/104] Update README-RU.md --- README-RU.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README-RU.md b/README-RU.md index a389c7b..a7e38c1 100644 --- a/README-RU.md +++ b/README-RU.md @@ -715,6 +715,7 @@ using (marker.Auto()) * [Поддержка классической C# многопоточности](https://github.com/DCFApixels/DragonECS-ClassicThreads) * Отношения (Work in progress) * Интеграция с движком Unity (Work in progress) +

From ab45c22d85ad3a261e5698bba51d4c42f6f76fa9 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 8 Nov 2023 19:55:57 +0800 Subject: [PATCH 054/104] Update README-RU.md --- README-RU.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README-RU.md b/README-RU.md index a7e38c1..1df1023 100644 --- a/README-RU.md +++ b/README-RU.md @@ -119,8 +119,7 @@ struct PlayerTag : IEcsTagComponent {} Встроенные виды компонентов: * `IEcsComponent` - Компоненты с данными. * `IEcsTagComponent` - Компоненты-теги. Без данных. -* `IEcsHybridComponent` - Гибридные компоненты. Предназначены для упрощения смешивания архитектурных подходов в проекте. Например можно MonoBehaviour-ы Unity прикреплять как обычные компоненты. -> Компоненты-теги хоть и не имеют данных, само наличие или отсутствие компонента-тега у сущности уже несет информацию и может применяться для определения типа сущности. +* `IEcsHybridComponent` - Гибридные компоненты. Испольщуются для реализации [гибридности](#Гибридность). ## System **Системы** - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы: @@ -316,10 +315,11 @@ public struct WorldComponent : IEcsWorldComponent > Компоненты можно применять для создания расширений в связке с методами расширений. ## Пул -Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей -* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс IEcsComponent; -* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с EcsPool имеет лучше оптимизацию памяти и действий с пулом, хранит в себе struct-компоненты реализующие IEcsTagComponent; - +Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей: +* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс `IEcsComponent`; +* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с `EcsPool` имеет лучше оптимизацию памяти и скорости, хранит struct-компоненты `IEcsTagComponent`; +* `EcsHybridPool` - пул для гибридных компонентов. Испольщуются для реализации [гибридности](#Гибридность), хранит struct-компоненты `IEcsHybridComponent`; + Пулы имеют 5 основных метода и их разновидности: ``` csharp // Один из способов получить пул из мира. From 0637aec1dcf157637b59cf19f67958f7a7e31933 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 15 Nov 2023 17:47:11 +0800 Subject: [PATCH 055/104] simple refactoring/fixes --- src/Builtin/BaseProcesses.cs | 32 ++++++++++++++++---------------- src/Debug/EcsDebugUtility.cs | 1 - src/EcsWorld.pools.cs | 7 ++++--- src/Pools/EcsTagPool.cs | 13 +++++++++++++ src/entlong.cs | 29 ++++++++++++----------------- 5 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index d172d1b..8f226f1 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -45,19 +45,19 @@ namespace DCFApixels.DragonECS #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) { + _markers[i].Begin(); try { - _markers[i].Begin(); targets[i].PreInit(pipeline); - _markers[i].End(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } + _markers[i].End(); } #else foreach (var item in targets) @@ -66,7 +66,7 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } @@ -93,19 +93,19 @@ namespace DCFApixels.DragonECS #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) { + _markers[i].Begin(); try { - _markers[i].Begin(); targets[i].Init(pipeline); - _markers[i].End(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } + _markers[i].End(); } #else foreach (var item in targets) @@ -114,7 +114,7 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } @@ -141,19 +141,19 @@ namespace DCFApixels.DragonECS #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) { + _markers[i].Begin(); try { - _markers[i].Begin(); targets[i].Run(pipeline); - _markers[i].End(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } + _markers[i].End(); } #else foreach (var item in targets) @@ -162,7 +162,7 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } @@ -189,19 +189,19 @@ namespace DCFApixels.DragonECS #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) { + _markers[i].Begin(); try { - _markers[i].Begin(); targets[i].Destroy(pipeline); - _markers[i].End(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } + _markers[i].End(); } #else foreach (var item in targets) @@ -210,7 +210,7 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS - throw; + throw e; #endif EcsDebug.PrintError(e); } diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 83a8541..0b2a083 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; -using System.Text.RegularExpressions; namespace DCFApixels.DragonECS { diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index c047cd2..b9d02f0 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -70,7 +70,8 @@ namespace DCFApixels.DragonECS } private TPool CreatePool() where TPool : IEcsPoolImplementation, new() { - if (_poolIds.Contains(EcsTypeCode.Get())) + int poolTypeCode = EcsTypeCode.Get(); + if (_poolIds.Contains(poolTypeCode)) throw new EcsFrameworkException("The pool has already been created."); Type componentType = typeof(TPool).GetInterfaces().First(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IEcsPoolImplementation<>)).GetGenericArguments()[0]; @@ -78,12 +79,12 @@ namespace DCFApixels.DragonECS if (_componentIds.TryGetValue(componentTypeCode, out int componentID)) { - _poolIds[componentTypeCode] = componentID; + _poolIds[poolTypeCode] = componentID; } else { componentID = _poolsCount++; - _poolIds[componentTypeCode] = componentID; + _poolIds[poolTypeCode] = componentID; _componentIds[componentTypeCode] = componentID; } diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 496af44..2256c93 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Reflection; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS @@ -18,6 +19,18 @@ namespace DCFApixels.DragonECS private T _fakeComponent; + + private static bool _isInvalidType; + static EcsTagPool() + { + _isInvalidType = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Length > 0; + } + public EcsTagPool() + { + if (_isInvalidType) + throw new EcsFrameworkException($"{typeof(T).Name} type must not contain any data."); + } + #region Properites public int Count => _count; int IEcsPool.Capacity => -1; diff --git a/src/entlong.cs b/src/entlong.cs index dc1d730..ab56ba7 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -137,22 +137,6 @@ namespace DCFApixels.DragonECS } #endregion - #region Equals - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(entlong other) => full == other.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(long other) => full == other; - #endregion - - #region Object - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => $"entity(id:{id} g:{gen} w:{world} {(IsNull ? "null" : IsAlive ? "alive" : "not alive")})"; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) => obj is entlong other && full == other.full; - #endregion - #region operators [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(entlong a, entlong b) => a.full == b.full; @@ -169,7 +153,18 @@ namespace DCFApixels.DragonECS public static explicit operator int(entlong a) => a.ID; #endregion - #region DebuggerProxy + #region Other + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() => $"entity(id:{id} g:{gen} w:{world} {(IsNull ? "null" : IsAlive ? "alive" : "not alive")})"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) => obj is entlong other && full == other.full; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(entlong other) => full == other.full; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(long other) => full == other; + internal class DebuggerProxy { private List _componentsList; From c6db8d2fc0c04496a337d3ca0f1c12695401fa54 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 15 Nov 2023 17:49:31 +0800 Subject: [PATCH 056/104] fix usings --- src/Pools/EcsHybridPool.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index abdf0c3..d796481 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -4,8 +4,6 @@ using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; -using UnityEngine; -using static UnityEditor.Progress; namespace DCFApixels.DragonECS { From 910dfb2d5183ad312b5776b906cf9fb2b5ad3bdc Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 21 Nov 2023 10:41:41 +0800 Subject: [PATCH 057/104] rename UncheckedGetPool to GetPoolUnchecked --- src/EcsWorld.pools.cs | 2 +- src/Pools/EcsHybridPool.cs | 33 +++++++++++++++++++++++++++++++-- src/Pools/EcsPool.cs | 5 +++-- src/Pools/EcsTagPool.cs | 33 +++++++++++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index b9d02f0..f6a9040 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -36,7 +36,7 @@ namespace DCFApixels.DragonECS [UnityEngine.Scripting.Preserve] #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TPool UncheckedGetPool() where TPool : IEcsPoolImplementation, new() + public TPool GetPoolUnchecked() where TPool : IEcsPoolImplementation, new() { return UncheckedGet>().instance; } diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index d796481..85f9383 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -235,9 +235,9 @@ namespace DCFApixels.DragonECS return self.GetPool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsHybridPool UncheckedGetPool(this EcsWorld self) where T : IEcsHybridComponent + public static EcsHybridPool GetPoolUnchecked(this EcsWorld self) where T : IEcsHybridComponent { - return self.UncheckedGetPool>(); + return self.GetPoolUnchecked>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -255,6 +255,35 @@ namespace DCFApixels.DragonECS { return self.Optional>(); } + + //------------------------------------------------- + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsHybridPool GetHybridPool(this EcsWorld self) where T : IEcsHybridComponent + { + return self.GetPool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsHybridPool GetHybridPoolUnchecked(this EcsWorld self) where T : IEcsHybridComponent + { + return self.GetPoolUnchecked>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsHybridPool IncludeHybrid(this EcsAspectBuilderBase self) where T : IEcsHybridComponent + { + return self.Include>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsHybridPool ExcludeHybrid(this EcsAspectBuilderBase self) where T : IEcsHybridComponent + { + return self.Exclude>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsHybridPool OptionalHybrid(this EcsAspectBuilderBase self) where T : IEcsHybridComponent + { + return self.Optional>(); + } } public partial class EcsWorld diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 8b058ec..7fdcfc7 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Reflection; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS @@ -194,9 +195,9 @@ namespace DCFApixels.DragonECS return self.GetPool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsPool UncheckedGetPool(this EcsWorld self) where TComponent : struct, IEcsComponent + public static EcsPool GetPoolUnchecked(this EcsWorld self) where TComponent : struct, IEcsComponent { - return self.UncheckedGetPool>(); + return self.GetPoolUnchecked>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 2256c93..30d7d61 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -202,9 +202,9 @@ namespace DCFApixels.DragonECS return self.GetPool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsTagPool UncheckedGetPool(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent + public static EcsTagPool GetPoolUnchecked(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent { - return self.UncheckedGetPool>(); + return self.GetPoolUnchecked>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -222,5 +222,34 @@ namespace DCFApixels.DragonECS { return self.Optional>(); } + + //--------------------------------------------------- + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTagPool GetTagPool(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent + { + return self.GetPool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTagPool GetTagPoolUnchecked(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent + { + return self.GetPoolUnchecked>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTagPool IncludeTag(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent + { + return self.Include>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTagPool ExcludeTag(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent + { + return self.Exclude>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTagPool OptionalTag(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent + { + return self.Optional>(); + } } } From cfcd2d131a86b3ef473eafc37ee54af797fa9530 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 21 Nov 2023 10:43:02 +0800 Subject: [PATCH 058/104] add entlong.NewUnsafe --- src/entlong.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/entlong.cs b/src/entlong.cs index ab56ba7..8ccdfd8 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -95,6 +95,12 @@ namespace DCFApixels.DragonECS { this.full = full; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe entlong NewUnsafe(long id, long gen, long world) + { + long x = id << 48 | gen << 32 | id; + return *(entlong*)&x; + } #endregion #region TryGetters From c6c61890c9835b8806aadf79f075e373bb33bf42 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:28:15 +0800 Subject: [PATCH 059/104] update EcsDebug --- src/Debug/EcsDebug.cs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index cf58594..8857d0f 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -31,6 +31,9 @@ namespace DCFApixels.DragonECS public static class EcsDebug { + public const string WARNING_TAG = EcsConsts.DEBUG_WARNING_TAG; + public const string ERROR_TAG = EcsConsts.DEBUG_ERROR_TAG; + public static void Set() where T : DebugService, new() => DebugService.Set(); public static void Set(DebugService service) => DebugService.Set(service); @@ -135,11 +138,33 @@ namespace DCFApixels.DragonECS } public override void Print(string tag, object v) { - Console.WriteLine($"[{tag}] {v}"); + if (string.IsNullOrEmpty(tag)) + { + Console.WriteLine(v); + } + else + { + var color = Console.ForegroundColor; + switch (tag) + { + case EcsDebug.ERROR_TAG: + Console.ForegroundColor = ConsoleColor.Red; + break; + case EcsDebug.WARNING_TAG: + Console.ForegroundColor = ConsoleColor.Yellow; + break; + } + Console.WriteLine($"[{tag}] {v}"); + Console.ForegroundColor = color; + } } public override void Break() { - Console.ReadLine(); + var color = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine("Press Enter to сontinue."); + Console.ReadKey(); + Console.ForegroundColor = color; } public override void ProfilerMarkBegin(int id) { @@ -147,10 +172,13 @@ namespace DCFApixels.DragonECS } public override void ProfilerMarkEnd(int id) { + var color = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.DarkGray; _stopwatchs[id].Stop(); var time = _stopwatchs[id].Elapsed; _stopwatchs[id].Reset(); Print("ProfilerMark", _stopwatchsNames[id] + " s:" + time.TotalSeconds); + Console.ForegroundColor = color; } protected override void OnDelProfilerMark(int id) { From de88b0a8ddacf5c640d1e5bd64b68b67a9786af9 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:38:28 +0800 Subject: [PATCH 060/104] fix console messages color --- src/Debug/EcsDebug.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index 8857d0f..9443fd9 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -131,11 +131,14 @@ namespace DCFApixels.DragonECS private string[] _stopwatchsNames; public DefaultDebugService() { + Console.ForegroundColor = ConsoleColor.White; + Console.BackgroundColor = ConsoleColor.Black; #if !DISABLE_DRAGONECS_DEBUGGER _stopwatchs = new Stopwatch[64]; _stopwatchsNames = new string[64]; #endif } + public override void Print(string tag, object v) { if (string.IsNullOrEmpty(tag)) From 5e6ac8cb168bf3deaea95fcfd2a8bb255e8a28c1 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 11:53:41 +0800 Subject: [PATCH 061/104] add gc-like clearing of deleted entities --- src/Builtin/Systems.cs | 9 ++++++--- src/EcsPipeline.cs | 2 +- src/EcsWorld.cs | 19 ++++++++++++++++++- src/EcsWorld.static.cs | 3 ++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Builtin/Systems.cs b/src/Builtin/Systems.cs index f478cf6..c164dcb 100644 --- a/src/Builtin/Systems.cs +++ b/src/Builtin/Systems.cs @@ -12,14 +12,17 @@ namespace DCFApixels.DragonECS public SystemsLayerMarkerSystem(string name) => this.name = name; } [DebugHide, DebugColor(DebugColor.Grey)] - public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsInject + public class EndFrameSystem : IEcsRunProcess, IEcsInject { - private List _worlds = new List(); + private readonly List _worlds = new List(); public void Inject(EcsWorld obj) => _worlds.Add(obj); public void Run(EcsPipeline pipeline) { foreach (var world in _worlds) + { world.DeleteEmptyEntites(); + world.ReleaseDelEntityBuffer(); + } } } [DebugHide, DebugColor(DebugColor.Grey)] @@ -31,7 +34,7 @@ namespace DCFApixels.DragonECS public EcsPool pool; public Aspect(Builder b) => pool = b.Include(); } - List _worlds = new List(); + private readonly List _worlds = new List(); public void Inject(EcsWorld obj) => _worlds.Add(obj); public void Run(EcsPipeline pipeline) { diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 7d7bb35..a3979ec 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -160,7 +160,7 @@ namespace DCFApixels.DragonECS } public EcsPipeline Build() { - Add(new DeleteEmptyEntitesSystem(), EcsConsts.POST_END_LAYER); + Add(new EndFrameSystem(), EcsConsts.POST_END_LAYER); List result = new List(32); List basicBlockList = _systems[_basicLayer]; foreach (var item in _systems) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 9bb781e..b17d31e 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -2,6 +2,7 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS @@ -21,6 +22,8 @@ namespace DCFApixels.DragonECS private int[] _delEntBuffer; private int _delEntBufferCount; + private int _delEntBufferMinCount; + private int _usedSpace; private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); @@ -32,6 +35,7 @@ namespace DCFApixels.DragonECS public bool IsDestroyed => _isDestroyed; public int Count => _entitiesCount; public int Capacity => _entitesCapacity; //_denseEntities.Length; + internal int DelBufferCount => _usedSpace - _entitiesCount; //_denseEntities.Length; public EcsReadonlyGroup Entities => _allEntites.Readonly; public ReadOnlySpan AllPools => _pools;// new ReadOnlySpan(pools, 0, _poolsCount); #endregion @@ -59,7 +63,9 @@ namespace DCFApixels.DragonECS ArrayUtility.Fill(_gens, DEATH_GEN_BIT); _delEntBufferCount = 0; - _delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET]; + //_delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET]; + _delEntBuffer = new int[_entitesCapacity]; + _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); _allEntites = GetFreeGroup(); } @@ -127,15 +133,23 @@ namespace DCFApixels.DragonECS #endregion #region Entity + + private int FreeSpace => Capacity - _usedSpace; public int NewEmptyEntity() { + if(FreeSpace <= 1 && DelBufferCount > _delEntBufferMinCount) + ReleaseDelEntityBuffer(); + int entityID = _entityDispenser.GetFree(); + _usedSpace++; _entitiesCount++; if (_gens.Length <= entityID) { Array.Resize(ref _gens, _gens.Length << 1); Array.Resize(ref _componentCounts, _gens.Length); + Array.Resize(ref _delEntBuffer, _gens.Length); + _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity); _entitesCapacity = _gens.Length; @@ -213,12 +227,15 @@ namespace DCFApixels.DragonECS } public void ReleaseDelEntityBuffer() { + if (_delEntBufferCount <= 0) + return; ReadOnlySpan buffser = new ReadOnlySpan(_delEntBuffer, 0, _delEntBufferCount); foreach (var pool in _pools) pool.OnReleaseDelEntityBuffer(buffser); _listeners.InvokeOnReleaseDelEntityBuffer(buffser); for (int i = 0; i < _delEntBufferCount; i++) _entityDispenser.Release(_delEntBuffer[i]); + _usedSpace = _entitiesCount; _delEntBufferCount = 0; } public void DeleteEmptyEntites() diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index 4b11c25..558804e 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -18,7 +18,8 @@ namespace DCFApixels.DragonECS { private const short GEN_BITS = 0x7fff; private const short DEATH_GEN_BIT = short.MinValue; - private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2; + private const int DEL_ENT_BUFFER_SIZE_OFFSET = 5; + private const int DEL_ENT_BUFFER_MIN_SIZE = 64; private static EcsWorld[] Worlds = new EcsWorld[4]; private static IdDispenser _worldIdDispenser = new IdDispenser(0); From b580d6918517031c2f55dd2436d3567fef08e71b Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:08:49 +0800 Subject: [PATCH 062/104] refactoring DelEntBuffer --- src/EcsWorld.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index b17d31e..d4ca88b 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -2,7 +2,6 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS @@ -23,7 +22,7 @@ namespace DCFApixels.DragonECS private int[] _delEntBuffer; private int _delEntBufferCount; private int _delEntBufferMinCount; - private int _usedSpace; + private int _freeSpace; private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); @@ -35,7 +34,9 @@ namespace DCFApixels.DragonECS public bool IsDestroyed => _isDestroyed; public int Count => _entitiesCount; public int Capacity => _entitesCapacity; //_denseEntities.Length; - internal int DelBufferCount => _usedSpace - _entitiesCount; //_denseEntities.Length; + + public int DelEntBufferCount => _delEntBufferCount; + public EcsReadonlyGroup Entities => _allEntites.Readonly; public ReadOnlySpan AllPools => _pools;// new ReadOnlySpan(pools, 0, _poolsCount); #endregion @@ -133,15 +134,13 @@ namespace DCFApixels.DragonECS #endregion #region Entity - - private int FreeSpace => Capacity - _usedSpace; public int NewEmptyEntity() { - if(FreeSpace <= 1 && DelBufferCount > _delEntBufferMinCount) + if(_freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount) ReleaseDelEntityBuffer(); int entityID = _entityDispenser.GetFree(); - _usedSpace++; + _freeSpace--; _entitiesCount++; if (_gens.Length <= entityID) @@ -235,7 +234,7 @@ namespace DCFApixels.DragonECS _listeners.InvokeOnReleaseDelEntityBuffer(buffser); for (int i = 0; i < _delEntBufferCount; i++) _entityDispenser.Release(_delEntBuffer[i]); - _usedSpace = _entitiesCount; + _freeSpace = _entitesCapacity - _entitiesCount; _delEntBufferCount = 0; } public void DeleteEmptyEntites() From 12f166a01a424894903bf2aaab48396ea3ab1c9c Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:09:03 +0800 Subject: [PATCH 063/104] create temp TestPool --- src/Pools/EcsTestPool.cs | 325 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 src/Pools/EcsTestPool.cs diff --git a/src/Pools/EcsTestPool.cs b/src/Pools/EcsTestPool.cs new file mode 100644 index 0000000..b7abf99 --- /dev/null +++ b/src/Pools/EcsTestPool.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS +{ + /// Pool for IEcsComponent components + public sealed class EcsTestPool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack + where T : struct, IEcsTestComponent + { + private EcsWorld _source; + private int _componentID; + + + public const int MIN_CAPACITY_BITS_OFFSET = 4; + public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; + private const int EMPTY = -1; + + private int[] _buckets = Array.Empty(); + private Entry[] _entries = Array.Empty(); + + private int _count; + + private int _freeList; + private int _freeCount; + + private int _modBitMask; + + + private IEcsComponentReset _componentResetHandler = EcsComponentResetHandler.instance; + private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; + + private List _listeners = new List(); + + #region Properites + public int Count => _count; + public int Capacity => _entries.Length; + public int ComponentID => _componentID; + public Type ComponentType => typeof(T); + public EcsWorld World => _source; + #endregion + + #region Methods + public void Copy(int fromEntityID, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); +#endif + _componentCopyHandler.Copy(ref Get(fromEntityID), ref TryAddOrGet(toEntityID)); + } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); +#endif + _componentCopyHandler.Copy(ref Get(fromEntityID), ref toWorld.GetPool().TryAddOrGet(toEntityID)); + } + #endregion + + + #region Callbacks + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) + { + _source = world; + _componentID = componentID; + + int minCapacity = 512; + minCapacity = NormalizeCapacity(minCapacity); + _buckets = new int[minCapacity]; + for (int i = 0; i < minCapacity; i++) + _buckets[i] = EMPTY; + _entries = new Entry[minCapacity]; + _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; + } + void IEcsPoolImplementation.OnWorldResize(int newSize) { } + void IEcsPoolImplementation.OnWorldDestroy() { } + void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + foreach (var entityID in buffer) + TryDel(entityID); + } + #endregion + + #region Other + void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID) = (T)dataRaw; + object IEcsPool.GetRaw(int entityID) => Read(entityID); + void IEcsPool.SetRaw(int entityID, object dataRaw) => Get(entityID) = (T)dataRaw; + ref readonly T IEcsStructPool.Read(int entityID) => ref Read(entityID); + ref T IEcsStructPool.Get(int entityID) => ref Get(entityID); + #endregion + + #region Listeners + public void AddListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Add(listener); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Remove(listener); + } + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + #endregion + + + #region Find/Insert/Remove + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindEntry(long x, long y) + { + return FindEntry(x << 32 | y); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int FindEntry(long key) + { + for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next) + if (_entries[i].key == key) return i; + return -1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Has(int entityID) => Has(entityID, entityID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool Has(long x, long y) => Has(x << 32 | y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool Has(long key) { + for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next) + if (_entries[i].key == key) return true; + return false; + } + + public ref T Add(int entityID) => ref Add(entityID, entityID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref T Add(long x, long y) => ref Add(x << 32 | y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref T Add(long key) + { + int entityID = unchecked((int)key); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (Has(key: key)) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID); +#endif + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; + } + else + { + if (_count == _entries.Length) + Resize(); + index = _count++; + } + int targetBucket = unchecked((int)key & _modBitMask); + + ref var entry = ref _entries[index]; + entry.next = _buckets[targetBucket]; + entry.key = key; + entry.value = default; + _buckets[targetBucket] = index; + this.IncrementEntityComponentCount(entityID); + _listeners.InvokeOnAddAndGet(entityID); + return ref entry.value; + } + public ref T TryAddOrGet(int entityID) + { + throw new NotImplementedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T Get(int entityID) => ref Get(entityID, entityID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref T Get(long x, long y) => ref Get(x << 32 | y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref T Get(long key) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(unchecked((int)key)); +#endif + _listeners.InvokeOnGet(unchecked((int)key)); + return ref _entries[FindEntry(key)].value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(int entityID) => ref Read(entityID, entityID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(long x, long y) => ref Read(x << 32 | y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(long key) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(unchecked((int)key)); +#endif + return ref _entries[FindEntry(key)].value; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Del(int entityID) => Del(entityID, entityID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Del(long keyX, long keyY) => Del(keyX + (keyY << 32)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Del(long key) + { + int entityID = unchecked((int)key); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); +#endif + int bucket = unchecked((int)key & _modBitMask); + int last = -1; + for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next) + { + if (_entries[i].key == key) + { + if (last < 0) + { + _buckets[bucket] = _entries[i].next; + } + else + { + _entries[last].next = _entries[i].next; + } + _entries[i].next = _freeList; + _entries[i].key = -1; + //_entries[i].value = default; + _componentResetHandler.Reset(ref _entries[i].value); + _freeList = i; + _freeCount++; + this.DecrementEntityComponentCount(entityID); + _listeners.InvokeOnDel(entityID); + return; + } + } + } + public void TryDel(int entityID) + { + if (Has(entityID)) Del(entityID); + } + #endregion + + #region Resize + private void Resize() + { + int newSize = _buckets.Length << 1; + _modBitMask = (newSize - 1) & 0x7FFFFFFF; + + Contract.Assert(newSize >= _entries.Length); + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) + newBuckets[i] = EMPTY; + + Entry[] newEntries = new Entry[newSize]; + Array.Copy(_entries, 0, newEntries, 0, _count); + for (int i = 0; i < _count; i++) + { + if (newEntries[i].key >= 0) + { + int bucket = unchecked((int)newEntries[i].key & _modBitMask); + newEntries[i].next = newBuckets[bucket]; + newBuckets[bucket] = i; + } + } + _buckets = newBuckets; + _entries = newEntries; + } + + private int NormalizeCapacity(int capacity) + { + int result = MIN_CAPACITY; + while (result < capacity) result <<= 1; + return result; + } + #endregion + + #region Utils + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct Entry + { + public int next; // Index of next entry, -1 if last + public long key; + public T value; + } + #endregion + } + /// Standard component + public interface IEcsTestComponent { } + public static class EcsTestPoolExt + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTestPool GetPool(this EcsWorld self) where TComponent : struct, IEcsTestComponent + { + return self.GetPool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTestPool GetPoolUnchecked(this EcsWorld self) where TComponent : struct, IEcsTestComponent + { + return self.GetPoolUnchecked>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTestPool Include(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent + { + return self.Include>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTestPool Exclude(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent + { + return self.Exclude>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsTestPool Optional(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent + { + return self.Optional>(); + } + } +} From 5edd55df88651a0204c2649d7c9123e733185f43 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 17:35:03 +0800 Subject: [PATCH 064/104] rework EcsMask --- src/EcsAspect.cs | 84 ++++++++++++++++++++++++-------------- src/EcsWorld.cs | 35 ++++++++++++---- src/EcsWorld.pools.cs | 3 ++ src/Pools/EcsHybridPool.cs | 4 +- src/Pools/EcsPool.cs | 9 ++-- src/Pools/EcsPoolBase.cs | 8 ++-- src/Pools/EcsTagPool.cs | 6 +-- src/Pools/EcsTestPool.cs | 4 +- src/Utils/EcsMaskBit.cs | 17 ++++++++ 9 files changed, 117 insertions(+), 53 deletions(-) create mode 100644 src/Utils/EcsMaskBit.cs diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index b082e5a..1db9584 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -123,10 +123,10 @@ namespace DCFApixels.DragonECS foreach (var item in _combined) { EcsMask submask = item.aspect.mask; - maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения - maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения - maskInc.UnionWith(submask.inc); - maskExc.UnionWith(submask.exc); + maskInc.ExceptWith(submask.excChunckMasks);//удаляю конфликтующие ограничения + maskExc.ExceptWith(submask.incChunckMasks);//удаляю конфликтующие ограничения + maskInc.UnionWith(submask.incChunckMasks); + maskExc.UnionWith(submask.excChunckMasks); } maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения @@ -139,12 +139,29 @@ namespace DCFApixels.DragonECS maskExc = _exc; } - var inc = maskInc.ToArray(); - Array.Sort(inc); - var exc = maskExc.ToArray(); - Array.Sort(exc); + int[] inc = new int[0]; + int[] exc = new int[0]; + foreach (var v in maskInc) + { + var bit = EcsMaskBit.FromPoolID(v); + if (inc.Length <= bit.chankIndex) + Array.Resize(ref inc, bit.chankIndex + 1); + inc[bit.chankIndex] |= bit.mask; + } + foreach (var v in maskExc) + { + var bit = EcsMaskBit.FromPoolID(v); + if (exc.Length <= bit.chankIndex) + Array.Resize(ref exc, bit.chankIndex + 1); + exc[bit.chankIndex] |= bit.mask; + } - mask = new EcsMask(_world.id, inc, exc); + //var inc = maskInc.ToArray(); + //Array.Sort(inc); + //var exc = maskExc.ToArray(); + //Array.Sort(exc); + + mask = new EcsMask(_world.id, inc.ToArray(), exc.ToArray()); _world = null; _inc = null; _exc = null; @@ -203,25 +220,25 @@ namespace DCFApixels.DragonECS public sealed class EcsMask { internal readonly int worldID; - internal readonly int[] inc; - internal readonly int[] exc; + internal readonly int[] incChunckMasks; + internal readonly int[] excChunckMasks; public int WorldID => worldID; /// Including constraints - public ReadOnlySpan Inc => inc; + public ReadOnlySpan Inc => incChunckMasks; /// Excluding constraints - public ReadOnlySpan Exc => exc; + public ReadOnlySpan Exc => excChunckMasks; internal EcsMask(int worldID, int[] inc, int[] exc) { #if DEBUG - CheckConstraints(inc, exc); + //CheckConstraints(inc, exc); #endif this.worldID = worldID; - this.inc = inc; - this.exc = exc; + this.incChunckMasks = inc; + this.excChunckMasks = exc; } #region Object - public override string ToString() => CreateLogString(worldID, inc, exc); + public override string ToString() => CreateLogString(worldID, incChunckMasks, excChunckMasks); #endregion #region Debug utils @@ -264,17 +281,17 @@ namespace DCFApixels.DragonECS public readonly int worldID; public readonly int[] included; public readonly int[] excluded; - public readonly Type[] includedTypes; - public readonly Type[] excludedTypes; + //public readonly Type[] includedTypes; + //public readonly Type[] excludedTypes; public DebuggerProxy(EcsMask mask) { world = EcsWorld.GetWorld(mask.worldID); worldID = mask.worldID; - included = mask.inc; - excluded = mask.exc; - Type converter(int o) => world.GetComponentType(o); - includedTypes = included.Select(converter).ToArray(); - excludedTypes = excluded.Select(converter).ToArray(); + included = mask.incChunckMasks; + excluded = mask.excChunckMasks; + //Type converter(int o) => world.GetComponentType(o); + //includedTypes = included.Select(converter).ToArray(); + //excludedTypes = excluded.Select(converter).ToArray(); } public override string ToString() => CreateLogString(worldID, included, excluded); } @@ -335,31 +352,36 @@ namespace DCFApixels.DragonECS private EcsGroup.Enumerator _sourceGroup; private readonly int[] _inc; private readonly int[] _exc; - private readonly IEcsPoolImplementation[] _pools; + private readonly int[][] _entitiesComponentMasks; public Enumerator(EcsReadonlyGroup sourceGroup, EcsMask mask) { _sourceGroup = sourceGroup.GetEnumerator(); - _inc = mask.inc; - _exc = mask.exc; - _pools = sourceGroup.World._pools; + _inc = mask.incChunckMasks; + _exc = mask.excChunckMasks; + _entitiesComponentMasks = sourceGroup.World._entitiesComponentMasks; } public int Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _sourceGroup.Current; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { while (_sourceGroup.MoveNext()) { int e = _sourceGroup.Current; for (int i = 0, iMax = _inc.Length; i < iMax; i++) - if (!_pools[_inc[i]].Has(e)) goto next; + { + if (_inc[i] > 0 && (_entitiesComponentMasks[e][i] & _inc[i]) == 0) goto skip; + } for (int i = 0, iMax = _exc.Length; i < iMax; i++) - if (_pools[_exc[i]].Has(e)) goto next; + { + if (_exc[i] > 0 && (_entitiesComponentMasks[e][i] & _exc[i]) != 0) goto skip; + } return true; - next: continue; + skip: continue; } return false; } diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index d4ca88b..43ac5a2 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -30,6 +30,8 @@ namespace DCFApixels.DragonECS private List _listeners = new List(); private List _entityListeners = new List(); + internal int[][] _entitiesComponentMasks; + #region Properties public bool IsDestroyed => _isDestroyed; public int Count => _entitiesCount; @@ -66,6 +68,10 @@ namespace DCFApixels.DragonECS _delEntBufferCount = 0; //_delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET]; _delEntBuffer = new int[_entitesCapacity]; + _entitiesComponentMasks = new int[_entitesCapacity][]; + for (int i = 0; i < _entitesCapacity; i++) + _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; + _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); _allEntites = GetFreeGroup(); @@ -148,6 +154,10 @@ namespace DCFApixels.DragonECS Array.Resize(ref _gens, _gens.Length << 1); Array.Resize(ref _componentCounts, _gens.Length); Array.Resize(ref _delEntBuffer, _gens.Length); + Array.Resize(ref _entitiesComponentMasks, _gens.Length); + for (int i = _entitesCapacity; i < _gens.Length; i++) + _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; + _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity); _entitesCapacity = _gens.Length; @@ -212,14 +222,14 @@ namespace DCFApixels.DragonECS if (mask.worldID != id) throw new EcsFrameworkException("The types of the target world of the mask and this world are different."); #endif - for (int i = 0, iMax = mask.inc.Length; i < iMax; i++) + for (int i = 0, iMax = mask.incChunckMasks.Length; i < iMax; i++) { - if (!_pools[mask.inc[i]].Has(entityID)) + if (!_pools[mask.incChunckMasks[i]].Has(entityID)) return false; } - for (int i = 0, iMax = mask.exc.Length; i < iMax; i++) + for (int i = 0, iMax = mask.excChunckMasks.Length; i < iMax; i++) { - if (_pools[mask.exc[i]].Has(entityID)) + if (_pools[mask.excChunckMasks[i]].Has(entityID)) return false; } return true; @@ -288,13 +298,22 @@ namespace DCFApixels.DragonECS #region Components Increment [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void IncrementEntityComponentCount(int entityID) => _componentCounts[entityID]++; + internal void IncrementEntityComponentCount(int entityID, int componentID) + { + _componentCounts[entityID]++; + EcsMaskBit bit = EcsMaskBit.FromPoolID(componentID); + _entitiesComponentMasks[entityID][bit.chankIndex] |= bit.mask; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void DecrementEntityComponentCount(int entityID) + internal void DecrementEntityComponentCount(int entityID, int componentID) { var count = --_componentCounts[entityID]; - if (count == 0 && _allEntites.Has(entityID)) DelEntity(entityID); -#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS + EcsMaskBit bit = EcsMaskBit.FromPoolID(componentID); + _entitiesComponentMasks[entityID][bit.chankIndex] &= ~bit.mask; + + if (count == 0 && _allEntites.Has(entityID)) + DelEntity(entityID); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (count < 0) Throw.World_InvalidIncrementComponentsBalance(); #endif } diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index f6a9040..a68be2d 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -93,6 +93,9 @@ namespace DCFApixels.DragonECS int oldCapacity = _pools.Length; Array.Resize(ref _pools, _pools.Length << 1); ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length); + + for (int i = 0; i < _entitesCapacity; i++) + Array.Resize(ref _entitiesComponentMasks[i], _pools.Length / 32 + 1); } if (_pools[componentID] == _nullPool) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 85f9383..68520b7 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -65,7 +65,7 @@ namespace DCFApixels.DragonECS Array.Resize(ref _entities, _items.Length); } } - this.IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnAdd(entityID); if(isMain) component.OnAddToPool(_source.GetEntityLong(entityID)); @@ -126,7 +126,7 @@ namespace DCFApixels.DragonECS _mapping[entityID] = 0; _entities[itemIndex] = 0; _itemsCount--; - this.DecrementEntityComponentCount(entityID); + this.DecrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnDel(entityID); } public void Del(int entityID) diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 7fdcfc7..cd7d1aa 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -12,6 +12,7 @@ namespace DCFApixels.DragonECS { private EcsWorld _source; private int _componentID; + private EcsMaskBit _maskBit; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense @@ -50,7 +51,7 @@ namespace DCFApixels.DragonECS if (itemIndex >= _items.Length) Array.Resize(ref _items, _items.Length << 1); } - this.IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnAddAndGet(entityID); return ref _items[itemIndex]; } @@ -87,7 +88,7 @@ namespace DCFApixels.DragonECS if (itemIndex >= _items.Length) Array.Resize(ref _items, _items.Length << 1); } - this.IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnAdd(entityID); } _listeners.InvokeOnGet(entityID); @@ -110,7 +111,7 @@ namespace DCFApixels.DragonECS _recycledItems[_recycledItemsCount++] = itemIndex; _mapping[entityID] = 0; _itemsCount--; - this.DecrementEntityComponentCount(entityID); + this.DecrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) @@ -139,6 +140,8 @@ namespace DCFApixels.DragonECS _source = world; _componentID = componentID; + _maskBit = EcsMaskBit.FromPoolID(componentID); + const int capacity = 512; _mapping = new int[world.Capacity]; diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index bcd8b90..370dfca 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -72,14 +72,14 @@ namespace DCFApixels.DragonECS public static class IEcsPoolImplementationExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IncrementEntityComponentCount(this IEcsPoolImplementation self, int entityID) + public static void IncrementEntityComponentCount(this IEcsPoolImplementation self, int entityID, int componentID) { - self.World.IncrementEntityComponentCount(entityID); + self.World.IncrementEntityComponentCount(entityID, componentID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DecrementEntityComponentCount(this IEcsPoolImplementation self, int entityID) + public static void DecrementEntityComponentCount(this IEcsPoolImplementation self, int entityID, int componentID) { - self.World.DecrementEntityComponentCount(entityID); + self.World.DecrementEntityComponentCount(entityID, componentID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 30d7d61..c5175b3 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -47,7 +47,7 @@ namespace DCFApixels.DragonECS #endif _count++; _mapping[entityID] = true; - this.IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnAdd(entityID); } public void TryAdd(int entityID) @@ -56,7 +56,7 @@ namespace DCFApixels.DragonECS { _count++; _mapping[entityID] = true; - this.IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnAdd(entityID); } } @@ -72,7 +72,7 @@ namespace DCFApixels.DragonECS #endif _mapping[entityID] = false; _count--; - this.DecrementEntityComponentCount(entityID); + this.DecrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) diff --git a/src/Pools/EcsTestPool.cs b/src/Pools/EcsTestPool.cs index b7abf99..7eaff35 100644 --- a/src/Pools/EcsTestPool.cs +++ b/src/Pools/EcsTestPool.cs @@ -167,7 +167,7 @@ namespace DCFApixels.DragonECS entry.key = key; entry.value = default; _buckets[targetBucket] = index; - this.IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnAddAndGet(entityID); return ref entry.value; } @@ -235,7 +235,7 @@ namespace DCFApixels.DragonECS _componentResetHandler.Reset(ref _entries[i].value); _freeList = i; _freeCount++; - this.DecrementEntityComponentCount(entityID); + this.DecrementEntityComponentCount(entityID, _componentID); _listeners.InvokeOnDel(entityID); return; } diff --git a/src/Utils/EcsMaskBit.cs b/src/Utils/EcsMaskBit.cs new file mode 100644 index 0000000..7050222 --- /dev/null +++ b/src/Utils/EcsMaskBit.cs @@ -0,0 +1,17 @@ +namespace DCFApixels.DragonECS +{ + public readonly struct EcsMaskBit + { + public readonly int chankIndex; + public readonly int mask; + public EcsMaskBit(int chankIndex, int mask) + { + this.chankIndex = chankIndex; + this.mask = mask; + } + public static EcsMaskBit FromPoolID(int id) + { + return new EcsMaskBit(id / 32, 1 << (id % 32)); + } + } +} From f9db91eeb21e67a65e0c1c98c0c6a084dafc0236 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 17:52:40 +0800 Subject: [PATCH 065/104] update new masks --- src/EcsAspect.cs | 123 +++++++++++++++++++--------------------- src/EcsWorld.cs | 31 +++++----- src/Utils/EcsMaskBit.cs | 5 ++ 3 files changed, 78 insertions(+), 81 deletions(-) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 1db9584..27394a6 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -30,8 +30,8 @@ namespace DCFApixels.DragonECS public sealed class Builder : EcsAspectBuilderBase { private EcsWorld _world; - private HashSet _inc; - private HashSet _exc; + private Dictionary _inc; + private Dictionary _exc; private List _combined; public EcsWorld World => _world; @@ -40,8 +40,8 @@ namespace DCFApixels.DragonECS { _world = world; _combined = new List(); - _inc = new HashSet(); - _exc = new HashSet(); + _inc = new Dictionary(); + _exc = new Dictionary(); } internal static TAspect Build(EcsWorld world) where TAspect : EcsAspect { @@ -82,18 +82,24 @@ namespace DCFApixels.DragonECS private void IncludeImplicit(Type type) { int id = _world.GetComponentID(type); -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); -#endif - _inc.Add(id); +//#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS +// if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); +//#endif + var bit = EcsMaskBit.FromPoolID(id); + if (!_inc.ContainsKey(bit.chankIndex)) + _inc.Add(bit.chankIndex, 0); + _inc[bit.chankIndex] = _inc[bit.chankIndex] | bit.mask; } private void ExcludeImplicit(Type type) { int id = _world.GetComponentID(type); -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); -#endif - _exc.Add(id); +//#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS +// if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); +//#endif + var bit = EcsMaskBit.FromPoolID(id); + if (!_exc.ContainsKey(bit.chankIndex)) + _exc.Add(bit.chankIndex, 0); + _exc[bit.chankIndex] = _exc[bit.chankIndex] | bit.mask; } #endregion @@ -113,25 +119,26 @@ namespace DCFApixels.DragonECS private void End(out EcsMask mask) { - HashSet maskInc; - HashSet maskExc; + Dictionary maskInc; + Dictionary maskExc; if (_combined.Count > 0) { - maskInc = new HashSet(); - maskExc = new HashSet(); - _combined.Sort((a, b) => a.order - b.order); - foreach (var item in _combined) - { - EcsMask submask = item.aspect.mask; - maskInc.ExceptWith(submask.excChunckMasks);//удаляю конфликтующие ограничения - maskExc.ExceptWith(submask.incChunckMasks);//удаляю конфликтующие ограничения - maskInc.UnionWith(submask.incChunckMasks); - maskExc.UnionWith(submask.excChunckMasks); - } - maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения - maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения - maskInc.UnionWith(_inc); - maskExc.UnionWith(_exc); + throw new NotImplementedException(); + // maskInc = new Dictionary(); + // maskExc = new Dictionary(); + // _combined.Sort((a, b) => a.order - b.order); + // foreach (var item in _combined) + // { + // EcsMask submask = item.aspect.mask; + // maskInc.ExceptWith(submask.excChunckMasks);//удаляю конфликтующие ограничения + // maskExc.ExceptWith(submask.incChunckMasks);//удаляю конфликтующие ограничения + // maskInc.UnionWith(submask.incChunckMasks); + // maskExc.UnionWith(submask.excChunckMasks); + // } + // maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения + // maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения + // maskInc.UnionWith(_inc); + // maskExc.UnionWith(_exc); } else { @@ -139,29 +146,10 @@ namespace DCFApixels.DragonECS maskExc = _exc; } - int[] inc = new int[0]; - int[] exc = new int[0]; - foreach (var v in maskInc) - { - var bit = EcsMaskBit.FromPoolID(v); - if (inc.Length <= bit.chankIndex) - Array.Resize(ref inc, bit.chankIndex + 1); - inc[bit.chankIndex] |= bit.mask; - } - foreach (var v in maskExc) - { - var bit = EcsMaskBit.FromPoolID(v); - if (exc.Length <= bit.chankIndex) - Array.Resize(ref exc, bit.chankIndex + 1); - exc[bit.chankIndex] |= bit.mask; - } - - //var inc = maskInc.ToArray(); - //Array.Sort(inc); - //var exc = maskExc.ToArray(); - //Array.Sort(exc); - - mask = new EcsMask(_world.id, inc.ToArray(), exc.ToArray()); + mask = new EcsMask( + _world.id, + maskInc.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(), + maskExc.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray()); _world = null; _inc = null; _exc = null; @@ -220,14 +208,14 @@ namespace DCFApixels.DragonECS public sealed class EcsMask { internal readonly int worldID; - internal readonly int[] incChunckMasks; - internal readonly int[] excChunckMasks; + internal readonly EcsMaskBit[] incChunckMasks; + internal readonly EcsMaskBit[] excChunckMasks; public int WorldID => worldID; /// Including constraints - public ReadOnlySpan Inc => incChunckMasks; + public ReadOnlySpan Inc => incChunckMasks; /// Excluding constraints - public ReadOnlySpan Exc => excChunckMasks; - internal EcsMask(int worldID, int[] inc, int[] exc) + public ReadOnlySpan Exc => excChunckMasks; + internal EcsMask(int worldID, EcsMaskBit[] inc, EcsMaskBit[] exc) { #if DEBUG //CheckConstraints(inc, exc); @@ -266,11 +254,12 @@ namespace DCFApixels.DragonECS return false; } #endif - private static string CreateLogString(int worldID, int[] inc, int[] exc) + private static string CreateLogString(int worldID, EcsMaskBit[] inc, EcsMaskBit[] exc) { #if (DEBUG && !DISABLE_DEBUG) - string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1); - return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})"; + //string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1); + //return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})"; + return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization #else return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization #endif @@ -279,8 +268,8 @@ namespace DCFApixels.DragonECS { public readonly EcsWorld world; public readonly int worldID; - public readonly int[] included; - public readonly int[] excluded; + public readonly EcsMaskBit[] included; + public readonly EcsMaskBit[] excluded; //public readonly Type[] includedTypes; //public readonly Type[] excludedTypes; public DebuggerProxy(EcsMask mask) @@ -350,8 +339,8 @@ namespace DCFApixels.DragonECS public ref struct Enumerator { private EcsGroup.Enumerator _sourceGroup; - private readonly int[] _inc; - private readonly int[] _exc; + private readonly EcsMaskBit[] _inc; + private readonly EcsMaskBit[] _exc; private readonly int[][] _entitiesComponentMasks; public Enumerator(EcsReadonlyGroup sourceGroup, EcsMask mask) @@ -374,11 +363,13 @@ namespace DCFApixels.DragonECS int e = _sourceGroup.Current; for (int i = 0, iMax = _inc.Length; i < iMax; i++) { - if (_inc[i] > 0 && (_entitiesComponentMasks[e][i] & _inc[i]) == 0) goto skip; + EcsMaskBit bit = _inc[i]; + if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) == 0) goto skip; } for (int i = 0, iMax = _exc.Length; i < iMax; i++) { - if (_exc[i] > 0 && (_entitiesComponentMasks[e][i] & _exc[i]) != 0) goto skip; + EcsMaskBit bit = _exc[i]; + if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != 0) goto skip; } return true; skip: continue; diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 43ac5a2..9b11153 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -218,21 +218,22 @@ namespace DCFApixels.DragonECS public bool IsMatchesMask(EcsMask mask, int entityID) { -#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS - if (mask.worldID != id) - throw new EcsFrameworkException("The types of the target world of the mask and this world are different."); -#endif - for (int i = 0, iMax = mask.incChunckMasks.Length; i < iMax; i++) - { - if (!_pools[mask.incChunckMasks[i]].Has(entityID)) - return false; - } - for (int i = 0, iMax = mask.excChunckMasks.Length; i < iMax; i++) - { - if (_pools[mask.excChunckMasks[i]].Has(entityID)) - return false; - } - return true; + throw new NotImplementedException(); +//#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS +// if (mask.worldID != id) +// throw new EcsFrameworkException("The types of the target world of the mask and this world are different."); +//#endif +// for (int i = 0, iMax = mask.incChunckMasks.Length; i < iMax; i++) +// { +// if (!_pools[mask.incChunckMasks[i]].Has(entityID)) +// return false; +// } +// for (int i = 0, iMax = mask.excChunckMasks.Length; i < iMax; i++) +// { +// if (_pools[mask.excChunckMasks[i]].Has(entityID)) +// return false; +// } +// return true; } public void ReleaseDelEntityBuffer() { diff --git a/src/Utils/EcsMaskBit.cs b/src/Utils/EcsMaskBit.cs index 7050222..7d3dfda 100644 --- a/src/Utils/EcsMaskBit.cs +++ b/src/Utils/EcsMaskBit.cs @@ -13,5 +13,10 @@ { return new EcsMaskBit(id / 32, 1 << (id % 32)); } + + public override string ToString() + { + return $"bit({chankIndex}, {mask})"; + } } } From 6411abf86961b6b304ae44a164b1ea562fe3505b Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 19:05:00 +0800 Subject: [PATCH 066/104] functional recovery --- src/EcsAspect.cs | 145 +++++++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 62 deletions(-) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 27394a6..cb3d621 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -30,8 +30,8 @@ namespace DCFApixels.DragonECS public sealed class Builder : EcsAspectBuilderBase { private EcsWorld _world; - private Dictionary _inc; - private Dictionary _exc; + private HashSet _inc; + private HashSet _exc; private List _combined; public EcsWorld World => _world; @@ -40,8 +40,8 @@ namespace DCFApixels.DragonECS { _world = world; _combined = new List(); - _inc = new Dictionary(); - _exc = new Dictionary(); + _inc = new HashSet(); + _exc = new HashSet(); } internal static TAspect Build(EcsWorld world) where TAspect : EcsAspect { @@ -82,24 +82,18 @@ namespace DCFApixels.DragonECS private void IncludeImplicit(Type type) { int id = _world.GetComponentID(type); -//#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS -// if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); -//#endif - var bit = EcsMaskBit.FromPoolID(id); - if (!_inc.ContainsKey(bit.chankIndex)) - _inc.Add(bit.chankIndex, 0); - _inc[bit.chankIndex] = _inc[bit.chankIndex] | bit.mask; +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); +#endif + _inc.Add(id); } private void ExcludeImplicit(Type type) { int id = _world.GetComponentID(type); -//#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS -// if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); -//#endif - var bit = EcsMaskBit.FromPoolID(id); - if (!_exc.ContainsKey(bit.chankIndex)) - _exc.Add(bit.chankIndex, 0); - _exc[bit.chankIndex] = _exc[bit.chankIndex] | bit.mask; +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); +#endif + _exc.Add(id); } #endregion @@ -119,26 +113,25 @@ namespace DCFApixels.DragonECS private void End(out EcsMask mask) { - Dictionary maskInc; - Dictionary maskExc; + HashSet maskInc; + HashSet maskExc; if (_combined.Count > 0) { - throw new NotImplementedException(); - // maskInc = new Dictionary(); - // maskExc = new Dictionary(); - // _combined.Sort((a, b) => a.order - b.order); - // foreach (var item in _combined) - // { - // EcsMask submask = item.aspect.mask; - // maskInc.ExceptWith(submask.excChunckMasks);//удаляю конфликтующие ограничения - // maskExc.ExceptWith(submask.incChunckMasks);//удаляю конфликтующие ограничения - // maskInc.UnionWith(submask.incChunckMasks); - // maskExc.UnionWith(submask.excChunckMasks); - // } - // maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения - // maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения - // maskInc.UnionWith(_inc); - // maskExc.UnionWith(_exc); + maskInc = new HashSet(); + maskExc = new HashSet(); + _combined.Sort((a, b) => a.order - b.order); + foreach (var item in _combined) + { + EcsMask submask = item.aspect.mask; + maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения + maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения + maskInc.UnionWith(submask.inc); + maskExc.UnionWith(submask.exc); + } + maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения + maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения + maskInc.UnionWith(_inc); + maskExc.UnionWith(_exc); } else { @@ -146,10 +139,29 @@ namespace DCFApixels.DragonECS maskExc = _exc; } - mask = new EcsMask( - _world.id, - maskInc.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(), - maskExc.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray()); + Dictionary r = new Dictionary(); + foreach (var id in maskInc) + { + var bit = EcsMaskBit.FromPoolID(id); + if(!r.TryAdd(bit.chankIndex, bit.mask)) + r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; + } + EcsMaskBit[] incMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(); + r.Clear(); + foreach (var id in maskExc) + { + var bit = EcsMaskBit.FromPoolID(id); + if (!r.TryAdd(bit.chankIndex, bit.mask)) + r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; + } + EcsMaskBit[] excMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(); + + var inc = maskInc.ToArray(); + Array.Sort(inc); + var exc = maskExc.ToArray(); + Array.Sort(exc); + + mask = new EcsMask(_world.id, inc, exc, incMasks, excMasks); _world = null; _inc = null; _exc = null; @@ -210,23 +222,27 @@ namespace DCFApixels.DragonECS internal readonly int worldID; internal readonly EcsMaskBit[] incChunckMasks; internal readonly EcsMaskBit[] excChunckMasks; + internal readonly int[] inc; + internal readonly int[] exc; public int WorldID => worldID; /// Including constraints - public ReadOnlySpan Inc => incChunckMasks; + public ReadOnlySpan Inc => inc; /// Excluding constraints - public ReadOnlySpan Exc => excChunckMasks; - internal EcsMask(int worldID, EcsMaskBit[] inc, EcsMaskBit[] exc) + public ReadOnlySpan Exc => exc; + internal EcsMask(int worldID, int[] inc, int[] exc, EcsMaskBit[] incChunckMasks, EcsMaskBit[] excChunckMasks) { #if DEBUG - //CheckConstraints(inc, exc); + CheckConstraints(inc, exc); #endif + this.inc = inc; + this.exc = exc; this.worldID = worldID; - this.incChunckMasks = inc; - this.excChunckMasks = exc; + this.incChunckMasks = incChunckMasks; + this.excChunckMasks = excChunckMasks; } #region Object - public override string ToString() => CreateLogString(worldID, incChunckMasks, excChunckMasks); + public override string ToString() => CreateLogString(worldID, inc, exc); #endregion #region Debug utils @@ -254,12 +270,11 @@ namespace DCFApixels.DragonECS return false; } #endif - private static string CreateLogString(int worldID, EcsMaskBit[] inc, EcsMaskBit[] exc) + private static string CreateLogString(int worldID, int[] inc, int[] exc) { #if (DEBUG && !DISABLE_DEBUG) - //string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1); - //return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})"; - return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization + string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 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 @@ -268,19 +283,24 @@ namespace DCFApixels.DragonECS { public readonly EcsWorld world; public readonly int worldID; - public readonly EcsMaskBit[] included; - public readonly EcsMaskBit[] excluded; - //public readonly Type[] includedTypes; - //public readonly Type[] excludedTypes; + public readonly EcsMaskBit[] includedChunkMasks; + public readonly EcsMaskBit[] excludedChunkMasks; + public readonly int[] included; + public readonly int[] excluded; + public readonly Type[] includedTypes; + public readonly Type[] excludedTypes; + public DebuggerProxy(EcsMask mask) { world = EcsWorld.GetWorld(mask.worldID); worldID = mask.worldID; - included = mask.incChunckMasks; - excluded = mask.excChunckMasks; - //Type converter(int o) => world.GetComponentType(o); - //includedTypes = included.Select(converter).ToArray(); - //excludedTypes = excluded.Select(converter).ToArray(); + includedChunkMasks = mask.incChunckMasks; + excludedChunkMasks = mask.excChunckMasks; + included = mask.inc; + excluded = mask.exc; + Type converter(int o) => world.GetComponentType(o); + includedTypes = included.Select(converter).ToArray(); + excludedTypes = excluded.Select(converter).ToArray(); } public override string ToString() => CreateLogString(worldID, included, excluded); } @@ -361,14 +381,15 @@ namespace DCFApixels.DragonECS while (_sourceGroup.MoveNext()) { int e = _sourceGroup.Current; + EcsMaskBit bit; for (int i = 0, iMax = _inc.Length; i < iMax; i++) { - EcsMaskBit bit = _inc[i]; + bit = _inc[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) == 0) goto skip; } for (int i = 0, iMax = _exc.Length; i < iMax; i++) { - EcsMaskBit bit = _exc[i]; + bit = _exc[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != 0) goto skip; } return true; From 4c236a11f3b51e99960d07c70d0e10c76bdbb674 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 20:28:11 +0800 Subject: [PATCH 067/104] functional recovery --- src/EcsWorld.cs | 101 +++++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 9b11153..3791d87 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -150,36 +150,8 @@ namespace DCFApixels.DragonECS _entitiesCount++; if (_gens.Length <= entityID) - { - Array.Resize(ref _gens, _gens.Length << 1); - Array.Resize(ref _componentCounts, _gens.Length); - Array.Resize(ref _delEntBuffer, _gens.Length); - Array.Resize(ref _entitiesComponentMasks, _gens.Length); - for (int i = _entitesCapacity; i < _gens.Length; i++) - _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; + Upsize(); - _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); - ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity); - _entitesCapacity = _gens.Length; - - for (int i = 0; i < _groups.Count; i++) - { - if (_groups[i].TryGetTarget(out EcsGroup group)) - { - group.OnWorldResize(_gens.Length); - } - else - { - int last = _groups.Count - 1; - _groups[i--] = _groups[last]; - _groups.RemoveAt(last); - } - } - foreach (var item in _pools) - item.OnWorldResize(_gens.Length); - - _listeners.InvokeOnWorldResize(_gens.Length); - } _gens[entityID] &= GEN_BITS; _allEntites.Add(entityID); _entityListeners.InvokeOnNewEntity(entityID); @@ -218,22 +190,21 @@ namespace DCFApixels.DragonECS public bool IsMatchesMask(EcsMask mask, int entityID) { - throw new NotImplementedException(); -//#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS -// if (mask.worldID != id) -// throw new EcsFrameworkException("The types of the target world of the mask and this world are different."); -//#endif -// for (int i = 0, iMax = mask.incChunckMasks.Length; i < iMax; i++) -// { -// if (!_pools[mask.incChunckMasks[i]].Has(entityID)) -// return false; -// } -// for (int i = 0, iMax = mask.excChunckMasks.Length; i < iMax; i++) -// { -// if (_pools[mask.excChunckMasks[i]].Has(entityID)) -// return false; -// } -// return true; +#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS + if (mask.worldID != id) + throw new EcsFrameworkException("The types of the target world of the mask and this world are different."); +#endif + for (int i = 0, iMax = mask.incChunckMasks.Length; i < iMax; i++) + { + if (!_pools[mask.inc[i]].Has(entityID)) + return false; + } + for (int i = 0, iMax = mask.excChunckMasks.Length; i < iMax; i++) + { + if (_pools[mask.exc[i]].Has(entityID)) + return false; + } + return true; } public void ReleaseDelEntityBuffer() { @@ -322,6 +293,41 @@ namespace DCFApixels.DragonECS #endregion + #region Upsize + //[MethodImpl(MethodImplOptions.NoInlining)] + private void Upsize() + { + Array.Resize(ref _gens, _gens.Length << 1); + Array.Resize(ref _componentCounts, _gens.Length); + Array.Resize(ref _delEntBuffer, _gens.Length); + Array.Resize(ref _entitiesComponentMasks, _gens.Length); + for (int i = _entitesCapacity; i < _gens.Length; i++) + _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; + + _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); + ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity); + _entitesCapacity = _gens.Length; + + for (int i = 0; i < _groups.Count; i++) + { + if (_groups[i].TryGetTarget(out EcsGroup group)) + { + group.OnWorldResize(_gens.Length); + } + else + { + int last = _groups.Count - 1; + _groups[i--] = _groups[last]; + _groups.RemoveAt(last); + } + } + foreach (var item in _pools) + item.OnWorldResize(_gens.Length); + + _listeners.InvokeOnWorldResize(_gens.Length); + } + #endregion + #region Groups Pool internal void RegisterGroup(EcsGroup group) { @@ -427,4 +433,9 @@ namespace DCFApixels.DragonECS public static entlong ToEntityLong(this int self, EcsWorld world) => world.GetEntityLong(self); } #endregion + + public class PoolsController + { + + } } From b07256f020daccea27ddbb4b7771f8626f79f9e5 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 22 Nov 2023 20:30:26 +0800 Subject: [PATCH 068/104] move EcsMaskBit to EcsAspect.cs --- src/EcsAspect.cs | 20 ++++++++++++++++++++ src/Utils/EcsMaskBit.cs | 22 ---------------------- 2 files changed, 20 insertions(+), 22 deletions(-) delete mode 100644 src/Utils/EcsMaskBit.cs diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index cb3d621..7291cb4 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -216,6 +216,26 @@ namespace DCFApixels.DragonECS #endregion #region Mask + public readonly struct EcsMaskBit + { + public readonly int chankIndex; + public readonly int mask; + public EcsMaskBit(int chankIndex, int mask) + { + this.chankIndex = chankIndex; + this.mask = mask; + } + public static EcsMaskBit FromPoolID(int id) + { + return new EcsMaskBit(id / 32, 1 << (id % 32)); + } + + public override string ToString() + { + return $"bit({chankIndex}, {mask})"; + } + } + [DebuggerTypeProxy(typeof(DebuggerProxy))] public sealed class EcsMask { diff --git a/src/Utils/EcsMaskBit.cs b/src/Utils/EcsMaskBit.cs deleted file mode 100644 index 7d3dfda..0000000 --- a/src/Utils/EcsMaskBit.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace DCFApixels.DragonECS -{ - public readonly struct EcsMaskBit - { - public readonly int chankIndex; - public readonly int mask; - public EcsMaskBit(int chankIndex, int mask) - { - this.chankIndex = chankIndex; - this.mask = mask; - } - public static EcsMaskBit FromPoolID(int id) - { - return new EcsMaskBit(id / 32, 1 << (id % 32)); - } - - public override string ToString() - { - return $"bit({chankIndex}, {mask})"; - } - } -} From db863bf8bffbf7b7d8f20bb3335ada29f4a3d51b Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Thu, 23 Nov 2023 00:37:03 +0800 Subject: [PATCH 069/104] tmp commit --- src/EcsAspect.cs | 13 ++++++++----- src/EcsWorld.cs | 4 ++-- src/Pools/EcsPool.cs | 2 +- src/Pools/EcsTestPool.cs | 9 ++++++++- src/Utils/SparseArray64.cs | 12 ++++++------ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 7291cb4..e3b69a6 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -142,7 +142,7 @@ namespace DCFApixels.DragonECS Dictionary r = new Dictionary(); foreach (var id in maskInc) { - var bit = EcsMaskBit.FromPoolID(id); + var bit = EcsMaskBit.FromID(id); if(!r.TryAdd(bit.chankIndex, bit.mask)) r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; } @@ -150,7 +150,7 @@ namespace DCFApixels.DragonECS r.Clear(); foreach (var id in maskExc) { - var bit = EcsMaskBit.FromPoolID(id); + var bit = EcsMaskBit.FromID(id); if (!r.TryAdd(bit.chankIndex, bit.mask)) r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; } @@ -218,6 +218,10 @@ namespace DCFApixels.DragonECS #region Mask public readonly struct EcsMaskBit { + private const int BITS = 32; + private const int DIV_SHIFT = 5; + private const int MOD_MASK = BITS - 1; + public readonly int chankIndex; public readonly int mask; public EcsMaskBit(int chankIndex, int mask) @@ -225,11 +229,10 @@ namespace DCFApixels.DragonECS this.chankIndex = chankIndex; this.mask = mask; } - public static EcsMaskBit FromPoolID(int id) + public static EcsMaskBit FromID(int id) { - return new EcsMaskBit(id / 32, 1 << (id % 32)); + return new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); //аналогично new EcsMaskBit(id / BITS, 1 << (id % BITS)) но быстрее } - public override string ToString() { return $"bit({chankIndex}, {mask})"; diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 3791d87..0d0051e 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -273,14 +273,14 @@ namespace DCFApixels.DragonECS internal void IncrementEntityComponentCount(int entityID, int componentID) { _componentCounts[entityID]++; - EcsMaskBit bit = EcsMaskBit.FromPoolID(componentID); + EcsMaskBit bit = EcsMaskBit.FromID(componentID); _entitiesComponentMasks[entityID][bit.chankIndex] |= bit.mask; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void DecrementEntityComponentCount(int entityID, int componentID) { var count = --_componentCounts[entityID]; - EcsMaskBit bit = EcsMaskBit.FromPoolID(componentID); + EcsMaskBit bit = EcsMaskBit.FromID(componentID); _entitiesComponentMasks[entityID][bit.chankIndex] &= ~bit.mask; if (count == 0 && _allEntites.Has(entityID)) diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index cd7d1aa..ba41891 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -140,7 +140,7 @@ namespace DCFApixels.DragonECS _source = world; _componentID = componentID; - _maskBit = EcsMaskBit.FromPoolID(componentID); + _maskBit = EcsMaskBit.FromID(componentID); const int capacity = 512; diff --git a/src/Pools/EcsTestPool.cs b/src/Pools/EcsTestPool.cs index 7eaff35..3bbc04f 100644 --- a/src/Pools/EcsTestPool.cs +++ b/src/Pools/EcsTestPool.cs @@ -187,7 +187,14 @@ namespace DCFApixels.DragonECS if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(unchecked((int)key)); #endif _listeners.InvokeOnGet(unchecked((int)key)); - return ref _entries[FindEntry(key)].value; + //return ref _entries[FindEntry(key)].value; + + for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next) + if (_entries[i].key == key) return ref _entries[i].value; // return i; + +#pragma warning disable CS0251 // + return ref _entries[-1].value; // . +#pragma warning restore CS0251 // } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Utils/SparseArray64.cs b/src/Utils/SparseArray64.cs index 69053a4..946f800 100644 --- a/src/Utils/SparseArray64.cs +++ b/src/Utils/SparseArray64.cs @@ -25,15 +25,15 @@ namespace DCFApixels.DragonECS.Utils private int _modBitMask; #region Properties - public TValue this[long keyX, long keyY] + public ref TValue this[long keyX, long keyY] { - get => _entries[FindEntry(keyX + (keyY << 32))].value; - set => Insert(keyX + (keyY << 32), value); + get => ref _entries[FindEntry(keyX + (keyY << 32))].value; + //set => Insert(keyX + (keyY << 32), value); } - public TValue this[long key] + public ref TValue this[long key] { - get => _entries[FindEntry(key)].value; - set => Insert(key, value); + get => ref _entries[FindEntry(key)].value; + //set => Insert(key, value); } public int Count => _count; From cf18d104f8db5ec7ceafd08d7efc850953a5e376 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:38:48 +0800 Subject: [PATCH 070/104] fix --- src/EcsAspect.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index e3b69a6..ebfbaa3 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -408,12 +408,14 @@ namespace DCFApixels.DragonECS for (int i = 0, iMax = _inc.Length; i < iMax; i++) { bit = _inc[i]; - if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) == 0) goto skip; + //if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) == 0) goto skip; + if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) goto skip; } for (int i = 0, iMax = _exc.Length; i < iMax; i++) { bit = _exc[i]; - if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != 0) goto skip; + //if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != 0) goto skip; + if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) == bit.mask) goto skip; } return true; skip: continue; From 6e16e2962ef3202c838d856fbe11287c89919ceb Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:58:06 +0800 Subject: [PATCH 071/104] update remove "pipeline" argument from base processes add PoolsMediator update component mask --- src/Builtin/BaseProcesses.cs | 32 ++-- src/Builtin/InjectSystem.cs | 20 ++- src/Builtin/Systems.cs | 5 +- src/EcsPipeline.cs | 8 +- src/EcsWorld.cs | 50 ++++-- src/EcsWorld.pools.cs | 18 +- src/Pools/EcsHybridPool.cs | 27 +-- src/Pools/EcsPool.cs | 20 ++- src/Pools/EcsPoolBase.cs | 15 +- src/Pools/EcsTagPool.cs | 21 ++- src/Pools/EcsTestPool.cs | 332 ----------------------------------- 11 files changed, 123 insertions(+), 425 deletions(-) delete mode 100644 src/Pools/EcsTestPool.cs diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index 8f226f1..468047e 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -7,19 +7,19 @@ namespace DCFApixels.DragonECS #region Interfaces public interface IEcsPreInitProcess : IEcsProcess { - void PreInit(EcsPipeline pipeline); + void PreInit(); } public interface IEcsInitProcess : IEcsProcess { - void Init(EcsPipeline pipeline); + void Init(); } public interface IEcsRunProcess : IEcsProcess { - void Run(EcsPipeline pipeline); + void Run(); } public interface IEcsDestroyProcess : IEcsProcess { - void Destroy(EcsPipeline pipeline); + void Destroy(); } #endregion @@ -40,7 +40,7 @@ namespace DCFApixels.DragonECS } } #endif - public void PreInit(EcsPipeline pipeline) + public void PreInit() { #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) @@ -48,7 +48,7 @@ namespace DCFApixels.DragonECS _markers[i].Begin(); try { - targets[i].PreInit(pipeline); + targets[i].PreInit(); } catch (Exception e) { @@ -62,7 +62,7 @@ namespace DCFApixels.DragonECS #else foreach (var item in targets) { - try { item.PreInit(pipeline); } + try { item.PreInit(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS @@ -88,7 +88,7 @@ namespace DCFApixels.DragonECS } } #endif - public void Init(EcsPipeline pipeline) + public void Init() { #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) @@ -96,7 +96,7 @@ namespace DCFApixels.DragonECS _markers[i].Begin(); try { - targets[i].Init(pipeline); + targets[i].Init(); } catch (Exception e) { @@ -110,7 +110,7 @@ namespace DCFApixels.DragonECS #else foreach (var item in targets) { - try { item.Init(pipeline); } + try { item.Init(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS @@ -136,7 +136,7 @@ namespace DCFApixels.DragonECS } } #endif - public void Run(EcsPipeline pipeline) + public void Run() { #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) @@ -144,7 +144,7 @@ namespace DCFApixels.DragonECS _markers[i].Begin(); try { - targets[i].Run(pipeline); + targets[i].Run(); } catch (Exception e) { @@ -158,7 +158,7 @@ namespace DCFApixels.DragonECS #else foreach (var item in targets) { - try { item.Run(pipeline); } + try { item.Run(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS @@ -184,7 +184,7 @@ namespace DCFApixels.DragonECS } } #endif - public void Destroy(EcsPipeline pipeline) + public void Destroy() { #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) @@ -192,7 +192,7 @@ namespace DCFApixels.DragonECS _markers[i].Begin(); try { - targets[i].Destroy(pipeline); + targets[i].Destroy(); } catch (Exception e) { @@ -206,7 +206,7 @@ namespace DCFApixels.DragonECS #else foreach (var item in targets) { - try { item.Destroy(pipeline); } + try { item.Destroy(); } catch (Exception e) { #if DISABLE_CATH_EXCEPTIONS diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index 47b8b50..36d28d1 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -102,32 +102,36 @@ namespace DCFApixels.DragonECS } public class InjectSystemBase { } [DebugHide, DebugColor(DebugColor.Gray)] - public class InjectSystem : InjectSystemBase, IEcsPreInitProcess, IEcsInject, IEcsPreInitInjectProcess + public class InjectSystem : InjectSystemBase, IEcsInject, IEcsPreInitProcess, IEcsInject, IEcsPreInitInjectProcess { - private T _injectedData; + private EcsPipeline _pipeline; + void IEcsInject.Inject(EcsPipeline obj) => _pipeline = obj; private PreInitInjectController _injectController; void IEcsInject.Inject(PreInitInjectController obj) => _injectController = obj; + + private T _injectedData; + public InjectSystem(T injectedData) { if (injectedData == null) Throw.ArgumentNull(); _injectedData = injectedData; } - public void PreInit(EcsPipeline pipeline) + public void PreInit() { if (_injectedData == null) return; if (_injectController == null) { - _injectController = new PreInitInjectController(pipeline); - var injectMapRunner = pipeline.GetRunner>(); - pipeline.GetRunner().OnPreInitInjectionBefore(); + _injectController = new PreInitInjectController(_pipeline); + var injectMapRunner = _pipeline.GetRunner>(); + _pipeline.GetRunner().OnPreInitInjectionBefore(); injectMapRunner.Inject(_injectController); } - var injectRunnerGeneric = pipeline.GetRunner>(); + var injectRunnerGeneric = _pipeline.GetRunner>(); injectRunnerGeneric.Inject(_injectedData); if (_injectController.OnInject()) { _injectController.Destroy(); - var injectCallbacksRunner = pipeline.GetRunner(); + var injectCallbacksRunner = _pipeline.GetRunner(); injectCallbacksRunner.OnPreInitInjectionAfter(); EcsRunner.Destroy(injectCallbacksRunner); } diff --git a/src/Builtin/Systems.cs b/src/Builtin/Systems.cs index c164dcb..d8f908f 100644 --- a/src/Builtin/Systems.cs +++ b/src/Builtin/Systems.cs @@ -16,7 +16,7 @@ namespace DCFApixels.DragonECS { private readonly List _worlds = new List(); public void Inject(EcsWorld obj) => _worlds.Add(obj); - public void Run(EcsPipeline pipeline) + public void Run() { foreach (var world in _worlds) { @@ -29,6 +29,7 @@ namespace DCFApixels.DragonECS public class DeleteOneFrameComponentSystem : IEcsRunProcess, IEcsInject where TComponent : struct, IEcsComponent { + public EcsPipeline pipeline { get; set; } private sealed class Aspect : EcsAspect { public EcsPool pool; @@ -36,7 +37,7 @@ namespace DCFApixels.DragonECS } private readonly List _worlds = new List(); public void Inject(EcsWorld obj) => _worlds.Add(obj); - public void Run(EcsPipeline pipeline) + public void Run() { for (int i = 0, iMax = _worlds.Count; i < iMax; i++) { diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index a3979ec..e03478d 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -67,10 +67,10 @@ namespace DCFApixels.DragonECS ecsPipelineInjectRunner.Inject(this); EcsRunner.Destroy(ecsPipelineInjectRunner); var preInitRunner = GetRunner(); - preInitRunner.PreInit(this); + preInitRunner.PreInit(); EcsRunner.Destroy(preInitRunner); var initRunner = GetRunner(); - initRunner.Init(this); + initRunner.Init(); EcsRunner.Destroy(initRunner); _runRunnerCache = GetRunner(); @@ -85,7 +85,7 @@ namespace DCFApixels.DragonECS if (!_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Run)); if (_isDestoryed) Throw.Pipeline_MethodCalledAfterDestruction(nameof(Run)); #endif - _runRunnerCache.Run(this); + _runRunnerCache.Run(); } public void Destroy() { @@ -98,7 +98,7 @@ namespace DCFApixels.DragonECS return; } _isDestoryed = true; - GetRunner().Destroy(this); + GetRunner().Destroy(); } #endregion diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 0d0051e..c031b56 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -3,6 +3,7 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using static DCFApixels.DragonECS.EcsWorld; namespace DCFApixels.DragonECS { @@ -32,6 +33,8 @@ namespace DCFApixels.DragonECS internal int[][] _entitiesComponentMasks; + private readonly PoolsMediator _poolsMediator; + #region Properties public bool IsDestroyed => _isDestroyed; public int Count => _entitiesCount; @@ -47,6 +50,8 @@ namespace DCFApixels.DragonECS public EcsWorld() : this(true) { } internal EcsWorld(bool isIndexable) { + _poolsMediator = new PoolsMediator(this); + _entitesCapacity = 512; if (isIndexable) @@ -268,20 +273,18 @@ namespace DCFApixels.DragonECS //public void CloneEntity(int fromEntityID, EcsWorld toWorld, int toEntityID) #endregion - #region Components Increment + #region Components Register [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void IncrementEntityComponentCount(int entityID, int componentID) + private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { _componentCounts[entityID]++; - EcsMaskBit bit = EcsMaskBit.FromID(componentID); - _entitiesComponentMasks[entityID][bit.chankIndex] |= bit.mask; + _entitiesComponentMasks[entityID][maskBit.chankIndex] |= maskBit.mask; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void DecrementEntityComponentCount(int entityID, int componentID) + private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { var count = --_componentCounts[entityID]; - EcsMaskBit bit = EcsMaskBit.FromID(componentID); - _entitiesComponentMasks[entityID][bit.chankIndex] &= ~bit.mask; + _entitiesComponentMasks[entityID][maskBit.chankIndex] &= ~maskBit.mask; if (count == 0 && _allEntites.Has(entityID)) DelEntity(entityID); @@ -382,6 +385,34 @@ namespace DCFApixels.DragonECS } } #endregion + + public readonly struct PoolsMediator + { + private readonly EcsWorld _world; + public PoolsMediator(EcsWorld world) + { + if (world == null) + { + throw new ArgumentNullException(); + } + if (world._poolsMediator._world != null) + { + throw new MethodAccessException("Нельзя создавать вручную"); + } + _world = world; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + { + _world.RegisterEntityComponent(entityID, componentTypeID, maskBit); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void UnregisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + { + _world.UnregisterEntityComponent(entityID, componentTypeID, maskBit); + } + } } #region Callbacks Interface @@ -433,9 +464,4 @@ namespace DCFApixels.DragonECS public static entlong ToEntityLong(this int self, EcsWorld world) => world.GetEntityLong(self); } #endregion - - public class PoolsController - { - - } } diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index a68be2d..ad4dda0 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -77,15 +77,15 @@ namespace DCFApixels.DragonECS 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)) + if (_componentIds.TryGetValue(componentTypeCode, out int componentTypeID)) { - _poolIds[poolTypeCode] = componentID; + _poolIds[poolTypeCode] = componentTypeID; } else { - componentID = _poolsCount++; - _poolIds[poolTypeCode] = componentID; - _componentIds[componentTypeCode] = componentID; + componentTypeID = _poolsCount++; + _poolIds[poolTypeCode] = componentTypeID; + _componentIds[componentTypeCode] = componentTypeID; } if (_poolsCount >= _pools.Length) @@ -98,13 +98,13 @@ namespace DCFApixels.DragonECS Array.Resize(ref _entitiesComponentMasks[i], _pools.Length / 32 + 1); } - if (_pools[componentID] == _nullPool) + if (_pools[componentTypeID] == _nullPool) { var pool = new TPool(); - _pools[componentID] = pool; - pool.OnInit(this, componentID); + _pools[componentTypeID] = pool; + pool.OnInit(this, _poolsMediator, componentTypeID); } - return (TPool)_pools[componentID]; + return (TPool)_pools[componentTypeID]; } #endregion } diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 68520b7..4f9d93f 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -20,7 +20,8 @@ namespace DCFApixels.DragonECS where T : IEcsHybridComponent { private EcsWorld _source; - private int _componentID; + private int _componentTypeID; + private EcsMaskBit _maskBit; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense @@ -32,10 +33,12 @@ namespace DCFApixels.DragonECS private List _listeners = new List(); + private EcsWorld.PoolsMediator _mediator; + #region Properites public int Count => _itemsCount; public int Capacity => _items.Length; - public int ComponentID => _componentID; + public int ComponentID => _componentTypeID; public Type ComponentType => typeof(T); public EcsWorld World => _source; #endregion @@ -65,7 +68,7 @@ namespace DCFApixels.DragonECS Array.Resize(ref _entities, _items.Length); } } - this.IncrementEntityComponentCount(entityID, _componentID); + _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnAdd(entityID); if(isMain) component.OnAddToPool(_source.GetEntityLong(entityID)); @@ -126,7 +129,7 @@ namespace DCFApixels.DragonECS _mapping[entityID] = 0; _entities[itemIndex] = 0; _itemsCount--; - this.DecrementEntityComponentCount(entityID, _componentID); + _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnDel(entityID); } public void Del(int entityID) @@ -167,10 +170,12 @@ namespace DCFApixels.DragonECS #endregion #region Callbacks - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) + void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { _source = world; - _componentID = componentID; + _mediator = mediator; + _componentTypeID = componentTypeID; + _maskBit = EcsMaskBit.FromID(componentTypeID); const int capacity = 512; @@ -286,16 +291,15 @@ namespace DCFApixels.DragonECS } } - public partial class EcsWorld + public abstract partial class EcsWorld { - private Dictionary _mappings = new Dictionary(); - + private Dictionary _hybridMapping = new Dictionary(); internal HybridMapping GetHybridMapping(Type type) { - if(!_mappings.TryGetValue(type, out HybridMapping mapping)) + if(!_hybridMapping.TryGetValue(type, out HybridMapping mapping)) { mapping = new HybridMapping(this, type); - _mappings.Add(type, mapping); + _hybridMapping.Add(type, mapping); } return mapping; } @@ -313,7 +317,6 @@ namespace DCFApixels.DragonECS 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() { diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index ba41891..73fabc8 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -11,7 +11,7 @@ namespace DCFApixels.DragonECS where T : struct, IEcsComponent { private EcsWorld _source; - private int _componentID; + private int _componentTypeID; private EcsMaskBit _maskBit; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID @@ -25,10 +25,12 @@ namespace DCFApixels.DragonECS private List _listeners = new List(); + private EcsWorld.PoolsMediator _mediator; + #region Properites public int Count => _itemsCount; public int Capacity => _items.Length; - public int ComponentID => _componentID; + public int ComponentID => _componentTypeID; public Type ComponentType => typeof(T); public EcsWorld World => _source; #endregion @@ -51,7 +53,7 @@ namespace DCFApixels.DragonECS if (itemIndex >= _items.Length) Array.Resize(ref _items, _items.Length << 1); } - this.IncrementEntityComponentCount(entityID, _componentID); + _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnAddAndGet(entityID); return ref _items[itemIndex]; } @@ -88,7 +90,7 @@ namespace DCFApixels.DragonECS if (itemIndex >= _items.Length) Array.Resize(ref _items, _items.Length << 1); } - this.IncrementEntityComponentCount(entityID, _componentID); + _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnAdd(entityID); } _listeners.InvokeOnGet(entityID); @@ -111,7 +113,7 @@ namespace DCFApixels.DragonECS _recycledItems[_recycledItemsCount++] = itemIndex; _mapping[entityID] = 0; _itemsCount--; - this.DecrementEntityComponentCount(entityID, _componentID); + _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) @@ -135,12 +137,12 @@ namespace DCFApixels.DragonECS #endregion #region Callbacks - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) + void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { _source = world; - _componentID = componentID; - - _maskBit = EcsMaskBit.FromID(componentID); + _mediator = mediator; + _componentTypeID = componentTypeID; + _maskBit = EcsMaskBit.FromID(componentTypeID); const int capacity = 512; diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 370dfca..4ccffad 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -49,7 +49,7 @@ namespace DCFApixels.DragonECS /// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool. public interface IEcsPoolImplementation : IEcsPool { - void OnInit(EcsWorld world, int componentID); + void OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID); void OnWorldResize(int newSize); void OnReleaseDelEntityBuffer(ReadOnlySpan buffer); void OnWorldDestroy(); @@ -71,17 +71,6 @@ namespace DCFApixels.DragonECS } public static class IEcsPoolImplementationExtensions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IncrementEntityComponentCount(this IEcsPoolImplementation self, int entityID, int componentID) - { - self.World.IncrementEntityComponentCount(entityID, componentID); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DecrementEntityComponentCount(this IEcsPoolImplementation self, int entityID, int componentID) - { - self.World.DecrementEntityComponentCount(entityID, componentID); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrDummy(this IEcsPool self) { @@ -116,7 +105,7 @@ namespace DCFApixels.DragonECS #endregion #region Callbacks - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { } + void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { } void IEcsPoolImplementation.OnWorldDestroy() { } void IEcsPoolImplementation.OnWorldResize(int newSize) { } void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { } diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index c5175b3..86529de 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -10,7 +10,8 @@ namespace DCFApixels.DragonECS where T : struct, IEcsTagComponent { private EcsWorld _source; - private int _componentID; + private int _componentTypeID; + private EcsMaskBit _maskBit; private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private int _count; @@ -18,8 +19,9 @@ namespace DCFApixels.DragonECS private List _listeners = new List(); private T _fakeComponent; + private EcsWorld.PoolsMediator _mediator; - + #region Constructors private static bool _isInvalidType; static EcsTagPool() { @@ -30,11 +32,12 @@ namespace DCFApixels.DragonECS if (_isInvalidType) throw new EcsFrameworkException($"{typeof(T).Name} type must not contain any data."); } + #endregion #region Properites public int Count => _count; int IEcsPool.Capacity => -1; - public int ComponentID => _componentID; + public int ComponentID => _componentTypeID; public Type ComponentType => typeof(T); public EcsWorld World => _source; #endregion @@ -47,7 +50,7 @@ namespace DCFApixels.DragonECS #endif _count++; _mapping[entityID] = true; - this.IncrementEntityComponentCount(entityID, _componentID); + _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnAdd(entityID); } public void TryAdd(int entityID) @@ -56,7 +59,7 @@ namespace DCFApixels.DragonECS { _count++; _mapping[entityID] = true; - this.IncrementEntityComponentCount(entityID, _componentID); + _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnAdd(entityID); } } @@ -72,7 +75,7 @@ namespace DCFApixels.DragonECS #endif _mapping[entityID] = false; _count--; - this.DecrementEntityComponentCount(entityID, _componentID); + _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) @@ -116,10 +119,12 @@ namespace DCFApixels.DragonECS #endregion #region Callbacks - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) + void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { _source = world; - _componentID = componentID; + _mediator = mediator; + _componentTypeID = componentTypeID; + _maskBit = EcsMaskBit.FromID(componentTypeID); _mapping = new bool[world.Capacity]; _count = 0; diff --git a/src/Pools/EcsTestPool.cs b/src/Pools/EcsTestPool.cs deleted file mode 100644 index 3bbc04f..0000000 --- a/src/Pools/EcsTestPool.cs +++ /dev/null @@ -1,332 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DCFApixels.DragonECS -{ - /// Pool for IEcsComponent components - public sealed class EcsTestPool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack - where T : struct, IEcsTestComponent - { - private EcsWorld _source; - private int _componentID; - - - public const int MIN_CAPACITY_BITS_OFFSET = 4; - public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; - private const int EMPTY = -1; - - private int[] _buckets = Array.Empty(); - private Entry[] _entries = Array.Empty(); - - private int _count; - - private int _freeList; - private int _freeCount; - - private int _modBitMask; - - - private IEcsComponentReset _componentResetHandler = EcsComponentResetHandler.instance; - private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; - - private List _listeners = new List(); - - #region Properites - public int Count => _count; - public int Capacity => _entries.Length; - public int ComponentID => _componentID; - public Type ComponentType => typeof(T); - public EcsWorld World => _source; - #endregion - - #region Methods - public void Copy(int fromEntityID, int toEntityID) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); -#endif - _componentCopyHandler.Copy(ref Get(fromEntityID), ref TryAddOrGet(toEntityID)); - } - public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); -#endif - _componentCopyHandler.Copy(ref Get(fromEntityID), ref toWorld.GetPool().TryAddOrGet(toEntityID)); - } - #endregion - - - #region Callbacks - void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) - { - _source = world; - _componentID = componentID; - - int minCapacity = 512; - minCapacity = NormalizeCapacity(minCapacity); - _buckets = new int[minCapacity]; - for (int i = 0; i < minCapacity; i++) - _buckets[i] = EMPTY; - _entries = new Entry[minCapacity]; - _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; - } - void IEcsPoolImplementation.OnWorldResize(int newSize) { } - void IEcsPoolImplementation.OnWorldDestroy() { } - void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) - { - foreach (var entityID in buffer) - TryDel(entityID); - } - #endregion - - #region Other - void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID) = (T)dataRaw; - object IEcsPool.GetRaw(int entityID) => Read(entityID); - void IEcsPool.SetRaw(int entityID, object dataRaw) => Get(entityID) = (T)dataRaw; - ref readonly T IEcsStructPool.Read(int entityID) => ref Read(entityID); - ref T IEcsStructPool.Get(int entityID) => ref Get(entityID); - #endregion - - #region Listeners - public void AddListener(IEcsPoolEventListener listener) - { - if (listener == null) { throw new ArgumentNullException("listener is null"); } - _listeners.Add(listener); - } - public void RemoveListener(IEcsPoolEventListener listener) - { - if (listener == null) { throw new ArgumentNullException("listener is null"); } - _listeners.Remove(listener); - } - #endregion - - #region IEnumerator - IntelliSense hack - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); - #endregion - - - #region Find/Insert/Remove - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int FindEntry(long x, long y) - { - return FindEntry(x << 32 | y); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int FindEntry(long key) - { - for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next) - if (_entries[i].key == key) return i; - return -1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(int entityID) => Has(entityID, entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool Has(long x, long y) => Has(x << 32 | y); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool Has(long key) { - for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next) - if (_entries[i].key == key) return true; - return false; - } - - public ref T Add(int entityID) => ref Add(entityID, entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ref T Add(long x, long y) => ref Add(x << 32 | y); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ref T Add(long key) - { - int entityID = unchecked((int)key); -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (Has(key: key)) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID); -#endif - - int index; - if (_freeCount > 0) - { - index = _freeList; - _freeList = _entries[index].next; - _freeCount--; - } - else - { - if (_count == _entries.Length) - Resize(); - index = _count++; - } - int targetBucket = unchecked((int)key & _modBitMask); - - ref var entry = ref _entries[index]; - entry.next = _buckets[targetBucket]; - entry.key = key; - entry.value = default; - _buckets[targetBucket] = index; - this.IncrementEntityComponentCount(entityID, _componentID); - _listeners.InvokeOnAddAndGet(entityID); - return ref entry.value; - } - public ref T TryAddOrGet(int entityID) - { - throw new NotImplementedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Get(int entityID) => ref Get(entityID, entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ref T Get(long x, long y) => ref Get(x << 32 | y); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ref T Get(long key) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(unchecked((int)key)); -#endif - _listeners.InvokeOnGet(unchecked((int)key)); - //return ref _entries[FindEntry(key)].value; - - for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next) - if (_entries[i].key == key) return ref _entries[i].value; // return i; - -#pragma warning disable CS0251 // - return ref _entries[-1].value; // . -#pragma warning restore CS0251 // - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(int entityID) => ref Read(entityID, entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(long x, long y) => ref Read(x << 32 | y); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(long key) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(unchecked((int)key)); -#endif - return ref _entries[FindEntry(key)].value; - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(int entityID) => Del(entityID, entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Del(long keyX, long keyY) => Del(keyX + (keyY << 32)); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Del(long key) - { - int entityID = unchecked((int)key); -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (!Has(key: key)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); -#endif - int bucket = unchecked((int)key & _modBitMask); - int last = -1; - for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next) - { - if (_entries[i].key == key) - { - if (last < 0) - { - _buckets[bucket] = _entries[i].next; - } - else - { - _entries[last].next = _entries[i].next; - } - _entries[i].next = _freeList; - _entries[i].key = -1; - //_entries[i].value = default; - _componentResetHandler.Reset(ref _entries[i].value); - _freeList = i; - _freeCount++; - this.DecrementEntityComponentCount(entityID, _componentID); - _listeners.InvokeOnDel(entityID); - return; - } - } - } - public void TryDel(int entityID) - { - if (Has(entityID)) Del(entityID); - } - #endregion - - #region Resize - private void Resize() - { - int newSize = _buckets.Length << 1; - _modBitMask = (newSize - 1) & 0x7FFFFFFF; - - Contract.Assert(newSize >= _entries.Length); - int[] newBuckets = new int[newSize]; - for (int i = 0; i < newBuckets.Length; i++) - newBuckets[i] = EMPTY; - - Entry[] newEntries = new Entry[newSize]; - Array.Copy(_entries, 0, newEntries, 0, _count); - for (int i = 0; i < _count; i++) - { - if (newEntries[i].key >= 0) - { - int bucket = unchecked((int)newEntries[i].key & _modBitMask); - newEntries[i].next = newBuckets[bucket]; - newBuckets[bucket] = i; - } - } - _buckets = newBuckets; - _entries = newEntries; - } - - private int NormalizeCapacity(int capacity) - { - int result = MIN_CAPACITY; - while (result < capacity) result <<= 1; - return result; - } - #endregion - - #region Utils - [StructLayout(LayoutKind.Sequential, Pack = 4)] - private struct Entry - { - public int next; // Index of next entry, -1 if last - public long key; - public T value; - } - #endregion - } - /// Standard component - public interface IEcsTestComponent { } - public static class EcsTestPoolExt - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsTestPool GetPool(this EcsWorld self) where TComponent : struct, IEcsTestComponent - { - return self.GetPool>(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsTestPool GetPoolUnchecked(this EcsWorld self) where TComponent : struct, IEcsTestComponent - { - return self.GetPoolUnchecked>(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsTestPool Include(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent - { - return self.Include>(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsTestPool Exclude(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent - { - return self.Exclude>(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsTestPool Optional(this EcsAspectBuilderBase self) where TComponent : struct, IEcsTestComponent - { - return self.Optional>(); - } - } -} From 91a750394e4f9287c24bd2b3c21c8b84b0bf8284 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:34:33 +0800 Subject: [PATCH 072/104] add explicit binding of process to runner --- src/Builtin/BaseProcesses.cs | 5 ++++ src/Builtin/InjectSystem.cs | 2 ++ src/EcsRunner.cs | 47 ++++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index 468047e..bdf7b57 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -1,22 +1,27 @@ #pragma warning disable CS0162 // Обнаружен недостижимый код +using DCFApixels.DragonECS.Internal; using DCFApixels.DragonECS.RunnersCore; using System; namespace DCFApixels.DragonECS { #region Interfaces + [EcsBindWithRunner(typeof(EcsPreInitProcessRunner))] public interface IEcsPreInitProcess : IEcsProcess { void PreInit(); } + [EcsBindWithRunner(typeof(EcsInitProcessRunner))] public interface IEcsInitProcess : IEcsProcess { void Init(); } + [EcsBindWithRunner(typeof(EcsRunProcessRunner))] public interface IEcsRunProcess : IEcsProcess { void Run(); } + [EcsBindWithRunner(typeof(EcsDestroyProcessRunner))] public interface IEcsDestroyProcess : IEcsProcess { void Destroy(); diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index 36d28d1..300a9c9 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -5,10 +5,12 @@ using System.Linq; namespace DCFApixels.DragonECS { + [EcsBindWithRunner(typeof(EcsPreInjectRunner))] public interface IEcsPreInject : IEcsProcess { void PreInject(object obj); } + [EcsBindWithRunner(typeof(EcsInjectRunner<>))] public interface IEcsInject : IEcsProcess { void Inject(T obj); diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 704d3cd..0fbbf6e 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -23,6 +23,31 @@ namespace DCFApixels.DragonECS } public EcsRunnerFilterAttribute(object filter) : this(null, filter) { } } +#if UNITY_2020_3_OR_NEWER + [UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve] +#endif + [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public sealed class EcsBindWithRunnerAttribute : Attribute + { + private static readonly Type baseType = typeof(EcsRunner<>); + public readonly Type runnerType; + public EcsBindWithRunnerAttribute(Type runnerType) + { + if (runnerType == null) + throw new ArgumentNullException(); + if (!Check(runnerType)) + throw new ArgumentException(); + this.runnerType = runnerType; + } + private bool Check(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == baseType) + return true; + if (type.BaseType != null) + return Check(type.BaseType); + return false; + } + } public interface IEcsProcess { } @@ -188,7 +213,25 @@ namespace DCFApixels.DragonECS #region Instantiate private static TInterface Instantiate(EcsPipeline source, TInterface[] targets, bool isHasFilter, object filter) { - if (_subclass == null) EcsRunnerActivator.InitFor(); + if(_subclass == null) + { + Type interfaceType = typeof(TInterface); + if (interfaceType.TryGetAttribute(out EcsBindWithRunnerAttribute atr)) + { + Type runnerType = atr.runnerType; + if (interfaceType.IsGenericType) + { + Type[] genericTypes = interfaceType.GetGenericArguments(); + runnerType = runnerType.MakeGenericType(genericTypes); + } + _subclass = runnerType; + } + else + { + EcsRunnerActivator.InitFor(); + } + } + var instance = (EcsRunner)Activator.CreateInstance(_subclass); return (TInterface)(IEcsProcess)instance.Set(source, targets, isHasFilter, filter); } @@ -201,7 +244,7 @@ namespace DCFApixels.DragonECS return Instantiate(source, FilterSystems(source.AllSystems, filter), true, filter); } #endregion - + private EcsPipeline _source; protected TInterface[] targets; private ReadOnlyCollection _targetsSealed; From f25036ae6e9413ad734f5756af686720bd936de0 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:35:07 +0800 Subject: [PATCH 073/104] simple fixes --- src/EcsPipeline.cs | 2 +- src/EcsWorld.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index e03478d..b77cccc 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -59,7 +59,7 @@ namespace DCFApixels.DragonECS { if (_isInit == true) { - EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been initialized"); + EcsDebug.PrintWarning($"This {nameof(EcsPipeline)} has already been initialized"); return; } diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index c031b56..41b5f29 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -273,7 +273,7 @@ namespace DCFApixels.DragonECS //public void CloneEntity(int fromEntityID, EcsWorld toWorld, int toEntityID) #endregion - #region Components Register + #region RegisterEntityComponent/UnregisterEntityComponent [MethodImpl(MethodImplOptions.AggressiveInlining)] private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { From 8aae08b21e2b71f3c09de1cd54982e37593fd096 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:38:00 +0800 Subject: [PATCH 074/104] rename binding attribute --- src/Builtin/BaseProcesses.cs | 8 ++++---- src/Builtin/InjectSystem.cs | 4 ++-- src/EcsRunner.cs | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index bdf7b57..1277d7b 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -6,22 +6,22 @@ using System; namespace DCFApixels.DragonECS { #region Interfaces - [EcsBindWithRunner(typeof(EcsPreInitProcessRunner))] + [BindWithEcsRunner(typeof(EcsPreInitProcessRunner))] public interface IEcsPreInitProcess : IEcsProcess { void PreInit(); } - [EcsBindWithRunner(typeof(EcsInitProcessRunner))] + [BindWithEcsRunner(typeof(EcsInitProcessRunner))] public interface IEcsInitProcess : IEcsProcess { void Init(); } - [EcsBindWithRunner(typeof(EcsRunProcessRunner))] + [BindWithEcsRunner(typeof(EcsRunProcessRunner))] public interface IEcsRunProcess : IEcsProcess { void Run(); } - [EcsBindWithRunner(typeof(EcsDestroyProcessRunner))] + [BindWithEcsRunner(typeof(EcsDestroyProcessRunner))] public interface IEcsDestroyProcess : IEcsProcess { void Destroy(); diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index 300a9c9..3eda586 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -5,12 +5,12 @@ using System.Linq; namespace DCFApixels.DragonECS { - [EcsBindWithRunner(typeof(EcsPreInjectRunner))] + [BindWithEcsRunner(typeof(EcsPreInjectRunner))] public interface IEcsPreInject : IEcsProcess { void PreInject(object obj); } - [EcsBindWithRunner(typeof(EcsInjectRunner<>))] + [BindWithEcsRunner(typeof(EcsInjectRunner<>))] public interface IEcsInject : IEcsProcess { void Inject(T obj); diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 0fbbf6e..5a1e351 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -27,11 +27,11 @@ namespace DCFApixels.DragonECS [UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve] #endif [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] - public sealed class EcsBindWithRunnerAttribute : Attribute + public sealed class BindWithEcsRunnerAttribute : Attribute { - private static readonly Type baseType = typeof(EcsRunner<>); + private static readonly Type _baseType = typeof(EcsRunner<>); public readonly Type runnerType; - public EcsBindWithRunnerAttribute(Type runnerType) + public BindWithEcsRunnerAttribute(Type runnerType) { if (runnerType == null) throw new ArgumentNullException(); @@ -41,7 +41,7 @@ namespace DCFApixels.DragonECS } private bool Check(Type type) { - if (type.IsGenericType && type.GetGenericTypeDefinition() == baseType) + if (type.IsGenericType && type.GetGenericTypeDefinition() == _baseType) return true; if (type.BaseType != null) return Check(type.BaseType); @@ -216,7 +216,7 @@ namespace DCFApixels.DragonECS if(_subclass == null) { Type interfaceType = typeof(TInterface); - if (interfaceType.TryGetAttribute(out EcsBindWithRunnerAttribute atr)) + if (interfaceType.TryGetAttribute(out BindWithEcsRunnerAttribute atr)) { Type runnerType = atr.runnerType; if (interfaceType.IsGenericType) From 212bfa0f2afdd51f83531b945be462ea4657fc48 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:45:32 +0800 Subject: [PATCH 075/104] add HasComponent to PoolsMediator --- src/EcsWorld.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 41b5f29..ee0c24c 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -2,6 +2,7 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; +using System.Diagnostics.Metrics; using System.Runtime.CompilerServices; using static DCFApixels.DragonECS.EcsWorld; @@ -273,7 +274,7 @@ namespace DCFApixels.DragonECS //public void CloneEntity(int fromEntityID, EcsWorld toWorld, int toEntityID) #endregion - #region RegisterEntityComponent/UnregisterEntityComponent + #region Pools mediation [MethodImpl(MethodImplOptions.AggressiveInlining)] private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { @@ -292,6 +293,11 @@ namespace DCFApixels.DragonECS if (count < 0) Throw.World_InvalidIncrementComponentsBalance(); #endif } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool HasEntityComponent(int entityID, EcsMaskBit maskBit) + { + return (_entitiesComponentMasks[entityID][maskBit.chankIndex] & maskBit.mask) != maskBit.mask; + } #endregion #endregion @@ -389,7 +395,7 @@ namespace DCFApixels.DragonECS public readonly struct PoolsMediator { private readonly EcsWorld _world; - public PoolsMediator(EcsWorld world) + internal PoolsMediator(EcsWorld world) { if (world == null) { @@ -397,21 +403,26 @@ namespace DCFApixels.DragonECS } if (world._poolsMediator._world != null) { - throw new MethodAccessException("Нельзя создавать вручную"); + throw new MethodAccessException(); } _world = world; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + public void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { _world.RegisterEntityComponent(entityID, componentTypeID, maskBit); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void UnregisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + public void UnregisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { _world.UnregisterEntityComponent(entityID, componentTypeID, maskBit); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasComponent(int entityID, EcsMaskBit maskBit) + { + return _world.HasEntityComponent(entityID, maskBit); + } } } From f50697e43b5d91b86bd8c684dc0af05ee09150c4 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 6 Dec 2023 21:15:35 +0800 Subject: [PATCH 076/104] remove implicit bingding pf processes to runner --- src/Builtin/InjectSystem.cs | 1 + src/EcsRunner.cs | 119 +++++++++++------------------------- 2 files changed, 36 insertions(+), 84 deletions(-) diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index 3eda586..50673e1 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -15,6 +15,7 @@ namespace DCFApixels.DragonECS { void Inject(T obj); } + [BindWithEcsRunner(typeof(EcsPreInitInjectProcessRunner))] public interface IEcsPreInitInjectProcess : IEcsProcess { void OnPreInitInjectionBefore(); diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 5a1e351..a9f54e1 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -35,16 +35,16 @@ namespace DCFApixels.DragonECS { if (runnerType == null) throw new ArgumentNullException(); - if (!Check(runnerType)) + if (!CheckSubclass(runnerType)) throw new ArgumentException(); this.runnerType = runnerType; } - private bool Check(Type type) + private bool CheckSubclass(Type type) { if (type.IsGenericType && type.GetGenericTypeDefinition() == _baseType) return true; if (type.BaseType != null) - return Check(type.BaseType); + return CheckSubclass(type.BaseType); return false; } } @@ -65,86 +65,6 @@ namespace DCFApixels.DragonECS void Destroy(); } - internal static class EcsRunnerActivator - { - private static Dictionary _runnerHandlerTypes; //interface base type/Runner handler type pairs; - static EcsRunnerActivator() - { - List delayedExceptions = new List(); - Type runnerBaseType = typeof(EcsRunner<>); - List runnerHandlerTypes = new List(); - - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - runnerHandlerTypes.AddRange(assembly.GetTypes() - .Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition())); - } - -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - for (int i = 0; i < runnerHandlerTypes.Count; i++) - { - var e = CheckRunnerValide(runnerHandlerTypes[i]); - if (e != null) - { - runnerHandlerTypes.RemoveAt(i--); - delayedExceptions.Add(e); - } - } -#endif - _runnerHandlerTypes = new Dictionary(); - foreach (var item in runnerHandlerTypes) - { - Type interfaceType = item.BaseType.GenericTypeArguments[0]; - _runnerHandlerTypes.Add(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, item); - } - - if (delayedExceptions.Count > 0) - { - throw new AggregateException(delayedExceptions); - } - } - - private static Exception CheckRunnerValide(Type type) //TODO доработать проверку валидности реалиазации ранера - { - Type baseType = type.BaseType; - Type baseTypeArgument = baseType.GenericTypeArguments[0]; - - if (type.ReflectedType != null) - { - return new EcsRunnerImplementationException($"{GetGenericTypeFullName(type, 1)}.ReflectedType must be Null, but equal to {GetGenericTypeFullName(type.ReflectedType, 1)}."); - } - if (!baseTypeArgument.IsInterface) - { - return new EcsRunnerImplementationException($"Argument T of class EcsRunner, can only be an inetrface. The {GetGenericTypeFullName(baseTypeArgument, 1)} type is not an interface."); - } - - var interfaces = type.GetInterfaces(); - - if (!interfaces.Any(o => o == baseTypeArgument)) - { - return new EcsRunnerImplementationException($"Runner {GetGenericTypeFullName(type, 1)} does not implement interface {GetGenericTypeFullName(baseTypeArgument, 1)}."); - } - - return null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void InitFor() where TInterface : IEcsProcess - { - Type interfaceType = typeof(TInterface); - - if (!_runnerHandlerTypes.TryGetValue(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, out Type runnerType)) - { - throw new EcsRunnerImplementationException($"There is no implementation of a runner for the {GetGenericTypeFullName(1)} interface."); - } - if (interfaceType.IsGenericType) - { - Type[] genericTypes = interfaceType.GetGenericArguments(); - runnerType = runnerType.MakeGenericType(genericTypes); - } - EcsRunner.Register(runnerType); - } - } #if UNITY_2020_3_OR_NEWER [UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve] #endif @@ -211,6 +131,36 @@ namespace DCFApixels.DragonECS #endregion #region Instantiate + private static void CheckRunnerValide(Type type) //TODO доработать проверку валидности реалиазации ранера + { + Type targetInterface = typeof(TInterface); + if (type.IsAbstract) + { + throw new Exception(); + } + + Type GetRunnerBaseType(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(EcsRunner<>)) + return type; + if (type.BaseType != null) + return GetRunnerBaseType(type.BaseType); + return null; + } + Type baseType = GetRunnerBaseType(type); + Type baseTypeArgument = baseType.GenericTypeArguments[0]; + + if (baseTypeArgument != targetInterface) + { + throw new Exception(); + } + + if (!type.GetInterfaces().Any(o => o == targetInterface)) + { + throw new EcsRunnerImplementationException($"Runner {GetGenericTypeFullName(type, 1)} does not implement interface {GetGenericTypeFullName(baseTypeArgument, 1)}."); + } + } + private static TInterface Instantiate(EcsPipeline source, TInterface[] targets, bool isHasFilter, object filter) { if(_subclass == null) @@ -224,11 +174,12 @@ namespace DCFApixels.DragonECS Type[] genericTypes = interfaceType.GetGenericArguments(); runnerType = runnerType.MakeGenericType(genericTypes); } + CheckRunnerValide(runnerType); _subclass = runnerType; } else { - EcsRunnerActivator.InitFor(); + throw new EcsFrameworkException("Процесс не связан с раннером, используйте атрибуут BindWithEcsRunner(Type runnerType)"); } } From 0d1b005306524f538f4d7ecfbcc3363fa85b99dc Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:06:17 +0800 Subject: [PATCH 077/104] rename NewEntity --- src/EcsWorld.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index ee0c24c..bd6ecef 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -146,7 +146,7 @@ namespace DCFApixels.DragonECS #endregion #region Entity - public int NewEmptyEntity() + public int NewEntity() { if(_freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount) ReleaseDelEntityBuffer(); @@ -163,9 +163,9 @@ namespace DCFApixels.DragonECS _entityListeners.InvokeOnNewEntity(entityID); return entityID; } - public entlong NewEmptyEntityLong() + public entlong NewEntityLong() { - int e = NewEmptyEntity(); + int e = NewEntity(); return GetEntityLong(e); } public void DelEntity(int entityID) @@ -252,13 +252,13 @@ namespace DCFApixels.DragonECS } public int CloneEntity(int fromEntityID) { - int newEntity = NewEmptyEntity(); + int newEntity = NewEntity(); CopyEntity(fromEntityID, newEntity); return newEntity; } public int CloneEntity(int fromEntityID, EcsWorld toWorld) { - int newEntity = NewEmptyEntity(); + int newEntity = NewEntity(); CopyEntity(fromEntityID, toWorld, newEntity); return newEntity; } From 503fc23aca8b79bbb8b517ab0177a379949dc2f3 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:07:19 +0800 Subject: [PATCH 078/104] fix --- src/EcsRunner.cs | 4 ++-- src/Utils/ReflectionExtensions.cs | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/Utils/ReflectionExtensions.cs diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index a9f54e1..107cdd0 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -1,11 +1,11 @@ -using DCFApixels.DragonECS.RunnersCore; +using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.RunnersCore; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using static DCFApixels.DragonECS.EcsDebugUtility; diff --git a/src/Utils/ReflectionExtensions.cs b/src/Utils/ReflectionExtensions.cs new file mode 100644 index 0000000..2af1c51 --- /dev/null +++ b/src/Utils/ReflectionExtensions.cs @@ -0,0 +1,14 @@ +using System; +using System.Reflection; + +namespace DCFApixels.DragonECS.Internal +{ + internal static class ReflectionExtensions + { + public static bool TryGetAttribute(this MemberInfo self, out T attribute) where T : Attribute + { + attribute = self.GetCustomAttribute(); + return attribute != null; + } + } +} From a912b4716d8f389972fcc11a70174eea7f31aa58 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:15:48 +0800 Subject: [PATCH 079/104] rework EcsGroup Api --- src/EcsGroup.cs | 119 ++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 44 deletions(-) diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index e7f354e..40dacf8 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -84,9 +84,6 @@ namespace DCFApixels.DragonECS #region Object public override string ToString() => _source != null ? _source.ToString() : "NULL"; - public override int GetHashCode() => _source.GetHashCode(); - public override bool Equals(object obj) => obj is EcsGroup group && group == this; - public bool Equals(EcsReadonlyGroup other) => _source == other._source; #endregion #region Internal @@ -95,13 +92,6 @@ namespace DCFApixels.DragonECS #endregion - #region operators - public static bool operator ==(EcsReadonlyGroup a, EcsReadonlyGroup b) => a.Equals(b); - public static bool operator ==(EcsReadonlyGroup a, EcsGroup b) => a.Equals(b); - public static bool operator !=(EcsReadonlyGroup a, EcsReadonlyGroup b) => !a.Equals(b); - public static bool operator !=(EcsReadonlyGroup a, EcsGroup b) => !a.Equals(b); - #endregion - #region DebuggerProxy internal class DebuggerProxy : EcsGroup.DebuggerProxy { @@ -111,7 +101,7 @@ namespace DCFApixels.DragonECS } [DebuggerTypeProxy(typeof(DebuggerProxy))] - public unsafe class EcsGroup : IDisposable, IEquatable, IEnumerable + public unsafe class EcsGroup : IDisposable, IEnumerable { private EcsWorld _source; private int[] _dense; @@ -120,7 +110,11 @@ namespace DCFApixels.DragonECS internal bool _isReleased = true; #region Properties - public EcsWorld World => _source; + public EcsWorld World + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source; + } public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -352,6 +346,7 @@ namespace DCFApixels.DragonECS else AddInternal(item); } + public void Inverse() { foreach (var item in _source.Entities) @@ -360,6 +355,64 @@ namespace DCFApixels.DragonECS else AddInternal(item); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SetEquals(EcsReadonlyGroup group) => SetEquals(group.GetGroupInternal()); + public bool SetEquals(EcsGroup group) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + if (group.Count != Count) + return false; + foreach (var item in group) + if (!Has(item)) + return false; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Overlaps(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); + public bool Overlaps(EcsGroup group) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + foreach (var item in group) + if (!Has(item)) + return true; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsSubsetOf(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); + public bool IsSubsetOf(EcsGroup group) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + if (group.Count < Count) + return false; + foreach (var item in this) + if (!group.Has(item)) + return false; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsSupersetOf(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); + public bool IsSupersetOf(EcsGroup group) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + if (group.Count > Count) + return false; + foreach (var item in group) + if (!Has(item)) + return false; + return true; + } #endregion #region Static Set operations @@ -508,24 +561,16 @@ namespace DCFApixels.DragonECS #region Object public override string ToString() => $"group{{{string.Join(", ", _dense.Take(_count))}}}"; - public override bool Equals(object obj) => obj is EcsGroup group && Equals(group); - public bool Equals(EcsReadonlyGroup other) => Equals(other.GetGroupInternal()); - public bool Equals(EcsGroup other) - { - if (other is null || other.Count != Count) - return false; - foreach (var e in other) - if (!Has(e)) - return false; - return true; - } - public override int GetHashCode() - { - int hash = 0; - foreach (var item in this) - hash ^= 1 << (item % 32); //реализация от балды, так как не нужен, но фишка в том что хеш не учитывает порядок сущьностей, что явлется правильным поведением. - return hash; - } + //public override int GetHashCode() + //{ + // unchecked + // { + // uint hash = 0; + // foreach (var item in this) + // hash ^= ((uint)item * 314159u); //реализация от балды, так как не нужен, но фишка в том что хеш не учитывает порядок сущьностей, что явлется правильным поведением. + // return (int)hash; + // } + //} #endregion #region OtherMethods @@ -533,20 +578,6 @@ namespace DCFApixels.DragonECS public int First() => this[0]; [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Last() => this[_count - 1]; - - #endregion - - #region operators - private static bool StaticEquals(EcsGroup a, EcsReadonlyGroup b) => StaticEquals(a, b.GetGroupInternal()); - private static bool StaticEquals(EcsGroup a, EcsGroup b) - { - if (a is null || b is null) return false; - return a.Equals(b); - } - public static bool operator ==(EcsGroup a, EcsGroup b) => StaticEquals(a, b); - public static bool operator ==(EcsGroup a, EcsReadonlyGroup b) => StaticEquals(a, b); - public static bool operator !=(EcsGroup a, EcsGroup b) => !StaticEquals(a, b); - public static bool operator !=(EcsGroup a, EcsReadonlyGroup b) => !StaticEquals(a, b); #endregion #region OnWorldResize From 4b0d188955a93a3b8b416304f24bf7744304298a Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 20 Dec 2023 23:16:57 +0800 Subject: [PATCH 080/104] update debug attributes rename DebugName attributes to MetaName attributes rework DebugHide to MetaTags --- src/Builtin/BaseProcesses.cs | 16 ++- src/Builtin/InjectSystem.cs | 18 ++- src/Builtin/Systems.cs | 9 +- src/Consts.cs | 2 + .../Attributes/DebugDescriptionAttribute.cs | 11 -- src/Debug/Attributes/DebugHideAttribute.cs | 7 -- src/Debug/Attributes/DebugNameAttribute.cs | 11 -- src/Debug/EcsDebugUtility.cs | 113 ++++++++++++------ .../{Attributes.meta => MetaAttributes.meta} | 0 .../MetaColorAttribute.cs} | 39 +++--- .../MetaColorAttribute.cs.meta} | 0 .../MetaDescriptionAttribute.cs | 11 ++ .../MetaDescriptionAttribute.cs.meta} | 0 .../MetaGroupAttribute.cs} | 18 +-- .../MetaGroupAttribute.cs.meta} | 0 src/Debug/MetaAttributes/MetaNameAttribute.cs | 11 ++ .../MetaNameAttribute.cs.meta} | 0 src/Debug/MetaAttributes/MetaTagsAttribute.cs | 24 ++++ .../MetaTagsAttribute.cs.meta} | 0 src/EcsRunner.cs | 5 +- src/EcsWorld.cs | 2 - src/Utils/ReflectionExtensions.cs | 14 --- 22 files changed, 183 insertions(+), 128 deletions(-) delete mode 100644 src/Debug/Attributes/DebugDescriptionAttribute.cs delete mode 100644 src/Debug/Attributes/DebugHideAttribute.cs delete mode 100644 src/Debug/Attributes/DebugNameAttribute.cs rename src/Debug/{Attributes.meta => MetaAttributes.meta} (100%) rename src/Debug/{Attributes/DebugColorAttribute.cs => MetaAttributes/MetaColorAttribute.cs} (74%) rename src/Debug/{Attributes/DebugColorAttribute.cs.meta => MetaAttributes/MetaColorAttribute.cs.meta} (100%) create mode 100644 src/Debug/MetaAttributes/MetaDescriptionAttribute.cs rename src/Debug/{Attributes/DebugDescriptionAttribute.cs.meta => MetaAttributes/MetaDescriptionAttribute.cs.meta} (100%) rename src/Debug/{Attributes/DebugGroupAttribute.cs => MetaAttributes/MetaGroupAttribute.cs} (51%) rename src/Debug/{Attributes/DebugGroupAttribute.cs.meta => MetaAttributes/MetaGroupAttribute.cs.meta} (100%) create mode 100644 src/Debug/MetaAttributes/MetaNameAttribute.cs rename src/Debug/{Attributes/DebugNameAttribute.cs.meta => MetaAttributes/MetaNameAttribute.cs.meta} (100%) create mode 100644 src/Debug/MetaAttributes/MetaTagsAttribute.cs rename src/Debug/{Attributes/DebugHideAttribute.cs.meta => MetaAttributes/MetaTagsAttribute.cs.meta} (100%) delete mode 100644 src/Utils/ReflectionExtensions.cs diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index 1277d7b..6176df0 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -6,21 +6,25 @@ using System; namespace DCFApixels.DragonECS { #region Interfaces + [MetaName(nameof(PreInit))] [BindWithEcsRunner(typeof(EcsPreInitProcessRunner))] public interface IEcsPreInitProcess : IEcsProcess { void PreInit(); } + [MetaName(nameof(Init))] [BindWithEcsRunner(typeof(EcsInitProcessRunner))] public interface IEcsInitProcess : IEcsProcess { void Init(); } + [MetaName(nameof(Run))] [BindWithEcsRunner(typeof(EcsRunProcessRunner))] public interface IEcsRunProcess : IEcsProcess { void Run(); } + [MetaName(nameof(Destroy))] [BindWithEcsRunner(typeof(EcsDestroyProcessRunner))] public interface IEcsDestroyProcess : IEcsProcess { @@ -31,7 +35,7 @@ namespace DCFApixels.DragonECS namespace Internal { - [DebugColor(DebugColor.Orange)] + [MetaColor(MetaColor.Orange)] public sealed class EcsPreInitProcessRunner : EcsRunner, IEcsPreInitProcess { #if DEBUG && !DISABLE_DEBUG @@ -79,7 +83,7 @@ namespace DCFApixels.DragonECS #endif } } - [DebugColor(DebugColor.Orange)] + [MetaColor(MetaColor.Orange)] public sealed class EcsInitProcessRunner : EcsRunner, IEcsInitProcess { #if DEBUG && !DISABLE_DEBUG @@ -127,7 +131,7 @@ namespace DCFApixels.DragonECS #endif } } - [DebugColor(DebugColor.Orange)] + [MetaColor(MetaColor.Orange)] public sealed class EcsRunProcessRunner : EcsRunner, IEcsRunProcess { #if DEBUG && !DISABLE_DEBUG @@ -175,7 +179,7 @@ namespace DCFApixels.DragonECS #endif } } - [DebugColor(DebugColor.Orange)] + [MetaColor(MetaColor.Orange)] public sealed class EcsDestroyProcessRunner : EcsRunner, IEcsDestroyProcess { #if DEBUG && !DISABLE_DEBUG @@ -185,11 +189,11 @@ namespace DCFApixels.DragonECS _markers = new EcsProfilerMarker[targets.Length]; for (int i = 0; i < targets.Length; i++) { - _markers[i] = new EcsProfilerMarker($"{targets[i].GetType().Name}.{nameof(Destroy)}"); + _markers[i] = new EcsProfilerMarker($"{targets[i].GetType().Name}.{nameof(IEcsDestroyProcess.Destroy)}"); } } #endif - public void Destroy() + void IEcsDestroyProcess.Destroy() { #if DEBUG && !DISABLE_DEBUG for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++) diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index 50673e1..cc5c391 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -5,16 +5,19 @@ using System.Linq; namespace DCFApixels.DragonECS { + [MetaName(nameof(PreInject))] [BindWithEcsRunner(typeof(EcsPreInjectRunner))] public interface IEcsPreInject : IEcsProcess { void PreInject(object obj); } + [MetaName(nameof(Inject))] [BindWithEcsRunner(typeof(EcsInjectRunner<>))] public interface IEcsInject : IEcsProcess { void Inject(T obj); } + [MetaName("PreInitInject")] [BindWithEcsRunner(typeof(EcsPreInitInjectProcessRunner))] public interface IEcsPreInitInjectProcess : IEcsProcess { @@ -47,7 +50,8 @@ namespace DCFApixels.DragonECS _injectSystems = null; } } - [DebugHide, DebugColor(DebugColor.Gray)] + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] public sealed class EcsPreInjectRunner : EcsRunner, IEcsPreInject { void IEcsPreInject.PreInject(object obj) @@ -55,7 +59,8 @@ namespace DCFApixels.DragonECS foreach (var item in targets) item.PreInject(obj); } } - [DebugHide, DebugColor(DebugColor.Gray)] + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] public sealed class EcsInjectRunner : EcsRunner>, IEcsInject { private EcsBaseTypeInjectRunner _baseTypeInjectRunner; @@ -91,7 +96,8 @@ namespace DCFApixels.DragonECS public override void Inject(object obj) => _runner.PreInject(obj); } - [DebugHide, DebugColor(DebugColor.Gray)] + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] public sealed class EcsPreInitInjectProcessRunner : EcsRunner, IEcsPreInitInjectProcess { public void OnPreInitInjectionAfter() @@ -103,8 +109,10 @@ namespace DCFApixels.DragonECS foreach (var item in targets) item.OnPreInitInjectionBefore(); } } - public class InjectSystemBase { } - [DebugHide, DebugColor(DebugColor.Gray)] + public abstract class InjectSystemBase { } + + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Gray)] public class InjectSystem : InjectSystemBase, IEcsInject, IEcsPreInitProcess, IEcsInject, IEcsPreInitInjectProcess { private EcsPipeline _pipeline; diff --git a/src/Builtin/Systems.cs b/src/Builtin/Systems.cs index d8f908f..7f1e2ca 100644 --- a/src/Builtin/Systems.cs +++ b/src/Builtin/Systems.cs @@ -5,13 +5,15 @@ namespace DCFApixels.DragonECS { namespace Internal { - [DebugHide, DebugColor(DebugColor.Black)] + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Black)] public class SystemsLayerMarkerSystem : IEcsProcess { public readonly string name; public SystemsLayerMarkerSystem(string name) => this.name = name; } - [DebugHide, DebugColor(DebugColor.Grey)] + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Grey)] public class EndFrameSystem : IEcsRunProcess, IEcsInject { private readonly List _worlds = new List(); @@ -25,7 +27,8 @@ namespace DCFApixels.DragonECS } } } - [DebugHide, DebugColor(DebugColor.Grey)] + [MetaTags(MetaTags.HIDDEN)] + [MetaColor(MetaColor.Grey)] public class DeleteOneFrameComponentSystem : IEcsRunProcess, IEcsInject where TComponent : struct, IEcsComponent { diff --git a/src/Consts.cs b/src/Consts.cs index 41bfb71..307b30a 100644 --- a/src/Consts.cs +++ b/src/Consts.cs @@ -14,5 +14,7 @@ public const string BASIC_LAYER = nameof(BASIC_LAYER); public const string END_LAYER = nameof(END_LAYER); public const string POST_END_LAYER = nameof(POST_END_LAYER); + + public const string META_HIDDEN_TAG = "HiddenInDebagging"; } } diff --git a/src/Debug/Attributes/DebugDescriptionAttribute.cs b/src/Debug/Attributes/DebugDescriptionAttribute.cs deleted file mode 100644 index 1a7aacd..0000000 --- a/src/Debug/Attributes/DebugDescriptionAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace DCFApixels.DragonECS -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] - public sealed class DebugDescriptionAttribute : Attribute - { - public readonly string description; - public DebugDescriptionAttribute(string description) => this.description = description; - } -} diff --git a/src/Debug/Attributes/DebugHideAttribute.cs b/src/Debug/Attributes/DebugHideAttribute.cs deleted file mode 100644 index 38d7419..0000000 --- a/src/Debug/Attributes/DebugHideAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System; - -namespace DCFApixels.DragonECS -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] - public sealed class DebugHideAttribute : Attribute { } -} diff --git a/src/Debug/Attributes/DebugNameAttribute.cs b/src/Debug/Attributes/DebugNameAttribute.cs deleted file mode 100644 index 01f6fda..0000000 --- a/src/Debug/Attributes/DebugNameAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace DCFApixels.DragonECS -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] - public sealed class DebugNameAttribute : Attribute - { - public readonly string name; - public DebugNameAttribute(string name) => this.name = name; - } -} diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 0b2a083..4777e50 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -67,7 +68,7 @@ namespace DCFApixels.DragonECS GetName(type: obj.GetType(), maxGenericDepth); } public static string GetName(int maxGenericDepth = 2) => GetName(typeof(T), maxGenericDepth); - public static string GetName(Type type, int maxGenericDepth = 2) => type.TryGetCustomAttribute(out DebugNameAttribute atr) ? atr.name : GetGenericTypeName(type, maxGenericDepth); + public static string GetName(Type type, int maxGenericDepth = 2) => type.TryGetCustomAttribute(out MetaNameAttribute atr) ? atr.name : GetGenericTypeName(type, maxGenericDepth); public static bool TryGetCustomName(object obj, out string name) { return obj is IEcsDebugMetaProvider intr ? @@ -77,7 +78,7 @@ namespace DCFApixels.DragonECS public static bool TryGetCustomName(out string name) => TryGetCustomName(type: typeof(T), out name); public static bool TryGetCustomName(Type type, out string name) { - if (type.TryGetCustomAttribute(out DebugNameAttribute atr)) + if (type.TryGetCustomAttribute(out MetaNameAttribute atr)) { name = atr.name; return true; @@ -88,29 +89,29 @@ namespace DCFApixels.DragonECS #endregion #region GetGroup - public static DebugGroup GetGroup(object obj) + public static MetaGroup GetGroup(object obj) { return obj is IEcsDebugMetaProvider intr ? GetGroup(intr.DebugMetaSource) : GetGroup(type: obj.GetType()); } - public static DebugGroup GetGroup() => GetGroup(typeof(T)); - public static DebugGroup GetGroup(Type type) => type.TryGetCustomAttribute(out DebugGroupAttribute atr) ? atr.GetData() : DebugGroup.Empty; - public static bool TryGetGroup(object obj, out DebugGroup group) + public static MetaGroup GetGroup() => GetGroup(typeof(T)); + public static MetaGroup GetGroup(Type type) => type.TryGetCustomAttribute(out MetaGroupAttribute atr) ? atr.GetData() : MetaGroup.Empty; + public static bool TryGetGroup(object obj, out MetaGroup group) { return obj is IEcsDebugMetaProvider intr ? TryGetGroup(intr.DebugMetaSource, out group) : TryGetGroup(type: obj.GetType(), out group); } - public static bool TryGetGroup(out DebugGroup text) => TryGetGroup(typeof(T), out text); - public static bool TryGetGroup(Type type, out DebugGroup group) + public static bool TryGetGroup(out MetaGroup text) => TryGetGroup(typeof(T), out text); + public static bool TryGetGroup(Type type, out MetaGroup group) { - if (type.TryGetCustomAttribute(out DebugGroupAttribute atr)) + if (type.TryGetCustomAttribute(out MetaGroupAttribute atr)) { group = atr.GetData(); return true; } - group = DebugGroup.Empty; + group = MetaGroup.Empty; return false; } #endregion @@ -123,7 +124,7 @@ namespace DCFApixels.DragonECS GetDescription(type: obj.GetType()); } public static string GetDescription() => GetDescription(typeof(T)); - public static string GetDescription(Type type) => type.TryGetCustomAttribute(out DebugDescriptionAttribute atr) ? atr.description : string.Empty; + public static string GetDescription(Type type) => type.TryGetCustomAttribute(out MetaDescriptionAttribute atr) ? atr.description : string.Empty; public static bool TryGetDescription(object obj, out string text) { return obj is IEcsDebugMetaProvider intr ? @@ -133,7 +134,7 @@ namespace DCFApixels.DragonECS public static bool TryGetDescription(out string text) => TryGetDescription(typeof(T), out text); public static bool TryGetDescription(Type type, out string text) { - if (type.TryGetCustomAttribute(out DebugDescriptionAttribute atr)) + if (type.TryGetCustomAttribute(out MetaDescriptionAttribute atr)) { text = atr.description; return true; @@ -149,7 +150,7 @@ namespace DCFApixels.DragonECS private class WordColor { public int wordsCount; - public DebugColor color; + public MetaColor color; } private class NameColor { @@ -162,7 +163,7 @@ namespace DCFApixels.DragonECS { color = new WordColor(); _words.Add(word, color); - color.color = new DebugColor((byte)random.Next(), (byte)random.Next(), (byte)random.Next()).UpContrastColor() / 2; + color.color = new MetaColor((byte)random.Next(), (byte)random.Next(), (byte)random.Next()).UpContrastColor() / 2; } color.wordsCount++; colors.Add(color); @@ -177,7 +178,7 @@ namespace DCFApixels.DragonECS } return result; } - public DebugColor CalcColor() + public MetaColor CalcColor() { float r = 0, g = 0, b = 0; int totalWordsCount = CalcTotalWordsColor(); @@ -189,11 +190,11 @@ namespace DCFApixels.DragonECS g += m * color.color.g; b += m * color.color.b; } - return new DebugColor((byte)r, (byte)g, (byte)b); + return new MetaColor((byte)r, (byte)g, (byte)b); } } private static Dictionary _names = new Dictionary(); - private static DebugColor CalcNameColorFor(Type type) + private static MetaColor CalcNameColorFor(Type type) { Type targetType = type.IsGenericType ? type.GetGenericTypeDefinition() : type; if (!_names.TryGetValue(targetType, out NameColor nameColor)) @@ -224,39 +225,73 @@ namespace DCFApixels.DragonECS return words; } - public static DebugColor GetColor(object obj) + public static MetaColor GetColor(object obj) { return obj is IEcsDebugMetaProvider intr ? GetColor(intr.DebugMetaSource) : GetColor(type: obj.GetType()); } - public static DebugColor GetColor() => GetColor(typeof(T)); - public static DebugColor GetColor(Type type) + public static MetaColor GetColor() => GetColor(typeof(T)); + public static MetaColor GetColor(Type type) { - var atr = type.GetCustomAttribute(); + var atr = type.GetCustomAttribute(); return atr != null ? atr.color #if DEBUG //optimization for release build : CalcNameColorFor(type); #else - : DebugColor.BlackColor; + : MetaColor.BlackColor; #endif } - public static bool TryGetColor(object obj, out DebugColor color) + public static bool TryGetColor(object obj, out MetaColor color) { return obj is IEcsDebugMetaProvider intr ? TryGetColor(intr.DebugMetaSource, out color) : TryGetColor(type: obj.GetType(), out color); } - public static bool TryGetColor(out DebugColor color) => TryGetColor(typeof(T), out color); - public static bool TryGetColor(Type type, out DebugColor color) + public static bool TryGetColor(out MetaColor color) => TryGetColor(typeof(T), out color); + public static bool TryGetColor(Type type, out MetaColor color) { - var atr = type.GetCustomAttribute(); + var atr = type.GetCustomAttribute(); if (atr != null) { color = atr.color; return true; } - color = DebugColor.BlackColor; + color = MetaColor.BlackColor; + return false; + } + #endregion + + #region GetTags + public static IReadOnlyCollection GetTags(object obj) + { + return obj is IEcsDebugMetaProvider intr ? + GetTags(intr.DebugMetaSource) : + GetTags(type: obj.GetType()); + } + public static IReadOnlyCollection GetTags() => GetTags(typeof(T)); + public static IReadOnlyCollection GetTags(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? atr.Tags : Array.Empty(); + } + + public static bool TryGetTags(object obj, out IReadOnlyCollection tags) + { + return obj is IEcsDebugMetaProvider intr ? + TryGetTags(intr.DebugMetaSource, out tags) : + TryGetTags(type: obj.GetType(), out tags); + } + public static bool TryGetTags(out IReadOnlyCollection tags) => TryGetTags(typeof(T), out tags); + public static bool TryGetTags(Type type, out IReadOnlyCollection tags) + { + var atr = type.GetCustomAttribute(); + if (atr != null) + { + tags = atr.Tags; + return true; + } + tags = Array.Empty(); return false; } #endregion @@ -269,7 +304,7 @@ namespace DCFApixels.DragonECS IsHidden(type: obj.GetType()); } public static bool IsHidden() => IsHidden(typeof(T)); - public static bool IsHidden(Type type) => type.TryGetCustomAttribute(out DebugHideAttribute _); + public static bool IsHidden(Type type) => type.TryGetCustomAttribute(out MetaTagsAttribute atr) && atr.Tags.Contains(MetaTags.HIDDEN); #endregion #region MetaSource @@ -284,22 +319,22 @@ namespace DCFApixels.DragonECS #endregion #region GenerateTypeDebugData - public static TypeDebugData GenerateTypeDebugData(object obj) + public static TypeMetaData GenerateTypeDebugData(object obj) { return obj is IEcsDebugMetaProvider intr ? GenerateTypeDebugData(intr.DebugMetaSource) : GenerateTypeDebugData(type: obj.GetType()); } - public static TypeDebugData GenerateTypeDebugData() => GenerateTypeDebugData(typeof(T)); - public static TypeDebugData GenerateTypeDebugData(Type type) + public static TypeMetaData GenerateTypeDebugData() => GenerateTypeDebugData(typeof(T)); + public static TypeMetaData GenerateTypeDebugData(Type type) { - return new TypeDebugData( + return new TypeMetaData( type, GetName(type), GetGroup(type), GetColor(type), GetDescription(type), - IsHidden(type)); + GetTags(type).ToArray()); } #endregion @@ -320,22 +355,22 @@ namespace DCFApixels.DragonECS } [Serializable] - public sealed class TypeDebugData + public sealed class TypeMetaData { public readonly Type type; public readonly string name; - public readonly DebugGroup group; - public readonly DebugColor color; + public readonly MetaGroup group; + public readonly MetaColor color; public readonly string description; - public readonly bool isHidden; - public TypeDebugData(Type type, string name, DebugGroup group, DebugColor color, string description, bool isHidden) + public readonly string[] tags; + public TypeMetaData(Type type, string name, MetaGroup group, MetaColor color, string description, string[] tags) { this.type = type; this.name = name; this.group = group; this.color = color; this.description = description; - this.isHidden = isHidden; + this.tags = tags; } } } diff --git a/src/Debug/Attributes.meta b/src/Debug/MetaAttributes.meta similarity index 100% rename from src/Debug/Attributes.meta rename to src/Debug/MetaAttributes.meta diff --git a/src/Debug/Attributes/DebugColorAttribute.cs b/src/Debug/MetaAttributes/MetaColorAttribute.cs similarity index 74% rename from src/Debug/Attributes/DebugColorAttribute.cs rename to src/Debug/MetaAttributes/MetaColorAttribute.cs index fda3aa5..ea0bd90 100644 --- a/src/Debug/Attributes/DebugColorAttribute.cs +++ b/src/Debug/MetaAttributes/MetaColorAttribute.cs @@ -3,20 +3,23 @@ using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false, AllowMultiple = false)] - public sealed class DebugColorAttribute : Attribute + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public sealed class MetaColorAttribute : Attribute { - public readonly DebugColor color; - public byte r => color.r; - public byte g => color.g; - public byte b => color.b; - public DebugColorAttribute(byte r, byte g, byte b) => color = new DebugColor(r, g, b, 255); - public DebugColorAttribute(int colorCode) => color = new DebugColor(colorCode, true); + public readonly MetaColor color; + public byte R => color.r; + public byte G => color.g; + public byte B => color.b; + public float FloatT => R / (float)byte.MaxValue; + public float FloatG => G / (float)byte.MaxValue; + public float FloatB => B / (float)byte.MaxValue; + public MetaColorAttribute(byte r, byte g, byte b) => color = new MetaColor(r, g, b, 255); + public MetaColorAttribute(int colorCode) => color = new MetaColor(colorCode, true); } [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)] - public readonly struct DebugColor + public readonly struct MetaColor { - public static readonly DebugColor BlackColor = new DebugColor(Black); + public static readonly MetaColor BlackColor = new MetaColor(Black); /// color code Red. RGB is (255, 0, 0) public const int Red = (255 << 24) | (000 << 16) | (000 << 8) | 255; /// color code Green. RGB is (0, 255, 0) @@ -66,37 +69,37 @@ namespace DCFApixels.DragonECS [FieldOffset(2)] public readonly byte g; [FieldOffset(1)] public readonly byte b; [FieldOffset(0)] public readonly byte a; - public DebugColor(byte r, byte g, byte b) : this() + public MetaColor(byte r, byte g, byte b) : this() { this.r = r; this.g = g; this.b = b; a = 255; } - public DebugColor(byte r, byte g, byte b, byte a) : this() + public MetaColor(byte r, byte g, byte b, byte a) : this() { this.r = r; this.g = g; this.b = b; this.a = a; } - public DebugColor(int colorCode) : this() => this.colorCode = colorCode; - public DebugColor(int colorCode, bool withoutAlpha) : this() => this.colorCode = withoutAlpha ? colorCode | 255 : colorCode; + public MetaColor(int colorCode) : this() => this.colorCode = colorCode; + public MetaColor(int colorCode, bool withoutAlpha) : this() => this.colorCode = withoutAlpha ? colorCode | 255 : colorCode; public (byte, byte, byte) ToTupleRGB() => (r, g, b); public (byte, byte, byte, byte) ToTupleRGBA() => (r, g, b, a); - public DebugColor UpContrastColor() + public MetaColor UpContrastColor() { byte minChannel = Math.Min(Math.Min(r, g), b); byte maxChannel = Math.Max(Math.Max(r, g), b); if (maxChannel == minChannel) return default; float factor = 255f / (maxChannel - minChannel); - return new DebugColor((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor)); + return new MetaColor((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor)); } - public static DebugColor operator /(DebugColor a, float b) + public static MetaColor operator /(MetaColor a, float b) { - return new DebugColor((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b)); + return new MetaColor((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b)); } } } \ No newline at end of file diff --git a/src/Debug/Attributes/DebugColorAttribute.cs.meta b/src/Debug/MetaAttributes/MetaColorAttribute.cs.meta similarity index 100% rename from src/Debug/Attributes/DebugColorAttribute.cs.meta rename to src/Debug/MetaAttributes/MetaColorAttribute.cs.meta diff --git a/src/Debug/MetaAttributes/MetaDescriptionAttribute.cs b/src/Debug/MetaAttributes/MetaDescriptionAttribute.cs new file mode 100644 index 0000000..38b4f16 --- /dev/null +++ b/src/Debug/MetaAttributes/MetaDescriptionAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace DCFApixels.DragonECS +{ + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public sealed class MetaDescriptionAttribute : Attribute + { + public readonly string description; + public MetaDescriptionAttribute(string description) => this.description = description; + } +} diff --git a/src/Debug/Attributes/DebugDescriptionAttribute.cs.meta b/src/Debug/MetaAttributes/MetaDescriptionAttribute.cs.meta similarity index 100% rename from src/Debug/Attributes/DebugDescriptionAttribute.cs.meta rename to src/Debug/MetaAttributes/MetaDescriptionAttribute.cs.meta diff --git a/src/Debug/Attributes/DebugGroupAttribute.cs b/src/Debug/MetaAttributes/MetaGroupAttribute.cs similarity index 51% rename from src/Debug/Attributes/DebugGroupAttribute.cs rename to src/Debug/MetaAttributes/MetaGroupAttribute.cs index e0c7567..455877e 100644 --- a/src/Debug/Attributes/DebugGroupAttribute.cs +++ b/src/Debug/MetaAttributes/MetaGroupAttribute.cs @@ -3,13 +3,13 @@ using System.Text.RegularExpressions; namespace DCFApixels.DragonECS { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] - public sealed class DebugGroupAttribute : Attribute + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public sealed class MetaGroupAttribute : Attribute { - public static readonly DebugGroupAttribute Empty = new DebugGroupAttribute(""); + public static readonly MetaGroupAttribute Empty = new MetaGroupAttribute(""); public readonly string name; public readonly string rootCategory; - public DebugGroupAttribute(string name) + public MetaGroupAttribute(string name) { name = Regex.Replace(name, @"^[/|\\]+|[/|\\]+$", ""); rootCategory = Regex.Match(name, @"^(.*?)[/\\]").Groups[1].Value; @@ -19,15 +19,15 @@ namespace DCFApixels.DragonECS { return Regex.Split(name, @"[/|\\]"); } - public DebugGroup GetData() => new DebugGroup(this); + public MetaGroup GetData() => new MetaGroup(this); } - public readonly struct DebugGroup + public readonly struct MetaGroup { - public static readonly DebugGroup Empty = new DebugGroup(DebugGroupAttribute.Empty); - private readonly DebugGroupAttribute _source; + public static readonly MetaGroup Empty = new MetaGroup(MetaGroupAttribute.Empty); + private readonly MetaGroupAttribute _source; public string Name => _source.name; public string RootCategory => _source.rootCategory; - public DebugGroup(DebugGroupAttribute source) => _source = source; + public MetaGroup(MetaGroupAttribute source) => _source = source; public string[] SplitCategories() => _source.SplitCategories(); } } diff --git a/src/Debug/Attributes/DebugGroupAttribute.cs.meta b/src/Debug/MetaAttributes/MetaGroupAttribute.cs.meta similarity index 100% rename from src/Debug/Attributes/DebugGroupAttribute.cs.meta rename to src/Debug/MetaAttributes/MetaGroupAttribute.cs.meta diff --git a/src/Debug/MetaAttributes/MetaNameAttribute.cs b/src/Debug/MetaAttributes/MetaNameAttribute.cs new file mode 100644 index 0000000..c4c8acd --- /dev/null +++ b/src/Debug/MetaAttributes/MetaNameAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace DCFApixels.DragonECS +{ + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public sealed class MetaNameAttribute : Attribute + { + public readonly string name; + public MetaNameAttribute(string name) => this.name = name; + } +} diff --git a/src/Debug/Attributes/DebugNameAttribute.cs.meta b/src/Debug/MetaAttributes/MetaNameAttribute.cs.meta similarity index 100% rename from src/Debug/Attributes/DebugNameAttribute.cs.meta rename to src/Debug/MetaAttributes/MetaNameAttribute.cs.meta diff --git a/src/Debug/MetaAttributes/MetaTagsAttribute.cs b/src/Debug/MetaAttributes/MetaTagsAttribute.cs new file mode 100644 index 0000000..1dbf92a --- /dev/null +++ b/src/Debug/MetaAttributes/MetaTagsAttribute.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DCFApixels.DragonECS +{ + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] + public sealed class MetaTagsAttribute : Attribute + { + private readonly string[] _tags; + public IReadOnlyCollection Tags => _tags; + + [Obsolete("With empty parameters, this attribute makes no sense.", true)] + public MetaTagsAttribute() { } + public MetaTagsAttribute(params string[] tags) + { + _tags = tags.Where(o => !string.IsNullOrEmpty(o)).ToArray(); + } + } + public readonly ref struct MetaTags + { + public const string HIDDEN = EcsConsts.META_HIDDEN_TAG; + } +} diff --git a/src/Debug/Attributes/DebugHideAttribute.cs.meta b/src/Debug/MetaAttributes/MetaTagsAttribute.cs.meta similarity index 100% rename from src/Debug/Attributes/DebugHideAttribute.cs.meta rename to src/Debug/MetaAttributes/MetaTagsAttribute.cs.meta diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 107cdd0..89008d6 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -1,5 +1,4 @@ -using DCFApixels.DragonECS.Internal; -using DCFApixels.DragonECS.RunnersCore; +using DCFApixels.DragonECS.RunnersCore; using System; using System.Collections; using System.Collections.Generic; @@ -166,7 +165,7 @@ namespace DCFApixels.DragonECS if(_subclass == null) { Type interfaceType = typeof(TInterface); - if (interfaceType.TryGetAttribute(out BindWithEcsRunnerAttribute atr)) + if (interfaceType.TryGetCustomAttribute(out BindWithEcsRunnerAttribute atr)) { Type runnerType = atr.runnerType; if (interfaceType.IsGenericType) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index bd6ecef..c15d3ff 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -2,9 +2,7 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; -using System.Diagnostics.Metrics; using System.Runtime.CompilerServices; -using static DCFApixels.DragonECS.EcsWorld; namespace DCFApixels.DragonECS { diff --git a/src/Utils/ReflectionExtensions.cs b/src/Utils/ReflectionExtensions.cs deleted file mode 100644 index 2af1c51..0000000 --- a/src/Utils/ReflectionExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Reflection; - -namespace DCFApixels.DragonECS.Internal -{ - internal static class ReflectionExtensions - { - public static bool TryGetAttribute(this MemberInfo self, out T attribute) where T : Attribute - { - attribute = self.GetCustomAttribute(); - return attribute != null; - } - } -} From e7c623daf81488f863653905dfbc83199fe922f4 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 20 Dec 2023 23:21:10 +0800 Subject: [PATCH 081/104] simple refactoring --- src/Debug/EcsDebugUtility.cs | 4 ++-- src/EcsAspect.cs | 4 ++-- src/EcsRunner.cs | 8 ++++---- src/EcsWorld.cache.cs | 5 +---- src/EcsWorld.cs | 6 +++--- src/Pools/EcsHybridPool.cs | 16 ++++++++-------- src/Pools/EcsPool.cs | 1 - 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 4777e50..7e41fad 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -71,8 +71,8 @@ namespace DCFApixels.DragonECS public static string GetName(Type type, int maxGenericDepth = 2) => type.TryGetCustomAttribute(out MetaNameAttribute atr) ? atr.name : GetGenericTypeName(type, maxGenericDepth); public static bool TryGetCustomName(object obj, out string name) { - return obj is IEcsDebugMetaProvider intr ? - TryGetCustomName(intr.DebugMetaSource, out name) : + return obj is IEcsDebugMetaProvider intr ? + TryGetCustomName(intr.DebugMetaSource, out name) : TryGetCustomName(type: obj.GetType(), out name); } public static bool TryGetCustomName(out string name) => TryGetCustomName(type: typeof(T), out name); diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index ebfbaa3..bea4d3e 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -143,7 +143,7 @@ namespace DCFApixels.DragonECS foreach (var id in maskInc) { var bit = EcsMaskBit.FromID(id); - if(!r.TryAdd(bit.chankIndex, bit.mask)) + if (!r.TryAdd(bit.chankIndex, bit.mask)) r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; } EcsMaskBit[] incMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(); @@ -155,7 +155,7 @@ namespace DCFApixels.DragonECS r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; } EcsMaskBit[] excMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(); - + var inc = maskInc.ToArray(); Array.Sort(inc); var exc = maskExc.ToArray(); diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 89008d6..b0d4470 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -162,7 +162,7 @@ namespace DCFApixels.DragonECS private static TInterface Instantiate(EcsPipeline source, TInterface[] targets, bool isHasFilter, object filter) { - if(_subclass == null) + if (_subclass == null) { Type interfaceType = typeof(TInterface); if (interfaceType.TryGetCustomAttribute(out BindWithEcsRunnerAttribute atr)) @@ -181,7 +181,7 @@ namespace DCFApixels.DragonECS throw new EcsFrameworkException("Процесс не связан с раннером, используйте атрибуут BindWithEcsRunner(Type runnerType)"); } } - + var instance = (EcsRunner)Activator.CreateInstance(_subclass); return (TInterface)(IEcsProcess)instance.Set(source, targets, isHasFilter, filter); } @@ -194,7 +194,7 @@ namespace DCFApixels.DragonECS return Instantiate(source, FilterSystems(source.AllSystems, filter), true, filter); } #endregion - + private EcsPipeline _source; protected TInterface[] targets; private ReadOnlyCollection _targetsSealed; @@ -333,7 +333,7 @@ namespace DCFApixels.DragonECS public static IEnumerable GetEcsProcessInterfaces(this Type self) { - return self.GetInterfaces().Where(o=> o.IsEcsProcessInterface()); + return self.GetInterfaces().Where(o => o.IsEcsProcessInterface()); } #endregion diff --git a/src/EcsWorld.cache.cs b/src/EcsWorld.cache.cs index 487bc86..8729c4b 100644 --- a/src/EcsWorld.cache.cs +++ b/src/EcsWorld.cache.cs @@ -1,7 +1,4 @@ -using DCFApixels.DragonECS.Utils; -using System; - -namespace DCFApixels.DragonECS +namespace DCFApixels.DragonECS { public abstract partial class EcsWorld { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index c15d3ff..021ccbf 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -146,7 +146,7 @@ namespace DCFApixels.DragonECS #region Entity public int NewEntity() { - if(_freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount) + if (_freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount) ReleaseDelEntityBuffer(); int entityID = _entityDispenser.GetFree(); @@ -236,7 +236,7 @@ namespace DCFApixels.DragonECS { foreach (var pool in _pools) { - if (pool.Has(fromEntityID)) + if (pool.Has(fromEntityID)) pool.Copy(fromEntityID, toEntityID); } } @@ -285,7 +285,7 @@ namespace DCFApixels.DragonECS var count = --_componentCounts[entityID]; _entitiesComponentMasks[entityID][maskBit.chankIndex] &= ~maskBit.mask; - if (count == 0 && _allEntites.Has(entityID)) + if (count == 0 && _allEntites.Has(entityID)) DelEntity(entityID); #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (count < 0) Throw.World_InvalidIncrementComponentsBalance(); diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 4f9d93f..e48ecda 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -70,7 +70,7 @@ namespace DCFApixels.DragonECS } _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _listeners.InvokeOnAdd(entityID); - if(isMain) + if (isMain) component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; _entities[itemIndex] = entityID; @@ -84,7 +84,7 @@ namespace DCFApixels.DragonECS } public void Set(int entityID, T component) { - if(Has(entityID)) + if (Has(entityID)) Del(entityID); Add(entityID, component); } @@ -121,7 +121,7 @@ namespace DCFApixels.DragonECS #endif ref int itemIndex = ref _mapping[entityID]; T component = _items[itemIndex]; - if(isMain) + if (isMain) component.OnDelFromPool(_source.GetEntityLong(entityID)); if (_recycledItemsCount >= _recycledItems.Length) Array.Resize(ref _recycledItems, _recycledItems.Length << 1); @@ -296,7 +296,7 @@ namespace DCFApixels.DragonECS private Dictionary _hybridMapping = new Dictionary(); internal HybridMapping GetHybridMapping(Type type) { - if(!_hybridMapping.TryGetValue(type, out HybridMapping mapping)) + if (!_hybridMapping.TryGetValue(type, out HybridMapping mapping)) { mapping = new HybridMapping(this, type); _hybridMapping.Add(type, mapping); @@ -314,7 +314,7 @@ namespace DCFApixels.DragonECS private IEcsHybridPoolInternal _targetTypePool; private List _relatedPools; - private static Type hybridPoolType = typeof(EcsHybridPool<>); + 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(); @@ -346,17 +346,17 @@ namespace DCFApixels.DragonECS _source = source; _type = type; _relatedPools = new List(); - _sourceForReflection = new object[]{ source }; + _sourceForReflection = new object[] { source }; _targetTypePool = CreateHybridPool(type); foreach (var item in type.GetInterfaces()) { - if(IsEcsHybridComponentType(item)) + if (IsEcsHybridComponentType(item)) { _relatedPools.Add(CreateHybridPool(item)); } } Type baseType = type.BaseType; - while(baseType != typeof(object) && IsEcsHybridComponentType(baseType)) + while (baseType != typeof(object) && IsEcsHybridComponentType(baseType)) { _relatedPools.Add(CreateHybridPool(baseType)); baseType = baseType.BaseType; diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 73fabc8..315422b 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Reflection; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS From 3cfdce40244cc479d87d315dded2ef0cba7b3988 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Wed, 20 Dec 2023 23:31:04 +0800 Subject: [PATCH 082/104] rename IEcsDebugMetaProvider to IEcsMetaProvider --- src/Debug/EcsDebugUtility.cs | 52 +++++++++---------- src/Debug/Interfaces/IEcsDebugMetaProvider.cs | 7 --- src/Debug/Interfaces/IEcsMetaProvider.cs | 7 +++ 3 files changed, 33 insertions(+), 33 deletions(-) delete mode 100644 src/Debug/Interfaces/IEcsDebugMetaProvider.cs create mode 100644 src/Debug/Interfaces/IEcsMetaProvider.cs diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index 7e41fad..d2019a2 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -63,16 +63,16 @@ namespace DCFApixels.DragonECS #region GetName public static string GetName(object obj, int maxGenericDepth = 2) { - return obj is IEcsDebugMetaProvider intr ? - GetName(intr.DebugMetaSource, maxGenericDepth) : + return obj is IEcsMetaProvider intr ? + GetName(intr.MetaSource, maxGenericDepth) : GetName(type: obj.GetType(), maxGenericDepth); } public static string GetName(int maxGenericDepth = 2) => GetName(typeof(T), maxGenericDepth); public static string GetName(Type type, int maxGenericDepth = 2) => type.TryGetCustomAttribute(out MetaNameAttribute atr) ? atr.name : GetGenericTypeName(type, maxGenericDepth); public static bool TryGetCustomName(object obj, out string name) { - return obj is IEcsDebugMetaProvider intr ? - TryGetCustomName(intr.DebugMetaSource, out name) : + return obj is IEcsMetaProvider intr ? + TryGetCustomName(intr.MetaSource, out name) : TryGetCustomName(type: obj.GetType(), out name); } public static bool TryGetCustomName(out string name) => TryGetCustomName(type: typeof(T), out name); @@ -91,16 +91,16 @@ namespace DCFApixels.DragonECS #region GetGroup public static MetaGroup GetGroup(object obj) { - return obj is IEcsDebugMetaProvider intr ? - GetGroup(intr.DebugMetaSource) : + return obj is IEcsMetaProvider intr ? + GetGroup(intr.MetaSource) : GetGroup(type: obj.GetType()); } public static MetaGroup GetGroup() => GetGroup(typeof(T)); public static MetaGroup GetGroup(Type type) => type.TryGetCustomAttribute(out MetaGroupAttribute atr) ? atr.GetData() : MetaGroup.Empty; public static bool TryGetGroup(object obj, out MetaGroup group) { - return obj is IEcsDebugMetaProvider intr ? - TryGetGroup(intr.DebugMetaSource, out group) : + return obj is IEcsMetaProvider intr ? + TryGetGroup(intr.MetaSource, out group) : TryGetGroup(type: obj.GetType(), out group); } public static bool TryGetGroup(out MetaGroup text) => TryGetGroup(typeof(T), out text); @@ -119,16 +119,16 @@ namespace DCFApixels.DragonECS #region GetDescription public static string GetDescription(object obj) { - return obj is IEcsDebugMetaProvider intr ? - GetDescription(intr.DebugMetaSource) : + return obj is IEcsMetaProvider intr ? + GetDescription(intr.MetaSource) : GetDescription(type: obj.GetType()); } public static string GetDescription() => GetDescription(typeof(T)); public static string GetDescription(Type type) => type.TryGetCustomAttribute(out MetaDescriptionAttribute atr) ? atr.description : string.Empty; public static bool TryGetDescription(object obj, out string text) { - return obj is IEcsDebugMetaProvider intr ? - TryGetDescription(intr.DebugMetaSource, out text) : + return obj is IEcsMetaProvider intr ? + TryGetDescription(intr.MetaSource, out text) : TryGetDescription(type: obj.GetType(), out text); } public static bool TryGetDescription(out string text) => TryGetDescription(typeof(T), out text); @@ -227,8 +227,8 @@ namespace DCFApixels.DragonECS public static MetaColor GetColor(object obj) { - return obj is IEcsDebugMetaProvider intr ? - GetColor(intr.DebugMetaSource) : + return obj is IEcsMetaProvider intr ? + GetColor(intr.MetaSource) : GetColor(type: obj.GetType()); } public static MetaColor GetColor() => GetColor(typeof(T)); @@ -244,8 +244,8 @@ namespace DCFApixels.DragonECS } public static bool TryGetColor(object obj, out MetaColor color) { - return obj is IEcsDebugMetaProvider intr ? - TryGetColor(intr.DebugMetaSource, out color) : + return obj is IEcsMetaProvider intr ? + TryGetColor(intr.MetaSource, out color) : TryGetColor(type: obj.GetType(), out color); } public static bool TryGetColor(out MetaColor color) => TryGetColor(typeof(T), out color); @@ -265,8 +265,8 @@ namespace DCFApixels.DragonECS #region GetTags public static IReadOnlyCollection GetTags(object obj) { - return obj is IEcsDebugMetaProvider intr ? - GetTags(intr.DebugMetaSource) : + return obj is IEcsMetaProvider intr ? + GetTags(intr.MetaSource) : GetTags(type: obj.GetType()); } public static IReadOnlyCollection GetTags() => GetTags(typeof(T)); @@ -278,8 +278,8 @@ namespace DCFApixels.DragonECS public static bool TryGetTags(object obj, out IReadOnlyCollection tags) { - return obj is IEcsDebugMetaProvider intr ? - TryGetTags(intr.DebugMetaSource, out tags) : + return obj is IEcsMetaProvider intr ? + TryGetTags(intr.MetaSource, out tags) : TryGetTags(type: obj.GetType(), out tags); } public static bool TryGetTags(out IReadOnlyCollection tags) => TryGetTags(typeof(T), out tags); @@ -299,8 +299,8 @@ namespace DCFApixels.DragonECS #region IsHidden public static bool IsHidden(object obj) { - return obj is IEcsDebugMetaProvider intr ? - IsHidden(intr.DebugMetaSource) : + return obj is IEcsMetaProvider intr ? + IsHidden(intr.MetaSource) : IsHidden(type: obj.GetType()); } public static bool IsHidden() => IsHidden(typeof(T)); @@ -310,19 +310,19 @@ namespace DCFApixels.DragonECS #region MetaSource public static bool IsMetaSourceProvided(object obj) { - return obj is IEcsDebugMetaProvider; + return obj is IEcsMetaProvider; } public static object GetMetaSource(object obj) { - return obj is IEcsDebugMetaProvider intr ? intr.DebugMetaSource : obj; + return obj is IEcsMetaProvider intr ? intr.MetaSource : obj; } #endregion #region GenerateTypeDebugData public static TypeMetaData GenerateTypeDebugData(object obj) { - return obj is IEcsDebugMetaProvider intr ? - GenerateTypeDebugData(intr.DebugMetaSource) : + return obj is IEcsMetaProvider intr ? + GenerateTypeDebugData(intr.MetaSource) : GenerateTypeDebugData(type: obj.GetType()); } public static TypeMetaData GenerateTypeDebugData() => GenerateTypeDebugData(typeof(T)); diff --git a/src/Debug/Interfaces/IEcsDebugMetaProvider.cs b/src/Debug/Interfaces/IEcsDebugMetaProvider.cs deleted file mode 100644 index 706c8b9..0000000 --- a/src/Debug/Interfaces/IEcsDebugMetaProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DCFApixels.DragonECS -{ - public interface IEcsDebugMetaProvider - { - object DebugMetaSource { get; } - } -} diff --git a/src/Debug/Interfaces/IEcsMetaProvider.cs b/src/Debug/Interfaces/IEcsMetaProvider.cs new file mode 100644 index 0000000..a7bc11a --- /dev/null +++ b/src/Debug/Interfaces/IEcsMetaProvider.cs @@ -0,0 +1,7 @@ +namespace DCFApixels.DragonECS +{ + public interface IEcsMetaProvider + { + object MetaSource { get; } + } +} From 6fcd27492ce67292cc804c64fecfca1f05f8a408 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:31:52 +0800 Subject: [PATCH 083/104] update Readme --- README-RU.md | 88 +++++++++++++++++++++++++++++----------------------- README.md | 11 +++++-- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/README-RU.md b/README-RU.md index 1df1023..1a468f3 100644 --- a/README-RU.md +++ b/README-RU.md @@ -13,43 +13,46 @@ | Languages: | [Русский](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md) | [English(WIP)](https://github.com/DCFApixels/DragonECS) | | :--- | :--- | :--- | -Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. -> **Warning**: Проект в стадии разработки. API может меняться. +Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. Вднохновлен [LeoEcs](https://github.com/Leopotam/ecslite). +> [!WARNING] Проект в стадии разработки. API может меняться. > Readme еще не завершен ## Оглавление -* [Установка](#Установка) - * [Unity-модуль](#Unity-модуль) - * [В виде иходников](#В-виде-иходников) - * [Версионирование](#Версионирование) -* [Основные концепции](#Основные-концепции) - * [Entity](#Entity) - * [Component](#Component) - * [System](#System) -* [Концепции фреймворка](#Концепции-фреймворка) - * [Пайплайн](#Пайплайн) - * [Построение](#Построение) - * [Внедрение зависимостей](#Внедрение-зависимостей) - * [Модули](#Модули) - * [Слои](#Слои) - * [Процессы](#Процессы) - * [Мир](#Мир) - * [Пул](#Пул) - * [Группа](#Группа) - * [Аспект](#Аспект) - * [Запрос](#Запрос) - * [Корень ECS](#Корень-ECS) - * [Пример для Unity](#Пример-для-Unity) - * [Общий пример](#Общий-пример) - * [Гибридность](#Гибридность) -* [Debug](#Debug) - * [Атрибуты](#Атрибуты) - * [EcsDebug](#EcsDebug) - * [Профилирование](#Профилирование) -* [Расширения](#Расширения) -* [FAQ](#FAQ) +- [DragonECS - C# Entity Component System Framework](#dragonecs---c-entity-component-system-framework) + - [Оглавление](#оглавление) +- [Установка](#установка) + - [Версионирование](#версионирование) +- [Основные концепции](#основные-концепции) + - [Entity](#entity) + - [Component](#component) + - [System](#system) +- [Концепции фреймворка](#концепции-фреймворка) + - [Пайплайн](#пайплайн) + - [Построение](#построение) + - [Внедрение зависимостей](#внедрение-зависимостей) + - [Модули](#модули) + - [Слои](#слои) + - [Процессы](#процессы) + - [Мир](#мир) + - [Компоненты мира](#компоненты-мира) + - [Пул](#пул) + - [Аспект](#аспект) + - [Запросы](#запросы) + - [Группа](#группа) + - [Корень ECS](#корень-ecs) + - [Пример для Unity](#пример-для-unity) + - [Общий пример](#общий-пример) + - [Гибридность](#гибридность) +- [Debug](#debug) + - [Атрибуты](#атрибуты) + - [EcsDebug](#ecsdebug) + - [Профилирование](#профилирование) +- [Расширения](#расширения) +- [FAQ](#faq) + - ['ReadOnlySpan\<\>' could not be found](#readonlyspan-could-not-be-found) +- [Обратная связь](#обратная-связь) -

+
# Установка * ### Unity-модуль @@ -63,7 +66,7 @@ https://github.com/DCFApixels/DragonECS.git ### Версионирование В DragonECS применяется следующая семантика версионирования: [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md) -

+
# Основные концепции ## Entity @@ -140,6 +143,9 @@ class SomeSystem : IEcsPreInitProcess, IEcsInitProcess, IEcsRunProcess, IEcsDest } ``` > Для реализации дополнительных процессов перейдите к разделу [Процессы](#Процессы). + +
+ # Концепции фреймворка ## Пайплайн Является контейнером и движком систем, определяя поочередность их вызова, предоставляющий механизм для сообщений между системами и механизм внедрения зависимостей. Реализован в виде класса `EcsPipeline`. @@ -563,7 +569,7 @@ public class EcsRoot } ``` ## Гибридность -Для смешивания архитектурных подходов классического OOP и ECS используется специальный пул `EcsHybridPool`. Принцип работы этого пула несколько отличается от других, он упрощает поддержу наследования и полиморфизма. +Для смешивания архитектурных подходов классического OOP и ECS используется специальный пул `EcsHybridPool`. Принцип работы этого пула несколько отличается от других и он упрощает поддержу наследования и полиморфизма.
Как это работает? @@ -645,7 +651,7 @@ bool isCamera = _world.GetPool().Has(entity); // ... ``` -

+
# Debug Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. @@ -708,7 +714,7 @@ using (marker.Auto()) } ``` -

+
# Расширения * [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections) @@ -717,7 +723,7 @@ using (marker.Auto()) * Интеграция с движком Unity (Work in progress) -

+
# FAQ ## 'ReadOnlySpan<>' could not be found @@ -727,5 +733,9 @@ The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing ``` Чтобы починить добавте директиву `ENABLE_DUMMY_SPAN` в `Project Settings/Player/Other Settings/Scripting Define Symbols`. -


+
+# Обратная связь +Discord для дискуссий [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf) + +


\ No newline at end of file diff --git a/README.md b/README.md index bc81d31..3f7a492 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,17 @@

Version License - +Discord

# DragonECS - C# Entity Component System Framework | Languages: | [Русский](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md) | [English(WIP)](https://github.com/DCFApixels/DragonECS) | | :--- | :--- | :--- | -The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies -> **Warning**: The project is a work in progress, API may change. +The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs](https://github.com/Leopotam/ecslite). +> [!WARNING] +> The project is a work in progress, API may change. +> > While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md). # Versioning @@ -23,3 +25,6 @@ DragonECS uses this versioning semantics: [Open](https://gist.github.com/DCFApix * [Classic C# multithreading support](https://github.com/DCFApixels/DragonECS-ClassicThreads) * Relations (Work in progress) * Unity integration (Work in progress) + +# Feedback +Discord for discussions [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf) \ No newline at end of file From 97c474225be8233793b407a118fbd8502b7171cc Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:41:20 +0800 Subject: [PATCH 084/104] Update README-RU.md --- README-RU.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README-RU.md b/README-RU.md index 1a468f3..4e37ab3 100644 --- a/README-RU.md +++ b/README-RU.md @@ -14,7 +14,8 @@ | :--- | :--- | :--- | Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. Вднохновлен [LeoEcs](https://github.com/Leopotam/ecslite). -> [!WARNING] Проект в стадии разработки. API может меняться. +> [!WARNING] +> Проект в стадии разработки. API может меняться. > Readme еще не завершен ## Оглавление @@ -738,4 +739,4 @@ The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing # Обратная связь Discord для дискуссий [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf) -


\ No newline at end of file +


From 97a9281c4d3f092f905f79b72895ad6519b18f3f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 22 Dec 2023 18:11:41 +0800 Subject: [PATCH 085/104] Update EcsGroup.cs --- src/EcsGroup.cs | 150 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 22 deletions(-) diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 40dacf8..7542435 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { + //_dense заполняется с индекса 1 [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)] [DebuggerTypeProxy(typeof(DebuggerProxy))] public readonly ref struct EcsReadonlyGroup @@ -185,11 +186,13 @@ namespace DCFApixels.DragonECS #endregion #region Add/Remove - public void UncheckedAdd(int entityID) => AddInternal(entityID); - public void Add(int entityID) + public void AddUnchecked(int entityID) => AddInternal(entityID); + public bool Add(int entityID) { - if (Has(entityID)) return; + if (Has(entityID)) + return false; AddInternal(entityID); + return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void AddInternal(int entityID) @@ -203,11 +206,13 @@ namespace DCFApixels.DragonECS _sparse[entityID] = _count; } - public void UncheckedRemove(int entityID) => RemoveInternal(entityID); - public void Remove(int entityID) + public void RemoveUnchecked(int entityID) => RemoveInternal(entityID); + public bool Remove(int entityID) { - if (!Has(entityID)) return; + if (!Has(entityID)) + return false; RemoveInternal(entityID); + return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void RemoveInternal(int entityID) @@ -289,6 +294,8 @@ namespace DCFApixels.DragonECS #endregion #region Set operations + + #region UnionWith /// as Union sets [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UnionWith(EcsReadonlyGroup group) => UnionWith(group.GetGroupInternal()); @@ -302,7 +309,50 @@ namespace DCFApixels.DragonECS if (!Has(item)) AddInternal(item); } + public void UnionWithRange(int[] range) + { + foreach (var item in range) + { + if (!Has(item)) + AddInternal(item); + } + } + public void UnionWithRange(Span range) + { + foreach (var item in range) + { + if (!Has(item)) + AddInternal(item); + } + } + public void UnionWithRange(ReadOnlySpan range) + { + foreach (var item in range) + { + if (!Has(item)) + AddInternal(item); + } + } + public void UnionWithRange(T range) where T : IList + { + for (int i = 0; i < range.Count; i++) + { + int item = range[i]; + if (!Has(item)) + AddInternal(item); + } + } + public void UnionWithRange(IEnumerable otherRange) + { + foreach (var item in otherRange) + { + if (!Has(item)) + AddInternal(item); + } + } + #endregion + #region ExceptWith /// as Except sets [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ExceptWith(EcsReadonlyGroup group) => ExceptWith(group.GetGroupInternal()); @@ -312,11 +362,63 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif - foreach (var item in this) - if (group.Has(item)) - RemoveInternal(item); + if (group.Count > Count) + { + foreach (var item in this) + if (group.Has(item)) + RemoveInternal(item); + } + else + { + foreach (var item in group) + if (Has(item)) + RemoveInternal(item); + } } + public void ExceptWithRange(int[] range) + { + foreach (var item in range) + { + if (Has(item)) + RemoveInternal(item); + } + } + public void ExceptWithRange(Span range) + { + foreach (var item in range) + { + if (Has(item)) + RemoveInternal(item); + } + } + public void ExceptWithRange(ReadOnlySpan range) + { + foreach (var item in range) + { + if (Has(item)) + RemoveInternal(item); + } + } + public void ExceptWithRange(T range) where T : IList + { + for (int i = 0; i < range.Count; i++) + { + int item = range[i]; + if (Has(item)) + RemoveInternal(item); + } + } + public void ExceptWithRange(IEnumerable otherRange) + { + foreach (var item in otherRange) + { + if (!Has(item)) + AddInternal(item); + } + } + #endregion + #region IntersectWith /// as Intersect sets [MethodImpl(MethodImplOptions.AggressiveInlining)] public void IntersectWith(EcsReadonlyGroup group) => IntersectWith(group.GetGroupInternal()); @@ -330,7 +432,9 @@ namespace DCFApixels.DragonECS if (!group.Has(item)) RemoveInternal(item); } + #endregion + #region SymmetricExceptWith /// as Symmetric Except sets [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SymmetricExceptWith(EcsReadonlyGroup group) => SymmetricExceptWith(group.GetGroupInternal()); @@ -346,7 +450,9 @@ namespace DCFApixels.DragonECS else AddInternal(item); } + #endregion + #region Inverse public void Inverse() { foreach (var item in _source.Entities) @@ -355,7 +461,9 @@ namespace DCFApixels.DragonECS else AddInternal(item); } + #endregion + #region SetEquals [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool SetEquals(EcsReadonlyGroup group) => SetEquals(group.GetGroupInternal()); public bool SetEquals(EcsGroup group) @@ -370,7 +478,9 @@ namespace DCFApixels.DragonECS return false; return true; } + #endregion + #region Overlaps [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); public bool Overlaps(EcsGroup group) @@ -383,7 +493,9 @@ namespace DCFApixels.DragonECS return true; return false; } + #endregion + #region IsSubsetOf [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); public bool IsSubsetOf(EcsGroup group) @@ -398,7 +510,9 @@ namespace DCFApixels.DragonECS return false; return true; } + #endregion + #region IsSupersetOf [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); public bool IsSupersetOf(EcsGroup group) @@ -415,6 +529,8 @@ namespace DCFApixels.DragonECS } #endregion + #endregion + #region Static Set operations /// as Intersect sets /// new group from pool @@ -560,24 +676,14 @@ namespace DCFApixels.DragonECS #endregion #region Object - public override string ToString() => $"group{{{string.Join(", ", _dense.Take(_count))}}}"; - //public override int GetHashCode() - //{ - // unchecked - // { - // uint hash = 0; - // foreach (var item in this) - // hash ^= ((uint)item * 314159u); //реализация от балды, так как не нужен, но фишка в том что хеш не учитывает порядок сущьностей, что явлется правильным поведением. - // return (int)hash; - // } - //} + public override string ToString() => $"group{{{string.Join(", ", _dense.Skip(1).Take(_count))}}}"; #endregion #region OtherMethods [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int First() => this[0]; + public int First() => _dense[1]; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Last() => this[_count - 1]; + public int Last() => _dense[_count]; #endregion #region OnWorldResize From a72f54270dc666a7aae2b4963eac604e4d9e1542 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 22 Dec 2023 23:25:31 +0800 Subject: [PATCH 086/104] Update EcsGroup.cs --- src/EcsGroup.cs | 66 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 7542435..65c1fb5 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -342,9 +342,9 @@ namespace DCFApixels.DragonECS AddInternal(item); } } - public void UnionWithRange(IEnumerable otherRange) + public void UnionWithRange(IEnumerable range) { - foreach (var item in otherRange) + foreach (var item in range) { if (!Has(item)) AddInternal(item); @@ -408,12 +408,12 @@ namespace DCFApixels.DragonECS RemoveInternal(item); } } - public void ExceptWithRange(IEnumerable otherRange) + public void ExceptWithRange(IEnumerable range) { - foreach (var item in otherRange) + foreach (var item in range) { - if (!Has(item)) - AddInternal(item); + if (Has(item)) + RemoveInternal(item); } } #endregion @@ -720,4 +720,58 @@ namespace DCFApixels.DragonECS } #endregion } + +#if false + public static class EcsGroupAliases + { + /// Alias for UnionWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(this EcsGroup self, EcsGroup group) + { + self.UnionWith(group); + } + /// Alias for UnionWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add(this EcsGroup self, EcsReadonlyGroup group) + { + self.UnionWith(group); + } + /// Alias for ExceptWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Remove(this EcsGroup self, EcsGroup group) + { + self.ExceptWith(group); + } + /// Alias for ExceptWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Remove(this EcsGroup self, EcsReadonlyGroup group) + { + self.ExceptWith(group); + } + /// Alias for SymmetricExceptWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Xor(this EcsGroup self, EcsGroup group) + { + self.SymmetricExceptWith(group); + } + /// Alias for SymmetricExceptWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Xor(this EcsGroup self, EcsReadonlyGroup group) + { + self.SymmetricExceptWith(group); + } + /// Alias for IntersectWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void And(this EcsGroup self, EcsGroup group) + { + self.IntersectWith(group); + } + /// Alias for IntersectWith + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void And(this EcsGroup self, EcsReadonlyGroup group) + { + self.IntersectWith(group); + } + } +#endif } \ No newline at end of file From aa1fe139c43319edb132c72ae09f58f75ae390e9 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 23 Dec 2023 16:07:59 +0800 Subject: [PATCH 087/104] Update README-RU.md --- README-RU.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README-RU.md b/README-RU.md index 4e37ab3..28f6fae 100644 --- a/README-RU.md +++ b/README-RU.md @@ -5,7 +5,7 @@

Version License - +Discord

# DragonECS - C# Entity Component System Framework @@ -19,8 +19,6 @@ > Readme еще не завершен ## Оглавление -- [DragonECS - C# Entity Component System Framework](#dragonecs---c-entity-component-system-framework) - - [Оглавление](#оглавление) - [Установка](#установка) - [Версионирование](#версионирование) - [Основные концепции](#основные-концепции) @@ -50,7 +48,6 @@ - [Профилирование](#профилирование) - [Расширения](#расширения) - [FAQ](#faq) - - ['ReadOnlySpan\<\>' could not be found](#readonlyspan-could-not-be-found) - [Обратная связь](#обратная-связь)
@@ -655,7 +652,7 @@ bool isCamera = _world.GetPool().Has(entity);
# Debug -Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. +Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. Так же многие типы имеют свой DebuggerProxy для более информативного отображения в IDE. ## Атрибуты В чистом виде дебаг-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах. ``` c# From 909a9fe3e743628ac87a2bd4de9eca2fb6334500 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 23 Dec 2023 20:17:28 +0800 Subject: [PATCH 088/104] update EcsGroup --- src/{ => Collections}/EcsGroup.cs | 20 ++++++++++++++++---- src/{ => Collections}/EcsGroup.cs.meta | 0 2 files changed, 16 insertions(+), 4 deletions(-) rename src/{ => Collections}/EcsGroup.cs (95%) rename src/{ => Collections}/EcsGroup.cs.meta (100%) diff --git a/src/EcsGroup.cs b/src/Collections/EcsGroup.cs similarity index 95% rename from src/EcsGroup.cs rename to src/Collections/EcsGroup.cs index 65c1fb5..4f71c6b 100644 --- a/src/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -93,6 +93,11 @@ namespace DCFApixels.DragonECS #endregion + #region Convertions + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(EcsReadonlyGroup a) => a.ToSpan(); + #endregion + #region DebuggerProxy internal class DebuggerProxy : EcsGroup.DebuggerProxy { @@ -623,7 +628,7 @@ namespace DCFApixels.DragonECS public Enumerator(EcsGroup group) { _dense = group._dense; - _count = group._count; + _count = group._count > _dense.Length ? _dense.Length : group._count; _index = 0; } public int Current @@ -632,7 +637,7 @@ namespace DCFApixels.DragonECS get => _dense[_index]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() => ++_index <= _count && _count < _dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + public bool MoveNext() => ++_index <= _count; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны } public readonly struct LongsIterator : IEnumerable { @@ -661,7 +666,7 @@ namespace DCFApixels.DragonECS { world = group.World; _dense = group._dense; - _count = group._count; + _count = group._count > _dense.Length ? _dense.Length : group._count; _index = 0; } public entlong Current @@ -670,7 +675,7 @@ namespace DCFApixels.DragonECS get => world.GetEntityLong(_dense[_index]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() => ++_index <= _count && _count < _dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + public bool MoveNext() => ++_index <= _count; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны } } #endregion @@ -694,6 +699,13 @@ namespace DCFApixels.DragonECS } #endregion + #region Convertions + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator EcsReadonlyGroup(EcsGroup a) => a.Readonly; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(EcsGroup a) => a.ToSpan(); + #endregion + #region DebuggerProxy internal class DebuggerProxy { diff --git a/src/EcsGroup.cs.meta b/src/Collections/EcsGroup.cs.meta similarity index 100% rename from src/EcsGroup.cs.meta rename to src/Collections/EcsGroup.cs.meta From 18345109c6e57f1bdb7e720610a5c48fcc97d38f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 00:44:44 +0800 Subject: [PATCH 089/104] Update README-RU.md --- README-RU.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-RU.md b/README-RU.md index 28f6fae..6ac28fa 100644 --- a/README-RU.md +++ b/README-RU.md @@ -5,7 +5,7 @@

Version License -Discord +Discord

# DragonECS - C# Entity Component System Framework From 70cf1be4082ba59f5972a6be20404d91d6a0ca32 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 15:40:19 +0800 Subject: [PATCH 090/104] update --- src/Builtin/BaseProcesses.cs | 4 ++ src/Collections/EcsGroup.cs | 58 +++++++++++++-------- src/Collections/EcsSpan.cs | 81 +++++++++++++++++++++++++++++ src/EcsAspect.cs | 28 +++++++++- src/EcsRunner.cs | 2 +- src/EcsWorld.cs | 4 ++ src/Executors/EcsQueryExecutor.cs | 28 +++++++--- src/Executors/EcsWhereExecutor.cs | 85 ++++++++++++++++++++++++++++--- src/Utils/GenericEnumerable.cs | 21 ++++++++ src/Utils/ReadOnlySpanDummy.cs | 2 +- 10 files changed, 275 insertions(+), 38 deletions(-) create mode 100644 src/Collections/EcsSpan.cs create mode 100644 src/Utils/GenericEnumerable.cs diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index 6176df0..8697b95 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -7,24 +7,28 @@ namespace DCFApixels.DragonECS { #region Interfaces [MetaName(nameof(PreInit))] + [MetaColor(MetaColor.Orange)] [BindWithEcsRunner(typeof(EcsPreInitProcessRunner))] public interface IEcsPreInitProcess : IEcsProcess { void PreInit(); } [MetaName(nameof(Init))] + [MetaColor(MetaColor.Orange)] [BindWithEcsRunner(typeof(EcsInitProcessRunner))] public interface IEcsInitProcess : IEcsProcess { void Init(); } [MetaName(nameof(Run))] + [MetaColor(MetaColor.Orange)] [BindWithEcsRunner(typeof(EcsRunProcessRunner))] public interface IEcsRunProcess : IEcsProcess { void Run(); } [MetaName(nameof(Destroy))] + [MetaColor(MetaColor.Orange)] [BindWithEcsRunner(typeof(EcsDestroyProcessRunner))] public interface IEcsDestroyProcess : IEcsProcess { diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 4f71c6b..09eb1dd 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -23,6 +23,11 @@ namespace DCFApixels.DragonECS #region Properties public bool IsNull => _source == null; + public int WorldID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source.World.id; + } public EcsWorld World { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -116,6 +121,11 @@ namespace DCFApixels.DragonECS internal bool _isReleased = true; #region Properties + public int WorldID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source.id; + } public EcsWorld World { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -619,7 +629,7 @@ namespace DCFApixels.DragonECS yield return _dense[i]; } public LongsIterator GetLongs() => new LongsIterator(this); - public ref struct Enumerator + public struct Enumerator : IEnumerator { private readonly int[] _dense; private readonly int _count; @@ -636,8 +646,13 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _dense[_index]; } + object IEnumerator.Current => Current; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => ++_index <= _count; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } } public readonly struct LongsIterator : IEnumerable { @@ -680,25 +695,6 @@ namespace DCFApixels.DragonECS } #endregion - #region Object - public override string ToString() => $"group{{{string.Join(", ", _dense.Skip(1).Take(_count))}}}"; - #endregion - - #region OtherMethods - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int First() => _dense[1]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Last() => _dense[_count]; - #endregion - - #region OnWorldResize - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void OnWorldResize(int newSize) - { - Array.Resize(ref _sparse, newSize); - } - #endregion - #region Convertions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator EcsReadonlyGroup(EcsGroup a) => a.Readonly; @@ -706,7 +702,26 @@ namespace DCFApixels.DragonECS public static implicit operator ReadOnlySpan(EcsGroup a) => a.ToSpan(); #endregion - #region DebuggerProxy + #region Other + public override string ToString() + { + return $"group{{{string.Join(", ", _dense.Skip(1).Take(_count))}}}"; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int First() + { + return _dense[1]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Last() + { + return _dense[_count]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void OnWorldResize(int newSize) + { + Array.Resize(ref _sparse, newSize); + } internal class DebuggerProxy { private EcsGroup _group; @@ -726,7 +741,6 @@ namespace DCFApixels.DragonECS public int Count => _group.Count; public int CapacityDense => _group.CapacityDense; public int CapacitySparce => _group.CapacitySparce; - public override string ToString() => _group.ToString(); public DebuggerProxy(EcsGroup group) => _group = group; } diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs new file mode 100644 index 0000000..f7b011f --- /dev/null +++ b/src/Collections/EcsSpan.cs @@ -0,0 +1,81 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS +{ + public readonly ref struct EcsSpan + { + private readonly int _worldID; + private readonly ReadOnlySpan _values; + + #region Properties + public int WorldID => _worldID; + public EcsWorld World => EcsWorld.GetWorld(_worldID); + public int Length => _values.Length; + public bool IsEmpty => _values.IsEmpty; + public readonly int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _values[index]; + } + #endregion + + #region Constructors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsSpan(int worldID, ReadOnlySpan span) + { + _worldID = worldID; + _values = span; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsSpan(int worldID, int[] array) + { + _worldID = worldID; + _values = array; + } + internal EcsSpan(int worldID, int[] array, int length) + { + _worldID = worldID; + _values = new ReadOnlySpan(array, 0, length); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsSpan(int worldID, int[] array, int start, int length) + { + _worldID = worldID; + _values = new ReadOnlySpan(array, start, length); + } + #endregion + + #region Object +#pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член + [Obsolete($"Equals() on {nameof(EcsSpan)} will always throw an exception. Use the equality operator instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => throw new NotSupportedException(); + [Obsolete($"GetHashCode() on {nameof(EcsSpan)} will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException(); +#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член + public override string ToString() => _values.ToString(); + #endregion + + #region operators + public static bool operator ==(EcsSpan left, EcsSpan right) => left._values == right._values; + public static bool operator !=(EcsSpan left, EcsSpan right) => left._values != right._values; + #endregion + + #region Enumerator + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan.Enumerator GetEnumerator() => _values.GetEnumerator(); + #endregion + + #region Other + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsSpan Slice(int start) => new EcsSpan(_worldID, _values.Slice(start)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsSpan Slice(int start, int length) => new EcsSpan(_worldID, _values.Slice(start, length)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int[] ToArray() => _values.ToArray(); + #endregion + } +} diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index bea4d3e..b7c7411 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -334,13 +334,15 @@ namespace DCFApixels.DragonECS #region Iterator public ref struct EcsAspectIterator { + public readonly int worldID; public readonly EcsMask mask; private EcsReadonlyGroup _sourceGroup; private Enumerator _enumerator; public EcsAspectIterator(EcsAspect aspect, EcsReadonlyGroup sourceGroup) { - mask = aspect.mask; + worldID = aspect.World.id; + mask = aspect.mask; _sourceGroup = sourceGroup; _enumerator = default; } @@ -361,6 +363,30 @@ namespace DCFApixels.DragonECS while (enumerator.MoveNext()) group.AddInternal(enumerator.Current); } + public int CopyTo(ref int[] array) + { + var enumerator = GetEnumerator(); + int count = 0; + while (enumerator.MoveNext()) + { + if(array.Length <= count) + Array.Resize(ref array, array.Length << 1); + array[count++] = enumerator.Current; + } + return count; + } + public EcsSpan CopyToSpan(ref int[] array) + { + var enumerator = GetEnumerator(); + int count = 0; + while (enumerator.MoveNext()) + { + if (array.Length <= count) + Array.Resize(ref array, array.Length << 1); + array[count++] = enumerator.Current; + } + return new EcsSpan(worldID, array, count); + } #region object public override string ToString() diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index b0d4470..e229c4d 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -239,7 +239,7 @@ namespace DCFApixels.DragonECS _filter = null; OnDestroy(); } - protected virtual void OnSetup() { } + protected virtual void OnSetup() { } //rename to OnInitialize protected virtual void OnDestroy() { } } } diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 021ccbf..cae19ab 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -141,6 +141,10 @@ namespace DCFApixels.DragonECS { return GetExecutor>().Execute(); } + public EcsSpan WhereSpan() where TAspect : EcsAspect + { + return GetExecutor>().Execute(); + } #endregion #region Entity diff --git a/src/Executors/EcsQueryExecutor.cs b/src/Executors/EcsQueryExecutor.cs index c33cb3f..fdbf56a 100644 --- a/src/Executors/EcsQueryExecutor.cs +++ b/src/Executors/EcsQueryExecutor.cs @@ -1,16 +1,32 @@ -namespace DCFApixels.DragonECS +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS { public abstract class EcsQueryExecutor { - private EcsWorld _world; - public EcsWorld World => _world; + private EcsWorld _source; + public int WorldID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source.id; + } + public EcsWorld World + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source; + } + public abstract long Version { get; } internal void Initialize(EcsWorld world) { - _world = world; + _source = world; OnInitialize(); } - internal void Destroy() => OnDestroy(); + internal void Destroy() + { + OnDestroy(); + _source = null; + } protected abstract void OnInitialize(); protected abstract void OnDestroy(); } -} +} \ No newline at end of file diff --git a/src/Executors/EcsWhereExecutor.cs b/src/Executors/EcsWhereExecutor.cs index 89a45ac..a1be62a 100644 --- a/src/Executors/EcsWhereExecutor.cs +++ b/src/Executors/EcsWhereExecutor.cs @@ -1,19 +1,29 @@ -namespace DCFApixels.DragonECS +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS { public sealed class EcsWhereExecutor : EcsQueryExecutor where TAspect : EcsAspect { private TAspect _aspect; private EcsGroup _filteredGroup; - private long _executeVersion; + private long _version; #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - private EcsProfilerMarker _executeWhere = new EcsProfilerMarker("Where"); + private readonly EcsProfilerMarker _executeMarker = new EcsProfilerMarker("Where"); #endif #region Properties - public TAspect Aspect => _aspect; - internal long ExecuteVersion => _executeVersion; + public TAspect Aspect + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _aspect; + } + public sealed override long Version + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _version; + } #endregion #region OnInitialize/OnDestroy @@ -29,19 +39,80 @@ #endregion #region Methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsReadonlyGroup Execute() => ExecuteFor(_aspect.World.Entities); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsReadonlyGroup ExecuteFor(EcsReadonlyGroup sourceGroup) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - _executeWhere.Begin(); + _executeMarker.Begin(); if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. + if (sourceGroup.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения. #endif + unchecked { _version++; } _aspect.GetIteratorFor(sourceGroup).CopyTo(_filteredGroup); #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - _executeWhere.End(); + _executeMarker.End(); #endif return _filteredGroup.Readonly; } #endregion } + + + + + public sealed class EcsWhereSpanExecutor : EcsQueryExecutor where TAspect : EcsAspect + { + private TAspect _aspect; + private int[] _filteredEntities; + + private long _version; + +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + private readonly EcsProfilerMarker _executeMarker = new EcsProfilerMarker("Where"); +#endif + + #region Properties + public TAspect Aspect + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _aspect; + } + public sealed override long Version + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _version; + } + #endregion + + #region OnInitialize/OnDestroy + protected sealed override void OnInitialize() + { + _aspect = World.GetAspect(); + _filteredEntities = new int[32]; + } + protected sealed override void OnDestroy() { } + #endregion + + #region Methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsSpan Execute() => ExecuteFor(_aspect.World.Entities); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsSpan ExecuteFor(EcsReadonlyGroup sourceGroup) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + _executeMarker.Begin(); + if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. + if (sourceGroup.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения. +#endif + unchecked { _version++; } + EcsSpan result = _aspect.GetIteratorFor(sourceGroup).CopyToSpan(ref _filteredEntities); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + _executeMarker.End(); +#endif + return result; + } + #endregion + } } diff --git a/src/Utils/GenericEnumerable.cs b/src/Utils/GenericEnumerable.cs new file mode 100644 index 0000000..a03a71d --- /dev/null +++ b/src/Utils/GenericEnumerable.cs @@ -0,0 +1,21 @@ +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS.Internal +{ + public readonly struct GenericEnumerable : IEnumerable where TEnumerator : IEnumerator + { + public readonly TEnumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public GenericEnumerable(TEnumerator enumerator) => _enumerator = enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TEnumerator GetEnumerator() => _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() => _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + IEnumerator IEnumerable.GetEnumerator() => _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator GenericEnumerable(TEnumerator enumerator) => new GenericEnumerable(enumerator); + } +} \ No newline at end of file diff --git a/src/Utils/ReadOnlySpanDummy.cs b/src/Utils/ReadOnlySpanDummy.cs index 74ddd8b..508b375 100644 --- a/src/Utils/ReadOnlySpanDummy.cs +++ b/src/Utils/ReadOnlySpanDummy.cs @@ -38,13 +38,13 @@ namespace DCFApixels.DragonECS #endregion #region Constructors + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan(T[] array) { _array = array ?? Array.Empty(); _start = 0; _length = array.Length; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan(T[] array, int start, int length) { From bf145650ee418e55a88a9e7d2d45d090ba70a6f0 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 15:48:24 +0800 Subject: [PATCH 091/104] Update EcsGroup.cs --- src/Collections/EcsGroup.cs | 80 +++++-------------------------------- 1 file changed, 10 insertions(+), 70 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 09eb1dd..f94ca62 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -324,42 +324,12 @@ namespace DCFApixels.DragonECS if (!Has(item)) AddInternal(item); } - public void UnionWithRange(int[] range) + public void UnionWith(EcsSpan span) { - foreach (var item in range) - { - if (!Has(item)) - AddInternal(item); - } - } - public void UnionWithRange(Span range) - { - foreach (var item in range) - { - if (!Has(item)) - AddInternal(item); - } - } - public void UnionWithRange(ReadOnlySpan range) - { - foreach (var item in range) - { - if (!Has(item)) - AddInternal(item); - } - } - public void UnionWithRange(T range) where T : IList - { - for (int i = 0; i < range.Count; i++) - { - int item = range[i]; - if (!Has(item)) - AddInternal(item); - } - } - public void UnionWithRange(IEnumerable range) - { - foreach (var item in range) +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + foreach (var item in span) { if (!Has(item)) AddInternal(item); @@ -390,42 +360,12 @@ namespace DCFApixels.DragonECS RemoveInternal(item); } } - public void ExceptWithRange(int[] range) + public void ExceptWith(EcsSpan span) { - foreach (var item in range) - { - if (Has(item)) - RemoveInternal(item); - } - } - public void ExceptWithRange(Span range) - { - foreach (var item in range) - { - if (Has(item)) - RemoveInternal(item); - } - } - public void ExceptWithRange(ReadOnlySpan range) - { - foreach (var item in range) - { - if (Has(item)) - RemoveInternal(item); - } - } - public void ExceptWithRange(T range) where T : IList - { - for (int i = 0; i < range.Count; i++) - { - int item = range[i]; - if (Has(item)) - RemoveInternal(item); - } - } - public void ExceptWithRange(IEnumerable range) - { - foreach (var item in range) +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + foreach (var item in span) { if (Has(item)) RemoveInternal(item); From 4e140a39d847b16a1dbc7deee6e4527a5903d911 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 15:54:34 +0800 Subject: [PATCH 092/104] Update EcsGroup.cs --- src/Collections/EcsGroup.cs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index f94ca62..26c5156 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -86,6 +86,24 @@ namespace DCFApixels.DragonECS public int First() => _source.First(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Last() => _source.Last(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SetEquals(EcsReadonlyGroup group) => _source.SetEquals(group._source); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SetEquals(EcsGroup group) => _source.SetEquals(group); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Overlaps(EcsReadonlyGroup group) => _source.Overlaps(group._source); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Overlaps(EcsGroup group) => _source.Overlaps(group); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsSubsetOf(EcsReadonlyGroup group) => _source.IsSubsetOf(group._source); + public bool IsSubsetOf(EcsGroup group) => _source.IsSubsetOf(group); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsSupersetOf(EcsReadonlyGroup group) => _source.Overlaps(group._source); + public bool IsSupersetOf(EcsGroup group) => _source.IsSupersetOf(group); #endregion #region Object @@ -452,7 +470,7 @@ namespace DCFApixels.DragonECS #region IsSubsetOf [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsSubsetOf(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); + public bool IsSubsetOf(EcsReadonlyGroup group) => IsSubsetOf(group.GetGroupInternal()); public bool IsSubsetOf(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS @@ -469,7 +487,7 @@ namespace DCFApixels.DragonECS #region IsSupersetOf [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsSupersetOf(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal()); + public bool IsSupersetOf(EcsReadonlyGroup group) => IsSupersetOf(group.GetGroupInternal()); public bool IsSupersetOf(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS From 87ec923ff9ec449ff3479c57f6469a8c596a4731 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:04:24 +0800 Subject: [PATCH 093/104] Update EcsGroup.cs --- src/Collections/EcsGroup.cs | 39 +++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 26c5156..9c4619f 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -91,11 +91,15 @@ namespace DCFApixels.DragonECS public bool SetEquals(EcsReadonlyGroup group) => _source.SetEquals(group._source); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool SetEquals(EcsGroup group) => _source.SetEquals(group); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SetEquals(EcsSpan span) => _source.SetEquals(span); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(EcsReadonlyGroup group) => _source.Overlaps(group._source); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(EcsGroup group) => _source.Overlaps(group); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Overlaps(EcsSpan span) => _source.Overlaps(span); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(EcsReadonlyGroup group) => _source.IsSubsetOf(group._source); @@ -451,6 +455,18 @@ namespace DCFApixels.DragonECS return false; return true; } + public bool SetEquals(EcsSpan span) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + if (span.Length != Count) + return false; + foreach (var item in span) + if (!Has(item)) + return false; + return true; + } #endregion #region Overlaps @@ -461,8 +477,27 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif - foreach (var item in group) - if (!Has(item)) + if(group.Count > Count) + { + foreach (var item in this) + if (group.Has(item)) + return true; + } + else + { + foreach (var item in group) + if (Has(item)) + return true; + } + return false; + } + public bool Overlaps(EcsSpan span) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); +#endif + foreach (var item in span) + if (Has(item)) return true; return false; } From 7a6dc93bdb5b856c42cea5df0e3b8d3708950907 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 17:09:01 +0800 Subject: [PATCH 094/104] Update EcsGroup.cs --- src/Collections/EcsGroup.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 9c4619f..463ccff 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -349,7 +349,7 @@ namespace DCFApixels.DragonECS public void UnionWith(EcsSpan span) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); + if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException(); #endif foreach (var item in span) { @@ -385,7 +385,7 @@ namespace DCFApixels.DragonECS public void ExceptWith(EcsSpan span) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); + if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException(); #endif foreach (var item in span) { @@ -458,7 +458,7 @@ namespace DCFApixels.DragonECS public bool SetEquals(EcsSpan span) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); + if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException(); #endif if (span.Length != Count) return false; @@ -494,7 +494,7 @@ namespace DCFApixels.DragonECS public bool Overlaps(EcsSpan span) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_source != span.World) Throw.Group_ArgumentDifferentWorldsException(); + if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException(); #endif foreach (var item in span) if (Has(item)) From 89cb4c68b9c8264a84261abc91020a4d693082b9 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 18:05:30 +0800 Subject: [PATCH 095/104] fix EcsGroup --- src/Collections/EcsGroup.cs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 463ccff..50d5fb7 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { //_dense заполняется с индекса 1 + //в операциях изменяющих состояние группы нельзя итерироваться по this, либо осторожно учитывать этот момент [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)] [DebuggerTypeProxy(typeof(DebuggerProxy))] public readonly ref struct EcsReadonlyGroup @@ -369,17 +370,39 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif + //if (group.Count > Count) // вариант 1. есть итерация по this + //{ + // foreach (var item in this) + // if (group.Has(item)) + // RemoveInternal(item); + //} + //else + //{ + // foreach (var item in group) + // if (Has(item)) + // RemoveInternal(item); + //} + + //foreach (var item in group) // вариант 2 + // if (Has(item)) + // RemoveInternal(item); + if (group.Count > Count) { - foreach (var item in this) + for (int i = _count; i > 0; i--)//итерация в обратном порядке исключает ошибки при удалении элементов + { + int item = _dense[i]; if (group.Has(item)) RemoveInternal(item); + } } else { foreach (var item in group) + { if (Has(item)) RemoveInternal(item); + } } } public void ExceptWith(EcsSpan span) @@ -405,9 +428,12 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (World != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif - foreach (var item in this) + for (int i = _count; i > 0; i--)//итерация в обратном порядке исключает ошибки при удалении элементов + { + int item = _dense[i]; if (!group.Has(item)) RemoveInternal(item); + } } #endregion From 82c5dbc93917e6f2e45f2611564fed14ddc1e63f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 24 Dec 2023 18:11:20 +0800 Subject: [PATCH 096/104] Update ArrayUtility.cs --- src/Utils/ArrayUtility.cs | 41 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Utils/ArrayUtility.cs b/src/Utils/ArrayUtility.cs index 9eab0b6..b70bcc5 100644 --- a/src/Utils/ArrayUtility.cs +++ b/src/Utils/ArrayUtility.cs @@ -1,4 +1,8 @@ -namespace DCFApixels.DragonECS.Utils +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System; + +namespace DCFApixels.DragonECS.Utils { internal static class ArrayUtility { @@ -12,4 +16,39 @@ array[i] = value; } } + + internal static unsafe class UnmanagedArrayUtility + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* New(int capacity) where T : struct + { + return Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * capacity).ToPointer(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* NewAndInit(int capacity) where T : struct + { + int newSize = Marshal.SizeOf(typeof(T)) * capacity; + byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); + + for (int i = 0; i < newSize; i++) + *(newPointer + i) = 0; + + return newPointer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Free(void* pointer) + { + Marshal.FreeHGlobal(new IntPtr(pointer)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* Resize(void* oldPointer, int newCount) where T : struct + { + return (Marshal.ReAllocHGlobal( + new IntPtr(oldPointer), + new IntPtr(Marshal.SizeOf(typeof(T)) * newCount))).ToPointer(); + } + } } From 1ecad4de1df72a8e51ba200dcffbe501702c54b6 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 13:07:53 +0800 Subject: [PATCH 097/104] Update entity lifecycle/collcetions update auto release DelEntBuffer replace ReadnolySpan to EcsSpan --- src/Builtin/Aspects.cs | 30 +++--- src/Builtin/Systems.cs | 4 +- src/Collections/EcsGroup.cs | 66 ++++++------- src/Collections/EcsSpan.cs | 23 ++++- src/EcsAspect.cs | 26 +++--- src/EcsWorld.cs | 149 +++++++++++++++++++++++------- src/EcsWorld.pools.cs | 4 +- src/EcsWorld.static.cs | 21 ++++- src/Executors/EcsWhereExecutor.cs | 16 ++-- 9 files changed, 221 insertions(+), 118 deletions(-) diff --git a/src/Builtin/Aspects.cs b/src/Builtin/Aspects.cs index 4844048..0e9c607 100644 --- a/src/Builtin/Aspects.cs +++ b/src/Builtin/Aspects.cs @@ -121,20 +121,20 @@ var combined = self.GetAspect>(); a0 = combined.a0; a1 = combined.a1; - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } public static EcsReadonlyGroup Where(this EcsWorld self) where A0 : EcsAspect where A1 : EcsAspect { - return self.Where>(); + return self.WhereToGroup>(); } public static EcsReadonlyGroup WhereFor(this EcsWorld self, EcsReadonlyGroup sourceGroup) where A0 : EcsAspect where A1 : EcsAspect { - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } #endregion @@ -155,7 +155,7 @@ a0 = combined.a0; a1 = combined.a1; a2 = combined.a2; - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } public static EcsReadonlyGroup Where(this EcsWorld self) @@ -163,14 +163,14 @@ where A1 : EcsAspect where A2 : EcsAspect { - return self.Where>(); + return self.WhereToGroup>(); } public static EcsReadonlyGroup WhereFor(this EcsWorld self, EcsReadonlyGroup sourceGroup) where A0 : EcsAspect where A1 : EcsAspect where A2 : EcsAspect { - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } #endregion @@ -194,7 +194,7 @@ a1 = combined.a1; a2 = combined.a2; a3 = combined.a3; - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } public static EcsReadonlyGroup Where(this EcsWorld self) @@ -203,7 +203,7 @@ where A2 : EcsAspect where A3 : EcsAspect { - return self.Where>(); + return self.WhereToGroup>(); } public static EcsReadonlyGroup WhereFor(this EcsWorld self, EcsReadonlyGroup sourceGroup) where A0 : EcsAspect @@ -211,7 +211,7 @@ where A2 : EcsAspect where A3 : EcsAspect { - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } #endregion @@ -238,7 +238,7 @@ a2 = combined.a2; a3 = combined.a3; a4 = combined.a4; - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } @@ -249,7 +249,7 @@ where A3 : EcsAspect where A4 : EcsAspect { - return self.Where>(); + return self.WhereToGroup>(); } public static EcsReadonlyGroup WhereFor(this EcsWorld self, EcsReadonlyGroup sourceGroup) where A0 : EcsAspect @@ -258,7 +258,7 @@ where A3 : EcsAspect where A4 : EcsAspect { - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } #endregion @@ -288,7 +288,7 @@ a3 = combined.a3; a4 = combined.a4; a5 = combined.a5; - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } @@ -300,7 +300,7 @@ where A4 : EcsAspect where A5 : EcsAspect { - return self.Where>(); + return self.WhereToGroup>(); } public static EcsReadonlyGroup WhereFor(this EcsWorld self, EcsReadonlyGroup sourceGroup) where A0 : EcsAspect @@ -310,7 +310,7 @@ where A4 : EcsAspect where A5 : EcsAspect { - return self.WhereFor>(sourceGroup); + return self.WhereToGroupFor>(sourceGroup); } #endregion } diff --git a/src/Builtin/Systems.cs b/src/Builtin/Systems.cs index 7f1e2ca..5428395 100644 --- a/src/Builtin/Systems.cs +++ b/src/Builtin/Systems.cs @@ -23,7 +23,7 @@ namespace DCFApixels.DragonECS foreach (var world in _worlds) { world.DeleteEmptyEntites(); - world.ReleaseDelEntityBuffer(); + world.ReleaseDelEntityBufferAll(); } } } @@ -47,7 +47,7 @@ namespace DCFApixels.DragonECS EcsWorld world = _worlds[i]; if (world.IsComponentTypeDeclared()) { - foreach (var e in world.Where(out Aspect a)) + foreach (var e in world.WhereToGroup(out Aspect a)) a.pool.Del(e); } } diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 50d5fb7..fba66cb 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -77,9 +77,9 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Bake(List entities) => _source.Bake(entities); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan ToSpan() => _source.ToSpan(); + public EcsSpan ToSpan() => _source.ToSpan(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan ToSpan(int start, int length) => _source.ToSpan(start, length); + public EcsSpan ToSpan(int start, int length) => _source.ToSpan(start, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsGroup.LongsIterator GetLongs() => _source.GetLongs(); @@ -121,12 +121,9 @@ namespace DCFApixels.DragonECS #endregion - #region Convertions + #region Other [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(EcsReadonlyGroup a) => a.ToSpan(); - #endregion - - #region DebuggerProxy + public static implicit operator EcsSpan(EcsReadonlyGroup a) => a.ToSpan(); internal class DebuggerProxy : EcsGroup.DebuggerProxy { public DebuggerProxy(EcsReadonlyGroup group) : base(group._source) { } @@ -321,13 +318,16 @@ namespace DCFApixels.DragonECS foreach (var e in this) entities.Add(e); } - public ReadOnlySpan ToSpan() => new ReadOnlySpan(_dense, 0, _count); - public ReadOnlySpan ToSpan(int start, int length) + public EcsSpan ToSpan() + { + return new EcsSpan(WorldID, _dense); + } + public EcsSpan ToSpan(int start, int length) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (start + length > _count) Throw.ArgumentOutOfRange(); #endif - return new ReadOnlySpan(_dense, start, length); + return new EcsSpan(WorldID, _dense, start, length); } #endregion @@ -637,28 +637,18 @@ namespace DCFApixels.DragonECS #region Enumerator [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() => new Enumerator(this); - IEnumerator IEnumerable.GetEnumerator() - { - for (int i = 0; i < _count; i++) - yield return _dense[i]; - } - IEnumerator IEnumerable.GetEnumerator() - { - for (int i = 0; i < _count; i++) - yield return _dense[i]; - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public LongsIterator GetLongs() => new LongsIterator(this); public struct Enumerator : IEnumerator { private readonly int[] _dense; - private readonly int _count; - private int _index; + private uint _index; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator(EcsGroup group) { _dense = group._dense; - _count = group._count > _dense.Length ? _dense.Length : group._count; - _index = 0; + _index = (uint)(group._count > _dense.Length ? _dense.Length : group._count); } public int Current { @@ -667,7 +657,7 @@ namespace DCFApixels.DragonECS } object IEnumerator.Current => Current; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() => ++_index <= _count; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + public bool MoveNext() => --_index > 0; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -689,38 +679,34 @@ namespace DCFApixels.DragonECS for (int i = 0; i < _group._count; i++) yield return _group.World.GetEntityLong(_group._dense[i]); } - public ref struct Enumerator + public struct Enumerator : IEnumerator { private readonly EcsWorld world; private readonly int[] _dense; - private readonly int _count; - private int _index; + private uint _index; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator(EcsGroup group) { world = group.World; _dense = group._dense; - _count = group._count > _dense.Length ? _dense.Length : group._count; - _index = 0; + _index = (uint)(group._count > _dense.Length ? _dense.Length : group._count); } public entlong Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => world.GetEntityLong(_dense[_index]); } + object IEnumerator.Current => Current; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() => ++_index <= _count; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + public bool MoveNext() => ++_index > 0; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } } } #endregion - #region Convertions - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator EcsReadonlyGroup(EcsGroup a) => a.Readonly; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(EcsGroup a) => a.ToSpan(); - #endregion - #region Other public override string ToString() { @@ -741,6 +727,10 @@ namespace DCFApixels.DragonECS { Array.Resize(ref _sparse, newSize); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator EcsReadonlyGroup(EcsGroup a) => a.Readonly; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator EcsSpan(EcsGroup a) => a.ToSpan(); internal class DebuggerProxy { private EcsGroup _group; diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index f7b011f..5306b9e 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -10,15 +10,30 @@ namespace DCFApixels.DragonECS private readonly ReadOnlySpan _values; #region Properties - public int WorldID => _worldID; - public EcsWorld World => EcsWorld.GetWorld(_worldID); - public int Length => _values.Length; - public bool IsEmpty => _values.IsEmpty; + public int WorldID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _worldID; + } + public EcsWorld World + { + get => EcsWorld.GetWorld(_worldID); + } + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _values.Length; + } public readonly int this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _values[index]; } + public bool IsNull + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _values.IsEmpty; + } #endregion #region Constructors diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index b7c7411..abadc2a 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -188,9 +188,9 @@ namespace DCFApixels.DragonECS { return new EcsAspectIterator(this, source.Entities); } - public EcsAspectIterator GetIteratorFor(EcsReadonlyGroup sourceGroup) + public EcsAspectIterator GetIteratorFor(EcsSpan span) { - return new EcsAspectIterator(this, sourceGroup); + return new EcsAspectIterator(this, span); } #endregion @@ -336,14 +336,14 @@ namespace DCFApixels.DragonECS { public readonly int worldID; public readonly EcsMask mask; - private EcsReadonlyGroup _sourceGroup; + private EcsSpan _span; private Enumerator _enumerator; - public EcsAspectIterator(EcsAspect aspect, EcsReadonlyGroup sourceGroup) + public EcsAspectIterator(EcsAspect aspect, EcsSpan span) { worldID = aspect.World.id; mask = aspect.mask; - _sourceGroup = sourceGroup; + _span = span; _enumerator = default; } @@ -403,33 +403,33 @@ namespace DCFApixels.DragonECS #region Enumerator [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(_sourceGroup, mask); + public Enumerator GetEnumerator() => new Enumerator(_span, mask); public ref struct Enumerator { - private EcsGroup.Enumerator _sourceGroup; + private ReadOnlySpan.Enumerator _span; private readonly EcsMaskBit[] _inc; private readonly EcsMaskBit[] _exc; private readonly int[][] _entitiesComponentMasks; - public Enumerator(EcsReadonlyGroup sourceGroup, EcsMask mask) + public Enumerator(EcsSpan span, EcsMask mask) { - _sourceGroup = sourceGroup.GetEnumerator(); + _span = span.GetEnumerator(); _inc = mask.incChunckMasks; _exc = mask.excChunckMasks; - _entitiesComponentMasks = sourceGroup.World._entitiesComponentMasks; + _entitiesComponentMasks = span.World._entitiesComponentMasks; } public int Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _sourceGroup.Current; + get => _span.Current; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - while (_sourceGroup.MoveNext()) + while (_span.MoveNext()) { - int e = _sourceGroup.Current; + int e = _span.Current; EcsMaskBit bit; for (int i = 0, iMax = _inc.Length; i < iMax; i++) { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index cae19ab..544f4e8 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -23,6 +23,7 @@ namespace DCFApixels.DragonECS private int _delEntBufferCount; private int _delEntBufferMinCount; private int _freeSpace; + //private bool _isEnableReleaseDelEntBuffer = true; private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); @@ -111,38 +112,75 @@ namespace DCFApixels.DragonECS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Get() where T : struct => ref WorldComponentPool.GetForWorld(id); + public ref T Get() where T : struct + { + return ref WorldComponentPool.GetForWorld(id); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T UncheckedGet() where T : struct => ref WorldComponentPool.UncheckedGetForWorld(id); + public ref T GetUnchecked() where T : struct + { + return ref WorldComponentPool.GetForWorldUnchecked(id); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T Get(int worldID) where T : struct => ref WorldComponentPool.GetForWorld(worldID); + public static ref T Get(int worldID) where T : struct + { + return ref WorldComponentPool.GetForWorld(worldID); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T UncheckedGet(int worldID) where T : struct => ref WorldComponentPool.UncheckedGetForWorld(worldID); + public static ref T GetUnchecked(int worldID) where T : struct + { + return ref WorldComponentPool.GetForWorldUnchecked(worldID); + } #endregion #region Where Query - public EcsReadonlyGroup WhereFor(EcsReadonlyGroup sourceGroup, out TAspect aspect) where TAspect : EcsAspect + public EcsReadonlyGroup WhereToGroupFor(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect { + ReleaseDelEntityBufferAll(); var executor = GetExecutor>(); aspect = executor.Aspect; - return executor.ExecuteFor(sourceGroup); + return executor.ExecuteFor(span); } - public EcsReadonlyGroup WhereFor(EcsReadonlyGroup sourceGroup) where TAspect : EcsAspect + public EcsReadonlyGroup WhereToGroupFor(EcsSpan span) where TAspect : EcsAspect { - return GetExecutor>().ExecuteFor(sourceGroup); + ReleaseDelEntityBufferAll(); + return GetExecutor>().ExecuteFor(span); } - public EcsReadonlyGroup Where(out TAspect aspect) where TAspect : EcsAspect + public EcsReadonlyGroup WhereToGroup(out TAspect aspect) where TAspect : EcsAspect { + ReleaseDelEntityBufferAll(); var executor = GetExecutor>(); aspect = executor.Aspect; return executor.Execute(); } - public EcsReadonlyGroup Where() where TAspect : EcsAspect + public EcsReadonlyGroup WhereToGroup() where TAspect : EcsAspect { + ReleaseDelEntityBufferAll(); return GetExecutor>().Execute(); } - public EcsSpan WhereSpan() where TAspect : EcsAspect + + public EcsSpan WhereFor(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect { + ReleaseDelEntityBufferAll(); + var executor = GetExecutor>(); + aspect = executor.Aspect; + return executor.ExecuteFor(span); + } + public EcsSpan WhereFor(EcsSpan span) where TAspect : EcsAspect + { + ReleaseDelEntityBufferAll(); + return GetExecutor>().ExecuteFor(span); + } + public EcsSpan Where(out TAspect aspect) where TAspect : EcsAspect + { + ReleaseDelEntityBufferAll(); + var executor = GetExecutor>(); + aspect = executor.Aspect; + return executor.Execute(); + } + public EcsSpan Where() where TAspect : EcsAspect + { + ReleaseDelEntityBufferAll(); return GetExecutor>().Execute(); } #endregion @@ -150,8 +188,8 @@ namespace DCFApixels.DragonECS #region Entity public int NewEntity() { - if (_freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount) - ReleaseDelEntityBuffer(); + //if (_isEnableReleaseDelEntBuffer && _freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount) + // ReleaseDelEntityBufferAll(); int entityID = _entityDispenser.GetFree(); _freeSpace--; @@ -177,9 +215,8 @@ namespace DCFApixels.DragonECS _gens[entityID] |= DEATH_GEN_BIT; _entitiesCount--; _entityListeners.InvokeOnDelEntity(entityID); - - if (_delEntBufferCount >= _delEntBuffer.Length) - ReleaseDelEntityBuffer(); + //if (_delEntBufferCount >= _delEntBuffer.Length) + // ReleaseDelEntityBufferAll(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe entlong GetEntityLong(int entityID) @@ -214,24 +251,15 @@ namespace DCFApixels.DragonECS } return true; } - public void ReleaseDelEntityBuffer() - { - if (_delEntBufferCount <= 0) - return; - ReadOnlySpan buffser = new ReadOnlySpan(_delEntBuffer, 0, _delEntBufferCount); - foreach (var pool in _pools) - pool.OnReleaseDelEntityBuffer(buffser); - _listeners.InvokeOnReleaseDelEntityBuffer(buffser); - for (int i = 0; i < _delEntBufferCount; i++) - _entityDispenser.Release(_delEntBuffer[i]); - _freeSpace = _entitesCapacity - _entitiesCount; - _delEntBufferCount = 0; - } + public void DeleteEmptyEntites() { foreach (var e in _allEntites) { - if (_componentCounts[e] <= 0) DelEntity(e); + if (_componentCounts[e] <= 0) + { + DelEntity(e); + } } } @@ -304,8 +332,64 @@ namespace DCFApixels.DragonECS #endregion + #region DelEntBuffer + //public AutoReleaseDelEntBufferLonkUnloker DisableAutoReleaseDelEntBuffer() + //{ + // _isEnableReleaseDelEntBuffer = false; + // return new AutoReleaseDelEntBufferLonkUnloker(this); + //} + //public void EnableAutoReleaseDelEntBuffer() + //{ + // _isEnableReleaseDelEntBuffer = true; + //} + //public readonly struct AutoReleaseDelEntBufferLonkUnloker : IDisposable + //{ + // private readonly EcsWorld _source; + // public AutoReleaseDelEntBufferLonkUnloker(EcsWorld source) + // { + // _source = source; + // } + // public void Dispose() + // { + // _source.EnableAutoReleaseDelEntBuffer(); + // } + //} + public void ReleaseDelEntityBufferAll() + { + ReleaseDelEntityBuffer(-1); + } + public void ReleaseDelEntityBuffer(int count) + { + if (_delEntBufferCount <= 0) + { + return; + } + + if (count < 0) + { + count = _delEntBufferCount; + } + else if (count > _delEntBufferCount) + { + count = _delEntBufferCount; + } + _delEntBufferCount -= count; + ReadOnlySpan buffser = new ReadOnlySpan(_delEntBuffer, _delEntBufferCount, count); + for (int i = 0; i < _poolsCount; i++) + { + _pools[i].OnReleaseDelEntityBuffer(buffser); + } + _listeners.InvokeOnReleaseDelEntityBuffer(buffser); + for (int i = 0; i < buffser.Length; i++) + { + _entityDispenser.Release(buffser[i]); + } + _freeSpace += count;// _entitesCapacity - _entitiesCount; + } + #endregion + #region Upsize - //[MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining)] private void Upsize() { Array.Resize(ref _gens, _gens.Length << 1); @@ -394,6 +478,7 @@ namespace DCFApixels.DragonECS } #endregion + #region PoolsMediator public readonly struct PoolsMediator { private readonly EcsWorld _world; @@ -409,7 +494,6 @@ namespace DCFApixels.DragonECS } _world = world; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { @@ -426,6 +510,7 @@ namespace DCFApixels.DragonECS return _world.HasEntityComponent(entityID, maskBit); } } + #endregion } #region Callbacks Interface diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index ad4dda0..dd2a53c 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -38,7 +38,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public TPool GetPoolUnchecked() where TPool : IEcsPoolImplementation, new() { - return UncheckedGet>().instance; + return GetUnchecked>().instance; } #if UNITY_2020_3_OR_NEWER [UnityEngine.Scripting.Preserve] @@ -54,7 +54,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPool UncheckedGetPool(int worldID) where TPool : IEcsPoolImplementation, new() { - return UncheckedGet>(worldID).instance; + return GetUnchecked>(worldID).instance; } #endregion diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index 558804e..fdea0e9 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -41,7 +41,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetData(int worldID) => ref WorldComponentPool.GetForWorld(worldID); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T UncheckedGetData(int worldID) => ref WorldComponentPool.UncheckedGetForWorld(worldID); + public static ref T UncheckedGetData(int worldID) => ref WorldComponentPool.GetForWorldUnchecked(worldID); private abstract class DataReleaser { @@ -57,11 +57,24 @@ namespace DCFApixels.DragonECS private static IEcsWorldComponent _interface = EcsWorldComponentHandler.instance; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T Get(int itemIndex) => ref _items[itemIndex]; + public static ref T Get(int itemIndex) + { + return ref _items[itemIndex]; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T GetForWorld(int worldID) => ref _items[GetItemIndex(worldID)]; + public static ref T GetForWorld(int worldID) + { + return ref _items[GetItemIndex(worldID)]; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T UncheckedGetForWorld(int worldID) => ref _items[_mapping[worldID]]; + public static ref T GetForWorldUnchecked(int worldID) + { +#if (DEBUG && !DISABLE_DEBUG) + if (_mapping[worldID] <= 0) + throw new Exception(); +#endif + return ref _items[_mapping[worldID]]; + } public static int GetItemIndex(int worldID) { if (_mapping.Length < Worlds.Length) diff --git a/src/Executors/EcsWhereExecutor.cs b/src/Executors/EcsWhereExecutor.cs index a1be62a..df3df19 100644 --- a/src/Executors/EcsWhereExecutor.cs +++ b/src/Executors/EcsWhereExecutor.cs @@ -42,15 +42,15 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsReadonlyGroup Execute() => ExecuteFor(_aspect.World.Entities); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsReadonlyGroup ExecuteFor(EcsReadonlyGroup sourceGroup) + public EcsReadonlyGroup ExecuteFor(EcsSpan span) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS _executeMarker.Begin(); - if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. - if (sourceGroup.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения. + if (span.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. + if (span.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения. #endif unchecked { _version++; } - _aspect.GetIteratorFor(sourceGroup).CopyTo(_filteredGroup); + _aspect.GetIteratorFor(span).CopyTo(_filteredGroup); #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS _executeMarker.End(); #endif @@ -99,15 +99,15 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsSpan Execute() => ExecuteFor(_aspect.World.Entities); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsSpan ExecuteFor(EcsReadonlyGroup sourceGroup) + public EcsSpan ExecuteFor(EcsSpan span) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS _executeMarker.Begin(); - if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. - if (sourceGroup.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения. + if (span.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. + if (span.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения. #endif unchecked { _version++; } - EcsSpan result = _aspect.GetIteratorFor(sourceGroup).CopyToSpan(ref _filteredEntities); + EcsSpan result = _aspect.GetIteratorFor(span).CopyToSpan(ref _filteredEntities); #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS _executeMarker.End(); #endif From 43d84891578a1ab021786ee32abe235f38fbfeaf Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 17:33:48 +0800 Subject: [PATCH 098/104] add Bake methods to EcsSpan --- src/Collections/EcsSpan.cs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index 5306b9e..a463302 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; @@ -62,6 +63,34 @@ namespace DCFApixels.DragonECS } #endregion + #region Methdos + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int[] Bake() + { + int[] result = new int[_values.Length]; + _values.CopyTo(result); + return result; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Bake(ref int[] entities) + { + if (entities.Length < _values.Length) + Array.Resize(ref entities, _values.Length); + int[] result = new int[_values.Length]; + _values.CopyTo(result); + return _values.Length; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Bake(List entities) + { + entities.Clear(); + foreach (var e in _values) + { + entities.Add(e); + } + } + #endregion + #region Object #pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член [Obsolete($"Equals() on {nameof(EcsSpan)} will always throw an exception. Use the equality operator instead.")] From b5300f7b43af3cff59657f7374e552af2620863f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 17:58:20 +0800 Subject: [PATCH 099/104] tmp update --- src/Builtin/InjectSystem.cs | 8 ++++---- src/Utils/ArrayUtility.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index cc5c391..b9ab4e1 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -83,17 +83,17 @@ namespace DCFApixels.DragonECS { public abstract void Inject(object obj); } - internal class EcsBaseTypeInjectRunner : EcsBaseTypeInjectRunner + internal sealed class EcsBaseTypeInjectRunner : EcsBaseTypeInjectRunner { private IEcsInject _runner; public EcsBaseTypeInjectRunner(EcsPipeline pipeline) => _runner = pipeline.GetRunner>(); - public override void Inject(object obj) => _runner.Inject((T)obj); + public sealed override void Inject(object obj) => _runner.Inject((T)obj); } - internal class EcsObjectTypePreInjectRunner : EcsBaseTypeInjectRunner + internal sealed class EcsObjectTypePreInjectRunner : EcsBaseTypeInjectRunner { private IEcsPreInject _runner; public EcsObjectTypePreInjectRunner(EcsPipeline pipeline) => _runner = pipeline.GetRunner(); - public override void Inject(object obj) => _runner.PreInject(obj); + public sealed override void Inject(object obj) => _runner.PreInject(obj); } [MetaTags(MetaTags.HIDDEN)] diff --git a/src/Utils/ArrayUtility.cs b/src/Utils/ArrayUtility.cs index b70bcc5..7ad4540 100644 --- a/src/Utils/ArrayUtility.cs +++ b/src/Utils/ArrayUtility.cs @@ -20,13 +20,13 @@ namespace DCFApixels.DragonECS.Utils internal static unsafe class UnmanagedArrayUtility { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void* New(int capacity) where T : struct + public static T* New(int capacity) where T : unmanaged { - return Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * capacity).ToPointer(); + return (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void* NewAndInit(int capacity) where T : struct + public static T* NewAndInit(int capacity) where T : unmanaged { int newSize = Marshal.SizeOf(typeof(T)) * capacity; byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); @@ -34,7 +34,7 @@ namespace DCFApixels.DragonECS.Utils for (int i = 0; i < newSize; i++) *(newPointer + i) = 0; - return newPointer; + return (T*)newPointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -44,9 +44,9 @@ namespace DCFApixels.DragonECS.Utils } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void* Resize(void* oldPointer, int newCount) where T : struct + public static T* Resize(void* oldPointer, int newCount) where T : unmanaged { - return (Marshal.ReAllocHGlobal( + return (T*)(Marshal.ReAllocHGlobal( new IntPtr(oldPointer), new IntPtr(Marshal.SizeOf(typeof(T)) * newCount))).ToPointer(); } From 0b6c1e8d268b61de4b754f41d7f63b5cacb1221f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:02:53 +0800 Subject: [PATCH 100/104] fixes --- src/Collections/EcsGroup.cs | 8 ++++---- src/EcsWorld.static.cs | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index fba66cb..6323e1c 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -107,7 +107,7 @@ namespace DCFApixels.DragonECS public bool IsSubsetOf(EcsGroup group) => _source.IsSubsetOf(group); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsSupersetOf(EcsReadonlyGroup group) => _source.Overlaps(group._source); + public bool IsSupersetOf(EcsReadonlyGroup group) => _source.IsSupersetOf(group._source); public bool IsSupersetOf(EcsGroup group) => _source.IsSupersetOf(group); #endregion @@ -648,7 +648,7 @@ namespace DCFApixels.DragonECS public Enumerator(EcsGroup group) { _dense = group._dense; - _index = (uint)(group._count > _dense.Length ? _dense.Length : group._count); + _index = (uint)(group._count > _dense.Length ? _dense.Length : group._count) + 1; } public int Current { @@ -689,7 +689,7 @@ namespace DCFApixels.DragonECS { world = group.World; _dense = group._dense; - _index = (uint)(group._count > _dense.Length ? _dense.Length : group._count); + _index = (uint)(group._count > _dense.Length ? _dense.Length : group._count) + 1; } public entlong Current { @@ -698,7 +698,7 @@ namespace DCFApixels.DragonECS } object IEnumerator.Current => Current; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() => ++_index > 0; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны + public bool MoveNext() => --_index > 0; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index fdea0e9..10c18e9 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -64,7 +64,8 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetForWorld(int worldID) { - return ref _items[GetItemIndex(worldID)]; + int index = GetItemIndex(worldID); + return ref _items[index]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetForWorldUnchecked(int worldID) @@ -78,8 +79,11 @@ namespace DCFApixels.DragonECS public static int GetItemIndex(int worldID) { if (_mapping.Length < Worlds.Length) + { Array.Resize(ref _mapping, Worlds.Length); + } + ref short itemIndex = ref _mapping[worldID]; if (itemIndex <= 0) { @@ -92,6 +96,10 @@ namespace DCFApixels.DragonECS { itemIndex = ++_count; } + if(_items.Length <= itemIndex) + { + Array.Resize(ref _items, _items.Length << 1); + } _interface.Init(ref _items[itemIndex], Worlds[worldID]); _dataReleaseres.Add(new Releaser()); } From 770c8c5522839d2b9c6062e94fe363df6526fe42 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:03:00 +0800 Subject: [PATCH 101/104] update debug --- src/Consts.cs | 1 + src/Debug/EcsDebug.cs | 5 +++++ src/EcsWorld.cs | 23 +++++++++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Consts.cs b/src/Consts.cs index 307b30a..8100e90 100644 --- a/src/Consts.cs +++ b/src/Consts.cs @@ -8,6 +8,7 @@ public const string DEBUG_PREFIX = "[DEBUG] "; public const string DEBUG_WARNING_TAG = "WARNING"; public const string DEBUG_ERROR_TAG = "ERROR"; + public const string DEBUG_PASS_TAG = "PASS"; public const string PRE_BEGIN_LAYER = nameof(PRE_BEGIN_LAYER); public const string BEGIN_LAYER = nameof(BEGIN_LAYER); diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index 9443fd9..de0dba5 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -33,6 +33,7 @@ namespace DCFApixels.DragonECS { public const string WARNING_TAG = EcsConsts.DEBUG_WARNING_TAG; public const string ERROR_TAG = EcsConsts.DEBUG_ERROR_TAG; + public const string PASS_TAG = EcsConsts.DEBUG_PASS_TAG; public static void Set() where T : DebugService, new() => DebugService.Set(); public static void Set(DebugService service) => DebugService.Set(service); @@ -40,6 +41,7 @@ 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 PrintPass(object v) => Print(EcsConsts.DEBUG_PASS_TAG, v); public static void Print() { #if !DISABLE_DRAGONECS_DEBUGGER @@ -156,6 +158,9 @@ namespace DCFApixels.DragonECS case EcsDebug.WARNING_TAG: Console.ForegroundColor = ConsoleColor.Yellow; break; + case EcsDebug.PASS_TAG: + Console.ForegroundColor = ConsoleColor.Green; + break; } Console.WriteLine($"[{tag}] {v}"); Console.ForegroundColor = color; diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 544f4e8..3dc275d 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -469,11 +469,30 @@ namespace DCFApixels.DragonECS { list.Clear(); var itemsCount = GetComponentsCount(entityID); - - for (var i = 0; i < _pools.Length; i++) + for (var i = 0; i < _poolsCount; i++) { if (_pools[i].Has(entityID)) + { + itemsCount--; list.Add(_pools[i].GetRaw(entityID)); + if (itemsCount <= 0) + break; + } + } + } + public void GetComponentTypes(int entityID, HashSet typeSet) + { + typeSet.Clear(); + var itemsCount = GetComponentsCount(entityID); + for (var i = 0; i < _poolsCount; i++) + { + if (_pools[i].Has(entityID)) + { + itemsCount--; + typeSet.Add(_pools[i].ComponentType); + if (itemsCount <= 0) + break; + } } } #endregion From 16b273d5e40601f4e992d535a3a90e0ae225925e Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:41:48 +0800 Subject: [PATCH 102/104] Update DelEntBuffer --- src/EcsWorld.cs | 85 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 3dc275d..a071784 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -23,7 +23,7 @@ namespace DCFApixels.DragonECS private int _delEntBufferCount; private int _delEntBufferMinCount; private int _freeSpace; - //private bool _isEnableReleaseDelEntBuffer = true; + private bool _isEnableReleaseDelEntBuffer = true; private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); @@ -41,6 +41,7 @@ namespace DCFApixels.DragonECS public int Capacity => _entitesCapacity; //_denseEntities.Length; public int DelEntBufferCount => _delEntBufferCount; + public bool IsEnableReleaseDelEntBuffer => _isEnableReleaseDelEntBuffer; public EcsReadonlyGroup Entities => _allEntites.Readonly; public ReadOnlySpan AllPools => _pools;// new ReadOnlySpan(pools, 0, _poolsCount); @@ -136,51 +137,75 @@ namespace DCFApixels.DragonECS #region Where Query public EcsReadonlyGroup WhereToGroupFor(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if(_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.ExecuteFor(span); } public EcsReadonlyGroup WhereToGroupFor(EcsSpan span) where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if (_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } return GetExecutor>().ExecuteFor(span); } public EcsReadonlyGroup WhereToGroup(out TAspect aspect) where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if (_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.Execute(); } public EcsReadonlyGroup WhereToGroup() where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if (_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } return GetExecutor>().Execute(); } public EcsSpan WhereFor(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if (_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.ExecuteFor(span); } public EcsSpan WhereFor(EcsSpan span) where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if (_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } return GetExecutor>().ExecuteFor(span); } public EcsSpan Where(out TAspect aspect) where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if (_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.Execute(); } public EcsSpan Where() where TAspect : EcsAspect { - ReleaseDelEntityBufferAll(); + if (_isEnableReleaseDelEntBuffer) + { + ReleaseDelEntityBufferAll(); + } return GetExecutor>().Execute(); } #endregion @@ -333,27 +358,27 @@ namespace DCFApixels.DragonECS #endregion #region DelEntBuffer - //public AutoReleaseDelEntBufferLonkUnloker DisableAutoReleaseDelEntBuffer() - //{ - // _isEnableReleaseDelEntBuffer = false; - // return new AutoReleaseDelEntBufferLonkUnloker(this); - //} - //public void EnableAutoReleaseDelEntBuffer() - //{ - // _isEnableReleaseDelEntBuffer = true; - //} - //public readonly struct AutoReleaseDelEntBufferLonkUnloker : IDisposable - //{ - // private readonly EcsWorld _source; - // public AutoReleaseDelEntBufferLonkUnloker(EcsWorld source) - // { - // _source = source; - // } - // public void Dispose() - // { - // _source.EnableAutoReleaseDelEntBuffer(); - // } - //} + public AutoReleaseDelEntBufferLonkUnloker DisableAutoReleaseDelEntBuffer() + { + _isEnableReleaseDelEntBuffer = false; + return new AutoReleaseDelEntBufferLonkUnloker(this); + } + public void EnableAutoReleaseDelEntBuffer() + { + _isEnableReleaseDelEntBuffer = true; + } + public readonly struct AutoReleaseDelEntBufferLonkUnloker : IDisposable + { + private readonly EcsWorld _source; + public AutoReleaseDelEntBufferLonkUnloker(EcsWorld source) + { + _source = source; + } + public void Dispose() + { + _source.EnableAutoReleaseDelEntBuffer(); + } + } public void ReleaseDelEntityBufferAll() { ReleaseDelEntityBuffer(-1); From e3c7aa8aac975707a70fc81b3c507ee20b3a85f2 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 23:26:56 +0800 Subject: [PATCH 103/104] update for unity --- src/Collections.meta | 8 ++++++++ src/Collections/EcsSpan.cs | 4 ++-- .../EcsSpan.cs.meta} | 2 +- src/Debug/EcsDebugUtility.cs | 4 ++-- src/Debug/Interfaces/IEcsMetaProvider.cs.meta | 11 +++++++++++ src/Debug/MetaAttributes/MetaColorAttribute.cs | 6 +++++- src/Utils/GenericEnumerable.cs.meta | 11 +++++++++++ 7 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 src/Collections.meta rename src/{Debug/Interfaces/IEcsDebugMetaProvider.cs.meta => Collections/EcsSpan.cs.meta} (83%) create mode 100644 src/Debug/Interfaces/IEcsMetaProvider.cs.meta create mode 100644 src/Utils/GenericEnumerable.cs.meta diff --git a/src/Collections.meta b/src/Collections.meta new file mode 100644 index 0000000..6d26a82 --- /dev/null +++ b/src/Collections.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2e026d1a6d4fd884ea7324b6097703c5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index a463302..d95f773 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -93,10 +93,10 @@ namespace DCFApixels.DragonECS #region Object #pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член - [Obsolete($"Equals() on {nameof(EcsSpan)} will always throw an exception. Use the equality operator instead.")] + [Obsolete("Equals() on EcsSpan will always throw an exception. Use the equality operator instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) => throw new NotSupportedException(); - [Obsolete($"GetHashCode() on {nameof(EcsSpan)} will always throw an exception.")] + [Obsolete("GetHashCode() on EcsSpan will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() => throw new NotSupportedException(); #pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член diff --git a/src/Debug/Interfaces/IEcsDebugMetaProvider.cs.meta b/src/Collections/EcsSpan.cs.meta similarity index 83% rename from src/Debug/Interfaces/IEcsDebugMetaProvider.cs.meta rename to src/Collections/EcsSpan.cs.meta index 793a2f6..0fc6d1c 100644 --- a/src/Debug/Interfaces/IEcsDebugMetaProvider.cs.meta +++ b/src/Collections/EcsSpan.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2356ad2f91cd0a84db3d572a9f3c33f5 +guid: 55c6215b2c0f45849b191532a01e1dfe MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs index d2019a2..af4b262 100644 --- a/src/Debug/EcsDebugUtility.cs +++ b/src/Debug/EcsDebugUtility.cs @@ -340,13 +340,13 @@ namespace DCFApixels.DragonECS #region ReflectionExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool TryGetCustomAttribute(this Type self, out T attribute) where T : Attribute + public static bool TryGetCustomAttribute(this Type self, out T attribute) where T : Attribute { attribute = self.GetCustomAttribute(); return attribute != null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool TryGetCustomAttribute(this MemberInfo self, out T attribute) where T : Attribute + public static bool TryGetCustomAttribute(this MemberInfo self, out T attribute) where T : Attribute { attribute = self.GetCustomAttribute(); return attribute != null; diff --git a/src/Debug/Interfaces/IEcsMetaProvider.cs.meta b/src/Debug/Interfaces/IEcsMetaProvider.cs.meta new file mode 100644 index 0000000..b99941e --- /dev/null +++ b/src/Debug/Interfaces/IEcsMetaProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5379b4d037441ed4cb4171648c1453d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Debug/MetaAttributes/MetaColorAttribute.cs b/src/Debug/MetaAttributes/MetaColorAttribute.cs index ea0bd90..3538942 100644 --- a/src/Debug/MetaAttributes/MetaColorAttribute.cs +++ b/src/Debug/MetaAttributes/MetaColorAttribute.cs @@ -10,7 +10,7 @@ namespace DCFApixels.DragonECS public byte R => color.r; public byte G => color.g; public byte B => color.b; - public float FloatT => R / (float)byte.MaxValue; + public float FloatR => R / (float)byte.MaxValue; public float FloatG => G / (float)byte.MaxValue; public float FloatB => B / (float)byte.MaxValue; public MetaColorAttribute(byte r, byte g, byte b) => color = new MetaColor(r, g, b, 255); @@ -69,6 +69,10 @@ namespace DCFApixels.DragonECS [FieldOffset(2)] public readonly byte g; [FieldOffset(1)] public readonly byte b; [FieldOffset(0)] public readonly byte a; + public float FloatR => r / (float)byte.MaxValue; + public float FloatG => g / (float)byte.MaxValue; + public float FloatB => b / (float)byte.MaxValue; + public float FloatA => a / (float)byte.MaxValue; public MetaColor(byte r, byte g, byte b) : this() { this.r = r; diff --git a/src/Utils/GenericEnumerable.cs.meta b/src/Utils/GenericEnumerable.cs.meta new file mode 100644 index 0000000..358d843 --- /dev/null +++ b/src/Utils/GenericEnumerable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38e5b41bce0941b488ae64c200dcf965 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From aacbda55d28fa9221012d6ac5a15ad681c7ec084 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 31 Dec 2023 23:56:08 +0800 Subject: [PATCH 104/104] update Readme --- README-RU.md | 76 +++++++++++++++++++++++++++++++++++++--------------- README.md | 6 ++++- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/README-RU.md b/README-RU.md index 6ac28fa..803ee99 100644 --- a/README-RU.md +++ b/README-RU.md @@ -14,11 +14,17 @@ | :--- | :--- | :--- | Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. Вднохновлен [LeoEcs](https://github.com/Leopotam/ecslite). + +> [!IMPORTANT] +> И с Новым Годом + > [!WARNING] > Проект в стадии разработки. API может меняться. > Readme еще не завершен ## Оглавление +- [DragonECS - C# Entity Component System Framework](#dragonecs---c-entity-component-system-framework) + - [Оглавление](#оглавление) - [Установка](#установка) - [Версионирование](#версионирование) - [Основные концепции](#основные-концепции) @@ -48,6 +54,7 @@ - [Профилирование](#профилирование) - [Расширения](#расширения) - [FAQ](#faq) + - ['ReadOnlySpan\<\>' could not be found](#readonlyspan-could-not-be-found) - [Обратная связь](#обратная-связь)
@@ -73,7 +80,7 @@ https://github.com/DCFApixels/DragonECS.git * `entlong` - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации; ``` csharp // Создание новой сущности в мире. -int entityID = _world.NewEmptyEntity(); +int entityID = _world.NewEntity(); // Удаление сущности. _world.DelEntity(entityID); @@ -128,16 +135,16 @@ struct PlayerTag : IEcsTagComponent {} class SomeSystem : IEcsPreInitProcess, IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess { // Будет вызван один раз в момент работы EcsPipeline.Init() и до срабатывания IEcsInitProcess.Init() - public void PreInit (EcsPipeline pipeline) { } + public void PreInit () { } // Будет вызван один раз в момент работы EcsPipeline.Init() и после срабатывания IEcsPreInitProcess.PreInit() - public void Init (EcsPipeline pipeline) { } + public void Init () { } // Будет вызван один раз в момент работы EcsPipeline.Run(). - public void Run (EcsPipeline pipeline) { } + public void Run () { } // Будет вызван один раз в момент работы EcsPipeline.Destroy() - public void Destroy (EcsPipeline pipeline) { } + public void Destroy () { } } ``` > Для реализации дополнительных процессов перейдите к разделу [Процессы](#Процессы). @@ -164,12 +171,45 @@ pipeline.Init(); // Инициализация пайплайна > Для одновременного построения и инициализации есть метод Builder.BuildAndInit(); ### Внедрение зависимостей Внедрение зависимостей - это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder. + +> [!WARNING] +> Внедрение идет параллельно с PreInit, поэтому в PreInit инъекция - не гарантируется. +> [!WARNING] +> Экземпляр EcsPipeline автоматически внедряется до еще до PreInit. ``` c# +SomeData _someData; +//... EcsPipelone pipeline = EcsPipeline.New() //... .Inject(_someData) // Внедрит в системы экземпляр _someData //... .BuildAndInit(); + +//... + +class SomeSystem : IInject, IEcsRunProcess +{ + // Для внедрения используется интерфейс IInject и его метод Inject(T obj) + SomeData _someData + public void Inject(SomeData obj) => _someData = obj; + + public void PreInit () + { + // тут возможно еще не внедрен _someData + } + public void Init () + { + // тут можно пользовать _someData + } + public void Run () + { + // тут можно пользовать _someData + } + public void Destroy () + { + // тут можно пользовать _someData + } +} ``` ### Модули Группы систем реализующие общую фичу можно объединять в модули, и просто добавлять модули в Pipeline. @@ -226,8 +266,9 @@ EcsPipelone pipeline = EcsPipeline.New()
Пользовательские процессы -Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от EcsRunner. Пример: +Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от EcsRunner. А после к интерфейсу добавте атрибут `BindWithEcsRunner` для связи. Пример: ```c# +[BindWithEcsRunner(typeof(DoSomethingProcessRunner))] interface IDoSomethingProcess : IEcsProcess { void Do(); @@ -437,14 +478,7 @@ for (int i = 0; i < group.Count; i++) //... } ``` -Так как группы это множества, они содержат операции над множествами. Каждый метод имеет 2 варианта, с записью результата в groupA, либо с возвращением новой группы: - -
- Визуализация методов - -![Визуализация методов группы](https://github.com/DCFApixels/DragonECS/assets/99481254/f2c85a9f-949c-4908-9a02-acc3c883a22b) - -
+Так как группы это множества, они содержат методы аналогичные `ISet`. Редактирующие методы имеет 2 варианта, с записью результата в groupA, либо с возвращением новой группы: ``` c# // Объединение groupA и groupB @@ -654,24 +688,24 @@ bool isCamera = _world.GetPool().Has(entity); # Debug Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. Так же многие типы имеют свой DebuggerProxy для более информативного отображения в IDE. ## Атрибуты -В чистом виде дебаг-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах. +В чистом виде мета-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах. ``` c# using DCFApixels.DragonECS; // Задает пользовательское название типа, по умолчанию используется имя типа. -[DebugName("SomeComponent")] +[MetaName("SomeComponent")] // Используется для группировки типов. -[DebugGroup("Abilities/Passive/")] +[MetaGroup("Abilities/Passive/")] // Задает цвет типа в системе rgb, где каждый канал принимает значение от 0 до 255, по умолчанию белый. -[DebugColor(DebugColor.Red)] // или [DebugColor(255, 0, 0)] +[MetaColor(MetaColor.Red)] // или [DebugColor(255, 0, 0)] // Добавляет описание типу. -[DebugDescription("The quick brown fox jumps over the lazy dog")] +[MetaDescription("The quick brown fox jumps over the lazy dog")] -// Скрывает тип. -[DebugHide] +// Добавляет строковые теги. +[MetaTags(...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе public struct Component { } ``` ## EcsDebug diff --git a/README.md b/README.md index 3f7a492..584f008 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,11 @@ | Languages: | [Русский](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md) | [English(WIP)](https://github.com/DCFApixels/DragonECS) | | :--- | :--- | :--- | -The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs](https://github.com/Leopotam/ecslite). +The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs](https://github.com/Leopotam/ecslite). + +> [!IMPORTANT] +> And a Happy New Year. + > [!WARNING] > The project is a work in progress, API may change. >