Implement Multiple Registries
Introduction
The Multiple Registries is a feature designed to support multiple local users logging in to the same game instance in AccelByte Gaming Services (AGS). 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 containers of API clients that store logged-in player information and acts as a player when calling AGS APIs.
The current SDK has 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 AGS APIs. Game developers will need to use this new class and the multiple registries will maintain this class.
The ApiClient
infrastructure offers the following advantages:
- Reduces singleton and static class usage.
- Easier to manage for developers, who can easily create and 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
You have installed the plugins and modules of the game engine you're using.
- note
If you want to use the AGS Online Subsystem (OSS) in Unreal Engine, you will need these additional plugins:
You have configured your game client SDK.
Implement Multiple Registries using Client SDKs
Include the following library at 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 using a unique key for each player so that each player can be isolated by calling
GetApiClient()
.- 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 = AccelByteSDK.GetClientRegistry().GetApi("0");
// Define User B
ApiClient apiClientA = AccelByteSDK.GetClientRegistry().GetApi("1");NOTES- For the unique key, apply the following rules:
- Unreal Engine: 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.
As this is a gateway, you must authenticate each player before they can access any AGS service. To do this, set up an AGS login method. You can use any login method that AGS 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);noteIf you are implementing multiple registries in OSS, use
LocalUserNum
as a user key in every function call and set delegate to differentiate each player.// Define User
ApiClient apiClientA = AccelByteSDK.GetClientRegistry().GetApi("0");
ApiClient apiClientB = AccelByteSDK.GetClientRegistry().GetApi("1");
User userA = apiClientA.GetUser();
User userB = apiClientB.GetUser();
// Login with username
userA.LoginWithUsernameV3(
"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.LoginWithUsernameV3(
"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);
}
});
What's next
Learn how to migrate your game from a singleton-based registry to multiple registries.