【Minecraft Quilt】Minecraft 1.19.xで前提ModがFabricなModをQuiltへ移植してみた

# はじめに
この記事では、Quiltでのコーディング方法（QSLなどの使い方）にも一応触れますが、
Quiltがどういうものなのか、全体的に紹介する記事となっています。

私自身もQuiltに詳しいわけでも無く、数日前に初めて知ったばかりですが、
FabricからQuiltに移植することにしたので、知名度向上の意味も込めて、
記事を残しておきます。

# 開発環境について
<details>
<summary>開発環境についてみる</summary><div>

## 移植前の開発環境

|項目                    |内容                             |
|:----------------------|:--------------------------------|
| IDE                   | IntelliJ IDEA Community 2022.2  |
| Yarn mappings         | 1.19+build.2                    |
| Fabric loader version | 0.14.7                          |
| Fabric API version    | 0.55.3+1.19                     |

## 移植後の開発環境

|項目                            |内容                             |
|:-------------------------------|:--------------------------------|
| IDE                            | IntelliJ IDEA Community 2022.2  |
| Quilt mappings                 | 1.19+build.1                    |
| Quilt loader version           | 0.17.3                          |
| Quilted Fabric API version     | 2.0.0-beta.9+0.58.0-1.19        |
| Quilt Standard Library version | 2.0.0-beta.16                   |
</div></details>

# Quiltとは
Quiltとは、FabricからフォークしたMinecraftの新しいModloaderです。
Quiltは2021年の4月20日に発足したプロジェクトで、
Fabricの様々な問題を解決する事を目的にしているようです。

また、新しくできたModloaderということもあり、
**開発途中で2022年8月現在ベータ版となっています。**

## Fabricからフォークした理由
幾つかの理由があるそうですが、公式のブログを参考に簡単に書き起こしてみます。

- 運営状況の改善
    - 開放的な運営を目指していて、一人の人間による中央集権的な運営は行わない
- 利害関係の改善
    - 技術力のある人だけが決定権を持つことや、利用者だけが決定権を持つといった偏った利害関係を抑制する
    - Quiltチームは、必要最小限の影響力にとどめる
- Mod読込の改善
    - Mod読込に関する幾つかの改善をする
- 依存関係の改善
    - 前提Modなどの依存関係を、ユーザーが都度ダウンロードする必要性を無くす
- 安全なバイトコード修正の実装
    - Mixinや直接的なバイトコードの修正に変わって安全な手法を提案する
- 試行錯誤
    - 失敗は成功の母にならって、トライアンドエラーで修正を行うかどうかの議論を行う

といった具合のようです。
Fabricが技術革新に大きく貢献した半面、最近では下火になっていて
組織的な課題を抱えていることが、フォークした理由のようです。

詳しい内容を知りたい場合、公式のブログを見るとよいです。

https://quiltmc.org/blog/2021/04/20/this-is-quilt/  
## Fabricとの互換性
先ほどのブログにも書かれている通り、
現状ではFabricのModもQuiltで読み込むことができ、互換性があります。
ここで言う互換性とは、FabricのModをQuiltで読み込めるという一方的な互換性であり、
**QuiltのModをFabricで読み込む事はできません。**
また、Fabricとの互換性はあくまでも現状であり、
**将来的には互換性が無くなる可能性もあるので**、注意が必要です。
# 開発者向けWiki
現状、あまり機能していないように思います。
ただ、このWikiもGitHubで管理されているので、プルリクなどでコミュニティ主導で
創り上げていく目的があるように思いました。

https://modder.wiki.quiltmc.org

# コミュニティ
コミュニティはDiscord上で活動をしているようです。

https://quiltmc.org/usage/#community-links

<details>
<summary>完全に主観ですが</summary>
<div>

Discordはコミュニティとしてあまり良いとは思いません。
というのも、Discordでディスカッションされたナレッジは、
Googleなどの検索エンジンにヒットしませんし、
Discordのコミュニティに入っている人たちだけで完結してしまうような気がします。
Discordのコミュニティに参加すれば良いだけかも知れませんが、ゲストが許されないコミュニティは
開かれたコミュニティと言えるのか、少し疑問ではあります。
フォーラムやマストドンもあるようですが、あまり動いてなさそうです。

</div>
</details>

# 移植する方法
本題です。
互換性で触れたように、移植後のModはFabricとは互換性がなくなるので注意が必要です。

## Quilt Modding テンプレートの準備
Fabric同様にModのテンプレートが用意されているので、そちらを使います。

