> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Game Saves の競合

> ローカルとクラウドのセーブ データが分岐したときに PlayFab Game Saves の競合を解決し、同期戦略を使用してデバイス間のプレイヤーの進行状況を保護します。

# Game Save の競合とアトミック ユニット

## 概要

セーブの競合は、複数のデバイスで同じゲーム データを変更した場合に発生します。システムはどちらのバージョンを保持するかを決定する必要があります。セーブ データの構造を設計するには、システムが競合をどのように検出および解決するかを理解する必要があります。

## 競合が発生するタイミング

競合は、同期操作 (`PFGameSaveFilesAddUserWithUiAsync`) の実行時に、以下のすべての条件が true の場合にのみ発生します。

1. **ローカルの変更が存在する**: 最後の同期以降にローカルでファイルを変更した。
2. **クラウドの変更が存在する**: 最後の同期以降に別のデバイスが新しいデータをアップロードした。
3. **同じアトミック ユニット**: 両方の変更が同じルート レベルのフォルダーにある。

### 競合検出マトリックス

| ローカル デバイスの変更 | 同じアトミック ユニットにクラウドの変更あり | 結果        |
| ------------ | ---------------------- | --------- |
| ファイルの変更      | Yes                    | **競合**    |
| ファイルの削除      | Yes                    | **競合**    |
| 変更なし         | Yes                    | ダウンロードが続行 |
| ファイルの変更      | No                     | アップロードが続行 |
| ファイルの削除      | No                     | 削除が続行     |

## アトミック ユニットとは?

一部のファイル同期システムでは、ファイルごとに競合を扱います。同じファイルをアップロードとダウンロードする必要がある場合は競合とみなされます。Game Saves では、各ルート レベルのサブフォルダーが *アトミック ユニット* として扱われます。

<Note>
  **各ルート レベルのサブフォルダーは 1 つのアトミック ユニットです。**
</Note>

ルート レベルのサブフォルダー内の任意のファイルまたはサブフォルダーをダウンロードする必要があり、同じルート サブフォルダー内の任意のファイルまたはフォルダーをアップロードする必要がある場合、アトミック ユニット全体が競合状態になります。

このアプローチにより、以下が可能になります。

* **データ整合性の維持**: 相互依存するファイルは一貫して維持されます
* **分離の提供**: 別々のフォルダー内の独立したデータは競合なく同期されます
* **競合の最小化**: 異なるデバイス上の異なるアトミック ユニットへの変更は自動的にマージされます

### セーブ構造の例

```
SaveRoot/
├── save.dat              ← Atomic unit: root
├── player.dat            ← Atomic unit: root
├── Save1/
│   ├── stats.json        ← Atomic unit: Save1
│   └── inventory.json    ← Atomic unit: Save1
├── Save2/
│   ├── stats.json        ← Atomic unit: Save2
│   └── inventory.json    ← Atomic unit: Save2
└── WorldState/
    ├── map.dat           ← Atomic unit: WorldState
    └── npcs/
        └── positions.dat ← Atomic unit: WorldState
```

### 競合シナリオ

| デバイス A の変更           | デバイス B の変更                      | 競合?     | 理由                       |
| -------------------- | ------------------------------- | ------- | ------------------------ |
| `Save1/stats.json`   | `Save1/inventory.json`          | **YES** | 同じアトミック ユニット: Save1      |
| `Save1/stats.json`   | `Save2/stats.json`              | No      | 異なるユニット: Save1 と Save2   |
| `WorldState/map.dat` | `WorldState/npcs/positions.dat` | **YES** | 同じアトミック ユニット: WorldState |
| `save.dat`           | `Save1/stats.json`              | No      | 異なるユニット: root と Save1    |
| `player.dat`         | `save.dat`                      | **YES** | 同じアトミック ユニット: root       |

## ルート レベルのファイル: 特殊なケース

**セーブ ルートにあるすべてのファイルは 1 つのアトミック ユニットを共有します。**

システムは、セーブ ルート (任意のサブフォルダーではなく) に直接配置したすべてのファイルを 1 つのアトミック ユニットとしてまとめてグループ化します。ローカルで 1 つのルート レベル ファイルを変更し、別のデバイスが別のルート レベル ファイルを変更した場合、この違いによって競合が発生します。

| デバイス A (ローカル)       | デバイス B (クラウド)          | 同じアトミック ユニット? | 結果        |
| ------------------- | ---------------------- | ------------- | --------- |
| `rootfile1.txt` を変更 | `rootfile2.txt` を変更    | ✅ YES         | **競合**    |
| `rootfile1.txt` を変更 | `save1/config.ini` を変更 | ❌ NO          | 競合なし、両方同期 |
| `save.dat` を削除      | `progress.dat` を変更     | ✅ YES         | **競合**    |

## ユーザーの選択オプション

競合が発生すると、プレイヤーは以下から選択します。

* **ローカル データを使用 (Keep Local)**: デバイスの現在のセーブ データを保持します。
* **クラウド データを使用 (Keep Cloud)**: クラウドのセーブ データをダウンロードして使用します。

### 重要: 解決はオール オア ナッシング

<Note>
  ⚠️ **重要**: アトミック ユニットは競合が *いつ* 検出されるかを決定しますが、ユーザーの競合解決の選択はアトミック ユニット単位ではなく **セーブ全体** に適用されます。
</Note>

### 例: 混在する競合シナリオ

```
SaveRoot/
├── SlotA/ ← Local: modified, Cloud: modified  → CONFLICT
├── SlotB/ ← Local: modified, Cloud: unchanged → Local-only change
├── SlotC/ ← Local: unchanged, Cloud: modified → Cloud-only change
└── SlotD/ ← Local: modified, Cloud: unchanged → Local-only change
```

