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

# MPSD から PlayFab Multiplayer と MPA への移行

> XBOX タイトルを MPSD から PlayFab Multiplayer ロビーおよび Multiplayer Activity (MPA) に移行して招待、マッチメイキング、最近のプレイヤーを実装するための移行ガイド。

## はじめに

このドキュメントは、現在 MPSD を使用しており、マルチプレイヤーゲームで PlayFab Multiplayer と MPA を使用するように移行したいゲーム開発者向けです。このドキュメントでは、最も一般的なマルチプレイヤーのシナリオを取り上げ、PlayFab Multiplayer を MPA とともに使用する方法を示すコードスニペットを提供します。

### Multiplayer Session Directory (MPSD) の概要

* ユーザーグループを接続するために必要な情報を共有するための、フル機能を備えたセッションサービス
* 招待や参加機能のために XBOX UI と統合
* SmartMatch マッチメイキングと完全に統合
* セッションは事前定義されたセッションテンプレートから派生
* 接続検出とセッションフローのための機能が統合
* サービス間で利用可能

### Multiplayer Activity Service (MPA) の概要

* プレイヤーアクティビティ、招待、および最近のプレイヤーに関する XBOX Live 統合を簡素化するための軽量サービス
* 招待の送信/受け入れおよび参加時にシェルおよびコンソールオペレーティングシステムと連携
* セッション管理やマッチメイキングは行わない
* サービス間で利用可能

### PlayFab Multiplayer の概要

* ロビーの検索や閲覧機能を含む、完全なマルチプレイヤーロビーサービス
* 透過的な API 統合を備えたクロスプラットフォームのリアルタイムサービス通知
* リアルタイム通知をサポートする完全なマッチメイキングサービス

## 初期化

次の表は、初期化のために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| `XblMultiplayerAddSubscriptionLostHandler` | `PFMultiplayerInitialize` |
| `XblMultiplayerAddConnectionIdChangedHandler` | `PFMultiplayerSetEntityToken` |
| `XblMultiplayerSetSubscriptionsEnabled` | |
| `XblMultiplayerSessionCurrentUserSetStatus` | |

### 初期化 - サンプルコード

PlayFab titleID でライブラリを初期化し、PlayFab サービスへのログイン中に受け取ったエンティティトークンを設定します。

```cpp theme={null}
PFMultiplayerHandle pfmHandle{};
HRESULT hr = PFMultiplayerInitialize(pfTitleId, &pfmHandle);
if (FAILED(hr))
{
    //...
}

hr = PFMultiplayerSetEntityToken(pfmHandle, &entityKey, entityToken);
if (FAILED(hr))
{
    //...
}
```

## ロビーの状態変更

次の表は、セッション/ロビーに関連するイベントを処理するために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| `XblMultiplayerSessionSubscribedChangeTypes` | `PFMultiplayerStartProcessingLobbyStateChanges` |
| `XblMultiplayerSessionChangedHandler` | `PFMultiplayerFinishProcessingLobbyStateChanges` |
| `XblMultiplayerSessionSubscriptionLostHandler` | |

### ロビーの状態変更 - サンプルコード

状態変更の処理を開始することをライブラリに通知します。キューに入れられた各状態変更を処理してから、状態変更の処理が完了したことを通知します。

