Merge pull request #76 from fishyboteso/feature/fullauto

Full auto improvements
This commit is contained in:
Adam Saudagar 2021-05-15 18:31:49 +05:30 committed by GitHub
commit 381f573109
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 491 additions and 256 deletions

View File

@ -5,8 +5,29 @@ from fishy.engine import SemiFisherEngine
from fishy.engine.fullautofisher.engine import FullAuto
class EngineEventHandler:
class IEngineHandler:
def __init__(self):
...
def start_event_handler(self):
...
def toggle_semifisher(self):
...
def toggle_fullfisher(self):
...
def check_pixel_val(self):
...
def quit(self):
...
class EngineEventHandler(IEngineHandler):
def __init__(self, gui_ref):
super().__init__()
self.event_handler_running = True
self.event = []

View File

@ -1,26 +1,16 @@
import logging
from fishy.engine.fullautofisher.engine import FullAuto, State
from pynput.keyboard import Key
from fishy.helper import hotkey
from fishy.helper.hotkey import Key
def get_controls(engine: FullAuto):
from fishy.engine.fullautofisher.player import Player
from fishy.engine.fullautofisher.recorder import Recorder
def get_controls(controls: 'Controls'):
controls = [
("MODE_SELECT", {
Key.RIGHT: (Recorder(engine).toggle_recording, "start/stop record"),
Key.UP: (engine.calibrator.calibrate, "calibrate mode"),
Key.LEFT: (Player(engine).toggle_move, "start/stop play"),
Key.DOWN: (lambda: engine.controls.select_mode("TEST1"), "test mode"),
Key.DOWN: (lambda: controls.select_mode("TEST1"), "test mode"),
}),
("TEST1", {
Key.RIGHT: (engine.test.print_coods, "print coordinates"),
Key.UP: (engine.test.look_for_hole, "look for hole up down"),
Key.LEFT: (engine.test.set_target, "set target"),
Key.DOWN: (engine.test.move_to_target, "move to target")
})
("TEST1", {})
]
return controls
@ -44,10 +34,6 @@ class Controls:
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]:

View File

