From c0c67b233934c30a6bf5e51256b3541fd4b83840 Mon Sep 17 00:00:00 2001 From: Cody <149296239+notdevcody@users.noreply.github.com> Date: Fri, 14 Jun 2024 20:22:23 +0200 Subject: [PATCH] Minor refactoring + Beta Forge patch --- README.md | 16 ++--- .../java/uk/betacraft/legacyfix/LFLogger.java | 22 +++--- .../betacraft/legacyfix/LegacyFixAgent.java | 32 +++++---- .../uk/betacraft/legacyfix/patch/Patch.java | 3 +- .../legacyfix/patch/URLClassLoaderBridge.java | 2 +- .../legacyfix/patch/impl/BetaForgePatch.java | 36 ++++++++++ .../patch/impl/DisableControllersPatch.java | 21 +++--- .../legacyfix/patch/impl/ModloaderPatch.java | 70 ++++++++----------- .../patch/impl/TexturePackFolderPatch.java | 51 ++++++++++++++ .../patch/impl/TexturepackFolderPatch.java | 59 ---------------- 10 files changed, 166 insertions(+), 146 deletions(-) create mode 100644 src/main/java/uk/betacraft/legacyfix/patch/impl/BetaForgePatch.java create mode 100644 src/main/java/uk/betacraft/legacyfix/patch/impl/TexturePackFolderPatch.java delete mode 100644 src/main/java/uk/betacraft/legacyfix/patch/impl/TexturepackFolderPatch.java diff --git a/README.md b/README.md index beff577..ccdc5f8 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -# LegacyFix -![](/.github/img/banner.webp) +
+

LegacyFix

