Skip to main content

Implement Lobby service for the Unreal Engine SDK

Last updated on October 27, 2023

Overview

danger

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

The Lobby service is part of the AccelByte services and provides continuous connection between your game and players by using WebSocket.

Prerequisites

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

To complete the steps in this guide, you need to:

  1. Implement the Authentication service and successfully test the flow.

  2. Set up your own Lobby Configurations in the Admin Portal.

Quick Reference

References
#include "Core/AccelByteRegistry.h"
#include "Api/AccelByteLobbyApi.h"
Lobby Connect Notification Events
FRegistry::Lobby.SetConnectionClosedDelegate(Api::Lobby::FConnectionClosed::CreateWeakLambda(this, [](int32 StatusCode, const FString& Reason, bool bWasClean)
{
// On Connection Closed
}));

FRegistry::Lobby.SetDisconnectNotifDelegate(Api::Lobby::FDisconnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsDisconnectNotif& Result)
{
// On Disconnect Notification
}));
Lobby Connect
FRegistry::Lobby.SetConnectSuccessDelegate(FSimpleDelegate::CreateWeakLambda(this, []()
{
// On Connect to Lobby Success
}));

FRegistry::Lobby.SetConnectFailedDelegate(FErrorHandler::CreateWeakLambda(this, [](int32 Code, const FString& Message)
{
// On Connect Failed
}));

FRegistry::Lobby.Connect();
Lobby Disconnect
FRegistry::Lobby.Disconnect();
Check if Lobby is Connected
FRegistry::Lobby.IsConnected()

Quickstart Guide

In this guide, you will learn how to use AGS Starter Lobby services.

Follow the steps below to start to use the Lobby services:

  1. Create a user widget C++ class called AccelByteLobby to specify the flow between functions when creating a Lobby menu.

  2. Add the AccelByte header to ensure the functions work correctly for the Lobby services:

    . . .  
    #include "Api/AccelByteLobbyApi.h"
    #include "Core/AccelByteRegistry.h"
    . . .
  3. Create a new function called SetLobbyNotificationDelegate() to notify the player when the Lobby Connection is updated. Once completed, put all the lobby-related delegates inside it. These delegates include:

    DelegatesResponse
    SetConnectSuccessDelegate()Lobby connection successful
    SetConnectFailedDelegate()Lobby connection failed
    SetConnectionClosedDelegate()Lobby connection process interrupted by a technical issue
    SetDisconnectNotifDelegate()Player logged out of the game
    NOTE

    At this point, although we make reference to the Lobby service, the Lobby is not yet connected. For now, we will just add debug logs in these tutorials to notify us of any updates after calling the references.

    After you have added the delegates, the function should look like this:

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

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

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

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

    FRegistry::Lobby.SetDisconnectNotifDelegate(Api::Lobby::FDisconnectNotif::CreateWeakLambda(this, [](const FAccelByteModelsDisconnectNotif& Result)
    {
    UE_LOG(LogTemp, Log, TEXT("Disconnected from Lobby"));
    }));
    }
  4. Create a new function called ConnectToLobby(). This will be triggered after Login is successful and will open a WebSocket that sets delegates to receive responses after requesting a connection to the Lobby service by calling SetLobbyNotificationDelegate().

    void UAccelByteLobby::ConnectToLobby()
    {
    SetLobbyNotificationDelegate();
    FRegistry::Lobby.Connect();
    }
  5. Test your code by running the function.

  6. After successfully connecting to the Lobby service, we can move to AccelByteAuth.cpp and add a call to the FRegistry::Lobby.Connect() function on the success delegate from LoginWithUsername(). You can find LoginWithUsername() inside OnLoginButtonClicked().

  7. Test the function by triggering OnLoginButtonClicked().

    void UAccelByteAuth::OnLoginButtonClicked()
    {
    ...
    FVoidHandler::CreateWeakLambda(this, []()
    {
    UE_LOG(LogTemp, Log, TEXT("Login success"));

    FRegistry::Lobby.Connect();
    if (FRegistry::Lobby.IsConnected())
    {
    UE_LOG(LogTemp, Log, TEXT("Lobby is connected!"));
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("Lobby is not connected!"));
    }
    }),
    ...
    }

    If you have configured your code correctly, you will see the message Lobby is connected! after logging in.

    NOTE

    If the Lobby is not connected successfully, check your game project configuration and make sure the user is logged in. As long as the Lobby is connected, you can use all the Lobby services available.

  8. (Optional) By default, if you quit your application, you are automatically disconnected from the Lobby. To manually disconnect without closing the application, use the OnLogoutButtonClicked() function in the AccelByteAuth.cpp.

    void UAccelByteAuth::OnLogoutButtonClicked()
    {
    ...
    FRegistry::Lobby.Disconnect();
    ...
    }

