「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く | GameBusiness.jp

「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く

ゲーム開発 サーバー・ホスティング

「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く
  • 「プログラミングに国籍はない」日本メーカーと協業して優れたオンラインゲーム運用を―Nettentionペ・ヒョンジク氏に訊く

サーバーエンジン万古風霜ストーリー

筆者紹介
ベ・ヒョンジク(Nettention 創業者)
韓国で最も有名なゲームサーバー開発者の一人。ゲームサーバー&ネットワークエンジンである「プラウドネット」を開発した人物です。プラウザネットは現在、200社ほどのゲーム開発会社に提供されています。


はじめに

世の中にはゲーム開発に使用されるサーバー&ネットワークライブラリーが多くあります。通称、サーバーエンジンと呼ばれています。サーバーエンジンは、サーバー側のモジュールとネットワークのクライアントモジュールから構成されています。


サーバーエンジンは、商用ソフトウェアなどを購入して使用するものもありますが、オープンソースも数多く存在します。ほとんどの場合は、オープンソースが使用されています。オープンソースは無料という長所もありますが、何よりもソースが公開されているので制約なしに何でもできるのがその理由です。しかし、商用サーバーエンジンも大きな長所を持っています。オープンソースと違い、トラブルが発生しても自分の力で全ての問題を解決する必要がありません。

商用サーバーエンジンを使用するとゲーム開発者の手によって、便利にトラブルを解決することができます。すなわち、お金を払って時間を買うわけです。しかし、私のように商用サーバーエンジンを提供する立場では、その反対になります。ゲームサーバーを開発をしてオンラインゲームを発売開始する過程では、さまざまなことが生じます。この時、私たちはトラブルを解決するために時間を使うことになります。簡単に5分で解決される場合もありますが、半月経っても解決できない場合もあり、飛行機に乗って現場へ行くと解決される場合もありました。

トラブルを解決する過程は当然大変です。しかし、トラブルを解決する過程で得る経験は大きいです。私は1995年からゲーム会社でプログラマーとして働きました。そして、2004年まで多様なオンラインゲームを開発しました。この13年間、私は多くのノウハウを得ました。ところが、2008年から2016年の現在までゲームサーバーエンジンの開発と維持補修やトラブルシューティングをしながら蓄積した経験は、ここ13年より何倍も多かったように感じます。

ここで、これまで私が得た経験において、皆さんの役に立つと考えられるものをいくつか選定して紹介したいと思います。


プラウドネットを使ったモバイルRPGゲーム『MARVEL FUTURE FIGHT』

パケット送信と受信コードの自動生成

私はゲームサーバー開発を1997年から始めました。これは私の誇りですが、Windows NT基板のIOCPでゲームサーバー開発方法を開拓した者の一人が私です。当時、ゲームサーバー開発する上で、不便であった点が一つありました。それは、パケットを送受信する過程をいちいちコーディングしなければならない点でした。

パケットを送受信する過程をいちいちコーディングすることは不便であるだけではなく、間違いの起きる可能性があるという短所がありました。下のコードを見てみましょう。


パケットを送る側ではx、y、zを送っています。しかし、受ける側ではx、zのみをパケットから取り出しています。当然、このコードはトラブルを引き起こします。皆さんのプログラムが小さければ、このような間違いはしないと思います。しかし、皆さんのプログラムが大きくなって、多くの人が皆さんのプログラムを修正したりしたら話が変わります。

このトラブルをいかに簡単に解決できるでしょうか?それは、パケットの送受信コードを人ではなくコンピューターが代わりに作るようにすれば良いです。例えば、あなたがキャラクターの移動と攻撃を処理する送信コードと受信コードを作成するとしましょう。コンピューターがあなたのコードを代わりに作るようにするために、あなたは下記のように関数を定義すれば良いです。


そして、あるプログラムを実行して上記の関数定義を分析するようにします。そして、送信コードと受信コードを自動生成するようにしましょう。そうすると、このように生じたコードが生成されます。


もう、皆さんがすべきことはすべて終わりました。他のコンピューターにパケットを送信したい場合は、自動生成されたコードの関数を呼び出すことだけで良いのです。パケットを受けるコンピューターでは同名の関数が呼び出されます。