```cpp theme={null}
HRESULT hr = PFMultiplayerStartProcessingLobbyStateChanges(MultiplayerHandle, &StateChangeCount, &StateChanges);
if (FAILED(hr))
{
    //...
}

for (uint32_t i = 0; i < StateChangeCount; ++i)
{
    const PFLobbyStateChange& Change = *StateChanges[i];

    switch (Change.stateChangeType)
    {
    case PFLobbyStateChangeType::CreateAndJoinLobbyCompleted:        /*...*/ break;
    case PFLobbyStateChangeType::JoinLobbyCompleted:                 /*...*/ break;
    case PFLobbyStateChangeType::MemberAdded:                        /*...*/ break;
    case PFLobbyStateChangeType::AddMemberCompleted:                 /*...*/ break;
    case PFLobbyStateChangeType::MemberRemoved:                      /*...*/ break;
    case PFLobbyStateChangeType::ForceRemoveMemberCompleted:         /*...*/ break;
    case PFLobbyStateChangeType::LeaveLobbyCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::Updated:                            /*...*/ break;
    case PFLobbyStateChangeType::PostUpdateCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::Disconnecting:                      /*...*/ break;
    case PFLobbyStateChangeType::Disconnected:                       /*...*/ break;
    case PFLobbyStateChangeType::JoinArrangedLobbyCompleted:         /*...*/ break;
    case PFLobbyStateChangeType::FindLobbiesCompleted:               /*...*/ break;
    case PFLobbyStateChangeType::InviteReceived:                     /*...*/ break;
    case PFLobbyStateChangeType::InviteListenerStatusChanged:        /*...*/ break;
    case PFLobbyStateChangeType::SendInviteCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::CreateAndClaimServerLobbyCompleted: /*...*/ break;
    case PFLobbyStateChangeType::ClaimServerLobbyCompleted:          /*...*/ break;
    case PFLobbyStateChangeType::ServerPostUpdateCompleted:          /*...*/ break;
    case PFLobbyStateChangeType::ServerDeleteLobbyCompleted:         /*...*/ break;
    }
}

hr = PFMultiplayerFinishProcessingLobbyStateChanges(MultiplayerHandle, StateChangeCount, StateChanges);
if (FAILED(hr))
{
    //...
}
```

## ロビーの作成

次の表は、セッション/ロビーの作成および参加のために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| `XblMultiplayerSessionReferenceCreate` | `PFMultiplayerCreateAndJoinLobby` |
| `XblMultiplayerSessionCreateHandle` | |
| `XblMultiplayerSessionJoin` | |
| `XblMultiplayerAddSessionChangedHandler` | |
| `XblMultiplayerSessionSetSessionChangeSubscription` | |
| `XblMultiplayerSessionSetHostDeviceToken` | |
| `XblMultiplayerWriteSessionAsync` | |

> [!NOTE]
>
> ロビーを作成するのに、PlayFab Game Manager での追加のセットアップや構成は必要ありません。すべての構成はコード内で行うことができます。

### ロビーの作成 - サンプルコード

ロビーを構成し、任意の初期ロビープロパティまたはメンバープロパティを設定してから、ロビーを作成して参加します。

```cpp theme={null}
PFLobbyCreateConfiguration createConfig{};
createConfig.maxMemberCount = 4;
createConfig.ownerMigrationPolicy = PFLobbyOwnerMigrationPolicy::Automatic;
createConfig.accessPolicy = PFLobbyAccessPolicy::Public;

const char* memberPropertyKeys[] { "favoriteColor" };
const char* memberPropertyValues[] { "blue" };

PFLobbyJoinConfiguration joinConfig{};
joinConfig.memberPropertyCount = 1;
joinConfig.memberPropertyKeys = memberPropertyKeys;
joinConfig.memberPropertyValues = memberPropertyValues;

PFLobbyHandle myLobby{};

HRESULT hr = PFMultiplayerCreateAndJoinLobby(
    pfmHandle,           // PFMultiplayerHandle
    &localUserEntityKey, // local user 
    &createConfig,       // create config
    &joinConfig,         // join config
    nullptr,             // async context (optional)   
    &myLobby);           // lobby handle

if (FAILED(hr))
{
    //...
}
```

## ロビーの検索

次の表は、セッション/ロビーを検索するために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| `XblMultiplayerGetSearchHandlesAsync` | `PFMultiplayerFindLobbies` |
| `XblMultiplayerSearchHandleGetId` | |
| `XblMultiplayerSearchHandleGetCustomSessionPropertiesJson` | |
| `XblMultiplayerSearchHandleGetMemberCounts` | |
| `XblMultiplayerSearchHandleGetSessionClosed` | |

### ロビーの検索 - サンプルコード

検索構成を設定してからロビーを検索します。

