Find D2R window using win32api instead of pattern matching (#453)

This commit is contained in:
Vladimir Makaev
2022-01-18 20:32:35 +01:00
committed by GitHub
parent c939877c7b
commit b890ff266f
4 changed files with 63 additions and 24 deletions
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 = []