Version: jp

how-to

note

Unity マッチメイキングは現在、クローズドベータテスト中です。このサービスの詳細については、マッチメイキングに関するドキュメントを参照してください。チュートリアル、FAQ、API 情報が含まれます。

Multiplay を使用してマルチプレイヤーゲームをホストすることに興味をお持ちですか?詳細についてはお問い合わせください

このページでは、Multiplay マッチメーカーを操作するための高度なシナリオについて説明します。

マッチメーカーの概要#

Multiplay マッチメイキングは、皆さんのオンラインゲームサービスのバックエンドに合った、セキュリティで保護された高速かつカスタマイズ可能な一貫したマッチメイキングを実現します。

次の画像をクリックして、マッチメイキングが Multiplay のゲームサーバーホスティングとどのように連携するかを詳しく示した YouTube ビデオをご覧ください。

Matchmaking with Multiplay | Unite Copenhagen

マッチメイキングシステムには、高度な同時並行処理に対応した、カスタマイズ可能な分散型マッチ関数が備わっており、これによってチケットのプールが処理されます。チケットとは、特定のプレイヤーやプレイヤーグループのマッチメイキングリクエストを表すものです。システムは申請されたマッチを処理し、Multiplay のクラウドスケールのハイブリッドゲームサーバーホスティングに専用ゲームサーバーを割り当て、チケットを接続の詳細で更新することで、ゲームクライアントが割り当てられたサーバーをポーリングして接続し、プレイできるようにします。

マッチメイキングワークフローの概要図

Unity パッケージ#

Multiplay マッチメイキングは完全にエンジン非依存です。一部の Unity パッケージは、開発を容易にするために提供されています。他の Unity パッケージと同様、ソースコードはパッケージとともに提供されています。

パッケージ最新バージョン説明
com.unity.ucg.qos0.1.1-preview.4サンプルとリクエストフローを含んだマッチメイキングクライアント
com.unity.ucg.matchmaking-client0.2.1-previewMultiplay QoS サーバーと通信するためのクライアント
com.unity.ucg.usqp0.1.1-preview.3ゲームサーバーと通信するための Multiplay サーバークエリプロトコルパッケージ
note

このリストはパッケージの最新のリストであり、マッチメイキングサンプルとともに含まれているどのパッケージよりも優先されます。

これらのパッケージは現在 Unity パッケージマネージャーでは表示されません。プロジェクトでこれらを参照するには、パッケージ manifest.jsonエントリーを直接追加する必要があります。

チケット#

チケットはマッチメイキングリクエストの基本単位です。チケットは、プレイヤーやプレイヤーグループのゲームプレイの意向を表します。チケットには、カスタムマッチメイキングロジックでプレイヤーをゲームに参加させるために必要なすべてのデータが含まれています。

チケットには属性とプロパティが含まれています。

  • 属性 - マッチ関数の内部からチケットを照会する際に使用する、インデックス可能なフィールドです。属性の例としては、マップ、モード、プラットフォーム、チケットに含まれるプレイヤー、チームスキル、ゲームの勝利数などが挙げられます。なお、属性は現在 double 値に制限されています。

  • Properties - マッチ関数がデータベースからチケットを取得した後に利用できるカスタム形式のデータのマップです。API では、プロパティマップは <string, byte[]> の形式で表現されます。JSON API を使用している場合、byte[] は Base64 エンコードされた文字列として表現する必要があります(CreateTicket API のドキュメントを参照)。事前エンコード済みのカスタムデータには、JSON、bytes、protobuf など、さまざまな形式を使用できます。使用する形式は、特定のユースケースでの有効性や、ゲームクライアントコードとの全般的な互換性に応じて決定できます。プロパティに含めるデータの例としては、インベントリ、サービス品質(QoS)の詳細、マップの票数、最近のゲームの記録、キャンペーンの進捗などが挙げられます。

詳細については、Tickets API のドキュメントを参照してください。

Created 属性#

created グローバル属性は自動的にインデックスが付けられ、すべてのチケットで照会のために利用できるようになります。この属性は、チケットが作成された時刻を表す Unix UTC ミリ秒の long 値で構成されます。これにより、マッチ関数が所定の時間マッチを待機しているチケットのプールを考慮できるようになります。

詳細については、「Match 関数のサンプル」の TimeoutFunction を参照してください。

マッチ関数#

次のリンクでは、マッチ関数に関する詳しい情報が提供されています。

開発#

マッチ関数を作成する際には、IMatchFunction インターフェースを定義する単一の ExecuteAsync メソッドを実装します。このメソッドは通常、高い頻度で呼び出されます(ただし設定で変更可能)。

マッチ関数は、マッチメイキングチケットをマッチ申請に変える役割を果たします。マッチ申請は、グループ化されたプレイヤーのオンラインゲームセッションで使用される、専用サーバーを割り当てるために使用されます。各マッチ関数のアクティブ化には、次のフェーズを通過する必要があります。

1.提出されたマッチメイキングチケットを照会する。 2.自作のカスタムロジックを使用してチケットをグループ化する。 3.自作のカスタムロジックを使用してマッチ申請を生成し、スコア付けして返す。

マッチ関数から出力された申請は、マッチメーカーに対する潜在的なマッチを表します。申請には、1 つのマッチで一緒にプレイする必要があるチケットのリストと、その申請に関するプロパティが含まれています。

  • 申請には Score フィールドがあります。マッチ関数の開発者はこれを使って、その申請の重要度をスコア付けできます。
  • 申請のプロパティには、JSON 文字列フィールドが含まれています。このフィールドには、チケットに割り当てたいその他のデータ(チーム編成や選択したマップなど)を含めることができます。このデータは、マッチメイキングプロセスで処理されるその他のチケットに割り当てることもできます。