```cpp theme={null}
PFLobbySearchConfiguration searchConfiguration{};
searchConfiguration.filterString = filterString.c_str(); // filtering
searchConfiguration.sortString = sortString.c_str();     // sorting
searchConfiguration.clientSearchResultCount = 50;        // limits the number of results
searchConfiguration.friendsFilter;                       // return only lobbies with friends in them

HRESULT hr = PFMultiplayerFindLobbies(
    pfmHandle,          // PFMultiplayerHandle
    &localUserEntityKey,  // local user
    &searchConfiguration, // search config
    nullptr);             // async context (optional)

if (FAILED(hr))
{
    //...
} 
```

次に、イベントの状態変更が返されたら、任意の検索結果を処理します。

```cpp theme={null}
const auto& stateChange = static_cast<const PFLobbyFindLobbiesCompletedStateChange&>(change);
if (SUCCEEDED(stateChange.result))
{
    for (uint32_t i = 0; i < stateChange.searchResultCount; ++i)
    {
        const PFLobbySearchResult& searchResult = stateChange.searchResults[i];
        searchResult.lobbyId;            // lobby id
        searchResult.connectionString;   // connection string
        searchResult.ownerEntity;        // lobby host
        searchResult.maxMemberCount;     // lobby size
        searchResult.currentMemberCount; // players in lobby
        
        for (uint32_t j = 0; j < searchResult.searchPropertyCount; ++j) 
        {
            const char* searchPropertyKey = searchResult.searchPropertyKeys[j];
            const char* searchPropertyValue = searchResult.searchPropertyValues[j];
            /*...*/
        }
        
        for (uint32_t k = 0; k < searchResult.friendCount; ++k) 
        {
            PFEntityKey friendEntityKey = searchResult.friends[k];
            /*...*/
        }
    }
}
else
{
    //...
}
```

## ロビー検索キー

カスタム検索プロパティを定義する際には、制限された一連のキーのみを使用できます。

* 文字列プロパティの場合、次のキーがサポートされます: string\_key1、string\_key2、\[...] string\_key30
* 数値プロパティの場合、次のキーがサポートされます: number\_key1、number\_key2、\[...] number\_key30

## ロビー検索演算子

**FindLobbies** API のクエリ文字列は、OData に似た構文で構造化されます。フィルター文字列の最大サイズは 600 文字です。

これらの OData 演算子を使用してクエリ文字列を構成できます。演算子は大文字と小文字が区別されます。

| 演算子 | 意味 | 例 |
| - | - | - |
| eq | 等しい | string\_key1 eq 'CaptureTheFlag' |
| lt | 未満 | number\_key2 lt 10 |
| le | 以下 | number\_key2 le 10 |
| gt | より大きい | number\_key3 gt 100 |
| ge | 以上 | number\_key3 ge 100 |
| ne | 等しくない | string\_key1 ne 'CaptureTheFlag' |
| and | かつ | string\_key1 eq 'CaptureTheFlag' and number\_key2 lt 10 |

> [!NOTE]
>
> 文字列プロパティを比較する場合は、比較する値を必ず一重引用符で囲んでください。たとえば、"string\_key1 eq **'SOME STRING VALUE'**" のようにします。数値プロパティは囲む必要はありません。

使用可能な事前定義済みの演算子もあります。指定する際には "lobby/" を前に付ける必要があります。

| 演算子 | 意味 | 例 |
| - | - | - |
| memberCount | ロビー内のプレイヤー数 | lobby/memberCount eq 5 |
| maxMemberCount | ロビー内で許可されるプレイヤーの最大数 | lobby/maxMemberCount gt 10 |
| memberCountRemaining | ロビーに参加可能なプレイヤーの残り数 | lobby/memberCountRemaining gt 0 |
| membershipLock | ロビーのロック状態。'Unlocked' または 'Locked' と等しくなければならない | lobby/membershipLock eq 'Unlocked' |
| amOwner | 自分がオーナーであるロビー。'true' と等しくなければならない | lobby/amOwner eq 'true' |
| amMember | 自分がメンバーであるロビー。'true' と等しくなければならない | lobby/amMember eq 'true' |
| amServer | サーバーがクライアント所有のロビーに参加したロビー。'true' と等しくなければならない | lobby/amServer eq 'true' |

## 検索結果の並べ替え

