GS2-LoginReward
This is a mechanism that distributes rewards to players who log in to the game every day. It provides a generic version of patterns frequently used in operations, such as distributing items that change daily, login bonuses that loop on a 7- or 30-day cycle, and the ability to retroactively compensate for missed days.
Mode
There are two ways to provide login bonuses: 《Scheduled Mode》 and 《Streaming Mode》.
graph LR
Mode{Bonus mode} --> Schedule["Scheduled Mode<br/>(mode: schedule)"]
Mode --> Streaming["Streaming Mode<br/>(mode: streaming)"]
Schedule -- Fixed to date --> SchEx["Day 1=Gold, Day 2=Silver...<br/>Missed days are skipped"]
Streaming -- Consumed in order --> StrEx["Item 1=Gold, Item 2=Silver...<br/>Carries over even if missed"]
Streaming --> Repeat{"repeat setting"}
Repeat -- enabled --> Loop["Restart from the beginning after reaching the end"]
Repeat -- disabled --> Stop["Stop at the end"]
Scheduled Mode
This mode is used in conjunction with GS2-Schedule events. Define the transaction actions to be distributed as rewards for each schedule date. Rewards change every 24 hours from the event start date and time, and each reward can be received once. Any rewards missed along the way will be skipped.
Streaming Mode
Streaming mode distributes the transaction actions set as rewards in the stream from the top in order. Even if there is a day you did not receive the reward, it will not be skipped and you will obtain the next reward in the stream.
If you associate a GS2-Schedule event with a Streaming Mode login bonus, you will receive the next reward in the stream every 24 hours from the event’s start date and time. If not set, you specify the time in 24-hour increments in the UTC time zone at which the reward type changes.
Repeat
Streaming-mode login bonuses can be configured to repeat.
If the repeat setting (repeat: enabled) is enabled, when the end of the stream is reached, the next day reward distribution resumes from the beginning of the stream.
By using this feature, a permanent login bonus can be looped every 7 or 30 days.
If you select repeat: disabled, once distribution has reached the end of the stream, no further rewards are obtained from that bonus. This can be used for time-limited campaigns.
Missed-receive Relief
The Missed-receive Relief feature can be used when you miss an item in Scheduled Mode, or when you end up in a state during the event period of Streaming Mode where you cannot obtain all items in the stream. By paying a configured cost, you can obtain the missed items.
Missed-receive relief is only available for login bonuses associated with GS2-Schedule events, and only rewards up to the number of days elapsed from the event’s start date can be received. In other words, future login bonuses cannot be received even if the cost is paid.
For the missed-receive relief cost, you can set consume actions such as GS2-Money2 in missedReceiveReliefConsumeActions, enabling operations where players spend gems to recover missed days.
You can also specify verify actions in missedReceiveReliefVerifyActions as preconditions for receipt.
Adjustment with buffs
By integrating with GS2-Buff, you can apply buffs to the bonus model’s acquireActions and missedReceiveReliefConsumeActions, dynamically adjusting the reward contents and missed-receive relief costs according to events.
For example, you can run operations such as “double login bonuses during the campaign period” or “halve missed-receive relief costs for VIP players”.
Script Triggers
Setting receiveScript in the namespace allows the receive / receiveDone script hooks to be called before and after the login-bonus receipt process. Scripts support both synchronous and asynchronous execution, with asynchronous execution supporting external integration via GS2-Script or Amazon EventBridge.
The main event triggers and script setting names that can be configured are as follows:
receiveScript(completion notification:receiveDone): before and after receiving the login bonus
Managing master data
By registering master data, you can configure the data and behavior available to the microservice.
The types of master data are as follows:
BonusModel: Defines daily or streaming reward distribution
| Master Data Field | Description |
|---|---|
name |
The bonus model name |
mode |
schedule (Scheduled) or streaming (Streaming) |
periodEventId |
The associated GS2-Schedule event ID (optional) |
resetHour |
UTC hour at which rewards switch in Streaming Mode when no event is associated |
repeat |
Loop behavior in Streaming Mode (enabled / disabled) |
rewards |
A list of acquireActions distributed at each step |
missedReceiveRelief |
Whether missed-receive relief is enabled (enabled / disabled) |
missedReceiveReliefVerifyActions |
Verify actions before executing missed-receive relief |
missedReceiveReliefConsumeActions |
Consume actions when executing missed-receive relief |
Below is an example JSON of master data.
{
"version": "2020-10-19",
"bonusModels": [
{
"name": "bonus-0001",
"metadata": "7days",
"mode": "streaming",
"resetHour": 15,
"repeat": "enabled",
"rewards": [
{ "acquireActions": [ { "action": "Gs2Inventory:AcquireItemSetByUserId", "request": "..." } ] },
{ "acquireActions": [ { "action": "Gs2Inventory:AcquireItemSetByUserId", "request": "..." } ] }
],
"missedReceiveRelief": "disabled"
}
]
}Master data can be registered from the Management Console, or you can set up a workflow that reflects data from GitHub or registers via CI using GS2-Deploy.
Transaction Actions
GS2-LoginReward provides the following transaction actions:
Consume Actions
| Action | Use |
|---|---|
Gs2LoginReward:MarkReceivedByUserId |
Marks the specified step as received. |
Acquire Actions
| Action | Use |
|---|---|
Gs2LoginReward:DeleteReceiveStatusByUserId |
Resets the receiving status. Use this when you want the player to receive the login bonus from the beginning again. |
Gs2LoginReward:UnmarkReceivedByUserId |
Reverts a specified step to the not-received state. |
By using “Reset receiving status” as an acquire action, it is possible to perform processes such as restarting the login bonus progress from the beginning when a specific item is acquired or at an event milestone. This allows for periodic campaign resets or providing players with opportunities to re-earn bonuses upon achieving special conditions.
Example implementation
Receiving login bonuses
ReceiveAsync automatically receives the next bonus available for receipt. Distributed items are returned as an EzTransactionDomain, so call WaitAsync on the return value to apply the result.
var transaction = await gs2.LoginReward.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Bonus(
).ReceiveAsync(
bonusModelName: "bonus-0001",
config: null
);
await transaction.WaitAsync(); const auto Future = Gs2->LoginReward->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Bonus(
)->Receive(
"bonus-0001", // bonusModelName
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;Receiving rewards for missed days (missed-receive relief)
By calling MissedReceiveAsync, you pay the cost defined in missedReceiveReliefConsumeActions to receive a specific past step that was missed.
var transaction = await gs2.LoginReward.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Bonus(
).MissedReceiveAsync(
bonusModelName: "bonus-0001",
stepNumber: 2,
config: null
);
await transaction.WaitAsync(); const auto Future = Gs2->LoginReward->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Bonus(
)->MissedReceive(
"bonus-0001", // bonusModelName
2, // stepNumber
nullptr // config
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Get the receipt status of the login bonus
ReceiveStatus.ReceivedSteps contains an array of booleans indicating whether each step has been received.
This can be used, for example, when displaying a calendar UI that shows “Day N received”.
var item = await gs2.LoginReward.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).ReceiveStatus(
bonusModelName: "bonus-0001"
).ModelAsync();
for (var i = 0; i < item.ReceivedSteps.Count; i++) {
Debug.Log($"step {i + 1}: {(item.ReceivedSteps[i] ? "received" : "not yet")}");
} const auto Domain = Gs2->LoginReward->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->ReceiveStatus(
"bonus-0001" // bonusModelName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();Check the contents of the login bonus
When displaying a preview in the UI of “the reward to be obtained tomorrow” or “the reward to be obtained on the last day”, you can obtain the AcquireActions of each step from the Rewards of the BonusModel.
var item = await gs2.LoginReward.Namespace(
namespaceName: "namespace-0001"
).BonusModel(
bonusModelName: "bonus-0001"
).ModelAsync();
foreach (var reward in item.Rewards) {
foreach (var action in reward.AcquireActions) {
Debug.Log($"{action.Action}: {action.Request}");
}
} const auto Domain = Gs2->LoginReward->Namespace(
"namespace-0001" // namespaceName
)->BonusModel(
"bonus-0001" // bonusModelName
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Item = Future->GetTask().Result();