Merge branch 'dev'

This commit is contained in:
Mikhail 2023-05-26 06:41:12 +08:00
commit d8c73e3c0d
30 changed files with 323 additions and 2373 deletions

View File

@ -1,72 +0,0 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
[DebugColor(DebugColor.White)]
public struct Parent : IEcsAttachComponent
{
public entlong entity;
[EditorBrowsable(EditorBrowsableState.Never)]
public entlong Target
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => entity;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => entity = value;
}
}
public static class ParentUtility
{
// public static int GetRootOrSelf(this HierarchySubject s, int entityID) => s.parents.GetRootOrSelf(entityID);
public static int GetRootOrSelf(this EcsAttachPool<Parent> parents, int entityID)
{
while (parents.Has(entityID) && parents.Read(entityID).entity.TryGetID(out int child))
entityID = child;
return entityID;
}
// public static bool IsRoot(this HierarchySubject s, int entityID) => s.parents.IsRoot(entityID);
public static bool IsRoot(this EcsAttachPool<Parent> parents, int entityID)
{
return !(parents.Has(entityID) && parents.Read(entityID).entity.IsAlive);
}
// public static bool TryGetRoot(this HierarchySubject s, int entityID, out int rootEntityID) => TryGetRoot(s.parents, entityID, out rootEntityID);
public static bool TryGetRoot(this EcsAttachPool<Parent> parents, EcsSubject conditionSubject, int entityID, out int rootEntityID)
{
rootEntityID = entityID;
while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child) && !conditionSubject.IsMatches(child))
rootEntityID = child;
return rootEntityID != entityID;
}
public static bool TryGetRoot(this EcsAttachPool<Parent> parents, int entityID, out int rootEntityID)
{
rootEntityID = entityID;
while (parents.Has(rootEntityID) && parents.Read(rootEntityID).entity.TryGetID(out int child))
rootEntityID = child;
return rootEntityID != entityID;
}
public static bool TryFindParentWithSubject(this EcsAttachPool<Parent> 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<TComponent>(this EcsAttachPool<Parent> parents, int entityID, out int resultEntityID) where TComponent : struct
{
var pool = parents.World.AllPools[parents.World.GetComponentID<TComponent>()];
resultEntityID = entityID;
while (!pool.Has(resultEntityID) &&
parents.Has(resultEntityID) &&
parents.Read(resultEntityID).entity.TryGetID(out int child))
{
resultEntityID = child;
}
return pool.Has(resultEntityID);
}
}
}

View File

