MultiMCをFreeBSDに移植してみた

マインクラフトを六年前からやっていますので新しいOSに引っ越したらマインクラフトの導入が早い頃から課題になります。5年前FreeBSDを使ってみた時は公式ランチャーしか知らずにSolarisと同様にシェルスクリプトでjarを実行することで済ませたのですが、暫くmacOSを使ってみると、知り合いが勧めたMultiMCを使うようになりました。その名の通り複数のインスタンスを管理しやすくするだけでなく、拡張の管理もログ閲覧も清潔なインタフェースで提供してくれます。五年振りのFreeBSD生活を始めようとすればMultiMCを使い続けたいのは言うまでもありません。ただ、portsになく、ネットでFreeBSDへの移植という話は一切なかったようで、自分で移植しないといけませんでした。

本アプリケーションをFreeBSD上で動かせる

MultiMCは今まで弄ったことのないQt5のC++アプリケーションです。勉強になりますね。ソースコードを覗いてみると、Linuxを前提とした条文の多さに驚愕しました。例えば

#ifdef Q_OS_WIN32
#define currentSystem Os_Windows
#else
#ifdef Q_OS_MAC
#define currentSystem Os_OSX
#else
#define currentSystem Os_Linux
#endif

MacかWindowsでなければLinuxとしますね。殆どのところはQt提供のQ_OS_LINUXで条件付けられますが、ここのOs_Linuxはどのライブラリが自動的にどウンロードされるか判定します。このままFreeBSDで実行すると、Linux用lwjglバイナリがインスタンスのディレクトリにダウンロードされてクラッシュを起こしてしまいます。殆どのライブラリはFreeBSDでそのまま動くjarなのですが、少々nativesと言われる共有ライブラリもあり、初起動でMojangのウェブサイトから同名のディレクトリにダウンロードされます。勿論、そこにはFreeBSDのライブラリがありません。Windowsなどで様々なライブラリが提供されるらしいけど、うちのUnix系の場合、lwjglしか要らず、パッケージとしてインストールできます。というわけで

 QString MinecraftInstance::getNativePath() const
 {
+#if defined(Q_OS_FREEBSD)
+       QDir natives_dir("/usr/local/lib/lwjgl2.9.3/");
+#else
        QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/"));
+#endif
        return natives_dir.absolutePath();
 }

でパッチを当てます。natives_dirの中にlwjglが検出され、先のヘッダーにOs_LinuxではなくOs_FreeBSDを定義しておきましたのでLinux用ライブラリをもうダウンロードしようとしません(つまり同梱のプラットフォーム別リストの何もが一致しなくなったため、ダウンロードすべきnativesのリストが空です)。

他にはメモリ容量確認が問題です。Linux前提のコードは/proc/meminfoからメモリ容量を読み込もうとしますのでFreeBSDでは0が返されて、GUIでXmXを設定するスピンボタンが非アクティブになってしまいます。最大限を上げられないと上手く行けませんね。XmX設定が使えない

代わりにsysctlを参照する関数を書くことで直せます。PrintInstanceInfoなどゲーム実行と直接関係のないところでも似た問題がありますのでそれも敢えて直しておきました。

#elif defined Q_OS_FREEBSD
char buff[512];
FILE *fp = popen("sysctl hw.physmem", "r");
if (fp != NULL)
{
        while(fgets(buff, 512, fp) != NULL)
        {
                std::string str(buff);
                uint64_t mem = std::stoull(str.substr(12, std::string::npos));
                return mem * 1024ull;
        }
}
#endif

後は所々#ifdef Q_OS_LINUX#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)にするような簡単な作業だけです。

portとして片付ける

愚痴を零すことになってしまいますが、MultiMCのビルドスクリプトでCMAKE_INSTALL_PREFIXDESTDIRの使い分けが間違っているのです。プロジェクトのBUILD.mdに照らしてみますと

mkdir build
mkdir install
git clone --recursive git@github.com:MultiMC/MultiMC5.git src
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install ../src
make -j8 install

という仕方が勧められていますね。以下の方が正しいだと思いますが。

cmake -DCMAKE_INSTALL_PREFIX=/usr/local ../src
make -j8 DESTDIR=../install install

この変わった仕組みは多分、Windows/Macのためのバンドルパッケージが想定されているからです。C:\Program Filesとか/Applications/MultiMC.appに収めるとPREFIXなんて無意味なのですね。ただ、普通のUnix系だと困ります。幸いに0.5.1の後、Unix系向けのインストール設定が追加されました(やっぱりLinuxに因んでlin-systemと呼ばれます)が、少々問題が残っています。application/CMakeLists.txt内、インストールパスが何故か固定してあります。

eif(MultiMC_LAYOUT_REAL STREQUAL "lin-system")
        set(MultiMC_BINARY_DEST_DIR "usr/bin" CACHE STRING "Relative path from packaging root to the binary directory")
        set(MultiMC_LIBRARY_DEST_DIR "usr/lib" CACHE STRING "Relative path from packaging root to the library directory")
        set(MultiMC_SHARE_DEST_DIR "usr/share/multimc" CACHE STRING "Relative path from packaging root to the shared data directory")

ここの/usr/usr/localに変えればいいのですが、CMAKE_INSTALL_PREFIXも変えないと/usr/local/usr/bin/multimcなど絶対にいかない結果が齎されます。という訳でCMAKE_INSTALL_PREFIX=/という奇策を取ります。しかも、MakefileCMAKE_ARGS+= -DCMAKE_INSTALL_PREFIX=/を入れても無効果のようなのでCMakeLists.txtに直接パッチを当てる必要があります。

+set(CMAKE_INSTALL_PREFIX "/")

これで出来上がり。興味のある方は是非リポジトリからインストールしてみて下さい。