メインコンテンツまでスキップ

パーティセッションを実装する - パーティ入門 - (Unreal Engine モジュール)

Last updated on May 30, 2024

Unwrap the online session

Creating a party is essentially just creating a new session of type "party", so managing a party is similar to managing an online session. In this section, you will learn how to set up essential party features using the AccelByte Gaming Services (AGS) Online Subsystem (OSS).

In the Byte Wars project, there is already an online session class created named PartyOnlineSession. This class is the child of the USessionEssentialsOnlineSession class, which is the class you used to create a session in the previous Byte Wars module (Session Essentials). The PartyOnlineSession class contains party-related features, but in this tutorial, you will use a starter version of it so you can implement party-related features from scratch.

What's in the starter pack

To follow this tutorial, starter online session class named PartyOnlineSession_Starter has been prepared for you. This class consists of the following files:

  • The Header file, which can be found in /Source/AccelByteWars/TutorialModules/PartyEssentials/PartyOnlineSession_Starter.h.
  • The CPP file, can be found in /Source/AccelByteWars/TutorialModules/PartyEssentials/PartyOnlineSession_Starter.cpp.

The PartyOnlineSession_Starter class has several functions provided.

  • You have created several functions to reference some of AGS OSS interfaces. First is the FOnlineSessionV2AccelByte function, which is referenced in the UAccelByteWarsOnlineSessionBase::GetABSessionInt() function. Then, the UserInterface function, which is referenced in the UAccelByteWarsOnlineSessionBase::GetUserInt() function. You will use these to implement party features later.

    FOnlineSessionV2AccelBytePtr UAccelByteWarsOnlineSessionBase::GetABSessionInt()
    {
    return StaticCastSharedPtr<FOnlineSessionV2AccelByte>(GetSessionInt());
    }

    IOnlineUserPtr UAccelByteWarsOnlineSessionBase::GetUserInt() const
    {
    const UWorld* World = GetWorld();
    if (!ensure(World))
    {
    return nullptr;
    }

    return Online::GetUserInterface(World);
    }
  • Helper functions to trigger an event when the player leaves the party session. You will need to do this for several party features later. For example, to create a new party, the player must leave any party session first. You can bind an event to this function so that when the player leaves the party, it will create a new party session. You will see more examples later.

    void UPartyOnlineSession_Starter::OnLeavePartyToTriggerEvent(FName SessionName, bool bSucceeded, const TDelegate<void(bool bWasSuccessful)> OnComplete)
    {
    // Abort if not a party session.
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    OnComplete.ExecuteIfBound(false);
    return;
    }

    OnComplete.ExecuteIfBound(bSucceeded);
    }
  • Helper functions to query party member information, such as a display name and avatar.

    void UPartyOnlineSession_Starter::QueryUserInfo(const int32 LocalUserNum, const TArray<FUniqueNetIdRef>& UserIds, const FOnQueryUsersInfoComplete& OnComplete)
    {
    // Safety
    if (!GetUserInt())
    {
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, OnComplete]()
    {
    OnComplete.ExecuteIfBound(false, {});
    }));
    return;
    }

    TArray<FUserOnlineAccountAccelByte*> UserInfo;
    if (RetrieveUserInfoCache(UserIds, UserInfo))
    {
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, UserInfo, OnComplete]()
    {
    OnComplete.ExecuteIfBound(true, UserInfo);
    }));
    }
    // Some data does not exist in the cache, query everything
    else
    {
    // Bind delegate
    if (OnQueryUserInfoCompleteDelegateHandle.IsValid())
    {
    GetUserInt()->OnQueryUserInfoCompleteDelegates->Remove(OnQueryUserInfoCompleteDelegateHandle);
    OnQueryUserInfoCompleteDelegateHandle.Reset();
    }
    OnQueryUserInfoCompleteDelegateHandle = GetUserInt()->OnQueryUserInfoCompleteDelegates->AddWeakLambda(
    this, [OnComplete, this](
    int32 LocalUserNum,
    bool bSucceeded,
    const TArray<FUniqueNetIdRef>& UserIds,
    const FString& ErrorMessage)
    {
    OnQueryUserInfoComplete(LocalUserNum, bSucceeded, UserIds, ErrorMessage, OnComplete);
    });

    if (!GetUserInt()->QueryUserInfo(LocalUserNum, UserIds))
    {
    OnQueryUserInfoComplete(LocalUserNum, false, UserIds, "", OnComplete);
    }
    }
    }

    void UPartyOnlineSession_Starter::OnQueryUserInfoComplete(int32 LocalUserNum, bool bSucceeded, const TArray<FUniqueNetIdRef>& UserIds, const FString& ErrorMessage, const FOnQueryUsersInfoComplete& OnComplete)
    {
    // reset delegate handle
    GetUserInt()->OnQueryUserInfoCompleteDelegates->Remove(OnQueryUserInfoCompleteDelegateHandle);
    OnQueryUserInfoCompleteDelegateHandle.Reset();

    if (bSucceeded)
    {
    TArray<FUserOnlineAccountAccelByte*> OnlineUsers;
    if (!RetrieveUserInfoCache(UserIds, OnlineUsers))
    {
    CacheUserInfo(LocalUserNum, UserIds);

    for (const FUniqueNetIdRef& UserId : UserIds)
    {
    TSharedPtr<FOnlineUser> OnlineUserPtr = GetUserInt()->GetUserInfo(LocalUserNum, UserId.Get());
    if (OnlineUserPtr.IsValid())
    {
    TSharedPtr<FUserOnlineAccountAccelByte> AbUserPtr = StaticCastSharedPtr<
    FUserOnlineAccountAccelByte>(OnlineUserPtr);
    OnlineUsers.AddUnique(AbUserPtr.Get());
    }
    }
    }
    OnComplete.ExecuteIfBound(true, OnlineUsers);
    }
    else
    {
    OnComplete.ExecuteIfBound(false, {});
    }
    }
  • Helper functions to prompt push notifications for party events, such as when a new member joins or leaves a party, a party invitation response, etc.

    UPromptSubsystem* UPartyOnlineSession_Starter::GetPromptSubystem()
    {
    UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
    if (!GameInstance)
    {
    return nullptr;
    }

    return GameInstance->GetSubsystem<UPromptSubsystem>();
    }
  • There are two pre-defined delegates that you can use to bind and unbind your party event delegates later. These functions will be called when the online session is initialized and deinitialized respectively.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    // TODO: Bind your party event delegates here.
    }

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    // TODO: Unbind your party event delegates here.
    }

Implement party utilities

In this section, you will implement some party utilities that you can use later, such as displaying party members. You will implement getting the party member list and getting the current party leader.

  1. First, declare several functions. Open the PartyOnlineSession_Starter class Header file and add the following code:

    public:
    virtual TArray<FUniqueNetIdRef> GetPartyMembers() override;
    virtual FUniqueNetIdPtr GetPartyLeader() override;
    virtual bool IsInParty(const FUniqueNetIdPtr UserId);
    virtual bool IsPartyLeader(const FUniqueNetIdPtr UserId) override;
  2. Then, open the PartyOnlineSession_Starter class CPP file and define the functions as follows:

    TArray<FUniqueNetIdRef> UPartyOnlineSession_Starter::GetPartyMembers()
    {
    if (GetABSessionInt())
    {
    const FNamedOnlineSession* PartySession = GetABSessionInt()->GetNamedSession(GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession));
    if (PartySession)
    {
    return PartySession->RegisteredPlayers;
    }
    }

    return TArray<FUniqueNetIdRef>();
    }

    FUniqueNetIdPtr UPartyOnlineSession_Starter::GetPartyLeader()
    {
    if (GetABSessionInt())
    {
    const FNamedOnlineSession* PartySession = GetABSessionInt()->GetNamedSession(GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession));
    if (PartySession)
    {
    return GetABSessionInt()->GetSessionLeaderId(PartySession);
    }
    }

    return nullptr;
    }

    bool UPartyOnlineSession_Starter::IsInParty(const FUniqueNetIdPtr UserId)
    {
    if (!UserId)
    {
    return false;
    }

    const TPartyMemberArray Members = GetPartyMembers();
    for (const auto& Member : Members)
    {
    if (!Member.Get().IsValid())
    {
    continue;
    }

    if (Member.Get() == UserId.ToSharedRef().Get())
    {
    return true;
    }
    }

    return false;
    }

    bool UPartyOnlineSession_Starter::IsPartyLeader(const FUniqueNetIdPtr UserId)
    {
    return GetPartyLeader() && UserId && UserId.ToSharedRef().Get() == GetPartyLeader().ToSharedRef().Get();
    }

