GS2-Quest トランザクションアクション

検証/消費/入手の各トランザクションアクションの仕様

アクションの組み合わせと同時実行

すべてのサービスに共通する前提は トランザクションアクションの組み合わせ にまとめています。先にそちらを読んでください。この節の残りは GS2-Quest 固有の内容です。

GS2-Quest がトランザクションに指定できるアクションは 2 つで、どちらもネームスペースとユーザーの組で決まる 1 人のユーザーの進行を対象にします。進行は 1 ユーザーにつき 1 つだけ存在し、これがプレイヤーを同時に 1 つのクエストに限る仕組みになっています。

操作同じ行を重ねたとき入れ子越し別の対象になる境界直列実行モード有効時
進行の作成
CreateProgressByUserId
まったく同じ指定でも発行時にエラー失敗するネームスペース・ユーザー破棄との衝突は引き続き失敗する。破棄は消費アクションなので先に実行され、作成の存在チェック自体は通るが、破棄と作成は同じ行への 2 つの書き込みになり、それらをまとめられずトランザクションが失敗する (400)。そもそも作成と破棄を 1 つのトランザクションに直接並べること自体、発行時に拒否される。作成どうしの衝突も変わらず失敗する。後から実行される方は進行がすでに存在する状態を見て、自分自身の作成条件が成立しなくなるため、値が一致しないからではなく、この理由で引き続き失敗する
進行の破棄
DeleteProgressByUserId
まったく同じ指定でも発行時にエラー1 件に統合されるネームスペース・ユーザー入れ子越しでは 1 件に統合されなくなり、失敗するようになる。2 件目の破棄はすでに無くなった進行に対して実行され、見つからない (404) として弾かれる

作成と破棄は行が違い、境界が同じです。進行の有無にかかわらず、1 つのトランザクションに入れることはできません。発行時にエラーになります。

進行の作成は、すでに別の進行があると失敗します。この判定はトランザクション開始時点の状態に対して行われるため、enableAtomicCommit が有効な場合は、衝突の問題を別にしても、進行を破棄してから新しいクエストを開始するという流れを 1 つのトランザクションで行うことはできません。トランザクションを分けてください。直列実行モード(enableSequentialExecution または TransactionSettingV2)を有効にしても、この問題は解消しません。消費アクションは入手アクションより先に実行されるため、作成は先に実行された破棄を見られるようになり存在チェックは通りますが、破棄と作成は同じ行への 2 つの書き込みになり、それらをまとめる時点でトランザクションが失敗します (400)。進行を破棄してから新しいクエストを開始するには、引き続きトランザクションを 2 つに分けてください。作成と破棄を 1 つのトランザクションに直接並べることはできないという規則も変わりません。これはトランザクションの発行時、どのアクションも実行される前に働く拒否だからです。

進行は 1 ユーザーにつき 1 つしかないため、同じユーザーへの作成を 1 つのトランザクションに 2 つ並べること自体が矛盾した指示です。どちらを実行すべきか決める根拠がないので、questModelIdforce も設定もまったく同じであっても、発行時にエラーになります。「まとめ買いで 3 個買ってクエストを 3 回開始する」という指示は表現できないと考えてください。破棄どうしも同じで、何件並べても発行時にエラーになります。

クエストの報酬は別に発行されます

クエストの完了や失敗はトランザクションアクションではありません。クエストが終了すると、GS2-Quest がその報酬を自身のトランザクションとして発行します。そのため報酬が属するサービスの制限がそのまま当てはまり、下記も当てはまります。

このトランザクションには進行の破棄が必ず含まれます。そのため enableAtomicCommit が有効な場合、報酬に同じネームスペースの CreateProgressByUserId を置いて次のクエストを自動的に開始することはできません。作成が 1 つだけであっても、破棄との同居になるため発行時にエラーになります。クエストを連鎖させたい場合はトランザクションを分けてください。

入れ子になったトランザクションに注意

内側から作成された進行と、外側から破棄された進行が衝突して、トランザクションが失敗します。作成どうしも同じ進行を二重に書き込むので失敗します。進行の作成は毎回ちがう識別子と乱数の種を書き込むため、指定がまったく同じでも書き込む値は一致せず、値が一致したときの統合には当たりません。破棄どうしだけは同じ進行の削除が冪等なので、入れ子越しでも 1 回にまとまり、失敗しません。

直列実行モードを有効にすると、前半の失敗はなくなります。破棄は消費アクションなので、作成を起動する入手アクションより必ず先に実行され、内側の作成が実行される時点ではすでに破棄が終わっているため、衝突しなくなります。後半は変わりません。2 つの作成のうち後から実行される方は、進行がすでに存在する状態を見るため、自分自身の作成が成立せず、トランザクションは引き続き失敗します。理由が「値が絶対に一致しない」ことから「ふつうの条件不成立」に変わるだけです。

