SDK 레퍼런스 (greenhaven-plugin)

use greenhaven_plugin::*; 하나로 아래가 모두 들어옵니다. 좌표는 모두 칸(1 m)이고, y는 블록의 가운데입니다 (마을 땅은 y 0의 블록, 윗면 0.5).

  1. 함수
  2. Player
  3. World
    1. 알림과 효과
    2. 몬스터와 엔티티
    3. 값과 데이터
    4. 하루
    5. 블록
    6. 구조물과 주민
  4. Server
  5. OfflinePlayer
  6. Npc
  7. Entity
  8. Mob
  9. Structure
  10. Block
  11. Gui
  12. 데이터 저장
  13. sys — 원본 함수

함수

   
now() → f64 벽시계: 1970년부터의 ms (UTC), 서버의 모든 월드에 같음
setting(key) → Option<String> 이 플러그인의 설정 값, 글자로 ("12.5", "true"); 없는 설정은 None
setting_num(key, or) → f64 숫자 설정, 없으면 or
setting_bool(key, or) → bool 켜기/끄기 설정, 없으면 or

Player

이벤트의 플레이어 (e.player()). Tick::players()가 주는 것은 그 사람.

메서드  
name() → String 이름 ("": 월드 주인)
position() → (f64, f64) 위치 (칸)
facing() → (f64, f64) 바라보는 방향 (행동의)
give(item, n) / take(item, n) 아이템 주기 / 가져가기
add_gold(d) 골드 (음수면 뺌)
add_energy(d) 기력
heal(n) 체력
add_xp(skill, n) 스킬 경험치
add_friendship(villager, points) 주민 호감도
teleport(x, z) 옮기기 (서버의 이동 판정도 그 자리를 받아들임)
set_time(minute) 그 사람이 있는 월드의 시각 (6:00 = 360 ~ 2:00 = 1560; 날짜는 그대로), 그 월드의 게임에 바로 알림
set_walk_speed(times) 걸음 빠르기 배수 (1이 원래). 게임의 걸음과 서버의 이동 판정이 함께 바뀜. 바꿀 때까지 유지
set_model(id) clear_model() 커스텀 모델로 그림 / 원래 모습으로. 연결된 동안만, 계정은 그대로
play_anim(id) stop_anim() 감정 표현 재생 / 끝내기. 가만히 서 있을 때만, 움직이거나 행동하면 끝남
say(line) 머리 위의 한마디
message(line) 채팅 한 줄
title(line) 화면 가운데 큰 제목
where_text(line) 화면의 장소 문구 ("": 게임의 것)
open_shop(shop) 가게 열기 (VillagerData.SHOPS의 id)
play_music(track) 지금 곡 틀기 (Music.TRACKS의 id나 res://….ogg; "": 원래 목록으로)
visit(owner) 열기구로 그 사람 월드에 바로 내림 (PlayerVisit이 불림)
home() 자기 월드로
show(&Gui) 창 띄우기 (Gui). PlayerLogin에서는 들어오기 전의 입력 창
get(k) / set(k, v) 그 사람에게 남기는 숫자 (이 플러그인의 것; 저장됨). plugin.json의 바가 보여 주는 값
data_put(key, &[u8]) → bool 그 사람의 바이트 (데이터 저장)
data_get(key) → Option<Vec<u8>>  
data_del(key)  

Tick에서 Tick::players()가 준 Player라도 give take add_gold add_energy say는 월드 주인에게 갑니다.

World

이벤트의 월드 (e.world()).

알림과 효과

메서드  
broadcast(line) 월드 모두의 채팅
play_music(track) 월드의 모두에게 곡 ("": 원래 목록으로)
particle(name, x, y, z) 파티클: "fire" "smoke" "sparkle" "splash" "leaf" "hit" 또는 res://….png
sound(name, x, y, z) 소리: "hit" "pickup" "click" "step_soft" "step_hard" 또는 res://….ogg
set_weather(name, days_ahead) 그날(0 오늘)의 날씨: "sunny" "rain" "storm" "snow" …

몬스터와 엔티티

메서드  
strike(x, z, radius, damage) 그 둘레의 몬스터 공격
spawn_mob(kind, x, z) 이벤트의 층에 몬스터 하나 (지상은 밤에만 남음)
spawn(model, x, y, z) → Option<Entity> 이 플러그인의 엔티티: res:// 모델(glb, 씬) 또는 png(늘 카메라를 보는 그림)
entities() → Vec<Entity> 이 플러그인의 엔티티들
npcs() → Vec<Npc> 주민들

값과 데이터

메서드  
get(k) / set(k, v) 월드에 남기는 숫자 (이 플러그인의 것; 저장됨)
data_put(key, &[u8]) → bool 데이터 저장
data_get(key) → Option<Vec<u8>>  
data_del(key)  
data_keys(prefix) → Vec<String> 이 플러그인의 키 중 prefix로 시작하는 것
save_now() 월드를 아침처럼 지금 저장 (게임, 블록, 데이터; 날은 그대로 이어짐)
set_live_saving(on) 1분마다·플레이어가 나갈 때 저장 (잠이 없는 서버). server.properties save-mode보다 우선
set_quit_warning(text) 자기 전에 나갈 때 게임이 띄우는 경고 ("" 없음, 200자까지). server.properties quit-warning보다 우선

하루

메서드  
set_day(day) 날짜 (1이 첫날; 계절은 날짜에서). 월드의 게임에 바로 알림
set_day_start(minute) 이 월드의 아침 시각 (6:00 = 360)
set_day_end(minute) 이 월드의 하루의 끝 (그때 DayEnd "clock"; 다음날 6:00 = 1800)
end_day() 지금 하루를 끝냄, 모두 잔 것처럼 (먼저 DayEnd "end_day")

블록

메서드  
block(x, y, z) → Block 그 블록 (아직 안 만든 청크는 바닐라 땅으로)
top(x, z) → i64 기둥에서 가장 높은 단단한 블록의 y (없으면 Block::LOWEST - 1)
set_block(x, y, z, b) 블록 놓기 (저장됨)
fill(from, to, b) 상자 모양 채우기, 양 끝 포함, 한 번에 청크 넷까지

구조물과 주민

메서드  
structures() → Vec<Structure> 월드에 선 것들 (건물, 울타리, 성벽, 성문, 정거장 …)
put_structure(json) → Option<Structure> 세우기: {"k": "building", "m": "house_Lv1", "x", "z", "yaw", "plot"}, 칸 사각형 종류는 {"k": "block", "cells": [x, z, w, d]}
remove_plot(plot) 구획 통째로 치우기 ("wall": 성벽·성문·바위 둑·암초, "station", "core" 마을 한가운데 …)
spawn_npc(json) → bool 주민 더하기 ({"id", "name", "look", "home", "shop", "schedule": [[480, "square"]] …})

월드와 하루에 자세히.

Server

월드와 상관없는 서버의 값. Server (단위 구조체)로 씁니다.

메서드  
get(k) → f64 어느 이벤트에서나 읽음
set(k, v) PlayerLogin과 그 입력 창의 Button, ServerTick에서만
set_shared_world(on) 모두의 월드 (월드와 하루). ServerTick이나 PlayerLogin에서만
#[on(ServerTick)]
fn count(_e: ServerTick) {
    Server.set("clock", Server.get("clock") + 1.0);
}

OfflinePlayer

지금 접속해 있지 않은 사람까지, 이 서버에 한 번이라도 들어온 누구든 읽습니다. 읽기 전용입니다.

   
Server.offline_player(name) → OfflinePlayer 이름으로
Server.offline_players() → Vec<OfflinePlayer> 들어온 적 있는 모두, 처음 들어온 순서
메서드  
name() 이름
has_played() → bool 저장된 게임이 있는지
gold() day() 골드, 그 사람 월드의 날짜
last_seen() → f64 마지막으로 나간 때 (1970년부터의 ms)
level(skill) friendship(villager) item_count(item) 스킬 레벨, 주민 호감도, 가방의 개수
num(k) "gold" "day" "energy" "seen" "level.<스킬>" "xp.<스킬>" "friendship.<주민>" "item.<id>"
get(k) 이 플러그인이 그 사람에게 남긴 숫자, 그 사람 월드에서 (Player::get)
world_get(k) 이 플러그인이 그 사람 월드에 남긴 숫자 (World::get)
data_get(key) / world_data_get(key) 이 플러그인이 그 사람에게 / 그 사람 월드에 맡긴 바이트
#[command("부자", "골드가 가장 많은 농장 주인")]
fn richest(c: Command) {
    let best = Server.offline_players().into_iter().max_by_key(|p| p.gold());
    if let Some(p) = best {
        c.player().message(&format!("{}: {}골드", p.name(), p.gold()));
    }
}
  • 마지막으로 저장된 상태를 읽습니다 (그 사람 월드의 아침, save_now, 실시간 저장). 지금 접속해 있는 사람도 그렇게 읽히니, 그날 한 일까지 보려면 그 사람 월드의 이벤트에서 Player를 쓰세요.
  • 다른 플러그인의 값과 데이터는 보이지 않습니다.
  • 이벤트 한 번에 디스크에서 읽는 사람은 32명까지입니다(넘으면 없는 사람처럼 읽힘). 한 번 읽은 사람은 파일이 바뀔 때까지 다시 읽지 않으니, 같은 사람을 여러 번 읽는 것은 괜찮습니다.

Npc

주민 (Npc(id); Words::npc(), World::npcs()).

메서드  
get(k) "x" "z" "moving" "hidden" "indoors"
position() → (f64, f64)  
go_to(x, z, hold_secs) 그곳(설 수 있는 가장 가까운 칸)으로 보내 몇 초 머물게 함. 일정보다 먼저
say(line) 머리 위의 한마디, 월드의 모두에게
set(json) 덮어쓰기 ({"home": "...", "schedule": [...]})
remove() 월드에서 빼기

Entity

이 플러그인의 엔티티 (World::spawn). 저장되지 않습니다: 서버를 다시 켜면 사라지니, 남길 것은 World::set에.

메서드  
get(k) / set(k, v) "x" "y" "z" "yaw" "scale" (get은 "alive"도)
position() → (x, y, z)  
move_to(x, y, z)  
play(clip) 모델의 애니메이션
remove()  
#[on(Button)]
fn fireball(e: Button) {
    let (x, z) = e.player().position();
    let (fx, fz) = e.player().facing();
    if let Some(ball) = e.world().spawn("res://plugins/example/fireball.glb", x, 1.0, z) {
        e.world().set(&format!("ball.{}.vx", ball.0), fx * 8.0);
        e.world().set(&format!("ball.{}.vz", ball.0), fz * 8.0);
    }
}

Mob

몬스터 하나 (Mob(serial); MobStep::mob()).

메서드  
get(k) mob 이벤트에서: "x" "z" "hp" "tier" "speed" "damage" "size" "t"
set(k, v) "x" "z"(옮기기) "hp"(0이면 사라짐) "speed" "damage", "vx" "vz"(이번 걸음의 속도: 원래 움직임 대신)
attack(damage, reach) 그 거리 안의 가장 가까운 플레이어를 침 (닿을 때의 공격은 따로 그대로)
remove() 없애기 (떨굼 없음)

어느 이벤트에서나 쓸 수 있습니다(tick·button에서 소환한 것을 움직이는 등).

Structure

월드에 선 것 하나 (Structure(id)).

메서드  
get(k) → f64 "x" "z" "y" "yaw" "w" "d" "scale"
text(k) → String "k"(무엇) "m"(모델) "plot" "n" "key" "kind" "label" "text" "s" "owner"
kind() / plot() text("k") / text("plot")
remove()  
move_to(x, z, yaw)  

Block

블록 종류 (Block(번호)).

상수 번호  
Block::AIR 0  
Block::GRASS 1  
Block::SAND 2  
Block::SNOW 3  
Block::STONE 4  
Block::DIRT 5  
Block::ROAD 6  
Block::BED 7 물 밑 모래
Block::WATER 8  
Block::LOWEST -8 가장 낮은 블록의 y (그 아래는 모두 단단함). 청크는 이로부터 64칸 높이

Block::named("stone") → Option<Block>: 이름(또는 번호)으로.

Gui

플레이어에게 보이는 창 (Player::show). 빌더로 만듭니다.

메서드  
Gui::new(id) 창의 id (버튼을 누르면 Button::gui())
Gui::close(id) 열린 그 창 닫기 (show로 보냄)
.title(s) 제목
.text(s) 본문
.npc(id) 주민의 얼굴과 이름
.image(path) res:// 그림
.button(id, label) 버튼 (나무)
.button_styled(id, label, style) "go"(초록) "red" ""(나무)
.input(id, label, placeholder, max) 한 줄 입력 칸 (max 글자, 1~256). 누르면 Button::input(id)
.secret(id, label, placeholder, max) 가려 쓰는 칸 (비밀번호)
.json() 보낼 JSON (원본 ABI의 gui)
#[on(PlayerLogin)]
fn ask(e: PlayerLogin) {
    e.player().show(&Gui::new("pw").title("비밀번호").text("이 서버의 비밀번호를 넣어 주세요")
        .secret("pw", "비밀번호", "", 64).button_styled("ok", "들어가기", "go"));
}

데이터 저장

플러그인이 파일 대신 서버에 맡기는 바이트입니다 (World::data_*, 플레이어마다 Player::data_*: 그 월드 안, 그 사람 것).

  • 키·플러그인마다의 한도는 없고, 월드 하나의 모든 플러그인 데이터가 합쳐 100 MB까지 (넘는 put은 false).
  • 다른 플러그인의 키는 보이지 않습니다.
  • 저장 시점은 게임과 같습니다 (아침, save_now, 실시간 저장). 그 전에도 읽으면 방금 넣은 것이 보입니다.
  • 위치: <data>/plugin_data/<월드>/<플러그인>/.
let w = e.world();
w.data_put("log/1", "첫 수확".as_bytes());
for k in w.data_keys("log/") {
    let line = String::from_utf8(w.data_get(&k).unwrap_or_default()).unwrap_or_default();
}

sys — 원본 함수

SDK가 감싸지 않은 것은 greenhaven_plugin::sys에 “gh” 함수 그대로 있습니다 (목록).