たくさんの自由帳

自作 MOD の Minecraft 1.21.11 移行とマッピング移行メモ

投稿日 : | 0 日前

文字数(だいたい) : 10031

どうもこんばんわ。
1.21.11 対応版がクリスマスプレゼントです。 もう年末ですね。寒い!!!

本題

Minecraft 1.21.11が来たので自作MODを対応させました。やり?の使い方がわからんかった。。
チェストがクリスマス仕様?一体何のことやら

攻略中

おわり

NeoForgeForgeに関しては、特にそんな大きな変更はないです。
またFabricに関しても大きな変更はありません。Yarn マッピングからMojang マッピングの移行作業をやるならそこそこ大変です。

前回

差分

Fabricですが、Yarn マッピングからMojang マッピングへ移行したため差分が大きいです、

ドキュメント

Fabricチームが書いてくれた資料です。
今回はMojang マッピングへの移行を行うので、難読化がなくなった記事も。

NeoForgeも。今の段階のFabricと違ってMojang マッピングを使っているのでその話があります。

難読化がなくなったというニュース

難読化されなくなる。
今まではMinecraft.jar ファイルを何らかの方法で.java コード相当に復元したとしても、でたらめな名前が表示されるだけになると思う。やったことないので知らない。

目的としては、でたらめな名前に置き換えることで解析をある程度妨害できるので、中で使っているプログラムや画像、音声なんかを引っこ抜かれないようにするため。だと思ってる。
(次点にでたらめな短い名前に置き換えればコード自体が小さくなり、ファイルサイズ自体が小さくなるのも見込めますが、多分それが目的じゃないと思うので)

ところが難読化を辞めるとのこと。MODユーザーのためだって。
お金取ってるゲームでこの判断がみんなにも出来るだろうか。

これまでにもMODユーザーには結構親身に対応してくれていて、MojangMinecraftを難読化しつつも、数年前から難読化を解除し元のプログラムに戻すためのファイルを公開していました。
マッピングファイル、難読化する前の名前とした後の名前の関連付けを記録したファイル)
が、今回は難読化自体をやめてしまおうと。

大きなニュースに思えますが、遊ぶ側は特に影響ないです。
また作る側も、難読化されないので元のMinecraftコードが見やすくはなると思う・・・今までは各自マッピングファイルを使って難読化前相当のコードを生成してたのでその手間がなくなる。くらい。

NeoForgeForgeはすでにMojang 公式マッピングファイルを利用していたため、置き換えが大変みたいな問題ないはず?
Fabricは自分たちでマッピングファイルを作っていました。そして今回、難読化を辞めたバージョンより自分たちで作ったマッピングを廃止することを決定したそうです。

まずはSNAPSHOTリリースから難読化を解除した版が配信されるとのこと。
いつ正式版に適用されるかわからないので、1.21.11の今急いでやる必要もない。が、そこまで大変じゃなかったです(後述)

ついでにバージョン命名ルールも変わる

1.21.11の次は26.1になる。iOSみたいになったね。
これが俗に言うカレンダーバージョニングですか、今よりわかりやすくなるならそれでいいと思います。

Fabric チーム側の話

これは完全に余談ですが、公式の声明をみてください。

FabricmoddingではMojang 公式マッピングではなく、自分たちで作っていたYarn マッピングがデフォルトで採用されていました。(moddingテンプレートがそれだったはず)
同じMODでも、FabricコードNeoForge / Forgeでのコードを見比べると、部分的に名前が違って見えるのは、このマッピングが違うから。
難読化されたクラス名、関数名にどんな名前をつけるか。の関連付けをしてくれていたのが、このYarn / Mojang マッピングファイルだったわけですね。

Yarn マッピングIntermediaryというプロジェクトが生成した賜物でした。
このIntermediaryは、難読化された関数、クラスに、人間がわかる名前をつける機能だけでなく、バージョンを跨いで人間が読める同じ名前を付ける機能もありました。

