MoonTools.ECS/src/RelationStorage.cs

278 lines
6.7 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
2022-04-08 05:52:03 +00:00
2022-04-06 19:53:50 +00:00
namespace MoonTools.ECS
{
2022-04-19 19:35:21 +00:00
internal abstract class RelationStorage
2022-04-06 19:53:50 +00:00
{
2022-12-07 21:53:23 +00:00
public abstract unsafe void Set(int entityA, int entityB, void* relationData);
2022-12-13 08:34:35 +00:00
public abstract int GetStorageIndex(int entityA, int entityB);
2022-12-07 21:53:23 +00:00
public abstract unsafe void* Get(int relationStorageIndex);
2022-12-03 07:43:54 +00:00
public abstract void UnrelateAll(int entityID);
2022-12-13 08:34:35 +00:00
public abstract ReverseSpanEnumerator<Entity> OutRelations(int entityID);
2022-12-07 06:06:02 +00:00
public abstract RelationStorage CreateStorage();
public abstract void Clear();
2022-04-19 19:35:21 +00:00
}
// Relation is the two entities, A related to B.
// TRelation is the data attached to the relation.
internal class RelationStorage<TRelation> : RelationStorage where TRelation : unmanaged
2022-04-19 19:35:21 +00:00
{
private int count = 0;
private Dictionary<Relation, int> indices = new Dictionary<Relation, int>(16);
private Relation[] relations = new Relation[16];
private TRelation[] relationDatas = new TRelation[16];
2022-12-13 08:34:35 +00:00
private Dictionary<int, IndexableSet<Entity>> outRelations = new Dictionary<int, IndexableSet<Entity>>(16);
private Dictionary<int, IndexableSet<Entity>> inRelations = new Dictionary<int, IndexableSet<Entity>>(16);
private Stack<IndexableSet<Entity>> listPool = new Stack<IndexableSet<Entity>>();
2022-04-06 19:53:50 +00:00
2022-04-19 19:35:21 +00:00
public IEnumerable<(Entity, Entity, TRelation)> All()
2022-04-06 19:53:50 +00:00
{
for (var i = 0; i < count; i += 1)
2022-04-06 19:53:50 +00:00
{
var relation = relations[i];
yield return (relation.A, relation.B, relationDatas[i]);
2022-04-06 19:53:50 +00:00
}
}
public void Set(Relation relation, TRelation relationData)
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
if (indices.TryGetValue(relation, out var index))
{
relationDatas[index] = relationData;
return;
}
2022-04-07 03:08:28 +00:00
2022-04-06 19:53:50 +00:00
var idA = relation.A.ID;
var idB = relation.B.ID;
if (!outRelations.ContainsKey(idA))
2022-04-06 19:53:50 +00:00
{
outRelations[idA] = AcquireHashSetFromPool();
2022-04-06 19:53:50 +00:00
}
outRelations[idA].Add(idB);
2022-04-06 19:53:50 +00:00
if (!inRelations.ContainsKey(idB))
2022-04-06 19:53:50 +00:00
{
inRelations[idB] = AcquireHashSetFromPool();
2022-04-06 19:53:50 +00:00
}
inRelations[idB].Add(idA);
2022-04-06 19:53:50 +00:00
if (count >= relationDatas.Length)
{
2022-06-23 00:05:33 +00:00
Array.Resize(ref relations, relations.Length * 2);
Array.Resize(ref relationDatas, relationDatas.Length * 2);
}
relations[count] = relation;
relationDatas[count] = relationData;
indices.Add(relation, count);
count += 1;
2022-04-06 19:53:50 +00:00
}
2022-12-13 08:34:35 +00:00
public TRelation Get(Relation relation)
{
return relationDatas[indices[relation]];
}
2022-04-07 03:08:28 +00:00
public bool Has(Relation relation)
{
return indices.ContainsKey(relation);
2022-04-07 03:08:28 +00:00
}
2022-12-13 08:34:35 +00:00
public override ReverseSpanEnumerator<Entity> OutRelations(int entityID)
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
if (outRelations.TryGetValue(entityID, out var entityOutRelations))
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
return entityOutRelations.GetEnumerator();
}
else
{
return ReverseSpanEnumerator<Entity>.Empty;
2022-04-06 19:53:50 +00:00
}
}
2022-12-13 08:34:35 +00:00
public Entity OutFirst(int entityID)
2022-04-06 19:53:50 +00:00
{
#if DEBUG
2022-12-13 08:34:35 +00:00
if (!outRelations.ContainsKey(entityID) || outRelations[entityID].Count == 0)
2022-04-06 19:53:50 +00:00
{
throw new KeyNotFoundException("No out relations to this entity!");
}
#endif
2022-12-13 08:34:35 +00:00
return outRelations[entityID][0];
}
public bool HasOutRelation(int entityID)
{
return outRelations.ContainsKey(entityID) && outRelations[entityID].Count > 0;
}
public int OutRelationCount(int entityID)
{
2022-12-13 08:34:35 +00:00
return outRelations.TryGetValue(entityID, out var entityOutRelations) ? entityOutRelations.Count : 0;
}
2022-12-13 08:34:35 +00:00
public ReverseSpanEnumerator<Entity> InRelations(int entityID)
{
2022-12-13 08:34:35 +00:00
if (inRelations.TryGetValue(entityID, out var entityInRelations))
{
2022-12-13 08:34:35 +00:00
return entityInRelations.GetEnumerator();
}
else
{
return ReverseSpanEnumerator<Entity>.Empty;
2022-04-06 19:53:50 +00:00
}
}
2022-12-13 08:34:35 +00:00
public Entity InFirst(int entityID)
{
#if DEBUG
2022-12-13 08:34:35 +00:00
if (!inRelations.ContainsKey(entityID) || inRelations[entityID].Count == 0)
{
throw new KeyNotFoundException("No out relations to this entity!");
}
#endif
2022-12-13 08:34:35 +00:00
return inRelations[entityID][0];
}
public bool HasInRelation(int entityID)
{
return inRelations.ContainsKey(entityID) && inRelations[entityID].Count > 0;
}
public int InRelationCount(int entityID)
{
2022-12-13 08:34:35 +00:00
return inRelations.TryGetValue(entityID, out var entityInRelations) ? entityInRelations.Count : 0;
}
2022-12-03 07:43:54 +00:00
public (bool, bool) Remove(Relation relation)
2022-04-06 19:53:50 +00:00
{
2022-12-03 07:43:54 +00:00
var aEmpty = false;
var bEmpty = false;
2022-12-13 08:34:35 +00:00
if (outRelations.TryGetValue(relation.A.ID, out var entityOutRelations))
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
entityOutRelations.Remove(relation.B.ID);
2022-12-03 07:43:54 +00:00
if (outRelations[relation.A.ID].Count == 0)
{
aEmpty = true;
}
2022-04-06 19:53:50 +00:00
}
2022-12-13 08:34:35 +00:00
if (inRelations.TryGetValue(relation.B.ID, out var entityInRelations))
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
entityInRelations.Remove(relation.A.ID);
2022-12-03 07:43:54 +00:00
if (inRelations[relation.B.ID].Count == 0)
{
bEmpty = true;
}
2022-04-06 19:53:50 +00:00
}
2022-12-13 08:34:35 +00:00
if (indices.TryGetValue(relation, out var index))
{
var lastElementIndex = count - 1;
// move an element into the hole
if (index != lastElementIndex)
{
var lastRelation = relations[lastElementIndex];
indices[lastRelation] = index;
relationDatas[index] = relationDatas[lastElementIndex];
relations[index] = lastRelation;
}
count -= 1;
indices.Remove(relation);
}
2022-12-03 07:43:54 +00:00
return (aEmpty, bEmpty);
2022-04-06 19:53:50 +00:00
}
2022-12-13 08:34:35 +00:00
private IndexableSet<Entity> AcquireHashSetFromPool()
2022-12-07 06:06:02 +00:00
{
if (listPool.Count == 0)
{
2022-12-13 08:34:35 +00:00
listPool.Push(new IndexableSet<Entity>());
2022-12-07 06:06:02 +00:00
}
return listPool.Pop();
}
2022-12-13 08:34:35 +00:00
private void ReturnHashSetToPool(IndexableSet<Entity> hashSet)
2022-12-07 06:06:02 +00:00
{
hashSet.Clear();
listPool.Push(hashSet);
}
// untyped methods used for internal implementation
2022-12-07 21:53:23 +00:00
public override unsafe void Set(int entityA, int entityB, void* relationData)
2022-12-07 06:06:02 +00:00
{
2022-12-07 21:53:23 +00:00
Set(new Relation(entityA, entityB), *((TRelation*) relationData));
}
2022-12-13 08:34:35 +00:00
public override int GetStorageIndex(int entityA, int entityB)
{
return indices[new Relation(entityA, entityB)];
}
2022-12-07 21:53:23 +00:00
public override unsafe void* Get(int relationStorageIndex)
{
fixed (void* p = &relations[relationStorageIndex])
{
return p;
}
2022-12-07 06:06:02 +00:00
}
2022-12-03 07:43:54 +00:00
public override void UnrelateAll(int entityID)
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
if (outRelations.TryGetValue(entityID, out var entityOutRelations))
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
foreach (var entityB in entityOutRelations)
2022-04-06 19:53:50 +00:00
{
2022-04-19 19:35:21 +00:00
Remove(new Relation(entityID, entityB));
2022-04-06 19:53:50 +00:00
}
2022-12-13 08:34:35 +00:00
ReturnHashSetToPool(entityOutRelations);
outRelations.Remove(entityID);
2022-04-06 19:53:50 +00:00
}
2022-12-13 08:34:35 +00:00
if (inRelations.TryGetValue(entityID, out var entityInRelations))
2022-04-06 19:53:50 +00:00
{
2022-12-13 08:34:35 +00:00
foreach (var entityA in entityInRelations)
2022-04-06 19:53:50 +00:00
{
2022-04-19 19:35:21 +00:00
Remove(new Relation(entityA, entityID));
2022-04-06 19:53:50 +00:00
}
2022-12-13 08:34:35 +00:00
ReturnHashSetToPool(entityInRelations);
inRelations.Remove(entityID);
2022-04-06 19:53:50 +00:00
}
}
2022-12-07 06:06:02 +00:00
public override RelationStorage<TRelation> CreateStorage()
2022-04-06 19:53:50 +00:00
{
2022-12-07 06:06:02 +00:00
return new RelationStorage<TRelation>();
}
public override void Clear()
{
count = 0;
indices.Clear();
foreach (var set in inRelations.Values)
{
ReturnHashSetToPool(set);
}
inRelations.Clear();
foreach (var set in outRelations.Values)
{
ReturnHashSetToPool(set);
}
outRelations.Clear();
2022-04-06 19:53:50 +00:00
}
}
}