From c63ff4c3ba095703fb0c8f9a78970821582dc7c4 Mon Sep 17 00:00:00 2001 From: Adam Saudagar Date: Sat, 17 Oct 2020 21:34:44 +0530 Subject: [PATCH] refactored semifisher state machine feature to be much simpler, - now different systems can subscribe to semifisher events --- fishy/engine/fullautofisher/engine.py | 6 +- fishy/engine/semifisher/engine.py | 35 +++-- fishy/engine/semifisher/fishing_event.py | 180 ++++++++--------------- fishy/engine/semifisher/fishing_mode.py | 96 ++++-------- 4 files changed, 112 insertions(+), 205 deletions(-) diff --git a/fishy/engine/fullautofisher/engine.py b/fishy/engine/fullautofisher/engine.py index 2b969f4..3babba9 100644 --- a/fishy/engine/fullautofisher/engine.py +++ b/fishy/engine/fullautofisher/engine.py @@ -10,7 +10,7 @@ import pytesseract from fishy.engine import SemiFisherEngine from fishy.engine.common.window import WindowClient -from fishy.engine.semifisher import fishing_event +from fishy.engine.semifisher import fishing_event, fishing_mode from fishy.engine.common.IEngine import IEngine from pynput import keyboard, mouse @@ -171,7 +171,7 @@ class FullAuto(IEngine): if e == "look": self._hole_found_flag = True - fishing_event.subscribers.append(found_hole) + fishing_mode.subscribers.append(found_hole) t = 0 while not self._hole_found_flag and t <= self.factors[2] / 2: @@ -184,7 +184,7 @@ class FullAuto(IEngine): t -= 0.05 self._curr_rotate_y = t - fishing_event.subscribers.remove(found_hole) + fishing_mode.subscribers.remove(found_hole) return self._hole_found_flag def set_target(self): diff --git a/fishy/engine/semifisher/engine.py b/fishy/engine/semifisher/engine.py index 7b45aae..2426bd2 100644 --- a/fishy/engine/semifisher/engine.py +++ b/fishy/engine/semifisher/engine.py @@ -1,19 +1,19 @@ import time import typing -from collections import Callable from threading import Thread +from typing import Callable import cv2 import logging -import pywintypes +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.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 ..common.window import WindowClient +from fishy.engine.semifisher import fishing_mode, fishing_event +from fishy.engine.semifisher.pixel_loc import PixelLoc if typing.TYPE_CHECKING: from fishy.gui import GUI @@ -29,15 +29,7 @@ class SemiFisherEngine(IEngine): Starts the fishing code explained in comments in detail """ - - action_key = self.config.get("action_key", "e") - - # initializes fishing modes and their callbacks - FishingMode("hook", 0, HookEvent(action_key, False)) - FishingMode("stick", 1, StickEvent()) - FishingMode("look", 2, LookEvent(action_key)) - FishingMode("idle", 3, IdleEvent(self.config.get("uid"), self.config.get("sound_notification"))) - + fishing_event.init() self.fishPixWindow = WindowClient(color=cv2.COLOR_RGB2HSV) # check for game window and stuff @@ -55,13 +47,15 @@ class SemiFisherEngine(IEngine): self.fishPixWindow.crop = PixelLoc.val hue_value = capture[0][0][0] - FishingMode.loop(hue_value) + fishing_mode.loop(hue_value) + logging.info("Fishing engine stopped") self.gui.bot_started(False) + fishing_event.destroy() def _wait_and_check(self): time.sleep(10) - if not fishing_event._FishingStarted and self.start: + if not FishEvent.FishingStarted and self.start: self.gui.show_error("Doesn't look like fishing has started\n\n" "Check out #read-me-first on our discord channel to troubleshoot the issue") @@ -79,4 +73,9 @@ class SemiFisherEngine(IEngine): Thread(target=show, args=()).start() +if __name__ == '__main__': + logging.getLogger("").setLevel(logging.DEBUG) + # noinspection PyTypeChecker + fisher = SemiFisherEngine(None, None) + fisher.toggle_start() diff --git a/fishy/engine/semifisher/fishing_event.py b/fishy/engine/semifisher/fishing_event.py index 9c2c3e2..ea01cd8 100644 --- a/fishy/engine/semifisher/fishing_event.py +++ b/fishy/engine/semifisher/fishing_event.py @@ -5,153 +5,93 @@ also implements callbacks which is called when states are changed """ import logging import time -from abc import abstractmethod, ABC +from fishy.engine.semifisher import fishing_mode from playsound import playsound from fishy import web +from fishy.engine.semifisher.fishing_mode import State from fishy.helper import helper import keyboard -_fishCaught = 0 -_totalFishCaught = 0 -_stickInitTime = 0 -_fish_times = [] -_hole_start_time = 0 -_FishingStarted = False -subscribers = [] +class FishEvent: + fishCaught = 0 + totalFishCaught = 0 + stickInitTime = 0 + fish_times = [] + hole_start_time = 0 + FishingStarted = False + previousState = State.IDLE + + # initialize these + action_key = 'e' + collect_r = False + uid = None + sound = False -def _notify(event): - for subscriber in subscribers: - subscriber(event) +def init(): + # todo load config + fishing_mode.subscribers.append(fisher_callback) -class FishEvent(ABC): - @abstractmethod - def on_enter_callback(self, previous_mode): - pass - - @abstractmethod - def on_exit_callback(self, current_mode): - pass +def destroy(): + fishing_mode.subscribers.remove(fisher_callback) -class HookEvent(FishEvent): - - def __init__(self, action_key: str, collect_r: bool): - self.action_key = action_key - self.collect_r = collect_r - - def on_enter_callback(self, previous_mode): - """ - called when the fish hook is detected - increases the `fishCaught` and `totalFishCaught`, calculates the time it took to catch - presses e to catch the fish - - :param previous_mode: previous mode in the state machine - """ - global _fishCaught, _totalFishCaught - - _fishCaught += 1 - _totalFishCaught += 1 - time_to_hook = time.time() - _stickInitTime - _fish_times.append(time_to_hook) - logging.info("HOOOOOOOOOOOOOOOOOOOOOOOK....... " + str(_fishCaught) + " caught " + "in " + str( - round(time_to_hook, 2)) + " secs. " + "Total: " + str(_totalFishCaught)) - # pyautogui.press(self.action_key) - keyboard.press_and_release(self.action_key) - - if self.collect_r: - time.sleep(0.1) - keyboard.press_and_release('r') - time.sleep(0.1) - - _notify("hook") +def fisher_callback(event: State): + callbacks_map = {State.HOOK: on_hook, State.LOOK: on_look, State.IDLE: on_idle, State.STICK: on_stick} + callbacks_map[event]() + FishEvent.previousState = event - - def on_exit_callback(self, current_mode): - pass - - -class LookEvent(FishEvent): +def on_hook(): """ - state when looking on a fishing hole + called when the fish hook is detected + increases the `fishCaught` and `totalFishCaught`, calculates the time it took to catch + presses e to catch the fish """ + FishEvent.fishCaught += 1 + FishEvent.totalFishCaught += 1 + time_to_hook = time.time() - FishEvent.stickInitTime + FishEvent.fish_times.append(time_to_hook) + logging.info("HOOOOOOOOOOOOOOOOOOOOOOOK....... " + str(FishEvent.fishCaught) + " caught " + "in " + str( + round(time_to_hook, 2)) + " secs. " + "Total: " + str(FishEvent.totalFishCaught)) - def __init__(self, action_key: str): - self.action_key = action_key + keyboard.press_and_release(FishEvent.action_key) - def on_enter_callback(self, previous_mode): - """ - presses e to throw the fishing rod - :param previous_mode: previous mode in the state machine - """ - keyboard.press_and_release(self.action_key) - _notify("look") - - def on_exit_callback(self, current_mode): - pass + if FishEvent.collect_r: + time.sleep(0.1) + keyboard.press_and_release('r') + time.sleep(0.1) -class IdleEvent(FishEvent): +def on_look(): """ - State when the fishing hole is depleted or the bot is doing nothing + presses e to throw the fishing rod """ - - def __init__(self, uid, sound: bool): - """ - sets the flag to send notification on phone - """ - self.uid = uid - self.sound = sound - - def on_enter_callback(self, previous_mode): - """ - Resets the fishCaught counter and logs a message depending on the previous state - :param previous_mode: previous mode in the state machine - """ - global _fishCaught - - if _fishCaught > 0: - web.send_hole_deplete(self.uid, _fishCaught, time.time() - _hole_start_time, _fish_times) - _fishCaught = 0 - if self.sound: - playsound(helper.manifest_file("sound.mp3"), False) - - if previous_mode.name == "hook": - logging.info("HOLE DEPLETED") - else: - logging.info("FISHING INTERRUPTED") - - _notify("idle") - - def on_exit_callback(self, current_mode): - pass + keyboard.press_and_release(FishEvent.action_key) -class StickEvent(FishEvent): - """ - State when fishing is going on - """ +def on_idle(): + if FishEvent.fishCaught > 0: + web.send_hole_deplete(FishEvent.uid, FishEvent.fishCaught, time.time() - FishEvent.hole_start_time, + FishEvent.fish_times) + FishEvent.fishCaught = 0 + if FishEvent.sound: + playsound(helper.manifest_file("sound.mp3"), False) - def on_enter_callback(self, previous_mode): - """ - resets the fishing timer - :param previous_mode: previous mode in the state machine - """ - global _stickInitTime, _hole_start_time, _fish_times, _FishingStarted + if FishEvent.previousState == State.HOOK: + logging.info("HOLE DEPLETED") + else: + logging.info("FISHING INTERRUPTED") - _stickInitTime = time.time() - _FishingStarted = True - if _fishCaught == 0: - _hole_start_time = time.time() - _fish_times = [] +def on_stick(): + FishEvent.stickInitTime = time.time() + FishEvent.FishingStarted = True - _notify("stick") - - def on_exit_callback(self, current_mode): - pass + if FishEvent.fishCaught == 0: + FishEvent.hole_start_time = time.time() + FishEvent.fish_times = [] diff --git a/fishy/engine/semifisher/fishing_mode.py b/fishy/engine/semifisher/fishing_mode.py index ea0d05a..82ac186 100644 --- a/fishy/engine/semifisher/fishing_mode.py +++ b/fishy/engine/semifisher/fishing_mode.py @@ -1,70 +1,38 @@ +from enum import Enum + +subscribers = [] + + +class State(Enum): + HOOK = 60, + STICK = 18, + LOOK = 100, + IDLE = -1 + + +def _notify(event): + for subscriber in subscribers: + subscriber(event) + + class FishingMode: + CurrentMode = State.IDLE + PrevMode = State.IDLE + + +def loop(hue_values): """ - State machine for fishing modes + Executed in the start of the main loop in fishy.py + Changes modes, calls mode events (callbacks) when mode is changed - HValues hue values for each fishing mode - CurrentCount number of times same hue color is read before it changes state - CurrentMode current mode of the state machine - PrevMode previous mode of the state machine - FishingStarted probably does nothing (not sure though) - Modes list of states + :param hue_values: hue_values read by the bot """ - HValues = [60, 18, 100] + FishingMode.CurrentMode = State.IDLE + for s in State: + if hue_values == s.value: + FishingMode.CurrentMode = s - CurrentMode = None - PrevMode = None + if FishingMode.CurrentMode != FishingMode.PrevMode: + _notify(FishingMode.CurrentMode) - Modes = [] - - def __init__(self, name, label, event): - """ - create a new state - :param name: name of the state - :param label: integer, label of the state (int) - :param event: object of class containing onEnterCallback & onExitCallback functions - which are called when state is changed - """ - self.name = name - self.label = label - self.event = event - - FishingMode.Modes.append(self) - - @staticmethod - def get_by_label(label): - """ - find a state using label - :param label: label integer - :return: state - """ - for m in FishingMode.Modes: - if m.label == label: - return m - - @staticmethod - def loop(hue_values): - """ - Executed in the start of the main loop in fishy.py - Changes modes, calls mode events (callbacks) when mode is changed - - :param hue_values: hue_values read by the bot - """ - if FishingMode.PrevMode is None: - FishingMode.PrevMode = FishingMode.get_by_label(3) - - current_label = 3 - for i, val in enumerate(FishingMode.HValues): - if hue_values == val: - current_label = i - - FishingMode.CurrentMode = FishingMode.get_by_label(current_label) - - if FishingMode.CurrentMode != FishingMode.PrevMode: - - if FishingMode.PrevMode.event is not None: - FishingMode.PrevMode.event.on_exit_callback(FishingMode.CurrentMode) - - if FishingMode.CurrentMode.event is not None: - FishingMode.CurrentMode.event.on_enter_callback(FishingMode.PrevMode) - - FishingMode.PrevMode = FishingMode.CurrentMode + FishingMode.PrevMode = FishingMode.CurrentMode