Fabric ModをKotlinで開発する方法・導入ガイド


Fabric ModをKotlinで開発する方法・導入ガイド

MinecraftのMod開発は、ゲームに新しい要素や機能を追加する創造的なプロセスです。伝統的にJavaが使われてきましたが、近年ではKotlinのようなモダンな言語も選択肢に入ってきました。特に、軽量でモジュール性の高いModdingフレームワークであるFabricとKotlinを組み合わせることで、効率的かつ安全にModを開発することが可能です。

この記事では、Fabric ModをKotlinで開発するための環境構築から基本的なMod作成までを詳細に解説します。約5000語を費やし、初心者の方でも理解できるよう、各ステップや概念を丁寧に説明していきます。

はじめに:FabricとKotlinでMod開発を選ぶ理由

Minecraft Mod開発には、主にForgeとFabricという二つの主要なModdingフレームワークが存在します。それぞれに特徴がありますが、FabricはForgeに比べて比較的軽量で、アップデートへの追従が早い傾向があります。また、APIがシンプルで、開発者が低レベルの機能にアクセスしやすい構造になっています。

一方、KotlinはJetBrainsが開発したJVM(Java Virtual Machine)上で動作するプログラミング言語です。Javaとの完全な相互運用性を持ちながら、Javaよりも簡潔で安全なコードを書くことができます。特にMod開発においては、以下のようなメリットがあります。

  • 簡潔な構文: ボイラープレートコード(定型的な記述)が少なく、Javaに比べて少ないコード量で同じ機能を実現できます。
  • 安全性: Null安全が言語レベルでサポートされており、Mod開発で頻繁に遭遇するNullPointerExceptionのリスクを減らすことができます。
  • 相互運用性: JavaライブラリやAPIをそのまま使用できるため、Fabric APIやMinecraftの既存コードとシームレスに連携できます。
  • モダンな機能: データクラス、コルーチン、拡張関数など、開発効率を高める多くのモダンな機能を提供します。

これらの理由から、FabricとKotlinの組み合わせは、効率的でメンテナンスしやすいMinecraft Modを開発するための強力な選択肢となります。

開発環境の構築

Mod開発を始める前に、適切な開発環境を準備する必要があります。以下のソフトウェアが必要です。

  1. Java Development Kit (JDK): MinecraftとFabricはJava上で動作するため、開発にはJDKが必要です。Minecraftのバージョンによって推奨されるJDKバージョンが異なります。一般的にはJava 17またはJava 21が使用されますが、開発対象のMinecraftバージョンに合ったJDKを用意してください。Oracle JDKやOpenJDK(Adoptium Temurinなど)があります。
  2. 統合開発環境 (IDE): コードの記述、デバッグ、ビルドなどを効率的に行うためにIDEは不可欠です。Kotlin開発にはIntelliJ IDEAが最も推奨されます。Community Editionは無償で利用できます。EclipseやVS Codeでも開発は可能ですが、KotlinのサポートやGradleとの連携を考慮するとIntelliJ IDEAが最もスムーズです。
  3. Git: バージョン管理システムです。Mod開発の進捗管理や問題発生時のロールバックに役立ちます。必須ではありませんが、導入を強く推奨します。

JDKのインストール

使用しているOS(Windows, macOS, Linux)に応じたJDKインストーラーをダウンロードし、インストールします。インストール後、コマンドプロンプトやターミナルで java -version および javac -version を実行し、正しくインストールされ、パスが通っていることを確認してください。

bash
java -version
javac -version

IntelliJ IDEAのインストール

JetBrainsの公式ウェブサイトからIntelliJ IDEA Community Editionをダウンロードし、インストールします。インストール時にKotlinプラグインが有効になっていることを確認してください(通常はデフォルトで有効です)。

Fabric開発ワークスペースのセットアップ

Fabric Mod開発は、通常Gradleというビルドツールを使用して行います。Fabricでは、fabric-example-mod というテンプレートプロジェクトを提供しており、これを基に開発を始めるのが最も簡単です。

  1. fabric-example-mod をクローンまたはダウンロード:
    GitHubのリポジトリからプロジェクトをダウンロードするか、Gitを使ってクローンします。

    bash
    git clone https://github.com/FabricMC/fabric-example-mod.git your-mod-name
    cd your-mod-name

  2. Gradleラッパーの実行(初回のみ):
    プロジェクトに含まれるGradleラッパー(gradlew または gradlew.bat)を実行して、Gradleのダウンロードと初期設定を行います。これにより、必要な依存関係やFabric Loomプラグインがダウンロードされます。

    “`bash

    Linux/macOS

    ./gradlew

    Windows

    ./gradlew.bat
    ``
    このコマンドはGradleを初期化するだけでなく、
    genSources` タスクを実行してMinecraftのソースコードをダウンロードし、開発用にセットアップします。これには時間がかかる場合があります。

  3. IntelliJ IDEAでプロジェクトを開く:
    IntelliJ IDEAを起動し、「Open」または「開く」を選択します。クローン/ダウンロードしたプロジェクトのルートディレクトリ(build.gradle ファイルや gradlew ファイルがある場所)を選択して開きます。

    IntelliJ IDEAは自動的にGradleプロジェクトとして認識し、必要な依存関係の解決やプロジェクト構造の構築を行います。画面右下などに表示されるGradle同期のプロンプトが表示されたら、必ず同期を実行してください。同期が完了するまで待ちます。

  4. Kotlin設定の確認:
    プロジェクトが開いたら、Kotlinが正しく設定されているか確認します。
    build.gradle.kts ファイル(Gradle Kotlin DSLを使用する場合)または build.gradle ファイル(Groovy DSLを使用する場合)を開きます。fabric-example-mod はデフォルトでGroovy DSL (build.gradle) を使用していますが、Kotlin DSL (build.gradle.kts) に変更することも可能です。Kotlin DSLを使用する場合は、以下のスニペットが build.gradle.kts に含まれていることを確認します。

    “`kotlin
    plugins {
    // … その他のプラグイン
    kotlin(“jvm”) version “1.9.20” // 使用するKotlinのバージョン
    }

    repositories {
    mavenCentral() // Kotlin標準ライブラリなどのリポジトリ
    }
    ``
    また、ソースセットのディレクトリ構造がKotlinに対応しているか確認します。通常は
    src/main/kotlinsrc/client/kotlin,src/server/kotlinディレクトリを使用します。fabric-example-modのGroovyバージョンではsrc/main/javaがデフォルトですが、Kotlinコードを置くためにsrc/main/kotlinディレクトリを作成し、IDEにソースルートとして認識させる必要があります。(右クリック ->Mark Directory as->Sources Root`)。

    もしKotlin DSL (build.gradle.kts) を最初から使いたい場合は、新しいプロジェクトを作成する際にGradle + Kotlinを選び、Fabric Loomを適用する方法もありますが、既存の fabric-example-mod をKotlin対応させる方が手軽な場合が多いです。この記事では、fabric-example-mod を基に、Kotlinファイルを追加して開発を進める方法を解説します。

