mixiグループ Advent Calendar 2017 23日目の記事です
はじめに
XFLAGスタジオでファイトリーグ1というゲームのエンジニアをしている @kodam です。本日は日頃から考えているゲームサーバの未来の話をしようと思います。
スマホゲームの問題
スマホゲームは年々すごいリッチになり、ユーザが期待するレベル高い現状があります。
それにともなって問題点や課題点がたくさんあります。
3Dグラフィックスやリアルタイムな通信は当たり前で、ストレスフルなユーザ間コミュニケーションが可能なものが多いです。また対戦ゲームも人気があり、下図のクラロワ2のようにリアルタイムでどこに何のユニットが置かれたかという情報に遅延が許されないものがあります。また全世界で同時プレイされるようなゲームだと、0ダウンタイムでのサーバ更新なども要求されます。
セキュリティ面だと、サーバサイドにロジックを持っていくことでメモリ改ざんチートを防ぐ等があります。そのトレードオフとして、APIサーバへ負荷が集中する中での低レイテンシへの工夫が求められます。
対戦型のゲームではマッチメイキングも重要になっており、自分の勝率や戦績、位置情報に合った相手と数秒以内に対戦できないといけません。これらに関しては「ElixirとPhoenixでスケールする対戦マッチングシステムを設計する」が詳しいでしょう。
また、スマホゲームはカジュアルなものが多く、家の中はもちろん電車やエレベータやwifiのないオフィスなど様々なネットワーク環境でプレイされることが想定されます。また、世界規模のゲームになるとネットワークが不安定な国であっても快適に遊ばれることが求められます。
今回フォーカスしたい問題
本当は他にもたくさん書きたいことはあるのですが、今回は以下について取り上げます。
- 家でゲームやってて外に出たら通信不良でアプリが使えない問題
- IPが変わるのでリコネクト出来ない
- 電車の中でゲームをしてると瞬断してアプリが遅延する問題
- そもそもリコネクトに時間がかかる
つまり、wifi/4Gが切り替わってもゲームし続けられる環境、瞬断してもゲームを進行可能な環境が求められるわけです。
ファイトリーグでの解決方法
ファイトリーグは3×4のマス目にカードを配置していく対戦型のボードゲームです。
お互いにファイターと呼ばれるチップを置いていき、攻撃や効果によって相手のリーダーのHPを0にしたチームの勝ちとなります。
ファイトリーグのゲームは対戦ゲームなので常に対戦相手がいて、どちらかが欠けてもゲームは成り立ちません。手番スキップ時のオートプレイはありますが、1手違うだけで戦局が大きく変わってしまいます。なので、バトル中に切断された場合すぐに復帰できる仕組みが大事となります。下図の様にファイトリーグでは対戦相手が出すのを迷ってるのも伺えます。
バトル中に接続断を検知するとリコネクト可能な時間を設けその間に再接続できるとゲームが再進行します。
また、バトル内情報はすべてRedisにキャッシュとして保存されており、一度切断されて新しいコネクションを結んでも、キャッシュ上のデータを参照するため復帰が可能です。
サーバとの通信にはWebSocketを利用しており、サーバクライアントモデルのアーキテクチャです。バトル内の処理はほとんどWebScoketを通しております。
再接続も同一コネクションでかつタイムアウトと認識されてなければ、それなりに高速にリコネクトをします。
しかし、IPが切り替わってしまうような場合、もう一度WebScoketのハンドシェイクをしたり、古いコネクションがタイムアウトの検知に遅れてしまうため、リコネクトに数十秒かかってしまいます。数秒を争うようなゲームだと致命的になってしまいます。現状、瞬断には強いけどネットワークの切り替わりには少し弱いです。
他の解決手段
近年0RTTでリコネクトできるQUICやTCPレイヤーで冗長化するMultipathTCPにすごい魅力を感じたので、これらがゲームサーバに利用できないかを調べてみました。
RUDPやKCPとかでも良いのではって話もあるかと思いますが、HTTP互換だとWebSocketで動いてるアプリケーションも動いて良いなと思ったので、既存のプロトコルを使うアプローチにしました。
QUIC
UDP上にTLSとHTTP2を載せるためのトランスポート層のプロトコルです。QUICは色々すごいのですが、個人的に注目したいのはConnectionUUIDというセッションを維持する仕組みです。これはWifi/4Gの切り替えが起きて経路(IP)が変わったとしても0RTTで再接続可能な仕組みです。もしHTTP2にWebSocket2が実装されたらWebSocket2 over QUICが流行ったりするのかなと妄想しています。
またHTTP2の実装だけではなくてオレオレプロトコルoverQUICなんても実装出来ると思います。頑張りがあれば。
MultipathTCP
MultipathTCP(MPTCP)は複数の経路のTCPを共有する仕組みで、クライアントとサーバ間の通信を安定化/帯域幅の増加をするものです。MPTCPはiOS11からSDKで利用できるみたいで、簡単に試すことが出来るようです。
ゲームのネットワークアーキテクチャ
ゲームで利用されるネットワークアーキテクチャは様々で、それぞれの利用方法が異なります。
まずは簡単にアーキテクチャについて解説します。
P2P通信
小規模のアドホックなネットワークであればturn/stunサーバを使ったP2Pモデルが使われています。この辺はあんまり関係ないので省略します。
この場合、瞬断に対してはタイムアウトを長めに取ることによって回避することができます。
しかしネットワークの切り替わりには弱く、stun/turnが複数のIPアドレスに対応する必要があります。(トランスポート層が切り替えられるなら可能?)
WebRTCのDataChannelのプロトコルはSCTPと呼ばれるTCP/UDPのハイブリットなプロトコルを採用しており、マルチホーミングというネットワークインターフェースを束ねてくれる機能があります。これによりネットワークのインタフェースが変わっても、リコネクトしなくて済みます。また、このプロトコルをSCTPからQUICに変えてみる動きもあるみたいです。
- WebRTC(DataChannel) over SCTP/QUIC
- WebRTCのDataChannelのトランスポート層をQUICで作ったやつ
- https://www.youtube.com/watch?v=mIvyOFu1c1Q
Unityには5.1から追加されたUNETというUDP/WebSocketをサポートしたP2P(ホストとゲストのアドホック)機能があります。(多分P2P以外にもサーバでUnity動かせばServer/Clientモデルになりそう) こちらはstun/turnでP2P通信するのが一般的そうですがトランスポート層をいじる低レイヤーなAPIが用意されてるため自前でQUICを実装できそうです。
- UNET over QUIC
- 誰か作って/知ってたら教えて
Server/Client通信
Server/Clientモデルでリアルタイム性を持たせる通信では、UDP/TCPで独自プロトコルの実装やWebSocketを利用しています。使いやすい規格であればWebSocketが使いやすいのかなと思います。
- WebScoker2 over QUIC?
- HTTP2にWebSocketが実装されると行けそう?
- WebSocket over MultipathTCP
- iOS11からMPTCPに対応したっぽい
- 実装が楽そうなので試したい
今後の話
次は実際にWebSocketやWebRTCをMPTCPやQUICを使ってネットワーク切替時の通信断/オーバーヘッドが無くなってるかを検証したいです。軽く調べた感じだとWebSocketのQUIC/MPTCP実装を見つけられなかったので自前で実装する必要があるかもです。
明日は @tacke_jp の「Noto Emojiにコントリビュートし縦書きできるようにした話」です。お楽しみに!!!
参考資料
- https://qiita.com/urakarin/items/dd1cb2f25e2c80178e5d
- http://tips.hecomi.com/entry/2015/08/14/220030
- http://postd.cc/googles-quic-protocol-moving-web-tcp-udp
- https://dev.classmethod.jp/smartphone/iphone/mptcp-on-amazon-ec2/
-
ファイトリーグ https://fight-league.com/ ↩
-
クラッシュ・ロワイヤル https://clashroyale.com/ja ↩