diff --git a/build.gradle.kts b/build.gradle.kts index 274dbea..10d2f97 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,10 +10,12 @@ minecraft.version("1.8.9") repositories { maven("https://jitpack.io") + maven("https://repo.spongepowered.org/maven/") } dependencies { - compileOnly("com.github.Weave-MC:Weave-Loader:0c09d7496f") + compileOnly("org.spongepowered:mixin:0.8.5") + compileOnly("com.github.Weave-MC:Weave-Loader:6a9e6a3245") } tasks.compileJava { @@ -21,7 +23,5 @@ tasks.compileJava { } tasks.jar { - manifest.attributes( - "Weave-Entry" to "wtf.zani.vanillamenu.VanillaMenu" - ) + destinationDirectory.set(File("${System.getProperty("user.home")}/.lunarclient/mods")) } diff --git a/src/main/java/wtf/zani/vanillamenu/VanillaMenu.java b/src/main/java/wtf/zani/vanillamenu/VanillaMenu.java index 7fde492..7d792cc 100644 --- a/src/main/java/wtf/zani/vanillamenu/VanillaMenu.java +++ b/src/main/java/wtf/zani/vanillamenu/VanillaMenu.java @@ -1,19 +1,14 @@ package wtf.zani.vanillamenu; -import club.maxstats.weave.loader.api.HookManager; import club.maxstats.weave.loader.api.ModInitializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; -import wtf.zani.vanillamenu.hooks.MinecraftClientHook; public class VanillaMenu implements ModInitializer { public static final Logger logger = LogManager.getLogger(); @Override - public void preInit(@NotNull HookManager hookManager) { - logger.info("Adding Vanilla Menu's hook"); - - hookManager.register(new MinecraftClientHook()); + public void init() { + logger.info("Initializing VanillaMenu"); } } diff --git a/src/main/java/wtf/zani/vanillamenu/hooks/MinecraftClientHook.java b/src/main/java/wtf/zani/vanillamenu/hooks/MinecraftClientHook.java deleted file mode 100644 index 2974337..0000000 --- a/src/main/java/wtf/zani/vanillamenu/hooks/MinecraftClientHook.java +++ /dev/null @@ -1,105 +0,0 @@ -package wtf.zani.vanillamenu.hooks; - -import club.maxstats.weave.loader.api.Hook; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGameOver; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.ScaledResolution; -import org.jetbrains.annotations.NotNull; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; -import wtf.zani.vanillamenu.VanillaMenu; -import wtf.zani.vanillamenu.util.ClassUtil; - -import java.io.IOException; - -public class MinecraftClientHook extends Hook { - public static boolean isFirst = true; - - public MinecraftClientHook() { - super("net/minecraft/client/Minecraft"); - } - - @Override - public void transform(@NotNull ClassNode classNode, @NotNull AssemblerConfig assemblerConfig) { - final MethodNode displayGuiScreen = classNode.methods - .stream() - .filter(methodNode -> methodNode.name.equals("displayGuiScreen")) - .findFirst() - .orElseThrow(); - - displayGuiScreen.instructions.clear(); - - if (displayGuiScreen.localVariables != null) displayGuiScreen.localVariables.clear(); - if (displayGuiScreen.tryCatchBlocks != null) displayGuiScreen.tryCatchBlocks.clear(); - - try { - // completely overwrite the patches lunar does to displayGuiScreen with our own modified code - final ClassNode hookClassNode = ClassUtil.openClass(this.getClass().getName()); - final MethodNode hookDisplayGuiScreen = hookClassNode.methods - .stream() - .filter(methodNode -> methodNode.name.equals("displayGuiScreen")) - .findFirst() - .orElseThrow(); - - displayGuiScreen.instructions.add(hookDisplayGuiScreen.instructions); - - if (displayGuiScreen.localVariables != null) - displayGuiScreen.localVariables.addAll(hookDisplayGuiScreen.localVariables); - if (displayGuiScreen.tryCatchBlocks != null) - displayGuiScreen.tryCatchBlocks.addAll(hookDisplayGuiScreen.tryCatchBlocks); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unused") - public void displayGuiScreen(GuiScreen screen) { - final Minecraft mc = Minecraft.getMinecraft(); - - if (mc.currentScreen == null && isFirst) { - if (!screen.getClass().getName().equals(GuiMainMenu.class.getName())) { - VanillaMenu.logger.info("Replaced Lunar's main menu!"); - - isFirst = false; - - screen = new GuiMainMenu(); - } else { - return; - } - } - - if (mc.currentScreen != null) { - mc.currentScreen.onGuiClosed(); - } - - if (screen == null && mc.theWorld == null) { - screen = new GuiMainMenu(); - } else if (screen == null && mc.thePlayer.getHealth() <= 0.0F) { - screen = new GuiGameOver(); - } - - if (screen instanceof GuiMainMenu) { - mc.gameSettings.showDebugInfo = false; - mc.ingameGUI.getChatGUI().clearChatMessages(); - } - - mc.currentScreen = screen; - - if (screen != null) { - ScaledResolution scaledRes = new ScaledResolution(mc); - - int scaledWidth = scaledRes.getScaledWidth(); - int scaledHeight = scaledRes.getScaledHeight(); - - screen.setWorldAndResolution(mc, scaledWidth, scaledHeight); - - mc.setIngameNotInFocus(); - mc.skipRenderWorld = false; - } else { - mc.getSoundHandler().resumeSounds(); - mc.setIngameFocus(); - } - } -} diff --git a/src/main/java/wtf/zani/vanillamenu/hooks/VanillaMenuHook.java b/src/main/java/wtf/zani/vanillamenu/hooks/VanillaMenuHook.java new file mode 100644 index 0000000..1f4889d --- /dev/null +++ b/src/main/java/wtf/zani/vanillamenu/hooks/VanillaMenuHook.java @@ -0,0 +1,39 @@ +package wtf.zani.vanillamenu.hooks; + +import club.maxstats.weave.loader.api.Hook; +import org.jetbrains.annotations.NotNull; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.Arrays; + +@SuppressWarnings("unused") +public class VanillaMenuHook extends Hook { + public VanillaMenuHook() { + super("net/minecraft/client/Minecraft"); + } + + @Override + public void transform(@NotNull ClassNode classNode, @NotNull AssemblerConfig assemblerConfig) { + final MethodNode displayGuiScreen = classNode.methods + .stream() + .filter(methodNode -> methodNode.name.equals("displayGuiScreen")) + .findFirst() + .orElseThrow(); + final InsnList filteredInstructions = new InsnList(); + + Arrays.stream(displayGuiScreen.instructions.toArray()) + .filter(instruction -> { + if (instruction instanceof final MethodInsnNode methodCall) { + return !methodCall.name.endsWith("$impl$displayGuiScreen"); + } + + return true; + }) + .forEach(filteredInstructions::add); + + assemblerConfig.computeFrames(); + } +} diff --git a/src/main/java/wtf/zani/vanillamenu/mixin/MinecraftMixin.java b/src/main/java/wtf/zani/vanillamenu/mixin/MinecraftMixin.java new file mode 100644 index 0000000..e7dc88f --- /dev/null +++ b/src/main/java/wtf/zani/vanillamenu/mixin/MinecraftMixin.java @@ -0,0 +1,32 @@ +package wtf.zani.vanillamenu.mixin; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.multiplayer.WorldClient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import wtf.zani.vanillamenu.VanillaMenu; + +@Mixin(Minecraft.class) +public class MinecraftMixin { + @Shadow + public WorldClient theWorld; + + private boolean isFirst = true; + + @ModifyVariable(at = @At(value = "HEAD"), method = "displayGuiScreen", argsOnly = true) + public GuiScreen modifyScreen(GuiScreen screen) { + if (this.theWorld == null && screen != null && !screen.getClass().getName().startsWith("net.minecraft") && isFirst) { + isFirst = false; + + VanillaMenu.logger.info("Replaced Lunar's main menu!"); + + return new GuiMainMenu(); + } + + return screen; + } +} diff --git a/src/main/java/wtf/zani/vanillamenu/util/ClassUtil.java b/src/main/java/wtf/zani/vanillamenu/util/ClassUtil.java deleted file mode 100644 index 3044042..0000000 --- a/src/main/java/wtf/zani/vanillamenu/util/ClassUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -package wtf.zani.vanillamenu.util; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.tree.ClassNode; -import wtf.zani.vanillamenu.VanillaMenu; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Objects; - -public class ClassUtil { - public static ClassNode openClass(String className) throws IOException { - final InputStream classStream = Objects.requireNonNull( - ClassUtil.class.getClassLoader() - .getResourceAsStream(className.replace('.', '/') + ".class")); - - final ClassNode classNode = new ClassNode(); - final ClassReader classReader = new ClassReader(classStream.readAllBytes()); - - classReader.accept(classNode, 0); - classStream.close(); - - return classNode; - } - - // unused but helpful for debugging - public static void dumpClass(ClassNode classNode) { - try { - final ClassWriter classWriter = new ClassWriter(0); - - classNode.accept(classWriter); - - final byte[] classBytes = classWriter.toByteArray(); - - final OutputStream outputFile = new FileOutputStream(classNode.name.replace('/', '.')); - - outputFile.write(classBytes); - outputFile.close(); - } catch (IOException exception) { - VanillaMenu.logger.warn("Failed to dump class", exception); - } - } -} diff --git a/src/main/resources/vanillamenu.mixins.json b/src/main/resources/vanillamenu.mixins.json new file mode 100644 index 0000000..ff3e66a --- /dev/null +++ b/src/main/resources/vanillamenu.mixins.json @@ -0,0 +1,7 @@ +{ + "compatibilityLevel": "JAVA_8", + "package": "wtf.zani.vanillamenu.mixin", + "mixins": [ + "MinecraftMixin" + ] +} \ No newline at end of file diff --git a/src/main/resources/weave.mod.json b/src/main/resources/weave.mod.json new file mode 100644 index 0000000..8332c20 --- /dev/null +++ b/src/main/resources/weave.mod.json @@ -0,0 +1,11 @@ +{ + "mixinConfigs": [ + "vanillamenu.mixins.json" + ], + "hooks": [ + "wtf.zani.vanillamenu.hooks.VanillaMenuHook" + ], + "entrypoints": [ + "wtf.zani.vanillamenu.VanillaMenu" + ] +} \ No newline at end of file