### GitHubからクローンする
今回はIDEにInteliJ IDEAを使っているので、GitHubからクローンしてプロジェクトを作ります。

https://github.com/QuiltMC/quilt-template-mod

Get from Version ControlからテンプレートのURLを貼り付けるだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447593/69d1f4fb-c728-4965-e262-b58292b4d0ba.png)

クローンが終わるとプロジェクトが開き、Gradleが走るので
Gradleのタスクが終わるまで放置しましょう。

### Quiltバージョンの指定など各種設定
Fabricでいうところの```gradle.properties```に相当する設定は、
```gradle```ディレクトリ内の```libs.versions.toml```で行います。
Modloaderのバージョン、マッピングのバージョン、ライブラリのバージョンなどは
ここで設定を行います。

```toml:libs.versions.toml
[versions]
minecraft = "1.19"
quilt_mappings = "1.19+build.1"
quilt_loader = "0.17.3"
qsl_version = "2.0.0-beta.16"
quilted_fabric_api = "2.0.0-beta.9+0.58.0-1.19"

[libraries]
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
quilt_mappings = { module = "org.quiltmc:quilt-mappings", version.ref = "quilt_mappings" }
quilt_loader = { module = "org.quiltmc:quilt-loader", version.ref = "quilt_loader" }

quilted_fabric_api = { module = "org.quiltmc.quilted-fabric-api:quilted-fabric-api", version.ref = "quilted_fabric_api" }
quilted_fabric_api_deprecated = { module = "org.quiltmc.quilted-fabric-api:quilted-fabric-api-deprecated", version.ref = "quilted_fabric_api" }

[bundles]
quilted_fabric_api = ["quilted_fabric_api", "quilted_fabric_api_deprecated"]

[plugins]
quilt_loom = { id = "org.quiltmc.loom", version = "0.12.+" }

```

設定内容に関しては、Fabric同様にQuiltでもバージョン設定の参考サイト
が公開されているので、Modを作りたいMinecraftのバージョンを指定して
表示された通りに、設定を変更するだけです。

ただし、このサイト情報が古いのか```gradle.properties```で
設定をする時の表記方法なので、ダブルクォーテーションで囲むなど
少し手を加える必要があります。

https://lambdaurora.dev/tools/import_quilt.html

## quilt.mod.jsonの編集
Fabricにおける```fabric.mod.json```に相当する物です。
Modに関する情報が```quilt_loader```で括られていたり
細かい違いはありますが、基本的にはFabricの物を流用できそうです。

```json:quilt.mod.json(Fabric)
"entrypoints": {
    "main": "mod.torchbowmod.TorchBowMod",
    "client": "mod.torchbowmod.TorchBowModClient"
}
```
```json:quilt.mod.json(Quilt)
"entrypoints": {
    "init": "mod.torchbowmod.TorchBowMod",
    "client_init": "mod.torchbowmod.TorchBowModClient"
}
```
ただ、エントリーポイントに関しては変更が必要です。
Fabricでは```main```でしたが、Quiltでは```init```へ変更されています。
またクライアントサイドでは```client```から```client_init```へ変更されています。

## Fabric API から Quilt Standard Library への移行
### Quilt Standard Libraryについて
Quiltでは、FabricにおけるFabric APIの代わりに、Quilt Standard Library通称QSLを利用します。
その為、Fabricで書かれたModをQuiltに移植する場合、QSLのAPIを利用するように
ソースコードを書き換える必要があります。

しかしながら、Quiltが開発中という事もありQuilt Standard Libraryで未実装な処理も多いです。
例として、Entity関連の処理や、レンダリング関係の処理などが実装されておりません。

具体的な対応状況はQSLのリポジトリのReadmeを見てください。

https://github.com/QuiltMC/quilt-standard-libraries

実装されていない部分に関してはFabric APIを利用する形になります。
前提Modとして配布されているQSLでは、Quilted Fabric API通称QFAPIも同梱されており
QSLで対応できない処理に関してはQFAPIを利用する形になります。
QFAPIは名前からも分かるようにFabric APIを呼び出す事が可能なので、
Fabricから移植する際はソースコードを書き換える必要はありません。

とはいえ、Fabricとの互換性はあくまでも現状であり、将来的にも互換性が保証される
わけではないので、QSLで実装可能な場合書き換える事をお勧めします。

憶測なのですが、QFAPIを利用してFabric APIを呼び出す時、QSLにて実装されている
処理に関しては、非推奨アノテーションが付けられているように感じました。
なので、Fabric APIの処理で非推奨になっていた場合QSLに書き換えてみると
良いかもしれません。

