# 你的第一段模组代码
**难度分级:☆**
现在,万事俱备,你可以正式开始编写你的模组了!
**以下所有代码均以EOK项目为例**
## FORGE模组目录结构
在IDE下,你可以看到类似如下的目录结构:
![](https://img.kancloud.cn/85/02/85020224160a27a3dba0e8a8509024a2_452x516.png)
在forge开发目录下,所有的源代码都需要放在\src目录下。其中java文件夹中为该模组源代码,resources文件夹中为该模组用到的所有资源文件(贴图,模型,配置文件,语言文件等)。在java文件夹中,通常情况下我们会以**组织名.用户名.模组名**的形式来命名模组根目录,其中的**包(Package)** 命名方式可根据自己的习惯或团队的规定选择。对于resources文件夹,其中模组资源的根目录必须是**assets/[你的模组ID]**,其中至少包含**方块状态(blockstates),语言(lang),模型(models),贴图(textures)** 四个文件夹。mcmod.info文件用于控制在游戏中的模组列表里显示的内容(可以在代码中使用**useMetadata = true**打开),pack.mcmeta为模组信息,用处不大。
## 主类
任何Forge模组都需要有一个**主类** 用以被Forge所识别并加载。在EOK项目中,EOK类即为项目的主类。主类之前必须使用 **@Mod** 注释标识,Forge也只会监听此类中的@EventHandler。对于@Mod注释可以设置如下参数:
| 属性名称 | 类型 | 默认值 | 描述 |
| ------------ | ------------ | ------------ | ------------ |
| modid | String | 必须手动赋值 | 该模组的唯一标识符,必须全小写字母且不得超过64个字符 |
| name | String | "" | 对玩家显示的模组名 |
| version | String | "" | 该模组版本号,只能出现数字和点(例如:1.0.0) |
| dependencies | String | "" | 该模组的依赖模组,可以选择四种模式:"before", "after", "required-before", "required-after",用法参考Forge开发文档:[链接](https://mcforge.readthedocs.io/en/latest/gettingstarted/dependencymanagement/) |
| useMetadata | boolean | false | 如果设置为true,则forge会使用该模组的mcmod.info中的信息 |
| clientSideOnly/serverSideOnly | boolean | false | 如果模组仅需要在客户端/服务端中运行,应当设置为true |
| acceptedMinecraftVersions | String | "" | 该模组可以接受的Minecraft版本范围 |
| acceptableRemoteVersions | String | "" | 该模组可以接受的远程(服务器/客户端中)模组版本范围 |
| acceptableSaveVersions | String | "" | 该模组可以接受的存档中的模组版本范围 |
| certificateFingerprint | String | "" | 该模组的签名标识(参考Forge开发文档:[链接](https://mcforge.readthedocs.io/en/latest/concepts/jarsigning/ "forge签名")) |
| modLanguage | String | "java" | 该模组源代码使用的语言,仅接受"java"或"scala" |
| modLanguageAdapter | String | "" | 该模组中LanguageAdapter类的位置,如果设置了但该类没有默认的构造函数或没有实现ILanguageAdapter接口,Forge将发生崩溃 |
| canBeDeactivated | boolean | false | 该mod能否被关闭 |
| guiFactory | String | "" | 该模组中GuiFactory类的位置,如果设置了则该类必须实现IModGuiFactory接口 |
| updateJSON | String | "" | 该模组用于更新版本的JSON文件远程地址 |
如果你的模组中不需要某一项属性或只需要将其设为默认值,你可以不用在@mod注释里写出。
例如,在EOK中@mod的设置即为:
~~~
@Mod(modid = EOK.MODID, name = EOK.NAME, version = EOK.VERSION, useMetadata = true)
public class EOK
{
public static final String MODID = "eok";
public static final String NAME = "Evolution Of Knowledge";
public static final String VERSION = "0.0.1";
...
}
~~~
## @EventHandler
整个Forge的运作基本都基于一种叫**事件(Event)** 的系统上。**@EventHandler** 注释使得Forge可以在不同的**游戏生命周期(Life Cycle)** 调用该模组对应函数中的指令。Forge的生命周期中有以下事件节点:
| 事件名称 | 此时模组应该做的事 |
| --- | --- |
| FMLPreInitializationEvent | 读取配置文件,注册物品,方块等需要调用**GameRegistry**的行为 |
| FMLInitializationEvent | 对模组进行设置。初始化你构建的数据结构,注册合成表,发送**FMLInterModComms**消息给别的模组 |
| FMLPostInitializationEvent | 接收其他模组发来的交互信息,完成对模组的设置 |
| FMLServerAboutToStartEvent | 处理服务器创建之前你需要执行的任务 |
| FMLServerStartingEvent | 服务器启动时你需要执行的任务,注册命令,对服务器进行修改等 |
| FMLServerStartedEvent | 服务器启动后你需要执行的任务 |
| FMLServerStoppingEvent | 服务器执行关闭过程中你需要执行的任务 |
| FMLServerStoppedEvent | 服务器关闭后你需要执行的清理任务(通常只在本地服务器有效) |
如果你需要将一个函数设置为某一个生命周期需要被forge调用的函数,你需要在**函数前**加上 **@EventHandler** 注释,并在参数中传入对应的**事件类型** ,例如EOK中的相关代码即为:
~~~
@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
...
}
@EventHandler
public void init(FMLInitializationEvent event)
{
...
}
@EventHandler
public void postInit(FMLPostInitializationEvent event)
{
...
}
~~~
## 实例
和modid类似,模组的实例也是一种对模组的唯一标识方式。实例在网络的收发及模组间的联动是必不可少的,模组的实例定义必须加上 **@Mod.Instance** 注释,类型为主类的类名,如EOK的实例定义即为:
~~~
@Mod.Instance
public static EOK instance;
~~~
## 代理
和许多游戏一样,Minecraft在服务器和客户端上有两套相似却又不同的代码。在很多情况下我们也需要在服务器和客户端上运行不同的代码,这就需要依赖**代理(Proxy)** 来对他们进行分类。代理需要单独创建新的**代理类** 来实现,一般分别被命名为**ClientProxy** 以及**ServerProxy** (如果没有需要单独在服务端运行的代码,可以直接写成**CommonProxy** 来放需要同时在客户端和服务端运行的代码)。在代理类中,我们同样需要定义参数分别为Forge生命周期对应的事件类型。如果你的模组没有需要单独在服务端运行的代码,那么你可以让你的ClientProxy直接**继承(extends)** CommonProxy并通过**覆写函数+super关键字** 来减少代码量。
EOK的CommonProxy类基本结构如下:
~~~
public class CommonProxy {
public void preInit(FMLPreInitializationEvent event) {
...
}
public void init(FMLInitializationEvent event) {
...
}
public void postInit(FMLPostInitializationEvent event) {
...
}
~~~
ClientProxy类结构如下:
~~~
public class ClientProxy extends CommonProxy{
public void preInit(FMLPreInitializationEvent event){
super.preInit(event);
}
public void init(FMLInitializationEvent event){
super.init(event);
}
public void postInit(FMLPostInitializationEvent event){
super.postInit(event);
}
~~~
新建完代理类以后,我们需要在主类中告诉Forge去寻找相应的代理类。在Forge中,对于代理端的声明需要使用 **@SidedProxy** 进行注释,参数中的**clientSide** 为客户端代理所在的**完整类名(包名+类名)** ,**serverSide** 为服务端代理所在的完整类名。例如EOK**主类**中对于代理的声明如下:
~~~
@SidedProxy(clientSide = "com.gonggongjohn.eok.ClientProxy", serverSide = "com.gonggongjohn.eok.CommonProxy")
public static CommonProxy proxy;
~~~
随后我们在主类的**声明周期函数**中调用**代理类**中的代码即可:
**什么你不知道“声明周期函数”“代理类”是啥? 再看一遍教程**
~~~
proxy.preInit(event);
proxy.init(event);
proxy.postInit(event);
~~~
- 0.引子
- 基础篇 - 1.构建开发环境
- 基础篇 - 2.主类和代理
- 基础篇 - 3.创建一个物品
- 基础篇 - 4.创建一个方块
- 基础篇 - 4.1自定义方块模型
- 基础篇 - 5.初探事件系统
- 基础篇 - 6.Capability系统
- 基础篇 - 7.创建一个方块实体
- 基础篇 - 8.你的第一个GUI
- 基础篇 - 9.网络与通讯
- 进阶篇 - 0.更复杂的Mod
- 进阶篇 - 1.Minecraft渲染原理
- 进阶篇 - 2.更复杂的GUI
- 进阶篇 - 3.物品进阶
- 高级篇 - 1.深入探索OpenGL
- 高级篇 - 1.1OpenGL颜色渲染
- 高级篇 - 1.2OpenGL光照系统
- 高级篇 - 2.Minecraft贴图加载原理