mirror of
https://github.com/fishyboteso/fishyboteso.git
synced 2024-08-30 18:32:13 +00:00
full auto bug fixing
- changed engine.toggle_start to abstract method - check for state before turning off bot engine - added and corrected logging messages for contorls and calibration - callibrate._get_factor returning None fixed - optimized controls - removed config for show crop - recorder now runs asksaveasfile in gui thread and blocks itself till resoponse - corrrected logic for saving failed files - semifisher dont log started bot engine if not ran from gui - added file name label in full auto config top - call in thread now can return values - gui now saves location when closed - dynamic config location, now uses correct document folder if config file is not found in incorrect document folder
This commit is contained in:
parent
367e2bea55
commit
a964c65776
@ -24,11 +24,9 @@ class IEngine(ABC):
|
||||
|
||||
return self.get_gui().funcs
|
||||
|
||||
@abstractmethod
|
||||
def toggle_start(self):
|
||||
self.start = not self.start
|
||||
if self.start:
|
||||
self.thread = Thread(target=self.run)
|
||||
self.thread.start()
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def run(self):
|
||||
|
@ -44,7 +44,7 @@ def _update_factor(key, value):
|
||||
|
||||
|
||||
def _get_factor(key):
|
||||
config.get("full_auto_factors", {}).get(key)
|
||||
return config.get("full_auto_factors", {}).get(key)
|
||||
|
||||
|
||||
class Calibrate:
|
||||
@ -72,7 +72,7 @@ class Calibrate:
|
||||
# endregion
|
||||
|
||||
def all_callibrated(self):
|
||||
return self.crop and self.move_factor and self.rot_factor
|
||||
return self.crop is not None and self.move_factor is not None and self.rot_factor is not None
|
||||
|
||||
def toggle_show(self):
|
||||
self.engine.show_crop = not self.engine.show_crop
|
||||
@ -81,8 +81,8 @@ class Calibrate:
|
||||
if enable_crop:
|
||||
self.engine.show_crop = True
|
||||
crop = get_crop_coods(self.engine.window)
|
||||
self.engine.window.crop = self.engine.crop
|
||||
_update_factor("crop", crop)
|
||||
self.engine.window.crop = crop
|
||||
|
||||
def walk_calibrate(self):
|
||||
walking_time = 3
|
||||
@ -105,6 +105,7 @@ class Calibrate:
|
||||
|
||||
move_factor = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) / walking_time
|
||||
_update_factor("move_factor", move_factor)
|
||||
logging.info("done")
|
||||
|
||||
def rotate_calibrate(self):
|
||||
rotate_times = 50
|
||||
@ -128,6 +129,7 @@ class Calibrate:
|
||||
|
||||
rot_factor = (rot3 - rot2) / rotate_times
|
||||
_update_factor("rot_factor", rot_factor)
|
||||
logging.info("done")
|
||||
|
||||
def time_to_reach_bottom_callibrate(self):
|
||||
self._callibrate_state = 0
|
||||
@ -135,12 +137,12 @@ class Calibrate:
|
||||
def _f8_pressed():
|
||||
self._callibrate_state += 1
|
||||
|
||||
logging.info("Now loop up and press f8")
|
||||
logging.info("look straight up and press f8")
|
||||
hotkey.set_hotkey(Key.F8, _f8_pressed)
|
||||
|
||||
wait_until(lambda: self._callibrate_state == 1)
|
||||
|
||||
logging.info("looking down now, as soon as you look on the floor, press f8 again")
|
||||
logging.info("as soon as you look on the floor, press f8 again")
|
||||
|
||||
y_cal_start_time = time.time()
|
||||
while self._callibrate_state == 1:
|
||||
@ -150,3 +152,4 @@ class Calibrate:
|
||||
|
||||
time_to_reach_bottom = time.time() - y_cal_start_time
|
||||
_update_factor("time_to_reach_bottom", time_to_reach_bottom)
|
||||
logging.info("done")
|
||||
|
@ -2,20 +2,19 @@ import logging
|
||||
|
||||
from fishy.helper import hotkey, helper
|
||||
|
||||
from fishy.engine.fullautofisher.engine import FullAuto
|
||||
from fishy.engine.fullautofisher.engine import FullAuto, State
|
||||
from fishy.helper.hotkey import Key
|
||||
|
||||
|
||||
def get_controls(engine: FullAuto):
|
||||
from fishy.engine.fullautofisher.recorder import Recorder
|
||||
from fishy.engine.fullautofisher.player import Player
|
||||
|
||||
controls = [
|
||||
("MODE_SELECT", {
|
||||
Key.RIGHT: (lambda: engine.controls.select_mode("CALIBRATE"), "calibrate mode"),
|
||||
Key.UP: (lambda: engine.controls.select_mode("PLAY"), "play mode"),
|
||||
Key.LEFT: (lambda: engine.controls.select_mode("RECORD"), "record mode"),
|
||||
Key.DOWN: (lambda: engine.controls.select_mode("TEST"), "test mode")
|
||||
Key.UP: (lambda: engine.controls.select_mode("TEST1"), "test mode"),
|
||||
Key.LEFT: (Player(engine).toggle_move, "start/stop play"),
|
||||
Key.DOWN: (Recorder(engine).toggle_recording, "start/stop record"),
|
||||
}),
|
||||
("CALIBRATE", {
|
||||
Key.RIGHT: (engine.calibrate.update_crop, "cropping"),
|
||||
@ -23,16 +22,10 @@ def get_controls(engine: FullAuto):
|
||||
Key.LEFT: (engine.calibrate.rotate_calibrate, "rotation"),
|
||||
Key.DOWN: (engine.calibrate.time_to_reach_bottom_callibrate, "look up down")
|
||||
}),
|
||||
("PLAY/RECORD", {
|
||||
Key.RIGHT: (Player(engine).toggle_move, "start/stop play"),
|
||||
Key.UP: (Recorder(engine).toggle_recording, "start/stop record"),
|
||||
Key.LEFT: (None, "not implemented"),
|
||||
Key.DOWN: (None, "not implemented")
|
||||
}),
|
||||
("TEST1", {
|
||||
Key.RIGHT: (engine.test.print_coods, "print coordinates"),
|
||||
Key.UP: (engine.test.look_for_hole, "look for hole up down"),
|
||||
Key.LEFT: (None, "not implemented"),
|
||||
Key.LEFT: (None, ""),
|
||||
Key.DOWN: (lambda: engine.controls.select_mode("TEST2"), "show next")
|
||||
}),
|
||||
("TEST2", {
|
||||
@ -52,20 +45,27 @@ class Controls:
|
||||
self.controls = controls
|
||||
|
||||
def initialize(self):
|
||||
self.select_mode("MODE_SELECT")
|
||||
self.select_mode(self.controls[0][0])
|
||||
|
||||
def log_help(self):
|
||||
help_str = f"\nCONTROLS: {self.controls[self.current_menu][0]}"
|
||||
for key, meta in self.controls[self.current_menu][1].items():
|
||||
func, name = meta
|
||||
if func:
|
||||
hotkey.set_hotkey(key, func)
|
||||
help_str += f"\n{key.value}: {name}"
|
||||
logging.info(help_str)
|
||||
|
||||
def select_mode(self, mode):
|
||||
if FullAuto.state != State.NONE:
|
||||
self.log_help()
|
||||
return
|
||||
|
||||
self.current_menu = 0
|
||||
for i, control in enumerate(self.controls):
|
||||
if mode == control[0]:
|
||||
self.current_menu = i
|
||||
|
||||
help_str = F"CONTROLS: {self.controls[self.current_menu][0]}"
|
||||
for key, meta in self.controls[self.current_menu][1].items():
|
||||
func, name = meta
|
||||
hotkey.set_hotkey(key, func)
|
||||
help_str += f"\n{key.value}: {name}"
|
||||
logging.info(help_str)
|
||||
self.log_help()
|
||||
|
||||
def unassign_keys(self):
|
||||
keys = []
|
||||
|
@ -4,6 +4,7 @@ import tempfile
|
||||
import traceback
|
||||
import uuid
|
||||
from enum import Enum
|
||||
from threading import Thread
|
||||
from zipfile import ZipFile
|
||||
|
||||
import cv2
|
||||
@ -69,17 +70,12 @@ class FullAuto(IEngine):
|
||||
self.calibrate = Calibrate(self)
|
||||
self.test = Test(self)
|
||||
self.controls = Controls(controls.get_controls(self))
|
||||
|
||||
@property
|
||||
def show_crop(self):
|
||||
return config.get("show_window_full_auto", False)
|
||||
|
||||
@show_crop.setter
|
||||
def show_crop(self, x):
|
||||
config.set("show_window_full_auto", x)
|
||||
self.show_crop = False
|
||||
|
||||
def run(self):
|
||||
logging.info("Loading please wait...")
|
||||
self.show_crop = False
|
||||
FullAuto.state = State.NONE
|
||||
|
||||
self.gui.bot_started(True)
|
||||
fishing_event.unsubscribe()
|
||||
self.fisher.toggle_start()
|
||||
@ -95,6 +91,9 @@ class FullAuto(IEngine):
|
||||
logging.info("tesseract not found")
|
||||
downlaoad_and_extract_tesseract()
|
||||
|
||||
if not self.calibrate.all_callibrated():
|
||||
logging.error("you need to callibrate first")
|
||||
|
||||
self.controls.initialize()
|
||||
while self.start and WindowClient.running():
|
||||
self.window.show(self.show_crop, func=image_pre_process)
|
||||
@ -103,7 +102,7 @@ class FullAuto(IEngine):
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
if not self.window.get_capture():
|
||||
if self.window.get_capture() is None:
|
||||
logging.error("Game window not found")
|
||||
|
||||
self.gui.bot_started(False)
|
||||
@ -203,6 +202,16 @@ class FullAuto(IEngine):
|
||||
time.sleep(0.05)
|
||||
self._curr_rotate_y -= 0.05
|
||||
|
||||
def toggle_start(self):
|
||||
if self.start and FullAuto.state != State.NONE:
|
||||
logging.info("Please turn off RECORDING/PLAYING first")
|
||||
return
|
||||
|
||||
self.start = not self.start
|
||||
if self.start:
|
||||
self.thread = Thread(target=self.run)
|
||||
self.thread.start()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.getLogger("").setLevel(logging.DEBUG)
|
||||
|
@ -41,6 +41,8 @@ class Player:
|
||||
self.start_moving_flag = not self.start_moving_flag
|
||||
if self.start_moving_flag:
|
||||
self._start_route()
|
||||
else:
|
||||
logging.info("Waiting for the last action to finish...")
|
||||
|
||||
def _hole_complete_callback(self, e):
|
||||
if e == fishing_event.State.IDLE:
|
||||
@ -52,7 +54,6 @@ class Player:
|
||||
if not timeline:
|
||||
return
|
||||
|
||||
logging.info("starting, press f8 to stop")
|
||||
forward = True
|
||||
i = 0
|
||||
while self.start_moving_flag:
|
||||
|
@ -34,7 +34,7 @@ class Recorder:
|
||||
|
||||
def _start_recording(self):
|
||||
FullAuto.state = State.RECORDING
|
||||
logging.info("f8 to stop recording")
|
||||
logging.info("starting, press f8 to mark hole")
|
||||
hotkey.set_hotkey(Recorder.mark_hole_key, self._mark_hole)
|
||||
|
||||
self.timeline = []
|
||||
@ -54,10 +54,15 @@ class Recorder:
|
||||
|
||||
hotkey.free_key(Recorder.mark_hole_key)
|
||||
|
||||
files = [('Fishy File', '*.fishy')]
|
||||
file = None
|
||||
while not file:
|
||||
file = asksaveasfile(mode='wb', filetypes=files, defaultextension=files)
|
||||
def func():
|
||||
_file = None
|
||||
files = [('Fishy File', '*.fishy')]
|
||||
while not _file:
|
||||
_file = asksaveasfile(mode='wb', filetypes=files, defaultextension=files)
|
||||
|
||||
return _file
|
||||
|
||||
file = self.engine.get_gui().call_in_thread(func, block=True)
|
||||
data = {"full_auto_path": self.timeline}
|
||||
pprint(data)
|
||||
pickle.dump(data, file)
|
||||
|
@ -2,6 +2,7 @@ import logging
|
||||
import os
|
||||
import tempfile
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from zipfile import ZipFile
|
||||
|
||||
import cv2
|
||||
@ -9,6 +10,7 @@ import cv2
|
||||
import pytesseract
|
||||
|
||||
from fishy.helper.downloader import download_file_from_google_drive
|
||||
from fishy.helper.helper import get_documents
|
||||
|
||||
directory = os.path.join(os.environ["APPDATA"], "Tesseract-OCR")
|
||||
|
||||
@ -44,5 +46,5 @@ def get_values_from_image(img):
|
||||
return float(vals[0]), float(vals[1]), float(vals[2])
|
||||
except Exception:
|
||||
logging.error("Couldn't read coods, make sure 'crop' calibration is correct")
|
||||
cv2.imwrite(f"fail_{str(uuid.uuid4())[:8]}", img)
|
||||
cv2.imwrite(os.path.join(get_documents(), "fishy_failed_reads", f"{datetime.now()}.jpg"), img)
|
||||
return None
|
||||
|
@ -35,7 +35,9 @@ class SemiFisherEngine(IEngine):
|
||||
|
||||
# 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")
|
||||
|
||||
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()
|
||||
while self.start and WindowClient.running():
|
||||
capture = self.fishPixWindow.get_capture()
|
||||
@ -74,6 +76,12 @@ class SemiFisherEngine(IEngine):
|
||||
time.sleep(5)
|
||||
Thread(target=show, args=()).start()
|
||||
|
||||
def toggle_start(self):
|
||||
self.start = not self.start
|
||||
if self.start:
|
||||
self.thread = Thread(target=self.run)
|
||||
self.thread.start()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.getLogger("").setLevel(logging.DEBUG)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import os
|
||||
import typing
|
||||
from tkinter.filedialog import askopenfilename
|
||||
|
||||
@ -21,6 +22,12 @@ def start_fullfisher_config(gui: 'GUI'):
|
||||
controls_frame = Frame(top)
|
||||
top.title("Config")
|
||||
|
||||
def file_name():
|
||||
file = config.get("full_auto_rec_file", None)
|
||||
if file is None:
|
||||
return "Not Selected"
|
||||
return os.path.basename(file)
|
||||
|
||||
def select_file():
|
||||
file = askopenfilename(filetypes=[('Python Files', '*.fishy')])
|
||||
if not file:
|
||||
@ -29,8 +36,12 @@ def start_fullfisher_config(gui: 'GUI'):
|
||||
config.set("full_auto_rec_file", file)
|
||||
logging.info(f"loaded {file}")
|
||||
|
||||
Button(controls_frame, text="Select fishy file", command=select_file).grid(row=0, column=0)
|
||||
Label(controls_frame, text="Use semi-fisher config for rest").grid(row=2, column=0)
|
||||
file_name_label.set(file_name())
|
||||
|
||||
file_name_label = StringVar(value=file_name())
|
||||
Label(controls_frame, textvariable=file_name_label).grid(row=0, column=0)
|
||||
Button(controls_frame, text="Select fishy file", command=select_file).grid(row=0, column=1)
|
||||
Label(controls_frame, text="Use semi-fisher config for rest").grid(row=2, column=0, columnspan=2)
|
||||
|
||||
controls_frame.pack(padx=(5, 5), pady=(5, 5))
|
||||
top.start()
|
||||
|
@ -5,6 +5,8 @@ from tkinter.ttk import *
|
||||
|
||||
import typing
|
||||
|
||||
from fishy.helper import helper
|
||||
|
||||
from fishy.web import web
|
||||
|
||||
from fishy.libs.tkhtmlview import HTMLLabel
|
||||
|
@ -1,6 +1,7 @@
|
||||
import logging
|
||||
import uuid
|
||||
from tkinter import OptionMenu, Button, IntVar
|
||||
from typing import List, Callable, Optional
|
||||
from typing import List, Callable, Optional, Dict, Any
|
||||
import threading
|
||||
|
||||
from fishy.web import web
|
||||
@ -12,6 +13,7 @@ from fishy.gui.funcs import GUIFuncs
|
||||
from . import main_gui
|
||||
from .log_config import GUIStreamHandler
|
||||
from ..helper.config import config
|
||||
from ..helper.helper import wait_until
|
||||
|
||||
|
||||
class GUI:
|
||||
@ -23,7 +25,8 @@ class GUI:
|
||||
self._start_restart = False
|
||||
self._destroyed = True
|
||||
self._log_strings = []
|
||||
self._function_queue: List[Callable] = []
|
||||
self._function_queue: Dict[str, Callable] = {}
|
||||
self._result_queue: Dict[str, Any] = {}
|
||||
self._bot_running = False
|
||||
|
||||
# UI items
|
||||
@ -70,11 +73,20 @@ class GUI:
|
||||
|
||||
def _clear_function_queue(self):
|
||||
while len(self._function_queue) > 0:
|
||||
func = self._function_queue.pop(0)
|
||||
func()
|
||||
_id, func = self._function_queue.popitem()
|
||||
result = func()
|
||||
self._result_queue[_id] = result
|
||||
|
||||
def call_in_thread(self, func: Callable):
|
||||
self._function_queue.append(func)
|
||||
def call_in_thread(self, func: Callable, block=False):
|
||||
_id = str(uuid.uuid4())
|
||||
self._function_queue[_id] = func
|
||||
|
||||
if not block:
|
||||
return None
|
||||
|
||||
wait_until(lambda: _id in self._result_queue)
|
||||
|
||||
return self._result_queue.pop(_id)
|
||||
|
||||
def _get_start_stop_text(self):
|
||||
return "STOP (F9)" if self._bot_running else "START (F9)"
|
||||
|
@ -32,7 +32,6 @@ def _create(gui: 'GUI'):
|
||||
|
||||
gui._root = ThemedTk(theme="equilux", background=True)
|
||||
gui._root.title("Fishybot for Elder Scrolls Online")
|
||||
|
||||
gui._root.iconbitmap(helper.manifest_file('icon.ico'))
|
||||
|
||||
# region menu
|
||||
@ -111,6 +110,8 @@ def _create(gui: 'GUI'):
|
||||
_apply_theme(gui)
|
||||
gui._root.update()
|
||||
gui._root.minsize(gui._root.winfo_width() + 10, gui._root.winfo_height() + 10)
|
||||
if config.get("win_loc") is not None:
|
||||
gui._root.geometry(config.get("win_loc"))
|
||||
|
||||
hotkey.set_hotkey(Key.F9, gui.funcs.start_engine)
|
||||
|
||||
@ -118,8 +119,10 @@ def _create(gui: 'GUI'):
|
||||
def set_destroy():
|
||||
if gui._bot_running:
|
||||
logging.info("Turn off the bot engine first")
|
||||
else:
|
||||
gui._destroyed = True
|
||||
return
|
||||
|
||||
config.set("win_loc", gui._root.geometry())
|
||||
gui._destroyed = True
|
||||
|
||||
gui._root.protocol("WM_DELETE_WINDOW", set_destroy)
|
||||
gui._destroyed = False
|
||||
|
@ -6,7 +6,16 @@ import json
|
||||
import os
|
||||
|
||||
# path to save the configuration file
|
||||
filename = os.path.join(os.environ["HOMEDRIVE"], os.environ["HOMEPATH"], "Documents", "fishy_config.json")
|
||||
|
||||
|
||||
def filename():
|
||||
from fishy.helper.helper import get_documents
|
||||
name = "fishy_config.json"
|
||||
_filename = os.path.join(os.environ["HOMEDRIVE"], os.environ["HOMEPATH"], "Documents", name)
|
||||
if os.path.exists(_filename):
|
||||
return _filename
|
||||
|
||||
return os.path.join(get_documents(), name)
|
||||
|
||||
|
||||
class Config:
|
||||
@ -16,7 +25,7 @@ class Config:
|
||||
cache the configuration in a dict for faster access,
|
||||
if file is not found initialize the dict
|
||||
"""
|
||||
self.config_dict = json.loads(open(filename).read()) if os.path.exists(filename) else dict()
|
||||
self.config_dict = json.loads(open(filename()).read()) if os.path.exists(filename()) else dict()
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""
|
||||
@ -51,7 +60,7 @@ class Config:
|
||||
"""
|
||||
save the cache to the file
|
||||
"""
|
||||
with open(filename, 'w') as f:
|
||||
with open(filename(), 'w') as f:
|
||||
f.write(json.dumps(self.config_dict))
|
||||
|
||||
|
||||
|
@ -12,13 +12,12 @@ from uuid import uuid1
|
||||
from hashlib import md5
|
||||
|
||||
from win32com.client import Dispatch
|
||||
from win32comext.shell import shell, shellcon
|
||||
|
||||
import fishy
|
||||
import winshell
|
||||
|
||||
from fishy import web
|
||||
from . import Config
|
||||
from .config import config
|
||||
|
||||
|
||||
def not_implemented():
|
||||
@ -49,6 +48,8 @@ def open_web(website):
|
||||
|
||||
|
||||
def initialize_uid():
|
||||
from .config import config
|
||||
|
||||
if config.get("uid") is not None:
|
||||
return
|
||||
|
||||
@ -106,6 +107,8 @@ def manifest_file(rel_path):
|
||||
|
||||
|
||||
def create_shortcut_first():
|
||||
from .config import config
|
||||
|
||||
if not config.get("shortcut_created", False):
|
||||
create_shortcut(False)
|
||||
config.set("shortcut_created", True)
|
||||
@ -159,5 +162,9 @@ def check_addon(name):
|
||||
logging.error("couldn't install addon, try doing it manually")
|
||||
|
||||
|
||||
def get_documents():
|
||||
return shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
|
||||
|
||||
|
||||
def restart():
|
||||
os.execl(sys.executable, *([sys.executable] + sys.argv))
|
||||
|
Loading…
Reference in New Issue
Block a user