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

# Game Saves の UI コールバック

> コールバックの登録、状態マシンの処理、競合や再試行の応答 API の呼び出しにより、PlayFab Game Saves のカスタム同期 UI を実装します。

Game Saves は、同期操作中のイベントにゲームが対応できるようにする一連の UI コールバックを提供します。XBOX および Windows では、プラットフォームがこれらのイベントに対する組み込みの UI を提供します。その他のプラットフォーム (Steam Deck など) では、これらのコールバックを処理することでゲームが独自の UI を実装する必要があります。

XBOX および Windows でコールバックを設定すると、プラットフォームが提供する UI が実装で上書きされます。これは、ゲームがすべてのプラットフォームで一貫したプレイヤーエクスペリエンスを必要とする場合に便利です。すべての場所で同じコールバックを登録すると、組み込みの UI は表示されません。

## 状態マシンの動作

Game Saves は、UI コールバックを非同期操作のライフサイクルと調整するために内部の状態マシンを使用します。UI コールバックが発生すると、非同期操作は一時停止します。コールバックが解決されるまで `XAsyncBlock` コールバックは発生しません。ゲームが対応する応答 API を呼び出すか、非同期操作がキャンセルされるまで、状態マシンは進行しません。

つまり:

* 各コールバックタイプには対応する応答 API があります。応答 API を呼び出して、システムに次に何をすべきかを伝えます。
* 応答 API は、コールバック関数の内側または外側から呼び出すことができます。
* 応答アクションが `Retry` の場合、操作は再試行され、同じコールバックが再度発生する可能性があります。
* `XAsyncBlock` コールバックは、操作が終端状態 (成功、キャンセル、またはオフラインフォールバック) に達したときにのみ発生します。

たとえば、レート制限のためにアップロードが失敗した場合:

1. エラーとともに `PFGameSaveFilesUiSyncFailedCallback` が発生します。
2. `XAsyncBlock` コールバックはまだ発生しません。状態マシンは応答を待機します。
3. ユーザーが `Retry` を選択して再試行も失敗した場合、同期失敗コールバックが再度発生します。
4. ユーザーが `Cancel` を選択すると、`XAsyncBlock` コールバックが `E_PF_GAMESAVE_USER_CANCELLED` で発生します。
5. 再試行が成功すると、`XAsyncBlock` コールバックが `S_OK` で発生します。

## コールバックがトリガーされるタイミング

UI コールバックは、次の 2 つの非同期操作中にのみ発生します:

| 操作 | トリガーされる可能性のあるコールバック |
| - | - |
| `PFGameSaveFilesAddUserWithUiAsync` | 進行状況、同期失敗、アクティブデバイスの競合、競合、ストレージ不足 |
| `PFGameSaveFilesUploadWithUiAsync` | 進行状況、同期失敗 |

## コールバックの登録

`PFGameSaveFilesAddUserWithUiAsync` または `PFGameSaveFilesUploadWithUiAsync` を呼び出す前に、すべてのコールバックを登録します:

```cpp theme={null}
PFGameSaveUICallbacks callbacks{};
callbacks.progressCallback = MyProgressCallback;
callbacks.progressContext = nullptr;
callbacks.syncFailedCallback = MySyncFailedCallback;
callbacks.syncFailedContext = nullptr;
callbacks.activeDeviceContentionCallback = MyActiveDeviceContentionCallback;
callbacks.activeDeviceContentionContext = nullptr;
callbacks.conflictCallback = MyConflictCallback;
callbacks.conflictContext = nullptr;
callbacks.outOfStorageCallback = MyOutOfStorageCallback;
callbacks.outOfStorageContext = nullptr;

HRESULT hr = PFGameSaveFilesSetUiCallbacks(&callbacks);
```

## コールバックリファレンス

### 進行状況

アップロードまたはダウンロードの進行状況を報告します。コールバック内で `PFGameSaveFilesUiProgressGetProgress` を使用して、現在の `PFGameSaveFilesSyncState`、完了したバイト数、および合計バイト数を取得します。

**コールバック**: `PFGameSaveFilesUiProgressCallback`

**応答 API**: `PFGameSaveFilesSetUiProgressResponse`

| アクション | 効果 |
| - | - |
| `Cancel` | 操作をキャンセルします。`XAsyncBlock` コールバックが `E_PF_GAMESAVE_USER_CANCELLED` で発生します。 |

> [!NOTE]
>
> 進行状況コールバックは、続行するために応答を必要としません。操作は独自に進行し続けます。ユーザーがキャンセルしたい場合にのみ応答 API を呼び出してください。

#### 同期状態

`PFGameSaveFilesSyncState` 列挙型は、操作がどのフェーズにあるかを示します:

| 状態 | 説明 | 保存フォルダーへの書き込みは安全か? |
| - | - | - |
| `NotStarted` | 操作はまだ開始されていません | はい |
| `PreparingForDownload` | クラウドからのダウンロードの準備中 | はい |
| `Downloading` | クラウドからダウンロード中 | いいえ |
| `PreparingForUpload` | ローカルファイルの読み取りと圧縮中 | いいえ |
| `Uploading` | クラウドへのアップロード中 (ローカルファイルはキャプチャ済み) | はい |
| `SyncComplete` | 操作完了 | はい |

### 同期失敗

たとえばネットワークの問題やレート制限などが原因で同期操作が失敗したときに発生します。

**コールバック**: `PFGameSaveFilesUiSyncFailedCallback`

**パラメーター**: `PFGameSaveFilesSyncState` (失敗したフェーズ) と `HRESULT` (エラーコード) を受け取ります。

**応答 API**: `PFGameSaveFilesSetUiSyncFailedResponse`

| アクション | 効果 |
| - | - |
| `Cancel` | 操作をキャンセルします。`XAsyncBlock` コールバックが `E_PF_GAMESAVE_USER_CANCELLED` で発生します。 |
| `Retry` | 失敗した操作を再試行します。再試行が失敗した場合、このコールバックが再度発生します。 |
| `UseOffline` | `PFGameSaveFilesAddUserWithUiAsync` の間のみ有効です。`XAsyncBlock` コールバックは `S_OK` で発生しますが、システムはオフラインモードに入ります。この状態を検出するには `PFGameSaveFilesIsConnectedToCloud()` を使用します。 |

オフラインモード動作の詳細については、[Game Saves オフラインモード](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/offline.md) を参照してください。

### アクティブデバイスの競合

別のデバイスがこのユーザーの既にアクティブデバイスになっている場合、`PFGameSaveFilesAddUserWithUiAsync` 中に発生します。コールバックは、ローカルとリモート両方の保存データの `PFGameSaveDescriptor` 構造体を受け取ります。これには、ユーザーが決定するのに役立つように表示できるデバイス名、タイムスタンプ、保存サイズが含まれます。

**コールバック**: `PFGameSaveFilesUiActiveDeviceContentionCallback`

**応答 API**: `PFGameSaveFilesSetUiActiveDeviceContentionResponse`

| アクション | 効果 |
| - | - |
| `Cancel` | 操作をキャンセルします。`XAsyncBlock` コールバックが `E_PF_GAMESAVE_USER_CANCELLED` で発生します。 |
| `Retry` | 再試行します。他のデバイスがすぐに解放されるとユーザーが期待している場合に便利です。他のデバイスがまだアクティブな場合、このコールバックが再度発生します。 |
| `SyncLastSavedData` | ローカルデバイスをアクティブにして同期します。リモートデバイスはもうアップロードできなくなり、アクティブデバイス変更通知を受け取ります。 |

アクティブデバイスの動作の詳細については、[Game Saves のアクティブデバイスの変更](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/activedevicechanges.md) を参照してください。

### 競合

ローカル セーブ データとクラウド セーブ データが分岐したときに、`PFGameSaveFilesAddUserWithUiAsync` 中に発生します。コールバックは、ローカルとリモート両方の保存データの `PFGameSaveDescriptor` 構造体を受け取ります。

**コールバック**: `PFGameSaveFilesUiConflictCallback`

**応答 API**: `PFGameSaveFilesSetUiConflictResponse`

| アクション | 効果 |
| - | - |
| `Cancel` | 操作をキャンセルします。`XAsyncBlock` コールバックが `E_PF_GAMESAVE_USER_CANCELLED` で発生します。 |
| `TakeLocal` | ローカル保存データを保持し、クラウドにアップロードします。 |
| `TakeRemote` | ローカルの変更を破棄し、クラウド セーブ データをダウンロードします。 |

> [!NOTE]
>
> 競合の解決は、個々のファイルやフォルダーではなく、保存全体に適用されます。競合がアトミック単位レベルでどのように検出され、グローバルに解決されるかの詳細については、[Game Saves の競合](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/conflicts.md) を参照してください。

### ストレージ不足

ローカルデバイスにクラウドから保存データをダウンロードするための十分なディスク容量がない場合、`PFGameSaveFilesAddUserWithUiAsync` 中に発生します。コールバックは、必要な容量を示す `requiredBytes` を受け取ります。

**コールバック**: `PFGameSaveFilesUiOutOfStorageCallback`

**応答 API**: `PFGameSaveFilesSetUiOutOfStorageResponse`

| アクション | 効果 |
| - | - |
| `Cancel` | 操作をキャンセルします。`XAsyncBlock` コールバックが `E_PF_GAMESAVE_USER_CANCELLED` で発生します。 |
| `Retry` | ユーザーがローカルストレージ容量を解放した後に再試行します。まだ十分な容量がない場合、このコールバックが再度発生します。 |

