正文:
I. 开发环境配置
我们需要一个带mixin的forge开发环境,这一步相当折磨人,网络不好的话半天时间都得砸这上面,但是不要灰心,过了这个坎接下来基本是顺风顺水。
1. 下载资源 & 修改build.gradle
首先去forge官网https://www.mcbbs.net/plugin.php?id=link_redirect&target=https%3A%2F%2Ffiles.minecraftforge.net%2Fnet%2Fminecraftforge%2Fforge%2F下载一个1.12.2的mdk,将其解压
我选择解压到D:\LocalCodes\Minecraft\Example中,注意到解压出来的内容中有build.gradle和gradlew.bat两个文件,其他文件我们暂且忽略不管。先用记事本打开build.gradle
- buildscript {
- repositories {
- maven { url = 'https://maven.minecraftforge.net/' }
- mavenCentral()
- }
- dependencies {
- classpath 'net.minecraftforge.gradle:ForgeGradle:3.+'
- }
- }
- apply plugin: 'net.minecraftforge.gradle'
- // Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
- apply plugin: 'eclipse'
- apply plugin: 'maven-publish'
- ...
很长一串文本(Groovy语言,不去管他),接下来我们要对build.gradle文件进行一系列修改
按照上图进行修改,一共6处修改,务必确保每处修改都已完成。教程不对gradle进行深入阐述,但了解一些gradle相关的内容(不必完全掌握)会对你mod的项目管理有很大帮助,也能让你在部署开发环境的时候少走些弯路。
2. 运行gradle
接下来启动cmd,进入build.gradle所在的文件夹(我的话就是D:\LocalCodes\Minecraft\Example),调用命令
- gradlew build
初次运行会跑相当长一段时间,其间会下载各种游戏资源,并进行反编译。如果运行失败,并在log中显示了timeout字样,网络问题,多试几次说不定就好了。
运行成功后会出现BUILD SUCCESSFUL字样。上图是我运行gradlew build时的情况,因为之前已经下载过相关资源并反编译,所以速度很快。
注:你可能阅读过很多forge环境配置的教程,而每一篇都叫你调用gradlew eclipse之类的命令,然后说mod发布的时候调用gradlew build。一上来就build是在干什么?众所周知gradlew build是将src里的源码编译成能在生产环境(玩家的运行环境)下运行的jar,但运行gradlew build时还会在没配置过环境的电脑上“下载各种游戏资源,并进行反编”,教程正是利用了这个特点来配置无IDE的开发环境。
II. 编写你的第一个Mixin+Forge Mod
1. 写什么好呢?
我打算写个把地狱门边框从黑曜石改为木头的模组,大体效果是这样的
原先是搭黑曜石框点火启动,现在是搭木头框点火启动。
为了写出这个mod,我们得先知道mc里地狱门是怎么一回事,玩过mc的都知道怎么建地狱门,用黑曜石搭一个框,中间留出2x3的空白,然后用打火石点火即可,有经验的玩家还知道打雷引发的火,或者火焰弹引发的火也可以启动地狱门。事实上,用黑曜石搭一个合适的框,只要是火方块就能启动地狱门(在框内的空白中填充传送门方块)。教程专注于如何编写mixin+forge模组,在这里不对机制的代码基础进行阐述,相关内容参阅BlockFire.java、BlockPortal.java及Teleporter.java。
2. 第一个mixin
你会发现mixin指代了很多东西,有时候它指spongepowered开发的mixin library,同时它又指mixin library定义的一类文件mixin,所谓的mixin文件可以被理解为一种特殊的class文件,一定被@Mixin修饰,且在运行时被mixin library特殊处理。
我们先写个mixin对BlockPortal$Size.class进行修改,在src/main/java/com/example/examplemod下新建文件夹mixin,其下新建文件MixinBlockPortal$Size.java,用记事本打开,复制以下内容
package com.example.examplemod.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.objectweb.asm.Opcodes;
import net.minecraft.block.BlockPortal;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
@Mixin(BlockPortal.Size.class)
public abstract class MixinBlockPortal$Size {
@Redirect(method = "getDistanceUntilEdge", at = @At(value = "FIELD", target = "net.minecraft.init.Blocks.OBSIDIAN:Lnet/minecraft/block/Block;", opcode = Opcodes.GETSTATIC))
private Block proxy_getDistanceUntilEdge_getStatic_OBSIDIAN() {
return Blocks.LOG;
}
@Redirect(method = "calculatePortalHeight", at = @At(value = "FIELD", target = "net.minecraft.init.Blocks.OBSIDIAN:Lnet/minecraft/block/Block;", opcode = Opcodes.GETSTATIC))
private Block proxy_calculatePortalHeight_getStatic_OBSIDIAN() {
return Blocks.LOG;
}
}
这样我们的第一个mixin就写好了,教程不会详细讲述mixin如何写,如果对这块内容有需求,参见文末的拓展阅读。
3. mixins.example.json
有了mixin文件,我们还需要告诉mixin library该mixin的存在,这样它才能应用你的mixin。首先在src/main/resources下新建文件mixins.example.json,复制以下内容
{
"required": true,
"compatibilityLevel": "JAVA_8",
"package": "com.example.examplemod.mixin",
"mixins": [
"MixinBlockPortal$Size"
],
"client": [
],
"server": [
],
"injectors": {
"defaultRequire": 1
},
"refmap": "mixins.example.refmap.json"
}
接着在build.gradle的最后加上代码
mixin {
add sourceSets.main, 'mixins.example.refmap.json'
config 'mixins.example.json'
}
我们先暂且不管mixins.example.json以及这次对build.gradle的修改作用是什么,这块内容将在Part III 2.1(modid-1.0.jar结构分析)时提及。
4. 第二个mixin
现在写第二个mixin,在MixinBlockPortal$Size.java所处的文件夹下(即文件夹mixin)新建文件MixinTeleporter.java,复制以下内容
package com.example.examplemod.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.objectweb.asm.Opcodes;
import net.minecraft.world.Teleporter;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
@Mixin(Teleporter.class)
public abstract class MixinTeleporter {
@Redirect(method = "makePortal", at = @At(value = "FIELD", target = "net.minecraft.init.Blocks.OBSIDIAN:Lnet/minecraft/block/Block;", opcode = Opcodes.GETSTATIC))
private Block proxy_makePortal_getStatic_OBSIDIAN() {
return Blocks.LOG;
}
@Redirect(method = "placeInPortal", at = @At(value = "FIELD", target = "net.minecraft.init.Blocks.OBSIDIAN:Lnet/minecraft/block/Block;", opcode = Opcodes.GETSTATIC))
private Block proxy_placeInPortal_getStatic_OBSIDIAN() {
return Blocks.LOG;
}
}
接着修改mixins.example.json,在"MixinBlockPortal$Size"后加入, "MixinTeleporter",使之变为"MixinBlockPortal$Size", "MixinTeleporter"。
III. 发布!
别着急,mc模组从开发环境到生产环境(玩家的运行环境)之间还有一段路要走,视情况可能比开发环境配置还棘手。不过,在一般情况下就是一句gradlew build的事。
1. Build看看
cmd中执行
- gradlew build
成功build,到build/libs中拿结果(modid-1.0.jar文件)。