Guide
Kotlin e Java
Per le mod grandi. La stessa API di CraftScript, con tipi statici, l’IDE completo e qualsiasi libreria Java, più alcune parti che per ora esistono solo qui. Questa guida usa Kotlin; Java funziona allo stesso modo ed è mostrato alla fine.
Cosa puoi fare
| Ambito | Nell’API | In CraftScript |
|---|---|---|
| Oggetti, blocchi, ricette | ContentBuilder: item, block, shaped, shapeless, smelting |
Sì |
| Creature | ContentBuilder.creature: corpo, texture, statistiche, Behaviour, TargetRule, bottino, comparsa naturale, addomesticamento, allevamento, cavalcata, comportamenti propri |
Sì |
| Suoni e particelle | ContentBuilder.sound, ContentBuilder.particle |
Sì |
| Generazione del mondo | ContentBuilder: ore, tree, patch, structure (pool, plan/build), biome, dimension, feature, terrain, biomes, carver, tag. Vedi Generazione del mondo e Generazione tramite codice |
Tutto tranne feature, tag, salt e aboveBottom |
| Interfaccia | Form e player.showForm; server.bossBar; player.setSidebar |
Sì |
| Eventi | Crafting.events.on<E> { }, con priorità e annullamento |
Sì |
| Effetti, attributi, equipaggiamento | addEffect, attribute, setAttribute, equipment, setEquipment, EquipmentSlot |
Yes |
| Tasti | ContentBuilder.key, PlayerKeyEvent |
Yes |
| Comandi | Crafting.commands.command(…): sottocomandi, argomenti tipizzati, permessi, alias |
Sì |
| Mondo e giocatori | World, Block, Entity, Player, Inventory, Server |
Sì |
| Attività | Crafting.scheduler: runLater, runRepeating, runAsync, runSync |
Senza lavoro in background |
| Impostazioni su file | Config tipizzata in TOML, sincronizzabile con i client |
Sì (config) |
| Rete | Crafting.network.channel(…), con codec personalizzati |
Sì (listen, send) |
| Dati salvati | player.data["rubies.visits"] = n, data.getInt(…); anche su entità, blocchi e mondi |
Sì |
| Librerie Java | Qualsiasi | No |
Ciò che l’API non ha ancora non esiste in nessun linguaggio. Non aggirarlo chiamando classi di Minecraft: la mod perderebbe la garanzia di continuare a funzionare quando il gioco cambia.
Crea il progetto
crafting new rubies --kind kotlin
cd rubies && gradle buildL’API non è ancora in un repository Maven pubblico. Installala nel tuo Maven locale dal codice sorgente di Crafting e dichiarala come compileOnly: è Crafting a fornirla alla mod in esecuzione.
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 funziona con Java 25crafting new genera questo file e un progetto già diviso per responsabilità: <Name>Mod.kt, che si limita a collegare le parti, content/ModContent.kt, events/Welcome.kt e commands/GemCommand.kt.
Struttura
Package per tema, una classe di ingresso che si limita a collegare le parti e un file per parte. La guida Organizzare una mod spiega perché; ecco la mod di esempio completa:
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/
La classe di ingresso non fa nulla da sola: crea ciò che è condiviso e chiama ogni parte.
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()
}
}Il manifest
{
"id": "rubies",
"name": "Rubies",
"version": "1.0.0",
"kind": "mod",
"side": "both",
"entrypoints": ["com.example.rubies.RubiesMod"],
"depends": { "crafting": ">=0.1.0" }
}| Campo | Cos’è |
|---|---|
id |
2–64 caratteri: lettere minuscole, cifre, _ o -, che inizia con una lettera. È anche il namespace dei tuoi oggetti e blocchi. |
kind |
mod o plugin (solo server, ricaricabile; vedi sotto). |
side |
both, client o server. Una mod client viene ignorata senza avvisi su un server dedicato. |
entrypoints |
Le classi che implementano CraftingMod. |
depends, optional, incompatible |
Id → intervallo di versioni: >=1.2 <2, ^1.4, ~1.4.2, 1.20.x, [1.0,2.0). Se qualcosa non va, Crafting segnala tutti i problemi insieme, con un suggerimento. |
loadAfter, loadBefore |
Ordine di caricamento senza una dipendenza obbligatoria. |
Ciclo di vita
interface CraftingMod {
fun onLoad(ctx: ModContext)
fun onReady(ctx: ModContext) {}
fun onShutdown(ctx: ModContext) {}
}onLoad: registra contenuti, eventi e comandi.- Crafting crea i blocchi e gli oggetti nel gioco. Da questo momento, registrare contenuti lancia
RegistryFrozenException. onReady: tutte le mod sono caricate;Crafting.runtime.isModLoaded("other_mod")ti dice se un’altra mod è presente.onShutdown: all’uscita.
Plugin per server
Un plugin è una CraftingMod con "kind": "plugin" e "side": "server"; crafting new chatguard --kind plugin crea la classe di ingresso, events/ChatFilter.kt e commands/HelloCommand.kt. Non aggiunge blocchi né oggetti, quindi i giocatori con Minecraft senza mod possono entrare in un server che ha solo plugin. Lo attivi, lo disattivi e lo ricarichi senza riavviare: onLoad e onReady vengono eseguiti quando viene attivato, onShutdown quando viene disattivato o ricaricato.
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 è attivo") }
}
}
override fun onShutdown(ctx: ModContext) = ctx.logger.info("ChatGuard disabled")
}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("&cResta gentile, per favore")
}
}
}
}Tutto ciò che un plugin registra è legato a esso. /crafting reload chatguard chiama onShutdown, rimuove i suoi listener, comandi e attività, rilegge il jar e chiama onLoad. In modalità sviluppo succede automaticamente quando sostituisci il jar.
| Mod | Plugin | |
|---|---|---|
| Cartella | mods/ |
plugins/ |
| Lato | Client e/o server | Solo server |
| Nuovi blocchi e oggetti | Sì | No |
| Ricarica senza riavvio | No | Sì |
| Possono entrare client senza mod | No, se aggiunge contenuti | Sì |
Java
Stessa API, stessa struttura. Da Java, il punto di ingresso è Crafting.INSTANCE, passi ogni parametro che ha un valore predefinito e un listener restituisce 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;
});
}
}Modi vecchi
Funzionano ancora, ma sono segnati come deprecati e hanno un sostituto:
| Prima | Ora |
|---|---|
class X : CraftingPlugin() con onEnable/onDisable |
CraftingMod con "kind": "plugin" |
Metodi con @Subscribe e events.register(obj) |
events.on<E> { } (Java: events.subscribe(E.class, …)) |
@Mod("id") sulla classe |
La classe negli entrypoints del manifest |
EntityDamageEvent.source |
damageType (un id, come minecraft:fall) |
world.setBlockState(x, y, z, "…[open=true]") |
world.setBlock(x, y, z, type, mapOf("open" to true)) |
Regole
- Non importare nulla da
net.minecraft. È questo che permette alla mod di continuare a funzionare quando il gioco cambia. - Registra i contenuti solo in
onLoad. - Un’attività
runAsyncnon tocca il mondo; torna al thread del gioco conrunSync.