From 453426a232707aa2db175327a39f67f799dda03d Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Tue, 9 Aug 2022 14:41:31 -0700 Subject: [PATCH] rename and add new relation lookup methods --- src/ComponentDepot.cs | 3 + src/ComponentStorage.cs | 8 ++- src/EntityComponentReader.cs | 40 +++++++++-- src/IndexableSet.cs | 5 ++ src/RelationDepot.cs | 45 ++++++++++-- src/RelationStorage.cs | 130 ++++++++++++++++++++++++----------- src/System.cs | 5 ++ 7 files changed, 187 insertions(+), 49 deletions(-) diff --git a/src/ComponentDepot.cs b/src/ComponentDepot.cs index a5ada46..c6eca73 100644 --- a/src/ComponentDepot.cs +++ b/src/ComponentDepot.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace MoonTools.ECS { @@ -17,6 +18,7 @@ namespace MoonTools.ECS private HashSet TypesWithDisabledSerialization = new HashSet(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Register() where TComponent : unmanaged { if (!storages.ContainsKey(typeof(TComponent))) @@ -33,6 +35,7 @@ namespace MoonTools.ECS return storages[type]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private ComponentStorage Lookup() where TComponent : unmanaged { // TODO: is it possible to optimize this? diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs index c7d256b..8324558 100644 --- a/src/ComponentStorage.cs +++ b/src/ComponentStorage.cs @@ -46,7 +46,7 @@ namespace MoonTools.ECS #if DEBUG if (nextID == 0) { - throw new ArgumentOutOfRangeException("Component storage is empty!"); + throw new IndexOutOfRangeException("Component storage is empty!"); } #endif return ref components[0]; @@ -119,6 +119,12 @@ namespace MoonTools.ECS public Entity FirstEntity() { +#if DEBUG + if (nextID == 0) + { + throw new IndexOutOfRangeException("Component storage is empty!"); + } +#endif return new Entity(entityIDs[0]); } diff --git a/src/EntityComponentReader.cs b/src/EntityComponentReader.cs index 46b49f9..81b2c76 100644 --- a/src/EntityComponentReader.cs +++ b/src/EntityComponentReader.cs @@ -61,14 +61,46 @@ namespace MoonTools.ECS return RelationDepot.Related(a.ID, b.ID); } - protected IEnumerable<(Entity, TRelationKind)> RelatedToA(in Entity entity) where TRelationKind : unmanaged + // relations go A->B, so given A, will give all outgoing B relations. + protected IEnumerable<(Entity, TRelationKind)> OutRelations(in Entity entity) where TRelationKind : unmanaged { - return RelationDepot.RelatedToA(entity.ID); + return RelationDepot.OutRelations(entity.ID); } - protected IEnumerable<(Entity, TRelationKind)> RelatedToB(in Entity entity) where TRelationKind : unmanaged + protected (Entity, TRelationKind) OutRelationSingleton(in Entity entity) where TRelationKind : unmanaged { - return RelationDepot.RelatedToB(entity.ID); + return RelationDepot.OutRelationSingleton(entity.ID); + } + + protected bool HasOutRelation(in Entity entity) where TRelationKind : unmanaged + { + return RelationDepot.HasOutRelation(entity.ID); + } + + protected int OutRelationCount(in Entity entity) where TRelationKind : unmanaged + { + return RelationDepot.OutRelationCount(entity.ID); + } + + // Relations go A->B, so given B, will give all incoming A relations. + protected IEnumerable<(Entity, TRelationKind)> InRelations(in Entity entity) where TRelationKind : unmanaged + { + return RelationDepot.InRelations(entity.ID); + } + + protected (Entity, TRelationKind) InRelationSingleton(in Entity entity) where TRelationKind : unmanaged + { + return RelationDepot.InRelationSingleton(entity.ID); + } + + protected bool HasInRelation(in Entity entity) where TRelationKind : unmanaged + { + return RelationDepot.HasInRelation(entity.ID); + } + + protected int InRelationCount(in Entity entity) where TRelationKind : unmanaged + { + return RelationDepot.InRelationCount(entity.ID); } } } diff --git a/src/IndexableSet.cs b/src/IndexableSet.cs index ea54652..bacb429 100644 --- a/src/IndexableSet.cs +++ b/src/IndexableSet.cs @@ -80,6 +80,11 @@ namespace MoonTools.ECS } } + public void Clear() + { + Count = 0; + } + public void Save(IndexableSetState state) { ReadOnlySpan arrayBytes = MemoryMarshal.Cast(array); diff --git a/src/RelationDepot.cs b/src/RelationDepot.cs index c3a65b3..4b8652c 100644 --- a/src/RelationDepot.cs +++ b/src/RelationDepot.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace MoonTools.ECS { @@ -15,6 +16,7 @@ namespace MoonTools.ECS } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private RelationStorage Lookup() where TRelationKind : unmanaged { Register(); @@ -31,6 +33,11 @@ namespace MoonTools.ECS Lookup().Remove(relation); } + public void UnrelateAll(int entityID) where TRelationKind : unmanaged + { + Lookup().UnrelateAll(entityID); + } + // FIXME: optimize this public void OnEntityDestroy(int entityID) { @@ -50,14 +57,44 @@ namespace MoonTools.ECS return Lookup().Has(new Relation(idA, idB)); } - public IEnumerable<(Entity, TRelationKind)> RelatedToA(int entityID) where TRelationKind : unmanaged + public IEnumerable<(Entity, TRelationKind)> OutRelations(int entityID) where TRelationKind : unmanaged { - return Lookup().RelatedToA(entityID); + return Lookup().OutRelations(entityID); } - public IEnumerable<(Entity, TRelationKind)> RelatedToB(int entityID) where TRelationKind : unmanaged + public (Entity, TRelationKind) OutRelationSingleton(int entityID) where TRelationKind : unmanaged { - return Lookup().RelatedToB(entityID); + return Lookup().OutFirst(entityID); + } + + public int OutRelationCount(int entityID) where TRelationKind : unmanaged + { + return Lookup().OutRelationCount(entityID); + } + + public bool HasOutRelation(int entityID) where TRelationKind : unmanaged + { + return Lookup().HasOutRelation(entityID); + } + + public IEnumerable<(Entity, TRelationKind)> InRelations(int entityID) where TRelationKind : unmanaged + { + return Lookup().InRelations(entityID); + } + + public (Entity, TRelationKind) InRelationSingleton(int entityID) where TRelationKind : unmanaged + { + return Lookup().InFirst(entityID); + } + + public bool HasInRelation(int entityID) where TRelationKind : unmanaged + { + return Lookup().HasInRelation(entityID); + } + + public int InRelationCount(int entityID) where TRelationKind : unmanaged + { + return Lookup().InRelationCount(entityID); } public void Save(RelationDepotState state) diff --git a/src/RelationStorage.cs b/src/RelationStorage.cs index 7abf778..bf86f9d 100644 --- a/src/RelationStorage.cs +++ b/src/RelationStorage.cs @@ -20,9 +20,9 @@ namespace MoonTools.ECS private Dictionary indices = new Dictionary(16); private Relation[] relations = new Relation[16]; private TRelation[] relationDatas = new TRelation[16]; - private Dictionary> entitiesRelatedToA = new Dictionary>(16); - private Dictionary> entitiesRelatedToB = new Dictionary>(16); - private Stack> listPool = new Stack>(); + private Dictionary> outRelations = new Dictionary>(16); + private Dictionary> inRelations = new Dictionary>(16); + private Stack> listPool = new Stack>(); public IEnumerable<(Entity, Entity, TRelation)> All() { @@ -45,17 +45,17 @@ namespace MoonTools.ECS var idA = relation.A.ID; var idB = relation.B.ID; - if (!entitiesRelatedToA.ContainsKey(idA)) + if (!outRelations.ContainsKey(idA)) { - entitiesRelatedToA[idA] = AcquireHashSetFromPool(); + outRelations[idA] = AcquireHashSetFromPool(); } - entitiesRelatedToA[idA].Add(idB); + outRelations[idA].Add(idB); - if (!entitiesRelatedToB.ContainsKey(idB)) + if (!inRelations.ContainsKey(idB)) { - entitiesRelatedToB[idB] = AcquireHashSetFromPool(); + inRelations[idB] = AcquireHashSetFromPool(); } - entitiesRelatedToB[idB].Add(idA); + inRelations[idB].Add(idA); if (count >= relationDatas.Length) { @@ -74,12 +74,12 @@ namespace MoonTools.ECS return indices.ContainsKey(relation); } - // FIXME: is there a more descriptive name for these? - public IEnumerable<(Entity, TRelation)> RelatedToA(int entityID) + // FIXME: creating the new Relation in here is slightly deranged + public IEnumerable<(Entity, TRelation)> OutRelations(int entityID) { - if (entitiesRelatedToA.ContainsKey(entityID)) + if (outRelations.ContainsKey(entityID)) { - foreach (var id in entitiesRelatedToA[entityID]) + foreach (var id in outRelations[entityID]) { var relation = new Relation(entityID, id); yield return (relation.B, relationDatas[indices[relation]]); @@ -87,11 +87,33 @@ namespace MoonTools.ECS } } - public IEnumerable<(Entity, TRelation)> RelatedToB(int entityID) + public (Entity, TRelation) OutFirst(int entityID) { - if (entitiesRelatedToB.ContainsKey(entityID)) +#if DEBUG + if (!outRelations.ContainsKey(entityID)) { - foreach (var id in entitiesRelatedToB[entityID]) + throw new KeyNotFoundException("No out relations to this entity!"); + } +#endif + var relation = new Relation(entityID, outRelations[entityID][0]); + return (relation.B, relationDatas[indices[relation]]); + } + + public bool HasOutRelation(int entityID) + { + return outRelations.ContainsKey(entityID) && outRelations[entityID].Count > 0; + } + + public int OutRelationCount(int entityID) + { + return outRelations.ContainsKey(entityID) ? outRelations[entityID].Count : 0; + } + + public IEnumerable<(Entity, TRelation)> InRelations(int entityID) + { + if (inRelations.ContainsKey(entityID)) + { + foreach (var id in inRelations[entityID]) { var relation = new Relation(id, entityID); yield return (relation.A, relationDatas[indices[relation]]); @@ -99,16 +121,39 @@ namespace MoonTools.ECS } } + public (Entity, TRelation) InFirst(int entityID) + { +#if DEBUG + if (!inRelations.ContainsKey(entityID)) + { + throw new KeyNotFoundException("No out relations to this entity!"); + } +#endif + + var relation = new Relation(inRelations[entityID][0], entityID); + return (relation.A, relationDatas[indices[relation]]); + } + + public bool HasInRelation(int entityID) + { + return inRelations.ContainsKey(entityID) && inRelations[entityID].Count > 0; + } + + public int InRelationCount(int entityID) + { + return inRelations.ContainsKey(entityID) ? inRelations[entityID].Count : 0; + } + public bool Remove(Relation relation) { - if (entitiesRelatedToA.ContainsKey(relation.A.ID)) + if (outRelations.ContainsKey(relation.A.ID)) { - entitiesRelatedToA[relation.A.ID].Remove(relation.B.ID); + outRelations[relation.A.ID].Remove(relation.B.ID); } - if (entitiesRelatedToB.ContainsKey(relation.B.ID)) + if (inRelations.ContainsKey(relation.B.ID)) { - entitiesRelatedToB[relation.B.ID].Remove(relation.A.ID); + inRelations[relation.B.ID].Remove(relation.A.ID); } if (indices.ContainsKey(relation)) @@ -133,42 +178,47 @@ namespace MoonTools.ECS return false; } - public override void OnEntityDestroy(int entityID) + public void UnrelateAll(int entityID) { - if (entitiesRelatedToA.ContainsKey(entityID)) + if (outRelations.ContainsKey(entityID)) { - foreach (var entityB in entitiesRelatedToA[entityID]) + foreach (var entityB in outRelations[entityID]) { Remove(new Relation(entityID, entityB)); } - ReturnHashSetToPool(entitiesRelatedToA[entityID]); - entitiesRelatedToA.Remove(entityID); + ReturnHashSetToPool(outRelations[entityID]); + outRelations.Remove(entityID); } - if (entitiesRelatedToB.ContainsKey(entityID)) + if (inRelations.ContainsKey(entityID)) { - foreach (var entityA in entitiesRelatedToB[entityID]) + foreach (var entityA in inRelations[entityID]) { Remove(new Relation(entityA, entityID)); } - ReturnHashSetToPool(entitiesRelatedToB[entityID]); - entitiesRelatedToB.Remove(entityID); + ReturnHashSetToPool(inRelations[entityID]); + inRelations.Remove(entityID); } } - private HashSet AcquireHashSetFromPool() + public override void OnEntityDestroy(int entityID) + { + UnrelateAll(entityID); + } + + private IndexableSet AcquireHashSetFromPool() { if (listPool.Count == 0) { - listPool.Push(new HashSet()); + listPool.Push(new IndexableSet()); } return listPool.Pop(); } - private void ReturnHashSetToPool(HashSet hashSet) + private void ReturnHashSetToPool(IndexableSet hashSet) { hashSet.Clear(); listPool.Push(hashSet); @@ -206,24 +256,24 @@ namespace MoonTools.ECS state.RelationDatas.CopyTo(MemoryMarshal.Cast(relationDatas)); indices.Clear(); - entitiesRelatedToA.Clear(); - entitiesRelatedToB.Clear(); + outRelations.Clear(); + inRelations.Clear(); for (var i = 0; i < state.Count; i += 1) { var relation = relations[i]; indices[relation] = i; - if (!entitiesRelatedToA.ContainsKey(relation.A.ID)) + if (!outRelations.ContainsKey(relation.A.ID)) { - entitiesRelatedToA[relation.A.ID] = AcquireHashSetFromPool(); + outRelations[relation.A.ID] = AcquireHashSetFromPool(); } - entitiesRelatedToA[relation.A.ID].Add(relation.B.ID); + outRelations[relation.A.ID].Add(relation.B.ID); - if (!entitiesRelatedToB.ContainsKey(relation.B.ID)) + if (!inRelations.ContainsKey(relation.B.ID)) { - entitiesRelatedToB[relation.B.ID] = AcquireHashSetFromPool(); + inRelations[relation.B.ID] = AcquireHashSetFromPool(); } - entitiesRelatedToB[relation.B.ID].Add(relation.A.ID); + inRelations[relation.B.ID].Add(relation.A.ID); } count = state.Count; diff --git a/src/System.cs b/src/System.cs index 1fd33d4..1a993ec 100644 --- a/src/System.cs +++ b/src/System.cs @@ -78,6 +78,11 @@ namespace MoonTools.ECS RelationDepot.Remove(new Relation(entityA, entityB)); } + protected void UnrelateAll(in Entity entity) where TRelationKind : unmanaged + { + RelationDepot.UnrelateAll(entity.ID); + } + // FIXME: this is insanely inefficient protected void Destroy(in Entity entity) {