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