Last Updated: 4/22/2022, 3:20:37 AM

# Party

# Overview

The AccelByte Lobby Service includes Party features to enable multiplayer play. All Party features use WebSocket to ensure smooth communication between players. Players can interact with parties in several ways, including:

  • User Party Info shows players information about their party, including the Party ID, Leader ID, and the list of Party Members.
  • Create Party allows players to create a new party that other players can join.
  • Invite to Party allows players to invite their friends to join their party. Both the inviter and invitee will receive a notification when the invitation is sent.
  • Join Party allows players to join a party that they’ve been invited to join. When the player joins the party the inviter and other party members will be notified.
  • Reject Party Invitation allows players to reject a party invitation. The inviter will be notified that their invitation was rejected.
  • Promote Member as a Party Leader allows players to set another player as the party leader.
  • Kick Party allows party leaders to remove another player from their party.

# Permissions

Permissions are used to grant access to specific resources within our services. Make sure your account has the following permissions before you attempt to manage a party in the Admin Portal.

Usage Resource Action
Admin Get All Parties ADMIN:NAMESPACE:{namespace}:PARTY:STORAGE Read
Admin Get Party Data ADMIN:NAMESPACE:{namespace}:PARTY:STORAGE Read
Update Party Attributes ADMIN:NAMESPACE:{namespace}:PARTY:STORAGE Update
Admin Get User Party Data ADMIN:NAMESPACE:{namespace}:PARTY:STORAGE Read

Permissions work slightly differently depending on whether they are assigned to IAM Clients or Roles assigned to users. For more information, read the Authentication and Authorization documentation.

# Implementing Party Interaction using the SDK

# Create a Party

Players can create a party that can be used for matchmaking. A player can only create one party at a time, and will become the leader of the party they create.

# Invite to Party

Players can invite other players to their party, as long as the invited player is not already in a party. Party invitees will receive a notification that they have been invited to join a party and current party members will also receive a notification that someone has been invited to the party. Party invitees are identified by the inviteeUserId parameter in the Unity SDK and by inviteePlayerId in the Unreal Engine SDK.

# Party Codes

Party leaders can send party codes to players so that those players can join their party. Only party leaders can use party codes. They can also refresh or revoke a party code.

# Generate or Refresh a Party Code

Use the following function to generate or refresh a party code.

# Get a Party Code

Once a code is generated, party leaders can get the code and share it from other players. This function allows party leaders to get the party code.

TIP

If the success response returns an empty party code, you need to call the GeneratePartyCode function to generate a new party code.

# Revoke a Party Code

Party leaders can revoke a party code. If a party code is revoked, players will no longer be able to use it to join the party. This function allows party leaders to revoke the party code.

# Join a Party with a Party Code

Players that have been sent a party code can use it to join a party. This function allows players to join a party using a party code.

# Join a Party

Players that are invited to a party can accept the invitation and join the party. Party invitees will receive a notification that they’ve joined the party and current party members will also receive a notification that someone is joining the party. The party is identified by the partyID parameter and the invitation by the invitationToken parameter.

# Reject a Party Invitation

When a player has been invited to a party, they can choose to reject the invitation. Party invitees will receive a notification that they’ve rejected the invitation and current party members will also receive a notification that someone has rejected the party invitation. The party is identified by the partyID parameter and the invitation by the invitationToken parameter.

# Promote a Member as the Party Leader

The party leader can promote another party member to become the new party leader. All party members will be notified of the new party leader. The new party leader is identified by the memberUserId parameter.

# Kick Players From a Party

The party leader has the privilege to remove a party member from their party by kicking them out of the party. Kicked party member will receive a notification that they’ve been kicked out of the party, and current party members will also receive a notification that someone has been kicked from the party. The kicked party member is identified by the memberUserId parameter in Unity and by KickPlayerId in Unreal Engine.

# Leave a Party

Party members can choose to leave their party. If the party leader leaves the party, party leadership will be passed to another party member automatically. Party members that leave the party will receive a notification that they’ve left the party and current party members will also receive a notification that someone is leaving the party.

# Implementing Party Storage using the SDK

Party Storage enables a party to store party attributes that can be passed from the game server to the game client. The game server will write the party’s storage to ensure that all party members get the same information or notification from the server.

# Party Data Update Notification

Use this function to notify players of any updates to party data. Party members will be notified if an event occurs in the party, such as when a player accepts a party invitation, joins the party, or leaves the party.

# Get Party Data (Client-Side)

Use this function to retrieve specific party data from the client side. Make sure you have players logged in to allow this API call.

# Get Party Data (Server-Side)

Use this function to retrieve specific party data from the server side. Make sure the game server is authenticated to allow this API call.

# Write Party Data

Write Party Data is an exclusive feature for the game server. In writing party data, it can occur that multiple servers write to the same party at the same time. To avoid this, our SDK has a timestamp in the backend to ensure that the latest write data will never be overwritten with older data.

# Implementing the Party Service using the Unity SDK

# Quick Reference

References
using AccelByte.Api;
using AccelByte.Core;
using AccelByte.Models;
Party Notification Events
AccelBytePlugin.GetLobby().InvitedToParty += result => {};
AccelBytePlugin.GetLobby().JoinedParty += result => {};
AccelBytePlugin.GetLobby().KickedFromParty += result =>{};
AccelBytePlugin.GetLobby().LeaveFromParty += result =>{};
AccelBytePlugin.GetLobby().RejectedPartyInvitation += result =>{};
Create Party
AccelBytePlugin.GetLobby().CreateParty(result =>
{
    // If there is not an error, display the success information
    if (!result.IsError)
    {
        Debug.Log("Successfully create a party");
    }
});
Invite to Party
string userId;

AccelBytePlugin.GetLobby().InviteToParty(userId, result =>
{
    if (!result.IsError)
        Debug.Log("Successfully invite an invitee");
    }
});
Kick from Party
string userId;

AccelBytePlugin.GetLobby().KickPartyMember(userId, result =>
{
    if (result.IsError)
    {
        Debug.Log($"Successfully kick member from party");
    }
});
Leave Party
AccelBytePlugin.GetLobby().LeaveParty(result =>
{
    // If there is not an error, display the success information
    if (!result.IsError)
    {
        Debug.Log("Successfully leave from party");
    }
});
Promote to Party Leader
string userId;

AccelBytePlugin.GetLobby().PromotePartyLeader(userId, result =>
{
    if (!result.IsError)
    {
        Debug.Log("Successfully promote member to be a party leader");
    }
});
Join Party
PartyInvitation partyInvitation;

AccelBytePlugin.GetLobby().JoinParty(partyInvitation.partyID, partyInvitation.invitationToken, result =>
{
    if (!result.IsError)
    {
        Debug.Log("Successfully join the party");
    }
});
Reject Party Invitation
PartyInvitation partyInvitation;

AccelBytePlugin.GetLobby().RejectPartyInvitation(partyInvitation.partyID, partyInvitation.invitationToken, result =>
{
    if (result.IsError)
    {
        Debug.Log("Successfully reject the party invitation");
    }
});
Get Users’ Info
string[] usersIdList;

AccelBytePlugin.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 (opens new window) and Friends (opens new window) 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.

We will start by adding simple party logic into the game.

  1. Create a new script called PartyHandler.cs and attach it to the AccelByteHandler gameObject.

  2. Add the following AccelByte libraries to the top of the script:

using AccelByte.Api;
using AccelByte.Models;
using AccelByte.Core;
  1. Add some basic Party functionalities 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 as partyID, the leader’s leaderID, a list of the party members’ userIDs, a list of the invitees’ userIDs, and the party invitation’s invitationToken.
public void CreateParty()
{
    AccelBytePlugin.GetLobby().CreateParty(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");
        }
    });
}

