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