本文为原文的衍生作品,和原文均使用 MIT 协议。请在必要时附带以下协议全文:
- MIT License
- Copyright (c) Yanbing Zhao
- Copyright (c) Microsoft Corporation. All rights reserved.
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Brigadier(直译为准将、旅长)是一个基于 MIT 协议的,第三方开发者可自由使用的项目,可用于命令解析和命令调度。Brigadier 针对 Minecraft Java 版设计并应用于此。
安装
开发者可通过 Maven 或者 Gradle 安装 Brigadier,其中,相应的 Maven 仓库位于 libraries.minecraft.net。项目的 Group ID 为 com.mojang,其 Artifact ID 为 brigadier。
Gradle
请首先引用我们的 Maven 仓库:
- maven {
- url "https://libraries.minecraft.net"
- }
然后引用我们这一项目(请将 (the latest version) 替换成最新版本!):
- compile 'com.mojang:brigadier:(the latest version)'
Maven
请首先引用我们的 Maven 仓库:
- <repository>
- <id>minecraft-libraries</id>
- <name>Minecraft Libraries</name>
- <url>https://libraries.minecraft.net</url>
- </repository>
然后引用我们这一项目(请将 (the latest version) 替换成最新版本!):
- <dependency>
- <groupId>com.mojang</groupId>
- <artifactId>brigadier</artifactId>
- <version>(the latest version)</version>
- </dependency>
贡献代码
目前,向项目贡献代码我们暂不接受,请等待我们理顺完相关流程。
大部分开发者在贡献代码时需要同意一个许可协议(Contributor License Agreement,CLA),作为开发者有权利授权这个项目,当然也要授权这个项目,允许使用这部分代码的声明。详情请参见 https://cla.microsoft.com。
该项目采用了微软开源行为准则(Microsoft Open Source Code of Conduct),详情请参阅 FAQ。如有其他问题或意见,请联系 opencode@microsoft.com。
用法说明
作为 Brigadier 的核心,开发者首先需要一个 CommandDispatcher<S> 作为命令调度器,其中 <S> 可用于代表任何在项目中作为“命令执行者”的对象。
命令调度器的核心被称为“命令节点树”,树上的命令节点,也就是 CommandNode,共同组成了合法命令所有可能的格式。
注册新命令
我们首先需要构建命令节点树,再将其用于命令解析和命令调度。注册的过程可以抽象为追加数据,因此,对于无法获取源代码的项目,开发者同样可以以十分方便的方式扩展其中的命令节点树。
我们鼓励开发者使用生成器模式注册命令,从而尽可能减小代码冗余。
“命令”这个概念本身的含义相当宽泛,不过通常情况下它意味着命令节点树的终止点。每个命令节点都可以追加一个用于执行命令,也就是 executes 的函数,这表示如果命令输入在此处终止,那么相应的函数将会被调用,并传入已经收集的上下文。
我们考虑以下示例:
- CommandDispatcher<CommandSourceStack> dispatcher = new CommandDispatcher();
- dispatcher.register(
- literal("foo")
- .then(
- argument("bar", integer())
- .executes(c -> {
- System.out.println("Bar is " + getInteger(c, "bar"));
- return 1;
- })
- )
- .executes(c -> {
- System.out.println("Called foo with no arguments");
- return 1;
- })
- );
这段代码注册了两个可以被称作“命令”的东西:foo 和 foo <bar>。通常情况下 <bar> 也可被称作 foo 的“子命令”,同时相应的,它对应着一个子节点。
整棵树首先要有“根节点”,其中根节点的子节点必须是字符串字面量,也就是 LiteralCommandNode。我们在根节点下注册了一个命令:literal("foo"),这代表用户必须输入 foo 这一字面量,才能触发这一命令。
接下来我们追加了一个子节点,代表后续输入的解析,和一个 executes 块,用于在用户的输入到此为止时执行命令。
子节点的解析方式如出一辙,不过已经不限于字符串字面量了。额外允许使用的命令节点,也就是 ArgumentCommandNode,由名称和参数类型组成。
参数类型可以是你想得到的任何类型,我们也鼓励你构造自己的参数类型,以便无缝集成到你自己的产品当中。Brigadier 本身内置了几个参数类型,如 IntegerArgumentType 等。
不同的参数类型将决定不同的解析方式,从而尽可能匹配输入,并在解析成功时保存解析结果,在解析失败时抛出相应的异常。
对于整数参数类型而言,123 将会被解析为整数(int)形式的 123 并保存,而对于 onetwothree 而言,解析时将会抛出一个解析错误的异常。
实际执行命令时,注册命令时提供的上下文将参与解析,从而提供相应的参数。
解析用户输入
我们注册了我们的命令,是时候解析用户输入了。如果你不关心更具体的东西,你只需要以 dispatcher.execute("foo 123", source) 这种方式调用,那么你的任务就完成了。
execute 方法的返回值和相应的命令执行后的返回值对应。对于不同的命令,这一返回值的意义也不同,而对于开发者来说,这一返回值通常没什么用。
source 应为 <S> 的实例,比如说用户和玩家等。这一参数有助于让不同的命令了解一些和发生了什么有关的上下文。
命令在解析或执行等阶段如果出错,那么将会抛出 CommandSyntaxException 异常。你也可以抛出一个 RuntimeException,它将不被处理,而是像气泡一样一点一点从调用栈上升,直到最后被抛出。
如果你想将解析和命令执行分离开来,或者说缓存解析结果,从而执行多次,你可以将解析和执行分成两步:
- final ParseResults<S> parse = dispatcher.parse("foo 123", source);
- final int result = execute(parse);
因为解析的过程相对费时,同时对于很多项目来说,缓存解析结果也相对容易,因此我们强烈建议开发者这么做。
您也可以在执行命令之前(或压根不执行的同时)对命令进行进一步的检查。
检查命令
如果你调用 parse 方法解析命令,那么你可以事先得知某个命令(如果有的话)将要做什么,从而能够以相对安全迅速的方式提示用户。
使用这种方式解析命令不会刻意地抛出异常,因此返回的 ParseResults<S> 将会包含一个可能出现的上下文,用于指示可能会执行的命令(您可以从中检查用户输入,得知一些和开始结束的位置有关的信息)。返回值同样会包含解析错误的集合,用于指示每一个尝试解析但解析出错的命令节点。在解析出错时,这样的一个集合将会对查找原因有所帮助。
显示用法说明
Brigadier 提供了两种方式显示用法说明,你需要提供一个命令节点从而使用这两种方式中的任何一种。
getAllUsage(node, source, restricted) 方法的返回值是相应命令节点下,所有可能的命令(也就是终止点)及其人类可读形式组成的序列。如果 restricted 设置为 true,那么返回值将不会包含任何 source 无法触及的命令。对于上面的示例,返回值应为 ["foo", "foo <bar>"]
getSmartUsage(node, source) 将会返回子节点到其相对应的“聪明的”人类可读形式的映射。后续节点的信息将会被压缩合并,最终得到和可选参数和类型等有关的信息,返回值将会表现出诸如 foo (<bar>) 等形式。
Markdown 备份
整个主题使用 Markdown 编写,并使用相关工具转换为 BBCode。该部分内容为备份,和主题正文无关。