示例#1
0
    def switch_fleet(self, context):
        preset_id = self._get_next_preset_id(context)
        Log.log_msg(f"Switching to Fleet Preset {preset_id}.")
        if preset_id not in self.presets:
            Log.log_error(
                f"Fleet Preset {preset_id} is not specified in-game. Please "
                f"check your config.")
            exit(1)

        list_idx = (preset_id if preset_id < 5 else 5) - 1
        idx_offset = preset_id - 5
        if idx_offset > 0:
            kca_u.kca.sleep()
            self._scroll_preset_list(idx_offset)

        kca_u.kca.r['top'].hover()
        kca_u.kca.sleep()
        preset_idx_region = Region(kca_u.kca.game_x + 410,
                                   kca_u.kca.game_y + 275 + (list_idx * 76),
                                   70, 45)
        kca_u.kca.click_existing(preset_idx_region,
                                 'fleetswitcher|fleetswitch_button.png')
        if kca_u.kca.exists('left',
                            'fleetswitcher|fleetswitch_fail_check.png'):
            Log.log_error(
                f"Could not switch in fleet preset {preset_id}. Please check "
                f"your config and fleet presets.")
            exit(1)
        Log.log_msg(f"Fleet Preset {preset_id} loaded.")

        if context == 'combat':
            self._set_next_combat_preset()
示例#2
0
文件: nodes.py 项目: ly2314/kcauto
    def navigate_to(self, target, api_update=True):
        """Method that interacts with the game to transition from the current
        node to the destination via the pre-defined connections.

        Args:
            target (str): name of destination node

        Returns:
            NavNode: NavNode instance of the destination
        """
        if target in self.connections:
            c = self.connections[target]
            kca_u.kca.hover('top')
            if c['click_target'] == 'QUEST_MENU':
                kca_u.kca.click('top_menu_quest')
            else:
                kca_u.kca.wait_and_click(
                    kca_u.kca.r[c['click_target_region']], c['click_target'])
            if api_update:
                api.api.update_from_api({c['api_target']})
            kca_u.kca.hover('top')
            kca_u.kca.sleep()
            kca_u.kca.wait(
                kca_u.kca.r[c['wait_target_region']], c['wait_target'], 20,
                NEAR_EXACT)
            return c['target']
        else:
            Log.log_error(
                f"Not possible to navigate to '{target}' from {self.name} "
                "screen.")
示例#3
0
文件: api_core.py 项目: ly2314/kcauto
 def update_ship_library_from_json(self):
     try:
         ship_data = JsonData.load_json('data|temp|get_data_ship.json')
         shp.ships.update_ship_library(ship_data)
     except FileNotFoundError as e:
         Log.log_error(
             "get_data_ship.json not found. Please run kcauto from the "
             "Kancolle splash screen to download data.")
         Log.log_error(e)
         sys.exit(1)
示例#4
0
 def __init__(self):
     Log.log_success("Initializing kcauto.")
     if arg.args.parsed_args.cfg_path:
         self.cfg_path = arg.args.parsed_args.cfg_path
     else:
         cfg_file = arg.args.parsed_args.cfg
         self.cfg_path = JsonData.create_path(f'configs|{cfg_file}.json')
     self.initialize_config()
     if not self.general:
         Log.log_error("Error loading config.")
         exit(1)
示例#5
0
文件: recovery.py 项目: ly2314/kcauto
    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
