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> typeToComponentIDs = new Dictionary>(); private readonly List activeComponents = new List(); private readonly List inactiveComponents = new List(); private readonly HashSet componentsMarkedForActivation = new HashSet(); private readonly HashSet componentsMarkedForDeactivation = new HashSet(); private readonly HashSet componentsMarkedForRemoval = new HashSet(); private readonly Dictionary pendingUpdates = new Dictionary(); //shared references with EntityManager private readonly HashSet entitiesWithAddedComponents; private readonly HashSet entitiesWithRemovedComponents; public ComponentManager( DrawLayerManager drawLayerManager, HashSet entitiesWithAddedComponents, HashSet entitiesWithRemovedComponents ) { this.drawLayerManager = drawLayerManager; this.entitiesWithAddedComponents = entitiesWithAddedComponents; this.entitiesWithRemovedComponents = entitiesWithRemovedComponents; } internal void RegisterEntity(Guid entityID) { entityIDToComponentIDs.Add(entityID, new HashSet()); } internal Guid AddComponent(Guid entityID, TComponent component) where TComponent : struct, IComponent { var componentID = Guid.NewGuid(); IDToComponent[componentID] = component; componentIDToType[componentID] = typeof(TComponent); if (!typeToComponentIDs.ContainsKey(typeof(TComponent))) { typeToComponentIDs.Add(typeof(TComponent), new HashSet()); } typeToComponentIDs[typeof(TComponent)].Add(componentID); entityIDToComponentIDs[entityID].Add(componentID); componentIDToEntityID[componentID] = entityID; activeComponents.Add(componentID); entitiesWithAddedComponents.Add(entityID); return componentID; } internal Guid AddDrawComponent(Guid entityID, TComponent component, int layer = 0) where TComponent : struct, IComponent { var componentID = AddComponent(entityID, component); drawLayerManager.RegisterComponentWithLayer(componentID, layer); return componentID; } internal IEnumerable GetComponentIDsByEntityID(Guid entityID) { return entityIDToComponentIDs.ContainsKey(entityID) ? entityIDToComponentIDs[entityID] : Enumerable.Empty(); } internal IEnumerable> GetComponentsByEntity(Guid entityID) { return GetComponentIDsByEntityID(entityID).Intersect(activeComponents).Select((id) => new ValueTuple(id, IDToComponent[id])); } internal IEnumerable> GetActiveComponentsByType() where TComponent : struct, IComponent { return typeToComponentIDs.ContainsKey(typeof(TComponent)) ? typeToComponentIDs[typeof(TComponent)].Intersect(activeComponents).Select((id) => new ValueTuple(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) : Enumerable.Empty>(); } internal IEnumerable> GetActiveComponentsByType(Type type) { return typeToComponentIDs.ContainsKey(type) ? typeToComponentIDs[type].Intersect(activeComponents).Select((id) => new ValueTuple(id, IDToComponent[id])) : Enumerable.Empty>(); } internal IEnumerable> GetComponentsByEntityAndType(Guid entityID) where TComponent : struct, IComponent { var entityComponentsByType = GetComponentsByEntity(entityID).Where((pair) => componentIDToType[pair.Item1] == typeof(TComponent)).Select((pair) => new ValueTuple(pair.Item1, (TComponent)pair.Item2)); var activeComponentsByType = GetActiveComponentsByType(); return activeComponentsByType.Select((triple) => (triple.Item2, triple.Item3)).Intersect(entityComponentsByType); } internal IEnumerable> GetComponentsByEntityAndType(Guid entityID, Type type) { var entityComponents = GetComponentsByEntity(entityID); var activeComponentsByType = GetActiveComponentsByType(type); return entityComponents.Intersect(activeComponentsByType); } internal IEnumerable GetAllComponentTypesOfEntity(Guid entityID) { return GetComponentIDsByEntityID(entityID).Select((id) => componentIDToType[id]); } internal bool EntityHasComponentOfType(Guid entityID) where TComponent : struct, IComponent { return GetComponentsByEntityAndType(entityID).Any(); } internal bool EntityHasComponentOfType(Guid entityID, Type type) { return GetComponentsByEntityAndType(entityID, type).Any(); } 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 with ID {0} was updated multiple times this frame", 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 Activate(Guid componentID) { if (inactiveComponents.Remove(componentID)) { activeComponents.Add(componentID); } var entityID = GetEntityIDByComponentID(componentID); entitiesWithAddedComponents.Add(entityID); } internal void MarkForDeactivation(Guid componentID) { componentsMarkedForDeactivation.Add(componentID); } internal void DeactivateMarkedComponents() { foreach (var componentID in componentsMarkedForDeactivation) { Deactivate(componentID); } componentsMarkedForDeactivation.Clear(); } private void Deactivate(Guid componentID) { if (activeComponents.Remove(componentID)) { inactiveComponents.Add(componentID); } var entityID = GetEntityIDByComponentID(componentID); entitiesWithRemovedComponents.Add(entityID); } 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 component = IDToComponent[componentID]; var type = componentIDToType[componentID]; activeComponents.Remove(componentID); inactiveComponents.Remove(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); entitiesWithRemovedComponents.Add(entityID); } public void RegisterDestroyedEntity(Guid entityID) { entityIDToComponentIDs.Remove(entityID); } } }