開発環境のテスト

環境が正しく設定されたか確認するために、Modを実行してみましょう。

IntelliJ IDEAのGradleツールウィンドウ(通常は右側のタブ)を開き、Tasks -> fabric の中にある runClient タスクをダブルクリックして実行します。または、Run/Debug Configurationを設定して実行することもできます。

Run/Debug Configurationを設定する方法:
1. IntelliJ IDEAのメニューバーから Run -> Edit Configurations... を選択します。
2. 左上の + ボタンをクリックし、Gradle を選択します。
3. 名前を Minecraft Client とします。
4. Gradle project に現在のプロジェクトを選択します。
5. TasksrunClient と入力します。
6. Apply -> OK をクリックします。
7. これで、画面上部の実行ボタンの隣に「Minecraft Client」という設定が追加されます。この設定を選択して実行ボタン(緑色の再生アイコン)をクリックします。

これにより、Mod開発環境がロードされたMinecraftクライアントが起動します。ゲームが正常に起動すれば、開発環境のセットアップは完了です。同様に、runServer タスクで開発サーバーを起動することもできます。

Fabric Modの基本構造

Fabric Modはいくつかの基本的な要素で構成されます。

  1. fabric.mod.json: Modのマニフェストファイルです。ModのID、バージョン、名前、説明、依存関係、そして後述するエントリポイントなどを定義します。これはModのルートディレクトリに配置する必要があります。
  2. エントリポイント: Modの起動時にFabricが呼び出す特定のエントリポイントインターフェースを実装したクラスです。代表的なものに ModInitializer があります。
  3. Modのコード: ゲームに機能を追加するための実際のKotlin/Javaコードです。アイテム、ブロック、エンティティ、イベントハンドラなどを定義・登録します。

fabric.mod.json の設定

fabric.mod.json はJSON形式のファイルです。重要なフィールドは以下の通りです。

“`json
{
“schemaVersion”: 1,
“id”: “your_mod_id”, // Modの一意なID(小文字、アンダースコア、数字のみ)
“version”: “${version}”, // Modのバージョン。Gradleで置換されるのが一般的
“name”: “Your Mod Name”, // Modの表示名
“description”: “A brief description of your mod.”, // Modの説明
“authors”: [ // 作者リスト
“Your Name”
],
“contact”: { // 連絡先情報
“homepage”: “https://your.homepage.url/”,
“sources”: “https://your.source.code/url/”
},
“license”: “CC0-1.0”, // ライセンス情報
“icon”: “assets/your_mod_id/icon.png”, // Modのアイコンパス

“environment”: ““, // Modが動作する環境 (““, “client”, “server”)
“entrypoints”: { // エントリポイントの設定
“main”: [ // メインエントリポイント(クライアント・サーバー共通)
“com.example.your_mod_id.YourModId” // 実装クラスの完全修飾名
],
“client”: [ // クライアント専用エントリポイント
// “com.example.your_mod_id.YourModIdClient”
],
“server”: [ // サーバー専用エントリポイント
// “com.example.your_mod_id.YourModIdServer”
]
},
“mixins”: [ // Mixinの設定(高度なトピック)
“your_mod_id.mixins.json”
],
“depends”: { // 依存関係
“fabricloader”: “>=0.15.0”, // 必要なFabric Loaderのバージョン
“minecraft”: “~1.20.1”, // 互換性のあるMinecraftバージョン
“java”: “>=17”, // 必要なJavaバージョン
“fabric-api”: “” // Fabric APIへの依存(推奨)
},
“suggests”: { // 推奨されるMod
“another_mod”: “

}
}
``id` フィールドは非常に重要で、Mod内のアイテムやブロックなどの識別子(Identifier)の一部として使用されます。このIDはMod間で重複しないように、他のModとの衝突を避けるためにユニークである必要があります。慣習として、ModのIDは全て小文字のスネークケースで記述されます。

entrypoints フィールドは、FabricがModをロードする際に実行するクラスを指定します。main はクライアントとサーバーの両方で実行されるコード、client はクライアント専用、server はサーバー専用のコードのエントリポイントを指定します。通常は main エントリポイントでModの初期化を行います。

メインModクラスの作成(Kotlin)

main エントリポイントで指定したクラスは、net.fabricmc.api.ModInitializer インターフェースを実装する必要があります。このインターフェースには onInitialize() というメソッドがあり、FabricがModをロードした際にこのメソッドが呼び出されます。

Kotlinでは、シングルトンオブジェクトとしてModのメインクラスを定義するのが一般的です。これにより、インスタンスを意識することなく、Mod全体で共有されるロジックや設定を管理できます。

src/main/kotlin/com/example/your_mod_id/YourModId.kt のようなパスに以下の内容でファイルを作成します。(your_mod_id やパッケージ名は適宜変更してください)

