碎碎念...
关于命令系统的构思,我采用了一些Web框架的路由设计。
他们是像这样的:
注: 这个例子用到了Rapidoid Web框架。
所以我在Phoenix中也引入了这种设计。
可以说,这部分是最让我引以为豪的地方了。
这篇文章属于Phoenix Framework 入门教程的一部分。
注册一个命令执行器
咱建议,在插件被启用的时候就马上让Phoenix给我们的模块注册监听器。所以,在我们项目主类的onEnable()
方法下,写上这么一行:
Phoenix.getCommandManager().registerCommand(this,new MyCommandExecutor());
如果一切顺利的话,接下来, 你的IDE会开始报错。(没报错的同学也没事,接着看往下操作。)
我们根据IDE的提示,创建一个叫做MyCommandExecutor
的类,它就是我们的命令执行器。
在这里,我建议所有的命令执行器都统一丢在一个.command
包下,方便区分。
根据Phoenix的要求,这个类需要实现Command
接口(Interface)。
...
下面是一个来自PhoenixHelloWorld的例子:
让我们来看看。
Phoenix的命令执行器主要由注解组成。
要想编写一个命令执行器,首先你需要让你的类实现Command
接口,并且在类上写一个@PhoenixCommand
注解。
接下来则是许多带有@CommandRouter
注解的方法,以及两个带有@Override
注解的方法,它们的返回类型都是CommandResult
。
那么,Phoenix的命令执行器到底是怎样运作的呢?
@PhoenixCommand注解
这个注解向Phoenix简单介绍了我们的命令执行器。
目前它有如下几个参数:
名称 | 数据类型 | 必填 | 描述 |
---|---|---|---|
label | String | 是 | 命令的名称。 |
description | String | 否 | 命令的描述,通常用来显示在/help菜单中。 |
permission | String | 否 | 命令的权限。执行这个命令时必须满足的权限,你可以留空来实现自己更详细的权限判断。 |
alias | String[] | 否 | 命令的别名,命令发送者可以通过alias执行这个命令。 |
当一切都满足之后,Phoenix才会进一步寻找这个类中的 @CommandRouter
注解。
@CommandRouter注解
这个注解出现在命令执行器的 方法(Methods) 上,正如它的名字所示的那样,它代表着一个命令 路由(Router) 。
名称 | 数据类型 | 必填 | 描述 |
---|---|---|---|
args | String | 是 | 命令的参数。 |
permission | String | 否 | 执行该参数需要的权限。 |
sender | Sender | 否 | 命令的触发者类型。 |
last | boolean | 否 | 是否为最后一个路由。 |
args,参数
args 表示的是命令的参数。参数是什么意思呢?
它指的是命令发送者执行的一串字符中,从第一个空格开始的内容。
举个例子, 某个玩家输入了/hello world !
,
那么hello
就是**@PhoenixCommand** 中的 label 或者 alias,而world
和!
则是参数。
考虑到参数的多样性,我把它简单地分为 命名参数(Named Parameters) 和 变量参数(Variable Parameters) 两类。
我们用上面的代码作为例子,来学习一下如何使用这两种参数。
命名参数
所谓命名参数,指的是不可变的、规定好的参数。
在上面的例子中,help(...)
就是一个命名参数。
命令发送者只有输入/demo help
,才能调用这个命令,而输入/demo h e l p
或者/demo help 123
等内容,都是无法调用的。
所以,命名参数就是你规定命令发送者要输入的内容。
变量参数
我们经常需要使用命令向服务器传达某个信息,使用命名参数固然无法满足要求,所以我们需要变量。
在上面的例子中,echo(...)
方法就是一个典型的例子。
玩家输入/demo echo test
, Phoenix 就会把test识别为<msg>
中的内容,并保存到CommandContent
中,并且就像Map
一样容易调用。
在args
中,我们使用一对尖括号<>
来指定一个变量的名字。Phoenix会将命令发送者发送命令的适当部分转换为这个变量名,让你在CommandContent
中读取它。
参考 echo_2(....)
,变量参数的数量基本上没有限制,你设置好几个也没有问题。
你可能会考虑到,如果我想让玩家输入一句话,这里面也许会有很多单词或者空格,但又不能确定数量,怎么办呢?
很简单!在变量名之后加上三个点号...
,就像echo_a(...)
中的那样。Phoenix遇到这种标识符之后,就会把之后所有的内容全部转换为数组型String,你可以通过CommandContent
中的getStrings(...)
来读取参数。
值得注意的是,...
是一个标识符,但它也是变量名的一部分,因此你在读取时可千万别忘了带上它。
sender,发送者类型
有些命令你并不想被玩家执行,有些命令只能由玩家执行。
你可以通过sender来限制发送者的类型。只有发送者与sender中的类型相同,命令才会调用。
sender的值默认为All(所有),可选的参数有:
- Console(控制台)
- RemoteConsole(远程控制台,如CRON)
- Block(方块,如命令方块)
- Player(玩家)
last,最终路由
Phoenix的命令执行器是一个路由系统。也就是说 — 在匹配到某个路由符合条件后,Phoenix会执行它,然后寻找下一个符合条件的@CommandRouter
,直到它找完为止。
通过这一特性你可以写许多具有连贯性的命令。
如果你将last
设置为true
,Phoenix在调用这个方法之后就会停止,不再寻找下一个路由。
Command接口
这个接口除了告诉Phoenix,你的类是一个命令执行器之外,它还要求你实现两个方法,也就是加了@Override
注解的两个:onRoot(...)
与onMisHandle(...)
。
onRoot
@CommandRouter所有的内容都需要参数。它不能匹配到没有参数的内容。
所以,如果命令发送者没有携带任何参数,它就会调用onRoot
方法。在此时,CommandContent
应当是空的,无论你提供什么样的参数,都不会有结果。
onMisHandled
当命令发送者的参数没有被任何一个路由匹配到时,就会它就会被执行。此时的CommandContent
应当也是空的。你可以在这里写你自己的提示内容。如告诉命令发送者:参数错误,输入/demo help
获取帮助。
CommandResult类
每个路由的返回值都是 CommandResult类,该类可以用于我们向服务端反馈命令的执行情况。
这个类主要包含下列几个字段:
- affectedBlocks - 影响的方块数量,默认为
0
。 - affectedEntities - 影响的实体数量,默认为
0
。 - affectedItems - 影响的物品数量,默认为
0
。 - queryResult - 匹配到的结果数量,默认为
0
。 - reason - 执行失败原因,默认为
NONE
。
你可以使用Builder
来构造一个CommandResult。
假设我们正在编写一个命令,玩家执行之后将会以 消耗手上的1个物品 为代价,造成一次爆炸,
这个爆炸 破坏了10个方块,杀死了5只生物,那么我们可以这样写我们的CommandResult
:
reason
则用来标识命令执行失败的原因:
- NONE - 没有失败,默认值。
- NOT_FOUND - 没有匹配到路由。
- NO_PERMISSION - 权限不足。
- INTERNAL_ERROR - 内部错误。
- INVALID_ARGUMENT - 参数错误。
更多的时候,你的命令可能不会影响什么东西。这样写CommandResult实属繁琐,因此Phoenix API内置了 5 种常用的反馈结果:
CommandResult.success();
- 该命令执行成功。CommandResult.notFound();
- 该命令没有匹配到路由。CommandResult.internalError();
- 该命令在执行过程中遇到内部错误。CommandResult.invalidArgument();
- 该命令的参数不正确。CommandResult.permissionDenied();
- 该命令执行者没有权限执行这个命令。
至此,你已经学完了Phoenix Framework
的命令部分。
如果本文章出现任何问题,请您在评论区或者通过其他方式向作者反馈,谢谢。