@ -1,8 +1,8 @@
import math
import logging
import math
import time
import traceback
from enum import Enum
from threading import Thread
import cv2
@ -12,11 +12,17 @@ from fishy.constants import fishyqr, lam2, libgps
from fishy.engine import SemiFisherEngine
from fishy.engine.common.IEngine import IEngine
from fishy.engine.common.window import WindowClient
from fishy.engine.fullautofisher.mode.calibrator import Calibrator
from fishy.engine.fullautofisher.mode.imode import FullAutoMode
from fishy.engine.fullautofisher.mode.player import Player
from fishy.engine.fullautofisher.mode.recorder import Recorder
from fishy.engine.fullautofisher.qr_detection import (get_qr_location,
get_values_from_image)
from fishy.engine.semifisher import fishing_event, fishing_mode
from fishy.engine.semifisher.fishing_mode import FishingMode
from fishy.helper import helper, hotkey
from fishy.helper.config import config
from fishy.helper.helper import log_raise, wait_until, is_eso_active
from fishy.helper.helper import sign
mse = mouse.Controller()
@ -32,21 +38,10 @@ def image_pre_process(img):
return img
class State(Enum):
NONE = 0
PLAYING = 1
RECORDING = 2
OTHER = 3
class FullAuto(IEngine):
rotate_by = 30
state = State.NONE
def __init__(self, gui_ref):
from fishy.engine.fullautofisher import controls
from fishy.engine.fullautofisher.calibrator import Calibrator
from fishy.engine.fullautofisher.controls import Controls
from fishy.engine.fullautofisher.test import Test
super().__init__(gui_ref)
@ -56,9 +51,10 @@ class FullAuto(IEngine):
self.fisher = SemiFisherEngine(None)
self.calibrator = Calibrator(self)
self.test = Test(self)
self.controls = Controls(controls.get_controls(self))
self.show_crop = False
self.mode = None
def run(self):
addons_req = [libgps, lam2, fishyqr]
@ -66,57 +62,81 @@ class FullAuto(IEngine):
if not helper.addon_exists(*addon):
helper.install_addon(*addon)
FullAuto.state = State.NONE
self.gui.bot_started(True)
self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name="Full auto debug")
self.mode = None
if config.get("calibrate", False):
self.mode = Calibrator(self)
elif FullAutoMode(config.get("full_auto_mode", 0)) == FullAutoMode.Player:
self.mode = Player(self)
elif FullAutoMode(config.get("full_auto_mode", 0)) == FullAutoMode.Recorder:
self.mode = Recorder(self)
if not is_eso_active():
logging.info("Waiting for eso window to be active...")
wait_until(lambda: is_eso_active() or not self.start)
if self.start:
logging.info("starting in 2 secs...")
time.sleep(2)
# noinspection PyBroadException
try:
if self.window.get_capture() is None:
log_raise("Game window not found")
self.window.crop = get_qr_location(self.window.get_capture())
if self.window.crop is None:
logging.warning("FishyQR not found")
self.start = False
raise Exception("FishyQR not found")
log_raise("FishyQR not found")
if not self.calibrator.all_callibrated():
logging.error("you need to calibrate first")
if not self.calibrator.all_calibrated():
log_raise("you need to calibrate first")
self.fisher.toggle_start()
fishing_event.unsubscribe()
if self.show_crop:
self.start_show()
self.stop_on_inactive()
self.controls.initialize()
while self.start and WindowClient.running():
if self.show_crop:
self.window.show(self.show_crop, func=image_pre_process)
else:
time.sleep(0.1)
except:
self.mode.run()
except Exception:
traceback.print_exc()
if self.window.get_capture() is None:
logging.error("Game window not found")
self.start = False
self.gui.bot_started(False)
self.controls.unassign_keys()
self.window.show(False)
logging.info("Quitting")
self.window.destory()
self.fisher.toggle_start()
def get_coods(self):
def start_show(self):
def func():
while self.start and WindowClient.running():
self.window.show(self.show_crop, func=image_pre_process)
Thread(target=func).start()
def stop_on_inactive(self):
def func():
wait_until(lambda: not is_eso_active())
self.start = False
Thread(target=func).start()
def get_coords(self):
"""
There is chance that this function give None instead of a QR.
Need to handle manually
todo find a better way of handling None: switch from start bool to state which knows
todo its waiting for qr which doesn't block the engine when commanded to close
"""
img = self.window.processed_image(func=image_pre_process)
return get_values_from_image(img)
def move_to(self, target):
if target is None:
logging.error("set target first")
return
def move_to(self, target) -> bool:
current = self.get_coords()
if not current:
return False
if not self.calibrator.all_callibrated():
logging.error("you need to callibrate first")
return
current = self.get_coods()
print(f"Moving from {(current[0], current[1])} to {target}")
move_vec = target[0] - current[0], target[1] - current[1]
@ -124,12 +144,13 @@ class FullAuto(IEngine):
print(f"distance: {dist}")
if dist < 5e-05:
print("distance very small skipping")
return
return True
target_angle = math.degrees(math.atan2(-move_vec[1], move_vec[0])) + 90
from_angle = current[2]
self.rotate_to(target_angle, from_angle)
if not self.rotate_to(target_angle, from_angle):
return False
walking_time = dist / self.calibrator.move_factor
print(f"walking for {walking_time}")
@ -137,10 +158,14 @@ class FullAuto(IEngine):
time.sleep(walking_time)
kb.release('w')
print("done")
return True
def rotate_to(self, target_angle, from_angle=None):
def rotate_to(self, target_angle, from_angle=None) -> bool:
if from_angle is None:
_, _, from_angle = self.get_coods()
coords = self.get_coords()
if not coords:
return False
_, _, from_angle = coords
if target_angle < 0:
target_angle = 360 + target_angle
@ -161,7 +186,9 @@ class FullAuto(IEngine):
mse.move(sign(rotate_times) * FullAuto.rotate_by * -1, 0)
time.sleep(0.05)
def look_for_hole(self):
return True
def look_for_hole(self) -> bool:
self._hole_found_flag = False
if FishingMode.CurrentMode == fishing_mode.State.LOOKING:
@ -194,10 +221,6 @@ class FullAuto(IEngine):
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)