@ -5,37 +5,8 @@ using System.Linq;
namespace DCFApixels.DragonECS
{
//TODO развить идею инжектов
//1) добавить расширенный метод инжекта, с 2 джинерик-аргументами, первый базовый тип и второй инжектируемый тип.
//напримере это будет работать так Inject<object, Foo> делает инжект объекта типа Foo для систем с IEcsInject<object> или с IEcsInject<Foo>
//2) добавить контейнер, который автоматически создается, собирает в себя все пре-инжекты и авто-инжектится во все системы.
//добавить контейнер, который автоматически создается, собирает в себя все пре-инжекты и авто-инжектится во все системы.
//но это спорная идея
namespace Internal
{
internal class PreInitInjectController
{
private EcsPipeline _source;
private InjectSystemBase[] _injectSystems;
private int _injectCount;
public PreInitInjectController(EcsPipeline source)
{
_injectCount = 0;
_source = source;
_injectSystems = _source.AllSystems.OfType<InjectSystemBase>().ToArray();
}
public bool OnInject()
{
_injectCount++;
return IsInjectionEnd;
}
public bool IsInjectionEnd => _injectCount >= _injectSystems.Length;
public void Destroy()
{
_source = null;
_injectSystems = null;
}
}
}
public interface IEcsPreInject : IEcsSystem
{
void PreInject(object obj);
@ -52,6 +23,29 @@ namespace DCFApixels.DragonECS
namespace Internal
{
internal class PreInitInjectController
{
private EcsPipeline _source;
private InjectSystemBase[] _injectSystems;
private int _injectCount;
public bool IsInjectionEnd => _injectCount >= _injectSystems.Length;
public PreInitInjectController(EcsPipeline source)
{
_injectCount = 0;
_source = source;
_injectSystems = _source.AllSystems.OfType<InjectSystemBase>().ToArray();
}
public bool OnInject()
{
_injectCount++;
return IsInjectionEnd;
}
public void Destroy()
{
_source = null;
_injectSystems = null;
}
}
[DebugHide, DebugColor(DebugColor.Gray)]
public sealed class EcsPreInjectRunner : EcsRunner<IEcsPreInject>, IEcsPreInject
{
@ -86,55 +80,43 @@ namespace DCFApixels.DragonECS
foreach (var item in targets) item.OnPreInitInjectionBefore();
}
}
}
public class InjectSystemBase { }
[DebugHide, DebugColor(DebugColor.Gray)]
public class InjectSystem<T> : InjectSystemBase, IEcsPreInitProcess, IEcsInject<PreInitInjectController>, IEcsPreInitInjectProcess
{
private T _injectedData;
private PreInitInjectController _injectController;
void IEcsInject<PreInitInjectController>.Inject(PreInitInjectController obj) => _injectController = obj;
public InjectSystem(T injectedData)
public class InjectSystemBase { }
[DebugHide, DebugColor(DebugColor.Gray)]
public class InjectSystem<T> : InjectSystemBase, IEcsPreInitProcess, IEcsInject<PreInitInjectController>, IEcsPreInitInjectProcess
{
_injectedData = injectedData;
}
public void PreInit(EcsPipeline pipeline)
{
if (_injectedData == null)
return;
if (_injectController == null)
private T _injectedData;
private PreInitInjectController _injectController;
void IEcsInject<PreInitInjectController>.Inject(PreInitInjectController obj) => _injectController = obj;
public InjectSystem(T injectedData)
{
_injectController = new PreInitInjectController(pipeline);
var injectMapRunner = pipeline.GetRunner<IEcsInject<PreInitInjectController>>();
pipeline.GetRunner<IEcsPreInitInjectProcess>().OnPreInitInjectionBefore();
injectMapRunner.Inject(_injectController);
_injectedData = injectedData;
}
var injectRunnerGeneric = pipeline.GetRunner<IEcsInject<T>>();
injectRunnerGeneric.Inject(_injectedData);
if (_injectController.OnInject())
public void PreInit(EcsPipeline pipeline)
{
_injectController.Destroy();
var injectCallbacksRunner = pipeline.GetRunner<IEcsPreInitInjectProcess>();
injectCallbacksRunner.OnPreInitInjectionAfter();
EcsRunner.Destroy(injectCallbacksRunner);
if (_injectedData == null) return;
if (_injectController == null)
{
_injectController = new PreInitInjectController(pipeline);
var injectMapRunner = pipeline.GetRunner<IEcsInject<PreInitInjectController>>();
pipeline.GetRunner<IEcsPreInitInjectProcess>().OnPreInitInjectionBefore();
injectMapRunner.Inject(_injectController);
}
var injectRunnerGeneric = pipeline.GetRunner<IEcsInject<T>>();
injectRunnerGeneric.Inject(_injectedData);
if (_injectController.OnInject())
{
_injectController.Destroy();
var injectCallbacksRunner = pipeline.GetRunner<IEcsPreInitInjectProcess>();
injectCallbacksRunner.OnPreInitInjectionAfter();
EcsRunner.Destroy(injectCallbacksRunner);
}
}
}
public void OnPreInitInjectionBefore() { }
public void OnPreInitInjectionAfter()
{
_injectController = null;
public void OnPreInitInjectionBefore() { }
public void OnPreInitInjectionAfter() => _injectController = null;
}
}
public static class InjectSystemExstensions
public static class InjectSystemExtensions
{
public static EcsPipeline.Builder Inject<T>(this EcsPipeline.Builder self, T data)
{

View File

@ -1,11 +0,0 @@
namespace DCFApixels.DragonECS
{
public sealed class HierarchySubject : EcsSubject
{
public readonly EcsAttachPool<Parent> parents;
public HierarchySubject(Builder b)
{
parents = b.Include<Parent>();
}
}
}

View File

@ -1,53 +1,51 @@
using System.Collections.Generic;
using DCFApixels.DragonECS.Internal;
namespace DCFApixels.DragonECS
{
[DebugHide, DebugColor(DebugColor.Black)]
public class SystemsBlockMarkerSystem : IEcsSystem
namespace Internal
{
public readonly string name;
public SystemsBlockMarkerSystem(string name) { this.name = name; }
}
[DebugHide, DebugColor(DebugColor.Grey)]
public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsPreInject
{
private List<EcsWorld> _worlds = new List<EcsWorld>();
public void PreInject(object obj)
[DebugHide, DebugColor(DebugColor.Black)]
public class SystemsBlockMarkerSystem : IEcsSystem
{
if (obj is EcsWorld world)
_worlds.Add(world);
public readonly string name;
public SystemsBlockMarkerSystem(string name) => this.name = name;
}
public void Run(EcsPipeline pipeline)
[DebugHide, DebugColor(DebugColor.Grey)]
public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsPreInject
{
foreach (var world in _worlds)
world.DeleteEmptyEntites();
}
}
[DebugHide, DebugColor(DebugColor.Grey)]
public class DeleteOneFrameComponentSystem<TWorld, TComponent> : IEcsRunProcess, IEcsInject<TWorld>
where TWorld : EcsWorld<TWorld>
where TComponent : struct, IEcsComponent
{
private TWorld _world;
public void Inject(TWorld obj) => _world = obj;
private sealed class Subject : EcsSubject
{
public EcsPool<TComponent> pool;
public Subject(Builder b)
private List<EcsWorld> _worlds = new List<EcsWorld>();
public void PreInject(object obj)
{
pool = b.Include<TComponent>();
if (obj is EcsWorld world)
_worlds.Add(world);
}
public void Run(EcsPipeline pipeline)
{
foreach (var world in _worlds)
world.DeleteEmptyEntites();
}
}
public void Run(EcsPipeline pipeline)
[DebugHide, DebugColor(DebugColor.Grey)]
public class DeleteOneFrameComponentSystem<TWorld, TComponent> : IEcsRunProcess, IEcsInject<TWorld>
where TWorld : EcsWorld<TWorld>
where TComponent : struct, IEcsComponent
{
foreach (var e in _world.Where(out Subject s))
s.pool.Del(e);
private sealed class Subject : EcsSubject
{
public EcsPool<TComponent> pool;
public Subject(Builder b) => pool = b.Include<TComponent>();
}
private TWorld _world;
public void Inject(TWorld obj) => _world = obj;
public void Run(EcsPipeline pipeline)
{
foreach (var e in _world.Where(out Subject s))
s.pool.Del(e);
}
}
}
public static class DeleteOneFrameComponentSystemExt
public static class DeleteOneFrameComponentSystemExtensions
{
private const string AUTO_DEL_LAYER = nameof(AUTO_DEL_LAYER);
public static EcsPipeline.Builder AutoDel<TWorld, TComponent>(this EcsPipeline.Builder b)

View File

@ -1,106 +0,0 @@
using DCFApixels.DragonECS.RunnersCore;
namespace DCFApixels.DragonECS
{
public interface IEcsComponentAdd : IEcsSystem
{
void OnComponentAdd<T>(int entityID);
}
public interface IEcsComponentWrite : IEcsSystem
{
void OnComponentWrite<T>(int entityID);
}
public interface IEcsComponentDel : IEcsSystem
{
void OnComponentDel<T>(int entityID);
}
public interface IEcsComponentLifecycle : IEcsComponentAdd, IEcsComponentWrite, IEcsComponentDel { }
namespace Internal
{
[DebugColor(DebugColor.Orange)]
public sealed class EcsEntityAddComponentRunner : EcsRunner<IEcsComponentAdd>, IEcsComponentAdd
{
public void OnComponentAdd<T>(int entityID)
{
foreach (var item in targets) item.OnComponentAdd<T>(entityID);
}
}
[DebugColor(DebugColor.Orange)]
public sealed class EcsEntityChangeComponentRunner : EcsRunner<IEcsComponentWrite>, IEcsComponentWrite
{
public void OnComponentWrite<T>(int entityID)
{
foreach (var item in targets) item.OnComponentWrite<T>(entityID);
}
}
[DebugColor(DebugColor.Orange)]
public sealed class EcsEntityDelComponentRunner : EcsRunner<IEcsComponentDel>, IEcsComponentDel
{
public void OnComponentDel<T>(int entityID)
{
foreach (var item in targets) item.OnComponentDel<T>(entityID);
}
}
}
public interface IEcsEntityCreate : IEcsSystem
{
void OnEntityCreate(int entityID);
}
public interface IEcsEntityDestroy : IEcsSystem
{
void OnEntityDestroy(int entityID);
}
public interface IEcsEntityLifecycle : IEcsEntityCreate, IEcsEntityDestroy { }
namespace Internal
{
[DebugColor(DebugColor.Orange)]
public sealed class EcsEntityCreateRunner : EcsRunner<IEcsEntityCreate>, IEcsEntityCreate
{
public void OnEntityCreate(int entityID)
{
foreach (var item in targets) item.OnEntityCreate(entityID);
}
}
[DebugColor(DebugColor.Orange)]
public sealed class EcsEntityDestroyRunner : EcsRunner<IEcsEntityDestroy>, IEcsEntityDestroy
{
public void OnEntityDestroy(int entityID)
{
foreach (var item in targets) item.OnEntityDestroy(entityID);
}
}
}
public interface IEcsWorldCreate : IEcsSystem
{
void OnWorldCreate(EcsWorld world);
}
public interface IEcsWorldDestroy : IEcsSystem
{
void OnWorldDestroy(EcsWorld world);
}
public interface IEcsWorldLifecycle : IEcsWorldCreate, IEcsWorldDestroy { }
namespace Internal
{
[DebugColor(DebugColor.Orange)]
public sealed class EcsWorldCreateRunner : EcsRunner<IEcsWorldCreate>, IEcsWorldCreate
{
public void OnWorldCreate(EcsWorld world)
{
foreach (var item in targets) item.OnWorldCreate(world);
}
}
[DebugColor(DebugColor.Orange)]
public sealed class EcsWorldDestryRunner : EcsRunner<IEcsWorldDestroy>, IEcsWorldDestroy
{
public void OnWorldDestroy(EcsWorld world)
{
foreach (var item in targets) item.OnWorldDestroy(world);
}
}
}
}

View File

@ -1,5 +1,9 @@
namespace DCFApixels.DragonECS
{
internal sealed class EcsNullWorld : EcsWorld<EcsNullWorld>
{
public EcsNullWorld() : base(false) { }
}
public sealed class EcsDefaultWorld : EcsWorld<EcsDefaultWorld> { }
public sealed class EcsEventWorld : EcsWorld<EcsEventWorld> { }
}

View File

@ -9,7 +9,6 @@
public const string DEBUG_WARNING_TAG = "WARNING";
public const string DEBUG_ERROR_TAG = "ERROR";
public const string PRE_BEGIN_LAYER = nameof(PRE_BEGIN_LAYER);
public const string BEGIN_LAYER = nameof(BEGIN_LAYER);
public const string BASIC_LAYER = nameof(BASIC_LAYER);

View File

@ -1,4 +1,5 @@
using System;
using DCFApixels.DragonECS.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@ -19,16 +20,13 @@ namespace DCFApixels.DragonECS
public readonly ref struct AutoScope
{
private readonly int id;
private readonly int _id;
public AutoScope(int id)
{
this.id = id;
_id = id;
EcsDebug.ProfileMarkBegin(id);
}
public void Dispose()
{
EcsDebug.ProfileMarkEnd(id);
}
public void Dispose() => EcsDebug.ProfileMarkEnd(_id);
}
}
@ -37,20 +35,19 @@ namespace DCFApixels.DragonECS
public static void Set<T>() where T : DebugService, new() => DebugService.Set<T>();
public static void Set(DebugService service) => DebugService.Set(service);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Print(object v) => DebugService.Instance.Print(v);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Print(string tag, object v)
{
#if !DISABLE_DRAGONECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUGGER
DebugService.Instance.Print(tag, v);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int RegisterMark(string name)
{
#if !DISABLE_DRAGONECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUGGER
return DebugService.Instance.RegisterMark(name);
#else
return 0;
@ -59,21 +56,21 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DeleteMark(string name)
{
#if !DISABLE_DRAGONECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUGGER
DebugService.Instance.DeleteMark(name);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProfileMarkBegin(int id)
{
#if !DISABLE_DRAGONECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUGGER
DebugService.Instance.ProfileMarkBegin(id);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProfileMarkEnd(int id)
{
#if !DISABLE_DRAGONECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUGGER
DebugService.Instance.ProfileMarkEnd(id);
#endif
}
@ -145,7 +142,7 @@ namespace DCFApixels.DragonECS
public DefaultDebugService()
{
#if !DISABLE_DRAGONECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUGGER
_stopwatchs = new Stopwatch[64];
_stopwatchsNames= new string[64];
#endif

View File

@ -5,11 +5,14 @@ namespace DCFApixels.DragonECS
{
public static class EcsDebugUtility
{
public static string GetGenericTypeFullName<T>(int maxDepth = 2) => GetGenericTypeFullName(typeof(T), maxDepth);
public static string GetGenericTypeFullName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, true);
public static string GetGenericTypeName<T>(int maxDepth = 2) => GetGenericTypeName(typeof(T), maxDepth);
public static string GetGenericTypeName(Type type, int maxDepth = 2)
public static string GetGenericTypeName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, false);
private static string GetGenericTypeNameInternal(Type type, int maxDepth, bool isFull)
{
#if (DEBUG && !DISABLE_DEBUG)
string friendlyName = type.Name;
string friendlyName = isFull ? type.FullName : type.Name;
if (!type.IsGenericType || maxDepth == 0)
return friendlyName;
@ -21,7 +24,7 @@ namespace DCFApixels.DragonECS
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; ++i)
{
string typeParamName = GetGenericTypeName(typeParameters[i], maxDepth - 1);
string typeParamName = GetGenericTypeNameInternal(typeParameters[i], maxDepth - 1, false);//чтобы строка не была слишком длинной, используются сокращенные имена для типов аргументов
friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
}
friendlyName += ">";

View File

@ -1,15 +1,12 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Profiling;
using delayedOp = System.Int32;
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
using static DCFApixels.DragonECS.EcsGroup.ThrowHalper;
#endif
namespace DCFApixels.DragonECS
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
using static EcsGroup.ThrowHalper;
#endif
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
public readonly ref struct EcsReadonlyGroup
{
@ -45,7 +42,7 @@ namespace DCFApixels.DragonECS
public bool IsReleazed
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.IsReleazed;
get => _source.IsReleased;
}
public int this[int index]
{
@ -93,26 +90,13 @@ namespace DCFApixels.DragonECS
#endregion
}
// индексация начинается с 1
// _delayedOps это int[] для отложенных операций, хранятся отложенные операции в виде int значения, если старший бит = 0 то это опреация добавленияб если = 1 то это операция вычитания
public unsafe class EcsGroup : IDisposable, IEquatable<EcsGroup>
{
private const int DEALAYED_ADD = 0;
private const int DEALAYED_REMOVE = int.MinValue;
private EcsWorld _source;
private int[] _dense;
private int[] _sparse;
private int _count;
private delayedOp[] _delayedOps;
private int _delayedOpsCount;
private int _lockCount;
private bool _isReleazed = true;
private bool _isReleased = true;
#region Properties
public EcsWorld World => _source;
@ -136,17 +120,17 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new EcsReadonlyGroup(this);
}
public bool IsReleazed
public bool IsReleased
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _isReleazed;
get => _isReleased;
}
public int this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (index < 0 || index >= Count) ThrowArgumentOutOfRange();
#endif
return _dense[index];
@ -161,27 +145,15 @@ namespace DCFApixels.DragonECS
return world.GetGroupFromPool();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EcsGroup(EcsWorld world, int denseCapacity = 64, int delayedOpsCapacity = 128)
internal EcsGroup(EcsWorld world, int denseCapacity = 64)
{
_source = world;
_source.RegisterGroup(this);
_dense = new int[denseCapacity];
_sparse = new int[world.Capacity];
_delayedOps = new delayedOp[delayedOpsCapacity];
_lockCount = 0;
_delayedOpsCount = 0;
_count = 0;
}
//защита от криворукости
//перед сборкой мусора снова создает сильную ссылку и возвращает в пул
//TODO переделат или удалить, так как сборщик мусора просыпается только после 12к и более экземпляров, только тогда и вызывается финализатор, слишком жирно
~EcsGroup()
{
Release();
}
#endregion
#region Has
@ -211,17 +183,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AddInternal(int entityID)
{
//if (_lockCount > 0)
//{
// AddDelayedOp(entityID, DEALAYED_ADD);
// return;
//}
AggressiveAdd(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AggressiveAdd(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (Has(entityID)) ThrowAlreadyContains(entityID);
#endif
if (++_count >= _dense.Length)
@ -239,17 +201,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RemoveInternal(int entityID)
{
//if (_lockCount > 0)
//{
// AddDelayedOp(entityID, DEALAYED_REMOVE);
// return;
//}
AggressiveRemove(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AggressiveRemove(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowDoesNotContain(entityID);
#endif
_dense[_sparse[entityID]] = _dense[_count];
@ -257,22 +209,12 @@ namespace DCFApixels.DragonECS
_sparse[entityID] = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddDelayedOp(int entityID, int isAddBitFlag)
{
if (_delayedOpsCount >= _delayedOps.Length)
{
Array.Resize(ref _delayedOps, _delayedOps.Length << 1);
}
_delayedOps[_delayedOpsCount++] = entityID | isAddBitFlag; // delayedOp = entityID add isAddBitFlag
}
public void RemoveUnusedEntityIDs()
{
foreach (var e in this)
{
if (!_source.IsUsed(e))
AggressiveRemove(e);
RemoveInternal(e);
}
}
#endregion
@ -293,13 +235,13 @@ namespace DCFApixels.DragonECS
public void CopyFrom(EcsReadonlyGroup group) => CopyFrom(group.GetGroupInternal());
public void CopyFrom(EcsGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (group.World != _source) throw new ArgumentException("groupFilter.WorldIndex != WorldIndex");
#endif
if(_count > 0)
Clear();
foreach (var item in group)
AggressiveAdd(item);
AddInternal(item);
}
public EcsGroup Clone()
{
@ -316,12 +258,12 @@ namespace DCFApixels.DragonECS
/// <summary>as Union sets</summary>
public void UnionWith(EcsGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (_source != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in group)
if (!Has(item))
AggressiveAdd(item);
AddInternal(item);
}
/// <summary>as Except sets</summary>
@ -330,12 +272,12 @@ namespace DCFApixels.DragonECS
/// <summary>as Except sets</summary>
public void ExceptWith(EcsGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (_source != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in this)
if (group.Has(item))
AggressiveRemove(item);
RemoveInternal(item);
}
/// <summary>as Intersect sets</summary>
@ -344,12 +286,12 @@ namespace DCFApixels.DragonECS
/// <summary>as Intersect sets</summary>
public void AndWith(EcsGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (World != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in this)
if (!group.Has(item))
AggressiveRemove(item);
RemoveInternal(item);
}
/// <summary>as Symmetric Except sets</summary>
@ -358,14 +300,14 @@ namespace DCFApixels.DragonECS
/// <summary>as Symmetric Except sets</summary>
public void XorWith(EcsGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (_source != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in group)
if (Has(item))
AggressiveRemove(item);
RemoveInternal(item);
else
AggressiveAdd(item);
AddInternal(item);
}
#endregion
@ -374,13 +316,13 @@ namespace DCFApixels.DragonECS
/// <returns>new group from pool</returns>
public static EcsGroup Except(EcsGroup a, EcsGroup b)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (a._source != b._source) throw new ArgumentException("a.WorldIndex != b.WorldIndex");
#endif
EcsGroup result = a._source.GetGroupFromPool();
foreach (var item in a)
if (!b.Has(item))
result.AggressiveAdd(item);
result.AddInternal(item);
a._source.ReleaseGroup(a);
return result;
}
@ -388,13 +330,13 @@ namespace DCFApixels.DragonECS
/// <returns>new group from pool</returns>
public static EcsGroup And(EcsGroup a, EcsGroup b)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (a._source != b._source) throw new ArgumentException("a.WorldIndex != b.WorldIndex");
#endif
EcsGroup result = a._source.GetGroupFromPool();
foreach (var item in a)
if (b.Has(item))
result.AggressiveAdd(item);
result.AddInternal(item);
a._source.ReleaseGroup(a);
return result;
}
@ -402,12 +344,12 @@ namespace DCFApixels.DragonECS
/// <returns>new group from pool</returns>
public static EcsGroup Union(EcsGroup a, EcsGroup b)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (a._source != b._source) throw new ArgumentException("a.WorldIndex != b.WorldIndex");
#endif
EcsGroup result = a._source.GetGroupFromPool();
foreach (var item in a)
result.AggressiveAdd(item);
result.AddInternal(item);
foreach (var item in a)
result.Add(item);
return result;
@ -415,34 +357,9 @@ namespace DCFApixels.DragonECS
#endregion
#region GetEnumerator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Unlock()
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_lockCount <= 0)
{
throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}.");
}
#endif
if (--_lockCount <= 0)
{
for (int i = 0; i < _delayedOpsCount; i++)
{
delayedOp op = _delayedOps[i];
if (op >= 0) //delayedOp.IsAdded
AggressiveAdd(op & int.MaxValue); //delayedOp.EcsEntity
else
AggressiveRemove(op & int.MaxValue); //delayedOp.EcsEntity
}
}
}
private ProfilerMarker _getEnumeratorReturn = new ProfilerMarker("EcsGroup.GetEnumerator");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator()
{
// _lockCount++;
return new Enumerator(this);
}
#endregion
@ -538,13 +455,13 @@ namespace DCFApixels.DragonECS
}
public void Release()
{
_isReleazed = true;
_isReleased = true;
_source.ReleaseGroup(this);
}
#endregion
#region ThrowHalper
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
internal static class ThrowHalper
{
[MethodImpl(MethodImplOptions.NoInlining)]
@ -558,6 +475,7 @@ namespace DCFApixels.DragonECS
#endregion
}
#region Extensions
public static class EcsGroupExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -571,4 +489,5 @@ namespace DCFApixels.DragonECS
if (array.Length < self.CapacityDense) Array.Resize(ref array, self.CapacityDense);
}
}
#endregion
}

View File

@ -1,4 +1,5 @@
using DCFApixels.DragonECS.RunnersCore;
using DCFApixels.DragonECS.Internal;
using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Collections;
using System.Collections.Generic;
@ -10,21 +11,6 @@ namespace DCFApixels.DragonECS
{
public sealed class EcsPipeline
{
private static EcsPipeline _empty;
public static EcsPipeline Empty
{
get
{
if(_empty == null)
{
_empty = new EcsPipeline(Array.Empty<IEcsSystem>());
_empty.Init();
_empty._isEmptyDummy = true;
}
return _empty;
}
}
private IEcsSystem[] _allSystems;
private Dictionary<Type, IEcsRunner> _runners;
private IEcsRunProcess _runRunnerCache;
@ -104,7 +90,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Run()
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
CheckBeforeInitForMethod(nameof(Run));
CheckAfterDestroyForMethod(nameof(Run));
#endif
@ -115,7 +101,7 @@ namespace DCFApixels.DragonECS
if (_isEmptyDummy)
return;
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
CheckBeforeInitForMethod(nameof(Run));
#endif
if (_isDestoryed == true)
@ -129,7 +115,7 @@ namespace DCFApixels.DragonECS
#endregion
#region StateChecks
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
private void CheckBeforeInitForMethod(string methodName)
{
if (!_isInit)
@ -159,7 +145,6 @@ namespace DCFApixels.DragonECS
private HashSet<Type> _uniqueTypes;
private readonly Dictionary<string, List<IEcsSystem>> _systems;
private readonly string _basicLayer;
public readonly LayerList Layers;
public Builder()
{
@ -197,10 +182,7 @@ namespace DCFApixels.DragonECS
List<IEcsSystem> list;
if (!_systems.TryGetValue(layerName, out list))
{
list = new List<IEcsSystem>
{
new SystemsBlockMarkerSystem(layerName.ToString())
};
list = new List<IEcsSystem> { new SystemsBlockMarkerSystem(layerName.ToString()) };
_systems.Add(layerName, list);
}
if ((_uniqueTypes.Add(system.GetType()) == false && isUnique))
@ -219,8 +201,6 @@ namespace DCFApixels.DragonECS
public EcsPipeline Build()
{
//EcsDebug.Print(string.Join(", ", Layers));
Add(new DeleteEmptyEntitesSystem(), EcsConsts.POST_END_LAYER);
List<IEcsSystem> result = new List<IEcsSystem>(32);
@ -350,30 +330,21 @@ namespace DCFApixels.DragonECS
public interface IEcsModule
{
public void ImportSystems(EcsPipeline.Builder b);
void ImportSystems(EcsPipeline.Builder b);
}
#region Extensions
public static class EcsPipelineExtensions
{
public static bool IsNullOrDestroyed(this EcsPipeline self)
{
return self == null || self.IsDestoryed;
}
public static bool IsNullOrDestroyed(this EcsPipeline self) => self == null || self.IsDestoryed;
public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, string layerName = null)
{
foreach (var item in range)
{
self.Add(item, layerName);
}
foreach (var item in range) self.Add(item, layerName);
return self;
}
public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, string layerName = null)
{
foreach (var item in range)
{
self.AddUnique(item, layerName);
}
foreach (var item in range) self.AddUnique(item, layerName);
return self;
}
public static EcsPipeline BuildAndInit(this EcsPipeline.Builder self)
@ -382,7 +353,6 @@ namespace DCFApixels.DragonECS
result.Init();
return result;
}
}
#endregion
}

View File

@ -5,6 +5,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using static DCFApixels.DragonECS.EcsDebugUtility;
namespace DCFApixels.DragonECS
{
@ -31,21 +32,19 @@ namespace DCFApixels.DragonECS
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IEcsRunner
{
public EcsPipeline Source { get; }
public Type Interface { get; }
public IList Targets { get; }
public object Filter { get; }
public bool IsHasFilter { get; }
public bool IsDestroyed { get; }
public bool IsEmpty { get; }
public void Destroy();
EcsPipeline Source { get; }
Type Interface { get; }
IList Targets { get; }
object Filter { get; }
bool IsHasFilter { get; }
bool IsDestroyed { get; }
bool IsEmpty { get; }
void Destroy();
}
internal static class EcsRunnerActivator
{
private static Dictionary<Type, Type> _runnerHandlerTypes; //interface base type/Runner handler type pairs;
static EcsRunnerActivator()
{
List<Exception> delayedExceptions = new List<Exception>();
@ -58,25 +57,23 @@ namespace DCFApixels.DragonECS
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition()));
}
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
for (int i = 0; i < runnerHandlerTypes.Count; i++)
{
var e = CheckRunnerValide(runnerHandlerTypes[i]);
if (e != null)
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
for (int i = 0; i < runnerHandlerTypes.Count; i++)
{
runnerHandlerTypes.RemoveAt(i--);
delayedExceptions.Add(e);
var e = CheckRunnerValide(runnerHandlerTypes[i]);
if (e != null)
{
runnerHandlerTypes.RemoveAt(i--);
delayedExceptions.Add(e);
}
}
}
#endif
_runnerHandlerTypes = new Dictionary<Type, Type>();
foreach (var item in runnerHandlerTypes)
{
Type interfaceType = item.BaseType.GenericTypeArguments[0];
// if(!_runnerHandlerTypes.ContainsKey(interfaceType.GUID))//TODO это кажется костыль, изначально все работало без этого ифа
_runnerHandlerTypes.Add(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, item);
_runnerHandlerTypes.Add(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, item);
}
if (delayedExceptions.Count > 0)
{
foreach (var item in delayedExceptions) EcsDebug.Print(EcsConsts.DEBUG_ERROR_TAG, item.Message);
@ -91,24 +88,23 @@ namespace DCFApixels.DragonECS
if (type.ReflectedType != null)
{
return new EcsRunnerImplementationException($"{type.FullName}.ReflectedType must be Null, but equal to {type.ReflectedType.FullName}.");
return new EcsRunnerImplementationException($"{GetGenericTypeFullName(type, 1)}.ReflectedType must be Null, but equal to {GetGenericTypeFullName(type.ReflectedType, 1)}.");
}
if (!baseTypeArgument.IsInterface)
{
return new EcsRunnerImplementationException($"Argument T of class EcsRunner<T>, can only be an inetrface.The {baseTypeArgument.FullName} type is not an interface.");
return new EcsRunnerImplementationException($"Argument T of class EcsRunner<T>, can only be an inetrface. The {GetGenericTypeFullName(baseTypeArgument, 1)} type is not an interface.");
}
var interfaces = type.GetInterfaces();
if (!interfaces.Any(o => o == baseTypeArgument))
{
return new EcsRunnerImplementationException($"Runner {type.FullName} does not implement interface {baseTypeArgument.FullName}.");
return new EcsRunnerImplementationException($"Runner {GetGenericTypeFullName(type, 1)} does not implement interface {GetGenericTypeFullName(baseTypeArgument, 1)}.");
}
return null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InitFor<TInterface>() where TInterface : IEcsSystem
{
@ -116,7 +112,7 @@ namespace DCFApixels.DragonECS
if (!_runnerHandlerTypes.TryGetValue(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, out Type runnerType))
{
throw new Exception();
throw new EcsRunnerImplementationException($"There is no implementation of a runner for the {GetGenericTypeFullName<TInterface>(1)} interface.");
}
if (interfaceType.IsGenericType)
{
@ -134,23 +130,23 @@ namespace DCFApixels.DragonECS
private static Type _subclass;
internal static void Register(Type subclass)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_subclass != null)
{
throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass");
}
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (_subclass != null)
{
throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass");
}
Type interfaceType = typeof(TInterface);
Type interfaceType = typeof(TInterface);
var interfaces = interfaceType.GetInterfaces();
if (interfaceType.IsInterface == false)
{
throw new ArgumentException($"{typeof(TInterface).FullName} is not interface");
}
if (interfaces.Length != 1 || interfaces[0] != typeof(IEcsSystem))
{
throw new ArgumentException($"{typeof(TInterface).FullName} does not directly inherit the {nameof(IEcsSystem)} interface");
}
var interfaces = interfaceType.GetInterfaces();
if (interfaceType.IsInterface == false)
{
throw new ArgumentException($"{typeof(TInterface).FullName} is not interface");
}
if (interfaces.Length != 1 || interfaces[0] != typeof(IEcsSystem))
{
throw new ArgumentException($"{typeof(TInterface).FullName} does not directly inherit the {nameof(IEcsSystem)} interface");
}
#endif
_subclass = subclass;
}
@ -192,9 +188,7 @@ namespace DCFApixels.DragonECS
#region Instantiate
private static TInterface Instantiate(EcsPipeline source, TInterface[] targets, bool isHasFilter, object filter)
{
if (_subclass == null)
EcsRunnerActivator.InitFor<TInterface>();
if (_subclass == null) EcsRunnerActivator.InitFor<TInterface>();
var instance = (EcsRunner<TInterface>)Activator.CreateInstance(_subclass);
return (TInterface)(IEcsSystem)instance.Set(source, targets, isHasFilter, filter);
}
@ -235,7 +229,6 @@ namespace DCFApixels.DragonECS
OnSetup();
return this;
}
internal void Rebuild()
{
if (_isHasFilter)
@ -243,7 +236,6 @@ namespace DCFApixels.DragonECS
else
Set(_source, FilterSystems(_source.AllSystems, _filter), _isHasFilter, _filter);
}
public void Destroy()
{
_isDestroyed = true;
@ -254,11 +246,11 @@ namespace DCFApixels.DragonECS
_filter = null;
OnDestroy();
}
protected virtual void OnSetup() { }
protected virtual void OnDestroy() { }
}
}
#region Extensions
public static class EcsRunner
{

View File

@ -13,7 +13,6 @@ namespace DCFApixels.DragonECS
internal EcsWorld source;
[EditorBrowsable(EditorBrowsableState.Always)]
internal EcsMask mask;
private bool _isInit;
#region Properties
@ -26,7 +25,7 @@ namespace DCFApixels.DragonECS
#endregion
#region Methods
public bool IsMatches(int entityID) => source.IsMaskCompatible(mask, entityID);
public bool IsMatches(int entityID) => source.IsMatchesMask(mask, entityID);
#endregion
#region Builder
@ -84,7 +83,7 @@ namespace DCFApixels.DragonECS
public void IncludeImplicit<TComponent>()
{
int id = _world.GetComponentID<TComponent>();
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
#endif
_inc.Add(_world.GetComponentID<TComponent>());
@ -92,7 +91,7 @@ namespace DCFApixels.DragonECS
public void ExcludeImplicit<TComponent>()
{
int id = _world.GetComponentID<TComponent>();
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
#endif
_exc.Add(_world.GetComponentID<TComponent>());
@ -111,6 +110,7 @@ namespace DCFApixels.DragonECS
#endregion
}
#region Extensions
public static class EcsSubjectExtensions
{
public static EcsSubjectIterator<TSubject> GetIterator<TSubject>(this TSubject self) where TSubject : EcsSubject
@ -122,6 +122,7 @@ namespace DCFApixels.DragonECS
return new EcsSubjectIterator<TSubject>(self, sourceGroup);
}
}
#endregion
#region BuilderBase
public abstract class EcsSubjectBuilderBase
@ -135,20 +136,18 @@ namespace DCFApixels.DragonECS
#region Mask
public sealed class EcsMask
{
internal readonly Type WorldType;
internal readonly int[] Inc;
internal readonly int[] Exc;
internal readonly Type _worldType;
internal readonly int[] _inc;
internal readonly int[] _exc;
public EcsMask(Type worldType, int[] inc, int[] exc)
{
WorldType = worldType;
Inc = inc;
Exc = exc;
_worldType = worldType;
_inc = inc;
_exc = exc;
}
public override string ToString()
{
return $"Inc({string.Join(", ", Inc)}) Exc({string.Join(", ", Exc)})";
return $"Inc({string.Join(", ", _inc)}) Exc({string.Join(", ", _exc)})";
}
}
#endregion
@ -181,7 +180,7 @@ namespace DCFApixels.DragonECS
group.Clear();
var enumerator = GetEnumerator();
while (enumerator.MoveNext())
group.AggressiveAdd(enumerator.Current);
group.AddInternal(enumerator.Current);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() => new Enumerator(sourceGroup, s);
@ -207,9 +206,9 @@ namespace DCFApixels.DragonECS
public Enumerator(EcsReadonlyGroup sourceGroup, EcsSubject subject)
{
_sourceGroup = sourceGroup.GetEnumerator();
_inc = subject.mask.Inc;
_exc = subject.mask.Exc;
_pools = subject.World.pools;
_inc = subject.mask._inc;
_exc = subject.mask._exc;
_pools = subject.World._pools;
}
public int Current
{

View File

@ -1,17 +1,11 @@
using System;
using DCFApixels.DragonECS.Internal;
using DCFApixels.DragonECS.Utils;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
using Internal;
internal sealed class EcsNullWorld : EcsWorld<EcsNullWorld>
{
public EcsNullWorld() : base(false) { }
}
public abstract class EcsWorld
{
private const short GEN_BITS = 0x7fff;
@ -20,6 +14,7 @@ namespace DCFApixels.DragonECS
public static EcsWorld[] Worlds = new EcsWorld[8];
private static IntDispenser _worldIdDispenser = new IntDispenser(0);
public readonly short uniqueID;
private int _worldTypeID;
@ -27,17 +22,17 @@ namespace DCFApixels.DragonECS
private IntDispenser _entityDispenser;
private int _entitiesCount;
private int _entitesCapacity;
private short[] _gens; //старший бит указывает на то жива ли сущьность.
private short[] _gens; //старший бит указывает на то жива ли сущность
private short[] _componentCounts;
private EcsGroup _allEntites;
//буфер удаления откладывает освобождение андишников сущьностей.
//Нужен для того чтобы запускать некоторые процесыы связанные с удалением сущьности не по одному при каждом удалении, а пачкой
//буфер удаления откладывает освобождение андишников сущностей.
//Нужен для того чтобы запускать некоторые процесыы связанные с удалением сущности не по одному при каждом удалении, а пачкой
//В теории такой подход частично улучшает ситуацию с переполнением поколений
private int[] _delEntBuffer;
private int _delEntBufferCount;
internal IEcsPoolImplementation[] pools;
internal IEcsPoolImplementation[] _pools;
private EcsNullPool _nullPool;
private int _poolsCount = 0;
@ -55,7 +50,7 @@ namespace DCFApixels.DragonECS
public int Count => _entitiesCount;
public int Capacity => _entitesCapacity; //_denseEntities.Length;
public EcsReadonlyGroup Entities => _allEntites.Readonly;
public ReadOnlySpan<IEcsPoolImplementation> AllPools => pools;// new ReadOnlySpan<IEcsPoolImplementation>(pools, 0, _poolsCount);
public ReadOnlySpan<IEcsPoolImplementation> AllPools => _pools;// new ReadOnlySpan<IEcsPoolImplementation>(pools, 0, _poolsCount);
public int PoolsCount => _poolsCount;
#endregion
@ -84,8 +79,8 @@ namespace DCFApixels.DragonECS
_entityDispenser = new IntDispenser(0);
_nullPool = EcsNullPool.instance;
pools = new IEcsPoolImplementation[512];
ArrayUtility.Fill(pools, _nullPool);
_pools = new IEcsPoolImplementation[512];
ArrayUtility.Fill(_pools, _nullPool);
_gens = new short[_entitesCapacity];
_componentCounts = new short[_entitesCapacity];
@ -106,7 +101,7 @@ namespace DCFApixels.DragonECS
_entityDispenser = null;
//_denseEntities = null;
_gens = null;
pools = null;
_pools = null;
_nullPool = null;
_subjects = null;
_executors = null;
@ -130,27 +125,27 @@ namespace DCFApixels.DragonECS
{
int uniqueID = WorldMetaStorage.GetComponentId<TComponent>(_worldTypeID);
if (uniqueID >= pools.Length)
if (uniqueID >= _pools.Length)
{
int oldCapacity = pools.Length;
Array.Resize(ref pools, pools.Length << 1);
ArrayUtility.Fill(pools, _nullPool, oldCapacity, oldCapacity - pools.Length);
int oldCapacity = _pools.Length;
Array.Resize(ref _pools, _pools.Length << 1);
ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length);
}
if (pools[uniqueID] == _nullPool)
if (_pools[uniqueID] == _nullPool)
{
var pool = new TPool();
pools[uniqueID] = pool;
_pools[uniqueID] = pool;
pool.OnInit(this, uniqueID);
_poolsCount++;
//EcsDebug.Print(pool.GetType().FullName);
}
return (TPool)pools[uniqueID];
return (TPool)_pools[uniqueID];
}
#endregion
#region Queries
#region Subjects
public TSubject GetSubject<TSubject>() where TSubject : EcsSubject
{
int uniqueID = WorldMetaStorage.GetSubjectId<TSubject>(_worldTypeID);
@ -160,119 +155,60 @@ namespace DCFApixels.DragonECS
_subjects[uniqueID] = EcsSubject.Builder.Build<TSubject>(this);
return (TSubject)_subjects[uniqueID];
}
#region Iterate
public EcsSubjectIterator<TSubject> IterateFor<TSubject>(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject
{
subject = GetSubject<TSubject>();
return subject.GetIteratorFor(sourceGroup);
}
public EcsSubjectIterator<TSubject> IterateFor<TSubject>(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
{
return GetSubject<TSubject>().GetIteratorFor(sourceGroup);
}
public EcsSubjectIterator<TSubject> Iterate<TSubject>(out TSubject subject) where TSubject : EcsSubject
{
subject = GetSubject<TSubject>();
return subject.GetIterator();
}
public EcsSubjectIterator<TSubject> Iterate<TSubject>() where TSubject : EcsSubject
{
return GetSubject<TSubject>().GetIterator();
}
#endregion
#region Where
private EcsWhereExecutor<TSubject> GetWhereExecutor<TSubject>() where TSubject : EcsSubject
#region Queries
public TExecutor GetExecutor<TExecutor>(Func<EcsWorld, TExecutor> builder) where TExecutor: EcsQueryExecutor
{
int id = WorldMetaStorage.GetExecutorId<EcsWhereExecutor<TSubject>>(_worldTypeID);
int id = WorldMetaStorage.GetExecutorId<TExecutor>(_worldTypeID);
if (id >= _executors.Length)
Array.Resize(ref _executors, _executors.Length << 1);
if (_executors[id] == null)
_executors[id] = new EcsWhereExecutor<TSubject>(GetSubject<TSubject>());
return (EcsWhereExecutor<TSubject>)_executors[id];
_executors[id] = builder(this);
return (TExecutor)_executors[id];
}
private EcsWhereExecutor<TSubject> EcsWhereExecutorBuilder<TSubject>(EcsWorld world) where TSubject : EcsSubject
{
return new EcsWhereExecutor<TSubject>(world.GetSubject<TSubject>());
}
public EcsWhereResult<TSubject> WhereFor<TSubject>(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject
{
var executor = GetWhereExecutor<TSubject>();
var executor = GetExecutor(EcsWhereExecutorBuilder<TSubject>);
subject = executor.Subject;
return executor.ExecuteFor(sourceGroup);
}
public EcsWhereResult<TSubject> WhereFor<TSubject>(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
{
return GetWhereExecutor<TSubject>().ExecuteFor(sourceGroup);
return GetExecutor(EcsWhereExecutorBuilder<TSubject>).ExecuteFor(sourceGroup);
}
public EcsWhereResult<TSubject> Where<TSubject>(out TSubject subject) where TSubject : EcsSubject
{
var executor = GetWhereExecutor<TSubject>();
var executor = GetExecutor(EcsWhereExecutorBuilder<TSubject>);
subject = executor.Subject;
return executor.Execute();
}
public EcsWhereResult<TSubject> Where<TSubject>() where TSubject : EcsSubject
{
return GetWhereExecutor<TSubject>().Execute();
return GetExecutor(EcsWhereExecutorBuilder<TSubject>).Execute();
}
#endregion
#region Join
private EcsJoinAttachExecutor<TSubject, TAttachComponent> GetJoinAttachExecutor<TSubject, TAttachComponent>()
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
#region IsMatchesMask
public bool IsMatchesMask(EcsMask mask, int entityID)
{
int id = WorldMetaStorage.GetExecutorId<EcsJoinAttachExecutor<TSubject, TAttachComponent>>(_worldTypeID);
if (id >= _executors.Length)
Array.Resize(ref _executors, _executors.Length << 1);
if (_executors[id] == null)
_executors[id] = new EcsJoinAttachExecutor<TSubject, TAttachComponent>(GetSubject<TSubject>());
return (EcsJoinAttachExecutor<TSubject, TAttachComponent>)_executors[id];
}
public EcsJoinAttachResult<TSubject, TAttachComponent> JoinFor<TSubject, TAttachComponent>(EcsReadonlyGroup sourceGroup, out TSubject subject)
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
var executor = GetJoinAttachExecutor<TSubject, TAttachComponent>();
subject = executor.Subject;
return executor.ExecuteFor(sourceGroup);
}
public EcsJoinAttachResult<TSubject, TAttachComponent> JoinFor<TSubject, TAttachComponent>(EcsReadonlyGroup sourceGroup)
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
return GetJoinAttachExecutor<TSubject, TAttachComponent>().ExecuteFor(sourceGroup);
}
public EcsJoinAttachResult<TSubject, TAttachComponent> Join<TSubject, TAttachComponent>(out TSubject subject)
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
var executor = GetJoinAttachExecutor<TSubject, TAttachComponent>();
subject = executor.Subject;
return executor.Execute();
}
public EcsJoinAttachResult<TSubject, TAttachComponent> Join<TSubject, TAttachComponent>()
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
return GetJoinAttachExecutor<TSubject, TAttachComponent>().Execute();
}
#endregion
#endregion
#region IsMaskCompatible
public bool IsMaskCompatible(EcsMask mask, int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (mask.WorldType != Archetype)
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (mask._worldType != Archetype)
throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TTableArhetype)");
#endif
for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++)
for (int i = 0, iMax = mask._inc.Length; i < iMax; i++)
{
if (!pools[mask.Inc[i]].Has(entityID))
if (!_pools[mask._inc[i]].Has(entityID))
return false;
}
for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++)
for (int i = 0, iMax = mask._exc.Length; i < iMax; i++)
{
if (pools[mask.Exc[i]].Has(entityID))
if (_pools[mask._exc[i]].Has(entityID))
return false;
}
return true;
@ -305,7 +241,7 @@ namespace DCFApixels.DragonECS
_groups.RemoveAt(last);
}
}
foreach (var item in pools)
foreach (var item in _pools)
item.OnWorldResize(_gens.Length);
_listeners.InvokeOnWorldResize(_gens.Length);
@ -341,30 +277,30 @@ namespace DCFApixels.DragonECS
public void ReleaseDelEntityBuffer()
{
ReadOnlySpan<int> buffser = new ReadOnlySpan<int>(_delEntBuffer, 0, _delEntBufferCount);
foreach (var pool in pools)
foreach (var pool in _pools)
pool.OnReleaseDelEntityBuffer(buffser);
_listeners.InvokeOnReleaseDelEntityBuffer(buffser);
for (int i = 0; i < _delEntBufferCount; i++)
_entityDispenser.Release(_delEntBuffer[i]);
_delEntBufferCount = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short GetGen(int entityID) => _gens[entityID];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short GetComponentsCount(int entityID) => _componentCounts[entityID];
public void DeleteEmptyEntites()
{
foreach (var e in _allEntites)
{
if (_componentCounts[e] <= 0)
DelEntity(e);
if (_componentCounts[e] <= 0) DelEntity(e);
}
}
public void CopyEntity(int fromEntityID, int toEntityID)
{
foreach (var pool in pools)
foreach (var pool in _pools)
{
if(pool.Has(fromEntityID))
pool.Copy(fromEntityID, toEntityID);
if(pool.Has(fromEntityID)) pool.Copy(fromEntityID, toEntityID);
}
}
public int CloneEntity(int fromEntityID)
@ -376,7 +312,7 @@ namespace DCFApixels.DragonECS
public void CloneEntity(int fromEntityID, int toEntityID)
{
CopyEntity(fromEntityID, toEntityID);
foreach (var pool in pools)
foreach (var pool in _pools)
{
if (!pool.Has(fromEntityID)&& pool.Has(toEntityID))
pool.Del(toEntityID);
@ -384,19 +320,14 @@ namespace DCFApixels.DragonECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void IncrementEntityComponentCount(int entityID)
{
_componentCounts[entityID]++;
}
internal void IncrementEntityComponentCount(int entityID) => _componentCounts[entityID]++;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void DecrementEntityComponentCount(int entityID)
{
var count = --_componentCounts[entityID];
if(count == 0 && _allEntites.Has(entityID))
DelEntity(entityID);
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (count < 0) throw new EcsFrameworkException("нарушен баланс инкремента.декремента компонентов");
if(count == 0 && _allEntites.Has(entityID)) DelEntity(entityID);
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (count < 0) throw new EcsFrameworkException("нарушен баланс инкремента/декремента компонентов");
#endif
}
#endregion
@ -408,13 +339,12 @@ namespace DCFApixels.DragonECS
}
internal EcsGroup GetGroupFromPool()
{
if (_groupsPool.Count <= 0)
return new EcsGroup(this);
if (_groupsPool.Count <= 0) return new EcsGroup(this);
return _groupsPool.Pop();
}
internal void ReleaseGroup(EcsGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (group.World != this)
throw new ArgumentException("groupFilter.WorldIndex != this");
#endif
@ -431,10 +361,10 @@ namespace DCFApixels.DragonECS
if (itemsCount == 0)
return;
for (var i = 0; i < pools.Length; i++)
for (var i = 0; i < _pools.Length; i++)
{
if (pools[i].Has(entityID))
list.Add(pools[i].GetRaw(entityID));
if (_pools[i].Has(entityID))
list.Add(_pools[i].GetRaw(entityID));
if (list.Count >= itemsCount)
break;
}
@ -485,41 +415,25 @@ namespace DCFApixels.DragonECS
}
#region Utils
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 24)]
internal readonly struct PoolRunners
{
public readonly IEcsComponentAdd add;
public readonly IEcsComponentWrite write;
public readonly IEcsComponentDel del;
public PoolRunners(EcsPipeline pipeline)
{
add = pipeline.GetRunner<IEcsComponentAdd>();
write = pipeline.GetRunner<IEcsComponentWrite>();
del = pipeline.GetRunner<IEcsComponentDel>();
}
}
public static class WorldMetaStorage
{
private static List<Resizer> resizer = new List<Resizer>();
private static int tokenCount = 0;
private static int[] componentCounts = new int[0];
private static int[] queryCounts = new int[0];
private static List<Resizer> _resizer = new List<Resizer>();
private static int _tokenCount = 0;
private static int[] _componentCounts = new int[0];
private static int[] _subjectsCounts = new int[0];
private static Dictionary<Type, int> _worldIds = new Dictionary<Type, int>();
private static class WorldIndex<TWorldArchetype>
{
public static int id = GetWorldId(typeof(TWorldArchetype));
}
private static int GetToken()
{
tokenCount++;
Array.Resize(ref componentCounts, tokenCount);
Array.Resize(ref queryCounts, tokenCount);
foreach (var item in resizer)
item.Resize(tokenCount);
return tokenCount - 1;
_tokenCount++;
Array.Resize(ref _componentCounts, _tokenCount);
Array.Resize(ref _subjectsCounts, _tokenCount);
foreach (var item in _resizer)
item.Resize(_tokenCount);
return _tokenCount - 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetWorldId(Type archetype)
@ -557,17 +471,17 @@ namespace DCFApixels.DragonECS
public static int[] ids;
static Component()
{
ids = new int[tokenCount];
ids = new int[_tokenCount];
for (int i = 0; i < ids.Length; i++)
ids[i] = -1;
resizer.Add(new Resizer<T>());
_resizer.Add(new Resizer<T>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
id = componentCounts[token]++;
id = _componentCounts[token]++;
return id;
}
}
@ -576,17 +490,17 @@ namespace DCFApixels.DragonECS
public static int[] ids;
static Subject()
{
ids = new int[tokenCount];
ids = new int[_tokenCount];
for (int i = 0; i < ids.Length; i++)
ids[i] = -1;
resizer.Add(new Resizer<T>());
_resizer.Add(new Resizer<T>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
id = queryCounts[token]++;
id = _subjectsCounts[token]++;
return id;
}
}
@ -595,24 +509,24 @@ namespace DCFApixels.DragonECS
public static int[] ids;
static Executor()
{
ids = new int[tokenCount];
ids = new int[_tokenCount];
for (int i = 0; i < ids.Length; i++)
ids[i] = -1;
resizer.Add(new Resizer<T>());
_resizer.Add(new Resizer<T>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
id = queryCounts[token]++;
id = _subjectsCounts[token]++;
return id;
}
}
}
#endregion
#region Callbacks Interface //TODO
#region Callbacks Interface
public interface IEcsWorldEventListener
{
void OnWorldResize(int newSize);
@ -621,10 +535,7 @@ namespace DCFApixels.DragonECS
void OnNewEntity(int entityID);
void OnDelEntity(int entityID);
}
#endregion
#region Extensions
public static class WorldEventListExtensions
internal static class WorldEventListExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnWorldResize(this List<IEcsWorldEventListener> self, int newSize)

View File

@ -1,189 +0,0 @@
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsJoinAttachExecutor<TSubject, TAttachComponent> : EcsQueryExecutor
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
private readonly TSubject _subject;
internal readonly EcsGroup _filteredGroup;
private EcsWorld _targetWorld;
private EcsAttachPool<TAttachComponent> _targetPool;
private int _targetWorldCapacity = -1;
private int _targetPoolCapacity = -1;
private int[] _mapping;
private int[] _counts;
private EntityLinkedList _linkedBasket;
private bool _isInitTargetWorld = false;
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join");
private long _executeVersion;
#region Properties
public TSubject Subject => _subject;
internal long ExecuteVersion => _executeVersion;
#endregion
#region Constructors
public EcsJoinAttachExecutor(TSubject subject)
{
_subject = subject;
_filteredGroup = EcsGroup.New(subject.World);
_targetPool = subject.World.GetPool<TAttachComponent>();
}
#endregion
#region Methods
public EcsJoinAttachResult<TSubject, TAttachComponent> Execute() => ExecuteFor(_targetPool.Entities);
public EcsJoinAttachResult<TSubject, TAttachComponent> ExecuteFor(EcsReadonlyGroup sourceGroup)
{
_executeJoin.Begin();
var world = _subject.World;
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
#endif
if (!_isInitTargetWorld)
InitTargetWorlds(sourceGroup.World);
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
else
if (_targetWorld != sourceGroup.World) throw new System.ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
#endif
//Подготовка массивов
if (_targetWorldCapacity < _targetWorld.Capacity)
{
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
}
else
{
ArrayUtility.Fill(_counts, 0);
ArrayUtility.Fill(_mapping, 0);
}
if (_targetPoolCapacity < _targetPool.Capacity)
{
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket.Resize(_targetPoolCapacity);
}
_linkedBasket.Clear();
//Конец подготовки массивов
var iterator = new EcsSubjectIterator<TSubject>(_subject, sourceGroup);
foreach (var attachID in iterator)
{
entlong attachTarget = _targetPool.Read(attachID).Target;
if (!attachTarget.IsAlive)
{
//_targetPool.Del(attachID);
continue;
}
int attachTargetID = attachTarget.id;
//if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery
ref int nodeIndex = ref _mapping[attachTargetID];
if (nodeIndex <= 0)
nodeIndex = _linkedBasket.Add(attachID);
else
_linkedBasket.Insert(nodeIndex, attachID);
_counts[attachTargetID]++;
}
_executeVersion++;
_executeJoin.End();
return new EcsJoinAttachResult<TSubject, TAttachComponent>(_subject, this , _executeVersion);
}
private void InitTargetWorlds(EcsWorld targetWorld)
{
_targetWorld = targetWorld;
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
_isInitTargetWorld = true;
}
internal sealed override void Destroy()
{
_filteredGroup.Release();
_targetWorld = null;
_mapping = null;
_counts = null;
_linkedBasket = null;
}
#endregion
#region Internal result methods
internal bool Has(int attachedEnttiyID) => _filteredGroup.Has(attachedEnttiyID);
internal EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
internal int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0;
internal int GetNodesCount(int entityID) => _counts[entityID];
internal bool HasNode(int entityID, int attachedEntityID) => _filteredGroup.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID;
#endregion
}
#region JoinAttachExecuter Results
public readonly ref struct EcsJoinAttachResult<TSubject, TAttachComponent>
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
public readonly TSubject s;
private readonly EcsJoinAttachExecutor<TSubject, TAttachComponent> _executer;
private readonly long _verison;
public bool IsRelevant => _verison == _executer.ExecuteVersion;
public EcsJoinAttachResult(TSubject s, EcsJoinAttachExecutor<TSubject, TAttachComponent> executer, long version)
{
this.s = s;
_executer = executer;
_verison = version;
}
public bool Has(int attachedEnttiyID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.Has(attachedEnttiyID);
}
public EntityLinkedList.EnumerableSpan GetNodes(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.GetNodes(entityID);
}
public int GetNode(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.GetNode(entityID);
}
public int GetNodesCount(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.GetNodesCount(entityID);
}
public bool HasNode(int entityID, int attachedEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.HasNode(entityID, attachedEntityID);
}
}
#endregion
}

View File

@ -1,198 +0,0 @@
using System;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsJoinHierarchyExecutor<TSubject, TAttachComponent> : EcsQueryExecutor
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
private readonly TSubject _subject;
internal readonly EcsGroup _filteredGroup;
private EcsWorld _targetWorld;
private EcsAttachPool<TAttachComponent> _targetPool;
private int _targetWorldCapacity = -1;
private int _targetPoolCapacity = -1;
private int[] _mapping;
private int[] _counts;
private EntityLinkedList _linkedBasket;
private bool _isInitTargetWorld = false;
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where");
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join");
private long _executeVersion;
#region Properties
public TSubject Subject => _subject;
internal long ExecuteVersion => _executeVersion;
#endregion
#region Constructors
public EcsJoinHierarchyExecutor(TSubject subject)
{
_subject = subject;
_filteredGroup = EcsGroup.New(subject.World);
_targetPool = subject.World.GetPool<TAttachComponent>();
}
#endregion
#region Methods
public EcsJoinHierarchyResult<TSubject, TAttachComponent> Execute() => ExecuteFor(_targetPool.Entities);
public EcsJoinHierarchyResult<TSubject, TAttachComponent> ExecuteFor(EcsReadonlyGroup sourceGroup)
{
var world = _subject.World;
_executeJoin.Begin();
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (sourceGroup.IsNull) throw new ArgumentNullException();//TODO составить текст исключения.
#endif
if (!_isInitTargetWorld)
InitTargetWorlds(sourceGroup.World);
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
else
if (_targetWorld != sourceGroup.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
#endif
//Подготовка массивов
if (_targetWorldCapacity < _targetWorld.Capacity)
{
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
}
else
{
ArrayUtility.Fill(_counts, 0);
ArrayUtility.Fill(_mapping, 0);
}
if (_targetPoolCapacity < _targetPool.Capacity)
{
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket.Resize(_targetPoolCapacity);
}
_linkedBasket.Clear();
//Конец подготовки массивов
var iterator = new EcsSubjectIterator<TSubject>(_subject, sourceGroup);
foreach (var attachID in iterator)
{
entlong attachTarget = _targetPool.Read(attachID).Target;
if (!attachTarget.IsAlive)
{
//_targetPool.Del(attachID);
continue;
}
int attachTargetID = attachTarget.id;
//if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery
ref int nodeIndex = ref _mapping[attachTargetID];
if (nodeIndex <= 0)
nodeIndex = _linkedBasket.Add(attachID);
else
_linkedBasket.Insert(nodeIndex, attachID);
_counts[attachTargetID]++;
}
_executeVersion++;
_executeJoin.End();
return new EcsJoinHierarchyResult<TSubject, TAttachComponent>(_subject, this , _executeVersion);
}
private void InitTargetWorlds(EcsWorld targetWorld)
{
_targetWorld = targetWorld;
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
_isInitTargetWorld = true;
}
internal sealed override void Destroy()
{
_filteredGroup.Release();
_targetWorld = null;
_mapping = null;
_counts = null;
_linkedBasket = null;
}
#endregion
#region Internal result methods
internal bool Has(int attachedEnttiyID) => _filteredGroup.Has(attachedEnttiyID);
internal EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
internal int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0;
internal int GetNodesCount(int entityID) => _counts[entityID];
internal bool HasNode(int entityID, int attachedEntityID) => _filteredGroup.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID;
internal EntityLinkedList.EnumerableSpan GetSubNodes(int entityID) => throw new NotImplementedException();
internal int GetSubNode(int entityID) => throw new NotImplementedException();
internal bool GetSubNodesCount(int entityID, int attachedEntityID) => throw new NotImplementedException();
internal bool HasSubNode(int entityID, int attachedEntityID) => throw new NotImplementedException();
#endregion
}
#region JoinAttachExecuter Results
public readonly ref struct EcsJoinHierarchyResult<TSubject, TAttachComponent>
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
public readonly TSubject s;
private readonly EcsJoinHierarchyExecutor<TSubject, TAttachComponent> _executer;
private readonly long _verison;
public bool IsRelevant => _verison == _executer.ExecuteVersion;
public EcsJoinHierarchyResult(TSubject s, EcsJoinHierarchyExecutor<TSubject, TAttachComponent> executer, long version)
{
this.s = s;
_executer = executer;
_verison = version;
}
public bool Has(int attachedEnttiyID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.Has(attachedEnttiyID);
}
public EntityLinkedList.EnumerableSpan GetNodes(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.GetNodes(entityID);
}
public int GetNode(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.GetNode(entityID);
}
public int GetNodesCount(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.GetNodesCount(entityID);
}
public bool HasNode(int entityID, int attachedEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new InvalidOperationException();//TODO составить текст исключения.
#endif
return _executer.HasNode(entityID, attachedEntityID);
}
}
#endregion
}