Implement party creation

In this section, you will implement features to create a new party session.

  1. First, open the PartyOnlineSession_Starter class Header file. Then, create a new variable that defines your party session template. Make sure its value is the same as the party session template you created in the previous tutorial (Set up a party session).

    private:
    const FString PartySessionTemplate = FString("unreal-party");
  2. In the same file, declare a function to create a party session.

    public:
    virtual void CreateParty(const int32 LocalUserNum) override;
  3. Then, declare a callback function that will be called when the party creation process completes.

    protected:
    virtual void OnCreatePartyComplete(FName SessionName, bool bSucceeded) override;
  4. Next, declare a delegate that will be called when the party creation process completes. You can use this delegate to trigger events when the party is created. For example, you can bind a UI event to display the newly created party's members.

    public:
    virtual FOnCreateSessionComplete* GetOnCreatePartyCompleteDelegates()
    {
    return &OnCreatePartyCompleteDelegates;
    }

    private:
    FOnCreateSessionComplete OnCreatePartyCompleteDelegates;
  5. Now, define these functions starting with CreateParty(). Open the PartyOnlineSession_Starter class CPP file and add the following code. This function leaves any party session first before creating a new party session.

    void UPartyOnlineSession_Starter::CreateParty(const int32 LocalUserNum)
    {
    const FName SessionName = GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession);

    // Safety.
    if (!GetABSessionInt())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot create a party. Session Interface is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnCreatePartyComplete(SessionName, false);
    }));
    return;
    }

    // Always create a new party. Thus, leave any left-over party session first.
    if (GetABSessionInt()->IsInPartySession())
    {
    if (OnLeaveSessionForTriggerDelegateHandle.IsValid())
    {
    GetOnLeaveSessionCompleteDelegates()->Remove(OnLeaveSessionForTriggerDelegateHandle);
    OnLeaveSessionForTriggerDelegateHandle.Reset();
    }

    OnLeaveSessionForTriggerDelegateHandle = GetOnLeaveSessionCompleteDelegates()->AddUObject(
    this,
    &ThisClass::OnLeavePartyToTriggerEvent,
    TDelegate<void(bool)>::CreateWeakLambda(this, [this, LocalUserNum, SessionName](bool bWasSuccessful)
    {
    if (bWasSuccessful)
    {
    CreateParty(LocalUserNum);
    }
    else
    {
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnCreatePartyComplete(SessionName, false);
    }));
    }
    }
    ));

    LeaveSession(SessionName);
    return;
    }

    // Create a new party session.
    CreateSession(
    LocalUserNum,
    SessionName,
    FOnlineSessionSettings(),
    EAccelByteV2SessionType::PartySession,
    PartySessionTemplate);
    }
  6. Next, define the OnCreatePartyComplete() function by adding the code below. This function broadcasts the on-complete delegate you defined in the Header file earlier.

    void UPartyOnlineSession_Starter::OnCreatePartyComplete(FName SessionName, bool bSucceeded)
    {
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    if (bSucceeded)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to create a party"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to create a party"));
    }

    OnCreatePartyCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
  7. Then, you need to bind the OnCreatePartyComplete() so it will be called when the party creation process completes. You can do this by adding the code below in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    }
    }
  8. Finally, when the online session is deinitialized, you need to unbind to stop listening to the party creation process completion event. You can do this by adding the following code in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    }
    }

Implement leave party

In this section, you will implement the feature to leave a party session.

  1. Open the PartyOnlineSession_Starter class Header file and declare a function to leave a party.

    public:
    virtual void LeaveParty(const int32 LocalUserNum) override;
  2. In the same file, declare a callback function that will be called when the leave party process completes.

    protected:
    virtual void OnLeavePartyComplete(FName SessionName, bool bSucceeded) override;
  3. Next, declare a delegate that will be called when the leave party process completes. You can use this delegate's wrapper to trigger events when the leave party process completes.

    public:
    virtual FOnDestroySessionComplete* GetOnLeavePartyCompleteDelegates()
    {
    return &OnLeavePartyCompleteDelegates;
    }

    private:
    FOnDestroySessionComplete OnLeavePartyCompleteDelegates;
  4. Now, define the these functions starting with the LeaveParty(). Open the PartyOnlineSession_Starter class CPP file and add the code below. This function creates a new party after the player leaves the original one.

    void UPartyOnlineSession_Starter::LeaveParty(const int32 LocalUserNum)
    {
    const FName SessionName = GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession);

    if (!GetABSessionInt() || !GetABSessionInt()->IsInPartySession())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot leave a party. Session Interface is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnLeavePartyComplete(SessionName, false);
    }));
    return;
    }

    // After leaving a party, automatically create a new one.
    if (OnLeaveSessionForTriggerDelegateHandle.IsValid())
    {
    GetOnLeaveSessionCompleteDelegates()->Remove(OnLeaveSessionForTriggerDelegateHandle);
    OnLeaveSessionForTriggerDelegateHandle.Reset();
    }
    OnLeaveSessionForTriggerDelegateHandle = GetOnLeaveSessionCompleteDelegates()->AddUObject(
    this,
    &ThisClass::OnLeavePartyToTriggerEvent,
    TDelegate<void(bool)>::CreateWeakLambda(this, [this, LocalUserNum, SessionName](bool bWasSuccessful)
    {
    if (bWasSuccessful)
    {
    CreateParty(LocalUserNum);
    }
    else
    {
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnLeavePartyComplete(SessionName, false);
    }));
    }
    }
    ));

    // Leave party.
    LeaveSession(SessionName);
    }
  5. Next, define the OnLeavePartyComplete() function by adding the code below. This function broadcasts the on-complete delegate you defined in the Header file.

    void UPartyOnlineSession_Starter::OnLeavePartyComplete(FName SessionName, bool bSucceeded)
    {
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    if (bSucceeded)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to leave a party"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to leave a party"));
    }

    OnLeavePartyCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
  6. Then, you need to bind the OnLeavePartyComplete() so it will be called when the leave party process completes. You can do this by adding the following code in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);
    }
    }
  7. Finally, when the online session is deinitialized, you need to unbind to stop listening to the leave party process completion event. You can do this by adding the code below in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);
    }
    }

Implement party invitation sending

