1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "component_query.h" 17 18#include <base/containers/array_view.h> 19#include <base/containers/iterator.h> 20#include <base/containers/type_traits.h> 21#include <base/containers/unordered_map.h> 22#include <base/containers/vector.h> 23#include <base/namespace.h> 24#include <core/ecs/entity.h> 25#include <core/ecs/intf_component_manager.h> 26#include <core/ecs/intf_ecs.h> 27#include <core/ecs/intf_entity_manager.h> 28#include <core/log.h> 29#include <core/namespace.h> 30 31CORE_BEGIN_NAMESPACE() 32using BASE_NS::array_view; 33using BASE_NS::move; 34 35ComponentQuery::~ComponentQuery() 36{ 37 UnregisterEcsListeners(); 38} 39 40bool ComponentQuery::ResultRow::IsValidComponentId(size_t index) const 41{ 42 if (index < components.size()) { 43 return components[index] != IComponentManager::INVALID_COMPONENT_ID; 44 } 45 return false; 46} 47 48void ComponentQuery::SetupQuery( 49 const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup) 50{ 51 const size_t componentCount = operations.size() + 1; 52 enableLookup_ = enableEntityLookup; 53 54 result_.clear(); 55 valid_ = false; 56 57 // Unregistering any old listeners because the operations might not use the same managers. 58 UnregisterEcsListeners(); 59 60 managers_.clear(); 61 managers_.reserve(componentCount); 62 operationMethods_.clear(); 63 operationMethods_.reserve(componentCount); 64 65 managers_.push_back(const_cast<IComponentManager*>(&baseComponentSet)); 66 operationMethods_.push_back(Operation::REQUIRE); 67 for (auto& operation : operations) { 68 managers_.push_back(const_cast<IComponentManager*>(&operation.target)); 69 CORE_ASSERT(managers_.back()); 70 operationMethods_.push_back(operation.method); 71 } 72 73 if (enableListeners_) { 74 RegisterEcsListeners(); 75 } 76} 77 78bool ComponentQuery::Execute() 79{ 80 if (enableListeners_ && valid_) { 81 // No changes detected since previous execute. 82 return false; 83 } 84 if (managers_.empty()) { 85 // Query setup not done. 86 return false; 87 } 88 if (!managers_[0]) { 89 // Base manager is null. 90 return false; 91 } 92 93 const IComponentManager& baseComponentSet = *managers_[0]; 94 95 const auto baseComponents = baseComponentSet.GetComponentCount(); 96 result_.resize(baseComponents); 97 98 auto& em = baseComponentSet.GetEcs().GetEntityManager(); 99 const size_t managerCount = managers_.size(); 100 size_t index = 0U; 101 for (IComponentManager::ComponentId id = 0; id < baseComponents; ++id) { 102 if (const Entity entity = baseComponentSet.GetEntity(id); em.IsAlive(entity)) { 103 auto& row = result_[index]; 104 row.entity = entity; 105 row.components.resize(managerCount, IComponentManager::INVALID_COMPONENT_ID); 106 row.components[0U] = id; 107 108 bool valid = true; 109 110 // NOTE: starting from index 1 that is the first manager after the base component set. 111 for (size_t i = 1; valid && (i < managerCount); ++i) { 112 const auto& manager = managers_[i]; 113 const auto componentId = 114 manager ? manager->GetComponentId(entity) : IComponentManager::INVALID_COMPONENT_ID; 115 row.components[i] = componentId; 116 117 switch (operationMethods_[i]) { 118 case Operation::REQUIRE: { 119 // for required components ID must be valid 120 valid = (componentId != IComponentManager::INVALID_COMPONENT_ID); 121 break; 122 } 123 124 case Operation::OPTIONAL: { 125 // for optional ID doesn't matter 126 break; 127 } 128 129 default: { 130 valid = false; 131 } 132 } 133 } 134 135 if (valid) { 136 if (enableLookup_) { 137 mapping_[entity] = index; 138 } 139 ++index; 140 } 141 } 142 } 143 result_.resize(index); 144 145 valid_ = true; 146 return true; 147} 148 149void ComponentQuery::Execute( 150 const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup) 151{ 152 SetupQuery(baseComponentSet, operations, enableEntityLookup); 153 Execute(); 154} 155 156void ComponentQuery::SetEcsListenersEnabled(bool enableListeners) 157{ 158 enableListeners_ = enableListeners; 159 if (enableListeners_) { 160 RegisterEcsListeners(); 161 } else { 162 UnregisterEcsListeners(); 163 } 164} 165 166bool ComponentQuery::IsValid() const 167{ 168 return valid_; 169} 170 171array_view<const ComponentQuery::ResultRow> ComponentQuery::GetResults() const 172{ 173 return { result_.data(), result_.size() }; 174} 175 176const ComponentQuery::ResultRow* ComponentQuery::FindResultRow(Entity entity) const 177{ 178 if (EntityUtil::IsValid(entity)) { 179 const auto it = mapping_.find(entity); 180 if (it != mapping_.end() && it->second < result_.size()) { 181 return &(result_[it->second]); 182 } 183 } 184 185 return nullptr; 186} 187 188void ComponentQuery::RegisterEcsListeners() 189{ 190 if (!registered_ && !managers_.empty()) { 191 // Listen to changes in managers so the result can be automatically invalidated. 192 ecs_ = &managers_[0]->GetEcs(); 193 for (auto& manager : managers_) { 194 if (manager) { 195 ecs_->AddListener(*manager, *this); 196 } 197 } 198 ecs_->AddListener(static_cast<IEcs::EntityListener&>(*this)); 199 registered_ = true; 200 } 201} 202 203void ComponentQuery::UnregisterEcsListeners() 204{ 205 if (registered_ && !managers_.empty() && ecs_) { 206 for (auto& manager : managers_) { 207 if (manager) { 208 ecs_->RemoveListener(*manager, *this); 209 } 210 } 211 ecs_->RemoveListener(static_cast<IEcs::EntityListener&>(*this)); 212 registered_ = false; 213 } 214} 215 216void ComponentQuery::OnEntityEvent(const IEcs::EntityListener::EventType type, const array_view<const Entity> entities) 217{ 218 if (!valid_) { 219 // Listener is only used to invalidate the quety. If the query is already invalid -> no need to check anything. 220 return; 221 } 222 if (type == IEcs::EntityListener::EventType::ACTIVATED || type == IEcs::EntityListener::EventType::DEACTIVATED) { 223 const auto managerCount = managers_.size(); 224 for (const auto& entity : entities) { 225 // We are only interested in entities that have all the required managers. 226 bool isRelevantEntity = true; 227 for (size_t i = 0; i < managerCount; ++i) { 228 if (operationMethods_[i] == Operation::OPTIONAL) { 229 continue; 230 } 231 if (managers_[i] && !managers_[i]->HasComponent(entity)) { 232 // This entity is missing a required manager -> irrelevant. 233 isRelevantEntity = false; 234 break; 235 } 236 } 237 238 if (isRelevantEntity) { 239 // Marking this query as invalid. No need to iterate entities further. 240 valid_ = false; 241 return; 242 } 243 } 244 } else if (type == IEcs::EntityListener::EventType::DESTROYED) { 245 if (enableLookup_) { 246 for (const auto& entity : entities) { 247 mapping_.erase(entity); 248 } 249 } 250 } 251} 252 253void ComponentQuery::OnComponentEvent(const IEcs::ComponentListener::EventType type, 254 const IComponentManager& /* componentManager */, const array_view<const Entity> /* entities */) 255{ 256 // We only get events from relevant managers. If they have new or deleted components, the query is no longer valid. 257 if (type == IEcs::ComponentListener::EventType::CREATED || type == IEcs::ComponentListener::EventType::DESTROYED) { 258 valid_ = false; 259 } 260} 261CORE_END_NAMESPACE() 262