正在载入

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

编写于

最近更新

命令参数类型

命令描述语句中的参数类型决定解析器读取多少输入、如何校验,以及执行器从 ctx.get(...) 取得哪种 Java 对象。

本文列出 Bukkit Runtime 当前默认安装的全部参数类型。类型分为 Core 基础类型、Bukkit 对象类型和交互类型。未列出的类型不在默认支持范围内。

rs give <target:minecraft:player> <item:minecraft:item> [amount:int(1..64)=1]

上面的命令会依次得到 PlayerMaterialInteger。参数名用于从命令上下文取值,类型 ID 位于冒号之后。

通用写法

必填参数使用尖括号,可选参数使用方括号:

<name:string>
[page:int]
[page:int=1]

没有显式类型时,参数默认为 string

<name>

等价于:

<name:string>

Bukkit 会先将命令输入分割为 token,一个参数通常读取其中一个 token。玩家输入中的普通空格会分隔 token,命令服务目前不提供引号转义系统。需要接收带空格文本时应使用 text

参数值通过上下文取得:

String name = ctx.get("name");
int page = ctx.getOr("page", 1);

get(...) 的泛型只帮助 Java 调用端接收返回值,不会改变实际类型。接收类型必须与解析器返回类型一致。

类型约束

类型配置有三种形式:

<amount:int(1..64)>
<amount:int[1..64]>
<amount:int(min=1,max=64)>

圆括号可以承载位置配置或命名配置。方括号形式专门表示闭区间。花括号会把内部内容作为 body 原样交给解析器,适合正则表达式:

<value:regex{^[a-z|]+$}>

配置语法只负责产生元数据。每种解析器只读取自己支持的键,未知配置不会自动产生校验效果。

字符串

string 读取一个 token,返回 String

rs user name <name:string>

输入:

/rs user name Steve

执行器取得:

String name = ctx.get("name");

若玩家继续输入其他 token,而规范中没有后续参数,整条命令会被判定为参数错误。因此 string 不适合直接接收包含空格的名称、Lore 或消息。

string 可以使用正则约束:

<name:string(regex=^[A-Za-z0-9_]{3,16}$)>

也可以把正则放入花括号:

<name:string{^[A-Za-z0-9_]{3,16}$}>

匹配使用 Java String.matches(...),要求整个 token 满足表达式,而不是只匹配其中一部分。

文本

text 读取当前位置及之后的全部 token,以单个空格重新连接,返回 String

rs lore add <line:text>

输入:

/rs lore add First lore line

执行器取得 First lore line

text 必须是命令中的最后一个参数。只要联合类型中包含 text,该参数同样必须位于末尾,因为命令服务需要先决定是否吞并后续输入。

text 也支持与 string 相同的正则约束:

<line:text(regex=.+)>

需要至少一个非空字符时,.+ 是常用规则。Bukkit 不会把空白内容作为参数 token 传入,因此必填 text 在没有输入时仍会直接报告缺失参数。

正则表达式

regex 是专门用于正则校验的单 token 字符串类型,返回 String

rs code <value:regex{[A-F0-9]{8}}>

它与 string 一样,不会接收多个 token。花括号形式适合包含逗号、圆括号或联合符号的表达式,因为其中内容不会被命令规范解析器拆分。

下面两种写法的效果相同:

<value:regex{^[a-z]+$}>
<value:regex(regex=^[a-z]+$)>

如果希望校验带空格的整段文本,应使用 text(regex=...),而不是 regex

整数

int 使用 Java 整数解析规则解析一个 token,返回 Integer

rs page <page:int>

输入必须能被 Integer.parseInt(...) 接受,并且不能超出 32 位有符号整数范围。小数、千位分隔符和超出范围的数字都会解析失败。

范围的上下界均包含在允许范围内:

<amount:int(1..64)>
<amount:int[1..64]>
<amount:int(min=1,max=64)>

三种写法都会要求 1 <= amount <= 64。上下界本身也必须是合法的 int 文本。

布尔

bool 解析一个布尔值 token,返回 Booleanboolean 是功能相同的兼容别名。

rs feature <enabled:bool>
boolean enabled = ctx.get("enabled");

它只接受不区分大小写的 truefalse

/rs feature true
/rs feature FALSE

yesnoonoff10 都不会被自动转换。Tab 补全会按照当前输入前缀提供 truefalse

浮点数

