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