Find D2R window using win32api instead of pattern matching (#453)
This commit is contained in:
@@ -19,6 +19,7 @@ discord_status_condensed=0
|
||||
info_screenshots=1
|
||||
loot_screenshots=0
|
||||
saved_games_folder=
|
||||
find_window_via_win32_api=0
|
||||
d2r_path=C:\Program Files (x86)\Diablo II Resurrected\D2R.exe
|
||||
; if you set this field to 1, botty will save the top-most visible character template just after
|
||||
; the online/offline tab and upon restart will attempt to find the same template to select the char
|
||||
|
||||
+2
-1
@@ -112,7 +112,8 @@ class Config:
|
||||
"info_screenshots": bool(int(Config._select_val("general", "info_screenshots"))),
|
||||
"loot_screenshots": bool(int(Config._select_val("general", "loot_screenshots"))),
|
||||
"d2r_path": Config._select_val("general", "d2r_path"),
|
||||
"restart_d2r_when_stuck": bool(int(Config._select_val("general", "restart_d2r_when_stuck")))
|
||||
"restart_d2r_when_stuck": bool(int(Config._select_val("general", "restart_d2r_when_stuck"))),
|
||||
"find_window_via_win32_api": bool(int(Config._select_val("general", "find_window_via_win32_api")))
|
||||
}
|
||||
|
||||
# Added for dclone ip hunting
|
||||
|
||||
+45
-21
@@ -5,6 +5,7 @@ import time
|
||||
from logger import Logger
|
||||
from typing import Tuple
|
||||
from config import Config
|
||||
from utils import misc
|
||||
from utils.misc import load_template
|
||||
import os
|
||||
|
||||
@@ -24,12 +25,42 @@ class Screen:
|
||||
self._config = Config()
|
||||
self._monitor_roi = self._sct.monitors[monitor_idx]
|
||||
# auto find offests
|
||||
self.found_offsets = False
|
||||
position = None
|
||||
if self._config.general["find_window_via_win32_api"] :
|
||||
Logger.debug("Using WinAPI to search for window under D2R.exe process")
|
||||
position = self.find_window_via_winapi()
|
||||
if position is None:
|
||||
Logger.debug("Can't find any window owned by D2R.exe falling back to matching via assets. Make sure D2R is in focus and you are on the hero selection screen")
|
||||
|
||||
if position is None:
|
||||
position = self.find_window_via_assets(wait)
|
||||
|
||||
if position is not None:
|
||||
self._set_window_position(*position)
|
||||
else:
|
||||
if self._config.general["info_screenshots"]:
|
||||
cv2.imwrite("./info_screenshots/error_d2r_window_not_found_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self.grab())
|
||||
Logger.error("Could not find hero selection or template for ingame, shutting down")
|
||||
Logger.error("Could not determine window offset. Please make sure you have the D2R window " +
|
||||
f"focused and that you are on the hero selection screen when pressing {self._config.general['resume_key']}")
|
||||
|
||||
def _set_window_position(self, offset_x: int, offset_y: int):
|
||||
Logger.debug(f"Set offsets: left {offset_x}px, top {offset_y}px")
|
||||
self._monitor_roi["top"] += offset_y
|
||||
self._monitor_roi["left"] += offset_x
|
||||
self._monitor_x_range = (self._monitor_roi["left"] + 10, self._monitor_roi["left"] + self._monitor_roi["width"] - 10)
|
||||
self._monitor_y_range = (self._monitor_roi["top"] + 10, self._monitor_roi["top"] + self._monitor_roi["height"] - 10)
|
||||
self._monitor_roi["width"] = self._config.ui_pos["screen_width"]
|
||||
self._monitor_roi["height"] = self._config.ui_pos["screen_height"]
|
||||
self.found_offsets = True
|
||||
|
||||
|
||||
def find_window_via_assets(self, wait: int) -> Tuple[int, int]:
|
||||
template = load_template(f"assets/templates/main_menu_top_left.png", 1.0)
|
||||
template_ingame = load_template(f"assets/templates/window_ingame_offset_reference.png", 1.0)
|
||||
start = time.time()
|
||||
self.found_offsets = False
|
||||
Logger.info("Searching for window offsets. Make sure D2R is in focus and you are on the hero selection screen")
|
||||
debug_max_val = 0
|
||||
start = time.time()
|
||||
while time.time() - start < wait:
|
||||
img = self.grab()
|
||||
self._sct = mss()
|
||||
@@ -53,25 +84,18 @@ class Screen:
|
||||
if max_val < 0.93:
|
||||
Logger.warning(f"Your template match score to calc corner was lower then usual ({max_val*100:.1f}% confidence). " +
|
||||
"You might run into template matching issues along the way!")
|
||||
offset_left, offset_top = max_pos
|
||||
Logger.debug(f"Set offsets: left {offset_left}px, top {offset_top}px")
|
||||
self._monitor_roi["top"] += offset_top
|
||||
self._monitor_roi["left"] += offset_left
|
||||
self._monitor_x_range = (self._monitor_roi["left"] + 10, self._monitor_roi["left"] + self._monitor_roi["width"] - 10)
|
||||
self._monitor_y_range = (self._monitor_roi["top"] + 10, self._monitor_roi["top"] + self._monitor_roi["height"] - 10)
|
||||
self._monitor_roi["width"] = self._config.ui_pos["screen_width"]
|
||||
self._monitor_roi["height"] = self._config.ui_pos["screen_height"]
|
||||
self.found_offsets = True
|
||||
break
|
||||
if not self.found_offsets:
|
||||
if self._config.general["info_screenshots"]:
|
||||
cv2.imwrite("./info_screenshots/error_d2r_window_not_found_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self.grab())
|
||||
Logger.error("Could not find hero selection or template for ingame, shutting down")
|
||||
Logger.error(f"The max score that could be found was: ({debug_max_val*100:.1f}% confidence)")
|
||||
Logger.error("Could not determine window offset. Please make sure you have the D2R window " +
|
||||
f"focused and that you are on the hero selection screen when pressing {self._config.general['resume_key']}")
|
||||
|
||||
return max_pos
|
||||
Logger.error(f"The max score that could be found was: ({debug_max_val*100:.1f}% confidence)")
|
||||
return None
|
||||
|
||||
def find_window_via_winapi(self) -> Tuple[int, int]:
|
||||
position = misc.find_d2r_window()
|
||||
if position is None:
|
||||
return None
|
||||
|
||||
offset_x, offset_y, _, _ = position
|
||||
return offset_x, offset_y
|
||||
|
||||
def convert_monitor_to_screen(self, screen_coord: Tuple[float, float]) -> Tuple[float, float]:
|
||||
return (screen_coord[0] - self._monitor_roi["left"], screen_coord[1] - self._monitor_roi["top"])
|
||||
|
||||
|
||||
+15
-2
@@ -11,12 +11,25 @@ import os
|
||||
from math import cos, sin, dist
|
||||
import subprocess
|
||||
from win32con import HWND_TOPMOST, SWP_NOMOVE, SWP_NOSIZE, HWND_TOP, HWND_BOTTOM, SWP_NOZORDER, SWP_NOOWNERZORDER, HWND_DESKTOP, SWP_NOSENDCHANGING, SWP_SHOWWINDOW, HWND_NOTOPMOST
|
||||
from win32gui import GetWindowText, SetWindowPos, EnumWindows
|
||||
|
||||
from win32gui import GetWindowText, SetWindowPos, EnumWindows, GetClientRect, ClientToScreen
|
||||
from win32process import GetWindowThreadProcessId
|
||||
import psutil
|
||||
|
||||
def close_down_d2():
|
||||
subprocess.call(["taskkill","/F","/IM","D2R.exe"], stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def find_d2r_window():
|
||||
if os.name == 'nt':
|
||||
window_list = []
|
||||
EnumWindows(lambda w, l: l.append((w, *GetWindowThreadProcessId(w))), window_list)
|
||||
for (hwnd, _, process_id) in window_list:
|
||||
if psutil.Process(process_id).name() == "D2R.exe":
|
||||
left, top, right, bottom = GetClientRect(hwnd)
|
||||
(left, top), (right, bottom) = ClientToScreen(hwnd, (left, top)), ClientToScreen(hwnd, (right, bottom))
|
||||
return (left, top, right, bottom)
|
||||
return None
|
||||
|
||||
def set_d2r_always_on_top():
|
||||
if os.name == 'nt':
|
||||
windows_list = []
|
||||
|
||||
Reference in New Issue
Block a user