Take a sneak peak of our new documentation Read More
Last Updated: 4/5/2023, 10:32:25 PM

# AccelByte Cloud OSS for Unreal Engine

# Overview

The AccelByte Cloud Online Subsystem (opens new window) (OSS) is the high-level bridge between Unreal Engine and AccelByte services that comprises interfaces that access AccelByte services and its features. The AccelByte Cloud OSS is designed to handle higher level logic with asynchronous communication and delegates (opens new window), and is designed to be modular by grouping similar service-specific APIs that support features together, allowing:

  • Game developers to use the features provided by the Online Services provider and combine them with first-party platform services.
  • Service developers, such as AccelByte, to develop features by creating implementation that uses multiple APIs together.

# Prerequisites

NOTE

All versions of the AccelByte Cloud OSS support Unreal Engine 4.26 and 4.27. OSS version 0.6.0 and above only support Unreal Engine 5.

To complete the steps in this document you need to:

# Configuration

  1. Add the following required plugins to uproject file.

  2. Add public dependency modules to the Build.cs file.

  3. Add the following module to the Target.cs file.

  4. Edit the DefaultEngine.ini file to include the following settings.

  5. In the same DefaultEngine.ini file, enable the AccelByte NetDriver.

  6. Edit your platform-specific config .ini file located inside your platform’s folder (e.g., Config/Windows/WindowsEngine.ini).

# LocalUserNum

The AccelByte Cloud OSS is able to handle multiple local users playing the same game instance by identifying the controller index of the players. The same controller index is defined as LocalUserNum in most OSS interfaces. The AccelByte Cloud OSS stores users’ online information using LocalUserNum as the key.

# UniqueNetId

UniqueNetId is the abstraction of the user’s profile in online services. In AccelByte Cloud OSS, it is derived as AccelByte Composite UniqueNetId and consists of AccelByte userId, originating platform, and originating platform userId data fields.

# Interfaces

The AccelByte Cloud OSS expands upon Unreal Engine OSS’s predefined interfaces, giving developers the ability to further extend and integrate AccelByte Cloud online services into their games. These interfaces wrap APIs in asynchronous calls so your game can still run as usual.

# Online Identity Interface

Online Identity Interface is used for player authentication and profiles.

# Login

The AccelByte Cloud OSS offers several different login types based on your native platform, or with other platforms by providing the type and credentials in the FOnlineAccelByteAccountCredentials class. There are several different login types available in our OSS. These login types are categorized in enumerations in EAccelByteLoginType. The types include:

  • DeviceId
  • AccelByte
  • Xbox
  • PS4
  • PS5
  • Launcher
  • Steam
  • RefreshToken
const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
FOnlineIdentityInterfacePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(OnlineSubsystem->GetIdentityInterface());

if (IdentityInterface.IsValid())
{
   FOnlineAccelByteAccountCredentials Credentials{ EAccelByteLoginType::AccelByte
         , Username, Password };
   // Login
   IdentityInterface->AddOnLoginCompleteDelegate_Handle(LocalUserNum
      , FOnLoginCompleteDelegate::CreateLambda([](int32 LocalUserNum, bool bLoginWasSuccessful, const FUniqueNetId& UserId, const FString& LoginError)
         {
            if (bLoginWasSuccessful)
            {
               // Do something when player successfully logged in
            }
            else
            {
               // Do something when player failed to log in
            }
         })
      );
   IdentityInterface->Login(LocalUserNum, Credentials);
}

After successfully logging in, a player will need to connect to the Lobby to be able to use social-related interfaces such as Friends, Party, and Presence. You can connect to these manually by calling ConnectAccelByteLobby, or by setting bAutoLobbyConnectAfterLoginSuccess to true in DefaultEngine.ini.

const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
FOnlineIdentityInterfacePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(OnlineSubsystem->GetIdentityInterface());

if (IdentityInterface.IsValid())
{
   // Connect to AccelByte Lobby Websocket, can be automatically called after
   // successful login by configuring bAutoLobbyConnectAfterLoginSuccess to true
   // in DefaultEngine.ini
   IdentityInterface->ConnectAccelByteLobby(LocalUserNum);
}

# Logout

When the player is finished playing, log out using the following code.

const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
FOnlineIdentityInterfacePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(OnlineSubsystem->GetIdentityInterface());

if (IdentityInterface.IsValid())
{
   // Logout
   IdentityInterface->AddOnLogoutCompleteDelegate_Handle(LocalUserNum
      , FOnLogoutCompleteDelegate::CreateLambda([](int32 LocalUserNum, bool bLogoutWasSuccessful)
         {
            if (bLogoutWasSuccessful)
            {
               // Do something when player successfully logged out
            }
            else
            {
               // Do something when player failed to log out
            }
         })
      );
   IdentityInterface->Logout(LocalUserNum);
}

# Online Agreement Interface

The Online Agreement Interface is used to query or accept the legal agreement policies of players.

# User Not Complied

After a player successfully logs in, the Online Agreement Interface will check whether the player has complied with all the required legal policies. If the player has complied (as is the usual flow), it will trigger OnLoginCompleteDelegates and the player login flow will be complete. However, if a player has not complied with one or more required legal policies, it will trigger OnUserNotCompliedDelegates and the player will be unable to use the services.

