• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_) || managers_.empty() || !managers_[0]) {
81         // No changes detected since previous execute.
82         // Query setup not done.
83         // Base manager is null.
84         return false;
85     }
86 
87     const IComponentManager& baseComponentSet = *managers_[0];
88 
89     const auto baseComponents = baseComponentSet.GetComponentCount();
90     result_.resize(baseComponents);
91 
92     auto& em = baseComponentSet.GetEcs().GetEntityManager();
93     const size_t managerCount = managers_.size();
94     size_t index = 0U;
95     for (IComponentManager::ComponentId id = 0; id < baseComponents; ++id) {
96         const Entity entity = baseComponentSet.GetEntity(id);
97         if (!em.IsAlive(entity)) {
98             continue;
99         }
100         auto& row = result_[index];
101         row.entity = entity;
102         row.components.resize(managerCount, IComponentManager::INVALID_COMPONENT_ID);
103         row.components[0U] = id;
104 
105         bool valid = true;
106 
107         // NOTE: starting from index 1 that is the first manager after the base component set.
108         for (size_t i = 1; valid && (i < managerCount); ++i) {
109             const auto& manager = managers_[i];
110             const auto componentId =
111                 manager ? manager->GetComponentId(entity) : IComponentManager::INVALID_COMPONENT_ID;
112             row.components[i] = componentId;
113 
114             switch (operationMethods_[i]) {
115                 case Operation::REQUIRE: {
116                     // for required components ID must be valid
117                     valid = (componentId != IComponentManager::INVALID_COMPONENT_ID);
118                     break;
119                 }
120 
121                 case Operation::OPTIONAL: {
122                     // for optional ID doesn't matter
123                     break;
124                 }
125 
126                 default: {
127                     valid = false;
128                 }
129             }
130         }
131 
132         if (valid) {
133             if (enableLookup_) {
134                 mapping_[entity] = index;
135             }
136             ++index;
137         }
138     }
139     result_.resize(index);
140 
141     valid_ = true;
142     return true;
143 }
144 
Execute(const IComponentManager & baseComponentSet,const array_view<const Operation> operations,bool enableEntityLookup)145 void ComponentQuery::Execute(
146     const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup)
147 {
148     SetupQuery(baseComponentSet, operations, enableEntityLookup);
149     Execute();
150 }
151 
SetEcsListenersEnabled(bool enableListeners)152 void ComponentQuery::SetEcsListenersEnabled(bool enableListeners)
153 {
154     enableListeners_ = enableListeners;
155     if (enableListeners_) {
156         RegisterEcsListeners();
157     } else {
158         UnregisterEcsListeners();
159     }
160 }
161 
IsValid() const162 bool ComponentQuery::IsValid() const
163 {
164     return valid_;
165 }
166 
GetResults() const167 array_view<const ComponentQuery::ResultRow> ComponentQuery::GetResults() const
168 {
169     return { result_.data(), result_.size() };
170 }
171 
FindResultRow(Entity entity) const172 const ComponentQuery::ResultRow* ComponentQuery::FindResultRow(Entity entity) const
173 {
174     if (EntityUtil::IsValid(entity)) {
175         const auto it = mapping_.find(entity);
176         if (it != mapping_.end() && it->second < result_.size()) {
177             return &(result_[it->second]);
178         }
179     }
180 
181     return nullptr;
182 }
183 
RegisterEcsListeners()184 void ComponentQuery::RegisterEcsListeners()
185 {
186     if (!registered_ && !managers_.empty()) {
187         // Listen to changes in managers so the result can be automatically invalidated.
188         ecs_ = &managers_[0]->GetEcs();
189         for (auto& manager : managers_) {
190             if (manager) {
191                 ecs_->AddListener(*manager, *this);
192             }
193         }
194         ecs_->AddListener(static_cast<IEcs::EntityListener&>(*this));
195         registered_ = true;
196     }
197 }
198 
UnregisterEcsListeners()199 void ComponentQuery::UnregisterEcsListeners()
200 {
201     if (registered_ && !managers_.empty() && ecs_) {
202         for (auto& manager : managers_) {
203             if (manager) {
204                 ecs_->RemoveListener(*manager, *this);
205             }
206         }
207         ecs_->RemoveListener(static_cast<IEcs::EntityListener&>(*this));
208         registered_ = false;
209     }
210 }
211 
OnEntityEvent(const IEcs::EntityListener::EventType type,const array_view<const Entity> entities)212 void ComponentQuery::OnEntityEvent(const IEcs::EntityListener::EventType type, const array_view<const Entity> entities)
213 {
214     if (type == IEcs::EntityListener::EventType::ACTIVATED || type == IEcs::EntityListener::EventType::DEACTIVATED) {
215         if (!valid_) {
216             // Listener is only used to invalidate the quety. If the query is already invalid -> no need to check
217             // anything.
218             return;
219         }
220         const auto managerCount = managers_.size();
221         for (const auto& entity : entities) {
222             // We are only interested in entities that have all the required managers.
223             bool isRelevantEntity = true;
224             for (size_t i = 0; i < managerCount; ++i) {
225                 if (operationMethods_[i] == Operation::OPTIONAL) {
226                     continue;
227                 }
228                 if (managers_[i] && !managers_[i]->HasComponent(entity)) {
229                     // This entity is missing a required manager -> irrelevant.
230                     isRelevantEntity = false;
231                     break;
232                 }
233             }
234 
235             if (isRelevantEntity) {
236                 // Marking this query as invalid. No need to iterate entities further.
237                 valid_ = false;
238                 return;
239             }
240         }
241     } else if (type == IEcs::EntityListener::EventType::DESTROYED) {
242         if (enableLookup_) {
243             for (const auto& entity : entities) {
244                 mapping_.erase(entity);
245             }
246         }
247     }
248 }
249 
OnComponentEvent(const IEcs::ComponentListener::EventType type,const IComponentManager &,const array_view<const Entity>)250 void ComponentQuery::OnComponentEvent(const IEcs::ComponentListener::EventType type,
251     const IComponentManager& /* componentManager */, const array_view<const Entity> /* entities */)
252 {
253     // We only get events from relevant managers. If they have new or deleted components, the query is no longer valid.
254     if (type == IEcs::ComponentListener::EventType::CREATED || type == IEcs::ComponentListener::EventType::DESTROYED) {
255         valid_ = false;
256     }
257 }
258 CORE_END_NAMESPACE()
259