SWEet

A Software Engineer Is Eating Technologies

ドラゴンクエストXを支える技術を読んで 〜普通のソフトウェアエンジニアから見た視点〜

ドラゴンクエストXを支える技術」を読みました

www.amazon.co.jp

読んだ感想

非常に面白い本でした.オンラインゲームのプログラミングに関わったことがない人でも参考になる知見が沢山詰まっています. 本書はドラゴンクエストXにおける元テクニカルディレクター,現プロデューサーの青山さんが執筆しています.

元々,Web+DB Pressで連載されたものをリライトしたものです. 折角なので普段はWeb系の開発に携わっている & ドラゴンクエストXの一般ユーザーとして面白かったところをいくつかピックアップしようと思います.

前提知識

まず読者である僕が持っている知識について書いておきます. なぜそんなことを書くかというと,僕が面白いと言ってもそれはある程度プログラミング等の開発知識があるから面白いと思えたことも多々あると考えているからです.プログラミングに一切関わりがないドラクエXユーザーが抱く感想とはまた違うかもしれないということ予め伝えておきます.

  • プログラミング: 普段はWeb関係の開発に従事しています. ドラクエXで言うなら冒険者の広場やお出かけ便利ツールの開発がこれに当たります.
  • コンピュータに関する知識: 大学院で専攻している内容でもあるので基礎的なものは大丈夫だと思います.ドラクエXではメモリの最適化,グラフィック処理,移動干渉やトランザクション処理のロジック全般がこれに当たります.
  • ネットワークに関する知識: 所謂TCP/IPレベルのネットワークや負荷分散などのノウハウについては研究している分野でもあるのでほぼ大丈夫でした.

と,こんな感じの人が読んだらどういう感想やどこが面白いと思ったのかというのを書いていきます. 順番としては本書の章立てに従っていきます.

メモリの最適化を意識したコーディング

コンピュータにおけるプログラムの実行は基本的にメモリとCPUが肝です. プログラムを実行する際にはWindowsで言えば .exe ファイル,所謂実行形式のファイルをOSがメモリに領域を確保して,展開し,CPUがメモリに展開された命令を一つずつ実行するというフローになっています. これは当然オンラインゲームでも同じです.PCやSwitch, WiiUなどのメモリ上でドラクエXのクライアントが展開され実行されているわけです. 勿論重要なプログラムはインターネット上にあるドラクエXの心臓部でもあるゲームサーバで実行されているわけですが.

ここで問題になるのがメモリには限界があるということです.オンラインゲームはどんどんアップデートを重ねてそのプログラムの大きさは非常に大きいものになっていきます.しかし,メモリには容量の限界があるため例えば20GBもあるプログラムは大抵の家庭用PCでは満足に動かないと思います.

本書でも再三言われていますが,ドラクエXではそんなメモリの容量を事細かに気にしながらプログラミングをしています. コラムでは構造体のメモリアラインメントに関する知識に関しても触れていました.

例えば多くのCPUで要求される構造体(size_t)のアライメントは 32bit環境では 4byte, 64bit環境では 8byte だったりします. 次の擬似コードではメンバ変数のサイズの合計は6byte(int: 4byte, char: 1byte)です.

struct Hoge {
    int A;
    char B;
    char C;
}

このコード自体はエラーなどはないのですが,アラインメントされていない場合は環境(CPUやメモリ)によっては構造体のサイズが変わってしまうかもしれないのです. そこで次のように明示的に確保される容量を示します(本書とは違うように書いてます)

struct Hoge {
    Size32int A;
    Size8char B;
    Size8char C;
    Size16bits padding; 
}

ここまでのレベルで最適化を意識することはほぼないので読んでて非常に新鮮でした.

使用するプログラミング言語

ドラクエXではC++Luaが主な開発言語として使用されているようです. ゲームならまぁC++だろうなというのは思いましたがLuaが使用されているのは以外でした.

Luaは高負荷にはならないクライアント側のちょっとした処理をPDCAのサイクルを早く回すために用いられているようです. ただ,LuaC++の連携は具体的にどうやってやっているかはちょっとわかりません(RPCなのかC++からLuaを直接呼び出しているのかそのまた逆か).

大規模プロジェクトならではのビルド環境

