प्लगिन का व्याकरण
specVersion 1
NimBlock प्लगिन कोड नहीं है: यह एक JSON फ़ाइल है जो नियमों का वर्णन करती है, जिसे एक बार सबके लिए लिखे गए Paper इंजन द्वारा पढ़ा और चलाया जाता है। यह पेज बिल्कुल यही बताता है कि इसमें क्या लिखने की अनुमति है, और इसके अलावा कुछ भी संभव नहीं है।
एक नियम, बस एक नियम
पूरा व्याकरण एक वाक्य में समा जाता है: जब यह होता है, अगर ये कंडीशन पूरी होती हैं, तो यह करो। एक प्लगिन नियमों की एक सूची है, एक नियम में एक ट्रिगर, शून्य से 20 तक कंडीशन और एक से 50 तक एक्शन होते हैं।
कहीं भी कोई Java जनरेट नहीं होता, न ही कोई कंपाइलेशन होता है: सर्वर एक ऐसा इंजन लोड करता है जो स्पेक को पढ़ता है। यही वह खासियत है जो आगे मायने रखती है, क्योंकि Minecraft के नए वर्शन की कीमत सिर्फ़ एक बार इस इंजन में चुकानी पड़ती है, न कि उससे बने हर प्लगिन में अलग से।
एक स्पेक कैसी दिखती है
{
"specVersion": 1,
"name": "स्वागत",
"rules": [
{
"id": "स्वागत",
"name": "स्वागत संदेश",
"match": "all",
"trigger": { "type": "player.join" },
"conditions": [
{ "type": "player.has_permission", "params": { "permission": "nimblock.vip" } }
],
"actions": [
{ "type": "player.send_message", "params": { "message": "&6स्वागत है, {player}!" } }
]
}
]
}
match की वैल्यू all (डिफ़ॉल्ट) या any होती
है, और किसी कंडीशन पर "not": true भी लगाया जा सकता है।
enabled: false किसी नियम को फ़ाइल में रखता है लेकिन उसे चालू नहीं
करता।
व्याकरण, EBNF में
नीचे दी गई टर्मिनल सूचियाँ कैटलॉग से जनरेट की जाती हैं: ये वही आइडेंटिफ़ायर हैं जिन्हें इंजन चला सकता है, कोई हाथ से लिखा गया अनुवाद नहीं।
<plugin> ::= "{" "specVersion" ":" 1 ","
[ "id" ":" <slug64> "," ]
"name" ":" <label> ","
[ "description" ":" <text500> "," ]
"rules" ":" "[" <rule> { "," <rule> } "]" "}" (* 1..200 *)
<rule> ::= "{" [ "id" ":" <slug32> "," ]
"name" ":" <label> ","
[ "enabled" ":" <boolean> "," ] (* = true *)
[ "match" ":" ( "all" | "any" ) "," ] (* = "all" *)
"trigger" ":" <trigger> ","
[ "conditions" ":" "[" [ <condition>
{ "," <condition> } ] "]" "," ] (* 0..20 *)
"actions" ":" "[" <action>
{ "," <action> } "]" "}" (* 1..50 *)
<trigger> ::= "{" "type" ":" <trigger-id> [ "," "params" ":" <params> ] "}"
<condition> ::= "{" "type" ":" <condition-id> [ "," "not" ":" <boolean> ]
[ "," "params" ":" <params> ] "}"
<action> ::= "{" "type" ":" <action-id> [ "," "params" ":" <params> ] "}"
<params> ::= "{" [ <param-name> ":" <param-value>
{ "," <param-name> ":" <param-value> } ] "}"
<param-value> ::= <template> | <identifier> | <integer> | <number>
| <boolean> | <choice>
<template> ::= { <literal> | <variable> | <colour> } (* 0..2000 *)
<literal> ::= /[^{}]/ | "{{" | "}}"
<variable> ::= "{" /[a-zA-Z][a-zA-Z0-9_]*/ "}"
<colour> ::= "&" ( "0".."9" | "a".."f" | "k".."o" | "r" )
<identifier> ::= /[A-Za-z][A-Za-z0-9_:.\/-]{0,63}/
<slug32> ::= /[A-Za-z0-9_-]{1,32}/
<slug64> ::= /.{0,64}/
<label> ::= /.{1,64}/
<text500> ::= /.{0,500}/
<trigger-id> ::= "player.join"
| "player.quit"
| "player.chat"
| "player.death"
| "player.respawn"
| "block.break"
| "block.place"
| "command"
| "schedule.repeat"
<condition-id> ::= "player.has_permission"
| "player.is_op"
| "player.in_world"
| "player.health_below"
| "text.contains"
| "text.equals"
| "number.compare"
| "chance"
<action-id> ::= "player.send_message"
| "player.send_actionbar"
| "player.send_title"
| "player.play_sound"
| "player.give_item"
| "player.give_effect"
| "player.heal"
| "player.teleport"
| "player.kick"
| "broadcast"
| "server.run_command"
| "cancel_event"
| "log"
| "delay"
इन प्रोडक्शन नियमों में दो चीज़ें नहीं दिखतीं, क्योंकि उनका संबंध रूप से नहीं बल्कि
तालमेल (कोहेरेंस) से है। जिस कंडीशन या एक्शन को किसी प्लेयर की ज़रूरत होती है, वह
सिर्फ़ ऐसे ट्रिगर के नीचे लिखी जा सकती है जो कोई प्लेयर देता हो: "खिलाड़ी को ठीक
करो" का "हर N सेकंड में" के नीचे कोई मतलब नहीं बनता। और
cancel_event सिर्फ़ किसी रद्द किए जा सकने वाले ट्रिगर के नीचे ही लिखा
जा सकता है। दोनों को लिखते समय ही अस्वीकार कर दिया जाता है, चलाते समय नहीं।
टेम्पलेट्स
text टाइप के पैरामीटर टेम्पलेट होते हैं:
{player} की जगह वह वैल्यू आ जाती है जो ट्रिगर ने दी थी,
{{ एक लिटरल ब्रेस देता है, और कलर कोड
&a &l ट्रांसलेट हो जाते हैं।
जिस वैरिएबल के बारे में ट्रिगर को कुछ पता नहीं, वह टेक्स्ट नहीं बल्कि एरर है। हर ट्रिगर बताता है कि वह क्या देता है (नीचे दी गई सूची देखें): इस सूची से बाहर पढ़ने के लिए कुछ नहीं है, और टाइपिंग की गलती को लिखते समय ही अस्वीकार कर दिया जाता है, ताकि वह खिलाड़ियों के चैट में न पहुँच जाए। किसी ट्रिगर के अपने पैरामीटर टेम्पलेट नहीं होते: वे सर्वर के शुरू होते समय पढ़े जाते हैं, उससे पहले कि कुछ भी बदलने के लिए मौजूद हो।
Minecraft के नाम दो तरीकों से लिखे जाते हैं, यह इस पर निर्भर करता है कि वे किसे
दर्शाते हैं। किसी आइटम को दोनों वर्तनी में लिखा जा सकता है
(DIAMOND_SWORD या diamond_sword)। किसी
साउंड या इफ़ेक्ट को उसकी Minecraft की-की
(entity.player.levelup, speed) से लिखा जाता है, Bukkit
के कॉन्स्टेंट से नहीं: यह की क्लाइंट तक ज्यों की त्यों पहुँचती है, और वर्शन बदलने
पर भी बनी रहती है।
व्याकरण जो अनुमति नहीं देता
यह बंद (closed) है, और यह बाकी सब से पहले कह देना ज़रूरी है। कैटलॉग में जो है वह किया जा सकता है, बाकी सब नामुमकिन है और नामुमकिन ही रहेगा: मनमाना Java चलाने का कोई तरीका नहीं है। यही वजह है कि स्टूडियो तुरंत काम करता है (कुछ भी कंपाइल नहीं करना पड़ता) और कोई भी स्पेक, चाहे वह कहीं से भी आई हो, इंस्टॉल करने में हानिरहित है।
कंडीशन नेस्ट नहीं होतीं: यह वर्शन 1 की जान-बूझकर रखी गई सीमा है,
कोई बूलियन ट्री ब्लॉक्स में ठीक से नहीं बनता, और all / any
/ not लगभग हमेशा काफ़ी होते हैं। इससे बाहर निकलने का एक ही रास्ता है:
"कंसोल के तौर पर एक कमांड चलाओ" एक्शन, जिसके पास उस सर्वर पर ठीक उतनी ही ताक़त होती
है जितनी कंसोल के पास होती है, न कम न ज़्यादा।
ट्रिगर
जो किसी नियम को शुरू करता है, और हर ट्रिगर टेम्पलेट के लिए जो वैरिएबल देता है।
player.join
एक खिलाड़ी जुड़ता है
जिस पल खिलाड़ी सर्वर पर आता है, उसकी वर्ल्ड लोड होने के बाद।
देता है:
{player} जुड़ने वाला खिलाड़ी,
{world} जिस वर्ल्ड में वह आता है
player.quit
एक खिलाड़ी छोड़ता है
जिस पल खिलाड़ी सर्वर छोड़ता है। वह अब भी पहुँच में है, लेकिन भेजा गया कोई भी मैसेज खो जाता है।
देता है:
{player} जाने वाला खिलाड़ी,
{world} जो वर्ल्ड वह छोड़ता है
player.chat
एक खिलाड़ी चैट में लिखता है
रद्द करने योग्य
मैसेज बाकी खिलाड़ियों तक पहुँचने से पहले। रद्द किया जा सकता है: ऐसे में मैसेज किसी को नहीं दिखता।
देता है:
{player} मैसेज लिखने वाला,
{world} उसकी वर्ल्ड,
{message} लिखा गया मैसेज
player.death
एक खिलाड़ी मरता है
खिलाड़ी की मौत पर, रीस्पॉन स्क्रीन आने से पहले।
देता है:
{player} मरने वाला खिलाड़ी,
{world} उसकी वर्ल्ड,
{killer} जिम्मेदार खिलाड़ी, अगर कोई नहीं है तो खाली
player.respawn
एक खिलाड़ी रीस्पॉन होता है
जब खिलाड़ी मरने के बाद फिर से गेम में लौटता है।
देता है:
{player} रीस्पॉन होने वाला खिलाड़ी,
{world} रीस्पॉन वाली वर्ल्ड
block.break
एक खिलाड़ी ब्लॉक तोड़ता है
रद्द करने योग्य
ब्लॉक गायब होने से पहले। रद्द किया जा सकता है: ब्लॉक अपनी जगह बना रहता है।
देता है:
{player} तोड़ने वाला खिलाड़ी,
{world} उसकी वर्ल्ड,
{block} ब्लॉक का प्रकार (DIAMOND_ORE),
{x} ब्लॉक का X निर्देशांक,
{y} ब्लॉक की ऊँचाई,
{z} ब्लॉक का Z निर्देशांक
block.place
एक खिलाड़ी ब्लॉक रखता है
रद्द करने योग्य
ब्लॉक रखे जाने से पहले। रद्द किया जा सकता है: ब्लॉक नहीं रखा जाता।
देता है:
{player} रखने वाला खिलाड़ी,
{world} उसकी वर्ल्ड,
{block} रखे गए ब्लॉक का प्रकार,
{x} ब्लॉक का X निर्देशांक,
{y} ब्लॉक की ऊँचाई,
{z} ब्लॉक का Z निर्देशांक
command
एक खिलाड़ी कमांड टाइप करता है
सर्वर पर एक नई कमांड बनाता है। ⚠️ जिस कमांड का नाम पहले मौजूद नहीं था, वह सर्वर के दोबारा शुरू होने पर ही दिखती है: Paper नए कमांड नाम केवल स्टार्टअप पर स्वीकार करता है। रूल का कंटेंट, इसके उलट, बिना रीस्टार्ट के रीलोड हो जाता है।
-
nametext कमांड का नाम, स्लैश के बिना -
descriptiontext वैकल्पिक हेल्प में दिखने वाला विवरण -
permissiontext वैकल्पिक जरूरी परमिशन, सबके लिए खाली
देता है:
{player} कमांड टाइप करने वाला खिलाड़ी,
{world} उसकी वर्ल्ड,
{args} कमांड के बाद जो लिखा गया, ज्यों का त्यों
schedule.repeat
हर N सेकंड में
जब तक सर्वर चलता है, रूल को दोहराता है। कोई खिलाड़ी शामिल नहीं होता: सिर्फ वे ऐक्शन इस्तेमाल हो सकते हैं जिन्हें खिलाड़ी की जरूरत नहीं।
-
secondsinteger [1..86400] अंतराल, सेकंड में
कंडीशन
जो फ़िल्टर करता है। हर नियम में शून्य से 20 तक, जिन्हें match से जोड़ा जाता है।
player.has_permission
खिलाड़ी के पास परमिशन है
खिलाड़ी चाहिए
-
permissiontext परमिशन
player.is_op
खिलाड़ी ऑपरेटर है
खिलाड़ी चाहिए
player.in_world
खिलाड़ी इस वर्ल्ड में है
खिलाड़ी चाहिए
-
worldtext वर्ल्ड का नाम
player.health_below
खिलाड़ी के पास X दिलों से कम हैं
खिलाड़ी चाहिए
हेल्थ पॉइंट में: 20 पॉइंट 10 दिलों के बराबर होते हैं।
-
valuenumber [0..1024] हेल्थ पॉइंट
text.contains
एक टेक्स्ट में शामिल है
-
texttext जाँचा गया टेक्स्ट -
searchtext जो उसमें खोजा जा रहा है -
ignoreCaseboolean वैकल्पिक, डिफ़ॉल्ट:trueबड़े-छोटे अक्षरों को अनदेखा करें
text.equals
एक टेक्स्ट बराबर है
-
texttext जाँचा गया टेक्स्ट -
valuetext अपेक्षित वैल्यू -
ignoreCaseboolean वैकल्पिक, डिफ़ॉल्ट:trueबड़े-छोटे अक्षरों को अनदेखा करें
number.compare
दो नंबरों की तुलना करें
दोनों पक्ष टेम्पलेट होते हैं: {y} की तुलना 62 से बिना कोई कोड लिखे की जा सकती है।
-
lefttext बायाँ पक्ष -
operator< | <= | == | != | >= | > तुलना -
righttext दायाँ पक्ष
chance
रैंडम रूप से, X% समय
-
percentnumber [0..100] प्रतिशत
एक्शन
जो होता है। हर नियम में एक से 50 तक, जो क्रम में चलाए जाते हैं।
player.send_message
खिलाड़ी को मैसेज भेजें
खिलाड़ी चाहिए
-
messagetext मैसेज
player.send_actionbar
ऐक्शन बार के ऊपर मैसेज दिखाएँ
खिलाड़ी चाहिए
-
messagetext मैसेज
player.send_title
बड़ा टाइटल दिखाएँ
खिलाड़ी चाहिए
-
titletext टाइटल -
subtitletext वैकल्पिक सबटाइटल -
fadeInnumber [0..60] वैकल्पिक, डिफ़ॉल्ट:0.5दिखने में लगने वाला समय, सेकंड में -
staynumber [0..600] वैकल्पिक, डिफ़ॉल्ट:3समय, सेकंड में -
fadeOutnumber [0..60] वैकल्पिक, डिफ़ॉल्ट:0.5गायब होने में लगने वाला समय, सेकंड में
player.play_sound
खिलाड़ी के लिए साउंड बजाएँ
खिलाड़ी चाहिए
-
soundidentifier साउंड, Minecraft की-नेम में (entity.player.levelup) -
volumenumber [0..10] वैकल्पिक, डिफ़ॉल्ट:1वॉल्यूम -
pitchnumber [0.5..2] वैकल्पिक, डिफ़ॉल्ट:1पिच
player.give_item
खिलाड़ी को आइटम दें
खिलाड़ी चाहिए
जो इन्वेंट्री में नहीं समाता, वह उसके पैरों के पास ज़मीन पर गिर जाता है।
-
materialidentifier आइटम (DIAMOND_SWORD) -
amountinteger [1..2304] वैकल्पिक, डिफ़ॉल्ट:1मात्रा
player.give_effect
खिलाड़ी पर इफ़ेक्ट लगाएँ
खिलाड़ी चाहिए
-
effectidentifier इफ़ेक्ट, Minecraft की-नेम में (night_vision) -
secondsinteger [1..86400] वैकल्पिक, डिफ़ॉल्ट:10समय, सेकंड में -
amplifierinteger [0..255] वैकल्पिक, डिफ़ॉल्ट:0लेवल, 0 से शुरू
player.heal
खिलाड़ी को ठीक करें
खिलाड़ी चाहिए
हेल्थ पूरी, भूख शांत।
player.teleport
खिलाड़ी को टेलीपोर्ट करें
खिलाड़ी चाहिए
-
xnumber [-30000000..30000000] X निर्देशांक -
ynumber [-512..1024] ऊँचाई -
znumber [-30000000..30000000] Z निर्देशांक -
worldtext वैकल्पिक वर्ल्ड, खिलाड़ी की अपनी वर्ल्ड के लिए खाली
player.kick
खिलाड़ी को किक करें
खिलाड़ी चाहिए
-
reasontext दिखाई जाने वाली वजह
broadcast
पूरे सर्वर पर घोषणा करें
-
messagetext मैसेज
server.run_command
कंसोल की तरह कमांड चलाएँ
व्याकरण का आख़िरी रास्ता: जो कुछ कंसोल कर सकता है। इस सर्वर पर कंसोल की पूरी ताकत, इसलिए सोच-समझकर ही लिखें।
-
commandtext कमांड, स्लैश के बिना
cancel_event
जो अभी हुआ उसे रद्द करें
रद्द करने योग्य ट्रिगर चाहिए
यह सिर्फ उन ट्रिगर्स पर मौजूद होता है जिन्हें रद्द किया जा सकता है: मैसेज नहीं भेजा जाता, ब्लॉक नहीं टूटता।
log
सर्वर के कंसोल में लिखें
-
messagetext मैसेज -
levelinfo | warn वैकल्पिक, डिफ़ॉल्ट:infoलेवल
delay
इंतज़ार करें
रूल के बाकी ऐक्शन को रोक देता है। जो रूल इंतज़ार करता है वह अब कुछ भी रद्द नहीं कर सकता: "रद्द करें" इससे पहले आना चाहिए।
-
secondsnumber [0.05..3600] सेकंड
प्रकाशित फ़ाइलें
JSON Schema हर डिप्लॉय पर कैटलॉग से जनरेट होता है, और यह मशीन द्वारा पढ़ा जा सकने वाला संदर्भ है: जो एडिटर इसका पालन करता है, वह आइडेंटिफ़ायर ऑटो-कम्प्लीट करता है और अनजान पैरामीटर को अस्वीकार कर देता है।
- plugin-1.schema.json : स्कीमा, JSON Schema draft 2020-12।
- catalogue-1.json : ख़ुद कैटलॉग, वह सोर्स जिससे बाक़ी सब कुछ बनता है (वैलिडेटर, स्कीमा, एडिटर के ब्लॉक्स और Java इंजन)।
कैटलॉग में लेबल फ़्रेंच में हैं, क्योंकि फ़्रेंच सोर्स भाषा है। आइडेंटिफ़ायर का अनुवाद नहीं किया जाता: इंजन इन्हीं को चलाता है।
जब व्याकरण बदलता है तो क्या होता है
कोई नया ट्रिगर, कंडीशन या एक्शन जोड़ने से कुछ नहीं टूटता: पहले से लिखी गई कोई स्पेक
चलती रहती है। लेकिन कोई ब्रेकिंग चेंज specVersion को
बढ़ा देता है, और उसके बाद इंजन जो नहीं समझता उसे ग़लत तरीके से चलाने के बजाय
अस्वीकार कर देता है, और इसे सर्वर के कंसोल में बता देता है।
ऊपर दिए गए URL इसी नंबर को साथ लेकर चलते हैं:
plugin-1.schema.json हमेशा इसी व्याकरण को दर्शाएगा,
भले ही किसी दिन कोई और वर्शन मौजूद हो जाए।