diff --git a/DragonECS.csproj b/DragonECS.csproj index f0b44cf..41358e5 100644 --- a/DragonECS.csproj +++ b/DragonECS.csproj @@ -10,7 +10,7 @@ DCFApixels.DragonECS DragonECS - 0.9.1 + 0.9.9 DCFApixels ECS Framework for Game Engines with C# and .Net Platform DCFApixels diff --git a/package.json b/package.json index ace3df9..fcffa56 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "displayName": "DragonECS", "description": "C# Entity Component System Framework", "unity": "2020.3", - "version": "0.9.4", + "version": "0.9.9", "repository": { "type": "git", "url": "https://github.com/DCFApixels/DragonECS.git" diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index e1cfd1c..52344b5 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -10,7 +10,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "The process to run when EcsPipeline.Init() is called. Before Init")] - [MetaID("DE26527C92015AFDD4ECF4D81A4C946B")] + [MetaID("DragonECS_DE26527C92015AFDD4ECF4D81A4C946B")] public interface IEcsPreInit : IEcsProcess { void PreInit(); @@ -19,7 +19,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "The process to run when EcsPipeline.Init() is called. After PreInit")] - [MetaID("CC45527C9201DF82DCAAAEF33072F9EF")] + [MetaID("DragonECS_CC45527C9201DF82DCAAAEF33072F9EF")] public interface IEcsInit : IEcsProcess { void Init(); @@ -28,7 +28,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "The process to run when EcsPipeline.Run() is called.")] - [MetaID("9654527C9201BE75546322B9BB03C131")] + [MetaID("DragonECS_9654527C9201BE75546322B9BB03C131")] public interface IEcsRun : IEcsProcess { void Run(); @@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "The process to run when EcsPipeline.Destroy() is called.")] - [MetaID("4661527C9201EE669C6EB61B19899AE5")] + [MetaID("DragonECS_4661527C9201EE669C6EB61B19899AE5")] public interface IEcsDestroy : IEcsProcess { void Destroy(); @@ -62,7 +62,7 @@ namespace DCFApixels.DragonECS.Internal [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "...")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("3273527C9201285BAA0A463F700A50FB")] + [MetaID("DragonECS_3273527C9201285BAA0A463F700A50FB")] internal sealed class EcsPreInitRunner : EcsRunner, IEcsPreInit { private RunHelper _helper; @@ -83,7 +83,7 @@ namespace DCFApixels.DragonECS.Internal [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "...")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("ED85527C9201A391AB8EC0B734917859")] + [MetaID("DragonECS_ED85527C9201A391AB8EC0B734917859")] internal sealed class EcsInitRunner : EcsRunner, IEcsInit { private RunHelper _helper; @@ -104,7 +104,7 @@ namespace DCFApixels.DragonECS.Internal [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "...")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("2098527C9201F260C840BFD50BC7E0BA")] + [MetaID("DragonECS_2098527C9201F260C840BFD50BC7E0BA")] internal sealed class EcsRunRunner : EcsRunner, IEcsRun { private readonly struct Pair @@ -150,9 +150,10 @@ namespace DCFApixels.DragonECS.Internal catch (Exception e) { #if DRAGONECS_DISABLE_CATH_EXCEPTIONS - throw; -#endif + throw e; +#else EcsDebug.PrintError(e); +#endif } finally { @@ -184,7 +185,7 @@ namespace DCFApixels.DragonECS.Internal [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "...")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("06A6527C92010430ACEB3DA520F272CC")] + [MetaID("DragonECS_06A6527C92010430ACEB3DA520F272CC")] internal sealed class EcsDestroyRunner : EcsRunner, IEcsDestroy { private RunHelper _helper; diff --git a/src/Builtin/Worlds.cs b/src/Builtin/Worlds.cs index e7697be..75e5e4d 100644 --- a/src/Builtin/Worlds.cs +++ b/src/Builtin/Worlds.cs @@ -10,12 +10,13 @@ namespace DCFApixels.DragonECS [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.WORLDS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Inherits EcsWorld without extending its functionality and is used for specific injections. Can be used to store regular game entities, can also be used as a single world in the game for all entities.")] [DebuggerTypeProxy(typeof(DebuggerProxy))] - [MetaID("4EE3527C92015BAB0299CB7B4E2663D1")] + [MetaID("DragonECS_4EE3527C92015BAB0299CB7B4E2663D1")] public sealed class EcsDefaultWorld : EcsWorld, IInjectionUnit { - public EcsDefaultWorld() : base() { } - public EcsDefaultWorld(EcsWorldConfig config = null, string name = null, short worldID = -1) : base(config, name == null ? "Default" : name, worldID) { } - public EcsDefaultWorld(IConfigContainer configs, string name = null, short worldID = -1) : base(configs, name == null ? "Default" : name, worldID) { } + private const string DEFAULT_NAME = "Default"; + public EcsDefaultWorld() : base(default(EcsWorldConfig), DEFAULT_NAME) { } + public EcsDefaultWorld(EcsWorldConfig config = null, string name = null, short worldID = -1) : base(config, name == null ? DEFAULT_NAME : name, worldID) { } + public EcsDefaultWorld(IConfigContainer configs, string name = null, short worldID = -1) : base(configs, name == null ? DEFAULT_NAME : name, worldID) { } void IInjectionUnit.InitInjectionNode(InjectionGraph nodes) { nodes.AddNode(this); } } /// EcsWrold for store event entities. @@ -23,12 +24,13 @@ namespace DCFApixels.DragonECS [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.WORLDS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Inherits EcsWorld without extending its functionality and is used for specific injections. Can be used to store event entities.")] [DebuggerTypeProxy(typeof(DebuggerProxy))] - [MetaID("D7CE527C920160BCD765EFA72DBF8B89")] + [MetaID("DragonECS_D7CE527C920160BCD765EFA72DBF8B89")] public sealed class EcsEventWorld : EcsWorld, IInjectionUnit { - public EcsEventWorld() : base() { } - public EcsEventWorld(EcsWorldConfig config = null, string name = null, short worldID = -1) : base(config, name == null ? "Events" : name, worldID) { } - public EcsEventWorld(IConfigContainer configs, string name = null, short worldID = -1) : base(configs, name == null ? "Events" : name, worldID) { } + private const string DEFAULT_NAME = "Events"; + public EcsEventWorld() : base(default(EcsWorldConfig), DEFAULT_NAME) { } + public EcsEventWorld(EcsWorldConfig config = null, string name = null, short worldID = -1) : base(config, name == null ? DEFAULT_NAME : name, worldID) { } + public EcsEventWorld(IConfigContainer configs, string name = null, short worldID = -1) : base(configs, name == null ? DEFAULT_NAME : name, worldID) { } void IInjectionUnit.InitInjectionNode(InjectionGraph nodes) { nodes.AddNode(this); } } } diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 7334897..5d70cfe 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -400,7 +400,7 @@ namespace DCFApixels.DragonECS { if (++_count >= _dense.Length) { - Array.Resize(ref _dense, ArrayUtility.NormalizeSizeToPowerOfTwo(_count << 1)); + Array.Resize(ref _dense, ArrayUtility.NextPow2(_count << 1)); } _dense[_count] = entityID; @@ -531,7 +531,7 @@ namespace DCFApixels.DragonECS { if (minSize >= _dense.Length) { - Array.Resize(ref _dense, ArrayUtility.NormalizeSizeToPowerOfTwo_ClampOverflow(minSize)); + Array.Resize(ref _dense, ArrayUtility.NextPow2_ClampOverflow(minSize)); } } @@ -618,7 +618,7 @@ namespace DCFApixels.DragonECS { if (dynamicBuffer.Length < _count) { - Array.Resize(ref dynamicBuffer, ArrayUtility.NormalizeSizeToPowerOfTwo(_count)); + Array.Resize(ref dynamicBuffer, ArrayUtility.NextPow2(_count)); } int i = 0; foreach (var e in this) diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index 58bc198..48c26c4 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -97,7 +97,7 @@ namespace DCFApixels.DragonECS { if (dynamicBuffer.Length < _values.Length) { - Array.Resize(ref dynamicBuffer, ArrayUtility.NormalizeSizeToPowerOfTwo(_values.Length)); + Array.Resize(ref dynamicBuffer, ArrayUtility.NextPow2(_values.Length)); } int i = 0; foreach (var e in this) @@ -246,7 +246,7 @@ namespace DCFApixels.DragonECS { if (dynamicBuffer.Length < _source.Count) { - Array.Resize(ref dynamicBuffer, ArrayUtility.NormalizeSizeToPowerOfTwo(_source.Count)); + Array.Resize(ref dynamicBuffer, ArrayUtility.NextPow2(_source.Count)); } int i = 0; foreach (var e in this) diff --git a/src/DataInterfaces.cs b/src/DataInterfaces.cs index c2ce421..3a46db5 100644 --- a/src/DataInterfaces.cs +++ b/src/DataInterfaces.cs @@ -3,7 +3,7 @@ #endif using System.Runtime.CompilerServices; -namespace DCFApixels.DragonECS +namespace DCFApixels.DragonECS.Core { #region IEcsWorldComponent public interface IEcsWorldComponent @@ -44,11 +44,11 @@ namespace DCFApixels.DragonECS void Enable(ref T component); void Disable(ref T component); } - public static class EcsComponentResetHandler + public static class EcsComponentLifecycleHandler { public static readonly IEcsComponentLifecycle instance; public static readonly bool isHasHandler; - static EcsComponentResetHandler() + static EcsComponentLifecycleHandler() { T def = default; if (def is IEcsComponentLifecycle intrf) diff --git a/src/DebugUtils/EcsDebug.cs b/src/DebugUtils/EcsDebug.cs index 3eac63d..0f5608e 100644 --- a/src/DebugUtils/EcsDebug.cs +++ b/src/DebugUtils/EcsDebug.cs @@ -1,16 +1,18 @@ #if DISABLE_DEBUG #undef DEBUG #endif +using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Internal; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; +using static DCFApixels.DragonECS.EcsConsts; namespace DCFApixels.DragonECS { - using static EcsConsts; + #region EcsProfilerMarker public readonly struct EcsProfilerMarker { #if DEBUG || DRAGONECS_ENABLE_DEBUG_SERVICE @@ -75,13 +77,15 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator EcsProfilerMarker(string markerName) { return new EcsProfilerMarker(markerName); } } + #endregion [MetaColor(MetaColor.DragonRose)] [MetaGroup(PACK_GROUP, DEBUG_GROUP)] [MetaDescription(AUTHOR, "Debugging utility. To modify or change the behavior, create a new class inherited from DebugService and set this service using DebugService.Set().")] - [MetaID("10A4587C92013B55820D8604D718A1C3")] + [MetaID("DragonECS_10A4587C92013B55820D8604D718A1C3")] public static class EcsDebug { + #region Set public static void Set() where T : DebugService, new() { DebugService.Set(); @@ -90,7 +94,12 @@ namespace DCFApixels.DragonECS { DebugService.Set(service); } + #endregion + #region Print +#if UNITY_2021_3_OR_NEWER + [UnityEngine.HideInCallstack] +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void PrintWarning(object v) { @@ -99,6 +108,9 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.PrintWarning(v); #endif } +#if UNITY_2021_3_OR_NEWER + [UnityEngine.HideInCallstack] +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void PrintError(object v) { @@ -107,6 +119,9 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.PrintError(v); #endif } +#if UNITY_2021_3_OR_NEWER + [UnityEngine.HideInCallstack] +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void PrintErrorAndBreak(object v) { @@ -115,6 +130,9 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.PrintErrorAndBreak(v); #endif } +#if UNITY_2021_3_OR_NEWER + [UnityEngine.HideInCallstack] +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void PrintPass(object v) { @@ -123,6 +141,9 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.PrintPass(v); #endif } +#if UNITY_2021_3_OR_NEWER + [UnityEngine.HideInCallstack] +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Print() { @@ -131,6 +152,9 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.Print(); #endif } +#if UNITY_2021_3_OR_NEWER + [UnityEngine.HideInCallstack] +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Print(object v) { @@ -139,6 +163,9 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.Print(v); #endif } +#if UNITY_2021_3_OR_NEWER + [UnityEngine.HideInCallstack] +#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Print(string tag, object v) { @@ -147,6 +174,9 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.Print(tag, v); #endif } + #endregion + + #region Other [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Break() { @@ -154,9 +184,12 @@ namespace DCFApixels.DragonECS DebugService.CurrentThreadInstance.Break(); #endif } + #endregion + #region Events public static OnPrintHandler OnPrint = delegate { }; public delegate void OnPrintHandler(string tag, object v); + #endregion } //------------------------------------------------------------------------------------------------------------// @@ -164,19 +197,23 @@ namespace DCFApixels.DragonECS public abstract class DebugService { private static DebugService _instance; - private static object _lock = new object(); + private readonly static object _lock = new object(); - private static HashSet _threadServiceClonesSet = new HashSet(); + private readonly static HashSet _threadServiceClonesSet = new HashSet(); [ThreadStatic] private static DebugService _currentThreadInstanceClone; [ThreadStatic] private static DebugService _currentThreadInstance; // для сравнения - private static IdDispenser _idDispenser = new IdDispenser(16, 0); - private static Dictionary _nameIdTable = new Dictionary(); + private readonly static IdDispenser _idDispenser = new IdDispenser(16, 0); + private readonly static Dictionary _nameIdTable = new Dictionary(); #region Properties + public static bool IsNullOrDefault + { + get { return _instance == null || _instance is NullDebugService || _instance is DefaultDebugService; } + } public static DebugService Instance { get { return _instance; } @@ -207,10 +244,14 @@ namespace DCFApixels.DragonECS } #endregion - #region Constructors + #region Static Constructor static DebugService() { +#if !UNITY_5_3_OR_NEWER + Set(new NullDebugService()); +#else Set(new DefaultDebugService()); +#endif } #endregion @@ -241,15 +282,17 @@ namespace DCFApixels.DragonECS { service.OnNewProfilerMark(record.Value, record.Key); } - service.OnServiceSetup(oldService); + oldService?.OnDisableBaseService(service); + service.OnEnableBaseService(oldService); OnServiceChanged(service); } } } #endregion - #region OnServiceSetup/CreateThreadInstance - protected virtual void OnServiceSetup(DebugService oldService) { } + #region OnEnable/OnDisable/CreateThreadInstance + protected virtual void OnEnableBaseService(DebugService prevService) { } + protected virtual void OnDisableBaseService(DebugService nextService) { } protected abstract DebugService CreateThreadInstance(); #endregion @@ -328,10 +371,17 @@ namespace DCFApixels.DragonECS } #endregion - public static OnServiceChangedHandler OnServiceChanged = delegate { }; + #region Events + public static event OnServiceChangedHandler OnServiceChanged = delegate { }; public delegate void OnServiceChangedHandler(DebugService service); + #endregion } +} + +namespace DCFApixels.DragonECS.Core +{ + #region DebugServiceExtensions public static class DebugServiceExtensions { public static void PrintWarning(this DebugService self, object v) @@ -361,7 +411,9 @@ namespace DCFApixels.DragonECS } //TODO PrintJson возможно будет добавлено когда-то } + #endregion + #region DefaultServices //------------------------------------------------------------------------------------------------------------// public sealed class NullDebugService : DebugService @@ -379,7 +431,6 @@ namespace DCFApixels.DragonECS public sealed class DefaultDebugService : DebugService { -#if !UNITY_5_3_OR_NEWER private const string PROFILER_MARKER = "ProfilerMark"; private const string PROFILER_MARKER_CACHE = "[" + PROFILER_MARKER + "] "; @@ -548,14 +599,6 @@ namespace DCFApixels.DragonECS return this.AutoToString(); } } -#else - protected sealed override DebugService CreateThreadInstance() { return this; } - public sealed override void Break() { } - public sealed override void Print(string tag, object v) { } - public sealed override void ProfilerMarkBegin(int id) { } - public sealed override void ProfilerMarkEnd(int id) { } - protected sealed override void OnDelProfilerMark(int id) { } - protected sealed override void OnNewProfilerMark(int id, string name) { } -#endif } + #endregion } \ No newline at end of file diff --git a/src/DebugUtils/EcsDebugUtility.cs b/src/DebugUtils/EcsDebugUtility.cs index 9d9e3ed..f7303dd 100644 --- a/src/DebugUtils/EcsDebugUtility.cs +++ b/src/DebugUtils/EcsDebugUtility.cs @@ -1,6 +1,7 @@ #if DISABLE_DEBUG #undef DEBUG #endif +using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; #if DEBUG || !REFLECTION_DISABLED @@ -312,6 +313,9 @@ namespace DCFApixels.DragonECS { public static TypeMeta GetMeta(this object self) { +#if DEBUG && DRAGONECS_DEEP_DEBUG + if (self is Type type) { Throw.DeepDebugException(); } +#endif return EcsDebugUtility.GetTypeMeta(self); } public static TypeMeta ToMeta(this Type self) diff --git a/src/DebugUtils/MetaAttributes/MetaDescriptionAttribute.cs b/src/DebugUtils/MetaAttributes/MetaDescriptionAttribute.cs index 4119212..6b5352a 100644 --- a/src/DebugUtils/MetaAttributes/MetaDescriptionAttribute.cs +++ b/src/DebugUtils/MetaAttributes/MetaDescriptionAttribute.cs @@ -28,6 +28,7 @@ namespace DCFApixels.DragonECS { get { return string.IsNullOrEmpty(Author) == false; } } + public MetaDescription(string text) : this(null, text) { } public MetaDescription(string author, string text) { if (author == null) { author = string.Empty; } diff --git a/src/DebugUtils/MetaAttributes/MetaGroupAttribute.cs b/src/DebugUtils/MetaAttributes/MetaGroupAttribute.cs index f2545d7..832c3da 100644 --- a/src/DebugUtils/MetaAttributes/MetaGroupAttribute.cs +++ b/src/DebugUtils/MetaAttributes/MetaGroupAttribute.cs @@ -12,54 +12,67 @@ namespace DCFApixels.DragonECS [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] public sealed class MetaGroupAttribute : EcsMetaAttribute { - public const char SEPARATOR = '/'; - public readonly MetaGroup Data; + public const char SEPARATOR = MetaGroup.SEPARATOR; + public readonly string Name = string.Empty; [Obsolete(EcsMetaAttributeHalper.EMPTY_NO_SENSE_MESSAGE)] - public MetaGroupAttribute() { Data = MetaGroup.Empty; } - public MetaGroupAttribute(string name) { Data = new MetaGroup(name); } - public MetaGroupAttribute(params string[] path) : this(string.Join(SEPARATOR, path)) { } + public MetaGroupAttribute() { } + public MetaGroupAttribute(string name) { Name = name; } + public MetaGroupAttribute(params string[] path) { Name = string.Join(SEPARATOR, path); } } [DebuggerDisplay("{Name}")] public class MetaGroup { - public const char SEPARATOR = MetaGroupAttribute.SEPARATOR; + public const char SEPARATOR = '/'; + private const string SEPARATOR_STR = "/"; public const string UNGROUPED = ""; private const string PATTERN = @"Module(?=/)"; public static readonly MetaGroup Empty = new MetaGroup(UNGROUPED); public readonly string Name; - private string[] _path = null; + private string[] _splited = null; public IReadOnlyCollection Splited { get { - if (_path == null) + if (_splited == null) { - _path = EcsMetaAttributeHalper.Split(SEPARATOR, Name); + _splited = EcsMetaAttributeHalper.Split(SEPARATOR, Name); } - return _path; + return _splited; } } public bool IsEmpty { get { return this == Empty; } } - public MetaGroup(string name) + private MetaGroup(string name) { if (string.IsNullOrEmpty(name)) { Name = UNGROUPED; return; } - name = name.Replace('\\', SEPARATOR); + name = Regex.Replace(name, @"(\s*[\/\\]+\s*)+", SEPARATOR_STR).Trim(); if (name[name.Length - 1] != SEPARATOR) { name += SEPARATOR; } + if (name[0] == SEPARATOR) + { + name = name.Substring(1); + } Name = Regex.Replace(name, PATTERN, ""); Name = string.Intern(Name); } + public static MetaGroup FromName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return Empty; + } + return new MetaGroup(name); + } public static MetaGroup FromNameSpace(Type type) { if (string.IsNullOrWhiteSpace(type.Namespace)) diff --git a/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs b/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs index 55f4b71..c42f9bf 100644 --- a/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs +++ b/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs @@ -4,6 +4,10 @@ using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Internal; using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; @@ -17,62 +21,384 @@ namespace DCFApixels.DragonECS { if (string.IsNullOrEmpty(id)) { - Throw.ArgumentNull(nameof(id)); + EcsDebug.PrintError("The identifier cannot be empty or null"); + id = string.Empty; } - if (MetaID.IsGenericID(id) == false) + if (MetaID.IsValidID(id) == false) { - Throw.ArgumentException($"Identifier {id} contains invalid characters: ,<>"); + EcsDebug.PrintError($"Identifier {id} contains invalid characters. Allowed charset: {MetaID.ALLOWED_CHARSET}"); + id = string.Empty; } - id = string.Intern(id); ID = id; } } - public static class MetaID + public static unsafe class MetaID { - [ThreadStatic] - private static Random _randon; - [ThreadStatic] - private static byte[] _buffer; - [ThreadStatic] - private static bool _isInit; + public const string ALLOWED_CHARSET = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + //public static bool IsFixedNameType(Type type) + //{ + // if (type.IsPrimitive) + // { + // return true; + // } + // if(type == typeof(string)) + // { + // return true; + // } + // return false; + //} public static bool IsGenericID(string id) { - return Regex.IsMatch(id, @"^[^,<>\s]*$"); + return id[id.Length - 1] == '>' || Regex.IsMatch(id, @"^[^,<>\s]*$"); + } + public static bool IsValidID(string id) + { + return Regex.IsMatch(id, @"^[a-zA-Z0-9_]+$"); } - public static unsafe string GenerateNewUniqueID() + + [ThreadStatic] + private static uint _randonState; + public static string GenerateNewUniqueID() { - if (_isInit == false) + const int BYTES = 16; + const int CHARS = BYTES * 2; + const string CHARSET = "0123456789ABCDEF"; + + if (_randonState == 0) { IntPtr prt = Marshal.AllocHGlobal(1); long alloc = (long)prt; - Marshal.Release(prt); - _randon = new Random((int)alloc); - _buffer = new byte[8]; - _isInit = true; + Marshal.FreeHGlobal(prt); + _randonState = (uint)alloc ^ (uint)DateTime.Now.Millisecond; } - byte* hibits = stackalloc byte[8]; - long* hibitsL = (long*)hibits; - hibitsL[0] = DateTime.Now.Ticks; - hibitsL[1] = _randon.Next(); + byte* bytes = stackalloc byte[BYTES]; + Span x = new Span(bytes, BYTES); + long* bytesLong = (long*)bytes; + uint* bytesUInt = (uint*)bytes; + bytesLong[0] = DateTime.Now.Ticks; + _randonState = BitsUtility.NextXorShiftState(_randonState); + bytesUInt[2] = _randonState; + _randonState = BitsUtility.NextXorShiftState(_randonState); + bytesUInt[3] = _randonState; - for (int i = 0; i < 8; i++) + + char* str = stackalloc char[CHARS]; + for (int i = 0, j = 0; i < BYTES; i++) { - _buffer[i] = hibits[i]; + byte b = bytes[i]; + str[j++] = CHARSET[b & 0x0000_000F]; + str[j++] = CHARSET[(b >> 4) & 0x0000_000F]; } - return BitConverter.ToString(_buffer).Replace("-", ""); + return new string(str, 0, CHARS); } public static string IDToAttribute(string id) { - return $"[MetaID(\"id\")]"; + return $"[MetaID(\"{id}\")]"; } + public static string ConvertIDToTypeName(string id) + { + id = id.Replace("_1", "__"); + id = id.Replace("_2", "__"); + id = id.Replace("_3", "__"); + + id = id.Replace("<", "_1"); + id = id.Replace(">", "_2"); + id = id.Replace(",", "_3"); + return "_" + id; + } + public static string ParseIDFromTypeName(string name) + { + char* buffer = TempBuffer.Get(name.Length); + int count = 0; + //skip name[0] char + for (int i = 1, iMax = name.Length; i < iMax; i++) + { + char current = name[i]; + if (current == '_') + { + if (++i >= iMax) { break; } + current = name[i]; + switch (current) + { + case '1': current = '<'; break; + case '2': current = '>'; break; + case '3': current = ','; break; + } + } + buffer[count++] = current; + } + return new string(buffer, 0, count); + } + public static string GenerateNewUniqueIDWithAttribute() { return IDToAttribute(GenerateNewUniqueID()); } + + + public static bool TryFindMetaIDCollisions(IEnumerable metas, out CollisionList collisions) + { + collisions = new CollisionList(metas); + return collisions.IsHasAnyCollision; + } + public static CollisionList FindMetaIDCollisions(IEnumerable metas) + { + return new CollisionList(metas); + } + + #region CollisionList + [DebuggerTypeProxy(typeof(DebuggerProxy))] + [DebuggerDisplay("HasAnyCollision: {IsHasAnyCollision} ListsCount: {Count}")] + public class CollisionList : IEnumerable + { + private LinkedList[] _linkedLists; + private Entry[] _entries; + private int _collisionsCount; + private int _listsCount; + private HashSet _collidingIDs; + + public int CollisionsCount + { + get { return _collisionsCount; } + } + public int Count + { + get { return _listsCount; } + } + public bool IsHasAnyCollision + { + get { return _listsCount > 0; } + } + public Collision this[int index] + { + get + { + var list = _linkedLists[index]; + return new Collision(this, list.headNode, list.count); + } + } + + public bool IsCollidingID(string id) + { + if(_collidingIDs== null) + { + return false; + } + return _collidingIDs.Contains(id); + } + + public CollisionList(IEnumerable metas) + { + var metasCount = metas.Count(); + Dictionary listIndexes = new Dictionary(metasCount); + _linkedLists = new LinkedList[metasCount]; + _entries = new Entry[metasCount]; + + bool hasCollision = false; + + _listsCount = 0; + foreach (var meta in metas) + { + if (meta.IsHasMetaID() == false) { continue; } + if (listIndexes.TryGetValue(meta.MetaID, out int headIndex)) + { + hasCollision = true; + } + else + { + headIndex = _listsCount++; + listIndexes.Add(meta.MetaID, headIndex); + } + int nodeIndex = _collisionsCount++; + + ref var list = ref _linkedLists[headIndex]; + ref Entry entry = ref _entries[nodeIndex]; + if (list.count == 0) + { + entry.next = -1; + } + else + { + entry.next = list.headNode; + } + entry.meta = meta; + list.headNode = nodeIndex; + listIndexes[meta.MetaID] = headIndex; + list.count++; + } + + if (hasCollision) + { + _collidingIDs = new HashSet(); + for (int i = 0; i < _listsCount; i++) + { + ref var list = ref _linkedLists[i]; + if (list.count <= 1) + { + _linkedLists[i--] = _linkedLists[--_listsCount]; + } + } + for (int i = 0; i < _listsCount; i++) + { + _collidingIDs.Add(this[i].MetaID); + } + } + else + { + _listsCount = 0; + } + } + + [DebuggerDisplay("Count: {count}")] + private struct LinkedList + { + public int count; + public int headNode; + } + [DebuggerDisplay("ID: {meta.MetaID} next: {next}")] + public struct Entry + { + public TypeMeta meta; + public int next; + } + + #region Enumerator + public Enumerator GetEnumerator() { return new Enumerator(this, _listsCount); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public struct Enumerator : IEnumerator + { + private readonly CollisionList _collisions; + private readonly int _count; + private int _index; + public Collision Current + { + get + { + var list = _collisions._linkedLists[_index]; + return new Collision(_collisions, list.headNode, list.count); + } + } + object IEnumerator.Current { get { return Current; } } + public Enumerator(CollisionList collisions, int count) + { + _collisions = collisions; + _count = count; + _index = -1; + } + public bool MoveNext() { return ++_index < _count; } + public void Dispose() { } + public void Reset() { _index = -1; } + } + #endregion + + [DebuggerDisplay("Count: {Count}")] + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public readonly struct Collision : IEnumerable + { + private readonly CollisionList _collisions; + private readonly string _metaID; + private readonly int _head; + private readonly int _count; + public int Count + { + get { return _count; } + } + public string MetaID + { + get { return _metaID; } + } + internal Collision(CollisionList collisions, int head, int count) + { + _collisions = collisions; + if (count == 0) + { + _head = 0; + _metaID = string.Empty; + } + else + { + _head = head; + _metaID = collisions._entries[_head].meta.MetaID; + } + _head = head; + _count = count; + } + + #region Enumerator + public Enumerator GetEnumerator() { return new Enumerator(_collisions._entries, _head); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public struct Enumerator : IEnumerator + { + private readonly Entry[] _linkedEntries; + private readonly int _head; + private int _nextIndex; + private int _index; + public TypeMeta Current { get { return _linkedEntries[_index].meta; } } + object IEnumerator.Current { get { return Current; } } + public Enumerator(Entry[] linkedEntries, int head) + { + _linkedEntries = linkedEntries; + _head = head; + _nextIndex = _head; + _index = -1; + } + public bool MoveNext() + { + if (_nextIndex < 0) { return false; } + _index = _nextIndex; + _nextIndex = _linkedEntries[_index].next; + return true; + } + public void Dispose() { } + public void Reset() + { + _nextIndex = _head; + _index = -1; + } + } + #endregion + + #region DebuggerProxy + private class DebuggerProxy + { + public Type[] Types; + public DebuggerProxy(Collision collision) + { + Types = collision.Select(o => o.Type).ToArray(); + } + } + #endregion + } + #region DebuggerProxy + private class DebuggerProxy + { + public string[][] Lists; + public DebuggerProxy(CollisionList collisions) + { + Lists = new string[collisions.Count][]; + int i = 0; + foreach (var list in collisions) + { + int j = 0; + Lists[i] = new string[list.Count]; + foreach (var typeMeta in list) + { + Lists[i][j] = typeMeta.MetaID; + j++; + } + i++; + } + } + } + #endregion + } + #endregion } } \ No newline at end of file diff --git a/src/DebugUtils/TypeMeta.cs b/src/DebugUtils/TypeMeta.cs index 1a822ba..8eceb19 100644 --- a/src/DebugUtils/TypeMeta.cs +++ b/src/DebugUtils/TypeMeta.cs @@ -3,8 +3,10 @@ #endif using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.PoolsCore; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; #if DEBUG || !REFLECTION_DISABLED @@ -36,14 +38,14 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.DEBUG_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Intended for extending meta information of types, for customization of type display in the editor. You can get it by using the object.GetMeta() or Type.ToMeta() extension method. Meta information is collected from meta attributes.")] - [MetaID("248D587C9201EAEA881F27871B4D18A6")] + [MetaID("DragonECS_248D587C9201EAEA881F27871B4D18A6")] [DebuggerTypeProxy(typeof(DebuggerProxy))] public sealed class TypeMeta : ITypeMeta { private const string NULL_NAME = "NULL"; public static readonly TypeMeta NullTypeMeta; - private static object _lock = new object(); + private static readonly object _lock = new object(); private static readonly Dictionary _metaCache = new Dictionary(); private static int _increment = 1; @@ -65,6 +67,10 @@ namespace DCFApixels.DragonECS private string _metaID; private EcsTypeCode _typeCode; + private bool _isProcess; + private bool _isComponent; + private bool _isPool; + private InitFlag _initFlags = InitFlag.None; #region Constructors @@ -78,9 +84,9 @@ namespace DCFApixels.DragonECS _name = NULL_NAME, _typeName = NULL_NAME, - _color = new MetaColor(MetaColor.Black), + _color = MetaColor.Black, _description = new MetaDescription("", NULL_NAME), - _group = new MetaGroup(""), + _group = MetaGroup.Empty, _tags = Array.Empty(), _metaID = string.Empty, _typeCode = EcsTypeCodeManager.Get(typeof(void)), @@ -270,6 +276,7 @@ namespace DCFApixels.DragonECS return _metaID; } } + public bool IsHasMetaID() { return string.IsNullOrEmpty(MetaID) == false; } #endregion #region TypeCode @@ -287,6 +294,45 @@ namespace DCFApixels.DragonECS } #endregion + #region ReflectionInfo + public bool IsComponent + { + get + { + if (_initFlags.HasFlag(InitFlag.ReflectionInfo) == false) + { + MetaGenerator.GetReflectionInfo(this); + _initFlags |= InitFlag.ReflectionInfo; + } + return _isComponent; + } + } + public bool IsProcess + { + get + { + if (_initFlags.HasFlag(InitFlag.ReflectionInfo) == false) + { + MetaGenerator.GetReflectionInfo(this); + _initFlags |= InitFlag.ReflectionInfo; + } + return _isProcess; + } + } + public bool IsPool + { + get + { + if (_initFlags.HasFlag(InitFlag.ReflectionInfo) == false) + { + MetaGenerator.GetReflectionInfo(this); + _initFlags |= InitFlag.ReflectionInfo; + } + return _isPool; + } + } + #endregion + #region InitializeAll public TypeMeta InitializeAll() { @@ -316,9 +362,10 @@ namespace DCFApixels.DragonECS Tags = 1 << 4, MetaID = 1 << 5, TypeCode = 1 << 6, - //MemberType = 1 << 7, + ReflectionInfo = 1 << 7, + //MemberType = 1 << 8, - All = Name | Group | Color | Description | Tags | TypeCode | MetaID //| MemberType + All = Name | Group | Color | Description | Tags | TypeCode | MetaID | ReflectionInfo //| MemberType } #endregion @@ -336,7 +383,17 @@ namespace DCFApixels.DragonECS return false; #endif } - public static bool IsHasMeta(Type type) + public static bool TryGetCustomMeta(Type type, out TypeMeta meta) + { + if (IsHasCustomMeta(type)) + { + meta = type.ToMeta(); + return true; + } + meta = null; + return false; + } + public static bool IsHasCustomMeta(Type type) { #if DEBUG || !REFLECTION_DISABLED return CheckEcsMemener(type) || Attribute.GetCustomAttributes(type, typeof(EcsMetaAttribute), false).Length > 0; @@ -348,7 +405,7 @@ namespace DCFApixels.DragonECS public static bool IsHasMetaID(Type type) { #if DEBUG || !REFLECTION_DISABLED - return type.HasAttribute(); + return TryGetCustomMeta(type, out TypeMeta meta) && meta.IsHasMetaID(); #else EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMetaID)} method does not work."); return false; @@ -404,6 +461,15 @@ namespace DCFApixels.DragonECS } #endregion + #region Obsolete + [Obsolete("Use TryGetCustomMeta(type)")] + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool IsHasMeta(Type type) + { + return IsHasCustomMeta(type); + } + #endregion + #region MetaGenerator private static class MetaGenerator { @@ -442,7 +508,6 @@ namespace DCFApixels.DragonECS #endregion #region GetColor - private static MetaColor AutoColor(TypeMeta meta) { int hash; @@ -463,7 +528,7 @@ namespace DCFApixels.DragonECS return (isCustom ? atr.color : AutoColor(meta), isCustom); #else EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetColor)} method does not work."); - return (AutoColor(type), false); + return (MetaColor.White, false); #endif } #endregion @@ -472,7 +537,14 @@ namespace DCFApixels.DragonECS public static MetaGroup GetGroup(Type type) { #if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает - return type.TryGetAttribute(out MetaGroupAttribute atr) ? atr.Data : MetaGroup.FromNameSpace(type); + if (type.TryGetAttribute(out MetaGroupAttribute atr)) + { + return MetaGroup.FromName(atr.Name); + } + else + { + return MetaGroup.FromNameSpace(type); + } #else EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetGroup)} method does not work."); return MetaGroup.Empty; @@ -507,9 +579,6 @@ namespace DCFApixels.DragonECS #endregion #region GetMetaID -#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает - private static Dictionary _idTypePairs = new Dictionary(); -#endif public static string GetMetaID(Type type) { #if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает @@ -524,22 +593,17 @@ namespace DCFApixels.DragonECS string id = atr.ID; if (type.IsGenericType && type.IsGenericTypeDefinition == false) { - var metaIds = type.GetGenericArguments().Select(o => GetMetaID(o)); - if (metaIds.Any(o => string.IsNullOrEmpty(o))) + var metaIDs = type.GetGenericArguments().Select(o => GetMetaID(o)); + if (metaIDs.Any(o => string.IsNullOrEmpty(o))) { id = string.Empty; } else { - id = $"{id}<{string.Join(", ", metaIds)}>"; + id = $"{id}<{string.Join(", ", metaIDs)}>"; } } - if (string.IsNullOrEmpty(id) == false && - _idTypePairs.TryGetValue(id, out Type otherType) && type != otherType) - { - Throw.Exception($"Types {type.ToMeta().TypeName} and {otherType.ToMeta().TypeName} have duplicate {atr.ID} MetaID."); - } - _idTypePairs[id] = type; + id = string.Intern(id); return id; } #else @@ -548,6 +612,15 @@ namespace DCFApixels.DragonECS #endif } #endregion + + #region GetReflectionInfo + public static void GetReflectionInfo(TypeMeta meta) + { + meta._isComponent = typeof(IEcsComponentMember).IsAssignableFrom(meta.Type); + meta._isProcess = typeof(IEcsProcess).IsAssignableFrom(meta.Type); + meta._isPool = typeof(IEcsPoolImplementation).IsAssignableFrom(meta.Type); + } + #endregion } #endregion } diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 1f56e26..c1dc6e1 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -125,7 +125,7 @@ namespace DCFApixels.DragonECS #endregion //Инициализация аспектов проходит в синхронизированном состоянии, поэтому использование _staticMaskCache потоко безопасно. - private static Dictionary _staticMaskCache = new Dictionary(); + private readonly static Dictionary _staticMaskCache = new Dictionary(); internal EcsWorld _source; internal EcsMask _mask; @@ -272,13 +272,13 @@ namespace DCFApixels.DragonECS public TPool IncludePool() where TPool : IEcsPoolImplementation, new() { var pool = CachePool(); - IncludeImplicit(pool.ComponentType); + SetMaskInclude(pool.ComponentType); return pool; } public TPool ExcludePool() where TPool : IEcsPoolImplementation, new() { var pool = CachePool(); - ExcludeImplicit(pool.ComponentType); + SetMaskExclude(pool.ComponentType); return pool; } public TPool OptionalPool() where TPool : IEcsPoolImplementation, new() @@ -291,14 +291,14 @@ namespace DCFApixels.DragonECS var pool = _world.GetPoolInstance(); return pool; } - private void IncludeImplicit(Type type) + public void SetMaskInclude(Type type) { if (_maskBuilder.IsNull == false) { _maskBuilder.Inc(type); } } - private void ExcludeImplicit(Type type) + public void SetMaskExclude(Type type) { if (_maskBuilder.IsNull == false) { @@ -334,8 +334,8 @@ namespace DCFApixels.DragonECS IncludePool(); ExcludePool(); OptionalPool(); - IncludeImplicit(null); - ExcludeImplicit(null); + SetMaskInclude(null); + SetMaskExclude(null); } #endregion } diff --git a/src/EcsMask.cs b/src/EcsMask.cs index d6b843b..56c42b3 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -488,7 +488,7 @@ namespace DCFApixels.DragonECS [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] #endif - public class EcsMaskIterator + public class EcsMaskIterator : IDisposable { // TODO есть идея перенести эти ChunckBuffer-ы в стек, // для этого нужно проработать дизайн так чтобы память в стеке выделялась за пределами итератора и GetEnumerator, @@ -555,6 +555,15 @@ namespace DCFApixels.DragonECS } } unsafe ~EcsMaskIterator() + { + Cleanup(false); + } + public void Dispose() + { + Cleanup(true); + GC.SuppressFinalize(this); + } + private void Cleanup(bool disposing) { _sortIncBuffer.ReadonlyDispose(); //_sortExcBuffer.ReadonlyDispose();// использует общую памяять с _sortIncBuffer; @@ -563,7 +572,7 @@ namespace DCFApixels.DragonECS } #endregion - #region SortConstraints + #region SortConstraints/TryFindEntityStorage private unsafe int SortConstraints_Internal() { UnsafeArray sortIncBuffer = _sortIncBuffer; @@ -611,7 +620,7 @@ namespace DCFApixels.DragonECS // Поэтому исключающее ограничение игнорируется для maxEntites. return maxEntites; } - private unsafe bool TryGetEntityStorage(out IEntityStorage storage) + private unsafe bool TryFindEntityStorage(out IEntityStorage storage) { if (_isHasAnyEntityStorage) { @@ -737,7 +746,7 @@ namespace DCFApixels.DragonECS { return new Enumerator(_span.Slice(0, 0), _iterator); } - if (_iterator.TryGetEntityStorage(out IEntityStorage storage)) + if (_iterator.TryFindEntityStorage(out IEntityStorage storage)) { return new Enumerator(storage.ToSpan(), _iterator); } @@ -811,6 +820,7 @@ namespace DCFApixels.DragonECS #region Iterate/Enumerable OnlyInc [MethodImpl(MethodImplOptions.AggressiveInlining)] public OnlyIncEnumerable IterateOnlyInc(EcsSpan span) { return new OnlyIncEnumerable(this, span); } + #if ENABLE_IL2CPP [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] @@ -952,29 +962,30 @@ namespace DCFApixels.DragonECS.Internal internal const int STACK_BUFFER_THRESHOLD = 100; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void ConvertToChuncks(EcsMaskChunck* ptr, UnsafeArray input, UnsafeArray output) + internal static void ConvertToChuncks(EcsMaskChunck* bufferPtr, UnsafeArray input, UnsafeArray output) { for (int i = 0; i < input.Length; i++) { - ptr[i] = EcsMaskChunck.FromID(input.ptr[i]); + bufferPtr[i] = EcsMaskChunck.FromID(input.ptr[i]); } - for (int inputI = 0, outputI = 0; outputI < output.Length; inputI++, ptr++) + for (int inputI = 0, outputI = 0; outputI < output.Length; inputI++, bufferPtr++) { - int maskX = ptr->mask; - if (maskX == 0) { continue; } - int chunkIndexX = ptr->chunkIndex; + int stackingMask = bufferPtr->mask; + if (stackingMask == 0) { continue; } + int stackingChunkIndex = bufferPtr->chunkIndex; - EcsMaskChunck* subptr = ptr; - for (int j = 1; j < input.Length - inputI; j++, subptr++) + EcsMaskChunck* bufferSpanPtr = bufferPtr + 1; + for (int j = 1; j < input.Length - inputI; j++, bufferSpanPtr++) { - if (subptr->chunkIndex == chunkIndexX) + if (bufferSpanPtr->chunkIndex == stackingChunkIndex) { - maskX |= subptr->mask; - *subptr = default; + stackingMask |= bufferSpanPtr->mask; + *bufferSpanPtr = default; } } - output.ptr[outputI] = new EcsMaskChunck(chunkIndexX, maskX); + + output.ptr[outputI] = new EcsMaskChunck(stackingChunkIndex, stackingMask); outputI++; } } diff --git a/src/EcsPipeline.Builder.cs b/src/EcsPipeline.Builder.cs index d66f6d3..f81533b 100644 --- a/src/EcsPipeline.Builder.cs +++ b/src/EcsPipeline.Builder.cs @@ -17,7 +17,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(PACK_GROUP, OTHER_GROUP)] [MetaDescription(AUTHOR, "...")] - [MetaID("FC38597C9201C15D1A14D133237BD67F")] + [MetaID("DragonECS_FC38597C9201C15D1A14D133237BD67F")] public interface IEcsDefaultAddParams { AddParams AddParams { get; } @@ -726,7 +726,7 @@ namespace DCFApixels.DragonECS #region SerializableTemplate public EcsPipelineTemplate GenerateSerializableTemplate() { - var it = new LinkedListIterator(_systemNodes, _systemNodesCount, _startIndex); + var it = new LinkedListCountIterator(_systemNodes, _systemNodesCount, _startIndex); EcsPipelineTemplate result = new EcsPipelineTemplate(); result.layers = new string[Layers.Count]; result.records = new EcsPipelineTemplate.Record[it.Count]; diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 6ad2331..0bebeec 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -14,6 +14,7 @@ using static DCFApixels.DragonECS.EcsConsts; namespace DCFApixels.DragonECS { public interface IEcsMember { } + public interface IEcsComponentMember : IEcsMember { } public interface INamedMember { string Name { get; } @@ -22,7 +23,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(PACK_GROUP, OTHER_GROUP)] [MetaDescription(AUTHOR, "...")] - [MetaID("F064557C92010419AB677453893D00AE")] + [MetaID("DragonECS_F064557C92010419AB677453893D00AE")] public interface IEcsPipelineMember : IEcsProcess { EcsPipeline Pipeline { get; set; } @@ -31,7 +32,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(PACK_GROUP, OTHER_GROUP)] [MetaDescription(AUTHOR, "Container and engine for systems. Responsible for configuring the execution order of systems, providing a mechanism for messaging between systems, and a dependency injection mechanism.")] - [MetaID("9F5A557C9201C5C3D9BCAC2FF1CC07D4")] + [MetaID("DragonECS_9F5A557C9201C5C3D9BCAC2FF1CC07D4")] public sealed partial class EcsPipeline { private readonly IConfigContainer _configs; @@ -113,20 +114,18 @@ namespace DCFApixels.DragonECS private static IEcsProcess[] _buffer; private T[] CreateProcess() where T : IEcsProcess { - if (_buffer == null || _buffer.Length < _allSystems.Length) - { - Array.Resize(ref _buffer, _allSystems.Length); - } - int l = 0; + ArrayUtility.UpsizeWithoutCopy(ref _buffer, _allSystems.Length); + int bufferLength = 0; for (int i = 0, iMax = _allSystems.Length; i < iMax; i++) { if (_allSystems[i] is T) { - _buffer[l++] = _allSystems[i]; + _buffer[bufferLength++] = _allSystems[i]; } } - T[] result = new T[l]; - Array.Copy(_buffer, result, l); + T[] result = new T[bufferLength]; + Array.Copy(_buffer, result, bufferLength); + Array.Clear(_buffer, 0, bufferLength); return result; } #endregion @@ -288,7 +287,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.Black)] [MetaGroup(PACK_GROUP, OTHER_GROUP)] [MetaDescription(AUTHOR, "An auxiliary type of system for dividing a pipeline into layers. This system is automatically added to the EcsPipeline.")] - [MetaID("42596C7C9201D0B85D1335E6E4704B57")] + [MetaID("DragonECS_42596C7C9201D0B85D1335E6E4704B57")] public class SystemsLayerMarkerSystem : IEcsProcess { public readonly string name; diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 1b457ae..227a53a 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -14,7 +14,7 @@ namespace DCFApixels.DragonECS [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "...")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("EF8A557C9201E6F04D4A76DC670BDE19")] + [MetaID("DragonECS_EF8A557C9201E6F04D4A76DC670BDE19")] public interface IEcsProcess : IEcsMember { } namespace RunnersCore @@ -69,7 +69,7 @@ namespace DCFApixels.DragonECS [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "...")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("E49B557C92010E46DF1602972BC988BC")] + [MetaID("DragonECS_E49B557C92010E46DF1602972BC988BC")] public interface IEcsRunner : IEcsProcess { EcsPipeline Pipeline { get; } @@ -84,7 +84,7 @@ namespace DCFApixels.DragonECS [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "...")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("7DB3557C9201F85E0E1C17D7B19D9CEE")] + [MetaID("DragonECS_7DB3557C9201F85E0E1C17D7B19D9CEE")] public abstract class EcsRunner : EcsRunner, IEcsRunner, IEcsProcess where TProcess : IEcsProcess { @@ -211,9 +211,10 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DRAGONECS_DISABLE_CATH_EXCEPTIONS - throw; -#endif + throw e; +#else EcsDebug.PrintError(e); +#endif } _markers[i].End(); } @@ -251,9 +252,10 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DRAGONECS_DISABLE_CATH_EXCEPTIONS - throw; -#endif + throw e; +#else EcsDebug.PrintError(e); +#endif } _markers[i].End(); } @@ -378,9 +380,10 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DRAGONECS_DISABLE_CATH_EXCEPTIONS - throw; -#endif + throw e; +#else EcsDebug.PrintError(e); +#endif } finally { @@ -433,9 +436,10 @@ namespace DCFApixels.DragonECS catch (Exception e) { #if DRAGONECS_DISABLE_CATH_EXCEPTIONS - throw; -#endif + throw e; +#else EcsDebug.PrintError(e); +#endif } finally { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index c0674d8..71cf329 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -48,7 +48,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.WORLDS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Container for entities and components.")] - [MetaID("AEF3557C92019C976FC48F90E95A9DA6")] + [MetaID("DragonECS_AEF3557C92019C976FC48F90E95A9DA6")] [DebuggerTypeProxy(typeof(DebuggerProxy))] public partial class EcsWorld : IEntityStorage, IEcsMember, INamedMember { @@ -65,6 +65,8 @@ namespace DCFApixels.DragonECS private int[] _delEntBuffer = Array.Empty(); private int _delEntBufferCount = 0; + private int[] _emptyEntities = Array.Empty(); + private int _emptyEntitiesCount = 0; private bool _isEnableAutoReleaseDelEntBuffer = true; internal int _entityComponentMaskLength; @@ -194,12 +196,12 @@ namespace DCFApixels.DragonECS _poolsMediator = new PoolsMediator(this); - int poolsCapacity = ArrayUtility.NormalizeSizeToPowerOfTwo(config.PoolsCapacity); + int poolsCapacity = ArrayUtility.NextPow2(config.PoolsCapacity); _pools = new IEcsPoolImplementation[poolsCapacity]; _poolSlots = new PoolSlot[poolsCapacity]; ArrayUtility.Fill(_pools, _nullPool); - int entitiesCapacity = ArrayUtility.NormalizeSizeToPowerOfTwo(config.EntitiesCapacity); + int entitiesCapacity = ArrayUtility.NextPow2(config.EntitiesCapacity); _entityDispenser = new IdDispenser(entitiesCapacity, 0, OnEntityDispenserResized); _executorCoures = new Dictionary<(Type, object), IQueryExecutorImplementation>(config.PoolComponentsCapacity); @@ -253,6 +255,38 @@ namespace DCFApixels.DragonECS { return Get>().Instance; } + public void GetAspects(out TAspect0 a0) + where TAspect0 : new() + { + a0 = GetAspect(); + } + public void GetAspects(out TAspect0 a0, out TAspect1 a1) + where TAspect0 : new() + where TAspect1 : new() + { + a0 = GetAspect(); + a1 = GetAspect(); + } + public void GetAspects(out TAspect0 a0, out TAspect1 a1, out TAspect2 a2) + where TAspect0 : new() + where TAspect1 : new() + where TAspect2 : new() + { + a0 = GetAspect(); + a1 = GetAspect(); + a2 = GetAspect(); + } + public void GetAspects(out TAspect0 a0, out TAspect1 a1, out TAspect2 a2, out TAspect3 a3) + where TAspect0 : new() + where TAspect1 : new() + where TAspect2 : new() + where TAspect3 : new() + { + a0 = GetAspect(); + a1 = GetAspect(); + a2 = GetAspect(); + a3 = GetAspect(); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TAspect GetAspect(out EcsMask mask) where TAspect : new() { @@ -327,6 +361,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public int NewEntity(int entityID) { + _entityDispenser.Upsize(entityID + 1); #if DEBUG if (IsUsed(entityID)) { Throw.World_EntityIsAlreadyСontained(entityID); } #elif DRAGONECS_STABILITY_MODE @@ -348,6 +383,7 @@ namespace DCFApixels.DragonECS slot.gen |= GEN_SLEEP_MASK; } _entityListeners.InvokeOnNewEntity(entityID); + MoveToEmptyEntities(entityID); } @@ -380,7 +416,7 @@ namespace DCFApixels.DragonECS public void DelEntity(int entityID) { #if DEBUG - if (IsUsed(entityID) == false) { Throw.World_EntityIsAlreadyСontained(entityID); } + if (IsUsed(entityID) == false) { Throw.World_EntityIsNotContained(entityID); } #elif DRAGONECS_STABILITY_MODE if (IsUsed(entityID) == false) { return; } #endif @@ -390,6 +426,24 @@ namespace DCFApixels.DragonECS _entitiesCount--; _entityListeners.InvokeOnDelEntity(entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToEmptyEntities(int entityID) + { + _emptyEntities[_emptyEntitiesCount++] = entityID; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool RemoveFromEmptyEntities(int entityID) + { + for (int i = _emptyEntitiesCount - 1; i >= 0; i--) + { + if(_emptyEntities[i] == entityID) + { + _emptyEntities[i] = _emptyEntities[--_emptyEntitiesCount]; + return true; + } + } + return false; + } #endregion #region Other @@ -477,20 +531,60 @@ namespace DCFApixels.DragonECS #elif DRAGONECS_STABILITY_MODE if (mask.WorldID != ID) { return false; } #endif - for (int i = 0, iMax = mask._incs.Length; i < iMax; i++) + + +#if DEBUG && DRAGONECS_DEEP_DEBUG + bool IsMatchesMaskDeepDebug(EcsMask mask_, int entityID_) { - if (!_pools[mask._incs[i]].Has(entityID)) + for (int i = 0, iMax = mask_._incs.Length; i < iMax; i++) { + if (!_pools[mask_._incs[i]].Has(entityID_)) + { + return false; + } + } + for (int i = 0, iMax = mask_._excs.Length; i < iMax; i++) + { + if (_pools[mask_._excs[i]].Has(entityID_)) + { + return false; + } + } + return true; + } + bool deepDebug = IsMatchesMaskDeepDebug(mask, entityID); +#endif + + var incChuncks = mask._incChunckMasks; + var excChuncks = mask._excChunckMasks; + var componentMaskStartIndex = entityID << _entityComponentMaskLengthBitShift; + + for (int i = 0; i < incChuncks.Length; i++) + { + var bit = incChuncks[i]; + if ((_entityComponentMasks[componentMaskStartIndex + bit.chunkIndex] & bit.mask) != bit.mask) + { +#if DEBUG && DRAGONECS_DEEP_DEBUG + if (false != deepDebug) { Throw.DeepDebugException(); } +#endif return false; } } - for (int i = 0, iMax = mask._excs.Length; i < iMax; i++) + for (int i = 0; i < excChuncks.Length; i++) { - if (_pools[mask._excs[i]].Has(entityID)) + var bit = excChuncks[i]; + if ((_entityComponentMasks[componentMaskStartIndex + bit.chunkIndex] & bit.mask) != 0) { +#if DEBUG && DRAGONECS_DEEP_DEBUG + if (false != deepDebug) { Throw.DeepDebugException(); } +#endif return false; } } + +#if DEBUG && DRAGONECS_DEEP_DEBUG + if (true != deepDebug) { Throw.DeepDebugException(); } +#endif return true; } #endregion @@ -745,13 +839,24 @@ namespace DCFApixels.DragonECS } public void ReleaseDelEntityBufferAll() { - ReleaseDelEntityBuffer(_delEntBufferCount); + ReleaseDelEntityBuffer(-1); } public unsafe void ReleaseDelEntityBuffer(int count) { - if (_delEntBufferCount <= 0) { return; } + if (_emptyEntitiesCount <= 0 && _delEntBufferCount <= 0) { return; } unchecked { _version++; } + for (int i = 0; i < _emptyEntitiesCount; i++) + { + TryDelEntity(_emptyEntities[i]); + } + _emptyEntitiesCount = 0; + + if(count < 0) + { + count = _delEntBufferCount; + } + count = Math.Max(0, Math.Min(count, _delEntBufferCount)); _delEntBufferCount -= count; int slisedCount = count; @@ -831,6 +936,7 @@ namespace DCFApixels.DragonECS SetEntityComponentMaskLength(CalcEntityComponentMaskLength()); //_pools.Length / COMPONENT_MASK_CHUNK_SIZE + 1; Array.Resize(ref _entities, newSize); Array.Resize(ref _delEntBuffer, newSize); + Array.Resize(ref _emptyEntities, newSize); Array.Resize(ref _entityComponentMasks, newSize * _entityComponentMaskLength); ArrayUtility.Fill(_entities, EntitySlot.Empty, _entitiesCapacity); @@ -951,10 +1057,8 @@ namespace DCFApixels.DragonECS public ReadOnlySpan GetComponentsFor(int entityID) { int count = GetComponentTypeIDsFor_Internal(entityID, ref _componentIDsBuffer); - if (_componentsBuffer == null || _componentsBuffer.Length < count) - { - _componentsBuffer = new object[count]; - } + ArrayUtility.UpsizeWithoutCopy(ref _componentIDsBuffer, count); + for (int i = 0; i < count; i++) { _componentsBuffer[i] = _pools[_componentIDsBuffer[i]].GetRaw(entityID); @@ -982,14 +1086,7 @@ namespace DCFApixels.DragonECS private unsafe int GetComponentTypeIDsFor_Internal(int entityID, ref int[] componentIDs) { var itemsCount = GetComponentsCount(entityID); - if (componentIDs == null) - { - componentIDs = new int[itemsCount]; - } - if (componentIDs.Length < itemsCount) - { - Array.Resize(ref componentIDs, itemsCount); - } + ArrayUtility.UpsizeWithoutCopy(ref componentIDs, itemsCount); if (itemsCount <= 0) { return 0; } @@ -1004,8 +1101,8 @@ namespace DCFApixels.DragonECS int arrayIndex = 0; for (int chunkIndex = entityID << _entityComponentMaskLengthBitShift, chunkIndexMax = chunkIndex + _entityComponentMaskLength; - chunkIndex < chunkIndexMax; - chunkIndex++) + chunkIndex < chunkIndexMax; + chunkIndex++) { int chunk = _entityComponentMasks[chunkIndex]; if (chunk == 0) @@ -1150,15 +1247,19 @@ namespace DCFApixels.DragonECS { private EcsWorld _world; private List _queries; + public string Name { get { return _world.Name; } } public EntitySlotInfo[] Entities { get { EntitySlotInfo[] result = new EntitySlotInfo[_world.Count]; int i = 0; - foreach (var e in _world.ToSpan()) + using (_world.DisableAutoReleaseDelEntBuffer()) { - result[i++] = _world.GetEntitySlotInfoDebug(e); + foreach (var e in _world.ToSpan()) + { + result[i++] = _world.GetEntitySlotInfoDebug(e); + } } return result; } @@ -1166,6 +1267,7 @@ namespace DCFApixels.DragonECS public long Version { get { return _world.Version; } } public IEcsPool[] Pools { get { return _world._pools; } } public short ID { get { return _world.ID; } } + public bool IsDestroyed { get { return _world._isDestroyed; } } public List MaskQueries { get { return _queries; } } public DebuggerProxy(EcsWorld world) { diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index 1c1d047..d2216d5 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -40,6 +40,17 @@ namespace DCFApixels.DragonECS { return FindPoolInstance_Internal(GetComponentTypeID(componentType)); } + public bool TryFindPoolInstance(int componentTypeID, out IEcsPool pool) + { + pool = FindPoolInstance(componentTypeID); + return pool.IsNullOrDummy() == false; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryFindPoolInstance(Type componentType, out IEcsPool pool) + { + pool = FindPoolInstance(componentType); + return pool.IsNullOrDummy() == false; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] private IEcsPool FindPoolInstance_Internal(int componentTypeID) { @@ -267,7 +278,11 @@ namespace DCFApixels.DragonECS ref PoolSlot slot = ref _poolSlots[componentTypeID]; slot.count++; slot.version++; - _entities[entityID].componentsCount++; + var count = _entities[entityID].componentsCount++; + if (count == 0 && IsUsed(entityID)) + { + RemoveFromEmptyEntities(entityID); + } _entityComponentMasks[(entityID << _entityComponentMaskLengthBitShift) + maskBit.chunkIndex] |= maskBit.mask; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -282,7 +297,7 @@ namespace DCFApixels.DragonECS if (count == 0 && IsUsed(entityID)) { - DelEntity(entityID); + MoveToEmptyEntities(entityID); } CheckUnregisterValid(count, entityID); } @@ -298,7 +313,11 @@ namespace DCFApixels.DragonECS ref PoolSlot slot = ref _poolSlots[componentTypeID]; slot.count++; slot.version++; - _entities[entityID].componentsCount++; + var count = _entities[entityID].componentsCount++; + if(count == 0 && IsUsed(entityID)) + { + RemoveFromEmptyEntities(entityID); + } return true; } return false; @@ -319,7 +338,7 @@ namespace DCFApixels.DragonECS if (count == 0 && IsUsed(entityID)) { - DelEntity(entityID); + MoveToEmptyEntities(entityID); } CheckUnregisterValid(count, entityID); return true; diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index 6e26f9f..9d3b3f9 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -1,6 +1,7 @@ #if DISABLE_DEBUG #undef DEBUG #endif +using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; @@ -25,11 +26,11 @@ namespace DCFApixels.DragonECS private static EcsWorld[] _worlds = Array.Empty(); private static readonly IdDispenser _worldIdDispenser = new IdDispenser(4, 0, n => Array.Resize(ref _worlds, n)); - private static StructList _allWorldComponentPools = new StructList(64); + private static readonly object _worldLock = new object(); + private StructList _worldComponentPools; private int _builtinWorldComponentsCount = 0; - private static readonly object _worldLock = new object(); static EcsWorld() { @@ -80,6 +81,7 @@ namespace DCFApixels.DragonECS public static void ResetStaticState() { + var nullworld = _worlds[0]; for (int i = 1; i < _worlds.Length; i++) { var world = _worlds[i]; @@ -91,6 +93,8 @@ namespace DCFApixels.DragonECS } world = null; } + _worlds = new EcsWorld[_worldIdDispenser.Capacity]; + _worlds[0] = nullworld; _worldIdDispenser.ReleaseAll(); } @@ -136,8 +140,8 @@ namespace DCFApixels.DragonECS private static short _count; private static short[] _recycledItems = new short[4]; private static short _recycledItemsCount; - private static IEcsWorldComponent _interface = EcsWorldComponentHandler.instance; - private static Abstract _controller = new Abstract(); + private static readonly IEcsWorldComponent _interface = EcsWorldComponentHandler.instance; + private static readonly Abstract _controller = new Abstract(); static WorldComponentPool() { _allWorldComponentPools.Add(_controller); diff --git a/src/Executors/EcsWhereExecutor.cs b/src/Executors/EcsWhereExecutor.cs index c35a6d6..d439441 100644 --- a/src/Executors/EcsWhereExecutor.cs +++ b/src/Executors/EcsWhereExecutor.cs @@ -89,6 +89,28 @@ namespace DCFApixels.DragonECS.Internal public EcsSpan Execute() { Execute_Iternal(); +#if DEBUG && DRAGONECS_DEEP_DEBUG + var newSpan = new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount); + using (EcsGroup group = EcsGroup.New(World)) + { + foreach (var e in World.Entities) + { + if (World.IsMatchesMask(Mask, e)) + { + group.Add(e); + } + } + + if (group.SetEquals(newSpan) == false) + { + int[] array = new int[_filteredAllEntities.Length]; + var count = _iterator.IterateTo(World.Entities, ref array); + + EcsDebug.PrintError(newSpan.ToString() + "\r\n" + group.ToSpan().ToString()); + Throw.DeepDebugException(); + } + } +#endif return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -99,6 +121,16 @@ namespace DCFApixels.DragonECS.Internal return Execute(); } ExecuteFor_Iternal(span); +#if DEBUG && DRAGONECS_DEEP_DEBUG + var newSpan = new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount); + foreach (var e in newSpan) + { + if (World.IsMatchesMask(Mask, e) == false) + { + Throw.DeepDebugException(); + } + } +#endif return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount); } diff --git a/src/Executors/EcsWhereToGroupExecutor.cs b/src/Executors/EcsWhereToGroupExecutor.cs index 7765503..8d57c6e 100644 --- a/src/Executors/EcsWhereToGroupExecutor.cs +++ b/src/Executors/EcsWhereToGroupExecutor.cs @@ -68,6 +68,24 @@ namespace DCFApixels.DragonECS.Internal { _version++; _iterator.IterateTo(World.Entities, _filteredAllGroup); +#if DEBUG && DRAGONECS_DEEP_DEBUG + if(_filteredGroup == null) + { + _filteredGroup = EcsGroup.New(World); + } + _filteredGroup.Clear(); + foreach (var e in World.Entities) + { + if(World.IsMatchesMask(Mask, e)) + { + _filteredGroup.Add(e); + } + } + if(_filteredAllGroup.SetEquals(_filteredGroup) == false) + { + throw new System.InvalidOperationException(); + } +#endif } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Executors/MaskQueryExecutor.cs b/src/Executors/MaskQueryExecutor.cs index de96a69..5676805 100644 --- a/src/Executors/MaskQueryExecutor.cs +++ b/src/Executors/MaskQueryExecutor.cs @@ -125,6 +125,7 @@ namespace DCFApixels.DragonECS.Core _maskInc = mask._incs; _maskExc = mask._excs; _count = 1 + mask._incs.Length + mask._excs.Length; + _versions = UnmanagedArrayUtility.NewAndInit(_count); } public bool Check() @@ -158,17 +159,17 @@ namespace DCFApixels.DragonECS.Core { *_versions = _world.Version; - long* ptr = _versions; + long* versionsPtr = _versions; var slots = _world._poolSlots; foreach (var slotIndex in _maskInc) { - ptr++; - *ptr = slots[slotIndex].version; + versionsPtr++; + *versionsPtr = slots[slotIndex].version; } foreach (var slotIndex in _maskExc) { - ptr++; - *ptr = slots[slotIndex].version; + versionsPtr++; + *versionsPtr = slots[slotIndex].version; } } public bool CheckAndNext() @@ -179,25 +180,27 @@ namespace DCFApixels.DragonECS.Core } *_versions = _world.Version; - long* ptr = _versions; + long* versionsPtr = _versions; var slots = _world._poolSlots; - bool result = _count != 1; + // Так как проверки EXC работают не правильно при отсутсвии INC, + // то проверки без INC должны всегда возвращать false. + bool result = _maskInc.Length > 0; foreach (var slotIndex in _maskInc) { - ptr++; - if (*ptr != slots[slotIndex].version) + versionsPtr++; + if (*versionsPtr != slots[slotIndex].version) { result = false; - *ptr = slots[slotIndex].version; + *versionsPtr = slots[slotIndex].version; } } foreach (var slotIndex in _maskExc) { - ptr++; - if (*ptr != slots[slotIndex].version) + versionsPtr++; + if (*versionsPtr != slots[slotIndex].version) { result = false; - *ptr = slots[slotIndex].version; + *versionsPtr = slots[slotIndex].version; } } return result; diff --git a/src/Injections/Utils/Interfaces.cs b/src/Injections/Utils/Interfaces.cs index 14f07f0..6a8a18b 100644 --- a/src/Injections/Utils/Interfaces.cs +++ b/src/Injections/Utils/Interfaces.cs @@ -9,7 +9,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.DI_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "The interface of the dependency injection process.")] - [MetaID("4C86537C92019AA24383CBF53CBD456C")] + [MetaID("DragonECS_4C86537C92019AA24383CBF53CBD456C")] public interface IEcsInject : IEcsInjectProcess { void Inject(T obj); @@ -22,7 +22,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.DI_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "The process interface that signals the completion of injection during pipeline initialization via the EcsPipeline.Init() method.")] - [MetaID("05C3537C920155AFC044C900E4F17D90")] + [MetaID("DragonECS_05C3537C920155AFC044C900E4F17D90")] public interface IOnInitInjectionComplete : IEcsProcess { void OnBeforeInitInjection(); diff --git a/src/Internal/ArrayUtility.cs b/src/Internal/ArrayUtility.cs index 4927430..c761f77 100644 --- a/src/Internal/ArrayUtility.cs +++ b/src/Internal/ArrayUtility.cs @@ -14,13 +14,13 @@ namespace DCFApixels.DragonECS.Internal { int Next { get; } } - internal readonly struct LinkedListIterator : IEnumerable + internal readonly struct LinkedListCountIterator : IEnumerable where T : ILinkedNext { public readonly T[] Array; public readonly int Count; public readonly int StartIndex; - public LinkedListIterator(T[] array, int count, int startIndex) + public LinkedListCountIterator(T[] array, int count, int startIndex) { Array = array; Count = count; @@ -66,6 +66,58 @@ namespace DCFApixels.DragonECS.Internal void IEnumerator.Reset() { throw new NotSupportedException(); } } } + internal readonly struct LinkedListIterator : IEnumerable + where T : ILinkedNext + { + public readonly T[] Array; + public readonly int EndIndex; + public readonly int StartIndex; + public LinkedListIterator(T[] array, int endIndex, int startIndex) + { + Array = array; + EndIndex = endIndex; + StartIndex = startIndex; + } + public Enumerator GetEnumerator() + { + return new Enumerator(Array, EndIndex, StartIndex); + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public struct Enumerator : IEnumerator + { + private readonly T[] _array; + private readonly int _endIndex; + private readonly int _startIndex; + private int _nextIndex; + private int _index; + public ref T Current { get { return ref _array[_index]; } } + T IEnumerator.Current { get { return Current; } } + object IEnumerator.Current { get { return Current; } } + public Enumerator(T[] array, int endIndex, int head) + { + _array = array; + _startIndex = head; + _nextIndex = _startIndex; + _endIndex = endIndex; + _index = _endIndex; + } + public bool MoveNext() + { + if (_nextIndex < 0) { return false; } + _index = _nextIndex; + _nextIndex = _array[_index].Next; + return true; + } + public void Dispose() { } + public void Reset() + { + _nextIndex = _startIndex; + _index = _endIndex; + } + } + } + internal static class ArrayUtility { @@ -92,58 +144,33 @@ namespace DCFApixels.DragonECS.Internal Array.Copy(array, array.Length - rightHeadLength, result, array.Length - rightHeadLength, rightHeadLength); // copy right head array = result; } - private static int GetHighBitNumber(uint bits) - { - if (bits == 0) - { - return -1; - } - int bit = 0; - if ((bits & 0xFFFF0000) != 0) - { - bits >>= 16; - bit |= 16; - } - if ((bits & 0xFF00) != 0) - { - bits >>= 8; - bit |= 8; - } - if ((bits & 0xF0) != 0) - { - bits >>= 4; - bit |= 4; - } - if ((bits & 0xC) != 0) - { - bits >>= 2; - bit |= 2; - } - if ((bits & 0x2) != 0) - { - bit |= 1; - } - return bit; - } - public static int NormalizeSizeToPowerOfTwo(int minSize) + + public static int NextPow2(int v) { unchecked { - return 1 << (GetHighBitNumber((uint)minSize - 1u) + 1); + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return ++v; } } - public static int NormalizeSizeToPowerOfTwo_ClampOverflow(int minSize) + public static int NextPow2_ClampOverflow(int v) { unchecked { - int hibit = (GetHighBitNumber((uint)minSize - 1u) + 1); - if (hibit >= 32) + const int NO_SIGN_HIBIT = 0x40000000; + if ((v & NO_SIGN_HIBIT) != 0) { return int.MaxValue; } - return 1 << hibit; + return NextPow2(v); } } + public static void Fill(T[] array, T value, int startIndex = 0, int length = -1) { if (length < 0) @@ -159,6 +186,39 @@ namespace DCFApixels.DragonECS.Internal array[i] = value; } } + + + public static void UpsizeWithoutCopy(ref T[] array, int minSize) + { + if (array == null || minSize > array.Length) + { + array = new T[minSize]; + } + } + public static void Upsize(ref T[] array, int minSize) + { + if (array == null) + { + array = new T[minSize]; + } + else if (minSize > array.Length) + { + Array.Resize(ref array, minSize); + } + } + public static void UpsizeToNextPow2(ref T[] array, int minSize) + { + if (array == null) + { + minSize = NextPow2(minSize); + array = new T[minSize]; + } + else if (minSize > array.Length) + { + minSize = NextPow2(minSize); + Array.Resize(ref array, minSize); + } + } } internal readonly struct EnumerableInt : IEnumerable { diff --git a/src/Internal/EcsTypeCodeManager.cs b/src/Internal/EcsTypeCodeManager.cs index 52cb11e..1bafb05 100644 --- a/src/Internal/EcsTypeCodeManager.cs +++ b/src/Internal/EcsTypeCodeManager.cs @@ -12,46 +12,6 @@ using Unity.IL2CPP.CompilerServices; namespace DCFApixels.DragonECS.Internal { -#if ENABLE_IL2CPP - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] -#endif - [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")] - internal readonly struct EcsTypeCodeKey : IEquatable - { - public readonly Type Type; - public readonly string NameKey; - public EcsTypeCodeKey(Type type, string nameKey) - { - Type = type; - NameKey = nameKey; - } - public bool Equals(EcsTypeCodeKey other) - { - return Type == other.Type && NameKey == other.NameKey; - } - public override bool Equals(object obj) - { - return obj is EcsTypeCodeKey other && Equals(other); - } - public override int GetHashCode() - { - return HashCode.Combine(Type, NameKey); - } - public override string ToString() - { - if (string.IsNullOrEmpty(NameKey)) - { - return Type.ToString(); - } - return $"{Type} {NameKey}"; - } - public static implicit operator EcsTypeCodeKey(Type type) { return new EcsTypeCodeKey(type, string.Empty); } - private string GetDebuggerDisplay() - { - return ToString(); - } - } //TODO разработать возможность ручного устанавливания ID типам. //это может быть полезно как детерминированность для сети #if ENABLE_IL2CPP @@ -62,7 +22,7 @@ namespace DCFApixels.DragonECS.Internal { private static readonly Dictionary _codes = new Dictionary(); private static int _increment = 1; - private static object _lock = new object(); + private static readonly object _lock = new object(); public static int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -83,7 +43,6 @@ namespace DCFApixels.DragonECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsTypeCode Get() { return EcsTypeCodeCache.code; } public static bool Has(Type type) { return _codes.ContainsKey(type); } - public static bool Has() { return _codes.ContainsKey(typeof(T)); } public static EcsTypeCodeKey FindTypeOfCode(EcsTypeCode typeCode) { foreach (var item in _codes) @@ -123,4 +82,40 @@ namespace DCFApixels.DragonECS.Internal return this.AutoToString(false); } } +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] +#endif + [DebuggerDisplay("{" + nameof(ToString) + "()}")] + internal readonly struct EcsTypeCodeKey : IEquatable + { + public readonly Type Type; + public readonly string NameKey; + public EcsTypeCodeKey(Type type, string nameKey) + { + Type = type; + NameKey = nameKey; + } + public bool Equals(EcsTypeCodeKey other) + { + return Type == other.Type && NameKey == other.NameKey; + } + public override bool Equals(object obj) + { + return obj is EcsTypeCodeKey other && Equals(other); + } + public override int GetHashCode() + { + return HashCode.Combine(Type, NameKey); + } + public override string ToString() + { + if (string.IsNullOrEmpty(NameKey)) + { + return Type.ToString(); + } + return $"{Type} {NameKey}"; + } + public static implicit operator EcsTypeCodeKey(Type type) { return new EcsTypeCodeKey(type, string.Empty); } + } } \ No newline at end of file diff --git a/src/Internal/IdDispenser.cs b/src/Internal/IdDispenser.cs index 3ac1a83..47e993b 100644 --- a/src/Internal/IdDispenser.cs +++ b/src/Internal/IdDispenser.cs @@ -35,7 +35,7 @@ namespace DCFApixels.DragonECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _usedCount; } } - public int Size + public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _size; } @@ -247,7 +247,7 @@ namespace DCFApixels.DragonECS.Internal [MethodImpl(MethodImplOptions.NoInlining)] private void Upsize_Internal(int minSize) { - Resize(ArrayUtility.NormalizeSizeToPowerOfTwo_ClampOverflow(minSize)); + Resize(ArrayUtility.NextPow2_ClampOverflow(minSize)); } private void Resize(int newSize) { @@ -339,7 +339,7 @@ namespace DCFApixels.DragonECS.Internal { get { - Pair[] result = new Pair[_target.Size]; + Pair[] result = new Pair[_target.Capacity]; for (int i = 0; i < result.Length; i++) { result[i] = new Pair( @@ -354,7 +354,7 @@ namespace DCFApixels.DragonECS.Internal { get { - ID[] result = new ID[_target.Size]; + ID[] result = new ID[_target.Capacity]; for (int i = 0; i < result.Length; i++) { int id = _target._dense[i]; @@ -365,7 +365,7 @@ namespace DCFApixels.DragonECS.Internal } public bool IsValid => _target.IsValid(); public int Count => _target.Count; - public int Size => _target.Size; + public int Capacity => _target.Capacity; public int NullID => _target._nullID; internal readonly struct ID { diff --git a/src/Internal/StructList.cs b/src/Internal/StructList.cs index d7ad9aa..7488304 100644 --- a/src/Internal/StructList.cs +++ b/src/Internal/StructList.cs @@ -26,7 +26,7 @@ namespace DCFApixels.DragonECS.Internal set { if (value <= _items.Length) { return; } - value = ArrayUtility.NormalizeSizeToPowerOfTwo(value); + value = ArrayUtility.NextPow2(value); Array.Resize(ref _items, value); } } @@ -53,7 +53,7 @@ namespace DCFApixels.DragonECS.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public StructList(int capacity) { - _items = new T[ArrayUtility.NormalizeSizeToPowerOfTwo(capacity)]; + _items = new T[ArrayUtility.NextPow2(capacity)]; _count = 0; } diff --git a/src/Internal/UnsafeArray.cs b/src/Internal/UnsafeArray.cs index ad8e385..9f00cbe 100644 --- a/src/Internal/UnsafeArray.cs +++ b/src/Internal/UnsafeArray.cs @@ -44,6 +44,7 @@ namespace DCFApixels.DragonECS.Internal } } } + #if ENABLE_IL2CPP [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 92ab48b..4801ede 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -21,8 +21,8 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Standard component.")] - [MetaID("84D2537C9201D6F6B92FEC1C8883A07A")] - public interface IEcsComponent : IEcsMember { } + [MetaID("DragonECS_84D2537C9201D6F6B92FEC1C8883A07A")] + public interface IEcsComponent : IEcsComponentMember { } /// Pool for IEcsComponent components #if ENABLE_IL2CPP @@ -31,7 +31,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Pool for IEcsComponent components.")] - [MetaID("C501547C9201A4B03FC25632E4FAAFD7")] + [MetaID("DragonECS_C501547C9201A4B03FC25632E4FAAFD7")] [DebuggerDisplay("Count: {Count} Type: {ComponentType}")] public sealed class EcsPool : IEcsPoolImplementation, IEcsStructPool, IEntityStorage, IEnumerable //IEnumerable - IntelliSense hack where T : struct, IEcsComponent @@ -51,8 +51,8 @@ namespace DCFApixels.DragonECS private bool _isDenseEntitiesDelayedValid = false; private int _recycledCount = 0; - private readonly IEcsComponentLifecycle _componentLifecycleHandler = EcsComponentResetHandler.instance; - private readonly bool _isHasComponentLifecycleHandler = EcsComponentResetHandler.isHasHandler; + private readonly IEcsComponentLifecycle _componentLifecycleHandler = EcsComponentLifecycleHandler.instance; + private readonly bool _isHasComponentLifecycleHandler = EcsComponentLifecycleHandler.isHasHandler; private readonly IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; private readonly bool _isHasComponentCopyHandler = EcsComponentCopyHandler.isHasHandler; @@ -101,11 +101,12 @@ namespace DCFApixels.DragonECS { ref int itemIndex = ref _mapping[entityID]; #if DEBUG + if (_source.IsUsed(entityID) == false) { Throw.Ent_ThrowIsNotAlive(_source, entityID); } if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent(entityID); } if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE if (itemIndex > 0) { return ref Get(entityID); } - if (_isLocked) { return ref _items[0]; } + if (_isLocked | _source.IsUsed(entityID) == false) { return ref _items[0]; } #endif itemIndex = GetFreeItemIndex(entityID); _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); @@ -279,15 +280,7 @@ namespace DCFApixels.DragonECS _maskBit = EcsMaskChunck.FromID(componentTypeID); _mapping = new int[world.Capacity]; - Resize(ArrayUtility.NormalizeSizeToPowerOfTwo(world.Configs.GetWorldConfigOrDefault().PoolComponentsCapacity)); - //_capacity = ArrayUtility.NormalizeSizeToPowerOfTwo(world.Configs.GetWorldConfigOrDefault().PoolComponentsCapacity); - //_items = new T[_capacity]; - //_sparseEntities = new int[_capacity]; - //_denseEntitiesDelayed = new int[_capacity]; - //for (int i = 0; i < _capacity; i++) - //{// ����� �������������� ��� ����� ������ ����������, � ������ ������ free index-� ���� _denseEntitiesDelayed ���������� 0, �� free index ���������� ����� ��������� - // _denseEntitiesDelayed[i] = i; - //} + Resize(ArrayUtility.NextPow2(world.Configs.GetWorldConfigOrDefault().PoolComponentsCapacity)); } void IEcsPoolImplementation.OnWorldResize(int newSize) { @@ -314,7 +307,7 @@ namespace DCFApixels.DragonECS { Add(entityID) = dataRaw == null ? default : (T)dataRaw; } - object IEcsReadonlyPool.GetRaw(int entityID) { return Get(entityID); } + object IEcsReadonlyPool.GetRaw(int entityID) { return Read(entityID); } void IEcsPool.SetRaw(int entityID, object dataRaw) { Get(entityID) = dataRaw == null ? default : (T)dataRaw; @@ -515,14 +508,100 @@ namespace DCFApixels.DragonECS IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion - #region MarkersConverter + #region Convertors public static implicit operator EcsPool(IncludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsPool(ExcludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsPool(OptionalMarker a) { return a.GetInstance>(); } public static implicit operator EcsPool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } #endregion + + #region Apply + public static void Apply(ref T component, int entityID, short worldID) + { + EcsWorld.GetPoolInstance>(worldID).TryAddOrGet(entityID) = component; + } + public static void Apply(ref T component, int entityID, EcsPool pool) + { + pool.TryAddOrGet(entityID) = component; + } + #endregion } +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] +#endif + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct ReadonlyEcsPool : IEcsReadonlyPool //IEnumerable - IntelliSense hack + where T : struct, IEcsComponent + { + private readonly EcsPool _pool; + + #region Properties + public int ComponentTypeID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.ComponentTypeID; } + } + public Type ComponentType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.ComponentType; } + } + public EcsWorld World + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.World; } + } + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.Count; } + } + public bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.IsReadOnly; } + } + public ref readonly T this[int entityID] + { + get { return ref _pool.Read(entityID); } + } + #endregion + + #region Constructors + internal ReadonlyEcsPool(EcsPool pool) + { + _pool = pool; + } + #endregion + + #region Methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Has(int entityID) { return _pool.Has(entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Get(int entityID) { return ref _pool.Read(entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(int entityID) { return ref _pool.Read(entityID); } + object IEcsReadonlyPool.GetRaw(int entityID) { return _pool.Read(entityID); } + +#if !DRAGONECS_DISABLE_POOLS_EVENTS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } +#endif +#endregion + + #region Convertors + public static implicit operator ReadonlyEcsPool(EcsPool a) { return new ReadonlyEcsPool(a); } + public static implicit operator ReadonlyEcsPool(IncludeMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsPool(ExcludeMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsPool(OptionalMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsPool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } + #endregion + } + + public static class EcsPoolExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 172f933..4793eaf 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -26,6 +26,13 @@ namespace DCFApixels.DragonECS.PoolsCore /// Component type. public interface IEcsPoolImplementation : IEcsPoolImplementation { } + //TODO + //public interface IEcsReadonlyPoolImplementation : IEcsReadonlyPool + // where TPool : IEcsReadonlyPoolImplementation + //{ + // void Init(ref TPool pool); + //} + #region EcsPoolThrowHelper public static class EcsPoolThrowHelper { @@ -74,7 +81,7 @@ namespace DCFApixels.DragonECS.Internal [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "A placeholder type, an instance of this type replaces the null ref.")] [MetaTags(MetaTags.HIDDEN)] - [MetaID("460E547C9201227A4956AC297F67B484")] + [MetaID("DragonECS_460E547C9201227A4956AC297F67B484")] [DebuggerDisplay("-")] public sealed class EcsNullPool : IEcsPoolImplementation { @@ -135,13 +142,13 @@ namespace DCFApixels.DragonECS.Internal throw new NullInstanceException(); #endif } - void IEcsReadonlyPool.Copy(int fromEntityID, int toEntityID) + void IEcsPool.Copy(int fromEntityID, int toEntityID) { #if DEBUG throw new NullInstanceException(); #endif } - void IEcsReadonlyPool.Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + void IEcsPool.Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) { #if DEBUG throw new NullInstanceException(); @@ -189,11 +196,10 @@ namespace DCFApixels.DragonECS #region Methods bool Has(int entityID); object GetRaw(int entityID); - void Copy(int fromEntityID, int toEntityID); - void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID); #endregion #if !DRAGONECS_DISABLE_POOLS_EVENTS + #region Add/Remove Listeners void AddListener(IEcsPoolEventListener listener); void RemoveListener(IEcsPoolEventListener listener); @@ -209,6 +215,8 @@ namespace DCFApixels.DragonECS void SetRaw(int entityID, object dataRaw); void Del(int entityID); void ClearAll(); + void Copy(int fromEntityID, int toEntityID); + void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID); #endregion } @@ -261,6 +269,12 @@ namespace DCFApixels.DragonECS entityID = self.World.NewEntity(); return ref self.Add(entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T NewEntityLong(this IEcsStructPool self, out entlong entity) where T : struct + { + entity = self.World.NewEntityLong(); + return ref self.Add(entity.GetIDUnchecked()); + } } #endregion diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index b7626d3..72100cc 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -20,8 +20,8 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Tag component or component without data.")] - [MetaID("8D3E547C92013C6A2C2DFC8D2F1FA297")] - public interface IEcsTagComponent : IEcsMember { } + [MetaID("DragonECS_8D3E547C92013C6A2C2DFC8D2F1FA297")] + public interface IEcsTagComponent : IEcsComponentMember { } /// Pool for IEcsTagComponent components. #if ENABLE_IL2CPP @@ -30,7 +30,7 @@ namespace DCFApixels.DragonECS [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Pool for IEcsTagComponent components. EcsTagPool is optimized for storing tag components or components without data.")] - [MetaID("9D80547C9201E852E4F17324EAC1E15A")] + [MetaID("DragonECS_9D80547C9201E852E4F17324EAC1E15A")] [DebuggerDisplay("Count: {Count} Type: {ComponentType}")] public sealed class EcsTagPool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack where T : struct, IEcsTagComponent @@ -104,10 +104,11 @@ namespace DCFApixels.DragonECS public void Add(int entityID) { #if DEBUG + if (_source.IsUsed(entityID) == false) { Throw.Ent_ThrowIsNotAlive(_source, entityID); } if (Has(entityID)) { EcsPoolThrowHelper.ThrowAlreadyHasComponent(entityID); } if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE - if (Has(entityID) || _isLocked) { return; } + if (Has(entityID) | _source.IsUsed(entityID) == false | _isLocked) { return; } #endif _count++; _mapping[entityID] = true; @@ -311,12 +312,99 @@ namespace DCFApixels.DragonECS IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion - #region MarkersConverter + #region Convertors public static implicit operator EcsTagPool(IncludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsTagPool(ExcludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsTagPool(OptionalMarker a) { return a.GetInstance>(); } public static implicit operator EcsTagPool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } #endregion + + #region Apply + public static void Apply(ref T component, int entityID, short worldID) + { + EcsWorld.GetPoolInstance>(worldID).TryAdd(entityID); + } + public static void Apply(ref T component, int entityID, EcsTagPool pool) + { + pool.TryAdd(entityID); + } + #endregion + } + +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] +#endif + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct ReadonlyEcsTagPool : IEcsReadonlyPool //IEnumerable - IntelliSense hack + where T : struct, IEcsTagComponent + { + private readonly EcsTagPool _pool; + + #region Properties + public int ComponentTypeID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.ComponentTypeID; } + } + public Type ComponentType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.ComponentType; } + } + public EcsWorld World + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.World; } + } + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.Count; } + } + public bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.IsReadOnly; } + } + public bool this[int entityID] + { + get { return _pool.Has(entityID); } + } + #endregion + + #region Constructors + internal ReadonlyEcsTagPool(EcsTagPool pool) + { + _pool = pool; + } + #endregion + + #region Methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Has(int entityID) { return _pool.Has(entityID); } + object IEcsReadonlyPool.GetRaw(int entityID) + { +#if DEBUG + if (Has(entityID) == false) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } +#endif + return default; + } + +#if !DRAGONECS_DISABLE_POOLS_EVENTS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } +#endif + #endregion + + #region Convertors + public static implicit operator ReadonlyEcsTagPool(EcsTagPool a) { return new ReadonlyEcsTagPool(a); } + public static implicit operator ReadonlyEcsTagPool(IncludeMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsTagPool(ExcludeMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsTagPool(OptionalMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsTagPool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } + #endregion } public static class EcsTagPoolExtensions diff --git a/src/Utils/EcsPipelineTemplate.cs b/src/Utils/EcsPipelineTemplate.cs index d24b065..59617df 100644 --- a/src/Utils/EcsPipelineTemplate.cs +++ b/src/Utils/EcsPipelineTemplate.cs @@ -13,7 +13,7 @@ namespace DCFApixels.DragonECS [MetaTags(MetaTags.HIDDEN)] [MetaDescription(AUTHOR, "...")] [MetaGroup(PACK_GROUP, OTHER_GROUP)] - [MetaID("128D547C9201EEAC49B05F89E4A253DF")] + [MetaID("DragonECS_128D547C9201EEAC49B05F89E4A253DF")] [MetaColor(MetaColor.DragonRose)] public class EcsPipelineTemplate : IEcsModule { diff --git a/src/Utils/Exceptions.cs b/src/Utils/Exceptions.cs index ef53bf6..d4086a3 100644 --- a/src/Utils/Exceptions.cs +++ b/src/Utils/Exceptions.cs @@ -118,6 +118,11 @@ namespace DCFApixels.DragonECS.Internal throw new InvalidOperationException($"The method {methodName} can only be executed before creating entities in the world."); } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Ent_ThrowIsNotAlive(EcsWorld world, int entityID) + { + Ent_ThrowIsNotAlive((world, entityID)); + } [MethodImpl(MethodImplOptions.NoInlining)] internal static void Ent_ThrowIsNotAlive(entlong entity) { diff --git a/src/Utils/ITemplateNode.cs b/src/Utils/ITemplateNode.cs index bd5cb8a..ff0392b 100644 --- a/src/Utils/ITemplateNode.cs +++ b/src/Utils/ITemplateNode.cs @@ -20,6 +20,20 @@ namespace DCFApixels.DragonECS self.Apply(worldID, entityID); return (EcsWorld.GetWorld(worldID), entityID); } + public static void Apply(this ITemplateNode self, EcsWorld world, int entityID) + { + self.Apply(world.ID, entityID); + } + public static int ApplyAndReturn(this ITemplateNode self, EcsWorld world, int entityID) + { + self.Apply(world.ID, entityID); + return entityID; + } + public static entlong ApplyAndReturnLong(this ITemplateNode self, EcsWorld world, int entityID) + { + self.Apply(world.ID, entityID); + return (world, entityID); + } public static int NewEntity(this EcsWorld world, ITemplateNode template) { int e = world.NewEntity(); diff --git a/src/entlong.cs b/src/entlong.cs index d7e64d3..9b25a94 100644 --- a/src/entlong.cs +++ b/src/entlong.cs @@ -147,6 +147,20 @@ namespace DCFApixels.DragonECS return IsAlive; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Unpack(out int id) + { +#if DEBUG + if (IsAlive == false) { Throw.Ent_ThrowIsNotAlive(this); } +#elif DRAGONECS_STABILITY_MODE + if (IsAlive == false) + { + id = EcsConsts.NULL_ENTITY_ID; + return; + } +#endif + id = _id; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Unpack(out int id, out EcsWorld world) { @@ -216,6 +230,12 @@ namespace DCFApixels.DragonECS id = _id; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryUnpack(out int id) + { + id = _id; + return IsAlive; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryUnpack(out int id, out EcsWorld world) {