World State Save and Load #2
|
@ -4,6 +4,7 @@
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -11,13 +11,13 @@ namespace MoonTools.ECS
|
||||||
|
|
||||||
private Dictionary<Type, HashSet<FilterSignature>> typeToFilterSignatures = new Dictionary<Type, HashSet<FilterSignature>>();
|
private Dictionary<Type, HashSet<FilterSignature>> typeToFilterSignatures = new Dictionary<Type, HashSet<FilterSignature>>();
|
||||||
|
|
||||||
private Dictionary<int, HashSet<Type>> entityComponentMap = new Dictionary<int, HashSet<Type>>();
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private Dictionary<Type, Filter> singleComponentFilters = new Dictionary<Type, Filter>();
|
private Dictionary<Type, Filter> singleComponentFilters = new Dictionary<Type, Filter>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
internal void Register<TComponent>() where TComponent : struct
|
private HashSet<Type> TypesWithDisabledSerialization = new HashSet<Type>();
|
||||||
|
|
||||||
|
internal void Register<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
if (!storages.ContainsKey(typeof(TComponent)))
|
if (!storages.ContainsKey(typeof(TComponent)))
|
||||||
{
|
{
|
||||||
|
@ -33,19 +33,19 @@ namespace MoonTools.ECS
|
||||||
return storages[type];
|
return storages[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : struct
|
private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
// TODO: is it possible to optimize this?
|
// TODO: is it possible to optimize this?
|
||||||
Register<TComponent>();
|
Register<TComponent>();
|
||||||
return (ComponentStorage<TComponent>) storages[typeof(TComponent)];
|
return (ComponentStorage<TComponent>) storages[typeof(TComponent)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Some<TComponent>() where TComponent : struct
|
public bool Some<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TComponent>().Any();
|
return Lookup<TComponent>().Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Has<TComponent>(int entityID) where TComponent : struct
|
public bool Has<TComponent>(int entityID) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TComponent>().Has(entityID);
|
return Lookup<TComponent>().Has(entityID);
|
||||||
}
|
}
|
||||||
|
@ -55,29 +55,22 @@ namespace MoonTools.ECS
|
||||||
return Lookup(type).Has(entityID);
|
return Lookup(type).Has(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref readonly TComponent Get<TComponent>(int entityID) where TComponent : struct
|
public ref readonly TComponent Get<TComponent>(int entityID) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ref Lookup<TComponent>().Get(entityID);
|
return ref Lookup<TComponent>().Get(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref readonly TComponent Get<TComponent>() where TComponent : struct
|
public ref readonly TComponent Get<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ref Lookup<TComponent>().Get();
|
return ref Lookup<TComponent>().Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct
|
public void Set<TComponent>(int entityID, in TComponent component) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
Lookup<TComponent>().Set(entityID, component);
|
var existed = Lookup<TComponent>().Set(entityID, component);
|
||||||
|
|
||||||
if (!entityComponentMap.ContainsKey(entityID))
|
|
||||||
{
|
|
||||||
entityComponentMap.Add(entityID, new HashSet<Type>());
|
|
||||||
}
|
|
||||||
|
|
||||||
var notFound = entityComponentMap[entityID].Add(typeof(TComponent));
|
|
||||||
|
|
||||||
// update filters
|
// update filters
|
||||||
if (notFound)
|
if (!existed)
|
||||||
{
|
{
|
||||||
if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures))
|
if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures))
|
||||||
{
|
{
|
||||||
|
@ -89,24 +82,22 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity GetSingletonEntity<TComponent>() where TComponent : struct
|
public Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TComponent>().FirstEntity();
|
return Lookup<TComponent>().FirstEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
|
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TComponent>().AllComponents();
|
return Lookup<TComponent>().AllComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Remove(Type type, int entityID)
|
private void Remove(Type type, int entityID)
|
||||||
{
|
{
|
||||||
Lookup(type).Remove(entityID);
|
var existed = Lookup(type).Remove(entityID);
|
||||||
|
|
||||||
var found = entityComponentMap[entityID].Remove(type);
|
|
||||||
|
|
||||||
// update filters
|
// update filters
|
||||||
if (found)
|
if (existed)
|
||||||
{
|
{
|
||||||
if (typeToFilterSignatures.TryGetValue(type, out var filterSignatures))
|
if (typeToFilterSignatures.TryGetValue(type, out var filterSignatures))
|
||||||
{
|
{
|
||||||
|
@ -118,14 +109,12 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove<TComponent>(int entityID) where TComponent : struct
|
public void Remove<TComponent>(int entityID) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
Lookup<TComponent>().Remove(entityID);
|
var existed = Lookup<TComponent>().Remove(entityID);
|
||||||
|
|
||||||
var found = entityComponentMap[entityID].Remove(typeof(TComponent));
|
|
||||||
|
|
||||||
// update filters
|
// update filters
|
||||||
if (found)
|
if (existed)
|
||||||
{
|
{
|
||||||
if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures))
|
if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures))
|
||||||
{
|
{
|
||||||
|
@ -137,16 +126,12 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: is there some way to optimize this without complicating serialization?
|
||||||
public void OnEntityDestroy(int entityID)
|
public void OnEntityDestroy(int entityID)
|
||||||
{
|
{
|
||||||
if (entityComponentMap.ContainsKey(entityID))
|
foreach (var type in storages.Keys)
|
||||||
{
|
{
|
||||||
foreach (var type in entityComponentMap[entityID])
|
Remove(type, entityID);
|
||||||
{
|
|
||||||
Remove(type, entityID);
|
|
||||||
}
|
|
||||||
|
|
||||||
entityComponentMap.Remove(entityID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +216,53 @@ namespace MoonTools.ECS
|
||||||
filterSignatureToEntityIDs[filterSignature].Add(entityID);
|
filterSignatureToEntityIDs[filterSignature].Add(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DisableSerialization<TComponent>() where TComponent : unmanaged
|
||||||
|
{
|
||||||
|
TypesWithDisabledSerialization.Add(typeof(TComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(ComponentDepotState state)
|
||||||
|
{
|
||||||
|
foreach (var (type, storage) in storages)
|
||||||
|
{
|
||||||
|
if (!TypesWithDisabledSerialization.Contains(type))
|
||||||
|
{
|
||||||
|
if (!state.StorageStates.ContainsKey(type))
|
||||||
|
{
|
||||||
|
state.StorageStates.Add(type, storage.CreateState());
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Save(state.StorageStates[type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (signature, set) in filterSignatureToEntityIDs)
|
||||||
|
{
|
||||||
|
// FIXME: we could cache this
|
||||||
|
if (!signature.Included.Overlaps(TypesWithDisabledSerialization) && !signature.Excluded.Overlaps(TypesWithDisabledSerialization))
|
||||||
|
{
|
||||||
|
if (!state.FilterStates.ContainsKey(signature))
|
||||||
|
{
|
||||||
|
state.FilterStates[signature] = new IndexableSetState<int>(set.Count);
|
||||||
|
}
|
||||||
|
set.Save(state.FilterStates[signature]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(ComponentDepotState state)
|
||||||
|
{
|
||||||
|
foreach (var (type, storageState) in state.StorageStates)
|
||||||
|
{
|
||||||
|
storages[type].Load(storageState);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (signature, setState) in state.FilterStates)
|
||||||
|
{
|
||||||
|
filterSignatureToEntityIDs[signature].Load(setState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public IEnumerable<object> Debug_GetAllComponents(int entityID)
|
public IEnumerable<object> Debug_GetAllComponents(int entityID)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
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 ComponentStorage
|
internal abstract class ComponentStorage
|
||||||
{
|
{
|
||||||
public abstract bool Has(int entityID);
|
public abstract bool Has(int entityID);
|
||||||
public abstract void Remove(int entityID);
|
public abstract bool Remove(int entityID);
|
||||||
public abstract object Debug_Get(int entityID);
|
public abstract object Debug_Get(int entityID);
|
||||||
|
public abstract ComponentStorageState CreateState();
|
||||||
|
public abstract void Save(ComponentStorageState state);
|
||||||
|
public abstract void Load(ComponentStorageState state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: we can probably get rid of this weird entity storage system by using filters
|
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : unmanaged
|
||||||
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct
|
|
||||||
{
|
{
|
||||||
private int nextID;
|
private int nextID;
|
||||||
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
|
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
|
||||||
|
@ -49,8 +52,11 @@ namespace MoonTools.ECS
|
||||||
return ref components[0];
|
return ref components[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set(int entityID, in TComponent component)
|
// Returns true if the entity already had this component.
|
||||||
|
public bool Set(int entityID, in TComponent component)
|
||||||
{
|
{
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
if (!entityIDToStorageIndex.ContainsKey(entityID))
|
if (!entityIDToStorageIndex.ContainsKey(entityID))
|
||||||
{
|
{
|
||||||
var index = nextID;
|
var index = nextID;
|
||||||
|
@ -64,12 +70,17 @@ namespace MoonTools.ECS
|
||||||
|
|
||||||
entityIDToStorageIndex[entityID] = index;
|
entityIDToStorageIndex[entityID] = index;
|
||||||
entityIDs[index] = entityID;
|
entityIDs[index] = entityID;
|
||||||
|
|
||||||
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
components[entityIDToStorageIndex[entityID]] = component;
|
components[entityIDToStorageIndex[entityID]] = component;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Remove(int entityID)
|
// Returns true if the entity had this component.
|
||||||
|
public override bool Remove(int entityID)
|
||||||
{
|
{
|
||||||
if (entityIDToStorageIndex.ContainsKey(entityID))
|
if (entityIDToStorageIndex.ContainsKey(entityID))
|
||||||
{
|
{
|
||||||
|
@ -88,7 +99,11 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
|
|
||||||
nextID -= 1;
|
nextID -= 1;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
@ -106,5 +121,44 @@ namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
return new Entity(entityIDs[0]);
|
return new Entity(entityIDs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override ComponentStorageState CreateState()
|
||||||
|
{
|
||||||
|
return ComponentStorageState.Create<TComponent>(nextID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Save(ComponentStorageState state)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> entityIDBytes = MemoryMarshal.Cast<int, byte>(new ReadOnlySpan<int>(entityIDs, 0, nextID));
|
||||||
|
|
||||||
|
if (entityIDBytes.Length > state.EntityIDs.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref state.EntityIDs, entityIDBytes.Length);
|
||||||
|
}
|
||||||
|
entityIDBytes.CopyTo(state.EntityIDs);
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> componentBytes = MemoryMarshal.Cast<TComponent, byte>(AllComponents());
|
||||||
|
if (componentBytes.Length > state.Components.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref state.Components, componentBytes.Length);
|
||||||
|
}
|
||||||
|
componentBytes.CopyTo(state.Components);
|
||||||
|
|
||||||
|
state.Count = nextID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(ComponentStorageState state)
|
||||||
|
{
|
||||||
|
state.EntityIDs.CopyTo(MemoryMarshal.Cast<int, byte>(entityIDs));
|
||||||
|
state.Components.CopyTo(MemoryMarshal.Cast<TComponent, byte>(components));
|
||||||
|
|
||||||
|
entityIDToStorageIndex.Clear();
|
||||||
|
for (var i = 0; i < state.Count; i += 1)
|
||||||
|
{
|
||||||
|
entityIDToStorageIndex[entityIDs[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextID = state.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,52 +5,43 @@ namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
public abstract class EntityComponentReader
|
public abstract class EntityComponentReader
|
||||||
{
|
{
|
||||||
internal EntityStorage EntityStorage;
|
protected readonly World World;
|
||||||
internal ComponentDepot ComponentDepot;
|
internal EntityStorage EntityStorage => World.EntityStorage;
|
||||||
internal RelationDepot RelationDepot;
|
internal ComponentDepot ComponentDepot => World.ComponentDepot;
|
||||||
|
internal RelationDepot RelationDepot => World.RelationDepot;
|
||||||
protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot);
|
protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot);
|
||||||
|
|
||||||
internal void RegisterEntityStorage(EntityStorage entityStorage)
|
public EntityComponentReader(World world)
|
||||||
{
|
{
|
||||||
EntityStorage = entityStorage;
|
World = world;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RegisterComponentDepot(ComponentDepot componentDepot)
|
protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : unmanaged
|
||||||
{
|
|
||||||
ComponentDepot = componentDepot;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RegisterRelationDepot(RelationDepot relationDepot)
|
|
||||||
{
|
|
||||||
RelationDepot = relationDepot;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
|
|
||||||
{
|
{
|
||||||
return ComponentDepot.ReadComponents<TComponent>();
|
return ComponentDepot.ReadComponents<TComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool Has<TComponent>(in Entity entity) where TComponent : struct
|
protected bool Has<TComponent>(in Entity entity) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ComponentDepot.Has<TComponent>(entity.ID);
|
return ComponentDepot.Has<TComponent>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool Some<TComponent>() where TComponent : struct
|
protected bool Some<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ComponentDepot.Some<TComponent>();
|
return ComponentDepot.Some<TComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ref readonly TComponent Get<TComponent>(in Entity entity) where TComponent : struct
|
protected ref readonly TComponent Get<TComponent>(in Entity entity) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ref ComponentDepot.Get<TComponent>(entity.ID);
|
return ref ComponentDepot.Get<TComponent>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ref readonly TComponent GetSingleton<TComponent>() where TComponent : struct
|
protected ref readonly TComponent GetSingleton<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ref ComponentDepot.Get<TComponent>();
|
return ref ComponentDepot.Get<TComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Entity GetSingletonEntity<TComponent>() where TComponent : struct
|
protected Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
return ComponentDepot.GetSingletonEntity<TComponent>();
|
return ComponentDepot.GetSingletonEntity<TComponent>();
|
||||||
}
|
}
|
||||||
|
@ -60,22 +51,22 @@ namespace MoonTools.ECS
|
||||||
return EntityStorage.Exists(entity);
|
return EntityStorage.Exists(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : struct
|
protected IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return RelationDepot.Relations<TRelationKind>();
|
return RelationDepot.Relations<TRelationKind>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool Related<TRelationKind>(in Entity a, in Entity b) where TRelationKind : struct
|
protected bool Related<TRelationKind>(in Entity a, in Entity b) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return RelationDepot.Related<TRelationKind>(a.ID, b.ID);
|
return RelationDepot.Related<TRelationKind>(a.ID, b.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(in Entity entity) where TRelationKind : struct
|
protected IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return RelationDepot.RelatedToA<TRelationKind>(entity.ID);
|
return RelationDepot.RelatedToA<TRelationKind>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(in Entity entity) where TRelationKind : struct
|
protected IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(in Entity entity) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return RelationDepot.RelatedToB<TRelationKind>(entity.ID);
|
return RelationDepot.RelatedToB<TRelationKind>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,75 @@
|
||||||
namespace MoonTools.ECS
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
internal class EntityStorage
|
internal class EntityStorage
|
||||||
{
|
{
|
||||||
public IDStorage idStorage = new IDStorage();
|
private int nextID = 0;
|
||||||
|
private readonly Stack<int> availableIDs = new Stack<int>();
|
||||||
|
private readonly HashSet<int> availableIDHash = new HashSet<int>();
|
||||||
|
|
||||||
public Entity Create()
|
public Entity Create()
|
||||||
{
|
{
|
||||||
return new Entity(idStorage.NextID());
|
return new Entity(NextID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Exists(in Entity entity)
|
public bool Exists(in Entity entity)
|
||||||
{
|
{
|
||||||
return idStorage.Taken(entity.ID);
|
return Taken(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Destroy(in Entity entity)
|
public void Destroy(in Entity entity)
|
||||||
{
|
{
|
||||||
idStorage.Release(entity.ID);
|
Release(entity.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(EntityStorageState state)
|
||||||
|
{
|
||||||
|
state.NextID = nextID;
|
||||||
|
state.availableIDs.Clear();
|
||||||
|
foreach (var id in availableIDs)
|
||||||
|
{
|
||||||
|
state.availableIDs.Add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(EntityStorageState state)
|
||||||
|
{
|
||||||
|
nextID = state.NextID;
|
||||||
|
availableIDs.Clear();
|
||||||
|
availableIDHash.Clear();
|
||||||
|
foreach (var id in state.availableIDs)
|
||||||
|
{
|
||||||
|
availableIDs.Push(id);
|
||||||
|
availableIDHash.Add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int NextID()
|
||||||
|
{
|
||||||
|
if (availableIDs.Count > 0)
|
||||||
|
{
|
||||||
|
var id = availableIDs.Pop();
|
||||||
|
availableIDHash.Remove(id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var id = nextID;
|
||||||
|
nextID += 1;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Taken(int id)
|
||||||
|
{
|
||||||
|
return !availableIDHash.Contains(id) && id < nextID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Release(int id)
|
||||||
|
{
|
||||||
|
availableIDs.Push(id);
|
||||||
|
availableIDHash.Add(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,14 +23,14 @@ namespace MoonTools.ECS
|
||||||
Excluded = excluded;
|
Excluded = excluded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterBuilder Include<TComponent>() where TComponent : struct
|
public FilterBuilder Include<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
ComponentDepot.Register<TComponent>();
|
ComponentDepot.Register<TComponent>();
|
||||||
Included.Add(typeof(TComponent));
|
Included.Add(typeof(TComponent));
|
||||||
return new FilterBuilder(ComponentDepot, Included, Excluded);
|
return new FilterBuilder(ComponentDepot, Included, Excluded);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterBuilder Exclude<TComponent>() where TComponent : struct
|
public FilterBuilder Exclude<TComponent>() where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
ComponentDepot.Register<TComponent>();
|
ComponentDepot.Register<TComponent>();
|
||||||
Excluded.Add(typeof(TComponent));
|
Excluded.Add(typeof(TComponent));
|
||||||
|
|
|
@ -7,8 +7,8 @@ namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
private const int HASH_FACTOR = 97;
|
private const int HASH_FACTOR = 97;
|
||||||
|
|
||||||
public HashSet<Type> Included;
|
public readonly HashSet<Type> Included;
|
||||||
public HashSet<Type> Excluded;
|
public readonly HashSet<Type> Excluded;
|
||||||
|
|
||||||
public FilterSignature(HashSet<Type> included, HashSet<Type> excluded)
|
public FilterSignature(HashSet<Type> included, HashSet<Type> excluded)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
|
||||||
{
|
|
||||||
internal class IDStorage
|
|
||||||
{
|
|
||||||
private int nextID = 0;
|
|
||||||
|
|
||||||
private readonly Stack<int> availableIDs = new Stack<int>();
|
|
||||||
private readonly HashSet<int> availableIDHash = new HashSet<int>();
|
|
||||||
|
|
||||||
public int NextID()
|
|
||||||
{
|
|
||||||
if (availableIDs.Count > 0)
|
|
||||||
{
|
|
||||||
var id = availableIDs.Pop();
|
|
||||||
availableIDHash.Remove(id);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var id = nextID;
|
|
||||||
nextID += 1;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Taken(int id)
|
|
||||||
{
|
|
||||||
return !availableIDHash.Contains(id) && id < nextID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Release(int id)
|
|
||||||
{
|
|
||||||
availableIDs.Push(id);
|
|
||||||
availableIDHash.Add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
internal class IndexableSet<T> : IEnumerable<T> where T : notnull
|
internal class IndexableSet<T> : IEnumerable<T> where T : unmanaged
|
||||||
{
|
{
|
||||||
private Dictionary<T, int> indices;
|
private Dictionary<T, int> indices;
|
||||||
private T[] array;
|
private T[] array;
|
||||||
|
@ -78,5 +79,32 @@ namespace MoonTools.ECS
|
||||||
yield return array[i];
|
yield return array[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Save(IndexableSetState<T> state)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> arrayBytes = MemoryMarshal.Cast<T, byte>(array);
|
||||||
|
|
||||||
|
if (arrayBytes.Length > state.Array.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref state.Array, arrayBytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayBytes.CopyTo(state.Array);
|
||||||
|
|
||||||
|
state.Count = Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(IndexableSetState<T> state)
|
||||||
|
{
|
||||||
|
state.Array.CopyTo(MemoryMarshal.Cast<T, byte>(array));
|
||||||
|
|
||||||
|
indices.Clear();
|
||||||
|
for (var i = 0; i < state.Count; i += 1)
|
||||||
|
{
|
||||||
|
indices[array[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Count = state.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
private Dictionary<Type, RelationStorage> storages = new Dictionary<Type, RelationStorage>();
|
private Dictionary<Type, RelationStorage> storages = new Dictionary<Type, RelationStorage>();
|
||||||
|
|
||||||
private void Register<TRelationKind>() where TRelationKind : struct
|
private void Register<TRelationKind>() where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
if (!storages.ContainsKey(typeof(TRelationKind)))
|
if (!storages.ContainsKey(typeof(TRelationKind)))
|
||||||
{
|
{
|
||||||
|
@ -15,18 +15,18 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RelationStorage<TRelationKind> Lookup<TRelationKind>() where TRelationKind : struct
|
private RelationStorage<TRelationKind> Lookup<TRelationKind>() where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
Register<TRelationKind>();
|
Register<TRelationKind>();
|
||||||
return (RelationStorage<TRelationKind>) storages[typeof(TRelationKind)];
|
return (RelationStorage<TRelationKind>) storages[typeof(TRelationKind)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add<TRelationKind>(Relation relation, TRelationKind relationData) where TRelationKind : struct
|
public void Set<TRelationKind>(Relation relation, TRelationKind relationData) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
Lookup<TRelationKind>().Add(relation, relationData);
|
Lookup<TRelationKind>().Set(relation, relationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove<TRelationKind>(Relation relation) where TRelationKind : struct
|
public void Remove<TRelationKind>(Relation relation) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
Lookup<TRelationKind>().Remove(relation);
|
Lookup<TRelationKind>().Remove(relation);
|
||||||
}
|
}
|
||||||
|
@ -40,24 +40,45 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : struct
|
public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TRelationKind>().All();
|
return Lookup<TRelationKind>().All();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Related<TRelationKind>(int idA, int idB) where TRelationKind : struct
|
public bool Related<TRelationKind>(int idA, int idB) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TRelationKind>().Has(new Relation(idA, idB));
|
return Lookup<TRelationKind>().Has(new Relation(idA, idB));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(int entityID) where TRelationKind : struct
|
public IEnumerable<(Entity, TRelationKind)> RelatedToA<TRelationKind>(int entityID) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TRelationKind>().RelatedToA(entityID);
|
return Lookup<TRelationKind>().RelatedToA(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(int entityID) where TRelationKind : struct
|
public IEnumerable<(Entity, TRelationKind)> RelatedToB<TRelationKind>(int entityID) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
return Lookup<TRelationKind>().RelatedToB(entityID);
|
return Lookup<TRelationKind>().RelatedToB(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Save(RelationDepotState state)
|
||||||
|
{
|
||||||
|
foreach (var (type, storage) in storages)
|
||||||
|
{
|
||||||
|
if (!state.StorageStates.ContainsKey(type))
|
||||||
|
{
|
||||||
|
state.StorageStates.Add(type, storage.CreateState());
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Save(state.StorageStates[type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(RelationDepotState state)
|
||||||
|
{
|
||||||
|
foreach (var (type, storageState) in state.StorageStates)
|
||||||
|
{
|
||||||
|
storages[type].Load(storageState);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,46 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
internal abstract class RelationStorage
|
internal abstract class RelationStorage
|
||||||
{
|
{
|
||||||
|
public abstract RelationStorageState CreateState();
|
||||||
|
public abstract void Save(RelationStorageState state);
|
||||||
|
public abstract void Load(RelationStorageState state);
|
||||||
public abstract void OnEntityDestroy(int entityID);
|
public abstract void OnEntityDestroy(int entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relation is the two entities, A related to B.
|
// Relation is the two entities, A related to B.
|
||||||
// TRelation is the data attached to the relation.
|
// TRelation is the data attached to the relation.
|
||||||
internal class RelationStorage<TRelation> : RelationStorage where TRelation : struct
|
internal class RelationStorage<TRelation> : RelationStorage where TRelation : unmanaged
|
||||||
{
|
{
|
||||||
private Dictionary<Relation, TRelation> relations = new Dictionary<Relation, TRelation>(16);
|
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];
|
||||||
private Dictionary<int, HashSet<int>> entitiesRelatedToA = new Dictionary<int, HashSet<int>>(16);
|
private Dictionary<int, HashSet<int>> entitiesRelatedToA = new Dictionary<int, HashSet<int>>(16);
|
||||||
private Dictionary<int, HashSet<int>> entitiesRelatedToB = new Dictionary<int, HashSet<int>>(16);
|
private Dictionary<int, HashSet<int>> entitiesRelatedToB = new Dictionary<int, HashSet<int>>(16);
|
||||||
private Stack<HashSet<int>> listPool = new Stack<HashSet<int>>();
|
private Stack<HashSet<int>> listPool = new Stack<HashSet<int>>();
|
||||||
|
|
||||||
public IEnumerable<(Entity, Entity, TRelation)> All()
|
public IEnumerable<(Entity, Entity, TRelation)> All()
|
||||||
{
|
{
|
||||||
foreach (var relationData in relations)
|
for (var i = 0; i < count; i += 1)
|
||||||
{
|
{
|
||||||
yield return (relationData.Key.A, relationData.Key.B, relationData.Value);
|
var relation = relations[i];
|
||||||
|
yield return (relation.A, relation.B, relationDatas[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(Relation relation, TRelation relationData)
|
public void Set(Relation relation, TRelation relationData)
|
||||||
{
|
{
|
||||||
if (relations.ContainsKey(relation)) { return; }
|
if (indices.ContainsKey(relation))
|
||||||
|
{
|
||||||
|
var index = indices[relation];
|
||||||
|
relationDatas[index] = relationData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var idA = relation.A.ID;
|
var idA = relation.A.ID;
|
||||||
var idB = relation.B.ID;
|
var idB = relation.B.ID;
|
||||||
|
@ -43,12 +57,20 @@ namespace MoonTools.ECS
|
||||||
}
|
}
|
||||||
entitiesRelatedToB[idB].Add(idA);
|
entitiesRelatedToB[idB].Add(idA);
|
||||||
|
|
||||||
relations.Add(relation, relationData);
|
if (count >= relationDatas.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref relationDatas, relationDatas.Length * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
relations[count] = relation;
|
||||||
|
relationDatas[count] = relationData;
|
||||||
|
indices.Add(relation, count);
|
||||||
|
count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Has(Relation relation)
|
public bool Has(Relation relation)
|
||||||
{
|
{
|
||||||
return relations.ContainsKey(relation);
|
return indices.ContainsKey(relation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: is there a more descriptive name for these?
|
// FIXME: is there a more descriptive name for these?
|
||||||
|
@ -59,7 +81,7 @@ namespace MoonTools.ECS
|
||||||
foreach (var id in entitiesRelatedToA[entityID])
|
foreach (var id in entitiesRelatedToA[entityID])
|
||||||
{
|
{
|
||||||
var relation = new Relation(entityID, id);
|
var relation = new Relation(entityID, id);
|
||||||
yield return (relation.B, relations[relation]);
|
yield return (relation.B, relationDatas[indices[relation]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +93,7 @@ namespace MoonTools.ECS
|
||||||
foreach (var id in entitiesRelatedToB[entityID])
|
foreach (var id in entitiesRelatedToB[entityID])
|
||||||
{
|
{
|
||||||
var relation = new Relation(id, entityID);
|
var relation = new Relation(id, entityID);
|
||||||
yield return (relation.A, relations[relation]);
|
yield return (relation.A, relationDatas[indices[relation]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +110,26 @@ namespace MoonTools.ECS
|
||||||
entitiesRelatedToB[relation.B.ID].Remove(relation.A.ID);
|
entitiesRelatedToB[relation.B.ID].Remove(relation.A.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return relations.Remove(relation);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnEntityDestroy(int entityID)
|
public override void OnEntityDestroy(int entityID)
|
||||||
|
@ -131,5 +172,60 @@ namespace MoonTools.ECS
|
||||||
hashSet.Clear();
|
hashSet.Clear();
|
||||||
listPool.Push(hashSet);
|
listPool.Push(hashSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
entitiesRelatedToA.Clear();
|
||||||
|
entitiesRelatedToB.Clear();
|
||||||
|
for (var i = 0; i < state.Count; i += 1)
|
||||||
|
{
|
||||||
|
var relation = relations[i];
|
||||||
|
indices[relation] = i;
|
||||||
|
|
||||||
|
if (!entitiesRelatedToA.ContainsKey(relation.A.ID))
|
||||||
|
{
|
||||||
|
entitiesRelatedToA[relation.A.ID] = AcquireHashSetFromPool();
|
||||||
|
}
|
||||||
|
entitiesRelatedToA[relation.A.ID].Add(relation.B.ID);
|
||||||
|
|
||||||
|
if (!entitiesRelatedToB.ContainsKey(relation.B.ID))
|
||||||
|
{
|
||||||
|
entitiesRelatedToB[relation.B.ID] = AcquireHashSetFromPool();
|
||||||
|
}
|
||||||
|
entitiesRelatedToB[relation.B.ID].Add(relation.A.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
count = state.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{
|
{
|
||||||
public abstract class Renderer : EntityComponentReader
|
public abstract class Renderer : EntityComponentReader
|
||||||
{
|
{
|
||||||
public Renderer(World world)
|
public Renderer(World world) : base(world) { }
|
||||||
{
|
|
||||||
world.AddRenderer(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonTools.ECS
|
||||||
|
{
|
||||||
|
internal class ComponentDepotState
|
||||||
|
{
|
||||||
|
public Dictionary<Type, ComponentStorageState> StorageStates = new Dictionary<Type, ComponentStorageState>();
|
||||||
|
public Dictionary<FilterSignature, IndexableSetState<int>> FilterStates = new Dictionary<FilterSignature, IndexableSetState<int>>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonTools.ECS
|
||||||
|
{
|
||||||
|
internal class ComponentStorageState
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
public byte[] EntityIDs;
|
||||||
|
public byte[] Components;
|
||||||
|
|
||||||
|
public unsafe static ComponentStorageState Create<TComponent>(int count) where TComponent : unmanaged
|
||||||
|
{
|
||||||
|
return new ComponentStorageState(
|
||||||
|
count,
|
||||||
|
count * sizeof(int),
|
||||||
|
count * sizeof(TComponent)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComponentStorageState(int count, int entityIDSize, int componentSize)
|
||||||
|
{
|
||||||
|
Count = count;
|
||||||
|
EntityIDs = new byte[entityIDSize];
|
||||||
|
Components = new byte[componentSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonTools.ECS
|
||||||
|
{
|
||||||
|
internal class EntityStorageState
|
||||||
|
{
|
||||||
|
public int NextID;
|
||||||
|
public List<int> availableIDs = new List<int>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonTools.ECS
|
||||||
|
{
|
||||||
|
internal class IndexableSetState<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
public byte[] Array;
|
||||||
|
|
||||||
|
public unsafe IndexableSetState(int count)
|
||||||
|
{
|
||||||
|
Count = count;
|
||||||
|
Array = new byte[sizeof(T) * count];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonTools.ECS
|
||||||
|
{
|
||||||
|
internal class RelationDepotState
|
||||||
|
{
|
||||||
|
public Dictionary<Type, RelationStorageState> StorageStates = new Dictionary<Type, RelationStorageState>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MoonTools.ECS
|
||||||
|
{
|
||||||
|
internal class RelationStorageState
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
public byte[] Relations;
|
||||||
|
public byte[] RelationDatas;
|
||||||
|
|
||||||
|
public unsafe static RelationStorageState Create<TRelation>(int count) where TRelation : unmanaged
|
||||||
|
{
|
||||||
|
return new RelationStorageState(
|
||||||
|
count,
|
||||||
|
count * sizeof(Relation),
|
||||||
|
count * sizeof(TRelation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RelationStorageState(int count, int relationSize, int relationDataSize)
|
||||||
|
{
|
||||||
|
Count = count;
|
||||||
|
Relations = new byte[relationSize];
|
||||||
|
RelationDatas = new byte[relationDataSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace MoonTools.ECS
|
||||||
|
{
|
||||||
|
public class WorldState
|
||||||
|
{
|
||||||
|
internal readonly ComponentDepotState ComponentDepotState;
|
||||||
|
internal readonly EntityStorageState EntityStorageState;
|
||||||
|
internal readonly RelationDepotState RelationDepotState;
|
||||||
|
|
||||||
|
public WorldState()
|
||||||
|
{
|
||||||
|
ComponentDepotState = new ComponentDepotState();
|
||||||
|
EntityStorageState = new EntityStorageState();
|
||||||
|
RelationDepotState = new RelationDepotState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,17 +5,9 @@ namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
public abstract class System : EntityComponentReader
|
public abstract class System : EntityComponentReader
|
||||||
{
|
{
|
||||||
internal MessageDepot MessageDepot;
|
internal MessageDepot MessageDepot => World.MessageDepot;
|
||||||
|
|
||||||
internal void RegisterMessageDepot(MessageDepot messageDepot)
|
public System(World world) : base(world) { }
|
||||||
{
|
|
||||||
MessageDepot = messageDepot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public System(World world)
|
|
||||||
{
|
|
||||||
world.AddSystem(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Update(TimeSpan delta);
|
public abstract void Update(TimeSpan delta);
|
||||||
|
|
||||||
|
@ -24,7 +16,7 @@ namespace MoonTools.ECS
|
||||||
return EntityStorage.Create();
|
return EntityStorage.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Set<TComponent>(in Entity entity, in TComponent component) where TComponent : struct
|
protected void Set<TComponent>(in Entity entity, in TComponent component) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// check for use after destroy
|
// check for use after destroy
|
||||||
|
@ -36,52 +28,52 @@ namespace MoonTools.ECS
|
||||||
ComponentDepot.Set<TComponent>(entity.ID, component);
|
ComponentDepot.Set<TComponent>(entity.ID, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Remove<TComponent>(in Entity entity) where TComponent : struct
|
protected void Remove<TComponent>(in Entity entity) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
ComponentDepot.Remove<TComponent>(entity.ID);
|
ComponentDepot.Remove<TComponent>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ReadOnlySpan<TMessage> ReadMessages<TMessage>() where TMessage : struct
|
protected ReadOnlySpan<TMessage> ReadMessages<TMessage>() where TMessage : unmanaged
|
||||||
{
|
{
|
||||||
return MessageDepot.All<TMessage>();
|
return MessageDepot.All<TMessage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TMessage ReadMessage<TMessage>() where TMessage : struct
|
protected TMessage ReadMessage<TMessage>() where TMessage : unmanaged
|
||||||
{
|
{
|
||||||
return MessageDepot.First<TMessage>();
|
return MessageDepot.First<TMessage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool SomeMessage<TMessage>() where TMessage : struct
|
protected bool SomeMessage<TMessage>() where TMessage : unmanaged
|
||||||
{
|
{
|
||||||
return MessageDepot.Some<TMessage>();
|
return MessageDepot.Some<TMessage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
|
protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged, IHasEntity
|
||||||
{
|
{
|
||||||
return MessageDepot.WithEntity<TMessage>(entity.ID);
|
return MessageDepot.WithEntity<TMessage>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
|
protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged, IHasEntity
|
||||||
{
|
{
|
||||||
return ref MessageDepot.FirstWithEntity<TMessage>(entity.ID);
|
return ref MessageDepot.FirstWithEntity<TMessage>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
|
protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged, IHasEntity
|
||||||
{
|
{
|
||||||
return MessageDepot.SomeWithEntity<TMessage>(entity.ID);
|
return MessageDepot.SomeWithEntity<TMessage>(entity.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Send<TMessage>(in TMessage message) where TMessage : struct
|
protected void Send<TMessage>(in TMessage message) where TMessage : unmanaged
|
||||||
{
|
{
|
||||||
MessageDepot.Add(message);
|
MessageDepot.Add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : struct
|
protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
RelationDepot.Add<TRelationKind>(new Relation(entityA, entityB), relationData);
|
RelationDepot.Set<TRelationKind>(new Relation(entityA, entityB), relationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : struct
|
protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged
|
||||||
{
|
{
|
||||||
RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB));
|
RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB));
|
||||||
}
|
}
|
||||||
|
|
51
src/World.cs
51
src/World.cs
|
@ -2,37 +2,22 @@
|
||||||
{
|
{
|
||||||
public class World
|
public class World
|
||||||
{
|
{
|
||||||
private readonly EntityStorage EntityStorage = new EntityStorage();
|
internal readonly EntityStorage EntityStorage = new EntityStorage();
|
||||||
private readonly ComponentDepot ComponentDepot = new ComponentDepot();
|
internal readonly ComponentDepot ComponentDepot = new ComponentDepot();
|
||||||
private readonly MessageDepot MessageDepot = new MessageDepot();
|
internal readonly MessageDepot MessageDepot = new MessageDepot();
|
||||||
private readonly RelationDepot RelationDepot = new RelationDepot();
|
internal readonly RelationDepot RelationDepot = new RelationDepot();
|
||||||
|
|
||||||
internal void AddSystem(System system)
|
|
||||||
{
|
|
||||||
system.RegisterEntityStorage(EntityStorage);
|
|
||||||
system.RegisterComponentDepot(ComponentDepot);
|
|
||||||
system.RegisterMessageDepot(MessageDepot);
|
|
||||||
system.RegisterRelationDepot(RelationDepot);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddRenderer(Renderer renderer)
|
|
||||||
{
|
|
||||||
renderer.RegisterEntityStorage(EntityStorage);
|
|
||||||
renderer.RegisterComponentDepot(ComponentDepot);
|
|
||||||
renderer.RegisterRelationDepot(RelationDepot);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity CreateEntity()
|
public Entity CreateEntity()
|
||||||
{
|
{
|
||||||
return EntityStorage.Create();
|
return EntityStorage.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set<TComponent>(Entity entity, in TComponent component) where TComponent : struct
|
public void Set<TComponent>(Entity entity, in TComponent component) where TComponent : unmanaged
|
||||||
{
|
{
|
||||||
ComponentDepot.Set(entity.ID, component);
|
ComponentDepot.Set(entity.ID, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send<TMessage>(in TMessage message) where TMessage : struct
|
public void Send<TMessage>(in TMessage message) where TMessage : unmanaged
|
||||||
{
|
{
|
||||||
MessageDepot.Add(message);
|
MessageDepot.Add(message);
|
||||||
}
|
}
|
||||||
|
@ -41,5 +26,29 @@
|
||||||
{
|
{
|
||||||
MessageDepot.Clear();
|
MessageDepot.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DisableSerialization<TComponent>() where TComponent : unmanaged
|
||||||
|
{
|
||||||
|
ComponentDepot.DisableSerialization<TComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldState CreateState()
|
||||||
|
{
|
||||||
|
return new WorldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(WorldState state)
|
||||||
|
{
|
||||||
|
ComponentDepot.Save(state.ComponentDepotState);
|
||||||
|
EntityStorage.Save(state.EntityStorageState);
|
||||||
|
RelationDepot.Save(state.RelationDepotState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(WorldState state)
|
||||||
|
{
|
||||||
|
ComponentDepot.Load(state.ComponentDepotState);
|
||||||
|
EntityStorage.Load(state.EntityStorageState);
|
||||||
|
RelationDepot.Load(state.RelationDepotState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue