1
0
mirror of https://github.com/0O0o0oOoO00/Alas.git synced 2026-05-14 13:39:25 +08:00

Merge branch '20230309' into smallkai

This commit is contained in:
xhqss
2024-04-11 22:12:34 +08:00
120 changed files with 1417 additions and 723 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -189,3 +189,5 @@ To add a new event, add a new row in here, and run `python -m module.config.conf
| 20240314 | event 20220324 cn | Virtual Tower Rerun | 复刻虚像构筑之塔 | Virtual Tower Rerun | 幻像の塔(復刻) | - |
| 20240321 | raid 20230629 | Reflections of the Oasis | - | - | - | 綠洲往事 |
| 20240328 | raid 20240328 | From Zero to Hero | 从零开始的魔王讨伐之旅 | From Zero to Hero | ゼロから頑張る魔王討伐 | - |
| 20240403 | event 20211111 cn | The Flame-Touched Dagger | - | - | - | 復刻杰諾瓦的焰火 |
| 20240411 | event 20220224 cn | Abyssal Refrain Rerun | 复刻深度回音 | Abyssal Refrain Rerun | 鳴動せし星霜の淵(復刻) | - |

View File

@@ -54,6 +54,13 @@ class Config(ConfigBase):
MAP_HAS_AMBUSH = False
MAP_HAS_MYSTERY = True
# ===== End of generated config =====
HOMO_EDGE_COLOR_RANGE = (0, 12)
HOMO_EDGE_HOUGHLINES_THRESHOLD = 210
MAP_SWIPE_MULTIPLY = (1.101, 1.122)
MAP_SWIPE_MULTIPLY_MINITOUCH = (1.065, 1.085)
MAP_SWIPE_MULTIPLY_MAATOUCH = (1.034, 1.053)
HOMO_STORAGE = ((6, 5), [(211, 175), (782, 175), (158, 569), (800, 569)])
MAP_ENSURE_EDGE_INSIGHT_CORNER = 'top'
class Campaign(CampaignBase):

View File

@@ -58,6 +58,13 @@ class Config(ConfigBase):
MAP_HAS_AMBUSH = False
MAP_HAS_MYSTERY = False
# ===== End of generated config =====
HOMO_EDGE_COLOR_RANGE = (0, 12)
HOMO_EDGE_HOUGHLINES_THRESHOLD = 210
MAP_SWIPE_MULTIPLY = (1.101, 1.122)
MAP_SWIPE_MULTIPLY_MINITOUCH = (1.065, 1.085)
MAP_SWIPE_MULTIPLY_MAATOUCH = (1.034, 1.053)
HOMO_STORAGE = ((6, 4), [(448, 180), (1051, 180), (426, 513), (1100, 513)])
MAP_ENSURE_EDGE_INSIGHT_CORNER = 'top'
class Campaign(CampaignBase):

View File

@@ -55,6 +55,13 @@ class Config(ConfigBase):
MAP_HAS_AMBUSH = False
MAP_HAS_MYSTERY = True
# ===== End of generated config =====
HOMO_EDGE_COLOR_RANGE = (0, 12)
HOMO_EDGE_HOUGHLINES_THRESHOLD = 210
MAP_SWIPE_MULTIPLY = (1.101, 1.122)
MAP_SWIPE_MULTIPLY_MINITOUCH = (1.065, 1.085)
MAP_SWIPE_MULTIPLY_MAATOUCH = (1.034, 1.053)
HOMO_STORAGE = ((6, 5), [(211, 175), (782, 175), (158, 569), (800, 569)])
MAP_ENSURE_EDGE_INSIGHT_CORNER = 'top'
class Campaign(CampaignBase):

View File

@@ -58,6 +58,13 @@ class Config(ConfigBase):
MAP_HAS_AMBUSH = False
MAP_HAS_MYSTERY = False
# ===== End of generated config =====
HOMO_EDGE_COLOR_RANGE = (0, 12)
HOMO_EDGE_HOUGHLINES_THRESHOLD = 210
MAP_SWIPE_MULTIPLY = (1.101, 1.122)
MAP_SWIPE_MULTIPLY_MINITOUCH = (1.065, 1.085)
MAP_SWIPE_MULTIPLY_MAATOUCH = (1.034, 1.053)
HOMO_STORAGE = ((6, 4), [(448, 180), (1051, 180), (426, 513), (1100, 513)])
MAP_ENSURE_EDGE_INSIGHT_CORNER = 'top'
class Campaign(CampaignBase):

View File

@@ -315,11 +315,12 @@
},
"GemsFarming": {
"ChangeFlagship": "ship",
"CommonCV": "any",
"ChangeVanguard": "ship",
"CommonDD": "any",
"ALLowLowVanguardLevel": true,
"FleetNumberInHardMode": 0,
"StopIFAutoNotEnsured": true,
"CommonCV": "any",
"CommissionLimit": true
},
"FlagshipFilter": {

View File

@@ -53,5 +53,5 @@ class AppManager(DeployConfig):
Progress.UpdateAlasApp()
return False
self.app_asar_replace(os.getcwd())
Progress.UpdateAlasApp()
# self.app_asar_replace(os.getcwd())
# Progress.UpdateAlasApp()

View File

@@ -55,7 +55,7 @@ class ConfigModel:
# Webui
WebuiHost: str = "0.0.0.0"
WebuiPort: int = 22267
WebuiPort: int = 22367
Language: str = "en-US"
Theme: str = "default"
DpiScaling: bool = True
@@ -80,42 +80,21 @@ class DeployConfig(ConfigModel):
self.config_template = {}
self.read()
# Redirection
if self.Repository in [
'https://gitee.com/LmeSzinc/AzurLaneAutoScript',
'https://gitee.com/lmeszinc/azur-lane-auto-script-mirror',
'https://e.coding.net/llop18870/alas/AzurLaneAutoScript.git',
'https://e.coding.net/saarcenter/alas/AzurLaneAutoScript.git',
'https://git.saarcenter.com/LmeSzinc/AzurLaneAutoScript.git',
]:
self.Repository = 'git://git.lyoko.io/AzurLaneAutoScript'
# Bypass webui.config.DeployConfig.__setattr__()
# Don't write these into deploy.yaml
super().__setattr__(
'GitOverCdn',
self.Repository == 'git://git.lyoko.io/AzurLaneAutoScript' and self.Branch == 'master'
)
if self.Repository in ['global']:
super().__setattr__('Repository', 'https://github.com/LmeSzinc/AzurLaneAutoScript')
if self.Repository in ['cn']:
super().__setattr__('Repository', 'git://git.lyoko.io/AzurLaneAutoScript')
super().__setattr__('GitOverCdn', self.Repository in ['cn'])
if self.Repository in ['global', 'cn']:
super().__setattr__('Repository', 'https://github.com/LmeSzinc/StarRailCopilot')
self.write()
self.show_config()
@cached_property
def flag_feature_test_0_4_0(self):
flag = os.path.exists('./toolkit/flag_feature_test_0_4_0')
logger.info(f'flag_feature_test_0_4_0: {flag}')
return flag
def show_config(self):
logger.hr("Show deploy config", 1)
for k, v in self.config.items():
if k in ("Password", "SSHUser"):
continue
if self.config_template.get(k) == v:
if self.config_template[k] == v:
continue
logger.info(f"{k}: {v}")

View File

@@ -2,6 +2,7 @@ import asyncio
import filecmp
import os
import shutil
import sys
import typing as t
from dataclasses import dataclass
@@ -9,7 +10,8 @@ from deploy.Windows.alas import AlasManager
from deploy.Windows.logger import logger
from deploy.Windows.utils import cached_property
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
if sys.platform.startswith("win"):
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
@dataclass

View File

@@ -2,9 +2,10 @@ import configparser
import os
from deploy.Windows.config import DeployConfig
from deploy.git_over_cdn.client import GitOverCdnClient
from deploy.Windows.logger import Progress, logger
from deploy.Windows.utils import *
from deploy.Windows.utils import cached_property
from deploy.git_over_cdn.client import GitOverCdnClient
class GitConfigParser(configparser.ConfigParser):
def check(self, section, option, value):
@@ -16,6 +17,25 @@ class GitConfigParser(configparser.ConfigParser):
return False
class GitOverCdnClientWindows(GitOverCdnClient):
def update(self, *args, **kwargs):
Progress.GitInit()
_ = super().update(*args, **kwargs)
Progress.GitShowVersion()
return _
@cached_property
def latest_commit(self) -> str:
_ = super().latest_commit
Progress.GitLatestCommit()
return _
def download_pack(self):
_ = super().download_pack()
Progress.GitDownloadPack()
return _
class GitManager(DeployConfig):
@staticmethod
def remove(file):
@@ -111,7 +131,7 @@ class GitManager(DeployConfig):
@property
def goc_client(self):
client = GitOverCdnClient(
url='https://vip.123pan.cn/1818706573/pack/LmeSzinc_AzurLaneAutoScript_master',
url='https://vip.123pan.cn/1815343254/pack/LmeSzinc_StarRailCopilot_master',
folder=self.root_filepath,
source='origin',
branch='master',

View File

@@ -61,6 +61,9 @@ class Progress:
GitCheckout = Percentage(48)
GitShowVersion = Percentage(50)
GitLatestCommit = Percentage(25)
GitDownloadPack = Percentage(40)
KillExisting = Percentage(60)
UpdateDependency = Percentage(70)
UpdateAlasApp = Percentage(75)

View File

@@ -1,9 +1,9 @@
Deploy:
Git:
# URL of AzurLaneAutoScript repository
# [CN user] Use 'git://git.lyoko.io/AzurLaneAutoScript' for faster and more stable download
# [Other] Use 'https://github.com/LmeSzinc/AzurLaneAutoScript'
Repository: 'https://github.com/LmeSzinc/AzurLaneAutoScript'
# [CN user] Use 'cn' to get update from git-over-cdn service
# [Other] Use 'global' to get update from https://github.com/LmeSzinc/StarRailCopilot
Repository: 'global'
# Branch of Alas
# [Developer] Use 'dev', 'app', etc, to try new features
# [Other] Use 'master', the stable branch
@@ -128,13 +128,14 @@ Deploy:
WebuiHost: 0.0.0.0
# --port. Port to listen
# You will be able to access webui via `http://{host}:{port}`
# [In most cases] Default to 22267
WebuiPort: 22267
# [In most cases] Default to 22367
WebuiPort: 22367
# Language to use on web ui
# 'zh-CN' for Chinese simplified
# 'en-US' for English
# 'ja-JP' for Japanese
# 'zh-TW' for Chinese traditional
# 'es-ES' for Spanish
Language: en-US
# Theme of web ui
# 'default' for light theme

