mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2026-04-22 01:45:55 +08:00
Merge branch 'dev'
This commit is contained in:
commit
d8c73e3c0d
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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> { }
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 += ">";
|
||||
|
||||
147
src/EcsGroup.cs
147
src/EcsGroup.cs
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
249
src/EcsWorld.cs
249
src/EcsWorld.cs
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
{
|
||||
public abstract class EcsQueryExecutor
|
||||
{
|
||||
internal abstract void Destroy();
|
||||
internal void Destroy() => OnDestroy();
|
||||
protected abstract void OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
|
||||
@ -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>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
namespace DCFApixels.DragonECS.Utils
|
||||
{
|
||||
internal sealed class IntDispenser
|
||||
{
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public static class Extensions
|
||||
public static class IntExtensions
|
||||
{
|
||||
public static entlong ToEntityLong(this int self, EcsWorld world)
|
||||
{
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user