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