Skip to content

命令

执行原版命令

rust
let r = ctx.server().execute_command("gamerule doDaylightCycle false")?;
if r.success { println!("{}", r.output); }

以控制台身份执行。

这是很多写操作最省事的实现

/fill/clone/setblock/summon/effect/tp——引擎内部的实现比逐格调用 API 快一个数量级,而且行为和管理员手打完全一致。

大范围改方块尤其如此:一百万格的 Block::set 循环是一百万次 FFI,一条 /fill 是一次。

注册命令

rust
use levilamina::prelude::*;

ctx.server()
    .command("kit", "领取礼包", CommandPermission::Any)
    .overload(|o| o)                                              // /kit
    .overload(|o| o.required("name", ParamType::String))          // /kit <名字>
    .overload(|o| o                                               // /kit give <玩家> <名字>
        .required_enum("give", ParamType::Enum, "kit_kw_give")
        .required("target", ParamType::Player)
        .required("name", ParamType::String))
    .register(|inv| {
        match inv.overload {
            0 => inv.success("可用礼包:新手、建筑"),
            1 => {
                let name = inv.arg("name").and_then(|v| v.as_str()).unwrap_or("");
                inv.success(&format!("已发放 {name}"));
            }
            2 => { /* … */ }
            _ => inv.error("用法错误"),
        }
    })?;

重载编号 = 注册顺序

inv.overload 是第几个 .overload()。中间插新的会把后面全部错位——加在末尾,或者同步改 match

参数

按名字取,不是按下标:

rust
let name = inv.arg("name").and_then(|v| v.as_str()).unwrap_or("");
let n    = inv.arg("count").and_then(|v| v.as_i64()).unwrap_or(1);
let on   = inv.arg("flag").and_then(|v| v.as_bool()).unwrap_or(false);

常用 ParamTypeString Int Float Bool Player Actor BlockPos Vec3 Item BlockName Effect Message Enum SoftEnum。全表见 Command API

谁在执行

rust
let who = &inv.origin;
println!("{} type={}", who.name, who.origin_type);

// 控制台执行时这两个是 None —— 别 unwrap
if let (Some(dim), Some(pos)) = (who.dimension, who.position) {
    // 有位置
}

枚举与运行时补全

固定取值用枚举

rust
ctx.server().register_command_enum("kit_action", &[("give", 0), ("list", 1)])?;

运行时会变的用 soft enum

rust
// 先注册成空的
ctx.server().register_command_soft_enum("kit_names", &[])?;

// 之后随时刷新
ctx.server().update_command_soft_enum("kit_names", SoftEnumOp::Set, &["新手", "建筑"])?;

为什么必须有 soft enum

基岩版的命令树在玩家进服时下发一次,之后不能重新注册命令。soft enum 是唯一能在运行时改变客户端补全内容的机制。

所以模式是:命令一次注册定死,变化的部分放进 soft enum。新建一个世界不需要重启服务器,刷新 soft enum 就出现在补全里了。

权限

rust
pub enum CommandPermission { Any = 0, GameDirectors = 1, Admin = 2, Host = 3, Owner = 4 }

引擎层的粗粒度门禁。细的自己在处理函数里判:

rust
.register(|inv| {
    if !是管理员(&inv.origin.name) {
        inv.error("§c权限不足");
        return;
    }
    // …
})?;

控制台专属命令怎么做

把权限设成 Owner,或者在处理函数里检查 origin 是不是控制台。

授权类命令建议只允许控制台执行:如果管理员能给别人授权,那任何一个被误授权的账号都能把权限扩散出去,而且没有回滚点。控制台是唯一不依赖数据库状态就能拿到的入口,把它当信任根,最坏情况下权限表整个写坏也能恢复。

老接口

没有参数的单条命令,用 register_command 就够:

rust
ctx.server().register_command("ping", "测试", CommandPermission::Any, |inv| {
    inv.success("pong");
})?;

拿到的是整行原文,没有客户端补全。有参数就换 CommandBuilder

拦别人的命令

ExecutingCommandEvent,见 事件。注意它只报玩家发起的命令。