refactored semifisher state machine feature to be much simpler,

- now different systems can subscribe to semifisher events
This commit is contained in:
Adam Saudagar 2020-10-17 21:34:44 +05:30
parent 315adf9799
commit c63ff4c3ba
4 changed files with 112 additions and 205 deletions

View File

@ -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):

View File

@ -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()

View File

@ -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 = []

View File

@ -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