note

シナリオが複雑な場合や不正なチケットをエラー処理したい場合は、申請のプロパティ内で ConnectionOverride および AssignmentError フィールドを使用して、割り当て機能をバックエンドでスキップすることができます。

アップロード#

csproj からビルドした関数を zip 圧縮するときは、.zip ファイル内のアセンブリがフラットであり、かつ異なるサブディレクトリにネストされないように注意してください。

詳細については、コマンドラインインターフェースのドキュメントを参照してください。

チケットの照会#

すべてのマッチ関数における最初の手順は、マッチへの参加を待機しているプレイヤーがいるかどうかを確認するためのチケットを照会することです。チケットには開発者定義の属性が含まれます(サービス品質(QoS)の接続品質や、プレイヤー設定(ランク別ゲームとカジュアルゲームのどちらを希望するか)など)。

プールとは、チケットを照会するためのフィルターを生成するものです。プールはマッチ関数設定で指定されます(これについては、マッチメーカー API のドキュメントで説明されています)。プールは FunctionContext パラメーターの Pools プロパティを通じてマッチ関数に提供されます。このプロパティは、マッチ関数でチケットを照会するデフォルトのフィルターセットとして機能します。

クエリには、複数のフィルターを指定できます。返されるチケットのリストは、それらのフィルターに一致するすべてのチケットの交差部分です。

  • 条件を満たすチケットがない場合、マッチ関数は戻って次のアクティブ化まで待機します。
  • チケットが見つかった場合は、カスタムマッチロジックを使用してチケットを申請へとグループ化します。

開発者はマッチ関数の設定ファイルにあるプールを無視して、独自のプールやクエリフィルターのセットを作成することもできます。TimeoutFunction を使用すると、チケットの created 属性(チケットが作成された時刻を示すタイムスタンプ)に基づくフィルターによって、プールが 1 つだけ存在している状態にすることができます。クエリが実行されると、フィルターに一致するすべてのチケットが返され、その時点で、カスタムマッチロジックを使用してチケットをグループ化できるようになります。

note

付属の TimeoutFunction サンプルは、プールがない場合に独自のプールを作成するマッチ関数の例です。

申請のスコア付け#

申請には、その申請の品質や緊急度を評価するための Score プロパティが含まれています。分散実行されたり並列実行されたりするというマッチ関数の性質もあって、チケットは一度に複数の申請に属する可能性があります。そのため、それらの申請のうちどれが実際にマッチになるかを決定するために、スコアが使用されます。まずは、申請内のチケットの待ち時間に基づく指数関数的な優先順位を使用することで、より長時間待機しているプレイヤーが優先されるようにすることを検討してください。

note

付属のサンプルには、待ち時間に対する RMSE(二乗平均平方根誤差)の実装が含まれています。

サンプル関数#

マッチメーカーサービスでは、いくつかのサンプルマッチ関数が提供されています(次のリストで説明しています)。これらの関数は、そのまま使用することもできますし、カスタム関数のベースとして使用することもできます。

  • SimpleFunction - チケットの playercount 属性に基づいてプレイヤーをグループ化します。単一のチケットがプレイヤーのグループを表すことがあるため、この関数はチームのチケットをグループ化することで、チームを申請にグループ化する方法を示す例を提供します。
  • TimeoutFunction - 設定した時間が経過しても割り当てられなかったチケットに AssignmentError を設定します。この関数は、チケットを期限切れにする汎用コードとして使用できます。これにより、チケットが申請へとグループ化されるまでユーザーが無期限に待たされることを回避できます。
  • BackfillFunction - 専用ゲームサーバー(DGS)から直接マッチメイキングするための特殊な関数です。この関数は、接続が失われたまたはセッションの途中に終了したプレイヤーをバックフィルした後、ゲームの実行中に空いた席を新しいプレイヤーで埋めるために使用できます。詳細については、「バックフィル API の呼び出し」を参照してください。
  • TeamQosFunction - チケットに対する許容可能なリージョンを探すための「上位 3つ」の方法を使用して、Multiplay QoS サービスからのサービス品質(QoS)データを使用する方法を示します。この関数には、簡単なチーム機能とパーティ機能も含まれています。

新しい .csproj での関数のビルド#

新しい .csproj で関数をビルドするには、関数サンプルファイルから external ディレクトリを抽出した後、それらのファイルをアセンブリ参照としてプロジェクトに追加します。

<ItemGroup>
<Reference Include="Unity.Services.Matchmaking.Functions.Base, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\external\Unity.Services.Matchmaking.Functions.Base.dll</HintPath>
</Reference>
<Reference Include="Unity.Services.Matchmaking.Functions.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\external\Unity.Services.Matchmaking.Functions.Contracts.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Unity.Services.Matchmaking.Matchmaker.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\external\Unity.Services.Matchmaking.Matchmaker.Contracts.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
note

Newtonsoft.Json を使ってビルドする場合は、関数が正常にロードされることを担保するために、12.0.2 に対してコンパイルをし、<Private>false</Private> を使用してビルド出力から関数を除外するようにしてください。

設定#

設定ファイルは、マッチメイキング関数の管理コンポーネントとなるよう意図されています。設定を使用すると、次のアクションを実行できます。

  • 実行する関数を指定する
  • 関数に渡すすべてのカスタムパラメーターを設定する
  • 関数が動作するチケットのプールを決定する
  • プールのセグメント化を通じてスケーリングや速度の懸念に対処する
  • 結果として生成されるマッチについて Multiplay の専用サーバーを割り当てるフリートとプロフィールを指定する

詳細については、Configs API のドキュメントを参照してください。

ゼロダウンタイムのゲームサーバーのビルド#

"multiplay": {
"Profile": "profileId",
"FleetId": "fleetId",
"Access": "...",
"Secret": "...",
}

profileId は、Multiplay に設定されているサーバー設定 + バイナリです。プロフィールをスワップすることで、インプレースサーバーロールアウトを実行して、マッチの新しいサーバービルド上へのルーティングを開始できます。

カスタム関数の設定#

"config": {
"TeamCount": 20,
"TeamMin": 2,
"TeamMax": 3,
"DefaultRegion": "2402974c-95de-4528-ba0c-d146ba86cd62",
"MapsInRotation": ["dust", "moon", "mountain"]
}

config は完全なカスタム JSON オブジェクトで、各サイクルのアクティブ化時にデシリアライズされ、ご自身のマッチ関数で利用できるようになります。config を使用すると、次のアクションを実行できます。

  • 汎用関数用のフィールドを設定し、より具体的にする(プレイヤー間のスキルの差をどれだけ許容するか)
  • 関数で Boolean スイッチを使って有効化できるルールを変更および有効化する
  • 関数が 1 つをランダムにまたはプレイヤー設定を通じて選択できるように、どのマップがローテーション中であるかを指定する

前述のコードに基づくと、csharp マッチ関数設定 POCO は次のコードスニペットのようになります。

public class PrototypeConfig
{
public int TeamCount { get; set; }
public int TeamMin { get; set; }
public int TeamMax { get; set; }
public string DefaultRegion { get; set; }
public string[] MapsInRotation { get; set; }
}

プラットフォームとイテレーション#

マッチメイキングでは、プラットフォーム分離とサンドボックス化に加え、開発、QA、リリースの母集団を設けるという考え方もサポートされています。特に母集団がゲームサーバーの異なるバージョンのビルドまたはフリートを参照している場合、厳密なフィルターや分離に設定ファイルのプールを使用します。

次の例は、単一のマッチメーカー上でサンドボックス化された分離を実行するためのプロセスを示したものです。

Config A
「developers-playlist-1」、フリート -&gt; 夜間のプッシュを実行している開発フリート
プール - 2 と 2 の間の「ロール」
Config B
「Qa-playlist-1」、フリート -&gt; 安定したゲームを実行しているステージングフリート
プール - 「3 と 3 の間のロール」
Config C
「prod-playlist-1」、フリート -&gt; ライブのゲームビルドを実行している運用フリート
プール - 「1 と 1 の間のロール」

数多くの分離要件を呼び出すシナリオの場合、セグメンテーションを使用して、同じスタイルのターゲティングを実行する自動プールを生成できます。

次の例は、単一のマッチメーカー上でサンドボックス化された分離を実行するためのプロセスをコーディングするもう 1 つの方法を示したものです。

{
"attribute": "role",
"min": 1,
"max": 4,
"segmentation": {
"DistributionType": "Uniform",
"SegmentBehaviorType": "BucketCount",
"Value": 3
}
}

この例のフォーマットでは、[1,2)、[2,3)、および [3,4) の間のロールをターゲットにした、3 つの異なるプールが生成されます。

この戦略は、クロスプラットフォームプレイがない場合のプラットフォーム分離にも使用できます。ただし、多くのシナリオでは、プールフィルターを使ってこのタスクを実行するよりも、分離を処理するための関数ロジックを記述するほうが簡単です。どの方法でこのアクションを実行するかは、その分離シナリオが静的ロジック(プールのフィルター)と動的カスタムロジック(マッチ関数)のどちらに対応しているかによって決まってきます。

セグメンテーションとスケール#

{
"attribute": "skill",
"min": 0,
"max": 5000,
"segmentation": {
"DistributionType": "Normal",
"SegmentBehaviorType": "BucketCount",
"Value": 12
}
}

セグメンテーションは、別個のクエリセグメントが指定された関数の複数同時実行をトリガーするための手段です。バックエンドでは min と max の設定が指定された DistributionType が使用され、その母集団を表す合理的な曲線が生成された後、SegmentBehaviorType と Value により新しい範囲のセットが生成されます。

サポートされている DistributionType 設定は、Uniform と Normal です。

note

Auto および LinearApproximation の DistributionType 設定は、現在サポートされていません。

サポートされている SegmentBehaviorType 設定は、TargetPercentage、BucketSize、BucketCount です。これらの設定では、分布が新しい min 設定と max 設定に分割されます。

たとえば、前述のサンプルコードでは関数が 12 回同時に実行されます。各実行は、プール内の生成済みの静的フィルターを使ってアクティブ化されます。具体的には、次の画像で説明されている min 設定と max 設定を使ってレンダリングされる、スキル属性フィルターが使用されます。

Expansions

関数の実行数を増やす主な利点は、各関数が処理を担うプールのサイズを小さくすることです。このプロセスにより、CPU とメモリの圧迫も減り、処理中のボトルネックが軽減されて、チケットの全母集団がより高速かつ予測どおりに使い切られるようになります。

バックフィル V2#

バックフィル v2 の背景にある基本的なコンセプトは、専用ゲームサーバー(DGS)では、チケットに対するニーズを特殊なチケット(バックフィルチケット)として表すことができるという考え方です。これらのチケットは、マッチ関数によって作成、照会、更新することができます。DGS では、これらのチケットを承認するか、最新の望ましいステートで置き換える必要があります。これについては、以下で説明するいくつかの注意点があります。また DGS では、任意の時点でバックフィルチケットが作成または削除される可能性があります。

