Use the Online Subsystem to log in - Login with device ID - (Unreal Engine module)
Unwrap the Online Subsystem
In this tutorial, you will learn how to implement logins with device IDs using the AccelByte Gaming Services (AGS) Online Subsystem (OSS).
Byte Wars uses the Game Instance Subsystem named UAuthEssentialsSubsystem
to act as the wrapper to cache and handle login-related functions when using the AGS OSS. Because it's derived from the Game Instance Subsystem, it's easy to access, has the same lifetime of the game instance, and provides modularity without overriding engine classes.
For more info about Subsystems, read Unreal Engine Programming Subsystems.
The UAuthEssentialsSubsystem
will mainly be using the Identity Interface from the AGS OSS. The Identity Interface handles account-related interactions with AGS as a service providing the ability to authenticate users and obtain access tokens, which will later allow a user to utilize the AGS features if they authenticated properly.
Read more on Identity Interface from Unreal Engine Online Subsystem Documentation.
The diagram below explains how the UAuthEssentialsSubsystem
will connect the login widget to the AGS OSS.
What's in the starter pack
The starter class UAuthEssentialsSubsystem
for you to modify is provided for you in the Resources section and consists of:
- A Header file:
/Source/AccelByteWars/TutorialModules/Access/AuthEssentials/AuthEssentialsSubsystem_Starter.h
- A CPP file:
/Source/AccelByteWars/TutorialModules/Access/AuthEssentials/AuthEssentialsSubsystem_Starter.cpp
The starter class includes the following functionalities:
Include Online Identity Interface in the Header file:
// ...
#include "OnlineIdentityInterfaceAccelByte.h"A pointer to AccelByte Identity Interface declared in the Header file:
protected:
// ...
FOnlineIdentityAccelBytePtr IdentityInterface;A multicast delegate for the widget class to bind on when a login completes is called from AGS OSS:
DECLARE_MULTICAST_DELEGATE_TwoParams(FAuthOnLoginComplete, bool /*bWasSuccessful*/, const FString& /*ErrorMessage*/);
typedef FAuthOnLoginComplete::FDelegate FAuthOnLoginCompleteDelegate;Account Credentials declaration in the Header file:
protected:
// ...
FOnlineAccountCredentials Credentials;Helper functions to set and clear Credentials:
void UAuthEssentialsSubsystem_Starter::SetAuthCredentials(const EAccelByteLoginType& LoginMethod, const FString& Id, const FString& Token)
{
Credentials.Type = (LoginMethod == EAccelByteLoginType::None) ? TEXT("") : FAccelByteUtilities::GetUEnumValueAsString(LoginMethod);
Credentials.Id = Id;
Credentials.Token = Token;
}void UAuthEssentialsSubsystem_Starter::ClearAuthCredentials()
{
Credentials.Type = TEXT("");
Credentials.Id = TEXT("");
Credentials.Token = TEXT("");
}A login function to trigger the login:
public:
// ...
void Login(const APlayerController* PC, const FAuthOnLoginCompleteDelegate& OnLoginComplete);Validation of both the Online Subsystem and Identity Interface in
UAuthEssentialsSubsystem_Starter::Initialize()
:void UAuthEssentialsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
// Get Online Subsystem and make sure it's valid.
FOnlineSubsystemAccelByte* Subsystem = static_cast<FOnlineSubsystemAccelByte*>(Online::GetSubsystem(GetWorld()));
if (!ensure(Subsystem))
{
UE_LOG_AUTH_ESSENTIALS(Warning, TEXT("The online subsystem is invalid. Please make sure OnlineSubsystemAccelByte is enabled and the DefaultPlatformService under [OnlineSubsystem] in the Engine.ini file is set to AccelByte."));
return;
}
// Grab the reference of AccelByte Identity Interface and make sure it's valid.
IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(Subsystem->GetIdentityInterface());
if (!ensure(IdentityInterface.IsValid()))
{
UE_LOG_AUTH_ESSENTIALS(Warning, TEXT("Identity interface is not valid."));
return;
}
// ...
}
Implement login using AGS OSS
Open
AccelByteWars.sln
using Visual Studio and open theAuthEssentialsSubsystem_Starter
class Header file. Create a new function declaration calledOnLoginComplete
as the callback that is used when the login request is completed. This callback function will later be bound to theFOnLoginCompleteDelegate
delegate provided by the Identity Interface. We will also pass our own eventFAuthOnLoginCompleteDelegate_Starter
that will later be triggered, so the login widget can set the UI based on the login result.private:
void OnLoginComplete(int32 LocalUserNum, bool bLoginWasSuccessful, const FUniqueNetId& UserId, const FString& LoginError, const FAuthOnLoginCompleteDelegate_Starter OnLoginComplete);Next, open the
AuthEssentialsSubsystem_Starter
class CPP file and navigate to theUAuthEssentialsSubsystem_Starter::Login()
function. Then, replace the logging in code with the code below. This function will perform a login using theIdentityInterface
:void UAuthEssentialsSubsystem_Starter::Login(const APlayerController* PC, const FAuthOnLoginCompleteDelegate& OnLoginComplete)
{
if (!ensure(IdentityInterface.IsValid()))
{
FString Message = TEXT("Cannot login. Identity interface is not valid.");
UE_LOG_AUTH_ESSENTIALS(Warning, TEXT("%s"), *Message);
OnLoginComplete.ExecuteIfBound(false, *Message);
return;
}
const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
ensure(LocalPlayer != nullptr);
int32 LocalUserNum = LocalPlayer->GetControllerId();
IdentityInterface->AddOnLoginCompleteDelegate_Handle(LocalUserNum, FOnLoginCompleteDelegate::CreateUObject(this, &UAuthEssentialsSubsystem_Starter::OnLoginComplete, OnLoginComplete));
IdentityInterface->Login(LocalUserNum, Credentials);
/*
* Logout On Game Exit
* Workaround for the lobby not properly disconnecting when closing the PIE game.
*/
if (UAccelByteWarsGameInstance* ByteWarsGameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance()); ensure(ByteWarsGameInstance))
{
ByteWarsGameInstance->OnGameInstanceShutdownDelegate.AddWeakLambda(this, [this, LocalUserNum]()
{
IdentityInterface->Logout(LocalUserNum);
UE_LOG_AUTH_ESSENTIALS(Warning, TEXT("Logging out local player %d"), LocalUserNum);
});
}
}Then, create the
OnLoginComplete()
function definition. This function will invoke theFAuthOnLoginCompleteDelegate_Starter
to inform whether the login process was successful or not.void UAuthEssentialsSubsystem_Starter::OnLoginComplete(int32 LocalUserNum, bool bLoginWasSuccessful, const FUniqueNetId& UserId, const FString& LoginError, const FAuthOnLoginCompleteDelegate OnLoginComplete)
{
if (bLoginWasSuccessful)
{
UE_LOG_AUTH_ESSENTIALS(Log, TEXT("Login user successful."));
}
else
{
UE_LOG_AUTH_ESSENTIALS(Warning, TEXT("Login user failed. Message: %s"), *LoginError);
}
IdentityInterface->ClearOnLoginCompleteDelegates(LocalUserNum, this);
OnLoginComplete.ExecuteIfBound(bLoginWasSuccessful, LoginError);
}Build the AccelByteWars project and make sure there is no compile errors.
Resources
- The files used in this tutorial section are available in the Byte Wars Unreal GitHub repository.