API
Konfiguracja i sieć
Ustawienia, które gracz lub właściciel serwera może zmienić bez dotykania kodu, oraz twoje własne dane przesyłane między klientem a serwerem. Jedno i drugie działa z CraftScript i z Kotlina.
Konfiguracja w CraftScript
Osobny plik, settings.craft, deklaruje ustawienia z ich wartościami domyślnymi; reszta moda je importuje:
// Values the server owner can change in config/rubies/settings.toml
const SETTINGS = config("settings", {
bonus_chance: 0.25,
welcome_rubies: 3
})import { give_rubies } from "/lib/rewards"
import { SETTINGS } from "/settings"
on break(e) {
let p = e.player
if p == null { return }
if e.block.type == "minecraft:diamond_ore" and chance(SETTINGS["bonus_chance"]) {
give_rubies(p, 1)
p.play_sound("rubies:ruby_chime")
p.world.spawn_particle("rubies:ruby_spark", e.block.location, 12, 0.4, 0.02)
}
}config("settings", { … })za pierwszym razem tworzyconfig/rubies/settings.tomli zwraca bieżące wartości. Typ każdego ustawienia wynika z jego wartości domyślnej: liczba całkowita pozostaje całkowitą.config("settings"), bez wartości, czyta je ponownie po ich zadeklarowaniu.- Trzeci argument
truesprawia, że serwer wysyła swoje wartości klientom, gdy się łączą. - Nazwa używa małych liter, cyfr,
_lub-. Mod może mieć kilka plików ustawień.
Konfiguracja w Kotlinie
Jedna klasa na mod z wartościami do zmiany, ich wartościami domyślnymi i opisem:
package com.example.rubies
class RubiesConfig : Config("rubies", synced = true) {
var dropChance by double("drop_chance", 0.25, "Chance that diamond ore also drops a ruby")
var armorReduction by double("armor_reduction", 0.2, "Damage reduction while holding a ruby")
}override fun onLoad(ctx: ModContext) {
val config = Crafting.configs.load(RubiesConfig())
MiningEvents.register(config) // każda część dostaje potrzebną jej konfigurację
}Crafting za pierwszym razem tworzy config/rubies.toml:
# rubies — generated by Crafting. Edit freely; comments are regenerated.
# Chance that diamond ore also drops a ruby
# default: 0.25
drop_chance = 0.25
# Damage reduction while holding a ruby
# default: 0.2
armor_reduction = 0.2- Typy:
int,long,double,bool,string,stringList. - Komentarze są generowane od nowa; wartości wpisane przez użytkownika zostają zachowane.
- Nieprawidłowa wartość niczego nie psuje: Crafting ostrzega w dzienniku i używa wartości domyślnej.
Crafting.configs.reload(config)czyta plik ponownie;save(config)go zapisuje.- Z
synced = trueserwer wysyła swoje wartości klientom z Craftingiem, gdy się łączą, więc wszyscy grają według tych samych zasad.
Na twoje własne pliki ctx.configDir (config/<mod>/) i ctx.dataDir (data/<mod>/) są tworzone, gdy o nie poprosisz.
Kto może dołączyć do serwera
Przy połączeniu klient i serwer porównują swoje mody. Zasady:
| Sytuacja | Wynik |
|---|---|
| Serwer ma tylko wtyczki lub mody po stronie serwera; klient nie ma modów | Dołącza |
| Serwer ma mody, które dodają zawartość; klient nie ma modów | Nie może dołączyć i widzi dokładną listę potrzebnych modów |
| Klientowi brakuje moda, który dodaje zawartość | Nie może dołączyć: „Brakujące mody: rubies 1.2.0” |
| Inna wersja główna (1.x i 2.x) | Nie może dołączyć, z pokazaniem obu wersji |
| Inna wersja podrzędna (1.2 i 1.3) | Dołącza, z ostrzeżeniem w dzienniku serwera |
| Mody tylko dla klienta | Zawsze dozwolone |
Dlatego wersjonuj swój mod starannie: zwiększaj wersję główną tylko wtedy, gdy klient i serwer w różnych wersjach nie mogą już grać razem.
Kanały w CraftScript
Kanał przenosi dane między klientem a serwerem: liczby, napisy, wartości logiczne, listy i mapy. Nazwa "ping" staje się rubies:ping, więc nie koliduje z innymi modami.
// Każda strona nasłuchuje tego, co odbiera: pakiet dociera tylko do strony, która nasłuchuje.
// Serwer: przywitaj każdego gracza, który dołącza.
on join {
send(player, "ping", { n: 1 })
}
// Klient: odpowiedz.
listen("ping", fun(data, from) {
send_to_server("pong", { n: data.n + 1 })
})
// Serwer: odczytaj odpowiedź (from to gracz, który ją wysłał).
listen("pong", fun(data, from) {
log("{from?.name} odpowiedział {data.n}")
})listen(channel, fun(data, player) { … })odbiera;playerto nadawca na serwerze, anullna kliencie.send(player, channel, data)wysyła z serwera do gracza;send_to_server(channel, data)wysyła z klienta do serwera.- Pakiet dociera tylko do strony, która nasłuchuje na danym kanale: każda strona ogłasza swoje kanały przy połączeniu. Dlatego mod wywołuje
listenpo obu stronach dla tego, co każda z nich odbiera. - Gdy mod zostaje przeładowany lub wyłączony, jego wywołania
listenznikają razem z nim.
Kanały w Kotlinie
Kanał wysyła wartości jednego typu między klientem a serwerem. Działa tak samo w świecie jednoosobowym jak na serwerze.
package com.example.rubies.net
object SparkleChannel {
private val channel = Crafting.network.channel(Identifier.of("rubies", "sparkle"), PacketCodec.STRING)
fun register() {
channel.onReceive { message, ctx ->
// ctx.player: nadawca na serwerze; null na kliencie
}
}
fun announce(text: String) = channel.broadcast(text) // serwer → każdy klient
fun tell(player: Player, text: String) = channel.sendTo(player, text)
}- Dane są kodowane w zwartym formacie binarnym przez
PacketWriteriPacketReader. - Crafting nigdy nie wysyła swoich pakietów do klienta bez Craftingu: to by go rozłączyło.
sendTonic nie wysyła graczowi, który nie ogłosił tego kanału;Crafting.network.isChannelAvailable(player, id)mówi ci to z góry. - Uszkodzony pakiet trafia do dziennika i zostaje odrzucony; nigdy nie wyłącza serwera.
Własne typy
package com.example.rubies.net
data class Bounty(val target: String, val reward: Int)
object BountyChannel {
private val CODEC = PacketCodec.codec<Bounty>(
{ b, out -> out.string(b.target).varInt(b.reward) },
{ input -> Bounty(input.string(), input.varInt()) },
)
val channel = Crafting.network.channel(Identifier.of("rubies", "bounty"), CODEC)
}