• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <cinttypes>
17 
18 #include <core/ecs/intf_entity_manager.h>
19 #include <core/log.h>
20 #include <core/property/intf_property_api.h>
21 
22 #include <ecs_serializer/ecs_clone_util.h>
23 #include <ecs_serializer/intf_entity_collection.h>
24 
25 using namespace BASE_NS;
26 using namespace CORE_NS;
27 
28 ECS_SERIALIZER_BEGIN_NAMESPACE()
29 
30 class EntityCollection : public IEntityCollection, private IEntityCollection::IListener {
31 public:
32     using Ptr = BASE_NS::unique_ptr<EntityCollection, Deleter>;
33 
34     EntityCollection(CORE_NS::IEcs& ecs, BASE_NS::string_view uri, BASE_NS::string_view contextUri);
35 
36     void AddListener(IEntityCollection::IListener& listener) override;
37     void RemoveListener(IEntityCollection::IListener& listener) override;
38 
39     //
40     // From IEntityCollection
41     //
42     CORE_NS::IEcs& GetEcs() const override;
43     BASE_NS::string GetUri() const override;
44     BASE_NS::string GetContextUri() const override;
45 
46     BASE_NS::string GetSrc() const override;
47     void SetSrc(BASE_NS::string_view src) override;
48 
49     BASE_NS::string GetType() const override;
50     void SetType(BASE_NS::string_view type) override;
51 
52     size_t GetEntityCount() const override;
53     CORE_NS::EntityReference GetEntity(size_t collectionIndex) const override;
54     CORE_NS::EntityReference GetEntity(BASE_NS::string_view localContextId) const override;
55     BASE_NS::array_view<const CORE_NS::EntityReference> GetEntities() const override;
56     void AddEntity(CORE_NS::EntityReference entitity) override;
57     void AddEntities(BASE_NS::array_view<const CORE_NS::EntityReference> entities) override;
58     bool RemoveEntity(CORE_NS::EntityReference entitity) override;
59     void RemoveEntities(BASE_NS::array_view<const CORE_NS::EntityReference> entities) override;
60     void SetId(BASE_NS::string_view id, CORE_NS::EntityReference entity) override;
61     BASE_NS::string_view GetId(CORE_NS::Entity entity) const override;
62 
63     size_t GetSubCollectionCount() const override;
64     IEntityCollection* GetSubCollection(size_t index) override;
65     const IEntityCollection* GetSubCollection(size_t index) const override;
66     int32_t GetSubCollectionIndex(BASE_NS::string_view uri) const override;
67     int32_t GetSubCollectionIndexByRoot(CORE_NS::Entity entity) const override;
68     IEntityCollection& AddSubCollection(BASE_NS::string_view uri, BASE_NS::string_view contextUri) override;
69     IEntityCollection& AddSubCollectionClone(IEntityCollection& collection, BASE_NS::string_view uri) override;
70     void RemoveSubCollection(size_t index) override;
71 
72     size_t GetEntityCountRecursive(bool includeDestroyed) const override;
73     void GetEntitiesRecursive(
74         bool includeDestroyed, BASE_NS::vector<CORE_NS::EntityReference>& entitiesOut) const override;
75 
76     bool Contains(CORE_NS::Entity entity) const override;
77     bool IsExternal(CORE_NS::Entity entity) const override;
78     bool isSubCollectionRoot(CORE_NS::Entity entity) const override;
79     CORE_NS::EntityReference GetReference(CORE_NS::Entity entity) const override;
80 
81     void SetActive(bool active) override;
82     bool IsActive() const override;
83 
84     void MarkDestroyed(bool destroyed) override;
85     bool IsMarkedDestroyed() const override;
86 
87     void MarkModified(bool modified) override;
88     void MarkModified(bool modified, bool recursive) override;
89     bool IsMarkedModified() const override;
90 
91     void Clear() override;
92 
93     void CopyContents(IEntityCollection& srcCollection) override;
94 
95     BASE_NS::vector<CORE_NS::EntityReference> CopyContentsWithSerialization(IEntityCollection& srcCollection) override;
96     BASE_NS::vector<CORE_NS::EntityReference> CopyContentsWithSerialization(
97         IEntityCollection& srcCollection, BASE_NS::array_view<const CORE_NS::EntityReference> entities) override;
98 
99     bool MarkComponentSerialized(CORE_NS::Entity entity, BASE_NS::Uid component, bool serialize) override;
100     bool MarkAllPropertiesSerialized(CORE_NS::Entity entity, BASE_NS::Uid component) override;
101     bool MarkPropertySerialized(
102         CORE_NS::Entity entity, BASE_NS::Uid component, BASE_NS::string_view propertyPath, bool serialize) override;
103     bool IsPropertySerialized(
104         CORE_NS::Entity entity, BASE_NS::Uid component, BASE_NS::string_view propertyPath) override;
105     const PropertyList* GetSerializedProperties(CORE_NS::Entity entity, BASE_NS::Uid component) const override;
106 
107 protected:
108     void Destroy() override;
109     ~EntityCollection() override;
110 
111 private:
112     // Prevent copying.
113     EntityCollection(const EntityCollection&) = delete;
114     EntityCollection& operator=(const EntityCollection&) = delete;
115 
116     // From IEntityCollection::IListener
117     void ModifiedChanged(IEntityCollection& entityCollection, bool modified) override;
118 
119     void DoGetEntitiesRecursive(bool includeDestroyed, BASE_NS::vector<CORE_NS::EntityReference>& entitiesOut) const;
120 
121     void ClonePrivate(EntityCollection& dst) const;
122     void DoCloneRecursive(EntityCollection& dst) const;
123 
124     // Create a better data structure for this information.
125     // Components to be serialized in an entity.
126     using ComponentMap = BASE_NS::unordered_map<BASE_NS::Uid, PropertyList>;
127     BASE_NS::unordered_map<CORE_NS::Entity, ComponentMap> serializationInfo_;
128 
129     CORE_NS::IEcs& ecs_;
130     BASE_NS::string uri_;
131     BASE_NS::string contextUri_;
132 
133     BASE_NS::string src_ {};
134     BASE_NS::string type_ {};
135 
136     BASE_NS::unordered_map<BASE_NS::string, CORE_NS::EntityReference> namedEntities_;
137     BASE_NS::vector<CORE_NS::EntityReference> entities_;
138 
139     BASE_NS::vector<EntityCollection::Ptr> collections_;
140 
141     bool isActive_ { true };
142     bool isMarkedDestroyed_ { false };
143     bool isMarkedModified_ { false };
144 
145     BASE_NS::vector<IEntityCollection::IListener*> listeners_;
146 };
147 
EntityCollection(IEcs & ecs,string_view uri,string_view contextUri)148 EntityCollection::EntityCollection(IEcs& ecs, string_view uri, string_view contextUri)
149     : ecs_(ecs), uri_(uri), contextUri_(contextUri)
150 {}
151 
AddListener(IEntityCollection::IListener & listener)152 void EntityCollection::AddListener(IEntityCollection::IListener& listener)
153 {
154     BASE_ASSERT(&listener);
155     listeners_.emplace_back(&listener);
156 }
157 
RemoveListener(IEntityCollection::IListener & listener)158 void EntityCollection::RemoveListener(IEntityCollection::IListener& listener)
159 {
160     BASE_ASSERT(&listener);
161     for (size_t i = 0; i < listeners_.size(); ++i) {
162         if (&listener == listeners_[i]) {
163             listeners_.erase(listeners_.begin() + static_cast<int64_t>(i));
164             return;
165         }
166     }
167 
168     // trying to remove a non-existent listener.
169     BASE_ASSERT(true);
170 }
171 
GetEcs() const172 IEcs& EntityCollection::GetEcs() const
173 {
174     return ecs_;
175 }
176 
GetUri() const177 string EntityCollection::GetUri() const
178 {
179     return uri_;
180 }
181 
GetContextUri() const182 string EntityCollection::GetContextUri() const
183 {
184     return contextUri_;
185 }
186 
GetSrc() const187 string EntityCollection::GetSrc() const
188 {
189     return src_;
190 }
191 
SetSrc(string_view src)192 void EntityCollection::SetSrc(string_view src)
193 {
194     src_ = src;
195     MarkModified(true);
196 }
197 
GetType() const198 string EntityCollection::GetType() const
199 {
200     return type_;
201 }
202 
SetType(string_view type)203 void EntityCollection::SetType(string_view type)
204 {
205     type_ = type;
206     MarkModified(true);
207 }
208 
GetEntityCount() const209 size_t EntityCollection::GetEntityCount() const
210 {
211     return entities_.size();
212 }
213 
GetEntity(size_t collectionIndex) const214 EntityReference EntityCollection::GetEntity(size_t collectionIndex) const
215 {
216     BASE_ASSERT(collectionIndex < entities_.size());
217     if (collectionIndex >= entities_.size()) {
218         return EntityReference {};
219     }
220     return entities_[collectionIndex];
221 }
222 
GetEntity(string_view localContextId) const223 EntityReference EntityCollection::GetEntity(string_view localContextId) const
224 {
225     const auto it = namedEntities_.find(localContextId);
226     if (it != namedEntities_.end()) {
227         return it->second;
228     }
229     return EntityReference {};
230 }
231 
GetEntities() const232 array_view<const EntityReference> EntityCollection::GetEntities() const
233 {
234     return entities_;
235 }
236 
AddEntity(EntityReference entity)237 void EntityCollection::AddEntity(EntityReference entity)
238 {
239     AddEntities({ &entity, 1 });
240 }
241 
AddEntities(array_view<const EntityReference> entities)242 void EntityCollection::AddEntities(array_view<const EntityReference> entities)
243 {
244     bool modified = false;
245     entities_.reserve(entities_.size() + entities.size());
246     for (auto entity : entities) {
247         if (entity != Entity {}) {
248             // make sure that the same entity is not added twice.
249             entities_.emplace_back(entity);
250             modified = true;
251         } else {
252             CORE_LOG_W("Trying to add empty entity to a collection '%s'", uri_.c_str());
253         }
254     }
255     if (modified) {
256         MarkModified(true);
257     }
258 }
259 
RemoveEntity(EntityReference entity)260 bool EntityCollection::RemoveEntity(EntityReference entity)
261 {
262     for (size_t i = 0; i < entities_.size(); ++i) {
263         if (entities_[i] == entity) {
264             entities_.erase(entities_.begin() + static_cast<int64_t>(i));
265 
266             // Also remove any related id mappings.
267             for (auto it = namedEntities_.begin(); it != namedEntities_.end(); ++it) {
268                 if (it->second == entity) {
269                     namedEntities_.erase(it);
270                     break;
271                 }
272             }
273 
274             // If this collection is overriding another "template" collection, when removing entities, we
275             // need to remember that and the information about deletion needs to be serialized. Maybe check if
276             // (src_.empty()). However this also needs to work with undo
277 
278             MarkModified(true);
279             return true;
280         }
281     }
282 
283     // Not found. Check the sub-collections.
284     for (auto& collection : collections_) {
285         BASE_ASSERT(collection);
286         if (collection->RemoveEntity(entity)) {
287             MarkModified(true);
288             return true;
289         }
290     }
291 
292     return false;
293 }
294 
RemoveEntities(array_view<const EntityReference> entities)295 void EntityCollection::RemoveEntities(array_view<const EntityReference> entities)
296 {
297     for (auto entity : entities) {
298         RemoveEntity(entity);
299     }
300 }
301 
SetId(string_view id,EntityReference entity)302 void EntityCollection::SetId(string_view id, EntityReference entity)
303 {
304     namedEntities_[id] = entity;
305 }
GetId(Entity entity) const306 string_view EntityCollection::GetId(Entity entity) const
307 {
308     for (auto& it : namedEntities_) {
309         if (it.second == entity) {
310             return it.first;
311         }
312     }
313     return {};
314 }
315 
GetSubCollectionCount() const316 size_t EntityCollection::GetSubCollectionCount() const
317 {
318     return collections_.size();
319 }
320 
GetSubCollection(size_t index)321 IEntityCollection* EntityCollection::GetSubCollection(size_t index)
322 {
323     if (index >= collections_.size()) {
324         return nullptr;
325     }
326     return collections_.at(index).get();
327 }
328 
GetSubCollection(size_t index) const329 const IEntityCollection* EntityCollection::GetSubCollection(size_t index) const
330 {
331     if (index >= collections_.size()) {
332         return nullptr;
333     }
334     return collections_.at(index).get();
335 }
336 
GetSubCollectionIndex(string_view uri) const337 int32_t EntityCollection::GetSubCollectionIndex(string_view uri) const
338 {
339     for (size_t i = 0; i < collections_.size(); ++i) {
340         BASE_ASSERT(collections_[i]);
341         if (collections_[i]->GetUri() == uri) {
342             return static_cast<int32_t>(i);
343         }
344     }
345     return -1;
346 }
347 
GetSubCollectionIndexByRoot(Entity entity) const348 int32_t EntityCollection::GetSubCollectionIndexByRoot(Entity entity) const
349 {
350     if (entity != Entity {}) {
351         for (size_t i = 0; i < collections_.size(); ++i) {
352             BASE_ASSERT(collections_[i]);
353             if (collections_[i]->GetEntity("/") == entity) {
354                 return static_cast<int32_t>(i);
355             }
356         }
357     }
358     return -1;
359 }
360 
AddSubCollection(string_view uri,string_view contextUri)361 IEntityCollection& EntityCollection::AddSubCollection(string_view uri, string_view contextUri)
362 {
363     collections_.emplace_back(EntityCollection::Ptr { new EntityCollection(ecs_, uri, contextUri) });
364 
365     // listen to changes in subcollection
366     collections_.back()->AddListener(*this);
367 
368     MarkModified(true);
369     return *collections_.back();
370 }
371 
AddSubCollectionClone(IEntityCollection & collection,string_view uri)372 IEntityCollection& EntityCollection::AddSubCollectionClone(IEntityCollection& collection, string_view uri)
373 {
374     // use just the public api
375     collections_.emplace_back(EntityCollection::Ptr { new EntityCollection(ecs_, uri, collection.GetContextUri()) });
376     auto& ec = *collections_.back();
377     static_cast<EntityCollection&>(collection).ClonePrivate(ec);
378 
379     // listen to changes in subcollection
380     ec.AddListener(*this);
381 
382     MarkModified(true);
383     return ec;
384 }
385 
RemoveSubCollection(size_t index)386 void EntityCollection::RemoveSubCollection(size_t index)
387 {
388     BASE_ASSERT(index < collections_.size());
389     if (index < collections_.size()) {
390         // stop listening to changes in subcollection
391         auto& ec = collections_.at(index);
392         ec->RemoveListener(*this);
393 
394         collections_.erase(collections_.begin() + static_cast<int64_t>(index));
395         MarkModified(true);
396     }
397 }
398 
GetEntityCountRecursive(bool includeDestroyed) const399 size_t EntityCollection::GetEntityCountRecursive(bool includeDestroyed) const
400 {
401     if (!includeDestroyed && IsMarkedDestroyed()) {
402         return 0;
403     }
404 
405     auto size = entities_.size();
406     for (const auto& collection : collections_) {
407         BASE_ASSERT(collection);
408         size += collection->GetEntityCountRecursive(includeDestroyed);
409     }
410     return size;
411 }
412 
GetEntitiesRecursive(bool includeDestroyed,vector<EntityReference> & entitiesOut) const413 void EntityCollection::GetEntitiesRecursive(bool includeDestroyed, vector<EntityReference>& entitiesOut) const
414 {
415     // NOTE: Cloning depends on ordering of entitiesOut.
416     entitiesOut.reserve(entitiesOut.size() + GetEntityCountRecursive(includeDestroyed));
417     DoGetEntitiesRecursive(includeDestroyed, entitiesOut);
418 }
419 
DoGetEntitiesRecursive(bool includeDestroyed,vector<EntityReference> & entitiesOut) const420 void EntityCollection::DoGetEntitiesRecursive(bool includeDestroyed, vector<EntityReference>& entitiesOut) const
421 {
422     if (!includeDestroyed && IsMarkedDestroyed()) {
423         return;
424     }
425 
426     entitiesOut.insert(entitiesOut.end(), entities_.begin(), entities_.end());
427     for (const auto& collection : collections_) {
428         BASE_ASSERT(collection);
429         collection->DoGetEntitiesRecursive(includeDestroyed, entitiesOut);
430     }
431 }
432 
Contains(Entity entity) const433 bool EntityCollection::Contains(Entity entity) const
434 {
435     for (auto it : entities_) {
436         if (it == entity) {
437             return true;
438         }
439     }
440     for (const auto& collection : collections_) {
441         if (!collection->IsMarkedDestroyed()) {
442             if (collection->Contains(entity)) {
443                 return true;
444             }
445         }
446     }
447     return false;
448 }
449 
IsExternal(Entity entity) const450 bool EntityCollection::IsExternal(Entity entity) const
451 {
452     for (auto it : entities_) {
453         if (it == entity) {
454             return false;
455         }
456     }
457     return true;
458 }
459 
isSubCollectionRoot(Entity entity) const460 bool EntityCollection::isSubCollectionRoot(Entity entity) const
461 {
462     if (entity == Entity {}) {
463         return false;
464     }
465 
466     for (const auto& collection : collections_) {
467         if (collection->GetEntity("/") == entity) {
468             return true;
469         }
470     }
471 
472     return false;
473 }
474 
GetReference(CORE_NS::Entity entity) const475 CORE_NS::EntityReference EntityCollection::GetReference(CORE_NS::Entity entity) const
476 {
477     if (Contains(entity)) {
478         auto ref = GetEcs().GetEntityManager().GetReferenceCounted(entity);
479 
480         // Check that this entity was reference counted already (it should be part of the collection).
481         CORE_ASSERT(ref.GetRefCount() > 1);
482 
483         return ref;
484     }
485     return {};
486 }
487 
SetActive(bool active)488 void EntityCollection::SetActive(bool active)
489 {
490     isActive_ = active;
491     const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
492 
493     auto& em = ecs_.GetEntityManager();
494     for (auto& entity : entities_) {
495         em.SetActive(entity, effectivelyActive);
496     }
497 
498     for (auto& collection : collections_) {
499         BASE_ASSERT(collection);
500         collection->SetActive(active);
501     }
502 }
503 
IsActive() const504 bool EntityCollection::IsActive() const
505 {
506     return isActive_;
507 }
508 
MarkDestroyed(bool destroyed)509 void EntityCollection::MarkDestroyed(bool destroyed)
510 {
511     MarkModified(true);
512     isMarkedDestroyed_ = destroyed;
513     const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
514 
515     // Change the active state of entities without changing the active state of the collection itself.
516     auto& em = ecs_.GetEntityManager();
517     for (auto& entity : entities_) {
518         em.SetActive(entity, effectivelyActive);
519     }
520 
521     for (auto& collection : collections_) {
522         BASE_ASSERT(collection);
523         collection->MarkDestroyed(destroyed);
524     }
525 }
526 
IsMarkedDestroyed() const527 bool EntityCollection::IsMarkedDestroyed() const
528 {
529     return isMarkedDestroyed_;
530 }
531 
MarkModified(bool modified)532 void EntityCollection::MarkModified(bool modified)
533 {
534     if (isMarkedModified_ != modified) {
535         if (uri_ == "project://assets/prefabs/boxes.prefab") {
536             CORE_LOG_D("Modified uri=%s, src=%s, active=%i", uri_.c_str(), src_.c_str(), isActive_);
537         }
538 
539         isMarkedModified_ = modified;
540         for (auto* l : listeners_) {
541             l->ModifiedChanged(*this, modified);
542         }
543     }
544 }
545 
MarkModified(bool modified,bool recursive)546 void EntityCollection::MarkModified(bool modified, bool recursive)
547 {
548     if (recursive && !collections_.empty()) {
549         for (auto& c : collections_) {
550             c->MarkModified(modified, true);
551         }
552     }
553     MarkModified(modified);
554 }
555 
IsMarkedModified() const556 bool EntityCollection::IsMarkedModified() const
557 {
558     return isMarkedModified_;
559 }
560 
Clear()561 void EntityCollection::Clear()
562 {
563     serializationInfo_.clear();
564     namedEntities_.clear();
565     entities_.clear();
566     collections_.clear();
567 
568     MarkModified(true);
569 }
570 
CopyContents(IEntityCollection & srcCollection)571 void EntityCollection::CopyContents(IEntityCollection& srcCollection)
572 {
573     // use just the public api
574     static_cast<EntityCollection&>(srcCollection).ClonePrivate(*this);
575     MarkModified(true);
576 }
577 
578 namespace {
CopyFrom(IEntityCollection & srcCollection,array_view<const EntityReference> srcEntities,IEntityCollection & dstCollection,unordered_map<Entity,Entity> & oldToNew,vector<EntityReference> & clonedOut,bool copyCollections)579 void CopyFrom(IEntityCollection& srcCollection, array_view<const EntityReference> srcEntities,
580     IEntityCollection& dstCollection, unordered_map<Entity, Entity>& oldToNew, vector<EntityReference>& clonedOut,
581     bool copyCollections)
582 {
583     BASE_NS::unordered_map<IEntityCollection*, bool> copiedCollections;
584 
585     for (auto& srcEntity : srcEntities) {
586         auto index = srcCollection.GetSubCollectionIndexByRoot(srcEntity);
587         if (index >= 0) {
588             auto* col = srcCollection.GetSubCollection(static_cast<size_t>(index));
589             auto& copy = dstCollection.AddSubCollection(col->GetUri(), col->GetContextUri());
590             copy.SetSrc(col->GetSrc());
591             vector<EntityReference> allEntities;
592             col->GetEntitiesRecursive(false, allEntities);
593             CopyFrom(*col, allEntities, copy, oldToNew, clonedOut, true);
594             copiedCollections[col] = true;
595         } else if (!srcCollection.IsExternal(srcEntity)) {
596             auto dstEntity = CloneEntityReference(srcCollection.GetEcs(), srcEntity, dstCollection.GetEcs());
597             clonedOut.emplace_back(dstEntity);
598             oldToNew[srcEntity] = dstEntity;
599             dstCollection.AddEntity(dstEntity);
600             auto id = srcCollection.GetId(srcEntity);
601             if (!id.empty()) {
602                 dstCollection.SetId(id, dstEntity);
603             }
604         }
605     }
606 
607     if (copyCollections) {
608         auto subCollectionCount = srcCollection.GetSubCollectionCount();
609         for (size_t subIndex = 0; subIndex < subCollectionCount; subIndex++) {
610             auto* subCollection = srcCollection.GetSubCollection(subIndex);
611             if (!copiedCollections.contains(subCollection)) {
612                 auto& copy = dstCollection.AddSubCollection(subCollection->GetUri(), subCollection->GetContextUri());
613                 copy.SetSrc(subCollection->GetSrc());
614                 vector<EntityReference> allEntities;
615                 subCollection->GetEntitiesRecursive(false, allEntities);
616                 CopyFrom(*subCollection, allEntities, copy, oldToNew, clonedOut, false);
617             }
618         }
619     }
620 }
621 
GetOldEntity(unordered_map<Entity,Entity> & oldToNew,CORE_NS::Entity newEntity)622 CORE_NS::Entity GetOldEntity(unordered_map<Entity, Entity>& oldToNew, CORE_NS::Entity newEntity)
623 {
624     for (auto& e : oldToNew) {
625         if (e.second == newEntity) {
626             return e.first;
627         }
628     }
629     return {};
630 }
631 
CopySerializationInfo(IEntityCollection & srcCollection,IEntityCollection & dstCollection,unordered_map<Entity,Entity> & oldToNew,vector<EntityReference> & entitiesOut)632 void CopySerializationInfo(IEntityCollection& srcCollection, IEntityCollection& dstCollection,
633     unordered_map<Entity, Entity>& oldToNew, vector<EntityReference>& entitiesOut)
634 {
635     // Copy all serialization info for copied entites from their original entities
636     // Note that the root collections contain all the serialization info.
637     for (auto& cm : srcCollection.GetEcs().GetComponentManagers()) {
638         for (auto& entityOut : entitiesOut) {
639             auto srcEntity = GetOldEntity(oldToNew, entityOut);
640             if (CORE_NS::EntityUtil::IsValid(srcEntity)) {
641                 auto* properties = srcCollection.GetSerializedProperties(srcEntity, cm->GetUid());
642                 if (properties) {
643                     dstCollection.MarkComponentSerialized(entityOut, cm->GetUid(), true);
644                     for (auto property : *properties) {
645                         dstCollection.MarkPropertySerialized(entityOut, cm->GetUid(), property, true);
646                     }
647                 }
648             }
649         }
650     }
651 }
652 } // namespace
653 
CopyContentsWithSerialization(IEntityCollection & srcCollection,array_view<const EntityReference> entities)654 vector<EntityReference> EntityCollection::CopyContentsWithSerialization(
655     IEntityCollection& srcCollection, array_view<const EntityReference> entities)
656 {
657     unordered_map<Entity, Entity> oldToNew;
658 
659     vector<EntityReference> entitiesOut;
660     entitiesOut.reserve(entities.size());
661     CopyFrom(srcCollection, entities, *this, oldToNew, entitiesOut, false);
662     CopySerializationInfo(srcCollection, *this, oldToNew, entitiesOut);
663 
664     for (auto& entity : entitiesOut) {
665         RewriteEntityReferences(GetEcs(), entity, oldToNew);
666     }
667     return entitiesOut;
668 }
669 
CopyContentsWithSerialization(IEntityCollection & srcCollection)670 vector<EntityReference> EntityCollection::CopyContentsWithSerialization(IEntityCollection& srcCollection)
671 {
672     vector<EntityReference> allEntities;
673     srcCollection.GetEntitiesRecursive(false, allEntities);
674     return CopyContentsWithSerialization(srcCollection, allEntities);
675 }
676 
ClonePrivate(EntityCollection & dst) const677 void EntityCollection::ClonePrivate(EntityCollection& dst) const
678 {
679     // Clone all collections recursively.
680     DoCloneRecursive(dst);
681 
682     //
683     // Remap entity properties that are pointing to the src entities to point to cloned ones.
684     //
685     unordered_map<Entity, Entity> oldToNew;
686 
687     vector<EntityReference> sourceEntities;
688     GetEntitiesRecursive(false, sourceEntities);
689     vector<EntityReference> clonedEntities;
690     dst.GetEntitiesRecursive(false, clonedEntities);
691 
692     // NOTE: Assuming the order in GetEntitiesRecursive is consistent.
693     BASE_ASSERT(sourceEntities.size() == clonedEntities.size());
694     const auto entityCount = sourceEntities.size();
695     for (size_t i = 0; i < entityCount; ++i) {
696         oldToNew[sourceEntities[i]] = clonedEntities[i];
697     }
698     for (auto& entity : clonedEntities) {
699         RewriteEntityReferences(dst.GetEcs(), entity, oldToNew);
700     }
701 }
702 
DoCloneRecursive(EntityCollection & dst) const703 void EntityCollection::DoCloneRecursive(EntityCollection& dst) const
704 {
705     // Clone entities.
706     dst.entities_ = CloneEntityReferences(ecs_, { entities_.data(), entities_.size() }, dst.GetEcs());
707 
708     // Create id mapping but reference cloned entities instead of the original
709     BASE_ASSERT(entities_.size() == dst.entities_.size());
710     dst.namedEntities_.clear();
711     dst.namedEntities_.reserve(namedEntities_.size());
712     const auto entityCount = entities_.size();
713     for (const auto& it : namedEntities_) {
714         for (size_t i = 0; i < entityCount; ++i) {
715             if (it.second == entities_[i]) {
716                 dst.SetId(it.first, dst.entities_[i]);
717                 break;
718             }
719         }
720     }
721 
722     // Recurse.
723     dst.collections_.reserve(collections_.size());
724     for (auto& collection : collections_) {
725         BASE_ASSERT(collection);
726 
727         if (!collection->IsMarkedDestroyed()) {
728             dst.collections_.emplace_back(EntityCollection::Ptr {
729                 new EntityCollection(dst.GetEcs(), collection->GetUri(), collection->GetContextUri()) });
730             auto& clonedChild = *dst.collections_.back();
731             collection->DoCloneRecursive(clonedChild);
732         }
733     }
734 }
735 
736 namespace {
737 
SetPropertyDefined(EntityCollection::PropertyList & pl,string_view propertyPath)738 bool SetPropertyDefined(EntityCollection::PropertyList& pl, string_view propertyPath)
739 {
740     for (const auto& prop : pl) {
741         if (prop == propertyPath) {
742             // Already marked as a property defined by this node.
743             return false;
744         }
745 
746         // check if the property we are trying to set is a sub-property of an already defined property
747         auto len1 = prop.length();
748         auto len2 = propertyPath.length();
749         if ((len2 > len1) && (propertyPath[len1] == '.')) {
750             auto view1 = prop.substr(0, len1);
751             auto view2 = propertyPath.substr(0, len1);
752             if (view1 == view2) {
753                 // already defined in a higher level, so no need to define this sub-property
754                 return false;
755             }
756         }
757     }
758     pl.push_back(string(propertyPath));
759     return true;
760 }
SetPropertyUndefined(EntityCollection::PropertyList & pl,string_view propertyPath)761 bool SetPropertyUndefined(EntityCollection::PropertyList& pl, string_view propertyPath)
762 {
763     for (size_t i = 0; i < pl.size(); ++i) {
764         if (pl[i] == propertyPath) {
765             pl.erase(pl.begin() + static_cast<int64_t>(i));
766             return true;
767         }
768     }
769     return false;
770 }
771 
772 } // namespace
773 
MarkComponentSerialized(Entity entity,Uid component,bool serialize)774 bool EntityCollection::MarkComponentSerialized(Entity entity, Uid component, bool serialize)
775 {
776     bool changed = false;
777     const auto entityInfo = serializationInfo_.find(entity);
778     const bool entityFound = (entityInfo != serializationInfo_.end());
779     if (serialize) {
780         if (!entityFound) {
781             serializationInfo_[entity][component] = {};
782             changed = true;
783         } else {
784             const auto componentInfo = entityInfo->second.find(component);
785             const bool componentFound = (componentInfo != entityInfo->second.end());
786             if (!componentFound) {
787                 entityInfo->second[component] = {};
788                 changed = true;
789             }
790         }
791     } else {
792         if (entityFound) {
793             entityInfo->second.erase(component);
794             changed = true;
795         }
796     }
797 
798     if (changed) {
799         MarkModified(true);
800     }
801 
802     return changed;
803 }
804 
MarkAllPropertiesSerialized(Entity entity,Uid component)805 bool EntityCollection::MarkAllPropertiesSerialized(Entity entity, Uid component)
806 {
807     bool changed = false;
808 
809     auto cm = GetEcs().GetComponentManager(component);
810     if (!cm) {
811         CORE_LOG_W("Set modified: Unrecognized component");
812         return false;
813     }
814 
815     auto info = serializationInfo_.find(entity);
816     if (info == serializationInfo_.end()) {
817         serializationInfo_[entity] = {};
818         info = serializationInfo_.find(entity);
819         changed = true;
820     }
821 
822     const auto& propertyApi = cm->GetPropertyApi();
823     const auto propertyCount = propertyApi.PropertyCount();
824     for (size_t i = 0; i < propertyCount; ++i) {
825         auto* property = propertyApi.MetaData(i);
826         changed = changed | SetPropertyDefined(info->second[component], property->name);
827     }
828 
829     if (changed) {
830         MarkModified(true);
831     }
832 
833     return changed;
834 }
835 
MarkPropertySerialized(Entity entity,Uid component,string_view propertyPath,bool serialize)836 bool EntityCollection::MarkPropertySerialized(Entity entity, Uid component, string_view propertyPath, bool serialize)
837 {
838     bool changed = false;
839 
840     auto* cm = GetEcs().GetComponentManager(component);
841     if (cm) {
842         auto info = serializationInfo_.find(entity);
843         if (serialize) {
844             if (info == serializationInfo_.end()) {
845                 serializationInfo_[entity][component] = {};
846                 info = serializationInfo_.find(entity);
847                 changed = true;
848             }
849             changed = changed | SetPropertyDefined(info->second[component], propertyPath);
850         } else {
851             if (info != serializationInfo_.end()) {
852                 changed = changed | SetPropertyUndefined(info->second[component], propertyPath);
853             }
854         }
855     }
856 
857     if (changed) {
858         MarkModified(true);
859     }
860 
861     return changed;
862 }
863 
GetSerializedProperties(Entity entity,Uid component) const864 const IEntityCollection::PropertyList* EntityCollection::GetSerializedProperties(Entity entity, Uid component) const
865 {
866     const auto info = serializationInfo_.find(entity);
867     if (info != serializationInfo_.end()) {
868         const auto props = info->second.find(component);
869         if (props != info->second.end()) {
870             return &props->second;
871         }
872     }
873     return nullptr;
874 }
875 
IsPropertySerialized(Entity entity,Uid component,string_view propertyPath)876 bool EntityCollection::IsPropertySerialized(Entity entity, Uid component, string_view propertyPath)
877 {
878     auto info = serializationInfo_.find(entity);
879     if (info == serializationInfo_.end()) {
880         return false;
881     } else {
882         const auto props = info->second.find(component);
883         if (props == info->second.end()) {
884             return false;
885         } else {
886             for (auto& prop : props->second) {
887                 if (prop == propertyPath) {
888                     return true;
889                 }
890             }
891         }
892     }
893     return false;
894 }
895 
Destroy()896 void EntityCollection::Destroy()
897 {
898     delete this;
899 }
900 
~EntityCollection()901 EntityCollection::~EntityCollection()
902 {
903     Clear();
904 }
905 
ModifiedChanged(IEntityCollection & entityCollection,bool modified)906 void EntityCollection::ModifiedChanged(IEntityCollection& entityCollection, bool modified)
907 {
908     CORE_UNUSED(entityCollection);
909 
910     // subcollection changed, propagate modified status
911     if (modified) {
912         MarkModified(modified);
913     }
914 }
915 
CreateEntityCollection(IEcs & ecs,string_view uri,string_view contextUri)916 IEntityCollection::Ptr CreateEntityCollection(IEcs& ecs, string_view uri, string_view contextUri)
917 {
918     return EntityCollection::Ptr { new EntityCollection(ecs, uri, contextUri) };
919 }
920 
921 ECS_SERIALIZER_END_NAMESPACE()
922