If you want to save the PartyInfo of the created party, you can set the value based on result.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 add inviteeUserId as the function’s parameter.
public void InviteToParty(string inviteeUserId)
{
    AccelBytePlugin.GetLobby().InviteToParty(inviteeUserId, 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");
        }
    });
}
  • Kick Friend from Party
    This function requires the UserId of the party member you want to kick from the party, so add memberUserId as the function’s parameter.
public void KickParty(string memberUserId)
{
    AccelBytePlugin.GetLobby().KickPartyMember(memberUserId, 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");
        }
    });
}
  • Leave from Party

Use this function to leave a party.

public void LeaveParty()
{    
    AccelBytePlugin.GetLobby().LeaveParty(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");
        }
    });
}
  • Promote Friend to Party Leader
    This function requires the UserId of the party member you want to promote to party leader, so add memberUserId as the function’s parameter.
public void PromotePartyLeader(string memberUserId)
{
    AccelBytePlugin.GetLobby().PromotePartyLeader(memberUserId, 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");
        }
    });
}
  1. Most of the Party Invitation’s UI will be a spawnable prefab. Create a new script called PartyInvitationPanel.cs and add the following on the top:
using AccelByte.Api;
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.

  1. 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 add inviteeUserId as the function’s parameter.
public void JoinParty(PartyInvitation partyInvitation)
{
    AccelBytePlugin.GetLobby().JoinParty(partyInvitation.partyID, partyInvitation.invitationToken, 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");
        }
    });
}
  • Reject Party Invitation
    This function requires the UserID of the party member you want to kick from the party, so add memberUserId as the function’s parameter.
public void RejectPartyInvitation(PartyInvitation partyInvitation)
{
AccelBytePlugin.GetLobby().RejectPartyInvitation(partyInvitation.partyID, partyInvitation.invitationToken, 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");
        }
    });
}

You can use the result value from the AccelBytePlugin.GetLobby().InvitedToParty event for this PartyInvitation value.

  1. To send a notification to a player on any activity related to the current party, use these events below:
AccelBytePlugin.GetLobby().InvitedToParty += result => {};
AccelBytePlugin.GetLobby().JoinedParty += result => {};
AccelBytePlugin.GetLobby().KickedFromParty += result =>{};
AccelBytePlugin.GetLobby().LeaveFromParty += result =>{};
AccelBytePlugin.GetLobby().RejectedPartyInvitation += result =>{};

You can find these in LobbyHandler.cs under the ConnectToLobby() function. For now, we will just add a Debug.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");}
  1. 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:

AccelBytePlugin.GetLobby().PartyDataUpdateNotif += result =>{}

This event will return a result with the class type PartyDataUpdateNotif which contains Party data such as PartyInfo. PartyDataUpdateNotif does not have an invitationToken, but does have updatedAt which indicates when the Party data is updated, and custom_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 the ConnectToLobby() function. In this case, since we only need to display Party data, just add a Debug.Log to notify the party member’s UserID 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. The PartyInfo class contains Party data such as the partyID, leaderID, members (userID), invitees (userID), and an invitationToken. To get this PartyInfo, use the following function:

AccelBytePlugin.GetLobby().GetPartyInfo(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");
    }
});

This function returns a result with the class type PartyInfo, and you can access its values with result.Value.<the object you need>. For example, if you want to retrieve the partyID, use result.Value.partyId.

TIP

You can only retrieve UserID from PartyDataUpdateNotif and PartyInfo, so you will need to use the GetUser() function to retrieve user data.

AccelBytePlugin.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}");
    }
});

AccelBytePlugin.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 contains avatarUrl.

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. Otherwise, you are now ready to move on to Matchmaking (opens new window).

# Step by Step Guide

UI Implementation
  1. 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)
  1. 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.

Party

  1. 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

Party

  1. 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

Party

  1. 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 Scroll View’s content.

Party

Code Implementation

Now that you have some basic Party functionalities, you can implement these into your UI.

  1. Inside PartyHandler.cs, add UnityEngine’s UI library:
