From b5f134845ddc776842942cb2f3b513ab427c0369 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 8 Apr 2023 05:50:44 +0800 Subject: [PATCH] rework identifier implementation --- src/Builtin/WorldRunners.cs | 8 +-- src/EcsGroup.cs | 8 +-- src/EcsQueryMember.cs | 56 +++++------------ src/EcsTable.cs | 35 +++++++++++ src/EcsWorld.cs | 16 ++--- src/{ent.cs => Entities/EcsEntity.cs} | 87 +++++++++++---------------- src/Entities/EcsRelation.cs | 74 +++++++++++++++++++++++ src/Entities/ent.cs | 28 +++++++++ src/Utils/ITabelRecord.cs | 2 +- src/rel.cs | 36 ----------- 10 files changed, 205 insertions(+), 145 deletions(-) create mode 100644 src/EcsTable.cs rename src/{ent.cs => Entities/EcsEntity.cs} (64%) create mode 100644 src/Entities/EcsRelation.cs create mode 100644 src/Entities/ent.cs delete mode 100644 src/rel.cs diff --git a/src/Builtin/WorldRunners.cs b/src/Builtin/WorldRunners.cs index 5687b75..79271e1 100644 --- a/src/Builtin/WorldRunners.cs +++ b/src/Builtin/WorldRunners.cs @@ -38,23 +38,23 @@ public interface IEcsEntityCreate : IEcsSystem { - public void OnEntityCreate(ent entity); + public void OnEntityCreate(EcsEntity entity); } public interface IEcsEntityDestroy : IEcsSystem { - public void OnEntityDestroy(ent entity); + public void OnEntityDestroy(EcsEntity entity); } public interface IEcsEntityLifecycle : IEcsEntityCreate, IEcsEntityDestroy { } public sealed class EcsEntityCreateRunner : EcsRunner, IEcsEntityCreate { - public void OnEntityCreate(ent entity) + public void OnEntityCreate(EcsEntity entity) { foreach (var item in targets) item.OnEntityCreate(entity); } } public sealed class EcsEntityDestroyRunner : EcsRunner, IEcsEntityDestroy { - public void OnEntityDestroy(ent entity) + public void OnEntityDestroy(EcsEntity entity) { foreach (var item in targets) item.OnEntityDestroy(entity); } diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 6387d69..657c816 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -5,8 +5,6 @@ using delayedOp = System.Int32; namespace DCFApixels.DragonECS { - - [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)] public readonly ref struct EcsReadonlyGroup { @@ -232,9 +230,9 @@ namespace DCFApixels.DragonECS { delayedOp op = _delayedOps[i]; if (op >= 0) //delayedOp.IsAdded - UncheckedAdd(op & int.MaxValue); //delayedOp.Entity + UncheckedAdd(op & int.MaxValue); //delayedOp.EcsEntity else - UncheckedRemove(op & int.MaxValue); //delayedOp.Entity + UncheckedRemove(op & int.MaxValue); //delayedOp.EcsEntity } } } @@ -266,7 +264,7 @@ namespace DCFApixels.DragonECS public ent Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _source.World.GetEntity(_dense[_index]); + get => new ent(_dense[_index]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => ++_index <= _count && _count<_dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны diff --git a/src/EcsQueryMember.cs b/src/EcsQueryMember.cs index e24bd7d..4053222 100644 --- a/src/EcsQueryMember.cs +++ b/src/EcsQueryMember.cs @@ -8,10 +8,10 @@ namespace DCFApixels.DragonECS public interface IEcsQueryMember where TComponent : struct { - public ref TComponent Write(int entityID); - public ref readonly TComponent Read(int entityID); - public bool Has(int entityID); - public void Del(int entityID); + public ref TComponent Write(ent entityID); + public ref readonly TComponent Read(ent entityID); + public bool Has(ent entityID); + public void Del(ent entityID); } [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] @@ -22,21 +22,13 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] internal inc(EcsPool pool) => _pool = pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(ent entity) => ref _pool.Write(entity.id); + public ref TComponent Write(ent entityID) => ref _pool.Write(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(int entityID) => ref _pool.Write(entityID); + public ref readonly TComponent Read(ent entityID) => ref _pool.Read(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly TComponent Read(ent entity) => ref _pool.Read(entity.id); + public bool Has(ent entityID) => _pool.Has(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly TComponent Read(int entityID) => ref _pool.Read(entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(ent entity) => _pool.Has(entity.id); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(int entityID) => _pool.Has(entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(ent entity) => _pool.Del(entity.id); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(int entityID) => _pool.Del(entityID); + public void Del(ent entityID) => _pool.Del(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => $"{(_pool == null ? "NULL" : _pool.World.ArchetypeType.Name)}inc<{typeof(TComponent).Name}>"; @@ -52,21 +44,13 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] internal exc(EcsPool pool) => _pool = pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(ent entity) => ref _pool.Write(entity.id); + public ref TComponent Write(ent entityID) => ref _pool.Write(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(int entityID) => ref _pool.Write(entityID); + public ref readonly TComponent Read(ent entityID) => ref _pool.Read(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly TComponent Read(ent entity) => ref _pool.Read(entity.id); + public bool Has(ent entityID) => _pool.Has(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly TComponent Read(int entityID) => ref _pool.Read(entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(ent entity) => _pool.Has(entity.id); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(int entityID) => _pool.Has(entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(ent entity) => _pool.Del(entity.id); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(int entityID) => _pool.Del(entityID); + public void Del(ent entityID) => _pool.Del(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => $"{(_pool == null ? "NULL" : _pool.World.ArchetypeType.Name)}exc<{typeof(TComponent).Name}>"; @@ -82,21 +66,13 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] internal opt(EcsPool pool) => _pool = pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(ent entity) => ref _pool.Write(entity.id); + public ref TComponent Write(ent entityID) => ref _pool.Write(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(int entityID) => ref _pool.Write(entityID); + public ref readonly TComponent Read(ent entityID) => ref _pool.Read(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly TComponent Read(ent entity) => ref _pool.Read(entity.id); + public bool Has(ent entityID) => _pool.Has(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly TComponent Read(int entityID) => ref _pool.Read(entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(ent entity) => _pool.Has(entity.id); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(int entityID) => _pool.Has(entityID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(ent entity) => _pool.Del(entity.id); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(int entityID) => _pool.Del(entityID); + public void Del(ent entityID) => _pool.Del(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => $"{(_pool == null ? "NULL" : _pool.World.ArchetypeType.Name)}opt<{typeof(TComponent).Name}>"; diff --git a/src/EcsTable.cs b/src/EcsTable.cs new file mode 100644 index 0000000..469070a --- /dev/null +++ b/src/EcsTable.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DCFApixels.DragonECS +{ + public class EcsTable + { + private IEcsPool[] _pools; + private EcsNullPool _nullPool; + + private int[] _denseEntities; + private int[] _sparceEntities; + private int _entitiesCount; + + private List[] _filtersByIncludedComponents; + private List[] _filtersByExcludedComponents; + + private EcsQueryBase[] _queries; + + private List _groups; + + #region Internal Properties + public int Count => _entitiesCount; + public int Capacity => _denseEntities.Length; + #endregion + + public ReadOnlySpan GetAllPools() => new ReadOnlySpan(_pools); + //public int GetComponentID() => ; + } + + +} diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 115d097..abf2751 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -18,10 +18,10 @@ namespace DCFApixels.DragonECS #endregion #region Entities - public ent NewEntity(); - public void DelEntity(ent entity); + public EcsEntity NewEntity(); + public void DelEntity(EcsEntity entity); public bool EntityIsAlive(int entityID, short gen); - public ent GetEntity(int entityID); + public EcsEntity GetEntity(int entityID); public void Destroy(); #endregion } @@ -344,7 +344,7 @@ namespace DCFApixels.DragonECS #endregion #region Entity - public ent NewEntity() + public EcsEntity NewEntity() { int entityID = _entityDispenser.GetFree(); if (_entityDispenser.LastInt >= _denseEntities.Length) @@ -360,11 +360,11 @@ namespace DCFApixels.DragonECS item.OnWorldResize(_gens.Length); } _gens[entityID] |= short.MinValue; - ent entity = new ent(entityID, _gens[entityID]++, id); + EcsEntity entity = new EcsEntity(entityID, _gens[entityID]++, id); _entityCreate.OnEntityCreate(entity); return entity; } - public void DelEntity(ent entity) + public void DelEntity(EcsEntity entity) { _entityDispenser.Release(entity.id); _gens[entity.id] |= short.MinValue; @@ -373,9 +373,9 @@ namespace DCFApixels.DragonECS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ent GetEntity(int entityID) + public EcsEntity GetEntity(int entityID) { - return new ent(entityID, _gens[entityID], id); + return new EcsEntity(entityID, _gens[entityID], id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EntityIsAlive(int entityID, short gen) diff --git a/src/ent.cs b/src/Entities/EcsEntity.cs similarity index 64% rename from src/ent.cs rename to src/Entities/EcsEntity.cs index c5aa14d..4063ecb 100644 --- a/src/ent.cs +++ b/src/Entities/EcsEntity.cs @@ -1,28 +1,18 @@ using System; -using System.ComponentModel; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { - public interface IEntityRecord : ITabelRecord - { - public bool IsSpecific { get; } - internal long Full { get; } - public short Gen { get; } - public short World { get; } - } - // uniqueID - 32 bits - // gen - 16 bits - // world - 16 bits - [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] - public readonly partial struct ent : - IEquatable, - IEquatable, - IEntityRecord - { - public static readonly ent NULL = default; + /// Permanent relation entity identifier + [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] + public readonly partial struct EcsEntity : IEquatable, IEquatable + { + public static readonly EcsEntity NULL = default; + // uniqueID - 32 bits + // gen - 16 bits + // world - 16 bits [FieldOffset(0)] internal readonly long full; //Union [FieldOffset(3)] @@ -32,69 +22,64 @@ namespace DCFApixels.DragonECS [FieldOffset(0)] public readonly short world; - #region IEntityRecord - bool IEntityRecord.IsSpecific => false; - long IEntityRecord.Full => full; - int ITabelRecord.Id => id; - short IEntityRecord.Gen => gen; - short IEntityRecord.World => world; - #endregion + public ent Ent + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new ent(id); + } #region Constructors [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ent(int id, short gen, short world) : this() + public EcsEntity(int id, short gen, short world) : this() { this.id = id; this.gen = gen; this.world = world; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ent(long full) : this() + internal EcsEntity(long full) : this() { this.full = full; } #endregion - #region GetHashCode - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => unchecked((int)(full)) ^ (int)(full >> 32); - #endregion - #region Equals [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) => obj is ent other && full == other.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ent other) => full == other.full; + public bool Equals(EcsEntity other) => full == other.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(long other) => full == other; #endregion - #region ToString + #region Object [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => $"ent(uniqueID:{id} gen:{gen} world:{world})"; + public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() => $"Entity(id:{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 ent a, in ent b) => a.full == b.full; + public static bool operator ==(in EcsEntity a, in EcsEntity b) => a.full == b.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(in ent a, in ent b) => a.full != b.full; + public static bool operator !=(in EcsEntity a, in EcsEntity b) => a.full != b.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator long(in ent a) => a.full; + public static explicit operator long(in EcsEntity a) => a.full; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator int(in ent a) => a.id; + public static explicit operator ent(in EcsEntity a) => a.Ent; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator ent(in long a) => new ent(a); + public static explicit operator EcsEntity(in long a) => new EcsEntity(a); #endregion } - public readonly partial struct ent + public readonly partial struct EcsEntity { - private static EcsProfilerMarker _IsNullMarker = new EcsProfilerMarker("ent.IsNull"); - private static EcsProfilerMarker _ReadMarker = new EcsProfilerMarker("ent.Read"); - private static EcsProfilerMarker _WriteMarker = new EcsProfilerMarker("ent.Write"); - private static EcsProfilerMarker _HasMarker = new EcsProfilerMarker("ent.Has"); - private static EcsProfilerMarker _DelMarker = new EcsProfilerMarker("ent.Del"); + private static EcsProfilerMarker _IsNullMarker = new EcsProfilerMarker("EcsEntity.IsNull"); + private static EcsProfilerMarker _ReadMarker = new EcsProfilerMarker("EcsEntity.Read"); + private static EcsProfilerMarker _WriteMarker = new EcsProfilerMarker("EcsEntity.Write"); + private static EcsProfilerMarker _HasMarker = new EcsProfilerMarker("EcsEntity.Has"); + private static EcsProfilerMarker _DelMarker = new EcsProfilerMarker("EcsEntity.Del"); public bool IsNull { @@ -140,15 +125,15 @@ namespace DCFApixels.DragonECS public static partial class entExtensions { - private static EcsProfilerMarker _IsAliveMarker = new EcsProfilerMarker("ent.IsAlive"); + private static EcsProfilerMarker _IsAliveMarker = new EcsProfilerMarker("EcsEntity.IsAlive"); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAlive(this ref ent self) + public static bool IsAlive(this ref EcsEntity self) { //using (_IsAliveMarker.Auto()) //{ bool result = EcsWorld.Worlds[self.world].EntityIsAlive(self.id, self.gen); - if (!result) self = ent.NULL; + if (!result) self = EcsEntity.NULL; return result; //} } diff --git a/src/Entities/EcsRelation.cs b/src/Entities/EcsRelation.cs new file mode 100644 index 0000000..ed55ff2 --- /dev/null +++ b/src/Entities/EcsRelation.cs @@ -0,0 +1,74 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS +{ + /// Permanent relation entity identifier + [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] + public readonly partial struct EcsRelation : IEquatable, IEquatable + { + public static readonly EcsEntity NULL = default; + // uniqueID - 32 bits + // gen - 16 bits + // world - 16 bits + [FieldOffset(0)] + internal readonly long full; //Union + [FieldOffset(3)] + public readonly int id; + [FieldOffset(1)] + public readonly short rightWorld; + [FieldOffset(0)] + public readonly short leftWorld; + + public ent Ent + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new ent(id); + } + + #region Constructors + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsRelation(int id, short leftWorld, short rightWorld) : this() + { + this.id = id; + this.leftWorld = leftWorld; + this.rightWorld = rightWorld; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsRelation(long full) : this() + { + this.full = full; + } + #endregion + + #region Equals + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(EcsRelation 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() => $"Relation(id:{id} leftWorld:{leftWorld} rightWorld:{rightWorld})"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) => obj is EcsRelation other && full == other.full; + #endregion + + #region operators + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(in EcsRelation a, in EcsRelation b) => a.full == b.full; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(in EcsRelation a, in EcsRelation b) => a.full != b.full; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator long(in EcsRelation a) => a.full; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator ent(in EcsRelation a) => a.Ent; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator EcsRelation(in long a) => new EcsRelation(a); + #endregion + } +} diff --git a/src/Entities/ent.cs b/src/Entities/ent.cs new file mode 100644 index 0000000..da30841 --- /dev/null +++ b/src/Entities/ent.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS +{ +#pragma warning disable CS0660, CS0661 + /// Single frame entity identifier + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)] + public readonly ref partial struct ent + { + internal readonly int id; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ent(int id) => this.id = id; + + public static explicit operator ent(int id) => new ent(id); + public static explicit operator int(ent entityID) => entityID.id; + + public static bool operator ==(ent a, ent b) => a.id == b.id; + public static bool operator !=(ent a, ent b) => a.id != b.id; + + public static bool operator ==(ent a, Null? _) => a.id == 0; + public static bool operator ==(Null? _, ent b) => b.id == 0; + public static bool operator !=(ent a, Null? _) => a.id != 0; + public static bool operator !=(Null? _, ent b) => b.id != 0; + + public struct Null { } + } +} diff --git a/src/Utils/ITabelRecord.cs b/src/Utils/ITabelRecord.cs index e7006d6..d2f9b15 100644 --- a/src/Utils/ITabelRecord.cs +++ b/src/Utils/ITabelRecord.cs @@ -2,7 +2,7 @@ { public interface ITabelRecord { - //TODO rename to index; Так ent по определению станет идентификатором + //TODO rename to index; Так EcsEntity по определению станет идентификатором public int Id { get; } } } diff --git a/src/rel.cs b/src/rel.cs deleted file mode 100644 index 580b1d8..0000000 --- a/src/rel.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DCFApixels.DragonECS.TODO -{ - //TODO реализовать систему отношений. - //идея есть миры-коннекторы, они являются наборами сущьностей-отношений - //сущьности отношения, это теже сущьности но их полный идентификатор - это 32 бита - одна сущьность и 32 - другая - //айди миров левой и правой части записываются в мир-коннектор и для конвертации айди сущьности в полный идентификатор, надо взять данные из него. - //Проблема: если хранить айди мира для левой части отношения в одном месте, а для правого в другом и только раграничивать на лево и право будет проблема с тем что связи работают только в одном направлении - //в этом мире вообще не будет вестиь учет гена, потому что для отношений он не нужен - - //миры коннекторы можно назвать Relations или RealtionTable - - - - [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)] - public readonly ref partial struct rel - { - public readonly int id; - public readonly short leftWorld; - public readonly short rightWorld; - - #region Constructors - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public rel(int id, short leftWorld, short rightWorld) - { - this.id = id; - this.leftWorld = leftWorld; - this.rightWorld = rightWorld; - } - #endregion - } -}