この通信は何のためにあるのか

ロビーに入ったあと、部屋を作る・部屋を探すという操作を受け持つのがcivgs-gamebrowserである。タイトルにあるQRSBは、ここで使われる、向きの違う2つの通信の略称である。どちらもGameSpyが提供していた仕組みの名前で、ロビー画面の部屋一覧はこの2つで成り立っている。

  • QRは、Query & Reporting(クエリ・アンド・レポーティング)の略である。部屋を作った側(ホスト)のクライアントが、「こんな部屋を作りました」「この部屋はまだ開いています」と、自分の部屋の状態をサーバーに報告するための仕組みである。
  • SBは、Server Browser(サーバーブラウザ)の略である。部屋を探す側(参加者)のクライアントが、サーバーから部屋の一覧を取得するための仕組みである。「サーバー」はゲームの部屋のことを指しており、その一覧を眺める機能、という意味の名前である。
略称ポート役割
QRUDP 27900部屋の登録と生存報告
SBTCP 28910部屋一覧の取得と増減の通知

ホストが部屋を作ると、クライアントはcivgs-gamebrowserのQRに、部屋の情報を報告する。サーバーはこれを部屋の台帳として持ち、参加者がSBで一覧を要求したときに返す。ホストは部屋が開いている間、定期的に報告を続ける。部屋が増えたり減ったりしたときは、一覧を開いている参加者にも通知される。ホストからの連絡がしばらく途絶えた部屋は、台帳から自動的に削除される。

ホスト部屋を作る人civgs-gamebrowser部屋の台帳1部屋につき1件連絡が途絶えると自動的に削除参加者部屋を探す人QR / UDP 27900登録・生存報告(定期的に)SB / TCP 28910一覧の取得増減の通知対戦データはホストと参加者の間を直接流れるゲームブラウザは「相手の住所」を教えるだけで、対戦データは通らない
ホストはQRで部屋を登録し、参加者はSBで一覧を取得する。対戦中のデータはゲームブラウザを経由しない。

なぜ普通のサーバーでは動かないのか

QRもSBも、GameSpy独自の形式で作られた通信である。特にSBの応答は扱いが厳しく、それらしいデータを返せばよいというものではない。

  • 一覧の応答は保護されている。 応答は暗号化されており、クライアントが期待する決まった手順どおりに組み立てないと、読むことができない。
  • 応答の中身は、クライアントが「そう書いてある」と信じて読む。 各部屋のデータには、続く内容の構成を示す情報が付く。実際のデータと食い違うと、クライアントは以降を取り違えたまま読み進める。実際にこの食い違いを起こすと、Civ4クライアントがクラッシュすることを確認している。

このため、civgs-gamebrowserはこの形式に合わせて作られたものを使う必要がある。Civ4 Internet Lobbyでは、pylobbyのゲームブラウザ実装をベースにしている。

参加方式を決める通信である

IRCの通信(TCP 6667)とは違い、この通信は**「部屋に参加できるかどうか」に直接関わる**。サーバーは一覧を返すとき、参加者に対して「サーバーから見たあなたの接続元アドレス(公開IPアドレス。インターネットから見える、自宅の回線のアドレス)はこれです」という情報も伝える。参加者側のクライアントは、これを一覧の中のホストの公開IPアドレス(部屋を登録してきた接続元)と比べ、次のように接続方式を決める。

参加者が部屋一覧から参加する部屋を選ぶ参加者の公開IP(サーバーが通知)とホストの公開IP(部屋の登録元)は同じか?同じ違う同一ネットワークとみなすホストのLAN内アドレスへ直接接続する別ネットワークとみなすNAT越え(NATNEG、UDP 27901)を経由して直接接続するホストと参加者の直接通信以降はサーバーを経由しない
参加者が自分の公開IPとホストの公開IPを比べて、LAN直結にするかNAT越えにするかが決まる。

