堆栈反混淆与报告格式

基本信息

属性 值
映射来源 https://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/methods.csv
本地缓存 bettercrashes-stackdeobfuscator-methods.csv(工作目录下)
期望 SHA-256 63de115ead3e848e529de81e81ada9dff694e50fb5c6d92ec1e9037fe50ca191
开关 stacktraceDeobfuscation(默认 true)
实现类 vfyjxf.bettercrashes.utils.StacktraceDeobfuscator

映射获取与校验

StacktraceDeobfuscator.init(File)(:43-114):

  1. 文件已存在 → 直接读,不联网
  2. 文件不存在 → 下载,边写边用 Guava Hashing.sha256() 累计摘要
  3. 摘要与硬编码值不符 → 删文件、return,放弃反混淆
  4. 下载/解析异常 → logger.warn 后 return,放弃反混淆
  5. 全部成功才赋值 srgMcpMethodMap(注释:确保映射是完整的)

CSV 解析:跳表头,按第一个逗号切出 srgName, 再从其后取到下一个逗号切出 mcpName。 DEBUG_IN_DEV(硬编码 false)控制映射方向: false 时 SRG → MCP(生产),true 时反向(仅开发自测)。

堆栈改写

deobfuscateThrowable(Throwable)(:116-125)用 ArrayDeque 广度遍历 cause 链 与全部 suppressed 异常,对每个异常调用 deobfuscateStacktrace 重建整个 StackTraceElement[]。 deobfuscateMethodName 在映射表为 null 时原样返回(:139-141), 所以反混淆失败时行为退化为无操作。

报告格式改写

CrashReportMixin @Overwrite 了两个方法:

getCompleteReport()(:108-130):

  • 头部 // + 一行 witty comment(见下)
  • 额外插入 Time: yyyy-MM-dd HH:mm:ss z 一行(原版没有)
  • 用 Throwable.printStackTrace 的输出替换原版 getCauseStackTraceOrString,目的是打印 cause 与 suppressed(注释明说)
  • 分隔线为 87 个 -(for (int i = 0; i < 87; i++))
  • 末尾统一把制表符替换成 4 空格

getSectionsInStringBuilder(StringBuilder)(:143-150): 先把所有 section 逐个追加,每个后面补一个 \n,最后追加主 category。 与原版的差异是每个 section 之间多一个空行。

Witty comment 的彩蛋

betterCrashes$getVanillaFixComment()(:152-162): 有 1% 概率(Math.random() < 0.01)且已识别出嫌疑 mod 时, 取第一个 mod 的 authorList.get(0)(作者名), 返回 "I blame " + author + ".";否则回落到原版 CrashReport.getWittyComment()。

源码缺陷(源码核对 2026-10-01 审计)

  • ⚠️ 性能缺陷(高影响):CrashReportMixin.java:73-79 中 StacktraceDeobfuscator.deobfuscateThrowable(cause)(第 78 行) 在 if (BetterCrashesConfig.stacktraceDeobfuscation) 之外,无条件执行。 只有 init(...)(第 76 行)被开关保护。 ⇒ 即使玩家把开关设为 false,异常树中每个 throwable 的 StackTraceElement[] 仍会被全量重建(每个元素 new 一个新对象), 只是方法名原样返回。关闭开关省不掉这份开销。
  • ⚠️ 映射文件写在进程工作目录(new File(String.format("%s-stackdeobfuscator-methods.csv", MODID)), CrashReportMixin.java:76),不是 mcDataDir。 ⇒ 路径依赖启动方式,IDE 与启动器下位置不同。
  • ⚠️ 源码缺陷(死代码):StacktraceDeobfuscator.java:150-156 保留了 public static void main(String[] args) 测试入口, 里面 init(new File("methods.csv")) 后直接遍历 srgMcpMethodMap.entrySet() —— 若 init 失败(映射为空)此行 NPE。
  • ⚠️ 关闭反混淆时 deobfuscateThrowable 仍会遍历 cause + suppressed 全树, 深层异常链较多的崩溃点上会明显加重崩溃瞬间的卡顿。