配置界面
基本信息
| 属性 | 值 |
|---|---|
| 工厂类 | com.github.lunatrius.core.client.gui.GuiFactory |
| 配置页 | com.github.lunatrius.core.client.gui.GuiModConfig |
| 模组列表改写 | com.github.lunatrius.core.handler.GuiHandler |
| 触发开关 | ConfigurationHandler.replaceInGameConfig(默认 true) |
本条目覆盖 3 个类,共同实现 @Mod 注解上 guiFactory = Reference.GUI_FACTORY 指向的整套 FML 配置界面。
GuiFactory implements IModGuiFactory
| 方法 | 实现 |
|---|---|
public void initialize(Minecraft minecraftInstance) |
空方法体 |
public Class<? extends GuiScreen> mainConfigGuiClass() |
return GuiModConfig.class; |
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() |
return null; |
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) |
return null; |
[!NOTE] 因为
mainConfigGuiClass()恒返回非 null 的GuiModConfig.class,GuiFactory自身不会被GuiHandler从模组列表中剔除。runtimeGuiCategories()与getHandlerFor()返回null表示本 mod 不提供运行时配置项。
GuiModConfig extends GuiConfig
| 成员 | 值 |
|---|---|
| 构造函数 | public GuiModConfig(GuiScreen guiScreen) |
| 父类第 2 参 | getConfigElements() — 静态方法动态生成 |
| 父类第 3 参 | Reference.MODID — 配置 GUI 的标题字符串 |
| 父类第 4 参 | false — parentChanged |
| 父类第 5 参 | false — worldGenEnabled |
| 父类第 6 参 | GuiConfig.getAbridgedConfigPath(ConfigurationHandler.configuration.toString()) — 底部显示的文件路径 |
分类页生成逻辑
private static List<IConfigElement> getConfigElements() 遍历 ConfigurationHandler.configuration.getCategoryNames(),对每个分类名构造一个 ConfigElement,并对分类对象调用 setLanguageKey(Names.Config.LANG_PREFIX + ".category." + name)。
因此分类页完全由配置文件内容决定:手动往 LunatriusCore.cfg 里加一个分类,刷新界面后就会多出一页。当前默认产生 2 页,即 versioncheck 与 tweaks。
GuiHandler
事件订阅
| 成员 | 值 |
|---|---|
| 订阅方法 | @SubscribeEvent public void onGuiOpen(final GuiOpenEvent event) |
| 事件类型 | net.minecraftforge.client.event.GuiOpenEvent |
| 注册总线 | MinecraftForge.EVENT_BUS(在 ClientProxy.registerTickers() 中注册) |
行为
条件判断为 ConfigurationHandler.replaceInGameConfig && event.gui instanceof GuiIngameModOptions,满足时执行 event.gui = new GuiModConfigList(Minecraft.getMinecraft().currentScreen)。
[!WARNING] 这里没有调用
event.setCanceled(true),而是直接改写event.gui字段。GuiOpenEvent本身是可取消事件,改字段是绕过取消机制直接替换目标界面。
私有内部类 GuiModConfigList extends GuiModList
| 项目 | 值 |
|---|---|
| 可见性 | private static class |
| 构造函数 | public GuiModConfigList(final GuiScreen screen),内部 super(screen) |
构造函数用反射取出 FML 模组列表并做过滤:
| 步骤 | 源码 |
|---|---|
| 1. 反射取字段 | ReflectionHelper.findField(GuiModList.class, "mods") |
| 2. 强转为列表 | (List<ModContainer>) fieldMods.get(this) |
| 3. 遍历并剔除 | 若 guiFactory == null || guiFactory.mainConfigGuiClass() == null 则 iterator.remove() |
| 4. 取工厂 | FMLClientHandler.instance().getGuiFactoryFor(mod) |
| 5. 异常处理 | catch (Exception e) → Reference.logger.error("Failed to tweak mod list!", e) |
[!IMPORTANT] 效果是:FML 原生的模组列表页中,所有没有实现配置界面的 mod 会被隐藏。这正是
replaceInGameConfig描述里「Replace the FML test config GUI with a working GUI」的实际含义——不是替换本 mod 的配置页,而是清理整个模组列表页。
[!NOTE]
ReflectionHelper.findField依赖GuiModList的私有字段名mods。若 FML 改名,该反射会失败,但已被 try-catch 兜住,只会打一条Failed to tweak mod list!日志而不崩溃。