View File

@@ -5,21 +5,29 @@ from module.combat.assets import BATTLE_PREPARATION
from module.equipment.assets import *
from module.equipment.equipment_change import EquipmentChange
from module.equipment.fleet_equipment import OCR_FLEET_INDEX
from module.exception import CampaignEnd, RequestHumanTakeover
from module.exception import CampaignEnd, ScriptError, RequestHumanTakeover
from module.handler.assets import AUTO_SEARCH_MAP_OPTION_OFF
from module.logger import logger
from module.map.assets import (FLEET_PREPARATION, MAP_PREPARATION, FLEET_ENTER_FLAGSHIP_HARD_1,
from module.map.assets import (FLEET_ENTER_FLAGSHIP_HARD_1,
FLEET_ENTER_FLAGSHIP_HARD_2, FLEET_ENTER_HARD_1, FLEET_ENTER_HARD_2,
FLEET_ENTER_FLAGSHIP_HARD_1_3, FLEET_ENTER_FLAGSHIP_HARD_2_3, FLEET_ENTER_HARD_1_3,
FLEET_ENTER_HARD_2_3)
from module.retire.assets import (DOCK_CHECK, TEMPLATE_BOGUE, TEMPLATE_HERMES, TEMPLATE_LANGLEY, TEMPLATE_RANGER,
from module.retire.assets import (
DOCK_SHIP_DOWN)
from module.map.assets import FLEET_PREPARATION, MAP_PREPARATION
from module.retire.assets import (
DOCK_CHECK,
TEMPLATE_BOGUE, TEMPLATE_HERMES, TEMPLATE_LANGLEY, TEMPLATE_RANGER,
TEMPLATE_CASSIN_1, TEMPLATE_CASSIN_2, TEMPLATE_DOWNES_1, TEMPLATE_DOWNES_2,
TEMPLATE_AULICK, TEMPLATE_FOOTE
)
from module.retire.dock import Dock
from module.retire.scanner import ShipScanner
from module.ui.page import page_fleet, page_event
from module.ui.assets import BACK_ARROW
from module.config.config import deep_get
import inflection
from module.ui.page import page_fleet
SIM_VALUE = 0.95
@@ -216,6 +224,7 @@ class GemsFarming(CampaignRun, Dock, EquipmentChange):
Returns:
bool: True if vanguard changed
"""
logger.hr('Change vanguard', level=1)
logger.attr('ChangeVanguard', self.config.GemsFarming_ChangeVanguard)
if self.change_vanguard_equip:
@@ -243,7 +252,8 @@ class GemsFarming(CampaignRun, Dock, EquipmentChange):
self.dock_select_one(button)
self.dock_filter_set()
self.dock_select_confirm(check_button=self.page_fleet_check_button)
self.dock_sort_method_dsc_set()
self.dock_select_confirm(check_button=page_fleet.check_button)
def get_common_rarity_cv(self, lv=31, emotion=16):
"""
@@ -260,7 +270,6 @@ class GemsFarming(CampaignRun, Dock, EquipmentChange):
scanner.disable('rarity')
if self.config.GemsFarming_CommonCV == 'any':
logger.info('')
self.dock_sort_method_dsc_set(False)
@@ -326,13 +335,61 @@ class GemsFarming(CampaignRun, Dock, EquipmentChange):
fleet=self.config.Fleet_Fleet1, status='free')
scanner.disable('rarity')
self.dock_sort_method_dsc_set()
ships = scanner.scan(self.device.image)
if ships:
# Don't need to change current
return ships
scanner.set_limitation(fleet=0)
return scanner.scan(self.device.image, output=False)
candidates = self.find_candidates(self.get_templates(self.config.GemsFarming_CommonDD), scanner)
if candidates:
return candidates
else:
logger.info('No specific DD was found, try reversed order.')
return candidates
def find_candidates(self, template, scanner):
"""
Find candidates based on template matching using a scanner.
"""
candidates = []
for item in template:
candidates = [ship for ship in scanner.scan(self.device.image, output=False)
if item.match(self.image_crop(ship.button), similarity=SIM_VALUE)]
if candidates:
break
return candidates
@staticmethod
def get_templates(common_dd):
"""
Returns the corresponding template list based on CommonDD
"""
if common_dd == 'any':
return [
TEMPLATE_CASSIN_1, TEMPLATE_CASSIN_2,
TEMPLATE_DOWNES_1, TEMPLATE_DOWNES_2,
TEMPLATE_AULICK,
TEMPLATE_FOOTE
]
elif common_dd == 'aulick_or_foote':
return [
TEMPLATE_AULICK,
TEMPLATE_FOOTE
]
elif common_dd == 'cassin_or_downes':
return [
TEMPLATE_CASSIN_1, TEMPLATE_CASSIN_2,
TEMPLATE_DOWNES_1, TEMPLATE_DOWNES_2
]
else:
logger.error(f'Invalid CommonDD setting: {common_dd}')
raise ScriptError(f'Invalid CommonDD setting: {common_dd}')
def solve_hard_flagship_black(self):
if self.hard_mode:

View File

@@ -116,7 +116,8 @@
"aScreenCap_nc",
"DroidCast",
"DroidCast_raw",
"scrcpy"
"scrcpy",
"nemu_ipc"
]
},
"ControlMethod": {
@@ -127,7 +128,8 @@
"uiautomator2",
"minitouch",
"Hermit",
"MaaTouch"
"MaaTouch",
"nemu_ipc"
]
},
"ScreenshotDedithering": {
@@ -1606,6 +1608,17 @@
"ship_equip"
]
},
"CommonCV": {
"type": "select",
"value": "any",
"option": [
"any",
"langley",
"bogue",
"ranger",
"hermes"
]
},
"ChangeVanguard": {
"type": "select",
"value": "ship",
@@ -1615,6 +1628,15 @@
"ship_equip"
]
},
"CommonDD": {
"type": "select",
"value": "any",
"option": [
"any",
"aulick_or_foote",
"cassin_or_downes"
]
},
"ALLowLowVanguardLevel": {
"type": "checkbox",
"value": true
@@ -1632,17 +1654,6 @@
"type": "checkbox",
"value": true
},
"CommonCV": {
"type": "select",
"value": "any",
"option": [
"any",
"langley",
"bogue",
"ranger",
"hermes"
]
},
"CommissionLimit": {
"type": "checkbox",
"value": true
@@ -1891,13 +1902,13 @@
],
"display": "hide",
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -2224,13 +2235,13 @@
"event_20240229_cn"
],
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -2672,13 +2683,13 @@
"event_20240229_cn"
],
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -4514,13 +4525,13 @@
"event_20240229_cn"
],
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -4979,13 +4990,13 @@
"event_20240229_cn"
],
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -5444,13 +5455,13 @@
"event_20240229_cn"
],
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -5909,13 +5920,13 @@
"event_20240229_cn"
],
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -6364,13 +6375,13 @@
"event_20240229_cn"
],
"option_bold": [
"event_20210422_cn",
"event_20220324_cn"
"event_20211111_cn",
"event_20220224_cn"
],
"cn": "event_20220324_cn",
"en": "event_20220324_cn",
"jp": "event_20220324_cn",
"tw": "event_20210422_cn"
"cn": "event_20220224_cn",
"en": "event_20220224_cn",
"jp": "event_20220224_cn",
"tw": "event_20211111_cn"
},
"Mode": {
"type": "select",
@@ -9835,11 +9846,9 @@
"value": "emulator",
"option": [
"emulator",
"emulator_android_12",
"plone_cloud_with_adb",
"phone_cloud_without_adb",
"android_phone",
"android_phone_12",
"android_phone_vmos"
]
},