“`kotlin
package com.example.your_mod_id // プロジェクトのパッケージ名に合わせる

import net.fabricmc.api.ModInitializer
import org.slf4j.LoggerFactory

// Modのメインクラスをシングルトンオブジェクトとして定義
object YourModId : ModInitializer {

// ロガーの取得
private val LOGGER = LoggerFactory.getLogger("YourModId") // fabric.mod.jsonのidを使用するのが慣習

// Mod初期化時に呼び出されるメソッド
override fun onInitialize() {
    // 初期化ログの出力
    LOGGER.info("Hello Fabric world with Kotlin!")

    // ここにModのアイテムやブロックなどの登録処理を記述する
    // 例: MyItems.register()
    // 例: MyBlocks.register()
}

// Mod IDを定数として持っておくと便利
const val MOD_ID = "your_mod_id"

}
“`

object YourModId : ModInitializer とすることで、YourModId という名前のシングルトンオブジェクトが ModInitializer インターフェースを実装します。onInitialize() メソッド内に、Modが起動した際に行いたい処理(アイテムやブロックの登録、イベントリスナーの設定など)を記述します。

LOGGER は、Modの動作状況などをコンソールに出力するために使用します。エラーや警告などの情報を記録するのに役立ちます。

MOD_ID を定数として定義しておくと、Mod内の他の場所で fabric.mod.json のIDを参照する必要がある場合に便利です。

このクラスを fabric.mod.json"entrypoints": { "main": [...] } フィールドに指定する必要があります。

json
"entrypoints": {
"main": [
"com.example.your_mod_id.YourModId" // <-- ここをKotlinオブジェクトの完全修飾名に合わせる
],
// ... 他のエントリポイント
}

Modの基本的な機能:アイテムとブロックの追加

Mod開発の最も基本的な機能の一つは、新しいアイテムやブロックをゲームに追加することです。Fabric APIは、これらの要素を効率的に登録するためのAPIを提供しています。

Minecraftでは、ほとんど全てのゲーム要素が「レジストリ(Registry)」に登録されています。レジストリは、ゲームが特定のID(Identifier)に対応する要素(アイテム、ブロック、コマンドなど)を検索するためのマップのようなものです。新しい要素をゲームに追加するには、適切なレジストリに登録する必要があります。

識別子 (Identifier)

Minecraftでは、全ての登録可能な要素は Identifier というクラスで一意に識別されます。Identifiernamespace:path という形式を持ちます。
* namespace: 通常はModのID(fabric.mod.jsonid)。Minecraft本体の要素は minecraft ネームスペースを使用します。
* path: そのネームスペース内での要素の一意な名前(例: my_item, custom_block)。

Kotlinでは、Identifier("your_mod_id", "my_item") のように作成します。前述の MOD_ID 定数を使うと便利です。

kotlin
// 例
val myItemIdentifier = Identifier(YourModId.MOD_ID, "my_item")

アイテムの登録

新しいアイテムを追加するには、net.minecraft.item.Item クラスのインスタンスを作成し、net.minecraft.registry.Registries.ITEM に登録します。

アイテムクラスをシングルトンオブジェクトとして定義し、その中でアイテムインスタンスを管理・登録するのがKotlinらしいやり方です。

src/main/kotlin/com/example/your_mod_id/registry/MyItems.kt のようなファイルを作成します。

“`kotlin
package com.example.your_mod_id.registry

import com.example.your_mod_id.YourModId // メインModクラスをインポート
import net.minecraft.item.Item
import net.minecraft.item.ItemGroup
import net.minecraft.item.ItemGroups // アイテムグループ定義のクラス
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.text.Text // アイテム名などのテキスト表示用
import net.minecraft.util.Identifier
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents // アイテムグループへの追加イベント

// Modのアイテムを管理・登録するためのシングルトンオブジェクト
object MyItems {

// 新しいアイテムのインスタンスを作成
// Item.Settings() でアイテムの設定を行う
val MY_CUSTOM_ITEM: Item = Item(Item.Settings())

// アイテムをゲームに登録するメソッド
fun registerItems() {
    // Registry.register(レジストリ, 識別子, 登録するオブジェクト)
    Registry.register(Registries.ITEM, Identifier(YourModId.MOD_ID, "my_custom_item"), MY_CUSTOM_ITEM)

    // 他のアイテムもここで登録する
    // val ANOTHER_ITEM: Item = Item(Item.Settings())
    // Registry.register(Registries.ITEM, Identifier(YourModId.MOD_ID, "another_item"), ANOTHER_ITEM)
}

// アイテムを既存のアイテムグループに追加するメソッド
// アイテムグループはゲーム内のインベントリやクリエイティブタブの分類
fun addItemsToItemGroups() {
    // ItemGroupEvents.ModifyEntries を使用して、特定のアイテムグループが作成される際にアイテムを追加する
    ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS).register { entries ->
        // .add() メソッドでアイテムを追加
        entries.add(MY_CUSTOM_ITEM)
    }

    // 他のグループに追加する場合
    // ItemGroupEvents.modifyEntriesEvent(ItemGroups.BUILDING_BLOCKS).register { entries ->
    //     entries.add(ANOTHER_ITEM)
    // }
}

}
“`

この MyItems オブジェクトの registerItems() メソッドを、メインModクラス YourModIdonInitialize() メソッドから呼び出すことで、アイテムがゲーム起動時に登録されます。

YourModId.kt を以下のように修正します。

“`kotlin
package com.example.your_mod_id

import com.example.your_mod_id.registry.MyItems // 作成したアイテムレジストリをインポート
import net.fabricmc.api.ModInitializer
import org.slf4j.LoggerFactory

object YourModId : ModInitializer {

private val LOGGER = LoggerFactory.getLogger("YourModId")

const val MOD_ID = "your_mod_id"

override fun onInitialize() {
    LOGGER.info("Hello Fabric world with Kotlin!")

    // ここでアイテム登録メソッドを呼び出す
    MyItems.registerItems()
    // アイテムグループへの追加メソッドも呼び出す
    MyItems.addItemsToItemGroups()

    // 他のレジストリ登録(ブロックなど)もここで行う
    // MyBlocks.registerBlocks()
}

}
“`