難読化は新しいバージョンが公開されるたびに行われます。よって、例えばBlocks.jarは今のバージョンではab.javaになっていても、新しいバージョンではaab.javaになるかもしれません。
Intermediaryはちゃんとクラスや関数の中身を見て前のバージョンと同じなら同じ名前をつけてくれました。

ある程度MODで遊んだことがあればわかるかもしれないですが、バグ修正バージョンで同じMODファイルがなぜか遊べるみたいな。
バグ修正バージョン対応版を開発者が出さなくても、なぜか動いているみたいな。この裏側にはIntermediaryがいたわけ・・・

ところが、難読化がされなくなるということは、Yarn/Mojang マッピング自体がそもそも不要になることを意味します。
そのため、Yarn マッピングは廃止されることが発表されました。既存のバージョン向けには残るが、新しくリリースされることはなく、テンプレートや Fabric ドキュメントでは Mojang マッピングを採用するようになります。

using_mojang_mapping

遊ぶ側メリット

ほぼない。しいていえば

クラッシュしたときのスタックトレースが意味のある文字列になる。のと、
Minecraftの挙動を調べるとかはわんちゃん出来るんじゃないでしょうか。調べるにしろEULAが引き続き適用されるとは思いますが。

作る側メリット

難読化されなくなるため、Yarn / Mojang マッピングからMinecraftのソースコードを復元する手間がなくなるはず。
GradleにあるgenSourcesする手間が不要に!

あとはマッピングファイルからソースの復元とは違い、そもそも難読化されないので、関数の中で使われている変数名(ローカル変数)の名前がそのまま表示されるようになるはず?。
ProGuardによらないと思うが、マッピングファイルの手が及ぶのはクラスクラスの関数、変数まで。それ以上の、関数内の変数名は難読化されている以上見れなかったんじゃないかな?

正直、大きく取り上げた割には時間が節約できるとか、関数の中の変数の名前が見れるとかなので、実はそこまで大騒ぎすることでもない。

Fabric Mojang マッピング移行

本家

私はKotlinで書いているので、Loom Gradle プラグインではなく、Ravel IDEA プラグインの方を使うことにした。
Javaのみの場合はどちらでも良いらしい。

plugin_download

上の手順にしたがって入れます。zipファイルがダウンロード出来るので、IDEA設定プラグインの設定を開いて、歯車を押してInstall Plugin From Diskを選び、さっきのzipを読み込む。

install_plugin

次に、yarn マッピングmojang マッピングのファイルをダウンロードします。

から、今使っているyarn マッピングのバージョンを選択してダウンロード。gradle.propertiesを開いて、ここの値と同じものを探します。

minecraft_version=1.21.11
yarn_mappings=1.21.11+build.3 # ←これ
loader_version=0.18.4
loom_version=1.14-SNAPSHOT
fabric_kotlin_version=1.13.8+kotlin.2.3.0

同じバージョンを探す

見つけたら開いて、その中のmergedv2.jarファイルをダウンロードします。

ダウンロード

ダウンロードできたら、7zip等を使い展開し、mappingsフォルダのmappings.tinyを探します。このファイルが必要です。

展開

これが欲しかった

Mojang マッピングの方は以下を開いて、
https://piston-meta.mojang.com/mc/game/version_manifest_v2.json

JSON

JSON オブジェクトがいくつもありますが、自分のバージョン(今回1.21.11)が書いてあるJSON オブジェクトを探し、その中のurlの値に書いてあるアドレスにアクセスします。

client_mappings_object

開いたら、サイト内検索でclient_mappingsで検索をかけます。
見つかったら同様にurlの値のアドレスにアクセスしてください。できたら、謎の文字がいっぱいでてくるので、何でも良いのでダウンロードしてください。

client.txt

一番楽なのはWebサイトを右クリックして名前をつけて保存するのがいいかと。

2つそろった