View File

@@ -32,10 +32,28 @@ Emulator:
option: [ disabled, ]
ScreenshotMethod:
value: auto
option: [ auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy ]
option: [
auto,
ADB,
ADB_nc,
uiautomator2,
aScreenCap,
aScreenCap_nc,
DroidCast,
DroidCast_raw,
scrcpy,
nemu_ipc,
]
ControlMethod:
value: minitouch
option: [ ADB, uiautomator2, minitouch, Hermit, MaaTouch ]
option: [
ADB,
uiautomator2,
minitouch,
Hermit,
MaaTouch,
nemu_ipc,
]
ScreenshotDedithering: false
AdbRestart: false
EmulatorInfo:
@@ -251,17 +269,20 @@ GemsFarming:
ChangeFlagship:
value: ship
option: [ ship, ship_equip ]
CommonCV:
value: any
option: [ any, langley, bogue, ranger, hermes ]
ChangeVanguard:
value: ship
option: [ disabled, ship, ship_equip ]
CommonDD:
value: any
option: [ any, aulick_or_foote , cassin_or_downes ]
ALLowLowVanguardLevel: true
FleetNumberInHardMode:
value: 0
option: [ 0, 1, 2 ]
StopIFAutoNotEnsured: true
CommonCV:
value: any
option: [ any, langley, bogue, ranger, hermes ]
CommissionLimit: true
FlagshipFilter:
Sort:
@@ -732,9 +753,11 @@ Benchmark:
DeviceType:
value: emulator
option: [
emulator, emulator_android_12,
plone_cloud_with_adb, phone_cloud_without_adb,
android_phone, android_phone_12, android_phone_vmos
emulator,
plone_cloud_with_adb,
phone_cloud_without_adb,
android_phone,
android_phone_vmos,
]
TestScene:
value: screenshot_click

View File

@@ -21,8 +21,8 @@ class GeneratedConfig:
Emulator_Serial = 'auto'
Emulator_PackageName = 'auto' # auto, com.bilibili.azurlane, com.YoStarEN.AzurLane, com.YoStarJP.AzurLane, com.hkmanjuu.azurlane.gp, com.bilibili.blhx.huawei, com.bilibili.blhx.mi, com.tencent.tmgp.bilibili.blhx, com.bilibili.blhx.baidu, com.bilibili.blhx.qihoo, com.bilibili.blhx.nearme.gamecenter, com.bilibili.blhx.vivo, com.bilibili.blhx.mz, com.bilibili.blhx.dl, com.bilibili.blhx.lenovo, com.bilibili.blhx.uc, com.bilibili.blhx.mzw, com.yiwu.blhx.yx15, com.bilibili.blhx.m4399, com.bilibili.blhx.bilibiliMove, com.hkmanjuu.azurlane.gp.mc
Emulator_ServerName = 'disabled' # disabled, cn_android-0, cn_android-1, cn_android-2, cn_android-3, cn_android-4, cn_android-5, cn_android-6, cn_android-7, cn_android-8, cn_android-9, cn_android-10, cn_android-11, cn_android-12, cn_android-13, cn_android-14, cn_android-15, cn_android-16, cn_android-17, cn_android-18, cn_android-19, cn_android-20, cn_android-21, cn_android-22, cn_android-23, cn_ios-0, cn_ios-1, cn_ios-2, cn_ios-3, cn_ios-4, cn_ios-5, cn_ios-6, cn_ios-7, cn_ios-8, cn_ios-9, cn_ios-10, cn_channel-0, cn_channel-1, cn_channel-2, cn_channel-3, cn_channel-4, en-0, en-1, en-2, en-3, en-4, en-5, jp-0, jp-1, jp-2, jp-3, jp-4, jp-5, jp-6, jp-7, jp-8, jp-9, jp-10, jp-11, jp-12, jp-13, jp-14, jp-15, jp-16, jp-17
Emulator_ScreenshotMethod = 'auto' # auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy
Emulator_ControlMethod = 'minitouch' # ADB, uiautomator2, minitouch, Hermit, MaaTouch
Emulator_ScreenshotMethod = 'auto' # auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy, nemu_ipc
Emulator_ControlMethod = 'minitouch' # ADB, uiautomator2, minitouch, Hermit, MaaTouch, nemu_ipc
Emulator_ScreenshotDedithering = False
Emulator_AdbRestart = False
@@ -154,6 +154,7 @@ class GeneratedConfig:
GemsFarming_FleetNumberInHardMode = 0 # 0, 1, 2
GemsFarming_StopIFAutoNotEnsured = True
GemsFarming_CommonCV = 'any' # any, langley, bogue, ranger, hermes
GemsFarming_CommonDD = 'any' # any, aulick_or_foote, cassin_or_downes
GemsFarming_CommissionLimit = True
# Group `FlagshipFilter`
@@ -448,7 +449,7 @@ class GeneratedConfig:
OpsiDaemon_SelectEnemy = True
# Group `Benchmark`
Benchmark_DeviceType = 'emulator' # emulator, emulator_android_12, plone_cloud_with_adb, phone_cloud_without_adb, android_phone, android_phone_12, android_phone_vmos
Benchmark_DeviceType = 'emulator' # emulator, plone_cloud_with_adb, phone_cloud_without_adb, android_phone, android_phone_vmos
Benchmark_TestScene = 'screenshot_click' # screenshot_click, screenshot, click
# Group `AzurLaneUncensored`

View File

@@ -755,6 +755,11 @@ class ConfigUpdater:
key = key.split(".")
key[-1] = key[-1].replace("Value", "Record")
yield ".".join(key), datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Oh no, dynamic dropdown update can only be used on pywebio > 1.8.0
# elif key == 'Alas.Emulator.ScreenshotMethod' and value == 'nemu_ipc':
# yield 'Alas.Emulator.ControlMethod', 'nemu_ipc'
# elif key == 'Alas.Emulator.ControlMethod' and value == 'nemu_ipc':
# yield 'Alas.Emulator.ScreenshotMethod', 'nemu_ipc'
def read_file(self, config_name, is_template=False):
"""

View File

@@ -417,7 +417,8 @@
"aScreenCap_nc": "aScreenCap_nc",
"DroidCast": "DroidCast",
"DroidCast_raw": "DroidCast_raw",
"scrcpy": "scrcpy"
"scrcpy": "scrcpy",
"nemu_ipc": "nemu_ipc"
},
"ControlMethod": {
"name": "Control Method",
@@ -426,7 +427,8 @@
"uiautomator2": "uiautomator2",
"minitouch": "minitouch",
"Hermit": "Hermit",
"MaaTouch": "MaaTouch"
"MaaTouch": "MaaTouch",
"nemu_ipc": "nemu_ipc"
},
"ScreenshotDedithering": {
"name": "Image Color De-dithering",
@@ -709,7 +711,7 @@
"event_20211125_cn": "World-spanning Arclight Rerun",
"event_20211229_cn": "Tower of Transcendence Rerun",
"event_20220210_cn": "Northern Overture Rerun",
"event_20220224_cn": "Abyssal Refrain",
"event_20220224_cn": "Abyssal Refrain Rerun",
"event_20220310_tw": "復刻斯圖爾特的硝煙",
"event_20220324_cn": "Virtual Tower Rerun",
"event_20220407_tw": "蒼紅的迴響(復刻)",
@@ -1140,6 +1142,15 @@
"ship": "Change Ship",
"ship_equip": "Change Ship + Gears"
},
"CommonCV": {
"name": "Flagship Common CV/CVL Preference",
"help": "",
"any": "any",
"langley": "langley",
"bogue": "bogue",
"ranger": "ranger",
"hermes": "hermes"
},
"ChangeVanguard": {
"name": "Change Vanguard",
"help": "Change vanguard if flagship or vanguard are emotion exhausted.If you choose not to change, your fleet will ignore the low emotion warning and continue combat.\nSwitch out to new level 100(70) common vanguard after current flagship has reached level 32+ (Only CN players'0 limit break ship can reach level 100)\n\nThe vanguard's equipment is replaced when being switched out by first recording the current setup. Only populated equipment slots will be replaced.",
@@ -1162,14 +1173,12 @@
"name": "GemsFarming.StopIFAutoNotEnsured.name",
"help": "GemsFarming.StopIFAutoNotEnsured.help"
},
"CommonCV": {
"name": "Flagship Common CV/CVL Preference",
"CommonDD": {
"name": "Flagship Common DD Preference",
"help": "",
"any": "any",
"langley": "langley",
"bogue": "bogue",
"ranger": "ranger",
"hermes": "hermes"
"aulick_or_foote": "aulick or foote",
"cassin_or_downes": "cassin or downes"
},
"CommissionLimit": {
"name": "Prevent Too Many Urgent Commissions",
@@ -2629,13 +2638,11 @@
"DeviceType": {
"name": "Device Type",
"help": "",
"emulator": "Emulator (Android <= 9)",
"emulator_android_12": "Emulator (Android > 9)",
"plone_cloud_with_adb": "Phone cloud with public network ADB",
"phone_cloud_without_adb": "Phone cloud without public network ADB",
"android_phone": "Android Phone (Android <= 9)",
"android_phone_12": "Android Phone (Android > 9)",
"android_phone_vmos": "Android Phone (VMOS emulator)"
"emulator": "Emulators",
"plone_cloud_with_adb": "Phone clouds with public network ADB",
"phone_cloud_without_adb": "Phone clouds without public network ADB",
"android_phone": "Android Phones",
"android_phone_vmos": "Android Phones (VMOS emulator)"
},
"TestScene": {
"name": "Test Scene",

View File

@@ -417,7 +417,8 @@
"aScreenCap_nc": "aScreenCap_nc",
"DroidCast": "DroidCast",
"DroidCast_raw": "DroidCast_raw",
"scrcpy": "scrcpy"
"scrcpy": "scrcpy",
"nemu_ipc": "nemu_ipc"
},
"ControlMethod": {
"name": "Emulator.ControlMethod.name",
@@ -426,7 +427,8 @@
"uiautomator2": "uiautomator2",
"minitouch": "minitouch",
"Hermit": "Hermit",
"MaaTouch": "MaaTouch"
"MaaTouch": "MaaTouch",
"nemu_ipc": "nemu_ipc"
},
"ScreenshotDedithering": {
"name": "Emulator.ScreenshotDedithering.name",
@@ -709,7 +711,7 @@
"event_20211125_cn": "弧光は交わる世界にて(復刻)",
"event_20211229_cn": "遡望せし虹彩の塔(復刻)",
"event_20220210_cn": "凍絶の北海(復刻)",
"event_20220224_cn": "鳴動せし星霜の淵",
"event_20220224_cn": "鳴動せし星霜の淵(復刻)",
"event_20220310_tw": "復刻斯圖爾特的硝煙",
"event_20220324_cn": "幻像の塔(復刻)",
"event_20220407_tw": "蒼紅的迴響(復刻)",
@@ -1140,13 +1142,6 @@
"ship": "ship",
"ship_equip": "ship_equip"
},
"ChangeVanguard": {
"name": "GemsFarming.ChangeVanguard.name",
"help": "GemsFarming.ChangeVanguard.help",
"disabled": "disabled",
"ship": "ship",
"ship_equip": "ship_equip"
},
"ALLowLowVanguardLevel": {
"name": "GemsFarming.ALLowLowVanguardLevel.name",
"help": "GemsFarming.ALLowLowVanguardLevel.help"
@@ -1171,6 +1166,20 @@
"ranger": "ranger",
"hermes": "hermes"
},
"ChangeVanguard": {
"name": "GemsFarming.ChangeVanguard.name",
"help": "GemsFarming.ChangeVanguard.help",
"disabled": "disabled",
"ship": "ship",
"ship_equip": "ship_equip"
},
"CommonDD": {
"name": "GemsFarming.CommonDD.name",
"help": "GemsFarming.CommonDD.help",
"any": "any",
"aulick_or_foote": "aulick or foote",
"cassin_or_downes": "cassin or downes"
},
"CommissionLimit": {
"name": "GemsFarming.CommissionLimit.name",
"help": "GemsFarming.CommissionLimit.help"
@@ -2630,11 +2639,9 @@
"name": "Benchmark.DeviceType.name",
"help": "Benchmark.DeviceType.help",
"emulator": "emulator",
"emulator_android_12": "emulator_android_12",
"plone_cloud_with_adb": "plone_cloud_with_adb",
"phone_cloud_without_adb": "phone_cloud_without_adb",
"android_phone": "android_phone",
"android_phone_12": "android_phone_12",
"android_phone_vmos": "android_phone_vmos"
},
"TestScene": {

View File

@@ -417,7 +417,8 @@
"aScreenCap_nc": "aScreenCap_nc",
"DroidCast": "DroidCast",
"DroidCast_raw": "DroidCast_raw",
"scrcpy": "scrcpy"
"scrcpy": "scrcpy",
"nemu_ipc": "nemu_ipc"
},
"ControlMethod": {
"name": "模拟器控制方案",
@@ -426,7 +427,8 @@
"uiautomator2": "uiautomator2",
"minitouch": "minitouch",
"Hermit": "Hermit",
"MaaTouch": "MaaTouch"
"MaaTouch": "MaaTouch",
"nemu_ipc": "nemu_ipc"
},
"ScreenshotDedithering": {
"name": "去除图片色彩抖动",
@@ -709,7 +711,7 @@
"event_20211125_cn": "复刻交汇世界的弧光",
"event_20211229_cn": "复刻逆转彩虹之塔",
"event_20220210_cn": "复刻北境序曲",
"event_20220224_cn": "深度回音",
"event_20220224_cn": "复刻深度回音",
"event_20220310_tw": "復刻斯圖爾特的硝煙",
"event_20220324_cn": "复刻虚像构筑之塔",
"event_20220407_tw": "蒼紅的迴響(復刻)",
@@ -1140,6 +1142,15 @@
"ship": "更换舰船",
"ship_equip": "更换舰船 + 装备"
},
"CommonCV": {
"name": "指定旗舰航母",
"help": "",
"any": "任意",
"langley": "兰利",
"bogue": "博格",
"ranger": "突击者",
"hermes": "竞技神"
},
"ChangeVanguard": {
"name": "更换前排",
"help": "当前排红脸时更换前排,选择不更换则会强制红脸出击\n换前排通过找一艘心情不低于16、等级100的白鹰白皮驱逐完成所以尽量保证有足够多的驱逐。国服以外则为等级70的白鹰白船驱逐。\n\n换装备只会更换正在装备中的栏位即使是白装也会更换。如果指定了旗舰则会更换全部5个装备未指定旗舰只会更换设备。",
@@ -1162,14 +1173,12 @@
"name": "无法设置职能时停止任务",
"help": "无法设置舰队职能时,以以下方式停止任务以免跨队队伍出击,石油被大量消耗:\n若设置了错误推送则推送任务停止的消息并停止该任务。\n若没有设置错误推送则直接停止Alas。"
},
"CommonCV": {
"name": "指定旗舰航母",
"CommonDD": {
"name": "指定前排",
"help": "",
"any": "任意",
"langley": "兰利",
"bogue": "博格",
"ranger": "突击者",
"hermes": "竞技神"
"aulick_or_foote": "奥利克或富特",
"cassin_or_downes": "卡辛或唐斯"
},
"CommissionLimit": {
"name": "防止紧急委托数量过多",
@@ -2629,12 +2638,10 @@
"DeviceType": {
"name": "设备类型",
"help": "",
"emulator": "模拟器 安卓<=9",
"emulator_android_12": "模拟器 安卓>9",
"emulator": "模拟器",
"plone_cloud_with_adb": "云手机 有公网ADB",
"phone_cloud_without_adb": "云手机 无公网ADB",
"android_phone": "安卓真机 安卓<=9",
"android_phone_12": "安卓真机 安卓>9",
"android_phone": "安卓真机",
"android_phone_vmos": "安卓真机VMOS虚拟机"
},
"TestScene": {

View File

@@ -417,7 +417,8 @@
"aScreenCap_nc": "aScreenCap_nc",
"DroidCast": "DroidCast",
"DroidCast_raw": "DroidCast_raw",
"scrcpy": "scrcpy"
"scrcpy": "scrcpy",
"nemu_ipc": "nemu_ipc"
},
"ControlMethod": {
"name": "模擬器控制方案",
@@ -426,7 +427,8 @@
"uiautomator2": "uiautomator2",
"minitouch": "minitouch",
"Hermit": "Hermit",
"MaaTouch": "MaaTouch"
"MaaTouch": "MaaTouch",
"nemu_ipc": "nemu_ipc"
},
"ScreenshotDedithering": {
"name": "去除圖片色彩抖動",
@@ -705,7 +707,7 @@
"event_20210916_cn": "碧海光粼",
"event_20211028_cn": "復刻穹頂下的聖詠曲",
"event_20211028_tw": "復刻光與影的鳶尾之華",
"event_20211111_cn": "杰諾瓦的焰火",
"event_20211111_cn": "復刻杰諾瓦的焰火",
"event_20211125_cn": "復刻-交匯世界的弧光",
"event_20211229_cn": "逆轉彩虹之塔",
"event_20220210_cn": "復刻北境序曲",
@@ -1140,6 +1142,15 @@
"ship": "更換艦船",
"ship_equip": "更換艦船 + 裝備"
},
"CommonCV": {
"name": "指定旗艦航母",
"help": "",
"any": "任意",
"langley": "蘭利",
"bogue": "博格",
"ranger": "突擊者",
"hermes": "競技神"
},
"ChangeVanguard": {
"name": "更換前排",
"help": "當前排紅臉時更換前排,選擇不更換則會強制紅臉出擊\n換前排通過找一艘心情不低於16、等級70的白鷹白船驅逐完成所以盡量保證有足夠多的驅逐。國服則為等級100的白鷹白船驅逐。\n\n換裝備只會更換正在裝備中的欄位即使是白裝也會更換。如果指定了旗艦則會更換全部5個裝備未指定旗艦隻會更換設備。",
@@ -1162,14 +1173,12 @@
"name": "GemsFarming.StopIFAutoNotEnsured.name",
"help": "GemsFarming.StopIFAutoNotEnsured.help"
},
"CommonCV": {
"name": "指定旗艦航母",
"CommonDD": {
"name": "指定前排",
"help": "",
"any": "任意",
"langley": "蘭利",
"bogue": "博格",
"ranger": "突擊者",
"hermes": "競技神"
"aulick_or_foote": "奧利克或富特",
"cassin_or_downes": "卡辛或唐斯"
},
"CommissionLimit": {
"name": "防止緊急委託數量過多",
@@ -2629,12 +2638,10 @@
"DeviceType": {
"name": "設備類型",
"help": "",
"emulator": "模擬器 安卓<=9",
"emulator_android_12": "模擬器 安卓>9",
"emulator": "模擬器",
"plone_cloud_with_adb": "雲手機 有公網ADB",
"phone_cloud_without_adb": "雲手機 無公網ADB",
"android_phone": "安卓真機 安卓<=9",
"android_phone_12": "安卓真機 安卓>9",
"android_phone": "安卓真機",
"android_phone_vmos": "安卓真機VMOS虛擬機"
},
"TestScene": {

View File

@@ -69,17 +69,19 @@ class Benchmark(DaemonBase, CampaignUI):
if not isinstance(cost, (float, int)):
return Text(cost, style="bold bright_red")
if cost < 0.10:
if cost < 0.025:
return Text('Insane Fast', style="bold bright_green")
if cost < 0.100:
return Text('Ultra Fast', style="bold bright_green")
if cost < 0.20:
if cost < 0.200:
return Text('Very Fast', style="bright_green")
if cost < 0.30:
if cost < 0.300:
return Text('Fast', style="green")
if cost < 0.50:
if cost < 0.500:
return Text('Medium', style="yellow")
if cost < 0.75:
if cost < 0.750:
return Text('Slow', style="red")
if cost < 1.00:
if cost < 1.000:
return Text('Very Slow', style="bright_red")
return Text('Ultra Slow', style="bold bright_red")
@@ -88,11 +90,11 @@ class Benchmark(DaemonBase, CampaignUI):
if not isinstance(cost, (float, int)):
return Text(cost, style="bold bright_red")
if cost < 0.1:
if cost < 0.100:
return Text('Fast', style="bright_green")
if cost < 0.2:
if cost < 0.200:
return Text('Medium', style="yellow")
if cost < 0.4:
if cost < 0.400:
return Text('Slow', style="red")
return Text('Very Slow', style="bright_red")
@@ -178,7 +180,9 @@ class Benchmark(DaemonBase, CampaignUI):
return [l for l in screenshot if l not in args]
# No ascreencap on Android > 9
if device in ['emulator_android_12', 'android_phone_12']:
sdk = self.device.sdk_ver
logger.info(f'sdk_ver: {sdk}')
if not (21 <= sdk <= 28):
screenshot = remove('aScreenCap', 'aScreenCap_nc')
# No nc loopback
if device in ['plone_cloud_with_adb']:
@@ -187,6 +191,8 @@ class Benchmark(DaemonBase, CampaignUI):
if device == 'android_phone_vmos':
screenshot = ['ADB', 'aScreenCap', 'DroidCast', 'DroidCast_raw']
click = ['ADB', 'Hermit', 'MaaTouch']
if self.device.nemu_ipc_available():
screenshot.append('nemu_ipc')
scene = self.config.Benchmark_TestScene
if 'screenshot' not in scene:
@@ -223,6 +229,8 @@ class Benchmark(DaemonBase, CampaignUI):
screenshot = remove('aScreenCap', 'aScreenCap_nc')
if self.device.is_chinac_phone_cloud:
screenshot = remove('ADB_nc', 'aScreenCap_nc')
if self.device.nemu_ipc_available():
screenshot.append('nemu_ipc')
screenshot = tuple(screenshot)
self.TEST_TOTAL = 3

View File

@@ -9,7 +9,7 @@ from module.logger import logger
class AppControl(Adb, WSA, Uiautomator2):
hierarchy: etree._Element
_app_u2_family = ['uiautomator2', 'minitouch', 'scrcpy', 'MaaTouch']
_app_u2_family = ['uiautomator2', 'minitouch', 'scrcpy', 'MaaTouch', 'nemu_ipc']
def app_is_running(self) -> bool:
method = self.config.Emulator_ControlMethod

View File

@@ -11,7 +11,7 @@ import uiautomator2 as u2
from adbutils import AdbClient, AdbDevice, AdbTimeout, ForwardItem, ReverseItem
from adbutils.errors import AdbError
from module.base.decorator import Config, cached_property, del_cached_property
from module.base.decorator import Config, cached_property, del_cached_property, run_once
from module.base.utils import ensure_time
from module.config.server import VALID_CHANNEL_PACKAGE, VALID_PACKAGE, set_server
from module.device.connection_attr import ConnectionAttr
@@ -266,15 +266,20 @@ class Connection(ConnectionAttr):
return True
return False
@cached_property
def nemud_app_keep_alive(self) -> str:
res = self.adb_getprop('nemud.app_keep_alive')
logger.attr('nemud.app_keep_alive', res)
return res
@retry
def check_mumu_app_keep_alive(self):
if not self.is_mumu_family:
return False
res = self.adb_getprop('nemud.app_keep_alive')
logger.attr('nemud.app_keep_alive', res)
res = self.nemud_app_keep_alive
if res == '':
# Empry property, might not be a mumu emulator or might be an old mumu
# Empty property, probably MuMu6 or MuMu12 version < 3.5.6
return True
elif res == 'false':
# Disabled
@@ -287,6 +292,15 @@ class Connection(ConnectionAttr):
logger.warning(f'Invalid nemud.app_keep_alive value: {res}')
return False
@cached_property
def is_mumu_over_version_356(self) -> bool:
"""
Returns:
bool: If MuMu12 version >= 3.5.6,
which has nemud.app_keep_alive and always be a vertical device
"""
return self.nemud_app_keep_alive != ''
@cached_property
def _nc_server_host_port(self):
"""
@@ -752,23 +766,42 @@ class Connection(ConnectionAttr):
If serial=='auto' and only 1 device detected, use it
"""
logger.hr('Detect device')
logger.info('Here are the available devices, '
'copy to Alas.Emulator.Serial to use it or set Alas.Emulator.Serial="auto"')
devices = self.list_device()
available = SelectedGrids([])
devices = SelectedGrids([])
# Show available devices
available = devices.select(status='device')
for device in available:
logger.info(device.serial)
if not len(available):
logger.info('No available devices')
@run_once
def brute_force_connect():
logger.info('Brute force connect')
from deploy.Windows.emulator import EmulatorManager
manager = EmulatorManager()
manager.brute_force_connect()
# Show unavailable devices if having any
unavailable = devices.delete(available)
if len(unavailable):
logger.info('Here are the devices detected but unavailable')
for device in unavailable:
logger.info(f'{device.serial} ({device.status})')
for _ in range(2):
logger.info('Here are the available devices, '
'copy to Alas.Emulator.Serial to use it or set Alas.Emulator.Serial="auto"')
devices = self.list_device()
# Show available devices
available = devices.select(status='device')
for device in available:
logger.info(device.serial)
if not len(available):
logger.info('No available devices')
# Show unavailable devices if having any
unavailable = devices.delete(available)
if len(unavailable):
logger.info('Here are the devices detected but unavailable')
for device in unavailable:
logger.info(f'{device.serial} ({device.status})')
# brute_force_connect
if self.config.Emulator_Serial == 'auto' and available.count == 0:
logger.warning(f'No available device found')
brute_force_connect()
continue
else:
break
# Auto device detection
if self.config.Emulator_Serial == 'auto':
@@ -822,6 +855,36 @@ class Connection(ConnectionAttr):
f'Using serial: {emu_serial}')
self.serial = emu_serial
# Redirect MuMu12 from 127.0.0.1:7555 to 127.0.0.1:16xxx
if self.serial == '127.0.0.1:7555':
for _ in range(2):
mumu12 = available.select(may_mumu12_family=True)
if mumu12.count == 1:
emu_serial = mumu12.first_or_none().serial
logger.warning(f'Redirect MuMu12 {self.serial} to {emu_serial}')
self.serial = emu_serial
self.config.Emulator_Serial = emu_serial
break
elif mumu12.count >= 2:
logger.warning(f'Multiple MuMu12 serial found, cannot redirect')
break
else:
# Only 127.0.0.1:7555
if self.is_mumu_over_version_356:
logger.warning(f'Device {self.serial} is MuMu12 but corresponding port not found')
brute_force_connect()
devices = self.list_device()
# Show available devices
available = devices.select(status='device')
for device in available:
logger.info(device.serial)
if not len(available):
logger.info('No available devices')
continue
else:
# MuMu6
break
@retry
def list_package(self, show_log=True):
"""

