2022-05-03 04:51:11 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2023-10-13 20:42:22 +00:00
|
|
|
|
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-21 07:13:04 +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>> OutRelationSets = new Dictionary<Entity, IndexableSet<Entity>>(16);
|
|
|
|
|
internal Dictionary<Entity, IndexableSet<Entity>> InRelationSets = new Dictionary<Entity, IndexableSet<Entity>>(16);
|
|
|
|
|
private Stack<IndexableSet<Entity>> ListPool = new Stack<IndexableSet<Entity>>();
|
2023-11-03 19:40:26 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
private bool IsDisposed;
|
2023-11-03 19:40:26 +00:00
|
|
|
|
|
|
|
|
|
public RelationStorage(int relationDataSize)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-11-21 07:13:04 +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-21 07:13:04 +00:00
|
|
|
|
return new ReverseSpanEnumerator<(Entity, Entity)>(Relations.ToSpan<(Entity, Entity)>());
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public unsafe void Set<T>(in Entity entityA, in Entity entityB, in T relationData) where T : unmanaged
|
|
|
|
|
{
|
|
|
|
|
var relation = (entityA, entityB);
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (Indices.TryGetValue(relation, out var index))
|
2023-10-13 20:42:22 +00:00
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
RelationDatas.Set(index, relationData);
|
2023-11-03 19:40:26 +00:00
|
|
|
|
return;
|
2023-10-13 20:42:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (!OutRelationSets.ContainsKey(entityA))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
OutRelationSets[entityA] = AcquireHashSetFromPool();
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
2023-11-21 07:13:04 +00:00
|
|
|
|
OutRelationSets[entityA].Add(entityB);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (!InRelationSets.ContainsKey(entityB))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
InRelationSets[entityB] = AcquireHashSetFromPool();
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2023-11-21 07:13:04 +00:00
|
|
|
|
InRelationSets[entityB].Add(entityA);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
Relations.Append(relation);
|
|
|
|
|
RelationDatas.Append(relationData);
|
|
|
|
|
Indices.Add(relation, Relations.Count - 1);
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
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
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
var relationIndex = Indices[(entityA, entityB)];
|
|
|
|
|
return ref RelationDatas.Get<T>(relationIndex);
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
public bool Has(Entity entityA, Entity entityB)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return Indices.ContainsKey((entityA, entityB));
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-04-06 19:53:50 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
public ReverseSpanEnumerator<Entity> OutRelations(Entity Entity)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (OutRelationSets.TryGetValue(Entity, out var entityOutRelations))
|
2023-01-10 00:41:00 +00:00
|
|
|
|
{
|
2023-11-03 19:40:26 +00:00
|
|
|
|
return entityOutRelations.GetEnumerator();
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
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
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (!OutRelationSets.ContainsKey(Entity) || OutRelationSets[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
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
#endif
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return OutRelationSets[Entity][n];
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
public bool HasOutRelation(Entity Entity)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return OutRelationSets.ContainsKey(Entity) && OutRelationSets[Entity].Count > 0;
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
public int OutRelationCount(Entity Entity)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return OutRelationSets.TryGetValue(Entity, out var entityOutRelations) ? entityOutRelations.Count : 0;
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
public ReverseSpanEnumerator<Entity> InRelations(Entity Entity)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (InRelationSets.TryGetValue(Entity, out var entityInRelations))
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
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
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
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)
|
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
#if DEBUG
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (!InRelationSets.ContainsKey(Entity) || InRelationSets[Entity].Count == 0)
|
2023-11-03 19:40:26 +00:00
|
|
|
|
{
|
|
|
|
|
throw new KeyNotFoundException("No in relations to this entity!");
|
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return InRelationSets[Entity][n];
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HasInRelation(Entity Entity)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return InRelationSets.ContainsKey(Entity) && InRelationSets[Entity].Count > 0;
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int InRelationCount(Entity Entity)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return InRelationSets.TryGetValue(Entity, out var entityInRelations) ? entityInRelations.Count : 0;
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public (bool, bool) Remove(in Entity entityA, in Entity entityB)
|
|
|
|
|
{
|
|
|
|
|
var aEmpty = false;
|
|
|
|
|
var bEmpty = false;
|
|
|
|
|
var relation = (entityA, entityB);
|
2022-08-09 21:41:31 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (OutRelationSets.TryGetValue(entityA, out var entityOutRelations))
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
2023-11-03 19:40:26 +00:00
|
|
|
|
entityOutRelations.Remove(entityB);
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (OutRelationSets[entityA].Count == 0)
|
2023-11-03 19:40:26 +00:00
|
|
|
|
{
|
|
|
|
|
aEmpty = true;
|
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (InRelationSets.TryGetValue(entityB, out var entityInRelations))
|
2022-08-09 21:41:31 +00:00
|
|
|
|
{
|
2023-11-03 19:40:26 +00:00
|
|
|
|
entityInRelations.Remove(entityA);
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (InRelationSets[entityB].Count == 0)
|
2023-11-03 19:40:26 +00:00
|
|
|
|
{
|
|
|
|
|
bEmpty = true;
|
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (Indices.TryGetValue(relation, out var index))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
var lastElementIndex = Relations.Count - 1;
|
2023-01-10 00:41:00 +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-21 07:13:04 +00:00
|
|
|
|
var lastRelation = Relations.Get<(Entity, Entity)>(lastElementIndex);
|
|
|
|
|
Indices[lastRelation] = index;
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
RelationDatas.Delete(index);
|
|
|
|
|
Relations.Delete(index);
|
2023-11-03 23:51:26 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
Indices.Remove(relation);
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
return (aEmpty, bEmpty);
|
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
public void RemoveEntity(in Entity entity)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (OutRelationSets.TryGetValue(entity, out var entityOutRelations))
|
2023-11-03 19:40:26 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (var entityB in entityOutRelations)
|
|
|
|
|
{
|
|
|
|
|
Remove(entity, entityB);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
ReturnHashSetToPool(entityOutRelations);
|
2023-11-21 07:13:04 +00:00
|
|
|
|
OutRelationSets.Remove(entity);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (InRelationSets.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);
|
2023-11-21 07:13:04 +00:00
|
|
|
|
InRelationSets.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()
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (ListPool.Count == 0)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
ListPool.Push(new IndexableSet<Entity>());
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
return ListPool.Pop();
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
private void ReturnHashSetToPool(IndexableSet<Entity> hashSet)
|
|
|
|
|
{
|
|
|
|
|
hashSet.Clear();
|
2023-11-21 07:13:04 +00:00
|
|
|
|
ListPool.Push(hashSet);
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
public void Clear()
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
Indices.Clear();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
foreach (var set in InRelationSets.Values)
|
2023-01-10 00:41:00 +00:00
|
|
|
|
{
|
2023-11-03 19:40:26 +00:00
|
|
|
|
ReturnHashSetToPool(set);
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
2023-11-21 07:13:04 +00:00
|
|
|
|
InRelationSets.Clear();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
foreach (var set in OutRelationSets.Values)
|
2023-01-10 00:41:00 +00:00
|
|
|
|
{
|
2023-11-03 19:40:26 +00:00
|
|
|
|
ReturnHashSetToPool(set);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
2023-11-21 07:13:04 +00:00
|
|
|
|
OutRelationSets.Clear();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
Relations.Clear();
|
|
|
|
|
RelationDatas.Clear();
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
if (!IsDisposed)
|
2023-01-10 00:41:00 +00:00
|
|
|
|
{
|
2023-11-03 19:40:26 +00:00
|
|
|
|
Clear();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2023-11-03 19:40:26 +00:00
|
|
|
|
if (disposing)
|
2023-01-10 00:41:00 +00:00
|
|
|
|
{
|
2023-11-21 07:13:04 +00:00
|
|
|
|
foreach (var set in ListPool)
|
2023-10-13 20:42:22 +00:00
|
|
|
|
{
|
2023-11-03 19:40:26 +00:00
|
|
|
|
set.Dispose();
|
2023-10-13 20:42:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
Relations.Dispose();
|
|
|
|
|
RelationDatas.Dispose();
|
2023-10-13 20:42:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 07:13:04 +00:00
|
|
|
|
IsDisposed = true;
|
2023-10-13 20:42:22 +00:00
|
|
|
|
}
|
2023-11-03 19:40:26 +00:00
|
|
|
|
}
|
2023-10-13 20:42:22 +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
|
|
|
|
}
|
|
|
|
|
}
|