このアーキテクチャーの主な目標は、必要以上のサーバーが誤って作成される可能性を排除することです。容量を持つ既存の DGS はすべて、マッチ関数内で表し、確認することができるので、新しいサーバーを割り当てるか、それとも既存のサーバーを活用するかの決定を最も効果的に行うことが可能です。

バックフィルの全般的概要#

バックフィルは、一般的なマッチメイキングのケースとは異なります。典型的なマッチメイキングシナリオでは、その時点で関連付けられていない複数のプレイヤーやサーバーのセットが、各種のカスタムアルゴリズムに基づいてペアリングされます。バックフィルの場合は、この種のマッチがすでに作成されているものの、何らかの理由でプレイヤーが不足したり、割り当て済みプレイヤーの通信が失敗し、それが原因でレンダリングが不完全になっているものと考えることができます。いずれにせよ、主な違いは、バックフィルの場合、マッチをホストするサーバーがすでに選択されており、現在新しいプレイヤーを取得する必要が生じているという点です。

次の画像は、バックフィルの典型的な使用方法を説明したものです。

バックフィルメソッドの例

詳細については、バックフィル API のドキュメントを参照してください。

以下のセクションでは、バックフィルのユースケースをカテゴリ別に説明します。

目標プレイヤー数を維持するためのバックフィル#

サーバーでは、目標とするプレイヤー数を維持するためにバックフィルが使用される場合があります。この状況はさまざまな理由で発生する可能性があります。たとえば、次のようなシナリオが考えられます。

  • マッチ中にドロップアウトしたユーザーを置き換えるため
  • サーバーの活用率を最大限に高めてコストを下げるため
  • 目標の数のプレイヤーが接続されてからマッチが開始されるようにする(たとえば、バトルロイヤルを思い浮かべてみてください。バトルロイヤルでは、まずプレイヤーが待機場所に接続し、すべてのプレイヤーが接続するか、またはバックフィルによって置き換えられた後、マッチが開始されます)

チームのバランスをとるためのバックフィル#

チームベースモードでは、あるチームがもう一方のチームに対して大幅に優勢になると、劣勢のプレイヤーがマッチが終了する前に接続解除することは珍しくありません。しかし、多くのゲームは決められた数や種類のプレイヤーがいなければゲームプレイを公平に続行できなくなるか、場合によってはまったく続行できなくなるよう設計されています。特に、プレイヤーが特定のロールに結び付けられている場合には、その傾向が強くなります(たとえば、クラスベースのゲームモードの場合)。

このシナリオのユースケース例としては、次のイベントが考えられます。

  • チームのバランスを再調整せずに、ドロップアウトするプレイヤーを置き換える
    • ドロップアウトしたプレイヤーをできるだけ早く置き換える
    • 負けているチームからドロップアウトしたプレイヤーをより高スキルなプレイヤーで置き換えることにより、勝っているチームのリードを埋め合わせる
  • チーム間のバランスを再調整してからプレイヤーを置き換える
    • たとえば、8 対 4 とチーム間のバランスが不均衡になっている場合、サーバーは 6 対 6 になるように 2 人のプレイヤーを移動したうえで、両チームが再度定員に達するまでバックフィルリクエストを実行し、対等なスキルのプレイヤーを探すことができます。
  • ゲームを一時停止し、バックフィルが完了するまたはタイムアウトするまで待機する
    • たとえば、特定のペアリング(運転手と射撃手のペアなど)が必要なロールベースのゲームで、必要なロールが満たされないとゲームを正常に(またはまったく)続行できなくなるケース。

バックフィル V2 のライフサイクルの概要#

下の図は、BackfillTicket のフローを説明したものです。

バックフィルメソッドの例

バックフィル v2 では、チケットに次のライフサイクルがあります。

1.作成されます 2.マッチ関数によって確認され、バックフィルチケットに関連付けられます。 3.チケットは、バックフィルチケットが DGS によって承認されるのを待機します 1.チケットがタイムアウトした場合、それらのチケットはマッチメーカープールに戻ります。これは、DGS がさまざまな理由で BackfillTicket を承認しなかった場合に起こります。 2.DGS がバックフィルチケットを却下した場合(バックフィルチケットを削除するか、最新のサーバーステートで更新した場合)も、チケットはマッチメーカーのチケットプールにリリースされます 3.DGS がバックフィルチケットを承認した場合、チケットはその作成時に自身に関連付けられた割り当てデータを受け取り、サーバー接続を与えられて、通常のチケット割り当てフローと同様に割り当てられます。

マッチ関数の例#

次の図は、バックフィルリクエストを通じてチケットを割り当てるためにマッチ関数と DGS が実行するステップを説明したものです。

バックフィルメソッドの例

上記の例では、マッチ関数が実行され、5 人のプレイヤーが利用可能であることと、既存のバックフィルチケットがないことが確認されます。マッチ関数では、「このタイプのチケットを処理する時点では他にサーバーが存在していない」と見なし、5 人のプレイヤーに新しいバックフィルチケットを関連付けることができます。

その時点で、バックフィルチケットを割り当てられた新しいサーバー割り当てリクエストが作成されます。

そのサーバーの起動中、マッチメーカーはマッチメイキングのサイクルをさらにいくつか実行する場合があります。

マッチ関数が再度実行されると、それらの関数は新しいバックフィルチケットと、マッチ関数によって記述されたステートを確認します(このケースの場合は、現在のプレイヤー数と必要なプレイヤー数)。

