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 现用 Forge 10.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 后丢失。