示例#6
0
    def hook_chrome(self, host="localhost", port=9222):
        """Method that initializes the necessary hooks to Chrome using
        PyChromeDevTools. The visual hook connects to the tab that actually
        contains the Kancolle HTML5 canvas, while the api hook connects to the
        tab that interacts with the Kancolle backend. The former is used for
        detecting refreshes and when using the Chrome driver interaction mode,
        while the latter is used for reading all API interactions.

        Args:
            host (str, optional): Chrome dev protocol server address. Defaults
                to "localhost".
            port (int, optional): Chrome dev protocol server port. Defaults to
                9222.

        Raises:
            Exception: could not find Kancolle tabs in Chrome.
        """
        Log.log_msg("Hooking into Chrome.")
        self.visual_hook = PyChromeDevTools.ChromeInterface(host=host,
                                                            port=port)
        self.api_hook = PyChromeDevTools.ChromeInterface(host=host, port=port)

        visual_tab = None
        visual_tab_id = None
        api_tab = None
        api_tab_id = None
        for n, tab in enumerate(self.visual_hook.tabs):
            if tab['url'] == VISUAL_URL:
                visual_tab = n
                visual_tab_id = tab['id']
            if API_URL in tab['url']:
                api_tab = n
                api_tab_id = tab['id']

        if visual_tab_id is None or api_tab_id is None:
            Log.log_error(
                "No Kantai Collection tab found in Chrome. Shutting down "
                "kcauto.")
            raise Exception(
                "No running Kantai Collection tab found in Chrome.")

        self.visual_hook.connect_targetID(visual_tab_id)
        Log.log_debug(
            f"Connected to visual tab ({visual_tab}:{visual_tab_id})")
        self.visual_hook.Page.enable()

        self.api_hook.connect_targetID(api_tab_id)
        self.api_hook.Network.enable()
        Log.log_debug(f"Connected to API tab ({api_tab}:{api_tab_id})")
        Log.log_success("Connected to Chrome")
示例#7
0
文件: recovery.py 项目: ly2314/kcauto
    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
示例#8
0
    def initialize_config(self):
        update = False
        config_json = self._load_cfg_json()
        initial_load = True
        new_update_time = os.path.getmtime(self.cfg_path)

        if self.general:
            initial_load = False
            if config_json != self.general._config:
                Log.log_msg("Changes detected from previous config load.")
            else:
                Log.log_debug("No change from previous config load.")
                self.last_cfg_update_time = new_update_time
                return False

        try:
            new_general = ConfigGeneral(config_json)
            new_expedition = ConfigExpedition(config_json)
            new_pvp = ConfigPvP(config_json)
            new_combat = ConfigCombat(config_json)
            new_event_reset = ConfigEventReset(config_json)
            new_ship_switcher = ConfigShipSwitcher(config_json)
            new_passive_repair = ConfigPassiveRepair(config_json)
            new_quest = ConfigQuest(config_json)
            new_scheduler = ConfigScheduler(config_json)
            update = True
        except Exception as e:
            Log.log_error(e)

        if update:
            Log.log_success("Config successfully loaded.")
            if not initial_load:
                Log.log_success("Hot-reloading config in 3...")
                sleep(1)
                Log.log_success("2...")
                sleep(1)
                Log.log_success("1...")
                sleep(1)
            self.general = new_general
            self.expedition = new_expedition
            self.pvp = new_pvp
            self.combat = new_combat
            self.event_reset = new_event_reset
            self.ship_switcher = new_ship_switcher
            self.passive_repair = new_passive_repair
            self.quest = new_quest
            self.scheduler = new_scheduler
            self.last_cfg_update_time = new_update_time
            return True
示例#9
0
文件: recovery.py 项目: ly2314/kcauto
    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