using UnityEngine.UI;
  1. 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;
  1. Create a new dictionary that will save your Party members’ User IDs and display names.
public Dictionary<string, string> partyMembers { private set; get; }
  1. Since **FriendsPanel **can be called from both the Menu and the Lobby, create a new enum for an exit mode. In FriendsManagementHandler.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;
        }
    }
}
#endregion
  1. To avoid the listener being set up twice, add a new boolean as a flag checker in FriendsManagementHandler.cs, and then change the Setup() function:
private bool isInitialized = false;

...

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);

    // Check whether the FriendsPanel already set up or not
    if (isInitialized)
    {
        DisplayMode = FriendsMode.Default;
        return;
    }
    // Run the setup if it still hasn't
    else
    {
        isInitialized = true;

        DisplayMode = FriendsMode.Friends;

        // reset listenera, so it won't triggered more than once
        friendsTabButton.onClick.RemoveAllListeners();
        pendingTabButton.onClick.RemoveAllListeners();
        blockedTabButton.onClick.RemoveAllListeners();

        // add the listeners
        friendsTabButton.onClick.AddListener(() => DisplayMode = FriendsMode.Friends);
        ...
        friendsSearchButton.onClick.AddListener(() => friendsSearchPanel.gameObject.SetActive(true));
    }
}
  1. Create a function in PartyHandler.cs to set up your Party UI when the LobbyPanel 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);
    });
}

  1. Before you continue with PartyHandler.cs, connect the LobbyPanel with the MenuPanel’s LobbyButton. In MenuHandler.cs, add a reference for the LobbyButton.
public Button LobbyButton;
  1. Inside the Create() function, add the LobbyButton’s event listener and change the FriendsButton’s Setup():
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);
            ...
        });
}
  1. In the Unity Editor, on the MenuHandler.cs script component in the AccelByteHandler gameObject, drag the following objects to the appropriate variables:

Party

  1. Go to FriendStatusPanel.cs and add an inviteToPartyButton listener in the SetupButton() function. Use the LobbyHandler’s instance to get PartyHandler without needing to use the Find function.
public void SetupButton()
{
    inviteToPartyButton.onClick.AddListener(() =>
    {
LobbyHandler.Instance.GetComponent<PartyHandler>().InviteToParty(_userData.userId);
    });
    ...
}
  1. In FriendsManagementHandler.cs, in the CreateFriendUI() function, call the SetupButton() that you made earlier and set up the Friend’s UI per user.
private void CreateFriendUI(PublicUserData userData)
{
        FriendStatusPanel panel = Instantiate(friendsDisplayPrefab, friendsDisplayPanel).GetComponent<FriendStatusPanel>();
        ...
        // set up the any FriendStatusPanel's related button
        panel.SetupButton();
}
  1. Update your PartyInvitationPanel.cs for when the PartyInvitationPopup 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)
{
    AccelBytePlugin.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);});
}
  1. Add the following in PartyInvitationPanel.cs to destroy the PartyInvitationPopUp 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);
}
  1. In the Unity editor, open the PartyInvitationPopUp prefab. Add the PartyInvitationPanels.cs script as a component, then drag the objects to their exposed variables:

Party

  1. In PartyHandler.cs, create a new function to Display Party data to the PartyListPanel using the BulkGetUserInfo() 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
    AccelBytePlugin.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 (AccelBytePlugin.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);
            }
        }
    });
}
  1. 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 using GetPartyInfo().
private void Start()
{
    if (AccelBytePlugin.GetLobby().IsConnected)
    {
        AccelBytePlugin.GetLobby().GetPartyInfo(partyInfoResult =>
        {
            // update PartyID in UI
            partyIdText.text = "PartyID: " + partyInfoResult.Value.partyID;

            ResetPlayerEntryUI();

            // Get all party members data based on _partyUserIds, then update data to UI
            AccelBytePlugin.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 (AccelBytePlugin.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);
                    }
                }
            });
        });
    }
}
  1. 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";
    }
}
  1. 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()
{
        AccelBytePlugin.GetLobby().LeaveParty(result =>
        {
            if (result.IsError)
            ...
            else
            {
                ...
                // reset all Party-related UIs
                ResetPartyId();
                ResetPlayerEntryUI();
            }
        });
}

