2022-05-03 04:51:11 +00:00
|
|
|
|
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
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public abstract unsafe void Set(int entityA, int entityB, void* relationData);
|
|
|
|
|
public abstract int GetStorageIndex(int entityA, int entityB);
|
|
|
|
|
public abstract unsafe void* Get(int relationStorageIndex);
|
|
|
|
|
public abstract void UnrelateAll(int entityID);
|
|
|
|
|
public abstract ReverseSpanEnumerator<Entity> OutRelations(int entityID);
|
|
|
|
|
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.
|
2022-05-03 04:51:11 +00:00
|
|
|
|
internal class RelationStorage<TRelation> : RelationStorage where TRelation : unmanaged
|
2022-04-19 19:35:21 +00:00
|
|
|
|
{
|
2022-05-03 04:51:11 +00:00
|
|
|
|
private int count = 0;
|
2023-01-10 00:41:00 +00:00
|
|
|
|
private Dictionary<(Entity, Entity), int> indices = new Dictionary<(Entity, Entity), int>(16);
|
|
|
|
|
private (Entity, Entity)[] relations = new (Entity, Entity)[16];
|
2022-05-03 04:51:11 +00:00
|
|
|
|
private TRelation[] relationDatas = new TRelation[16];
|
2023-01-10 00:41:00 +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
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public ReverseSpanEnumerator<(Entity, Entity)> All()
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return new ReverseSpanEnumerator<(Entity, Entity)>(new Span<(Entity, Entity)>(relations, 0, count));
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public void Set(in Entity entityA, in Entity entityB, TRelation relationData)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
var relation = (entityA, entityB);
|
|
|
|
|
|
|
|
|
|
if (indices.TryGetValue(relation, out var index))
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
|
|
|
|
relationDatas[index] = relationData;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-04-07 03:08:28 +00:00
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
var idA = entityA.ID;
|
|
|
|
|
var idB = entityB.ID;
|
2022-04-06 19:53:50 +00:00
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (!outRelations.ContainsKey(idA))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
outRelations[idA] = AcquireHashSetFromPool();
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
outRelations[idA].Add(idB);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (!inRelations.ContainsKey(idB))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
inRelations[idB] = AcquireHashSetFromPool();
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
inRelations[idB].Add(idA);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
|
2022-05-03 04:51:11 +00:00
|
|
|
|
if (count >= relationDatas.Length)
|
|
|
|
|
{
|
2022-06-23 00:05:33 +00:00
|
|
|
|
Array.Resize(ref relations, relations.Length * 2);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public TRelation Get(in Entity entityA, in Entity entityB)
|
|
|
|
|
{
|
|
|
|
|
return relationDatas[indices[(entityA, entityB)]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Has((Entity, Entity) relation)
|
2022-04-07 03:08:28 +00:00
|
|
|
|
{
|
2022-05-03 04:51:11 +00:00
|
|
|
|
return indices.ContainsKey(relation);
|
2022-04-07 03:08:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public override ReverseSpanEnumerator<Entity> OutRelations(int entityID)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
if (outRelations.TryGetValue(entityID, out var entityOutRelations))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return entityOutRelations.GetEnumerator();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return ReverseSpanEnumerator<Entity>.Empty;
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public Entity OutFirst(int entityID)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
#if DEBUG
|
2023-01-10 00:41:00 +00:00
|
|
|
|
if (!outRelations.ContainsKey(entityID) || outRelations[entityID].Count == 0)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
throw new KeyNotFoundException("No out relations to this entity!");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return outRelations[entityID][0];
|
2022-08-09 21:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HasOutRelation(int entityID)
|
|
|
|
|
{
|
|
|
|
|
return outRelations.ContainsKey(entityID) && outRelations[entityID].Count > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int OutRelationCount(int entityID)
|
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return outRelations.TryGetValue(entityID, out var entityOutRelations) ? entityOutRelations.Count : 0;
|
2022-08-09 21:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public ReverseSpanEnumerator<Entity> InRelations(int entityID)
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
if (inRelations.TryGetValue(entityID, out var entityInRelations))
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return entityInRelations.GetEnumerator();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return ReverseSpanEnumerator<Entity>.Empty;
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public Entity InFirst(int entityID)
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
|
|
|
|
#if DEBUG
|
2023-01-10 00:41:00 +00:00
|
|
|
|
if (!inRelations.ContainsKey(entityID) || inRelations[entityID].Count == 0)
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
2023-02-09 23:15:41 +00:00
|
|
|
|
throw new KeyNotFoundException("No in relations to this entity!");
|
2022-08-09 21:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return inRelations[entityID][0];
|
2022-08-09 21:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HasInRelation(int entityID)
|
|
|
|
|
{
|
|
|
|
|
return inRelations.ContainsKey(entityID) && inRelations[entityID].Count > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int InRelationCount(int entityID)
|
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return inRelations.TryGetValue(entityID, out var entityInRelations) ? entityInRelations.Count : 0;
|
2022-08-09 21:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public (bool, bool) Remove(in Entity entityA, in Entity entityB)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
var aEmpty = false;
|
|
|
|
|
var bEmpty = false;
|
|
|
|
|
var relation = (entityA, entityB);
|
|
|
|
|
|
|
|
|
|
if (outRelations.TryGetValue(entityA.ID, out var entityOutRelations))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
entityOutRelations.Remove(entityB.ID);
|
|
|
|
|
if (outRelations[entityA.ID].Count == 0)
|
|
|
|
|
{
|
|
|
|
|
aEmpty = true;
|
|
|
|
|
}
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
if (inRelations.TryGetValue(entityB.ID, out var entityInRelations))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
entityInRelations.Remove(entityA.ID);
|
|
|
|
|
if (inRelations[entityB.ID].Count == 0)
|
|
|
|
|
{
|
|
|
|
|
bEmpty = true;
|
|
|
|
|
}
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
if (indices.TryGetValue(relation, out var index))
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return (aEmpty, bEmpty);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
private IndexableSet<Entity> AcquireHashSetFromPool()
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (listPool.Count == 0)
|
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
listPool.Push(new IndexableSet<Entity>());
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return listPool.Pop();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
private void ReturnHashSetToPool(IndexableSet<Entity> hashSet)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-04-19 19:35:21 +00:00
|
|
|
|
hashSet.Clear();
|
2022-04-06 19:53:50 +00:00
|
|
|
|
listPool.Push(hashSet);
|
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
// untyped methods used for internal implementation
|
|
|
|
|
|
|
|
|
|
public override unsafe void Set(int entityA, int entityB, void* relationData)
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
Set(entityA, entityB, *((TRelation*) relationData));
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public override int GetStorageIndex(int entityA, int entityB)
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return indices[(entityA, entityB)];
|
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public override unsafe void* Get(int relationStorageIndex)
|
|
|
|
|
{
|
|
|
|
|
fixed (void* p = &relations[relationStorageIndex])
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return p;
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public override void UnrelateAll(int entityID)
|
|
|
|
|
{
|
|
|
|
|
if (outRelations.TryGetValue(entityID, out var entityOutRelations))
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
foreach (var entityB in entityOutRelations)
|
|
|
|
|
{
|
|
|
|
|
Remove(entityID, entityB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReturnHashSetToPool(entityOutRelations);
|
|
|
|
|
outRelations.Remove(entityID);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
if (inRelations.TryGetValue(entityID, out var entityInRelations))
|
|
|
|
|
{
|
|
|
|
|
foreach (var entityA in entityInRelations)
|
|
|
|
|
{
|
|
|
|
|
Remove(entityA, entityID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReturnHashSetToPool(entityInRelations);
|
|
|
|
|
inRelations.Remove(entityID);
|
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public override RelationStorage<TRelation> CreateStorage()
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return new RelationStorage<TRelation>();
|
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public override void Clear()
|
|
|
|
|
{
|
|
|
|
|
count = 0;
|
2022-05-03 04:51:11 +00:00
|
|
|
|
indices.Clear();
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
foreach (var set in inRelations.Values)
|
|
|
|
|
{
|
|
|
|
|
ReturnHashSetToPool(set);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
inRelations.Clear();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
foreach (var set in outRelations.Values)
|
|
|
|
|
{
|
|
|
|
|
ReturnHashSetToPool(set);
|
|
|
|
|
}
|
|
|
|
|
outRelations.Clear();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|