file save finished, not tested

Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com>
This commit is contained in:
chcurran
2021-09-15 15:42:54 -07:00
parent 0dfcc1ea42
commit 296fca722e
17 changed files with 588 additions and 140 deletions
@@ -686,7 +686,7 @@ namespace ScriptCanvasEditor
void EditorGraphUpgradeMachine::OnComplete(IState::ExitStatus exitStatus)
{
UpgradeNotifications::Bus::Broadcast(&UpgradeNotifications::OnGraphUpgradeComplete, m_asset, exitStatus == IState::ExitStatus::Skipped);
UpgradeNotificationsBus::Broadcast(&UpgradeNotifications::OnGraphUpgradeComplete, m_asset, exitStatus == IState::ExitStatus::Skipped);
m_asset = {};
}
@@ -236,8 +236,6 @@ namespace ScriptCanvasEditor
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
using Bus = AZ::EBus<UpgradeNotifications>;
virtual void OnUpgradeStart() {}
virtual void OnUpgradeComplete() {}
virtual void OnUpgradeCancelled() {}
@@ -245,6 +243,5 @@ namespace ScriptCanvasEditor
virtual void OnGraphUpgradeComplete(AZ::Data::Asset<AZ::Data::AssetData>&, bool skipped = false) { (void)skipped; }
};
using UpgradeNotificationsBus = AZ::EBus<UpgradeNotifications>;
}
@@ -829,12 +829,12 @@ namespace ScriptCanvasEditor
NodePaletteModel::NodePaletteModel()
: m_paletteId(AZ::Entity::MakeId())
{
UpgradeNotifications::Bus::Handler::BusConnect();
UpgradeNotificationsBus::Handler::BusConnect();
}
NodePaletteModel::~NodePaletteModel()
{
UpgradeNotifications::Bus::Handler::BusDisconnect();
UpgradeNotificationsBus::Handler::BusDisconnect();
DisconnectLambdas();
@@ -61,7 +61,7 @@ namespace ScriptCanvasEditor
class NodePaletteModel
: public GraphCanvas::CategorizerInterface
, UpgradeNotifications::Bus::Handler
, UpgradeNotificationsBus::Handler
{
public:
typedef AZStd::unordered_map< ScriptCanvas::NodeTypeIdentifier, NodePaletteModelInformation* > NodePaletteRegistry;
@@ -148,7 +148,7 @@ namespace ScriptCanvasEditor
, m_assetModel(assetModel)
, m_categorizer(nodePaletteModel)
{
UpgradeNotifications::Bus::Handler::BusConnect();
UpgradeNotificationsBus::Handler::BusConnect();
if (m_assetModel)
{
@@ -166,7 +166,7 @@ namespace ScriptCanvasEditor
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
UpgradeNotifications::Bus::Handler::BusDisconnect();
UpgradeNotificationsBus::Handler::BusDisconnect();
}
@@ -57,7 +57,7 @@ namespace ScriptCanvasEditor
: public GraphCanvas::NodePaletteTreeItem
, AzFramework::AssetCatalogEventBus::Handler
, AZ::Data::AssetBus::MultiHandler
, UpgradeNotifications::Bus::Handler
, UpgradeNotificationsBus::Handler
, AZ::SystemTickBus::Handler
{
public:
@@ -98,19 +98,19 @@ namespace ScriptCanvasEditor
class ScriptCanvasAssetBrowserModel
: public AzToolsFramework::AssetBrowser::AssetBrowserFilterModel
, private UpgradeNotifications::Bus::Handler
, private UpgradeNotificationsBus::Handler
{
public:
explicit ScriptCanvasAssetBrowserModel(QObject* parent = nullptr)
: AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent)
{
UpgradeNotifications::Bus::Handler::BusConnect();
UpgradeNotificationsBus::Handler::BusConnect();
}
~ScriptCanvasAssetBrowserModel() override
{
UpgradeNotifications::Bus::Handler::BusDisconnect();
UpgradeNotificationsBus::Handler::BusDisconnect();
}
void OnUpgradeStart() override
@@ -52,9 +52,9 @@ namespace ScriptCanvasEditor
m_view->tableWidget->setColumnWidth(3, 22);
m_view->textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
m_view->textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
connect(m_view->scanButton, &QPushButton::pressed, this, &Controller::OnScanButtonPress);
connect(m_view->closeButton, &QPushButton::pressed, this, &Controller::OnCloseButtonPress);
connect(m_view->upgradeAllButton, &QPushButton::pressed, this, &Controller::OnUpgradeButtonPress);
connect(m_view->scanButton, &QPushButton::pressed, this, &Controller::OnButtonPressScan);
connect(m_view->closeButton, &QPushButton::pressed, this, &Controller::OnButtonPressClose);
connect(m_view->upgradeAllButton, &QPushButton::pressed, this, &Controller::OnButtonPressUpgrade);
m_view->progressBar->setValue(0);
m_view->progressBar->setVisible(false);
@@ -87,12 +87,12 @@ namespace ScriptCanvasEditor
LogBus::Broadcast(&LogTraits::Clear);
}
void Controller::OnCloseButtonPress()
void Controller::OnButtonPressClose()
{
reject();
}
void Controller::OnScanButtonPress()
void Controller::OnButtonPressScan()
{
// \todo move to another file
auto isUpToDate = [this](AZ::Data::Asset<AZ::Data::AssetData> asset)
@@ -128,6 +128,148 @@ namespace ScriptCanvasEditor
ModelRequestsBus::Broadcast(&ModelRequestsTraits::Scan, config);
}
void Controller::OnButtonPressUpgrade()
{
auto simpleUpdate = [this](AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (asset.GetType() == azrtti_typeid<ScriptCanvasAsset>())
{
ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs<ScriptCanvasAsset>();
AZ_Assert(scriptCanvasAsset, "Unable to get the asset of ScriptCanvasAsset, but received type: %s"
, azrtti_typeid<ScriptCanvasAsset>().template ToString<AZStd::string>().c_str());
if (!scriptCanvasAsset)
{
return;
}
AZ::Entity* scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity();
AZ_Assert(scriptCanvasEntity, "View::UpgradeGraph The Script Canvas asset must have a valid entity");
if (!scriptCanvasEntity)
{
return;
}
AZ::Entity* queryEntity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(queryEntity, &AZ::ComponentApplicationRequests::FindEntity, scriptCanvasEntity->GetId());
if (queryEntity)
{
if (queryEntity->GetState() == AZ::Entity::State::Active)
{
queryEntity->Deactivate();
}
scriptCanvasEntity = queryEntity;
}
if (scriptCanvasEntity->GetState() == AZ::Entity::State::Constructed)
{
scriptCanvasEntity->Init();
}
if (scriptCanvasEntity->GetState() == AZ::Entity::State::Init)
{
scriptCanvasEntity->Activate();
}
AZ_Assert(scriptCanvasEntity->GetState() == AZ::Entity::State::Active, "Graph entity is not active");
auto graphComponent = scriptCanvasEntity->FindComponent<ScriptCanvasEditor::Graph>();
AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component");
if (graphComponent)
{
graphComponent->UpgradeGraph
( asset
, m_view->forceUpgrade->isChecked() ? Graph::UpgradeRequest::Forced : Graph::UpgradeRequest::IfOutOfDate
, m_view->verbose->isChecked());
}
}
};
auto onReadyOnlyFile = [this]()->bool
{
int result = QMessageBox::No;
QMessageBox mb
( QMessageBox::Warning
, QObject::tr("Failed to Save Upgraded File")
, QObject::tr("The upgraded file could not be saved because the file is read only.\n"
"Do you want to make it writeable and overwrite it?")
, QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::No
, this);
result = mb.exec();
return result == QMessageBox::YesToAll;
};
ModifyConfiguration config;
config.modification = simpleUpdate;
config.onReadOnlyFile = onReadyOnlyFile;
config.backupGraphBeforeModification = m_view->makeBackupCheckbox->isChecked();
ModelRequestsBus::Broadcast(&ModelRequestsTraits::Modify, config);
}
void Controller::OnUpgradeModificationBegin([[maybe_unused]] const ModifyConfiguration& config, const AZ::Data::AssetInfo& info)
{
QList<QTableWidgetItem*> items = m_view->tableWidget->findItems(info.m_relativePath.c_str(), Qt::MatchFlag::MatchExactly);
if (!items.isEmpty())
{
for (auto* item : items)
{
int row = item->row();
SetRowBusy(row);
}
}
}
void Controller::OnUpgradeModificationEnd
( [[maybe_unused]] const ModifyConfiguration& config
, const AZ::Data::AssetInfo& info
, ModificationResult result)
{
if (result.errorMessage.empty())
{
VE_LOG("Successfully modified %s", result.assetInfo.m_relativePath.c_str());
}
else
{
VE_LOG("Failed to modify %s: %s", result.assetInfo.m_relativePath.c_str(), result.errorMessage.data());
}
QList<QTableWidgetItem*> items = m_view->tableWidget->findItems(info.m_relativePath.c_str(), Qt::MatchFlag::MatchExactly);
if (!items.isEmpty())
{
for (auto* item : items)
{
int row = item->row();
if (result.errorMessage.empty())
{
SetRowSucceeded(row);
}
else
{
SetRowFailed(row, "");
}
}
}
m_view->progressBar->setVisible(true);
++m_handledAssetCount;
m_view->progressBar->setValue(m_handledAssetCount);
}
void Controller::OnGraphUpgradeComplete(AZ::Data::Asset<AZ::Data::AssetData>& asset, bool skipped)
{
ModificationResult result;
result.asset = asset;
AZ::Data::AssetCatalogRequestBus::BroadcastResult
( result.assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, asset.GetId());
if (skipped)
{
result.errorMessage = "Failed in editor upgrade state machine - check logs";
}
ModificationNotificationsBus::Broadcast(&ModificationNotificationsTraits::ModificationComplete, result);
}
void Controller::OnScanBegin(size_t assetCount)
{
m_handledAssetCount = 0;
@@ -264,68 +406,6 @@ namespace ScriptCanvasEditor
OnScannedGraph(info, Filtered::No);
}
void Controller::OnUpgradeButtonPress()
{
auto simpleUpdate = [this](AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (asset.GetType() == azrtti_typeid<ScriptCanvasAsset>())
{
ScriptCanvasAsset* scriptCanvasAsset = asset.GetAs<ScriptCanvasAsset>();
AZ_Assert(scriptCanvasAsset, "Unable to get the asset of ScriptCanvasAsset, but received type: %s"
, azrtti_typeid<ScriptCanvasAsset>().template ToString<AZStd::string>().c_str());
if (!scriptCanvasAsset)
{
return;
}
AZ::Entity* scriptCanvasEntity = scriptCanvasAsset->GetScriptCanvasEntity();
AZ_Assert(scriptCanvasEntity, "View::UpgradeGraph The Script Canvas asset must have a valid entity");
if (!scriptCanvasEntity)
{
return;
}
AZ::Entity* queryEntity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(queryEntity, &AZ::ComponentApplicationRequests::FindEntity, scriptCanvasEntity->GetId());
if (queryEntity)
{
if (queryEntity->GetState() == AZ::Entity::State::Active)
{
queryEntity->Deactivate();
}
scriptCanvasEntity = queryEntity;
}
if (scriptCanvasEntity->GetState() == AZ::Entity::State::Constructed)
{
scriptCanvasEntity->Init();
}
if (scriptCanvasEntity->GetState() == AZ::Entity::State::Init)
{
scriptCanvasEntity->Activate();
}
AZ_Assert(scriptCanvasEntity->GetState() == AZ::Entity::State::Active, "Graph entity is not active");
auto graphComponent = scriptCanvasEntity->FindComponent<ScriptCanvasEditor::Graph>();
AZ_Assert(graphComponent, "The Script Canvas entity must have a Graph component");
if (graphComponent)
{
graphComponent->UpgradeGraph
( asset
, m_view->forceUpgrade->isChecked() ? Graph::UpgradeRequest::Forced : Graph::UpgradeRequest::IfOutOfDate
, m_view->verbose->isChecked());
}
}
};
ModifyConfiguration config;
config.modification = simpleUpdate;
config.backupGraphBeforeModification = m_view->makeBackupCheckbox->isChecked();
ModelRequestsBus::Broadcast(&ModelRequestsTraits::Modify, config);
}
void Controller::OnUpgradeBegin
( const ModifyConfiguration& config
, [[maybe_unused]] const AZStd::vector<AZ::Data::AssetInfo>& assets)
@@ -359,8 +439,13 @@ namespace ScriptCanvasEditor
m_view->spinner->SetBusyIconSize(16);
}
void Controller::OnUpgradeComplete()
void Controller::OnUpgradeComplete(const ModificationResults& result)
{
QString spinnerText = QStringLiteral("Upgrade Complete - ");
spinnerText.append(QString::asprintf(" - Upgraded: %zu, Failed: %zu"
, result.m_successes.size()
, result.m_failures.size()));
m_view->spinner->SetText(spinnerText);
SetSpinnerIsBusy(false);
}
@@ -41,7 +41,7 @@ namespace ScriptCanvasEditor
//! Handles display change notifications, handles state change notifications, sends control requests
class Controller
: public AzQtComponents::StyledDialog
, private UpgradeNotifications::Bus::Handler
, private UpgradeNotificationsBus::Handler
, private ModelNotificationsBus::Handler
{
Q_OBJECT
@@ -62,10 +62,12 @@ namespace ScriptCanvasEditor
void AddLogEntries();
void OnCloseButtonPress();
void OnScanButtonPress();
void OnUpgradeButtonPress();
void OnButtonPressClose();
void OnButtonPressScan();
void OnButtonPressUpgrade();
void OnGraphUpgradeComplete(AZ::Data::Asset<AZ::Data::AssetData>&, bool skipped) override;
void OnScanBegin(size_t assetCount) override;
void OnScanComplete(const ScanResult& result) override;
void OnScanFilteredGraph(const AZ::Data::AssetInfo& info) override;
@@ -77,7 +79,7 @@ namespace ScriptCanvasEditor
// for single operation UI updates, just check the assets size, or note it on the request
void OnUpgradeBegin(const ModifyConfiguration& config, const AZStd::vector<AZ::Data::AssetInfo>& assets) override;
void OnUpgradeComplete() override;
void OnUpgradeComplete(const ModificationResults& results) override;
void OnUpgradeDependenciesGathered(const AZ::Data::AssetInfo& info, Result result) override;
void OnUpgradeDependencySortBegin
( const ModifyConfiguration& config
@@ -86,6 +88,8 @@ namespace ScriptCanvasEditor
( const ModifyConfiguration& config
, const AZStd::vector<AZ::Data::AssetInfo>& assets
, const AZStd::vector<size_t>& sortedOrder) override;
void OnUpgradeModificationBegin(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info) override;
void OnUpgradeModificationEnd(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info, ModificationResult result) override;
void SetSpinnerIsBusy(bool isBusy);
void SetRowBusy(int index);
@@ -6,8 +6,15 @@
*
*/
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/IO/FileIOEventBus.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/IO/FileOperations.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
#include <Editor/View/Windows/Tools/UpgradeTool/FileSaver.h>
#include <ScriptCanvas/Assets/ScriptCanvasAssetHandler.h>
namespace FileSaverCpp
{
@@ -44,6 +51,183 @@ namespace ScriptCanvasEditor
{
namespace VersionExplorer
{
FileSaver::FileSaver
( AZStd::function<bool()> onReadOnlyFile
, AZStd::function<void(const FileSaveResult& result)> onComplete)
: m_onReadOnlyFile(onReadOnlyFile)
, m_onComplete(onComplete)
{}
void FileSaver::PerformMove
( AZStd::string tmpFileName
, AZStd::string target
, size_t remainingAttempts)
{
FileSaverCpp::FileEventHandler fileEventHandler;
if (remainingAttempts == 0)
{
AZ::SystemTickBus::QueueFunction([this, tmpFileName]()
{
FileSaveResult result;
result.fileSaveError = "Failed to move updated file from temporary location to tmpFileName destination";
result.tempFileRemovalError = RemoveTempFile(tmpFileName);
m_onComplete(result);
});
}
else if (remainingAttempts == 2)
{
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
// before the last attempt, flush all the caches
AZ::IO::FileRequestPtr flushRequest = streamer->FlushCaches();
streamer->SetRequestCompleteCallback(flushRequest
, [this, remainingAttempts, tmpFileName, target]([[maybe_unused]] AZ::IO::FileRequestHandle request)
{
// One last try
AZ::SystemTickBus::QueueFunction(
[this, remainingAttempts, tmpFileName, target]() { PerformMove(tmpFileName, target, remainingAttempts - 1); });
});
streamer->QueueRequest(flushRequest);
}
else
{
// the actual move attempt
auto moveResult = AZ::IO::SmartMove(tmpFileName.c_str(), target.c_str());
if (moveResult.GetResultCode() == AZ::IO::ResultCode::Success)
{
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str());
// Bump the slice asset up in the asset processor's queue.
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, target.c_str());
AZ::SystemTickBus::QueueFunction([this, tmpFileName]()
{
FileSaveResult result;
result.tempFileRemovalError = RemoveTempFile(tmpFileName);
m_onComplete(result);
});
}
else
{
AZ_Warning(ScriptCanvas::k_VersionExplorerWindow.data(), false, "moving converted file to tmpFileName destination failed: %s, trying again", target.c_str());
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(target.c_str());
streamer->SetRequestCompleteCallback(flushRequest, [this, tmpFileName, target, remainingAttempts]([[maybe_unused]] AZ::IO::FileRequestHandle request)
{
// Continue saving.
AZ::SystemTickBus::QueueFunction([this, tmpFileName, target, remainingAttempts]() { PerformMove(tmpFileName, target, remainingAttempts - 1); });
});
streamer->QueueRequest(flushRequest);
}
}
}
void FileSaver::OnSourceFileReleased(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
AZStd::string relativePath, fullPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId());
bool fullPathFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, relativePath, fullPath);
AZStd::string tmpFileName;
// here we are saving the graph to a temp file instead of the original file and then copying the temp file to the original file.
// This ensures that AP will not a get a file change notification on an incomplete graph file causing it to fail processing. Temp files are ignored by AP.
if (!AZ::IO::CreateTempFileName(fullPath.c_str(), tmpFileName))
{
FileSaveResult result;
result.fileSaveError = "Failure to create temporary file name";
m_onComplete(result);
return;
}
bool tempSavedSucceeded = false;
AZ::IO::FileIOStream fileStream(tmpFileName.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText);
if (fileStream.IsOpen())
{
if (asset.GetType() == azrtti_typeid<ScriptCanvasEditor::ScriptCanvasAsset>())
{
ScriptCanvasEditor::ScriptCanvasAssetHandler handler;
tempSavedSucceeded = handler.SaveAssetData(asset, &fileStream);
}
fileStream.Close();
}
if (!tempSavedSucceeded)
{
FileSaveResult result;
result.fileSaveError = "Save asset data to temporary file failed";
m_onComplete(result);
return;
}
AzToolsFramework::SourceControlCommandBus::Broadcast
( &AzToolsFramework::SourceControlCommandBus::Events::RequestEdit
, fullPath.c_str()
, true
, [this, fullPath, tmpFileName]([[maybe_unused]] bool success, const AzToolsFramework::SourceControlFileInfo& info)
{
constexpr const size_t k_maxAttemps = 10;
if (!info.IsReadOnly())
{
PerformMove(tmpFileName, fullPath, k_maxAttemps);
}
else if (m_onReadOnlyFile && m_onReadOnlyFile())
{
AZ::IO::SystemFile::SetWritable(info.m_filePath.c_str(), true);
PerformMove(tmpFileName, fullPath, k_maxAttemps);
}
else
{
FileSaveResult result;
result.fileSaveError = "Source file was and remained read-only";
result.tempFileRemovalError = RemoveTempFile(tmpFileName);
m_onComplete(result);
}
});
}
AZStd::string FileSaver::RemoveTempFile(AZStd::string_view tempFile)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (!fileIO)
{
return "GraphUpgradeComplete: No FileIO instance";
}
if (fileIO->Exists(tempFile.data()) && !fileIO->Remove(tempFile.data()))
{
return AZStd::string::format("Failed to remove temporary file: %s", tempFile.data());
}
return "";
}
void FileSaver::Save(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
AZStd::string relativePath, fullPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, asset.GetId());
bool fullPathFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult
(fullPathFound
, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath
, relativePath, fullPath);
if (!fullPathFound)
{
FileSaveResult result;
result.fileSaveError = "Full source path not found";
m_onComplete(result);
}
else
{
auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
AZ::IO::FileRequestPtr flushRequest = streamer->FlushCache(fullPath);
streamer->SetRequestCompleteCallback(flushRequest, [this, asset]([[maybe_unused]] AZ::IO::FileRequestHandle request)
{
this->OnSourceFileReleased(asset);
});
streamer->QueueRequest(flushRequest);
}
}
}
}
@@ -14,10 +14,35 @@ namespace ScriptCanvasEditor
{
namespace VersionExplorer
{
struct FileSaveResult
{
AZStd::string fileSaveError;
AZStd::string tempFileRemovalError;
};
class FileSaver
{
public:
AZ_CLASS_ALLOCATOR(FileSaver, AZ::SystemAllocator, 0);
FileSaver
( AZStd::function<bool()> onReadOnlyFile
, AZStd::function<void(const FileSaveResult& result)> onComplete);
void Save(AZ::Data::Asset<AZ::Data::AssetData> asset);
private:
AZStd::function<void(const FileSaveResult& result)> m_onComplete;
AZStd::function<bool()> m_onReadOnlyFile;
void OnSourceFileReleased(AZ::Data::Asset<AZ::Data::AssetData> asset);
void PerformMove
( AZStd::string source
, AZStd::string target
, size_t remainingAttempts);
AZStd::string RemoveTempFile(AZStd::string_view tempFile);
};
}
}
@@ -103,7 +103,7 @@ namespace ScriptCanvasEditor
void Model::OnModificationComplete()
{
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnScanComplete, m_scanner->GetResult());
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeComplete, m_modifier->GetResult());
m_modifier.reset();
m_scanner.reset();
Idle();
@@ -57,7 +57,8 @@ namespace ScriptCanvasEditor
State m_state = State::Idle;
Log m_log;
// these two are managed by the same class because the modifer will only operate on the results of the scanner
AZStd::unique_ptr<Modifier> m_modifier;
AZStd::unique_ptr<Scanner> m_scanner;
AZStd::unique_ptr<ScriptCanvas::Grammar::SettingsCache> m_settingsCache;
@@ -17,11 +17,27 @@ namespace ScriptCanvasEditor
struct ModifyConfiguration
{
AZStd::function<void(AZ::Data::Asset<AZ::Data::AssetData>)> modification;
AZStd::function<bool()> onReadOnlyFile;
bool modifySingleAsset = false;
bool backupGraphBeforeModification = false;
bool successfulDependencyUpgradeRequired = true;
};
struct ModificationResult
{
AZ::Data::Asset<AZ::Data::AssetData> asset;
AZ::Data::AssetInfo assetInfo;
AZStd::string errorMessage;
};
class ModificationNotificationsTraits
: public AZ::EBusTraits
{
public:
virtual void ModificationComplete(const ModificationResult& result) = 0;
};
using ModificationNotificationsBus = AZ::EBus<ModificationNotificationsTraits>;
struct ScanConfiguration
{
AZStd::function<bool(AZ::Data::Asset<AZ::Data::AssetData>)> filter;
@@ -37,9 +53,10 @@ namespace ScriptCanvasEditor
};
using ModelRequestsBus = AZ::EBus<ModelRequestsTraits>;
struct ModificationResult
struct ModificationResults
{
AZStd::vector<AZ::Data::AssetInfo> m_successes;
AZStd::vector<ModificationResult> m_failures;
};
struct ScanResult
@@ -67,9 +84,7 @@ namespace ScriptCanvasEditor
virtual void OnScanUnFilteredGraph(const AZ::Data::AssetInfo& info) = 0;
virtual void OnUpgradeBegin(const ModifyConfiguration& config, const AZStd::vector<AZ::Data::AssetInfo>& assets) = 0;
virtual void OnUpgradeComplete() = 0;
// virtual void OnUpgradeModificationBegin(const ModifyConfiguration& config) = 0;
// virtual void OnUpgradeModification(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info, Result result) = 0;
virtual void OnUpgradeComplete(const ModificationResults& results) = 0;
virtual void OnUpgradeDependenciesGathered(const AZ::Data::AssetInfo& info, Result result) = 0;
virtual void OnUpgradeDependencySortBegin
( const ModifyConfiguration& config
@@ -78,6 +93,8 @@ namespace ScriptCanvasEditor
( const ModifyConfiguration& config
, const AZStd::vector<AZ::Data::AssetInfo>& assets
, const AZStd::vector<size_t>& sortedOrder) = 0;
virtual void OnUpgradeModificationBegin(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info) = 0;
virtual void OnUpgradeModificationEnd(const ModifyConfiguration& config, const AZ::Data::AssetInfo& info, ModificationResult result) = 0;
};
using ModelNotificationsBus = AZ::EBus<ModelNotificationsTraits>;
}
@@ -30,6 +30,7 @@ namespace ScriptCanvasEditor
, m_assets(assets)
, m_onComplete(onComplete)
{
AZ_Assert(m_config.modification, "No modification function provided");
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeBegin, modification, m_assets);
AZ::SystemTickBus::Handler::BusConnect();
}
@@ -52,6 +53,11 @@ namespace ScriptCanvasEditor
return iter->second;
}
const ModificationResults& Modifier::GetResult() const
{
return m_results;
}
void Modifier::GatherDependencies()
{
AZ::SerializeContext* serializeContext{};
@@ -140,6 +146,84 @@ namespace ScriptCanvasEditor
}
}
void Modifier::ModificationComplete(const ModificationResult& result)
{
m_result = result;
if (result.errorMessage.empty())
{
SaveModifiedGraph(result);
}
else
{
ReportModificationError(result.errorMessage);
}
}
void Modifier::ModifyCurrentAsset()
{
m_result = {};
m_result.assetInfo = GetCurrentAsset();
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeModificationBegin, m_config, GetCurrentAsset());
if (auto asset = LoadAsset())
{
ModificationNotificationsBus::Handler::BusConnect();
m_modifyState = ModifyState::InProgress;
m_config.modification(asset);
}
else
{
ReportModificationError("Failed to load during modification");
}
}
void Modifier::ModifyNextAsset()
{
ModelNotificationsBus::Broadcast
( &ModelNotificationsTraits::OnUpgradeModificationEnd, m_config, GetCurrentAsset(), m_result);
m_modifyState = ModifyState::Idle;
++m_assetIndex;
m_result = {};
}
void Modifier::ReportModificationError(AZStd::string_view report)
{
m_result.asset = {};
m_result.errorMessage = report;
m_results.m_failures.push_back(m_result);
ModifyNextAsset();
}
void Modifier::ReportModificationSuccess()
{
m_results.m_successes.push_back(m_result.assetInfo);
ModifyNextAsset();
}
void Modifier::OnFileSaveComplete(const FileSaveResult& result)
{
if (!result.tempFileRemovalError.empty())
{
VE_LOG
( "Temporary file not removed for %s: %s"
, m_result.assetInfo.m_relativePath.c_str()
, result.tempFileRemovalError.c_str());
}
m_fileSaver.reset();
if (result.fileSaveError.empty())
{
ReportModificationSuccess();
}
else
{
ReportModificationError(result.fileSaveError);
}
}
void Modifier::OnSystemTick()
{
switch (m_state)
@@ -154,50 +238,13 @@ namespace ScriptCanvasEditor
}
}
const AZStd::unordered_set<size_t>* Modifier::Sorter::GetDependencies(size_t index) const
void Modifier::SaveModifiedGraph(const ModificationResult& result)
{
auto iter = modifier->m_dependencies.find(index);
return iter != modifier->m_dependencies.end() ? &iter->second : nullptr;
}
void Modifier::Sorter::Sort()
{
for (size_t index = 0; index != modifier->m_assets.size(); ++index)
{
Visit(index);
}
}
void Modifier::Sorter::Visit(size_t index)
{
if (markedPermanent.contains(index))
{
return;
}
if (markedTemporary.contains(index))
{
AZ_Error
( ScriptCanvas::k_VersionExplorerWindow.data()
, false
, "Modifier: Dependency sort has failed during, circular dependency detected for Asset: %s"
, modifier->GetCurrentAsset().m_relativePath.c_str());
return;
}
markedTemporary.insert(index);
if (auto dependencies = GetDependencies(index))
{
for (auto& dependency : *dependencies)
{
Visit(dependency);
}
}
markedTemporary.erase(index);
markedPermanent.insert(index);
modifier->m_dependencyOrderedAssetIndicies.push_back(index);
m_modifyState = ModifyState::Saving;
m_fileSaver = AZStd::make_unique<FileSaver>
( m_config.onReadOnlyFile
, [this](const FileSaveResult& result) { OnFileSaveComplete(result); });
m_fileSaver->Save(result.asset);
}
void Modifier::SortGraphsByDependencies()
@@ -208,6 +255,11 @@ namespace ScriptCanvasEditor
sorter.Sort();
}
ModificationResults&& Modifier::TakeResult()
{
return AZStd::move(m_results);
}
void Modifier::TickGatherDependencies()
{
if (m_assetIndex == 0)
@@ -260,8 +312,69 @@ namespace ScriptCanvasEditor
void Modifier::TickUpdateGraph()
{
if (m_assetIndex == m_assets.size())
{
VE_LOG("Modifier: Complete.");
AZ::SystemTickBus::Handler::BusDisconnect();
if (m_onComplete)
{
m_onComplete();
}
}
else
{
if (m_modifyState == ModifyState::Idle)
{
ModifyCurrentAsset();
}
}
}
const AZStd::unordered_set<size_t>* Modifier::Sorter::GetDependencies(size_t index) const
{
auto iter = modifier->m_dependencies.find(index);
return iter != modifier->m_dependencies.end() ? &iter->second : nullptr;
}
void Modifier::Sorter::Sort()
{
for (size_t index = 0; index != modifier->m_assets.size(); ++index)
{
Visit(index);
}
}
void Modifier::Sorter::Visit(size_t index)
{
if (markedPermanent.contains(index))
{
return;
}
if (markedTemporary.contains(index))
{
AZ_Error
(ScriptCanvas::k_VersionExplorerWindow.data()
, false
, "Modifier: Dependency sort has failed during, circular dependency detected for Asset: %s"
, modifier->GetCurrentAsset().m_relativePath.c_str());
return;
}
markedTemporary.insert(index);
if (auto dependencies = GetDependencies(index))
{
for (auto& dependency : *dependencies)
{
Visit(dependency);
}
}
markedTemporary.erase(index);
markedPermanent.insert(index);
modifier->m_dependencyOrderedAssetIndicies.push_back(index);
}
}
}
@@ -8,9 +8,10 @@
#pragma once
#include <ScriptCanvas/Core/Core.h>
#include <Editor/View/Windows/Tools/UpgradeTool/ModelTraits.h>
#include <AzCore/Component/TickBus.h>
#include <Editor/View/Windows/Tools/UpgradeTool/FileSaver.h>
#include <Editor/View/Windows/Tools/UpgradeTool/ModelTraits.h>
#include <ScriptCanvas/Core/Core.h>
namespace ScriptCanvasEditor
{
@@ -18,6 +19,7 @@ namespace ScriptCanvasEditor
{
class Modifier
: private AZ::SystemTickBus::Handler
, private ModificationNotificationsBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(Modifier, AZ::SystemAllocator, 0);
@@ -27,6 +29,9 @@ namespace ScriptCanvasEditor
, AZStd::vector<AZ::Data::AssetInfo>&& assets
, AZStd::function<void()> onComplete);
const ModificationResults& GetResult() const;
ModificationResults&& TakeResult();
private:
friend class Sorter;
@@ -48,7 +53,16 @@ namespace ScriptCanvasEditor
ModifyingGraphs
};
enum class ModifyState
{
Idle,
InProgress,
Saving,
};
// the two states reside in this class because the modification is only complete if the new source file saves out
State m_state = State::GatheringDependencies;
ModifyState m_modifyState = ModifyState::Idle;
size_t m_assetIndex = 0;
AZStd::function<void()> m_onComplete;
// asset infos in scanned order
@@ -61,12 +75,21 @@ namespace ScriptCanvasEditor
AZStd::vector<size_t> m_failures;
ModifyConfiguration m_config;
ModificationResult m_result;
ModificationResults m_results;
AZStd::unique_ptr<FileSaver> m_fileSaver;
void GatherDependencies();
const AZ::Data::AssetInfo& GetCurrentAsset() const;
AZStd::unordered_set<size_t>& GetOrCreateDependencyIndexSet();
AZ::Data::Asset<AZ::Data::AssetData> LoadAsset();
void ModifyCurrentAsset();
void ModifyNextAsset();
void ModificationComplete(const ModificationResult& result) override;
void ReportModificationError(AZStd::string_view report);
void ReportModificationSuccess();
void SaveModifiedGraph(const ModificationResult& result);
void SortGraphsByDependencies();
void OnFileSaveComplete(const FileSaveResult& result);
void OnSystemTick() override;
void TickGatherDependencies();
void TickUpdateGraph();
@@ -25,7 +25,6 @@ namespace ScriptCanvasEditor
Scanner(const ScanConfiguration& config, AZStd::function<void()> onComplete);
const ScanResult& GetResult() const;
ScanResult&& TakeResult();
private: