mirror of
https://github.com/fishyboteso/fishyboteso.git
synced 2024-08-30 18:32:13 +00:00
re structured window into server client model so that multiple engine can use them simultaneously
This commit is contained in:
parent
825ce11ced
commit
315adf9799
@ -8,7 +8,7 @@ import win32gui
|
||||
|
||||
import fishy
|
||||
from fishy import web, helper, gui
|
||||
from fishy.engine.event_handler import EngineEventHandler
|
||||
from fishy.engine.common.event_handler import EngineEventHandler
|
||||
from fishy.gui import GUI, splash
|
||||
from fishy.helper import Config, hotkey
|
||||
|
||||
|
0
fishy/engine/common/__init__.py
Normal file
0
fishy/engine/common/__init__.py
Normal file
105
fishy/engine/common/window.py
Normal file
105
fishy/engine/common/window.py
Normal file
@ -0,0 +1,105 @@
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
import cv2
|
||||
import imutils
|
||||
|
||||
from fishy.engine.common import window_server
|
||||
from fishy.engine.common.window_server import WindowServer, Status
|
||||
from fishy.helper import helper
|
||||
|
||||
|
||||
class WindowClient:
|
||||
clients: List['WindowClient'] = []
|
||||
|
||||
def __init__(self, crop=None, color=None, scale=None, show_name=None):
|
||||
"""
|
||||
create a window instance with these pre process
|
||||
:param crop: [x1,y1,x2,y2] array defining the boundaries to crop
|
||||
:param color: color to use example cv2.COLOR_RGB2HSV
|
||||
:param scale: scaling the window
|
||||
"""
|
||||
self.color = color
|
||||
self.crop = crop
|
||||
self.scale = scale
|
||||
self.show_name = show_name
|
||||
self.showing = False
|
||||
|
||||
if len(WindowClient.clients) == 0:
|
||||
window_server.start()
|
||||
WindowClient.clients.append(self)
|
||||
|
||||
def __del__(self):
|
||||
WindowClient.clients.remove(self)
|
||||
if len(WindowClient.clients) == 0:
|
||||
window_server.stop()
|
||||
|
||||
def get_capture(self):
|
||||
"""
|
||||
copies the recorded screen and then pre processes its
|
||||
:return: game window image
|
||||
"""
|
||||
if WindowServer.status == Status.CRASHED:
|
||||
return None
|
||||
|
||||
if not window_server.screen_ready():
|
||||
logging.info("waiting fors screen...")
|
||||
helper.wait_until(window_server.screen_ready)
|
||||
logging.info("screen ready, continuing...")
|
||||
|
||||
temp_img = WindowServer.Screen
|
||||
|
||||
if temp_img is None:
|
||||
return None
|
||||
|
||||
if self.color is not None:
|
||||
temp_img = cv2.cvtColor(temp_img, self.color)
|
||||
|
||||
if self.crop is not None:
|
||||
temp_img = temp_img[self.crop[1]:self.crop[3], self.crop[0]:self.crop[2]]
|
||||
|
||||
if self.scale is not None:
|
||||
temp_img = cv2.resize(temp_img, (self.scale[0], self.scale[1]), interpolation=cv2.INTER_AREA)
|
||||
|
||||
return temp_img
|
||||
|
||||
def processed_image(self, func=None):
|
||||
"""
|
||||
processes the image using the function provided
|
||||
:param func: function to process image
|
||||
:return: processed image
|
||||
"""
|
||||
if WindowServer.status == Status.CRASHED:
|
||||
return None
|
||||
|
||||
if func is None:
|
||||
return self.get_capture()
|
||||
else:
|
||||
return func(self.get_capture())
|
||||
|
||||
def show(self, resize=None, func=None, ready_img=None):
|
||||
"""
|
||||
Displays the processed image for debugging purposes
|
||||
:param ready_img: send ready image, just show the `ready_img` directly
|
||||
:param name: unique name for the image, used to create a new window
|
||||
:param resize: scale the image to make small images more visible
|
||||
:param func: function to process the image
|
||||
"""
|
||||
if WindowServer.status == Status.CRASHED:
|
||||
return
|
||||
|
||||
if not self.show_name:
|
||||
logging.warning("You need to assign a name first")
|
||||
return
|
||||
|
||||
if ready_img is None:
|
||||
img = self.processed_image(func)
|
||||
|
||||
if resize is not None:
|
||||
img = imutils.resize(img, width=resize)
|
||||
else:
|
||||
img = ready_img
|
||||
cv2.imshow(self.show_name, img)
|
||||
|
||||
self.showing = True
|
106
fishy/engine/common/window_server.py
Normal file
106
fishy/engine/common/window_server.py
Normal file
@ -0,0 +1,106 @@
|
||||
import logging
|
||||
from enum import Enum
|
||||
from threading import Thread
|
||||
|
||||
import cv2
|
||||
import math
|
||||
|
||||
import pywintypes
|
||||
import win32gui
|
||||
from win32api import GetSystemMetrics
|
||||
|
||||
import numpy as np
|
||||
from PIL import ImageGrab
|
||||
|
||||
|
||||
|
||||
class Status(Enum):
|
||||
CRASHED = -1,
|
||||
STOPPED = 0,
|
||||
RUNNING = 1
|
||||
|
||||
|
||||
class WindowServer:
|
||||
"""
|
||||
Records the game window, and allows to create instance to process it
|
||||
"""
|
||||
Screen = None
|
||||
windowOffset = None
|
||||
titleOffset = None
|
||||
hwnd = None
|
||||
status = Status.STOPPED
|
||||
|
||||
|
||||
def init(borderless: bool):
|
||||
"""
|
||||
Executed once before the main loop,
|
||||
Finds the game window, and calculates the offset to remove the title bar
|
||||
"""
|
||||
try:
|
||||
WindowServer.hwnd = win32gui.FindWindow(None, "Elder Scrolls Online")
|
||||
rect = win32gui.GetWindowRect(WindowServer.hwnd)
|
||||
client_rect = win32gui.GetClientRect(WindowServer.hwnd)
|
||||
WindowServer.windowOffset = math.floor(((rect[2] - rect[0]) - client_rect[2]) / 2)
|
||||
WindowServer.titleOffset = ((rect[3] - rect[1]) - client_rect[3]) - WindowServer.windowOffset
|
||||
if borderless:
|
||||
WindowServer.titleOffset = 0
|
||||
WindowServer.status = Status.RUNNING
|
||||
except pywintypes.error:
|
||||
logging.error("Game window not found")
|
||||
WindowServer.status = Status.CRASHED
|
||||
|
||||
|
||||
def loop():
|
||||
"""
|
||||
Executed in the start of the main loop
|
||||
finds the game window location and captures it
|
||||
"""
|
||||
bbox = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))
|
||||
|
||||
temp_screen = np.array(ImageGrab.grab(bbox=bbox))
|
||||
|
||||
temp_screen = cv2.cvtColor(temp_screen, cv2.COLOR_BGR2RGB)
|
||||
|
||||
rect = win32gui.GetWindowRect(WindowServer.hwnd)
|
||||
crop = (
|
||||
rect[0] + WindowServer.windowOffset, rect[1] + WindowServer.titleOffset, rect[2] - WindowServer.windowOffset,
|
||||
rect[3] - WindowServer.windowOffset)
|
||||
|
||||
WindowServer.Screen = temp_screen[crop[1]:crop[3], crop[0]:crop[2]]
|
||||
|
||||
if WindowServer.Screen.size == 0:
|
||||
logging.error("Don't minimize or drag game window outside the screen")
|
||||
WindowServer.status = Status.CRASHED
|
||||
|
||||
|
||||
def loop_end():
|
||||
cv2.waitKey(25)
|
||||
|
||||
from fishy.engine.common.window import WindowClient
|
||||
for c in WindowClient.clients:
|
||||
if not c.showing:
|
||||
cv2.destroyWindow(c.show_name)
|
||||
|
||||
|
||||
def run():
|
||||
# todo use config
|
||||
while WindowServer.status == Status.RUNNING:
|
||||
loop()
|
||||
loop_end()
|
||||
|
||||
|
||||
def start():
|
||||
if WindowServer.status == Status.RUNNING:
|
||||
return
|
||||
|
||||
init(False)
|
||||
if WindowServer.status == Status.RUNNING:
|
||||
Thread(target=run).start()
|
||||
|
||||
|
||||
def screen_ready():
|
||||
return WindowServer.Screen is not None or WindowServer.status == Status.CRASHED
|
||||
|
||||
|
||||
def stop():
|
||||
WindowServer.status = Status.STOPPED
|
@ -1,5 +1,4 @@
|
||||
import math
|
||||
from threading import Thread
|
||||
|
||||
import cv2
|
||||
import logging
|
||||
@ -10,31 +9,27 @@ import pywintypes
|
||||
import pytesseract
|
||||
|
||||
from fishy.engine import SemiFisherEngine
|
||||
from fishy.engine.common.window import WindowClient
|
||||
from fishy.engine.semifisher import fishing_event
|
||||
|
||||
from fishy.engine.IEngine import IEngine
|
||||
from fishy.engine.window import Window
|
||||
from fishy.engine.common.IEngine import IEngine
|
||||
from pynput import keyboard, mouse
|
||||
|
||||
from fishy.helper import Config, hotkey
|
||||
from fishy.helper.helper import wait_until
|
||||
from fishy.helper.hotkey import Key
|
||||
|
||||
mse = mouse.Controller()
|
||||
kb = keyboard.Controller()
|
||||
|
||||
|
||||
offset = 10
|
||||
|
||||
|
||||
def sign(x):
|
||||
return -1 if x < 0 else 1
|
||||
|
||||
offset = 10
|
||||
|
||||
def get_crop_coods(window):
|
||||
Window.loop()
|
||||
img = window.get_capture()
|
||||
img = cv2.inRange(img, 0, 1)
|
||||
Window.loop_end()
|
||||
|
||||
cnt, h = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
@ -99,14 +94,7 @@ class FullAuto(IEngine):
|
||||
logging.info("Loading please wait...")
|
||||
self.initalize_keys()
|
||||
|
||||
try:
|
||||
Window.init(False)
|
||||
except pywintypes.error:
|
||||
logging.info("Game window not found")
|
||||
self.toggle_start()
|
||||
return
|
||||
|
||||
self.window = Window(color=cv2.COLOR_RGB2GRAY)
|
||||
self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name="Full auto debug")
|
||||
self.window.crop = get_crop_coods(self.window)
|
||||
self._tesseract_dir = self.config.get("tesseract_dir", None)
|
||||
|
||||
@ -119,12 +107,11 @@ class FullAuto(IEngine):
|
||||
self.gui.bot_started(True)
|
||||
|
||||
while self.start:
|
||||
Window.loop()
|
||||
|
||||
self.window.show("test", func=image_pre_process)
|
||||
Window.loop_end()
|
||||
self.window.show(func=image_pre_process)
|
||||
cv2.waitKey(25)
|
||||
self.gui.bot_started(False)
|
||||
unassign_keys()
|
||||
logging.info("Quit")
|
||||
|
||||
def get_coods(self):
|
||||
return get_values_from_image(self.window.processed_image(func=image_pre_process), self._tesseract_dir)
|
||||
@ -230,5 +217,5 @@ if __name__ == '__main__':
|
||||
bot = FullAuto(c, None)
|
||||
fisher = SemiFisherEngine(c, None)
|
||||
hotkey.initalize()
|
||||
# fisher.toggle_start()
|
||||
fisher.toggle_start()
|
||||
bot.toggle_start()
|
||||
|
@ -8,12 +8,12 @@ import logging
|
||||
|
||||
import pywintypes
|
||||
|
||||
from fishy.engine.IEngine import IEngine
|
||||
from fishy.engine.common.IEngine import IEngine
|
||||
from fishy.engine.semifisher import fishing_event
|
||||
from .fishing_event import HookEvent, StickEvent, LookEvent, IdleEvent
|
||||
from .fishing_mode import FishingMode
|
||||
from .pixel_loc import PixelLoc
|
||||
from fishy.engine.window import Window
|
||||
from ..common.window import WindowClient
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from fishy.gui import GUI
|
||||
@ -31,16 +31,6 @@ class SemiFisherEngine(IEngine):
|
||||
"""
|
||||
|
||||
action_key = self.config.get("action_key", "e")
|
||||
borderless = self.config.get("borderless", False)
|
||||
|
||||
# initialize widow
|
||||
# noinspection PyUnresolvedReferences
|
||||
try:
|
||||
Window.init(borderless)
|
||||
except pywintypes.error:
|
||||
logging.info("Game window not found")
|
||||
self.start = False
|
||||
return
|
||||
|
||||
# initializes fishing modes and their callbacks
|
||||
FishingMode("hook", 0, HookEvent(action_key, False))
|
||||
@ -48,27 +38,24 @@ class SemiFisherEngine(IEngine):
|
||||
FishingMode("look", 2, LookEvent(action_key))
|
||||
FishingMode("idle", 3, IdleEvent(self.config.get("uid"), self.config.get("sound_notification")))
|
||||
|
||||
self.fishPixWindow = Window(color=cv2.COLOR_RGB2HSV)
|
||||
self.fishPixWindow = WindowClient(color=cv2.COLOR_RGB2HSV)
|
||||
|
||||
# check for game window and stuff
|
||||
self.gui.bot_started(True)
|
||||
logging.info("Starting the bot engine, look at the fishing hole to start fishing")
|
||||
Thread(target=self._wait_and_check).start()
|
||||
while self.start:
|
||||
# Services to be ran in the start of the main loop
|
||||
success = Window.loop()
|
||||
capture = self.fishPixWindow.get_capture()
|
||||
|
||||
if not success:
|
||||
if capture is None:
|
||||
# if window server crashed
|
||||
self.gui.bot_started(False)
|
||||
self.toggle_start()
|
||||
continue
|
||||
|
||||
# get the PixelLoc and find the color values, to give it to `FishingMode.Loop`
|
||||
self.fishPixWindow.crop = PixelLoc.val
|
||||
hue_value = self.fishPixWindow.get_capture()[0][0][0]
|
||||
hue_value = capture[0][0][0]
|
||||
FishingMode.loop(hue_value)
|
||||
# Services to be ran in the end of the main loop
|
||||
Window.loop_end()
|
||||
logging.info("Fishing engine stopped")
|
||||
self.gui.bot_started(False)
|
||||
|
||||
|
@ -1,130 +0,0 @@
|
||||
import logging
|
||||
|
||||
import cv2
|
||||
import math
|
||||
import win32gui
|
||||
from win32api import GetSystemMetrics
|
||||
|
||||
import imutils
|
||||
import numpy as np
|
||||
from PIL import ImageGrab
|
||||
|
||||
|
||||
class Window:
|
||||
"""
|
||||
Records the game window, and allows to create instance to process it
|
||||
"""
|
||||
Screen = None
|
||||
windowOffset = None
|
||||
titleOffset = None
|
||||
hwnd = None
|
||||
showing = False
|
||||
|
||||
def __init__(self, crop=None, color=None, scale=None):
|
||||
"""
|
||||
create a window instance with these pre process
|
||||
:param crop: [x1,y1,x2,y2] array defining the boundaries to crop
|
||||
:param color: color to use example cv2.COLOR_RGB2HSV
|
||||
:param scale: scaling the window
|
||||
"""
|
||||
self.color = color
|
||||
self.crop = crop
|
||||
self.scale = scale
|
||||
|
||||
@staticmethod
|
||||
def init(borderless: bool):
|
||||
"""
|
||||
Executed once before the main loop,
|
||||
Finds the game window, and calculates the offset to remove the title bar
|
||||
"""
|
||||
Window.hwnd = win32gui.FindWindow(None, "Elder Scrolls Online")
|
||||
rect = win32gui.GetWindowRect(Window.hwnd)
|
||||
client_rect = win32gui.GetClientRect(Window.hwnd)
|
||||
Window.windowOffset = math.floor(((rect[2] - rect[0]) - client_rect[2]) / 2)
|
||||
Window.titleOffset = ((rect[3] - rect[1]) - client_rect[3]) - Window.windowOffset
|
||||
if borderless:
|
||||
Window.titleOffset = 0
|
||||
|
||||
@staticmethod
|
||||
def loop():
|
||||
"""
|
||||
Executed in the start of the main loop
|
||||
finds the game window location and captures it
|
||||
"""
|
||||
Window.showing = False
|
||||
|
||||
bbox = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))
|
||||
|
||||
temp_screen = np.array(ImageGrab.grab(bbox=bbox))
|
||||
|
||||
temp_screen = cv2.cvtColor(temp_screen, cv2.COLOR_BGR2RGB)
|
||||
|
||||
rect = win32gui.GetWindowRect(Window.hwnd)
|
||||
crop = (rect[0] + Window.windowOffset, rect[1] + Window.titleOffset, rect[2] - Window.windowOffset,
|
||||
rect[3] - Window.windowOffset)
|
||||
|
||||
Window.Screen = temp_screen[crop[1]:crop[3], crop[0]:crop[2]]
|
||||
|
||||
if Window.Screen.size == 0:
|
||||
logging.info("Don't minimize or drag game window outside the screen")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def loop_end():
|
||||
"""
|
||||
Executed in the end of the game loop
|
||||
"""
|
||||
cv2.waitKey(25)
|
||||
|
||||
if not Window.showing:
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
def get_capture(self):
|
||||
"""
|
||||
copies the recorded screen and then pre processes its
|
||||
:return: game window image
|
||||
"""
|
||||
temp_img = Window.Screen
|
||||
|
||||
if self.color is not None:
|
||||
temp_img = cv2.cvtColor(temp_img, self.color)
|
||||
|
||||
if self.crop is not None:
|
||||
temp_img = temp_img[self.crop[1]:self.crop[3], self.crop[0]:self.crop[2]]
|
||||
|
||||
if self.scale is not None:
|
||||
temp_img = cv2.resize(temp_img, (self.scale[0], self.scale[1]), interpolation=cv2.INTER_AREA)
|
||||
|
||||
return temp_img
|
||||
|
||||
def processed_image(self, func=None):
|
||||
"""
|
||||
processes the image using the function provided
|
||||
:param func: function to process image
|
||||
:return: processed image
|
||||
"""
|
||||
if func is None:
|
||||
return self.get_capture()
|
||||
else:
|
||||
return func(self.get_capture())
|
||||
|
||||
def show(self, name, resize=None, func=None, ready_img=None):
|
||||
"""
|
||||
Displays the processed image for debugging purposes
|
||||
:param ready_img: send ready image, just show the `ready_img` directly
|
||||
:param name: unique name for the image, used to create a new window
|
||||
:param resize: scale the image to make small images more visible
|
||||
:param func: function to process the image
|
||||
"""
|
||||
if ready_img is None:
|
||||
img = self.processed_image(func)
|
||||
|
||||
if resize is not None:
|
||||
img = imutils.resize(img, width=resize)
|
||||
else:
|
||||
img = ready_img
|
||||
cv2.imshow(name, img)
|
||||
|
||||
Window.showing = True
|
@ -5,7 +5,7 @@ import threading
|
||||
|
||||
from ttkthemes import ThemedTk
|
||||
|
||||
from fishy.engine.event_handler import EngineEventHandler
|
||||
from fishy.engine.common.event_handler import EngineEventHandler
|
||||
from fishy.gui import config_top
|
||||
from fishy.gui.funcs import GUIFuncs
|
||||
from . import main_gui
|
||||
|
Loading…
Reference in New Issue
Block a user