Item.Settings() には様々な設定を指定できます。例えば、スタックサイズの上限、耐久値、エンチャントの可否などです。

ブロックの登録

新しいブロックを追加するには、net.minecraft.block.Block クラスのインスタンスを作成し、net.minecraft.registry.Registries.BLOCK に登録します。ブロックはアイテムとしても存在する必要があるため(インベントリに入れて設置するため)、関連付けられた net.minecraft.item.BlockItemRegistries.ITEM に登録する必要があります。

src/main/kotlin/com/example/your_mod_id/registry/MyBlocks.kt のようなファイルを作成します。

“`kotlin
package com.example.your_mod_id.registry

import com.example.your_mod_id.YourModId // メインModクラスをインポート
import net.minecraft.block.AbstractBlock
import net.minecraft.block.Block
import net.minecraft.block.Blocks // 既存のブロック定義
import net.minecraft.item.BlockItem
import net.minecraft.item.Item
import net.minecraft.item.ItemGroups
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.util.Identifier
import net.fabricmc.fabric.api.item.v1.FabricItemSettings // Fabric独自のアイテム設定
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings // Fabric独自のブロック設定

// Modのブロックを管理・登録するためのシングルトンオブジェクト
object MyBlocks {

// 新しいブロックのインスタンスを作成
// FabricBlockSettings を使用するとより多くの設定が可能
val MY_CUSTOM_BLOCK: Block = Registry.register(
    Registries.BLOCK, // ブロックレジストリ
    Identifier(YourModId.MOD_ID, "my_custom_block"), // 識別子
    Block(FabricBlockSettings.copy(Blocks.STONE).strength(4.0f)) // 元のブロック(STONE)の設定をコピーし、強度を変更
)

// ブロックに対応するアイテム(BlockItem)のインスタンスを作成し登録
// BlockItemのコンストラクタには対応するBlockインスタンスとItemSettingsを指定
val MY_CUSTOM_BLOCK_ITEM: Item = Registry.register(
    Registries.ITEM, // アイテムレジストリ
    Identifier(YourModId.MOD_ID, "my_custom_block"), // 識別子(ブロックと同じにするのが一般的)
    BlockItem(MY_CUSTOM_BLOCK, FabricItemSettings()) // 対応ブロックとアイテム設定
)


// ブロックをゲームに登録するメソッド(BlockItemも含む)
// Kotlinでは、プロパティ初期化時にRegistry.registerを直接呼び出すことが多いが、
// 明示的にメソッドにまとめることも可能
fun registerBlocks() {
    // 上記のプロパティ定義で既に登録が完了しているため、ここでは追加の登録処理は不要
    // もしプロパティ初期化で登録しない場合は、ここでRegistry.registerを呼び出す
}

// ブロックアイテムを既存のアイテムグループに追加するメソッド
fun addBlockItemsToItemGroups() {
    ItemGroupEvents.modifyEntriesEvent(ItemGroups.BUILDING_BLOCKS).register { entries ->
        // ブロックアイテムを追加
        entries.add(MY_CUSTOM_BLOCK_ITEM)
    }
}

}
“`

MY_CUSTOM_BLOCK の定義では、Registry.register をプロパティ初期化時に直接呼び出しています。これはKotlinのコード構造と相性が良く、一般的によく使われるパターンです。ただし、これにより登録順序がファイル内の定義順になることに注意が必要です。

FabricBlockSettings を使用することで、既存のブロックの設定をコピーしたり、硬さ、爆破耐性、音、光レベルなど、様々な物理的特性を設定できます。

MyBlocks オブジェクトの registerBlocks()addBlockItemsToItemGroups() メソッドを、メインModクラス YourModIdonInitialize() メソッドから呼び出します。

“`kotlin
package com.example.your_mod_id

import com.example.your_mod_id.registry.MyBlocks // 作成したブロックレジストリをインポート
import com.example.your_mod_id.registry.MyItems // 作成したアイテムレジストリをインポート
import net.fabricmc.api.ModInitializer
import org.slf4j.LoggerFactory

object YourModId : ModInitializer {

private val LOGGER = LoggerFactory.getLogger("YourModId")

const val MOD_ID = "your_mod_id"

override fun onInitialize() {
    LOGGER.info("Hello Fabric world with Kotlin!")

    // アイテムとブロックの登録を呼び出す
    MyItems.registerItems()
    MyBlocks.registerBlocks() // このメソッド自体は今は空でも良い(初期化時に登録しているため)

    // アイテムグループへの追加も呼び出す
    MyItems.addItemsToItemGroups()
    MyBlocks.addBlockItemsToItemGroups()

    LOGGER.info("Items and blocks registered for ${MOD_ID}!")
}

}
“`

これで、ゲーム内に新しいカスタムアイテムとカスタムブロックが追加され、クリエイティブインベントリの指定したタブから利用できるようになります。

注意: アイテムやブロックをゲーム内で正しく表示するには、テクスチャやモデルなどのリソースパックが必要です。これらは src/main/resources/assets/your_mod_id/ ディレクトリ以下に配置します。
* テクスチャ: assets/your_mod_id/textures/item/my_custom_item.png, assets/your_mod_id/textures/block/my_custom_block.png
* モデル: assets/your_mod_id/models/item/my_custom_item.json, assets/your_mod_id/models/block/my_custom_block.json
* ブロックの状態定義: assets/your_mod_id/blockstates/my_custom_block.json

これらのリソースの作成方法については、Minecraftの公式WikiやFabric Wikiに詳しい情報があります。この記事ではコードによるMod開発に焦点を当てるため、リソースパックについては割愛します。しかし、Modを配布する際にはこれらのリソースも一緒にパッケージングする必要があります。

イベントハンドリング

MinecraftのMod開発では、ゲーム内で発生する様々なイベント(プレイヤーの行動、ブロックの破壊/設置、Mobのスポーン、Tick更新など)に反応して処理を実行することがよくあります。Fabric APIは、これらのイベントを購読(Subscribe)し、独自のロジックを挿入するための柔軟なイベントシステムを提供しています。

