メインコンテンツまでスキップ

オンラインサブシステムを使用してログインする - デバイス ID でログインする - (Unreal Engine モジュール)

Last updated on October 23, 2024

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

  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 is completed. This callback function will later be bound to the FOnLoginCompleteDelegate delegate provided by the Identity Interface. We will also pass our own event FAuthOnLoginCompleteDelegate_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);
  2. Next, open the AuthEssentialsSubsystem_Starter class CPP file and navigate to the UAuthEssentialsSubsystem_Starter::Login() function. Then, replace the logging in code with the code below. This function will perform a login using the IdentityInterface:

    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);
    });
    }
    }
  3. Then, create the OnLoginComplete() function definition. 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 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 errors.

Resources