> ## 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.

# PlayFab Game Saves のアカウント リンク戦略

> プライマリ リンク ID と、必須または任意のプラットフォーム アカウント リンクを含む、PlayFab Game Saves のクロス プログレッション戦略を比較します。

## プライマリ リンク ID

任意のクロス プログレッション戦略の最初の要件は、ゲーム プラットフォームをまたがるプレイヤー ID を決定することです。プレイヤーは、ゲームが利用可能な任意のデバイスでこの ID でサインインできる必要があります。ファースト パーティ タイトルでは、これはしばしば Microsoft アカウント (MSA)/XBOX アカウントです。多くのサード パーティでは、これは OpenID Connect 実装を通じて公開されるパブリッシャー ID である可能性が高いです。このプレイヤー ID の 2 つの要件は次のとおりです。

1. プレイヤーがゲームをプレイできるすべてのプラットフォームで利用可能であること
2. `LoginWithXbox` または `LoginWithOpenIdConnect` などの既存の PlayFab Login 呼び出しでサポートされていること

これら 2 つの要件を超えて、ID が何らかのデバイス ベースのキャッシュと、その後のゲーム起動時のサイレント サインインをサポートしていると便利です。これは厳密な要件ではありませんが、ワークフローの複雑さやプレイヤーの摩擦を減らすことができます。

## プラットフォーム ネイティブ ID

プラットフォーム ネイティブ ID は、プレイヤーがゲームを実行しているプラットフォームによって提供されるアカウント システムです。例:

* Steam: Steam アカウントおよび認証チケット (`ISteamUser::GetAuthTicketForWebApi`)
* XBOX (PC/コンソール): MSA/XBOX の `XUserHandle` および XSTS トークン
* PlayStation: PSN オンライン ID / アカウント
* Nintendo: Nintendo サービス アカウント / デバイス ID

これらの ID は通常、指定されたデバイスのアクティブ プレイヤー用の `PFLocalUserHandle` を作成および再利用するために使用されます (たとえば、`PFLocalUserCreateHandleWithSteamUser` や `XUserHandle` のラップ)。適切な場合には、プロバイダー固有の PlayFab Login API (たとえば、`LoginWithSteam`、`LoginWithXbox`) を通じて認証するためにも使用できます。

クロス プログレッションのシナリオでは、プラットフォーム ネイティブ ID はプライマリ リンク ID にリンクされ、プログレスと権利がプラットフォーム間でプレイヤーに従うようにします。このドキュメントの推奨ワークフローでは、プラットフォーム ネイティブ ID をローカル ユーザー コンテキストとして使用し、プライマリ リンク ID をクロス プラットフォームのアンカーとして使用します。

## リンク戦略の選択肢

このドキュメントでは、2 つの関連する戦略を検討します。

1. すべてのプレイヤーは、プレイする前にプラットフォーム ネイティブ ID とプライマリ リンク ID をリンクする **必要** があります。
2. プラットフォーム ネイティブ ID とプライマリ リンク ID の間のリンクは **任意** ですが、プレイ前に強く推奨されます。

最初の戦略は実装が容易で、進行状況を失う可能性を含む困難な決定をプレイヤーに強いることを避けられます。ただし、ゲームを開始する際の初期摩擦が増加し、プレイヤーが前もってのリンク要件に十分な価値を見出せない場合、否定的な感情につながる可能性があります。これは特に、多くのプレイヤーを取り込むために非常に低い障壁に依存する多くの Free-to-Play およびモバイル ゲームには適していません。

2 番目の戦略は、将来的な潜在的な競合や困難な決定、および開発者のコーディングの複雑さの増加と引き換えに、事前のプレイヤー エクスペリエンスを改善します。プレイヤーは追加のサインイン要件なしで開始できます。ゲームがサポートしていれば、完全にオフラインで開始することさえできます。その柔軟性は、リンクを決定したときに将来的な問題を引き起こす可能性があります。リンクする ID が別のプラットフォーム上にプレエグジスティングな進行状況を持っている可能性があります。その場合、リンクを確立すると、進行状況が破棄されるか、ゲームが処理する必要のある複雑なマージが発生する可能性があります。ゲームがこの戦略を選択する場合でも、できるだけ早くリンクを促し、リンクの利点を説明し、リンクしないことの潜在的なリスクを警告することが推奨されます。

最終的に、これらの戦略の選択はビジネス視点から推進する必要があります。一方が他方より明らかに優れているわけではありません。ローンチ後にオプション 2 からオプション 1 に移行するとプレイヤーの不満が生じるという市場の明確な証拠があります。これは、この選択を早い段階で正しく行い、最初から設計に組み込むことの重要性を示しています。

