GS2-Mission Transaction Actions
Combining actions, and concurrency
The background common to every service is collected in Combining Transaction Actions. Read that first; the rest of this section is what GS2-Mission adds to it.
The transaction actions of GS2-Mission address two kinds of target.
- Counter: identified by the combination of namespace, user, and counter name. It holds one value per reset type.
- Progress of a mission group: identified by the combination of namespace, user, and mission group. It records which tasks are complete and which rewards have been taken, per task.
| Operation | Repeated in one transaction | Across a nested transaction | Boundary that separates targets |
|---|---|---|---|
Adding to a counterIncreaseCounterByUserId | Combined, and the values are added up | Added up | namespace, user, counter name |
Subtracting from a counterDecreaseCounterByUserId | Combined, and the values are added up | Fails | namespace, user, counter name |
Resetting a counterResetCounterByUserId | The scopes are combined into one action. Above a total of 20 scopes the combining stops and the second and later ones are silently dropped | Fails | namespace, user, counter name |
Setting a counterSetCounterByUserId | Combined if equal; rejected when the transaction is issued if the values differ | Fails | namespace, user, counter name |
Taking a rewardReceiveByUserId BatchReceiveByUserId | Gathered into one, with duplicate task names removed | Fails | namespace, user, mission group |
Reverting a receiveRevertReceiveByUserId | Combined if the task is the same; rejected when the transaction is issued if the tasks differ | Fails | namespace, user, mission group |
| All verify actions | First wins. VerifyCounterValueByUserId is the exception: two with differing thresholds are both checked | No collision, because they only read | target, verify type, threshold |
Only additions to a counter are written as a pure increment; everything else rewrites the counter as a whole. That is why additions merge even across a nested transaction, while subtracting, resetting, and setting cannot sit alongside another row on the same boundary. Two subtractions are fine because they are combined into one before execution.
Do not place reverts for different tasks in the same transaction. A revert writes to one row per mission group, so the boundary cannot be split per task. Listing the same task more than once is fine — the duplicates are removed — but listing two different tasks is rejected with an error (400). The check runs where acquire actions are gathered, so it applies not only to APIs that issue a transaction but also to APIs that merely preview the rewards. Receives are solved by folding them into a batch receive, but there is no API that takes an array of reverts, so the same fix is not available here.
Receives for the same mission group are gathered into a single batch receive however many you list. Writing the same task twice is not an error either; the duplicate is simply removed. Only when the gathered task count exceeds the limit of the receiving side does it become a too-many error (400) at run time.
There is one combination the table does not cover. You cannot take the reward of a task that the same transaction completed. Completion is recorded as a result of adding to a counter, but taking a reward looks at the progress as of the start of the transaction, so it cannot see that completion yet. Split the transaction.
Taking a large number of tasks at once can hit an internal limit on how much can be checked in one go. As a rough guide, this starts to happen somewhere above a dozen or so tasks in one transaction. Where a transaction that takes many rewards fails, split it into several.
Verify actions look at the state as of the start of the transaction. You cannot verify a counter value or a completion that is reached by the same transaction.
Take care with nested transactions
Additions to a counter are safe across a nested transaction: updates arriving by either route end up as one update. Progress is safe too, as long as the tasks differ.
Subtracting, resetting, and setting a counter are not. Touching the same counter both from the inside and from the outside makes the transaction fail. Check whether a reward or a draw moves one of your mission counters.
If you want to avoid these restrictions
Adding to and setting a counter, and reverting a receive, are acquire actions; subtracting from and resetting a counter, and taking a reward, are consume actions. Turning acquireActionUseJobQueue on clears a collision between an addition and a set, but a collision with a subtraction or a reset, and taking the reward of the same task twice, are between consume actions and need enableAtomicCommit turned off. Reverting receives for different tasks cannot be worked around by either setting: the check runs when the transaction is issued, before the setting is applied. Split the transaction.
Concurrency and retries
Additions to a counter do not conflict however many concurrent requests overlap. Within one mission group, completions and receives for different tasks do not conflict either.
Subtracting, resetting, and setting a counter, taking the same reward, and changes made through the console or the server API are written with a revision check, so they may return a conflict (409) when another update to the same target overlaps. It also happens across a counter’s reset boundary. Nothing is wrong with the request, so retrying will succeed.
Verify Action
Gs2Mission:VerifyCompleteByUserId
Verify Completion Status by User ID
Verifies the completion or receipt status of a mission task for the specified user.
Quantity specification supported: NO
| Type | Condition | Required | Default | Value Limits | Description | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||||||||||||||
| missionGroupName | string | ✓ | ~ 128 chars | Mission Group Name The name of the mission group that this completion record belongs to. One Complete record exists per user per mission group. | ||||||||||||||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||||||||||||||
| verifyType | string (enum) enum { “completed”, “notCompleted”, “received”, “notReceived”, “completedAndNotReceived” } | ✓ | Type of verification
| |||||||||||||||
| missionTaskName | string | ✓ | ~ 128 chars | Mission Task Model name Unique Mission Task Model name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||||||||||||||
| multiplyValueSpecifyingQuantity | bool | false | Whether to multiply the value used for verification when specifying the quantity | |||||||||||||||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:VerifyCompleteByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"missionGroupName": "[string]Mission Group Name",
"userId": "[string]User ID",
"verifyType": "[string]Type of verification",
"missionTaskName": "[string]Mission Task Model name",
"multiplyValueSpecifyingQuantity": "[bool]Whether to multiply the value used for verification when specifying the quantity",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:VerifyCompleteByUserId
request:
namespaceName: "[string]Namespace name"
missionGroupName: "[string]Mission Group Name"
userId: "[string]User ID"
verifyType: "[string]Type of verification"
missionTaskName: "[string]Mission Task Model name"
multiplyValueSpecifyingQuantity: "[bool]Whether to multiply the value used for verification when specifying the quantity"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").verify.verify_complete_by_user_id({
namespaceName="[string]Namespace name",
missionGroupName="[string]Mission Group Name",
userId="[string]User ID",
verifyType="[string]Type of verification",
missionTaskName="[string]Mission Task Model name",
multiplyValueSpecifyingQuantity="[bool]Whether to multiply the value used for verification when specifying the quantity",
timeOffsetToken="[string]Time offset token",
})Gs2Mission:VerifyCounterValueByUserId
Verify counter value by User ID
Verifies that a counter’s scoped value meets the specified condition for the specified user.
Quantity specification supported: YES
| Type | Condition | Required | Default | Value Limits | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||||||||||||||||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||||||||||||||||
| counterName | string | ✓ | ~ 128 chars | Counter Model name The name of the Counter Model that this counter instance is based on. Links to the counter model definition that specifies the scopes and reset timings. | ||||||||||||||||
| verifyType | string (enum) enum { “less”, “lessEqual”, “greater”, “greaterEqual”, “equal”, “notEqual” } | ✓ | Type of verification
| |||||||||||||||||
| scopeType | string (enum) enum { “resetTiming”, “verifyAction” } | “resetTiming” | Scope type Indicates whether this scoped value is based on a reset timing schedule or a verify action condition.
| |||||||||||||||||
| resetType | string (enum) enum { “notReset”, “daily”, “weekly”, “monthly”, “days” } | {scopeType} == “resetTiming” | ✓* | Reset timing The reset timing for this scoped value. Determines the period over which the counter value is accumulated before being reset. Only applicable when scopeType is “resetTiming”.
* Required if scopeType is “resetTiming” | ||||||||||||||||
| conditionName | string | {scopeType} == “verifyAction” | ✓* | ~ 128 chars | Condition Name The name of the verify action condition that this scoped value corresponds to. Used to identify which condition scope this value belongs to. Only applicable when scopeType is “verifyAction”. * Required if scopeType is “verifyAction” | |||||||||||||||
| value | long | 0 | 0 ~ 9223372036854775805 | Count value The accumulated counter value for this scope. Increases when the counter is incremented and decreases when decremented. The value is capped at the maximum and will not go below zero. | ||||||||||||||||
| multiplyValueSpecifyingQuantity | bool | false | Whether to multiply the value used for verification when specifying the quantity | |||||||||||||||||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:VerifyCounterValueByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"counterName": "[string]Counter Model name",
"verifyType": "[string]Type of verification",
"scopeType": "[string]Scope type",
"resetType": "[string]Reset timing",
"conditionName": "[string]Condition Name",
"value": "[long]Count value",
"multiplyValueSpecifyingQuantity": "[bool]Whether to multiply the value used for verification when specifying the quantity",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:VerifyCounterValueByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
counterName: "[string]Counter Model name"
verifyType: "[string]Type of verification"
scopeType: "[string]Scope type"
resetType: "[string]Reset timing"
conditionName: "[string]Condition Name"
value: "[long]Count value"
multiplyValueSpecifyingQuantity: "[bool]Whether to multiply the value used for verification when specifying the quantity"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").verify.verify_counter_value_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
counterName="[string]Counter Model name",
verifyType="[string]Type of verification",
scopeType="[string]Scope type",
resetType="[string]Reset timing",
conditionName="[string]Condition Name",
value="[long]Count value",
multiplyValueSpecifyingQuantity="[bool]Whether to multiply the value used for verification when specifying the quantity",
timeOffsetToken="[string]Time offset token",
})Consume Action
Gs2Mission:ReceiveByUserId
Receive rewards for mission accomplishment
Marks the specified mission task as received for the specified user.
Quantity specification supported: NO
Reversible action: YES
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| missionGroupName | string | ✓ | ~ 128 chars | Mission Group Name The name of the mission group that this completion record belongs to. One Complete record exists per user per mission group. | ||
| missionTaskName | string | ✓ | ~ 128 chars | Task Name | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:ReceiveByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"missionGroupName": "[string]Mission Group Name",
"missionTaskName": "[string]Task Name",
"userId": "[string]User ID",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:ReceiveByUserId
request:
namespaceName: "[string]Namespace name"
missionGroupName: "[string]Mission Group Name"
missionTaskName: "[string]Task Name"
userId: "[string]User ID"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").consume.receive_by_user_id({
namespaceName="[string]Namespace name",
missionGroupName="[string]Mission Group Name",
missionTaskName="[string]Task Name",
userId="[string]User ID",
timeOffsetToken="[string]Time offset token",
})Gs2Mission:BatchReceiveByUserId
Receive rewards for multiple mission tasks in bulk
Marks multiple mission tasks as received at once within the same mission group.
Quantity specification supported: NO
Reversible action: NO
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| missionGroupName | string | ✓ | ~ 128 chars | Mission Group Name The name of the mission group that this completion record belongs to. One Complete record exists per user per mission group. | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| missionTaskNames | List<string> | ✓ | 1 ~ 100 items | Task name list | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:BatchReceiveByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"missionGroupName": "[string]Mission Group Name",
"userId": "[string]User ID",
"missionTaskNames": [
"[string]Task Name"
],
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:BatchReceiveByUserId
request:
namespaceName: "[string]Namespace name"
missionGroupName: "[string]Mission Group Name"
userId: "[string]User ID"
missionTaskNames:
- "[string]Task Name"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").consume.batch_receive_by_user_id({
namespaceName="[string]Namespace name",
missionGroupName="[string]Mission Group Name",
userId="[string]User ID",
missionTaskNames={
"[string]Task Name"
},
timeOffsetToken="[string]Time offset token",
})Gs2Mission:DecreaseCounterByUserId
Decrease counter by User ID
Subtracts the specified value from the counter for the specified user.
Quantity specification supported: YES
Reversible action: YES
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| counterName | string | ✓ | ~ 128 chars | Counter Model name The name of the Counter Model that this counter instance is based on. Links to the counter model definition that specifies the scopes and reset timings. | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| value | long | ✓ | 1 ~ 9223372036854775805 | Value to be subtracted | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:DecreaseCounterByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"counterName": "[string]Counter Model name",
"userId": "[string]User ID",
"value": "[long]Value to be subtracted",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:DecreaseCounterByUserId
request:
namespaceName: "[string]Namespace name"
counterName: "[string]Counter Model name"
userId: "[string]User ID"
value: "[long]Value to be subtracted"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").consume.decrease_counter_by_user_id({
namespaceName="[string]Namespace name",
counterName="[string]Counter Model name",
userId="[string]User ID",
value="[long]Value to be subtracted",
timeOffsetToken="[string]Time offset token",
})Gs2Mission:ResetCounterByUserId
Reset counter by User ID
Resets the counter values for the specified scopes for the specified user.
Quantity specification supported: NO
Reversible action: NO
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| counterName | string | ✓ | ~ 128 chars | Counter Model name The name of the Counter Model that this counter instance is based on. Links to the counter model definition that specifies the scopes and reset timings. | ||
| scopes | List<ScopedValue> | ✓ | 1 ~ 20 items | List of scopes | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:ResetCounterByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"userId": "[string]User ID",
"counterName": "[string]Counter Model name",
"scopes": [
{
"scopeType": "[string]Scope type",
"resetType": "[string]Reset timing",
"conditionName": "[string]Condition Name",
"value": "[long]Count value",
"nextResetAt": "[long]Next reset timing",
"updatedAt": "[long]Last Updated Timestamp"
}
],
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:ResetCounterByUserId
request:
namespaceName: "[string]Namespace name"
userId: "[string]User ID"
counterName: "[string]Counter Model name"
scopes:
- scopeType: "[string]Scope type"
resetType: "[string]Reset timing"
conditionName: "[string]Condition Name"
value: "[long]Count value"
nextResetAt: "[long]Next reset timing"
updatedAt: "[long]Last Updated Timestamp"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").consume.reset_counter_by_user_id({
namespaceName="[string]Namespace name",
userId="[string]User ID",
counterName="[string]Counter Model name",
scopes={
{
scopeType="[string]Scope type",
resetType="[string]Reset timing",
conditionName="[string]Condition Name",
value="[long]Count value",
nextResetAt="[long]Next reset timing",
updatedAt="[long]Last Updated Timestamp"
}
},
timeOffsetToken="[string]Time offset token",
})Acquire Action
Gs2Mission:RevertReceiveByUserId
Revert the status of mission accomplishment to unreceived
Reverts the received status of a mission task back to unreceived.
Quantity specification supported: NO
Reversible action: YES
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| missionGroupName | string | ✓ | ~ 128 chars | Mission Group Name The name of the mission group that this completion record belongs to. One Complete record exists per user per mission group. | ||
| missionTaskName | string | ✓ | ~ 128 chars | Task Name | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:RevertReceiveByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"missionGroupName": "[string]Mission Group Name",
"missionTaskName": "[string]Task Name",
"userId": "[string]User ID",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:RevertReceiveByUserId
request:
namespaceName: "[string]Namespace name"
missionGroupName: "[string]Mission Group Name"
missionTaskName: "[string]Task Name"
userId: "[string]User ID"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").acquire.revert_receive_by_user_id({
namespaceName="[string]Namespace name",
missionGroupName="[string]Mission Group Name",
missionTaskName="[string]Task Name",
userId="[string]User ID",
timeOffsetToken="[string]Time offset token",
})Gs2Mission:IncreaseCounterByUserId
Increase counter by User ID
Adds the specified value to the counter for the specified user. After incrementing, all mission tasks referencing this counter are automatically re-evaluated, and any newly completed missions are returned in the changedCompletes response.
Quantity specification supported: YES
Reversible action: YES
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| counterName | string | ✓ | ~ 128 chars | Counter Model name The name of the Counter Model that this counter instance is based on. Links to the counter model definition that specifies the scopes and reset timings. | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| value | long | ✓ | 1 ~ 9223372036854775805 | Value to be added | ||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:IncreaseCounterByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"counterName": "[string]Counter Model name",
"userId": "[string]User ID",
"value": "[long]Value to be added",
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:IncreaseCounterByUserId
request:
namespaceName: "[string]Namespace name"
counterName: "[string]Counter Model name"
userId: "[string]User ID"
value: "[long]Value to be added"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").acquire.increase_counter_by_user_id({
namespaceName="[string]Namespace name",
counterName="[string]Counter Model name",
userId="[string]User ID",
value="[long]Value to be added",
timeOffsetToken="[string]Time offset token",
})Gs2Mission:SetCounterByUserId
Set counter by User ID
Sets the counter scoped values directly for the specified user, replacing existing values. Returns both the old and new counter states, as well as any newly completed missions in the changedCompletes response.
Quantity specification supported: NO
Reversible action: NO
| Type | Condition | Required | Default | Value Limits | Description | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128 chars | Namespace name Unique Namespace name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.). | ||
| counterName | string | ✓ | ~ 128 chars | Counter Model name The name of the Counter Model that this counter instance is based on. Links to the counter model definition that specifies the scopes and reset timings. | ||
| userId | string | ✓ | ~ 128 chars | User ID Specify #{userId} to substitute the currently logged-in user’s ID. | ||
| values | List<ScopedValue> | 0 ~ 20 items | List of values to be set | |||
| timeOffsetToken | string | ~ 1024 chars | Time offset token |
{
"action": "Gs2Mission:SetCounterByUserId",
"request": {
"namespaceName": "[string]Namespace name",
"counterName": "[string]Counter Model name",
"userId": "[string]User ID",
"values": [
{
"scopeType": "[string]Scope type",
"resetType": "[string]Reset timing",
"conditionName": "[string]Condition Name",
"value": "[long]Count value",
"nextResetAt": "[long]Next reset timing",
"updatedAt": "[long]Last Updated Timestamp"
}
],
"timeOffsetToken": "[string]Time offset token"
}
}action: Gs2Mission:SetCounterByUserId
request:
namespaceName: "[string]Namespace name"
counterName: "[string]Counter Model name"
userId: "[string]User ID"
values:
- scopeType: "[string]Scope type"
resetType: "[string]Reset timing"
conditionName: "[string]Condition Name"
value: "[long]Count value"
nextResetAt: "[long]Next reset timing"
updatedAt: "[long]Last Updated Timestamp"
timeOffsetToken: "[string]Time offset token"transaction.service("mission").acquire.set_counter_by_user_id({
namespaceName="[string]Namespace name",
counterName="[string]Counter Model name",
userId="[string]User ID",
values={
{
scopeType="[string]Scope type",
resetType="[string]Reset timing",
conditionName="[string]Condition Name",
value="[long]Count value",
nextResetAt="[long]Next reset timing",
updatedAt="[long]Last Updated Timestamp"
}
},
timeOffsetToken="[string]Time offset token",
})