Minecraft 模组开发原理笔记
本文最后更新于 315 天前,其中的信息可能已经有所发展或是发生改变。

从 MC 程序到开发用代码,再重新到程序

这里涉及两个主要内容:Java 的反编译;类的混淆和反混淆

反编译就不多讲了,这个是每门语言都有的逆向过程,这里基于 Java 字节码的特性初步取得代码

也正是因为 Java 的反编译不难,所以 MC 采取混淆的防护手段,但对于 Java 高手来说,人工破译仅仅是时间问题

源代码我都拿到了,是不是想干什么都可以了?

但 MC 可是一个不开源的商业项目啊——虽然有条件修改游戏,而且有利于社群发展,但还是得看官方态度的

为何模组的开发会有漫长的波折和各种方案?其实是多年来官方和个人开发者拉扯的结果

换句话说,MC 模组开发史看起来非常复杂,但 MC 本身是屎山,模组开发工具也是屎山。作为一个单纯的模组开发者,摸清当今主流的方案并且会用才是有价值的,造轮子的事情都是有余力了再去了解也不迟

以下的内容基于个人对模组开发原理的初步学习总结,如有错误恳请指正

旧版本Forge处理的混淆

旧版本主要是1.12及以前,反混淆以民间组织为主,代码的基本关系:
$$
notch名(全部混淆,且无固定格式)↔srg名(类内字段和方法名格式统一但不可读)↔mcp名(字段和方法名也可读)
$$

  • 为什么要用三套名字,两层映射?

mcp 名源于对 notch 名的翻译,一个 notch 名可能因为版本升级而改变用法或后续翻译优化而不断产生新版本 mcp 名

而 srg 名选择将频繁更新的部分译名用 func_ field_ 开头的格式表示,保留这些混淆来保持命名的稳定性,是可读性和版本兼容性的折中策略

mcp 名是民间给出的命名,当然可以打包成一个游戏程序并运行,但精力花费极大却有最差的兼容性

(还是因为Java太好反编译了才从命名上下毒)