このクエリの並べ替えを昇順 ("asc") または降順 ("desc") で含む OData スタイルの文字列。OrderBy 句は、任意の検索数値キーまたは数値の事前定義検索キーに使用できます。特定の数値に最も近い順に並べ替えるには、距離モニカーを使用して指定された数値検索キーからの距離で並べ替えることができます。距離ソートで昇順または降順を使用することはできません。このフィールドは、1 つのソート句または 1 つの距離句のみをサポートします。ソートが指定されていない場合、または指定されたソートに対してタイブレイクが必要な場合、デフォルトのソートは作成時刻に基づく降順になります。

| 例 | 意味 |
| - | - |
| number\_key1 asc | 数値検索キーの昇順 |
| lobby/memberCount desc | 数値検索キーの降順 |
| distance(number\_key1 = 5) | 指定された数値からの距離でソート |
| | 作成時刻の降順 |

### 検索結果の並べ替えとフィルタリング - サンプルコード

```cpp theme={null}
PFLobbySearchConfiguration searchConfiguration{};​
​
PFLobbySearchFriendsFilter friendsFilter{};    ​
friendsFilter.includeXboxFriendsToken = MyGame::GetLocalUserXboxToken();​
searchConfiguration.friendsFilter = &friendsFilter;​

// Create filter string for ranked deathmatch with skill between 10-20​
std::string filterString;​
filterString +=  "string_key1 eq DeathMatch and ";​ 
filterString +=  "string_key2 eq Ranked and ";​
filterString +=  "number_key1 -ge 10 and ";​
filterString +=  "number_key1 -le 20";​
​
// Create sort string based on skill level​
std::string sortString;​
sortString += std::string("distance{number_key1=" + std::to_string(playerSkill.c_str()) + "}";​

searchConfiguration.filterString = filterString.c_str();​
searchConfiguration.sortString = sortString.c_str();​
searchConfiguration.clientSearchResultCount = 10;        // limits the number of results​
```

## ロビーへの参加

次の表は、セッション/ロビーへの参加のために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| `XblMultiplayerGetSessionByHandleAsync` | `PFMultiplayerJoinLobby` |
| `XblMultiplayerSessionJoin` | |
| `XblMultiplayerAddSessionChangedHandler` | |
| `XblMultiplayerSessionSetSessionChangeSubscription` | |
| `XblMultiplayerSessionCurrentUserSetStatus` | |
| `XblMultiplayerWriteSessionByHandleAsync` | |
| `XblMultiplayerSessionCloseHandle` | |

> [!NOTE]
>
> ロビーに参加するには接続文字列が必要です。通常、ロビーのホストは自身のアクティビティにこの接続文字列を設定するか、招待経由で送信します。接続文字列を取得するには `PFLobbyGetConnectionString` を呼び出す必要があります。

### ロビーへの参加 - サンプルコード

初期参加構成を設定してから、ロビーに参加します。

```cpp theme={null}
const char* memberPropertyKeys[] { "number", "name"};
const char* memberPropertyValues[] { "8675309", "Jenny"};

PFLobbyJoinConfiguration joinConfig{};
joinConfig.memberPropertyCount = 2;
joinConfig.memberPropertyKeys = memberPropertyKeys;
joinConfig.memberPropertyValues = memberPropertyValues;

PFLobbyHandle myLobby{};

HRESULT hr = PFMultiplayerJoinLobby(
    pfmHandle,             // PFMultiplayerHandle
    &localUserEntityKey,   // local user
    lobbyConnectionString, // connection string
    &joinConfig,           // join config
    nullptr,               // async context (optional)
    &myLobby);             // handle to the lobby

if (FAILED(hr))
{
    //...
} 
```

## ロビーの更新

次の表は、セッション/ロビーの更新のために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| `XblMultiplayerGetSessionByHandleAsync` | `PFLobbyPostUpdate` |
| `XblMultiplayerWriteSessionByHandleAsync` | |
| `XblMultiplayerSessionCloseHandle` | |

> [!NOTE]
>
> `PFLobbyPostUpdate` を使用して、ロビープロパティとメンバープロパティの両方を更新できます。関数を 1 回呼び出すことで、1 種類または両方の種類のプロパティを更新できます。