Fabricのイベントシステムは、インターフェースとイベントコールバックの仕組みに基づいています。特定のイベントインターフェースを実装したリスナーを登録することで、イベント発生時にそのリスナーのメソッドが呼び出されます。

Kotlinでは、ラムダ式や関数参照、あるいはオブジェクト式を使ってイベントリスナーを簡潔に記述できます。

イベントリスナーの登録例:サーバーTickイベント

サーバーのTick(ゲーム内時間単位の更新)ごとに処理を実行したい場合を考えます。Fabric APIには ServerTickEvents.END_SERVER_TICK というイベントがあります。これはサーバーが1 Tickの処理を完了した後に発生します。

YourModId.ktonInitialize() メソッド内でイベントリスナーを登録します。

“`kotlin
package com.example.your_mod_id

import com.example.your_mod_id.registry.MyBlocks
import com.example.your_mod_id.registry.MyItems
import net.fabricmc.api.ModInitializer
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents // サーバーTickイベントをインポート
import net.minecraft.server.MinecraftServer
import org.slf4j.LoggerFactory

object YourModId : ModInitializer {

private val LOGGER = LoggerFactory.getLogger("YourModId")

const val MOD_ID = "your_mod_id"

override fun onInitialize() {
    LOGGER.info("Hello Fabric world with Kotlin!")

    MyItems.registerItems()
    MyBlocks.registerBlocks()
    MyItems.addItemsToItemGroups()
    MyBlocks.addBlockItemsToItemGroups()

    // サーバーTickイベントリスナーを登録
    ServerTickEvents.END_SERVER_TICK.register { server: MinecraftServer ->
        // サーバーの1 Tick終わりに実行したい処理
        // 例: サーバーコンソールにメッセージを出力(頻繁すぎるとログが膨大になるので注意)
        // LOGGER.info("Server ticked!")

        // より実用的な例として、特定の条件を満たしたプレイヤーにエフェクトを与えるなど
        // server.playerManager.playerList.forEach { player ->
        //     // 各プレイヤーに対する処理
        // }
    }

    LOGGER.info("Items and blocks registered for ${MOD_ID}!")
}

}
“`

ServerTickEvents.END_SERVER_TICK.register { ... } の部分がイベントリスナーの登録です。register メソッドには、イベントが発生したときに呼び出されるラムダ式(または ServerTickEvents.EndTick インターフェースを実装したオブジェクト)を渡します。ラムダ式の引数 server: MinecraftServer は、イベントが発生したサーバーインスタンスです。

他の様々なイベントがFabric APIによって提供されています。例えば:
* PlayerBlockPlacementEvents.AFTER_PLACE: プレイヤーがブロックを設置した後に発生
* AttackEntityEvents.ALLOW_ATTACK: プレイヤーがエンティティを攻撃できるか判定
* UseBlockEvents.BY_PLAYER: プレイヤーがブロックを右クリックした際に発生
* CommandRegistrationCallback.EVENT: サーバーコマンドが登録される際に発生

これらのイベントは、net.fabricmc.fabric.api.event パッケージ以下の様々なサブパッケージに分類されています。必要なイベントはFabric APIのドキュメントで検索してください。

クライアントとサーバー

Minecraftはクライアント-サーバーモデルで動作します。Mod開発においても、クライアント側でしか実行されない処理(レンダリング、GUI、キー入力など)と、サーバー側でしか実行されない処理(ゲームロジック、ワールド状態、コマンド実行など)を区別する必要があります。

Fabricでは、fabric.mod.jsonentrypoints フィールドで clientserver のエントリポイントを指定することで、それぞれの環境専用のコードを実行できます。

クライアント専用コード

クライアント専用の処理を行いたい場合は、net.fabricmc.api.ClientModInitializer インターフェースを実装したクラスを作成し、fabric.mod.json"entrypoints": { "client": [...] } に指定します。

src/client/kotlin/com/example/your_mod_id/YourModIdClient.kt のようなファイルを作成します。

“`kotlin
package com.example.your_mod_id

import net.fabricmc.api.ClientModInitializer
import org.slf4j.LoggerFactory

// クライアント専用のMod初期化を行うシングルトンオブジェクト
object YourModIdClient : ClientModInitializer {

private val LOGGER = LoggerFactory.getLogger("YourModIdClient") // クライアント用ロガー

override fun onInitializeClient() {
    LOGGER.info("Hello Fabric client world with Kotlin!")

    // ここにクライアント専用の初期化処理を記述する
    // 例: レンダリング関連の登録、キーバインドの設定、クライアントイベントリスナーの登録
    // MyRendering.registerRenderers()
    // KeybindingHelper.registerKeybinding(...)
}

}
“`

そして、fabric.mod.json にクライアントエントリポイントを追加します。

json
"entrypoints": {
"main": [
"com.example.your_mod_id.YourModId"
],
"client": [ // クライアントエントリポイントを追加
"com.example.your_mod_id.YourModIdClient" // クライアント用クラスの完全修飾名
],
"server": []
},
"environment": "*" // または "client" にすることも可能だが、"*" が一般的

