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 
CORE_BEGIN_NAMESPACE()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