次の方法で共有


XGameSave の技術概要

XGameSave では、プレーヤーがゲーム データをクラウドに保存できます。 このトピックでは、保存したデータがコンソールとクラウド ストレージの間で更新または同期されたときに、背後でどのように処理されているかについて説明します。

XGameSaveSubmitUpdateAsync の動作

XGameSave を使用してデータを保存し、後にそれをクラウドに格納するには、XGameSaveSubmitUpdateAsync メソッドを呼び出してデータを追加、削除、更新する必要があります。 XGameSaveSubmitUpdateAsync が呼び出されると、XGameSaveSubmitBlobWrite and XGameSaveSubmitBlobDelete によってその呼び出しで指定されたバッファーが、アプリケーション パーティションからシステム パーティションの専用のメモリ領域にすぐにコピーされます。 メモリがシステム パーティションに正常にコピーされると、XGameSaveSubmitUpdateAsync の結果が返され、データ用にローカルで割り当てたメモリを解放しても安全であることがアプリケーションに伝えられます。

その後、システムが BLOB を本体のハード ドライブに保存し、そのコンテナーでの操作全体をコミットする最終的なコンテナーの更新を実行して操作を完了します。

XGameSaveSubmitUpdateAsync データを受け取るための共有パーティション内のメモリには 16 MB の上限が設けられています。 専用の 16 MB バッファー内に空きメモリが十分にないために、システムが XGameSaveSubmitUpdateAsync の呼び出しを即座に処理できない場合、呼び出しは処理のキューに入れられます。 システムは継続的に、16 MB のバッファーからハード ドライブにデータを転送します。 16 MB のバッファー内の領域が使用できるようになると、キュー内の更新が要求された順序で処理されます。

更新されたデータがコンソールのハード ドライブに保存されると、XGameSave システムが取得してクラウド上にアップロードできるようになります。 このアップロードを完了するために、システムは現在のコンテナーの状態のスナップショットを撮り、その変更をクラウドにアップロードします。 このアップロードは、ゲームプレイ中に定期的な間隔で発生するか、ゲームが一時停止または終了したときに発生します。

クラウドへのアップロードのプロセスは、変更されたデータをコンソールのハード ドライブにコピーする操作に似ています。 各 BLOB がサービスにアップロードされます。 個々の BLOB がサービスにアップロードされ、更新操作は、他のすべてのアップロードされた BLOB を参照するコンテナー ファイルの最終的な更新によってコミットされます。 クラウドへのアップロードでは、このように 1 回の最終的な更新に統合することで、XGameSaveSubmitUpdateAsync 呼び出しで参照されているすべてのデータが完全にコミットされるか、コンテナーが変更されないままになるかのどちらかになります。 このようにすることで、アップロード処理中にシステムがオフラインになったり、電源が切断されたりした場合でも、ユーザーは別の Xbox One コンソールにアクセスして、クラウドからデータをダウンロードし、すべてのコンテナーが一貫した状態でプレイを続行できます。

Important

コンテナーをまたいだデータの依存性は安全ではありません。 個々の XGameSaveSubmitUpdateAsync 呼び出しの結果は、完全に適用されるか、まったく適用されないかのどちらかになることが保証されます。

XGameSaveSubmitUpdateAsync 呼び出しでは、コンテナーの有効な状態を保つために、その後の XGameSaveSubmitUpdateAsync 呼び出しが正常に完了すると見なしてはいけません。 つまりアプリケーションは、必要なすべてのデータをコンテナーに保存するのに、複数の XGameSaveSubmitUpdateAsync 呼び出しに依存することはできません。 各 XGameSaveSubmitUpdateAsync 呼び出しは、指定したコンテナーの内容をアプリが後で読み取れるように有効な状態にしておく必要があります。

