この記事はElixir Advent Calendar 2015の18日目の記事です。
Elixirで作るMMO
Elixirというプログラミング言語が人気だ。関数型言語erlang VM上で動く関数型言語だ。Rubyっぽい書き方ができるといわれている。
Elixir Advent Calendar 2015というカレンダーができるくらい人気だ。
この記事では、Elixirで作るMMO-RPGの基礎というタイトルを付けた。なぜならばElixirはMMO1を作るのにとても適していると考えたからだ。
今回、elixirでMMOを作ろうと思ったが、残念ながらMMOを作る基礎的な部分しかできなかった。その作った分を紹介しながらなぜelixirがMMOに向いているかを見ていこうと思う。
MMOの構成と作りたかったもの
MMOの骨格はなんだろう。私は、通信、位置同期、チャットだと考える。オンライン上を自由に歩ける。集まってチャットが出来る。このふたつができれば、MMOの基礎ができたんじゃないんかなと感じる。
通信と、座標位置とチャット
図にするとこんな感じだ。
ユーザーはそれぞれサーバに接続し、キャラクターを一体動かせる。それぞれのキャラクターは自由に動くことが出来て、近くのユーザーと会話が出来る。
今回作ったところ 通信と座標位置制御
今回は、此の内、通信の部分と、自分の近くに誰がいるかという部分だけ作った(それ以外は間に合わなかった)。作ったコードは、githubに上げている。こちらのレポジトリにある。 https://github.com/shibacow/elixir_rtree_test
検証用のコードが大半だ。
通信
通信の部分は erlangのranchというライブラリを使っている。テスト下プログラムは大体、test_me.ex にある。8000番ポートで待ち受けている。pythonで作ったel.pyから接続を待ち受けて、通信する。その際のデータのシリアライズにはmessagepackを利用している。他の接続者と通信をしたかったが、やり方がわからなかった。
座標位置の制御
座標位置の制御にはrtreeを使う。主な用途は、自分の位置から近い人を割り出すために使う。erlangにもrtee用のライブラリがあり、rstarというライブラリがある。このライブラリを使えば例えば、Matching = rstar:search_around(TFinal, Point, 10.0)
みたいな感じで、10距離以内のキャラクターをピックアップするといった使い方が出来るようだ。rtree_test.exに試したテストコードを載せている。作成中に思ったが、rstarで座標の更新が出来ず、削除と挿入を繰り返している。うまいやり方があるかわからなかった。
作ってみての感想
まあ、最後まで完成しなかったので大きなことは言えないが、通信、座標位置の制御の基礎的部分はすこし作れた。実際は、
通信と位置描画
- プレイヤーとサーバの通信
- 自分の位置を基準に自分の周りにどのキャラクターがいるかの抽出
- 自分の周りのキャラクターに自分の位置を知らせて描画する。
- キー入力に応じて、自分の位置をアップデート。
- そのたびに次ビュンの周りのキャラクターに自分の位置を知らせて描画する
チャット
- 自分のキャラクターがしゃべる。
- 自分の位置を基準に、周りにどのキャラクターがいるかを抽出
- 周りのキャラクターに自分が発したメッセージを送信する。
- 周りのキャラクターはそれを受信し、描画する
という実装が必要で、今回作ったものはその初歩の初歩と言えるだろう。
ただ、今回は、それよりも何故Elixir(Erlang)がMMOに向いているかという点を見ていこうと思う。
なぜElixirがMMOに向いているか?
シングルCPUの性能限界
MMOは処理が多い
MMOは通信や位置制御、チャットの送信、バトル、クエストなどCPUを結構酷使する。特に通信と位置制御は毎秒更新や更新情報を周りのユーザーに知らせるといったメッセージ送信の処理が行われるのでCPUを使う。CPUを使うならC言語やC++と言った言語の方が得意ではないかと思われるかも知れない。確かにC言語やC++は早い。ただしそれは、シングルCPUならという条件がつく。
シングルCPUのクロックアップから、マルチコア、メニーコアへ
2000年代は、CPUのクロックスピードを上げていた。700Mhz,1Ghz,2GHz,3GHzとCPUの速度が上がった。しかし最近では、クロックスピードは2GHzで、CPUのコア数をどんどん増やしていく方向にシフトしていっている。Intelはコア数が50-60位のIntel Phiなどをリリースしている。つまり、CPUメーカーやGPUメーカーは、シングルCPUのクロックスピードを上げて、処理速度を高めるよりも、コア数を増やして、(並列の)処理速度を高める方向に向かっている。
erlangは並列処理向き
erlangやそのVM上に乗っているElixirは並列処理が得意だ。プロセスを生成すれば、別のCPUで処理してくれる。
この論文は、64coreのシステムでerlangの並列処理の性能を評価したものだ。
スケジューラーの数(使用するCPUの数みたいなもの)を増やせば増やすほど大体リニアに性能が向上している。erlangやElixirはもともと並列処理向けに言語を設計しているので、あまり悩まなくても、メニーコアのCPUの性能が出せると思われる。
C言語やC++で並列処理を書くのは難しい。
C言語やC++はもともとシングルCPUで性能が出るように設計されている。マルチスレッドやマルチコアの処理がそこまで得意なわけではない2。
Elixir(Erlang)はメニーコア時代に即したプログラミング言語である
上に見てきたように今でもC言語の方がシングルCPU環境では早い。しかし、シングルコアではなく、32CPUcore,64CPUcore,128CPUcoreとCPUのコア数を増やすことで処理性能をアップさせるなら、並列処理を簡単に書けるErlangやElixirの方が、性能を上げやすいのではないかと思っている。
けして、C言語やC++がメニーコア時代に即していないと言っているのではない。並列処理をカリカリにチェーンできればC言語の方が早いだろう。ただ、64コア128コアになった時に、マルチスレッドやマルチプロセスでチューニングとか、さっぱり出来る気がしない。
大体プロセス、なんでもプロセス
Elixirの軽量プロセス
Elixir(erlang)のもう一つの特徴は、その高い耐障害性にある。ElixirはVM独自で軽量のプロセスを持っており、多量のプロセスを作ることが出来る。そして、一つのプロセスが死んでも、他のプロセスは影響を受けない。また、そのプロセスが落ちた後瞬時に別プロセスを立ち上げるsupervisorという仕組みを標準で用意している。
MMOの寿命管理は難しい
MMOはユーザーが通信しながら遊ぶ。また、ユーザーが遊ぶ時間もまちまちだ。そのため、ユーザーのセッションの寿命管理も大変だ。図示すると、次のようになる。
通信セッションは、各ユーザーが実際にサーバに接続したセッション。そしてその接続セッションとは別に、プレイヤーキャラクターのセッションを管理している。何故、通信セッションをそのままキャラクターのセッションにしないかというと、通信回線によっては瞬断が起こるからだ。プレイヤーキャラクターと通信セッションを分けないと、瞬断が起こるたびに、ゲームのタイトル画面に戻されていしまい、ユーザーは不便な思いをする。
Elixirを使うなら、これらのセッションは全て、プロセスを使うと良いのではないかと考える。そうすることで、例えば、不具合が起こった際も、その影響は個々のユーザーの通信セッションやキャラクターセッションに限定させることが出来る。
例えば、上の例のように、一番下のキャラクターセッションで障害が起こって、プロセスが停止しても、他のキャラクターセッションや通信セッションに影響は及ばない。
チャットもクエストもフィールドの移動もバトルもプロセス
MMOは寿命を持ったものが大量に存在する。チャット、クエスト、バトルもそうだ。それらをプロセスとして抽象化したらどうだろう。
例えば4人で協力対戦する場合を考える。途中入場、途中退場可能なシステムだ。プレイヤー2は途中で入場し、プレイヤー4は途中で退場する。バトルセッションやプレイヤーそれぞれをプロセスとして扱う。たとえ、何らかの理由でプレイヤー4のプロセスがクラッシュしても、他のバトルセッションやユーザーセッションには影響がない。当然1サーバーで一つのバトルセッションしか動かなさいという非効率的なことは出来ないので、何百、何千というバトルセッションをプロセスとして動かしても平気だ。
バトルを行っているプレイヤーやモンスターの束をバトルプロセスという束で包み、更にそのバトルセッションを複数集めて束にすれば、それぞれ異なった寿命を持つものを比較的簡単に管理できる。
似た仕組みは、チャット、クエスト、バトルでも採用できる。つまり、プロセスの入れ子構造を使うことで比較的簡単に複雑な状態を管理出来るのではないかと考えている。
トランザクションもプロセス
その意味でいえば、MMOでよく行われるアイテムの取引もプロセスとして抽象化出来る。
交換の前にトレードプロセスを立ち上げる。
プレイヤーAとプレイヤーBがそれぞれ品物を交換する。プレイヤーAとプレイヤーBがそれぞれ貰ったものを確認する。互いに良ければ、コミットして、取引終了。そうでなければプロセスごと、消してしまえば、ロールバックしたことになり、取引は無かったことになる。
プロセスとして抽象化することで、トランザクションの開始、コミット、ロールバックが実装出来るのではないかと思っている。(ちょっとココらへんは自信ないです)。
マクロを使った動的なゲームロジック生成
Elixirにはマクロという仕組みがある。曰くコードを生成するコードだそうだ。自分も詳しくは分からないが、この機能を使えば、複雑になりがちなゲームロジックをシンプルに記述できるのではないかと思っている(調査が足らなくて本当の所はわかっていない)。
実際にはまだまだ難しい所
今まで、ElixirでMMO作れるんじゃないかという話をしてきたが、実際に作ったらCやC++などの言語に比べて次のような問題に直面するだろう。
言語としての遅さ
上で、C言語より早くなるとあおり気味に書いたが、実際はC言語と比べて言語的に早いわけではない。言語的には何倍も遅いだろう。ただ、並列処理が得意なので、メニーコアの時代に見かけ上処理が早くなると考えている。例えばRTreeなどはC言語で書きたいという話も出てくるかも知れない。
IOの遅さ
これも、C言語の方が早いだろう。IOは並列処理も難しい事が多い(一つのファイルに書き込むとか)ので、IOを多用するMMO(ネットワークもIOの一種だ)では、IOの遅さがボトルネックになってしまうかも知れない。
総じて、やはりカリカリにチェーンされたC言語には敵わないので、それらが生きてくる局面では、elixirの採用は難しいかも知れない。
まとめ
Elixirを使えばMMOをより簡単に作れるのではないかと考え、基礎的な調査を行った。また、何故Elixir(Erlang)がMMOを作る上で役に立つかを考察した。今のところ可能性がある位でまだ特に何かするということは無いが、MMOの作成コストが上がれば、よりシンプルに通信アプリケーションを書けるElixirを使って、MMOを書こうという試みがなされるかも知れない。
参考資料
今での、オンラインゲーム(特にMMO)を作るなら、参照すべき事が多い書物だ。
ニコニコ生放送の要件がほとんどMMOとかぶる。ユーザーセッションをプロセスにアタッチすればユーザーセッションの寿命管理を、プロセスに任せられるだろうと言う発想はこのスライドを見て思いついた。