Linlang 高级字符串用于在普通文本中组合 Minecraft 的颜色、文本样式、悬浮信息和点击行为。
一段最简单的高级字符串如下:
点击这里[@color=yellow;click.run="/rs help"]
点击这里 是显示文本,紧随其后的 [@...] 是这段文本的描述符。描述符可以为空或省略,此时文本按原版样式显示。
一条高级字符串可以由任意数量的文本片段组成:
欢迎使用[@color=gray]琳琅[@gradient="#ff70c8,#70ddff";bold]![]
描述符只作用于它前方相邻的文本片段,不会向后继承。上例中:
欢迎使用 使用灰色。琳琅 使用渐变色并加粗。! 由空描述符 [] 明确标记为原版样式。只有 [@ 开始的中括号内容会被识别为高级描述符。因此,[VIP]、[系统] 等普通文本不需要转义。
[] 是片段边界,不会显示在游戏中。如果确实需要显示 [@ 或 [],请转义左中括号:
\[@color=red]
\[]
反斜线本身使用 \\ 表示。
同一片段的多个描述项使用分号 ; 分隔。@ 只写在开头的 [@ 中,后续直接写描述项名称;包含空格、分号或其他特殊字符的值应使用双引号 "" 包裹。例如:
this[@click.run="/gamemode 1";hover="gamemode 1"]
this[@click.run="/gamemode 1";@hover="gamemode 1"] 是错误写法:解析器会把 @hover 当作未知描述项。
支持 Minecraft 颜色或更多色彩(十六进制颜色):
警告[@color=red]
自定义颜色[@color=#70ddff]
可用的文本样式包括:
重要[@bold]
说明[@italic]
链接[@underlined]
删除[@strikethrough]
随机字符[@obfuscated]
样式也可以显式设置为 false,例如 [@bold=false]。
渐变与彩虹效果的写法如下:
琳琅[@gradient="#ff70c8,#70ddff"]
彩虹文本[@rainbow]
渐变至少需要两个颜色值,也可以按顺序提供更多颜色。运行时会按 Unicode 字符为文本计算颜色。
悬浮文本、插入文本和字体键分别使用:
查看说明[@hover="鼠标悬浮时显示的内容"]
玩家名称[@insertion="PlayerName"]
自定义字体[@font="minecraft:uniform"]
悬浮内容也会按高级字符串解析,可以继续使用颜色、样式和变量。嵌套深度上限为 8 层。不建议嵌套这么多。
keybind 和 translate 分别把片段解释为游戏按键与 Minecraft 翻译键,不能同时使用:
key.jump[@keybind]
item.minecraft.diamond_sword[@translate]
点击行为包括:
执行命令[@click.run="/rs help"]
建议命令[@click.suggest="/rs "]
复制文本[@click.copy="需要复制的文本"]
打开网页[@click.url="https://jling.me"]
执行命令行为以对应玩家身份执行;建议命令行为为玩家输入框填充建议的内容。复制文本行为会把文本内容加入玩家电脑的剪贴板中,这一行为可能被玩家电脑的权限拦截。
一个文本片段只能声明一个点击行为。click.* 和 hover 不会自动改变文字外观;若要让可交互文字更醒目,可以同时设置 color 和 underlined。click.run 仍以点击玩家的身份执行命令,玩家需要有相应权限,命令本身也须被当前服务器接受。语言文件中包含 click.run 或 click.url 时,应把该语言文件视为可信资源,不要把玩家输入直接作为高级文本使用。
LinText 是已经解析好的不可变组件树,不只是保存源码。配置和语言文件使用上面的语法;Java 代码既可以解析字符串,也可以逐段构造:
import api.linlang.text.LinText;
LinText help = LinText.of(
"点击查看帮助[@color=yellow;click.run=\"/rs help\"]"
);
LinText built = LinText.builder()
.append("点击查看帮助")
.color("yellow")
.runCommand("/rs help")
.build();
LinText.plain(value) 永远把内容当普通文本,不解析描述符。toPlainText() 用于搜索和日志,toLegacyText() 用于只接受旧式色码的接口;两者都无法保留点击和悬浮行为。跨服传输应使用 toJson() / fromJson(),并由网络协议明确约定组件格式与版本。
消息服务可以直接接受高级字符串:
messenger.send(
player,
"点击查看帮助[@color=yellow;click.run=\"/rs help\"]"
);
LangText 同样是一种文本来源,因此语言文件可以直接保存高级字符串:
public LangText success = LangText.of(
"操作成功[@color=green]"
);
success: '操作成功[@color=green]'
发送时直接传入语言字段引用:
messenger.send(player, lang.success);
消息服务会在每次发送时重新读取 LangText,普通语言重载后不需要重新注册消息模板。需要组合固定文本和动态语言字段时,使用 TextSource.join(...);它会在每次解析时读取各字段。LinText.builder().append(langField).build() 则会立即取得当前值,得到固定快照。
高级字符串继续使用 {name} 形式的命名变量:
give-item: '{player} 获得了 {item}'
messenger.send(
player,
lang.giveItem,
"player", player.getName(),
"item", itemName
);
其中,要替换的变量类型决定替换方式:
String、数字、布尔值和其他普通对象会作为普通文本插入,不会再次解析其中的描述符。LinText、LangText 等 TextSource 会作为独立高级文本片段插入。因此,将玩家名称作为普通字符串变量传入时,即使其中包含 [@click.run=...],也不会生成点击行为。需要让变量包含高级文本效果时,应显式创建 LinText:
LinText itemName = LinText.of("钻石[@color=aqua]");
messenger.send(player, lang.giveItem, "item", itemName);
在代码中也可显式区分两类变量:
TextArgs args = TextArgs.builder()
.plain("player", player.getName())
.rich("item", itemName)
.build();
LinText result = LinText.parse("{player} 获得 {item}").bind(args);
TextContext 还可带入指定语言。绑定会作用于可见文字、悬浮文字以及点击命令中的 {name};普通变量始终不会获得新的文本行为。
命令帮助可以使用 CommandOptions.desc(TextSource) 和 label(name, TextSource)。GUI 标题、图标名称和 Lore 可在配置中使用相同描述符与语言引用;这些位置最终降级为旧式文本,不支持点击或悬浮。命令参数标签最终作为纯文本显示。
使用 {{ 可以输出普通左花括号。未提供值的变量会保持原样。
琳琅发送给玩家的聊天消息使用组件,可以保留颜色、悬浮和点击行为。ActionBar 虽使用组件发送,但显示区域不能响应悬浮或点击;标题、GUI 文字及控制台使用旧式文本,只保留可表示的颜色与样式。纯文本化与旧式文本化是明确的能力降级,并非原组件的等价序列化。
这里的聊天是指琳琅消息服务主动发送给玩家的聊天消息,不等于玩家输入的原生聊天。运行时也可按位置选择解析玩家输入的高级字符串;配置、权限及各位置的能力限制见游戏内高级字符串 ⇱。
未知描述符、未闭合引号或非法颜色会让 LinText.parse(...) 抛出 IllegalArgumentException。通过 Messenger 发送时,解析失败被报告为 LIN-TEXT-PARSE-FAIL,不会误报为传输故障。语言文件加载时也会报告文件、语言和键名。开发者构造消息时,只应把可信的文件文本交给解析器;从命令参数等收到的玩家输入应作为普通变量传入。明确开启的游戏输入监听另受配置和权限控制。单段源码最多 32768 字符。
flowchart LR
Source["String、LinText 或 LangText"] --> Parse["解析或读取当前组件树"]
Parse --> Args["在组件树上绑定变量"]
Args --> Prefix["按消息策略组合前缀"]
Prefix --> Render["按目标能力渲染组件"]
Render --> Transport["由消息传输层投递"]
高级字符串本身只描述文本内容,不包含标题时长、消息通道、接收者或降级策略。这些信息属于消息服务,而不是文本描述符。
讨论