Add Ghostbox hardware and logistics automation
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
@@ -20,35 +21,489 @@ else:
|
||||
|
||||
|
||||
MAIN_CONFIG_FILE = "wow_multikey_qt.json"
|
||||
BACKEND_TIANYA = "tianya"
|
||||
BACKEND_GHOST = "ghost"
|
||||
BACKEND_DIRECTINPUT = "directinput"
|
||||
TIANYA_DLL_PATH = "ddl/wyhkm.dll"
|
||||
GHOSTBOX_MODULE_PATH = "Ghostbox_ddl/ghostbox.py"
|
||||
|
||||
|
||||
class _TianyaBackend:
|
||||
name = BACKEND_TIANYA
|
||||
|
||||
def __init__(self, controller, dll_path=TIANYA_DLL_PATH):
|
||||
self._controller = controller
|
||||
self._dll_path_setting = dll_path
|
||||
self._wyhkm = None
|
||||
self.dll_path = None
|
||||
|
||||
def configure(self, dll_path=None):
|
||||
if dll_path:
|
||||
self._dll_path_setting = dll_path
|
||||
|
||||
def label(self):
|
||||
return self.name if self.is_available() else f"{self.name}_unavailable"
|
||||
|
||||
def is_available(self):
|
||||
if self._wyhkm:
|
||||
try:
|
||||
if self._wyhkm.IsOpen(0):
|
||||
return True
|
||||
except Exception:
|
||||
self._wyhkm = None
|
||||
|
||||
self.dll_path = self._controller._resolve_resource_path(self._dll_path_setting)
|
||||
if not self.dll_path:
|
||||
self._controller._log(f">>> [tianya_control] DLL not found: {self._dll_path_setting}")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
try:
|
||||
hkmdll = windll.LoadLibrary(self.dll_path)
|
||||
hkmdll.DllInstall.argtypes = (c_long, c_longlong)
|
||||
|
||||
if hkmdll.DllInstall(1, 2) < 0:
|
||||
self._controller._log(">>> [tianya_control] DllInstall failed")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
pythoncom.CoInitialize()
|
||||
self._wyhkm = win32com.client.Dispatch("wyp.hkm")
|
||||
|
||||
dev_id = self._wyhkm.SearchDevice(0x2612, 0x1701, 0)
|
||||
if dev_id == -1:
|
||||
self._controller._log(">>> [tianya_control] Device not found (Index 0)")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
if not self._wyhkm.Open(dev_id, 0):
|
||||
self._controller._log(">>> [tianya_control] Open device failed")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
self._wyhkm.SetMode(1, 1)
|
||||
self._wyhkm.SetMode(2, 1)
|
||||
self._wyhkm.SetKeyInterval(30, 50)
|
||||
self._wyhkm.SetMouseInterval(30, 50)
|
||||
|
||||
self._controller._log(f">>> [tianya_control] Device opened and initialized: {dev_id}")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] Init failed: {exc}")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
def _normalize_key(self, key_str):
|
||||
return str(key_str).strip().upper()
|
||||
|
||||
def delay_rnd(self, min_ms, max_ms):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
self._wyhkm.DelayRnd(int(min_ms), int(max_ms))
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def key_down(self, key_str):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
self._wyhkm.KeyDown(self._normalize_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] KeyDown failed ({key_str}): {exc}")
|
||||
return False
|
||||
|
||||
def key_up(self, key_str):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
self._wyhkm.KeyUp(self._normalize_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] KeyUp failed ({key_str}): {exc}")
|
||||
return False
|
||||
|
||||
def key_press(self, key_str):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
self._wyhkm.KeyPress(self._normalize_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] KeyPress failed ({key_str}): {exc}")
|
||||
return False
|
||||
|
||||
def move_to(self, x, y):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
return bool(self._wyhkm.MoveTo(int(x), int(y)))
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] MoveTo failed ({x}, {y}): {exc}")
|
||||
return False
|
||||
|
||||
def move_r(self, dx, dy):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
return bool(self._wyhkm.MoveR(int(dx), int(dy)))
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] MoveR failed ({dx}, {dy}): {exc}")
|
||||
return False
|
||||
|
||||
def left_click(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
return bool(self._wyhkm.LeftClick())
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] LeftClick failed: {exc}")
|
||||
return False
|
||||
|
||||
def right_click(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
return bool(self._wyhkm.RightClick())
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] RightClick failed: {exc}")
|
||||
return False
|
||||
|
||||
def left_down(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
return bool(self._wyhkm.LeftDown())
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] LeftDown failed: {exc}")
|
||||
return False
|
||||
|
||||
def left_up(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
return bool(self._wyhkm.LeftUp())
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [tianya_control] LeftUp failed: {exc}")
|
||||
return False
|
||||
|
||||
|
||||
class _GhostboxBackend:
|
||||
name = BACKEND_GHOST
|
||||
LEFT_BUTTON = 1
|
||||
RIGHT_BUTTON = 2
|
||||
|
||||
def __init__(self, controller, module_path=GHOSTBOX_MODULE_PATH):
|
||||
self._controller = controller
|
||||
self._module_path_setting = module_path
|
||||
self._module = None
|
||||
self.module_path = None
|
||||
|
||||
def configure(self, module_path=None):
|
||||
if module_path and module_path != self._module_path_setting:
|
||||
self._module_path_setting = module_path
|
||||
self._module = None
|
||||
|
||||
def label(self):
|
||||
return self.name if self.is_available() else f"{self.name}_unavailable"
|
||||
|
||||
def _load_module(self):
|
||||
if self._module is not None:
|
||||
return self._module
|
||||
|
||||
self.module_path = self._controller._resolve_resource_path(self._module_path_setting)
|
||||
if not self.module_path:
|
||||
self._controller._log(f">>> [ghost_control] module not found: {self._module_path_setting}")
|
||||
return None
|
||||
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location("ghostbox_runtime", self.module_path)
|
||||
if spec is None or spec.loader is None:
|
||||
self._controller._log(f">>> [ghost_control] load spec failed: {self.module_path}")
|
||||
return None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
self._module = module
|
||||
return module
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [ghost_control] load failed: {exc}")
|
||||
self._module = None
|
||||
return None
|
||||
|
||||
def is_available(self):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
if gb.isconnected():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
result = gb.opendevice()
|
||||
connected = bool(gb.isconnected())
|
||||
if not connected:
|
||||
self._controller._log(f">>> [ghost_control] Open device failed: result={result}")
|
||||
return False
|
||||
|
||||
try:
|
||||
gb.setpresskeydelay(30, 50)
|
||||
gb.setpressmousebuttondelay(30, 50)
|
||||
gb.setmousemovementdelay(3, 5)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._controller._log(">>> [ghost_control] Device opened and initialized")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [ghost_control] Init failed: {exc}")
|
||||
return False
|
||||
|
||||
def _normalize_key(self, key_str):
|
||||
return str(key_str).strip().lower()
|
||||
|
||||
def _call(self, action, func, *args):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
func(*args)
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [ghost_control] {action} failed: {exc}")
|
||||
return False
|
||||
|
||||
def delay_rnd(self, min_ms, max_ms):
|
||||
low = min(int(min_ms), int(max_ms))
|
||||
high = max(int(min_ms), int(max_ms))
|
||||
time.sleep(random.uniform(low, high) / 1000.0)
|
||||
return True
|
||||
|
||||
def key_down(self, key_str):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call(f"KeyDown({key_str})", gb.presskeybyname, self._normalize_key(key_str))
|
||||
|
||||
def key_up(self, key_str):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call(f"KeyUp({key_str})", gb.releasekeybyname, self._normalize_key(key_str))
|
||||
|
||||
def key_press(self, key_str):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call(f"KeyPress({key_str})", gb.pressandreleasekeybyname, self._normalize_key(key_str))
|
||||
|
||||
def move_to(self, x, y):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call(f"MoveTo({x}, {y})", gb.movemouseto, int(x), int(y))
|
||||
|
||||
def move_r(self, dx, dy):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call(f"MoveR({dx}, {dy})", gb.movemouserelative, int(dx), int(dy))
|
||||
|
||||
def left_click(self):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call("LeftClick", gb.pressandreleasemousebutton, self.LEFT_BUTTON)
|
||||
|
||||
def right_click(self):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call("RightClick", gb.pressandreleasemousebutton, self.RIGHT_BUTTON)
|
||||
|
||||
def left_down(self):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call("LeftDown", gb.pressmousebutton, self.LEFT_BUTTON)
|
||||
|
||||
def left_up(self):
|
||||
gb = self._load_module()
|
||||
if gb is None:
|
||||
return False
|
||||
return self._call("LeftUp", gb.releasemousebutton, self.LEFT_BUTTON)
|
||||
|
||||
|
||||
class _DirectInputBackend:
|
||||
name = BACKEND_DIRECTINPUT
|
||||
|
||||
def __init__(self, controller):
|
||||
self._controller = controller
|
||||
self._last_error = None
|
||||
|
||||
def label(self):
|
||||
return self.name if self.is_available() else f"{self.name}_unavailable"
|
||||
|
||||
def is_available(self):
|
||||
if pydirectinput is not None:
|
||||
return True
|
||||
if self._last_error != _PYDIRECTINPUT_IMPORT_ERROR:
|
||||
self._last_error = _PYDIRECTINPUT_IMPORT_ERROR
|
||||
self._controller._log(f">>> [input_control] pydirectinput unavailable: {_PYDIRECTINPUT_IMPORT_ERROR}")
|
||||
return False
|
||||
|
||||
def _normalize_key(self, key_str):
|
||||
return str(key_str).strip().lower()
|
||||
|
||||
def delay_rnd(self, min_ms, max_ms):
|
||||
if not self.is_available():
|
||||
return False
|
||||
low = min(int(min_ms), int(max_ms))
|
||||
high = max(int(min_ms), int(max_ms))
|
||||
time.sleep(random.uniform(low, high) / 1000.0)
|
||||
return True
|
||||
|
||||
def key_down(self, key_str):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.keyDown(self._normalize_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] keyDown failed ({key_str}): {exc}")
|
||||
return False
|
||||
|
||||
def key_up(self, key_str):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.keyUp(self._normalize_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] keyUp failed ({key_str}): {exc}")
|
||||
return False
|
||||
|
||||
def key_press(self, key_str):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.press(self._normalize_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] press failed ({key_str}): {exc}")
|
||||
return False
|
||||
|
||||
def move_to(self, x, y):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.moveTo(int(x), int(y))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] moveTo failed ({x}, {y}): {exc}")
|
||||
return False
|
||||
|
||||
def move_r(self, dx, dy):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.moveRel(int(dx), int(dy))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] moveRel failed ({dx}, {dy}): {exc}")
|
||||
return False
|
||||
|
||||
def left_click(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.click(button="left")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] left click failed: {exc}")
|
||||
return False
|
||||
|
||||
def right_click(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.click(button="right")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] right click failed: {exc}")
|
||||
return False
|
||||
|
||||
def left_down(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.mouseDown(button="left")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] left mouseDown failed: {exc}")
|
||||
return False
|
||||
|
||||
def left_up(self):
|
||||
if not self.is_available():
|
||||
return False
|
||||
try:
|
||||
pydirectinput.mouseUp(button="left")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._controller._log(f">>> [directinput] left mouseUp failed: {exc}")
|
||||
return False
|
||||
|
||||
|
||||
class HardwareController:
|
||||
_instance = None
|
||||
_wyhkm = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not cls._instance:
|
||||
cls._instance = super(HardwareController, cls).__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, dll_path="ddl/wyhkm.dll", use_hardware_input=None):
|
||||
def __init__(
|
||||
self,
|
||||
dll_path=TIANYA_DLL_PATH,
|
||||
use_hardware_input=None,
|
||||
use_tianya_box=None,
|
||||
use_ghost_box=None,
|
||||
ghostbox_module_path=GHOSTBOX_MODULE_PATH,
|
||||
backend=None,
|
||||
):
|
||||
if getattr(self, "_bootstrapped", False):
|
||||
if dll_path:
|
||||
self._dll_path_setting = dll_path
|
||||
if use_hardware_input is not None:
|
||||
self.configure(use_hardware_input=use_hardware_input, dll_path=dll_path)
|
||||
self._tianya_backend.configure(dll_path=dll_path)
|
||||
self._ghost_backend.configure(module_path=ghostbox_module_path)
|
||||
if backend is not None or use_hardware_input is not None or use_tianya_box is not None or use_ghost_box is not None:
|
||||
self.configure(
|
||||
use_hardware_input=use_hardware_input,
|
||||
use_tianya_box=use_tianya_box,
|
||||
use_ghost_box=use_ghost_box,
|
||||
dll_path=dll_path,
|
||||
ghostbox_module_path=ghostbox_module_path,
|
||||
backend=backend,
|
||||
)
|
||||
return
|
||||
|
||||
self._bootstrapped = True
|
||||
self._dll_path_setting = dll_path
|
||||
self._backend = "hardware"
|
||||
self._backend = BACKEND_TIANYA
|
||||
self._last_backend_log = None
|
||||
self._last_directinput_error = None
|
||||
self.dll_path = None
|
||||
self._tianya_backend = _TianyaBackend(self, dll_path)
|
||||
self._ghost_backend = _GhostboxBackend(self, ghostbox_module_path)
|
||||
self._directinput_backend = _DirectInputBackend(self)
|
||||
|
||||
if use_hardware_input is None:
|
||||
use_hardware_input = self._load_backend_preference(default=True)
|
||||
self.configure(use_hardware_input=use_hardware_input, dll_path=dll_path)
|
||||
if backend is None and use_hardware_input is None and use_tianya_box is None and use_ghost_box is None:
|
||||
use_tianya_box, use_ghost_box = self._load_backend_preference(default_tianya=True)
|
||||
self.configure(
|
||||
use_hardware_input=use_hardware_input,
|
||||
use_tianya_box=use_tianya_box,
|
||||
use_ghost_box=use_ghost_box,
|
||||
backend=backend,
|
||||
)
|
||||
|
||||
def _runtime_base_dir(self):
|
||||
if getattr(sys, "frozen", False):
|
||||
@@ -100,18 +555,21 @@ class HardwareController:
|
||||
return candidate
|
||||
return os.path.join(self._runtime_base_dir(), MAIN_CONFIG_FILE)
|
||||
|
||||
def _load_backend_preference(self, default=True):
|
||||
def _load_backend_preference(self, default_tianya=True):
|
||||
config_path = self._config_path()
|
||||
if not os.path.exists(config_path):
|
||||
return default
|
||||
return default_tianya, False
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
bot_cfg = (cfg or {}).get("bot") or {}
|
||||
return bool(bot_cfg.get("use_hardware_input", default))
|
||||
has_new_flags = "use_tianya_box" in bot_cfg or "use_ghost_box" in bot_cfg
|
||||
if has_new_flags:
|
||||
return bool(bot_cfg.get("use_tianya_box", False)), bool(bot_cfg.get("use_ghost_box", False))
|
||||
return bool(bot_cfg.get("use_hardware_input", default_tianya)), False
|
||||
except Exception as exc:
|
||||
self._log(f">>> [input_control] load config failed: {exc}")
|
||||
return default
|
||||
return default_tianya, False
|
||||
|
||||
def _log(self, message):
|
||||
print(message)
|
||||
@@ -122,68 +580,28 @@ class HardwareController:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _normalize_hardware_key(self, key_str):
|
||||
return str(key_str).strip().upper()
|
||||
def _normalize_backend(self, backend):
|
||||
if backend in (BACKEND_TIANYA, "hardware"):
|
||||
return BACKEND_TIANYA
|
||||
if backend == BACKEND_GHOST:
|
||||
return BACKEND_GHOST
|
||||
if backend == BACKEND_DIRECTINPUT:
|
||||
return BACKEND_DIRECTINPUT
|
||||
return BACKEND_DIRECTINPUT
|
||||
|
||||
def _normalize_directinput_key(self, key_str):
|
||||
return str(key_str).strip().lower()
|
||||
def _select_backend(self, use_tianya_box, use_ghost_box):
|
||||
if bool(use_tianya_box):
|
||||
return BACKEND_TIANYA
|
||||
if bool(use_ghost_box):
|
||||
return BACKEND_GHOST
|
||||
return BACKEND_DIRECTINPUT
|
||||
|
||||
def _directinput_available(self):
|
||||
if pydirectinput is not None:
|
||||
return True
|
||||
if self._last_directinput_error != _PYDIRECTINPUT_IMPORT_ERROR:
|
||||
self._last_directinput_error = _PYDIRECTINPUT_IMPORT_ERROR
|
||||
self._log(f">>> [input_control] pydirectinput unavailable: {_PYDIRECTINPUT_IMPORT_ERROR}")
|
||||
return False
|
||||
|
||||
def _ensure_hardware_ready(self):
|
||||
if self._wyhkm:
|
||||
try:
|
||||
if self._wyhkm.IsOpen(0):
|
||||
return True
|
||||
except Exception:
|
||||
self._wyhkm = None
|
||||
|
||||
self.dll_path = self._resolve_resource_path(self._dll_path_setting)
|
||||
if not self.dll_path:
|
||||
self._log(f">>> [hardware_control] DLL not found: {self._dll_path_setting}")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
try:
|
||||
hkmdll = windll.LoadLibrary(self.dll_path)
|
||||
hkmdll.DllInstall.argtypes = (c_long, c_longlong)
|
||||
|
||||
if hkmdll.DllInstall(1, 2) < 0:
|
||||
self._log(">>> [hardware_control] DllInstall failed")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
pythoncom.CoInitialize()
|
||||
self._wyhkm = win32com.client.Dispatch("wyp.hkm")
|
||||
|
||||
dev_id = self._wyhkm.SearchDevice(0x2612, 0x1701, 0)
|
||||
if dev_id == -1:
|
||||
self._log(">>> [hardware_control] Device not found (Index 0)")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
if not self._wyhkm.Open(dev_id, 0):
|
||||
self._log(">>> [hardware_control] Open device failed")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
|
||||
self._wyhkm.SetMode(1, 1)
|
||||
self._wyhkm.SetMode(2, 1)
|
||||
self._wyhkm.SetKeyInterval(30, 50)
|
||||
self._wyhkm.SetMouseInterval(30, 50)
|
||||
|
||||
self._log(f">>> [hardware_control] Device opened and initialized: {dev_id}")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] Init failed: {exc}")
|
||||
self._wyhkm = None
|
||||
return False
|
||||
def _current_backend(self):
|
||||
if self._backend == BACKEND_TIANYA:
|
||||
return self._tianya_backend
|
||||
if self._backend == BACKEND_GHOST:
|
||||
return self._ghost_backend
|
||||
return self._directinput_backend
|
||||
|
||||
def _log_backend(self):
|
||||
label = self.backend_label()
|
||||
@@ -191,92 +609,62 @@ class HardwareController:
|
||||
self._last_backend_log = label
|
||||
self._log(f">>> [input_control] backend={label}")
|
||||
|
||||
def configure(self, use_hardware_input=True, dll_path=None):
|
||||
if dll_path:
|
||||
self._dll_path_setting = dll_path
|
||||
self._backend = "hardware" if bool(use_hardware_input) else "directinput"
|
||||
if self._backend == "hardware":
|
||||
self._ensure_hardware_ready()
|
||||
def configure(
|
||||
self,
|
||||
use_hardware_input=None,
|
||||
use_tianya_box=None,
|
||||
use_ghost_box=None,
|
||||
dll_path=None,
|
||||
ghostbox_module_path=None,
|
||||
backend=None,
|
||||
):
|
||||
self._tianya_backend.configure(dll_path=dll_path)
|
||||
self._ghost_backend.configure(module_path=ghostbox_module_path)
|
||||
|
||||
if backend is not None:
|
||||
self._backend = self._normalize_backend(backend)
|
||||
else:
|
||||
self._directinput_available()
|
||||
if use_tianya_box is None and use_ghost_box is None:
|
||||
if use_hardware_input is None:
|
||||
use_tianya_box, use_ghost_box = self._load_backend_preference(default_tianya=True)
|
||||
else:
|
||||
use_tianya_box, use_ghost_box = bool(use_hardware_input), False
|
||||
else:
|
||||
use_tianya_box = bool(use_tianya_box)
|
||||
use_ghost_box = bool(use_ghost_box)
|
||||
if use_hardware_input is not None and not bool(use_hardware_input):
|
||||
use_tianya_box, use_ghost_box = False, False
|
||||
self._backend = self._select_backend(use_tianya_box, use_ghost_box)
|
||||
|
||||
self._log_backend()
|
||||
return self._backend
|
||||
|
||||
def uses_hardware_input(self):
|
||||
return self._backend == "hardware"
|
||||
return self._backend in (BACKEND_TIANYA, BACKEND_GHOST)
|
||||
|
||||
def uses_tianya_box(self):
|
||||
return self._backend == BACKEND_TIANYA
|
||||
|
||||
def uses_ghost_box(self):
|
||||
return self._backend == BACKEND_GHOST
|
||||
|
||||
def backend_label(self):
|
||||
if self.uses_hardware_input():
|
||||
return "hardware" if self._ensure_hardware_ready() else "hardware_unavailable"
|
||||
return "directinput" if self._directinput_available() else "directinput_unavailable"
|
||||
return self._current_backend().label()
|
||||
|
||||
def is_available(self):
|
||||
if self.uses_hardware_input():
|
||||
return self._ensure_hardware_ready()
|
||||
return self._directinput_available()
|
||||
return self._current_backend().is_available()
|
||||
|
||||
def delay_rnd(self, min_ms, max_ms):
|
||||
if self.uses_hardware_input() and self.is_available():
|
||||
try:
|
||||
self._wyhkm.DelayRnd(int(min_ms), int(max_ms))
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
if self._directinput_available():
|
||||
low = min(int(min_ms), int(max_ms))
|
||||
high = max(int(min_ms), int(max_ms))
|
||||
time.sleep(random.uniform(low, high) / 1000.0)
|
||||
return self._current_backend().delay_rnd(min_ms, max_ms)
|
||||
|
||||
def key_down(self, key_str):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
self._wyhkm.KeyDown(self._normalize_hardware_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] KeyDown failed ({key_str}): {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.keyDown(self._normalize_directinput_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] keyDown failed ({key_str}): {exc}")
|
||||
return False
|
||||
return self._current_backend().key_down(key_str)
|
||||
|
||||
def key_up(self, key_str):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
self._wyhkm.KeyUp(self._normalize_hardware_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] KeyUp failed ({key_str}): {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.keyUp(self._normalize_directinput_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] keyUp failed ({key_str}): {exc}")
|
||||
return False
|
||||
return self._current_backend().key_up(key_str)
|
||||
|
||||
def key_press(self, key_str):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
self._wyhkm.KeyPress(self._normalize_hardware_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] KeyPress failed ({key_str}): {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.press(self._normalize_directinput_key(key_str))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] press failed ({key_str}): {exc}")
|
||||
return False
|
||||
return self._current_backend().key_press(key_str)
|
||||
|
||||
def keyDown(self, key_str):
|
||||
return self.key_down(key_str)
|
||||
@@ -288,103 +676,25 @@ class HardwareController:
|
||||
return self.key_press(key_str)
|
||||
|
||||
def move_to(self, x, y):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
return bool(self._wyhkm.MoveTo(int(x), int(y)))
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] MoveTo failed ({x}, {y}): {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.moveTo(int(x), int(y))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] moveTo failed ({x}, {y}): {exc}")
|
||||
return False
|
||||
return self._current_backend().move_to(x, y)
|
||||
|
||||
def move_r(self, dx, dy):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
return bool(self._wyhkm.MoveR(int(dx), int(dy)))
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] MoveR failed ({dx}, {dy}): {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.moveRel(int(dx), int(dy))
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] moveRel failed ({dx}, {dy}): {exc}")
|
||||
return False
|
||||
return self._current_backend().move_r(dx, dy)
|
||||
|
||||
def MoveR(self, dx, dy):
|
||||
return self.move_r(dx, dy)
|
||||
|
||||
def left_click(self):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
return bool(self._wyhkm.LeftClick())
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] LeftClick failed: {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.click(button="left")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] left click failed: {exc}")
|
||||
return False
|
||||
return self._current_backend().left_click()
|
||||
|
||||
def right_click(self):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
return bool(self._wyhkm.RightClick())
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] RightClick failed: {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.click(button="right")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] right click failed: {exc}")
|
||||
return False
|
||||
return self._current_backend().right_click()
|
||||
|
||||
def left_down(self):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
return bool(self._wyhkm.LeftDown())
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] LeftDown failed: {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.mouseDown(button="left")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] left mouseDown failed: {exc}")
|
||||
return False
|
||||
return self._current_backend().left_down()
|
||||
|
||||
def left_up(self):
|
||||
if self.uses_hardware_input():
|
||||
if self.is_available():
|
||||
try:
|
||||
return bool(self._wyhkm.LeftUp())
|
||||
except Exception as exc:
|
||||
self._log(f">>> [hardware_control] LeftUp failed: {exc}")
|
||||
return False
|
||||
if self._directinput_available():
|
||||
try:
|
||||
pydirectinput.mouseUp(button="left")
|
||||
return True
|
||||
except Exception as exc:
|
||||
self._log(f">>> [directinput] left mouseUp failed: {exc}")
|
||||
return False
|
||||
return self._current_backend().left_up()
|
||||
|
||||
|
||||
hw_ctrl = HardwareController()
|
||||
|
||||
Reference in New Issue
Block a user