snapshot refinements
parent
8061590195
commit
f628735025
|
@ -47,12 +47,6 @@ namespace MoonTools.ECS
|
||||||
return ref Lookup<TComponent>().Get(entityID);
|
return ref Lookup<TComponent>().Get(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// used for debugging and template instantiation
|
|
||||||
internal object UntypedGet(int entityID, int componentTypeIndex)
|
|
||||||
{
|
|
||||||
return storages[componentTypeIndex].UntypedGet(entityID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref readonly TComponent GetFirst<TComponent>() where TComponent : unmanaged
|
public ref readonly TComponent GetFirst<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ref Lookup<TComponent>().GetFirst();
|
return ref Lookup<TComponent>().GetFirst();
|
||||||
|
@ -63,10 +57,7 @@ namespace MoonTools.ECS
|
||||||
Lookup<TComponent>().Set(entityID, component);
|
Lookup<TComponent>().Set(entityID, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Set(int entityID, int componentTypeIndex, object component)
|
|
||||||
{
|
|
||||||
storages[componentTypeIndex].Set(entityID, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged
|
public Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
|
@ -99,8 +90,21 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to fill snapshot depot with correct storages
|
// these methods used to implement snapshots, templates, and debugging
|
||||||
public void FillMissingStorages(ComponentDepot other)
|
|
||||||
|
// FIXME: use unsafe pointers instead of object
|
||||||
|
internal object UntypedGet(int entityID, int componentTypeIndex)
|
||||||
|
{
|
||||||
|
return storages[componentTypeIndex].UntypedGet(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: use unsafe pointers instead of object
|
||||||
|
internal void Set(int entityID, int componentTypeIndex, object component)
|
||||||
|
{
|
||||||
|
storages[componentTypeIndex].Set(entityID, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateMissingStorages(ComponentDepot other)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < ComponentTypeIndices.Count; i += 1)
|
for (var i = 0; i < ComponentTypeIndices.Count; i += 1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace MoonTools.ECS
|
||||||
return EntityToComponentTypeIndices[entityID].Remove(componentTypeIndex);
|
return EntityToComponentTypeIndices[entityID].Remove(componentTypeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRelation(int entityID, int relationIndex)
|
public void AddRelationKind(int entityID, int relationIndex)
|
||||||
{
|
{
|
||||||
EntityToRelationTypeIndices[entityID].Add(relationIndex);
|
EntityToRelationTypeIndices[entityID].Add(relationIndex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,6 @@ namespace MoonTools.ECS
|
||||||
return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded);
|
return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GuidToInt(Guid guid)
|
|
||||||
{
|
|
||||||
return BitConverter.ToInt32(guid.ToByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
var hashcode = 1;
|
var hashcode = 1;
|
||||||
|
|
|
@ -94,6 +94,27 @@ namespace MoonTools.ECS
|
||||||
Check(entityID, ComponentTypeIndices.GetIndex<TComponent>());
|
Check(entityID, ComponentTypeIndices.GetIndex<TComponent>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CheckSatisfied(int entityID, FilterSignature filterSignature)
|
||||||
|
{
|
||||||
|
foreach (var type in filterSignature.Included)
|
||||||
|
{
|
||||||
|
if (!EntityStorage.HasComponent(entityID, type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in filterSignature.Excluded)
|
||||||
|
{
|
||||||
|
if (EntityStorage.HasComponent(entityID, type))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckFilter(int entityID, FilterSignature filterSignature)
|
private void CheckFilter(int entityID, FilterSignature filterSignature)
|
||||||
{
|
{
|
||||||
foreach (var type in filterSignature.Included)
|
foreach (var type in filterSignature.Included)
|
||||||
|
|
|
@ -51,11 +51,6 @@ namespace MoonTools.ECS
|
||||||
Lookup<TRelationKind>().UnrelateAll(entityID);
|
Lookup<TRelationKind>().UnrelateAll(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnrelateAll(int entityID, int relationStorageIndex)
|
|
||||||
{
|
|
||||||
storages[relationStorageIndex].UnrelateAll(entityID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged
|
public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TRelationKind>().All();
|
return Lookup<TRelationKind>().All();
|
||||||
|
@ -105,5 +100,49 @@ namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
return Lookup<TRelationKind>().InRelationCount(entityID);
|
return Lookup<TRelationKind>().InRelationCount(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// untyped methods used for destroying and snapshots
|
||||||
|
|
||||||
|
public void Set(int entityA, int entityB, int relationTypeIndex, object relationData)
|
||||||
|
{
|
||||||
|
storages[relationTypeIndex].Set(entityA, entityB, relationData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnrelateAll(int entityID, int relationTypeIndex)
|
||||||
|
{
|
||||||
|
storages[relationTypeIndex].UnrelateAll(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(int, object)> InRelations(int entityID, int relationTypeIndex)
|
||||||
|
{
|
||||||
|
return storages[relationTypeIndex].UntypedInRelations(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(int, object)> OutRelations(int entityID, int relationTypeIndex)
|
||||||
|
{
|
||||||
|
return storages[relationTypeIndex].UntypedOutRelations(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < RelationTypeIndices.Count; i += 1)
|
||||||
|
{
|
||||||
|
if (storages[i] != null)
|
||||||
|
{
|
||||||
|
storages[i].Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateMissingStorages(RelationDepot other)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < RelationTypeIndices.Count; i += 1)
|
||||||
|
{
|
||||||
|
if (storages[i] == null && other.storages[i] != null)
|
||||||
|
{
|
||||||
|
storages[i] = other.storages[i].CreateStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
internal abstract class RelationStorage
|
internal abstract class RelationStorage
|
||||||
{
|
{
|
||||||
|
public abstract void Set(int entityA, int entityB, object relationData);
|
||||||
public abstract void UnrelateAll(int entityID);
|
public abstract void UnrelateAll(int entityID);
|
||||||
|
public abstract IEnumerable<(int, object)> UntypedInRelations(int entityID);
|
||||||
|
public abstract IEnumerable<(int, object)> UntypedOutRelations(int entityID);
|
||||||
|
// used to create correctly typed storage on snapshot
|
||||||
|
public abstract RelationStorage CreateStorage();
|
||||||
|
public abstract void Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relation is the two entities, A related to B.
|
// Relation is the two entities, A related to B.
|
||||||
|
@ -185,6 +190,29 @@ namespace MoonTools.ECS
|
||||||
return (aEmpty, bEmpty);
|
return (aEmpty, bEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IndexableSet<int> AcquireHashSetFromPool()
|
||||||
|
{
|
||||||
|
if (listPool.Count == 0)
|
||||||
|
{
|
||||||
|
listPool.Push(new IndexableSet<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return listPool.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReturnHashSetToPool(IndexableSet<int> hashSet)
|
||||||
|
{
|
||||||
|
hashSet.Clear();
|
||||||
|
listPool.Push(hashSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// untyped methods used for internal implementation
|
||||||
|
|
||||||
|
public override void Set(int entityA, int entityB, object relationData)
|
||||||
|
{
|
||||||
|
Set(new Relation(entityA, entityB), (TRelation) relationData);
|
||||||
|
}
|
||||||
|
|
||||||
public override void UnrelateAll(int entityID)
|
public override void UnrelateAll(int entityID)
|
||||||
{
|
{
|
||||||
if (outRelations.ContainsKey(entityID))
|
if (outRelations.ContainsKey(entityID))
|
||||||
|
@ -210,20 +238,43 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexableSet<int> AcquireHashSetFromPool()
|
public override IEnumerable<(int, object)> UntypedInRelations(int entityID)
|
||||||
{
|
{
|
||||||
if (listPool.Count == 0)
|
foreach (var (entity, relationData) in InRelations(entityID))
|
||||||
{
|
{
|
||||||
listPool.Push(new IndexableSet<int>());
|
yield return (entity.ID, relationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return listPool.Pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReturnHashSetToPool(IndexableSet<int> hashSet)
|
public override IEnumerable<(int, object)> UntypedOutRelations(int entityID)
|
||||||
{
|
{
|
||||||
hashSet.Clear();
|
foreach (var (entity, relationData) in OutRelations(entityID))
|
||||||
listPool.Push(hashSet);
|
{
|
||||||
|
yield return (entity.ID, relationData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RelationStorage<TRelation> CreateStorage()
|
||||||
|
{
|
||||||
|
return new RelationStorage<TRelation>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Clear()
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
indices.Clear();
|
||||||
|
|
||||||
|
foreach (var set in inRelations.Values)
|
||||||
|
{
|
||||||
|
ReturnHashSetToPool(set);
|
||||||
|
}
|
||||||
|
inRelations.Clear();
|
||||||
|
|
||||||
|
foreach (var set in outRelations.Values)
|
||||||
|
{
|
||||||
|
ReturnHashSetToPool(set);
|
||||||
|
}
|
||||||
|
outRelations.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
public class Snapshot
|
public class Snapshot
|
||||||
|
@ -7,29 +10,71 @@ namespace MoonTools.ECS
|
||||||
|
|
||||||
private EntityStorage SnapshotEntityStorage;
|
private EntityStorage SnapshotEntityStorage;
|
||||||
private ComponentDepot SnapshotComponentDepot;
|
private ComponentDepot SnapshotComponentDepot;
|
||||||
|
private RelationDepot SnapshotRelationDepot;
|
||||||
|
|
||||||
|
private List<int> SnapshotToWorldID = new List<int>();
|
||||||
|
private Dictionary<int, int> WorldToSnapshotID = new Dictionary<int, int>();
|
||||||
|
|
||||||
internal Snapshot(World world)
|
internal Snapshot(World world)
|
||||||
{
|
{
|
||||||
World = world;
|
World = world;
|
||||||
SnapshotEntityStorage = new EntityStorage();
|
SnapshotEntityStorage = new EntityStorage();
|
||||||
SnapshotComponentDepot = new ComponentDepot(World.ComponentTypeIndices);
|
SnapshotComponentDepot = new ComponentDepot(World.ComponentTypeIndices);
|
||||||
|
SnapshotRelationDepot = new RelationDepot(World.RelationTypeIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Take(Filter filter)
|
public void Take(Filter filter)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
Filter = filter;
|
Filter = filter;
|
||||||
SnapshotComponentDepot.FillMissingStorages(World.ComponentDepot);
|
SnapshotComponentDepot.CreateMissingStorages(World.ComponentDepot);
|
||||||
|
SnapshotRelationDepot.CreateMissingStorages(World.RelationDepot);
|
||||||
|
|
||||||
foreach (var worldEntity in filter.Entities)
|
foreach (var worldEntity in filter.Entities)
|
||||||
{
|
{
|
||||||
var snapshotEntity = SnapshotEntityStorage.Create();
|
var snapshotEntity = SnapshotEntityStorage.Create();
|
||||||
|
WorldToSnapshotID.Add(worldEntity.ID, snapshotEntity.ID);
|
||||||
|
|
||||||
foreach (var componentTypeIndex in World.EntityStorage.ComponentTypeIndices(worldEntity.ID))
|
foreach (var componentTypeIndex in World.EntityStorage.ComponentTypeIndices(worldEntity.ID))
|
||||||
{
|
{
|
||||||
SnapshotEntityStorage.SetComponent(snapshotEntity.ID, componentTypeIndex);
|
SnapshotEntityStorage.SetComponent(snapshotEntity.ID, componentTypeIndex);
|
||||||
SnapshotComponentDepot.Set(snapshotEntity.ID, componentTypeIndex, World.ComponentDepot.UntypedGet(worldEntity.ID, componentTypeIndex));
|
SnapshotComponentDepot.Set(snapshotEntity.ID, componentTypeIndex, World.ComponentDepot.UntypedGet(worldEntity.ID, componentTypeIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var worldEntity in filter.Entities)
|
||||||
|
{
|
||||||
|
var snapshotEntityID = WorldToSnapshotID[worldEntity.ID];
|
||||||
|
|
||||||
|
foreach (var relationTypeIndex in World.EntityStorage.RelationTypeIndices(worldEntity.ID))
|
||||||
|
{
|
||||||
|
SnapshotEntityStorage.AddRelationKind(snapshotEntityID, relationTypeIndex);
|
||||||
|
|
||||||
|
foreach (var (otherEntityID, relationData) in World.RelationDepot.InRelations(worldEntity.ID, relationTypeIndex))
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (!World.FilterStorage.CheckSatisfied(otherEntityID, Filter.Signature))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Snapshot entity {worldEntity.ID} is related to non-snapshot entity {otherEntityID}!");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
var otherSnapshotID = WorldToSnapshotID[otherEntityID];
|
||||||
|
SnapshotRelationDepot.Set(otherSnapshotID, snapshotEntityID, relationTypeIndex, relationData);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (otherEntityID, relationData) in World.RelationDepot.OutRelations(worldEntity.ID, relationTypeIndex))
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (!World.FilterStorage.CheckSatisfied(otherEntityID, Filter.Signature))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Snapshot entity {worldEntity.ID} is related to non-snapshot entity {otherEntityID}!");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
var otherSnapshotID = WorldToSnapshotID[otherEntityID];
|
||||||
|
SnapshotRelationDepot.Set(snapshotEntityID, otherSnapshotID, relationTypeIndex, relationData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Restore()
|
public void Restore()
|
||||||
|
@ -47,6 +92,7 @@ namespace MoonTools.ECS
|
||||||
for (var i = 0; i < SnapshotEntityStorage.Count; i += 1)
|
for (var i = 0; i < SnapshotEntityStorage.Count; i += 1)
|
||||||
{
|
{
|
||||||
var entity = World.CreateEntity();
|
var entity = World.CreateEntity();
|
||||||
|
SnapshotToWorldID.Add(entity.ID);
|
||||||
|
|
||||||
foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i))
|
foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i))
|
||||||
{
|
{
|
||||||
|
@ -55,12 +101,37 @@ namespace MoonTools.ECS
|
||||||
World.ComponentDepot.Set(entity.ID, componentTypeIndex, SnapshotComponentDepot.UntypedGet(i, componentTypeIndex));
|
World.ComponentDepot.Set(entity.ID, componentTypeIndex, SnapshotComponentDepot.UntypedGet(i, componentTypeIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < SnapshotEntityStorage.Count; i += 1)
|
||||||
|
{
|
||||||
|
var worldID = SnapshotToWorldID[i];
|
||||||
|
|
||||||
|
foreach (var relationTypeIndex in SnapshotEntityStorage.RelationTypeIndices(i))
|
||||||
|
{
|
||||||
|
World.EntityStorage.AddRelationKind(worldID, relationTypeIndex);
|
||||||
|
|
||||||
|
foreach (var (otherEntityID, relationData) in SnapshotRelationDepot.InRelations(i, relationTypeIndex))
|
||||||
|
{
|
||||||
|
var otherEntityWorldID = SnapshotToWorldID[otherEntityID];
|
||||||
|
World.RelationDepot.Set(otherEntityWorldID, worldID, relationTypeIndex, relationData);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (otherEntityID, relationData) in SnapshotRelationDepot.OutRelations(i, relationTypeIndex))
|
||||||
|
{
|
||||||
|
var otherEntityWorldID = SnapshotToWorldID[otherEntityID];
|
||||||
|
World.RelationDepot.Set(worldID, otherEntityWorldID, relationTypeIndex, relationData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Clear()
|
private void Clear()
|
||||||
{
|
{
|
||||||
SnapshotEntityStorage.Clear();
|
SnapshotEntityStorage.Clear();
|
||||||
SnapshotComponentDepot.Clear();
|
SnapshotComponentDepot.Clear();
|
||||||
|
SnapshotRelationDepot.Clear();
|
||||||
|
SnapshotToWorldID.Clear();
|
||||||
|
WorldToSnapshotID.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,8 @@ namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
RelationDepot.Set<TRelationKind>(new Relation(entityA, entityB), relationData);
|
RelationDepot.Set<TRelationKind>(new Relation(entityA, entityB), relationData);
|
||||||
var relationTypeIndex = RelationTypeIndices.GetIndex<TRelationKind>();
|
var relationTypeIndex = RelationTypeIndices.GetIndex<TRelationKind>();
|
||||||
EntityStorage.AddRelation(entityA.ID, relationTypeIndex);
|
EntityStorage.AddRelationKind(entityA.ID, relationTypeIndex);
|
||||||
EntityStorage.AddRelation(entityB.ID, relationTypeIndex);
|
EntityStorage.AddRelationKind(entityB.ID, relationTypeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged
|
protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged
|
||||||
|
|
Loading…
Reference in New Issue