diff --git a/config/template.json b/config/template.json index 3083c1feb..18cc6c40f 100644 --- a/config/template.json +++ b/config/template.json @@ -1415,7 +1415,8 @@ }, "AddNewStudent": { "Enable": false, - "Favorite": true + "Favorite": true, + "MinLevel": 50 }, "Storage": { "Storage": {} diff --git a/deploy/Windows/emulator.py b/deploy/Windows/emulator.py index 3dfc74f15..11def046a 100644 --- a/deploy/Windows/emulator.py +++ b/deploy/Windows/emulator.py @@ -142,10 +142,13 @@ class EmulatorManager(AlasManager): for adb in replace: logger.info(f'Replacing {adb}') bak = self.adb_path_to_backup(adb, new_backup=True) - logger.info(f'{adb} -----> {bak}') - shutil.move(adb, bak) - logger.info(f'{self.adb} -----> {adb}') - shutil.copy(self.adb, adb) + try: + logger.info(f'{adb} -----> {bak}') + shutil.move(adb, bak) + logger.info(f'{self.adb} -----> {adb}') + shutil.copy(self.adb, adb) + except OSError as e: + logger.warning(f'Failed to replace {adb}, {e}') def adb_recover(self): """ diff --git a/deploy/emulator.py b/deploy/emulator.py index 2ccbd4ec4..7eb076a75 100644 --- a/deploy/emulator.py +++ b/deploy/emulator.py @@ -50,10 +50,11 @@ class VirtualBoxEmulator: return root try: - reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f'{self.UNINSTALL_REG}\\{self.name}', 0) + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f'{self.UNINSTALL_REG}\\{self.name}', 0) as reg: + res = winreg.QueryValueEx(reg, 'UninstallString')[0] except FileNotFoundError: - reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f'{self.UNINSTALL_REG_2}\\{self.name}', 0) - res = winreg.QueryValueEx(reg, 'UninstallString')[0] + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f'{self.UNINSTALL_REG_2}\\{self.name}', 0) as reg: + res = winreg.QueryValueEx(reg, 'UninstallString')[0] file = re.search('"(.*?)"', res) file = file.group(1) if file else res @@ -70,17 +71,17 @@ class VirtualBoxEmulator: str: Installation dir or None """ try: - reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0) - root = winreg.QueryValueEx(reg, key)[0] - if os.path.exists(root): - return root + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0) as reg: + root = winreg.QueryValueEx(reg, key)[0] + if os.path.exists(root): + return root except FileNotFoundError: pass try: - reg = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0) - root = winreg.QueryValueEx(reg, key)[0] - if os.path.exists(root): - return root + with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0) as reg: + root = winreg.QueryValueEx(reg, key)[0] + if os.path.exists(root): + return root except FileNotFoundError: pass @@ -137,16 +138,19 @@ class VirtualBoxEmulator: """ for ori, bak in zip(self.adb_binary, self.adb_backup): logger.info(f'Replacing {ori}') - if os.path.exists(ori): - if filecmp.cmp(adb, ori, shallow=True): - logger.info(f'{adb} is same as {ori}, skip') + try: + if os.path.exists(ori): + if filecmp.cmp(adb, ori, shallow=True): + logger.info(f'{adb} is same as {ori}, skip') + else: + logger.info(f'{ori} -----> {bak}') + shutil.move(ori, bak) + logger.info(f'{adb} -----> {ori}') + shutil.copy(adb, ori) else: - logger.info(f'{ori} -----> {bak}') - shutil.move(ori, bak) - logger.info(f'{adb} -----> {ori}') - shutil.copy(adb, ori) - else: - logger.info(f'{ori} not exists, skip') + logger.info(f'{ori} not exists, skip') + except OSError as e: + logger.warning(f'Failed to replace {ori}, {e}') def adb_recover(self): """ Revert adb replacement """ diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 16c693b8f..4a64a05ae 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -375,6 +375,7 @@ "LDPlayer3", "LDPlayer4", "LDPlayer9", + "LDPlayer14", "MuMuPlayer", "MuMuPlayerX", "MuMuPlayer12", @@ -7363,6 +7364,10 @@ "Favorite": { "type": "checkbox", "value": true + }, + "MinLevel": { + "type": "input", + "value": 50 } }, "Storage": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 482ea2c35..75553dced 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -69,6 +69,7 @@ EmulatorInfo: LDPlayer3, LDPlayer4, LDPlayer9, + LDPlayer14, MuMuPlayer, MuMuPlayerX, MuMuPlayer12, @@ -401,6 +402,7 @@ ControlExpOverflow: AddNewStudent: Enable: false Favorite: true + MinLevel: 50 Research: UseCube: value: only_05_hour diff --git a/module/config/config_generated.py b/module/config/config_generated.py index e4bbd1bd7..0cc5c4e33 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -27,7 +27,7 @@ class GeneratedConfig: Emulator_AdbRestart = False # Group `EmulatorInfo` - EmulatorInfo_Emulator = 'auto' # auto, NoxPlayer, NoxPlayer64, BlueStacks4, BlueStacks5, BlueStacks4HyperV, BlueStacks5HyperV, LDPlayer3, LDPlayer4, LDPlayer9, MuMuPlayer, MuMuPlayerX, MuMuPlayer12, MEmuPlayer + EmulatorInfo_Emulator = 'auto' # auto, NoxPlayer, NoxPlayer64, BlueStacks4, BlueStacks5, BlueStacks4HyperV, BlueStacks5HyperV, LDPlayer3, LDPlayer4, LDPlayer9, LDPlayer14, MuMuPlayer, MuMuPlayerX, MuMuPlayer12, MEmuPlayer EmulatorInfo_name = None EmulatorInfo_path = None @@ -223,6 +223,7 @@ class GeneratedConfig: # Group `AddNewStudent` AddNewStudent_Enable = False AddNewStudent_Favorite = True + AddNewStudent_MinLevel = 50 # Group `Research` Research_UseCube = 'only_05_hour' # always_use, only_05_hour, only_no_project, do_not_use diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index b0558e33e..cb27af379 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -482,6 +482,7 @@ "LDPlayer3": "LD Player 3", "LDPlayer4": "LD Player 4", "LDPlayer9": "LD Player 9", + "LDPlayer14": "LDPlayer 14", "MuMuPlayer": "MuMu Player", "MuMuPlayerX": "MuMu Player X", "MuMuPlayer12": "MuMu Player 12", @@ -1531,6 +1532,10 @@ "Favorite": { "name": "Use Favorite Filter", "help": "During student selection apply the favorite filter\nThereby, giving priority to certain ship girls\nApply the favorite status to a ship girl in-game" + }, + "MinLevel": { + "name": "Add Students with Level >= X Only", + "help": "" } }, "Research": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 60e649f48..c9a372592 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -482,6 +482,7 @@ "LDPlayer3": "LDPlayer3", "LDPlayer4": "LDPlayer4", "LDPlayer9": "LDPlayer9", + "LDPlayer14": "LDPlayer14", "MuMuPlayer": "MuMuPlayer", "MuMuPlayerX": "MuMuPlayerX", "MuMuPlayer12": "MuMuPlayer12", @@ -1531,6 +1532,10 @@ "Favorite": { "name": "好ましい選択", "help": "" + }, + "MinLevel": { + "name": "レベルがX以上のキャラクターのみを追加してください。", + "help": "" } }, "Research": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 449b173c2..e15cc6902 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -482,6 +482,7 @@ "LDPlayer3": "雷电模拟器3", "LDPlayer4": "雷电模拟器4", "LDPlayer9": "雷电模拟器9", + "LDPlayer14": "雷电模拟器14", "MuMuPlayer": "MuMu模拟器", "MuMuPlayerX": "MuMu模拟器X", "MuMuPlayer12": "MuMu模拟器12", @@ -1531,6 +1532,10 @@ "Favorite": { "name": "优先选择常用", "help": "" + }, + "MinLevel": { + "name": "仅添加等级 >= X 的角色", + "help": "" } }, "Research": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 5a87a05b3..8e567535d 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -482,6 +482,7 @@ "LDPlayer3": "雷電模擬器3", "LDPlayer4": "雷電模擬器4", "LDPlayer9": "雷電模擬器9", + "LDPlayer14": "雷電模擬器14", "MuMuPlayer": "MuMu模擬器", "MuMuPlayerX": "MuMu模擬器X", "MuMuPlayer12": "MuMu模擬器12", @@ -1531,6 +1532,10 @@ "Favorite": { "name": "優先選擇常用", "help": "" + }, + "MinLevel": { + "name": "僅新增等級 >= X 的角色", + "help": "" } }, "Research": { diff --git a/module/daemon/benchmark.py b/module/daemon/benchmark.py index 9bc3d6c0b..19fcf7f07 100644 --- a/module/daemon/benchmark.py +++ b/module/daemon/benchmark.py @@ -39,7 +39,7 @@ class Benchmark(DaemonBase, CampaignUI): record = [] for n in range(1, self.TEST_TOTAL + 1): - start = time.time() + start = time.perf_counter() try: func(*args, **kwargs) @@ -52,7 +52,7 @@ class Benchmark(DaemonBase, CampaignUI): logger.warning(f'Benchmark tests failed on func: {func.__name__}') return 'Failed' - cost = time.time() - start + cost = time.perf_counter() - start logger.attr( f'{str(n).rjust(2, "0")}/{self.TEST_TOTAL}', f'{float2str(cost)}' @@ -194,6 +194,10 @@ class Benchmark(DaemonBase, CampaignUI): if device == 'android_phone_vmos': screenshot = ['ADB', 'aScreenCap', 'DroidCast', 'DroidCast_raw'] click = ['ADB', 'Hermit', 'MaaTouch'] + # Droidcast on SDK 23 (Android 6.0) to SDK 32 (Android 12) + if not (23 <= sdk <= 32): + screenshot = remove('DroidCast', 'DroidCast_raw') + if self.device.nemu_ipc_available(): screenshot.append('nemu_ipc') if self.device.ldopengl_available(): diff --git a/module/device/method/ldopengl.py b/module/device/method/ldopengl.py index fbd6009ca..b4e6c38f0 100644 --- a/module/device/method/ldopengl.py +++ b/module/device/method/ldopengl.py @@ -332,7 +332,7 @@ class LDOpenGL(Platform): if not self.is_ldplayer_bluestacks_family: return False logger.attr('EmulatorInfo_Emulator', self.config.EmulatorInfo_Emulator) - if self.config.EmulatorInfo_Emulator not in ['LDPlayer9']: + if self.config.EmulatorInfo_Emulator not in ['LDPlayer9', 'LDPlayer14']: return False try: diff --git a/module/device/platform/emulator_base.py b/module/device/platform/emulator_base.py index 19943fd95..522c6b51d 100644 --- a/module/device/platform/emulator_base.py +++ b/module/device/platform/emulator_base.py @@ -155,7 +155,8 @@ class EmulatorBase: LDPlayer3 = 'LDPlayer3' LDPlayer4 = 'LDPlayer4' LDPlayer9 = 'LDPlayer9' - LDPlayerFamily = [LDPlayer3, LDPlayer4, LDPlayer9] + LDPlayer14 = 'LDPlayer14' + LDPlayerFamily = [LDPlayer3, LDPlayer4, LDPlayer9, LDPlayer14] MuMuPlayer = 'MuMuPlayer' MuMuPlayerX = 'MuMuPlayerX' MuMuPlayer12 = 'MuMuPlayer12' diff --git a/module/device/platform/emulator_windows.py b/module/device/platform/emulator_windows.py index 78a5b6b0c..c48a0922a 100644 --- a/module/device/platform/emulator_windows.py +++ b/module/device/platform/emulator_windows.py @@ -110,6 +110,8 @@ class Emulator(EmulatorBase): return cls.LDPlayer4 elif dir1 == 'ldplayer9': return cls.LDPlayer9 + elif dir1 == 'ldplayer14': + return cls.LDPlayer14 else: return cls.LDPlayer3 if exe == 'nemuplayer.exe': @@ -538,8 +540,11 @@ class EmulatorManager(EmulatorManagerBase): exe.add(file) # LDPlayer install path - for path in [r'SOFTWARE\leidian\ldplayer', - r'SOFTWARE\leidian\ldplayer9']: + for path in [ + r'SOFTWARE\leidian\ldplayer', + r'SOFTWARE\leidian\ldplayer9', + r'SOFTWARE\leidian\ldplayer14', + ]: ld = self.get_install_dir_from_reg(path, 'InstallDir') if ld: ld = abspath(os.path.join(ld, './dnplayer.exe')) diff --git a/module/device/platform/platform_base.py b/module/device/platform/platform_base.py index 9d5c61b9c..944f7a8ff 100644 --- a/module/device/platform/platform_base.py +++ b/module/device/platform/platform_base.py @@ -181,6 +181,20 @@ class PlatformBase(Connection, EmulatorManagerBase): logger.info(f'Found emulator instance: {instance}') return instance + # search by emulator type first, which is the easiest setting for user to setup, so more trustworthy + # Multiple instances in given serial, name and path, search by emulator + if emulator: + search_args['type'] = emulator + select = instances.select(**search_args) + if select.count == 0: + logger.warning(f'No emulator instances with {search_args}, type invalid') + search_args.pop('type') + elif select.count == 1: + instance = select[0] + logger.hr('Emulator instance', level=2) + logger.info(f'Found emulator instance: {instance}') + return instance + # Multiple instances in given serial, search by name if name: search_args['name'] = name @@ -207,19 +221,6 @@ class PlatformBase(Connection, EmulatorManagerBase): logger.info(f'Found emulator instance: {instance}') return instance - # Multiple instances in given serial, name and path, search by emulator - if emulator: - search_args['type'] = emulator - select = instances.select(**search_args) - if select.count == 0: - logger.warning(f'No emulator instances with {search_args}, type invalid') - search_args.pop('type') - elif select.count == 1: - instance = select[0] - logger.hr('Emulator instance', level=2) - logger.info(f'Found emulator instance: {instance}') - return instance - # Still too many instances, search from running emulators running = remove_duplicated_path(list(self.iter_running_emulator())) logger.info('Running emulators') diff --git a/module/reward/reward.py b/module/reward/reward.py index 9a839bf6b..b713691f8 100644 --- a/module/reward/reward.py +++ b/module/reward/reward.py @@ -89,6 +89,8 @@ class Reward(UI): click_interval.reset() clicked = True continue + if self.appear(MISSION_UNFINISH, offset=(20, 20)): + return clicked def _reward_mission_claim_receive(self): """ diff --git a/module/tactical/tactical_class.py b/module/tactical/tactical_class.py index 3c2e9f022..b8c9f5b9d 100644 --- a/module/tactical/tactical_class.py +++ b/module/tactical/tactical_class.py @@ -643,27 +643,34 @@ class RewardTacticalClass(Dock): # Wait until they turn into # [120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120] level_ocr = LevelOcr(CARD_LEVEL_GRIDS.buttons, name='DOCK_LEVEL_OCR', threshold=64) - timeout = Timer(1, count=1).start() - while 1: + list_level = [] + for _ in self.loop(timeout=1): list_level = level_ocr.ocr(self.device.image) first_ship = next((i for i, x in enumerate(list_level) if x > 0), len(list_level)) first_empty = next((i for i, x in enumerate(list_level) if x == 0), len(list_level)) - if timeout.reached(): - logger.warning('Wait ship cards timeout') - break if first_empty >= first_ship: break - self.device.screenshot() + else: + logger.warning('Wait ship cards timeout') + + try: + min_level = int(self.config.AddNewStudent_MinLevel) + if min_level < 1: + min_level = 1 + except (ValueError, TypeError) as e: + logger.warning(f'Invalid AddNewStudent_MinLevel: {self.config.AddNewStudent_MinLevel}, {e}') + min_level = 1 + logger.attr('AddNewStudent_MinLevel', min_level) should_select_button = None for button, level in list(zip(CARD_GRIDS.buttons, list_level))[self.dock_select_index:]: # Select ship LV > 1 only - if level > 1: + if level >= min_level: should_select_button = button break if should_select_button is None: - logger.info('No ships with level > 1 in dock') + logger.info(f'No ships with level >= {min_level} in dock') return False # select a ship