GS2-Distributor
GS2-Distributor is the core service for executing transactions that span across multiple GS2 microservices. It provides the foundation for safely executing a sequence of operations that span multiple microservices, such as “consume an item to receive a reward” or “consume stamina to start a quest”.
In GS2, operations that are disadvantageous to the player are called “consume actions”, and operations that are advantageous are called “acquire actions”.
GS2-Distributor receives a “transaction” that bundles these actions together, then forwards and executes them against the appropriate microservices, allowing all of the processing that makes up the game cycle to be handled in a consistent manner.
For more details on how transactions work, see Transaction.
Actions that compose a transaction
A GS2 transaction is composed of the following three types of actions.
graph LR Issue["Transaction Issued<br/>(Store / Quest / Mission, etc.)"] --> Verify["Verify Action"] Verify --> Consume["Consume Action"] Consume --> Acquire["Acquire Action"] Acquire --> Done["Done"]
Verify Action
An action that checks in advance, before starting transaction execution, whether the consume actions and acquire actions can be performed.
For example, conditions such as “must possess a specific item”, “rank must be at or above a certain value”, or “a specific quest must already be cleared” can be checked before any consumption occurs.
If verification fails, the consume and acquire actions are not executed.
Consume Action
An action that represents processing that is disadvantageous to the player.
This includes consuming items, consuming currency, consuming stamina, incrementing a usage limit counter, and similar operations.
When a consume action is executed, each microservice issues a signature proving that “it has been executed”. This signature is verified when the next acquire action is executed, and the acquire action cannot run unless all consume actions have been passed.
Acquire Action
An action that represents processing that is advantageous to the player.
This includes acquiring items, acquiring currency, gaining experience, starting a quest, and similar operations.
An acquire action is executed only when the signatures indicating that all related consume actions have completed successfully are collected.
Structure of a transaction
A GS2 transaction is issued in the structure of “multiple consume actions + one acquire action”.
Transactions are returned as the response of transaction issuance APIs from each microservice such as GS2-Showcase / GS2-Quest / GS2-Mission, and the game client executes the obtained transaction through GS2-Distributor.
The GS2 SDK includes a mechanism that automatically executes transactions, and in most cases the game side does not need to be conscious of transaction execution.
Simply calling WaitAsync on the TransactionDomain included in the issuance API result causes the consume and acquire actions to be executed in the correct order and the result to be retrieved.
DistributorModel
A DistributorModel is a unit of resource distribution configuration registered within a namespace.
The following can be configured for a DistributorModel:
inboxNamespaceId: GRN of the GS2-Inbox namespace to which contents are automatically forwarded if they overflow.whiteListTargetIds: Whitelist of actions allowed to be executed via transactions.
By preparing multiple DistributorModels, you can use different forwarding rules for different purposes such as store, quest, and mission.
Overflow handling for distributions
When executing an acquire action, the result may not be receivable on the spot due to reasons such as exceeding the player’s holding limit.
To prepare for such cases, setting the inboxNamespaceId of the DistributorModel will automatically forward the undelivered content as a message to GS2-Inbox, allowing the player to receive it later.
This prevents situations where rewards are lost because they could not be received.
graph TD
Acquire["Execute Acquire Action"] --> Check{"Grantable to player?"}
Check -- Yes --> Granted["Grant to player"]
Check -- "No (limit exceeded, etc.)" --> Inbox["Forward to GS2-Inbox"]
Inbox --> Receive["Player receives later"]
Batch Request
GS2-Distributor provides a “batch request” feature that bundles multiple GS2 API calls into a single request for execution.
Since multiple service queries can be completed in a single API call, this reduces network round trips and improves response time. It is especially useful in cases such as fetching initial data from multiple microservices at game startup.
Each request included in a batch request is processed independently, and a response is returned for each.
Master Data Management
Registering master data allows you to configure data and behaviors available to the microservice.
Master data types include:
DistributorModel: Transaction forwarding rules and the forwarding destination on overflow
Below is a JSON example of master data:
{
"version": "2019-09-09",
"distributorModels": [
{
"name": "default",
"metadata": "default distributor",
"inboxNamespaceId": "grn:gs2:{region}:{ownerId}:inbox:namespace-0001"
}
]
}Master data can be registered via the management console, imported from GitHub, or registered from CI using GS2-Deploy.
Transaction Actions
GS2-Distributor is the service that executes transactions issued by other services, and it does not itself issue consume or acquire actions.
Example Implementation
Executing a transaction
In the GS2 SDK, calling WaitAsync on the TransactionDomain returned by each service’s transaction issuance API automatically executes the transaction through GS2-Distributor.
Below is an example of executing a transaction using the GS2-Mission reward receipt process.
var transaction = await gs2.Mission.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Complete(
missionGroupName: "group-0001"
).ReceiveRewardsAsync(
missionTaskName: "task-0001"
);
await transaction.WaitAsync(); const auto Future = Gs2->Mission->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->Complete(
"group-0001" // missionGroupName
)->ReceiveRewards(
"task-0001" // missionTaskName
);
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;Executing a batch request
Multiple GS2 API calls are executed in a single round trip.
var result = await gs2.Distributor.Namespace(
namespaceName: "namespace-0001"
).BatchExecuteApiAsync(
requestPayloads: new [] {
new Gs2.Unity.Gs2Distributor.Model.EzBatchRequestPayload
{
RequestId = "1",
Service = "inventory",
MethodName = "describe_inventories",
Parameter = "{\"namespaceName\":\"namespace-0001\"}",
},
new Gs2.Unity.Gs2Distributor.Model.EzBatchRequestPayload
{
RequestId = "2",
Service = "experience",
MethodName = "describe_statuses",
Parameter = "{\"namespaceName\":\"namespace-0001\"}",
},
}
); const auto Future = Gs2->Distributor->Namespace(
"namespace-0001" // namespaceName
)->BatchExecuteApi(
[]
{
const auto v = MakeShared<TArray<Gs2::UE5::Distributor::Model::FEzBatchRequestPayloadPtr>>();
v->Add(MakeShared<Gs2::UE5::Distributor::Model::FEzBatchRequestPayload>()
->WithRequestId(TOptional<FString>("1"))
->WithService(TOptional<FString>("inventory"))
->WithMethodName(TOptional<FString>("describe_inventories"))
->WithParameter(TOptional<FString>("{\"namespaceName\":\"namespace-0001\"}")));
v->Add(MakeShared<Gs2::UE5::Distributor::Model::FEzBatchRequestPayload>()
->WithRequestId(TOptional<FString>("2"))
->WithService(TOptional<FString>("experience"))
->WithMethodName(TOptional<FString>("describe_statuses"))
->WithParameter(TOptional<FString>("{\"namespaceName\":\"namespace-0001\"}")));
return v;
}()
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Freezing master data
If master data is updated after a transaction has been issued, inconsistencies may arise between the issued transaction and the current master data.
By calling FreezeMasterData of GS2-Distributor, the master data version used for the logged-in player is fixed, avoiding inconsistencies caused by switching master data during gameplay.
await gs2.Distributor.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).FreezeMasterDataAsync(
); // FreezeMasterData is not provided in the Unreal Engine SDK.
// Use the management console / GS2 CLI / general-purpose SDKs for various languages (C# / Go / Python / TypeScript / PHP / Java).