From a0f4f16b9842ccf4adf58ba586419198351354f2 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Mon, 21 Jun 2021 15:47:25 -0700 Subject: [PATCH] Fix Duplicate function to correctly replace the aliases in patches that get ported over. Also correctly undo/redo link creation. (#1449) --- .../Prefab/PrefabPublicHandler.cpp | 47 +++++++++++++++---- .../Prefab/PrefabPublicHandler.h | 2 +- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 3d02e797cc..a5cd50b36e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -922,8 +922,8 @@ namespace AzToolsFramework return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided.")); } - // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you - // cannot duplicate an instance from itself. + // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance + // This is because containers, despite representing the nested instance in the parent, are owned by the child. if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate) { commonOwningInstance = commonOwningInstance->get().GetParentInstance(); @@ -967,17 +967,18 @@ namespace AzToolsFramework // Duplicate any nested entities and instances as requested AZStd::unordered_map newInstanceAliasToOldInstanceMap; + AZStd::unordered_map duplicateEntityAliasMap; DuplicateNestedEntitiesInInstance(commonOwningInstance->get(), - entities, instanceDomAfter, duplicatedEntityAndInstanceIds); - DuplicateNestedInstancesInInstance(commonOwningInstance->get(), - instances, instanceDomAfter, duplicatedEntityAndInstanceIds, - newInstanceAliasToOldInstanceMap); + entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap); PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication"); command->SetParent(undoBatch.GetUndoBatch()); command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); command->Redo(); + DuplicateNestedInstancesInInstance(commonOwningInstance->get(), + instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap); + // Create links for our duplicated instances (if any were duplicated) for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap) { @@ -995,8 +996,35 @@ namespace AzToolsFramework PrefabDom linkPatchesCopy; linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator()); - m_prefabSystemComponentInterface->CreateLink( - commonOwningInstance->get().GetTemplateId(), oldInstance->GetTemplateId(), newInstanceAlias, linkPatchesCopy); + // If the instance was duplicated as part of an ancestor's nested hierarchy, the container's parent patch + // will need to be refreshed to point to the new duplicated parent entity + auto oldInstanceContainerEntityId = oldInstance->GetContainerEntityId(); + AZ_Assert(oldInstanceContainerEntityId.IsValid(), "Instance returned invalid Container Entity Id"); + + AZ::EntityId previousParentEntityId; + AZ::TransformBus::EventResult(previousParentEntityId, oldInstanceContainerEntityId, &AZ::TransformBus::Events::GetParentId); + + if (previousParentEntityId.IsValid() && AZStd::find(duplicatedEntityAndInstanceIds.begin(), duplicatedEntityAndInstanceIds.end(), previousParentEntityId)) + { + auto oldParentAlias = commonOwningInstance->get().GetEntityAlias(previousParentEntityId); + if (oldParentAlias.has_value() && duplicateEntityAliasMap.contains(oldParentAlias->get())) + { + // Get the dom into a QString for search/replace purposes + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + linkPatchesCopy.Accept(writer); + + QString linkPatchesString(buffer.GetString()); + + ReplaceOldAliases(linkPatchesString, oldParentAlias->get(), duplicateEntityAliasMap[oldParentAlias->get()]); + + linkPatchesCopy.Parse(linkPatchesString.toUtf8().constData()); + } + } + + PrefabUndoHelpers::CreateLink( + oldInstance->GetTemplateId(), commonOwningInstance->get().GetTemplateId(), + AZStd::move(linkPatchesCopy), newInstanceAlias, undoBatch.GetUndoBatch()); } // Select the duplicated entities/instances @@ -1507,14 +1535,13 @@ namespace AzToolsFramework void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance, const AZStd::vector& entities, PrefabDom& domToAddDuplicatedEntitiesUnder, - EntityIdList& duplicatedEntityIds) + EntityIdList& duplicatedEntityIds, AZStd::unordered_map& oldAliasToNewAliasMap) { if (entities.empty()) { return; } - AZStd::unordered_map oldAliasToNewAliasMap; AZStd::unordered_map aliasToEntityDomMap; for (AZ::Entity* entity : entities) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 65e1391722..fc5906c80e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -87,7 +87,7 @@ namespace AzToolsFramework */ void DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance, const AZStd::vector& entities, PrefabDom& domToAddDuplicatedEntitiesUnder, - EntityIdList& duplicatedEntityIds); + EntityIdList& duplicatedEntityIds, AZStd::unordered_map& oldAliasToNewAliasMap); /** * Duplicate a list of instances owned by a common owning instance by directly * copying/modifying their entries in the instance DOM