Skip to main content

Unreal Engine Module - Add a Friend - Use the Online Subsystem to manage friend requests

Last updated on January 13, 2024

What's in the Starter Pack

In Module: Search Player, you implemented some friend functionalities in the FriendSubsystem_Starter subsystem class. You will continue to use that class to follow this section.

Before you start, we have prepared several delegates in the /Source/AccelByteWars/TutorialModules/Social/FriendsEssentials/FriendsEssentialsModels.h file that you will use along with this tutorial.

  • Delegates to be can be used as callback when cached friends data is updated.

    DECLARE_DELEGATE(FOnCachedFriendsDataUpdated);
  • Delegates to be can be used as callback when getting received and sent friend requests completed.

    DECLARE_DELEGATE_ThreeParams(FOnGetInboundFriendRequestListComplete, bool /*bWasSuccessful*/, TArray<UFriendData*> /*FriendRequests*/, const FString& /*ErrorMessage*/);
    DECLARE_DELEGATE_ThreeParams(FOnGetOutboundFriendRequestListComplete, bool /*bWasSuccessful*/, TArray<UFriendData*> /*FriendRequests*/, const FString& /*ErrorMessage*/);
  • Delegates to be can be used as callback when accept, reject, and cancel friend requests are completed.

    DECLARE_DELEGATE_TwoParams(FOnAcceptFriendRequestComplete, bool /*bWasSuccessful*/, const FString& /*ErrorMessage*/);
    DECLARE_DELEGATE_TwoParams(FOnRejectFriendRequestComplete, bool /*bWasSuccessful*/, const FString& /*ErrorMessage*/);
    DECLARE_DELEGATE_TwoParams(FOnCancelFriendRequestComplete, bool /*bWasSuccessful*/, const FString& /*ErrorMessage*/);

Implement Get Received Friend Requests

In this section, you will implement functionalities to get the received friend requests list.

  1. In the previous tutorial, you have created a function namely CacheFriendList() in the FriendsSubsystem_Starter that is used to get and cache the friend list which also includes the received friends requests. Therefore, to get the received friend requests, you only need to filter that cached friend list.

  2. Open the FriendsSubsystem_Starter class header file and create the following function declarations. The inbound friend request is an alternative term for received friend request which should not be confused.

    public:
    void GetInboundFriendRequestList(const APlayerController* PC, const FOnGetInboundFriendRequestListComplete& OnComplete= FOnGetInboundFriendRequestListComplete());
  3. Next, let's create the definition for the function above. Open the FriendsSubsystem_Starter class CPP file and add the following code. This function will filter the cached friend list to get the received friend requests.

    void UFriendsSubsystem_Starter::GetInboundFriendRequestList(const APlayerController* PC, const FOnGetInboundFriendRequestListComplete& OnComplete)
    {
    if (!ensure(FriendsInterface))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot query friend request list. Friends Interface is not valid."));
    return;
    }

    // Get friend inbound request list from cache.
    GetCacheFriendList(PC, FOnGetCacheFriendListComplete::CreateWeakLambda(this, [this, OnComplete](bool bWasSuccessful, TArray<TSharedRef<FOnlineFriend>>& CachedFriendList, const FString& ErrorMessage)
    {
    if (bWasSuccessful)
    {
    // Filter pending inbound friend requests.
    CachedFriendList = CachedFriendList.FilterByPredicate([](const TSharedRef<FOnlineFriend>& Friend)
    {
    return Friend->GetInviteStatus() == EInviteStatus::PendingInbound;
    });

    TArray<UFriendData*> InboundFriendRequestList;
    for (const TSharedRef<FOnlineFriend>& TempData : CachedFriendList)
    {
    InboundFriendRequestList.Add(UFriendData::ConvertToFriendData(TempData));
    }

    OnComplete.ExecuteIfBound(true, InboundFriendRequestList, TEXT(""));
    }
    else
    {
    OnComplete.ExecuteIfBound(false, TArray<UFriendData*>(), ErrorMessage);
    }
    }));
    }
  4. Congratulations! Your get received friend requests list functionalities are completed.

Implement Get Sent Friend Requests

