在 Minecraft 原版中,我们可以为文字定义一些特殊效果,例如鼠标移上去时显示介绍,单击复制到剪贴板……这不是什么魔法,事实上,实现这样的功能很简单。
这项功能叫原始 JSON 文本,如果不知道它,可以先阅读 Wiki 上的有关内容。
在 Bukkit 中,我们无法直接发送一个 JSON 对象到客户端,但这不算什么,有人已经为我们提供了等效的 API。
这一套 API 不是 Bukkit API 的一部分,它是由 BungeeCord 提供的。哦,别紧张,我们不需要额外的库。由于这个 API 很常用,它已经被 Spigot 和 Paper 等服务端集成了。
BungeeCord 的聊天 API 位于 net.md_5.bungee.chat
这个包下,看到 md_5
时你是不是「咯噔」了一下?没错,笔者一开始也想到了 4-1 时候的噩梦,不过后来发现,这是误会。md_5
是 Spigot 服务端的一位作者——BungeeCord 是 TA 开发的。
要说明的是,BungeeCord 的 JavaDocs 并不是 Paper 的一部分,它有自己的 JavaDocs:
另外,这里我们只是要实现聊天栏按钮这一功能,因此仅介绍此 API 的一部分。
BungeeCord 就 BungeeCord 吧,反正能够实现我们的功能就行。
我们要用到的是 net.md_5.bungee.chat.TextComponent
这个类。
下面我们演示如何创建一个链接,并且在玩家单击时让 Minecraft 将其打开。
BaseComponent url = new TextComponent("点我前往 Love-And-Tolerance 官网!");
// 创建一个文本组件
url.setHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT, // 动作:显示文本
new Text("https://love-tolerance.com/") // 内容:普通文本
));
url.setClickEvent(new ClickEvent(
ClickEvent.Action.OPEN_URL, // 动作:打开 URL
"https://love-tolerance.com/" // 要打开的 URL
));
url.setColor(ChatColor.GOLD);
// 设置颜色
Objects.requireNonNull(Bukkit.getPlayer("ThatRarityEG")).sendMessage(url);
// 发送!
我们先不谈它是什么原理,我们看看效果(1.16.5 原版,资源包是 Love-And-Tolerance,语言是 Modern Equish Full):
好,接下来我们解释这是怎么做到的。
首先,我们要转换一下思路,在 BungeeCord 开发者的眼里,一个文本可以被拆分成不可再分的小「块」,这个「块」就是 BaseComponent
,它是下面要说到的 TextComponent
的父类。
每一「块」具有这样的特点:
- 同样的颜色和样式(加粗、斜体等)
- 同样的行为(悬浮标签和点击操作等)
上面的例子中,整个 URL 是一「块」,因为它样式相同(金色),行为相同(悬浮显示 URL,点击打开 URL)。
分块之后,我们只需要分别创建每一块就可以了。
首先,实例化一个 TextComponent
:
BaseComponent url = new TextComponent("基本文本");
这里的文本是基本文本,稍后将对它们整体设置样式。
那我要是想分别设置每一个字的样式呢?
对不起,不行,这是一「块」,样式应当相同。如果需要单独设置样式,请将它接着拆分。
然后我们可以对其设置颜色与样式:
url.setColor(ChatColor.GOLD); // 颜色
url.setBold(true); // 加粗
url.setItalic(true); // 斜体
url.setUnderlined(true); // 下划线
url.setStrikeThrough(true); // 删除线
url.setObfuscated(true); // 变模糊
请留意一下,这里的 ChatColor
是 net.md_5.bungee.api.ChatColor
,不是 org.bukkit.ChatColor
。
接下来进入最有趣的部分。
setHoverEvent
设置当鼠标悬浮时要做的事。
这个方法需要一个 HoverEvent
对象,该对象的构造方法如下:
public HoverEvent(
HoverEvent.Action action,
Content content
)
action
表示「要做之事」,content
表示显示的内容。
说到「要做之事」,你可能会想到事件处理,很遗憾,这里不行,事实上 Minecraft 能做的事情很有限,那都有哪些事情呢?参考 HoverEvent.Action
枚举,我们看到:
public static enum Action {
SHOW_TEXT, // 显示文本
SHOW_ITEM, // 显示物品
SHOW_ENTITY; // 显示实体标签
}
实际上 content
的类型是与 action
有关的:,不能随便写:
- 如果
action
是SHOW_TEXT
,content
就得是new Text(...)
- 如果
action
是SHOW_ITEM
,content
就得是new Item(...)
- 如果
action
是SHOW_ENTITY
,content
就得是new Entity(...)
Text
,Item
和 Entity
都是 net.md_5.bungee.api.chat.hover.content
包下的,别导入错了!
我们逐一介绍。
Text
类的构造方法很简单:
public Text(String text)
提供一个 String
即可。
Item
用于基于 NBT 显示物品的信息,至于怎么获取 NBT……自己看着办吧(笑)。
public Item(
String id,
int count,
ItemTag tag
) // Item 构造方法
ItemTag tag = ItemTag.ofNbt(<NBT 标签>.toString());
// 如何制造 ItemTag,NBT 的获取可以使用反射
Entity
类用于显示一个实体的信息:
public Entity(
String type, // 类型,尚不明确
@NonNull String id, // getUniqueId().toString() 即可
BaseComponent name // 名字,也可以使用 BaseComponent,例如 new TextComponent("SHEEP_REALMS")
)
提供的 UUID 可能是与某个实体相关的……也不明确。
总而言之,虽然鼠标悬浮时可以显示文本、物品、实体,但只有文本才用起来方便。
setClickEvent
设置当点击时要做的事。
这个方法需要一个 ClickEvent
对象,该对象的构造方法如下:
public ClickEvent(
ClickEvent.Action action,
String content
)
和上面很像,只不过 content
变成了普通的 String
。
点击时能做的事同样很有限,可以参考 ClickEvent.Action
:
OPEN_URL, // 建议打开 URL
OPEN_FILE, // 打开文件,多人游戏无法使用
RUN_COMMAND, // 以玩家身份运行命令
SUGGEST_COMMAND, // 建议玩家使用命令
CHANGE_PAGE, // 换页,仅在书中可用
COPY_TO_CLIPBOARD; // 复制到剪贴板
OPEN_FILE
多人游戏中无法使用,因此不介绍。
OPEN_URL
用于建议客户端打开一个 URL,content
即为 URL 地址。
RUN_COMMAND
以玩家身份执行 content
(需要带 /
),这是聊天栏按钮最大的作用,你可以控制玩家执行一条命令,并使用命令处理器完成处理,这也是一种 CLI。
SUGGEST_COMMAND
将 content
打在玩家聊天窗口,但不默认发送,需要玩家确认。
COPY_TO_CLIPBOARD
单击将 content
复制到剪贴板。
CHANGE_PAGE
用于切换书页,仅用在 BookMeta
的 setPages
方法中。虽然页面是 int
类型,但这里还是要写成 String
:
new ClickEvent(ClickEvent.Action.CHANGE_PAGE, "3");
// 切换第三页
用这个方法做出每一「块」后,再将它们小心地收集到一起,以后就可以使用它们啦!
发送消息,设置书页,设置告示牌时都可以使用呢……而且这些方法通常都支持多个 BaseComponent
,依次填进去就行啦。
因此我们总结出使用奇特文本的方法:
- 创建
TextComponent
- 设置样式
- 设置悬浮事件:
new HoverEvent(HoverEvent.Action.XXX, new XXX(...))
- 设置点击事件:
new ClickEvent(ClickEvent.Action.XXX, "参数")
- 放到需要的地方去
就这些,很简单,对吧?配合命令使用,TextComponent
能够发挥出不小的威力呢~