global update

rework queries
update remove entities
add sanitize checks
fixes
etс
This commit is contained in:
Mikhail 2023-05-07 00:50:02 +08:00
parent ec4f8325b6
commit c0c089ec01
39 changed files with 2024 additions and 1469 deletions

View File

@ -2,28 +2,28 @@
namespace DCFApixels.DragonECS
{
public interface IEcsPreInitSystem : IEcsSystem
public interface IEcsPreInitProcess : IEcsSystem
{
public void PreInit(EcsPipeline pipeline);
}
public interface IEcsInitSystem : IEcsSystem
public interface IEcsInitProcess : IEcsSystem
{
public void Init(EcsPipeline pipeline);
}
public interface IEcsRunSystem : IEcsSystem
public interface IEcsRunProcess : IEcsSystem
{
public void Run(EcsPipeline pipeline);
}
public interface IEcsDestroySystem : IEcsSystem
public interface IEcsDestroyProcess : IEcsSystem
{
public void Destroy(EcsPipeline pipeline);
}
public interface IEcsBaseSystem : IEcsInitSystem, IEcsRunSystem, IEcsDestroySystem { }
public interface IEcsBaseSystem : IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess { }
namespace Internal
{
[DebugColor(DebugColor.Orange)]
public sealed class EcsPreInitRunner : EcsRunner<IEcsPreInitSystem>, IEcsPreInitSystem
public sealed class EcsPreInitProcessRunner : EcsRunner<IEcsPreInitProcess>, IEcsPreInitProcess
{
#if DEBUG && !DISABLE_DEBUG
private EcsProfilerMarker[] _markers;
@ -53,7 +53,7 @@ namespace DCFApixels.DragonECS
#endif
}
[DebugColor(DebugColor.Orange)]
public sealed class EcsInitRunner : EcsRunner<IEcsInitSystem>, IEcsInitSystem
public sealed class EcsInitProcessRunner : EcsRunner<IEcsInitProcess>, IEcsInitProcess
{
#if DEBUG && !DISABLE_DEBUG
private EcsProfilerMarker[] _markers;
@ -83,7 +83,7 @@ namespace DCFApixels.DragonECS
#endif
}
[DebugColor(DebugColor.Orange)]
public sealed class EcsRunRunner : EcsRunner<IEcsRunSystem>, IEcsRunSystem
public sealed class EcsRunProcessRunner : EcsRunner<IEcsRunProcess>, IEcsRunProcess
{
#if DEBUG && !DISABLE_DEBUG
private EcsProfilerMarker[] _markers;
@ -114,7 +114,7 @@ namespace DCFApixels.DragonECS
#endif
}
[DebugColor(DebugColor.Orange)]
public sealed class EcsDestroyRunner : EcsRunner<IEcsDestroySystem>, IEcsDestroySystem
public sealed class EcsDestroyProcessRunner : EcsRunner<IEcsDestroyProcess>, IEcsDestroyProcess
{
#if DEBUG && !DISABLE_DEBUG
private EcsProfilerMarker[] _markers;

View File

@ -1,12 +1,14 @@
using System.Runtime.CompilerServices;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
public struct Parent : IEcsAttachComponent
{
public EcsEntity entity;
public entlong entity;
public EcsEntity Target
[EditorBrowsable(EditorBrowsableState.Never)]
public entlong Target
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => entity;
@ -14,4 +16,36 @@ namespace DCFApixels.DragonECS
set => entity = value;
}
}
public static class ParentUtility
{
// public static int GetRootOrSelf(this HierarchySubject s, int entityID) => s.parents.GetRootOrSelf(entityID);
public static int GetRootOrSelf(this EcsAttachPool<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;
}
}
}

View File

@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS
{
public void Inject(T obj);
}
public interface IEcsPreInitInjectCallbacks : IEcsSystem
public interface IEcsPreInitInjectProcess : IEcsSystem
{
public void OnPreInitInjectionBefore();
public void OnPreInitInjectionAfter();
@ -53,7 +53,7 @@ namespace DCFApixels.DragonECS
namespace Internal
{
[DebugHide, DebugColor(DebugColor.Gray)]
public sealed class PreInjectRunner : EcsRunner<IEcsPreInject>, IEcsPreInject
public sealed class EcsPreInjectRunner : EcsRunner<IEcsPreInject>, IEcsPreInject
{
void IEcsPreInject.PreInject(object obj)
{
@ -61,7 +61,7 @@ namespace DCFApixels.DragonECS
}
}
[DebugHide, DebugColor(DebugColor.Gray)]
public sealed class InjectRunner<T> : EcsRunner<IEcsInject<T>>, IEcsInject<T>
public sealed class EcsInjectRunner<T> : EcsRunner<IEcsInject<T>>, IEcsInject<T>
{
private IEcsPreInject _preInjectchache;
void IEcsInject<T>.Inject(T obj)
@ -75,7 +75,7 @@ namespace DCFApixels.DragonECS
}
}
[DebugHide, DebugColor(DebugColor.Gray)]
public sealed class InjectCallbacksRunner : EcsRunner<IEcsPreInitInjectCallbacks>, IEcsPreInitInjectCallbacks
public sealed class EcsPreInitInjectProcessRunner : EcsRunner<IEcsPreInitInjectProcess>, IEcsPreInitInjectProcess
{
public void OnPreInitInjectionAfter()
{
@ -91,7 +91,7 @@ namespace DCFApixels.DragonECS
public class InjectSystemBase { }
[DebugHide, DebugColor(DebugColor.Gray)]
public class InjectSystem<T> : InjectSystemBase, IEcsPreInitSystem, IEcsInject<PreInitInjectController>, IEcsPreInitInjectCallbacks
public class InjectSystem<T> : InjectSystemBase, IEcsPreInitProcess, IEcsInject<PreInitInjectController>, IEcsPreInitInjectProcess
{
private T _injectedData;
@ -105,12 +105,14 @@ namespace DCFApixels.DragonECS
public void PreInit(EcsPipeline pipeline)
{
if (_injectedData == null)
return;
if (_injectController == null)
{
_injectController = new PreInitInjectController(pipeline);
var injectMapRunner = pipeline.GetRunner<IEcsInject<PreInitInjectController>>();
pipeline.GetRunner<IEcsPreInitInjectCallbacks>().OnPreInitInjectionBefore();
pipeline.GetRunner<IEcsPreInitInjectProcess>().OnPreInitInjectionBefore();
injectMapRunner.Inject(_injectController);
}
@ -120,7 +122,7 @@ namespace DCFApixels.DragonECS
if (_injectController.OnInject())
{
_injectController.Destroy();
var injectCallbacksRunner = pipeline.GetRunner<IEcsPreInitInjectCallbacks>();
var injectCallbacksRunner = pipeline.GetRunner<IEcsPreInitInjectProcess>();
injectCallbacksRunner.OnPreInitInjectionAfter();
EcsRunner.Destroy(injectCallbacksRunner);
}

View File

@ -1,29 +0,0 @@
namespace DCFApixels.DragonECS
{
public sealed class EmptyQuery : EcsQueryBase
{
private long _whereVersion;
public override long WhereVersion => _whereVersion;
public EmptyQuery(Builder b) { }
public sealed override WhereResult Where()
{
groupFilter = source.Entities.GetGroupInternal();
return new WhereResult(this, ++_whereVersion);
}
protected sealed override void OnBuild(Builder b) { }
}
public static partial class EcsWorldExtensions
{
public static WhereResult WhereAll(this EcsWorld self) => self.Select<EmptyQuery>().Where();
}
public sealed class HierarchyQuery : EcsJoinAttachQuery<Parent>
{
public HierarchyQuery(Builder b) { }
}
}

11
src/Builtin/Subjects.cs Normal file
View File

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

View File

@ -2,7 +2,7 @@
namespace DCFApixels.DragonECS
{
[DebugColor(DebugColor.Black)]
[DebugHide, DebugColor(DebugColor.Black)]
public class SystemsBlockMarkerSystem : IEcsSystem
{
public readonly string name;
@ -13,7 +13,7 @@ namespace DCFApixels.DragonECS
}
}
[DebugHide, DebugColor(DebugColor.Grey)]
public class DeleteEmptyEntitesSsytem : IEcsRunSystem, IEcsPreInject
public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsPreInject
{
private List<EcsWorld> _worlds = new List<EcsWorld>();
public void PreInject(object obj)
@ -30,42 +30,45 @@ namespace DCFApixels.DragonECS
}
[DebugHide, DebugColor(DebugColor.Grey)]
public class DeleteOneFrameComponentSystem<TWorld, TComponent> : IEcsRunSystem, IEcsInject<TWorld>
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 Query : EcsQuery
private sealed class Subject : EcsSubject
{
public EcsPool<TComponent> pool;
public Query(Builder b)
public Subject(Builder b)
{
pool = b.Include<TComponent>();
}
}
public void Run(EcsPipeline pipeline)
{
foreach (var e in _world.Where(out Query q))
q.pool.Del(e);
foreach (var e in _world.Where(out Subject s))
s.pool.Del(e);
}
}
public static class DeleteOneFrameComponentSystemExt
{
private const string AUTO_DEL_LAYER = nameof(AUTO_DEL_LAYER);
public static EcsPipeline.Builder AutoDel<TWorld, TComponent>(this EcsPipeline.Builder b)
where TWorld : EcsWorld<TWorld>
where TComponent : struct, IEcsComponent
{
b.Add(new DeleteOneFrameComponentSystem<TWorld, TComponent>());
b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_LAYER);
b.AddUnique(new DeleteOneFrameComponentSystem<TWorld, TComponent>(), AUTO_DEL_LAYER);
return b;
}
/// <summary> for EcsDefaultWorld </summary>
public static EcsPipeline.Builder AutoDel<TComponent>(this EcsPipeline.Builder b)
where TComponent : struct, IEcsComponent
{
b.Add(new DeleteOneFrameComponentSystem<EcsDefaultWorld, TComponent>());
b.Layers.Insert(EcsConsts.POST_END_LAYER, AUTO_DEL_LAYER);
b.AddUnique(new DeleteOneFrameComponentSystem<EcsDefaultWorld, TComponent>(), AUTO_DEL_LAYER);
return b;
}
}

View File

@ -46,7 +46,7 @@ namespace DCFApixels.DragonECS
public interface IEcsEntityCreate : IEcsSystem
{
public void OnEntityCreate(EcsEntity entity);
public void OnEntityCreate(int entityID);
}
public interface IEcsEntityDestroy : IEcsSystem
{
@ -59,9 +59,9 @@ namespace DCFApixels.DragonECS
[DebugColor(DebugColor.Orange)]
public sealed class EcsEntityCreateRunner : EcsRunner<IEcsEntityCreate>, IEcsEntityCreate
{
public void OnEntityCreate(EcsEntity entity)
public void OnEntityCreate(int entityID)
{
foreach (var item in targets) item.OnEntityCreate(entity);
foreach (var item in targets) item.OnEntityCreate(entityID);
}
}
[DebugColor(DebugColor.Orange)]

View File

@ -1,17 +1,15 @@
namespace DCFApixels.DragonECS
{
//TODO использовать этот мир для холостых вызовов. чтоб все нулевые ентити стучались в него, так можно попробовать сократить число проверок
//internal sealed class EcsDeathWrold : EcsWorld<EcsDefaultWorld>
//{
// public EcsDeathWrold(EcsPipeline pipeline) : base(pipeline) { }
//}
public sealed class EcsDefaultWorld : EcsWorld<EcsDefaultWorld>
{
public EcsDefaultWorld() : base(null) { }
public EcsDefaultWorld(EcsPipeline pipeline) : base(pipeline) { }
}
public sealed class EcsPhysicsEventsWorld : EcsWorld<EcsPhysicsEventsWorld>
{
public EcsPhysicsEventsWorld() : base(null) { }
public EcsPhysicsEventsWorld(EcsPipeline pipeline) : base(pipeline) { }
}
public sealed class EcsEventWorld : EcsWorld<EcsEventWorld>
{
public EcsEventWorld() : base(null) { }

View File

@ -2,16 +2,18 @@
{
public class EcsConsts
{
public const string EXCEPTION_MESSAGE_PREFIX = "[DragonECS] ";
public const string FRAMEWORK_NAME = "DragonECS";
public const string EXCEPTION_MESSAGE_PREFIX = "["+ FRAMEWORK_NAME + "] ";
public const string DEBUG_PREFIX = "[DEBUG] ";
public const string DEBUG_WARNING_TAG = "WARNING";
public const string DEBUG_ERROR_TAG = "ERROR";
public const string PRE_BEGIN_SYSTEMS_BLOCK = nameof(PRE_BEGIN_SYSTEMS_BLOCK);
public const string BEGIN_SYSTEMS_BLOCK = nameof(BEGIN_SYSTEMS_BLOCK);
public const string BASIC_SYSTEMS_BLOCK = nameof(BASIC_SYSTEMS_BLOCK);
public const string END_SYSTEMS_BLOCK = nameof(END_SYSTEMS_BLOCK);
public const string POST_END_SYSTEMS_BLOCK = nameof(POST_END_SYSTEMS_BLOCK);
public const string PRE_BEGIN_LAYER = nameof(PRE_BEGIN_LAYER);
public const string BEGIN_LAYER = nameof(BEGIN_LAYER);
public const string BASIC_LAYER = nameof(BASIC_LAYER);
public const string END_LAYER = nameof(END_LAYER);
public const string POST_END_LAYER = nameof(POST_END_LAYER);
}
}

View File

@ -0,0 +1,14 @@
using System;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
public sealed class DebugDescriptionAttribute : Attribute
{
public readonly string description;
public DebugDescriptionAttribute(string description)
{
this.description = description;
}
}
}

View File

@ -2,6 +2,6 @@
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
public sealed class DebugHideAttribute : Attribute { }
}

View File

@ -2,7 +2,7 @@
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
public sealed class DebugNameAttribute : Attribute
{
public readonly string name;

View File

@ -1,16 +0,0 @@
using System;
namespace DCFApixels.DragonECS
{
public class EcsComponentMask
{
internal Type WorldArchetype;
internal int[] Inc;
internal int[] Exc;
public override string ToString()
{
return $"Inc({string.Join(", ", Inc)}) Exc({string.Join(", ", Exc)})";
}
}
}

View File

@ -6,6 +6,10 @@ using delayedOp = System.Int32;
namespace DCFApixels.DragonECS
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
using static EcsGroup.ThrowHalper;
#endif
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
public readonly ref struct EcsReadonlyGroup
{
@ -17,6 +21,7 @@ namespace DCFApixels.DragonECS
#endregion
#region Properties
public bool IsNull => _source == null;
public EcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -42,15 +47,24 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.IsReleazed;
}
public int this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source[index];
}
#endregion
#region Methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID) => _source.Has(entityID);
public bool Has(int entityID) => _source.Has(entityID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup Clone() => _source.Clone();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int First() => _source.First();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Last() => _source.Last();
#endregion
#region Object
@ -98,7 +112,7 @@ namespace DCFApixels.DragonECS
private int _lockCount;
private bool _isReleazed = true;
private bool _isReleazed = true;
#region Properties
public EcsWorld World => _source;
@ -127,6 +141,17 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _isReleazed;
}
public int this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (index < 0 || index >= Count) ThrowArgumentOutOfRange();
#endif
return _dense[index];
}
}
#endregion
#region Constrcutors/Finalizer
@ -196,6 +221,9 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AggressiveAdd(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (Has(entityID)) ThrowAlreadyContains(entityID);
#endif
if (++_count >= _dense.Length)
Array.Resize(ref _dense, _dense.Length << 1);
_dense[_count] = entityID;
@ -221,6 +249,9 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AggressiveRemove(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowDoesNotContain(entityID);
#endif
_dense[_sparse[entityID]] = _dense[_count];
_sparse[_dense[_count--]] = _sparse[entityID];
_sparse[entityID] = 0;
@ -247,18 +278,7 @@ namespace DCFApixels.DragonECS
#endregion
#region Sort/Clear
public void Sort()
{
int increment = 1;
for (int i = 0; i < _dense.Length; i++)
{
if (_sparse[i] > 0)
{
_sparse[i] = increment;
_dense[increment++] = i;
}
}
}
//public void Sort() { } //TODO прошлай реализация сортировки не удачная, так как в dense могут храниться занчения больше чем dense.Length
public void Clear()
{
_count = 0;
@ -398,7 +418,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Unlock()
{
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_lockCount <= 0)
{
throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}.");
@ -481,6 +501,14 @@ namespace DCFApixels.DragonECS
}
#endregion
#region OtherMethods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int First() => this[0];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Last() => this[_count - 1];
#endregion
#region operators
private static bool StaticEquals(EcsGroup a, EcsReadonlyGroup b) => StaticEquals(a, b.GetGroupInternal());
private static bool StaticEquals(EcsGroup a, EcsGroup b)
@ -514,6 +542,20 @@ namespace DCFApixels.DragonECS
_source.ReleaseGroup(this);
}
#endregion
#region ThrowHalper
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
internal static class ThrowHalper
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowAlreadyContains(int entityID) => throw new EcsFrameworkException($"This group already contains entity {entityID}.");
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowArgumentOutOfRange() => throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count.");
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowDoesNotContain(int entityID) => throw new EcsFrameworkException($"This group does not contain entity {entityID}.");
}
#endif
#endregion
}
public static class EcsGroupExtensions

View File

@ -1,329 +0,0 @@
using System;
using System.Xml.Schema;
using Unity.Profiling;
using UnityEditor.Search;
using UnityEngine;
namespace DCFApixels.DragonECS
{
public abstract class EcsJoinAttachQueryBase : EcsQueryBase
{
public abstract void Join(WhereResult targetWorldWhereQuery);
}
public abstract class EcsJoinAttachQuery<TAttachComponent> : EcsJoinAttachQueryBase
where TAttachComponent : struct, IEcsAttachComponent
{
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 long _executeWhereVersion = 0;
private long _executeJoinVersion = 0;
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where");
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join");
#region Properties
public EcsWorld AttachWorld => _targetWorld;
public EcsAttachPool<TAttachComponent> Attach => _targetPool;
public sealed override long WhereVersion => _executeWhereVersion;
public long JoinVersion => _executeJoinVersion;
#endregion
protected sealed override void OnBuild(Builder b)
{
_targetPool = b.Include<TAttachComponent>();
}
public sealed override WhereResult Where()
{
using (_executeWhere.Auto())
{
_executeWhereVersion++;
ExecuteWhere(_targetPool.Entities, groupFilter);
return new WhereResult(this, WhereVersion);
}
}
public sealed override void Join(WhereResult targetWorldWhereQuery)
{
_executeJoin.Begin();
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (targetWorldWhereQuery.IsNull)
throw new ArgumentNullException();//TODO составить текст исключения.
#endif
if (!_isInitTargetWorld)
InitTargetWorlds(targetWorldWhereQuery.World);
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
else
if (_targetWorld != targetWorldWhereQuery.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
#endif
//Подготовка массивов
if (_targetWorldCapacity < _targetWorld.Capacity)
{
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
}
else
{
ArrayUtility.Fill(_counts, 0);
ArrayUtility.Fill(_mapping, 0);
}
if (_targetPoolCapacity < _targetPool.Capacity)
{
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket.Resize(_targetPoolCapacity);
}
_linkedBasket.Clear();
//Конец подготовки массивов
Where();
foreach (var attachID in groupFilter)
{
EcsEntity attachTarget = _targetPool.Read(attachID).Target;
if (!attachTarget.IsAlive)
{
//_targetPool.Del(attachID);
continue;
}
int attachTargetID = attachTarget.id;
ref int nodeIndex = ref _mapping[attachTargetID];
if (nodeIndex <= 0)
nodeIndex = _linkedBasket.Add(attachID);
else
_linkedBasket.Insert(nodeIndex, attachID);
_counts[attachTargetID]++;
}
_executeJoinVersion++;
_executeJoin.End();
}
private void InitTargetWorlds(EcsWorld targetWorld)
{
_targetWorld = targetWorld;
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
_isInitTargetWorld = true;
}
public bool Has(int attachedEnttiyID) => groupFilter.Has(attachedEnttiyID);
public EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
public int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0;
public int GetNodesCount(int entityID) => _counts[entityID];
public bool HasNode(int entityID, int attachedEntityID) => groupFilter.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID;
}
/*
public abstract class EcsJoinRelationQuery<TRelationComponent> : EcsQueryBase
where TRelationComponent : struct, IEcsRelationComponent
{
private EcsWorld _firstWorld;
private EcsWorld _secondWorld;
private EcsRelationPool<TRelationComponent> _targetPool;
internal int _targetPoolCapacity = -1;
private bool _isInitTargetWorlds = false;
private bool _isJoinExecuted = false;
private long _executeWhereVersion = 0;
private long _executeJoinVersion = 0;
public readonly Orientation ToSecond = new Orientation();
public readonly Orientation ToFirst = new Orientation();
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinRelationQuery.Where");
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinRelationQuery.Join");
#region Properties
public EcsWorld RelationFirstWorld => _firstWorld;
public EcsWorld RelationSecondWorld => _secondWorld;
public EcsRelationPool<TRelationComponent> Relation => _targetPool;
public bool IsMonoWorldRelation => _firstWorld == _secondWorld;
public sealed override long WhereVersion => _executeWhereVersion;
public long JoinVersion => _executeJoinVersion;
#endregion
protected sealed override void OnBuild(Builder b)
{
_targetPool = b.Include<TRelationComponent>();
}
public sealed override WhereResult Where()
{
using (_executeWhere.Auto())
{
_executeWhereVersion++;
ExecuteWhere(_targetPool.Entities, groupFilter);
return new WhereResult(this, WhereVersion);
}
}
//TODO
// реализовать возможность получить список всех связей между двумя сущьностями одной напрваленности, и сделать метод для получения одной такой связи
//
public void Join(WhereResult firstWorldWhereQuery, WhereResult secondWorldWhereQuery)
{
_executeJoin.Begin();
_isJoinExecuted = false;
if (_isInitTargetWorlds == false)
{
InitTargetWorlds();
if (_isInitTargetWorlds == false)
return;
};
// //Подготовка массивов
// if (_targetWorldCapacity < _targetWorld.Capacity)
// {
// _targetWorldCapacity = _targetWorld.Capacity;
// _mapping = new int[_targetWorldCapacity];
// _counts = new int[_targetWorldCapacity];
// }
// else
// {
// ArrayUtility.Fill(_counts, 0);
// ArrayUtility.Fill(_mapping, 0);
// }
// if (_targetPoolCapacity < _targetPool.Capacity)
// {
// _targetPoolCapacity = _targetPool.Capacity;
// _linkedBasket.Resize(_targetPoolCapacity);
// }
// _linkedBasket.Clear();
// //Конец подготовки массивов
Where();
foreach (var attachID in groupFilter)
{
EcsEntity attachTarget = _targetPool.Read(attachID).First;
if (!attachTarget.IsAlive)
{
//_targetPool.Del(attachID);
continue;
}
int attachTargetID = attachTarget.id;
ref int nodeIndex = ref _mapping[attachTargetID];
if (nodeIndex <= 0)
nodeIndex = _linkedBasket.Add(attachID);
else
_linkedBasket.Insert(nodeIndex, attachID);
_counts[attachTargetID]++;
}
_isJoinExecuted = true;
_executeJoinVersion++;
_executeJoin.End();
_executeJoinVersion++;
_isJoinExecuted = true;
_executeJoin.End();
}
private void InitTargetWorlds()
{
foreach (var e in groupFilter)
{
ref readonly var rel = ref _targetPool.Read(e);
if (rel.First.IsNotNull)
_firstWorld = EcsWorld.Worlds[rel.First.world];
if (rel.Second.IsNotNull)
_secondWorld = EcsWorld.Worlds[rel.Second.world];
if (_firstWorld != null && _secondWorld != null)
{
_isInitTargetWorlds = true;
break;
}
}
if (_isInitTargetWorlds)
{
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
}
}
public bool Has(int relationEntityID) => groupFilter.Has(relationEntityID);
public class Orientation
{
internal int targetWorldCapacity = -1;
internal int[] mapping;
internal int[] counts;
internal EntityLinkedList linkedBasket;
public bool HasRelation(int fromEntityID, int toEntityID)
{
throw new NotImplementedException();
}
public bool HasNode(int fromEntityID, int toEntityID)
{
throw new NotImplementedException();
}
public EntityLinkedList.EnumerableSpan GetRelations(int fromEntityID) => linkedBasket.Span(mapping[fromEntityID], counts[fromEntityID]);
public int GetRelation(int fromEntityID) => counts[fromEntityID] > 0 ? linkedBasket.Get(mapping[fromEntityID]) : 0;
public int GetRelationsCount(int fromEntityID) => counts[fromEntityID];
}
}*/
#region Extensions
public static class EcsJoinQueryBaseExtensions
{
public static void Join(this EcsJoinAttachQueryBase self, EcsWorld targetWorld)
{
self.Join(targetWorld.Where<EmptyQuery>());
}
public static TQuery Join<TQuery>(this EcsWorld self, EcsWorld targetWorld, out TQuery query) where TQuery : EcsJoinAttachQueryBase
{
return self.Join(targetWorld.WhereAll(), out query);
}
public static TQuery Join<TQuery>(this EcsWorld self, EcsWorld targetWorld) where TQuery : EcsJoinAttachQueryBase
{
return self.Join<TQuery>(targetWorld.WhereAll());
}
/* public static class EcsJoinRelationQueryExtensions
{
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, EcsWorld firstWorld, EcsWorld secondWorld)
where TRelationComponent : struct, IEcsRelationComponent
{
self.Join(firstWorld.Where<EmptyQuery>(), secondWorld.Where<EmptyQuery>());
}
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, EcsWorld firstWorld, WhereResult secondWorldWhereQuery)
where TRelationComponent : struct, IEcsRelationComponent
{
self.Join(firstWorld.Where<EmptyQuery>(), secondWorldWhereQuery);
}
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, WhereResult firstWorldWhereQuery, EcsWorld secondWorld)
where TRelationComponent : struct, IEcsRelationComponent
{
self.Join(firstWorldWhereQuery, secondWorld.Where<EmptyQuery>());
}
}*/
}
#endregion
}

View File

@ -1,7 +1,9 @@
using DCFApixels.DragonECS.RunnersCore;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
@ -25,7 +27,7 @@ namespace DCFApixels.DragonECS
private IEcsSystem[] _allSystems;
private Dictionary<Type, IEcsRunner> _runners;
private IEcsRunSystem _runRunnerCache;
private IEcsRunProcess _runRunnerCache;
private ReadOnlyCollection<IEcsSystem> _allSystemsSealed;
private ReadOnlyDictionary<Type, IEcsRunner> _allRunnersSealed;
@ -89,20 +91,20 @@ namespace DCFApixels.DragonECS
var ecsPipelineInjectRunner = GetRunner<IEcsInject<EcsPipeline>>();
ecsPipelineInjectRunner.Inject(this);
EcsRunner.Destroy(ecsPipelineInjectRunner);
var preInitRunner = GetRunner<IEcsPreInitSystem>();
var preInitRunner = GetRunner<IEcsPreInitProcess>();
preInitRunner.PreInit(this);
EcsRunner.Destroy(preInitRunner);
var initRunner = GetRunner<IEcsInitSystem>();
var initRunner = GetRunner<IEcsInitProcess>();
initRunner.Init(this);
EcsRunner.Destroy(initRunner);
_runRunnerCache = GetRunner<IEcsRunSystem>();
_runRunnerCache = GetRunner<IEcsRunProcess>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Run()
{
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
CheckBeforeInitForMethod(nameof(Run));
CheckAfterDestroyForMethod(nameof(Run));
#endif
@ -113,7 +115,7 @@ namespace DCFApixels.DragonECS
if (_isEmptyDummy)
return;
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
CheckBeforeInitForMethod(nameof(Run));
#endif
if (_isDestoryed == true)
@ -122,12 +124,12 @@ namespace DCFApixels.DragonECS
return;
}
_isDestoryed = true;
GetRunner<IEcsDestroySystem>().Destroy(this);
GetRunner<IEcsDestroyProcess>().Destroy(this);
}
#endregion
#region StateChecks
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
private void CheckBeforeInitForMethod(string methodName)
{
if (!_isInit)
@ -155,48 +157,51 @@ namespace DCFApixels.DragonECS
{
private const int KEYS_CAPACITY = 4;
private HashSet<Type> _uniqueTypes;
private readonly List<object> _blockExecutionOrder;
private readonly Dictionary<object, List<IEcsSystem>> _systems;
private readonly string _basicBlocKey;
private bool _isBasicBlockDeclared;
private bool _isOnlyBasicBlock;
private readonly Dictionary<string, List<IEcsSystem>> _systems;
private readonly string _basicLayer;
public readonly LayerList Layers;
public Builder()
{
_basicBlocKey = EcsConsts.BASIC_SYSTEMS_BLOCK;
_basicLayer = EcsConsts.BASIC_LAYER;
Layers = new LayerList(this, _basicLayer);
Layers.Insert(EcsConsts.BASIC_LAYER, EcsConsts.PRE_BEGIN_LAYER, EcsConsts.BEGIN_LAYER);
Layers.InsertAfter(EcsConsts.BASIC_LAYER, EcsConsts.END_LAYER, EcsConsts.POST_END_LAYER);
_uniqueTypes = new HashSet<Type>();
_blockExecutionOrder = new List<object>(KEYS_CAPACITY);
_systems = new Dictionary<object, List<IEcsSystem>>(KEYS_CAPACITY);
_isBasicBlockDeclared = false;
_isOnlyBasicBlock = true;
SystemsBlock(EcsConsts.PRE_BEGIN_SYSTEMS_BLOCK);
SystemsBlock(EcsConsts.BEGIN_SYSTEMS_BLOCK);
_systems = new Dictionary<string, List<IEcsSystem>>(KEYS_CAPACITY);
}
public Builder Add(IEcsSystem system, object blockKey = null)
public Builder Add(IEcsSystem system, string layerName = null)
{
AddInternal(system, blockKey, false);
AddInternal(system, layerName, false);
return this;
}
public Builder AddUnique(IEcsSystem system, object blockKey = null)
public Builder AddUnique(IEcsSystem system, string layerName = null)
{
AddInternal(system, blockKey, true);
AddInternal(system, layerName, true);
return this;
}
private void AddInternal(IEcsSystem system, object blockKey, bool isUnique)
private void AddInternal(IEcsSystem system, string layerName, bool isUnique)
{
if (blockKey == null) blockKey = _basicBlocKey;
if (layerName == null) layerName = _basicLayer;
List<IEcsSystem> list;
if (!_systems.TryGetValue(blockKey, out list))
if (!_systems.TryGetValue(layerName, out list))
{
list = new List<IEcsSystem>();
list.Add(new SystemsBlockMarkerSystem(blockKey.ToString()));
_systems.Add(blockKey, list);
list = new List<IEcsSystem>
{
new SystemsBlockMarkerSystem(layerName.ToString())
};
_systems.Add(layerName, list);
}
if ((_uniqueTypes.Add(system.GetType()) == false && isUnique))
return;
list.Add(system);
if (system is IEcsModule module)//если система одновременно явялется и системой и модулем то за один Add будет вызван Add и AddModule
AddModule(module);
}
public Builder AddModule(IEcsModule module)
@ -205,70 +210,133 @@ namespace DCFApixels.DragonECS
return this;
}
public Builder BasicSystemsBlock()
{
_isBasicBlockDeclared = true;
_blockExecutionOrder.Add(_basicBlocKey);
return this;
}
public Builder SystemsBlock(string blockKey)
{
if (blockKey == null || blockKey == _basicBlocKey)
return BasicSystemsBlock();
_isOnlyBasicBlock = false;
_blockExecutionOrder.Add(blockKey);
return this;
}
public Builder InsertSystemsBlock(string blockKey, string beforeBlockKey)
{
if (blockKey == null || blockKey == _basicBlocKey)
{
_isBasicBlockDeclared = true;
blockKey = _basicBlocKey;
}
_isOnlyBasicBlock = false;
int index = _blockExecutionOrder.IndexOf(beforeBlockKey);
index = index < 0 ? _blockExecutionOrder.Count - 1 : index;
_blockExecutionOrder.Insert(index, blockKey);
return this;
}
public EcsPipeline Build()
{
if (_isOnlyBasicBlock)
{
return new EcsPipeline(_systems[_basicBlocKey].ToArray());
}
//EcsDebug.Print(string.Join(", ", Layers));
if(_isBasicBlockDeclared == false)
_blockExecutionOrder.Insert(_blockExecutionOrder.IndexOf(EcsConsts.BEGIN_SYSTEMS_BLOCK) + 1, _basicBlocKey);//вставить после BEGIN_SYSTEMS_BLOCK
SystemsBlock(EcsConsts.END_SYSTEMS_BLOCK);
SystemsBlock(EcsConsts.POST_END_SYSTEMS_BLOCK);
Add(new DeleteEmptyEntitesSsytem(), EcsConsts.POST_END_SYSTEMS_BLOCK);
Add(new DeleteEmptyEntitesSystem(), EcsConsts.POST_END_LAYER);
List<IEcsSystem> result = new List<IEcsSystem>(32);
List<IEcsSystem> basicBlockList = _systems[_basicBlocKey];
List<IEcsSystem> basicBlockList = _systems[_basicLayer];
foreach (var item in _systems)
{
if (!_blockExecutionOrder.Contains(item.Key))
{
if (!Layers.Has(item.Key))
basicBlockList.AddRange(item.Value);
}
}
foreach (var item in _blockExecutionOrder)
foreach (var item in Layers)
{
if(_systems.TryGetValue(item, out var list))
{
result.AddRange(list);
}
}
return new EcsPipeline(result.ToArray());
}
public class LayerList : IEnumerable<string>
{
private const string ADD_LAYER = nameof(ADD_LAYER); // автоматический слой нужный только для метода Add
private Builder _source;
private List<string> _layers;
private string _basicLayerName;
public LayerList(Builder source, string basicLayerName)
{
_source = source;
_layers = new List<string>(16) { basicLayerName, ADD_LAYER };
_basicLayerName = basicLayerName;
}
public Builder Add(string newLayer) => Insert(ADD_LAYER, newLayer);
public Builder Insert(string targetLayer, string newLayer)
{
if (Has(newLayer)) return _source;
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
_layers.Insert(index, newLayer);
return _source;
}
public Builder InsertAfter(string targetLayer, string newLayer)
{
if (Has(newLayer)) return _source;
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
if (++index >= _layers.Count)
_layers.Add(newLayer);
else
_layers.Insert(index, newLayer);
return _source;
}
public Builder Move(string targetLayer, string movingLayer)
{
_layers.Remove(movingLayer);
return Insert(targetLayer, movingLayer);
}
public Builder MoveAfter(string targetLayer, string movingLayer)
{
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
_layers.Remove(movingLayer);
return InsertAfter(targetLayer, movingLayer);
}
public Builder Add(params string[] newLayers) => Insert(ADD_LAYER, newLayers);
public Builder Insert(string targetLayer, params string[] newLayers)
{
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
_layers.InsertRange(index, newLayers.Where(o => !Has(o)));
return _source;
}
public Builder InsertAfter(string targetLayer, params string[] newLayers)
{
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
if (++index >= _layers.Count)
_layers.AddRange(newLayers.Where(o => !Has(o)));
else
_layers.InsertRange(index, newLayers.Where(o => !Has(o)));
return _source;
}
public Builder Move(string targetLayer, params string[] movingLayers)
{
foreach (var movingLayer in movingLayers)
_layers.Remove(movingLayer);
return Insert(targetLayer, movingLayers);
}
public Builder MoveAfter(string targetLayer, params string[] movingLayers)
{
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
foreach (var movingLayer in movingLayers)
_layers.Remove(movingLayer);
return InsertAfter(targetLayer, movingLayers);
}
public bool Has(string layer) => _layers.Contains(layer);
public List<string>.Enumerator GetEnumerator() => _layers.GetEnumerator();
IEnumerator<string> IEnumerable<string>.GetEnumerator() => _layers.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _layers.GetEnumerator();
}
}
#endregion
}
@ -281,27 +349,23 @@ namespace DCFApixels.DragonECS
#region Extensions
public static class EcsPipelineExtensions
{
public static void GetRunner<T>(this EcsPipeline self, out T runner) where T : IEcsSystem
{
runner = self.GetRunner<T>();
}
public static bool IsNullOrDestroyed(this EcsPipeline self)
{
return self == null || self.IsDestoryed;
}
public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, object blockKey = null)
public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, string layerName = null)
{
foreach (var item in range)
{
self.Add(item, blockKey);
self.Add(item, layerName);
}
return self;
}
public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, object blockKey = null)
public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, string layerName = null)
{
foreach (var item in range)
{
self.AddUnique(item, blockKey);
self.AddUnique(item, layerName);
}
return self;
}