このようなテクニックをRPC、すなわち、Remote Procedure Callと呼びます。今はGoogle protobufのようなものがこのようなことをしたりしますが、私はこの技法を2000年に初めて作って、この技法を実現する方法を書籍にして出版したりしました。



私はこの書籍を英語で作成しました。この本はのちに中国語にも翻訳されました。

私が作ったサーバーエンジン「プラウドネット」は、2008年に発売開始されましたが、その中には私が自ら制作したRPCが入っていました。

サーバーエンジンの簡単な使い方

プラウドネットにはクライアントとサーバー間のネットワーキングを行う機能と、クライアント間のP2Pネットワーキングをする機能が一緒に入っています。プラウドネットの特徴の一つは、この機能を簡単に使えるようにすることにあります。

ここで、プラウドネットの使用方法を見てみます。

ゲームサーバーをオンにするためには、NetServer インスタンスを作ってStart関数を呼び出すだけで良いです。たったのこれだけです。


クライアントがサーバーに接続するためには、NetClient インスタンスを作って Connect 関数を呼び出すだけで良いです。そうすると、サーバーではクライアントが入って来たことが分かって、OnClientJoin 関数をコールバックします。皆さんはOnClientJoin関数を作れば良いのです。一方、クライアントではサーバーとの接続が完了してOnJoinServerComplete 関数がコールバックされます。やはり、皆さんはこの関数を実現すれば良いのです。


クライアントとサーバー間のネットワーキングは、RPCを利用すれば良いです。

クライアントでサーバーにKnight_Moveメッセージを送るためには、クライアントではKnight_Move 関数を呼び出して、サーバーでKnight_Move関数を作って入れれば良いです。当然ですが、リアルタイムマルチプレイオンラインゲームで要する速いスピードで処理されます。

サーバーからクライアント55へKnight_ShowMove メッセージを送信するためには、サーバーからKnight_ShowMoveを呼び出せば良いです。この時、一番目のパラメーターでクライアントID55を入れれば良いのです。


クライアント55とクライアント56、57がお互いにP2Pで話し合うためには、これらをP2P グループで縛れば良いです。P2PグループはQQメッセンジャー画面のようなものと見れば良いです。同じメッセンジャー画面の人々の同士ではお互いに話し合うことができますが、メッセンジャー画面に招待されなければ話し合うことができないということと同じです。

先にサーバーよりCreateP2PGroupを呼び出しますが、パラメーターで55,56,57を入れましょう。そして、P2PグループのID 90が出たとします。

クライアントは自分がP2Pグループ90に入っているということをすぐ分かるようになります。そして、P2Pグループ90には自分自身及び他のクライアント55,56,57があることが分かるようになります。

クライアント55が他のクライアント56にKnight_Attackを送るためには、Knight_Attackを呼び出しながらパラメーターとして56を入れれば良いです。そうすると、クライアント56では直ちにKnight_Attack 関数が実行されます。クライアント57がKnight_Attackを呼び出しますが、P2Pグループ 90をパラメーターで入れれば、クライアント55,56,57は全てKnight_Attackをコールバックしてもらうようになります。


このような点を利用すると、オンラインゲームを開発をする時にサーバーにかかる負荷を減らしながら、プレーヤー間の位置を取り交わす時にかかる時間を短縮することができます。例えば、MMORPGでほとんどのゲームロジックをクライアントとサーバー間のメッセージングで処理しますが、プレーヤーの移動を知らせるメッセージはP2P通信とC/S通信を一緒に使用します。


大規模マルチプレイオンラインゲーム、すなわち、MMOゲームではプレーヤーの動きを他のプレーヤーに送りません。プレーヤー55の動きはプレーヤー55が見られる他のプレーヤーへのみ送られます。プラウドネットは、このため内部実現がよくできています。「プレーヤー55を見られるプレーヤー」という意味のP2Pグループを作っておいて、プレーヤー55を見られる他のプレーヤー57が視野内に入って来ればプレーヤー57をP2Pグループに入れれば良いです。反対に、プレーヤー58が視野の外へ行けばプレーヤー58をP2Pグループより除外すれば良いです。皆さんは、このためにNetServerのJoinP2PGroup、LeaveP2PGroupを呼び出せば良いです。プラウドネットはこれらをよく呼び出しても副作用なしによく作動するようになっています。


