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 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 __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):
def on_hook():
"""
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
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))
_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)
keyboard.press_and_release(FishEvent.action_key)
if self.collect_r:
if FishEvent.collect_r:
time.sleep(0.1)
keyboard.press_and_release('r')
time.sleep(0.1)
_notify("hook")
def on_exit_callback(self, current_mode):
pass
class LookEvent(FishEvent):
"""
state when looking on a fishing hole
"""
def __init__(self, action_key: str):
self.action_key = action_key
def on_enter_callback(self, previous_mode):
def on_look():
"""
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
keyboard.press_and_release(FishEvent.action_key)
class IdleEvent(FishEvent):
"""
State when the fishing hole is depleted or the bot is doing nothing
"""
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:
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)
if previous_mode.name == "hook":
if FishEvent.previousState == State.HOOK:
logging.info("HOLE DEPLETED")
else:
logging.info("FISHING INTERRUPTED")
_notify("idle")
def on_exit_callback(self, current_mode):
pass
def on_stick():
FishEvent.stickInitTime = time.time()
FishEvent.FishingStarted = True
class StickEvent(FishEvent):
"""
State when fishing is going on
"""
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
_stickInitTime = time.time()
_FishingStarted = True
if _fishCaught == 0:
_hole_start_time = time.time()
_fish_times = []
_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:
"""
State machine for fishing modes
CurrentMode = State.IDLE
PrevMode = State.IDLE
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
"""
HValues = [60, 18, 100]
CurrentMode = None
PrevMode = None
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):
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)
FishingMode.CurrentMode = State.IDLE
for s in State:
if hue_values == s.value:
FishingMode.CurrentMode = s
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)
_notify(FishingMode.CurrentMode)
FishingMode.PrevMode = FishingMode.CurrentMode