From 8ff27437afdae52974d52623ae9d52f823c10aa4 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 28 Feb 2021 00:32:33 +0100 Subject: [PATCH] Add configurable, small Replay Viewer button (closes #471) --- build.gradle | 9 +- .../com/replaymod/core/versions/MCVer.java | 6 + .../java/com/replaymod/replay/Setting.java | 10 ++ .../replaymod/replay/handler/GuiHandler.java | 120 +++++++++++++++++- .../replay/mixin/Mixin_MoveRealmsButton.java | 9 +- 5 files changed, 150 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 38d975af..5e9b99ab 100644 --- a/build.gradle +++ b/build.gradle @@ -199,6 +199,12 @@ repositories { name = "fabric" url = "https://maven.fabricmc.net/" } + maven { + url 'https://maven.terraformersmc.com/releases/' + content { + includeGroup 'com.terraformersmc' + } + } maven { url 'https://jitpack.io' content { @@ -323,8 +329,7 @@ dependencies { if (FABRIC) { if (mcVersion >= 11602) { - // TODO re-add to runtime once available - modCompileOnly 'io.github.prospector:modmenu:1.14.0+build.24' + modCompile 'com.terraformersmc:modmenu:1.16.8' } else if (mcVersion >= 11600) { modCompile 'io.github.prospector:modmenu:1.14.0+build.24' } else { diff --git a/src/main/java/com/replaymod/core/versions/MCVer.java b/src/main/java/com/replaymod/core/versions/MCVer.java index b14cc869..d5282f4b 100644 --- a/src/main/java/com/replaymod/core/versions/MCVer.java +++ b/src/main/java/com/replaymod/core/versions/MCVer.java @@ -223,6 +223,12 @@ public class MCVer { if (message.equals(b.getMessage())) { return Optional.of(b); } + //#if MC>=11600 + // Fuzzy match (copy does not include children) + if (b.getMessage() != null && b.getMessage().copy().equals(message)) { + return Optional.of(b); + } + //#endif } return Optional.empty(); } diff --git a/src/main/java/com/replaymod/replay/Setting.java b/src/main/java/com/replaymod/replay/Setting.java index a16e42e7..b2303c61 100644 --- a/src/main/java/com/replaymod/replay/Setting.java +++ b/src/main/java/com/replaymod/replay/Setting.java @@ -1,6 +1,10 @@ package com.replaymod.replay; import com.replaymod.core.SettingsRegistry; +import com.replaymod.replay.handler.GuiHandler.MainMenuButtonPosition; + +import java.util.Arrays; +import java.util.stream.Collectors; public final class Setting extends SettingsRegistry.SettingKeys { public static final Setting SHOW_CHAT = make("showChat", "showchat", true); @@ -8,6 +12,12 @@ public final class Setting extends SettingsRegistry.SettingKeys { public static final SettingsRegistry.MultipleChoiceSettingKeys CAMERA = new SettingsRegistry.MultipleChoiceSettingKeys<>( "replay", "camera", "replaymod.gui.settings.camera", "replaymod.camera.classic"); + public static final SettingsRegistry.MultipleChoiceSettingKeys MAIN_MENU_BUTTON = + new SettingsRegistry.MultipleChoiceSettingKeys<>( + "replay", "mainMenuButton", null, MainMenuButtonPosition.DEFAULT.name()); + static { + MAIN_MENU_BUTTON.setChoices(Arrays.stream(MainMenuButtonPosition.values()).map(Enum::name).collect(Collectors.toList())); + } private static Setting make(String key, String displayName, T defaultValue) { return new Setting<>(key, displayName, defaultValue); diff --git a/src/main/java/com/replaymod/replay/handler/GuiHandler.java b/src/main/java/com/replaymod/replay/handler/GuiHandler.java index af8f67ff..8f8b4a85 100644 --- a/src/main/java/com/replaymod/replay/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/replay/handler/GuiHandler.java @@ -1,6 +1,12 @@ package com.replaymod.replay.handler; +import com.replaymod.core.gui.GuiReplayButton; +import com.replaymod.replay.Setting; +import de.johni0702.minecraft.gui.container.VanillaGuiScreen; +import de.johni0702.minecraft.gui.element.GuiTooltip; +import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.utils.lwjgl.Point; import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.gui.screen.GuiReplayViewer; @@ -28,9 +34,11 @@ import net.minecraft.client.gui.widget.ButtonWidget; import java.io.IOException; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.function.Consumer; +import java.util.stream.Stream; import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.replay.ReplayModReplay.LOGGER; @@ -217,6 +225,34 @@ public class GuiHandler extends EventRegistrations { return; } + boolean isCustomMainMenuMod = guiScreen.getClass().getName().endsWith("custommainmenu.gui.GuiFakeMain"); + + MainMenuButtonPosition buttonPosition = MainMenuButtonPosition.valueOf(mod.getCore().getSettingsRegistry().get(Setting.MAIN_MENU_BUTTON)); + if (buttonPosition != MainMenuButtonPosition.BIG && !isCustomMainMenuMod) { + VanillaGuiScreen vanillaGui = VanillaGuiScreen.wrap(guiScreen); + + GuiReplayButton replayButton = new GuiReplayButton(); + replayButton + .onClick(() -> new GuiReplayViewer(mod).display()) + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.replayviewer")); + + vanillaGui.setLayout(new CustomLayout(vanillaGui.getLayout()) { + private Point pos; + + @Override + protected void layout(de.johni0702.minecraft.gui.container.GuiScreen container, int width, int height) { + if (pos == null) { + // Delaying computation so we can take into account buttons + // added after our callback. + pos = determineButtonPos(buttonPosition, guiScreen, buttonList); + } + size(replayButton, 20, 20); + pos(replayButton, pos.getX(), pos.getY()); + } + }).addElements(null, replayButton); + return; + } + int x = guiScreen.width / 2 - 100; // We want to position our button below the realms button int y = findButton(buttonList, "menu.online", 14) @@ -246,7 +282,7 @@ public class GuiHandler extends EventRegistrations { this::onButton ); //#if FABRIC<=0 - //$$ if (guiScreen.getClass().getName().endsWith("custommainmenu.gui.GuiFakeMain")) { + //$$ if (isCustomMainMenuMod) { //$$ // CustomMainMenu uses a different list in the event than in its Fake gui //$$ buttonList.add(button); //$$ return; @@ -255,6 +291,70 @@ public class GuiHandler extends EventRegistrations { addButton(guiScreen, button); } + private Point determineButtonPos(MainMenuButtonPosition buttonPosition, Screen guiScreen, List buttonList) { + Point topRight = new Point(guiScreen.width - 20 - 5, 5); + + if (buttonPosition == MainMenuButtonPosition.TOP_LEFT) { + return new Point(5, 5); + } else if (buttonPosition == MainMenuButtonPosition.TOP_RIGHT) { + return topRight; + } else if (buttonPosition == MainMenuButtonPosition.DEFAULT) { + return Stream.of( + findButton(buttonList, "menu.singleplayer", 1), + findButton(buttonList, "menu.multiplayer", 2), + findButton(buttonList, "menu.online", 14), + findButton(buttonList, "modmenu.title", 6) + ) + // skip buttons which do not exist + .flatMap(it -> it.map(Stream::of).orElseGet(Stream::empty)) + // skip buttons which already have something next to them + .filter(it -> buttonList.stream().noneMatch(button -> + button.x <= it.x + it.getWidth() + 4 + 20 + && button.y <= it.y + it.getHeight() + && button.x + button.getWidth() >= it.x + it.getWidth() + 4 + && button.y + button.getHeight() >= it.y + )) + // then take the bottom-most and if there's two, the right-most + .max(Comparator.comparingInt(it -> it.y).thenComparingInt(it -> it.x)) + // and place ourselves next to it + .map(it -> new Point(it.x + it.getWidth() + 4, it.y)) + // if all fails, just go with TOP_RIGHT + .orElse(topRight); + } else { + return Optional.of(buttonList).flatMap(buttons -> { + switch (buttonPosition) { + case LEFT_OF_SINGLEPLAYER: + case RIGHT_OF_SINGLEPLAYER: + return findButton(buttons, "menu.singleplayer", 1); + case LEFT_OF_MULTIPLAYER: + case RIGHT_OF_MULTIPLAYER: + return findButton(buttons, "menu.multiplayer", 2); + case LEFT_OF_REALMS: + case RIGHT_OF_REALMS: + return findButton(buttons, "menu.online", 14); + case LEFT_OF_MODS: + case RIGHT_OF_MODS: + return findButton(buttons, "modmenu.title", 6); + } + throw new RuntimeException(); + }).map(button -> { + switch (buttonPosition) { + case LEFT_OF_SINGLEPLAYER: + case LEFT_OF_MULTIPLAYER: + case LEFT_OF_REALMS: + case LEFT_OF_MODS: + return new Point(button.x - 4 - 20, button.y); + case RIGHT_OF_MODS: + case RIGHT_OF_SINGLEPLAYER: + case RIGHT_OF_MULTIPLAYER: + case RIGHT_OF_REALMS: + return new Point(button.x + button.getWidth() + 4, button.y); + } + throw new RuntimeException(); + }).orElse(topRight); + } + } + //#if MC>=11400 private void onButton(InjectedButton button) { Screen guiScreen = button.guiScreen; @@ -334,4 +434,22 @@ public class GuiHandler extends EventRegistrations { //$$ } //#endif } + + public enum MainMenuButtonPosition { + // The old big button below Realms/Mods which pushes other buttons around. + BIG, + // Right of the bottom-most button in the main block of buttons (so not the quit button). + // That will generally be either RIGHT_OF_REALMS or RIGHT_OF_MODS depending on version and installed mods. + DEFAULT, + TOP_LEFT, + TOP_RIGHT, + LEFT_OF_SINGLEPLAYER, + RIGHT_OF_SINGLEPLAYER, + LEFT_OF_MULTIPLAYER, + RIGHT_OF_MULTIPLAYER, + LEFT_OF_REALMS, + RIGHT_OF_REALMS, + LEFT_OF_MODS, + RIGHT_OF_MODS, + } } diff --git a/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java b/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java index 12f19084..3066899f 100644 --- a/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java +++ b/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java @@ -1,6 +1,9 @@ //#if MC>=11600 package com.replaymod.replay.mixin; +import com.replaymod.core.ReplayMod; +import com.replaymod.replay.Setting; +import com.replaymod.replay.handler.GuiHandler.MainMenuButtonPosition; import net.minecraft.client.gui.screen.TitleScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -14,7 +17,11 @@ public abstract class Mixin_MoveRealmsButton { index = 2 ) private int adjustRealmsButton(int height) { - return height - 24 * 4; + String setting = ReplayMod.instance.getSettingsRegistry().get(Setting.MAIN_MENU_BUTTON); + if (MainMenuButtonPosition.valueOf(setting) == MainMenuButtonPosition.BIG) { + height -= 24 * 4; + } + return height; } } //#endif