開発者がC/SネットワーキングとP2Pネットワーキングを自由自在に使うことができるようにするためには、サーバーエンジンはそのバックグラウンドでいかなる状況でもよく作動するようにしなければなりません。そして、多様なネットワーク環境でも間違いなしに作動しなければなりません。

待機時間のない P2P 接続過程

開発者はP2Pネットワークゲームやアプリケーションを開発する時、サーバーエンジンが下記のように作動することを希望します。

「クライアント間でP2P接続をするための関数を呼び出すと、直ちに無条件で通信できなければならない」

P2P通信をするためには、NAT Hole punchという過程を経なければなりません。この過程は、0.1秒から2秒ほど必要となります。まれに、ホールパンチングが全くできない場合もあります。そのような場合に、ホールパンチングになるまで待ったり、ホールパンチングされない場合のための別途のコードを作ったりすることは面倒です。

私は、このような不便を解決するために、次のような方法を選択しました。

「まだホールパンチングができていないから、リレー通信をさせます。その間にバックグラウンドでホールパンチングを試みます。ホールパンチングに成功したら、その時からP2P通信をさせます。」

これを作るのは、あまり難しいことではありません。しかし、トラブルとなるのはP2P間のreliable メッセージを取り交わすことでした。reliable メッセージングとは、TCPのように「私が送るものは相手が必ず受け取る」ことを保障することです。また、TCPのreliable メッセージングとは、sender windowにあるパケットを相手が受け取ったということが確認できなければ再び送るアルゴリズムです。クライアント同士でP2Pリレー通信をしていた間にホールパンチングに成功すれば、sender windowも安全にホールパンチングに成功した通信チャンネルに移動されなければなりません。初めてプラウドネットを開発した時、この部分が私にとって初めての挑戦でしたが、私はこれを見事に解決することができました。

発売開始後のトラブル発生と解決の攻防戦 #1

プラウドネットを発売開始する前は、私は11年間蓄積してきたゲームサーバー開発のノウハウのみで十分かと思っていました。私が作ったサーバーエンジンを利用して作ったゲームが東南アジアでサービス開始する前までのことです。

プラウドネットを使った一ゲームがシンガポールでサービスを開始して、マレーシアなどの周辺国でもプレイがされました。ところが、このゲームをプレイしていた人々のうちの一部は、ゲームができない状況にありました。このトラブルは短時間では解決できませんでした。結局、私は飛行機に乗ってシンガポールに行き、現場でトラブルの原因を探しました。その結果、ゲームユーザーのインターネットルーターが原因でした。

プラウドネットを開発する当時、私はP2Pホールパンチングのための技法を調査しました。インターネットルーターは大きく4種類に分けられます。これらのうち、ホールパンチングが一番難しい機種は、Symmetric NATルーターです。Symmetric NATでは、ホールパンチングを普通の方法ではできません。その代わりに、フォト番号の予測技法が必要となります。100%ではありませんが、ポート番号の予測技法を使えば、ホールパンチングは可能です。

ところで、問題点はこのポート番号の予測技法が副作用を起こすということです。下図で両クライアントはSymmetric NATの後にあると仮定してみましょう。両クライアント間にポート番号の予測が成功するために、複数回のホールパンチングを試みなければなりません。


この過程で、NATルーター、すなわちインターネットルーターでは、数多くのport mapping entryがルーターメモリー内に発生するようになります。しかし、セキュリティ上の理由で、いくつかのインターネット共有は数千以上のport mapping entryを作ることができなくなっています。

このトラブルは結局解決しました。port mapping entryがよく発生する副作用を減らしました。Symmetric NATのためのポート予測技法は、数秒が経ってもホールパンチングがされない時、最後の手段として行うことにしました。

その他にも、一部のインターネットルーターでホールパンチングが消えるトラブルもありました。下図では、クライアント1がルーターの後ろにあります。この時、クライアント2とホールパンチングをします。そして、クライアント3,4,5,6ともホールパンチングをします。


ほとんどのコンピューターではこれは問題になりません。しかし、一部のインターネットルーターではこのようにすると、すぐさまクライアント2との通信が途絶されました。面白いことは、中国のネットカフェのルーターではこのような現象が発生しないということです。韓国の一部のインターネットルーターで発生しました。

ホールパンチングの正体は、インターネットルーター内でのport mapping entryの追加です。なお、内部endpointと外部 endpointからなるtupleの集合をport mapping entryと呼びます。

