恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Minecraft 1.20.1 Forge模组框架从零搭建完整指南
首页
资讯中心
/
Minecraft 1.20.1 Forge模组框架从零搭建完整指南
Minecraft 1.20.1 Forge模组框架从零搭建完整指南
发布时间:2026/10/18 2:06:08
Minecraft模组开发这两年越来越火身边不少朋友从玩整合包开始转向自己动手写模组但十个人里有六七个都栽在了第一步框架搭不起来。Forge作为老牌模组加载器生态最全、教程也最多可真正从零开始把构建脚本、主类加载、注册机制、资源文件这一整套流程串起来的完整教程反而少见多数人照着碎片文章折腾两三天最后连一个自定义物品都加不进去。这篇文章我直接用Minecraft 1.20.1 Forge 47.2.0这套组合为例手把手带你把Forge模组框架从空目录搭起来Wizard风格逐步走通配套完整源码我已经同步到Gitee仓库你可以边看源码边操作每一步都清楚自己在干什么。内容适合刚接触Forge开发、被零散教程搞晕的新手也适合想沉淀一套可复用模板、后面专注写玩法逻辑的开发者。1. 为什么还要自己搭一套Forge模组框架1.1 现成工具与自定义框架的取舍先说点掏心窝的话。现在做模组最快的方式确实是MCreator拖拖拽拽就能生成一个能跑的模组很多玩家用它做出的内容也挺漂亮如果你只是想做个简单的装备或维度MCreator完全够用。但它有个绕不开的短板生成出来的代码是固定的你想做自定义渲染、复杂合成逻辑、多人联机数据同步、服务端与客户端行为区分模板就很难伸展最后经常要手动去翻生成的一大堆类和混淆映射反而更痛苦。另一个常见路子是找个开源模组仓库把别人的骨架直接拷过来改。这办法我也用过但别人的骨架通常和具体玩法强耦合物品、方块、结构生成、音效、成就全揉在一起你要删干净得一层层剥很多时候删着删着就删出崩溃。拿过来改的代码风格也不是自己的维护起来心态很容易崩。所以我后来的习惯是把模组开发中所有模组都通用的那部分抽出来单独搭成一套框架比如主类、注册入口、事件总线订阅、配置系统、日志、资源文件目录结构这些基础结构稳定之后后面每次做新模组都从这套骨架上长肉只写增量内容不碰基础设施。1.2 一套合格模组框架必须具备的核心模块从我的实操经验看一个能反复用的Forge模组框架至少要包含六块内容。主类与生命周期入口用Mod注解标记的入口类负责在Mod构造阶段初始化DeferredRegister、订阅FML生命周期事件。注册体系基于DeferredRegister的物品、方块、实体、音效等注册入口延迟注册、按命名空间隔离这是整套框架的地基。事件总线接入区分ModBus模组加载阶段事件和ForgeBus游戏运行阶段事件框架需要把这两个总线的订阅方式固定下来形成约定。资源文件骨架mods.toml、语言文件、模型文件、材质目录、配方目录都要预先铺好避免后续每次新加物品都要手动建目录。配置文件雏形Forge自带的ForgeConfigSpec配置系统框架阶段就接上后面加数值、开关只需往里追加字段。构建脚本与运行环境build.gradle、gradle.properties、run目录约定、minecraft扩展块里的客户端/服务端运行配置。我见过不少人跳过这些直接写玩法写到一半发现不知道物品怎么注册到创造模式标签页或者模型文件路径错一个字母导致全是紫黑方块。框架的意义是把这些约定的东西一次配好、反复使用所以这一步不能省省掉的后面都会以更难看的方式还回来。1.3 为什么选1.20.1 Forge 47.2.0版本选择这件事我会单独说两句。目前社区里模组数量最多、教程最全、兼容性最稳的版本之一就是1.20.1Forge对应版本是47.2.x系列API相对稳定很多成熟模组都基于它踩坑参考资源也最多。选1.20.1还有个现实原因它要求Java 17这个版本是长期支持版本配置比Java 8时代干净很多而且ForgeGradle插件对Java 17的支持已经很成熟不会遇到老版本那种反复切JDK的窘境。如果你打算做联机模组1.20.1的服务端表现也比较可靠玩家群落仍然很大不会出现模组做完了没人玩这种尴尬。2. 开工前的环境准备与工具链选型2.1 JDK、Gradle、IDE的组合选择这套组合我在多台机器上实测下来很稳直接照抄即可组件推荐版本说明JDK17建议用发行版的LTS版本1.20.1强制要求Java 17过低会编译失败Gradle8.1.1左右由ForgeGradle插件管理手动装8.x即可ForgeGradle6.0.x在settings.gradle里通过插件声明Forge47.2.0对应Minecraft 1.20.1IDEIntelliJ IDEA社区版免费也可以用Eclipse但IDEA对Gradle工程支持更好小知识点Forge 1.20.1用的是官方映射表official mappings所以你在代码里看到的是minecraft、ServerPlayer这类正常方法名而不是远古时期那种func_12345混淆名。这也是我推荐新版本的原因之一看源码、搜教程都友好得多。2.2 Gitee源码仓库的结构说明与导入标题里说了这套源码已经放在Gitee我先把仓库目录结构摆出来方便你对照。forge-mod-framework/ ├── build.gradle # 构建脚本核心 ├── settings.gradle # 插件仓库配置 ├── gradle.properties # 版本号集中管理 ├── .gitignore # 排除build、run、.idea └── src/main/ ├── java/com/example/myframework/ │ ├── ModMain.java # Mod主类 │ ├── registration/ModItems.java # 物品注册入口 │ ├── item/ExampleItem.java # 示例物品类 │ └── config/FrameworkConfig.java # 配置文件雏形 └── resources/ ├── META-INF/mods.toml # 模组元信息 └── assets/myframework/ ├── lang/en_us.json ├── lang/zh_cn.json ├── models/item/example_item.json └── textures/item/example_item.png从Gitee拉取有两种方式一个是直接git clone整个仓库到本地然后在IDEA里用Open or Import选中build.gradle以Gradle项目导入另一个是下载ZIP包解压后导入。无论哪种注意导入后要等右下角Gradle同步完成第一次同步要下载大量依赖耗时取决于网络状况。仓库里的.gitignore我已经配好不会把build、run这些运行产生的临时目录提交上去你fork之后也建议保持这个习惯。2.3 国内网络环境下Gradle依赖下载的优化首次同步Gradle时卡在下载是几乎每个新手都会撞见的问题。ForgeGradle会从Maven仓库拉取Minecraft、Forge、映射表等一大堆文件总量经常高达几百MB甚至更多网速稍慢就卡到怀疑人生。我的做法是在settings.gradle的pluginManagement和repositories里加上阿里云Maven镜像具体见后面的构建脚本章节。这些配置不属于Forge框架本身但属于你没它活不下去的基础设施所以我提前放在这里强调如果你发现同步一直失败或极慢八成要先检查仓库URL配置而不是怀疑JDK装错了。3. 手把手从零搭建完整实操流程3.1 第一步生成基础工程有两种路径搭建起点有两条路我建议你优先选第一条。路线A直接用Gitee仓库里的完整工程这是最省事的方式仓库里已经是配置好、能直接Sync的完整Gradle项目。你clone下来之后只需要三步打开IDEA导入、等待Gradle同步完成、确认JDK是17。这条路线适合想快速跑通、后面慢慢理解细节的人。路线B从官方MDK手动搭如果你想像我一样彻底掌握每一层配置可以去Forge官网下载对应版本的MDKMod Development Kit解压后得到官方预设的build.gradle、src、gradle/目录然后按下一节的顺序手动替换配置。这条路线多花半小时但对后面排错帮助巨大我强烈建议至少完整走一遍。我自己的习惯是先走一遍B、理解全部配置后后续新项目直接改A里的模板。你可以根据自己的基础选但如果上来就是纯小白别硬刚B直接从A开始跑通了再回头研究细节。3.2 第二步配置构建脚本build.gradle这是整个框架里最核心的配置文件。我提供一个已经精简过、能直接用的版本关键位置都有注释。plugins { id eclipse id idea id maven-publish id net.minecraftforge.gradle version [6.0,6.2) } version 1.0.0 group com.example.myframework base { archivesName myframework } java.toolchain.languageVersion JavaLanguageVersion.of(17) minecraft { mappings channel: official, version: 1.20.1 runs { client { workingDirectory project.file(run) property forge.logging.markers, REGISTRIES property forge.logging.console.level, debug mods { myframework { source sourceSets.main } } } server { workingDirectory project.file(run) property forge.logging.markers, REGISTRIES property forge.logging.console.level, debug mods { myframework { source sourceSets.main } } } data { workingDirectory project.file(run) property forge.logging.markers, REGISTRIES property forge.logging.console.level, debug args --mod, myframework, --all, --output, file(src/generated/resources/), --existing, file(src/main/resources/) mods { myframework { source sourceSets.main } } } } } repositories { maven { name Aliyun url https://maven.aliyun.com/repository/public } maven { url https://jitpack.io } } dependencies { minecraft net.minecraftforge:forge:1.20.1-47.2.0 } jar { manifest { attributes([ Specification-Title : myframework, Specification-Vendor : example, Specification-Version : 1, Implementation-Title : project.name, Implementation-Version: project.jar.archiveVersion, Implementation-Vendor : example ]) } }几个关键点我拆开讲一下。mappings channel: official这行决定你用官方映射还是MCP映射。新手直接用official最省心代码里的方法名都是你看得懂的名字如果选parchment这类带Javadoc注释的映射写起来更舒服但需要额外配置这里不展开。runs块里定义了三种运行配置client启动客户端、server启动服务端、data用于Gradle数据生成器生成配方和模型文件。注意mods块里的名称myframework要和后面mods.toml里的modId一致否则运行时Forge会报找不到mod源这个问题我见人踩过很多次。依赖配置里minecraft行指定Forge版本格式是Minecraft版本-Forge版本注意是47.2.0不是47.2括号里多了个0写错会直接搜不到依赖。如果你从Gitee仓库fork版本号集中在gradle.properties里管理改起来更方便。3.3 第三步配置模组元信息mods.tomlsrc/main/resources/META-INF/mods.toml是Forge识别你模组的身份证。文件格式是TOML看起来简单但几个字段的大意你得搞清楚。modLoaderjavafml loaderVersion[47,) licenseMIT [[mods]] modIdmyframework version1.0.0 displayNameMy Framework authorsYourName description A clean Minecraft Forge mod framework. [[dependencies.myframework]] modIdforge mandatorytrue versionRange[47,) orderingNONE sideBOTH [[dependencies.myframework]] modIdminecraft mandatorytrue versionRange[1.20.1,1.21) orderingNONE sideBOTHmodLoader固定为javafmlloaderVersion写[47,)表示要求Forge加载器版本大于等于47。[[mods]]段里modId是全模组的唯一标识我用的myframework你在自己的项目里一定要改成独特的名字因为玩家同时装很多模组时modId冲突会导致注册混乱甚至崩溃。[[dependencies.myframework]]段声明这个模组依赖Forge和Minecraftmandatorytrue意思是缺了就不让启动sideBOTH表示客户端和服务端都需要。如果你以后写只影响客户端的模组这里可以写sideCLIENT。3.4 第四步创建主类与生命周期入口主类是整个模组启动的钩子。Forge加载模组时会通过modId找到有Mod注解的类并实例化构造方法里执行的所有初始化逻辑都属于模组加载阶段。package com.example.myframework; import com.example.myframework.registration.ModItems; import com.mojang.logging.LogUtils; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.slf4j.Logger; Mod(ModMain.MODID) public class ModMain { public static final String MODID myframework; public static final Logger LOGGER LogUtils.getLogger(); public ModMain() { IEventBus modEventBus FMLJavaModLoadingContext.get().getModEventBus(); ModItems.ITEMS.register(modEventBus); MinecraftForge.EVENT_BUS.register(this); LOGGER.info(My Framework initialized); } }这段代码有四个动作值得关注。Mod(ModMain.MODID)注解里的值是modId必须和mods.toml里的modId完全一致。构造方法里先把modEventBus取出来这是模组加载阶段的专用事件总线。ModItems.ITEMS.register(modEventBus)把我们后面要写的物品注册表挂载到事件总线上这一步是注册机制的心脏。MinecraftForge.EVENT_BUS.register(this)把主类自身注册到游戏运行阶段的Forge总线以后可以直接在主类里写SubscribeEvent方法监听游戏事件。我见过不少新手在构造方法里写SubscribeEvent方法然后发现事件永远触发不了原因是SubscribeEvent只能用在已经注册到总线上的对象里而主类构造阶段总线还没完成注册。如果你只在主类里写SubscribeEvent必须同时调用MinecraftForge.EVENT_BUS.register(this)否则事件静默丢失。3.5 第五步编写配置类框架配置文件这一步很多人会跳过但我建议框架阶段就接上后面加东西真的会方便很多。下面是一个最小的ForgeConfigSpec配置类。package com.example.myframework.config; import net.minecraftforge.common.ForgeConfigSpec; public class FrameworkConfig { public static final ForgeConfigSpec.Builder BUILDER new ForgeConfigSpec.Builder(); public static final ForgeConfigSpec SPEC; public static final ForgeConfigSpec.BooleanValue EXAMPLE_TOGGLE; public static final ForgeConfigSpec.DoubleValue EXAMPLE_RATE; static { BUILDER.comment(Framework example settings).push(general); EXAMPLE_TOGGLE BUILDER .comment(Enable example feature) .define(exampleToggle, true); EXAMPLE_RATE BUILDER .comment(Example rate value) .defineInRange(exampleRate, 1.0, 0.0, 10.0); BUILDER.pop(); SPEC BUILDER.build(); } }使用的时候在主类构造方法里注册ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, FrameworkConfig.SPEC);这个配置系统会在run/config目录下生成一个myframework-common.toml文件玩家可以直接编辑。框架阶段就把配置接口铺好后面做玩法时只需要往BUILDER里追加字段省很大事。4. 框架核心机制拆解注册、事件与生命周期4.1 两条事件总线千万别搞混Forge的事件系统是模组开发最容易绕晕的部分因为它有两条总线一条是FMLJavaModLoadingContext.get().getModEventBus()简称Mod总线另一条是MinecraftForge.EVENT_BUS简称Forge总线。简单理解Mod总线处理的是模组加载过程中发生的事比如注册物品、注册方块、注册实体、配置加载等这些事件发生在游戏启动早期顺序敏感Forge总线处理的是游戏运行过程中持续发生的事比如玩家加入、怪物生成、方块破坏、按键点击等。新手最常见的错误是把两类事件监听放错总线。比如用SubscribeEvent监听RegisterEvent属于Mod总线却注册到了Forge总线上结果你发现注册逻辑永远不执行。反过来如果你把玩家登录事件挂到Mod总线上那个方法在启动阶段就结束了玩家上线时根本不会触发。我的经验是注册相关事件永远走Mod总线游戏玩法相关事件永远走Forge总线写代码前先想清楚自己要监听的是加载时还是运行时。4.2 注册机制DeferredRegister的工作方式很多刚接触Forge的开发者会困惑为什么新版本模组里看不到RegistryEvent.RegisterItem这种老式注册方法了因为Forge在新版推荐使用DeferredRegister它其实是个延迟注册的封装你调用ITEMS.register(example_item, () - new Item(...))时并不会立刻注册物品而是把工厂函数存起来等到Mod总线上的注册事件真正触发时也就是游戏启动、注册表准备就绪的时刻才批量执行。这样做有两个好处一是代码顺序和解耦你可以在任意位置声明注册不用关心注册时机二是RegistryObject返回值是延迟填充的你可以在注册之前就把物品引用赋值给静态字段Forge会在合适的时机关联真实对象。下面是我们框架里的物品注册入口和主类配合就能跑通整个注册流程。package com.example.myframework.registration; import com.example.myframework.ModMain; import net.minecraft.world.item.Item; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; public class ModItems { public static final DeferredRegisterItem ITEMS DeferredRegister.create(ForgeRegistries.ITEMS, ModMain.MODID); public static final RegistryObjectItem EXAMPLE_ITEM ITEMS.register(example_item, () - new Item(new Item.Properties())); }注意DeferredRegister.create第一个参数传的是ForgeRegistries.ITEMS这是Forge提供的物品注册表第二个参数是命名空间也就是modId。注册出来的物品完整注册名是myframework:example_item中间的冒号在Minecraft里是命名空间与路径的分隔符所有模组内容都要遵守这个约定这也是多个模组能共存而不冲突的底层机制。4.3 物品、方块、实体的注册范式对比物品注册最简单方块和实体稍微复杂一点。方块注册通常要同时注册方块本身和方块对应的物品形式实体则要注册实体类型和渲染器。我把三种最典型的注册范式放在一起类比注册目标DeferredRegister的类型额外步骤物品ForgeRegistries.ITEMS无需额外步骤直接new Item方块ForgeRegistries.BLOCKS注册Block时再注册BlockItem实体ForgeRegistries.ENTITY_TYPES客户端还要注册EntityRenderer以方块为例你会在方块注册类里看到类似这样的代码public static final RegistryObjectBlock EXAMPLE_BLOCK BLOCKS.register(example_block, () - new Block(BlockBehaviour.Properties.copy(Blocks.STONE))); public static final RegistryObjectItem EXAMPLE_BLOCK_ITEM ITEMS.register(example_block, () - new BlockItem(EXAMPLE_BLOCK.get(), new Item.Properties()));BlockItem就是玩家手里拿着能放置的方块物品它和方块是两个注册条目但注册名通常保持一致这样Minecraft才认得这个物品对应哪个方块。实体的流程更复杂涉及EntityType.Builder、属性、生成规则后面做玩法时再扩展框架阶段先把这种一个内容多条注册链的思路建立起来就够用了。4.4 客户端与服务端的安全分离写模组时一定会遇到一个经典问题客户端独有的类渲染、按键、GUI在服务端不存在如果你在共用代码里直接引用服务端启动会直接崩。Forge的约定是用OnlyIn(Dist.CLIENT)注解标记客户端专用方法或类同时用DistExecutor.safeRunWhenOn(Dist.CLIENT, ...)做安全调用。以客户端事件监听为例官方推荐的方式是单独建一个客户端事件类并在类上直接标注Mod.EventBusSubscriber(modid ModMain.MODID, bus Mod.EventBusSubscriber.Bus.FORGE, value Dist.CLIENT)。这个注解省去了手动注册的步骤Forge发现这个类后会自动把它注册到对应总线上同时只会在客户端环境加载。我在Gitee源码里放的框架预留了一个event/ClientEvents.java空类就带了这个注解方便你以后直接往里填客户端逻辑。这种约定说起来很简单但新手经常在图省事时直接在共用类里调客户端方法导致服务端启动即崩。框架阶段把分离机制铺好能省下一堆调试时间。5. 用示例物品验证框架顺便打通资源文件5.1 创建一个简单示例物品类框架搭好之后必须验证全链路能跑通否则空转没有意义。我选择的验证方式就是往框架里加一个自定义物品因为一个物品从注册到模型、材质、语言文件跨越了几乎全部关键环节任何一环断了都会有明显表现。这里创建一个带右键效果的示例物品简单但足够验证机制。package com.example.myframework.item; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; public class ExampleItem extends Item { public ExampleItem(Properties properties) { super(properties); } Override public InteractionResultHolderItemStack use(Level level, Player player, InteractionHand hand) { if (!level.isClientSide) { player.sendSystemMessage(net.minecraft.network.chat.Component.literal(Hello from My Framework!)); } return InteractionResultHolder.sidedSuccess(player.getItemInHand(hand), level.isClientSide); } }注意use方法里最后返回的sidedSuccess第一个参数是玩家手里的物品栈第二个参数是level.isClientSide()的判断结果。这样客户端显示动画、服务端执行逻辑两边不会重复触发。如果你忘记这个判断右键物品时消息可能会在客户端和逻辑端各发一次双端联机时会出现重复消息。然后把ModItems里的匿名Item改成使用这个类public static final RegistryObjectItem EXAMPLE_ITEM ITEMS.register(example_item, () - new ExampleItem(new Item.Properties().stacksTo(16)));stacksTo(16)限制堆叠上限这是物品属性里很常用的配置食物、工具、材料各有不同的堆叠策略框架阶段先用一个普通物品验证即可。5.2 模型与材质文件紫黑方块的克星物品注册成功但模型缺失时游戏里会渲染成经典紫黑方块同时日志里刷一堆找不到模型/材质的警告。这是新手最常撞见的视觉问题九个里有八个都是路径写错。Minecraft的模型加载遵循严格的路径规则以命名空间myframework为例物品模型文件必须放在assets/myframework/models/item/example_item.json材质文件放在assets/myframework/textures/item/example_item.png模型的纹理字段引用路径时也要保持一致。最简模块化物品模型文件长这样{ parent: minecraft:item/generated, textures: { layer0: myframework:item/example_item } }这里parent: minecraft:item/generated表示使用最通用的平面物品渲染纹理通过layer0指定。myframework:item/example_item会自动映射到assets/myframework/textures/item/example_item.png。你只要保证四个路径一致JSON文件名等于未注册路径、材质文件名等于JSON里引用的纹理路径、JSON文件路径正确、材质文件真实存在紫黑方块就会消失。材质文件本身建议用带透明通道的PNG尺寸通常是16x16像素命名全部小写字母加下划线。很多人直接用一个占位材质后面再做像素画这是很聪明的做法先把链路跑通美术和玩法是后话。5.3 语言文件给物品起个正常名字物品虽然能显示但名字没配语言文件的话游戏里会显示成item.myframework.example_item这种原始注册名非常丑陋。语言文件放在assets/myframework/lang/en_us.json和zh_cn.json内容格式如下。{ item.myframework.example_item: Example Item }{ item.myframework.example_item: 示例物品 }键名的格式是item. 命名空间 . 注册路径这是一个固定的拼接规则。方块对应block.开头实体对应entity.开头模组名等一般用itemGroup.开头。语言文件支持UTF-8编码但在Windows下保存时容易被改成GBK导致游戏里中文变乱码我的经验是IDE里设置File Encoding为UTF-8JSON文件另存时也显式选UTF-8不要用系统默认。6. 常见问题与排查技巧实录6.1 环境与构建阶段的高频问题这一节我专门把实际跑这个框架时真会遇到的报错整理成速查表每一类都附上我自己的排查思路。现象可能原因处理方式Gradle同步卡在Download依赖仓库访问慢配置阿里云Maven镜像或等待重试编译报错cannot find symbol: class Logger依赖没同步完整先清Gradle缓存再重新同步Mod类找不到或modId不匹配mods.toml与主类注解不一致核对modId大小写必须完全一致启动游戏时提示缺少依赖modmods.toml的dependencies配置错误检查[[dependencies.myframework]]段运行时黑屏闪退事件监听放错总线检查SubscribeEvent注册的总线物品名字显示成item.xxx语言文件缺失或键名错误核对lang目录及键名拼写物品显示紫黑方块模型文件或材质路径错误按5.2节核对四条路径6.2 日志与崩溃报告怎么看遇到问题第一反应不是瞎猜而是看日志。Forge的运行日志默认写在run/logs/latest.log崩溃信息会写一个独立的crash-reports/目录文件名带时间戳。我的习惯是崩溃后先看第一屏的Description和Exception再把堆栈里带mods.或com.example前缀的行单独抠出来分析这通常就是出错位置。大量模组玩家上传的崩溃报告其实都是这种半截日志真正有用的只有前几十行和最后三十行。日志里还有一个坑INFO级日志很多真正的错误往往混在中间。建议先在IDEA里直接搜ERROR、Exception、Caused by这三个关键词。Caused by后面经常跟着真正的原因比如NoSuchMethodError那八成是版本不匹配FileNotFoundException才是路径问题。排查时按先版本、再路径、后逻辑这个顺序走能省很多时间。6.3 团队协作与源码管理建议Gitee源码仓库同步这件事我还要啰嗦几句。如果你把这个框架用于团队或长期项目建议从一开始就维护好.gitignore至少排除build/、run/、.gradle/、.idea/、*.iml这几个目录。我之前见过团队里有人把run目录整个提交上去里面带着几百MB的世界存档仓库瞬间臃肿克隆一次痛苦一次。另外建议在gradle.properties里统一管理Minecraft版本、Forge版本、模组版本号不要散落在各个文件里。这样切换Minecraft版本时只改一处重新同步Gradle即可。如果你基于Gitee仓库fork了这套框架记得改掉group、archivesName、modId这些标识字段否则发布到社区时会和其他人的模组冲突低级但致命。6.4 几个调试习惯和心态建议踩过足够多的坑之后我总结了几个调试习惯分享给你参考。第一每次新加内容先跑一次客户端确认加载无误再继续加下一个不要一口气写完几十个类再启动否则报错时压根不知道是哪一环断了。第二善用LOGGER.debug而不是System.out.println日志系统自带时间和等级过滤比控制台打印好排查得多。第三改动build.gradle、mods.toml这种配置文件后要重新同步Gradle再启动直接点运行会加载旧配置表现就是改了没反应很多人被这个假象骗了很长时间。还有一点心态上的体会模组开发绝大多数报错都是路径错误、版本失配、总线挂错这三类没有任何一个是你学不会的。遇到报错先按这三类排查90%的问题都能自己解决剩下10%再把日志贴到社区求助但记得带上完整日志和版本信息别只问为什么崩了。把框架搭稳、把日志看懂模组开发的自由度会突然大很多后面做玩法和内容就都不难了。