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:

src/my_plugin.cpp
#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:

src/my_plugin.cpp
#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:

ValueWho can run it
PermissionDefault::TrueEveryone
PermissionDefault::FalseNo one, unless explicitly granted
PermissionDefault::OperatorOperators only
PermissionDefault::NotOperatorNon-operators only
PermissionDefault::ConsoleConsole only

Handle the command

Override onCommand in your plugin class. Return true if you handled it, or false to show the usage message:

include/my_plugin.h
#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:

include/my_plugin.h
    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:

include/my_plugin.h
    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:

include/my_plugin.h
    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.

On this page