トラブルのあるルーターでは、内部endpointが等しく、外部endpointが異なる場合に承諾しませんでした。これが原因だったのです。

これはこのように解決しました。Client 1からClient 2,3,4,5と通信する時に使われる内部 endpointも違うようにしたのです。Client 1はClient 2,3,4,5と通信する時にお互いに違う UDP socketを使うようにしました。

攻防戦 #2

ゲームサーバーで使うメッセージングのほとんどは、TCPの利用で十分です。TCPは非常によく作られたプロトコルで、ほぼ全ての状況で安定的に作動します。パケットの遺失率が1%未満であるか、レーテンシーが100ミリ秒以下なら、TCPを利用しても問題になりません。

しかし、インターネット環境があまり良くない時は、結果が変わります。TCPはパケット遺失が発生する時、長い遅延が発生します。オンラインゲームでTCPのみを利用する場合、品質が良くないインターネットでは、キャラクターの動きがたまに一時的に停止する現象が発生します。この現象をstutteringと言います。

私のサーバーエンジンは、このような点を考慮して、TCPとUDPを全て使います。ゲーム開発者は、ほぼ全ての場合にTCPを使います。しかし、キャラクターの移動や機関銃の乱射のためのパケットは、UDPを利用した方がもっと効果的です。

UDPはTCPよりもプログラミングしやすいです。UDPは相手のendpointだけ知っていれば、送信する関数と受信する関数でパケットを簡単に取り交わすことができます。TCPは相手のendpointを知っていてもconnect, listen, accept過程を処理しなければなりません。理解を深めていただくために、下にコードを作成してみました。あまり差が見えませんが、epollやIOCPなどを交ぜて使い始めると、急に複雑になります。


しかし、ライブサービスでは反対になってしまいます。多様なネットワーク環境ではTCPよりUDPの方が取り扱いがややこしいです。TCPはネットワーク速度が耐えられない量以上の送信をする場合、回線速度に合わせて送信速度が遅くなります。しかし、UDPはこのような状況で、もっと深刻なトラブルを起こします。

私は、このトラブルを中国で経験し、その解決には半月ほどかかりました。結局はトラブルの原因を見つけて解決しましたので、どのように解決したのかを下記で説明します。

中国でこのトラブルに初めて接した時、TCP socketで発生したエラーコードは、ECONNABORTEDでした。このエラーコードが何を意味するのか文書で確認しました。


上記を見ると、“data transmission time-out”と表現されています。このエラーコードを理解するためには、TCPがどのように作動するかを知らなければなりません。

TCPはARQアルゴリズムを基本にして作動します。ARQアルゴリズムでは、一方からデータを送った時、もう一方で「受け取った」と知らせます。「受け取った」ことを私たちはAcknowledgement、すなわち、ACKと呼びます。データを送る側ではACKが来なければ、しばらく後でデータを再度送ります。ACKが続けて来なければ、送る方ではデータを繰り返して送ります。データを送ったのにかかわらず、ACKが20秒以内に到着しなければ、OSはTCP接続を遮断してしまいます。そして、TCP socketはエラーコードECONNABORTEDを返還します。

このトラブルは、次のように簡単に再現できます。


機器A、Bは同じLANにあります。Cは遠く離れています。ここで次のような実験をします。

Aは Cに TCP接続をして、とても少ない量のデータを取り交わします。この状態では問題がありません。次の実験を追加します。BはCとUDPで大きい量のデータを取り交わします。この実験を追加して間もなく、AとC間のTCP接続は切れるようになります。そしてECONNABORTED エラーコードがリターンされます。

このような現象は、ネットカフェでも発生することがあります。


例えば、この図のようにネットカフェに複数の機器があると仮定して見ましょう。左側にある2台の機器間にUDPで大きい量の通信をします。左側の二つの機器はファイル送信をするアプリと仮定してみましょう。この時、面白いことが発生することがあります。残りの機器が、サーバーとの接続がたまに切れるようになるのです。ファイル送信をするアプリと関係ないアプリ、たとえばメッセンジャーやゲームなども同じです。

どうしてUDPだけこのような問題が起こるのでしょうか?それは混雑制御アルゴリズムによって説明できます。

