Files
UnrealEngine/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialDebugTools.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

1019 lines
34 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SocialDebugTools.h"
#include "Interfaces/OnlineUserInterface.h"
#include "Interfaces/OnlinePresenceInterface.h"
#include "Misc/ScopeExit.h"
#include "Online/Presence.h"
#include "OnlineFramework/CommonConfig.h"
#include "OnlineFramework/CommonAccount.h"
#include "OnlineFramework/CommonAccountUtils.h"
#include "OnlineSubsystemUtils.h"
#include "Party/PartyMember.h"
#include "SocialManager.h"
#include "TimerManager.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(SocialDebugTools)
USocialDebugTools::USocialDebugTools()
: bAutoAcceptFriendInvites(true)
, bAutoAcceptPartyInvites(true)
{
}
void USocialDebugTools::Shutdown()
{
// shutdown OSS instances that were created
for (auto& Entry : Contexts)
{
FInstanceContext& Context = Entry.Value;
GetContext(Context.Name).Shutdown();
}
Contexts.Empty();
}
void USocialDebugTools::PrintExecUsage() const
{
UE_LOG(LogParty, Log, TEXT("Usage SOCIAL DEBUG CONTEXT=<Name> <COMMAND> <PARAMS>"));
}
void USocialDebugTools::PrintExecCommands() const
{
UE_LOG(LogParty, Log, TEXT("LOGIN <Id> <Auth> <optional CONTEXT=<Name2> <Id2> <Auth2> CONTEXT=<Name3> <Id3> <Auth3> ...>"));
UE_LOG(LogParty, Log, TEXT("LOGOUT"));
UE_LOG(LogParty, Log, TEXT("JOINPARTY <Id> <Auth> <optional FriendName>"));
UE_LOG(LogParty, Log, TEXT("LEAVEPARTY"));
UE_LOG(LogParty, Log, TEXT("AUTOACCEPTFRIENDINVITES"));
UE_LOG(LogParty, Log, TEXT("AUTOACCEPTPARTYINVITES"));
UE_LOG(LogParty, Log, TEXT("JIP"));
UE_LOG(LogParty, Log, TEXT("HELP"));
}
#if UE_ALLOW_EXEC_COMMANDS
bool USocialDebugTools::Exec(class UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Out)
{
if (FParse::Command(&Cmd, TEXT("DEBUG")))
{
FString Instance;
FParse::Value(Cmd, TEXT("CONTEXT="), Instance);
if (FParse::Command(&Cmd, TEXT("HELP")))
{
PrintExecUsage();
PrintExecCommands();
}
else if (Instance.IsEmpty())
{
PrintExecUsage();
}
else
{
bool bAllInstancesRequested = false;
TArray<FString> TargetInstances;
// strip out context to parse next entry
FParse::Command(&Cmd, *FString(TEXT("CONTEXT=") + Instance));
if(Instance.Equals(TEXT("all")))
{
bAllInstancesRequested = true;
GetContextNames(TargetInstances);
if(TargetInstances.Num() == 0)
{
UE_LOG(LogParty, Log, TEXT("CONTEXT=ALL used, but no OSS contexts found!"));
}
}
else
{
TargetInstances.Add(Instance);
}
return RunCommand(Cmd, TargetInstances);
}
return true;
}
return false;
}
#endif // UE_ALLOW_EXEC_COMMANDS
void USocialDebugTools::Login(const FString& Instance, const FOnlineAccountCredentials& Credentials, const FLoginComplete& OnComplete)
{
bool bResult = false;
FInstanceContext& Context = GetContext(Instance);
IOnlineSubsystem* OnlineSub = Context.GetOSS();
if (OnlineSub)
{
IOnlineIdentityPtr OnlineIdentity = OnlineSub->GetIdentityInterface();
if (OnlineIdentity.IsValid())
{
if (OnlineIdentity->GetLoginStatus(LocalUserNum) == ELoginStatus::LoggedIn)
{
bResult = true;
}
else
{
if (!Context.LoginCompleteDelegateHandle.IsValid())
{
auto PresenceDelegateV2 = [this, Instance, OnComplete](const UE::Online::FPresenceUpdated& Event)
{
FInstanceContext& ContextTmp = GetContext(Instance);
OnComplete.ExecuteIfBound(true);
ContextTmp.OnPresenceUpdatedHandle.Unbind();
};
auto PresenceDelegate = FOnPresenceReceivedDelegate::CreateLambda([this, Instance, OnComplete](const class FUniqueNetId& LocalUserId, const TSharedRef<FOnlineUserPresence>& Presence)
{
FInstanceContext& ContextTmp = GetContext(Instance);
OnComplete.ExecuteIfBound(true);
if (ContextTmp.PresenceReceivedDelegateHandle.IsValid())
{
ContextTmp.GetOSS()->GetPresenceInterface()->ClearOnPresenceReceivedDelegate_Handle(ContextTmp.PresenceReceivedDelegateHandle);
ContextTmp.PresenceReceivedDelegateHandle.Reset();
}
});
auto LoginDelegate = FOnLoginCompleteDelegate::CreateLambda([this, Instance, PresenceDelegate, PresenceDelegateV2, OnComplete](int32 InLocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{
FInstanceContext& ContextTmp = GetContext(Instance);
if (bWasSuccessful)
{
if (USocialManager::ShouldReadPresenceFromOnlineServices())
{
using namespace UE::Online;
using namespace UE::OnlineFramework;
const FName& PresenceFrameworkInstance = USocialManager::GetPresenceFrameworkInstance();
FCommonConfig CommonConfig(this);
FAccountId LocalUserAccountId, PresenceUserAccountId;
if (IOnlineIdentityPtr OnlineIdentity = ContextTmp.GetOSS()->GetIdentityInterface())
{
if (FUniqueNetIdPtr LocalUserNetIdPtr = OnlineIdentity->GetUniquePlayerId(InLocalUserNum))
{
if (const FCommonAccountPtr LocalUserCommonAccountPtr = GetCommonAccountFromV1(CommonConfig, LocalUserNetIdPtr, GetFirstFrameworkInstanceName(LocalUserNetIdPtr)))
{
LocalUserAccountId = LocalUserCommonAccountPtr->GetId(PresenceFrameworkInstance);
}
}
}
if (const FCommonAccountPtr PresenceUserCommonAccountPtr = GetCommonAccountFromV1(CommonConfig, UserId.AsShared(), GetFirstFrameworkInstanceName(UserId)))
{
PresenceUserAccountId = PresenceUserCommonAccountPtr->GetId(PresenceFrameworkInstance);
}
if (LocalUserAccountId.IsValid() && PresenceUserAccountId.IsValid())
{
if (IOnlineServicesPtr OnlineServices = CommonConfig.GetServices(PresenceFrameworkInstance))
{
if (IPresencePtr Presence = OnlineServices->GetPresenceInterface())
{
ContextTmp.OnPresenceUpdatedHandle = Presence->OnPresenceUpdated().Add(this, PresenceDelegateV2);
TSharedRef<FUserPresence> UpdatedPresence = MakeShared<FUserPresence>();
UpdatedPresence->AccountId = PresenceUserAccountId;
UpdatedPresence->Status = EUserPresenceStatus::Online;
UpdatedPresence->StatusString = FString(TEXT("Golem:")) + Instance;
FUpdatePresence::Params Params = { LocalUserAccountId, UpdatedPresence };
Presence->UpdatePresence(MoveTemp(Params));
}
else
{
OnComplete.ExecuteIfBound(false);
}
}
else
{
OnComplete.ExecuteIfBound(false);
}
}
else
{
OnComplete.ExecuteIfBound(false);
}
}
else
{
IOnlinePresencePtr OnlinePresence = ContextTmp.GetOSS()->GetPresenceInterface();
if (OnlinePresence.IsValid())
{
ContextTmp.PresenceReceivedDelegateHandle = OnlinePresence->AddOnPresenceReceivedDelegate_Handle(PresenceDelegate);
FOnlinePresenceSetPresenceParameters Status;
Status.State = EOnlinePresenceState::Online;
Status.StatusStr = FString(TEXT("Golem:")) + Instance;
Status.Properties.Emplace(); // Intentionally empty to clear out existing fields
OnlinePresence->SetPresence(UserId, MoveTemp(Status));
}
else
{
OnComplete.ExecuteIfBound(false);
}
}
}
else
{
OnComplete.ExecuteIfBound(false);
}
if (ContextTmp.LoginCompleteDelegateHandle.IsValid())
{
ContextTmp.GetOSS()->GetIdentityInterface()->ClearOnLoginCompleteDelegate_Handle(InLocalUserNum, ContextTmp.LoginCompleteDelegateHandle);
ContextTmp.LoginCompleteDelegateHandle.Reset();
}
});
Context.LoginCompleteDelegateHandle = OnlineIdentity->AddOnLoginCompleteDelegate_Handle(LocalUserNum, LoginDelegate);
OnlineIdentity->Login(LocalUserNum, Credentials);
return;
}
}
}
}
OnComplete.ExecuteIfBound(bResult);
}
void USocialDebugTools::Logout(const FString& Instance, const FLogoutComplete& OnComplete)
{
bool bResult = false;
FInstanceContext& Context = GetContext(Instance);
IOnlineSubsystem* OnlineSub = Context.GetOSS();
if (OnlineSub)
{
IOnlineIdentityPtr OnlineIdentity = OnlineSub->GetIdentityInterface();
if (OnlineIdentity.IsValid())
{
if (OnlineIdentity->GetLoginStatus(LocalUserNum) == ELoginStatus::NotLoggedIn)
{
bResult = true;
}
else
{
if (!Context.LogoutCompleteDelegateHandle.IsValid())
{
auto Delegate = FOnLogoutCompleteDelegate::CreateLambda([this, Instance, OnComplete](int32 InLocalUserNum, bool bWasSuccessful)
{
OnComplete.ExecuteIfBound(bWasSuccessful);
FInstanceContext& ContextTmp = GetContext(Instance);
if (ContextTmp.LogoutCompleteDelegateHandle.IsValid())
{
GetContext(Instance).GetOSS()->GetIdentityInterface()->ClearOnLogoutCompleteDelegate_Handle(InLocalUserNum, ContextTmp.LogoutCompleteDelegateHandle);
ContextTmp.LogoutCompleteDelegateHandle.Reset();
}
});
Context.LogoutCompleteDelegateHandle = OnlineIdentity->AddOnLogoutCompleteDelegate_Handle(LocalUserNum, Delegate);
OnlineIdentity->Logout(LocalUserNum);
return;
}
}
}
}
OnComplete.ExecuteIfBound(bResult);
}
void USocialDebugTools::JoinInProgress(const FString& Instance, const FJoinInProgressComplete& OnComplete)
{
// Assume an error, and trigger delegate on scope exit
TOptional<FJoinInProgressComplete> OnError = OnComplete;
ON_SCOPE_EXIT
{
// Execute error
if (OnError)
{
// Error reason doesn't matter, but we're very unlikely to receive this from the JIP target
OnError->ExecuteIfBound(EPartyJoinDenialReason::OssUnavailable);
}
};
FInstanceContext& Context = GetContext(Instance);
IOnlineSubsystem* OnlineSub = Context.GetOSS();
if (!OnlineSub)
{
return;
}
FUniqueNetIdPtr LocalUserId = Context.GetLocalUserId();
if (!LocalUserId)
{
return;
}
// Find the party we're in
IOnlinePartyPtr PartySystem = OnlineSub->GetPartyInterface();
if (!PartySystem)
{
return;
}
FOnlinePartyConstPtr Party = PartySystem->GetParty(*LocalUserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (!Party)
{
return;
}
// JIP on the party leader
FUniqueNetIdPtr JipTargetId = Party->LeaderId;
if (!JipTargetId.IsValid())
{
return;
}
FPartyMemberJoinInProgressRequest Request;
Request.Target = JipTargetId;
Request.Time = FDateTime::UtcNow().ToUnixTimestamp();
if (Context.SetJIPRequest(Request))
{
OnError.Reset(); // No longer assuming an error
// Wait for player to respond
TSharedRef<FDelegateHandle> DelegateHandleRef = MakeShared<FDelegateHandle>();
TSharedRef<FTimerHandle> TimerHandleRef = MakeShared<FTimerHandle>();
auto CleanupJipRequest = [this, Instance, PartySystem = PartySystem.ToWeakPtr(), OnComplete, DelegateHandleRef, TimerHandleRef](EPartyJoinDenialReason Result)
{
// Clear our JIP request
FInstanceContext& LocalContext = GetContext(Instance);
LocalContext.SetJIPRequest(FPartyMemberJoinInProgressRequest());
OnComplete.ExecuteIfBound(Result);
// Copy lambda captures as they will be deleted through the unbinds here
TSharedRef<FDelegateHandle> DelegateHandleCopy = DelegateHandleRef;
TSharedRef<FTimerHandle> TimerHandleCopy = TimerHandleRef;
IOnlinePartyPtr StrongPartySystem = PartySystem.Pin();
UWorld* World = GetWorld();
if (StrongPartySystem)
{
StrongPartySystem->ClearOnPartyMemberDataReceivedDelegate_Handle(*DelegateHandleCopy);
}
if (World)
{
World->GetTimerManager().ClearTimer(*TimerHandleCopy);
}
};
auto OnPartyMemberDataReceivedLambda = [this, LocalUserId, JipTargetId, Request, CleanupJipRequest](const FUniqueNetId& InLocalUserId, const FOnlinePartyId& InPartyId, const FUniqueNetId& InMemberId, const FName& InNamespace, const FOnlinePartyData& InPartyMemberData)
{
if (InMemberId == *JipTargetId)
{
FPartyMemberRepData ReceivedPartyMemberRepData;
if (FVariantDataConverter::VariantMapToUStruct(InPartyMemberData.GetKeyValAttrs(), FPartyMemberRepData::StaticStruct(), &ReceivedPartyMemberRepData, 0, CPF_Transient | CPF_RepSkip))
{
for (const FPartyMemberJoinInProgressResponse& Response : ReceivedPartyMemberRepData.GetJoinInProgressDataResponses())
{
if (Response.RequestTime != Request.Time ||
Response.Requester != LocalUserId)
{
// Response was not for us.
continue;
}
CleanupJipRequest(static_cast<EPartyJoinDenialReason>(Response.DenialReason));
return;
}
}
}
};
*DelegateHandleRef = PartySystem->AddOnPartyMemberDataReceivedDelegate_Handle(FOnPartyMemberDataReceivedDelegate::CreateWeakLambda(this, OnPartyMemberDataReceivedLambda));
// wait 60 seconds for a response
if (UWorld* World = GetWorld())
{
World->GetTimerManager().SetTimer(*TimerHandleRef, FTimerDelegate::CreateWeakLambda(this, CleanupJipRequest, static_cast<EPartyJoinDenialReason>(EPartyJoinDenialReason::OssUnavailable)), 60, false);
}
}
}
void USocialDebugTools::JoinParty(const FString& Instance, const FString& FriendName, const FJoinPartyComplete& OnComplete)
{
FInstanceContext& Context = GetContext(Instance);
IOnlineSubsystem* OnlineSub = Context.GetOSS();
if (OnlineSub)
{
if (FUniqueNetIdPtr LocalUserId = Context.GetLocalUserId())
{
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
// query user id by name
if (!FriendName.IsEmpty())
{
IOnlineUserPtr OnlineUser = OnlineSub->GetUserInterface();
if (OnlineUser.IsValid())
{
OnlineUser->QueryUserIdMapping(*LocalUserId, FriendName, IOnlineUser::FOnQueryUserMappingComplete::CreateLambda([this, Instance, OnlineParty, OnComplete](bool bWasSuccessful, const FUniqueNetId& UserId, const FString& DisplayNameOrEmail, const FUniqueNetId& FoundUserId, const FString& Error)
{
if (bWasSuccessful)
{
IOnlinePartyJoinInfoConstPtr JoinInfo = OnlineParty->GetAdvertisedParty(UserId, FoundUserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (JoinInfo.IsValid())
{
OnlineParty->JoinParty(UserId, *JoinInfo, FOnJoinPartyComplete::CreateLambda([this, Instance, OnlineParty, OnComplete](const FUniqueNetId& UserIdTmp, const FOnlinePartyId& PartyId, const EJoinPartyCompletionResult Result, const int32 NotApprovedReason)
{
bool bSuccess = Result == EJoinPartyCompletionResult::Succeeded;
if (bSuccess)
{
FOnlinePartyDataConstPtr PartyMemberData = GetContext(Instance).GetPartyMemberData();
if (PartyMemberData.IsValid())
{
OnlineParty->UpdatePartyMemberData(UserIdTmp, PartyId, DefaultPartyDataNamespace, *PartyMemberData);
}
}
else
{
UE_LOG(LogParty, Warning, TEXT("Party context[%s] join attempt denied for reason [%d]"), *Instance, (int32)Result);
}
OnComplete.ExecuteIfBound(bSuccess);
}));
return;
}
}
OnComplete.ExecuteIfBound(false);
}));
return;
}
}
else
{
IOnlinePartyJoinInfoConstPtr JoinInfo = GetDefaultPartyJoinInfo();
if (JoinInfo.IsValid())
{
OnlineParty->JoinParty(*LocalUserId, *JoinInfo, FOnJoinPartyComplete::CreateLambda([this, Instance, OnlineParty, OnComplete](const FUniqueNetId& UserId, const FOnlinePartyId& PartyId, const EJoinPartyCompletionResult Result, const int32 NotApprovedReason)
{
bool bSuccess = Result == EJoinPartyCompletionResult::Succeeded;
if (bSuccess)
{
FOnlinePartyDataConstPtr PartyMemberData = GetContext(Instance).GetPartyMemberData();
if (PartyMemberData.IsValid())
{
OnlineParty->UpdatePartyMemberData(UserId, PartyId, DefaultPartyDataNamespace, *PartyMemberData);
}
}
else
{
UE_LOG(LogParty, Warning, TEXT("Party context[%s] join attempt denied for reason [%d]"), *Instance, (int32)Result);
}
OnComplete.ExecuteIfBound(bSuccess);
}));
return;
}
}
}
}
}
OnComplete.ExecuteIfBound(false);
}
void USocialDebugTools::LeaveParty(const FString& Instance, const FLeavePartyComplete& OnComplete)
{
FInstanceContext& Context = GetContext(Instance);
IOnlineSubsystem* OnlineSub = GetContext(Instance).GetOSS();
if (OnlineSub)
{
if (FUniqueNetIdPtr UserId = Context.GetLocalUserId())
{
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
TSharedPtr<const FOnlineParty> ExistingParty = OnlineParty->GetParty(*UserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (ExistingParty.IsValid())
{
OnlineParty->LeaveParty(*UserId, *ExistingParty->PartyId, true, FOnLeavePartyComplete::CreateLambda([this, Instance, OnComplete](const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, const ELeavePartyCompletionResult Result)
{
OnComplete.ExecuteIfBound(Result == ELeavePartyCompletionResult::Succeeded);
}));
return;
}
}
}
}
OnComplete.ExecuteIfBound(false);
}
void USocialDebugTools::CleanupParties(const FString& Instance, const FCleanupPartiesComplete& OnComplete)
{
FInstanceContext& Context = GetContext(Instance);
IOnlineSubsystem* OnlineSub = Context.GetOSS();
if (OnlineSub)
{
if (FUniqueNetIdPtr UserId = Context.GetLocalUserId())
{
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
OnlineParty->CleanupParties(*UserId, FOnCleanupPartiesComplete::CreateLambda([this, Instance, OnComplete](const FUniqueNetId& LocalUserId, const FOnlineError& Result)
{
OnComplete.ExecuteIfBound(Result.WasSuccessful());
}));
return;
}
}
}
OnComplete.ExecuteIfBound(false);
}
void USocialDebugTools::SetPartyMemberData(const FString& Instance, const UStruct* StructType, const void* StructData, const FSetPartyMemberDataComplete& OnComplete)
{
check(StructType);
check(StructData);
bool bResult = false;
FInstanceContext& Context = GetContext(Instance);
// parse from struct to party data
FOnlinePartyData OnlinePartyData;
if (FVariantDataConverter::UStructToVariantMap(StructType, StructData, OnlinePartyData.GetKeyValAttrs(), 0, CPF_Transient | CPF_RepSkip))
{
if (!Context.PartyMemberData.IsValid())
{
Context.PartyMemberData = MakeShared<FOnlinePartyData>();
}
// cache party data on the context so it can be sent whenever a party is joined
*Context.PartyMemberData = OnlinePartyData;
// send the party data if connected to a party
IOnlineSubsystem* OnlineSub = Context.GetOSS();
if (OnlineSub)
{
if (FUniqueNetIdPtr UserId = Context.GetLocalUserId())
{
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
TSharedPtr<const FOnlineParty> ExistingParty = OnlineParty->GetParty(*UserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (ExistingParty.IsValid())
{
const FOnlinePartyId& PartyId = *ExistingParty->PartyId;
if (StructType->IsChildOf(FPartyMemberRepData::StaticStruct()))
{
UE_LOG(LogParty, Display, TEXT("Sending rep data update for member within party [%s]."), *PartyId.ToDebugString());
bResult = OnlineParty->UpdatePartyMemberData(*UserId, PartyId, DefaultPartyDataNamespace, *Context.PartyMemberData);
}
}
}
}
}
}
OnComplete.ExecuteIfBound(bResult);
}
void USocialDebugTools::SetPartyMemberDataJson(const FString& Instance, const FString& JsonStr, const FSetPartyMemberDataComplete& OnComplete)
{
bool bResult = false;
FInstanceContext& Context = GetContext(Instance);
// parse from struct to party data
FOnlinePartyData OnlinePartyData;
OnlinePartyData.FromJson(JsonStr);
if (OnlinePartyData.GetKeyValAttrs().Num() > 0)
{
if (!Context.PartyMemberData.IsValid())
{
Context.PartyMemberData = MakeShared<FOnlinePartyData>();
}
// cache party data on the context so it can be sent whenever a party is joined
*Context.PartyMemberData = OnlinePartyData;
// send the party data if connected to a party
IOnlineSubsystem* OnlineSub = Context.GetOSS();
if (OnlineSub)
{
if (FUniqueNetIdPtr UserId = Context.GetLocalUserId())
{
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
TSharedPtr<const FOnlineParty> ExistingParty = OnlineParty->GetParty(*UserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (ExistingParty.IsValid())
{
const FOnlinePartyId& PartyId = *ExistingParty->PartyId;
UE_LOG(LogParty, Display, TEXT("Sending rep data update for member within party [%s]."), *PartyId.ToDebugString());
bResult = OnlineParty->UpdatePartyMemberData(*UserId, PartyId, DefaultPartyDataNamespace, *Context.PartyMemberData);
}
}
}
}
}
else
{
UE_LOG(LogParty, Warning, TEXT("SetPartyMemberDataJson didnt parse data from JsonStr=[%s]."), *JsonStr);
}
OnComplete.ExecuteIfBound(bResult);
}
IOnlineSubsystem* USocialDebugTools::GetDefaultOSS() const
{
if (UWorld* World = GetWorld())
{
return Online::GetSubsystem(World, MCP_SUBSYSTEM);
}
else
{
return IOnlineSubsystem::Get(MCP_SUBSYSTEM);
}
}
USocialManager& USocialDebugTools::GetSocialManager() const
{
USocialManager* OuterSocialManager = GetTypedOuter<USocialManager>();
check(OuterSocialManager);
return *OuterSocialManager;
}
IOnlinePartyJoinInfoConstPtr USocialDebugTools::GetDefaultPartyJoinInfo() const
{
IOnlinePartyJoinInfoConstPtr Result;
IOnlineSubsystem* OnlineSub = GetDefaultOSS();
if (OnlineSub)
{
IOnlineIdentityPtr OnlineIdentity = OnlineSub->GetIdentityInterface();
if (OnlineIdentity.IsValid())
{
FUniqueNetIdPtr UserId = OnlineIdentity->GetUniquePlayerId(LocalUserNum);
if (UserId.IsValid())
{
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
Result = OnlineParty->GetAdvertisedParty(*UserId, *UserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (!Result.IsValid())
{
TSharedPtr<const FOnlineParty> ExistingParty = OnlineParty->GetParty(*UserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (ExistingParty.IsValid())
{
FString JsonJoinInfo = OnlineParty->MakeJoinInfoJson(*UserId, *ExistingParty->PartyId);
Result = OnlineParty->MakeJoinInfoFromJson(JsonJoinInfo);
}
}
}
}
}
}
return Result;
}
USocialDebugTools::FInstanceContext& USocialDebugTools::GetContext(const FString& Instance)
{
FInstanceContext* Context = Contexts.Find(Instance);
if (!Context)
{
Context = &Contexts.Add(Instance, FInstanceContext(Instance, *this));
Context->Init();
NotifyContextInitialized(*Context);
}
return *Context;
}
USocialDebugTools::FInstanceContext* USocialDebugTools::GetContextForUser(const FUniqueNetId& UserId)
{
FInstanceContext* Result = nullptr;
for (TPair<FString, FInstanceContext>& Entry : Contexts)
{
FInstanceContext& Context = Entry.Value;
if (Context.GetOSS())
{
if (FUniqueNetIdPtr LocalUserId = Context.GetLocalUserId())
{
if (*LocalUserId == UserId)
{
Result = &Context;
break;
}
}
}
}
return Result;
}
bool USocialDebugTools::RunCommand(const TCHAR* Cmd, const TArray<FString>& TargetInstances)
{
if (FParse::Command(&Cmd, TEXT("LOGIN")))
{
if (TargetInstances.Num() > 1)
{
UE_LOG(LogParty, Log, TEXT("CONTEXT=ALL cannot be used for the LOGIN command!"));
}
else
{
FString InstanceName = TargetInstances[0];
FString Id = FParse::Token(Cmd, false);
FString Auth = FParse::Token(Cmd, false);
int MaxConcurrentLogins = 512; // Reasonable limit to prevent infinite loop
while (!Id.IsEmpty() && !Auth.IsEmpty() && MaxConcurrentLogins >= 0)
{
Login(InstanceName, FOnlineAccountCredentials(TEXT("epic"), Id, Auth), FLoginComplete::CreateLambda([this, InstanceName](bool bSuccess)
{
UE_LOG(LogParty, Display, TEXT("Login OSS context[%s] %s"), *InstanceName, *LexToString(bSuccess));
}));
// Allow for more logins by concatenating more context=name auth pass
if (FParse::Value(Cmd, TEXT("CONTEXT="), InstanceName))
{
// strip out context to parse next entry
FParse::Command(&Cmd, *FString(TEXT("CONTEXT=") + InstanceName));
Id = FParse::Token(Cmd, false);
Auth = FParse::Token(Cmd, false);
--MaxConcurrentLogins;
}
else
{
break;
}
}
}
return true;
}
else if (FParse::Command(&Cmd, TEXT("LOGOUT")))
{
for (const FString& TargetInstance : TargetInstances)
{
Logout(TargetInstance, FLogoutComplete::CreateLambda([this, TargetInstance](bool bSuccess)
{
UE_LOG(LogParty, Display, TEXT("Logout OSS context[%s] %s"), *TargetInstance, *LexToString(bSuccess));
}));
}
return true;
}
else if (FParse::Command(&Cmd, TEXT("JOINPARTY")))
{
const FString Id = FParse::Token(Cmd, false);
const FString Auth = FParse::Token(Cmd, false);
const FString FriendName = FParse::Token(Cmd, false);
for (const FString& TargetInstance : TargetInstances)
{
Login(TargetInstance, FOnlineAccountCredentials(TEXT("epic"), Id, Auth), FLoginComplete::CreateLambda([this, TargetInstance, FriendName](bool bSuccess)
{
UE_LOG(LogParty, Display, TEXT("Login OSS context[%s] %s"), *TargetInstance, *LexToString(bSuccess));
if (bSuccess)
{
CleanupParties(TargetInstance, FCleanupPartiesComplete::CreateLambda([this, TargetInstance, FriendName](bool bCleanupPartiesSuccess)
{
LeaveParty(TargetInstance, FLeavePartyComplete::CreateLambda([this, TargetInstance, FriendName](bool bLeavePartySuccess)
{
UE_LOG(LogParty, Display, TEXT("Leave party OSS context[%s] %s"), *TargetInstance, *LexToString(bLeavePartySuccess));
JoinParty(TargetInstance, FriendName, FJoinPartyComplete::CreateLambda([this, TargetInstance](bool bJoinPartySuccess)
{
UE_LOG(LogParty, Display, TEXT("Join party OSS context[%s] %s"), *TargetInstance, *LexToString(bJoinPartySuccess));
}));
}));
}));
}
}));
}
return true;
}
else if (FParse::Command(&Cmd, TEXT("JIP")))
{
for (const FString& TargetInstance : TargetInstances)
{
JoinInProgress(TargetInstance, FJoinInProgressComplete::CreateLambda([this, TargetInstance](EPartyJoinDenialReason Result)
{
UE_LOG(LogParty, Display, TEXT("JIP OSS context[%s] %s"), *TargetInstance, ToString(Result));
}));
}
return true;
}
else if (FParse::Command(&Cmd, TEXT("LEAVEPARTY")))
{
for (const FString& TargetInstance : TargetInstances)
{
LeaveParty(TargetInstance, FLeavePartyComplete::CreateLambda([this, TargetInstance](bool bSuccess)
{
UE_LOG(LogParty, Display, TEXT("Leave party OSS context[%s] %s"), *TargetInstance, *LexToString(bSuccess));
}));
}
return true;
}
else if (FParse::Command(&Cmd, TEXT("AUTOACCEPTFRIENDINVITES")))
{
const FString Enabled = FParse::Token(Cmd, false);
if (Enabled == TEXT("true") || Enabled == TEXT("1"))
{
bAutoAcceptFriendInvites = true;
}
else if (Enabled == TEXT("false") || Enabled == TEXT("0"))
{
bAutoAcceptFriendInvites = false;
}
return true;
}
else if (FParse::Command(&Cmd, TEXT("AUTOACCEPTPARTYINVITES")))
{
const FString Enabled = FParse::Token(Cmd, false);
if (Enabled == TEXT("true") || Enabled == TEXT("1"))
{
bAutoAcceptPartyInvites = true;
}
else if (Enabled == TEXT("false") || Enabled == TEXT("0"))
{
bAutoAcceptPartyInvites = false;
}
return true;
}
else if (FParse::Command(&Cmd, TEXT("DUMPPARTY")))
{
for (const FString& TargetInstance : TargetInstances)
{
UE_LOG(LogParty, Display, TEXT("---DUMPPARTY - party OSS context[%s]"), *TargetInstance);
IOnlineSubsystem* OnlineSub = GetContext(TargetInstance).GetOSS();
if (OnlineSub &&
OnlineSub->GetPartyInterface().IsValid())
{
OnlineSub->GetPartyInterface()->DumpPartyState();
}
}
return true;
}
return false;
}
void USocialDebugTools::FInstanceContext::Init()
{
const FString OSSName = FName(MCP_SUBSYSTEM).ToString() + TEXT(":") + Name;
OnlineSub = IOnlineSubsystem::Get(*OSSName);
if (OnlineSub)
{
// register delegates
IOnlineFriendsPtr OnlineFriends = OnlineSub->GetFriendsInterface();
if (OnlineFriends.IsValid())
{
FriendInviteReceivedDelegateHandle = OnlineFriends->AddOnInviteReceivedDelegate_Handle(FOnInviteReceivedDelegate::CreateUObject(&Owner, &USocialDebugTools::HandleFriendInviteReceived));
}
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
PartyInviteReceivedDelegateHandle = OnlineParty->AddOnPartyInviteReceivedExDelegate_Handle(FOnPartyInviteReceivedExDelegate::CreateUObject(&Owner, &USocialDebugTools::HandlePartyInviteReceived));
PartyJoinRequestReceivedDelegateHandle = OnlineParty->AddOnPartyJoinRequestReceivedDelegate_Handle(FOnPartyJoinRequestReceivedDelegate::CreateUObject(&Owner, &USocialDebugTools::HandlePartyJoinRequestReceived));
}
}
}
void USocialDebugTools::FInstanceContext::Shutdown()
{
if (OnlineSub)
{
// unregister delegates
IOnlineFriendsPtr OnlineFriends = OnlineSub->GetFriendsInterface();
if (OnlineFriends.IsValid())
{
OnlineFriends->ClearOnInviteReceivedDelegate_Handle(FriendInviteReceivedDelegateHandle);
OnlineFriends.Reset();
}
IOnlinePartyPtr OnlineParty = OnlineSub->GetPartyInterface();
if (OnlineParty.IsValid())
{
OnlineParty->ClearOnPartyInviteReceivedExDelegate_Handle(PartyInviteReceivedDelegateHandle);
OnlineParty->ClearOnPartyJoinRequestReceivedDelegate_Handle(PartyJoinRequestReceivedDelegateHandle);
OnlineParty.Reset();
}
OnlineSub->Shutdown();
const FString OSSName = FName(MCP_SUBSYSTEM).ToString() + TEXT(":") + Name;
IOnlineSubsystem::Destroy(*OSSName);
}
}
FUniqueNetIdPtr USocialDebugTools::FInstanceContext::GetLocalUserId() const
{
FUniqueNetIdPtr LocalUserId;
if (OnlineSub)
{
IOnlineIdentityPtr OnlineIdentity = OnlineSub->GetIdentityInterface();
if (OnlineIdentity.IsValid())
{
LocalUserId = OnlineIdentity->GetUniquePlayerId(LocalUserNum);
}
}
return LocalUserId;
}
void USocialDebugTools::FInstanceContext::ModifyPartyField(const FString& FieldName, const FVariantData& FieldValue)
{
if (!OnlineSub)
{
return;
}
IOnlinePartyPtr PartySystem = OnlineSub->GetPartyInterface();
if (!PartySystem)
{
return;
}
FUniqueNetIdPtr LocalUserId = GetLocalUserId();
if (!LocalUserId)
{
return;
}
TSharedPtr<const FOnlineParty> Party = PartySystem->GetParty(*LocalUserId, IOnlinePartySystem::GetPrimaryPartyTypeId());
if (!Party)
{
return;
}
// Get current party data and update the JIP field
if (!PartyMemberData)
{
if (FOnlinePartyDataConstPtr ExistingPartyMemberData = PartySystem->GetPartyMemberData(*LocalUserId, *Party->PartyId, *LocalUserId, DefaultPartyDataNamespace))
{
PartyMemberData = MakeShared<FOnlinePartyData>(*ExistingPartyMemberData);
}
}
if (!PartyMemberData)
{
PartyMemberData = MakeShared<FOnlinePartyData>();
}
PartyMemberData->SetAttribute(FieldName, FieldValue);
PartySystem->UpdatePartyMemberData(*LocalUserId, *Party->PartyId, DefaultPartyDataNamespace , *PartyMemberData);
}
bool USocialDebugTools::FInstanceContext::SetJIPRequest(const FPartyMemberJoinInProgressRequest& InRequest)
{
FPartyMemberRepData PartyMemberRepData;
PartyMemberRepData.MarkOwnerless();
PartyMemberRepData.SetJoinInProgressDataRequest(InRequest);
FOnlinePartyData OnlinePartyData;
const FString JipFieldName(TEXT("JoinInProgressData"));
if (FVariantDataConverter::UStructToVariantMap(FPartyMemberRepData::StaticStruct(), &PartyMemberRepData, OnlinePartyData.GetKeyValAttrs(), 0, CPF_Transient | CPF_RepSkip) &&
ensure(OnlinePartyData.GetKeyValAttrs().Contains(JipFieldName)))
{
ModifyPartyField(JipFieldName, OnlinePartyData.GetKeyValAttrs().FindChecked(JipFieldName));
return true;
}
return false;
}
void USocialDebugTools::HandleFriendInviteReceived(const FUniqueNetId& LocalUserId, const FUniqueNetId& FriendId)
{
if (bAutoAcceptFriendInvites)
{
FInstanceContext* Context = GetContextForUser(LocalUserId);
if (Context)
{
IOnlineFriendsPtr OnlineFriends = Context->GetOSS()->GetFriendsInterface();
if (OnlineFriends.IsValid())
{
OnlineFriends->AcceptInvite(LocalUserNum, FriendId, EFriendsLists::ToString(EFriendsLists::Default));
}
}
}
}
void USocialDebugTools::HandlePartyInviteReceived(const FUniqueNetId& LocalUserId, const IOnlinePartyJoinInfo& Invitation)
{
if (bAutoAcceptPartyInvites)
{
FInstanceContext* Context = GetContextForUser(LocalUserId);
if (Context)
{
IOnlinePartyPtr OnlineParty = Context->GetOSS()->GetPartyInterface();
if (OnlineParty.IsValid())
{
const FString Instance = Context->Name;
OnlineParty->JoinParty(LocalUserId, Invitation, FOnJoinPartyComplete::CreateLambda([this, Instance, OnlineParty](const FUniqueNetId& UserId, const FOnlinePartyId& PartyIdTmp, const EJoinPartyCompletionResult Result, const int32 NotApprovedReason)
{
bool bSuccess = Result == EJoinPartyCompletionResult::Succeeded;
if (bSuccess)
{
FOnlinePartyDataConstPtr PartyMemberData = GetContext(Instance).GetPartyMemberData();
if (PartyMemberData.IsValid())
{
OnlineParty->UpdatePartyMemberData(UserId, PartyIdTmp, DefaultPartyDataNamespace, *PartyMemberData);
}
}
else
{
UE_LOG(LogParty, Warning, TEXT("Party context[%s] invite join attempt denied for reason [%d]"), *Instance, (int32)Result);
}
}));
}
}
}
}
void USocialDebugTools::HandlePartyJoinRequestReceived(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, const IOnlinePartyPendingJoinRequestInfo& JoinRequestInfo)
{
FInstanceContext* Context = GetContextForUser(LocalUserId);
if (Context)
{
IOnlinePartyPtr OnlineParty = Context->GetOSS()->GetPartyInterface();
if (OnlineParty.IsValid())
{
TArray<IOnlinePartyUserPendingJoinRequestInfoConstRef> JoiningUsers;
JoinRequestInfo.GetUsers(JoiningUsers);
check(JoiningUsers.IsValidIndex(0));
OnlineParty->ApproveJoinRequest(LocalUserId, PartyId, *JoiningUsers[0]->GetUserId(), true);
return;
}
}
}