In this section, you will implement functionalities to get the sent friend requests list.

  1. Open the FriendsSubsystem_Starter class header file and create the following function declarations. The outbound friend request is an alternative term for received friend request which should not be confused.

    public:
    void GetOutboundFriendRequestList(const APlayerController* PC, const FOnGetOutboundFriendRequestListComplete& OnComplete = FOnGetOutboundFriendRequestListComplete());
  2. Next, let's create the definition for the function above. Open the FriendsSubsystem_Starter class CPP file and add the following code. Similar to getting received friend request, to get sent friend request you only need to filter the cached friend list.

    void UFriendsSubsystem_Starter::GetOutboundFriendRequestList(const APlayerController* PC, const FOnGetOutboundFriendRequestListComplete& OnComplete)
    {
    if (!ensure(FriendsInterface))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot query friend request list. Friends Interface is not valid."));
    return;
    }

    // Get friend outbound request list from cache.
    GetCacheFriendList(PC, FOnGetCacheFriendListComplete::CreateWeakLambda(this, [this, OnComplete](bool bWasSuccessful, TArray<TSharedRef<FOnlineFriend>>& CachedFriendList, const FString& ErrorMessage)
    {
    if (bWasSuccessful)
    {
    // Filter pending outbound friend requests.
    CachedFriendList = CachedFriendList.FilterByPredicate([](const TSharedRef<FOnlineFriend>& Friend)
    {
    return Friend->GetInviteStatus() == EInviteStatus::PendingOutbound;
    });

    TArray<UFriendData*> OutbondFriendRequestList;
    for (const TSharedRef<FOnlineFriend>& TempData : CachedFriendList)
    {
    OutbondFriendRequestList.Add(UFriendData::ConvertToFriendData(TempData));
    }

    OnComplete.ExecuteIfBound(true, OutbondFriendRequestList, TEXT(""));
    }
    else
    {
    OnComplete.ExecuteIfBound(false, TArray<UFriendData*>(), ErrorMessage);
    }
    }));
    }
  3. Congratulations! Your get sent friend requests list functionalities are completed.

Implement Accept Received Friend Request

In this section, you will implement functionalities to accept a received friend request.

  1. Open the FriendsSubsystem_Starter class header file and declare the following function.

    public:
    void AcceptFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnAcceptFriendRequestComplete& OnComplete = FOnAcceptFriendRequestComplete());
  2. You also need to create a callback function to handle when the accepting friend request process is completed.

    protected:
    void OnAcceptFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnAcceptFriendRequestComplete OnComplete);
  3. Now, let's define the functions above. Open the FriendsSubsystem_Starter class CPP file and define the AcceptFriendRequest() function. This function will accept a friend request and call the OnAcceptFriendRequestComplete() function to handle the callback.

    void UFriendsSubsystem_Starter::AcceptFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnAcceptFriendRequestComplete& OnComplete)
    {
    if (!ensure(FriendsInterface) || !ensure(PromptSubsystem))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot accept friend request. Friends Interface or Prompt Subsystem is not valid."));
    return;
    }

    PromptSubsystem->ShowLoading(ACCEPT_FRIEND_REQUEST_MESSAGE);

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);
    FriendsInterface->AcceptInvite(LocalUserNum, FriendUserId.GetUniqueNetId().ToSharedRef().Get(), TEXT(""), FOnAcceptInviteComplete::CreateUObject(this, &ThisClass::OnAcceptFriendRequestComplete, OnComplete));
    }
  4. Finally, define the OnAcceptFriendRequestComplete() function that will be called upon the accept friend request process is completed. This function prints a log to show whether the accept received friend request process was successful or not, trigger the callback delegate, and show a pop-up message telling the player the status of their request. Do note that PromptSubsystem is Byte Wars specifics, you won't find that exact object in a blank project.

    void UFriendsSubsystem_Starter::OnAcceptFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnAcceptFriendRequestComplete OnComplete)
    {
    PromptSubsystem->HideLoading();

    if (bWasSuccessful)
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Success to accept a friend request."));

    PromptSubsystem->ShowMessagePopUp(MESSAGE_PROMPT_TEXT, SUCCESS_ACCEPT_FRIEND_REQUEST);
    OnComplete.ExecuteIfBound(true, TEXT(""));
    }
    else
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Failed to accept a friend request. Error: %s"), *ErrorStr);

    PromptSubsystem->ShowMessagePopUp(ERROR_PROMPT_TEXT, FText::FromString(ErrorStr));
    OnComplete.ExecuteIfBound(false, ErrorStr);
    }
    }
  5. Congratulations! Your accept friend request functionalities are completed.

