> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-SkillTree トランザクションアクション

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




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

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

GS2-SkillTree のトランザクションアクションは、ネームスペース・ユーザー・プロパティの組で決まる 1 つのステータスを対象にします。ステータスはスキルツリーのどのノードを解放済みかを記録するもので、更新のたびに全体が書き換わります。

操作 | 同じ行を重ねたとき | 入れ子越し | 別の対象になる境界 | 直列実行モード有効時
--- | --- | --- | --- | ---
ノードの解放<br>`MarkReleaseByUserId` | 統合され、ノード名が重複を除いて結合される | 失敗する | ネームスペース・ユーザー・プロパティ | 成立する。内側から届く解放は、外側がすでに書き込んだ状態の上に適用される
ノードの抑制<br>`MarkRestrainByUserId` | 統合され、ノード名が重複を除いて結合される | 失敗する | ネームスペース・ユーザー・プロパティ | 成立する。内側から届く抑制は、外側がすでに書き込んだ状態の上に適用される

ノードの解放には、そのノードが必要とするものの支払いが伴い、抑制するとそれが返却されます。これらの支払いと返却は自身のトランザクションとして発行されるため、それらが属するサービスの制限を受けます。

返却は支払いから導出されます。解放の消費アクション 1 つ 1 つが、それを取り消す入手アクションに変換されます。リセットは解放済みのノードをまとめて処理するため、別々のノードから導出された返却が 1 つのトランザクションに集まります。たとえば 2 つのノードが同じミッショングループの別のタスクに対する `Gs2Mission:ReceiveByUserId` を必要としている場合、その返却どうしが衝突して抑制やリセットが失敗します。**直列実行モードを有効にしても、この特定のケースは解消されません。衝突の原因は、導出された 2 つの取り消しアクションが同じミッショングループの違うタスクを指定していることにあり、これは実行が始まる前、トランザクションの発行時点で GS2-Mission 側が拒否するためです。何を組み合わせられるかは GS2-Mission のページを確認してください。**

前提関係のないノードを複数解放するだけなら、1 つのアクションの `nodeModelNames` に並べて指定しても、アクションを複数指定して統合させても、どちらでもかまいません。

前提関係があるノードを一緒に解放したい場合は、1 つのアクションにまとめる必要があります。ノードの解放は、その前提となるノードがすでに解放されていることを確認しますが、この判定はトランザクション開始時点のステータスに対して行われるためです。アクションを 2 つに分けると、前提ノードを解放したことがもう一方からは見えません。

解放と抑制は行が違い、境界が同じです。1 つのトランザクションで行うことはできません。トランザクションを分けてください。**直列実行モード（`enableSequentialExecution` または `TransactionSettingV2`）を有効にすると、この制約はなくなります。アクションが 1 件ずつ実行され、後から実行される方は先に実行されたアクションが書き込んだステータスを読んだうえで実行されるため、同じステータスに対する解放と抑制を 1 つのトランザクションに入れられるようになります。**

プロパティが違えば別のステータスなので、複数のプロパティのノードを 1 つのトランザクションで解放するのは問題ありません。

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

並べて書けば 1 件に統合されたはずの同じステータスへの解放どうしでも、片方が内側から届く場合はトランザクションが失敗します。直列実行モードを有効にすると、この失敗は起こらなくなります。内側のトランザクションは外側と同じ直列実行の区間で実行されるようになるため、内側の解放は外側がすでに書き込んだ状態の上に適用されます。

### 制限を回避したい場合

ノードの解放は入手アクション、抑制は消費アクションです。解放どうしの衝突は `acquireActionUseJobQueue` を有効にすれば解消できますが、解放と抑制の同居は `enableAtomicCommit` を無効にしないと解消しません。前提関係のあるノードを一緒に解放したい場合は、どちらの設定を使う場合でも 1 つのアクションにまとめるのが確実です。

### 同時実行とリトライ

同じステータスを複数のリクエストが同時に更新した場合、後から確定した側がコンフリクト (409) になります。リクエストの内容に問題があるわけではないので、リトライすれば成功します。リトライ時には最新のステータスに対して前提条件が確認し直されます。

異なるプロパティのノードを同時に解放する場合はコンフリクトしません。

---



## Consume Action

消費アクション

### Gs2SkillTree:MarkRestrainByUserId

ユーザーIDを指定してノードの解放状態を未解放化<br>

