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