From 5dc7c09cb7714abef97df6f00478a1dc1c651291 Mon Sep 17 00:00:00 2001 From: Adam Saudagar Date: Tue, 7 Mar 2023 00:37:26 +0530 Subject: [PATCH] created interface for sslib and choose one using config --- fishy/constants.py | 2 + fishy/engine/common/screenshot.py | 90 ++++++++++++++++++++++++++++ fishy/engine/common/window_server.py | 21 ++----- fishy/helper/depless.py | 13 ++++ requirements.txt | 1 + 5 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 fishy/engine/common/screenshot.py diff --git a/fishy/constants.py b/fishy/constants.py index df6d93e..6b96b6c 100644 --- a/fishy/constants.py +++ b/fishy/constants.py @@ -10,3 +10,5 @@ libgps = ("LibGPS", "https://cdn.esoui.com/downloads/file601/LibGPS_3_2_0.zip", libmapping = ("LibMapPing", "https://cdn.esoui.com/downloads/file1302/LibMapPing_2_0_0.zip", 200) libdl = ("LibDebugLogger", "https://cdn.esoui.com/downloads/file2275/LibDebugLogger_2_4_1.zip", 241) libchatmsg = ("LibChatMessage", "https://cdn.esoui.com/downloads/file2382/LibChatMessage_1_2_0.zip", 120) + +d3dshot_git = "git+https://github.com/fauskanger/D3DShot.git#egg=D3DShot" diff --git a/fishy/engine/common/screenshot.py b/fishy/engine/common/screenshot.py new file mode 100644 index 0000000..0121739 --- /dev/null +++ b/fishy/engine/common/screenshot.py @@ -0,0 +1,90 @@ +import logging +import subprocess +import sys +from abc import ABC, abstractmethod +from typing import Optional + +import numpy as np +from numpy import ndarray + +from fishy import constants +from fishy.helper.config import config +from fishy.helper.depless import singleton_proxy, install_and_import +from fishy.osservices.os_services import os_services + + +class IScreenShot(ABC): + @abstractmethod + def setup(self) -> bool: + ... + + @abstractmethod + def grab(self) -> ndarray: + ... + + +def get_monitor_id(monitors_iterator, get_top_left) -> Optional[int]: + monitor_rect = os_services.get_monitor_rect() + if monitor_rect is None: + logging.error("Game window not found") + return None + + for i, m in enumerate(monitors_iterator): + top, left = get_top_left(m) + if top == monitor_rect[0] and left == monitor_rect[1]: + return i + + return None + + +class MSS(IScreenShot): + def __init__(self): + from mss import mss + self.monitor_id = None + self.sct = mss() + + def setup(self) -> bool: + self.monitor_id = get_monitor_id(self.sct.monitors, lambda m: (m["top"], m["left"])) + return self.monitor_id is not None + + # noinspection PyTypeChecker + def grab(self) -> ndarray: + sct_img = self.sct.grab(self.sct.monitors[self.monitor_id]) + return np.array(sct_img) + + +class PyAutoGUI(IScreenShot): + def setup(self) -> bool: + return True + + def grab(self) -> ndarray: + import pyautogui + image = pyautogui.screenshot(all_screens=True) + return np.array(image) + + +class D3DShot(IScreenShot): + # noinspection PyPackageRequirements + def __init__(self): + for i in range(2): + install_and_import("d3dshot", constants.d3dshot_git) + # noinspection PyUnresolvedReferences + self.d3 = d3dshot.create(capture_output="numpy") + + def setup(self) -> bool: + monitor_id = get_monitor_id(self.d3.display, lambda m: (m.top, m.left)) + if monitor_id is None: + return False + + self.d3.display = self.d3.displays[monitor_id] + return True + + def grab(self) -> ndarray: + return self.d3.screenshot() + + +LIBS = [MSS, PyAutoGUI, D3DShot] + + +def create() -> IScreenShot: + return LIBS[config.get("sslib", 0)]() diff --git a/fishy/engine/common/window_server.py b/fishy/engine/common/window_server.py index ec043ab..f7ee27a 100644 --- a/fishy/engine/common/window_server.py +++ b/fishy/engine/common/window_server.py @@ -3,10 +3,9 @@ from enum import Enum from threading import Thread import numpy as np - -from mss import mss from mss.base import MSSBase +from fishy.engine.common import screenshot from fishy.helper.helper import print_exc from fishy.osservices.os_services import os_services @@ -24,10 +23,8 @@ class WindowServer: Screen: np.ndarray = None windowOffset = None status = Status.STOPPED - sct: MSSBase = None - + sslib = None crop = None - monitor_id = -1 def init(): @@ -35,26 +32,18 @@ def init(): Executed once before the main loop, Finds the game window, and calculates the offset to remove the title bar """ + WindowServer.sslib = screenshot.create() WindowServer.status = Status.RUNNING - WindowServer.sct = mss() - WindowServer.crop = os_services.get_game_window_rect() - monitor_rect = os_services.get_monitor_rect() - if monitor_rect is None or WindowServer.crop is None: + if WindowServer.crop is None or not WindowServer.sslib.setup(): logging.error("Game window not found") WindowServer.status = Status.CRASHED return - for i, m in enumerate(WindowServer.sct.monitors): - if m["top"] == monitor_rect[0] and m["left"] == monitor_rect[1]: - WindowServer.monitor_id = i - def get_cropped_screenshot(): - sct_img = WindowServer.sct.grab(WindowServer.sct.monitors[WindowServer.monitor_id]) - # noinspection PyTypeChecker - ss = np.array(sct_img) + ss = WindowServer.sslib.grab() crop = WindowServer.crop cropped_ss = ss[crop[1]:crop[3], crop[0]:crop[2]] if cropped_ss.size == 0: diff --git a/fishy/helper/depless.py b/fishy/helper/depless.py index d29a054..1fb866a 100644 --- a/fishy/helper/depless.py +++ b/fishy/helper/depless.py @@ -1,6 +1,7 @@ """ no imports from fishy itself here, or anything which depends on fishy """ +import importlib def singleton_proxy(instance_name): @@ -18,3 +19,15 @@ def singleton_proxy(instance_name): return NewClass return decorator + + +def install_and_import(package, package_url=None): + try: + importlib.import_module(package) + except ImportError: + import pip + if package_url is None: + package_url = package + pip.main(['install', package_url]) + finally: + globals()[package] = importlib.import_module(package) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3a01449..69b0fbc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,5 @@ keyboard playsound event-scheduler mouse +pyautogui mss \ No newline at end of file