### ロビーの更新 - サンプルコード (ロビープロパティ)

```cpp theme={null}
const char* lobbyPropertyKeys[] { "exampleKey_1", "exampleKey_2" };
const char* lobbyPropertyValues[] { "exampleValue_1234", "exampleValue_ABCD" };

PFLobbyDataUpdate lobbyUpdateData{};
lobbyUpdateData.lobbyPropertyCount = 2;
lobbyUpdateData.lobbyPropertyKeys = lobbyPropertyKeys;
lobbyUpdateData.lobbyPropertyValues = lobbyPropertyValues;

HRESULT hr = PFLobbyPostUpdate(
    myLobby,             // handle to the lobby
    &localUserEntityKey, // local user
    &lobbyUpdateData,    // update data for the lobby
    nullptr,             // update data for a member
    nullptr);            // async context (optional)

if (FAILED(hr))
{
    //...
} 
```

### ロビーの更新 - サンプルコード (メンバープロパティ)

```cpp theme={null}
const char* memberPropertyKeys[] { "favoriteColor" };
const char* memberPropertyValues[] { "yellow" };

PFLobbyMemberDataUpdate memberUpdateData{};
memberUpdateData.lobbyPropertyCount = 1;
memberUpdateData.lobbyPropertyKeys = memberPropertyKeys;
memberUpdateData.lobbyPropertyValues = memberPropertyKeys;

HRESULT hr = PFLobbyPostUpdate(
    myLobby,             // handle to the lobby
    &localUserEntityKey, // local user
    nullptr,             // update data for the lobby
    & memberUpdateData   // update data for a member
    nullptr);            // async context (optional)

if (FAILED(hr))
{
    //...
}
```

## マッチメイキング

PlayFab Multiplayer のマッチメイキング API は、MPSD のマッチメイキング API と比較的類似しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| Smartmatch ホッパーを介した構成 | マッチメイキングキューに基づく |
| ホッパーは MPSD セッションテンプレートにバインドされる | マッチメイキングルールはキューに適用される |
| マッチメイキングルールはホッパーに適用される | マッチメイキングはマッチ チケットを介して開始される |
| マッチメイキングには既存の MPSD セッションが必要 | マッチメイキングの結果は新しい PlayFab ロビー |
| マッチメイキングの結果は新しい MPSD セッション | PlayFab マルチプレイヤー サーバー割り当てをサポート |

> [!NOTE]
>
> マッチメイキングキューは PlayFab Game Manager を介して構成する必要があります。

## マッチメイキングの状態変更

次の表は、マッチメイキングに関連するイベントを処理するために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | PlayFab Multiplayer |
| - | - |
| `XblMultiplayerSessionSubscribedChangeTypes` | `PFMultiplayerStartProcessingMatchmakingStateChanges` |
| `XblMultiplayerSessionChangedHandler` | `PFMultiplayerFinishProcessingMatchmakingStateChanges` |
| `XblMultiplayerSessionSubscriptionLostHandler` | |

### マッチメイキングの状態変更 - サンプルコード

状態変更の処理を開始することをライブラリに通知します。キューに入れられた各状態変更を処理してから、状態変更の処理が完了したことを通知します。

```cpp theme={null}
uint32_t stateChangeCount = 0;
const PFMatchmakingStateChange* const* stateChanges = nullptr;

HRESULT hr = PFMultiplayerStartProcessingMatchmakingStateChanges(pfmHandle, &stateChangeCount, &stateChanges);
if (FAILED(hr))
{
    //...
}

for (uint32 i = 0; i < stateChangeCount; ++i)
{
    const PFMatchmakingStateChange& change = *stateChanges[i];
    
    switch (change.stateChangeType)
    {
    case PFMatchmakingStateChangeType::TicketStatusChanged: /*...*/ break;
    case PFMatchmakingStateChangeType::TicketCompleted:     /*...*/ break;
    }
}

hr = PFMultiplayerFinishProcessingMatchmakingStateChanges(pfmHandle, stateChangeCount, stateChanges);
if (FAILED(hr))
{
    //...
}
```

## マッチメイキングの開始

