MoonTools.ECS/src/RelationStorage.cs

285 lines
6.3 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using MoonTools.ECS.Collections;
2022-04-08 05:52:03 +00:00
2023-11-03 19:40:26 +00:00
namespace MoonTools.ECS;
// TODO: implement this entire class with NativeMemory equivalents, can just memcpy for snapshots
internal class RelationStorage
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
internal NativeArray relations;
internal NativeArray relationDatas;
internal Dictionary<(Entity, Entity), int> indices = new Dictionary<(Entity, Entity), int>(16);
internal Dictionary<Entity, IndexableSet<Entity>> outRelations = new Dictionary<Entity, IndexableSet<Entity>>(16);
internal Dictionary<Entity, IndexableSet<Entity>> inRelations = new Dictionary<Entity, IndexableSet<Entity>>(16);
private Stack<IndexableSet<Entity>> listPool = new Stack<IndexableSet<Entity>>();
private bool disposed;
public RelationStorage(int relationDataSize)
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
relations = new NativeArray(Unsafe.SizeOf<(Entity, Entity)>());
relationDatas = new NativeArray(relationDataSize);
2022-04-19 19:35:21 +00:00
}
2023-11-03 19:40:26 +00:00
public ReverseSpanEnumerator<(Entity, Entity)> All()
2022-04-19 19:35:21 +00:00
{
2023-11-03 19:40:26 +00:00
return new ReverseSpanEnumerator<(Entity, Entity)>(relations.ToSpan<(Entity, Entity)>());
}
public unsafe void Set<T>(in Entity entityA, in Entity entityB, in T relationData) where T : unmanaged
{
var relation = (entityA, entityB);
if (indices.TryGetValue(relation, out var index))
{
2023-11-03 19:40:26 +00:00
relationDatas.Set(index, relationData);
return;
}
2023-11-03 19:40:26 +00:00
if (!outRelations.ContainsKey(entityA))
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
outRelations[entityA] = AcquireHashSetFromPool();
2022-04-06 19:53:50 +00:00
}
2023-11-03 19:40:26 +00:00
outRelations[entityA].Add(entityB);
2022-04-06 19:53:50 +00:00
2023-11-03 19:40:26 +00:00
if (!inRelations.ContainsKey(entityB))
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
inRelations[entityB] = AcquireHashSetFromPool();
}
inRelations[entityB].Add(entityA);
2022-04-06 19:53:50 +00:00
2023-11-03 19:40:26 +00:00
relations.Append(relation);
relationDatas.Append(relationData);
indices.Add(relation, relations.Count - 1);
}
2022-04-06 19:53:50 +00:00
2023-11-03 19:40:26 +00:00
public ref T Get<T>(in Entity entityA, in Entity entityB) where T : unmanaged
{
var relationIndex = indices[(entityA, entityB)];
return ref relationDatas.Get<T>(relationIndex);
}
2023-11-03 19:40:26 +00:00
public bool Has(Entity entityA, Entity entityB)
{
return indices.ContainsKey((entityA, entityB));
}
2022-04-06 19:53:50 +00:00
2023-11-03 19:40:26 +00:00
public ReverseSpanEnumerator<Entity> OutRelations(Entity Entity)
{
if (outRelations.TryGetValue(Entity, out var entityOutRelations))
{
2023-11-03 19:40:26 +00:00
return entityOutRelations.GetEnumerator();
}
2023-11-03 19:40:26 +00:00
else
2022-04-07 03:08:28 +00:00
{
2023-11-03 19:40:26 +00:00
return ReverseSpanEnumerator<Entity>.Empty;
2022-04-07 03:08:28 +00:00
}
2023-11-03 19:40:26 +00:00
}
2022-04-07 03:08:28 +00:00
2023-11-03 19:40:26 +00:00
public Entity OutFirst(Entity Entity)
{
return OutNth(Entity, 0);
}
2022-04-06 19:53:50 +00:00
2023-11-03 19:40:26 +00:00
public Entity OutNth(Entity Entity, int n)
{
#if DEBUG
if (!outRelations.ContainsKey(Entity) || outRelations[Entity].Count == 0)
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
throw new KeyNotFoundException("No out relations to this entity!");
2023-03-20 21:26:27 +00:00
}
#endif
2023-11-03 19:40:26 +00:00
return outRelations[Entity][n];
}
2023-11-03 19:40:26 +00:00
public bool HasOutRelation(Entity Entity)
{
return outRelations.ContainsKey(Entity) && outRelations[Entity].Count > 0;
}
2023-11-03 19:40:26 +00:00
public int OutRelationCount(Entity Entity)
{
return outRelations.TryGetValue(Entity, out var entityOutRelations) ? entityOutRelations.Count : 0;
}
2023-11-03 19:40:26 +00:00
public ReverseSpanEnumerator<Entity> InRelations(Entity Entity)
{
if (inRelations.TryGetValue(Entity, out var entityInRelations))
{
2023-11-03 19:40:26 +00:00
return entityInRelations.GetEnumerator();
2022-04-06 19:53:50 +00:00
}
2023-11-03 19:40:26 +00:00
else
{
2023-11-03 19:40:26 +00:00
return ReverseSpanEnumerator<Entity>.Empty;
2023-03-20 21:26:27 +00:00
}
2023-11-03 19:40:26 +00:00
}
2023-03-20 21:26:27 +00:00
2023-11-03 19:40:26 +00:00
public Entity InFirst(Entity Entity)
{
return InNth(Entity, 0);
}
public Entity InNth(Entity Entity, int n)
{
#if DEBUG
2023-11-03 19:40:26 +00:00
if (!inRelations.ContainsKey(Entity) || inRelations[Entity].Count == 0)
{
throw new KeyNotFoundException("No in relations to this entity!");
}
#endif
2023-11-03 19:40:26 +00:00
return inRelations[Entity][n];
}
public bool HasInRelation(Entity Entity)
{
return inRelations.ContainsKey(Entity) && inRelations[Entity].Count > 0;
}
public int InRelationCount(Entity Entity)
{
return inRelations.TryGetValue(Entity, out var entityInRelations) ? entityInRelations.Count : 0;
}
public (bool, bool) Remove(in Entity entityA, in Entity entityB)
{
var aEmpty = false;
var bEmpty = false;
var relation = (entityA, entityB);
2023-11-03 19:40:26 +00:00
if (outRelations.TryGetValue(entityA, out var entityOutRelations))
{
2023-11-03 19:40:26 +00:00
entityOutRelations.Remove(entityB);
if (outRelations[entityA].Count == 0)
{
aEmpty = true;
}
}
2023-11-03 19:40:26 +00:00
if (inRelations.TryGetValue(entityB, out var entityInRelations))
{
2023-11-03 19:40:26 +00:00
entityInRelations.Remove(entityA);
if (inRelations[entityB].Count == 0)
{
bEmpty = true;
}
}
2023-11-03 19:40:26 +00:00
if (indices.TryGetValue(relation, out var index))
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
var lastElementIndex = relations.Count - 1;
2023-11-03 19:40:26 +00:00
relationDatas.Delete(index);
relations.Delete(index);
2022-04-06 19:53:50 +00:00
2023-11-03 19:40:26 +00:00
// move an element into the hole
if (index != lastElementIndex)
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
var lastRelation = relations.Get<(Entity, Entity)>(lastElementIndex);
indices[lastRelation] = index;
2022-04-06 19:53:50 +00:00
}
2023-11-03 19:40:26 +00:00
indices.Remove(relation);
}
2023-11-03 19:40:26 +00:00
return (aEmpty, bEmpty);
}
2023-11-03 19:40:26 +00:00
public void RemoveEntity(in Entity entity)
{
if (outRelations.TryGetValue(entity, out var entityOutRelations))
{
foreach (var entityB in entityOutRelations)
{
Remove(entity, entityB);
}
2023-11-03 19:40:26 +00:00
ReturnHashSetToPool(entityOutRelations);
outRelations.Remove(entity);
2022-04-06 19:53:50 +00:00
}
2023-11-03 19:40:26 +00:00
if (inRelations.TryGetValue(entity, out var entityInRelations))
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
foreach (var entityA in entityInRelations)
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
Remove(entityA, entity);
2022-04-06 19:53:50 +00:00
}
2023-11-03 19:40:26 +00:00
ReturnHashSetToPool(entityInRelations);
inRelations.Remove(entity);
2022-04-06 19:53:50 +00:00
}
2023-11-03 19:40:26 +00:00
}
2022-04-06 19:53:50 +00:00
2023-11-03 19:40:26 +00:00
internal IndexableSet<Entity> AcquireHashSetFromPool()
{
if (listPool.Count == 0)
2022-04-06 19:53:50 +00:00
{
2023-11-03 19:40:26 +00:00
listPool.Push(new IndexableSet<Entity>());
2022-04-06 19:53:50 +00:00
}
2023-11-03 19:40:26 +00:00
return listPool.Pop();
}
2023-11-03 19:40:26 +00:00
private void ReturnHashSetToPool(IndexableSet<Entity> hashSet)
{
hashSet.Clear();
listPool.Push(hashSet);
}
2023-11-03 19:40:26 +00:00
public void Clear()
{
indices.Clear();
2023-11-03 19:40:26 +00:00
foreach (var set in inRelations.Values)
{
2023-11-03 19:40:26 +00:00
ReturnHashSetToPool(set);
}
2023-11-03 19:40:26 +00:00
inRelations.Clear();
2023-11-03 19:40:26 +00:00
foreach (var set in outRelations.Values)
{
2023-11-03 19:40:26 +00:00
ReturnHashSetToPool(set);
}
2023-11-03 19:40:26 +00:00
outRelations.Clear();
2023-11-03 19:40:26 +00:00
relations.Clear();
relationDatas.Clear();
}
2023-11-03 19:40:26 +00:00
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
2023-11-03 19:40:26 +00:00
Clear();
2023-11-03 19:40:26 +00:00
if (disposing)
{
2023-11-03 19:40:26 +00:00
foreach (var set in listPool)
{
2023-11-03 19:40:26 +00:00
set.Dispose();
}
2023-11-03 19:40:26 +00:00
relations.Dispose();
relationDatas.Dispose();
}
2023-11-03 19:40:26 +00:00
disposed = true;
}
2023-11-03 19:40:26 +00:00
}
2023-11-03 19:40:26 +00:00
// ~RelationStorage()
// {
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
// Dispose(disposing: false);
// }
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
2022-04-06 19:53:50 +00:00
}
}