In this section, you will implement the feature to send party invitations.

  1. Open the PartyOnlineSession_Starter class Header file and declare a function to send a party invitation.

    public:
    virtual void SendPartyInvite(const int32 LocalUserNum, const FUniqueNetIdPtr& Invitee) override;
  2. In the same file, declare a callback function that will be called when the send party invitation process completes.

    protected:
    virtual void OnSendPartyInviteComplete(const FUniqueNetId& Sender, FName SessionName, bool bWasSuccessful, const FUniqueNetId& Invitee) override;
  3. Next, declare a delegate that will be called when the send party invitation process completes. You can use this delegate's wrapper to trigger events when the send party invitation completes.

    public:
    virtual FOnSendSessionInviteComplete* GetOnSendPartyInviteCompleteDelegates() override
    {
    return &OnSendPartyInviteCompleteDelegates;;
    }

    private:
    FOnSendSessionInviteComplete OnSendPartyInviteCompleteDelegates;
  4. Now, define these functions starting with SendPartyInvite(). Open the PartyOnlineSession_Starter class CPP file and add the code below. This function sends a party invitation to the target invitee.

    void UPartyOnlineSession_Starter::SendPartyInvite(const int32 LocalUserNum, const FUniqueNetIdPtr& Invitee)
    {
    if (!GetABSessionInt())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot send a party invitation. Session Interface is not valid."));
    return;
    }

    const APlayerController* SenderPC = GetPlayerControllerByLocalUserNum(LocalUserNum);
    if (!SenderPC)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot send a party invitation. Sender's PlayerController is not valid."));
    return;
    }

    const FUniqueNetIdPtr SenderId = GetLocalPlayerUniqueNetId(SenderPC);
    if (!SenderId)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot send a party invitation. Sender's NetId is not valid."));
    return;
    }

    const FName SessionName = GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession);
    if (!Invitee)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot send a party invitation. Invitee's NetId is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SenderId, SessionName, Invitee]()
    {
    OnSendPartyInviteComplete(SenderId.ToSharedRef().Get(), SessionName, false, Invitee.ToSharedRef().Get());
    }));
    return;
    }

    GetABSessionInt()->SendSessionInviteToFriend(
    SenderId.ToSharedRef().Get(),
    SessionName,
    Invitee.ToSharedRef().Get());
    }
  5. Next, define the OnSendPartyInviteComplete() function by adding the code below. This function broadcasts the on-complete delegate you defined in the Header file and displays a push notification.

    void UPartyOnlineSession_Starter::OnSendPartyInviteComplete(const FUniqueNetId& Sender, FName SessionName, bool bWasSuccessful, const FUniqueNetId& Invitee)
    {
    // Abort if not a party session.
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    const FUniqueNetIdAccelByteUserRef InviteeABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(Invitee.AsShared());
    if (bWasSuccessful)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to send party invitation to %s"),
    InviteeABId->IsValid() ? *InviteeABId->GetAccelByteId() : TEXT("Unknown"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to send party invitation to %s"),
    InviteeABId->IsValid() ? *InviteeABId->GetAccelByteId() : TEXT("Unknown"));
    }

    // Display push notification.
    if (GetPromptSubystem())
    {
    GetPromptSubystem()->PushNotification(bWasSuccessful ? SUCCESS_SEND_PARTY_INVITE : FAILED_SEND_PARTY_INVITE);
    }

    OnSendPartyInviteCompleteDelegates.Broadcast(Sender, SessionName, bWasSuccessful, Invitee);
    }
  6. Then, you need to bind the OnSendPartyInviteComplete() so it will be called when the send party invitation process completes. You can do this by adding the code below in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.AddUObject(this, &ThisClass::OnSendPartyInviteComplete);
    }
    }
  7. Finally, when the online session is deinitialized, you need to unbind it to stop listening to the send party invitation process completion event. You can do this by adding the code below in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.RemoveAll(this);
    }
    }

Implement invitation acceptance and party joining

In this section, you will implement the feature to accept a party invitation and join a party session.

  1. Open the PartyOnlineSession_Starter class Header file and declare a function to join a party.

    public:
    virtual void JoinParty(const int32 LocalUserNum, const FOnlineSessionSearchResult& PartySessionResult) override;
  2. In the same file, declare a callback function that will be called when the join party process completes.

    protected:
    virtual void OnJoinPartyComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result) override;
  3. Next, declare a delegate that will be called when the join party process completes. You can use this delegate's wrapper to trigger events when the join party process completes.

    public:
    virtual FOnJoinSessionComplete* GetOnJoinPartyCompleteDelegates()
    {
    return &OnJoinPartyCompleteDelegates;
    }

    private:
    FOnJoinSessionComplete OnJoinPartyCompleteDelegates;
  4. Now, define these functions starting with JoinParty(). Open the PartyOnlineSession_Starter class CPP file and add the code below. This function leaves any party session first before joining a new party session.

    void UPartyOnlineSession_Starter::JoinParty(const int32 LocalUserNum, const FOnlineSessionSearchResult& PartySessionResult)
    {
    const FName SessionName = GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession);

    if (!GetABSessionInt())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot join a party. Session Interface is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnJoinPartyComplete(SessionName, EOnJoinSessionCompleteResult::Type::UnknownError);
    }));
    return;
    }

    // Always leave any party before joining a new party.
    if (GetABSessionInt()->IsInPartySession())
    {
    if (OnLeaveSessionForTriggerDelegateHandle.IsValid())
    {
    GetOnLeaveSessionCompleteDelegates()->Remove(OnLeaveSessionForTriggerDelegateHandle);
    OnLeaveSessionForTriggerDelegateHandle.Reset();
    }

    OnLeaveSessionForTriggerDelegateHandle = GetOnLeaveSessionCompleteDelegates()->AddUObject(
    this,
    &ThisClass::OnLeavePartyToTriggerEvent,
    TDelegate<void(bool)>::CreateWeakLambda(this, [this, LocalUserNum, PartySessionResult, SessionName](bool bWasSuccessful)
    {
    if (bWasSuccessful)
    {
    JoinParty(LocalUserNum, PartySessionResult);
    }
    else
    {
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnJoinPartyComplete(SessionName, EOnJoinSessionCompleteResult::Type::UnknownError);
    }));
    }
    }
    ));

    LeaveSession(SessionName);
    return;
    }

    // Join a new party.
    JoinSession(LocalUserNum, SessionName, PartySessionResult);
    }
  5. Next, define the OnJoinPartyComplete() function by adding the code below. This function broadcasts the on-complete delegate you defined in the Header file.

    void UPartyOnlineSession_Starter::OnJoinPartyComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
    {
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    if (Result == EOnJoinSessionCompleteResult::Type::Success)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to join a party"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to join a party"));
    }

    OnJoinPartyCompleteDelegates.Broadcast(SessionName, Result);
    }
  6. Then, you need to bind the OnJoinPartyComplete() so it will be called when the join party process completes. You can do this by adding the following code in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.AddUObject(this, &ThisClass::OnSendPartyInviteComplete);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.AddUObject(this, &ThisClass::OnJoinPartyComplete);
    }
    }
  7. Finally, when the online session is deinitialized, you need to unbind it to stop listening to the join party process completion event. You can do this by adding the code below in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.RemoveAll(this);
    }
    }

Implement party invitation rejections

