AGS OSS for Unreal Engine
LocalUserNum
The AGS 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 AGS OSS stores a user's online information using LocalUserNum as the key.
UniqueNetId
UniqueNetId is the abstraction of the user's profile in online services. In AGS OSS, it is derived as AGS Composite UniqueNetId and consists of AGS userId, originating platform, and originating platform userId data fields.
Interfaces
The AGS OSS expands upon the predefined interfaces of UE's OSS, giving developers the ability to further extend and integrate AGS 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 AGS 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 the 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);
FOnlineIdentityAccelBytePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(Subsystem->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 service 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 the DefaultEngine.ini file.
const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
FOnlineIdentityAccelBytePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(Subsystem->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 this code:
const UWorld* World = GameEngine->GetGameWorld();
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(World, ACCELBYTE_SUBSYSTEM);
FOnlineIdentityAccelBytePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(Subsystem->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, it will trigger OnLoginCompleteDelegates and the player login flow will be complete. However, if a player has not complied with any of the 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);
}
Accept 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);
}