diff --git a/src/main/java/com/replaymod/core/KeyBindingRegistry.java b/src/main/java/com/replaymod/core/KeyBindingRegistry.java index efbe38bc..addb407e 100644 --- a/src/main/java/com/replaymod/core/KeyBindingRegistry.java +++ b/src/main/java/com/replaymod/core/KeyBindingRegistry.java @@ -35,7 +35,9 @@ import org.lwjgl.glfw.GLFW; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; public class KeyBindingRegistry extends EventRegistrations { private static final String CATEGORY = "replaymod.title"; @@ -44,19 +46,20 @@ public class KeyBindingRegistry extends EventRegistrations { //#endif private Map keyBindings = new HashMap(); + private Set onlyInReplay = new HashSet<>(); private Multimap keyBindingHandlers = ArrayListMultimap.create(); private Multimap repeatedKeyBindingHandlers = ArrayListMultimap.create(); private Multimap rawHandlers = ArrayListMultimap.create(); - public void registerKeyBinding(String name, int keyCode, Runnable whenPressed) { - keyBindingHandlers.put(registerKeyBinding(name, keyCode), whenPressed); + public void registerKeyBinding(String name, int keyCode, Runnable whenPressed, boolean onlyInRepay) { + keyBindingHandlers.put(registerKeyBinding(name, keyCode, onlyInRepay), whenPressed); } - public void registerRepeatedKeyBinding(String name, int keyCode, Runnable whenPressed) { - repeatedKeyBindingHandlers.put(registerKeyBinding(name, keyCode), whenPressed); + public void registerRepeatedKeyBinding(String name, int keyCode, Runnable whenPressed, boolean onlyInRepay) { + repeatedKeyBindingHandlers.put(registerKeyBinding(name, keyCode, onlyInRepay), whenPressed); } - private KeyBinding registerKeyBinding(String name, int keyCode) { + private KeyBinding registerKeyBinding(String name, int keyCode, boolean onlyInRepay) { KeyBinding keyBinding = keyBindings.get(name); if (keyBinding == null) { //#if FABRIC>=1 @@ -72,6 +75,11 @@ public class KeyBindingRegistry extends EventRegistrations { //$$ ClientRegistry.registerKeyBinding(keyBinding); //#endif keyBindings.put(name, keyBinding); + if (onlyInRepay) { + this.onlyInReplay.add(keyBinding); + } + } else if (!onlyInRepay) { + this.onlyInReplay.remove(keyBinding); } return keyBinding; } @@ -84,6 +92,10 @@ public class KeyBindingRegistry extends EventRegistrations { return Collections.unmodifiableMap(keyBindings); } + public Set getOnlyInReplay() { + return Collections.unmodifiableSet(onlyInReplay); + } + //#if MC>=11400 { on(KeyBindingEventCallback.EVENT, this::handleKeyBindings); } { on(KeyEventCallback.EVENT, (keyCode, scanCode, action, modifiers) -> handleRaw(keyCode, action)); } diff --git a/src/main/java/com/replaymod/core/ReplayMod.java b/src/main/java/com/replaymod/core/ReplayMod.java index 0fb75540..48388e23 100644 --- a/src/main/java/com/replaymod/core/ReplayMod.java +++ b/src/main/java/com/replaymod/core/ReplayMod.java @@ -318,7 +318,7 @@ public class ReplayMod implements public void registerKeyBindings(KeyBindingRegistry registry) { registry.registerKeyBinding("replaymod.input.settings", 0, () -> { new GuiReplaySettings(null, settingsRegistry).display(); - }); + }, false); } @Override diff --git a/src/main/java/com/replaymod/core/mixin/Mixin_ContextualKeyBindings.java b/src/main/java/com/replaymod/core/mixin/Mixin_ContextualKeyBindings.java new file mode 100644 index 00000000..18e1ec58 --- /dev/null +++ b/src/main/java/com/replaymod/core/mixin/Mixin_ContextualKeyBindings.java @@ -0,0 +1,63 @@ +//#if MC>=11400 +package com.replaymod.core.mixin; + +import com.replaymod.core.ReplayMod; +import com.replaymod.replay.ReplayModReplay; +import net.minecraft.client.options.KeyBinding; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * We have bunch of keybindings which only have an effect while in a replay but heavily conflict with vanilla ones + * otherwise. To work around this, we prevent our keybindings (or conflicting ones) from making it into the keysByCode + * map, depending on the current context. + */ +@Mixin(KeyBinding.class) +public class Mixin_ContextualKeyBindings { + @Shadow @Final private static Map keysById; + + @Unique private static final List temporarilyRemoved = new ArrayList<>(); + + @Inject(method = "updateKeysByCode", at = @At("HEAD")) + private static void preContextualKeyBindings(CallbackInfo ci) { + Set onlyInReplay = ReplayMod.instance.getKeyBindingRegistry().getOnlyInReplay(); + if (ReplayModReplay.instance.getReplayHandler() != null) { + // In replay, remove any conflicting key bindings, so that ours are guaranteed in + Mixin_ContextualKeyBindings.keysById.values().removeIf(keyBinding -> { + for (KeyBinding exclusiveBinding : onlyInReplay) { + if (keyBinding.equals(exclusiveBinding) && keyBinding != exclusiveBinding) { + temporarilyRemoved.add(keyBinding); + return true; + } + } + return false; + }); + } else { + // Not in a replay, remove all replay-exclusive keybindings + for (KeyBinding keyBinding : onlyInReplay) { + if (Mixin_ContextualKeyBindings.keysById.remove(keyBinding.getId()) != null) { + temporarilyRemoved.add(keyBinding); + } + } + } + } + + @Inject(method = "updateKeysByCode", at = @At("RETURN")) + private static void postContextualKeyBindings(CallbackInfo ci) { + for (KeyBinding keyBinding : temporarilyRemoved) { + Mixin_ContextualKeyBindings.keysById.put(keyBinding.getId(), keyBinding); + } + temporarilyRemoved.clear(); + } +} +//#endif diff --git a/src/main/java/com/replaymod/extras/FullBrightness.java b/src/main/java/com/replaymod/extras/FullBrightness.java index 5587e3f8..2459af5b 100644 --- a/src/main/java/com/replaymod/extras/FullBrightness.java +++ b/src/main/java/com/replaymod/extras/FullBrightness.java @@ -53,7 +53,7 @@ public class FullBrightness extends EventRegistrations implements Extra { updateIndicator(replayHandler.getOverlay()); } } - }); + }, true); register(); } diff --git a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java index 65fa6a3b..0cbbbf8c 100644 --- a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java +++ b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java @@ -93,7 +93,7 @@ public class PlayerOverview extends EventRegistrations implements Extra { new PlayerOverviewGui(PlayerOverview.this, players).display(); } } - }); + }, true); register(); } diff --git a/src/main/java/com/replaymod/recording/ReplayModRecording.java b/src/main/java/com/replaymod/recording/ReplayModRecording.java index b37a416d..94ae0902 100644 --- a/src/main/java/com/replaymod/recording/ReplayModRecording.java +++ b/src/main/java/com/replaymod/recording/ReplayModRecording.java @@ -58,7 +58,7 @@ public class ReplayModRecording implements Module { core.printInfoToChat("replaymod.chat.addedmarker"); } } - }); + }, false); } @Override diff --git a/src/main/java/com/replaymod/replay/ReplayHandler.java b/src/main/java/com/replaymod/replay/ReplayHandler.java index 18769e4d..5c88dfc0 100644 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -237,7 +237,7 @@ public class ReplayHandler { //#endif overlay.setVisible(false); - ReplayModReplay.instance.replayHandler = null; + ReplayModReplay.instance.forcefullyStopReplay(); mc.openScreen(null); diff --git a/src/main/java/com/replaymod/replay/ReplayModReplay.java b/src/main/java/com/replaymod/replay/ReplayModReplay.java index da416ae3..cd484740 100644 --- a/src/main/java/com/replaymod/replay/ReplayModReplay.java +++ b/src/main/java/com/replaymod/replay/ReplayModReplay.java @@ -23,6 +23,7 @@ import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.studio.ReplayStudio; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.options.KeyBinding; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -46,7 +47,7 @@ public class ReplayModReplay implements Module { public static Logger LOGGER = LogManager.getLogger(); - protected ReplayHandler replayHandler; + private ReplayHandler replayHandler; public ReplayHandler getReplayHandler() { return replayHandler; @@ -78,7 +79,7 @@ public class ReplayModReplay implements Module { } } } - }); + }, true); registry.registerKeyBinding("replaymod.input.thumbnail", Keyboard.KEY_N, new Runnable() { @Override @@ -113,7 +114,7 @@ public class ReplayModReplay implements Module { }); } } - }); + }, true); registry.registerKeyBinding("replaymod.input.playpause", Keyboard.KEY_P, new Runnable() { @Override @@ -122,7 +123,7 @@ public class ReplayModReplay implements Module { replayHandler.getOverlay().playPauseButton.onClick(); } } - }); + }, true); registry.registerKeyBinding("replaymod.input.quickmode", Keyboard.KEY_Q, () -> { if (replayHandler != null) { @@ -133,19 +134,19 @@ public class ReplayModReplay implements Module { replayHandler.getReplaySender().setAsyncMode(true); })); } - }); + }, true); core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.rollclockwise", Keyboard.KEY_L, () -> { // Noop, actual handling logic in CameraEntity#update - }); + }, true); core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.rollcounterclockwise", Keyboard.KEY_J, () -> { // Noop, actual handling logic in CameraEntity#update - }); + }, true); core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.resettilt", Keyboard.KEY_K, () -> { Optional.ofNullable(replayHandler).map(ReplayHandler::getCameraEntity).ifPresent(c -> c.roll = 0); - }); + }, true); } @Override @@ -191,10 +192,16 @@ public class ReplayModReplay implements Module { } } replayHandler = new ReplayHandler(replayFile, true); + //#if MC>=11400 + KeyBinding.updateKeysByCode(); // see Mixin_ContextualKeyBindings + //#endif } public void forcefullyStopReplay() { replayHandler = null; + //#if MC>=11400 + KeyBinding.updateKeysByCode(); // see Mixin_ContextualKeyBindings + //#endif } public ReplayMod getCore() { diff --git a/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java b/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java index 6dd3c208..72409aeb 100644 --- a/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java +++ b/src/main/java/com/replaymod/simplepathing/ReplayModSimplePathing.java @@ -82,13 +82,13 @@ public class ReplayModSimplePathing extends EventRegistrations implements Module pathPreview.registerKeyBindings(registry); core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.keyframerepository", Keyboard.KEY_X, () -> { if (guiPathing != null) guiPathing.keyframeRepoButtonPressed(); - }); + }, true); core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.clearkeyframes", Keyboard.KEY_C, () -> { if (guiPathing != null) guiPathing.clearKeyframesButtonPressed(); - }); + }, true); core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> { if (guiPathing != null) guiPathing.syncTimeButtonPressed(); - }); + }, true); core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> { if (guiPathing != null) guiPathing.deleteButtonPressed(); }); diff --git a/src/main/java/com/replaymod/simplepathing/preview/PathPreview.java b/src/main/java/com/replaymod/simplepathing/preview/PathPreview.java index fb11f524..4c3be797 100644 --- a/src/main/java/com/replaymod/simplepathing/preview/PathPreview.java +++ b/src/main/java/com/replaymod/simplepathing/preview/PathPreview.java @@ -42,7 +42,7 @@ public class PathPreview extends EventRegistrations { SettingsRegistry settings = mod.getCore().getSettingsRegistry(); settings.set(Setting.PATH_PREVIEW, !settings.get(Setting.PATH_PREVIEW)); settings.save(); - }); + }, true); } private void update() { diff --git a/src/main/resources/mixins.core.replaymod.json b/src/main/resources/mixins.core.replaymod.json index 6d4fcac1..c403dc01 100644 --- a/src/main/resources/mixins.core.replaymod.json +++ b/src/main/resources/mixins.core.replaymod.json @@ -6,6 +6,7 @@ "client": [ //#if MC>=11400 "AbstractButtonWidgetAccessor", + "Mixin_ContextualKeyBindings", "MixinGameRenderer", "MixinModResourcePackUtil", //#endif