remove implicit usings

pull/2/head
cosmonaut 2022-04-07 22:52:03 -07:00
parent 1123ef5662
commit 37d10db955
22 changed files with 781 additions and 721 deletions

View File

@ -2,7 +2,6 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>

View File

@ -1,259 +1,263 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
internal class ComponentDepot namespace MoonTools.ECS
{ {
private Dictionary<Type, ComponentStorage> storages = new Dictionary<Type, ComponentStorage>(); internal class ComponentDepot
private Dictionary<FilterSignature, IndexableSet<int>> filterSignatureToEntityIDs = new Dictionary<FilterSignature, IndexableSet<int>>();
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
{ {
if (!storages.ContainsKey(typeof(TComponent))) private Dictionary<Type, ComponentStorage> storages = new Dictionary<Type, ComponentStorage>();
private Dictionary<FilterSignature, IndexableSet<int>> filterSignatureToEntityIDs = new Dictionary<FilterSignature, IndexableSet<int>>();
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
{ {
storages.Add(typeof(TComponent), new ComponentStorage<TComponent>()); if (!storages.ContainsKey(typeof(TComponent)))
#if DEBUG
singleComponentFilters.Add(typeof(TComponent), CreateFilter(new HashSet<Type>() { typeof(TComponent) }, new HashSet<Type>()));
#endif
}
}
private ComponentStorage Lookup(Type type)
{
return storages[type];
}
private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : struct
{
// TODO: is it possible to optimize this?
Register<TComponent>();
return storages[typeof(TComponent)] as ComponentStorage<TComponent>;
}
public bool Some<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().Any();
}
public bool Has<TComponent>(int entityID) where TComponent : struct
{
return Lookup<TComponent>().Has(entityID);
}
private bool Has(Type type, int entityID)
{
return Lookup(type).Has(entityID);
}
public ref readonly TComponent Get<TComponent>(int entityID) where TComponent : struct
{
return ref Lookup<TComponent>().Get(entityID);
}
public ref readonly TComponent Get<TComponent>() where TComponent : struct
{
return ref Lookup<TComponent>().Get();
}
public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct
{
Lookup<TComponent>().Set(entityID, component);
if (!entityComponentMap.ContainsKey(entityID))
{
entityComponentMap.Add(entityID, new HashSet<Type>());
}
var notFound = entityComponentMap[entityID].Add(typeof(TComponent));
// update filters
if (notFound)
{
if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures))
{ {
foreach (var filterSignature in filterSignatures) storages.Add(typeof(TComponent), new ComponentStorage<TComponent>());
#if DEBUG
singleComponentFilters.Add(typeof(TComponent), CreateFilter(new HashSet<Type>() { typeof(TComponent) }, new HashSet<Type>()));
#endif
}
}
private ComponentStorage Lookup(Type type)
{
return storages[type];
}
private ComponentStorage<TComponent> Lookup<TComponent>() where TComponent : struct
{
// TODO: is it possible to optimize this?
Register<TComponent>();
return storages[typeof(TComponent)] as ComponentStorage<TComponent>;
}
public bool Some<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().Any();
}
public bool Has<TComponent>(int entityID) where TComponent : struct
{
return Lookup<TComponent>().Has(entityID);
}
private bool Has(Type type, int entityID)
{
return Lookup(type).Has(entityID);
}
public ref readonly TComponent Get<TComponent>(int entityID) where TComponent : struct
{
return ref Lookup<TComponent>().Get(entityID);
}
public ref readonly TComponent Get<TComponent>() where TComponent : struct
{
return ref Lookup<TComponent>().Get();
}
public void Set<TComponent>(int entityID, in TComponent component) where TComponent : struct
{
Lookup<TComponent>().Set(entityID, component);
if (!entityComponentMap.ContainsKey(entityID))
{
entityComponentMap.Add(entityID, new HashSet<Type>());
}
var notFound = entityComponentMap[entityID].Add(typeof(TComponent));
// update filters
if (notFound)
{
if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures))
{ {
CheckFilter(filterSignature, entityID); foreach (var filterSignature in filterSignatures)
{
CheckFilter(filterSignature, entityID);
}
} }
} }
} }
}
public Entity GetSingletonEntity<TComponent>() where TComponent : struct public Entity GetSingletonEntity<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().FirstEntity();
}
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().AllComponents();
}
private void Remove(Type type, int entityID)
{
Lookup(type).Remove(entityID);
var found = entityComponentMap[entityID].Remove(type);
// update filters
if (found)
{ {
if (typeToFilterSignatures.TryGetValue(type, out var filterSignatures)) return Lookup<TComponent>().FirstEntity();
}
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().AllComponents();
}
private void Remove(Type type, int entityID)
{
Lookup(type).Remove(entityID);
var found = entityComponentMap[entityID].Remove(type);
// update filters
if (found)
{ {
foreach (var filterSignature in filterSignatures) if (typeToFilterSignatures.TryGetValue(type, out var filterSignatures))
{ {
CheckFilter(filterSignature, entityID); foreach (var filterSignature in filterSignatures)
{
CheckFilter(filterSignature, entityID);
}
} }
} }
} }
}
public void Remove<TComponent>(int entityID) where TComponent : struct public void Remove<TComponent>(int entityID) where TComponent : struct
{
Lookup<TComponent>().Remove(entityID);
var found = entityComponentMap[entityID].Remove(typeof(TComponent));
// update filters
if (found)
{ {
if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures)) Lookup<TComponent>().Remove(entityID);
var found = entityComponentMap[entityID].Remove(typeof(TComponent));
// update filters
if (found)
{ {
foreach (var filterSignature in filterSignatures) if (typeToFilterSignatures.TryGetValue(typeof(TComponent), out var filterSignatures))
{ {
CheckFilter(filterSignature, entityID); foreach (var filterSignature in filterSignatures)
{
CheckFilter(filterSignature, entityID);
}
} }
} }
} }
}
public void OnEntityDestroy(int entityID) public void OnEntityDestroy(int entityID)
{
if (entityComponentMap.ContainsKey(entityID))
{ {
foreach (var type in entityComponentMap[entityID]) if (entityComponentMap.ContainsKey(entityID))
{ {
Remove(type, entityID); foreach (var type in entityComponentMap[entityID])
}
entityComponentMap.Remove(entityID);
}
}
public Filter CreateFilter(HashSet<Type> included, HashSet<Type> excluded)
{
var filterSignature = new FilterSignature(included, excluded);
if (!filterSignatureToEntityIDs.ContainsKey(filterSignature))
{
filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<int>());
foreach (var type in included)
{
if (!typeToFilterSignatures.ContainsKey(type))
{ {
typeToFilterSignatures.Add(type, new HashSet<FilterSignature>()); Remove(type, entityID);
} }
typeToFilterSignatures[type].Add(filterSignature); entityComponentMap.Remove(entityID);
} }
}
foreach (var type in excluded) public Filter CreateFilter(HashSet<Type> included, HashSet<Type> excluded)
{
var filterSignature = new FilterSignature(included, excluded);
if (!filterSignatureToEntityIDs.ContainsKey(filterSignature))
{ {
if (!typeToFilterSignatures.ContainsKey(type)) filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<int>());
foreach (var type in included)
{ {
typeToFilterSignatures.Add(type, new HashSet<FilterSignature>()); if (!typeToFilterSignatures.ContainsKey(type))
{
typeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
}
typeToFilterSignatures[type].Add(filterSignature);
} }
typeToFilterSignatures[type].Add(filterSignature); foreach (var type in excluded)
{
if (!typeToFilterSignatures.ContainsKey(type))
{
typeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
}
typeToFilterSignatures[type].Add(filterSignature);
}
} }
return new Filter(this, included, excluded);
} }
return new Filter(this, included, excluded);
}
// FIXME: this dictionary should probably just store entities // FIXME: this dictionary should probably just store entities
public IEnumerable<Entity> FilterEntities(Filter filter) public IEnumerable<Entity> FilterEntities(Filter filter)
{
foreach (var id in filterSignatureToEntityIDs[filter.Signature])
{ {
yield return new Entity(id); foreach (var id in filterSignatureToEntityIDs[filter.Signature])
}
}
public IEnumerable<Entity> FilterEntitiesRandom(Filter filter)
{
foreach (var index in RandomGenerator.LinearCongruentialGenerator(FilterCount(filter)))
{
yield return new Entity(filterSignatureToEntityIDs[filter.Signature][index]);
}
}
public Entity FilterRandomEntity(Filter filter)
{
var randomIndex = RandomGenerator.Next(FilterCount(filter));
return new Entity(filterSignatureToEntityIDs[filter.Signature][randomIndex]);
}
public int FilterCount(Filter filter)
{
return filterSignatureToEntityIDs[filter.Signature].Count;
}
private void CheckFilter(FilterSignature filterSignature, int entityID)
{
foreach (var type in filterSignature.Included)
{
if (!Has(type, entityID))
{ {
filterSignatureToEntityIDs[filterSignature].Remove(entityID); yield return new Entity(id);
return;
} }
} }
foreach (var type in filterSignature.Excluded) public IEnumerable<Entity> FilterEntitiesRandom(Filter filter)
{ {
if (Has(type, entityID)) foreach (var index in RandomGenerator.LinearCongruentialGenerator(FilterCount(filter)))
{ {
filterSignatureToEntityIDs[filterSignature].Remove(entityID); yield return new Entity(filterSignatureToEntityIDs[filter.Signature][index]);
return;
} }
} }
filterSignatureToEntityIDs[filterSignature].Add(entityID); public Entity FilterRandomEntity(Filter filter)
}
#if DEBUG
public IEnumerable<object> Debug_GetAllComponents(int entityID)
{
foreach (var (type, storage) in storages)
{ {
if (storage.Has(entityID)) var randomIndex = RandomGenerator.Next(FilterCount(filter));
{ return new Entity(filterSignatureToEntityIDs[filter.Signature][randomIndex]);
yield return storage.Debug_Get(entityID);
}
} }
}
public IEnumerable<Entity> Debug_GetEntities(Type componentType) public int FilterCount(Filter filter)
{
return singleComponentFilters[componentType].Entities;
}
public IEnumerable<Type> Debug_SearchComponentType(string typeString)
{
foreach (var type in storages.Keys)
{ {
if (type.ToString().ToLower().Contains(typeString.ToLower())) return filterSignatureToEntityIDs[filter.Signature].Count;
}
private void CheckFilter(FilterSignature filterSignature, int entityID)
{
foreach (var type in filterSignature.Included)
{ {
yield return type; if (!Has(type, entityID))
{
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
return;
}
}
foreach (var type in filterSignature.Excluded)
{
if (Has(type, entityID))
{
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
return;
}
}
filterSignatureToEntityIDs[filterSignature].Add(entityID);
}
#if DEBUG
public IEnumerable<object> Debug_GetAllComponents(int entityID)
{
foreach (var (type, storage) in storages)
{
if (storage.Has(entityID))
{
yield return storage.Debug_Get(entityID);
}
} }
} }
public IEnumerable<Entity> Debug_GetEntities(Type componentType)
{
return singleComponentFilters[componentType].Entities;
}
public IEnumerable<Type> Debug_SearchComponentType(string typeString)
{
foreach (var type in storages.Keys)
{
if (type.ToString().ToLower().Contains(typeString.ToLower()))
{
yield return type;
}
}
}
#endif
} }
#endif
} }