In this section, you will implement the feature to reject a party invitation.

  1. Open the PartyOnlineSession_Starter class Header file and declare the following function.

    public:
    virtual void RejectPartyInvite(const int32 LocalUserNum, const FOnlineSessionInviteAccelByte& PartyInvite) override;
  2. In the same file, declare a callback function that will be called when the reject party invitation process completes.

    protected:
    virtual void OnRejectPartyInviteComplete(bool bWasSuccessful) override;
  3. Next, declare a delegate that will be called when the reject party invitation process completes. You can use this delegate's wrapper to trigger events when the reject party invitation process completes.

    public:
    virtual FOnRejectSessionInviteComplete* GetOnRejectPartyInviteCompleteDelegate() override
    {
    return &OnRejectPartyInviteCompleteDelegate;
    }

    private:
    FOnRejectSessionInviteComplete OnRejectPartyInviteCompleteDelegate;
  4. Now, define these functions starting with the RejectPartyInvite(). Open the PartyOnlineSession_Starter class CPP file and add the code below. This function rejects a party invitation received by the player. The callback will be handled by the OnRejectPartyInviteComplete() function.

    void UPartyOnlineSession_Starter::RejectPartyInvite(const int32 LocalUserNum, const FOnlineSessionInviteAccelByte& PartyInvite)
    {
    if (!GetABSessionInt())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot reject a party invitation. Session Interface is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this]()
    {
    OnRejectPartyInviteComplete(false);
    }));
    return;
    }

    const APlayerController* RejecterPC = GetPlayerControllerByLocalUserNum(LocalUserNum);
    if (!RejecterPC)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot reject a party invitation. Rejecter's PlayerController is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this]()
    {
    OnRejectPartyInviteComplete(false);
    }));
    return;
    }

    const FUniqueNetIdPtr RejecterId = GetLocalPlayerUniqueNetId(RejecterPC);
    if (!RejecterId)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot reject a party invitation. Rejecter's NetId is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this]()
    {
    OnRejectPartyInviteComplete(false);
    }));
    return;
    }

    GetABSessionInt()->RejectInvite(
    RejecterId.ToSharedRef().Get(),
    PartyInvite,
    FOnRejectSessionInviteComplete::CreateUObject(this, &ThisClass::OnRejectPartyInviteComplete));
    }
  5. Next, define the OnRejectPartyInviteComplete() function by adding the code below. This function executes the on-complete delegate you defined in the Header file.

    void UPartyOnlineSession_Starter::OnRejectPartyInviteComplete(bool bWasSuccessful)
    {
    if (bWasSuccessful)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to reject party invitation"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to reject party invitation"));
    }

    OnRejectPartyInviteCompleteDelegate.ExecuteIfBound(bWasSuccessful);
    OnRejectPartyInviteCompleteDelegate.Unbind();
    }
  6. Now, create some functions to listen for when the player party invitation is rejected so the invitation sender can be informed. Open the PartyOnlineSession_Starter class Header file and declare the following function:

    protected:
    virtual void OnPartyInviteRejected(FName SessionName, const FUniqueNetId& RejecterId) override;
  7. Next, declare a delegate that will be called when the party invitation is rejected. You can use this delegate's wrapper to trigger events when the party invitation is rejected.

    public:
    virtual FOnSessionInviteRejected* GetOnPartyInviteRejectedDelegates() override
    {
    return &OnPartyInviteRejectedDelegates;
    }

    private:
    FOnSessionInviteRejected OnPartyInviteRejectedDelegates;
  8. Now, define the OnPartyInviteRejected() function. Open the PartyOnlineSession_Starter class CPP file and add the code below. This function broadcasts the on-complete delegate you have defined in the Header file and also pushes a notification to display who rejects the party invitation.

    void UPartyOnlineSession_Starter::OnPartyInviteRejected(FName SessionName, const FUniqueNetId& RejecterId)
    {
    // Abort if not a party session.
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    const FUniqueNetIdAccelByteUserRef RejecterABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(RejecterId.AsShared());
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Party invitation is rejected by %s"),
    RejecterABId->IsValid() ? *RejecterABId->GetAccelByteId() : TEXT("Unknown"));

    // Display push notification to show who rejected the invitation.
    QueryUserInfo(0, TPartyMemberArray{ RejecterABId },
    FOnQueryUsersInfoComplete::CreateWeakLambda(this, [this, RejecterABId](const bool bSucceeded, const TArray<FUserOnlineAccountAccelByte*>& UsersInfo)
    {
    if (UsersInfo.IsEmpty() || !UsersInfo[0] || !GetPromptSubystem())
    {
    return;
    }

    FUserOnlineAccountAccelByte MemberInfo = *UsersInfo[0];

    const FText NotifMessage = FText::Format(PARTY_INVITE_REJECTED_MESSAGE, FText::FromString(
    MemberInfo.GetDisplayName().IsEmpty() ?
    UTutorialModuleOnlineUtility::GetUserDefaultDisplayName(RejecterABId.Get()) :
    MemberInfo.GetDisplayName()
    ));

    FString AvatarURL;
    MemberInfo.GetUserAttribute(ACCELBYTE_ACCOUNT_GAME_AVATAR_URL, AvatarURL);

    GetPromptSubystem()->PushNotification(NotifMessage, AvatarURL, true);
    }
    ));

    OnPartyInviteRejectedDelegates.Broadcast(SessionName, RejecterId);
    }
  9. Then, you need to bind the OnPartyInviteRejected() so it will be called when the party invitation is rejected. You can do this by adding the code below in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.AddUObject(this, &ThisClass::OnSendPartyInviteComplete);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.AddUObject(this, &ThisClass::OnJoinPartyComplete);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.AddUObject(this, &ThisClass::OnPartyInviteRejected);
    }
    }
  10. Finally, when the online session is deinitialized, you need to unbind it to stop listening to the party invitation rejected event. You can do this by adding the code below in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.RemoveAll(this);
    }
    }

Handle received party invitations

