如何用Python读取原始鼠标输入?适配Minecraft宏录制场景
读取原始鼠标输入实现Minecraft宏录制
问题分析
你提到PyAutoGUI和Pynput的常规方法只能获取屏幕坐标,但Minecraft会锁定鼠标在屏幕中心,导致坐标变化无法反映真实的鼠标移动输入。你之前的代码存在几个关键问题:
- 计算
change_y时错误地减去了base_position_x,正确应为base_position_y - 回放代码里
zip(x_coords, x_coords)是笔误,应该是zip(x_coords, y_coords) - PyAutoGUI的
move()默认是绝对坐标移动,而非相对偏移,需改用moveRel() - 依赖屏幕坐标的方案本身不适用于鼠标被锁定的场景,必须读取原始鼠标输入事件才能解决问题
解决方案:读取原始鼠标输入
Windows平台(使用pywin32监听原始输入)
通过Windows原始输入API捕获鼠标的原始移动数据,不受屏幕坐标限制:
import win32api import win32con import win32gui import time # 注册原始输入设备 def register_raw_input(hwnd): rid = win32gui.RAWINPUTDEVICE() rid.usUsagePage = 0x01 rid.usUsage = 0x02 # 指定鼠标设备 rid.dwFlags = win32con.RIDEV_INPUTSINK rid.hwndTarget = hwnd win32gui.RegisterRawInputDevices([rid]) # 解析原始输入消息,提取鼠标偏移量 def process_raw_input(msg): data = win32gui.GetRawInputData(msg.lParam, win32con.RID_INPUT) if data: raw_input = data[1] if raw_input.header.dwType == win32con.RIM_TYPEMOUSE: mouse_data = raw_input.data.mouse dx = mouse_data.lLastX dy = mouse_data.lLastY return dx, dy return 0, 0 # 创建隐藏窗口接收原始输入消息 hwnd = win32gui.CreateWindowEx(0, win32gui.WNDCLASS(), "RawInputWindow", 0, 0, 0, 0, 0, 0, 0, None) register_raw_input(hwnd) # 录制原始鼠标移动(包含时间戳) recorded_moves = [] start_time = time.time() print("开始录制,按Ctrl+C停止") try: while True: msg = win32gui.GetMessage(None, 0, 0) if msg.message == win32con.WM_INPUT: dx, dy = process_raw_input(msg) if dx != 0 or dy != 0: recorded_moves.append((dx, dy, time.time() - start_time)) win32gui.TranslateMessage(msg) win32gui.DispatchMessage(msg) time.sleep(0.001) except KeyboardInterrupt: print("录制结束") # 回放原始鼠标移动 print("开始回放") start_time = time.time() for dx, dy, timestamp in recorded_moves: # 同步时间戳 while time.time() - start_time < timestamp: time.sleep(0.001) # 模拟原始鼠标移动 win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, dx, dy, 0, 0)
Linux平台(使用evdev)
通过evdev直接读取输入设备的原始事件,需要先安装依赖:pip install evdev
from evdev import InputDevice, categorize, ecodes import time # 替换为你的鼠标设备路径,可通过`ls /dev/input/event*`查找 mouse_device = InputDevice('/dev/input/event5') recorded_moves = [] start_time = time.time() print("开始录制,按Ctrl+C停止") try: for event in mouse_device.read_loop(): if event.type == ecodes.EV_REL: rel_event = categorize(event) if rel_event.event.code == ecodes.REL_X: recorded_moves.append(('x', rel_event.event.value, time.time() - start_time)) elif rel_event.event.code == ecodes.REL_Y: recorded_moves.append(('y', rel_event.event.value, time.time() - start_time)) except KeyboardInterrupt: print("录制结束") # 回放原始移动(需root权限或用户加入input组) print("开始回放") start_time = time.time() for axis, value, timestamp in recorded_moves: while time.time() - start_time < timestamp: time.sleep(0.001) if axis == 'x': mouse_device.write(ecodes.EV_REL, ecodes.REL_X, value) elif axis == 'y': mouse_device.write(ecodes.EV_REL, ecodes.REL_Y, value) mouse_device.syn() # 同步事件,确保系统接收
注意事项
- Windows下运行需管理员权限,否则无法捕获原始输入
- Linux下需将用户加入
input组(sudo usermod -aG input 你的用户名)或用root权限运行 - Minecraft的反作弊机制可能检测模拟输入,建议在单机模式测试,避免账号风险
内容的提问来源于stack exchange,提问作者user24963864
相关产品推荐
相关产品推荐

