Skip to content

Commit ae48863

Browse files
committed
Add initial game hotfixes.
1 parent 37068fe commit ae48863

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

patching/src/main/java/com/fox2code/foxloader/patching/TransformerUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,11 @@ public static void insertToEndOfCode(MethodNode methodNode, InsnList insnList) {
554554
}
555555
}
556556

557+
/**
558+
* @param insnList the instruction list to remove instructions from
559+
* @param start the start of the range to remove (exclusive)
560+
* @param end the end of the range to remove (exclusive)
561+
*/
557562
public static void removeInstructionsInRange(InsnList insnList, AbstractInsnNode start, AbstractInsnNode end) {
558563
if (start == end) throw new IllegalStateException("End is start: " + insnList.indexOf(start));
559564
AbstractInsnNode remInsn = start.getNext();

patching/src/main/java/com/fox2code/foxloader/patching/game/GamePatches.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ private static void addGamePatch(GamePatch gamePatch) {
5454
}
5555

5656
static {
57+
if (HotfixesPatch.USE_HOTFIXES) {
58+
addGamePatch(new HotfixesPatch());
59+
}
5760
addGamePatch(new RegistryPatch());
5861
addGamePatch(new ClientGameDirectoryPatch());
5962
addGamePatch(new NetworkConnectionPatch());
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023-2025 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.fox2code.foxloader.patching.game;
25+
26+
import com.fox2code.foxloader.patching.TransformerUtils;
27+
import org.objectweb.asm.Opcodes;
28+
import org.objectweb.asm.Type;
29+
import org.objectweb.asm.tree.*;
30+
31+
/**
32+
* Theses are just hotfixes for the game, dedicated to fixing ReIndev bugs and crashes.
33+
*/
34+
final class HotfixesPatch extends GamePatch {
35+
private static final String Packet = "net/minecraft/common/networking/Packet";
36+
private static final String Packet249Chunk = "net/minecraft/common/networking/Packet249Chunk";
37+
private static final String Minecraft = "net/minecraft/client/Minecraft";
38+
private static final String GameSettings = "net/minecraft/client/util/GameSettings";
39+
static final boolean USE_HOTFIXES = true;
40+
41+
HotfixesPatch() {
42+
super(new String[]{Packet, Minecraft, GameSettings});
43+
}
44+
45+
@Override
46+
public ClassNode transform(ClassNode classNode) {
47+
switch (classNode.name) {
48+
case Packet:
49+
patchPacket(classNode);
50+
break;
51+
case Minecraft:
52+
patchMinecraft(classNode);
53+
break;
54+
case GameSettings:
55+
patchGameSettings(classNode);
56+
break;
57+
}
58+
return classNode;
59+
}
60+
61+
private static void patchPacket(ClassNode classNode) {
62+
// Hotfix Packet249Chunk being parsed by the server if sent by the client.
63+
MethodNode clInit = TransformerUtils.getMethod(classNode, "<clinit>");
64+
for (AbstractInsnNode abstractInsnNode : clInit.instructions) {
65+
if (abstractInsnNode.getOpcode() == LDC) {
66+
LdcInsnNode ldcInsnNode = (LdcInsnNode) abstractInsnNode;
67+
if (ldcInsnNode.cst instanceof Type &&
68+
Packet249Chunk.equals(((Type) ldcInsnNode.cst).getInternalName())) {
69+
AbstractInsnNode previous = ldcInsnNode.getPrevious();
70+
AbstractInsnNode previous2 = previous.getPrevious();
71+
if (previous.getOpcode() == ICONST_1 && previous2.getOpcode() == ICONST_1) {
72+
clInit.instructions.set(previous, new InsnNode(ICONST_0));
73+
clInit.instructions.set(previous2, new InsnNode(ICONST_0));
74+
}
75+
break;
76+
}
77+
}
78+
}
79+
}
80+
81+
private static void patchMinecraft(ClassNode classNode) {
82+
// Hotfix switching to fullscreen sometimes causing the game to crash.
83+
MethodNode toggleFullscreenImpl = TransformerUtils.findMethod(classNode, "toggleFullscreenImpl");
84+
FieldNode gameSettingsField = TransformerUtils.findFieldDesc(classNode, "L" + GameSettings + ";");
85+
if (toggleFullscreenImpl == null || gameSettingsField == null) return;
86+
for (AbstractInsnNode abstractInsnNode : toggleFullscreenImpl.instructions) {
87+
MethodInsnNode methodInsnNode;
88+
if (abstractInsnNode.getOpcode() == Opcodes.INVOKEVIRTUAL &&
89+
GameSettings.equals((methodInsnNode = (MethodInsnNode) abstractInsnNode).owner) &&
90+
"setOptionValue".equals(methodInsnNode.name) && methodInsnNode.desc.endsWith(")V")) {
91+
AbstractInsnNode start = TransformerUtils.previousNonCodeInsn(methodInsnNode);
92+
TransformerUtils.removeInstructionsInRange(
93+
toggleFullscreenImpl.instructions, start, methodInsnNode.getNext());
94+
InsnList insnList = new InsnList();
95+
insnList.add(new VarInsnNode(ALOAD, 0));
96+
insnList.add(new FieldInsnNode(GETFIELD, Minecraft,
97+
gameSettingsField.name, gameSettingsField.desc));
98+
insnList.add(new VarInsnNode(ILOAD, 1));
99+
insnList.add(new MethodInsnNode(INVOKEVIRTUAL, GameSettings,
100+
"setFullscreenState", "(Z)V"));
101+
toggleFullscreenImpl.instructions.insert(start, insnList);
102+
}
103+
}
104+
}
105+
106+
private static void patchGameSettings(ClassNode classNode) {
107+
// Hotfix switching to fullscreen sometimes causing the game to crash.
108+
if (TransformerUtils.findMethod(classNode, "setFullscreenState", "(Z)V") != null) {
109+
return;
110+
}
111+
FieldNode fullscreenField = TransformerUtils.getField(classNode, "fullScreen");
112+
MethodNode setFullscreenState = new MethodNode(ACC_PUBLIC,
113+
"setFullscreenState", "(Z)V", null, null);
114+
setFullscreenState.instructions.add(new VarInsnNode(ALOAD, 0));
115+
setFullscreenState.instructions.add(new VarInsnNode(ALOAD, 1));
116+
setFullscreenState.instructions.add(new FieldInsnNode(PUTFIELD,
117+
GameSettings, fullscreenField.name, fullscreenField.desc));
118+
setFullscreenState.instructions.add(new InsnNode(RETURN));
119+
TransformerUtils.setThisParameterName(classNode, setFullscreenState);
120+
TransformerUtils.setParameterName(setFullscreenState, 1, "fullscreen");
121+
classNode.methods.add(setFullscreenState);
122+
}
123+
}

0 commit comments

Comments
 (0)