示例#10
0
    def find_kancolle(self):
        """Method that finds the Kancolle game on-screen and determine the UI
        being used as well as the position of the game. On first startup the
        method will look for all UIs until one is found; on subsequent runs
        it will first look for the last found UI. Generates or modifies
        pre-defined regions accordingly.

        Raises:
            FindFailed: could not find the game on-screen.
        """
        Log.log_msg("Finding kancolle.")
        ref_r = None
        attempt = 0
        screen = Region()

        # look for last-seen UI, if set
        if self.last_ui:
            try:
                ref_r = self.find(screen,
                                  f'global|kc_ref_point_{self.last_ui}.png',
                                  EXACT)
            except FindFailed:
                self.last_ui = None
                Log.log_debug("Last known UI not found.")

        # if last-seen UI was not found, or if kcauto is in first start
        while not ref_r:
            try:
                ref_r = self.find(screen, 'global|kc_ref_point_1.png', EXACT)
                self.last_ui = 1
                Log.log_debug("Using UI 1 or 2")
                break
            except FindFailed:
                Log.log_debug("Not using UI 1 or 2")
            try:
                ref_r = self.find(screen, 'global|kc_ref_point_2.png', EXACT)
                self.last_ui = 2
                Log.log_debug("Using UI 3")
                break
            except FindFailed:
                Log.log_debug("Not using UI 3")
            try:
                ref_r = self.find(screen, 'global|kc_ref_point_3.png', EXACT)
                self.last_ui = 3
                Log.log_debug("Using UI 4 or 5")
                break
            except FindFailed:
                Log.log_debug("Not using UI 4 or 5")
            attempt += 1
            sleep(1)
            if attempt > 3:
                Log.log_error("Could not find Kancolle reference point.")
                raise FindFailed()

        new_game_x = ref_r.x - 144
        new_game_y = ref_r.y
        Log.log_debug(f"Game X:{new_game_x}, Y:{new_game_y}")

        # define click callback as needed
        if not arg.args.parsed_args.no_click_track:
            ImageMatch.click_callback = clt.click_tracker.track_click

        # define click and hover method overrides as needed
        if (cfg.config.general.interaction_mode is
                InteractionModeEnum.CHROME_DRIVER):
            ImageMatch.override_click_method = self._override_click_method
            ImageMatch.override_hover_method = self._override_hover_method

        if new_game_x != self.game_x or new_game_y != self.game_y:
            if not self.game_x or self.game_y:
                Log.log_success("Game found. Initializing regions.")
            else:
                Log.log_msg("Game has moved. Shifting regions.")
            self.game_x = new_game_x
            self.game_y = new_game_y
            self._update_regions()

        return True
示例#11
0
文件: api_core.py 项目: ly2314/kcauto
 def _load_api_data(self, request_data, data):
     request_type = request_data['type']
     if data['api_result'] != 1:
         Log.log_debug("Encountered non-1 API result.")
         Log.log_debug(data)
         if data['api_result'] == 201:
             Log.log_error("Encountered catbomb.")
             raise Catbomb201Exception
         else:
             raise ApiException
     if request_type is KCSAPIEnum.GET_DATA:
         return self._process_get_data(data)
     elif request_type is KCSAPIEnum.REQUIRE_INFO:
         return self._process_require_info(data)
     elif request_type is KCSAPIEnum.PORT:
         return self._process_port(data)
     elif request_type is KCSAPIEnum.SORTIE_MAPS:
         return self._process_sortie_maps(data)
     elif request_type is KCSAPIEnum.SORTIE_START:
         return self._process_sortie_start(data)
     elif request_type is KCSAPIEnum.SORTIE_NEXT:
         return self._process_sortie_next(data)
     elif request_type is KCSAPIEnum.SORTIE_BATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_NIGHTBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_AIRBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_LD_AIRBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_LD_SHOOTING:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_N2D:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_NIGHT_ONLY:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_ECF_BATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_ECF_NIGHTBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_BATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_NIGHTBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_AIRBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_WATERBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_LD_AIRBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_LD_SHOOTING:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_N2D:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_NIGHT_ONLY:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_EACH_NIGHT_ONLY:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_ECF_BATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_ECF_AIRBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_ECF_WATERBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_ECF_LD_AIRBATTLE:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_ECF_LD_SHOOTING:
         return self._process_battle(data)
     elif request_type is KCSAPIEnum.SORTIE_RESULT:
         return self._process_battle_result(data)
     elif request_type is KCSAPIEnum.SORTIE_CF_RESULT:
         return self._process_battle_result(data)
     elif request_type is KCSAPIEnum.SORTIE_SHIPDECK:
         return self._process_battle_deck(data)
     elif request_type is KCSAPIEnum.EXPEDITION_LIST:
         return self._process_expedition_list(data)
     elif request_type is KCSAPIEnum.EXPEDITION_START:
         return self._process_expedition_start(data)
     elif request_type is KCSAPIEnum.PVP_LIST:
         return self._process_pvp_list(data)
     elif request_type is KCSAPIEnum.PVP_ENEMY_INFO:
         return self._process_pvp_enemy_info(data)
     elif request_type is KCSAPIEnum.FLEETCOMP_PRESETS:
         return self._process_fleetcomp_presets(data)
     elif request_type is KCSAPIEnum.REPAIR_DOCKS:
         return self._process_repair_dock_data(data)
     elif request_type is KCSAPIEnum.QUEST_LIST:
         return self._process_quest_data(data)
     return None
