1 /*
2 * Copyright (C) 2023 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 <core/ecs/intf_ecs.h>
19 #include <core/ecs/intf_entity_manager.h>
20 #include <core/log.h>
21 #include <core/namespace.h>
22
23 CORE_BEGIN_NAMESPACE()
24 using BASE_NS::array_view;
25 using BASE_NS::move;
26
~ComponentQuery()27 ComponentQuery::~ComponentQuery()
28 {
29 UnregisterEcsListeners();
30 }
31
IsValidComponentId(size_t index) const32 bool ComponentQuery::ResultRow::IsValidComponentId(size_t index) const
33 {
34 if (index < components.size()) {
35 return components[index] != IComponentManager::INVALID_COMPONENT_ID;
36 }
37 return false;
38 }
39
SetupQuery(const IComponentManager & baseComponentSet,const array_view<const Operation> operations,bool enableEntityLookup)40 void ComponentQuery::SetupQuery(
41 const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup)
42 {
43 const size_t componentCount = operations.size() + 1;
44 enableLookup_ = enableEntityLookup;
45
46 result_.clear();
47 valid_ = false;
48
49 // Unregistering any old listeners because the operations might not use the same managers.
50 UnregisterEcsListeners();
51
52 managers_.clear();
53 managers_.reserve(componentCount);
54 operationMethods_.clear();
55 operationMethods_.reserve(componentCount);
56
57 managers_.emplace_back(const_cast<IComponentManager*>(&baseComponentSet));
58 operationMethods_.emplace_back(Operation::REQUIRE);
59 for (auto& operation : operations) {
60 managers_.emplace_back(const_cast<IComponentManager*>(&operation.target));
61 CORE_ASSERT(managers_.back());
62 operationMethods_.emplace_back(operation.method);
63 }
64
65 if (enableListeners_) {
66 RegisterEcsListeners();
67 }
68 }
69
Execute()70 bool ComponentQuery::Execute()
71 {
72 if (enableListeners_ && valid_) {
73 // No changes detected since previous execute.
74 return false;
75 }
76 if (managers_.empty()) {
77 // Query setup not done.
78 return false;
79 }
80
81 const IComponentManager& baseComponentSet = *managers_[0];
82
83 result_.clear();
84 result_.reserve(baseComponentSet.GetComponentCount());
85
86 auto& em = baseComponentSet.GetEcs().GetEntityManager();
87 for (IComponentManager::ComponentId id = 0; id < baseComponentSet.GetComponentCount(); ++id) {
88 if (const Entity entity = baseComponentSet.GetEntity(id); em.IsAlive(entity)) {
89 const size_t managerCount = managers_.size();
90
91 ResultRow row { entity, managerCount };
92 auto componentIt = row.components.begin();
93 *componentIt++ = id;
94
95 bool valid = true;
96
97 // NOTE: starting from index 1 that is the first manager after the base component set.
98 for (size_t i = 1; valid && (i < managerCount); ++i, ++componentIt) {
99 const auto& manager = *managers_[i];
100 const auto componentId = manager.GetComponentId(entity);
101 *componentIt = componentId;
102
103 switch (operationMethods_[i]) {
104 case Operation::REQUIRE: {
105 // for required components ID must be valid
106 valid = (componentId != IComponentManager::INVALID_COMPONENT_ID);
107 break;
108 }
109
110 case Operation::OPTIONAL: {
111 // for optional ID doesn't matter
112 break;
113 }
114
115 default: {
116 valid = false;
117 }
118 }
119 }
120
121 if (valid) {
122 if (enableLookup_) {
123 mapping_[entity] = result_.size();
124 }
125
126 result_.emplace_back(move(row));
127 }
128 }
129 }
130
131 valid_ = true;
132 return true;
133 }
134
Execute(const IComponentManager & baseComponentSet,const array_view<const Operation> operations,bool enableEntityLookup)135 void ComponentQuery::Execute(
136 const IComponentManager& baseComponentSet, const array_view<const Operation> operations, bool enableEntityLookup)
137 {
138 SetupQuery(baseComponentSet, operations, enableEntityLookup);
139 Execute();
140 }
141
SetEcsListenersEnabled(bool enableListeners)142 void ComponentQuery::SetEcsListenersEnabled(bool enableListeners)
143 {
144 enableListeners_ = enableListeners;
145 if (enableListeners_) {
146 RegisterEcsListeners();
147 } else {
148 UnregisterEcsListeners();
149 }
150 }
151
IsValid() const152 bool ComponentQuery::IsValid() const
153 {
154 return valid_;
155 }
156
GetResults() const157 array_view<const ComponentQuery::ResultRow> ComponentQuery::GetResults() const
158 {
159 return { result_.data(), result_.size() };
160 }
161
FindResultRow(Entity entity) const162 const ComponentQuery::ResultRow* ComponentQuery::FindResultRow(Entity entity) const
163 {
164 const auto it = mapping_.find(entity);
165 if (it != mapping_.end() && it->second < result_.size()) {
166 return &(result_[it->second]);
167 }
168
169 return nullptr;
170 }
171
RegisterEcsListeners()172 void ComponentQuery::RegisterEcsListeners()
173 {
174 if (!registered_ && !managers_.empty()) {
175 // Listen to changes in managers so the result can be automatically invalidated.
176 ecs_ = &managers_[0]->GetEcs();
177 for (auto& manager : managers_) {
178 ecs_->AddListener(*manager, *this);
179 }
180 ecs_->AddListener(static_cast<IEcs::EntityListener&>(*this));
181 registered_ = true;
182 }
183 }
184
UnregisterEcsListeners()185 void ComponentQuery::UnregisterEcsListeners()
186 {
187 if (registered_ && !managers_.empty() && ecs_) {
188 for (auto& manager : managers_) {
189 ecs_->RemoveListener(*manager, *this);
190 }
191 ecs_->RemoveListener(static_cast<IEcs::EntityListener&>(*this));
192 registered_ = false;
193 }
194 }
195
OnEntityEvent(const IEcs::EntityListener::EventType type,const array_view<const Entity> entities)196 void ComponentQuery::OnEntityEvent(const IEcs::EntityListener::EventType type, const array_view<const Entity> entities)
197 {
198 if (!valid_) {
199 // Listener is only used to invalidate the quety. If the query is already invalid -> no need to check anything.
200 return;
201 }
202 if (type == IEcs::EntityListener::EventType::ACTIVATED || type == IEcs::EntityListener::EventType::DEACTIVATED) {
203 const auto managerCount = managers_.size();
204 for (const auto& entity : entities) {
205 // We are only interested in entities that have all the required managers.
206 bool isRelevantEntity = true;
207 for (size_t i = 0; i < managerCount; ++i) {
208 if (operationMethods_[i] == Operation::OPTIONAL) {
209 continue;
210 }
211 if (!managers_[i]->HasComponent(entity)) {
212 // This entity is missing a required manager -> irrelevant.
213 isRelevantEntity = false;
214 break;
215 }
216 }
217
218 if (isRelevantEntity) {
219 // Marking this query as invalid. No need to iterate entities further.
220 valid_ = false;
221 return;
222 }
223 }
224 }
225 }
226
OnComponentEvent(const IEcs::ComponentListener::EventType type,const IComponentManager & componentManager,const array_view<const Entity> entities)227 void ComponentQuery::OnComponentEvent(const IEcs::ComponentListener::EventType type,
228 const IComponentManager& componentManager, const array_view<const Entity> entities)
229 {
230 // We only get events from relevant managers. If they have new or deleted components, the query is no longer valid.
231 if (type == IEcs::ComponentListener::EventType::CREATED || type == IEcs::ComponentListener::EventType::DESTROYED) {
232 valid_ = false;
233 }
234 }
235 CORE_END_NAMESPACE()
236