Merge pull request #66 from fishyboteso/feature/fullauto

Full auto engine
This commit is contained in:
Adam Saudagar 2021-04-18 12:02:14 +05:30 committed by GitHub
commit b066f29798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 155 additions and 168 deletions

View File

@ -1,3 +1,6 @@
apiversion = 2 apiversion = 2
chalutier = ("Chalutier", "https://github.com/fishyboteso/Chalutier/raw/619b4ab0b8ff91746afda855542e886d27b7a794/Chalutier_1.1.2.zip", 112) chalutier = ("Chalutier", "https://github.com/fishyboteso/Chalutier/raw/619b4ab0b8ff91746afda855542e886d27b7a794/Chalutier_1.1.2.zip", 112)
lam2 = ("LibAddonMenu-2.0", "https://www.esoui.com/downloads/dl7/LibAddonMenu-2.0r32.zip", 32) lam2 = ("LibAddonMenu-2.0", "https://www.esoui.com/downloads/dl7/LibAddonMenu-2.0r32.zip", 32)
fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/files/6329586/FishyQR.zip", 100)
libgps = ("LibGPS", "https://cdn.esoui.com/downloads/file601/LibGPS_3_0_3.zip", 30)

View File

@ -47,16 +47,11 @@ def _get_factor(key):
return config.get("full_auto_factors", {}).get(key) return config.get("full_auto_factors", {}).get(key)
class Calibrate: class Calibrator:
def __init__(self, engine: FullAuto): def __init__(self, engine: FullAuto):
self._callibrate_state = -1 self._callibrate_state = -1
self.engine = engine self.engine = engine
# region getters
@property
def crop(self):
return _get_factor("crop")
@property @property
def move_factor(self): def move_factor(self):
return _get_factor("move_factor") return _get_factor("move_factor")
@ -65,26 +60,15 @@ class Calibrate:
def rot_factor(self): def rot_factor(self):
return _get_factor("rot_factor") return _get_factor("rot_factor")
@property
def time_to_reach_bottom(self):
return _get_factor("time_to_reach_bottom")
# endregion # endregion
def all_callibrated(self): def all_callibrated(self):
return self.crop is not None and self.move_factor is not None and self.rot_factor is not None return self.move_factor is not None and self.rot_factor is not None
def toggle_show(self): def toggle_show(self):
self.engine.show_crop = not self.engine.show_crop self.engine.show_crop = not self.engine.show_crop
def update_crop(self, enable_crop=True): def _walk_calibrate(self):
if enable_crop:
self.engine.show_crop = True
crop = get_crop_coods(self.engine.window)
_update_factor("crop", crop)
self.engine.window.crop = crop
def walk_calibrate(self):
walking_time = 3 walking_time = 3
coods = self.engine.get_coods() coods = self.engine.get_coods()
@ -107,7 +91,7 @@ class Calibrate:
_update_factor("move_factor", move_factor) _update_factor("move_factor", move_factor)
logging.info("done") logging.info("done")
def rotate_calibrate(self): def _rotate_calibrate(self):
rotate_times = 50 rotate_times = 50
coods = self.engine.get_coods() coods = self.engine.get_coods()
@ -131,25 +115,7 @@ class Calibrate:
_update_factor("rot_factor", rot_factor) _update_factor("rot_factor", rot_factor)
logging.info("done") logging.info("done")
def time_to_reach_bottom_callibrate(self): def calibrate(self):
self._callibrate_state = 0 self._walk_calibrate()
self._rotate_calibrate()
def _f8_pressed():
self._callibrate_state += 1
logging.info("look straight up and press f8")
hotkey.set_hotkey(Key.F8, _f8_pressed)
wait_until(lambda: self._callibrate_state == 1)
logging.info("as soon as you look on the floor, press f8 again")
y_cal_start_time = time.time()
while self._callibrate_state == 1:
mse.move(0, FullAuto.rotate_by)
time.sleep(0.05)
hotkey.free_key(Key.F8)
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