View File

@ -1,195 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public abstract class EcsQueryBase
{
internal EcsWorld source;
internal EcsGroup groupFilter;
internal EcsQueryMask mask;
private bool _isInit;
#region Properties
public EcsQueryMask Mask => mask;
public EcsWorld World => source;
public bool IsInit => _isInit;
public abstract long WhereVersion { get; }
#endregion
#region Builder
protected virtual void Init(Builder b) { }
protected abstract void OnBuild(Builder b);
public sealed class Builder : EcsQueryBuilderBase
{
private EcsWorld _world;
private List<int> _inc;
private List<int> _exc;
public EcsWorld World => _world;
private Builder(EcsWorld world)
{
_world = world;
_inc = new List<int>(8);
_exc = new List<int>(4);
}
internal static TQuery Build<TQuery>(EcsWorld world) where TQuery : EcsQueryBase
{
Builder builder = new Builder(world);
Type queryType = typeof(TQuery);
ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null);
EcsQueryBase newQuery;
if (constructorInfo != null)
{
newQuery = (EcsQueryBase)constructorInfo.Invoke(new object[] { builder });
}
else
{
newQuery = (EcsQueryBase)Activator.CreateInstance(typeof(TQuery));
newQuery.Init(builder);
}
newQuery.groupFilter = EcsGroup.New(world);
newQuery.source = world;
newQuery.OnBuild(builder);
builder.End(out newQuery.mask);
newQuery._isInit = true;
return (TQuery)(object)newQuery;
}
public sealed override TPool Include<TComponent, TPool>()
{
_inc.Add(_world.GetComponentID<TComponent>());
return _world.GetPool<TComponent, TPool>();
}
public sealed override TPool Exclude<TComponent, TPool>()
{
_exc.Add(_world.GetComponentID<TComponent>());
return _world.GetPool<TComponent, TPool>();
}
public sealed override TPool Optional<TComponent, TPool>()
{
return _world.GetPool<TComponent, TPool>();
}
private void End(out EcsQueryMask mask)
{
_inc.Sort();
_exc.Sort();
mask = new EcsQueryMask(_world.Archetype, _inc.ToArray(), _exc.ToArray());
_world = null;
_inc = null;
_exc = null;
}
}
#endregion
public abstract WhereResult Where();
protected void ExecuteWhere(EcsReadonlyGroup group, EcsGroup result)
{
var pools = World.pools;
result.Clear();
foreach (var e in group)
{
for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++)
{
if (!pools[mask.Inc[i]].Has(e))
goto next;
}
for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++)
{
if (pools[mask.Exc[i]].Has(e))
goto next;
}
result.AggressiveAdd(e);
next: continue;
}
}
//protected void IsMaskCompatible
protected void ExecuteWhereAndSort(EcsReadonlyGroup group, EcsGroup result)
{
ExecuteWhere(group, result);
result.Sort();
}
}
//TODO есть идея проверки того что запросбылвыполнен путем создания ref struct которыйсодержит результат выполнения заапроса и существует только в стеке.
//таким образом для каждой системы он будет выполняться единожды, без холостых перезапусков, скорее всего эта система будет работать лучше чем "Версии запросов"
public abstract class EcsQuery : EcsQueryBase
{
private ProfilerMarker _execute = new ProfilerMarker("EcsQuery.Where");
private long _executeWhereVersion = 0;
#region Properties
//на данный момент бесполное свойство, основная идея, реализовать все методы запроса с аргументом (..., ref long version),
//далее в теле метододов сравнивать с текущей версией, и если они отличаются, выполнять запрос и перезаписывать значение version
//таким образом можно добиться сокращение "холостых" выполнений запроса, тоесть в рамках одной системы запрос всегда будет выполняться один раз
//даже при повторном вызове.
//Но нужно добавить метод для принудительного повторения запроса без сравнения вресий.
//TODO реализовать описанное выше поведение
//TODO проверить что лучше подходит int или long. long делает этот механизм очень надежным, но возможно его использование будет существенно влиять на производительность.
public sealed override long WhereVersion => _executeWhereVersion;
#endregion
protected sealed override void OnBuild(Builder b) { }
public sealed override WhereResult Where()
{
using (_execute.Auto())
{
ExecuteWhereAndSort(World.Entities, groupFilter);
return new WhereResult(this, ++_executeWhereVersion);
}
}
public bool Has(int entityID)
{
return groupFilter.Has(entityID);
}
}
public class EcsQueryMask : EcsComponentMask
{
public EcsQueryMask(Type worldArchetypeType, int[] inc, int[] exc)
{
WorldArchetype = worldArchetypeType;
Inc = inc;
Exc = exc;
}
}
public abstract class EcsQueryBuilderBase
{
public abstract TPool Include<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
public abstract TPool Exclude<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
public abstract TPool Optional<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
}
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 16)]
public readonly ref struct WhereResult
{
public readonly EcsQueryBase query; //ref = 8 byte
public readonly long version; //long = 8 byte
#region Properties
public bool IsNull => query == null;
public EcsWorld World => query.World;
public bool IsActual => query.WhereVersion == version;
#endregion
public WhereResult(EcsQueryBase query, long version)
{
this.query = query;
this.version = version;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup.Enumerator GetEnumerator() => query.groupFilter.GetEnumerator();
}
}