### ソースコードの書き換え
今回は、ソースコードの書き換え例として、自作Modのソースコードを用います。

<details>
<summary>
ソースコード
</summary>
<div>
#### Fabric版
https://github.com/noriokun4649/TorchBowMod-fabric
#### Quilt版
https://github.com/noriokun4649/TorchBowMod-quilt
</div>
</details>

私のModで利用しているFabric APIのクラス名は以下の通りです。

- ModInitializer
- ClientModInitializer
- FabricEntityTypeBuilder
- FabricItemGroupBuilder
- EntityRendererRegistry
- ClientPlayNetworking

これらを、QSLで置き換えると
このようになります。

- [ModInitializer](#modinitializerclientmodinitializer)
- [ClientModInitializer](#modinitializerclientmodinitializer)
- 該当無し
- [QuiltItemGroup](#quiltitemgroup)
- 該当無し
- [ClientPlayNetworking](#clientplaynetworking)

クラス名が同じ物に関しては、インポートする
パッケージがFabricからQuiltになります。
また、冒頭でも触れたとおり、同名のクラスが
用意されている場合は、非推奨アノテーションが
付けられていると思うので、インポートしなおすことで
QSLに置き換えることが出来ます。

では早速、これらのコードを実際に書き換えてみます。

#### ModInitializer、ClientModInitializer
まずは、```ModInitializer```についてです。
```java:TorchBowMod.java (Fabric)
public class TorchBowMod implements ModInitializer {
	@Override
	public void onInitialize() {
        //hoge
    }
}
```
```java:TorchBowMod.java (Quilt)
public class TorchBowMod implements ModInitializer {
	@Override
	public void onInitialize(ModContainer mod) {
        //hoge
    }
}
```
見ても分かるとおり、インターフェースの仮引数に
```ModContainer```が追加されました。
この```ModContainer```を使うことで、Modのバージョンや
ModIDなどを、簡単に扱えるようになりました。
従来では、スタティック変数を用意してそこにModIDなどを
ハードコーディングしていたと思いますが、
これを使えば、```quilt.mod.json```に書かれてる内容を
参照できるので、保守性はあがりそうです。
```ClientModInitializer```も同様です。

#### QuiltItemGroup
次に```QuiltItemGroup```です。
Fabricでは```FabricItemGroupBuilder```でしたが、
クラス名が変わっています。
その為、ソースコードもそのまま流用は出来ず、
少し書き換える必要があります。
```java:TorchBowMod.java(Fabric)
public static final ItemGroup TORCH_BOW_GROUP = FabricItemGroupBuilder.build(new Identifier(MODID, "torchbowmod_tab"),
            () -> new ItemStack(TorchBowMod.TORCH_BOW_ITEM));
```
```java:TorchBowMod.java(Quilt)
public static final ItemGroup TORCH_BOW_GROUP = QuiltItemGroup.builder(new Identifier(MODID, "torchbowmod_tab"))
			.icon(() -> new ItemStack(TorchBowMod.TORCH_BOW_ITEM)).build();
```
Fabricでは、ビルダーの引数にグループタブ名とアイコンを同時に指定していましたが
Quiltでは、グループタブ名だけ指定し、メソッドチェーンでアイコンを指定する
形になっています。
引数に指定する形に比べて、メソッド名で何を指定しているかが分かるので、
扱いやすくなっています。

#### ClientPlayNetworking
このクラスに関しては、FabricとQuiltで変わった点は
特にありませんので、割愛します。
あえて、触れるとしたらマッピングの違いによって
多少メソッド名が変わったぐらいです。 

# Mod配布時の注意点
現状では、Quilt Modloaderに致命的なバグがあり、
**Modのファイル名にブラケット（```[]```）があると起動時にクラッシュします。**
そのため、CurseForgeなどで配布する際には、
ファイル名にブラケットを入れないようにする必要があります。

なお、このバグは2022年6月20日に報告されていますが、修正はされていないようです。

https://github.com/QuiltMC/quilt-loader/issues/90  

# さいごに
今回、IssusでFabric版が互換性あるはずなのにQuiltに読み込まれないということで
Quiltに対応させるコトになったのですが。
実は、最後に触れたQuiltのバグが原因で読み込まれないということが分かりました。

とはいえ、新しいQuiltというModloaderを知る事ができましたし、
いろいろな知見を得ることが出来たので、とても良かったと思っています。

おかげで、Fabric APIからQSLへ移行することも出来ましたし。

ということで、ぜひQuiltに対応させてみてはどうでしょうか。