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 
31 CORE_BEGIN_NAMESPACE()
32 using BASE_NS::array_view;
33 using BASE_NS::move;
34 
~ComponentQuery()35 ComponentQuery::~ComponentQuery()
36 {
37     UnregisterEcsListeners();
38 }
39 
IsValidComponentId(size_t index) const40 bool 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 
SetupQuery(const IComponentManager & baseComponentSet,const array_view<const Operation> operations,bool enableEntityLookup)48 void 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 
Execute()78 bool 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 
Execute(const IComponentManager & baseComponentSet,const array_view<const Operation> operations,bool enableEntityLookup)149 void ComponentQuery::Execute(
150     const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup)
151 {
152     SetupQuery(baseComponentSet, operations, enableEntityLookup);
153     Execute();
154 }
155 
SetEcsListenersEnabled(bool enableListeners)156 void ComponentQuery::SetEcsListenersEnabled(bool enableListeners)
157 {
158     enableListeners_ = enableListeners;
159     if (enableListeners_) {
160         RegisterEcsListeners();
161     } else {
162         UnregisterEcsListeners();
163     }
164 }
165 
IsValid() const166 bool ComponentQuery::IsValid() const
167 {
168     return valid_;
169 }
170 
GetResults() const171 array_view<const ComponentQuery::ResultRow> ComponentQuery::GetResults() const
172 {
173     return { result_.data(), result_.size() };
174 }
175 
FindResultRow(Entity entity) const176 const 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 
RegisterEcsListeners()188 void 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 
UnregisterEcsListeners()203 void 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 
OnEntityEvent(const IEcs::EntityListener::EventType type,const array_view<const Entity> entities)216 void 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 
OnComponentEvent(const IEcs::ComponentListener::EventType type,const IComponentManager &,const array_view<const Entity>)253 void 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 }
261 CORE_END_NAMESPACE()
262