## 望ましい状態

戦略に関係なく、最終的な目標は、すべてのプレイヤーを同じ状態に置くことです。プレイヤーのプライマリ リンク ID は、プレイするすべてのデバイスでプラットフォーム ネイティブ ID にリンクされている必要があります。その状態では、進行状況はプライマリ リンク ID に一貫して関連付けられ、プラットフォーム ネイティブ ID をすべてのプラットフォームで有効なプロキシ ID として使用できます。

一部のプラットフォームでは、プライマリ リンク ID がプラットフォーム ネイティブ ID **である** 可能性があります (XBOX 上で動作するファースト パーティ ゲーム)。それらの場合、望ましい状態を達成することは自明です。既存のサインインおよびリンクのガイダンスで十分であるため、このドキュメントではそれらについて詳しく取り上げません。

## LocalUser とログインの比較

PlayFab SDK に存在する 2 つの関連しているが別々の概念を区別することが重要です。

LocalUserCreate 呼び出しは、認証を実行せずにローカル ユーザー オブジェクトを構築し、PFLocalUserHandle を返します。これは、プラットフォーム固有または永続化されたローカル ID (たとえば、XUserHandle のラップ) によってユーザーを識別してキャッシュし、操作やゲーム インスタンス間で同じローカル コンテキストを再利用できるようにします。この操作は純粋にローカルで、ネットワーク リクエストの実行、エンティティ トークンの取得、PlayFab アカウントの作成は行いません。

対照的に、Login 呼び出しは PlayFab でローカル ユーザーを認証し、トークンや ID を含む認証済みエンティティを確立します。Login 呼び出しはネットワーク リクエスト (/Client/LoginWithXbox など) を実行し、createAccount のようなフラグを尊重します。成功すると結果がキャッシュされ、後続の呼び出しは認証済みの状態を再利用できます。

要するに、ローカル ユーザーの作成は、PlayFab Game Saves に必要なローカル ID とハンドル管理を設定するのに対し、サインインは PlayFab に接続してエンティティを必要とする API を認証および有効化するステップです。

## 戦略 1 - プライマリ ID リンクが必須

この戦略を説明するため、Steam でリリースする XBOX ファースト パーティ ゲームを取り上げます。ゲームは XBOX/MSA をプライマリ リンク ID として使用します。ゲームプレイの前にすべてのプレイヤーが XBOX/MSA にサインインすることを要求します。ゲームは引き続き Steam ID に基づく LocalUserHandle を使用しますが、プレイヤーが XBOX ID を作成してリンクするまで、実際のサインインをブロックします。その後の起動では、リンク済みとして検証されているため、Steam ID を直接使用できます。

### 概要のサマリー

* プレイヤーはプレイ前にクロス プラットフォーム ID (XBOX/MSA) でサインインする必要があります。
* プラットフォーム アカウント (Steam) からローカル プレイヤーを作成しますが、XBOX/MSA サインインが完了するまでオンライン プレイを保留します。
* XBOX/MSA サインインの後、プラットフォーム アカウントをクロス プラットフォーム アカウントにリンクします。
* クロス プラットフォーム アカウントに接続されるようにローカル プラットフォーム プロファイルを更新します。
* リンクが確立されているため、将来の起動はシームレスです: プレイヤーはすぐにプレイに進めます。

### 詳細ウォークスルー

**Steam ローカル ユーザー ハンドルを作成する**

* `PFLocalUserCreateHandleWithSteamUser(serviceConfigHandle, customContext, outLocalUserHandle)` を呼び出します。
* 結果: `PFLocalUserHandle`; まだ認証されていません。

**オンラインになろうとする**

* `PFLocalUserLoginAsync(localUserHandle, /*createAccount*/ false, async)` を呼び出します。
* 完了時、`PFLocalUserLoginGetResult` が `E_PF_ACCOUNT_NOT_FOUND` で失敗した場合、アカウントをブートストラップするために XBOX サインインをトリガーします。
* `PFLocalUserLoginGetResult` が成功した場合、既に望ましい状態のアカウントを持っており、オンラインになっています。ワークフローは完了です。

<Note>
  タイトルがクロス プラットフォーム ID を現在のユーザーとリンクした状態を維持する必要がある場合 (たとえば、ゲームが XBOX サインインを必要とし、エンティティの XBOX リンクが現在サインインしている XBOX アカウントと一致することを期待する場合)、プラットフォーム ログインの成功だけでは不十分です。ログイン後に `PFAccountManagementClientGetAccountInfoAsync` を呼び出して、クロス プラットフォーム リンクが現在のユーザーと一致するかどうかを検証してください。一致しない場合、エンティティは古いクロス プラットフォーム ID に結びついている可能性があります。以下の **XBOX 経由でサインイン** および **Steam をリンク** 手順に従って再調整してください。