double 使用 Java 浮点数解析规则解析一个 token,返回 Double

rs scale <ratio:double(0.0..1.0)>

它支持普通小数和 Java 能够识别的科学计数法。范围同样是包含上下界的闭区间:

<ratio:double[0.0..1.0]>
<ratio:double(min=0.0,max=1.0)>

当前解析器直接使用 Double.parseDouble(...),因此未声明合适范围时,也可能接受 NaN 或无穷值。业务逻辑要求有限数值时,应在执行器中再使用 Double.isFinite(...) 检查。尤其需要注意,NaN 不会被普通的大小比较排除。

枚举

enum 只允许规范中列出的候选值,返回规范中声明的原始选项字符串。

rs mode <mode:enum(add,remove,clear)>

匹配不区分大小写。玩家输入 ADD 时,执行器仍会取得规范中声明的 add

String mode = ctx.get("mode");

枚举值也可以写在花括号中:

<mode:enum{add,remove,clear}>

Tab 补全会按照玩家当前输入的前缀返回匹配选项。枚举适合选项固定且数量较少的参数,不会自动映射为 Java enum 实例。

UUID

uuid 解析标准 UUID 字符串,返回 java.util.UUID

rs profile <id:uuid>
UUID uniqueId = ctx.get("id");

输入必须使用包含连字符的标准 36 字符形式:

123e4567-e89b-12d3-a456-426614174000

字母大小写不影响解析。32 字符无连字符形式、玩家名和其他缩写形式不会被接受。uuid 不检查该 UUID 是否属于玩家或其他实际对象,类型解析器也不提供实际 UUID 候选;参数为空时,命令服务仍会显示参数说明或参数名作为输入提示。

玩家

minecraft:player 由 Bukkit Runtime 提供,使用 Bukkit.getPlayerExact(...) 查找在线玩家,返回 Player

rs inspect <target:minecraft:player>
Player target = ctx.get("target");

具体要求如下:

  1. 目标必须当前在线。
  2. 必须能够被 Bukkit 精确玩家名查询匹配。
  3. 不会返回离线玩家,也不会按名称片段选择最接近的玩家。
  4. Tab 补全来自当前在线玩家列表,并按输入前缀过滤。

如果命令需要同时接受在线和离线玩家,应使用 minecraft:offline-player。自行实现模糊匹配时,可以继续使用 string 接收,再在业务服务中查询。

离线玩家

minecraft:offline-player 由 Bukkit Runtime 提供,返回 OfflinePlayeroffline-player 是功能相同的简写。

rs history <target:minecraft:offline-player>
OfflinePlayer target = ctx.get("target");

该类型接受两种输入:

  1. 标准 36 字符 UUID:通过 Bukkit.getOfflinePlayer(UUID) 取得对象,不要求服务器已有玩家记录。
  2. 玩家名:先精确检查在线玩家,再在 Bukkit.getOfflinePlayers() 的已有记录中进行不区分大小写的完整名称匹配。

名称输入不会进行包含匹配或最接近名称选择,也不会因为任意未知名称而创建新的离线玩家对象。服务器没有该名称记录时解析失败。使用 UUID 得到的 OfflinePlayer 仍可能从未进入过当前服务器,业务代码可以根据需要检查 hasPlayedBefore()isOnline()getName()

Tab 补全优先列出在线玩家,再补充服务器已知的离线玩家名,自动去除重复项并限制候选数量。UUID 不参与补全。

物品材质

minecraft:item 由 Bukkit Runtime 提供,通过 Material.matchMaterial(...) 解析,返回 Material

rs give <item:minecraft:item>
Material material = ctx.get("item");

该类型表示物品或方块的材质枚举,不会创建 ItemStack,也不包含数量、名称、Lore 或 NBT。是否接受某种名称形式由当前 Bukkit 版本的 Material.matchMaterial(...) 决定,常见输入包括 stoneminecraft:stone

Tab 补全使用 Bukkit 材质的 namespaced key,例如 minecraft:stone。为了避免一次返回过多候选,Runtime 只提供前部的一小批匹配结果。

坐标

minecraft:location 由 Bukkit Runtime 提供,返回 Locationlocation 是功能相同的简写。

位置必须写成一个不含空格的逗号分隔 token,支持以下四种结构:

x,y,z
x,y,z,yaw,pitch
world,x,y,z
world,x,y,z,yaw,pitch

