@ -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 modific tion complete notification for different result" ) ;
ReportModificationError ( " Received modific a tion 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 : : ModifyNextAsse t( )
void Modifier : : InitializeResul t( )
{
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 ( ) ) ;