In the previous sections, you have implemented accepting and rejecting party invitations. In this section, you will implement the features to handle the received party invitation. This implementation will display a prompt to let your player choose to accept or reject a party invitation.

  1. Open the PartyOnlineSession_Starter class Header file and declare the following function:

    protected:
    virtual void OnPartyInviteReceived(const FUniqueNetId& UserId, const FUniqueNetId& FromId, const FOnlineSessionInviteAccelByte& PartyInvite) override;
  2. Next, declare a delegate that will be called when the party invitation is received. You can use this delegate's wrapper to trigger events when the party invitation is received.

    public:
    virtual FOnV2SessionInviteReceivedDelegate* GetOnPartyInviteReceivedDelegate() override
    {
    return &OnPartyInviteReceivedDelegate;
    }

    private:
    FOnV2SessionInviteReceivedDelegate OnPartyInviteReceivedDelegate;
  3. Now, define this function. Open the PartyOnlineSession_Starter class CPP file and add the code below. What this function does is push a notification to inform the player to accept or reject the party invitation. It also broadcasts the on-complete delegate you defined in the Header file.

    void UPartyOnlineSession_Starter::OnPartyInviteReceived(const FUniqueNetId& UserId, const FUniqueNetId& FromId, const FOnlineSessionInviteAccelByte& PartyInvite)
    {
    // Abort if not a party session.
    if (UserId == FromId || PartyInvite.SessionType != EAccelByteV2SessionType::PartySession)
    {
    return;
    }

    const APlayerController* PC = GetPlayerControllerByUniqueNetId(UserId.AsShared());
    if (!PC)
    {
    return;
    }

    const FUniqueNetIdAccelByteUserRef SenderABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(UserId.AsShared());
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Receives party invitation from %s"),
    SenderABId->IsValid() ? *SenderABId->GetAccelByteId() : TEXT("Unknown"));

    const int32 LocalUserNum = GetLocalUserNumFromPlayerController(PC);

    // Display push notification to allow the player to accept/reject the party invitation.
    QueryUserInfo(0, TPartyMemberArray{ SenderABId },
    FOnQueryUsersInfoComplete::CreateWeakLambda(this, [this, LocalUserNum, SenderABId, PartyInvite]
    (const bool bSucceeded, const TArray<FUserOnlineAccountAccelByte*>& UsersInfo)
    {
    if (UsersInfo.IsEmpty() || !UsersInfo[0] || !GetPromptSubystem())
    {
    return;
    }

    FUserOnlineAccountAccelByte MemberInfo = *UsersInfo[0];

    const FText NotifMessage = FText::Format(PARTY_INVITE_RECEIVED_MESSAGE, FText::FromString(
    MemberInfo.GetDisplayName().IsEmpty() ?
    UTutorialModuleOnlineUtility::GetUserDefaultDisplayName(SenderABId.Get()) :
    MemberInfo.GetDisplayName()
    ));

    FString AvatarURL;
    MemberInfo.GetUserAttribute(ACCELBYTE_ACCOUNT_GAME_AVATAR_URL, AvatarURL);

    GetPromptSubystem()->PushNotification(
    NotifMessage,
    AvatarURL,
    true,
    ACCEPT_PARTY_INVITE_MESSAGE,
    REJECT_PARTY_INVITE_MESSAGE,
    FText::GetEmpty(),
    FPushNotificationDelegate::CreateWeakLambda(this, [this, LocalUserNum, PartyInvite](EPushNotificationActionResult ActionButtonResult)
    {
    switch (ActionButtonResult)
    {
    // Show accept party invitation confirmation.
    case EPushNotificationActionResult::Button1:
    DisplayJoinPartyConfirmation(LocalUserNum, PartyInvite);
    break;
    // Reject party invitation.
    case EPushNotificationActionResult::Button2:
    RejectPartyInvite(LocalUserNum, PartyInvite);
    break;
    }
    }
    ));
    }
    ));

    OnPartyInviteReceivedDelegate.ExecuteIfBound(UserId, FromId, PartyInvite);
    OnPartyInviteReceivedDelegate.Unbind();
    }
  4. If the player accepts the invitation, the function above will call a new function named DisplayJoinPartyConfirmation(). Create its declaration first. Open the PartyOnlineSession_Starter class Header file and add the following code.

    protected:
    void DisplayJoinPartyConfirmation(const int32 LocalUserNum, const FOnlineSessionInviteAccelByte& PartyInvite);
  5. Next, define this function. Open the PartyOnlineSession_Starter class CPP file and add the code below. This function displays a dialog to ask a player that's already in a party whether they want to leave and join a new party.

    void UPartyOnlineSession_Starter::DisplayJoinPartyConfirmation(const int32 LocalUserNum, const FOnlineSessionInviteAccelByte& PartyInvite)
    {
    // Join the party if not in any party yet.
    if (!GetABSessionInt()->IsInPartySession())
    {
    JoinParty(LocalUserNum, PartyInvite.Session);
    return;
    }

    // Show confirmation to leave the current party and join the new party.
    GetPromptSubystem()->ShowDialoguePopUp(
    PARTY_POPUP_MESSAGE,
    JOIN_NEW_PARTY_CONFIRMATION_MESSAGE,
    EPopUpType::ConfirmationYesNo,
    FPopUpResultDelegate::CreateWeakLambda(this, [this, LocalUserNum, PartyInvite](EPopUpResult Result)
    {
    switch (Result)
    {
    case EPopUpResult::Confirmed:
    // If confirmed, join the new party.
    JoinParty(LocalUserNum, PartyInvite.Session);
    break;
    case EPopUpResult::Declined:
    // If declined, reject the party invitation.
    RejectPartyInvite(LocalUserNum, PartyInvite);
    break;
    }
    }
    ));
    }
  6. Then, you need to bind the OnPartyInviteReceived() so it will be called when the party invitation is received. You can do this by adding the code below in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.AddUObject(this, &ThisClass::OnSendPartyInviteComplete);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.AddUObject(this, &ThisClass::OnJoinPartyComplete);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.AddUObject(this, &ThisClass::OnPartyInviteRejected);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.AddUObject(this, &ThisClass::OnPartyInviteReceived);
    }
    }
  7. Finally, when the online session is deinitialized, you need to unbind to stop listening to the party invitation received event. You can do this by adding the following code in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.RemoveAll(this);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.RemoveAll(this);
    }
    }

Implement removing (kicking) party members

In this section, you will implement the feature to kick a player from a party.

  1. Open the PartyOnlineSession_Starter class Header file and declare the following function:

    public:
    virtual void KickPlayerFromParty(const int32 LocalUserNum, const FUniqueNetIdPtr& KickedPlayer) override;
  2. In the same file, declare a callback function that will be called when the kick party member process completes.

    protected:
    virtual void OnKickPlayerFromPartyComplete(bool bWasSuccessful, const FUniqueNetId& KickedPlayer) override;
  3. Next, declare a delegate that will be called when the kick player from the party process completes. You can use this delegate's wrapper to trigger events when the kick player from the party process completes.

    public:
    virtual FOnKickPlayerComplete* GetOnKickPlayerFromPartyDelegate() override
    {
    return &OnKickPlayerFromPartyCompleteDelegate;
    }

    private:
    FOnKickPlayerComplete OnKickPlayerFromPartyCompleteDelegate;
  4. Now, define these functions starting with KickPlayerFromParty(). Open the PartyOnlineSession_Starter class CPP file and add the code below. This function kicks the target player from the party. The callback will be handled by the OnKickPlayerFromPartyComplete() function.

    void UPartyOnlineSession_Starter::KickPlayerFromParty(const int32 LocalUserNum, const FUniqueNetIdPtr& KickedPlayer)
    {
    if (!GetABSessionInt())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot kick a player from the party. Session Interface is not valid."));
    return;
    }

    if (!KickedPlayer)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot kick a player from the party. KickedPlayer's NetId is not valid."));
    return;
    }

    const APlayerController* PC = GetPlayerControllerByLocalUserNum(LocalUserNum);
    if (!PC)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot kick a player from the party. Kicker's PlayerController is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, KickedPlayer]()
    {
    OnKickPlayerFromPartyComplete(false, KickedPlayer.ToSharedRef().Get());
    }));
    return;
    }

    const FUniqueNetIdPtr PlayerNetId = GetLocalPlayerUniqueNetId(PC);
    if (!PlayerNetId)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot kick a player from the party. Kicker's NetId is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, KickedPlayer]()
    {
    OnKickPlayerFromPartyComplete(false, KickedPlayer.ToSharedRef().Get());
    }));
    return;
    }

    GetABSessionInt()->KickPlayer(
    PlayerNetId.ToSharedRef().Get(),
    GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession),
    KickedPlayer.ToSharedRef().Get(),
    FOnKickPlayerComplete::CreateUObject(this, &ThisClass::OnKickPlayerFromPartyComplete));
    }
  5. Next, define the OnKickPlayerFromPartyComplete() function by adding the code below. This function executes the on-complete delegate you defined in the Header file earlier.

    void UPartyOnlineSession_Starter::OnKickPlayerFromPartyComplete(bool bWasSuccessful, const FUniqueNetId& KickedPlayer)
    {
    const FUniqueNetIdAccelByteUserRef KickedPlayerABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(KickedPlayer.AsShared());
    if (bWasSuccessful)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to kick %s from the party."),
    KickedPlayerABId->IsValid() ? *KickedPlayerABId->GetAccelByteId() : TEXT("Unknown"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to kick %s from the party."),
    KickedPlayerABId->IsValid() ? *KickedPlayerABId->GetAccelByteId() : TEXT("Unknown"));
    }

    OnKickPlayerFromPartyCompleteDelegate.ExecuteIfBound(bWasSuccessful, KickedPlayer);
    OnKickPlayerFromPartyCompleteDelegate.Unbind();
    }
  6. You have implemented the kicking of players from a party feature. Now, create several functions to listen for when the player is kicked so the kicked player can be informed. Open the PartyOnlineSession_Starter class Header file and declare the following function:

    protected:
    virtual void OnKickedFromParty(FName SessionName) override;
  7. Next, declare a delegate that will be called when the player is kicked from the party. You can use this delegate's wrapper to trigger events when the player is kicked from the party.

    public:
    virtual FOnKickedFromSession* GetOnKickedFromPartyDelegates() override
    {
    return &OnKickedFromPartyDelegates;
    }

    private:
    FOnKickedFromSession OnKickedFromPartyDelegates;
  8. Now, define the OnKickedFromParty() function. Open the PartyOnlineSession_Starter class CPP file and add the following code. This function broadcasts the on-complete delegate you defined in the Header file and pushes a notification to the player kicked from the party.

    void UPartyOnlineSession_Starter::OnKickedFromParty(FName SessionName)
    {
    // Abort if not a party session.
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    UE_LOG_PARTYESSENTIALS(Log, TEXT("Current logged player is kicked from the party"));

    // Display push notification.
    if (GetPromptSubystem())
    {
    GetPromptSubystem()->PushNotification(KICKED_FROM_PARTY_MESSAGE);
    }

    OnKickedFromPartyDelegates.Broadcast(SessionName);
    }
  9. Then, you need to bind the OnKickedFromParty() function so it will be called when the player is kicked from the party session. You can do this by adding the code below in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.AddUObject(this, &ThisClass::OnSendPartyInviteComplete);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.AddUObject(this, &ThisClass::OnJoinPartyComplete);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.AddUObject(this, &ThisClass::OnPartyInviteRejected);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.AddUObject(this, &ThisClass::OnPartyInviteReceived);

    GetABSessionInt()->OnKickedFromSessionDelegates.AddUObject(this, &ThisClass::OnKickedFromParty);
    }
    }
  10. Finally, when the online session is deinitialized, you need to unbind it to stop listening to the player kicked from the party session event. You can do this by adding the code below in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.RemoveAll(this);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.RemoveAll(this);

    GetABSessionInt()->OnKickedFromSessionDelegates.RemoveAll(this);
    }
    }

