TooMuchLoot
[!INFO] Git Commit:
826cbaf| Updated: 2026-10-01
modid:TML(来自 TooMuchLoot.java:22 @Mod(modid = "TML", name = "TooMuchLoot", ...),
注意是全大写;gradle.properties 的 modId = TML 与之一致)
显示名: TooMuchLoot
mcmod.info 描述: “Making dungeon loot rare again…”
依赖: 仅 mod_MinecraftForge(mcmod.info),代码里无 dependencies 声明
现状:纯配置驱动的战利品表改写器,零内容
9 个 Java 文件里没有任何 registerBlock / registerItem / registerEntity /
registerTileEntity / Enchantment / Potion / BiomeGen。本 mod 不添加任何方块、
物品、实体或生物群系,它做的事只有一件:把 Forge ChestGenHooks 战利品表从 Java 代码
搬到 XML 配置文件里,让玩家能自己改。
| 项目 | 数量 |
|---|---|
| 方块 / 物品 / 实体 / TileEntity | 0 / 0 / 0 / 0 |
| 指令 | 1(/loot,权限 2) |
| 配置文件格式 | 1(LootGroup XML,3 种 loading_mode) |
| 事件订阅 | 0(只挂 FMLPreInitializationEvent 与 FMLServerStartingEvent) |
| 客户端代码 | 0(无 @SideOnly,无 guiFactory) |
实现机制
没有用 Mixin、没有 coremod,而是反射直接改写 Forge 私有静态字段:
ChestGenHooks.chestInfo(HashMap<String, ChestGenHooks>)与ChestGenHooks.contents(每个 hooks 实例的List<WeightedRandomChestContent>),两个字段setAccessible(true)后被永久反射持有(TooMuchLoot.java:50-68、31、39-40)- 任一字段取不到 →
failed = true,打FMLLog.bigWarning,serverStarting直接返回,/loot指令也不注册(TooMuchLoot.java:42、73-75) restoreCachedLootTable()靠重新copyLootTable一份新表再chestInfo.set(...)回滚 (ChestLootLoader.java:70-77)
⚠️ 这套方案把 Forge 内部私有字段名硬编码进 mod。若 Forge 版本改动
ChestGenHooks的字段布局,TML 会在 preInit 直接失效(表现为没有/loot指令),而不是崩溃。 GTNH 现用 Forge10.13.4.1614的这两个字段确实存在。
生命周期
| 阶段 | 动作 | 源码 |
|---|---|---|
| preInit | 建 config/TooMuchLoot/loot/generated/ 三级目录;反射取 chestInfo / contents |
TooMuchLoot.java:45-69 |
| serverStarting | 注册 /loot;缓存原版战利品表;收集全部 chest-gen 分类名;目录为空时导出原版表;加载全部 XML |
TooMuchLoot.java:72-105 |
init 与 postInit 完全没有实现——主类里只有 preInit 和 serverStarting 两个
@Mod.EventHandler。
游戏内内容 / 机制列表
- /loot 指令 - 权限等级 2,4 个子命令(reload / generate / reset / spawnDebugChest),外加
/loot generate <category> - loading_mode 三种加载模式 - OVERRIDE 覆盖 / ADD 追加 / REMOVE 移除,同分类只能覆盖一次
- LootGroup XML 文件格式 - 根元素属性 4 个、
<loot>条目属性 6 个,count 默认-1归一化规则
本 mod 没有的内容(源码核实)
src/main/java 下 registerBlock、registerItem、registerEntity、registerTileEntity、
addCommand 之外无 GameRegistry 注册;无 @SideOnly(Side.CLIENT);无 mcmod.info
以外的语言文件、贴图、模型(src/main/resources 只有 mcmod.info 一个文件);
无 mixins.*.json。无 @Mod 的 guiFactory。
已知源码问题
- 反字段名失败即静默半死:
copyLootTable的整个循环包在catch (Exception ex) {}里, 空 catch 体(ChestLootLoader.java:66)。若循环中途出错,函数会返回一个只含部分 key 的新表,/loot reset之后那些分类的战利品会被静默清空,且没有任何日志。restoreCachedLootTable的 catch 同样为空(ChestLootLoader.java:76)。 loadFiles用scanDir.listFiles(FILTER)但不过滤目录:loadFiles(lootFolder)(TooMuchLoot.java:104)扫的是loot/而非loot/generated/,所以首次自动导出的 原版参考文件不会被加载;但用户若把 XML 放进generated/则不会被扫到——两处行为都 与文档直觉相反,值得注意(ChestLootLoader.java:122)。lootFolder.listFiles()无 null 检查:TooMuchLoot.java:94在mkdirs()之后立即listFiles().length,权限不足导致mkdirs失败时直接 NPE。ChestLootCategory.checkCountValues()是死代码(ChestLootCategory.java:46-57), 见 loading_mode 末尾。generateFiles无条件覆盖已有文件:ChestLootLoader.java:93先file.delete()再写, 用户手动改过的generated/*.xml会在/loot generate后丢失。