インターネットを構成する様々なネットワーク機器は、結局コンピューターです。ネットワーク機器は各自が処理することができるパケット量が決まっています。この量を超過する量のパケットが到着するようになれば、ネットワーク機器はパケットを捨てるようになります。これがパケットロスを引き起こします。


プラウドネットの使われたスポーツゲーム 『チャグチャグ』

もし、UDPの通信量があまりにも多い場合は、ネットワーク機器はUDPパケットを処理することに全力を尽くさなければなりません。しかし、それではTCPパケットを処理できる機会を失われてしまうようになります。そして、TCPはretransmissionをするようになります。これを20秒間続けて試すと結局TCP接続が切れます。

TCPだけを使う場合、このような問題は発生しません。なぜなら、TCPは自主的な混雑制御アルゴリズムを持っているからです。これをTFRC、すなわち、TCP friendly rate controlと呼びます。

UDPは、自主的な混雑制御アルゴリズムを持っていません。したがって、UDPネットワークプログラミングをする時は、混雑制御を考慮しなければなりません。このトラブルを解決するために、私たちは多くの時間がかかりました。


プラウドネットの使われたモバイルゲーム『セブンナイツ』


MMO ゲームでのマルチコアを活用する

ゲームセッション一つにプレーヤー数が数十人以下である場合は、サーバー開発が簡単です。私たちはこのようなゲームサーバーをシングルスレッドモデルで作れば良いのです。そして、ゲームサーバープロセスをCPUコア数だけ実行すれば簡単に解決されます。

ところが、MMOゲームでは話が変わります。ゲームセッション一つにプレーヤー数千人が入るようになります。このような場合に、ゲームサーバーをシングルスレッドで作ってしまうと、1個のCPU コアのみを使うため性能限界を起こすことがあります。


このような問題を解決するためには、1個のMMOゲームサーバープロセスは、マルチスレードで作動をしなければならないこともあります。

Windows サーバーでは、IOCPを利用して、thread poolingを簡単に開発することができます。しかし、Linuxではepollを使わなければなりません。epollはシングルスレッドに相応しいAPIです。したがって、thread poolingを実現しやすくありません。

結局、このトラブルも私たちは解決しました。Linuxにも関わらず、thread poolを置いて、epollオブジェクトを各スレッドに置きました。そしてsocketをそれぞれのepollに分配しました。

epollや IOCPを使ってもゲームサーバーでかなり多くの時間を占めることは、socket APIの send関数の特徴です。MMOゲームサーバーのロジック処理が少ない時間を占めても、send関数が多くの時間を占めるようになります。


プラウドネットの使われたIPカメラ「Forview Pixie」

私たちはこのような問題も解決しました。send関数の呼び出しをスレドプールで行うようにしました。MMO ゲームサーバー内ではプレーヤーやNPCキャラクターの行動を複数のクライアントに多重送信をよく行います。この部分も性能のボトルネックになることがあります。

この部分を速く処理するためには、各socketが持っているmessage queueに対するアクセス過程で発生するlock回数を減らすのです。もちろん、このlockは非常に短い瞬間なので、spin lockを使ってもいいです。しかし、spin lock、これはmutexであり、contentionが発生すると性能低下が深刻です。この問題も解決するために、私たちはlow context-switch loop アルゴリズムを作って入れました。low context-switch loopでは複数のsocketのmessage queueのtry-lockを試みて、try-lockが失敗すれば飛ばします。そして、残りのmessage queueを全てアクセスした後、飛ばしたものなどだけを集めて再びtry-lockをする方式です。

この方式を取り入れて、私たちが得た成果は、結構大きかったです。


このアルゴリズムはかなり効果的でした。MMOゲームサーバーがシングルスレッドで作られていて、MMOゲームサーバーのロジッグ演算量が少ないにも関わらず、非同期socket APIで多くの時間がかかる問題を解決したからです。このような特徴のため、多くのMMORPGがプラウドネットで作られています。

中国で経験した珍しい事

韓国で中国のゲームをしたり、中国で韓国のゲームをしたりする場合は多くはありません。しかし、ないことでもありません。私たちはこんな状況におけるおかしい現象を一つ見つけました。


左側の機器は中国にあります。右側の機器は韓国にあります。左側の機器でABC, ACD, AEFというメッセージを送ります。右側の機器でデータを受信して見ます。おかしいことに、送ったこともないメッセージが混じっています。