Implement promoting a party member to leader

In this section, you will implement the feature to promote a new party leader.

  1. Open the PartyOnlineSession_Starter class Header file and declare the following function:

    public:
    virtual void PromotePartyLeader(const int32 LocalUserNum, const FUniqueNetIdPtr& NewLeader) override;
  2. In the same file, declare a callback function that will be called when the promote party leader process completes.

    protected:
    virtual void OnPromotePartyLeaderComplete(const FUniqueNetId& NewLeader, const FOnlineError& Result) override;
  3. Next, declare a delegate that will be called when the promote party leader process completes. You can use this delegate's wrapper to trigger events when the promote party leader process completes.

    public:
    virtual FOnPromotePartySessionLeaderComplete* GetOnPromotePartyLeaderCompleteDelegate() override
    {
    return &OnPromotePartyLeaderCompleteDelegate;
    }

    private:
    FOnPromotePartySessionLeaderComplete OnPromotePartyLeaderCompleteDelegate;
  4. Now, define these functions starting with the PromotePartyLeader() function. Open the PartyOnlineSession_Starter class CPP file and add the code below. This function promotes a new party leader. Then, the callback will be handled by the OnPromotePartyLeaderComplete() function.

    void UPartyOnlineSession_Starter::PromotePartyLeader(const int32 LocalUserNum, const FUniqueNetIdPtr& NewLeader)
    {
    if (!GetABSessionInt())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot promote new party leader. Session Interface is not valid."));
    return;
    }

    if (!NewLeader)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot promote new party leader. New Leader NetId is not valid."));
    return;
    }

    const APlayerController* PC = GetPlayerControllerByLocalUserNum(LocalUserNum);
    if (!PC)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot promote new party leader. Promoter's PlayerController is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, NewLeader]()
    {
    OnPromotePartyLeaderComplete(NewLeader.ToSharedRef().Get(), FOnlineError(false));
    }));
    return;
    }

    const FUniqueNetIdPtr PlayerNetId = GetLocalPlayerUniqueNetId(PC);
    if (!PlayerNetId)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot promote new party leader. Promoter's NetId is not valid."));
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, NewLeader]()
    {
    OnPromotePartyLeaderComplete(NewLeader.ToSharedRef().Get(), FOnlineError(false));
    }));
    return;
    }

    GetABSessionInt()->PromotePartySessionLeader(
    PlayerNetId.ToSharedRef().Get(),
    GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession),
    NewLeader.ToSharedRef().Get(),
    FOnPromotePartySessionLeaderComplete::CreateUObject(this, &ThisClass::OnPromotePartyLeaderComplete));
    }
  5. Next, define the OnPromotePartyLeaderComplete() function by adding the code below. This function executes the on-complete delegate you defined in the Header file.

    void UPartyOnlineSession_Starter::OnPromotePartyLeaderComplete(const FUniqueNetId& NewLeader, const FOnlineError& Result)
    {
    const FUniqueNetIdAccelByteUserRef NewLeaderABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(NewLeader.AsShared());
    if (Result.bSucceeded)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to promote %s as the new party leader."),
    NewLeaderABId->IsValid() ? *NewLeaderABId->GetAccelByteId() : TEXT("Unknown"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to promote %s as the new party leader."),
    NewLeaderABId->IsValid() ? *NewLeaderABId->GetAccelByteId() : TEXT("Unknown"));
    }

    OnPromotePartyLeaderCompleteDelegate.ExecuteIfBound(NewLeader, Result);
    OnPromotePartyLeaderCompleteDelegate.Unbind();
    }
  6. The feature to promote the party leader is complete. When a new party leader is set, the party session update event will be called. You can use this event to inform other party members that a new leader is set. You will learn how to handle party session update events in the next tutorial. For now, create a function to show that a new party leader is set. To do this, you can cache the last party member and compare it with the new party leader. If they are not the same, then a new party leader is set. Open the PartyOnlineSession_Starter class Header file and you declare the following variable. You will use this to cache the last party leader.

    private:
    FUniqueNetIdPtr LastPartyLeader;
  7. When the player creates or joins a new party, cache the current party leader. This acts as the initial value for the LastPartyLeader variable. Now, open the PartyOnlineSession_Starter class CPP file and add the following code:

    void UPartyOnlineSession_Starter::OnCreatePartyComplete(FName SessionName, bool bSucceeded)
    {
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    if (bSucceeded)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to create a party"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to create a party"));
    }

    // Cache the party leader.
    LastPartyLeader = GetPartyLeader();

    OnCreatePartyCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
    void UPartyOnlineSession_Starter::OnJoinPartyComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
    {
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    if (Result == EOnJoinSessionCompleteResult::Type::Success)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to join a party"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to join a party"));
    }

    // Cache the party leader.
    LastPartyLeader = GetPartyLeader();

    OnJoinPartyCompleteDelegates.Broadcast(SessionName, Result);
    }
  8. Now, create a function to display a notification if a new party leader is set. Open the PartyOnlineSession_Starter class Header file and declare the following function. Later, you will call this function when the party session update event is invoked.

    protected:
    void DisplayCurrentPartyLeader();
  9. Next, open the PartyOnlineSession_Starter class CPP file and define the function above. This function pushes a notification to show the new party leader information.

    void UPartyOnlineSession_Starter::DisplayCurrentPartyLeader()
    {
    // Abort if the party leader is the same.
    if (LastPartyLeader && IsPartyLeader(LastPartyLeader))
    {
    return;
    }

    LastPartyLeader = GetPartyLeader();
    const FUniqueNetIdAccelByteUserPtr LeaderABId = StaticCastSharedPtr<const FUniqueNetIdAccelByteUser>(LastPartyLeader);

    // Query party leader information and then display a notification.
    QueryUserInfo(0, TPartyMemberArray{ LeaderABId.ToSharedRef() },
    FOnQueryUsersInfoComplete::CreateWeakLambda(this, [this, LeaderABId]
    (const bool bSucceeded, const TArray<FUserOnlineAccountAccelByte*>& UsersInfo)
    {
    if (UsersInfo.IsEmpty() || !UsersInfo[0] || !GetPromptSubystem())
    {
    return;
    }

    FUserOnlineAccountAccelByte MemberInfo = *UsersInfo[0];

    const FText NotifMessage = FText::Format(PARTY_NEW_LEADER_MESSAGE, FText::FromString(
    MemberInfo.GetDisplayName().IsEmpty() ?
    UTutorialModuleOnlineUtility::GetUserDefaultDisplayName(LeaderABId.ToSharedRef().Get()) :
    MemberInfo.GetDisplayName()
    ));

    FString AvatarURL;
    MemberInfo.GetUserAttribute(ACCELBYTE_ACCOUNT_GAME_AVATAR_URL, AvatarURL);

    GetPromptSubystem()->PushNotification(NotifMessage, AvatarURL, true);
    }
    ));
    }