Congratulations! You have successfully connected to the Lobby.

Read on for a step-by-step guide to UI and code implementation. Otherwise, you are now ready to move on to the Friends service.

Step-by-Step Guide

Implement The UI

NOTE

The following steps are optional as the Lobby service can run without any widgets implemented.

In this step-by-step guide, we will implement the HUD to get the Lobby Class reference. This means we need to create a Lobby Menu widget but, for now, we will leave it empty.

  1. Create an empty widget blueprint class called WB_LobbyMenu.

  2. Assign the AccelByteLobby as the parent class of the WB_LobbyMenu.

Implement The Code

In this section, we will show you what you need to do to change between the Login and Lobby widgets using a HUD.

TutorialMenuHUD

  1. Create a HUD Blueprint class called BP_TutorialHUD.

  2. Create a HUD C++ class called TutorialMenuHUD and add the class as a parent class for BP_TutorialHUD.

  3. Open the TutorialMenuHUD class and add the following headers at the top of the class:

    ...
    #include "AccelByte/Authentication/AccelByteAuth.h"
    #include "AccelByte/Lobby/AccelByteLobby.h"
  4. Add the BeginPlay() function to check and set all of your class pointers.

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

    APlayerController* PlayerController = GetOwningPlayerController();

    check(LoginMenuClass != nullptr);
    check(LobbyMenuClass != nullptr);

    LoginMenu = CreateWidget<UAccelByteAuth>(PlayerController, LoginMenuClass.Get());
    LobbyMenu = CreateWidget<UAccelByteLobby>(PlayerController, LobbyMenuClass.Get());
    }

  5. Add the Login and Lobby's C++ class in the BP_TutorialHUD under the pointer variables you declared in the BeginPlay() function.

NOTE

You will need to repeat Step 5 in order to add additional UI-related user widgets for in-game stores, inventories, leaderboards, etc.

  1. Add const functions that will act as getters for the widgets.

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

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

  2. Add some functions that will be called to change to the desired menu page. Since we only have Login and Lobby widgets, create two new functions to open these widgets:

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

    void ATutorialMenuHUD::OpenLobbyMenu()
    {
    LobbyMenu->SetVisibility(ESlateVisibility::Visible);
    }
  3. Add another function that will be used to destroy the widgets when you close the page.

     void ATutorialMenuHUD::CloseLobbyMenu()
    {
    LobbyMenu->SetVisibility(ESlateVisibility::Collapsed);
    }
  4. Inside the BeginPlay()function, check the current session's userId. If it is empty, call the OpenLoginMenu() function.

    void ATutorialMenuHUD::BeginPlay()
    {
    ...
    if (FRegistry::Credentials.GetUserId().IsEmpty())
    {
    OpenLoginMenu();
    }
    }
  1. Open the AccelByteAuth class and header file and add the TutorialMenuHUD class.

    ...
    #include "TutorialProject/TutorialMenuHUD.h"
  2. In the AccelByteAuth.cpp, define the TutorialMenuHUD's pointer in the NativeConstruct() function.

    void UAccelByteAuth::NativeConstruct()
    {
    ...
    TutorialMenuHUD = Cast<ATutorialMenuHUD>(GetWorld()->GetFirstPlayerController()->GetHUD());
    ...
    }
  3. Inside the LoginSuccess() function, call the ConnectToLobby() function with the Lobby getter.

    void UAccelByteAuth::LoginSuccess()
    {
    ...
    if(GetOwningPlayer() && GetOwningPlayer()->GetHUD())
    {
    TutorialMenuHUD->GetLobbyMenu()->ConnectToLobby();
    this->RemoveFromParent();
    }
    }
  4. Add the Lobby Disconnect function inside the OnLogoutButtonClicked() function in AccelByteAuth.cpp.

    void UAccelByteAuth::OnLogoutButtonClicked()
    {
    ...

    if (FRegistry::Lobby.IsConnected())
    {
    FRegistry::Lobby.Disconnect();
    }
    }

