Unity SDK を使用してパーティサービスを実装する
Quick Reference
This topic is specific to the AccelByte Gaming Services (AGS) Shared Cloud tier.
References
using AccelByte.Api;
using AccelByte.Core;
using AccelByte.Models;
Get Lobby Service
Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
Party Notification Events
lobby.InvitedToParty += result => {};
lobby.JoinedParty += result => {};
lobby.KickedFromParty += result =>{};
lobby.LeaveFromParty += result =>{};
lobby.RejectedPartyInvitation += result =>{};
Create Party
ResultCallback<PartyInfo> createPartyCallback = result =>
{
// If there is no error, display the success information
if (!result.IsError)
{
Debug.Log("Successfully create a party");
}
};
lobby.CreateParty(createPartyCallback);
Invite to Party
string userId;
lobby.InviteToParty(userId, result =>
{
if (!result.IsError)
{
Debug.Log("Successfully invite an invitee");
}
});
Kick from Party
string userId;
lobby.KickPartyMember(userId, result =>
{
if (result.IsError)
{
Debug.Log($"Successfully kick member from party");
}
});
Leave Party
lobby.LeaveParty(result =>
{
// If there is no error, display the success information
if (!result.IsError)
{
Debug.Log("Successfully leave from party");
}
});
Promote to Party Leader
string userId;
lobby.PromotePartyLeader(userId, result =>
{
if (!result.IsError)
{
Debug.Log("Successfully promoted member to be a party leader");
}
});
Join Party
PartyInvitation partyInvitation;
lobby.JoinParty(partyInvitation.partyID, partyInvitation.invitationToken, result =>
{
if (!result.IsError)
{
Debug.Log("Successfully joined the party");
}
});
Reject Party Invitation
PartyInvitation partyInvitation;
lobby.RejectPartyInvitation(partyInvitation.partyID, partyInvitation.invitationToken, result =>
{
if (result.IsError)
{
Debug.Log("Successfully rejected the party invitation");
}
});
Get Users' Info
string[] usersIdList;
AccelByteSDK.GetClientRegistry().GetApi().GetUser().BulkGetUserInfo(usersIdList, result => {
// Loop the result's Users Data, return in BaseUserInfo
foreach (BaseUserInfo user in result.Value.data)
{
// Do something with the user data
}
});
Quickstart Guide
In this tutorial, you will learn how to use Party services. This guide assumes that you have already implemented the Lobby and Friends services.
Since Party implementation can vary for each game, you can familiarize yourself with other concepts and classes in the LobbyModels.cs
file inside the plugin SDK.
You will start by adding simple party logic into the game.
Create a new script called
PartyHandler.cs
and attach it to theAccelByteHandler
gameObject.Add the following AccelByte libraries to the top of the script:
using UnityEngine;
using AccelByte.Api;
using AccelByte.Models;
using AccelByte.Core;Add some basic Party functionality in
PartyHandler.cs
so you can call these later from your UI:- Create a Party This function will return a result from the class type
PartyInfo
which contains Party data such aspartyID
, the leader'sleaderID
, a list of the party members'userIDs
, a list of the invitees'userIDs
, and the party invitation'sinvitationToken
.
public void CreateParty()
{
ResultCallback<PartyInfo> createPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to create party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully create a party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().CreateParty(createPartyCallback);
}If you want to save the
PartyInfo
of the created party, you can set the value based onresult.Value
.if (result.IsError)
...
else
{
...
partyInfo = result.Value;
}- Invite Friend to Party This function requires the
UserID
of the player you want to invite in order to send an invitation, so addinviteeUserId
as the function's parameter.
public void InviteToParty(string inviteeUserId)
{
ResultCallback inviteToPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to invite user to party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully invite an invitee");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().InviteToParty(inviteeUserId, inviteToPartyCallback);
}- Kick Friend from Party This function requires the
UserID
of the party member you want to kick from the party, so addmemberUserId
as the function's parameter.
public void KickParty(string memberUserId)
{
ResultCallback kickPartyMemberCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to kick user from party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log($"Successfully kick member {memberUserId} from party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().KickPartyMember(memberUserId, kickPartyMemberCallback);
}- Leave from Party Use this function to leave a party.
public void LeaveParty()
{
ResultCallback leavePartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to leave party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully leave from party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().LeaveParty(leavePartyCallback);
}- Promote Friend to Party Leader This function requires the
UserID
of the party member you want to promote to party leader, so addmemberUserId
as the function's parameter.
public void PromotePartyLeader(string memberUserId)
{
ResultCallback<PartyPromoteLeaderResponse> promotePartyLeaderCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to promote member to be a party leader: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully promote member to be a party leader");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().PromotePartyLeader(memberUserId, promotePartyLeaderCallback);
}- Create a Party This function will return a result from the class type
Most of the party invitation's UI will be a spawnable prefab. Create a new script called
PartyInvitationPanel.cs
and add the following to the top:using UnityEngine;
using AccelByte.Core;
using AccelByte.Models;This script will hold functions that handle the Party Invitation's related events. You can attach this in your Party Invitation prefab later on.
Add some functions to handle party invitations in
PartyInvitationPanel.cs
:- Accept Party Invitation (Join Party) This function requires the
UserID
of the player you want to invite in order to send an invitation to that player, so addinviteeUserId
as the function's parameter.
public void JoinParty(PartyInvitation partyInvitation)
{
ResultCallback<PartyInfo> joinPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to join party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully join the party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().JoinParty(partyInvitation.partyID, partyInvitation.invitationToken, joinPartyCallback);
}- Reject Party Invitation This function requires the
UserID
of the party member you want to kick from the party, so addmemberUserId
as the function's parameter.
public void RejectPartyInvitation(PartyInvitation partyInvitation)
{
ResultCallback<PartyRejectResponse> rejectPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to reject party invitation: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully reject the party invitation");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().RejectPartyInvitation(partyInvitation.partyID, partyInvitation.invitationToken, rejectPartyCallback);
}You can use the result value from the
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().InvitedToParty
event for this PartyInvitation value.- Accept Party Invitation (Join Party) This function requires the
To send a notification to a player on any activity related to the current party, find
LobbyHandler.cs
and add this under theConnectToLobby()
function. For now, we will just add aDebug.Log
for each event.lobby.InvitedToParty += result => { Debug.Log($"Invited by: {result.Value.from}"); };
lobby.JoinedParty += result => { Debug.Log("Invitee join a party"); };
lobby.KickedFromParty += result => { Debug.Log("You're kicked from party"); };
lobby.LeaveFromParty += result => { Debug.Log($"{result.Value.userID} leave the party"); };
lobby.RejectedPartyInvitation += result => { Debug.Log("Invitee rejected a party invitation"); };You may wish to retrieve Party data. There are a few ways you can do this.
If you want to retrieve Party data every time the data is updated, use this event:
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().PartyDataUpdateNotif += result => {}
This event will return a result with the class type
PartyDataUpdateNotif
which contains Party data such asPartyInfo
.PartyDataUpdateNotif
does not have aninvitationToken
, but does haveupdatedAt
which indicates when the Party data is updated, andcustom_attribute
which is a Dictionary that you can use with any custom info.As with the last step, you can find this event in
LobbyHandler.cs
under theConnectToLobby()
function. In this case, since we only need to display Party data, just add aDebug.Log
to notify the party member'sUserID
via the Console output.lobby.PartyDataUpdateNotif += result =>{ Debug.Log($"Current Party Members: {result.Value.members}");}
You can also retrieve Party data using the
GetPartyInfo()
function. ThePartyInfo
class contains Party data such as thepartyID
,leaderID
, members (UserID
), invitees (UserID
), and aninvitationToken
. To get thisPartyInfo
, use the following function:ResultCallback<PartyInfo> getPartyInfoCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to leave party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully left party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().GetPartyInfo(getPartyInfoCallback);This function returns a result with the class type
PartyInfo
, and you can access its values withresult.Value.<the object you need>
. For example, if you want to retrieve thepartyID
, useresult.Value.partyId
.TIPYou can only retrieve UserID from
PartyDataUpdateNotif
andPartyInfo
, so you will need to use theGetUser()
function to retrieve user data.AccelByteSDK.GetClientRegistry().GetApi().GetUser().BulkGetUserInfo(usersIdList, result => {
// Loop the result's Users Data, return in BaseUserInfo
foreach (BaseUserInfo user in result.Value.data)
{
Debug.Log($"User data: {user.displayName}");
}
});
AccelByteSDK.GetClientRegistry().GetApi().GetUser().GetUserByUserId(userId, result => {
Debug.Log($"User data: {result.Value}");
});Depending on the situation, when displaying Party data, you may want to use
BulkGetUserInfo()
which also containsavatarUrl
.
Congratulations! You have successfully learnt how to use the Party service!
Continue on for a step-by-step example of the UI and code implementation.
Step-by-step guide
UI Implementation
The Party service is part of the Lobby, so start by creating a new panel or scene for the Lobby and add the following objects:
- Header text
- Exit button
- Empty panel, for the Lobby Page content (optional)
The Lobby panel will have more than just Party UIs, so split the Lobby panel with two new panels. Once completed, add a Friends Management button to redirect players to the Friends panel.
Add Party-related UI objects including:
- Party ID text
- Create Party button
- Party Member List panel
- Player Entry panel (since the default party size is four players, create four panels and parent them to the Party Member List panel)
- Leave Party button
Create a pop-up or panel for the Party Invitation and ensure that it has the following UI elements:
- Invitation text
- Accept button
- Reject button
You will also need a notification box for log messages, so create a new panel and add the following:
- Sub-header text
- Scroll View
- Log Message Display prefab
The Log Message Display prefab will spawn if a log message is created. Create a new panel, ensure it has a Log Message text, and link it under the content of the Scroll View.
Code Implementation
Now that you have some basic Party functionality, you can implement these into your UI.
Inside
PartyHandler.cs
, add Unity Engine's UI library:using UnityEngine;
using UnityEngine.UI;Add references to your Party UI:
[SerializeField]
private GameObject LobbyWindow;
[SerializeField]
private Transform canvasTransform;
[SerializeField]
private Transform partyDisplayPanel;
[SerializeField]
private GameObject partyInvitationPrefab;
#region Buttons
[SerializeField]
private Button friendsManagementButton;
[SerializeField]
private Button createPartyButton;
[SerializeField]
private Button leavePartyButton;
[SerializeField]
private Button exitButton;
#endregion
[SerializeField]
private Text partyIdText;Create a new dictionary that will save your Party members' User IDs and display names.
public Dictionary<string, string> partyMembers { private set; get; }
Since
FriendsPanel
can be called from both the Menu and the Lobby, create a new enum for an exit mode. InFriendsManagementHandler.cs
, create a new enum with its getter and setter.#region ExitMode
public enum ExitMode
{
Menu,
Lobby
}
private ExitMode ExitScreen
{
get => ExitScreen;
set
{
switch (value)
{
case ExitMode.Menu:
FriendsManagementWindow.SetActive(false);
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
break;
case ExitMode.Lobby:
FriendsManagementWindow.SetActive(false);
GetComponent<LobbyHandler>().LobbyWindow.SetActive(true);
break;
}
}
}
#endregionTo avoid the listener being set up twice, add a new boolean as a flag checker in
FriendsManagementHandler.cs
, and then change theSetup()
function:public void Setup(ExitMode exitType)
{
// Reset the exit button's listener, then add the listener based on the exit screen type
exitButton.onClick.RemoveAllListeners();
exitButton.onClick.AddListener(() =>
{
...
ExitScreen = exitType;
});
}Create a function in
PartyHandler.cs
to set up your Party UI when theLobbyPanel
is set to active./// Setup Party UI in Lobby and prepare state
public void SetupParty()
{
friendsManagementButton.onClick.AddListener(() =>
{
GetComponent<FriendsManagementHandler>().Setup(FriendsManagementHandler.ExitMode.Lobby);
LobbyWindow.SetActive(false);
GetComponent<FriendsManagementHandler>().FriendsManagementWindow.SetActive(true);
});
createPartyButton.onClick.AddListener(() => { CreateParty(); });
leavePartyButton.onClick.AddListener(() => { LeaveParty(); });
exitButton.onClick.AddListener(() =>
{
LobbyWindow.SetActive(false);
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
});
}Before you continue with
PartyHandler.cs
, connect theLobbyPanel
with theMenuPanel
'sLobbyButton
. InMenuHandler.cs
, add a reference for theLobbyButton
.public Button LobbyButton;
Inside the
Create()
function, add theLobbyButton
's event listener and change theFriendsButton
'sSetup()
:public void Create()
{
LobbyButton.onClick.AddListener(() =>
{
GetComponent<PartyHandler>().SetupParty();
Menu.gameObject.SetActive(false);
GetComponent<LobbyHandler>().LobbyWindow.SetActive(true);
});
FriendsButton.onClick.AddListener(() =>
{
GetComponent<FriendsManagementHandler>().Setup(FriendsManagementHandler.ExitMode.Menu);
...
});
}In the Unity Editor, on the
MenuHandler.cs
script component in theAccelByteHandler
game object, drag the following objects to the appropriate variables.Go to
FriendStatusPanel.cs
and add aninviteToPartyButton
listener in theSetupButton()
function. Use theLobbyHandler
's instance to getPartyHandler
without needing to use the Find function.public void SetupButton()
{
inviteToPartyButton.onClick.AddListener(() =>
{
LobbyHandler.Instance.GetComponent<PartyHandler>().InviteToParty(_userData.userId);
});
...
}Update your
PartyInvitationPanel.cs
for when thePartyInvitationPopup
is spawned, such as in the following example:// Add some UI references
[SerializeField]
private GameObject invitationPopUp;
[SerializeField]
private Text invitationText;
[SerializeField]
private Button acceptInvitationButton;
[SerializeField]
private Button rejectInvitationButton;
// Create a function to setup the Popup and its listener
public void Setup(PartyInvitation partyInvitation)
{
AccelByteSDK.GetClientRegistry().GetApi().GetUser().GetUserByUserId(partyInvitation.from, result =>
{
invitationText.text = result.Value.displayName + " invite you to join their party\nAccept invitation?";
});
acceptInvitationButton.onClick.AddListener(() => { JoinParty(partyInvitation);});
rejectInvitationButton.onClick.AddListener(() => { RejectPartyInvitation(partyInvitation);});
}Add the following in
PartyInvitationPanel.cs
to destroy thePartyInvitationPopup
after the player accepts or rejects an invitation:public void JoinParty(PartyInvitation partyInvitation)
{
...
// Destroy the PopUp prefab
Destroy(invitationPopUp);
}
public void RejectPartyInvitation(PartyInvitation partyInvitation)
{
...
// Destroy the PopUp prefab
Destroy(invitationPopUp);
}In the Unity editor, open the
PartyInvitationPopup
prefab. Add thePartyInvitationPanels.cs
script as a component, then drag the objects to their exposed variables.In
PartyHandler.cs
, create a new function to Display Party data to thePartyListPanel
using theBulkGetUserInfo()
function to retrieve player data./// Display all Party Data to PartyList UI
public void DisplayPartyData(Result<PartyDataUpdateNotif> partyDataNotifResult)
{
// Update PartyID in UI
partyIdText.text = "PartyID: " + partyDataNotifResult.Value.partyId;
// Get all party members data based on _partyUserIds, then update data to UI
AccelByteSDK.GetClientRegistry().GetApi().GetUser().BulkGetUserInfo(partyDataNotifResult.Value.members, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to get party member's data: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
// Initialize dictionary
partyMembers = new Dictionary<string, string>();
// Result data's order => reversed order of _partyIserIds
int _index = result.Value.data.Length;
foreach (BaseUserInfo user in result.Value.data)
{
_index -= 1;
// Get transform of PlayerEntryDisplay, which is child of PartyListPanel
Transform playerEntryDisplay = partyDisplayPanel.GetChild(_index).transform;
if (user.userId == partyDataNotifResult.Value.leader)
{
// Set LeaderStatusIndicator as active
Transform leaderStatusIndicator = playerEntryDisplay.GetChild(0).transform;
leaderStatusIndicator.gameObject.SetActive(true);
}
else
{
if (AccelByteSDK.GetClientRegistry().GetApi().GetUser().Session.UserId == partyDataNotifResult.Value.leader)
{
// Set PartyLeaderButton (promote button) as active, then add listener when onclick button
Transform partyLeaderButton = playerEntryDisplay.GetChild(1).transform;
partyLeaderButton.gameObject.SetActive(true);
partyLeaderButton.GetComponent<Button>().onClick.AddListener(() =>
{
PromotePartyLeader(user.userId);
});
// Set KickPartyButton as active, then add listener when onclick button
Transform kickPartyButton = playerEntryDisplay.GetChild(2).transform;
kickPartyButton.gameObject.SetActive(true);
kickPartyButton.GetComponent<Button>().onClick.AddListener(() =>
{
KickParty(user.userId);
});
}
}
// Set DisplayNameText as active, then change text to User's Display Name
Transform displayNameText = playerEntryDisplay.GetChild(3).transform;
displayNameText.gameObject.SetActive(true);
displayNameText.GetComponent<Text>().text = user.displayName;
partyMembers.Add(user.userId, user.displayName);
}
}
});
}You may want to display Party Member data if the Lobby is still connected. You can do this by adding the
Start()
function and then add Display Party data functionality usingGetPartyInfo()
.private void Start()
{
Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
if (lobby.IsConnected)
{
lobby.GetPartyInfo(partyInfoResult =>
{
// Update PartyID in the UI
partyIdText.text = "PartyID: " + partyInfoResult.Value.partyID;
ResetPlayerEntryUI();
// Get all party members data based on _partyUserIds, then update data in the UI
AccelByteSDK.GetClientRegistry().GetApi().GetUser().BulkGetUserInfo(partyInfoResult.Value.members, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to get party member's data: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
// Initialize dictionary
partyMembers = new Dictionary<string, string>();
// Result data's order => reversed order of _partyIserIds
int index = result.Value.data.Length;
foreach (BaseUserInfo user in result.Value.data)
{
index -= 1;
// Get transform of PlayerEntryDisplay, which is child of PartyListPanel
Transform playerEntryDisplay = partyDisplayPanel.GetChild(index).transform;
if (user.userId == partyInfoResult.Value.leaderID)
{
// Set LeaderStatusIndicator as active
Transform leaderStatusIndicator = playerEntryDisplay.GetChild(0).transform;
leaderStatusIndicator.gameObject.SetActive(true);
}
else
{
if (AccelByteSDK.GetClientRegistry().GetApi().GetUser().Session.UserId == partyInfoResult.Value.leaderID)
{
// Set PartyLeaderButton (promote button) as active, then add listener when onclick button
Transform partyLeaderButton = playerEntryDisplay.GetChild(1).transform;
partyLeaderButton.gameObject.SetActive(true);
partyLeaderButton.GetComponent<Button>().onClick.AddListener(() =>
{
PromotePartyLeader(user.userId);
});
// Set KickPartyButton as active, then add listener when onclick button
Transform kickPartyButton = playerEntryDisplay.GetChild(2).transform;
kickPartyButton.gameObject.SetActive(true);
kickPartyButton.GetComponent<Button>().onClick.AddListener(() =>
{
KickParty(user.userId);
});
}
}
// Set DisplayNameText as active, then change text to User's Display Name
Transform displayNameText = playerEntryDisplay.GetChild(3).transform;
displayNameText.gameObject.SetActive(true);
displayNameText.GetComponent<Text>().text = user.displayName;
partyMembers.Add(user.userId, user.displayName);
}
}
});
});
}
}Prepare some Reset functions to reset the Party data and UI in
PartyHandler.cs
:/// Reset Party ID's UI
public void ResetPartyId()
{
partyIdText.text = "PartyID: ###############################";
partyMembers = null;
}
/// Reset Party List's UI
public void ResetPlayerEntryUI()
{
foreach(Transform playerEntryDisplay in partyDisplayPanel)
{
// Set LeaderStatusIndicator as not active
Transform leaderStatusIndicator = playerEntryDisplay.GetChild(0).transform;
leaderStatusIndicator.gameObject.SetActive(false);
// Set PartyLeaderButton (promote button) as not active, then remove all listener on button
Transform partyLeaderButton = playerEntryDisplay.GetChild(1).transform;
partyLeaderButton.gameObject.SetActive(false);
partyLeaderButton.GetComponent<Button>().onClick.RemoveAllListeners();
// Set KickPartyButton as not active, then remove all listener on button
Transform kickPartyButton = playerEntryDisplay.GetChild(2).transform;
kickPartyButton.gameObject.SetActive(false);
kickPartyButton.GetComponent<Button>().onClick.RemoveAllListeners();
// Set DisplayNameText as not active and set value to default text
Transform displayNameText = playerEntryDisplay.GetChild(3).transform;
displayNameText.gameObject.SetActive(false);
displayNameText.GetComponent<Text>().text = "PlayerUsername";
}
}The Party UI must be reset when a player leaves their current party, when displaying data, or on a player's first connection to the Lobby. To do this, call the Reset function in
PartyHandler.cs
:public void LeaveParty()
{
ResultCallback leavePartyCallback = result =>
{
if (result.IsError)
{
...
}
else
{
...
// Reset all Party-related UIs
ResetPartyId();
ResetPlayerEntryUI();
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().LeaveParty(leavePartyCallback);
}
public void DisplayPartyData(Result<PartyDataUpdateNotif> partyDataNotifResult)
{
// Udate PartyID in UI
...
// Reset the Party List's UI
ResetPlayerEntryUI();
...
}You will need the notification display to appear in the notification box. To do this, create a new script called
LogMessagePanel.cs
and attach it to theLogMessageDisplay
prefab. Once completed, add the following to the script:using UnityEngine;
using UnityEngine.UI;
public class LogMessagePanel : MonoBehaviour
{
[SerializeField]
private Text messageText;
/// Update Notification Message's UI
public void UpdateNotificationUI(string text, Color color)
{
messageText.text = text;
messageText.color = color;
}
}In
LobbyHandler.cs
, add the following UI references:public GameObject LobbyWindow;
#region Notification Box
[Header("Notification Box")]
[SerializeField]
private Transform notificationBoxContentView;
[SerializeField]
private GameObject logMessagePrefab;
#endregionAdd the following function in
LobbyHandler.cs
. This function will be called to instantiate the log message in the notification box./// Write the log message on the notification box
public void WriteLogMessage(string text, Color color)
{
LogMessagePanel logPanel = Instantiate(logMessagePrefab, notificationBoxContentView).GetComponent<LogMessagePanel>();
logPanel.UpdateNotificationUI(text, color);
}In the Unity Editor, on your
AccelByteHandler
, drag the following objects to the exposed variables.In
PartyHandler.cs
, create functions to handle each update event inLobbyHandler.cs
and to create notification messages./// Called on update when a party invitation is received
public void InvitePartyNotification(PartyInvitation partyInvitation)
{
Debug.Log($"Invited by: {partyInvitation.from}");
PartyInvitationPanel invitationPanel = Instantiate(partyInvitationPrefab, canvasTransform).GetComponent<PartyInvitationPanel>();
invitationPanel.Setup(partyInvitation);
}
/// Called on update when kicked from party
public void KickPartyNotification()
{
Debug.Log("You're kicked from party");
ResetPartyId();
ResetPlayerEntryUI();
}
/// Called on update when a friend joins the party
public void JoinedPartyNotification(JoinNotification joinNotification)
{
Debug.Log("Invitee join a party");
AccelByteSDK.GetClientRegistry().GetApi().GetUser().GetUserByUserId(joinNotification.userID, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to get user data: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
LobbyHandler.Instance.WriteLogMessage($"[Party] {result.Value.displayName} join the party", Color.black);
}
});
}
/// Called on update when a friend leaves the party
public void LeavePartyNotification(LeaveNotification leaveNotification)
{
if (leaveNotification.userID != AccelByteSDK.GetClientRegistry().GetApi().GetUser().Session.UserId)
{
Debug.Log($"{leaveNotification.userID} leave the party");
LobbyHandler.Instance.WriteLogMessage($"[Party] {partyMembers[leaveNotification.userID]} leave the party", Color.black);
}
}While still in
PartyHandler.cs
, add the following code under thePromotePartyLeader()
function to notify players whether their Promote Party Leader action succeeds or fails.public void PromotePartyLeader(string memberUserId)
{
// Instantiate the notification message
NotificationMessagePanel notificationPanel = Instantiate(notificationMessagePrefab, notificationContentView).GetComponent<NotificationMessagePanel>();
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().PromotePartyLeader(memberUserId, result =>
{
if (result.IsError)
{
...
// Update the messageText that failed to promote leader
LobbyHandler.Instance.WriteLogMessage($"[Party] Failed to promote {partyMembers[memberUserId]} to be party leader", Color.black);
}
else
{
...
// Update the messageText that success to promote leader
LobbyHandler.Instance.WriteLogMessage($"[Party] Successfully promote {partyMembers[memberUserId]} to be party leader", Color.black);
}
});
}Create new functions in
NotificationHandler.cs
that will be called from the notifications events when there is an update.// Collection of party notifications
#region Party
/// Called when user gets party invitation
public void OnInvitedToParty(Result<PartyInvitation> result)
{
GetComponent<PartyHandler>().InvitePartyNotification(result.Value);
}
/// Called when user joins the party
public void OnJoinedParty(Result<JoinNotification> result)
{
GetComponent<PartyHandler>().JoinedPartyNotification(result.Value);
}
/// Called when user is kicked by party leader
public void OnKickedFromParty(Result<KickNotification> result)
{
GetComponent<PartyHandler>().KickPartyNotification();
}
/// Called when user leaves the party
public void OnLeaveFromParty(Result<LeaveNotification> result)
{
GetComponent<PartyHandler>().LeavePartyNotification(result.Value);
}
/// Called when user rejects party invitation
public void OnRejectedPartyInvitation(Result<PartyRejectNotif> result)
{
Debug.Log("[Party-Notification] Invitee rejected a party invitation");
}
/// Called when party data is updated
public void OnPartyDataUpdateNotif(Result<PartyDataUpdateNotif> result)
{
GetComponent<PartyHandler>().DisplayPartyData(result);
}
#endregionChange your notification update events in
LobbyHandler.cs
under theConnectToLobby()
function, such as in the following example:public void ConnectToLobby()
{
...
// Party
lobby.InvitedToParty += notificationHandler.OnInvitedToParty;
lobby.JoinedParty += notificationHandler.OnJoinedParty;
lobby.KickedFromParty += notificationHandler.OnKickedFromParty;
lobby.LeaveFromParty += notificationHandler.OnLeaveFromParty;
lobby.RejectedPartyInvitation += notificationHandler.OnRejectedPartyInvitation;
lobby.PartyDataUpdateNotif += notificationHandler.OnPartyDataUpdateNotif;
...
}Update the
RemoveLobbyListeners()
function inLobbyHandler.cs
to reset the notification update events, such as in the following example:public void RemoveLobbyListeners()
{
...
// Party
lobby.InvitedToParty -= notificationHandler.OnInvitedToParty;
lobby.JoinedParty -= notificationHandler.OnJoinedParty;
lobby.KickedFromParty -= notificationHandler.OnKickedFromParty;
lobby.LeaveFromParty -= notificationHandler.OnLeaveFromParty;
lobby.RejectedPartyInvitation -= notificationHandler.OnRejectedPartyInvitation;
lobby.PartyDataUpdateNotif -= notificationHandler.OnPartyDataUpdateNotif;
}Save and return to the Unity Editor. In your scene, on the
AccelByteHandler
gameObject, drag the appropriate objects into the exposed variables of thePartyHandler
script component.
Congratulations! You have now fully implemented the Party service.
Full code for reference
PartyHandler.cs
using UnityEngine;
using UnityEngine.UI;
using AccelByte.Core;
using AccelByte.Models;
using AccelByte.Api;
using System.Collections.Generic;
public class PartyHandler : MonoBehaviour
{
[SerializeField]
private GameObject LobbyWindow;
[SerializeField]
private Transform canvasTransform;
[SerializeField]
private Transform partyDisplayPanel;
[SerializeField]
private GameObject partyInvitationPrefab;
#region Buttons
[SerializeField]
private Button friendsManagementButton;
[SerializeField]
private Button createPartyButton;
[SerializeField]
private Button leavePartyButton;
[SerializeField]
private Button exitButton;
#endregion
[SerializeField]
private Text partyIdText;
public Dictionary<string, string> partyMembers
{
private set; get;
}
/// <summary>
/// Display all Party Data to PartyList UI
/// </summary>
/// <param name="partyDataNotifResult"> </param>
public void DisplayPartyData(Result<PartyDataUpdateNotif> partyDataNotifResult)
{
// Update PartyID in UI
partyIdText.text = "PartyID: " + partyDataNotifResult.Value.partyId;
// Reset the Party List's UI
ResetPlayerEntryUI();
// Get all party members data based on _partyUserIds, then update data to UI
AccelByteSDK.GetClientRegistry().GetApi().GetUser().BulkGetUserInfo(partyDataNotifResult.Value.members, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to get party member's data: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
// Initialize dictionary
partyMembers = new Dictionary<string, string>();
// Result data's order => reversed order of _partyIserIds
int index = result.Value.data.Length;
foreach (BaseUserInfo user in result.Value.data)
{
index -= 1;
// Get transform of PlayerEntryDisplay, which is child of PartyListPanel
Transform playerEntryDisplay = partyDisplayPanel.GetChild(index).transform;
if (user.userId == partyDataNotifResult.Value.leader)
{
// Set LeaderStatusIndicator as active
Transform leaderStatusIndicator = playerEntryDisplay.GetChild(0).transform;
leaderStatusIndicator.gameObject.SetActive(true);
}
else
{
if (AccelByteSDK.GetClientRegistry().GetApi().GetUser().Session.UserId == partyDataNotifResult.Value.leader)
{
// Set PartyLeaderButton (promote button) as active, then add listener when onclick button
Transform partyLeaderButton = playerEntryDisplay.GetChild(1).transform;
partyLeaderButton.gameObject.SetActive(true);
partyLeaderButton.GetComponent<Button>().onClick.AddListener(() =>
{
PromotePartyLeader(user.userId);
});
// Set KickPartyButton as active, then add listener when onclick button
Transform kickPartyButton = playerEntryDisplay.GetChild(2).transform;
kickPartyButton.gameObject.SetActive(true);
kickPartyButton.GetComponent<Button>().onClick.AddListener(() =>
{
KickParty(user.userId);
});
}
}
// Set DisplayNameText as active, then change text to User's Display Name
Transform displayNameText = playerEntryDisplay.GetChild(3).transform;
displayNameText.gameObject.SetActive(true);
displayNameText.GetComponent<Text>().text = user.displayName;
partyMembers.Add(user.userId, user.displayName);
}
}
});
}
/// <summary>
/// Setup Party UI in Lobby and prepare state
/// </summary>
public void SetupParty()
{
friendsManagementButton.onClick.AddListener(() =>
{
GetComponent<FriendsManagementHandler>().Setup(FriendsManagementHandler.ExitMode.Lobby);
LobbyWindow.SetActive(false);
GetComponent<FriendsManagementHandler>().FriendsManagementWindow.SetActive(true);
});
createPartyButton.onClick.AddListener(() => { CreateParty(); });
leavePartyButton.onClick.AddListener(() => { LeaveParty(); });
exitButton.onClick.AddListener(() =>
{
LobbyWindow.SetActive(false);
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
});
}
/// <summary>
/// Create party and update partyID to UI
/// </summary>
public void CreateParty()
{
ResultCallback<PartyInfo> createPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to create party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully create a party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().CreateParty(createPartyCallback);
}
/// <summary>
/// Invite friend to party
/// </summary>
/// <param name="inviteeUserId"> userId of the user that will received the invitation</param>
public void InviteToParty(string inviteeUserId)
{
ResultCallback inviteToPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to invite user to party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully invite an invitee");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().InviteToParty(inviteeUserId, inviteToPartyCallback);
}
public void KickParty(string memberUserId)
{
ResultCallback kickPartyMemberCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to kick user from party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log($"Successfully kick member {memberUserId} from party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().KickPartyMember(memberUserId, kickPartyMemberCallback);
}
/// <summary>
/// Leave party
/// </summary>
public void LeaveParty()
{
ResultCallback leavePartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to leave party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully leave from party");
// Reset all Party-related UIs
ResetPartyId();
ResetPlayerEntryUI();
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().LeaveParty(leavePartyCallback);
}
/// <summary>
/// Promote member to be a party leader
/// </summary>
/// <param name="memberUserId"> userId of the member that will be promoted as party leader</param>
public void PromotePartyLeader(string memberUserId)
{
ResultCallback<PartyPromoteLeaderResponse> promotePartyLeaderCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to promote member to be a party leader: error code: {result.Error.Code} message: {result.Error.Message}");
// Update the messageText that failed to promote leader
LobbyHandler.Instance.WriteLogMessage($"[Party] Failed to promote {partyMembers[memberUserId]} to be party leader", Color.black);
}
else
{
Debug.Log("Successfully promote member to be a party leader");
// Update the messageText that success to promote leader
LobbyHandler.Instance.WriteLogMessage($"[Party] Successfully promote {partyMembers[memberUserId]} to be party leader", Color.black);
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().PromotePartyLeader(memberUserId, promotePartyLeaderCallback);
}
/// <summary>
/// Reset Party ID's UI
/// </summary>
public void ResetPartyId()
{
partyIdText.text = "PartyID: ###############################";
partyMembers = null;
}
/// <summary>
/// Reset Party List's UI
/// </summary>
public void ResetPlayerEntryUI()
{
foreach (Transform playerEntryDisplay in partyDisplayPanel)
{
// Set LeaderStatusIndicator as not active
Transform leaderStatusIndicator = playerEntryDisplay.GetChild(0).transform;
leaderStatusIndicator.gameObject.SetActive(false);
// Set PartyLeaderButton (promote button) as not active, then remove all listener on button
Transform partyLeaderButton = playerEntryDisplay.GetChild(1).transform;
partyLeaderButton.gameObject.SetActive(false);
partyLeaderButton.GetComponent<Button>().onClick.RemoveAllListeners();
// Set KickPartyButton as not active, then remove all listener on button
Transform kickPartyButton = playerEntryDisplay.GetChild(2).transform;
kickPartyButton.gameObject.SetActive(false);
kickPartyButton.GetComponent<Button>().onClick.RemoveAllListeners();
// Set DisplayNameText as not active and set value to default text
Transform displayNameText = playerEntryDisplay.GetChild(3).transform;
displayNameText.gameObject.SetActive(false);
displayNameText.GetComponent<Text>().text = "PlayerUsername";
}
}
/// <summary>
/// Called on update when a party invitation is received
/// </summary>
public void InvitePartyNotification(PartyInvitation partyInvitation)
{
Debug.Log($"Invited by: {partyInvitation.from}");
PartyInvitationPanel invitationPanel = Instantiate(partyInvitationPrefab, canvasTransform).GetComponent<PartyInvitationPanel>();
invitationPanel.Setup(partyInvitation);
}
/// <summary>
/// Called on update when kicked from party
/// </summary>
public void KickPartyNotification()
{
Debug.Log("You're kicked from party");
ResetPartyId();
ResetPlayerEntryUI();
}
/// <summary>
/// Called on update when a friend joins the party
/// </summary>
public void JoinedPartyNotification(JoinNotification joinNotification)
{
Debug.Log("Invitee join a party");
AccelByteSDK.GetClientRegistry().GetApi().GetUser().GetUserByUserId(joinNotification.userID, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to get user data: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
LobbyHandler.Instance.WriteLogMessage($"[Party] {result.Value.displayName} join the party", Color.black);
}
});
}
/// <summary>
/// Called on update when a friend leaves the party
/// </summary>
public void LeavePartyNotification(LeaveNotification leaveNotification)
{
if (leaveNotification.userID != AccelByteSDK.GetClientRegistry().GetApi().GetUser().Session.UserId)
{
Debug.Log($"{leaveNotification.userID} leave the party");
LobbyHandler.Instance.WriteLogMessage($"[Party] {partyMembers[leaveNotification.userID]} leave the party", Color.black);
}
}
private void Start()
{
Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
if (lobby.IsConnected)
{
lobby.GetPartyInfo(partyInfoResult =>
{
// Update PartyID in the UI
partyIdText.text = "PartyID: " + partyInfoResult.Value.partyID;
ResetPlayerEntryUI();
// Get all party members data based on _partyUserIds, then update data in the UI
AccelByteSDK.GetClientRegistry().GetApi().GetUser().BulkGetUserInfo(partyInfoResult.Value.members, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to get party member's data: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
// Initialize dictionary
partyMembers = new Dictionary<string, string>();
// Result data's order => reversed order of _partyIserIds
int index = result.Value.data.Length;
foreach (BaseUserInfo user in result.Value.data)
{
index -= 1;
// Get transform of PlayerEntryDisplay, which is child of PartyListPanel
Transform playerEntryDisplay = partyDisplayPanel.GetChild(index).transform;
if (user.userId == partyInfoResult.Value.leaderID)
{
// Set LeaderStatusIndicator as active
Transform leaderStatusIndicator = playerEntryDisplay.GetChild(0).transform;
leaderStatusIndicator.gameObject.SetActive(true);
}
else
{
if (AccelByteSDK.GetClientRegistry().GetApi().GetUser().Session.UserId == partyInfoResult.Value.leaderID)
{
// Set PartyLeaderButton (promote button) as active, then add listener when onclick button
Transform partyLeaderButton = playerEntryDisplay.GetChild(1).transform;
partyLeaderButton.gameObject.SetActive(true);
partyLeaderButton.GetComponent<Button>().onClick.AddListener(() =>
{
PromotePartyLeader(user.userId);
});
// Set KickPartyButton as active, then add listener when onclick button
Transform kickPartyButton = playerEntryDisplay.GetChild(2).transform;
kickPartyButton.gameObject.SetActive(true);
kickPartyButton.GetComponent<Button>().onClick.AddListener(() =>
{
KickParty(user.userId);
});
}
}
// Set DisplayNameText as active, then change text to User's Display Name
Transform displayNameText = playerEntryDisplay.GetChild(3).transform;
displayNameText.gameObject.SetActive(true);
displayNameText.GetComponent<Text>().text = user.displayName;
partyMembers.Add(user.userId, user.displayName);
}
}
});
});
}
}
}
PartyInvitationPanel.cs
using UnityEngine;
using AccelByte.Core;
using AccelByte.Models;
using UnityEngine.UI;
public class PartyInvitationPanel : MonoBehaviour
{
[SerializeField]
private GameObject invitationPopUp;
[SerializeField]
private Text invitationText;
[SerializeField]
private Button acceptInvitationButton;
[SerializeField]
private Button rejectInvitationButton;
/// <summary>
/// Setup PartyInvitation's Popup UI and event listener
/// </summary>
public void Setup(PartyInvitation partyInvitation)
{
AccelByteSDK.GetClientRegistry().GetApi().GetUser().GetUserByUserId(partyInvitation.from, result =>
{
invitationText.text = result.Value.displayName + " invite you to join their party\nAccept invitation?";
});
acceptInvitationButton.onClick.AddListener(() => { JoinParty(partyInvitation); });
rejectInvitationButton.onClick.AddListener(() => { RejectPartyInvitation(partyInvitation); });
}
/// <summary>
/// Accept the invitation and join the party
/// </summary>
public void JoinParty(PartyInvitation partyInvitation)
{
ResultCallback<PartyInfo> joinPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to join party: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully join the party");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().JoinParty(partyInvitation.partyID, partyInvitation.invitationToken, joinPartyCallback);
// Destroy the PopUp prefab
Destroy(invitationPopUp);
}
/// <summary>
/// Reject the party invitation
/// </summary>
public void RejectPartyInvitation(PartyInvitation partyInvitation)
{
ResultCallback<PartyRejectResponse> rejectPartyCallback = result =>
{
// If there is an error, display the error information
if (result.IsError)
{
Debug.Log($"Failed to reject party invitation: error code: {result.Error.Code} message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully reject the party invitation");
}
};
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().RejectPartyInvitation(partyInvitation.partyID, partyInvitation.invitationToken, rejectPartyCallback);
// Destroy the PopUp prefab
Destroy(invitationPopUp);
}
}
LogMessagePanel.cs
using UnityEngine;
using UnityEngine.UI;
public class LogMessagePanel : MonoBehaviour
{
[SerializeField]
private Text messageText;
/// <summary>
/// Update Notification Message's UI
/// </summary>
/// <param name="text"> text that will be shown in the party notification</param>
public void UpdateNotificationUI(string text, Color color)
{
messageText.text = text;
messageText.color = color;
}
}
LobbyHandler.cs
using UnityEngine;
using AccelByte.Api;
using AccelByte.Core;
public class LobbyHandler : MonoBehaviour
{
/// <summary>
/// Private Instance
/// </summary>
static LobbyHandler instance;
/// <summary>
/// The Instance Getter
/// </summary>
public static LobbyHandler Instance => instance;
/// <summary>
/// The Instance Getter
/// </summary>
private Lobby lobby;
[HideInInspector]
public NotificationHandler notificationHandler;
public GameObject LobbyWindow;
#region Notification Box
[Header("Notification Box")]
[SerializeField]
private Transform notificationBoxContentView;
[SerializeField]
private GameObject logMessagePrefab;
#endregion
private void Awake()
{
// Check if another Instance is already created, and if so delete this one, otherwise destroy the object
if (instance != null && instance != this)
{
Destroy(this);
return;
}
else
{
instance = this;
}
// Get the the object handler
notificationHandler = gameObject.GetComponent<NotificationHandler>();
}
/// <summary>
/// Connect to the <see cref="Lobby"/> and setup CallBacks
/// </summary>
public void ConnectToLobby()
{
//Get a reference to the instance of the Lobby
lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
//Init menu handler
GetComponent<MenuHandler>().Create();
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
//Connection
lobby.Connected += notificationHandler.OnConnected;
lobby.Disconnecting += notificationHandler.OnDisconnecting;
lobby.Disconnected += notificationHandler.OnDisconnected;
// Friends
lobby.FriendsStatusChanged += notificationHandler.OnFriendsStatusChanged;
lobby.FriendRequestAccepted += notificationHandler.OnFriendRequestAccepted;
lobby.OnIncomingFriendRequest += notificationHandler.OnIncomingFriendRequest;
lobby.FriendRequestCanceled += notificationHandler.OnFriendRequestCanceled;
lobby.FriendRequestRejected += notificationHandler.OnFriendRequestRejected;
lobby.OnUnfriend += notificationHandler.OnUnfriend;
// Party
lobby.InvitedToParty += notificationHandler.OnInvitedToParty;
lobby.JoinedParty += notificationHandler.OnJoinedParty;
lobby.KickedFromParty += notificationHandler.OnKickedFromParty;
lobby.LeaveFromParty += notificationHandler.OnLeaveFromParty;
lobby.RejectedPartyInvitation += notificationHandler.OnRejectedPartyInvitation;
lobby.PartyDataUpdateNotif += notificationHandler.OnPartyDataUpdateNotif;
//Connect to the Lobby
if (!lobby.IsConnected)
{
lobby.Connect();
}
}
public void RemoveLobbyListeners()
{
//Remove delegate from Lobby
//Connection
lobby.Connected -= notificationHandler.OnConnected;
lobby.Disconnecting -= notificationHandler.OnDisconnecting;
lobby.Disconnected -= notificationHandler.OnDisconnected;
// Friends
lobby.FriendsStatusChanged -= notificationHandler.OnFriendsStatusChanged;
lobby.FriendRequestAccepted -= notificationHandler.OnFriendRequestAccepted;
lobby.OnIncomingFriendRequest -= notificationHandler.OnIncomingFriendRequest;
lobby.FriendRequestCanceled -= notificationHandler.OnFriendRequestCanceled;
lobby.FriendRequestRejected -= notificationHandler.OnFriendRequestRejected;
lobby.OnUnfriend -= notificationHandler.OnUnfriend;
// Party
lobby.InvitedToParty -= notificationHandler.OnInvitedToParty;
lobby.JoinedParty -= notificationHandler.OnJoinedParty;
lobby.KickedFromParty -= notificationHandler.OnKickedFromParty;
lobby.LeaveFromParty -= notificationHandler.OnLeaveFromParty;
lobby.RejectedPartyInvitation -= notificationHandler.OnRejectedPartyInvitation;
lobby.PartyDataUpdateNotif -= notificationHandler.OnPartyDataUpdateNotif;
}
public void DisconnectFromLobby()
{
if (lobby != null && lobby.IsConnected)
{
lobby.Disconnect();
}
}
/// <summary>
/// Write the log message on the notification box
/// </summary>
public void WriteLogMessage(string text, Color color)
{
LogMessagePanel logPanel = Instantiate(logMessagePrefab, notificationBoxContentView).GetComponent<LogMessagePanel>();
logPanel.UpdateNotificationUI(text, color);
}
private void OnApplicationQuit()
{
// Attempt to Disconnect from the Lobby when the Game Quits
DisconnectFromLobby();
}
}
MenuHandler.cs
using UnityEngine;
using UnityEngine.UI;
public class MenuHandler : MonoBehaviour
{
public Transform Menu;
public Button FriendsButton;
public Button LobbyButton;
private bool initialize = false;
public void Create()
{
if (initialize)
{
return;
}
LobbyButton.onClick.AddListener(() =>
{
GetComponent<PartyHandler>().SetupParty();
Menu.gameObject.SetActive(false);
GetComponent<LobbyHandler>().LobbyWindow.SetActive(true);
});
FriendsButton.onClick.AddListener(() =>
{
GetComponent<FriendsManagementHandler>().Setup(FriendsManagementHandler.ExitMode.Menu);
Menu.gameObject.SetActive(false);
GetComponent<FriendsManagementHandler>().FriendsManagementWindow.SetActive(true);
});
initialize = true;
}
}
NotificationHandler.cs
using UnityEngine;
using AccelByte.Models;
using AccelByte.Core;
public class NotificationHandler : MonoBehaviour
{
#region Notifications
// Collection of connection notifications
#region Connections
/// <summary>
/// Called when lobby is connected
/// </summary>
public void OnConnected()
{
Debug.Log("Lobby Connected");
}
/// <summary>
/// Called when connection is disconnecting
/// </summary>
/// <param name="result"> Contains data of message</param>
public void OnDisconnecting(Result<DisconnectNotif> result)
{
Debug.Log($"Lobby Disconnecting {result.Value.message}");
}
/// <summary>
/// Called when connection is being disconnected
/// </summary>
/// <param name="result"> Contains data of websocket close code</param>
public void OnDisconnected(WsCloseCode result)
{
Debug.Log($"Lobby Disconnected: {result}");
}
#endregion
// Collection of friend notifications
#region Friends
/// <summary>
/// Called when friend status is changed
/// </summary>
/// <param name="result"> Contains data of user id, availability, status, etc</param>
public void OnFriendsStatusChanged(Result<FriendsStatusNotif> result)
{
GetComponent<FriendsManagementHandler>().UpdateFriends(result.Value);
}
/// <summary>
/// Called when friend request is accepted
/// </summary>
/// <param name="result"> Contains data of friend's user id</param>
public void OnFriendRequestAccepted(Result<Friend> result)
{
Debug.Log($"Accepted a Friend Request from user {result.Value.friendId}");
}
/// <summary>
/// Called when there is incomming friend request
/// </summary>
/// <param name="result"> Contains data of friend's user id</param>
public void OnIncomingFriendRequest(Result<Friend> result)
{
Debug.Log($"Received a Friend Request from user {result.Value.friendId}");
}
/// <summary>
/// Called when friend is unfriend
/// </summary>
/// <param name="result"> Contains data of friend's user id</param>
public void OnUnfriend(Result<Friend> result)
{
Debug.Log($"Unfriended User {result.Value.friendId}");
}
/// <summary>
/// Called when friend request is canceled
/// </summary>
/// <param name="result"> Contains data of sender user id</param>
public void OnFriendRequestCanceled(Result<Acquaintance> result)
{
Debug.Log($"Cancelled a Friend Request from user {result.Value.userId}");
}
/// <summary>
/// Called when friend request is rejected
/// </summary>
/// <param name="result"> Contains data of rejector user id</param>
public void OnFriendRequestRejected(Result<Acquaintance> result)
{
Debug.Log($"Rejected a Friend Request from user {result.Value.userId}");
}
#endregion
// Collection of party notifications
#region Party
/// <summary>
/// Called when user gets party invitation
/// </summary>
/// <param name="result"> Contains data of inviter, party id, and invitation token</param>
public void OnInvitedToParty(Result<PartyInvitation> result)
{
GetComponent<PartyHandler>().InvitePartyNotification(result.Value);
}
/// <summary>
/// Called when user joins the party
/// </summary>
/// <param name="result"> Contains data of joined user id</param>
public void OnJoinedParty(Result<JoinNotification> result)
{
GetComponent<PartyHandler>().JoinedPartyNotification(result.Value);
}
/// <summary>
/// Called when user is kicked by party leader
/// </summary>
/// <param name="result"> Contains data of party leader's user id, party id, and kicked user id</param>
public void OnKickedFromParty(Result<KickNotification> result)
{
GetComponent<PartyHandler>().KickPartyNotification();
}
/// <summary>
/// Called when user leaves the party
/// </summary>
/// <param name="result"> Contains data of party leader's user id and leaver user id</param>
public void OnLeaveFromParty(Result<LeaveNotification> result)
{
GetComponent<PartyHandler>().LeavePartyNotification(result.Value);
}
/// <summary>
/// Called when user rejects party invitation
/// </summary>
/// <param name="result"> Contains data of party id, party leader's user id, and rejector user id</param>
public void OnRejectedPartyInvitation(Result<PartyRejectNotif> result)
{
Debug.Log("[Party-Notification] Invitee rejected a party invitation");
}
/// <summary>
/// Called when party data is updated
/// </summary>
/// <param name="result"> Contains data of updated party</param>
public void OnPartyDataUpdateNotif(Result<PartyDataUpdateNotif> result)
{
GetComponent<PartyHandler>().DisplayPartyData(result);
}
#endregion
#endregion
}
FriendsManagementHandler.cs
using AccelByte.Api;
using AccelByte.Models;
using AccelByte.Core;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Serialization;
using System.Collections.Generic;
using Button = UnityEngine.UI.Button;
public class FriendsManagementHandler : MonoBehaviour
{
private Dictionary<string, FriendStatusPanel> friendUIDictionary = new Dictionary<string, FriendStatusPanel>();
[Header("Panels")]
public GameObject FriendsManagementWindow;
[FormerlySerializedAs("FriendsPanel")]
[SerializeField]
private RectTransform friendsPanel;
[SerializeField]
private RectTransform requestsPanel;
[SerializeField]
private RectTransform blockedPanel;
#region Buttons
[Header("Buttons")]
[SerializeField]
private Button friendsTabButton;
[SerializeField]
private Button pendingTabButton;
[SerializeField]
private Button blockedTabButton;
[SerializeField]
private Button exitButton;
#endregion
[Header("Friends Panel")]
[SerializeField]
private Transform friendsDisplayPanel;
[FormerlySerializedAs("FriendsDisplayPrefab")]
[SerializeField]
private GameObject friendsDisplayPrefab;
[SerializeField]
private Button friendsSearchButton;
[SerializeField]
private Transform friendsDisplayPlaceholder;
private bool isInitialized = false;
#region Search
[Header("Search Panel")]
[SerializeField]
private GameObject addFriendsPrefab;
[SerializeField]
private Transform friendsSearchPanel;
[SerializeField]
private InputField friendsSearchInputField;
[SerializeField]
private RectTransform friendsSearchScrollView;
[SerializeField]
private Button friendsSearchQuitButton;
[SerializeField]
private Transform friendsSearchPlaceholder;
#endregion
#region Pending
[Header("Pending Panel")]
[SerializeField]
private RectTransform pendingIncomingRequestsContent;
[SerializeField]
private RectTransform pendingOutgoingRequestsContent;
[SerializeField]
private GameObject pendingIncomingRequestsPrefab;
[SerializeField]
private GameObject pendingOutgoingRequestsPrefab;
[SerializeField]
private Transform pendingIncomingRequestPlaceholder;
[SerializeField]
private Transform pendingOutgoingRequestPlaceholder;
[SerializeField]
private Text pendingIncomingRequestText;
[SerializeField]
private Text pendingOutgoingRequestText;
#endregion
#region Blocked
[Header("Blocked")]
[SerializeField]
private RectTransform blockedContent;
[SerializeField]
private GameObject blockedUserPrefab;
[SerializeField]
private Transform blockedDisplayPlaceholder;
#endregion
#region ExitMode
public enum ExitMode
{
Menu,
Lobby
}
private ExitMode ExitScreen
{
get => ExitScreen;
set
{
switch (value)
{
case ExitMode.Menu:
FriendsManagementWindow.SetActive(false);
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
break;
case ExitMode.Lobby:
FriendsManagementWindow.SetActive(false);
GetComponent<LobbyHandler>().LobbyWindow.SetActive(true);
break;
}
}
}
#endregion
private bool setupStatus = false;
public enum FriendsMode
{
Default,
Friends,
Pending,
Blocked
};
private FriendsMode displayMode = FriendsMode.Default;
private FriendsMode DisplayMode
{
get => displayMode;
set
{
switch (value)
{
case FriendsMode.Default:
friendsPanel.gameObject.SetActive(true);
requestsPanel.gameObject.SetActive(false);
blockedPanel.gameObject.SetActive(false);
break;
case FriendsMode.Friends:
friendsPanel.gameObject.SetActive(true);
requestsPanel.gameObject.SetActive(false);
blockedPanel.gameObject.SetActive(false);
GetFriends();
break;
case FriendsMode.Pending:
requestsPanel.gameObject.SetActive(true);
friendsPanel.gameObject.SetActive(false);
blockedPanel.gameObject.SetActive(false);
DisplayPending();
break;
case FriendsMode.Blocked:
blockedPanel.gameObject.SetActive(true);
friendsPanel.gameObject.SetActive(false);
requestsPanel.gameObject.SetActive(false);
DisplayBlocked();
break;
}
}
}
/// <summary>
/// Setup UI and prepare State
/// </summary>
/// <param name="exitType"> name of the destination panel</param>
public void Setup(ExitMode exitType)
{
// Reset the exit button's listener, then add the listener based on the exit screen type
exitButton.onClick.RemoveAllListeners();
exitButton.onClick.AddListener(() =>
{
FriendsManagementWindow.SetActive(false);
GetComponent<MenuHandler>().Menu.gameObject.SetActive(true);
ExitScreen = exitType;
});
// Check whether the FriendsPanel is already set up or not
if (setupStatus)
{
DisplayMode = FriendsMode.Default;
return;
}
// Run the setup if it hasn't
else
{
setupStatus = true;
DisplayMode = FriendsMode.Friends;
// Reset listeners, so it won't be triggered more than once
friendsTabButton.onClick.RemoveAllListeners();
pendingTabButton.onClick.RemoveAllListeners();
blockedTabButton.onClick.RemoveAllListeners();
// Add the listeners
friendsTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Friends);
pendingTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Pending);
blockedTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Blocked);
friendsSearchPanel.gameObject.SetActive(false);
friendsDisplayPlaceholder.gameObject.SetActive(true);
friendsSearchQuitButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(false));
friendsSearchButton.onClick.AddListener(DisplaySearch);
friendsSearchInputField.onEndEdit.AddListener(SearchForFriends);
friendsSearchButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(true));
}
}
/// <summary>
/// Get Friends and Display them
/// </summary>
public void GetFriends()
{
// Cleanup First
LoopThroughTransformAndDestroy(friendsDisplayPanel.transform, friendsDisplayPlaceholder);
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().LoadFriendsList(result =>
{
if (!result.IsError)
{
// Check if no friends were returned
if (result.Value.friendsId.Length <= 0)
{
// Display the Placeholder Text
friendsDisplayPlaceholder.gameObject.SetActive(true);
return;
}
User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
// Send requests to create UI for each friend
Debug.Log("Loaded Friends List Successfully");
var userIds = new System.Collections.Generic.List<string>();
foreach (string friendID in result.Value.friendsId)
{
userIds.Add(friendID);
user.GetUserByUserId(friendID, x =>
{
CreateFriendUI(x.Value);
});
}
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().BulkGetUserPresence(userIds.ToArray(), bulkResult =>
{
if (!bulkResult.IsError)
{
foreach (var friend in bulkResult.Value.data)
{
user.GetUserByUserId(friend.userID, x =>
{
Debug.Log($"Friend : {x.Value.displayName}, Status : {friend.activity}, and Availability : {friend.availability.ToString()}");
});
}
}
});
}
else
{
Debug.LogWarning("Error in Getting Friends");
}
});
}
public void UpdateFriends(FriendsStatusNotif notification)
{
// Find the friend and update it's UI
if (friendUIDictionary.ContainsKey(notification.userID))
{
friendUIDictionary[notification.userID].SetOnlineStatus(notification);
}
// Otherwise we should handle this in some way, possibly creating a Friend UI piece
else
{
Debug.Log("Unregistered Friend received a Notification");
}
}
private void ListQueriedusers(PagedPublicUsersInfo pagedInfo)
{
// Cleanup First
LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);
if (pagedInfo.data.Length <= 0)
{
friendsSearchPlaceholder.gameObject.SetActive(true);
}
else
{
friendsSearchPlaceholder.gameObject.SetActive(false);
foreach (PublicUserInfo info in pagedInfo.data)
{
FriendsAddPanel addPanel = Instantiate(addFriendsPrefab, friendsSearchScrollView).GetComponent<FriendsAddPanel>();
addPanel.Create(info);
}
}
}
private void SearchForFriends(string query)
{
AccelByteSDK.GetClientRegistry().GetApi().GetUser().SearchUsers(query, result =>
{
if (!result.IsError)
{
ListQueriedusers(result.Value);
}
else
{
Debug.LogWarning($"Unable to Query Users Code: {result.Error.Code}, Message: {result.Error.Message}");
}
});
}
private void DisplaySearch()
{
friendsSearchPanel.gameObject.SetActive(true);
LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);
friendsSearchPlaceholder.gameObject.SetActive(true);
}
private void CreateFriendUI(PublicUserData userData)
{
FriendStatusPanel panel = Instantiate(friendsDisplayPrefab, friendsDisplayPanel).GetComponent<FriendStatusPanel>();
panel.Create(userData);
if (!friendUIDictionary.ContainsKey(userData.userId))
{
friendUIDictionary.Add(userData.userId, panel);
}
// Set up the FriendStatusPanel's related button
panel.SetupButton();
}
/// <summary>
/// A utility function to Destroy all Children of the parent transform. Optionally do not remove a specific Transform
/// </summary>
/// <param name="parent">Parent Object to destroy children</param>
/// <param name="doNotRemove">Optional specified Transform that should NOT be destroyed</param>
private static void LoopThroughTransformAndDestroy(Transform parent, Transform doNotRemove = null)
{
// Loop through all the children and add them to a List to then be deleted
List<GameObject> toBeDeleted = new List<GameObject>();
foreach (Transform t in parent)
{
//except the Do Not Remove transform if there is one
if (t != doNotRemove)
{
toBeDeleted.Add(t.gameObject);
}
}
// Loop through list and Delete all Children
for (int i = 0; i < toBeDeleted.Count; i++)
{
Destroy(toBeDeleted[i]);
}
}
private void DisplayPending()
{
LoopThroughTransformAndDestroy(pendingIncomingRequestsContent.transform, pendingIncomingRequestPlaceholder);
LoopThroughTransformAndDestroy(pendingOutgoingRequestsContent.transform, pendingOutgoingRequestPlaceholder);
Lobby lobby = AccelByteSDK.GetClientRegistry().GetApi().GetLobby();
// Get all Incoming Friend Requests
lobby.ListIncomingFriends(result =>
{
// Check for an Error
if (result.IsError)
{
Debug.LogWarning($"Unable to get Incoming Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
// Set the placeholder Text to be active so it doesn't look broken
pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
}
else
{
if (result.Value.friendsId.Length <= 0)
{
pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
}
else
{
pendingIncomingRequestPlaceholder.gameObject.SetActive(false);
}
// Loop through all the UserID's returned by the Friends callback and get their PublicUserData
User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
foreach (string userID in result.Value.friendsId)
{
// Request the PublicUserData for the specific Friend
user.GetUserByUserId(userID, userResult =>
{
// If there's an error, report it and do nothing else
if (userResult.IsError)
{
Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
}
// If we have valid data, log the display name
else
{
Debug.Log($"Friend request from : {userResult.Value.displayName}");
FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
outgoingPanel.Create(userResult.Value);
}
});
}
}
});
// Get all outgoing friend requests
lobby.ListOutgoingFriends(result =>
{
// Check for an error
if (result.IsError)
{
Debug.LogWarning($"Unable to get Outgoing Friend Lists Code: {result.Error.Code}, Message: {result.Error.Message}");
}
else
{
if (result.Value.friendsId.Length <= 0)
{
pendingOutgoingRequestPlaceholder.gameObject.SetActive(true);
}
else
{
pendingOutgoingRequestPlaceholder.gameObject.SetActive(false);
}
User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
// Loop through all the UserID's returned by the Friends callback and get their PublicUserData
foreach (string userID in result.Value.friendsId)
{
// Request the PublicUserData for the specific Friend
user.GetUserByUserId(userID, userResult =>
{
// If it's an error, report it and do nothing else
if (userResult.IsError)
{
Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
}
// If we have valid data, log the display name
else
{
Debug.Log($"Pending Friend Request to : {userResult.Value.displayName}");
FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
outgoingPanel.Create(userResult.Value);
}
});
}
}
});
}
private void DisplayBlocked()
{
// Cleanup First
LoopThroughTransformAndDestroy(blockedContent.transform, blockedDisplayPlaceholder);
// Get Blocked List
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().GetListOfBlockedUser(result =>
{
if (result.IsError)
{
Debug.LogWarning($"Unable to get Blocked Player List Code: {result.Error.Code}, Message: {result.Error.Message}");
blockedDisplayPlaceholder.gameObject.SetActive(true);
}
else
{
blockedDisplayPlaceholder.gameObject.SetActive(false);
User user = AccelByteSDK.GetClientRegistry().GetApi().GetUser();
// Loop through all the UserID's returned by the callback and get their PublicUserData
foreach (BlockedData blockedUser in result.Value.data)
{
// Request the PublicUserData for the specific User
user.GetUserByUserId(blockedUser.blockedUserId, userResult =>
{
// If it's an Error, report it and do nothing else
if (userResult.IsError)
{
Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
}
else
{
// If we have valid data, instantiate the prefab for the specific UI piece and call relevant functions
FriendsBlockedPanel blockedPanel = Instantiate(blockedUserPrefab, blockedContent).GetComponent<FriendsBlockedPanel>();
// Pass the PublicUserData into this function
blockedPanel.Create(userResult.Value);
}
});
}
}
});
}
}
FriendStatusPanel.cs
using AccelByte.Models;
using AccelByte.Core;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;
public class FriendStatusPanel : MonoBehaviour
{
PublicUserData userData;
[SerializeField]
private Image profilePicture;
[SerializeField]
private Image statusDisplay;
[SerializeField]
private Button chatButton;
[SerializeField]
private Button inviteToPartyButton;
[SerializeField]
private Button unfriendButton;
[SerializeField]
private Button blockButton;
[SerializeField]
private Text displayNameText;
[SerializeField]
private Text onlineStatusText;
public void SetOnlineStatus(FriendsStatusNotif notification)
{
switch (notification.availability)
{
case UserStatus.Offline:
onlineStatusText.text = "Offline";
statusDisplay.color = Color.black;
break;
case UserStatus.Online:
onlineStatusText.text = "Online";
statusDisplay.color = Color.green;
break;
case UserStatus.Busy:
onlineStatusText.text = "Busy";
statusDisplay.color = Color.yellow;
break;
case UserStatus.Invisible:
onlineStatusText.text = "Offline";
statusDisplay.color = Color.black;
break;
default:
onlineStatusText.text = $"INVALID UNHANDLED {notification.availability}";
statusDisplay.color = Color.magenta;
break;
}
Debug.Log($"Friend Status for {notification.userID} changed to {notification.availability}");
}
public void Create(PublicUserData publicUserData)
{
userData = publicUserData;
displayNameText.text = userData.displayName;
}
/// <summary>
/// Setup UI Button Listener
/// </summary>
public void SetupButton()
{
unfriendButton.onClick.AddListener(() =>
{
AccelByteSDK.GetClientRegistry().GetApi().GetLobby().Unfriend(userData.userId, result =>
{
if (result.IsError)
{
Debug.Log($"Failed to unfriend a friend: code: {result.Error.Code}, message: {result.Error.Message}");
}
else
{
Debug.Log("Successfully unfriend a friend!");
LobbyHandler.Instance.GetComponent<FriendsManagementHandler>().GetFriends();
}
});
});
}
}