View File

@ -1,12 +1,17 @@
import logging
import math
import time
import typing
import cv2
import numpy as np
if typing.TYPE_CHECKING:
from fishy.engine.fullautofisher.engine import FullAuto
from fishy.engine.fullautofisher.mode.imode import IMode
from pynput import keyboard, mouse
from fishy.engine.fullautofisher.engine import FullAuto
from fishy.helper.config import config
mse = mouse.Controller()
@ -43,8 +48,8 @@ def _get_factor(key):
return config.get("full_auto_factors", {}).get(key)
class Calibrator:
def __init__(self, engine: FullAuto):
class Calibrator(IMode):
def __init__(self, engine: 'FullAuto'):
self._callibrate_state = -1
self.engine = engine
@ -58,8 +63,11 @@ class Calibrator:
# endregion
def all_callibrated(self):
return self.move_factor is not None and self.rot_factor is not None
def all_calibrated(self):
return self.move_factor is not None and \
self.rot_factor is not None and \
self.move_factor != 0 and \
self.rot_factor != 0
def toggle_show(self):
self.engine.show_crop = not self.engine.show_crop
@ -67,7 +75,7 @@ class Calibrator:
def _walk_calibrate(self):
walking_time = 3
coods = self.engine.get_coods()
coods = self.engine.get_coords()
if coods is None:
return
@ -78,19 +86,21 @@ class Calibrator:
kb.release('w')
time.sleep(0.5)
coods = self.engine.get_coods()
coods = self.engine.get_coords()
if coods is None:
return
x2, y2, rot2 = coods
move_factor = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) / walking_time
_update_factor("move_factor", move_factor)
logging.info("done")
logging.info("walk calibrate done")
def _rotate_calibrate(self):
from fishy.engine.fullautofisher.engine import FullAuto
rotate_times = 50
coods = self.engine.get_coods()
coods = self.engine.get_coords()
if coods is None:
return
_, _, rot2 = coods
@ -99,7 +109,7 @@ class Calibrator:
mse.move(FullAuto.rotate_by, 0)
time.sleep(0.05)
coods = self.engine.get_coods()
coods = self.engine.get_coords()
if coods is None:
return
x3, y3, rot3 = coods
@ -109,8 +119,10 @@ class Calibrator:
rot_factor = (rot3 - rot2) / rotate_times
_update_factor("rot_factor", rot_factor)
logging.info("done")
logging.info("rotate calibrate done")
def calibrate(self):
def run(self):
self._walk_calibrate()
self._rotate_calibrate()
config.set("calibrate", False)
logging.info("calibration done")

View File

@ -0,0 +1,14 @@
from abc import ABC, abstractmethod
from enum import Enum
class FullAutoMode(Enum):
Player = 0
Recorder = 1
class IMode(ABC):
@abstractmethod
def run(self):
...

View File