Congratulations! You have now fully implemented the Lobby, which is the gateway to all other AGS Starter services.

Go to the next section to learn how to implement AGS Starter Friends service.

Full Code for reference

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

#pragma once

#include "CoreMinimal.h"
#include "AccelByteLobby.generated.h"

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

#pragma region Utilities

public:

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

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

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

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

#pragma region Utilities

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

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

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

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

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

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

#pragma endregion
`TutorialMenuHUD.h`
// Copyright (c) 2021 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#pragma once

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

class UAccelByteAuth;
class UAccelByteLobby;

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

protected:

virtual void BeginPlay() override;

public:

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

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

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

protected:

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

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

public:

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

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

private:

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

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

#include "TutorialMenuHUD.h"
#include "AccelByte/Authentication/AccelByteAuth.h"
#include "AccelByte/Lobby/AccelByteLobby.h"

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

APlayerController* PlayerController = GetOwningPlayerController();

check(LoginMenuClass != nullptr);
check(LobbyMenuClass != nullptr);

LoginMenu = CreateWidget<UAccelByteAuth>(PlayerController, LoginMenuClass.Get());
LobbyMenu = CreateWidget<UAccelByteLobby>(PlayerController, LobbyMenuClass.Get());

if (FRegistry::Credentials.GetUserId().IsEmpty())
{
OpenLoginMenu();
}
}

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

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

void ATutorialMenuHUD::CloseLobbyMenu()
{
LobbyMenu->SetVisibility(ESlateVisibility::Collapsed);
}
`AccelByteAuth.h`
// Copyright (c) 2021 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "AccelByteAuth.generated.h"

class UVerticalBox;
class UEditableTextBox;
class UButton;
class ATutorialMenuHUD;

/**
* Component for logging in a user with the AccelByte back end as well as methods for grabbing information relating to that user.
* This code covers AccelByte services including :
*
* - Login with username
*/

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

protected:

virtual void NativeConstruct() override;

/**
* @brief Editable Text Box for Username inside MainMenu Widget.
*/
UPROPERTY(meta = (BindWidget))
UEditableTextBox* Etb_Username;

/**
* @brief Editable Text Box for Password inside MainMenu Widget.
*/
UPROPERTY(meta = (BindWidget))
UEditableTextBox* Etb_Password;

/**
* @brief Take Button Login inside MainMenu Widget.
*/
UPROPERTY(meta = (BindWidget))
UButton* Btn_Login;

/**
* @brief Text Block to display Login Status
*/
UPROPERTY(meta = (BindWidget))
UTextBlock* T_LoginStatus;

/**
* @brief Instantiate all casting to the main menu HUD
*/
ATutorialMenuHUD* TutorialMenuHUD;

public:

/**
* @brief Log an account in using the AccelByte SDK. This is executed automatically on component construction unless
* otherwise configured.
*/
UFUNCTION()
void OnLoginButtonClicked();

/**
* @brief Logout a session using the AccelByte SDK. This is executed automatically on component construction unless
* otherwise configured.
*/
UFUNCTION()
void OnLogoutButtonClicked();

