more serialization changes
parent
b30d53e71d
commit
be95e80265
|
@ -4,6 +4,7 @@
|
|||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x64</Platforms>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -11,13 +11,13 @@ namespace MoonTools.ECS
|
|||
|
||||
private Dictionary<Type, HashSet<FilterSignature>> typeToFilterSignatures = new Dictionary<Type, HashSet<FilterSignature>>();
|
||||
|
||||
private Dictionary<int, HashSet<Type>> entityComponentMap = new Dictionary<int, HashSet<Type>>();
|
||||
|
||||
#if DEBUG
|
||||
private Dictionary<Type, Filter> singleComponentFilters = new Dictionary<Type, Filter>();
|
||||
#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)))
|
||||
{
|
||||
|
@ -33,19 +33,19 @@ namespace MoonTools.ECS
|
|||
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?
|
||||
Register<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();
|
||||
}
|
||||
|
||||
public bool Has<TComponent>(int entityID) where TComponent : struct
|
||||
public bool Has<TComponent>(int entityID) where TComponent : unmanaged
|
||||
{
|
||||
return Lookup<TComponent>().Has(entityID);
|
||||
}
|
||||
|
@ -55,29 +55,22 @@ namespace MoonTools.ECS
|
|||
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);
|
||||
}
|
||||
|
||||
public ref readonly TComponent Get<TComponent>() where TComponent : struct
|
||||
public ref readonly TComponent Get<TComponent>() where TComponent : unmanaged
|
||||
{
|
||||
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);
|
||||
|
||||
if (!entityComponentMap.ContainsKey(entityID))
|
||||
{
|
||||
entityComponentMap.Add(entityID, new HashSet<Type>());
|
||||
}
|
||||
|
||||
var notFound = entityComponentMap[entityID].Add(typeof(TComponent));
|
||||
var existed = Lookup<TComponent>().Set(entityID, component);
|
||||
|
||||
// update filters
|
||||
if (notFound)
|
||||
if (!existed)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
|
||||
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : unmanaged
|
||||
{
|
||||
return Lookup<TComponent>().AllComponents();
|
||||
}
|
||||
|
||||
private void Remove(Type type, int entityID)
|
||||
{
|
||||
Lookup(type).Remove(entityID);
|
||||
|
||||
var found = entityComponentMap[entityID].Remove(type);
|
||||
var existed = Lookup(type).Remove(entityID);
|
||||
|
||||
// update filters
|
||||
if (found)
|
||||
if (existed)
|
||||
{
|
||||
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 found = entityComponentMap[entityID].Remove(typeof(TComponent));
|
||||
var existed = Lookup<TComponent>().Remove(entityID);
|
||||
|
||||
// update filters
|
||||
if (found)
|
||||
if (existed)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (entityComponentMap.ContainsKey(entityID))
|
||||
foreach (var type in storages.Keys)
|
||||
{
|
||||
foreach (var type in entityComponentMap[entityID])
|
||||
{
|
||||
Remove(type, entityID);
|
||||
}
|
||||
|
||||
entityComponentMap.Remove(entityID);
|
||||
Remove(type, entityID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,6 +216,11 @@ namespace MoonTools.ECS
|
|||
filterSignatureToEntityIDs[filterSignature].Add(entityID);
|
||||
}
|
||||
|
||||
public void DisableSerialization<TComponent>() where TComponent : unmanaged
|
||||
{
|
||||
TypesWithDisabledSerialization.Add(typeof(TComponent));
|
||||
}
|
||||
|
||||
public ComponentDepotState CreateState()
|
||||
{
|
||||
return new ComponentDepotState();
|
||||
|
@ -238,12 +228,30 @@ namespace MoonTools.ECS
|
|||
|
||||
public void Serialize(ComponentDepotState state)
|
||||
{
|
||||
// FIXME: this is creating garbage
|
||||
state.StorageStates.Clear();
|
||||
foreach (var (type, storage) in storages)
|
||||
{
|
||||
var storageState = storage.CreateState();
|
||||
storage.Serialize(storageState);
|
||||
state.StorageStates.Add(type, storageState);
|
||||
// FIXME: we could cache this
|
||||
if (!TypesWithDisabledSerialization.Contains(type))
|
||||
{
|
||||
var storageState = storage.CreateState();
|
||||
storage.Serialize(storageState);
|
||||
state.StorageStates.Add(type, storageState);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this is creating garbage
|
||||
state.FilterStates.Clear();
|
||||
foreach (var (signature, set) in filterSignatureToEntityIDs)
|
||||
{
|
||||
// FIXME: we could cache this
|
||||
if (!signature.Included.Overlaps(TypesWithDisabledSerialization) && !signature.Excluded.Overlaps(TypesWithDisabledSerialization))
|
||||
{
|
||||
var setState = new IndexableSetState<int>(set.Count);
|
||||
set.Save(setState);
|
||||
state.FilterStates[signature] = setState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,6 +261,11 @@ namespace MoonTools.ECS
|
|||
{
|
||||
storages[type].Deserialize(storageState);
|
||||
}
|
||||
|
||||
foreach (var (signature, setState) in state.FilterStates)
|
||||
{
|
||||
filterSignatureToEntityIDs[signature].Load(setState);
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
|
|
@ -3,8 +3,9 @@ using System.Collections.Generic;
|
|||
|
||||
namespace MoonTools.ECS
|
||||
{
|
||||
public class ComponentDepotState
|
||||
{
|
||||
public class ComponentDepotState
|
||||
{
|
||||
public Dictionary<Type, ComponentStorageState> StorageStates = new Dictionary<Type, ComponentStorageState>();
|
||||
public Dictionary<FilterSignature, IndexableSetState<int>> FilterStates = new Dictionary<FilterSignature, IndexableSetState<int>>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,14 @@ namespace MoonTools.ECS
|
|||
internal abstract class ComponentStorage
|
||||
{
|
||||
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 ComponentStorageState CreateState();
|
||||
public abstract void Serialize(ComponentStorageState state);
|
||||
public abstract void Deserialize(ComponentStorageState state);
|
||||
}
|
||||
|
||||
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct
|
||||
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : unmanaged
|
||||
{
|
||||
private int nextID;
|
||||
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
|
||||
|
@ -52,8 +52,11 @@ namespace MoonTools.ECS
|
|||
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))
|
||||
{
|
||||
var index = nextID;
|
||||
|
@ -67,12 +70,17 @@ namespace MoonTools.ECS
|
|||
|
||||
entityIDToStorageIndex[entityID] = index;
|
||||
entityIDs[index] = entityID;
|
||||
|
||||
result = false;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
@ -91,7 +99,11 @@ namespace MoonTools.ECS
|
|||
}
|
||||
|
||||
nextID -= 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
|
@ -137,6 +149,8 @@ namespace MoonTools.ECS
|
|||
{
|
||||
serializedComponentStorage.EntityIdToStorageIndex[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
serializedComponentStorage.Count = nextID;
|
||||
}
|
||||
|
||||
public override void Deserialize(ComponentStorageState serializedComponentStorage)
|
||||
|
@ -149,6 +163,8 @@ namespace MoonTools.ECS
|
|||
{
|
||||
entityIDToStorageIndex[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
nextID = serializedComponentStorage.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MoonTools.ECS
|
||||
{
|
||||
|
@ -11,12 +10,12 @@ namespace MoonTools.ECS
|
|||
public byte[] EntityIDs;
|
||||
public byte[] Components;
|
||||
|
||||
public static ComponentStorageState Create<TComponent>(int count)
|
||||
public unsafe static ComponentStorageState Create<TComponent>(int count) where TComponent : unmanaged
|
||||
{
|
||||
return new ComponentStorageState(
|
||||
count,
|
||||
count * Marshal.SizeOf<int>(),
|
||||
count * Marshal.SizeOf<TComponent>()
|
||||
count * sizeof(int),
|
||||
count * sizeof(TComponent)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ namespace MoonTools.ECS
|
|||
Excluded = excluded;
|
||||
}
|
||||
|
||||
public FilterBuilder Include<TComponent>() where TComponent : struct
|
||||
public FilterBuilder Include<TComponent>() where TComponent : unmanaged
|
||||
{
|
||||
ComponentDepot.Register<TComponent>();
|
||||
Included.Add(typeof(TComponent));
|
||||
return new FilterBuilder(ComponentDepot, Included, Excluded);
|
||||
}
|
||||
|
||||
public FilterBuilder Exclude<TComponent>() where TComponent : struct
|
||||
public FilterBuilder Exclude<TComponent>() where TComponent : unmanaged
|
||||
{
|
||||
ComponentDepot.Register<TComponent>();
|
||||
Excluded.Add(typeof(TComponent));
|
||||
|
|
|
@ -7,8 +7,8 @@ namespace MoonTools.ECS
|
|||
{
|
||||
private const int HASH_FACTOR = 97;
|
||||
|
||||
public HashSet<Type> Included;
|
||||
public HashSet<Type> Excluded;
|
||||
public readonly HashSet<Type> Included;
|
||||
public readonly HashSet<Type> Excluded;
|
||||
|
||||
public FilterSignature(HashSet<Type> included, HashSet<Type> excluded)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
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 T[] array;
|
||||
|
@ -78,5 +79,38 @@ namespace MoonTools.ECS
|
|||
yield return array[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(IndexableSetState<T> state)
|
||||
{
|
||||
state.Indices.Clear();
|
||||
foreach (var (key, value) in indices)
|
||||
{
|
||||
state.Indices[key] = value;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
indices.Clear();
|
||||
foreach (var kvp in state.Indices)
|
||||
{
|
||||
indices[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
state.Array.CopyTo(MemoryMarshal.Cast<T, byte>(array));
|
||||
|
||||
Count = state.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MoonTools.ECS
|
||||
{
|
||||
public class IndexableSetState<T> where T : unmanaged
|
||||
{
|
||||
public int Count;
|
||||
public Dictionary<T, int> Indices;
|
||||
public byte[] Array;
|
||||
|
||||
public unsafe IndexableSetState(int count)
|
||||
{
|
||||
Count = count;
|
||||
Indices = new Dictionary<T, int>(count);
|
||||
Array = new byte[sizeof(T) * count];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,12 +12,12 @@
|
|||
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);
|
||||
}
|
||||
|
||||
public void Send<TMessage>(in TMessage message) where TMessage : struct
|
||||
public void Send<TMessage>(in TMessage message) where TMessage : unmanaged
|
||||
{
|
||||
MessageDepot.Add(message);
|
||||
}
|
||||
|
@ -27,6 +27,11 @@
|
|||
MessageDepot.Clear();
|
||||
}
|
||||
|
||||
public void DisableSerialization<TComponent>() where TComponent : unmanaged
|
||||
{
|
||||
ComponentDepot.DisableSerialization<TComponent>();
|
||||
}
|
||||
|
||||
public ComponentDepotState Serialize()
|
||||
{
|
||||
var state = ComponentDepot.CreateState();
|
||||
|
|
Loading…
Reference in New Issue