新建一个主类
新建一个包(这里是com.github.ustc_zzzz.fmltutor
),并在其中新建一个类(强烈建议这个类的类名和你的Mod名称相同),这就是这个Mod的主类了。
把下面的代码抄进你新建的类中,下面我会解释为什么要这么做。
src/main/java/com/github/ustc_zzzz/fmltutor/FMLTutor.java:
package com.github.ustc_zzzz.fmltutor;
import com.github.ustc_zzzz.fmltutor.common.CommonProxy;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.Mod.Instance;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
/**
* @author ustc_zzzz
*/
@Mod(modid = FMLTutor.MODID, name = FMLTutor.NAME, version = FMLTutor.VERSION, acceptedMinecraftVersions = "1.8.9")
public class FMLTutor
{
public static final String MODID = "fmltutor";
public static final String NAME = "FML Tutor";
public static final String VERSION = "1.0.0";
@Instance(FMLTutor.MODID)
public static FMLTutor instance;
@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
// TODO
}
@EventHandler
public void init(FMLInitializationEvent event)
{
// TODO
}
@EventHandler
public void postInit(FMLPostInitializationEvent event)
{
// TODO
}
}
package
、import
部分含义显而易见。
src/main/java/com/github/ustc_zzzz/fmltutor/FMLTutor.java(部分):
@Mod(modid = FMLTutor.MODID, name = FMLTutor.NAME, version = FMLTutor.VERSION, acceptedMinecraftVersions = "1.8.9")
上面这一行是Java从JDK1.5引入的一种机制:注解。注解的功能类似于代码中的注释,不同的是,注解不仅提供代码功能的说明,还是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。
比如下面三个最常见的注解:
@Override
用于声明该方法覆写了父类方法,使用该注解,如果(由于拼写错误等)没有覆写,编译器会报错@SuppressWarning
用于显式忽略警告,使用该注解,编译器不会产生对应代码的警告@Deprecated
用于标记一个方法不应该再被使用,使用该注解,如果在代码中调用了该方法,编译器会产生一个警告
因为注解中可以添加键-值对,所以动态查看一个注解的内容是有意义的,也是可行的。比如这个注解,FML在加载这个Mod的时候,就会去自动寻找含有@Mod
注解的类,并读取下面的数据:
modid
指的就是该Mod的唯一标识符name
指的是该Mod的名称version
指的是该Mod的版本号,在Mod间的依赖关系时可能会用作识别acceptedMinecraftVersions
指的是Mod接受的Minecraft版本,当版本不对时,FML会优雅地抛出一个错误而不是继续加载这个Mod
acceptedMinecraftVersions
约定的版本声明如下:
1.8.9
(本教程)表示该Mod只支持Minecraft 1.8.9[1.8,1.9)
表示该Mod支持从1.8(包含)到1.9(不包含)的所有Minecraft版本[1.8,1.10]
表示该Mod支持从1.8(包含)到1.10(包含)的所有Minecraft版本[1.8,)
表示该Mod支持从1.8(包含)之后出现的所有Minecraft版本(,1.8],[1.9,)
表示该Mod支持1.8(包含)之前出现的所有Minecraft版本和从1.9(包含)之后出现的所有Minecraft版本
当然这个注解也有着其他数据(没有声明的数据作为默认值),比如该Mod依赖于什么Mod等等,有些比较有必要的数据会在后面部分讲到。
下面的三个方法带有@EventHandler
注解,它们的作用也是类似。Forge在找到这个类后,会检查这个类中所有含有@EventHandler
注解的方法,并通过方法的参数类型来判定到底应该在何时调用它们:
- 含有
FMLPreInitializationEvent
参数的方法(这里是preInit
)在所有Mod初始化之前调用,这时候应该加载配置文件,实例化物品和方块,并注册它们。 - 含有
FMLInitializationEvent
参数的方法(这里是init
)用于该Mod的初始化,这时候应该为Mod进行设置,如注册合成表和烧炼系统,并且向其他Mod发送交互信息。 - 含有
FMLPostInitializationEvent
参数的方法(这里是postInit
)在所有Mod都初始化之后调用,这时候应该接收其他Mod发送的交互信息,并完成对Mod的设置。
有些Mod会把注册方块、物品等等操作放在Mod初始化阶段完成,这种做法是不推荐的,Forge推荐在preInit
阶段完成。
@Instance
注解的作用是将生成的该Mod的实例,注册到对应的Mod的id,同时,也可以访问其他Mod的id对应的实例,当然,这里的id要和本Mod的id相同。
代理?那是什么
众所周知,Minecraft Mod有客户端和服务端两种使用方式,而两种方式的差异足够大使得Mod需要采用两种初始化方式,而两种方式的差异又足够小使得Mod没有必要制作客户端和服务端两个版本。这时候代理便起到了区别两种初始化方式的作用。在单机运行时,Minecraft也会生成一个本地服务端。服务端和客户端之间的差异十分复杂,甚至很多都只是经验之谈,然而有一点往往是通用的,服务端的代码,往往客户端都会执行。
在主类中添加以下代码:
src/main/java/com/github/ustc_zzzz/fmltutor/FMLTutor.java(部分):
@SidedProxy(clientSide = "com.github.ustc_zzzz.fmltutor.client.ClientProxy",
serverSide = "com.github.ustc_zzzz.fmltutor.common.CommonProxy")
public static CommonProxy proxy;
Forge会在加载Mod的时候自动使用上面的类名对这个代理进行实例化。
当然,我们需要创建上面所说的CommonProxy
,和ClientProxy
。
新建一个包com.github.ustc_zzzz.fmltutor.common
,在其中新建一个类CommonProxy
:
src/main/java/com/github/ustc_zzzz/fmltutor/common/CommonProxy.java:
package com.github.ustc_zzzz.fmltutor.common;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
public class CommonProxy
{
public void preInit(FMLPreInitializationEvent event)
{
}
public void init(FMLInitializationEvent event)
{
}
public void postInit(FMLPostInitializationEvent event)
{
}
}
新建包com.github.ustc_zzzz.fmltutor.client
,新建类ClientProxy
,并继承类CommonProxy
:
src/main/java/com/github/ustc_zzzz/fmltutor/client/ClientProxy.java:
package com.github.ustc_zzzz.fmltutor.client;
import com.github.ustc_zzzz.fmltutor.common.CommonProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
public class ClientProxy extends CommonProxy
{
@Override
public void preInit(FMLPreInitializationEvent event)
{
super.preInit(event);
}
@Override
public void init(FMLInitializationEvent event)
{
super.init(event);
}
@Override
public void postInit(FMLPostInitializationEvent event)
{
super.postInit(event);
}
}
主类的调整:
src/main/java/com/github/ustc_zzzz/fmltutor/FMLTutor.java(部分):
@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
proxy.preInit(event);
}
@EventHandler
public void init(FMLInitializationEvent event)
{
proxy.init(event);
}
@EventHandler
public void postInit(FMLPostInitializationEvent event)
{
proxy.postInit(event);
}
很明显,当服务端被初始化时,CommonProxy
类中对应方法会被调用,如果是客户端,ClientProxy
类中对应方法会被调用,这样我们就可以实现服务端和客户端的差异。
完善你的Mod信息
一个Mod的信息在其jar根目录下的mcmod.info
文件里,这里是src/main/resources/mcmod.info
,打开就可以完善你的Mod信息。注意:version
和mcversion
字段不应修改,它们会在Gradle构建Mod的时候被自动替换掉。你应该更改build.gradle
文件。
比如,本教程的mcmod.info
文件是这个样子的:
src/main/resources/mcmod.info:
[
{
"modid": "fmltutor",
"name": "FML Tutor",
"description": "A Minecraft 1.8 Forge Mod Loader Tutorial by ustc_zzzz.",
"version": "${version}",
"mcversion": "${mcversion}",
"url": "https://github.com/ustc-zzzz/fmltutor/wiki",
"updateUrl": "https://github.com/ustc-zzzz/fmltutor/tags",
"authorList": ["ustc_zzzz"],
"credits": "Notch, Cpw, etc.",
"logoFile": "",
"screenshots": [],
"dependencies": []
}
]