A command block registers a slash command on the server. The smallest one is a name and an execute block:
command "ping" {
execute {
send "pong" to sender
}
}sender is bound inside every command body — whoever ran the command, a player or the console. send <message> to <target> writes a message; ${...} interpolation and <lime>-style color tags are covered in Step 04.
The same command, mapped from Skript:
command /hello:
description: Say hello
trigger:
send "&aHello, %player%!" to playercommand "hello" {
description: "Say hello"
execute {
send "<green>Hello, ${sender}!" to sender
}
}Why it maps this way
trigger: becomes execute { }, %player% becomes ${sender}, and &a color codes become MiniMessage tags like <green>. Skript's player is always a player; sender may be the console, and the compiler flags the difference where it matters (Step 04).
Permission and description
command "heal" {
permission: "myserver.heal"
description: "Restore your health to full"
execute {
if sender is a Player {
set sender.health to sender.max_health
send "<lime>Healed!" to sender
}
}
}permission— the sender must hold this node or the command is refused (you'll grant nodes in Step 16).description— shows up in command listings; also good documentation.
Both are optional.
Aliases
Chaining command headers gives one body several names, and the short form drops the repeated keyword:
command "tp",
command "teleport" {
execute {
send "same body, two names" to sender
}
}
command "gm", "gamemode" {
execute {
send "short alias form" to sender
}
}Either way the loader registers one command per name, sharing the body.
Arguments
Declare typed arguments in an arguments block. Each line is name: Type, optionally with = default:
command "give" {
permission: "myserver.give"
description: "Give an item to a player"
arguments {
material: String
amount: Integer = 1
target: Player = sender
}
execute {
set stack to item(args.material, args.amount)
send "gave ${args.target.name} ${args.amount}x ${args.material}" to sender
}
}An argument with a default is optional on the command line; one without is required. So /give diamond works (amount 1, target you), and so does /give diamond 64 Swofty.
Arguments live under the args root: args.material, args.target. Deep paths like args.target.name go through the property system and are checked against the declared argument type at compile time.
The base types are String, Integer, Double, Boolean, Player, and Location — Step 04 has the full type system. Two more argument shapes are worth knowing now and understanding later:
dest: either<Player|Location>— accepts more than one type; you tell them apart withis a(Step 07).who: optional<Player>— the argument may be absent, and the type system makes you deal with that (Step 07 is entirely about this).
command /give <text> [<integer=1>] [<player=%player%>]:
trigger:
send "&aGave %arg-3% %arg-2%x %arg-1%" to playercommand "give" {
arguments {
material: String
amount: Integer = 1
target: Player = sender
}
execute {
send "<green>Gave ${args.target.name} ${args.amount}x ${args.material}" to sender
}
}Why it maps this way
Skript's positional <text> [<integer=1>] with arg-1, arg-2 becomes a named, typed arguments block. A default makes an argument optional, and every use site says args.amount, not "the second one" — get the order wrong and it's a compile error, not a scrambled message.
Bailing out early
halt stops the script immediately — the idiomatic guard-clause exit:
command "smite" {
arguments {
victim: either<Player|Location>
}
execute {
if args.victim is not a Player {
send "<red>You can only smite players" to sender
halt
}
send "<gold>Smitten." to sender
}
}Guards, loops, and the rest of the control-flow toolkit are Step 05; first, the other way scripts get triggered.