| MPSD | PlayFab Multiplayer |
| - | - |
| MPSD セッションの作成呼び出し... | `PFMultiplayerCreateMatchmakingTicket` |
| `XblMatchmakingCreateMatchTicketAsync` | `PFMultiplayerJoinMatchmakingTicketFromId` |
| `XblMatchmakingCreateMatchTicketResult` | `PFMatchmakingTicketGetStatus` |
| `XblMultiplayerSessionMatchmakingServer` | `PFMatchmakingTicketGetMatch` |
| MPSD セッションへの参加呼び出し... | `PFMultiplayerJoinArrangedLobby` |

### マッチメイキングの開始 - サンプルコード

```cpp theme={null}
PFMatchmakingTicketConfiguration matchTicketConfig{};
matchTicketConfig.timeoutInSeconds;        // how long to attempt matchmaking
matchTicketConfig.queueName;               // matchmaking queue name
matchTicketConfig.membersToMatchWithCount; // num remote players to go into matchmaking with
matchTicketConfig.membersToMatchWith;      // remote players to go into matchmaking with

HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    pfmHandle,                   // PFMultiplayerHandle
    1,                           // local user count
    &currentUserEntityKey,       // local users
    nullptr,                     // local user attributes (optional)
    &ticketConfig,               // ticket config
    nullptr,                     // async context (optional)
    &m_activeMatchmakingTicket); // matchmaking ticket

if (FAILED(hr))
{
    //...
} 

hr = PFMultiplayerJoinMatchmakingTicketFromId(
    pfmHandle,                   // PFMultiplayerHandle
    1,                           // local user count
    &currentUserEntityKey,       // local users
    nullptr,                     // local user attributes (optional)
    ticketId,                    // matchmaking ticket to join
    queueName,                   // matchmaking queue name
    nullptr,                     // async context (optional)
    &m_activeMatchmakingTicket); // matchmaking ticket

if (FAILED(hr))
{
    //...
}
```

> [!NOTE]
>
> マッチメイキングは、`membersToMatchWith` フィールドに指定されたすべてのメンバーが参加するまで開始されません。

次に、マッチが見つかり、状態変更が返されたら、arranged lobby に参加します。

```cpp theme={null}
const auto& stateChange = static_cast<const PFMatchmakingTicketCompletedStateChange&>(change);
if (SUCCEEDED(stateChange.result))
{
    PFMatchmakingTicketStatus status{};
    HRESULT hr = PFMatchmakingTicketGetStatus(stateChange.ticket, &status);
    if (SUCCEEDED(hr))
    {
        if (status == PFMatchmakingTicketStatus::Matched)
        {
            const PFMatchmakingMatchDetails* matchDetails = nullptr;
            hr = PFMatchmakingTicketGetMatch(stateChange.ticket, &matchDetails);
            if (SUCCEEDED(hr))
            {
                const char* memberPropertyKeys[] { "favoriteCheese" };
                const char* memberPropertyValues[] { "Wensleydale" };

                PFLobbyArrangedJoinConfiguration joinConfig{};
                joinConfig.accessPolicy = PFLobbyAccessPolicy::Private;
                joinConfig.maxMemberCount = 4;
                joinConfig.ownerMigrationPolicy = PFLobbyOwnerMigrationPolicy::Automatic;
                joinConfig.memberPropertyCount = 1;
                joinConfig.memberPropertyKeys = memberPropertyKeys;
                joinConfig.memberPropertyValues = memberPropertyValues;
                
                PFLobbyHandle myLobby{};

                hr = PFMultiplayerJoinArrangedLobby(
                    pfmHandle,                            // PFMultiplayerHandle
                    &localUserEntityKey,                  // local user
                    matchDetails->lobbyArrangementString, // connection string
                    &config,                              // join config
                    nullptr,                              // async context (optional)
                    &myLobby);                            // handle to the lobby

                if (FAILED(hr))
                {
                    //...
                }
            }
        }
    }
}
```

## クリーンアップ

次の表は、クリーンアップとシャットダウンのために MPSD および PlayFab Multiplayer で使用される類似の関数のリストを示しています。

