Configuration
ShadowCommand
The commands your server needs and Minecraft does not have.
Where the files are
plugins/ShadowCommand/ The plugin creates them on first start and updates them by itself on new versions, keeping your changes.
Applying changes
/scmd reload [file] Reloads the files without restarting the server.
No setting found
Try another word, or search every plugin.
Settings
The files that decide how the plugin behaves. Start here.
config.yml
General settings 77 settings
config.ymlplugins/ShadowCommand/config.yml File notes
Every value in here already carries the right number for a normal server: you can drop the jar in and never open this file. When you do need to change something, the line above each key says what it does and the line below says what happens if you get it wrong.
Durations are written in plain text: 500ms, 20t (ticks), 30s, 5m, 2h30m, 7d. A bare number means seconds.
Colours are written with &: &a &c &l &r, and &#FF8800 for an exact colour. Gradients are the one exception: <gradient:#a:#b>text</gradient>.
Keys, sections and comments are English everywhere in ShadowCommand. Anything a player reads is a message, and messages live in lang/: write them in whatever language your server speaks.
config-version3Licence
ShadowCommand is a single jar with two editions inside. With no key it runs the FREE edition: the whole engine, with a ceiling on how many commands, GUIs, triggers and schedules you can have. With a signed key it runs Premium, with no ceilings.
Keep reading
The plugin never shuts itself down over a licence problem. The worst case is going back to free, and saying in console why.
You normally touch nothing here: a key is activated in game with
/scmd license claim <purchase-code> (the one you got when buying) /scmd license activate <key> (if you already hold a key)
and it is written to license.yml, which sits outside this file on purpose: a config gets sent to support, a licence does not.
license4 keysTHE KEY LIVES IN license.yml (same folder as this file), or in the central plugins/ShadowControl/licenses.yml for the whole suite. The 'key' below is only a fallback for deploy systems that generate config.yml: leave it empty.
The key does NOT live here: it lives in license.yml, in this folder, written at first start (or in plugins/ShadowControl/licenses.yml for the whole suite). Only if you would rather keep it here than in license.yml. The environment variable SHADOWCOMMAND_LICENSE and the system property -Dshadowcommand.license=... work too.
grace-days7Days of grace before falling back to free when a subscription expires or when the server address does not match the licence. A renewal has almost always already gone through, and an address that does not add up is far more often a NAT than a thief.
server-ip""The address this server is known by, if you know it. Behind a proxy the machine never sees its own domain, and this line is the only way to tell it without calling anyone.
resolve-public-iptrueOne call, at startup, to find out this server's public address: behind NAT it is the only way to recognise it. The answer comes from the activation service below (its /ip address), nobody else is asked. Turn it off and, if the licence is bound to a domain, the grace period still applies. Only when a licence is installed: a server without a key asks nobody. false = never ask.
activation-url'https://shadow-licences.riccardobotins.workers.dev'Activation service. This is the BASE address: the plugin appends /claim, /check and /ip by itself.
Empty = fully offline verification, and the key is pasted by hand. Set = /scmd license claim <code> does everything on its own, and
every three hours the server checks the licence has not been revoked.
messages3 keysdefault-locale"en_US"Language used when the player's own is not available, and by the console. Ships with en_US and it_IT. Set "it_IT" for an Italian server.
follow-client-localetrueEvery player sees their client's language, if the file exists.
auto-updatetrueOn update, your files (this one, lang/ and the other files the plugin created) are moved to the new version automatically: new settings and corrected messages arrive, and everything you had changed or added is carried over. The old files are kept in backup/<date>/; if something needs your attention you will find PORTING-<version>.txt in the plugin folder, saying what to fix. Set false to have nothing touched: new settings and messages are then read from the plugin itself.
storage6 keystable-prefix"sc_"Table prefix. Change it only if you share a database with something else.
fallback-to-localtrueIf MySQL does not answer, start on the local file instead of blocking startup.
flush-interval"30s"How often dirty variables and cooldowns are written to disk.
storage.mysql7 keysenabledfalseSet true and fill in the fields below: the plugin creates the tables itself.
host"127.0.0.1"port3306database"shadowcommand"username"root"password""parameters"useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=utf8"storage.pool5 keysmaximum-pool-size10minimum-idle2connection-timeout-ms10000idle-timeout-ms600000max-lifetime-ms1800000storage.h21 keyauto-serverfalseLets another program open the local file while the server is running. You only need it to look inside with an external tool.
engine5 keyscase-insensitive-comparetrue"YES" and "yes" are the same in comparisons. Set false to tell them apart.
strict-by-defaultfalsetrue = the first error always stops the script. Out of the box a failing action is reported and the script carries on.
default-cooldown0Cooldown applied to commands that do not declare one. 0 = none.
deny-sound"block.note_block.bass"Sound played when a requirement denies. Empty for silence.
engine.limits6 keysmax-steps10000Maximum instructions per run: stops a loop that never ends.
max-depth16Maximum call depth: stops two functions that call each other.
max-sessions-per-player8Concurrent scripts per player.
max-wait"10m"Longest a wait: may last. A longer wait is cut down to this.
max-command-chain8A command that runs a command that runs a command...
action-warn-millis25Past this threshold a single action is logged as slow, and past four times it a stretch of script that held the thread without ever letting go is logged too. Waiting does not count: a script that says "wait: 1s" stands still for a second holding nothing, which is the whole reason wait exists.
commands5 keysauto-permissiontrueGenerate shadowcommand.command.<name> for commands with no permission written.
auto-permission-defaulttrueWho may use a command that declares no permission: true | op | false
register-permissionstrueRegister generated permissions with the server, so LuckPerms completes them.
unknown-argument-shows-usagetrueA wrong argument shows the full usage line.
security7 keyssecurity.op-action3 keysThe op: action runs a command from the console, with the player's placeholders already filled in: write the player into the command when it needs one (op: gamemode creative {player}). It never makes anybody an operator. Until 1.0.3 it did, for one line: files that use op: are listed in console at startup. It also stops when console-action.allowed below is false.
enabledtrueallowed-commands[ ]Empty = any command. Otherwise only the ones listed.
logtrueEvery use is logged in console with who, what and from which command.
security.bypass-action1 keyThe bypass: action runs a command granting a permission for an instant.
enabledtruesecurity.console-action1 keyThe console: action (and op:, which runs from the console too).
allowedtruesecurityforbidden-commands[stop, op, deop, reload]Commands no script may run, by any means. This is the emergency brake: a badly written file must not be able to shut the server down.
security.block-actions2 keysThe actions that change blocks: fill, cylinder, move, paste...
Out of the box they do NOT ask for a permission, and that is not an oversight: the normal use is a command of yours that opens a gate for any player at all, and demanding that player hold a builder permission would break every command of that kind. Turn it on when your staff may write commands but must not be able to rewrite the map.
require-permissionfalsepermission"shadowcommand.blocks"securitymax-broadcast-per-second10Protection against an announcement repeated by mistake every tick.
escape-player-inputtrueAnything a player writes - the name of the item in hand, the text on a sign, the arguments of a command - is defused before it goes into a message: a "<click:run_command:...>" typed into a nickname stays written, it does not become a button. Leave this on.
Block editing
A floor is not one block, it is forty thousand. The fill, cylinder, sphere, move and paste actions do not place them all at once: they do a few thousand per tick until they are done, so a town square goes up in two seconds without the server missing a beat.
Keep reading
Whatever fits in one chunk and inside a tick's budget happens RIGHT AWAY: a gate opens in the same instant, not on the next tick.
blocks5 keysmax-blocks500000The parachute against one zero too many. A fill written wrong rewrites half a world, and this line is the difference between a mistake and a disaster.
per-tick4000How many blocks per tick at most. Higher = faster and heavier.
undo-history5How many operations can be taken back with the undo action, per player. 0 turns undo off and keeps nothing in memory.
undo-max-blocks50000An operation bigger than this is not recorded for undo: holding half a million blocks in memory for a "maybe" is not worth it.
physicsfalseWhether the world should react while it fills (sand falling, torches popping off). Off is much faster and is almost always what you want: for a single gate or door use physics:true in the action itself.
triggers3 keysenabledtrueTurns every trigger off at once, without deleting the files.
move-check-interval4Ticks between two player-move-block checks.
region-check-interval10Ticks between two region checks, for region-enter and region-exit.
variables3 keyscache-offline-minutes10How long the variables of someone who just left stay in memory.
purge-after-days0Delete the variables of anyone who has not joined in this many days. 0 = never.
expire-check-interval"1h"How often expired ones are cleaned up.
logging4 keyslogging.audit2 keysenabledfalseRecord every run in the database: who, what, with what outcome.
keep-days30How many days to keep the rows.
loggingdebugfalseVery verbose log, useful only while hunting a problem.
log-slow-actionstrueReport actions slower than the threshold in engine.limits.
log-sqlfalseWrite every query to the log. Only to understand a database problem.
compatibility3 keysdollar-placeholderstrueAccept $player, $arg1, $world like MyCommand.
section-color-codestrueAccept the section character as well as &.
mycommand-typestrueAccept type: RUN_COMMAND, runcmd:, permission-required and the like.
integrations4 keyssuitetrueHook into ShadowEssentials and the other plugins in the suite.
vaulttrueEconomy and permissions through Vault.
placeholderapitrueReading and providing %...% placeholders.
regionstrueShadowRegions or WorldGuard, for the triggers and the region: requirement.
bStats - anonymous usage statistics (https://bstats.org)
What is sent, every 30 minutes: that this server runs ShadowCommand, the plugin, Minecraft/Paper and Java versions, the operating system, the number of CPU cores, how many players are online (a number, nothing else), a random serverUuid that identifies nothing, and the edition in use (free, premium or creator). No player data leaves the server: no names, no UUIDs, no addresses, and nothing from your commands, menus or scripts.
metrics1 keyenabledtruefalse switches it off for this plugin only. enabled: false in plugins/bStats/config.yml switches it off for every plugin on this server at once.
overrides.yml
Command overrides 4 settings
overrides.ymlplugins/ShadowCommand/overrides.yml File notes
This is where you decide what to do with other plugins' commands: block them, replace them with a script of your own, or give them a shorter name.
The check happens on what the player types, not on the command tree, so it works with plugins that register after us and it catches the aliases too (/pl as well as /plugins).
The permission shadowcommand.bypass.block skips every block.
config-version2Blocked commands
blockNothing is blocked out of the box: these are examples, and they work the moment you remove the comment marks.
- command: "plugins" aliases-too: true # blocks /pl and /? as well bypass-permission: "server.seeplugins" message: "&7This command is &cnot available&7."
- command: "me" message: "&7This command is &cturned off&7 on this server."
- command: "minecraft:tell" aliases-too: false
Commands replaced by a script of ours
intercept- command: "spawn"
cancel-original: true # false = runs the original as well
run:
- "message: &7Taking you to spawn..."
- "wait: 2s"
- "teleport: spawn"
- "sound: entity.enderman.teleport"Aliases: type one thing, run another
aliasThe name on the left becomes what is on the right, arguments included. gm0: "gamemode survival" gm1: "gamemode creative" w: "msg"
panel.yml
The panel 8 settings
panel.ymlplugins/ShadowCommand/panel.yml File notes
How the panel that /scmd opens looks.
Everything in here already has a fallback in the code: deleting a key breaks nothing, it just goes back to the built-in value.
Slots are counted from 0. The panel is five rows tall (0-44).
Keys are English. THE WORDS: a title or a name written "lang:<key>" comes from the language files in lang/, under admin.gui.panel, so every admin reads it in their own language. Write a text instead - for example name: "&e&lMy commands" - and that text is shown as it is, to everybody: what you write here wins over the language file. The same goes for a lore you add yourself.
config-version2panel15 keystitle"lang:admin.gui.panel.title"title:
rows, filler, menus, triggers, schedules, variables, stats, errors, vocabulary, close, import, infothe eight doors
panel.commands3 keysslot10materialCOMMAND_BLOCKname"lang:admin.gui.panel.commands"lore: [ ... ] # write one and it replaces the automatic lore
the bottom row
panel.reload3 keysClose sits in the middle of the last row, where it sits in every other window of the plugin - the example menu and the variable list both put it there. It used to be in the corner, and the version book in the middle: two windows side by side asked for two different clicks.
slot38materialLIME_CONCRETEname"lang:admin.gui.panel.reload" schedules.yml
Scheduled commands 15 settings
schedules.ymlplugins/ShadowCommand/schedules.yml File notes
The things that happen by themselves. Three ways of saying when:
every: "10m" every so often at: "04:00" at one time of day, every day cron: "0 * * * *" minute hour day month day-of-week
Useful keys:
enabled turns one off without deleting it
on-start also runs once when the server starts
for-each-player repeats for every online player, with {player} filled
async runs off the main thread (safe actions only)
conditions the same requirements commands useOut of the box there is one announcement, turned off. Turn it on to see how it behaves.
What a player reads ships in English: it is yours to rewrite. An Italian version of this file is in esempi/it_IT/schedules.yml. It is not loaded: copy it over this one to use it.
config-version2schedules4 keysschedules.announcement3 keysAn announcement every ten minutes. Off: turn it on when you want it.
enabledfalseevery"10m"run["broadcast: &8[&fInfo&8] &7Type &f/rules&7 to…]schedules.wage5 keysAn hourly wage for whoever holds the permission. Off out of the box: it touches the economy, and nobody should find money in their pocket without having decided so.
enabledfalsecron"0 * * * *"for-each-playertrueconditions["permission: server.wage"]run["money: give 100", "actionbar: &7+&a100 coins&7 &8(hourly wage)"]+1
schedules.cleanup3 keysGround item cleanup, with two minutes of warning.
enabledfalseevery"30m"run["broadcast: &8[&fCleanup&8] &7Items on the ground will disappear in &f2 minutes&7.", "wait: 1m", "broadcast: &8[&fCleanup&8] &f1 minute&7 left.", "wait: 50s", "broadcast: &8[&fCleanup&8] &7In &c10 seconds&7.", "wait: 1 …+7
schedules.nightly-backup3 keysA nightly backup. The command depends on your host: change it.
enabledfalseat"04:00"run["console: save-all", "log: nightly save done"] commands/examples.yml
Custom commands 7 settings
commands/examples.ymlplugins/ShadowCommand/commands/examples.yml File notes
A catalogue: one example of every kind of command you can build.
THREE ARE LIVE - /rules, /discord and /examplemenu work the moment the server starts. Everything else is commented out, so a fresh install stays clean and nothing you did not ask for appears in the command list.
TO TURN ONE ON, remove the two characters "# " from the front of every line of the block and run /scmd reload. The indentation is already right underneath.
Create as many files as you like in this folder, sub-folders included. The file name means nothing to the plugin: it is only there so you can find things again.
Everything you type - command names, keys, options, values - is English. Everything a player reads is a message, and messages are yours to write in any language: the ones below ship in English. An Italian version of this file is in esempi/it_IT/commands/examples.yml. It is not loaded: copy it over this one to use it.
MESSAGE STYLE, the same one lang/ uses:
&7 the body of the sentence &f the words that matter &c what is wrong or missing &a what has just gone right &8 the quiet part no &l: a message is read, not shouted
Placeholders with {}: {player} {arg1} {var:points} {math:1+2} The full list is in /scmd placeholders and in docs/09-VARIABILI.md.
config-version201 Text only live
The shortest command there is: a list of lines, no script. Good for rules, info, staff lists, anything that only has to be printed.
rules2 keysdescription"The server rules"text["&8&m ", "&fServer rules", "&8 1 &7Respect the others.", "&8 2 &7No cheats or third-party programs.", "&8 3 &7No advertising other servers.", "&8 4 &7Have fun.", "&8&m …+6
02 Script with a clickable link live
run: is a script, one action per line. Here a single message, with the address itself clickable.
discord2 keysdescription"Our Discord"run["message: &7Join us on <click:open_url:'https…]03 Opens a GUI live
One line, and the command opens menus/example.yml. The same window can be opened from a script with menu: example.
04 Aliases, usage line and visibility
Everything a command can say about itself. hidden: true keeps it out of the client's tab list without turning it off.
website:
description: "The server website"
aliases: [ "site", "web" ]
usage: "/website"
hidden: false
run:
- "message: &7Our website: &fwww.example.com"05 Typed arguments
The client completes them and the plugin checks them before your script runs. Types: player, offline-player, world, material, choice, int, decimal, word, text, boolean. {arg1} is the first, {args:2-} everything from the second on.
kit:
description: "Claim a kit"
permission: "server.kit.{arg1}"
arguments:
- name: "kind"
type: choice
values: [ "wood", "iron", "diamond" ]
required: true
error: "&7Pick one of &fwood&7, &firon&7 and &fdiamond&7."
- name: "amount"
type: int
min: 1
max: 5
default: 1
error: "&7The amount goes from &f1&7 to &f5&7."
run:
- "message: &7Here is the &f{arg1}&7 kit &8(x{arg2})"
- "repeat: {arg2}"
- " if: {arg1} == diamond"
- " give: DIAMOND_SWORD 1 name:'&fKit sword' enchant:SHARPNESS:3 unbreakable"
- " elseif: {arg1} == iron"
- " give: IRON_SWORD 1"
- " else:"
- " give: WOODEN_SWORD 1"
- " end"
- "end"
- "sound: entity.player.levelup"06 Sub-commands
A command with children, each with its own permission, its own arguments and its own script. Nest as deep as you like.
staff:
description: "Staff tools"
permission: "server.staff"
permission-default: op
run:
- "message: &7Use &f/staff list&7 or &f/staff warn&7."
subcommands:list:
description: "Who is online"
run:
- "message: &7Online: &f{server_online}&7/&f{server_max}"
- "message: &7Names: &f{server_online_names}"warn:
description: "Send a player a warning"
arguments:
- { name: "target", type: player, required: true }
- { name: "reason", type: text, required: true }
run:
- "tell: {arg1} &cWarning &8- &7{args:2-}"
- "message: &7Warning sent to &f{arg1}&7."
- "console: say &8[staff] &7{player} warned {arg1}"07 Permissions
A command with no permission: gets shadowcommand.command.<name> and everyone has it. Write one, or a list, and say what happens when the player does not hold it.
vipkit:
description: "The VIP kit"
permissions: [ "server.vip", "server.bundle.kit" ]
permissions-mode: any # all (default) needs them all, any needs one
permission-message: "&7This kit is for &fVIPs&7. Have a look at &f/shop&7, {player}."
run:
- "give: DIAMOND 8"08 Requirements
Checked before anything runs: nothing is taken and nothing happens when one denies. 48 of them, listed by /scmd requirements. requirements-mode: all (default) | any | none
home:
description: "Go back to your home"
player-only: true
requirements:
- "world: !world_the_end"
- type: condition
value: "{var:home_x|none} != none"
deny: "&7You have not set a home yet. Use &f/sethome&7."
- type: condition
value: "{health} > 4"
deny: "&7You are &ctoo hurt&7 to teleport."
sound: "block.note_block.bass"
requirements-mode: all
run:
- "teleport: {var:home_x} {var:home_y} {var:home_z} {var:home_world}"
- "message: &7Welcome back &ahome&7."09 Cost
Money, experience, levels, hunger, health, items. All or nothing: if one piece cannot be paid, nothing at all is taken.
repairhand:
description: "Repair what you are holding"
permission: "server.repair"
requirements:
- "item-in-hand: !AIR"
cost:
money: 250
items:
- "IRON_INGOT 4"
message: "&7You paid &f250 coins&7 and &f4 ingots&7."
run:
- "console: repair {player}"
- "message: &7Repaired. &aDone&7."
- "sound: block.anvil_use"10 Cooldown
Per player, or one for the whole server. It survives a restart, so a 24 hour kit really is 24 hours.
daily:
description: "The daily reward"
cooldown: "24h"
cooldown-persistent: true
cooldown-bypass: "server.nocooldown"
cooldown-message: "&7You already claimed the reward. Come back in &f{cooldown_left}&7."
run:
- "money: give 500"
- "give: DIAMOND 1"
- "message: &7Reward claimed. &aCome back tomorrow&7."11 Warm-up
The wait before it starts, with a boss bar, cancelled if the player moves or takes damage.
spawn:
description: "Go back to spawn"
player-only: true
warmup: "5s"
warmup-bossbar: true
warmup-cancel-on-move: true
warmup-cancel-on-damage: true
run:
- "teleport: 0 100 0 world"
- "message: &7You are at &fspawn&7."
- "sound: entity.enderman.teleport"12 Confirmation
The question arrives in chat with two clickable buttons and a one-shot code: nobody can answer in your place. Never make a player retype a command to confirm it.
clearinv:
description: "Empty your inventory"
player-only: true
confirm: true
confirm-text: "&7You are about to empty &call&7 of your inventory."
run:
- "clear:"
- "message: &7Inventory emptied."13 Who may run it
player-only keeps the console out, console-only keeps the players out. Useful for anything a scheduler or a panel calls.
announce:
description: "Announcement from the console"
console-only: true
arguments:
- { name: "text", type: text, required: true }
run:
- "broadcast: &8[&fInfo&8] &7{args}"
- "sound-all: block.note_block.pling 0.5 1.5"14 Variables
var: this player, gvar: everyone with their own copy, svar: one for the whole server. They live in the database, so they survive a restart and can be shared between servers over MySQL.
stats:
description: "Your statistics"
run:
- "var: add opened 1"
- "message: &8&m "
- "message: &7Statistics opened &f{var:opened}&7 times."
- "message: &7Health: &f{health}&7/&f{maxhealth} &7Level: &f{level}"
- "message: &7World: &f{world} &7Ping: &f{ping}ms"
- "message: &7Play time: &f{playtime}"
- "message: &8&m "15 Branching
if / elseif / else / end, with comparisons on anything: numbers, text, placeholders, free expressions.
health:
description: "How are you doing"
player-only: true
run:
- "if: {health} >= 18"
- " message: &7You are &avery well&7. &8({health}/{maxhealth})"
- "elseif: {health} >= 8"
- " message: &7You are a bit &fbattered&7. &8({health}/{maxhealth})"
- "else:"
- " message: &7You are in &cbad shape&7! &8({health}/{maxhealth})"
- " effect: REGENERATION 10 1"
- "end"16 Loops and waiting
repeat, while, for, break, and wait: - which suspends the script without holding the server for a single tick.
countdown:
description: "A countdown"
permission: "server.staff"
run:
- "for: n in 5,4,3,2,1"
- " broadcast: &7Starting in &f{n}&7..."
- " sound-all: block.note_block.hat"
- " wait: 1s"
- "end"
- "broadcast: &aGo!"
- "sound-all: block.note_block.pling 1 2"17 Chance and random
chance: N opens a block that runs N times out of a hundred, and {rand:...} picks a number or an entry.
crate:
description: "Open a prize crate"
cooldown: "1h"
cost:
items:
- "TRIPWIRE_HOOK 1"
run:
- "repeat: 5"
- " actionbar: &7Drawing &f{rand:iron,gold,diamond,emerald}"
- " sound: block.note_block.hat"
- " wait: {math:{loop}*4}t"
- "end"
- "chance: 2"
- " give: NETHER_STAR 1"
- " broadcast: &f{player} &7drew a &fNether star&7!"
- " stop"
- "end"
- "chance: 20"
- " give: DIAMOND 8"
- " message: &7You won &f8 diamonds&7."
- " stop"
- "end"
- "money: give {rand:50-250}"
- "message: &7Consolation prize: &f{result} coins&7."18 Functions
A block of script other commands can call, with arguments and a return value. They can live here under functions:, or in their own file: see functions/examples.yml.
vote:
description: "Vote for the server"
run:
- "call: separator"
- "message: &7Vote here: &fwww.example.com/vote"
- "call: separator"functions:
separator:
run:
- "message: &8&m "19 Building from a command
fill, hollow, walls, outline, replace, cylinder, sphere, move, copy, paste, undo and blockdata. ~ is where the player stands. A gate is one blockdata line, and it opens in the same instant.
gate:
description: "Opens and closes the gate"
permission: "server.gate"
cooldown: "2s"
run:
- "blockdata: 102 71 102 toggle=open"
- "blockdata: 103 71 102 toggle=open"
- "sound: block.fence_gate.open"
- "message: &7The gate moves."floor:
description: "A floor under your feet"
permission: "server.builder"
player-only: true
run:
- "fill: ~-3 ~-1 ~-3 ~3 ~-1 ~3 SMOOTH_STONE"
- "message: &7Done. If you do not like it: &f/revert"revert:
description: "Undo the last block changes"
permission: "server.builder"
run:
- "undo: 1"
- "message: &7Put back &aas it was&7."20 Running something the player could not
console: runs from the console. op: runs from the console too, with the player's placeholders filled in (op: gamemode creative {player}), and every op: use is logged with who and what: it never makes anyone an operator. bypass: grants one permission for one line only, runas: runs the line as somebody else.
giverank:
description: "Give a player a rank"
permission: "server.staff.rank"
arguments:
- { name: "target", type: player, required: true }
- { name: "rank", type: choice, values: [ "vip", "vip+", "mvp" ], required: true }
confirm: true
confirm-text: "&7You are about to give &f{arg2}&7 to &f{arg1}&7."
run:
- "bypass: luckperms.user.parent.add | lp user {arg1} parent add {arg2}"
- "tell: {arg1} &7You received the rank &a{arg2}&7."
- "broadcast: &f{arg1} &7is now &f{arg2}&7."
- "log: [ranks] {player} gave {arg2} to {arg1}"21 Alternative endings
What to do when something says no. Without these the player just gets the standard message from lang/.
shop:
description: "Opens the shop"
menu: "shop"
cooldown: "3s"
requirements:
- "world: !world_the_end"
on-deny:
- "sound: block.note_block.bass"
on-cooldown:
- "message: &7Another &f{cooldown_left}&7 before you can open it again."
on-error:
- "message: &7Something went wrong, try again."22 Placeholders from other plugins
%...% is PlaceholderAPI: anything any other plugin publishes can be read here. Without PlaceholderAPI installed they stay written as is.
profile:
description: "Your profile"
run:
- "message: &7Rank: &f%vault_group%"
- "message: &7Balance: &f{money_formatted}"
- "message: &7Position: &f{x}, {y}, {z} &8({world})" functions/examples.yml
Functions 6 settings
functions/examples.ymlplugins/ShadowCommand/functions/examples.yml File notes
A function is a block of script other scripts can call:
- "call: reward 3 'first join'"
- "message: &7The function returned {result}."Arguments become local variables, so they never touch the player's own variables. Recursion is allowed up to the limit in config.yml (engine.limits.max-depth).
Functions can also live inside a command file, under a "functions:" key: they are together here only because it reads better.
What a player reads ships in English: it is yours to rewrite. An Italian version of this file is in esempi/it_IT/functions/examples.yml. It is not loaded: copy it over this one to use it.
config-version2functions3 keysfunctions.reward2 keysGives diamonds, says so, and returns how many it gave.
args[amount, reason]run["give: DIAMOND {amount}", "message: &7+&f{amount}&7 diamonds &8({reason})", "sound: entity.experience_orb.pickup", "return: {amount}"]+3
functions.separator1 keyA separator line, because writing it out every time gets old.
run["message: &8&m…]functions.announce2 keysAn announcement in the server's own format, written once: change it here and it changes everywhere.
args[text]run["broadcast: &8[&fInfo&8] &7{text}", "sound-all: block.note_block.pling 0.5 1.5"]+1
Languages
The texts the plugin shows to players, one file per language.
lang/
Texts and messages 2 files
lang/plugins/ShadowCommand/lang/ File notes
This file holds what the players read, and it is the final fallback: a key missing from every other language lands here. The keys are English like everywhere else in the plugin; only the values are text for a player, so only the values get translated.
HOUSE STYLE - keep it when you edit these lines:
&7 is the body of every message. Nothing else. &f marks the words that matter: names, numbers, files, commands. &c marks what is missing, blocked or wrong. &a marks what has just gone right. &8 is for the quiet part: ids, hints, separators. No &l in chat. A message is read, not shouted.
Colours are written with &: &a &c &r, and &#FF8800 for an exact one. The only exception are gradients, which no & code can express:
<gradient:#7E57C2:#4FC3F7>text</gradient>
System message placeholders are written <like_this>. The ones you write in your own commands and menus are {like_this}.
A message can be a string or a small map:
key: text: "&cText" channel: ACTION_BAR # CHAT | ACTION_BAR | TITLE | SUBTITLE | NONE sound: block.note_block.bass volume: 0.7 pitch: 0.8 cooldown: "2s" # anti spam, per player
channel: NONE silences a message without deleting the key.
Every message the plugin shows to players, one file per language. The keys are not listed one by one: here are the files, how many texts they hold and the placeholders they use.
| File | Language | Texts |
|---|---|---|
lang/en_US.yml reference | English | 893 |
lang/it_IT.yml | Italiano | 893 |
Most used placeholders
The plugin replaces them with the right value: leave them written as they are.
<prefix><count><value><command><max><player><page><pages><name><error><status><where><id><file>
Message groups
prefix1common20command31cost10requirement19engine4problem45note60input5admin364license51blocks2catalog281
Other
Ready-made content, examples, imports: the files you copy and edit as a model.
triggers/
Automatic triggers 2 files
triggers/plugins/ShadowCommand/triggers/ 2 files with the same structure. Below, 2 of them in full, one for each different shape; the others are listed by name.
examples.ymlsuite.yml
Example triggers/examples.yml
File notes
A script that runs by itself when something happens.
TWO ARE LIVE - the welcome on a first join and the line on every join. Everything else has enabled: false and is there to be read and turned on: one example per kind of trigger.
Listeners are only registered when somebody uses them: turn off every trigger of one kind and that listener does not even exist, so it costs nothing. /scmd triggers shows which ones are live and how many times they have fired.
MESSAGE STYLE, the same one lang/ uses:
&7 the body of the sentence &f the words that matter &c what is wrong or missing &a what has just gone right &8 the quiet part no &l in chat
Every event name is in docs/08-TRIGGER.md and in /scmd triggers.
What a player reads ships in English: it is yours to rewrite. An Italian version of this file is in esempi/it_IT/triggers/examples.yml. It is not loaded: copy it over this one to use it.
config-version2triggers9 keysA player's very first join. Fires once per player, ever
triggers.welcome2 keyseventplayer-first-joinrun["broadcast: &8[&a+&8] &f{player} &7joined for the first time.", "title: &fWelcome", "subtitle: &7Read the rules with &f/rules", "give: BREAD 16", "sound: entity.player.levelup"]+4
Every join: one line on the action bar, so the chat stays clean
triggers.greeting2 keyseventplayer-joinrun["wait: 1s", "actionbar: &7Welcome back, &f{player}&7."]+1
triggers.diamonds6 keysA filter on the event itself: diamonds only, one world only, with a cooldown so a vein does not spam the chat.
eventblock-breakenabledfalseblock"DIAMOND_ORE,DEEPSLATE_DIAMOND_ORE"world"world"cooldown"3s"run["broadcast: &f{player} &7found diamonds at &f…]A sign that opens a GUI
The first line is coloured automatically when somebody writes it, and only who holds the permission may.
On death: a line to whoever died, with the cause and the killer
triggers.death3 keyseventplayer-deathenabledfalserun["message: &7You died&8: &c{death_cause}", "if: {killer} != ''", " message: &7You were killed by &c{killer}&7.", end]+3
Cancelling an event: here, flower pots may not be broken
cancel: true always cancels; the "cancel" action decides case by case.
triggers.protect-flowers6 keyseventblock-breakenabledfalseblock"FLOWER_POT"conditions["permission: !server.staff"]canceltruerun["message: &7Hands off &cthe flower pots&7."]Walking into a region, and out of it again. Needs ShadowRegions or
WorldGuard: without one of the two this trigger is not registered.
triggers.arena-enter4 keyseventregion-enterenabledfalseregion"arena"run["title: &fArena", "subtitle: &7This is where we fight.", "sound: entity.ender_dragon.growl 0.4 1.4"]+2
Right-clicking with a chosen item in hand
The action names are the server's own: RIGHT_CLICK_AIR when there is no block in front, RIGHT_CLICK_BLOCK when there is, so both are listed.
Chat: reading what was written, and cancelling it when needed
{message} is what the player typed.
triggers.no-caps5 keyseventplayer-chatenabledfalseconditions["permission: !server.staff", "condition: {len:{message}} > 8", "condition: {upper:{message}} == {message}"]+2
canceltruerun["message: &7Write in &clower case&7, please."] Example triggers/suite.yml
File notes
Reacting to OTHER plugins' events, without depending on them.
event: bukkit-event class: "full.package.of.TheEventClass"
The class is looked for in every enabled plugin. If the plugin that defines it is not installed, the trigger stays off and shows up in /scmd errors with the name you wrote: a wrong package is otherwise the hardest thing to notice.
Every readable property of the event becomes a placeholder:
getNewLevel() -> {new_level}
isPremium() -> {premium}
getPlayer() -> {player}/scmd debug lists them while it happens.
Everything in here is off out of the box: turn on what you need.
What a player reads ships in English: it is yours to rewrite. An Italian version of this file is in esempi/it_IT/triggers/suite.yml. It is not loaded: copy it over this one to use it.
config-version2triggers7 keysShadowBattlePass: somebody goes up a tier
triggers.pass-tier-up4 keysenabledfalseeventbukkit-eventclass"net.shadowmc.battlepass.api.event.BattlePassTierUpEvent"run["broadcast: &8[&fPass&8] &f{player} &7reached tier &f{new_tier}&7.", "sound-all: entity.player.levelup 0.5 1.2"]+1
pass-tier-up:
pass-quest-done, pass-season-start, match-end, crate-open, variable-changedtriggers.match-join4 keysShadowGames: joining a match. The pass experience is handed out here, so the two plugins talk without knowing about each other.
enabledfalseeventbukkit-eventclass"net.shadowmc.games.api.event.GamePlayerJoinEvent"run