Use the OSS and SDK to get challenges and claim rewards - Challenge - (Unreal Engine module)
Unwrap the Subsystem
In this tutorial, you will learn how to retrieve challenges and claim rewards using the AccelByte Gaming Services (AGS) Online Subsystem (OSS) and Software Development Kit (SDK).
In the Byte Wars project, a Game Instance Subsystem named ChallengeEssentialsSubsystem
has already been created. This subsystem contains basic challenge-related functionality. You will use a starter version of this subsystem, allowing you to implement the challenge features from scratch.
What's in the Starter Pack
To follow this tutorial, a starter subsystem class named ChallengeEssentialsSubsystem_Starter
has been prepared. This class is available in the Resources section and consists of the following files:
- Header file:
/Source/AccelByteWars/TutorialModules/Engagement/ChallengeEssentials/ChallengeEssentialsSubsystem_Starter.h
- CPP file:
/Source/AccelByteWars/TutorialModules/Engagement/ChallengeEssentials/ChallengeEssentialsSubsystem_Starter.cpp
The ChallengeEssentialsSubsystem_Starter
class provides several key functionalities:
A function to get the AGS SDK challenge API interface. You will use this function to send challenge-related requests.
AccelByte::Api::ChallengePtr UChallengeEssentialsSubsystem_Starter::GetChallengeApi() const
{
AccelByte::FApiClientPtr ApiClient = UTutorialModuleOnlineUtility::GetApiClient(this);
if (!ApiClient)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("AccelByte API Client is invalid."));
return nullptr;
}
return ApiClient->GetChallengeApi().Pin();
}A function to get the AGS OSS store interface. You will use this function to retrieve challenge reward item information.
FOnlineStoreV2AccelBytePtr UChallengeEssentialsSubsystem_Starter::GetStoreInterface() const
{
const IOnlineSubsystem* Subsystem = Online::GetSubsystem(GetWorld());
if (!ensure(Subsystem))
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("The online subsystem is invalid. Please make sure OnlineSubsystemAccelByte is enabled and DefaultPlatformService under [OnlineSubsystem] in the Engine.ini set to AccelByte."));
return nullptr;
}
return StaticCastSharedPtr<FOnlineStoreV2AccelByte>(Subsystem->GetStoreV2Interface());
}
In addition to the subsystem class, there is a file named ChallengeEssentialsModels.h
that contains helper classes, delegates, and constants. This file is located at:
/Source/AccelByteWars/TutorialModules/Engagement/ChallengeEssentials/ChallengeEssentialsModels.h
This file includes the following helpers:
A helper class named
ChallengeGoalData
to store challenge goal information. You will use this class with the UI component to display the list of challenge goals.UCLASS()
class ACCELBYTEWARS_API UChallengeGoalData : public UObject
{
GENERATED_BODY()
public:
FAccelByteModelsChallengeGoal Goal;
FAccelByteModelsChallengeGoalProgress Progress;
TArray<FChallengeGoalRewardData> Rewards{};
FString EndDateTime = TEXT("");
FString GetEndTimeDuration()
{
FDateTime ParsedEndDateTime{};
if (!FDateTime::ParseIso8601(*EndDateTime, ParsedEndDateTime))
{
return TEXT("");
}
/* Return duration in "dd hh mm" format.
* If duration is less than a minute, return "< 1m" instead. */
const FTimespan Duration = ParsedEndDateTime - FDateTime::UtcNow();
FString DurationStr{};
if (Duration.GetDays() > 0)
{
DurationStr += FString::Printf(TEXT("%dd "), Duration.GetDays());
}
if (Duration.GetHours() > 0)
{
DurationStr += FString::Printf(TEXT("%dh "), Duration.GetHours());
}
if (Duration.GetMinutes() > 0)
{
DurationStr += FString::Printf(TEXT("%dm"), Duration.GetMinutes());
}
else
{
DurationStr = TEXT("< 1m");
}
return DurationStr;
}
};A helper class named
ChallengeGoalRewardData
to store challenge reward information. You will use this class with the UI component to display the list of rewards.USTRUCT()
struct ACCELBYTEWARS_API FChallengeGoalRewardData
{
GENERATED_BODY()
FString Sku = TEXT("");
FString Name = TEXT("");
FString IconUrl = TEXT("");
int32 Quantity = 0;
};Delegates to handle request callbacks.
DECLARE_DELEGATE_ThreeParams(FOnGetChallengeCodeComplete, bool bWasSuccessful, const FString& ErrorMessage, const FAccelByteModelsChallenge& Result);
DECLARE_DELEGATE_ThreeParams(FOnGetChallengeGoalsComplete, bool bWasSuccessful, const FString& ErrorMessage, const TArray<UChallengeGoalData*>& Result);
DECLARE_DELEGATE_TwoParams(FOnQueryRewardItemsBySkusRecursivelyComplete, bool bWasSuccessful, const FString& ErrorMessage);
DECLARE_DELEGATE_ThreeParams(FOnQueryRewardItemsInformationComplete, bool bWasSuccessful, const FString& ErrorMessage, const TArray<UChallengeGoalData*>& Result);
DECLARE_DELEGATE_TwoParams(FOnClaimChallengeGoalRewardsComplete, bool bWasSuccessful, const FString& ErrorMessage);Constants for logging and displaying messages.
#define ACCELBYTEWARS_LOCTEXT_NAMESPACE "AccelByteWars"
#define INVALID_CHALLENGE_INTERFACE_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Invalid Challenge Interface", "Invalid Challenge Interface")
#define EMPTY_CHALLENGE_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Empty Challenge", "No Challenge Found")
#define EMPTY_CLAIMABLE_CHALLENGE_REWARD_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Empty Claimable Challenge Reward", "No Claimable Challenge Reward Found")
#define CLAIMED_CHALLENGE_REWARD_LABEL NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Claimed Challenge Reward Label", "Claimed")
#define CLAIMABLE_CHALLENGE_REWARD_LABEL NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Claimable Challenge Reward Label", "Claim")
#define ALLTIME_CHALLENGE_TITLE_LABEL NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "All Time Challenges", "All Time Challenges")
#define PERIODIC_CHALLENGE_TITLE_LABEL NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "{0} Challenges", "{0} Challenges")
Get challenge by period
Previously, you configured the challenge and its rotation/period in the Admin Portal. In this section, you will learn how to implement functionality to retrieve challenge information based on the selected period.
Open the header file of the
ChallengeEssentialsSubsystem_Starter
class and declare the following function:public:
void GetChallengeByPeriod(
const EAccelByteModelsChallengeRotation Period,
const FOnGetChallengeCodeComplete& OnComplete = FOnGetChallengeCodeComplete());Next, open the CPP file of the
ChallengeEssentialsSubsystem_Starter
class and define the function. This function sends a request to retrieve all available challenges and filters the one that matches the specified period.void UChallengeEssentialsSubsystem_Starter::GetChallengeByPeriod(
const EAccelByteModelsChallengeRotation Period,
const FOnGetChallengeCodeComplete& OnComplete)
{
const AccelByte::Api::ChallengePtr ChallengeApi = GetChallengeApi();
if (!ChallengeApi)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge. Challenge API Client is invalid."));
OnComplete.ExecuteIfBound(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString(), FAccelByteModelsChallenge{});
return;
}
// Get the list of challenges and return the one that matches with the requested period.
ChallengeApi->GetChallenges(
AccelByte::THandler<FAccelByteModelsGetChallengesResponse>::CreateWeakLambda(this, [Period, OnComplete](const FAccelByteModelsGetChallengesResponse& Result)
{
for (const FAccelByteModelsChallenge& Challenge : Result.Data)
{
// Skip inactive challenge.
if (Challenge.Status == EAccelByteModelsChallengeStatus::RETIRED)
{
continue;
}
// Challenge codes in Byte Wars use the <engine>-<period> format, e.g., unreal-alltime or unreal-weekly.
if (Challenge.Code.Contains("unreal") && Challenge.Rotation == Period)
{
UE_LOG_CHALLENGE_ESSENTIALS(Log, TEXT("Success to get challenge with %s period. Challenge code: %s"), *FAccelByteUtilities::GetUEnumValueAsString(Period), *Challenge.Code);
OnComplete.ExecuteIfBound(true, TEXT(""), Challenge);
return;
}
}
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge. No challenge found with %s period."), *FAccelByteUtilities::GetUEnumValueAsString(Period));
OnComplete.ExecuteIfBound(false, EMPTY_CHALLENGE_MESSAGE.ToString(), FAccelByteModelsChallenge{});
}),
AccelByte::FErrorHandler::CreateWeakLambda(this, [OnComplete](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge. Error %d: %s"), ErrorCode, *ErrorMessage);
OnComplete.ExecuteIfBound(false, ErrorMessage, FAccelByteModelsChallenge{});
}),
EAccelByteModelsChallengeSortBy::UPDATED_AT_DESC,
EAccelByteModelsChallengeStatus::NONE);
}The result returned by the function above includes challenge information such as the challenge name and challenge code.
Get challenge goals
In this section, you will learn how to retrieve challenge goals, their progress, and associated rewards.
Open the header file of the
ChallengeEssentialsSubsystem_Starter
class and declare the following functions to retrieve the list of challenge goals:public:
// ...
void GetChallengeGoalList(
const FUniqueNetIdPtr UserId,
const FAccelByteModelsChallenge& Challenge,
const FOnGetChallengeGoalsComplete& OnComplete = FOnGetChallengeGoalsComplete());private:
void OnGetChallengeGoalListComplete(
bool bIsSucceeded,
const FString& ErrorMessage,
const TArray<UChallengeGoalData*> Goals,
const FOnGetChallengeGoalsComplete OnComplete);In the same file, declare the following functions to query reward item information:
private:
// ...
void QueryRewardItemsInformation(
const FUniqueNetIdPtr UserId,
const TArray<UChallengeGoalData*> Goals,
const FOnQueryRewardItemsInformationComplete& OnComplete);private:
// ...
void QueryRewardItemsBySkusRecursively(
const FUniqueNetIdPtr UserId,
TArray<FString> ItemSkusToQuery,
const FOnQueryRewardItemsBySkusRecursivelyComplete& OnComplete);private:
// ...
void OnQueryRewardItemsInformationComplete(
bool bWasSuccessful,
const FString& Error,
const TArray<UChallengeGoalData*> Goals,
const FOnQueryRewardItemsInformationComplete OnComplete);Now open the CPP file of the
ChallengeEssentialsSubsystem_Starter
class. Start by defining theGetChallengeGoalList()
function. This function sends a request to evaluate the player's challenge progress, retrieves the goal progress, and then callsQueryRewardItemsInformation()
to fetch reward item details. Finally, it invokesOnGetChallengeGoalListComplete()
to return the result.void UChallengeEssentialsSubsystem_Starter::GetChallengeGoalList(
const FUniqueNetIdPtr UserId,
const FAccelByteModelsChallenge& Challenge,
const FOnGetChallengeGoalsComplete& OnComplete)
{
if (!UserId)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge goal list. User ID is invalid."));
OnGetChallengeGoalListComplete(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString(), {}, OnComplete);
return;
}
const AccelByte::Api::ChallengePtr ChallengeApi = GetChallengeApi();
if (!ChallengeApi)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge goal list. Challenge API Client is invalid."));
OnGetChallengeGoalListComplete(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString(), {}, OnComplete);
return;
}
// Request to evaluate to update challenge goals progresses.
ChallengeApi->EvaluateChallengeProgress(AccelByte::FVoidHandler::CreateWeakLambda(this, [this, ChallengeApi, Challenge, UserId, OnComplete]()
{
// Get the goal list and their progress.
ChallengeApi->GetChallengeProgress(
Challenge.Code,
AccelByte::THandler<FAccelByteModelsChallengeProgressResponse>::CreateWeakLambda(this, [this, Challenge, UserId, OnComplete](const FAccelByteModelsChallengeProgressResponse& Result)
{
// Construct new goal object and add it to the list.
TArray<UChallengeGoalData*> GoalDataList{};
for (const FAccelByteModelsChallengeGoalProgress& Progress : Result.Data)
{
if (UChallengeGoalData* GoalData = NewObject<UChallengeGoalData>())
{
GoalData->Goal = Progress.Goal;
GoalData->Progress = Progress;
GoalData->EndDateTime = (Challenge.Rotation == EAccelByteModelsChallengeRotation::NONE) ? TEXT("") : Result.Meta.Period.EndTime.ToIso8601();
GoalDataList.Add(GoalData);
}
}
// Query reward item information for all goals.
QueryRewardItemsInformation(
UserId,
GoalDataList,
FOnQueryRewardItemsInformationComplete::CreateWeakLambda(this, [this, OnComplete](bool bWasSuccessful, const FString& ErrorMessage, const TArray<UChallengeGoalData*>& Result)
{
// Operation is complete, return the result.
OnGetChallengeGoalListComplete(bWasSuccessful, ErrorMessage, Result, OnComplete);
}));
}),
AccelByte::FErrorHandler::CreateWeakLambda(this, [this, OnComplete](int32 ErrorCode, const FString& ErrorMessage)
{
OnGetChallengeGoalListComplete(false, ErrorMessage, {}, OnComplete);
}));
}),
AccelByte::FErrorHandler::CreateWeakLambda(this, [this, OnComplete](int32 ErrorCode, const FString& ErrorMessage)
{
OnGetChallengeGoalListComplete(false, ErrorMessage, {}, OnComplete);
}));
}Define the
QueryRewardItemsInformation()
function. This function checks whether the reward item information is already cached in the OSS. If not, it sends a request to retrieve the reward item details using theQueryRewardItemsBySkusRecursively()
function. Then, the response is handled by theOnQueryRewardItemsInformationComplete()
function.void UChallengeEssentialsSubsystem_Starter::QueryRewardItemsInformation(
const FUniqueNetIdPtr UserId,
const TArray<UChallengeGoalData*> Goals,
const FOnQueryRewardItemsInformationComplete& OnComplete)
{
if (!UserId)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to query reward items info. User ID is invalid."));
OnQueryRewardItemsInformationComplete(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString(), Goals, OnComplete);
return;
}
const FOnlineStoreV2AccelBytePtr StoreInterface = GetStoreInterface();
if (!StoreInterface)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to query reward items info. Store interface is invalid."));
OnQueryRewardItemsInformationComplete(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString(), Goals, OnComplete);
return;
}
// Collect reward item SKUs to query.
TArray<FString> RewardItemSkusToQuery{};
for (const UChallengeGoalData* GoalData : Goals)
{
if (!GoalData) continue;
for (const FAccelByteModelsChallengeGoalReward& Reward : GoalData->Goal.Rewards)
{
// The reward item ID from backend response is actually the item's SKU.
RewardItemSkusToQuery.AddUnique(Reward.ItemId);
}
};
// Check if the reward items information is already cached.
TArray<FString> CachedOfferIds{};
TArray<FOnlineStoreOfferAccelByteRef> CachedOffers{};
StoreInterface->GetOffers(CachedOffers);
Algo::Transform(CachedOffers, CachedOfferIds, [](const FOnlineStoreOfferAccelByteRef Item) { return Item->Sku; });
RewardItemSkusToQuery.RemoveAll([&CachedOfferIds](const FString& Item) { return CachedOfferIds.Contains(Item); });
// Return success if all reward items are already cached.
if (RewardItemSkusToQuery.IsEmpty())
{
UE_LOG_CHALLENGE_ESSENTIALS(Log, TEXT("Success to query reward items info. All infos are already cached."));
OnQueryRewardItemsInformationComplete(true, TEXT(""), Goals, OnComplete);
return;
}
// Query reward items information by SKUs recursively.
QueryRewardItemsBySkusRecursively(
UserId,
RewardItemSkusToQuery,
FOnQueryRewardItemsBySkusRecursivelyComplete::CreateUObject(this, &ThisClass::OnQueryRewardItemsInformationComplete, Goals, OnComplete));
}Next, define the
QueryRewardItemsBySkusRecursively()
function. This function send request to query items information by SKU one by one.void UChallengeEssentialsSubsystem_Starter::QueryRewardItemsBySkusRecursively(
const FUniqueNetIdPtr UserId,
TArray<FString> ItemSkusToQuery,
const FOnQueryRewardItemsBySkusRecursivelyComplete& OnComplete)
{
// All item is queried, the operation is completed.
if (ItemSkusToQuery.IsEmpty())
{
UE_LOG_CHALLENGE_ESSENTIALS(Log, TEXT("Success to query reward items info by SKUs."));
OnComplete.ExecuteIfBound(true, TEXT(""));
return;
}
if (!UserId)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to query reward items info by SKUs. User ID is invalid."));
OnComplete.ExecuteIfBound(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString());
return;
}
const FOnlineStoreV2AccelBytePtr StoreInterface = GetStoreInterface();
if (!StoreInterface)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to query reward items info by SKUs. Store interface is invalid."));
OnComplete.ExecuteIfBound(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString());
return;
}
const FString CurrentSku = ItemSkusToQuery[0];
ItemSkusToQuery.RemoveAt(0);
StoreInterface->QueryOfferBySku(
UserId.ToSharedRef().Get(),
CurrentSku,
FOnQueryOnlineStoreOffersComplete::CreateWeakLambda(this, [this, UserId, ItemSkusToQuery, OnComplete]
(bool bWasSuccessful, const TArray<FUniqueOfferId>& OfferIds, const FString& Error)
{
// Abort if failed to query item by SKU.
if (!bWasSuccessful)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to query reward items by SKUs. Error: %s"), *Error);
OnComplete.ExecuteIfBound(false, Error);
return;
}
// Recurse to next item SKU
QueryRewardItemsBySkusRecursively(UserId, ItemSkusToQuery, OnComplete);
}));
}Then, define the
OnQueryRewardItemsInformationComplete()
function. This function gathers reward information such as SKU, name, icon, and quantity, then passes the result to the assigned delegate.void UChallengeEssentialsSubsystem_Starter::OnQueryRewardItemsInformationComplete(
bool bWasSuccessful,
const FString& Error,
const TArray<UChallengeGoalData*> Goals,
const FOnQueryRewardItemsInformationComplete OnComplete)
{
if (!bWasSuccessful)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to query reward items info. Error: %s"), *Error);
OnComplete.ExecuteIfBound(false, Error, {});
return;
}
// Construct goal reward data based on queried information.
for (UChallengeGoalData* GoalData : Goals)
{
if (!GoalData) continue;
for (const FAccelByteModelsChallengeGoalReward& Reward : GoalData->Goal.Rewards)
{
// Get item offer from cache by using the item SKU (the reward item ID is the item's SKU).
TSharedPtr<FOnlineStoreOfferAccelByte> Offer = GetStoreInterface()->GetOfferBySkuAccelByte(Reward.ItemId);
FChallengeGoalRewardData RewardData;
RewardData.Sku = Reward.ItemId;
RewardData.Name = Reward.ItemName;
RewardData.Quantity = (int32)Reward.Qty;
FString IconKey = TEXT("IconUrl");
if (Offer && Offer->DynamicFields.Contains(IconKey))
{
RewardData.IconUrl = *Offer->DynamicFields.Find(IconKey);
}
GoalData->Rewards.Add(RewardData);
}
}
UE_LOG_CHALLENGE_ESSENTIALS(Log, TEXT("Success to query reward items info."));
OnComplete.ExecuteIfBound(true, TEXT(""), Goals);
}Finally, define the
OnGetChallengeGoalListComplete()
function. This is the last step in the goal retrieval process. It sorts the goal list based on completion and claimed rewards, then returns the result using the assigned delegate.void UChallengeEssentialsSubsystem_Starter::OnGetChallengeGoalListComplete(
bool bIsSucceeded,
const FString& ErrorMessage,
const TArray<UChallengeGoalData*> Goals,
const FOnGetChallengeGoalsComplete OnComplete)
{
if (!bIsSucceeded)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to get challenge goal list. Error: %s"), *ErrorMessage);
OnComplete.ExecuteIfBound(false, ErrorMessage, {});
return;
}
// Sort the result based on the unclaimed rewards and completion status.
TArray<UChallengeGoalData*> Result = Goals;
Result.Sort([](UChallengeGoalData& Goal1, UChallengeGoalData& Goal2)
{
const EAccelByteModelsChallengeGoalProgressStatus CompleteStatus = EAccelByteModelsChallengeGoalProgressStatus::COMPLETED;
const bool bIsCompleted = (Goal1.Progress.Status == CompleteStatus) > (Goal2.Progress.Status == CompleteStatus);
const bool bIsClaimed = Goal1.Progress.ToClaimRewards.IsEmpty() < Goal2.Progress.ToClaimRewards.IsEmpty();
return bIsClaimed || bIsCompleted;
});
UE_LOG_CHALLENGE_ESSENTIALS(Log, TEXT("Success to get challenge goal list."));
OnComplete.ExecuteIfBound(true, ErrorMessage, Result);
}
Claim challenge rewards
In this section, you will learn how to claim challenge rewards.
Open the header file of the
ChallengeEssentialsSubsystem_Starter
class and declare the following function:public:
// ...
void ClaimChallengeGoalRewards(
const TArray<FString>& RewardIDs,
const FOnClaimChallengeGoalRewardsComplete& OnComplete = FOnClaimChallengeGoalRewardsComplete());Now, open the CPP file of the
ChallengeEssentialsSubsystem_Starter
class and define the function above. This function sends a request to claim the rewards by passing a list of reward IDs. It then returns the result through the assigned delegate.void UChallengeEssentialsSubsystem_Starter::ClaimChallengeGoalRewards(
const TArray<FString>& RewardIDs,
const FOnClaimChallengeGoalRewardsComplete& OnComplete)
{
const AccelByte::Api::ChallengePtr ChallengeApi = GetChallengeApi();
if (!ChallengeApi)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to claim challenge goal reward. Challenge API Client is invalid."));
OnComplete.ExecuteIfBound(false, INVALID_CHALLENGE_INTERFACE_MESSAGE.ToString());
return;
}
ChallengeApi->ClaimReward(
FAccelByteModelsChallengeRewardClaimRequest{ RewardIDs },
AccelByte::THandler<TArray<FAccelByteModelsChallengeReward>>::CreateWeakLambda(this, [OnComplete](const TArray<FAccelByteModelsChallengeReward>& Result)
{
// Abort if there is no claimable rewards.
if (Result.IsEmpty())
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to claim challenge rewards. No claimable reward found."));
OnComplete.ExecuteIfBound(false, EMPTY_CLAIMABLE_CHALLENGE_REWARD_MESSAGE.ToString());
return;
}
UE_LOG_CHALLENGE_ESSENTIALS(Log, TEXT("Success to claim challenge rewards."));
OnComplete.ExecuteIfBound(true, TEXT(""));
}),
AccelByte::FErrorHandler::CreateWeakLambda(this, [OnComplete](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG_CHALLENGE_ESSENTIALS(Warning, TEXT("Failed to claim challenge rewards. Error %d: %s"), ErrorCode, *ErrorMessage);
OnComplete.ExecuteIfBound(false, ErrorMessage);
}));
}
Resources
The files used in this tutorial section are available in the Unreal Byte Wars GitHub repository.
- AccelByteWars/Source/AccelByteWars/TutorialModules/Engagement/ChallengeEssentials/ChallengeEssentialsSubsystem_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Engagement/ChallengeEssentials/ChallengeEssentialsSubsystem_Starter.cpp
- AccelByteWars/Source/AccelByteWars/TutorialModules/Engagement/ChallengeEssentials/ChallengeEssentialsModels.h