ガイド
Kotlin と Java
大規模な Mod 向けです。CraftScript と同じ API を、静的型、IDE のフル機能、あらゆる Java ライブラリとともに使えます。今のところここにしかない部分もいくつかあります。このガイドでは Kotlin を使いますが、Java も同じように動き、最後に紹介します。
できること
| 分野 | API では | CraftScript では |
|---|---|---|
| アイテム、ブロック、レシピ | ContentBuilder:item、block、shaped、shapeless、smelting |
あり |
| モブ | ContentBuilder.creature:体、テクスチャ、ステータス、Behaviour、TargetRule、ドロップ、自然スポーン、手なずけ、繁殖、騎乗、独自の行動 |
あり |
| サウンドとパーティクル | ContentBuilder.sound、ContentBuilder.particle |
あり |
| ワールド生成 | ContentBuilder:ore、tree、patch、structure(プール、plan/build)、biome、dimension、feature、terrain、biomes、carver、tag。ワールド生成とコードによる生成を参照 |
feature、tag、salt、aboveBottom 以外すべて |
| インターフェース | Form と player.showForm、server.bossBar、player.setSidebar |
あり |
| イベント | Crafting.events.on<E> { }。優先度とキャンセル付き |
あり |
| エフェクト、属性、装備 | addEffect, attribute, setAttribute, equipment, setEquipment, EquipmentSlot |
Yes |
| キー | ContentBuilder.key, PlayerKeyEvent |
Yes |
| コマンド | Crafting.commands.command(…):サブコマンド、型付きの引数、権限、エイリアス |
あり |
| ワールドとプレイヤー | World、Block、Entity、Player、Inventory、Server |
あり |
| タスク | Crafting.scheduler:runLater、runRepeating、runAsync、runSync |
バックグラウンド処理なし |
| ファイルの設定 | TOML の型付き Config。クライアントと同期できます |
あり(config) |
| ネットワーク | Crafting.network.channel(…)。独自のコーデック付き |
あり(listen、send) |
| セーブデータ | player.data["rubies.visits"] = n、data.getInt(…)。エンティティ、ブロック、ワールドにも |
あり |
| Java ライブラリ | 好きなものを | なし |
API にまだないものは、どの言語にもありません。Minecraft のクラスを呼び出して回避しないでください。ゲームが変わっても動き続けるという保証を Mod が失ってしまいます。
プロジェクトを作る
crafting new rubies --kind kotlin
cd rubies && gradle buildAPI はまだ公開の Maven リポジトリにありません。Crafting のソースからローカルの Maven にインストールし、compileOnly として宣言してください。実行時には Crafting が Mod に提供します。
cd crafting && ./gradlew :crafting-api:publishToMavenLocalplugins { kotlin("jvm") version "2.4.21" }
repositories { mavenCentral(); mavenLocal() }
dependencies {
compileOnly("dev.crafting:crafting-api:0.1.0")
}
kotlin { jvmToolchain(25) } // Minecraft 26.x は Java 25 で動作するcrafting new はこのファイルと、最初から責務ごとに分かれたプロジェクトを生成します。各部分をつなぐだけの <Name>Mod.kt、content/ModContent.kt、events/Welcome.kt、commands/GemCommand.kt です。
構成
テーマごとのパッケージ、各部分をつなぐだけのエントリークラス、そして部分ごとに 1 つのファイル。理由は Mod の整理 ガイドで説明しています。これが完全なサンプル Mod です。
rubies/
├── build.gradle.kts
├── settings.gradle.kts
└── src/main/
├── kotlin/com/example/rubies/
│ ├── RubiesMod.kt entry point: only wires the parts
│ ├── RubiesConfig.kt player settings
│ ├── content/
│ │ ├── Items.kt
│ │ ├── Blocks.kt
│ │ ├── Creatures.kt
│ │ ├── Sounds.kt
│ │ ├── Particles.kt
│ │ └── Recipes.kt
│ ├── storage/
│ │ └── Visits.kt saved data: the keys, in one place
│ ├── events/
│ │ ├── MiningEvents.kt
│ │ ├── CombatEvents.kt
│ │ └── WelcomeEvents.kt
│ ├── ui/
│ │ └── ShopForm.kt forms: what the player sees
│ └── commands/
│ ├── RubyCommand.kt
│ └── ShopCommand.kt
└── resources/
├── crafting.mod.json
└── assets/rubies/
├── sounds/ruby/chime.ogg
└── textures/
├── item/ruby.png
├── entity/ruby_golem.png
└── particle/ruby_spark_0.png, ruby_spark_1.pngsrc/main/kotlin/com/example/rubies/
エントリークラス自体は何もしません。共有するものを作り、各部分を呼び出します。
package com.example.rubies
class RubiesMod : CraftingMod {
override fun onLoad(ctx: ModContext) {
val config = Crafting.configs.load(RubiesConfig())
val content = ContentBuilder(ctx.metadata.id, Crafting.registries)
Items.register(content)
Blocks.register(content)
Creatures.register(content)
Sounds.register(content)
Particles.register(content)
Recipes.register(content)
MiningEvents.register(config)
CombatEvents.register(config)
WelcomeEvents.register()
RubyCommand.register()
ShopCommand.register()
}
}マニフェスト
{
"id": "rubies",
"name": "Rubies",
"version": "1.0.0",
"kind": "mod",
"side": "both",
"entrypoints": ["com.example.rubies.RubiesMod"],
"depends": { "crafting": ">=0.1.0" }
}| フィールド | 内容 |
|---|---|
id |
2–64 文字:小文字、数字、_ または - で、英字で始まります。アイテムとブロックの名前空間にもなります。 |
kind |
mod または plugin(サーバー専用、再読み込み可能。下記参照)。 |
side |
both、client、server。クライアントの Mod は、マルチプレイ用サーバーでは何も言わずに無視されます。 |
entrypoints |
CraftingMod を実装するクラス。 |
depends、optional、incompatible |
id → バージョン範囲:>=1.2 <2、^1.4、~1.4.2、1.20.x、[1.0,2.0)。何かが失敗すると、Crafting はすべての問題をまとめて、対処法とともに報告します。 |
loadAfter、loadBefore |
厳密な依存関係なしの読み込み順。 |
ライフサイクル
interface CraftingMod {
fun onLoad(ctx: ModContext)
fun onReady(ctx: ModContext) {}
fun onShutdown(ctx: ModContext) {}
}onLoad:コンテンツ、イベント、コマンドを登録します。- Crafting がゲーム内にブロックとアイテムを作成します。これ以降、コンテンツを登録すると
RegistryFrozenExceptionが投げられます。 onReady:すべての Mod が読み込まれています。Crafting.runtime.isModLoaded("other_mod")で別の Mod があるかどうかがわかります。onShutdown:終了時。
サーバープラグイン
プラグインは "kind": "plugin" と "side": "server" を持つ CraftingMod です。crafting new chatguard --kind plugin で、エントリークラス、events/ChatFilter.kt、commands/HelloCommand.kt が作成されます。ブロックやアイテムを追加しないので、プラグインしかないサーバーには Mod なしの Minecraft のプレイヤーも参加できます。再起動せずに有効化、無効化、再読み込みができます。有効化時に onLoad と onReady が、無効化や再読み込みのときに onShutdown が実行されます。
crafting new chatguard --kind pluginpackage com.example.chatguard
class ChatGuardPlugin : CraftingMod {
override fun onLoad(ctx: ModContext) {
ChatFilter.register()
Crafting.commands.command("guard") {
permission = "chatguard.admin"
executes { it.reply("ChatGuard は有効です") }
}
}
override fun onShutdown(ctx: ModContext) = ctx.logger.info("ChatGuard を無効にしました")
}package com.example.chatguard.events
object ChatFilter {
private val blocked = listOf("spam", "scam")
fun register() {
Crafting.events.on<PlayerChatEvent> { e ->
if (blocked.any { it in e.message.lowercase() }) {
e.cancel()
e.player.send("&c仲良くしましょう")
}
}
}
}プラグインが登録したものはすべて、そのプラグインに紐づきます。/crafting reload chatguard は onShutdown を呼び、リスナー、コマンド、タスクを取り除き、jar を読み直して onLoad を呼びます。開発モードでは、jar を置き換えると自動でこれが行われます。
| Mod | プラグイン | |
|---|---|---|
| フォルダー | mods/ |
plugins/ |
| 動作する側 | クライアントとサーバーのどちらか、または両方 | サーバーのみ |
| 新しいブロックとアイテム | 可 | 不可 |
| 再起動なしの再読み込み | 不可 | 可 |
| Mod なしのクライアントの参加 | コンテンツを追加する場合は不可 | 可 |
Java
同じ API、同じ構成です。Java からは、エントリーポイントは Crafting.INSTANCE で、デフォルト値のある引数もすべて渡し、リスナーは Unit.INSTANCE を返します。
package com.example.rubies;
public final class RubiesMod implements CraftingMod {
@Override
public void onLoad(ModContext ctx) {
ContentBuilder content = new ContentBuilder(ctx.getMetadata().getId(), Crafting.INSTANCE.getRegistries());
Items.register(content);
WelcomeEvents.register();
}
}package com.example.rubies.events;
public final class WelcomeEvents {
public static void register() {
Crafting.INSTANCE.getEvents().subscribe(PlayerJoinEvent.class, EventPriority.NORMAL, false, null, e -> {
e.getPlayer().send("&aWelcome, " + e.getPlayer().getName() + "!");
return Unit.INSTANCE;
});
}
}古い書き方
今も動きますが、非推奨としてマークされており、代わりの方法があります。
| 以前 | 現在 |
|---|---|
onEnable/onDisable を持つ class X : CraftingPlugin() |
"kind": "plugin" を持つ CraftingMod |
@Subscribe を付けたメソッドと events.register(obj) |
events.on<E> { }(Java:events.subscribe(E.class, …)) |
クラスに付けた @Mod("id") |
マニフェストの entrypoints に書いたクラス |
EntityDamageEvent.source |
damageType(minecraft:fall のような id) |
world.setBlockState(x, y, z, "…[open=true]") |
world.setBlock(x, y, z, type, mapOf("open" to true)) |
ルール
net.minecraftからは何もインポートしないでください。それが、ゲームが変わっても Mod が動き続けられる理由です。- コンテンツは
onLoadでのみ登録します。 runAsyncのタスクはワールドに触れません。ゲームのスレッドにはrunSyncで戻ります。