How to Handle Accepting Agreements During Connection Errors
During a network disruption, such as scheduled maintenance or unexpected connection issues, the player's accepted agreements might not be properly updated in the legal service. If this issue is not handled correctly, the player may be left unaware that their agreement status has not been updated, leading to confusion or potential access issues.
To prevent this, it's important to implement error handling that retries the request until it successfully updates the player's agreement status. A common approach is to use a timer-based retry system at the game level. Here's an example of how to implement this retry system with a maximum of 5 retry attempts and retry delay 10 seconds.
- Unreal Engine OSS
- Unreal Engine SDK
- Unity
...
int MaxRetryAttempts{5};
int NRetryAttempt{0};
int RetryDelay{10};
FTimerHandle RetryTimerHandle;
FDelegateHandle AccelByteAcceptAgreementPoliciesHandle;
int32 LocalUserNum = 0;
...
void AcceptLegal()
{
...
auto ABSubsystem = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
auto AgreementInterface = ABSubsystem->GetAgreementInterface();
auto ABAgreementInterface = StaticCastSharedPtr<FOnlineAgreementAccelBytePtr>(AgreementInterface);
TArray<FABAcceptAgreementPoliciesRequest> DocumentToAccept;
FABAcceptAgreementPoliciesRequest PoliciesRequest;
PoliciesRequest.BasePolicyId = TEXT("<PolicyVersionId>");
PoliciesRequest.LocaleCode = TEXT("en");
DocumentToAccept.Add(PoliciesRequest);
AccelByteAcceptAgreementPoliciesHandle = AgreementInterface->AddAccelByteOnAcceptAgreementPoliciesCompletedDelegate_Handle(LocalUserNum
, FAccelByteOnAcceptAgreementPoliciesCompletedDelegate::CreateLambda([this](int32 AcceptAgreementLocalUserNum, bool bWasSuccessful, const FOnlineErrorAccelByte& ResultState)
{
if (bWasSuccessful)
{
// Do something when the accept the agreement is successful.
NRetryAttempt = 0;
}
else
{
// Implement a solution to handle failed request.
if(NRetryAttempt < MaxRetryAttempts)
{
GetWorld()->GetTimerManager().SetTimer(RetryTimerHandle, [this]()
{
UpdatePlayerMMR();
}, RetryDelay, false);
NRetryAttempt++;
}
else
{
// Show an error message pop up!
}
}
AgreementInterface->ClearAccelByteOnAcceptAgreementPoliciesCompletedDelegate_Handle(LocalUserNum, AccelByteAcceptAgreementPoliciesHandle);
})
);
AgreementInterface->AcceptAgreementPolicies(LocalUserNum, DocumentToAccept);
...
}
...
int MaxRetryAttempts{5};
int NRetryAttempt{0};
int RetryDelay{10};
FTimerHandle RetryTimerHandle;
...
void AcceptLegal()
{
...
FApiClientPtr ApiClient = FMultiRegistry::GetApiClient();
TArray<FAccelByteModelsAcceptAgreementRequest> AcceptAgreementRequests;
FAccelByteModelsAcceptAgreementRequest AcceptRequest;
AcceptRequest.IsAccepted = true;
AcceptRequest.PolicyId = TEXT("<PolicyId>");
AcceptRequest.PolicyVersionId = TEXT("<PolicyVersionId>");
AcceptRequest.LocalizedPolicyVersionId = TEXT("<LocalizedPolicyVersionId>");
AcceptAgreementRequests.Add(AcceptRequest);
ApiClient->Agreement.BulkAcceptPolicyVersions(AcceptAgreementRequests,
THandler<FAccelByteModelsAcceptAgreementResponse>::CreateLambda([this](const FAccelByteModelsAcceptAgreementResponse& Result)
{
// Do something when the accept the agreement is successful.
NRetryAttempt = 0;
}), FErrorHandler::CreateLambda([this](int32 Code, const FString& Message)
{
UE_LOG(LogTemp, Warning, TEXT("Error accept the agreement. Code: %d, Message: %s"), ErrorCode, *ErrorMessage);
if(ErrorCode == static_cast<int32>(ErrorCodes::NetworkError))
{
// Implement a solution to handle HTTP retry timeout.
if(NRetryAttempt < MaxRetryAttempts)
{
GetWorld()->GetTimerManager().SetTimer(RetryTimerHandle, [this]()
{
AcceptLegal();
}, RetryDelay, false);
NRetryAttempt++;
}
else
{
// Show an error message pop up!
}
}
})
);
...
}
...
int maxRetryAttempts = 5;
int nRetryAttempt = 0;
int retryDelay = 10;
...
public void AcceptLegal()
{
...
List<AcceptAgreementRequest> acceptAgreementRequests = new List<AcceptAgreementRequest>();
AcceptAgreementRequest acceptAgreementRequest = new AcceptAgreementRequest
{
isAccepted = true,
policyId = "<policyId>",
policyVersionId = "<policyVersionId>",
localizedPolicyVersionId = "<LocalizedPolicyVersionId>"
};
acceptAgreementRequests.Add(acceptAgreementRequest);
AccelByteSDK.GetClientRegistry().GetApi().GetAgreement().BulkAcceptPolicyVersions(acceptAgreementRequests.ToArray(), result =>
{
if (!result.IsError)
{
// Do something when the accept the agreement is successful.
nRetryAttempt = 0;
}
else
{
// Implement a solution to handle failed request.
Debug.LogWarning($"Unable to login. Code: {result.Error.Code}, Message: {result.Error.Message}");
if(result.Error.Code.Equals(ErrorCode.NetworkError))
{
// Implement a solution to handle HTTP retry timeout.
StartCoroutine(RetryAcceptLegal);
}
}
});
...
}
private IEnumerator RetryAcceptLegal()
{
if(nRetryAttempt < maxRetryAttempts)
{
yield return new WaitForSeconds(retryDelay);
nRetryAttempt++;
AcceptLegal();
}
else
{
// Show an error message pop up!
}
}
If the request encounters one of the error codes listed here, the error delegate will not be triggered when the system starts to retry the request. The error delegate will only be triggered after the retry process ends—either when the retry limit is reached (timeout) or if the final response code is not one of the listed error codes.