Implement Party with Unreal Engine SDK
Introduction
This topic is specific to the AccelByte Gaming Services (AGS) Shared Cloud tier.
AccelByte Gaming Services (AGS) Party allows you to enable your players to create a party with other players. This article will show you how to implement AGS Party into your Unreal Engine game with AGS Shared Cloud tier.
Prerequisites
This guide assumes that you have already implemented AGS Lobby and Friends. Ensure sure your players can successfully connect to Lobby and invite their friends before proceeding.
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 AGS Game SDK plugin. For some implementations, you don't need to implement the Friends service first, since party invitations only require user IDs.
Quick reference
References
#include "OnlineSessionInterfaceV2AccelByte.h"
#include "OnlineSubsystemAccelByteSessionSettings.h"
#include "OnlineSubsystemSessionSettings.h"
Party notification events
// Listen for party invitations
SessionInterface->AddOnV2SessionInviteReceivedDelegate_Handle(FOnV2SessionInviteReceivedDelegate::CreateLambda( [](const FUniqueNetId &UserId, const FUniqueNetId &, const FOnlineSessionInviteAccelByte &InviteResult)
{
if (InviteResult.SessionType == EAccelByteV2SessionType::PartySession)
{
// Handle on party session invite notifications
// InviteResult contains the party details
}
}));
// Listen for kicked from party session notifications
SessionInterface->AddOnKickedFromSessionDelegate_Handle(FOnKickedFromSessionDelegate::CreateLambda([](FName SessionName)
{
if (SessionName == NAME_PartySession)
{
// player kicked from party
}
}));
SessionInterface->AddOnSessionParticipantsChangeDelegate_Handle(FOnSessionParticipantsChangeDelegate::CreateLambda([](FName SessionName, const FUniqueNetId& Member, bool bJoined)
{
if (SessionName == NAME_PartySession)
{
// Member left party if bJoined is false
// Member joined party if bJoined is true
}
}));
SessionInterface->AddOnSessionInviteRejectedDelegate_Handle(
FOnSessionInviteRejectedDelegate::CreateLambda([](FName SessionName, const FUniqueNetId& RejecterId)
{
if (SessionName == NAME_PartySession)
{
// party invitation Rejected
}
}));
SessionInterface->AddOnSessionMemberStorageUpdateReceivedDelegate_Handle(FOnSessionMemberStorageUpdateReceivedDelegate::CreateLambda([&SessionInterface](FName SessionName, const FUniqueNetId& UpdatedMemberId)
{
if (SessionName == NAME_PartySession)
{
// Party member storage updated
const FNamedOnlineSession* PartySession = SessionInterface->GetNamedSession(SessionName);
const TSharedPtr<FOnlineSessionInfoAccelByteV2> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(PartySession->SessionInfo);
TUniqueNetIdMap<FJsonObjectWrapper> MemberStorages;
SessionInfo->GetAllSessionMemberStorage(MemberStorages);
}
}));
SessionInterface->AddOnSessionLeaderStorageUpdateReceivedDelegate_Handle(FOnSessionLeaderStorageUpdateReceivedDelegate::CreateLambda([&SessionInterface](FName SessionName)
{
if (SessionName == NAME_PartySession)
{
// Party member storage updated
const FNamedOnlineSession* PartySession = SessionInterface->GetNamedSession(SessionName);
const TSharedPtr<FOnlineSessionInfoAccelByteV2> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(PartySession->SessionInfo);
FJsonObjectWrapper LeaderStorage;
SessionInfo->GetSessionLeaderStorage(LeaderStorage);
}
}));
Create Party
SessionInterface->AddOnCreateSessionCompleteDelegate_Handle( FOnCreateSessionCompleteDelegate::CreateLambda([](const FName SessionName, const bool bWasSuccessful)
{
if (SessionName == NAME_PartySession)
{
if (bWasSuccessful)
{
// Handle party creation
}
// Handle party creation errors
}
}));
FOnlineSessionSettings NewSessionSettings;
NewSessionSettings.NumPrivateConnections = 4;
NewSessionSettings.Set(SETTING_SESSION_JOIN_TYPE, TEXT("INVITE_ONLY"));
NewSessionSettings.Set(SETTING_SESSION_TYPE, SETTING_SESSION_TYPE_PARTY_SESSION);
NewSessionSettings.Set(SETTING_SESSION_TEMPLATE_NAME, TEXT("TemplateNameFromAdminPortal"));
SessionInterface->CreateSession(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, NewSessionSettings);
Invite to Party
SessionInterface->AddOnSendSessionInviteCompleteDelegate_Handle(FOnSendSessionInviteCompleteDelegate::CreateLambda([](const FUniqueNetId& LocalSenderId, FName SessionName, bool bWasSuccessful, const FUniqueNetId& InviteeId)
{
if (SessionName == NAME_PartySession && bWasSuccessful)
{
// Send party invite success
}
}));
SessionInterface->SendSessionInviteToFriend(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, OtherUserPlayerId.ToSharedRef().Get());
Leave party
auto PartySession = SessionInterface->GetNamedSession(NAME_PartySession);
auto PartySessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2> (PartySession->SessionInfo);
FString PartyId = PartySessionInfo->GetBackendSessionDataAsPartySession()->ID;
FOnLeaveSessionComplete OnLeavePartyComplete =
FOnLeaveSessionComplete::CreateLambda([](const bool bWasSuccessful, FString SessionId)
{
if (bWasSuccessful)
{
// Successfully left a party
}
});
SessionInterface->LeaveSession(LocalPlayerId.ToSharedRef().Get(), EAccelByteV2SessionType::PartySession, PartyId, OnLeavePartyComplete);
Promote to Party leader
// Listen for party session updates
SessionInterface->AddOnSessionUpdateReceivedDelegate_Handle(FOnSessionUpdateReceivedDelegate::CreateLambda([SessionInterface](FName SessionName)
{
if (SessionName == NAME_PartySession)
{
// there is an update notification for the party
auto LatestPartySession = SessionInterface->GetNamedSession(NAME_PartySession);
}
}));
// Promote other user
FOnPromotePartySessionLeaderComplete OnPromotePartySessionLeaderComplete =
FOnPromotePartySessionLeaderComplete::CreateLambda([](const FUniqueNetId &PromotedUserId, const FOnlineError &Result)
{
if (Result.bSucceeded)
{
// successfully promoted new party leader
}
});
SessionInterface->PromotePartySessionLeader(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, OtherUserPlayerId.ToSharedRef().Get(), OnPromotePartySessionLeaderComplete);
Kick from Party
// Listen for kicked from party session notifications
SessionInterface->AddOnKickedFromSessionDelegate_Handle(FOnKickedFromSessionDelegate::CreateLambda([](FName SessionName)
{
if (SessionName == NAME_PartySession)
{
// player kicked from party
}
}));
// Kick player from party session
FOnKickPlayerComplete OnKickPlayerComplete =
FOnKickPlayerComplete::CreateLambda([](const bool bWasSuccessful, const FUniqueNetId &KickedPlayerId)
{
if (bWasSuccessful)
{
// Player successfully kicked from the party
}
});
SessionInterface->KickPlayer(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, PlayerIdToKick.ToSharedRef().Get());
Join Party
// Listen for party invitations
SessionInterface->AddOnV2SessionInviteReceivedDelegate_Handle(FOnV2SessionInviteReceivedDelegate::CreateLambda( [](const FUniqueNetId &UserId, const FUniqueNetId &, const FOnlineSessionInviteAccelByte &InviteResult)
{
if (InviteResult.SessionType == EAccelByteV2SessionType::PartySession)
{
// Handle on party session invite notifications
// InviteResult contains the party details
}
}));
// Add delegate on join party session completed
SessionInterface->AddOnJoinSessionCompleteDelegate_Handle( FOnJoinSessionCompleteDelegate::CreateLambda([](FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (SessionName == NAME_PartySession)
{
// Join party session completed, check the Result
}
}));
// Join the party session
FOnlineSessionInviteAccelByte Invitation; // The invitation from the notification
SessionInterface->JoinSession(*LocalPlayerId.Get(), NAME_PartySession, Invitation.Session);
Reject Party invitation
FOnRejectSessionInviteComplete OnRejectSessionInviteComplete = FOnRejectSessionInviteComplete::CreateLambda([](const bool bWasSuccessful)
{
if (bWasSuccessful)
{
// successfully reject party invitation
}
});
// Join the party session
FOnlineSessionInviteAccelByte Invitation; // The invitation from the notification
SessionInterface->RejectInvite(LocalPlayerId.ToSharedRef().Get(), Invitation, OnRejectSessionInviteComplete);
Get current Party info
const FNamedOnlineSession* PartySession = SessionInterface->GetNamedSession(NAME_PartySession);
const TSharedPtr<FOnlineSessionInfoAccelByteV2> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(PartySession->SessionInfo);
TSharedPtr<FAccelByteModelsV2BaseSession> SessionData = SessionInfo->GetBackendSessionData();
Get user storage
const FNamedOnlineSession* PartySession = SessionInterface->GetNamedSession(NAME_PartySession);
const TSharedPtr<FOnlineSessionInfoAccelByteV2> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(PartySession->SessionInfo);
TUniqueNetIdMap<FJsonObjectWrapper> MemberStorages;
SessionInfo->GetAllSessionMemberStorage(MemberStorages);
FJsonObjectWrapper LeaderStorage;
SessionInfo->GetSessionLeaderStorage(LeaderStorage);
Quick start
Follow the steps in these sections to get started with AGS Party quickly.
Create a party implementation
Set the Maximum Party Member value and Auto kick on disconnect features from the Party Configurations in the Admin Portal.
Create a User Widget C++ class called
AccelByteParty
to accommodate the functions for the Party Menu widget.Add the following header to ensure the functions work correctly for AGS Lobby features:
.cpp
...
#include "OnlineSessionInterfaceV2AccelByte.h"
#include "OnlineSubsystemAccelByteSessionSettings.h"
#include "OnlineSubsystemSessionSettings.h"
...
Create a party
This function returns a result from the FNamedOnlineSession
class type, which contains party data such as:
PartyId
LeaderId
, the leader'suserId
- Members, array of the party members'
userIds
- Invitees, array of the invitees'
userIds
InvitationToken
, the party invitation's token
- .cpp
- .h
void UAccelByteParty::OnClickedCreateParty()
{
SessionInterface->AddOnCreateSessionCompleteDelegate_Handle( FOnCreateSessionCompleteDelegate::CreateLambda([](const FName SessionName, const bool bWasSuccessful)
{
if (SessionName == NAME_PartySession)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Create Party Success!"));
}
UE_LOG(LogTemp, Error, TEXT("Create Party Error!"));
}
}));
FOnlineSessionSettings NewSessionSettings;
NewSessionSettings.NumPrivateConnections = 4;
NewSessionSettings.Set(SETTING_SESSION_JOIN_TYPE, TEXT("INVITE_ONLY"));
NewSessionSettings.Set(SETTING_SESSION_TYPE, SETTING_SESSION_TYPE_PARTY_SESSION);
NewSessionSettings.Set(SETTING_SESSION_TEMPLATE_NAME, TEXT("TemplateNameFromAdminPortal"));
SessionInterface->CreateSession(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, NewSessionSettings);
}
/**
* @brief Callback when creating a party.
*/
UFUNCTION()
void OnClickedCreateParty();
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.
- .cpp
- .h
void UAccelByteParty::OnClickedInviteParty()
{
SessionInterface->AddOnSendSessionInviteCompleteDelegate_Handle(FOnSendSessionInviteCompleteDelegate::CreateLambda([](const FUniqueNetId& LocalSenderId, FName SessionName, bool bWasSuccessful, const FUniqueNetId& InviteeId)
{
if (SessionName == NAME_PartySession && bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Send Party Invite Success!"));
}
}));
SessionInterface->SendSessionInviteToFriend(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, OtherUserPlayerId.ToSharedRef().Get());
}
/**
* @brief Callback when inviting to a party.
* @param UserId user ID to invited.
*/
UFUNCTION()
void OnClickedInviteParty(const FString& UserId);
Leave a party
Use this function to leave a party.
- .cpp
- .h
void UAccelByteParty::OnClickedLeaveParty()
{
auto PartySession = SessionInterface->GetNamedSession(NAME_PartySession);
auto PartySessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2> (PartySession->SessionInfo);
FString PartyId = PartySessionInfo->GetBackendSessionDataAsPartySession()->ID;
FOnLeaveSessionComplete OnLeavePartyComplete =
FOnLeaveSessionComplete::CreateLambda([](const bool bWasSuccessful, FString SessionId)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Leave Party Success!"));
}
});
SessionInterface->LeaveSession(LocalPlayerId.ToSharedRef().Get(), EAccelByteV2SessionType::PartySession, PartyId, OnLeavePartyComplete);
}
/**
* @brief Callback when leaving Party.
*/
UFUNCTION()
void OnClickedLeaveParty();
Promote a friend to party leader
You need the UserID
of the Party member you want to promote to the party leader.
- .cpp
- .h
void UAccelByteParty::OnClickedPartyLeader()
{
FOnPromotePartySessionLeaderComplete OnPromotePartySessionLeaderComplete =
FOnPromotePartySessionLeaderComplete::CreateLambda([](const FUniqueNetId &PromotedUserId, const FOnlineError &Result)
{
if (Result.bSucceeded)
{
UE_LOG(LogTemp, Log, TEXT("Promote Party Leader Success!"));
}
});
SessionInterface->PromotePartySessionLeader(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, OtherUserPlayerId.ToSharedRef().Get(), OnPromotePartySessionLeaderComplete);
}
/**
* @brief Callback for OnClicked Party Leader Button.
*/
UFUNCTION()
void OnClickedPartyLeader();
Kick a friend from a party
You need the UserID
of the party member you want kicked from the party.
- .cpp
- .h
void UAccelByteParty::OnClickedKickParty()
{
FOnKickPlayerComplete OnKickPlayerComplete =
FOnKickPlayerComplete::CreateLambda([](const bool bWasSuccessful, const FUniqueNetId &KickedPlayerId)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Kick Party Member Success!"));
}
});
SessionInterface->KickPlayer(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, PlayerIdToKick.ToSharedRef().Get());
}
```cpp
/**
* @brief Callback for OnClicked Kick Party.
*/
UFUNCTION()
void OnClickedKickParty();
```
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 "OnlineSessionInterfaceV2AccelByte.h"
#include "OnlineSubsystemAccelByteSessionSettings.h"
#include "OnlineSubsystemSessionSettings.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.
Add the following functions to handle party invitations in the
AccelBytePartyInvitationPopUp
class:
Handle party invitations
Follow the steps in this section to implement handling party invitation.
Accept party invitation (join party)
This function requires partyId
and invitationToken
which are contained in a PartyInvitation
object.
- .cpp
- .h
void UAccelBytePartyInvitationPopUp::OnClickedAcceptPartyInvitation()
{
SessionInterface->AddOnJoinSessionCompleteDelegate_Handle( FOnJoinSessionCompleteDelegate::CreateLambda([](FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (SessionName == NAME_PartySession)
{
UE_LOG(LogTemp, Log, TEXT("Join Party Success!"));
}
}));
// Join the party session
FOnlineSessionInviteAccelByte Invitation; // The invitation from the notification
SessionInterface->JoinSession(*LocalPlayerId.Get(), NAME_PartySession, Invitation.Session);
}
/**
* @brief Callback for accepting Party invitation.
*/
UFUNCTION()
void OnClickedAcceptPartyInvitation();
Reject party invitation
This function requires partyId
and invitationToken
, which are contained in a PartyInvitation
object.
- .cpp
- .h
void UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation()
{
FOnRejectSessionInviteComplete OnRejectSessionInviteComplete = FOnRejectSessionInviteComplete::CreateLambda([](const bool bWasSuccessful)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Reject Party Success!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Reject Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}
});
FOnlineSessionInviteAccelByte Invitation; // The invitation from notification
SessionInterface->RejectInvite(LocalPlayerId.ToSharedRef().Get(), Invitation, OnRejectSessionInviteComplete);
}
/**
* @brief Callback for rejecting the party invitation.
*/
UFUNCTION()
void OnClickedRejectPartyInvitation();
Use the result value from the
SessionInterface->AddOnV2SessionInviteReceivedDelegate_Handle()
event for theFOnlineSessionInviteAccelByte
value.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 AGS Lobby.Add a log for each event:
- .cpp
- .h
void UAccelByteParty::SetNotificationDelegate()
{
// Listen for party invitation
SessionInterface->AddOnV2SessionInviteReceivedDelegate_Handle(FOnV2SessionInviteReceivedDelegate::CreateLambda( [](const FUniqueNetId &UserId, const FUniqueNetId &, const FOnlineSessionInviteAccelByte &InviteResult)
{
if (InviteResult.SessionType == EAccelByteV2SessionType::PartySession)
{
UE_LOG(LogTemp, Log, TEXT("Received a party invitation!"));
}
}));
// Listen for kicked from party session notification
SessionInterface->AddOnKickedFromSessionDelegate_Handle(FOnKickedFromSessionDelegate::CreateLambda([](FName SessionName)
{
if (SessionName == NAME_PartySession)
{
UE_LOG(LogTemp, Log, TEXT("Kicked from a party!"));
}
}));
SessionInterface->AddOnSessionParticipantsChangeDelegate_Handle(FOnSessionParticipantsChangeDelegate::CreateLambda([](FName SessionName, const FUniqueNetId& Member, bool bJoined)
{
if (SessionName == NAME_PartySession)
{
if(bJoined)
{
UE_LOG(LogTemp, Log, TEXT("User joined party!"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("User left party!"));
}
}
}));
SessionInterface->AddOnSessionInviteRejectedDelegate_Handle(
FOnSessionInviteRejectedDelegate::CreateLambda([](FName SessionName, const FUniqueNetId& RejecterId)
{
if (SessionName == NAME_PartySession)
{
UE_LOG(LogTemp, Log, TEXT("Invitation rejected!"));
}
}));
}/**
* @brief Function where all the notifications are being set.
*/
void SetNotificationDelegate();You may wish to retrieve the party data. The code snippet below shows how to get party data from
SessionInterface
:void UAccelByteParty::SetNotificationDelegate()
{
...
SessionInterface->AddOnSessionUpdateReceivedDelegate_Handle(FOnSessionUpdateReceivedDelegate::CreateLambda([SessionInterface](FName SessionName)
{
if (SessionName == NAME_PartySession)
{
// there is update notification for party
auto LatestPartySession = SessionInterface->GetNamedSession(NAME_PartySession);
UE_LOG(LogTemp, Log, TEXT("On any party data update"));
}
}));
}You may wish to unbind delegates from
SessionInterface
using the method below:...
SessionInterface->ClearOnSessionParticipantsChangeDelegate_Handle(OnSessionParticipantsChangeDelegate)
SessionInterface->ClearOnSessionInviteRejectedDelegate_Handle(OnSessionInviteRejectedDelegate);
...
Congratulations! You have successfully implemented AGS Party.
Step-by-Step guide
Follow the steps in this section for more detailed step-by-step instructions for implementing AGS Party for AGS Shared Cloud tier.
Implement the UI
Create a widget blueprint class called
WB_Party
and re-parent theWB_Party
widget blueprint to theAccelByteParty
C++ class.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.
To display a party member's information, create a widget blueprint class called
WB_PartyPlayerEntry
.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.
Create a widget blueprint class called
WB_InvitationParty
.Re-parent the
WB_InvitationParty
widget blueprint to theAccelBytePartyInvitationPopUp
C++ class.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.
To prepare the Notification Box widget, start by creating a new widget blueprint class called
WB_LobbyNotificationEntry
.Add a text box for the notification text to the
WB_LobbyNotificationEntry
widget blueprint.Create another widget blueprint class called
WB_Notification
.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.
noteDon't forget to set the Entry Widget Class under the List Entries component with
WB_LobbyNotificationEntry
.Since the party functionality 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.
- Scroll box to add the
Implement the code
Add the code from the examples in this section.
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.
Do this by adding all of the party-related classes to the top of the
TutorialMenuHUD
class.- .cpp
- .h
...
#include "AccelByte/Party/AccelByteParty.h"
......
class UAccelByteParty;
...Initialize the pointer to refer to the Party widget classes in the
BeginPlay()
function.- .cpp
- .h
void ATutorialMenuHUD::BeginPlay()
{
...
check(PartyClass != nullptr);
...
PartyMenu = CreateWidget<UAccelByteParty>(PlayerController, PartyClass.Get());
...
}/**
* @brief Party Menu widget class
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelByteParty> PartyClass;
/**
* @brief Party Menu widget pointer
*/
UPROPERTY()
UAccelByteParty* PartyMenu;Add a getter function for the Party Menu widgets' pointer.
- .h
/**
* @brief Getter for Party Menu widget
*/
UAccelByteParty* GetPartyMenu() const {return PartyMenu; }
Party's player entry
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
).Open the
AccelBytePartyPlayerEntry
class and add the following to the the top of the class:- .cpp
- .h
...
#include "AccelByteParty.h"
#include "OnlineSessionInterfaceV2AccelByte.h"
#include "OnlineSubsystemAccelByteSessionSettings.h"
#include "OnlineSubsystemSessionSettings.h"
#include "Components/Image.h"
#include "Components/TextBlock.h"
#include "Components/Button.h"
......
#include "OnlineSessionInterfaceV2AccelByte.h"
#include "OnlineSubsystemAccelByteSessionSettings.h"
#include "OnlineSubsystemSessionSettings.h"
...
class UImage;
class UTextBlock;
class UButton;
...Specify all the widget components in the
AccelBytePartyPlayerEntry
header file.- .h
/**
* @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;Create a variable called
UserData
to store the user data required by the party functionalities. To getUserData
from other scripts, create a function that will return the variable.- .cpp
- .h
FSimpleUserData UAccelBytePartyPlayerEntry::GetUserData() const
{
return UserData;
}/**
* @brief Get User Data stored in this entry.
*/
FSimpleUserData GetUserData() const;
/**
* @brief Models to store User Data contained in this entry.
*/
FSimpleUserData UserData;Move the
OnClickedPartyLeader()
function and theOnClickedKickParty()
function fromAccelByteParty
to theAccelBytePartyPlayerEntry
class.Once you have done that, modify both functions using
CreateUObject()
instead ofCreateWeakLambda()
for better readability.- .cpp
- .h
void UAccelBytePartyPlayerEntry::OnClickedPartyLeader()
{
FOnPromotePartySessionLeaderComplete OnPromotePartySessionLeaderComplete =
FOnPromotePartySessionLeaderComplete::CreateLambda([](const FUniqueNetId &PromotedUserId, const FOnlineError &Result)
{
if (Result.bSucceeded)
{
UE_LOG(LogTemp, Log, TEXT("Promote Party Leader Success!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Promote Party Leader Failed!"));
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, "Unable to Promote Party Leader!");
}
});
SessionInterface->PromotePartySessionLeader(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, FUniqueNetIdAccelByteUser::Create(UserData.UserId).Get(), OnPromotePartySessionLeaderComplete);
}
void UAccelBytePartyPlayerEntry::OnClickedKickParty()
{
FOnKickPlayerComplete OnKickPlayerComplete =
FOnKickPlayerComplete::CreateLambda([](const bool bWasSuccessful, const FUniqueNetId &KickedPlayerId)
{
if (bWasSuccessful)
{
// Player successfully kicked from the party
}
});
SessionInterface->KickPlayer(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, FUniqueNetIdAccelByteUser::Create(UserData.UserId).Get());
}/**
* @brief Callback for OnClicked Party Leader Button.
*/
UFUNCTION()
void OnClickedPartyLeader();
/**
* @brief Callback for OnClicked Kick Party.
*/
UFUNCTION()
void OnClickedKickParty();Add the
NativeConstruct()
override function and set up the button widgets to be called by their functions on the button clicked.- .cpp
- .h
void UAccelBytePartyPlayerEntry::NativeConstruct()
{
Super::NativeConstruct();
Btn_PartyLeader->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyPlayerEntry::OnClickedPartyLeader);
Btn_KickPlayer->OnClicked.AddUniqueDynamic(this, &UAccelBytePartyPlayerEntry::OnClickedKickParty);
}...
virtual void NativeConstruct() override;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.- .cpp
- .h
void UAccelBytePartyPlayerEntry::InitData(const FString& PlayerId)
{
UserData.UserId = PlayerId;
IdentityInterface->GetApiClient(USER_LOCAL_NUM)->User.GetUserByUserId(
PlayerId,
THandler<FSimpleUserData>::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnSuccessGetUserId),
FErrorHandler::CreateUObject(this, &UAccelBytePartyPlayerEntry::OnFailedGetUserId));
}/**
* @brief Initialize User ID and convert to FSimpleUserData in here.
*/
void InitData(const FString& PlayerId);
/**
* @brief Callback for successfully getting the user ID.
*/
void OnSuccessGetUserId(const FSimpleUserData& Data);
/**
* @brief Callback for failing to get user ID.
*/
void OnFailedGetUserId(int32 ErrorCode, const FString& ErrorMessage);Define what to do after the
GetUserByUserId()
query result is received in both of the response functions you declared earlier.- .cpp
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);
}Create a function to enable visibility of the Promote Leader and Kick Party Member buttons if the player is the party leader.
- .cpp
- .h
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);
}
}/**
* @brief Enabling the button to Promote Leader and Kick Party member.
* @param bEnable Enable all button components.
*/
void EnableAllComponents(bool bIsEnable) const;Create a function that will show the party leader's icon image.
- .cpp
- .h
void UAccelBytePartyPlayerEntry::SetImageIconLeader(bool bIsLeader) const
{
if (bIsLeader)
{
Img_LeaderIcon->SetVisibility(ESlateVisibility::Visible);
}
else
{
Img_LeaderIcon->SetVisibility(ESlateVisibility::Hidden);
}
}/**
* @brief Set Image Icon to party member.
* @param bIsLeader Checks if the player is the leader of the party.
*/
void SetImageIconLeader(bool bIsLeader) const;
Party menu
Open the
AccelByteParty
header and class, then add the following includes at the top of the header and class:- .cpp
- .h
#include "AccelBytePartyPlayerEntry.h"
#include "Components/ScrollBox.h"
#include "Components/Button.h"
#include "Components/Image.h"
#include "Components/TextBlock.h"
#include "TutorialProject/TutorialMenuHUD.h"#include "AccelBytePartyInvitationPopUp.h"
...
class UAccelBytePartyPlayerEntry;
class UScrollBox;
class UButton;
class UTextBlock;
class ATutorialMenuHUD;Specify all the widgets components in the
AccelByteParty
header file.- .h
/**
* @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 the party ID.
*/
UPROPERTY(meta = (BindWidget))
UTextBlock* Tb_PartyID;Declare a
NativeConstruct()
override function to initialize the widget components and theTotalPartyMember
based on your Max Party Member configuration in the AGS Admin Portal.- .cpp
- .h
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();
}...
virtual void NativeConstruct() override;
/**
* @brief Tutorial Menu HUD to handle all functionality of the UI.
*/
ATutorialMenuHUD* TutorialMenuHUD;
/**
* @brief Define maximum party member allowed.
*/
UPROPERTY()
int TotalPartyMember;Create a new function to enable and disable the create party button and the leave party button, depending on the situation.
- .cpp
- .h
void UAccelByteParty::SetCreatePartyButtonEnabled(bool bIsCreateParty) const
{
Btn_CreateParty->SetIsEnabled(bIsCreateParty);
Btn_LeaveParty->SetIsEnabled(!bIsCreateParty);
}/**
* @brief Set enabled / disabled state of the Create party & Leave party button.
*/
void SetCreatePartyButtonEnabled(bool bIsCreateParty) const;Also create a function to display the party leader icon if the player is a party member. To check on each player in the party, create an array that will hold the party's player entry data.
- .cpp
- .h
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 == IdentityInterface->GetApiClient(USER_LOCAL_NUM)->CredentialsRef->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);
}
}
}/**
* @brief Update the leader icon.
* @param LeaderId The ID of the current party leader.
*/
void SetUpdateLeaderIcon(const FString& LeaderId) const;
/**
* @brief Copy Array of player entry.
*/
TArray<TWeakObjectPtr<UAccelBytePartyPlayerEntry>> PartyPlayerEntryWidgetArray;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.- .cpp
- .h
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);
}/**
* @brief Create an Empty Party List with UMG.
*/
void RefreshPartyList();
/**
* @brief Reference to Party Player Entry Class.
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelBytePartyPlayerEntry> PartyPlayerEntryClass;
/**
* @brief Current Party Information
*/
FAccelByteModelsPartyDataNotif PartyInfo;Then, create a new function that will refresh all of the Party Widgets state.
- .cpp
- .h
void UAccelByteParty::RefreshPartyEntries()
{
Tb_PartyID->SetText(FText::FromString("##############"));
SetCreatePartyButtonEnabled(true);
RefreshPartyList();
}/**
* @brief Refresh party entries.
*/
void RefreshPartyEntries();Also prepare a new function that will reset the current stored Party Info.
- .cpp
- .h
void UAccelByteParty::ResetPartyInfo()
{
const FAccelByteModelsPartyDataNotif EmptyData;
PartyInfo = EmptyData;
bIsInParty = false;
RefreshPartyEntries();
}/**
* @brief Reset current PartyInfo class variable and refresh current party entries.
*/
void ResetPartyInfo();
bool bIsInParty;Remove
SetCreatePartyResponseDelegate
,SetLeavePartyResponseDelegate
, andSetInvitePartyResponseDelegate
from their current function. Put them in theSetNotificationDelegate()
function instead.Also, modify the
SetNotificationDelegate()
function by changing theCreateWeakLambda
withCreateUObject
for readability.- .cpp
- .h
void UAccelByteParty::OnClickedCreateParty()
{
FOnlineSessionSettings NewSessionSettings;
NewSessionSettings.NumPrivateConnections = 4;
NewSessionSettings.Set(SETTING_SESSION_JOIN_TYPE, TEXT("INVITE_ONLY"));
NewSessionSettings.Set(SETTING_SESSION_TYPE, SETTING_SESSION_TYPE_PARTY_SESSION);
NewSessionSettings.Set(SETTING_SESSION_TEMPLATE_NAME, TEXT("TemplateNameFromAdminPortal"));
SessionInterface->CreateSession(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, NewSessionSettings);
}
void UAccelByteParty::OnClickedLeaveParty()
{
auto PartySession = SessionInterface->GetNamedSession(NAME_PartySession);
auto PartySessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2> (PartySession->SessionInfo);
FString PartyId = PartySessionInfo->GetBackendSessionDataAsPartySession()->ID;
SessionInterface->LeaveSession(LocalPlayerId.ToSharedRef().Get(), EAccelByteV2SessionType::PartySession, PartyId, OnLeavePartyComplete);
}
void UAccelByteParty::OnClickedInviteParty(const FString& UserId)
{
SessionInterface->SendSessionInviteToFriend(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, FUniqueNetIdAccelByteUser::Create(UserId).Get());
}
void UAccelByteParty::SetNotificationDelegate()
{
SessionInterface->OnCreateSessionCompleteDelegates.AddUObject(this, &UAccelByteParty::OnCreateSessionComplete);
SessionInterface->OnDestroySessionCompleteDelegates.AddUObject(this, &UAccelByteParty::OnLeaveSessionComplete);
SessionInterface->OnSendSessionInviteCompleteDelegates.AddUObject(this, &UAccelByteParty::OnSendSessionInviteComplete);
SessionInterface->OnV2SessionInviteReceivedDelegates.AddUObject(this, &UAccelByteParty::OnSessionInviteReceived);
SessionInterface->OnKickedFromSessionDelegates.AddUObject(this, &UAccelByteParty::OnKickedFromParty);
SessionInterface->OnSessionParticipantsChangeDelegates.AddUObject(this, &UAccelByteParty::OnPartyMembersChange);
SessionInterface->OnSessionUpdateReceivedDelegates.AddUObject(this, &UAccelByteParty::OnPartySessionUpdateReceived);
}// Response for creating a party.
void OnCreateSessionComplete(FName SessionName, bool bSucceeded);
// Response for leaving a party.
void OnLeaveSessionComplete(FName SessionName, bool bSucceeded);
// Response for inviting friend to a party.
void OnSendSessionInviteComplete(const FUniqueNetId& LocalSenderId, FName SessionName, bool bSucceeded, const FUniqueNetId& InviteeId);
// Response for getting a notification for party invitation.
void OnSessionInviteReceived(const FUniqueNetId& UserId, const FUniqueNetId& FromId, const FOnlineSessionInviteAccelByte& Invite)
// Response for notification when someone is kicked from the party.
void OnKickedFromParty(FName SessionName);
// Response for notifications when someone joins or leaves the party.
void OnPartyMembersChange(FName SessionName, const FUniqueNetId& Member, bool bJoined);
// Response for updating party data when someone does a party-related action.
void OnPartySessionUpdateReceived(FName SessionName);Now, define the response delegate functions you declared earlier for each create party, leave party, and invite to party response query.
Create Party's Success and Failed Response
- .cpp
void UAccelByteParty::OnCreateSessionComplete(FName SessionName, bool bSucceeded)
{
if (SessionName != NAME_PartySession)
{
return;
}
if(bSucceeded)
{
UE_LOG(LogTemp, Log, TEXT("Create Party Success!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Create Party Failed"));
}
}Leave Party's Success and Failed Response
- .cpp
void UAccelByteParty::OnLeaveSessionComplete(FName SessionName, bool bSucceeded)
{
if (SessionName != NAME_PartySession)
{
return;
}
if (bSucceeded)
{
UE_LOG(LogTemp, Log, TEXT("Leave Party Success!"));
ResetPartyInfo();
}
else
{
UE_LOG(LogTemp, Error, TEXT("Leave Party Failed"));
}
}Invite to Party's Success and Failed Response
- .cpp
void UAccelByteParty::OnSendSessionInviteComplete(const FUniqueNetId& LocalSenderId, FName SessionName, bool bSucceeded, const FUniqueNetId& InviteeId)
{
if (SessionName != NAME_PartySession)
{
return;
}
if (bSucceeded)
{
UE_LOG(LogTemp, Log, TEXT("Invite to Party Success!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Invite to Party Failed: %d , %s"), ErrorCode, *ErrorMessage);
}
}Define the notification events' callback functions to update your widgets based on each event updates:
OnInvitePartyGetInviteNotice()
- .cpp
void UAccelByteParty::OnSessionInviteReceived(const FUniqueNetId& UserId, const FUniqueNetId& FromId, const FOnlineSessionInviteAccelByte& Invite)
{
if (Invite.SessionType != EAccelByteV2SessionType::PartySession)
{
return;
}
UE_LOG(LogTemp, Log, TEXT("On Get Invite to Party Notice!"));
}OnInvitePartyKickedNotice()
- .cpp
void UAccelByteParty::OnKickedFromParty(FName SessionName)
{
if (SessionName != NAME_PartySession)
{
return;
}
UE_LOG(LogTemp, Log, TEXT("You have been kicked from the party!"));
ResetPartyInfo();
}OnLeavePartyNotice()
- .cpp
void UAccelByteParty::OnPartyMembersChange(FName SessionName, const FUniqueNetId& Member, bool bJoined)
{
if (SessionName != NAME_PartySession)
{
return;
}
if (bJoined)
{
UE_LOG(LogTemp, Log, TEXT("On Some Player Joined Notice!"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("On Some Player Leaved Notice!"));
}
}OnPartyDataUpdateResponse()
- .cpp
void UAccelByteParty::OnPartySessionUpdateReceived(FName SessionName)
{
if (SessionName != NAME_PartySession)
{
return;
}
UE_LOG(LogTemp, Log, TEXT("On Party Data Update Response!"));
}
Party invitation pop-up
Open the
AccelBytePartyInvitationPopUp
class and add the widget-related header at the top of the class- .cpp
- .h
...
#include "Components/Button.h"
#include "TutorialProject/TutorialMenuHUD.h"
#include "TutorialProject/TutorialProjectUtilities.h"...
#include "AccelByteParty.h"
#include "Models/AccelByteLobbyModels.h"
...
class ATutorialMenuHUD;
class UTextBlock;
class UButton;Specify all the widget components in the
AccelBytePartyInvitationPopUp
header file.- .h
/**
* @brief Text Box for 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;Add the
NativeConstruct()
override function to initialize theTutorialMenuHUD
object and widgets.- .cpp
- .h
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);
}...
virtual void NativeConstruct() override;
...
/**
* @brief Tutorial Menu HUD to handle all functionality of the UI.
*/
ATutorialMenuHUD* TutorialMenuHUD;Create a function that initializes the local
InvitationData
you created in the QuickStart section if you haven't created a new local variable.- .cpp
- .h
void UAccelBytePartyInvitationPopUp::InitData(const FAccelByteModelsPartyGetInvitedNotice& Result)
{
InvitationData = Result;
}/**
* @brief Initialize ResultInvited data to get player response to party host.
*/
void InitData(const FAccelByteModelsPartyGetInvitedNotice& Result);
...
/**
* @brief Get Invitation notice data to pass player accepting / rejecting
* party invitation request.
*/
FAccelByteModelsPartyGetInvitedNotice InvitationData;Modify the
OnClickedAcceptPartyInvitation()
function by adding code to disable the button after clicking it.- .cpp
void UAccelBytePartyInvitationPopUp::OnClickedAcceptPartyInvitation()
{
Btn_AcceptParty->SetIsEnabled(false);
SessionInterface->AddOnJoinSessionCompleteDelegate_Handle( FOnJoinSessionCompleteDelegate::CreateLambda([](FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (SessionName == NAME_PartySession && Result == EOnJoinSessionCompleteResult::Success)
{
UE_LOG(LogTemp, Log, TEXT("Join Party Success!"));
}
}));
// Join the party session
FOnlineSessionInviteAccelByte Invitation; // The invitation from the notification
SessionInterface->JoinSession(*LocalPlayerId.Get(), NAME_PartySession, Invitation.Session);
}Modify the
OnClickedRejectPartyInvitation()
function by adding widget implementation.- .cpp
void UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation()
{
Btn_RejectParty->SetIsEnabled(false);
FOnRejectSessionInviteComplete OnRejectSessionInviteComplete = FOnRejectSessionInviteComplete::CreateLambda([](const bool bWasSuccessful)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Reject Party Success!"));
RemoveFromParent();
}
else
{
UE_LOG(LogTemp, Error, TEXT("Reject Party Error! %d: %s"), ErrorCode, *ErrorMessage);
RemoveFromParent();
}
});
// Join the party session
FOnlineSessionInviteAccelByte Invitation; // The invitation from the notification
SessionInterface->RejectInvite(LocalPlayerId.ToSharedRef().Get(), Invitation, OnRejectSessionInviteComplete);
}Open the
AccelByteParty
class, create a reference to the party invitation pop-up class and set theAccelBytePartyInvitationPopUp
class as the reference.Also, in the
OnInvitePartyGetInviteNotice()
function, add the code to create a new party invitation pop-up widget.- .cpp
- .h
void UAccelByteParty::OnInvitePartyGetInviteNotice(const FAccelByteModelsPartyGetInvitedNotice& Result)
{
...
const TWeakObjectPtr<UAccelBytePartyInvitationPopUp> InvitationPopUpWidget = MakeWeakObjectPtr<UAccelBytePartyInvitationPopUp>(
CreateWidget<UAccelBytePartyInvitationPopUp>(this, PartyInvitationPopUpClass.Get()));
InvitationPopUpWidget.Get()->InitData(Result);
InvitationPopUpWidget->AddToViewport();
}/**
* @brief Reference to Party Invitation Pop Up Class.
*/
UPROPERTY(EditDefaultsOnly)
TSubclassOf<UAccelBytePartyInvitationPopUp> PartyInvitationPopUpClass;
Party-related widget implementation
Now that you have AGS Party implemented:
Open your
AccelByteFriendEntry
class and add the following code inside theOnClickedInviteParty()
function. Make sure theTutorialMenuHUD
pointer andUserData
exist.- .cpp
...
#include "../Party/AccelByteParty.h"
...
UAccelByteFriendEntry::OnClickedInviteParty()
{
TutorialMenuHUD->GetPartyMenu()->OnClickedInviteParty(UserData.UserId);
}Next, open the
AccelByteLobby
header class and include theAccelByteParty
header file.- .cpp
- .h
...
#include "../Party/AccelByteParty.h"
...
class UScrollBox;Still in the
AccelByteLobby
class, in theSetLobbyNotificationDelegate()
function, under the Lobby'sSetConnectSuccessDelegate()
lambda, add some code to prepare the party widgets in the lobby page.- .cpp
void UAccelByteLobby::SetLobbyNotificationDelegate()
{
IdentityInterface->AddAccelByteOnConnectLobbyCompleteDelegate_Handle(USER_LOCAL_NUM
, FAccelByteOnConnectLobbyCompleteDelegate::CreateLambda([](int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FOnlineErrorAccelByte& Error)
{
...
TutorialMenuHUD->GetPartyMenu()->SetCreatePartyButtonEnabled(true);
TutorialMenuHUD->GetPartyMenu()->RefreshPartyEntries();
}));
...
}Specify the party's box widget in
AccelByteLobby
class, then create a function to spawn the party widget to lobby page.- .cpp
- .h
void UAccelByteLobby::AddPartyChild(UAccelByteParty* PartyMenu)
{
Sb_Party->AddChild(PartyMenu);
}/**
* @brief Scroll Box to spawn Party Widget.
*/
UPROPERTY(meta = (BindWidget))
UScrollBox* Sb_Party;
/**
* @brief Add new Party Menu to the party scroll box widget child.
* @param PartyMenu Party Menu that is going to be added to the party scroll box widget child.
*/
UFUNCTION()
void AddPartyChild(UAccelByteParty* PartyMenu);Open the
TutorialMenuHUD
class, then call Lobby'sAddPartyChild()
function in theInitMainMenu()
function.- .cpp
void ATutorialMenuHUD::InitMainMenu()
{
...
LobbyMenu->AddPartyChild(PartyMenu);
...
}In the
TutorialMenuHUD
class, refresh the party list when opening the lobby menu.- .cpp
void ATutorialMenuHUD::OpenLobbyMenu()
{
...
PartyMenu->RefreshPartyList();
}Also, in the
TutorialMenuHUD
class, create a new function to refresh the party entries on closing the lobby menu.- .cpp
- .h
void ATutorialMenuHUD::OnCloseLobbyMenu()
{
PartyMenu->RefreshPartyEntries();
CloseLobbyMenu();
}/**
* @brief Refreshes Party entries and close lobby menu
*/
void OnCloseLobbyMenu();Back to
AccelByteLobby.cpp
, call theOnCloseLobbyMenu()
you created earlier inside Lobby's notification delegate on the connection closed and disconnected.- .cpp
void UAccelByteLobby::SetLobbyNotificationDelegate()
{
...
IdentityInterface->AddAccelByteOnLobbyConnectionClosedDelegate_Handle(USER_LOCAL_NUM
, FAccelByteOnLobbyConnectionClosedDelegate::CreateLambda([](int32 LocalUserNum, const FUniqueNetId& UserId, int32 StatusCode, const FString& Reason, bool bWasClean)
{
TutorialMenuHUD->OnCloseLobbyMenu();
}));
...
}
Congratulations! You have now fully implemented AGS Party.
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()
{
SessionInterface->AddOnJoinSessionCompleteDelegate_Handle( FOnJoinSessionCompleteDelegate::CreateLambda([](FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (SessionName == NAME_PartySession)
{
UE_LOG(LogTemp, Log, TEXT("Join Party Success!"));
}
}));
// Join the party session
FOnlineSessionInviteAccelByte Invitation; // The invitation from the notification
SessionInterface->JoinSession(*LocalPlayerId.Get(), NAME_PartySession, Invitation.Session);
}
void UAccelBytePartyInvitationPopUp::OnClickedRejectPartyInvitation()
{
FOnRejectSessionInviteComplete OnRejectSessionInviteComplete = FOnRejectSessionInviteComplete::CreateLambda([](const bool bWasSuccessful)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Reject Party Success!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Reject Party Error! %d: %s"), ErrorCode, *ErrorMessage);
}
});
FOnlineSessionInviteAccelByte Invitation; // The invitation from the notification
SessionInterface->RejectInvite(LocalPlayerId.ToSharedRef().Get(), Invitation, OnRejectSessionInviteComplete);
}
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 the 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 convert to FSimpleUserData in here.
*/
void InitData(const FString& PlayerId);
/**
* @brief Enabling button to Promote Leader and Kick Party member.
* @param bEnable Enable all button components.
*/
void EnableAllComponents(bool bIsEnable) const;
/**
* @brief Set Image Icon for 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 for when party member is promoted to leader.
*/
void OnPartyPromoteLeaderResponse(const FAccelByteModelsPartyPromoteLeaderResponse& Result);
/**
* @brief Response fpr when kicked from party.
*/
void OnInvitePartyKickMemberResponse(const FAccelByteModelsKickPartyMemberResponse& Result);
/**
* @brief Callback for successfully getting 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;
IdentityInterface->GetApiClient(USER_LOCAL_NUM)->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()
{
FOnPromotePartySessionLeaderComplete OnPromotePartySessionLeaderComplete =
FOnPromotePartySessionLeaderComplete::CreateLambda([](const FUniqueNetId &PromotedUserId, const FOnlineError &Result)
{
if (Result.bSucceeded)
{
UE_LOG(LogTemp, Log, TEXT("Promote Party Leader Success!"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("Promote Party Leader Failed!"));
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, "Unable to Promote Party Leader!");
}
});
SessionInterface->PromotePartySessionLeader(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, FUniqueNetIdAccelByteUser::Create(UserData.UserId).Get(), OnPromotePartySessionLeaderComplete);
}
void UAccelBytePartyPlayerEntry::OnClickedKickParty()
{
SessionInterface->KickPlayer(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, FUniqueNetIdAccelByteUser::Create(UserData.UserId).Get());
}
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 AGS features including:
*
* - Create Party
* - Leave Party
* - Invite to Party
* - Get Notification when leaving party
* - Get Notification when joining 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 to 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 getting notification for invitation to party.
*/
void OnInvitePartyGetInviteNotice(const FAccelByteModelsPartyGetInvitedNotice& Result);
/**
* @brief Response for notification when someone kicked from the party.
*/
void OnInvitePartyKickedNotice(const FAccelByteModelsGotKickedFromPartyNotice& Result);
/**
* @brief Response for notification when someone leaves party.
*/
void OnLeavePartyNotice(const FAccelByteModelsLeavePartyNotice& Result);
/**
* @brief Response for updating party data when someone does a party-related action.
*/
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()
{
FOnlineSessionSettings NewSessionSettings;
NewSessionSettings.NumPrivateConnections = 4;
NewSessionSettings.Set(SETTING_SESSION_JOIN_TYPE, TEXT("INVITE_ONLY"));
NewSessionSettings.Set(SETTING_SESSION_TYPE, SETTING_SESSION_TYPE_PARTY_SESSION);
NewSessionSettings.Set(SETTING_SESSION_TEMPLATE_NAME, TEXT("TemplateNameFromAdminPortal"));
SessionInterface->CreateSession(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, NewSessionSettings);
}
void UAccelByteParty::OnClickedLeaveParty()
{
auto PartySession = SessionInterface->GetNamedSession(NAME_PartySession);
auto PartySessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2> (PartySession->SessionInfo);
FString PartyId = PartySessionInfo->GetBackendSessionDataAsPartySession()->ID;
SessionInterface->LeaveSession(LocalPlayerId.ToSharedRef().Get(), EAccelByteV2SessionType::PartySession, PartyId, OnLeavePartyComplete);
}
void UAccelByteParty::OnClickedInviteParty(const FString& UserId)
{
SessionInterface->SendSessionInviteToFriend(LocalPlayerId.ToSharedRef().Get(), NAME_PartySession, FUniqueNetIdAccelByteUser::Create(UserId).Get());
}
void UAccelByteParty::SetNotificationDelegate()
{
SessionInterface->OnCreateSessionCompleteDelegates.AddUObject(this, &UAccelByteParty::OnCreateSessionComplete);
SessionInterface->OnDestroySessionCompleteDelegates.AddUObject(this, &UAccelByteParty::OnLeaveSessionComplete);
SessionInterface->OnSendSessionInviteCompleteDelegates.AddUObject(this, &UAccelByteParty::OnSendSessionInviteComplete);
SessionInterface->OnV2SessionInviteReceivedDelegates.AddUObject(this, &UAccelByteParty::OnSessionInviteReceived);
SessionInterface->OnKickedFromSessionDelegates.AddUObject(this, &UAccelByteParty::OnKickedFromParty);
SessionInterface->OnSessionParticipantsChangeDelegates.AddUObject(this, &UAccelByteParty::OnPartyMembersChange);
SessionInterface->OnSessionUpdateReceivedDelegates.AddUObject(this, &UAccelByteParty::OnPartySessionUpdateReceived);
}
void UAccelByteParty::CreatePartyList()
{
// Clear any entries when going back and forth in 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 the local player is the 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 to Party Success!"));
}
void UAccelByteParty::OnInvitePartyFailed(int32 ErrorCode, const FString& ErrorMessage)
{
const FString DebugMessage = FString::Printf(TEXT("Invite to Party Failed: %d , %s"), ErrorCode, *ErrorMessage);
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Cyan, DebugMessage);
UE_LOG(LogTemp, Warning, TEXT("Invite to 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 the pop-up widget **/
const TWeakObjectPtr<UAccelBytePartyInvitationPopUp> InvitationPopUpWidget = MakeWeakObjectPtr<UAccelBytePartyInvitationPopUp>(
CreateWidget<UAccelBytePartyInvitationPopUp>(this, PartyInvitationPopUpClass.Get()));
/** Initialize pop-up 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 is 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 Initialize the construct function on the first time the main menu opens;
*/
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 "OnlineIdentityInterfaceAccelByte.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 AGS 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 "OnlineIdentityInterfaceAccelByte.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();
IdentityInterface->ConnectAccelByteLobby(USER_LOCAL_NUM);
}
void UAccelByteLobby::AddPartyChild(UAccelByteParty* PartyMenu) const
{
Sb_Party->AddChild(PartyMenu);
}
void UAccelByteLobby::SetLobbyNotificationDelegate()
{
}
#pragma endregion