unifying Ids and adding Relations
parent
344a0082b4
commit
545637aaf3
|
@ -7,11 +7,11 @@ internal class Archetype
|
|||
public World World;
|
||||
public ArchetypeSignature Signature;
|
||||
public List<Column> ComponentColumns = new List<Column>();
|
||||
public List<EntityId> RowToEntity = new List<EntityId>();
|
||||
public List<Id> RowToEntity = new List<Id>();
|
||||
|
||||
public Dictionary<ComponentId, int> ComponentToColumnIndex =
|
||||
new Dictionary<ComponentId, int>();
|
||||
public SortedDictionary<ComponentId, ArchetypeEdge> Edges = new SortedDictionary<ComponentId, ArchetypeEdge>();
|
||||
public Dictionary<Id, int> ComponentToColumnIndex =
|
||||
new Dictionary<Id, int>();
|
||||
public SortedDictionary<Id, ArchetypeEdge> Edges = new SortedDictionary<Id, ArchetypeEdge>();
|
||||
|
||||
public int Count => RowToEntity.Count;
|
||||
|
||||
|
|
|
@ -7,36 +7,36 @@ namespace MoonTools.ECS.Rev2
|
|||
{
|
||||
public static ArchetypeSignature Empty = new ArchetypeSignature(0);
|
||||
|
||||
List<int> Ids;
|
||||
List<ulong> Ids;
|
||||
|
||||
public int Count => Ids.Count;
|
||||
|
||||
public ComponentId this[int i] => new ComponentId(Ids[i]);
|
||||
public Id this[int i] => new Id(Ids[i]);
|
||||
|
||||
public ArchetypeSignature()
|
||||
{
|
||||
Ids = new List<int>();
|
||||
Ids = new List<ulong>();
|
||||
}
|
||||
|
||||
public ArchetypeSignature(int capacity)
|
||||
{
|
||||
Ids = new List<int>(capacity);
|
||||
Ids = new List<ulong>(capacity);
|
||||
}
|
||||
|
||||
// Maintains sorted order
|
||||
public void Insert(ComponentId componentId)
|
||||
public void Insert(Id componentId)
|
||||
{
|
||||
var index = Ids.BinarySearch(componentId.Id);
|
||||
var index = Ids.BinarySearch(componentId.Value);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
Ids.Insert(~index, componentId.Id);
|
||||
Ids.Insert(~index, componentId.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(ComponentId componentId)
|
||||
public void Remove(Id componentId)
|
||||
{
|
||||
var index = Ids.BinarySearch(componentId.Id);
|
||||
var index = Ids.BinarySearch(componentId.Value);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MoonTools.ECS.Rev2
|
||||
{
|
||||
internal readonly record struct ComponentId(int Id) : IHasId, IComparable<ComponentId>
|
||||
{
|
||||
public int CompareTo(ComponentId other)
|
||||
{
|
||||
return Id.CompareTo(other.Id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
namespace MoonTools.ECS.Rev2;
|
||||
|
||||
public readonly record struct Entity(uint Id);
|
|
@ -1,4 +0,0 @@
|
|||
namespace MoonTools.ECS.Rev2
|
||||
{
|
||||
public readonly record struct EntityId(int Id) : IHasId;
|
||||
}
|
|
@ -7,8 +7,8 @@ namespace MoonTools.ECS.Rev2
|
|||
public class Filter
|
||||
{
|
||||
private Archetype EmptyArchetype;
|
||||
private HashSet<ComponentId> Included;
|
||||
private HashSet<ComponentId> Excluded;
|
||||
private HashSet<Id> Included;
|
||||
private HashSet<Id> Excluded;
|
||||
|
||||
public EntityEnumerator Entities => new EntityEnumerator(this);
|
||||
internal ArchetypeEnumerator Archetypes => new ArchetypeEnumerator(this);
|
||||
|
@ -47,7 +47,7 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
}
|
||||
|
||||
public EntityId RandomEntity
|
||||
public Id RandomEntity
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
|
||||
// WARNING: this WILL crash if the index is out of range!
|
||||
public EntityId NthEntity(int index)
|
||||
public Id NthEntity(int index)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
|
@ -93,7 +93,7 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
}
|
||||
|
||||
internal Filter(Archetype emptyArchetype, HashSet<ComponentId> included, HashSet<ComponentId> excluded)
|
||||
internal Filter(Archetype emptyArchetype, HashSet<Id> included, HashSet<Id> excluded)
|
||||
{
|
||||
EmptyArchetype = emptyArchetype;
|
||||
Included = included;
|
||||
|
@ -165,12 +165,12 @@ namespace MoonTools.ECS.Rev2
|
|||
|
||||
public ref struct EntityEnumerator
|
||||
{
|
||||
private EntityId CurrentEntity;
|
||||
private Id CurrentEntity;
|
||||
|
||||
public EntityEnumerator GetEnumerator() => this;
|
||||
|
||||
// TODO: pool this
|
||||
Queue<EntityId> EntityQueue = new Queue<EntityId>();
|
||||
Queue<Id> EntityQueue = new Queue<Id>();
|
||||
|
||||
internal EntityEnumerator(Filter filter)
|
||||
{
|
||||
|
@ -190,7 +190,7 @@ namespace MoonTools.ECS.Rev2
|
|||
return EntityQueue.TryDequeue(out CurrentEntity);
|
||||
}
|
||||
|
||||
public EntityId Current => CurrentEntity;
|
||||
public Id Current => CurrentEntity;
|
||||
}
|
||||
|
||||
public ref struct RandomEntityEnumerator
|
||||
|
@ -208,7 +208,7 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
|
||||
public bool MoveNext() => LinearCongruentialEnumerator.MoveNext();
|
||||
public EntityId Current => Filter.NthEntity(LinearCongruentialEnumerator.Current);
|
||||
public Id Current => Filter.NthEntity(LinearCongruentialEnumerator.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,17 @@ namespace MoonTools.ECS.Rev2
|
|||
public ref struct FilterBuilder
|
||||
{
|
||||
World World;
|
||||
HashSet<ComponentId> Included;
|
||||
HashSet<ComponentId> Excluded;
|
||||
HashSet<Id> Included;
|
||||
HashSet<Id> Excluded;
|
||||
|
||||
internal FilterBuilder(World world)
|
||||
{
|
||||
World = world;
|
||||
Included = new HashSet<ComponentId>();
|
||||
Excluded = new HashSet<ComponentId>();
|
||||
Included = new HashSet<Id>();
|
||||
Excluded = new HashSet<Id>();
|
||||
}
|
||||
|
||||
private FilterBuilder(World world, HashSet<ComponentId> included, HashSet<ComponentId> excluded)
|
||||
private FilterBuilder(World world, HashSet<Id> included, HashSet<Id> excluded)
|
||||
{
|
||||
World = world;
|
||||
Included = included;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace MoonTools.ECS.Rev2;
|
||||
|
||||
public readonly record struct Id : 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)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@ using System.Collections.Generic;
|
|||
|
||||
namespace MoonTools.ECS.Rev2
|
||||
{
|
||||
internal class IdAssigner<T> where T : struct, IHasId
|
||||
internal class IdAssigner
|
||||
{
|
||||
int Next;
|
||||
Queue<int> AvailableIds = new Queue<int>();
|
||||
ulong Next;
|
||||
Queue<ulong> AvailableIds = new Queue<ulong>();
|
||||
|
||||
public T Assign()
|
||||
public Id Assign()
|
||||
{
|
||||
if (!AvailableIds.TryDequeue(out var id))
|
||||
{
|
||||
|
@ -15,12 +15,12 @@ namespace MoonTools.ECS.Rev2
|
|||
Next += 1;
|
||||
}
|
||||
|
||||
return new T { Id = id };
|
||||
return new Id(id);
|
||||
}
|
||||
|
||||
public void Unassign(T idHaver)
|
||||
public void Unassign(Id id)
|
||||
{
|
||||
AvailableIds.Enqueue(idHaver.Id);
|
||||
AvailableIds.Enqueue(id.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,22 +8,21 @@ namespace MoonTools.ECS.Rev2
|
|||
public class World : IDisposable
|
||||
{
|
||||
// Get ComponentId from a Type
|
||||
internal static Dictionary<Type, ComponentId> TypeToComponentId = new Dictionary<Type, ComponentId>();
|
||||
internal static Dictionary<Type, Id> TypeToComponentId = new Dictionary<Type, Id>();
|
||||
// Get element size from a ComponentId
|
||||
internal static Dictionary<ComponentId, int> ElementSizes = new Dictionary<ComponentId, int>();
|
||||
internal static Dictionary<Id, int> ElementSizes = new Dictionary<Id, int>();
|
||||
|
||||
// Lookup from ArchetypeSignature to Archetype
|
||||
internal Dictionary<ArchetypeSignature, Archetype> ArchetypeIndex = new Dictionary<ArchetypeSignature, Archetype>();
|
||||
|
||||
// Going from EntityId to Archetype and storage row
|
||||
Dictionary<EntityId, Record> EntityIndex = new Dictionary<EntityId, Record>();
|
||||
Dictionary<Id, Record> EntityIndex = new Dictionary<Id, Record>();
|
||||
|
||||
// Going from ComponentId to Archetype list
|
||||
Dictionary<ComponentId, List<Archetype>> ComponentIndex = new Dictionary<ComponentId, List<Archetype>>();
|
||||
Dictionary<Id, List<Archetype>> ComponentIndex = new Dictionary<Id, List<Archetype>>();
|
||||
|
||||
// ID Management
|
||||
IdAssigner<EntityId> EntityIdAssigner = new IdAssigner<EntityId>();
|
||||
IdAssigner<ComponentId> ComponentIdAssigner = new IdAssigner<ComponentId>();
|
||||
IdAssigner IdAssigner = new IdAssigner();
|
||||
|
||||
internal readonly Archetype EmptyArchetype;
|
||||
|
||||
|
@ -59,9 +58,9 @@ namespace MoonTools.ECS.Rev2
|
|||
return archetype;
|
||||
}
|
||||
|
||||
public EntityId CreateEntity()
|
||||
public Id CreateEntity()
|
||||
{
|
||||
var entityId = EntityIdAssigner.Assign();
|
||||
var entityId = IdAssigner.Assign();
|
||||
EntityIndex.Add(entityId, new Record(EmptyArchetype, EmptyArchetype.Count));
|
||||
EmptyArchetype.RowToEntity.Add(entityId);
|
||||
return entityId;
|
||||
|
@ -70,27 +69,41 @@ namespace MoonTools.ECS.Rev2
|
|||
// used as a fast path by snapshot restore
|
||||
internal void CreateEntityOnArchetype(Archetype archetype)
|
||||
{
|
||||
var entityId = EntityIdAssigner.Assign();
|
||||
var entityId = IdAssigner.Assign();
|
||||
EntityIndex.Add(entityId, new Record(archetype, archetype.Count));
|
||||
archetype.RowToEntity.Add(entityId);
|
||||
}
|
||||
|
||||
// used as a fast path by Archetype.ClearAll and snapshot restore
|
||||
internal void FreeEntity(EntityId entityId)
|
||||
internal void FreeEntity(Id entityId)
|
||||
{
|
||||
EntityIndex.Remove(entityId);
|
||||
EntityIdAssigner.Unassign(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
|
||||
private void RegisterComponent<T>() where T : unmanaged
|
||||
{
|
||||
var componentId = ComponentIdAssigner.Assign();
|
||||
var componentId = IdAssigner.Assign();
|
||||
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
|
||||
{
|
||||
if (!TypeToComponentId.ContainsKey(typeof(T)))
|
||||
|
@ -99,22 +112,13 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
}
|
||||
|
||||
// non-generic variant for use with Transfer
|
||||
internal void AddComponentIndexEntry(ComponentId componentId)
|
||||
{
|
||||
if (!ComponentIndex.ContainsKey(componentId))
|
||||
{
|
||||
ComponentIndex.Add(componentId, new List<Archetype>());
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ComponentId GetComponentId<T>() where T : unmanaged
|
||||
private Id GetComponentId<T>() where T : unmanaged
|
||||
{
|
||||
return TypeToComponentId[typeof(T)];
|
||||
}
|
||||
|
||||
public bool Has<T>(EntityId entityId) where T : unmanaged
|
||||
public bool Has<T>(Id entityId) where T : unmanaged
|
||||
{
|
||||
var componentId = GetComponentId<T>();
|
||||
var record = EntityIndex[entityId];
|
||||
|
@ -122,7 +126,7 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
|
||||
// will throw if non-existent
|
||||
public unsafe ref T Get<T>(EntityId entityId) where T : unmanaged
|
||||
public unsafe ref T Get<T>(Id entityId) where T : unmanaged
|
||||
{
|
||||
var componentId = GetComponentId<T>();
|
||||
|
||||
|
@ -133,7 +137,7 @@ namespace MoonTools.ECS.Rev2
|
|||
return ref ((T*) column.Elements)[record.Row];
|
||||
}
|
||||
|
||||
public unsafe void Set<T>(in EntityId entityId, in T component) where T : unmanaged
|
||||
public unsafe void Set<T>(in Id entityId, in T component) where T : unmanaged
|
||||
{
|
||||
TryRegisterComponentId<T>();
|
||||
var componentId = GetComponentId<T>();
|
||||
|
@ -152,7 +156,7 @@ namespace MoonTools.ECS.Rev2
|
|||
}
|
||||
}
|
||||
|
||||
private void Add<T>(EntityId entityId, in T component) where T : unmanaged
|
||||
private void Add<T>(Id entityId, in T component) where T : unmanaged
|
||||
{
|
||||
Archetype? nextArchetype;
|
||||
|
||||
|
@ -191,7 +195,7 @@ namespace MoonTools.ECS.Rev2
|
|||
column.Append(component);
|
||||
}
|
||||
|
||||
public void Remove<T>(EntityId entityId) where T : unmanaged
|
||||
public void Remove<T>(Id entityId) where T : unmanaged
|
||||
{
|
||||
Archetype? nextArchetype;
|
||||
|
||||
|
@ -223,7 +227,105 @@ namespace MoonTools.ECS.Rev2
|
|||
MoveEntityToLowerArchetype(entityId, row, archetype, nextArchetype, componentId);
|
||||
}
|
||||
|
||||
public void Destroy(EntityId entityId)
|
||||
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
|
||||
{
|
||||
TryRegisterComponentId<T>();
|
||||
var relationDataTypeId = GetComponentId<T>();
|
||||
|
||||
var typeId = Pair(relationDataTypeId, entityB);
|
||||
|
||||
TryRegisterTypeId(typeId, Unsafe.SizeOf<T>());
|
||||
SetRelationData(entityA, typeId, relation);
|
||||
}
|
||||
|
||||
public bool Related<T>(in Id entityA, in Id entityB) where T : unmanaged
|
||||
{
|
||||
var relationDataTypeId = GetComponentId<T>();
|
||||
var typeId = Pair(relationDataTypeId, entityB);
|
||||
return Has(entityA, typeId);
|
||||
}
|
||||
|
||||
public T GetRelationData<T>(in Id entityA, in Id entityB) where T : unmanaged
|
||||
{
|
||||
var relationDataTypeId = GetComponentId<T>();
|
||||
var typeId = Pair(relationDataTypeId, entityB);
|
||||
return Get<T>(entityA, typeId);
|
||||
}
|
||||
|
||||
private unsafe ref T Get<T>(Id entityId, Id typeId) where T : unmanaged
|
||||
{
|
||||
var record = EntityIndex[entityId];
|
||||
var columnIndex = record.Archetype.ComponentToColumnIndex[typeId];
|
||||
var column = record.Archetype.ComponentColumns[columnIndex];
|
||||
|
||||
return ref ((T*) column.Elements)[record.Row];
|
||||
}
|
||||
|
||||
private bool Has(Id entityId, Id typeId)
|
||||
{
|
||||
var record = EntityIndex[entityId];
|
||||
return record.Archetype.ComponentToColumnIndex.ContainsKey(typeId);
|
||||
}
|
||||
|
||||
private void Add<T>(Id entityId, Id typeId, in T component) where T : unmanaged
|
||||
{
|
||||
Archetype? nextArchetype;
|
||||
|
||||
// move the entity to the new archetype
|
||||
var record = EntityIndex[entityId];
|
||||
var archetype = record.Archetype;
|
||||
|
||||
if (archetype.Edges.TryGetValue(typeId, out var edge))
|
||||
{
|
||||
nextArchetype = edge.Add;
|
||||
}
|
||||
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);
|
||||
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)
|
||||
{
|
||||
var record = EntityIndex[entityId];
|
||||
var archetype = record.Archetype;
|
||||
|
@ -244,10 +346,10 @@ namespace MoonTools.ECS.Rev2
|
|||
|
||||
archetype.RowToEntity.RemoveAt(archetype.Count - 1);
|
||||
EntityIndex.Remove(entityId);
|
||||
EntityIdAssigner.Unassign(entityId);
|
||||
IdAssigner.Unassign(entityId);
|
||||
}
|
||||
|
||||
private void MoveEntityToHigherArchetype(EntityId entityId, int row, Archetype from, Archetype to)
|
||||
private void MoveEntityToHigherArchetype(Id entityId, int row, Archetype from, Archetype to)
|
||||
{
|
||||
for (int i = 0; i < from.ComponentColumns.Count; i += 1)
|
||||
{
|
||||
|
@ -276,7 +378,7 @@ namespace MoonTools.ECS.Rev2
|
|||
to.RowToEntity.Add(entityId);
|
||||
}
|
||||
|
||||
private void MoveEntityToLowerArchetype(EntityId entityId, int row, Archetype from, Archetype to, ComponentId removed)
|
||||
private void MoveEntityToLowerArchetype(Id entityId, int row, Archetype from, Archetype to, Id removed)
|
||||
{
|
||||
for (int i = 0; i < from.ComponentColumns.Count; i += 1)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue