From c0c089ec0133848551821d9894a134e0261aed7c Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 7 May 2023 00:50:02 +0800 Subject: [PATCH] global update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rework queries update remove entities add sanitize checks fixes etс --- src/Builtin/BaseRunners.cs | 18 +- src/Builtin/Components.cs | 40 ++- src/Builtin/InjectSystem.cs | 16 +- src/Builtin/Queries.cs | 29 -- src/Builtin/Subjects.cs | 11 + src/Builtin/Systems.cs | 21 +- src/Builtin/WorldRunners.cs | 6 +- src/Builtin/Worlds.cs | 12 +- src/Consts.cs | 14 +- .../Attributes/DebugDescriptionAttribute.cs | 14 + src/Debug/Attributes/DebugHideAttribute.cs | 2 +- src/Debug/Attributes/DebugNameAttribute.cs | 2 +- src/EcsComponentMask.cs | 16 - src/EcsGroup.cs | 72 +++- src/EcsJoinQuery.cs | 329 ------------------ src/EcsPipeline.cs | 238 ++++++++----- src/EcsQuery.cs | 195 ----------- src/EcsRunner.cs | 12 +- src/EcsSubject.cs | 236 +++++++++++++ src/EcsWorld.cs | 278 +++++++++++---- src/Entities/EcsEntity.cs | 115 ------ src/Exceptions/EcsRelationException.cs | 2 +- .../EcsRunnerImplementationException.cs | 2 +- src/Executors/EcsJoinAttachExecutor.cs | 189 ++++++++++ src/Executors/EcsJoinHierarchyExecutor.cs | 198 +++++++++++ src/Executors/EcsQueryExecutor.cs | 7 + src/Executors/EcsWhereExecutor.cs | 72 ++++ src/Interfaces/IEcsComponentReset.cs | 42 --- src/Pools/EcsAttachPool.cs | 121 +++++-- src/Pools/EcsNotNullPool.cs | 76 ++-- src/Pools/EcsPool.cs | 144 +++++--- src/Pools/EcsPoolBase.cs | 237 +++++++++---- src/Pools/EcsReadonlyPool.cs | 139 -------- src/Pools/EcsRelationPool.cs | 154 -------- src/Pools/EcsSinglePool.cs | 108 ++++-- src/Pools/EcsTagPool.cs | 137 ++++++-- src/Utils/EcsRelationTableArchetype.cs | 16 - src/Utils/EntityLinkedList.cs | 4 +- src/entlong.cs | 169 +++++++++ 39 files changed, 2024 insertions(+), 1469 deletions(-) delete mode 100644 src/Builtin/Queries.cs create mode 100644 src/Builtin/Subjects.cs create mode 100644 src/Debug/Attributes/DebugDescriptionAttribute.cs delete mode 100644 src/EcsComponentMask.cs delete mode 100644 src/EcsJoinQuery.cs delete mode 100644 src/EcsQuery.cs create mode 100644 src/EcsSubject.cs delete mode 100644 src/Entities/EcsEntity.cs create mode 100644 src/Executors/EcsJoinAttachExecutor.cs create mode 100644 src/Executors/EcsJoinHierarchyExecutor.cs create mode 100644 src/Executors/EcsQueryExecutor.cs create mode 100644 src/Executors/EcsWhereExecutor.cs delete mode 100644 src/Interfaces/IEcsComponentReset.cs delete mode 100644 src/Pools/EcsReadonlyPool.cs delete mode 100644 src/Pools/EcsRelationPool.cs delete mode 100644 src/Utils/EcsRelationTableArchetype.cs create mode 100644 src/entlong.cs diff --git a/src/Builtin/BaseRunners.cs b/src/Builtin/BaseRunners.cs index a92fc37..e3fcf6a 100644 --- a/src/Builtin/BaseRunners.cs +++ b/src/Builtin/BaseRunners.cs @@ -2,28 +2,28 @@ namespace DCFApixels.DragonECS { - public interface IEcsPreInitSystem : IEcsSystem + public interface IEcsPreInitProcess : IEcsSystem { public void PreInit(EcsPipeline pipeline); } - public interface IEcsInitSystem : IEcsSystem + public interface IEcsInitProcess : IEcsSystem { public void Init(EcsPipeline pipeline); } - public interface IEcsRunSystem : IEcsSystem + public interface IEcsRunProcess : IEcsSystem { public void Run(EcsPipeline pipeline); } - public interface IEcsDestroySystem : IEcsSystem + public interface IEcsDestroyProcess : IEcsSystem { public void Destroy(EcsPipeline pipeline); } - public interface IEcsBaseSystem : IEcsInitSystem, IEcsRunSystem, IEcsDestroySystem { } + public interface IEcsBaseSystem : IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess { } namespace Internal { [DebugColor(DebugColor.Orange)] - public sealed class EcsPreInitRunner : EcsRunner, IEcsPreInitSystem + public sealed class EcsPreInitProcessRunner : EcsRunner, IEcsPreInitProcess { #if DEBUG && !DISABLE_DEBUG private EcsProfilerMarker[] _markers; @@ -53,7 +53,7 @@ namespace DCFApixels.DragonECS #endif } [DebugColor(DebugColor.Orange)] - public sealed class EcsInitRunner : EcsRunner, IEcsInitSystem + public sealed class EcsInitProcessRunner : EcsRunner, IEcsInitProcess { #if DEBUG && !DISABLE_DEBUG private EcsProfilerMarker[] _markers; @@ -83,7 +83,7 @@ namespace DCFApixels.DragonECS #endif } [DebugColor(DebugColor.Orange)] - public sealed class EcsRunRunner : EcsRunner, IEcsRunSystem + public sealed class EcsRunProcessRunner : EcsRunner, IEcsRunProcess { #if DEBUG && !DISABLE_DEBUG private EcsProfilerMarker[] _markers; @@ -114,7 +114,7 @@ namespace DCFApixels.DragonECS #endif } [DebugColor(DebugColor.Orange)] - public sealed class EcsDestroyRunner : EcsRunner, IEcsDestroySystem + public sealed class EcsDestroyProcessRunner : EcsRunner, IEcsDestroyProcess { #if DEBUG && !DISABLE_DEBUG private EcsProfilerMarker[] _markers; diff --git a/src/Builtin/Components.cs b/src/Builtin/Components.cs index 3a47bfb..8d609ad 100644 --- a/src/Builtin/Components.cs +++ b/src/Builtin/Components.cs @@ -1,12 +1,14 @@ -using System.Runtime.CompilerServices; +using System.ComponentModel; +using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { public struct Parent : IEcsAttachComponent { - public EcsEntity entity; + public entlong entity; - public EcsEntity Target + [EditorBrowsable(EditorBrowsableState.Never)] + public entlong Target { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => entity; @@ -14,4 +16,36 @@ namespace DCFApixels.DragonECS set => entity = value; } } + + public static class ParentUtility + { + // public static int GetRootOrSelf(this HierarchySubject s, int entityID) => s.parents.GetRootOrSelf(entityID); + public static int GetRootOrSelf(this EcsAttachPool parents, int entityID) + { + while (parents.Has(entityID) && parents.Read(entityID).entity.TryGetID(out int child)) + entityID = child; + return entityID; + } + // public static bool IsRoot(this HierarchySubject s, int entityID) => s.parents.IsRoot(entityID); + public static bool IsRoot(this EcsAttachPool parents, int entityID) + { + return !(parents.Has(entityID) && parents.Read(entityID).entity.IsAlive); + } + + // public static bool TryGetRoot(this HierarchySubject s, int entityID, out int rootEntityID) => TryGetRoot(s.parents, entityID, out rootEntityID); + public static bool TryGetRoot(this EcsAttachPool parents, EcsSubject conditionSubject, int entityID, out int rootEntityID) + { + rootEntityID = entityID; + while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child) && conditionSubject.IsMatches(child)) + rootEntityID = child; + return rootEntityID != entityID; + } + public static bool TryGetRoot(this EcsAttachPool parents, int entityID, out int rootEntityID) + { + rootEntityID = entityID; + while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child)) + rootEntityID = child; + return rootEntityID != entityID; + } + } } diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index af62917..187dde6 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS { public void Inject(T obj); } - public interface IEcsPreInitInjectCallbacks : IEcsSystem + public interface IEcsPreInitInjectProcess : IEcsSystem { public void OnPreInitInjectionBefore(); public void OnPreInitInjectionAfter(); @@ -53,7 +53,7 @@ namespace DCFApixels.DragonECS namespace Internal { [DebugHide, DebugColor(DebugColor.Gray)] - public sealed class PreInjectRunner : EcsRunner, IEcsPreInject + public sealed class EcsPreInjectRunner : EcsRunner, IEcsPreInject { void IEcsPreInject.PreInject(object obj) { @@ -61,7 +61,7 @@ namespace DCFApixels.DragonECS } } [DebugHide, DebugColor(DebugColor.Gray)] - public sealed class InjectRunner : EcsRunner>, IEcsInject + public sealed class EcsInjectRunner : EcsRunner>, IEcsInject { private IEcsPreInject _preInjectchache; void IEcsInject.Inject(T obj) @@ -75,7 +75,7 @@ namespace DCFApixels.DragonECS } } [DebugHide, DebugColor(DebugColor.Gray)] - public sealed class InjectCallbacksRunner : EcsRunner, IEcsPreInitInjectCallbacks + public sealed class EcsPreInitInjectProcessRunner : EcsRunner, IEcsPreInitInjectProcess { public void OnPreInitInjectionAfter() { @@ -91,7 +91,7 @@ namespace DCFApixels.DragonECS public class InjectSystemBase { } [DebugHide, DebugColor(DebugColor.Gray)] - public class InjectSystem : InjectSystemBase, IEcsPreInitSystem, IEcsInject, IEcsPreInitInjectCallbacks + public class InjectSystem : InjectSystemBase, IEcsPreInitProcess, IEcsInject, IEcsPreInitInjectProcess { private T _injectedData; @@ -105,12 +105,14 @@ namespace DCFApixels.DragonECS public void PreInit(EcsPipeline pipeline) { + if (_injectedData == null) + return; if (_injectController == null) { _injectController = new PreInitInjectController(pipeline); var injectMapRunner = pipeline.GetRunner>(); - pipeline.GetRunner().OnPreInitInjectionBefore(); + pipeline.GetRunner().OnPreInitInjectionBefore(); injectMapRunner.Inject(_injectController); } @@ -120,7 +122,7 @@ namespace DCFApixels.DragonECS if (_injectController.OnInject()) { _injectController.Destroy(); - var injectCallbacksRunner = pipeline.GetRunner(); + var injectCallbacksRunner = pipeline.GetRunner(); injectCallbacksRunner.OnPreInitInjectionAfter(); EcsRunner.Destroy(injectCallbacksRunner); } diff --git a/src/Builtin/Queries.cs b/src/Builtin/Queries.cs deleted file mode 100644 index 1e42e07..0000000 --- a/src/Builtin/Queries.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace DCFApixels.DragonECS -{ - - public sealed class EmptyQuery : EcsQueryBase - { - private long _whereVersion; - - public override long WhereVersion => _whereVersion; - - public EmptyQuery(Builder b) { } - - public sealed override WhereResult Where() - { - groupFilter = source.Entities.GetGroupInternal(); - return new WhereResult(this, ++_whereVersion); - } - - protected sealed override void OnBuild(Builder b) { } - } - public static partial class EcsWorldExtensions - { - public static WhereResult WhereAll(this EcsWorld self) => self.Select().Where(); - } - - public sealed class HierarchyQuery : EcsJoinAttachQuery - { - public HierarchyQuery(Builder b) { } - } -} diff --git a/src/Builtin/Subjects.cs b/src/Builtin/Subjects.cs new file mode 100644 index 0000000..8c3b369 --- /dev/null +++ b/src/Builtin/Subjects.cs @@ -0,0 +1,11 @@ +namespace DCFApixels.DragonECS +{ + public sealed class HierarchySubject : EcsSubject + { + public readonly EcsAttachPool parents; + public HierarchySubject(Builder b) + { + parents = b.Include(); + } + } +} diff --git a/src/Builtin/Systems.cs b/src/Builtin/Systems.cs index c2a65db..a61f702 100644 --- a/src/Builtin/Systems.cs +++ b/src/Builtin/Systems.cs @@ -2,7 +2,7 @@ namespace DCFApixels.DragonECS { - [DebugColor(DebugColor.Black)] + [DebugHide, DebugColor(DebugColor.Black)] public class SystemsBlockMarkerSystem : IEcsSystem { public readonly string name; @@ -13,7 +13,7 @@ namespace DCFApixels.DragonECS } } [DebugHide, DebugColor(DebugColor.Grey)] - public class DeleteEmptyEntitesSsytem : IEcsRunSystem, IEcsPreInject + public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsPreInject { private List _worlds = new List(); public void PreInject(object obj) @@ -30,42 +30,45 @@ namespace DCFApixels.DragonECS } [DebugHide, DebugColor(DebugColor.Grey)] - public class DeleteOneFrameComponentSystem : IEcsRunSystem, IEcsInject + public class DeleteOneFrameComponentSystem : IEcsRunProcess, IEcsInject where TWorld : EcsWorld where TComponent : struct, IEcsComponent { private TWorld _world; public void Inject(TWorld obj) => _world = obj; - private sealed class Query : EcsQuery + private sealed class Subject : EcsSubject { public EcsPool pool; - public Query(Builder b) + public Subject(Builder b) { pool = b.Include(); } } public void Run(EcsPipeline pipeline) { - foreach (var e in _world.Where(out Query q)) - q.pool.Del(e); + foreach (var e in _world.Where(out Subject s)) + s.pool.Del(e); } } public static class DeleteOneFrameComponentSystemExt { + private const string AUTO_DEL_LAYER = nameof(AUTO_DEL_LAYER); public static EcsPipeline.Builder AutoDel(this EcsPipeline.Builder b) where TWorld : EcsWorld where TComponent : struct, IEcsComponent { - b.Add(new DeleteOneFrameComponentSystem()); + b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_LAYER); + b.AddUnique(new DeleteOneFrameComponentSystem(), AUTO_DEL_LAYER); return b; } /// for EcsDefaultWorld public static EcsPipeline.Builder AutoDel(this EcsPipeline.Builder b) where TComponent : struct, IEcsComponent { - b.Add(new DeleteOneFrameComponentSystem()); + b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_LAYER); + b.AddUnique(new DeleteOneFrameComponentSystem(), AUTO_DEL_LAYER); return b; } } diff --git a/src/Builtin/WorldRunners.cs b/src/Builtin/WorldRunners.cs index 07c9b88..2123165 100644 --- a/src/Builtin/WorldRunners.cs +++ b/src/Builtin/WorldRunners.cs @@ -46,7 +46,7 @@ namespace DCFApixels.DragonECS public interface IEcsEntityCreate : IEcsSystem { - public void OnEntityCreate(EcsEntity entity); + public void OnEntityCreate(int entityID); } public interface IEcsEntityDestroy : IEcsSystem { @@ -59,9 +59,9 @@ namespace DCFApixels.DragonECS [DebugColor(DebugColor.Orange)] public sealed class EcsEntityCreateRunner : EcsRunner, IEcsEntityCreate { - public void OnEntityCreate(EcsEntity entity) + public void OnEntityCreate(int entityID) { - foreach (var item in targets) item.OnEntityCreate(entity); + foreach (var item in targets) item.OnEntityCreate(entityID); } } [DebugColor(DebugColor.Orange)] diff --git a/src/Builtin/Worlds.cs b/src/Builtin/Worlds.cs index ae56da8..02bc174 100644 --- a/src/Builtin/Worlds.cs +++ b/src/Builtin/Worlds.cs @@ -1,17 +1,15 @@ namespace DCFApixels.DragonECS { - //TODO использовать этот мир для холостых вызовов. чтоб все нулевые ентити стучались в него, так можно попробовать сократить число проверок - //internal sealed class EcsDeathWrold : EcsWorld - //{ - // public EcsDeathWrold(EcsPipeline pipeline) : base(pipeline) { } - //} - - public sealed class EcsDefaultWorld : EcsWorld { public EcsDefaultWorld() : base(null) { } public EcsDefaultWorld(EcsPipeline pipeline) : base(pipeline) { } } + public sealed class EcsPhysicsEventsWorld : EcsWorld + { + public EcsPhysicsEventsWorld() : base(null) { } + public EcsPhysicsEventsWorld(EcsPipeline pipeline) : base(pipeline) { } + } public sealed class EcsEventWorld : EcsWorld { public EcsEventWorld() : base(null) { } diff --git a/src/Consts.cs b/src/Consts.cs index d229921..d7c2323 100644 --- a/src/Consts.cs +++ b/src/Consts.cs @@ -2,16 +2,18 @@ { public class EcsConsts { - public const string EXCEPTION_MESSAGE_PREFIX = "[DragonECS] "; + public const string FRAMEWORK_NAME = "DragonECS"; + + public const string EXCEPTION_MESSAGE_PREFIX = "["+ FRAMEWORK_NAME + "] "; public const string DEBUG_PREFIX = "[DEBUG] "; public const string DEBUG_WARNING_TAG = "WARNING"; public const string DEBUG_ERROR_TAG = "ERROR"; - public const string PRE_BEGIN_SYSTEMS_BLOCK = nameof(PRE_BEGIN_SYSTEMS_BLOCK); - public const string BEGIN_SYSTEMS_BLOCK = nameof(BEGIN_SYSTEMS_BLOCK); - public const string BASIC_SYSTEMS_BLOCK = nameof(BASIC_SYSTEMS_BLOCK); - public const string END_SYSTEMS_BLOCK = nameof(END_SYSTEMS_BLOCK); - public const string POST_END_SYSTEMS_BLOCK = nameof(POST_END_SYSTEMS_BLOCK); + public const string PRE_BEGIN_LAYER = nameof(PRE_BEGIN_LAYER); + public const string BEGIN_LAYER = nameof(BEGIN_LAYER); + 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); } } diff --git a/src/Debug/Attributes/DebugDescriptionAttribute.cs b/src/Debug/Attributes/DebugDescriptionAttribute.cs new file mode 100644 index 0000000..88a50c5 --- /dev/null +++ b/src/Debug/Attributes/DebugDescriptionAttribute.cs @@ -0,0 +1,14 @@ +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 index 1d98e62..38d7419 100644 --- a/src/Debug/Attributes/DebugHideAttribute.cs +++ b/src/Debug/Attributes/DebugHideAttribute.cs @@ -2,6 +2,6 @@ namespace DCFApixels.DragonECS { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)] + [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 index c0584fc..30c4d57 100644 --- a/src/Debug/Attributes/DebugNameAttribute.cs +++ b/src/Debug/Attributes/DebugNameAttribute.cs @@ -2,7 +2,7 @@ namespace DCFApixels.DragonECS { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] public sealed class DebugNameAttribute : Attribute { public readonly string name; diff --git a/src/EcsComponentMask.cs b/src/EcsComponentMask.cs deleted file mode 100644 index 71ce604..0000000 --- a/src/EcsComponentMask.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace DCFApixels.DragonECS -{ - public class EcsComponentMask - { - internal Type WorldArchetype; - internal int[] Inc; - internal int[] Exc; - public override string ToString() - { - return $"Inc({string.Join(", ", Inc)}) Exc({string.Join(", ", Exc)})"; - } - } - -} diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 5b6c778..96d7d71 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -6,6 +6,10 @@ using delayedOp = System.Int32; namespace DCFApixels.DragonECS { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + using static EcsGroup.ThrowHalper; +#endif + [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)] public readonly ref struct EcsReadonlyGroup { @@ -17,6 +21,7 @@ namespace DCFApixels.DragonECS #endregion #region Properties + public bool IsNull => _source == null; public EcsWorld World { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -42,15 +47,24 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _source.IsReleazed; } + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source[index]; + } #endregion #region Methods [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Contains(int entityID) => _source.Has(entityID); + public bool Has(int entityID) => _source.Has(entityID); [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsGroup Clone() => _source.Clone(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int First() => _source.First(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Last() => _source.Last(); #endregion #region Object @@ -98,7 +112,7 @@ namespace DCFApixels.DragonECS private int _lockCount; - private bool _isReleazed = true; + private bool _isReleazed = true; #region Properties public EcsWorld World => _source; @@ -127,6 +141,17 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _isReleazed; } + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (index < 0 || index >= Count) ThrowArgumentOutOfRange(); +#endif + return _dense[index]; + } + } #endregion #region Constrcutors/Finalizer @@ -196,6 +221,9 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void AggressiveAdd(int entityID) { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (Has(entityID)) ThrowAlreadyContains(entityID); +#endif if (++_count >= _dense.Length) Array.Resize(ref _dense, _dense.Length << 1); _dense[_count] = entityID; @@ -221,6 +249,9 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void AggressiveRemove(int entityID) { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowDoesNotContain(entityID); +#endif _dense[_sparse[entityID]] = _dense[_count]; _sparse[_dense[_count--]] = _sparse[entityID]; _sparse[entityID] = 0; @@ -247,18 +278,7 @@ namespace DCFApixels.DragonECS #endregion #region Sort/Clear - public void Sort() - { - int increment = 1; - for (int i = 0; i < _dense.Length; i++) - { - if (_sparse[i] > 0) - { - _sparse[i] = increment; - _dense[increment++] = i; - } - } - } + //public void Sort() { } //TODO прошлай реализация сортировки не удачная, так как в dense могут храниться занчения больше чем dense.Length public void Clear() { _count = 0; @@ -398,7 +418,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Unlock() { -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (_lockCount <= 0) { throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}."); @@ -481,6 +501,14 @@ namespace DCFApixels.DragonECS } #endregion + #region OtherMethods + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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) @@ -514,6 +542,20 @@ namespace DCFApixels.DragonECS _source.ReleaseGroup(this); } #endregion + + #region ThrowHalper +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + internal static class ThrowHalper + { + [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}."); + } +#endif + #endregion } public static class EcsGroupExtensions diff --git a/src/EcsJoinQuery.cs b/src/EcsJoinQuery.cs deleted file mode 100644 index 4e07d2e..0000000 --- a/src/EcsJoinQuery.cs +++ /dev/null @@ -1,329 +0,0 @@ -using System; -using System.Xml.Schema; -using Unity.Profiling; -using UnityEditor.Search; -using UnityEngine; - -namespace DCFApixels.DragonECS -{ - public abstract class EcsJoinAttachQueryBase : EcsQueryBase - { - public abstract void Join(WhereResult targetWorldWhereQuery); - } - public abstract class EcsJoinAttachQuery : EcsJoinAttachQueryBase - where TAttachComponent : struct, IEcsAttachComponent - { - private EcsWorld _targetWorld; - private EcsAttachPool _targetPool; - - private int _targetWorldCapacity = -1; - private int _targetPoolCapacity = -1; - - private int[] _mapping; - private int[] _counts; - private EntityLinkedList _linkedBasket; - - private bool _isInitTargetWorld = false; - - private long _executeWhereVersion = 0; - private long _executeJoinVersion = 0; - - private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where"); - private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join"); - - #region Properties - public EcsWorld AttachWorld => _targetWorld; - public EcsAttachPool Attach => _targetPool; - - public sealed override long WhereVersion => _executeWhereVersion; - public long JoinVersion => _executeJoinVersion; - #endregion - - protected sealed override void OnBuild(Builder b) - { - _targetPool = b.Include(); - } - public sealed override WhereResult Where() - { - using (_executeWhere.Auto()) - { - _executeWhereVersion++; - ExecuteWhere(_targetPool.Entities, groupFilter); - return new WhereResult(this, WhereVersion); - } - } - - public sealed override void Join(WhereResult targetWorldWhereQuery) - { - _executeJoin.Begin(); -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (targetWorldWhereQuery.IsNull) - throw new ArgumentNullException();//TODO составить текст исключения. -#endif - if (!_isInitTargetWorld) - InitTargetWorlds(targetWorldWhereQuery.World); -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - else - if (_targetWorld != targetWorldWhereQuery.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир -#endif - - //Подготовка массивов - if (_targetWorldCapacity < _targetWorld.Capacity) - { - _targetWorldCapacity = _targetWorld.Capacity; - _mapping = new int[_targetWorldCapacity]; - _counts = new int[_targetWorldCapacity]; - } - else - { - ArrayUtility.Fill(_counts, 0); - ArrayUtility.Fill(_mapping, 0); - } - if (_targetPoolCapacity < _targetPool.Capacity) - { - _targetPoolCapacity = _targetPool.Capacity; - _linkedBasket.Resize(_targetPoolCapacity); - } - _linkedBasket.Clear(); - //Конец подготовки массивов - - Where(); - foreach (var attachID in groupFilter) - { - EcsEntity attachTarget = _targetPool.Read(attachID).Target; - if (!attachTarget.IsAlive) - { - //_targetPool.Del(attachID); - continue; - } - int attachTargetID = attachTarget.id; - - ref int nodeIndex = ref _mapping[attachTargetID]; - if (nodeIndex <= 0) - nodeIndex = _linkedBasket.Add(attachID); - else - _linkedBasket.Insert(nodeIndex, attachID); - _counts[attachTargetID]++; - } - - _executeJoinVersion++; - _executeJoin.End(); - } - private void InitTargetWorlds(EcsWorld targetWorld) - { - _targetWorld = targetWorld; - - _targetWorldCapacity = _targetWorld.Capacity; - _mapping = new int[_targetWorldCapacity]; - _counts = new int[_targetWorldCapacity]; - - _targetPoolCapacity = _targetPool.Capacity; - _linkedBasket = new EntityLinkedList(_targetPoolCapacity); - _isInitTargetWorld = true; - } - - public bool Has(int attachedEnttiyID) => groupFilter.Has(attachedEnttiyID); - public EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]); - public int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0; - public int GetNodesCount(int entityID) => _counts[entityID]; - public bool HasNode(int entityID, int attachedEntityID) => groupFilter.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID; - } - /* - public abstract class EcsJoinRelationQuery : EcsQueryBase - where TRelationComponent : struct, IEcsRelationComponent - { - private EcsWorld _firstWorld; - private EcsWorld _secondWorld; - private EcsRelationPool _targetPool; - - internal int _targetPoolCapacity = -1; - - private bool _isInitTargetWorlds = false; - private bool _isJoinExecuted = false; - - private long _executeWhereVersion = 0; - private long _executeJoinVersion = 0; - - public readonly Orientation ToSecond = new Orientation(); - public readonly Orientation ToFirst = new Orientation(); - - private ProfilerMarker _executeWhere = new ProfilerMarker("JoinRelationQuery.Where"); - private ProfilerMarker _executeJoin = new ProfilerMarker("JoinRelationQuery.Join"); - - #region Properties - public EcsWorld RelationFirstWorld => _firstWorld; - public EcsWorld RelationSecondWorld => _secondWorld; - public EcsRelationPool Relation => _targetPool; - public bool IsMonoWorldRelation => _firstWorld == _secondWorld; - - public sealed override long WhereVersion => _executeWhereVersion; - public long JoinVersion => _executeJoinVersion; - #endregion - - protected sealed override void OnBuild(Builder b) - { - _targetPool = b.Include(); - } - public sealed override WhereResult Where() - { - using (_executeWhere.Auto()) - { - _executeWhereVersion++; - ExecuteWhere(_targetPool.Entities, groupFilter); - return new WhereResult(this, WhereVersion); - } - } - - //TODO - // реализовать возможность получить список всех связей между двумя сущьностями одной напрваленности, и сделать метод для получения одной такой связи - // - - public void Join(WhereResult firstWorldWhereQuery, WhereResult secondWorldWhereQuery) - { - _executeJoin.Begin(); - _isJoinExecuted = false; - if (_isInitTargetWorlds == false) - { - InitTargetWorlds(); - if (_isInitTargetWorlds == false) - return; - }; - - // //Подготовка массивов - // if (_targetWorldCapacity < _targetWorld.Capacity) - // { - // _targetWorldCapacity = _targetWorld.Capacity; - // _mapping = new int[_targetWorldCapacity]; - // _counts = new int[_targetWorldCapacity]; - // } - // else - // { - // ArrayUtility.Fill(_counts, 0); - // ArrayUtility.Fill(_mapping, 0); - // } - // if (_targetPoolCapacity < _targetPool.Capacity) - // { - // _targetPoolCapacity = _targetPool.Capacity; - // _linkedBasket.Resize(_targetPoolCapacity); - // } - // _linkedBasket.Clear(); - // //Конец подготовки массивов - - Where(); - foreach (var attachID in groupFilter) - { - EcsEntity attachTarget = _targetPool.Read(attachID).First; - if (!attachTarget.IsAlive) - { - //_targetPool.Del(attachID); - continue; - } - int attachTargetID = attachTarget.id; - - ref int nodeIndex = ref _mapping[attachTargetID]; - if (nodeIndex <= 0) - nodeIndex = _linkedBasket.Add(attachID); - else - _linkedBasket.Insert(nodeIndex, attachID); - _counts[attachTargetID]++; - } - - _isJoinExecuted = true; - _executeJoinVersion++; - _executeJoin.End(); - - _executeJoinVersion++; - _isJoinExecuted = true; - _executeJoin.End(); - } - - private void InitTargetWorlds() - { - foreach (var e in groupFilter) - { - ref readonly var rel = ref _targetPool.Read(e); - if (rel.First.IsNotNull) - _firstWorld = EcsWorld.Worlds[rel.First.world]; - if (rel.Second.IsNotNull) - _secondWorld = EcsWorld.Worlds[rel.Second.world]; - if (_firstWorld != null && _secondWorld != null) - { - _isInitTargetWorlds = true; - break; - } - } - - if (_isInitTargetWorlds) - { - _targetWorldCapacity = _targetWorld.Capacity; - _mapping = new int[_targetWorldCapacity]; - _counts = new int[_targetWorldCapacity]; - - _targetPoolCapacity = _targetPool.Capacity; - _linkedBasket = new EntityLinkedList(_targetPoolCapacity); - } - } - - - public bool Has(int relationEntityID) => groupFilter.Has(relationEntityID); - - - public class Orientation - { - internal int targetWorldCapacity = -1; - - internal int[] mapping; - internal int[] counts; - internal EntityLinkedList linkedBasket; - - public bool HasRelation(int fromEntityID, int toEntityID) - { - throw new NotImplementedException(); - } - public bool HasNode(int fromEntityID, int toEntityID) - { - throw new NotImplementedException(); - } - public EntityLinkedList.EnumerableSpan GetRelations(int fromEntityID) => linkedBasket.Span(mapping[fromEntityID], counts[fromEntityID]); - public int GetRelation(int fromEntityID) => counts[fromEntityID] > 0 ? linkedBasket.Get(mapping[fromEntityID]) : 0; - public int GetRelationsCount(int fromEntityID) => counts[fromEntityID]; - } - }*/ - - #region Extensions - public static class EcsJoinQueryBaseExtensions - { - public static void Join(this EcsJoinAttachQueryBase self, EcsWorld targetWorld) - { - self.Join(targetWorld.Where()); - } - - public static TQuery Join(this EcsWorld self, EcsWorld targetWorld, out TQuery query) where TQuery : EcsJoinAttachQueryBase - { - return self.Join(targetWorld.WhereAll(), out query); - } - public static TQuery Join(this EcsWorld self, EcsWorld targetWorld) where TQuery : EcsJoinAttachQueryBase - { - return self.Join(targetWorld.WhereAll()); - } - /* public static class EcsJoinRelationQueryExtensions - { - public static void Join(this EcsJoinRelationQuery self, EcsWorld firstWorld, EcsWorld secondWorld) - where TRelationComponent : struct, IEcsRelationComponent - { - self.Join(firstWorld.Where(), secondWorld.Where()); - } - public static void Join(this EcsJoinRelationQuery self, EcsWorld firstWorld, WhereResult secondWorldWhereQuery) - where TRelationComponent : struct, IEcsRelationComponent - { - self.Join(firstWorld.Where(), secondWorldWhereQuery); - } - public static void Join(this EcsJoinRelationQuery self, WhereResult firstWorldWhereQuery, EcsWorld secondWorld) - where TRelationComponent : struct, IEcsRelationComponent - { - self.Join(firstWorldWhereQuery, secondWorld.Where()); - } - }*/ - } - #endregion -} diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 804f0b2..3e5fb67 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -1,7 +1,9 @@ using DCFApixels.DragonECS.RunnersCore; using System; +using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS @@ -25,7 +27,7 @@ namespace DCFApixels.DragonECS private IEcsSystem[] _allSystems; private Dictionary _runners; - private IEcsRunSystem _runRunnerCache; + private IEcsRunProcess _runRunnerCache; private ReadOnlyCollection _allSystemsSealed; private ReadOnlyDictionary _allRunnersSealed; @@ -89,20 +91,20 @@ namespace DCFApixels.DragonECS var ecsPipelineInjectRunner = GetRunner>(); ecsPipelineInjectRunner.Inject(this); EcsRunner.Destroy(ecsPipelineInjectRunner); - var preInitRunner = GetRunner(); + var preInitRunner = GetRunner(); preInitRunner.PreInit(this); EcsRunner.Destroy(preInitRunner); - var initRunner = GetRunner(); + var initRunner = GetRunner(); initRunner.Init(this); EcsRunner.Destroy(initRunner); - _runRunnerCache = GetRunner(); + _runRunnerCache = GetRunner(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Run() { -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS CheckBeforeInitForMethod(nameof(Run)); CheckAfterDestroyForMethod(nameof(Run)); #endif @@ -113,7 +115,7 @@ namespace DCFApixels.DragonECS if (_isEmptyDummy) return; -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS CheckBeforeInitForMethod(nameof(Run)); #endif if (_isDestoryed == true) @@ -122,12 +124,12 @@ namespace DCFApixels.DragonECS return; } _isDestoryed = true; - GetRunner().Destroy(this); + GetRunner().Destroy(this); } #endregion #region StateChecks -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS private void CheckBeforeInitForMethod(string methodName) { if (!_isInit) @@ -155,48 +157,51 @@ namespace DCFApixels.DragonECS { private const int KEYS_CAPACITY = 4; private HashSet _uniqueTypes; - private readonly List _blockExecutionOrder; - private readonly Dictionary> _systems; - private readonly string _basicBlocKey; - private bool _isBasicBlockDeclared; - private bool _isOnlyBasicBlock; + private readonly Dictionary> _systems; + private readonly string _basicLayer; + + public readonly LayerList Layers; public Builder() { - _basicBlocKey = EcsConsts.BASIC_SYSTEMS_BLOCK; + _basicLayer = EcsConsts.BASIC_LAYER; + + Layers = new LayerList(this, _basicLayer); + + Layers.Insert(EcsConsts.BASIC_LAYER, EcsConsts.PRE_BEGIN_LAYER, EcsConsts.BEGIN_LAYER); + Layers.InsertAfter(EcsConsts.BASIC_LAYER, EcsConsts.END_LAYER, EcsConsts.POST_END_LAYER); + _uniqueTypes = new HashSet(); - _blockExecutionOrder = new List(KEYS_CAPACITY); - _systems = new Dictionary>(KEYS_CAPACITY); - _isBasicBlockDeclared = false; - _isOnlyBasicBlock = true; - - SystemsBlock(EcsConsts.PRE_BEGIN_SYSTEMS_BLOCK); - SystemsBlock(EcsConsts.BEGIN_SYSTEMS_BLOCK); + _systems = new Dictionary>(KEYS_CAPACITY); } - public Builder Add(IEcsSystem system, object blockKey = null) + public Builder Add(IEcsSystem system, string layerName = null) { - AddInternal(system, blockKey, false); + AddInternal(system, layerName, false); return this; } - public Builder AddUnique(IEcsSystem system, object blockKey = null) + public Builder AddUnique(IEcsSystem system, string layerName = null) { - AddInternal(system, blockKey, true); + AddInternal(system, layerName, true); return this; } - - private void AddInternal(IEcsSystem system, object blockKey, bool isUnique) + private void AddInternal(IEcsSystem system, string layerName, bool isUnique) { - if (blockKey == null) blockKey = _basicBlocKey; + if (layerName == null) layerName = _basicLayer; List list; - if (!_systems.TryGetValue(blockKey, out list)) + if (!_systems.TryGetValue(layerName, out list)) { - list = new List(); - list.Add(new SystemsBlockMarkerSystem(blockKey.ToString())); - _systems.Add(blockKey, list); + list = new List + { + new SystemsBlockMarkerSystem(layerName.ToString()) + }; + _systems.Add(layerName, list); } if ((_uniqueTypes.Add(system.GetType()) == false && isUnique)) return; list.Add(system); + + if (system is IEcsModule module)//если система одновременно явялется и системой и модулем то за один Add будет вызван Add и AddModule + AddModule(module); } public Builder AddModule(IEcsModule module) @@ -205,70 +210,133 @@ namespace DCFApixels.DragonECS return this; } - public Builder BasicSystemsBlock() - { - _isBasicBlockDeclared = true; - _blockExecutionOrder.Add(_basicBlocKey); - return this; - } - public Builder SystemsBlock(string blockKey) - { - if (blockKey == null || blockKey == _basicBlocKey) - return BasicSystemsBlock(); - - _isOnlyBasicBlock = false; - _blockExecutionOrder.Add(blockKey); - return this; - } - public Builder InsertSystemsBlock(string blockKey, string beforeBlockKey) - { - if (blockKey == null || blockKey == _basicBlocKey) - { - _isBasicBlockDeclared = true; - blockKey = _basicBlocKey; - } - - _isOnlyBasicBlock = false; - int index = _blockExecutionOrder.IndexOf(beforeBlockKey); - index = index < 0 ? _blockExecutionOrder.Count - 1 : index; - _blockExecutionOrder.Insert(index, blockKey); - return this; - } - public EcsPipeline Build() { - if (_isOnlyBasicBlock) - { - return new EcsPipeline(_systems[_basicBlocKey].ToArray()); - } + //EcsDebug.Print(string.Join(", ", Layers)); - if(_isBasicBlockDeclared == false) - _blockExecutionOrder.Insert(_blockExecutionOrder.IndexOf(EcsConsts.BEGIN_SYSTEMS_BLOCK) + 1, _basicBlocKey);//вставить после BEGIN_SYSTEMS_BLOCK - SystemsBlock(EcsConsts.END_SYSTEMS_BLOCK); - SystemsBlock(EcsConsts.POST_END_SYSTEMS_BLOCK); - Add(new DeleteEmptyEntitesSsytem(), EcsConsts.POST_END_SYSTEMS_BLOCK); + Add(new DeleteEmptyEntitesSystem(), EcsConsts.POST_END_LAYER); List result = new List(32); - - List basicBlockList = _systems[_basicBlocKey]; + List basicBlockList = _systems[_basicLayer]; foreach (var item in _systems) { - if (!_blockExecutionOrder.Contains(item.Key)) - { + if (!Layers.Has(item.Key)) basicBlockList.AddRange(item.Value); - } } - foreach (var item in _blockExecutionOrder) + foreach (var item in Layers) { if(_systems.TryGetValue(item, out var list)) - { result.AddRange(list); - } } return new EcsPipeline(result.ToArray()); } + + public class LayerList : IEnumerable + { + private const string ADD_LAYER = nameof(ADD_LAYER); // автоматический слой нужный только для метода Add + + private Builder _source; + private List _layers; + + private string _basicLayerName; + + public LayerList(Builder source, string basicLayerName) + { + _source = source; + _layers = new List(16) { basicLayerName, ADD_LAYER }; + _basicLayerName = basicLayerName; + } + + public Builder Add(string newLayer) => Insert(ADD_LAYER, newLayer); + public Builder Insert(string targetLayer, string newLayer) + { + if (Has(newLayer)) return _source; + + int index = _layers.IndexOf(targetLayer); + if (index < 0) + throw new KeyNotFoundException($"Layer {targetLayer} not found"); + _layers.Insert(index, newLayer); + return _source; + } + public Builder InsertAfter(string targetLayer, string newLayer) + { + if (Has(newLayer)) return _source; + + if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER + targetLayer = ADD_LAYER; + + int index = _layers.IndexOf(targetLayer); + if (index < 0) + throw new KeyNotFoundException($"Layer {targetLayer} not found"); + + if (++index >= _layers.Count) + _layers.Add(newLayer); + else + _layers.Insert(index, newLayer); + return _source; + } + public Builder Move(string targetLayer, string movingLayer) + { + _layers.Remove(movingLayer); + return Insert(targetLayer, movingLayer); + } + public Builder MoveAfter(string targetLayer, string movingLayer) + { + if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER + targetLayer = ADD_LAYER; + + _layers.Remove(movingLayer); + return InsertAfter(targetLayer, movingLayer); + } + + public Builder Add(params string[] newLayers) => Insert(ADD_LAYER, newLayers); + public Builder Insert(string targetLayer, params string[] newLayers) + { + int index = _layers.IndexOf(targetLayer); + if (index < 0) + throw new KeyNotFoundException($"Layer {targetLayer} not found"); + _layers.InsertRange(index, newLayers.Where(o => !Has(o))); + return _source; + } + public Builder InsertAfter(string targetLayer, params string[] newLayers) + { + int index = _layers.IndexOf(targetLayer); + if (index < 0) + throw new KeyNotFoundException($"Layer {targetLayer} not found"); + + if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER + targetLayer = ADD_LAYER; + + if (++index >= _layers.Count) + _layers.AddRange(newLayers.Where(o => !Has(o))); + else + _layers.InsertRange(index, newLayers.Where(o => !Has(o))); + return _source; + } + public Builder Move(string targetLayer, params string[] movingLayers) + { + foreach (var movingLayer in movingLayers) + _layers.Remove(movingLayer); + return Insert(targetLayer, movingLayers); + } + public Builder MoveAfter(string targetLayer, params string[] movingLayers) + { + if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER + targetLayer = ADD_LAYER; + + foreach (var movingLayer in movingLayers) + _layers.Remove(movingLayer); + return InsertAfter(targetLayer, movingLayers); + } + + public bool Has(string layer) => _layers.Contains(layer); + + public List.Enumerator GetEnumerator() => _layers.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _layers.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _layers.GetEnumerator(); + } } #endregion } @@ -281,27 +349,23 @@ namespace DCFApixels.DragonECS #region Extensions public static class EcsPipelineExtensions { - public static void GetRunner(this EcsPipeline self, out T runner) where T : IEcsSystem - { - runner = self.GetRunner(); - } public static bool IsNullOrDestroyed(this EcsPipeline self) { return self == null || self.IsDestoryed; } - public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable range, object blockKey = null) + public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable range, string layerName = null) { foreach (var item in range) { - self.Add(item, blockKey); + self.Add(item, layerName); } return self; } - public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable range, object blockKey = null) + public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable range, string layerName = null) { foreach (var item in range) { - self.AddUnique(item, blockKey); + self.AddUnique(item, layerName); } return self; } diff --git a/src/EcsQuery.cs b/src/EcsQuery.cs deleted file mode 100644 index 5fef1bd..0000000 --- a/src/EcsQuery.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Unity.Profiling; - -namespace DCFApixels.DragonECS -{ - public abstract class EcsQueryBase - { - internal EcsWorld source; - internal EcsGroup groupFilter; - internal EcsQueryMask mask; - - private bool _isInit; - - #region Properties - public EcsQueryMask Mask => mask; - public EcsWorld World => source; - public bool IsInit => _isInit; - - public abstract long WhereVersion { get; } - #endregion - - #region Builder - protected virtual void Init(Builder b) { } - protected abstract void OnBuild(Builder b); - public sealed class Builder : EcsQueryBuilderBase - { - private EcsWorld _world; - private List _inc; - private List _exc; - - public EcsWorld World => _world; - - private Builder(EcsWorld world) - { - _world = world; - _inc = new List(8); - _exc = new List(4); - } - internal static TQuery Build(EcsWorld world) where TQuery : EcsQueryBase - { - Builder builder = new Builder(world); - Type queryType = typeof(TQuery); - ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null); - EcsQueryBase newQuery; - if (constructorInfo != null) - { - newQuery = (EcsQueryBase)constructorInfo.Invoke(new object[] { builder }); - } - else - { - newQuery = (EcsQueryBase)Activator.CreateInstance(typeof(TQuery)); - newQuery.Init(builder); - } - newQuery.groupFilter = EcsGroup.New(world); - newQuery.source = world; - newQuery.OnBuild(builder); - builder.End(out newQuery.mask); - newQuery._isInit = true; - return (TQuery)(object)newQuery; - } - - public sealed override TPool Include() - { - _inc.Add(_world.GetComponentID()); - return _world.GetPool(); - } - public sealed override TPool Exclude() - { - _exc.Add(_world.GetComponentID()); - return _world.GetPool(); - } - public sealed override TPool Optional() - { - return _world.GetPool(); - } - - private void End(out EcsQueryMask mask) - { - _inc.Sort(); - _exc.Sort(); - mask = new EcsQueryMask(_world.Archetype, _inc.ToArray(), _exc.ToArray()); - _world = null; - _inc = null; - _exc = null; - } - } - #endregion - public abstract WhereResult Where(); - - protected void ExecuteWhere(EcsReadonlyGroup group, EcsGroup result) - { - var pools = World.pools; - result.Clear(); - foreach (var e in group) - { - for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++) - { - if (!pools[mask.Inc[i]].Has(e)) - goto next; - } - for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++) - { - if (pools[mask.Exc[i]].Has(e)) - goto next; - } - result.AggressiveAdd(e); - next: continue; - } - } - //protected void IsMaskCompatible - protected void ExecuteWhereAndSort(EcsReadonlyGroup group, EcsGroup result) - { - ExecuteWhere(group, result); - result.Sort(); - } - } - - //TODO есть идея проверки того что запросбылвыполнен путем создания ref struct которыйсодержит результат выполнения заапроса и существует только в стеке. - //таким образом для каждой системы он будет выполняться единожды, без холостых перезапусков, скорее всего эта система будет работать лучше чем "Версии запросов" - public abstract class EcsQuery : EcsQueryBase - { - private ProfilerMarker _execute = new ProfilerMarker("EcsQuery.Where"); - - private long _executeWhereVersion = 0; - - #region Properties - //на данный момент бесполное свойство, основная идея, реализовать все методы запроса с аргументом (..., ref long version), - //далее в теле метододов сравнивать с текущей версией, и если они отличаются, выполнять запрос и перезаписывать значение version - //таким образом можно добиться сокращение "холостых" выполнений запроса, тоесть в рамках одной системы запрос всегда будет выполняться один раз - //даже при повторном вызове. - //Но нужно добавить метод для принудительного повторения запроса без сравнения вресий. - //TODO реализовать описанное выше поведение - //TODO проверить что лучше подходит int или long. long делает этот механизм очень надежным, но возможно его использование будет существенно влиять на производительность. - public sealed override long WhereVersion => _executeWhereVersion; - #endregion - - protected sealed override void OnBuild(Builder b) { } - public sealed override WhereResult Where() - { - using (_execute.Auto()) - { - ExecuteWhereAndSort(World.Entities, groupFilter); - return new WhereResult(this, ++_executeWhereVersion); - } - } - - public bool Has(int entityID) - { - return groupFilter.Has(entityID); - } - } - - public class EcsQueryMask : EcsComponentMask - { - public EcsQueryMask(Type worldArchetypeType, int[] inc, int[] exc) - { - WorldArchetype = worldArchetypeType; - Inc = inc; - Exc = exc; - } - } - public abstract class EcsQueryBuilderBase - { - public abstract TPool Include() where TComponent : struct where TPool : EcsPoolBase, new(); - public abstract TPool Exclude() where TComponent : struct where TPool : EcsPoolBase, new(); - public abstract TPool Optional() where TComponent : struct where TPool : EcsPoolBase, new(); - } - - [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 16)] - public readonly ref struct WhereResult - { - public readonly EcsQueryBase query; //ref = 8 byte - public readonly long version; //long = 8 byte - - #region Properties - public bool IsNull => query == null; - public EcsWorld World => query.World; - public bool IsActual => query.WhereVersion == version; - #endregion - - public WhereResult(EcsQueryBase query, long version) - { - this.query = query; - this.version = version; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsGroup.Enumerator GetEnumerator() => query.groupFilter.GetEnumerator(); - } -} diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index a6e6f16..31e866c 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -1,5 +1,4 @@ -using DCFApixels.DragonECS.RunnersCore; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -9,6 +8,9 @@ using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { + using RunnersCore; + using System.ComponentModel; + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] sealed class EcsRunnerFilterAttribute : Attribute { @@ -26,6 +28,7 @@ namespace DCFApixels.DragonECS namespace RunnersCore { + [EditorBrowsable(EditorBrowsableState.Never)] public interface IEcsRunner { public EcsPipeline Source { get; } @@ -55,7 +58,7 @@ namespace DCFApixels.DragonECS .Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition())); } -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS for (int i = 0; i < runnerHandlerTypes.Count; i++) { var e = CheckRunnerValide(runnerHandlerTypes[i]); @@ -128,6 +131,7 @@ namespace DCFApixels.DragonECS EcsRunner.Register(runnerType); } } + [EditorBrowsable(EditorBrowsableState.Never)] public abstract class EcsRunner : IEcsSystem, IEcsRunner where TInterface : IEcsSystem { @@ -135,7 +139,7 @@ namespace DCFApixels.DragonECS private static Type _subclass; internal static void Register(Type subclass) { -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (_subclass != null) { throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass"); diff --git a/src/EcsSubject.cs b/src/EcsSubject.cs new file mode 100644 index 0000000..1222e0c --- /dev/null +++ b/src/EcsSubject.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; + +namespace DCFApixels.DragonECS +{ + public abstract class EcsSubject + { + [EditorBrowsable(EditorBrowsableState.Always)] + internal EcsWorld source; + [EditorBrowsable(EditorBrowsableState.Always)] + internal EcsMask mask; + + private bool _isInit; + + #region Properties + [EditorBrowsable(EditorBrowsableState.Never)] + public EcsMask Mask => mask; + [EditorBrowsable(EditorBrowsableState.Never)] + public EcsWorld World => source; + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsInit => _isInit; + #endregion + + #region Methods + public bool IsMatches(int entityID) => source.IsMaskCompatible(mask, entityID); + #endregion + + #region Builder + protected virtual void Init(Builder b) { } + public sealed class Builder : EcsSubjectBuilderBase + { + private EcsWorld _world; + private List _inc; + private List _exc; + + public EcsWorld World => _world; + + private Builder(EcsWorld world) + { + _world = world; + _inc = new List(8); + _exc = new List(4); + } + internal static TSubject Build(EcsWorld world) where TSubject : EcsSubject + { + Builder builder = new Builder(world); + Type queryType = typeof(TSubject); + ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null); + EcsSubject newSubject; + if (constructorInfo != null) + { + newSubject = (EcsSubject)constructorInfo.Invoke(new object[] { builder }); + } + else + { + newSubject = (EcsSubject)Activator.CreateInstance(typeof(TSubject)); + newSubject.Init(builder); + } + newSubject.source = world; + builder.End(out newSubject.mask); + newSubject._isInit = true; + return (TSubject)newSubject; + } + + public sealed override TPool Include() + { + IncludeImplicit(); + return _world.GetPool(); + } + public sealed override TPool Exclude() + { + + return _world.GetPool(); + } + public sealed override TPool Optional() + { + return _world.GetPool(); + } + + public void IncludeImplicit() + { + int id = _world.GetComponentID(); +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list."); +#endif + _inc.Add(_world.GetComponentID()); + } + public void ExcludeImplicit() + { + int id = _world.GetComponentID(); +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list."); +#endif + _exc.Add(_world.GetComponentID()); + } + + private void End(out EcsMask mask) + { + _inc.Sort(); + _exc.Sort(); + mask = new EcsMask(_world.Archetype, _inc.ToArray(), _exc.ToArray()); + _world = null; + _inc = null; + _exc = null; + } + } + #endregion + } + + public static class EcsSubjectExtensions + { + public static EcsSubjectIterator GetIterator(this TSubject self) where TSubject : EcsSubject + { + return new EcsSubjectIterator(self, self.World.Entities); + } + public static EcsSubjectIterator GetIteratorFor(this TSubject self, EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject + { + return new EcsSubjectIterator(self, sourceGroup); + } + } + + #region BuilderBase + public abstract class EcsSubjectBuilderBase + { + public abstract TPool Include() where TComponent : struct where TPool : IEcsPoolImplementation, new(); + public abstract TPool Exclude() where TComponent : struct where TPool : IEcsPoolImplementation, new(); + public abstract TPool Optional() where TComponent : struct where TPool : IEcsPoolImplementation, new(); + } + #endregion + + #region Mask + public sealed class EcsMask + { + internal readonly Type WorldType; + internal readonly int[] Inc; + internal readonly int[] Exc; + + public EcsMask(Type worldType, int[] inc, int[] exc) + { + WorldType = worldType; + Inc = inc; + Exc = exc; + } + + public override string ToString() + { + return $"Inc({string.Join(", ", Inc)}) Exc({string.Join(", ", Exc)})"; + } + } + #endregion + + #region Iterator + public ref struct EcsSubjectIterator where TSubject : EcsSubject + { + public readonly TSubject s; + private EcsReadonlyGroup sourceGroup; + private Enumerator enumerator; + + public EcsSubjectIterator(TSubject s, EcsReadonlyGroup sourceGroup) + { + this.s = s; + this.sourceGroup = sourceGroup; + enumerator = default; + } + + public int Entity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => enumerator.Current; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Begin() => enumerator = GetEnumerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Next() => enumerator.MoveNext(); + public void CopyTo(EcsGroup group) + { + group.Clear(); + var enumerator = GetEnumerator(); + while (enumerator.MoveNext()) + group.AggressiveAdd(enumerator.Current); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(sourceGroup, s); + + public override string ToString() + { + StringBuilder result = new StringBuilder(); + foreach (var e in this) + { + result.Append(e); + result.Append(", "); + } + return result.ToString(); + } + + public ref struct Enumerator + { + private EcsGroup.Enumerator _sourceGroup; + private readonly int[] _inc; + private readonly int[] _exc; + private readonly IEcsPoolImplementation[] _pools; + + public Enumerator(EcsReadonlyGroup sourceGroup, EcsSubject subject) + { + _sourceGroup = sourceGroup.GetEnumerator(); + _inc = subject.mask.Inc; + _exc = subject.mask.Exc; + _pools = subject.World.pools; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _sourceGroup.Current; + } + 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; + for (int i = 0, iMax = _exc.Length; i < iMax; i++) + if (_pools[_exc[i]].Has(e)) goto next; + return true; + next: continue; + } + return false; + } + } + } + #endregion +} diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index c4424bc..d84f595 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -5,6 +5,13 @@ using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { + using Internal; + + internal sealed class EcsNullWorld : EcsWorld + { + public EcsNullWorld() : base(null, false) { } + } + public abstract class EcsWorld { private const short GEN_BITS = 0x7fff; @@ -15,7 +22,7 @@ namespace DCFApixels.DragonECS private static IntDispenser _worldIdDispenser = new IntDispenser(0); public readonly short uniqueID; - private int _worldArchetypeID; + private int _worldTypeID; private IntDispenser _entityDispenser; private int _entitiesCount; @@ -30,10 +37,11 @@ namespace DCFApixels.DragonECS private int[] _delEntBuffer; private int _delEntBufferCount; - internal EcsPoolBase[] pools; + internal IEcsPoolImplementation[] pools; private EcsNullPool _nullPool; - private EcsQueryBase[] _queries; + private EcsSubject[] _subjects; + private EcsQueryExecutor[] _executors; private EcsPipeline _pipeline; @@ -50,26 +58,35 @@ namespace DCFApixels.DragonECS public int Capacity => _entitesCapacity; //_denseEntities.Length; public EcsPipeline Pipeline => _pipeline; public EcsReadonlyGroup Entities => _allEntites.Readonly; - public ReadOnlySpan AllPools => pools; + public ReadOnlySpan AllPools => pools; #endregion #region Constructors/Destroy - public EcsWorld(EcsPipeline pipline) + static EcsWorld() + { + EcsNullWorld nullWorld = new EcsNullWorld(); + Worlds[0] = nullWorld; + } + public EcsWorld(EcsPipeline pipline) : this(pipline, true) { } + internal EcsWorld(EcsPipeline pipline, bool isIndexable) { _entitesCapacity = 512; - uniqueID = (short)_worldIdDispenser.GetFree(); - if (uniqueID >= Worlds.Length) - Array.Resize(ref Worlds, Worlds.Length << 1); - Worlds[uniqueID] = this; + if (isIndexable) + { + uniqueID = (short)_worldIdDispenser.GetFree(); + if (uniqueID >= Worlds.Length) + Array.Resize(ref Worlds, Worlds.Length << 1); + Worlds[uniqueID] = this; + } - _worldArchetypeID = WorldMetaStorage.GetWorldId(Archetype); + _worldTypeID = WorldMetaStorage.GetWorldId(Archetype); _pipeline = pipline ?? EcsPipeline.Empty; if (!_pipeline.IsInit) pipline.Init(); _entityDispenser = new IntDispenser(0); _nullPool = EcsNullPool.instance; - pools = new EcsPoolBase[512]; + pools = new IEcsPoolImplementation[512]; ArrayUtility.Fill(pools, _nullPool); _gens = new short[_entitesCapacity]; @@ -82,13 +99,16 @@ namespace DCFApixels.DragonECS _groups = new List>(); _allEntites = GetGroupFromPool(); - _queries = new EcsQueryBase[128]; + _subjects = new EcsSubject[128]; + _executors = new EcsQueryExecutor[128]; _entityCreate = _pipeline.GetRunner(); _entityDestry = _pipeline.GetRunner(); _pipeline.GetRunner>().Inject(this); _pipeline.GetRunner().OnWorldCreate(this); } + + public void Destroy() { _entityDispenser = null; @@ -96,7 +116,8 @@ namespace DCFApixels.DragonECS _gens = null; pools = null; _nullPool = null; - _queries = null; + _subjects = null; + _executors = null; Worlds[uniqueID] = null; _worldIdDispenser.Release(uniqueID); @@ -109,14 +130,14 @@ namespace DCFApixels.DragonECS #endregion #region GetComponentID - public int GetComponentID() => WorldMetaStorage.GetComponentId(_worldArchetypeID);////ComponentType.uniqueID; + public int GetComponentID() => WorldMetaStorage.GetComponentId(_worldTypeID);////ComponentType.uniqueID; #endregion #region GetPool - public TPool GetPool() where TComponent : struct where TPool : EcsPoolBase, new() + public TPool GetPool() where TComponent : struct where TPool : IEcsPoolImplementation, new() { - int uniqueID = WorldMetaStorage.GetComponentId(_worldArchetypeID); + int uniqueID = WorldMetaStorage.GetComponentId(_worldTypeID); if (uniqueID >= pools.Length) { @@ -129,8 +150,7 @@ namespace DCFApixels.DragonECS { var pool = new TPool(); pools[uniqueID] = pool; - pool.PreInitInternal(this, uniqueID); - pool.InvokeInit(this); + pool.OnInit(this, uniqueID); //EcsDebug.Print(pool.GetType().FullName); } @@ -140,44 +160,118 @@ namespace DCFApixels.DragonECS #endregion #region Queries - public TQuery Select() where TQuery : EcsQueryBase + public TSubject GetSubject() where TSubject : EcsSubject { - int uniqueID = WorldMetaStorage.GetQueryId(_worldArchetypeID); - if (uniqueID >= _queries.Length) - Array.Resize(ref _queries, _queries.Length << 1); - if (_queries[uniqueID] == null) - _queries[uniqueID] = EcsQueryBase.Builder.Build(this); - return (TQuery)_queries[uniqueID]; + int uniqueID = WorldMetaStorage.GetSubjectId(_worldTypeID); + if (uniqueID >= _subjects.Length) + Array.Resize(ref _subjects, _subjects.Length << 1); + if (_subjects[uniqueID] == null) + _subjects[uniqueID] = EcsSubject.Builder.Build(this); + return (TSubject)_subjects[uniqueID]; } - public WhereResult Where(out TQuery query) where TQuery : EcsQueryBase + #region Iterate + public EcsSubjectIterator IterateFor(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject { - query = Select(); - return query.Where(); - } - public WhereResult Where() where TQuery : EcsQueryBase - { - return Select().Where(); - } - public TQuery Join(WhereResult targetWorldWhereQuery, out TQuery query) where TQuery : EcsJoinAttachQueryBase - { - query = Select(); - query.Join(targetWorldWhereQuery); - return query; + subject = GetSubject(); + return subject.GetIteratorFor(sourceGroup); } - public TQuery Join(WhereResult targetWorldWhereQuery) where TQuery : EcsJoinAttachQueryBase + public EcsSubjectIterator IterateFor(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject { - TQuery query = Select(); - query.Join(targetWorldWhereQuery); - return query; + return GetSubject().GetIteratorFor(sourceGroup); + } + public EcsSubjectIterator Iterate(out TSubject subject) where TSubject : EcsSubject + { + subject = GetSubject(); + return subject.GetIterator(); + } + public EcsSubjectIterator Iterate() where TSubject : EcsSubject + { + return GetSubject().GetIterator(); } #endregion - #region IsMaskCompatible - public bool IsMaskCompatible(EcsComponentMask mask, int entityID) + #region Where + private EcsWhereExecutor GetWhereExecutor() where TSubject : EcsSubject { -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (mask.WorldArchetype != Archetype) + int id = WorldMetaStorage.GetExecutorId>(_worldTypeID); + if (id >= _executors.Length) + Array.Resize(ref _executors, _executors.Length << 1); + if (_executors[id] == null) + _executors[id] = new EcsWhereExecutor(GetSubject()); + return (EcsWhereExecutor)_executors[id]; + } + public EcsWhereResult WhereFor(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject + { + var executor = GetWhereExecutor(); + subject = executor.Subject; + return executor.ExecuteFor(sourceGroup); + } + public EcsWhereResult WhereFor(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject + { + return GetWhereExecutor().ExecuteFor(sourceGroup); + } + public EcsWhereResult Where(out TSubject subject) where TSubject : EcsSubject + { + var executor = GetWhereExecutor(); + subject = executor.Subject; + return executor.Execute(); + } + public EcsWhereResult Where() where TSubject : EcsSubject + { + return GetWhereExecutor().Execute(); + } + #endregion + + #region Join + private EcsJoinAttachExecutor GetJoinAttachExecutor() + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + int id = WorldMetaStorage.GetExecutorId>(_worldTypeID); + if (id >= _executors.Length) + Array.Resize(ref _executors, _executors.Length << 1); + if (_executors[id] == null) + _executors[id] = new EcsJoinAttachExecutor(GetSubject()); + return (EcsJoinAttachExecutor)_executors[id]; + } + public EcsJoinAttachResult JoinFor(EcsReadonlyGroup sourceGroup, out TSubject subject) + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + var executor = GetJoinAttachExecutor(); + subject = executor.Subject; + return executor.ExecuteFor(sourceGroup); + } + public EcsJoinAttachResult JoinFor(EcsReadonlyGroup sourceGroup) + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + return GetJoinAttachExecutor().ExecuteFor(sourceGroup); + } + public EcsJoinAttachResult Join(out TSubject subject) + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + var executor = GetJoinAttachExecutor(); + subject = executor.Subject; + return executor.Execute(); + } + public EcsJoinAttachResult Join() + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + return GetJoinAttachExecutor().Execute(); + } + #endregion + + #endregion + + #region IsMaskCompatible + public bool IsMaskCompatible(EcsMask mask, int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (mask.WorldType != Archetype) throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TTableArhetype)"); #endif for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++) @@ -195,7 +289,7 @@ namespace DCFApixels.DragonECS #endregion #region Entity - public EcsEntity NewEntity() + public int NewEntity() { int entityID = _entityDispenser.GetFree(); _entitiesCount++; @@ -221,13 +315,17 @@ namespace DCFApixels.DragonECS } } foreach (var item in pools) - item.InvokeOnWorldResize(_gens.Length); + item.OnWorldResize(_gens.Length); } _gens[entityID] &= GEN_BITS; - EcsEntity entity = new EcsEntity(entityID, ++_gens[entityID], uniqueID); - _entityCreate.OnEntityCreate(entity); + _entityCreate.OnEntityCreate(entityID); _allEntites.Add(entityID); - return entity; + return entityID; + } + public entlong NewEntityLong() + { + int e = NewEntity(); + return GetEntityLong(e); } public void DelEntity(int entityID) @@ -242,13 +340,16 @@ namespace DCFApixels.DragonECS ReleaseDelEntityBuffer(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsEntity GetEcsEntity(int entityID) => new EcsEntity(entityID, _gens[entityID], uniqueID); //TODO придумать получше имя метода + public entlong GetEntityLong(int entityID) => new entlong(entityID, _gens[entityID], uniqueID); //TODO придумать получше имя метода [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsAlive(int entityID, short gen) => _gens[entityID] == gen; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsUsed(int entityID) => (_gens[entityID] | DEATH_GEN_BIT) == 0; + public bool IsUsed(int entityID) => _gens[entityID] >= 0; public void ReleaseDelEntityBuffer() { + ReadOnlySpan buffser = new ReadOnlySpan(_delEntBuffer, 0, _delEntBufferCount); + foreach (var pool in pools) + pool.OnReleaseDelEntityBuffer(buffser); for (int i = 0; i < _delEntBufferCount; i++) _entityDispenser.Release(_delEntBuffer[i]); _delEntBufferCount = 0; @@ -264,8 +365,6 @@ namespace DCFApixels.DragonECS } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void IncrementEntityComponentCount(int entityID) { @@ -278,7 +377,7 @@ namespace DCFApixels.DragonECS if(count == 0) DelEntity(entityID); -#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (count < 0) throw new EcsFrameworkException("нарушен баланс инкремента.декремента компонентов"); #endif } @@ -305,6 +404,42 @@ namespace DCFApixels.DragonECS _groupsPool.Push(group); } #endregion + + #region Debug + // public int GetComponents(int entity, ref object[] list) + // { + // var entityOffset = GetRawEntityOffset(entity); + // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; + // if (itemsCount == 0) { return 0; } + // if (list == null || list.Length < itemsCount) + // { + // list = new object[_pools.Length]; + // } + // var dataOffset = entityOffset + RawEntityOffsets.Components; + // for (var i = 0; i < itemsCount; i++) + // { + // list[i] = _pools[_entities[dataOffset + i]].GetRaw(entity); + // } + // return itemsCount; + // } + // + // public int GetComponentTypes(int entity, ref Type[] list) + // { + // var entityOffset = GetRawEntityOffset(entity); + // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; + // if (itemsCount == 0) { return 0; } + // if (list == null || list.Length < itemsCount) + // { + // list = new Type[_pools.Length]; + // } + // var dataOffset = entityOffset + RawEntityOffsets.Components; + // for (var i = 0; i < itemsCount; i++) + // { + // list[i] = _pools[_entities[dataOffset + i]].GetComponentType(); + // } + // return itemsCount; + // } + #endregion } public abstract class EcsWorld : EcsWorld @@ -312,6 +447,7 @@ namespace DCFApixels.DragonECS { public override Type Archetype => typeof(TWorldArchetype); public EcsWorld(EcsPipeline pipline) : base(pipline) { } + internal EcsWorld(EcsPipeline pipline, bool isIndexable) : base(pipline, isIndexable) { } } #region Utils @@ -366,7 +502,9 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetComponentId(int worldID) => Component.Get(worldID); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetQueryId(int worldID) => Query.Get(worldID); + public static int GetSubjectId(int worldID) => Subject.Get(worldID); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetExecutorId(int worldID) => Executor.Get(worldID); private abstract class Resizer { public abstract void Resize(int size); @@ -376,7 +514,8 @@ namespace DCFApixels.DragonECS public override void Resize(int size) { Array.Resize(ref Component.ids, size); - Array.Resize(ref Query.ids, size); + Array.Resize(ref Subject.ids, size); + Array.Resize(ref Executor.ids, size); } } private static class Component @@ -398,10 +537,29 @@ namespace DCFApixels.DragonECS return id; } } - private static class Query + private static class Subject { public static int[] ids; - static Query() + static Subject() + { + ids = new int[tokenCount]; + for (int i = 0; i < ids.Length; i++) + ids[i] = -1; + resizer.Add(new Resizer()); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Get(int token) + { + ref int id = ref ids[token]; + if (id < 0) + id = queryCounts[token]++; + return id; + } + } + private static class Executor + { + public static int[] ids; + static Executor() { ids = new int[tokenCount]; for (int i = 0; i < ids.Length; i++) diff --git a/src/Entities/EcsEntity.cs b/src/Entities/EcsEntity.cs deleted file mode 100644 index a4509af..0000000 --- a/src/Entities/EcsEntity.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DCFApixels.DragonECS -{ - // uniqueID - 32 bits - // gen - 16 bits - // world - 16 bits - /// Strong identifier/Permanent entity identifier - [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] - public readonly partial struct EcsEntity : IEquatable, IEquatable - { - public static readonly EcsEntity NULL = default; - [FieldOffset(0)] - internal readonly long full; //Union - [FieldOffset(0)] - public readonly int id; - [FieldOffset(4)] - public readonly short gen; - [FieldOffset(6)] - public readonly short world; - - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //public ent ToEnt() => EcsWorld.Worlds[world].IsAlive(id, gen) ? new ent(id) : default; - - public bool IsAlive => EcsWorld.Worlds[world].IsAlive(id, gen); - - #region Constructors - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsEntity(int id, short gen, short world) : this() - { - this.id = id; - this.gen = gen; - this.world = world; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal EcsEntity(long full) : this() - { - this.full = full; - } - #endregion - - #region Equals - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(EcsEntity 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(uniqueID:{id} gen:{gen} world:{world})"; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) => obj is EcsEntity other && full == other.full; - #endregion - - #region operators - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(in EcsEntity a, in EcsEntity b) => a.full == b.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(in EcsEntity a, in EcsEntity b) => a.full != b.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator long(in EcsEntity a) => a.full; - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //public static explicit operator ent(in EcsEntity a) => a.ToEnt(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator EcsEntity(in long a) => new EcsEntity(a); - #endregion - } - - public readonly partial struct EcsEntity - { - private static EcsProfilerMarker _IsNullMarker = new EcsProfilerMarker("EcsEntity.IsNull"); - - public bool IsNull - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - //using (_IsNullMarker.Auto()) - return this == NULL; - } - } - public bool IsNotNull - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - //using (_IsNullMarker.Auto()) - return this != NULL; - } - } - - public EcsWorld GetWorld() => EcsWorld.Worlds[world]; - } - - public static partial class entExtensions - { - private static EcsProfilerMarker _IsAliveMarker = new EcsProfilerMarker("EcsEntity.IsAlive"); - - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - // public static bool IsAlive(this ref EcsEntity self) - // { - // //using (_IsAliveMarker.Auto()) - // //{ - // bool result = EcsWorld.Worlds[self.world].IsAlive(self.id, self.gen); - // if (!result) self = EcsEntity.NULL; - // return result; - // //} - // } - } -} diff --git a/src/Exceptions/EcsRelationException.cs b/src/Exceptions/EcsRelationException.cs index 0e1dff1..9b2e60e 100644 --- a/src/Exceptions/EcsRelationException.cs +++ b/src/Exceptions/EcsRelationException.cs @@ -3,7 +3,7 @@ namespace DCFApixels.DragonECS { [Serializable] - public class EcsRelationException : Exception + public class EcsRelationException : EcsFrameworkException { public EcsRelationException() { } public EcsRelationException(string message) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message) { } diff --git a/src/Exceptions/EcsRunnerImplementationException.cs b/src/Exceptions/EcsRunnerImplementationException.cs index a4e64d0..b5f2ad5 100644 --- a/src/Exceptions/EcsRunnerImplementationException.cs +++ b/src/Exceptions/EcsRunnerImplementationException.cs @@ -3,7 +3,7 @@ namespace DCFApixels.DragonECS { [Serializable] - public class EcsRunnerImplementationException : Exception + public class EcsRunnerImplementationException : EcsFrameworkException { public EcsRunnerImplementationException() { } public EcsRunnerImplementationException(string message) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message) { } diff --git a/src/Executors/EcsJoinAttachExecutor.cs b/src/Executors/EcsJoinAttachExecutor.cs new file mode 100644 index 0000000..d0d5acf --- /dev/null +++ b/src/Executors/EcsJoinAttachExecutor.cs @@ -0,0 +1,189 @@ +using Unity.Profiling; + +namespace DCFApixels.DragonECS +{ + public sealed class EcsJoinAttachExecutor : EcsQueryExecutor + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + private readonly TSubject _subject; + internal readonly EcsGroup _filteredGroup; + private EcsWorld _targetWorld; + + private EcsAttachPool _targetPool; + + private int _targetWorldCapacity = -1; + private int _targetPoolCapacity = -1; + + private int[] _mapping; + private int[] _counts; + private EntityLinkedList _linkedBasket; + + private bool _isInitTargetWorld = false; + + private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join"); + + private long _executeVersion; + + #region Properties + public TSubject Subject => _subject; + internal long ExecuteVersion => _executeVersion; + #endregion + + #region Constructors + public EcsJoinAttachExecutor(TSubject subject) + { + _subject = subject; + _filteredGroup = EcsGroup.New(subject.World); + _targetPool = subject.World.GetPool(); + } + #endregion + + #region Methods + public EcsJoinAttachResult Execute() => ExecuteFor(_targetPool.Entities); + public EcsJoinAttachResult ExecuteFor(EcsReadonlyGroup sourceGroup) + { + _executeJoin.Begin(); + var world = _subject.World; +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. +#endif + if (!_isInitTargetWorld) + InitTargetWorlds(sourceGroup.World); +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + else + if (_targetWorld != sourceGroup.World) throw new System.ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир +#endif + + //Подготовка массивов + if (_targetWorldCapacity < _targetWorld.Capacity) + { + _targetWorldCapacity = _targetWorld.Capacity; + _mapping = new int[_targetWorldCapacity]; + _counts = new int[_targetWorldCapacity]; + } + else + { + ArrayUtility.Fill(_counts, 0); + ArrayUtility.Fill(_mapping, 0); + } + if (_targetPoolCapacity < _targetPool.Capacity) + { + _targetPoolCapacity = _targetPool.Capacity; + _linkedBasket.Resize(_targetPoolCapacity); + } + _linkedBasket.Clear(); + //Конец подготовки массивов + + var iterator = new EcsSubjectIterator(_subject, sourceGroup); + foreach (var attachID in iterator) + { + entlong attachTarget = _targetPool.Read(attachID).Target; + if (!attachTarget.IsAlive) + { + //_targetPool.Del(attachID); + continue; + } + int attachTargetID = attachTarget.id; + //if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery + + ref int nodeIndex = ref _mapping[attachTargetID]; + if (nodeIndex <= 0) + nodeIndex = _linkedBasket.Add(attachID); + else + _linkedBasket.Insert(nodeIndex, attachID); + _counts[attachTargetID]++; + } + + _executeVersion++; + _executeJoin.End(); + + return new EcsJoinAttachResult(_subject, this , _executeVersion); + } + private void InitTargetWorlds(EcsWorld targetWorld) + { + _targetWorld = targetWorld; + + _targetWorldCapacity = _targetWorld.Capacity; + _mapping = new int[_targetWorldCapacity]; + _counts = new int[_targetWorldCapacity]; + + _targetPoolCapacity = _targetPool.Capacity; + _linkedBasket = new EntityLinkedList(_targetPoolCapacity); + _isInitTargetWorld = true; + } + internal sealed override void Destroy() + { + _filteredGroup.Release(); + _targetWorld = null; + _mapping = null; + _counts = null; + _linkedBasket = null; + } + #endregion + + #region Internal result methods + internal bool Has(int attachedEnttiyID) => _filteredGroup.Has(attachedEnttiyID); + internal EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]); + internal int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0; + internal int GetNodesCount(int entityID) => _counts[entityID]; + internal bool HasNode(int entityID, int attachedEntityID) => _filteredGroup.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID; + #endregion + } + + #region JoinAttachExecuter Results + public readonly ref struct EcsJoinAttachResult + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + public readonly TSubject s; + private readonly EcsJoinAttachExecutor _executer; + private readonly long _verison; + + public bool IsRelevant => _verison == _executer.ExecuteVersion; + + public EcsJoinAttachResult(TSubject s, EcsJoinAttachExecutor executer, long version) + { + this.s = s; + _executer = executer; + _verison = version; + } + + public bool Has(int attachedEnttiyID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.Has(attachedEnttiyID); + } + public EntityLinkedList.EnumerableSpan GetNodes(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.GetNodes(entityID); + } + public int GetNode(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.GetNode(entityID); + } + public int GetNodesCount(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.GetNodesCount(entityID); + } + public bool HasNode(int entityID, int attachedEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.HasNode(entityID, attachedEntityID); + } + } + #endregion +} diff --git a/src/Executors/EcsJoinHierarchyExecutor.cs b/src/Executors/EcsJoinHierarchyExecutor.cs new file mode 100644 index 0000000..34ff289 --- /dev/null +++ b/src/Executors/EcsJoinHierarchyExecutor.cs @@ -0,0 +1,198 @@ +using System; +using Unity.Profiling; + +namespace DCFApixels.DragonECS +{ + public sealed class EcsJoinHierarchyExecutor : EcsQueryExecutor + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + private readonly TSubject _subject; + internal readonly EcsGroup _filteredGroup; + private EcsWorld _targetWorld; + + private EcsAttachPool _targetPool; + + private int _targetWorldCapacity = -1; + private int _targetPoolCapacity = -1; + + private int[] _mapping; + private int[] _counts; + private EntityLinkedList _linkedBasket; + + private bool _isInitTargetWorld = false; + + private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where"); + private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join"); + + private long _executeVersion; + + #region Properties + public TSubject Subject => _subject; + internal long ExecuteVersion => _executeVersion; + #endregion + + #region Constructors + public EcsJoinHierarchyExecutor(TSubject subject) + { + _subject = subject; + _filteredGroup = EcsGroup.New(subject.World); + _targetPool = subject.World.GetPool(); + } + #endregion + + #region Methods + public EcsJoinHierarchyResult Execute() => ExecuteFor(_targetPool.Entities); + public EcsJoinHierarchyResult ExecuteFor(EcsReadonlyGroup sourceGroup) + { + var world = _subject.World; + _executeJoin.Begin(); +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (sourceGroup.IsNull) throw new ArgumentNullException();//TODO составить текст исключения. +#endif + if (!_isInitTargetWorld) + InitTargetWorlds(sourceGroup.World); +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + else + if (_targetWorld != sourceGroup.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир +#endif + + //Подготовка массивов + if (_targetWorldCapacity < _targetWorld.Capacity) + { + _targetWorldCapacity = _targetWorld.Capacity; + _mapping = new int[_targetWorldCapacity]; + _counts = new int[_targetWorldCapacity]; + } + else + { + ArrayUtility.Fill(_counts, 0); + ArrayUtility.Fill(_mapping, 0); + } + if (_targetPoolCapacity < _targetPool.Capacity) + { + _targetPoolCapacity = _targetPool.Capacity; + _linkedBasket.Resize(_targetPoolCapacity); + } + _linkedBasket.Clear(); + //Конец подготовки массивов + + var iterator = new EcsSubjectIterator(_subject, sourceGroup); + foreach (var attachID in iterator) + { + entlong attachTarget = _targetPool.Read(attachID).Target; + if (!attachTarget.IsAlive) + { + //_targetPool.Del(attachID); + continue; + } + int attachTargetID = attachTarget.id; + //if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery + + ref int nodeIndex = ref _mapping[attachTargetID]; + if (nodeIndex <= 0) + nodeIndex = _linkedBasket.Add(attachID); + else + _linkedBasket.Insert(nodeIndex, attachID); + _counts[attachTargetID]++; + } + + _executeVersion++; + _executeJoin.End(); + + return new EcsJoinHierarchyResult(_subject, this , _executeVersion); + } + private void InitTargetWorlds(EcsWorld targetWorld) + { + _targetWorld = targetWorld; + + _targetWorldCapacity = _targetWorld.Capacity; + _mapping = new int[_targetWorldCapacity]; + _counts = new int[_targetWorldCapacity]; + + _targetPoolCapacity = _targetPool.Capacity; + _linkedBasket = new EntityLinkedList(_targetPoolCapacity); + _isInitTargetWorld = true; + } + internal sealed override void Destroy() + { + _filteredGroup.Release(); + _targetWorld = null; + _mapping = null; + _counts = null; + _linkedBasket = null; + } + #endregion + + #region Internal result methods + internal bool Has(int attachedEnttiyID) => _filteredGroup.Has(attachedEnttiyID); + + internal EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]); + internal int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0; + internal int GetNodesCount(int entityID) => _counts[entityID]; + internal bool HasNode(int entityID, int attachedEntityID) => _filteredGroup.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID; + + + internal EntityLinkedList.EnumerableSpan GetSubNodes(int entityID) => throw new NotImplementedException(); + internal int GetSubNode(int entityID) => throw new NotImplementedException(); + internal bool GetSubNodesCount(int entityID, int attachedEntityID) => throw new NotImplementedException(); + internal bool HasSubNode(int entityID, int attachedEntityID) => throw new NotImplementedException(); + #endregion + } + + #region JoinAttachExecuter Results + public readonly ref struct EcsJoinHierarchyResult + where TSubject : EcsSubject + where TAttachComponent : struct, IEcsAttachComponent + { + public readonly TSubject s; + private readonly EcsJoinHierarchyExecutor _executer; + private readonly long _verison; + + public bool IsRelevant => _verison == _executer.ExecuteVersion; + + public EcsJoinHierarchyResult(TSubject s, EcsJoinHierarchyExecutor executer, long version) + { + this.s = s; + _executer = executer; + _verison = version; + } + + public bool Has(int attachedEnttiyID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.Has(attachedEnttiyID); + } + public EntityLinkedList.EnumerableSpan GetNodes(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.GetNodes(entityID); + } + public int GetNode(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.GetNode(entityID); + } + public int GetNodesCount(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.GetNodesCount(entityID); + } + public bool HasNode(int entityID, int attachedEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения. +#endif + return _executer.HasNode(entityID, attachedEntityID); + } + } + #endregion +} diff --git a/src/Executors/EcsQueryExecutor.cs b/src/Executors/EcsQueryExecutor.cs new file mode 100644 index 0000000..1a15dc1 --- /dev/null +++ b/src/Executors/EcsQueryExecutor.cs @@ -0,0 +1,7 @@ +namespace DCFApixels.DragonECS +{ + public abstract class EcsQueryExecutor + { + internal abstract void Destroy(); + } +} diff --git a/src/Executors/EcsWhereExecutor.cs b/src/Executors/EcsWhereExecutor.cs new file mode 100644 index 0000000..e47b56b --- /dev/null +++ b/src/Executors/EcsWhereExecutor.cs @@ -0,0 +1,72 @@ +using Unity.Profiling; + +namespace DCFApixels.DragonECS +{ + public sealed class EcsWhereExecutor : EcsQueryExecutor where TSubject : EcsSubject + { + private readonly TSubject _subject; + private readonly EcsGroup _filteredGroup; + + private long _executeVersion; + + private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where"); + + #region Properties + public TSubject Subject => _subject; + internal long ExecuteVersion => _executeVersion; + #endregion + + #region Constructors + public EcsWhereExecutor(TSubject subject) + { + _subject = subject; + _filteredGroup = EcsGroup.New(subject.World); + } + #endregion + + #region Methods + public EcsWhereResult Execute() => ExecuteFor(_subject.World.Entities); + public EcsWhereResult ExecuteFor(EcsReadonlyGroup sourceGroup) + { + using (_executeWhere.Auto()) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения. +#endif + _subject.GetIteratorFor(sourceGroup).CopyTo(_filteredGroup); + return new EcsWhereResult(this, _filteredGroup.Readonly); + } + } + internal sealed override void Destroy() + { + _filteredGroup.Release(); + } + #endregion + } + + #region WhereExecuter Results + public readonly ref struct EcsWhereResult where TSubject : EcsSubject + { + public readonly TSubject s; + private readonly EcsWhereExecutor _executer; + public readonly EcsReadonlyGroup group; + private readonly long _version; + public bool IsRelevant => _version == _executer.ExecuteVersion; + + public EcsWhereResult(EcsWhereExecutor executer, EcsReadonlyGroup group) + { + _executer = executer; + _version = executer.ExecuteVersion; + s = executer.Subject; + this.group = group; + } + public EcsGroup.Enumerator GetEnumerator() + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения. +#endif + return group.GetEnumerator(); + } + } + #endregion +} diff --git a/src/Interfaces/IEcsComponentReset.cs b/src/Interfaces/IEcsComponentReset.cs deleted file mode 100644 index 8abb356..0000000 --- a/src/Interfaces/IEcsComponentReset.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using System.Runtime.CompilerServices; - -namespace DCFApixels.DragonECS -{ - public interface IEcsComponentReset - { - public void Reset(ref T component); - } - public static class EcsComponentResetHandler - { - public static readonly IEcsComponentReset instance; - public static readonly bool isHasHandler; - static EcsComponentResetHandler() - { - Type targetType = typeof(T); - if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType))) - { - instance = (IEcsComponentReset)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType)); - isHasHandler = true; - } - else - { - instance = new ComponentResetDummyHandler(); - isHasHandler = false; - } - } - } - internal sealed class ComponentResetDummyHandler : IEcsComponentReset - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Reset(ref T component) => component = default; - } - internal sealed class ComponentResetHandler : IEcsComponentReset - where T : IEcsComponentReset - { - private T _fakeInstnace = default; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Reset(ref T component) => _fakeInstnace.Reset(ref component); - } -} diff --git a/src/Pools/EcsAttachPool.cs b/src/Pools/EcsAttachPool.cs index 908bbee..f8d877c 100644 --- a/src/Pools/EcsAttachPool.cs +++ b/src/Pools/EcsAttachPool.cs @@ -1,13 +1,19 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Runtime.CompilerServices; -using Unity.Profiling; namespace DCFApixels.DragonECS { + using static EcsPoolThrowHalper; // - public sealed class EcsAttachPool : EcsPoolBase + /// Pool for IEcsAttachComponent components + public sealed class EcsAttachPool : IEcsPoolImplementation, IEnumerable //IntelliSense hack where T : struct, IEcsAttachComponent { + private EcsWorld _source; + private int _id; + private bool[] _entityFlags;// index = entityID / value = entityFlag;/ value = 0 = no entityID private T[] _items; //sparse private int _count; @@ -30,11 +36,17 @@ namespace DCFApixels.DragonECS #region Properites public int Count => _count; public int Capacity => _items.Length; + public int ComponentID => _id; + public Type ComponentType => typeof(T); + public EcsWorld World => _source; #endregion #region Init - protected override void Init(EcsWorld world) + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { + _source = world; + _id = componentID; + _poolRunners = new PoolRunners(world.Pipeline); _entities = EcsGroup.New(world); @@ -45,23 +57,14 @@ namespace DCFApixels.DragonECS } #endregion - #region Write/Read/Has/Del - private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add"); - private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write"); - private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read"); - private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); - private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); - public void Add(int entityID, EcsEntity target) + #region Methods + public void Add(int entityID, entlong target) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld) - { - throw new EcsRelationException(); - } + if (_sanitizeTargetWorld > 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent(entityID); _sanitizeTargetWorld = target.world; + if (Has(entityID)) ThrowAlreadyHasComponent(entityID); #endif - // using (_addMark.Auto()) - // { ref bool entityFlag = ref _entityFlags[entityID]; if (entityFlag == false) { @@ -72,59 +75,107 @@ namespace DCFApixels.DragonECS } _poolRunners.write.OnComponentWrite(entityID); _items[entityID].Target = target; - // } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(int entityID, EcsEntity target) + public void Set(int entityID, entlong target) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld) - { - throw new EcsRelationException(); - } + if (!Has(entityID)) ThrowNotHaveComponent(entityID); + if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent(entityID); _sanitizeTargetWorld = target.world; #endif - // using (_writeMark.Auto()) _poolRunners.write.OnComponentWrite(entityID); _items[entityID].Target = target; } + public void AddOrSet(int entityID, entlong target) + { + if (Has(entityID)) + Set(entityID, target); + else + Add(entityID, target); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Read(int entityID) { - // using (_readMark.Auto()) +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif return ref _items[entityID]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sealed override bool Has(int entityID) + public bool Has(int entityID) { - // using (_hasMark.Auto()) return _entityFlags[entityID]; } public void Del(int entityID) { - // using (_delMark.Auto()) - // { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif _entities.Remove(entityID); _entityFlags[entityID] = false; _count--; _poolRunners.del.OnComponentDel(entityID); - // } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TryDel(int entityID) + { + if (Has(entityID)) Del(entityID); + } + public void Copy(int fromEntityID, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + if (Has(toEntityID)) + Set(toEntityID, Read(fromEntityID).Target); + else + Add(toEntityID, Read(fromEntityID).Target); } #endregion #region WorldCallbacks - protected override void OnWorldResize(int newSize) + void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _entityFlags, newSize); Array.Resize(ref _items, newSize); } - protected override void OnDestroy() { } + void IEcsPoolImplementation.OnWorldDestroy() { } + void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + foreach (var item in buffer) + TryDel(item); + } + #endregion + + #region Other + ref T IEcsPool.Add(int entityID) + { + if (!Has(entityID)) + Add(entityID, entlong.NULL); + return ref _items[entityID]; + } + ref T IEcsPool.Write(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif + return ref _items[entityID]; + } + void IEcsPool.AddRaw(int entityID, object dataRaw) => ((IEcsPool)this).Add(entityID) = (T)dataRaw; + object IEcsPool.GetRaw(int entityID) => Read(entityID); + void IEcsPool.SetRaw(int entityID, object dataRaw) => ((IEcsPool)this).Write(entityID) = (T)dataRaw; + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); #endregion } public interface IEcsAttachComponent { - public EcsEntity Target { get; set; } + public entlong Target { get; set; } } public static class EcsAttachComponentPoolExt { @@ -133,15 +184,15 @@ namespace DCFApixels.DragonECS return self.GetPool>(); } - public static EcsAttachPool Include(this EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent + public static EcsAttachPool Include(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent { return self.Include>(); } - public static EcsAttachPool Exclude(this EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent + public static EcsAttachPool Exclude(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent { return self.Exclude>(); } - public static EcsAttachPool Optional(this EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent + public static EcsAttachPool Optional(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent { return self.Optional>(); } diff --git a/src/Pools/EcsNotNullPool.cs b/src/Pools/EcsNotNullPool.cs index 56f8cc4..7ed0f16 100644 --- a/src/Pools/EcsNotNullPool.cs +++ b/src/Pools/EcsNotNullPool.cs @@ -1,27 +1,39 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Runtime.CompilerServices; -using Unity.Profiling; namespace DCFApixels.DragonECS { // - public sealed class EcsNotNullPool : EcsPoolBase - where T : struct, INotNullComponent + /// Pool for IEcsNotNullComponent components + public sealed class EcsNotNullPool : IEcsPoolImplementation, IEnumerable //IntelliSense hack + where T : struct, IEcsNotNullComponent { + private EcsWorld _source; + private int _id; + private T[] _items; //sparse private int _count; private IEcsComponentReset _componentResetHandler; + private IEcsComponentCopy _componentCopyHandler; private PoolRunners _poolRunners; #region Properites public int Count => _count; public int Capacity => _items.Length; + public int ComponentID => _id; + public Type ComponentType => typeof(T); + public EcsWorld World => _source; #endregion #region Init - protected override void Init(EcsWorld world) + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { + _source = world; + _id = componentID; + _items = new T[world.Capacity]; _count = 0; @@ -30,59 +42,77 @@ namespace DCFApixels.DragonECS } #endregion - #region Write/Read/Has - private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add"); - private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write"); - private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read"); - private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); - private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); + #region Methods [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Write(int entityID) { - // using (_writeMark.Auto()) _poolRunners.write.OnComponentWrite(entityID); return ref _items[entityID]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Read(int entityID) { - // using (_readMark.Auto()) return ref _items[entityID]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sealed override bool Has(int entityID) + public bool Has(int entityID) { - // using (_hasMark.Auto()) return true; } + public void Copy(int fromEntityID, int toEntityID) + { + _componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID)); + } #endregion - #region WorldCallbacks - protected override void OnWorldResize(int newSize) + #region Callbacks + void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _items, newSize); } - protected override void OnDestroy() { } + void IEcsPoolImplementation.OnWorldDestroy() { } + void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + foreach (var entityID in buffer) + _componentResetHandler.Reset(ref _items[entityID]); + } + #endregion + + #region Other + ref T IEcsPool.Add(int entityID) => ref Write(entityID); + ref readonly T IEcsPool.Read(int entityID) => ref Read(entityID); + ref T IEcsPool.Write(int entityID) => ref Write(entityID); + void IEcsPool.Del(int entityID) { } + void IEcsPool.AddRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw; + object IEcsPool.GetRaw(int entityID) => Write(entityID); + void IEcsPool.SetRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw; + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); #endregion } - - public interface INotNullComponent { } + /// + /// Not null component. Is present on all entities, without explicit addition and cannot be deleted + /// + public interface IEcsNotNullComponent { } public static class EcsNotNullPoolExt { - public static EcsNotNullPool GetPool(this EcsWorld self) where TNotNullComponent : struct, INotNullComponent + public static EcsNotNullPool GetPool(this EcsWorld self) where TNotNullComponent : struct, IEcsNotNullComponent { return self.GetPool>(); } - public static EcsNotNullPool Include(this EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent + public static EcsNotNullPool Include(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent { return self.Include>(); } - public static EcsNotNullPool Exclude(this EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent + public static EcsNotNullPool Exclude(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent { return self.Exclude>(); } - public static EcsNotNullPool Optional(this EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent + public static EcsNotNullPool Optional(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent { return self.Optional>(); } diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index e4eafb0..8a3c0a4 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -2,14 +2,16 @@ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using Unity.Profiling; namespace DCFApixels.DragonECS { - public sealed class EcsPool : EcsPoolBase + using static EcsPoolThrowHalper; + /// Pool for IEcsComponent components + public sealed class EcsPool : IEcsPoolImplementation, IEnumerable //IntelliSense hack where T : struct, IEcsComponent { - public static string name = typeof(T).Name; + private EcsWorld _source; + private int _id; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense @@ -18,16 +20,23 @@ namespace DCFApixels.DragonECS private int _recycledItemsCount; private IEcsComponentReset _componentResetHandler; + private IEcsComponentCopy _componentCopyHandler; private PoolRunners _poolRunners; #region Properites public int Count => _itemsCount; public int Capacity => _items.Length; + public int ComponentID => _id; + public Type ComponentType => typeof(T); + public EcsWorld World => _source; #endregion #region Init - protected override void Init(EcsWorld world) + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { + _source = world; + _id = componentID; + const int capacity = 512; _mapping = new int[world.Capacity]; @@ -37,21 +46,59 @@ namespace DCFApixels.DragonECS _itemsCount = 0; _componentResetHandler = EcsComponentResetHandler.instance; + _componentCopyHandler = EcsComponentCopyHandler.instance; _poolRunners = new PoolRunners(world.Pipeline); } #endregion - #region Write/Read/Has/Del - private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add"); - private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write"); - private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read"); - private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); - private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); + #region Methods public ref T Add(int entityID) { // using (_addMark.Auto()) // { ref int itemIndex = ref _mapping[entityID]; +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (itemIndex > 0) 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); + _poolRunners.add.OnComponentAdd(entityID); + _poolRunners.write.OnComponentWrite(entityID); + return ref _items[itemIndex]; + // } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T Write(int entityID) + { + // using (_writeMark.Auto()) +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif + _poolRunners.write.OnComponentWrite(entityID); + return ref _items[_mapping[entityID]]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(int entityID) + { + // using (_readMark.Auto()) +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif + return ref _items[_mapping[entityID]]; + } + public ref T TryAddOrWrite(int entityID) + { + ref int itemIndex = ref _mapping[entityID]; if (itemIndex <= 0) { if (_recycledItemsCount > 0) @@ -65,62 +112,75 @@ namespace DCFApixels.DragonECS if (itemIndex >= _items.Length) Array.Resize(ref _items, _items.Length << 1); } - - //_mapping[entityID] = itemIndex; TODO - IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID); _poolRunners.add.OnComponentAdd(entityID); } _poolRunners.write.OnComponentWrite(entityID); return ref _items[itemIndex]; - // } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Write(int entityID) + public bool Has(int entityID) { - // using (_writeMark.Auto()) - _poolRunners.write.OnComponentWrite(entityID); - return ref _items[_mapping[entityID]]; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(int entityID) - { - // using (_readMark.Auto()) - return ref _items[_mapping[entityID]]; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sealed override bool Has(int entityID) - { - // using (_hasMark.Auto()) return _mapping[entityID] > 0; } public void Del(int entityID) { - // using (_delMark.Auto()) - // { - +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif ref int itemIndex = ref _mapping[entityID]; _componentResetHandler.Reset(ref _items[itemIndex]); if (_recycledItemsCount >= _recycledItems.Length) Array.Resize(ref _recycledItems, _recycledItems.Length << 1); _recycledItems[_recycledItemsCount++] = itemIndex; - itemIndex = 0; + _mapping[entityID] = 0; _itemsCount--; - DecrementEntityComponentCount(entityID); + this.DecrementEntityComponentCount(entityID); _poolRunners.del.OnComponentDel(entityID); - // } + } + public void TryDel(int entityID) + { + if (Has(entityID)) Del(entityID); + } + public void Copy(int fromEntityID, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + if (Has(toEntityID)) + _componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID)); + else + _componentCopyHandler.Copy(ref Write(fromEntityID), ref Add(toEntityID)); } #endregion - #region WorldCallbacks - protected override void OnWorldResize(int newSize) + #region Callbacks + void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); } - protected override void OnDestroy() { } + 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) => Write(entityID) = (T)dataRaw; + ref readonly T IEcsPool.Read(int entityID) => ref Read(entityID); + ref T IEcsPool.Write(int entityID) => ref Write(entityID); + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); #endregion } - + /// Standard component public interface IEcsComponent { } public static class EcsPoolExt { @@ -129,15 +189,15 @@ namespace DCFApixels.DragonECS return self.GetPool>(); } - public static EcsPool Include(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent + public static EcsPool Include(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent { return self.Include>(); } - public static EcsPool Exclude(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent + public static EcsPool Exclude(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent { return self.Exclude>(); } - public static EcsPool Optional(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent + public static EcsPool Optional(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent { return self.Optional>(); } diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 92bd41f..4502e92 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -1,77 +1,194 @@ using System; -using System.Collections; -using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { - public abstract class EcsPoolBase + public interface IEcsPool { - private int _id = -1; - private EcsWorld _world; - internal void PreInitInternal(EcsWorld world, int id) - { - _id = id; - _world = world; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void IncrementEntityComponentCount(int entityID) - { - _world.IncrementEntityComponentCount(entityID); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void DecrementEntityComponentCount(int entityID) - { - _world.DecrementEntityComponentCount(entityID); - } - #region Properties - public abstract Type ComponentType { get; } - public EcsWorld World => _world; + public int ComponentID { get; } + public Type ComponentType { get; } + public EcsWorld World { get; } + public int Count { get; } + public int Capacity { get; } #endregion #region Methods - public abstract bool Has(int entityID); - - protected abstract void Init(EcsWorld world); - protected abstract void OnWorldResize(int newSize); - protected abstract void OnDestroy(); - #endregion - - #region Internal - internal void InvokeInit(EcsWorld world) => Init(world); - internal void InvokeOnWorldResize(int newSize) => OnWorldResize(newSize); - internal void InvokeOnDestroy() => OnDestroy(); + bool Has(int entityID); + void Del(int entityID); + void AddRaw(int entityID, object dataRaw); + object GetRaw(int entityID); + void SetRaw(int entityID, object dataRaw); + void Copy(int fromEntityID, int toEntityID); #endregion } - public abstract class EcsPoolBase : EcsPoolBase, IEnumerable + public interface IEcsPool { - public sealed override Type ComponentType => typeof(T); - //Релазиация интерфейса IEnumerator не работает, нужно только чтобы IntelliSense предлагала названия на основе T. Не нашел другого способа - #region IEnumerable - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); - #endregion + ref T Add(int entityID); + ref readonly T Read(int entityID); + ref T Write(int entityID); } - - public struct NullComponent { } - public sealed class EcsNullPool : EcsPoolBase + /// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool. + /// Component type + public interface IEcsPoolImplementation : IEcsPool { - public static EcsNullPool instance => new EcsNullPool(); - - #region Properties - public sealed override Type ComponentType => typeof(NullComponent); - #endregion - - #region Methods - public sealed override bool Has(int index) => false; - #endregion - - #region Callbacks - protected override void Init(EcsWorld world) { } - protected override void OnWorldResize(int newSize) { } - protected override void OnDestroy() { } - #endregion + void OnInit(EcsWorld world, int componentID); + void OnWorldResize(int newSize); + void OnReleaseDelEntityBuffer(ReadOnlySpan buffer); + void OnWorldDestroy(); } + /// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool. + /// Component type + public interface IEcsPoolImplementation : IEcsPool, IEcsPoolImplementation { } + public static class EcsPoolThrowHalper + { + public static void ThrowAlreadyHasComponent(int entityID) + { + throw new EcsFrameworkException($"Entity({entityID}) already has component {typeof(T).Name}."); + } + public static void ThrowNotHaveComponent(int entityID) + { + throw new EcsFrameworkException($"Entity({entityID}) has no component {typeof(T).Name}."); + } + public static void ThrowInvalidComponentType(Type invalidType) + { + throw new EcsFrameworkException($"Invalid component type, {typeof(T).Name} required."); + } + public static void ThrowWorldDifferent(int entityID) + { + throw new EcsRelationException($"The world of the target entity({entityID}) in {typeof(T).Name} is not the same as the world of the other entities. "); + } + } + public static class IEcsPoolImplementationExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IncrementEntityComponentCount(this IEcsPoolImplementation self, int entityID) + { + self.World.IncrementEntityComponentCount(entityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DecrementEntityComponentCount(this IEcsPoolImplementation self, int entityID) + { + self.World.DecrementEntityComponentCount(entityID); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNullOrDummy(this IEcsPool self) + { + return self == null || self is Internal.EcsNullPool; + } + } + + #region Dummy + namespace Internal + { + public struct NullComponent { } + public sealed class EcsNullPool : IEcsPoolImplementation + { + public static EcsNullPool instance => new EcsNullPool(); + + #region Properties + int IEcsPool.ComponentID => throw new NotImplementedException(); + Type IEcsPool.ComponentType => typeof(NullComponent); + EcsWorld IEcsPool.World => throw new NotImplementedException(); + public int Count => 0; + public int Capacity => 0; + #endregion + + #region Methods + bool IEcsPool.Has(int index) => false; + void IEcsPool.Del(int entityID) => throw new NotImplementedException(); + void IEcsPool.AddRaw(int entityID, object dataRaw) => throw new NotImplementedException(); + object IEcsPool.GetRaw(int entityID) => throw new NotImplementedException(); + void IEcsPool.SetRaw(int entity, object dataRaw) => throw new NotImplementedException(); + void IEcsPool.Copy(int fromEntityID, int toEntityID) => throw new NotImplementedException(); + ref NullComponent IEcsPool.Add(int entityID) => throw new NotImplementedException(); + ref readonly NullComponent IEcsPool.Read(int entityID) => throw new NotImplementedException(); + ref NullComponent IEcsPool.Write(int entityID) => throw new NotImplementedException(); + #endregion + + #region Callbacks + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { } + void IEcsPoolImplementation.OnWorldDestroy() { } + void IEcsPoolImplementation.OnWorldResize(int newSize) { } + void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { } + #endregion + } + } + #endregion + + #region Reset/Copy interfaces + public interface IEcsComponentReset + { + public void Reset(ref T component); + } + public static class EcsComponentResetHandler + { + public static readonly IEcsComponentReset instance; + public static readonly bool isHasHandler; + static EcsComponentResetHandler() + { + Type targetType = typeof(T); + if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType))) + { + instance = (IEcsComponentReset)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType)); + isHasHandler = true; + } + else + { + instance = new ComponentResetDummyHandler(); + isHasHandler = false; + } + } + } + internal sealed class ComponentResetDummyHandler : IEcsComponentReset + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset(ref T component) => component = default; + } + internal sealed class ComponentResetHandler : IEcsComponentReset + where T : IEcsComponentReset + { + private T _fakeInstnace = default; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset(ref T component) => _fakeInstnace.Reset(ref component); + } + + public interface IEcsComponentCopy + { + public void Copy(ref T from, ref T to); + } + public static class EcsComponentCopyHandler + { + public static readonly IEcsComponentCopy instance; + public static readonly bool isHasHandler; + static EcsComponentCopyHandler() + { + Type targetType = typeof(T); + if (targetType.GetInterfaces().Contains(typeof(IEcsComponentCopy<>).MakeGenericType(targetType))) + { + instance = (IEcsComponentCopy)Activator.CreateInstance(typeof(ComponentCopyHandler<>).MakeGenericType(targetType)); + isHasHandler = true; + } + else + { + instance = new ComponentCopyDummyHandler(); + isHasHandler = false; + } + } + } + internal sealed class ComponentCopyDummyHandler : IEcsComponentCopy + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Copy(ref T from, ref T to) => to = from; + } + internal sealed class ComponentCopyHandler : IEcsComponentCopy + where T : IEcsComponentCopy + { + private T _fakeInstnace = default; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Copy(ref T from, ref T to) => _fakeInstnace.Copy(ref from, ref to); + } + #endregion } diff --git a/src/Pools/EcsReadonlyPool.cs b/src/Pools/EcsReadonlyPool.cs deleted file mode 100644 index 7f234be..0000000 --- a/src/Pools/EcsReadonlyPool.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Unity.Profiling; - -namespace DCFApixels.DragonECS -{ - public sealed class EcsReadonlyPool : EcsPoolBase - where T : struct, IEcsReadonlyComponent - { - public static string name = typeof(T).Name; - - 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; - private PoolRunners _poolRunners; - - #region Properites - public int Count => _itemsCount; - public int Capacity => _items.Length; - #endregion - - #region Init - protected override void Init(EcsWorld world) - { - const int capacity = 512; - - _mapping = new int[world.Capacity]; - _recycledItems = new int[128]; - _recycledItemsCount = 0; - _items = new T[capacity]; - _itemsCount = 0; - - _componentResetHandler = EcsComponentResetHandler.instance; - _poolRunners = new PoolRunners(world.Pipeline); - } - #endregion - - #region Write/Read/Has/Del - private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add"); - private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write"); - private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read"); - private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); - private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); - public ref T Add(int entityID) - { - // using (_addMark.Auto()) - // { - ref int itemIndex = ref _mapping[entityID]; - if (itemIndex <= 0) - { - if (_recycledItemsCount > 0) - { - itemIndex = _recycledItems[--_recycledItemsCount]; - _itemsCount++; - } - else - { - itemIndex = ++_itemsCount; - if (itemIndex >= _items.Length) - Array.Resize(ref _items, _items.Length << 1); - } - - //_mapping[entityID] = itemIndex; TODO проверить что это лишнее дейсвие - _poolRunners.add.OnComponentAdd(entityID); - } - else - { - _componentResetHandler.Reset(ref _items[itemIndex]); - } - _poolRunners.write.OnComponentWrite(entityID); - return ref _items[itemIndex]; - // } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(int entityID) - { - // using (_readMark.Auto()) - return ref _items[_mapping[entityID]]; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sealed override bool Has(int entityID) - { - // using (_hasMark.Auto()) - return _mapping[entityID] > 0; - } - public void Del(int entityID) - { - // using (_delMark.Auto()) - // { - ref int itemIndex = ref _mapping[entityID]; - _componentResetHandler.Reset(ref _items[itemIndex]); - if (_recycledItemsCount >= _recycledItems.Length) - Array.Resize(ref _recycledItems, _recycledItems.Length << 1); - _recycledItems[_recycledItemsCount++] = itemIndex; - itemIndex = 0; - _itemsCount--; - _poolRunners.del.OnComponentDel(entityID); - // } - } - #endregion - - #region WorldCallbacks - protected override void OnWorldResize(int newSize) - { - Array.Resize(ref _mapping, newSize); - } - protected override void OnDestroy() { } - - #endregion - } - - public interface IEcsReadonlyComponent { } - public static class EcsReadonlyPoolExt - { - public static EcsReadonlyPool GetPool(this EcsWorld self) where TReadolnyComponent : struct, IEcsReadonlyComponent - { - return self.GetPool>(); - } - - public static EcsReadonlyPool Include(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent - { - return self.Include>(); - } - public static EcsReadonlyPool Exclude(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent - { - return self.Exclude>(); - } - public static EcsReadonlyPool Optional(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent - { - return self.Optional>(); - } - } -} diff --git a/src/Pools/EcsRelationPool.cs b/src/Pools/EcsRelationPool.cs deleted file mode 100644 index e17fc33..0000000 --- a/src/Pools/EcsRelationPool.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using Unity.Profiling; - -namespace DCFApixels.DragonECS -{ - // - public sealed class EcsRelationPool : EcsPoolBase - where T : struct, IEcsRelationComponent - { - private bool[] _entityFlags;// index = entityID / value = entityFlag;/ value = 0 = no entityID - private T[] _items; //sparse - private int _count; - private PoolRunners _poolRunners; - - private EcsGroup _entities; - public EcsReadonlyGroup Entities => _entities.Readonly; - -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - private short _sanitizeFirstWorld = -1; - private short _sanitizeSecondWorld = -1; -#endif - - #region Properites - public int Count => _count; - public int Capacity => _items.Length; - #endregion - - #region Init - protected override void Init(EcsWorld world) - { - _poolRunners = new PoolRunners(world.Pipeline); - - _entityFlags = new bool[world.Capacity]; - _items = new T[world.Capacity]; - _count = 0; - } - #endregion - - #region Write/Read/Has/Del - private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add"); - private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write"); - private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read"); - private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); - private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); - public void Add(int entityID, EcsEntity first, EcsEntity second) - { -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if((_sanitizeFirstWorld >= 0 && first.world != _sanitizeFirstWorld) && - (_sanitizeSecondWorld >= 0 && second.world != _sanitizeSecondWorld)) - { - throw new EcsRelationException(); - } -#endif - // using (_addMark.Auto()) - // { - ref bool entityFlag = ref _entityFlags[entityID]; - if (entityFlag == false) - { - entityFlag = true; - _count++; - _entities.Add(entityID); - _poolRunners.add.OnComponentAdd(entityID); - } - _poolRunners.write.OnComponentWrite(entityID); - _items[entityID].Set(first, second); - // } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(int entityID, EcsEntity first, EcsEntity second) - { -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if ((_sanitizeFirstWorld >= 0 && first.world != _sanitizeFirstWorld) && - (_sanitizeSecondWorld >= 0 && second.world != _sanitizeSecondWorld)) - { - throw new EcsRelationException(); - } - _sanitizeFirstWorld = first.world; - _sanitizeSecondWorld = second.world; -#endif - // using (_writeMark.Auto()) - //{ - _poolRunners.write.OnComponentWrite(entityID); - _items[entityID].Set(first, second); - //} - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(int entityID) - { - // using (_readMark.Auto()) - return ref _items[entityID]; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sealed override bool Has(int entityID) - { - // using (_hasMark.Auto()) - return _entityFlags[entityID]; - } - public void Del(int entityID) - { - // using (_delMark.Auto()) - // { - _entities.Remove(entityID); - _entityFlags[entityID] = false; - _count--; - _poolRunners.del.OnComponentDel(entityID); - // } - } - #endregion - - #region WorldCallbacks - protected override void OnWorldResize(int newSize) - { - Array.Resize(ref _entityFlags, newSize); - Array.Resize(ref _items, newSize); - } - protected override void OnDestroy() { } - #endregion - } - - public interface IEcsRelationComponent - { - public EcsEntity First { get; set; } - public EcsEntity Second { get; set; } - } - public static class IEcsRelationComponentExt - { - public static void Set(this ref T self, EcsEntity first, EcsEntity second) where T : struct, IEcsRelationComponent - { - self.First = first; - self.Second = second; - } - } - public static class EcsRelationPoolExt - { - public static EcsRelationPool GetPool(this EcsWorld self) where TRelationComponent : struct, IEcsRelationComponent - { - return self.GetPool>(); - } - - public static EcsRelationPool Include(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent - { - return self.Include>(); - } - public static EcsRelationPool Exclude(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent - { - return self.Exclude>(); - } - public static EcsRelationPool Optional(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent - { - return self.Optional>(); - } - } -} diff --git a/src/Pools/EcsSinglePool.cs b/src/Pools/EcsSinglePool.cs index 662a589..c3dacac 100644 --- a/src/Pools/EcsSinglePool.cs +++ b/src/Pools/EcsSinglePool.cs @@ -1,12 +1,17 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Runtime.CompilerServices; -using Unity.Profiling; namespace DCFApixels.DragonECS { - public sealed class EcsSinglePool : EcsPoolBase + using static EcsPoolThrowHalper; + public sealed class EcsSinglePool : IEcsPoolImplementation, IEnumerable //IntelliSense hack where T : struct, IEcsSingleComponent { + private EcsWorld _source; + private int _id; + private int[] _mapping; private int _count; @@ -21,77 +26,112 @@ namespace DCFApixels.DragonECS get => ref _component; } public int Count => _count; + int IEcsPool.Capacity => -1; + public int ComponentID => _id; + public Type ComponentType => typeof(T); + public EcsWorld World => _source; #endregion #region Init - protected override void Init(EcsWorld world) + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { + _source = world; + _id = componentID; + _mapping = new int[world.Capacity]; _count = 0; _poolRunners = new PoolRunners(world.Pipeline); } #endregion - #region Write/Read/Has/Del - private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add"); - private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write"); - private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read"); - private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); - private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); + #region Methods public ref T Add(int entityID) { - // using (_addMark.Auto()) - // { - if (_mapping[entityID] <= 0) - { - _mapping[entityID] = ++_count; - IncrementEntityComponentCount(entityID); - _poolRunners.add.OnComponentAdd(entityID); - } +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (Has(entityID)) ThrowAlreadyHasComponent(entityID); +#endif + _mapping[entityID] = ++_count; + this.IncrementEntityComponentCount(entityID); + _poolRunners.add.OnComponentAdd(entityID); return ref _component; - // } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Write(int entityID) { - // using (_writeMark.Auto()) +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif _poolRunners.write.OnComponentWrite(entityID); return ref _component; } + public ref T TryAddOrWrite(int entityID) + { + if (!Has(entityID)) + return ref Add(entityID); + return ref Write(entityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Read(int entityID) { - // using (_readMark.Auto()) +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif return ref _component; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sealed override bool Has(int entityID) + public bool Has(int entityID) { - // using (_hasMark.Auto()) return _mapping[entityID] > 0; } public void Del(int entityID) { - // using (_delMark.Auto()) - // { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif _mapping[entityID] = 0; _count--; - DecrementEntityComponentCount(entityID); + this.DecrementEntityComponentCount(entityID); _poolRunners.del.OnComponentDel(entityID); - // } + } + public void TryDel(int entityID) + { + if (Has(entityID)) Del(entityID); + } + public void Copy(int fromEntityID, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + TryAddOrWrite(toEntityID); } #endregion - #region WorldCallbacks - protected override void OnWorldResize(int newSize) + #region Callbacks + void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); } - protected override void OnDestroy() { } + 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) => Instance = (T)dataRaw; + object IEcsPool.GetRaw(int entityID) => Instance; + void IEcsPool.SetRaw(int entityID, object dataRaw) => Instance = (T)dataRaw; + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); #endregion } - /// Singleton component + /// Singleton component public interface IEcsSingleComponent { } public static class EcsSinglePoolExt { @@ -101,15 +141,15 @@ namespace DCFApixels.DragonECS return self.GetPool>(); } - public static EcsSinglePool Include(this EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent + public static EcsSinglePool Include(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent { return self.Include>(); } - public static EcsSinglePool Exclude(this EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent + public static EcsSinglePool Exclude(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent { return self.Exclude>(); } - public static EcsSinglePool Optional(this EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent + public static EcsSinglePool Optional(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent { return self.Optional>(); } diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index d19aa5b..923cb36 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -1,24 +1,38 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Runtime.CompilerServices; -using Unity.Profiling; namespace DCFApixels.DragonECS { - public sealed class EcsTagPool : EcsPoolBase + using static EcsPoolThrowHalper; + public sealed class EcsTagPool : IEcsPoolImplementation, IEnumerable //IntelliSense hack where T : struct, IEcsTagComponent { + private EcsWorld _source; + private int _id; + private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private int _count; private PoolRunners _poolRunners; + private T _fakeComponent; + #region Properites public int Count => _count; + int IEcsPool.Capacity => -1; + public int ComponentID => _id; + public Type ComponentType => typeof(T); + public EcsWorld World => _source; #endregion #region Init - protected override void Init(EcsWorld world) + void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { + _source = world; + _id = componentID; + _mapping = new bool[world.Capacity]; _count = 0; @@ -26,50 +40,125 @@ namespace DCFApixels.DragonECS } #endregion - #region Add/Has/Del - private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add"); - private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); - private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); + #region Method public void Add(int entityID) { - // using (_addMark.Auto()) - // { - if (_mapping[entityID] == false) +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (Has(entityID)) ThrowAlreadyHasComponent(entityID); +#endif + _count++; + _mapping[entityID] = true; + this.IncrementEntityComponentCount(entityID); + _poolRunners.add.OnComponentAdd(entityID); + } + public void TryAdd(int entityID) + { + if (!_mapping[entityID]) { _count++; _mapping[entityID] = true; - IncrementEntityComponentCount(entityID); + this.IncrementEntityComponentCount(entityID); _poolRunners.add.OnComponentAdd(entityID); } - // } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public sealed override bool Has(int entityID) + public bool Has(int entityID) { - // using (_hasMark.Auto()) return _mapping[entityID]; } public void Del(int entityID) { - // using (_delMark.Auto()) - // { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif _mapping[entityID] = false; _count--; - DecrementEntityComponentCount(entityID); + this.DecrementEntityComponentCount(entityID); _poolRunners.del.OnComponentDel(entityID); - // } + } + public void TryDel(int entityID) + { + if (Has(entityID)) Del(entityID); + } + public void Copy(int fromEntityID, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + TryAdd(toEntityID); + } + public void Set(int entityID, bool isHas) + { + if (isHas) + { + if (!Has(entityID)) + Add(entityID); + } + else + { + if (Has(entityID)) + Del(entityID); + } } #endregion - #region WorldCallbacks - protected override void OnWorldResize(int newSize) + #region Callbacks + void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); } - protected override void OnDestroy() { } + void IEcsPoolImplementation.OnWorldDestroy() { } + + void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + foreach (var entityID in buffer) + TryDel(entityID); + } + #endregion + + #region Other + ref T IEcsPool.Add(int entityID) + { + Add(entityID); + return ref _fakeComponent; + } + ref readonly T IEcsPool.Read(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif + return ref _fakeComponent; + } + ref T IEcsPool.Write(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif + return ref _fakeComponent; + } + void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID); + object IEcsPool.GetRaw(int entityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif + return _fakeComponent; + } + void IEcsPool.SetRaw(int entityID, object dataRaw) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(entityID)) ThrowNotHaveComponent(entityID); +#endif + } + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); #endregion } + /// Component without data public interface IEcsTagComponent { } public static class EcsTagPoolExt { @@ -78,15 +167,15 @@ namespace DCFApixels.DragonECS return self.GetPool>(); } - public static EcsTagPool Include(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent + public static EcsTagPool Include(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent { return self.Include>(); } - public static EcsTagPool Exclude(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent + public static EcsTagPool Exclude(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent { return self.Exclude>(); } - public static EcsTagPool Optional(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent + public static EcsTagPool Optional(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent { return self.Optional>(); } diff --git a/src/Utils/EcsRelationTableArchetype.cs b/src/Utils/EcsRelationTableArchetype.cs deleted file mode 100644 index 33ab50f..0000000 --- a/src/Utils/EcsRelationTableArchetype.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace DCFApixels.DragonECS.Internal -{ - public abstract class EcsRelationTableArchetypeBase - { - public EcsRelationTableArchetypeBase() - { - throw new TypeAccessException("Сreating instances of EcsRelationTableArchetype class is not available."); - } - } - public sealed class EcsRelationTableArchetype : EcsRelationTableArchetypeBase - where TLeftWorld : EcsWorld - where TRightWorld : EcsWorld - { } -} diff --git a/src/Utils/EntityLinkedList.cs b/src/Utils/EntityLinkedList.cs index f801865..7ff22ec 100644 --- a/src/Utils/EntityLinkedList.cs +++ b/src/Utils/EntityLinkedList.cs @@ -46,10 +46,10 @@ namespace DCFApixels.DragonECS /// new node index public int Insert(int nodeIndex, int entityID) { - _nodes[_count].Set(entityID, _nodes[nodeIndex].next); + _nodes[++_count].Set(entityID, _nodes[nodeIndex].next); _nodes[nodeIndex].next = _count; _lastNodeIndex = _count; - return _count++; + return _count; } public int Add(int entityID) => Insert(_lastNodeIndex, entityID); diff --git a/src/entlong.cs b/src/entlong.cs new file mode 100644 index 0000000..cb1f37c --- /dev/null +++ b/src/entlong.cs @@ -0,0 +1,169 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS +{ + using static entlong.ThrowHalper; + // uniqueID - 32 bits + // gen - 16 bits + // world - 16 bits + /// Strong identifier/Permanent entity identifier + [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] + public readonly struct entlong : IEquatable, IEquatable + { + public static readonly entlong NULL = default; + [FieldOffset(0)] + internal readonly long full; //Union + [FieldOffset(0)] + internal readonly int id; + [FieldOffset(4)] + internal readonly short gen; + [FieldOffset(6)] + internal readonly short world; + + #region Properties + public bool IsAlive + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => EcsWorld.Worlds[world].IsAlive(id, gen); + } + public bool IsNull + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this == NULL; + } + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsNotNull + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this != NULL; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public int ID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsAlive) ThrowIsNotAlive(this); +#endif + return id; + } + } + [EditorBrowsable(EditorBrowsableState.Never)] + public short Gen + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsAlive) ThrowIsNotAlive(this); +#endif + return gen; + } + } + public EcsWorld World + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsAlive) ThrowIsNotAlive(this); +#endif + return EcsWorld.Worlds[world]; + } + } + [EditorBrowsable(EditorBrowsableState.Never)] + public short WorldID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!IsAlive) ThrowIsNotAlive(this); +#endif + return world; + } + } + #endregion + + #region Constructors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public entlong(int id, short gen, short world) : this() + { + this.id = id; + this.gen = gen; + this.world = world; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal entlong(long full) : this() + { + this.full = full; + } + #endregion + + #region TryGetters + public bool TryGetID(out int id) + { + id = this.id; + return IsAlive; + } + public bool TryGetWorld(out EcsWorld world) + { + world = EcsWorld.Worlds[this.world]; + return IsAlive; + } + public bool TryUnpack(out EcsWorld world, out int id) + { + world = EcsWorld.Worlds[this.world]; + id = this.id; + return IsAlive; + } + #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} {(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 ==(in entlong a, in entlong b) => a.full == b.full; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(in entlong a, in entlong b) => a.full != b.full; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator long(in entlong a) => a.full; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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 identifier is null."); + else + throw new EcsFrameworkException("The entity is not alive."); + } + } + #endregion + } +}