マルチプレイヤー セッションの参照
このトピックは、マルチプレイヤーのセッション参照を使用することで、指定された条件を満たすオープン マルチプレイヤー ゲーム セッションの一覧をユーザーがクエリできるようにするために使用します。
マルチプレイヤー セッションの参照は、November 2016 で導入されました。 セッション参照のシナリオでは、ゲームのユーザーは参加可能なゲーム セッションのリストを取得できます。 このリストの各セッション エントリにはゲームに関するいくつかの追加メタデータが含まれています。ユーザーはその情報を使用して、参加するセッションを選択できます。 また、ユーザーはメタデータを使用してセッションのリストをフィルターできます。 ユーザーは興味のあるゲーム セッションを見つけて、セッションに参加できます。
ユーザーは、新しいゲーム セッションを作成し、マッチメイキングを行うのではなく、セッションの参照機能を使用して他のプレイヤーを集めることもできます。 マルチプレイヤー セッションの参照と従来のマッチメイキングは異なります。 ユーザーは、マルチプレイヤー セッションの参照を使用して、参加するゲーム セッションを選択します。 マッチメイキングでは、ユーザーは、該当するゲーム セッションに自動的にユーザーを配置しようとする「ゲームを探す」オプションを選択します。
一方、マルチプレイヤー セッションの参照は、低速な手動のプロセスであり、ユーザーにとって常に最適なゲーム エクスペリエンスが選ばれるとは限りませんが、ユーザーに選択権が与えられるため、ゲームの選択結果は主観的により満足度の高いものになる可能性があります。 マルチプレイヤー セッションの参照とマッチメイキングの両方のシナリオをゲームに組み込むのが一般的です。 通常、マッチメイキングは一般的にプレイされるゲーム モードに使用され、マルチプレイヤー セッションの参照はカスタム ゲームに使用されます。
シナリオ例
John はバトル アリーナ マルチプレイヤー ゲームをプレイしたいと思っていますが、すべてのユーザーがランダムにヒーローを選択するゲームをプレイしたいと思っています。 彼は、オープン ゲーム セッションのリストを取得し、マルチプレイヤー セッションの参照を使用して、「ランダム ヒーロー」が説明に含まれるゲームを探します。
タイトルの UI で可能であれば、「ランダム ヒーロー」ゲーム モードを選択して、「ランダム ヒーロー」ゲームを示すタグ付きのセッションだけを取得することができます。 John は気に入ったゲームが見つかれば、そのセッションに参加できます。 十分なメンバーが参加すると、ゲーム セッションのホストはゲームを開始できます。
ロール
マルチプレイヤー セッションの参照機能内のセッションでは、特定のロールのユーザーを募集できます。 たとえば、ユーザーは、5 人以下の突撃クラス、2 人以上のヒーラー ロール、1 人以上のタンク ロールを含むセッションを指定するゲーム セッションを作成できます。
別のユーザーがセッションに参加するときは、ロールを事前に選択できます。 その後、サービスは、選択したロールで使用可能なオープン スロットの数に基づいて、セッションに参加可能かどうかを判断します。 ユーザーがフレンドの参加のために 2 スロットを予約する場合、セッションの「フレンド」ロールを指定でき、ホストとフレンドであるユーザーだけが「フレンド」ロールに専用の 2 スロットを埋めることができます。
ロールについて詳しくは、「マルチプレイヤーのロール」を参照してください。
マルチプレイヤー セッションの参照の動作
セッションの参照は、検索ハンドルを使用することによって動作します。 検索ハンドルは、セッションの参照と、検索属性などのセッションに関する追加メタデータを含む、データのパケットです。
タイトルは、マルチプレイヤー セッションの参照対応の新しいゲーム セッションを作成するとき、セッションの検索ハンドルを作成します。 検索ハンドルは、タイトルの検索ハンドルを管理するマルチプレイヤー サービス ディレクトリ (MPSD) に格納されます。 タイトルは、セッションのリストを取得する必要があるときは、検索クエリを MPSD に送信できます。MPSD は検索条件に一致する検索ハンドルのリストを返します。 その後、タイトルはセッションのリストを使用して、ユーザーに参加可能なゲームの一覧を表示できます。 セッションに空きがない場合、またはそれ以外の理由で参加できない場合は、タイトルは MPSD からの検索ハンドルを削除して、マルチプレイヤー セッションの参照クエリにセッションが表示されないようにできます。
注意
検索ハンドルは、セッションの一覧をユーザーに表示するために使用します。 バックグラウンドのマッチメイキングの検索ハンドルを使用しないでください。 バックグラウンドのマッチメイキングでは、SmartMatch の使用をお勧めします。 詳細については、「Multiplayer Manager での SmartMatch の使用によるマルチプレイヤー ゲームの検索」を参照してください。
マルチプレイヤー セッションの参照用にセッションを設定する
セッションで検索ハンドルを使用するには、セッションで searchable
および userAuthorizationStyle
が true
に設定されている必要があります。
userAuthorizationStyle
機能は UWP ゲームでのみ必須となりますが、Microsoft Game Development Kit (GDK) ゲームを含むすべての Xbox ゲームに実装することをお勧めします。 これにより、タイトルの将来の移植性が保証されます。
注意
userAuthorizationStyle
を true
に設定すると、セッションの readRestriction
および joinRestriction
が local
ではなく none
になります。 つまり、ゲーム セッションに参加するには、タイトルで検索ハンドルまたは転送ハンドルを使う必要があります。
これらの機能は、Xbox サービスを構成するときにセッション テンプレートで設定できます。
注意
マルチプレイヤー セッションの参照では、実際のゲームプレイに使用されるセッションでのみ検索ハンドルを作成します。 ロビー セッションには使用しません。
ゲーム セッションの所有権
SmartMatch やフレンドのみのセッションなど、多くのゲーム セッションの種類では、ホストまたは所有者は不要です。 ただし、マルチプレイヤー セッションの参照ゲームを設定する際に、所有者が必要になる場合があります。
所有者管理セッションがあると、セッションから他のメンバーを削除したり、他のメンバーの所有権の状態を変更したりするなど、ホストにいくつかの利点があります。
セッションで所有者を使用するには、セッションで hasOwners
を true
に設定する必要があります。
マルチプレイヤーのロールを使っている場合、所有者だけがユーザーにロールを割り当てることができるように設定できます。
注意
セッションの所有者が Xbox メンバーをブロックすると、そのメンバーはセッションに参加できません。
すべての所有者がセッションから退出した場合、サービスはセッションに対して定義されている ownershipPolicy.migration
ポリシーに基づいてセッションを処理します。
ポリシーが「oldest」の場合は、セッションに最も長くいるプレイヤーが新しい所有者として設定されます。
ポリシーが「endsession」(指定されていない場合は既定値) の場合は、サービスはセッションを終了し、残っているすべてのプレイヤーをセッションから削除します。
検索ハンドル
検索ハンドルは、JSON 構造として MPSD に格納されます。 検索ハンドルには、セッションへの参照だけでなく、検索属性と呼ばれる検索のための追加メタデータも含まれます。 セッションの検索ハンドルは常に 1 つしか作成できません。 Xbox サービス API を使ってセッションの検索ハンドルを作成するには、XblMultiplayerCreateSearchHandleAsync メソッドを呼び出します。
検索属性
検索属性は、次のコンポーネントで構成されます。
tags
は、ユーザーがゲーム セッションの分類に使用できる、ハッシュタグのような文字列記述子です。- タグは、文字で始まる必要があり、スペースを含むことはできず、100 文字未満にする必要があります。
- タグの例: "ProRankOnly"、"norocketlaunchers"、"cityMaps"。
strings
はテキスト変数です。- 文字列名は、文字で始まる必要があり、スペースを含むことはできず、100 文字未満にする必要があります。
- 文字列メタデータの例: "Weapons"="knives+pistols+rifles"、"MapName"="UrbanCityAssault"、"description"="Fun casual game, new people welcome"。
numbers
は数値の変数です。 XSAPI は、float 型として数値を取得します。- 数字の名前は、文字で始まる必要があり、スペースを含むことはできず、100 文字未満にする必要があります。
- 数値メタデータの例: "MinLevel" = 25、"MaxRank" = 10。
注意
サービスではタグと文字列値の大文字小文字の区別が維持されますが、タグを照会するときは tolower()
関数を使用する必要があります。 つまり、タグと文字列値は現在、大文字が含まれていても、小文字として扱われます。
XSAPI では、XblMultiplayerCreateSearchHandleAsync メソッドを使用してタグやメタデータなどの検索属性を設定できます。
その他の詳細
検索ハンドルを取得した結果には、セッションに関する他の有用なデータも含まれます (セッションが閉じられているかどうか、セッションに何らかの参加制限があるか、など)。
XSAPI では、これらの詳細と検索属性は、検索クエリの後で返される XblMultiplayerSearchHandle
に含まれます。
検索ハンドルを削除する
セッションに空きがない場合や、セッションが閉じられた場合など、マルチプレイヤー セッションの参照からセッションを削除するときは、検索ハンドルを削除できます。 Xbox API では、XblMultiplayerDeleteSearchHandleAsync メソッドを使用して、検索ハンドルを削除します。
メタデータで検索ハンドルを作成する
次のコードでは、Xbox マルチプレイヤー API を使用してセッションの検索ハンドルを作成する方法を示します。
フラット C
size_t tagsCount = 1;
XblMultiplayerSessionTag tags[1] = {};
tags[0] = XblMultiplayerSessionTag{ "SessionTag" };
size_t numberAttributesCount = 1;
XblMultiplayerSessionNumberAttribute numberAttributes[1] = {};
numberAttributes[0] = XblMultiplayerSessionNumberAttribute{ "numberattributename", 1.1 };
size_t strAttributesCount = 1;
XblMultiplayerSessionStringAttribute strAttributes[1] = {};
strAttributes[0] = XblMultiplayerSessionStringAttribute{ "stringattributename", "string attribute value" };
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
XblMultiplayerSearchHandle searchHandle{ nullptr };
HRESULT hr = XblMultiplayerCreateSearchHandleResult(asyncBlock, &searchHandle);
if (SUCCEEDED(hr))
{
const char* handleId{ nullptr };
XblMultiplayerSearchHandleGetId(searchHandle, &handleId);
}
};
HRESULT hr = XblMultiplayerCreateSearchHandleAsync(
xblContextHandle,
&xblMultiplayerSessionReference,
tags,
tagsCount,
numberAttributes,
numberAttributesCount,
strAttributes,
strAttributesCount,
asyncBlock.get()
);
if (SUCCEEDED(hr))
{
// The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
// If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
asyncBlock.release();
}
コード例で使用される関数の詳細については、「multiplayer_c」を参照してください。
セッションの検索クエリを作成する
検索ハンドルのリストを取得するときは、検索クエリを使用して、特定の条件に一致するセッションの結果を限定できます。
検索クエリの構文は OData スタイルの構文であり、次の演算子だけがサポートされます。
演算子 | 説明 |
---|---|
eq | equals (次の値と等しい) |
ne | not equal to (次の値と等しくない) |
gt | greater than (次の値より大きい) |
ge | greater than or equal (次の値以上) |
lt | less than (次の値より小さい) |
le | less than or equal (次の値以下) |
and | 論理 AND |
or | 論理 OR (下の「注」を参照) |
注意
また、ラムダ式と tolower
正規関数も使用できます。
現在、他の OData 関数はサポートされていません。
タグまたは文字列値を検索するときは、検索クエリで tolower
関数を使用する必要があります。
このサービスは小文字の文字列の検索のみをサポートします。
Xbox サービスは、検索クエリに一致する最初の 100 個の結果のみを返します。
結果が多すぎる場合にユーザーが検索クエリを絞り込むことができるようにします。
注意
フィルター文字列のクエリでは論理 OR
ステートメントがサポートされています。
ただし、使用できる OR
は 1 つだけで、クエリのルートで指定する必要があります。
クエリに複数の OR
を含めることはできません。また、OR
がクエリ構造の最上位でない位置に出現するクエリを作成することもできません。
検索ハンドルのクエリの例
restful 呼び出しの「Filter」では、すべての検索ハンドルに対するクエリで使用する OData Filter 言語文字列を指定します。
マルチプレイヤー 2015 API では、XblMultiplayerGetSearchHandlesAsync メソッドの searchFilter
パラメーターで検索フィルター文字列を指定できます。
現在、以下のフィルター シナリオがサポートされています。
フィルター | 検索フィルター文字列 |
---|---|
1 人のメンバーの xuid '1234566' | "session/memberXuids/any(d:d eq '1234566')" |
1 人のオーナーの xuid ' 1234566 ' | "session/ownerXuids/any(d:d eq '1234566')" |
' classb ' と等しい文字列 'forzacarclass' | "tolower(strings/forzacarclass) eq 'classb'" |
6 に等しい数値 'forzaskill' | "numbers/forzaskill eq 6" |
1.5 より大きい数値 'halokdratio' | "numbers/halokdratio gt 1.5" |
タグ 'coolpeopleonly' である | "tags/any(d:tolower(d) eq 'coolpeopleonly')" |
タグ 'cursingallowed' を含まないセッション | "tags/any(d:tolower(d) ne 'cursingallowed')" |
数値が 0 に等しい数値 'rank' を含まないセッション | "numbers/rank ne 0" |
'classa' に等しい 文字列 'forzacarclass' を含まないセッション | "tolower(strings/forzacarclass) ne 'classa'" |
タグが 'coolpeopleonly' であり、数値 'halokdratio' が 7.5 に等しい | "tags/any(d:tolower(d) eq 'coolpeopleonly') eq true and numbers/halokdratio eq 7.5" |
数値 'halodkratio' が 1.5 以上であり、数値 'rank' が 60 より小さく、数値 'customnumbervalue' が 5 以下である | "numbers/halokdratio ge 1.5 and numbers/rank lt 60 and numbers/customnumbervalue le 5" |
実績 ID が '123456' | "achievementIds/any(d:d eq '123456')" |
言語コード 'en' | "language eq 'en'" |
スケジュールされた時刻、指定された時刻以下のスケジュールされた時刻をすべて返す | "session/scheduledTime le '2009-06-15T13:45:30.0900000Z'" |
投稿時刻、指定された時刻より小さい投稿時刻をすべて返す | "session/postedTime lt '2009-06-15T13:45:30.0900000Z'" |
セッション登録状態 | "session/registrationState eq 'registered'" |
セッション メンバーの数が 5 に等しい | "session/membersCount eq 5" |
セッション メンバーの目標数が 1 より大きい | "session/targetMembersCount gt 1" |
セッション メンバーの最大数が 3 より小さい | "session/maxMembersCount lt 3" |
セッション メンバーの目標数とセッション メンバー数の差異が 5 以下 | "session/targetMembersCountRemaining le 5" |
セッション メンバーの最大数とセッション メンバー数の差異が 2 より大きい | "session/maxMembersCountRemaining gt 2" |
セッション メンバーの目標数とセッション メンバー数の差異が 15 以下。
ロールに目標数が指定されていない場合、このクエリでは、セッション メンバーの最大数とセッション メンバー数の差異を使用してフィルター処理が行われます。 |
"session/needs le 15" |
ロールの種類 "lfg" のロール "confirmed" で、このロールのメンバー数が 5 に等しい | "session/roles/lfg/confirmed/count eq 5" |
ロールの種類 "lfg" のロール "confirmed" で、このロールの目標数が 1 より大きい。
ロールに目標数が指定されていない場合、ロールの最大数が代わりに使用されます。 |
"session/roles/lfg/confirmed/target gt 1" |
ロールの種類 "lfg" のロール "confirmed" で、ロールの目標数とロールのメンバー数の差異が 15 以下。
ロールに目標数が指定されていない場合、このクエリでは、ロールの最大数とロールのメンバー数の差異を使用してフィルター処理が行われます。 |
"session/roles/lfg/confirmed/needs le 15" |
特定のキーワードを含むセッションを指すすべての検索ハンドル | "session/keywords/any(d:tolower(d) eq 'level2')" |
特定の SCID に属しているセッションを指すすべての検索ハンドル | "session/scid eq '151512315'" |
特定のテンプレート名が使用されているセッションを指すすべての検索ハンドル | "session/templateName eq 'mytemplate1'" |
'elite' というタグを持っているか、'guns' の数が 15 より大きく、文字列 'clan' が 'purple' に等しいすべての検索ハンドル | "tags/any(a:tolower(a) eq 'elite') or number/guns gt 15 and string/clan eq 'purple'" |
検索結果を更新する
ゲームで、セッションの一覧を自動的に更新しないでください。 UI を使用してユーザーが一覧を手動で更新できるようにしてください。 このようにすると、より望ましいフィルター結果が得られるようにユーザー検索条件を調整する際に便利です。 ユーザーがセッションに参加しようとしてもセッションに空きがないか閉じている場合も、ゲームは検索結果を更新する必要があります。
注意
検索の更新が多すぎるとサービスがスロットリングする可能性があります。 タイトルではクエリの更新頻度を制限する必要があります。
サービスの呼び出し量を減らすために、検索ハンドルにカスタム セッション プロパティが含まれています。これらのプロパティを使用して、すぐに変化するセッション属性を保存およびクエリすることができます。 これらの属性は、検索属性に保存することはできません。
検索ハンドルのクエリ
次のコードでは、検索ハンドルを照会する方法を示します。
API は、クエリに一致するすべての検索ハンドルを表す XblMultiplayerSearchHandle
オブジェクトのコレクションを返します。
フラット C
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
size_t resultCount{ 0 };
auto hr = XblMultiplayerGetSearchHandlesResultCount(asyncBlock, &resultCount);
if (SUCCEEDED(hr) && resultCount > 0)
{
auto handles = new XblMultiplayerSearchHandle[resultCount];
hr = XblMultiplayerGetSearchHandlesResult(asyncBlock, handles, resultCount);
if (SUCCEEDED(hr))
{
// Process handles
for (auto i = 0u; i < resultCount; ++i)
{
const char* handleId{ nullptr };
XblMultiplayerSearchHandleGetId(handles[i], &handleId);
XblMultiplayerSearchHandleCloseHandle(handles[i]);
}
}
}
};
const char* sessionName{ "MinGameSession" };
const char* orderByAttribute{ nullptr };
bool orderAscending{ false };
const char* searchFilter{ nullptr };
const char* socialGroup{ nullptr };
HRESULT hr = XblMultiplayerGetSearchHandlesAsync(
xblContextHandle,
scid,
sessionName,
orderByAttribute,
orderAscending,
searchFilter,
socialGroup,
asyncBlock.get()
);
if (SUCCEEDED(hr))
{
// The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
// If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
asyncBlock.release();
}
コード例で使用される関数の詳細については、「multiplayer_c」を参照してください。
検索ハンドルを使用してセッションに参加する
ユーザーが参加するセッションの検索ハンドルを取得した後、XblMultiplayerWriteSessionByHandleAsync を使用して、セッションに追加します。
注意
XblMultiplayerWriteSessionAsync メソッドを使用してマルチプレイヤー セッションの参照セッションに参加することはできません。