Files
UnrealEngine/Engine/Plugins/Online/OnlineSubsystem/Source/Public/Interfaces/OnlineStatsInterface.h
2025-05-18 13:04:45 +08:00

310 lines
9.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Online/CoreOnline.h"
#include "OnlineKeyValuePair.h"
struct FOnlineError;
using FOnlineStatValue = FVariantData;
ONLINESUBSYSTEM_API DECLARE_LOG_CATEGORY_EXTERN(LogOnlineStats, Log, All);
#define UE_LOG_ONLINE_STATS(Verbosity, Format, ...) \
{ \
UE_LOG(LogOnlineStats, Verbosity, TEXT("%s%s"), ONLINE_LOG_PREFIX, *FString::Printf(Format, ##__VA_ARGS__)); \
}
/** Object to represent a new stat value and how to use it in relation to previous values */
struct FOnlineStatUpdate
{
public:
/** How should we modify this stat in relation to previous values? */
enum class EOnlineStatModificationType : uint8
{
/** Let the backend decide how to update this value (or set to new value if backend does not decide) */
Unknown,
/** Add the new value to the previous value */
Sum,
/** Overwrite previous value with the new value */
Set,
/** Only replace previous value if new value is larger */
Largest,
/** Only replace previous value if new value is smaller */
Smallest
};
/** Construct an empty FOnlineStatUpdate */
FOnlineStatUpdate()
: NewValue(0)
, ModificationType(EOnlineStatModificationType::Unknown)
{
}
/** Construct a new FOnlineStatUpdate from a value */
FOnlineStatUpdate(const FOnlineStatValue& InNewValue, const EOnlineStatModificationType InModificationType)
: NewValue(InNewValue)
, ModificationType(InModificationType)
{
}
FOnlineStatUpdate(FOnlineStatValue&& InNewValue, const EOnlineStatModificationType InModificationType)
: NewValue(MoveTemp(InNewValue))
, ModificationType(InModificationType)
{
}
/** Copy/Assignment construction */
FOnlineStatUpdate(FOnlineStatUpdate&& Other) = default;
FOnlineStatUpdate(const FOnlineStatUpdate& Other) = default;
FOnlineStatUpdate& operator=(FOnlineStatUpdate&& Other) = default;
FOnlineStatUpdate& operator=(const FOnlineStatUpdate& Other) = default;
/** Set this stat update to a new value/modification type */
void Set(const FOnlineStatValue& InNewValue, const EOnlineStatModificationType InModificationType)
{
NewValue = InNewValue;
ModificationType = InModificationType;
}
void Set(FOnlineStatValue&& InNewValue, const EOnlineStatModificationType InModificationType)
{
NewValue = MoveTemp(InNewValue);
ModificationType = InModificationType;
}
/** Get the current value */
const FOnlineStatValue& GetValue() const
{
return NewValue;
}
/** Get the current modification type */
EOnlineStatModificationType GetModificationType() const
{
return ModificationType;
}
/** Get the current type of stat (int32, float, etc) */
EOnlineKeyValuePairDataType::Type GetType() const
{
return NewValue.GetType();
}
/** Get the value of this stat as a string */
FString ToString() const
{
return NewValue.ToString();
}
/** Returns true if this stat is numeric */
bool IsNumeric() const
{
return NewValue.IsNumeric();
}
private:
template<typename T>
T SwitchOnNumeric(const T& Left, const T& Right, EOnlineStatModificationType ModType) const
{
switch (ModificationType)
{
case EOnlineStatModificationType::Sum:
return Left + Right;
case EOnlineStatModificationType::Set:
return Right;
case EOnlineStatModificationType::Largest:
return (Left > Right) ? Left : Right;
case EOnlineStatModificationType::Smallest:
return (Left > Right) ? Right : Left;
case EOnlineStatModificationType::Unknown:
default:
return Right; //default- new
}
}
public:
/** Takes the value saved here and its corresponding ModificationType and outputs the new final value. New final value should be directly set onto the preexisting value */
FOnlineStatValue GetResult(const FOnlineStatValue& Other) const
{
if(Other.GetType() != NewValue.GetType())
{
// mismatched- just return the new value
return NewValue;
}
switch(Other.GetType())
{
case EOnlineKeyValuePairDataType::Int32:
{
int32 Old, New;
Other.GetValue(Old);
NewValue.GetValue(New);
FVariantData ReturnData;
ReturnData.SetValue(SwitchOnNumeric(Old, New, ModificationType));
return ReturnData;
}
case EOnlineKeyValuePairDataType::Int64:
{
int64 Old, New;
Other.GetValue(Old);
NewValue.GetValue(New);
FVariantData ReturnData;
ReturnData.SetValue(SwitchOnNumeric(Old,New, ModificationType));
return ReturnData;
}
case EOnlineKeyValuePairDataType::UInt32:
{
uint32 Old, New;
Other.GetValue(Old);
NewValue.GetValue(New);
FVariantData ReturnData;
ReturnData.SetValue(SwitchOnNumeric(Old, New, ModificationType));
return ReturnData;
}
case EOnlineKeyValuePairDataType::UInt64:
{
uint64 Old, New;
Other.GetValue(Old);
NewValue.GetValue(New);
FVariantData ReturnData;
ReturnData.SetValue(SwitchOnNumeric(Old, New, ModificationType));
return ReturnData;
}
case EOnlineKeyValuePairDataType::Float:
{
float Old, New;
Other.GetValue(Old);
NewValue.GetValue(New);
FVariantData ReturnData;
ReturnData.SetValue(SwitchOnNumeric(Old, New, ModificationType));
return ReturnData;
}
case EOnlineKeyValuePairDataType::Double:
{
double Old, New;
Other.GetValue(Old);
NewValue.GetValue(New);
FVariantData ReturnData;
ReturnData.SetValue(SwitchOnNumeric(Old, New, ModificationType));
return ReturnData;
}
case EOnlineKeyValuePairDataType::String:
default:
{
return NewValue; // only valid operation here is Set
}
}
}
private:
FOnlineStatValue NewValue;
EOnlineStatModificationType ModificationType;
};
template <class TStatType>
struct FOnlineUserStatsPair
{
public:
FOnlineUserStatsPair(const FUniqueNetIdRef InAccount)
: Account(InAccount)
{
check(Account->IsValid());
}
FOnlineUserStatsPair(const FUniqueNetIdRef InAccount, const TMap<FString, TStatType>& InStats)
: Account(InAccount)
, Stats(InStats)
{
check(Account->IsValid());
}
FOnlineUserStatsPair(const FUniqueNetIdRef InAccount, TMap<FString, TStatType>&& InStats)
: Account(InAccount)
, Stats(MoveTemp(InStats))
{
check(Account->IsValid());
}
public:
FUniqueNetIdRef Account;
TMap<FString, TStatType> Stats;
using StatType = TStatType;
};
/** Delegate called when a stat update has completed, with a ResultState parameter to represent success or failure */
DECLARE_DELEGATE_OneParam(FOnlineStatsUpdateStatsComplete, const FOnlineError& /*ResultState*/);
/** A pair of a user and an array of their stats */
using FOnlineStatsUserStats = FOnlineUserStatsPair<FOnlineStatValue>;
/** Delegate called when a user's stats have finished being queried, with a ResultState parameter to represent success or failure */
DECLARE_DELEGATE_TwoParams(FOnlineStatsQueryUserStatsComplete, const FOnlineError& /*ResultState*/, const TSharedPtr<const FOnlineStatsUserStats>& /*QueriedStats*/);
/** Delegate called when multiple users' stats have finished being queried, with a ResultState parameter to represent success or failure */
DECLARE_DELEGATE_TwoParams(FOnlineStatsQueryUsersStatsComplete, const FOnlineError& /*ResultState*/, const TArray<TSharedRef<const FOnlineStatsUserStats>>& /*UsersStatsResult*/);
/** A pair of a user and an array of their stats to be updated */
using FOnlineStatsUserUpdatedStats = FOnlineUserStatsPair<FOnlineStatUpdate>;
/** An interface to update stat backends with */
class IOnlineStats
{
public:
virtual ~IOnlineStats() {};
public:
/**
* Query a specific user's stats
*
* @param LocalUserId User to query as (if applicable)
* @param StatsUser User to get stats for
* @param Delegate Called when the user's stats have finished being requested and are now available, or when we fail to retrieve the user's stats
*/
virtual void QueryStats(const FUniqueNetIdRef LocalUserId, const FUniqueNetIdRef StatsUser, const FOnlineStatsQueryUserStatsComplete& Delegate) = 0;
/**
* Query a one or more user's stats
*
* @param LocalUserId User to query as (if applicable)
* @param StatsUser Users to get stats for
* @param StatNames Stats to get stats for all specified users
* @param Delegate Called when the user's stats have finished being requested and are now available, or when we fail to retrieve the user's stats
*/
virtual void QueryStats(const FUniqueNetIdRef LocalUserId, const TArray<FUniqueNetIdRef>& StatUsers, const TArray<FString>& StatNames, const FOnlineStatsQueryUsersStatsComplete& Delegate) = 0;
/**
* Get a user's cached stats object
*
* @param StatsUserId The user to get stats for
* @return The results if cached, else a null pointer
*/
virtual TSharedPtr<const FOnlineStatsUserStats> GetStats(const FUniqueNetIdRef StatsUserId) const = 0;
/**
* Asynchronous update one or more user's stats
*
* @param LocalUserId The user to update the stats as (if applicable)
* @param UpdatedStats The array of user to stats pairs to update the backend with
* @param Delegate Called when update has completed
*/
virtual void UpdateStats(const FUniqueNetIdRef LocalUserId, const TArray<FOnlineStatsUserUpdatedStats>& UpdatedUserStats, const FOnlineStatsUpdateStatsComplete& Delegate) = 0;
#if !UE_BUILD_SHIPPING
/**
* Request the stats reset, for debugging purposes
*
* @param StatsUserId The user who's stats are to be deleted
*/
virtual void ResetStats( const FUniqueNetIdRef StatsUserId ) = 0;
#endif // !UE_BUILD_SHIPPING
};