4. マッチメイク

4.1. マッチメイクとは

マッチメイクとは、ユーザーがインターネット上で一緒に通信プレイをする相手を見つける、マルチプレイ仲介機能のことです。

NEXでは、ユーザーのグループをギャザリングと呼び、マッチメイクにより引き合わされ、一緒に通信プレイを行っているユーザーのギャザリングをマッチメイクセッションと呼びます。 P2P セッションとは独立した概念ですのでご注意ください。

マッチメイク時は、アプリケーションが指定した条件に見合ったマッチメイクセッションを検索して参加することが可能です。 例えば、フレンドが作成したマッチメイクセッションの検索や、アプリケーションが独自に指定した属性値を持つマッチメイクセッションの検索などができます。 また、相手を指定してマッチメイクセッションを検索することも可能です。

アプリケーションの用途に応じて一人のユーザーが複数のマッチメイクセッションを作成したり、参加したりすることができます。 例えば一つのマッチメイクセッションを P2P 通信のセッション管理に利用し、もう一つのマッチメイクセッションでチームの管理を行い、さらにもう一つのマッチメイクセッションでフレンドとのメッセージングに利用するといった使い方ができます。 特にチームを組むといった用途においては「 4.10. 複数のユーザーをまとめてマッチメイクセッションに参加させる 」を参照してください。

NEXでは、マッチメイクセッションの所有者のことをオーナー、 P2P 通信で他の端末から参加を受け付ける代表端末のことをホストと呼びます。

マッチメイクセッションより広いグルーピングを行いたい場合は永続ギャザリング( 4.18. )を利用できます。 マッチメイクセッションの場合はユーザーがサーバーからログアウトしたときにマッチメイクセッションから自動的に退出しますが、永続ギャザリングの場合は参加したままにすることができます。

マッチメイクにはゲームサーバーを介した push 型のイベント通知や、インスタントメッセージング、公平な試合結果記録機能なども備わっています。

4.2. マッチメイク全体の流れ

マッチメイク全体の流れは以下のようになります。

  1. ゲームサーバーにログインする
  2. サービスクライアントを初期化する( 4.3.
  3. マッチメイクセッションを登録する。またはマッチメイクセッションを検索して参加する( 4.4., 4.5., 4.6.
  4. マッチメイクセッション参加者同士で相互にコネクションを確立する(詳細は「NEX P2P マニュアル」を参照してください)
  5. マッチメイクセッションから退出する。不要であれば削除する( 4.7., 4.15.7.
  6. サービスクライアントの終了処理を行い、ゲームサーバーからログアウトする。または (3) に戻る。

4.3. サービスクライアントの初期化・終了

NEX では、ゲームサーバーが提供するマッチメイク・ランキング・インスタントメッセージなどの各種サービスに対応したサービスクライアントの API を介してゲームサーバーと通信を行います。 各サービスクライアントの初期化には、有効な Credential オブジェクトのポインタを ServiceClient::Bind() でセットする必要があります。 マッチメイクで使用するサービスクライアントは ServiceClient クラスを継承する以下の 3 つのクラスです。

  • MatchmakeExtensionClient : マッチメイクセッションの登録・検索・参加などを行います。
  • NATTraversalClient : NATトラバーサルを行います。初期化・終了以外はライブラリ内で自動的に使用されます。
  • MessagingClient : インスタントメッセージの送受信を行います。インスタントメッセージを使用しない場合は不要です。

Credentials オブジェクトはゲームサーバーにログインしたあと、NgsFacade::GetCredentials() で取得できます。 ゲームサーバーからログアウトする前に、ServiceClient オブジェクトを破棄してください ( Code 4.1 )。

Code 4.1 サービスクライアントの初期化・終了の例
// ゲームサーバーにログイン
LoginGameServer(pNgsFacade);
Credentials* pCredentials = pNgsFacade->GetCredentials();

// ログインの都度 ServiceClient を生成する。
MatchmakeExtensionClient* pClient = qNew MatchmakeExtensionClient();
pClient->Bind(pCredentials);

// 処理を行う
pClient->DoSomething();

// ログアウト前にオブジェクトを破棄
qDelete pClient;

// ログアウト
LogoutGameServer(pNgsFacade);

4.4. マッチメイクセッションを登録する

マッチメイクセッションをゲームサーバーに登録するには、 MatchmakeExtensionClient::CreateMatchmakeSession()を使用します( Code 4.2 )。 CreateMatchmakeSession()を実行したクライアントは自動的にそのマッチメイクセッションのオーナーおよび P2P 通信のホストとして参加します。 マッチメイクセッションのオーナーでない端末が、 P2P 通信のホストとなるには、 MatchMakingClient::UpdateSessionHost()を呼ぶ必要があります。

マッチメイクセッション登録完了後、 P2P セッションを作成して他の参加者がセッションに参加するのを待ち受けます。 詳細は「NEX P2P マニュアル」を参照してください

Code 4.2 マッチメイクセッションの設定・登録の例
static const qUnsignedInt32 s_gameMode = 1;
static const qUnsignedInt16 s_MaxParticipations = 10;

MatchmakeExtensionClient matchmakeClient;

void ExecuteCreateMatchmakeSession()
{
    // マッチメイクセッションの条件設定
    MatchmakeSession matchmakeSession;
    matchmakeSession.SetGameMode(s_gameMode);
    matchmakeSession.SetAttribute(1, 3);
    matchmakeSession.SetMatchmakeSystemType(MATCHMAKE_SYSTEM_TYPE_ANYBODY);
    matchmakeSession.SetMaxParticipants(s_MaxParticipations);
    matchmakeSession.SetFlag(GatheringFlags::MigrateOwner);

    qVector<qUnsignedInt8> applicationBuffer;
    applicationBuffer.push_back(1);
    matchmakeSession.SetApplicationBuffer(applicationBuffer);
    matchmakeSession.SetDescription(NEX_T("Description"));
    matchmakeSession.SetOpenParticipation(false);

    CreateMatchmakeSessionParam createMatchmakeSessionParam;
    createMatchmakeSessionParam.SetSourceMatchmakeSession(matchmakeSession);
    createMatchmakeSessionParam.SetJoinMessage(NEX_T("create matchmake session"));
    ProtocolCallContext oContext;

    // マッチメイクセッションをサーバーに登録し、参加する
    if (!matchmakeClient.CreateMatchmakeSession(&oContext, createMatchmakeSessionParam, &matchmakeClient))
    {
        // エラー処理
        return;
    }
    // 非同期処理の結果を待つ
    oContext.Wait();
    if (oContext.GetState()!=CallContext::CallSuccess)
    {
        // エラー処理
    }
}

4.6. マッチメイクセッションに参加する

マッチメイクセッションに参加するには、MatchmakeExtensionClient::JoinMatchmakeSession() を使用します。

リクエストを受け取ったゲームサーバーは、以下の内容を確認し、問題ない場合にのみ参加処理を継続します。

  • マッチメイクセッションの参加人数が最大参加人数を超えていないこと

  • マッチメイクセッションが締め切られていないこと

  • マッチメイクセッションの他の参加者が自分をブロックリストに登録していないこと

    • MatchmakeBlockListParam を使用することで、他の参加者が自分をブロックリストに登録していてもマッチメイクセッションに参加することができます
  • 自分がマッチメイクセッションの他の参加者をブロックリストに登録していないこと

    • MatchmakeBlockListParam を使用することで、自分が他の参加者をブロックリストに登録していてもマッチメイクセッションに参加することができます

マッチメイクセッション参加完了後、 P2P 通信を行うためには、別途 P2P セッションに参加する必要があります。 詳細は「NEX P2P マニュアル」を参照してください。

4.7. マッチメイクセッションから退出する

マッチメイクセッションから退出するには、MatchMakingClient::EndParticipation() を使用します。 この関数には、 ProtocolCallContext オブジェクトへのポインタ、退出するマッチメイクセッションのID, クライアントの詳細情報を渡します。

マッチメイクセッションのオーナーが退出もしくはログアウトすると、デフォルトではそのマッチメイクセッションは削除されます。 ギャザリングフラグに GatheringFlags::MigrateOwner を指定した場合は削除されずに他の参加者へオーナー権限が移譲されます。 ただし、他に参加者がいない場合はマッチメイクセッションは削除されます。

注意

P2P セッションに参加している場合、マッチメイクセッションから退出するだけでなく、必要に応じて P2P セッションからも退出する必要があります。 詳細は「NEX P2P マニュアル」を参照してください。

4.8. オートマッチメイク機能を利用する

オートマッチメイク機能を利用すると、検索条件に一致するマッチメイクセッションが存在すれば参加し、存在しなければその条件でマッチメイクセッションを登録することができます。検索条件はリストで複数指定することが可能で、 ゲームサーバーは受け取った検索条件のリストの最初から順番に検索を行います(参加できるセッションが見つかった段階で検索は打ち切られます)。 また、参加者からブロックリストに登録されている場合であっても、 参加者をブロックリストに登録している場合であっても参加してしまうことはありません。 ブロックリストをチェックするかどうかは MatchmakeBlockListParam を使用することで変更できます。

オートマッチメイク機能を利用するには、MatchmakeExtensionClient::AutoMatchmake() を使用します。 MatchmakeSession への条件設定については、「 4.11. マッチメイクセッションの条件設定 」を、MatchmakeSessionSearchCriteria については、「 4.12. 検索条件・検索範囲の設定 」を参照してください。

検索条件に一致するマッチメイクセッションが複数存在する場合、ゲームサーバーは MatchmakeSessionSearchCriteria::SetSelectionMethod() 指定した選び方に基づいて参加するマッチメイクセッションを決定します。デフォルトはランダムです。 MatchmakeSessionSearchCriteria::SetSelectionMethod() では MatchmakeSelectionMethod の値を指定できます。 MatchmakeSelectionMethod については、「 4.8.1. マッチメイクセッションの選び方 」を参照してください。

非同期処理に成功すると、引数で渡した MathmakeSession オブジェクトに参加または登録したマッチメイクセッションの情報がセットされます。 新規でマッチメイクセッションを登録したのか、既存のマッチメイクセッションに参加したのかは、Gathering::GetOwnerPID() 関数の返り値が自分のプリンシパルIDと等しいかどうかで判別できます。

4.8.1. マッチメイクセッションの選び方

オートマッチメイク機能を利用するとき、検索条件に一致するマッチメイクセッションが複数存在する場合、ゲームサーバーは MatchmakeSessionSearchCriteria::SetSelectionMethod() で指定した選び方に基づいて参加するマッチメイクセッションを決定します。デフォルトはランダムです。 MatchmakeSessionSearchCriteria::SetSelectionMethod() では MatchmakeSelectionMethod の値を指定できます。 検索条件に一致するマッチメイクセッションが50件を超える場合は、任意の50件に対して MatchmakeSessionSearchCriteria::SetSelectionMethod() で指定した選び方が適用されます。

MATCHMAKE_SELECTION_METHOD_RANDOM を指定した場合、ランダムに選択します。

MATCHMAKE_SELECTION_METHOD_NEAREST_NEIGHBOR を指定した場合、マッチメイクセッションの属性値が MatchmakeExtensionClient::AutoMatchmake() の引数に与えた MatchmakeSession オブジェクトの属性値に最も近いマッチメイクセッションを選びます。 属性値は、インデックス MATCHMAKE_SELECTION_METHOD_NEAREST_NEIGHBOR の属性値が使用されます。 属性値が同じマッチメイクセッションが複数存在する場合、選ばれるマッチメイクセッションはギャザリングID順となります。 検索条件に一致するマッチメイクセッションが50件を超える場合は、必ずしも属性値が最も近いマッチメイクセッションが選択されるとは限りません。

MATCHMAKE_SELECTION_METHOD_BROADEN_RANGE を指定した場合、レンジ拡大形式でマッチメイクセッションを選びます。 レンジとは、連続する整数を任意の閾値で何段階かに区切ったとき、その区切られた一区間のことをいいます。 デフォルトでは、ELOレーティングを使うことを想定して、0から10000までの整数が、0~1100、1101~1240、1241~1400、1401~1520、1521~1620、1621~1720、1721~1815、1816~1925、1926~2040、2041~2180、2181~2300、2301~10000の12段階に区切られており、区切られた各区間がレンジとなります。 レンジ拡大形式では、検索条件に合致するマッチメイクセッションの中から、マッチメイクセッションの属性値が MatchmakeExtensionClient::AutoMatchmake() の引数に与えた MatchmakeSession オブジェクトの属性値と同じレンジにあるマッチメイクセッションをランダムで選びます。 属性値は、インデックス MATCHMAKE_SESSION_BROADEN_RANGE_ATTRIBUTE_INDEX の属性値が使用されます。 同じレンジにマッチメイクセッションが見つからなかった場合は、現在のレンジに隣接する任意のレンジまで範囲を拡大し、拡大後の範囲内にある検索条件に合致するマッチメイクセッションの中から、インデックス MATCHMAKE_SESSION_BROADEN_RANGE_ATTRIBUTE_INDEX の属性値が最も近いマッチメイクセッションを選びます。 デフォルトでは、現在のレンジに隣接する上下2レンジまで範囲を拡大します。 先の例では、例えば1401~1520のレンジでマッチメイクセッションを探して見つからなかった場合、1101~1720まで範囲を拡大します。 この機能は、例えばレーティングスコアによってランク付けがされるようなゲームの場合、インデックス MATCHMAKE_SESSION_BROADEN_RANGE_ATTRIBUTE_INDEX の属性値をレーティングスコアに、 レンジをランクに対応させることで、近いランクの人とのマッチメイクが可能になります。 WebAPI のドキュメント「 12.2.2. レンジ拡大形式オートマッチメイク機能向け API 」も併せてご覧ください。

MATCHMAKE_SELECTION_METHOD_PROGRESS_SCORE を指定した場合、検索条件に合致するマッチメイクセッションの中から、MatchmakeSession::SetProgressScore() もしくは MatchmakeExtensionClient::UpdateProgressScore() で設定したゲーム進行度が最も大きいマッチメイクセッションを選びます。 例えば、対戦が開始されても引き続き新たな参加者を受け付けるマッチメイクセッションにおいて、セッションの新たな参加者は次の対戦から参加することができる場合に、この機能は有用です。 このとき、対戦が開始して間もないセッションに参加するよりも、対戦が終了しそうなセッションに参加した方が、次の対戦までの待ち時間が少なくて済みます。 これの実現には、対戦の開始時にはセッションのゲーム進行度を 0 に、対戦が進むにつれ MatchmakeExtensionClient::UpdateProgressScore() で定期的にゲーム進行度を上げていきます。 なお、 MatchmakeExtensionClient::UpdateProgressScore() を頻繁に呼ぶとサーバーに負荷がかかります。 最小アップデート間隔 UPDATE_PROGRESS_SCORE_MINIMUM_INTERVAL_TIME 秒以上の間隔でアップデートするようにしてください。

MATCHMAKE_SELECTION_METHOD_BROADEN_RANGE_WITH_PROGRESS_SCORE を指定した場合、レンジ拡大形式とゲーム進行度を組み合わせてマッチメイクセッションを選択します。 検索条件に合致するマッチメイクセッションの中から、マッチメイクセッションの属性値が MatchmakeExtensionClient::AutoMatchmake() の引数に与えた MatchmakeSession オブジェクトの属性値と同じレンジにあるマッチメイクセッションのうち、ゲーム進行度が最も大きいマッチメイクセッションを選びます。 属性値は、インデックス MATCHMAKE_SESSION_BROADEN_RANGE_ATTRIBUTE_INDEX の属性値が使用されます。 同じレンジにマッチメイクセッションが見つからなかった場合は、現在のレンジに隣接するレンジまで範囲を拡大し、拡大後の範囲内にある検索条件に合致するマッチメイクセッションの中から、ゲーム進行度が最も大きいマッチメイクセッションを選びます。

MATCHMAKE_SELECTION_METHOD_SCORE_BASED を指定した場合、様々なパラメータをスコア付けして最適なマッチメイクセッションを選択します。 あらかじめ各種パラメータの値に対するスコアをスコア換算設定に定義しておくことにより、検索条件に合致するマッチメイクセッションのパラメータと自身のパラメータと比較することでそのトータルスコアがもっとも高いマッチメイクセッションを選びます。 使用できるパラメータは以下の通りです。

  • レーティング値の差
  • 切断率の差
  • 違反率の差
  • インターネット経路を考慮した距離
  • 国の一致
  • 距離情報が無いときの国が一致
  • マッチメイクセッションの存続時間
  • マッチメイクセッションの進行度

スコア換算設定は設定インデックスを切り替えることで複数種類使用することができます。 例えばゲームモードによって設定インデックスを切り替えてスコアの割り振りを変化させることができます。 MatchmakeSessionSearchCriteria を複数指定するときに一番目の条件には厳しめのスコア換算設定を用い、 二番目の条件には緩めのスコア換算設定を用いることでより適したマッチメイクセッションを探すこともできます。 本機能を利用するには MatchmakeParam を引数に取る MatchmakeSessionSearchCriteria::SetSelectionMethod()のオーバーロード関数を利用する必要があります。 また、AutoMatchmakeParam::SetSourceMatchmakeSession に指定する MatchmakeSession オブジェクトにも MatchmakeSession::SetMatchmakeParam() を使用して、MatchmakeSessionSearchCriteria::SetSelectionMethod() に指定したものと同じ MatchmakeParam オブジェクトを指定する必要があります。 自身のレーティング値、切断率、違反率、国といった情報はアプリケーションがMatchmakeParam に対してセットする必要があります。 MatchmakeExtensionClient::AutoMatchmake() で参加したマッチメイクセッションに含まれる MatchmakeParam からはトータルスコアを取得できます。 ただし、自分自身がオーナーとなった場合はマッチしなかったことを意味するためトータルスコアを取得できません。 アプリケーションでレーティング値、切断率、違反率を算出する場合は MatchmakeReferee の利用をご検討ください。 WebAPI のドキュメント「 12.2.3. スコア付けによるオートマッチメイク機能向け API 」も併せてご覧ください。

Code 4.5 MATCHMAKE_SELECTION_METHOD_SCORE_BASED の利用法
MatchmakeParam param;
param.SetSettingIndex(0);
param.SetRatingValue(1500);

MatchmakeSessionSearchCriteria criteria;
criteria.SetMatchmakeSystemType(MATCHMAKE_SYSTEM_TYPE_ANYBODY);
criteria.SetSelectionMethod(MATCHMAKE_SELECTION_METHOD_SCORE_BASED, param);
qList<MatchmakeSessionSearchCriteria> criteriaList;
criteriaList.push_back(criteria);

MatchmakeSession matchmakeSession;
matchmakeSession.SetGameMode(0);
matchmakeSession.SetMatchmakeSystemType(MATCHMAKE_SYSTEM_TYPE_ANYBODY);
matchmakeSession.SetMaxParticipants(4);
matchmakeSession.SetMatchmakeParam(param);

AutoMatchmakeParam autoMachmakeParam;
autoMachmakeParam.SetSearchCriteria(criteria);
autoMachmakeParam.SetSourceMatchmakeSession(matchmakeSession);

ProtocolCallContext oContext;
matchmakeExtensionClient.AutoMatchmake(&oContext, autoMachmakeParam, &matchmakeSession);

4.8.2. オートマッチメイク時の新規マッチメイクセッションの作成遅延

P2Pネットワーク全体の NAT トラバーサルの成功率を高めるために、オートマッチメイクは、 NAT タイプが EDM の端末や失敗率が高い端末からの呼び出し時、検索条件のマッチメイクセッションが存在しない場合に新規マッチメイクセッションを作成するタイミングを最大1秒間遅延させます。この間、マッチメイクセッションの検索を繰り返し行い、遅延している間はクライアントへ応答しません。 マッチメイクセッション作成を遅延させることにより、該当端末が他のマッチメイクセッションに参加する確率を高め、マッチメイクのホストになりにくくします。 遅延時間は、デフォルト1秒です。デフォルト1秒より伸ばす設定を希望する場合は、お問い合わせください。

新規作成遅延の発生条件は以下の通りです。

  • NAT タイプが EDM 、もしくは、 NAT トラバーサル失敗率が 80% 以上と集計されたグローバル IP アドレスを持つ端末からのアクセスであった

グローバル IP アドレスごとのNATタイプの判定結果や NAT トラバーサル成否は、端末のログアウトもしくは切断検出時にゲームサーバーで記録され、NEX3.5 以降のゲームサーバー間で共有されます。有効期限は、最終報告から1週間です。

MatchmakeSession::SetOption0Flag() で、 MatchmakeFlags::MatchmakeSessionOption0 の MatchmakeFlags::MATCHMAKE_SESSION_OPTION0_FORCE_AUTOMATCH_DELAY を指定することで、強制的に遅延させることができます。 デフォルト1秒より伸ばす設定を行った場合は、必ずこのフラグをONにしてデバッグを行ってください。

4.9. アプリケーションで P2P セッションへの参加受付を判断する場合の注意事項

ゲームサーバー上でマッチメイクセッションがオープンになっているときは任意のタイミングでそのマッチメイクセッションに関連付いた P2P セッションへ参加要求が来る可能性があります。 特に、オートマッチメイク機能を使用した場合は、ゲームサーバー上で参加可能なマッチメイクセッション上位10件の中からランダムで参加するものを選ぶため、同時に同じマッチメイクセッションに参加する可能性が高くなります。

そのため、アプリケーションでは短い間隔での参加要求を受け付けられるようにし、P2P セッションへの参加要求は極力アプリケーション判断で拒否しない仕様にしてください。 (締切タイミングのずれへの対処のための参加人数の超過チェックを除く)

P2P セッションに参加する段階での拒否はマッチメイク成立効率を下げる原因となります。

長時間参加できない状況が続くときは、 MatchmakeExtensionClient::CloseParticipation()、MatchmakeExtensionClient::OpenParticipation()を実行するようにしてください

4.10. 複数のユーザーをまとめてマッチメイクセッションに参加させる

あらかじめフレンドや知り合い同士で集まったり、条件の合う人を募集したりした後、その人たちとチームを組んで一緒に対戦したり協力プレイしたいといったケースがあります。 複数のユーザーをまとめてマッチメイクセッションに参加させることで、そういった機能を実現できます。

複数のユーザーをまとめて参加させるには MatchmakeExtensionClient::AutoMatchmake()、MatchmakeExtensionClient::CreateMatchmakeSession()、MatchmakeExtensionClient::JoinMatchmakeSession() の各 API のうち AutoMatchmakeParamCreateMatchmakeSessionParamJoinMatchmakeSessionParam の各オブジェクトを引数に取るオーバーロード関数を使用します。 例えば JoinMatchmakeSessionParam::SetAdditionalParticipant() に参加させたいプリンシパル ID を指定すると、自分自身とそのプリンシパル ID のユーザーをアトミックにマッチメイクセッションに参加させます。 結果は全員参加に成功するか、何らかのエラーが発生して全員参加に失敗するかのどちらかとなります。 オプションとして JoinMatchmakeSessionParam::SetGatheringIdForParticipationCheck() を使用すると参加者全員が特定のギャザリングに参加していることを確認できます。 JoinMatchmakeSessionParam::SetJoinMatchmakeSessionBehavior() に JOIN_MATCHMAKE_SESSION_BEHAVIOR_IM_ALREADY_JOINED を指定すると自分自身が参加済みのマッチメイクセッションに JoinMatchmakeSessionParam::SetAdditionalParticipant() で指定した他のユーザーを参加させることができます。 追加で参加させたユーザーに対しては NotificationEvents::AddedToGathering が通知されます。 他のユーザーによってマッチメイクに参加させられたユーザーがマッチメイクの情報を取得する場合、 MatchmakeExtensionClient::FindMatchmakeSessionByGatheringIdDetail() を使用します。この API は MatchmakeExtensionClient::FindMatchmakeSessionByGatheringId() で取得できる情報に加え、P2P 通信用の共通鍵も取得します。

4.11. マッチメイクセッションの条件設定

MatchmakeSession クラスのオブジェクトにパラメータ(他のクライアントから検索される際の条件値や付加情報)を設定します。 また、MatchmakeSession::Reset() で設定をリセットすることができます。

Table 4.1 に設定可能なパラメータの一覧を示します。

Table 4.1 マッチメイクセッションに指定できる項目
項目名 概要 初期値 設定関数
ゲームモード ランクマッチ、フリー対戦、COOPモードなど、マッチメイクのモードを表す数値です。ゲームが任意の値を使用可能です。この値で対象のマッチメイクセッションを絞り込めるとサーバーでの検索パフォーマンスが向上します。そのためできる限りゲームモードを細分化して設定することを推奨します。 0 SetGameMode()
マッチメイクシステムタイプ マッチメイクの種類(誰でも参加可能/マッチメイクセッションのオーナーがフレンドの場合のみ参加可能)です。MATCHMAKE_SYSTEM_TYPE_ANYBODY, MATCHMAKE_SYSTEM_TYPE_FRIEND がサポートされます。MATCHMAKE_SYSTEM_TYPE_FRIENDを指定している場合、オーナーがゲームサーバーからログアウトした時点でマッチメイクセッションへの参加が自動的に締め切られます。ただし、SetMatchmakeSystemType() の第二引数にFriendOnlyParticipationPolicyArgument::WithoutClose を指定した場合は自動的に締め切られなくなります。 MATCHMAKE_SYSTEM_TYPE_INVALID SetMatchmakeSystemType()
属性 ラウンド数、対戦目的、レベルなど、ゲームが独自に検索条件として使用できる属性値で、最大6個までの指定が可能です。 全て 0 SetAttribute()
最大参加人数 マッチメイクセッションの最大参加人数です。マッチメイクセッションの参加者が最大参加人数を超える場合、そのマッチメイクセッションへの参加はできなくなります。 0 SetMaxParticipants()
ギャザリングフラグ ギャザリングの振る舞いを指定するフラグです。例えば、マッチメイクセッションのオーナーがマッチメイクセッション退出時(NGSからのログアウト時・EndParticipation実行時)にオーナーマイグレーションを自動で行うかを指定する GatheringFlags::MigrateOwner といったフラグを設定可能です。 0x0 SetFlag(), ClearFlag()
アプリケーション定義データ アプリケーションが自由に利用できるデータです。たとえばマッチメイクセッションの状態など、検索条件に含める必要のないデータを設定できます(最大サイズ 512 byte)。 NULL SetApplicationBuffer()
説明文字列 マッチメイクセッションの説明文字列です。アプリケーションが任意の値を設定できます(最大サイズ 256 文字)。 NEX_T(“”) SetDescription()
募集状態フラグ マッチメイクセッション登録時点の募集状態を指定します。true の場合は待ち受け状態で登録され、false の場合は締め切り状態で登録されます。 True SetOpenParticipation()
ゲーム進行度 マッチメイクセッションの選び方を MATCHMAKE_SELECTION_METHOD_PROGRESS_SCORE などにした場合に利用されるアプリケーション定義値です。 MAX_PROGRESS_SCORE SetProgressScore()
オプション MatchmakeFlags::MatchmakeSessionOption0 で列挙される、マッチメイクセッションに付加するオプションを指定します。 MATCHMAKE_SESSION_OPTION0_NONE SetOption0Flag()
マッチメイクパラメータ マッチメイクセッションの選び方を MATCHMAKE_SELECTION_METHOD_SCORE_BASED にした場合などに利用される、マッチメイクセッションに付加するパラメータを指定します。 MatchmakeParam() SetMatchmakeParam()
ユーザーパスワード マッチメイクセッションに参加する際に必要となるパスワードを設定します。最大 MAX_MATCHMAKE_SESSION_USER_PASSWORD_LENGTH (32) 文字指定できます。空文字列を指定した場合はパスワードを設定しません。 NEX_T(“”) SetUserPassword()
関連する永続ギャザリングの ID マッチメイクセッションに関連付ける永続ギャザリングの ID を指定します。永続ギャザリングに関連付けられたマッチメイクセッションの数は PersistentGathering::GetMatchmakeSessionCount() で取得できます。INVALID_GATHERINGID を指定すると関連付けは行われません。 INVALID_GATHERINGID SetReferPersistentGatheringId()
あいことば あいことばを指定します。空文字列を指定することにより、あいことばを利用しない設定にすることができます。 NEX_T(“”) SetCodeword()

補足

マッチメイクセッションを待ち受け状態で登録した場合、自分が P2P 通信を開始する前に他のクライアントから通信を開始しようとすることもありえます。マッチメイクセッション参加後に P2P 通信に移行するようなアプリケーションの場合、締め切り状態で登録して P2P 通信の準備が整った後で待ち受け状態に変更するような実装を推奨します。

補足

Table 4.1 の設定関数と、設定した条件をリセットする MatchmakeSession::Reset() 以外の設定関数を使用したり、MatchmakeSession クラスおよび親クラスである Gathering クラスのクラス変数を直接変更したりしないでください。そのような場合の動作は保証されません。

4.12. 検索条件・検索範囲の設定

検索条件は、MatchmakeSessionSearchCriteria クラスオブジェクトに設定します。 Table 4.2MatchmakeSessionSearchCriteria に設定可能な検索条件を示します。 設定した検索条件は全て AND で結合されます。また、MatchmakeSessionSearchCriteria::Reset() で初期値に戻すことができます。

Table 4.2 検索条件に指定できる項目
項目名 概要 初期値 設定関数
ゲームモード ゲームの種類を表す数値です。指定は必須です。 なし SetGameMode()
マッチメイクシステムタイプ マッチメイクの種類(誰でも参加可能/マッチメイクセッションのオーナーがフレンドの場合のみ参加可能)です。MATCHMAKE_SYSTEM_TYPE_FRIEND を指定している場合、サーバー上でオーナーが自分のフレンドかどうかをチェックします。指定は必須です。 なし SetMatchmakeSystemType()
属性 ゲームが独自に検索条件として使用できる属性値で、最大6個までの指定が可能です。指定しなかった場合は任意の値にマッチするようになります。属性値はインデックス順に関係なく一括評価されます。SetAttributeWithRange() では属性値を範囲指定することが可能です。 任意の値 SetAttribute(),SetAttributeWithRange()
最大参加人数 最大参加人数を指定します。指定しなかった場合、任意の値にマッチするようになります。 任意の値 SetMaxParticipants(),SetMaxParticipantsWithRange()
満員除外フラグ マッチメイクセッションが満員になっているものを除外するかを指定します。 False SetVacantOnly()
ホスト未決定除外フラグ P2P 通信のホストが決定していないものを除外するかを指定します。 True SetExcludeNonHostPid()
締め切り除外フラグ 参加が締め切られているものを除外するかを指定します。 True SetExcludeLocked()
ユーザーパスワード設定除外フラグ マッチメイクセッションユーザーパスワードが設定されたものを除外するかを設定します。 True SetExcludeUserPasswordSet()
マッチメイクセッションの選び方 検索条件を満たすマッチメイクセッションが複数存在した場合に、参加するマッチメイクセッションを選ぶ方法を指定します(オートマッチメイク時のみ利用)。 MATCHMAKE_SELECTION_METHOD_RANDOM SetSelectionMethod()
関連する永続ギャザリングの ID 指定した永続ギャザリングに関連するマッチメイクセッションが検索されます。INVALID_GATHERING を指定すると任意の値にマッチするようになります。 INVALID_GATHERING SetReferPersistentGatheringId()
あいことば あいことばを指定します。空文字列を指定することにより、あいことばを利用しない設定にすることができます。 NEX_T(“”) SetCodeword()

検索範囲を指定するには、ResultRange クラスオブジェクトにオフセットと検索結果の最大サイズを指定します。 デフォルトのオフセット値は 0 、取得リストの最大サイズは 100 です。取得できるリストの並びはマッチメイクセッションのidの昇順になりますが、オフセットに RESULTRANGE_ANY_OFFSET を指定すると、検索条件に合致するデータをサーバー上で無作為に並び替えた上でクライアントに結果が渡されます。 取得リストの最大サイズは 100 以下にしてください。

検索条件を指定せずに、クライアント側でゲームサーバーから受け取った検索結果をフィルタリングするような実装では、希望する条件に合致するマッチメイクセッションが取得できる保証が無いため、効率的なマッチメイクができません。 そのため、完全一致で実現できる条件(例:仕向け地、ラウンド数など)については、クライアント側でフィルタせずにSetAttribute()で指定できる属性値を使って検索条件に含めるようにして下さい。

4.13. マッチメイク検索の留意事項

マッチメイクの検索処理は重い処理です。検索対象となる待ちマッチメイクセッションをなるべく作らないように、積極的にギャザリングを埋める実装を行ってください。 マッチメイク条件を細分化しすぎると、ギャザリングが埋まりにくく、ユーザーを待たせることになり、ユーザー体験を損ねることとなります。

また、検索対象から外れるように積極的に制御してください。 マッチメイクセッションが最大参加人数に達した場合は、自動的に検索対象からは外されるように MatchmakeSessionSearchCriteria::SetVacantOnly() を設定してください。 人数が未達でゲームを始めた場合に、明らかに途中参加してこない期間(長時間プレイするゲームで、ゲーム自体が始まった後)には、 MatchmakeExtensionClient::CloseParticipation() を実行して、ギャザリングを閉じて、検索対象からはずすようにしてください。

特定のユーザー同士でのマッチメイクを実現するために、マッチメイクセッションにパスワード等でユーザー制限をかける方法が考えられます。このような制限付きマッチメイクセッションが一般のマッチメイクの検索結果に表示されると、条件によっては多くの制限付きのマッチメイクセッションが検索結果に表示されることがあります。 このような状態になると、ユーザーにより再検索が何度も行われ、サーバーへの負荷やユーザーへの負担が発生します。 従って、マッチメイク検索時には、制限付きマッチメイクセッションは一覧に表示しない。または制限なしマッチメイクセッションのみを検索する手段を提供する事を推奨します。

また、作成したマッチメイクセッションに対して、ゲーム外部で参加者を募る仕様の場合には、サーバーへの負荷を低減させるために、検索用のコードをタグに格納して検索するのではなく、ギャザリング ID をコード化したものを表示させ、これを MatchmakeExtensionClient::FindMatchmakeSessionByGatheringId() で検索する方法を推奨します。

4.14. マッチメイクセッションにパスワードを設ける

作成するマッチメイクセッションにはパスワードを設けることができます。 パスワードにはユーザーパスワードとシステムパスワードの二種類あり、ユーザーパスワードはプレイヤー自身で任意のパスワードを設定し、システムパスワードはサーバーがパスワードを設定します。 サーバーに登録するマッチメイクセッションに対して MatchmakeSession::SetUserPassword() でユーザーパスワードを設定できます。 システムパスワードはすでにサーバに登録されたマッチメイクセッションに対して MatchmakeExtensionClient::GenerateMatchmakeSessionSystemPassword() を呼び出すことで設定できます。 システムパスワードが設定されるとマッチメイクセッションの参加者に対して NotificationEvents::MatchmakeSessionSystemPasswordSet が通知されます。 システムパスワードを解除するには MatchmakeExtensionClient::ClearMatchmakeSessionSystemPassword() を使用します。このとき NotificationEvents::MatchmakeSessionSystemPasswordClear が通知されます。 ユーザーパスワードやシステムパスワードがかけられたマッチメイクセッションに参加するには参加時に正しいパスワードを指定する必要があります。 それぞれのパスワードは JoinMatchmakeSessionParam を引数に取る MatchmakeExtensionClient::JoinMatchmakeSession() に対して JoinMatchmakeSessionParam::SetUserPassword() や JoinMatchmakeSessionParam::SetSystemPassword() でパスワードをセットした JoinMatchmakeSessionParam オブジェクトを指定します。 システムパスワードがかけられたマッチメイクセッションは自動的に MatchmakeExtensionClient::BrowseMatchmakeSession() で見つからなくなります。MatchmakeExtensionClient::FindMatchmakeSessionByGatheringId() では検索可能です。 ユーザーパスワードがかけられたマッチメイクセッションを MatchmakeExtensionClient::BrowseMatchmakeSession() で検索するかどうかは MatchmakeSessionSearchCriteria::SetExcludeUserPasswordSet() の設定に依存します。 システムパスワードは特定のプレイヤーが参加するのを待つケースなどで有用です。 例えば対戦中に意図せずサーバーから切断したプレイヤーが元のマッチメイクセッションに復帰したい場合、対戦を開始する前に MatchmakeExtensionClient::GenerateMatchmakeSessionSystemPassword() でシステムパスワードを発行しておき、切断が発生した際に発行されたシステムパスワードを使ってマッチメイクセッションに戻るという使い方ができます。 ユーザーパスワードとシステムパスワードは MatchmakeExtensionClient::FindMatchmakeSessionByGatheringId() や MatchmakeExtensionClient::BrowseMatchmakeSession() で取得したマッチメイクセッションからは取得できません。 サーバーから取得したマッチメイクセッションに対し、MatchmakeSession::GetUserPassword() を呼び出すと常に空文字列を返します。 ただし、ユーザーパスワードとシステムパスワードがセットされているかどうかは MatchmakeSession::GetUserPasswordEnabled() や MatchmakeSession::GetSystemPasswordEnabled() で取得することができます。

4.15. 登録したマッチメイクセッションを変更する

マッチメイクセッションのオーナーはゲームサーバーへ登録した後にも各種条件の変更ができます。

4.15.1. マッチメイクセッションの参加を募集する

マッチメイクセッションへの参加を募集するには、MatchmakeExtensionClient::OpenParticipation() を使用します。 この関数の引数には、ProtocolCallContext オブジェクトへのポインタ、参加募集するマッチメイクセッションの ID を引数として渡します。

非同期処理が成功すると、他のクライアントがマッチメイクセッションに参加できるようになります(マッチメイクセッション検索時の募集状態フラグも true になります)。 ただし、マッチメイクの最大参加人数に達していると、それ以上新規のクライアントが参加することはできません。

4.15.2. マッチメイクセッションの参加を締め切る

マッチメイクの最大参加人数に達すると、それ以上新規のクライアントが参加することはできませんが、最大参加人数に達していない場合でも新規クライアントの参加を締め切ることが可能です。

マッチメイクセッションへの参加を締め切るには、MatchmakeExtensionClient::CloseParticipation() 関数を使用します。 この関数の引数には、ProtocolCallContextオブジェクトへのポインタ、参加を締め切るマッチメイクセッションのIDを引数として渡します。

非同期処理が成功すると、他のクライアントがマッチメイクセッションに参加できなくなります(マッチメイクセッション検索時の募集状態フラグも false になります)。 すでに参加しているクライアントには影響はありません。 また NotificationEvents::ParticipationEvent 通知のタイミングと締め切り処理タイミングの差異により、呼び出し成功後に ParticipationEvents::Participate 通知が届く可能性がありますのでご注意ください。

注意

マッチメイクセッションへの参加後に P2P セッションへの参加を行う場合は注意が必要です。 NAT トラバーサルや P2P セッションへの参加処理に時間がかかることが原因で、マッチメイクセッションの参加を締め切った後しばらくしてからP2P セッションへの参加が完了することがあります。 P2P セッションへ参加するタイミングでそのクライアントを拒否するとマッチメイクの効率を大きく下げることに繋がります。 特に最大参加人数に満たない状態でマッチメイクセッションを締め切る場合は NotificationEvents::ParticipationEvent による通知をハンドリングし、マッチメイクセッションにおける参加者数と P2P セッションにおける参加者数が食い違っている状況では P2P セッションへの参加を拒否しないようにしてください。

4.15.3. マッチメイクセッションのオーナー権限を移譲する

マッチメイクセッションのオーナー権限を別のクライアントに移譲するには、MatchMakingClient::MigrateGatheringOwnership() を使用します。 通常、オーナーがマッチメイクセッションから退出する際に使用します。 ギャザリングフラグに GatheringFlags::MigrateOwner が指定されている場合は自動でオーナー移譲が発生するため、退出時の MatchMakingClient::MigrateGatheringOwnership() の呼び出しは不要です。 マッチメイクセッションにおいてはその参加者のみがオーナーになることができます。

この関数の引数には、ProtocolCallContext オブジェクトへのポインタ、オーナーを移譲するマッチメイクセッションのID、オーナーを移譲したいクライアントのプリンシパル ID のリストを渡します。 プリンシパル ID のリストが空の場合、マッチメイクセッションに参加しているクライアントの中から新しいオーナーが自動的に選択されます(通常はこちらを使用してください)。

ギャザリングフラグに GatheringFlags::ChangeOwnerByOtherHost が設定されているマッチメイクセッションに対しては MatchMakingClient::UpdateGatheringOwnership() もしくは MatchMakingClient::UpdateSessionHost() を使用することでオーナー権限をギャザリング参加者が奪うことができます。 これらの API は P2P のホストとギャザリングのオーナーを一致させたいケースで有用です。

4.15.4. マッチメイクセッションの情報を変更する

マッチメイクセッションの情報を変更するには MatchmakeExtensionClient::UpdateMatchmakeSession() 関数を使用します。 UpdateMatchmakeSessionParam で変更対象にする項目と変更後の値を指定します。 ただし、以下の項目は変更できません。

マッチメイクシステムタイプは MATCHMAKE_SYSTEM_TYPE_ANYBODY と MATCHMAKE_SYSTEM_TYPE_FRIEND との間のみ変更できます。 .. _matchmake_update_attribute:

4.15.5. 属性値を変更する

属性値を変更するためには、個別に変更する MatchmakeExtensionClient::ModifyMatchmakeSessionAttribute() と、全て更新する MatchmakeExtensionClient::UpdateMatchmakeSessionAttribute() の 2 つがあります。 ModifyMatchmakeSessionAttribute() の引数には、ProtocolCallContextオブジェクトへのポインタ、変更するマッチメイクセッションのID、変更する属性のインデックス、変更後の値を渡します。UpdateMatchmakeSessionAttribute() の引数には、ProtocolCallContext オブジェクトへのポインタ、変更するマッチメイクセッションのID、変更後の属性値の配列を渡します。

4.15.6. アプリケーション定義データを変更する

アプリケーション定義データを変更するためには、MatchmakeExtensionClient::UpdateApplicationBuffer() を使用します。 この関数の引数には、ProtocolCallContext オブジェクトへのポインタ、変更するマッチメイクセッションのID、変更後のデータを渡します。

4.15.7. マッチメイクセッションを削除する

マッチメイクセッションを削除するには、MatchMakingClient::UnregisterGathering() を使用します。 この関数の引数には、 ProtocolCallContext オブジェクトへのポインタ、削除するマッチメイクセッションのIDを渡します。 マッチメイクセッションは、以下のケースで自動的に削除されます。

  • ギャザリングフラグに GatheringFlag::OwnerMigration が設定されていない場合に、マッチメイクセッションのオーナーがギャザリングから退出もしくはゲームサーバーからログアウトしたとき
  • マッチメイクセッションの参加者が 0 人になったとき

意味の無いマッチメイクセッションがゲームサーバー上に残されてしまわないように、不要になったマッチメイクセッションは MatchMakingClient::UnregisterGathering() で削除してください。

4.16. フレンドプレゼンスライブラリを使用した合流

4.16.1. 概要

フレンドプレゼンスライブラリを使用して NEX におけるギャザリングの参加状況をプレゼンスとしてフレンドと共有することができます。 共有されたフレンドのプレゼンスはアプリケーションやフレンドリストが取得することができ、その情報を元にフレンドが参加中のギャザリングへ参加することができます。 フレンドプレゼンスライブラリによる合流の補助機能は、以下の2つの処理から構成されます。

  • プレゼンスの共有 NEX のギャザリング参加状況にあわせて MatchmakeSession オブジェクトのパラメータなどを nn::friends::GameMode 構造体に格納し、 nn::friends::UpdateGameMode()で自分の合流可能状況を更新する
  • プレゼンスの取得・監視と合流 合流する側は、フレンドのプレゼンス (≒参加しているマッチメイクセッション) の変更を監視し、自分と合流可能になったフレンドを発見し、そのプレゼンスに含まれるギャザリング ID を指定して相手が参加しているマッチメイクセッションへMatchmakeExtensionClient::JoinMatchmakeSession()で参加する

4.16.1.1. フレンドリストを介した合流

フレンドリストは、フレンドが公開しているプレゼンスを把握してその状態を表示します。 このとき、自分が相手と同じゲームをプレイしている必要はなく、お互いにオンラインであればフレンドのプレイ中ソフトとその状態を見ることができます。

フレンドがオンラインで、かつ公開しているプレゼンスが合流可能であるとき、該当のフレンドカードの「プレイ中のソフトに参加する」ボタンが有効になります。 このボタンをタッチすると、(現在プレイ中のソフトがあればそれを終了し)フレンドが遊んでいるソフトを起動することができます。

アプリケーションは、フレンドリストから起動された場合、フレンドリストから起動されたことと、その際に選ばれたフレンドのフレンドキーを起動後に取得することができます。 このフレンドキーからフレンドの最新のプレゼンスを取得し、その情報を元に NEX でフレンドが参加中のギャザリングへ合流します。

4.16.1.2. 同一の合流ゲーム ID を持つアプリ同士でのアプリ内合流

お互いに同じ合流ゲーム ID を指定したソフトをプレイ中の場合、フレンドが公開しているプレゼンスのパラメータをアプリケーションで直接取得することができます。 そのパラメータを元に、相手が合流を受け入れているかを判断し、相手が参加中のギャザリングへ NEX で合流します。

相手のプレゼンスが合流を受け入れているかの判断や、プレゼンスの変更監視はフレンドプレゼンスライブラリが補助します。

4.16.2. 実装

4.16.2.1. アイコンデータへの合流情報の埋め込み

BSF ファイルの中で、 JoinGameId に合流ゲーム ID を、 JoinGameModeMask に合流ゲームモードマスクを設定してください。 設定方法の詳細は ctr_makebanner のマニュアルをご参照ください。

合流ゲーム ID には、 NEX のゲームサーバ ID を使用します。 この合流ゲーム ID に同じ値を設定しているアプリケーション同士がフレンド間でプレゼンスを共有できます。

ゲームモードマスクは、最下位ビットがモード 0 、最上位ビットがモード 63 への合流可能を表す、ビットマスクの形で指定します。 この値はフレンドリストからのアプリ起動時の判定にのみ使用され、アプリ起動後の動作には影響しません。

ここで指定した値は CTR MasterEditor で確認できます。

アイコンデータに設定した合流ゲーム ID を挙動に反映させるには、アプリケーションを HOME メニューもしくは開発用メニューから起動する必要があります。PARTNER-CTR Debugger や IS-SNAKE-BOX を使用してデバッガソフトウェアから起動した場合は正しく設定されませんのでご注意ください。 CTR 本体に SD カードを挿していると、 HOME メニューがアイコンデータを SD カードにキャッシュしてそれを使いまわすため、アイコンデータの更新が挙動に反映されないことがあります。その場合はアイコンデータキャッシュを削除してください。アイコンデータキャッシュの削除方法については、CTR-SDK のパッケージに付属しているマニュアルの「HOMEメニュー」から「デバッグ機能の使用方法」を参照してください。

補足

「HOMEメニュー」は、下記どちらからでも参照することができます。

  • CTR-SDK パッケージの index.html 内のメニュー一覧
  • CTR_SDK\documents\DevHomeMenu\HomeMenu.html

4.16.2.2. 合流の準備

フレンドライブラリでフレンドとプレゼンスを共有するには、お互いにオンラインで、かつ同一の合流ゲーム ID を指定している必要があります。

システムによるバックグラウンドのログイン要求によってオンラインになっている場合でもフレンドとプレゼンスを共有することができますが、アプリケーション自身がログイン要求を出さないと、オンライン状態の維持は保証されません。 そのため、合流に関する機能を利用する前に nn::friends::Login() 関数でログイン要求を出してください。

4.16.2.3. プレゼンスの共有(合流される側の実装)

フレンドに、適切に自分の合流受入可能状態を伝えるために、NEX のギャザリング参加状況が変化するたびに nn::friends::UpdateGameMode() 関数を使用して自分のプレゼンスを更新してください。

nn::friends::GameMode 構造体の各要素には、以下のものを指定してください。

  • joinAvailabilityFlag : 自分の参加しているマッチメイクセッションにフレンドが合流するのを認めるかを指定します。ここで合流を認めないと、以下のパラメータがすべて合流可能に必要な条件を満たしていたとしても、合流不可と判定されます。JoinAvailability 列挙体の値を使用してください。
Table 4.3 joinAvailabilityFlag と合流可能判定の関係
joinAvailabilityFlag JOIN_AVAILABILITY_JOINABLE JOIN_AVAILABILITY_JOINABLE_APP_ONLY JOIN_AVAILABILITY_NOT_JOINABLE
フレンドリストの「プレイ中のソフトに参加する」ボタン 条件を満たせば有効 常に無効 常に無効
nn::friends::FriendPresense::IsJoinable() 条件を満たせば true 条件を満たせば true 常に false
  • matchmakeSystemType : 自分の参加しているマッチメイクの種類です。参加中の MatchmakeSession オブジェクトのマッチメイクシステムタイプの値を格納してください。
  • joinGameId : 合流可能なゲームタイトルを識別するための ID です。UpdateGameMode ではこの値を指定しても無視され、アイコンデータに指定した合流ゲーム ID が使い続けられます。
  • joinGameMode : 合流可能なゲームモードを識別するための値です。 NEX では32ビットの値域が使用可能ですが、フレンドリストやフレンドプレゼンスライブラリで合流可能と判定するためには 0 から 63 までの値を使用する必要があります。参加中の MatchmakeSession オブジェクトのゲームモードの値を格納してください。
  • ownerPrincipalId : 自分が参加しているマッチメイクセッションのオーナーのプリンシパル ID を指定します。参加中の MatchmakeSession オブジェクトのオーナーのプリンシパル ID の値を格納してください。マッチメイクシステムタイプが FRIEND の場合はオーナーとフレンド関係が成立しているかどうかが合流可否の判定に必要になるため、オーナーの変更があった場合は GameMode を更新するようにししてください。
  • joinGroupId : 自分が参加しているマッチメイクセッションの ID を指定します。参加中の MatchmakeSession オブジェクトのギャザリング ID の値を格納してください。
  • applicationArg : アプリケーションが任意に定義できる情報です。

UpdateGameMode() のコール頻度自体に制限はありませんが、更新した内容がサーバーやフレンドへ通知されるには、最短でも前回の通知から 10 秒の間隔があきます。詳細は「CTR プログラミングマニュアル 無線通信編」の「4.3.5.2 フレンドサーバーやフレンドとの情報の同期」を参照してください。

これより高頻度にプレゼンスを更新してもフレンド側には随時伝わりませんのでご注意ください。

4.16.2.4. プレゼンスの取得と監視(合流する側の実装)

フレンドのプレゼンスは nn::friends::GetFriendPresence()で取得できます。 これで取得した nn::friends::FriendPresence 構造体に対して nn::friends::FriendPresence::IsJoinable()で相手の合流受入可能状態を判定できます。 この時、引数には自分が合流できるゲームモードをビットマスクの形で指定してください。 合流可能と判定されれば、 joinGroupId メンバの値を NEX のギャザリング ID として指定して、そのギャザリングへ参加処理をおこなって構いません。

フレンドのプレゼンスの変化は、 nn::friends::AttachToEventNotification() や nn::friends::GetEventNotification() を使用してプレゼンスの変更通知を監視することでハンドリングできます。 プレゼンスの変更通知が来た場合は IsJoinable()での判定をやり直してください。

4.16.2.5. フレンドリストからの起動のハンドリング

アプリがフレンドリストから起動された場合、 nn::friends::IsFromFriendList() がtrue を返します。 また、その際、引数に渡したバッファに、フレンドリストで選択したフレンドの FriendKey が格納されます。 引数取得後は、その FriendKey が指すフレンドのプレゼンスを取得し、速やかに合流のシーケンスへ移行してください。

4.16.3. 注意事項

  • プレゼンスで合流可能と判定してギャザリングへ参加をおこなう場合でも、参加処理での失敗判定は通常どおりにおこなってください。プレゼンスで取得できる合流受入可能状態は、リアルタイムに正しい状態を提示しているとは限りません。また、その後の参加処理の間に相手の状態が変化している可能性もあります。
  • 合流ゲームモードのマスクを指定する箇所では、必要十分なビットだけを立てるようにしてください。先に制作されたバージョンで必要以上に広く合流可能判定してしまうと、次回作やローカライズ版で合流ゲーム ID を共有する場合に、新しいゲームモードを追加する妨げになることがあります。
  • フレンドリストからの合流に対応しない場合でも、アプリ内でプレゼンスの共有をおこなう場合は makebanner でアイコンデータに合流ゲーム ID を埋め込む必要があります。この場合、アイコンデータのゲームモードマスクには 0 を指定し、またプレゼンス更新時の joinAvailabilityFlag の値には JOIN_AVAILABILITY_JOINABLE_APP_ONLY を指定してください。ここの設定を誤ると、フレンドリストで「プレイ中のソフトに参加する」ボタンは有効になるが、起動できなくなってしまいます。

4.17. ブロックリスト

特定のユーザーからのハラスメント対策としてブロックリストを利用することができます。ブロックリストは以下の機能を提供します。

  • ブロックリストに登録したユーザーが、自分が参加しているマッチメイクセッションに参加するのをブロックする
  • ブロックリストに登録したユーザーが参加しているマッチメイクセッションに、自分が参加してしまうのをブロックする
  • ブロックリストに登録したユーザーが、自分の状態を取得できないようにする(プライバシーフィルタ機能)

アプリの仕様上、ブロックリスト機能を無効にしたい場合は、マッチメイクセッションへの参加時に MatchmakeBlockListParam を使用して設定してください。

ブロックリストはゲームサーバー毎(したがってアプリケーション毎)に1ユーザーにつき 100 人登録可能です。 100 人以上登録しようとした場合、先に登録したものから順番にブロックリストから削除されていきます(先入れ先出し方式)。 そのため、ブロックリストの管理をアプリケーション内で行うかどうかは、アプリケーション毎に決めることができます。

MatchmakeExtensionClient::GetPlayingSession() および MatchmakeExtensionClient::FindPersistentGatheringByParticipant() については、検索対象となるユーザーが自分をブロックリストに登録している場合は常に空のリストを返します。 ただし、そのギャザリングの他の参加者が自分をブロックリストに登録しているかどうかについては関知しません。

また、MatchmakeExtensionClient::BrowseMatchmakeSession()や MatchmakeExtensionClient::FindMatchmakeSessionByGatheringId() などではブロックリストに登録した/されたユーザーが参加しているかどうかのチェックを行いません。 したがって、MatchmakeExtensionClient::JoinMatchmakeSession() で参加しようとしたタイミングまでブロックリストに登録した/されたユーザーが参加しているかどうかは分かりません。 プライバシーフィルタ機能はあくまで特定のユーザーに対する付きまといを防止するものであることに留意してください。

Code 4.6 ブロックリスト管理用API
//指定したユーザーをゲームサーバー上のブロックリストに追加する
qBool MatchmakeExtensionClient::AddToBlockList(
                                    ProtocolCallContext* pContext,
                                    const qList<PrincipalID>& PrincipalIDList);

// 指定したユーザーをゲームサーバー上のブロックリストから削除する
qBool MatchmakeExtensionClient::RemoveFromBlockList(
                                    ProtocolCallContext* pContext,
                                    const qList<PrincipalID>& principalIDList);

// ブロックリストをクリアする
qBool MatchmakeExtensionClient::ClearBlockList(ProtocolCallContext* pContext);

// 自分がブロックリストに登録したユーザーを取得する
qBool MatchmakeExtensionClient::GetMyBlockList(
                                    ProtocolCallContext* pContext,
                                    qList<PrincipalID>* pPrincipalIDList);

4.18. 永続ギャザリング

永続ギャザリングとは、仲間同士だけでマッチメイクできるようにする仕組みのことで、例えば学校のクラスメイトだけが参加するような永続ギャザリングのルームを作って、そこからマッチメイクしたりすることができます。

フレンドマッチでも同様のことを実現できますが、フレンド関係を結ばなくても永続ギャザリングに参加することが可能なため、開発者側からゲームの公式永続ギャザリングなどを提供し、大会を開催するなども可能です。

永続ギャザリングを作成すると、明示的に削除するまで永続的にサーバーに残ります。 永続ギャザリングへの参加状態も永続的に残ります(デフォルト設定における動作)。 永続ギャザリング参加者同士でマッチメイクを行う場合は、永続ギャザリングのギャザリングIDを関連する永続ギャザリング ID としてマッチメイクセッションを登録し、それを検索・参加することで実現します。

永続ギャザリングはギャザリングの一種ですが、マッチメイクセッションとはギャザリングとその参加状態が永続的に残るかどうかが大きく異なります。

永続ギャザリングを使う例としては、以下の様なものが考えられます。

  • 公式永続ギャザリングでの大会、ランキングなど
  • 特定のコースやルールだけで対戦する永続ギャザリング
  • 学校のクラスメイト全員が入って、永続ギャザリング内の人同士でマッチメイクする

なお、永続ギャザリングはゲーム毎に独立したものになります。

4.18.1. 永続ギャザリングとマッチメイクセッションの相違点

永続ギャザリングとマッチメイクセッションは、生存期間だけでなく以下のような相違点があります。

Table 4.4 永続ギャザリングとマッチメイクセッションの相違点の一覧
相違点 永続ギャザリング マッチメイクセッション
削除のタイミング ・参加者が 0 になる(*1)・UnregisterGathering() を呼ぶ ・参加者が 0 になる・UnregisterGathering() を呼ぶ
離脱のタイミング ・EndParticipation() を呼ぶ(*2) ・EndParticipation() を呼ぶ・ゲームサーバーからログアウトする
参加人数 無制限(制限をつけることも可能) P2Pで通信するクライアント数分
サーバーからの通知 なし 参加・退出時に通知が push される
メッセージング 最大参加者人数を32人以下に設定している場合のみ可(MessagingClient::SendChat()) あり(MessagingClient::SendChat())
P2P 通信 なし。別途マッチメイクセッションを登録・参加したのちにP2Pに移行する あり
オーナーマイグレーション なし あり
ブロックリスト なし あり
締切 なし。ただし参加可能な期間を指定することは可能 あり

(*1) ギャザリングフラグに GatheringFlags::AllowNoParticipant を指定することで、0人でも存続させることが可能

(*2) ギャザリングフラグに GatheringFlags::NoPersistentParticipation を指定することで、ゲームサーバーとの切断(ログアウト含む)で自動的に離脱することが可能

4.18.2. 永続ギャザリングの作成・削除・更新

永続ギャザリングを作成するには、PersistentGathering クラスオブジェクトに永続ギャザリングの条件を設定し、 MatchmakeExtensionClient::CreatePersistentGathering() を実行してください ( Table 4.5 の項目以外は設定しないでください。意図しない動作になることがあります)。 MatchmakeExtensionClient::CreatePersistentGathering() の非同期処理に成功すると、ゲームサーバーにより永続ギャザリングを識別するためにギャザリングIDが付与されます。 CreatePersistentGathering() を実行したユーザーはその永続ギャザリングのオーナーになり、かつその永続ギャザリングに参加します。 1ユーザーあたりの永続ギャザリング作成上限は PERSISTENT_GATHERING_CREATION_MAX(4) まで、参加上限は PERSISTENT_GATHERING_PARTICIPATION_MAX(16) 件までです。

永続ギャザリングのオーナーは MatchmakeExtensionClient::UpdatePersistentGathering() で永続ギャザリングを更新でき、 MatchMakingClient::UnregisterGathering() で削除されます。 オーナー以外が参加している永続ギャザリングでも削除可能です。永続ギャザリングが削除された場合、永続ギャザリングに参加しているユーザーは退出したことになります。

公式永続ギャザリングは、作成時にオーナーのプリンシパル ID が特別な プリンシパル ID 101 となり、 作成したクライアントから削除・更新ができなくなります。 後述の NMAS からの操作で、削除・更新を行ってください。

開発環境で作成された公式永続ギャザリングは、ロットチェック環境に自動的にコピーされます。ロットチェック開始前に、必要な公式永続ギャザリングが存在する状態にしてください。製品環境へはコピーされません。

Table 4.5 永続ギャザリングの設定項目
項目名 概要 初期値 設定関数
永続ギャザリングタイプ 永続ギャザリングの種別(誰でも参加可能、パスワード付き永続ギャザリング、公式永続ギャザリング)を指定します。 Open SetPersistentGatheringType()
属性 ゲームが独自に検索条件として使用できる属性値で、最大 6 個までの指定が可能です。 0 SetAttribute()
最大参加人数 0 を指定した場合、最大参加人数は無制限になります。 0 SetMaxParticipants()
アプリケーション定義データ アプリケーションが自由に利用できるデータです。たとえば永続ギャザリングの状態など、検索条件に含める必要のないデータを設定できます(最大サイズ 512 byte)。 NULL SetApplicationBuffer()
説明文字列 永続ギャザリングの説明文字列です。アプリケーションが任意の値を設定できます(最大サイズ 256文字)。 NEX_T(“”) SetDescription()
参加可能開始日 永続ギャザリングに参加可能となる日時を取得します。DateTieme::Never()を指定した場合、無制限になります。 DateTime::Never() SetParticipationStartDate ()
参加可能終了日 永続ギャザリングの参加締切日となる日時を取得します。DateTieme::Never()を指定した場合、無制限になります。 DateTime::Never() SetParticipationEndDate ()

4.18.3. 永続ギャザリングの検索

永続ギャザリングの検索には、以下の関数を使用できます。 FindPersistentGatheringByParticipant () は以下の場合には空リストを返します。

  • 検索対象者が自分をブロックリストに登録している場合

現在、属性値を指定した永続ギャザリングの検索はサポートされていません。

Code 4.7 永続ギャザリングの検索関数
// ID で検索
MatchmakeExtensionClient::FindPersistentGatheringByGatheringID(
                                    ProtocolCallContext *pContext,
                                    qList<GatheringID> gatheringIdList,
                                    qList<PersistentGathering> *pPersistentGatheringList);

// 参加者で検索
MatchmakeExtensionClient::FindPersistentGatheringByParticipant(
                                    ProtocolCallContext *pContext,
                                    PrincipalID principalId,
                                    const ResultRange& resultRange,
                                    qList<PersistentGathering>* pPersistentGatheringList);

// 公式永続ギャザリング(永続ギャザリングタイプが PERSISTENT_GATHERING_TYPE_OFFICIAL) を検索
MatchmakeExtensionClient::FindOfficialPersistentGathering(
                                    ProtocolCallContext *pContext,
                                    qBool isAvailableOnly,
                                    const ResultRange& resultRange,
                                    qList<PersistentGathering>* pPersistentGatheringList);

// オーナーのプリンシパルIDで検索
MatchmakeExtensionClient::FindMatchmakeSessionByOwner(ProtocolCallContext *pContext,
                                    PrincipalID pidOwner,
                                    const ResultRange& resultRange,
                                    qList<MatchmakeSession> *lstMatchmakeSession);

4.18.4. 永続ギャザリングの参加・退出

永続ギャザリングの参加には MatchmakeExtensionClient::JoinPersistentGathering() を、退出時には MatchmakeExtensionClient::EndParticipation() をそれぞれ利用します。 マッチメイクセッションとは異なり、参加退出時に永続ギャザリングオーナーに対する通知は発生しません。 なお、1 ユーザーあたりの永続ギャザリングの参加上限は PERSISTENT_GATHERING_PARTICIPATION_MAX (16) に制限されており、これ以上の永続ギャザリングに参加しようとすると、非同期処理が QERROR(RendezVous, PersistentGatheringParticipationMax) で失敗します。

4.18.5. 永続ギャザリングコード

永続ギャザリングの ID としてユーザーインタフェース上に表示する場合は、その永続ギャザリングのギャザリング ID を永続ギャザリングコードに変換して表示する必要があります。 永続ギャザリングコードは、ギャザリング ID にゲーム固有のアクセスキーをもとにしたHMAC値を付与した 10 進 14 桁の整数になります。 IsValidPersistentGatheringCode() で妥当な永続ギャザリングコードであると判断されても、その永続ギャザリングが存在するかどうかは、ゲームサーバーに問い合わせる必要があることに注意してください。 なお、 Code 4.8 の永続ギャザリングコード変換関数は、ゲームサーバーにログインしていなくても使用可能です。

Code 4.8 永続ギャザリングコード変換関数
// 永続ギャザリングコードのHMAC鍵を設定する。ゲーム固有のアクセスキーを指定すること
static void MatchmakingClient::SetPersistentGatheringCodeKey(const String& strKey);

// 永続ギャザリングコードのHMAC鍵をクリアする
static void MatchmakingClient::ClearPersistentGatheringCodeKey();

// ギャザリングIDを永続ギャザリングコードに変換する
static qUnsignedInt64 MatchmakingClient::GatheringIdToPersistentGatheringCode(GatheringID gatheringId);

// 永続ギャザリングコードをギャザリングIDに変換する
static GatheringID MatchmakingClient::PersistentGatheringCodeToGatheringId(qUnsignedInt64 persistentGatheringCode);

// 妥当な永続ギャザリングコードであるかを検証する
static qBool MatchmakingClient::IsValidPersistentGatheringCode(qUnsignedInt64 persistentGatheringCode);

4.18.6. マッチメイク・ランキングとの連携

永続ギャザリングは永続的なギャザリングですが、永続ギャザリング単体ではマッチメイクを行うことはできません。 永続ギャザリングに関連したマッチメイクを行うには、永続ギャザリングのギャザリング ID を関連付けたマッチメイクセッション(以下、永続ギャザリングマッチ)を別途登録する必要があります。 永続ギャザリングマッチを登録するには、MatchmakeSession::SetReferPersistentGatheringId() を使い関連する永続ギャザリングの ID をセットしてください。 その他の設定は通常のマッチメイクと違いはありません。

永続ギャザリングマッチが登録されると、PersistentGathering::GetMatchmakeSessionCount() で取得できる永続ギャザリングマッチ開催数がインクリメントされます(削除時にはデクリメントします)。

補足

NEX 3.7 以前は、マッチメイクセッションと永続ギャザリングを関連付けるために、登録するマッチメイクセッションのマッチメイクシステムタイプに MATCHMAKE_SYSTEM_TYPE_PERSISTENT_GATHERING を指定し、0 番目の属性に永続ギャザリングのギャザリング ID を指定する必要がありました。 NEX 3.8 以降では互換性を保つため、マッチメイクシステムタイプに MATCHMAKE_SYSTEM_TYPE_PERSISTENT_GATHERING を指定し、0 番目の属性に永続ギャザリングのギャザリング ID を指定し、関連する永続ギャザリングとして INVALID_GATHERINGID が指定された場合に、サーバーで自動的に 0 番目の属性値が関連する永続ギャザリングの ID として登録されます。 サーバーから取得したマッチメイクセッションに対して MatchmakeSession::SetReferPersistentGatheringId() を呼び出すとその ID を取得できます。 永続ギャザリングマッチ開催数もカウントされます。

永続ギャザリングに参加しているユーザー間でランキングを実施する場合(永続ギャザリングランキング)はカテゴリに永続ギャザリングのギャザリングIDを使用することで行う事ができます。 ただし、通常のランキングと同じカテゴリを使用することができません。 通常のランキングで 0~10 番までのカテゴリを使用する場合永続ギャザリングランキングでは 11 番以降がふられるように工夫してください。

4.18.7. NEX ゲームサーバ管理ツール( NMAS )

NEX ゲームサーバ管理ツール (NMAS, Nex server MAnagement System) の永続ギャザリング管理機能からは、永続ギャザリングの作成や削除、編集などの操作を行うことができます。 開発環境、製品環境のどちらにも対応しています。

NMAS へは、以下の URL からアクセスすることができます。

https://nmas.mng.nintendo.net/nmas/

ログイン ID とパスワードは OMAS と同じものを使用してください。

4.19. 通知イベントをハンドリングする

あるイベントがゲームサーバーで発生したときに、ゲームサーバーから別のクライアントへ送られるメッセージのことを通知イベントと呼びます。 例えばクライアントがマッチメイクセッションに参加したとき、そのマッチメイクセッションに参加している他のクライアントにメッセージが通知されます。

クライアントプロセスが通知イベントを受信するためには、NotificationEventHandlerNgsFacade::RegisterNotificationEventHandler() でクライアントプロセスに登録されていなければなりません。 すべての通知イベントを受信するためには、この登録処理をゲームサーバーにログインする前に行ってください。ログイン後に登録すると、ログインしてから登録までの間に送られた通知イベントを受信できないことがあります。 ハンドラが登録されると、クライアントプロセスが通知イベントを受信するたびに、イベントがハンドラへ送られ、ProcessNotificationEvent システムコールバックが呼び出されます。 デフォルトでは、このコールバックは受信した通知イベントをすべて無視するようになっていますが、通知イベントの処理方法をカスタマイズしたい場合は、NotificationEventHandler をサブクラス化して、システムコールバックを再実装します。

ProcessNotificationEvent システムコールバックには、イベントを生成したクライアントのプリンシパル ID や、イベントタイプ、最大 3 個のパラメータを格納した NotificationEvent クラスオブジェクトが渡されます。 NEX で使用されるイベントタイプ(NotificationEvent::GetType () で取得可能)は以下の通りです。

  • NotificationEvents::ParticipationEvent :マッチメイクセッションの参加・退出・切断時に通知されます。デフォルトではマッチメイクセッションのオーナーに通知されますが、GatheringFlags::NotifyParticipationEventsToAllParticipants を使用することで参加者全員に通知されるように設定することも可能です。サブタイプには参加・退出・切断のどのイベントかを示す ParticipationEvents 列挙子がセットされます。また、 NotificationEvent::GetParam1() でマッチメイクセッションの ID、 NotificationEvent::GetParam2() で参加・退出・切断したクライアントのプリンシパル ID、 NotificationEvent::GetStringParam() で参加・退出時の詳細情報が取得できます。
  • NotificationEvents::OwnershipChangeEvent :マッチメイクセッションのオーナーが、あるクライアントから別のクライアントに移動したときに、マッチメイクセッションの参加者全員に送信されます。 NotificationEvent::GetParam1() でマッチメイクセッションのID, NotificationEvent::GetParam2() で新しいオーナーのプリンシパル ID が取得できます。
  • NotificationEvents::GatheringUnregistered :マッチメイクセッションが削除されたときに、マッチメイクセッションの参加者全員に通知されます。 NotificationEvent::GetParam1() でマッチメイクセッションの ID が取得できます。
  • NotificationEvents::HostChangeEvent : マッチメイクセッションの P2P 通信のホスト情報が更新されたときに、前のホストに対して通知されます。 NotificationEvent::GetParam1() でマッチメイクセッションの ID が取得できます。
  • NotificationEvents::GameNotificationEvent 1~8 :ゲームが独自に使用できるプレゼンスイベントです。MatchmakeExtensionClient::UpdateNotificationData () 関数実行時に、同じゲームサーバーにログインしているフレンドに対して通知イベントが送信されます。詳細は「 4.20. ゲーム固有の通知イベントを利用する 」を参照してください。
  • NotificationEvents::GameNotificationLogout : ゲーム定義イベントをセットしていた場合に、セットしていたユーザーがゲームサーバーからログアウトしたときにフレンドに通知されるイベントです。このログアウトは電源断などで発生する非明示的なログアウトも含まれます。
  • NotificationEvents::GameServerMaintenance : ゲームサーバーのメンテナンス開始通知です。詳細は「 4.21. ゲームサーバーのメンテナンス通知イベントをハンドリングする 」を参照してください。
  • NotificationEvents::MaintenanceAnnouncement : ゲームサーバーのメンテナンス予告通知です。詳細は「 4.21. ゲームサーバーのメンテナンス通知イベントをハンドリングする 」を参照してください。
  • NotificationEvents::RoundStarted : ラウンド開始時にラウンド参加者全員に通知されるイベントです。
  • NotificationEvents::FirstRoundReportReceived : ラウンドの最初のレポートをサーバが受信したときに参加者全員に通知されるイベントです。
  • NotificationEvents::RoundSummarized : ラウンドの集計処理が終了したときに参加者全員に通知されるイベントです。
  • NotificationEvents::AddedToGathering : 自分自身がギャザリングに追加されたときに追加された人に対して通知されるイベントです。 NotificationEvent::GetParam1() で追加されたギャザリングの ID、 NotificationEvent::GetParam2() で追加されたユーザのプリンシパル ID (つまり自分のプリンシパル ID)が取得できます。 NotificationEvent::GetStringParam() にはイベント発生元ユーザーが参加時に指定した参加メッセージが取得できます。NotificationEvent::GetSource() で参加させる RMC を呼び出したユーザーのプリンシパル ID を取得可能です。
Code 4.9 NotificationEventHandlerのサブクラス化
// NGSから通知を処理するイベントハンドラ
class MyNotificationEventHandler : public NotificationEventHandler {
public:
    void ProcessNotificationEvent(const NotificationEvent &oEvent){

        switch (oEvent.GetType())
        {
        case NotificationEvents::ParticipationEvent:
            switch (oEvent.GetSubType())
            {
            case ParticipationEvents::Participate:
                NN_LOG("%u join %u\n", oEvent.GetParam2(), oEvent.GetParam1());
                break;
            case ParticipationEvents::EndParticipation:
                NN_LOG("%u end %u\n", oEvent.GetParam2(), oEvent.GetParam1());
                break;
            case ParticipationEvents::Disconnect:
                NN_LOG("%u disconnect %u\n", oEvent.GetParam2(), oEvent.GetParam1());
                break;
            default:
                NN_LOG("unspoorted participation event\n");
                break;
            }
            break;
        case NotificationEvents::OwnershipChangeEvent:
            NN_LOG("matchmakesession %u changed owner to %u\n", oEvent.GetParam1, oEvent.GetParam2());
            break;
        case NotificationEvents::GameNotificationEvent1:
            NN_LOG("game specific event 1\n");
            break;
        case GameServerMaintenance:
            NN_LOG("game server maintenance event\n");
            break;
        default:
            NN_LOG("unsupported notification event\n");
            break;
        }
    }
};

4.20. ゲーム固有の通知イベントを利用する

同じゲームサーバーにログインしているフレンドに対して、ゲームが独自に定義した通知イベントを送信することができます。 この通知イベントの受信側のハンドリング方法は、「 4.19. 通知イベントをハンドリングする 」を参照してください。 この機能は、ユーザーが通信相手にイベントを送信するような実装を想定してサーバー側も含めて設計されていますので、それを超えるような頻度の送信を行わないでください。具体的には、次のような制限になります。

  • ユーザーがイベントを送信する、ということ以外のデータ送信に使用してはいけません。例えば、本来 P2P で送受信されるべきである対戦中のデータのやりとりなどです。
  • ユーザーの連続操作による過度の送信を防ぐために、1 分間に 10 回の頻度を超えないように制限してください。
  • ゲーム定義のイベントタイプは NotificationEvents::GameNotificationEvent 1~8 を使用してください。

4.20.1. 通知イベントをサーバーに登録し、フレンドに通知する

ゲーム固有のイベントをフレンドに通知するには、MatchmakeExtensionClient::UpdateNotificationData () を使用します。 この関数には、ProtocolCallContext オブジェクトへのポインタ、通知の種類、ゲームで独自に定義する通知データ(qUnsignedInt32 の値 2 個、文字列 1 個(256 文字まで))を渡します。 通知の種類はシステムで定義されたイベントタイプと被ることのないように、NotificationEvents::GameNotificationEvent1~ NotificationEvents::GameNotificationEvent8までの値を設定してください。 なお、登録した通知イベントはゲームサーバーから切断した時点で自動的にゲームサーバーから削除されます。

4.20.2. 登録された通知イベントを取得する

すでに登録されたフレンドの通知イベントを取得するには、MatchmakeExtensionClient::GetFriendNotificationData () 関数を使用します。この関数には、ProtocolCallContext オブジェクトへのポインタ、非同期処理の結果を格納するqList<NotificationEvent>へのポインタ、取得したい通知の種類を引数として渡します。

補足

本関数はログイン時にのみ使用し、ログイン中は通知イベントをハンドリングしてください。

4.21. ゲームサーバーのメンテナンス通知イベントをハンドリングする

4.21.1. ゲームサーバーのメンテナンス通知イベント

ゲームサーバーのメンテナンスが実施されるとき、クライアントはメンテナンスの予告と開始を表すイベント通知をハンドリングすることができます。 この通知をハンドリングすることで、ユーザーにメンテナンスが開始されることを表示したり、マッチメイクを利用するゲームモードへ遷移するメニューをユーザが選択できないようにすることができます。 メンテナンス予告、開始の通知はゲームサーバーにログインしているすべての端末に通知され、もし予告通知が行われた後または開始通知が行われた後にゲームサーバにログインした場合は、ログイン後に予告通知または開始通知がクライアントに対して通知されます。

メンテナンスの予告、開始の通知は通常ログイン後 30 秒以内に行われます。ただし、30 秒以内に 100% 通知が届くことを保証するものではありませんので、30 秒という時間をアプリケーションの制御に利用しないようにしてください。

ゲームサーバーによるメンテナンス開始の通知を利用したい場合にはご相談ください。デフォルトではオフです。 メンテナンス予告の通知は設定をおこなったタイトルだけ通知されます。メンテナンス予告通知を利用したい場合はご相談ください。

補足

NEX3 ではメンテナンス開始前には新規のログインやマッチメイクを受け付けませんでしたが、NEX4 では開始前であっても受け付けるようになりました。 ユーザをマッチメイクに参加させたくない場合にはゲームサーバーによるメンテナンス通知機能を利用して、アプリケーション側で適切にハンドリングしてください。 NEX4 でも NEX3 同様にメンテナンス開始前にログインやマッチメイク API を封鎖したい場合にはご相談ください。

_images/Fig_Server_Maintenance.png

Figure 4.1 メンテナンスの流れと通知イベント

4.21.2. メンテナンス予告通知イベントをハンドリングする

ゲームサーバーのメンテナンス開始約1時間前に、予告通知として NotificationEvents::MaintenanceAnnouncement が通知されます。 NotificationEvent::GetParam1() でメンテナンス開始時刻が Unix Epoch Time で取得でき、 DataTime クラスの DataTime::FromUnixEpochTime() を使用してサーバーメンテナンス開始時刻を取得できます。 通知される時刻は UTC となります。 緊急時は、1 時間前にメンテナンス予告通知ができない場合があります。

4.21.3. メンテナンス開始通知イベントをハンドリングする

ゲームサーバーのメンテナンス開始時に、ゲームサーバーによるメンテナンス通知設定が有効の場合に、開始通知として NotificationEvents::GameServerMaintenance が通知されます。 その後、5分から10分程度後にメンテナンス実施としてゲームサーバーを停止します。

このイベントに対する処理としては下記のようなものが考えられます。ゲームの仕様にあった対処方法を検討してください。

  • メンテナンスの開始をユーザーに通知してマッチメイクを解散させるように誘導を行う
  • NotificationEvents::GameServerMaintenance を受けてもユーザーにはなにも通知せずにゲームを続ける

4.22. メッセージング機能を利用する

メッセージング機能では、テキストメッセージやバイナリメッセージをクライアント間で交換できるようにする一般的な方法を提供します。 Reliable通信を使用して、指定したユーザーにゲームサーバー経由でインスタントメッセージを送信します。 送信相手がゲームサーバにログインしている場合にのみ送信することができます。

この機能は、ユーザーが通信相手にメッセージを送信するような実装を想定してサーバー側も含めて設計されていますので、それを超えるような頻度の送信を行わないでください。具体的には、次のような制限になります。

  • ユーザーがメッセージを送信する、ということ以外のデータ送信に使用してはいけません。例えば、本来 P2P で送受信されるべきである対戦中のデータのやりとりなどです。
  • ユーザーの連続操作による過度の送信を防ぐために、1 分間に 10 回の頻度を超えないように制限してください。

4.22.1. メッセージを送信する

特定のクライアントにメッセージを送信するには、MessagingClient::SendInstant () 関数を使用します。 この関数には、ProtocolCallContextオブジェクトへのポインタ、送信先のクライアントのプリンシパル ID、UserMessage またはその派生クラス(TextMessage, BinaryMessage) のオブジェクトのポインタを格納したUserMessageDataHolderオブジェクトを引数に渡します。 ( Code 4.10 )。

特定のマッチメイクセッションに参加しているクライアント全員にメッセージを送信するには、MessagingClient::SendChat() 関数を使用します。 この関数には、ProtocolCallContext オブジェクトへのポインタ、送信先のマッチメイクセッションの ID、UserMessage またはその派生クラス(TextMessage, BinaryMessage) のオブジェクトのポインタを格納したUserMessageDataHolderオブジェクトを引数に渡します。

補足

非同期処理はゲームサーバーまでメッセージが到達した段階で成功を返します。 そのため、メッセージが送信先に到達したかは非同期処理の結果からは分かりません。 ゲームサーバーまで到達したメッセージは、送信先がゲームサーバーと切断されていなければ到達性が保証されます。

注意

MessageClient::SendChat() で想定している接続数は 32 台です。これを超える台数で本 API を使用する企画がある場合はお問い合わせください。

Code 4.10 テキストメッセージ送信処理のサンプル
void SendInstantMessage(MessagingClient *pMessagingClient, PrincipalID target, const String& message)
{
    TextMessage *pTextMessage = qNew TextMessage();
    UserMessageDataHolder oDataHolder = pTextMessage;

    // メッセージをセットする
    pTextMessage->SetTextBody(message);

    ProtocolCallContext oContext;
    if(pMessagingClient->SendInstant(&oContext, pid, oDataHolder))
    {
        oContext.Wait();
        if(oContext.GetState() != CallContext::CallSuccess)
        {
            // エラーハンドリング
        }
    }
    else
    {
        // エラーハンドリング
    }
}

4.22.2. メッセージの通知をハンドリングする

クライアントがメッセージを受信するためには、MessagingClient::Bind () 実行後に、MessagingNotificationHandlerMessagingClient::RegisterMessagingNotificationHandler () 関数でクライアントプロセスに登録されていなければなりません。 ハンドラが登録されると、クライアントが通知メッセージを受信するたびに、メッセージがハンドラへ送られ、DeliverInstantMessage コールバックが呼び出されます。 メッセージの処理方法をカスタマイズしたい場合は、MessagingNotificationHandler をサブクラス化して、システムコールバックを再実装します( Code 4.11 )。

DeliverInstantMessage コールバック内では、DataHolder::Get() によって格納しているメッセージを取得します。 取得したメッセージが、UserMessage, TextMessage, BinaryMessage のどのオブジェクトであるかは、DynamicCast() を使用します。

例えば、TextMessage のオブジェクトであるかは、TextMessage::DynamicCast() を使用して動的にキャスト可能かで判別します。 同様に BinaryMessage であるかは BinaryMessage::DynamicCast() を使用します。

以下にメッセージのオブジェクトの判別を行うメッセージの通知のハンドリング例を示します。

Code 4.11 MessagingNotificationHandlerのサブクラス化
class MyMessagingNotificationHandler: public MessagingNotificationHandler
{
public:
// MessagingNotificationHandlerからオーバーライド
void DeliverInstantMessage(const DataHolder& hMsg)
{
    if (TextMessage::DynamicCast(hMsg.Get()))
    {
        // TextMessage::DynamicCast() が成功する場合、受信メッセージは TextMessage です。
        TextMessage* pTextMsg = static_cast<TextMessage*>( hMsg.Get() );
        NN_LOG("from:%u message:%ls \n", pTextMsg->GetSenderID() ,pTextMsg->GetTextBody().CStr());
    }
    else if (BinaryMessage::DynamicCast(hMsg.Get()))
    {
        // BinaryMessage::DynamicCast() が成功する場合、受信メッセージは BinaryMessage です。
        BinaryMessage* pBinaryMsg = static_cast<BinaryMessage*>( hMsg.Get() );
        NN_LOG("from:%u size:%u \n", pBinaryMsg->GetSenderID() ,pBinaryMsg->GetBufferSize());
    }
};

4.23. MatchmakeReferee 機能を利用する

MatchmakeReferee 機能は、クライアントが送信した対戦レポートを集計、一致確認することによって公平な結果の記録を行うものです。 この記録を以降 個人統計 と呼びます。

個人統計は以下の内容になります。

  • プライマリ関連 NEX ユニーク ID ( NEX ユニーク ID を使わない場合はプリンシパル ID )
  • カテゴリ
  • 最後に使われたプリンシパル ID
  • 最近100戦の切断数
  • 最近100戦の違反数
  • 最近100戦のレポート不一致数
  • 最近100戦の勝ち数
  • 最近100戦の負け数
  • 最近100戦の引き分け数
  • これまで全ての切断数
  • これまで全ての違反数
  • これまで全てのレポート不一致数
  • これまで全ての勝ち数
  • これまで全ての負け数
  • これまで全ての引き分け数
  • レーティング値

レポート不一致 以外の項目については、クライアントがレポートしてきた内容を元に値を更新します。

4.23.1. 個人統計の記録単位

デフォルトの記録単位は プリンシパル ID 毎になりますが、NEX ユニーク ID を プライマリ関連 NEX ユニーク ID として登録することで、より細かい単位で記録することができます。

詳細は「 11.3.2.1. MatchmakeReferee 機能での NEX ユニーク ID の利用 」を参照してください。

NEX ユニーク ID を使用しないデフォルトの動作では、内部的に プリンシパル ID がプライマリ関連 NEX ユニーク ID として扱われます。

4.23.2. MatchmakeReferee 機能全体の流れ

  1. プライマリ関連 NEX ユニーク ID をセーブデータからロードする。
  2. NgsFacade::Login() でログインする。
  3. プライマリ関連 NEX ユニーク ID が無ければ UtilityClient::AcquireNexUniqueIdWithPassword () で取得する。
  4. UtilityClient::AssociateNexUniqueIdWithMyPrincipalId() を呼び出し、使用するプライマリ関連NEXユニークIDを宣言する。
  5. MatchmakeRefereeClient::GetOrCreateStats() で個人統計を作成もしくは取得する。
  6. 必要に応じて MatchmakeRefereeClient::GetNotSummarizedRound() で未集計のラウンドがないかチェックする。
  7. マッチメイクを行いマッチメイクセッションに参加、 P2P セッションを確立する。
  8. P2P セッションホストが MatchmakeRefereeClient::StartRound() を呼び出す。 NotificationEvents::RoundStarted の通知を受け取ったラウンド参加者は必要に応じて MatchmakeRefereeClient::GetStats() で対戦相手の個人統計を取得する。
  9. 途中抜けする場合、MatchmakeRefereeClient::EndRoundWithoutReport() を呼び出す。
  10. 試合が終了したら MatchmakeRefereeClient::EndRound() で各ユーザそれぞれが全ユーザ分の試合結果を qVector< MatchmakeRefereePersonalRoundResult > でレポートする。
  11. 集計完了の通知 ( NotificationEvents::RoundSummarized ) が来るまで待ち、試合結果を表示する。

補足

個人統計の管理を プリンシパル ID 単位で行う場合、 1 , 3 , 4 の処理は不要です。

4.23.3. サーバーのレポート集計処理

サーバーのレポート集計処理は、ラウンド参加者全員分のレポート ( MatchmakeRefereeClient::EndRoundWithoutReport() による空レポートを含む ) がサーバーに届くか、最初のレポート ( MatchmakeRefereeClient::EndRoundWithoutReport() による空レポートは含まない ) がサーバーに届いてから 1 分後に行われます。

MatchmakeRefereeClient::EndRoundWithoutReport() 関数は、 1分間のタイムアウトを待つことなく集計を開始させるためのものです。 この関数を呼ばなくても動作はしますが、集計処理の開始が遅れるので、 途中抜け等で MatchmakeRefereeClient::EndRound() が呼べない状況の場合は必ず呼んでおくようにしてください。

サーバーのレポート集計処理では、送信されたレポート ( qVector< MatchmakeRefereePersonalRoundResult > ) が他のラウンド参加者のレポートと一致しているかチェックされます。 qVector< MatchmakeRefereePersonalRoundResult > の一致確認は各 MatchmakeRefereePersonalRoundResult の pid が一致するもの同士で行われるので、 qVector 内で MatchmakeRefereePersonalRoundResult の順序は各ユーザ が送信するレポート間で同一である必要はありません。 送信されたレポートの数の過半数(レポート数が 5 なら 3 、 4 なら 3 )以上のデータが一致すればそのデータが最終的な結果となります。 この処理を以降 正規化 と呼びます。

正規化されたレポートはサーバー側で 1 時間保持されます。正規化されたレポートの取得は、 MatchmakeRefereeClient::GetRound() で行います。 正規化されたレポートを元にプライマリ関連 NEX ユニーク ID に紐付く個人統計が更新されます。 個人統計は永続情報になります。 個人統計の取得は MatchmakeRefereeClient::GetStats() で行います。 個人統計の詳細については、 MatchmakeRefereeStats を参照してください。

以下のケースは無効試合扱いになり、個人統計は更新されません。

4.24. あいことば機能を利用する

ユーザー間で共有する任意の文字列を「あいことば」と呼びます。あいことば機能とはそのあいことばを使いマッチメイクするものです。

あいことばを決めるユーザーは、どの範囲の知り合いにそれを共有するかによって、以下のような遊び方をすることができます。

  • 特定の数人にだけあいことばを共有して、少人数でマッチメイクして遊ぶ
  • SNSやイベントで繋がりのある不特定多数の相手に共有して、そのなかの誰かとマッチメイクして遊ぶ

協力・対戦プレイしたい相手と事前にフレンドになっておく必要がなく、あいことばと遊ぶ時間を知らせておくだけで手軽にマッチメイクできるのがあいことば機能の特徴です。

4.24.1. あいことばの仕様

あいことばは Unicode で最大32文字まで指定できます。文字種は UCS-2 の範囲で利用できます。

あいことばとして利用できない文字が設定された時には MatchmakeSession::SetCodeword() や MatchmakeSessionSearchCriteria::SetCodeword() で false が返されますのでユーザに適切なあいことばの設定をうながしてください。

あいことばとして使用する文言はユニークである必要はなく、同一のあいことばを設定したマッチメイクセッションを複数作成することができます。

同一のあいことばを設定したマッチメイクセッションが複数存在する場合には、MatchmakeExtensionClient::BrowseMatchmakeSession() で複数のマッチメイクセッションが取得できますので、ユーザに入りたいマッチメイクセッションを選択させてください。

不特定多数が参加するイベントなど、マッチメイクセッションを特定する必要がない状況ではオートマッチメイク機能を利用することもできます。

確実に特定のマッチメイクセッションに参加したい場合にはあいことば機能を使わずにギャザリングIDを指定してマッチメイクセッションに参加することを推奨します。

4.24.2. あいことばを設定したマッチメイクセッションを登録する

あいことば機能を利用するためには MatchmakeSession::SetCodeword() を使用してマッチメイクセッションにあいことばを設定する必要があります。

Code 4.12 マッチメイクセッション登録処理のサンプル
ProtocolCallContext oContext;
MatchmakeSession matchmakeSession, joinedMatchmakeSession;
matchmakeSession.SetGameMode(gameMode);
matchmakeSession.SetMaxParticipants(3);
matchmakeSession.SetMatchmakeSystemType(MATCHMAKE_SYSTEM_TYPE_ANYBODY);

// MatchmakeSessionにあいことばを設定する
matchmakeSession.SetCodeword("あいことば");

CreateMatchmakeSessionParam createMatchmakeSessionParam;
createMatchmakeSessionParam.SetSourceMatchmakeSession(matchmakeSession);
// マッチメイクセッションをサーバーに登録し、参加する
if (!matchmakeExtensionClient.CreateMatchmakeSession(&oContext, createMatchmakeSessionParam, &joinedMatchmakeSession))
{
    // エラー処理
    return;
}
// 非同期処理の結果を待つ
oContext.Wait();
if (oContext.GetState()!=CallContext::CallSuccess)
{
    // エラー処理
}

4.24.3. あいことばを設定したマッチメイクセッションに参加する

MatchmakeSessionSearchCriteria::SetCodeword() にあいことばを指定することによって、あいことばが一致するマッチメイクセッションを検索することができます。

MatchmakeSessionSearchCriteria::SetAttribute() を使用した属性値による検索とあいことば機能による検索はどちらか一方のみ利用が可能です。

あいことばと属性値の両方が検索条件として指定されていた場合には属性値は無視されあいことばによる検索が実行されます。

Code 4.13 マッチメイクセッション検索処理のサンプル
ProtocolCallContext oContext;
ResultRange range(0, 20);
qList<MatchmakeSession> lstResult;

MatchmakeSessionSearchCriteria searchCriteria;
searchCriteria.SetGameMode(gameMode);
searchCriteria.SetMatchmakeSystemType(MATCHMAKE_SYSTEM_TYPE_ANYBODY);

// マッチメイクセッションの検索条件にあいことばを設定する
searchCriteria.SetCodeword("あいことば");

matchmakeExtensionClient.BrowseMatchmakeSession(&oContext, searchCriteria, range, &lstResult);

4.24.4. あいことばをオートマッチメイク機能で利用する

あいことばはオートマッチメイク機能でも利用することができます。

利用するには MatchmakeSession::SetCodeword() と MatchmakeSessionSearchCriteria::SetCodeword() を使い、マッチメイクセッションおよび検索条件にあいことばを設定してください

Code 4.14 オートマッチメイク機能のサンプル
String codeword = "あいことば";

ProtocolCallContext oContext;
MatchmakeSession matchmakeSession, joinedMatchmakeSession;
matchmakeSession.SetGameMode(gameMode);
matchmakeSession.SetMaxParticipants(3);
matchmakeSession.SetMatchmakeSystemType(MATCHMAKE_SYSTEM_TYPE_ANYBODY);

// MatchmakeSessionにあいことばを設定する
matchmakeSession.SetCodeword(codeword);

AutoMatchmakeParam autoMatchmakeParam;
autoMatchmakeParam.SetSourceMatchmakeSession(matchmakeSession);
autoMatchmakeParam.SetJoinMessage(NEX_T("joined"));
qList<MatchmakeSessionSearchCriteria> listSearchCriteria;

MatchmakeSessionSearchCriteria criteria;
criteria.SetGameMode(gameMode);
criteria.SetMatchmakeSystemType(MATCHMAKE_SYSTEM_TYPE_ANYBODY);

// MatchmakeSessionSearchCriteriaにもあいことばを設定する
criteria.SetCodeword(codeword);

listSearchCriteria.push_back(criteria);
autoMatchmakeParam.SetSearchCriteria(listSearchCriteria);
matchmakeExtensionClient.AutoMatchmake(&oContext, autoMatchmakeParam, &joinedMatchmakeSession);

4.25. エラーハンドリング

4.25.1. API 呼び出し時のエラー

ゲームサーバーとの通信が発生する非同期 API が false を返した場合は以下の要因が考えられます。

  1. 別の非同期処理中の、CallContext オブジェクトを引数として渡した。
  2. ServiceClient::Bind() が実行されていない。
  3. ゲームサーバーへのデータ送信が失敗(ゲームサーバーとの接続が切れていたなど)

全ての非同期関数は、(1)から順番にチェックします。(1) (2) はプログラムのエラーとなりますが、 (3) はネットワークの状態によっては発生します。この状態からゲームサーバーとの通信を復旧させるためには再度ログインする必要があります。 そのため、「通信エラーが発生しました」とエラーメッセージを表示して通信の終了処理を行ってください( Code 4.15 )。

4.25.2. 非同期処理のエラー

非同期処理の結果は CallContext::GetOutcome() で取得可能です。 関数ごとにアプリケーションでハンドリングするべきエラーについては、リファレンスマニュアルに記載しています。

記載されていないエラーが発生した場合には、ErrorCodeConverter::ConvertToNetworkErrorCode() でネットワークエラーコードを取得し、エラー・EULAアプレットに渡した後に通信の終了処理を行ってください( Code 4.15 )。

Code 4.15 エラーハンドリングの例
MatchmakeExtensionClient client;
ProtocolCallContext context;

// API 呼び出しのエラーハンドリング
if(!client.JoinMatchmakeSession(&context, gid, NEX_T("join message")))
{

    // 処理中のCallContextを渡した
    NN_ASSERT(context.GetState() != CallContext::CallInProgress);

    // Bind が実行されていない
    NN_ASSERT(client.GetCredentials() != NULL);




    // その他、ネットワークに接続されていない場合など
    // 「通信エラーが発生しました」メッセージを表示(エラーコードは不要)
    // ログアウト・終了処理に移行
}

// 非同期処理のエラーハンドリング
context.Wait(); //ここでブロックして待ちます


if(context.GetState() != CallContext::CallSuccess)
//注意: contextをCancelする場合には、CallContext::CallCancelledとの比較も必要です。
{
    qResult result = context.GetOutcome();

    // アプリケーションでハンドリングすべきエラー
    if(result == QERROR(RendezVous, SessionVoid))
    {
        // マッチメイクセッションが存在しない
    }
    else if(result == QERROR(RendezVous, SessionFull))
    {
        // マッチメイクセッションが満室
    }
    else
    {

        u32 code = ErrorCodeConverter::ConvertToNetworkErrorCode(result);
        //エラー・EULAアプレットを起動



        // ログアウト・終了処理に移行
    }
}