はじめに
今年のゴールデンウィークに子供と一緒にMinecraftを楽しむため、自宅でPaperMCサーバーを立てました。その時に作ったコードの紹介をします。
ゲーム内で迷子になりやすい私は、テレポート機能を持つプラグインを探しましたが、気に入ったものが見つかりませんでした。そこで、Minecraft用の言語であるSkriptを使って自作することにしました。
Skriptの資料が少なく苦労しましたが、同じような問題を抱える方の参考になればと思い、私のスクリプトを公開します。皆様のお役に立てれば幸いです。
どうぞご利用ください!
対象
- サーバー、及びプラグインは自力で設定できる方
- Skriptをこれから勉強したい方
環境
- サーバー:
- marctv/minecraft-papermc-server:1.20.6 → Dockerで立ち上げました。
- 使用プラグイン:
- Skript-2.8.7.jar
- SkCheese-1.4.1.jar
- skript-yaml-1.5.jar
- skUtilities.v0.9.2.jar
機能一覧
- テレポート位置の登録・削除
- テレポート位置のアイコンを選択可
- 死亡時の位置を自動で登録
- 登録位置にテレポート
使用方法
実行コマンド/tpでguiを開く
新規追加をクリック
名前を登録
アイコンを選択
追加完了
青ブロックをクリックして赤くなると削除モードになります
コードで参考になるかなと思う箇所
- 定数 (options:)
- 変数 (hoge:)
- 配列 (hage::)
- If文、Loop文、Case文
- 引数を使った関数 (引数がhogeの場合関数内は_hogeとして扱う。_をつけるとローカル変数になる)
- Skriptでのテキスト入力方法(テレポート新規作成時に名前を付ける部分で使っています
- yamlの読み書き
- configファイルの設定
- guiの表示及びページング
文法について
ほぼ英語の文法そのままで記載できます
-
if event-item is stone named ”reload” → event-itemは名前がreloadのstoneか?
-
set {a} to “b” → 変数aにbをセット
-
set {_lc} to location of {_p} → 変数_lcに変数_pのlocation をセット
コード
もっとすっきり書けるかもしれませんがこれが限界です。
/plugins/Skript/data/config.yml
debug: false
page_size: 36 #max 36 1ページ内に表示する項目数
/plugins/Skript/mytp.sk
options:
yml_dir:"plugins/Skript/data/"
answer_y:"y"
answer_n:"n"
answer_q:"q"
answer_any:"any"
act_add:"act_add"
act_del:"act_del"
act_close:"act_close"
act_select_icon:"act_select_icon"
act_teleport:"act_teleport"
act_next:"act_next"
act_prev:"act_prev"
gui_name1:"TP MENU"
gui_name2:"SELECT ITEM"
key_yml_id:"key_yml_id"
key_click_item_id:"click_id"
key_mode:"key_mode"
value_mode_add:"value_mode_add"
value_mode_delete:"value_mode_delete"
key_input_status:"key_input_status"
value_input_status_none:"value_input_status_none"
value_input_status_wait:"value_input_status_wait"
value_input_status_inputed:"value_input_status_inputed"
key_input_name:"key_input_name"
key_is_tp_close:"key_is_tp_close"
key_is_tp_start:"key_is_tp_start"
key_tp_list:"key_tp_list"
key_tp_is_delete:"key_tp_is_delete"
key_tp_max_page:"key_tp_max_page"
key_tp_page:"key_tp_page"
key_tp_count:"key_tp_count"
key_lc_location:"key_lc_location"
key_lc_name:"key_lc_name"
key_lc_item:"key_lc_item"
key_lc_action:"key_lc_action"
key_lc_select_no:"key_lc_select_no"
key_gui_name1_slots:"key_gui_name1_slots"
pink:pink stained glass
red:red stained glass
blue:blue stained glass
yellow:yellow stained glass
local function clearIcons():
clear {ICONS::*}
local function setIcons(myslot:object,myitem:object,myname:object,myaction:object):
set {ICONS::%{_myslot}%} to {_myslot}
set {ICONS::%{_myslot}%::item} to {_myitem}
set {ICONS::%{_myslot}%::name} to {_myname}
set {ICONS::%{_myslot}%::action} to {_myaction}
on load:
clearIcons()
setIcons(0,{@pink},"ピンク",{@act_select_icon})
setIcons(1,{@red},"赤",{@act_select_icon})
setIcons(2,{@blue},"青",{@act_select_icon})
setIcons(3,{@yellow},"黄",{@act_select_icon})
setIcons(53,barrier,"止める",{@act_close})
load yaml "%{@yml_dir}%config.yml" as "config"
set {DEBUG} to yaml value "debug" from "config"
set {PAGE_SIZE} to yaml value "page_size" from "config"
command /tp:
trigger:
openGui(player)
local function setSlots(p:player,slot:number,item:object,name:object,action:object):
setProp({_p},"%{@key_gui_name1_slots}%::%{_slot}%",{_slot})
setProp({_p},"%{@key_gui_name1_slots}%::%{_slot}%::name",{_name})
setProp({_p},"%{@key_gui_name1_slots}%::%{_slot}%::item",{_item})
setProp({_p},"%{@key_gui_name1_slots}%::%{_slot}%::action",{_action})
local function openGui(p:player):
setProp({_p},{@key_is_tp_start},true)
getLocation({_p})
deleteListProp({_p},{@key_gui_name1_slots})
set {_del} to getProp({_p},{@key_tp_is_delete})
if {_del} is true:
setSlots({_p},46,red concrete,"削除中止",{@act_del})
else:
setSlots({_p},46,blue concrete,"削除",{@act_del})
setSlots({_p},45,white stained glass,"新規追加",{@act_add})
setSlots({_p},53,barrier,"閉じる",{@act_close})
set {_page} to getProp({_p},{@key_tp_page}) + 1
set {_max_page} to getProp({_p},{@key_tp_max_page})
if 1 < {_page}:
setSlots({_p},49,stone,"前ページ",{@act_prev})
if {_page} < {_max_page}:
setSlots({_p},51,stone,"次ページ",{@act_next})
setSlots({_p},50,white stained glass pane,"%{_page}%/%{_max_page}%","")
loop {PAGE_SIZE} times:
set {_lc_num} to ({_page} -1 ) * {PAGE_SIZE} + loop-number - 1
debugLog({_p},"load lc no :%{_lc_num}% page:%{_page}% count:%{PAGE_SIZE}%")
set {_name} to getProp({_p},"%{@key_tp_list}%::%{_lc_num}%::%{@key_lc_name}%")
set {_item} to getProp({_p},"%{@key_tp_list}%::%{_lc_num}%::%{@key_lc_item}%")
set {_action} to getProp({_p},"%{@key_tp_list}%::%{_lc_num}%::%{@key_lc_action}%")
setSlots({_p},loop-number - 1,{_item},{_name},{_action})
open chest inventory with 6 row named {@gui_name1} to {_p}
loop getListProp({_p},{@key_gui_name1_slots}):
set {_slot} to loop-value
debugLog({_p},"getListProp %{_slot}%")
set {_name} to getProp({_p},"%{@key_gui_name1_slots}%::%{_slot}%::name")
set {_item} to getProp({_p},"%{@key_gui_name1_slots}%::%{_slot}%::item")
set slot {_slot} of {_p}'s current inventory to {_item} named {_name}
if {DEBUG} is true:
set slot 47 of {_p}'s current inventory to stone named "reload"
local function openGui2(p:player):
open chest inventory with 6 row named {@gui_name2} to {_p}
loop {ICONS::*}:
set {_slot} to loop-index
set slot {ICONS::%{_slot}%} of {_p}'s current inventory to {ICONS::%{_slot}%::item} named {ICONS::%{_slot}%::name}
on inventory click:
set {_slot} to index of event-slot
name of clicked inventory is {@gui_name1}:
cancel event
if event-item is stone named "reload":
debugLog(player,"reload")
execute player command "/skript reload scripts"
close player's inventory
set {_action} to getProp(player,"%{@key_gui_name1_slots}%::%{_slot}%::action")
debugLog(player,"_action :%{_action}%")
switch {_action}:
case {@act_add}:
send "名前を入力してください (q:キャンセル)" to player
setProp(player,{@key_input_status},{@value_input_status_wait})
closeForNext(player)
case {@act_teleport}:
set {_lc_no} to {_slot} + {PAGE_SIZE} * getProp(player,{@key_tp_page})
if {_lc_no} is getProp(player,{@key_lc_select_no}):
set {_del} to getProp(player,{@key_tp_is_delete})
if {_del} is true:
set {_lc_id} to getProp(player,"%{@key_tp_list}%::%{_lc_no}%")
debugLog(player,"del lc id:%{_lc_id}%")
delLocation(player,{_lc_id})
exit(player)
openGui(player)
else:
set {_lc} to getProp(player,"%{@key_tp_list}%::%{_lc_no}%::%{@key_lc_location}%")
teleport player to {_lc}
exit(player)
else:
setProp(player,{@key_lc_select_no},{_lc_no})
case {@act_close}:
exit(player)
case {@act_next}:
set {_page} to getProp(player,{@key_tp_page}) + 1
setProp(player,{@key_tp_page},{_page})
closeForNext(player)
openGui(player)
case {@act_prev}:
set {_page} to getProp(player,{@key_tp_page}) - 1
setProp(player,{@key_tp_page},{_page})
closeForNext(player)
openGui(player)
case {@act_del}:
setProp(player,{@key_lc_select_no},-1)
set {_del} to getProp(player,{@key_tp_is_delete})
if {_del} is true:
setProp(player,{@key_tp_is_delete},false)
else:
setProp(player,{@key_tp_is_delete},true)
closeForNext(player)
openGui(player)
default:
set {_none} to true
name of clicked inventory is {@gui_name2}:
cancel event
switch {ICONS::%{_slot}%::action}:
case {@act_select_icon}:
set {_item} to {ICONS::%{_slot}%::item}
set {_name} to getProp(player,{@key_input_name})
saveLocation(player,{_item},{_name})
exit(player)
openGui(player)
case {@act_close}:
clearPlayerStatus(player)
closeForNext(player)
openGui(player)
default:
set {_none} to true
local function exit(p:player):
close {_p}'s inventory
local function closeForNext(p:player):
setProp({_p},{@key_is_tp_close},true)
close {_p}'s inventory
on inventory close:
if getProp(player,{@key_is_tp_close}) is true:
setProp(player,{@key_is_tp_close},false)
debugLog(player,"イベントクローズ")
else:
debugLog(player,"その他クローズ")
if getProp(player,{@key_is_tp_start}) is true:
clearPlayerStatus(player)
on death of player:
if victim is player:
saveLocation(victim,stone,"死んだ!")
local function saveLocation(p:player,myitem:object,myname:object):
set {_lc_uuid} to random uuid
set {_lc} to location of {_p}
set {_yml_id} to getProp({_p},{@key_yml_id})
set yaml value "locations.%{_lc_uuid}%.lc" from {_yml_id} to {_lc}
set yaml value "locations.%{_lc_uuid}%.name" from {_yml_id} to {_myname}
set yaml value "locations.%{_lc_uuid}%.item" from {_yml_id} to {_myitem}
save yaml {_yml_id}
local function getLocation(p:player):
deleteListProp({_p},{@key_tp_list})
set {_yml_id} to getProp({_p},{@key_yml_id})
set {_cnt} to 0
loop yaml node list "locations" from {_yml_id}:
setProp({_p},"%{@key_tp_list}%::%{_cnt}%",loop-value-1)
set {_lc} to yaml value "%loop-value-1%.lc" from {_yml_id}
debugLog({_p},"loc x %x of {_lc}%")
set {_name} to yaml value "%loop-value-1%.name" from {_yml_id}
set {_item} to yaml value "%loop-value-1%.item" from {_yml_id}
setProp({_p},"%{@key_tp_list}%::%{_cnt}%",loop-value-1)
setProp({_p},"%{@key_tp_list}%::%{_cnt}%::%{@key_lc_location}%",{_lc})
setProp({_p},"%{@key_tp_list}%::%{_cnt}%::%{@key_lc_name}%",{_name})
setProp({_p},"%{@key_tp_list}%::%{_cnt}%::%{@key_lc_item}%",{_item})
setProp({_p},"%{@key_tp_list}%::%{_cnt}%::%{@key_lc_action}%",{@act_teleport})
add 1 to {_cnt}
setProp({_p},{@key_tp_count},{_cnt})
setProp({_p},{@key_tp_max_page},ceil({_cnt} / {PAGE_SIZE}))
local function delLocation(p:player,lc_id:text):
set {_yml_id} to getProp({_p},{@key_yml_id})
delete yml value {_lc_id} from {_yml_id}
save yaml {_yml_id}
on chat:
set {_input} to message
if getProp(player,{@key_input_status}) is {@value_input_status_wait}:
set {_ans} to answerCheck({_input})
if {_ans} is {@answer_q}:
send "登録をキャンセルしました" to player
clearPlayerStatus(player)
else:
setProp(player,{@key_input_status},{@value_input_status_inputed})
setProp(player,{@key_input_name},{_input})
openGui2(player)
cancel event
command /tpinit:
trigger:
playerInit(player)
on join:
playerInit(player)
on quit:
playerQuit(player)
local function playerInit(p:player):
set {_name} to name of {_p}
set {_yml_id} to "%{@yml_dir}%%{_name}%.yml"
load yaml {_yml_id} as {_yml_id}
clear {prop::%{_uuid}%::*}
setProp({_p},{@key_yml_id},{_yml_id})
clearPlayerStatus({_p})
debugLog({_p},"use debugLog")
local function clearPlayerStatus(p:player):
setProp({_p},{@key_input_status},{@value_input_status_none})
setProp({_p},{@key_input_name},"")
setProp({_p},{@key_is_tp_close},false)
setProp({_p},{@key_is_tp_start},false)
setProp({_p},{@key_tp_max_page},0)
setProp({_p},{@key_tp_count},0)
setProp({_p},{@key_tp_page},0)
setProp({_p},{@key_tp_is_delete},false)
setProp({_p},{@key_lc_select_no},-1)
local function playerQuit(p:player):
set {_uuid} to uuid of {_p}
delete {prop::%{_uuid}%::*}
set {_yml_id} to getProp({_p},{@key_yml_id})
unload yaml {_yml_id}
local function setProp(p:player,key:object,val:object):
debugLog({_p}, "setProp %{_key}% %{_val}%" )
set {_uuid} to uuid of {_p}
set {prop::%{_uuid}%::%{_key}%} to {_val}
local function getProp(p:player,key:string)::object:
set {_uuid} to uuid of {_p}
return {prop::%{_uuid}%::%{_key}%}
local function getListProp(p:player,key:string)::objects:
set {_uuid} to uuid of {_p}
return {prop::%{_uuid}%::%{_key}%::*}
local function deleteListProp(p:player,key:string):
set {_uuid} to uuid of {_p}
delete {prop::%{_uuid}%::%{_key}%::*}
local function answerCheck(inp: string) :: string:
switch {_inp} in lowercase:
case {@answer_y}:
return {@answer_y}
case {@answer_n}:
return {@answer_n}
case {@answer_q}:
return {@answer_q}
default:
return {@answer_any}
command /tppropcheck:
trigger:
set {_uuid} to uuid of player
loop {prop::%{_uuid}%::*}:
send "prop %loop-index% : %loop-value%" to player
local function debugLog(p:player,mes:text):
if {DEBUG} is true:
send "--debug %{_mes}%" to {_p}
終わりに
- 英語の文法で書くというところが慣れないと難しい
- 簡単に書けるようになっている分javaなどに比べ機能が削られているのでそれはそれで難しい
- 本格的なプラグインはjavaで書く必要があるため、気軽に作れるのはいいところ