Handle party session updates

The party session will be updated when party-related events occur. For example, when a new party member joins or leaves, a new party leader is set, etc. In this section, you will implement some features to handle those party session updates.

  1. Open the PartyOnlineSession_Starter class Header file and declare the following variable:

    private:
    /* Helper variable to cache party member status. */
    TMap<FString, bool> PartyMemberStatus;
  2. In the same file, declare the following functions:

    protected:
    virtual void OnPartyMembersChange(FName SessionName, const FUniqueNetId& Member, bool bJoined) override;
    virtual void OnPartySessionUpdateReceived(FName SessionName) override;
  3. In the same file, declare delegates and their wrapper to be broadcasted by the function you just declared above.

    public:
    virtual FOnSessionParticipantsChange* GetOnPartyMembersChangeDelegates() override
    {
    return &OnPartyMembersChangeDelegates;
    }
    virtual FOnSessionUpdateReceived* GetOnPartySessionUpdateReceivedDelegates() override
    {
    return &OnPartySessionUpdateReceivedDelegates;
    }

    private:
    FOnSessionParticipantsChange OnPartyMembersChangeDelegates;
    FOnSessionUpdateReceived OnPartySessionUpdateReceivedDelegates;
  4. Define these functions starting with OnPartyMembersChange() by adding the code below. This function will be called when the party member changes (e.g., when the party member has joined or left). This function will push a notification to show which party member has joined or left the party. It also calls the function you created in the previous section (Implement promoting a party member to leader) to show notifications regarding the new party leader. It will also broadcast the on-complete delegate you defined in the Header file.

    void UPartyOnlineSession_Starter::OnPartyMembersChange(FName SessionName, const FUniqueNetId& Member, bool bJoined)
    {
    // Abort if not a party session.
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    const FUniqueNetIdAccelByteUserRef MemberABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(Member.AsShared());
    const FString MemberABUIdStr = MemberABId->GetAccelByteId();

    UE_LOG_PARTYESSENTIALS(Log, TEXT("Party participant %s %s to/from the party"),
    MemberABId->IsValid() ? *MemberABId->GetAccelByteId() : TEXT("Unknown"),
    bJoined ? TEXT("joined") : TEXT("left"));

    /* Since this event could be called multiple times, we cache the party member status.
    * This cache is used to execute the following functionalities only when the party member status is changed (not the same status).*/
    if (PartyMemberStatus.Contains(MemberABUIdStr))
    {
    if (PartyMemberStatus[MemberABUIdStr] == bJoined)
    {
    // Abort if the status is the same.
    return;
    }
    PartyMemberStatus[MemberABUIdStr] = bJoined;
    }
    if (!PartyMemberStatus.Contains(MemberABUIdStr))
    {
    PartyMemberStatus.Add(MemberABUIdStr, bJoined);
    }

    // Query member information then displays a push notification to show who joined/left the party.
    QueryUserInfo(0, TPartyMemberArray{ MemberABId },
    FOnQueryUsersInfoComplete::CreateWeakLambda(this, [this, MemberABId, bJoined]
    (const bool bSucceeded, const TArray<FUserOnlineAccountAccelByte*>& UsersInfo)
    {
    if (UsersInfo.IsEmpty() || !UsersInfo[0] || !GetPromptSubystem())
    {
    return;
    }

    FUserOnlineAccountAccelByte MemberInfo = *UsersInfo[0];

    const FString MemberDisplayName = MemberInfo.GetDisplayName().IsEmpty() ?
    UTutorialModuleOnlineUtility::GetUserDefaultDisplayName(MemberABId.Get()) :
    MemberInfo.GetDisplayName();

    const FText NotifMessage = bJoined ?
    FText::Format(PARTY_MEMBER_JOINED_MESSAGE, FText::FromString(MemberDisplayName)) :
    FText::Format(PARTY_MEMBER_LEFT_MESSAGE, FText::FromString(MemberDisplayName));

    FString AvatarURL;
    MemberInfo.GetUserAttribute(ACCELBYTE_ACCOUNT_GAME_AVATAR_URL, AvatarURL);

    GetPromptSubystem()->PushNotification(NotifMessage, AvatarURL, true);
    }
    ));

    // Show notification if a new party leader is set.
    DisplayCurrentPartyLeader();

    OnPartyMembersChangeDelegates.Broadcast(SessionName, Member, bJoined);
    }
  5. The function above caches the party member status (either joined or left) in the PartyMemberStatus variable. This variable is a helper to make sure we handle OnPartyMembersChange() only if the party member has actually changed. Since it is a cache, we need to clean it up when necessary. Thus, add the following code to the following functions:

    void UPartyOnlineSession_Starter::OnCreatePartyComplete(FName SessionName, bool bSucceeded)
    {
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    if (bSucceeded)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to create a party"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to create a party"));
    }

    // Cache the party leader.
    LastPartyLeader = GetPartyLeader();

    // Reset the party member status cache.
    PartyMemberStatus.Empty();

    OnCreatePartyCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
    void UPartyOnlineSession_Starter::OnJoinPartyComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
    {
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    if (Result == EOnJoinSessionCompleteResult::Type::Success)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to join a party"));
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to join a party"));
    }

    // Cache the party leader.
    LastPartyLeader = GetPartyLeader();

    // Reset the party member status cache.
    PartyMemberStatus.Empty();

    OnJoinPartyCompleteDelegates.Broadcast(SessionName, Result);
    }
  6. Next, define the OnPartySessionUpdateReceived() function. This function will be called when the party session is updated, such as when a new party leader is set or when it receives any session updates from the backend. This tutorial uses this event to also push a notification to show who the new party leader is. It will then broadcast the on-complete delegate you defined in the Header file.

    void UPartyOnlineSession_Starter::OnPartySessionUpdateReceived(FName SessionName)
    {
    // Abort if not a party session.
    if (SessionName != GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession))
    {
    return;
    }

    UE_LOG_PARTYESSENTIALS(Log, TEXT("Party session is updated"));

    // Show notification if a new party leader is set.
    DisplayCurrentPartyLeader();

    OnPartySessionUpdateReceivedDelegates.Broadcast(SessionName);
    }
  7. Now, you need to bind the functions you just defined so they will be called by the respective party events. You can do this by adding the code below in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.AddUObject(this, &ThisClass::OnSendPartyInviteComplete);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.AddUObject(this, &ThisClass::OnJoinPartyComplete);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.AddUObject(this, &ThisClass::OnPartyInviteRejected);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.AddUObject(this, &ThisClass::OnPartyInviteReceived);

    GetABSessionInt()->OnKickedFromSessionDelegates.AddUObject(this, &ThisClass::OnKickedFromParty);

    GetABSessionInt()->OnSessionParticipantsChangeDelegates.AddUObject(this, &ThisClass::OnPartyMembersChange);
    GetABSessionInt()->OnSessionUpdateReceivedDelegates.AddUObject(this, &ThisClass::OnPartySessionUpdateReceived);
    }
    }
  8. Finally, you need to unbind those functions so they won't be called when the online session is deinitialized. You can do this by adding the code below in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.RemoveAll(this);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.RemoveAll(this);

    GetABSessionInt()->OnKickedFromSessionDelegates.RemoveAll(this);

    GetABSessionInt()->OnSessionParticipantsChangeDelegates.RemoveAll(this);
    GetABSessionInt()->OnSessionUpdateReceivedDelegates.RemoveAll(this);
    }
    }

