Skip to main content

Unreal Engine Module - Login with Device ID - Use the Online Subsystem to log in

Last updated on January 13, 2024

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.

Note

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.

Note

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

  1. Open AccelByteWars.sln using Visual Studio and open the AuthEssentialsSubsystem_Starter class header file, create a new function declaration called OnLoginComplete as the callback that is used when the login request completed. This callback function will later be bind to the FOnLoginCompleteDelegate delegate provided by the Identity Interface. We will also pass our own event FAuthOnLoginCompleteDelegate_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);
  2. Next, open the AuthEssentialsSubsystem_Starter class CPP file and navigate to the UAuthEssentialsSubsystem_Starter::Login() function, then replace the logging code with the following implementation. This function will perform login using the IdentityInterface.

    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);
    });
    }
    }
  3. Then, create the OnLoginComplete() function definition as well. This function will invoke the FAuthOnLoginCompleteDelegate_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);
    }
  4. Build the AccelByteWars project and make sure there is no compile error.

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