GS2-Showcase
This feature is used to sell products in the game.
The difference from GS2-Exchange is the existence of a showcase. DisplayItems can be placed on a showcase, and you can configure the cost required to purchase a DisplayItem and the reward obtained when purchasing it.
There are two types of showcases: the “Standard Showcase,” on which fixed DisplayItems are placed, and the “Random Showcase,” on which the displayed contents are randomly drawn at regular intervals.
graph TD Master["Master data"] --> Showcase["Standard showcase<br/>(ShowcaseModel)"] Master --> RandomShowcase["Random showcase<br/>(RandomShowcaseModel)"] Showcase --> DisplayItem["DisplayItem<br/>(SalesItem / SalesItemGroup)"] RandomShowcase --> RandomDisplayItem["RandomDisplayItem<br/>(stock / weight)"] Player["Player"] -- Buy --> DisplayItem Player -- RandomShowcaseBuy --> RandomDisplayItem DisplayItem -- "consumeActions / acquireActions" --> Transaction["GS2-Distributor<br/>execute transaction"] RandomDisplayItem -- "consumeActions / acquireActions" --> Transaction
Standard Showcase
On a standard showcase, all of the specified DisplayItems are placed. There are two types of DisplayItem: “SalesItem” and “SalesItemGroup.”
SalesItem
A SalesItem can be configured with the cost required to purchase it and the reward obtained when purchased.
A SalesItem expresses its behavior using the following three kinds of actions.
| Item | Description |
|---|---|
verifyActions |
Verify actions for purchase conditions. Verifies the state of the specified microservice, and rejects the purchase if the conditions are not met. |
consumeActions |
Actions consumed at the time of purchase. Currency in GS2-Money2 or items in GS2-Inventory, etc., are consumed as the cost. |
acquireActions |
Actions to acquire at the time of purchase. Distributes rewards to any microservice such as GS2-Inventory / GS2-Experience / GS2-Lottery. |
SalesItemGroup
A SalesItemGroup is a mechanism in which the SalesItem being sold changes depending on the number of purchases.
A SalesItemGroup can contain multiple SalesItems, and for all but the last item in the list, a GS2-Limit counter increment must be set as the cost. When a SalesItemGroup is placed on a showcase, the system determines whether the internal SalesItems are purchasable, and the first SalesItem determined to be purchasable is displayed.
By using this feature, you can sell an item at half price for first purchases only, realize items whose price rises with each purchase, or add a bonus on the 10th purchase, and so on.
graph LR
Buy["Purchase request"] --> Check1{"SalesItem 1<br/>(first-time only)"}
Check1 -- Purchasable --> Sell1["Sell SalesItem 1"]
Check1 -- Already purchased --> Check2{"SalesItem 2<br/>(2nd to 9th time)"}
Check2 -- Purchasable --> Sell2["Sell SalesItem 2"]
Check2 -- Limit reached --> Check3{"SalesItem 3<br/>(10th-time bonus)"}
Check3 -- Purchasable --> Sell3["Sell SalesItem 3"]
Random Showcase
On a random showcase, a specified number of DisplayItems are randomly drawn from those specified in master data and placed.
A random showcase is controlled by the following parameters.
| Item | Description |
|---|---|
maximumNumberOfChoice |
The maximum number of DisplayItems placed in a single drawing |
displayItems |
The list of RandomDisplayItems eligible for drawing. Each item has a weight (drawing weight) and stock (inventory). |
baseTimestamp / resetIntervalHours |
The interval at which the displayed contents are redrawn. The display is refreshed at a fixed interval starting from the specified time. |
salesPeriodEventId |
A GS2-Schedule event ID. Specify this when you want to restrict the sales period. |
The drawing result is stored per player as RandomShowcaseStatus, and stock is consumed at the time of purchase. When the redraw interval is reached, the displayed contents are updated on the next access.
Adjustment via Buffs
By integrating with GS2-Buff, you can dynamically adjust acquireActions, verifyActions, and consumeActions of DisplayItem and RandomDisplayItemModel. For random display items, the stock can also be overwritten. This allows flexible modification of rewards, required costs, and inventory quantities to align with events or campaigns.
Script Triggers
By setting buyScript on the namespace, you can invoke a custom script at the timing of an item purchase.
The main configurable event triggers and script setting names are as follows:
buyScript: When an item is purchased
By leveraging these, you can insert custom validation, audit log output, KPI aggregation, etc., into the purchase process.
Transaction Actions
GS2-Showcase provides the following transaction actions:
- Consume actions: increment purchase count
- Acquire actions: decrement purchase count, force re-draw of a random showcase
By using “force re-draw of a random showcase” as an acquire action, you can safely execute processes such as forcefully updating the shop’s displayed items as a reward when a specific item is obtained or a quest is cleared, all within a transaction. This enables providing a product lineup that is timely and aligned with the player’s progress. Additionally, by using “decrement purchase count,” operations such as individually restoring product purchase limits (for example, re-releasing limited items) are also facilitated.
Master Data Operation
Registering master data allows you to configure data and behaviors available to the microservice.
Master data types include the following:
ShowcaseModel: Definition of a standard showcase. You can placeSalesItemorSalesItemGroupasDisplayItem.RandomShowcaseModel: Definition of a random showcase.maximumNumberOfChoiceitems are drawn from a list ofRandomDisplayItemModeland displayed.SalesItem/SalesItemMaster: Definition of a single product for sale.SalesItemGroup/SalesItemGroupMaster: Definition of a product group whose displayed contents change depending on the purchase status.
Master data can be registered via the Management Console, by reflecting data from GitHub, or by setting up workflows to register via CI using GS2-Deploy.
The following is a JSON example of master data including a random showcase.
{
"version": "2019-09-13",
"showcases": [
{
"name": "showcase-0001",
"metadata": "Regular shop",
"salesPeriodEventId": null,
"displayItems": [
{
"displayItemId": "display-item-0001",
"type": "salesItem",
"salesItemName": "item-0001"
}
]
}
],
"randomShowcases": [
{
"name": "random-showcase-0001",
"metadata": "Daily shop",
"maximumNumberOfChoice": 4,
"baseTimestamp": 1700000000000,
"resetIntervalHours": 24,
"displayItems": [
{
"name": "display-item-0001",
"weight": 10,
"stock": 3,
"consumeActions": [],
"acquireActions": []
}
]
}
]
}Example Implementation
Standard Showcase
Get a showcase
var item = await gs2.Showcase.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Showcase(
showcaseName: "showcase-0001"
).ModelAsync(); const auto Domain = Gs2->Showcase->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Showcase(
"showcase-0001" // showcaseName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();Get a list of showcases
An API for retrieving the list of showcases is not provided in the game engine SDK. If you need to retrieve it, please use the Management Console / GS2 CLI / general SDKs for each language (C# / Go / Python / TypeScript / PHP / Java).
Buy an item
var result = await gs2.Showcase.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Showcase(
showcaseName: "showcase-0001"
).DisplayItem(
displayItemId: "display-item-0001"
).BuyAsync(
quantity: 1,
config: null
);
await result.WaitAsync(); const auto Domain = Gs2->Showcase->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Showcase(
"showcase-0001" // showcaseName
)->DisplayItem(
"display-item-0001" // displayItemId
);
const auto Future = Domain->Buy(
1, // quantity
nullptr // config
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Transaction = Future->GetTask().Result();
const auto Future2 = Transaction->Wait();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;Buy is executed as a transaction processed through GS2-Distributor. To reflect the transaction result, call WaitAsync / Wait on the returned EzTransactionDomain and wait for the transaction to complete.
Random Showcase
Get a showcase
var items = await gs2.Showcase.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).RandomShowcase(
showcaseName: "showcase-0001"
).RandomDisplayItemsAsync(
).ToListAsync(); const auto It = Gs2->Showcase->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->RandomShowcase(
"showcase-0001" // showcaseName
)->RandomDisplayItems(
);
TArray<Gs2::UE5::Showcase::Model::FEzRandomDisplayItemPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Buy an item
var result = await gs2.Showcase.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).RandomShowcase(
showcaseName: "showcase-0001"
).RandomDisplayItem(
displayItemName: "display-item-0001"
).RandomShowcaseBuyAsync(
quantity: 1,
config: null
);
await result.WaitAsync(); const auto Future = Gs2->Showcase->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->RandomShowcase(
"showcase-0001" // showcaseName
)->RandomDisplayItem(
"display-item-0001" // displayItemName
)->RandomShowcaseBuy(
1, // quantity
nullptr // config
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}
const auto Transaction = Future->GetTask().Result();
const auto Future2 = Transaction->Wait();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;Get the state of a random showcase
A random showcase holds, per player, the drawing result and purchase count as RandomShowcaseStatus.
Retrieve this when you want to check the next redraw time or the number of items already purchased.
var item = await gs2.Showcase.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).RandomShowcaseStatus(
showcaseName: "showcase-0001"
).ModelAsync(); const auto Domain = Gs2->Showcase->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->RandomShowcaseStatus(
"showcase-0001" // showcaseName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();