You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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")

排查原因

  1. 游戏输入拦截机制:Metin2作为3D网游,会拦截普通用户态的模拟输入,仅响应更接近硬件级的输入信号。
  2. 模拟逻辑错误:
    • ctypes代码中反复按空格但未持续按住,且按键参数(如dwFlags)设置不符合游戏识别要求。
    • pynput代码中仅短暂按住空格(0.1秒)后立即释放,未实现"持续按住"的需求,同时Timer嵌套可能导致逻辑混乱。
  3. 权限与焦点问题:脚本未以管理员权限运行,或游戏窗口未处于前台焦点,导致输入无法被接收。

修复方案

前置准备

  • 以管理员身份运行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:34:55