GS2-LoginReward

Login bonus feature

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();

Detailed Reference