2019-07-09 02:14:11 -04:00
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
# include "BundlePrereqCombinedStatusHelper.h"
# include "Containers/Ticker.h"
# include "InstallBundleManagerPrivatePCH.h"
FBundlePrereqCombinedStatusHelper : : FBundlePrereqCombinedStatusHelper ( )
2019-07-12 00:18:00 -04:00
: DownloadWeight ( 1.f )
, InstallWeight ( 1.f )
, RequiredBundleNames ( )
2019-07-09 02:14:11 -04:00
, BundleStatusCache ( )
, CachedBundleWeights ( )
, CurrentCombinedStatus ( )
, bBundleNeedsUpdate ( false )
, InstallBundleManager ( nullptr )
, TickHandle ( )
{
SetupDelegates ( ) ;
}
FBundlePrereqCombinedStatusHelper : : ~ FBundlePrereqCombinedStatusHelper ( )
{
CleanUpDelegates ( ) ;
}
2019-07-12 00:18:00 -04:00
FBundlePrereqCombinedStatusHelper : : FBundlePrereqCombinedStatusHelper ( const FBundlePrereqCombinedStatusHelper & Other )
{
* this = Other ;
}
FBundlePrereqCombinedStatusHelper : : FBundlePrereqCombinedStatusHelper ( FBundlePrereqCombinedStatusHelper & & Other )
{
* this = MoveTemp ( Other ) ;
}
FBundlePrereqCombinedStatusHelper & FBundlePrereqCombinedStatusHelper : : operator = ( const FBundlePrereqCombinedStatusHelper & Other )
{
if ( this ! = & Other )
{
//Just copy all this data
DownloadWeight = Other . DownloadWeight ;
InstallWeight = Other . InstallWeight ;
RequiredBundleNames = Other . RequiredBundleNames ;
BundleStatusCache = Other . BundleStatusCache ;
CachedBundleWeights = Other . CachedBundleWeights ;
CurrentCombinedStatus = Other . CurrentCombinedStatus ;
bBundleNeedsUpdate = Other . bBundleNeedsUpdate ;
InstallBundleManager = Other . InstallBundleManager ;
//Don't copy TickHandle as we want to setup our own here
SetupDelegates ( ) ;
}
return * this ;
}
FBundlePrereqCombinedStatusHelper & FBundlePrereqCombinedStatusHelper : : operator = ( FBundlePrereqCombinedStatusHelper & & Other )
{
if ( this ! = & Other )
{
//Just copy small data
DownloadWeight = Other . DownloadWeight ;
InstallWeight = Other . InstallWeight ;
CurrentCombinedStatus = Other . CurrentCombinedStatus ;
bBundleNeedsUpdate = Other . bBundleNeedsUpdate ;
InstallBundleManager = Other . InstallBundleManager ;
//Move bigger data
RequiredBundleNames = MoveTemp ( Other . RequiredBundleNames ) ;
BundleStatusCache = MoveTemp ( Other . BundleStatusCache ) ;
CachedBundleWeights = MoveTemp ( Other . CachedBundleWeights ) ;
//Prevent other from having callbacks now that its information is gone
Other . CleanUpDelegates ( ) ;
//Don't copy TickHandle as we want to setup our own here
SetupDelegates ( ) ;
}
return * this ;
}
2019-07-09 02:14:11 -04:00
void FBundlePrereqCombinedStatusHelper : : SetupDelegates ( )
{
2019-07-12 00:18:00 -04:00
CleanUpDelegates ( ) ;
2019-07-09 02:14:11 -04:00
IPlatformInstallBundleManager : : InstallBundleCompleteDelegate . AddRaw ( this , & FBundlePrereqCombinedStatusHelper : : OnBundleInstallComplete ) ;
2019-07-12 00:18:00 -04:00
TickHandle = FTicker : : GetCoreTicker ( ) . AddTicker ( FTickerDelegate : : CreateRaw ( this , & FBundlePrereqCombinedStatusHelper : : Tick ) ) ;
2019-07-09 02:14:11 -04:00
}
void FBundlePrereqCombinedStatusHelper : : CleanUpDelegates ( )
{
IPlatformInstallBundleManager : : InstallBundleCompleteDelegate . RemoveAll ( this ) ;
if ( TickHandle . IsValid ( ) )
{
FTicker : : GetCoreTicker ( ) . RemoveTicker ( TickHandle ) ;
TickHandle . Reset ( ) ;
}
}
void FBundlePrereqCombinedStatusHelper : : SetBundlesToTrackFromContentState ( FInstallBundleContentState & BundleContentState )
{
RequiredBundleNames . Empty ( ) ;
CachedBundleWeights . Empty ( ) ;
2019-07-10 16:52:53 -04:00
BundleStatusCache . Empty ( ) ;
2019-07-09 02:14:11 -04:00
//Track if we need any kind of bundle updates
2019-07-15 21:03:47 -04:00
bBundleNeedsUpdate = false ;
2019-07-09 02:14:11 -04:00
if ( BundleContentState . State = = EInstallBundleContentState : : NotInstalled | | BundleContentState . State = = EInstallBundleContentState : : NeedsUpdate )
{
bBundleNeedsUpdate = true ;
}
2019-07-15 21:03:47 -04:00
CurrentCombinedStatus . bBundleRequiresUpdate = bBundleNeedsUpdate ;
2019-07-09 02:14:11 -04:00
//Save required bundles and their weights
for ( TPair < FName , float > & BundleStatePair : BundleContentState . IndividualBundleWeights )
{
RequiredBundleNames . Add ( BundleStatePair . Key ) ;
2019-07-10 16:52:53 -04:00
CachedBundleWeights . FindOrAdd ( BundleStatePair . Key ) = BundleStatePair . Value ;
2019-07-09 02:14:11 -04:00
}
//Go ahead and calculate initial values from the Bundle Cache
UpdateBundleCache ( ) ;
}
void FBundlePrereqCombinedStatusHelper : : UpdateBundleCache ( )
{
//if we haven't already set this up, lets try to set it now
if ( nullptr = = InstallBundleManager )
{
InstallBundleManager = FPlatformMisc : : GetPlatformInstallBundleManager ( ) ;
}
if ( ensureAlwaysMsgf ( ( nullptr ! = InstallBundleManager ) , TEXT ( " Invalid InstallBundleManager during UpdateBundleCache! Needs to be valid during run! " ) ) )
{
for ( FName & BundleName : RequiredBundleNames )
{
TOptional < FInstallBundleStatus > BundleProgress = InstallBundleManager - > GetBundleProgress ( BundleName ) ;
//Copy progress to the cache as long as we have progress to copy.
if ( BundleProgress . IsSet ( ) )
{
FInstallBundleStatus & CachedStatus = BundleStatusCache . FindOrAdd ( BundleName ) ;
CachedStatus = BundleProgress . GetValue ( ) ;
}
}
}
}
void FBundlePrereqCombinedStatusHelper : : UpdateCombinedStatus ( )
{
CurrentCombinedStatus . ProgressPercent = GetCombinedProgressPercent ( ) ;
EInstallBundleStatus EarliestBundleState = EInstallBundleStatus : : Count ;
bool bIsAnythingPaused = false ;
2019-07-17 10:18:29 -04:00
bool bIsAnythingFinishing = false ;
2019-07-09 02:14:11 -04:00
2019-07-15 21:03:47 -04:00
//if we don't yet have a bundle status cache entry for a particular requirement
//then we can't yet tell what work is required on that bundle yet. We need to go ahead and make sure we don't
//show a status like "Installed" before we know what state that bundle is in. Make sure we show at LEAST
//updating in that case, so start with Downloading since that is the first Updating case
if ( ( BundleStatusCache . Num ( ) < RequiredBundleNames . Num ( ) )
& & ( BundleStatusCache . Num ( ) > 0 ) )
{
EarliestBundleState = EInstallBundleStatus : : Downloading ;
}
2019-07-17 10:18:29 -04:00
float EarliestFinishingPercent = 1.0f ;
2019-07-09 02:14:11 -04:00
for ( const TPair < FName , FInstallBundleStatus > & BundlePair : BundleStatusCache )
{
if ( BundlePair . Value . Status < EarliestBundleState )
{
EarliestBundleState = BundlePair . Value . Status ;
}
2019-07-17 10:18:29 -04:00
if ( ! bIsAnythingFinishing & & BundlePair . Value . Status = = EInstallBundleStatus : : Finishing )
{
EarliestFinishingPercent = BundlePair . Value . Finishing_Percent ;
bIsAnythingFinishing = true ;
}
2019-07-09 02:14:11 -04:00
2019-07-17 10:18:29 -04:00
bIsAnythingPaused = bIsAnythingPaused | | BundlePair . Value . PauseFlags ! = EInstallBundlePauseFlags : : None ;
2019-07-09 02:14:11 -04:00
}
//if we have any paused bundles, and we have any bundle that isn't finished installed, we are Paused
//if everything is installed ignore the pause flags as we completed after pausing the bundles
CurrentCombinedStatus . bIsPaused = ( bIsAnythingPaused & & ( EarliestBundleState < EInstallBundleStatus : : Installed ) ) ;
2019-07-15 21:03:47 -04:00
//if the bundle does not need an update, all the phases we go through don't support pausing (Mounting ,Compiling Shaders, etc)
//Otherwise start with True and override those specific cases bellow
CurrentCombinedStatus . bDoesCurrentStateSupportPausing = bBundleNeedsUpdate ;
2019-07-09 02:14:11 -04:00
2019-07-23 11:02:18 -04:00
if ( ( EarliestBundleState = = EInstallBundleStatus : : Requested ) | | ( EarliestBundleState = = EInstallBundleStatus : : Count ) )
2019-07-09 02:14:11 -04:00
{
CurrentCombinedStatus . CombinedState = FCombinedBundleStatus : : ECombinedBundleStateEnum : : Initializing ;
}
2019-07-17 10:18:29 -04:00
else if ( EarliestBundleState < = EInstallBundleStatus : : Installing )
2019-07-09 02:14:11 -04:00
{
CurrentCombinedStatus . CombinedState = FCombinedBundleStatus : : ECombinedBundleStateEnum : : Updating ;
}
2019-07-17 10:18:29 -04:00
else if ( EarliestBundleState < = EInstallBundleStatus : : Finishing )
2019-07-09 02:14:11 -04:00
{
2019-07-20 02:33:10 -04:00
//Handles the case where one of our Bundles was finishing and we have finished everything else.
//Now just shows our earliest bundle that is finishing.
2019-07-17 10:18:29 -04:00
if ( bIsAnythingFinishing )
{
CurrentCombinedStatus . CombinedState = FCombinedBundleStatus : : ECombinedBundleStateEnum : : Finishing ;
CurrentCombinedStatus . ProgressPercent = EarliestFinishingPercent ;
}
else
{
CurrentCombinedStatus . CombinedState = FCombinedBundleStatus : : ECombinedBundleStateEnum : : Updating ;
}
2019-07-09 02:14:11 -04:00
}
else if ( EarliestBundleState = = EInstallBundleStatus : : Installed )
{
CurrentCombinedStatus . CombinedState = FCombinedBundleStatus : : ECombinedBundleStateEnum : : Finished ;
CurrentCombinedStatus . bDoesCurrentStateSupportPausing = false ;
}
else
{
CurrentCombinedStatus . CombinedState = FCombinedBundleStatus : : ECombinedBundleStateEnum : : Unknown ;
}
}
float FBundlePrereqCombinedStatusHelper : : GetCombinedProgressPercent ( )
{
float AllBundleProgressPercent = 0.f ;
ensureAlwaysMsgf ( ( CachedBundleWeights . Num ( ) > = BundleStatusCache . Num ( ) ) , TEXT ( " Missing Cache Entries for BundleWeights!Any missing bundles will have 0 for their progress! " ) ) ;
for ( TPair < FName , FInstallBundleStatus > & BundleStatusPair : BundleStatusCache )
{
float * FoundWeight = CachedBundleWeights . Find ( BundleStatusPair . Key ) ;
if ( ensureAlwaysMsgf ( ( nullptr ! = FoundWeight ) , TEXT ( " Found missing entry for BundleWeight! Bundle %s does not have a weight entry! " ) , * ( BundleStatusPair . Key . ToString ( ) ) ) )
{
AllBundleProgressPercent + = ( ( * FoundWeight ) * GetIndividualWeightedProgressPercent ( BundleStatusPair . Value ) ) ;
}
}
FMath : : Clamp ( AllBundleProgressPercent , 0.f , 1.0f ) ;
return AllBundleProgressPercent ;
}
float FBundlePrereqCombinedStatusHelper : : GetIndividualWeightedProgressPercent ( FInstallBundleStatus & BundleStatus )
{
const float TotalWeight = DownloadWeight + InstallWeight ;
float CombinedOverallProgressPercent = 0.f ;
2019-07-20 02:33:10 -04:00
//If we are done installing then lets just coung our progress as 1.0f. Finishing progress is handled in UpdateCombinedStatus
if ( BundleStatus . Status > EInstallBundleStatus : : Installing )
2019-07-09 02:14:11 -04:00
{
CombinedOverallProgressPercent = 1.0f ;
}
else
{
if ( ensureAlwaysMsgf ( ( TotalWeight > 0 ) , TEXT ( " Invalid Weights for FBundleStatusTracker! Need to have weights > 0.f! " ) ) )
{
float DownloadWeightedPercent = 0.f ;
bool bDidHaveBackgroundDownloadProgress = false ;
if ( DownloadWeight > 0.f )
{
//Skip background downloads from contributing to progress if they aren't in use
bDidHaveBackgroundDownloadProgress = ( BundleStatus . BackgroundDownloadProgress . IsSet ( ) & & ( BundleStatus . BackgroundDownloadProgress - > BytesDownloaded > 0 ) ) ;
if ( bDidHaveBackgroundDownloadProgress )
{
const float AppliedDownloadWeight = DownloadWeight / TotalWeight ;
if ( BundleStatus . Status > EInstallBundleStatus : : Downloading )
{
//if we have moved past the download phase, just consider these
//downloads as having finished
DownloadWeightedPercent = 1.0f * AppliedDownloadWeight ;
}
else
{
DownloadWeightedPercent = BundleStatus . BackgroundDownloadProgress - > PercentComplete * AppliedDownloadWeight ;
}
}
}
float InstallWeightedPercent = 0.f ;
2019-08-06 11:42:45 -04:00
//If we didn't have background download progress, just use our InstallWeight as we don't have Download Progress to use
const float AppliedInstallWeight = bDidHaveBackgroundDownloadProgress ? ( InstallWeight / TotalWeight ) : 1.0f ;
InstallWeightedPercent = BundleStatus . Install_Percent * AppliedInstallWeight ;
2019-07-09 02:14:11 -04:00
CombinedOverallProgressPercent = DownloadWeightedPercent + InstallWeightedPercent ;
FMath : : Clamp ( CombinedOverallProgressPercent , 0.f , 1.0f ) ;
}
}
return CombinedOverallProgressPercent ;
}
bool FBundlePrereqCombinedStatusHelper : : Tick ( float dt )
{
UpdateBundleCache ( ) ;
UpdateCombinedStatus ( ) ;
//just always keep ticking
return true ;
}
const FBundlePrereqCombinedStatusHelper : : FCombinedBundleStatus & FBundlePrereqCombinedStatusHelper : : GetCurrentCombinedState ( ) const
{
return CurrentCombinedStatus ;
}
void FBundlePrereqCombinedStatusHelper : : OnBundleInstallComplete ( FInstallBundleResultInfo CompletedBundleInfo )
{
const FName CompletedBundleName = CompletedBundleInfo . BundleName ;
const bool bBundleCompletedSuccessfully = ( CompletedBundleInfo . Result = = EInstallBundleResult : : OK ) ;
2019-07-10 16:52:53 -04:00
const bool bWasRequiredBundle = RequiredBundleNames . Contains ( CompletedBundleInfo . BundleName ) ;
2019-07-09 02:14:11 -04:00
2019-07-10 16:52:53 -04:00
if ( bBundleCompletedSuccessfully & & bWasRequiredBundle )
2019-07-09 02:14:11 -04:00
{
//Make sure our BundleCache shows this as finished all the way
FInstallBundleStatus & BundleCacheInfo = BundleStatusCache . FindOrAdd ( CompletedBundleName ) ;
BundleCacheInfo . Status = EInstallBundleStatus : : Installed ;
TOptional < FInstallBundleStatus > BundleProgress = InstallBundleManager - > GetBundleProgress ( CompletedBundleName ) ;
if ( ensureAlwaysMsgf ( BundleProgress . IsSet ( ) , TEXT ( " Expected to find BundleProgress for completed bundle, but did not. Leaving old progress values " ) ) )
{
BundleCacheInfo = BundleProgress . GetValue ( ) ;
}
}
}