public void DisplayPartyData(Result<PartyDataUpdateNotif> partyDataNotifResult)
{
    // update PartyID in UI
    ...

    // reset the all the Party List's UI
    ResetPlayerEntryUI();

    ...
}
  1. 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 the LogMessageDisplay 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;
    }
}
  1. 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;
#endregion
  1. Add 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);
}
  1. In the Unity Editor, on your AccelByteHandler, drag the following objects to the exposed variables:

Party

  1. In PartyHandler.cs, create functions to handle each update event in LobbyHandler.cs and to create notification messages.
/// Called on update when received a party invitation
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 friend joined the party
public void JoinedPartyNotification(JoinNotification joinNotification)
{
    Debug.Log("Invitee join a party");
    AccelBytePlugin.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 friend left the party
public void LeavePartyNotification(LeaveNotification leaveNotification)
{
    if (leaveNotification.userID != AccelBytePlugin.GetUser().Session.UserId)
    {
        Debug.Log($"{leaveNotification.userID} leave the party");
        LobbyHandler.Instance.WriteLogMessage($"[Party] {partyMembers[leaveNotification.userID]} leave the party", Color.black);
    }
}
  1. While still in PartyHandler.cs, add the following code under the PromotePartyLeader() 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>();

    AccelBytePlugin.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);
        }
    });
}
  1. 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 to 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 from 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);
}
#endregion
  1. Change your notification update events in LobbyHandler.cs under the ConnectToLobby() 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;  
    ...
}
  1. Update the RemoveLobbyListeners() function in LobbyHandler.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;
}
  1. Save and return to the Unity Editor. In your scene, on the AccelByteHandler gameObject, drag the appropriate objects into the exposed variables of the PartyHandler script component.

Party

Congratulations! You have now fully implemented the Party service.

Proceed to the next section to learn how to implement Matchmaking (opens new window).

# Full Code