</Note>

**XBOX 経由でサインインする**

* `PFAuthenticationLoginWithXUserRequest` を構築します:
  * 必要に応じて PlayFab アカウントを作成するために `createAccount=true` を設定します。
  * PC にサインインしている XBOX ユーザーから `XUserHandle` を提供します。
* `PFAuthenticationLoginWithXUserAsync(serviceConfigHandle, request, async)` を呼び出します。
* `PFAuthenticationLoginWithXUserGetResultSize(...)` と `PFAuthenticationLoginWithXUserGetResult(...)` で完了し、`PFEntityHandle` を取得します。
* 注: LoginWithXUser バリアントが利用できない場合は、`PFAuthenticationLoginWithXbox` を使用します。これには、`XUserHandle` から XSTS トークンを自分で抽出する必要があります。

**Steam を認証済み (XBOX ベース) PlayFab アカウントにリンクする**

* 現在の Steam 認証チケット (`ISteamUser::GetAuthTicketForWebApi` または統合内の同等物から。コード内の正確な関数名を確認してください) を使用してクライアント リンク リクエストを構築します。
* `PFAccountManagementClientLinkSteamAccountAsync(entityHandle, linkRequest, async)` を呼び出します。
* 注: 「LinkSteamAccount」は Authentication ではなく Account Management の下にあります。
* 成功すると、プレイヤーの Steam ID が XBOX ベースの PlayFab アカウントにリンクされます。

**Steam ローカル ユーザーをエンティティに関連付ける (リンク後)**

* `PFLocalUserLoginAsync(steamLocalUserHandle, /*createAccount*/ false, async)` を再度呼び出します。
* `PFLocalUserLoginGetResult` が成功するようになり、得られる `PFEntityHandle` は Steam ローカル ユーザーにバインドされます。

**今後の通常操作**

* Game Save やその他のオンライン API は認証済みの `PFEntityHandle` を使用します。
* その後のゲーム起動は、すべてのリンクが整っているため、プレイに直接移行できます。

注記

* `PFLocalUserCreateHandleWithSteamUser` は単独ではサインインしませんが、`PFGameSaveFilesAddUserWithUiAsync` を呼び出そうとするとサインイン試行が発生します。その呼び出しの前にアカウント リンク フローを経ていることを確認してください。

### リンク競合の処理

Steam を XBOX ベースのエンティティにリンクする際に、2 つの異なる競合が発生する可能性があります。それぞれ異なる修復方法が必要です。

| エラー                                   | 意味                                                | 修復方法                                                                                                                                                                                      |
| ------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `E_PF_LINKED_ACCOUNT_ALREADY_CLAIMED` | 現在の Steam アカウントは **異なる** PlayFab エンティティにリンクされている。 | 同意の後、`PFAccountManagementClientLinkSteamAccountAsync` を `forceLink=true` で再度呼び出します。これにより Steam リンクが現在のエンティティに移動します。                                                                       |
| `E_PF_ACCOUNT_ALREADY_LINKED`         | 現在のエンティティには既に **異なる** Steam アカウントがリンクされている。       | 最初に `PFAccountManagementClientUnlinkSteamAccountAsync` を呼び出して古い Steam リンクを削除し、その後、新しい Steam アカウントに対して `PFAccountManagementClientLinkSteamAccountAsync` を呼び出します。`forceLink` はこのエラーを解決しません。 |

**推奨フロー:**

* XBOX サインインの後、`PFAccountManagementClientGetAccountInfoAsync(xboxEntity, getInfoRequest, async)` を呼び出してアカウント情報を取得し、リンクされた ID を確認します。
* エンティティに既に **異なる Steam アカウント** がリンクされている場合:
  * 続行するとこのエンティティから古い Steam 関連付けが削除されることをプレイヤーに警告します。同意を得ます。
  * `PFAccountManagementClientUnlinkSteamAccountAsync(xboxEntity, unlinkRequest, async)` を呼び出して既存の Steam リンクを削除します。
  * 次に、現在の Steam チケットで `PFAccountManagementClientLinkSteamAccountAsync` を呼び出します。
* リンク呼び出しが `E_PF_LINKED_ACCOUNT_ALREADY_CLAIMED` を返す場合 (Steam アカウントが別のエンティティに属する):
  * この Steam アカウントは別の PlayFab アカウントに関連付けられており、ここでリンクすると関連付けが削除されることをプレイヤーに警告します。
  * 同意の後、`forceLink=true` で再試行します。

これにより、プレイヤーの主体性が保たれ、意図せずリンクが上書きされることを避けつつ、ID を調整する明確なパスが提供されます。

### サンプル C++ フロー (Steam ファースト ゲート、XBOX ブートストラップ、Steam リンク、Steam で再サインイン):

<Info>
  このサンプルではライフタイム管理を簡素化するために `static XAsyncBlock` 変数を使用しています。本番コードでは、同時実行または再入呼び出しをサポートするために、非同期ブロックを動的に (たとえば、コンテキスト構造体の一部として) 割り当てる必要があります。
</Info>

```cpp theme={null}
// Assumes: serviceConfigHandle, taskQueue, and a signed-in XUserHandle (xUserHandle) are available
//
// NOTE: RETURN_IF_FAILED is used here for brevity. Because XAsyncBlock callbacks return void,
// production code should replace it with proper error handling (for example, log and return).

// Single context block used by multiple nested callbacks
struct Strategy1AsyncCtx
{
    PFServiceConfigHandle serviceConfig{};
    XTaskQueueHandle queue{};
    PFLocalUserHandle steamUser{};
    XUserHandle xUserHandle{};
    PFEntityHandle xboxEntity{};
};

// 1) Create Steam local user
PFLocalUserHandle steamUser{};
RETURN_IF_FAILED(PFLocalUserCreateHandleWithSteamUser(serviceConfigHandle, /*customContext*/ nullptr, &steamUser));

// 2) Attempt Steam login with createAccount=false
XAsyncBlock steamLoginAsync{};
steamLoginAsync.queue = taskQueue;
// Provide context for nested callbacks
// NOTE: Caller is responsible for deleting s1ctx in the terminal callback.
Strategy1AsyncCtx* s1ctx = new Strategy1AsyncCtx{ serviceConfigHandle, taskQueue, steamUser, xUserHandle };
steamLoginAsync.context = s1ctx;
steamLoginAsync.callback = [](XAsyncBlock* async)
{
    auto* ctx = static_cast<Strategy1AsyncCtx*>(async->context);
    // Try to get result-size; failure means login failed
    size_t bufferSize{};
    HRESULT hrSize = PFLocalUserLoginGetResultSize(async, &bufferSize);
    if (FAILED(hrSize))
    {
        if (hrSize == E_PF_ACCOUNT_NOT_FOUND)
        {
            // 3) Bootstrap via Xbox (provider API), then link Steam
            PFAuthenticationLoginWithXUserRequest xreq{};
            xreq.createAccount = true;
            xreq.user = ctx->xUserHandle; // supply your signed-in XUserHandle

            static XAsyncBlock xboxLoginAsync{}; // ensure lifetime until callback
            xboxLoginAsync.queue = async->queue;
            xboxLoginAsync.context = ctx;
            xboxLoginAsync.callback = [](XAsyncBlock* xAsync)
            {
                auto* ctx = static_cast<Strategy1AsyncCtx*>(xAsync->context);
                // Obtain Xbox login result
                PFEntityHandle xboxEntity{};
                size_t xSize{};
                RETURN_IF_FAILED(PFAuthenticationLoginWithXUserGetResultSize(xAsync, &xSize));
                std::vector<uint8_t> xbuf(xSize);
                PFAuthenticationLoginResult const* xres{};
                RETURN_IF_FAILED(PFAuthenticationLoginWithXUserGetResult(xAsync, &xboxEntity, xbuf.size(), xbuf.data(), &xres, nullptr));
                ctx->xboxEntity = xboxEntity;

                // Detect existing Steam linkage before linking
                // Build a minimal GetAccountInfo request
                // Then call PFAccountManagementClientGetAccountInfoAsync(xboxEntity, &getInfoReq, &getInfoAsync)
                // and inspect the linked accounts section of the result.
                //
                // If the entity already has a DIFFERENT Steam account linked:
                //   Call PFAccountManagementClientUnlinkSteamAccountAsync(xboxEntity, &unlinkReq, &unlinkAsync)
                //   to remove the old Steam link, then proceed to link the current Steam account below.
                //   forceLink does NOT resolve this case (E_PF_ACCOUNT_ALREADY_LINKED error).
                //
                // If a DIFFERENT entity has this Steam account claimed:
                //   The link call below will fail with E_PF_LINKED_ACCOUNT_ALREADY_CLAIMED.
                //   Retry with forceLink=true after obtaining player consent.

                // Link Steam to Xbox-backed account
                PFAccountManagementClientLinkSteamAccountRequest linkReq{};
                linkReq.steamTicket = GetCurrentSteamTicket(); // your helper wrapping ISteamUser::GetAuthTicketForWebApi
                bool isServiceSpecific = true;
                linkReq.ticketIsServiceSpecific = &isServiceSpecific;
                // To resolve E_PF_LINKED_ACCOUNT_ALREADY_CLAIMED after consent:
                // bool forceLink = true;
                // linkReq.forceLink = &forceLink;

                static XAsyncBlock linkAsync{};
                linkAsync.queue = xAsync->queue;
                linkAsync.context = ctx;
                linkAsync.callback = [](XAsyncBlock* linkXAsync)
                {
                    auto* ctx = static_cast<Strategy1AsyncCtx*>(linkXAsync->context);
                    RETURN_IF_FAILED(XAsyncGetStatus(linkXAsync, false));

                    // 4) Re-login Steam local user to bind entity handle
                    static XAsyncBlock steamReloginAsync{};
                    steamReloginAsync.queue = linkXAsync->queue;
                    steamReloginAsync.context = ctx;
                    steamReloginAsync.callback = [](XAsyncBlock* reloginAsync)
                    {
                        auto* ctx = static_cast<Strategy1AsyncCtx*>(reloginAsync->context);
                        PFEntityHandle entity{};
                        RETURN_IF_FAILED(PFLocalUserLoginGetResult(reloginAsync, &entity, 0, nullptr, nullptr, nullptr));
                        // Now the Steam local user has an associated entity
                    };

                    RETURN_IF_FAILED(PFLocalUserLoginAsync(ctx->steamUser, /*createAccount*/ false, &steamReloginAsync));
                };

                RETURN_IF_FAILED(PFAccountManagementClientLinkSteamAccountAsync(ctx->xboxEntity, &linkReq, &linkAsync));
            };

            // NOTE: On platforms that don't support LoginWithXUser, replace this with PFAuthenticationLoginWithXboxAsync
            RETURN_IF_FAILED(PFAuthenticationLoginWithXUserAsync(ctx->serviceConfig, &xreq, &xboxLoginAsync));
        }
        // Other failures: handle/log as needed
        return;
    }

    // Steam login succeeded: get entity
    PFEntityHandle entity{};
    RETURN_IF_FAILED(PFLocalUserLoginGetResult(async, &entity, 0, nullptr, nullptr, nullptr));
    // Use entity as needed; PFServices/GameSave APIs, etc.
};

RETURN_IF_FAILED(PFLocalUserLoginAsync(steamUser, /*createAccount*/ false, &steamLoginAsync));
```