View File

@ -1,106 +1,110 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
internal abstract class ComponentStorage namespace MoonTools.ECS
{ {
public abstract bool Has(int entityID); internal abstract class ComponentStorage
public abstract void Remove(int entityID);
public abstract object Debug_Get(int entityID);
}
// FIXME: we can probably get rid of this weird entity storage system by using filters
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct
{
private int nextID;
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
private int[] entityIDs = new int[16];
private TComponent[] components = new TComponent[16];
public bool Any()
{ {
return nextID > 0; public abstract bool Has(int entityID);
public abstract void Remove(int entityID);
public abstract object Debug_Get(int entityID);
} }
public override bool Has(int entityID) // FIXME: we can probably get rid of this weird entity storage system by using filters
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct
{ {
return entityIDToStorageIndex.ContainsKey(entityID); private int nextID;
} private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
private int[] entityIDs = new int[16];
private TComponent[] components = new TComponent[16];
public ref readonly TComponent Get(int entityID) public bool Any()
{
return ref components[entityIDToStorageIndex[entityID]];
}
public override object Debug_Get(int entityID)
{
return components[entityIDToStorageIndex[entityID]];
}
public ref readonly TComponent Get()
{
#if DEBUG
if (nextID == 0)
{ {
throw new ArgumentOutOfRangeException("Component storage is empty!"); return nextID > 0;
} }
#endif
return ref components[0];
}
public void Set(int entityID, in TComponent component) public override bool Has(int entityID)
{
if (!entityIDToStorageIndex.ContainsKey(entityID))
{ {
var index = nextID; return entityIDToStorageIndex.ContainsKey(entityID);
nextID += 1; }
if (index >= components.Length) public ref readonly TComponent Get(int entityID)
{
return ref components[entityIDToStorageIndex[entityID]];
}
public override object Debug_Get(int entityID)
{
return components[entityIDToStorageIndex[entityID]];
}
public ref readonly TComponent Get()
{
#if DEBUG
if (nextID == 0)
{ {
Array.Resize(ref components, components.Length * 2); throw new ArgumentOutOfRangeException("Component storage is empty!");
Array.Resize(ref entityIDs, entityIDs.Length * 2); }
#endif
return ref components[0];
}
public void Set(int entityID, in TComponent component)
{
if (!entityIDToStorageIndex.ContainsKey(entityID))
{
var index = nextID;
nextID += 1;
if (index >= components.Length)
{
Array.Resize(ref components, components.Length * 2);
Array.Resize(ref entityIDs, entityIDs.Length * 2);
}
entityIDToStorageIndex[entityID] = index;
entityIDs[index] = entityID;
} }
entityIDToStorageIndex[entityID] = index; components[entityIDToStorageIndex[entityID]] = component;
entityIDs[index] = entityID;
} }
components[entityIDToStorageIndex[entityID]] = component; public override void Remove(int entityID)
}
public override void Remove(int entityID)
{
if (entityIDToStorageIndex.ContainsKey(entityID))
{ {
var storageIndex = entityIDToStorageIndex[entityID]; if (entityIDToStorageIndex.ContainsKey(entityID))
entityIDToStorageIndex.Remove(entityID);
var lastElementIndex = nextID - 1;
// move a component into the hole to maintain contiguous memory
if (lastElementIndex != storageIndex)
{ {
var lastEntityID = entityIDs[lastElementIndex]; var storageIndex = entityIDToStorageIndex[entityID];
entityIDToStorageIndex[lastEntityID] = storageIndex; entityIDToStorageIndex.Remove(entityID);
components[storageIndex] = components[lastElementIndex];
entityIDs[storageIndex] = lastEntityID; var lastElementIndex = nextID - 1;
// move a component into the hole to maintain contiguous memory
if (lastElementIndex != storageIndex)
{
var lastEntityID = entityIDs[lastElementIndex];
entityIDToStorageIndex[lastEntityID] = storageIndex;
components[storageIndex] = components[lastElementIndex];
entityIDs[storageIndex] = lastEntityID;
}
nextID -= 1;
} }
nextID -= 1;
} }
}
public void Clear() public void Clear()
{ {
nextID = 0; nextID = 0;
entityIDToStorageIndex.Clear(); entityIDToStorageIndex.Clear();
} }
public ReadOnlySpan<TComponent> AllComponents() public ReadOnlySpan<TComponent> AllComponents()
{ {
return new ReadOnlySpan<TComponent>(components, 0, nextID); return new ReadOnlySpan<TComponent>(components, 0, nextID);
} }
public Entity FirstEntity() public Entity FirstEntity()
{ {
return new Entity(entityIDs[0]); return new Entity(entityIDs[0]);
}
} }
} }