View File

@ -2,6 +2,7 @@
{
public abstract class EcsQueryExecutor
{
internal abstract void Destroy();
internal void Destroy() => OnDestroy();
protected abstract void OnDestroy();
}
}

View File

@ -30,14 +30,14 @@ namespace DCFApixels.DragonECS
{
using (_executeWhere.Auto())
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
#endif
_subject.GetIteratorFor(sourceGroup).CopyTo(_filteredGroup);
return new EcsWhereResult<TSubject>(this, _filteredGroup.Readonly);
}
}
internal sealed override void Destroy()
protected sealed override void OnDestroy()
{
_filteredGroup.Release();
}
@ -62,7 +62,7 @@ namespace DCFApixels.DragonECS
}
public EcsGroup.Enumerator GetEnumerator()
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
#endif
return group.GetEnumerator();

View File

@ -1,224 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
using static EcsPoolThrowHalper;
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
/// <summary>Pool for IEcsAttachComponent components</summary>
public sealed class EcsAttachPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
where T : struct, IEcsAttachComponent
{
private EcsWorld _source;
private int _id;
private bool[] _entityFlags;// index = entityID / value = entityFlag;/ value = 0 = no entityID
private T[] _items; //sparse
private int _count;
private List<IEcsPoolEventListener> _listeners;
private EcsGroup _entities;
public EcsReadonlyGroup Entities
{
get
{
_entities.RemoveUnusedEntityIDs();
return _entities.Readonly;
}
}
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
private short _sanitizeTargetWorld = -1;
#endif
#region Properites
public int Count => _count;
public int Capacity => _items.Length;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
_listeners = new List<IEcsPoolEventListener>();
_entities = EcsGroup.New(world);
_entityFlags = new bool[world.Capacity];
_items = new T[world.Capacity];
_count = 0;
}
#endregion
#region Methods
public void Add(int entityID, entlong target)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_sanitizeTargetWorld > 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent<T>(entityID);
_sanitizeTargetWorld = target.world;
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
#endif
ref bool entityFlag = ref _entityFlags[entityID];
if (entityFlag == false)
{
entityFlag = true;
_count++;
_entities.Add(entityID);
foreach (var item in _listeners) item.OnAdd(entityID);
}
foreach (var item in _listeners) item.OnWrite(entityID);
_items[entityID].Target = target;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int entityID, entlong target)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent<T>(entityID);
_sanitizeTargetWorld = target.world;
#endif
_listeners.InvokeOnWrite(entityID);
_items[entityID].Target = target;
}
public void AddOrSet(int entityID, entlong target)
{
if (Has(entityID))
Set(entityID, target);
else
Add(entityID, target);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _items[entityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID)
{
return _entityFlags[entityID];
}
public void Del(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_entities.Remove(entityID);
_entityFlags[entityID] = false;
_count--;
_listeners.InvokeOnDel(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TryDel(int entityID)
{
if (Has(entityID)) Del(entityID);
}
public void Copy(int fromEntityID, int toEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
#endif
if (Has(toEntityID))
Set(toEntityID, Read(fromEntityID).Target);
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<T>(fromEntityID);
#endif
if (Has(toEntityID))
toWorld.GetPool<T>().Set(toEntityID, Read(fromEntityID).Target);
else
toWorld.GetPool<T>().Add(toEntityID, Read(fromEntityID).Target);
}
#endregion
#region WorldCallbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _entityFlags, newSize);
Array.Resize(ref _items, newSize);
}
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
foreach (var item in buffer)
TryDel(item);
}
#endregion
#region Other
ref T IEcsPool<T>.Add(int entityID)
{
if (!Has(entityID))
Add(entityID, entlong.NULL);
return ref _items[entityID];
}
ref T IEcsPool<T>.Write(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _items[entityID];
}
void IEcsPool.AddRaw(int entityID, object dataRaw) => ((IEcsPool<T>)this).Add(entityID) = (T)dataRaw;
object IEcsPool.GetRaw(int entityID) => Read(entityID);
void IEcsPool.SetRaw(int entityID, object dataRaw) => ((IEcsPool<T>)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<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
public interface IEcsAttachComponent
{
public entlong Target { get; set; }
}
public static class EcsAttachComponentPoolExt
{
public static EcsAttachPool<TAttachComponent> GetPool<TAttachComponent>(this EcsWorld self) where TAttachComponent : struct, IEcsAttachComponent
{
return self.GetPool<TAttachComponent, EcsAttachPool<TAttachComponent>>();
}
public static EcsAttachPool<TAttachComponent> Include<TAttachComponent>(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
{
return self.Include<TAttachComponent, EcsAttachPool<TAttachComponent>>();
}
public static EcsAttachPool<TAttachComponent> Exclude<TAttachComponent>(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
{
return self.Exclude<TAttachComponent, EcsAttachPool<TAttachComponent>>();
}
public static EcsAttachPool<TAttachComponent> Optional<TAttachComponent>(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
{
return self.Optional<TAttachComponent, EcsAttachPool<TAttachComponent>>();
}
}
}

View File

@ -1,140 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
/// <summary>Pool for IEcsNotNullComponent components</summary>
public sealed class EcsNotNullPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
where T : struct, IEcsNotNullComponent
{
private EcsWorld _source;
private int _id;
private T[] _items; //sparse
private int _count;
private IEcsComponentReset<T> _componentResetHandler;
private IEcsComponentCopy<T> _componentCopyHandler;
private List<IEcsPoolEventListener> _listeners;
#region Properites
public int Count => _count;
public int Capacity => _items.Length;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
_items = new T[world.Capacity];
_count = 0;
_listeners = new List<IEcsPoolEventListener>();
_componentResetHandler = EcsComponentResetHandler<T>.instance;
_componentCopyHandler = EcsComponentCopyHandler<T>.instance;
}
#endregion
#region Methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entityID)
{
_listeners.InvokeOnWrite(entityID);
return ref _items[entityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
return ref _items[entityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID)
{
return true;
}
public void Copy(int fromEntityID, int toEntityID)
{
_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<T>().Write(toEntityID));
}
#endregion
#region Callbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _items, newSize);
}
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
foreach (var entityID in buffer)
_componentResetHandler.Reset(ref _items[entityID]);
}
#endregion
#region Other
ref T IEcsPool<T>.Add(int entityID) => ref Write(entityID);
ref readonly T IEcsPool<T>.Read(int entityID) => ref Read(entityID);
ref T IEcsPool<T>.Write(int entityID) => ref Write(entityID);
void IEcsPool.Del(int entityID) { }
void IEcsPool.AddRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw;
object IEcsPool.GetRaw(int entityID) => Write(entityID);
void IEcsPool.SetRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw;
#endregion
#region 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<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
/// <summary>
/// Not null component. Is present on all entities, without explicit addition and cannot be deleted
/// </summary>
public interface IEcsNotNullComponent { }
public static class EcsNotNullPoolExt
{
public static EcsNotNullPool<TNotNullComponent> GetPool<TNotNullComponent>(this EcsWorld self) where TNotNullComponent : struct, IEcsNotNullComponent
{
return self.GetPool<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
}
public static EcsNotNullPool<TNotNullComponent> Include<TNotNullComponent>(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent
{
return self.Include<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
}
public static EcsNotNullPool<TNotNullComponent> Exclude<TNotNullComponent>(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent
{
return self.Exclude<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
}
public static EcsNotNullPool<TNotNullComponent> Optional<TNotNullComponent>(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent
{
return self.Optional<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
}
}
}

View File

@ -7,7 +7,7 @@ namespace DCFApixels.DragonECS
{
using static EcsPoolThrowHalper;
/// <summary>Pool for IEcsComponent components</summary>
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
where T : struct, IEcsComponent
{
private EcsWorld _source;
@ -59,7 +59,7 @@ namespace DCFApixels.DragonECS
// using (_addMark.Auto())
// {
ref int itemIndex = ref _mapping[entityID];
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (itemIndex > 0) ThrowAlreadyHasComponent<T>(entityID);
#endif
if (_recycledItemsCount > 0)
@ -82,7 +82,7 @@ namespace DCFApixels.DragonECS
public ref T Write(int entityID)
{
// using (_writeMark.Auto())
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_listeners.InvokeOnWrite(entityID);
@ -92,7 +92,7 @@ namespace DCFApixels.DragonECS
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _items[_mapping[entityID]];
@ -126,7 +126,7 @@ namespace DCFApixels.DragonECS
}
public void Del(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
ref int itemIndex = ref _mapping[entityID];
@ -145,14 +145,14 @@ namespace DCFApixels.DragonECS
}
public void Copy(int fromEntityID, int toEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
#endif
_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 (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
#endif
_componentCopyHandler.Copy(ref Write(fromEntityID), ref toWorld.GetPool<T>().TryAddOrWrite(toEntityID));

View File

@ -1,177 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
using static EcsPoolThrowHalper;
public sealed class EcsSinglePool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
where T : struct, IEcsSingleComponent
{
private EcsWorld _source;
private int _id;
private int[] _mapping;
private int _count;
private T _component;
private List<IEcsPoolEventListener> _listeners;
#region Properites
public ref T Instance
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _component;
}
public int Count => _count;
int IEcsPool.Capacity => -1;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
_mapping = new int[world.Capacity];
_count = 0;
_listeners = new List<IEcsPoolEventListener>();
}
#endregion
#region Methods
public ref T Add(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
#endif
_mapping[entityID] = ++_count;
this.IncrementEntityComponentCount(entityID);
_listeners.InvokeOnAddAndWrite(entityID);
return ref _component;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_listeners.InvokeOnWrite(entityID);
return ref _component;
}
public ref T TryAddOrWrite(int entityID)
{
if (!Has(entityID))
return ref Add(entityID);
return ref Write(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _component;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID)
{
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_mapping[entityID] = 0;
_count--;
this.DecrementEntityComponentCount(entityID);
_listeners.InvokeOnDel(entityID);
}
public void TryDel(int entityID)
{
if (Has(entityID)) Del(entityID);
}
public void Copy(int fromEntityID, int toEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
#endif
TryAddOrWrite(toEntityID);
}
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
#endif
toWorld.GetPool<T>().TryAddOrWrite(toEntityID);
}
#endregion
#region Callbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
}
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
foreach (var entityID in buffer)
TryDel(entityID);
}
#endregion
#region Other
void IEcsPool.AddRaw(int entityID, object dataRaw) => Instance = (T)dataRaw;
object IEcsPool.GetRaw(int entityID) => Instance;
void IEcsPool.SetRaw(int entityID, object dataRaw) => Instance = (T)dataRaw;
#endregion
#region 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<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
/// <summary>Singleton component</summary>
public interface IEcsSingleComponent { }
public static class EcsSinglePoolExt
{
public static EcsSinglePool<TSingleComponent> GetPool<TSingleComponent>(this EcsWorld self)
where TSingleComponent : struct, IEcsSingleComponent
{
return self.GetPool<TSingleComponent, EcsSinglePool<TSingleComponent>>();
}
public static EcsSinglePool<TSingleComponent> Include<TSingleComponent>(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
{
return self.Include<TSingleComponent, EcsSinglePool<TSingleComponent>>();
}
public static EcsSinglePool<TSingleComponent> Exclude<TSingleComponent>(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
{
return self.Exclude<TSingleComponent, EcsSinglePool<TSingleComponent>>();
}
public static EcsSinglePool<TSingleComponent> Optional<TSingleComponent>(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
{
return self.Optional<TSingleComponent, EcsSinglePool<TSingleComponent>>();
}
}
}

View File

@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
using static EcsPoolThrowHalper;
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
where T : struct, IEcsTagComponent
{
private EcsWorld _source;
@ -43,7 +43,7 @@ namespace DCFApixels.DragonECS
#region Method
public void Add(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
#endif
_count++;
@ -68,7 +68,7 @@ namespace DCFApixels.DragonECS
}
public void Del(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_mapping[entityID] = false;
@ -82,14 +82,14 @@ namespace DCFApixels.DragonECS
}
public void Copy(int fromEntityID, int toEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
#endif
TryAdd(toEntityID);
}
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
#endif
toWorld.GetPool<T>().TryAdd(toEntityID);
@ -138,14 +138,14 @@ namespace DCFApixels.DragonECS
}
ref readonly T IEcsPool<T>.Read(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _fakeComponent;
}
ref T IEcsPool<T>.Write(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _fakeComponent;
@ -153,14 +153,14 @@ namespace DCFApixels.DragonECS
void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID);
object IEcsPool.GetRaw(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return _fakeComponent;
}
void IEcsPool.SetRaw(int entityID, object dataRaw)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
}

View File

@ -1,7 +1,4 @@
using System;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
namespace DCFApixels.DragonECS.Utils
{
internal static class ArrayUtility
{
@ -15,34 +12,4 @@ namespace DCFApixels.DragonECS
array[i] = value;
}
}
internal static unsafe class UnmanagedArrayUtility
{
public static void* New<T>(int elementCount) where T : struct
{
return Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * elementCount).ToPointer();
}
public static void* NewAndInit<T>(int elementCount) where T : struct
{
int newSizeInBytes = Marshal.SizeOf(typeof(T)) * elementCount;
byte* newArrayPointer = (byte*)Marshal.AllocHGlobal(newSizeInBytes).ToPointer();
for (int i = 0; i < newSizeInBytes; i++)
*(newArrayPointer + i) = 0;
return newArrayPointer;
}
public static void Free(void* pointerToUnmanagedMemory)
{
Marshal.FreeHGlobal(new IntPtr(pointerToUnmanagedMemory));
}
public static void* Resize<T>(void* oldPointer, int newElementCount) where T : struct
{
return (Marshal.ReAllocHGlobal(new IntPtr(oldPointer),
new IntPtr(Marshal.SizeOf(typeof(T)) * newElementCount))).ToPointer();
}
}
}

View File

@ -5,7 +5,7 @@ namespace DCFApixels.DragonECS
{
public class EntityLinkedList
{
public const int Enter = 0;
private const int ENTER = 0;
private Node[] _nodes;
private int _count;
@ -35,7 +35,7 @@ namespace DCFApixels.DragonECS
//ArrayUtility.Fill(_nodes, Node.Empty);
for (int i = 0; i < _nodes.Length; i++)
_nodes[i].next = 0;
_lastNodeIndex = Enter;
_lastNodeIndex = ENTER;
_count = 0;
}
@ -82,7 +82,7 @@ namespace DCFApixels.DragonECS
{
_nodes = nodes;
_index = -1;
_next = Enter;
_next = ENTER;
}
public int Current => _nodes[_index].entityID;
public bool MoveNext()

View File

@ -1,7 +1,7 @@
using System.Collections.Concurrent;
using System.Threading;
namespace DCFApixels.DragonECS
namespace DCFApixels.DragonECS.Utils
{
internal sealed class IntDispenser
{

View File

@ -1,6 +1,6 @@
namespace DCFApixels.DragonECS
{
public static class Extensions
public static class IntExtensions
{
public static entlong ToEntityLong(this int self, EcsWorld world)
{

View File

@ -1,259 +0,0 @@
//SparseArray. Analogous to Dictionary<int, T>, but faster.
//Benchmark result of indexer.get speed test with 300 elements:
//[Dictinary: 5.786us] [SparseArray: 2.047us].
using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
public class SparseArray<TValue>
{
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<int>();
private Entry[] _entries = Array.Empty<Entry>();
private int[] _dense;
private int _count;
private int _freeList;
private int _freeCount;
private int _modBitMask;
#region Properties
public ref TValue this[int key]
{
get => ref _entries[FindEntry(key)].value;
//set => Insert(key, value);
}
public int Count => _count;
#endregion
#region Constructors
public SparseArray(int minCapacity = MIN_CAPACITY)
{
minCapacity = NormalizeCapacity(minCapacity);
_buckets = new int[minCapacity];
for (int i = 0; i < minCapacity; i++)
_buckets[i] = EMPTY;
_entries = new Entry[minCapacity];
_modBitMask = (minCapacity - 1) & 0x7FFFFFFF;
}
#endregion
#region Add
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(int key, TValue value)
{
#if DEBUG
if (Contains(key))
throw new ArgumentException("Contains(hashKey) is true");
#endif
Insert(key, value);
}
#endregion
#region Find/Insert/Remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindEntry(int key)
{
for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next)
if (_entries[i].hashKey == key) return i;
return -1;
}
private void Insert(int key, TValue value)
{
int targetBucket = key & _modBitMask;
for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
{
if (_entries[i].hashKey == key)
{
_entries[i].value = value;
return;
}
}
int index;
if (_freeCount > 0)
{
index = _freeList;
_freeList = _entries[index].next;
_freeCount--;
}
else
{
if (_count == _entries.Length)
{
Resize();
targetBucket = key & _modBitMask;
}
index = _count++;
}
_entries[index].next = _buckets[targetBucket];
_entries[index].hashKey = key;
_entries[index].value = value;
_buckets[targetBucket] = index;
}
public bool Remove(int key)
{
int bucket = key & _modBitMask;
int last = -1;
for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next)
{
if (_entries[i].hashKey == key)
{
if (last < 0)
{
_buckets[bucket] = _entries[i].next;
}
else
{
_entries[last].next = _entries[i].next;
}
_entries[i].next = _freeList;
_entries[i].hashKey = -1;
_entries[i].value = default;
_freeList = i;
_freeCount++;
return true;
}
}
return false;
}
#endregion
#region TryGetValue
public bool TryGetValue(int key, out TValue value)
{
int index = FindEntry(key);
if (index < 0)
{
value = default;
return false;
}
value = _entries[index].value;
return true;
}
#endregion
#region Contains
public bool Contains(int key)
{
return FindEntry(key) >= 0;
}
#endregion
#region Clear
public void Clear()
{
if (_count > 0)
{
for (int i = 0; i < _buckets.Length; i++)
{
_buckets[i] = -1;
}
Array.Clear(_entries, 0, _count);
_count = 0;
}
}
#endregion
#region Resize
private void Resize()
{
int newSize = _buckets.Length << 1;
_modBitMask = (newSize - 1) & 0x7FFFFFFF;
Contract.Assert(newSize >= _entries.Length);
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++)
newBuckets[i] = EMPTY;
Entry[] newEntries = new Entry[newSize];
Array.Copy(_entries, 0, newEntries, 0, _count);
for (int i = 0; i < _count; i++)
{
if (newEntries[i].hashKey >= 0)
{
int bucket = newEntries[i].hashKey % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
_buckets = newBuckets;
_entries = newEntries;
}
private int NormalizeCapacity(int capacity)
{
int result = MIN_CAPACITY;
while (result < capacity) result <<= 1;
return result;
}
#endregion
//#region Enumerator
// public Enumerator GetEnumerator() => new Enumerator(this);
// public struct Enumerator
// {
// private SparseArray<TValue> _source;
// private int index;
// private int curretnItmeIndex;
//
// public ref readonly TValue Current
// {
// get
// {
// return ref _source._entries[curretnItmeIndex].value;
// }
// }
//
// public Enumerator(SparseArray<TValue> 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<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
// index++;
// return true;
// }
// index++;
// }
//
// index = dictionary.count + 1;
// current = new KeyValuePair<TKey, TValue>();
// 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 TValue value;
}
#endregion
}
}

View File

@ -1,409 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace DCFApixels.DragonECS
{
public class SparseSet : IEnumerable<int>, ICollection<int>, IReadOnlyCollection<int>
{
public const int DEFAULT_DENSE_CAPACITY = 8;
public const int DEFAULT_SPARSE_CAPACITY = 16;
public const int MIN_CAPACITY = 4;
public const int MAX_CAPACITY = int.MaxValue;
private int[] _dense;
private int[] _sparse;
private int _count;
#region Properties
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
}
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dense.Length;
}
public int CapacitySparse
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _sparse.Length;
}
public int this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if DEBUG
ThrowHalper.CheckOutOfRange(this, index);
#endif
return _dense[index];
}
}
#endregion
#region Constructors
public SparseSet() : this(DEFAULT_DENSE_CAPACITY, DEFAULT_SPARSE_CAPACITY) { }
public SparseSet(int denseCapacity, int sparseCapacity)
{
denseCapacity = denseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(denseCapacity);
sparseCapacity = sparseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(sparseCapacity);
_dense = new int[denseCapacity];
_sparse = new int[sparseCapacity];
Reset();
}
#endregion
#region Add/AddRange
public void Add<T>(int value, ref T[] normalizedArray)
{
Add(value);
Normalize(ref normalizedArray);
}
public void Add(int value)
{
#if DEBUG
ThrowHalper.CheckValueIsPositive(value);
ThrowHalper.CheckValueNotContained(this, value);
#endif
if (_count >= _dense.Length)
Array.Resize(ref _dense, _dense.Length << 1);
if (value >= _sparse.Length)
{
int neadedSpace = _sparse.Length;
while (value >= neadedSpace)
neadedSpace <<= 1;
int i = _sparse.Length;
Array.Resize(ref _sparse, neadedSpace);
//loop unwinding
for (; i < neadedSpace;)
{
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
}
}
_dense[_count] = value;
_sparse[value] = _count++;
}
public bool TryAdd<T>(int value, ref T[] normalizedArray)
{
if (Contains(value)) return false;
Add(value);
Normalize(ref normalizedArray);
return true;
}
public bool TryAdd(int value)
{
if (Contains(value)) return false;
Add(value);
return true;
}
public void AddRange<T>(IEnumerable<int> range, ref T[] normalizedArray)
{
AddRange(range);
Normalize(ref normalizedArray);
}
public void AddRange(IEnumerable<int> range)
{
foreach (var item in range)
{
if (Contains(item)) continue;
Add(item);
}
}
#endregion
#region Contains
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int value)
{
return value >= 0 && value < CapacitySparse && _sparse[value] >= 0;
}
#endregion
#region Remove
public void Remove(int value)
{
#if DEBUG
ThrowHalper.CheckValueContained(this, value);
#endif
_dense[_sparse[value]] = _dense[--_count];
_sparse[_dense[_count]] = _sparse[value];
_sparse[value] = -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryRemove(int value)
{
if (!Contains(value)) return false;
Remove(value);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(int index)
{
#if DEBUG
ThrowHalper.CheckOutOfRange(this, index);
#endif
Remove(_dense[index]);
}
#endregion
#region Other
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Normalize<T>(ref T[] array)
{
if (array.Length < CapacityDense) Array.Resize(ref array, CapacityDense);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(int value)
{
if (value < 0 || !Contains(value)) return -1;
return _sparse[value];
}
public void Sort()
{
int increment = 0;
for (int i = 0; i < CapacitySparse; i++)
{
if (_sparse[i] < _count)
{
_sparse[i] = increment;
_dense[increment++] = i;
}
}
}
public void HardSort()
{
int inc = 0;
int inc2 = _count;
for (int i = 0; i < CapacitySparse; i++)
{
if (_sparse[i] >= 0)
{
_sparse[i] = inc;
_dense[inc++] = i;
}
else
{
_dense[inc2++] = i;
}
}
}
public void CopyTo(SparseSet other)
{
other._count = _count;
if (CapacitySparse != other.CapacitySparse)
Array.Resize(ref other._sparse, CapacitySparse);
if (CapacityDense != other.CapacityDense)
Array.Resize(ref other._dense, CapacityDense);
_sparse.CopyTo(other._sparse, 0);
_dense.CopyTo(other._dense, 0);
}
public void CopyTo(int[] array, int arrayIndex)
{
#if DEBUG
if (arrayIndex < 0) throw new ArgumentException("arrayIndex is less than 0");
if (arrayIndex + _count >= array.Length) throw new ArgumentException("The number of elements in the source List<T> is greater than the available space from arrayIndex to the end of the destination array.");
#endif
for (int i = 0; i < _count; i++, arrayIndex++)
{
array[arrayIndex] = this[i];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int NormalizeCapacity(int value)
{
return value + (MIN_CAPACITY - (value % MIN_CAPACITY));
}
#endregion
#region Clear/Reset
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() => _count = 0;
public void Reset()
{
Clear();
//loop unwinding
for (int i = 0; i < _sparse.Length;)
{
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
}
}
public void Reset(int newDenseCapacity, int newSparseCapacity)
{
newDenseCapacity = newDenseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(newDenseCapacity);
newSparseCapacity = newSparseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(newSparseCapacity);
if (CapacitySparse != newSparseCapacity)
Array.Resize(ref _sparse, newSparseCapacity);
if (CapacityDense != newDenseCapacity)
Array.Resize(ref _dense, newDenseCapacity);
Reset();
}
#endregion
#region Enumerator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RefEnumerator GetEnumerator() => new RefEnumerator(_dense, _count);
public ref struct RefEnumerator
{
private readonly int[] _dense;
private readonly int _count;
private int _index;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RefEnumerator(int[] values, int count)
{
_dense = values;
_count = count;
_index = -1;
}
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dense[_index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => ++_index < _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset() => _index = -1;
}
IEnumerator<int> IEnumerable<int>.GetEnumerator() => new Enumerator(_dense, _count);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dense, _count);
public struct Enumerator : IEnumerator<int> //to implement the IEnumerable interface and use the ref structure, 2 Enumerators were created.
{
private readonly int[] _dense;
private readonly int _count;
private int _index;
public Enumerator(int[] values, int count)
{
_dense = values;
_count = count;
_index = -1;
}
public int Current => _dense[_index];
object IEnumerator.Current => _dense[_index];
public void Dispose() { }
public bool MoveNext() => ++_index < _count;
public void Reset() => _index = -1;
}
#endregion
#region ICollection
bool ICollection<int>.IsReadOnly => false;
bool ICollection<int>.Remove(int value) => TryRemove(value);
#endregion
#region Debug
public string Log()
{
StringBuilder logbuild = new StringBuilder();
for (int i = 0; i < CapacityDense; i++)
{
logbuild.Append(_dense[i] + ", ");
}
logbuild.Append("\n\r");
for (int i = 0; i < CapacitySparse; i++)
{
logbuild.Append(_sparse[i] + ", ");
}
logbuild.Append("\n\r --------------------------");
logbuild.Append("\n\r");
for (int i = 0; i < CapacityDense; i++)
{
logbuild.Append((i < _count ? _dense[i].ToString() : "_") + ", ");
}
logbuild.Append("\n\r");
for (int i = 0; i < CapacitySparse; i++)
{
logbuild.Append((_sparse[i] >= 0 ? _sparse[i].ToString() : "_") + ", ");
}
logbuild.Append("\n\r Count: " + _count);
logbuild.Append("\n\r Capacity: " + CapacitySparse);
logbuild.Append("\n\r IsValide: " + IsValide_Debug());
logbuild.Append("\n\r");
return logbuild.ToString();
}
public bool IsValide_Debug()
{
bool isPass = true;
for (int index = 0; index < _count; index++)
{
int value = _dense[index];
isPass = isPass && _sparse[value] == index;
}
return isPass;
}
#if DEBUG
private static class ThrowHalper
{
public static void CheckCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentException("Capacity cannot be a negative number");
}
public static void CheckValueIsPositive(int value)
{
if (value < 0)
throw new ArgumentException("The SparseSet can only contain positive numbers");
}
public static void CheckValueContained(SparseSet source, int value)
{
if (!source.Contains(value))
throw new ArgumentException($"Value {value} is not contained");
}
public static void CheckValueNotContained(SparseSet source, int value)
{
if (source.Contains(value))
throw new ArgumentException($"Value {value} is already contained");
}
public static void CheckOutOfRange(SparseSet source, int index)
{
if (index < 0 || index >= source.Count)
throw new ArgumentOutOfRangeException($"Index {index} was out of range. Must be non-negative and less than the size of the collection.");
}
}
#endif
#endregion
}
}

View File

@ -1,4 +1,5 @@
using System;
#pragma warning disable IDE1006
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -6,9 +7,7 @@ using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
using static entlong.ThrowHalper;
// uniqueID - 32 bits
// gen - 16 bits
// world - 16 bits
// [ id 32 | gen 16 | world 16 ]
/// <summary>Strong identifier/Permanent entity identifier</summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)]
public readonly struct entlong : IEquatable<long>, IEquatable<entlong>
@ -34,12 +33,6 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this == NULL;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsNotNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this != NULL;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public int ID
@ -47,7 +40,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return id;
@ -59,7 +52,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return gen;
@ -70,7 +63,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return EcsWorld.Worlds[world];
@ -82,7 +75,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return world;
@ -135,7 +128,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString() => $"entity(id:{id} g:{gen} w:{world} {(IsAlive ? "alive" : "not alive")})";
public override string ToString() => $"entity(id:{id} g:{gen} w:{world} {(IsNull ? "null" : IsAlive ? "alive" : "not alive")})";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is entlong other && full == other.full;
#endregion
@ -159,9 +152,9 @@ namespace DCFApixels.DragonECS
public static void ThrowIsNotAlive(entlong entity)
{
if (entity.IsNull)
throw new EcsFrameworkException("The entity identifier is null.");
throw new EcsFrameworkException($"The {entity} is null.");
else
throw new EcsFrameworkException("The entity is not alive.");
throw new EcsFrameworkException($"The {entity} is not alive.");
}
}
#endregion