前言

我是一个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极大的简化了模组的开发难度并且提高了模组间的兼容性。

模组大致可以分为三部分,assetsdata和代码。assets是模组的资源文件,通常只会在客户端生效,与资源包的作用一致,并且可以被资源包覆盖。data是数据包文件,通常用于添加配方等,与数据包作用一致。代码部分则是模组运行的代码,包含了模组运行的大部分逻辑。

Forge/NeoForge

Forge是Minecraft最早的模组加载器之一,也是目前为止模组数量最多的模组加载器。它提供了大量的API供模组开发者使用,因此使开发模组变得更为方便,因此对性能的要求较大。

NeoForge是Forge的一个分支,并且不兼容Forge,出现这个分叉是由于Forge的管理者对开发者的态度并不友善,因此就出现此分支,当然,Forge的所有优缺点也被一并继承了过来。

Fabric/Quilt

Fabric是目前主流的模组加载器之一,起步比Forge更晚,并且目前模组数量还不及Forge。Fabric采用了模块化设计,对于用户而言,它分为Fabric LoaderFabric APIModMenu,语言模块KotlinScalaFabric Loader本身只提供一个注入功能,并不提供适用于Minecraft的API,从Fabric Wiki上可以得知,Fabric Loader也可以注入其它使用Java编写的应用程序。Fabric API提供了大量的API供Minecraft模组开发者使用,几乎每个Fabric模组都需要用到它。ModMenu提供了一个模组菜单供用户查看模组信息和修改模组配置。语言模块则为其它变成语言提供支持。

Quilt是一个Fabric的分支,兼容大部分(几乎所有)Fabric模组,并且提供了一些自己的API。