قواعد الإضافات
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: المفتاح
يُرسَل كما هو إلى العميل، وهو يبقى صالحًا عبر الإصدارات.
ما لا تسمح به القواعد
إنها مغلقة، ويجب قول هذا قبل أي شيء آخر. كل ما يرد في الكتالوج ممكن، وما عداه مستحيل، وسيبقى كذلك: لا توجد أي وسيلة لتنفيذ شيفرة 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،
وعندها يرفض المحرّك ما لا يفهمه بدلًا من تنفيذه بشكل خاطئ، ويُعلن ذلك في وحدة
تحكم الخادم.
تحمل الروابط أعلاه هذا الرقم: سيبقى plugin-1.schema.json
يشير دائمًا إلى هذه القواعد بعينها، حتى يوم يوجد فيه إصدار آخر.