Skip to content

Commit

Permalink
Merge pull request #253 from Pbatch/wikified-errors
Browse files Browse the repository at this point in the history
add wikified errors in the whole project + pylint (my eyes bleed)
  • Loading branch information
Leviaria committed Sep 19, 2024
2 parents 229806f + 896b8d9 commit 562cb0f
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 90 deletions.
3 changes: 0 additions & 3 deletions clashroyalebuildabot/actions/generic/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ def __init__(self, index, tile_x, tile_y):
self.tile_x = tile_x
self.tile_y = tile_y

if self.CARD is None:
raise ValueError("Each action must set the CARD attribute")

def __repr__(self):
return f"{self.CARD.name} at ({self.tile_x}, {self.tile_y})"

Expand Down
12 changes: 10 additions & 2 deletions clashroyalebuildabot/bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from clashroyalebuildabot.emulator.emulator import Emulator
from clashroyalebuildabot.namespaces import Screens
from clashroyalebuildabot.visualizer import Visualizer
from error_handling import WikifiedError

pause_event = threading.Event()
pause_event.set()
Expand All @@ -42,7 +43,9 @@ def __init__(self, actions, config):

cards = [action.CARD for action in actions]
if len(cards) != 8:
raise ValueError(f"Must provide 8 cards but was given: {cards}")
raise WikifiedError(
"005", f"Must provide 8 cards but {len(cards)} was given"
)
self.cards_to_actions = dict(zip(cards, actions))

self.visualizer = Visualizer(**config["visuals"])
Expand Down Expand Up @@ -148,7 +151,7 @@ def play_action(self, action):
self.emulator.click(*card_centre)
self.emulator.click(*tile_centre)

def step(self):
def _handle_play_pause_in_step(self):
if not pause_event.is_set():
if not Bot.is_paused_logged:
logger.info("Bot paused.")
Expand All @@ -159,6 +162,8 @@ def step(self):
logger.info("Bot resumed.")
Bot.is_resumed_logged = True

def step(self):
self._handle_play_pause_in_step()
old_screen = self.state.screen if self.state else None
self.set_state()
new_screen = self.state.screen
Expand All @@ -184,6 +189,9 @@ def step(self):
self._log_and_wait("Starting game", 2)
return

self._handle_game_step()

def _handle_game_step(self):
actions = self.get_actions()
if not actions:
self._log_and_wait("No actions available", self.play_action_delay)
Expand Down
21 changes: 13 additions & 8 deletions clashroyalebuildabot/detectors/card_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from clashroyalebuildabot.constants import CARD_CONFIG
from clashroyalebuildabot.constants import IMAGES_DIR
from clashroyalebuildabot.namespaces.cards import Cards
from error_handling import WikifiedError


class CardDetector:
Expand Down Expand Up @@ -53,15 +54,19 @@ def _calculate_card_hashes(self):
),
dtype=np.float32,
)
for i, card in enumerate(self.cards):
path = os.path.join(IMAGES_DIR, "cards", f"{card.name}.jpg")
pil_image = Image.open(path)

multi_hash = self._calculate_multi_hash(pil_image)
card_hashes[i] = np.tile(
np.expand_dims(multi_hash, axis=2), (1, 1, self.HAND_SIZE)
)
try:
for i, card in enumerate(self.cards):
path = os.path.join(IMAGES_DIR, "cards", f"{card.name}.jpg")
pil_image = Image.open(path)

multi_hash = self._calculate_multi_hash(pil_image)
card_hashes[i] = np.tile(
np.expand_dims(multi_hash, axis=2), (1, 1, self.HAND_SIZE)
)
except Exception as e:
raise WikifiedError(
"005", "Can't load cards and their images."
) from e
return card_hashes

def _detect_cards(self, image):
Expand Down
5 changes: 3 additions & 2 deletions clashroyalebuildabot/detectors/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
from clashroyalebuildabot.detectors.screen_detector import ScreenDetector
from clashroyalebuildabot.detectors.unit_detector import UnitDetector
from clashroyalebuildabot.namespaces import State
from error_handling import WikifiedError


class Detector:
DECK_SIZE = 8

