文本来源(IText)

基本信息

属性 值
接口 lumien.custommainmenu.lib.texts.IText(唯一方法 String get())
分发器 GuiConfig.getWantedText(GuiConfig.java:429-442)
实现数 4

解析顺序

getWantedText 严格按以下顺序判断,先匹配先生效:

顺序 条件 实现类 行为
1 以 web: 开头 TextURL 去掉 4 字符,后台线程拉取 URL 内容当文本
2 以 file: 开头 TextResourceLocation 去掉 5 字符,从游戏资源管理器读文件全文
3 StatCollector.canTranslate(键) 为真 TextTranslatable 交原版本地化系统翻译
4 以上都不满足 TextString 原样当字面量

⚠️ 第 2 条 file: 分支会吞掉合法翻译键:想翻译的键名恰好以 file: 开头时不可能,但反过来——file: 后面写一个不存在的资源路径不会报错,get() 返回空串(见下)。 ⚠️ 判 canTranslate 意味着翻译结果依赖当前语言设置;换语言后同一个配置项会显示不同内容。

四个实现对比

实现 触发写法 数据来源 首次读取时机 失败表现
TextString 任意普通字符串 字面量 构造即固定 不可能失败
TextTranslatable 能被 canTranslate 认出的键(如 menu.singleplayer) StatCollector.translateToLocal 每次 get() 都重新翻译 键不存在时根本不会走到这里
TextResourceLocation file:<资源路径> Minecraft.getResourceManager().getResource(rl) 懒加载,首次 get() 时读并缓存全文 资源不存在 → string = null,get() 返回 ""(并永久缓存 null,不会重试)
TextURL web:<URL> LoadStringURL 守护线程 构造时立即起线程异步拉取 URL 格式错 → printStackTrace,url 保持 null

TextResourceLocation 的读盘细节

get() 用 \n 拼接所有行(TextResourceLocation.java:36-58),所以文件一行就是一个候选值——这正是 飘字(SplashText) 逐行随机抽取的基础。默认飘字源就是它,路径 texts/splashes.txt(无域 → 默认 minecraft 域)。

⚠️ 空文件会走 inputLine == null 分支,把字面量 "null" 追加进结果(TextResourceLocation.java:47)。

TextURL 的线程安全隐患

TextURL.string 被 LoadStringURL 守护线程写、被渲染线程读,但既没有 volatile 也没有正确加锁:

位置 代码
读(TextURL.get) String string = this.string; synchronized (string) { return this.string; }
写(LoadStringURL.run) String e = this.text.string; synchronized (e) { this.text.string = builder.toString(); }

两处都是锁在可变的 String 值上而非固定对象。写线程把 this.string 从 "" 换成新串后,锁对象随之改变;读线程此后锁的是另一个 monitor,与写线程互不排斥。属于经典反模式(对 String 加锁还有 String.intern 池冲突风险)。实际影响通常是"偶尔读到半更新的引用",在 1.7.10 的渲染节奏下多数时候不显现,但不能认为线程安全。

适用位置

相关条目