Implement automatic party creation

In the Byte Wars game, we want it to automatically create a new party every time the game starts or when the player is not yet in a party. In this section, you will learn how to do that.

  1. Open the PartyOnlineSession_Starter class Header file and declare the following functions.

    protected:
    void OnLeaveRestoredPartyToTriggerEvent(const FUniqueNetId& LocalUserId, const FOnlineError& Result, const TDelegate<void(bool bSucceeded)> OnComplete);
    void OnConnectLobbyComplete(int32 LocalUserNum, bool bSucceeded, const FUniqueNetId& UserId, const FString& Error);
  2. Next, open the PartyOnlineSession_Starter class CPP file and define the OnLeaveRestoredPartyToTriggerEvent() function. This function will restore any party sessions and then leave those restored sessions. This is necessary, because when the game closes or crashes, there is a possibility that the last party sessions are not yet terminated on the backend. Since we want to game to create a new party every time the game starts, we need this function to leave the leftover party sessions.

    void UPartyOnlineSession_Starter::OnLeaveRestoredPartyToTriggerEvent(const FUniqueNetId& LocalUserId, const FOnlineError& Result, const TDelegate<void(bool bSucceeded)> OnComplete)
    {
    // Abort if failed to restore sessions.
    if (!Result.bSucceeded)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to leave restored party sessions. Error: %s"), *Result.ErrorMessage.ToString());
    OnComplete.ExecuteIfBound(false);
    return;
    }

    // Safety.
    if (!GetABSessionInt())
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to leave restored party sessions. Session Interface is not valid."));
    OnComplete.ExecuteIfBound(false);
    return;
    }

    const TArray<FOnlineRestoredSessionAccelByte> RestoredParties = GetABSessionInt()->GetAllRestoredPartySessions();

    // If empty, no need to leave the restored party sessions.
    if (RestoredParties.IsEmpty())
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("No need to leave party session, restored party sessions are empty."));
    OnComplete.ExecuteIfBound(true);
    return;
    }

    // Leave the restored party session then invoke the on-complete event.
    // Since player can only be in a single party, then leave the first restored party session.
    GetABSessionInt()->LeaveRestoredSession(
    LocalUserId,
    RestoredParties[0],
    FOnLeaveSessionComplete::CreateWeakLambda(this, [this, OnComplete](bool bWasSuccessful, FString SessionId)
    {
    if (bWasSuccessful)
    {
    UE_LOG_PARTYESSENTIALS(Log, TEXT("Success to leave restored party session %s"), *SessionId);
    }
    else
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Failed to leave restored party session %s"), *SessionId);
    }

    OnComplete.ExecuteIfBound(bWasSuccessful);
    }
    ));
    }
  3. Now, define the OnConnectLobbyComplete() function. The game will call this function upon the player connecting to the lobby, which is the event when the player has successfully logged in and is ready to use AccelByte Gaming Services (AGS) online sessions. This function will leave leftover party sessions and then create a new party. It also binds an event to create a party when the player gets kicked from a party. This way, the game will always try to create a new party when the player is not in any party.

    void UPartyOnlineSession_Starter::OnConnectLobbyComplete(int32 LocalUserNum, bool bSucceeded, const FUniqueNetId& UserId, const FString& Error)
    {
    if (!bSucceeded)
    {
    UE_LOG_PARTYESSENTIALS(Warning, TEXT("Cannot initialize party. Failed to connect to lobby. Error: %s."), *Error);
    return;
    }

    // Bind event to create a new party when got kicked.
    GetOnKickedFromPartyDelegates()->AddWeakLambda(this, [this, LocalUserNum](FName SessionName)
    {
    CreateParty(LocalUserNum);
    });

    // Restore and leave party, then create a new party.
    GetABSessionInt()->RestoreActiveSessions(
    UserId,
    FOnRestoreActiveSessionsComplete::CreateUObject(
    this,
    &ThisClass::OnLeaveRestoredPartyToTriggerEvent,
    TDelegate<void(bool)>::CreateWeakLambda(this, [this, LocalUserNum](bool bWasSuccessful)
    {
    if (bWasSuccessful)
    {
    CreateParty(LocalUserNum);
    }
    })
    ));
    }
  4. Now, you need to bind the function you just defined so they will be called by the event. You can do this by adding the code below in the predefined RegisterOnlineDelegates() function, which is the first function to be called when the online session is initialized.

    void UPartyOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    InitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyComplete);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyComplete);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.AddUObject(this, &ThisClass::OnSendPartyInviteComplete);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.AddUObject(this, &ThisClass::OnJoinPartyComplete);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.AddUObject(this, &ThisClass::OnPartyInviteRejected);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.AddUObject(this, &ThisClass::OnPartyInviteReceived);

    GetABSessionInt()->OnKickedFromSessionDelegates.AddUObject(this, &ThisClass::OnKickedFromParty);

    GetABSessionInt()->OnSessionParticipantsChangeDelegates.AddUObject(this, &ThisClass::OnPartyMembersChange);
    GetABSessionInt()->OnSessionUpdateReceivedDelegates.AddUObject(this, &ThisClass::OnPartySessionUpdateReceived);
    }

    if (GetABIdentityInt())
    {
    GetABIdentityInt()->OnConnectLobbyCompleteDelegates->AddUObject(this, &ThisClass::OnConnectLobbyComplete);
    }
    }
  5. Finally, you need to unbind that function so it won't be called when the online session is deinitialized. You can do this by adding the code below in the predefined ClearOnlineDelegates() function, which is the first function to be called when the online session is deinitialized.

    void UPartyOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    DeinitializePartyGeneratedWidgets();

    if (GetABSessionInt())
    {
    GetABSessionInt()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnDestroySessionCompleteDelegates.RemoveAll(this);

    GetABSessionInt()->OnSendSessionInviteCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnJoinSessionCompleteDelegates.RemoveAll(this);
    GetABSessionInt()->OnSessionInviteRejectedDelegates.RemoveAll(this);
    GetABSessionInt()->OnV2SessionInviteReceivedDelegates.RemoveAll(this);

    GetABSessionInt()->OnKickedFromSessionDelegates.RemoveAll(this);

    GetABSessionInt()->OnSessionParticipantsChangeDelegates.RemoveAll(this);
    GetABSessionInt()->OnSessionUpdateReceivedDelegates.RemoveAll(this);
    }

    if (GetABIdentityInt())
    {
    GetABIdentityInt()->OnConnectLobbyCompleteDelegates->RemoveAll(this);
    }
    }

Resources