View File

@ -1,7 +1,10 @@
// NOTE: these methods are very inefficient // NOTE: these methods are very inefficient
// this class should only be used in debugging contexts!! // this class should only be used in debugging contexts!!
#if DEBUG #if DEBUG
using System;
using System.Collections.Generic;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
public abstract class DebugSystem : System public abstract class DebugSystem : System

View File

@ -1,26 +1,29 @@
namespace MoonTools.ECS; using System;
public struct Entity : IEquatable<Entity> namespace MoonTools.ECS
{ {
public int ID { get; } public struct Entity : IEquatable<Entity>
internal Entity(int id)
{ {
ID = id; public int ID { get; }
}
public override bool Equals(object? obj) internal Entity(int id)
{ {
return obj is Entity entity && Equals(entity); ID = id;
} }
public override int GetHashCode() public override bool Equals(object? obj)
{ {
return HashCode.Combine(ID); return obj is Entity entity && Equals(entity);
} }
public bool Equals(Entity other) public override int GetHashCode()
{ {
return ID == other.ID; return HashCode.Combine(ID);
}
public bool Equals(Entity other)
{
return ID == other.ID;
}
} }
} }

View File

@ -1,79 +1,83 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
public abstract class EntityComponentReader namespace MoonTools.ECS
{ {
internal EntityStorage EntityStorage; public abstract class EntityComponentReader
internal ComponentDepot ComponentDepot;
internal RelationDepot RelationDepot;
protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot);
internal void RegisterEntityStorage(EntityStorage entityStorage)
{ {
EntityStorage = entityStorage; internal EntityStorage EntityStorage;
} internal ComponentDepot ComponentDepot;
internal RelationDepot RelationDepot;
protected FilterBuilder FilterBuilder => new FilterBuilder(ComponentDepot);
internal void RegisterComponentDepot(ComponentDepot componentDepot) internal void RegisterEntityStorage(EntityStorage entityStorage)
{ {
ComponentDepot = componentDepot; EntityStorage = entityStorage;
} }
internal void RegisterRelationDepot(RelationDepot relationDepot) internal void RegisterComponentDepot(ComponentDepot componentDepot)
{ {
RelationDepot = relationDepot; ComponentDepot = componentDepot;
} }
protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct internal void RegisterRelationDepot(RelationDepot relationDepot)
{ {
return ComponentDepot.ReadComponents<TComponent>(); RelationDepot = relationDepot;
} }
protected bool Has<TComponent>(in Entity entity) where TComponent : struct protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
{ {
return ComponentDepot.Has<TComponent>(entity.ID); return ComponentDepot.ReadComponents<TComponent>();
} }
protected bool Some<TComponent>() where TComponent : struct protected bool Has<TComponent>(in Entity entity) where TComponent : struct
{ {
return ComponentDepot.Some<TComponent>(); return ComponentDepot.Has<TComponent>(entity.ID);
} }
protected ref readonly TComponent Get<TComponent>(in Entity entity) where TComponent : struct protected bool Some<TComponent>() where TComponent : struct
{ {
return ref ComponentDepot.Get<TComponent>(entity.ID); return ComponentDepot.Some<TComponent>();
} }
protected ref readonly TComponent GetSingleton<TComponent>() where TComponent : struct protected ref readonly TComponent Get<TComponent>(in Entity entity) where TComponent : struct
{ {
return ref ComponentDepot.Get<TComponent>(); return ref ComponentDepot.Get<TComponent>(entity.ID);
} }
protected Entity GetSingletonEntity<TComponent>() where TComponent : struct protected ref readonly TComponent GetSingleton<TComponent>() where TComponent : struct
{ {
return ComponentDepot.GetSingletonEntity<TComponent>(); return ref ComponentDepot.Get<TComponent>();
} }
protected bool Exists(in Entity entity) protected Entity GetSingletonEntity<TComponent>() where TComponent : struct
{ {
return EntityStorage.Exists(entity); return ComponentDepot.GetSingletonEntity<TComponent>();
} }
protected IEnumerable<Relation> Relations<TRelationKind>() protected bool Exists(in Entity entity)
{ {
return RelationDepot.Relations<TRelationKind>(); return EntityStorage.Exists(entity);
} }
protected bool Related<TRelationKind>(in Entity a, in Entity b) protected IEnumerable<Relation> Relations<TRelationKind>()
{ {
return RelationDepot.Related<TRelationKind>(a.ID, b.ID); return RelationDepot.Relations<TRelationKind>();
} }
protected IEnumerable<Entity> RelatedToA<TRelationKind>(in Entity entity) protected bool Related<TRelationKind>(in Entity a, in Entity b)
{ {
return RelationDepot.RelatedToA<TRelationKind>(entity.ID); return RelationDepot.Related<TRelationKind>(a.ID, b.ID);
} }
protected IEnumerable<Entity> RelatedToB<TRelationKind>(in Entity entity) protected IEnumerable<Entity> RelatedToA<TRelationKind>(in Entity entity)
{ {
return RelationDepot.RelatedToB<TRelationKind>(entity.ID); return RelationDepot.RelatedToA<TRelationKind>(entity.ID);
}
protected IEnumerable<Entity> RelatedToB<TRelationKind>(in Entity entity)
{
return RelationDepot.RelatedToB<TRelationKind>(entity.ID);
}
} }
} }

