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