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:
Adam Saudagar 2020-12-01 02:34:33 +05:30
parent 367e2bea55
commit a964c65776
14 changed files with 133 additions and 63 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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