git mv Code\Sandbox\Plugins Code/Editor/Plugins

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-06-29 12:42:54 -07:00
parent e34e36cb35
commit 1696680240
215 changed files with 0 additions and 0 deletions
@@ -0,0 +1,341 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorCommon_precompiled.h"
#include <SaveUtilities/AsyncSaveRunner.h>
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
#include <AzCore/IO/SystemFile.h>
#include <ActionOutput.h>
namespace AZ
{
/* -------------------------- *
* == Save Operation Cache == *
* -------------------------- */
SaveOperationController::SaveOperationCache::SaveOperationCache(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation, SaveOperationController& owner, bool isDelete)
: m_fullSavePath(fullPath)
, m_saveOperation(saveOperation)
, m_owner(owner)
, m_isDelete(isDelete)
{
}
void SaveOperationController::SaveOperationCache::Run(const AZStd::shared_ptr<ActionOutput>& actionOutput)
{
if (m_isDelete)
{
RunDelete(actionOutput);
return;
}
// Create the callback to pass to the SourceControlAPI
AzToolsFramework::SourceControlResponseCallback callback =
[this, actionOutput](bool success, const AzToolsFramework::SourceControlFileInfo& info)
{
if (success || !info.IsReadOnly())
{
if (m_saveOperation && !m_saveOperation(m_fullSavePath, actionOutput))
{
success = false;
if (actionOutput)
{
actionOutput->AddError("Failed to save entries/dependencies", m_fullSavePath);
}
}
}
if (!success && actionOutput)
{
AZStd::string message;
AZStd::string details = m_fullSavePath;
// If there's no attempt to save any data it's assumed that this function was called to add an existing file to source control.
// Rather than report this as an error, report it as a warning as no data was lost.
bool reportAsWarning = !m_saveOperation;
bool moreDetails = true;
// Be more specific with errors so as to give the user the best chance at fixing them
if (!info.HasFlag(AzToolsFramework::SCF_OpenByUser))
{
if (info.HasFlag(AzToolsFramework::SCF_OutOfDate))
{
message = "The file being worked on doesn't contain the latest changes from source control";
moreDetails = false;
}
else if (info.IsLockedByOther())
{
message = "The file is already exclusively opened by another user";
details = info.m_StatusUser + " -> " + m_fullSavePath;
moreDetails = false;
}
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_ProviderIsDown)
{
message = "Failed to put entries/dependencies into source control as the provider is not available.\n";
}
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_CertificateInvalid)
{
message = "Failed to put entries/dependencies into source control as the source control has an invalid certificate.\n";
}
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_ProviderError)
{
message = "Failed to put entries/dependencies into source control as the provider reported an error.\n";
}
else if (!info.IsManaged())
{
message = "Failed to put entries/dependencies into source control as they are outside the current workspace mapping.\n";
reportAsWarning = true;
}
else
{
message = "Make sure the disk is not full or the file is not write-protected or not currently in use.\n";
}
}
else
{
message = "File marked as 'Open By User' but still failed.\n";
}
if (moreDetails)
{
message += "Please see the source control icon in the status bar for further details";
}
if (reportAsWarning)
{
actionOutput->AddWarning(message, details);
success = true;
}
else
{
actionOutput->AddError(message, details);
}
}
m_owner.HandleOperationComplete(this, success);
};
using SCCommandBus = AzToolsFramework::SourceControlCommandBus;
SCCommandBus::Broadcast(&SCCommandBus::Events::RequestEdit, m_fullSavePath.c_str(), true, callback);
}
void SaveOperationController::SaveOperationCache::RunDelete(const AZStd::shared_ptr<ActionOutput>& actionOutput)
{
// Create the callback to pass to the SourceControlAPI
AzToolsFramework::SourceControlResponseCallback callback =
[this, actionOutput](bool success, const AzToolsFramework::SourceControlFileInfo& info)
{
if (success || !info.IsManaged())
{
success = true;
if (m_saveOperation && !m_saveOperation(m_fullSavePath, actionOutput))
{
success = false;
if (actionOutput)
{
actionOutput->AddError("Failed to delete entry", m_fullSavePath.c_str());
}
}
}
else if (actionOutput)
{
// Be more specific with errors so as to give the user the best chance at fixing them
if (!info.HasFlag(AzToolsFramework::SCF_OpenByUser))
{
if (info.HasFlag(AzToolsFramework::SourceControlFlags::SCF_OutOfDate))
{
actionOutput->AddError("Source Control Issue - You do not have latest changes from source control for file", m_fullSavePath);
}
else if (info.IsLockedByOther())
{
actionOutput->AddError("Source Control Issue - File exclusively opened by another user", info.m_StatusUser + " -> " + m_fullSavePath);
}
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_ProviderIsDown
|| info.m_status == AzToolsFramework::SourceControlStatus::SCS_CertificateInvalid
|| info.m_status == AzToolsFramework::SourceControlStatus::SCS_ProviderError)
{
actionOutput->AddError("Source Control Issue - Failed to remove file from source control, check your connection to your source control service", m_fullSavePath.c_str());
}
else
{
actionOutput->AddError("Unknown Issue with source control.", m_fullSavePath);
}
}
else
{
actionOutput->AddError("Source Control Issue - File marked as 'Open By User' but still failed.", m_fullSavePath);
}
}
m_owner.HandleOperationComplete(this, success);
};
using SCCommandBus = AzToolsFramework::SourceControlCommandBus;
SCCommandBus::Broadcast(&SCCommandBus::Events::RequestDelete, m_fullSavePath.c_str(), callback);
}
/* ------------------------------- *
* == Save Operation Controller == *
* ------------------------------- */
SaveOperationController::SaveOperationController(AsyncSaveRunner& owner)
: m_owner(owner)
, m_completedCount(0)
{
}
void SaveOperationController::AddDeleteOperation(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation)
{
m_allSaveOperations.push_back(AZStd::make_shared<SaveOperationCache>(fullPath, saveOperation, *this, true));
}
void SaveOperationController::AddSaveOperation(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation)
{
m_allSaveOperations.push_back(AZStd::make_shared<SaveOperationCache>(fullPath, saveOperation, *this));
}
void SaveOperationController::SetOnCompleteCallback(SaveCompleteCallback onThisRunnerComplete)
{
m_onSaveComplete = onThisRunnerComplete;
}
void SaveOperationController::RunAll(const AZStd::shared_ptr<ActionOutput>& actionOutput)
{
m_completedCount = 0;
// If for some reason there are no save operations in this controller, then we need
// to notify the runner and return so that this controller can be properly counted
// as being completed.
if (m_allSaveOperations.empty())
{
m_owner.HandleRunnerFinished(this, true);
return;
}
for (AZStd::shared_ptr<SaveOperationCache>& saveOperation : m_allSaveOperations)
{
saveOperation->Run(actionOutput);
}
}
void SaveOperationController::HandleOperationComplete([[maybe_unused]] SaveOperationCache* saveOperation, bool success)
{
if (!success)
{
m_currentSaveResult = false;
}
AZ_Assert(AZStd::find_if(m_allSaveOperations.begin(), m_allSaveOperations.end(),
[saveOperation](const AZStd::shared_ptr<SaveOperationCache>& target) -> bool
{
return target.get() == saveOperation;
}) != m_allSaveOperations.end(),
"Attempting to cleanup completed save operation failed. Operation not found. Target file was: '%s'",
saveOperation->m_fullSavePath.c_str());
m_completedCount++;
if (m_completedCount >= m_allSaveOperations.size())
{
if (m_onSaveComplete)
{
m_onSaveComplete(m_currentSaveResult);
}
m_owner.HandleRunnerFinished(this, m_currentSaveResult);
}
}
/* ----------------------- *
* == Async Save Runner == *
* ----------------------- */
AZStd::shared_ptr<SaveOperationController> AsyncSaveRunner::GenerateController()
{
AZStd::shared_ptr<SaveOperationController> saveEntryController = AZStd::make_shared<SaveOperationController>(*this);
m_allSaveControllers.push_back(saveEntryController);
return saveEntryController;
}
void AsyncSaveRunner::Run(const AZStd::shared_ptr<ActionOutput>& actionOutput, SaveCompleteCallback onSaveAllComplete, ControllerOrder order)
{
m_counter = 0;
m_order = order;
m_actionOutput = actionOutput;
m_onSaveAllComplete = onSaveAllComplete;
// If for some reason there are no save operations in this runner, then we need to run
// the callback now and return so that the caller is properly notified.
if (m_allSaveControllers.empty())
{
if (m_onSaveAllComplete)
{
m_onSaveAllComplete(true);
}
return;
}
if (order == ControllerOrder::Random)
{
for (auto& saveOp : m_allSaveControllers)
{
saveOp->RunAll(actionOutput);
}
}
else if (order == ControllerOrder::Sequential)
{
m_allSaveControllers[0]->RunAll(actionOutput);
}
else
{
AZ_Assert(false, "Invalid ControllerOrder: %i", order);
}
}
void AsyncSaveRunner::HandleRunnerFinished([[maybe_unused]] SaveOperationController* runner, bool success)
{
if (!success)
{
m_allWereSuccessfull = false;
}
if (m_order == ControllerOrder::Random)
{
AZ_Assert(AZStd::find_if(m_allSaveControllers.begin(), m_allSaveControllers.end(),
[runner](const AZStd::shared_ptr<SaveOperationController>& target) -> bool
{
return target.get() == runner;
}) != m_allSaveControllers.end(), "Attempting to cleanup completed save runner failed");
m_counter++;
}
else if (m_order == ControllerOrder::Sequential)
{
AZ_Assert(m_counter < m_allSaveControllers.size(),
"Counter for save controllers has become invalid (%i vs. %i).", static_cast<int>(m_counter), m_allSaveControllers.size());
AZ_Assert(m_allSaveControllers[m_counter].get() == runner, "Completed incorrect save runner for index %i.", static_cast<int>(m_counter));
m_counter++;
if (m_counter < m_allSaveControllers.size())
{
m_allSaveControllers[m_counter]->RunAll(m_actionOutput);
}
}
else
{
AZ_Assert(false, "Invalid ControllerOrder: %i", m_order);
}
if (m_counter >= m_allSaveControllers.size())
{
m_actionOutput.reset();
if (m_onSaveAllComplete)
{
m_onSaveAllComplete(m_allWereSuccessfull);
}
}
}
}
@@ -0,0 +1,168 @@
#pragma once
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/std/string/string.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/containers/vector.h>
#include <EditorCommonAPI.h>
/*
------------------
AsyncSaveRunner:
------------------
== Overview ==
This class is meant to be a container for 1-n save operations that need to work with async source control commands. The asynchronous aspect of the SourceControlBus becomes
much more difficult when you have many operations because there is a management problem in knowing how soon until all commands have completed. This class will provide you
with an easy to use interface for specifying the save operations, and providing a single callback to be called once all those operations have been completed.
== Note ==
This class accepts lambdas and operates asynchronously. While the callbacks will be called on the main thread, **YOU MUST GUARANTEE LIFETIME YOURSELF**
== Usage ==
To use this class, you need to guarantee the lifetime of the save runner. The best way to do that is to store it as a member variable in the class that runs the save. Storing
it in a pointer type (or smart-pointer type) will help you control it's lifetime and memory.
Once you have a guaranteed lifetime AsyncSaveRunner, you'll build SaveOperationController instances that will manage all of your individual save operations, and will
run the source control pieces for you. This allows you to focus on specifying the pieces you care about.
This is easiest to see why you would want that, would be a 'Save All' scenario.
Lets imagine a save function that saves an item, which consists of saving a "header" file and an "entry" file:
void SaveItem(int index)
{
auto item = m_items[index];
auto controller = m_saveRunner->GenerateController();
controller->AddSaveOperation(m_headerSaver.getPath(), [item](const AZStd::string& fullPath, const AZStd::shared_ptr<ActionOutput>& actionOutput)->bool
{
return item->headerSaver.save();
}
);
controller->AddSaveOperation(m_entry.getPath(), [item](const AZStd::string& fullPath, const AZStd::shared_ptr<ActionOutput>& actionOutput)->bool
{
return item->entry.save();
}
);
}
You can see that the AsyncSaveRunner was used to make a save operation controller, called 'controller' and that controller was filled out with save operations.
If it was desired, you could even add a callback per controller to know when each controller is finished (in case you have to run a custom notification or something
on the item).
You could imagine SaveItem being called 1-n times, adding more and more SaveOperationController instances to the AsyncSaveRunner. Once the runner is all filled out
you call run on it and pass it a callback. This callback will only be called once. This leaves our example to look like this:
void SaveAll(AZStd::shared_ptr<AZ::ActionOutput> output, AZ::SaveCompleteCallback onComplete)
{
m_saveRunner = AZStd::make_shared<AZ::AsyncSaveRunner>();
for(int index = 0; index < m_numItems; ++index)
{
Saveitem(index);
}
m_saveRunner->Run(output,
[this](bool success)
{
m_saveRunner = nullptr;
if(onComplete)
{
onComplete(success);
}
}
);
}
*/
namespace AZ
{
class ActionOutput;
using SaveCompleteCallback = AZStd::function<void(bool success)>;
using SynchronousSaveOperation = AZStd::function<bool(const AZStd::string& fullPath, const AZStd::shared_ptr<ActionOutput>& actionOutput)>;
class AsyncSaveRunner;
// Stores a cache of synchronous save operations, and runs them on completion of asynchronous source control operations.
class EDITOR_COMMON_API SaveOperationController
{
public:
explicit SaveOperationController(AsyncSaveRunner& owner);
void AddSaveOperation(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation);
void AddDeleteOperation(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation);
void SetOnCompleteCallback(SaveCompleteCallback onThisRunnerComplete);
void RunAll(const AZStd::shared_ptr<ActionOutput>& actionOutput);
// Caches all synchronous save operations and associated data. Controlled by a SaveOperationController.
class SaveOperationCache
{
public:
SaveOperationCache(const AZStd::string& fullPath, SynchronousSaveOperation saveOperation, SaveOperationController& owner, bool isDelete = false);
void Run(const AZStd::shared_ptr<ActionOutput>& actionOutput);
void RunDelete(const AZStd::shared_ptr<ActionOutput>& actionOutput);
friend class SaveOperationController;
private:
AZStd::string m_fullSavePath;
SynchronousSaveOperation m_saveOperation;
SaveOperationController& m_owner;
bool m_isDelete;
};
void HandleOperationComplete(SaveOperationCache* saveOperation, bool success);
friend class SaveRunner;
private:
AsyncSaveRunner& m_owner;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::vector<AZStd::shared_ptr<SaveOperationCache>> m_allSaveOperations;
SaveCompleteCallback m_onSaveComplete;
AZStd::atomic<size_t> m_completedCount;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
bool m_currentSaveResult = true;
};
// Builds, stores and executes SaveOperationController instances
class EDITOR_COMMON_API AsyncSaveRunner
{
public:
enum class ControllerOrder
{
// Random will run controllers at once and completion will happen randomly.
Random,
// Controllers are executed in order, waiting for one controller before starting
// the next one. Controllers internally will still have their executions
// complete in random order.
Sequential
};
AZStd::shared_ptr<SaveOperationController> GenerateController();
void Run(const AZStd::shared_ptr<ActionOutput>& actionOutput, SaveCompleteCallback onSaveAllComplete, ControllerOrder order);
private:
friend class SaveOperationController;
void HandleRunnerFinished(SaveOperationController* runner, bool success);
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::vector<AZStd::shared_ptr<SaveOperationController>> m_allSaveControllers;
SaveCompleteCallback m_onSaveAllComplete;
AZStd::shared_ptr<ActionOutput> m_actionOutput;
// If controller order is random this keeps track of the number of completed tasks, if the order is sequential it
// keeps track of the currently executing controller.
AZStd::atomic<size_t> m_counter;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
ControllerOrder m_order;
bool m_allWereSuccessfull = true;
};
}