View File

@@ -146,7 +146,12 @@ class ConnectionAttr:
def is_mumu_family(self):
# 127.0.0.1:7555
# 127.0.0.1:16384 + 32*n
return self.serial == '127.0.0.1:7555' or self.serial.startswith('127.0.0.1:16')
return self.serial == '127.0.0.1:7555' or self.is_mumu12_family
@cached_property
def is_mumu12_family(self):
# 127.0.0.1:16384 + 32*n
return len(self.serial) == 15 and self.serial.startswith('127.0.0.1:16')
@cached_property
def is_emulator(self):

View File

@@ -5,11 +5,12 @@ from module.base.utils import *
from module.device.method.hermit import Hermit
from module.device.method.maatouch import MaaTouch
from module.device.method.minitouch import Minitouch
from module.device.method.nemu_ipc import NemuIpc
from module.device.method.scrcpy import Scrcpy
from module.logger import logger
class Control(Hermit, Minitouch, Scrcpy, MaaTouch):
class Control(Hermit, Minitouch, Scrcpy, MaaTouch, NemuIpc):
def handle_control_check(self, button):
# Will be overridden in Device
pass
@@ -22,6 +23,7 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch):
'minitouch': self.click_minitouch,
'Hermit': self.click_hermit,
'MaaTouch': self.click_maatouch,
'nemu_ipc': self.click_nemu_ipc,
}
def click(self, button, control_check=True):
@@ -78,6 +80,8 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch):
self.long_click_scrcpy(x, y, duration)
elif method == 'MaaTouch':
self.long_click_maatouch(x, y, duration)
elif method == 'nemu_ipc':
self.long_click_nemu_ipc(x, y, duration)
else:
self.swipe_adb((x, y), (x, y), duration)
@@ -86,13 +90,9 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch):
p1, p2 = ensure_int(p1, p2)
duration = ensure_time(duration)
method = self.config.Emulator_ControlMethod
if method == 'minitouch':
logger.info('Swipe %s -> %s' % (point2str(*p1), point2str(*p2)))
elif method == 'uiautomator2':
if method == 'uiautomator2':
logger.info('Swipe %s -> %s, %s' % (point2str(*p1), point2str(*p2), duration))
elif method == 'scrcpy':
logger.info('Swipe %s -> %s' % (point2str(*p1), point2str(*p2)))
elif method == 'MaaTouch':
elif method in ['minitouch', 'MaaTouch', 'scrcpy', 'nemu_ipc']:
logger.info('Swipe %s -> %s' % (point2str(*p1), point2str(*p2)))
else:
# ADB needs to be slow, or swipe doesn't work
@@ -114,6 +114,8 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch):
self.swipe_scrcpy(p1, p2)
elif method == 'MaaTouch':
self.swipe_maatouch(p1, p2)
elif method == 'nemu_ipc':
self.swipe_nemu_ipc(p1, p2)
else:
self.swipe_adb(p1, p2, duration=duration)
@@ -163,8 +165,10 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch):
self.drag_scrcpy(p1, p2, point_random=point_random)
elif method == 'MaaTouch':
self.drag_maatouch(p1, p2, point_random=point_random)
elif method == 'nemu_ipc':
self.drag_nemu_ipc(p1, p2, point_random=point_random)
else:
logger.warning(f'Control method {method} does not support drag well, '
f'falling back to ADB swipe may cause unexpected behaviour')
self.swipe_adb(p1, p2, duration=ensure_time(swipe_duration * 2))
self.click(Button(area=(), color=(), button=area_offset(point_random, p2), name=name ),False)
self.click(Button(area=(), color=(), button=area_offset(point_random, p2), name=name), False)