そこで、パケット分析ツールを利用してみました。しかし、パケット分析ツールでも送ったことのないパケットが混じっていました。当然、送った方のパケット分析ツールでは正常な内容を見せてくれました。

一体どういった事なのでしょうか?ハッキングでしょうか?私も正確な原因はまだ分かりません。これを読んで知っている方は教えていただければ幸いです。見当をつけるとすると、多分中国の国レベルのファイアウォールで何かがあったのではないかと思います。


とにかく、私たちはこの問題を次のように解決しました。それは、パケットのヘッダーの内容を毎回変わるようにすることです。このようにしてから、送る方と受ける方のデータは等しくなりました。

この他にも、私たちは様々な問題点を見つけて解決しました。たとえば、PMTU discovery fail が発生する一部インターネット環境での通信障害トラブルや、MMOゲームで不特定多数のプレーヤーとP2P接続を結んで切ることを繰り返す場合に発生する接続障害トラブルなどを解決しました。

TCPを利用して、C/S ネットワーキングとUDP、P2Pを状況に合わせて混在させてゲームを開発することは難しいことです。しかし、その代価で得るものは大きかったです。なぜなら、ゲーム中で非常に素早く動くキャラクターに対する精緻な処理ができたからです。

分散サーバーとデータベースキャッシュ
商用化を開始するオンラインゲームのうち、たった一台のゲームサーバー機器を使う場合はほとんどありません。同時接続者がどれだけ入って来るか分からないからです。このために必要なことは、同時接続者をたくさん処理できるように、サーバーエンジンで分散処理サーバーを簡単に作れるようにする機能を用いることです。

オンラインゲームでゲームサーバーが1次的によくすることは、プレーヤーの情報をロードして保存することです。これはデータベースに保存される場合が多いです。しかし、ゲームサーバー開発者はデータベースの取り扱いを煩わしがります。なぜなら、ゲームサーバーを開発する時、データベースのクエリー構文を毎回作成することは厄介なことだからです。さらに、データベースを取り扱う部分を間違って設計すると、サーバーの性能が急激に減少することも理由に挙げられます。


プラウドネットの使われたモバイルFPS ゲーム『ファイナルショット』

プラウドネットを発売開始した時、ゲームサーバーを開発する人々にはこれらの情報を提供しました。また、私たちはゲームサーバー開発者がデータベースをどのようなパターンでアクセスするかを調べました。幸いにも私たちはゲームサーバーを長く開発した経験があったので、パターンを見つけ出すことはあまり難しくありませんでした。

ゲームサーバー開発者がデータベースをアクセスする代表的な類型はwriteです。パケットゲームに例えるならば、プレーヤーデータをディスクに保存することと同じです。ゲーム開発者は、マルチスレッドプログラミングを不便に思います。彼らは、プレーヤーデータをデータベースに保存する過程は、非同期で処理されることを望んでいます。
私たちはこの点をあらかじめ予測したし、一方的なデータアクセス(unilateral data access)APIを作って入れました。私たちはこの機能をDatabase cacheと呼んでいます。


ゲームサーバー開発者は、ゲームサーバー内で私たちが提供した関数を呼び出せば良いです。“どのプレーヤーのどのアイテムがどのように変わった。”ということを関数のパラメーターに入れてです。私たちの作った関数は非同期で実行されます。したがって、即時に関数がリターンされます。関数が即時にリターンされるので、ゲームサーバー開発者がスレッドに関して間違いを起こすことはありません。もし、このようなものがなかったら、サーバー開発者の間違い一つで同時接続者が増えるほどサーバーの処理速度が急激に低くなるトラブルが発生したはずです。

しかし、これだけでは十分ではありませんでした。この機能を使ったゲーム企業は、不満を持っていました。私たちが初めて作った機能は、プレーヤーJohnの情報をゲームサーバーの中で1個のみがアクセスできました。ゲームサーバーを開発する人々は、Johnの情報を他のゲームサーバーでもアクセスできることを。望んでいました。