ビルドというものはプログラミング言語でかかれたファイル群を実行可能な一つのファイルにコンパイルすることを指しています. 往々にして大きなプログラムになるほどこのビルドには時間がかかるものです. 例えば有名なコンパイラである LLVM というOSSは自分のPCでビルドしようとすると2, 3時間かかったりします. ソースコードを修正するたびにそんなに時間がかかっていたら生産性も下がってしまいますよね. ビルドを自動化したり高速化するのもプログラマ界隈では重要なミッションです.

ドラクエXでは distcc という分散コンパイラというものを使っています.大体インターネットでコンパイラと調べると一つのPC上で処理が完結するものが殆どですが,distccは複数のPCで分散してコンパイルを行うことで高速化を図るというものです.プロダクション環境で使っている例を初めて見たので感動でした.もしかしたら色々なところで使われているのかもしれませんがあまり話題にならないのとオープンにしていない可能性もあります.

また自動ビルドによってテストを一定間隔で実行して報告するような仕組みも作っているようです.これは似たような事例が ゼルダの伝説 ブレスオブザワイルド の開発の話でも上がっていましたね.大きいプロジェクトですとこういった仕組みの構築が開発の生産性向上に繋がりやすいのでしょう.

小さいプロジェクトとかだとMakeだけとかでもいいですけど何万行とかのコードベースなってくると色々な工夫が必要になってくるって感じはよくわかります.

厳密なバージョン管理とPDCAの回し方

コードベースが巨大になるにつれて1コミットが思わぬバグを生んだりするっていうのはまぁよくあることです. どうやらドラクエXの開発でも実際にそういうことがあったらしく原因や改善策について触れています. 特にリリースしたバージョンのコードベースに変更を加える際には小さい変更でも必ずテクニカルディレクターのレビューを通してからじゃないとマージされないそうです.

ちなみに,バージョン管理にはSVNを使ってるらしいです.Gitでも良かったそうですが使い慣れてるほうにした模様. コードベースが巨大になるとブランチのチェックアウトにもかなり時間がかかるそうでそれは体験したことないので興味深いです.

PDCAは基本的に最近のWeb開発とあまり変わらないようです.ただ,ゲームなのでドッグフーディングに費やす時間はとても多いように文面から感じました. そこからリリースまでに何回もFixを重ねてリリースブランチとするみたいな感じらしいです.

クライアントとサーバそれぞれのメモリ管理

クライアント側

クライアント側は基本的にグラフィック周りがボトルネックになる模様. 例えば,フィールドを散策してモンスターが画面に写ったり消えたりするたびに

// モンスターをメモリに確保
Monster *m = (Monster *)malloc(sizeof(Monster));

// なんか処理(バトルだったり話しかけたり)

// モンスターをメモリから解放
free(m);

みたいな処理が行われるわけです.こういうことを繰り返しているとメモリの断片化ということが起こってメモリが中途半端に開いてる箇所が沢山増えてしまう. そうすると効率的にメモリを使えなくて処理が遅くなったりしちゃうわけですね.

ドラクエXではこのメモリの断片化を防ぐために解放したメモリ領域を適切に再利用することで解決しています.

この問題を読んでいる時にドルボードのブースト機能を思い出しました. いつだったかりっきーが「ドルボードの高速化は技術的に非常に難しい問題」というようなことを言っていたと思います.

これは後述されるグラフィックの処理と移動干渉の問題も絡んでいると思いますが,基本的にユーザーが移動するたびにユーザーの位置や視点から画面に映るオブジェクトを算出しメモリに確保したりしなきゃいけません.これは非常に計算量の大きい問題なのでそれがドルボードの加速によってより短いスパンで行われると描画演算が追いつかないみたいな問題があったのかもしれません(移動してるけど風景が何もないみたいな).

常時高速化だとクライアント側の負荷も相当になるので一時的なブースト機能ということで実装されたのでしょう(あくまで憶測です)

もう一つクライアント側ではプログラムオーバーレイという技術を使ってメモリの節約を図っています. 一見小難しい言葉ですが要は「普段使わないプログラムは読み込まないで必要になったら読み込む」ということです.

本書の例では住宅村や魔法の迷宮などのインスタンスオブジェクトが挙げられていました.メギストリスで討伐を買うために並んでいるときに住宅村のプログラムをメモリに読み込む必要はありませんからね.

サーバ側のメモリ管理

サーバ側は基本的にコアロジックが集約されているので速度が重視されるものが多いです. そのためメモリのスワップアウトはなるべく防ぎたいというのがドラクエXの開発側の意見です.