View File

@ -1,21 +1,22 @@
namespace MoonTools.ECS; namespace MoonTools.ECS
internal class EntityStorage
{ {
public IDStorage idStorage = new IDStorage(); internal class EntityStorage
public Entity Create()
{ {
return new Entity(idStorage.NextID()); public IDStorage idStorage = new IDStorage();
}
public bool Exists(in Entity entity) public Entity Create()
{ {
return idStorage.Taken(entity.ID); return new Entity(idStorage.NextID());
} }
public void Destroy(in Entity entity) public bool Exists(in Entity entity)
{ {
idStorage.Release(entity.ID); return idStorage.Taken(entity.ID);
}
public void Destroy(in Entity entity)
{
idStorage.Release(entity.ID);
}
} }
} }

View File

@ -1,20 +1,24 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
public class Filter namespace MoonTools.ECS
{ {
internal FilterSignature Signature; public class Filter
private ComponentDepot ComponentDepot;
internal Filter(ComponentDepot componentDepot, HashSet<Type> included, HashSet<Type> excluded)
{ {
ComponentDepot = componentDepot; internal FilterSignature Signature;
Signature = new FilterSignature(included, excluded); private ComponentDepot ComponentDepot;
internal Filter(ComponentDepot componentDepot, HashSet<Type> included, HashSet<Type> excluded)
{
ComponentDepot = componentDepot;
Signature = new FilterSignature(included, excluded);
}
public IEnumerable<Entity> Entities => ComponentDepot.FilterEntities(this);
public IEnumerable<Entity> EntitiesInRandomOrder => ComponentDepot.FilterEntitiesRandom(this);
public Entity RandomEntity => ComponentDepot.FilterRandomEntity(this);
public int Count => ComponentDepot.FilterCount(this);
public bool Empty => Count == 0;
} }
public IEnumerable<Entity> Entities => ComponentDepot.FilterEntities(this);
public IEnumerable<Entity> EntitiesInRandomOrder => ComponentDepot.FilterEntitiesRandom(this);
public Entity RandomEntity => ComponentDepot.FilterRandomEntity(this);
public int Count => ComponentDepot.FilterCount(this);
public bool Empty => Count == 0;
} }