指定されたノードが拘束可能であることを検証し（依存するノードが解放状態でないことが必要です）、未解放としてマークします。

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

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

|  | 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128文字 | ネームスペース名<br>ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 |
| userId | string |  | ✓|  |  ~ 128文字 | ユーザーID<br>`#{userId}` と設定することでログイン中のユーザーIDに置換されます。 |
| propertyId | string |  | ✓|  |  ~ 1024文字 | プロパティID<br>ユーザーごとに複数の独立したスキルツリーインスタンスを持つための識別子。<br>同一ネームスペース内で、プレイヤーが異なるキャラクターやコンテキストに対して別々のスキルツリーを持つシナリオを実現します。<br>最大1024文字。 |
| nodeModelNames | List&lt;string&gt; |  | ✓|  | 1 ~ 1000 items | ノードモデル名のリスト |
| timeOffsetToken | string |  | |  |  ~ 1024文字 | タイムオフセットトークン |




**JSON**
```json
{
    "action": "Gs2SkillTree:MarkRestrainByUserId",
    "request": {
        "namespaceName": "[string]ネームスペース名",
        "userId": "[string]ユーザーID",
        "propertyId": "[string]プロパティID",
        "nodeModelNames": [
            "[string]ノードモデル名"
        ],
        "timeOffsetToken": "[string]タイムオフセットトークン"
    }
}
```

**YAML**
```yaml

action: Gs2SkillTree:MarkRestrainByUserId
request:
  namespaceName: "[string]ネームスペース名"
  userId: "[string]ユーザーID"
  propertyId: "[string]プロパティID"
  nodeModelNames: 
    - "[string]ノードモデル名"
  timeOffsetToken: "[string]タイムオフセットトークン"
```

**GS2-Script**
```lua

transaction.service("skillTree").consume.mark_restrain_by_user_id({
    namespaceName="[string]ネームスペース名",
    userId="[string]ユーザーID",
    propertyId="[string]プロパティID",
    nodeModelNames={
        "[string]ノードモデル名"
    },
    timeOffsetToken="[string]タイムオフセットトークン",
})
```


---

## Acquire Action

入手アクション

### Gs2SkillTree:MarkReleaseByUserId

ユーザーIDを指定して解放済みノードを記録<br>

指定されたノードが解放可能であることを検証し（前提ノードが既に解放済みである必要があります）、解放済みとしてマークします。

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

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

|  | 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128文字 | ネームスペース名<br>ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 |
| userId | string |  | ✓|  |  ~ 128文字 | ユーザーID<br>`#{userId}` と設定することでログイン中のユーザーIDに置換されます。 |
| propertyId | string |  | ✓|  |  ~ 1024文字 | プロパティID<br>ユーザーごとに複数の独立したスキルツリーインスタンスを持つための識別子。<br>同一ネームスペース内で、プレイヤーが異なるキャラクターやコンテキストに対して別々のスキルツリーを持つシナリオを実現します。<br>最大1024文字。 |
| nodeModelNames | List&lt;string&gt; |  | ✓|  | 1 ~ 1000 items | ノードモデル名のリスト |
| timeOffsetToken | string |  | |  |  ~ 1024文字 | タイムオフセットトークン |




**JSON**
```json
{
    "action": "Gs2SkillTree:MarkReleaseByUserId",
    "request": {
        "namespaceName": "[string]ネームスペース名",
        "userId": "[string]ユーザーID",
        "propertyId": "[string]プロパティID",
        "nodeModelNames": [
            "[string]ノードモデル名"
        ],
        "timeOffsetToken": "[string]タイムオフセットトークン"
    }
}
```

**YAML**
```yaml

action: Gs2SkillTree:MarkReleaseByUserId
request:
  namespaceName: "[string]ネームスペース名"
  userId: "[string]ユーザーID"
  propertyId: "[string]プロパティID"
  nodeModelNames: 
    - "[string]ノードモデル名"
  timeOffsetToken: "[string]タイムオフセットトークン"
```

**GS2-Script**
```lua

transaction.service("skillTree").acquire.mark_release_by_user_id({
    namespaceName="[string]ネームスペース名",
    userId="[string]ユーザーID",
    propertyId="[string]プロパティID",
    nodeModelNames={
        "[string]ノードモデル名"
    },
    timeOffsetToken="[string]タイムオフセットトークン",
})
```


---



