2022-05-03 04:51:11 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Runtime.InteropServices;
|
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-05-03 04:51:11 +00:00
|
|
|
|
public abstract RelationStorageState CreateState();
|
|
|
|
|
public abstract void Save(RelationStorageState state);
|
|
|
|
|
public abstract void Load(RelationStorageState state);
|
2022-04-19 19:35:21 +00:00
|
|
|
|
public abstract void OnEntityDestroy(int entityID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
private Dictionary<Relation, int> indices = new Dictionary<Relation, int>(16);
|
|
|
|
|
private Relation[] relations = new Relation[16];
|
|
|
|
|
private TRelation[] relationDatas = new TRelation[16];
|
2022-08-09 21:41:31 +00:00
|
|
|
|
private Dictionary<int, IndexableSet<int>> outRelations = new Dictionary<int, IndexableSet<int>>(16);
|
|
|
|
|
private Dictionary<int, IndexableSet<int>> inRelations = new Dictionary<int, IndexableSet<int>>(16);
|
|
|
|
|
private Stack<IndexableSet<int>> listPool = new Stack<IndexableSet<int>>();
|
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
|
|
|
|
{
|
2022-05-03 04:51:11 +00:00
|
|
|
|
for (var i = 0; i < count; i += 1)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-05-03 04:51:11 +00:00
|
|
|
|
var relation = relations[i];
|
|
|
|
|
yield return (relation.A, relation.B, relationDatas[i]);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 04:51:11 +00:00
|
|
|
|
public void Set(Relation relation, TRelation relationData)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-05-03 04:51:11 +00:00
|
|
|
|
if (indices.ContainsKey(relation))
|
|
|
|
|
{
|
|
|
|
|
var index = indices[relation];
|
|
|
|
|
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;
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2022-04-07 03:08:28 +00:00
|
|
|
|
public bool Has(Relation relation)
|
|
|
|
|
{
|
2022-05-03 04:51:11 +00:00
|
|
|
|
return indices.ContainsKey(relation);
|
2022-04-07 03:08:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
// FIXME: creating the new Relation in here is slightly deranged
|
|
|
|
|
public IEnumerable<(Entity, TRelation)> OutRelations(int entityID)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (outRelations.ContainsKey(entityID))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
foreach (var id in outRelations[entityID])
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-04-19 19:35:21 +00:00
|
|
|
|
var relation = new Relation(entityID, id);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
yield return (relation.B, relationDatas[indices[relation]]);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
public (Entity, TRelation) OutFirst(int entityID)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
#if DEBUG
|
|
|
|
|
if (!outRelations.ContainsKey(entityID))
|
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
|
|
|
|
|
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])
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-04-19 19:35:21 +00:00
|
|
|
|
var relation = new Relation(id, entityID);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
yield return (relation.A, relationDatas[indices[relation]]);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-06 19:53:50 +00:00
|
|
|
|
public bool Remove(Relation relation)
|
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (outRelations.ContainsKey(relation.A.ID))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
outRelations[relation.A.ID].Remove(relation.B.ID);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (inRelations.ContainsKey(relation.B.ID))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
inRelations[relation.B.ID].Remove(relation.A.ID);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 04:51:11 +00:00
|
|
|
|
if (indices.ContainsKey(relation))
|
|
|
|
|
{
|
|
|
|
|
var index = indices[relation];
|
|
|
|
|
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);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
public void UnrelateAll(int entityID)
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (outRelations.ContainsKey(entityID))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
foreach (var entityB in outRelations[entityID])
|
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-08-09 21:41:31 +00:00
|
|
|
|
ReturnHashSetToPool(outRelations[entityID]);
|
|
|
|
|
outRelations.Remove(entityID);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (inRelations.ContainsKey(entityID))
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
foreach (var entityA in inRelations[entityID])
|
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-08-09 21:41:31 +00:00
|
|
|
|
ReturnHashSetToPool(inRelations[entityID]);
|
|
|
|
|
inRelations.Remove(entityID);
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
public override void OnEntityDestroy(int entityID)
|
|
|
|
|
{
|
|
|
|
|
UnrelateAll(entityID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IndexableSet<int> AcquireHashSetFromPool()
|
2022-04-06 19:53:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (listPool.Count == 0)
|
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
listPool.Push(new IndexableSet<int>());
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return listPool.Pop();
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
private void ReturnHashSetToPool(IndexableSet<int> 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
|
|
|
|
|
|
|
|
|
public override RelationStorageState CreateState()
|
|
|
|
|
{
|
|
|
|
|
return RelationStorageState.Create<TRelation>(count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Save(RelationStorageState state)
|
|
|
|
|
{
|
|
|
|
|
ReadOnlySpan<byte> relationBytes = MemoryMarshal.Cast<Relation, byte>(relations);
|
|
|
|
|
|
|
|
|
|
if (relationBytes.Length > state.Relations.Length)
|
|
|
|
|
{
|
|
|
|
|
Array.Resize(ref state.Relations, relationBytes.Length);
|
|
|
|
|
}
|
|
|
|
|
relationBytes.CopyTo(state.Relations);
|
|
|
|
|
|
|
|
|
|
ReadOnlySpan<byte> relationDataBytes = MemoryMarshal.Cast<TRelation, byte>(relationDatas);
|
|
|
|
|
|
|
|
|
|
if (relationDataBytes.Length > state.RelationDatas.Length)
|
|
|
|
|
{
|
|
|
|
|
Array.Resize(ref state.RelationDatas, relationDataBytes.Length);
|
|
|
|
|
}
|
|
|
|
|
relationDataBytes.CopyTo(state.RelationDatas);
|
|
|
|
|
|
|
|
|
|
state.Count = count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Load(RelationStorageState state)
|
|
|
|
|
{
|
|
|
|
|
state.Relations.CopyTo(MemoryMarshal.Cast<Relation, byte>(relations));
|
|
|
|
|
state.RelationDatas.CopyTo(MemoryMarshal.Cast<TRelation, byte>(relationDatas));
|
|
|
|
|
|
|
|
|
|
indices.Clear();
|
2022-08-09 21:41:31 +00:00
|
|
|
|
outRelations.Clear();
|
|
|
|
|
inRelations.Clear();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
for (var i = 0; i < state.Count; i += 1)
|
|
|
|
|
{
|
|
|
|
|
var relation = relations[i];
|
|
|
|
|
indices[relation] = i;
|
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (!outRelations.ContainsKey(relation.A.ID))
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
outRelations[relation.A.ID] = AcquireHashSetFromPool();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
outRelations[relation.A.ID].Add(relation.B.ID);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
2022-08-09 21:41:31 +00:00
|
|
|
|
if (!inRelations.ContainsKey(relation.B.ID))
|
2022-05-03 04:51:11 +00:00
|
|
|
|
{
|
2022-08-09 21:41:31 +00:00
|
|
|
|
inRelations[relation.B.ID] = AcquireHashSetFromPool();
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
2022-08-09 21:41:31 +00:00
|
|
|
|
inRelations[relation.B.ID].Add(relation.A.ID);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count = state.Count;
|
|
|
|
|
}
|
2022-04-06 19:53:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|