@ -0,0 +1,119 @@
import logging
import math
import pickle
import time
from pprint import pprint
import typing
from threading import Thread
from fishy.engine.fullautofisher.mode.imode import IMode
from fishy.engine.semifisher import fishing_event, fishing_mode
from fishy.helper.helper import log_raise, wait_until, kill_thread
if typing.TYPE_CHECKING:
from fishy.engine.fullautofisher.engine import FullAuto
from fishy.helper import helper
from fishy.helper.config import config
def get_rec_file():
file = config.get("full_auto_rec_file")
if not file:
logging.error("Please select a fishy file first from config")
return None
file = open(file, 'rb')
data = pickle.load(file)
file.close()
if "full_auto_path" not in data:
logging.error("invalid file")
return None
return data["full_auto_path"]
def find_nearest(timeline, current):
"""
:param timeline: recording timeline
:param current: current coord
:return: Tuple[index, distance, target_coord]
"""
distances = [(i, math.sqrt((target[0] - current[0]) ** 2 + (target[1] - current[1]) ** 2), target)
for i, (command, target) in enumerate(timeline) if command == "move_to"]
return min(distances, key=lambda d: d[1])
class Player(IMode):
def __init__(self, engine: 'FullAuto'):
self.recording = False
self.engine = engine
self.hole_complete_flag = False
self.start_moving_flag = False
self.i = 0
self.forward = True
self.timeline = None
def run(self):
self._init()
while self.engine.start:
self._loop()
time.sleep(0.1)
logging.info("player stopped")
def _init(self):
self.timeline = get_rec_file()
if not self.timeline:
log_raise("data not found, can't start")
logging.info("starting player")
coords = self.engine.get_coords()
if not coords:
log_raise("QR not found")
self.i = find_nearest(self.timeline, coords)[0]
def _loop(self):
action = self.timeline[self.i]
if action[0] == "move_to":
if not self.engine.move_to(action[1]):
return
elif action[0] == "check_fish":
if not self.engine.move_to(action[1]):
return
if not self.engine.rotate_to(action[1][2]):
return
fishing_mode.subscribers.append(self._hole_complete_callback)
fishing_event.subscribe()
# scan for fish hole
logging.info("scanning")
# if found start fishing and wait for hole to complete
if self.engine.look_for_hole():
logging.info("starting fishing")
self.hole_complete_flag = False
helper.wait_until(lambda: self.hole_complete_flag or not self.engine.start)
self.engine.rotate_back()
else:
logging.info("no hole found")
# continue when hole completes
fishing_mode.subscribers.remove(self._hole_complete_callback)
fishing_event.unsubscribe()
self.next()
def next(self):
self.i += 1 if self.forward else -1
if self.i >= len(self.timeline):
self.forward = False
self.i = len(self.timeline) - 1
elif self.i < 0:
self.forward = True
self.i = 0
def _hole_complete_callback(self, e):
if e == fishing_event.State.IDLE:
self.hole_complete_flag = True

View File

@ -0,0 +1,149 @@
import logging
import os
import pickle
import time
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import askyesno
from typing import List, Optional
import typing
from tkinter.filedialog import asksaveasfile
from fishy.engine.fullautofisher.mode import player
from fishy.helper import helper
from fishy.helper.helper import empty_function, log_raise
from fishy.helper.popup import PopUp
from playsound import playsound
from fishy.helper.config import config
if typing.TYPE_CHECKING:
from fishy.engine.fullautofisher.engine import FullAuto
from fishy.engine.fullautofisher.mode.imode import IMode
from fishy.helper.hotkey import Key
from fishy.helper.hotkey_process import HotKey
class Recorder(IMode):
recording_fps = 1
mark_hole_key = Key.F8
def __init__(self, engine: 'FullAuto'):
self.recording = False
self.engine = engine
self.timeline = []
def _mark_hole(self):
coods = self.engine.get_coords()
if not coods:
logging.warning("QR not found, couldn't record hole")
return
self.timeline.append(("check_fish", coods))
playsound(helper.manifest_file("beep.wav"), False)
logging.info("check_fish")
def run(self):
old_timeline: Optional[List] = None
start_from = None
if config.get("edit_recorder_mode"):
logging.info("moving to nearest coord in recording")
old_timeline = player.get_rec_file()
coords = self.engine.get_coords()
if not coords:
log_raise("QR not found")
start_from = player.find_nearest(old_timeline, coords)
if not self.engine.move_to(start_from[2]):
log_raise("QR not found")
logging.info("starting, press LMB to mark hole")
hk = HotKey()
hk.start_process(self._mark_hole)
self.timeline = []
while self.engine.start:
start_time = time.time()
coods = self.engine.get_coords()
if not coods:
time.sleep(0.1)
continue
self.timeline.append(("move_to", (coods[0], coods[1])))
time_took = time.time() - start_time
if time_took <= Recorder.recording_fps:
time.sleep(Recorder.recording_fps - time_took)
else:
logging.warning("Took too much time to record")
hk.stop()
if config.get("edit_recorder_mode"):
logging.info("moving to nearest coord in recording")
# todo allow the user the chance to wait for qr
coords = self.engine.get_coords()
if not coords:
log_raise("QR not found")
end = player.find_nearest(old_timeline, coords)
self.engine.move_to(end[2])
part1 = old_timeline[:start_from[0]]
part2 = old_timeline[end[0]:]
self.timeline = part1 + self.timeline + part2
self._ask_to_save()
def _open_save_popup(self):
top = PopUp(empty_function, self.engine.get_gui()._root, background=self.engine.get_gui()._root["background"])
controls_frame = ttk.Frame(top)
top.title("Save Recording?")
button = [-1]
def button_pressed(_button):
button[0] = _button
top.quit_top()
selected_text = f"\n\nSelected: {os.path.basename(config.get('full_auto_rec_file'))}" if config.get('full_auto_rec_file') else ""
ttk.Label(controls_frame, text=f"Do you want to save the recording?{selected_text}").grid(row=0, column=0, columnspan=3, pady=(0, 5))
_overwrite = tk.NORMAL if config.get("full_auto_rec_file") else tk.DISABLED
ttk.Button(controls_frame, text="Overwrite", command=lambda: button_pressed(0), state=_overwrite).grid(row=1, column=0, pady=(5, 0))
ttk.Button(controls_frame, text="Save As", command=lambda: button_pressed(1)).grid(row=1, column=1)
ttk.Button(controls_frame, text="Cancel", command=lambda: button_pressed(2)).grid(row=1, column=2)
controls_frame.pack(padx=(5, 5), pady=(5, 5))
top.start()
return button[0]
def _ask_to_save(self):
def func():
_file = None
files = [('Fishy File', '*.fishy')]
while True:
button = self._open_save_popup()
if button == 0 and config.get("full_auto_rec_file"):
return open(config.get("full_auto_rec_file"), 'wb')
if button == 1:
_file = asksaveasfile(mode='wb', filetypes=files, defaultextension=files)
if _file:
return _file
if button == 2:
return None
file: typing.BinaryIO = self.engine.get_gui().call_in_thread(func, block=True)
if not file:
return
data = {"full_auto_path": self.timeline}
pickle.dump(data, file)
config.set("full_auto_rec_file", file.name)
logging.info(f"saved {os.path.basename(file.name)} recording, and loaded it in player")
file.close()

