• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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