制限を回避したい場合

進行の作成は入手アクション、破棄は消費アクションです。作成どうし・破棄どうしの重複は、検査がトランザクションの発行時、設定が適用されるより前に走るため、acquireActionUseJobQueue を有効にしても enableAtomicCommit を無効にしても解消しません。指定を 1 つに減らすか、トランザクションを分けてください。

作成と破棄の同居は、enableAtomicCommit を無効にすれば解消します。このとき消費アクションが入手アクションより先に実行されるため、破棄してから新しいクエストを開始するという流れも成立するようになります。acquireActionUseJobQueue は入手アクションどうしを分離する設定なので、同居の解消には当てにしないでください。入手アクションが 1 つだけのときはジョブキューに積まれず、破棄と同じトランザクションに残ります。

同時実行とリトライ

同じユーザーの進行を複数のリクエストが同時に作成・破棄した場合、後から確定した側がコンフリクト (409) になります。リトライすると最新の状態で判定し直されるので、進行が期待した状態であれば成功し、すでに別のクエストが始まっていればエラーが返ります。


Consume Action

消費アクション

Gs2Quest:DeleteProgressByUserId

ユーザーIDを指定してクエスト進行状況を削除

指定したユーザーの現在のクエスト進行状況を削除します。進行中のクエストをキャンセルし、新しいクエストを開始できるようにします。

数量指定可能なアクション:いいえ

反転可能なアクション:いいえ

有効化条件必須デフォルト値の制限説明
namespaceNamestring
~ 128文字ネームスペース名
ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。
userIdstring
~ 128文字ユーザーID
#{userId} と設定することでログイン中のユーザーIDに置換されます。
timeOffsetTokenstring~ 1024文字タイムオフセットトークン
{
    "action": "Gs2Quest:DeleteProgressByUserId",
    "request": {
        "namespaceName": "[string]ネームスペース名",
        "userId": "[string]ユーザーID",
        "timeOffsetToken": "[string]タイムオフセットトークン"
    }
}
action: Gs2Quest:DeleteProgressByUserId
request:
  namespaceName: "[string]ネームスペース名"
  userId: "[string]ユーザーID"
  timeOffsetToken: "[string]タイムオフセットトークン"
transaction.service("quest").consume.delete_progress_by_user_id({
    namespaceName="[string]ネームスペース名",
    userId="[string]ユーザーID",
    timeOffsetToken="[string]タイムオフセットトークン",
})

Acquire Action

入手アクション

Gs2Quest:CreateProgressByUserId

ユーザーIDを指定してクエストの進行状況を作成

クエストモデルを参照し、クエストコンテンツの抽選を行ってクエストを開始します。すでに進行中のクエストがある場合、force フラグが設定されていなければエラーを返します。

数量指定可能なアクション:いいえ

反転可能なアクション:はい

有効化条件必須デフォルト値の制限説明
namespaceNamestring
~ 128文字ネームスペース名
ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。
userIdstring
~ 128文字ユーザーID
#{userId} と設定することでログイン中のユーザーIDに置換されます。
questModelIdstring
~ 1024文字開始するクエストモデル GRN
forceboolfalseすでに開始しているクエストがある場合にそれを破棄して開始するか
configList<Config>[]0 ~ 32 itemsトランザクションの変数に適用する設定値
timeOffsetTokenstring~ 1024文字タイムオフセットトークン
{
    "action": "Gs2Quest:CreateProgressByUserId",
    "request": {
        "namespaceName": "[string]ネームスペース名",
        "userId": "[string]ユーザーID",
        "questModelId": "[string]開始するクエストモデルGRN",
        "force": "[bool]すでに開始しているクエストがある場合にそれを破棄して開始するか",
        "config": [
            {
                "key": "[string]名前",
                "value": "[string]値"
            }
        ],
        "timeOffsetToken": "[string]タイムオフセットトークン"
    }
}
action: Gs2Quest:CreateProgressByUserId
request:
  namespaceName: "[string]ネームスペース名"
  userId: "[string]ユーザーID"
  questModelId: "[string]開始するクエストモデルGRN"
  force: "[bool]すでに開始しているクエストがある場合にそれを破棄して開始するか"
  config: 
    - key: "[string]名前"
      value: "[string]値"
  timeOffsetToken: "[string]タイムオフセットトークン"
transaction.service("quest").acquire.create_progress_by_user_id({
    namespaceName="[string]ネームスペース名",
    userId="[string]ユーザーID",
    questModelId="[string]開始するクエストモデルGRN",
    force="[bool]すでに開始しているクエストがある場合にそれを破棄して開始するか",
    config={
        {
            key="[string]名前",
            value="[string]値"
        }
    },
    timeOffsetToken="[string]タイムオフセットトークン",
})