PartyHandler.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using AccelByte.Api;
using AccelByte.Core;
using AccelByte.Models;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

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; }

    private void Start()
    {
        if (AccelBytePlugin.GetLobby().IsConnected)
        {
            AccelBytePlugin.GetLobby().GetPartyInfo(partyInfoResult =>
            {
                // update PartyID in UI
                partyIdText.text = "PartyID: " + partyInfoResult.Value.partyID;

                ResetPlayerEntryUI();

                // Get all party members data based on _partyUserIds, then update data to UI
                AccelBytePlugin.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 (AccelBytePlugin.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);
                        }
                    }
                });
            });
        }
    }


    /// <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()
    {
        AccelBytePlugin.GetLobby().CreateParty(result =>
        {
            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");
            }
        });
    }

    /// <summary>
    /// Invite friend to party
    /// </summary>
    /// <param name="inviteeUserId"> userId of the user that will received the invitation</param>
    public void InviteToParty(string inviteeUserId)
    {
        AccelBytePlugin.GetLobby().InviteToParty(inviteeUserId, result =>
        {
            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");
            }
        });
    }

    /// <summary>
    /// Kick user from party
    /// </summary>
    /// <param name="memberUserId"> userId of the member that will be kicked from the party</param>
    public void KickParty(string memberUserId)
    {
        AccelBytePlugin.GetLobby().KickPartyMember(memberUserId, result =>
        {
            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");
            }
        });
    }

    /// <summary>
    /// Leave from party
    /// </summary>
    public void LeaveParty()
    {
        AccelBytePlugin.GetLobby().LeaveParty(result =>
        {
            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");

                ResetPartyId();
                ResetPlayerEntryUI();
            }
        });
    }

    /// <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)
    {
        AccelBytePlugin.GetLobby().PromotePartyLeader(memberUserId, result =>
        {
            if (result.IsError)
            {
                Debug.Log($"Failed to promote member to be party leader: error code: {result.Error.Code} message: {result.Error.Message}");
                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");
                LobbyHandler.Instance.WriteLogMessage($"[Party] Successfully promote {partyMembers[memberUserId]} to be party leader", Color.black);
            }
        });
    }

    /// <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;

        ResetPlayerEntryUI();

        // Get all party members data based on _partyUserIds, then update data to UI
        AccelBytePlugin.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 (AccelBytePlugin.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>
    /// 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 received a party invitation
    /// </summary>
    /// <param name="partyInvitation"> contains Party Invitation's data, consists of sender's userId, partyId, and invitationToken</param>
    public void InvitePartyNotification(PartyInvitation partyInvitation)
    {
        Debug.Log($"[Party-Notification] 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("[Party-Notification] You're kicked from party");
        ResetPartyId();
        ResetPlayerEntryUI();
    }

    /// <summary>
    /// Called on update when friend joined the party
    /// </summary>
    /// <param name="joinNotification"> contains data of the user who just joined the party, consists of user's userId</param>
    public void JoinedPartyNotification(JoinNotification joinNotification)
    {
        Debug.Log("[Party-Notification] Invitee join a party");
        AccelBytePlugin.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 friend left the party
    /// </summary>
    /// <param name="leaveNotification"> contains userId's data of the user who just left and the party leader's userId</param>
    public void LeavePartyNotification(LeaveNotification leaveNotification)
    {
        if (leaveNotification.userID != AccelBytePlugin.GetUser().Session.UserId)
        {
            Debug.Log($"{leaveNotification.userID} leave the party");
            LobbyHandler.Instance.WriteLogMessage($"{partyMembers[leaveNotification.userID]} leave the party", Color.black);
        }
    }
}
PartyInvitation.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
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)
    {
            AccelBytePlugin.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)
    {
            AccelBytePlugin.GetLobby().JoinParty(partyInvitation.partyID, partyInvitation.invitationToken, result =>
            {
                if (result.IsError)
                {
                        Debug.Log($"Failed to join party: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                        Debug.Log("Successfully join a party");
                }
            });

            Destroy(invitationPopUp);
    }

    /// <summary>
    /// Reject the party invitation
    /// </summary>
    public void RejectPartyInvitation(PartyInvitation partyInvitation)
    {
AccelBytePlugin.GetLobby().RejectPartyInvitation(partyInvitation.partyID, partyInvitation.invitationToken, result =>
            {
                if (result.IsError)
                {
                        Debug.Log($"Failed to reject party invitation: error code: {result.Error.Code} message: {result.Error.Message}");
                }
                else
                {
                        Debug.Log("Successfully reject a party invitation");
                }
            });

            Destroy(invitationPopUp);
    }
}
LogMessagePanel.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using AccelByte.Api;
using AccelByte.Models;
using System.Collections;
using System.Collections.Generic;
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
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using UnityEngine;
using AccelByte.Api;

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;

    public GameObject LobbyWindow;

    #region Notification Box
    [Header("Notification Box")]
    [SerializeField]
    private Transform notificationBoxContentView;
    [SerializeField]
    private GameObject logMessagePrefab;
    #endregion

    [HideInInspector]
    public NotificationHandler notificationHandler;


    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 = AccelBytePlugin.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 (AccelBytePlugin.GetLobby().IsConnected)
        {
            AccelBytePlugin.GetLobby().Disconnect();
        }
    }

    private void OnApplicationQuit()
    {
        // Attempt to Disconnect from the Lobby when the Game Quits
        DisconnectFromLobby();
    }

    /// <summary>
    /// Write the log message on the notification box
    /// </summary>
    /// <param name="text"> text that will be shown in the party notification</param>
    public void WriteLogMessage(string text, Color color)
    {
        LogMessagePanel logPanel = Instantiate(logMessagePrefab, notificationBoxContentView).GetComponent<LogMessagePanel>();
        logPanel.UpdateNotificationUI(text, color);
    }
}
MenuHandler.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MenuHandler : MonoBehaviour
{
    public Transform Menu;

    public Button LobbyButton;
    public Button FriendsButton;

    private bool isInitialized = false;

    public void Create()
    {
        if (isInitialized) return;

        isInitialized = true;

        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);
        });
    }    
}
NotificationHandler.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

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 to 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 from 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
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using System.Collections.Generic;
using UnityEngine;
using AccelByte.Api;
using AccelByte.Models;
using UnityEngine.Serialization;
using UnityEngine.UI;
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;

    #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

    private bool setupStatus = false;

    #region FriendsMode
    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;
            }
        }
    }
    #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

    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");
        }
    }

    /// <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(() => ExitScreen = exitType);

        // Check whether the FriendsPanel already set up or not
        if (setupStatus)
        {
            DisplayMode = FriendsMode.Default;
            return;
        }
        // Run the setup if it still hasn't
        else
        {
            setupStatus = true;

            DisplayMode = FriendsMode.Friends;

            // reset listenera, so it won't 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>
    private void GetFriends()
    {
        //Cleanup First
        LoopThroughTransformAndDestroy(friendsDisplayPanel.transform, friendsDisplayPlaceholder);

        AccelBytePlugin.GetLobby().LoadFriendsList(result =>
        {
            //Check this is not an error
            if (!result.IsError)
            {
                //Check if no friends were returned
                if (result.Value.friendsId.Length <= 0)
                {
                    //Display the Placeholder Text
                    friendsDisplayPlaceholder.gameObject.SetActive(true);
                    return;
                }
                //Hide the Placeholder Text
                friendsDisplayPlaceholder.gameObject.SetActive(false);


                //Fire off Requests to create UI for each friend
                Debug.Log("Loaded Friends List Succesfully");
                foreach (string friendID in result.Value.friendsId)
                {
                    Debug.Log($"Friend : {friendID}");
                    AccelBytePlugin.GetUser().GetUserByUserId(friendID, x =>
                    {
                        CreateFriendUI(x.Value);
                    });
                }    
            }
            else
            {
                //Display the Placeholder
                friendsDisplayPlaceholder.gameObject.SetActive(true);
                Debug.LogWarning("Error in Getting Friends");
            }

        });
    }

    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);
        }

        panel.SetupButton();
    }

    private void DisplaySearch()
    {
        friendsSearchPanel.gameObject.SetActive(true);
        LoopThroughTransformAndDestroy(friendsSearchScrollView.transform, friendsSearchPlaceholder);
        friendsSearchPlaceholder.gameObject.SetActive(true);
    }

    private void SearchForFriends(string query)
    {
        AccelBytePlugin.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 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 DisplayPending()
    {
        //Cleanup First, remove all Children from the Contents OTHER than the Placeholders
        LoopThroughTransformAndDestroy(pendingIncomingRequestsContent.transform, pendingIncomingRequestPlaceholder);
        LoopThroughTransformAndDestroy(pendingOutgoingRequestsContent.transform, pendingOutgoingRequestPlaceholder);

        //Get all Incoming Friend Requests
        AccelBytePlugin.GetLobby().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 just look broken
                pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
            }
            else
            {
                //If there are Zero Incoming Requests, set the PlaceHolder to be active
                if (result.Value.friendsId.Length <= 0)
                {
                    pendingIncomingRequestPlaceholder.gameObject.SetActive(true);
                }
                //Otherwise set the PlaceHolder to be inactive
                else
                {
                    pendingIncomingRequestPlaceholder.gameObject.SetActive(false);
                }

                //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
                    AccelBytePlugin.GetUser().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, Instantiate the Prefab for the specific UI Piece and call relevant functions
                        else
                        {
                            FriendsIncomingPanel incomingPanel = Instantiate(pendingIncomingRequestsPrefab, pendingIncomingRequestsContent).GetComponent<FriendsIncomingPanel>();
                            //Pass the PublicUserData into this function
                            incomingPanel.Create(userResult.Value);

                        }
                    });
                }
            }
        });

        AccelBytePlugin.GetLobby().ListOutgoingFriends(result =>
        {
            if (result.IsError)
            {
                Debug.LogWarning($"Unable to get Outgoing Requests Code: {result.Error.Code}, Message: {result.Error.Message}");
            }
            else
            {
                if (result.Value.friendsId.Length <= 0)
                {
                    pendingOutgoingRequestPlaceholder.gameObject.SetActive(true);
                }
                else
                {
                    pendingOutgoingRequestPlaceholder.gameObject.SetActive(false);
                }
                foreach (string userID in result.Value.friendsId)
                {
                    AccelBytePlugin.GetUser().GetUserByUserId(userID, userResult =>
                    {
                        if (userResult.IsError)
                        {
                            Debug.LogWarning($"Unable to User Code: {userResult.Error.Code}, Message: {userResult.Error.Message}");
                        }
                        else
                        {
                            FriendsOutgoingPanel outgoingPanel = Instantiate(pendingOutgoingRequestsPrefab, pendingOutgoingRequestsContent).GetComponent<FriendsOutgoingPanel>();
                            outgoingPanel.Create(userResult.Value);
                        }

                    });

                }
            }
        });

    }

    private void DisplayBlocked()
    {
        //Cleanup First
        LoopThroughTransformAndDestroy(blockedContent.transform,blockedDisplayPlaceholder);

        //Get Blocked List
        AccelBytePlugin.GetLobby().GetListOfBlockedUser(result =>
        {
            //Check for an Error
            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);
                //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
                    AccelBytePlugin.GetUser().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}");
                        }
                        //If we have valid data, Instantiate the Prefab for the specific UI Piece and call relevant functions
                        else
                        {
                            FriendsBlockedPanel blockedPanel = Instantiate(blockedUserPrefab, blockedContent).GetComponent<FriendsBlockedPanel>();
                            //Pass the PublicUserData into this function
                            blockedPanel.Create(userResult.Value);

                        }

                    });

                }
            }
        });


    }


    /// <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]);
        }
    }
}
FriendStatusPanel.cs
// Copyright (c) 2021 - 2022 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

