MoonTools.ECS/src/ComponentDepot.cs

264 lines
6.6 KiB
C#
Raw Normal View History

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