private:

/**
* @brief Function behaviour when Login success. This function called inside AccelByte Login OnSuccess
* delegates inside lambda. Can be improved or changed how login behaviour works inside this function.
*/
void LoginSuccess();

/**
* @brief Function behaviour when Login failed. This function called inside AccelByte Login OnFailed
* delegates inside lambda. Can be improved or changed how login behaviour works inside this function.
*
* @param ErrorCode error code HTTP request. e.g 404.
* @param ErrorMessage error message HTTP request. e.g Unauthorized.
* @param ErrorJson error message for OAuth
*/
void LoginFailed(int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorJson);

/**
* @brief Function behaviour when Logout success. This function called inside AccelByte Logout OnSuccess
* delegates inside lambda. Can be improved or changed how Logout behaviour works inside this function.
*/
void LogoutSuccess();

/**
* @brief Function behaviour when Logout failed. This function called inside AccelByte Logout OnFailed
* delegates inside lambda. Can be improved or changed how Logout behaviour works inside this function.
*
* @param ErrorCode error code HTTP request. e.g 404.
* @param ErrorMessage error message HTTP request. e.g Unauthorized.
*/
void LogoutFailed(int32 ErrorCode, const FString& ErrorMessage);

};
`AccelByteAuth.cpp`
// Copyright (c) 2021 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#include "AccelByteAuth.h"
#include "Api/AccelByteUserApi.h"
#include "Core/AccelByteRegistry.h"
#include "Components/Button.h"
#include "Components/EditableTextBox.h"
#include "Components/TextBlock.h"
#include "TutorialProject/TutorialMenuHUD.h"

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

TutorialMenuHUD = Cast<ATutorialMenuHUD>(GetWorld()->GetFirstPlayerController()->GetHUD());

UE_LOG(LogTemp, Log, TEXT("Login with Username"));

T_LoginStatus->SetText(FText::FromString("Please Login"));
Btn_Login->OnClicked.AddUniqueDynamic(this, &UAccelByteAuth::OnLoginButtonClicked);
}

void UAccelByteAuth::OnLoginButtonClicked()
{
T_LoginStatus->SetText(FText::FromString("Logging in..."));

FRegistry::User.LoginWithUsername(
Etb_Username->GetText().ToString(),
Etb_Password->GetText().ToString(),
FVoidHandler::CreateUObject(this, &UAccelByteAuth::LoginSuccess),
FCustomErrorHandler::CreateUObject(this, &UAccelByteAuth::LoginFailed));
}

void UAccelByteAuth::OnLogoutButtonClicked()
{
FRegistry::User.Logout(
FVoidHandler::CreateUObject(this, &UAccelByteAuth::LogoutSuccess),
FErrorHandler::CreateUObject(this, &UAccelByteAuth::LogoutFailed));

if (FRegistry::Lobby.IsConnected())
{
FRegistry::Lobby.Disconnect();
}
}

void UAccelByteAuth::LoginSuccess()
{
UE_LOG(LogTemp, Log, TEXT("Login Success"));
T_LoginStatus->SetText(FText::FromString("Login successful"));

if(GetOwningPlayer() && GetOwningPlayer()->GetHUD())
{
TutorialMenuHUD->GetLobbyMenu()->ConnectToLobby();
this->RemoveFromParent();
}
}

void UAccelByteAuth::LoginFailed(int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorJson)
{
UE_LOG(LogTemp, Error, TEXT("Login Failed : %d , %s"), ErrorCode, *ErrorMessage);
T_LoginStatus->SetText(FText::FromString(FString::Printf(TEXT("Login Failed : %d , %s"), ErrorCode, *ErrorMessage)));
}

void UAccelByteAuth::LogoutSuccess()
{
UE_LOG(LogTemp, Log, TEXT("Logout Success"));
}

void UAccelByteAuth::LogoutFailed(int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Logout Failed : %d , %s"), ErrorCode, *ErrorMessage);
}