View File

@@ -1,5 +1,4 @@
import collections
import sys
from datetime import datetime
from module.base.timer import Timer
@@ -17,11 +16,6 @@ from module.exception import (
from module.handler.assets import GET_MISSION
from module.logger import logger
if sys.platform == 'win32':
from module.device.platform.platform_windows import PlatformWindows as Platform
else:
from module.device.platform.platform_base import PlatformBase as Platform
def show_function_call():
"""
@@ -63,7 +57,7 @@ def show_function_call():
logger.info('Function calls:' + ''.join(func_list))
class Device(Screenshot, Control, AppControl, Platform):
class Device(Screenshot, Control, AppControl):
_screen_size_checked = False
detect_record = set()
click_record = collections.deque(maxlen=15)
@@ -87,12 +81,13 @@ class Device(Screenshot, Control, AppControl, Platform):
)
raise
self.screenshot_interval_set()
# Auto-fill emulator info
if self.config.EmulatorInfo_Emulator == 'auto':
_ = self.emulator_instance
self.screenshot_interval_set()
self.method_check()
# Temp fix for MuMu 12 before DroidCast updated
if self.is_mumu_family:
logger.info('Patching screenshot method for mumu')
self.config.override(Emulator_ScreenshotMethod='ADB_nc')
# Auto-select the fastest screenshot method
if not self.config.is_template_config and self.config.Emulator_ScreenshotMethod == 'auto':
self.run_simple_screenshot_benchmark()
@@ -110,7 +105,22 @@ class Device(Screenshot, Control, AppControl, Platform):
bench = Benchmark(config=self.config, device=self)
method = bench.run_simple_screenshot_benchmark()
# Set
self.config.Emulator_ScreenshotMethod = method
with self.config.multi_set():
self.config.Emulator_ScreenshotMethod = method
if method == 'nemu_ipc':
self.config.Emulator_ControlMethod = 'nemu_ipc'
def method_check(self):
"""
Check combinations of screenshot method and control methods
"""
# nemu_ipc should be together
if self.config.Emulator_ScreenshotMethod == 'nemu_ipc' and self.config.Emulator_ControlMethod != 'nemu_ipc':
logger.warning('When using nemu_ipc, both screenshot and control should use nemu_ipc')
self.config.Emulator_ControlMethod = 'nemu_ipc'
if self.config.Emulator_ScreenshotMethod != 'nemu_ipc' and self.config.Emulator_ControlMethod == 'nemu_ipc':
logger.warning('When not using nemu_ipc, both screenshot and control should not use nemu_ipc')
self.config.Emulator_ControlMethod = 'minitouch'
def handle_night_commission(self, daily_trigger='21:00', threshold=30):
"""
@@ -161,6 +171,8 @@ class Device(Screenshot, Control, AppControl, Platform):
# stop it during wait
if self.config.Emulator_ScreenshotMethod == 'scrcpy':
self._scrcpy_server_stop()
if self.config.Emulator_ScreenshotMethod == 'nemu_ipc':
self.nemu_ipc_release()
def stuck_record_add(self, button):
self.detect_record.add(str(button))

View File

@@ -1,325 +0,0 @@
import os
import re
import winreg
import subprocess
from adbutils.errors import AdbError
from deploy.Windows.emulator import VirtualBoxEmulator
from module.base.decorator import cached_property
from module.device.connection import Connection
from module.device.method.utils import get_serial_pair
from module.exception import RequestHumanTakeover, EmulatorNotRunningError
from module.logger import logger
class EmulatorInstance(VirtualBoxEmulator):
def __init__(self, name, root_path, emu_path,
vbox_path=None, vbox_name=None, kill_para=None, multi_para=None):
"""
Args:
name (str): Emulator name in windows uninstall list.
root_path (str): Relative path from uninstall.exe to emulator installation folder.
emu_path (str): Relative path to executable simulator file.
vbox_path (str): Relative path to virtual box folder.
vbox_name (str): Regular Expression to match the name of .vbox file.
kill_para (str): Parameters required by kill emulator.
multi_para (str): Parameters required by start multi open emulator,
#id will be replaced with the real ID.
"""
super().__init__(
name=name,
root_path=root_path,
adb_path=None,
vbox_path=vbox_path,
vbox_name=vbox_name,
)
self.emu_path = emu_path
self.kill_para = kill_para
self.multi_para = multi_para
@cached_property
def id_and_serial(self):
"""
Returns:
list[str, str]: List of multi_id and serial.
"""
vbox = []
for path, folders, files in os.walk(os.path.join(self.root, self.vbox_path)):
for file in files:
if re.match(self.vbox_name, file):
file = os.path.join(path, file)
vbox.append(file)
serial = []
for file in vbox:
with open(file, 'r', encoding='utf-8', errors='ignore') as f:
for line in f.readlines():
# <Forwarding name="port2" proto="1" hostip="127.0.0.1" hostport="62026" guestport="5555"/>
res = re.search('<*?hostport="(.*?)".*?guestport="5555"/>', line)
if res:
serial.append([os.path.basename(file).split(".")[0], f'127.0.0.1:{res.group(1)}'])
return serial
class Bluestacks5Instance(EmulatorInstance):
@cached_property
def root(self):
try:
return super().root
except FileNotFoundError:
self.name = 'BlueStacks_nxt_cn'
return super().root
@cached_property
def id_and_serial(self):
try:
reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\BlueStacks_nxt")
except FileNotFoundError:
reg = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\BlueStacks_nxt_cn")
directory = winreg.QueryValueEx(reg, 'UserDefinedDir')[0]
with open(os.path.join(directory, 'bluestacks.conf'), encoding='utf-8') as f:
content = f.read()
emulators = re.findall(r'bst.instance.(\w+).status.adb_port="(\d+)"', content)
serial = []
for emulator in emulators:
serial.append([emulator[0], f'127.0.0.1:{emulator[1]}'])
return serial
class EmulatorManager(Connection):
pid = None
SUPPORTED_EMULATORS = {
'nox_player': EmulatorInstance(
name="Nox",
root_path=".",
emu_path="./Nox.exe",
vbox_path="./BignoxVMS",
vbox_name='.*.vbox$',
kill_para='-quit',
multi_para='-clone:#id',
),
'mumu_player': EmulatorInstance(
name="Nemu",
root_path=".",
emu_path="./EmulatorShell/NemuPlayer.exe",
vbox_path="./vms",
vbox_name='.*.nemu$',
),
'bluestacks_5': Bluestacks5Instance(
name='BlueStacks_nxt',
root_path='.',
emu_path='./HD-Player.exe',
multi_para='--instance #id',
),
}
def detect_emulator(self, serial, emulator=None):
"""
Args:
serial (str):
emulator (EmulatorInstance):
Returns:
list[EmulatorInstance, str]:Emulator and multi_id
"""
if emulator is None:
logger.info('Detect emulator from all emulators installed')
emulators = []
for emulator in self.SUPPORTED_EMULATORS.values():
try:
serials = emulator.id_and_serial
for cur_serial in serials:
if cur_serial[1] == serial:
emulators.append([emulator, cur_serial[0]])
except FileNotFoundError:
pass
logger.info('Detected emulators:')
for emulator in emulators:
logger.info(f'Name: {emulator[0].name}, Multi_id: {emulator[1]}')
if len(emulators) == 1 or \
(len(emulators) > 0 and emulators[0][0] == self.SUPPORTED_EMULATORS['mumu_player']):
logger.info('Find the only emulator, using it')
return emulators[0][0], emulators[0][1]
elif len(emulators) == 0:
logger.warning('The emulator corresponding to serial is not found, '
'please check the setting or use custom command')
else:
logger.warning('Multiple emulators with the same serial have been found, '
'please select one manually or use custom command')
raise RequestHumanTakeover
else:
try:
logger.info(f'Detect emulator from {emulator.name}')
serials = emulator.id_and_serial
for cur_serial in serials:
if cur_serial[1] == serial:
logger.info('Find the only emulator, using it')
return emulator, cur_serial[0]
except FileNotFoundError:
pass
logger.warning('The emulator corresponding to serial is not found, '
'please check the setting or use custom command')
raise RequestHumanTakeover
@staticmethod
def execute(command):
"""
Args:
command (str):
Returns:
subprocess.Popen:
"""
command = command.replace(r"\\", "/").replace("\\", "/").replace('"', '"')
logger.info(f'Execute: {command}')
return subprocess.Popen(command, close_fds=True) # only work on Windows
@staticmethod
def task_kill(pid=None, name=None):
"""
Args:
pid (list, int):
name (list, str):
Returns:
subprocess.Popen:
"""
command = 'taskkill '
if pid is not None:
if isinstance(pid, list):
for p in pid:
command += f'/pid {p} '
else:
command += f'/pid {pid} '
elif name is not None:
if isinstance(name, list):
for n in name:
command += f'/im {n} '
else:
command += f'/im {name} '
else:
raise RequestHumanTakeover
command += '/t /f'
return EmulatorManager.execute(command)
def adb_connect(self, serial):
try:
return super(EmulatorManager, self).adb_connect(serial)
except EmulatorNotRunningError:
raise RequestHumanTakeover
def detect_emulator_status(self, serial):
devices = self.list_device()
for device in devices:
if device.serial == serial:
return device.status
return 'offline'
def emulator_start(self, serial, emulator=None, multi_id=None, command=None):
"""
Args:
serial (str): Expected serial after simulator starts successfully.
emulator (EmulatorInstance): Emulator to start.
multi_id (str): Emulator ID used by multi open emulator.
command (str): Customized path and parameters of the simulator to start.
Return:
bool: If start successful.
"""
if command is None:
command = '\"' + os.path.abspath(os.path.join(emulator.root, emulator.emu_path)) + '\"'
if emulator.multi_para is not None and multi_id is not None:
command += " " + emulator.multi_para.replace("#id", multi_id)
logger.info('Start emulator')
pipe = self.execute(command)
self.pid = pipe.pid
self.sleep(10)
for _ in range(20):
if pipe.poll() is not None:
break
try:
if super().adb_connect(serial):
# Wait until emulator start completely
self.sleep(10)
return True
except EmulatorNotRunningError:
pass
self.sleep(5)
return False
def emulator_kill(self, serial, emulator=None, multi_id=None, command=None):
"""
Args:
serial (str): Expected serial after simulator starts successfully.
emulator (EmulatorInstance): Emulator to start.
multi_id (str): Emulator ID used by multi open emulator.
command (str): Customized path and parameters of the simulator to start.
Return:
bool: If kill successful.
"""
if command is None and emulator.kill_para is not None:
command = '\"' + os.path.abspath(os.path.join(emulator.root, emulator.emu_path)) + '\"'
if emulator.multi_para is not None and multi_id is not None:
command += " " + emulator.multi_para.replace("#id", multi_id)
command += " " + emulator.kill_para
logger.info('Kill emulator')
if emulator == self.SUPPORTED_EMULATORS['bluestacks_5']:
try:
self.adb_command(['reboot', '-p'], timeout=20)
if self.detect_emulator_status(serial) == 'offline':
self.pid = None
return True
except AdbError:
return False
if emulator == self.SUPPORTED_EMULATORS['mumu_player']:
self.task_kill(pid=None, name=['NemuHeadless.exe', 'NemuPlayer.exe', 'NemuSvc.exe'])
elif command is not None:
self.execute(command)
else:
self.task_kill(pid=self.pid, name=os.path.basename(emulator.emu_path))
self.sleep(5)
for _ in range(10):
if self.detect_emulator_status(serial) == 'offline':
self.pid = None
return True
self.sleep(2)
return False
def emulator_restart(self):
serial, _ = get_serial_pair(self.serial)
if serial is None:
serial = self.serial
if os.name != 'nt':
logger.warning('Restart simulator only works under Windows platform')
return False
logger.hr('Emulator restart')
if self.config.RestartEmulator_EmulatorType == 'auto':
emulator, multi_id = self.detect_emulator(serial)
else:
emulator = self.SUPPORTED_EMULATORS[self.config.RestartEmulator_EmulatorType]
emulator, multi_id = self.detect_emulator(serial, emulator=emulator)
for _ in range(3):
if not self.emulator_kill(serial, emulator, multi_id):
continue
if self.emulator_start(serial, emulator, multi_id):
return True
logger.warning('Restart emulator failed for 3 times, please check your settings')
raise RequestHumanTakeover

View File

@@ -146,7 +146,7 @@ class Adb(Connection):
if image is None:
raise ImageTruncated('Empty image after cv2.imdecode')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
cv2.cvtColor(image, cv2.COLOR_BGR2RGB, dst=image)
if image is None:
raise ImageTruncated('Empty image after cv2.cvtColor')

View File

@@ -165,11 +165,11 @@ class AScreenCap(Connection):
# ValueError: cannot reshape array of size 0 into shape (720,1280,4)
raise ImageTruncated(str(e))
image = cv2.flip(image, 0)
cv2.flip(image, 0, dst=image)
if image is None:
raise ImageTruncated('Empty image after cv2.flip')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
cv2.cvtColor(image, cv2.COLOR_BGR2RGB, dst=image)
if image is None:
raise ImageTruncated('Empty image after cv2.cvtColor')

Some files were not shown because too many files have changed in this diff Show More