この問題を説明するために、Bob というキャラクターが所有する黄金 (Gold) と食料 (Food) の量をコンテナーで追跡するシナリオについて考えてみましょう。 ゲームでは、Food と Gold という 2 つの BLOB を格納できます。 次の図に示すように、ボブは最初に 100 単位の Gold を持っていて、インベントリに Food 単位はありません。

図 1. Bob は 100 単位の Glod がある状態でスタートする。

図の例。Bob は 100 単位の Gold を持っています。

ボブは、Gold を 50 単位使いました。 ゲームは、Gold の BLOB の値を 50 に更新する XGameSaveSubmitUpdateAsync 呼び出しを準備します。

システムは、更新された BLOB とコンテナー更新に関する情報を更新バッファーにキャプチャします。 次の図に示すように、新しい BLOB の値がハード ドライブにコピーされます。

図 2. システムが更新された情報をキャプチャし、値をハード ドライブにコピーします。

図の例。ここで、Bob が 50 単位の Gold を消費します。

次の図に示すように、システムは、新しい BLOB を参照するハード ドライブ上のコンテナー ファイルを更新します。 最終的に、システムは、参照されていない BLOB をガベージ コレクション操作で削除します。

図 3. システムがハード ドライブ上のコンテナー ファイルを更新し、参照されていない BLOB を削除します。

図の例。接続ストレージで Bob の新しい Gold 値を上書きします。

注意

1 回の XGameSaveSubmitUpdateAsync 呼び出しごとに使用する BLOB が増えれば増えるほど、データを確実に格納するために必要なアトミックなファイル システム処理の完了に要する時間は長くなります。 前の例でのデータ ストレージの情報は非常に小さなものですが、これは、1 つのコンテナー内の複数の BLOB をアトミック更新する動作を明確に示すことを意図しています。

複数の BLOB の更新 – 間違ったやり方

Bob が食料を購入するシナリオを考えます。 1 単位の Glod で 1 単位の Food を買うことができます。Bob は 25 単位の Food を買いたいと思っています。

アプリケーションでは、25 単位の Food を追加する XGameSaveSubmitUpdateAsync 呼び出しと、25 単位の Gold を Bob_Inventory コンテナーから差し引く別の呼び出しを発行できます。 ただし、両方の XGameSaveSubmitUpdateAsync 呼び出しの完了ハンドラーが呼び出された場合でも、ハード ドライブへのデータの書き込みを停止させる可能性がある停電や、クラウドへの不完全な同期などが原因で、誤った結果となる場合があります。

次の図は、システムによって実行される手順と、いずれかの手順で停電が発生した場合の結果について説明しています。

両方の XGameSaveSubmitUpdateAsync 呼び出しからのデータはシステムの更新バッファーに既に含まれており、両方の呼び出しのゲームの完了ハンドラーが呼び出されたものと仮定します。

まず、次の図に示すように、新しい Food BLOB の値に対するデータがハード ドライブに書き込まれます。

図 4. システムが Food の BLOB の値をハード ドライブに書き込みます。

図の例。システムが Food の BLOB 値をディスクに書き込みます。

次に、システムは、新しく書き込まれた値を参照するようにコンテナーを更新します。 次の図に示されているように、この手順の実行後、次の手順の前に電源が失われた場合、Bob は、25 単位の Food を得ても対応する Gold がインベントリーから差し引かれないという得な取引をすることになります。

図 5. 新しく書き込まれた値を参照するようにシステムがコンテナーを更新する。

図の例。新しく書き込まれた値を参照するようにシステムがコンテナーを更新します。

次に、次の図に示すように、新しい Gold BLOB の値に対するデータがハード ドライブに書き込まれます。 Bob_Inventory コンテナーによって参照される Gold の値がまだ更新されていません。 Bob は 25 単位以上の Gold を持っていますが、私達が求める結果に一歩近づきます。

図 6. システムが新しい Gold BLOB の値に対するデータをハード ドライブに書き込む

図の例。システムが新しい Gold BLOB の値に対するデータをハード ドライブに書き込みます。

最後に、システムは新しく書き込まれたGold の BLOB を参照するようにコンテナー ファイルを更新します。意図した結果が以下の図に示されます。

図 7. 新しく書き込まれた Gold の BLOB を参照するようにシステムがコンテナー ファイルを更新する

図の例。システムは、新しく書き込まれた Gold の BLOB を参照するようにコンテナー ファイルを更新します。

複数の BLOB の更新 – 正しい方法

停電によって誤った中間状態が発生する可能性がないように、Bob のインベントリの Gold と Food の量をアトミックに更新する適切な方法は、1 回の XGameSaveSubmitUpdateAsync 呼び出しで両方の BLOB を更新することです。 この場合、システムでは次のような手順が実行されます。

まず、次の図に示すように、新しい Food BLOB の値に対するデータがハード ドライブに書き込まれます。

図 8. システムが新しい Food の BLOB 値に対するデータを書き込む

図の例。システムが新しい Food の BLOB の値に対するデータを書き込みます

次に、次の図に示すように、新しい Gold BLOB の値に対するデータがハード ドライブに書き込まれます。

図 9. システムが新しい Gold の BLOB 値に対するデータを書き込む

図の例。システムが新しい Gold の BLOB 値に対するデータを書き込みます。

最後に、以下の図に示すように、システムが、両方の新しい BLOB を参照するようにコンテナー ファイルを更新します。

図 10. 両方の新しい BLOB を参照するようにシステムがコンテナー ファイルを更新する

図の例。システムは、両方の新しい BLOB を参照するようにコンテナー ファイルを更新します。

この例は非常に単純ですが、目的の更新をすべて含む 1 回の XGameSaveSubmitUpdateAsync 呼び出しを発行することで、コンテナー内のデータに対してアトミックに適用されるべきすべての変更を実行することの重要性を示しています。 ゴールドで食料を購入するケースをこのように処理することで、アプリでは、誤って一方の値のみが更新され、本来よりも多いゴールドがキャラクターに残る可能性がある競合条件を回避します。

XGameSave ストレージ領域の同期

XGameSave ストレージ領域を同期するときに、XGameSave サービスは次の 4 つのプロセスを実行します。

  • 接続のチェック
  • ロックの取得
  • コンテナーの一覧表示、比較、および結合ロジック
  • コンテナーのダウンロード

アプリケーションから XGameSave ストレージ領域へのアクセスを要求されると、システムは同期プロセスを実行し、Xbox One コンソール間でユーザーのセーブ データが一貫した状態で維持されるようにして、そのデータをオフライン プレイで使用できるようにします。 同期は、その所要時間が変動する可能性があり、ユーザーの判断を必要とする場合があるため、そのプロセスのさまざまな段階でシステムからユーザーに UI が表示されることがあります。

同期 UI がアクティブな場合でも、ユーザーは Xbox ボタンを押していつでもアプリケーションを離れることができます。 システムは UI を非表示にし、ユーザーの操作なしで可能な範囲で同期は続行されます。

ユーザーがアプリケーションに戻ると、同期が完了していない限り、UI が再度表示されます。 UI が非表示のときに、ユーザーの選択をシステムが勝手に推測することはありません。

ユーザーがホーム画面にいるときはシステムの同期 UI は表示されず、アプリのレンダリングは大きなアプリ タイルに表示され続けるため、XGameSaveInitializeProvider 呼び出しの完了を待っている間、アプリでは状況に応じて適切なビジュアルをレンダリングすることが重要です。 レンダリングを続行することで、アプリがまだインタラクティブであり、データのロードを待機していることがユーザーに示されます。

次の図は、アプリから XGameSave ストレージ領域を要求されたときの、システムのシーケンスを大まかに示しています。 シーケンス全体に数秒以上かかる場合は、システムによって同期 UI が表示されます。

