Посібники
CraftScript
Мова Crafting. Ключові слова англійською, короткий синтаксис, жодної компіляції: ти зберігаєш файл, і мод перезавантажується. Вона підходить і для мода на десять рядків, і для мода на сорок файлів із класами й типами.
Що можна зробити вже сьогодні
| Сфера | Як |
|---|---|
| Предмети | item "ruby" { … }: рідкісність, сяйво, їжа, інструмент, міцність, текстура, вкладка творчого режиму, дія під час використання. |
| Блоки | block "ruby_ore" { … }: міцність, вибухостійкість, світло, звук, інструмент і мінімальний рівень, здобич, тертя, прозорість, дія під час використання. Форми та моделі: дивись Форми блоків. Сходи, двері, паркани, скрині, таблички, посіви, руди та інші види блоків гри, сидіння й блоки заввишки в два: дивись Види блоків. Блоки також можуть реагувати на кроки й тіки, бути порталами, мати стани, використовувати редстоун і зберігати предмети: див. Блоки, предмети й рецепти. |
| Генерація світу | ore, tree, patch, structure, biome: те, що з’являється в нових чанках; terrain, biome_map, carver і структури з plan/build для рельєфу, біомів, печер і споруд, створених кодом. Дивись Генерація світу і Генерація кодом. |
| Істоти | creature "ghost" { … }: тіло ігрової істоти з твоєю текстурою або власною 3D-моделлю, характеристиками, поведінкою, цілями й здобиччю, а також природну появу, приборкання, розведення, їзду, дальні атаки та анімації за ключовими кадрами. Дивись Істоти. |
| Звуки й частинки | sound "chime" { file: … }, particle "spark" { textures: … }; використовуються за id. Дивись Звуки й частинки. |
| Рецепти | recipe shaped, shapeless, smelting. Моделі, здобич, теги й переклади генеруються за тебе. |
| Події | on join, on break(e)… усі події, з пріоритетом і скасуванням. |
| Броня, зброя, перезарядка | armor: { slot, defense, texture }, щоб вдягати, on_hit під час удару, cooldown між використаннями. Див. Блоки, предмети та рецепти. |
| Ефекти, атрибути, спорядження | e.add_effect(…), e.attribute(…), e.set_attribute(…), e.set_equipment(…). Див. Світ і гравці. |
| Клавіші | key "dash" { label: "Dash", default: "R" } і on key(e); події сутностей on interact_entity і on attack. Див. Інтерфейс і Події. |
| Команди | command pay(to: player, amount: int) requires "eco.pay", з псевдонімами, описом і дозволами. |
| Час | every 5 minutes { }, after 10 seconds { }, on tick. |
| Світ і гравці | Блоки зі станами, сутності, вибухи, частинки, звуки, час доби, скрині, таблички; інвентар, здоров’я, заголовки, телепортація. Дивись Світ і гравці. |
| Інтерфейс | form(…) і player.show_form, server.boss_bar, player.set_sidebar. Дивись Інтерфейс. |
| Збережені дані | p.data.set("rubies.visits", n), p.data.get_int("rubies.visits"): у гравців, сутностей, блоків і світів; переживають перезапуск. Дивись Світ і гравці. |
| Налаштування й мережа | config("settings", { … }), listen, send, send_to_server. Дивись Налаштування й мережа. |
| Великі моди | Необов’язкові типи, класи з успадкуванням, переліки, модулі, константи. |
Лише Kotlin: фонова робота, бібліотеки Java і, в генерації світу, фічі та теги, створені кодом.
Файли
Мод — це один файл (mods/gems.craft) або, що краще, коли він розростається, тека з main.craft і файлами, які той імпортує. main.craft починається із заголовка mod, що задає id, назву й версію:
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"Як розкласти решту по теках і чому: Як організувати мод.
Значення й змінні
let coins = 100
const MAX_HOMES = 3 // const: не можна присвоїти знову
let greeting = "У тебе {coins * 2} монет"
let names = ["Ana", "Ben"]
let prices = { gold: 5, ruby: 2 }
names.add("Cleo")
prices["diamond"] = 9
log(len(names), prices.gold, prices.keys())Числа (завжди дробові), рядки, true/false, null, списки й мапи. Подвійні лапки підставляють {expression}; \{ записує звичайну фігурну дужку. Одинарні лапки нічого не підставляють. Кольори чату записуються через &: "&aЗелений".
Керування виконанням
fun describe(coins: number, banned: bool): string {
if coins > 100 and not banned {
return "багатій"
} else if coins == 0 {
return "без грошей"
}
return "нормально"
}
fun countdown() {
for i in 1..5 { broadcast("{6 - i}…") } // діапазон із кінцями включно
for p in players() { p.send("Старт!") }
}
fun who(target: Player?, fallback: Player): string {
let p = target ?? fallback // ?? : значення, якщо null
return p?.name ?? "ніхто" // ?. : безпечно з null
}Функції
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 })
}Параметри зі значеннями за замовчуванням, return і анонімні функції через fun(x) { … } — саме так їх передають у map, filter або on_use.
Події
Усередині on властивості події — це змінні (player, message, block…), або ж можна дати події назву: on chat(e). e.cancel() скасовує її, якщо її можна скасувати.
const BLOCKED = ["spam", "scam"]
on chat(e) priority high {
if BLOCKED.any(fun(w) { return e.message.contains(w) }) {
e.cancel()
e.player.send("&cБудь ласка, спілкуйся дружньо")
}
}Команди
command heal(target: player?, amount: number?) requires "rubies.heal" alias "h" describe "Вилікувати гравця" {
let p = target ?? player
if p == null { return "Вкажи гравця" }
p.health = min(p.max_health, p.health + (amount ?? 20))
return "{p.name} вилікувано"
}Усередині команди є sender, player (або null, якщо її виконує консоль) і args. Те, що ти повертаєш через return, і є відповіддю. Якщо аргументу бракує або він не того типу, гравець бачить правильний синтаксис.
Час
every 5 minutes { broadcast("&6Не забудь проголосувати") }
after 10 seconds { log("сервер працює вже 10 секунд") }Великі моди
Необов’язкові типи
Де ти пишеш тип, він перевіряється; де не пишеш, код лишається вільним. Навіть якщо ти не напишеш жодного, перевірка знає типи всього API:
on join {
player.helth = 20 // друкарська помилка
player.name = "Admin" // властивість лише для читання
player.give() // бракує аргументу
}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, "багато") → error: Argument 'amount' of heal() must be number, got stringТипи: number, string, bool, id, list<T>, map<T>, T? (може бути null), any, void, fun, типи API (Player, World, Block, Entity, ItemStack, Location, Inventory…), події, а також твої класи й переліки. Після перевірки на null перевірка знає, що значення існує:
command home(target: player?) {
if target == null { return "Вкажи гравця" }
target.teleport(world().block_at(0, 80, 0).location) // тут target уже не null
}Класи
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 платять половину
}
}Присвоєння поля, якого клас не оголошує, — помилка (тож друкарські помилки не прослизнуть). Якщо клас визначає init(…), це конструктор; якщо ні, аргументи заповнюють поля по черзі. is(value, Class) перевіряє тип.
Переліки
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") → KINGМодулі
import { VipWallet } from "/lib/wallet"
import { rank_for } from "/lib/rank"
let wallets = {}
on join {
wallets[player.name] = VipWallet()
player.send("Ранг: {rank_for(wallets[player.name].coins).name}")
}Усе, що файл оголошує поза будь-яким блоком, можна імпортувати: функції, класи, переліки, константи. Кожен файл виконується лише один раз, навіть якщо його імпортують кілька файлів.
Перевірка, перезавантаження й безпека
- Усе перевіряється під час завантаження мода: помилка не дає йому завантажитися й каже, де вона; попередження йде в журнал.
crafting check main.craftробить те саме з термінала, ідучи за імпортами. - У режимі розробки (
-Dcrafting.dev=true, завжди ввімкнений уcrafting sandbox) збереження перезавантажує мод; так само діє/crafting reload <id>. Новий предмет чи блок потребує перезапуску гри; усе інше змінюється одразу. - Скрипт бачить лише API Crafting: він не може читати файли, відкривати з’єднання чи використовувати класи Java.
- Кожна подія, команда чи таймер мають ліміт у 2 000 000 кроків: нескінченний цикл переривається з помилкою, а не заморожує сервер.
- Помилка під час виконання записується в журнал із файлом і рядком, а сервер працює далі.
Усі функції, оператори й члени: Довідник CraftScript.