A command with arguments and permissions
Status: Implemented. Custom suggestions, redirects and client-side commands are Planned.
Goal: /ruby give <targets> [amount] for operators and /ruby count for everyone.
Language: Lua, with C++ and C at the end. APIs: Commands.
1. The tree
ruby
├── count everyone
└── give (level 2) operators only
└── <targets: players>
└── <amount: integer>
Requirements apply to a node and everything below it, and clients only see what they may use.
2. Lua
local RUBY = "my_mod:ruby"
local function give(ctx, amount)
local targets = ctx:entities("targets") -- always a list
for _, p in ipairs(targets) do p:give(RUBY, amount) end
ctx:success(("Gave %d rubies to %d players"):format(amount, #targets))
return #targets
end
cl.on("server.command_register", function()
local cmd = cl.cmd
cl.cmd.register(
cmd.literal("ruby")
:then_(cmd.literal("count"):executes(function(ctx)
local p = ctx:player()
if not p then ctx:failure("Only players have pockets.") return 0 end
ctx:success("You have " .. p:count_item(RUBY) .. " rubies.")
return 1
end))
:then_(cmd.literal("give"):requires_permission(2)
:then_(cmd.argument("targets", "players")
:executes(function(ctx) return give(ctx, 1) end)
:then_(cmd.argument("amount", "integer")
:executes(function(ctx) return give(ctx, ctx:arg("amount")) end)))))
end)
targets accepts names, UUIDs and selectors (@a, @p[distance=..10]) and fails with vanilla’s message when nothing matches.
3. Feedback conventions
ctx:success(text)for the result line (plain, like vanilla; colour it with a text if you want).ctx:failure(text)for errors (red, like vanilla).- Return the number of things affected (> 0 means success), like vanilla commands.
4. C++
using namespace clight::cmd;
clight::on<clight::ev::ServerCommandRegister>([](cl_ev_command_register&) {
auto give = [](clight::CmdCtx c, int32_t n) {
auto targets = c.arg_entities("targets");
if (!targets) return 0;
for (clight::Entity p : *targets) p.give("my_mod:ruby", n);
c.reply(std::format("Gave {} rubies to {} players", n, targets->size()));
return int(targets->size());
};
literal("ruby")
.then(literal("count").executes([](clight::CmdCtx c) {
clight::Entity p = c.player();
if (!p) { c.send_failure("Only players have pockets."); return 0; }
c.reply(std::format("You have {} rubies.", p.count_item("my_mod:ruby")));
return 1;
}))
.then(literal("give").requires_level(2)
.then(argument("targets", "players")
.executes([give](clight::CmdCtx c) { return give(c, 1); })
.then(argument("amount", "integer")
.executes([give](clight::CmdCtx c) { return give(c, c.arg_i32("amount").value_or(1)); }))))
.register_command();
}).detach();
5. C
static int32_t count_cmd(cl_cmd_ctx c, void* u) {
char msg[64];
cl_entity p = cl_cmd_ctx_player(c);
(void)u;
if (CL_IS_NULL(p)) return 0;
snprintf(msg, sizeof msg, "You have %d rubies.", cl_easy_count(p, "my_mod:ruby"));
cl_easy_send_message(p, msg);
return 1;
}
static void on_commands(void* payload, void* user) {
cl_cmd_node root = cl_cmd_literal(CL_STR("ruby"));
cl_cmd_node count = cl_cmd_literal(CL_STR("count"));
(void)payload; (void)user;
cl_cmd_executes(count, count_cmd, NULL);
cl_cmd_then(root, count);
cl_cmd_register_tree(root);
}
/* mod_main: CL_ON(CL_EV_SERVER_COMMAND_REGISTER, on_commands) */
Planned
Custom suggestions for arguments, redirects and forks (/execute-style), permission nodes with defaults, and commands that run only on the client.