適当にファイルを選んで右クリックし、Refactorを押して、Remap Using Ravelを押します。

Ravel

するとこんなダイアログがでます。

Ravel_dialog

まず左側の+を押します。Add Local File Mappting...を選びます。選んだら、ファイルを選ぶ画面になるので、まずはyarnの方を読み込ませます。
mappings.tinyですね。

add_mapping

dialog

選んだら、それぞれ以下のように選びます。

なまえあたい
Source Namespacenamed
Destination Namespaceofficial

出来たらOK、同様にMojangclient.txtを選び、以下のように選びます。

なまえあたい
Source Namespacetarget
Destination Namespacesource

最後、右側にモジュールが表示されています。一個ずつ選んで+ボタンを押しましょう。すべてやります。

これでOKを押すと処理が始まります。

yarn とのお別れ

build.gradleファイルを開き、以下のyarn_mappingsの1行をofficialMojangMappings()に置き換えます。

- mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
+ mappings loom.officialMojangMappings()

gradle.propertiesファイルのyarn_mappingsの値も使わなくなったので削除できます。

- yarn_mappings=1.21.11+build.3

エラーを治す

IDEAを使っている場合は、左の虫メガネボタンを押すか、Shift + Ctrl + Fキーを同時押しするかで全体検索を出すことが出来ます。 ここで、TODO(Ravel)で検索をかけることで、自動で置き換えできなかった箇所が見つかるという仕組み。

TODOを解決

package nameがおかしい言われてますが、確かに存在するのでよくわからない。

あと普通にJavaコードのimport間違えている箇所があるのでそれは手動で直した。

Gradle Sync する

IDEAの右にあるゾウのボタンGradle)を押し、その中にある更新ボタンみたいなのを押します。
これをするまでソースコードが赤いままになると思う。

GradleSync

これで終わりなはず・・・!

起動してみる

再生マークの隣りにあるドロップダウンメニューをMinecraft Clientにしてチャレンジ・・・

実行

しっぱい!w
コード直します。

'useWithoutItem' overrides nothing. Potential signatures for overriding

'useWithoutItem' overrides nothing. Potential signatures for overriding:
fun useWithoutItem(blockState: BlockState, level: Level, blockPos: BlockPos, player: Player, blockHitResult: BlockHitResult): InteractionResult

NonNullだったらしい。シグネチャ違い

override fun useWithoutItem(blockState: BlockState?, level: Level?, blockPos: BlockPos?, player: Player?, blockHitResult: BlockHitResult?): InteractionResult?

override fun useWithoutItem(blockState: BlockState, level: Level, blockPos: BlockPos, player: Player, blockHitResult: BlockHitResult): InteractionResult

したらなおった。ところでmoddingにおいてKotlinJava相互のNull安全の体験、こんなにも良かった記憶ないんだけど。
と思って調べたらNull系アノテーションがJSpecifyになったらしい。相互利用が良くなったのはこれのおかげかも知れない

Type argument is not within its bounds: must be subtype of 'Any'.

<T>だとAny?nullを許容できてしまうので、<T:Any>してNonNullの型のみにする

private fun <T : Any> applyEffects(
    entries: List<ConditionalEffect<T>>,
    lootContext: LootContext,
    onEffect: (T) -> Unit
) { }

Argument type mismatch: actual type is 'Item.Properties?', but 'Item.Properties' was expected

同様にNonNullが保証されたらしい

class ClickManaitaBaseItem(settings: Properties?, private val dropSize: Int = 2) : Item(settings)

class ClickManaitaBaseItem(settings: Properties, private val dropSize: Int = 2) : Item(settings)

Mojang マッピングしょかん

  • Kotlinを使っている場合、?(Nullable)を消していく作業
    • NonNullなの?信じるよ?
  • ワールドクラスがLevelって名前なんだけど由来何なんだろう、?
    • これとかはyarnのが好きだった
  • なんかiとかjとかの引数名の時があるんだけど手動で難読化してるの?
    • 誰ののせいなのかわからん

