Mga gabay
Pag-aayos ng mod
Isang beses mo lang isusulat ang isang mod pero aalagaan mo ito nang maraming taon. Ang pinakanakakatulong sa pag-aalaga ay hindi ang wika: kung saan nakalagay ang lahat. Nagmumungkahi ang gabay na ito ng isang istruktura para sa CraftScript at isa para sa Kotlin, at ipinapaliwanag kung bakit.
Apat na patakaran
- Isang responsibilidad bawat file. Isang file para sa mga ore, isa pa para sa mga recipe, isa pa para sa bawat command. Kung kailangan mo ang salitang “at” para ilarawan ang isang file, malamang na dalawang file iyon.
- Igrupo ayon sa paksa, hindi sa laki. Mga folder gaya ng
content/,events/,commands/,ui/atlib/. Alam ng sinumang magbukas ng mod kung saan titingin nang hindi binabasa ang code. - Ihiwalay ang data sa behaviour. Ang meron (mga item, block, recipe) ay listahan ng mga property. Ang nangyayari (mga event, command) ay code. Kapag pinaghalo mo sila, mapipilitan kang magbasa ng logic para lang magpalit ng isang numero.
- Entry point na nagkakabit lang ng mga bahagi. Sinasabi ng
main.crafto ng class ng mod kung anong mga bahagi ang meron. Wala silang laman na patakaran ng laro.
Sa ganito, ang pag-rebalance ng mod ay pag-edit ng content/recipes.craft; ang pag-ayos ng command ay pagbukas ng commands/; at hindi nakatago ang bug sa isang event sa gitna ng dalawandaang linya ng mga block.
Sa CraftScript
mods/rubies/
├── main.craft the mod and the list of its parts
├── settings.craft settings for the server owner
├── content/ data: what exists
│ ├── items.craft
│ ├── ores.craft
│ ├── world.craft
│ ├── dimensions.craft
│ ├── creatures.craft
│ ├── sounds.craft
│ ├── particles.craft
│ └── recipes.craft
├── events/ behaviour: what happens
│ ├── mining.craft
│ ├── hunting.craft
│ └── welcome.craft
├── ui/ forms: what the player sees
│ └── shop.craft
├── commands/
│ ├── realm.craft
│ ├── rubies.craft
│ └── shop.craft
├── lib/ shared logic
│ ├── rewards.craft
│ └── storage.craft saved data: the keys, in one place
├── sounds/
│ └── ruby/chime.ogg
├── structures/
│ └── ruby_shrine.nbt
└── textures/
├── item/ruby.png
├── block/ruby_ore.png, ruby_log.png, ruby_leaves.png, ruby_flower.png
├── entity/ruby_golem.png
└── particle/ruby_spark_0.png, ruby_spark_1.pngmods/rubies/
Idinedeklara lang nito ang mod at inililista ang mga bahagi nito. Sa pagbasa nito, malalaman mo kung ano ang meron at nasaan.
mod "rubies" { name: "Rubies", version: "1.0.0", author: "you" }
// What exists
import "content/items"
import "content/ores"
import "content/world"
import "content/dimensions"
import "content/creatures"
import "content/sounds"
import "content/particles"
import "content/recipes"
// What happens
import "events/mining"
import "events/hunting"
import "events/welcome"
import "commands/realm"
import "commands/rubies"
import "commands/shop"Paano gumagana ang mga import
- Pinapatakbo nang isang beses ng
import "content/items"ang file na iyon. Nagiging bahagi ng mod ang mga item, block, event at command na idinedeklara nito, na para bang nasamain.craftang mga ito. - Nagdadala ng mga partikular na pangalan ang
import { give_rubies } from "/lib/rewards". Binabasa mula sa folder ng mod ang path na nagsisimula sa/, kaya pareho itong gumagana mula sa anumang subfolder; kung walang/, relative ito sa file na nag-i-import. Opsyonal ang extension na.craft. - Dinadala ng
import "lib/rewards" as rewardsang buong module sa ilalim ng isang pangalan (rewards.give_rubies(p, 1)). Kung walangas, ang pangalan ay ang pangalan ng file, kaya nag-o-overwrite sa isa't isa ang dalawang na-import na file na may parehong pangalan: gumamit ng magkaibang pangalan o ngas. - Walang path na puwedeng lumabas sa folder ng mod, at nagbibigay ng error ang circular import na nagsasabi kung saan.
Shared na logic
Nasa lib/ ang function na ginagamit ng dalawang file. Kung may sariling state ang logic (isang ekonomiya, isang pila ng quest), gumamit ng class kasama ang mga method nito, nasa lib/ din; tingnan ang CraftScript. Ang variable na isang file lang ang gumagamit ay nananatili sa file na iyon, pero tandaan na nawawala ito kapag nag-restart: anumang kailangang tumagal ay nasa saved data.
Settings at saved data sa sarili nilang file
Dalawang bagay na ginagamit ng natitirang bahagi ng mod pero hindi dapat alam ang loob:
- Mga setting (
settings.craft, sa root): iisangconfig(…)kasama ang lahat ng default value. Ini-import ng ibang file angSETTINGSat binabasa ang kailangan nila; walang naglalagay nang direkta ng mga numero ng balance sa code. - Naka-save na data (
lib/storage.craft): ang mga key at ilang maliliit na function, gaya ngvisits(p)atadd_visit(p). Tinatawag ng code na nagbibilang ng mga visit angadd_visit(player)at hindi nito alam na may key narubies.visits. Kung magbago ito balang araw, sa iisang lugar mo lang ito babaguhin.
Huwag pangalanang config.craft ang file na iyon: kapag na-import nang walang as, matatabunan ng pangalan nito ang function na config.
Sa Kotlin
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/
Walang ginagawa nang mag-isa ang entry class: ginagawa nito ang mga shared at tinatawag ang bawat bahagi.
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()
}
}Inalis ang mga linyang import ng bawat file para mas madaling basahin.
- Mga package ayon sa paksa:
content,events,commands. Isangobjectbawat file na may function naregister. - Nagkakabit lang ng mga bahagi ang class ng mod: ginagawa nito ang shared ng ilang bahagi (ang configuration, ang
ContentBuilder) at ipinapasa ito sa kanila. Walang bahaging kumukuha ng anuman nang mag-isa, kaya maiintindihan mo ang bawat isa sa pagbasa lang ng file nito. - Mga id bilang constant sa object na nagre-register sa kanila (
Items.RUBY). Nagiging compile error ang typo. - Saved data sa
storage/: bawat key sa isang object na may mga function gaya ngVisits.of(player)atVisits.add(player). Nasa class naConfigang settings, na ipinapasa sa sinumang nangangailangan.
Sa Java
Pareho ang istruktura, pero class sa halip na object. Ginagamit mo ang API sa pamamagitan ng Crafting.INSTANCE, at nagre-return ng Unit.INSTANCE ang mga listener:
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;
});
}
}Isang kumpletong halimbawa: Frostlands
Ang Frostlands ay iisang CraftScript mod na nakaayos ayon sa bahagi, at ginagamit nito ang karamihan sa inilalarawan ng gabay na ito. Ang main.craft nito ay naglilista lang ng mga bahagi:
mod "frostlands" { name: "Frostlands", version: "1.0.0" }
// One file per part of the mod. Each part only declares what it is about.
import "content/materials" // frost crystals: their ore, the recipes made from them
import "content/gear" // armor, blade, wand, the frozen effect and the frost bolt
import "content/blocks" // the gate, a lamp, an ice chest, a frost pad
import "world/frostlands" // the frozen dimension
import "creatures/drake" // tame, ride and fly a frost drake
import "creatures/wisp" // wisps that shoot frost bolts at the undead
import "creatures/yak" // yaks to breed
import "controls/keys" // dash and roar keys
import "events/welcome" // a first greeting
import "commands/kit" // /frostkit for trying it all outcontent/materials: isang kristal, ang ore nito at ang mga resipe mula rito.content/gear: armor, isang talim na nagyeyelo, isang wand na bumabaril ng projectile ng mod na may cooldown, at isang effect ng mod.content/blocks: isang portal gate, isang lampara na sinisindihan sa click o ng redstone, isang ice chest at isang jump pad.world/frostlands: isang dimensyon.creatures/drake: isang nilalang na may sariling model at makinis na mga animation; maaari mo itong paamuin, sakyan at palipanin, at umuungol ito sa isang key.creatures/wisp: mga wisp na bumabaril sa mga undead.creatures/yak: mga yak na nagpaparami.controls/keys: ang mga key ng dash at ungol.events/welcome: isang unang pagbati.commands/kit: isang command para subukan ang lahat.
Ang file ng nilalang ay nagdedeklara lang ng nilalang na iyon. Ito ang drake:
creature "drake" {
body: "minecraft:bee"
model: "entity/drake"
texture: "entity/drake"
size: [1.4, 1.1]
health: 30
speed: 0.4
tame_item: "frostlands:frost_crystal"
rideable: true
behaviour: [{ follow_owner: 1.4, distance: 10 }, { fly_around: 1 }, { look_at_players: 8 }, "look_around"]
spawns: [{ biomes: ["minecraft:ice_spikes"], weight: 3, group: [1, 1] }]
}Tingnan ang Mga nilalang para sa bawat isa sa mga property na iyon.
Kailan maghahati
| Senyales | Ano ang gagawin |
|---|---|
| Lumampas sa mga 150 linya ang isang file | Hanapin ang mga paksang pinaghahalo nito at paghiwalayin ang mga ito. |
| Kinokopya mo ang parehong function sa dalawang file | Ilipat ito sa lib/ at i-import. |
| Lumalabas sa ilang lugar ang isang numero ng balance | Isang setting sa settings.craft (o sa class na Config sa Kotlin), o constant kung hindi ito dapat mababago. |
| Isinusulat mo ang parehong data key sa ilang file | Ilipat ito sa lib/storage.craft kasama ang dalawang function para magbasa at mag-save. |
| Tatlong magkakaibang bagay ang ginagawa ng isang event | Tatlong listener para sa iisang event, bawat isa sa file ng paksa nito. |