つまり、サーバーから見た「ホストの接続元IP」と「参加者の接続元IP」が一致するかどうかで、参加の経路が変わる。ロビーサーバーをインターネット側(WAN)に置いているのは、この判定が意図どおりに働くようにするためでもある。ホストと参加者が同じ家庭内ネットワークにいれば、どちらも同じグローバルIPアドレスとして見えるので、LAN内で直接つながる。別々の場所にいれば、NAT越えの仕組み(家庭のルーターの内側にいる者どうしが、ポート開放をしなくても直接つながるための手順)によってつながる。NAT越えの流れはシステム全体像と通信シーケンスを参照してほしい。

この判定は、同じサーバー、同じパケットキャプチャの中で、両者の接続元IPが揃った瞬間に失敗から成功に変わる実験によって確かめている。

ホストが申告する情報は原因ではない

ホストのクライアントは、部屋をサーバーに登録するとき(QRの報告)、大きく2種類の情報を送る。

  • 部屋の表示用の情報: 部屋名、マップ、人数など。ロビーの部屋一覧に表示される内容で、参加者にも届く。
  • ホスト自身のネットワークに関する情報: 「自分の公開IPアドレスとポート番号はこれ」「NAT越えが必要か」といった項目。ホストのクライアントが自分で判断して書き込むもので、これをホストの自己申告値と呼んでいる。

サーバーは、自己申告値が正しいかどうかを確かめず、そのまま台帳に載せている。実際に、ホストが申告した公開IPアドレスは、回線の本当の公開IPアドレスと食い違っていた。ルーターの内側にいるホストのクライアントは、自分の公開アドレスを正確には知らないためと考えられる。

このため、当初は「ホストの自己申告値が間違っているせいで、参加できないのではないか」と疑っていた。しかし、参加者が一覧を要求するときに指定する項目に、自己申告値は含まれていない。つまり、自己申告値は参加者には届かず、参加できるかどうかにも影響しないことが確認できている。参加可否の判定に使われるのは、サーバー自身が観測した接続元アドレス(前の節の図の判定)である。参加できない原因を探すときは、自己申告値は切り分けから外してよい。

症状から見る切り分け

部屋が見えない、参加できないときに、プレイする側が試せることをまとめる。ロビーサーバーの中は見えないので、次の順に「自分の側で正しいか」を確かめていく。

  1. hostsファイルが参加方法のとおりになっているか。 一部だけ追記した場合に抜けが出やすいので、内容を丸ごと貼り直すと確実である。書き換えたあとは、Civ4を再起動する。
  2. 稼働状況・お知らせで、ゲームブラウザの行が「● 稼働中」か。 「● 稼働中」なら、この記事のQR・SB・NAT越えを処理するサーバー側の準備はできている(この3つは同じプロセスである)。「● 停止中」なら、サーバー側の問題なので復旧を待つ。
  3. 参加する全員のCiv4が、同じバージョン(3.19)か。 バージョンが違うと、接続できない。
  4. パソコンのウイルス対策ソフトやファイアウォールが、通信を遮断していないか。

そのうえで、症状ごとの目安は次のとおりである。

  • 自分が作った部屋が、他の人の一覧に出ない: 上の1〜4を、部屋を作ったホストのパソコンで確認する。確認したあとで、部屋を作り直してみる。相手側のhostsが原因のこともあるので、相手にも1を確認してもらう。
  • 部屋の一覧が空のまま、または一覧を取得できない: 上の1と2を確認する。
  • ロビーの処理が、途中で次々に失敗する: hostsファイルに、お使いのエディション名の付いた名前が、まとめて登録されていない可能性が高い。1のとおり、内容を丸ごと貼り直す。
  • 一覧には部屋が出るが、参加操作で「交信しています」のまま固まる: hostsファイルに、お使いのエディション名の付いた、NAT越え用の名前が登録されていない可能性が高い。1のとおり、内容を丸ごと貼り直す。

それでも直らないときは、掲示板に報告してほしい。次の内容を書いてもらえると、原因を絞りやすい。

  • お使いのエディション(BTS日本語版など)と、OS(Windows、Linuxなど)
  • 部屋を作る側(ホスト)か、参加する側か
  • どの操作で、画面にどう表示されたか

ログインの側の仕組みについては、ログイン時のGameSpy Presence(GP)通信(TCP 29900・29901)についてを参照してほしい。