| MPSD | MPA |
| - | - |
| `XblMultiplayerRemoveSubscriptionLostHandler` | `PFMultiplayerUninitialize` |
| `XblMultiplayerRemoveConnectionIdChangedHandler` | |
| `XblMultiplayerSetSubscriptionsEnabled` | |

> [!NOTE]
>
> `PFMultiplayerUninitialize` を呼び出す前に、必ずすべてのアクティブなロビーから離脱し、進行中のマッチメイキングチケットを破棄してください。

### クリーンアップ - サンプルコード

```cpp theme={null}
HRESULT hr = PFMultiplayerUninitialize(pfmHandle);
if (FAILED(hr))
{
    //...
}
```

## アクティビティ

次の表は、アクティビティを管理するために MPSD および MPA で使用される類似の関数のリストを示しています。

| MPSD | MPA |
| - | - |
| `XblMultiplayerSetActivityAsync` | `XblMultiplayerActivitySetActivityAsync` |
| `XblMultiplayerClearActivityAsync` | `XblMultiplayerActivityDeleteActivityAsync` |
| | `XblMultiplayerActivityGetActivityAsync` |
| | `XblMultiplayerActivityGetActivityResultSize` |
| | `XblMultiplayerActivityGetActivityResult` |

> [!NOTE]
>
> アクティビティを設定したり招待を送信したりする場合は、必ず `PFLobbyGetConnectionString` から返される接続文字列を使用してください。

### アクティビティ - サンプルコード

```cpp theme={null}
const char* connectionString;
HRESULT hr = PFLobbyGetConnectionString(myLobby, &connectionString);
if (FAILED(hr))
{
    //...
}

uint32_t maxPlayerCount;
hr = PFLobbyGetMaxMemberCount(myLobby, &maxPlayerCount);
if (FAILED(hr))
{
    //...
}

uint32_t lobbyMemberCount;
const PFEntityKey* lobbyMembers;
hr = PFLobbyGetMembers(myLobby, &lobbyMemberCount, &lobbyMembers);
if (FAILED(hr))
{
    //...
}

const char* lobbyId;
hr = PFLobbyGetLobbyId(myLobby, &lobbyId);
if (FAILED(hr))
{
    //...
}

PFLobbyAccessPolicy pfAccessPolicy;
hr = PFLobbyGetAccessPolicy(LobbyHandle, &pfAccessPolicy);
if (FAILED(hr))
{
    //...
}

XblMultiplayerActivityJoinRestriction joinRestriction = XblMultiplayerActivityJoinRestriction::InviteOnly;

switch (pfAccessPolicy)
{
case PFLobbyAccessPolicy::Public:  joinRestriction = XblMultiplayerActivityJoinRestriction::Public; break;
case PFLobbyAccessPolicy::Friends: joinRestriction = XblMultiplayerActivityJoinRestriction::Followed; break;
case PFLobbyAccessPolicy::Private: joinRestriction = XblMultiplayerActivityJoinRestriction::InviteOnly; break;
}

XblMultiplayerActivityInfo info{};
info.connectionString = connectionString;
info.joinRestriction = joinRestriction;
info.maxPlayers = maxPlayerCount;
info.currentPlayers = lobbyMemberCount;
info.groupId = lobbyId;
info.xuid = myXuid;

auto async = std::make_unique<XAsyncBlock>();
async->queue = queue;
async->callback = [](XAsyncBlock* async) 
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async };
    HRESULT hr = XAsyncGetStatus(async, false);
    if(FAILED(hr))
    {
        //...
    }
};

HRESULT hr = XblMultiplayerActivitySetActivityAsync(
    xblContext,    // XblContextHandle
    &info,         // XblMultiplayerActivityInfo
    false,         // Allow cross-platform joins
    async.get()    // XAsyncBlock
);

if (SUCCEEDED(hr)) 
{
    async.release();
}
else
{
    //...
}
```

## 招待

次の表は、招待を送受信するために MPSD および MPA で使用される類似の関数のリストを示しています。

