Implement Subsystem - Store Item Purchase - (Unreal Engine module)
Unwrap the subsystem
Byte Wars uses the Game Instance Subsystem called StoreItemPurchaseSubsystem
to wrap the AccelByte Gaming Services (AGS) Online Subsystem (OSS). This subsystem uses FOnlinePurchaseAccelByte
, which is AccelByte's implementation of Unreal Engine’s IOnlinePurchase
interface. In this tutorial, you'll be working with a starter version of the subsystem, allowing you to implement the required functions from scratch.
What's in the Starter Pack
To follow along with this tutorial, a starter subsystem class named StoreItemPurchaseSubsystem_Starter
has been prepared. You can find it in the Resources section. It includes the following files:
- Header file:
Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/StoreItemPurchaseSubsystem_Starter.h
- CPP file:
Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/StoreItemPurchaseSubsystem_Starter.cpp
The StoreItemPurchaseSubsystem_Starter
class contains several helpful components:
- Declaration and initialization of the AGS OSS
FOnlinePurchaseAccelByte
interface, which provides access to AGS Software Development Kit (SDK) features.
private:
FOnlinePurchaseAccelBytePtr PurchaseInterface;
void UStoreItemPurchaseSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
const IOnlinePurchasePtr PurchasePtr = Online::GetSubsystem(GetWorld())->GetPurchaseInterface();
ensure(PurchasePtr);
PurchaseInterface = StaticCastSharedPtr<FOnlinePurchaseAccelByte>(PurchasePtr);
ensure(PurchaseInterface);
// ...
}
- Helper function to retrieve the unique Net ID from a Player Controller. This is necessary because widgets, which are the primary users of this subsystem, use Player Controllers to reference players, while the OSS interface requires a unique Net ID to identify the user.
private:
// ...
FUniqueNetIdPtr GetLocalPlayerUniqueNetId(const APlayerController* PlayerController) const;
Additionally, there is a model file located at Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/StoreItemPurchaseModel.h
that defines the delegate used to handle backend responses.
DECLARE_MULTICAST_DELEGATE_OneParam(FOnOrderComplete, const FOnlineError& /*Error*/)
Implement item purchase
-
Open
StoreItemPurchaseSubsystem_Starter
header file and declare the following function to send an item purchase request.public:
void CreateNewOrder(
const APlayerController* OwningPlayer,
const TWeakObjectPtr<UStoreItemDataObject> StoreItemData,
const int32 SelectedPriceIndex,
const int32 Quantity = 1) const; -
Declare the following delegate and function to handle the response from backend.
public:
// ...
FOnOrderComplete OnCheckoutCompleteDelegates;private:
// ...
void OnCreateNewOrderComplete(
bool bWasSuccessful,
const FAccelByteModelsOrderInfo& OrderInfo,
const FOnlineErrorAccelByte& OnlineError) const; -
Open the
StoreItemPurchaseSubsystem_Starter
CPP file and implement theCreateNewOrder()
function. This function sends the item purchase request to the backend.void UStoreItemPurchaseSubsystem_Starter::CreateNewOrder(
const APlayerController* OwningPlayer,
const TWeakObjectPtr<UStoreItemDataObject> StoreItemData,
const int32 SelectedPriceIndex,
const int32 Quantity) const
{
const UStoreItemPriceDataObject* SelectedPrice = StoreItemData->GetPrices()[SelectedPriceIndex];
const FAccelByteModelsOrderCreate Order{
StoreItemData->GetStoreItemId(),
Quantity,
static_cast<int32>(SelectedPrice->GetRegularPrice()) * Quantity,
static_cast<int32>(SelectedPrice->GetFinalPrice()) * Quantity,
FPreConfigCurrency::GetCodeFromType(SelectedPrice->GetCurrencyType())
};
PurchaseInterface->CreateNewOrder(
*GetLocalPlayerUniqueNetId(OwningPlayer).Get(),
Order);
} -
Implement the
OnCreateNewOrderComplete()
function. This function triggers theOnCheckoutCompleteDelegates
delegate with the data returned from the backend.void UStoreItemPurchaseSubsystem_Starter::OnCreateNewOrderComplete(
bool bWasSuccessful,
const FAccelByteModelsOrderInfo& OrderInfo,
const FOnlineErrorAccelByte& OnlineError) const
{
if (OnCheckoutCompleteDelegates.IsBound())
{
OnCheckoutCompleteDelegates.Broadcast(OnlineError);
}
} -
Still in the CPP file, locate the
Initialize()
function and replace the existing implementation with the code below. This code binds theOnCreateNewOrderComplete()
function to the corresponding delegate in theFOnlinePurchaseAccelByte
interface.void UStoreItemPurchaseSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
const IOnlinePurchasePtr PurchasePtr = Online::GetSubsystem(GetWorld())->GetPurchaseInterface();
ensure(PurchasePtr);
PurchaseInterface = StaticCastSharedPtr<FOnlinePurchaseAccelByte>(PurchasePtr);
ensure(PurchaseInterface);
PurchaseInterface->OnCreateNewOrderCompleteDelegates.AddUObject(this, &ThisClass::OnCreateNewOrderComplete);
} -
Navigate to the
Deinitialize()
function and replace the existing implementation with the code below. This code unbinds theOnCreateNewOrderComplete()
function.void UStoreItemPurchaseSubsystem_Starter::Deinitialize()
{
Super::Deinitialize();
PurchaseInterface->ClearOnCreateNewOrderCompleteDelegates(this);
}
Resources
- The files used in this tutorial section are available in the Byte Wars GitHub repository.