672 lines
22 KiB
C++
672 lines
22 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "InstallBundleSourceBulk.h"
|
|
|
|
#include "Algo/AnyOf.h"
|
|
#include "DefaultInstallBundleManagerPrivate.h"
|
|
#include "HAL/PlatformFileManager.h"
|
|
#include "HAL/PlatformFile.h"
|
|
#include "IO/IoStoreOnDemand.h"
|
|
#include "IPlatformFilePak.h"
|
|
#include "InstallBundleManagerUtil.h"
|
|
#include "Misc/CommandLine.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Internationalization/Regex.h"
|
|
#include "Misc/App.h"
|
|
|
|
#define LOG_SOURCE_BULK(Verbosity, Format, ...) LOG_INSTALL_BUNDLE_MAN(Verbosity, TEXT("InstallBundleSourceBulk: ") Format, ##__VA_ARGS__)
|
|
|
|
#define LOG_SOURCE_BULK_OVERRIDE(VerbosityOverride, Verbosity, Format, ...) LOG_INSTALL_BUNDLE_MAN_OVERRIDE(VerbosityOverride, Verbosity, TEXT("InstallBundleSourceBulk: ") Format, ##__VA_ARGS__)
|
|
|
|
//Helper class used to load/save BulkBuildBundle information from/to disk
|
|
class FBulkBuildBundleMapJsonInfo
|
|
: public FJsonSerializable
|
|
{
|
|
public:
|
|
|
|
//Simple Wrapper to hold bundle name in a way we can serialize it to/from a map with JSON_SERIALIZE_MAP_SERIALIZABLE
|
|
class FJsonBundleNameWrapper
|
|
: public FJsonSerializable
|
|
{
|
|
public:
|
|
BEGIN_JSON_SERIALIZER
|
|
JSON_SERIALIZE("BundleName", BundleName);
|
|
END_JSON_SERIALIZER
|
|
|
|
FString BundleName;
|
|
|
|
FJsonBundleNameWrapper()
|
|
: BundleName()
|
|
{}
|
|
};
|
|
|
|
BEGIN_JSON_SERIALIZER
|
|
JSON_SERIALIZE_MAP_SERIALIZABLE("BulkBuildBundleByFileMap", BulkBuildBundleByFileMap, FJsonBundleNameWrapper);
|
|
END_JSON_SERIALIZER
|
|
|
|
bool LoadFromFile(FStringView FilePath)
|
|
{
|
|
FString JSONStringOnDisk;
|
|
if (FPaths::FileExists(FilePath.GetData()))
|
|
{
|
|
FFileHelper::LoadFileToString(JSONStringOnDisk, FilePath.GetData());
|
|
}
|
|
|
|
if (!JSONStringOnDisk.IsEmpty())
|
|
{
|
|
return ensureAlwaysMsgf(
|
|
FromJson(JSONStringOnDisk),
|
|
TEXT("Invalid JSON found while parsing BulkBuildBundleMapInfo from JSON: %s loaded from file:%.*s"),
|
|
*JSONStringOnDisk,
|
|
FilePath.Len(), FilePath.GetData());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SaveToFile(FStringView FilePath)
|
|
{
|
|
return ensureAlwaysMsgf(
|
|
FFileHelper::SaveStringToFile(ToJson(), FilePath.GetData()),
|
|
TEXT("Error saving Json output of FBulkBuildBundleMapInfo to %.*s"),
|
|
FilePath.Len(),FilePath.GetData());
|
|
}
|
|
|
|
//Takes in a list of files found on disk and uses our parsed BulkBuildBundleByFileMap to fill out the OutBulkBuildBundles Map.
|
|
//Removes all found entries from the OutBundleFileList that were successfully processed.
|
|
bool AppendEntriesToBulkBuildBundleMap(TArray<FString>& InOutBundleFileList, TMap<FName, TArray<FString>>& InOutBulkBuildBundles)
|
|
{
|
|
int32 OriginalBulkBuildBundleNum = InOutBulkBuildBundles.Num();
|
|
|
|
if (InOutBundleFileList.Num() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int FileIndex = 0; FileIndex < InOutBundleFileList.Num();)
|
|
{
|
|
FString& FileInList = InOutBundleFileList[FileIndex];
|
|
FJsonBundleNameWrapper* FoundBundleNameString = BulkBuildBundleByFileMap.Find(FileInList);
|
|
if (FoundBundleNameString)
|
|
{
|
|
FName BundleName(*(FoundBundleNameString->BundleName));
|
|
TArray<FString>& FoundFileList = InOutBulkBuildBundles.FindOrAdd(BundleName);
|
|
FoundFileList.AddUnique(FileInList);
|
|
|
|
//Remove and don't increment FileIndex so that we re-check this swapped index next pass if valid
|
|
InOutBundleFileList.RemoveAtSwap(FileIndex);
|
|
}
|
|
else
|
|
{
|
|
++FileIndex;
|
|
}
|
|
}
|
|
|
|
//return true if we've added values to OutBulkBuildBundles
|
|
return (InOutBulkBuildBundles.Num() > OriginalBulkBuildBundleNum);
|
|
}
|
|
|
|
bool IsEmpty()
|
|
{
|
|
return (BulkBuildBundleByFileMap.Num() == 0);
|
|
}
|
|
|
|
//Gets the path to use for the BulkBuildInfo file if its present in the cooked data on device
|
|
static FStringView GetBulkBuildBundleInfoCookedPath()
|
|
{
|
|
static FString CookedPath = FPaths::Combine(FPaths::ProjectContentDir(), TEXT("BulkBuildMeta"), TEXT("CachedBuilkBuildBuildInfo.json"));
|
|
return CookedPath;
|
|
}
|
|
|
|
//Gets the path to use for the BulkBuildInfo file if its cached locally on the device
|
|
static FStringView GetBulkBuildBundleInfoLocalCachedPath()
|
|
{
|
|
static FString LocalCachedPath = FPaths::Combine(FPaths::ProjectUserDir(), TEXT("Saved"), TEXT("BulkBuildMeta"), TEXT("CachedBuilkBuildBuildInfo.json"));
|
|
return LocalCachedPath;
|
|
}
|
|
|
|
//Constructor to generate BulkBuildBundleByFileMap from BulkBuildBundlesIn, useful for loading the BulkBuildBundle info from
|
|
//memory and then saving it out to file
|
|
FBulkBuildBundleMapJsonInfo(const TMap<FName, TArray<FString>>& BulkBuildBundlesIn)
|
|
: FBulkBuildBundleMapJsonInfo()
|
|
{
|
|
for(const TPair<FName, TArray<FString>>& BundlePair : BulkBuildBundlesIn)
|
|
{
|
|
for (const FString& FileName : BundlePair.Value)
|
|
{
|
|
FJsonBundleNameWrapper& FoundBundleName = BulkBuildBundleByFileMap.FindOrAdd(FileName);
|
|
FoundBundleName.BundleName = BundlePair.Key.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
FBulkBuildBundleMapJsonInfo()
|
|
: BulkBuildBundleByFileMap()
|
|
{};
|
|
|
|
private:
|
|
//Serialized BulkBuildBundle (FileName)->(Bundle FString version of FName) information
|
|
TMap<FString, FJsonBundleNameWrapper> BulkBuildBundleByFileMap;
|
|
};
|
|
|
|
FInstallBundleSourceBulk::FInstallBundleSourceBulk()
|
|
{
|
|
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FInstallBundleSourceBulk::Tick));
|
|
}
|
|
|
|
FInstallBundleSourceBulk::~FInstallBundleSourceBulk()
|
|
{
|
|
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
|
TickHandle.Reset();
|
|
|
|
InstallBundleUtil::CleanupInstallBundleAsyncIOTasks(InitAsyncTasks);
|
|
}
|
|
|
|
bool FInstallBundleSourceBulk::Tick(float dt)
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_FInstallBundleSourceBulk_Tick);
|
|
|
|
TickInit();
|
|
|
|
// Check for Init Task Completion
|
|
InstallBundleUtil::FinishInstallBundleAsyncIOTasks(InitAsyncTasks);
|
|
|
|
return true;
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::TickInit()
|
|
{
|
|
if (!OnInitCompleteCallback.IsBound())
|
|
return;
|
|
|
|
while (InitState == EInstallBundleManagerInitState::NotInitialized && InitStepResult == EAsyncInitStepResult::Done)
|
|
{
|
|
if (InitResult != EInstallBundleManagerInitResult::OK)
|
|
{
|
|
if (LastInitStep != InitStep)
|
|
{
|
|
// Only fire init analytic for failures the first time we retry
|
|
AsyncInit_FireInitAnlaytic();
|
|
LastInitStep = InitStep;
|
|
}
|
|
|
|
if(bRetryInit)
|
|
{
|
|
LOG_SOURCE_BULK(Warning, TEXT("Retrying initialization after %s"), LexToString(InitResult));
|
|
InitResult = EInstallBundleManagerInitResult::OK;
|
|
bRetryInit = false;
|
|
}
|
|
else
|
|
{
|
|
LOG_SOURCE_BULK(Warning, TEXT("Initialization Failed - %s"), LexToString(InitResult));
|
|
InitState = EInstallBundleManagerInitState::Failed;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LastInitStep = InitStep;
|
|
++InstallBundleUtil::CastAsUnderlying(InitStep);
|
|
}
|
|
|
|
switch (InitStep)
|
|
{
|
|
case EAsyncInitStep::None:
|
|
LOG_SOURCE_BULK(Fatal, TEXT("Trying to use init state None"));
|
|
break;
|
|
case EAsyncInitStep::MakeBundlesForBulkBuild:
|
|
AsyncInit_MakeBundlesForBulkBuild();
|
|
break;
|
|
case EAsyncInitStep::Finishing:
|
|
AsyncInit_FireInitAnlaytic();
|
|
InitState = EInstallBundleManagerInitState::Succeeded;
|
|
break;
|
|
default:
|
|
LOG_SOURCE_BULK(Fatal, TEXT("Unknown Init Step %s"), LexToString(InitStep));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (InitState == EInstallBundleManagerInitState::Succeeded || InitState == EInstallBundleManagerInitState::Failed)
|
|
{
|
|
FInstallBundleSourceAsyncInitInfo InitInfo;
|
|
InitInfo.Result = InitResult;
|
|
InitInfo.bShouldUseFallbackSource = false;
|
|
|
|
OnInitCompleteCallback.Execute(AsShared(), MoveTemp(InitInfo));
|
|
OnInitCompleteCallback = nullptr;
|
|
}
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::AsyncInit_FireInitAnlaytic()
|
|
{
|
|
LOG_SOURCE_BULK(Display, TEXT("Fire Init Analytic: %s"), LexToString(InitResult));
|
|
|
|
InstallBundleManagerAnalytics::FireEvent_InitBundleSourceBulkComplete(AnalyticsProvider.Get(), LexToString(InitResult));
|
|
|
|
InitStepResult = EAsyncInitStepResult::Done;
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::AsyncInit_MakeBundlesForBulkBuild()
|
|
{
|
|
LOG_SOURCE_BULK(Display, TEXT("Making Bundles for Bulk Build"));
|
|
|
|
InitStepResult = EAsyncInitStepResult::Waiting;
|
|
|
|
TArray<FString> PakSearchDirs;
|
|
FPakPlatformFile::GetPakFolders(FCommandLine::Get(), PakSearchDirs);
|
|
|
|
//Get setting for if we limit our file list to only .pak files
|
|
bool bOnlyGatherPaksInBulkData = false;
|
|
if (!GConfig->GetBool(TEXT("InstallBundleSource.Bulk.MiscSettings"), TEXT("bOnlyGatherPaksInBulkData"), bOnlyGatherPaksInBulkData, GInstallBundleIni))
|
|
{
|
|
bOnlyGatherPaksInBulkData = false;
|
|
}
|
|
|
|
TSharedPtr<TArray<FString>, ESPMode::ThreadSafe> FoundFiles = MakeShared<TArray<FString>, ESPMode::ThreadSafe>();
|
|
InstallBundleUtil::StartInstallBundleAsyncIOTask(InitAsyncTasks,
|
|
[FoundFiles, PakSearchDirs=MoveTemp(PakSearchDirs), ContentDir=FPaths::ProjectContentDir(), bOnlyGatherPaksInBulkData]()
|
|
{
|
|
// Custom visitor because we need to deal with multiple extensions
|
|
class FFindBulkFilesVisitor : public IPlatformFile::FDirectoryVisitor
|
|
{
|
|
public:
|
|
FRWLock FoundFilesLock;
|
|
TArray<FString>& FoundFiles;
|
|
bool bOnlyGatherPaksInBulkData;
|
|
FFindBulkFilesVisitor(TArray<FString>& InFoundFiles, bool bInOnlyGatherPaksInBulkData)
|
|
: IPlatformFile::FDirectoryVisitor(EDirectoryVisitorFlags::ThreadSafe)
|
|
, FoundFiles(InFoundFiles)
|
|
, bOnlyGatherPaksInBulkData(bInOnlyGatherPaksInBulkData)
|
|
{}
|
|
|
|
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
|
|
{
|
|
const FStringView FileExtensions[] = { TEXTVIEW(".pak"), TEXTVIEW(".uondemandtoc") };
|
|
|
|
if (!bIsDirectory)
|
|
{
|
|
bool bFound = !bOnlyGatherPaksInBulkData;
|
|
if (!bFound)
|
|
{
|
|
FStringView PathExtension = FPathViews::GetExtension(FilenameOrDirectory, true);
|
|
for (FStringView Extension : FileExtensions)
|
|
{
|
|
if (PathExtension == Extension)
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
FString FileName(FilenameOrDirectory);
|
|
FRWScopeLock ScopeLock(FoundFilesLock, SLT_Write);
|
|
FoundFiles.Emplace(MoveTemp(FileName));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
FFindBulkFilesVisitor Visitor(*FoundFiles, bOnlyGatherPaksInBulkData);
|
|
for (const FString& SearchDir : PakSearchDirs)
|
|
{
|
|
FPlatformFileManager::Get().GetPlatformPhysical().IterateDirectoryRecursively(*SearchDir, Visitor);
|
|
}
|
|
},
|
|
[this, FoundFiles]()
|
|
{
|
|
//First load any existing BulkBuildBundleIni entries and use those to sort FoundFiles into bundles
|
|
const bool bHasAnyFilesToParse = FoundFiles.IsValid() && (FoundFiles->Num() > 0);
|
|
bool bDidLoadAllFilesFromMetadata = bHasAnyFilesToParse ? TryLoadBulkBuildBundleMetadata(*FoundFiles, BulkBuildBundles) : false;
|
|
|
|
// Prune out any remaining paks that were mounted at startup
|
|
for (int i = 0; i < FoundFiles->Num();)
|
|
{
|
|
const FString& File = (*FoundFiles)[i];
|
|
if (File.MatchesWildcard(FPakPlatformFile::GetMountStartupPaksWildCard()))
|
|
{
|
|
FoundFiles->RemoveAtSwap(i);
|
|
}
|
|
else
|
|
{
|
|
++i;
|
|
}
|
|
}
|
|
|
|
//Still files left that weren't in the Metadata or StartupPaksWildcard, so we have files that will be manually parsed
|
|
if (FoundFiles->Num() > 0)
|
|
{
|
|
bDidLoadAllFilesFromMetadata = false;
|
|
}
|
|
|
|
LOG_SOURCE_BULK(Display, TEXT("Loaded %d Bundle Information from BulkBuildBundles Cache. %d Files Remaining."), BulkBuildBundles.Num(), FoundFiles->Num());
|
|
|
|
TArray<FString> SectionNames;
|
|
const FConfigFile* InstallBundleConfig = GConfig->FindConfigFile(GInstallBundleIni);
|
|
if (InstallBundleConfig)
|
|
{
|
|
for (const TPair<FString, FConfigSection>& Pair : *InstallBundleConfig)
|
|
{
|
|
if (Pair.Key.StartsWith(InstallBundleUtil::GetInstallBundleSectionPrefix()))
|
|
{
|
|
SectionNames.Add(Pair.Key);
|
|
}
|
|
}
|
|
|
|
//Skip sorting SectionNames if we have already loaded everything as we will not be applying any regex anyway
|
|
if (!bDidLoadAllFilesFromMetadata)
|
|
{
|
|
// Bundle regex need to be applied in order
|
|
SectionNames.StableSort([InstallBundleConfig](const FString& SectionA, const FString& SectionB) -> bool
|
|
{
|
|
int32 BundleAOrder = INT_MAX;
|
|
int32 BundleBOrder = INT_MAX;
|
|
|
|
if (!InstallBundleConfig->GetInt(*SectionA, TEXT("Order"), BundleAOrder))
|
|
{
|
|
LOG_SOURCE_BULK(Warning, TEXT("Bundle Section %s doesn't have an order"), *SectionA);
|
|
}
|
|
|
|
if (!InstallBundleConfig->GetInt(*SectionB, TEXT("Order"), BundleBOrder))
|
|
{
|
|
LOG_SOURCE_BULK(Warning, TEXT("Bundle Section %s doesn't have an order"), *SectionB);
|
|
}
|
|
|
|
return BundleAOrder < BundleBOrder;
|
|
});
|
|
}
|
|
|
|
//Ensure all known sections are appended to the BulkBuildBundles Map even if they have no files
|
|
for (const FString& Section : SectionNames)
|
|
{
|
|
const FString BundleName = Section.RightChop(InstallBundleUtil::GetInstallBundleSectionPrefix().Len());
|
|
TArray<FString>& BundleFileList = BulkBuildBundles.FindOrAdd(FName(*BundleName));
|
|
}
|
|
|
|
//If we had files remaining to manually parse, now sort the remaining files into bundles
|
|
if (!bDidLoadAllFilesFromMetadata)
|
|
{
|
|
for (const FString& Section : SectionNames)
|
|
{
|
|
const FString BundleName = Section.RightChop(InstallBundleUtil::GetInstallBundleSectionPrefix().Len());
|
|
TArray<FString>& BundleFileList = BulkBuildBundles.FindOrAdd(FName(*BundleName));
|
|
|
|
TArray<FString> StrSearchRegexPatterns;
|
|
if (!InstallBundleConfig->GetArray(*Section, TEXT("FileRegex"), StrSearchRegexPatterns))
|
|
continue;
|
|
|
|
TArray<FRegexPattern> SearchRegexPatterns;
|
|
SearchRegexPatterns.Reserve(StrSearchRegexPatterns.Num());
|
|
for (const FString& Str : StrSearchRegexPatterns)
|
|
{
|
|
SearchRegexPatterns.Emplace(Str, ERegexPatternFlags::CaseInsensitive);
|
|
}
|
|
|
|
for (int i = 0; i < FoundFiles->Num();)
|
|
{
|
|
const FString& File = (*FoundFiles)[i];
|
|
bool bMatches = false;
|
|
for (const FRegexPattern& Pattern : SearchRegexPatterns)
|
|
{
|
|
if (FRegexMatcher(Pattern, File).FindNext())
|
|
{
|
|
bMatches = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bMatches)
|
|
{
|
|
LOG_SOURCE_BULK(Verbose, TEXT("Adding %s to Bundle %s"), *File, *BundleName);
|
|
|
|
BundleFileList.AddUnique(File);
|
|
FoundFiles->RemoveAtSwap(i);
|
|
}
|
|
else
|
|
{
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//See if we should serialize out the manually parsed results
|
|
if (!bDidLoadAllFilesFromMetadata)
|
|
{
|
|
bool bShouldSerializeMissingBulkBuildDataIni = false;
|
|
if (!GConfig->GetBool(TEXT("InstallBundleSource.Bulk.MiscSettings"), TEXT("bShouldSerializeMissingBulkBuildDataIni"), bShouldSerializeMissingBulkBuildDataIni, GInstallBundleIni))
|
|
{
|
|
bShouldSerializeMissingBulkBuildDataIni = false;
|
|
}
|
|
|
|
if (bShouldSerializeMissingBulkBuildDataIni)
|
|
{
|
|
SerializeBulkBuildBundleMetadata(BulkBuildBundles);
|
|
}
|
|
}
|
|
|
|
LOG_SOURCE_BULK(Display, TEXT("Finished Making Bundles for Bulk Build"));
|
|
InitStepResult = EAsyncInitStepResult::Done;
|
|
});
|
|
}
|
|
|
|
bool FInstallBundleSourceBulk::TryLoadBulkBuildBundleMetadata(TArray<FString>& InOutFileList, TMap<FName, TArray<FString>>& InOutBulkBuildBundles)
|
|
{
|
|
FBulkBuildBundleMapJsonInfo LoadedBuildInfo;
|
|
|
|
//Always prioritize loading from the LocalCache
|
|
if (!LoadedBuildInfo.LoadFromFile(FBulkBuildBundleMapJsonInfo::GetBulkBuildBundleInfoLocalCachedPath()))
|
|
{
|
|
//If there is no local cache look for a cooked one
|
|
LoadedBuildInfo.LoadFromFile(FBulkBuildBundleMapJsonInfo::GetBulkBuildBundleInfoCookedPath());
|
|
}
|
|
|
|
if (!LoadedBuildInfo.IsEmpty())
|
|
{
|
|
return LoadedBuildInfo.AppendEntriesToBulkBuildBundleMap(InOutFileList, InOutBulkBuildBundles);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::SerializeBulkBuildBundleMetadata(const TMap<FName, TArray<FString>>& BulkBuildBundles)
|
|
{
|
|
FBulkBuildBundleMapJsonInfo JsonBulkBuildInfo(BulkBuildBundles);
|
|
if (!JsonBulkBuildInfo.IsEmpty())
|
|
{
|
|
FStringView FilePath = FBulkBuildBundleMapJsonInfo::GetBulkBuildBundleInfoLocalCachedPath();
|
|
const bool bSuccess = JsonBulkBuildInfo.SaveToFile(FilePath);
|
|
LOG_SOURCE_BULK(Display, TEXT("Saving BulkBuildBundle Cache to %.*s . bSuccess:%s"), FilePath.Len(), FilePath.GetData(), *LexToString(bSuccess));
|
|
}
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::GetOnDemandHostGroup(UE::IoStore::FOnDemandHostGroup& OutHostGroup)
|
|
{
|
|
ensureAlwaysMsgf(false, TEXT("FInstallBundleSourceBulk::GetOnDemandHostGroup not implemented!"));
|
|
}
|
|
|
|
FString FInstallBundleSourceBulk::GetOnDemandTocRelativeURL()
|
|
{
|
|
ensureAlwaysMsgf(false, TEXT("FInstallBundleSourceBulk::GetOnDemandTocRelativeURL not implemented!"));
|
|
return {};
|
|
}
|
|
|
|
EInstallBundleInstallState FInstallBundleSourceBulk::GetBundleInstallState(FName BundleName)
|
|
{
|
|
return EInstallBundleInstallState::UpToDate;
|
|
}
|
|
|
|
FInstallBundleSourceType FInstallBundleSourceBulk::GetSourceType() const
|
|
{
|
|
return FInstallBundleSourceType(TEXT("Bulk"));
|
|
}
|
|
|
|
FInstallBundleSourceInitInfo FInstallBundleSourceBulk::Init(
|
|
TSharedRef<InstallBundleUtil::FContentRequestStatsMap> InRequestStats,
|
|
TSharedPtr<IAnalyticsProviderET> InAnalyticsProvider,
|
|
TSharedPtr<InstallBundleUtil::PersistentStats::FPersistentStatContainerBase> PersistentStatsContainer)
|
|
{
|
|
AnalyticsProvider = MoveTemp(InAnalyticsProvider);
|
|
|
|
//Ignoring PersistentStatsContainer as we currently don't care about any stats for this bulk source
|
|
|
|
FInstallBundleSourceInitInfo InitInfo;
|
|
return InitInfo;
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::AsyncInit(FInstallBundleSourceInitDelegate Callback)
|
|
{
|
|
check(OnInitCompleteCallback.IsBound() == false);
|
|
OnInitCompleteCallback = MoveTemp(Callback);
|
|
|
|
if (InitState == EInstallBundleManagerInitState::Failed)
|
|
{
|
|
InitState = EInstallBundleManagerInitState::NotInitialized;
|
|
bRetryInit = true;
|
|
}
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::AsyncInit_QueryBundleInfo(FInstallBundleSourceQueryBundleInfoDelegate Callback)
|
|
{
|
|
check(InitState == EInstallBundleManagerInitState::Succeeded);
|
|
|
|
FInstallBundleSourceBundleInfoQueryResult ResultInfo;
|
|
|
|
const FConfigFile* InstallBundleConfig = GConfig->FindConfigFile(GInstallBundleIni);
|
|
if (InstallBundleConfig)
|
|
{
|
|
for (const TPair<FString, FConfigSection>& Pair : *InstallBundleConfig)
|
|
{
|
|
const FString& Section = Pair.Key;
|
|
FInstallBundleSourcePersistentBundleInfo BundleInfo;
|
|
if(!InstallBundleManagerUtil::LoadBundleSourceBundleInfoFromConfig(GetSourceType(), *InstallBundleConfig, Section, BundleInfo))
|
|
continue;
|
|
|
|
BundleInfo.BundleContentState = GetBundleInstallState(BundleInfo.BundleName);
|
|
|
|
if (TArray<FString>* FileList = BulkBuildBundles.Find(BundleInfo.BundleName))
|
|
{
|
|
BundleInfo.bContainsIoStoreOnDemandToc = Algo::AnyOf(
|
|
*FileList, [](FStringView File) { return File.EndsWith(TEXTVIEW(".uondemandtoc")); });
|
|
}
|
|
|
|
FName BundleName = BundleInfo.BundleName;
|
|
ResultInfo.SourceBundleInfoMap.Add(BundleName, MoveTemp(BundleInfo));
|
|
}
|
|
}
|
|
|
|
Callback.ExecuteIfBound(AsShared(), MoveTemp(ResultInfo));
|
|
}
|
|
|
|
EInstallBundleManagerInitState FInstallBundleSourceBulk::GetInitState() const
|
|
{
|
|
return InitState;
|
|
}
|
|
|
|
FString FInstallBundleSourceBulk::GetContentVersion() const
|
|
{
|
|
return InstallBundleUtil::GetAppVersion();
|
|
}
|
|
|
|
TSet<FName> FInstallBundleSourceBulk::GetBundleDependencies(FName InBundleName, TSet<FName>* SkippedUnknownBundles /*= nullptr*/) const
|
|
{
|
|
return InstallBundleManagerUtil::GetBundleDependenciesFromConfig(InBundleName, SkippedUnknownBundles);
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::GetContentState(TArrayView<const FName> BundleNames, EInstallBundleGetContentStateFlags Flags, FInstallBundleGetContentStateDelegate Callback)
|
|
{
|
|
FInstallBundleCombinedContentState State;
|
|
State.CurrentVersion.Add(GetSourceType(), InstallBundleUtil::GetAppVersion());
|
|
for (const FName& BundleName : BundleNames)
|
|
{
|
|
if(!BulkBuildBundles.Contains(BundleName))
|
|
continue;
|
|
|
|
LOG_SOURCE_BULK(Verbose, TEXT("Requesting Content State for %s"), *BundleName.ToString());
|
|
|
|
FInstallBundleContentState& IndividualBundleState = State.IndividualBundleStates.Add(BundleName);
|
|
IndividualBundleState.State = GetBundleInstallState(BundleName);
|
|
IndividualBundleState.Version.Add(GetSourceType(), InstallBundleUtil::GetAppVersion());
|
|
}
|
|
|
|
for (TPair<FName, FInstallBundleContentState>& Pair : State.IndividualBundleStates)
|
|
{
|
|
Pair.Value.Weight = 1.0f / State.IndividualBundleStates.Num();
|
|
}
|
|
|
|
Callback.ExecuteIfBound(MoveTemp(State));
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::RequestUpdateContent(FRequestUpdateContentBundleContext Context)
|
|
{
|
|
FNameBuilder BundleNameBuilder(Context.BundleName);
|
|
|
|
LOG_SOURCE_BULK_OVERRIDE(Context.LogVerbosityOverride, Display, TEXT("Requesting Bundle %s"), BundleNameBuilder.ToString());
|
|
|
|
InstallBundleUtil::FConfigMountOptions OnDemandMountOptions;
|
|
InstallBundleUtil::GetMountOptionsFromConfig(BundleNameBuilder, OnDemandMountOptions);
|
|
|
|
FInstallBundleSourceUpdateContentResultInfo ResultInfo;
|
|
ResultInfo.BundleName = Context.BundleName;
|
|
ResultInfo.Result = EInstallBundleResult::OK;
|
|
TArray<FString>* BundleFileList = BulkBuildBundles.Find(Context.BundleName);
|
|
if (BundleFileList)
|
|
{
|
|
ResultInfo.ContentPaths = *BundleFileList;
|
|
}
|
|
|
|
for (const FString& Path : ResultInfo.ContentPaths)
|
|
{
|
|
if (Path.EndsWith(TEXTVIEW(".uondemandtoc")))
|
|
{
|
|
TUniquePtr<UE::IoStore::FOnDemandMountArgs> MountArgs = MakeUnique<UE::IoStore::FOnDemandMountArgs>();
|
|
MountArgs->MountId = Context.BundleName.ToString();
|
|
GetOnDemandHostGroup(MountArgs->HostGroup);
|
|
MountArgs->TocRelativeUrl = GetOnDemandTocRelativeURL();
|
|
MountArgs->FilePath = Path;
|
|
MountArgs->Options = UE::IoStore::EOnDemandMountOptions::InstallOnDemand;
|
|
|
|
if (OnDemandMountOptions.bWithSoftReferences)
|
|
{
|
|
MountArgs->Options |= UE::IoStore::EOnDemandMountOptions::WithSoftReferences;
|
|
}
|
|
|
|
ResultInfo.OnDemandMountArgs.Add(MoveTemp(MountArgs));
|
|
}
|
|
}
|
|
|
|
if (OnDemandMountOptions.bWithSoftReferences)
|
|
{
|
|
ResultInfo.MountOptions.MountFlags |= FPakMountOptions::EMountFlags::WithSoftReferences;
|
|
}
|
|
|
|
ResultInfo.ProjectName = FApp::GetProjectName();
|
|
Context.CompleteCallback.ExecuteIfBound(AsShared(), MoveTemp(ResultInfo));
|
|
}
|
|
|
|
void FInstallBundleSourceBulk::SetErrorSimulationCommands(const FString& CommandLine)
|
|
{
|
|
#if INSTALL_BUNDLE_ALLOW_ERROR_SIMULATION
|
|
#endif // INSTALL_BUNDLE_ALLOW_ERROR_SIMULATION
|
|
}
|
|
|
|
const TCHAR* LexToString(FInstallBundleSourceBulk::EAsyncInitStep Val)
|
|
{
|
|
static const TCHAR* Strings[] =
|
|
{
|
|
TEXT("InstallBundleSourceBulk:None"),
|
|
TEXT("InstallBundleSourceBulk:MakeBundlesForBulkBuild"),
|
|
TEXT("InstallBundleSourceBulk:Finishing"),
|
|
};
|
|
static_assert(InstallBundleUtil::CastToUnderlying(FInstallBundleSourceBulk::EAsyncInitStep::Count) == UE_ARRAY_COUNT(Strings), "");
|
|
|
|
return Strings[InstallBundleUtil::CastToUnderlying(Val)];
|
|
}
|