// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "SocialToolkit.h" #include "Misc/ConfigCacheIni.h" DECLARE_DELEGATE_TwoParams(FOnQueryCompleted, FName, const TSharedRef&); class FSocialQueryBase : public TSharedFromThis { public: virtual ~FSocialQueryBase() {} virtual void ExecuteQuery() = 0; bool HasExecuted() const { return bHasExecuted; } ESocialSubsystem GetSubsystemType() const { return SubsystemType; } const USocialToolkit* GetOwningToolkit() const { return Toolkit.Get(); } protected: TWeakObjectPtr Toolkit; ESocialSubsystem SubsystemType; bool bHasExecuted = false; FOnQueryCompleted OnQueryCompleted; }; template class TSocialQuery : public FSocialQueryBase { public: using FQueryId = QueryUserIdT; using FOnQueryComplete = TDelegate; // All subclasses of TSocialQuery must implement this static method // Intentionally not implemented here to catch errors at compile time static FName GetQueryId(); virtual void AddUserId(const QueryUserIdT& UserId, const FOnQueryComplete& QueryCompleteHandler) { CompletionCallbacksByUserId.Add(UserId, QueryCompleteHandler); } template bool operator==(const TSharedRef& OtherQuery) const { return TIsDerivedFrom::IsDerived && OtherQuery->Subsystem == SubsystemType && OtherQuery->Toolkit == Toolkit; } protected: friend class FSocialQueryManager; TSocialQuery() {} void Initialize(const USocialToolkit& InToolkit, ESocialSubsystem InSubsystemType, const FOnQueryCompleted& InOnQueryCompleted) { Toolkit = &InToolkit; SubsystemType = InSubsystemType; OnQueryCompleted = InOnQueryCompleted; } inline IOnlineSubsystem* GetOSS() const { return Toolkit.IsValid() ? Toolkit->GetSocialOss(SubsystemType) : nullptr; } static TArray> CurrentQueries; TMap CompletionCallbacksByUserId; }; class FSocialQueryManager { public: template static TSharedRef GetQuery(const USocialToolkit& Toolkit, ESocialSubsystem SubsystemType) { return Get().GetQueryInternal(Toolkit, SubsystemType); } template static void AddUserId(const USocialToolkit& Toolkit, ESocialSubsystem InSubsystem, const typename SocialQueryT::FQueryId& InQueryId, const typename SocialQueryT::FOnQueryComplete& OnQueryCompleteHandler) { Get().GetQueryInternal(Toolkit, InSubsystem)->AddUserId(InQueryId, OnQueryCompleteHandler); } bool HandleExecuteQueries(float) { QUICK_SCOPE_CYCLE_COUNTER(STAT_FSocialQueryManager_HandleExecuteQueries); // Execute all pending queries TArray>> AllQueries; CurrentQueriesById.GenerateValueArray(AllQueries); for (const TArray>& Queries : AllQueries) { for (const TSharedRef& Query : Queries) { if (!Query->HasExecuted()) { Query->ExecuteQuery(); } } } TickExecuteHandle.Reset(); // Returning false ensures the ticker removes this delegate return false; } private: FSocialQueryManager() {} static FSocialQueryManager& Get() { static FSocialQueryManager SingletonInstance; return SingletonInstance; } template TSharedRef GetQueryInternal(const USocialToolkit& Toolkit, ESocialSubsystem SubsystemType) { const FName QueryId = SocialQueryT::GetQueryId(); TArray>& Queries = CurrentQueriesById.FindOrAdd(QueryId); for (const TSharedRef& Query : Queries) { if (Query->GetSubsystemType() == SubsystemType && Query->GetOwningToolkit() == &Toolkit && !Query->HasExecuted()) { return StaticCastSharedRef(Query); } } // No matching query found, so make a new one TSharedRef NewQuery = MakeShareable(new SocialQueryT); NewQuery->Initialize(Toolkit, SubsystemType, FOnQueryCompleted::CreateRaw(this, &FSocialQueryManager::HandleQueryComplete)); Queries.Add(NewQuery); float UserInfoQueryAggregationTime = 0.0f; GConfig->GetFloat(TEXT("Social"), TEXT("UserInfoQueryAggregationTime"), UserInfoQueryAggregationTime, GGameIni); // If we aren't already registered to execute our queries next tick, do so now if (!TickExecuteHandle.IsValid()) { TickExecuteHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FSocialQueryManager::HandleExecuteQueries), UserInfoQueryAggregationTime); } return NewQuery; } void HandleQueryComplete(FName QueryId, const TSharedRef& Query) { if (TArray>* Queries = CurrentQueriesById.Find(QueryId)) { Queries->Remove(Query); } } FTSTicker::FDelegateHandle TickExecuteHandle; TMap>> CurrentQueriesById; };