| MPSD | MPA |
| - | - |
| `XGameInviteRegisterForEvent` | `XGameInviteRegisterForEvent` |
| `XGameInviteUnregisterForEvent` | `XGameInviteUnregisterForEvent` |
| `XblMultiplayerSendInvitesAsync` | `XblMultiplayerActivitySendInvitesAsync` |
| `XGameUiShowSendGameInviteAsync` | `XGameUiShowMultiplayerActivityGameInviteAsync` |

### 招待 - サンプルコード (タイトル UI)

```cpp theme={null}
const char* connectionString;
HRESULT hr = PFLobbyGetConnectionString(myLobby, &connectionString);
if (SUCCEEDED(hr))
{
    auto async = std::make_unique<XAsyncBlock>();
    async->queue = queue;
    async->callback = [](XAsyncBlock* async)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async };
        HRESULT hr = XAsyncGetStatus(async, false);
        if(FAILED(hr))
        {
            //...   
        }
    };
    
    HRESULT hr = XblMultiplayerActivitySendInvitesAsync(
        xblContext,       // XblContextHandle 
        &xuid,            // recipient
        1,                // number of invited XUIDs
        true,             // allow cross-platform joins
        connectionString, // use lobby connection string
        async.get());

    if (SUCCEEDED(hr))
    {
        async.release();
    }
    else
    {
        //...
    }
}
else
{
    //...
}
```

### 招待 - サンプルコード (XBOX UI)

```cpp theme={null}
auto async = std::make_unique<XAsyncBlock>();
async->queue = queue;
async->callback = [](XAsyncBlock* async)
{
    std::unique_ptr<XAsyncBlock> async{ async };
    HRESULT hr = XGameUiShowMultiplayerActivityGameInviteResult(async);
    if(FAILED(hr))
    {
        //...   
    }
};

HRESULT hr = XGameUiShowMultiplayerActivityGameInviteAsync(
    async.get(), // XAsyncBlock
    user.get()   // XUserHandle that is sending the invite
);

if (SUCCEEDED(hr))
{
    async.release();
}
else
{
    //...
}
```

> [!NOTE]
>
> `XGameUiShowMultiplayerActivityGameInviteResult` は現在設定されているアクティビティを使用します。この関数を使用する前に、`XblMultiplayerActivitySetActivityAsync` を使用してアクティビティを設定する必要があります。

## 最近のプレイヤー

次の表は、MPSD および MPA を使用する際の最近のプレイヤーリストの管理方法を示しています。

| MPSD | MPA |
| - | - |
| プレイヤーは同じ MPSD セッションにいる必要がある | `XblMultiplayerActivityUpdateRecentPlayers` |
| セッションの `gameplay` プロパティが `true` に設定される | `XblMultiplayerActivityFlushRecentPlayersAsync` |
| 両方のプレイヤーがアクティブとしてマークされる | |

> [!NOTE]
>
> スロットリングを避けるため、`XblMultiplayerActivityUpdateRecentPlayers` への呼び出しをバッチ処理することがベストプラクティスです。

### 最近のプレイヤー - サンプルコード

```cpp theme={null}
XblMultiplayerActivityRecentPlayerUpdate update{};
update.xuid = metPlayerXuid;
update.encounterType = XblMultiplayerActivityEncounterType::Default;

HRESULT hr = XblMultiplayerActivityUpdateRecentPlayers(xblContext, &update, 1);
if (FAILED(hr))
{
    //...
}
```


## Related topics

- [XDK ネットワークから XBOX GDK への移行](https://docs.xbox.com/ja-jp/build/console-features/networking/xdk-migration/index.md)
- [Unity で Google サインインから Google Play Games サインインへの移行のフォールバック](https://docs.xbox.com/ja-jp/services/playfab/identity/player-identity/platform-specific-authentication/google-play-games-sign-in-migration-fallback.md)
- [移行ガイド: PlayFab スタンドアロン SDK (v1) から PlayFab 統合 SDK (v2) へ](https://docs.xbox.com/ja-jp/services/playfab/sdks/unified-sdk/migrating-from-v1.md)
- [Microsoft Game Development Kit (GDK) 移植ガイド](https://docs.xbox.com/ja-jp/home/build-first-title/porting-guides.md)
- [概念](https://docs.xbox.com/ja-jp/services/xbox-services/multiplayer/mpsd/concepts/index.md)
