ゲームへの案内

マッチング完了後の処理

プロトコルは、マッチング完了後は次のような手続きを踏んでゲームを開始するようになっている:
  1. マッチング完了の認識
  2. MS->GS: ゲーム準備リクエスト
  3. GS->MS: ゲーム準備OK
  4. MS->CL: ゲーム案内通知
  5. CL->GS: ゲーム接続

RoomModule

マッチングサーバーの仕事の大部分はフレームワークがやってくれる。

モジュールで実装する必要があるのは、ゲーム準備リクエスト(ReadyGameRequest) とゲーム案内通知(GuideGameNotify) をカスタマイズすることだけである。

void SampleRoomModule::setupReadyGameRequest(Room* _pRoom, ReadyGameRequest* pReq)
{
}

void SampleRoomModule::setupGuideGameNotify(Room* _pRoom, int memberIndex, GuideGameNotify* pMsg)
{
}

サンプルではカスタマイズしていない。

GameServer::handleReadyGameRequest

ゲームサーバーでは ReadyGameRequest を受け取って処理する必要がある。

ReadyGameRequest には以下のメンバ変数が含まれている:
class ReadyGameRequest: public ExtMessage< 0xD2, ReadyGameRequest >
{
      uint16_t domainIndex;
      uint16_t matchIndex;

      uint16_t tagRoomId;
      std::vector< MemberInfoIon* > members;
};
class MemberInfoIon: public ExtIon
{
      std::string userId;
      std::string secret;
};

domainIndex
AssignRoomRequest をした時のドメインインデックス。
複数のルーム集合を扱う場合は、この値でどのドメインかを判定する。
matchIndex
AssignRoomRequest では複数のルームを確保する。このルーム集合に対し 0 から振られたルーム番号。
マッチングサーバー上でのルーム番号とは異なるので、混同しないように注意。
tagRoomId
マッチングサーバー上でのルーム番号。ゲームサーバーで使うことは無いが、ReadyRoomReqeust の応答に含める必要がある。
members
部屋に入っているメンバー情報。ユーザーID とシークレット文字列である。
シークレットは、マッチング完了時に生成していたもの。マッチングサーバーからゲームクライアントに渡され、ゲームクライアントがゲームサーバーへログインする時に示される。
このシークレット情報をどう扱うかはアプリケーションが決める。特にセキュリティの要請が無ければ、単なるワンタイムパスワードとして用いればよいだろう。
さて、サンプルゲームサーバーのハンドラ関数は次のようになっている:
void GameServer::handleReadyGameRequest(ReadyGameRequest* pMsg)
{
   ResponseMessage res(pMsg);
   uint8_t err;
   printf("<< ReadyGameRequest\n");

   res.setDomainIndex(pMsg->domainIndex);
   ReadyGameResponse::SetTagRoomId(&res, pMsg->tagRoomId);
   if (! checkMatchId(pMsg->domainIndex, pMsg->matchIndex)) {
      err = ResponseMessage::ERR_BADPARAM;
      goto fail;
   }
   {
      Game* pGame   = &m_vecGame[pMsg->domainIndex];
      Match* pMatch = &pGame->vecMatch[pMsg->matchIndex];
      if (! pMatch->open(pMsg)) {
         goto fail;
      }
   }
   goto fin;

  fail:
   res.setResult((err != 0)? err: ResponseMessage::ERR_UNKNOWN);

  fin:
   post(m_matchCommId, &res);
   printf(">> ReadyGameResponse: %d\n", res.getResult());
}

ResponseMessage に、先ほど説明した tagRoomId を拡張データとして付加していることに注意。

後は ReadyGameRequest の情報に従って、ゲーム場(Match) をセットアップしている。

ゲームへのログイン

ゲームサーバーの準備が終わった後で、各クライアントに GuideGameNotify が投げられる。 ゲームクライアントはこの情報を元にゲームサーバーへ接続し、ゲームを開始する。

どうゲームを始めるかはゲームサーバーの仕様範囲であり、マッチングフレームワークは何も強制しない。ただ、利便のために LoginGameRequest というメッセージを提供している。

サンプルでは、ゲームクライアントはこのメッセージを利用してゲームサーバーへのログインを行っている。

void MainDialog::handleGuideGameNotify(GuideGameNotify* pMsg)
{
   m_msgLoginGameRequest.userId = m_userId;
   m_msgLoginGameRequest.secret = pMsg->secret;
   m_msgLoginGameRequest.tagDomain = pMsg->tagDomain;
   m_msgLoginGameRequest.tagMatch = pMsg->tagMatch;
   m_nine.connectGameLater(pMsg->host.c_str(), pMsg->port);
}

void MainDialog::onGameConnect(bool succeed)
{
   postGame(&m_msgLoginGameRequest);
}

GuideGameNotify に含まれている、シークレット情報とタグ情報をそのまま LoginGameRequest へコピーする。実際にこのメッセージを送信するのは、ゲームサーバーへ接続した後である。

ゲームサーバーのアドレスは GuideGameNotify に含まれている。この情報を使って接続を行う。