スワップアウトというのはプロセスに割り当てられたメモリ領域をオーバーした場合は足りない部分はHDDやSSDの記憶領域を利用させてもらうことでメモリ不足を解決するわけです.

メモリが足りてるならいいじゃないかと思うかもしれませんが問題は速度です.

一般的なメモリの読み込み速度は 10GB/sSSD, HDDは最速で 100MB/s となっています. SSDですらメモリの100倍遅いです.

というわけでサーバプロセスがサーバのメモリを食い尽くしてしまわないように各サーバで適切にプロセスを割り当てて稼働させているようです.

サーバ側で動くゲームプロセスにおける工夫

サーバプロセスはシングルスレッドで動く

シングルスレッドは文字通りCPUから割り当てられるスレッドが一つということです. 一般的にプログラムはマルチスレッドで処理した方が高速になります(マルチスレッドの高速化手法については非常に説明が大変なので割愛). しかし,マルチスレッドは排他制御を必然的に組み込まなくてはならなくなり,デバッグが大変になります.

コードベースが巨大になるとどこがバグの原因なのか突き止めるのはもっと大変になります.そういった点を考慮してドラクエXではシングルスレッドで実装したそうです.

ゲームDB

ドラクエXのイベントでも度々開発者の方々が口にするDB(データベース),ドラクエXのデータの全てが詰まっているDBは単一障害点として非常に慎重に扱わなければなりません.

Oracle Exadata

ドラクエXではバックエンドのDBとしてOracle Exadataが使われています.なんかのイベントでよーすぴが言っていたように 銀行で使うようなDB の通りエグい検索速度,容量,そしてお値段(調べるとわかりますが数千万円は軽く越えます).

大規模なWebシステムで使うクラウドベンダが提供しているDBを一ヶ月利用しても高くて10万ちょい,一年で100万ぐらいと考えるとその規模の大きさがわかります.

Exadataには基本的にプレイヤーの情報全てが入っていると考えていいでしょう(持ち物,ステータス,ストーリーの進行状況etc...).

個人的に一番すごいなと思ったのが旅人バザーにおけるDBの使い方です.

旅人バザーはユーザーが出品した商品を他のユーザーが買うことができるシステムです.ドラクエXの経済は基本的にこれを基準に回っており,現実世界の経済と何ら変わりはありません.しかし,問題になるのがトランザクション管理です.

トランザクションというのは例えば

  1. AさんがBさんの商品を1000ゴールドで購入
  2. Aさんが1000ゴールド消費し商品を獲得
  3. Bさんが1000ゴールド獲得

というような一連の取引を指します.銀行のシステムなどでよく使われる言葉です. インターネットの世界では何があるかわからないので手順2と3の間で回線が切れてしまう可能性もあります.

そうした場合Aさんが1000ゴールド消費したにもかかわらずBさんは何も得ていないことになってしまい不整合が発生します. そのためにもDBやプログラムでこのトランザクションを途中で失敗したら状態を戻して0から取引をやり直す というような処理にしてあげなくてはなりません.

こういうった処理は非常に厳密にやらなくてはならないので大変です.

更に旅人バザーは全サーバ共通となっているので全ユーザが日夜取引を行うことでその負荷は最早現実世界の銀行システムと何ら変わらないのではないのでしょうか. 驚くべきはこういったシステムを銀行システムの開発者でもないオンライゲーム開発者がやっているということです.

同じ開発者からして,その大変さは推して図るべしとでもいいましょうか.とにかく凄いです.

Kyoto Tycoon

Kyoto TycoonはインメモリKVSというものです. 立ち位置としては毎回DBのアクセスするのは負荷がかかるので読み込み頻度が高く,更新されにくいデータなどはこちらに保存しておいて読み込ませることで,負荷の分散と読み込みの高速化を図っているという感じです.

TycoonとExadataが上手く協力して落ちないシステムを構築しているわけですね.

この章ではアイテム情報テーブルの構造なども書かれていますがそれは読んで確かめてみてください(書くのがだるい).

まとめ

僕が技術的に面白いなと思ったことをさらっと触れてみました. 移動干渉については少し濃密すぎるしどう考えても簡単にかけないので省きました.ぜひ買って読んでみてください.

プログラマの視点から見てもユーザーの視点から見ても本当に面白い本でした

というわけで安西先生!バトマスは天下無双が強化されて非常に楽しいです.ただ,はやぶさ斬り も強化してくださいお願いします