From 08189b93a69e772ba574593f26048821e094c51f Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Fri, 3 Oct 2025 11:17:19 -0400
Subject: [PATCH 01/37] chore: move CigaretteScreen instance into Cigarette pkg
---
src/main/java/dev/cigarette/Cigarette.java | 2 ++
src/main/java/dev/cigarette/gui/CigaretteScreen.java | 2 +-
src/main/java/dev/cigarette/gui/KeyBinding.java | 6 ++----
3 files changed, 5 insertions(+), 5 deletions(-)
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 afaf6625..cd65e7e6 100644
--- a/src/main/java/dev/cigarette/gui/CigaretteScreen.java
+++ b/src/main/java/dev/cigarette/gui/CigaretteScreen.java
@@ -38,7 +38,7 @@ public class CigaretteScreen extends Screen {
private int categoryCount = 0;
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
index 3745c97d..d18cae95 100644
--- a/src/main/java/dev/cigarette/gui/KeyBinding.java
+++ b/src/main/java/dev/cigarette/gui/KeyBinding.java
@@ -14,8 +14,6 @@ public class KeyBinding implements ClientModInitializer {
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"));
@@ -25,8 +23,8 @@ public void onInitializeClient() {
if (client.currentScreen instanceof CigaretteScreen) {
client.currentScreen.close();
} else {
- screen.setParent(client.currentScreen);
- client.setScreen(screen);
+ Cigarette.SCREEN.setParent(client.currentScreen);
+ client.setScreen(Cigarette.SCREEN);
}
}
});
From 4ebbd397a559fac5385b49dfba501653a76f0f1d Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Fri, 3 Oct 2025 12:49:40 -0400
Subject: [PATCH 02/37] create keybinding helper
---
.../dev/cigarette/helper/KeybindHelper.java | 132 ++++++++++++++++++
1 file changed, 132 insertions(+)
create mode 100644 src/main/java/dev/cigarette/helper/KeybindHelper.java
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..8f99fa25
--- /dev/null
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -0,0 +1,132 @@
+package dev.cigarette.helper;
+
+import dev.cigarette.Cigarette;
+import dev.cigarette.gui.CigaretteScreen;
+import net.minecraft.client.MinecraftClient;
+import org.lwjgl.glfw.GLFW;
+
+public class KeybindHelper {
+ /**
+ * Keybind to toggle {@code CigaretteScreen}.
+ */
+ public static CigaretteKeyBind TOGGLE_GUI = new CigaretteKeyBind(GLFW.GLFW_KEY_RIGHT_SHIFT);
+
+ /**
+ * 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 handleByGUI(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 (key == GLFW.GLFW_KEY_ESCAPE || (action == GLFW.GLFW_PRESS && TOGGLE_GUI.matches(key))) {
+ 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 handleByGUI()}.
+ *
+ * @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 handleBlockedInputs(MinecraftClient client, int key, int scancode, int action, int modifiers) {
+ if (TOGGLE_GUI.matches(key)) return false;
+ return false;
+ }
+
+ /**
+ * Attempts to handle a key event outside the {@code CigaretteScreen} GUI and after input blocking.
+ *
{@code KeyboardMixin} checks this handler last, after {@code handleBlockedInputs()}.
+ *
+ * @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 handleCustom(MinecraftClient client, int key, int scancode, int action, int modifiers) {
+ if (action != GLFW.GLFW_PRESS) return false;
+ if (TOGGLE_GUI.matches(key)) {
+ Cigarette.SCREEN.setParent(client.currentScreen);
+ client.setScreen(Cigarette.SCREEN);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * A custom key bind used instead of the native {@code KeyBinding} class.
+ */
+ public static class CigaretteKeyBind {
+ /**
+ * The key code that triggers this keybind
+ */
+ private int key;
+ /**
+ * The default key code set for this keybind
+ */
+ private final int defaultKey;
+
+ /**
+ * Creates a custom key bind.
+ *
+ * @param defaultKey The key and default key to use
+ */
+ public CigaretteKeyBind(int defaultKey) {
+ this.key = defaultKey;
+ this.defaultKey = defaultKey;
+ }
+
+ /**
+ * {@return whether the input key matches the saved key code}
+ *
+ * @param key The key code to compare
+ */
+ public boolean matches(int key) {
+ return this.key == key;
+ }
+
+ /**
+ * {@return the key code that should trigger this key bind}
+ */
+ public int key() {
+ return this.key;
+ }
+
+ /**
+ * Sets the saved key.
+ *
+ * @param key The key code to save to this key bind
+ */
+ public void setKey(int key) {
+ this.key = key;
+ }
+
+ /**
+ * Resets this key bind to the default key.
+ */
+ public void reset() {
+ this.key = this.defaultKey;
+ }
+ }
+}
From 92b9185f4151676766e763df7c0b81c68683436b Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Fri, 3 Oct 2025 12:50:45 -0400
Subject: [PATCH 03/37] create Minecraft Keyboard mixin
---
.../dev/cigarette/mixin/KeyboardMixin.java | 25 +++++++++++++++++++
src/main/resources/cigarette.mixins.json | 4 +--
2 files changed, 27 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/dev/cigarette/mixin/KeyboardMixin.java
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..e6d31ebc
--- /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 (KeybindHelper.handleByGUI(client, key, scancode, action, modifiers) || KeybindHelper.handleBlockedInputs(client, key, scancode, action, modifiers) || KeybindHelper.handleCustom(client, key, scancode, action, modifiers)) {
+ ci.cancel();
+ return;
+ }
+ }
+}
diff --git a/src/main/resources/cigarette.mixins.json b/src/main/resources/cigarette.mixins.json
index 713f4a1b..b88ff572 100644
--- a/src/main/resources/cigarette.mixins.json
+++ b/src/main/resources/cigarette.mixins.json
@@ -10,14 +10,14 @@
"MinecraftClientInvoker",
"PlayerEntityMixin",
"PropertyMixin",
- "ScoreboardMixin",
- "ClientWorldAccessor"
+ "ScoreboardMixin"
],
"injectors": {
"defaultRequire": 1
},
"client": [
"KeyboardInputMixin",
+ "KeyboardMixin",
"MouseMixin"
]
}
\ No newline at end of file
From 63d944fe56f3c63282a975b604a7f1efdc7d61b7 Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Fri, 3 Oct 2025 12:54:43 -0400
Subject: [PATCH 04/37] remove KeyBinding mod initializer
---
.../java/dev/cigarette/gui/KeyBinding.java | 32 -------------------
src/main/resources/fabric.mod.json | 3 +-
2 files changed, 1 insertion(+), 34 deletions(-)
delete mode 100644 src/main/java/dev/cigarette/gui/KeyBinding.java
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 d18cae95..00000000
--- a/src/main/java/dev/cigarette/gui/KeyBinding.java
+++ /dev/null
@@ -1,32 +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;
-
- @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 {
- Cigarette.SCREEN.setParent(client.currentScreen);
- client.setScreen(Cigarette.SCREEN);
- }
- }
- });
- }
-}
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": [
From c59418e28c22143796d8483a903de3eef40048bb Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Fri, 3 Oct 2025 13:19:14 -0400
Subject: [PATCH 05/37] add binding and press checks
---
.../dev/cigarette/helper/KeybindHelper.java | 55 ++++++++++++++++++-
1 file changed, 52 insertions(+), 3 deletions(-)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index 8f99fa25..638aa8e3 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -5,11 +5,15 @@
import net.minecraft.client.MinecraftClient;
import org.lwjgl.glfw.GLFW;
+import java.util.HashSet;
+
public class KeybindHelper {
/**
* Keybind to toggle {@code CigaretteScreen}.
*/
- public static CigaretteKeyBind TOGGLE_GUI = new CigaretteKeyBind(GLFW.GLFW_KEY_RIGHT_SHIFT);
+ public static final CigaretteKeyBind TOGGLE_GUI = new CigaretteKeyBind(GLFW.GLFW_KEY_RIGHT_SHIFT);
+
+ private static final HashSet customBinds = new HashSet<>();
/**
* Attempts to handle a key event inside the {@code CigaretteScreen} GUI.
@@ -71,6 +75,11 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
client.setScreen(Cigarette.SCREEN);
return true;
}
+ for (CigaretteKeyBind binding : customBinds) {
+ if (!binding.matches(key)) continue;
+ binding.processAction(action);
+ return true;
+ }
return false;
}
@@ -79,13 +88,21 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
*/
public static class CigaretteKeyBind {
/**
- * The key code that triggers this keybind
+ * The key code that triggers this keybind.
*/
private int key;
/**
- * The default key code set for this keybind
+ * The default key code set for this keybind.
*/
private final int defaultKey;
+ /**
+ * Whether this key is currently pressed or not.
+ */
+ private boolean pressed = false;
+ /**
+ * The number of times this key has been pressed.
+ */
+ private int timesPressed = 0;
/**
* Creates a custom key bind.
@@ -95,6 +112,38 @@ public static class CigaretteKeyBind {
public CigaretteKeyBind(int defaultKey) {
this.key = defaultKey;
this.defaultKey = defaultKey;
+ customBinds.add(this);
+ }
+
+ /**
+ * Updates {@code pressed} and {@code timesPressed} depending on the action that occurred on the key.
+ *
+ * @param glfwAction The key events action as defined by GLFW
+ */
+ protected void processAction(int glfwAction) {
+ switch (glfwAction) {
+ case GLFW.GLFW_PRESS -> {
+ this.timesPressed++;
+ this.pressed = true;
+ }
+ case GLFW.GLFW_RELEASE -> this.pressed = false;
+ }
+ }
+
+ /**
+ * {@return whether this keybind is currently pressed}
+ */
+ public boolean isPressed() {
+ return this.pressed;
+ }
+
+ /**
+ * {@return whether this keybind was pressed}
+ */
+ public boolean wasPressed() {
+ if (this.timesPressed == 0) return false;
+ this.timesPressed--;
+ return true;
}
/**
From 3aa146f46e9382595c343fcef3921cb55eda34fc Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Fri, 3 Oct 2025 14:41:43 -0400
Subject: [PATCH 06/37] add input blocking methods
---
.../dev/cigarette/helper/KeybindHelper.java | 54 ++++++++++++++++++-
1 file changed, 52 insertions(+), 2 deletions(-)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index 638aa8e3..1c80a494 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -3,18 +3,27 @@
import dev.cigarette.Cigarette;
import dev.cigarette.gui.CigaretteScreen;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.option.KeyBinding;
import org.lwjgl.glfw.GLFW;
import java.util.HashSet;
public class KeybindHelper {
+ /**
+ * Set of custom keybinds that can be triggered.
+ */
+ private static final HashSet customBinds = new HashSet<>();
+
+ /**
+ * Set of keybinds that should be cancelled.
+ */
+ private static final HashSet blockedInputs = new HashSet<>();
+
/**
* Keybind to toggle {@code CigaretteScreen}.
*/
public static final CigaretteKeyBind TOGGLE_GUI = new CigaretteKeyBind(GLFW.GLFW_KEY_RIGHT_SHIFT);
- private static final HashSet customBinds = new HashSet<>();
-
/**
* Attempts to handle a key event inside the {@code CigaretteScreen} GUI.
*
{@code KeyboardMixin} checks this handler first.
@@ -54,6 +63,11 @@ public static boolean handleByGUI(MinecraftClient client, int key, int scancode,
*/
public static boolean handleBlockedInputs(MinecraftClient client, int key, int scancode, int action, int modifiers) {
if (TOGGLE_GUI.matches(key)) return false;
+ for (KeyBinding keybind : blockedInputs) {
+ if (keybind.matchesKey(key, scancode)) {
+ return true;
+ }
+ }
return false;
}
@@ -83,6 +97,42 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
return false;
}
+ /**
+ * Attempts to start input blocking. {@return false if input blocking is already running} Use {@code forceBlockInputs()} to bypass this check.
+ *
+ * @param keybindIds The keybind IDs to pass into {@code KeyBinding.byId()}
+ */
+ public static boolean tryBlockInputs(String... keybindIds) {
+ if (!blockedInputs.isEmpty()) return false;
+ for (String id : keybindIds) {
+ KeyBinding keybind = KeyBinding.byId(id);
+ if (keybind == null) continue;
+ blockedInputs.add(keybind);
+ }
+ return true;
+ }
+
+ /**
+ * Force starts input blocking with the provided configuration. Overrides any previously started configurations.
+ *
+ * @param keybindIds The keybind IDs to pass into {@code KeyBinding.byId()}
+ */
+ public static void forceBlockInputs(String... keybindIds) {
+ blockedInputs.clear();
+ for (String id : keybindIds) {
+ KeyBinding keybind = KeyBinding.byId(id);
+ if (keybind == null) continue;
+ blockedInputs.add(keybind);
+ }
+ }
+
+ /**
+ * Disable input blocking.
+ */
+ public static void unblock() {
+ blockedInputs.clear();
+ }
+
/**
* A custom key bind used instead of the native {@code KeyBinding} class.
*/
From 76ef306836dbd6f51c0734aba00c5cf1b36c2e1e Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Fri, 3 Oct 2025 14:52:41 -0400
Subject: [PATCH 07/37] add mouse blocking enabled by default when keys are
blocked
---
.../dev/cigarette/helper/KeybindHelper.java | 36 +++++++++++++++++--
1 file changed, 34 insertions(+), 2 deletions(-)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index 1c80a494..1db64cdf 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -14,11 +14,21 @@ public class KeybindHelper {
*/
private static final HashSet customBinds = new HashSet<>();
+ /**
+ * The module that is blocking inputs.
+ */
+ private static Object blockingModule = null;
+
/**
* Set of keybinds that should be cancelled.
*/
private static final HashSet blockedInputs = new HashSet<>();
+ /**
+ * Whether to block mouse movements from updating the players yaw/pitch.
+ */
+ private static boolean blockMouse = false;
+
/**
* Keybind to toggle {@code CigaretteScreen}.
*/
@@ -102,13 +112,15 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
*
* @param keybindIds The keybind IDs to pass into {@code KeyBinding.byId()}
*/
- public static boolean tryBlockInputs(String... keybindIds) {
+ public static boolean tryBlockInputs(Object module, String... keybindIds) {
if (!blockedInputs.isEmpty()) return false;
for (String id : keybindIds) {
KeyBinding keybind = KeyBinding.byId(id);
if (keybind == null) continue;
blockedInputs.add(keybind);
}
+ blockMouse = true;
+ blockingModule = module;
return true;
}
@@ -117,13 +129,15 @@ public static boolean tryBlockInputs(String... keybindIds) {
*
* @param keybindIds The keybind IDs to pass into {@code KeyBinding.byId()}
*/
- public static void forceBlockInputs(String... keybindIds) {
+ public static void forceBlockInputs(Object module, String... keybindIds) {
blockedInputs.clear();
for (String id : keybindIds) {
KeyBinding keybind = KeyBinding.byId(id);
if (keybind == null) continue;
blockedInputs.add(keybind);
}
+ blockMouse = true;
+ blockingModule = module;
}
/**
@@ -131,6 +145,24 @@ public static void forceBlockInputs(String... keybindIds) {
*/
public static void unblock() {
blockedInputs.clear();
+ blockMouse = false;
+ blockingModule = null;
+ }
+
+ /**
+ * {@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 the mouse is being input blocked}
+ */
+ public static boolean isMouseBlocked() {
+ return blockMouse;
}
/**
From fe99bdf9ecc0da781ec2098bd16285310c81bfba Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 00:32:37 -0400
Subject: [PATCH 08/37] create keybind wrappers and InputBlocker class
---
.../helper/keybind/InputBlocker.java | 37 ++++++++++++
.../helper/keybind/MinecraftKeybind.java | 58 +++++++++++++++++++
.../helper/keybind/VirtualKeybind.java | 50 ++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
create mode 100644 src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
create mode 100644 src/main/java/dev/cigarette/helper/keybind/VirtualKeybind.java
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..d239b00d
--- /dev/null
+++ b/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
@@ -0,0 +1,37 @@
+package dev.cigarette.helper.keybind;
+
+public class InputBlocker {
+ private final MinecraftKeybind[] blockedBindings;
+ protected boolean complete = false;
+
+ public InputBlocker(MinecraftKeybind... blockedBindings) {
+ this.blockedBindings = blockedBindings;
+ }
+
+ /**
+ * 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;
+ }
+
+ public boolean process(int key, int scancode, int action, int modifiers) {
+ if (!this.complete()) return false;
+ for (MinecraftKeybind binding : blockedBindings) {
+ if (!binding.isOf(key, scancode)) continue;
+ 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..b61e0a54
--- /dev/null
+++ b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
@@ -0,0 +1,58 @@
+package dev.cigarette.helper.keybind;
+
+import net.minecraft.client.option.KeyBinding;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.lwjgl.glfw.GLFW;
+
+public class MinecraftKeybind extends VirtualKeybind {
+ private @Nullable KeyBinding minecraftBinding;
+ private final @NotNull String minecraftKeybindId;
+
+ public MinecraftKeybind(@NotNull String keybindId) {
+ super(GLFW.GLFW_KEY_UNKNOWN);
+ this.minecraftBinding = KeyBinding.byId(keybindId);
+ this.minecraftKeybindId = keybindId;
+ }
+
+ /**
+ * 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();
+ }
+
+ @Override
+ public boolean isOf(int key, int scancode) {
+ if (this.minecraftBinding == null) return false;
+ return this.minecraftBinding.matchesKey(key, scancode);
+ }
+}
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..035e1745
--- /dev/null
+++ b/src/main/java/dev/cigarette/helper/keybind/VirtualKeybind.java
@@ -0,0 +1,50 @@
+package dev.cigarette.helper.keybind;
+
+import org.lwjgl.glfw.GLFW;
+
+public class VirtualKeybind {
+ protected int currentKey;
+ protected final int defaultKey;
+ protected boolean pressed = false;
+ protected int timesPressed = 0;
+
+ public VirtualKeybind(int defaultKey) {
+ this.currentKey = defaultKey;
+ this.defaultKey = defaultKey;
+ }
+
+ /**
+ * 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;
+ }
+
+ public boolean isOf(int key, int scancode) {
+ return this.currentKey == key;
+ }
+}
From 16d039d31ccb2600531eb54a756e68a01563b860 Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 00:41:38 -0400
Subject: [PATCH 09/37] use new wrappers
---
.../dev/cigarette/helper/KeybindHelper.java | 186 ++++--------------
1 file changed, 36 insertions(+), 150 deletions(-)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index 1db64cdf..00ff89ce 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -2,37 +2,41 @@
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.minecraft.client.MinecraftClient;
-import net.minecraft.client.option.KeyBinding;
+import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.util.HashSet;
public class KeybindHelper {
+ public static final MinecraftKeybind KEY_SNEAK = new MinecraftKeybind("key.sneak");
+ 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_RIGHT_CLICK = new MinecraftKeybind("key.use");
+ public static final MinecraftKeybind KEY_JUMP = new MinecraftKeybind("key.jump");
/**
- * Set of custom keybinds that can be triggered.
+ * Keybind to toggle {@code CigaretteScreen}.
*/
- private static final HashSet customBinds = new HashSet<>();
+ public static final VirtualKeybind TOGGLE_GUI = new VirtualKeybind(GLFW.GLFW_KEY_RIGHT_SHIFT);
/**
- * The module that is blocking inputs.
- */
- private static Object blockingModule = null;
-
- /**
- * Set of keybinds that should be cancelled.
+ * Set of custom keybinds that can be triggered.
*/
- private static final HashSet blockedInputs = new HashSet<>();
+ private static final HashSet customBinds = new HashSet<>();
/**
- * Whether to block mouse movements from updating the players yaw/pitch.
+ * The module that is blocking inputs.
*/
- private static boolean blockMouse = false;
+ private static Object blockingModule = null;
/**
- * Keybind to toggle {@code CigaretteScreen}.
+ * The input blocker to pass events through.
*/
- public static final CigaretteKeyBind TOGGLE_GUI = new CigaretteKeyBind(GLFW.GLFW_KEY_RIGHT_SHIFT);
+ private static @Nullable InputBlocker blockedInputs = null;
/**
* Attempts to handle a key event inside the {@code CigaretteScreen} GUI.
@@ -53,7 +57,7 @@ public static boolean handleByGUI(MinecraftClient client, int key, int scancode,
case GLFW.GLFW_RELEASE -> CigaretteScreen.bindingKey.keyReleased(key, scancode, modifiers);
}
} else {
- if (key == GLFW.GLFW_KEY_ESCAPE || (action == GLFW.GLFW_PRESS && TOGGLE_GUI.matches(key))) {
+ if (key == GLFW.GLFW_KEY_ESCAPE || (action == GLFW.GLFW_PRESS && TOGGLE_GUI.isOf(key, scancode))) {
Cigarette.SCREEN.close();
}
}
@@ -72,13 +76,9 @@ public static boolean handleByGUI(MinecraftClient client, int key, int scancode,
* @return Whether the key event should be cancelled
*/
public static boolean handleBlockedInputs(MinecraftClient client, int key, int scancode, int action, int modifiers) {
- if (TOGGLE_GUI.matches(key)) return false;
- for (KeyBinding keybind : blockedInputs) {
- if (keybind.matchesKey(key, scancode)) {
- return true;
- }
- }
- return false;
+ if (TOGGLE_GUI.isOf(key, scancode)) return false;
+ if (blockedInputs == null) return false;
+ return blockedInputs.process(key, scancode, action, modifiers);
}
/**
@@ -94,14 +94,14 @@ public static boolean handleBlockedInputs(MinecraftClient client, int key, int s
*/
public static boolean handleCustom(MinecraftClient client, int key, int scancode, int action, int modifiers) {
if (action != GLFW.GLFW_PRESS) return false;
- if (TOGGLE_GUI.matches(key)) {
+ if (TOGGLE_GUI.isOf(key, scancode)) {
Cigarette.SCREEN.setParent(client.currentScreen);
client.setScreen(Cigarette.SCREEN);
return true;
}
- for (CigaretteKeyBind binding : customBinds) {
- if (!binding.matches(key)) continue;
- binding.processAction(action);
+ for (VirtualKeybind binding : customBinds) {
+ if (!binding.isOf(key, scancode)) continue;
+ binding.physicalAction(action);
return true;
}
return false;
@@ -110,33 +110,23 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
/**
* Attempts to start input blocking. {@return false if input blocking is already running} Use {@code forceBlockInputs()} to bypass this check.
*
- * @param keybindIds The keybind IDs to pass into {@code KeyBinding.byId()}
+ * @param module
+ * @param blocker
*/
- public static boolean tryBlockInputs(Object module, String... keybindIds) {
- if (!blockedInputs.isEmpty()) return false;
- for (String id : keybindIds) {
- KeyBinding keybind = KeyBinding.byId(id);
- if (keybind == null) continue;
- blockedInputs.add(keybind);
- }
- blockMouse = true;
- blockingModule = module;
+ 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 keybindIds The keybind IDs to pass into {@code KeyBinding.byId()}
+ * @param module
+ * @param blocker
*/
- public static void forceBlockInputs(Object module, String... keybindIds) {
- blockedInputs.clear();
- for (String id : keybindIds) {
- KeyBinding keybind = KeyBinding.byId(id);
- if (keybind == null) continue;
- blockedInputs.add(keybind);
- }
- blockMouse = true;
+ public static void forceBlockInputs(Object module, InputBlocker blocker) {
+ blockedInputs = blocker;
blockingModule = module;
}
@@ -144,8 +134,7 @@ public static void forceBlockInputs(Object module, String... keybindIds) {
* Disable input blocking.
*/
public static void unblock() {
- blockedInputs.clear();
- blockMouse = false;
+ blockedInputs = null;
blockingModule = null;
}
@@ -157,107 +146,4 @@ public static void unblock() {
public static boolean isBlocking(Object module) {
return blockingModule == module;
}
-
- /**
- * {@return whether the mouse is being input blocked}
- */
- public static boolean isMouseBlocked() {
- return blockMouse;
- }
-
- /**
- * A custom key bind used instead of the native {@code KeyBinding} class.
- */
- public static class CigaretteKeyBind {
- /**
- * The key code that triggers this keybind.
- */
- private int key;
- /**
- * The default key code set for this keybind.
- */
- private final int defaultKey;
- /**
- * Whether this key is currently pressed or not.
- */
- private boolean pressed = false;
- /**
- * The number of times this key has been pressed.
- */
- private int timesPressed = 0;
-
- /**
- * Creates a custom key bind.
- *
- * @param defaultKey The key and default key to use
- */
- public CigaretteKeyBind(int defaultKey) {
- this.key = defaultKey;
- this.defaultKey = defaultKey;
- customBinds.add(this);
- }
-
- /**
- * Updates {@code pressed} and {@code timesPressed} depending on the action that occurred on the key.
- *
- * @param glfwAction The key events action as defined by GLFW
- */
- protected void processAction(int glfwAction) {
- switch (glfwAction) {
- case GLFW.GLFW_PRESS -> {
- this.timesPressed++;
- this.pressed = true;
- }
- case GLFW.GLFW_RELEASE -> this.pressed = false;
- }
- }
-
- /**
- * {@return whether this keybind is currently pressed}
- */
- public boolean isPressed() {
- return this.pressed;
- }
-
- /**
- * {@return whether this keybind was pressed}
- */
- public boolean wasPressed() {
- if (this.timesPressed == 0) return false;
- this.timesPressed--;
- return true;
- }
-
- /**
- * {@return whether the input key matches the saved key code}
- *
- * @param key The key code to compare
- */
- public boolean matches(int key) {
- return this.key == key;
- }
-
- /**
- * {@return the key code that should trigger this key bind}
- */
- public int key() {
- return this.key;
- }
-
- /**
- * Sets the saved key.
- *
- * @param key The key code to save to this key bind
- */
- public void setKey(int key) {
- this.key = key;
- }
-
- /**
- * Resets this key bind to the default key.
- */
- public void reset() {
- this.key = this.defaultKey;
- }
- }
}
From 818e623b62ed27d6dcb15c5b3e235f43618be29d Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 00:43:48 -0400
Subject: [PATCH 10/37] add mouse blocking
---
.../java/dev/cigarette/helper/KeybindHelper.java | 7 +++++++
.../cigarette/helper/keybind/InputBlocker.java | 15 +++++++++++++++
src/main/java/dev/cigarette/mixin/MouseMixin.java | 10 ++--------
3 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index 00ff89ce..d8f2adf8 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -146,4 +146,11 @@ public static void unblock() {
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();
+ }
}
diff --git a/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java b/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
index d239b00d..40f66745 100644
--- a/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
+++ b/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
@@ -2,12 +2,27 @@
public class InputBlocker {
private final MinecraftKeybind[] blockedBindings;
+ private boolean blockCamera = false;
protected boolean complete = false;
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;
+ }
+
+ public boolean blocksCamera() {
+ return this.blockCamera;
+ }
+
/**
* Verifies that each keybind object is linked with their native Minecraft {@code KeyBinding}.
*
diff --git a/src/main/java/dev/cigarette/mixin/MouseMixin.java b/src/main/java/dev/cigarette/mixin/MouseMixin.java
index 8c7034de..f6ff6ff3 100644
--- a/src/main/java/dev/cigarette/mixin/MouseMixin.java
+++ b/src/main/java/dev/cigarette/mixin/MouseMixin.java
@@ -1,9 +1,7 @@
package dev.cigarette.mixin;
-import dev.cigarette.lib.InputOverride;
-import net.minecraft.client.MinecraftClient;
+import dev.cigarette.helper.KeybindHelper;
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 +11,7 @@
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();
}
}
From df444595f91ab6b397c952f54647f5c8975c6c34 Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 01:30:03 -0400
Subject: [PATCH 11/37] add method to trigger virtual key press
---
.../dev/cigarette/helper/keybind/MinecraftKeybind.java | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
index b61e0a54..95b36575 100644
--- a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
+++ b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
@@ -1,6 +1,7 @@
package dev.cigarette.helper.keybind;
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;
@@ -50,6 +51,15 @@ public boolean wasVirtuallyPressed() {
return this.minecraftBinding.wasPressed();
}
+ /**
+ * Virtually presses the key triggering any Minecraft related events.
+ */
+ public void press() {
+ if (this.minecraftBinding == null) return;
+ InputUtil.Key key = InputUtil.fromTranslationKey(this.minecraftBinding.getBoundKeyTranslationKey());
+ KeyBinding.onKeyPressed(key);
+ }
+
@Override
public boolean isOf(int key, int scancode) {
if (this.minecraftBinding == null) return false;
From ef9ff9ffc0d4b5e89cef9abd579b25f397dbd57c Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 02:00:43 -0400
Subject: [PATCH 12/37] fix: wrapped keybinds do not receive events
---
.../dev/cigarette/helper/KeybindHelper.java | 30 +++++++++++++++++++
.../helper/keybind/MinecraftKeybind.java | 11 +++++++
2 files changed, 41 insertions(+)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index d8f2adf8..56f139c6 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -18,6 +18,18 @@ public class KeybindHelper {
public static final MinecraftKeybind KEY_MOVE_RIGHT = new MinecraftKeybind("key.right");
public static final MinecraftKeybind KEY_RIGHT_CLICK = new MinecraftKeybind("key.use");
public static final MinecraftKeybind KEY_JUMP = new MinecraftKeybind("key.jump");
+
+ private static final HashSet wrappedBindings = new HashSet<>();
+
+ static {
+ wrappedBindings.add(KEY_SNEAK);
+ wrappedBindings.add(KEY_MOVE_BACK);
+ wrappedBindings.add(KEY_MOVE_LEFT);
+ wrappedBindings.add(KEY_MOVE_RIGHT);
+ wrappedBindings.add(KEY_RIGHT_CLICK);
+ wrappedBindings.add(KEY_JUMP);
+ }
+
/**
* Keybind to toggle {@code CigaretteScreen}.
*/
@@ -93,6 +105,11 @@ public static boolean handleBlockedInputs(MinecraftClient client, int key, int s
* @return Whether the key event is handled and should be cancelled
*/
public static boolean handleCustom(MinecraftClient client, int key, int scancode, int action, int modifiers) {
+ for (MinecraftKeybind keybind : wrappedBindings) {
+ if (!keybind.isAttached() && !keybind.tryToAttach()) continue;
+ if (!keybind.isOf(key, scancode)) continue;
+ keybind.physicalAction(action);
+ }
if (action != GLFW.GLFW_PRESS) return false;
if (TOGGLE_GUI.isOf(key, scancode)) {
Cigarette.SCREEN.setParent(client.currentScreen);
@@ -107,6 +124,19 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
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 register
+ */
+ public static void registerWrapper(MinecraftKeybind keybind) {
+ for (MinecraftKeybind existing : wrappedBindings) {
+ if (existing.equals(keybind)) return;
+ }
+ wrappedBindings.add(keybind);
+ }
+
/**
* Attempts to start input blocking. {@return false if input blocking is already running} Use {@code forceBlockInputs()} to bypass this check.
*
diff --git a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
index 95b36575..ca142688 100644
--- a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
+++ b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
@@ -60,6 +60,17 @@ public void press() {
KeyBinding.onKeyPressed(key);
}
+ /**
+ * {@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;
From 510e3f5914a3b45b4a7599485d55e2ac4fab726c Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 02:38:58 -0400
Subject: [PATCH 13/37] properly support mouse events (no scrolling)
---
.../dev/cigarette/helper/KeybindHelper.java | 57 ++++++++++++++++---
.../helper/keybind/InputBlocker.java | 15 ++++-
.../helper/keybind/MinecraftKeybind.java | 12 ++++
.../helper/keybind/VirtualKeybind.java | 22 +++++++
.../dev/cigarette/mixin/KeyboardMixin.java | 2 +-
.../java/dev/cigarette/mixin/MouseMixin.java | 14 +++++
6 files changed, 113 insertions(+), 9 deletions(-)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index 56f139c6..46f3cd6e 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -16,7 +16,7 @@ public class KeybindHelper {
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_RIGHT_CLICK = new MinecraftKeybind("key.use");
+ public static final MinecraftKeybind KEY_RIGHT_CLICK = new MinecraftKeybind("key.use").asMouse();
public static final MinecraftKeybind KEY_JUMP = new MinecraftKeybind("key.jump");
private static final HashSet wrappedBindings = new HashSet<>();
@@ -61,7 +61,7 @@ public class KeybindHelper {
* @param modifiers The events modifier
* @return Whether the key event is handled and should be cancelled
*/
- public static boolean handleByGUI(MinecraftClient client, int key, int scancode, int action, int modifiers) {
+ 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) {
@@ -78,7 +78,7 @@ public static boolean handleByGUI(MinecraftClient client, int key, int scancode,
/**
* Checks if the input should be cancelled because of modules blocking inputs.
- *
{@code KeyboardMixin} checks this handler second, after {@code handleByGUI()}.
+ *
{@code KeyboardMixin} checks this handler second, after {@code handleKeyByGUI()}.
*
* @param client The Minecraft client
* @param key The events key code
@@ -87,15 +87,30 @@ public static boolean handleByGUI(MinecraftClient client, int key, int scancode,
* @param modifiers The events modifier
* @return Whether the key event should be cancelled
*/
- public static boolean handleBlockedInputs(MinecraftClient client, int key, int scancode, int action, int modifiers) {
+ public static boolean handleBlockedKeyInputs(MinecraftClient client, int key, int scancode, int action, int modifiers) {
if (TOGGLE_GUI.isOf(key, scancode)) return false;
if (blockedInputs == null) return false;
- return blockedInputs.process(key, scancode, action, modifiers);
+ 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 handleBlockedInputs()}.
+ *
{@code KeyboardMixin} checks this handler last, after {@code handleBlockedKeyInputs()}.
*
* @param client The Minecraft client
* @param key The events key code
@@ -104,9 +119,10 @@ public static boolean handleBlockedInputs(MinecraftClient client, int key, int s
* @param modifiers The events modifier
* @return Whether the key event is handled and should be cancelled
*/
- public static boolean handleCustom(MinecraftClient client, int key, int scancode, int action, int modifiers) {
+ 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);
}
@@ -117,6 +133,7 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
return true;
}
for (VirtualKeybind binding : customBinds) {
+ if (binding.isMouse()) continue;
if (!binding.isOf(key, scancode)) continue;
binding.physicalAction(action);
return true;
@@ -124,6 +141,32 @@ public static boolean handleCustom(MinecraftClient client, int key, int scancode
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()}.
diff --git a/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java b/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
index 40f66745..3eac1cfc 100644
--- a/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
+++ b/src/main/java/dev/cigarette/helper/keybind/InputBlocker.java
@@ -40,13 +40,26 @@ private boolean complete() {
return this.complete;
}
- public boolean process(int key, int scancode, int action, int modifiers) {
+ 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;
}
+
+ 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
index ca142688..6af1790c 100644
--- a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
+++ b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
@@ -71,9 +71,21 @@ public boolean equals(Object o) {
return this.minecraftBinding.getTranslationKey().equals(other.minecraftBinding.getTranslationKey());
}
+ @Override
+ public MinecraftKeybind asMouse() {
+ this.isMouse = true;
+ return this;
+ }
+
@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
index 035e1745..67b69754 100644
--- a/src/main/java/dev/cigarette/helper/keybind/VirtualKeybind.java
+++ b/src/main/java/dev/cigarette/helper/keybind/VirtualKeybind.java
@@ -7,12 +7,30 @@ public class VirtualKeybind {
protected final int defaultKey;
protected boolean pressed = false;
protected int timesPressed = 0;
+ protected boolean isMouse = false;
public VirtualKeybind(int defaultKey) {
this.currentKey = defaultKey;
this.defaultKey = defaultKey;
}
+ /**
+ * Indicates that this keybind should receive events from the mouse instead of the keyboard.
+ *
+ * @return This object for method chaining
+ */
+ public VirtualKeybind asMouse() {
+ this.isMouse = true;
+ return this;
+ }
+
+ /**
+ * {@return whether this a mouse keybind or not}
+ */
+ public boolean isMouse() {
+ return this.isMouse;
+ }
+
/**
* Processes a physical action on the key.
*
@@ -47,4 +65,8 @@ public boolean wasPhysicallyPressed() {
public boolean isOf(int key, int scancode) {
return this.currentKey == key;
}
+
+ public boolean isOfMouse(int button) {
+ return this.currentKey == button;
+ }
}
diff --git a/src/main/java/dev/cigarette/mixin/KeyboardMixin.java b/src/main/java/dev/cigarette/mixin/KeyboardMixin.java
index e6d31ebc..382ea92b 100644
--- a/src/main/java/dev/cigarette/mixin/KeyboardMixin.java
+++ b/src/main/java/dev/cigarette/mixin/KeyboardMixin.java
@@ -17,7 +17,7 @@ private void onKey(long window, int key, int scancode, int action, int modifiers
ci.cancel();
return;
}
- if (KeybindHelper.handleByGUI(client, key, scancode, action, modifiers) || KeybindHelper.handleBlockedInputs(client, key, scancode, action, modifiers) || KeybindHelper.handleCustom(client, key, scancode, action, modifiers)) {
+ if (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 f6ff6ff3..203b4c1e 100644
--- a/src/main/java/dev/cigarette/mixin/MouseMixin.java
+++ b/src/main/java/dev/cigarette/mixin/MouseMixin.java
@@ -1,6 +1,7 @@
package dev.cigarette.mixin;
import dev.cigarette.helper.KeybindHelper;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.client.Mouse;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -14,4 +15,17 @@ private void updateMouse(double timeDelta, CallbackInfo ci) {
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 (KeybindHelper.handleBlockedMouseInputs(client, button, action, mods) || KeybindHelper.handleCustomMouse(client, button, action, mods)) {
+ ci.cancel();
+ return;
+ }
+ }
}
From e85f5019c78d0b11e37ac3170bf9db86c1b7e6f9 Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 03:10:08 -0400
Subject: [PATCH 14/37] add virtual key holding and releasing
---
.../helper/keybind/MinecraftKeybind.java | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
index 6af1790c..87535b76 100644
--- a/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
+++ b/src/main/java/dev/cigarette/helper/keybind/MinecraftKeybind.java
@@ -60,6 +60,24 @@ public void press() {
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 releases the key.
+ */
+ public void release() {
+ if (this.minecraftBinding == null) return;
+ InputUtil.Key key = InputUtil.fromTranslationKey(this.minecraftBinding.getBoundKeyTranslationKey());
+ KeyBinding.setKeyPressed(key, false);
+ }
+
/**
* {@return whether the translation key name of both objects are identical}
*
From c603665307d1ae3ac31141a49cbaaab1761d465d Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Sat, 4 Oct 2025 03:28:05 -0400
Subject: [PATCH 15/37] fix: mouse virtual state desyncing from physical state
after unblocking
---
.../java/dev/cigarette/helper/KeybindHelper.java | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/main/java/dev/cigarette/helper/KeybindHelper.java b/src/main/java/dev/cigarette/helper/KeybindHelper.java
index 46f3cd6e..762bd057 100644
--- a/src/main/java/dev/cigarette/helper/KeybindHelper.java
+++ b/src/main/java/dev/cigarette/helper/KeybindHelper.java
@@ -6,6 +6,8 @@
import dev.cigarette.helper.keybind.MinecraftKeybind;
import dev.cigarette.helper.keybind.VirtualKeybind;
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;
@@ -209,6 +211,7 @@ public static void forceBlockInputs(Object module, InputBlocker blocker) {
public static void unblock() {
blockedInputs = null;
blockingModule = null;
+ updateKeyStates();
}
/**
@@ -226,4 +229,15 @@ public static boolean isBlocking(Object module) {
public static boolean isMouseBlocked() {
return blockedInputs != null && blockedInputs.blocksCamera();
}
+
+ 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);
+ }
+ }
}
From 6731f9dfdcd4ba5f7b00a91be59b8a41e6c17213 Mon Sep 17 00:00:00 2001
From: UndercoverGoose <53491740+UndercoverGoose@users.noreply.github.com>
Date: Tue, 7 Oct 2025 15:07:12 -0400
Subject: [PATCH 16/37] create tick helper to schedule events to occur in X
ticks
---
.../java/dev/cigarette/helper/TickHelper.java | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 src/main/java/dev/cigarette/helper/TickHelper.java
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..2cef7069
--- /dev/null
+++ b/src/main/java/dev/cigarette/helper/TickHelper.java
@@ -0,0 +1,39 @@
+package dev.cigarette.helper;
+
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
+import net.minecraft.util.Pair;
+
+import java.util.HashMap;
+
+public class TickHelper {
+ private static final HashMap