Fixed CreatePrefab to use correct absolute path (#1044)

The initial CreatePrefab flow was trying to go from absolute -> relative -> absolute path before the file had ever been saved, so the relative -> absolute path conversion generated an error and always produced a project-relative path, even if the initial path was in a gem.  For example, trying to save "c:/o3de/Gems/Camera/Assets/Entity1.prefab" would instead create "c:/o3de/AutomatedTesting/Entity1.prefab".  This change preserves the absolute path throughout the initial creation flow so that the file is saved in the correct location.
This commit is contained in:
Mike Balfour
2021-06-01 10:50:10 -05:00
committed by GitHub
parent 7d1fedc10c
commit 895bbafa9e
7 changed files with 69 additions and 8 deletions
@@ -303,6 +303,45 @@ namespace AzToolsFramework
return true; return true;
} }
bool PrefabLoader::SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath)
{
AZ_Assert(absolutePath.IsAbsolute(), "SaveTemplateToFile requires an absolute path for saving the initial prefab file.");
const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId);
if (!domAndFilepath)
{
return false;
}
// Verify that the absolute path provided to this matches the relative path saved in the template.
// Otherwise, the saved prefab won't be able to be loaded.
auto relativePath = GenerateRelativePath(absolutePath);
if (relativePath != domAndFilepath->second)
{
AZ_Error(
"Prefab", false,
"PrefabLoader::SaveTemplateToFile - "
"Failed to save template '%s' to location '%.*s'."
"Error: Relative path '%.*s' for location didn't match template name.",
domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), AZ_STRING_ARG(relativePath.Native()));
return false;
}
auto outcome = AzFramework::FileFunc::WriteJsonFile(domAndFilepath->first, absolutePath);
if (!outcome.IsSuccess())
{
AZ_Error(
"Prefab", false,
"PrefabLoader::SaveTemplateToFile - "
"Failed to save template '%s' to location '%.*s'."
"Error: %s",
domAndFilepath->second.c_str(), AZ_STRING_ARG(absolutePath.Native()), outcome.GetError().c_str());
return false;
}
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, false);
return true;
}
bool PrefabLoader::SaveTemplateToString(TemplateId templateId, AZStd::string& output) bool PrefabLoader::SaveTemplateToString(TemplateId templateId, AZStd::string& output)
{ {
const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId); const auto& domAndFilepath = StoreTemplateIntoFileFormat(templateId);
@@ -72,6 +72,16 @@ namespace AzToolsFramework
*/ */
bool SaveTemplate(TemplateId templateId) override; bool SaveTemplate(TemplateId templateId) override;
/**
* Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in the template.
* Converts Prefab Template form into .prefab form by collapsing nested Template info
* into a source path and patches.
* @param templateId Id of the template to be saved
* @param absolutePath Absolute path to save the file to
* @return bool on whether the operation succeeded or not
*/
bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) override;
/** /**
* Saves a Prefab Template into the provided output string. * Saves a Prefab Template into the provided output string.
* Converts Prefab Template form into .prefab form by collapsing nested Template info * Converts Prefab Template form into .prefab form by collapsing nested Template info
@@ -60,6 +60,16 @@ namespace AzToolsFramework
*/ */
virtual bool SaveTemplate(TemplateId templateId) = 0; virtual bool SaveTemplate(TemplateId templateId) = 0;
/**
* Saves a Prefab Template to the provided absolute source path, which needs to match the relative path in the template.
* Converts Prefab Template form into .prefab form by collapsing nested Template info
* into a source path and patches.
* @param templateId Id of the template to be saved
* @param absolutePath Absolute path to save the file to
* @return bool on whether the operation succeeded or not
*/
virtual bool SaveTemplateToFile(TemplateId templateId, AZ::IO::PathView absolutePath) = 0;
/** /**
* Saves a Prefab Template into the provided output string. * Saves a Prefab Template into the provided output string.
* Converts Prefab Template form into .prefab form by collapsing nested Template info * Converts Prefab Template form into .prefab form by collapsing nested Template info
@@ -64,7 +64,7 @@ namespace AzToolsFramework
m_prefabUndoCache.Destroy(); m_prefabUndoCache.Destroy();
} }
PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView absolutePath)
{ {
EntityList inputEntityList, topLevelEntities; EntityList inputEntityList, topLevelEntities;
AZ::EntityId commonRootEntityId; AZ::EntityId commonRootEntityId;
@@ -76,6 +76,8 @@ namespace AzToolsFramework
return findCommonRootOutcome; return findCommonRootOutcome;
} }
AZ_Assert(absolutePath.IsAbsolute(), "CreatePrefab requires an absolute path for saving the initial prefab file.");
InstanceOptionalReference instanceToCreate; InstanceOptionalReference instanceToCreate;
{ {
// Initialize Undo Batch object // Initialize Undo Batch object
@@ -144,7 +146,8 @@ namespace AzToolsFramework
// Create the Prefab // Create the Prefab
instanceToCreate = prefabEditorEntityOwnershipInterface->CreatePrefab( instanceToCreate = prefabEditorEntityOwnershipInterface->CreatePrefab(
entities, AZStd::move(instancePtrs), filePath, commonRootEntityOwningInstance); entities, AZStd::move(instancePtrs), m_prefabLoaderInterface->GenerateRelativePath(absolutePath),
commonRootEntityOwningInstance);
if (!instanceToCreate) if (!instanceToCreate)
{ {
@@ -254,7 +257,7 @@ namespace AzToolsFramework
} }
// Save Template to file // Save Template to file
m_prefabLoaderInterface->SaveTemplate(instanceToCreate->get().GetTemplateId()); m_prefabLoaderInterface->SaveTemplateToFile(instanceToCreate->get().GetTemplateId(), absolutePath);
return AZ::Success(); return AZ::Success();
} }
@@ -46,7 +46,7 @@ namespace AzToolsFramework
void UnregisterPrefabPublicHandlerInterface(); void UnregisterPrefabPublicHandlerInterface();
// PrefabPublicInterface... // PrefabPublicInterface...
PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) override; PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView absolutePath) override;
PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override; PrefabOperationResult InstantiatePrefab(AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) override;
PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override; PrefabOperationResult SavePrefab(AZ::IO::Path filePath) override;
PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override; PrefabEntityResult CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position) override;
@@ -46,10 +46,10 @@ namespace AzToolsFramework
* Create a prefab out of the entities provided, at the path provided. * Create a prefab out of the entities provided, at the path provided.
* Automatically detects descendants of entities, and discerns between entities and child instances. * Automatically detects descendants of entities, and discerns between entities and child instances.
* @param entityIds The entities that should form the new prefab (along with their descendants). * @param entityIds The entities that should form the new prefab (along with their descendants).
* @param filePath The path for the new prefab file. * @param filePath The absolute path for the new prefab file.
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error. * @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
*/ */
virtual PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView filePath) = 0; virtual PrefabOperationResult CreatePrefab(const AZStd::vector<AZ::EntityId>& entityIds, AZ::IO::PathView absolutePath) = 0;
/** /**
* Instantiate a prefab from a prefab file. * Instantiate a prefab from a prefab file.
@@ -333,8 +333,7 @@ namespace AzToolsFramework
} }
} }
auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab( auto createPrefabOutcome = s_prefabPublicInterface->CreatePrefab(selectedEntities, prefabFilePath.data());
selectedEntities, s_prefabLoaderInterface->GenerateRelativePath(prefabFilePath.data()));
if (!createPrefabOutcome.IsSuccess()) if (!createPrefabOutcome.IsSuccess())
{ {