View File

@ -1,93 +0,0 @@
import logging
import pickle
from pprint import pprint
from fishy.engine.fullautofisher.engine import FullAuto, State
from fishy.engine.semifisher import fishing_event, fishing_mode
from fishy.helper import helper
from fishy.helper.config import config
def _get_rec_file():
file = config.get("full_auto_rec_file")
if not file:
logging.error("Please select a fishy file first from config")
return None
file = open(file, 'rb')
data = pickle.load(file)
file.close()
pprint(data)
if "full_auto_path" not in data:
logging.error("invalid file")
return None
return data["full_auto_path"]
class Player:
def __init__(self, engine: 'FullAuto'):
self.recording = False
self.engine = engine
self.hole_complete_flag = False
self.start_moving_flag = False
def toggle_move(self):
if FullAuto.state != State.PLAYING and FullAuto.state != State.NONE:
return
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:
self.hole_complete_flag = True
def _start_route(self):
timeline = _get_rec_file()
if not timeline:
logging.log("data not found, can't start")
return
FullAuto.state = State.PLAYING
logging.info("starting to move")
forward = True
i = 0
while self.start_moving_flag:
action = timeline[i]
if action[0] == "move_to":
self.engine.move_to(action[1])
elif action[0] == "check_fish":
self.engine.move_to(action[1])
self.engine.rotate_to(action[1][2])
fishing_event.subscribe()
fishing_mode.subscribers.append(self._hole_complete_callback)
# scan for fish hole
logging.info("scanning")
if self.engine.look_for_hole():
logging.info("starting fishing")
self.hole_complete_flag = False
helper.wait_until(lambda: self.hole_complete_flag or not self.start_moving_flag)
self.engine.rotate_back()
else:
logging.info("no hole found")
# if found start fishing and wait for hole to complete
# contine when hole completes
fishing_event.unsubscribe()
fishing_mode.subscribers.remove(self._hole_complete_callback)
i += 1 if forward else -1
if i >= len(timeline):
forward = False
i = len(timeline) - 1
elif i < 0:
forward = True
i = 0
logging.info("stopped")
FullAuto.state = State.NONE

View File