マッチ関数はその後、そのバックフィルチケットに関連付けられた申請に 10 個の新しいチケットすべてを追加して、そのバックフィルチケットの埋め合わせを優先付けできます。

最後に DGS が起動し、ApproveTicket API を直ちに呼び出します。この API は、バックフィルチケットに関連付けられた 15 のチケットを DGS に割り当てます。ApproveTicket のチケットリクエストは、バックフィルチケットの最新バージョンを DGS に返します。この時点で、DGS はマッチ関数によって記述されたすべてのカスタム属性/プロパティにフルアクセスできるようになります。

マッチ関数は、バックフィルチケットがいっぱいになるまでチケットを追加していくことができます。DGS は、追加チケットのリクエストが満たされたことをバックフィルチケットのステートを通じて確認すると、そのチケットを削除します。また、サーバーはこの時点でプレイヤーを失う可能性があります。その場合、サーバーは既存のバックフィルチケットを更新して新しいサーバーステートを反映するか、バックフィルチケットがまだない場合は、新しいバックフィルチケットを作成します。(以下で説明しています)

DGS はバックフィルチケットを更新する必要がある#

下の図は、DGS が更新 API を通じてバックフィルチケットへの更新を行った場合に、何が起こるかを示したものです。

バックフィルメソッドの例

上記の例では、DGS がチケットを更新して、そのサーバーが 3 人のプレイヤーを失ったことを示し、マッチ関数の次の実行でそのステートが取得され、3 人のプレイヤーが追加された後、数回の DGS 承認を経て、それらの新しいチケットが表示されます。

:マッチ関数は、現在のロードに応じて、.3 ~ 3 秒の間のどこかで実行される可能性があります。通常、処理は 1 秒以内に実行されますが、それゆえに DGS では、チケットを複数回承認しないと、更新されたチケットが確認されない可能性があります。

DGS によるバックフィルチケットの更新頻度はきわめて高い#


下の図は、マッチ関数が自身の確認したバックフィルチケットの最新バージョンを更新しようとしたした際、それと同じタイミングで、DGS が更新 API を通じてバックフィルチケットへの更新を行った場合、何が起こるかを示したものです。

バックフィルメソッドの例

上記の図では、マッチ関数が 5 つのチケットを追加しようとしたのと同時に、DGS が更新を実行して、目標チケット数を 10 から 13 に増やしています。バックフィルチケットは、この種の古い読み取り/書き込みによる競合を検出するようにバージョニングされています。マッチ関数は、チケットのステートに対する権限を持たないため、常に失敗します。

上記の例では、マッチ関数が再試行されますが、再び同じことが起こります。

実際、DGS がマッチ関数の実行速度よりも速くチケットを更新し続けた場合、処理が前進する保証はありません。

caution

このような状況を回避するには、マッチ関数で変更が行われたことが次の承認で確認されるまで(あるいは、少なくともマッチメーカーの最大サイクルである 3 秒以上)、DGS では重要な更新を保留するようにしてください。そうすれば、DGS がチケットを更新している際に、マッチメーカーが古いチケットへの変更を申請し続け、その状態で処理が行き詰まる可能性を排除できます。

申請に加えられた変更#

申請は、BackfillTicket プロパティを使用できるように更新されました。このプロパティをマッチ関数で設定すると、マッチ関数を既存のバックフィルチケットに関連付けたり、新しいバックフィルチケットを作成することができます。

新しい BackfillTicket とサーバー割り当てを作成する#

新しく作成されたバックフィルチケット(バックフィルチケット ID がない BackfillTicket)に BackfillTicket プロパティが設定されている場合、それは新しいサーバー割り当てであると見なされ、バックフィルチケットが作成されて関連付けられます。マッチメーカーでは、次のステップが実行されます。

  • 新しいバックフィルチケットが作成され、その ID がセッションデータに送られて、DGS に付与されます
  • 申請からのチケットがバックフィルチケットに関連付けられます
チケットを既存の BackfillTicket に割り当てる#

既存のバックフィルチケットが照会される場合、その照会はすでに設定された ID を使って実行されます。更新できるのは ID だけです(属性およびプロパティ)。マッチ関数では、チケット ID の追加、プロパティの追加、カウンター(たとえば、「必要なプレイヤー数」)の更新などといったことが行なえます。申請を作成して BackfillTicket プロパティをこのバックフィルチケットに設定すると、マッチメーカーはエバリュエーターを通じて申請を実行し(各バックフィルチケットで申請が 1 つだけ作成されるようにして)、追加のチケットをバックフィルチケットに再度関連付けます。

チケットが関連付けられる(またはリリースされる)方法#

この時点で、新しい(または更新された)バックフィルチケットについては、次のアクションおよび不活動により、チケットが更新されるか、割り当てられるか、元のマッチメイキングプールへとリリースされます。

  • バックフィルサーバーに割り当てられたチケットの最小予約時間が経過すると、すべてのチケットがプールへとリリースされます。これにより、応答のないサーバーによってチケットが永続的にキャプチャされる事態が回避されます。
  • サーバーがバックフィルチケットを受け入れると、すべてのチケットが割り当てられます。
  • サーバーがバックフィルチケットを更新/削除すると、すべてのチケットがリリースされます。

バックフィル V2 API#

コントラクト/ API#

バックフィル V2 では、新しい API とコントラクトが導入されています。詳細については、バックフィル V2 API のドキュメントバックフィル V2 のコントラクトを参照してください。

バックフィル V2 の SDK サポート#

最新バージョンの SDK がダウンロードされていることを確認してください。

ITicketData#

カスタムマッチ関数が受け取る ITicketData インスタンスには、バックフィルチケットを照会するための新しいメソッドがあります。

/// <summary>
/// チケットデータに関する照会のコレクションを実行し、結果のユニオンを返します
/// </summary>
Task<IEnumerable&lt;BackfillTicket>&gt; QueryBackfillTicketsAsync(Query query);

このメソッドは、マッチ関数でチケットを照会するために使用されるバージョンと同じもののように見えますが、返されるのはバックフィルチケットです。

BackfillTicket#

SDK には、新しいバックフィルチケットが含まれています(これはバックフィル API 内の JSON としても出現します)。

public class BackfillTicket
{
public string Id { get; set; }
public long Created { get; set; }
public int RecordVersion { get; set; }
public string Connection { get; set; }
public Dictionary<string, double> Attributes { get; set; }
public Dictionary<string, byte[]> Properties { get; set; }
}

Id、Created、および RecordVersion プロパティは読み取り専用であり、API または GetBackfillTickets メソッドを通じてバックフィルチケットが取得されたときの値のまま維持される必要があります。RecordVersion を設定しようとすると、更新が失敗する可能性があります。その他を更新しようとした場合は、単に無視されます。

属性とプロパティ#

バックフィルチケットは、属性とプロパティに関して、通常のチケットと非常によく似ています。ただし、バックフィルチケットの属性とプロパティは完全にミュータブルです。

バックフィルチケットを作成する際には、通常のマッチメイキングチケットの照会に使用されるフィルターを反映した属性を持たせるように注意してください(これらは深く関連しています)。

バックフィルチケットの属性を記述する際の戦略の例としては、マッチ関数に付与されたフィルターのリストをループ処理し、各フィルターの最小値を反映した属性を作成するというやり方が挙げられます。これはルールではないので、クエリのスコープを必要なだけ広く(または狭く)して、マッチングされる属性をバックフィルチケットに設定するよう注意する必要があります。

申請#

申請クラスには、バックフィルチケット用の新しいフィールドがあります(概要セクションで説明しています)。

public class Proposal
{
...
/// <summary>
/// バックフィルチケットを取得または設定します。
/// </summary>
public BackfillTicket BackfillTicket { get; set; }
...
}

申請の BackfillTicket プロパティを新しい(Id フィールドが null の)バックフィルチケットに設定すると、申請が承認された場合に新しいサーバーが作成されます。BackfillTicket の ID には値が入力され、割り当て時にはその ID が(セッションプロパティを通じて)サーバーに渡されます。

申請の BackfillTicket プロパティを既存のバックフィルチケット(たとえば、QueryBackfillTicketsAsync メソッドを通じて返されるバックフィルチケット)に設定すると、申請が既存のバックフィルチケットに関連付けられます。申請が承認されると、バックフィルチケットは申請内の新しいチケットで更新されます。API の呼び出しに関するセクションを参照してください

MatchProperties#

MatchProperties クラスには、バックフィルチケット ID 用の新しいフィールドがあります。

public class MatchProperties
{
...
// null でない場合は、これがサーバー割り当てに関連付けられたバックフィルチケットの ID になります
public string BackfillTicketId { get; set; }
...
}

DGS は、起動時にセッションデータサービスからセッションデータレコードをフェッチし、マッチ関数によって記述された内容を確認します。バックフィルチケット ID が null でない場合、それは DGS がバックフィルチケットを通じて作成されたことを意味し、現在 DGS によって承認されるのを待っているチケットがあることを意味します。通常、ここでの最善のコースは、バックフィルチケットに対する承認を直ちに呼び出して、それらのチケットのマッチング時間を減らすことです。 詳細については、 DGS からのセッション API の呼び出しに関するセクションを参照してください。

サービス品質#

実行中の Multiplay マッチメイキングのデプロイとともに使用するマッチ関数のセットを開発する際には、マッチ関数での意思決定に使用される入力について考慮してください。考えられる入力の 1 つとしては、利用可能なデータセンターとの関連で見た、クライアントのサービス品質(QoS)があります。

このセクションでは、マッチメイキングクライアントがチケットの提出時に QoS 結果をどのように提供する可能性があるかについて説明します。提供されているマッチメイキングや QoS クライアントのサンプルパッケージをクライアントが使用していることは前提としていませんが、同じ原理の下で運用されているものとします。

note

以下のセクション内の情報は、チケット内の QoS 結果を使用する唯一の方法ではありません。カスタムマッチ関数がここに列挙されていないデータに依存している場合は、チケットを適宜変更することもできます。

このセクションでは、開発者が Multiplay の専用サーバーホスティングを使用している場合を想定しています。そのため、Multiplay 専用サーバーを割り当てたり結合したりするためには、いくつかのデータが必要となります。また、読者は一般的な Multiplay ホスティング用語について理解している必要があります。

QoS API の情報については、Multiplay QoS ユーザーガイドを参照してください。

QoS の概要#

Multiplay でのサーバーの割り当てには、認証済みのクライアントと、いくつかのフィールドの入力が必要です(フリート ID、プロフィール ID、リージョン ID など)。Multiplay QoS プロトコルを使用することで、現在アクティブなリージョンをクライアントが特定できるようなり、ゲームサーバーの割り当てや結合ができるようになります。また、特定のリージョンへの接続品質が良好なプレイヤーをグループ化できるようになります。

マッチ関数への QoS 結果の報告#

