Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/main/java/top/fpsmaster/features/command/CommandKeys.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package top.fpsmaster.features.command;

import org.lwjgl.input.Keyboard;
import top.fpsmaster.features.settings.impl.BindKeys;

import java.util.Locale;

/**
* Key-name parsing for {@code .bind} and {@code .shortcut set}, over LWJGL2's keyboard tables.
* Key-name parsing for {@code .bind} and {@code .shortcut set}: 键盘走 LWJGL2 键盘表,鼠标认
* {@code MOUSE4} 这类名字(码值与显示名见 {@link BindKeys})。
*
* <p>{@link Keyboard#getKeyIndex(String)} returns {@link Keyboard#KEY_NONE} for anything it does not
* know, which is the same value a deliberate unbind uses. {@link #parse(String)} therefore returns
Expand All @@ -32,6 +34,10 @@ public static Integer parse(String raw) {
return Keyboard.KEY_NONE;
}
}
Integer mouse = BindKeys.fromName(normalized);
if (mouse != null) {
return mouse;
}
int key = Keyboard.getKeyIndex(normalized.toUpperCase(Locale.ROOT));
return key == Keyboard.KEY_NONE ? null : key;
}
Expand All @@ -41,7 +47,7 @@ public static String format(int key) {
if (key == Keyboard.KEY_NONE) {
return "NONE";
}
String name = Keyboard.getKeyName(key);
return name == null || name.isEmpty() ? "KEY_" + key : name;
String name = BindKeys.name(key);
return name == null ? "KEY_" + key : name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import top.fpsmaster.exception.FileException;
import top.fpsmaster.features.manager.Category;
import top.fpsmaster.features.manager.Module;
import top.fpsmaster.features.settings.impl.BindKeys;
import top.fpsmaster.features.settings.impl.BindSetting;
import top.fpsmaster.features.settings.impl.BooleanSetting;
import top.fpsmaster.features.settings.impl.ModeSetting;
Expand Down Expand Up @@ -81,7 +82,7 @@ public static boolean isZoomBindDown() {
return false;
}

if (Keyboard.isKeyDown(zoomKey)) {
if (BindKeys.isDown(zoomKey)) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import top.fpsmaster.event.events.EventUpdate;
import top.fpsmaster.features.manager.Category;
import top.fpsmaster.features.manager.Module;
import top.fpsmaster.features.settings.impl.BindKeys;
import top.fpsmaster.features.settings.impl.BindSetting;
import top.fpsmaster.features.settings.impl.BooleanSetting;
import top.fpsmaster.features.settings.impl.NumberSetting;
Expand Down Expand Up @@ -66,7 +67,7 @@ public void onUpdate(EventUpdate e) {
}

public boolean isZoomKeyDown() {
return Keyboard.isKeyDown(zoomBind.getValue());
return BindKeys.isDown(zoomBind.getValue());
}

public static boolean isZoomKeyActive() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import top.fpsmaster.event.events.EventRender3D;
import top.fpsmaster.features.manager.Category;
import top.fpsmaster.features.manager.Module;
import top.fpsmaster.features.settings.impl.BindKeys;
import top.fpsmaster.features.settings.impl.BindSetting;

public class FreeLook extends Module {
Expand Down Expand Up @@ -34,16 +35,16 @@ public void onRender3D(EventRender3D e) {
if (Minecraft.getMinecraft().currentScreen != null) return;

if (!perspectiveToggled) {
if (Keyboard.isKeyDown(bind.getValue())) {
perspectiveToggled = true;
if (BindKeys.isDown(bind.getValue())) {
perspectiveToggled = true;
if (Minecraft.getMinecraft().thePlayer != null) {
cameraYaw = Minecraft.getMinecraft().thePlayer.rotationYaw;
cameraPitch = Minecraft.getMinecraft().thePlayer.rotationPitch;
}
previousPerspective = Minecraft.getMinecraft().gameSettings.hideGUI;
Minecraft.getMinecraft().gameSettings.thirdPersonView = 1;
}
} else if (!Keyboard.isKeyDown(bind.getValue())) {
} else if (!BindKeys.isDown(bind.getValue())) {
perspectiveToggled = false;
Minecraft.getMinecraft().gameSettings.thirdPersonView = previousPerspective ? 1 : 0;
}
Expand Down
90 changes: 90 additions & 0 deletions src/main/java/top/fpsmaster/features/settings/impl/BindKeys.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package top.fpsmaster.features.settings.impl;

import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;

/**
* 绑定值的按键码约定:键盘走 LWJGL2 码(&gt;0,0 = 未绑定);鼠标走原版 {@code GuiControls}
* 写进 options.txt 的 {@code -100 + 按钮序号},与原版控制里绑的侧键共用一份码。左/右键留给
* 界面点击与攻击,只有中键和侧键可绑。
*/
public final class BindKeys {
/** 原版鼠标码基准:{@code -100 + button}(button 是 LWJGL 的 0..n,0 = 左键)。 */
public static final int MOUSE_BASE = -100;

/** 可绑定的最小鼠标按钮:中键。 */
public static final int MIN_BINDABLE_BUTTON = 2;

/** 鼠标按钮数上限,越界当无效。 */
public static final int MAX_MOUSE_BUTTONS = 8;

/** 鼠标码过 prism raw key 通道的中转基准:那条通道拿 -1 表示「这一帧没按键」。 */
private static final int MOUSE_WIRE_BASE = 1000;

private BindKeys() {
}

/** 负数是鼠标码({@code -100 + 按钮}),正数才是键盘码。 */
public static boolean isMouse(int code) {
return code < 0;
}

/** 鼠标码 → LWJGL 按钮号(0 = 左键);不是鼠标码时结果无意义。 */
public static int mouseButton(int code) {
return code - MOUSE_BASE;
}

public static int mouseCode(int button) {
return MOUSE_BASE + button;
}

/** 这个鼠标按钮能不能当绑定:中键与侧键可以,左右键留给界面与战斗。 */
public static boolean isBindableButton(int button) {
return button >= MIN_BINDABLE_BUTTON && button < MAX_MOUSE_BUTTONS;
}

/** 鼠标码 → prism raw key 通道能接受的非负值(键盘码原样过)。 */
public static int toWire(int code) {
return isMouse(code) ? MOUSE_WIRE_BASE + mouseButton(code) : code;
}

/** {@link #toWire} 的逆运算。 */
public static int fromWire(int code) {
return code >= MOUSE_WIRE_BASE ? mouseCode(code - MOUSE_WIRE_BASE) : code;
}

/** 按住式绑定(缩放、自由视角)的按下判断。 */
public static boolean isDown(int code) {
if (isMouse(code)) {
int button = mouseButton(code);
return isBindableButton(button) && Mouse.isButtonDown(button);
}
return code > 0 && Keyboard.isKeyDown(code);
}

/** 显示名;未绑定或认不出的码返回 {@code null},由调用点决定兜底文案。 */
public static String name(int code) {
if (isMouse(code)) {
int button = mouseButton(code);
return isBindableButton(button) ? "MOUSE" + (button + 1) : null;
}
if (code <= 0) {
return null;
}
String name = Keyboard.getKeyName(code);
return name == null || name.isEmpty() || "NONE".equalsIgnoreCase(name) ? null : name;
}

/** 名字 → 码,认 "MOUSE4" 这类鼠标名(1 起数,和显示名一致);认不出返回 {@code null}。 */
public static Integer fromName(String name) {
if (name == null || !name.regionMatches(true, 0, "MOUSE", 0, 5)) {
return null;
}
try {
int button = Integer.parseInt(name.substring(5).trim()) - 1;
return isBindableButton(button) ? Integer.valueOf(mouseCode(button)) : null;
} catch (NumberFormatException e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package top.fpsmaster.forge.mixin;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainerCreative;
import net.minecraft.inventory.Slot;
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;

/**
* 创造模式物品栏的热键栏换位只同步了一半:非「物品栏」页里悬停热键栏格按热键栏键(mode 2),
* 原版走 {@code handleMouseClick} 末尾分支,本地两格都换了,却只给悬停格发
* {@code sendSlotPacket}——目标热键栏格从没发给服务端,服务端那格还是旧物品,一放置就露馅。
* 原版用数字键也一样,鼠标侧键绑热键栏后更容易撞上。
*
* <p>挂 TAIL:物品格(创造物品表)那支和丢弃等分支都中途 return 不会走到这里;「物品栏」页那支
* 已经 detectAndSendChanges 过,这里再发一次同值包无害。
*/
@Mixin(GuiContainerCreative.class)
public class GuiContainerCreativeMixin_HotbarSwapSync {

@Inject(method = "handleMouseClick", at = @At("TAIL"))
private void edge$syncSwappedHotbarSlot(Slot slotIn, int slotId, int clickedButton, int clickType, CallbackInfo ci) {
if (clickType != 2 || slotIn == null || clickedButton < 0 || clickedButton >= 9) {
return;
}
Minecraft mc = Minecraft.getMinecraft();
// inventoryContainer 里热键栏 i 是 36 + i。
mc.playerController.sendSlotPacket(mc.thePlayer.inventory.getStackInSlot(clickedButton), 36 + clickedButton);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package top.fpsmaster.forge.mixin;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import org.lwjgl.input.Mouse;
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;
import top.fpsmaster.event.EventDispatcher;
import top.fpsmaster.event.events.EventKey;
import top.fpsmaster.features.settings.impl.BindKeys;
import top.fpsmaster.replay.ReplayDirectorIsolation;

/**
* 世界里的鼠标中键/侧键:补一个 {@link EventKey}(码 = {@code -100 + 按钮},见 {@link BindKeys}),
* 和键盘路径共用一条总线。
*
* <p>界面里的按键不走这里——runTick 的鼠标循环在界面打开时已被 handleInput 抽干,改由
* {@link MixinGuiScreen_MouseBinds} 挂在 handleMouseInput 上。
*/
@Mixin(Minecraft.class)
public class MinecraftMixin_MouseBinds {

@Shadow
public GuiScreen currentScreen;

/**
* 挂在 runTick 处理鼠标事件的位置:此处的当前事件(按钮号/按下与否)已经就绪。
*/
@Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lorg/lwjgl/input/Mouse;getEventButton()I"))
private void edge$mouseBinds(CallbackInfo ci) {
if (currentScreen != null || !Mouse.getEventButtonState()) {
return;
}
int button = Mouse.getEventButton();
if (!BindKeys.isBindableButton(button) || ReplayDirectorIsolation.isDirectorView()) {
return;
}
EventDispatcher.dispatchEvent(new EventKey(BindKeys.mouseCode(button)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package top.fpsmaster.forge.mixin;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.inventory.GuiContainer;
import org.lwjgl.input.Mouse;
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;
import top.fpsmaster.features.settings.impl.BindKeys;
import top.fpsmaster.forge.mixin.accessor.GuiContainerAccessor;

/**
* 容器界面里的鼠标中键/侧键:原版只有键盘事件会进 {@code GuiContainer.keyTyped},背包里按
* 侧键(在原版控制里绑给热键栏/丢弃)什么都不动。这里把这次按键按进 keyTyped,与键盘一致。
*
* <p>挂 handleMouseInput 而不是 runTick 的鼠标循环:界面打开时鼠标事件由
* {@code GuiScreen.handleInput} 抽干后逐个派到这里,runTick 那条循环收不到。
*/
@Mixin(GuiScreen.class)
public class MixinGuiScreen_MouseBinds {

@Inject(method = "handleMouseInput", at = @At("HEAD"))
private void edge$mouseKeyTyped(CallbackInfo ci) {
GuiScreen self = (GuiScreen) (Object) this;
// 按键抬起也带着同一个按钮号,不滤掉的话换位会连做两下、互相抵消。
if (!Mouse.getEventButtonState() || !(self instanceof GuiContainer)
|| Minecraft.getMinecraft().currentScreen != self) {
return;
}
int button = Mouse.getEventButton();
if (!BindKeys.isBindableButton(button)) {
return;
}
// 取方块是原版唯一认鼠标按钮的容器键(mouseClicked 里按 getKeyCode() + 100 判),
// 再喂一次 keyTyped 会双踩同一次取方块。
Minecraft mc = Minecraft.getMinecraft();
if (BindKeys.mouseCode(button) == mc.gameSettings.keyBindPickBlock.getKeyCode()) {
return;
}
((GuiContainerAccessor) self).fpsmaster$keyTyped((char) 0, BindKeys.mouseCode(button));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package top.fpsmaster.forge.mixin.accessor;

import net.minecraft.client.gui.inventory.GuiContainer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;

/**
* Reaches {@code GuiContainer.keyTyped}, which is protected.
*
* <p>原版只把键盘事件送进 keyTyped;鼠标绑定的按键要在背包里换位/丢弃/选取方块,就得自己按一次。
*/
@Mixin(GuiContainer.class)
public interface GuiContainerAccessor {

@Invoker("keyTyped")
void fpsmaster$keyTyped(char typedChar, int keyCode);
}
9 changes: 6 additions & 3 deletions src/main/java/top/fpsmaster/ui/click/MainPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import top.fpsmaster.features.settings.Setting;
import top.fpsmaster.features.settings.impl.AutoTextEntry;
import top.fpsmaster.features.settings.impl.AutoTextSetting;
import top.fpsmaster.features.settings.impl.BindKeys;
import top.fpsmaster.features.settings.impl.BindSetting;
import top.fpsmaster.features.settings.impl.BooleanSetting;
import top.fpsmaster.features.settings.impl.ColorSetting;
Expand Down Expand Up @@ -345,11 +346,13 @@ private static boolean matchesSearch(Module module, String rawQuery) {
}

private String keyName(int keyCode) {
String name = keyCode == 0 ? null : Keyboard.getKeyName(keyCode);
return name == null || name.isEmpty() ? FPSMaster.i18n.get("clickgui.bind.none") : name;
String name = BindKeys.name(keyCode);
return name == null ? FPSMaster.i18n.get("clickgui.bind.none") : name;
}

private static int normalizeKey(int keyCode) {
return keyCode == Keyboard.KEY_BACK || keyCode == Keyboard.KEY_DELETE ? 0 : keyCode;
// 过桥的鼠标码(1000+n)先翻回来,再判空绑定。
int code = BindKeys.fromWire(keyCode);
return code == Keyboard.KEY_BACK || code == Keyboard.KEY_DELETE ? 0 : code;
}
}
12 changes: 3 additions & 9 deletions src/main/java/top/fpsmaster/ui/click/UiChrome.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package top.fpsmaster.ui.click;

import org.lwjgl.input.Keyboard;
import top.fpsmaster.FPSMaster;
import top.fpsmaster.features.settings.impl.BindKeys;
import top.fpsmaster.font.impl.UFontRenderer;
import top.fpsmaster.ui.kit.EdgeUi;
import top.fpsmaster.utils.render.draw.Hover;
Expand Down Expand Up @@ -187,14 +187,8 @@ public static void searchBox(float x, float y, float width, float height, boolea
}

public static String keyName(int key) {
if (key <= 0) {
return FPSMaster.i18n.get("clickgui.bind.none");
}
String name = Keyboard.getKeyName(key);
if (name == null || name.isEmpty() || "NONE".equalsIgnoreCase(name)) {
return FPSMaster.i18n.get("clickgui.bind.none");
}
return name;
String name = BindKeys.name(key);
return name == null ? FPSMaster.i18n.get("clickgui.bind.none") : name;
}

public static float keyChipWidth(String text) {
Expand Down
Loading
Loading