@ -42,7 +42,7 @@ def get_values_from_image(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")
logging.error("FishyQR not found")
return None
except Exception:
logging.error("Couldn't read coods, make sure 'crop' calibration is correct")

View File

@ -1,70 +0,0 @@
import logging
import pickle
import time
from pprint import pprint
from tkinter.filedialog import asksaveasfile
from fishy.engine.fullautofisher.engine import FullAuto, State
from fishy.helper.hotkey import Key
from fishy.helper.hotkey_process import HotKey
class Recorder:
recording_fps = 1
mark_hole_key = Key.F8
def __init__(self, engine: FullAuto):
self.recording = False
self.engine = engine
self.timeline = []
def _mark_hole(self):
coods = self.engine.get_coods()
self.timeline.append(("check_fish", coods))
logging.info("check_fish")
def toggle_recording(self):
if FullAuto.state != State.RECORDING and FullAuto.state != State.NONE:
return
self.recording = not self.recording
if self.recording:
self._start_recording()
def _start_recording(self):
FullAuto.state = State.RECORDING
logging.info("starting, press f8 to mark hole")
hk = HotKey()
hk.start_process(self._mark_hole)
self.timeline = []
while self.recording:
start_time = time.time()
coods = None
while not coods:
coods = self.engine.get_coods()
self.timeline.append(("move_to", (coods[0], coods[1])))
time_took = time.time() - start_time
if time_took <= Recorder.recording_fps:
time.sleep(Recorder.recording_fps - time_took)
else:
logging.warning("Took too much time to record")
hk.stop()
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)
file.close()
FullAuto.state = State.NONE

View File

@ -8,11 +8,12 @@ class Test:
self.engine = engine
self.target = None
# noinspection PyProtectedMember
def print_coods(self):
logging.info(self.engine.get_coods())
logging.info(self.engine.get_coords())
def set_target(self):
self.target = self.engine.get_coods()
self.target = self.engine.get_coords()
logging.info(f"target_coods are {self.target}")
def move_to_target(self):

View File

@ -9,13 +9,13 @@ import time
import keyboard
from playsound import playsound
from win32gui import GetForegroundWindow, GetWindowText
from fishy import web
from fishy.engine.semifisher import fishing_mode
from fishy.engine.semifisher.fishing_mode import FishingMode, State
from fishy.helper import helper
from fishy.helper.config import config
from fishy.helper.helper import is_eso_active
class FishEvent:
@ -44,7 +44,7 @@ def _fishing_sleep(waittime, lower_limit_ms=16, upper_limit_ms=2500):
def if_eso_is_focused(func):
def wrapper():
if GetWindowText(GetForegroundWindow()) != "Elder Scrolls Online":
if not is_eso_active():
logging.warning("ESO window is not focused")
return
func()

View File

