解决EssentialsX中/nick颜色代码破坏经济消息格式的完整方案
问题现象与技术背景
当玩家使用/nick命令设置带有颜色代码的昵称后,经济相关消息(如/balance查询结果)出现格式错乱。典型表现为货币数值被错误染色、数字与玩家名重叠,或聊天窗口出现无意义字符。这一问题在EssentialsX 2.19.0+版本中尤为突出,根源在于昵称颜色代码未被正确隔离,导致格式化函数解析异常。
核心代码路径分析
1. 昵称处理机制
在Commandnick.java中,昵称设置通过formatString()应用颜色代码转换:
// 应用权限检查并格式化昵称
final String newNick = user == null ? FormatUtil.replaceFormat(nick) :
FormatUtil.formatString(user, "essentials.nick", nick);
当changeDisplayName为true时(默认配置),玩家的displayName会直接嵌入颜色代码,而非原始文本。
2. 经济消息构造流程
Commandbalance.java中的消息发送逻辑存在设计缺陷:
// 未过滤颜色代码直接拼接
sender.sendTl("balanceOther", target.getDisplayName(),
AdventureUtil.parsed(NumberUtil.displayCurrency(target.getMoney(), ess)));
target.getDisplayName()返回包含颜色代码的字符串(如§cSteve),与货币格式化结果直接拼接后,破坏了整体消息结构。
问题复现与环境验证
必要条件
- 服务器配置:
change-displayname: true(默认开启) - 玩家权限:
essentials.nick.changecolors - 操作步骤:
- 执行
/nick &6Admin设置金色昵称 - 其他玩家执行
/balance Admin查看余额
- 执行
预期结果
Admin的余额: $100.00
实际结果
§6Admin的余额: $§6100.00 // 货币数值被错误染色
深层技术原因
1. 颜色代码渗透
Minecraft使用§作为颜色代码前缀,但EssentialsX的NumberUtil.displayCurrency()未对输入文本进行净化:
// NumberUtil.java 未处理颜色代码
public static String displayCurrency(BigDecimal value, IEssentials ess) {
return new DecimalFormat(ess.getSettings().getCurrencyFormat()).format(value);
}
2. 配置连锁效应
config.yml中的关键设置形成冲突链:
change-displayname: true # 使用带颜色的displayName
nickname-prefix: '~' # 前缀与颜色代码叠加
ignore-colors-in-max-nick-length: false # 颜色代码占用字符长度
解决方案与实施指南
方案A:紧急规避措施(无需重启)
- 临时禁用颜色昵称权限:
/mangoperms user <player> revoke essentials.nick.changecolors
- 批量重置异常昵称:
/essentials:essentials reset nick all
方案B:配置优化(推荐)
修改config.yml关键参数:
change-displayname: false # 禁用带格式的显示名
ignore-colors-in-max-nick-length: true # 忽略颜色代码的长度计算
allowed-nicks-regex: '^[a-zA-Z0-9_]+$' # 禁止特殊字符
执行/essentials reload使配置生效。
方案C:代码修复(开发环境)
- 在经济消息中剥离颜色代码:
// Commandbalance.java 修复示例
String cleanName = FormatUtil.stripFormat(target.getDisplayName());
sender.sendTl("balanceOther", cleanName,
AdventureUtil.parsed(NumberUtil.displayCurrency(target.getMoney(), ess)));
- 添加货币格式化防御处理:
// NumberUtil.java 增强
public static String displayCurrency(BigDecimal value, IEssentials ess) {
String raw = new DecimalFormat(ess.getSettings().getCurrencyFormat()).format(value);
return FormatUtil.stripFormat(raw); // 确保数值无格式污染
}
效果验证与兼容性测试
验证矩阵
| 测试场景 | 预期结果 | 自动化测试用例 |
|---|---|---|
| 带颜色昵称的余额查询 | 玩家名去色,货币格式正常 | BalanceFormatTest#testColoredNick |
| 超长昵称(含颜色代码) | 截断时忽略颜色字符 | NicknameTest#testColorLength |
| 权限不足的颜色设置 | 自动过滤颜色代码 | NickCommandTest#testNoColorPerm |
版本兼容性
- ✅ 2.19.2 ~ 2.20.1:直接应用配置方案B
- ⚠️ 2.18.x及以下:需额外修改
FormatUtil.java的stripFormat实现
长期防护策略
1. 代码层面
- 实现
SafeDisplayName工具类,统一处理显示名净化 - 为消息模板添加参数类型标记(如
{displayName!clean})
2. 配置层面
新增防御性配置项:
# 建议新增的安全配置
message-sanitization:
strip-colors-from-economy: true
max-formatted-nick-length: 20
3. 权限层面
细化颜色使用权限:
# 安全的权限分配
essentials.nick.changecolors: false # 全局禁用颜色昵称
essentials.nick.changecolors.vip: true # VIP用户有限开放
问题排查工具包
1. 显示名调试命令
// 临时添加到Commandessentials.java
sender.sendMessage("Raw name: " + target.getName());
sender.sendMessage("Display name: " + target.getDisplayName());
sender.sendMessage("Clean name: " + FormatUtil.stripFormat(target.getDisplayName()));
2. 颜色代码检测器
# 服务器控制台执行
essentials:debug format "§6TestName"
输出:
Original: §6TestName
Stripped: TestName
Length (raw): 8
Length (clean): 8
总结与最佳实践
EssentialsX的昵称颜色与消息格式化冲突本质上是数据边界未隔离导致的典型问题。推荐采用"三明治防御模式":
- 输入过滤:严格验证昵称格式(
allowed-nicks-regex) - 处理隔离:业务逻辑中使用原始名称,展示层单独处理格式化
- 输出净化:所有玩家可见文本必须通过
stripFormat()净化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



