|
@@ -30,38 +30,141 @@ namespace ScriptCanvasEditor
|
|
|
AZ_Assert(m_config.modification, "No modification function provided");
|
|
|
ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeBegin, modification, m_assets);
|
|
|
AZ::SystemTickBus::Handler::BusConnect();
|
|
|
+ AzFramework::AssetSystemInfoBus::Handler::BusConnect();
|
|
|
+ AzFramework::AssetSystemInfoBus::Handler::BusConnect();
|
|
|
+ m_result.asset = m_assets[GetCurrentIndex()];
|
|
|
}
|
|
|
|
|
|
- size_t Modifier::GetCurrentIndex() const
|
|
|
+ Modifier::~Modifier()
|
|
|
{
|
|
|
- return m_state == State::GatheringDependencies
|
|
|
- ? m_assetIndex
|
|
|
- : m_dependencyOrderedAssetIndicies[m_assetIndex];
|
|
|
+ AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
|
|
|
+ AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
|
|
|
+ }
|
|
|
|
|
|
+ bool Modifier::AllDependenciesCleared(const AZStd::unordered_set<size_t>& dependencies) const
|
|
|
+ {
|
|
|
+ for (auto index : dependencies)
|
|
|
+ {
|
|
|
+ SourceHandle dependency = m_assets[index];
|
|
|
+ CompleteDescriptionInPlace(dependency);
|
|
|
+
|
|
|
+ if (dependency.Id().IsNull() || !m_assetsCompletedByAP.contains(dependency.Id()))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
- AZStd::unordered_set<size_t>& Modifier::GetOrCreateDependencyIndexSet()
|
|
|
+ bool Modifier::AnyDependenciesFailed(const AZStd::unordered_set<size_t>& dependencies) const
|
|
|
{
|
|
|
- auto iter = m_dependencies.find(m_assetIndex);
|
|
|
- if (iter == m_dependencies.end())
|
|
|
+ for (auto index : dependencies)
|
|
|
{
|
|
|
- iter = m_dependencies.insert_or_assign(m_assetIndex, AZStd::unordered_set<size_t>()).first;
|
|
|
+ SourceHandle dependency = m_assets[index];
|
|
|
+ CompleteDescriptionInPlace(dependency);
|
|
|
+
|
|
|
+ if (dependency.Id().IsNull() || m_assetsFailedByAP.contains(dependency.Id()))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return iter->second;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- const ModificationResults& Modifier::GetResult() const
|
|
|
+ AZStd::sys_time_t Modifier::CalculateRemainingWaitTime(const AZStd::unordered_set<size_t>& dependencies) const
|
|
|
{
|
|
|
- return m_results;
|
|
|
+ auto maxSeconds = AZStd::chrono::seconds(dependencies.size() * m_config.perDependencyWaitSecondsMax);
|
|
|
+ auto waitedSeconds = AZStd::chrono::seconds(AZStd::chrono::system_clock::now() - m_waitTimeStamp);
|
|
|
+ return (maxSeconds - waitedSeconds).count();
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::SourceFileChanged(AZStd::string relativePath, [[maybe_unused]] AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid fileAssetId)
|
|
|
+ {
|
|
|
+ AZ_TracePrintf("SC", "received SourceFileChanged: %s", relativePath.c_str());
|
|
|
+ VE_LOG("received SourceFileChanged: %s", relativePath.c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::SourceFileFailed(AZStd::string relativePath, [[maybe_unused]] AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid fileAssetId)
|
|
|
+ {
|
|
|
+ AZ_TracePrintf("SC", "received SourceFileFailed: %s", relativePath.c_str());
|
|
|
+ VE_LOG("received SourceFileFailed: %s", relativePath.c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::ProcessNotifications()
|
|
|
+ {
|
|
|
+ AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
|
|
|
+
|
|
|
+ for (const auto& assetPath : m_successNotifications)
|
|
|
+ {
|
|
|
+ VE_LOG("received AssetCompilationSuccess: %s", assetPath.c_str());
|
|
|
+ SourceHandle sourceHandle(nullptr, {}, assetPath.c_str());
|
|
|
+ CompleteDescriptionInPlace(sourceHandle);
|
|
|
+
|
|
|
+ if (m_attemptedAssets.contains(sourceHandle.Id()))
|
|
|
+ {
|
|
|
+ m_assetsCompletedByAP.insert(sourceHandle.Id());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ m_successNotifications.clear();
|
|
|
+
|
|
|
+ for (const auto& assetPath : m_failureNotifications)
|
|
|
+ {
|
|
|
+ VE_LOG("received AssetCompilationFailed: %s", assetPath.c_str());
|
|
|
+ SourceHandle sourceHandle(nullptr, {}, assetPath.c_str());
|
|
|
+ CompleteDescriptionInPlace(sourceHandle);
|
|
|
+
|
|
|
+ if (m_attemptedAssets.contains(sourceHandle.Id()))
|
|
|
+ {
|
|
|
+ m_assetsFailedByAP.insert(sourceHandle.Id());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ m_failureNotifications.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::AssetCompilationSuccess([[maybe_unused]] const AZStd::string& assetPath)
|
|
|
+ {
|
|
|
+ AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
|
|
|
+ // test failure path m_successNotifications.insert(assetPath);
|
|
|
+ m_failureNotifications.insert(assetPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::AssetCompilationFailed(const AZStd::string& assetPath)
|
|
|
+ {
|
|
|
+ AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
|
|
|
+ m_failureNotifications.insert(assetPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::CheckDependencies()
|
|
|
+ {
|
|
|
+ ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeModificationBegin, m_config, m_result.asset);
|
|
|
+
|
|
|
+ if (auto dependencies = GetDependencies(GetCurrentIndex()); dependencies != nullptr && !dependencies->empty())
|
|
|
+ {
|
|
|
+ VE_LOG
|
|
|
+ ( "dependencies found for %s, update will wait for the AP to finish processing them"
|
|
|
+ , m_result.asset.Path().c_str());
|
|
|
+
|
|
|
+ m_waitTimeStamp = AZStd::chrono::system_clock::now();
|
|
|
+ m_waitLogTimeStamp = AZStd::chrono::system_clock::time_point{};
|
|
|
+ m_modifyState = ModifyState::WaitingForDependencyProcessing;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ m_modifyState = ModifyState::StartModification;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
void Modifier::GatherDependencies()
|
|
|
{
|
|
|
AZ::SerializeContext* serializeContext{};
|
|
|
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
|
|
AZ_Assert(serializeContext, "SerializeContext is required to enumerate dependent assets in the ScriptCanvas file");
|
|
|
|
|
|
+ LoadAsset();
|
|
|
bool anyFailures = false;
|
|
|
|
|
|
if (m_result.asset.Get() && m_result.asset.Mod()->GetGraphData())
|
|
@@ -101,7 +204,7 @@ namespace ScriptCanvasEditor
|
|
|
, nullptr))
|
|
|
{
|
|
|
anyFailures = true;
|
|
|
- VE_LOG("Modifier: ERROR - Failed to gather dependencies from graph data: %s"
|
|
|
+ VE_LOG("Modifier: ERROR - Failed to gather dependencies from graph data: %s"
|
|
|
, m_result.asset.Path().c_str())
|
|
|
}
|
|
|
}
|
|
@@ -111,16 +214,40 @@ namespace ScriptCanvasEditor
|
|
|
VE_LOG("Modifier: ERROR - Failed to load asset %s for modification, even though it scanned properly"
|
|
|
, m_result.asset.Path().c_str());
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
ModelNotificationsBus::Broadcast
|
|
|
( &ModelNotificationsTraits::OnUpgradeDependenciesGathered
|
|
|
, m_result.asset
|
|
|
, anyFailures ? Result::Failure : Result::Success);
|
|
|
+ }
|
|
|
|
|
|
- ReleaseCurrentAsset();
|
|
|
+ size_t Modifier::GetCurrentIndex() const
|
|
|
+ {
|
|
|
+ return m_state == State::GatheringDependencies
|
|
|
+ ? m_assetIndex
|
|
|
+ : m_dependencyOrderedAssetIndicies[m_assetIndex];
|
|
|
+ }
|
|
|
|
|
|
- // Flush asset database events to ensure no asset references are held by closures queued on Ebuses.
|
|
|
- AZ::Data::AssetManager::Instance().DispatchEvents();
|
|
|
+ const AZStd::unordered_set<size_t>* Modifier::GetDependencies(size_t index) const
|
|
|
+ {
|
|
|
+ auto iter = m_dependencies.find(index);
|
|
|
+ return iter != m_dependencies.end() ? &iter->second : nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ AZStd::unordered_set<size_t>& Modifier::GetOrCreateDependencyIndexSet()
|
|
|
+ {
|
|
|
+ auto iter = m_dependencies.find(m_assetIndex);
|
|
|
+ if (iter == m_dependencies.end())
|
|
|
+ {
|
|
|
+ iter = m_dependencies.insert_or_assign(m_assetIndex, AZStd::unordered_set<size_t>()).first;
|
|
|
+ }
|
|
|
+
|
|
|
+ return iter->second;
|
|
|
+ }
|
|
|
+
|
|
|
+ const ModificationResults& Modifier::GetResult() const
|
|
|
+ {
|
|
|
+ return m_results;
|
|
|
}
|
|
|
|
|
|
void Modifier::LoadAsset()
|
|
@@ -144,7 +271,7 @@ namespace ScriptCanvasEditor
|
|
|
}
|
|
|
else if (m_result.asset.Describe() != result.asset.Describe())
|
|
|
{
|
|
|
- ReportModificationError("Received modifiction complete notification for different result");
|
|
|
+ ReportModificationError("Received modification complete notification for different result");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -154,9 +281,6 @@ namespace ScriptCanvasEditor
|
|
|
|
|
|
void Modifier::ModifyCurrentAsset()
|
|
|
{
|
|
|
- m_result = {};
|
|
|
- m_result.asset = m_assets[GetCurrentIndex()];
|
|
|
- ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeModificationBegin, m_config, m_result.asset);
|
|
|
LoadAsset();
|
|
|
|
|
|
if (m_result.asset.IsGraphValid())
|
|
@@ -171,34 +295,55 @@ namespace ScriptCanvasEditor
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void Modifier::ModifyNextAsset()
|
|
|
+ void Modifier::InitializeResult()
|
|
|
{
|
|
|
- ModelNotificationsBus::Broadcast
|
|
|
- ( &ModelNotificationsTraits::OnUpgradeModificationEnd, m_config, m_result.asset, m_result);
|
|
|
+ m_result = {};
|
|
|
+
|
|
|
+ if (m_assetIndex != m_assets.size())
|
|
|
+ {
|
|
|
+ m_result.asset = m_assets[GetCurrentIndex()];
|
|
|
+ CompleteDescriptionInPlace(m_result.asset);
|
|
|
+ m_attemptedAssets.insert(m_result.asset.Id());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::NextAsset()
|
|
|
+ {
|
|
|
+ ++m_assetIndex;
|
|
|
+ InitializeResult();
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::NextModification()
|
|
|
+ {
|
|
|
+ ModelNotificationsBus::Broadcast( &ModelNotificationsTraits::OnUpgradeModificationEnd, m_config, m_result.asset, m_result);
|
|
|
ModificationNotificationsBus::Handler::BusDisconnect();
|
|
|
+ NextAsset();
|
|
|
+ m_fileSaveResult = {};
|
|
|
m_modifyState = ModifyState::Idle;
|
|
|
- ReleaseCurrentAsset();
|
|
|
- ++m_assetIndex;
|
|
|
- m_result = {};
|
|
|
}
|
|
|
|
|
|
void Modifier::ReleaseCurrentAsset()
|
|
|
{
|
|
|
m_result.asset = m_result.asset.Describe();
|
|
|
+ // Flush asset database events to ensure no asset references are held by closures queued on Ebuses.
|
|
|
+ AZ::Data::AssetManager::Instance().DispatchEvents();
|
|
|
}
|
|
|
|
|
|
void Modifier::ReportModificationError(AZStd::string_view report)
|
|
|
{
|
|
|
m_result.errorMessage = report;
|
|
|
m_results.m_failures.push_back({ m_result.asset.Describe(), report });
|
|
|
- ModifyNextAsset();
|
|
|
+ m_assetsFailedByAP.insert(m_result.asset.Id());
|
|
|
+ NextModification();
|
|
|
}
|
|
|
|
|
|
void Modifier::ReportModificationSuccess()
|
|
|
{
|
|
|
- m_result.asset = m_result.asset.Describe();
|
|
|
+ // \note DO NOT put asset into the m_assetsCompletedByAP here. That can only be done when the message is received by the AP
|
|
|
m_results.m_successes.push_back({ m_result.asset.Describe(), {} });
|
|
|
- ModifyNextAsset();
|
|
|
+ AzFramework::AssetSystemRequestBus::Broadcast(
|
|
|
+ &AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetByUuid, m_result.asset.Id());
|
|
|
+ NextModification();
|
|
|
}
|
|
|
|
|
|
void Modifier::ReportSaveResult()
|
|
@@ -214,9 +359,6 @@ namespace ScriptCanvasEditor
|
|
|
{
|
|
|
ReportModificationError(m_fileSaveResult.fileSaveError);
|
|
|
}
|
|
|
-
|
|
|
- m_fileSaveResult = {};
|
|
|
- m_modifyState = ModifyState::Idle;
|
|
|
}
|
|
|
|
|
|
void Modifier::OnFileSaveComplete(const FileSaveResult& result)
|
|
@@ -316,49 +458,87 @@ namespace ScriptCanvasEditor
|
|
|
|
|
|
m_assetIndex = 0;
|
|
|
m_state = State::ModifyingGraphs;
|
|
|
+ InitializeResult();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
GatherDependencies();
|
|
|
- ReleaseCurrentAsset();
|
|
|
- ++m_assetIndex;
|
|
|
+ NextAsset();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void Modifier::TickUpdateGraph()
|
|
|
{
|
|
|
- if (m_assetIndex == m_assets.size())
|
|
|
+ AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
|
|
|
+
|
|
|
+ switch (m_modifyState)
|
|
|
{
|
|
|
- VE_LOG("Modifier: Complete.");
|
|
|
- AZ::SystemTickBus::Handler::BusDisconnect();
|
|
|
+ case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::Idle:
|
|
|
+ if (m_assetIndex == m_assets.size())
|
|
|
+ {
|
|
|
+ VE_LOG("Modifier: Complete.");
|
|
|
+ AZ::SystemTickBus::Handler::BusDisconnect();
|
|
|
|
|
|
- if (m_onComplete)
|
|
|
+ if (m_onComplete)
|
|
|
+ {
|
|
|
+ m_onComplete();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- m_onComplete();
|
|
|
+ CheckDependencies();
|
|
|
}
|
|
|
+ break;
|
|
|
+ case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::WaitingForDependencyProcessing:
|
|
|
+ WaitForDependencies();
|
|
|
+ break;
|
|
|
+ case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::StartModification:
|
|
|
+ ModifyCurrentAsset();
|
|
|
+ break;
|
|
|
+ case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::ReportResult:
|
|
|
+ ReportSaveResult();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
}
|
|
|
- else
|
|
|
+ }
|
|
|
+
|
|
|
+ void Modifier::WaitForDependencies()
|
|
|
+ {
|
|
|
+ const AZ::s32 LogPeriodSeconds = 5;
|
|
|
+
|
|
|
+ ProcessNotifications();
|
|
|
+
|
|
|
+ auto dependencies = GetDependencies(GetCurrentIndex());
|
|
|
+ if (dependencies == nullptr || dependencies->empty() || AllDependenciesCleared(*dependencies))
|
|
|
+ {
|
|
|
+ m_modifyState = ModifyState::StartModification;
|
|
|
+ }
|
|
|
+ else if (AnyDependenciesFailed(*dependencies))
|
|
|
+ {
|
|
|
+ ReportModificationError("A required dependency failed to update, graph cannot update.");
|
|
|
+ }
|
|
|
+ else if (AZStd::chrono::seconds(CalculateRemainingWaitTime(*dependencies)).count() < 0)
|
|
|
+ {
|
|
|
+ ReportModificationError("Dependency update time has taken too long, aborting modification.");
|
|
|
+ }
|
|
|
+ else if (AZStd::chrono::seconds(AZStd::chrono::system_clock::now() - m_waitLogTimeStamp).count() > LogPeriodSeconds)
|
|
|
{
|
|
|
- AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
|
|
|
+ m_waitLogTimeStamp = AZStd::chrono::system_clock::now();
|
|
|
|
|
|
- switch (m_modifyState)
|
|
|
- {
|
|
|
- case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::Idle:
|
|
|
- ModifyCurrentAsset();
|
|
|
- break;
|
|
|
- case ScriptCanvasEditor::VersionExplorer::Modifier::ModifyState::ReportResult:
|
|
|
- ReportSaveResult();
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
+ AZ_TracePrintf
|
|
|
+ ( ScriptCanvas::k_VersionExplorerWindow.data()
|
|
|
+ , "Waiting for dependencies for %d more seconds: %s"
|
|
|
+ , AZStd::chrono::seconds(CalculateRemainingWaitTime(*dependencies)).count()
|
|
|
+ , m_result.asset.Path().c_str());
|
|
|
+
|
|
|
+ ModelNotificationsBus::Broadcast(&ModelNotificationsTraits::OnUpgradeDependencyWaitInterval, m_result.asset);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
+ return modifier->GetDependencies(index);
|
|
|
}
|
|
|
|
|
|
void Modifier::Sorter::Sort()
|
|
@@ -379,7 +559,7 @@ namespace ScriptCanvasEditor
|
|
|
if (markedTemporary.contains(index))
|
|
|
{
|
|
|
AZ_Error
|
|
|
- (ScriptCanvas::k_VersionExplorerWindow.data()
|
|
|
+ ( ScriptCanvas::k_VersionExplorerWindow.data()
|
|
|
, false
|
|
|
, "Modifier: Dependency sort has failed during, circular dependency detected for Asset: %s"
|
|
|
, modifier->m_result.asset.Path().c_str());
|