実装に関する注記:

* `E_PF_ACCOUNT_NOT_FOUND` を使用して XBOX ブートストラップ パスを決定します。他のエラーは別途処理します。
* Steam `PFLocalUserHandle` を引き続き使用します。プロバイダー固有の XBOX サインインは、Steam のリンクのみに使用される `entityHandle` を返します。
* リンク後、Steam で再度サインインしてエンティティを LocalUser にバインドします。
* リンク用に新しい Steam チケットを取得することを確認してください。
* 不要になったハンドルは閉じてください (`PFLocalUserCloseHandle`、`PFEntityCloseHandle`)。

## 戦略 2 - プライマリ ID リンクが任意

このシナリオでは、ゲームは Steam 上で動作し、XBOX/MSA をプライマリ リンク ID として使用します。プレイヤーは、XBOX/MSA にすぐにサインインすることなくプレイを開始できます。ゲームは明確な利点と警告を示して早期のリンクを推奨しますが、初期のゲームプレイはブロックされません。

### 概要のサマリー

* プラットフォーム アカウント (Steam) を使用してローカル プレイヤーを作成し、新しいアカウントを作成せずにオンラインでプレイを試みます。
* オンラインが機能する場合は続行します。そうでない場合は選択肢を提示します:
  * クロス プラットフォーム ID (XBOX/MSA) でサインインして統合アカウントを作成する、または
  * プラットフォームのみのアカウントを今すぐ作成してすぐにプレイを開始する。
* 利点とリスクを説明することで、XBOX/MSA への早期リンクを推奨します。
* プレイヤーがリンクを選択した場合:
  * リンクが直接成功した場合、統合アカウントでプレイを続行します。
  * XBOX/MSA アカウントが他の場所で既に進行状況を持っている場合、一時停止して、どの進行状況を保持するかを尋ねます (調整):
    * XBOX/MSA の進行状況を保持し、現在のプラットフォーム アカウントをアタッチする。
    * 現在のプラットフォームの進行状況を保持し、リンクを延期する。
