Skip to main content

Implement the Party service using the Unreal Engine SDK

Last updated on January 31, 2024

Introduction

danger

This topic is specific to the AccelByte Gaming Services (AGS) Starter tier.

The Party service is part of the AGS that allows you to create a Party with other players. This service is described in detail in the Party documentation.

Prerequisites

The examples in this document use AGS Starter UE SDK version 15.0.0. Any other version may not be compatible or may need different steps to implement.

This guide assumes that you have already implemented the Lobby and Friends services. Please make sure a player can successfully connect to the Lobby and invite their friends before proceeding with this guide.

note

Lobby has two function types:

  • Response delegate: The delegate accesses the result response from the SDK's functions. Not necessary, but, if used, must be defined before calling the Request function.
  • Request function: This function sends a request for services to the Websocket API. Can be called separately from the Response delegate in a different function.

Since party implementation can vary for each game, you can familiarize yourself with other concepts and classes in the AccelByteLobbyModels header file inside the plugin SDK. For some implementations, you don't even need to implement the Friends service first, since party invitations only require user IDs.

Quick reference

References
#include "Api/AccelByteLobbyApi.h"
#include "Core/AccelByteRegistry.h"
#include "Models/AccelByteLobbyModels.h"
Party notification events
FRegistry::Lobby.SetPartyGetInvitedNotifDelegate(Api::Lobby::FPartyGetInvitedNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyGetInvitedNotice& Result)
{
// On get invited to a party
}));
FRegistry::Lobby.SetPartyKickNotifDelegate(Api::Lobby::FPartyKickNotif::CreateWeakLambda(this, [](const FAccelByteModelsGotKickedFromPartyNotice& Result)
{
// On player kicked from party
}));
FRegistry::Lobby.SetPartyMemberLeaveNotifDelegate(Api::Lobby::FPartyMemberLeaveNotif::CreateWeakLambda(this, [](const FAccelByteModelsLeavePartyNotice& Result)
{
// On a member left the party
}));

FRegistry::Lobby.SetPartyJoinNotifDelegate(Api::Lobby::FPartyJoinNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyJoinNotice& Result)
{
// On party join
}));

FRegistry::Lobby.SetPartyInvitationRejectedNotifDelegate(Api::Lobby::FPartyRejectNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyRejectNotice& Result)
{
// On party invitation rejected
}));

FRegistry::Lobby.SetPartyDataUpdateNotifDelegate(Api::Lobby::FPartyDataUpdateNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyDataNotif& Result)
{
// On party data update
}));

FRegistry::Lobby.SetPartyMemberConnectNotifDelegate(Api::Lobby::FPartyMemberConnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyMemberConnectionNotice& Result)
{
// On party member reconnecting to the lobby
}));

FRegistry::Lobby.SetPartyMemberDisconnectNotifDelegate(Api::Lobby::FPartyMemberDisconnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyMemberConnectionNotice& Result)
{
// On party member disconnected from the lobby
}));

