前言
Minecraft
本质是一个Java程序,并使用了OpenGL
进行图形渲染,故模组的本质就是将自定义的Jar包加入类加载路径,并在程序启动后通过这些Jar包中的代码和资源文件对游戏程序本身的行为进行干预。
MCP起源
混淆
Minecraft
本身的Java字节码是混淆过的(例如使用ProGuard
插件),并不能通过反编译看到原始的类名、方法名和成员变量名等关键信息,能看到的只有下面这种形式的反编译结果:
aan.class
java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public abstract class aan extends aao {
private static final my<Boolean> bH;
public aan(amu var1) {
super(var1);
this.bF = false;
}
protected void i() {
super.i();
this.Y.a(bH, false);
}
protected void bM() {
super.bM();
this.a((wc)adh.a).a((double)this.dM());
this.a((wc)adh.d).a(0.17499999701976776);
this.a((wc)bx).a(0.5);
}
public boolean dm() {
return (Boolean)this.Y.a(bH);
}
public void q(boolean var1) {
this.Y.b(bH, var1);
}
protected int dn() {
return this.dm() ? 17 : super.dn();
}
public double aG() {
return super.aG() - 0.25;
}
protected qe do() {
super.do();
return qf.aD;
}
public void a(ur var1) {
super.a(var1);
if (this.dm()) {
if (!this.l.G) {
this.a(ain.a(aox.ae), 1);
}
this.q(false);
}
}
public static void b(ry var0, Class<?> var1) {
aao.c(var0, var1);
var0.a(rw.e, new tn(var1, new String[]{"Items"}));
}
public void b(fy var1) {
super.b(var1);
var1.a("ChestedHorse", this.dm());
if (this.dm()) {
ge var2 = new ge();
for(int var3 = 2; var3 < this.bC.w_(); ++var3) {
aip var4 = this.bC.a(var3);
if (!var4.b()) {
fy var5 = new fy();
var5.a("Slot", (byte)var3);
var4.a(var5);
var2.a(var5);
}
}
var1.a("Items", var2);
}
}
public void a(fy var1) {
super.a(var1);
this.q(var1.q("ChestedHorse"));
if (this.dm()) {
ge var2 = var1.c("Items", 10);
this.dC();
for(int var3 = 0; var3 < var2.c(); ++var3) {
fy var4 = var2.b(var3);
int var5 = var4.f("Slot") & 255;
if (var5 >= 2 && var5 < this.bC.w_()) {
this.bC.a(var5, new aip(var4));
}
}
}
this.dD();
}
public boolean c(int var1, aip var2) {
if (var1 == 499) {
if (this.dm() && var2.b()) {
this.q(false);
this.dC();
return true;
}
if (!this.dm() && var2.c() == ain.a(aox.ae)) {
this.q(true);
this.dC();
return true;
}
}
return super.c(var1, var2);
}
public boolean a(aed var1, ub var2) {
aip var3 = var1.b(var2);
if (var3.c() == air.bU) {
return super.a(var1, var2);
} else {
if (!this.l_()) {
if (this.du() && var1.aU()) {
this.c(var1);
return true;
}
if (this.aT()) {
return super.a(var1, var2);
}
}
if (!var3.b()) {
boolean var4 = this.b(var1, var3);
if (!var4 && !this.du()) {
if (var3.a(var1, this, var2)) {
return true;
}
this.dK();
return true;
}
if (!var4 && !this.dm() && var3.c() == ain.a(aox.ae)) {
this.q(true);
this.dp();
var4 = true;
this.dC();
}
if (!var4 && !this.l_() && !this.dG() && var3.c() == air.aD) {
this.c(var1);
return true;
}
if (var4) {
if (!var1.bO.d) {
var3.g(1);
}
return true;
}
}
if (this.l_()) {
return super.a(var1, var2);
} else if (var3.a(var1, this, var2)) {
return true;
} else {
this.g(var1);
return true;
}
}
}
protected void dp() {
this.a(qf.aE, 1.0F, (this.S.nextFloat() - this.S.nextFloat()) * 0.2F + 1.0F);
}
public int dt() {
return 5;
}
static {
bH = nb.a(aan.class, na.h);
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public abstract class aan extends aao {
private static final my<Boolean> bH;
public aan(amu var1) {
super(var1);
this.bF = false;
}
protected void i() {
super.i();
this.Y.a(bH, false);
}
protected void bM() {
super.bM();
this.a((wc)adh.a).a((double)this.dM());
this.a((wc)adh.d).a(0.17499999701976776);
this.a((wc)bx).a(0.5);
}
public boolean dm() {
return (Boolean)this.Y.a(bH);
}
public void q(boolean var1) {
this.Y.b(bH, var1);
}
protected int dn() {
return this.dm() ? 17 : super.dn();
}
public double aG() {
return super.aG() - 0.25;
}
protected qe do() {
super.do();
return qf.aD;
}
public void a(ur var1) {
super.a(var1);
if (this.dm()) {
if (!this.l.G) {
this.a(ain.a(aox.ae), 1);
}
this.q(false);
}
}
public static void b(ry var0, Class<?> var1) {
aao.c(var0, var1);
var0.a(rw.e, new tn(var1, new String[]{"Items"}));
}
public void b(fy var1) {
super.b(var1);
var1.a("ChestedHorse", this.dm());
if (this.dm()) {
ge var2 = new ge();
for(int var3 = 2; var3 < this.bC.w_(); ++var3) {
aip var4 = this.bC.a(var3);
if (!var4.b()) {
fy var5 = new fy();
var5.a("Slot", (byte)var3);
var4.a(var5);
var2.a(var5);
}
}
var1.a("Items", var2);
}
}
public void a(fy var1) {
super.a(var1);
this.q(var1.q("ChestedHorse"));
if (this.dm()) {
ge var2 = var1.c("Items", 10);
this.dC();
for(int var3 = 0; var3 < var2.c(); ++var3) {
fy var4 = var2.b(var3);
int var5 = var4.f("Slot") & 255;
if (var5 >= 2 && var5 < this.bC.w_()) {
this.bC.a(var5, new aip(var4));
}
}
}
this.dD();
}
public boolean c(int var1, aip var2) {
if (var1 == 499) {
if (this.dm() && var2.b()) {
this.q(false);
this.dC();
return true;
}
if (!this.dm() && var2.c() == ain.a(aox.ae)) {
this.q(true);
this.dC();
return true;
}
}
return super.c(var1, var2);
}
public boolean a(aed var1, ub var2) {
aip var3 = var1.b(var2);
if (var3.c() == air.bU) {
return super.a(var1, var2);
} else {
if (!this.l_()) {
if (this.du() && var1.aU()) {
this.c(var1);
return true;
}
if (this.aT()) {
return super.a(var1, var2);
}
}
if (!var3.b()) {
boolean var4 = this.b(var1, var3);
if (!var4 && !this.du()) {
if (var3.a(var1, this, var2)) {
return true;
}
this.dK();
return true;
}
if (!var4 && !this.dm() && var3.c() == ain.a(aox.ae)) {
this.q(true);
this.dp();
var4 = true;
this.dC();
}
if (!var4 && !this.l_() && !this.dG() && var3.c() == air.aD) {
this.c(var1);
return true;
}
if (var4) {
if (!var1.bO.d) {
var3.g(1);
}
return true;
}
}
if (this.l_()) {
return super.a(var1, var2);
} else if (var3.a(var1, this, var2)) {
return true;
} else {
this.g(var1);
return true;
}
}
}
protected void dp() {
this.a(qf.aE, 1.0F, (this.S.nextFloat() - this.S.nextFloat()) * 0.2F + 1.0F);
}
public int dt() {
return 5;
}
static {
bH = nb.a(aan.class, na.h);
}
}
这种形式的反编译结果基本不具有可读性,而且随着Minecraft
版本更新,混淆后名称还会变化,对于模组开发来说是很痛苦的。
反混淆
鉴于上述的反编译代码可读性差、混淆后名称随着版本更新变化等问题,Minecraft
模组的先行者们开始了对Minecraft
源码的反混淆工作,推测出了一套方法、类名、变量名的【混淆名称-中间名称-原始名称】映射及部分注释,部分内容如下:
searge | name | side | desc |
---|---|---|---|
field_100013_f | isPotionDurationMax | 0 | True if potion effect duration is at maximum, false otherwise. |
field_104024_v | openGLWarningLink | 0 | Link to the Mojang Support about minimum requirements |
field_104025_t | threadLock | 0 | The Object object utilized as a thread lock when performing non thread-safe operations |
因为Minecraft混淆后源码的各要素名称是不稳定的(下面简称Notch名,因为Notch是Minecraft创始人),反混淆推测出的各要素名称也是不稳定的(下面简称MCP名),两者之间的映射也是不稳定的,为了相对稳定的二进制映射,便有了中间层映射名(下面简称Searge名,因为Searge是MCP发起人)。
反混淆工作的最终成果就是MCP
(Mod Coder Pack
),但是MCP
官网的最终版本停留在1.12,1.12以及后的反混淆工程为MCP-Reborn
。