View File

@ -1,41 +1,45 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
public struct FilterBuilder namespace MoonTools.ECS
{ {
private ComponentDepot ComponentDepot; public struct FilterBuilder
private HashSet<Type> Included;
private HashSet<Type> Excluded;
internal FilterBuilder(ComponentDepot componentDepot)
{ {
ComponentDepot = componentDepot; private ComponentDepot ComponentDepot;
Included = new HashSet<Type>(); private HashSet<Type> Included;
Excluded = new HashSet<Type>(); private HashSet<Type> Excluded;
}
private FilterBuilder(ComponentDepot componentDepot, HashSet<Type> included, HashSet<Type> excluded) internal FilterBuilder(ComponentDepot componentDepot)
{ {
ComponentDepot = componentDepot; ComponentDepot = componentDepot;
Included = included; Included = new HashSet<Type>();
Excluded = excluded; Excluded = new HashSet<Type>();
} }
public FilterBuilder Include<TComponent>() where TComponent : struct private FilterBuilder(ComponentDepot componentDepot, HashSet<Type> included, HashSet<Type> excluded)
{ {
ComponentDepot.Register<TComponent>(); ComponentDepot = componentDepot;
Included.Add(typeof(TComponent)); Included = included;
return new FilterBuilder(ComponentDepot, Included, Excluded); Excluded = excluded;
} }
public FilterBuilder Exclude<TComponent>() where TComponent : struct public FilterBuilder Include<TComponent>() where TComponent : struct
{ {
ComponentDepot.Register<TComponent>(); ComponentDepot.Register<TComponent>();
Excluded.Add(typeof(TComponent)); Included.Add(typeof(TComponent));
return new FilterBuilder(ComponentDepot, Included, Excluded); return new FilterBuilder(ComponentDepot, Included, Excluded);
} }
public Filter Build() public FilterBuilder Exclude<TComponent>() where TComponent : struct
{ {
return ComponentDepot.CreateFilter(Included, Excluded); ComponentDepot.Register<TComponent>();
Excluded.Add(typeof(TComponent));
return new FilterBuilder(ComponentDepot, Included, Excluded);
}
public Filter Build()
{
return ComponentDepot.CreateFilter(Included, Excluded);
}
} }
} }

View File