jar ファイル作成

作る側はこれまで通りgradlebuildで良いはず。

遊ぶ側

遊ぶ側も同様にMODjarファイルFabric APIと、私みたいにKotlinで書いている場合はFabric Language Kotlinmodsフォルダへ入れれば起動できた。

そのほか

ここからはFabricNeoForgeForgeで同じ!

Gradle

  • Fabric
    • 9.2.1
  • NeoForge
    • 9.2.0
  • Forge
    • かわらず?

ResourceLocation→Identifier 名前変更

NeoForgeForgeみたいにMojang マッピングを使っている場合の話。

private static final ResourceLocation ID_CLICKMANAITA_WOOD = ResourceLocation.fromNamespaceAndPath(ClickManaita.MOD_ID, "clickmanaita_wood");
private static final ResourceKey<Item> KEY_CLICKMANAITA_WOOD = ResourceKey.create(Registries.ITEM, ID_CLICKMANAITA_WOOD);

public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(ClickManaita.MOD_ID);
public static final DeferredItem<ClickManaitaBaseItem> CLICKMANAITA_WOOD = ITEMS.register(KEY_CLICKMANAITA_WOOD.location().getPath(), () -> /* 省略... */ );

ResourceLocationIdentifierに置き換える。location()identifier()に置き換える

private static final Identifier ID_CLICKMANAITA_WOOD = Identifier.fromNamespaceAndPath(ClickManaita.MOD_ID, "clickmanaita_wood"); // ここと
private static final ResourceKey<Item> KEY_CLICKMANAITA_WOOD = ResourceKey.create(Registries.ITEM, ID_CLICKMANAITA_WOOD);

public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(ClickManaita.MOD_ID);
public static final DeferredItem<ClickManaitaBaseItem> CLICKMANAITA_WOOD = ITEMS.register(KEY_CLICKMANAITA_WOOD.identifier().getPath(), () -> /* 省略... */ ); // ここ

ところで、このIdentifierFabricYarn マッピングで使われていた名前なんですよね。以下参照
Mojang マッピングResourceLocationだった。が、なぜか今Fabricと同じ名前に変わった。

private val ID_CLICKMANAITA_WOOD = Identifier.of("clickmanaita", "clickmanaita_wood")
private val KEY_CLICKMANAITA_WOOD = RegistryKey.of(RegistryKeys.ITEM, ID_CLICKMANAITA_WOOD)

val CLICKMANAITA_WOOD = ClickManaitaBaseItem(settings = Item.Settings().registryKey(KEY_CLICKMANAITA_WOOD), dropSize = 2)
Registry.register(BuiltInRegistries.ITEM, KEY_CLICKMANAITA_WOOD, CLICKMANAITA_WOOD)

NeoForgeチームも言っていますが、どういう風の吹き回しでこのような改名になったのかは中の人しかわからないでしょう。

  • 単にResourceLocationよりも短いから採用した説
  • FabricMODMojang マッピングの移行時の混乱を避けるため説
  • Yarn マッピングのことを忘れないようにするため説
    • 尊敬と言うか敬意というか

NonNull アノテーション

使ったことがなくてよくわからないですが、JSpecifyってライブラリの@NonNullアノテーションが採用されたらしいです。
Kotlinnull安全機能は、Kotlin言語で書くだけでなく、Java言語でも@NonNullを書いたコードに対しても発動します。なのでMojang マッピングへの移行をしつつ、NonNullに書き直すようにする必要もあります。

Optionalを駆逐できるようになるのかはわかりません。
わたしてきにはOptional#isPresent()nullable != nullよりも優れてるとはあんまり思わないので、アノテーション付けるだけでいい感じに支援されるならもうそれでいいと思った。

おわりに

IDEAStart Free Trialボタンめっちゃ緑色で自己主張してて草