🐼

NeovimでScalaを使ってマインクラフトのプラグインを作ってみる

2023/05/08に公開

この記事はVim駅伝の2023-05-08向け記事です

tl;dr

  • Metalsを使えばNeovimでもScalaは書ける
  • マインクラフトのプラグインを作るならローカル環境を整備しよう

対象読者

  • Neovimを良く使う
  • VSCodeに未だ馴染めない
  • Scalaで開発をする
  • (Optional) マインクラフトのSpigotMC向けプラグインを作りたい

おことわり

正直、Scalaでの開発をするためにNeovimを使うのはオススメできません。
VSCodeやIntelliJを使う方が賢い選択だと思います。
Neovimに馴染んでいて、どうしてもNeovimでやりたい、という人だけが挑戦するべきことだと考えています。

NeovimでScalaを使ってマインクラフトのプラグインを作るまでの軌跡

  • Scala開発環境を構築する
  • Scalaプロジェクトのtemplateを用意する
  • マインクラフトプラグインのtemplateを用意する
  • 試しに動かしてみる

Scala開発環境を構築する

Neovimのユーザーの内いくらかは、LSP(Language Server Protocol)を前提にした言語サーバーを介して、

  • 補完
  • 診断(Diagnostics)の実行
  • 定義ジャンプ

など、各種編集支援を受けているものと思います。
Scalaにも有志によって言語サーバーが用意されており、Metalsの名で公開されています。

公式オススメのMetals利用方法

Metals公式では、nvim-metalsを使う方法がオススメされています。
デバッガと連携する Dap のサポートなど、様々なLSの枠を越えた機能が提供されていること、
フルセットでのセットアップが公式にメンテナンスされていることなどがメリットのようです。

作者自身が、「なぜnvim-lspconfigとの組み合わせではなく、nvim-metalsを使うべきか」と題してDiscussionに表明しています。
https://github.com/scalameta/nvim-metals/discussions/93

だが断る

私は「作者の方針に一度従ってみる」というのを大切にしているため、
一度は公式オススメのnvim-metalsを使った方法を利用してみました。

しかし、

  • negative:
    • telescope.nvimと徒に結合する
    • statuslineを犯してこようとする
    • 他の言語サーバーのattachとの共存が地味に面倒くさい
    • keymapの設定を公式オススメからコピペすると他の言語サーバーと異なる設定になってしまう
  • positive:
    • 他の言語サーバーのための設定が流用できるため、殆どMetalsのための設定は必要ない
    • Metalsの推しているLSPの枠を越えた機能利用に今のところ必要性を感じていない

など、小さなポリシーのずれを感じるシーンが多く、結局nvim-lspconfigとMetalsを自前で連携した方が楽、という結論に至りました。

実際、十分nvim-lspconfigが設定されている私の環境では、Metalsの為に必要な設定はごく僅かでした。

https://github.com/kyoh86/dotfiles/commit/79925fdf9a340add7434e9c1aef94e3c5c37675d
(nvim-metalsを取り除く差分含み)

  • Metalsをインストールする
  • nvim-lspconfigでmetalsのセットアップを呼び出す

だけで環境構築が完了しています。
LSP様々、といった感じですね。
nvim-metals独自の機能なども、恐らくLSPのAttachでちょっとした処理を追加するだけで済みそうです。
後々設定した際にはここに追記していこうと思います。

Scalaプロジェクトのtemplateを用意する

環境構築が終わったら、Scalaのプロジェクトを用意していきます。
といっても、公式マニュアルにある方法をそのまま実行するだけです。

SBT公式ドキュメントより:sbt new and Templates

  • 適当なディレクトリで sbt new scala/scala3.g8 を実行する
    • Template(例ではscala/scala3.g8)にはいくつかの種類があるので、詳しくは上記マニュアル参照
  • プロジェクトの名前を問われるので、答える

だけのことです。これで以下のような基本ファイルが用意されます。

  • <project name>/
    • README.md
    • build.sbt
    • project/
      • build.properties
    • src/
      • main/
        • scala/
          • Main.scala
      • test/
        • scala/
          • MySuite.scala
    • .gitignore

やや脱線する話ではありますが、このとき用意される.gitignoreにはなぜか

# macOS
.DS_Store
# VS Code
.vscode/

のように、開発者の環境に依存した内容が書かれています。
こういう開発者の環境に必要なignore条件は各開発者がgit config --globalで設定するべきものですから、取り除いておいてもいいかもしれません。

マインクラフトプラグインのtemplateを用意する

Scalaプロジェクトのtemplateが用意できたところで、次はマインクラフトのプラグインのtemplateを作っていきましょう。
こちらもSpigotMCの公式ドキュメントに従えばさほど難しくはありません。

Crash Course to Java

というだけのことです。

試しに動かしてみる

SpigotMCプラグイン開発の場合、テストを自動化するにしても実際に触ってみるにしても、
SpigotMCのサーバーを立ち上げて動かしてみた方が良いでしょう。

私が関わっているMODalというプラグインの場合、@thincaさんがDockerコンテナを用意してくれていました。

https://github.com/thinca/MODal/blob/main/compose.yml

自身のマインクラフトのプレイヤーID(またはUUID)を環境変数に設定しておけば、
そのプレイヤーをOPSとした環境を、docker composeを使ってコンテナを起動できます。

$ export MINECRAFT_OPS_PLAYER_ID=xxxxxxx
$ docker compose up -d

このコンテナでは、ホストの.local/data/dataにマウントしています。
そして/data/pluginsをプラグインディレクトリとして扱っています。
従って、ビルドした.jarファイルを.local/data/plugins/*.jarに設置すれば、有効なプラグインとして利用できます。

$ sbt package
$ mkdir -p ./.local/data/plugins/
$ cp ./target/scala-3.2.2/hoge_0.1.0.jar ./.local/data/plugins/

プラグインを設置したら、コンテナを再起動してプラグインの更新を反映しましょう。

$ docker compose up -d --force-recreate

ところで、SpigotMCにはPlugManXというプラグインがあります。
これを利用すると、プラグインのホットリロードが実現でき、時間のかかるコンテナの再起動をせずにすみます。

外部からホットリロードをかける場合:

$ docker compose exec minecraft rcon-cli plugman reload MODal

マイクラ内のコマンドラインからホットリロードをかける場合:

/plugman reload MODal

こちらのプラグインについても@thincaさんから教えていただきました。

おわりに

かなりマニアックな試みでしたが、快適な開発環境を得られています。
他にもNeovimでScalaを使ってマインクラフトのプラグインを書いてみたい、という人が何かの間違いで存在し、参考にしていただければ幸いです。

多くのTips、助言をいただけた@thincaさんに感謝!

Discussion