* リンク後、ローカル プラットフォーム プロファイルを更新して統合アカウントに接続されるようにします。
* 将来の起動はシームレスです: プレイヤーは (リンクが延期されなかった場合) 直接プレイに移行できます。

### 詳細ウォークスルー

**Steam ローカル ユーザー ハンドルを作成する**

* `PFLocalUserCreateHandleWithSteamUser(serviceConfigHandle, customContext, outLocalUserHandle)` を呼び出します。
* 結果: `PFLocalUserHandle`; まだ認証されていません。

**オンラインになろうとする (サイレント Steam パス)**

* `PFLocalUserLoginAsync(localUserHandle, /*createAccount*/ false, async)` を呼び出します。
* 完了時:
  * `PFLocalUserLoginGetResult` が成功した場合、得られた `PFEntityHandle` を使用してオンラインで進めます。
    * Steam ベースのアカウントが既に XBOX/MSA にリンクされているかどうかを確認します。`PFAccountManagementClientGetAccountInfoAsync(entityHandle, getInfoRequest, async)` を呼び出してリンクされた ID を確認してください。リンクされていない場合、以下のフロー (非ブロッキング) を使用して XBOX をリンクするようプレイヤーに促し、将来のクロス プログレッションがシームレスに機能するようにします。
  * `PFLocalUserLoginGetResult` が `E_PF_ACCOUNT_NOT_FOUND` で失敗する場合、アカウントが作成されるまでプレイヤーはオフラインのままです。2 つの選択肢を提示します:
    * XBOX ベースの PlayFab アカウントを今すぐ作成: XBOX/MSA でサインイン (たとえば、`PFAuthenticationLoginWithXUserAsync`)。
    * Steam で今すぐサインインしてプレイを開始: `PFLocalUserLoginAsync(localUserHandle, /*createAccount*/ true, async)` を呼び出して、ローカル ハンドルにバインドされた Steam ベースの PlayFab アカウントを作成します。成功時、得られた `PFEntityHandle` を使用してすぐにオンラインで進めます。
  * その他のエラーについては、優雅に処理します (再試行/バックオフ)。

**XBOX リンクを促す (非ブロッキング)**

* リンクの利点 (クロス プログレッション、権利の移植性) を提示します。
* XBOX/MSA サインインを提供します。

**XBOX 経由でサインインする (ブートストラップまたは調整検出付きの後付けリンク)**

* プレイヤーが後で XBOX リンクを開始する場合 (現在のセッションから既に Steam ベースの `PFEntityHandle` がある場合):
  * 新しい XBOX ベースのアカウントを作成しない。
  * 適切なリンク API を呼び出して XBOX ID を現在の Steam ベースのアカウントに直接リンクする (たとえば、`PFAccountManagementClientLinkXboxAccountAsync(currentSteamEntity, linkRequest, async)`)。
  * リンクが成功した場合、XBOX/MSA ID がリンクされました。通常の操作を続行します。
  * リンクがプレエグジスティングなリンク/競合エラー (XBOX ID が別の場所で既にリンクされている) で失敗した場合、アカウント調整モードに入ります。すぐに `createAccount=false` で XBOX/MSA サインインを実行し (たとえば、`PFAuthenticationLoginWithXUserAsync`)、XBOX ベースの `PFEntityHandle` を取得します。次に、プレイヤーに促す前にコンテキストとしてプロファイル メタデータを取得します。同意の後、リンクの決定に進みます。
* プレイヤーが XBOX の既存のアカウントを持たない場合 (ブートストラップ パス):
  * まず `PFAuthenticationLoginWithXUserAsync` (または `LoginWithXUser` が利用できない場合は `PFAuthenticationLoginWithXboxAsync`) を使用して `createAccount=false` で XBOX/MSA サインインを試みる。
    * サインインが成功した場合: XBOX/MSA アカウントには既に PlayFab アカウントがあり、既存の進行状況を持っている可能性が高い。アカウント調整モードに入り、どの進行状況を保持するかを選択する。
    * サインインが `E_PF_ACCOUNT_NOT_FOUND` で失敗した場合: `createAccount=true` で再度サインインして PlayFab アカウントを作成する。
  * `PFAuthenticationLoginWithXUserGetResultSize(...)` および `PFAuthenticationLoginWithXUserGetResult(...)` で完了し、`PFEntityHandle` を取得します。

**Steam を認証済み (XBOX ベース) PlayFab アカウントにリンクする**

