Unreal Engine Module - Login with Device ID - Use the Online Subsystem to log in
Unwrap the Subsystem
In this section, you will learn how to implement login with Device ID using AccelByte OSS. This section assumes that you have configured the Device ID login method in Admin Portal.
In Byte Wars, we are using the Game Instance Subsystem namely UAuthEssentialsSubsystem
to act as the wrapper to cache and handle login-related functionalities when using the AccelByte OSS. Because it's derived from 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 detailed info about Subsystems please read more at Unreal Engine Programming Subsystems.
The UAuthEssentialsSubsystem
will mainly be using the Identity Interface from AccelByte OSS. The Identity Interface handles account-related interaction with AGS as a service providing the ability to authenticate users and obtain access tokens. Which later if the user authenticated properly, will allow us as a user to utilize the AGS features.
More on Identity Interface from Unreal Engine Online Subsystem Documentation.
The diagram below explains how the UAuthEssentialsSubsystem
will connect the Login widget to the AccelByte OSS.
In the Implement Login using AccelByte OSS section, you will learn how to call Identity Interface's Login function from UAuthEssentialsSubsystem
and then set up the OnLoginComplete
callback to receive the login response from AGS.
What's in the starter pack
Just as we instructed for the UI section of this tutorial, we are providing the starter class UAuthEssentialsSubsystem
for you to modify. Both are available in the Resources section.
- Header file:
/Source/AccelByteWars/TutorialModules/Access/AuthEssentials/AuthEssentialsSubsystem_Starter.h
- CPP file:
/Source/AccelByteWars/TutorialModules/Access/AuthEssentials/AuthEssentialsSubsystem_Starter.cpp
The starter class has some functionality already supplied for you:
Include Online Identity Interface in the header file.
#include "OnlineIdentityInterfaceAccelByte.h"
Pointer to AccelByte Identity Interface declared in the header file.
protected:
FOnlineIdentityAccelBytePtr IdentityInterface;Multicast delegate for the widget class to bind on when login completes is called from AccelByte OSS.
DECLARE_MULTICAST_DELEGATE_TwoParams(FAuthOnLoginComplete_Starter, bool /*bWasSuccessful*/, const FString& /*ErrorMessage*/);
typedef FAuthOnLoginComplete_Starter::FDelegate FAuthOnLoginCompleteDelegate_Starter;Account Credentials declaration in 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("");
}Login function to triggers the actual login. At this state, this function wouldn't do anything, since it has no implementation. You will be adding that implementation in the next section.
public:
void Login(const APlayerController* PC, const FAuthOnLoginCompleteDelegate_Starter& OnLoginComplete);Lastly, validation of both online subsystem and identity interface in
UAuthEssentialsSubsystem_Starter::Initialize()
.const IOnlineSubsystem* Subsystem = Online::GetSubsystem(GetWorld());
if (!ensure(Subsystem)) return;
IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(Subsystem->GetIdentityInterface());
if (!ensure(IdentityInterface.IsValid())) return;
Implement Login using AccelByte 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 completed. This callback function will later be bind to theFOnLoginCompleteDelegate
delegate provided by the Identity Interface. We will also pass our own eventFAuthOnLoginCompleteDelegate_Starter
that later will 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 code with the following implementation. This function will perform login using theIdentityInterface
.void UAuthEssentialsSubsystem_Starter::Login(const APlayerController* PC, const FAuthOnLoginCompleteDelegate_Starter& OnLoginComplete)
{
if (!ensure(IdentityInterface.IsValid()))
{
FString Message = TEXT("Cannot login. Identiy interface is not valid.");
UE_LOG_AUTH_ESSENTIALS(Warning, TEXT("%s"), *Message);
OnLoginComplete.ExecuteIfBound(false, *Message);
return;
}
// Get local user number
const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer();
ensure(LocalPlayer != nullptr);
int32 LocalUserNum = LocalPlayer->GetControllerId();
// Perform login using IdentityInterface
IdentityInterface->AddOnLoginCompleteDelegate_Handle(LocalUserNum, FOnLoginCompleteDelegate::CreateUObject(this, &ThisClass::OnLoginComplete, OnLoginComplete));
IdentityInterface->Login(LocalUserNum, Credentials);
// Helper to logout the user when the game shutdown in PIE mode.
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 as well. 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_Starter 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 error.
Congratulations! You have successfully implemented the subsystem to login. In the next section, we will use this implementation for the login menu widget, so the user can interact with a button to execute the login process.
Resources
- The files used in this tutorial section are available in the Byte Wars GitHub repository.