import pyautogui from pyvisauto import Region, FindFailed from time import sleep import config.config_core as cfg import stats.stats_core as sts import util.kca as kca_u from util.logger import Log pyautogui.FAILSAFE = False class Recovery(object): """Recovery module. """ @classmethod def attempt_recovery(cls): """Primary method that runs through all the various recovery options. Runs through basic recovery, result screen recovery, catbomb recovery, then Chrome tab crash recoveries. Typically run when there has been a generic Exception caused. Returns: bool: True if recovery was successful; False otherwise. """ Log.log_warn("Attempting recovery.") screen = Region() if cls.basic_recovery(): Log.log_success("Basic Recovery successful.") sts.stats.recovery.recoveries_done += 1 sts.stats.recovery.basic_recoveries_done += 1 return True if ( kca_u.kca.exists(screen, 'global|next.png') or kca_u.kca.exists(screen, 'global|next_alt.png')): Log.log_warn("Results screen detected.") if cls.recovery_from_results(screen): Log.log_success("Results Recovery successful.") sts.stats.recovery.recoveries_done += 1 sts.stats.recovery.results_recoveries_done += 1 return True return False if kca_u.kca.exists(screen, 'global|catbomb.png'): Log.log_warn("Catbomb detected.") if cls.recovery_from_catbomb(screen=screen): return True else: Log.log_error("Catbomb Recovery failed.") return False if kca_u.kca.exists(screen, 'global|chrome_crash.png'): Log.log_warn("Chrome Crash (Type 1) detected.") if cls.recovery_from_chrome_crash(screen, crash_type=1): return True visual_events = kca_u.kca.visual_hook.pop_messages() for event in visual_events: if event['method'] == 'Inspector.targetCrashed': Log.log_warn("Chrome Crash (Type 2) detected.") if cls.recovery_from_chrome_crash(screen, crash_type=2): return True @staticmethod def basic_recovery(): """Method that contains steps for basic recovery attempt, which involves clearing any interaction-blocking browser popups. Returns: bool: True if recovery was successful; False otherwise. """ Log.log_debug("Attempting Basic Recovery.") if cfg.config.general.is_direct_control: pyautogui.moveTo(1, 1) pyautogui.press('esc') sleep(0.5) pyautogui.press('space') sleep(0.5) pyautogui.press('f10') sleep(0.5) try: if kca_u.kca.find_kancolle(): return True except FindFailed: pass return False @staticmethod def recovery_from_results(screen): """Method that contains steps for recovery attempt from a results screen (such as Expedition results screen, combat results screen, etc). Args: screen (Region): screen region. Returns: bool: True if recovery was successful; False otherwise. """ while ( kca_u.kca.exists(screen, 'global|next.png') or kca_u.kca.exists(screen, 'global|next_alt.png')): if kca_u.kca.exists(screen, 'global|next.png', cached=True): region = kca_u.kca.find(screen, 'global|next.png', cached=True) elif kca_u.kca.exists(screen, 'global|next_alt.png', cached=True): region = kca_u.kca.find( screen, 'global|next_alt.png', cached=True) region.click() if kca_u.kca.exists(screen, 'nav|home_menu_sortie.png'): return True return False @classmethod def recovery_from_catbomb(cls, screen=Region(), catbomb_201=False): """Method that contains steps for recovery attempt from catbombs. If the catbomb_201 flag is set to True the recovery is attempt is far less aggressive to not aggravate the fairies any further. Includes incremental fallback for retry attempts. Args: screen (Region, optional): screen region. Defaults to Region(). catbomb_201 (bool, optional): whether or not this is a 201 catbomb. Defaults to False. Returns: bool: True if recovery was successful; False otherwise. """ catbomb_count = 0 if not catbomb_201 else 2 if catbomb_201: if sts.stats.recovery.catbomb_201_encountered > 0: Log.log_error( "Multiple 201 catbombs encountered. Shutting down kcauto.") return False else: kca_u.kca.sleep(300) while catbomb_count < 3: cls._refresh_screen(screen) if kca_u.kca.start_kancolle(): Log.log_success("Catbomb Recovery successful.") kca_u.kca.hook_chrome(port=cfg.config.general.chrome_dev_port) sts.stats.recovery.recoveries_done += 1 if catbomb_201: sts.stats.recovery.catbomb_201_encountered += 1 sts.stats.recovery.catbomb_201_recoveries_done += 1 else: sts.stats.recovery.catbomb_recoveries_done += 1 return True elif kca_u.kca.exists(screen, 'global|catbomb.png'): if catbomb_201: Log.log_error( "Persistent 201 catbomb. Shutting down kcauto.") return False catbomb_count += 1 # incremental backoff; 16, 64, then 256 seconds sleep_len = pow(4, catbomb_count + 1) Log.log_warn( f"Catbomb Recovery attempt {catbomb_count}. Sleeping for " f"{sleep_len} seconds before next recovery attempt.") sleep(sleep_len) else: return False return False @classmethod def recovery_from_chrome_crash(cls, screen=Region(), crash_type=1): """Method that contains steps for recovery from Chrome tab crashes. Args: screen (Region, optional): screen region. Defaults to Region(). crash_type (int, optional): type of Chrome tab crash encountered. Defaults to 1. Returns: bool: True if recovery was successful; False otherwise. """ cls._refresh_screen(screen) if kca_u.kca.start_kancolle(): Log.log_success("Chrome Crash Recovery successful.") sts.stats.recovery.recoveries_done += 1 if crash_type == 1: sts.stats.recovery.chrome_crash_t1_recoveries_done += 1 elif crash_type == 2: sts.stats.recovery.chrome_crash_t2_recoveries_done += 1 return True Log.log_error("Chrome Crash Recovery failed.") return False @staticmethod def _refresh_screen(screen): """Helper method that contains steps for refreshing the tab. Includes logic to re-instantiate neccessary Chrome hooks. Args: screen (Region, optional): screen region. """ if cfg.config.general.is_direct_control: pyautogui.press('f5') sleep(0.5) pyautogui.press('space') sleep(0.5) pyautogui.press('tab') sleep(0.5) pyautogui.press('space') sleep(5) else: kca_u.kca.visual_hook.Page.reload() sleep(0.5) kca_u.kca.wait(screen, 'global|game_start.png', 90) sleep(3)