* 新しい XBOX ベースの PlayFab アカウントを作成したばかりの場合 (既存の Steam リンクがない場合) に適用されます。
* 以前にアカウント調整に入った場合、リンクの決定と必要な `forceLink` アクションはそこで処理されます。この手順はスキップします。
* 現在の Steam 認証チケット (`ISteamUser::GetAuthTicketForWebApi` または統合から) を使用してクライアント リンク リクエストを構築します。
* `forceLink=false` (デフォルト) で `PFAccountManagementClientLinkSteamAccountAsync(entityHandle, linkRequest, async)` を呼び出します。新しいアカウントであれば成功するはずです。
* リンクが失敗した場合、エラー条件として扱います (予期しない競合または認証失敗)。プレイヤーにエラーを提示し、再試行する前に再サインインを促すことを検討します。
* 成功すると、プレイヤーの Steam ID が XBOX ベースの PlayFab アカウントにリンクされます。

**Steam ローカル ユーザーをエンティティに関連付ける (リンク後)**

* `PFLocalUserLoginAsync(steamLocalUserHandle, /*createAccount*/ false, async)` を再度呼び出します。
* `PFLocalUserLoginGetResult` が成功するようになり、得られる `PFEntityHandle` は Steam ローカル ユーザーにバインドされます。

**今後の通常操作**

* Game Save やその他のオンライン API は認証済みの `PFEntityHandle` を使用します。
* その後のゲーム起動は、すべてのリンクが整っているため、プレイに直接移行できます。

注記

* 初期のゲームプレイを XBOX/MSA サインインでブロックしないでください。早期に促しつつ、プレイヤーが延期できるようにしてください。
* `PFLocalUserCreateHandleWithSteamUser` は単独でサインインしません。`PFGameSaveFilesAddUserWithUiAsync` のようにサインインをトリガーする API を呼び出す前に、リンク フローが完了していることを確認してください。

### アカウント調整 (XBOX/MSA の既存の進行状況)

プレイヤーが (たとえば、コンソールや別のプラットフォームからの) 既存の進行状況を持つ XBOX/MSA アカウントでサインインする場合、盲目的に新しいアカウントを作成したり既存のリンクを上書きしたりすることを避ける必要があります。慎重な 2 段階アプローチで検出および調整を行います。

<Tip>
  **プレイヤー向けの表現:** 開発者ドキュメントでは「調整 (reconciliation)」という用語を使用しています。プレイヤー UI では、「ゲーム プロファイルの選択」、「進行状況の選択」、「既存の進行状況を保持」など、より明確な用語を優先することで、混乱を減らし、プレイヤーが (マージではなく) 続行するプロファイルを 1 つ選択していることを強調します。
</Tip>

#### 検出フェーズ

* 調整には 2 つのパスから入ることができます:
  1. 既存の XBOX/MSA アカウントが検出された場合: `createAccount=false` で XBOX/MSA サインインを試みる。
     * サインインが成功した場合、XBOX/MSA ID には既に PlayFab アカウントがあり、既存の進行状況を持っている可能性が高い。調整に入る。
     * サインインが `E_PF_ACCOUNT_NOT_FOUND` で失敗した場合、ブートストラップに進む (`createAccount=true`)。調整に入る必要はありません。
  2. 後付けリンク競合が検出された場合: XBOX/MSA を現在の Steam ベースのアカウントにリンクする際、リンクがプレエグジスティング リンク/競合エラー (XBOX ID が別の Steam ID と既にリンクされている) で失敗する。
     * すぐに `createAccount=false` で XBOX/MSA サインインを実行し (たとえば、`PFAuthenticationLoginWithXUserAsync`)、XBOX ベースの `PFEntityHandle` を取得します。
     * コンテキストを提示するためにプロファイル メタデータ (たとえば、最近の進行状況、セーブ スロット、タイム スタンプ) を取得します。
     * どの進行状況を保持するかを決定するために調整に入る。

#### 調整フェーズ

* 分かりやすい選択肢を推奨します: XBOX/MSA (プライマリ リンク ID) の進行状況を保持し、ローカル Steam の進行状況を破棄します。これによりクロス プラットフォームのアンカーが保持され、複雑なマージを避けられます。
* プレイヤーには実質的に 3 つの選択肢があります (ゲームはこれらのうち 1〜2 のみを提供することを選択できます):
  1. XBOX/MSA の進行状況を保持 (推奨): 現在の Steam アカウントを既存の XBOX ベースの PlayFab アカウントにリンクする。
  2. 現在の Steam の進行状況を保持しリンクをキャンセル: 今のところ Steam ベースのアカウントで続行する。XBOX/MSA データをリンクまたは上書きしない。次回リンクを再度提案する。
  3. 現在の Steam の進行状況を保持し XBOX の進行状況を破棄: Steam ベースのアカウントに切り替え、意図的に XBOX/MSA の進行状況を破棄する。このパスは明示的なゲーム設計上の考慮が必要で、特に他のプラットフォーム ネイティブ ID が既に XBOX アカウントに接続されている場合は、軽々しく試みるべきではありません。このドキュメントでは、このシナリオの実装詳細は取り上げません。