## インプロセスとアウトオブプロセス (システム UI)

Game Saves が実行される場所はプラットフォームによって異なり、これは UI イベントの処理方法に影響します:

* **アウトオブプロセス (XBOX および Windows)**: XBOX および Windows では、Game Saves は常にアウトオブプロセスで実行されます。プラットフォーム (Connected Storage / GRTS) が同期を実行し、同期失敗、競合、アクティブデバイスの競合、ストレージ不足に対する組み込みのシステム UI ダイアログを提供します。プレイヤーはこれらのダイアログを直接操作し、プラットフォームはその結果を、ゲームがインプロセスで受け取るのと同じ非同期完了コードに変換します。組み込みの UI を置き換えるためにコールバックを登録することもできます。
* **インプロセス (Steam Deck などのその他のプラットフォーム)**: プラットフォーム サービスが利用できない場合、ゲームが Game Saves 同期エンジンをホストし、上記のコールバックを通じて UI イベントを処理します。ゲームは応答 API を使用して状態マシンを完全に制御します。

### キャンセルは常に起こり得る結果です

パスに関係なく、**すべての** UI フローは、同期が成功しないままユーザーが中止して終了する可能性があるため、ゲームは常に終端のキャンセルを処理する必要があります:

| ユーザーの意図 | 非同期完了 | `IsConnectedToCloud()` | 保存フォルダー |
| - | - | - | - |
| オンラインで続行 (成功) | `S_OK` | `true` | はい |
| オフラインでプレイ | `S_OK` | `false` | はい |
| キャンセル / 終了 (プレイヤー) | `E_PF_GAMESAVE_USER_CANCELLED` (`0x800704C7`) | 該当なし | **いいえ** |
| キャンセル (ゲームが `XAsyncCancel` を呼び出した) | `E_ABORT` (`0x80004004`) | 該当なし | **いいえ** |

プレイヤーがプレイを続行できる唯一の「中止」アクションは、同期失敗のプロンプトでの **Use Offline** (成功の結果) です。その他のキャンセルはすべて終端であり、保存フォルダーは残りません。*プレイを許可しない* として扱ってください。[Game Saves オフラインモード](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/offline.md#when-game-saves-enters-offline-mode-or-returns-an-error) を参照してください。

組み込みの (アウトオブプロセスの) システム UI では、これらの選択肢はプラットフォームの文言で表示されます。たとえば、同期失敗ダイアログには **"Try again"** / **"Play offline"** が表示され、競合ダイアログには **"Stop sync & continue"** / **"Quit game"** が表示されます。"Play offline" はオフラインモード (`S_OK`、未接続) に対応し、"Quit game" は `E_PF_GAMESAVE_USER_CANCELLED` に対応します。

**競合**のプロンプトをキャンセルすると、どちらのパスでも終端となり、`E_PF_GAMESAVE_USER_CANCELLED` で完了します。同期失敗のプロンプトに連鎖することはありません。

カスタムのクロスプラットフォーム UI (たとえば、Steam Deck の実装に合わせたもの) の場合は、すべての場所でコールバックを登録し、対象のハードウェアでアウトオブプロセスのフローを検証してください。パスに関係なく、完了結果に基づいてプレイを制御してください: `S_OK` + 接続済み → オンラインでプレイ、`S_OK` + 未接続 → オフラインでプレイ (プレイヤーに警告します)、フォルダーなしのキャンセル/失敗 → プレイをブロック。

## プラットフォーム要件

| プラットフォーム | UI コールバック |
| - | - |
| **XBOX および Windows** | オプション。組み込み UI を上書きするようにコールバックを設定します。 |
| **その他のプラットフォーム** (Steam Deck など) | **必須**。組み込みの UI は利用できません。ゲームはすべてのコールバックを処理する必要があります。 |

Steam Deck 実装の詳細については、[Steam Deck 実装ガイド](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/steam-deck-implementation.md) を参照してください。

## 関連コンテンツ

* [Game Saves クイックスタート](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/quickstart.md)
* [Game Saves オフラインモード](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/offline.md)
* [Game Saves の競合](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/conflicts.md)
* [Game Saves のアクティブデバイスの変更](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/activedevicechanges.md)


## Related topics

- [Game Saves クイックスタート](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/quickstart.md)
- [Game Saves のエラー処理に関する FAQ](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/error-handling-faq.md)
- [PlayFab Game Saves の Steam Deck 実装ガイド](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/steam-deck-implementation.md)
- [Game Saves ロールバック](https://docs.xbox.com/ja-jp/services/playfab/player-progression/game-saves/rollback.md)
- [GameInput コールバック](https://docs.xbox.com/ja-jp/build/core-features/common/input/advanced/input-callbacks.md)
