diff --git a/src/Builtin/BaseRunners.cs b/src/Builtin/BaseRunners.cs index e3fcf6a..539a849 100644 --- a/src/Builtin/BaseRunners.cs +++ b/src/Builtin/BaseRunners.cs @@ -4,19 +4,19 @@ namespace DCFApixels.DragonECS { public interface IEcsPreInitProcess : IEcsSystem { - public void PreInit(EcsPipeline pipeline); + void PreInit(EcsPipeline pipeline); } public interface IEcsInitProcess : IEcsSystem { - public void Init(EcsPipeline pipeline); + void Init(EcsPipeline pipeline); } public interface IEcsRunProcess : IEcsSystem { - public void Run(EcsPipeline pipeline); + void Run(EcsPipeline pipeline); } public interface IEcsDestroyProcess : IEcsSystem { - public void Destroy(EcsPipeline pipeline); + void Destroy(EcsPipeline pipeline); } public interface IEcsBaseSystem : IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess { } diff --git a/src/Builtin/Components.cs b/src/Builtin/Components.cs index 8d609ad..6cea199 100644 --- a/src/Builtin/Components.cs +++ b/src/Builtin/Components.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { + [DebugColor(DebugColor.White)] public struct Parent : IEcsAttachComponent { public entlong entity; @@ -36,7 +37,7 @@ namespace DCFApixels.DragonECS public static bool TryGetRoot(this EcsAttachPool parents, EcsSubject conditionSubject, int entityID, out int rootEntityID) { rootEntityID = entityID; - while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child) && conditionSubject.IsMatches(child)) + while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child) && !conditionSubject.IsMatches(child)) rootEntityID = child; return rootEntityID != entityID; } @@ -47,5 +48,25 @@ namespace DCFApixels.DragonECS rootEntityID = child; return rootEntityID != entityID; } + + public static bool TryFindParentWithSubject(this EcsAttachPool parents, EcsSubject conditionSubject, int entityID, out int resultEntityID) + { + resultEntityID = entityID; + while (parents.Has(resultEntityID) && parents.Read(resultEntityID).entity.TryGetID(out int child) && !conditionSubject.IsMatches(resultEntityID)) + resultEntityID = child; + return conditionSubject.IsMatches(resultEntityID); + } + public static bool TryFindParentWith(this EcsAttachPool parents, int entityID, out int resultEntityID) where TComponent : struct + { + var pool = parents.World.AllPools[parents.World.GetComponentID()]; + resultEntityID = entityID; + while (!pool.Has(resultEntityID) && + parents.Has(resultEntityID) && + parents.Read(resultEntityID).entity.TryGetID(out int child)) + { + resultEntityID = child; + } + return pool.Has(resultEntityID); + } } } diff --git a/src/Builtin/InjectSystem.cs b/src/Builtin/InjectSystem.cs index 187dde6..5fa251f 100644 --- a/src/Builtin/InjectSystem.cs +++ b/src/Builtin/InjectSystem.cs @@ -38,16 +38,16 @@ namespace DCFApixels.DragonECS public interface IEcsPreInject : IEcsSystem { - public void PreInject(object obj); + void PreInject(object obj); } public interface IEcsInject : IEcsSystem { - public void Inject(T obj); + void Inject(T obj); } public interface IEcsPreInitInjectProcess : IEcsSystem { - public void OnPreInitInjectionBefore(); - public void OnPreInitInjectionAfter(); + void OnPreInitInjectionBefore(); + void OnPreInitInjectionAfter(); } namespace Internal @@ -127,9 +127,7 @@ namespace DCFApixels.DragonECS EcsRunner.Destroy(injectCallbacksRunner); } } - public void OnPreInitInjectionBefore() { } - public void OnPreInitInjectionAfter() { _injectController = null; diff --git a/src/Builtin/Systems.cs b/src/Builtin/Systems.cs index a61f702..431afa3 100644 --- a/src/Builtin/Systems.cs +++ b/src/Builtin/Systems.cs @@ -6,12 +6,9 @@ namespace DCFApixels.DragonECS public class SystemsBlockMarkerSystem : IEcsSystem { public readonly string name; - - public SystemsBlockMarkerSystem(string name) - { - this.name = name; - } + public SystemsBlockMarkerSystem(string name) { this.name = name; } } + [DebugHide, DebugColor(DebugColor.Grey)] public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsPreInject { @@ -21,7 +18,6 @@ namespace DCFApixels.DragonECS if (obj is EcsWorld world) _worlds.Add(world); } - public void Run(EcsPipeline pipeline) { foreach (var world in _worlds) @@ -36,7 +32,6 @@ namespace DCFApixels.DragonECS { private TWorld _world; public void Inject(TWorld obj) => _world = obj; - private sealed class Subject : EcsSubject { public EcsPool pool; @@ -63,7 +58,7 @@ namespace DCFApixels.DragonECS b.AddUnique(new DeleteOneFrameComponentSystem(), AUTO_DEL_LAYER); return b; } - /// for EcsDefaultWorld + /// for EcsDefaultWorld public static EcsPipeline.Builder AutoDel(this EcsPipeline.Builder b) where TComponent : struct, IEcsComponent { diff --git a/src/Builtin/WorldRunners.cs b/src/Builtin/WorldRunners.cs index 2123165..105432b 100644 --- a/src/Builtin/WorldRunners.cs +++ b/src/Builtin/WorldRunners.cs @@ -4,15 +4,15 @@ namespace DCFApixels.DragonECS { public interface IEcsComponentAdd : IEcsSystem { - public void OnComponentAdd(int entityID); + void OnComponentAdd(int entityID); } public interface IEcsComponentWrite : IEcsSystem { - public void OnComponentWrite(int entityID); + void OnComponentWrite(int entityID); } public interface IEcsComponentDel : IEcsSystem { - public void OnComponentDel(int entityID); + void OnComponentDel(int entityID); } public interface IEcsComponentLifecycle : IEcsComponentAdd, IEcsComponentWrite, IEcsComponentDel { } @@ -46,11 +46,11 @@ namespace DCFApixels.DragonECS public interface IEcsEntityCreate : IEcsSystem { - public void OnEntityCreate(int entityID); + void OnEntityCreate(int entityID); } public interface IEcsEntityDestroy : IEcsSystem { - public void OnEntityDestroy(int entityID); + void OnEntityDestroy(int entityID); } public interface IEcsEntityLifecycle : IEcsEntityCreate, IEcsEntityDestroy { } @@ -76,11 +76,11 @@ namespace DCFApixels.DragonECS public interface IEcsWorldCreate : IEcsSystem { - public void OnWorldCreate(EcsWorld world); + void OnWorldCreate(EcsWorld world); } public interface IEcsWorldDestroy : IEcsSystem { - public void OnWorldDestroy(EcsWorld world); + void OnWorldDestroy(EcsWorld world); } public interface IEcsWorldLifecycle : IEcsWorldCreate, IEcsWorldDestroy { } diff --git a/src/Debug/Attributes/DebugColorAttribute.cs b/src/Debug/Attributes/DebugColorAttribute.cs index ea70081..253f64f 100644 --- a/src/Debug/Attributes/DebugColorAttribute.cs +++ b/src/Debug/Attributes/DebugColorAttribute.cs @@ -19,13 +19,11 @@ namespace DCFApixels.DragonECS { color = new ColorRecord(r, g, b); } - public DebugColorAttribute(DebugColor color) { this.color = new ColorRecord((int)color); } - [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)] private readonly struct ColorRecord // Union { @@ -67,13 +65,31 @@ namespace DCFApixels.DragonECS /// Magenta. RGB is (255, 0, 255) Magenta = (255 << 24) + (000 << 16) + (255 << 8), - /// Yellow. RGB is (255, 127, 0) - Orange = (255 << 24) + (127 << 16) + (000 << 8), - + /// Yellow. RGB is (255, 165, 0) + Orange = (255 << 24) + (165 << 16) + (000 << 8), + /// Yellow. RGB is (255, 69, 0) + OrangeRed = (255 << 24) + (69 << 16) + (000 << 8), + /// Lime. RGB is (125, 255, 0) + Lime = (125 << 24) + (255 << 16) + (000 << 8), + /// Lime. RGB is (127, 255, 212) + Aquamarine = (127 << 24) + (255 << 16) + (212 << 8), + /// Lime. RGB is (218, 165, 32) + Goldenrod = (218 << 24) + (165 << 16) + (32 << 8), + /// Yellow. RGB is (255, 105, 180) + DeepPink = (255 << 24) + (105 << 16) + (180 << 8), + /// Yellow. RGB is (220, 20, 60) + Crimson = (220 << 24) + (20 << 16) + (60 << 8), + /// Yellow. RGB is (138, 43, 226) + BlueViolet = (138 << 24) + (43 << 16) + (226 << 8), + /// Yellow. RGB is (255, 3, 62) + AmericanRose = (255 << 24) + (3 << 16) + (62 << 8), + /// Grey/Gray. RGB is (127, 127, 127) Gray = (127 << 24) + (127 << 16) + (127 << 8), /// Grey/Gray. RGB is (127, 127, 127) Grey = Gray, + /// Grey/Gray. RGB is (192, 192, 192) + Silver = (192 << 24) + (192 << 16) + (192 << 8), /// White. RGB is (255, 255, 255) White = -1, /// Black. RGB is (0, 0, 0) diff --git a/src/Debug/EcsDebugUtility.cs b/src/Debug/EcsDebugUtility.cs new file mode 100644 index 0000000..5565e8b --- /dev/null +++ b/src/Debug/EcsDebugUtility.cs @@ -0,0 +1,55 @@ +using System; +using System.Reflection; + +namespace DCFApixels.DragonECS +{ + public static class EcsDebugUtility + { + public static string GetGenericTypeName(int maxDepth = 2) => GetGenericTypeName(typeof(T), maxDepth); + public static string GetGenericTypeName(Type type, int maxDepth = 2) + { +#if (DEBUG && !DISABLE_DEBUG) + string friendlyName = type.Name; + if (!type.IsGenericType || maxDepth == 0) + return friendlyName; + + int iBacktick = friendlyName.IndexOf('`'); + if (iBacktick > 0) + friendlyName = friendlyName.Remove(iBacktick); + + friendlyName += "<"; + Type[] typeParameters = type.GetGenericArguments(); + for (int i = 0; i < typeParameters.Length; ++i) + { + string typeParamName = GetGenericTypeName(typeParameters[i], maxDepth - 1); + friendlyName += (i == 0 ? typeParamName : "," + typeParamName); + } + friendlyName += ">"; + return friendlyName; +#else //optimization for release build + return type.Name; +#endif + } + + public static string GetName() => GetName(typeof(T)); + public static string GetName(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? atr.name : GetGenericTypeName(type); + } + + public static string GetDescription() => GetDescription(typeof(T)); + public static string GetDescription(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? atr.description : string.Empty; + } + + public static (byte, byte, byte) GetColorRGB() => GetColorRGB(typeof(T)); + public static (byte, byte, byte) GetColorRGB(Type type) + { + var atr = type.GetCustomAttribute(); + return atr != null ? (atr.r, atr.g, atr.b) : ((byte)255, (byte)255, (byte)255); + } + } +} diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 3e5fb67..a898ecb 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -184,6 +184,13 @@ namespace DCFApixels.DragonECS AddInternal(system, layerName, true); return this; } + public Builder Remove() + { + _uniqueTypes.Remove(typeof(TSystem)); + foreach (var list in _systems.Values) + list.RemoveAll(o => o is TSystem); + return this; + } private void AddInternal(IEcsSystem system, string layerName, bool isUnique) { if (layerName == null) layerName = _basicLayer; diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 31e866c..d657be1 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS internal static class EcsRunnerActivator { - private static Dictionary _runnerHandlerTypes; //interface guid/Runner handler type pairs; + private static Dictionary _runnerHandlerTypes; //interface base type/Runner handler type pairs; static EcsRunnerActivator() { @@ -69,11 +69,12 @@ namespace DCFApixels.DragonECS } } #endif - _runnerHandlerTypes = new Dictionary(); + _runnerHandlerTypes = new Dictionary(); foreach (var item in runnerHandlerTypes) { Type interfaceType = item.BaseType.GenericTypeArguments[0]; - _runnerHandlerTypes.Add(interfaceType.GUID, item); +// if(!_runnerHandlerTypes.ContainsKey(interfaceType.GUID))//TODO это кажется костыль, изначально все работало без этого ифа + _runnerHandlerTypes.Add(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, item); } if (delayedExceptions.Count > 0) @@ -112,14 +113,8 @@ namespace DCFApixels.DragonECS internal static void InitFor() where TInterface : IEcsSystem { Type interfaceType = typeof(TInterface); - Type nonGenericInterfaceType = interfaceType; - if (nonGenericInterfaceType.IsGenericType) - { - nonGenericInterfaceType = nonGenericInterfaceType.GetGenericTypeDefinition(); - } - Guid interfaceGuid = nonGenericInterfaceType.GUID; - if (!_runnerHandlerTypes.TryGetValue(interfaceGuid, out Type runnerType)) + if (!_runnerHandlerTypes.TryGetValue(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, out Type runnerType)) { throw new Exception(); } diff --git a/src/EcsSubject.cs b/src/EcsSubject.cs index 1222e0c..eff687f 100644 --- a/src/EcsSubject.cs +++ b/src/EcsSubject.cs @@ -73,7 +73,7 @@ namespace DCFApixels.DragonECS } public sealed override TPool Exclude() { - + ExcludeImplicit(); return _world.GetPool(); } public sealed override TPool Optional() diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index d84f595..9b8f66d 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -9,7 +9,7 @@ namespace DCFApixels.DragonECS internal sealed class EcsNullWorld : EcsWorld { - public EcsNullWorld() : base(null, false) { } + public EcsNullWorld() : base(false) { } } public abstract class EcsWorld @@ -39,26 +39,24 @@ namespace DCFApixels.DragonECS internal IEcsPoolImplementation[] pools; private EcsNullPool _nullPool; + private int _poolsCount = 0; private EcsSubject[] _subjects; private EcsQueryExecutor[] _executors; - private EcsPipeline _pipeline; - private List> _groups; private Stack _groupsPool = new Stack(64); - private IEcsEntityCreate _entityCreate; - private IEcsEntityDestroy _entityDestry; + private List _listeners; #region Properties public abstract Type Archetype { get; } public int UniqueID => uniqueID; public int Count => _entitiesCount; public int Capacity => _entitesCapacity; //_denseEntities.Length; - public EcsPipeline Pipeline => _pipeline; public EcsReadonlyGroup Entities => _allEntites.Readonly; - public ReadOnlySpan AllPools => pools; + public ReadOnlySpan AllPools => pools;// new ReadOnlySpan(pools, 0, _poolsCount); + public int PoolsCount => _poolsCount; #endregion #region Constructors/Destroy @@ -67,11 +65,13 @@ namespace DCFApixels.DragonECS EcsNullWorld nullWorld = new EcsNullWorld(); Worlds[0] = nullWorld; } - public EcsWorld(EcsPipeline pipline) : this(pipline, true) { } - internal EcsWorld(EcsPipeline pipline, bool isIndexable) + public EcsWorld() : this(true) { } + internal EcsWorld(bool isIndexable) { _entitesCapacity = 512; + _listeners = new List(); + if (isIndexable) { uniqueID = (short)_worldIdDispenser.GetFree(); @@ -82,8 +82,6 @@ namespace DCFApixels.DragonECS _worldTypeID = WorldMetaStorage.GetWorldId(Archetype); - _pipeline = pipline ?? EcsPipeline.Empty; - if (!_pipeline.IsInit) pipline.Init(); _entityDispenser = new IntDispenser(0); _nullPool = EcsNullPool.instance; pools = new IEcsPoolImplementation[512]; @@ -101,14 +99,8 @@ namespace DCFApixels.DragonECS _subjects = new EcsSubject[128]; _executors = new EcsQueryExecutor[128]; - - _entityCreate = _pipeline.GetRunner(); - _entityDestry = _pipeline.GetRunner(); - _pipeline.GetRunner>().Inject(this); - _pipeline.GetRunner().OnWorldCreate(this); } - public void Destroy() { _entityDispenser = null; @@ -125,7 +117,6 @@ namespace DCFApixels.DragonECS public void DestryWithPipeline() { Destroy(); - _pipeline.Destroy(); } #endregion @@ -151,7 +142,7 @@ namespace DCFApixels.DragonECS var pool = new TPool(); pools[uniqueID] = pool; pool.OnInit(this, uniqueID); - + _poolsCount++; //EcsDebug.Print(pool.GetType().FullName); } @@ -289,7 +280,7 @@ namespace DCFApixels.DragonECS #endregion #region Entity - public int NewEntity() + public int NewEmptyEntity() { int entityID = _entityDispenser.GetFree(); _entitiesCount++; @@ -316,15 +307,17 @@ namespace DCFApixels.DragonECS } foreach (var item in pools) item.OnWorldResize(_gens.Length); + + _listeners.InvokeOnWorldResize(_gens.Length); } _gens[entityID] &= GEN_BITS; - _entityCreate.OnEntityCreate(entityID); _allEntites.Add(entityID); + _listeners.InvokeOnNewEntity(entityID); return entityID; } - public entlong NewEntityLong() + public entlong NewEmptyEntityLong() { - int e = NewEntity(); + int e = NewEmptyEntity(); return GetEntityLong(e); } @@ -334,7 +327,7 @@ namespace DCFApixels.DragonECS _delEntBuffer[_delEntBufferCount++] = entityID; _gens[entityID] |= DEATH_GEN_BIT; _entitiesCount--; - _entityDestry.OnEntityDestroy(entityID); + _listeners.InvokeOnDelEntity(entityID); if (_delEntBufferCount >= _delEntBuffer.Length) ReleaseDelEntityBuffer(); @@ -350,12 +343,13 @@ namespace DCFApixels.DragonECS ReadOnlySpan buffser = new ReadOnlySpan(_delEntBuffer, 0, _delEntBufferCount); foreach (var pool in pools) pool.OnReleaseDelEntityBuffer(buffser); + _listeners.InvokeOnReleaseDelEntityBuffer(buffser); for (int i = 0; i < _delEntBufferCount; i++) _entityDispenser.Release(_delEntBuffer[i]); _delEntBufferCount = 0; } public short GetGen(int entityID) => _gens[entityID]; - public short GetComponentCount(int entityID) => _componentCounts[entityID]; + public short GetComponentsCount(int entityID) => _componentCounts[entityID]; public void DeleteEmptyEntites() { foreach (var e in _allEntites) @@ -365,6 +359,30 @@ namespace DCFApixels.DragonECS } } + public void CopyEntity(int fromEntityID, int toEntityID) + { + foreach (var pool in pools) + { + if(pool.Has(fromEntityID)) + pool.Copy(fromEntityID, toEntityID); + } + } + public int CloneEntity(int fromEntityID) + { + int newEntity = NewEmptyEntity(); + CopyEntity(fromEntityID, newEntity); + return newEntity; + } + public void CloneEntity(int fromEntityID, int toEntityID) + { + CopyEntity(fromEntityID, toEntityID); + foreach (var pool in pools) + { + if (!pool.Has(fromEntityID)&& pool.Has(toEntityID)) + pool.Del(toEntityID); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void IncrementEntityComponentCount(int entityID) { @@ -374,7 +392,7 @@ namespace DCFApixels.DragonECS internal void DecrementEntityComponentCount(int entityID) { var count = --_componentCounts[entityID]; - if(count == 0) + if(count == 0 && _allEntites.Has(entityID)) DelEntity(entityID); #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS @@ -406,39 +424,55 @@ namespace DCFApixels.DragonECS #endregion #region Debug - // public int GetComponents(int entity, ref object[] list) - // { - // var entityOffset = GetRawEntityOffset(entity); - // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; - // if (itemsCount == 0) { return 0; } - // if (list == null || list.Length < itemsCount) - // { - // list = new object[_pools.Length]; - // } - // var dataOffset = entityOffset + RawEntityOffsets.Components; - // for (var i = 0; i < itemsCount; i++) - // { - // list[i] = _pools[_entities[dataOffset + i]].GetRaw(entity); - // } - // return itemsCount; - // } - // - // public int GetComponentTypes(int entity, ref Type[] list) - // { - // var entityOffset = GetRawEntityOffset(entity); - // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; - // if (itemsCount == 0) { return 0; } - // if (list == null || list.Length < itemsCount) - // { - // list = new Type[_pools.Length]; - // } - // var dataOffset = entityOffset + RawEntityOffsets.Components; - // for (var i = 0; i < itemsCount; i++) - // { - // list[i] = _pools[_entities[dataOffset + i]].GetComponentType(); - // } - // return itemsCount; - // } + public void GetComponents(int entityID, List list) + { + list.Clear(); + var itemsCount = GetComponentsCount(entityID); + if (itemsCount == 0) + return; + + for (var i = 0; i < pools.Length; i++) + { + if (pools[i].Has(entityID)) + list.Add(pools[i].GetRaw(entityID)); + if (list.Count >= itemsCount) + break; + } + } + + // public int GetComponents(int entity, ref object[] list) + // { + // var entityOffset = GetRawEntityOffset(entity); + // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; + // if (itemsCount == 0) { return 0; } + // if (list == null || list.Length < itemsCount) + // { + // list = new object[_pools.Length]; + // } + // var dataOffset = entityOffset + RawEntityOffsets.Components; + // for (var i = 0; i < itemsCount; i++) + // { + // list[i] = _pools[_entities[dataOffset + i]].GetRaw(entity); + // } + // return itemsCount; + // } + + // public int GetComponentTypes(int entity, ref Type[] list) + // { + // var entityOffset = GetRawEntityOffset(entity); + // var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount]; + // if (itemsCount == 0) { return 0; } + // if (list == null || list.Length < itemsCount) + // { + // list = new Type[_pools.Length]; + // } + // var dataOffset = entityOffset + RawEntityOffsets.Components; + // for (var i = 0; i < itemsCount; i++) + // { + // list[i] = _pools[_entities[dataOffset + i]].GetComponentType(); + // } + // return itemsCount; + // } #endregion } @@ -446,8 +480,8 @@ namespace DCFApixels.DragonECS where TWorldArchetype : EcsWorld { public override Type Archetype => typeof(TWorldArchetype); - public EcsWorld(EcsPipeline pipline) : base(pipline) { } - internal EcsWorld(EcsPipeline pipline, bool isIndexable) : base(pipline, isIndexable) { } + public EcsWorld() : base() { } + internal EcsWorld(bool isIndexable) : base(isIndexable) { } } #region Utils @@ -577,4 +611,46 @@ namespace DCFApixels.DragonECS } } #endregion + + #region Callbacks Interface //TODO + public interface IEcsWorldEventListener + { + void OnWorldResize(int newSize); + void OnReleaseDelEntityBuffer(ReadOnlySpan buffer); + void OnWorldDestroy(); + void OnNewEntity(int entityID); + void OnDelEntity(int entityID); + } + #endregion + + #region Extensions + public static class WorldEventListExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnWorldResize(this List self, int newSize) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnWorldResize(newSize); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnReleaseDelEntityBuffer(this List self, ReadOnlySpan buffer) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnReleaseDelEntityBuffer(buffer); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnWorldDestroy(this List self) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnWorldDestroy(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnNewEntity(this List self, int entityID) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnNewEntity(entityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnDelEntity(this List self, int entityID) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnDelEntity(entityID); + } + } + #endregion } diff --git a/src/Executors/EcsWhereExecutor.cs b/src/Executors/EcsWhereExecutor.cs index e47b56b..c3121d4 100644 --- a/src/Executors/EcsWhereExecutor.cs +++ b/src/Executors/EcsWhereExecutor.cs @@ -67,6 +67,11 @@ namespace DCFApixels.DragonECS #endif return group.GetEnumerator(); } + + public override string ToString() + { + return group.ToString(); + } } #endregion } diff --git a/src/Pools/EcsAttachPool.cs b/src/Pools/EcsAttachPool.cs index f8d877c..cf58bcd 100644 --- a/src/Pools/EcsAttachPool.cs +++ b/src/Pools/EcsAttachPool.cs @@ -17,7 +17,8 @@ namespace DCFApixels.DragonECS private bool[] _entityFlags;// index = entityID / value = entityFlag;/ value = 0 = no entityID private T[] _items; //sparse private int _count; - private PoolRunners _poolRunners; + + private List _listeners; private EcsGroup _entities; public EcsReadonlyGroup Entities @@ -47,7 +48,7 @@ namespace DCFApixels.DragonECS _source = world; _id = componentID; - _poolRunners = new PoolRunners(world.Pipeline); + _listeners = new List(); _entities = EcsGroup.New(world); @@ -71,9 +72,9 @@ namespace DCFApixels.DragonECS entityFlag = true; _count++; _entities.Add(entityID); - _poolRunners.add.OnComponentAdd(entityID); + foreach (var item in _listeners) item.OnAdd(entityID); } - _poolRunners.write.OnComponentWrite(entityID); + foreach (var item in _listeners) item.OnWrite(entityID); _items[entityID].Target = target; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -84,7 +85,7 @@ namespace DCFApixels.DragonECS if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent(entityID); _sanitizeTargetWorld = target.world; #endif - _poolRunners.write.OnComponentWrite(entityID); + _listeners.InvokeOnWrite(entityID); _items[entityID].Target = target; } public void AddOrSet(int entityID, entlong target) @@ -115,7 +116,7 @@ namespace DCFApixels.DragonECS _entities.Remove(entityID); _entityFlags[entityID] = false; _count--; - _poolRunners.del.OnComponentDel(entityID); + _listeners.InvokeOnDel(entityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TryDel(int entityID) @@ -132,6 +133,16 @@ namespace DCFApixels.DragonECS else Add(toEntityID, Read(fromEntityID).Target); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + if (Has(toEntityID)) + toWorld.GetPool().Set(toEntityID, Read(fromEntityID).Target); + else + toWorld.GetPool().Add(toEntityID, Read(fromEntityID).Target); + } #endregion #region WorldCallbacks @@ -167,6 +178,19 @@ namespace DCFApixels.DragonECS void IEcsPool.SetRaw(int entityID, object dataRaw) => ((IEcsPool)this).Write(entityID) = (T)dataRaw; #endregion + #region Listeners + public void AddListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Add(listener); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Remove(listener); + } + #endregion + #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); diff --git a/src/Pools/EcsNotNullPool.cs b/src/Pools/EcsNotNullPool.cs index 7ed0f16..7e482d9 100644 --- a/src/Pools/EcsNotNullPool.cs +++ b/src/Pools/EcsNotNullPool.cs @@ -18,7 +18,8 @@ namespace DCFApixels.DragonECS private IEcsComponentReset _componentResetHandler; private IEcsComponentCopy _componentCopyHandler; - private PoolRunners _poolRunners; + + private List _listeners; #region Properites public int Count => _count; @@ -37,8 +38,10 @@ namespace DCFApixels.DragonECS _items = new T[world.Capacity]; _count = 0; + _listeners = new List(); + _componentResetHandler = EcsComponentResetHandler.instance; - _poolRunners = new PoolRunners(world.Pipeline); + _componentCopyHandler = EcsComponentCopyHandler.instance; } #endregion @@ -46,7 +49,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Write(int entityID) { - _poolRunners.write.OnComponentWrite(entityID); + _listeners.InvokeOnWrite(entityID); return ref _items[entityID]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -63,6 +66,10 @@ namespace DCFApixels.DragonECS { _componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID)); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { + _componentCopyHandler.Copy(ref Write(fromEntityID), ref toWorld.GetPool().Write(toEntityID)); + } #endregion #region Callbacks @@ -88,6 +95,19 @@ namespace DCFApixels.DragonECS void IEcsPool.SetRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw; #endregion + #region Listeners + public void AddListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Add(listener); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Remove(listener); + } + #endregion + #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 8a3c0a4..0f04f13 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -21,7 +21,8 @@ namespace DCFApixels.DragonECS private IEcsComponentReset _componentResetHandler; private IEcsComponentCopy _componentCopyHandler; - private PoolRunners _poolRunners; + + private List _listeners; #region Properites public int Count => _itemsCount; @@ -45,9 +46,10 @@ namespace DCFApixels.DragonECS _items = new T[capacity]; _itemsCount = 0; + _listeners = new List(); + _componentResetHandler = EcsComponentResetHandler.instance; _componentCopyHandler = EcsComponentCopyHandler.instance; - _poolRunners = new PoolRunners(world.Pipeline); } #endregion @@ -72,8 +74,7 @@ namespace DCFApixels.DragonECS Array.Resize(ref _items, _items.Length << 1); } this.IncrementEntityComponentCount(entityID); - _poolRunners.add.OnComponentAdd(entityID); - _poolRunners.write.OnComponentWrite(entityID); + _listeners.InvokeOnAddAndWrite(entityID); return ref _items[itemIndex]; // } } @@ -84,7 +85,7 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (!Has(entityID)) ThrowNotHaveComponent(entityID); #endif - _poolRunners.write.OnComponentWrite(entityID); + _listeners.InvokeOnWrite(entityID); return ref _items[_mapping[entityID]]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -113,9 +114,9 @@ namespace DCFApixels.DragonECS Array.Resize(ref _items, _items.Length << 1); } this.IncrementEntityComponentCount(entityID); - _poolRunners.add.OnComponentAdd(entityID); + _listeners.InvokeOnAdd(entityID); } - _poolRunners.write.OnComponentWrite(entityID); + _listeners.InvokeOnWrite(entityID); return ref _items[itemIndex]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -136,7 +137,7 @@ namespace DCFApixels.DragonECS _mapping[entityID] = 0; _itemsCount--; this.DecrementEntityComponentCount(entityID); - _poolRunners.del.OnComponentDel(entityID); + _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) { @@ -147,10 +148,14 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); #endif - if (Has(toEntityID)) - _componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID)); - else - _componentCopyHandler.Copy(ref Write(fromEntityID), ref Add(toEntityID)); + _componentCopyHandler.Copy(ref Write(fromEntityID), ref TryAddOrWrite(toEntityID)); + } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + _componentCopyHandler.Copy(ref Write(fromEntityID), ref toWorld.GetPool().TryAddOrWrite(toEntityID)); } #endregion @@ -175,6 +180,19 @@ namespace DCFApixels.DragonECS ref T IEcsPool.Write(int entityID) => ref Write(entityID); #endregion + #region Listeners + public void AddListener(IEcsPoolEventListener listener) + { + if(listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Add(listener); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + if(listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Remove(listener); + } + #endregion + #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 4502e92..2e28565 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -1,4 +1,6 @@ -using System; +using DCFApixels.DragonECS.Internal; +using System; +using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -21,7 +23,22 @@ namespace DCFApixels.DragonECS object GetRaw(int entityID); void SetRaw(int entityID, object dataRaw); void Copy(int fromEntityID, int toEntityID); + void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID); #endregion + + #region Add/Remove Listeners + void AddListener(IEcsPoolEventListener listener); + void RemoveListener(IEcsPoolEventListener listener); + #endregion + } + public interface IEcsPoolEventListener + { + /// Called after adding an entity to the pool, but before changing values. + void OnAdd(int entityID); + /// Is called when EcsPool.Write or EcsPool.Add is called, but before changing values. + void OnWrite(int entityID); + /// Called after deleting an entity from the pool + void OnDel(int entityID); } public interface IEcsPool { @@ -41,6 +58,7 @@ namespace DCFApixels.DragonECS /// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool. /// Component type public interface IEcsPoolImplementation : IEcsPool, IEcsPoolImplementation { } + public static class EcsPoolThrowHalper { public static void ThrowAlreadyHasComponent(int entityID) @@ -76,7 +94,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrDummy(this IEcsPool self) { - return self == null || self is Internal.EcsNullPool; + return self == null || self == EcsNullPool.instance; } } @@ -89,11 +107,11 @@ namespace DCFApixels.DragonECS public static EcsNullPool instance => new EcsNullPool(); #region Properties - int IEcsPool.ComponentID => throw new NotImplementedException(); + int IEcsPool.ComponentID => -1; Type IEcsPool.ComponentType => typeof(NullComponent); EcsWorld IEcsPool.World => throw new NotImplementedException(); - public int Count => 0; - public int Capacity => 0; + public int Count => -1; + public int Capacity => -1; #endregion #region Methods @@ -103,6 +121,7 @@ namespace DCFApixels.DragonECS object IEcsPool.GetRaw(int entityID) => throw new NotImplementedException(); void IEcsPool.SetRaw(int entity, object dataRaw) => throw new NotImplementedException(); void IEcsPool.Copy(int fromEntityID, int toEntityID) => throw new NotImplementedException(); + void IEcsPool.Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) => throw new NotImplementedException(); ref NullComponent IEcsPool.Add(int entityID) => throw new NotImplementedException(); ref readonly NullComponent IEcsPool.Read(int entityID) => throw new NotImplementedException(); ref NullComponent IEcsPool.Write(int entityID) => throw new NotImplementedException(); @@ -114,6 +133,11 @@ namespace DCFApixels.DragonECS void IEcsPoolImplementation.OnWorldResize(int newSize) { } void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { } #endregion + + #region Listeners + public void AddListener(IEcsPoolEventListener listener) { } + public void RemoveListener(IEcsPoolEventListener listener) { } + #endregion } } #endregion @@ -191,4 +215,34 @@ namespace DCFApixels.DragonECS public void Copy(ref T from, ref T to) => _fakeInstnace.Copy(ref from, ref to); } #endregion + + #region Extensions + public static class PoolEventListExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnAdd(this List self, int entityID) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnAdd(entityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnAddAndWrite(this List self, int entityID) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) + { + self[i].OnAdd(entityID); + self[i].OnWrite(entityID); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnWrite(this List self, int entityID) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnWrite(entityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvokeOnDel(this List self, int entityID) + { + for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnDel(entityID); + } + } + #endregion } diff --git a/src/Pools/EcsSinglePool.cs b/src/Pools/EcsSinglePool.cs index c3dacac..e829af6 100644 --- a/src/Pools/EcsSinglePool.cs +++ b/src/Pools/EcsSinglePool.cs @@ -17,7 +17,7 @@ namespace DCFApixels.DragonECS private int _count; private T _component; - private PoolRunners _poolRunners; + private List _listeners; #region Properites public ref T Instance @@ -40,7 +40,7 @@ namespace DCFApixels.DragonECS _mapping = new int[world.Capacity]; _count = 0; - _poolRunners = new PoolRunners(world.Pipeline); + _listeners = new List(); } #endregion @@ -52,7 +52,7 @@ namespace DCFApixels.DragonECS #endif _mapping[entityID] = ++_count; this.IncrementEntityComponentCount(entityID); - _poolRunners.add.OnComponentAdd(entityID); + _listeners.InvokeOnAddAndWrite(entityID); return ref _component; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -61,7 +61,7 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (!Has(entityID)) ThrowNotHaveComponent(entityID); #endif - _poolRunners.write.OnComponentWrite(entityID); + _listeners.InvokeOnWrite(entityID); return ref _component; } public ref T TryAddOrWrite(int entityID) @@ -92,7 +92,7 @@ namespace DCFApixels.DragonECS _mapping[entityID] = 0; _count--; this.DecrementEntityComponentCount(entityID); - _poolRunners.del.OnComponentDel(entityID); + _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) { @@ -105,6 +105,13 @@ namespace DCFApixels.DragonECS #endif TryAddOrWrite(toEntityID); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + toWorld.GetPool().TryAddOrWrite(toEntityID); + } #endregion #region Callbacks @@ -126,6 +133,19 @@ namespace DCFApixels.DragonECS void IEcsPool.SetRaw(int entityID, object dataRaw) => Instance = (T)dataRaw; #endregion + #region Listeners + public void AddListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Add(listener); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Remove(listener); + } + #endregion + #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 923cb36..13270d8 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -15,7 +15,7 @@ namespace DCFApixels.DragonECS private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private int _count; - private PoolRunners _poolRunners; + private List _listeners; private T _fakeComponent; @@ -36,7 +36,7 @@ namespace DCFApixels.DragonECS _mapping = new bool[world.Capacity]; _count = 0; - _poolRunners = new PoolRunners(world.Pipeline); + _listeners = new List(); } #endregion @@ -49,7 +49,7 @@ namespace DCFApixels.DragonECS _count++; _mapping[entityID] = true; this.IncrementEntityComponentCount(entityID); - _poolRunners.add.OnComponentAdd(entityID); + _listeners.InvokeOnAdd(entityID); } public void TryAdd(int entityID) { @@ -58,7 +58,7 @@ namespace DCFApixels.DragonECS _count++; _mapping[entityID] = true; this.IncrementEntityComponentCount(entityID); - _poolRunners.add.OnComponentAdd(entityID); + _listeners.InvokeOnAdd(entityID); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -74,7 +74,7 @@ namespace DCFApixels.DragonECS _mapping[entityID] = false; _count--; this.DecrementEntityComponentCount(entityID); - _poolRunners.del.OnComponentDel(entityID); + _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) { @@ -87,6 +87,13 @@ namespace DCFApixels.DragonECS #endif TryAdd(toEntityID); } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (!Has(fromEntityID)) ThrowNotHaveComponent(fromEntityID); +#endif + toWorld.GetPool().TryAdd(toEntityID); + } public void Set(int entityID, bool isHas) { if (isHas) @@ -100,6 +107,13 @@ namespace DCFApixels.DragonECS Del(entityID); } } + public void Toggle(int entityID) + { + if (Has(entityID)) + Del(entityID); + else + Add(entityID); + } #endregion #region Callbacks @@ -152,6 +166,19 @@ namespace DCFApixels.DragonECS } #endregion + #region Listeners + public void AddListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Add(listener); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + if (listener == null) { throw new ArgumentNullException("listener is null"); } + _listeners.Remove(listener); + } + #endregion + #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); diff --git a/src/Utils/ArrayUtility.cs b/src/Utils/ArrayUtility.cs index 2f55154..e5e72b6 100644 --- a/src/Utils/ArrayUtility.cs +++ b/src/Utils/ArrayUtility.cs @@ -8,30 +8,22 @@ namespace DCFApixels.DragonECS public static void Fill(T[] array, T value, int startIndex = 0, int length = -1) { if (length < 0) - { length = array.Length; - } else - { length = startIndex + length; - } for (int i = startIndex; i < length; i++) - { array[i] = value; - } } } - internal static unsafe class UnmanagedArray + internal static unsafe class UnmanagedArrayUtility { - public static void* New(int elementCount) - where T : struct + public static void* New(int elementCount) where T : struct { return Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * elementCount).ToPointer(); } - public static void* NewAndInit(int elementCount) - where T : struct + public static void* NewAndInit(int elementCount) where T : struct { int newSizeInBytes = Marshal.SizeOf(typeof(T)) * elementCount; byte* newArrayPointer = (byte*)Marshal.AllocHGlobal(newSizeInBytes).ToPointer(); @@ -39,7 +31,7 @@ namespace DCFApixels.DragonECS for (int i = 0; i < newSizeInBytes; i++) *(newArrayPointer + i) = 0; - return (void*)newArrayPointer; + return newArrayPointer; } public static void Free(void* pointerToUnmanagedMemory) @@ -47,8 +39,7 @@ namespace DCFApixels.DragonECS Marshal.FreeHGlobal(new IntPtr(pointerToUnmanagedMemory)); } - public static void* Resize(void* oldPointer, int newElementCount) - where T : struct + public static void* Resize(void* oldPointer, int newElementCount) where T : struct { return (Marshal.ReAllocHGlobal(new IntPtr(oldPointer), new IntPtr(Marshal.SizeOf(typeof(T)) * newElementCount))).ToPointer(); diff --git a/src/Utils/Extensions.cs b/src/Utils/Extensions.cs new file mode 100644 index 0000000..2e1fd6e --- /dev/null +++ b/src/Utils/Extensions.cs @@ -0,0 +1,10 @@ +namespace DCFApixels.DragonECS +{ + public static class Extensions + { + public static entlong ToEntityLong(this int self, EcsWorld world) + { + return world.GetEntityLong(self); + } + } +} diff --git a/src/Utils/SparseArray.cs b/src/Utils/SparseArray.cs index e89ec0c..493eac7 100644 --- a/src/Utils/SparseArray.cs +++ b/src/Utils/SparseArray.cs @@ -1,42 +1,49 @@ -using System; +//SparseArray. Analogous to Dictionary, but faster. +//Benchmark result of indexer.get speed test with 300 elements: +//[Dictinary: 5.786us] [SparseArray: 2.047us]. +using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace DCFApixels +namespace DCFApixels.DragonECS { public class SparseArray { - public const int MIN_CAPACITY = 16; + public const int MIN_CAPACITY_BITS_OFFSET = 4; + public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; private const int EMPTY = -1; private int[] _buckets = Array.Empty(); private Entry[] _entries = Array.Empty(); + private int[] _dense; private int _count; private int _freeList; private int _freeCount; + private int _modBitMask; + #region Properties - public TValue this[int key] + public ref TValue this[int key] { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _entries[FindEntry(key)].value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => Insert(key, value); + get => ref _entries[FindEntry(key)].value; + //set => Insert(key, value); } public int Count => _count; #endregion #region Constructors - public SparseArray(int capacity = MIN_CAPACITY) + public SparseArray(int minCapacity = MIN_CAPACITY) { - _buckets = new int[capacity]; - for (int i = 0; i < capacity; i++) + minCapacity = NormalizeCapacity(minCapacity); + _buckets = new int[minCapacity]; + for (int i = 0; i < minCapacity; i++) _buckets[i] = EMPTY; - _entries = new Entry[capacity]; + _entries = new Entry[minCapacity]; + _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; } #endregion @@ -53,19 +60,16 @@ namespace DCFApixels #endregion #region Find/Insert/Remove + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int FindEntry(int key) { - key &= 0x7FFFFFFF; - for (int i = _buckets[key % _buckets.Length]; i >= 0; i = _entries[i].next) - { + for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next) if (_entries[i].hashKey == key) return i; - } return -1; } private void Insert(int key, TValue value) { - key &= 0x7FFFFFFF; - int targetBucket = key % _buckets.Length; + int targetBucket = key & _modBitMask; for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) { @@ -88,10 +92,9 @@ namespace DCFApixels if (_count == _entries.Length) { Resize(); - targetBucket = key % _buckets.Length; + targetBucket = key & _modBitMask; } - index = _count; - _count++; + index = _count++; } _entries[index].next = _buckets[targetBucket]; @@ -101,8 +104,7 @@ namespace DCFApixels } public bool Remove(int key) { - key &= 0x7FFFFFFF; - int bucket = key % _buckets.Length; + int bucket = key & _modBitMask; int last = -1; for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next) { @@ -168,6 +170,7 @@ namespace DCFApixels private void Resize() { int newSize = _buckets.Length << 1; + _modBitMask = (newSize - 1) & 0x7FFFFFFF; Contract.Assert(newSize >= _entries.Length); int[] newBuckets = new int[newSize]; @@ -188,14 +191,67 @@ namespace DCFApixels _buckets = newBuckets; _entries = newEntries; } + + private int NormalizeCapacity(int capacity) + { + int result = MIN_CAPACITY; + while (result < capacity) result <<= 1; + return result; + } #endregion + + //#region Enumerator + // public Enumerator GetEnumerator() => new Enumerator(this); + // public struct Enumerator + // { + // private SparseArray _source; + // private int index; + // private int curretnItmeIndex; + // + // public ref readonly TValue Current + // { + // get + // { + // return ref _source._entries[curretnItmeIndex].value; + // } + // } + // + // public Enumerator(SparseArray source) + // { + // _source = source; + // index = 0; + // curretnItmeIndex = 0; + // } + // + // public bool MoveNext() + // { + // // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue + // while ((uint)index < (uint)_source.count) + // { + // if (dictionary.entries[index].hashCode >= 0) + // { + // current = new KeyValuePair(dictionary.entries[index].key, dictionary.entries[index].value); + // index++; + // return true; + // } + // index++; + // } + // + // index = dictionary.count + 1; + // current = new KeyValuePair(); + // return false; + // } + // } + // #endregion + #region Utils [StructLayout(LayoutKind.Sequential, Pack = 4)] private struct Entry { public int next; // Index of next entry, -1 if last - public int hashKey; + public int hashKey; public TValue value; } #endregion