Implement Reject Received Friend Request

In this section, you will implement functionalities to reject received a friend request.

  1. Open the FriendsSubsystem_Starter class header file and declare the following function.

    public:
    void RejectFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnRejectFriendRequestComplete& OnComplete = FOnRejectFriendRequestComplete());
  2. You also need to create a callback function to handle when rejecting friend request process is completed.

    protected:
    void OnRejectFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnRejectFriendRequestComplete OnComplete);
  3. Now, let's define the functions above. Open the FriendsSubsystem_Starter class CPP file and define the RejectFriendRequest() function. This function will reject a friend request and call the OnRejectFriendRequestComplete() function to handle the callback.

    void UFriendsSubsystem_Starter::RejectFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnRejectFriendRequestComplete& OnComplete)
    {
    if (!ensure(FriendsInterface) || !ensure(PromptSubsystem))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot reject friend request. Friends Interface or Prompt Subsystem is not valid."));
    return;
    }

    PromptSubsystem->ShowLoading(REJECT_FRIEND_REQUEST_MESSAGE);

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);
    OnRejectFriendRequestCompleteDelegateHandle = FriendsInterface->AddOnRejectInviteCompleteDelegate_Handle(LocalUserNum, FOnRejectInviteCompleteDelegate::CreateUObject(this, &ThisClass::OnRejectFriendRequestComplete, OnComplete));
    FriendsInterface->RejectInvite(LocalUserNum, FriendUserId.GetUniqueNetId().ToSharedRef().Get(), TEXT(""));
    }
  4. Finally, define the OnRejectFriendRequestComplete() function that will be called upon the reject friend request process is completed. This function prints a log to show whether the reject received friend request process was successful or not, trigger the callback delegate, and shows a pop-up telling the player the status of their request.

    void UFriendsSubsystem_Starter::OnRejectFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnRejectFriendRequestComplete OnComplete)
    {
    PromptSubsystem->HideLoading();

    FriendsInterface->ClearOnRejectInviteCompleteDelegate_Handle(LocalUserNum, OnRejectFriendRequestCompleteDelegateHandle);

    if (bWasSuccessful)
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Success to reject a friend request."));

    PromptSubsystem->ShowMessagePopUp(MESSAGE_PROMPT_TEXT, SUCCESS_REJECT_FRIEND_REQUEST);
    OnComplete.ExecuteIfBound(true, TEXT(""));
    }
    else
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Failed to reject a friend request. Error: %s"), *ErrorStr);

    PromptSubsystem->ShowMessagePopUp(ERROR_PROMPT_TEXT, FText::FromString(ErrorStr));
    OnComplete.ExecuteIfBound(false, ErrorStr);
    }
    }
  5. Congratulations! Your reject friend request functionalities are completed.

Implement Cancel Sent Friend Request