示例#12
0
文件: nav.py 项目: ly2314/kcauto
    def to(cls, destination, max_sidestep=1):
        """Method to call to detect the current location and move to the
        specified destination, with or without sidesteps.

        Args:
            regions (dict): dict of pre-defined kcauto regions
            destination (str): name of the destination
            max_sidestep (int, optional): the max number of sidesteps to take;
                in the current implementation the name and type is a misnomer:
                if it is a non-zero number the code will sidestep once,
                otherwise never sidestep (should be renamed to 'sidestep' with
                bool type)

        Returns:
            bool: True if navigation was successful, False if no actions were
                taken
        """
        if (kca_u.kca.exists('home_menu', 'nav|home_menu_sortie.png')
                and destination == 'home'):
            # if visibly at home menu and destination is home, not refresh
            # home, short circuit
            return False

        sidestep = bool(randint(0, max_sidestep))
        kca_u.kca.hover('top')
        kca_u.kca.sleep()
        # Figure out where we are
        current_location = None
        if kca_u.kca.exists('home_menu', 'nav|home_menu_sortie.png'):
            Log.log_msg("At home")
            current_location = nodes.nav_nodes.home
        elif kca_u.kca.exists('side_menu', 'nav|side_menu_home.png'):
            Log.log_msg("At side menu")
            current_location = nodes.nav_nodes.side_menu
        elif kca_u.kca.exists('lower_left', 'nav|top_menu_home.png'):
            Log.log_msg("At top menu")
            current_location = nodes.nav_nodes.top_menu

        if not current_location:
            Log.log_error("Nav module could not figure out current location.")
            raise FindFailed()

        if current_location.name == 'home':
            # Starting from home screen
            if destination == 'home':
                # Already at home
                # Util.log_msg('Already at home.')
                return False
            elif destination == 'refresh_home':
                # Refresh home
                Log.log_msg("Refreshing home.")
                destination = 'home'
                current_location = current_location.navigate_to(
                    cls._choose_sidestep(destination), False)
            else:
                # Go to and side menu sub screen
                Log.log_msg(
                    "Navigating to {} screen.".format(destination))
                if destination in ('combat', 'pvp', 'expedition'):
                    current_location = current_location.navigate_to(
                        'sortie', False)
                    kca_u.kca.sleep(1)
                else:
                    if sidestep:
                        current_location = current_location.navigate_to(
                            cls._choose_sidestep(destination), False)
        elif current_location.name == 'side_menu':
            # Starting from a main menu item screen
            if destination in ('home', 'refresh_home'):
                # Go or refresh home
                Log.log_msg('Going home.')
                destination = 'home'
        elif current_location.name == 'top_menu':
            # Starting from top menu item. Theoretically, the script should
            # never attempt to go anywhere but home from here
            if destination in ('home', 'refresh_home'):
                Log.log_msg('Going home.')
                destination = 'home'

        current_location.navigate_to(destination)
        return True