2021-04-30 08:14:54 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "VirtualizationSourceControlUtilities.h"
# include "Async/TaskGraphInterfaces.h"
# include "ISourceControlModule.h"
# include "ISourceControlProvider.h"
# include "ISourceControlRevision.h"
# include "Misc/PackagePath.h"
# include "Misc/PackageSegment.h"
# include "SourceControlOperations.h"
2022-12-16 06:25:07 -05:00
# define LOCTEXT_NAMESPACE "Virtualization"
2021-04-30 08:14:54 -04:00
// When enabled we push the source control work to the main thread via the task graph system.
// Note that when enabled this can potentially cause thread locks so is no a production ready
// fix. Realistically we need to fix the SourceControl API to accept requests from any thread
// which is currently under discussion.
# define UE_FORCE_SOURCECONTROL_TO_MAIN_THREAD 1
// This code relies on changes to the SourceControl API which need to be discussed, enabling this
// will allow the code to compile so it can be submitted without causing problems to others.
// The feature is entirely opt in so it shouldn't affect anyone else.
# define UE_FIX_COMPILE_ISSUES 1
2021-10-27 15:14:40 -04:00
namespace UE : : Virtualization : : Experimental
2021-04-30 08:14:54 -04:00
{
2021-10-27 15:14:40 -04:00
2021-04-30 08:14:54 -04:00
bool FVirtualizationSourceControlUtilities : : SyncPayloadSidecarFile ( const FPackagePath & PackagePath )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FPayloadSideCarFunctionality : : SyncPayloadSidecarFile ) ;
if ( ! ISourceControlModule : : Get ( ) . IsEnabled ( ) )
{
2022-11-23 11:57:50 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Attempting to sync a .upayload for '%s' but revision control is disabled! " ) , * PackagePath . GetDebugName ( ) ) ;
2021-04-30 08:14:54 -04:00
return false ;
}
// We only allow the perforce source control system as we have not adequately tested the others!
const FName SourceControlName = ISourceControlModule : : Get ( ) . GetProvider ( ) . GetName ( ) ;
2021-07-27 09:45:11 -04:00
if ( SourceControlName ! = TEXT ( " Perforce " ) )
2021-04-30 08:14:54 -04:00
{
2022-11-23 11:57:50 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Attempting to sync a .upayload for '%s' but revision control is '%s' and only Perforce is currently supported! " ) , * PackagePath . GetDebugName ( ) , * SourceControlName . ToString ( ) ) ;
2021-04-30 08:14:54 -04:00
return false ;
}
# if UE_FORCE_SOURCECONTROL_TO_MAIN_THREAD
// ISourceControlOperation commands must be invoked from the game thread so we need to push the work there
// and then wait on the results.
bool bResult = false ;
FGraphEventRef EventRef = FFunctionGraphTask : : CreateAndDispatchWhenReady ( [ this , & bResult , PackagePath ] ( )
{
bResult = SyncPayloadSidecarFileInternal ( PackagePath ) ;
} , TStatId ( ) , nullptr , ENamedThreads : : GameThread ) ;
FTaskGraphInterface : : Get ( ) . WaitUntilTaskCompletes ( EventRef ) ;
return bResult ;
# else
return SyncPayloadSidecarFileInternal ( PackagePath ) ;
# endif //UE_FORCE_SOURCECONTROL_TO_MAIN_THREAD
}
bool FVirtualizationSourceControlUtilities : : SyncPayloadSidecarFileInternal ( const FPackagePath & PackagePath )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FPayloadSideCarFunctionality : : SyncPayloadSidecarFileInternal ) ;
ISourceControlProvider & SCCProvider = ISourceControlModule : : Get ( ) . GetProvider ( ) ;
const FString AssetFilePath = PackagePath . GetLocalFullPath ( EPackageSegment : : Header ) ;
const FString SidecarFilePath = PackagePath . GetLocalFullPath ( EPackageSegment : : PayloadSidecar ) ;
// Update the state of the .uasset file in the cache and store the history of the file
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FPayloadSideCarFunctionality : : SyncPayloadSidecarGameThread : : UpdateStatus ) ;
TSharedRef < FUpdateStatus , ESPMode : : ThreadSafe > UpdateStatusOperation = ISourceControlOperation : : Create < FUpdateStatus > ( ) ;
UpdateStatusOperation - > SetUpdateHistory ( true ) ;
if ( SCCProvider . Execute ( UpdateStatusOperation , AssetFilePath ) ! = ECommandResult : : Succeeded )
{
2022-11-23 11:57:50 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to update revision control state for '%s' " ) , * PackagePath . GetDebugName ( ) ) ;
2021-04-30 08:14:54 -04:00
return false ;
}
}
// Get the state of the .uasset from the cache
FSourceControlStatePtr State ;
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FPayloadSideCarFunctionality : : SyncPayloadSidecarGameThread : : GetState ) ;
State = SCCProvider . GetState ( AssetFilePath , EStateCacheUsage : : Use ) ;
if ( ! State )
{
2022-11-23 11:57:50 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to find revision control state for '%s' " ) , * PackagePath . GetDebugName ( ) ) ;
2021-04-30 08:14:54 -04:00
return false ;
}
}
// Make sure we have access to a valid revision
# if UE_FIX_COMPILE_ISSUES
checkf ( false , TEXT ( " Attempting to call SyncPayloadSidecarFile before UE_FIX_COMPILE_ISSUES was fixed! " ) ) ;
const int32 LocalRevision = INDEX_NONE ;
# else
const int32 LocalRevision = State - > GetLocalRevision ( ) ;
# endif //UE_FIX_COMPILE_ISSUES
if ( LocalRevision = = INDEX_NONE )
{
2022-11-23 11:57:50 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to find revision control revision for '%s' " ) , * PackagePath . GetDebugName ( ) ) ;
2021-04-30 08:14:54 -04:00
return false ;
}
// Now we can access the history for the revision that the .uasset is currently synced to
TSharedPtr < ISourceControlRevision , ESPMode : : ThreadSafe > Revision = State - > FindHistoryRevision ( LocalRevision ) ;
if ( ! Revision )
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to find the revision (%d) history for '%s' " ) , LocalRevision , * PackagePath . GetDebugName ( ) ) ;
return false ;
}
// For P4 the CheckInIdentifier is actually the changelist that the revision was submitted under
const int32 ChangelistNumber = Revision - > GetCheckInIdentifier ( ) ;
if ( ChangelistNumber = = INDEX_NONE )
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Revision (%d) history was invalid for '%s' " ) , LocalRevision , * PackagePath . GetDebugName ( ) ) ;
return false ;
}
// The revision number passed to FSync should be the changelist number, not the file revision
// NOTE: This will not force sync, if the file is the correct version in the workspace but has
// been deleted, this will not fix it!
{
TRACE_CPUPROFILER_EVENT_SCOPE ( FPayloadSideCarFunctionality : : SyncPayloadSidecarGameThread : : Sync ) ;
TSharedRef < FSync , ESPMode : : ThreadSafe > SyncCommand = ISourceControlOperation : : Create < FSync > ( ) ;
SyncCommand - > SetRevision ( FString : : Printf ( TEXT ( " %d " ) , ChangelistNumber ) ) ;
if ( SCCProvider . Execute ( SyncCommand , SidecarFilePath , EConcurrency : : Asynchronous ) = = ECommandResult : : Succeeded )
{
UE_LOG ( LogVirtualization , Verbose , TEXT ( " Successfully synced .upayload file for '%s' " ) , * PackagePath . GetDebugName ( ) ) ;
return true ;
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to sync .upayload file for '%s' " ) , * PackagePath . GetDebugName ( ) ) ;
return false ;
}
}
return true ;
}
2021-10-27 15:14:40 -04:00
} // namespace UE::Virtualization::Experimental
2022-12-16 06:25:07 -05:00
namespace UE : : Virtualization
{
bool TryCheckoutFiles ( const TArray < FString > & FilesToCheckState , TArray < FText > & OutErrors , TArray < FString > * OutFilesCheckedOut )
{
TArray < FSourceControlStateRef > PathStates ;
PathStates . Reserve ( FilesToCheckState . Num ( ) ) ;
ISourceControlProvider & SCCProvider = ISourceControlModule : : Get ( ) . GetProvider ( ) ;
2023-01-16 03:53:12 -05:00
// Early out if revision control is disabled
2022-12-16 06:25:07 -05:00
if ( ! SCCProvider . IsEnabled ( ) )
{
2023-01-16 03:53:12 -05:00
return true ;
2022-12-16 06:25:07 -05:00
}
ECommandResult : : Type UpdateResult = SCCProvider . GetState ( FilesToCheckState , PathStates , EStateCacheUsage : : ForceUpdate ) ;
if ( UpdateResult ! = ECommandResult : : Type : : Succeeded )
{
FText Message = LOCTEXT ( " VA_FileState " , " Failed to find the state of package files in revision control when trying to check them out " ) ;
OutErrors . Add ( MoveTemp ( Message ) ) ;
return false ;
}
TArray < FString > FilesToCheckout ;
FilesToCheckout . Reserve ( PathStates . Num ( ) ) ;
for ( const FSourceControlStateRef & State : PathStates )
{
if ( State - > IsSourceControlled ( ) & & ! State - > CanEdit ( ) & & State - > CanCheckout ( ) )
{
FilesToCheckout . Add ( State - > GetFilename ( ) ) ;
}
}
if ( ! FilesToCheckout . IsEmpty ( ) )
{
ECommandResult : : Type CheckoutResult = SCCProvider . Execute ( ISourceControlOperation : : Create < FCheckOut > ( ) , FilesToCheckout ) ;
if ( CheckoutResult = = ECommandResult : : Type : : Succeeded )
{
if ( OutFilesCheckedOut ! = nullptr )
{
* OutFilesCheckedOut = MoveTemp ( FilesToCheckout ) ;
}
}
else
{
FText Message = LOCTEXT ( " VA_Checkout " , " Failed to checkout packages from revision control " ) ;
OutErrors . Add ( MoveTemp ( Message ) ) ;
return false ;
}
}
return true ;
}
} //namespace UE::Virtualization
# undef LOCTEXT_NAMESPACE