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 SCENEPLUGIN_ECS_LISTENER
17 #define SCENEPLUGIN_ECS_LISTENER
18
19 #include <PropertyTools/property_data.h>
20 #include <scene_plugin/interface/intf_ecs_object.h>
21 #include <scene_plugin/namespace.h>
22
23 #include <3d/ecs/components/name_component.h>
24 #include <core/ecs/intf_ecs.h>
25 #include <core/property/intf_property_api.h>
26 #include <core/property/intf_property_handle.h>
27 #include <core/property/property_types.h>
28
29 SCENE_BEGIN_NAMESPACE()
30
31 class EcsListener;
32
33 REGISTER_INTERFACE(IEcsProxyObject, "74ad9154-5f61-4551-95db-249bae110d6d")
34 class IEcsProxyObject : public CORE_NS::IInterface {
35 META_INTERFACE(CORE_NS::IInterface, IEcsProxyObject, InterfaceId::IEcsProxyObject)
36
37 public:
38 virtual void SetCommonListener(BASE_NS::shared_ptr<SCENE_NS::EcsListener> listener) = 0;
39 virtual void DoEntityEvent(CORE_NS::IEcs::EntityListener::EventType type, const CORE_NS::Entity& entity) = 0;
40 virtual void DoComponentEvent(CORE_NS::IEcs::ComponentListener::EventType type,
41 const CORE_NS::IComponentManager& componentManager, const CORE_NS::Entity& entity) = 0;
42 };
43
44 SCENE_END_NAMESPACE()
45
46 META_TYPE(SCENE_NS::IEcsProxyObject::WeakPtr);
47 META_TYPE(SCENE_NS::IEcsProxyObject::Ptr);
48
SCENE_BEGIN_NAMESPACE()49 SCENE_BEGIN_NAMESPACE()
50
51 class EcsListener : public CORE_NS::IEcs::EntityListener, CORE_NS::IEcs::ComponentListener {
52 public:
53 bool IsCallbackActive()
54 {
55 return callbackActive_;
56 }
57
58 void SetEcs(const CORE_NS::IEcs::Ptr& ecs)
59 {
60 if (auto ecsRaw = ecs.get(); ecs_ != ecsRaw) {
61 Reset();
62 ecs_ = ecsRaw;
63 ecs_->AddListener((CORE_NS::IEcs::EntityListener&)*this);
64 }
65 }
66
67 void Reset(CORE_NS::IComponentManager* manager = nullptr)
68 {
69 entities_.clear();
70 if (ecs_) {
71 ecs_->RemoveListener((CORE_NS::IEcs::EntityListener&)*this);
72 for (auto& manager : componentManagers_) {
73 ecs_->RemoveListener(*manager.first, (CORE_NS::IEcs::ComponentListener&)*this);
74 }
75 componentManagers_.clear();
76 ecs_ = nullptr;
77 }
78 }
79
80 void AddEntity(const CORE_NS::Entity& entity, IEcsProxyObject::WeakPtr object, CORE_NS::IComponentManager& manager)
81 {
82 if (const auto& ite = entities_.find(entity.id); ite == entities_.cend()) {
83 entities_[entity.id].push_back(ComponentProxy::Create(manager, object));
84 } else {
85 // ToDo: should we add ref count or array to multiple instances?
86 // In principle there should not be more than one EcsObject per entity
87 // but it is possible to have several instances pointing to same entity
88 auto& vec = entities_[entity.id];
89 bool found = false;
90 for (auto& oldEntity : vec) {
91 if (object.Compare(oldEntity->entity) && &manager == oldEntity->cm) {
92 found = true;
93 oldEntity->refcount++;
94 SCENE_PLUGIN_VERBOSE_LOG("%s: multiple, count %zu", __func__, vec.size());
95 break;
96 }
97 }
98 if (!found) {
99 entities_[entity.id].push_back(ComponentProxy::Create(manager, object));
100 }
101 }
102
103 // we increase refcount for manager always regardless the duplicate entity subscriptions so that tear down will
104 // work
105 if (auto managerEntry = componentManagers_.find(&manager); managerEntry != componentManagers_.end()) {
106 managerEntry->second++;
107 } else {
108 // ToDo: could queue entity references instead reference counts so that we could
109 // traverse minimal amount of entities when dispatching the events
110 componentManagers_[&manager] = 1;
111 ecs_->AddListener(manager, (CORE_NS::IEcs::ComponentListener&)*this);
112 }
113 }
114
115 void RemoveEntity(
116 const CORE_NS::Entity& entity, IEcsProxyObject::WeakPtr object, CORE_NS::IComponentManager& manager)
117 {
118 if (const auto& ite = entities_.find(entity.id); ite != entities_.cend()) {
119 size_t ix = 0;
120 for (auto& obj : ite->second) {
121 if (obj->entity.Compare(object) && obj->cm == &manager) {
122 --obj->refcount;
123 if (!obj->refcount) {
124 ite->second.erase(ite->second.cbegin() + ix);
125 }
126 break;
127 }
128 ++ix;
129 }
130 if (ite->second.empty()) {
131 entities_.erase(entity.id);
132 }
133 }
134 if (--componentManagers_[&manager] == 0) {
135 ecs_->RemoveListener(manager, (CORE_NS::IEcs::ComponentListener&)*this);
136 componentManagers_.erase(&manager);
137 }
138 }
139
140 void OnEntityEvent(
141 CORE_NS::IEcs::EntityListener::EventType type, BASE_NS::array_view<const CORE_NS::Entity> entities) override
142 {
143 callbackActive_ = true;
144 for (const auto& e : entities) {
145 if (const auto& entity = entities_.find(e.id); entity != entities_.cend()) {
146 // hack to remove duplicates here! PLZ FIX
147 BASE_NS::vector<IEcsProxyObject::Ptr> tmp;
148 auto keepAlive = entity->second;
149 for (int ii = 0; ii < keepAlive.size(); ii++) {
150 const auto& object = keepAlive[ii];
151 if (auto proxy = object->entity.lock()) {
152 bool dupe=false;
153 for (auto t:tmp) {
154 if (t==proxy) {
155 dupe=true;
156 }
157 }
158 if (!dupe) {
159 tmp.push_back(proxy);
160 }
161 }
162 }
163 for (auto t:tmp) {
164 t->DoEntityEvent(type, e);
165 }
166 }
167 }
168 callbackActive_ = false;
169 }
170
171 void OnComponentEvent(CORE_NS::IEcs::ComponentListener::EventType type,
172 const CORE_NS::IComponentManager& componentManager,
173 BASE_NS::array_view<const CORE_NS::Entity> entities) override
174 {
175 callbackActive_ = true;
176 for (const auto& e : entities) {
177 // SCENE_PLUGIN_VERBOSE_LOG("%s: %i", __func__, e.id);
178 if (const auto& entity = entities_.find(e.id); entity != entities_.cend()) {
179 // hack to remove duplicates here! PLZ FIX
180 BASE_NS::vector<IEcsProxyObject::Ptr> tmp;
181 auto keepAlive = entity->second;
182 for (int ii = 0; ii < keepAlive.size(); ii++) {
183 const auto& object = keepAlive[ii];
184 if (auto proxy = object->entity.lock()) {
185 bool dupe=false;
186 for (auto t:tmp) {
187 if (t==proxy) {
188 dupe=true;
189 }
190 }
191 if (!dupe) {
192 tmp.push_back(proxy);
193 }
194 }
195 }
196 for (auto t:tmp) {
197 t->DoComponentEvent(type, componentManager, e);
198 }
199 }
200 }
201 callbackActive_ = false;
202 }
203 struct ComponentProxy {
204 static BASE_NS::shared_ptr<ComponentProxy> Create(
205 CORE_NS::IComponentManager& cm, IEcsProxyObject::WeakPtr entity)
206 {
207 return BASE_NS::shared_ptr<ComponentProxy> { new ComponentProxy { &cm, entity, 1 } };
208 }
209 CORE_NS::IComponentManager* cm;
210 IEcsProxyObject::WeakPtr entity;
211 size_t refcount { 0 };
212 };
213
214 BASE_NS::unordered_map<uint64_t, BASE_NS::vector<BASE_NS::shared_ptr<ComponentProxy>>> entities_;
215 CORE_NS::IEcs* ecs_ {};
216 BASE_NS::unordered_map<CORE_NS::IComponentManager*, size_t> componentManagers_;
217 bool callbackActive_ { false };
218 };
219
220 SCENE_END_NAMESPACE()
221
222 #endif // !SCENEPLUGIN_ECS_LISTENER
223