diff --git a/tasks/login/cloud.py b/tasks/login/cloud.py index 1c8a1c7f2..06a35b20a 100644 --- a/tasks/login/cloud.py +++ b/tasks/login/cloud.py @@ -2,13 +2,20 @@ import re from module.base.base import ModuleBase from module.base.timer import Timer -from module.base.utils import area_offset +from module.base.utils import area_offset, random_rectangle_vector_opted from module.device.method.utils import AreaButton from module.exception import GameNotRunningError, RequestHumanTakeover from module.logger import logger class XPath: + """ + xpath 元素,元素可通过 uiautomator2 内的 weditor.exe 查找 + """ + + """ + 登录界面元素 + """ # 帐号登录界面的进入游戏按钮,有这按钮说明帐号没登录 ACCOUNT_LOGIN = '//*[@text="进入游戏"]' # 登录后的弹窗,获得免费时长 @@ -26,14 +33,37 @@ class XPath: REMAIN_FREE = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tvRemainingFreeTime"]' # 主界面的开始游戏按钮 START_GAME = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/btnLauncher"]' + # 排队剩余时间 + QUEUE_REMAIN = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tvQueueInfoWaitTimeContent"]' + + """ + 游戏界面元素 + """ + # 网络状态 简洁 + FLOAT_STATE_SIMPLE = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tvSimpleNetStateMode"]' + # 网络状态 详细 + FLOAT_STATE_DETAIL = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tv_ping_value"]' + """ + 悬浮窗及侧边栏元素 + """ # 悬浮窗 FLOAT_WINDOW = '//*[@class="android.widget.ImageView"]' - # 悬浮窗内的延迟 + # 弹出侧边栏的 节点信息 # 将这个区域向右偏移作为退出悬浮窗的按钮 - FLOAT_DELAY = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tv_delay"]' - - '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/ivPingIcon"]' - '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tv_ping"]' + FLOAT_DELAY = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tv_node_region"]' + # 弹出侧边栏的滚动区域 + SCROLL_VIEW = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/innerScrollView"]' + # 画质选择 超高清 + # 选中时selected=True + SETTING_BITRATE_UHD = '//*[@text="超高清"]' + # 网络状态 开关 + SETTING_NET_STATE_TOGGLE = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/sw_net_state"]' + SETTING_NET_STATE_SIMPLE = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/mTvTitleOfSimpleMode"]' + SETTING_NET_STATE_DETAIL = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/mTvTitleOfDetailMode"]' + # 问题反馈 + SETTING_PROBLEM = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tv_problem"]' + # 下载游戏 + SETTING_DOWNLOAD = '//*[@resource-id="com.miHoYo.cloudgames.hkrpg:id/tv_downloadGame"]' class LoginAndroidCloud(ModuleBase): @@ -107,7 +137,8 @@ class LoginAndroidCloud(ModuleBase): def _cloud_enter(self, skip_first=False): """ Pages: - out: START_GAME + in: START_GAME + out: page_main """ logger.hr('Cloud enter') while 1: @@ -117,22 +148,39 @@ class LoginAndroidCloud(ModuleBase): self.device.dump_hierarchy() # End - if self.appear(XPath.FLOAT_WINDOW): - logger.info('Cloud game entered') - break + button = self.xpath(XPath.FLOAT_WINDOW) + if self.appear(button): + # Confirm float window size + width, height = button.size + if (width < 120 and height < 120) and (width / height < 0.6 or height / width < 0.6): + logger.info('Cloud game entered') + break + + # Queue daemon + button = self.xpath(XPath.QUEUE_REMAIN) + if self.appear(button): + remain = button.text + logger.info(f'Queue remain: {remain}') + self.device.stuck_record_clear() # Click if self.appear_then_click(XPath.START_GAME): continue if self.appear(XPath.POPUP_CONFIRM, interval=5): + title = self.xpath(XPath.POPUP_TITLE).text + logger.info(f'Popup: {title}') # 计费提示 # 本次游戏将使用畅玩卡无限畅玩 # - 进入游戏(9s) - 退出游戏 - title = self.xpath(XPath.POPUP_TITLE).text - logger.info(f'Popup: {title}') if title == '计费提示': self.device.click(self.xpath(XPath.POPUP_CONFIRM)) continue + # 是否使用星云币时长进入游戏 + # 使用后可优先排队进入游戏,本次游戏仅可使用星云币时长,无法消耗免费时长 + # - 确认使用 - 暂不使用 + if title == '是否使用星云币时长进入游戏': + self.device.click(self.xpath(XPath.POPUP_CONFIRM)) + continue # 连接中断 # 因为您长时间未操作游戏,已中断连接,错误码: -1022 # - 退出游戏 @@ -140,6 +188,13 @@ class LoginAndroidCloud(ModuleBase): self.device.click(self.xpath(XPath.POPUP_CONFIRM)) continue + # Disable net state display + if self._cloud_net_state_appear(): + self._cloud_setting_disable_net_state() + # Login to game + from tasks.login.login import Login + Login(config=self.config, device=self.device).handle_app_login() + def _cloud_setting_enter(self, skip_first=True): while 1: if skip_first: @@ -170,8 +225,72 @@ class LoginAndroidCloud(ModuleBase): self.device.click(button) continue + def _cloud_setting_disable_net_state(self, skip_first=True): + """ + Pages: + in: page_main + out: page_main + """ + self._cloud_setting_enter(skip_first=skip_first) + + skip_first = True + while 1: + if skip_first: + skip_first = False + else: + self.device.dump_hierarchy() + + button = self.xpath(XPath.SETTING_BITRATE_UHD) + if self.appear(button, interval=3): + if not button.selected: + logger.info('Set bitrate to UHD') + self.device.click(button) + continue + if self.appear(XPath.SETTING_NET_STATE_TOGGLE): + if self.appear(XPath.SETTING_NET_STATE_SIMPLE) or self.appear(XPath.SETTING_NET_STATE_DETAIL): + logger.info('Set net state to disabled') + self.appear_then_click(XPath.SETTING_NET_STATE_TOGGLE, interval=3) + continue + else: + logger.info('Net state display disabled') + break + # Scroll down + if not self.appear(XPath.SETTING_PROBLEM): + area = self.xpath(XPath.SCROLL_VIEW).area + # An area safe to swipe + area = (area[0], area[1], area[0] + 25, area[3]) + p1, p2 = random_rectangle_vector_opted( + (0, -450), box=area, random_range=(-10, -30, 10, 30), padding=2) + self.device.swipe(p1, p2, name='SETTING_SCROLL') + continue + + self._cloud_setting_exit(skip_first=True) + + def _cloud_net_state_appear(self): + """ + Returns: + bool: True if net state display is enabled + """ + if self.appear(XPath.FLOAT_STATE_SIMPLE): + logger.attr('Net state', 'FLOAT_STATE_SIMPLE') + return True + if self.appear(XPath.FLOAT_STATE_DETAIL): + logger.attr('Net state', 'FLOAT_STATE_DETAIL') + return True + logger.attr('Net state', None) + return False + def cloud_ensure_ingame(self): + """ + Pages: + in: Any + out: page_main + """ logger.hr('Cloud ensure ingame', level=1) + + if self.config.Emulator_GameClient != 'cloud_android': + self.config.Emulator_GameClient = 'cloud_android' + for _ in range(3): if self.device.app_is_running(): logger.info('Cloud game is already running') @@ -214,3 +333,24 @@ class LoginAndroidCloud(ModuleBase): logger.error('Failed to enter cloud game after 3 trials') return False + + def cloud_keep_alive(self): + """ + Randomly do something to prevent being kicked + + WARNING: + this may cause extra fee + """ + logger.hr('cloud_keep_alive', level=2) + while 1: + self.device.sleep((45, 60)) + + logger.info('cloud_keep_alive') + self._cloud_setting_enter(skip_first=False) + self._cloud_setting_exit(skip_first=True) + + +if __name__ == '__main__': + self = LoginAndroidCloud('src') + self.cloud_ensure_ingame() + self.cloud_keep_alive()