文本来源(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 的渲染节奏下多数时候不显现,但不能认为线程安全。
适用位置
- 按钮(Button):
text/hoverText/tooltip - 文本(Text):
text/hoverText - 飘字(SplashText):
texts/file
相关条目
- 贴图来源(ITexture) - 与之平行的贴图解析
- 占位符替换 - 取到文本之后的二次加工