实体精华配置文件
基本信息
| 属性 | 值 |
|---|---|
| 配置目录 | config/ThaumcraftMobAspects/(常量 ModFileCache.CONFIG_FOLDER) |
| 文件粒度 | 每个 modid 一个文件 <modid>.cfg |
| 文件格式 | Gson pretty-printed JSON 数组(不是 Forge cfg 语法) |
| 读取类型 | ThaumcraftEntity[](源码注释:数组是必需的,Gson 处理 MultiMapSet 有问题) |
| 容器 | HashMultimap<String, ThaumcraftEntity>,键为 modid |
这些 .cfg 文件虽然用 Forge 的扩展名,里面装的却是 JSON 文本。
postInit 的处理顺序(决定优先级)
post() 里的步骤顺序不可交换,因为同名去重是"先到先得":
| 步骤 | 动作 | 结果 |
|---|---|---|
| 1 | 遍历 getModIds(),readFromFile 读入全部已有配置 |
最先写入容器 |
| 2 | put 两个玩家名默认条目 |
同名时第 1 步的胜出 |
| 3 | 6 个插件的 getThaumcraftMobs() 逐个 put |
同名时配置值覆盖硬编码值 |
| 4 | 遍历 EntityList 所有实体名,put 空精华条目 |
同名时被前面的胜出 |
| 5 | 遍历容器,saveToFile 写回每个 modid 的文件 |
落盘 |
| 6 | 遍历容器,registerEntityTag |
空列表跳过 |
所以玩家改配置文件能覆盖插件的硬编码值——这是本 mod 唯一的定制入口。 反过来,改了插件源码也不会覆盖玩家已有的配置(除非玩家先删文件)。
第 4 步的注释是 // Add Generic Entities that have no values:
它为所有已注册实体(包括别的 mod 的)建立空条目,
目的其实是把每个实体都写进它所属 modid 的文件里,方便玩家在其中添加精华。
这些空条目在第 6 步因 aspectList.size() > 0 而被跳过,不注册任何东西。
modid 的推断:guessModId
配置文件的命名完全依赖 guessModId(String entityName) 的四级回退
(ModFileCache.java:67-103):
| 级别 | 条件 | 返回 |
|---|---|---|
| 1 | 实体名含点 | entityName.split("\\.")[0] |
| 2 | 扫 EntityList.classToStringMapping,找同包且注册名含点的项 |
该注册名的第一段 |
| 3 | 命中 entityPackageToPrefix 手工表 |
vanilla / vanillaitem / unknown |
| 4 | 兜底 | 包名第一段;包名无点则 unknown |
手工表在构造器里预置 5 条(ModFileCache.java:28-32):
| 包名前缀(去掉了最后一段类名) | modid |
|---|---|
net.minecraft.entity.boss |
vanilla |
net.minecraft.entity.monster |
vanilla |
net.minecraft.entity.passive |
vanilla |
net.minecraft.entity.item |
vanillaitem |
net.minecraft.entity.player |
PLAYER_CATEGORY |
第 1 级只看第一个点之前的部分,所以
"TwilightForest.Block&Chain Goblin" → TwilightForest(空格和 & 在第一个点之后,不影响)。
第 2 级刻意排除原版(要求注册名含点),避免原版实体被误判成某个 mod。
文件名的合法字符过滤
filterModIdToValidCharacters(ModFileCache.java:52-54)用
CharMatcher.anyOf("qwertyuiopasdfghjklzxcvbnm0QWERTYUIOPASDFGHJKLZXCVBNM123456789").retainFrom(modId)
——字符集合同时包含大小写字母与数字,只剔除其余字符。
实际影响:
BiomesOPlenty、MoCreatures的大小写被保留(集合含两种大小写字母), 文件名仍是BiomesOPlenty.cfg;- 若 modid 含
_、.、-,这些字符会被静默删除, 如some_mod→somemod.cfg; - 该过滤只作用于文件名,实体名本身不受影响。
getFile() 惰性创建目录与空文件(getParentFile().mkdirs() + createNewFile()),
所以即使某 mod 一个实体都没有,也会留下一个空的 [] 文件。
源码缺陷
UNKNOWN_CATEGORY 与 PLAYER_CATEGORY 是同一个字符串。
ModFileCache.java:19-20:
public static final String UNKNOWN_CATEGORY = "unknown";
public static final String PLAYER_CATEGORY = "unknown";
两个常量值都是 "unknown",显然是复制粘贴时漏改。后果是
玩家条目与无法归属的实体被混写进同一个 unknown.cfg,
玩家无法通过文件名区分"这俩是我加的"还是"这俩是兜底进来的"。
the_iguana_man / XanderGryphon 就在这个文件里。
每条记录都带 3 个冗余字段。
ThaumcraftEntity 的 _HEADER、_FOOTER、_COMMENT 是实例字段(public final),
不是 static。Gson 按实例字段序列化,因此 JSON 数组里每一条记录都会重复输出:
"_HEADER": "*****************************************",
"_FOOTER": "*****************************************",
"_COMMENT": "Aspects are in the form aspect : value ",
这些常量从未被读取,只是无害的体积浪费(O(实体数) × ~130 字符)。
IguanaLog 定义了但全仓库零引用。 IguanaLog 类提供 log 字段,
grep -rn "IguanaLog" src/ 除自身文件外无任何命中。
实际错误处理全部走 e.printStackTrace()(ModFileCache.java:47、
ThaumcraftMobAspects.java:157、:168),不写进 Minecraft 日志文件。
dependencies.gradle 声明了未被使用的运行时依赖。
其中 runtimeOnly("com.github.GTNewHorizons:Baubles:1.0.4:dev"),
但 grep -rin "bauble" src/main/java 无任何命中——源码完全不涉及 Baubles。
@Mod 的 dependencies 串里也没有 Baubles,所以它不会被强制加载。
ModFileCache.java:24 有重复分号:new HashMap<String, File>();;
语法合法、无功能影响,属遗留笔误。
相关条目
- 精华赋值与注册 - 第 6 步的注册条件与空列表跳过
- 各 Mod 生物插件 - 第 3 步被配置文件覆盖的硬编码值