状態マシンの動作
Game Saves は、UI コールバックを非同期操作のライフサイクルと調整するために内部の状態マシンを使用します。UI コールバックが発生すると、非同期操作は一時停止します。コールバックが解決されるまでXAsyncBlock コールバックは発生しません。ゲームが対応する応答 API を呼び出すか、非同期操作がキャンセルされるまで、状態マシンは進行しません。
つまり:
- 各コールバックタイプには対応する応答 API があります。応答 API を呼び出して、システムに次に何をすべきかを伝えます。
- 応答 API は、コールバック関数の内側または外側から呼び出すことができます。
- 応答アクションが
Retryの場合、操作は再試行され、同じコールバックが再度発生する可能性があります。 XAsyncBlockコールバックは、操作が終端状態 (成功、キャンセル、またはオフラインフォールバック) に達したときにのみ発生します。
- エラーとともに
PFGameSaveFilesUiSyncFailedCallbackが発生します。 XAsyncBlockコールバックはまだ発生しません。状態マシンは応答を待機します。- ユーザーが
Retryを選択して再試行も失敗した場合、同期失敗コールバックが再度発生します。 - ユーザーが
Cancelを選択すると、XAsyncBlockコールバックがE_PF_GAMESAVE_USER_CANCELLEDで発生します。 - 再試行が成功すると、
XAsyncBlockコールバックがS_OKで発生します。
コールバックがトリガーされるタイミング
UI コールバックは、次の 2 つの非同期操作中にのみ発生します:コールバックの登録
PFGameSaveFilesAddUserWithUiAsync または PFGameSaveFilesUploadWithUiAsync を呼び出す前に、すべてのコールバックを登録します:
コールバックリファレンス
進行状況
アップロードまたはダウンロードの進行状況を報告します。コールバック内でPFGameSaveFilesUiProgressGetProgress を使用して、現在の PFGameSaveFilesSyncState、完了したバイト数、および合計バイト数を取得します。
コールバック: PFGameSaveFilesUiProgressCallback
応答 API: PFGameSaveFilesSetUiProgressResponse
進行状況コールバックは、続行するために応答を必要としません。操作は独自に進行し続けます。ユーザーがキャンセルしたい場合にのみ応答 API を呼び出してください。
同期状態
PFGameSaveFilesSyncState 列挙型は、操作がどのフェーズにあるかを示します:
同期失敗
たとえばネットワークの問題やレート制限などが原因で同期操作が失敗したときに発生します。 コールバック:PFGameSaveFilesUiSyncFailedCallback
パラメーター: PFGameSaveFilesSyncState (失敗したフェーズ) と HRESULT (エラーコード) を受け取ります。
応答 API: PFGameSaveFilesSetUiSyncFailedResponse
オフラインモード動作の詳細については、Game Saves オフラインモード を参照してください。
アクティブデバイスの競合
別のデバイスがこのユーザーの既にアクティブデバイスになっている場合、PFGameSaveFilesAddUserWithUiAsync 中に発生します。コールバックは、ローカルとリモート両方の保存データの PFGameSaveDescriptor 構造体を受け取ります。これには、ユーザーが決定するのに役立つように表示できるデバイス名、タイムスタンプ、保存サイズが含まれます。
コールバック: PFGameSaveFilesUiActiveDeviceContentionCallback
応答 API: PFGameSaveFilesSetUiActiveDeviceContentionResponse
アクティブデバイスの動作の詳細については、Game Saves のアクティブデバイスの変更 を参照してください。
競合
ローカル セーブ データとクラウド セーブ データが分岐したときに、PFGameSaveFilesAddUserWithUiAsync 中に発生します。コールバックは、ローカルとリモート両方の保存データの PFGameSaveDescriptor 構造体を受け取ります。
コールバック: PFGameSaveFilesUiConflictCallback
応答 API: PFGameSaveFilesSetUiConflictResponse
競合の解決は、個々のファイルやフォルダーではなく、保存全体に適用されます。競合がアトミック単位レベルでどのように検出され、グローバルに解決されるかの詳細については、Game Saves の競合 を参照してください。
ストレージ不足
ローカルデバイスにクラウドから保存データをダウンロードするための十分なディスク容量がない場合、PFGameSaveFilesAddUserWithUiAsync 中に発生します。コールバックは、必要な容量を示す requiredBytes を受け取ります。
コールバック: PFGameSaveFilesUiOutOfStorageCallback
応答 API: PFGameSaveFilesSetUiOutOfStorageResponse
インプロセスとアウトオブプロセス (システム UI)
Game Saves が実行される場所はプラットフォームによって異なり、これは UI イベントの処理方法に影響します:- アウトオブプロセス (XBOX および Windows): XBOX および Windows では、Game Saves は常にアウトオブプロセスで実行されます。プラットフォーム (Connected Storage / GRTS) が同期を実行し、同期失敗、競合、アクティブデバイスの競合、ストレージ不足に対する組み込みのシステム UI ダイアログを提供します。プレイヤーはこれらのダイアログを直接操作し、プラットフォームはその結果を、ゲームがインプロセスで受け取るのと同じ非同期完了コードに変換します。組み込みの UI を置き換えるためにコールバックを登録することもできます。
- インプロセス (Steam Deck などのその他のプラットフォーム): プラットフォーム サービスが利用できない場合、ゲームが Game Saves 同期エンジンをホストし、上記のコールバックを通じて UI イベントを処理します。ゲームは応答 API を使用して状態マシンを完全に制御します。
キャンセルは常に起こり得る結果です
パスに関係なく、すべての UI フローは、同期が成功しないままユーザーが中止して終了する可能性があるため、ゲームは常に終端のキャンセルを処理する必要があります:
プレイヤーがプレイを続行できる唯一の「中止」アクションは、同期失敗のプロンプトでの Use Offline (成功の結果) です。その他のキャンセルはすべて終端であり、保存フォルダーは残りません。プレイを許可しない として扱ってください。Game Saves オフラインモード を参照してください。
組み込みの (アウトオブプロセスの) システム 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 + 未接続 → オフラインでプレイ (プレイヤーに警告します)、フォルダーなしのキャンセル/失敗 → プレイをブロック。
プラットフォーム要件
Steam Deck 実装の詳細については、Steam Deck 実装ガイド を参照してください。
