正在载入

等待时间过长时请刷新页面

编写于

最近更新

注册命令

命令组内注册与逐条注册的基本写法相同。

逐条注册适合用途独立、数量较少的命令。命令规范和执行器直接传给 register(...),权限、执行目标和帮助文本集中写在 CommandOptions 中。

import static api.linlang.command.CommandOptions.options;

LinCommand commands = lin.linCommand();

commands.register(
        "rs lore add <line:text>",
        ctx -> {
            Player player = ctx.requirePlayer("Only players can edit item lore");
            String line = ctx.get("line");
            itemEditor.addLore(player, line);
        },
        options()
                .permission("rainbowedit.lore.add")
                .player()
                .i18n(
                        lang.command.addLore.description,
                        "line", lang.command.addLore.line
                )
);

options() 来自 CommandOptions.options() 的静态导入。也可以直接写成 CommandOptions.options()

没有附加要求的命令可以省略选项:

commands.register("rs ping", ctx -> showPing());

默认选项不要求权限,执行位置为 ALL,并且不提供命令描述和参数标签。

命令规范

第一项参数是完整命令规范:

rs lore add <line:text>

其中 rs 是根命令,loreadd 是固定字面量,line 是业务代码使用的参数名,text 是参数类型。

关于必填参数、可选参数、默认值、联合类型和类型配置,请见:命令描述语句 ⇱。每种参数的输入要求与 Java 返回类型请见:命令参数类型 ⇱

命令执行与上下文

命令执行器接收 LinCommand.Ctx。常用成员包括:

  1. sender():返回平台命令发送者。
  2. get(name):取得已经解析的参数。
  3. getOr(name, defaultValue):取得参数,不存在时返回默认值。
  4. requirePlayer(message):要求当前发送者为玩家,否则抛出 IllegalStateException
  5. locale():返回当前命令服务使用的语言。
commands.register(
        "rs give <target:minecraft:player> <item:minecraft:item> [amount:int(1..64)=1]",
        ctx -> {
            Player target = ctx.get("target");
            Material item = ctx.get("item");
            int amount = ctx.getOr("amount", 1);
            giveItem(target, item, amount);
        },
        options().permission("rainbowedit.give")
);

get(...) 使用泛型返回值,但运行时不会检查接收变量的 Java 类型。变量类型应与规范中的解析器一致,否则会在业务代码中产生 ClassCastException。如果可选参数声明了默认值,省略输入时,get() 会取得解析后的默认值。

权限

逐条注册使用绝对权限节点:

options().permission("rainbowedit.lore.add")

传入 null 表示命令服务不添加权限要求。Bukkit 自己为根命令声明的权限仍然可能在进入 Linlang 前生效。

命令组选项可以通过 relativePermission(...) 声明相对权限片段。逐条注册没有组前缀,因此相对片段会被当作完整权限节点;逐条注册应使用 permission(...)

权限和执行者类型会在参数解析前检查。无权限发送者不会触发自定义参数解析或交互等待。

执行目标

ExecTarget 用于限制命令发送者:

  1. PLAYER:仅 Bukkit 玩家。
  2. CONSOLE:控制台与远程控制台。
  3. ALL:玩家或控制台。
options().player()

player()console()all() 分别对应 ExecTarget.PLAYERCONSOLEALL。也可以使用 target(...) 直接传入执行目标。

ExecTarget 不是权限系统。需要限制管理员命令时仍应同时声明权限。

命令国际化

推荐直接在 CommandOptions 中连接语言字段引用:

options()
        .i18n(
                lang.command.teleport.description,
                "target", lang.command.teleport.target,
                "world", lang.command.teleport.world
        )

i18n(...) 的第一个参数是整条命令的帮助说明,后续内容按“参数名、语言字段”成对排列。只传入第一个参数时,仅设置命令说明。参数名必须与规范中的名称一致;没有对应标签时,帮助文本回退到内联说明,最后回退到稳定参数名。

需要逐项设置时,仍然可以使用等价的 .desc(...).label(...) 写法。标签参数不是完整键值对或值不是 LangText 时,注册阶段会抛出 IllegalArgumentException

旧的 I18n、静态 DescLabels 注册方法继续保留。例如静态国际化命令仍然可以写成:

commands.register(
        "rs reload",
        ctx -> reloadPlugin(),
        LinCommand.Permission.perms("rainbowedit.reload"),
        LinCommand.ExecTarget.ALL,
        LinCommand.Desc.desc(
                "zh_CN", "重新加载插件",
                "en_GB", "Reload the plugin"
        ),
        LinCommand.Labels.create()
);

descProvider(...)labelProvider(...)registerLazy(...)I18nSupplier 适合需要自定义取值过程的代码。已有的 LinCommand.I18n 可以通过 options().i18n(i18n) 导入。普通语言对象应优先直接传入 LangText,这样引用关系更明确。

注册冲突

同一注册表中,命令路径以及必填、可选参数形状相同的叶子命令不能重复注册。参数名称或参数类型不同,不会使结构相同的命令变成两个可区分分支:

rs find <id:int>
rs find <name:string>

上面两条命令会发生注册冲突。请增加明确字面量:

rs find id <id:int>
rs find name <name:string>

如果同一个位置本来就接受多种类型,应使用联合类型并在一个执行器中处理:

rs find <target:int|string>

这种限制避免命令结果依赖注册顺序。

讨论

请登录账号