Registering commands
Declare and handle commands from a C++ plugin.
This page covers how to declare and handle commands in C++, from a one-line command up to sub-commands and sender checks. For usage-string syntax, parameter types, and enums, see Creating commands. The examples alias the namespace with namespace es = endstone; in my_plugin.h to keep type names short.
Declare a command
Inside the ENDSTONE_PLUGIN macro, declare commands with the command(...) builder:
#include "my_plugin.h"
ENDSTONE_PLUGIN("my_plugin", "0.1.0", MyPlugin)
{
description = "My first C++ plugin for Endstone servers";
command("hello")
.description("Greet the command sender.")
.usages("/hello");
}Chain .description(...) and .usages(...) to describe the command and the usage strings the client parses for tab-completion.
Control who can run it
By default every command requires the operator permission. Attach a permission with .permissions(...), then define its default level with the permission(...) builder:
#include "my_plugin.h"
ENDSTONE_PLUGIN("my_plugin", "0.1.0", MyPlugin)
{
description = "My first C++ plugin for Endstone servers";
command("hello")
.description("Greet the command sender.")
.usages("/hello")
.permissions("my_plugin.command.hello");
permission("my_plugin.command.hello")
.description("Allow users to use the /hello command.")
.default_(es::PermissionDefault::True);
}default_ sets who holds the permission:
| Value | Who can run it |
|---|---|
PermissionDefault::True | Everyone |
PermissionDefault::False | No one, unless explicitly granted |
PermissionDefault::Operator | Operators only |
PermissionDefault::NotOperator | Non-operators only |
PermissionDefault::Console | Console only |
Handle the command
Override onCommand in your plugin class. Return true if you handled it, or false to show the usage message:
#include <endstone/endstone.hpp>
namespace es = endstone;
class MyPlugin : public es::Plugin {
public:
bool onCommand(es::CommandSender &sender, const es::Command &command,
const std::vector<std::string> &args) override
{
if (command.getName() == "hello") {
sender.sendMessage("Hello World!");
}
return true;
}
};Read parameters
Declaring /hello [msg: message] makes msg optional; args holds the parsed values in order:
bool onCommand(es::CommandSender &sender, const es::Command &command,
const std::vector<std::string> &args) override
{
if (command.getName() == "hello") {
if (args.empty()) {
sender.sendMessage("Hello World!");
} else {
sender.sendMessage(args[0]);
}
}
return true;
}Remember to update the usage string to "/hello [msg: message]" in the command(...) builder.
Branch on a sub-command
A single command can offer a fixed set of actions with an enum parameter. Declare the choices in the usage string (command("warp").usages("/warp <add|list|del>")) and dispatch on the first argument:
bool onCommand(es::CommandSender &sender, const es::Command &command,
const std::vector<std::string> &args) override
{
if (command.getName() == "warp") {
const std::string &action = args[0];
if (action == "add") {
sender.sendMessage("Warp added.");
} else if (action == "list") {
sender.sendMessage("Listing warps...");
} else if (action == "del") {
sender.sendMessage("Warp deleted.");
}
}
return true;
}Because the parameter is mandatory, the client only accepts one of the listed values, so args[0] is always one of them.
Restrict to players
A command can be run from the console as well as in-game. When your logic needs an actual player, cast the sender with as<es::Player>() - it returns nullptr for a non-player - and bail out gracefully otherwise:
bool onCommand(es::CommandSender &sender, const es::Command &command,
const std::vector<std::string> &args) override
{
if (command.getName() == "ping") {
auto *player = sender.as<es::Player>();
if (!player) {
sender.sendMessage("Only players can run this command.");
return true;
}
player->sendPopup("Pong!");
}
return true;
}See Creating commands for the full set of parameter types and enum syntax.