diff --git a/src/main/java/dev/cigarette/Cigarette.java b/src/main/java/dev/cigarette/Cigarette.java index 1c27841c..a02cec91 100644 --- a/src/main/java/dev/cigarette/Cigarette.java +++ b/src/main/java/dev/cigarette/Cigarette.java @@ -7,6 +7,7 @@ import dev.cigarette.config.Config; import dev.cigarette.config.FileSystem; import dev.cigarette.events.Events; +import dev.cigarette.gui.CigaretteScreen; import dev.cigarette.gui.hud.notification.NotificationDisplay; import dev.cigarette.lib.ChatLogger; import net.fabricmc.api.ModInitializer; @@ -37,6 +38,7 @@ public class Cigarette implements ModInitializer { public static final Identifier LOGO_IDENTIFIER = Identifier.of("cigarette", "icon.png"); public static final boolean IN_DEV_ENVIRONMENT = FabricLoader.getInstance().isDevelopmentEnvironment(); public static Config CONFIG = Config.construct(); + public static CigaretteScreen SCREEN = new CigaretteScreen(); public static BedwarsAgent BEDWARS_AGENT = new BedwarsAgent(DevWidget.bedwarsAgent); public static MurderMysteryAgent MURDER_MYSTERY_AGENT = new MurderMysteryAgent(DevWidget.murderMysteryAgent); public static ZombiesAgent ZOMBIES_AGENT = new ZombiesAgent(DevWidget.zombiesAgent); diff --git a/src/main/java/dev/cigarette/gui/CigaretteScreen.java b/src/main/java/dev/cigarette/gui/CigaretteScreen.java index 0d084078..733e5603 100644 --- a/src/main/java/dev/cigarette/gui/CigaretteScreen.java +++ b/src/main/java/dev/cigarette/gui/CigaretteScreen.java @@ -90,7 +90,7 @@ public class CigaretteScreen extends Screen { */ public static @Nullable KeybindWidget bindingKey = null; - protected CigaretteScreen() { + public CigaretteScreen() { super(Text.literal("Cigarette Client")); } diff --git a/src/main/java/dev/cigarette/gui/KeyBinding.java b/src/main/java/dev/cigarette/gui/KeyBinding.java deleted file mode 100644 index 3745c97d..00000000 --- a/src/main/java/dev/cigarette/gui/KeyBinding.java +++ /dev/null @@ -1,34 +0,0 @@ -package dev.cigarette.gui; - -import dev.cigarette.Cigarette; -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.minecraft.client.util.InputUtil; -import net.minecraft.util.Identifier; -import org.lwjgl.glfw.GLFW; - -public class KeyBinding implements ClientModInitializer { - @SuppressWarnings("unused") - private static final Identifier HUD_KEYBINDING_ID = Identifier.of(Cigarette.MOD_ID, "hud-keybinding"); - - private static net.minecraft.client.option.KeyBinding keyBinding; - - private static final CigaretteScreen screen = new CigaretteScreen(); - - @Override - public void onInitializeClient() { - keyBinding = KeyBindingHelper.registerKeyBinding(new net.minecraft.client.option.KeyBinding("Toggle GUI", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_RIGHT_SHIFT, "Cigarette | User Interface")); - - ClientTickEvents.END_CLIENT_TICK.register(client -> { - while (keyBinding.wasPressed()) { - if (client.currentScreen instanceof CigaretteScreen) { - client.currentScreen.close(); - } else { - screen.setParent(client.currentScreen); - client.setScreen(screen); - } - } - }); - } -} diff --git a/src/main/java/dev/cigarette/gui/widget/KeybindWidget.java b/src/main/java/dev/cigarette/gui/widget/KeybindWidget.java index cf168ccb..8e3b6194 100644 --- a/src/main/java/dev/cigarette/gui/widget/KeybindWidget.java +++ b/src/main/java/dev/cigarette/gui/widget/KeybindWidget.java @@ -2,32 +2,25 @@ import dev.cigarette.Cigarette; import dev.cigarette.gui.CigaretteScreen; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import dev.cigarette.helper.KeybindHelper; +import dev.cigarette.helper.keybind.VirtualKeybind; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.option.KeyBinding; import net.minecraft.client.util.InputUtil; import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import java.util.Objects; -import java.util.UUID; import java.util.function.Consumer; /** * A widget that lets the user bind a key. */ public class KeybindWidget extends BaseWidget { - /** - * The internal Minecraft KeyBinding. - */ - private final KeyBinding keyBinding; - /** - * The KeyBindings actual key for rendering and configuration. - */ - private InputUtil.Key utilKey; + private final VirtualKeybind keyBinding = new VirtualKeybind(GLFW.GLFW_KEY_UNKNOWN); + private InputUtil.Key utilKey = InputUtil.UNKNOWN_KEY; /** * Creates a widget that stores a keybind and allows it to be configured. @@ -37,9 +30,7 @@ public class KeybindWidget extends BaseWidget { */ public KeybindWidget(String message, @Nullable String tooltip) { super(message, tooltip); - this.utilKey = InputUtil.UNKNOWN_KEY; - this.keyBinding = new KeyBinding(UUID.randomUUID().toString(), GLFW.GLFW_KEY_UNKNOWN, "cigarette.null"); - KeyBindingHelper.registerKeyBinding(this.keyBinding); + KeybindHelper.registerVirtualKey(this.keyBinding); } /** @@ -50,26 +41,19 @@ public KeybindWidget(String message, @Nullable String tooltip) { */ public KeybindWidget withDefaultKey(int key) { utilKey = InputUtil.fromKeyCode(key, 0); - keyBinding.setBoundKey(utilKey); + keyBinding.setDefaultKey(key); + keyBinding.setKey(key); super.withDefault(key); return this; } - /** - * Update the stored key of this widget from an input event. - * - * @param key The new key to set to this widget - */ - public void setBoundKey(@Nullable InputUtil.Key key) { - utilKey = key == null ? InputUtil.UNKNOWN_KEY : key; - keyBinding.setBoundKey(utilKey); - this.setRawState(utilKey.getCode()); + public void setBoundKey(int key) { + utilKey = key == GLFW.GLFW_KEY_UNKNOWN ? InputUtil.UNKNOWN_KEY : InputUtil.fromKeyCode(key, 0); + keyBinding.setKey(key); + this.setRawState(key); } - /** - * {@return the internal Minecraft KeyBinding} - */ - public KeyBinding getKeybind() { + public VirtualKeybind getKeybind() { return this.keyBinding; } @@ -143,12 +127,9 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if (!isBinding()) return false; if (keyCode == GLFW.GLFW_KEY_ESCAPE || keyCode == GLFW.GLFW_KEY_RIGHT_SHIFT) { - this.setBoundKey(null); + this.setBoundKey(GLFW.GLFW_KEY_UNKNOWN); } else { - InputUtil.Key key = InputUtil.fromKeyCode(keyCode, scanCode); - String keyName = key.getLocalizedText().getLiteralString(); - if (keyName == null) return true; - this.setBoundKey(key); + this.setBoundKey(keyCode); } clearBinding(); return true; diff --git a/src/main/java/dev/cigarette/gui/widget/ToggleKeybindWidget.java b/src/main/java/dev/cigarette/gui/widget/ToggleKeybindWidget.java index 3af56ca7..81af620e 100644 --- a/src/main/java/dev/cigarette/gui/widget/ToggleKeybindWidget.java +++ b/src/main/java/dev/cigarette/gui/widget/ToggleKeybindWidget.java @@ -2,7 +2,6 @@ import dev.cigarette.module.BaseModule; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.option.KeyBinding; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; @@ -44,13 +43,6 @@ public ToggleKeybindWidget withDefaultKey(int keyCode) { return this; } - /** - * {@return the internal Minecraft KeyBinding} - */ - public KeyBinding getKeybind() { - return this.widget.getKeybind(); - } - /** * Generator for modules using this as a top-level widget. * diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java new file mode 100644 index 00000000..9b600260 --- /dev/null +++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java @@ -0,0 +1,298 @@ +package dev.cigarette.helper; + +import dev.cigarette.Cigarette; +import dev.cigarette.gui.CigaretteScreen; +import dev.cigarette.helper.keybind.InputBlocker; +import dev.cigarette.helper.keybind.MinecraftKeybind; +import dev.cigarette.helper.keybind.VirtualKeybind; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; + +import java.util.HashSet; + +/** + * Helper class for handling the physical and virtual state of keybinds. Also supports input blocking via {@code InputBlocker}. + */ +public class KeybindHelper { + public static final MinecraftKeybind KEY_SPRINT = new MinecraftKeybind("key.sprint"); + public static final MinecraftKeybind KEY_SNEAK = new MinecraftKeybind("key.sneak"); + public static final MinecraftKeybind KEY_MOVE_FORWARD = new MinecraftKeybind("key.forward"); + public static final MinecraftKeybind KEY_MOVE_BACK = new MinecraftKeybind("key.back"); + public static final MinecraftKeybind KEY_MOVE_LEFT = new MinecraftKeybind("key.left"); + public static final MinecraftKeybind KEY_MOVE_RIGHT = new MinecraftKeybind("key.right"); + public static final MinecraftKeybind KEY_USE_ITEM = new MinecraftKeybind("key.use", true); + public static final MinecraftKeybind KEY_ATTACK = new MinecraftKeybind("key.attack", true); + public static final MinecraftKeybind KEY_PICK_ITEM = new MinecraftKeybind("key.pickItem", true); + public static final MinecraftKeybind KEY_JUMP = new MinecraftKeybind("key.jump"); + public static final MinecraftKeybind KEY_DROP_ITEM = new MinecraftKeybind("key.drop"); + public static final MinecraftKeybind KEY_TOGGLE_INVENTORY = new MinecraftKeybind("key.inventory"); + public static final MinecraftKeybind KEY_SLOT_1 = new MinecraftKeybind("key.hotbar.1"); + public static final MinecraftKeybind KEY_SLOT_2 = new MinecraftKeybind("key.hotbar.2"); + public static final MinecraftKeybind KEY_SLOT_3 = new MinecraftKeybind("key.hotbar.3"); + public static final MinecraftKeybind KEY_SLOT_4 = new MinecraftKeybind("key.hotbar.4"); + public static final MinecraftKeybind KEY_SLOT_5 = new MinecraftKeybind("key.hotbar.5"); + public static final MinecraftKeybind KEY_SLOT_6 = new MinecraftKeybind("key.hotbar.6"); + public static final MinecraftKeybind KEY_SLOT_7 = new MinecraftKeybind("key.hotbar.7"); + public static final MinecraftKeybind KEY_SLOT_8 = new MinecraftKeybind("key.hotbar.8"); + public static final MinecraftKeybind KEY_SLOT_9 = new MinecraftKeybind("key.hotbar.9"); + + /** + * Set of bindings that wrap native Minecraft {@code KeyBinding}'s. + */ + private static final HashSet wrappedBindings = new HashSet<>(); + + /** + * Keybind to toggle {@code CigaretteScreen}. + */ + public static final VirtualKeybind KEY_TOGGLE_GUI = new VirtualKeybind(GLFW.GLFW_KEY_RIGHT_SHIFT); + + /** + * Set of custom keybinds that can be triggered. + */ + private static final HashSet customBinds = new HashSet<>(); + + static { + wrappedBindings.add(KEY_SPRINT); + wrappedBindings.add(KEY_SNEAK); + wrappedBindings.add(KEY_MOVE_FORWARD); + wrappedBindings.add(KEY_MOVE_BACK); + wrappedBindings.add(KEY_MOVE_LEFT); + wrappedBindings.add(KEY_MOVE_RIGHT); + wrappedBindings.add(KEY_USE_ITEM); + wrappedBindings.add(KEY_ATTACK); + wrappedBindings.add(KEY_PICK_ITEM); + wrappedBindings.add(KEY_JUMP); + wrappedBindings.add(KEY_DROP_ITEM); + wrappedBindings.add(KEY_TOGGLE_INVENTORY); + wrappedBindings.add(KEY_SLOT_1); + wrappedBindings.add(KEY_SLOT_2); + wrappedBindings.add(KEY_SLOT_3); + wrappedBindings.add(KEY_SLOT_4); + wrappedBindings.add(KEY_SLOT_5); + wrappedBindings.add(KEY_SLOT_6); + wrappedBindings.add(KEY_SLOT_7); + wrappedBindings.add(KEY_SLOT_8); + wrappedBindings.add(KEY_SLOT_9); + + ClientTickEvents.END_CLIENT_TICK.register(client -> { + for (VirtualKeybind bind : customBinds) { + bind.unset(); + } + }); + } + + /** + * The module that is blocking inputs. + */ + private static Object blockingModule = null; + + /** + * The input blocker to pass events through. + */ + private static @Nullable InputBlocker blockedInputs = null; + + /** + * Attempts to handle a key event inside the {@code CigaretteScreen} GUI. + *

{@code KeyboardMixin} checks this handler first.

+ * + * @param client The Minecraft client + * @param key The events key code + * @param scancode The events scan code + * @param action The events GLFW action + * @param modifiers The events modifier + * @return Whether the key event is handled and should be cancelled + */ + public static boolean handleKeyByGUI(MinecraftClient client, int key, int scancode, int action, int modifiers) { + if (!(client.currentScreen instanceof CigaretteScreen)) return false; + if (CigaretteScreen.bindingKey != null) { + switch (action) { + case GLFW.GLFW_PRESS -> CigaretteScreen.bindingKey.keyPressed(key, scancode, modifiers); + case GLFW.GLFW_RELEASE -> CigaretteScreen.bindingKey.keyReleased(key, scancode, modifiers); + } + } else { + if (action == GLFW.GLFW_PRESS && (key == GLFW.GLFW_KEY_ESCAPE || KEY_TOGGLE_GUI.isOf(key, scancode))) { + Cigarette.SCREEN.close(); + } + } + return true; + } + + /** + * Checks if the input should be cancelled because of modules blocking inputs. + *

{@code KeyboardMixin} checks this handler second, after {@code handleKeyByGUI()}.

+ * + * @param client The Minecraft client + * @param key The events key code + * @param scancode The events scan code + * @param action The events GLFW action + * @param modifiers The events modifier + * @return Whether the key event should be cancelled + */ + public static boolean handleBlockedKeyInputs(MinecraftClient client, int key, int scancode, int action, int modifiers) { + if (KEY_TOGGLE_GUI.isOf(key, scancode)) return false; + if (blockedInputs == null) return false; + return blockedInputs.processKey(key, scancode, action, modifiers); + } + + /** + * Checks if the input should be cancelled because of modules blocking inputs. + *

{@code MouseMixin} checks this handler first.

+ * + * @param client The Minecraft client + * @param button The buttons key code + * @param action The events GLFW action + * @param mods The events modifier + * @return Whether the mouse event is handled and should be cancelled + */ + public static boolean handleBlockedMouseInputs(MinecraftClient client, int button, int action, int mods) { + if (blockedInputs == null) return false; + return blockedInputs.processMouse(button, action, mods); + } + + /** + * Attempts to handle a key event outside the {@code CigaretteScreen} GUI and after input blocking. + *

{@code KeyboardMixin} checks this handler last, after {@code handleBlockedKeyInputs()}.

+ * + * @param client The Minecraft client + * @param key The events key code + * @param scancode The events scan code + * @param action The events GLFW action + * @param modifiers The events modifier + * @return Whether the key event is handled and should be cancelled + */ + public static boolean handleCustomKeys(MinecraftClient client, int key, int scancode, int action, int modifiers) { + for (MinecraftKeybind keybind : wrappedBindings) { + if (!keybind.isAttached() && !keybind.tryToAttach()) continue; + if (keybind.isMouse()) continue; + if (!keybind.isOf(key, scancode)) continue; + keybind.physicalAction(action); + } + if (action != GLFW.GLFW_PRESS) return false; + if (KEY_TOGGLE_GUI.isOf(key, scancode)) { + Cigarette.SCREEN.setParent(client.currentScreen); + client.setScreen(Cigarette.SCREEN); + return true; + } + for (VirtualKeybind binding : customBinds) { + if (binding.isMouse()) continue; + if (!binding.isOf(key, scancode)) continue; + binding.physicalAction(action); + return true; + } + return false; + } + + /** + * Attempts to handle a mouse event outside the {@code CigaretteScreen} GUI and after input blocking. + *

{@code MouseMixin} checks this handler last, after {@code handleBlocksMouseInputs()}

+ * + * @param client The Minecraft client + * @param button The buttons key code + * @param action The events GLFW action + * @param mods The events modifier + * @return Whether the mouse event is handled and should be cancelled + */ + public static boolean handleCustomMouse(MinecraftClient client, int button, int action, int mods) { + for (MinecraftKeybind keybind : wrappedBindings) { + if (!keybind.isAttached() && !keybind.tryToAttach()) continue; + if (!keybind.isMouse()) continue; + if (!keybind.isOfMouse(button)) continue; + keybind.physicalAction(action); + } + for (VirtualKeybind binding : customBinds) { + if (!binding.isMouse()) continue; + if (!binding.isOfMouse(button)) continue; + binding.physicalAction(action); + return true; + } + return false; + } + + /** + * Register a {@code MinecraftKeybind} wrapper so it can receive physical events. Wrappers from {@code KeybindHelper} are already registered. + *

Required to use {@code isPhysicallyPressed()} and {@code wasPhysicallyPressed()}.

+ * + * @param keybind The keybind wrapper to register + */ + public static void registerWrapper(MinecraftKeybind keybind) { + for (MinecraftKeybind existing : wrappedBindings) { + if (existing.equals(keybind)) return; + } + wrappedBindings.add(keybind); + } + + /** + * Register a {@code VirtualKeybind} so it can receive physical events. + * + * @param keybind The keybind to register + */ + public static void registerVirtualKey(VirtualKeybind keybind) { + customBinds.add(keybind); + } + + /** + * Attempts to start input blocking. {@return false if input blocking is already running} Use {@code forceBlockInputs()} to bypass this check. + * + * @param module + * @param blocker + */ + public static boolean tryBlockInputs(Object module, InputBlocker blocker) { + if (blockedInputs != null) return false; + forceBlockInputs(module, blocker); + return true; + } + + /** + * Force starts input blocking with the provided configuration. Overrides any previously started configurations. + * + * @param module + * @param blocker + */ + public static void forceBlockInputs(Object module, InputBlocker blocker) { + blockedInputs = blocker; + blockingModule = module; + } + + /** + * Disable input blocking. + */ + public static void unblock() { + blockedInputs = null; + blockingModule = null; + updateKeyStates(); + } + + /** + * {@return whether the provided module is blocking inputs} + * + * @param module The module to check + */ + public static boolean isBlocking(Object module) { + return blockingModule == module; + } + + /** + * {@return whether an input blocker is applied that blocks mouse movement or not} + */ + public static boolean isMouseBlocked() { + return blockedInputs != null && blockedInputs.blocksCamera(); + } + + /** + * Reset the state of all key and mouse bindings to the last physical state reported by GLFW. Wraps {@code KeyBinding.updatePressedStates()} as that method does not reset mouse buttons. + */ + public static void updateKeyStates() { + long window = MinecraftClient.getInstance().getWindow().getHandle(); + KeyBinding.updatePressedStates(); + + int[] buttons = {GLFW.GLFW_MOUSE_BUTTON_LEFT, GLFW.GLFW_MOUSE_BUTTON_MIDDLE, GLFW.GLFW_MOUSE_BUTTON_RIGHT}; + for (int j : buttons) { + InputUtil.Key button = InputUtil.Type.MOUSE.createFromCode(j); + KeyBinding.setKeyPressed(button, GLFW.glfwGetMouseButton(window, j) == GLFW.GLFW_PRESS); + } + } +} diff --git a/src/main/java/dev/cigarette/helper/TickHelper.java b/src/main/java/dev/cigarette/helper/TickHelper.java new file mode 100644 index 00000000..89a8a5a4 --- /dev/null +++ b/src/main/java/dev/cigarette/helper/TickHelper.java @@ -0,0 +1,63 @@ +package dev.cigarette.helper; + +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.minecraft.util.Pair; + +import java.util.HashMap; + +/** + * Helper class for scheduling events to occur in the future. + */ +public class TickHelper { + /** + * Actions that only occur once. + */ + private static final HashMap> onceActions = new HashMap<>(); + + /** + * Schedule an {@code action} to occur after {@code ticks} number of ticks. + * + * @param key A unique identifier to store this action. Usually {@code this} unless multiple actions are needed. + * @param action The action to perform once the number of ticks has passed. + * @param ticks The number of ticks to wait until triggered. + */ + public static void scheduleOnce(Object key, Runnable action, int ticks) { + onceActions.put(key, new Pair<>(ticks, action)); + } + + /** + * Unschedule an event that was already scheduled. + * + * @param key The unique identifier used in scheduling the event. + */ + public static void unschedule(Object key) { + onceActions.remove(key); + } + + /** + * Get the remaining ticks until a scheduled event triggers. + * + * @param key The unique identifier used in scheduling the event. + * @return Remaining ticks until event is triggered + */ + public static int whenOnce(Object key) { + if (!onceActions.containsKey(key)) return -1; + return onceActions.get(key).getLeft(); + } + + static { + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (onceActions.isEmpty()) return; + for (Object key : onceActions.keySet()) { + Pair action = onceActions.get(key); + int remainingTicks = action.getLeft() - 1; + if (remainingTicks <= 0) { + onceActions.remove(key); + action.getRight().run(); + continue; + } + action.setLeft(remainingTicks); + } + }); + } +} diff --git a/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java b/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java new file mode 100644 index 00000000..72063cc6 --- /dev/null +++ b/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java @@ -0,0 +1,102 @@ +package dev.cigarette.helper.keybind; + +/** + * A grouping of keybinds and event handlers for deciding when to block inputs or let events propagate. + */ +public class InputBlocker { + /** + * The keybindings blocked when this blocker is activated. + */ + private final MinecraftKeybind[] blockedBindings; + /** + * Whether this blocker also prevents changes in player perspective by the mouse. + */ + private boolean blockCamera = false; + /** + * Whether all keybindings attached have been linked with their native Minecraft {@code KeyBinding}. + */ + protected boolean complete = false; + + /** + * Creates a new input blocker. + * + * @param blockedBindings The keybinds to block + */ + public InputBlocker(MinecraftKeybind... blockedBindings) { + this.blockedBindings = blockedBindings; + } + + /** + * Prevents the user from changing their yaw and pitch. + * + * @return This object for method chaining + */ + public InputBlocker preventCameraChanges() { + this.blockCamera = true; + return this; + } + + /** + * {@return whether this input blocker prevents perspective changes from the mouse} + */ + public boolean blocksCamera() { + return this.blockCamera; + } + + /** + * Verifies that each keybind object is linked with their native Minecraft {@code KeyBinding}. + * + * @return whether all keybinds are linked or not + */ + private boolean complete() { + if (this.complete) return true; + int linked = 0; + for (MinecraftKeybind binding : this.blockedBindings) { + if (binding.isAttached() || binding.tryToAttach()) { + linked++; + } + } + this.complete = linked == this.blockedBindings.length; + return this.complete; + } + + /** + * Processes a non-mouse key event through this blocker. + * + * @param key The key of the event + * @param scancode The scancode of the event + * @param action The action performed on the key + * @param modifiers The modifiers of the event + * @return Whether the event should be considered handled and no longer propagate or not + */ + public boolean processKey(int key, int scancode, int action, int modifiers) { + if (!this.complete()) return false; + for (MinecraftKeybind binding : blockedBindings) { + if (binding.isMouse) continue; + if (!binding.isOf(key, scancode)) continue; + binding.physicalAction(action); + return true; + } + return false; + } + + /** + * Processes a mouse key event through this blocker. + * + * @param button The mouse button of the event + * @param action The action performed on the mouse button + * @param modifiers The modifiers of the event + * @return Whether the event should be considered handled and no longer propagate or not + */ + public boolean processMouse(int button, int action, int modifiers) { + if (!this.complete()) return false; + for (MinecraftKeybind binding : blockedBindings) { + if (!binding.isMouse) continue; + if (!binding.isOfMouse(button)) continue; + System.out.println("Processing Mouse | button=" + button + " action=" + action); + binding.physicalAction(action); + return true; + } + return false; + } +} diff --git a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java new file mode 100644 index 00000000..0a51c97b --- /dev/null +++ b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java @@ -0,0 +1,188 @@ +package dev.cigarette.helper.keybind; + +import dev.cigarette.helper.TickHelper; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; + +/** + * A wrapper class for more precise control over a Minecraft {@code KeyBinding}. + */ +public class MinecraftKeybind extends VirtualKeybind { + /** + * The reference to the actual {@code KeyBinding}. + */ + private @Nullable KeyBinding minecraftBinding; + /** + * The keybind ID to support creation of keybinds before they exist. + */ + private final @NotNull String minecraftKeybindId; + + /** + * Create a new {@code KeyBinding} wrapper. + * + * @param keybindId The keybinds translation key + */ + public MinecraftKeybind(@NotNull String keybindId) { + super(GLFW.GLFW_KEY_UNKNOWN); + this.minecraftBinding = KeyBinding.byId(keybindId); + this.minecraftKeybindId = keybindId; + } + + /** + * Create a new {@code KeyBinding} wrapper. + * + * @param keybindId The keybinds translation key + * @param isMouse Whether this is a mouse keybind or not + */ + public MinecraftKeybind(@NotNull String keybindId, boolean isMouse) { + super(GLFW.GLFW_KEY_UNKNOWN); + this.minecraftBinding = KeyBinding.byId(keybindId); + this.minecraftKeybindId = keybindId; + this.isMouse = isMouse; + } + + @Override + public void setDefaultKey(int defaultKey) { + } + + @Override + public void setKey(int key) { + if (this.minecraftBinding == null) return; + if (this.isMouse) { + this.minecraftBinding.setBoundKey(InputUtil.Type.MOUSE.createFromCode(key)); + } else { + this.minecraftBinding.setBoundKey(InputUtil.Type.KEYSYM.createFromCode(key)); + } + } + + /** + * Attempts to link this object with the native {@code KeyBinding} from Minecraft. + * + * @return whether this object was linked successfully or not + */ + public boolean tryToAttach() { + this.minecraftBinding = KeyBinding.byId(this.minecraftKeybindId); + return this.isAttached(); + } + + /** + * Checks if this object is linked with the native {@code KeyBinding} from Minecraft. + * + * @return whether this object is linked or not + */ + public boolean isAttached() { + return this.minecraftBinding != null; + } + + /** + * {@return whether this key is being virtually held down or not} This can result from the key being physically held down or set to be held by other code. + */ + public boolean isVirtuallyPressed() { + if (this.minecraftBinding == null) return false; + return this.minecraftBinding.isPressed(); + } + + /** + * {@return whether this key was virtually pressed or not} This can result from the key being physically pressed or set to have been pressed by other code. + */ + public boolean wasVirtuallyPressed() { + if (this.minecraftBinding == null) return false; + return this.minecraftBinding.wasPressed(); + } + + /** + * Virtually presses the key triggering any Minecraft related events. For player movement, you must use {@code holdForTicks(1)} to simulate a key press because the client only uses {@code isPressed()} to process movement. + */ + public void press() { + if (this.minecraftBinding == null) return; + InputUtil.Key key = InputUtil.fromTranslationKey(this.minecraftBinding.getBoundKeyTranslationKey()); + KeyBinding.onKeyPressed(key); + } + + /** + * Virtually holds the key triggering any Minecraft related events. + */ + public void hold() { + if (this.minecraftBinding == null) return; + InputUtil.Key key = InputUtil.fromTranslationKey(this.minecraftBinding.getBoundKeyTranslationKey()); + KeyBinding.setKeyPressed(key, true); + } + + /** + * Virtually sets the holding state of the key. + * + * @param state The state to set + */ + public void hold(boolean state) { + if (this.minecraftBinding == null) return; + if (state) this.hold(); + else this.release(); + } + + /** + * Virtually hold this key for a specific number of ticks. + * + * @param numOfTicks The number of ticks to hold the key for + */ + public void holdForTicks(int numOfTicks) { + if (this.minecraftBinding == null) return; + this.hold(); + TickHelper.scheduleOnce(this, this::release, numOfTicks); + } + + /** + * Virtually releases the key. + */ + public void release() { + if (this.minecraftBinding == null) return; + InputUtil.Key key = InputUtil.fromTranslationKey(this.minecraftBinding.getBoundKeyTranslationKey()); + KeyBinding.setKeyPressed(key, false); + TickHelper.unschedule(this); + } + + /** + * Virtually releases this key for a specific number of ticks. + * + * @param numOfTicks The number of ticks to release the key for + */ + public void releaseForTicks(int numOfTicks) { + if (this.minecraftBinding == null) return; + this.release(); + TickHelper.scheduleOnce(this, this::hold, numOfTicks); + } + + /** + * The remaining ticks on the {@code holdForTicks()} or {@code releaseForTicks()}. + * + * @return The remaining ticks + */ + public int ticksLeft() { + return TickHelper.whenOnce(this); + } + + /** + * {@return whether the translation key name of both objects are identical} + * + * @param o the reference object with which to compare. + */ + public boolean equals(Object o) { + if (!(o instanceof MinecraftKeybind other) || this.minecraftBinding == null) return false; + if (other.minecraftBinding == null) return false; + return this.minecraftBinding.getTranslationKey().equals(other.minecraftBinding.getTranslationKey()); + } + + @Override + public boolean isOf(int key, int scancode) { + if (this.minecraftBinding == null) return false; + return this.minecraftBinding.matchesKey(key, scancode); + } + + @Override + public boolean isOfMouse(int button) { + if (this.minecraftBinding == null) return false; + return this.minecraftBinding.matchesMouse(button); + } +} diff --git a/src/main/java/dev/cigarette/helper/keybind/VirtualKeybind.java b/src/main/java/dev/cigarette/helper/keybind/VirtualKeybind.java new file mode 100644 index 00000000..17d75f13 --- /dev/null +++ b/src/main/java/dev/cigarette/helper/keybind/VirtualKeybind.java @@ -0,0 +1,133 @@ +package dev.cigarette.helper.keybind; + +import org.lwjgl.glfw.GLFW; + +/** + * A fake {@code KeyBinding} which handles the physical state of a key. + */ +public class VirtualKeybind { + /** + * The key currently bounded to. + */ + protected int currentKey; + /** + * The original key that was bounded to. + */ + protected int defaultKey; + /** + * Whether the bounded key is physically pressed. + */ + protected boolean pressed = false; + /** + * Times the bounded key was pressed. + */ + protected int timesPressed = 0; + /** + * Whether this is a mouse keybind or not. + */ + protected boolean isMouse = false; + + /** + * Creates a virtual keybind. Does not create any {@code KeyBinding}'s or register to any Minecraft events. + * + * @param defaultKey The keycode of the key to monitor + */ + public VirtualKeybind(int defaultKey) { + this.currentKey = defaultKey; + this.defaultKey = defaultKey; + } + + /** + * Creates a virtual keybind. Does not create any {@code KeyBinding}'s or register to any Minecraft events. + * + * @param defaultKey The keycode of the key to monitor + * @param isMouse Whether this is a mouse keybind or not + */ + public VirtualKeybind(int defaultKey, boolean isMouse) { + this.currentKey = defaultKey; + this.defaultKey = defaultKey; + this.isMouse = isMouse; + } + + /** + * Set the default key for this keybinding. Does not update the currently bounded key. + * + * @param defaultKey The key to set as default + */ + public void setDefaultKey(int defaultKey) { + this.defaultKey = defaultKey; + } + + /** + * Sets the current key for this keybinding. + * + * @param key The key to set as currently bounded + */ + public void setKey(int key) { + this.currentKey = key; + } + + /** + * {@return whether this a mouse keybind or not} + */ + public boolean isMouse() { + return this.isMouse; + } + + /** + * Processes a physical action on the key. + * + * @param glfwAction The GLFW action performed on this keybind + */ + public void physicalAction(int glfwAction) { + switch (glfwAction) { + case GLFW.GLFW_PRESS -> { + this.timesPressed++; + this.pressed = true; + } + case GLFW.GLFW_RELEASE -> this.pressed = false; + } + } + + /** + * {@return whether this key is being physically held down or not} + */ + public boolean isPhysicallyPressed() { + return this.pressed; + } + + /** + * {@return whether this key was physically pressed or not} + */ + public boolean wasPhysicallyPressed() { + if (this.timesPressed == 0) return false; + this.timesPressed--; + return true; + } + + /** + * {@return if the key is bounded to this keybind} + * + * @param key The key to check if bounded + * @param scancode The scancode for misc {@code GLFW_KEY_UNKNOWN} keys + */ + public boolean isOf(int key, int scancode) { + return !this.isMouse && this.currentKey == key; + } + + /** + * {@return if mouse button is bounded to this keybind} + * + * @param button The mouse button to check if bounded + */ + public boolean isOfMouse(int button) { + return this.isMouse && this.currentKey == button; + } + + /** + * Resets the {@link #timesPressed} property. Should be triggered at the end of every client tick to prevent queueing of events. + */ + public void unset() { + this.timesPressed = 0; + } +} diff --git a/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java b/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java deleted file mode 100644 index 726af328..00000000 --- a/src/main/java/dev/cigarette/mixin/KeyBindingAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package dev.cigarette.mixin; - -import net.minecraft.client.option.KeyBinding; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(KeyBinding.class) -public interface KeyBindingAccessor { - @Accessor("timesPressed") - public void setTimesPressed(int timesPressed); - - @Accessor - int getTimesPressed(); -} diff --git a/src/main/java/dev/cigarette/mixin/KeyboardInputMixin.java b/src/main/java/dev/cigarette/mixin/KeyboardInputMixin.java deleted file mode 100644 index 21894dd8..00000000 --- a/src/main/java/dev/cigarette/mixin/KeyboardInputMixin.java +++ /dev/null @@ -1,39 +0,0 @@ -package dev.cigarette.mixin; - -import dev.cigarette.lib.InputOverride; -import net.minecraft.client.input.Input; -import net.minecraft.client.input.KeyboardInput; -import net.minecraft.util.PlayerInput; -import net.minecraft.util.math.Vec2f; -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.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(KeyboardInput.class) -public abstract class KeyboardInputMixin extends Input { - @Shadow - private static float getMovementMultiplier(boolean positive, boolean negative) { - // dummy body - return 0; - } - - @Inject(method = "tick()V", at = @At("HEAD"), cancellable = true) - private void tick(CallbackInfo ci) { - if (!InputOverride.isActive) return; - ci.cancel(); - this.playerInput = new PlayerInput( - InputOverride.forwardKey, - InputOverride.backKey, - InputOverride.leftKey, - InputOverride.rightKey, - InputOverride.jumpKey, - InputOverride.sneakKey, - InputOverride.sprintKey - ); - float f = getMovementMultiplier(this.playerInput.forward(), this.playerInput.backward()); - float g = getMovementMultiplier(this.playerInput.left(), this.playerInput.right()); - this.movementVector = new Vec2f(g, f).normalize(); - } -} diff --git a/src/main/java/dev/cigarette/mixin/KeyboardMixin.java b/src/main/java/dev/cigarette/mixin/KeyboardMixin.java new file mode 100644 index 00000000..98aba9b1 --- /dev/null +++ b/src/main/java/dev/cigarette/mixin/KeyboardMixin.java @@ -0,0 +1,25 @@ +package dev.cigarette.mixin; + +import dev.cigarette.helper.KeybindHelper; +import net.minecraft.client.Keyboard; +import net.minecraft.client.MinecraftClient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Keyboard.class) +public class KeyboardMixin { + @Inject(method = "onKey", at = @At("HEAD"), cancellable = true) + private void onKey(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { + MinecraftClient client = MinecraftClient.getInstance(); + if (window != client.getWindow().getHandle()) { + ci.cancel(); + return; + } + if (client.currentScreen == null && (KeybindHelper.handleKeyByGUI(client, key, scancode, action, modifiers) || KeybindHelper.handleBlockedKeyInputs(client, key, scancode, action, modifiers) || KeybindHelper.handleCustomKeys(client, key, scancode, action, modifiers))) { + ci.cancel(); + return; + } + } +} diff --git a/src/main/java/dev/cigarette/mixin/MouseMixin.java b/src/main/java/dev/cigarette/mixin/MouseMixin.java index 8c7034de..52409a3e 100644 --- a/src/main/java/dev/cigarette/mixin/MouseMixin.java +++ b/src/main/java/dev/cigarette/mixin/MouseMixin.java @@ -1,9 +1,8 @@ package dev.cigarette.mixin; -import dev.cigarette.lib.InputOverride; +import dev.cigarette.helper.KeybindHelper; import net.minecraft.client.MinecraftClient; import net.minecraft.client.Mouse; -import net.minecraft.entity.Entity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -13,11 +12,20 @@ public class MouseMixin { @Inject(method = "updateMouse", at = @At("HEAD"), cancellable = true) private void updateMouse(double timeDelta, CallbackInfo ci) { - if (!InputOverride.isActive) return; - Entity player = MinecraftClient.getInstance().player; - if (player != null) { - player.setAngles(InputOverride.yaw, InputOverride.pitch); - } + if (!KeybindHelper.isMouseBlocked()) return; ci.cancel(); } + + @Inject(method = "onMouseButton", at = @At("HEAD"), cancellable = true) + private void onMouseButton(long window, int button, int action, int mods, CallbackInfo ci) { + MinecraftClient client = MinecraftClient.getInstance(); + if (window != client.getWindow().getHandle()) { + ci.cancel(); + return; + } + if (client.currentScreen == null && (KeybindHelper.handleBlockedMouseInputs(client, button, action, mods) || KeybindHelper.handleCustomMouse(client, button, action, mods))) { + ci.cancel(); + return; + } + } } diff --git a/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java b/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java index ee538958..d1f25523 100644 --- a/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java +++ b/src/main/java/dev/cigarette/module/bedwars/AutoBlockIn.java @@ -5,13 +5,12 @@ import dev.cigarette.gui.widget.KeybindWidget; import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.ToggleWidget; +import dev.cigarette.helper.KeybindHelper; import dev.cigarette.lib.PlayerEntityL; -import dev.cigarette.mixin.KeyBindingAccessor; import dev.cigarette.module.TickModule; import dev.cigarette.precomputed.BlockIn; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; @@ -31,7 +30,6 @@ public class AutoBlockIn extends TickModule { private final ToggleWidget switchToTool = new ToggleWidget("Switch to Tools", "Automatically switches to a tool once finished.").withDefaultState(true); private final SliderWidget variation = new SliderWidget("Variation", "Applies randomness to the delay between block places.").withBounds(0, 1, 4); - private KeyBinding rightClickKey = null; private boolean running = false; private BlockPos originalPos = null; private float originalYaw = 0; @@ -119,19 +117,10 @@ private void disableAndSwitch(@NotNull ClientPlayerEntity player) { return null; } - private void rightClick() { - KeyBindingAccessor useAccessor = (KeyBindingAccessor) rightClickKey; - useAccessor.setTimesPressed(useAccessor.getTimesPressed() + 1); - } - @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { - if (rightClickKey == null) { - rightClickKey = KeyBinding.byId("key.use"); - return; - } if (!running) { - if (!keybind.getKeybind().isPressed()) return; + if (!keybind.getKeybind().wasPhysicallyPressed()) return; BlockPos pos = player.getBlockPos(); for (BedwarsAgent.PersistentBed bed : BedwarsAgent.getVisibleBeds()) { if (bed.head().isWithinDistance(pos, proximityToBeds.getRawState()) || bed.foot().isWithinDistance(pos, proximityToBeds.getRawState())) { @@ -156,7 +145,7 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, previousVector = nextLookVector; PlayerEntityL.setRotationVector(player, nextLookVector); - rightClick(); + KeybindHelper.KEY_USE_ITEM.press(); int rand = variation.getRawState().intValue() > 0 ? (int) (Math.random() * variation.getRawState().intValue()) : 0; cooldownTicks = 16 - speed.getRawState().intValue() + rand; diff --git a/src/main/java/dev/cigarette/module/bedwars/Bridger.java b/src/main/java/dev/cigarette/module/bedwars/Bridger.java index 6d3415b8..41e772b1 100644 --- a/src/main/java/dev/cigarette/module/bedwars/Bridger.java +++ b/src/main/java/dev/cigarette/module/bedwars/Bridger.java @@ -5,12 +5,10 @@ import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.TextWidget; import dev.cigarette.gui.widget.ToggleWidget; -import dev.cigarette.lib.InputOverride; -import dev.cigarette.mixin.KeyBindingAccessor; +import dev.cigarette.helper.keybind.InputBlocker; import dev.cigarette.module.TickModule; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; @@ -18,8 +16,12 @@ import net.minecraft.util.math.Direction; import org.jetbrains.annotations.NotNull; +import static dev.cigarette.helper.KeybindHelper.*; + public class Bridger extends TickModule { public static final Bridger INSTANCE = new Bridger("bedwars.bridger", "Bridger", "Automatically bridges."); + public static final InputBlocker INPUT_BLOCKER = new InputBlocker(KEY_JUMP, KEY_MOVE_BACK, KEY_MOVE_LEFT, KEY_MOVE_RIGHT, + KEY_USE_ITEM, KEY_SNEAK).preventCameraChanges(); private final SliderWidget speed = new SliderWidget("Speed", "The higher the speed, the less time spent shifting. To look more legit or improve consistency, lower the speed. Setting to 3 will naturally god bridge straight & diagonally inconsistently.").withBounds(0, 2, 3); private final ToggleWidget blockSwap = new ToggleWidget("Auto Swap Blocks", "Automatically swap to the next available slot with placeable blocks when current stack runs out.").withDefaultState(true); @@ -27,16 +29,8 @@ public class Bridger extends TickModule { private final ToggleWidget toggleDiagonal = new ToggleWidget("Diagonal", "Toggles automatic diagonal bridging.").withDefaultState(true); private final ToggleWidget toggleDiagonalGod = new ToggleWidget("God Bridging", "Toggles diagonal god bridging when positioning yourself half a block from the corner.").withDefaultState(false); - private KeyBinding sneakKey = null; - private KeyBinding backwardsKey = null; - private KeyBinding leftKey = null; - private KeyBinding rightKey = null; - private KeyBinding rightClickKey = null; - private KeyBinding jumpKey = null; private int runningTicks = 0; - private int shiftDiabledTicks = 0; - - protected BridgeType bridgeType = BridgeType.NONE; + private BridgeType bridgingMode = BridgeType.NONE; private Bridger(String id, String name, String tooltip) { super(ToggleWidget::module, id, name, tooltip); @@ -49,14 +43,8 @@ private Bridger(String id, String name, String tooltip) { toggleDiagonalGod.registerConfigKey(id + ".diagonal.god"); } - private boolean isStraightBridge() { - if (!leftKey.isPressed() && !rightKey.isPressed()) return false; - if (leftKey.isPressed() && rightKey.isPressed()) return false; - return true; - } - private float straightBridgeYaw(Direction placingFace) { - boolean right = rightKey.isPressed(); + boolean right = KEY_MOVE_RIGHT.isPhysicallyPressed(); switch (placingFace) { case NORTH -> { return right ? 45f : -45f; @@ -76,10 +64,6 @@ private float straightBridgeYaw(Direction placingFace) { } } - private boolean isDiagonalBridge() { - return !leftKey.isPressed() && !rightKey.isPressed(); - } - private float getBoundedYaw(ClientPlayerEntity player) { float yaw = player.getYaw(); if (yaw < -180) { @@ -105,10 +89,6 @@ private float diagonalBridgeYaw(ClientPlayerEntity player) { return player.getYaw(); } - private boolean isDiagonalGodBridge(BlockPos playerPos, BlockPos blockPos) { - return playerPos.getX() == blockPos.getX() || playerPos.getZ() == blockPos.getZ(); - } - private void cycleIfNoBlocks(ClientPlayerEntity player) { if (!blockSwap.getRawState()) return; if (!BedwarsAgent.isBlock(player.getMainHandStack())) { @@ -119,46 +99,27 @@ private void cycleIfNoBlocks(ClientPlayerEntity player) { } } - private void rightClick(int times) { - KeyBindingAccessor useAccessor = (KeyBindingAccessor) rightClickKey; - useAccessor.setTimesPressed(useAccessor.getTimesPressed() + times); - } - - private void enable(BridgeType type, float yaw) { - this.bridgeType = type; - InputOverride.isActive = true; - InputOverride.pitch = 77.0f; - InputOverride.yaw = yaw; - InputOverride.sneakKey = true; - InputOverride.backKey = true; - InputOverride.leftKey = leftKey.isPressed(); - InputOverride.rightKey = rightKey.isPressed(); - InputOverride.jumpKey = false; + private void enable(@NotNull ClientPlayerEntity player, BridgeType mode, float yaw) { + this.bridgingMode = mode; + tryBlockInputs(this, INPUT_BLOCKER); + player.setPitch(77.0f); + player.setYaw(yaw); + KEY_SNEAK.hold(); } private void disable() { - this.bridgeType = BridgeType.NONE; - InputOverride.isActive = false; + unblock(); } @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { - if (sneakKey == null || backwardsKey == null || leftKey == null || rightKey == null || rightClickKey == null || jumpKey == null) { - this.sneakKey = KeyBinding.byId("key.sneak"); - this.backwardsKey = KeyBinding.byId("key.back"); - this.leftKey = KeyBinding.byId("key.left"); - this.rightKey = KeyBinding.byId("key.right"); - this.rightClickKey = KeyBinding.byId("key.use"); - this.jumpKey = KeyBinding.byId("key.jump"); - return; - } - if (bridgeType == BridgeType.NONE) { - if (!sneakKey.isPressed() || !backwardsKey.isPressed() || !rightClickKey.isPressed()) return; + if (this.bridgingMode == BridgeType.NONE) { + if (!KEY_SNEAK.isPhysicallyPressed() || !KEY_MOVE_BACK.isPhysicallyPressed() || !KEY_USE_ITEM.isPhysicallyPressed()) return; HitResult hitResult = client.crosshairTarget; if (hitResult == null || hitResult.getType() != HitResult.Type.BLOCK) return; - BlockHitResult blockHitResult = (BlockHitResult) hitResult; + BlockHitResult blockHitResult = (BlockHitResult) hitResult; Direction placingFace = blockHitResult.getSide(); if (placingFace == Direction.UP || placingFace == Direction.DOWN) return; @@ -167,16 +128,18 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (playerPos.getY() - blockPos.getY() != 1) return; if (Math.abs(playerPos.getX() - blockPos.getX()) > 1 || Math.abs(playerPos.getZ() - blockPos.getZ()) > 1) return; - if (isStraightBridge()) { - if (!toggleStraight.getRawState()) return; - enable(BridgeType.STRAIGHT, straightBridgeYaw(placingFace)); - } else if (isDiagonalBridge()) { - if (!toggleDiagonal.getRawState()) return; - boolean godBridge = toggleDiagonalGod.getRawState() && isDiagonalGodBridge(playerPos, blockPos); - enable(godBridge ? BridgeType.DIAGONAL_GOD : BridgeType.DIAGONAL, diagonalBridgeYaw(player)); + BridgeType mode = BridgeType.get(blockPos, playerPos, toggleStraight.getRawState(), toggleDiagonal.getRawState(), toggleDiagonalGod.getRawState()); + float yaw = 0; + switch (mode) { + case STRAIGHT -> yaw = straightBridgeYaw(placingFace); + case DIAGONAL, DIAGONAL_GOD -> yaw = diagonalBridgeYaw(player); + } + if (mode != BridgeType.NONE) { + enable(player, mode, yaw); } } else { - if (!sneakKey.isPressed() || !backwardsKey.isPressed() || !rightClickKey.isPressed()) { + if (!KEY_SNEAK.isPhysicallyPressed() || !KEY_MOVE_BACK.isPhysicallyPressed() || !KEY_USE_ITEM.isPhysicallyPressed()) { + bridgingMode = BridgeType.NONE; disable(); return; } @@ -186,57 +149,41 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, BlockHitResult blockHitResult = (BlockHitResult) hitResult; BlockPos blockPos = blockHitResult.getBlockPos(); - switch (bridgeType) { + boolean facingTop = blockHitResult.getSide() == Direction.UP; + boolean facingSide = !facingTop && blockPos.getY() < player.getY(); + + switch (bridgingMode) { case STRAIGHT -> { - if (!rightKey.isPressed() && !leftKey.isPressed()) { + if (!KEY_MOVE_RIGHT.isPhysicallyPressed() && !KEY_MOVE_LEFT.isPhysicallyPressed()) { disable(); return; } - InputOverride.jumpKey = jumpKey.isPressed(); - InputOverride.sneakKey = shiftDiabledTicks-- <= 0; - - cycleIfNoBlocks(player); - - if (blockHitResult.getSide() == Direction.UP) { - rightClick(1); - } else { - if (blockPos.getY() >= player.getY()) { - disable(); - return; - } - rightClick(1); - shiftDiabledTicks = 2 + speed.getRawState().intValue(); + KEY_JUMP.hold(KEY_JUMP.isPhysicallyPressed()); + KEY_USE_ITEM.press(); + if (facingSide) { + KEY_SNEAK.releaseForTicks(2 + speed.getRawState().intValue()); } } case DIAGONAL -> { - InputOverride.jumpKey = shiftDiabledTicks == 1 && jumpKey.isPressed(); - InputOverride.sneakKey = shiftDiabledTicks-- <= 0; - - if (blockHitResult.getSide() == Direction.UP) { - rightClick(1); - } else { - rightClick(2); - shiftDiabledTicks = 3 + speed.getRawState().intValue(); + KEY_JUMP.hold(KEY_JUMP.isPhysicallyPressed() && KEY_SNEAK.ticksLeft() == 1); + KEY_USE_ITEM.press(); + if (facingSide) { + KEY_USE_ITEM.press(); + KEY_SNEAK.releaseForTicks(3 + speed.getRawState().intValue()); } } case DIAGONAL_GOD -> { - InputOverride.jumpKey = shiftDiabledTicks == 1 && jumpKey.isPressed(); - InputOverride.sneakKey = runningTicks++ == 2; - - if (blockHitResult.getSide() == Direction.UP) { - rightClick(1); - } else { - rightClick(2); + KEY_JUMP.hold(KEY_JUMP.isPhysicallyPressed() && KEY_SNEAK.ticksLeft() == 1); + KEY_SNEAK.hold(runningTicks++ == 2); + KEY_USE_ITEM.press(); + if (facingSide) { + KEY_USE_ITEM.press(); if (runningTicks >= 80) { runningTicks = 0; } } } - case NONE -> { - InputOverride.jumpKey = jumpKey.isPressed(); - InputOverride.sneakKey = sneakKey.isPressed(); - } } cycleIfNoBlocks(player); @@ -251,16 +198,23 @@ public boolean inValidGame() { protected enum BridgeType { NONE(0), STRAIGHT(1), DIAGONAL(2), DIAGONAL_GOD(3); + protected static BridgeType get(BlockPos blockPos, BlockPos playerPos, boolean straight, boolean diagonal, boolean diagonalGod) { + boolean left = KEY_MOVE_LEFT.isPhysicallyPressed(); + boolean right = KEY_MOVE_RIGHT.isPhysicallyPressed(); + if (diagonal && !left && !right) { + if (diagonalGod && (playerPos.getX() == blockPos.getX() || playerPos.getZ() == blockPos.getZ())) { + return BridgeType.DIAGONAL_GOD; + } + return BridgeType.DIAGONAL; + } + if (straight && left != right) return BridgeType.STRAIGHT; + return BridgeType.NONE; + } + private final int id; BridgeType(int id) { this.id = id; } - - /* - public int getId() { - return this.id; - } - */ } -} \ No newline at end of file +} diff --git a/src/main/java/dev/cigarette/module/bedwars/DefenseViewer.java b/src/main/java/dev/cigarette/module/bedwars/DefenseViewer.java index 910354d6..ed299615 100644 --- a/src/main/java/dev/cigarette/module/bedwars/DefenseViewer.java +++ b/src/main/java/dev/cigarette/module/bedwars/DefenseViewer.java @@ -136,10 +136,10 @@ protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, if (color != 0) defensiveBlocks.put(pos, color); } } - while (increaseKey.getKeybind().wasPressed()) { + while (increaseKey.getKeybind().wasPhysicallyPressed()) { this.layer = Math.min(this.layer + 1, PyramidQuadrant.MAX_LAYER); } - while (decreaseKey.getKeybind().wasPressed()) { + while (decreaseKey.getKeybind().wasPhysicallyPressed()) { this.layer = Math.max(this.layer - 1, 0); } } diff --git a/src/main/java/dev/cigarette/module/combat/AutoClicker.java b/src/main/java/dev/cigarette/module/combat/AutoClicker.java index 87f27547..260c508b 100644 --- a/src/main/java/dev/cigarette/module/combat/AutoClicker.java +++ b/src/main/java/dev/cigarette/module/combat/AutoClicker.java @@ -2,11 +2,10 @@ import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.ToggleWidget; -import dev.cigarette.mixin.KeyBindingAccessor; +import dev.cigarette.helper.KeybindHelper; import dev.cigarette.module.TickModule; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; import net.minecraft.util.hit.HitResult; import org.jetbrains.annotations.NotNull; @@ -26,12 +25,10 @@ private AutoClicker(String id, String name, String tooltip) { protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { client.attackCooldown = 0; HitResult hitResult = client.crosshairTarget; - KeyBinding attackKey = KeyBinding.byId("key.attack"); - KeyBindingAccessor attackKeyAccessor = (KeyBindingAccessor) attackKey; - if (hitResult == null || attackKey == null || !attackKey.isPressed()) return; + if (hitResult == null || !KeybindHelper.KEY_ATTACK.isPhysicallyPressed()) return; if (Math.random() > clickPercent.getRawState()) return; if (hitResult.getType() != HitResult.Type.BLOCK) { - attackKeyAccessor.setTimesPressed(attackKeyAccessor.getTimesPressed() + 1); + KeybindHelper.KEY_ATTACK.press(); } } diff --git a/src/main/java/dev/cigarette/module/combat/PerfectHit.java b/src/main/java/dev/cigarette/module/combat/PerfectHit.java index fa04bbfc..a14a06d1 100644 --- a/src/main/java/dev/cigarette/module/combat/PerfectHit.java +++ b/src/main/java/dev/cigarette/module/combat/PerfectHit.java @@ -2,11 +2,10 @@ import dev.cigarette.gui.widget.SliderWidget; import dev.cigarette.gui.widget.ToggleWidget; -import dev.cigarette.mixin.KeyBindingAccessor; +import dev.cigarette.helper.KeybindHelper; import dev.cigarette.module.TickModule; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.option.KeyBinding; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; @@ -30,16 +29,14 @@ private PerfectHit(String id, String name, String tooltip) { protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { client.attackCooldown = 0; HitResult hitResult = client.crosshairTarget; - KeyBinding attackKey = KeyBinding.byId("key.attack"); - KeyBindingAccessor attackKeyAccessor = (KeyBindingAccessor) attackKey; - if (hitResult == null || attackKey == null || !attackKey.isPressed()) return; + if (hitResult == null || !KeybindHelper.KEY_ATTACK.isPhysicallyPressed()) return; if (hitResult.getType() == HitResult.Type.ENTITY) { EntityHitResult entityHitResult = (EntityHitResult) hitResult; Entity entity = entityHitResult.getEntity(); if (!(entity instanceof LivingEntity livingEntity)) return; if (livingEntity.hurtTime > 1) return; if (Math.random() > clickPercent.getRawState()) return; - attackKeyAccessor.setTimesPressed(attackKeyAccessor.getTimesPressed() + 1); + KeybindHelper.KEY_ATTACK.press(); } } diff --git a/src/main/java/dev/cigarette/module/keybind/AddGlassBlock.java b/src/main/java/dev/cigarette/module/keybind/AddGlassBlock.java index 69be35eb..a06f2722 100644 --- a/src/main/java/dev/cigarette/module/keybind/AddGlassBlock.java +++ b/src/main/java/dev/cigarette/module/keybind/AddGlassBlock.java @@ -25,7 +25,7 @@ private AddGlassBlock(String id, String name, String tooltip) { @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { - while (keybind.getKeybind().wasPressed()) { + while (keybind.getKeybind().wasPhysicallyPressed()) { HitResult target = client.crosshairTarget; if (target == null) break; if (target.getType() != HitResult.Type.BLOCK) break; diff --git a/src/main/java/dev/cigarette/module/keybind/BreakBlock.java b/src/main/java/dev/cigarette/module/keybind/BreakBlock.java index 94d4ec64..82b12146 100644 --- a/src/main/java/dev/cigarette/module/keybind/BreakBlock.java +++ b/src/main/java/dev/cigarette/module/keybind/BreakBlock.java @@ -23,7 +23,7 @@ private BreakBlock(String id, String name, String tooltip) { @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { - while (keybind.getKeybind().wasPressed()) { + while (keybind.getKeybind().wasPhysicallyPressed()) { HitResult target = client.crosshairTarget; if (target == null) break; if (target.getType() != HitResult.Type.BLOCK) break; diff --git a/src/main/java/dev/cigarette/module/keybind/VClip.java b/src/main/java/dev/cigarette/module/keybind/VClip.java index b409236d..e272542f 100644 --- a/src/main/java/dev/cigarette/module/keybind/VClip.java +++ b/src/main/java/dev/cigarette/module/keybind/VClip.java @@ -23,7 +23,7 @@ private VClip(String id, String name, String tooltip) { @Override protected void onEnabledTick(MinecraftClient client, @NotNull ClientWorld world, @NotNull ClientPlayerEntity player) { - while (keybind.getKeybind().wasPressed()) { + while (keybind.getKeybind().wasPhysicallyPressed()) { Vec3d pos = player.getPos(); BlockPos blockPos = player.getBlockPos(); for (int offset = 3; offset < 6; offset++) { diff --git a/src/main/resources/cigarette.mixins.json b/src/main/resources/cigarette.mixins.json index 713f4a1b..a01f8ee0 100644 --- a/src/main/resources/cigarette.mixins.json +++ b/src/main/resources/cigarette.mixins.json @@ -6,18 +6,16 @@ "BedBlockEntityMixin", "ClientWorldAccessor", "EntityMixin", - "KeyBindingAccessor", "MinecraftClientInvoker", "PlayerEntityMixin", "PropertyMixin", - "ScoreboardMixin", - "ClientWorldAccessor" + "ScoreboardMixin" ], "injectors": { "defaultRequire": 1 }, "client": [ - "KeyboardInputMixin", + "KeyboardMixin", "MouseMixin" ] } \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 4e915f3b..fb2073a2 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -18,8 +18,7 @@ "dev.cigarette.Cigarette" ], "client": [ - "dev.cigarette.Language", - "dev.cigarette.gui.KeyBinding" + "dev.cigarette.Language" ] }, "mixins": [