using System.Collections;
using System.Collections.Generic;
using AccelByte.Api;
using AccelByte.Models;
using UnityEngine;
using UnityEngine.UI;
using Image = UnityEngine.UI.Image;


public class FriendStatusPanel : MonoBehaviour
{
    [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;


    PublicUserData _userData;


    void SetOnlineStatus(FriendsStatusNotif notification)
    {
        switch (notification.availability)
        {
            case "offline":
                onlineStatusText.text = "Offline";
                statusDisplay.color = Color.black;
                break;
            case "online":
                onlineStatusText.text = "Online";
                statusDisplay.color = Color.green;
                break;
            case "busy":
                onlineStatusText.text = "Busy";
                statusDisplay.color = Color.yellow;
                break;
            case "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 pud)
    {
        _userData = pud;
        displayNameText.text = _userData.displayName;
    }

    public void UpdateUser(FriendsStatusNotif notification)
    {
        SetOnlineStatus(notification);

    }

    /// <summary>
    /// Setup UI Button Listener
    /// </summary>
    public void SetupButton()
    {
        inviteToPartyButton.onClick.AddListener(() =>
        {
            LobbyHandler.Instance.GetComponent<PartyHandler>().InviteToParty(_userData.userId);
        });
        unfriendButton.onClick.AddListener(() =>
        {
            AccelBytePlugin.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>().RefreshFriendsList();
                }
            });
        });
    }
}
  • Learn about our Chat service, which let’s party members interact with each other.
  • Check out our Matchmaking guide to see how parties can be matched to play against each other.
  • Players can form parties with their friends if you integrate our Friends service into your game.