801 lines
37 KiB
C++
801 lines
37 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Diffing/DiffManifests.h"
|
|
|
|
#include "Algo/Transform.h"
|
|
#include "Async/Async.h"
|
|
#include "HAL/ThreadSafeBool.h"
|
|
#include "Logging/LogMacros.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Misc/OutputDeviceRedirector.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Policies/CondensedJsonPrintPolicy.h"
|
|
#include "Serialization/JsonWriter.h"
|
|
#include "HttpModule.h"
|
|
|
|
#include "Common/ChunkDataSizeProvider.h"
|
|
#include "Common/FileSystem.h"
|
|
#include "Common/HttpManager.h"
|
|
#include "Common/SpeedRecorder.h"
|
|
#include "Common/StatsCollector.h"
|
|
#include "Installer/Statistics/DownloadServiceStatistics.h"
|
|
#include "Installer/ChunkReferenceTracker.h"
|
|
#include "Installer/DownloadService.h"
|
|
#include "Installer/InstallerAnalytics.h"
|
|
#include "Installer/OptimisedDelta.h"
|
|
#include "BuildPatchFileConstructor.h"
|
|
#include "BuildPatchManifest.h"
|
|
#include "BuildPatchUtil.h"
|
|
|
|
DECLARE_LOG_CATEGORY_CLASS(LogDiffManifests, Log, All);
|
|
|
|
// For the output file we'll use pretty json in debug, otherwise condensed.
|
|
#if UE_BUILD_DEBUG
|
|
typedef TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>> FDiffJsonWriter;
|
|
typedef TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>> FDiffJsonWriterFactory;
|
|
#else
|
|
typedef TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>> FDiffJsonWriter;
|
|
typedef TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR>> FDiffJsonWriterFactory;
|
|
#endif //UE_BUILD_DEBUG
|
|
|
|
namespace BuildPatchServices
|
|
{
|
|
namespace DiffHelpers
|
|
{
|
|
struct FSimConfig
|
|
{
|
|
public:
|
|
FSimConfig()
|
|
: InstallMode(EInstallMode::DestructiveInstall)
|
|
, DownloadSpeed(0)
|
|
, DiskReadSpeed(0)
|
|
, DiskWriteSpeed(0)
|
|
, BackupSerialisationSpeed(0)
|
|
, FileCreateTime(0)
|
|
{ }
|
|
|
|
BuildPatchServices::EInstallMode InstallMode;
|
|
double DownloadSpeed;
|
|
double DiskReadSpeed;
|
|
double DiskWriteSpeed;
|
|
double BackupSerialisationSpeed;
|
|
double FileCreateTime;
|
|
};
|
|
|
|
TArray<double> CalculateInstallTimeCoefficient(const FBuildPatchAppManifestRef& CurrentManifest, const TSet<FString>& InCurrentTags, const FBuildPatchAppManifestRef& InstallManifest, const TSet<FString>& InInstallTags, const TArray<FSimConfig>& SimConfigs)
|
|
{
|
|
// Process tag setup.
|
|
TSet<FString> CurrentTags = InCurrentTags;
|
|
TSet<FString> InstallTags = InInstallTags;
|
|
if (CurrentTags.Num() == 0)
|
|
{
|
|
CurrentManifest->GetFileTagList(CurrentTags);
|
|
}
|
|
if (InstallTags.Num() == 0)
|
|
{
|
|
InstallManifest->GetFileTagList(InstallTags);
|
|
}
|
|
CurrentTags.Add(TEXT(""));
|
|
InstallTags.Add(TEXT(""));
|
|
|
|
// Enumerate what is available in the current install.
|
|
TSet<FString> FilesInstalled;
|
|
TSet<FGuid> ChunksInstalled;
|
|
CurrentManifest->GetTaggedFileList(CurrentTags, FilesInstalled);
|
|
{
|
|
TSet<FGuid> ChunksReferenced;
|
|
CurrentManifest->GetChunksRequiredForFiles(FilesInstalled, ChunksReferenced);
|
|
CurrentManifest->EnumerateProducibleChunks(CurrentTags, ChunksReferenced, ChunksInstalled);
|
|
}
|
|
|
|
// Enumerate what is needed for the update.
|
|
TSet<FString> FilesToBuild;
|
|
TSet<FGuid> ChunksNeeded;
|
|
{
|
|
TSet<FString> TaggedFiles;
|
|
InstallManifest->GetTaggedFileList(InstallTags, TaggedFiles);
|
|
for (FString& TaggedFile : TaggedFiles)
|
|
{
|
|
const FFileManifest* const OldFile = CurrentManifest->GetFileManifest(TaggedFile);
|
|
const FFileManifest* const NewFile = InstallManifest->GetFileManifest(TaggedFile);
|
|
if (!OldFile || OldFile->FileHash != NewFile->FileHash || !FilesInstalled.Contains(TaggedFile))
|
|
{
|
|
FilesToBuild.Add(MoveTemp(TaggedFile));
|
|
}
|
|
}
|
|
}
|
|
FilesToBuild.Sort(TLess<FString>());
|
|
CurrentManifest->GetChunksRequiredForFiles(FilesToBuild, ChunksNeeded);
|
|
|
|
// Setup a chunk reference tracker.
|
|
TArray<FGuid> ChunkReferences;
|
|
for (const FString& FileToBuild : FilesToBuild)
|
|
{
|
|
const FFileManifest* const FileManifest = InstallManifest->GetFileManifest(FileToBuild);
|
|
for (const FChunkPart& ChunkPart : FileManifest->ChunkParts)
|
|
{
|
|
ChunkReferences.Add(ChunkPart.Guid);
|
|
}
|
|
}
|
|
TUniquePtr<IChunkReferenceTracker> ChunkReferenceTracker(FChunkReferenceTrackerFactory::Create(ChunkReferences));
|
|
|
|
// A private struct to simulate based on statistics configuration SimConfigs.
|
|
struct FInstallTimeSim
|
|
{
|
|
public:
|
|
FInstallTimeSim(const IChunkReferenceTracker& InChunkReferenceTracker, const FBuildPatchAppManifest& InInstallManifest, const TSet<FGuid>& InChunksInstalled, const FSimConfig& InConfig)
|
|
: ChunkReferenceTracker(InChunkReferenceTracker)
|
|
, InstallManifest(InInstallManifest)
|
|
, ChunksInstalled(InChunksInstalled)
|
|
, Config(InConfig)
|
|
, Timer(0.0)
|
|
{ }
|
|
|
|
void CreateFile()
|
|
{
|
|
Timer += Config.FileCreateTime;
|
|
}
|
|
|
|
void TickDownloads()
|
|
{
|
|
// Complete downloads.
|
|
while (DownloadChunks.Num() > 0 && DownloadChunks[0].Get<0>() <= Timer)
|
|
{
|
|
LoadedChunks.Add(DownloadChunks[0].Get<1>());
|
|
DownloadChunks.RemoveAt(0);
|
|
}
|
|
// Queue up some more downloads once our in-flight list is getting emptied.
|
|
if (DownloadChunks.Num() < 50)
|
|
{
|
|
TSet<FGuid> DownloadingChunks;
|
|
Algo::Transform(DownloadChunks, DownloadingChunks, [](const TTuple<double, FGuid>& Elem) { return Elem.Get<1>(); });
|
|
TFunction<bool(const FGuid&)> SelectPredicate = [&](const FGuid& ChunkId) { return !ChunksInstalled.Contains(ChunkId); };
|
|
TArray<FGuid> BatchLoadChunks = ChunkReferenceTracker.SelectFromNextReferences(100, SelectPredicate);
|
|
TFunction<bool(const FGuid&)> RemovePredicate = [&](const FGuid& ChunkId) { return DownloadingChunks.Contains(ChunkId) || LoadedChunks.Contains(ChunkId) || BackupChunks.Contains(ChunkId); };
|
|
BatchLoadChunks.RemoveAll(RemovePredicate);
|
|
double DownloadTime = FMath::Max(DownloadChunks.Num() > 0 ? DownloadChunks.Last().Get<0>() : 0, Timer);
|
|
for (const FGuid& BatchLoadChunk : BatchLoadChunks)
|
|
{
|
|
DownloadTime += (double)InstallManifest.GetChunkInfo(BatchLoadChunk)->FileSize / Config.DownloadSpeed;
|
|
DownloadChunks.Emplace(DownloadTime, BatchLoadChunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GetChunk(const FChunkInfo& ChunkInfo)
|
|
{
|
|
if (!LoadedChunks.Contains(ChunkInfo.Guid))
|
|
{
|
|
if (BackupChunks.Contains(ChunkInfo.Guid))
|
|
{
|
|
Timer += (double)ChunkInfo.FileSize / Config.DiskReadSpeed;
|
|
LoadedChunks.Add(ChunkInfo.Guid);
|
|
}
|
|
else if (ChunksInstalled.Contains(ChunkInfo.Guid))
|
|
{
|
|
Timer += (double)ChunkInfo.WindowSize / Config.DiskReadSpeed;
|
|
LoadedChunks.Add(ChunkInfo.Guid);
|
|
}
|
|
else
|
|
{
|
|
// figure out when this chunk will finish downloading
|
|
for (int32 DownloadChunkIdx = 0; DownloadChunkIdx < DownloadChunks.Num(); ++DownloadChunkIdx)
|
|
{
|
|
const double& TimeDownloaded = DownloadChunks[DownloadChunkIdx].Get<0>();
|
|
const FGuid& ChunkId = DownloadChunks[DownloadChunkIdx].Get<1>();
|
|
if (ChunkInfo.Guid == ChunkId)
|
|
{
|
|
Timer = TimeDownloaded;
|
|
LoadedChunks.Add(ChunkId);
|
|
DownloadChunks.RemoveAt(DownloadChunkIdx);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
checkf(LoadedChunks.Contains(ChunkInfo.Guid), TEXT("Logic error with timer simulation."));
|
|
}
|
|
}
|
|
|
|
void WriteData(uint32 Size)
|
|
{
|
|
Timer += (double)Size / Config.DiskWriteSpeed;
|
|
}
|
|
|
|
void EvaluateDestruction(const FFileManifest& OldFile)
|
|
{
|
|
if (Config.InstallMode == EInstallMode::DestructiveInstall)
|
|
{
|
|
// Collect all chunks in this file.
|
|
TSet<FGuid> FileManifestChunks;
|
|
Algo::Transform(OldFile.ChunkParts, FileManifestChunks, &FChunkPart::Guid);
|
|
FileManifestChunks = FileManifestChunks.Intersect(ChunksInstalled);
|
|
// Select all chunks still required from this file.
|
|
TFunction<bool(const FGuid&)> SelectPredicate = [&](const FGuid& ChunkId) { return !LoadedChunks.Contains(ChunkId) && FileManifestChunks.Contains(ChunkId); };
|
|
TArray<FGuid> BatchLoadChunks = ChunkReferenceTracker.GetNextReferences(TNumericLimits<int32>::Max(), SelectPredicate);
|
|
for (const FGuid& BatchLoadChunk : BatchLoadChunks)
|
|
{
|
|
// Load it from disk.
|
|
Timer += (double)InstallManifest.GetChunkInfo(BatchLoadChunk)->WindowSize / Config.BackupSerialisationSpeed;
|
|
// Save it to backup.
|
|
Timer += (double)InstallManifest.GetChunkInfo(BatchLoadChunk)->FileSize / Config.BackupSerialisationSpeed;
|
|
BackupChunks.Add(BatchLoadChunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
double GetTimer() const
|
|
{
|
|
return Timer;
|
|
}
|
|
|
|
// Dependencies
|
|
const IChunkReferenceTracker& ChunkReferenceTracker;
|
|
const FBuildPatchAppManifest& InstallManifest;
|
|
const TSet<FGuid>& ChunksInstalled;
|
|
const FSimConfig Config;
|
|
|
|
// Tracking
|
|
double Timer;
|
|
TSet<FGuid> LoadedChunks;
|
|
TSet<FGuid> BackupChunks;
|
|
TArray<TTuple<double, FGuid>> DownloadChunks;
|
|
};
|
|
|
|
// Setup the simulators and run the process through them.
|
|
TArray<FInstallTimeSim> TimeSims;
|
|
for (const FSimConfig& SimConfig : SimConfigs)
|
|
{
|
|
TimeSims.Emplace(*ChunkReferenceTracker.Get(), InstallManifest.Get(), ChunksInstalled, SimConfig);
|
|
}
|
|
for (const FString& FileToBuild : FilesToBuild)
|
|
{
|
|
// Create a new file.
|
|
for (FInstallTimeSim& TimeSim : TimeSims) { TimeSim.CreateFile(); }
|
|
|
|
// For each required chunk.
|
|
const FFileManifest& NewFileManifest = *InstallManifest->GetFileManifest(FileToBuild);
|
|
for (const FChunkPart& ChunkPart : NewFileManifest.ChunkParts)
|
|
{
|
|
// Process completed downloads.
|
|
for (FInstallTimeSim& TimeSim : TimeSims) { TimeSim.TickDownloads(); }
|
|
|
|
// Get the chunk.
|
|
const FChunkInfo& ChunkInfo = *InstallManifest->GetChunkInfo(ChunkPart.Guid);
|
|
for (FInstallTimeSim& TimeSim : TimeSims) { TimeSim.GetChunk(ChunkInfo); }
|
|
|
|
// Write the chunk to file.
|
|
for (FInstallTimeSim& TimeSim : TimeSims) { TimeSim.WriteData(ChunkPart.Size); }
|
|
ChunkReferenceTracker->PopReference(ChunkPart.Guid);
|
|
}
|
|
// If there's an old file to delete, add time for backing up all of the still referenced chunks.
|
|
if (FilesInstalled.Contains(FileToBuild))
|
|
{
|
|
const FFileManifest& OldFileManifest = *CurrentManifest->GetFileManifest(FileToBuild);
|
|
for (FInstallTimeSim& TimeSim : TimeSims) { TimeSim.EvaluateDestruction(OldFileManifest); }
|
|
}
|
|
}
|
|
|
|
// Return the simulation results.
|
|
TArray<double> Results;
|
|
for (FInstallTimeSim& TimeSim : TimeSims) { Results.Add(TimeSim.GetTimer()); }
|
|
return Results;
|
|
}
|
|
}
|
|
|
|
class FDiffManifests
|
|
: public IDiffManifests
|
|
{
|
|
public:
|
|
FDiffManifests(const FDiffManifestsConfiguration& InConfiguration);
|
|
~FDiffManifests();
|
|
|
|
// IChunkDeltaOptimiser interface begin.
|
|
virtual bool Run() override;
|
|
// IChunkDeltaOptimiser interface end.
|
|
|
|
private:
|
|
bool AsyncRun();
|
|
void HandleDownloadComplete(int32 RequestId, const FDownloadRef& Download);
|
|
|
|
private:
|
|
const FDiffManifestsConfiguration Configuration;
|
|
FTSTicker& CoreTicker;
|
|
FDownloadCompleteDelegate DownloadCompleteDelegate;
|
|
FDownloadProgressDelegate DownloadProgressDelegate;
|
|
TUniquePtr<IFileSystem> FileSystem;
|
|
TUniquePtr<IHttpManager> HttpManager;
|
|
TUniquePtr<IChunkDataSizeProvider> ChunkDataSizeProvider;
|
|
TUniquePtr<ISpeedRecorder> DownloadSpeedRecorder;
|
|
TUniquePtr<IInstallerAnalytics> InstallerAnalytics;
|
|
TUniquePtr<IDownloadServiceStatistics> DownloadServiceStatistics;
|
|
TUniquePtr<IDownloadService> DownloadService;
|
|
TUniquePtr<FStatsCollector> StatsCollector;
|
|
FThreadSafeBool bShouldRun;
|
|
|
|
// Manifest downloading
|
|
int32 RequestIdManifestA;
|
|
int32 RequestIdManifestB;
|
|
TPromise<FBuildPatchAppManifestPtr> PromiseManifestA;
|
|
TPromise<FBuildPatchAppManifestPtr> PromiseManifestB;
|
|
TFuture<FBuildPatchAppManifestPtr> FutureManifestA;
|
|
TFuture<FBuildPatchAppManifestPtr> FutureManifestB;
|
|
};
|
|
|
|
FDiffManifests::FDiffManifests(const FDiffManifestsConfiguration& InConfiguration)
|
|
: Configuration(InConfiguration)
|
|
, CoreTicker(FTSTicker::GetCoreTicker())
|
|
, DownloadCompleteDelegate(FDownloadCompleteDelegate::CreateRaw(this, &FDiffManifests::HandleDownloadComplete))
|
|
, DownloadProgressDelegate()
|
|
, FileSystem(FFileSystemFactory::Create())
|
|
, HttpManager(FHttpManagerFactory::Create())
|
|
, ChunkDataSizeProvider(FChunkDataSizeProviderFactory::Create())
|
|
, DownloadSpeedRecorder(FSpeedRecorderFactory::Create())
|
|
, InstallerAnalytics(FInstallerAnalyticsFactory::Create(nullptr))
|
|
, DownloadServiceStatistics(FDownloadServiceStatisticsFactory::Create(DownloadSpeedRecorder.Get(), ChunkDataSizeProvider.Get(), InstallerAnalytics.Get()))
|
|
, DownloadService(FDownloadServiceFactory::Create(HttpManager.Get(), FileSystem.Get(), DownloadServiceStatistics.Get(), InstallerAnalytics.Get()))
|
|
, StatsCollector(FStatsCollectorFactory::Create())
|
|
, bShouldRun(true)
|
|
, RequestIdManifestA(INDEX_NONE)
|
|
, RequestIdManifestB(INDEX_NONE)
|
|
, PromiseManifestA()
|
|
, PromiseManifestB()
|
|
, FutureManifestA(PromiseManifestA.GetFuture())
|
|
, FutureManifestB(PromiseManifestB.GetFuture())
|
|
{
|
|
}
|
|
|
|
FDiffManifests::~FDiffManifests()
|
|
{
|
|
}
|
|
|
|
bool FDiffManifests::Run()
|
|
{
|
|
// Run any core initialisation required.
|
|
FHttpModule::Get();
|
|
|
|
// Kick off Manifest downloads.
|
|
RequestIdManifestA = DownloadService->RequestFile(Configuration.ManifestAUri, DownloadCompleteDelegate, DownloadProgressDelegate);
|
|
RequestIdManifestB = DownloadService->RequestFile(Configuration.ManifestBUri, DownloadCompleteDelegate, DownloadProgressDelegate);
|
|
|
|
// Start the generation thread.
|
|
TFuture<bool> Thread = Async(EAsyncExecution::Thread, [this]() { return AsyncRun(); });
|
|
|
|
// Main timers.
|
|
double DeltaTime = 0.0;
|
|
double LastTime = FPlatformTime::Seconds();
|
|
|
|
// Setup desired frame times.
|
|
float MainsFramerate = 100.0f;
|
|
const float MainsFrameTime = 1.0f / MainsFramerate;
|
|
|
|
// Run the main loop.
|
|
while (bShouldRun)
|
|
{
|
|
// Increment global frame counter once for each app tick.
|
|
GFrameCounter++;
|
|
|
|
// Application tick.
|
|
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
|
|
FTSTicker::GetCoreTicker().Tick(DeltaTime);
|
|
GLog->FlushThreadedLogs();
|
|
|
|
// Control frame rate.
|
|
FPlatformProcess::Sleep(FMath::Max<float>(0.0f, MainsFrameTime - (FPlatformTime::Seconds() - LastTime)));
|
|
|
|
// Calculate deltas.
|
|
const double AppTime = FPlatformTime::Seconds();
|
|
DeltaTime = AppTime - LastTime;
|
|
LastTime = AppTime;
|
|
}
|
|
GLog->FlushThreadedLogs();
|
|
|
|
// Return thread success.
|
|
return Thread.Get();
|
|
}
|
|
|
|
bool FDiffManifests::AsyncRun()
|
|
{
|
|
FBuildPatchAppManifestPtr ManifestA = FutureManifestA.Get();
|
|
FBuildPatchAppManifestPtr ManifestB = FutureManifestB.Get();
|
|
bool bSuccess = true;
|
|
if (ManifestA.IsValid() == false)
|
|
{
|
|
UE_LOG(LogDiffManifests, Error, TEXT("Could not download ManifestA from %s."), *Configuration.ManifestAUri);
|
|
bSuccess = false;
|
|
}
|
|
if (ManifestB.IsValid() == false)
|
|
{
|
|
UE_LOG(LogDiffManifests, Error, TEXT("Could not download ManifestB from %s."), *Configuration.ManifestBUri);
|
|
bSuccess = false;
|
|
}
|
|
if (bSuccess)
|
|
{
|
|
// Check for delta file, replacing ManifestB if we find one
|
|
FOptimisedDeltaConfiguration OptimisedDeltaConfiguration(ManifestB.ToSharedRef());
|
|
OptimisedDeltaConfiguration.SourceManifest = ManifestA;
|
|
OptimisedDeltaConfiguration.DeltaPolicy = EDeltaPolicy::TryFetchContinueWithout;
|
|
OptimisedDeltaConfiguration.CloudDirectories = { FPaths::GetPath(Configuration.ManifestBUri) };
|
|
FOptimisedDeltaDependencies OptimisedDeltaDependencies;
|
|
OptimisedDeltaDependencies.DownloadService = DownloadService.Get();
|
|
TUniquePtr<IOptimisedDelta> OptimisedDelta(FOptimisedDeltaFactory::Create(OptimisedDeltaConfiguration, MoveTemp(OptimisedDeltaDependencies)));
|
|
ManifestB = OptimisedDelta->GetResult().GetValue();
|
|
const int32 MetaDownloadBytes = OptimisedDelta->GetMetaDownloadSize();
|
|
|
|
TSet<FString> TagsA, TagsB;
|
|
ManifestA->GetFileTagList(TagsA);
|
|
if (Configuration.TagSetA.Num() > 0)
|
|
{
|
|
TagsA = TagsA.Intersect(Configuration.TagSetA);
|
|
}
|
|
ManifestB->GetFileTagList(TagsB);
|
|
if (Configuration.TagSetB.Num() > 0)
|
|
{
|
|
TagsB = TagsB.Intersect(Configuration.TagSetB);
|
|
}
|
|
|
|
int64 NewChunksCount = 0;
|
|
int64 TotalChunkSize = 0;
|
|
TSet<FString> TaggedFileSetA;
|
|
TSet<FString> TaggedFileSetB;
|
|
TSet<FGuid> ChunkSetA;
|
|
TSet<FGuid> ChunkSetB;
|
|
ManifestA->GetTaggedFileList(TagsA, TaggedFileSetA);
|
|
ManifestA->GetChunksRequiredForFiles(TaggedFileSetA, ChunkSetA);
|
|
ManifestB->GetTaggedFileList(TagsB, TaggedFileSetB);
|
|
ManifestB->GetChunksRequiredForFiles(TaggedFileSetB, ChunkSetB);
|
|
TArray<FString> NewChunkPaths;
|
|
for (FGuid& ChunkB : ChunkSetB)
|
|
{
|
|
if (ChunkSetA.Contains(ChunkB) == false)
|
|
{
|
|
++NewChunksCount;
|
|
int32 ChunkFileSize = ManifestB->GetDataSize(ChunkB);
|
|
TotalChunkSize += ChunkFileSize;
|
|
NewChunkPaths.Add(FBuildPatchUtils::GetDataFilename(ManifestB.ToSharedRef(), ChunkB));
|
|
UE_LOG(LogDiffManifests, Verbose, TEXT("New chunk discovered: Size: %10lld, Path: %s"), ChunkFileSize, *NewChunkPaths.Last());
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogDiffManifests, Display, TEXT("New chunks: %lld"), NewChunksCount);
|
|
UE_LOG(LogDiffManifests, Display, TEXT("Total bytes: %lld"), TotalChunkSize);
|
|
|
|
TSet<FString> NewFilePaths = TaggedFileSetB.Difference(TaggedFileSetA);
|
|
TSet<FString> RemovedFilePaths = TaggedFileSetA.Difference(TaggedFileSetB);
|
|
TSet<FString> ChangedFilePaths;
|
|
TSet<FString> UnchangedFilePaths;
|
|
|
|
const TSet<FString>& SetToIterate = TaggedFileSetB.Num() > TaggedFileSetA.Num() ? TaggedFileSetA : TaggedFileSetB;
|
|
for (const FString& TaggedFile : SetToIterate)
|
|
{
|
|
FSHAHash FileHashA;
|
|
FSHAHash FileHashB;
|
|
if (ManifestA->GetFileHash(TaggedFile, FileHashA) && ManifestB->GetFileHash(TaggedFile, FileHashB))
|
|
{
|
|
if (FileHashA == FileHashB)
|
|
{
|
|
UnchangedFilePaths.Add(TaggedFile);
|
|
}
|
|
else
|
|
{
|
|
ChangedFilePaths.Add(TaggedFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Log download details.
|
|
FNumberFormattingOptions SizeFormattingOptions;
|
|
SizeFormattingOptions.MaximumFractionalDigits = 3;
|
|
SizeFormattingOptions.MinimumFractionalDigits = 3;
|
|
|
|
int64 DownloadSizeA = ManifestA->GetDownloadSize(TagsA);
|
|
int64 BuildSizeA = ManifestA->GetBuildSize(TagsA);
|
|
int64 DownloadSizeB = ManifestB->GetDownloadSize(TagsB);
|
|
int64 BuildSizeB = ManifestB->GetBuildSize(TagsB);
|
|
int64 DeltaDownloadSize = ManifestB->GetDeltaDownloadSize(TagsB, ManifestA.ToSharedRef(), TagsA) + MetaDownloadBytes;
|
|
int64 TempDiskSpaceReq = FileConstructorHelpers::CalculateRequiredDiskSpace(ManifestA, ManifestB.ToSharedRef(), EInstallMode::DestructiveInstall, TagsB);
|
|
|
|
// Break down the sizes and delta into new chunks per tag.
|
|
TMap<FString, int64> TagDownloadImpactA;
|
|
TMap<FString, int64> TagBuildImpactA;
|
|
TMap<FString, int64> TagDownloadImpactB;
|
|
TMap<FString, int64> TagBuildImpactB;
|
|
TMap<FString, int64> TagDeltaImpact;
|
|
for (const FString& Tag : TagsA)
|
|
{
|
|
TSet<FString> TagSet;
|
|
TagSet.Add(Tag);
|
|
TagDownloadImpactA.Add(Tag, ManifestA->GetDownloadSize(TagSet));
|
|
TagBuildImpactA.Add(Tag, ManifestA->GetBuildSize(TagSet));
|
|
}
|
|
for (const FString& Tag : TagsB)
|
|
{
|
|
TSet<FString> TagSet;
|
|
TagSet.Add(Tag);
|
|
TagDownloadImpactB.Add(Tag, ManifestB->GetDownloadSize(TagSet));
|
|
TagBuildImpactB.Add(Tag, ManifestB->GetBuildSize(TagSet));
|
|
TagDeltaImpact.Add(Tag, ManifestB->GetDeltaDownloadSize(TagSet, ManifestA.ToSharedRef(), TagsA));
|
|
}
|
|
if (MetaDownloadBytes > 0)
|
|
{
|
|
TagDeltaImpact.FindOrAdd(TEXT("")) += MetaDownloadBytes;
|
|
}
|
|
|
|
// Compare tag sets
|
|
TMap<FString, int64> CompareTagSetDeltaImpact;
|
|
TMap<FString, int64> CompareTagSetBuildImpactA;
|
|
TMap<FString, int64> CompareTagSetDownloadSizeA;
|
|
TMap<FString, int64> CompareTagSetBuildImpactB;
|
|
TMap<FString, int64> CompareTagSetDownloadSizeB;
|
|
TMap<FString, int64> CompareTagSetTempDiskSpaceReqs;
|
|
TSet<FString> CompareTagSetKeys;
|
|
for (const TSet<FString>& TagSet : Configuration.CompareTagSets)
|
|
{
|
|
TArray<FString> TagArrayCompare = TagSet.Array();
|
|
Algo::Sort(TagArrayCompare);
|
|
FString TagSetString = FString::Join(TagArrayCompare, TEXT(", "));
|
|
CompareTagSetKeys.Add(TagSetString);
|
|
CompareTagSetDeltaImpact.Add(TagSetString, ManifestB->GetDeltaDownloadSize(TagSet, ManifestA.ToSharedRef(), TagSet) + MetaDownloadBytes);
|
|
CompareTagSetBuildImpactB.Add(TagSetString, ManifestB->GetBuildSize(TagSet));
|
|
CompareTagSetDownloadSizeB.Add(TagSetString, ManifestB->GetDownloadSize(TagSet));
|
|
CompareTagSetBuildImpactA.Add(TagSetString, ManifestA->GetBuildSize(TagSet));
|
|
CompareTagSetDownloadSizeA.Add(TagSetString, ManifestA->GetDownloadSize(TagSet));
|
|
CompareTagSetTempDiskSpaceReqs.Add(TagSetString, FileConstructorHelpers::CalculateRequiredDiskSpace(ManifestA, ManifestB.ToSharedRef(), EInstallMode::DestructiveInstall, TagSet));
|
|
}
|
|
|
|
// Log the information.
|
|
TArray<FString> TagArrayB = TagsB.Array();
|
|
Algo::Sort(TagArrayB);
|
|
FString UntaggedLog(TEXT("(untagged)"));
|
|
FString TagLogList = FString::Join(TagArrayB, TEXT(", "));
|
|
if (TagLogList.IsEmpty() || TagLogList.StartsWith(TEXT(", ")))
|
|
{
|
|
TagLogList.InsertAt(0, UntaggedLog);
|
|
}
|
|
UE_LOG(LogDiffManifests, Display, TEXT("TagSet: %s"), *TagLogList);
|
|
UE_LOG(LogDiffManifests, Display, TEXT("%s %s:"), *ManifestA->GetAppName(), *ManifestA->GetVersionString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Download Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(DownloadSizeA).ToString(), *FText::AsMemory(DownloadSizeA, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(DownloadSizeA, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Build Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(BuildSizeA).ToString(), *FText::AsMemory(BuildSizeA, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(BuildSizeA, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT("%s %s:"), *ManifestB->GetAppName(), *ManifestB->GetVersionString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Download Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(DownloadSizeB).ToString(), *FText::AsMemory(DownloadSizeB, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(DownloadSizeB, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Build Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(BuildSizeB).ToString(), *FText::AsMemory(BuildSizeB, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(BuildSizeB, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT("%s %s -> %s %s:"), *ManifestA->GetAppName(), *ManifestA->GetVersionString(), *ManifestB->GetAppName(), *ManifestB->GetVersionString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Delta Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(DeltaDownloadSize).ToString(), *FText::AsMemory(DeltaDownloadSize, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(DeltaDownloadSize, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Temp Disk Space: %20s bytes (%10s, %11s)"), *FText::AsNumber(TempDiskSpaceReq).ToString(), *FText::AsMemory(TempDiskSpaceReq, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(TempDiskSpaceReq, &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(""));
|
|
|
|
for (const FString& Tag : TagArrayB)
|
|
{
|
|
UE_LOG(LogDiffManifests, Display, TEXT("%s Impact:"), *(Tag.IsEmpty() ? UntaggedLog : Tag));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Individual Download Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(TagDownloadImpactB[Tag]).ToString(), *FText::AsMemory(TagDownloadImpactB[Tag], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(TagDownloadImpactB[Tag], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Individual Build Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(TagBuildImpactB[Tag]).ToString(), *FText::AsMemory(TagBuildImpactB[Tag], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(TagBuildImpactB[Tag], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Individual Delta Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(TagDeltaImpact[Tag]).ToString(), *FText::AsMemory(TagDeltaImpact[Tag], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(TagDeltaImpact[Tag], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
}
|
|
|
|
for (const FString& TagSet : CompareTagSetKeys)
|
|
{
|
|
const FString& TagSetDisplay = TagSet.IsEmpty() || TagSet.StartsWith(TEXT(",")) ? UntaggedLog + TagSet : TagSet;
|
|
UE_LOG(LogDiffManifests, Display, TEXT("Impact of TagSet: %s"), *TagSetDisplay);
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Download Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(CompareTagSetDownloadSizeB[TagSet]).ToString(), *FText::AsMemory(CompareTagSetDownloadSizeB[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(CompareTagSetDownloadSizeB[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Build Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(CompareTagSetBuildImpactB[TagSet]).ToString(), *FText::AsMemory(CompareTagSetBuildImpactB[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(CompareTagSetBuildImpactB[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Delta Size: %20s bytes (%10s, %11s)"), *FText::AsNumber(CompareTagSetDeltaImpact[TagSet]).ToString(), *FText::AsMemory(CompareTagSetDeltaImpact[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(CompareTagSetDeltaImpact[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Temp Disk Space: %20s bytes (%10s, %11s)"), *FText::AsNumber(CompareTagSetTempDiskSpaceReqs[TagSet]).ToString(), *FText::AsMemory(CompareTagSetTempDiskSpaceReqs[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::SI).ToString(), *FText::AsMemory(CompareTagSetTempDiskSpaceReqs[TagSet], &SizeFormattingOptions, nullptr, EMemoryUnitStandard::IEC).ToString());
|
|
}
|
|
|
|
// Hit a destructive and nondestructive simulation for a few different specs.
|
|
TArray<DiffHelpers::FSimConfig> SimConfigs;
|
|
// Add some lower spec values, taken from around 25 percentile of stats at the time of writing [July 2019].
|
|
SimConfigs.AddDefaulted_GetRef().InstallMode = EInstallMode::DestructiveInstall;
|
|
SimConfigs.AddDefaulted_GetRef().InstallMode = EInstallMode::NonDestructiveInstall;
|
|
SimConfigs[0].DownloadSpeed = SimConfigs[1].DownloadSpeed = 1200000.0; // 1.2 MB/s
|
|
SimConfigs[0].DiskReadSpeed = SimConfigs[1].DiskReadSpeed = 30000000.0; // 30 MB/s
|
|
SimConfigs[0].DiskWriteSpeed = SimConfigs[1].DiskWriteSpeed = 25000000.0; // 25 MB/s
|
|
// We didn't have stats for BackupSerialisationSpeed, but it runs much slower than disk speed and so we tune it to be sure destructive is relatively penalizing.
|
|
SimConfigs[0].BackupSerialisationSpeed = SimConfigs[1].BackupSerialisationSpeed = 10000000.0; // 10 MB/s
|
|
// Add some lower spec values, taken from around 50 percentile of stats at the time of writing [July 2019].
|
|
SimConfigs.AddDefaulted_GetRef().InstallMode = EInstallMode::DestructiveInstall;
|
|
SimConfigs.AddDefaulted_GetRef().InstallMode = EInstallMode::NonDestructiveInstall;
|
|
SimConfigs[2].DownloadSpeed = SimConfigs[3].DownloadSpeed = 3500000.0; // 3.5 MB/s
|
|
SimConfigs[2].DiskReadSpeed = SimConfigs[3].DiskReadSpeed = 145000000.0; // 145 MB/s
|
|
SimConfigs[2].DiskWriteSpeed = SimConfigs[3].DiskWriteSpeed = 75000000.0; // 75 MB/s
|
|
// We didn't have stats for BackupSerialisationSpeed, but it runs much slower than disk speed and so we tune it to be sure destructive is relatively penalizing.
|
|
SimConfigs[2].BackupSerialisationSpeed = SimConfigs[3].BackupSerialisationSpeed = 20000000.0; // 20 MB/s
|
|
// Add some higher spec values, taken from around 75 percentile of stats at the time of writing [July 2019].
|
|
SimConfigs.AddDefaulted_GetRef().InstallMode = EInstallMode::DestructiveInstall;
|
|
SimConfigs.AddDefaulted_GetRef().InstallMode = EInstallMode::NonDestructiveInstall;
|
|
SimConfigs[4].DownloadSpeed = SimConfigs[5].DownloadSpeed = 13000000.0; // 13 MB/s
|
|
SimConfigs[4].DiskReadSpeed = SimConfigs[5].DiskReadSpeed = 295000000.0; // 295 MB/s
|
|
SimConfigs[4].DiskWriteSpeed = SimConfigs[5].DiskWriteSpeed = 125000000.0; // 125 MB/s
|
|
// We didn't have stats for BackupSerialisationSpeed, but it runs much slower than disk speed and so we tune it to be sure destructive is relatively penalizing.
|
|
SimConfigs[4].BackupSerialisationSpeed = SimConfigs[5].BackupSerialisationSpeed = 40000000.0; // 40 MB/s
|
|
|
|
// Run the calculations and log.
|
|
TArray<double> InstallTimeCoefficients = DiffHelpers::CalculateInstallTimeCoefficient(ManifestA.ToSharedRef(), TagsA, ManifestB.ToSharedRef(), TagsB, SimConfigs);
|
|
checkf(6 == InstallTimeCoefficients.Num() && 6 == SimConfigs.Num(), TEXT("Unexpected result size from CalculateInstallTimeCoefficient."));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(""));
|
|
UE_LOG(LogDiffManifests, Display, TEXT("Install time coefficients are not accurate timing representations, but are comparable from patch to patch."));
|
|
UE_LOG(LogDiffManifests, Display, TEXT("They can be used to spot out of the ordinary time requirements for installing an update."));
|
|
UE_LOG(LogDiffManifests, Display, TEXT("Install Time Coefficients:"));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Low-Spec DestructiveInstall: %s"), *FPlatformTime::PrettyTime(InstallTimeCoefficients[0]));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Low-Spec NonDestructiveInstall: %s"), *FPlatformTime::PrettyTime(InstallTimeCoefficients[1]));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Mid-Spec DestructiveInstall: %s"), *FPlatformTime::PrettyTime(InstallTimeCoefficients[2]));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" Mid-Spec NonDestructiveInstall: %s"), *FPlatformTime::PrettyTime(InstallTimeCoefficients[3]));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" High-Spec DestructiveInstall: %s"), *FPlatformTime::PrettyTime(InstallTimeCoefficients[4]));
|
|
UE_LOG(LogDiffManifests, Display, TEXT(" High-Spec NonDestructiveInstall: %s"), *FPlatformTime::PrettyTime(InstallTimeCoefficients[5]));
|
|
|
|
// Save the output.
|
|
if (bSuccess && Configuration.OutputFilePath.IsEmpty() == false)
|
|
{
|
|
FString JsonOutput;
|
|
TSharedRef<FDiffJsonWriter> Writer = FDiffJsonWriterFactory::Create(&JsonOutput);
|
|
Writer->WriteObjectStart();
|
|
{
|
|
Writer->WriteObjectStart(TEXT("ManifestA"));
|
|
{
|
|
Writer->WriteValue(TEXT("AppName"), ManifestA->GetAppName());
|
|
Writer->WriteValue(TEXT("AppId"), static_cast<int32>(ManifestA->GetAppID()));
|
|
Writer->WriteValue(TEXT("VersionString"), ManifestA->GetVersionString());
|
|
Writer->WriteValue(TEXT("DownloadSize"), DownloadSizeA);
|
|
Writer->WriteValue(TEXT("BuildSize"), BuildSizeA);
|
|
Writer->WriteObjectStart(TEXT("IndividualTagDownloadSizes"));
|
|
for (const TPair<FString, int64>& Pair : TagDownloadImpactA)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart("CompareTagSetDownloadSizes");
|
|
for (const TPair<FString, int64>& Pair : CompareTagSetDownloadSizeA)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart(TEXT("IndividualTagBuildSizes"));
|
|
for (const TPair<FString, int64>& Pair : TagBuildImpactA)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart("CompareTagSetBuildSizes");
|
|
for (const TPair<FString, int64>& Pair : CompareTagSetBuildImpactA)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart(TEXT("ManifestB"));
|
|
{
|
|
Writer->WriteValue(TEXT("AppName"), ManifestB->GetAppName());
|
|
Writer->WriteValue(TEXT("AppId"), static_cast<int32>(ManifestB->GetAppID()));
|
|
Writer->WriteValue(TEXT("VersionString"), ManifestB->GetVersionString());
|
|
Writer->WriteValue(TEXT("DownloadSize"), DownloadSizeB);
|
|
Writer->WriteValue(TEXT("BuildSize"), BuildSizeB);
|
|
Writer->WriteObjectStart(TEXT("IndividualTagDownloadSizes"));
|
|
for (const TPair<FString, int64>& Pair : TagDownloadImpactB)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart("CompareTagSetDownloadSizes");
|
|
for (const TPair<FString, int64>& Pair : CompareTagSetDownloadSizeB)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart(TEXT("IndividualTagBuildSizes"));
|
|
for (const TPair<FString, int64>& Pair : TagBuildImpactB)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart("CompareTagSetBuildSizes");
|
|
for (const TPair<FString, int64>& Pair : CompareTagSetBuildImpactB)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart(TEXT("Differential"));
|
|
{
|
|
Writer->WriteArrayStart(TEXT("NewFilePaths"));
|
|
for (const FString& NewFilePath : NewFilePaths)
|
|
{
|
|
Writer->WriteValue(NewFilePath);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
Writer->WriteArrayStart(TEXT("RemovedFilePaths"));
|
|
for (const FString& RemovedFilePath : RemovedFilePaths)
|
|
{
|
|
Writer->WriteValue(RemovedFilePath);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
Writer->WriteArrayStart(TEXT("ChangedFilePaths"));
|
|
for (const FString& ChangedFilePath : ChangedFilePaths)
|
|
{
|
|
Writer->WriteValue(ChangedFilePath);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
Writer->WriteArrayStart(TEXT("UnchangedFilePaths"));
|
|
for (const FString& UnchangedFilePath : UnchangedFilePaths)
|
|
{
|
|
Writer->WriteValue(UnchangedFilePath);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
Writer->WriteArrayStart(TEXT("NewChunkPaths"));
|
|
for (const FString& NewChunkPath : NewChunkPaths)
|
|
{
|
|
Writer->WriteValue(NewChunkPath);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
Writer->WriteValue(TEXT("TotalChunkSize"), TotalChunkSize);
|
|
Writer->WriteValue(TEXT("DeltaDownloadSize"), DeltaDownloadSize);
|
|
Writer->WriteValue(TEXT("TempDiskSpaceReq"), TempDiskSpaceReq);
|
|
Writer->WriteObjectStart(TEXT("IndividualTagDeltaSizes"));
|
|
for (const TPair<FString, int64>& Pair : TagDeltaImpact)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart(TEXT("CompareTagSetDeltaSizes"));
|
|
for (const TPair<FString, int64>& Pair : CompareTagSetDeltaImpact)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteObjectStart(TEXT("CompareTagSetTempDiskSpaceReqs"));
|
|
for (const TPair<FString, int64>& Pair : CompareTagSetTempDiskSpaceReqs)
|
|
{
|
|
Writer->WriteValue(Pair.Key, Pair.Value);
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->WriteArrayStart(TEXT("InstallTimeCoefficients"));
|
|
for (const double& InstallTimeCoefficient : InstallTimeCoefficients)
|
|
{
|
|
Writer->WriteValue(InstallTimeCoefficient);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
Writer->Close();
|
|
bSuccess = FFileHelper::SaveStringToFile(JsonOutput, *Configuration.OutputFilePath);
|
|
if (!bSuccess)
|
|
{
|
|
UE_LOG(LogDiffManifests, Error, TEXT("Could not save output to %s"), *Configuration.OutputFilePath);
|
|
}
|
|
}
|
|
}
|
|
bShouldRun = false;
|
|
return bSuccess;
|
|
}
|
|
|
|
void FDiffManifests::HandleDownloadComplete(int32 RequestId, const FDownloadRef& Download)
|
|
{
|
|
TPromise<FBuildPatchAppManifestPtr>* RelevantPromisePtr = RequestId == RequestIdManifestA ? &PromiseManifestA : RequestId == RequestIdManifestB ? &PromiseManifestB : nullptr;
|
|
if (RelevantPromisePtr != nullptr)
|
|
{
|
|
if (Download->ResponseSuccessful())
|
|
{
|
|
Async(EAsyncExecution::ThreadPool, [Download, RelevantPromisePtr]()
|
|
{
|
|
FBuildPatchAppManifestPtr Manifest = MakeShareable(new FBuildPatchAppManifest());
|
|
if (!Manifest->DeserializeFromData(Download->GetData()))
|
|
{
|
|
Manifest.Reset();
|
|
}
|
|
RelevantPromisePtr->SetValue(Manifest);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
RelevantPromisePtr->SetValue(FBuildPatchAppManifestPtr());
|
|
}
|
|
}
|
|
}
|
|
|
|
IDiffManifests* FDiffManifestsFactory::Create(const FDiffManifestsConfiguration& Configuration)
|
|
{
|
|
return new FDiffManifests(Configuration);
|
|
}
|
|
} |