+A utility made to patch old and misbehaving versions of Minecraft. +
-## What is it? -LegacyFix is a utility made to patch old and misbehaving versions of Minecraft. +![](/.github/img/banner.webp) ### What does it patch? It aims to patch all known issues with running legacy Minecraft.
@@ -22,9 +23,8 @@ As of now: - [ ] Joining servers with c0.0.15a - [ ] Unnecessary duplicates of sound files and assets - [ ] Offline saving in Classic versions +- [x] Forge for b1.7.3 - b1.8.1 ### How can I use it? -TODO - -### What programs use it? -- [BetaCraft Launcher V2](https://github.com/betacraftuk/betacraft-launcher/tree/v2) +- Used by default in the [BetaCraft v2 Launcher](https://github.com/betacraftuk/betacraft-launcher/tree/v2) +- Apply the javaagent in the launcher of your choice \ No newline at end of file diff --git a/src/main/java/uk/betacraft/legacyfix/LFLogger.java b/src/main/java/uk/betacraft/legacyfix/LFLogger.java index 0ea0666..7e39cb1 100644 --- a/src/main/java/uk/betacraft/legacyfix/LFLogger.java +++ b/src/main/java/uk/betacraft/legacyfix/LFLogger.java @@ -1,24 +1,24 @@ package uk.betacraft.legacyfix; +import uk.betacraft.legacyfix.patch.Patch; + public class LFLogger { - public static void info(String msg) { - System.out.println("[INFO] LF: " + msg); + public static void info(String ...lines) { + log("INFO", lines); } - public static void info(String ...lines) { - for (int i = 0; i < lines.length; i++) { - if (i == 0) { - System.out.println("[INFO] LF: " + lines[i]); - } else { - System.out.println(" " + lines[i]); - } - } + public static void error(Patch patch, Exception e) { + error(patch.getName(), e.toString()); } public static void error(String ...lines) { + log("ERROR", lines); + } + + public static void log(String prefix, String ...lines) { for (int i = 0; i < lines.length; i++) { if (i == 0) { - System.out.println("[ERROR] LF: " + lines[i]); + System.out.println("[LF] " + prefix + ": " + lines[i]); } else { System.out.println(" " + lines[i]); } diff --git a/src/main/java/uk/betacraft/legacyfix/LegacyFixAgent.java b/src/main/java/uk/betacraft/legacyfix/LegacyFixAgent.java index 2ac1c80..fb865b0 100644 --- a/src/main/java/uk/betacraft/legacyfix/LegacyFixAgent.java +++ b/src/main/java/uk/betacraft/legacyfix/LegacyFixAgent.java @@ -8,10 +8,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.lang.instrument.Instrumentation; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class LegacyFixAgent { private static final Map SETTINGS = new HashMap(); @@ -19,8 +16,8 @@ public class LegacyFixAgent { private static int jvmVersion = -1; public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - private static final HashMap RELEASE_INFO = GSON.fromJson(new BufferedReader(new InputStreamReader(LegacyFixAgent.class.getResourceAsStream("/releaseInfo.json"))), HashMap.class); - public static final String VERSION = RELEASE_INFO.containsKey("version") ? RELEASE_INFO.get("version") : "unknown"; + private static final HashMap RELEASE_INFO = GSON.fromJson(new BufferedReader(new InputStreamReader(LegacyFixAgent.class.getResourceAsStream("/releaseInfo.json"))), HashMap.class); + public static final String VERSION = RELEASE_INFO.containsKey("version") ? (String) RELEASE_INFO.get("version") : "unknown"; public static void premain(String agentArgs, final Instrumentation inst) { jvmVersion = getMajorJvmVersion(); @@ -34,17 +31,24 @@ public static void premain(String agentArgs, final Instrumentation inst) { } } - PATCHES.add(new DisableControllersPatch()); - PATCHES.add(new ModloaderPatch()); - PATCHES.add(new DeAwtPatch()); - PATCHES.add(new TexturepackFolderPatch()); + PATCHES.addAll(Arrays.asList( + new DisableControllersPatch(), + new TexturePackFolderPatch(), + new ModloaderPatch(), + new BetaForgePatch(), + new DeAwtPatch() + )); for (Patch patch : PATCHES) { if (patch.shouldApply()) { - if (patch.apply(inst)) { - LFLogger.info("Applied " + patch.getName()); - } else { - LFLogger.error("Failed to apply " + patch.getName() + "!"); + try { + if (patch.apply(inst)) { + LFLogger.info("Applied " + patch.getName()); + } else { + LFLogger.error("Failed to apply " + patch.getName()); + } + } catch (Exception e) { + LFLogger.error(patch, e); } } } diff --git a/src/main/java/uk/betacraft/legacyfix/patch/Patch.java b/src/main/java/uk/betacraft/legacyfix/patch/Patch.java index c3d5cd3..75cbd5a 100644 --- a/src/main/java/uk/betacraft/legacyfix/patch/Patch.java +++ b/src/main/java/uk/betacraft/legacyfix/patch/Patch.java @@ -35,8 +35,9 @@ public boolean shouldApply() { /** * Applies the patch. Should only ever be called in the agent's premain. * @return If the patch was correctly applied + * @throws Exception Exceptions related to class patching */ - public boolean apply(final Instrumentation inst) { + public boolean apply(final Instrumentation inst) throws Exception { return true; } } diff --git a/src/main/java/uk/betacraft/legacyfix/patch/URLClassLoaderBridge.java b/src/main/java/uk/betacraft/legacyfix/patch/URLClassLoaderBridge.java index f5c7d5b..659c313 100644 --- a/src/main/java/uk/betacraft/legacyfix/patch/URLClassLoaderBridge.java +++ b/src/main/java/uk/betacraft/legacyfix/patch/URLClassLoaderBridge.java @@ -14,7 +14,7 @@ */ @SuppressWarnings("unused") public class URLClassLoaderBridge extends URLClassLoader { - private static ArrayList urls = new ArrayList(); + private static final ArrayList urls = new ArrayList(); static { String classpath = System.getProperty("java.class.path"); diff --git a/src/main/java/uk/betacraft/legacyfix/patch/impl/BetaForgePatch.java b/src/main/java/uk/betacraft/legacyfix/patch/impl/BetaForgePatch.java new file mode 100644 index 0000000..1b9ff81 --- /dev/null +++ b/src/main/java/uk/betacraft/legacyfix/patch/impl/BetaForgePatch.java @@ -0,0 +1,36 @@ +package uk.betacraft.legacyfix.patch.impl; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.expr.ExprEditor; +import javassist.expr.MethodCall; +import uk.betacraft.legacyfix.patch.Patch; + +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; + +/** + * Patches b1.7.3 - b1.8.1 Forge + */ +public class BetaForgePatch extends Patch { + public BetaForgePatch() { + super("betaforge", "Beta Forge"); + } + + @Override + public boolean apply(Instrumentation inst) throws Exception { + CtClass clazz = pool.getOrNull("forge.ForgeHooksClient"); + if (clazz == null) return false; + + clazz.instrument(new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getMethodName().equals("toArray") && m.getSignature().equals("()[Ljava/lang/Object;")) { + m.replace("$_ = $0.toArray(new Integer[0]);"); + } + } + }); + + inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); + return true; + } +} diff --git a/src/main/java/uk/betacraft/legacyfix/patch/impl/DisableControllersPatch.java b/src/main/java/uk/betacraft/legacyfix/patch/impl/DisableControllersPatch.java index 48323f2..10ac535 100644 --- a/src/main/java/uk/betacraft/legacyfix/patch/impl/DisableControllersPatch.java +++ b/src/main/java/uk/betacraft/legacyfix/patch/impl/DisableControllersPatch.java @@ -13,19 +13,14 @@ public DisableControllersPatch() { } @Override - public boolean apply(Instrumentation inst) { - try { - CtClass clazz = pool.get("org.lwjgl.input.Controllers"); - CtMethod method = clazz.getDeclaredMethod("create"); - method.setBody( - "{ return; }" - ); + public boolean apply(Instrumentation inst) throws Exception { + CtClass clazz = pool.get("org.lwjgl.input.Controllers"); + CtMethod method = clazz.getDeclaredMethod("create"); + method.setBody( + "{ return; }" + ); - inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); - return true; - } catch (Exception e) { - e.printStackTrace(); - return false; - } + inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); + return true; } } diff --git a/src/main/java/uk/betacraft/legacyfix/patch/impl/ModloaderPatch.java b/src/main/java/uk/betacraft/legacyfix/patch/impl/ModloaderPatch.java index 0554c56..30b137f 100644 --- a/src/main/java/uk/betacraft/legacyfix/patch/impl/ModloaderPatch.java +++ b/src/main/java/uk/betacraft/legacyfix/patch/impl/ModloaderPatch.java @@ -19,7 +19,7 @@ public ModloaderPatch() { } @Override - public boolean apply(Instrumentation inst) { + public boolean apply(Instrumentation inst) throws Exception { if (LegacyFixAgent.getJvmVersion() >= 11) { String args = JvmUtils.getJvmArguments(); if (!( @@ -48,49 +48,41 @@ public boolean apply(Instrumentation inst) { } } - try { - CtClass clazz; - CtMethod method; - CtClass string = pool.get("java.lang.String"); + CtClass clazz = pool.get("java.lang.Class"); + CtClass string = pool.get("java.lang.String"); + CtMethod method = clazz.getDeclaredMethod("getDeclaredField", new CtClass[] {string}); - clazz = pool.get("java.lang.Class"); - method = clazz.getDeclaredMethod("getDeclaredField", new CtClass[] {string}); - method.setBody( - "{" + - " java.lang.reflect.Field[] fieldz = getDeclaredFields0(false);" + - " for (int i = 0; i < fieldz.length; i++) {" + - " java.lang.reflect.Field one = fieldz[i];" + - " if ($1.equals(one.getName())) {" + - " return one;" + - " }" + - " }" + - " return null;" + - "}" - ); + method.setBody( + "{" + + " java.lang.reflect.Field[] fieldz = getDeclaredFields0(false);" + + " for (int i = 0; i < fieldz.length; i++) {" + + " java.lang.reflect.Field one = fieldz[i];" + + " if ($1.equals(one.getName())) {" + + " return one;" + + " }" + + " }" + + " return null;" + + "}" + ); - method = clazz.getDeclaredMethod("getDeclaredFields"); - method.setBody( - "{" + - " return copyFields($0.getDeclaredFields0(false));" + - "}" - ); + method = clazz.getDeclaredMethod("getDeclaredFields"); + method.setBody( + "{" + + " return copyFields($0.getDeclaredFields0(false));" + + "}" + ); - inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); + inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); - clazz = pool.get("java.lang.ClassLoader"); - method = clazz.getDeclaredMethod("loadClass", new CtClass[] {string}); - method.insertBefore( - "if ($1.startsWith(\"\\.mod_\")) {" + - " $1 = $1.substring(1);" + - "}" - ); - - inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); - } catch (Exception e) { - e.printStackTrace(); - return false; - } + clazz = pool.get("java.lang.ClassLoader"); + method = clazz.getDeclaredMethod("loadClass", new CtClass[] {string}); + method.insertBefore( + "if ($1.startsWith(\"\\.mod_\")) {" + + " $1 = $1.substring(1);" + + "}" + ); + inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); return true; } diff --git a/src/main/java/uk/betacraft/legacyfix/patch/impl/TexturePackFolderPatch.java b/src/main/java/uk/betacraft/legacyfix/patch/impl/TexturePackFolderPatch.java new file mode 100644 index 0000000..45b6911 --- /dev/null +++ b/src/main/java/uk/betacraft/legacyfix/patch/impl/TexturePackFolderPatch.java @@ -0,0 +1,51 @@ +package uk.betacraft.legacyfix.patch.impl; + +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; + +import javassist.CtClass; +import javassist.CtMethod; +import uk.betacraft.legacyfix.LFLogger; +import uk.betacraft.legacyfix.patch.Patch; + +/** + * Patch for unresponsive "Open texture pack folder" button in versions before 1.2-pre on Linux and macOS + */ +public class TexturePackFolderPatch extends Patch { + public TexturePackFolderPatch() { + super("texturePackButton", "Patch open texture pack folder button"); + } + + @Override + public boolean apply(final Instrumentation inst) throws Exception { + CtClass clazz; + CtMethod method; + CtClass string = pool.get("java.lang.String"); + + clazz = pool.getOrNull("org.lwjgl.Sys"); + if (clazz == null) return false; + + method = clazz.getDeclaredMethod("openURL", new CtClass[] {string}); + method.insertBefore( + "if ($1 != null && $1.indexOf(\"file://\") == 0) {" + + " String txpfolder = $1.substring(7);" + + " try {" + + " Class desktopClass = Class.forName(\"java.awt.Desktop\");" + + " Object desktop = desktopClass.getMethod(\"getDesktop\").invoke((Object) null);" + + " desktopClass.getMethod(\"browse\", java.net.URI.class).invoke(desktop, (new java.io.File(txpfolder)).toURI());" + + " return true;" + + " } catch (Throwable t) {" + + " t.printStackTrace();" + // if failed, don't return and have a shot at the Sys.openURL method (vanilla behavior since 1.2-pre) + " }" + + "}" + ); + + inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); + return true; + } + + @Override + public boolean shouldApply() { + return true; + } +} diff --git a/src/main/java/uk/betacraft/legacyfix/patch/impl/TexturepackFolderPatch.java b/src/main/java/uk/betacraft/legacyfix/patch/impl/TexturepackFolderPatch.java deleted file mode 100644 index 164be6e..0000000 --- a/src/main/java/uk/betacraft/legacyfix/patch/impl/TexturepackFolderPatch.java +++ /dev/null @@ -1,59 +0,0 @@ -package uk.betacraft.legacyfix.patch.impl; - -import java.lang.instrument.ClassDefinition; -import java.lang.instrument.Instrumentation; - -import javassist.CtClass; -import javassist.CtMethod; -import uk.betacraft.legacyfix.patch.Patch; - -/** - * Patch for unresponsive "Open texture pack folder" button in versions before 1.2-pre on Linux and macOS - */ -public class TexturepackFolderPatch extends Patch { - - public TexturepackFolderPatch() { - super("patchTexturepackButton", "Patch open texture pack folder button"); - } - - @Override - public boolean apply(final Instrumentation inst) { - try { - CtClass clazz; - CtMethod method; - CtClass string = pool.get("java.lang.String"); - - clazz = pool.getOrNull("org.lwjgl.Sys"); - - if (clazz == null) - return true; - - method = clazz.getDeclaredMethod("openURL", new CtClass[] {string}); - method.insertBefore( - "if ($1 != null && $1.indexOf(\"file://\") == 0) {" + - " String txpfolder = $1.substring(7);" + - " try {" + - " Class desktopClass = Class.forName(\"java.awt.Desktop\");" + - " Object desktop = desktopClass.getMethod(\"getDesktop\").invoke((Object) null);" + - " desktopClass.getMethod(\"browse\", java.net.URI.class).invoke(desktop, (new java.io.File(txpfolder)).toURI());" + - " return true;" + // stop and return if successful - " } catch (Throwable t) {" + - " t.printStackTrace();" + // if failed, don't return and have a shot at the Sys.openURL method (vanilla behavior since 1.2-pre) - " }" + - "}" - ); - - inst.redefineClasses(new ClassDefinition(Class.forName(clazz.getName()), clazz.toBytecode())); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - - return true; - } - - @Override - public boolean shouldApply() { - return true; - } -}