* コミット前:
  * XBOX エンティティに対して `PFAccountManagementClientGetAccountInfoAsync` を呼び出して既存のプロバイダー リンクを確認し、Steam リンクが既に存在するかどうかを確認します。
  * エンティティに **異なる Steam アカウント** がリンクされている場合、新しいものが追加される前にこの既存の Steam 関連付けがエンティティから削除されることをプレイヤーに警告します。これには、現在の Steam アカウントをリンクする前に `PFAccountManagementClientUnlinkSteamAccountAsync` を呼び出す必要があります。`forceLink` はこのシナリオを解決しません。
  * **現在の Steam アカウント** が **別のエンティティ** にリンクされている場合、`PFAccountManagementClientLinkSteamAccountAsync` を呼び出すと `E_PF_LINKED_ACCOUNT_ALREADY_CLAIMED` が返されます。リンクによって Steam ID が別のエンティティから移動することを警告し、同意の後 `forceLink=true` で再試行します。

#### コミット アクション (プレイヤーの選択に基づく)

* プレイヤーが XBOX/MSA の進行状況を選択する場合:
  * XBOX サインインのエンティティが取得されていることを確認します (検出時にまだ行っていない場合は、`createAccount=false` で `PFAuthenticationLoginWithXUserAsync` を実行します)。
  * XBOX エンティティに既に異なる Steam アカウントがリンクされている場合、`PFAccountManagementClientUnlinkSteamAccountAsync` を呼び出して最初に削除します。
  * 現在の Steam を XBOX ベースのアカウントにリンクします。呼び出しが `E_PF_LINKED_ACCOUNT_ALREADY_CLAIMED` (Steam が別のエンティティにある) を返す場合、同意の後 `forceLink=true` で再試行します。
  * Steam ローカル ユーザーで再度サインインして (`PFLocalUserLoginAsync(..., /*createAccount*/ false, ...)`) エンティティをバインドします。
* プレイヤーが Steam の進行状況を選択する場合:
  * Steam の PlayFab アカウントがまだ存在しない場合は、`PFLocalUserLoginAsync(..., /*createAccount*/ true, ...)` で作成/バインドします。
  * XBOX リンクを延期します。すぐにゲームプレイを許可します。次回リンクを再度提案します。

#### 注記

* プレイヤーの決定に情報を提供するために、常に十分なコンテキストを取得して表示してください。
* プロンプトとデフォルトを時間の経過とともに改善するために、調整結果のテレメトリを記録してください。

### 戦略 2 のフロー ダイアグラム

以下のダイアグラムは完全な決定木を示しています。すべてのパスは、プレイヤーがプレイ可能な状態に到達する 1 つの終端ノードに収束します。

<img src="https://mintcdn.com/microsoft-4404708b/U1LR64ZWxo45eXwl/images/playfab/player-progression/game-saves/strategy2-flow.svg?fit=max&auto=format&n=U1LR64ZWxo45eXwl&q=85&s=a4c6001009c7729b9a68014f478b1a20" alt="Steam ローカル ユーザーの作成からサインイン、アカウント リンク、調整パスまでの完全な決定木を示す戦略 2 のフロー ダイアグラム。" width="1424" height="1968" data-path="images/playfab/player-progression/game-saves/strategy2-flow.svg" />

## 関連項目

* [PlayFab Game Saves の概要](/services/playfab/player-progression/game-saves/overview)
* [Game Saves クイックスタート](/services/playfab/player-progression/game-saves/quickstart)
* [Game Saves の競合](/services/playfab/player-progression/game-saves/conflicts)
* [トークンの有効期限と再ログイン](/services/playfab/sdks/c/relogin)


## Related topics

- [Unity で Google サインインから Google Play Games に移行する](/ja-jp/services/playfab/identity/player-identity/platform-specific-authentication/google-play-games-sign-in-migration-details.md)
- [XR-013 Microsoft アカウントとパブリッシャー アカウントの関連付け](/ja-jp/publishing/certification/xr/xr-013.md)
- [Game Saves の概要](/ja-jp/services/playfab/player-progression/game-saves/overview.md)
- [FMA XR-013: Microsoft アカウントとパブリッシャーアカウントのリンク](/ja-jp/publishing/certification/fma/xr-013.md)
- [クイックスタート (Windows) - Core SDK セットアップ](/ja-jp/services/playfab/sdks/unified-sdk/quickstart-core.md)
