Files
UnrealEngine/Engine/Source/Runtime/InstallBundleManager/Public/InstallBundleUtils.h
2025-05-18 13:04:45 +08:00

657 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Async/AsyncWork.h"
#include "Containers/Array.h"
#include "Containers/Map.h"
#include "Containers/SparseArray.h"
#include "Containers/Ticker.h"
#include "Containers/UnrealString.h"
#include "CoreMinimal.h"
#include "Delegates/IDelegateInstance.h"
#include "HAL/Platform.h"
#include "HAL/PlatformMisc.h"
#include "InstallBundleTypes.h"
#include "Misc/EmbeddedCommunication.h"
#include "Serialization/JsonSerializerMacros.h"
#include "Stats/Stats.h"
#include "Templates/Function.h"
#include "Templates/IsEnum.h"
#include "Templates/SharedPointer.h"
#include "Templates/UniquePtr.h"
#include "Templates/UnrealTemplate.h"
#include "UObject/NameTypes.h"
#include "Internationalization/Regex.h"
class FQueuedThreadPool;
namespace InstallBundleUtil
{
// Returns the app version in the same format as BPS versions
INSTALLBUNDLEMANAGER_API FString GetAppVersion();
INSTALLBUNDLEMANAGER_API bool HasInternetConnection(ENetworkConnectionType ConnectionType);
INSTALLBUNDLEMANAGER_API bool SplitHostUrl(const FStringView Url, FStringView& OutHost, FStringView& OutRemainder);
INSTALLBUNDLEMANAGER_API const TCHAR* GetInstallBundlePauseReason(EInstallBundlePauseFlags Flags);
INSTALLBUNDLEMANAGER_API const FString& GetInstallBundleSectionPrefix();
INSTALLBUNDLEMANAGER_API bool GetConfiguredBundleSources(TArray<FString>& OutSources, TMap<FString, FString>& OutFallbackSources);
// returns true if the given BundleName exists in the InstallBundle.ini config
INSTALLBUNDLEMANAGER_API bool HasInstallBundleInConfig(const FString& BundleName);
struct FConfigMountOptions
{
bool bWithSoftReferences = false;
};
INSTALLBUNDLEMANAGER_API bool GetMountOptionsFromConfig(const FStringView BundleName, FConfigMountOptions& OutMountOptions);
// Returns true
INSTALLBUNDLEMANAGER_API bool AllInstallBundlePredicate(const FConfigFile& InstallBundleConfig, const FString& Section);
// Returns true if this bundle should be included in a platform package
INSTALLBUNDLEMANAGER_API bool IsPlatformInstallBundlePredicate(const FConfigFile& InstallBundleConfig, const FString& Section);
// Returns an ordered regex list used to map files to bundles
// SectionPredicate can be used to exclude non-relavent bundles from the list
INSTALLBUNDLEMANAGER_API TArray<TPair<FString, TArray<FRegexPattern>>> LoadBundleRegexFromConfig(
const FConfigFile& InstallBundleConfig,
TFunctionRef<bool(const FConfigFile& InstallBundleConfig, const FString& Section)> SectionPredicate = AllInstallBundlePredicate);
// Finds a matching install bundle for a given path
INSTALLBUNDLEMANAGER_API bool MatchBundleRegex(
const TArray<TPair<FString, TArray<FRegexPattern>>>& BundleRegexList,
const FString& Path,
FString& OutBundleName);
constexpr float MinimumBundleWeight = 0.05f;
// It would really be nice to have these in core
template<class EnumType>
constexpr auto& CastAsUnderlying(EnumType &Type)
{
static_assert(TIsEnum<EnumType>::Value, "");
using UnderType = std::underlying_type_t<EnumType>;
return *reinterpret_cast<UnderType*>(&Type);
}
template<class EnumType>
constexpr const auto& CastAsUnderlying(const EnumType &Type)
{
static_assert(TIsEnum<EnumType>::Value, "");
using UnderType = std::underlying_type_t<EnumType>;
return *reinterpret_cast<const UnderType*>(&Type);
}
template<class EnumType>
constexpr auto CastToUnderlying(EnumType Type)
{
static_assert(TIsEnum<EnumType>::Value, "");
using UnderType = std::underlying_type_t<EnumType>;
return static_cast<UnderType>(Type);
}
template<typename EnumType, typename StringArrType, EnumType EnumCount = EnumType::Count>
const TCHAR* TLexToString(EnumType E, const StringArrType& Strings)
{
constexpr auto Count = InstallBundleUtil::CastToUnderlying(EnumCount);
static_assert(Count == UE_ARRAY_COUNT(Strings), "");
auto Idx = InstallBundleUtil::CastToUnderlying(E);
if (Idx >= 0 && Idx < Count)
{
return Strings[Idx];
}
else
{
return TEXT("");
}
}
// Keep the engine awake via RAII when running as an embedded app
class FInstallBundleManagerKeepAwake : public FEmbeddedKeepAwake
{
static INSTALLBUNDLEMANAGER_API FName Tag;
static INSTALLBUNDLEMANAGER_API FName TagWithRendering;
public:
FInstallBundleManagerKeepAwake(bool bNeedsRendering = false)
: FEmbeddedKeepAwake(bNeedsRendering ? TagWithRendering : Tag, bNeedsRendering) {}
};
class FInstallBundleManagerScreenSaverControl
{
static INSTALLBUNDLEMANAGER_API bool bDidDisableScreensaver;
static INSTALLBUNDLEMANAGER_API int DisableCount;
static INSTALLBUNDLEMANAGER_API void IncDisable();
static INSTALLBUNDLEMANAGER_API void DecDisable();
public:
FInstallBundleManagerScreenSaverControl()
{
IncDisable();
}
~FInstallBundleManagerScreenSaverControl()
{
DecDisable();
}
FInstallBundleManagerScreenSaverControl(const FInstallBundleManagerScreenSaverControl& Other)
{
IncDisable();
}
FInstallBundleManagerScreenSaverControl(FInstallBundleManagerScreenSaverControl&& Other) = default;
FInstallBundleManagerScreenSaverControl& operator=(const FInstallBundleManagerScreenSaverControl& Other) = default;
FInstallBundleManagerScreenSaverControl& operator=(FInstallBundleManagerScreenSaverControl&& Other)
{
DecDisable();
return *this;
}
};
// Helper to safely suppress installer analytics that will restore previous state when going out of scope
struct FInstallBundleSuppressAnalytics : FNoncopyable
{
INSTALLBUNDLEMANAGER_API FInstallBundleSuppressAnalytics();
INSTALLBUNDLEMANAGER_API ~FInstallBundleSuppressAnalytics();
INSTALLBUNDLEMANAGER_API void Enable();
INSTALLBUNDLEMANAGER_API void Disable();
static INSTALLBUNDLEMANAGER_API bool IsEnabled();
private:
bool bIsEnabled;
};
class FInstallBundleWork : public FNonAbandonableTask
{
public:
FInstallBundleWork() = default;
FInstallBundleWork(TUniqueFunction<void()> InWork, TUniqueFunction<void()> InOnComplete)
: WorkFunc(MoveTemp(InWork))
, OnCompleteFunc(MoveTemp(InOnComplete))
{}
void DoWork()
{
if (WorkFunc)
{
WorkFunc();
}
}
void CallOnComplete()
{
if (OnCompleteFunc)
{
OnCompleteFunc();
}
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FInstallBundleWork, STATGROUP_ThreadPoolAsyncTasks);
}
private:
TUniqueFunction<void()> WorkFunc;
TUniqueFunction<void()> OnCompleteFunc;
};
using FInstallBundleTask = FAsyncTask<FInstallBundleWork>;
using FAutoDeleteInstallBundleTask = FAutoDeleteAsyncTask<FInstallBundleWork>;
INSTALLBUNDLEMANAGER_API void StartInstallBundleAsyncIOTask(TUniqueFunction<void()> WorkFunc);
INSTALLBUNDLEMANAGER_API void StartInstallBundleAsyncIOTask(FQueuedThreadPool* ThreadPool, TUniqueFunction<void()> WorkFunc);
INSTALLBUNDLEMANAGER_API void StartInstallBundleAsyncIOTask(TArray<TUniquePtr<FInstallBundleTask>>& Tasks, TUniqueFunction<void()> WorkFunc, TUniqueFunction<void()> OnComplete);
INSTALLBUNDLEMANAGER_API void StartInstallBundleAsyncIOTask(FQueuedThreadPool* ThreadPool, TArray<TUniquePtr<FInstallBundleTask>>& Tasks, TUniqueFunction<void()> WorkFunc, TUniqueFunction<void()> OnComplete);
INSTALLBUNDLEMANAGER_API void FinishInstallBundleAsyncIOTasks(TArray<TUniquePtr<FInstallBundleTask>>& Tasks);
INSTALLBUNDLEMANAGER_API void CleanupInstallBundleAsyncIOTasks(TArray<TUniquePtr<FInstallBundleTask>>& Tasks);
struct FContentRequestStateStats
{
double StartTime = 0.0;
double EndTime = 0.0;
uint64 DataSize = 0;
bool bOpen = true;
double GetElapsedTime() const
{
return (EndTime > StartTime) ? (EndTime - StartTime) : 0.0;
}
};
struct FContentRequestStats
{
double StartTime = 0.0;
double EndTime = 0.0;
bool bOpen = true;
TMap<FString, FContentRequestStateStats> StateStats;
double GetElapsedTime() const
{
return (EndTime > StartTime) ? (EndTime - StartTime) : 0.0;
}
};
class FContentRequestStatsMap
{
private:
TMap<FName, InstallBundleUtil::FContentRequestStats> StatsMap;
public:
INSTALLBUNDLEMANAGER_API void StatsBegin(FName BundleName);
INSTALLBUNDLEMANAGER_API void StatsEnd(FName BundleName);
INSTALLBUNDLEMANAGER_API void StatsBegin(FName BundleName, const TCHAR* State);
INSTALLBUNDLEMANAGER_API void StatsEnd(FName BundleName, const TCHAR* State, uint64 DataSize = 0);
INSTALLBUNDLEMANAGER_API void StatsReset(FName BundleName);
const TMap<FName, InstallBundleUtil::FContentRequestStats>& GetMap() { return StatsMap; }
};
namespace PersistentStats
{
//Stats used to store timing information in FPersistentTimerData format
//_Real stats account for total time since a stat was started regardless of the app's state (shutdown, in foreground, etc.)
//_FG stats account for time while the app is active
//_BG stats are an estimate of time while the app is inactive (backgrounded, closed, etc.)
// *_BG stats are an estimate as we can not account for unexpected crashes, etc.
enum class ETimingStatNames : uint8
{
TotalTime_Real
, TotalTime_FG
, TotalTime_BG
, ChunkDBDownloadTime_Real
, ChunkDBDownloadTime_FG
, ChunkDBDownloadTime_BG
, InstallTime_Real
, InstallTime_FG
, InstallTime_BG
, PSOTime_Real
, PSOTime_FG
, PSOTime_BG
, NumStatNames //should always be final entry
};
//Stats used to store count information in an int format
enum class ECountStatNames : uint8
{
NumResumedFromBackground
, NumResumedFromLaunch
, NumBackgrounded
, NumStatNames //should always be final entry
};
INSTALLBUNDLEMANAGER_API const FString& LexToString(ETimingStatNames InType);
INSTALLBUNDLEMANAGER_API const FString& LexToString(ECountStatNames InType);
//Returns if the timer is in the _Real set
INSTALLBUNDLEMANAGER_API bool IsTimerReal(ETimingStatNames InTimer);
//Returns if the timer is in the _FG set
INSTALLBUNDLEMANAGER_API bool IsTimerFG(ETimingStatNames InTimer);
//Returns if the timer is in the _BG set
INSTALLBUNDLEMANAGER_API bool IsTimerBG(ETimingStatNames InTimer);
//Gets the _Real timer when supplied with the corresponding _FG or _BG timer name
INSTALLBUNDLEMANAGER_API ETimingStatNames GetAssociatedRealTimerName(ETimingStatNames InTimerType);
//Gets the _FG timer when supplied with the corresponding _Real or _BG timer name
INSTALLBUNDLEMANAGER_API ETimingStatNames GetAssociatedFGTimerName(ETimingStatNames InTimerType);
//Gets the _BG timer when supplied with the corresponding _Real or _FG timer name
INSTALLBUNDLEMANAGER_API ETimingStatNames GetAssociatedBGTimerName(ETimingStatNames InTimerType);
class FPersistentTimerData : public FJsonSerializable
{
public:
BEGIN_JSON_SERIALIZER
JSON_SERIALIZE("LastUpdateTime", LastUpdateTime);
JSON_SERIALIZE("TotalTime", CurrentValue);
END_JSON_SERIALIZER
public:
FPersistentTimerData()
: LastUpdateTime(0.)
, CurrentValue(0.)
{}
double LastUpdateTime;
double CurrentValue;
};
//Both ContentRequests and Individual Bundles use this to track data respective to themselves.
//Keeps us from having to write serialization / loading data for both cases that would be the same
class FPersistentStatsBase : public FJsonSerializable
{
public:
//We should not ever really be serializing this base class. Kept it here for reference.
INSTALLBUNDLEMANAGER_API BEGIN_JSON_SERIALIZER
JSON_SERIALIZE("AnalyticsSessionID", AnalyticsSessionID);
JSON_SERIALIZE_MAP("CountStats", CountStatMap);
JSON_SERIALIZE_MAP_SERIALIZABLE("TimingStats", TimingStatsMap, FPersistentTimerData);
END_JSON_SERIALIZER
public:
virtual ~FPersistentStatsBase() {}
//Tries to load persistent stats from disk and determine if we need to resume our previous persistent session or start a new one
INSTALLBUNDLEMANAGER_API void StatsBegin(const FString& ExpectedAnalyticsID, bool bForceResetData = false);
//Tries to set this stat pool as "inactive". By default also stop all active timers.
// NOTE: If you don't stop all active timers future calls to UpdateTimingStat will still increment this value and save it off!
INSTALLBUNDLEMANAGER_API void StatsEnd(bool bStopAllActiveTimers = true);
INSTALLBUNDLEMANAGER_API bool IsTimingStatStarted(ETimingStatNames StatToUpdate) const;
INSTALLBUNDLEMANAGER_API void StartTimingStat(ETimingStatNames StatToUpdate);
INSTALLBUNDLEMANAGER_API void StopTimingStat(ETimingStatNames StatToUpdate, bool UpdateTimerOnStop = true);
INSTALLBUNDLEMANAGER_API void UpdateTimingStat(ETimingStatNames StatToUpdate);
INSTALLBUNDLEMANAGER_API void UpdateAllActiveTimers();
INSTALLBUNDLEMANAGER_API void StopAllActiveTimers();
bool IsActive() const { return bIsActive; }
bool IsDirty() const { return bIsDirty; }
INSTALLBUNDLEMANAGER_API void IncrementCountStat(PersistentStats::ECountStatNames StatToUpdate);
INSTALLBUNDLEMANAGER_API bool HasTimingStat(ETimingStatNames StatToCheck) const;
INSTALLBUNDLEMANAGER_API bool HasCountStat(ECountStatNames StatToCheck) const;
INSTALLBUNDLEMANAGER_API const FPersistentTimerData* GetTimingStatData(ETimingStatNames StatToGet) const;
INSTALLBUNDLEMANAGER_API const int* GetCountStatData(ECountStatNames StatToGet) const;
//Saves the persistent stat data to disk in the location returned by GetFullPathForStatFile()
//Returns True if that save succeeds and false otherwise.
INSTALLBUNDLEMANAGER_API bool SaveStatsToDisk();
//Function that allows you to load stats from disk. Returns true if stats were either loaded from disk now, or previously were loaded from disk.
//NOTE: This does not begin stat collection, so you should still call StatsBegin on this before handling any stats!
INSTALLBUNDLEMANAGER_API bool LoadStatsFromDisk();
protected:
INSTALLBUNDLEMANAGER_API void ResetStats(const FString& NewAnalyticsSessionID);
//Called after we load data but before we process any of it.
//Useful for fixing up the data loaded from disk before it changes or is acted upon by anything else
//NOTE: A call to LoadStatsFromDisk will not necessarily call this! It will only be called when data is actually pulled from
//the disk, and its possible to skip pulling data from the disk!
INSTALLBUNDLEMANAGER_API void OnLoadingDataFromDisk();
//Helper to try and reconcile offline and active timers after we load data from disk
INSTALLBUNDLEMANAGER_API void HandleTimerStatsAfterDataLoad();
//Protected so we can only construct the derived versions of this Base class
FPersistentStatsBase()
: TimingStatsMap()
, CountStatMap()
, AnalyticsSessionID()
, bIsActive(false)
, bIsDirty(false)
, bHasLoadedFromDisk(false)
{
}
protected:
//Serialized Stat Data
TMap<FString, FPersistentTimerData> TimingStatsMap;
TMap<FString, int> CountStatMap;
FString AnalyticsSessionID;
//We don't serialize these as they are tracking behavior and not stats
bool bIsActive;
bool bIsDirty;
bool bHasLoadedFromDisk;
//Static Methods
public:
//Helper function that generates a basic ExpectedAnalyticsID from device and UE information
static INSTALLBUNDLEMANAGER_API const FString GetBaseExpectedAnalyticsID();
//Pure Virtual Methods
public:
//Returns Full Path to look for our persistent stat cache
virtual const FString GetFullPathForStatFile() const = 0;
};
//Each bundles persistent stats
class FBundlePersistentStats : public FPersistentStatsBase
{
public:
INSTALLBUNDLEMANAGER_API BEGIN_JSON_SERIALIZER
JSON_SERIALIZE("BundleName", BundleName);
JSON_SERIALIZE("AnalyticsSessionID", AnalyticsSessionID);
JSON_SERIALIZE_MAP("CountStats", CountStatMap);
JSON_SERIALIZE_MAP_SERIALIZABLE("TimingStats", TimingStatsMap, FPersistentTimerData);
END_JSON_SERIALIZER
private:
//Remove default constructor as we require a BundleName in the constructor
FBundlePersistentStats() = delete;
//Used in our full path and should signify the FString version of the FName of this bundle we are tracking
FString BundleName;
public:
FBundlePersistentStats(FName BundleNameIn)
: BundleName(BundleNameIn.ToString())
{
}
virtual ~FBundlePersistentStats() {}
//Implementation for FInstallBundleStatsContainerBase
public:
INSTALLBUNDLEMANAGER_API virtual const FString GetFullPathForStatFile() const override;
};
//Tracks a set of bundles and stats for a named session. This way you can have stats across multiple bundle installs grouped by some naming convention
class FSessionPersistentStats : public FPersistentStatsBase
{
public:
INSTALLBUNDLEMANAGER_API BEGIN_JSON_SERIALIZER
JSON_SERIALIZE("SessionName", SessionName);
JSON_SERIALIZE_ARRAY("RequiredBundles", RequiredBundles);
JSON_SERIALIZE("AnalyticsSessionID", AnalyticsSessionID);
JSON_SERIALIZE_MAP("CountStats", CountStatMap);
JSON_SERIALIZE_MAP_SERIALIZABLE("TimingStats", TimingStatsMap, FPersistentTimerData);
END_JSON_SERIALIZER
private:
//Remove default constructor as we require a ContentRequestName in the constructor
FSessionPersistentStats() = delete;
//Used in our full path and should signify some identifier for a given ContentRequest
FString SessionName;
//Used to keep track of all bundles that are part of this content request
TArray<FString> RequiredBundles;
public:
FSessionPersistentStats(const FString& ContentRequestNameIn)
: SessionName(ContentRequestNameIn)
, RequiredBundles()
{
}
virtual ~FSessionPersistentStats() {}
INSTALLBUNDLEMANAGER_API void AddRequiredBundles(const TArray<FString>& RequiredBundlesToAdd);
INSTALLBUNDLEMANAGER_API void AddRequiredBundles(const TArray<FName>& RequiredBundlesToAdd);
//Removes all Required Bundle Names except the ones in the NewRequiredBundlesList. If no NewRequiredBundlesList is supplied, just erases all RequiredBundles.
//This includes ones loaded from the persistent cache from previous runs. This re-saves the persistent cache file so they will be removed completely.
INSTALLBUNDLEMANAGER_API void ResetRequiredBundles(const TArray<FString>& NewRequiredBundles = TArray<FString>());
//Populates the passed in TArray with all the required bundles' names
INSTALLBUNDLEMANAGER_API void GetRequiredBundles(TArray<FString>& OutRequiredBundles) const;
//Implementation for FInstallBundleStatsContainerBase
public:
INSTALLBUNDLEMANAGER_API virtual const FString GetFullPathForStatFile() const override;
};
//Helper class to handle holding a collection of Bundle and Session stats.
class FPersistentStatContainerBase
{
public:
INSTALLBUNDLEMANAGER_API FPersistentStatContainerBase();
INSTALLBUNDLEMANAGER_API virtual ~FPersistentStatContainerBase();
//NOTE! These 2 functions are not virtual as we call them in the construct/deconstructor!
INSTALLBUNDLEMANAGER_API void InitializeBase();
INSTALLBUNDLEMANAGER_API void ShutdownBase();
//Starts Bundle Persistent Stat Tracking for the given BundleName. Will automatically load applicable data from the disk and reset that cache
//if the ExpectedAnalyticsID doesn't match the one previously serialized.
//Uses FInstallBundleStatsContainerBase::GetBaseExpectedAnalyticsID if an ExpectedAnalyticsID is not passed in or empty
INSTALLBUNDLEMANAGER_API virtual void StartBundlePersistentStatTracking(FName BundleName, const FString& ExpectedAnalyticsID = FString(), bool bForceResetStatData = false);
//Starts Session Persistent Stat Tracking under the given SessionName. Will automatically load applicable data from the disk and reset that cache
//if the ExpectedAnalyticsID doesn't match the one previously serialized.
//Uses FInstallBundleStatsContainerBase::GetBaseExpectedAnalyticsID if an ExpectedAnalyticsID is not passed in or empty
INSTALLBUNDLEMANAGER_API virtual void StartSessionPersistentStatTracking(const FString& SessionName, const TArray<FName>& RequiredBundles = TArray<FName>(), const FString& ExpectedAnalyticsID = FString(), bool bForceResetStatData = false);
INSTALLBUNDLEMANAGER_API virtual void StopBundlePersistentStatTracking(FName BundleName, bool bStopAllActiveTimers = true);
INSTALLBUNDLEMANAGER_API virtual void StopSessionPersistentStatTracking(const FString& SessionName, bool bStopAllActiveTimers = true);
INSTALLBUNDLEMANAGER_API virtual void StartBundlePersistentStatTimer(FName BundleName, ETimingStatNames TimerToStart);
INSTALLBUNDLEMANAGER_API virtual void StartSessionPersistentStatTimer(const FString& SessionName, ETimingStatNames TimerToStart);
INSTALLBUNDLEMANAGER_API virtual void StopBundlePersistentStatTimer(FName BundleName, ETimingStatNames TimerToStop);
INSTALLBUNDLEMANAGER_API virtual void StopSessionPersistentStatTimer(const FString& SessionName, ETimingStatNames TimerToStop);
INSTALLBUNDLEMANAGER_API virtual void UpdateBundlePersistentStatTimer(FName BundleName, ETimingStatNames TimerToUpdate);
INSTALLBUNDLEMANAGER_API virtual void UpdateSessionPersistentStatTimer(const FString& SessionName, ETimingStatNames TimerToUpdate);
INSTALLBUNDLEMANAGER_API virtual void IncrementBundlePersistentCounter(FName BundleName, ECountStatNames CounterToUpdate);
INSTALLBUNDLEMANAGER_API virtual void IncrementSessionPersistentCounter(const FString& SessionName, ECountStatNames CounterToUpdate);
INSTALLBUNDLEMANAGER_API virtual const FBundlePersistentStats* GetBundleStat(FName BundleName) const;
INSTALLBUNDLEMANAGER_API virtual const FSessionPersistentStats* GetSessionStat(const FString& SessionName) const;
INSTALLBUNDLEMANAGER_API virtual void SaveAllDirtyStatsToDisk();
//Deletes persistent stat information from our stats for this Session/Bundle to reduce memory useage
INSTALLBUNDLEMANAGER_API virtual void RemoveSessionStats(const FString& SessionName);
INSTALLBUNDLEMANAGER_API virtual void RemoveBundleStats(FName BundleName);
protected:
INSTALLBUNDLEMANAGER_API virtual bool Tick(float dt);
INSTALLBUNDLEMANAGER_API virtual void ResetTimerUpdate();
INSTALLBUNDLEMANAGER_API virtual void ResetDirtyStatUpdate();
INSTALLBUNDLEMANAGER_API virtual void UpdateAllBundlesActiveTimers();
INSTALLBUNDLEMANAGER_API virtual void UpdateAllSessionActiveTimers();
INSTALLBUNDLEMANAGER_API virtual void OnApp_EnteringBackground();
INSTALLBUNDLEMANAGER_API virtual void OnApp_EnteringForeground();
INSTALLBUNDLEMANAGER_API virtual void OnBackground_HandleBundleStats();
INSTALLBUNDLEMANAGER_API virtual void OnForeground_HandleBundleStats();
INSTALLBUNDLEMANAGER_API virtual void OnBackground_HandleSessionStats();
INSTALLBUNDLEMANAGER_API virtual void OnForeground_HandleSessionStats();
//Called whenever we start a timer
//NOTE: doesn't get called by timer's being Start/Stopped for bShouldAutoHandleFGBGStats
INSTALLBUNDLEMANAGER_API virtual void OnTimerStartedForStat(FPersistentStatsBase& BundleStatForTimer, ETimingStatNames TimerStarted);
//Called whenever we stop a timer
//NOTE: doesn't get called by timer's being Start/Stopped for bShouldAutoHandleFGBGStats
INSTALLBUNDLEMANAGER_API virtual void OnTimerStoppedForStat(FPersistentStatsBase& BundleStatForTimer, ETimingStatNames TimerStarted);
//Stops active foreground timers when going to background and starts applicable background version
//Also increments background counters
INSTALLBUNDLEMANAGER_API virtual void UpdateStatsForBackground(FPersistentStatsBase& StatToUpdate);
//Stops active background timers and resumes applicable foreground timers
//Also increments foreground counters
INSTALLBUNDLEMANAGER_API virtual void UpdateStatsForForeground(FPersistentStatsBase& StatToUpdate);
//Goes through this Session's RequiredBundles and loads data from disk for those bundles. Allows us to make sure we have
//full data in memory from disk without having to know the previous runs AnalyticsID to call StatsBegin on that bundle.
//NOTE: You should still call StatsBegin on the listed bundles before Starting/Incrementing/ETC any analytics! This just
//ensures that the previous run data exists for this given session!
INSTALLBUNDLEMANAGER_API virtual void LoadRequiredBundleDataFromDiskForSession(const FString& SessionName);
protected:
TMap<FName, FBundlePersistentStats> PerBundlePersistentStatMap;
TMap<FString, FSessionPersistentStats> SessionPersistentStatMap;
FTSTicker::FDelegateHandle TickHandle;
FDelegateHandle OnApp_EnteringForegroundHandle;
FDelegateHandle OnApp_EnteringBackgroundHandle;
//internal counter used to track when we need to run our timer update during tick. Requires bShouldAutoUpdateTimersInTick to be set to true to effect anything.
//reset to TimerAutoUpdateRate whenever we trigger the auto update.
float TimerAutoUpdateTimeRemaining;
//internal counter used to track when we need to update all of our dirty stats. Requires bShouldSaveDirtyStatsOnTick to be set to true to effect anything.
//reset to TimerAutoUpdateRate whenever we trigger the dirty stat update
float TimerDirtyStatUpdateTimeRemaining;
/****** Config Values ******/
// All values are loaded from GEngineIni in [InstallBundleManager.PersistentStatSettings]
/****** Timer Auto Update Settings ****/
//These settings determine if we auto-update our Timer stats on a tick. Without an auto-update timers would only update on Start / Stop potentially leaving us vulnerable to
//dropping a lot of timing information in the event of a crash. By setting these values you ensure your data is accurate to some interval.
//
//bShouldAutoUpdateTimersInTick -- Determines if the auto update feature is on at all. Rate of auto timer updates is determined by TimerAutoUpdateRate.
//
//TimerAutoUpdateRate -- Determines the rate we trigger the update in seconds. <=0 means update every tick.
//
bool bShouldAutoUpdateTimersInTick;
float TimerAutoUpdateRate;
/****** Stat Save To Disk Settings ****/
//These settings determine how we handle saving our stats to disk. Outside of these settings, stats ALWAYS try to save to disk on Start or End, but these settings
//let us also configure other times we may want to save the data to disk.
//
//bShouldSaveDirtyStatsOnTick -- If this is set to true then all stats that have bIsDirty set to true will try to save to disk every X seconds. Update rate determined by DirtyStatSaveToDiskRate
// This way we will not update a given stat more then once a tick to disk.
//
//DirtyStatSaveToDiskRate -- Determines how often we update our dirty stats in seconds. <=0 means update every tick.
bool bShouldSaveDirtyStatsOnTick;
float DirtyStatSaveToDiskRate;
//Determines if we should automatically Start,Stop,and Update the _FG and _BG versions of our timer stats.
//If true you only have to call Start/Stop on the _Real versions of all timers and we will automatically
//Start and Stop the _FG and _BG timers when going to background or returning to foreground to keep
//their data accurate.
bool bShouldAutoHandleFGBGStats;
};
}
struct IBundleSourceContentRequestSharedContext
{
virtual ~IBundleSourceContentRequestSharedContext() {}
};
struct FContentRequestSharedContext
{
FContentRequestSharedContext() = default;
FContentRequestSharedContext(const FContentRequestSharedContext& Other) = delete;
FContentRequestSharedContext(FContentRequestSharedContext&& Other) = default;
FContentRequestSharedContext& operator=(const FContentRequestSharedContext& Other) = delete;
FContentRequestSharedContext& operator=(FContentRequestSharedContext&& Other) = default;
TMap<FInstallBundleSourceType, TUniquePtr<IBundleSourceContentRequestSharedContext>> BundleSourceSharedContext;
};
using FContentRequestSharedContextPtr = TSharedPtr<FContentRequestSharedContext>;
}