ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Forge 1.20.1 模组开发全流程:安装、自定义方块到发布

Forge 1.20.1 模组开发全流程:安装、自定义方块到发布 简介Forge 1.20.1是面向Minecraft 1.20.1版本的模组开发工具包为Java开发者提供从环境初始化、代码编写到构建打包的完整支持特别适合希望给游戏添加自定义玩法、物品或机制的模组作者也适合想通过项目源码理解Forge工作方式的学习者。压缩包共17个文件仅111KB却覆盖了模组工程所需的全部基础模块Gradle构建配置与Wrapper脚本负责依赖管理、自动构建和跨平台执行Java源码与资源目录分离便于维护逻辑和静态内容属性文件、TOML配置及元数据文件声明构建参数、模组信息与资源行为版本控制规则保持代码库整洁变更日志、致谢名单、许可证和使用说明则分别记录更新、贡献、合规和上手指引。该模板对应Forge 1.20.1-47.4.2-mdk目前已有1443人学习下载是理解Forge MDK结构的理想入门样例。通过这套内容可以快速摸清源码、资源、配置之间的关联掌握Gradle在Java模组项目中的典型用法还能直接以它为起点搭建个人模组工程减少从零配置的时间成本并结合变更日志快速了解与上一版的差异更快进入核心功能设计与调试阶段。1. 把 forge-1.20.1 装成你的模组地基这条主线老玩家都绕不开如果你正在搜 forge-1.20.1多半是准备给 Minecraft 1.20.1 装模组或者干脆要开始做模组开发。MC Forge 的 1.20.1 版本几乎是模组圈子里公认的“稳定锚”——很多老牌核心模组只认真跟进到这一版再往后的新版本生态反而稀疏。它能解决两件事一是让普通玩家不用手动改 jar 包、一行命令就能挂上几十个模组二是给开发者一套完整的钩子注册方块、物品、实体时不需要改动 Minecraft 源码。这篇文章我会把安装、开发环境、注册第一个自定义方块、崩溃排查到打包发布全部过一遍过程和参数都是实际跑过的。适合从零开始的新手也适合上过车但总在某个坑里翻车的熟手。2. 安装 Forge 1.20.1安装器、服务端与客户端的三种打开方式2.1 先分清三种文件Installer、MDK 和 Server 包Forge 官网下载页打开后你会看到几个不同类型文件第一次接触的人很容易拿错。1.20.1 对应的下载列表里核心是forge-1.20.1-build-installer.jar这个安装器就是普通玩家和服务端管理员该用的东西。还有一个build-mdk压缩包是给开发者做模组用的 Gradle 工程模板常见后缀是.zip。最后有些页面会额外提供server.zip或client.zip本质上是从安装器生成的没有特殊需求就不必单独下载。判断自己该用哪个有一个很简单的标准你要“玩”模组就用 installer你要“写”模组就用 mdk你要开服还是用 installer 的--installServer参数。不少新手把 mdk 当成客户端安装包下下来发现没有install.bat以为是文件坏了其实是用错了入口。2.2 客户端安装一路点下去但要注意 Java 17客户端安装很简单但有个隐形门槛Forge 1.20.1 强制要求 Java 17低于这个版本安装器会直接报错。双击forge-1.20.1-build-installer.jar打开后选择Install client安装器会自动检测你机器上的 Minecraft 官方启动器目录。如果你用的是第三方启动器路径没有被自动识别就手动把.minecraft目录填进去点确定。安装完成后启动器里会多出一个版本命名通常是1.20.1-forge-build。启动时如果闪退最可能的原因是当前启动器默认的 Java 版本还是 Java 8 或 Java 11。在启动器里把该版本的 Java 可执行文件手动指向 JDK 17 就好。这里有个判断技巧启动日志里出现UnsupportedClassVersionError基本就是 Java 版本问题出现OutOfMemoryError才是内存问题别一上来就加 JVM 参数。2.3 服务端安装用命令行参数一次装干净服务端安装不推荐双击图形界面因为服务器通常在无桌面的 Linux 环境下。标准做法是在干净目录下执行java -jar forge-1.20.1-build-installer.jar --installServer后面的--installServer会让安装器把自己的服务端文件解压到当前目录并生成libraries文件夹、run.sh和run.bat脚本。完成后目录里会多出一个server.jar也可能叫minecraft_server.1.20.1.jar但实际启动不要直接java -jar server.jar而是要执行脚本。脚本内部会拼接多段 classpath 指向libraries手动方式容易漏依赖。安装器还支持几个实际用得上的参数。--javaPath可以强制指定 Java 可执行文件比如系统里装了多个 JDK 时可以这样写java -jar forge-1.20.1-build-installer.jar --installServer --javaPath /usr/lib/jvm/java-17-openjdk-amd64/bin/java--mirror可以指定镜像地址解决部分网络环境下载libraries慢的问题。但注意这个参数只影响安装器读取 library 的路径不能完全替代代理或换源方案。--target则可以把服务端安装到指定目录避免你提前cd。安装完成后第一次启动还要改两处把eula.txt里的eulafalse改成true再编辑user_jvm_args.txt填内存比如-Xms2G -Xmx4G。启动服务端用./run.sh看到Done (x.xxxs)!说明装好了。如果启动中途直接退出优先查看logs/latest.log最常出现的是Failed to find a suitable Java或者Invalid initial heap size前者是JAVA_HOME没设置到 JDK 17后者是user_jvm_args.txt里内存参数和机器不匹配。2.4 校验安装结果的三个指征装完先别急着塞模组用三个指征确认安装没问题第一启动器版本列表里出现1.20.1-forge-build说明客户端安装成功第二服务端目录下libraries/modules里能看到net/minecraftforge相关路径说明依赖齐全第三启动一次并进入游戏后按F3打开调试屏左上角或右下角显示Minecraft 1.20.1 / Forge字样说明加载链路通。如果这三个都对后面模组冲突排查就能少走一半弯路。3. 用 Forge 1.20.1 搭开发环境把 MDK 变成顺手的工作台3.1 MDK 解压后先分清哪些文件能改开发者用的 MDK 解压后是一个标准 Gradle 工程目录结构不复杂但很多人一上来就改错地方。核心有三个src/main/java和src/main/resources是放代码和资源文件的地方build.gradle控制构建与依赖gradle.properties承担部分环境变量配置。注意两者要区分开gradle.properties里通常写org.gradle.jvmargs-Xmx4G这类 Gradle 自身参数而build.gradle里的minecraft块才是配置 Forge 版本和 mappings 的关键区域。在你开始改build.gradle之前先确认 JDK 版本确实是 17。终端执行java -version如果是 8 或 11后面所有 Gradle 任务都会报错。另一个容易踩的是 Gradle 版本MDK 自带gradlew脚本和gradle-wrapper.properties一般情况下不要手动替换成系统安装的更高版本ForgeGradle 对新版 Gradle 的适配有滞后。3.2 build.gradle 里必须看懂的三个配置段下面是我习惯保留的一个最小骨架直接对应 1.20.1plugins { id eclipse id idea id net.minecraftforge.gradle version [6.0,6.2) } group com.example version 1.0.0 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 { examplemod { source sourceSets.main } } } server { workingDirectory project.file(run) property forge.logging.markers, REGISTRIES property forge.logging.console.level, debug mods { examplemod { source sourceSets.main } } } } } dependencies { minecraft net.minecraftforge:forge:1.20.1-build }逻辑上要搞清楚三件事mappings决定了你写代码时用的方法名和类名是“官方名”还是“旧版混淆名”。1.20.1 推荐channel: official这样BlockBehaviour、ResourceLocation这些名字和你下载到的 Forge 源码一致不装parchment也能正常编译。runs.client里的workingDirectory默认指向工程根目录下的run文件夹这个目录就是你的本地游戏存档与配置目录property开头的两行是 Forge 日志标记调试注册事件时有用平时可以保留。最后的dependencies中forge:1.20.1-build必须和安装器版本一致否则 Gradle 下载时找不到对应依赖。3.3 首次构建genSources 与 runClient配置完成后第一次执行要跑两个 Gradle 任务建议在终端里操作./gradlew genSources这个命令会下载 Minecraft 客户端官方源码并反编译到本地之后你在 IDE 里点进Minecraft类时就能看到方法体而不是一堆func_12345。MCP 时期的方法名混淆已经被废弃所以这一步在 1.20.1 下基本是稳定的不会出现卡半天没反应的情况。真正慢的是这一步背后的依赖下载国内网络建议先配置 Gradle 的仓库镜像否则容易超时。接着执行./gradlew runClientGradle 会编译主代码并启动一个带 Forge 的 Minecraft 实例这个实例的启动级别和你手动安装客户端一样但用的就是当前工程代码。第一次启动要加载资源可能黑屏两三分钟别以为死机。如果启动失败最优先看run/logs/latest.log里面Exception in thread main之前的那几行就是直接原因。一个常见问题runClient启动后游戏里看不到你的 mod。这通常不是代码问题而是mods.toml的modId和你Mod(examplemod)里的值不一致或者src/main/resources/META-INF/mods.toml根本没被资源处理。用./gradlew build编译一次再检查build/resources/main/META-INF/mods.toml是否存在可以快速定位。4. 从零写一个能跑的自定义方块跟着做一遍最稳的注册链路4.1 方块注册与物品关联缺一个环节方块就“不完整”这一节我们用 DeferredRegister 写一个带物品形态的普通方块。先创建主类并在构造函数里完成两个注册Mod(examplemod) public class ExampleMod { public static final String MODID examplemod; public static final DeferredRegisterBlock BLOCKS DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); public static final DeferredRegisterItem ITEMS DeferredRegister.create(ForgeRegistries.ITEMS, MODID); public static final RegistryObjectBlock EXAMPLE_BLOCK BLOCKS.register(example_block, () - new Block(BlockBehaviour.Properties.of() .strength(3.0f) .requiresCorrectToolForDrops())); public static final RegistryObjectItem EXAMPLE_BLOCK_ITEM ITEMS.register(example_block, () - new BlockItem(EXAMPLE_BLOCK.get(), new Item.Properties())); public ExampleMod() { BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus()); ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); } }逻辑说明方块本体和方块对应的物品是两套注册表BLOCKS管的是方块行为ITEMS管的是你手里拿的那份。如果不注册BlockItem游戏里最多只能通过指令放置方块但既不能放到你的物品栏也不能掉落。strength(3.0f)是硬度requiresCorrectToolForDrops()表示没有匹配的工具挖不掉。属性链来自BlockBehaviour.Properties不建议用被废弃的Block.Properties。这里还有一个注册时机的问题BLOCKS.register(...)和ITEMS.register(...)必须都挂在 mod event bus 上也就是FMLJavaModLoadingContext.get().getModEventBus()。如果把物品注册和方块注册挂到不同 bus后期可能出现物品注册顺序错乱导致某些依赖方块物品的配方无法被识别。4.2 模型文件与 blockstate路径对不上就白写代码注册只是第一步资源文件的路径必须严格对应。以example_block为例需要三个 JSONsrc/main/resources/assets/examplemod/blockstates/example_block.json{ variants: { : { model: examplemod:block/example_block } } }src/main/resources/assets/examplemod/models/block/example_block.json{ parent: minecraft:block/cube_all, textures: { all: examplemod:block/example_block } }src/main/resources/assets/examplemod/models/item/example_block.json{ parent: examplemod:block/example_block }与代码的对应关系是blockstates文件夹下的文件名必须等于注册名它决定了这个方块在不同状态时调用哪个模型models/block下是方块的实际模型textures里的all要指向assets/examplemod/textures/block/example_block.png否则渲染时直接出现紫黑格。models/item下则是物品栏里的显示模型直接继承方块模型即可。材料上要注意贴图尺寸必须是 16x16 的整数倍cube_all模型适合全方向同纹理的展示。如果你做的是复杂模型可以用minecraft:block/block做父模型但需要额外写elements数组很容易出 UV 拉伸建议第一版先从cube_all练手。4.3 给方块添加普通交互逻辑右键触发的控制台输出走到这一步方块已经能被放出来我们再给它一个最简单的逻辑右键时输出信息。修改第 4.1 节里的方块构造public static final RegistryObjectBlock EXAMPLE_BLOCK BLOCKS.register(example_block, () - new Block(BlockBehaviour.Properties.of() .strength(3.0f) .requiresCorrectToolForDrops()) { Override public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { if (!level.isClientSide()) { player.sendSystemMessage(Component.literal(Hello from example_block at pos.getX() , pos.getZ())); } return InteractionResult.sidedSuccess(level.isClientSide()); } });逻辑解释use是玩家右键方块时被调用的方法。level.isClientSide()判断当前是否是逻辑客户端我刚入行时常在这翻车——sendSystemMessage在服务端调用只能把消息发到服务器日志玩家看不到必须只在服务端执行发送然后返回sidedSuccess让本端继续处理。InteractionResult.sidedSuccess(level.isClientSide())的意思是如果是客户端返回成功如果是服务端也返回成功这样双方能一致判定响应。如果你要打开 GUI这里只需要在服务端调用player.openMenu(...)再配合MenuProvider接口逻辑上和这个模式一脉相承。先坚持把右键链路打通后面写复杂的交互时你会感谢现在这个“最小闭环”的调试思路。5. 常见问题排查Forge 1.20.1 启动崩、类找不到、资源不生效5.1 启动即崩溃不要盯滚动条去看 crash-reports 目录现象点击启动后窗口闪一下就退出控制台只有几行红字没有具体堆栈。原因Forge 客户端崩溃时绝大多数情况会生成完整崩溃报告但很多人习惯看启动器底部的滚动日志那里信息被截断真正原因在crash-reports文件夹里。解决找到.minecraft/crash-reports/下最新的.txt打开后搜索Caused by。比如Caused by: java.lang.NoClassDefFoundError: net/minecraft/world/level/block/Block这种直接说明有模组引用了旧版本或缺失的类。如果是java.lang.ExceptionInInitializerError大概率是某个模组在静态初始化阶段读配置失败。我的习惯是执行一条命令快速提取核心异常grep -A 20 Caused by crash-reports/*.txt | head -50然后根据异常类去对比当前加载的模组列表哪个模组依赖的 Forge 版本和你不一致优先从它下手。5.2 类找不到混淆映射与编译目标不一致现象开发环境下用runClient启动一切正常但打包成 jar 放到正常客户端后加载时报NoSuchMethodError或者ClassNotFoundException异常类里还有func_前缀的旧混淆名。原因打包时没有经过 reobf或者本地开发用的 mappings 与 Forge 发布到客户端的运行时映射不一致。Forge 1.20.1 的官方映射在编译时是“可读名”而客户端运行时是“官方混淆后名”需要由 ForgeGradle 自动转换。解决先确认build.gradle里mappings channel: official, version: 1.20.1没有写错再执行一次./gradlew build打包后到build/libs找到非sources的 jar用压缩工具打开查看net/minecraft/world/level/block下的类名是不是保留了Block这种名。如果出现func_12345说明 reobf 没执行成功需要检查工程的build/reobf目录是否存在。如果没有可以强制执行一次./gradlew reobfJar。另外不要把别人的模组源码直接编译后塞进自己的 jar每种 jar 对应的 mappings 可能不同硬塞必然出运行时报错。5.3 资源不生效改了 JSON 和贴图但游戏里始终紫黑格现象代码没问题blockstate 和 model 文件都写了但进游戏后方块显示紫黑色或者物品栏没有模型。原因最常见的三个——资源目录结构写错、文件名大小写不一致、贴图格式不是 PNG 或尺寸不对。解决先看build/resources/main/assets/examplemod/目录下是否生成了完整的blockstates、models、textures文件夹。没有生成说明 Gradle 没有把src/main/resources里的新文件覆盖到构建产物执行./gradlew build或者直接重启runClient。如果生成了还是紫黑格检查 JSON 里的路径和贴图文件实际路径是否完全一致特别注意textures路径不带.png后缀{ parent: minecraft:block/cube_all, textures: { all: examplemod:block/example_block } }对应文件必须是examplemod/textures/block/example_block.png。第三个容易忽略的问题是旧的资源缓存尤其在你切换过分支之后删除run目录下缓存的resourcepacks和已生成的方块缓存再重启。5.4 版本冲突同时装了 1.19.2 和 1.20.1 的 Forge 互相干扰现象启动器里明明有三个 Forge 版本但每次装新版本老版本进游戏就崩提示Wrong Minecraft jar。原因部分启动器没有开启版本隔离多个 Forge 版本写入了相同的libraries路径或者安装器在安装时误把1.20.1的 library 覆盖到了1.19.2版本里。解决优先使用官方启动器或支持版本隔离的第三方启动器确认每个版本有独立的libraries文件夹。在安装 1.20.1 时手动把安装目标指向当前启动器的versions目录不要让它自动选择。还有一个强迫症级别的做法换一个全新的.minecraft目录只安装 Forge 1.20.1等所有模组测试通过后再合并旧存档资源。多版本混装本质上还是 library 隔离问题别为了省空间而共用 libraries省下的空间不够填崩溃日志的。6. 把 mod 从 runClient 带到正式服打包、自检与发布前的三件事6.1 用 Gradle 产出可分发的 Mod JAR本地运行没问题不意味着能直接发给朋友。执行./gradlew build构建完成后build/libs下会出现三个文件examplemod-1.0.0.jar、examplemod-1.0.0-sources.jar、examplemod-1.0.0-api.jar如果配置了 api。真正要分发的是不带说明但没有重映射问题的第一个sources和api都不要发除非你是库作者。打开 jar 确认META-INF/mods.toml存在且modId、displayName、version与Mod入参完全一致。6.2 发布前三个自检用实例堵住低级错误推荐每次发布前跑一遍三个自检每项都有明确判断条件自检项操作方式通过标准物品可达性启动后runClient用/give s examplemod:example_block物品存在手持模型正常放置与交互将方块放到地上右键触发use控制台无NoSuchMethodError玩家看到自定义消息资源完整性使用资源包加载 jar 后检查日志Found invalid texture path不出现这三个自检覆盖了“注册 - 渲染 - 交互”三条主线对没有服务端逻辑的模组来说够了。如果有服务端交互再加一步登入服务器后用客户端测试放置和破坏看服务端latest.log是否同步输出。我自己栽过最大的跟头是发布前的uv设置忘了校验导致方块顶部纹理被拉伸评论区一周内追着问。从那以后我每次打包前都会强制走一遍上面自检进一个新存档、give 物品、放置、右键、存档关闭全部通过才会上传。这个过程看起来原始但能挡掉九成低级事故也让你对代码变更的影响范围有底气。希望帮到你。本文还有配套的精品资源点击获取
返回列表