Metin2游戏模拟按键代码无响应问题排查及修复方案咨询
在Metin2中Python模拟按键无响应的排查与修复方案
问题背景
需求为:让Metin2游戏持续按住空格键,每120秒点击一次数字4键。尝试了基于ctypes和pynput的两种模拟实现,均无响应。
原始代码1(ctypes实现)
import ctypes import time SendInput = ctypes.windll.user32.SendInput # C struct redefinitions PUL = ctypes.POINTER(ctypes.c_ulong) class KeyBdInput(ctypes.Structure): _fields_ = [("wVk", ctypes.c_ushort), ("wScan", ctypes.c_ushort), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", PUL)] class HardwareInput(ctypes.Structure): _fields_ = [("uMsg", ctypes.c_ulong), ("wParamL", ctypes.c_short), ("wParamH", ctypes.c_ushort)] class MouseInput(ctypes.Structure): _fields_ = [("dx", ctypes.c_long), ("dy", ctypes.c_long), ("mouseData", ctypes.c_ulong), ("dwFlags", ctypes.c_ulong), ("time",ctypes.c_ulong), ("dwExtraInfo", PUL)] class Input_I(ctypes.Union): _fields_ = [("ki", KeyBdInput), ("mi", MouseInput), ("hi", HardwareInput)] class Input(ctypes.Structure): _fields_ = [("type", ctypes.c_ulong), ("ii", Input_I)] # Actuals Functions def PressKey(hexKeyCode): extra = ctypes.c_ulong(0) ii_ = Input_I() ii_.ki = KeyBdInput( 0, hexKeyCode, 0x0008, 0, ctypes.pointer(extra) ) x = Input( ctypes.c_ulong(1), ii_ ) ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) def ReleaseKey(hexKeyCode): extra = ctypes.c_ulong(0) ii_ = Input_I() ii_.ki = KeyBdInput( 0, hexKeyCode, 0x0008 | 0x0002, 0, ctypes.pointer(extra) ) x = Input( ctypes.c_ulong(1), ii_ ) ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) while (True): PressKey(0x39) time.sleep(1)
原始代码2(pynput实现)
from pynput.keyboard import Key, Controller import time from threading import Timer keyboard = Controller() def hold_space(): keyboard.press(Key.space) print("Trzymanie spacji...") def release_space(): keyboard.release(Key.space) print("Zwolniono spację") def click_button_4(): keyboard.press('4') print("Kliknięto przycisk 4") keyboard.release('4') def repeat_space_click(): hold_space() Timer(0.1, release_space).start() # Zwolnij spację po 1 sekundzie # Uruchom ponownie klikanie spacją co 1 sekundę Timer(0.1, repeat_space_click).start() def repeat_button_4_click(): click_button_4() # Uruchom ponownie klikanie przycisku 4 co 120 sekund Timer(120, repeat_button_4_click).start() # Uruchom akcje po 5 sekundach Timer(5, repeat_space_click).start() Timer(5, repeat_button_4_click).start() try: # Uruchom pętlę główną, aby program nie zakończył się natychmiast while True: time.sleep(1) except KeyboardInterrupt: print("Przerwano przez użytkownika")
排查原因
- 游戏输入拦截机制:Metin2作为3D网游,会拦截普通用户态的模拟输入,仅响应更接近硬件级的输入信号。
- 模拟逻辑错误:
- ctypes代码中反复按空格但未持续按住,且按键参数(如
dwFlags)设置不符合游戏识别要求。 - pynput代码中仅短暂按住空格(0.1秒)后立即释放,未实现"持续按住"的需求,同时Timer嵌套可能导致逻辑混乱。
- ctypes代码中反复按空格但未持续按住,且按键参数(如
- 权限与焦点问题:脚本未以管理员权限运行,或游戏窗口未处于前台焦点,导致输入无法被接收。
修复方案
前置准备
- 以管理员身份运行Python脚本
- 确保Metin2窗口处于前台,脚本运行后不要切换窗口焦点
- 关闭游戏内的第三方辅助/反作弊增强设置(若有)
修复后的ctypes实现
import ctypes import time import threading # 定义常量 KEYEVENTF_SCANCODE = 0x0008 KEYEVENTF_KEYUP = 0x0002 SPACE_SCANCODE = 0x39 KEY_4_SCANCODE = 0x1E # 初始化SendInput user32 = ctypes.windll.user32 SendInput = user32.SendInput # 重新定义输入结构 PUL = ctypes.POINTER(ctypes.c_ulong) class KeyBdInput(ctypes.Structure): _fields_ = [("wVk", ctypes.c_ushort), ("wScan", ctypes.c_ushort), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", PUL)] class Input_I(ctypes.Union): _fields_ = [("ki", KeyBdInput)] class Input(ctypes.Structure): _fields_ = [("type", ctypes.c_ulong), ("ii", Input_I)] def press_key(scancode): extra = ctypes.c_ulong(0) ii = Input_I() ii.ki = KeyBdInput(0, scancode, KEYEVENTF_SCANCODE, 0, ctypes.pointer(extra)) input_obj = Input(1, ii) SendInput(1, ctypes.pointer(input_obj), ctypes.sizeof(input_obj)) def release_key(scancode): extra = ctypes.c_ulong(0) ii = Input_I() ii.ki = KeyBdInput(0, scancode, KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, 0, ctypes.pointer(extra)) input_obj = Input(1, ii) SendInput(1, ctypes.pointer(input_obj), ctypes.sizeof(input_obj)) def click_key_4(): press_key(KEY_4_SCANCODE) time.sleep(0.1) release_key(KEY_4_SCANCODE) print("已按下数字4键") # 120秒后再次执行 threading.Timer(120, click_key_4).start() try: # 持续按住空格 press_key(SPACE_SCANCODE) print("已持续按住空格键") # 启动数字4的定时点击 click_key_4() # 保持脚本运行 while True: time.sleep(1) except KeyboardInterrupt: # 释放空格 release_key(SPACE_SCANCODE) print("程序终止,已释放空格键")
修复后的pynput实现(适配游戏输入)
from pynput.keyboard import Key, Controller, Listener import time import threading keyboard = Controller() def click_key_4(): keyboard.press('4') time.sleep(0.1) keyboard.release('4') print("已按下数字4键") threading.Timer(120, click_key_4).start() try: # 持续按住空格 keyboard.press(Key.space) print("已持续按住空格键") # 启动数字4定时任务 click_key_4() # 保持脚本运行,监听终止信号 with Listener(on_press=None) as listener: listener.join() except KeyboardInterrupt: keyboard.release(Key.space) print("程序终止,已释放空格键")
补充说明
- 若仍无响应,可尝试使用
pywin32库直接向游戏窗口发送消息,或使用硬件级模拟工具(如AutoHotkey的SendInput模式)。 - 部分Metin2服务器可能检测模拟输入,使用前需确认服务器规则,避免账号封禁。
内容的提问来源于stack exchange,提问作者Kapi
相关产品推荐
相关产品推荐