Create Party
FRegistry::Lobby.SetCreatePartyResponseDelegate(
Api::Lobby::FPartyCreateResponse::CreateWeakLambda(this, [](const FAccelByteModelsCreatePartyResponse Result)
{
UE_LOG(LogTemp, Log, TEXT("Create Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Create Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendCreatePartyRequest();
Invite to Party
FRegistry::Lobby.SetInvitePartyResponseDelegate(
Api::Lobby::FPartyInviteResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyInviteResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Invite Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Log, TEXT("Invite Party Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendInviteToPartyRequest("DesiredUserId");
Leave Party
FRegistry::Lobby.SetLeavePartyResponseDelegate(
Api::Lobby::FPartyLeaveResponse::CreateWeakLambda(this, [](const FAccelByteModelsLeavePartyResponse& Result)
{
UE_LOG(LogTemp, Error, TEXT("Leave Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Log, TEXT("Leave Party Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendLeavePartyRequest();
Promote to Party leader
FRegistry::Lobby.SetPartyPromoteLeaderResponseDelegate(
Api::Lobby::FPartyPromoteLeaderResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyPromoteLeaderResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Promote Party Leader Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Promote Party Leader Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendPartyPromoteLeaderRequest("MemberUserId");
Kick from Party
FRegistry::Lobby.SetInvitePartyKickMemberResponseDelegate(
Api::Lobby::FPartyKickResponse::CreateWeakLambda(this, [](const FAccelByteModelsKickPartyMemberResponse& result)
{
UE_LOG(LogTemp, Log, TEXT("Kick Party Member Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Kick Party Member Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendKickPartyMemberRequest("MemberUserId");
Join Party
FRegistry::Lobby.SetInvitePartyJoinResponseDelegate(
Api::Lobby::FPartyJoinResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyJoinResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Accept Party Success! Member : %d"), Result.Members.Num());
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Accept Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendAcceptInvitationRequest("DesiredPartyId", "PartyInvitationToken");
Reject Party invitation
FRegistry::Lobby.SetInvitePartyRejectResponseDelegate(
Api::Lobby::FPartyRejectResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyRejectResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Reject Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Reject Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendRejectInvitationRequest("DesiredPartyId", "PartyInvitationToken");
Get current Party info
FRegistry::Lobby.SendInfoPartyRequest(
Api::Lobby::FPartyInfoResponse::CreateWeakLambda(this, [](const FAccelByteModelsInfoPartyResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Get Current party info success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Get current party info Error! %d: %s"), ErrorCode, *ErrorMessage);
}));
Get bulk User data
FRegistry::User.BulkGetUserInfo(
ArrayOfUsersId,
THandler<FListBulkUserInfo>::CreateWeakLambda(this, [](const FListBulkUserInfo& Result)
{
// On Get bulk user info success
},
FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
{
// On Get bulk user info failed
})));

Quickstart

Create a party implementation

Follow the steps below to get started:

  1. Set the Maximum Party Member value and Auto kick on disconnect features from Party Configurations in the Admin Portal.

  2. Create a User Widget C++ class called AccelByteParty to accommodate the functions for the Party Menu widget.

  3. Add the following header to ensure the functions work correctly for the Lobby services:

    .cpp

    ...
    #include "Api/AccelByteLobbyApi.h"
    #include "Core/AccelByteRegistry.h"
    ...
  4. Add some basic Party functions in the AccelByteParty class so you can call these later from your widgets:

Create a Party

This function returns a result from the FAccelByteModelsCreatePartyResponse class type which contains Party data such as:

  • PartyId
  • LeaderId, the leader's userId
  • Members, array of the party members' userIds
  • Invitees, array of the invitees' userIds
  • InvitationToken, the party invitation's token
void UAccelByteParty::OnClickedCreateParty()
{
FRegistry::Lobby.SetCreatePartyResponseDelegate(
Api::Lobby::FPartyCreateResponse::CreateWeakLambda(this, [](const FAccelByteModelsCreatePartyResponse Result)
{
UE_LOG(LogTemp, Log, TEXT("Create Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Create Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendCreatePartyRequest();
}

Invite friend to a Party

To send an invitation, you need the UserID of the player you want to invite, so add inviteeUserId as the function's parameter. Other party members can also invite people to the party.

void UAccelByteParty::OnClickedInviteParty()
{
FRegistry::Lobby.SetInvitePartyResponseDelegate(
Api::Lobby::FPartyInviteResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyInviteResponse Result)
{
UE_LOG(LogTemp, Log, TEXT("Invite Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Log, TEXT("Invite Party Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendInviteToPartyRequest("DesiredUserId");
}

Leave a Party

Use this function to leave a Party.

void UAccelByteParty::OnClickedLeaveParty()
{
FRegistry::Lobby.SetLeavePartyResponseDelegate(
Api::Lobby::FPartyLeaveResponse::CreateWeakLambda(this, [](FAccelByteModelsLeavePartyResponse Result)
{
UE_LOG(LogTemp, Error, TEXT("Leave Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Log, TEXT("Leave Party Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendLeavePartyRequest();
}

Promote a friend to Party leader

You need the UserID of the Party member you want to promote to Party leader.

void UAccelByteParty::OnClickedPartyLeader()
{
FRegistry::Lobby.SetPartyPromoteLeaderResponseDelegate(
Api::Lobby::FPartyPromoteLeaderResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyPromoteLeaderResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Promote Party Leader Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Promote Party Leader Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendPartyPromoteLeaderRequest("MemberUserId");
}

Kick a friend from a Party

You need the UserID of the Party member you want to kick from the Party.

void UAccelByteParty::OnClickedKickParty()
{
FRegistry::Lobby.SetInvitePartyKickMemberResponseDelegate(
Api::Lobby::FPartyKickResponse::CreateWeakLambda(this, [](FAccelByteModelsKickPartyMemberResponse result)
{
UE_LOG(LogTemp, Log, TEXT("Kick Party Member Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Kick Party Member Failed! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendKickPartyMemberRequest("MemberUserId");
}

  1. Most of the Party Invitation UIs will be spawnable widgets. Create a new user widget class called AccelBytePartyInvitationPopUp and add the following header on the top:

    .cpp

    ...
    #include "Api/AccelByteLobbyApi.h"
    #include "Core/AccelByteRegistry.h"
    ...

    This class will hold functions that handle events related to the Party Invitation. You can parent this class to your Party Invitation widget later on.

  2. Add the following functions to handle party invitations in the AccelBytePartyInvitationPopUp class:

Handle Party invitations

Accept Party invitation (join Party)

This function requires partyId and invitationToken which are contained in a PartyInvitation object.

  void UAccelBytePartyInvitationPopUp::OnClickedAcceptPartyInvitation()
{
FRegistry::Lobby.SetInvitePartyJoinResponseDelegate(
Api::Lobby::FPartyJoinResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyJoinResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Accept Party Success! Member : %d"), Result.Members.Num());
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Accept Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendAcceptInvitationRequest("DesiredPartyId", "PartyInvitationToken");
}

Reject Party invitation

This function requires partyId and invitationToken which are contained in a PartyInvitation object.

void UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation()
{
FRegistry::Lobby.SetInvitePartyRejectResponseDelegate(
Api::Lobby::FPartyRejectResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyRejectResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Reject Party Success!"));
}),
FErrorHandler::CreateWeakLambda(this, [](const int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Reject Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}));

FRegistry::Lobby.SendRejectInvitationRequest("DesiredPartyId", "PartyInvitationToken");
}

  1. Use the result value from the Lobby.SetPartyGetInvitedNotifDelegate() event for the FAccelByteModelsPartyGetInvitedNotice value.

  2. To send a notification to a player on any activity related to the current party, go back to the AccelByteParty class and create a new function to get all Party Notification Events with Lobby service.

  3. Add a log for each event:

void UAccelByteParty::SetNotificationDelegate()
{
FRegistry::Lobby.SetPartyGetInvitedNotifDelegate(Api::Lobby::FPartyGetInvitedNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyGetInvitedNotice& Result)
{
UE_LOG(LogTemp, Log, TEXT("On Get Invite to Party Notice!"));
}));
FRegistry::Lobby.SetPartyKickNotifDelegate(Api::Lobby::FPartyKickNotif::CreateWeakLambda(this, [](const FAccelByteModelsGotKickedFromPartyNotice& Result)
{
UE_LOG(LogTemp, Log, TEXT("You have been kicked from the party!"));
}));
FRegistry::Lobby.SetPartyMemberLeaveNotifDelegate(Api::Lobby::FPartyMemberLeaveNotif::CreateWeakLambda(this, [](const FAccelByteModelsLeavePartyNotice& Result)
{
UE_LOG(LogTemp, Log, TEXT("On a member left the party!"));
}));

FRegistry::Lobby.SetPartyMemberConnectNotifDelegate(Api::Lobby::FPartyMemberConnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyMemberConnectionNotice& Result)
{
UE_LOG(LogTemp, Log, TEXT("On a member reconnect to the party!"));
}));

FRegistry::Lobby.SetPartyMemberDisconnectNotifDelegate(Api::Lobby::FPartyMemberDisconnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyMemberConnectionNotice& Result)
{
UE_LOG(LogTemp, Log, TEXT("On a member disconnected from the party!"));
}));

}

  1. You may wish to retrieve Party data. There are a few ways you can do this:

    • SetPartyDataUpdateNotifDelegate()

      Add this lobby's notification event to retrieve Party data every time the data is updated. This will return a result with the class type FAccelByteModelsPartyDataNotif and contain data such as:

    • PartyId: the current player's Party ID.

    • Namespace: the current project's namespace.

    • Leader: the party leader's user ID.

    • Members: a list of the party members' user IDs.

    • Invitees: a list of the invited players' user IDs.

    • Custom_attribute, FJsonObjectWrapper: holds custom information.

    • UpdatedAt: timestamp of the party data update.

      .cpp

      void UAccelByteParty::SetNotificationDelegate()
      {
      ...
      FRegistry::Lobby.SetPartyDataUpdateNotifDelegate(Api::Lobby::FPartyDataUpdateNotif::CreateWeakLambda(this, [](const FAccelByteModelsPartyDataNotif& Result)
      {
      UE_LOG(LogTemp, Log, TEXT("On any party data update"));
      }));
      }

    • SendInfoPartyRequest()

    You can also retrieve Party data using the SendInfoPartyRequest() function. This will return a result with the class type FAccelByteModelsInfoPartyResponse and contain data such as:

    • PartyID: the Party's unique ID.
    • LeaderID: the Party leader's user ID.
    • Members (userIDs): a list of the Party members' user IDs.
    • Invitees (userIDs): a list of the invited players' user IDs.
    • InvitationToken: the token of the latest Party invitation sent.

    .cpp

    FRegistry::Lobby.SendInfoPartyRequest(Api::Lobby::FPartyInfoResponse::CreateWeakLambda(this, [](const FAccelByteModelsInfoPartyResponse& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Get information about current party success"));
    }),
    FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Log, TEXT("Get information about current party failed"));
    }));
  2. You may wish to unbind Party services. You can do this with the delegates UnbindPartyNotifEvents and UnbindPartyResponseEvents.

    .cpp

    ...
    FRegistry::Lobby.UnbindPartyNotifEvents();

    FRegistry::Lobby.UnbindPartyResponseEvents();
    ...
  3. After you call this code, you need to resubscribe all the notification and response delegates in both party services.

Congratulations! You have successfully learned how to use the Party service.

Step-by-Step guide

Implement the UI

  1. Create a widget blueprint class called WB_Party and re-parent the WB_Party widget blueprint to the AccelByteParty C++ class.

  2. Add the following objects to the Party widget blueprint:

    • Text box for Party Id.
    • Button to create a party.
    • Any box for the party members list.
    • Button to leave the party.
  3. To display a Party member's information, create a widget blueprint class called WB_PartyPlayerEntry.

  4. Add the following objects to the WB_PartyPlayerEntry widget blueprint:

    • Text box to display the player's username.
    • Button to promote a party leader.
    • Button to kick a player from the party.
    • Image for the party leader icon.

    To send a Party invitation, you will need a spawnable widget.

  5. Create a widget blueprint class called WB_InvitationParty.

  6. Re-parent the WB_InvitationParty widget blueprint to the AccelBytePartyInvitationPopUp C++ class.

  7. Add the following objects to the Party Invitation PopUp widget blueprint:

    • Text box for invitation text.
    • Button to accept the party invitation.
    • Button to reject the party invitation.
  8. To prepare the Notification Box widget, start by creating a new widget blueprint class called WB_LobbyNotificationEntry.

  9. Add a text box for the notification text to the WB_LobbyNotificationEntry widget blueprint.

  10. Create another widget blueprint class called WB_Notification.

  11. Add the following objects to the WB_Notification widget blueprint:

    • Text box for the subheader text.
    • List view to add the Lobby Notification entry dynamically.
    note

    Don't forget to set the Entry Widget Class under the List Entries component with WB_LobbyNotificationEntry.

  12. Since the Party service is part of the Lobby Menu in this example, add the following object components in WB_LobbyMenu:

    • Scroll box to add the WB_PartyMenu.
    • Border for the Notification Box widget blueprint to display the notification update to players.

Implement the code

Tutorial menu HUD

Before you move on, prepare all of the Party-related widgets so they are accessible to other classes via the TutorialMenuHUD class.

  1. Do this by adding all of the Party-related classes to the top of the TutorialMenuHUD class.

    ...
    #include "AccelByte/Party/AccelByteParty.h"
    ...
  2. Initialize the pointer to refer to the Party widget classes in the BeginPlay() function.

    void ATutorialMenuHUD::BeginPlay()
    {
    ...
    check(PartyClass != nullptr);

    ...
    PartyMenu = CreateWidget<UAccelByteParty>(PlayerController, PartyClass.Get());
    ...
    }
  3. Add a getter function for the Party Menu widgets' pointer.

    /**
    * @brief Getter for Party Menu widget
    */
    UAccelByteParty* GetPartyMenu() const {return PartyMenu; }

Party's player entry

  1. Create a user widget C++ class named AccelBytePartyPlayerEntry and set the class as the parent class of your Party Player Entry widget blueprint (in this case, WB_PartyPlayerEntry).

  2. Open the AccelBytePartyPlayerEntry class and add the following to the the top of the class:

    ...
    #include "AccelByteParty.h"
    #include "Api/AccelByteLobbyApi.h"
    #include "Api/AccelByteUserApi.h"
    #include "Core/AccelByteRegistry.h"
    #include "Components/Image.h"
    #include "Components/TextBlock.h"
    #include "Components/Button.h"
    ...
    1. Specify all the widget components in the AccelBytePartyPlayerEntry header file.
    /**
    * @brief Image to display leader status.
    */
    UPROPERTY(meta = (BindWidget))
    UImage* Img_LeaderIcon;

    /**
    * @brief Text Box for Player Username.
    */
    UPROPERTY(meta = (BindWidget))
    UTextBlock* Tb_PlayerUsername;

    /**
    * @brief Button for Promoting Party Leader.
    */
    UPROPERTY(meta = (BindWidget))
    UButton* Btn_PartyLeader;

    /**
    * @brief Button for Kick Party Member.
    */
    UPROPERTY(meta = (BindWidget))
    UButton* Btn_KickPlayer;

  3. Create a variable called UserData to store the user data required by the Party functionalities. To get UserData from other scripts, create a function that will return the variable.

    FSimpleUserData UAccelBytePartyPlayerEntry::GetUserData() const
    {
    return UserData;
    }

  4. Move the OnClickedPartyLeader() function and the OnClickedKickParty() function from AccelByteParty to the AccelBytePartyPlayerEntry class.

  5. Once you have done that, modify both functions using CreateUObject() instead of CreateWeakLambda() for better readability.

    void UAccelBytePartyPlayerEntry::OnClickedPartyLeader()
    {
    FRegistry::Lobby.SetPartyPromoteLeaderResponseDelegate(
    Api::Lobby::FPartyPromoteLeaderResponse::CreateWeakLambda(this, [](const FAccelByteModelsPartyPromoteLeaderResponse& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Promote Party Leader Success!"));
    }),
    FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Promote Party Leader Failed! %d: %s"), ErrorCode, *ErrorMessage);
    GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, "Unable to Promote Party Leader!");
    }));
    FRegistry::Lobby.SendPartyPromoteLeaderRequest(UserData.UserId);
    }

    void UAccelBytePartyPlayerEntry::OnClickedKickParty()
    {
    FRegistry::Lobby.SetInvitePartyKickMemberResponseDelegate(
    Api::Lobby::FPartyKickResponse::CreateWeakLambda(this, [](FAccelByteModelsKickPartyMemberResponse result)
    {
    UE_LOG(LogTemp, Log, TEXT("Kick Party Member Success!"));
    }),
    FErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Kick Party Member Failed! %d: %s"), ErrorCode, *ErrorMessage);
    }));
    FRegistry::Lobby.SendKickPartyMemberRequest(UserData.UserId);
    }

    1. Add the NativeConstruct() override function and set up the button widgets to be called by their functions on the button clicked.
    void UAccelBytePartyPlayerEntry::NativeConstruct()
    {
    Super::NativeConstruct();

    Btn_PartyLeader->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyPlayerEntry::OnClickedPartyLeader);
    Btn_KickPlayer->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyPlayerEntry::OnClickedKickParty);
    }
  6. Create a function to display the player's display name when the party's Player Entry initializes. Use the User SDK's GetUserByUserId() function to get the display name data.

    void UAccelBytePartyPlayerEntry::InitData(const FString& PlayerId)
    {
    UserData.UserId = PlayerId;

    FRegistry::User.GetUserByUserId(
    PlayerId,
    THandler<FSimpleUserData>::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnSuccessGetUserId),
    FErrorHandler::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnFailedGetUserId));
    }
  7. Define what to do after the GetUserByUserId() query result is received in both of the response functions you declared earlier.

    void UAccelBytePartyPlayerEntry::OnSuccessGetUserId(const FSimpleUserData& Data)
    {
    UserData = Data;
    Tb_PlayerUsername->SetText(FText::FromString(UserData.DisplayName));
    }

    void UAccelBytePartyPlayerEntry::OnFailedGetUserId(int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Get User Id Failed : %d , %s"), ErrorCode, *ErrorMessage);
    }
  8. Create a function to enable visibility of the Promote Leader and Kick Party Member buttons if the player is the party leader.

    void UAccelBytePartyPlayerEntry::EnableAllComponents(bool bIsEnable) const
    {
    if (bEnable)
    {
    Btn_KickPlayer->SetVisibility(ESlateVisibility::Visible);
    Btn_PartyLeader->SetVisibility(ESlateVisibility::Visible);
    }
    else
    {
    Btn_KickPlayer->SetVisibility(ESlateVisibility::Hidden);
    Btn_PartyLeader->SetVisibility(ESlateVisibility::Hidden);
    }
    }

  9. Create a function that will show the Party leader's icon image.

    void UAccelBytePartyPlayerEntry::SetImageIconLeader(bool bIsLeader) const
    {
    if (bIsLeader)
    {
    Img_LeaderIcon->SetVisibility(ESlateVisibility::Visible);
    }
    else
    {
    Img_LeaderIcon->SetVisibility(ESlateVisibility::Hidden);
    }
    }

Party menu

  1. Open the AccelByteParty header and class, then add the following includes at the top of the header and class:

    #include "AccelBytePartyPlayerEntry.h"
    #include "Components/ScrollBox.h"
    #include "Components/Button.h"
    #include "Components/Image.h"
    #include "Components/TextBlock.h"
    #include "TutorialProject/TutorialMenuHUD.h"

    1. Specify all the widgets components in the AccelByteParty header file.
    /**
    * @brief Scroll Box to spawn the player entry.
    */
    UPROPERTY(meta = (BindWidget))
    UScrollBox* Sb_Player;

    /**
    * @brief Button for Create Party.
    */
    UPROPERTY(meta = (BindWidget))
    UButton* Btn_CreateParty;

    /**
    * @brief Button for Leave Party.
    */
    UPROPERTY(meta = (BindWidget))
    UButton* Btn_LeaveParty;

    /**
    * @brief Text Block for displaying Party ID.
    */
    UPROPERTY(meta = (BindWidget))
    UTextBlock* Tb_PartyID;

  1. Declare a NativeConstruct() override function to initialize the widget components and the TotalPartyMember based on your Max Party Member configuration in Admin Portal.

    void UAccelByteParty::NativeConstruct()
    {
    Super::NativeConstruct();

    TutorialMenuHUD = Cast<ATutorialMenuHUD>(GetOwningPlayer()->GetHUD());

    Btn_CreateParty->OnClicked.AddUniqueDynamic(this, &UAccelByteParty::OnClickedCreateParty);
    Btn_LeaveParty->OnClicked.AddUniqueDynamic(this, &UAccelByteParty::OnClickedLeaveParty);

    TotalPartyMember = 4;

    SetNotificationDelegate();
    }
  2. Create a new function to enable and disable the Create Party button and the Leave Party button depending on the situation.

    void UAccelByteParty::SetCreatePartyButtonEnabled(bool bIsCreateParty) const
    {
    Btn_CreateParty->SetIsEnabled(bIsCreateParty);
    Btn_LeaveParty->SetIsEnabled(!bIsCreateParty);
    }

  3. Also create a function to display the Leader Icon if the player's a Party member. To check on each player in the Party, create an array that will hold our Party's player entry data.

    void UAccelByteParty::SetUpdateLeaderIcon(const FString& LeaderId) const
    {
    for (const TWeakObjectPtr<UAccelBytePartyPlayerEntry>& PlayerEntry : PartyPlayerEntryWidgetArray)
    {
    // Leader Entry
    if (LeaderId == PlayerEntry->GetUserData().UserId)
    {
    PlayerEntry->SetImageIconLeader(true);
    PlayerEntry->EnableAllComponents(false);
    }
    // Other entries when local player is leader
    else if (LeaderId == FRegistry::Credentials.GetUserId())
    {
    PlayerEntry->SetImageIconLeader(false);

    if (PlayerEntry->GetUserData().UserId.IsEmpty())
    {
    PlayerEntry->EnableAllComponents(false);
    }
    else
    {
    PlayerEntry->EnableAllComponents(true);
    }
    }
    // Other entries when local player is not leader
    else
    {
    PlayerEntry->SetImageIconLeader(false);
    PlayerEntry->EnableAllComponents(false);
    }
    }
    }
  4. Prepare a new function to refresh the Party's Player Entry list. Since you are also going to add the Party Player Entry widget, create a class reference to the WB_PartyPlayerEntry's class.

    void UAccelByteParty::RefreshPartyList()
    {
    Sb_Player->ClearChildren();
    PartyPlayerEntryWidgetArray.Empty();

    if (PartyInfo.PartyId.IsEmpty())
    {
    return;
    }

    for (int i = 0; i < TotalPartyMember; ++i)
    {
    TWeakObjectPtr<UAccelBytePartyPlayerEntry> PlayerEntry = MakeWeakObjectPtr<UAccelBytePartyPlayerEntry>(CreateWidget<UAccelBytePartyPlayerEntry>(this, PartyPlayerEntryClass.Get()));

    PlayerEntry->EnableAllComponents(false);

    PartyPlayerEntryWidgetArray.Add(PlayerEntry);

    if (i < PartyInfo.Members.Num())
    {
    PlayerEntry->InitData(PartyInfo.Members[i]);
    }

    Sb_Player->AddChild(PlayerEntry.Get());
    }

    Tb_PartyID->SetText(FText::FromString(PartyInfo.PartyId));

    SetCreatePartyButtonEnabled(false);
    SetUpdateLeaderIcon(PartyInfo.Leader);
    }

  5. Then, create a new function that will refresh all of the Party Widgets state.

    void UAccelByteParty::RefreshPartyEntries()
    {
    Tb_PartyID->SetText(FText::FromString("##############"));
    SetCreatePartyButtonEnabled(true);
    RefreshPartyList();
    }
  6. Also prepare a new function that will reset the current stored Party Info.

    void UAccelByteParty::ResetPartyInfo()
    {
    const FAccelByteModelsPartyDataNotif EmptyData;
    PartyInfo = EmptyData;
    bIsInParty = false;

    RefreshPartyEntries();
    }
  7. Remove SetCreatePartyResponseDelegate, SetLeavePartyResponseDelegate, and SetInvitePartyResponseDelegate from their current function.

  8. You are going to put them in the SetNotificationDelegate() function instead.

  9. Also, modify the SetNotificationDelegate() function by changing the CreateWeakLambda with CreateUObject for readability.

    void UAccelByteParty::OnClickedCreateParty()
    {
    FRegistry::Lobby.SendCreatePartyRequest();
    }

    void UAccelByteParty::OnClickedLeaveParty()
    {
    FRegistry::Lobby.SendLeavePartyRequest();
    }

    void UAccelByteParty::OnClickedInviteParty(const FString& UserId)
    {
    FRegistry::Lobby.SendInviteToPartyRequest(UserId);
    }

    void UAccelByteParty::SetNotificationDelegate()
    {
    FRegistry::Lobby.SetCreatePartyResponseDelegate(Api::Lobby::FPartyCreateResponse::CreateUObject(this, &UAccelByteParty::OnCreatePartyResponse),
    FErrorHandler::CreateUObject(this, &UAccelByteParty::OnCreatePartyFailed));

    FRegistry::Lobby.SetLeavePartyResponseDelegate(Api::Lobby::FPartyLeaveResponse::CreateUObject(this, &UAccelByteParty::OnLeavePartyResponse),
    FErrorHandler::CreateUObject(this, &UAccelByteParty::OnLeavePartyFailed));

    FRegistry::Lobby.SetInvitePartyResponseDelegate(Api::Lobby::FPartyInviteResponse::CreateUObject(this, &UAccelByteParty::OnInvitePartyResponse),
    FErrorHandler::CreateUObject(this, &UAccelByteParty::OnInvitePartyFailed));

    FRegistry::Lobby.SetPartyGetInvitedNotifDelegate(Api::Lobby::FPartyGetInvitedNotif::CreateUObject(this, &UAccelByteParty::OnInvitePartyGetInviteNotice));
    FRegistry::Lobby.SetPartyKickNotifDelegate(Api::Lobby::FPartyKickNotif::CreateUObject(this, &UAccelByteParty::OnInvitePartyKickedNotice));
    FRegistry::Lobby.SetPartyMemberLeaveNotifDelegate(Api::Lobby::FPartyMemberLeaveNotif::CreateUObject(this, &UAccelByteParty::OnLeavePartyNotice));
    FRegistry::Lobby.SetPartyDataUpdateNotifDelegate
    (Api::Lobby::FPartyDataUpdateNotif::CreateUObject(this, &UAccelByteParty::OnPartyDataUpdateResponse));
    }
  10. Now, define the response delegate functions you declared earlier for each Create Party, Leave Party, and Invite Party response query.

    Create Party's Success and Failed Response

    void UAccelByteParty::OnCreatePartyResponse(const FAccelByteModelsCreatePartyResponse& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Create Party Success!"));
    }

    void UAccelByteParty::OnCreatePartyFailed(int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Create Party Failed : %d , %s"), ErrorCode, *ErrorMessage);
    }

    Leave Party's Success and Failed Response

    void UAccelByteParty::OnLeavePartyResponse(const FAccelByteModelsLeavePartyResponse& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Leave Party Success!"));

    ResetPartyInfo();
    }

    void UAccelByteParty::OnLeavePartyFailed(int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Leave Party Failed : %d , %s"), ErrorCode, *ErrorMessage);
    }

    Invite to Party's Success and Failed Response

    void UAccelByteParty::OnInvitePartyResponse(const FAccelByteModelsPartyInviteResponse& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Invite Party Success!"));
    }

    void UAccelByteParty::OnInvitePartyFailed(int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Invite Party Failed : %d , %s"), ErrorCode, *ErrorMessage);
    }

  11. Define the notification events' callback functions to update your widgets based on each event updates:

    • OnInvitePartyGetInviteNotice()
    void UAccelByteParty::OnInvitePartyGetInviteNotice(const FAccelByteModelsPartyGetInvitedNotice& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("On Get Invite to Party Notice!"));
    }

    • OnInvitePartyKickedNotice()
    void UAccelByteParty::OnInvitePartyKickedNotice(const FAccelByteModelsGotKickedFromPartyNotice& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("You have been kicked from the party!"));

    ResetPartyInfo();
    }

    • OnLeavePartyNotice()
    void UAccelByteParty::OnLeavePartyNotice(const FAccelByteModelsLeavePartyNotice& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("On Some Player Leaved Notice!"));
    }

    • OnPartyDataUpdateResponse()
    void UAccelByteParty::OnPartyDataUpdateResponse(const FAccelByteModelsPartyDataNotif& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("On Party Data Update Response!"));

    PartyInfo = Result;

    RefreshPartyList();
    }

Party invitation pop-up

  1. Open the AccelBytePartyInvitationPopUp class and add the widget-related header at the top of the class

    ...
    #include "Components/Button.h"
    #include "TutorialProject/TutorialMenuHUD.h"
    #include "TutorialProject/TutorialProjectUtilities.h"

  1. Specify all the widget components in the AccelBytePartyInvitationPopUp header file.

    /**
    * @brief Text Box displaying Invitation.
    */
    UPROPERTY(meta = (BindWidget))
    UTextBlock* Tb_InvitationText;

    /**
    * @brief Button for Accepting Party Invitation.
    */
    UPROPERTY(meta = (BindWidget))
    UButton* Btn_AcceptParty;

    /**
    * @brief Button for Rejecting Party Invitation.
    */
    UPROPERTY(meta = (BindWidget))
    UButton* Btn_RejectParty;

  1. Add the NativeConstruct() override function to initialize the TutorialMenuHUD object and widgets.

    void UAccelBytePartyInvitationPopUp::NativeConstruct()
    {
    Super::NativeConstruct();

    Btn_AcceptParty->SetIsEnabled(true);
    Btn_RejectParty->SetIsEnabled(true);

    TutorialMenuHUD = Cast<ATutorialMenuHUD>(GetOwningPlayer()->GetHUD());

    Btn_AcceptParty->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyInvitationPopUp::OnClickedAcceptPartyInvitation);
    Btn_RejectParty->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation);
    }
  2. Create a function that initializes the local InvitationData you created in the QuickStart section. If you haven't created a new local variable.

    void UAccelBytePartyInvitationPopUp::InitData(const FAccelByteModelsPartyGetInvitedNotice& Result)
    {
    InvitationData = Result;
    }

  3. Modify the OnClickedAcceptPartyInvitation() function by adding code to disable the button after clicking it.

    void UAccelBytePartyInvitationPopUp::OnClickedAcceptPartyInvitation()
    {
    Btn_AcceptParty->SetIsEnabled(false);
    FRegistry::Lobby.SetInvitePartyJoinResponseDelegate(Api::Lobby::FPartyJoinResponse::CreateWeakLambda(this, [this](const FAccelByteModelsPartyJoinResponse& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Accept Party Success! Member : %d"), Result.Members.Num());

    RemoveFromParent();
    }),
    FErrorHandler::CreateWeakLambda(this, [this](const int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Accept Party Error! %d: %s"), ErrorCode, *ErrorMessage);

    TutorialMenuHUD->GetPartyMenu()->ResetPartyInfo();

    RemoveFromParent();
    }));

    FRegistry::Lobby.SendAcceptInvitationRequest(InvitationData.PartyId, InvitationData.InvitationToken);
    }

  4. Modify the OnClickedRejectPartyInvitation() function by adding widget implementation.

    void UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation()
    {
    Btn_RejectParty->SetIsEnabled(false);
    FRegistry::Lobby.SetInvitePartyRejectResponseDelegate(Api::Lobby::FPartyRejectResponse::CreateWeakLambda(this, [this](const FAccelByteModelsPartyRejectResponse& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Reject Party Success!"));

    RemoveFromParent();
    }),
    FErrorHandler::CreateWeakLambda(this, [this](const int32 ErrorCode, const FString& ErrorMessage)
    {
    UE_LOG(LogTemp, Error, TEXT("Reject Party Error! %d: %s"), ErrorCode, *ErrorMessage);

    RemoveFromParent();
    }));

    FRegistry::Lobby.SendRejectInvitationRequest(InvitationData.PartyId, InvitationData.InvitationToken);
    }
  5. Open the AccelByteParty class, create a reference to the Party Invitation pop-up class and set the AccelBytePartyInvitationPopUp class as the reference.

  6. Also, in the OnInvitePartyGetInviteNotice() function, add the code to create a new Party Invitation pop-up widget.

    void UAccelByteParty::OnInvitePartyGetInviteNotice(const FAccelByteModelsPartyGetInvitedNotice& Result)
    {
    ...

    const TWeakObjectPtr<UAccelBytePartyInvitationPopUp> InvitationPopUpWidget = MakeWeakObjectPtr<UAccelBytePartyInvitationPopUp>(
    CreateWidget<UAccelBytePartyInvitationPopUp>(this, PartyInvitationPopUpClass.Get()));

    InvitationPopUpWidget.Get()->InitData(Result);
    InvitationPopUpWidget->AddToViewport();
    }

Now that you have the Party service implemented:

  1. Open your AccelByteFriendEntry class and add the following code inside the OnClickedInviteParty() function. Make sure the TutorialMenuHUD pointer and UserData exist.

    ...
    #include "../Party/AccelByteParty.h"

    ...
    UAccelByteFriendEntry::OnClickedInviteParty()
    {
    TutorialMenuHUD->GetPartyMenu()->OnClickedInviteParty(UserData.UserId);
    }
  2. Next, open the AccelByteLobby header class and include the AccelByteParty header file.


  3. Still in the AccelByteLobby class, in the SetLobbyNotificationDelegate() function, under the Lobby's SetConnectSuccessDelegate() lambda, add some code to prepare the Party Widgets in Lobby Page.

    void UAccelByteLobby::SetLobbyNotificationDelegate()
    {
    FRegistry::Lobby.SetConnectSuccessDelegate(FSimpleDelegate::CreateWeakLambda(this, [this]()
    {
    ...
    TutorialMenuHUD->GetPartyMenu()->SetCreatePartyButtonEnabled(true);
    TutorialMenuHUD->GetPartyMenu()->RefreshPartyEntries();
    }));
    ...
    }

  4. Specify the Party's box widget in AccelByteLobby class, then create a function to spawn the Party Widget to Lobby Page.

    void UAccelByteLobby::AddPartyChild(UAccelByteParty* PartyMenu)
    {
    Sb_Party->AddChild(PartyMenu);
    }
  5. Open the TutorialMenuHUD class, then call the Lobby's AddPartyChild() function in the InitMainMenu() function.

    void ATutorialMenuHUD::InitMainMenu()
    {
    ...
    LobbyMenu->AddPartyChild(PartyMenu);
    ...
    }
  6. In the TutorialMenuHUD class, refresh the Party list when opening the Lobby Menu.

    void ATutorialMenuHUD::OpenLobbyMenu()
    {
    ...
    PartyMenu->RefreshPartyList();
    }

  7. Also, in the TutorialMenuHUD class, create a new function to refresh the Party Entries on closing the Lobby Menu.

    void ATutorialMenuHUD::OnCloseLobbyMenu()
    {
    PartyMenu->RefreshPartyEntries();
    CloseLobbyMenu();
    }

  8. Back to the AccelByteLobby.cpp, call the OnCloseLobbyMenu() you created earlier inside the Lobby's notification delegate on the connection closed and disconnected.

    void UAccelByteLobby::SetLobbyNotificationDelegate()
    {
    ...
    FRegistry::Lobby.SetConnectionClosedDelegate(Api::Lobby::FConnectionClosed::CreateWeakLambda(this, [this](int32 StatusCode, const FString& Reason, bool bWasClean)
    {
    ...
    TutorialMenuHUD->OnCloseLobbyMenu();
    }));

    FRegistry::Lobby.SetDisconnectNotifDelegate(Api::Lobby::FDisconnectNotif::CreateWeakLambda(this, [this](const FAccelByteModelsDisconnectNotif& Result)
    {
    ...
    TutorialMenuHUD->OnCloseLobbyMenu();
    }));
    ...
    }

Congratulations! You have now fully implemented the Party service.

Full code for reference

AccelBytePartyInvitationPopup.h
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Models/AccelByteLobbyModels.h"
#include "AccelBytePartyInvitationPopUp.generated.h"

class UTextBlock;
class UButton;

/**
* Pop-up window when the player gets a party invitation from a friend.
* This code covers AccelByte services including:
*
* - Get User Data by User ID
* - Accept Party Invitation
* - Reject Party Invitation
*/

UCLASS()
class TUTORIALPROJECT_API UAccelBytePartyInvitationPopUp : public UUserWidget
{
GENERATED_BODY()

virtual void NativeConstruct() override;

/**
* @brief Text Box displaying Invitation.
*/
UPROPERTY(meta = (BindWidget))
UTextBlock* Tb_InvitationText;
/**
* @brief Button for Accepting Party Invitation.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_AcceptParty;
/**
* @brief Button for Rejecting Party Invitation.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_RejectParty;

public:
/**
* @brief Initialize ResultInvited data to get player response to party host.
*/
UFUNCTION()
void InitData(const FAccelByteModelsPartyGetInvitedNotice& Result);
/**
* @brief Callback for accepting party invitation.
*/
UFUNCTION()
void OnClickedAcceptPartyInvitation();
/**
* @brief Callback for rejecting party invitation.
*/
UFUNCTION()
void OnClickedRejectPartyInvitation();

/**
* @brief Response when Accepting party invitation.
*/
void OnInvitePartyJoinResponse(const FAccelByteModelsPartyJoinResponse& Result);
/**
* @brief Response when Declining party invitation.
*/
void OnInvitePartyRejectResponse(const FAccelByteModelsPartyRejectResponse& Result);

/**
* @brief Get Invitation notice data to pass player accepting / rejecting
* party invitation request.
*/
FAccelByteModelsPartyGetInvitedNotice InvitationData;
};
AccelBytePartyInvitationPopup.cpp
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#include "AccelBytePartyInvitationPopUp.h"
// AccelByte Services
#include "Api/AccelByteLobbyApi.h"
#include "Core/AccelByteRegistry.h"
// Widget Components
#include "Components/Button.h"

void UAccelBytePartyInvitationPopUp::NativeConstruct()
{
Super::NativeConstruct();

Btn_AcceptParty->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyInvitationPopUp::OnClickedAcceptPartyInvitation);
Btn_RejectParty->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation);
}

void UAccelBytePartyInvitationPopUp::InitData(const FAccelByteModelsPartyGetInvitedNotice& Result)
{
InvitationData = Result;
}

void UAccelBytePartyInvitationPopUp::OnClickedAcceptPartyInvitation()
{
FRegistry::Lobby.SetInvitePartyJoinResponseDelegate(Api::Lobby::FPartyJoinResponse::CreateUObject(this, &UAccelBytePartyInvitationPopUp::OnInvitePartyJoinResponse));
FRegistry::Lobby.SendAcceptInvitationRequest(*InvitationData.PartyId, *InvitationData.InvitationToken);
}

void UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation()
{
FRegistry::Lobby.SetInvitePartyRejectResponseDelegate(Api::Lobby::FPartyRejectResponse::CreateUObject(this, &UAccelBytePartyInvitationPopUp::OnInvitePartyRejectResponse));
FRegistry::Lobby.SendRejectInvitationRequest(*InvitationData.PartyId, *InvitationData.InvitationToken);
}

void UAccelBytePartyInvitationPopUp::OnInvitePartyJoinResponse(const FAccelByteModelsPartyJoinReponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Join Party Success! Member : %d"), Result.Members.Num());
if (Result.Code != "0")
{
UE_LOG(LogTemp, Warning, TEXT("Join Party Failed!"));
}
this->RemoveFromParent();
}

void UAccelBytePartyInvitationPopUp::OnInvitePartyRejectResponse(const FAccelByteModelsPartyRejectResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Reject Party Success!"));
if (Result.Code != "0")
{
UE_LOG(LogTemp, Warning, TEXT("Reject Party Failed!"));
}
this->RemoveFromParent();
}
AccelBytePartyPlayerEntry.h
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Models/AccelByteLobbyModels.h"
#include "Models/AccelByteUserModels.h"
#include "AccelBytePartyPlayerEntry.generated.h"

class UImage;
class UTextBlock;
class UButton;

/**
* Party Player entry. This will hold individual data searched from Party member.
* This code covers AccelByte services including :
*
* - Get User by User ID
* - Kick Player
* - Promote Leader
*/
UCLASS()
class TUTORIALPROJECT_API UAccelBytePartyPlayerEntry : public UUserWidget
{
GENERATED_BODY()

virtual void NativeConstruct() override;

public:
/**
* @brief Initialize User ID and converted to FSimpleUserData in here.
*/
void InitData(const FString& PlayerId);

/**
* @brief Enabling button Promote Leader and Kick Party member.
* @param bEnable Enable all button components.
*/
void EnableAllComponents(bool bIsEnable) const;

/**
* @brief Set Image Icon to Party Member.
* @param bIsLeader Checks if the player is the leader of the party.
*/
void SetImageIconLeader(bool bIsLeader) const;

/**
* @brief Get User Data stored in this entry.
*/
FSimpleUserData GetUserData() const;

private:
/**
* @brief Image to display leader status.
*/
UPROPERTY(meta = (BindWidget))
UImage* Img_LeaderIcon;
/**
* @brief Text Box for Player Username.
*/
UPROPERTY(meta = (BindWidget))
UTextBlock* Tb_PlayerUsername;
/**
* @brief Button for Promoting Party Leader.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_PartyLeader;
/**
* @brief Button for Kick Party Member.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_KickPlayer;

/**
* @brief Callback for OnClicked Party Leader Button.
*/
UFUNCTION()
void OnClickedPartyLeader();
/**
* @brief Callback for OnClicked Kick Party.
*/
UFUNCTION()
void OnClickedKickParty();

/**
* @brief Response when party promoted to leader.
*/
void OnPartyPromoteLeaderResponse(const FAccelByteModelsPartyPromoteLeaderResponse& Result);
/**
* @brief Response when party kicked.
*/
void OnInvitePartyKickMemberResponse(const FAccelByteModelsKickPartyMemberResponse& Result);

/**
* @brief Callback for successfully get the user ID.
*/
void OnSuccessGetUserId(const FSimpleUserData& Data);
/**
* @brief Callback for failed getting user ID.
*/
void OnFailedGetUserId(int32 ErrorCode, const FString& ErrorMessage);

/**
* @brief Models to store User Data contained in this entry.
*/
FSimpleUserData UserData;
};
AccelBytePartyPlayerEntry.cpp
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#include "AccelBytePartyPlayerEntry.h"
#include "AccelByteParty.h"
// AccelByte Services
#include "Api/AccelByteLobbyApi.h"
#include "Api/AccelByteUserApi.h"
#include "Core/AccelByteRegistry.h"
// Widget Components
#include "Components/Image.h"
#include "Components/TextBlock.h"
#include "Components/Button.h"
// Menu HUD
#include "TutorialProject/TutorialMenuHUD.h"

void UAccelBytePartyPlayerEntry::NativeConstruct()
{
Super::NativeConstruct();

Btn_PartyLeader->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyPlayerEntry::OnClickedPartyLeader);
Btn_KickPlayer->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyPlayerEntry::OnClickedKickParty);
}

void UAccelBytePartyPlayerEntry::InitData(const FString& PlayerId)
{
UserData.UserId = PlayerId;

FRegistry::User.GetUserByUserId(
PlayerId,
THandler<FSimpleUserData>::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnSuccessGetUserId),
FErrorHandler::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnFailedGetUserId));
}

void UAccelBytePartyPlayerEntry::EnableAllComponents(bool bIsEnable) const
{
if (bEnable)
{
Btn_KickPlayer->SetVisibility(ESlateVisibility::Visible);
Btn_PartyLeader->SetVisibility(ESlateVisibility::Visible);
}
else
{
Btn_KickPlayer->SetVisibility(ESlateVisibility::Hidden);
Btn_PartyLeader->SetVisibility(ESlateVisibility::Hidden);
}
}

void UAccelBytePartyPlayerEntry::SetImageIconLeader(bool bIsLeader) const
{
if (bIsLeader)
{
Img_LeaderIcon->SetColorAndOpacity(FLinearColor::Yellow);
}
else
{
Img_LeaderIcon->SetColorAndOpacity(FLinearColor::White);
}
}

FSimpleUserData UAccelBytePartyPlayerEntry::GetUserData() const
{
return UserData;
}

void UAccelBytePartyPlayerEntry::OnClickedPartyLeader()
{
FRegistry::Lobby.SetPartyPromoteLeaderResponseDelegate(Api::Lobby::FPartyPromoteLeaderResponse::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnPartyPromoteLeaderResponse));
FRegistry::Lobby.SendPartyPromoteLeaderRequest(UserData.UserId);
}

void UAccelBytePartyPlayerEntry::OnClickedKickParty()
{
FRegistry::Lobby.SetInvitePartyKickMemberResponseDelegate(Api::Lobby::FPartyKickResponse::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnInvitePartyKickMemberResponse));
FRegistry::Lobby.SendKickPartyMemberRequest(UserData.UserId);
}

void UAccelBytePartyPlayerEntry::OnPartyPromoteLeaderResponse(const FAccelByteModelsPartyPromoteLeaderResponse& Result)
{
if (Result.Code == "0")
{
UE_LOG(LogTemp, Log, TEXT("Promote Party Leader Success!"));
SetImageIconLeader(true);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Promote Party Leader Failed!"));
}
}

void UAccelBytePartyPlayerEntry::OnInvitePartyKickMemberResponse(const FAccelByteModelsKickPartyMemberResponse& Result)
{
if (Result.Code == "0")
{
UE_LOG(LogTemp, Log, TEXT("Kick Party Member Success!"));
Cast<ATutorialMenuHUD>(GetOwningPlayer()->GetHUD())->GetPartyMenu()->CreatePartyList();
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Kick Party Member Failed!"));
}
}

void UAccelBytePartyPlayerEntry::OnSuccessGetUserId(const FSimpleUserData& Data)
{
UserData = Data;
Tb_PlayerUsername->SetText(FText::FromString(UserData.DisplayName));
}

void UAccelBytePartyPlayerEntry::OnFailedGetUserId(int32 ErrorCode, const FString& ErrorMessage)
{
const FString DebugMessage = FString::Printf(TEXT("Get User Id Failed : %d , %s"), ErrorCode, *ErrorMessage);
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, DebugMessage);

UE_LOG(LogTemp, Warning, TEXT("Get User Id Failed : Code: %d; Message: %s"), ErrorCode, *ErrorMessage);
}
AccelByteParty.h
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#pragma once

#include "CoreMinimal.h"
#include "AccelBytePartyInvitationPopUp.h"
#include "Blueprint/UserWidget.h"
#include "Models/AccelByteLobbyModels.h"
#include "AccelByteParty.generated.h"

class UAccelBytePartyPlayerEntry;
class UScrollBox;
class UButton;
class UTextBlock;

/**
* Party setup
* This code covers AccelByte services including :
*
* - Create Party
* - Leave Party
* - Invite Party
* - Get Notification when leave party
* - Get Notification when join party
* - Get Notification when someone kicked from party
* - Get Notification when someone promoted to be a party leader
* - Update party data
*/
UCLASS()
class TUTORIALPROJECT_API UAccelByteParty : public UUserWidget
{
GENERATED_BODY()

private:
virtual void NativeConstruct() override;

private:
/**
* @brief Scroll Box to spawn the player entry.
*/
UPROPERTY(meta = (BindWidget))
UScrollBox* Sb_Player;
/**
* @brief Button for Create Party.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_CreateParty;
/**
* @brief Button for Leave Party.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_LeaveParty;
/**
* @brief Text Block for displaying Party ID.
*/
UPROPERTY(meta = (BindWidget))
UTextBlock* Tb_PartyID;

public:
/**
* @brief Callback when creating party.
*/
UFUNCTION()
void OnClickedCreateParty();
/**
* @brief Callback when leaving party.
*/
UFUNCTION()
void OnClickedLeaveParty();
/**
* @brief Callback when inviting to party.
* @param UserId user ID to invited.
*/
UFUNCTION()
void OnClickedInviteParty(const FString& UserId);

private:
/**
* @brief Response for creating party.
*/
void OnCreatePartyResponse(const FAccelByteModelsCreatePartyResponse& Result);
/**
* @brief Response for failing create party.
*/
void OnCreatePartyFailed(int32 ErrorCode, const FString& ErrorMessage);

/**
* @brief Response for leaving party.
*/
void OnLeavePartyResponse(const FAccelByteModelsLeavePartyResponse& Result);
/**
* @brief Response for failing leaving party.
*/
void OnLeavePartyFailed(int32 ErrorCode, const FString& ErrorMessage);

/**
* @brief Response for inviting friend to party.
*/
void OnInvitePartyResponse(const FAccelByteModelsPartyInviteResponse& Result);
/**
* @brief Response for failing inviting friend to party.
*/
void OnInvitePartyFailed(int32 ErrorCode, const FString& ErrorMessage);

/**
* @brief Response for get notification for invitation party.
*/
void OnInvitePartyGetInviteNotice(const FAccelByteModelsPartyGetInvitedNotice& Result);
/**
* @brief Response for notification when someone kicked in the party.
*/
void OnInvitePartyKickedNotice(const FAccelByteModelsGotKickedFromPartyNotice& Result);

/**
* @brief Response for notification when someone leave party.
*/
void OnLeavePartyNotice(const FAccelByteModelsLeavePartyNotice& Result);
/**
* @brief Response for updating party data when someone do some action regarding to party.
*/
void OnPartyDataUpdateResponse(const FAccelByteModelsPartyDataNotif& Result);

public:
/**
* @brief Create an Empty Party List with UMG.
*/
void CreatePartyList();
/**
* @brief Function where all the notifications are being set.
*/
void SetNotificationDelegate();
/**
* @brief Refresh party entries.
*/
void SetRefreshPartyEntries();
/**
* @brief Update the leader icon.
* @param LeaderId The ID of the current party leader.
*/
void SetUpdateLeaderIcon(const FString& LeaderId) const;
/**
* @brief Set enabled / disabled state of the Create Party & Leave Party button.
*/
void SetCreatePartyButtonEnabled(bool bIsCreateParty) const;

private:
/**
* @brief Copy Array of player entry.
*/
TArray<TWeakObjectPtr<UAccelBytePartyPlayerEntry>> PartyPlayerEntryWidgetArray;
/**
* @brief Reference to Party Player Entry Class.
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelBytePartyPlayerEntry> PartyPlayerEntryClass;
/**
* @brief Reference to Party Invitation Pop Up Class.
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelBytePartyInvitationPopUp> PartyInvitationPopUpClass;

/**
* @brief Define maximum party member allowed.
*/
UPROPERTY(EditDefaultsOnly)
int MaximumPartyMember;
};
AccelByteParty.cpp
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#include "AccelByteParty.h"
#include "AccelBytePartyPlayerEntry.h"
// AccelByte Services
#include "Api/AccelByteLobbyApi.h"
#include "Core/AccelByteRegistry.h"
// Widget Components
#include "Components/ScrollBox.h"
#include "Components/Button.h"
#include "Components/Image.h"
#include "Components/TextBlock.h"
// Menu HUD
#include "TutorialProject/TutorialMenuHUD.h"

void UAccelByteParty::NativeConstruct()
{
Super::NativeConstruct();

Btn_CreateParty->OnClicked.AddUniqueDynamic(this, &UAccelByteParty::OnClickedCreateParty);
Btn_LeaveParty->OnClicked.AddUniqueDynamic(this, &UAccelByteParty::OnClickedLeaveParty);

SetNotificationDelegate();
}

void UAccelByteParty::OnClickedCreateParty()
{
FRegistry::Lobby.SendCreatePartyRequest();
}

void UAccelByteParty::OnClickedLeaveParty()
{
FRegistry::Lobby.SendLeavePartyRequest();
}

void UAccelByteParty::OnClickedInviteParty(const FString& UserId)
{
FRegistry::Lobby.SendInviteToPartyRequest(UserId);
}

void UAccelByteParty::SetNotificationDelegate()
{
FRegistry::Lobby.SetCreatePartyResponseDelegate(Api::Lobby::FPartyCreateResponse::CreateUObject(this, &UAccelByteParty::OnCreatePartyResponse),
FErrorHandler::CreateUObject(this, &UAccelByteParty::OnCreatePartyFailed));

FRegistry::Lobby.SetLeavePartyResponseDelegate(Api::Lobby::FPartyLeaveResponse::CreateUObject(this, &UAccelByteParty::OnLeavePartyResponse),
FErrorHandler::CreateUObject(this, &UAccelByteParty::OnLeavePartyFailed));

FRegistry::Lobby.SetInvitePartyResponseDelegate(Api::Lobby::FPartyInviteResponse::CreateUObject(this, &UAccelByteParty::OnInvitePartyResponse),
FErrorHandler::CreateUObject(this, &UAccelByteParty::OnInvitePartyFailed));

FRegistry::Lobby.SetPartyGetInvitedNotifDelegate(Api::Lobby::FPartyGetInvitedNotif::CreateUObject(this, &UAccelByteParty::OnInvitePartyGetInviteNotice));
FRegistry::Lobby.SetPartyKickNotifDelegate(Api::Lobby::FPartyKickNotif::CreateUObject(this, &UAccelByteParty::OnInvitePartyKickedNotice));
FRegistry::Lobby.SetPartyMemberLeaveNotifDelegate(Api::Lobby::FPartyMemberLeaveNotif::CreateUObject(this, &UAccelByteParty::OnLeavePartyNotice));
FRegistry::Lobby.SetPartyDataUpdateResponseDelegate(Api::Lobby::FPartyDataUpdateNotif::CreateUObject(this, &UAccelByteParty::OnPartyDataUpdateResponse));
}

void UAccelByteParty::CreatePartyList()
{
// Clear any entries when going back and forth the menu.
Sb_Player->ClearChildren();
PartyPlayerEntryWidgetArray.Empty();

for (int i = 0; i < MaximumPartyMember; ++i)
{
PartyPlayerEntryWidgetArray.Add(MakeWeakObjectPtr<UAccelBytePartyPlayerEntry>(
CreateWidget<UAccelBytePartyPlayerEntry>(this, PartyPlayerEntryClass.Get())
));

PartyPlayerEntryWidgetArray[i]->EnableAllComponents(false);

Sb_Player->AddChild(PartyPlayerEntryWidgetArray[i].Get());
}
}

void UAccelByteParty::SetRefreshPartyEntries()
{
Tb_PartyID->SetText(FText::FromString("##############"));
SetCreatePartyButtonEnabled(true);
CreatePartyList();
}

void UAccelByteParty::SetUpdateLeaderIcon(const FString& LeaderId) const
{
const FString& LocalPlayerId = Cast<ATutorialMenuHUD>(GetOwningPlayer()->GetHUD())->PlayerInfo.UserId;

for (const TWeakObjectPtr<UAccelBytePartyPlayerEntry>& PlayerEntry : PartyPlayerEntryWidgetArray)
{
// Check if local player is leader.
if (LeaderId == LocalPlayerId)
{
if (LeaderId == PlayerEntry->GetUserData().UserId)
{
PlayerEntry->EnableAllComponents(false);
}
else if (PlayerEntry->GetUserData().UserId.IsEmpty())
{
PlayerEntry->EnableAllComponents(false);
}
else
{
PlayerEntry->EnableAllComponents(true);
}
}
else
{
PlayerEntry->EnableAllComponents(false);
}

// Set leader icon's color.
if (LeaderId == PlayerEntry->GetUserData().UserId)
{
PlayerEntry->SetImageIconLeader(true);
}
else
{
PlayerEntry->SetImageIconLeader(false);
}
}
}

void UAccelByteParty::SetCreatePartyButtonEnabled(bool bIsCreateParty) const
{
Btn_CreateParty->SetIsEnabled(bIsCreateParty);
Btn_LeaveParty->SetIsEnabled(!bIsCreateParty);
}

void UAccelByteParty::OnCreatePartyResponse(const FAccelByteModelsCreatePartyResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Create Party Success!"));
}

void UAccelByteParty::OnCreatePartyFailed(int32 ErrorCode, const FString& ErrorMessage)
{
const FString DebugMessage = FString::Printf(TEXT("Create Party Failed : %d , %s"), ErrorCode, *ErrorMessage);
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, DebugMessage);
}

void UAccelByteParty::OnLeavePartyResponse(const FAccelByteModelsLeavePartyResponse& Result)
{
SetRefreshPartyEntries();

UE_LOG(LogTemp, Log, TEXT("Leave Party Success!"));
}

void UAccelByteParty::OnLeavePartyFailed(int32 ErrorCode, const FString& ErrorMessage)
{
const FString DebugMessage = FString::Printf(TEXT("Leave Party Failed : %d , %s"), ErrorCode, *ErrorMessage);
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, DebugMessage);

UE_LOG(LogTemp, Warning, TEXT("Leave Party Failed : Code: %d, Reason: %s"), ErrorCode, *ErrorMessage);
}

void UAccelByteParty::OnInvitePartyResponse(const FAccelByteModelsPartyInviteResponse& Result)
{
UE_LOG(LogTemp, Log, TEXT("Invite Party Success!"));
}

void UAccelByteParty::OnInvitePartyFailed(int32 ErrorCode, const FString& ErrorMessage)
{
const FString DebugMessage = FString::Printf(TEXT("Invite Party Failed : %d , %s"), ErrorCode, *ErrorMessage);
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, DebugMessage);

UE_LOG(LogTemp, Warning, TEXT("Invite Party Failed : Code: %d, Reason: %s"), ErrorCode, *ErrorMessage);
}

void UAccelByteParty::OnInvitePartyGetInviteNotice(const FAccelByteModelsPartyGetInvitedNotice& Result)
{
UE_LOG(LogTemp, Log, TEXT("Accept Party Invitation!"));

/** Create a weak object pointer that refers to our popup widget **/
const TWeakObjectPtr<UAccelBytePartyInvitationPopUp> InvitationPopUpWidget = MakeWeakObjectPtr<UAccelBytePartyInvitationPopUp>(
CreateWidget<UAccelBytePartyInvitationPopUp>(this, PartyInvitationPopUpClass.Get()));

/** Initialize popup widget and add it to viewport **/
InvitationPopUpWidget.Get()->InitData(Result);
InvitationPopUpWidget->AddToViewport();
}

void UAccelByteParty::OnInvitePartyKickedNotice(const FAccelByteModelsGotKickedFromPartyNotice& Result)
{
SetRefreshPartyEntries();
}

void UAccelByteParty::OnLeavePartyNotice(const FAccelByteModelsLeavePartyNotice& Result)
{
SetRefreshPartyEntries();
}

void UAccelByteParty::OnPartyDataUpdateResponse(const FAccelByteModelsPartyDataNotif& Result)
{
if (!Result.PartyId.IsEmpty())
{
for (int i = 0; i < Result.Members.Num(); ++i)
{
PartyPlayerEntryWidgetArray[i]->InitData(Result.Members[i]);
}

Tb_PartyID->SetText(FText::FromString(Result.PartyId));

SetCreatePartyButtonEnabled(false);
SetUpdateLeaderIcon(Result.Leader);
}
}
TutorialMenuHUD.h
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "TutorialMenuHUD.generated.h"

class UAccelByteAuth;
class UAccelByteLobby;
class UAccelByteFriends;
class UAccelByteFindFriend;
class UAccelByteParty;

/**
* Menu Widget Controller. All Widget functionality controlled here.
*/
UCLASS()
class TUTORIALPROJECT_API ATutorialMenuHUD : public AHUD
{
GENERATED_BODY()

protected:

virtual void BeginPlay() override;

public:

/**
* @brief Shows Login Menu on screen
*/
void OpenLoginMenu();

/**
* @brief Shows Main Menu on screen and destroys Login Menu
*/
void OpenMainMenu();

/**
* @brief Init the construct function when the first time open main menu;
*/
void InitMainMenu();

/**
* @brief Shows Lobby Menu which adds Party Menu in Sb_Party and destroys Main Menu
*/
UFUNCTION()
void OpenLobbyMenu();

/**
* @brief Shows Friends Menu on screen
*/
void OpenFriendsMenu();

/**
* @brief Shows Find Friends Menu on screen
*/
void OpenFindFriendsMenu();

/**
* @brief Destroys Main Menu widget
*/
void CloseMainMenu();

/**
* @brief Destroys Lobby Menu widget and shows Main Menu
*/
UFUNCTION()
void CloseLobbyMenu();

/**
* @brief Destroys Friend Menu widget
*/
void CloseFriendMenu();

/**
* @brief Destroys Find Friend Menu widget
*/
void CloseFindFriendsMenu();

protected:

/**
* @brief Login Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteAuth> LoginMenuClass;

/**
* @brief Lobby Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteLobby> LobbyMenuClass;

/**
* @brief Main Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteMainMenu> MainMenuClass;

/**
* @brief Friends Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteFriends> FriendsMenuClass;

/**
* @brief Find Friends Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteFindFriend> FindFriendsMenuClass;

/**
* @brief Party Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteParty> PartyClass;

public:

/**
* @brief Getter for Login Menu widget
*/
UAccelByteAuth* GetLoginMenu() const {return LoginMenu; }

/**
* @brief Getter for Main Menu widget
*/
UAccelByteMainMenu* GetMainMenu() const {return MainMenu; }

/**
* @brief Getter for Lobby Menu widget
*/
UAccelByteLobby* GetLobbyMenu() const {return LobbyMenu; }

/**
* @brief Getter for Friends Menu widget
*/
UAccelByteFriends* GetFriendsMenu() const {return FriendsMenu; }

/**
* @brief Getter for Find Friends Menu widget
*/
UAccelByteFindFriend* GetFindFriendsMenu() const {return FindFriendsMenu; }

/**
* @brief Getter for Party Menu widget
*/
UAccelByteParty* GetPartyMenu() const {return PartyMenu; }

private:

/**
* @brief Login Menu widget pointer
*/
UPROPERTY()
UAccelByteAuth* LoginMenu;

/**
* @brief Lobby Menu widget pointer
*/
UPROPERTY()
UAccelByteLobby* LobbyMenu;

/**
* @brief Main Menu widget pointer
*/
UPROPERTY()
UAccelByteMainMenu* MainMenu;

/**
* @brief Friends Menu widget pointer
*/
UPROPERTY()
UAccelByteFriends* FriendsMenu;

/**
* @brief Find Friends Menu widget pointer
*/
UPROPERTY()
UAccelByteFindFriend* FindFriendsMenu;

/**
* @brief Party Menu widget pointer
*/
UPROPERTY()
UAccelByteParty* PartyMenu;
};
TutorialMenuHUD.cpp
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#include "TutorialMenuHUD.h"
#include "AccelByte/Authentication/AccelByteAuth.h"
#include "AccelByte/Lobby/AccelByteLobby.h"
#include "AccelByte/AccelByteMainMenu.h"
#include "AccelByte/Friends/AccelbyteFindFriend.h"
#include "AccelByte/Friends/AccelByteFriends.h"
#include "AccelByte/Party/AccelByteParty.h"

void ATutorialMenuHUD::BeginPlay()
{
Super::BeginPlay();

APlayerController* PlayerController = GetOwningPlayerController();

check(LoginMenuClass != nullptr);
check(MainMenuClass != nullptr);
check(LobbyMenuClass != nullptr);
check(FriendsMenuClass != nullptr);
check(FindFriendsMenuClass != nullptr);
check(PartyClass != nullptr);

LoginMenu = CreateWidget<UAccelByteAuth>(PlayerController, LoginMenuClass.Get());
MainMenu = CreateWidget<UAccelByteMainMenu>(PlayerController, MainMenuClass.Get());
LobbyMenu = CreateWidget<UAccelByteLobby>(PlayerController, LobbyMenuClass.Get());
FriendsMenu = CreateWidget<UAccelByteFriends>(PlayerController, FriendsMenuClass.Get());
FindFriendsMenu = CreateWidget<UAccelByteFindFriend>(PlayerController, FindFriendsMenuClass.Get());
PartyMenu = CreateWidget<UAccelByteParty>(PlayerController, PartyClass.Get());
}

void ATutorialMenuHUD::OpenLoginMenu()
{
LoginMenu->AddToViewport();
}

void ATutorialMenuHUD::OpenMainMenu()
{
MainMenu->AddToViewport();
}

void ATutorialMenuHUD::InitMainMenu()
{
// Add Lobby to Viewport and set its visibility as collapsed
LobbyMenu->AddToViewport();
LobbyMenu->AddPartyChild(PartyMenu);
LobbyMenu->ConnectToLobby();
LobbyMenu->SetVisibility(ESlateVisibility::Collapsed);

OpenMainMenu();
}

void ATutorialMenuHUD::OpenLobbyMenu()
{
LobbyMenu->SetVisibility(ESlateVisibility::Visible);
}

void ATutorialMenuHUD::OpenFriendsMenu()
{
if (!FriendsMenu->IsInViewport())
{
FriendsMenu->AddToViewport();
}
else
{
FriendsMenu->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
FriendsMenu->RefreshFriendsList();
}
}

void ATutorialMenuHUD::OpenFindFriendsMenu()
{
FindFriendsMenu->AddToViewport();
}

void ATutorialMenuHUD::CloseMainMenu()
{
LoginMenu->AddToViewport();
MainMenu->RemoveFromViewport();
LobbyMenu->RemoveFromParent();
}

void ATutorialMenuHUD::CloseFindFriendsMenu()
{
FindFriendsMenu->RemoveFromParent();
}

void ATutorialMenuHUD::CloseLobbyMenu()
{
LobbyMenu->SetVisibility(ESlateVisibility::Collapsed);
}

void ATutorialMenuHUD::CloseFriendMenu()
{
if (!LobbyMenu->IsInViewport())
{
LobbyMenu->AddToViewport();
}
FriendsMenu->SetVisibility(ESlateVisibility::Collapsed);
}
AccelByteLobby.h
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#pragma once

#include "CoreMinimal.h"
#include "AccelByteLobby.generated.h"
#include "../Party/AccelByteParty.h"

class ATutorialMenuHUD;
class UButton;
class UScrollBox;

/**
* Component for Join to AccelByte Lobby.
* This code covers AccelByte services including :
*
* - Join Lobby
* - Leave Lobby
*/
UCLASS()
class TUTORIALPROJECT_API UAccelByteLobby : public UUserWidget
{
GENERATED_BODY()

#pragma region Initialization

protected:

virtual void NativeConstruct() override;

#pragma endregion

#pragma region Widget Components

protected:

/**
* @brief Button for Leave Lobby and back to Main Menu.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_LeaveLobby;

/**
* @brief Button for go to Friend Management Menu.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_FriendsManagement;

/**
* @brief Scroll Box to spawn Party Widget.
*/
UPROPERTY(meta = (BindWidget))
UScrollBox* Sb_Party;

#pragma endregion

#pragma region Widget Callbacks

private:

/**
* @brief Button callback on Open Friends Management.
*/
UFUNCTION()
void OnClickedOpenFriendsManagement();

#pragma endregion

#pragma region Utilities

public:

/**
* @brief Connect to AccelByte lobby.
*/
UFUNCTION()
void ConnectToLobby();

/**
* @brief Add new Party Menu to the party scroll box widget child.
* @param PartyMenu Party Menu that gonna add to party scroll box widget child.
*/
UFUNCTION()
void AddPartyChild(UAccelByteParty* PartyMenu) const;

/**
* @brief Set Lobby services notification delegates.
*/
void SetLobbyNotificationDelegate();

private:

/**
* @brief Tutorial Menu HUD pointer reference.
*/
UPROPERTY()
ATutorialMenuHUD* TutorialMenuHUD;

#pragma endregion
};
AccelByteLobby.cpp
// Copyright (c) 2024 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#include "AccelByteLobby.h"
#include "Api/AccelByteLobbyApi.h"
#include "Core/AccelByteRegistry.h"
#include "Components/Button.h"
#include "TutorialProject/TutorialMenuHUD.h"

#pragma region Initialization

void UAccelByteLobby::NativeConstruct()
{
Super::NativeConstruct();

check(GetOwningPlayer() != nullptr);
check(GetOwningPlayer()->GetHUD() != nullptr);

TutorialMenuHUD = Cast<ATutorialMenuHUD>(GetOwningPlayer()->GetHUD());

Btn_LeaveLobby->OnClicked.AddUniqueDynamic(TutorialMenuHUD, &ATutorialMenuHUD::CloseLobbyMenu);
Btn_FriendsManagement->OnClicked.AddUniqueDynamic(this, &UAccelByteLobby::OnClickedOpenFriendsManagement);
}

#pragma endregion
#pragma region Widget Callbacks

void UAccelByteLobby::OnClickedOpenFriendsManagement()
{
TutorialMenuHUD->OpenFriendsMenu();
}

#pragma endregion
#pragma region Utilities

void UAccelByteLobby::ConnectToLobby()
{
SetLobbyNotificationDelegate();
FRegistry::Lobby.Connect();
}

void UAccelByteLobby::AddPartyChild(UAccelByteParty* PartyMenu) const
{
Sb_Party->AddChild(PartyMenu);
}

void UAccelByteLobby::SetLobbyNotificationDelegate()
{
FRegistry::Lobby.SetConnectSuccessDelegate(FSimpleDelegate::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Successfully Connected to Lobby"));

TutorialMenuHUD->GetPartyMenu()->SetCreatePartyButtonEnabled(true);
TutorialMenuHUD->GetPartyMenu()->RefreshPartyEntries();
}));

FRegistry::Lobby.SetConnectFailedDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Error, TEXT("Failed Connect to Lobby : Code: %d; Message: %s"), Code, *Message);
}));

FRegistry::Lobby.SetErrorNotifDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Error, TEXT("Error Connect to Lobby : Code: %d; Message: %s"), Code, *Message);
}));

FRegistry::Lobby.SetConnectionClosedDelegate(Api::Lobby::FConnectionClosed::CreateWeakLambda The(this, [this](int32 StatusCode, const FString& Reason, bool bWasClean)
{
UE_LOG(LogTemp, Error, TEXT("Connection Closed, Code: %d Reason: %s Clean: %s"), StatusCode, *Reason, bWasClean ? TEXT("true") : TEXT("false"));

TutorialMenuHUD->OnCloseLobbyMenu();
}));

FRegistry::Lobby.SetDisconnectNotifDelegate(Api::Lobby::FDisconnectNotif::CreateWeakLambda(this, [this](const FAccelByteModelsDisconnectNotif& Result)
{
UE_LOG(LogTemp, Log, TEXT("Disconnected from Lobby"));

TutorialMenuHUD->OnCloseLobbyMenu();
}));
}

#pragma endregion