@ -1,47 +1,51 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
public struct FilterSignature namespace MoonTools.ECS
{ {
private const int HASH_FACTOR = 97; public struct FilterSignature
public HashSet<Type> Included;
public HashSet<Type> Excluded;
public FilterSignature(HashSet<Type> included, HashSet<Type> excluded)
{ {
Included = included; private const int HASH_FACTOR = 97;
Excluded = excluded;
}
public override bool Equals(object? obj) public HashSet<Type> Included;
{ public HashSet<Type> Excluded;
return obj is FilterSignature signature && Equals(signature);
}
public bool Equals(FilterSignature other) public FilterSignature(HashSet<Type> included, HashSet<Type> excluded)
{
return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded);
}
private int GuidToInt(Guid guid)
{
return BitConverter.ToInt32(guid.ToByteArray());
}
public override int GetHashCode()
{
int result = 1;
foreach (var type in Included)
{ {
result *= HASH_FACTOR + GuidToInt(type.GUID); Included = included;
Excluded = excluded;
} }
// FIXME: Is there a way to avoid collisions when this is the same set as included? public override bool Equals(object? obj)
foreach (var type in Excluded)
{ {
result *= HASH_FACTOR + GuidToInt(type.GUID); return obj is FilterSignature signature && Equals(signature);
} }
return result; public bool Equals(FilterSignature other)
{
return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded);
}
private int GuidToInt(Guid guid)
{
return BitConverter.ToInt32(guid.ToByteArray());
}
public override int GetHashCode()
{
int result = 1;
foreach (var type in Included)
{
result *= HASH_FACTOR + GuidToInt(type.GUID);
}
// FIXME: Is there a way to avoid collisions when this is the same set as included?
foreach (var type in Excluded)
{
result *= HASH_FACTOR + GuidToInt(type.GUID);
}
return result;
}
} }
} }

View File

@ -1,36 +1,39 @@
namespace MoonTools.ECS; using System.Collections.Generic;
internal class IDStorage namespace MoonTools.ECS
{ {
private int nextID = 0; internal class IDStorage
private readonly Stack<int> availableIDs = new Stack<int>();
private readonly HashSet<int> availableIDHash = new HashSet<int>();
public int NextID()
{ {
if (availableIDs.Count > 0) private int nextID = 0;
private readonly Stack<int> availableIDs = new Stack<int>();
private readonly HashSet<int> availableIDHash = new HashSet<int>();
public int NextID()
{ {
var id = availableIDs.Pop(); if (availableIDs.Count > 0)
availableIDHash.Remove(id); {
return id; var id = availableIDs.Pop();
availableIDHash.Remove(id);
return id;
}
else
{
var id = nextID;
nextID += 1;
return id;
}
} }
else
public bool Taken(int id)
{ {
var id = nextID; return !availableIDHash.Contains(id) && id < nextID;
nextID += 1;
return id;
} }
}
public bool Taken(int id) public void Release(int id)
{ {
return !availableIDHash.Contains(id) && id < nextID; availableIDs.Push(id);
} availableIDHash.Add(id);
}
public void Release(int id)
{
availableIDs.Push(id);
availableIDHash.Add(id);
} }
} }

View File

@ -1,6 +1,7 @@
namespace MoonTools.ECS; namespace MoonTools.ECS
public interface IHasEntity
{ {
Entity Entity { get; } public interface IHasEntity
{
Entity Entity { get; }
}
} }

View File

@ -1,4 +1,6 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {

View File

@ -1,59 +1,63 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
internal class MessageDepot namespace MoonTools.ECS
{ {
private Dictionary<Type, MessageStorage> storages = new Dictionary<Type, MessageStorage>(); internal class MessageDepot
private MessageStorage<TMessage> Lookup<TMessage>() where TMessage : struct
{ {
if (!storages.ContainsKey(typeof(TMessage))) private Dictionary<Type, MessageStorage> storages = new Dictionary<Type, MessageStorage>();
private MessageStorage<TMessage> Lookup<TMessage>() where TMessage : struct
{ {
storages.Add(typeof(TMessage), new MessageStorage<TMessage>()); if (!storages.ContainsKey(typeof(TMessage)))
{
storages.Add(typeof(TMessage), new MessageStorage<TMessage>());
}
return storages[typeof(TMessage)] as MessageStorage<TMessage>;
} }
return storages[typeof(TMessage)] as MessageStorage<TMessage>; public void Add<TMessage>(in TMessage message) where TMessage : struct
}
public void Add<TMessage>(in TMessage message) where TMessage : struct
{
Lookup<TMessage>().Add(message);
}
public bool Some<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().Some();
}
public ReadOnlySpan<TMessage> All<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().All();
}
public TMessage First<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().First();
}
public IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return Lookup<TMessage>().WithEntity(entityID);
}
public ref readonly TMessage FirstWithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return ref Lookup<TMessage>().FirstWithEntity(entityID);
}
public bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return Lookup<TMessage>().SomeWithEntity(entityID);
}
public void Clear()
{
foreach (var storage in storages.Values)
{ {
storage.Clear(); Lookup<TMessage>().Add(message);
}
public bool Some<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().Some();
}
public ReadOnlySpan<TMessage> All<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().All();
}
public TMessage First<TMessage>() where TMessage : struct
{
return Lookup<TMessage>().First();
}
public IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return Lookup<TMessage>().WithEntity(entityID);
}
public ref readonly TMessage FirstWithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return ref Lookup<TMessage>().FirstWithEntity(entityID);
}
public bool SomeWithEntity<TMessage>(int entityID) where TMessage : struct, IHasEntity
{
return Lookup<TMessage>().SomeWithEntity(entityID);
}
public void Clear()
{
foreach (var storage in storages.Values)
{
storage.Clear();
}
} }
} }
} }

View File

