Multiple Registries
Overview
Overview
Multiple Registries is a feature designed to support multiple local users logging in to the same game instance in AccelByte Cloud’s services. Having multiple registries allows game developers to create games that can support local multiplayer. It stores player information and progression for each player independently based on the account linked with the game controller.
Multiple registries are effectively containers of API clients that store logged-in player information and act as a player when calling AccelByte Cloud’s APIs.
The current SDK had limitations as it used a singleton class to represent a player, thus only allowing one account to log in at any given time within the same game instance. To overcome these limitations, we created a new class, ApiClient
to represent a player. It serves the same purpose and uses the same entry point to access AccelByte Cloud’s APIs. Game developers will need to use this new class and the multiple registries will maintain this class. The benefits of using the ApiClient
infrastructure include:
- Reduces singleton and/or static class usage.
- Easier to manage for developers, who can easily create and/or delete
ApiClient
instances. - More extensible as the
ApiClient
is used as the entry point to create custom API classes.
After you have implemented the ApiClient
class, the API calls operate in a similar manner to the previous SDK’s. These SDK changes will also impact game server implementation (which was previously mixed between client and server implementation). A new class called ServerApiClient
has therefore been created to focus on managing game server-related APIs.
Prerequisites
Before you can implement multiple registries, you will need to install the plugins and modules of the game engine you’re using. Choose either:
If you want to use the AccelByte Cloud Online Subsystem (OSS) in Unreal Engine, you will need these additional plugins:
Configuration
You also need to configure the SDK before applying to implement multiple registries in your game. For more information about AGS SDKs, refer to the following guides:
Implement Multiple Registries Using the Client SDKs
How to Implement Multiple Registries
- Include the following library in the top of your class before implementing multiple registries.
- Unreal
- Unreal OSS
- Unity
#include "Core/AccelByteMultiRegistry.h"
#include "OnlineSubsystem.h"
#include "OnlineSubsystemUtils.h"
using AccelByte.Api;
using AccelByte.Core;
The ApiClient represents a player in your game and holds the additional APIs needed to create and send requests to the backend services.
Define each player in this class so each player can be isolated by calling
GetApiClient()
, using an unique key for each player.NOTEFor the unique key, use the following rule:
- Unreal: use FString with the default as default.
- Unreal (OSS): use int with no default value.
- Unity: use string with the default as default. Make sure to use the same key if you want to call the function in other classes. :::
- Unreal
- Unreal OSS
- Unity
// Define User A
FApiClientPtr ApiClientA = FMultiRegistry::GetApiClient(TEXT("0"));
// Define User B
FApiClientPtr ApiClientB = FMultiRegistry::GetApiClient(TEXT("1"));
// Define AccelByte's Online Subsystem
const IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
const IOnlineIdentityPtr IdentityInterface = OnlineSub->GetIdentityInterface();
// Define Player A for LocalUserNum
const int PlayerNumA = 0; // User key A
// Define Player B for LocalUserNum
const int PlayerNumB = 1; // User key B
// Define User A
ApiClient apiClientA = MultiRegistry.GetApiClient("0");
// Define User B
ApiClient apiClientB = MultiRegistry.GetApiClient("1");
- As this is a gateway, you will need to authenticate each player before they can access any AccelByte Cloud services. To do this, set up AccelByte Cloud’s login method. You can use any login method that AccelByte Cloud allows, such as the
username
method in the example below:
- Unreal
- Unreal OSS
- Unity
// Define User
FApiClientPtr ApiClientA = FMultiRegistry::GetApiClient(TEXT("0"));
FApiClientPtr ApiClientB = FMultiRegistry::GetApiClient(TEXT("1"));
// Login with username
ApiClientA->User.LoginWithUsername(
TEXT("user+a@example.com"),
TEXT("Password321"),
FVoidHandler::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Login User A successful"));
}), FCustomErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorObject)
{
UE_LOG(LogTemp, Error, TEXT("Login User A Failed : %d, %s"), ErrorCode, *ErrorMessage);
}));
ApiClientB->User.LoginWithUsername(
TEXT("user+b@example.com"),
TEXT("Password321"),
FVoidHandler::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Login User B successful"));
}), FCustomErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorObject)
{
UE_LOG(LogTemp, Error, TEXT("Login User B Failed : %d, %s"), ErrorCode, *ErrorMessage);
}));
// Define AccelByte's Online Subsystem
const IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
const IOnlineIdentityPtr IdentityInterface = OnlineSub->GetIdentityInterface();
// Define Player 1
const int PlayerNumA = 0; // User key A
const int PlayerNumB = 1; // User key B
FOnlineAccountCredentials AccountCredentialsA;
AccountCredentialsA.Type = "AccelByte";
AccountCredentialsA.Id = TEXT("user+a@example.com");
AccountCredentialsA.Token = TEXT("Password321");
FOnlineAccountCredentials AccountCredentialsB;
AccountCredentialsB.Type = "AccelByte";
AccountCredentialsB.Id = TEXT("user+b@example.com");
AccountCredentialsB.Token = TEXT("Password321");
// Set login complete delegate
IdentityInterface->AddOnLoginCompleteDelegate_Handle(PlayerNumA, FOnLoginCompleteDelegate::CreateWeakLambda(this, []
(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{
UE_LOG(LogTemp, Log, TEXT("Login User A successful"));
}));
IdentityInterface->AddOnLoginCompleteDelegate_Handle(PlayerNumB, FOnLoginCompleteDelegate::CreateWeakLambda(this, []
(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{
UE_LOG(LogTemp, Log, TEXT("Login User B successful"));
}));
// Login with username
IdentityInterface->Login(PlayerNumA, AccountCredentialsA);
IdentityInterface->Login(PlayerNumB, AccountCredentialsB);
// Define User
ApiClient apiClientA = MultiRegistry.GetApiClient("0");
ApiClient apiClientB = MultiRegistry.GetApiClient("1");
User userA = apiClientA.GetApi<User, UserApi>();
User userB = apiClientB.GetApi<User, UserApi>();
// Login with username
userA.LoginWithUsername(
"user+a@example.com",
"Password321",
(Result<TokenData, OAuthError> result) =>
{
if (!result.IsError)
{
// show the login result
Debug.Log("Login player A successful");
}
else
{
Debug.Log("Login failed:" + result.IsError);
}
});
userB.LoginWithUsername(
"user+b@example.com",
"Password321",
(Result<TokenData, OAuthError> result) =>
{
if (!result.IsError)
{
// show the login result
Debug.Log("Login player B successful");
}
else
{
Debug.Log("Login failed:" + result.IsError);
}
});
If you are implementing multiple registries in the OSS, to differentiate each player, you need to use LocalUserNum
as a user key in every function call and set delegate.
:::
UI Implementation
The following diagram shows the flow you need to use an API in Unreal Engine to call the Login with Username
API.
Your UI implementation will may vary depending on your game and the login method you choose.
Follow the steps below to create this flow:
Call
Get Api Client
.On the
Return Value
, call the API you want to use. In this case, it will be theUser
API.Once this API has been successfully called, you can call the
Login with Username
API.Once the
Login with Username
API has been successfully called, your setup is complete.
Migrate to Multiple Registries
Overview
With the Multiple Registries feature, AccelByte Cloud supports multiplayer in a single game process. Use this guide to migrate your game from a singleton-based registry to multiple registries so your game can support multiple registries.
Unity
First of all, we assume all the APIs called from our SDK are called from singleton interfaces, which are the AccelBytePlugin
and AccelByteServerPlugin
classes.
This guide will help to transition the following APIs from singleton usage:
AccelBytePlugin
intoApiClient
.AccelByteServerPlugin
intoServerApiClient
.
Game Client
- Choose a key that represents the player identifier if you have multiple players in a single game process/execution (i.e., local co-op game).
- If there is only one player in one game process/execution, then there is no need to specify a key.
- Use Find all to locate all the
AccelBytePlugin.*
singleton classes in your project and replaceAccelBytePlugin.
withMultiRegistry.GetApiClient().
.
If you have multiple local players in the same game instance, replace AccelBytePlugin.
with MultiRegistry.GetApiClient("<INSERT_PLAYER_IDENTIFIER_HERE>").
.
- Check the compilation still runs after renaming.
- API Client
- Previous Usage
User user = MultiRegistry.GetApiClient().GetUser();
user.LoginWithUsername(
"user+a@example.com",
"Password321",
(Result<TokenData, OAuthError> result) =>
{
if (!result.IsError)
{
// show the login result
Debug.Log("Login player A successful");
}
else
{
Debug.Log("Login failed:" + result.IsError);
}
});
User user = AccelBytePlugin.GetUser();
user.LoginWithUserName(
"user+a@example.com",
"Password321",
(Result<TokenData, OAuthError> result) =>
{
if (!result.IsError)
{
// show the login result
Debug.Log("Login player A successful");
}
else
{
Debug.Log("Login failed:" + result.IsError);
}
});
Game Server
- Use Find all to locate all the
AccelByteServerPlugin.*
singleton classes in your project and replaceAccelByteServerPlugin.
withMultiRegistry.GetServerApiClient().
. - An error will occur on LoginWithClientCredentials(). We recommend replace the function call from
AccelByteServerPlugin.GetDedicatedServer().LoginWithClientCredentials(...)
tovar dsSession = MultiRegistry.GetServerApiClient().session; / yield return dsSession.LoginWithClientCredentials(...);
.
- API Client
- Previous Usage
yield return MultiRegistry.GetServerApiClient().session.LoginWithClientCredentials(
result =>
{
if (!result.IsError)
{
Debug.Log("Server authenticated");
}
else
{
Debug.Log("Server authentication failed:" + result.IsError);
}
});
AccelByteServerPlugin.GetDedicatedServer().LoginWithClientCredentials(result =>
{
if (!result.IsError)
{
Debug.Log("Server authenticated");
}
else
{
Debug.Log("Server authentication failed:" + result.IsError);
}
});
Unreal Engine
Game Client
The game client, called FApiClient
, is a class that can be created from FMultiRegistry
.
You must prepare a key to construct the FApiClient
. The key represents the managed user, or acts as an identifier. To handle several users at the same time, you can use this method to create multiple keys. /
Leave it empty to use the default user if the game has only one player in one game process/executable.
Follow the steps below to migrate FRegistry
to FMultiRegistry
:
Use Find all to locate all the
FRegistry::*
classes in your project and:- For singleton classes, replace
FRegistry::
withFMultiRegistry::Ge
.
WARNINGDo not change
FRegistry::Settings
,FRegistry::Credentials
, orFRegistry::HttpRetryScheduler
.- For singleton classes, replace
Check that the compilation still runs after you have renamed it. If not, undo your previous changes and repeat step 1.
- API Client
- Previous Usage
FMultiRegistry::GetApiClient()->User.LoginWithUsername(
TEXT("user+a@example.com"),
TEXT("Password321"),
FVoidHandler::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Login successful"));
}), FCustomErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorObject)
{
UE_LOG(LogTemp, Error, TEXT("Login Failed : %d, %s"), ErrorCode, *ErrorMessage);
}));
FRegistry::User.LoginWithUsername(
TEXT("user+a@example.com"),
TEXT("Password321"),
FVoidHandler::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Login successful"));
}), FCustomErrorHandler::CreateWeakLambda(this, [](int32 ErrorCode, const FString& ErrorMessage, const FJsonObject& ErrorObject)
{
UE_LOG(LogTemp, Error, TEXT("Login Failed : %d, %s"), ErrorCode, *ErrorMessage);
}));
Game Server
The steps to create the game server are similar to those for the game client in that you need to replace the registry The steps to migrate from FRegistry to FMultiRegistry are as follows:
Use Find all to locate all the
FRegistry::*
singleton classes in your project and replaceFRegistry::*
withFMultiRegistry::GetServerApiClient()->
.WARNINGDo not change
FRegistry::ServerSettings
.
- API Client
- Previous Usage
auto ServerApiClient = FMultiRegistry::GetServerApiClient();
ServerApiClient->ServerOauth2.LoginWithClientCredentials(FVoidHandler::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Login With Credentials success"));
}), FErrorHandler::CreateWeakLambda(this, [this](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Login With Credentials failed, Error Code: %d Error Message: %s"), ErrorCode, *ErrorMessage);
}));
FRegistry::ServerOauth2.LoginWithClientCredentials(FVoidHandler::CreateWeakLambda(this, [this]()
{
UE_LOG(LogTemp, Log, TEXT("Login With Credentials success"));
}), FErrorHandler::CreateWeakLambda(this, [this](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Error, TEXT("Login With Credentials failed, Error Code: %d Error Message: %s"), ErrorCode, *ErrorMessage);
}));
Use the AccelByte OSS with the Client SDKs
Follow the steps below to get an ApiClient
if your game is using OSS to handle logins:
Check the DefaultEngine.ini configuration file. Ensure that
bMultipleLocalUsersEnabled
is true if there are multiple local players in a single game process.If necessary, change it to true.
[OnlineSubsystemAccelByte]
bEnabled=true
bAutoLobbyConnectAfterLoginSuccess=false
bMultipleLocalUsersEnabled=true
First log in the user using AccelByte Cloud’s Online Identity Interface, which is used for player authentication and profiles.
Login and make note of the
LocalUserNum
that is passed.
virtual bool Login(int32 LocalUserNum, const FOnlineAccountCredentials& AccountCredentials) override;
Use
OnLoginCompleteDelegates
to find the login result from the identity interface.After the user has been logged in, call the following function from AccelByte Cloud’s Identity Interface:
IOnlineSubsystem* SubsystemAB = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
const FOnlineIdentityAccelBytePtr IdentityPtr = StaticCastSharedPtr<FOnlineIdentityAccelByte>(SubsystemAB->GetIdentityInterface());
FApiClientPtr ApiClient = IdentityPtr->GetApiClient(in32 LocalUserNum);
- Pass the same
LocalUserNum
that was used for login. FApiClientPtr
will be returned and can be used for various API calls by the specified user.