Plugin-grammatiken
specVersion 1
Ett NimBlock-plugin är inte kod: det är en JSON-fil som beskriver regler, läst och körd av en Paper-motor skriven en gång för alla. Den här sidan säger exakt vad man får skriva i den, och inget annat är möjligt.
En regel, och bara en regel
Hela grammatiken ryms i en enda mening: när detta händer, om dessa villkor gäller, gör då detta. Ett plugin är en lista med regler, en regel är en utlösare, noll till 20 villkor och en till 50 åtgärder.
Ingen Java genereras någonstans, och inget kompileras: servern laddar en motor som läser specen. Det är den egenskap som spelar roll längre fram, för en ny Minecraft-version betalas bara en gång, i den motorn, och inte i varje plugin som skrivits med den.
Hur en spec ser ut
{
"specVersion": 1,
"name": "Välkommen",
"rules": [
{
"id": "valkommen",
"name": "Välkomstmeddelande",
"match": "all",
"trigger": { "type": "player.join" },
"conditions": [
{ "type": "player.has_permission", "params": { "permission": "nimblock.vip" } }
],
"actions": [
{ "type": "player.send_message", "params": { "message": "&6Välkommen {player}!" } }
]
}
]
}
match är antingen all (standard) eller any,
och ett villkor kan ha "not": true. enabled: false behåller
en regel i filen utan att aktivera den.
Grammatiken, i EBNF
Terminallistorna nedan genereras från katalogen: det är bokstavligen de identifierare som motorn kan köra, inte en handskriven avskrift.
<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"
Två saker syns inte i dessa produktionsregler, eftersom de handlar om samstämmighet
och inte om form. Ett villkor eller en åtgärd som kräver en spelare kan bara skrivas
under en utlösare som tillhandahåller en: ”läka spelaren” är meningslöst under ”var
N:e sekund”. Och cancel_event kan bara skrivas under en avbrytbar
utlösare. Båda avvisas vid skrivandet, inte vid körning.
Mallar
Parametrar av typen text är mallar: {player}
ersätts där med det som utlösaren tillhandahöll, {{ ger en
bokstavlig klammerparentes, och färgkoderna &a &l
översätts.
En variabel som utlösaren inte känner till är ett fel, inte text. Varje utlösare deklarerar vad den tillhandahåller (se listan nedan): utanför den listan finns inget att läsa, och ett skrivfel avvisas när man skriver istället för att hamna i spelarnas chatt. En utlösares egna parametrar är däremot inte mallar: de läses när servern startar, innan det finns något alls att ersätta.
Minecraft-namn skrivs på två sätt beroende på vad de anger. Ett föremål
namnges i båda stavningarna (DIAMOND_SWORD eller diamond_sword).
Ett ljud eller en effekt namnges med sin
Minecraft-nyckel (entity.player.levelup, speed) och inte
med Bukkit-konstanten: nyckeln skickas oförändrad till klienten och den överlever
versionsbyten.
Vad grammatiken inte tillåter
Den är stängd, och det ska sägas först. Det som finns i katalogen går att göra, resten är omöjligt och kommer att förbli det: det finns inget sätt att köra godtycklig Java. Det är det som gör studion omedelbar (inget att kompilera) och en spec ofarlig att installera, oavsett varifrån den kommer.
Villkor kan inte nästlas: det är en medveten begränsning i version 1,
ett booleskt träd låter sig inte rita fint i block, och all / any
/ not räcker nästan alltid. Den enda utvägen är åtgärden ”kör ett
kommando som konsolen”, som på just den servern har exakt konsolens makt, varken mer
eller mindre.
Utlösare
Det som utlöser en regel, och de variabler som var och en tillhandahåller till mallarna.
player.join
En spelare går med
I samma stund som spelaren kommer in på servern, efter att deras värld har laddats.
ger:
{player} Spelaren som går med,
{world} Världen spelaren kommer till
player.quit
En spelare lämnar
I samma stund som spelaren lämnar servern. Spelaren går fortfarande att nå, men alla meddelanden som skickas går förlorade.
ger:
{player} Spelaren som lämnar,
{world} Världen spelaren lämnar
player.chat
En spelare skriver i chatten
avbrytbar
Innan meddelandet skickas till de andra spelarna. Kan avbrytas: ingen ser då meddelandet.
ger:
{player} Den som skrev meddelandet,
{world} Spelarens värld,
{message} Meddelandet som skrevs
player.death
En spelare dör
Vid spelarens död, innan återuppståndelseskärmen.
ger:
{player} Spelaren som dog,
{world} Spelarens värld,
{killer} Spelaren som var ansvarig, tom om det inte finns någon
player.respawn
En spelare återuppstår
När spelaren kommer tillbaka in i spelet efter döden.
ger:
{player} Spelaren som återuppstår,
{world} Världen spelaren återuppstår i
block.break
En spelare bryter ett block
avbrytbar
Innan blocket försvinner. Kan avbrytas: blocket blir kvar.
ger:
{player} Spelaren som bryter,
{world} Spelarens värld,
{block} Blocktypen (DIAMOND_ORE),
{x} Blockets X-koordinat,
{y} Blockets höjd,
{z} Blockets Z-koordinat
block.place
En spelare placerar ett block
avbrytbar
Innan blocket placeras. Kan avbrytas: blocket placeras inte.
ger:
{player} Spelaren som placerar,
{world} Spelarens värld,
{block} Blocktypen som placeras,
{x} Blockets X-koordinat,
{y} Blockets höjd,
{z} Blockets Z-koordinat
command
En spelare skriver ett kommando
Skapar ett nytt kommando på servern. ⚠️ Ett kommando vars namn inte fanns sedan tidigare visas först efter en omstart av servern: Paper accepterar bara nya kommandonamn vid uppstart. Regelns innehåll däremot laddas om utan omstart.
-
nametext Kommandots namn, utan snedstrecket -
descriptiontext valfritt Beskrivning som visas i hjälpen -
permissiontext valfritt Behörighet som krävs, tom för alla
ger:
{player} Spelaren som skrev kommandot,
{world} Spelarens värld,
{args} Det som följer efter kommandot, oförändrat
schedule.repeat
Var N:e sekund
Upprepar regeln så länge servern körs. Ingen spelare är inblandad: bara åtgärder som inte behöver någon kan användas.
-
secondsinteger [1..86400] Intervall i sekunder
Villkor
Det som filtrerar. Noll till 20 per regel, kombinerade med match.
player.has_permission
Spelaren har behörigheten
kräver en spelare
-
permissiontext Behörighet
player.is_op
Spelaren är operatör
kräver en spelare
player.in_world
Spelaren är i världen
kräver en spelare
-
worldtext Världens namn
player.health_below
Spelaren har färre än X hjärtan
kräver en spelare
I hälsopoäng: 20 poäng motsvarar 10 hjärtan.
-
valuenumber [0..1024] Hälsopoäng
text.contains
En text innehåller
-
texttext Texten som undersöks -
searchtext Det som söks efter -
ignoreCaseboolean valfritt, standard:trueIgnorera skiftläge
text.equals
En text är lika med
-
texttext Texten som undersöks -
valuetext Förväntat värde -
ignoreCaseboolean valfritt, standard:trueIgnorera skiftläge
number.compare
Jämför två tal
Båda sidorna är mallar: {y} jämförs med 62 utan att skriva någon kod.
-
lefttext Vänster sida -
operator< | <= | == | != | >= | > Jämförelse -
righttext Höger sida
chance
Slumpmässigt, X % av tiden
-
percentnumber [0..100] Procent
Åtgärder
Det som händer. En till 50 per regel, körda i ordning.
player.send_message
Skicka ett meddelande till spelaren
kräver en spelare
-
messagetext Meddelande
player.send_actionbar
Visa ett meddelande ovanför åtgärdsfältet
kräver en spelare
-
messagetext Meddelande
player.send_title
Visa en titel i stor text
kräver en spelare
-
titletext Titel -
subtitletext valfritt Undertitel -
fadeInnumber [0..60] valfritt, standard:0.5Intoning i sekunder -
staynumber [0..600] valfritt, standard:3Varaktighet i sekunder -
fadeOutnumber [0..60] valfritt, standard:0.5Uttoning i sekunder
player.play_sound
Spela ett ljud för spelaren
kräver en spelare
-
soundidentifier Ljud, som en Minecraft-nyckel (entity.player.levelup) -
volumenumber [0..10] valfritt, standard:1Volym -
pitchnumber [0.5..2] valfritt, standard:1Tonhöjd
player.give_item
Ge spelaren ett föremål
kräver en spelare
Det som inte får plats i inventariet hamnar på marken vid spelarens fötter.
-
materialidentifier Föremål (DIAMOND_SWORD) -
amountinteger [1..2304] valfritt, standard:1Antal
player.give_effect
Ge spelaren en effekt
kräver en spelare
-
effectidentifier Effekt, som en Minecraft-nyckel (night_vision) -
secondsinteger [1..86400] valfritt, standard:10Varaktighet i sekunder -
amplifierinteger [0..255] valfritt, standard:0Nivå, från 0
player.heal
Hela spelaren
kräver en spelare
Full hälsa, mätt hunger.
player.teleport
Teleportera spelaren
kräver en spelare
-
xnumber [-30000000..30000000] X-koordinat -
ynumber [-512..1024] Höjd -
znumber [-30000000..30000000] Z-koordinat -
worldtext valfritt Värld, tom för spelarens egen
player.kick
Sparka ut spelaren
kräver en spelare
-
reasontext Anledning som visas
broadcast
Meddela hela servern
-
messagetext Meddelande
server.run_command
Kör ett kommando som konsolen
Grammatikens nödutgång: allt som konsolen kan göra. Det är konsolens fulla makt över den här servern, så skriv det med vetskap om vad du gör.
-
commandtext Kommando, utan snedstrecket
cancel_event
Avbryt det som just hände
kräver en avbrytbar utlösare
Finns bara på avbrytbara utlösare: meddelandet skickas inte, blocket bryts inte.
log
Skriv till serverns konsol
-
messagetext Meddelande -
levelinfo | warn valfritt, standard:infoNivå
delay
Vänta
Pausar resten av regelns åtgärder. En regel som väntar kan inte längre avbryta något: "Avbryt" måste komma före.
-
secondsnumber [0.05..3600] Sekunder
De publicerade filerna
JSON Schema genereras från katalogen vid varje driftsättning, och det är den maskinläsbara referensen: en textredigerare som följer det kompletterar identifierare och avvisar okända parametrar.
- plugin-1.schema.json : schemat, i JSON Schema draft 2020-12.
- catalogue-1.json : själva katalogen, källan som allt annat härleds från (validatorn, schemat, editorns block och Java-motorn).
Katalogen har etiketterna på franska, som är källspråket. Identifierarna däremot översätts inte: det är de som motorn kör.
Vad som händer när grammatiken ändras
Att lägga till en utlösare, ett villkor eller en åtgärd förstör inget: en spec
skriven innan fortsätter att fungera. En brytande ändring ökar
däremot specVersion, och motorn vägrar då det den inte förstår istället
för att köra det fel, och säger det i serverns konsol.
Webbadresserna ovan bär det här numret: plugin-1.schema.json
kommer alltid att peka på just den här grammatiken, även den dag det finns en annan.