我的世界Bukkit服务器插件开发教程(十四)消息和命令补全器
十四、消息和命令补全器
在开始之前请确保你有net.md_5.bungee.*包!!!
本章会讲解 Spigot项目 中net.md_5.bungee开头的三个程序包,这三个程序包是关于聊天消息之类的,此外还有命令补全器。
上面三个程序包其实是 BungeeCord (简称BC)的一部分API(心疼没有汉化)。
标题中的“消息”是不准确的,因为 BC 是用于连接客户端与多台服务端之间的网络代理,玩家在多台服务器间跳来跳去,但他们实际觉得好像在多个世界之间跳,有利于减轻 CPU 的负荷。
标题中的“消息”又是准确的,因为 Spigot 包括了 BC 的聊天组件API。聊天 API 之外其他 BC 端的 API 是不可以使用的。
第一部分适用于所有BungeeCord端和Spigot端。
1.消息
1.1.基础
我想给某玩家发送一条消息,这很简单:
player.sendMessage(“Hello World!”);
但如果你在用 Spigot API,可以这样:
player.spigot().sendMessage(new TextComponent(“Hello World!”));
TextComponent就是文本组件,就是一条消息,它还包含了消息的文本、颜色、格式和事件。
1.2.颜色 & 格式
一条消息应该有如下几种格式:
格式解释Bold 粗体Italic斜体Underline下划线Strikethrough(删除线)Obfuscate乱码
所以我们可以给一条消息增加一些格式:
TextComponent message = new TextCompoent();message.setBold(true);//粗体message.setItalic(true);//斜体message.setUnderlined(true);//下划线message.setObfuscated(true);//乱码message.setStrikethrough(true);//删除线message.setColor(ChatColor.DARK_RED);//设置颜色player.spigot().sendMessage(message);//发送消息
但一条消息如果这么设置颜色和格式的话太臃肿了,所以我们可以使用 ComponentBuilder 类一行设置完一条消息的颜色和格式(就是建造模式):
player.spigot().sendMessage(new ComponentBuilder(“Hello World!”). color(ChatColor.RED). bold(true). create());//一定要在最后调用create方法创建一个TextComponent
除此之外,我们还可以将一条消息进行分段,并且每一段的格式和颜色是不一样的:
player.spigot().sendMessage(new ComponentBuilder(“Hello “). color(ChatColor.RED). bold(true). append(“World!”).//使用append进行分段 italic(true). create());
可以看到append方法就是将这条消息进行了一次分段,但最后呈现给玩家的还是一条完整的消息。
1.3.消息の位置
一条消息可以发在不同的位置,但必定是以下四种位置之一:
位置解释CHAT聊天位置SYSTEM系统聊天位置?ACTION_BAR位于经验条上方一段小小的文字的位置
sendMessage方法拥有第二种重载版本,可以将消息发送于指定的位置:
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, …);
其中,第一个参数就是消息的位置。
还可以用setCursor方法设置这段消息的光标(Cursor)位置。
1.4.消息国际化(i18n)
国际化尤为重要,一般说的i18n是指internationalization(国际化),取首尾字母 i 和 n 及中间18个字符称为i18n,指软件无需大变化便可适应不同地区或语言的需要。
每一种语言都有相应的配置文件,切换语言时软件就会加载相应的配置文件。比如zh_CN.properties就是中文下的配置文件,en_US.properties就是英文下的配置文件。
我们一般写时间是这么写的:2023/1/7/18:24。 但老外是这么写的:18:24/7/1/2023。
所以i18n不仅仅是翻译一段文字那么简单,还受上面的日期格式、文化等等因素影响,所以成了不少码农的噩梦。
但我们目的不是实现i18n,Minecraft 早已完成了国际化。语言配置文件格式形如:
“”: “”
尽管软件使用不同的语言编写,其配置文件的格式(如.xaml和.properties截然不同)或多或少会有点差异,但打死你都会在语言配置文件看见key和value。
其中key是一个在任何配置文件都恒不变的标识符,软件通过调用标识符来获取value的值,value就是用不同国家语言翻译后的句子。比如一句“你好”在中文配置文件和英文配置文件分别是:
# 中文”hello”: “你好” # 英文”hello”: “Hello”
插件也通过调用key来实现国际化,可以点 这里 来查看英文配置文件。
TranslatableComponent类就是专门用来国际化的文本组件,本质是将key提供给客户端让客户端获取相应的value。
TextComponent和TranslatableComponent全部从BaseComponent类中派生出来,但new一个BaseComponent是屁用没有。
TranslatableComponent message = new TranslatableComponent(“”);
参数填的就是key,比如我选一个death.attack.magic这个key。它在中文的客户端下就是被魔法杀死了。,英文的客户端下就是 was killed by magic.。
这样做的好处就是服务器往往会有来自不同国家的玩家游玩,比方说服务器给美国人全部呈现中文(哦,歪果仁的噩梦),不用说也得一头雾水,这样的服务器往往只适合在国内发展,在国外人家认不得字,自然而然不想玩了。
而插件作为服务器一大特色,就算不要求自己搞一个i18n,怎么说也要把原版发送给玩家的消息进行一次i18n吧。这样好歹能认出一些消息,退一万步也有想玩的念头了。
实际 TranslatableComponent 可以看成 TextComponent,只不过前者可以国际化罢了,并且可以用addWith方法增加一个新文本组件或一段文字,最后拼接成一段新的文字。并且TranslatableComponent也可以设置颜色、格式等等。
TranslatableComponent message = new TranslatableComponent(“item.record.11.desc”);message.addWith(” かわいい”);message.addWith(new TextComponent(” 哈哈哈哈”));message.setBold(true);
这样在中文下就是“11号唱片 かわいい 哈哈哈哈”,英文下就是“C418 – 11 かわいい 哈哈哈哈”。
(かわいい就是卡哇伊的意思啦~)
1.5.事件
文本事件分为ClickEvent和HoverEvent两种事件。
ClickEvent触发必定会是以下五种动作之一:
动作解释Action.OPEN_URL玩家点击后打开链接Action.OPEN_FILE玩家点击后打开文件Action.RUN_COMMAND玩家点击后执行指令Action.SUGGEST_COMMAND玩家点击文字后在获取后面设置的value并复制到玩家聊天框中Action.CHANGE_PAGE玩家点击后更改书本的页数
HoverEvent触发必定会是以下四种动作之一:
动作解释Action.SHOW_TEXT玩家鼠标悬浮后显示一段文字Action.SHOW_ACHIEVEMENT玩家鼠标悬浮后显示关于某种成就的介绍(在新版本中被弃用)Action.SHOW_ITEM玩家鼠标悬浮后显示关于某种物品的介绍Action.SHOW_ENTITY玩家鼠标悬浮后显示关于某种实体的介绍
这两种事件适用于所有继承BaseComponent的类,可以调用setClickEvent和setHoverEvent方法来添加事件。
TextComponent message = new TextComponent(“点我”);message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, “/kill @s”));message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(“123”)));
这样当玩家点击“点我”文字时就会执行/kill @s指令。
至于HoverEvent,它的后一个参数类型是Text类,可以当成TextComponent,因为Text还有另外一种构造方法,传的为BaseComponent数组,这可能说明我们可以传入多行文本。
2.命令补全
之前所重载的onCommand方法固然方便,但是当玩家输入指令时,会不可避免地出现这个参数填什么,这个参数该怎么填的情况。于是一般的插件都会将一个指令做成一个类,再到主类去注册这些指令。
为什么需要在指令上大刀阔斧呢?因为有了命令补全,玩家在输入到某个指令时会有对应的提示,知道该输入什么。
这里以FastSpeed类作为示例,我们需要实现接口TabExecutor。
示例指令:fastspeed 。
我们需要实现TabExecutor中的两个方法:onCommand和onTabComplete。onCommand相信大家已经很熟悉了,onTabComplete方法就是我们所说的命令补全。
@Overridepublic List onTabComplete(CommandSender sender, Command command, String label, String[] args);
参数和onCommand一样,但是返回类型是List,是提示玩家的消息。
怎么提示玩家呢,当玩家准备或正在输入第一个参数player-name时,我们需要提示这里输入的是玩家名。以此类推,第二个参数要提示输入飞行的时间,第三个参数要输入是否飞行。
那么我们只需判断玩家当前正在或准备输入第几个参数就行了。方法中的第三个参数args就是玩家当前输入的所有参数,可以用arg.length判断已经输入了几个参数。
注:fastspeed本身不是参数,后面的才是参数。
@Overridepublic List onTabComplete(CommandSender sender, Command command, String label, String[] args) { if(sender instanceof Player) {//是否为玩家 if(args.length > 3) return null;//三个参数输入完了,不提示 if(args.length == 0 || args.length == 1) return Collections.singletonList(“请输入玩家名”);//没有参数,或者已有一个参数 if(args.length == 2) return Collections.singletonList(“请输入奔跑时间”);//以此类推 return Collections.singletonList(“是否飞行,仅限输入true或false”); } return null;}
最后我们需要在onEnable方法中注册这个指令:
Objects.requireNonNull(Bukkit.getPluginCommand(“fastspeed”)).setExecutor(new FastSpeed());Objects.requireNonNull(Bukkit.getPluginCommand(“fastspeed”)).setTabCompleter(new FastSpeed());
至此,命令部分暂时告一段落。
上一篇:我的世界Bukkit服务器插件开发教程(十三)资源包与玩家资料 下一篇:我的世界Bukkit服务器插件开发教程(十五)世界生成器(上)