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 #ifndef CORE_UTIL_ECS_COMPONENT_QUERY_H 17 #define CORE_UTIL_ECS_COMPONENT_QUERY_H 18 19 #include <cstddef> 20 #include <cstdint> 21 22 #include <base/containers/unordered_map.h> 23 #include <base/containers/vector.h> 24 #include <base/namespace.h> 25 #include <core/ecs/entity.h> 26 #include <core/ecs/intf_component_manager.h> 27 #include <core/ecs/intf_ecs.h> 28 #include <core/namespace.h> 29 30 BASE_BEGIN_NAMESPACE() 31 template<class T> 32 class array_view; 33 BASE_END_NAMESPACE() 34 35 CORE_BEGIN_NAMESPACE() 36 /** Executes queries to component managers and outputs a result set that can be used to speed-up component data access. 37 */ 38 class ComponentQuery : private IEcs::EntityListener, private IEcs::ComponentListener { 39 public: 40 ComponentQuery() = default; 41 ~ComponentQuery() override; 42 43 /** Operations for the component query that are being applied in to of the base component set. */ 44 struct Operation { 45 /** Method of operation. */ 46 enum Method : uint8_t { 47 /** Looks up a component of given type and filters out the entity from the base set if it doesn't contain 48 such component. */ 49 REQUIRE, 50 /** Looks up a component of given type, but never filters out the entity from the base set. */ 51 OPTIONAL 52 }; 53 const IComponentManager& target; 54 Method method { REQUIRE }; 55 }; 56 57 /** Sets up a component query to component managers. 58 * @param baseComponentSet Components that are used as a base set for query, this should be the component manager 59 * that has least amount of components. 60 * @param operations Operations that are performed to base set when other component managers are merged to result. 61 * @param enableEntityLookup If true, allows to look up result row by using entity as a key (FindResultRow), this 62 * slightly slows down the look-up process. 63 */ 64 void SetupQuery(const IComponentManager& baseComponentSet, BASE_NS::array_view<const Operation> operations, 65 bool enableEntityLookup = false); 66 67 /** Executes the query. Assumes that the query has been set up earlier. 68 * @return True if there are possible changes in the query result since previous execute. 69 */ 70 bool Execute(); 71 72 [[deprecated]] void Execute(const IComponentManager& baseComponentSet, 73 BASE_NS::array_view<const Operation> operations, bool enableEntityLookup = false); 74 75 /** Enable or disable listening to ECS events. Enabling listeners will automatically invalidate this query when 76 * there are changes in the relevant component managers. 77 * @param enableListeners True to enable listening to ecs events to automatically invalidate the query. 78 */ 79 void SetEcsListenersEnabled(bool enableListeners); 80 81 /** Check if the result of this query is still valid. Requires a call to RegisterEcsListeners after each execution. 82 * @return True if there have been no changes in listened component managers since previous execute that would 83 * affect the query result. 84 */ 85 bool IsValid() const; 86 87 /** One row in a result set, describes the entity and its component ids. */ 88 struct ResultRow { 89 ResultRow() = default; 90 ~ResultRow() = default; 91 92 ResultRow(const ResultRow& rhs) = delete; 93 ResultRow& operator=(const ResultRow& rhs) = delete; 94 ResultRow(ResultRow&& rhs) noexcept = default; 95 ResultRow& operator=(ResultRow&& rhs) noexcept = default; 96 97 /** Checks whether component id in given index is valid. 98 * The component id can be invalid if the component was specified optional and it is not available) 99 * @param index Index of the component. 100 * @return True if the component id is valid, false if the component id is invalid (optional components that is 101 * not available). 102 */ 103 bool IsValidComponentId(size_t index) const; 104 105 /** Entity that contains the components. */ 106 Entity entity; 107 108 /** List of component ids, in the same order that the component managers were specified in the Execute(...) 109 * call. */ 110 BASE_NS::vector<IComponentManager::ComponentId> components; 111 }; 112 113 /** Returns The result of the query, in form of rows. 114 * @return Array of result rows, where each row describes an entity and its component ids. 115 */ 116 BASE_NS::array_view<const ResultRow> GetResults() const; 117 118 /** Look up result row for a given entity. To enable this functionality, the Execute() function needs to be called 119 * with enableEntityLookup parameter set as true. 120 * @param entity Entity to use in look-up 121 * @return Pointer to result row for given entity, or nullptr if there is no such row. 122 */ 123 const ResultRow* FindResultRow(Entity entity) const; 124 125 private: 126 void RegisterEcsListeners(); 127 void UnregisterEcsListeners(); 128 129 // IEcs::EntityListener 130 void OnEntityEvent(IEcs::EntityListener::EventType type, BASE_NS::array_view<const Entity> entities) override; 131 132 // IEcs::ComponentListener 133 void OnComponentEvent(IEcs::ComponentListener::EventType type, const IComponentManager& componentManager, 134 BASE_NS::array_view<const Entity> entities) override; 135 136 CORE_NS::IEcs* ecs_ { nullptr }; 137 BASE_NS::vector<ResultRow> result_; 138 BASE_NS::vector<IComponentManager*> managers_; 139 BASE_NS::vector<Operation::Method> operationMethods_; 140 BASE_NS::unordered_map<Entity, size_t> mapping_; 141 bool enableLookup_ { false }; 142 bool enableListeners_ { false }; 143 bool registered_ { false }; 144 bool valid_ { false }; 145 }; 146 CORE_END_NAMESPACE() 147 148 #endif // CORE_UTIL_ECS_COMPONENT_QUERY 149