Poradniki
CraftScript
Język Craftingu. Angielskie słowa kluczowe, krótka składnia, bez kompilacji: zapisujesz plik, a mod się przeładowuje. Sprawdza się w modzie na dziesięć wierszy i w takim na czterdzieści plików z klasami i typami.
Co możesz zrobić już dziś
| Obszar | Jak |
|---|---|
| Przedmioty | item "ruby" { … }: rzadkość, połysk, jedzenie, narzędzie, wytrzymałość, tekstura, zakładka trybu kreatywnego, akcja użycia. |
| Bloki | block "ruby_ore" { … }: twardość, odporność na wybuchy, światło, dźwięk, narzędzie i minimalny poziom, łup, tarcie, przezroczystość, akcja użycia. Kształty i modele: zobacz Kształty bloków. Schody, drzwi, płoty, skrzynie, tabliczki, uprawy, rudy i inne rodzaje z gry, siedzenia i bloki o wysokości dwóch bloków: zobacz Rodzaje bloków. Bloki mogą też reagować na kroki i ticki, być portalami, mieć stany, korzystać z redstone i przechowywać przedmioty: zobacz Bloki, przedmioty i receptury. |
| Generowanie świata | ore, tree, patch, structure, biome: to, co pojawia się w nowych chunkach; terrain, biome_map, carver i struktury z plan/build dla lądu, biomów, jaskiń i budowli tworzonych kodem. Zobacz Generowanie świata i Generowanie kodem. |
| Stworzenia | creature "ghost" { … }: ciało stworzenia z gry z twoją teksturą lub własnym modelem 3D, statystykami, zachowaniem, celami i łupem, a także samoistne pojawianie się, oswajanie, hodowlę, jazdę, ataki dystansowe i animacje klatek kluczowych. Zobacz Stworzenia. |
| Dźwięki i cząsteczki | sound "chime" { file: … }, particle "spark" { textures: … }; używane po id. Zobacz Dźwięki i cząsteczki. |
| Receptury | recipe shaped, shapeless, smelting. Modele, łup, tagi i tłumaczenia są generowane za ciebie. |
| Zdarzenia | on join, on break(e)… wszystkie zdarzenia, z priorytetem i anulowaniem. |
| Zbroje, bronie, czasy odnowienia | armor: { slot, defense, texture } do noszenia, on_hit przy uderzeniu, cooldown między użyciami. Zob. Bloki, przedmioty i receptury. |
| Efekty, atrybuty, wyposażenie | e.add_effect(…), e.attribute(…), e.set_attribute(…), e.set_equipment(…). Zob. Świat i gracze. |
| Klawisze | key "dash" { label: "Dash", default: "R" } i on key(e); zdarzenia jednostek on interact_entity i on attack. Zob. Interfejs i Zdarzenia. |
| Polecenia | command pay(to: player, amount: int) requires "eco.pay", z aliasami, opisem i uprawnieniami. |
| Czas | every 5 minutes { }, after 10 seconds { }, on tick. |
| Świat i gracze | Bloki ze stanami, byty, eksplozje, cząsteczki, dźwięki, pora dnia, skrzynie, tabliczki; ekwipunek, zdrowie, tytuły, teleportacja. Zobacz Świat i gracze. |
| Interfejs | form(…) i player.show_form, server.boss_bar, player.set_sidebar. Zobacz Interfejs. |
| Zapisane dane | p.data.set("rubies.visits", n), p.data.get_int("rubies.visits"): w graczach, bytach, blokach i światach; przetrwają restart. Zobacz Świat i gracze. |
| Ustawienia i sieć | config("settings", { … }), listen, send, send_to_server. Zobacz Konfiguracja i sieć. |
| Duże mody | Opcjonalne typy, klasy z dziedziczeniem, typy wyliczeniowe, moduły, stałe. |
Tylko w Kotlinie: praca w tle, biblioteki Javy oraz, w generowaniu świata, featury i tagi tworzone kodem.
Pliki
Mod to pojedynczy plik (mods/gems.craft) albo, lepiej gdy się rozrośnie, folder z main.craft i plikami, które importuje. main.craft zaczyna się od nagłówka mod, który podaje id, nazwę i wersję:
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"Jak podzielić resztę na foldery i dlaczego: Porządkowanie moda.
Wartości i zmienne
let coins = 100
const MAX_HOMES = 3 // const: nie można przypisać ponownie
let greeting = "Masz {coins * 2} monet"
let names = ["Ana", "Ben"]
let prices = { gold: 5, ruby: 2 }
names.add("Cleo")
prices["diamond"] = 9
log(len(names), prices.gold, prices.keys())Liczby (zawsze dziesiętne), napisy, true/false, null, listy i mapy. Cudzysłowy podwójne interpolują {expression}; \{ zapisuje dosłowny nawias klamrowy. Cudzysłowy pojedyncze nie interpolują. Kolory czatu zapisuje się przez &: "&aGreen".
Sterowanie przepływem
fun describe(coins: number, banned: bool): string {
if coins > 100 and not banned {
return "rich"
} else if coins == 0 {
return "broke"
}
return "fine"
}
fun countdown() {
for i in 1..5 { broadcast("{6 - i}…") } // zakres domknięty
for p in players() { p.send("Start!") }
}
fun who(target: Player?, fallback: Player): string {
let p = target ?? fallback // ?? : wartość, gdy null
return p?.name ?? "nikt" // ?. : bezpieczne przy null
}Funkcje
fun give_rubies(p: Player, amount: number = 1) {
p.give(item("rubies:ruby", amount))
}
fun healthy(): list<Player> {
return players().filter(fun(p) { return p.health > 10 })
}Parametry z wartościami domyślnymi, return i funkcje anonimowe z fun(x) { … }, za pomocą których przekazujesz je do map, filter czy on_use.
Zdarzenia
Wewnątrz on właściwości zdarzenia są zmiennymi (player, message, block…), albo możesz nazwać zdarzenie: on chat(e). e.cancel() anuluje je, jeśli da się je anulować.
const BLOCKED = ["spam", "scam"]
on chat(e) priority high {
if BLOCKED.any(fun(w) { return e.message.contains(w) }) {
e.cancel()
e.player.send("&cBądź miły, proszę")
}
}Polecenia
command heal(target: player?, amount: number?) requires "rubies.heal" alias "h" describe "Uleczenie gracza" {
let p = target ?? player
if p == null { return "Podaj gracza" }
p.health = min(p.max_health, p.health + (amount ?? 20))
return "Uleczono: {p.name}"
}Wewnątrz polecenia masz sender, player (lub null, jeśli uruchamia je konsola) i args. To, co zwrócisz przez return, jest odpowiedzią. Jeśli brakuje argumentu lub ma zły typ, gracz widzi poprawne użycie.
Czas
every 5 minutes { broadcast("&6Pamiętaj, żeby zagłosować") }
after 10 seconds { log("serwer działa od 10 sekund") }Duże mody
Opcjonalne typy
Tam, gdzie piszesz typ, jest on wymuszany; tam, gdzie nie, kod pozostaje swobodny. Nawet jeśli nie napiszesz żadnego, sprawdzarka zna typy całego API:
on join {
player.helth = 20 // literówka
player.name = "Admin" // właściwość tylko do odczytu
player.give() // brakujący argument
}welcome.craft:2: error: Player has no property 'helth'. Did you mean 'health'?
welcome.craft:3: error: Player.name is read-only
welcome.craft:4: error: give() expects 1 argument(s), got 0fun heal(p: Player, amount: number = 20): bool {
p.health = min(p.max_health, p.health + amount)
return true
}
// heal(player, "a lot") → error: Argument 'amount' of heal() must be number, got stringTypy: number, string, bool, id, list<T>, map<T>, T? (może być null), any, void, fun, typy API (Player, World, Block, Entity, ItemStack, Location, Inventory…), zdarzenia oraz twoje własne klasy i typy wyliczeniowe. Po sprawdzeniu null sprawdzarka wie, że wartość istnieje:
command home(target: player?) {
if target == null { return "Podaj gracza" }
target.teleport(world().block_at(0, 80, 0).location) // tutaj target nie jest już null
}Klasy
class Wallet {
coins: number = 100
fun pay(n: number): bool {
if n > this.coins { return false }
this.coins -= n
return true
}
}
class VipWallet extends Wallet {
fun pay(n: number): bool {
return super.pay(n / 2) // VIP-y płacą połowę
}
}Przypisanie pola, którego klasa nie deklaruje, jest błędem (więc literówki się nie prześlizgną). Jeśli klasa definiuje init(…), to jest konstruktor; jeśli nie, argumenty wypełniają pola po kolei. is(value, Class) sprawdza typ.
Typy wyliczeniowe
enum Rank { PEASANT, KNIGHT, LORD, KING }
fun rank_for(coins: number): Rank {
if coins >= 1000 { return Rank.LORD }
return Rank.PEASANT
}
// Rank.LORD.name → "LORD", Rank.values() → [PEASANT, …], Rank.from("king") → KINGModuły
import { VipWallet } from "/lib/wallet"
import { rank_for } from "/lib/rank"
let wallets = {}
on join {
wallets[player.name] = VipWallet()
player.send("Ranga: {rank_for(wallets[player.name].coins).name}")
}Wszystko, co plik deklaruje poza jakimkolwiek blokiem, da się zaimportować: funkcje, klasy, typy wyliczeniowe, stałe. Każdy plik uruchamia się tylko raz, nawet jeśli importuje go kilka plików.
Sprawdzanie, przeładowanie i bezpieczeństwo
- Wszystko jest sprawdzane przy wczytywaniu moda: błąd blokuje wczytanie i mówi, gdzie wystąpił; ostrzeżenie trafia do dziennika.
crafting check main.craftrobi to samo z terminala, podążając za importami. - W trybie deweloperskim (
-Dcrafting.dev=true, zawsze włączony wcrafting sandbox) zapisanie przeładowuje mod; tak samo/crafting reload <id>. Nowy przedmiot lub blok wymaga restartu gry; wszystko inne zmienia się od razu. - Skrypt widzi tylko API Craftingu: nie może czytać plików, otwierać połączeń ani używać klas Javy.
- Każde zdarzenie, polecenie lub timer ma limit 2 000 000 kroków: nieskończona pętla zostaje przerwana błędem, zamiast zamrozić serwer.
- Błąd w czasie działania trafia do dziennika z plikiem i wierszem, a serwer działa dalej.
Każda funkcja, operator i składowa: Dokumentacja CraftScript.