@ -1,87 +1,91 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
internal abstract class MessageStorage namespace MoonTools.ECS
{ {
public abstract void Clear(); internal abstract class MessageStorage
}
internal class MessageStorage<TMessage> : MessageStorage where TMessage : struct
{
private int count = 0;
private int capacity = 128;
private TMessage[] messages;
private Dictionary<int, List<int>> entityToIndices = new Dictionary<int, List<int>>();
public MessageStorage()
{ {
messages = new TMessage[capacity]; public abstract void Clear();
} }
public void Add(in TMessage message) internal class MessageStorage<TMessage> : MessageStorage where TMessage : struct
{ {
if (count == capacity) private int count = 0;
private int capacity = 128;
private TMessage[] messages;
private Dictionary<int, List<int>> entityToIndices = new Dictionary<int, List<int>>();
public MessageStorage()
{ {
capacity *= 2; messages = new TMessage[capacity];
Array.Resize(ref messages, capacity);
} }
messages[count] = message; public void Add(in TMessage message)
if (message is IHasEntity entityMessage)
{ {
if (!entityToIndices.ContainsKey(entityMessage.Entity.ID)) if (count == capacity)
{ {
entityToIndices.Add(entityMessage.Entity.ID, new List<int>()); capacity *= 2;
Array.Resize(ref messages, capacity);
} }
entityToIndices[entityMessage.Entity.ID].Add(count); messages[count] = message;
if (message is IHasEntity entityMessage)
{
if (!entityToIndices.ContainsKey(entityMessage.Entity.ID))
{
entityToIndices.Add(entityMessage.Entity.ID, new List<int>());
}
entityToIndices[entityMessage.Entity.ID].Add(count);
}
count += 1;
} }
count += 1; public bool Some()
}
public bool Some()
{
return count > 0;
}
public ReadOnlySpan<TMessage> All()
{
return new ReadOnlySpan<TMessage>(messages, 0, count);
}
public TMessage First()
{
return messages[0];
}
public IEnumerable<TMessage> WithEntity(int entityID)
{
if (entityToIndices.ContainsKey(entityID))
{ {
foreach (var index in entityToIndices[entityID]) return count > 0;
}
public ReadOnlySpan<TMessage> All()
{
return new ReadOnlySpan<TMessage>(messages, 0, count);
}
public TMessage First()
{
return messages[0];
}
public IEnumerable<TMessage> WithEntity(int entityID)
{
if (entityToIndices.ContainsKey(entityID))
{ {
yield return messages[index]; foreach (var index in entityToIndices[entityID])
{
yield return messages[index];
}
}
}
public ref readonly TMessage FirstWithEntity(int entityID)
{
return ref messages[entityToIndices[entityID][0]];
}
public bool SomeWithEntity(int entityID)
{
return entityToIndices.ContainsKey(entityID) && entityToIndices[entityID].Count > 0;
}
public override void Clear()
{
count = 0;
foreach (var set in entityToIndices.Values)
{
set.Clear();
} }
} }
} }
public ref readonly TMessage FirstWithEntity(int entityID)
{
return ref messages[entityToIndices[entityID][0]];
}
public bool SomeWithEntity(int entityID)
{
return entityToIndices.ContainsKey(entityID) && entityToIndices[entityID].Count > 0;
}
public override void Clear()
{
count = 0;
foreach (var set in entityToIndices.Values)
{
set.Clear();
}
}
} }

View File

@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
internal static class RandomGenerator internal static class RandomGenerator

View File

@ -1,3 +1,5 @@
using System;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
public struct Relation : IEquatable<Relation> public struct Relation : IEquatable<Relation>

View File

@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
internal class RelationDepot internal class RelationDepot

View File

@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
internal class RelationStorage internal class RelationStorage

View File

@ -1,9 +1,10 @@
namespace MoonTools.ECS; namespace MoonTools.ECS
public abstract class Renderer : EntityComponentReader
{ {
public Renderer(World world) public abstract class Renderer : EntityComponentReader
{ {
world.AddRenderer(this); public Renderer(World world)
{
world.AddRenderer(this);
}
} }
} }

View File

