From 734477dc288ccf0587e16a096b4a47e70ed3a9e6 Mon Sep 17 00:00:00 2001 From: Semjon Kerner Date: Fri, 9 Apr 2021 19:30:34 +0200 Subject: [PATCH 1/7] change state detection to dict and use rgb --- fishy/engine/common/window_server.py | 2 -- fishy/engine/semifisher/engine.py | 2 +- fishy/engine/semifisher/fishing_event.py | 32 ++++++++++--------- fishy/engine/semifisher/fishing_mode.py | 39 ++++++++++++------------ 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/fishy/engine/common/window_server.py b/fishy/engine/common/window_server.py index 4b4907b..65170e3 100644 --- a/fishy/engine/common/window_server.py +++ b/fishy/engine/common/window_server.py @@ -60,8 +60,6 @@ def loop(): 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, diff --git a/fishy/engine/semifisher/engine.py b/fishy/engine/semifisher/engine.py index f4d7bb9..b23344c 100644 --- a/fishy/engine/semifisher/engine.py +++ b/fishy/engine/semifisher/engine.py @@ -31,7 +31,7 @@ class SemiFisherEngine(IEngine): code explained in comments in detail """ fishing_event.init() - self.fishPixWindow = WindowClient(color=cv2.COLOR_RGB2HSV) + self.fishPixWindow = WindowClient() # check for game window and stuff self.gui.bot_started(True) diff --git a/fishy/engine/semifisher/fishing_event.py b/fishy/engine/semifisher/fishing_event.py index 895d566..d6dc036 100644 --- a/fishy/engine/semifisher/fishing_event.py +++ b/fishy/engine/semifisher/fishing_event.py @@ -28,7 +28,7 @@ class FishEvent: hole_start_time = 0 FishingStarted = False jitter = False - previousState = State.IDLE + previousState = "IDLE" # initialize these action_key = 'e' @@ -80,33 +80,35 @@ def subscribe(): if fisher_callback not in fishing_mode.subscribers: fishing_mode.subscribers.append(fisher_callback) - if FishingMode.CurrentMode == State.LOOKING: + if FishingMode.CurrentMode == ["LOOKING"]: fisher_callback(FishingMode.CurrentMode) def fisher_callback(event: State): callbacks_map = { - State.IDLE: on_idle, - State.LOOKAWAY: on_lookaway, - State.LOOKING: on_looking, - State.DEPLETED: on_depleted, - State.NOBAIT: on_nobait, - State.FISHING: on_fishing, - State.REELIN: on_reelin, - State.LOOT: on_loot, - State.INVFULL: on_invfull, - State.FIGHT: on_fight, - State.DEAD: on_dead + "IDLE": on_idle, + "LOOKAWAY": on_lookaway, + "LOOKING": on_looking, + "DEPLETED": on_depleted, + "NOBAIT": on_nobait, + "FISHING": on_fishing, + "REELIN": on_reelin, + "LOOT": on_loot, + "INVFULL": on_invfull, + "FIGHT": on_fight, + "DEAD": on_dead } try: callbacks_map[event]() FishEvent.previousState = event except KeyError as ex: - pass + logging.error("KeyError: State " + str(event) + " is not known.") + except TypeError as ex: + logging.error("TypeError when reading state: " + str(event)) def on_idle(): - if FishEvent.previousState in (State.FISHING, State.REELIN): + if FishEvent.previousState in ("FISHING", "REELIN"): logging.info("FISHING INTERRUPTED") _sound_and_send_fishy_data() diff --git a/fishy/engine/semifisher/fishing_mode.py b/fishy/engine/semifisher/fishing_mode.py index 7846fc7..a19c611 100644 --- a/fishy/engine/semifisher/fishing_mode.py +++ b/fishy/engine/semifisher/fishing_mode.py @@ -1,21 +1,22 @@ import time -from enum import Enum subscribers = [] -class State(Enum): - IDLE = [ 0, 0, 255] - LOOKAWAY = [150, 255, 76] - LOOKING = [100, 255, 101] - DEPLETED = [ 30, 255, 76] - NOBAIT = [ 96, 255, 255] - FISHING = [ 18, 165, 213] - REELIN = [ 60, 255, 204] - LOOT = [ 0, 255, 204] - INVFULL = [ 0, 255, 51] - FIGHT = [120, 255, 204] - DEAD = [ 0, 0, 51] +State = { + "IDLE" : [255, 255, 255], + "LOOKAWAY" : [ 76, 0, 76], + "LOOKING" : [101, 69, 0], + "DEPLETED" : [ 0, 76, 76], + "NOBAIT" : [255, 204, 0], + "FISHING" : [ 75, 156, 213], + "REELIN" : [ 0, 204, 0], + "LOOT" : [ 0, 0, 204], + "INVFULL" : [ 0, 0, 51], + "FIGHT" : [204, 0, 0], + "DEAD" : [ 51, 51, 51] +} + def _notify(event): for subscriber in subscribers: @@ -23,20 +24,20 @@ def _notify(event): class FishingMode: - CurrentMode = State.IDLE - PrevMode = State.IDLE + CurrentMode = "IDLE" + PrevMode = "IDLE" -def loop(hsv): +def loop(rgb): """ Executed in the start of the main loop in fishy.py Changes modes, calls mode events (callbacks) when mode is changed - :param hsv: hsv read by the bot + :param rgb: rgb read by the bot """ - FishingMode.CurrentMode = State.IDLE + FishingMode.CurrentMode = "IDLE" for s in State: - if all(hsv == s.value): + if all(rgb == State[s]): FishingMode.CurrentMode = s if FishingMode.CurrentMode != FishingMode.PrevMode: From f790a83acf6d21974e143e975ff64ab9886adb1a Mon Sep 17 00:00:00 2001 From: Semjon Kerner Date: Fri, 7 May 2021 14:13:55 +0200 Subject: [PATCH 2/7] fixup enum-dict --- fishy/engine/semifisher/fishing_event.py | 28 ++++++++++++------------ fishy/engine/semifisher/fishing_mode.py | 24 +++++++++++++++----- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/fishy/engine/semifisher/fishing_event.py b/fishy/engine/semifisher/fishing_event.py index d6dc036..cacff0e 100644 --- a/fishy/engine/semifisher/fishing_event.py +++ b/fishy/engine/semifisher/fishing_event.py @@ -28,7 +28,7 @@ class FishEvent: hole_start_time = 0 FishingStarted = False jitter = False - previousState = "IDLE" + previousState = State.IDLE # initialize these action_key = 'e' @@ -80,23 +80,23 @@ def subscribe(): if fisher_callback not in fishing_mode.subscribers: fishing_mode.subscribers.append(fisher_callback) - if FishingMode.CurrentMode == ["LOOKING"]: + if FishingMode.CurrentMode == State.LOOKING: fisher_callback(FishingMode.CurrentMode) def fisher_callback(event: State): callbacks_map = { - "IDLE": on_idle, - "LOOKAWAY": on_lookaway, - "LOOKING": on_looking, - "DEPLETED": on_depleted, - "NOBAIT": on_nobait, - "FISHING": on_fishing, - "REELIN": on_reelin, - "LOOT": on_loot, - "INVFULL": on_invfull, - "FIGHT": on_fight, - "DEAD": on_dead + State.IDLE: on_idle, + State.LOOKAWAY: on_lookaway, + State.LOOKING: on_looking, + State.DEPLETED: on_depleted, + State.NOBAIT: on_nobait, + State.FISHING: on_fishing, + State.REELIN: on_reelin, + State.LOOT: on_loot, + State.INVFULL: on_invfull, + State.FIGHT: on_fight, + State.DEAD: on_dead } try: callbacks_map[event]() @@ -108,7 +108,7 @@ def fisher_callback(event: State): def on_idle(): - if FishEvent.previousState in ("FISHING", "REELIN"): + if FishEvent.previousState in (State.FISHING, State.REELIN): logging.info("FISHING INTERRUPTED") _sound_and_send_fishy_data() diff --git a/fishy/engine/semifisher/fishing_mode.py b/fishy/engine/semifisher/fishing_mode.py index a19c611..c78230e 100644 --- a/fishy/engine/semifisher/fishing_mode.py +++ b/fishy/engine/semifisher/fishing_mode.py @@ -1,9 +1,23 @@ import time +from enum import Enum subscribers = [] -State = { +class State(Enum): + IDLE = 0 + LOOKAWAY = 1 + LOOKING = 2 + DEPLETED = 3 + NOBAIT = 5 + FISHING = 6 + REELIN = 7 + LOOT = 8 + INVFULL = 9 + FIGHT = 14 + DEAD = 15 + +Colors = { "IDLE" : [255, 255, 255], "LOOKAWAY" : [ 76, 0, 76], "LOOKING" : [101, 69, 0], @@ -24,8 +38,8 @@ def _notify(event): class FishingMode: - CurrentMode = "IDLE" - PrevMode = "IDLE" + CurrentMode = State.IDLE + PrevMode = State.IDLE def loop(rgb): @@ -35,9 +49,9 @@ def loop(rgb): :param rgb: rgb read by the bot """ - FishingMode.CurrentMode = "IDLE" + FishingMode.CurrentMode = State.IDLE for s in State: - if all(rgb == State[s]): + if all(rgb == Colors[s.name]): FishingMode.CurrentMode = s if FishingMode.CurrentMode != FishingMode.PrevMode: From ac18f3f2ccf843ca3e0c24d3971cc967ec80edfb Mon Sep 17 00:00:00 2001 From: Semjon Kerner Date: Fri, 7 May 2021 15:16:21 +0200 Subject: [PATCH 3/7] fixupfixup enum-dict --- fishy/engine/semifisher/fishing_mode.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fishy/engine/semifisher/fishing_mode.py b/fishy/engine/semifisher/fishing_mode.py index c78230e..4049dfe 100644 --- a/fishy/engine/semifisher/fishing_mode.py +++ b/fishy/engine/semifisher/fishing_mode.py @@ -18,17 +18,17 @@ class State(Enum): DEAD = 15 Colors = { - "IDLE" : [255, 255, 255], - "LOOKAWAY" : [ 76, 0, 76], - "LOOKING" : [101, 69, 0], - "DEPLETED" : [ 0, 76, 76], - "NOBAIT" : [255, 204, 0], - "FISHING" : [ 75, 156, 213], - "REELIN" : [ 0, 204, 0], - "LOOT" : [ 0, 0, 204], - "INVFULL" : [ 0, 0, 51], - "FIGHT" : [204, 0, 0], - "DEAD" : [ 51, 51, 51] + State.IDLE : [255, 255, 255], + State.LOOKAWAY : [ 76, 0, 76], + State.LOOKING : [101, 69, 0], + State.DEPLETED : [ 0, 76, 76], + State.NOBAIT : [255, 204, 0], + State.FISHING : [ 75, 156, 213], + State.REELIN : [ 0, 204, 0], + State.LOOT : [ 0, 0, 204], + State.INVFULL : [ 0, 0, 51], + State.FIGHT : [204, 0, 0], + State.DEAD : [ 51, 51, 51] } @@ -51,7 +51,7 @@ def loop(rgb): """ FishingMode.CurrentMode = State.IDLE for s in State: - if all(rgb == Colors[s.name]): + if all(rgb == Colors[s]): FishingMode.CurrentMode = s if FishingMode.CurrentMode != FishingMode.PrevMode: From b6a375f486ad9ba61023096142d74f20dc878b37 Mon Sep 17 00:00:00 2001 From: Semjon Kerner Date: Fri, 9 Apr 2021 20:50:53 +0200 Subject: [PATCH 4/7] add luaparser and read saved var --- fishy/engine/semifisher/engine.py | 6 +- fishy/engine/semifisher/fishing_event.py | 1 + fishy/helper/__init__.py | 4 +- fishy/helper/helper.py | 9 ++- fishy/helper/luaparser.py | 79 ++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 fishy/helper/luaparser.py diff --git a/fishy/engine/semifisher/engine.py b/fishy/engine/semifisher/engine.py index b23344c..7f23215 100644 --- a/fishy/engine/semifisher/engine.py +++ b/fishy/engine/semifisher/engine.py @@ -10,12 +10,14 @@ import logging from fishy.engine.semifisher.fishing_event import FishEvent from fishy.engine.common.window import WindowClient -from fishy.engine.semifisher.fishing_mode import FishingMode +from fishy.engine.semifisher.fishing_mode import State, FishingMode from fishy.engine.common.IEngine import IEngine from fishy.engine.semifisher import fishing_mode, fishing_event from fishy.engine.semifisher.pixel_loc import PixelLoc +from fishy.helper.luaparser import sv_color_extract + if typing.TYPE_CHECKING: from fishy.gui import GUI @@ -36,6 +38,8 @@ class SemiFisherEngine(IEngine): # check for game window and stuff self.gui.bot_started(True) + sv_color_extract(State) + if self.get_gui: logging.info("Starting the bot engine, look at the fishing hole to start fishing") Thread(target=self._wait_and_check).start() diff --git a/fishy/engine/semifisher/fishing_event.py b/fishy/engine/semifisher/fishing_event.py index cacff0e..5c6527e 100644 --- a/fishy/engine/semifisher/fishing_event.py +++ b/fishy/engine/semifisher/fishing_event.py @@ -98,6 +98,7 @@ def fisher_callback(event: State): State.FIGHT: on_fight, State.DEAD: on_dead } + try: callbacks_map[event]() FishEvent.previousState = event diff --git a/fishy/helper/__init__.py b/fishy/helper/__init__.py index dabfc52..2d3f6cf 100644 --- a/fishy/helper/__init__.py +++ b/fishy/helper/__init__.py @@ -1,4 +1,6 @@ from .auto_update import auto_upgrade, upgrade_avail, versions from .config import Config from .helper import open_web, initialize_uid, install_thread_excepthook, unhandled_exception_logging, manifest_file, \ - create_shortcut_first, addon_exists, get_addonversion, install_addon, remove_addon, restart, create_shortcut, not_implemented, update + create_shortcut_first, addon_exists, get_addonversion, install_addon, remove_addon, restart, create_shortcut, \ + not_implemented, update, get_savedvarsdir +from .luaparser import sv_color_extract diff --git a/fishy/helper/helper.py b/fishy/helper/helper.py index fb0b591..66b6036 100644 --- a/fishy/helper/helper.py +++ b/fishy/helper/helper.py @@ -145,11 +145,18 @@ def create_shortcut(anti_ghosting: bool): logging.error("Couldn't create shortcut") +def get_savedvarsdir(): + # noinspection PyUnresolvedReferences + from win32com.shell import shell, shellcon + documents = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0) + return os.path.join(documents, "Elder Scrolls Online", "live", "SavedVariables") + + def get_addondir(): # noinspection PyUnresolvedReferences from win32com.shell import shell, shellcon documents = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0) - return os.path.join(documents, "Elder Scrolls Online", "live", "Addons") + return os.path.join(documents, "Elder Scrolls Online", "live", "Addons") def addon_exists(name, url=None, v=None): diff --git a/fishy/helper/luaparser.py b/fishy/helper/luaparser.py new file mode 100644 index 0000000..7e8d5a5 --- /dev/null +++ b/fishy/helper/luaparser.py @@ -0,0 +1,79 @@ +import os +import logging +from math import floor +from .helper import get_savedvarsdir + + +def _sv_parser(path): + try: + with open(path, "r") as f: + lua = f.read() + + """ + bring lua saved-var file into a useable format: + - one line per expression (add \n where needed) + - remove all redundant characters + - make lowercase, split into list of expressions + - remove empty expressions + EXPRESSIONS: A) List-Start "name=", B) Variable assignment "name=val", C) List End "}" + """ + for old, new in ((",","\n"), ("{","{\n"), ("}","}\n"), ("{",""), (",", ""), ("[", ""), ("]", ""), ('"', ""), (" ", "")): + lua = lua.replace(old, new) + lua = lua.lower().split("\n") + lua = [expression for expression in lua if expression] + + """ + the lua saved-var file is parsed to a tree of dicts + each line represents either one node in the tree or the end of a subtree + the last symbol of each line decides the type of the node (branch vertex or leaf) + """ + stack = [] + root = (dict(),"root") + stack.append(root) + for line in lua: + if line == "": + break + if line[-1] == '=': #subtree start + t = dict() + tname = line.split("=")[0] + stack.append((t,tname)) + elif line[-1] == '}': #subtree end + t = stack.pop() + tp = stack.pop() + tp[0][t[1]] = t[0] + stack.append(tp) + else: #new element in tree + name,val = line.split("=") + t = stack.pop() + t[0][name] = val + stack.append(t) + return root[0] + + except Exception as ex: + logging.error("Error: '" + str(ex) + "' occured, while parsing ESO variables.") + return None + + +def sv_color_extract(states): + root = _sv_parser(os.path.join(get_savedvarsdir(), "Chalutier.lua")) + if root == None: + return states + + for i in range(4): + name, root = root.popitem() + colors = [] + for i in root["colors"]: + """ + ingame representation of colors range from 0 to 1 in float + these values are scaled by 255 + """ + rgb=[ + floor(float(root["colors"][i]["r"])*255), + floor(float(root["colors"][i]["g"])*255), + floor(float(root["colors"][i]["b"])*255) + ] + colors.append(rgb) + for i,s in enumerate(states): + states[s] = colors[i] + return states + From 58457ef7980bf9dcc3f28ac459207c14ae299952 Mon Sep 17 00:00:00 2001 From: Semjon Kerner Date: Fri, 7 May 2021 14:07:09 +0200 Subject: [PATCH 5/7] fixup luaparser --- fishy/engine/semifisher/engine.py | 4 ++-- fishy/helper/luaparser.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fishy/engine/semifisher/engine.py b/fishy/engine/semifisher/engine.py index 7f23215..17270ca 100644 --- a/fishy/engine/semifisher/engine.py +++ b/fishy/engine/semifisher/engine.py @@ -10,7 +10,7 @@ import logging from fishy.engine.semifisher.fishing_event import FishEvent from fishy.engine.common.window import WindowClient -from fishy.engine.semifisher.fishing_mode import State, FishingMode +from fishy.engine.semifisher.fishing_mode import Colors, FishingMode from fishy.engine.common.IEngine import IEngine from fishy.engine.semifisher import fishing_mode, fishing_event @@ -38,7 +38,7 @@ class SemiFisherEngine(IEngine): # check for game window and stuff self.gui.bot_started(True) - sv_color_extract(State) + sv_color_extract(Colors) if self.get_gui: logging.info("Starting the bot engine, look at the fishing hole to start fishing") diff --git a/fishy/helper/luaparser.py b/fishy/helper/luaparser.py index 7e8d5a5..ec621a8 100644 --- a/fishy/helper/luaparser.py +++ b/fishy/helper/luaparser.py @@ -18,7 +18,7 @@ def _sv_parser(path): EXPRESSIONS: A) List-Start "name=", B) Variable assignment "name=val", C) List End "}" """ for old, new in ((",","\n"), ("{","{\n"), ("}","}\n"), ("{",""), (",", ""), ("[", ""), ("]", ""), ('"', ""), (" ", "")): - lua = lua.replace(old, new) + lua = lua.replace(old, new) lua = lua.lower().split("\n") lua = [expression for expression in lua if expression] @@ -54,10 +54,10 @@ def _sv_parser(path): return None -def sv_color_extract(states): +def sv_color_extract(Colors): root = _sv_parser(os.path.join(get_savedvarsdir(), "Chalutier.lua")) if root == None: - return states + return Colors for i in range(4): name, root = root.popitem() @@ -73,7 +73,7 @@ def sv_color_extract(states): floor(float(root["colors"][i]["b"])*255) ] colors.append(rgb) - for i,s in enumerate(states): - states[s] = colors[i] - return states + for i,c in enumerate(Colors): + Colors[c] = colors[i] + return Colors From a1ce1ccae925bd15bb4ca8e57401760bd83b2166 Mon Sep 17 00:00:00 2001 From: Semjon Kerner Date: Mon, 12 Apr 2021 23:12:39 +0200 Subject: [PATCH 6/7] update chalutier addon for default dead color --- fishy/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fishy/constants.py b/fishy/constants.py index 20ae28b..e1b74bb 100644 --- a/fishy/constants.py +++ b/fishy/constants.py @@ -1,5 +1,5 @@ apiversion = 2 -chalutier = ("Chalutier", "https://github.com/fishyboteso/Chalutier/raw/619b4ab0b8ff91746afda855542e886d27b7a794/Chalutier_1.1.2.zip", 112) +chalutier = ("Chalutier", "https://github.com/fishyboteso/Chalutier/raw/main/Chalutier_1.1.3.zip", 113) lam2 = ("LibAddonMenu-2.0", "https://www.esoui.com/downloads/dl7/LibAddonMenu-2.0r32.zip", 32) fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/files/6329586/FishyQR.zip", 100) From fe3715b21b2fb014f3e75b91905db7499127c0a0 Mon Sep 17 00:00:00 2001 From: Semjon Kerner Date: Fri, 7 May 2021 11:49:54 +0200 Subject: [PATCH 7/7] fixup chalutier version --- fishy/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fishy/constants.py b/fishy/constants.py index e1b74bb..ec39bc7 100644 --- a/fishy/constants.py +++ b/fishy/constants.py @@ -1,5 +1,5 @@ apiversion = 2 -chalutier = ("Chalutier", "https://github.com/fishyboteso/Chalutier/raw/main/Chalutier_1.1.3.zip", 113) +chalutier = ("Chalutier", "https://www.esoui.com/downloads/dl2934/Chalutier_1.1.4.zip", 114) lam2 = ("LibAddonMenu-2.0", "https://www.esoui.com/downloads/dl7/LibAddonMenu-2.0r32.zip", 32) fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/files/6329586/FishyQR.zip", 100)