snapshot and restore relations

rev2
cosmonaut 2023-10-30 16:25:33 -07:00
parent 4d40103a6f
commit 9032eff699
8 changed files with 269 additions and 170 deletions

View File

@ -7,7 +7,7 @@ namespace MoonTools.ECS.Rev2
{ {
public static ArchetypeSignature Empty = new ArchetypeSignature(0); public static ArchetypeSignature Empty = new ArchetypeSignature(0);
List<ulong> Ids; List<uint> Ids;
public int Count => Ids.Count; public int Count => Ids.Count;
@ -15,12 +15,12 @@ namespace MoonTools.ECS.Rev2
public ArchetypeSignature() public ArchetypeSignature()
{ {
Ids = new List<ulong>(); Ids = new List<uint>();
} }
public ArchetypeSignature(int capacity) public ArchetypeSignature(int capacity)
{ {
Ids = new List<ulong>(capacity); Ids = new List<uint>(capacity);
} }
// Maintains sorted order // Maintains sorted order

View File

@ -10,7 +10,7 @@ namespace MoonTools.ECS.Rev2
public int Count; public int Count;
private int Capacity; private int Capacity;
private readonly int ElementSize; public readonly int ElementSize;
private bool IsDisposed; private bool IsDisposed;
@ -23,6 +23,11 @@ namespace MoonTools.ECS.Rev2
Elements = (nint) NativeMemory.Alloc((nuint) (ElementSize * Capacity)); Elements = (nint) NativeMemory.Alloc((nuint) (ElementSize * Capacity));
} }
public Span<T> ToSpan<T>()
{
return new Span<T>((void*) Elements, Count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(int i) where T : unmanaged public ref T Get<T>(int i) where T : unmanaged
{ {

View File

@ -24,13 +24,15 @@ namespace MoonTools.ECS.Rev2
public FilterBuilder Include<T>() where T : unmanaged public FilterBuilder Include<T>() where T : unmanaged
{ {
Included.Add(World.TypeToComponentId[typeof(T)]); World.TryRegisterTypeId<T>();
Included.Add(World.TypeToId[typeof(T)]);
return new FilterBuilder(World, Included, Excluded); return new FilterBuilder(World, Included, Excluded);
} }
public FilterBuilder Exclude<T>() where T : unmanaged public FilterBuilder Exclude<T>() where T : unmanaged
{ {
Excluded.Add(World.TypeToComponentId[typeof(T)]); World.TryRegisterTypeId<T>();
Excluded.Add(World.TypeToId[typeof(T)]);
return new FilterBuilder(World, Included, Excluded); return new FilterBuilder(World, Included, Excluded);
} }

View File

@ -2,27 +2,8 @@
namespace MoonTools.ECS.Rev2; namespace MoonTools.ECS.Rev2;
public readonly record struct Id : IComparable<Id> public readonly record struct Id(uint Value) : IComparable<Id>
{ {
public readonly ulong Value;
private const ulong HI = 0xFFFFFFFF00000000;
private const ulong LO = 0xFFFFFFFF;
public bool IsPair => (HI & Value) != 0;
public uint Low => (uint)(LO & Value);
public uint High => (uint)((HI & Value) >>> 32);
public Id(ulong value)
{
Value = value;
}
public Id(uint relation, uint target)
{
Value = (relation << 31) | target;
}
public int CompareTo(Id other) public int CompareTo(Id other)
{ {
return Value.CompareTo(other.Value); return Value.CompareTo(other.Value);

View File

@ -4,8 +4,8 @@ namespace MoonTools.ECS.Rev2;
internal class IdAssigner internal class IdAssigner
{ {
ulong Next; uint Next;
NativeArray<ulong> AvailableIds = new NativeArray<ulong>(); NativeArray<uint> AvailableIds = new NativeArray<uint>();
public Id Assign() public Id Assign()
{ {

View File

@ -5,28 +5,27 @@ using MoonTools.ECS.Collections;
namespace MoonTools.ECS.Rev2; namespace MoonTools.ECS.Rev2;
internal class Relation // TODO: implement this entire class with NativeMemory equivalents, can just memcpy for snapshots
internal class RelationStorage
{ {
private int count = 0; internal Column relations;
private int capacity = 16; internal Column relationDatas;
private Column relations; internal Dictionary<(Id, Id), int> indices = new Dictionary<(Id, Id), int>(16);
private Column relationDatas; internal Dictionary<Id, IndexableSet<Id>> outRelations = new Dictionary<Id, IndexableSet<Id>>(16);
private Dictionary<(Id, Id), int> indices = new Dictionary<(Id, Id), int>(16); internal Dictionary<Id, IndexableSet<Id>> inRelations = new Dictionary<Id, IndexableSet<Id>>(16);
private Dictionary<Id, IndexableSet<Id>> outRelations = new Dictionary<Id, IndexableSet<Id>>(16);
private Dictionary<Id, IndexableSet<Id>> inRelations = new Dictionary<Id, IndexableSet<Id>>(16);
private Stack<IndexableSet<Id>> listPool = new Stack<IndexableSet<Id>>(); private Stack<IndexableSet<Id>> listPool = new Stack<IndexableSet<Id>>();
private bool disposed; private bool disposed;
public Relation(int relationDataSize) public RelationStorage(int relationDataSize)
{ {
relations = new Column(Unsafe.SizeOf<(Id, Id)>()); relations = new Column(Unsafe.SizeOf<(Id, Id)>());
relationDatas = new Column(relationDataSize); relationDatas = new Column(relationDataSize);
} }
public unsafe ReverseSpanEnumerator<(Id, Id)> All() public ReverseSpanEnumerator<(Id, Id)> All()
{ {
return new ReverseSpanEnumerator<(Id, Id)>(new Span<(Id, Id)>((void*) relations.Elements, count)); return new ReverseSpanEnumerator<(Id, Id)>(relations.ToSpan<(Id, Id)>());
} }
public unsafe void Set<T>(in Id entityA, in Id entityB, in T relationData) where T : unmanaged public unsafe void Set<T>(in Id entityA, in Id entityB, in T relationData) where T : unmanaged
@ -51,16 +50,9 @@ internal class Relation
} }
inRelations[entityB].Add(entityA); inRelations[entityB].Add(entityA);
if (count >= capacity) relations.Append(relation);
{ relationDatas.Append(relationData);
relations.Resize(); indices.Add(relation, relations.Count - 1);
relationDatas.Resize();
}
(((Id, Id)*) relationDatas.Elements)[count] = relation;
((T*) relationDatas.Elements)[count] = relationData;
indices.Add(relation, count);
count += 1;
} }
public ref T Get<T>(in Id entityA, in Id entityB) where T : unmanaged public ref T Get<T>(in Id entityA, in Id entityB) where T : unmanaged
@ -69,9 +61,9 @@ internal class Relation
return ref relationDatas.Get<T>(relationIndex); return ref relationDatas.Get<T>(relationIndex);
} }
public bool Has((Id, Id) relation) public bool Has(Id entityA, Id entityB)
{ {
return indices.ContainsKey(relation); return indices.ContainsKey((entityA, entityB));
} }
public ReverseSpanEnumerator<Id> OutRelations(Id entityID) public ReverseSpanEnumerator<Id> OutRelations(Id entityID)
@ -177,11 +169,11 @@ internal class Relation
if (indices.TryGetValue(relation, out var index)) if (indices.TryGetValue(relation, out var index))
{ {
var lastElementIndex = relations.Count - 1;
relationDatas.Delete(index); relationDatas.Delete(index);
relations.Delete(index); relations.Delete(index);
var lastElementIndex = count - 1;
// move an element into the hole // move an element into the hole
if (index != lastElementIndex) if (index != lastElementIndex)
{ {
@ -189,14 +181,38 @@ internal class Relation
indices[lastRelation] = index; indices[lastRelation] = index;
} }
count -= 1;
indices.Remove(relation); indices.Remove(relation);
} }
return (aEmpty, bEmpty); return (aEmpty, bEmpty);
} }
private IndexableSet<Id> AcquireHashSetFromPool() public void RemoveEntity(Id entity)
{
if (outRelations.TryGetValue(entity, out var entityOutRelations))
{
foreach (var entityB in entityOutRelations)
{
Remove(entity, entityB);
}
ReturnHashSetToPool(entityOutRelations);
outRelations.Remove(entity);
}
if (inRelations.TryGetValue(entity, out var entityInRelations))
{
foreach (var entityA in entityInRelations)
{
Remove(entityA, entity);
}
ReturnHashSetToPool(entityInRelations);
inRelations.Remove(entity);
}
}
internal IndexableSet<Id> AcquireHashSetFromPool()
{ {
if (listPool.Count == 0) if (listPool.Count == 0)
{ {
@ -214,7 +230,6 @@ internal class Relation
public void Clear() public void Clear()
{ {
count = 0;
indices.Clear(); indices.Clear();
foreach (var set in inRelations.Values) foreach (var set in inRelations.Values)
@ -228,6 +243,9 @@ internal class Relation
ReturnHashSetToPool(set); ReturnHashSetToPool(set);
} }
outRelations.Clear(); outRelations.Clear();
relations.Count = 0;
relationDatas.Count = 0;
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
using MoonTools.ECS.Collections; using MoonTools.ECS.Collections;
namespace MoonTools.ECS.Rev2; namespace MoonTools.ECS.Rev2;
@ -8,7 +9,14 @@ public class Snapshot
private Dictionary<ArchetypeSignature, ArchetypeSnapshot> ArchetypeSnapshots = private Dictionary<ArchetypeSignature, ArchetypeSnapshot> ArchetypeSnapshots =
new Dictionary<ArchetypeSignature, ArchetypeSnapshot>(); new Dictionary<ArchetypeSignature, ArchetypeSnapshot>();
private Dictionary<Id, RelationSnapshot> RelationSnapshots =
new Dictionary<Id, RelationSnapshot>();
private Dictionary<Id, Record> EntityIndex = new Dictionary<Id, Record>(); private Dictionary<Id, Record> EntityIndex = new Dictionary<Id, Record>();
private Dictionary<Id, IndexableSet<Id>> EntityRelationIndex =
new Dictionary<Id, IndexableSet<Id>>();
private IdAssigner IdAssigner = new IdAssigner(); private IdAssigner IdAssigner = new IdAssigner();
public int Count public int Count
@ -32,7 +40,7 @@ public class Snapshot
foreach (var (archetypeSignature, archetypeSnapshot) in ArchetypeSnapshots) foreach (var (archetypeSignature, archetypeSnapshot) in ArchetypeSnapshots)
{ {
var archetype = world.ArchetypeIndex[archetypeSignature]; var archetype = world.ArchetypeIndex[archetypeSignature];
RestoreArchetypeSnapshot(archetype); archetypeSnapshot.Restore(archetype);
} }
// restore entity index // restore entity index
@ -44,6 +52,25 @@ public class Snapshot
// restore id assigner state // restore id assigner state
IdAssigner.CopyTo(world.IdAssigner); IdAssigner.CopyTo(world.IdAssigner);
// restore relation state
foreach (var (typeId, relationSnapshot) in RelationSnapshots)
{
var relationStorage = world.RelationIndex[typeId];
relationSnapshot.Restore(relationStorage);
}
// restore entity relation index state
// FIXME: arghhhh this is so slow
foreach (var (id, relationTypeSet) in EntityRelationIndex)
{
world.EntityRelationIndex[id].Clear();
foreach (var typeId in relationTypeSet)
{
world.EntityRelationIndex[id].Add(typeId);
}
}
} }
public void Take(World world) public void Take(World world)
@ -63,9 +90,32 @@ public class Snapshot
{ {
TakeArchetypeSnapshot(archetype); TakeArchetypeSnapshot(archetype);
} }
// copy relations
foreach (var (typeId, relationStorage) in world.RelationIndex)
{
TakeRelationSnapshot(typeId, relationStorage);
} }
internal void TakeArchetypeSnapshot(Archetype archetype) // copy entity relation index
// FIXME: arghhhh this is so slow
foreach (var (id, relationTypeSet) in world.EntityRelationIndex)
{
if (!EntityRelationIndex.ContainsKey(id))
{
EntityRelationIndex.Add(id, new IndexableSet<Id>());
}
EntityRelationIndex[id].Clear();
foreach (var typeId in relationTypeSet)
{
EntityRelationIndex[id].Add(typeId);
}
}
}
private void TakeArchetypeSnapshot(Archetype archetype)
{ {
if (!ArchetypeSnapshots.TryGetValue(archetype.Signature, out var archetypeSnapshot)) if (!ArchetypeSnapshots.TryGetValue(archetype.Signature, out var archetypeSnapshot))
{ {
@ -76,15 +126,19 @@ public class Snapshot
archetypeSnapshot.Take(archetype); archetypeSnapshot.Take(archetype);
} }
private void RestoreArchetypeSnapshot(Archetype archetype) private void TakeRelationSnapshot(Id typeId, RelationStorage relationStorage)
{ {
var archetypeSnapshot = ArchetypeSnapshots[archetype.Signature]; if (!RelationSnapshots.TryGetValue(typeId, out var snapshot))
archetypeSnapshot.Restore(archetype); {
snapshot = new RelationSnapshot(World.ElementSizes[typeId]);
RelationSnapshots.Add(typeId, snapshot);
}
snapshot.Take(relationStorage);
} }
private class ArchetypeSnapshot private class ArchetypeSnapshot
{ {
public ArchetypeSignature Signature;
private readonly Column[] ComponentColumns; private readonly Column[] ComponentColumns;
private readonly NativeArray<Id> RowToEntity; private readonly NativeArray<Id> RowToEntity;
@ -92,7 +146,6 @@ public class Snapshot
public ArchetypeSnapshot(ArchetypeSignature signature) public ArchetypeSnapshot(ArchetypeSignature signature)
{ {
Signature = signature;
ComponentColumns = new Column[signature.Count]; ComponentColumns = new Column[signature.Count];
RowToEntity = new NativeArray<Id>(); RowToEntity = new NativeArray<Id>();
@ -103,11 +156,6 @@ public class Snapshot
} }
} }
public void Clear()
{
RowToEntity.Clear();
}
public void Take(Archetype archetype) public void Take(Archetype archetype)
{ {
for (int i = 0; i < ComponentColumns.Length; i += 1) for (int i = 0; i < ComponentColumns.Length; i += 1)
@ -129,4 +177,54 @@ public class Snapshot
RowToEntity.CopyTo(archetype.RowToEntity); RowToEntity.CopyTo(archetype.RowToEntity);
} }
} }
private class RelationSnapshot
{
private Column Relations;
private Column RelationDatas;
public RelationSnapshot(int elementSize)
{
Relations = new Column(Unsafe.SizeOf<(Id, Id)>());
RelationDatas = new Column(elementSize);
}
public void Take(RelationStorage relationStorage)
{
relationStorage.relations.CopyAllTo(Relations);
relationStorage.relationDatas.CopyAllTo(RelationDatas);
}
public void Restore(RelationStorage relationStorage)
{
relationStorage.Clear();
Relations.CopyAllTo(relationStorage.relations);
RelationDatas.CopyAllTo(relationStorage.relationDatas);
for (int index = 0; index < Relations.Count; index += 1)
{
var relation = Relations.Get<(Id, Id)>(index);
relationStorage.indices[relation] = index;
relationStorage.indices[relation] = index;
if (!relationStorage.outRelations.ContainsKey(relation.Item1))
{
relationStorage.outRelations[relation.Item1] =
relationStorage.AcquireHashSetFromPool();
}
relationStorage.outRelations[relation.Item1].Add(relation.Item2);
if (!relationStorage.inRelations.ContainsKey(relation.Item2))
{
relationStorage.inRelations[relation.Item2] =
relationStorage.AcquireHashSetFromPool();
}
relationStorage.inRelations[relation.Item2].Add(relation.Item1);
}
}
}
} }

View File

@ -1,13 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using MoonTools.ECS.Collections;
namespace MoonTools.ECS.Rev2 namespace MoonTools.ECS.Rev2
{ {
public class World : IDisposable public class World : IDisposable
{ {
// Get ComponentId from a Type // Get ComponentId from a Type
internal static Dictionary<Type, Id> TypeToComponentId = new Dictionary<Type, Id>(); internal static Dictionary<Type, Id> TypeToId = new Dictionary<Type, Id>();
// Get element size from a ComponentId // Get element size from a ComponentId
internal static Dictionary<Id, int> ElementSizes = new Dictionary<Id, int>(); internal static Dictionary<Id, int> ElementSizes = new Dictionary<Id, int>();
@ -20,7 +22,16 @@ namespace MoonTools.ECS.Rev2
// Going from ComponentId to Archetype list // Going from ComponentId to Archetype list
Dictionary<Id, List<Archetype>> ComponentIndex = new Dictionary<Id, List<Archetype>>(); Dictionary<Id, List<Archetype>> ComponentIndex = new Dictionary<Id, List<Archetype>>();
// Relation Storages
internal Dictionary<Id, RelationStorage> RelationIndex =
new Dictionary<Id, RelationStorage>();
// Entity Relation Tracking
internal Dictionary<Id, IndexableSet<Id>> EntityRelationIndex =
new Dictionary<Id, IndexableSet<Id>>();
// ID Management // ID Management
// FIXME: Entity and Type Ids should be separated
internal IdAssigner IdAssigner = new IdAssigner(); internal IdAssigner IdAssigner = new IdAssigner();
internal readonly Archetype EmptyArchetype; internal readonly Archetype EmptyArchetype;
@ -59,58 +70,66 @@ namespace MoonTools.ECS.Rev2
var entityId = IdAssigner.Assign(); var entityId = IdAssigner.Assign();
EntityIndex.Add(entityId, new Record(EmptyArchetype, EmptyArchetype.Count)); EntityIndex.Add(entityId, new Record(EmptyArchetype, EmptyArchetype.Count));
EmptyArchetype.RowToEntity.Add(entityId); EmptyArchetype.RowToEntity.Add(entityId);
if (!EntityRelationIndex.ContainsKey(entityId))
{
EntityRelationIndex.Add(entityId, new IndexableSet<Id>());
}
return entityId; return entityId;
} }
// used as a fast path by snapshot restore internal void TryRegisterTypeId<T>() where T : unmanaged
internal void CreateEntityOnArchetype(Archetype archetype)
{ {
var entityId = IdAssigner.Assign(); if (!TypeToId.ContainsKey(typeof(T)))
archetype.RowToEntity.Add(entityId); {
var typeId = IdAssigner.Assign();
TypeToId.Add(typeof(T), typeId);
ElementSizes.Add(typeId, Unsafe.SizeOf<T>());
} }
// used as a fast path by Archetype.ClearAll and snapshot restore
internal void FreeEntity(Id entityId)
{
EntityIndex.Remove(entityId);
IdAssigner.Unassign(entityId);
}
private void RegisterTypeId(Id typeId, int elementSize)
{
ComponentIndex.Add(typeId, new List<Archetype>());
ElementSizes.Add(typeId, elementSize);
} }
// FIXME: would be much more efficient to do all this at load time somehow // FIXME: would be much more efficient to do all this at load time somehow
private void RegisterComponent<T>() where T : unmanaged private void RegisterComponent(Id typeId)
{ {
var componentId = IdAssigner.Assign(); ComponentIndex.Add(typeId, new List<Archetype>());
TypeToComponentId.Add(typeof(T), componentId);
ComponentIndex.Add(componentId, new List<Archetype>());
ElementSizes.Add(componentId, Unsafe.SizeOf<T>());
}
private void TryRegisterTypeId(Id typeId, int elementSize)
{
if (!ComponentIndex.ContainsKey(typeId))
{
RegisterTypeId(typeId, elementSize);
}
} }
private void TryRegisterComponentId<T>() where T : unmanaged private void TryRegisterComponentId<T>() where T : unmanaged
{ {
if (!TypeToComponentId.ContainsKey(typeof(T))) TryRegisterTypeId<T>();
var typeId = TypeToId[typeof(T)];
if (!ComponentIndex.ContainsKey(typeId))
{ {
RegisterComponent<T>(); RegisterComponent(typeId);
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private Id GetComponentId<T>() where T : unmanaged private Id GetComponentId<T>() where T : unmanaged
{ {
return TypeToComponentId[typeof(T)]; return TypeToId[typeof(T)];
}
private void RegisterRelationType(Id typeId)
{
RelationIndex.Add(typeId, new RelationStorage(ElementSizes[typeId]));
}
private void TryRegisterRelationType<T>() where T : unmanaged
{
TryRegisterTypeId<T>();
var typeId = TypeToId[typeof(T)];
if (!RelationIndex.ContainsKey(typeId))
{
RegisterRelationType(typeId);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private RelationStorage GetRelationStorage<T>() where T : unmanaged
{
return RelationIndex[TypeToId[typeof(T)]];
} }
public bool Has<T>(Id entityId) where T : unmanaged public bool Has<T>(Id entityId) where T : unmanaged
@ -222,43 +241,49 @@ namespace MoonTools.ECS.Rev2
MoveEntityToLowerArchetype(entityId, row, archetype, nextArchetype, componentId); MoveEntityToLowerArchetype(entityId, row, archetype, nextArchetype, componentId);
} }
private Id Pair(in Id relation, in Id target)
{
return new Id(relation.Low, target.Low);
}
public void Relate<T>(in Id entityA, in Id entityB, in T relation) where T : unmanaged public void Relate<T>(in Id entityA, in Id entityB, in T relation) where T : unmanaged
{ {
TryRegisterComponentId<T>(); TryRegisterRelationType<T>();
var relationDataTypeId = GetComponentId<T>(); var relationStorage = GetRelationStorage<T>();
relationStorage.Set(entityA, entityB, relation);
EntityRelationIndex[entityA].Add(TypeToId[typeof(T)]);
EntityRelationIndex[entityB].Add(TypeToId[typeof(T)]);
}
var typeId = Pair(relationDataTypeId, entityB); public void Unrelate<T>(in Id entityA, in Id entityB) where T : unmanaged
{
TryRegisterTypeId(typeId, Unsafe.SizeOf<T>()); var relationStorage = GetRelationStorage<T>();
SetRelationData(entityA, typeId, relation); relationStorage.Remove(entityA, entityB);
} }
public bool Related<T>(in Id entityA, in Id entityB) where T : unmanaged public bool Related<T>(in Id entityA, in Id entityB) where T : unmanaged
{ {
var relationDataTypeId = GetComponentId<T>(); var relationStorage = GetRelationStorage<T>();
var typeId = Pair(relationDataTypeId, entityB); return relationStorage.Has(entityA, entityB);
return Has(entityA, typeId);
} }
public T GetRelationData<T>(in Id entityA, in Id entityB) where T : unmanaged public T GetRelationData<T>(in Id entityA, in Id entityB) where T : unmanaged
{ {
var relationDataTypeId = GetComponentId<T>(); var relationStorage = GetRelationStorage<T>();
var typeId = Pair(relationDataTypeId, entityB); return relationStorage.Get<T>(entityA, entityB);
return Get<T>(entityA, typeId);
} }
private unsafe ref T Get<T>(Id entityId, Id typeId) where T : unmanaged public ReverseSpanEnumerator<(Id, Id)> Relations<T>() where T : unmanaged
{ {
var record = EntityIndex[entityId]; var relationStorage = GetRelationStorage<T>();
var columnIndex = record.Archetype.ComponentToColumnIndex[typeId]; return relationStorage.All();
var column = record.Archetype.ComponentColumns[columnIndex]; }
return ref ((T*) column.Elements)[record.Row]; public ReverseSpanEnumerator<Id> OutRelations<T>(Id entity) where T : unmanaged
{
var relationStorage = GetRelationStorage<T>();
return relationStorage.OutRelations(entity);
}
public ReverseSpanEnumerator<Id> InRelations<T>(Id entity) where T : unmanaged
{
var relationStorage = GetRelationStorage<T>();
return relationStorage.InRelations(entity);
} }
private bool Has(Id entityId, Id typeId) private bool Has(Id entityId, Id typeId)
@ -267,57 +292,19 @@ namespace MoonTools.ECS.Rev2
return record.Archetype.ComponentToColumnIndex.ContainsKey(typeId); return record.Archetype.ComponentToColumnIndex.ContainsKey(typeId);
} }
private void Add<T>(Id entityId, Id typeId, in T component) where T : unmanaged // used as a fast path by Archetype.ClearAll and snapshot restore
internal void FreeEntity(Id entityId)
{ {
Archetype? nextArchetype; EntityIndex.Remove(entityId);
IdAssigner.Unassign(entityId);
// move the entity to the new archetype foreach (var relationTypeIndex in EntityRelationIndex[entityId])
var record = EntityIndex[entityId];
var archetype = record.Archetype;
if (archetype.Edges.TryGetValue(typeId, out var edge))
{ {
nextArchetype = edge.Add; var relationStorage = RelationIndex[relationTypeIndex];
} relationStorage.RemoveEntity(entityId);
else
{
// FIXME: pool the signatures
var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1);
archetype.Signature.CopyTo(nextSignature);
nextSignature.Insert(typeId);
if (!ArchetypeIndex.TryGetValue(nextSignature, out nextArchetype))
{
nextArchetype = CreateArchetype(nextSignature);
} }
var newEdge = new ArchetypeEdge(nextArchetype, archetype); EntityRelationIndex[entityId].Clear();
archetype.Edges.Add(typeId, newEdge);
nextArchetype.Edges.Add(typeId, newEdge);
}
MoveEntityToHigherArchetype(entityId, record.Row, archetype, nextArchetype);
// add the new component to the new archetype
var columnIndex = nextArchetype.ComponentToColumnIndex[typeId];
var column = nextArchetype.ComponentColumns[columnIndex];
column.Append(component);
}
private unsafe void SetRelationData<T>(in Id entityId, in Id typeId, T data) where T : unmanaged
{
if (Has(entityId, typeId))
{
var record = EntityIndex[entityId];
var columnIndex = record.Archetype.ComponentToColumnIndex[typeId];
var column = record.Archetype.ComponentColumns[columnIndex];
((T*) column.Elements)[record.Row] = data;
}
else
{
Add(entityId, typeId, data);
}
} }
public void Destroy(Id entityId) public void Destroy(Id entityId)
@ -342,6 +329,14 @@ namespace MoonTools.ECS.Rev2
archetype.RowToEntity.RemoveLastElement(); archetype.RowToEntity.RemoveLastElement();
EntityIndex.Remove(entityId); EntityIndex.Remove(entityId);
IdAssigner.Unassign(entityId); IdAssigner.Unassign(entityId);
foreach (var relationTypeIndex in EntityRelationIndex[entityId])
{
var relationStorage = RelationIndex[relationTypeIndex];
relationStorage.RemoveEntity(entityId);
}
EntityRelationIndex[entityId].Clear();
} }
private void MoveEntityToHigherArchetype(Id entityId, int row, Archetype from, Archetype to) private void MoveEntityToHigherArchetype(Id entityId, int row, Archetype from, Archetype to)