QoS 結果を使用してプレイヤーをリージョン別にグループ化し、最適なリージョンからサーバーを動的に割り当てたい場合は、マッチメイキングチケットの送信時に QoS 結果をアタッチする必要があります。

QoS 結果をアタッチする際には、各結果のリージョン ID を含める必要があります(これは Discovery サービスから返されます)。マッチ関数では、マッチへのサーバーを割り当てるにあたってどのリージョンを使用するかを判断する際、Multiplay の割り当て呼び出しのときにクライアントから提供されるリージョン ID を使用できます。

C# の例#

この例では、QoS データが JSON オブジェクトとしてチケットのプロパティディクショナリにアタッチされています。これにより、構造体を JSON にシリアライズすると自然にマッチします。次のコードスニペットは、構造体の例を示したものです(C# で示しています)。

[Serializable]
public class QosTicketInfo
{
public List<QosResultMultiplay> QosResults;
public QosTicketInfo() => QosResults = new List<QosResultMultiplay>();
}
// Multiplay のサーバー割り当てに適合する QoS 結果
[Serializable]
public struct QosResultMultiplay
{
// Multiplay Region ID (from Discovery)
public string Region;
// Latency in milliseconds
public int Latency;
// Packet loss percentage [0.0..1.0]
public float PacketLoss;
}

この例では、QosTicketInfo(チケットにアタッチされる構造体)に QosResultMultiplay のリストが含まれています。これには、Discovery サービスからのリージョン ID と、リージョンごとの待ち時間とパケット損失の平均が含まれます。これらは、Multiplay QoS サービスを通じて QoS チェックを実行することで計算されます。

チケットを作成するときに、QosTicketInfo オブジェクト全体が JSON オブジェクトにシリアライズされ、「タイプの名前」がプロパティ名としてチケットにアタッチされます。たとえば、チケットに QoS データをアタッチするコードは次のコードスニペットのようになる場合があります。

QosTicketInfo results = GetQosResults();
string jsonResults = JsonSerializer(results);
ticket.Properties.Add(nameof(QosTicketInfo).ToLower(), jsonResults);

この例では、ticket は Multiplay マッチメイキングサービス用の作成済みチケットです。JSON オブジェクトは Properties に追加されています。これはチケットととも提出され、マッチ関数に提供されます。その後、開発者は QosTicketInfo プロパティを抽出してデシリアライズし、マッチ関数ロジック内部のオブジェクトに戻すことができます。

許容される QoS の上限の決定#

ほとんどのリアルタイムゲームでは、待ち時間やパケット損失についての上限値が設けられています。その値を超えた場合、ゲームプレイは許容範囲外(ゲームプレイの品質が明らかに落ちているが、プレイできないわけではない)または不可能(プレイヤーが接続を維持できない、またはゲームロジックが正常に機能しない)と見なされます。マッチメイキングに QoS 結果を使用すれば、これらの上限値を超えているリージョンでプレイヤーがゲームをプレイするという状況を回避することができます。

これらの上限を決める際には、接続があまりよくない場合のプレイヤーの体験について考慮すると同時に、接続があまりよくないプレイヤーと一緒にゲームをプレイしているときの他のプレイヤーの体験も考慮してください。たとえば、ゲームエンジンによっては待ち時間が長くパケット損失が多いプレイヤーに比較的よい体験を提供できますが、その他のプレイヤーにとってはそのプレイヤーと一緒にゲームをプレイすることが不可能になる程度にまで、プレイヤーがテレポートしているように見える場合があります。実際問題として、その場合はクライアントによって収集されたリージョンごとの QoS 結果を処理し、許容範囲外のリージョンやプレイ不可能なリージョンを除外する必要があります。

  • QoS 結果をフィルター処理する方法としては、チケットを提出する前にこのアクションを実行する方法(マッチ関数での処理時間を幾分節約できます)と、フィルター処理していない結果をすべて提出した後、マッチ関数の内部でフィルター処理を行う方法があります(こちらのほうがより柔軟に対処できる可能性もあります)。
  • プレイヤーの最善の QoS 結果がそのタイトルで「許容できる」待ち時間/損失の範囲を超えてはいるものの、「どうにかプレイできる」範囲内である場合は、その最善の結果を除くすべての結果を除外するという選択肢も考えられます。そうすれば、プレイヤーは接続品質が良くない場合でも、少なくともマッチを見つけることはできるようになります。そのような場合は、「接続品質」の警告をプレイヤーに表示することで、ゲームプレイに問題があることをあらかじめ伝えることができます。
  • プレイヤーの最善の QoS 結果がプレイ不可能のしきい値を超えている場合、通常は適切なエラーメッセージを表示し、プレイを控えてもらうようにしてください。

QoS に関するベストプラクティス#

Multiplay QoS プロトコルを操作する際には、次のベストプラクティスを考慮するようにしてください。

  • Discovery サービスでは、Multiplay の位置 ID も報告されます。この情報は、現時点では Multiplay のホスト割り当ての生成に必要ありませんが、マッチ関数にとって有用な場合は、QoS 結果に追加することもできます。
  • チケットに含まれるリージョンごとの待ち時間とパケット損失の値は、通常、そのリージョン内で接続される各 QoS サーバーの全レスポンスを平均化または平坦化したものを表す必要があります。結果の加重平均によって直近の結果に比重を置くようにすると、現在のネットワーク品質をより正確に示すことができる可能性があります。
  • パケット損失と待ち時間の兼ね合いを考慮するようにしてください。たとえば、リアルタイムゲームでは、接続の待ち時間を非常に短くした場合、標準的なゲームセッションでは通常の結果を出せたとしても、最小限のパケット損失が生じただけで、ゲームに影響が出る可能性があります。

Multiplay#

セッションの詳細の取得#

セッションサービスでは、マッチが作成されるたびにマッチの詳細が格納されます(設定ファイル、チケットの統計情報、マッチからのプロパティなど)。専用ゲームサーバー(DGS)は、サーバーの起動後にこれらのデータにアクセスすることができます。

セッションサービスとバックフィルサービスには、両方とも認証が必要です。セッションサービスを使用するには、前提条件として、初期オンボーディングのプロセス中にプロジェクト ID を指定し、JWT を Multiplay のプロフィール内に取得する必要があります。DGS では、バックフィルサービスとセッションサービスへの問い合わせを行う際、セッション ID と JWT を含んだ設定ファイルを読み取る必要があります。JWT は、ベアラー認証トークンとして使用されます。

note

Ping サーバーサンプルには、このフローを示すコードが含まれています。これにより、関数サンプルの一部に含まれているシンプルなバックフィル関数が呼び出されます。

必要なセットアップ#

JWT ベースの認証を有効にするには、初回セットアップのプロセス中に、マルチプレイサーバー割り当ての設定の一環として、マッチメーカーのプロジェクト ID(UPID)と Multiplay のアカウントサービス ID(ASID)を指定する必要があります。これらのデータによって、実行時にサーバーに提供される JWT が生成されます。

セッションサービスの JWT と ID を DGS で読み取る#

DGS は、設定ファイルへのパスを起動引数として取得するように設定する必要があります(たとえば、-config <path>)。設定ファイルには、含めたい任意の形式やデータを含めることができますが、設定ファイルの主な目的は、主要な Multiplay トークン値に対する、実行時の置換値を提供することです。

Ping サーバーサンプルの場合、設定ファイルは次の形式になっています(config.json として)。

{
"multiplay_uuid": "$$allocated_uuid$$",
"session_auth": "$$session_auth$$"
}

サーバーが割り当てを受け取ると、その値は実行時に、現在の割り当てに関連付けられている実際の値へと置き換えられます。

{
"multiplay_uuid": "d2adb890-b423-4f08-9a6b-55eff0e79a4d",
"session_auth": "bearer <jwt here>"
}

Ping サーバーサンプルのコードでは、ファイルが定期的にポーリングされ、新しいゲームセッションがいつ作成されたかが検出されます。

セッションサービスの JWT と ID(「multiplay_uuid」)が入力されたら、それらを使ってセッションサービスやバックフィルサービスに対する呼び出しを実行できます。

DGS からのセッション API の呼び出し#

セッション API への呼び出しは通常 DGS によって実行されます。また、multiplay_uuid フィールドと session_auth フィールドを含んだ設定ファイルをすでに処理していることが前提となります。

ベース URL: https://server-session.multiplay.com/v1/

GET /session/{multiplay_uuid}
現在のセッションのために格納されたデータを取得する。
multiplay_uuid:処理された設定ファイルの multiplay_uuid フィールドの値
Authorization:Authorization <bearer JWT>(設定ファイルの session_auth フィールドの JWT)
Response: 200 (MatchProperties)

例:GET https://server-session.multiplay.com/v1/session/d2adb890-b423-4f08-9a6b-55eff0e79a4d

public class MatchProperties
{
// マッチの作成時に使用された拡張
public BackendExpansion Expansion { get; set; }
// マッチ内のチケット
public List<Ticket> Tickets { get; set; }
// マッチ関数によってこのマッチに格納されたカスタムプロパティの JObject 表現
public JObject AssignmentProperties { get; set; }
// マッチの作成時に使用されたジェネレーター
public string GeneratorName { get; set; }
// マッチの作成時に使用された関数の名前
public string FunctionName { get; set; }
// null でない場合は、これがサーバー割り当てに関連付けられたバックフィルチケットの ID になります
public string BackfillTicketId { get; set; }
}

MatchProperties のコントラクトの追加の依存関係:

public class BackendExpansion
{
public TargetFunction Target { get; set; }
public JObject Config { get; set; }
public Dictionary<string, List&lt;Filter>> Pools { get; set; }
}
public class TargetFunction
{
public string Name { get; set; }
public string Version { get; set; }
public FunctionKind Kind { get; set; }
public int Port { get; set; }
}
public enum FunctionKind
{
None,
Rest,
Grpc,
Memory
}
public class Filter
{
public string Attribute { get; set; }
public double Max { get; set; }
public double Min { get; set; }
}
public class Ticket
{
public string Id { get; set; }
public Assignment Assignment { get; set; }
public IDictionary<string, double> Attributes { get; set; }
public long Created {get;set;}
public Dictionary<string, byte[]> Properties { get; set; }
}
public class Assignment
{
public string Connection { get; set; }
public string Error { get; set; }
public JObject Properties { get; set; }
}

DGS からのバックフィル API の呼び出し#

バックフィルエンドポイントでは、セッションサービスによって要求される認証と同じ認証が使用されます(つまり、「セッションサービスの ID と JWT の読み取り」セクションで説明されている、session_auth フィールドからの JWT)。

バックフィルエンドポイントでは、セッションサービスに格納されたマッチ設定のサブセットが取得されます。これは、セッションサービスから取得されたデータの場合もあれば、カスタムロジックに基づいて DGS によって作成されたデータの場合もあります。ただし、バックフィル API によって定義された拡張可能コントラクトに準拠していることが前提となります。

API の詳細とコントラクトについては、バックフィル API のドキュメントを参照してください。