@ -11,16 +11,10 @@ def get_controls(engine: FullAuto):
from fishy.engine.fullautofisher.player import Player from fishy.engine.fullautofisher.player import Player
controls = [ controls = [
("MODE_SELECT", { ("MODE_SELECT", {
Key.RIGHT: (lambda: engine.controls.select_mode("CALIBRATE"), "calibrate mode"), Key.RIGHT: (Recorder(engine).toggle_recording, "start/stop record"),
Key.UP: (lambda: engine.controls.select_mode("TEST1"), "test mode"), Key.UP: (engine.calibrator.calibrate, "calibrate mode"),
Key.LEFT: (Player(engine).toggle_move, "start/stop play"), Key.LEFT: (Player(engine).toggle_move, "start/stop play"),
Key.DOWN: (Recorder(engine).toggle_recording, "start/stop record"), Key.DOWN: (lambda: engine.controls.select_mode("TEST1"), "test mode"),
}),
("CALIBRATE", {
Key.RIGHT: (engine.calibrate.update_crop, "cropping"),
Key.UP: (engine.calibrate.walk_calibrate, "walking"),
Key.LEFT: (engine.calibrate.rotate_calibrate, "rotation"),
Key.DOWN: (engine.calibrate.time_to_reach_bottom_callibrate, "look up down")
}), }),
("TEST1", { ("TEST1", {
Key.RIGHT: (engine.test.print_coods, "print coordinates"), Key.RIGHT: (engine.test.print_coods, "print coordinates"),

View File

@ -14,8 +14,8 @@ import time
import numpy as np import numpy as np
import pytesseract import pytesseract
from fishy.engine.fullautofisher.tesseract import is_tesseract_installed, downlaoad_and_extract_tesseract, \ from fishy.constants import libgps, fishyqr, lam2
get_values_from_image from fishy.engine.fullautofisher.qr_detection import get_values_from_image, get_qr_location
from fishy.engine.semifisher.fishing_mode import FishingMode from fishy.engine.semifisher.fishing_mode import FishingMode
from fishy.engine import SemiFisherEngine from fishy.engine import SemiFisherEngine
@ -25,10 +25,9 @@ from fishy.engine.semifisher import fishing_mode, fishing_event
from fishy.engine.common.IEngine import IEngine from fishy.engine.common.IEngine import IEngine
from pynput import keyboard, mouse from pynput import keyboard, mouse
from fishy.helper import hotkey, helper from fishy.helper import hotkey, helper, hotkey_process
from fishy.helper.config import config from fishy.helper.config import config
from fishy.helper.downloader import download_file_from_google_drive from fishy.helper.helper import sign, addon_exists
from fishy.helper.helper import sign
from fishy.helper.hotkey import Key from fishy.helper.hotkey import Key
mse = mouse.Controller() mse = mouse.Controller()
@ -36,12 +35,11 @@ kb = keyboard.Controller()
def image_pre_process(img): def image_pre_process(img):
scale_percent = 200 # percent of original size scale_percent = 100 # percent of original size
width = int(img.shape[1] * scale_percent / 100) width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100) height = int(img.shape[0] * scale_percent / 100)
dim = (width, height) dim = (width, height)
img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA) img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
img = cv2.bitwise_not(img)
return img return img
@ -59,7 +57,7 @@ class FullAuto(IEngine):
def __init__(self, gui_ref): def __init__(self, gui_ref):
from fishy.engine.fullautofisher.controls import Controls from fishy.engine.fullautofisher.controls import Controls
from fishy.engine.fullautofisher import controls from fishy.engine.fullautofisher import controls
from fishy.engine.fullautofisher.calibrate import Calibrate from fishy.engine.fullautofisher.calibrator import Calibrator
from fishy.engine.fullautofisher.test import Test from fishy.engine.fullautofisher.test import Test
super().__init__(gui_ref) super().__init__(gui_ref)
@ -67,37 +65,41 @@ class FullAuto(IEngine):
self._curr_rotate_y = 0 self._curr_rotate_y = 0
self.fisher = SemiFisherEngine(None) self.fisher = SemiFisherEngine(None)
self.calibrate = Calibrate(self) self.calibrator = Calibrator(self)
self.test = Test(self) self.test = Test(self)
self.controls = Controls(controls.get_controls(self)) self.controls = Controls(controls.get_controls(self))
self.show_crop = False self.show_crop = False
def run(self): def run(self):
self.show_crop = False
addons_req = [libgps, lam2, fishyqr]
for addon in addons_req:
if not helper.addon_exists(*addon):
helper.install_addon(*addon)
FullAuto.state = State.NONE FullAuto.state = State.NONE
self.gui.bot_started(True) self.gui.bot_started(True)
fishing_event.unsubscribe()
self.fisher.toggle_start()
self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name="Full auto debug") self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name="Full auto debug")
try: try:
if self.calibrate.crop is None: self.window.crop = get_qr_location(self.window.get_capture())
self.calibrate.update_crop(enable_crop=False) if self.window.crop is None:
self.window.crop = self.calibrate.crop logging.warning("FishyQR not found")
self.start = False
raise Exception("FishyQR not found")
if not is_tesseract_installed(): if not self.calibrator.all_callibrated():
logging.info("tesseract not found") logging.error("you need to calibrate first")
downlaoad_and_extract_tesseract()
if not self.calibrate.all_callibrated(): self.fisher.toggle_start()
logging.error("you need to callibrate first") fishing_event.unsubscribe()
self.controls.initialize() self.controls.initialize()
while self.start and WindowClient.running(): while self.start and WindowClient.running():
if self.show_crop:
self.window.show(self.show_crop, func=image_pre_process) self.window.show(self.show_crop, func=image_pre_process)
if not self.show_crop: else:
time.sleep(0.1) time.sleep(0.1)
except: except:
traceback.print_exc() traceback.print_exc()
@ -121,7 +123,7 @@ class FullAuto(IEngine):
logging.error("set target first") logging.error("set target first")
return return
if not self.calibrate.all_callibrated(): if not self.calibrator.all_callibrated():
logging.error("you need to callibrate first") logging.error("you need to callibrate first")
return return
@ -140,7 +142,7 @@ class FullAuto(IEngine):
self.rotate_to(target_angle, from_angle) self.rotate_to(target_angle, from_angle)
walking_time = dist / self.calibrate.move_factor walking_time = dist / self.calibrator.move_factor
print(f"walking for {walking_time}") print(f"walking for {walking_time}")
kb.press('w') kb.press('w')
time.sleep(walking_time) time.sleep(walking_time)
@ -162,7 +164,7 @@ class FullAuto(IEngine):
if abs(angle_diff) > 180: if abs(angle_diff) > 180:
angle_diff = (360 - abs(angle_diff)) * sign(angle_diff) * -1 angle_diff = (360 - abs(angle_diff)) * sign(angle_diff) * -1
rotate_times = int(angle_diff / self.calibrate.rot_factor) * -1 rotate_times = int(angle_diff / self.calibrator.rot_factor) * -1
print(f"rotate_times: {rotate_times}") print(f"rotate_times: {rotate_times}")
@ -173,17 +175,17 @@ class FullAuto(IEngine):
def look_for_hole(self): def look_for_hole(self):
self._hole_found_flag = False self._hole_found_flag = False
if FishingMode.CurrentMode == fishing_mode.State.LOOK: if FishingMode.CurrentMode == fishing_mode.State.LOOKING:
return True return True
def found_hole(e): def found_hole(e):
if e == fishing_mode.State.LOOK: if e == fishing_mode.State.LOOKING:
self._hole_found_flag = True self._hole_found_flag = True
fishing_mode.subscribers.append(found_hole) fishing_mode.subscribers.append(found_hole)
t = 0 t = 0
while not self._hole_found_flag and t <= self.calibrate.time_to_reach_bottom / 3: while not self._hole_found_flag and t <= 1.25:
mse.move(0, FullAuto.rotate_by) mse.move(0, FullAuto.rotate_by)
time.sleep(0.05) time.sleep(0.05)
t += 0.05 t += 0.05

View File

@ -49,11 +49,14 @@ class Player:
self.hole_complete_flag = True self.hole_complete_flag = True
def _start_route(self): def _start_route(self):
FullAuto.state = State.PLAYING
timeline = _get_rec_file() timeline = _get_rec_file()
if not timeline: if not timeline:
logging.log("data not found, can't start")
return return
FullAuto.state = State.PLAYING
logging.info("starting to move")
forward = True forward = True
i = 0 i = 0
while self.start_moving_flag: while self.start_moving_flag:

View File

@ -0,0 +1,51 @@
import logging
import os
from datetime import datetime
import cv2
from fishy.helper.helper import get_documents
import numpy as np
from pyzbar.pyzbar import decode
def get_qr_location(og_img):
"""
code from https://stackoverflow.com/a/45770227/4512396
"""
gray = cv2.bilateralFilter(og_img, 11, 17, 17)
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(gray, kernel, iterations=2)
kernel = np.ones((4, 4), np.uint8)
img = cv2.dilate(erosion, kernel, iterations=2)
cnt, h = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
valid_crops = []
for i in range(len(cnt)):
area = cv2.contourArea(cnt[i])
if 500 < area < 100000:
mask = np.zeros_like(img)
cv2.drawContours(mask, cnt, i, 255, -1)
x, y, w, h = cv2.boundingRect(cnt[i])
qr_result = decode(og_img[y:h + y, x:w + x])
if qr_result:
valid_crops.append(((x, y, x + w, y + h), area))
return min(valid_crops, key=lambda c: c[1])[0] if valid_crops else None
# noinspection PyBroadException
def get_values_from_image(img):
try:
for qr in decode(img):
vals = qr.data.decode('utf-8').split(",")
return float(vals[0]), float(vals[1]), float(vals[2])
logging.error("FishyQR not found, try restarting the engine")
return None
except Exception:
logging.error("Couldn't read coods, make sure 'crop' calibration is correct")
cv2.imwrite(os.path.join(get_documents(), "fishy_failed_reads", f"{datetime.now()}.jpg"), img)
return None

View File

@ -6,8 +6,8 @@ from tkinter.filedialog import asksaveasfile
from fishy.engine.fullautofisher.engine import FullAuto, State from fishy.engine.fullautofisher.engine import FullAuto, State
from fishy.helper import hotkey
from fishy.helper.hotkey import Key from fishy.helper.hotkey import Key
from fishy.helper.hotkey_process import HotKey
class Recorder: class Recorder:
@ -35,7 +35,8 @@ class Recorder:
def _start_recording(self): def _start_recording(self):
FullAuto.state = State.RECORDING FullAuto.state = State.RECORDING
logging.info("starting, press f8 to mark hole") logging.info("starting, press f8 to mark hole")
hotkey.set_hotkey(Recorder.mark_hole_key, self._mark_hole) hk = HotKey()
hk.start_process(self._mark_hole)
self.timeline = [] self.timeline = []
@ -52,7 +53,7 @@ class Recorder:
else: else:
logging.warning("Took too much time to record") logging.warning("Took too much time to record")
hotkey.free_key(Recorder.mark_hole_key) hk.stop()
def func(): def func():
_file = None _file = None

View File

@ -1,50 +0,0 @@
import logging
import os
import tempfile
import uuid
from datetime import datetime
from zipfile import ZipFile
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")
def downlaoad_and_extract_tesseract():
logging.info("Tesseract-OCR downlaoding, Please wait...")
f = tempfile.NamedTemporaryFile(delete=False)
download_file_from_google_drive("16llzcBlaCsG9fm-rY2dD4Gvopnhm3XoE", f)
f.close()
logging.info("Tesseract-OCR downloaded, now installing")
with ZipFile(f.name, 'r') as z:
z.extractall(path=directory)
logging.info("Tesseract-OCR installed")
def is_tesseract_installed():
return os.path.exists(os.path.join(os.environ["APPDATA"], "Tesseract-OCR"))
# noinspection PyBroadException
def get_values_from_image(img):
try:
pytesseract.pytesseract.tesseract_cmd = directory + '/tesseract.exe'
tessdata_dir_config = f'--tessdata-dir "{directory}" -c tessedit_char_whitelist=0123456789.'
text = pytesseract.image_to_string(img, lang="eng", config=tessdata_dir_config)
text = text.replace(" ", "")
vals = text.split(":")
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(os.path.join(get_documents(), "fishy_failed_reads", f"{datetime.now()}.jpg"), img)
return None

View File

@ -39,6 +39,7 @@ class SemiFisherEngine(IEngine):
if self.get_gui: if self.get_gui:
logging.info("Starting the bot engine, look at the fishing hole to start fishing") logging.info("Starting the bot engine, look at the fishing hole to start fishing")
Thread(target=self._wait_and_check).start() Thread(target=self._wait_and_check).start()
while self.start and WindowClient.running(): while self.start and WindowClient.running():
capture = self.fishPixWindow.get_capture() capture = self.fishPixWindow.get_capture()
@ -59,7 +60,7 @@ class SemiFisherEngine(IEngine):
def _wait_and_check(self): def _wait_and_check(self):
time.sleep(10) time.sleep(10)
if not FishEvent.FishingStarted and self.start: if not FishEvent.FishingStarted and self.start:
logging.warn("Doesn't look like fishing has started \nCheck out #read-me-first on our discord channel to troubleshoot the issue") logging.warning("Doesn't look like fishing has started \nCheck out #read-me-first on our discord channel to troubleshoot the issue")
def show_pixel_vals(self): def show_pixel_vals(self):
def show(): def show():

View File

@ -1,31 +0,0 @@
import requests
def download_file_from_google_drive(id, file):
URL = "https://docs.google.com/uc?export=download"
session = requests.Session()
response = session.get(URL, params={'id': id}, stream=True)
token = get_confirm_token(response)
if token:
params = {'id': id, 'confirm': token}
response = session.get(URL, params=params, stream=True)
save_response_content(response, file)
def get_confirm_token(response):
for key, value in response.cookies.items():
if key.startswith('download_warning'):
return value
return None
def save_response_content(response, f):
CHUNK_SIZE = 32768
for chunk in response.iter_content(CHUNK_SIZE):
if chunk: # filter out keep-alive new chunks
f.write(chunk)

View File

@ -0,0 +1,46 @@
import time
from threading import Thread
import mouse
from multiprocessing import Process, Queue
def event_triggered(queue, e):
if not (type(e) == mouse.ButtonEvent and e.event_type == "up" and e.button == "left"):
return
# call the parent function here
queue.put("left click")
def run(inq, outq):
mouse.hook(lambda e: event_triggered(outq, e))
stop = False
while not stop:
time.sleep(1)
if inq.get() == "stop":
stop = True
class HotKey:
def __init__(self):
self.inq = Queue()
self.outq = Queue()
self.process = Process(target=run, args=(self.inq, self.outq))
def event_loop(self, func):
while True:
msg = self.outq.get()
if msg == "left click":
func()
def start_process(self, func):
self.process.start()
Thread(target=self.event_loop, args=(func,)).start()
def stop(self):
self.inq.put("stop")
self.process.join()
print("hotkey process ended")

View File

@ -10,7 +10,8 @@ requests
beautifulsoup4 beautifulsoup4
whatsmyip whatsmyip
pynput pynput
pytesseract
keyboard keyboard
playsound playsound
event-scheduler event-scheduler
pyzbar
mouse