In this section, you will implement functionality to cancel a sent friend request.

  1. Open the FriendsSubsystem_Starter class header file and declare the following function.

    public:
    void CancelFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnCancelFriendRequestComplete& OnComplete = FOnCancelFriendRequestComplete());
  2. You also need to create a callback function to handle when canceling the sent friend request process is completed.

    protected:
    void OnCancelFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnCancelFriendRequestComplete OnComplete);
  3. Now, let's define the functions above. Open the FriendsSubsystem_Starter class CPP file and define the CancelFriendRequest() function. This function will cancel a sent friend request and call the OnCancelFriendRequestComplete() function to handle the callback.

    void UFriendsSubsystem_Starter::CancelFriendRequest(const APlayerController* PC, const FUniqueNetIdRepl FriendUserId, const FOnCancelFriendRequestComplete& OnComplete)
    {
    if (!ensure(FriendsInterface) || !ensure(PromptSubsystem))
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Cannot cancel friend request. Friends Interface or Prompt Subsystem is not valid."));
    return;
    }

    PromptSubsystem->ShowLoading(CANCEL_FRIEND_REQUEST_MESSAGE);

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);
    OnCancelFriendRequestCompleteDelegateHandle = FriendsInterface->AddOnDeleteFriendCompleteDelegate_Handle(LocalUserNum, FOnDeleteFriendCompleteDelegate::CreateUObject(this, &ThisClass::OnCancelFriendRequestComplete, OnComplete));
    FriendsInterface->DeleteFriend(LocalUserNum, FriendUserId.GetUniqueNetId().ToSharedRef().Get(), TEXT(""));
    }
  4. Finally, define the OnCancelFriendRequestComplete() function that will be called upon the cancel sent friend request process is completed. This function simply prints a log to show whether the cancel sent friend request process was successful or not and trigger the callback delegate.

    void UFriendsSubsystem_Starter::OnCancelFriendRequestComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& FriendId, const FString& ListName, const FString& ErrorStr, const FOnCancelFriendRequestComplete OnComplete)
    {
    PromptSubsystem->HideLoading();

    FriendsInterface->ClearOnDeleteFriendCompleteDelegate_Handle(LocalUserNum, OnCancelFriendRequestCompleteDelegateHandle);

    if (bWasSuccessful)
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Success to cancel a friend request."));

    PromptSubsystem->ShowMessagePopUp(MESSAGE_PROMPT_TEXT, SUCCESS_CANCEL_FRIEND_REQUEST);
    OnComplete.ExecuteIfBound(true, TEXT(""));
    }
    else
    {
    UE_LOG_FRIENDS_ESSENTIALS(Warning, TEXT("Failed to cancel a friend request. Error: %s"), *ErrorStr);

    PromptSubsystem->ShowMessagePopUp(ERROR_PROMPT_TEXT, FText::FromString(ErrorStr));
    OnComplete.ExecuteIfBound(false, ErrorStr);
    }
    }
  5. Congratulations! Your cancel sent friend request functionality is completed.

Listen On Friend List Updated

When the players accept, reject, or cancel friend requests, the cached friend list will be updated automatically by AccelByte OSS. In this section, you will learn how to bind a delegate to be executed when the friend list is updated. It will be useful when you need to update the displayed entries widgets later.

  1. Open the FriendsSubsystem_Starter class header file and create the following function declarations.

    public:
    void BindOnCachedFriendsDataUpdated(const APlayerController* PC, const FOnCachedFriendsDataUpdated& Delegate);
    void UnbindOnCachedFriendsDataUpdated(const APlayerController* PC);
  2. Then, open the FriendsSubsystem_Starter class CPP file and create the definitions for the functions above. Let's start with the BindOnCachedFriendsDataUpdated() function.

    void UFriendsSubsystem_Starter::BindOnCachedFriendsDataUpdated(const APlayerController* PC, const FOnCachedFriendsDataUpdated& Delegate)
    {
    ensure(FriendsInterface);

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);

    // Add on friends changed delegate.
    OnFriendsChangeDelegateHandles.Add(LocalUserNum, FriendsInterface->AddOnFriendsChangeDelegate_Handle(LocalUserNum, FOnFriendsChangeDelegate::CreateWeakLambda(this, [Delegate]() { Delegate.ExecuteIfBound(); })));
    }
  3. Next, create the definition for the UnbindOnCachedFriendsDataUpdated() function.

    void UFriendsSubsystem_Starter::UnbindOnCachedFriendsDataUpdated(const APlayerController* PC)
    {
    ensure(FriendsInterface);

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);

    // Clear on friends changed delegate.
    FDelegateHandle TempHandle = OnFriendsChangeDelegateHandles[LocalUserNum];
    if (TempHandle.IsValid())
    {
    FriendsInterface->ClearOnFriendsChangeDelegate_Handle(LocalUserNum, TempHandle);
    }
    }
  4. Now let's understand the idea behind those functions. So basically, you can bind a delegate to be executed when the friend list is updated by using the BindOnCachedFriendsDataUpdated() function. To unbind that delegate, you can use the UnbindOnCachedFriendsDataUpdated() function. You will use these functions to update displayed entries widgets in the next section.

Resources