**ユーザーには (SlotA が原因で) 競合プロンプトが表示されます。**

| ユーザーの選択        | SlotA         | SlotB                 | SlotC               | SlotD                 |
| -------------- | ------------- | --------------------- | ------------------- | --------------------- |
| **Keep Local** | ✅ ローカル保持      | ✅ ローカルがアップロード         | ❌ クラウドの変更が **失われる** | ✅ ローカルがアップロード         |
| **Keep Cloud** | ✅ クラウドがダウンロード | ❌ ローカルの変更が **上書きされる** | ✅ クラウドがダウンロード       | ❌ ローカルの変更が **上書きされる** |

### これが重要な理由

1. **Keep Local はクラウドのみの変更を失う**: SlotA の競合により「Keep Local」を選択すると、別のデバイスが行った SlotC へのクラウド更新を受け取れません。

2. **Keep Cloud はローカルのみの変更を失う**: 「Keep Cloud」を選択すると、クラウドの状態が SlotB と SlotD へのローカル変更を上書きします。

3. **ロールバックが利用可能**: いずれの選択も、将来のロールバック機能のために破棄されたブランチを保持します。

### 設計のまとめ

| 側面        | 粒度                         |
| --------- | -------------------------- |
| 競合 **検出** | アトミック ユニットごと (ルート サブフォルダー) |
| 競合 **解決** | セーブ全体 (オール オア ナッシング)       |

このオール オア ナッシングのアプローチはプレイヤー エクスペリエンスをシンプルにします。アトミック ユニット単位の解決は技術的にはデータの一貫性を維持しますが (アトミック ユニットが一貫性の境界を定義するため)、ユーザー エクスペリエンスの課題を生み出します。

* プレイヤーはアトミック ユニットとフォルダー境界の概念を理解する必要があります。
* 混在した結果 (一部のフォルダーはローカルから、他はクラウドから) はプレイヤーを最終状態について困惑させる可能性があります。
* 競合するアトミック ユニットごとに個別にプロンプトを表示すると、競合解決が圧倒的になります。

**Keep Local** と **Keep Cloud** の単一の選択肢を提示することで、プレイヤーは基盤となるセーブ構造を理解する必要なしに、1 つの明確な決定を下します。

## 競合が発生しない場合

### 両側での削除

両方のデバイスが同じファイル (または同じアトミック ユニット内のファイル) を削除する場合、**競合は発生しません**。システムは両方のデバイスがそのファイルを削除すべきだと合意していると認識します。

### 異なるアトミック ユニット内の変更

デバイス A が `SlotA/` 内のファイルを変更し、デバイス B が `SlotB/` 内のファイルを変更する場合、競合は発生しません。同期は両方の変更を自動的にマージします。

## ベスト プラクティス

### 1. フォルダー構造を慎重に設計する

独立したセーブ ユニットにはサブフォルダーを使用します。

```
SaveRoot/
├── Slot1/           ← Each slot is independent
│   └── save.dat
├── Slot2/
│   └── save.dat
```

各スロットがルート レベルのサブフォルダーになるスロット ベースのゲーム セーブ システムは、アトミック ユニットを効果的に使用する方法のシンプルな例です。

その他の例:

* **共有参照データ**: アンロック済みコンテンツや実績など、任意のセーブ スロットが使用する可能性のあるデータを独自のサブフォルダーに格納すると、個々のセーブ スロットとは独立して同期されます。
* **大きなアセット コレクション**: ゲームがダウンロード済みコンテンツ パックやユーザー作成のレベルなど、独立して更新される大きなファイル セットを保存する場合は、それらを複数のルート サブフォルダーに分割することを検討してください。あるコレクションの更新が別のコレクションの更新と競合しないようにします。

### 2. 頻繁に変更されるデータにはルート レベル ファイルを避ける

すべてのルート レベル ファイルは 1 つのアトミック ユニットを共有します。独立して同期したい場合は、頻繁に変更されるファイルをルートに配置することを避けます。

**避けるべき例:**

```
SaveRoot/
├── autosave.dat     ← All root files = 1 atomic unit
└── save1.dat
└── save2.dat
```

**推奨例:**

```
SaveRoot/
├── AutoSave/
│   └── autosave.dat     ← Independent unit
└── Save1/
    └── gamesave.dat ← Independent unit
```

### 3. 関連するデータをまとめてグループ化する

一貫して維持する必要があるファイルには同じフォルダーを使用します。

```
SaveRoot/
├── Save1/
│   ├── stats.json       ← These files are interdependent
│   ├── inventory.json   ← and should conflict together
│   └── quests.json
```

### 4. 競合を最小化する

競合の可能性を減らすために、頻繁にアップロードしてください。同期の頻度が高いほど、2 台のデバイスに分岐した変更が生じる可能性が低くなります。

### 5. 競合率を監視する

[PlayStream イベント](/services/playfab/player-progression/game-saves/playstream-events) を使用して、競合が発生する頻度とプレイヤーが解決する方法を追跡します。競合率の上昇や「keep local」と「keep remote」の間の強い偏りは、UX やセーブ頻度の問題を示唆する可能性があり、調査する価値があります。


## Related topics

- [Game Saves システム ダイアログ](/ja-jp/build/core-features/common/game-save/game-saves-dialogues.md)
- [Game Saves の UI コールバック](/ja-jp/services/playfab/player-progression/game-saves/ui-callbacks.md)
- [Game Saves クイックスタート](/ja-jp/services/playfab/player-progression/game-saves/quickstart.md)
- [PlayFab Game Saves のアカウント リンク戦略](/ja-jp/services/playfab/player-progression/game-saves/linking.md)
- [Game Saves ロールバック](/ja-jp/services/playfab/player-progression/game-saves/rollback.md)
