fix: 修复语言重载与聊天颜色边界#718
Conversation
Code Review — #718 fix: 修复语言重载与聊天颜色边界这个 PR 的几处修复都站得住,其中 需要讨论的主要是 审阅方式:读 patch + 对照源码 + 实测(资源匹配三场景、新旧颜色解析 16 组输入、命名颜色 alpha 位差异对下游的影响)。未实跑 gradle 测试。 🟢 先说三个值得点出的真 buga. val showType = formated(extra["type"].toString(), sender, *args)
when {
showType == "keybind" -> ...
// args:
// - type: translate:1:Stone ← 注释里的用法带参数
showType == "translate" -> appendTranslation(showText, *showType.substringAfter(':').split(':').toTypedArray())注释明确写了配置形态是 新代码用 b. 场景 2 是最严重的: 场景 1/3 的修法(比对 c. 颜色解析吞掉非法文本。 旧 新代码结构清晰得多:找到 🟡 问题 1 —
|
| 项 | 结论 |
|---|---|
translate 分支不可达 |
showType == "translate" 与注释里的 translate:1:Stone 形态矛盾;且相等时 substringAfter(':') 返回整串。双重错误,parseJsonType 修对 |
parseJsonType 的实现 |
split(':') 后 first 作类型名、drop(1) 作参数。"translate" → ("translate", []),"translate:1:Stone" → ("translate", ["1","Stone"])。语义正确 |
normalizeJsonValue 递归 |
处理 ConfigurationSection(走 getValues(false))、Map、Iterable、Array 四种容器并递归,标量原样返回。解决了嵌套结构被扁平化的问题 |
init 的异常处理改进 |
旧代码整块 try { ... } catch (_: ClassCastException) {} —— 任何一个 arg 结构不对就静默丢弃全部 args。新代码逐项 as? + return@forEach,只跳过坏的那一项 |
jsonArgs.clear() 的加入 |
旧 init 直接 addAll,同一 Type 实例被 init 两次会累积。reload 场景下 nodes 是新建的所以实际不会触发,但加上更稳 |
| hover 列表支持 | 旧 extra["hover"].toString() 对 List 会得到 [a, b] 字面串;新代码 is List<*> -> hoverText(hover.map { ... }) 走多行重载。真修复 |
findLanguageResource 的精确匹配 |
实测三场景(_extra 前缀误匹配、缺资源抛异常、非配置扩展名)全部复现,新实现三项都正确 |
| 缺资源不中断 reload | first { } → firstOrNull + warning + return@forEach。Kotlin.first 无匹配时抛 NoSuchElementException,在 forEach 内会中断整个循环。这是最有价值的一处修复 |
getTypeFromExtensionOrNull 存在 |
Configuration.kt:333 确认,用它校验扩展名合理(与配置模块的支持范围一致) |
| FileWatcher 回调改用新 map | 旧代码 it.nodes.clear() 后分两次 loadNodes,并发读会看到不完整状态;新代码构建 reloaded 后 replaceNodes 一次替换 |
replaceLanguageFiles 的类型分支 |
if (target is SnapshotHashMap) replaceWith else clear+putAll。保留了对非 Snapshot 实现的兼容(例如测试注入普通 HashMap) |
SnapshotHashMap 覆盖完整性 |
27 个方法全部覆盖,含 clone/equals/hashCode/toString。读方法委托快照,写方法基于快照复制后 set,不会漏读父类空状态 |
| 颜色解析的严格化 | 实测 16 组输入:#fff(旧 0x000FFF/新拒绝)、#gggggg、#ffffffff、1,2(旧 0x010200)、1,2,3,4、300,0,0(旧溢出 0x12C0000)、-1,0,0(旧 -65536) 全部由"静默错误值"改为"拒绝并保留原文"。真改进 |
| 六位 HEX 的正则校验 | #[0-9a-fA-F]{6} 精确六位。旧 substring(1).toIntOrNull(16) 会接受任意长度(#fff → 4095、#ffffffff → 溢出 null → fallback 0) |
| RGB 分量范围校验 | `component < 0 |
split("\\" + separator, -1) 的 limit |
-1 保留尾部空串,所以 "1,2," 得 3 项但第 3 项解析失败 → null。正确拒绝 |
| 命名颜色 alpha 差异不影响下游 | 实测 new Color(-1) 与 new Color(0xFFFFFF) 的 RGB 分量相同;Int.mix 按分量提取。三处调用点(TypeJson.kt:73、TextBlock.kt:80,151)都不受影响 |
RESET 与命名颜色的传统色码 |
StandardColors.match 分支保持在 parseColor 之前判断(translate 里 knownColor.isPresent() 优先),命名颜色仍走 toChatColor() 而非 hex。符合"保持传统颜色码行为"的描述 |
addLanguage 的 changed 判定 |
languageCode.add(value) 在 ` |
| 测试覆盖 | ColorParsingTest 覆盖 HEX/RGB/前导零/零值/非法表达式/原文保留;LanguageBoundaryTest 覆盖资源匹配、快照替换、parseJsonType、normalizeJsonValue。都是纯逻辑测试可在 CI 跑 |
总结
translate 分支不可达、缺资源抛异常中断整个 reload、颜色解析静默产生错误值,这三处都是实际影响功能的缺陷,修得对。clear()+putAll() 到原子引用替换的改造也解决了真实的并发可见性窗口。
建议处理:
- 问题 2(描述与代码不符)——「已从文件删除的语言键仍可能残留」在旧代码里找不到对应(
clear()在前)。真正修的是并发可见性窗口,建议修正描述,否则评审会去找不存在的 bug。 - 问题 3(
score/gradient未同步)——三种判断风格并存,建议统一到typeName,让parseJsonType的引入完整。 - 问题 1(
SnapshotHashMap继承 HashMap)——设计动机(保持HashMap声明类型的 ABI)我理解,但建议加注释说明"不支持序列化"与"JDK 升级需复查新增默认方法";另外replaceWith的注释说 CAS 而实现是set,建议改措辞。 - 🔵 c/d 两条(
#000000其实没被误判、trim()引入的容忍度放宽)建议在描述/兼容性说明里微调表述。
说明:本次审阅未实跑 gradle 测试(含 PR 描述列出的两条命令)。以下为本机实测:
ResourceReader资源匹配三场景(_extra误匹配、缺资源抛NoSuchElementException、非配置扩展名);新旧颜色解析 16 组边界输入对比;命名颜色 alpha 位差异对new Color(int)与Int.mix的影响。其余结论基于 patch 与仓库源码推导,已逐条注明依据位置。
原有问题
语言重载、JSON 语言节点和颜色解析在边界输入下会丢失结构或保留旧状态:
TypeJson的translate:指令分支不可达,嵌套对象、数组和 hover 列表在转换时可能被扁平化或改变类型。典型触发场景与后果
#000000、带前导零的 RGB、可变宽度分量或非法颜色表达式:黑色被误判、颜色值截断,或原始文本丢失。本 PR 修改
TypeJson的translate:解析顺序,并保留嵌套对象、数组和 hover 列表的原始结构类型。Locale当前没有 Issue 所述 Unicode/CJK 分类逻辑,因此未新增无调用方 API。修改目的
保证语言内容在复杂 JSON、连续重载和并发读取场景下保持结构完整与状态一致,同时让颜色边界输入可预测且不会破坏用户原文。
兼容性与行为变化
验证
./gradlew :module:minecraft:minecraft-chat:test :module:minecraft:minecraft-i18n:test --rerun-tasks --no-parallel./gradlew :module:minecraft:minecraft-chat:build :module:minecraft:minecraft-i18n:build --rerun-tasks --no-parallelgit diff --check(仅 Windows LF→CRLF 提示)Refs #703