Dynmapを軽くしたい

Java版のマイクラサーバーで欠かせないのがDynmapである。Dynmapは大量のメモリとCPUを消費して大量のPNG画像をサーバに保存する。Dynmapは、1GBぐらいのチャンクデータ(anvil, .mcaリージョンファイル)に対して、10GB以上の画像を生成する。画像のレンダリング自体もゲーム処理よりも重いので問題だが、それ以上にストレージの容量消費が大きいので、結局、安いサーバーを使う場合は、すごく狭い範囲(2048とか)だけしか地図を表示できないってことになる。個人でそんなにでかいサーバーを借りれない。斜め視点の画像をなくして縮尺を大きくしたりしてもよいけど、楽しさが減ってしまう。

Dynmapがサーバーサイドで画像をレンダリングしたり保存をしないようにできれば大幅に軽くなるはずだ。

基本的な方針は、ブラウザで地図を見たいときに、サーバにある.mcaファイルの一部をそのままブラウザに送って、それをWebGLで直接描画する方法だ。GLM/WebGL-mapとか、他にもいろいろと試されているが、どれも広く使われるには至っていない。 

マイクラのサーバは、広大な世界のうち、プレイヤーの近傍のごく一部だけをメモリに乗せて稼働している。しかし、Dynmapを使う目的は、マイクラのサーバーのメモリに載っていない部分も含めた世界全体の広い範囲を見ることだ。これが問題になってしまう。

マイクラのチャンクデータは、Anvilファイル形式と呼ばれ、拡張子は.mcaである。Anvilファイルは 512x256x512ブロック=6700万ブロックのデータをバイナリ形式にして、だいたい1ブロックあたり3バイトに加えて看板とかチェストなども含めて、100~200MBぐらいのデータをZLIBで3~10MBぐらいに圧縮してあるデータだ。このファイルを1個読んで圧縮を展開するだけで、普通のマシンではJavaでもC++でも1秒以上の時間がかかってしまう。マイクラの世界は広いので、たとえば4096x4096ブロックの範囲には64のAnvilファイルが関与しているため、圧縮展開だけで1分以上かかってしまう。圧縮以外にもツリー構造になっている内容の解析でもそれなりの時間がかかる。

サーバーから圧縮されたままのAnvilファイルを全部ブラウザに送るならば、4096の範囲を見るには5MB=64=300MB以上のデータを送る必要がある。画面一杯のPNG画像ならば5MB以下ぐらいですむのだから、かなり大きい。大きいだけならいいが、ブラウザ側で圧縮展開をしたら、結局1分以上かかってしまうことになる。現実的ではないだろう。

地図として描画する必要がある情報だけに絞った、何らかの中間形式を考えて、それを送受信する必要がある。

Anvilファイルの大部分は、誰も見たことがない膨大な地下空間のブロックが占めている。4096x4096x256ブロックの半分は空気で、これは送信されない。高さだいたい64以下の部分は全部地面で、そこに膨大な量のブロックがあるが、プレイヤーが見たことがあるのはそのごく一部の表面だけだ。

その表面のブロックだけを保存して、あとはぜんぶ空気になっているようなデータを保存しておけばよいのではないか。地下空間の全体が綺麗に隙間なくブランチマイニングされているような地形であれば、中間形式ともとのブロックが同じデータになってしまうが、そんなことはまれだ。

感覚でいうと、地上世界なら、軽く10分の1,場合によっては50分の1になると思う。ネザーやEndならもっとで、100分の1になるかもしれない。いやEndは圧縮効率がそもそもすごいので、中間形式は不要かもしれないが。

ということは基本的な設計方針は、このようになるのではないか?

まずSpigot(Paper)サーバーのプラグインを実装し、プレイヤーが見た表面をAnvilとは別の形式(表面ブロックファイル)に保存していく。

地図クライアントから範囲について要求があったら、その表面ファイルを圧縮したデータをブラウザに送信する。ブラウザはそれを受信したら展開して、WebGLで表示する。

これで、Dynmapを軽くすることができるような気がする。


この記事が気に入ったらサポートをしてみませんか?