前言
我是一个Minecraft模组/插件开发者,开发了一段时间的模组和插件,也对Minecraft的运行原理有所了解,所以就想简单说说。
客户端文件结构
Minecraft客户端的文件结构大致分为assets,libraries,versions。
assets
这个文件夹内保存的是多个版本间通用的资源文件,包括方块模型,材质,音效等。
libraries
这个文件夹内保存的是Java第三方依赖库,如:gson。(注:模组加载器通常也会在这里面)
versions
这个文件夹内保存的是各个版本的数据文件,如下是一些主要的:
- 游戏版本.jar Minecraft主体
- crash-reports 崩溃报告目录
- logs 日志文件目录
- resourcepacks 资源包目录
- saves 存档目录
- screenshots 截图目录
- log4j2.xml log4j2配置文件
- options 游戏设置
服务端文件结构
服务端的文件结构大部分与客户端类似,不过会多几个文件,具体如下:
- world 世界文件,与saves中存放的类似
- banned-ips.json banip的列表
- banned-players 被ban的玩家列表
- ops.json op列表
- server.properties 服务器配置文件
- usercache.json 用户缓存文件
- whitelist.json 白名单
启动
服务端
Minecraft服务端的启动可以大致分为以下几个步骤:加载依赖库,加载模组/插件(如果有的话),启动服务端。
加载依赖库
加载位于libraries里的依赖库,同时也是加载模组加载器的必要过程。
加载模组
模组加载器加载后,会抢先在Minecraft服务器启动前加载模组,直到所有模组初始化完成后,才会继续进行下一步。
启动服务端
服务器在这时候就正式启动了,初始化完成后玩家就可以加入。
客户端
Minecraft客户端本质上是Minecraft服务器加客户端,在加载客户端之前会先加载服务端,具体过程如下。
加载客户端模组
同样的,模组会在客户端加载前抢先加载。
显示游戏窗口
这一点没什么好说的,玩MC的应该都很熟悉了。
加载资源
游戏窗口出现后就会开始加载资源包,包括Minecraft自带的所有资源以及模组的资源,这一步与重新加载资源包一样。
tick
tick也就是随机刻,Minecraft的绝大部分事件都会在这里执行。
正常情况下,每秒会执行20个tick,但是在性能不足的时候可能会更少,你也可以通过一些模组如carpet
或/tick
命令来修改它。
tick会做哪些事
Minecraft的每个实体都有tick方法,每个tick都会调用被加载的所有实体的tick方法,这也是为什么实体对TPS的影响如此之大。
同样的,作物生长,时间更改等也会在tick中执行,因此,每个tick的工作量都是很大的。
并且,由于tick是在同一个线程中执行的,因此Minecraft对单核心性能要求很高,“一核有难,多核围观”就是这么来的,也有部分项目如Folia
就是在尝试解决这个问题。
TPS
TPS也就是每秒执行tick的数量,20TPS是最正常的,在19~20间波动是时有发生的,如果你的服务器TPS过低,那么你应该升级服务器配置或降低服务器负载了。
注册表
Minecraft的所有物品,实体,方块等都会被写入注册表,也包括模组的物品等。
id
每个注册了的注册物都会有一个唯一的id,通常是模组id:物品id
的形式(Minecraft的模组id是minecraft
),使用命令获取物品,效果,方块等就是使用的这个id。
注册表比对
在加入服务器的时候,通常会将客户端与服务器的注册表进行对比,以防客户端与服务器未安装同样的模组,通常情况下是要求客户端必须包含服务器的注册物,客户端多出的注册物忽略。
模组
虽然这不属于官方的内容,不过这里还是简要谈论一下。
你可以把Minecraft理解为核心+内容的形式,模组就相当于其中的内容(部分模组可能会修改核心),模组的物品方块等与原版的方块是平等的,也就是说,模组的内容可以当作原版来看待,数据包和资源包对模组也同样适用。
Minecraft并不支持直接加载使用Java编写的第三方模组,尽管可以通过文件注入的方式来实现加载模组,但是这种方式比较麻烦,故有了模组加载器。模组加载器也是通过文件注入的方式来实现的,但是它提供的API极大的简化了模组的开发难度并且提高了模组间的兼容性。
模组大致可以分为三部分,assets
,data
和代码。assets
是模组的资源文件,通常只会在客户端生效,与资源包的作用一致,并且可以被资源包覆盖。data
是数据包文件,通常用于添加配方等,与数据包作用一致。代码部分则是模组运行的代码,包含了模组运行的大部分逻辑。
Forge/NeoForge
Forge是Minecraft最早的模组加载器之一,也是目前为止模组数量最多的模组加载器。它提供了大量的API供模组开发者使用,因此使开发模组变得更为方便,因此对性能的要求较大。
NeoForge是Forge的一个分支,并且不兼容Forge,出现这个分叉是由于Forge的管理者对开发者的态度并不友善,因此就出现此分支,当然,Forge的所有优缺点也被一并继承了过来。
Fabric/Quilt
Fabric是目前主流的模组加载器之一,起步比Forge更晚,并且目前模组数量还不及Forge。Fabric采用了模块化设计,对于用户而言,它分为Fabric Loader
,Fabric API
,ModMenu
,语言模块Kotlin
和Scala
。Fabric Loader
本身只提供一个注入功能,并不提供适用于Minecraft的API,从Fabric Wiki
上可以得知,Fabric Loader
也可以注入其它使用Java编写的应用程序。Fabric API
提供了大量的API供Minecraft模组开发者使用,几乎每个Fabric模组都需要用到它。ModMenu
提供了一个模组菜单供用户查看模组信息和修改模组配置。语言模块则为其它变成语言提供支持。
Quilt是一个Fabric的分支,兼容大部分(几乎所有)Fabric模组,并且提供了一些自己的API。