rename and add new relation lookup methods

filter_relations
cosmonaut 2022-08-09 14:41:31 -07:00
parent 5d3989a620
commit 453426a232
7 changed files with 187 additions and 49 deletions

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
@ -17,6 +18,7 @@ namespace MoonTools.ECS
private HashSet<Type> TypesWithDisabledSerialization = new HashSet<Type>(); private HashSet<Type> TypesWithDisabledSerialization = new HashSet<Type>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Register<TComponent>() where TComponent : unmanaged internal void Register<TComponent>() where TComponent : unmanaged
{ {
if (!storages.ContainsKey(typeof(TComponent))) if (!storages.ContainsKey(typeof(TComponent)))
@ -33,6 +35,7 @@ namespace MoonTools.ECS
return storages[type]; return storages[type];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : unmanaged private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : unmanaged
{ {
// TODO: is it possible to optimize this? // TODO: is it possible to optimize this?

View File

@ -46,7 +46,7 @@ namespace MoonTools.ECS
#if DEBUG #if DEBUG
if (nextID == 0) if (nextID == 0)
{ {
throw new ArgumentOutOfRangeException("Component storage is empty!"); throw new IndexOutOfRangeException("Component storage is empty!");
} }
#endif #endif
return ref components[0]; return ref components[0];
@ -119,6 +119,12 @@ namespace MoonTools.ECS
public Entity FirstEntity() public Entity FirstEntity()
{ {
#if DEBUG
if (nextID == 0)
{
throw new IndexOutOfRangeException("Component storage is empty!");
}
#endif
return new Entity(entityIDs[0]); return new Entity(entityIDs[0]);
} }

View File

@ -61,14 +61,46 @@ namespace MoonTools.ECS
return RelationDepot.Related<TRelationKind>(a.ID, b.ID); return RelationDepot.Related<TRelationKind>(a.ID, b.ID);
} }
protected IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(in Entity entity) where TRelationKind : unmanaged // relations go A->B, so given A, will give all outgoing B relations.
protected IEnumerable<(Entity, TRelationKind)> OutRelations<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{ {
return RelationDepot.RelatedToA<TRelationKind>(entity.ID); return RelationDepot.OutRelations<TRelationKind>(entity.ID);
} }
protected IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(in Entity entity) where TRelationKind : unmanaged protected (Entity, TRelationKind) OutRelationSingleton<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{ {
return RelationDepot.RelatedToB<TRelationKind>(entity.ID); return RelationDepot.OutRelationSingleton<TRelationKind>(entity.ID);
}
protected bool HasOutRelation<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{
return RelationDepot.HasOutRelation<TRelationKind>(entity.ID);
}
protected int OutRelationCount<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{
return RelationDepot.OutRelationCount<TRelationKind>(entity.ID);
}
// Relations go A->B, so given B, will give all incoming A relations.
protected IEnumerable<(Entity, TRelationKind)> InRelations<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{
return RelationDepot.InRelations<TRelationKind>(entity.ID);
}
protected (Entity, TRelationKind) InRelationSingleton<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{
return RelationDepot.InRelationSingleton<TRelationKind>(entity.ID);
}
protected bool HasInRelation<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{
return RelationDepot.HasInRelation<TRelationKind>(entity.ID);
}
protected int InRelationCount<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{
return RelationDepot.InRelationCount<TRelationKind>(entity.ID);
} }
} }
} }

View File

@ -80,6 +80,11 @@ namespace MoonTools.ECS
} }
} }
public void Clear()
{
Count = 0;
}
public void Save(IndexableSetState<T> state) public void Save(IndexableSetState<T> state)
{ {
ReadOnlySpan<byte> arrayBytes = MemoryMarshal.Cast<T, byte>(array); ReadOnlySpan<byte> arrayBytes = MemoryMarshal.Cast<T, byte>(array);

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
@ -15,6 +16,7 @@ namespace MoonTools.ECS
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private RelationStorage<TRelationKind> Lookup<TRelationKind>() where TRelationKind : unmanaged private RelationStorage<TRelationKind> Lookup<TRelationKind>() where TRelationKind : unmanaged
{ {
Register<TRelationKind>(); Register<TRelationKind>();
@ -31,6 +33,11 @@ namespace MoonTools.ECS
Lookup<TRelationKind>().Remove(relation); Lookup<TRelationKind>().Remove(relation);
} }
public void UnrelateAll<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
Lookup<TRelationKind>().UnrelateAll(entityID);
}
// FIXME: optimize this // FIXME: optimize this
public void OnEntityDestroy(int entityID) public void OnEntityDestroy(int entityID)
{ {
@ -50,14 +57,44 @@ namespace MoonTools.ECS
return Lookup<TRelationKind>().Has(new Relation(idA, idB)); return Lookup<TRelationKind>().Has(new Relation(idA, idB));
} }
public IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(int entityID) where TRelationKind : unmanaged public IEnumerable<(Entity, TRelationKind)> OutRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged
{ {
return Lookup<TRelationKind>().RelatedToA(entityID); return Lookup<TRelationKind>().OutRelations(entityID);
} }
public IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(int entityID) where TRelationKind : unmanaged public (Entity, TRelationKind) OutRelationSingleton<TRelationKind>(int entityID) where TRelationKind : unmanaged
{ {
return Lookup<TRelationKind>().RelatedToB(entityID); return Lookup<TRelationKind>().OutFirst(entityID);
}
public int OutRelationCount<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().OutRelationCount(entityID);
}
public bool HasOutRelation<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().HasOutRelation(entityID);
}
public IEnumerable<(Entity, TRelationKind)> InRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().InRelations(entityID);
}
public (Entity, TRelationKind) InRelationSingleton<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().InFirst(entityID);
}
public bool HasInRelation<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().HasInRelation(entityID);
}
public int InRelationCount<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().InRelationCount(entityID);
} }
public void Save(RelationDepotState state) public void Save(RelationDepotState state)