@ -1,92 +1,96 @@
namespace MoonTools.ECS; using System;
using System.Collections.Generic;
public abstract class System : EntityComponentReader namespace MoonTools.ECS
{ {
internal MessageDepot MessageDepot; public abstract class System : EntityComponentReader
internal void RegisterMessageDepot(MessageDepot messageDepot)
{ {
MessageDepot = messageDepot; internal MessageDepot MessageDepot;
}
public System(World world) internal void RegisterMessageDepot(MessageDepot messageDepot)
{
world.AddSystem(this);
}
public abstract void Update(TimeSpan delta);
protected Entity CreateEntity()
{
return EntityStorage.Create();
}
protected void Set<TComponent>(in Entity entity, in TComponent component) where TComponent : struct
{
#if DEBUG
// check for use after destroy
if (!Exists(entity))
{ {
throw new ArgumentException("This entity is not valid!"); MessageDepot = messageDepot;
} }
#endif
ComponentDepot.Set<TComponent>(entity.ID, component);
}
protected void Remove<TComponent>(in Entity entity) where TComponent : struct public System(World world)
{ {
ComponentDepot.Remove<TComponent>(entity.ID); world.AddSystem(this);
} }
protected ReadOnlySpan<TMessage> ReadMessages<TMessage>() where TMessage : struct public abstract void Update(TimeSpan delta);
{
return MessageDepot.All<TMessage>();
}
protected TMessage ReadMessage<TMessage>() where TMessage : struct protected Entity CreateEntity()
{ {
return MessageDepot.First<TMessage>(); return EntityStorage.Create();
} }
protected bool SomeMessage<TMessage>() where TMessage : struct protected void Set<TComponent>(in Entity entity, in TComponent component) where TComponent : struct
{ {
return MessageDepot.Some<TMessage>(); #if DEBUG
} // check for use after destroy
if (!Exists(entity))
{
throw new ArgumentException("This entity is not valid!");
}
#endif
ComponentDepot.Set<TComponent>(entity.ID, component);
}
protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity protected void Remove<TComponent>(in Entity entity) where TComponent : struct
{ {
return MessageDepot.WithEntity<TMessage>(entity.ID); ComponentDepot.Remove<TComponent>(entity.ID);
} }
protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity protected ReadOnlySpan<TMessage> ReadMessages<TMessage>() where TMessage : struct
{ {
return ref MessageDepot.FirstWithEntity<TMessage>(entity.ID); return MessageDepot.All<TMessage>();
} }
protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity protected TMessage ReadMessage<TMessage>() where TMessage : struct
{ {
return MessageDepot.SomeWithEntity<TMessage>(entity.ID); return MessageDepot.First<TMessage>();
} }
protected void Send<TMessage>(in TMessage message) where TMessage : struct protected bool SomeMessage<TMessage>() where TMessage : struct
{ {
MessageDepot.Add(message); return MessageDepot.Some<TMessage>();
} }
protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB) protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
{ {
RelationDepot.Add<TRelationKind>(new Relation(entityA, entityB)); return MessageDepot.WithEntity<TMessage>(entity.ID);
} }
protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) protected ref readonly TMessage ReadMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
{ {
RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB)); return ref MessageDepot.FirstWithEntity<TMessage>(entity.ID);
} }
protected void Destroy(in Entity entity) protected bool SomeMessageWithEntity<TMessage>(in Entity entity) where TMessage : struct, IHasEntity
{ {
ComponentDepot.OnEntityDestroy(entity.ID); return MessageDepot.SomeWithEntity<TMessage>(entity.ID);
RelationDepot.OnEntityDestroy(entity.ID); }
EntityStorage.Destroy(entity);
protected void Send<TMessage>(in TMessage message) where TMessage : struct
{
MessageDepot.Add(message);
}
protected void Relate<TRelationKind>(in Entity entityA, in Entity entityB)
{
RelationDepot.Add<TRelationKind>(new Relation(entityA, entityB));
}
protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB)
{
RelationDepot.Remove<TRelationKind>(new Relation(entityA, entityB));
}
protected void Destroy(in Entity entity)
{
ComponentDepot.OnEntityDestroy(entity.ID);
RelationDepot.OnEntityDestroy(entity.ID);
EntityStorage.Destroy(entity);
}
} }
} }

View File

@ -1,49 +1,50 @@
namespace MoonTools.ECS; namespace MoonTools.ECS
public class World
{ {
private readonly EntityStorage EntityStorage = new EntityStorage(); public class World
private readonly ComponentDepot ComponentDepot = new ComponentDepot();
private readonly MessageDepot MessageDepot = new MessageDepot();
private readonly RelationDepot RelationDepot = new RelationDepot();
internal void AddSystem(System system)
{ {
system.RegisterEntityStorage(EntityStorage); private readonly EntityStorage EntityStorage = new EntityStorage();
system.RegisterComponentDepot(ComponentDepot); private readonly ComponentDepot ComponentDepot = new ComponentDepot();
system.RegisterMessageDepot(MessageDepot); private readonly MessageDepot MessageDepot = new MessageDepot();
system.RegisterRelationDepot(RelationDepot); private readonly RelationDepot RelationDepot = new RelationDepot();
}
internal void AddRenderer(Renderer renderer) internal void AddSystem(System system)
{ {
renderer.RegisterEntityStorage(EntityStorage); system.RegisterEntityStorage(EntityStorage);
renderer.RegisterComponentDepot(ComponentDepot); system.RegisterComponentDepot(ComponentDepot);
renderer.RegisterRelationDepot(RelationDepot); system.RegisterMessageDepot(MessageDepot);
} system.RegisterRelationDepot(RelationDepot);
}
public void AddRelationKind<TRelationKind>() internal void AddRenderer(Renderer renderer)
{ {
RelationDepot.Register<TRelationKind>(); renderer.RegisterEntityStorage(EntityStorage);
} renderer.RegisterComponentDepot(ComponentDepot);
renderer.RegisterRelationDepot(RelationDepot);
}
public Entity CreateEntity() public void AddRelationKind<TRelationKind>()
{ {
return EntityStorage.Create(); RelationDepot.Register<TRelationKind>();
} }
public void Set<TComponent>(Entity entity, in TComponent component) where TComponent : struct public Entity CreateEntity()
{ {
ComponentDepot.Set(entity.ID, component); return EntityStorage.Create();
} }
public void Send<TMessage>(in TMessage message) where TMessage : struct public void Set<TComponent>(Entity entity, in TComponent component) where TComponent : struct
{ {
MessageDepot.Add(message); ComponentDepot.Set(entity.ID, component);
} }
public void FinishUpdate() public void Send<TMessage>(in TMessage message) where TMessage : struct
{ {
MessageDepot.Clear(); MessageDepot.Add(message);
}
public void FinishUpdate()
{
MessageDepot.Clear();
}
} }
} }