左側の上端にゲームサーバーがあります。左側の下端にアイテムの取り引きを担当するサーバーがあります。中央にはDB cache サーバーがあります。一番右側にあるのはDBです。プレーヤーJohnのデータはゲームサーバーによってアクセスされています。Johnの最新状態は、ゲームサーバーのメモリー内にcacheされています。私たちが希望するのは、アイテムの取り引きを担当するサーバーが、ゲームサーバーが持っているJohnのデータに安全にアクセスすることです。このために私たちは非独占アクセスAPIを追加しました。非独占アクセスAPIを利用すれば、他のゲームサーバーがメモリーにcacheしているデータを間接的にアクセスすることができます。私たちが作った非独占アクセスAPIは、マルチプロセッサープログラミングでよく扱うatomic operationと似ています。

サーバーエンジンの安全性のためのテスト構築

サーバーエンジンはトラブルに敏感です。安全性がとても重要なので、テストを幅広くしなければなりません。私たちはテストをする時、多様なネットワーク環境を類似に構築しました。多様なインターネットルーターと様々な仮想機器を使用してテストを行いました。


私たちが製作した分散ユニットテストプログラムは、複数のテストケースを並列で実行します。


プラウドネットの使われたFPSゲーム『S4リーグ』


スマホ、無線ネットワーク

PCオンラインゲームを開発する時とは違って、スマホとPC間のマルチプレイができるようにしなければなりません。スマホゲームでマルチプレイをする時は、無線ネットワークが持っている問題点を理解して解決する努力が必要です。

Wi-Fiネットワーク状態がとても良い時はPCオンラインゲームでのネットワーク状態に大きい差はありません。しかし、たまに100ミリ秒以上のレーテンシー、すなわち、ネットワーク遅延が発生したりします。


Wi-Fiネットワーク状態が悪い時は、レーテンシーがとても長くなります。ひいては500ミリ秒まで長くなったりします。場合によっては、パケット遺失率も15%を超えたりします。


スマホがWi-Fiゾーンを脱して、3Gや4Gネットワークに切り替わる場合もあります。このような場合、切り替えが行われる前まで約十秒ほどパケットを全く取り交わすことができなくなります。


PC オンラインゲームよりスマホオンラインゲームの方が、ネットワーク環境が悪い条件にあります。具体的には、レーテンシーがより大きくなり、パケット遺失の可能性もより高くなります。

パケット遺失率が高くなれば、TCPは再送信のための遅延が発生します。この遅延はパケットの往復にかかる時間、すなわち、RTTが長いほど長くなります。この遅延をRTOと呼びます。一般的には、RTTよりRTOがもっと長いです。


このような特徴のため、リアルタイムマルチプレイオンラインゲームでは、キャラクターの移動メッセージや機関銃の乱射のようなメッセージは、TCPよりUDPの方が相応しいです。

しかし、すべてのネットワーク環境でUDPを使えるのではありません。会社にあるファイアウォール機器や一部のWi-Fi、3G、4Gの基地局では、UDPを使うことができない場合もあります。このような場合には、UDPを使うことができないので、TCPを代わりに使わなければなりません。


すべてのモバイルネットワーク通信で、P2P ホールパンチングが成功するわけではありません。韓国のモバイルネットワーク通信では、約56%のP2P ホールパンチング成功率となっています。残りはP2P relayをするという意味です。

モバイルネットワークで一番重要なことは、Wi-Fi地域から脱する場合です。この時、リアルタイムマルチプレイオンラインゲームをしている場合、TCP接続が切れます。Wi-Fiから3Gや 4Gに切り替わると、すぐさま接続の切れるスマホもあれば、数十秒経ってから接続の切れるスマホもあります。接続が切り替わる直前まで取り交わしたパケットは遺失されます。

私たちは、このような問題も解決しました。Wi-Fi地域を脱して3Gや4Gに切り替わる間に取り交わすことのできなかったパケットは、メモリーに保管されます。そして、切り替えが終われば取り交わすことができなかったパケットを再送信します。開発者側から見ると、しばらくパケットが取り交わすことができないだけ、接続はずっと維持されます。

まだ終わってない挑戦

ゲーム市場はずっと変化しています。流行するゲームもずっと変わっています。人々が使う機器の種類もずっと変化しています。サーバー開発での方法論もずっと変わっています。このような変化に合わせてサーバーエンジンも続いて変化するしかありません。プラウドネットもまだまだ変化しています。今後を楽しみにしてください。


プラウドネットの使われたMMORPG『ラスティーハーツ』
《編集部》

関連ニュース

人気ニュースランキングや特集をお届け…メルマガ会員はこちら