onInitializeClient() メソッド内では、以下のような処理を行うことが多いです。
* ブロックエンティティのレンダラー登録 (BlockEntityRendererFactories)
* エンティティのレンダラー登録 (EntityRendererRegistry)
* カスタム画面(GUI)の登録 (ScreenRegistry)
* モデルの読み込み設定 (ModelLoadingRegistry)
* キーバインドの設定
* クライアントサイドのイベントリスナー登録(例: ClientTickEvents, WorldRenderEvents

サーバー専用コード

サーバー専用の処理を行いたい場合は、net.fabricmc.api.DedicatedServerModInitializer インターフェースを実装したクラスを作成し、fabric.mod.json"entrypoints": { "server": [...] } に指定します。

src/server/kotlin/com/example/your_mod_id/YourModIdServer.kt のようなファイルを作成します。

“`kotlin
package com.example.your_mod_id

import net.fabricmc.api.DedicatedServerModInitializer // サーバー専用のModInitializer
import org.slf4j.LoggerFactory

// サーバー専用のMod初期化を行うシングルトンオブジェクト
object YourModIdServer : DedicatedServerModInitializer {

private val LOGGER = LoggerFactory.getLogger("YourModIdServer") // サーバー用ロガー

override fun onInitializeServer() {
    LOGGER.info("Hello Fabric server world with Kotlin!")

    // ここにサーバー専用の初期化処理を記述する
    // 例: コマンドの登録、サーバーイベントリスナーの登録
    // CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, environment -> ... }
}

}
“`

そして、fabric.mod.json にサーバーエントリポイントを追加します。

json
"entrypoints": {
"main": [
"com.example.your_mod_id.YourModId"
],
"client": [
"com.example.your_mod_id.YourModIdClient"
],
"server": [ // サーバーエントリポイントを追加
"com.example.your_mod_id.YourModIdServer" // サーバー用クラスの完全修飾名
]
},
"environment": "*"

onInitializeServer() メソッド内では、以下のような処理を行うことが多いです。
* サーバーコマンドの登録 (CommandRegistrationCallback)
* サーバーサイドのイベントリスナー登録(例: ServerLifecycleEvents
* データ生成(レシピ、戦利品テーブルなど)

多くのModでは、クライアントとサーバー共通の処理(アイテム/ブロック登録など)は main エントリポイントで行い、環境固有の処理だけをそれぞれの専用エントリポイントで行います。

Kotlinを活用したMod開発テクニック

Kotlinを使用することで、Javaではやや冗長になる記述をより簡潔に、より安全に書くことができます。Mod開発で役立つKotlinの機能を見ていきましょう。

Null安全

Minecraftのコードベースには、nullを返す可能性のあるメソッドが多く存在します。Javaでは NullPointerException が発生しやすい要因となりますが、KotlinではNull安全な型システムによって、コンパイル時にNullの可能性をチェックできます。

Kotlinでは、型名の末尾に ? をつけることで、その型の変数がnullを許容することを示します(Nullable Type)。? がついていない型はNon-nullable Typeと呼ばれ、nullを格納できません。

kotlin
var potentiallyNullString: String? = null // nullを許容
var nonNullString: String = "Hello" // nullは不可

Nullableな変数にアクセスする際には、そのままではコンパイルエラーになります。安全にアクセスするための演算子があります。

  • セーフコール演算子 (?.): オブジェクトがnullでなければメソッドを呼び出す/プロパティにアクセスします。nullの場合は式全体がnullを返します。
    kotlin
    val length = potentiallyNullString?.length // potentiallyNullStringがnullなら length は null
  • Elvis演算子 (?:): オブジェクトがnullの場合に代替値を指定します。
    kotlin
    val name = playerName ?: "Unknown" // playerNameがnullなら name は "Unknown"
  • 非Nullアサーション演算子 (!!): オブジェクトがnullでないことを開発者が保証する場合に使用します。もしnullだった場合は NullPointerException が発生します。極力使用は避けるべきです。
    kotlin
    val requiredValue = nullableValue!! // nullableValueがnullだとクラッシュ

MinecraftのAPIを使う際には、Nullableな型をKotlinで扱うことに慣れる必要があります。例えば、PlayerEntity.getEntityWorld()World? を返すため、ワールドインスタンスにアクセスする前にnullチェックやセーフコールを行う必要があります。

kotlin
player.world?.let { world ->
// world が null でない場合に実行されるブロック
if (!world.isClient) {
// サーバー側の処理
}
}

拡張関数 (Extension Functions)

Kotlinの拡張関数は、既存のクラスに新しいメソッドを追加する機能です。元のクラスのソースコードを変更することなく、あたかもそのクラスのメンバーであるかのようにメソッドを呼び出せます。これは、MinecraftやFabric APIの既存クラスに便利なメソッドを追加するのに非常に役立ちます。

例えば、Identifier をより簡単に作成するための拡張関数を定義できます。

“`kotlin
package com.example.your_mod_id.util

import com.example.your_mod_id.YourModId
import net.minecraft.util.Identifier

// Stringクラスに対する拡張関数を定義
fun String.asIdentifier(): Identifier {
// レシーバー(この関数が呼び出されるStringオブジェクト)をthisで参照
return Identifier(YourModId.MOD_ID, this)
}

// 使用例:
// val myItemIdentifier = “my_custom_item”.asIdentifier()
// これは Identifier(YourModId.MOD_ID, “my_custom_item”) と同じ意味
“`

この拡張関数を定義することで、Mod IDを毎回指定することなく Identifier を作成できます。登録コードなどがより簡潔になります。

“`kotlin
// MyItems.kt 内での使用例
object MyItems {
// … アイテム定義

fun registerItems() {
    // Identifier(YourModId.MOD_ID, "my_custom_item") の代わりに
    Registry.register(Registries.ITEM, "my_custom_item".asIdentifier(), MY_CUSTOM_ITEM)
}
// ...

}
“`

データクラス (Data Classes)

Kotlinのデータクラスは、データを保持するためのクラスを簡潔に定義できます。コンストラクタ、ゲッター/セッター、equals(), hashCode(), toString(), copy() などのメソッドが自動的に生成されます。Mod開発でカスタム設定や構造化されたデータを保持する場合に便利です。

“`kotlin
data class MyCustomSettings(
val enableFeatureA: Boolean = true,
val limitValue: Int = 100,
val message: String = “Default message”
)

// 使用例:
val settings = MyCustomSettings(enableFeatureA = false, limitValue = 50)
println(settings.message) // “Default message”
println(settings.copy(limitValue = 200)) // MyCustomSettings(enableFeatureA=false, limitValue=200, message=”Default message”)
“`

シングルトンオブジェクト (object)

既にメインModクラスの例で紹介しましたが、Kotlinの object キーワードはシングルトン(アプリケーション全体でインスタンスが一つだけ存在するクラス)を簡単に定義できます。Mod開発では、Modのメインクラス、レジストリ管理クラス、設定クラスなど、インスタンスが複数不要な要素に広く使われます。

kotlin
// MyItems.kt の例のように、object として定義するとインスタンス化せずに直接メンバーにアクセスできる
// MyItems.MY_CUSTOM_ITEM
// MyItems.registerItems()

高階関数とラムダ式

Kotlinでは関数を他の関数の引数として渡したり、関数から関数を返したりできます(高階関数)。ラムダ式は無名関数を記述するための簡潔な構文です。

これはイベントハンドリングで特に強力です。Fabric APIのイベント登録メソッドは、リスナーインターフェースのインスタンスを引数に取りますが、そのインターフェースがSAM (Single Abstract Method) インターフェースである場合、ラムダ式で記述できます。

“`kotlin
// Javaの場合(匿名クラス)
// ServerTickEvents.END_SERVER_TICK.register(new ServerTickEvents.EndTick() {
// @Override
// public void onEndTick(MinecraftServer server) {
// // …
// }
// });

// Kotlinの場合(ラムダ式)
ServerTickEvents.END_SERVER_TICK.register { server: MinecraftServer ->
// …
}
“`

これにより、イベントリスナーの実装が非常に簡潔になります。

高度なトピック(概要)

Mixins

Mixinsは、既存のJavaクラスのバイトコードをコンパイル後に書き換えるための強力なメカニズムです。これにより、Minecraft本体や他のModのクラスのプライベートフィールドにアクセスしたり、メソッドの処理をフックしたり、完全に置き換えたりすることが可能になります。

Mixinは非常に低レベルな操作であり、適切に使用しないと他のModとの競合を引き起こしたり、ゲームのアップデートで簡単に壊れたりする可能性があります。Fabric Mod開発では広く使われていますが、初心者には難易度が高いトピックです。

Mixinを使用するには、Mixinの依存関係をGradleに追加し、fabric.mod.json にMixinの設定ファイル(通常 your_mod_id.mixins.json)を指定し、対象のクラスに対してMixinクラスを作成する必要があります。

Mixinの学習には、Fabric WikiのMixinに関する詳細なドキュメントを参照することを強く推奨します。

データ生成 (Data Generation)

レシピ、戦利品テーブル、タグ、構造物などのゲームデータは、JSONファイルとして定義されます。手動でJSONファイルを記述することも可能ですが、Modの要素が増えると管理が大変になります。

Fabric APIは、これらのJSONデータをJava/Kotlinコードで生成するためのData Generation APIを提供しています。これにより、コードでブロックやアイテムを参照しながらデータを生成できるため、一貫性が保たれ、エラーを防ぎやすくなります。

Data Generationは、fabric.mod.jsondata エントリポイントを使用します。

ネットワーク通信 (Networking)

クライアントとサーバー間でカスタムデータを送受信したい場合、Fabric APIのネットワーク通信APIを使用します。カスタムパケット(Message)を定義し、特定の Identifier (チャンネル)を使って送受信します。

サーバーからクライアントへデータを送る場合 (ServerPlayNetworking) や、クライアントからサーバーへデータを送る場合 (ClientPlayNetworking) など、様々なシナリオに対応したAPIが提供されています。

例えば、カスタムアイテム使用時にクライアント側で特殊なパーティクルを表示し、サーバー側で特定の効果を適用するなど、クライアントとサーバーの連携が必要な機能を実現するために使われます。

テストとデバッグ

Mod開発において、問題なく動作するか確認し、バグが発生した場合に原因を特定・修正するプロセスは非常に重要です。

Modの実行

環境構築のセクションで説明した通り、IntelliJ IDEAのGradleツールウィンドウから runClient または runServer タスクを実行することで、開発中のModが適用されたMinecraftを起動できます。

runClient: 統合版サーバーとクライアントの両方が同じJVMプロセスで起動します。シングルプレイヤーワールドのテストに便利です。
runServer: 専用サーバーが別のJVMプロセスで起動します。マルチプレイヤー環境やサーバー専用機能のテストに便利です。

デバッグ

IntelliJ IDEAでは、これらの実行設定に対してデバッグモードで起動できます。実行ボタンの隣にある虫のアイコンをクリックするか、Run -> Debug 'Minecraft Client' を選択します。

デバッグモードで起動すると、コードに設定したブレークポイント(コードの実行を一時停止させたい行)でプログラムの実行が止まります。これにより、その時点での変数の値を確認したり、コードの実行フローをステップ実行したりできます。

ブレークポイントを設定するには、コードエディタの行番号の左側のガターをクリックします。赤い丸が表示されればブレークポイントが設定されています。

ロギング

org.slf4j.Logger を使用して、Modの動作状況をコンソールに出力することは、デバッグにおいて非常に役立ちます。

“`kotlin
private val LOGGER = LoggerFactory.getLogger(YourModId.MOD_ID)

fun someFunction() {
LOGGER.info(“Entering someFunction…”)
// … 処理 …
LOGGER.warn(“Something might be wrong here.”)
// … 処理 …
LOGGER.error(“An error occurred!”, exception) // 例外と一緒に出力
LOGGER.debug(“Debug information: value = $someVariable”) // デバッグ時のみ表示されるログ
}
“`

LOGGER.info() は通常の情報、LOGGER.warn() は警告、LOGGER.error() はエラー、LOGGER.debug() は詳細なデバッグ情報を出力します。デフォルトではDEBUG以下のログは表示されないため、詳細な情報を確認したい場合はGradleの設定などでログレベルを変更する必要があります。

ゲーム起動時のコンソール出力やログファイル (logs/latest.log など) を確認することで、Modの初期化エラーや実行時の例外などを特定できます。

ビルドと配布

開発したModを他の人に使ってもらうためには、配布可能なJARファイルを作成する必要があります。

JARファイルのビルド

Gradleの build タスクを実行することで、ModのJARファイルが生成されます。

IntelliJ IDEAのGradleツールウィンドウで Tasks -> build -> build をダブルクリックします。または、ターミナルで以下のコマンドを実行します。

“`bash

Linux/macOS

./gradlew build

Windows

./gradlew.bat build
“`

ビルドが成功すると、プロジェクトルートの build/libs ディレクトリ内にJARファイルが生成されます。通常、ファイル名は your_mod_id-version.jar のようになります。Fabric開発で生成されるJARには、Modのコードだけでなく、fabric.mod.json やリソースパック (assets フォルダの内容) も含まれます。

JARファイルの種類

ビルドディレクトリ (build/libs) には、通常いくつかのJARファイルが生成されます。

  • your_mod_id-version.jar: これが配布用のMod JARです。依存関係は含まれません(通常、Fabric APIなどは別途ユーザーにインストールしてもらいます)。
  • your_mod_id-version-dev.jar: 開発環境で依存関係を含めて実行するためのJARです。配布用ではありません。
  • your_mod_id-version-sources.jar: ソースコードが含まれたJARです。これは配布には必須ではありませんが、他の開発者がModのコードを参照する際に役立ちます。

配布するのは -dev-sources がついていない、バージョン番号のみを含むJARファイル (your_mod_id-version.jar) です。

配布

ビルドされたJARファイルをModrinthやCurseForgeといったMod配布プラットフォームにアップロードすることで、他のプレイヤーがModをダウンロードして利用できるようになります。

ユーザーは、適切なバージョンのMinecraft、Fabric Loader、そして(通常は)Fabric APIをインストールした環境に、このMod JARファイルを.minecraft/mods フォルダに入れることで、Modを導入できます。

トラブルシューティング

Mod開発中には様々な問題に遭遇する可能性があります。一般的な問題とその解決策をいくつか紹介します。

  • Gradle同期エラー:
    • インターネット接続を確認します。
    • Gradleキャッシュをクリアしてみます (./gradlew cleanCache または ~/.gradle/caches を削除)。
    • JDKのバージョンが正しいか確認します。
    • build.gradle / build.gradle.kts ファイルに構文エラーがないか確認します。
    • IntelliJ IDEAを再起動したり、Gradleプロジェクトを再インポートしてみます。
  • Minecraft起動時のクラッシュ:
    • ゲームのクラッシュレポート (.minecraft/crash-reports/) やログファイル (.minecraft/logs/latest.log) を確認します。エラーメッセージやスタックトレースに原因のヒントがあります。
    • ModのコードにNullPointerExceptionなどの実行時エラーがないかデバッグします。
    • fabric.mod.json の設定(特にエントリポイントや依存関係)が正しいか確認します。
    • 使用しているFabric LoaderやFabric API、ModのバージョンがMinecraftのバージョンと互換性があるか確認します。
    • 他のModとの競合がないか、単体でModを実行して確認します。
  • Modの要素がゲームに表示されない(アイテム、ブロックなど):
    • onInitialize() メソッドで登録処理が正しく呼び出されているか確認します。
    • Registry.registerIdentifier がユニークで、fabric.mod.json のMod IDと一致しているか確認します。
    • リソースファイル(テクスチャ、モデルなど)が src/main/resources/assets/your_mod_id/ ディレクトリ以下に正しいパスと名前で配置されているか確認します。
    • リソースファイルの内容にJSON構文エラーなどがないか確認します。
  • Kotlinコードのコンパイルエラー:
    • IntelliJ IDEAのエディタに表示されるエラーメッセージを確認し、修正します。
    • Kotlinプラグインが正しくインストール・有効化されているか確認します。
    • GradleのKotlinプラグイン設定(バージョンなど)が正しいか確認します。
    • src/main/kotlin ディレクトリがソースルートとしてマークされているか確認します。

困ったときは、FabricMCのDiscordサーバーや関連するコミュニティで質問するのも良い方法です。ログファイルやクラッシュレポートを添付すると、他の人が問題を特定しやすくなります。

さらなる学習リソース

この記事はFabric ModをKotlinで開発するための導入ガイドですが、Mod開発の世界は広範です。さらに学習を進めるためのリソースを紹介します。

  • FabricMC Wiki: Fabric Loader、Fabric API、Mixinなどに関する公式ドキュメントです。非常に詳細で信頼性が高い情報源ですが、英語です。
  • Fabric API Documentation: Fabric APIが提供する様々なモジュールやイベントに関するJavadocです。
  • Kotlin Official Documentation: Kotlin言語自体の詳細なリファレンスです。
  • Gradle Documentation: ビルドツールであるGradleに関するドキュメントです。
  • FabricMC Discord Server: 他のFabric Mod開発者と交流したり、質問したりできるコミュニティです。

これらのリソースを活用しながら、様々なModのソースコードを読んだり、小さな機能から実装を試したりすることで、着実にスキルを向上させることができます。

まとめ

この記事では、Fabric ModをKotlinで開発するための環境構築から、Modの基本構造、アイテム・ブロックの登録、イベントハンドリング、クライアント/サーバーの区別、Kotlinの活用方法、ビルドと配布、トラブルシューティングまでを詳細に解説しました。

FabricとKotlinの組み合わせは、現代的な言語の利点を活かしつつ、軽量で柔軟なMod開発を可能にします。Kotlinの簡潔さ、安全性、Javaとの相互運用性は、Minecraft Mod開発の生産性と保守性を大きく向上させます。

Mod開発は奥深く、学ぶべきことはたくさんありますが、ここで紹介した基本的なステップを踏まえれば、あなた独自のMinecraft Modを作り始めることができるでしょう。ぜひ、アイデアを形にする楽しさを体験してください。

開発の過程で問題に直面することも多いと思いますが、公式ドキュメントやコミュニティの助けを借りながら、一つずつ解決していくことが重要です。

この記事が、あなたのFabric + Kotlin Mod開発の旅の良いスタート地点となることを願っています。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール