2025-01-08 05:30:09 +00:00
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
# include "Systems/OLSAssetManager.h"
2025-01-15 22:30:52 +00:00
2025-01-16 19:05:19 +00:00
# include "OLSLog.h"
2025-01-15 22:30:52 +00:00
# include "DataAssets/OLSGameDataAsset.h"
2025-01-08 05:30:09 +00:00
# include "Misc/App.h"
# include "Stats/StatsMisc.h"
# include "Misc/ScopedSlowTask.h"
# include "Engine/Engine.h"
# include "Systems/OLSAssetManagerStartupJob.h"
2025-01-13 22:36:08 +00:00
# include "DataAssets/OLSPawnDataAsset.h"
2025-01-08 05:30:09 +00:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(OLSAssetManager)
2025-01-16 19:05:19 +00:00
DEFINE_LOG_CATEGORY ( LogOLSAssetManager ) ;
2025-01-08 05:30:09 +00:00
const FName FOLSBundles : : Equipped ( " Equipped " ) ;
//////////////////////////////////////////////////////////////////////
static FAutoConsoleCommand CVarDumpLoadedAssets (
TEXT ( " OLS.DumpLoadedAssets " ) ,
TEXT ( " Shows all assets that were loaded via the asset manager and are currently in memory. " ) ,
FConsoleCommandDelegate : : CreateStatic ( UOLSAssetManager : : DumpLoadedAssets )
) ;
//////////////////////////////////////////////////////////////////////
# define STARTUP_JOB_WEIGHTED(jobFunc, jobWeight) StartupJobs.Add(FOLSAssetManagerStartupJob(#jobFunc, [this](const FOLSAssetManagerStartupJob& startupJob, TSharedPtr<FStreamableHandle>& loadHandle){jobFunc;}, jobWeight))
# define STARTUP_JOB(jobFunc) STARTUP_JOB_WEIGHTED(jobFunc, 1.f)
//////////////////////////////////////////////////////////////////////
UOLSAssetManager : : UOLSAssetManager ( )
{
DefaultPawnData = nullptr ;
}
UOLSAssetManager & UOLSAssetManager : : Get ( )
{
check ( GEngine ) ;
if ( UOLSAssetManager * Singleton = Cast < UOLSAssetManager > ( GEngine - > AssetManager ) )
{
return * Singleton ;
}
2025-01-16 19:05:19 +00:00
OLS_LOG_NO_WORLD ( LogOLSAssetManager , Fatal , TEXT ( " Invalid AssetManagerClassName in DefaultEngine.ini. It must be set to LyraAssetManager! " ) ) ;
2025-01-08 05:30:09 +00:00
// Fatal error above prevents this from being called.
return * NewObject < UOLSAssetManager > ( ) ;
}
2025-01-15 22:30:52 +00:00
void UOLSAssetManager : : DumpLoadedAssets ( )
2025-01-08 05:30:09 +00:00
{
2025-01-16 19:05:19 +00:00
OLS_LOG_NO_WORLD ( LogOLSAssetManager , Log , TEXT ( " ========== Start Dumping Loaded Assets ========== " ) ) ;
for ( const UObject * loadedAsset : Get ( ) . LoadedAssets )
{
OLS_LOG_NO_WORLD ( LogOLSAssetManager , Log , TEXT ( " %s " ) , GET_UOBJECT_NAME ( loadedAsset ) ) ;
}
OLS_LOG_NO_WORLD ( LogOLSAssetManager , Log , TEXT ( " ... %d assets in loaded pool " ) , Get ( ) . LoadedAssets . Num ( ) ) ;
OLS_LOG_NO_WORLD ( LogOLSAssetManager , Log , TEXT ( " ========== Finish Dumping Loaded Assets ========== " ) ) ;
2025-01-08 05:30:09 +00:00
}
2025-01-15 22:30:52 +00:00
const UOLSGameDataAsset & UOLSAssetManager : : GetGameData ( )
{
return GetOrLoadTypedGameData < UOLSGameDataAsset > ( OLSGameDataPath ) ;
2025-01-08 05:30:09 +00:00
}
2025-01-13 22:36:08 +00:00
const UOLSPawnDataAsset * UOLSAssetManager : : GetDefaultPawnData ( ) const
2025-01-08 05:30:09 +00:00
{
return GetAsset ( DefaultPawnData ) ;
}
template < typename GameDataClass >
const GameDataClass & UOLSAssetManager : : GetOrLoadTypedGameData ( const TSoftObjectPtr < GameDataClass > & dataPath )
{
if ( const TObjectPtr < UPrimaryDataAsset > * pResult = GameDataMap . Find ( GameDataClass : : StaticClass ( ) ) )
{
return * CastChecked < GameDataClass > ( * pResult ) ;
}
// Does a blocking load if needed
return * CastChecked < const GameDataClass > (
LoadGameDataOfClass ( GameDataClass : : StaticClass ( ) , dataPath , GameDataClass : : StaticClass ( ) - > GetFName ( ) ) ) ;
}
UObject * UOLSAssetManager : : SynchronousLoadAsset ( const FSoftObjectPath & assetPath )
{
if ( assetPath . IsValid ( ) )
{
TUniquePtr < FScopeLogTime > logTimePtr ;
if ( ShouldLogAssetLoads ( ) )
{
logTimePtr = MakeUnique < FScopeLogTime > ( * FString : : Printf ( TEXT ( " Synchronously loaded asset [%s] " ) , * assetPath . ToString ( ) ) , nullptr , FScopeLogTime : : ScopeLog_Seconds ) ;
}
if ( UAssetManager : : IsInitialized ( ) )
{
return UAssetManager : : GetStreamableManager ( ) . LoadSynchronous ( assetPath , false ) ;
}
// Use LoadObject if asset manager isn't ready yet.
return assetPath . TryLoad ( ) ;
}
return nullptr ;
}
bool UOLSAssetManager : : ShouldLogAssetLoads ( )
{
static bool shouldLogAssetLoads = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " LogAssetLoads " ) ) ;
return shouldLogAssetLoads ;
}
void UOLSAssetManager : : AddLoadedAsset ( const UObject * Asset )
{
if ( ensureAlways ( Asset ) )
{
FScopeLock LoadedAssetsLock ( & LoadedAssetsCritical ) ;
LoadedAssets . Add ( Asset ) ;
}
}
void UOLSAssetManager : : StartInitialLoading ( )
{
SCOPED_BOOT_TIMING ( " UOLSAssetManager::StartInitialLoading " ) ;
// This does all of the scanning, need to do this now even if loads are deferred
Super : : StartInitialLoading ( ) ;
STARTUP_JOB ( InitializeGameplayCueManager ( ) ) ;
{
// Load base game data asset
2025-01-16 19:05:19 +00:00
STARTUP_JOB_WEIGHTED ( GetGameData ( ) , 25.f ) ;
2025-01-08 05:30:09 +00:00
}
// Run all the queued up startup jobs
DoAllStartupJobs ( ) ;
}
# if WITH_EDITOR
void UOLSAssetManager : : PreBeginPIE ( bool shouldStartSimulate )
{
Super : : PreBeginPIE ( shouldStartSimulate ) ;
{
FScopedSlowTask slowTask ( 0 , NSLOCTEXT ( " OLSEditor " , " BeginLoadingPIEData " , " Loading PIE Data " ) ) ;
const bool shouldShowCancelButton = false ;
const bool shouldAllowInPIE = true ;
slowTask . MakeDialog ( shouldShowCancelButton , shouldAllowInPIE ) ;
2025-01-16 19:05:19 +00:00
const UOLSGameDataAsset & LocalGameDataCommon = GetGameData ( ) ;
2025-01-08 05:30:09 +00:00
// Intentionally after GetGameData to avoid counting GameData time in this timer
SCOPE_LOG_TIME_IN_SECONDS ( TEXT ( " PreBeginPIE asset preloading complete " ) , nullptr ) ;
// You could add preloading of anything else needed for the experience we'll be using here
// (e.g., by grabbing the default experience from the world settings + the experience override in developer settings)
}
}
# endif
UPrimaryDataAsset * UOLSAssetManager : : LoadGameDataOfClass (
TSubclassOf < UPrimaryDataAsset > dataClass ,
const TSoftObjectPtr < UPrimaryDataAsset > & dataClassPath ,
FPrimaryAssetType primaryAssetType )
{
UPrimaryDataAsset * asset = nullptr ;
DECLARE_SCOPE_CYCLE_COUNTER ( TEXT ( " Loading GameData Object " ) , STAT_GameData , STATGROUP_LoadTime ) ;
if ( ! dataClassPath . IsNull ( ) )
{
# if WITH_EDITOR
FScopedSlowTask slowTask ( 0 , FText : : Format ( NSLOCTEXT ( " OLSEditor " , " BeginLoadingGameDataTask " , " Loading GameData {0} " ) , FText : : FromName ( dataClass - > GetFName ( ) ) ) ) ;
const bool shouldShowCancelButton = false ;
const bool shouldAllowInPIE = true ;
slowTask . MakeDialog ( shouldShowCancelButton , shouldAllowInPIE ) ;
# endif
2025-01-16 19:05:19 +00:00
OLS_LOG ( LogOLSAssetManager , Log , TEXT ( " Loading GameData: %%s ... " ) , * dataClassPath . ToString ( ) ) ;
2025-01-08 05:30:09 +00:00
SCOPE_LOG_TIME_IN_SECONDS ( TEXT ( " ... GameData loaded! " ) , nullptr ) ;
// This can be called recursively in the editor because it is called on demand from PostLoad so force a sync load for primary asset and async load the rest in that case
if ( GIsEditor )
{
asset = dataClassPath . LoadSynchronous ( ) ;
LoadPrimaryAssetsWithType ( primaryAssetType ) ;
}
else
{
TSharedPtr < FStreamableHandle > handle = LoadPrimaryAssetsWithType ( primaryAssetType ) ;
if ( handle . IsValid ( ) )
{
handle - > WaitUntilComplete ( 0.0f , false ) ;
// This should always work
asset = Cast < UPrimaryDataAsset > ( handle - > GetLoadedAsset ( ) ) ;
}
}
}
if ( asset )
{
GameDataMap . Add ( dataClass , asset ) ;
}
else
{
// It is not acceptable to fail to load any GameData asset. It will result in soft failures that are hard to diagnose.
2025-01-16 19:05:19 +00:00
OLS_LOG ( LogOLSAssetManager , Fatal ,
TEXT (
" Failed to load GameData asset at %s. Type %s. This is not recoverable and likely means you do not have the correct data to run %s. "
) , * dataClassPath . ToString ( ) , * primaryAssetType . ToString ( ) , FApp : : GetProjectName ( ) ) ;
2025-01-08 05:30:09 +00:00
}
return asset ;
}
void UOLSAssetManager : : DoAllStartupJobs ( )
{
2025-01-16 19:05:19 +00:00
SCOPED_BOOT_TIMING ( " UOLSAssetManager::DoAllStartupJobs " ) ;
const double allStartupJobsStartTime = FPlatformTime : : Seconds ( ) ;
if ( IsRunningDedicatedServer ( ) )
{
// No need for periodic progress updates, just run the jobs
for ( const FOLSAssetManagerStartupJob & startupJob : StartupJobs )
{
startupJob . DoJob ( ) ;
}
}
else
{
if ( StartupJobs . Num ( ) > 0 )
{
float totalJobValue = 0.0f ;
for ( const FOLSAssetManagerStartupJob & startupJob : StartupJobs )
{
totalJobValue + = startupJob . JobWeight ;
}
float accumulatedJobValue = 0.0f ;
for ( FOLSAssetManagerStartupJob & startupJob : StartupJobs )
{
const float jobValue = startupJob . JobWeight ;
startupJob . SubstepProgressDelegate . BindLambda ( [ This = this , accumulatedJobValue , jobValue , totalJobValue ] ( float NewProgress )
{
const float SubstepAdjustment = FMath : : Clamp ( NewProgress , 0.0f , 1.0f ) * jobValue ;
const float OverallPercentWithSubstep = ( accumulatedJobValue + SubstepAdjustment ) / totalJobValue ;
This - > UpdateInitialGameContentLoadPercent ( OverallPercentWithSubstep ) ;
} ) ;
startupJob . DoJob ( ) ;
startupJob . SubstepProgressDelegate . Unbind ( ) ;
accumulatedJobValue + = jobValue ;
UpdateInitialGameContentLoadPercent ( accumulatedJobValue / totalJobValue ) ;
}
}
else
{
UpdateInitialGameContentLoadPercent ( 1.0f ) ;
}
}
StartupJobs . Empty ( ) ;
OLS_LOG ( LogOLSAssetManager , Display , TEXT ( " All startup jobs took %.2f seconds to complete " ) ,
FPlatformTime : : Seconds ( ) - allStartupJobsStartTime ) ;
2025-01-08 05:30:09 +00:00
}
void UOLSAssetManager : : InitializeGameplayCueManager ( )
{
SCOPED_BOOT_TIMING ( " UOLSAssetManager::InitializeGameplayCueManager " ) ;
2025-01-16 19:05:19 +00:00
// @TODO: Implement this after implementing ULyraGameplayCueManager.
2025-01-08 05:30:09 +00:00
// ULyraGameplayCueManager* GCM = ULyraGameplayCueManager::Get();
// check(GCM);
// GCM->LoadAlwaysLoadedCues();
}
void UOLSAssetManager : : UpdateInitialGameContentLoadPercent ( float gameContentPercent )
{
2025-01-16 20:54:02 +00:00
// Lyra left this comment.
// Could route this to the early startup loading screen.
2025-01-08 05:30:09 +00:00
}