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