View File

@ -1,5 +1,4 @@
using DCFApixels.DragonECS.RunnersCore;
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@ -9,6 +8,9 @@ using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
using RunnersCore;
using System.ComponentModel;
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
sealed class EcsRunnerFilterAttribute : Attribute
{
@ -26,6 +28,7 @@ namespace DCFApixels.DragonECS
namespace RunnersCore
{
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IEcsRunner
{
public EcsPipeline Source { get; }
@ -55,7 +58,7 @@ namespace DCFApixels.DragonECS
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition()));
}
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
for (int i = 0; i < runnerHandlerTypes.Count; i++)
{
var e = CheckRunnerValide(runnerHandlerTypes[i]);
@ -128,6 +131,7 @@ namespace DCFApixels.DragonECS
EcsRunner<TInterface>.Register(runnerType);
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class EcsRunner<TInterface> : IEcsSystem, IEcsRunner
where TInterface : IEcsSystem
{
@ -135,7 +139,7 @@ namespace DCFApixels.DragonECS
private static Type _subclass;
internal static void Register(Type subclass)
{
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_subclass != null)
{
throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass");

236
src/EcsSubject.cs Normal file
View File

@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
namespace DCFApixels.DragonECS
{
public abstract class EcsSubject
{
[EditorBrowsable(EditorBrowsableState.Always)]
internal EcsWorld source;
[EditorBrowsable(EditorBrowsableState.Always)]
internal EcsMask mask;
private bool _isInit;
#region Properties
[EditorBrowsable(EditorBrowsableState.Never)]
public EcsMask Mask => mask;
[EditorBrowsable(EditorBrowsableState.Never)]
public EcsWorld World => source;
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsInit => _isInit;
#endregion
#region Methods
public bool IsMatches(int entityID) => source.IsMaskCompatible(mask, entityID);
#endregion
#region Builder
protected virtual void Init(Builder b) { }
public sealed class Builder : EcsSubjectBuilderBase
{
private EcsWorld _world;
private List<int> _inc;
private List<int> _exc;
public EcsWorld World => _world;
private Builder(EcsWorld world)
{
_world = world;
_inc = new List<int>(8);
_exc = new List<int>(4);
}
internal static TSubject Build<TSubject>(EcsWorld world) where TSubject : EcsSubject
{
Builder builder = new Builder(world);
Type queryType = typeof(TSubject);
ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null);
EcsSubject newSubject;
if (constructorInfo != null)
{
newSubject = (EcsSubject)constructorInfo.Invoke(new object[] { builder });
}
else
{
newSubject = (EcsSubject)Activator.CreateInstance(typeof(TSubject));
newSubject.Init(builder);
}
newSubject.source = world;
builder.End(out newSubject.mask);
newSubject._isInit = true;
return (TSubject)newSubject;
}
public sealed override TPool Include<TComponent, TPool>()
{
IncludeImplicit<TComponent>();
return _world.GetPool<TComponent, TPool>();
}
public sealed override TPool Exclude<TComponent, TPool>()
{
return _world.GetPool<TComponent, TPool>();
}
public sealed override TPool Optional<TComponent, TPool>()
{
return _world.GetPool<TComponent, TPool>();
}
public void IncludeImplicit<TComponent>()
{
int id = _world.GetComponentID<TComponent>();
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
#endif
_inc.Add(_world.GetComponentID<TComponent>());
}
public void ExcludeImplicit<TComponent>()
{
int id = _world.GetComponentID<TComponent>();
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{typeof(TComponent).Name} already in constraints list.");
#endif
_exc.Add(_world.GetComponentID<TComponent>());
}
private void End(out EcsMask mask)
{
_inc.Sort();
_exc.Sort();
mask = new EcsMask(_world.Archetype, _inc.ToArray(), _exc.ToArray());
_world = null;
_inc = null;
_exc = null;
}
}
#endregion
}
public static class EcsSubjectExtensions
{
public static EcsSubjectIterator<TSubject> GetIterator<TSubject>(this TSubject self) where TSubject : EcsSubject
{
return new EcsSubjectIterator<TSubject>(self, self.World.Entities);
}
public static EcsSubjectIterator<TSubject> GetIteratorFor<TSubject>(this TSubject self, EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
{
return new EcsSubjectIterator<TSubject>(self, sourceGroup);
}
}
#region BuilderBase
public abstract class EcsSubjectBuilderBase
{
public abstract TPool Include<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new();
public abstract TPool Exclude<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new();
public abstract TPool Optional<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new();
}
#endregion
#region Mask
public sealed class EcsMask
{
internal readonly Type WorldType;
internal readonly int[] Inc;
internal readonly int[] Exc;
public EcsMask(Type worldType, int[] inc, int[] exc)
{
WorldType = worldType;
Inc = inc;
Exc = exc;
}
public override string ToString()
{
return $"Inc({string.Join(", ", Inc)}) Exc({string.Join(", ", Exc)})";
}
}
#endregion
#region Iterator
public ref struct EcsSubjectIterator<TSubject> where TSubject : EcsSubject
{
public readonly TSubject s;
private EcsReadonlyGroup sourceGroup;
private Enumerator enumerator;
public EcsSubjectIterator(TSubject s, EcsReadonlyGroup sourceGroup)
{
this.s = s;
this.sourceGroup = sourceGroup;
enumerator = default;
}
public int Entity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => enumerator.Current;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Begin() => enumerator = GetEnumerator();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Next() => enumerator.MoveNext();
public void CopyTo(EcsGroup group)
{
group.Clear();
var enumerator = GetEnumerator();
while (enumerator.MoveNext())
group.AggressiveAdd(enumerator.Current);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() => new Enumerator(sourceGroup, s);
public override string ToString()
{
StringBuilder result = new StringBuilder();
foreach (var e in this)
{
result.Append(e);
result.Append(", ");
}
return result.ToString();
}
public ref struct Enumerator
{
private EcsGroup.Enumerator _sourceGroup;
private readonly int[] _inc;
private readonly int[] _exc;
private readonly IEcsPoolImplementation[] _pools;
public Enumerator(EcsReadonlyGroup sourceGroup, EcsSubject subject)
{
_sourceGroup = sourceGroup.GetEnumerator();
_inc = subject.mask.Inc;
_exc = subject.mask.Exc;
_pools = subject.World.pools;
}
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _sourceGroup.Current;
}
public bool MoveNext()
{
while (_sourceGroup.MoveNext())
{
int e = _sourceGroup.Current;
for (int i = 0, iMax = _inc.Length; i < iMax; i++)
if (!_pools[_inc[i]].Has(e)) goto next;
for (int i = 0, iMax = _exc.Length; i < iMax; i++)
if (_pools[_exc[i]].Has(e)) goto next;
return true;
next: continue;
}
return false;
}
}
}
#endregion
}

View File

@ -5,6 +5,13 @@ using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
using Internal;
internal sealed class EcsNullWorld : EcsWorld<EcsNullWorld>
{
public EcsNullWorld() : base(null, false) { }
}
public abstract class EcsWorld
{
private const short GEN_BITS = 0x7fff;
@ -15,7 +22,7 @@ namespace DCFApixels.DragonECS
private static IntDispenser _worldIdDispenser = new IntDispenser(0);
public readonly short uniqueID;
private int _worldArchetypeID;
private int _worldTypeID;
private IntDispenser _entityDispenser;
private int _entitiesCount;
@ -30,10 +37,11 @@ namespace DCFApixels.DragonECS
private int[] _delEntBuffer;
private int _delEntBufferCount;
internal EcsPoolBase[] pools;
internal IEcsPoolImplementation[] pools;
private EcsNullPool _nullPool;
private EcsQueryBase[] _queries;
private EcsSubject[] _subjects;
private EcsQueryExecutor[] _executors;
private EcsPipeline _pipeline;
@ -50,26 +58,35 @@ namespace DCFApixels.DragonECS
public int Capacity => _entitesCapacity; //_denseEntities.Length;
public EcsPipeline Pipeline => _pipeline;
public EcsReadonlyGroup Entities => _allEntites.Readonly;
public ReadOnlySpan<EcsPoolBase> AllPools => pools;
public ReadOnlySpan<IEcsPoolImplementation> AllPools => pools;
#endregion
#region Constructors/Destroy
public EcsWorld(EcsPipeline pipline)
static EcsWorld()
{
EcsNullWorld nullWorld = new EcsNullWorld();
Worlds[0] = nullWorld;
}
public EcsWorld(EcsPipeline pipline) : this(pipline, true) { }
internal EcsWorld(EcsPipeline pipline, bool isIndexable)
{
_entitesCapacity = 512;
uniqueID = (short)_worldIdDispenser.GetFree();
if (uniqueID >= Worlds.Length)
Array.Resize(ref Worlds, Worlds.Length << 1);
Worlds[uniqueID] = this;
if (isIndexable)
{
uniqueID = (short)_worldIdDispenser.GetFree();
if (uniqueID >= Worlds.Length)
Array.Resize(ref Worlds, Worlds.Length << 1);
Worlds[uniqueID] = this;
}
_worldArchetypeID = WorldMetaStorage.GetWorldId(Archetype);
_worldTypeID = WorldMetaStorage.GetWorldId(Archetype);
_pipeline = pipline ?? EcsPipeline.Empty;
if (!_pipeline.IsInit) pipline.Init();
_entityDispenser = new IntDispenser(0);
_nullPool = EcsNullPool.instance;
pools = new EcsPoolBase[512];
pools = new IEcsPoolImplementation[512];
ArrayUtility.Fill(pools, _nullPool);
_gens = new short[_entitesCapacity];
@ -82,13 +99,16 @@ namespace DCFApixels.DragonECS
_groups = new List<WeakReference<EcsGroup>>();
_allEntites = GetGroupFromPool();
_queries = new EcsQueryBase[128];
_subjects = new EcsSubject[128];
_executors = new EcsQueryExecutor[128];
_entityCreate = _pipeline.GetRunner<IEcsEntityCreate>();
_entityDestry = _pipeline.GetRunner<IEcsEntityDestroy>();
_pipeline.GetRunner<IEcsInject<EcsWorld>>().Inject(this);
_pipeline.GetRunner<IEcsWorldCreate>().OnWorldCreate(this);
}
public void Destroy()
{
_entityDispenser = null;
@ -96,7 +116,8 @@ namespace DCFApixels.DragonECS
_gens = null;
pools = null;
_nullPool = null;
_queries = null;
_subjects = null;
_executors = null;
Worlds[uniqueID] = null;
_worldIdDispenser.Release(uniqueID);
@ -109,14 +130,14 @@ namespace DCFApixels.DragonECS
#endregion
#region GetComponentID
public int GetComponentID<T>() => WorldMetaStorage.GetComponentId<T>(_worldArchetypeID);////ComponentType<TWorldArchetype>.uniqueID;
public int GetComponentID<T>() => WorldMetaStorage.GetComponentId<T>(_worldTypeID);////ComponentType<TWorldArchetype>.uniqueID;
#endregion
#region GetPool
public TPool GetPool<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new()
public TPool GetPool<TComponent, TPool>() where TComponent : struct where TPool : IEcsPoolImplementation<TComponent>, new()
{
int uniqueID = WorldMetaStorage.GetComponentId<TComponent>(_worldArchetypeID);
int uniqueID = WorldMetaStorage.GetComponentId<TComponent>(_worldTypeID);
if (uniqueID >= pools.Length)
{
@ -129,8 +150,7 @@ namespace DCFApixels.DragonECS
{
var pool = new TPool();
pools[uniqueID] = pool;
pool.PreInitInternal(this, uniqueID);
pool.InvokeInit(this);
pool.OnInit(this, uniqueID);
//EcsDebug.Print(pool.GetType().FullName);
}
@ -140,44 +160,118 @@ namespace DCFApixels.DragonECS
#endregion
#region Queries
public TQuery Select<TQuery>() where TQuery : EcsQueryBase
public TSubject GetSubject<TSubject>() where TSubject : EcsSubject
{
int uniqueID = WorldMetaStorage.GetQueryId<TQuery>(_worldArchetypeID);
if (uniqueID >= _queries.Length)
Array.Resize(ref _queries, _queries.Length << 1);
if (_queries[uniqueID] == null)
_queries[uniqueID] = EcsQueryBase.Builder.Build<TQuery>(this);
return (TQuery)_queries[uniqueID];
int uniqueID = WorldMetaStorage.GetSubjectId<TSubject>(_worldTypeID);
if (uniqueID >= _subjects.Length)
Array.Resize(ref _subjects, _subjects.Length << 1);
if (_subjects[uniqueID] == null)
_subjects[uniqueID] = EcsSubject.Builder.Build<TSubject>(this);
return (TSubject)_subjects[uniqueID];
}
public WhereResult Where<TQuery>(out TQuery query) where TQuery : EcsQueryBase
#region Iterate
public EcsSubjectIterator<TSubject> IterateFor<TSubject>(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject
{
query = Select<TQuery>();
return query.Where();
}
public WhereResult Where<TQuery>() where TQuery : EcsQueryBase
{
return Select<TQuery>().Where();
}
public TQuery Join<TQuery>(WhereResult targetWorldWhereQuery, out TQuery query) where TQuery : EcsJoinAttachQueryBase
{
query = Select<TQuery>();
query.Join(targetWorldWhereQuery);
return query;
subject = GetSubject<TSubject>();
return subject.GetIteratorFor(sourceGroup);
}
public TQuery Join<TQuery>(WhereResult targetWorldWhereQuery) where TQuery : EcsJoinAttachQueryBase
public EcsSubjectIterator<TSubject> IterateFor<TSubject>(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
{
TQuery query = Select<TQuery>();
query.Join(targetWorldWhereQuery);
return query;
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 IsMaskCompatible
public bool IsMaskCompatible(EcsComponentMask mask, int entityID)
#region Where
private EcsWhereExecutor<TSubject> GetWhereExecutor<TSubject>() where TSubject : EcsSubject
{
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (mask.WorldArchetype != Archetype)
int id = WorldMetaStorage.GetExecutorId<EcsWhereExecutor<TSubject>>(_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];
}
public EcsWhereResult<TSubject> WhereFor<TSubject>(EcsReadonlyGroup sourceGroup, out TSubject subject) where TSubject : EcsSubject
{
var executor = GetWhereExecutor<TSubject>();
subject = executor.Subject;
return executor.ExecuteFor(sourceGroup);
}
public EcsWhereResult<TSubject> WhereFor<TSubject>(EcsReadonlyGroup sourceGroup) where TSubject : EcsSubject
{
return GetWhereExecutor<TSubject>().ExecuteFor(sourceGroup);
}
public EcsWhereResult<TSubject> Where<TSubject>(out TSubject subject) where TSubject : EcsSubject
{
var executor = GetWhereExecutor<TSubject>();
subject = executor.Subject;
return executor.Execute();
}
public EcsWhereResult<TSubject> Where<TSubject>() where TSubject : EcsSubject
{
return GetWhereExecutor<TSubject>().Execute();
}
#endregion
#region Join
private EcsJoinAttachExecutor<TSubject, TAttachComponent> GetJoinAttachExecutor<TSubject, TAttachComponent>()
where TSubject : EcsSubject
where TAttachComponent : struct, IEcsAttachComponent
{
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)
throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TTableArhetype)");
#endif
for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++)
@ -195,7 +289,7 @@ namespace DCFApixels.DragonECS
#endregion
#region Entity
public EcsEntity NewEntity()
public int NewEntity()
{
int entityID = _entityDispenser.GetFree();
_entitiesCount++;
@ -221,13 +315,17 @@ namespace DCFApixels.DragonECS
}
}
foreach (var item in pools)
item.InvokeOnWorldResize(_gens.Length);
item.OnWorldResize(_gens.Length);
}
_gens[entityID] &= GEN_BITS;
EcsEntity entity = new EcsEntity(entityID, ++_gens[entityID], uniqueID);
_entityCreate.OnEntityCreate(entity);
_entityCreate.OnEntityCreate(entityID);
_allEntites.Add(entityID);
return entity;
return entityID;
}
public entlong NewEntityLong()
{
int e = NewEntity();
return GetEntityLong(e);
}
public void DelEntity(int entityID)
@ -242,13 +340,16 @@ namespace DCFApixels.DragonECS
ReleaseDelEntityBuffer();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsEntity GetEcsEntity(int entityID) => new EcsEntity(entityID, _gens[entityID], uniqueID); //TODO придумать получше имя метода
public entlong GetEntityLong(int entityID) => new entlong(entityID, _gens[entityID], uniqueID); //TODO придумать получше имя метода
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsAlive(int entityID, short gen) => _gens[entityID] == gen;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsUsed(int entityID) => (_gens[entityID] | DEATH_GEN_BIT) == 0;
public bool IsUsed(int entityID) => _gens[entityID] >= 0;
public void ReleaseDelEntityBuffer()
{
ReadOnlySpan<int> buffser = new ReadOnlySpan<int>(_delEntBuffer, 0, _delEntBufferCount);
foreach (var pool in pools)
pool.OnReleaseDelEntityBuffer(buffser);
for (int i = 0; i < _delEntBufferCount; i++)
_entityDispenser.Release(_delEntBuffer[i]);
_delEntBufferCount = 0;
@ -264,8 +365,6 @@ namespace DCFApixels.DragonECS
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void IncrementEntityComponentCount(int entityID)
{
@ -278,7 +377,7 @@ namespace DCFApixels.DragonECS
if(count == 0)
DelEntity(entityID);
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (count < 0) throw new EcsFrameworkException("нарушен баланс инкремента.декремента компонентов");
#endif
}
@ -305,6 +404,42 @@ namespace DCFApixels.DragonECS
_groupsPool.Push(group);
}
#endregion
#region Debug
// public int GetComponents(int entity, ref object[] list)
// {
// var entityOffset = GetRawEntityOffset(entity);
// var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount];
// if (itemsCount == 0) { return 0; }
// if (list == null || list.Length < itemsCount)
// {
// list = new object[_pools.Length];
// }
// var dataOffset = entityOffset + RawEntityOffsets.Components;
// for (var i = 0; i < itemsCount; i++)
// {
// list[i] = _pools[_entities[dataOffset + i]].GetRaw(entity);
// }
// return itemsCount;
// }
//
// public int GetComponentTypes(int entity, ref Type[] list)
// {
// var entityOffset = GetRawEntityOffset(entity);
// var itemsCount = _entities[entityOffset + RawEntityOffsets.ComponentsCount];
// if (itemsCount == 0) { return 0; }
// if (list == null || list.Length < itemsCount)
// {
// list = new Type[_pools.Length];
// }
// var dataOffset = entityOffset + RawEntityOffsets.Components;
// for (var i = 0; i < itemsCount; i++)
// {
// list[i] = _pools[_entities[dataOffset + i]].GetComponentType();
// }
// return itemsCount;
// }
#endregion
}
public abstract class EcsWorld<TWorldArchetype> : EcsWorld
@ -312,6 +447,7 @@ namespace DCFApixels.DragonECS
{
public override Type Archetype => typeof(TWorldArchetype);
public EcsWorld(EcsPipeline pipline) : base(pipline) { }
internal EcsWorld(EcsPipeline pipline, bool isIndexable) : base(pipline, isIndexable) { }
}
#region Utils
@ -366,7 +502,9 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetComponentId<T>(int worldID) => Component<T>.Get(worldID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetQueryId<T>(int worldID) => Query<T>.Get(worldID);
public static int GetSubjectId<T>(int worldID) => Subject<T>.Get(worldID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetExecutorId<T>(int worldID) => Executor<T>.Get(worldID);
private abstract class Resizer
{
public abstract void Resize(int size);
@ -376,7 +514,8 @@ namespace DCFApixels.DragonECS
public override void Resize(int size)
{
Array.Resize(ref Component<T>.ids, size);
Array.Resize(ref Query<T>.ids, size);
Array.Resize(ref Subject<T>.ids, size);
Array.Resize(ref Executor<T>.ids, size);
}
}
private static class Component<T>
@ -398,10 +537,29 @@ namespace DCFApixels.DragonECS
return id;
}
}
private static class Query<T>
private static class Subject<T>
{
public static int[] ids;
static Query()
static Subject()
{
ids = new int[tokenCount];
for (int i = 0; i < ids.Length; i++)
ids[i] = -1;
resizer.Add(new Resizer<T>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Get(int token)
{
ref int id = ref ids[token];
if (id < 0)
id = queryCounts[token]++;
return id;
}
}
private static class Executor<T>
{
public static int[] ids;
static Executor()
{
ids = new int[tokenCount];
for (int i = 0; i < ids.Length; i++)

View File

@ -1,115 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
// uniqueID - 32 bits
// gen - 16 bits
// world - 16 bits
/// <summary>Strong identifier/Permanent entity identifier</summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)]
public readonly partial struct EcsEntity : IEquatable<long>, IEquatable<EcsEntity>
{
public static readonly EcsEntity NULL = default;
[FieldOffset(0)]
internal readonly long full; //Union
[FieldOffset(0)]
public readonly int id;
[FieldOffset(4)]
public readonly short gen;
[FieldOffset(6)]
public readonly short world;
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public ent ToEnt() => EcsWorld.Worlds[world].IsAlive(id, gen) ? new ent(id) : default;
public bool IsAlive => EcsWorld.Worlds[world].IsAlive(id, gen);
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsEntity(int id, short gen, short world) : this()
{
this.id = id;
this.gen = gen;
this.world = world;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EcsEntity(long full) : this()
{
this.full = full;
}
#endregion
#region Equals
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(EcsEntity other) => full == other.full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(long other) => full == other;
#endregion
#region Object
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString() => $"Entity(uniqueID:{id} gen:{gen} world:{world})";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is EcsEntity other && full == other.full;
#endregion
#region operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(in EcsEntity a, in EcsEntity b) => a.full == b.full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(in EcsEntity a, in EcsEntity b) => a.full != b.full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator long(in EcsEntity a) => a.full;
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public static explicit operator ent(in EcsEntity a) => a.ToEnt();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator EcsEntity(in long a) => new EcsEntity(a);
#endregion
}
public readonly partial struct EcsEntity
{
private static EcsProfilerMarker _IsNullMarker = new EcsProfilerMarker("EcsEntity.IsNull");
public bool IsNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
//using (_IsNullMarker.Auto())
return this == NULL;
}
}
public bool IsNotNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
//using (_IsNullMarker.Auto())
return this != NULL;
}
}
public EcsWorld GetWorld() => EcsWorld.Worlds[world];
}
public static partial class entExtensions
{
private static EcsProfilerMarker _IsAliveMarker = new EcsProfilerMarker("EcsEntity.IsAlive");
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static bool IsAlive(this ref EcsEntity self)
// {
// //using (_IsAliveMarker.Auto())
// //{
// bool result = EcsWorld.Worlds[self.world].IsAlive(self.id, self.gen);
// if (!result) self = EcsEntity.NULL;
// return result;
// //}
// }
}
}

View File

@ -3,7 +3,7 @@
namespace DCFApixels.DragonECS
{
[Serializable]
public class EcsRelationException : Exception
public class EcsRelationException : EcsFrameworkException
{
public EcsRelationException() { }
public EcsRelationException(string message) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message) { }

View File

@ -3,7 +3,7 @@
namespace DCFApixels.DragonECS
{
[Serializable]
public class EcsRunnerImplementationException : Exception
public class EcsRunnerImplementationException : EcsFrameworkException
{
public EcsRunnerImplementationException() { }
public EcsRunnerImplementationException(string message) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message) { }

View File

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

View File

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

View File

@ -0,0 +1,7 @@
namespace DCFApixels.DragonECS
{
public abstract class EcsQueryExecutor
{
internal abstract void Destroy();
}
}

View File

@ -0,0 +1,72 @@
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsWhereExecutor<TSubject> : EcsQueryExecutor where TSubject : EcsSubject
{
private readonly TSubject _subject;
private readonly EcsGroup _filteredGroup;
private long _executeVersion;
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where");
#region Properties
public TSubject Subject => _subject;
internal long ExecuteVersion => _executeVersion;
#endregion
#region Constructors
public EcsWhereExecutor(TSubject subject)
{
_subject = subject;
_filteredGroup = EcsGroup.New(subject.World);
}
#endregion
#region Methods
public EcsWhereResult<TSubject> Execute() => ExecuteFor(_subject.World.Entities);
public EcsWhereResult<TSubject> ExecuteFor(EcsReadonlyGroup sourceGroup)
{
using (_executeWhere.Auto())
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
#endif
_subject.GetIteratorFor(sourceGroup).CopyTo(_filteredGroup);
return new EcsWhereResult<TSubject>(this, _filteredGroup.Readonly);
}
}
internal sealed override void Destroy()
{
_filteredGroup.Release();
}
#endregion
}
#region WhereExecuter Results
public readonly ref struct EcsWhereResult<TSubject> where TSubject : EcsSubject
{
public readonly TSubject s;
private readonly EcsWhereExecutor<TSubject> _executer;
public readonly EcsReadonlyGroup group;
private readonly long _version;
public bool IsRelevant => _version == _executer.ExecuteVersion;
public EcsWhereResult(EcsWhereExecutor<TSubject> executer, EcsReadonlyGroup group)
{
_executer = executer;
_version = executer.ExecuteVersion;
s = executer.Subject;
this.group = group;
}
public EcsGroup.Enumerator GetEnumerator()
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsRelevant) throw new System.InvalidOperationException();//TODO составить текст исключения.
#endif
return group.GetEnumerator();
}
}
#endregion
}

View File

@ -1,42 +0,0 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
public interface IEcsComponentReset<T>
{
public void Reset(ref T component);
}
public static class EcsComponentResetHandler<T>
{
public static readonly IEcsComponentReset<T> instance;
public static readonly bool isHasHandler;
static EcsComponentResetHandler()
{
Type targetType = typeof(T);
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType)))
{
instance = (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType));
isHasHandler = true;
}
else
{
instance = new ComponentResetDummyHandler<T>();
isHasHandler = false;
}
}
}
internal sealed class ComponentResetDummyHandler<T> : IEcsComponentReset<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => component = default;
}
internal sealed class ComponentResetHandler<T> : IEcsComponentReset<T>
where T : IEcsComponentReset<T>
{
private T _fakeInstnace = default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => _fakeInstnace.Reset(ref component);
}
}