View File

@ -20,9 +20,9 @@ namespace MoonTools.ECS
private Dictionary<Relation, int> indices = new Dictionary<Relation, int>(16); private Dictionary<Relation, int> indices = new Dictionary<Relation, int>(16);
private Relation[] relations = new Relation[16]; private Relation[] relations = new Relation[16];
private TRelation[] relationDatas = new TRelation[16]; private TRelation[] relationDatas = new TRelation[16];
private Dictionary<int, HashSet<int>> entitiesRelatedToA = new Dictionary<int, HashSet<int>>(16); private Dictionary<int, IndexableSet<int>> outRelations = new Dictionary<int, IndexableSet<int>>(16);
private Dictionary<int, HashSet<int>> entitiesRelatedToB = new Dictionary<int, HashSet<int>>(16); private Dictionary<int, IndexableSet<int>> inRelations = new Dictionary<int, IndexableSet<int>>(16);
private Stack<HashSet<int>> listPool = new Stack<HashSet<int>>(); private Stack<IndexableSet<int>> listPool = new Stack<IndexableSet<int>>();
public IEnumerable<(Entity, Entity, TRelation)> All() public IEnumerable<(Entity, Entity, TRelation)> All()
{ {
@ -45,17 +45,17 @@ namespace MoonTools.ECS
var idA = relation.A.ID; var idA = relation.A.ID;
var idB = relation.B.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) if (count >= relationDatas.Length)
{ {
@ -74,12 +74,12 @@ namespace MoonTools.ECS
return indices.ContainsKey(relation); return indices.ContainsKey(relation);
} }
// FIXME: is there a more descriptive name for these? // FIXME: creating the new Relation in here is slightly deranged
public IEnumerable<(Entity, TRelation)> RelatedToA(int entityID) 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); var relation = new Relation(entityID, id);
yield return (relation.B, relationDatas[indices[relation]]); 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); var relation = new Relation(id, entityID);
yield return (relation.A, relationDatas[indices[relation]]); 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) 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)) if (indices.ContainsKey(relation))
@ -133,42 +178,47 @@ namespace MoonTools.ECS
return false; 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)); Remove(new Relation(entityID, entityB));
} }
ReturnHashSetToPool(entitiesRelatedToA[entityID]); ReturnHashSetToPool(outRelations[entityID]);
entitiesRelatedToA.Remove(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)); Remove(new Relation(entityA, entityID));
} }
ReturnHashSetToPool(entitiesRelatedToB[entityID]); ReturnHashSetToPool(inRelations[entityID]);
entitiesRelatedToB.Remove(entityID); inRelations.Remove(entityID);
} }
} }
private HashSet<int> AcquireHashSetFromPool() public override void OnEntityDestroy(int entityID)
{
UnrelateAll(entityID);
}
private IndexableSet<int> AcquireHashSetFromPool()
{ {
if (listPool.Count == 0) if (listPool.Count == 0)
{ {
listPool.Push(new HashSet<int>()); listPool.Push(new IndexableSet<int>());
} }
return listPool.Pop(); return listPool.Pop();
} }
private void ReturnHashSetToPool(HashSet<int> hashSet) private void ReturnHashSetToPool(IndexableSet<int> hashSet)
{ {
hashSet.Clear(); hashSet.Clear();
listPool.Push(hashSet); listPool.Push(hashSet);
@ -206,24 +256,24 @@ namespace MoonTools.ECS
state.RelationDatas.CopyTo(MemoryMarshal.Cast<TRelation, byte>(relationDatas)); state.RelationDatas.CopyTo(MemoryMarshal.Cast<TRelation, byte>(relationDatas));
indices.Clear(); indices.Clear();
entitiesRelatedToA.Clear(); outRelations.Clear();
entitiesRelatedToB.Clear(); inRelations.Clear();
for (var i = 0; i < state.Count; i += 1) for (var i = 0; i < state.Count; i += 1)
{ {
var relation = relations[i]; var relation = relations[i];
indices[relation] = 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; count = state.Count;

View File

@ -78,6 +78,11 @@ namespace MoonTools.ECS
RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB)); RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB));
} }
protected void UnrelateAll<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
{
RelationDepot.UnrelateAll<TRelationKind>(entity.ID);
}
// FIXME: this is insanely inefficient // FIXME: this is insanely inefficient
protected void Destroy(in Entity entity) protected void Destroy(in Entity entity)
{ {