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