const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
const FOnlineSubsystemAccelByte* ABSubsystem = static_cast<const FOnlineSubsystemAccelByte*>(Subsystem);
FOnlineAgreementAccelBytePtr AgreementInterface = ABSubsystem->GetAgreementInterface();

if (AgreementInterface.IsValid())
{
   AgreementInterface->AddOnUserNotCompliedDelegate_Handle(LocalUserNum
      , FOnUserNotCompliedDelegate::CreateLambda([]()
         {
            // Do something when player doesn’t comply with the legal agreements
         })
      );
}

# Query Eligible Agreements

After getting OnUserNotCompliedDelegates, the player will receive a list of eligible agreement policies. The player can use the QueryEligibleAgreements function to get the full list of the eligible policies, or they can filter to see only the agreement policies that have not yet been accepted. By default, this function will attempt to retrieve the list from the cache if it exists, and only request it from the service when it can’t be found on the cached list. However, the player can also force it to always request it from the service without looking on the cached list. Once completed, the function will trigger OnQueryEligibilitiesCompletedDelegates.

const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
const FOnlineSubsystemAccelByte* ABSubsystem = static_cast<const FOnlineSubsystemAccelByte*>(Subsystem);
FOnlineAgreementAccelBytePtr AgreementInterface = ABSubsystem->GetAgreementInterface();

if (AgreementInterface.IsValid())
{
   AgreementInterface->AddOnQueryEligibilitiesCompletedDelegate_Handle(LocalUserNum
      , FOnQueryEligibilitiesCompletedDelegate::CreateLambda([](int32 LocalUserNum, bool bWasSuccessful, const TArray<FAccelByteModelsRetrieveUserEligibilitiesResponse>& Response, const FString& ErrMessage)
         {
             UE_LOG(LogTemp, Log, TEXT("List of mandatory policies that need to accepted by user."));
            for (auto Policy : Response)
            {
               if (Policy.IsMandatory && !Policy.IsAccepted)
               {
                  UE_LOG(LogTemp, Log, TEXT("%s\n"), *Policy.PolicyId);
               }
            }
         })
      );

   // To get only the not yet accepted policies
   bool bNotAcceptedOnly = true;

   // To always request it to service instead of get it from cached list
   bool bAlwaysRequestToService = true;

   AgreementInterface->QueryEligibleAgreements(LocalUserNum
      , bNotAcceptedOnly
      , bAlwaysRequestToService);
}

# Get Localized Policy Contents

After retrieving the list of required policies, the player needs to be able to read the policy contents. Retrieve the localized policy content by using GetLocalizedPolicyContent with the policy id and their locale code. Like other queries, GetLocalizedPolicyContent will first check on the cached contents by default, and if it does not exist yet, will request it from the service. Once completed, the function will trigger OnGetLocalizedPolicyContentCompletedDelegates.

const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
const FOnlineSubsystemAccelByte* ABSubsystem = static_cast<const FOnlineSubsystemAccelByte*>(Subsystem);
FOnlineAgreementAccelBytePtr AgreementInterface = ABSubsystem->GetAgreementInterface();

if (AgreementInterface.IsValid())
{
   AgreementInterface->AddOnGetLocalizedPolicyContentCompletedDelegate_Handle(LocalUserNum
      , FOnGetLocalizedPolicyContentCompletedDelegate::CreateLambda([](int32 LocalUserNum, bool bWasSuccessful, const FString& Response, const FString& Error)
         {
            if (bWasSuccessful)
            {
               UE_LOG(LogTemp, Log, TEXT("Document: %s"), *Response);
            }
         })
      );

   // To always request it to service instead of get it from cached documents
   bool bAlwaysRequestToService = true;
   FString LocaleCode = "en";

   // You should be able to get the policy id from the eligible agreement list
   FString PolicyId = "";

   AgreementInterface->GetLocalizedPolicyContent(LocalUserNum
      , PolicyId
      , LocaleCode
      , bAlwaysRequestToService);
}

# Accepting Agreement Policies

After the player has read the policy content, they will be able to accept the policy if it has not yet been accepted. Accept policies in bulk by inputting the list by policy id and locale code. Once completed, the function will trigger OnAcceptAgreementPoliciesCompletedDelegates. If there are any mandatory documents on the list, it will also trigger the login and refresh the user token so the player will be able to use the services immediately after accepting the policies.

const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
const FOnlineSubsystemAccelByte* ABSubsystem = static_cast<const FOnlineSubsystemAccelByte*>(Subsystem);
FOnlineAgreementAccelBytePtr AgreementInterface = ABSubsystem->GetAgreementInterface();

if (AgreementInterface.IsValid())
{
   AgreementInterface->AddOnAcceptAgreementPoliciesCompletedDelegate_Handle(LocalUserNum
      , FOnAcceptAgreementPoliciesCompletedDelegate::CreateLambda([](int32 LocalUserNum, bool bWasSuccessful, const FString& Error)
      {
         if (!bWasSuccessful)
         {
            UE_LOG(LogTemp, Log, TEXT("Error: %s"), *Error);
         }
      })
   );

   TArray<FOnlineAgreementAccelByte::FABAcceptAgreementPoliciesRequest> DocumentToAccept;
   FString LocaleCode = "en";

   // You should be able to get the policy id from the eligible agreement list
   FString PolicyId = "";

   DocumentToAccept.Add({ PolicyId, LocaleCode });

   AgreementInterface->AcceptAgreementPolicies(LocalUserNum, DocumentToAccept);
}