在很长的一段时间里,带mod端都是以 srg 名运行的,这是实现 MC 原生代码和 MCP 编写的模组共生的桥梁

  • srg 名几乎随MC版本而发布

  • mcp 名则会有很多版本,而且只影响 IDE 编写代码的命名,不影响运行,因此不用和MC、srg绑定发布

  • 最后混淆成 srg 就可以和MC一同运行(至于如何运行,看下面的 反混淆运行原理

1.12 等版本由于比较古老,而默认配置更古老,初次构建时可能无法顺利下载原版代码和 mapping

所以需要修改 mapping 版本,例如:

minecraft {
    // mappings channel: 'snapshot', version: '20171003-1.12' 这是默认配置的版本 还是2017的snapshot
    mappings channel: 'stable', version: '39-1.12'  // 换较新版本
}

新版本Forge处理的混淆

有人说 Mojang 官方放出了反混淆方案并没有什么用,这里根据我今天的学习解释一下

官方反混淆不是 mcp 名形式,仍然保留了一些混淆(但比旧 srg 名要好了,主要是参数名混淆)

于是从 1.17 起,随版本稳定发布的官方半混淆方案取代了原 srg 名,感觉没啥坏处(srg 本人都被招安了)

但要注意的是,官方的反混淆没有改变 mcp 名的地位

与此同时,随着官方反混淆还一同出现了一个新项目:parchment,主要是补全官方保留的那点混淆名

在 forge 开发中,可以直接使用官方的反混淆方案,但等于没有,一般是添加一个 parchment 插件

官方给了一个半混淆方案,运行倒是不错,但想用在开发环境里就是搞笑了

不过 forge 的 MDK 有直接在注释里告诉你怎么改用 parchment

这里插一句,Gradle 用的是 Groovy 语法,语法糖甜到掉牙了

// build.gradle
plugins {
    // 添加
    id 'org.parchmentmc.librarian.forgegradle' version '1.+'
}

// settings.gradle
pluginManagement {
    repositories {
        // 添加
        maven { url = 'https://maven.parchmentmc.org' }
    }
}

// gradle.properties(以学习用例为例) 修改
mapping_channel=parchment
mapping_version=2023.06.26-1.20.1

于是,我们的开发实际可以认为是,最终使用了 parchment 的全套方案(包含官方混淆方案+补充内容)

而 forge 和 fabric(当然fabric仍然是可以用自己的运行方案的)借用了与官方方案对等的班混淆方案来运行

所以肯定不能说官方是没用的,只能说官方促进了模组开发的发展吗?如促。我们仍需要民间组织

其他派别的混淆处理

这个我了解的不多

比如 Fabric 就一直有一套自己的方案,当然官方提供方案后 Fabric 也能用

而其他的我们接触的比较多的就是一些服务端,在新版本都和 Forge 采用了类似的方案,即在半混淆方案上转向拥抱(或部分拥抱)官方

因为我没有具体研究过服务端的运行,所以我只确定理论上通过暴力手段兼容,Forge 和 Bukkit 等服务端是可以共存的,毕竟归根结底是同一个游戏。以 1.12 为例,比较有名的共存方案如 CatServer(MC 幻想乡用的就是这个)

后面有时间可能会了解一下服务端的处理方案

反混淆运行原理

我们自己添加的内容要在运行时加载,其实是在修改代码

如果是重新编译,那变量名都随便起,技术上也是难度较低的

  • 我们主要看看 runtime 修改代码(即游戏加载时将自定义的类代码加入或取代原生的类)

本质其实就是在类加载方法中,获取类的字节码并修改,再交给类加载器

在 JVM 找到类并修改,又涉及一个概念:class文件的签名,签名有约定的格式,这里有例子:Minecraft 服务端开发指北 | IzzelAliz's Blog(不过这个作者是开发框架的,我们用框架的不需要了解太深)

总之,相当于在加载 notch 名类时拦截加载器,把类按照映射 map 改名成 srg 名,再拿去加载

(就实现了基于 notch 名的游戏程序,在运行时却使用 srg 名,而模组代码也被混淆成 srg 名,这就架起了游戏和模组兼容的桥梁)

开始添加自己的逻辑

事件模型

事件模型,源于模组的目的即修改 MC 代码,通过拦截游戏运行中的一些标志,设为“事件触发”,可以对事件进行处理,比如访问事件相关的对象,或插入自定义逻辑代码,达到修改逻辑的效果

Forge 的事件类和 Fabric 的 Callback 是他们为开发者提供的现成 API。在顺利使用事件的触发和处理前,要经过一些固定流程完成事件的的注册等操作,这个就放到后面再说了

代码注入

代码注入,至少不是 Java 开发的主流方向,算是一种邪术了。结果其在运行时插入逻辑的功能使其成了模组开发的标配

当事件 API 不足以满足插入自定义逻辑的需求时,就要手动向游戏类内注入代码了,一般默认用 Mixin 技术(作为运行时修改代码的工具,所以兼容性还不错。但一旦出现不兼容甚至 crash 也是很头疼的)

注入代码的加载原理可以参考上面的“反混淆运行原理”,至少要对 class 签名有基本的认识

比如要在某个类的某个方法下的某一处添加一段自己的代码,注入工作需要做的是:找到这个类,找到对应方法,找到修改目标行的标志(比如某个方法调用的所在行),然后写一个调用自己添加的代码的逻辑

对于每条这样的注入,都需要写一大段判断条件,来搜索自己要注入的目标位置,再修改代码,再加载

于是干脆把这个流程封装起来在编译期完成,而我们只用写注解,例如以下形式:

@Mixin(修改目标类.class)
class CustomMixin{
    @Inject(method = "类下方法名", at = @At(value = "INVOKE", 
          target = "一长串签名(方法调用者类签名+方法参数类签名+方法返回值类签名)"))
  void atSomeTime(CallbackInfo ci) {
    MyTask.run();
  }
}

关于代码修改暂时就说这么多,看起来好像很复杂,但 IDEA 提供的 Minecraft 开发插件(特别是代码补全功能)能协助我们完成大部分工作

后续

由于我也是初学,初学者还是不要上来就抠太多技术细节

所以接下来还是跟着开源的项目和文档,进行正常的模组开发实现流程吧~

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