図 11. アプリから XGameSave ストレージ領域を要求されたときのシステムのシーケンス。

アプリから接続ストレージ領域を要求されたときのシステムのシーケンス。

システムは、4 つの段階を経て XGameSaveInitializeProvider 要求を処理します。

  • 接続のチェック
  • ロックの取得
  • コンテナーの一覧表示、比較、および結合ロジック
  • コンテナーのダウンロード

接続のチェック

XGameSaveInitializeProvider 要求の処理を開始するために、システムは接続をチェックします。 コンソールがオフラインの場合、同期プロセス全体がスキップされます。 現在のセッションの間、指定されたユーザーの XGameSave ストレージ領域はオフラインとマークされます。

次にアプリが同じユーザーの XGameSave ストレージ領域にアクセスし、システムがタイトル ストレージ サービスに到達できたときに、変更されたデータがクラウド ストレージと照合されます。 この場合、UI は表示されません。

ロックの取得

接続の確認後、システムは、アプリおよび指定のユーザーに関連付けられているクラウド ストレージ領域への排他的アクセスを取得しようとします。 これは、タイトル ストレージの XGameSave ストレージ領域にロック ファイルを配置することで実行されます。 本体がオンラインで、サービスに到達可能であり、短時間でロックを取得できる場合、UI は表示されず、同期プロセスが続行されます。

システムが特定の XGameSave ストレージ領域のロックを取得し、XGameSave ストレージ領域のインスタンスをアプリに返した場合、Web 要求の成功時には、その XGameSave ストレージ領域内のデータに対して実行されるアプリの API 呼び出しはいずれもブロックされません。 ロックによって十分な保護が提供されるため、アプリが XGameSave ストレージ領域を取得した後にユーザーがシステムからネットワーク ケーブルを取り外しても、API 呼び出しはローカルで使用可能なデータに基づいて動作します。

ロックの取得手順中に発生する可能性があるエラー シナリオがいくつかあります。

同期 UI

コンソールがオンラインであるが、短時間でサービスからロックが取得されなかった場合、「同期中」の UI が表示されます。

ロックの解除

ユーザーが、アプリを現在のコンソールで最後にプレイしてから別のコンソールでプレイした場合は、別のコンソールがストレージ領域への排他的アクセスを所有していて、データのアップロード中である場合があります。 また、別のコンソールでデータのアップロードが開始されたが、終了する前にその接続または電源が失われた可能性もあります。

どちらのケースもロックの競合と呼ばれ、いずれのケースでも、別の本体がデータのアップロード中であることを説明する UI がシステムによって表示されます。 ユーザーは、このプロセスが完了するのを待機するか、クラウド内の現時点で利用可能なデータを使用することができます。

ユーザーがクラウド データを使用することを選択した場合、システムはそのシステム用のロックを取得して (ロックを解除して)、ユーザーおよびアプリのクラウド ストレージへの排他的アクセスを取得します。 他の本体からのアップロードはキャンセルされ、同期プロセスが続行されます。

コンテナーの一覧表示、比較、および結合ロジック

ロックの取得後、システムは指定されたアプリおよびユーザーのクラウド内のすべてのコンテナーの一覧表示を要求します。 次に、ローカル ハード ドライブの内容とクラウド内のデータを比較し、比較の結果に応じて処理を進めます。

ローカル データがクラウドと一致する場合

他のコンソールからの変更がなく、クラウドとローカル ハード ドライブ内のデータが同じである場合、同期は完了しています。 この時点で XGameSaveInitializeProvider が結果と共に返され、アプリはロードと保存を進めることができます。

ローカル データがない場合

クラウドにはデータがあるが、ローカルのコンソールにはない場合、クラウドからのデータがローカルにダウンロードされます。 これが発生する可能性があるのは、ユーザーが友人の家で初めてプレイする場合などです。

同じコンテナーがローカルとクラウドで変更された場合