View File

@ -1,13 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
using static EcsPoolThrowHalper;
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
public sealed class EcsAttachPool<T> : EcsPoolBase<T>
/// <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;
@ -30,11 +36,17 @@ namespace DCFApixels.DragonECS
#region Properites
public int Count => _count;
public int Capacity => _items.Length;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
protected override void Init(EcsWorld world)
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
_poolRunners = new PoolRunners(world.Pipeline);
_entities = EcsGroup.New(world);
@ -45,23 +57,14 @@ namespace DCFApixels.DragonECS
}
#endregion
#region Write/Read/Has/Del
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
public void Add(int entityID, EcsEntity target)
#region Methods
public void Add(int entityID, entlong target)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld)
{
throw new EcsRelationException();
}
if (_sanitizeTargetWorld > 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent<T>(entityID);
_sanitizeTargetWorld = target.world;
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
#endif
// using (_addMark.Auto())
// {
ref bool entityFlag = ref _entityFlags[entityID];
if (entityFlag == false)
{
@ -72,59 +75,107 @@ namespace DCFApixels.DragonECS
}
_poolRunners.write.OnComponentWrite<T>(entityID);
_items[entityID].Target = target;
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int entityID, EcsEntity target)
public void Set(int entityID, entlong target)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld)
{
throw new EcsRelationException();
}
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
if (_sanitizeTargetWorld >= 0 && target.world != _sanitizeTargetWorld) ThrowWorldDifferent<T>(entityID);
_sanitizeTargetWorld = target.world;
#endif
// using (_writeMark.Auto())
_poolRunners.write.OnComponentWrite<T>(entityID);
_items[entityID].Target = target;
}
public void AddOrSet(int entityID, entlong target)
{
if (Has(entityID))
Set(entityID, target);
else
Add(entityID, target);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _items[entityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
public bool Has(int entityID)
{
// using (_hasMark.Auto())
return _entityFlags[entityID];
}
public void Del(int entityID)
{
// using (_delMark.Auto())
// {
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_entities.Remove(entityID);
_entityFlags[entityID] = false;
_count--;
_poolRunners.del.OnComponentDel<T>(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);
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _entityFlags, newSize);
Array.Resize(ref _items, newSize);
}
protected override void OnDestroy() { }
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<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 IEnumerator - IntelliSense hack
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
public interface IEcsAttachComponent
{
public EcsEntity Target { get; set; }
public entlong Target { get; set; }
}
public static class EcsAttachComponentPoolExt
{
@ -133,15 +184,15 @@ namespace DCFApixels.DragonECS
return self.GetPool<TAttachComponent, EcsAttachPool<TAttachComponent>>();
}
public static EcsAttachPool<TAttachComponent> Include<TAttachComponent>(this EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
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 EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
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 EcsQueryBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
public static EcsAttachPool<TAttachComponent> Optional<TAttachComponent>(this EcsSubjectBuilderBase self) where TAttachComponent : struct, IEcsAttachComponent
{
return self.Optional<TAttachComponent, EcsAttachPool<TAttachComponent>>();
}

View File

@ -1,27 +1,39 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
public sealed class EcsNotNullPool<T> : EcsPoolBase<T>
where T : struct, INotNullComponent
/// <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 PoolRunners _poolRunners;
#region Properites
public int Count => _count;
public int Capacity => _items.Length;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
protected override void Init(EcsWorld world)
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
_items = new T[world.Capacity];
_count = 0;
@ -30,59 +42,77 @@ namespace DCFApixels.DragonECS
}
#endregion
#region Write/Read/Has
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
#region Methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entityID)
{
// using (_writeMark.Auto())
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _items[entityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
return ref _items[entityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
public bool Has(int entityID)
{
// using (_hasMark.Auto())
return true;
}
public void Copy(int fromEntityID, int toEntityID)
{
_componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID));
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
#region Callbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _items, newSize);
}
protected override void OnDestroy() { }
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<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 IEnumerator - IntelliSense hack
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
public interface INotNullComponent { }
/// <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, INotNullComponent
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 EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent
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 EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent
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 EcsQueryBuilderBase self) where TNotNullComponent : struct, INotNullComponent
public static EcsNotNullPool<TNotNullComponent> Optional<TNotNullComponent>(this EcsSubjectBuilderBase self) where TNotNullComponent : struct, IEcsNotNullComponent
{
return self.Optional<TNotNullComponent, EcsNotNullPool<TNotNullComponent>>();
}

View File

@ -2,14 +2,16 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsPool<T> : EcsPoolBase<T>
using static EcsPoolThrowHalper;
/// <summary>Pool for IEcsComponent components</summary>
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
where T : struct, IEcsComponent
{
public static string name = typeof(T).Name;
private EcsWorld _source;
private int _id;
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
private T[] _items; //dense
@ -18,16 +20,23 @@ namespace DCFApixels.DragonECS
private int _recycledItemsCount;
private IEcsComponentReset<T> _componentResetHandler;
private IEcsComponentCopy<T> _componentCopyHandler;
private PoolRunners _poolRunners;
#region Properites
public int Count => _itemsCount;
public int Capacity => _items.Length;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
protected override void Init(EcsWorld world)
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
const int capacity = 512;
_mapping = new int[world.Capacity];
@ -37,21 +46,59 @@ namespace DCFApixels.DragonECS
_itemsCount = 0;
_componentResetHandler = EcsComponentResetHandler<T>.instance;
_componentCopyHandler = EcsComponentCopyHandler<T>.instance;
_poolRunners = new PoolRunners(world.Pipeline);
}
#endregion
#region Write/Read/Has/Del
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
#region Methods
public ref T Add(int entityID)
{
// using (_addMark.Auto())
// {
ref int itemIndex = ref _mapping[entityID];
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (itemIndex > 0) ThrowAlreadyHasComponent<T>(entityID);
#endif
if (_recycledItemsCount > 0)
{
itemIndex = _recycledItems[--_recycledItemsCount];
_itemsCount++;
}
else
{
itemIndex = ++_itemsCount;
if (itemIndex >= _items.Length)
Array.Resize(ref _items, _items.Length << 1);
}
this.IncrementEntityComponentCount(entityID);
_poolRunners.add.OnComponentAdd<T>(entityID);
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _items[itemIndex];
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entityID)
{
// using (_writeMark.Auto())
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _items[_mapping[entityID]];
}
public ref T TryAddOrWrite(int entityID)
{
ref int itemIndex = ref _mapping[entityID];
if (itemIndex <= 0)
{
if (_recycledItemsCount > 0)
@ -65,62 +112,75 @@ namespace DCFApixels.DragonECS
if (itemIndex >= _items.Length)
Array.Resize(ref _items, _items.Length << 1);
}
//_mapping[entityID] = itemIndex; TODO ïðîâåðèòü ÷òî ýòî ëèøíåå äåéñâèå
IncrementEntityComponentCount(entityID);
this.IncrementEntityComponentCount(entityID);
_poolRunners.add.OnComponentAdd<T>(entityID);
}
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _items[itemIndex];
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entityID)
public bool Has(int entityID)
{
// using (_writeMark.Auto())
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
return ref _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
{
// using (_hasMark.Auto())
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
// using (_delMark.Auto())
// {
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
ref int itemIndex = ref _mapping[entityID];
_componentResetHandler.Reset(ref _items[itemIndex]);
if (_recycledItemsCount >= _recycledItems.Length)
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
_recycledItems[_recycledItemsCount++] = itemIndex;
itemIndex = 0;
_mapping[entityID] = 0;
_itemsCount--;
DecrementEntityComponentCount(entityID);
this.DecrementEntityComponentCount(entityID);
_poolRunners.del.OnComponentDel<T>(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
if (Has(toEntityID))
_componentCopyHandler.Copy(ref Write(fromEntityID), ref Write(toEntityID));
else
_componentCopyHandler.Copy(ref Write(fromEntityID), ref Add(toEntityID));
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
#region Callbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
}
protected override void OnDestroy() { }
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
foreach (var entityID in buffer)
TryDel(entityID);
}
#endregion
#region Other
void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID) = (T)dataRaw;
object IEcsPool.GetRaw(int entityID) => Read(entityID);
void IEcsPool.SetRaw(int entityID, object dataRaw) => Write(entityID) = (T)dataRaw;
ref readonly T IEcsPool<T>.Read(int entityID) => ref Read(entityID);
ref T IEcsPool<T>.Write(int entityID) => ref Write(entityID);
#endregion
#region IEnumerator - IntelliSense hack
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
/// <summary>Standard component</summary>
public interface IEcsComponent { }
public static class EcsPoolExt
{
@ -129,15 +189,15 @@ namespace DCFApixels.DragonECS
return self.GetPool<TComponent, EcsPool<TComponent>>();
}
public static EcsPool<TComponent> Include<TComponent>(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent
public static EcsPool<TComponent> Include<TComponent>(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent
{
return self.Include<TComponent, EcsPool<TComponent>>();
}
public static EcsPool<TComponent> Exclude<TComponent>(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent
public static EcsPool<TComponent> Exclude<TComponent>(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent
{
return self.Exclude<TComponent, EcsPool<TComponent>>();
}
public static EcsPool<TComponent> Optional<TComponent>(this EcsQueryBuilderBase self) where TComponent : struct, IEcsComponent
public static EcsPool<TComponent> Optional<TComponent>(this EcsSubjectBuilderBase self) where TComponent : struct, IEcsComponent
{
return self.Optional<TComponent, EcsPool<TComponent>>();
}

View File

@ -1,77 +1,194 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
public abstract class EcsPoolBase
public interface IEcsPool
{
private int _id = -1;
private EcsWorld _world;
internal void PreInitInternal(EcsWorld world, int id)
{
_id = id;
_world = world;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void IncrementEntityComponentCount(int entityID)
{
_world.IncrementEntityComponentCount(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void DecrementEntityComponentCount(int entityID)
{
_world.DecrementEntityComponentCount(entityID);
}
#region Properties
public abstract Type ComponentType { get; }
public EcsWorld World => _world;
public int ComponentID { get; }
public Type ComponentType { get; }
public EcsWorld World { get; }
public int Count { get; }
public int Capacity { get; }
#endregion
#region Methods
public abstract bool Has(int entityID);
protected abstract void Init(EcsWorld world);
protected abstract void OnWorldResize(int newSize);
protected abstract void OnDestroy();
#endregion
#region Internal
internal void InvokeInit(EcsWorld world) => Init(world);
internal void InvokeOnWorldResize(int newSize) => OnWorldResize(newSize);
internal void InvokeOnDestroy() => OnDestroy();
bool Has(int entityID);
void Del(int entityID);
void AddRaw(int entityID, object dataRaw);
object GetRaw(int entityID);
void SetRaw(int entityID, object dataRaw);
void Copy(int fromEntityID, int toEntityID);
#endregion
}
public abstract class EcsPoolBase<T> : EcsPoolBase, IEnumerable<T>
public interface IEcsPool<T>
{
public sealed override Type ComponentType => typeof(T);
//Релазиация интерфейса IEnumerator не работает, нужно только чтобы IntelliSense предлагала названия на основе T. Не нашел другого способа
#region IEnumerable
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
ref T Add(int entityID);
ref readonly T Read(int entityID);
ref T Write(int entityID);
}
public struct NullComponent { }
public sealed class EcsNullPool : EcsPoolBase
/// <summary>Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>.</summary>
/// <typeparam name="T">Component type</typeparam>
public interface IEcsPoolImplementation : IEcsPool
{
public static EcsNullPool instance => new EcsNullPool();
#region Properties
public sealed override Type ComponentType => typeof(NullComponent);
#endregion
#region Methods
public sealed override bool Has(int index) => false;
#endregion
#region Callbacks
protected override void Init(EcsWorld world) { }
protected override void OnWorldResize(int newSize) { }
protected override void OnDestroy() { }
#endregion
void OnInit(EcsWorld world, int componentID);
void OnWorldResize(int newSize);
void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer);
void OnWorldDestroy();
}
/// <summary>Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>.</summary>
/// <typeparam name="T">Component type</typeparam>
public interface IEcsPoolImplementation<T> : IEcsPool<T>, IEcsPoolImplementation { }
public static class EcsPoolThrowHalper
{
public static void ThrowAlreadyHasComponent<T>(int entityID)
{
throw new EcsFrameworkException($"Entity({entityID}) already has component {typeof(T).Name}.");
}
public static void ThrowNotHaveComponent<T>(int entityID)
{
throw new EcsFrameworkException($"Entity({entityID}) has no component {typeof(T).Name}.");
}
public static void ThrowInvalidComponentType<T>(Type invalidType)
{
throw new EcsFrameworkException($"Invalid component type, {typeof(T).Name} required.");
}
public static void ThrowWorldDifferent<T>(int entityID)
{
throw new EcsRelationException($"The world of the target entity({entityID}) in {typeof(T).Name} is not the same as the world of the other entities. ");
}
}
public static class IEcsPoolImplementationExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IncrementEntityComponentCount<T>(this IEcsPoolImplementation<T> self, int entityID)
{
self.World.IncrementEntityComponentCount(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecrementEntityComponentCount<T>(this IEcsPoolImplementation<T> self, int entityID)
{
self.World.DecrementEntityComponentCount(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNullOrDummy(this IEcsPool self)
{
return self == null || self is Internal.EcsNullPool;
}
}
#region Dummy
namespace Internal
{
public struct NullComponent { }
public sealed class EcsNullPool : IEcsPoolImplementation<NullComponent>
{
public static EcsNullPool instance => new EcsNullPool();
#region Properties
int IEcsPool.ComponentID => throw new NotImplementedException();
Type IEcsPool.ComponentType => typeof(NullComponent);
EcsWorld IEcsPool.World => throw new NotImplementedException();
public int Count => 0;
public int Capacity => 0;
#endregion
#region Methods
bool IEcsPool.Has(int index) => false;
void IEcsPool.Del(int entityID) => throw new NotImplementedException();
void IEcsPool.AddRaw(int entityID, object dataRaw) => throw new NotImplementedException();
object IEcsPool.GetRaw(int entityID) => throw new NotImplementedException();
void IEcsPool.SetRaw(int entity, object dataRaw) => throw new NotImplementedException();
void IEcsPool.Copy(int fromEntityID, int toEntityID) => throw new NotImplementedException();
ref NullComponent IEcsPool<NullComponent>.Add(int entityID) => throw new NotImplementedException();
ref readonly NullComponent IEcsPool<NullComponent>.Read(int entityID) => throw new NotImplementedException();
ref NullComponent IEcsPool<NullComponent>.Write(int entityID) => throw new NotImplementedException();
#endregion
#region Callbacks
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { }
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnWorldResize(int newSize) { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
#endregion
}
}
#endregion
#region Reset/Copy interfaces
public interface IEcsComponentReset<T>
{
public void Reset(ref T component);
}
public static class EcsComponentResetHandler<T>
{
public static readonly IEcsComponentReset<T> instance;
public static readonly bool isHasHandler;
static EcsComponentResetHandler()
{
Type targetType = typeof(T);
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType)))
{
instance = (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType));
isHasHandler = true;
}
else
{
instance = new ComponentResetDummyHandler<T>();
isHasHandler = false;
}
}
}
internal sealed class ComponentResetDummyHandler<T> : IEcsComponentReset<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => component = default;
}
internal sealed class ComponentResetHandler<T> : IEcsComponentReset<T>
where T : IEcsComponentReset<T>
{
private T _fakeInstnace = default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => _fakeInstnace.Reset(ref component);
}
public interface IEcsComponentCopy<T>
{
public void Copy(ref T from, ref T to);
}
public static class EcsComponentCopyHandler<T>
{
public static readonly IEcsComponentCopy<T> instance;
public static readonly bool isHasHandler;
static EcsComponentCopyHandler()
{
Type targetType = typeof(T);
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentCopy<>).MakeGenericType(targetType)))
{
instance = (IEcsComponentCopy<T>)Activator.CreateInstance(typeof(ComponentCopyHandler<>).MakeGenericType(targetType));
isHasHandler = true;
}
else
{
instance = new ComponentCopyDummyHandler<T>();
isHasHandler = false;
}
}
}
internal sealed class ComponentCopyDummyHandler<T> : IEcsComponentCopy<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Copy(ref T from, ref T to) => to = from;
}
internal sealed class ComponentCopyHandler<T> : IEcsComponentCopy<T>
where T : IEcsComponentCopy<T>
{
private T _fakeInstnace = default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Copy(ref T from, ref T to) => _fakeInstnace.Copy(ref from, ref to);
}
#endregion
}