@ -5,6 +5,10 @@ import tkinter.ttk as ttk
import typing
from tkinter.filedialog import askopenfilename
from fishy.engine.common.event_handler import IEngineHandler
from fishy.engine.fullautofisher.mode.imode import FullAutoMode
from fishy.helper import helper
from fishy import web
from fishy.helper import helper
from fishy.helper.config import config
@ -35,10 +39,37 @@ def start_fullfisher_config(gui: 'GUI'):
file_name_label.set(file_name())
def start_calibrate():
top.quit_top()
config.set("calibrate", True)
gui.engine.toggle_fullfisher()
def mode_command():
config.set("full_auto_mode", mode_var.get())
edit_cb['state'] = "normal" if config.get("full_auto_mode", 0) == FullAutoMode.Recorder.value else "disable"
def edit_mode_changed():
config.set("edit_recorder_mode", edit_var.get())
file_name_label = tk.StringVar(value=file_name())
ttk.Label(controls_frame, textvariable=file_name_label).grid(row=0, column=0)
ttk.Button(controls_frame, text="Select fishy file", command=select_file).grid(row=0, column=1)
ttk.Label(controls_frame, text="Use semi-fisher config for rest").grid(row=2, column=0, columnspan=2)
mode_var = tk.IntVar(value=config.get("full_auto_mode", 0))
edit_var = tk.IntVar(value=config.get("edit_recorder_mode", 0))
ttk.Button(controls_frame, text="Calibrate", command=start_calibrate).grid(row=0, column=0, columnspan=2, pady=(5, 5))
ttk.Label(controls_frame, text="Mode: ").grid(row=1, column=0, pady=(5, 0))
ttk.Radiobutton(controls_frame, text="Player", variable=mode_var, value=FullAutoMode.Player.value, command=mode_command).grid(row=1, column=1, sticky="w")
ttk.Radiobutton(controls_frame, text="Recorder", variable=mode_var, value=FullAutoMode.Recorder.value, command=mode_command).grid(row=2, column=1, sticky="w", pady=(0, 5))
ttk.Label(controls_frame, text="Edit Mode: ").grid(row=3, column=0, pady=(5, 5))
edit_state = tk.NORMAL if config.get("full_auto_mode", 0) == FullAutoMode.Recorder.value else tk.DISABLED
edit_cb = ttk.Checkbutton(controls_frame, variable=edit_var, state=edit_state, command=edit_mode_changed)
edit_cb.grid(row=3, column=1, pady=(5, 5))
ttk.Button(controls_frame, text="Select fishy file", command=select_file).grid(row=4, column=0, columnspan=2, pady=(5, 0))
ttk.Label(controls_frame, textvariable=file_name_label).grid(row=5, column=0, columnspan=2, pady=(0, 5))
ttk.Label(controls_frame, text="Use semi-fisher config for rest").grid(row=6, column=0, columnspan=2, pady=(15, 5))
controls_frame.pack(padx=(5, 5), pady=(5, 5))
top.start()
@ -106,3 +137,10 @@ def start_semifisher_config(gui: 'GUI'):
controls_frame.pack(padx=(5, 5), pady=(5, 5))
top.start()
if __name__ == '__main__':
from fishy.gui import GUI
gui = GUI(lambda: IEngineHandler())
gui.call_in_thread(lambda: start_fullfisher_config(gui))
gui.create()

View File

@ -7,7 +7,7 @@ from typing import Any, Callable, Dict, Optional
from ttkthemes import ThemedTk
from fishy.engine.common.event_handler import EngineEventHandler
from fishy.engine.common.event_handler import EngineEventHandler, IEngineHandler
from fishy.gui import config_top
from fishy.gui.funcs import GUIFuncs
from fishy.web import web
@ -19,7 +19,7 @@ from .log_config import GUIStreamHandler
class GUI:
def __init__(self, get_engine: Callable[[], EngineEventHandler]):
def __init__(self, get_engine: Callable[[], IEngineHandler]):
self.funcs = GUIFuncs(self)
self.get_engine = get_engine

View File

@ -106,7 +106,7 @@ class config:
:param default: default value to return if key is not found
:return: config value
"""
return default if config._instance[key] is None else config._instance[key]
return default if config._instance is None or config._instance[key] is None else config._instance[key]
@staticmethod
def set(key, value, save=True):
@ -116,6 +116,9 @@ class config:
:param value: value to save
:param save: False if don't want to save right away
"""
if config._instance is None:
return
config._instance[key] = value
if save:
config.save_config()
@ -134,4 +137,6 @@ class config:
@staticmethod
def save_config():
if config._instance is None:
return
config._instance.save_config()

View File

@ -1,3 +1,4 @@
import ctypes
import logging
import os
import shutil
@ -17,6 +18,7 @@ import winshell
from playsound import playsound
from win32com.client import Dispatch
from win32comext.shell import shell, shellcon
from win32gui import GetForegroundWindow, GetWindowText
import fishy
from fishy import web
@ -222,8 +224,36 @@ def restart():
os.execl(sys.executable, *([sys.executable] + sys.argv))
def log_raise(msg):
logging.error(msg)
raise Exception(msg)
def update():
from .config import config
config.delete("dont_ask_update")
restart()
def is_eso_active():
return GetWindowText(GetForegroundWindow()) == "Elder Scrolls Online"
# noinspection PyProtectedMember,PyUnresolvedReferences
def _get_id(thread):
# returns id of the respective thread
if hasattr(thread, '_thread_id'):
return thread._thread_id
for id, thread in threading._active.items():
if thread is thread:
return id
def kill_thread(thread):
thread_id = _get_id(thread)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')