using System; using System.Collections.Generic; using System.Linq; using Encompass.Exceptions; namespace Encompass { internal class ComponentManager { private readonly DrawLayerManager drawLayerManager; private readonly Dictionary componentIDToType = new Dictionary(); private readonly Dictionary IDToComponent = new Dictionary(); private readonly Dictionary> entityIDToComponentIDs = new Dictionary>(); private readonly Dictionary componentIDToEntityID = new Dictionary(); private readonly Dictionary>> entityIDToComponentTypeToComponentIDs = new Dictionary>>(); private readonly Dictionary> typeToComponentIDs = new Dictionary>(); private readonly HashSet componentsMarkedForRemoval = new HashSet(); private readonly Dictionary pendingUpdates = new Dictionary(); public ComponentManager(DrawLayerManager drawLayerManager) { this.drawLayerManager = drawLayerManager; } internal void RegisterEntity(Guid entityID) { entityIDToComponentIDs.Add(entityID, new HashSet()); entityIDToComponentTypeToComponentIDs.Add(entityID, new Dictionary>()); } internal Guid NextID() { return Guid.NewGuid(); } internal Guid AddComponent(Entity entity, Guid componentID, TComponent component) where TComponent : struct, IComponent { IDToComponent[componentID] = component; componentIDToType[componentID] = typeof(TComponent); if (!typeToComponentIDs.ContainsKey(typeof(TComponent))) { typeToComponentIDs.Add(typeof(TComponent), new HashSet()); } typeToComponentIDs[typeof(TComponent)].Add(componentID); entityIDToComponentIDs[entity.ID].Add(componentID); if (!entityIDToComponentTypeToComponentIDs[entity.ID].ContainsKey(typeof(TComponent))) { entityIDToComponentTypeToComponentIDs[entity.ID].Add(typeof(TComponent), new HashSet()); } entityIDToComponentTypeToComponentIDs[entity.ID][typeof(TComponent)].Add(componentID); componentIDToEntityID[componentID] = entity.ID; return componentID; } internal Guid AddDrawComponent(Entity entity, Guid componentID, TComponent component, int layer = 0) where TComponent : struct, IComponent { AddComponent(entity, componentID, component); drawLayerManager.RegisterComponentWithLayer(componentID, layer); return componentID; } internal IEnumerable GetComponentIDsByEntityID(Guid entityID) { HashSet idSet; if (entityIDToComponentIDs.TryGetValue(entityID, out idSet)) { return idSet; } return Enumerable.Empty(); } internal IEnumerable<(Guid, TComponent)> GetComponentsByType() where TComponent : struct, IComponent { HashSet idSet; if (typeToComponentIDs.TryGetValue(typeof(TComponent), out idSet)) { return idSet.Select(id => (id, (TComponent)IDToComponent[id])); } return Enumerable.Empty<(Guid, TComponent)>(); } internal IEnumerable> GetComponentsByEntityAndType(Guid entityID) where TComponent : struct, IComponent { HashSet idSet; if (entityIDToComponentTypeToComponentIDs.ContainsKey(entityID) && entityIDToComponentTypeToComponentIDs[entityID].TryGetValue(typeof(TComponent), out idSet)) { return idSet.Select(id => (id, (TComponent)IDToComponent[id])); } return Enumerable.Empty<(Guid, TComponent)>(); } internal bool EntityHasComponentOfType(Guid entityID) where TComponent : struct, IComponent { HashSet idSet; if (entityIDToComponentTypeToComponentIDs.ContainsKey(entityID) && entityIDToComponentTypeToComponentIDs[entityID].TryGetValue(typeof(TComponent), out idSet)) { return idSet.Count > 0; } return false; } internal bool ComponentOfTypeExists() where TComponent : struct, IComponent { HashSet idSet; if (typeToComponentIDs.TryGetValue(typeof(TComponent), out idSet)) { return idSet.Count > 0; } return false; } internal IComponent GetComponentByID(Guid componentID) { return IDToComponent[componentID]; } internal Type GetComponentTypeByID(Guid componentID) { return componentIDToType[componentID]; } internal Guid GetEntityIDByComponentID(Guid componentID) { return componentIDToEntityID[componentID]; } internal void AddUpdateComponentOperation(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent { if (pendingUpdates.ContainsKey(componentID)) { throw new RepeatUpdateComponentException("Component {0} with ID {1} was updated multiple times this frame", typeof(TComponent).Name, componentID); } pendingUpdates.Add(componentID, newComponentValue); } internal void PerformComponentUpdates() { foreach (var idPair in pendingUpdates) { IDToComponent[idPair.Key] = idPair.Value; } pendingUpdates.Clear(); } internal void MarkAllComponentsOnEntityForRemoval(Guid entityID) { foreach (var componentID in GetComponentIDsByEntityID(entityID)) { MarkForRemoval(componentID); } } internal void MarkForRemoval(Guid componentID) { componentsMarkedForRemoval.Add(componentID); } internal void RemoveMarkedComponents() { foreach (var componentID in componentsMarkedForRemoval) { Remove(componentID); } componentsMarkedForRemoval.Clear(); } private void Remove(Guid componentID) { var type = componentIDToType[componentID]; var entityID = componentIDToEntityID[componentID]; if (entityIDToComponentIDs.ContainsKey(entityID)) { entityIDToComponentIDs[entityID].Remove(componentID); } IDToComponent.Remove(componentID); componentIDToType.Remove(componentID); componentIDToEntityID.Remove(componentID); typeToComponentIDs[type].Remove(componentID); drawLayerManager.UnRegisterComponentWithLayer(componentID); } public void RegisterDestroyedEntity(Guid entityID) { entityIDToComponentIDs.Remove(entityID); entityIDToComponentTypeToComponentIDs.Remove(entityID); } } }