def __init__(self, cards):
if len(cards) != self.DECK_SIZE:
raise ValueError(
f"You must specify all {self.DECK_SIZE} of your cards"
raise WikifiedError(
"005", f"You must specify all {self.DECK_SIZE} of your cards"
)

self.cards = deepcopy(cards)
Expand Down
59 changes: 33 additions & 26 deletions clashroyalebuildabot/emulator/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from clashroyalebuildabot.constants import EMULATOR_DIR
from clashroyalebuildabot.constants import SCREENSHOT_HEIGHT
from clashroyalebuildabot.constants import SCREENSHOT_WIDTH
from error_handling import WikifiedError


class Emulator:
Expand Down Expand Up @@ -67,7 +68,9 @@ def _get_valid_device_serial(self):
]

if not available_devices:
raise RuntimeError("No connected devices found") from e
raise WikifiedError(
"006", "No connected devices found"
) from e

fallback_device_serial = available_devices[0]
logger.info(
Expand All @@ -78,8 +81,8 @@ def _get_valid_device_serial(self):
logger.error(
f"Failed to execute adb devices: {str(adb_error)}"
)
raise RuntimeError(
"Could not find a valid device to connect to."
raise WikifiedError(
"006", "Could not find a valid device to connect to."
) from adb_error

def _start_recording(self):
Expand Down Expand Up @@ -151,11 +154,13 @@ def _run_command(self, command):
logger.error(str(e))
logger.error(f"stdout: {e.stdout}")
logger.error(f"stderr: {e.stderr}")
raise
raise WikifiedError("007", "ADB command failed.") from e

if result.returncode != 0:
logger.error(f"Error executing command: {result.stderr}")
raise RuntimeError("ADB command failed")
raise WikifiedError(
"007", "ADB command failed."
) from RuntimeError(result.stderr)

return result.stdout

Expand All @@ -171,35 +176,37 @@ def _update_frame(self):
logger.debug("Starting to update frames...")
for line in iter(self.video_thread.stdout.readline, b""):
try:
if not line:
last_frame = self._get_last_frame(line)
if not last_frame:
continue

if self.os_name == "windows":
line = line.replace(b"\r\n", b"\n")

packets = self.codec.parse(line)
if not packets:
continue

frames = self.codec.decode(packets[-1])
if not frames:
continue

self.frame = (
frames[-1]
.reformat(
width=SCREENSHOT_WIDTH,
height=SCREENSHOT_HEIGHT,
format="rgb24",
)
.to_image()
)
self.frame = last_frame.reformat(
width=SCREENSHOT_WIDTH,
height=SCREENSHOT_HEIGHT,
format="rgb24",
).to_image()

except av.AVError as av_error:
logger.error(f"Error while decoding video stream: {av_error}")
except Exception as e:
logger.error(f"Unexpected error in frame update: {str(e)}")

def _get_last_frame(self, line):
if not line:
return None

if self.os_name == "windows":
line = line.replace(b"\r\n", b"\n")

packets = self.codec.parse(line)
if not packets:
return None

frames = self.codec.decode(packets[-1])
if not frames:
return None
return frames[-1]

def _start_updating_frame(self):
self.frame_thread = threading.Thread(target=self._update_frame)
self.frame_thread.daemon = True
Expand Down
50 changes: 28 additions & 22 deletions clashroyalebuildabot/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,40 @@
from clashroyalebuildabot.gui.styles import set_styles
from clashroyalebuildabot.utils.logger import colorize_log
from clashroyalebuildabot.utils.logger import setup_logger
from error_handling import WikifiedError


class MainWindow(QMainWindow):
def __init__(self, config, actions):
super().__init__()
self.config = config
self.actions = actions
self.bot = None
self.bot_thread = None
self.is_running = False
try:
super().__init__()
self.config = config
self.actions = actions
self.bot = None
self.bot_thread = None
self.is_running = False

self.setWindowTitle(" ")
self.setGeometry(100, 100, 900, 600)
self.setWindowTitle(" ")
self.setGeometry(100, 100, 900, 600)

transparent_pixmap = QPixmap(1, 1)
transparent_pixmap.fill(Qt.GlobalColor.transparent)
self.setWindowIcon(QIcon(transparent_pixmap))
transparent_pixmap = QPixmap(1, 1)
transparent_pixmap.fill(Qt.GlobalColor.transparent)
self.setWindowIcon(QIcon(transparent_pixmap))

main_widget = QWidget(self)
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout(main_widget)
main_widget = QWidget(self)
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout(main_widget)

top_bar = setup_top_bar(self)
tab_widget = setup_tabs(self)
top_bar = setup_top_bar(self)
tab_widget = setup_tabs(self)

main_layout.addWidget(top_bar)
main_layout.addWidget(tab_widget)
main_layout.addWidget(top_bar)
main_layout.addWidget(tab_widget)

set_styles(self)
start_play_button_animation(self)
set_styles(self)
start_play_button_animation(self)
except Exception as e:
raise WikifiedError("004", "Error in GUI initialization.") from e

def log_handler_function(self, message):
formatted_message = colorize_log(message)
Expand Down Expand Up @@ -140,10 +144,12 @@ def bot_task(self):
)
self.bot.run()
self.stop_bot()
except Exception as e:
logger.error(f"Bot crashed: {e}")
except WikifiedError:
self.stop_bot()
raise
except Exception as e:
self.stop_bot()
raise WikifiedError("003", "Bot crashed.") from e

def append_log(self, message):
self.log_display.append(message)
10 changes: 4 additions & 6 deletions clashroyalebuildabot/gui/utils.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import os

from loguru import logger
import yaml

from clashroyalebuildabot.constants import SRC_DIR
from error_handling import WikifiedError


def load_config():
config = None
try:
config_path = os.path.join(SRC_DIR, "config.yaml")
with open(config_path, encoding="utf-8") as file:
config = yaml.safe_load(file)
return yaml.safe_load(file)
except Exception as e:
logger.error(f"Can't parse config, stacktrace: {e}")
return config
raise WikifiedError("002", "Can't parse config.") from e


def save_config(config):
Expand All @@ -23,4 +21,4 @@ def save_config(config):
with open(config_path, "w", encoding="utf-8") as file:
yaml.dump(config, file)
except Exception as e:
logger.error(f"Can't save config, stacktrace: {e}")
raise WikifiedError("000", "Can't save config.") from e
4 changes: 4 additions & 0 deletions clashroyalebuildabot/utils/error_handling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def get_wikified_error_message(error_code: str, reason: str) -> str:
return f"Error #E{str(error_code)}: {reason}.\
See https://github.com/Pbatch/ClashRoyaleBuildABot/wiki/Troubleshooting#error-e{str(error_code)} for more information.\
Full error message below:\n\n"
7 changes: 5 additions & 2 deletions clashroyalebuildabot/utils/git_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import subprocess
import sys
from time import sleep

from loguru import logger

from error_handling import WikifiedError


def _is_branch_late() -> bool:
subprocess.run(
Expand Down Expand Up @@ -42,4 +43,6 @@ def check_and_pull_updates() -> None:
sleep(3)
return
logger.error(f"Error while checking / pulling updates: {e.stderr}")
sys.exit(1)
raise WikifiedError(
"000", "Error while checking / pulling updates"
) from e
3 changes: 3 additions & 0 deletions error_handling/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .wikify_error import WikifiedError

__all__ = ["WikifiedError"]
14 changes: 14 additions & 0 deletions error_handling/wikify_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
def get_wikified_error_message(error_code: str, reason: str) -> str:
err_str = f"\u26A0 Error #E{str(error_code)}: {reason}"
link = "https://github.com/Pbatch/ClashRoyaleBuildABot/wiki/"
link += f"Troubleshooting#error-e{str(error_code)}"
err_str += f" See {link} for more information."
err_str += " You might find more context above this error.\n\n"
return err_str


class WikifiedError(Exception):
def __init__(self, error_code: str, reason: str):
self.error_code = error_code
self.reason = reason
super().__init__(get_wikified_error_message(error_code, reason))
Loading

0 comments on commit 562cb0f

Please sign in to comment.