例如:

rs teleport <destination:minecraft:location>
/rs teleport 10.5,64,-20
/rs teleport world_nether,10,70,-4,90,-15

执行器取得完整 Bukkit 位置对象:

Location destination = ctx.get("destination");

省略世界时,命令服务使用发送命令玩家的当前世界;控制台和远程控制台没有当前世界,因此必须使用带世界名称的结构。显式世界必须已经由 Bukkit 加载,名称先精确查找,再进行不区分大小写的完整匹配。

xyz 使用有限 doubleyawpitch 使用有限 float。省略朝向时二者均为 0NaN、无穷值、缺少坐标、空世界名称和多余字段都会解析失败。

当前位置类型目前不支持 ~ 相对坐标、局部坐标、空格分隔格式或自动使用发送者当前朝向。需要这些行为时,应在业务代码中基于解析结果和发送者位置进一步计算。

Tab 补全会在第一个逗号之前提供当前已加载世界的 世界名, 候选。坐标和朝向不提供自动补全。

交互参数

交互类型不会把玩家输入的 token 作为最终参数值。解析器到达该参数时会挂起命令,等待玩家完成对应游戏操作,再用事件对象恢复执行。

rs select <block:event:block>

玩家当前仍需输入一个占位 token 才能让解析器到达该参数:

/rs select begin

begin 会被丢弃,最终的 block 来自玩家之后点击的方块。

Bukkit Runtime 提供以下交互类型:

类型等待操作返回类型等待时间
event:block点击方块Block20 秒
break:block破坏方块Block20 秒
place:block放置方块Block20 秒
event:entity右键实体Entity20 秒
damage:entity攻击实体Entity20 秒
kill:entity击杀实体LivingEntity 的实际事件对象40 秒
event:item点击容器槽位ItemStack,可能为 null20 秒
shoot:block发射物命中方块Block30 秒

所有交互类型都要求命令发送者是 Player,因此相应叶子应通过 CommandOptions.player() 限制执行位置。被其他插件取消的事件不会完成等待,Linlang 也不会主动取消原本的点击、破坏、放置或攻击行为。

每名玩家同时只能保存一个待处理交互,等待到期后不会在到期瞬间主动发送消息,而是在之后收到对应事件时丢弃过期状态。完整流程和使用限制请见:交互式命令 ⇱

联合类型

联合类型使用最外层的 | 连接多个解析器:

<target:minecraft:player|string>

解析器从左到右尝试,返回第一个成功结果。上例中,在线玩家名会得到 Player,其他单 token 文本会回退为 String

Object target = ctx.get("target");
if (target instanceof Player player) {
    inspectOnlinePlayer(player);
} else {
    inspectName((String) target);
}

类型顺序会改变结果。string 在没有约束时几乎总能成功,因此通常应放在联合类型末尾。

配置内部的 | 不会拆分联合类型:

<action:string(regex=^(add|remove)$)|enum(clear)>

可选参数与默认值

可选参数没有输入且没有默认值时,不会写入命令上下文:

[page:int]

此时 ctx.get("page") 返回 null,也可以使用:

int page = ctx.getOr("page", 1);

规范中声明默认值时,默认文本仍会经过参数类型解析器:

[page:int(1..9)=1]

因此执行器会得到 Integer 类型的 1。默认值同样必须满足范围、正则或枚举约束。联合类型的默认值也会按照从左到右的顺序解析。

不支持的类型

命令服务不提供 longfloat、单独的 world、相对坐标或直接返回 ItemStack 的普通文本解析类型。将这些名称直接写入规范会在执行时得到“未知参数解析器”错误。

TypeResolver 是公开的解析器接口,但当前 LinCommand API 尚未提供注册自定义解析器的方法。不应通过强制转换到 Core 实现类来安装解析器;目前可以先用已有类型接收输入,再在业务代码中转换为所需对象。

解析顺序

flowchart LR
    Token["取得当前输入 token"] --> Union["按声明顺序尝试联合类型"]
    Union --> Resolver["找到对应 TypeResolver"]
    Resolver --> Meta["读取范围、正则或候选元数据"]
    Meta --> Value["返回 Java 对象"]
    Value --> Context["写入命令上下文"]
    Resolver --> Suspend["交互类型挂起命令"]
    Suspend --> Event["Bukkit 事件返回对象"]
    Event --> Context

讨论

请登录账号