View File

@ -1,139 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsReadonlyPool<T> : EcsPoolBase<T>
where T : struct, IEcsReadonlyComponent
{
public static string name = typeof(T).Name;
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
private T[] _items; //dense
private int _itemsCount;
private int[] _recycledItems;
private int _recycledItemsCount;
private IEcsComponentReset<T> _componentResetHandler;
private PoolRunners _poolRunners;
#region Properites
public int Count => _itemsCount;
public int Capacity => _items.Length;
#endregion
#region Init
protected override void Init(EcsWorld world)
{
const int capacity = 512;
_mapping = new int[world.Capacity];
_recycledItems = new int[128];
_recycledItemsCount = 0;
_items = new T[capacity];
_itemsCount = 0;
_componentResetHandler = EcsComponentResetHandler<T>.instance;
_poolRunners = new PoolRunners(world.Pipeline);
}
#endregion
#region Write/Read/Has/Del
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
public ref T Add(int entityID)
{
// using (_addMark.Auto())
// {
ref int itemIndex = ref _mapping[entityID];
if (itemIndex <= 0)
{
if (_recycledItemsCount > 0)
{
itemIndex = _recycledItems[--_recycledItemsCount];
_itemsCount++;
}
else
{
itemIndex = ++_itemsCount;
if (itemIndex >= _items.Length)
Array.Resize(ref _items, _items.Length << 1);
}
//_mapping[entityID] = itemIndex; TODO проверить что это лишнее дейсвие
_poolRunners.add.OnComponentAdd<T>(entityID);
}
else
{
_componentResetHandler.Reset(ref _items[itemIndex]);
}
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _items[itemIndex];
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
return ref _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
{
// using (_hasMark.Auto())
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
// using (_delMark.Auto())
// {
ref int itemIndex = ref _mapping[entityID];
_componentResetHandler.Reset(ref _items[itemIndex]);
if (_recycledItemsCount >= _recycledItems.Length)
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
_recycledItems[_recycledItemsCount++] = itemIndex;
itemIndex = 0;
_itemsCount--;
_poolRunners.del.OnComponentDel<T>(entityID);
// }
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
}
protected override void OnDestroy() { }
#endregion
}
public interface IEcsReadonlyComponent { }
public static class EcsReadonlyPoolExt
{
public static EcsReadonlyPool<TReadolnyComponent> GetPool<TReadolnyComponent>(this EcsWorld self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.GetPool<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
public static EcsReadonlyPool<TReadolnyComponent> Include<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.Include<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
public static EcsReadonlyPool<TReadolnyComponent> Exclude<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.Exclude<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
public static EcsReadonlyPool<TReadolnyComponent> Optional<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.Optional<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
}
}

View File

@ -1,154 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
//íå âëèÿåò íà ñ÷åò÷èê êîìïîíåíòîâ íà ñóùíîñòè
public sealed class EcsRelationPool<T> : EcsPoolBase<T>
where T : struct, IEcsRelationComponent
{
private bool[] _entityFlags;// index = entityID / value = entityFlag;/ value = 0 = no entityID
private T[] _items; //sparse
private int _count;
private PoolRunners _poolRunners;
private EcsGroup _entities;
public EcsReadonlyGroup Entities => _entities.Readonly;
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
private short _sanitizeFirstWorld = -1;
private short _sanitizeSecondWorld = -1;
#endif
#region Properites
public int Count => _count;
public int Capacity => _items.Length;
#endregion
#region Init
protected override void Init(EcsWorld world)
{
_poolRunners = new PoolRunners(world.Pipeline);
_entityFlags = new bool[world.Capacity];
_items = new T[world.Capacity];
_count = 0;
}
#endregion
#region Write/Read/Has/Del
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
public void Add(int entityID, EcsEntity first, EcsEntity second)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if((_sanitizeFirstWorld >= 0 && first.world != _sanitizeFirstWorld) &&
(_sanitizeSecondWorld >= 0 && second.world != _sanitizeSecondWorld))
{
throw new EcsRelationException();
}
#endif
// using (_addMark.Auto())
// {
ref bool entityFlag = ref _entityFlags[entityID];
if (entityFlag == false)
{
entityFlag = true;
_count++;
_entities.Add(entityID);
_poolRunners.add.OnComponentAdd<T>(entityID);
}
_poolRunners.write.OnComponentWrite<T>(entityID);
_items[entityID].Set(first, second);
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int entityID, EcsEntity first, EcsEntity second)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if ((_sanitizeFirstWorld >= 0 && first.world != _sanitizeFirstWorld) &&
(_sanitizeSecondWorld >= 0 && second.world != _sanitizeSecondWorld))
{
throw new EcsRelationException();
}
_sanitizeFirstWorld = first.world;
_sanitizeSecondWorld = second.world;
#endif
// using (_writeMark.Auto())
//{
_poolRunners.write.OnComponentWrite<T>(entityID);
_items[entityID].Set(first, second);
//}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
return ref _items[entityID];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
{
// using (_hasMark.Auto())
return _entityFlags[entityID];
}
public void Del(int entityID)
{
// using (_delMark.Auto())
// {
_entities.Remove(entityID);
_entityFlags[entityID] = false;
_count--;
_poolRunners.del.OnComponentDel<T>(entityID);
// }
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
{
Array.Resize(ref _entityFlags, newSize);
Array.Resize(ref _items, newSize);
}
protected override void OnDestroy() { }
#endregion
}
public interface IEcsRelationComponent
{
public EcsEntity First { get; set; }
public EcsEntity Second { get; set; }
}
public static class IEcsRelationComponentExt
{
public static void Set<T>(this ref T self, EcsEntity first, EcsEntity second) where T : struct, IEcsRelationComponent
{
self.First = first;
self.Second = second;
}
}
public static class EcsRelationPoolExt
{
public static EcsRelationPool<TRelationComponent> GetPool<TRelationComponent>(this EcsWorld self) where TRelationComponent : struct, IEcsRelationComponent
{
return self.GetPool<TRelationComponent, EcsRelationPool<TRelationComponent>>();
}
public static EcsRelationPool<TRelationComponent> Include<TRelationComponent>(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent
{
return self.Include<TRelationComponent, EcsRelationPool<TRelationComponent>>();
}
public static EcsRelationPool<TRelationComponent> Exclude<TRelationComponent>(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent
{
return self.Exclude<TRelationComponent, EcsRelationPool<TRelationComponent>>();
}
public static EcsRelationPool<TRelationComponent> Optional<TRelationComponent>(this EcsQueryBuilderBase self) where TRelationComponent : struct, IEcsRelationComponent
{
return self.Optional<TRelationComponent, EcsRelationPool<TRelationComponent>>();
}
}
}

View File

@ -1,12 +1,17 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsSinglePool<T> : EcsPoolBase<T>
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;
@ -21,77 +26,112 @@ namespace DCFApixels.DragonECS
get => ref _component;
}
public int Count => _count;
int IEcsPool.Capacity => -1;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
protected override void Init(EcsWorld world)
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
_mapping = new int[world.Capacity];
_count = 0;
_poolRunners = new PoolRunners(world.Pipeline);
}
#endregion
#region Write/Read/Has/Del
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
#region Methods
public ref T Add(int entityID)
{
// using (_addMark.Auto())
// {
if (_mapping[entityID] <= 0)
{
_mapping[entityID] = ++_count;
IncrementEntityComponentCount(entityID);
_poolRunners.add.OnComponentAdd<T>(entityID);
}
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
#endif
_mapping[entityID] = ++_count;
this.IncrementEntityComponentCount(entityID);
_poolRunners.add.OnComponentAdd<T>(entityID);
return ref _component;
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entityID)
{
// using (_writeMark.Auto())
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _component;
}
public ref T TryAddOrWrite(int entityID)
{
if (!Has(entityID))
return ref Add(entityID);
return ref Write(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _component;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
public bool Has(int entityID)
{
// using (_hasMark.Auto())
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
// using (_delMark.Auto())
// {
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_mapping[entityID] = 0;
_count--;
DecrementEntityComponentCount(entityID);
this.DecrementEntityComponentCount(entityID);
_poolRunners.del.OnComponentDel<T>(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);
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
#region Callbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
}
protected override void OnDestroy() { }
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<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 IEnumerator - IntelliSense hack
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
/// <summary> Singleton component </summary>
/// <summary>Singleton component</summary>
public interface IEcsSingleComponent { }
public static class EcsSinglePoolExt
{
@ -101,15 +141,15 @@ namespace DCFApixels.DragonECS
return self.GetPool<TSingleComponent, EcsSinglePool<TSingleComponent>>();
}
public static EcsSinglePool<TSingleComponent> Include<TSingleComponent>(this EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
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 EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
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 EcsQueryBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
public static EcsSinglePool<TSingleComponent> Optional<TSingleComponent>(this EcsSubjectBuilderBase self) where TSingleComponent : struct, IEcsSingleComponent
{
return self.Optional<TSingleComponent, EcsSinglePool<TSingleComponent>>();
}

View File

@ -1,24 +1,38 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsTagPool<T> : EcsPoolBase<T>
using static EcsPoolThrowHalper;
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEnumerable<T> //IntelliSense hack
where T : struct, IEcsTagComponent
{
private EcsWorld _source;
private int _id;
private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
private int _count;
private PoolRunners _poolRunners;
private T _fakeComponent;
#region Properites
public int Count => _count;
int IEcsPool.Capacity => -1;
public int ComponentID => _id;
public Type ComponentType => typeof(T);
public EcsWorld World => _source;
#endregion
#region Init
protected override void Init(EcsWorld world)
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
{
_source = world;
_id = componentID;
_mapping = new bool[world.Capacity];
_count = 0;
@ -26,50 +40,125 @@ namespace DCFApixels.DragonECS
}
#endregion
#region Add/Has/Del
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
#region Method
public void Add(int entityID)
{
// using (_addMark.Auto())
// {
if (_mapping[entityID] == false)
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
#endif
_count++;
_mapping[entityID] = true;
this.IncrementEntityComponentCount(entityID);
_poolRunners.add.OnComponentAdd<T>(entityID);
}
public void TryAdd(int entityID)
{
if (!_mapping[entityID])
{
_count++;
_mapping[entityID] = true;
IncrementEntityComponentCount(entityID);
this.IncrementEntityComponentCount(entityID);
_poolRunners.add.OnComponentAdd<T>(entityID);
}
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
public bool Has(int entityID)
{
// using (_hasMark.Auto())
return _mapping[entityID];
}
public void Del(int entityID)
{
// using (_delMark.Auto())
// {
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
_mapping[entityID] = false;
_count--;
DecrementEntityComponentCount(entityID);
this.DecrementEntityComponentCount(entityID);
_poolRunners.del.OnComponentDel<T>(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
TryAdd(toEntityID);
}
public void Set(int entityID, bool isHas)
{
if (isHas)
{
if (!Has(entityID))
Add(entityID);
}
else
{
if (Has(entityID))
Del(entityID);
}
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
#region Callbacks
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
}
protected override void OnDestroy() { }
void IEcsPoolImplementation.OnWorldDestroy() { }
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
foreach (var entityID in buffer)
TryDel(entityID);
}
#endregion
#region Other
ref T IEcsPool<T>.Add(int entityID)
{
Add(entityID);
return ref _fakeComponent;
}
ref readonly T IEcsPool<T>.Read(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
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 (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return ref _fakeComponent;
}
void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID);
object IEcsPool.GetRaw(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
return _fakeComponent;
}
void IEcsPool.SetRaw(int entityID, object dataRaw)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
#endif
}
#endregion
#region IEnumerator - IntelliSense hack
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
}
/// <summary>Component without data</summary>
public interface IEcsTagComponent { }
public static class EcsTagPoolExt
{
@ -78,15 +167,15 @@ namespace DCFApixels.DragonECS
return self.GetPool<TTagComponent, EcsTagPool<TTagComponent>>();
}
public static EcsTagPool<TTagComponent> Include<TTagComponent>(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent
public static EcsTagPool<TTagComponent> Include<TTagComponent>(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
{
return self.Include<TTagComponent, EcsTagPool<TTagComponent>>();
}
public static EcsTagPool<TTagComponent> Exclude<TTagComponent>(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent
public static EcsTagPool<TTagComponent> Exclude<TTagComponent>(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
{
return self.Exclude<TTagComponent, EcsTagPool<TTagComponent>>();
}
public static EcsTagPool<TTagComponent> Optional<TTagComponent>(this EcsQueryBuilderBase self) where TTagComponent : struct, IEcsTagComponent
public static EcsTagPool<TTagComponent> Optional<TTagComponent>(this EcsSubjectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
{
return self.Optional<TTagComponent, EcsTagPool<TTagComponent>>();
}

View File

@ -1,16 +0,0 @@
using System;
namespace DCFApixels.DragonECS.Internal
{
public abstract class EcsRelationTableArchetypeBase
{
public EcsRelationTableArchetypeBase()
{
throw new TypeAccessException("Сreating instances of EcsRelationTableArchetype class is not available.");
}
}
public sealed class EcsRelationTableArchetype<TLeftWorld, TRightWorld> : EcsRelationTableArchetypeBase
where TLeftWorld : EcsWorld<TLeftWorld>
where TRightWorld : EcsWorld<TRightWorld>
{ }
}

View File

@ -46,10 +46,10 @@ namespace DCFApixels.DragonECS
/// <returns> new node index</returns>
public int Insert(int nodeIndex, int entityID)
{
_nodes[_count].Set(entityID, _nodes[nodeIndex].next);
_nodes[++_count].Set(entityID, _nodes[nodeIndex].next);
_nodes[nodeIndex].next = _count;
_lastNodeIndex = _count;
return _count++;
return _count;
}
public int Add(int entityID) => Insert(_lastNodeIndex, entityID);

169
src/entlong.cs Normal file
View File

@ -0,0 +1,169 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS
{
using static entlong.ThrowHalper;
// uniqueID - 32 bits
// gen - 16 bits
// world - 16 bits
/// <summary>Strong identifier/Permanent entity identifier</summary>
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)]
public readonly struct entlong : IEquatable<long>, IEquatable<entlong>
{
public static readonly entlong NULL = default;
[FieldOffset(0)]
internal readonly long full; //Union
[FieldOffset(0)]
internal readonly int id;
[FieldOffset(4)]
internal readonly short gen;
[FieldOffset(6)]
internal readonly short world;
#region Properties
public bool IsAlive
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => EcsWorld.Worlds[world].IsAlive(id, gen);
}
public bool IsNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this == NULL;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsNotNull
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this != NULL;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public int ID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return id;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public short Gen
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return gen;
}
}
public EcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return EcsWorld.Worlds[world];
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public short WorldID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (!IsAlive) ThrowIsNotAlive(this);
#endif
return world;
}
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public entlong(int id, short gen, short world) : this()
{
this.id = id;
this.gen = gen;
this.world = world;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal entlong(long full) : this()
{
this.full = full;
}
#endregion
#region TryGetters
public bool TryGetID(out int id)
{
id = this.id;
return IsAlive;
}
public bool TryGetWorld(out EcsWorld world)
{
world = EcsWorld.Worlds[this.world];
return IsAlive;
}
public bool TryUnpack(out EcsWorld world, out int id)
{
world = EcsWorld.Worlds[this.world];
id = this.id;
return IsAlive;
}
#endregion
#region Equals
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(entlong other) => full == other.full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(long other) => full == other;
#endregion
#region Object
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString() => $"entity(id:{id} g:{gen} w:{world} {(IsAlive ? "alive" : "not alive")})";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is entlong other && full == other.full;
#endregion
#region operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(in entlong a, in entlong b) => a.full == b.full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(in entlong a, in entlong b) => a.full != b.full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator long(in entlong a) => a.full;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator entlong(in long a) => new entlong(a);
#endregion
#region ThrowHalper
internal static class ThrowHalper
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowIsNotAlive(entlong entity)
{
if (entity.IsNull)
throw new EcsFrameworkException("The entity identifier is null.");
else
throw new EcsFrameworkException("The entity is not alive.");
}
}
#endregion
}
}