ユーザーが、別のコンソールでプレイしてクラウド内のコンテナーを変更し、現在のコンソールをオフラインで使用して同じコンテナーを変更した場合、データは自動的に結合されません。 ユーザーは保持するデータを選択するよう求められます。 競合が発生した場合、ユーザーは置き換えポリシーを選択できます。ローカル データまたはクラウド データのどちらかが常に保持されるようにするか、キャンセルを選択して決断を先送りできます。 ユーザーが置き換えポリシーとしてクラウド データまたはローカル データのどちらかを選択した場合、同じ名前であるが内容が異なるは、そのポリシーに従って解決されます。

ユーザーがキャンセルを選択した場合、ユーザーがオフラインでプレイしていたかのように、ゲームは未解決状態の保存システムにアクセスできるようになります。 このケースでは、コンソールがオンラインである場合に、次にアプリが XGameSave ストレージ領域へのアクセスを要求したときに、競合解決 UI が再度表示されます。

コンテナーのダウンロード

競合が解決されると、システムでは、クラウドからダウンロードする必要があるコンテナーを識別するために必要なすべての情報が整います。 必要なすべてのコンテナーがダウンロードされ、XGameSaveInitializeProvider が呼び出された場合はそこから、XGameSaveInitializeProviderAsync が代わりに呼び出された場合は XGameSaveInitializeProviderResult を介して結果が返されます。 その後、アプリはロードと保存を進めることができます。

コンテナーのダウンロード中に発生する可能性があるエラー シナリオがいくつかあります。

ローカル ストレージの不足

必要なコンテナーのためのローカル ハード ドライブ領域が不足している場合、ローカルに保存されているデータを削除してディスク領域を空けるよう求める UI がユーザーに表示されます。 クラウドにバックアップされていない重要なデータをユーザーが完全に削除してしまわないように、UI では、単なるローカル キャッシュであるデータと現在の本体のみに保持されているデータが明確に示されます。

ユーザーに UI が表示されたときの処理は次のとおりです。

  • ユーザーが十分な領域を解放した場合、同期が続行され、完了します。

  • ユーザーが十分な領域を解放せずに UI をキャンセルした場合、XGameSave 初期化プロバイダーからの戻り値は E_GS_OUT_OF_LOCAL_STORAGE になります。 アプリでは、データを保存できない状態でプレイしようとしていることをユーザーに確認する必要があります。 ユーザーが同意した場合、アプリではそのユーザーのデータを保存せずに処理を進める必要があります。 ユーザーがプレイ中にデータを保存することを希望した場合、アプリでは再び XGameSaveInitializeProvider を呼び出して、領域を解放するための UI を再表示させる必要があります。

ゲームの保存に使用できるローカル ストレージの量は、デバイスの種類によって異なります。

Device ゲーム保存用の最大ローカル ストレージ
開発キット 1 GB
販売 Xbox One、Xbox One S、Xbox One X 9 GB
販売 Xbox Series S、Xbox Series X、xCloud 4 GB
販売デスクトップ PC PC の空き容量に制限あり

ユーザーが同期をキャンセルする

ユーザーが同期の完了を待たずに キャンセル を選択した場合、すべての保存 データが使用できるようにはならないことがユーザーに通知されます。 ただし、ゲームはありません。 この時点で XGameSaveInitializeProvider 呼び出しの結果が返され、アプリはロードと保存を進めることができます。

ネットワーク タイムアウト

ネットワーク接続またはサービスの可用性の問題が原因でデータのダウンロードがタイムアウトになった場合、ユーザーには、同期を再試行するオプションが提供されます。 ユーザーが再試行しないことを選択した場合、すべての保存データが使用できるようにはならないことがユーザーに通知されます。ゲームは不足しているデータについては関知しません。 この時点で XGameSaveInitializeProvider 呼び出しの結果が返され、アプリはロードと保存を進めることができます。