Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opt: Add full winapi support. #3934

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 129 additions & 87 deletions alas.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __init__(self, config_name='alas'):
# Failure count of tasks
# Key: str, task name, value: int, failure count
self.failure_record = {}
self.is_first_check = True

@cached_property
def config(self):
Expand Down Expand Up @@ -62,11 +63,11 @@ def checker(self):
logger.exception(e)
exit(1)

def run(self, command, skip_first_screenshot=False):
def run(self, command, *args, skip_first_screenshot=False, **kwargs):
try:
if not skip_first_screenshot:
self.device.screenshot()
self.__getattribute__(command)()
getattr(self, command)(*args, **kwargs)
return True
except TaskEnd:
return True
Expand All @@ -90,47 +91,42 @@ def run(self, command, skip_first_screenshot=False):
self.config.task_call('Restart')
self.device.sleep(10)
return False
except GamePageUnknownError:
except GamePageUnknownError as e:
logger.info('Game server may be under maintenance or network may be broken, check server status now')
self.checker.check_now()
if self.checker.is_available():
logger.critical('Game page unknown')
self.save_error_log()
handle_notify(
self.config.Error_OnePushConfig,
title=f"Alas <{self.config_name}> crashed",
content=f"<{self.config_name}> GamePageUnknownError",
)
exit(1)
else:
self.checker.wait_until_available()
return False
self.crash_exit(e, save=True)
self.checker.wait_until_available()
return False
except ScriptError as e:
logger.exception(e)
logger.critical('This is likely to be a mistake of developers, but sometimes just random issues')
handle_notify(
self.config.Error_OnePushConfig,
title=f"Alas <{self.config_name}> crashed",
content=f"<{self.config_name}> ScriptError",
)
exit(1)
except RequestHumanTakeover:
logger.critical('Request human takeover')
handle_notify(
self.config.Error_OnePushConfig,
title=f"Alas <{self.config_name}> crashed",
content=f"<{self.config_name}> RequestHumanTakeover",
)
exit(1)
self.crash_exit(e, 'This is likely to be a mistake of developers, but sometimes just random issues',
log_exc=True)
except ALASBaseException as e:
if not self.device.emulator_check():
self.reboot()
return False
self.crash_exit(e)
except Exception as e:
if not self.device.emulator_check():
self.reboot()
return False
self.crash_exit(e, log_exc=True, save=True)

def crash_exit(self, e: Exception, *msgs, log_exc=False, save=False):
if log_exc:
logger.exception(e)
else:
logger.critical(e)
for msg in msgs:
logger.critical(msg)
if save:
self.save_error_log()
handle_notify(
self.config.Error_OnePushConfig,
title=f"Alas <{self.config_name}> crashed",
content=f"<{self.config_name}> Exception occured",
)
exit(1)
handle_notify(
self.config.Error_OnePushConfig,
title=f"Alas <{self.config_name}> crashed",
content=f"<{self.config_name}> {e.__class__.__name__}"
)
exit(1)

def save_error_log(self):
"""
Expand Down Expand Up @@ -162,7 +158,23 @@ def save_error_log(self):
with open(f'{folder}/log.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)

def restart(self):
def check(self):
from module.device.platform import Platform
p = Platform(self.config_name)
if not p.emulator_check():
p.emulator_start()
self.run('restart')

def reboot(self, use_log=True):
if use_log:
logger.warning('Emulator is not running')
self.device.emulator_stop()
self.device.emulator_start()
del_cached_property(self, 'config')

def restart(self, reboot=False):
if reboot:
self.reboot(use_log=False)
from module.handler.login import LoginHandler
LoginHandler(self.config, device=self.device).app_restart()

Expand Down Expand Up @@ -455,42 +467,76 @@ def get_next_task(self):
if self.config.task.command != 'Alas':
release_resources(next_task=task.command)

if task.next_run > datetime.now():
logger.info(f'Wait until {task.next_run} for task `{task.command}`')
self.is_first_task = False
method = self.config.Optimization_WhenTaskQueueEmpty
if method == 'close_game':
logger.info('Close game during wait')
self.device.app_stop()
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue
if task.command != 'Restart':
self.run('start')
elif method == 'goto_main':
logger.info('Goto main page during wait')
self.run('goto_main')
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue
elif method == 'stay_there':
logger.info('Stay there during wait')
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue
else:
logger.warning(f'Invalid Optimization_WhenTaskQueueEmpty: {method}, fallback to stay_there')
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue
if task.next_run <= datetime.now():
break

logger.info(f'Wait until {task.next_run} for task `{task.command}`')
self.is_first_task = False

method: str = self.config.Optimization_WhenTaskQueueEmpty
remainingtime: float = (task.next_run - datetime.now()).total_seconds() / 60
buffertime: int = self.config.Optimization_ProcessBufferTime
if (
method == 'stop_emulator' and
self.device.emulator_check() and
remainingtime <= buffertime
):
method = 'close_game'
logger.info(
f"The time to next task `{task.command}` is {remainingtime:.2f} minutes, "
f"less than {buffertime} minutes, fallback to \"{method}\""
)

if method == 'close_game':
logger.info('Close game during wait')
self.device.app_stop()
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue
if task.command != 'Restart':
self.run('start')
elif method == 'goto_main':
logger.info('Goto main page during wait')
self.run('goto_main')
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue
elif method == 'stay_there':
logger.info('Stay there during wait')
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue
elif method == 'stop_emulator':
logger.info('Stop emulator during wait')
self.device.emulator_stop()
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
method: str = self.config.Optimization_WhenTaskQueueEmpty
if (
not self.device.emulator_check() and
method != 'stop_emulator'
):
self.run('reboot', skip_first_screenshot=True, use_log=False)
continue
if not self.device.emulator_check():
self.run('reboot', skip_first_screenshot=True, use_log=False)
self.run('start', skip_first_screenshot=True)
else:
logger.warning(f'Invalid Optimization_WhenTaskQueueEmpty: {method}, fallback to stay_there')
release_resources()
self.device.release_during_wait()
if not self.wait_until(task.next_run):
del_cached_property(self, 'config')
continue

break

AzurLaneConfig.is_hoarding_task = False
Expand All @@ -502,11 +548,10 @@ def loop(self):

while 1:
# Check update event from GUI
if self.stop_event is not None:
if self.stop_event.is_set():
logger.info("Update event detected")
logger.info(f"Alas [{self.config_name}] exited.")
break
if self.stop_event is not None and self.stop_event.is_set():
logger.info("Update event detected")
logger.info(f"Alas [{self.config_name}] exited.")
break
# Check game server maintenance
self.checker.wait_until_available()
if self.checker.is_recovered():
Expand All @@ -517,10 +562,13 @@ def loop(self):
del_cached_property(self, 'config')
logger.info('Server or network is recovered. Restart game client')
self.config.task_call('Restart')
# Get task
task = self.get_next_task()
if self.is_first_check:
self.check()
self.is_first_check = False
# Init device and change server
_ = self.device
# Get task
task = self.get_next_task()
self.device.config = self.config
# Skip first restart
if self.is_first_task and task == 'Restart':
Expand Down Expand Up @@ -548,13 +596,7 @@ def loop(self):
"Please read the help text of the options.")
logger.critical("Possible reason #2: There is a problem with this task. "
"Please contact developers or try to fix it yourself.")
logger.critical('Request human takeover')
handle_notify(
self.config.Error_OnePushConfig,
title=f"Alas <{self.config_name}> crashed",
content=f"<{self.config_name}> RequestHumanTakeover\nTask `{task}` failed 3 or more times.",
)
exit(1)
self.crash_exit(RequestHumanTakeover(f"Task `{task}` failed 3 or more times."))

if success:
del_cached_property(self, 'config')
Expand Down
4 changes: 2 additions & 2 deletions campaign/campaign_main/campaign_15_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ def map_data_init(self, map_):

map_has_mob_move = True

def strategy_set_execute(self, formation_index=None, sub_view=None, sub_hunt=None):
def strategy_set_execute(self, formation=None, sub_view=None, sub_hunt=None):
super().strategy_set_execute(
formation_index=formation_index,
formation=formation,
sub_view=sub_view,
sub_hunt=sub_hunt,
)
Expand Down
4 changes: 2 additions & 2 deletions campaign/campaign_war_archives/campaign_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from module.war_archives.dictionary import dic_archives_template

WAR_ARCHIVES_SWITCH = Switch('War_Archives_switch', is_selector=True)
WAR_ARCHIVES_SWITCH.add_status('ex', WAR_ARCHIVES_EX_ON)
WAR_ARCHIVES_SWITCH.add_status('sp', WAR_ARCHIVES_SP_ON)
WAR_ARCHIVES_SWITCH.add_state('ex', WAR_ARCHIVES_EX_ON)
WAR_ARCHIVES_SWITCH.add_state('sp', WAR_ARCHIVES_SP_ON)
WAR_ARCHIVES_SCROLL = Scroll(WAR_ARCHIVES_SCROLL, color=(247, 211, 66), name='WAR_ARCHIVES_SCROLL')


Expand Down
4 changes: 2 additions & 2 deletions campaign/event_20221124_cn/campaign_base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from module.campaign.campaign_base import CampaignBase as CampaignBase_
from module.combat.assets import GET_ITEMS_1_RYZA
from module.handler.fast_forward import auto_search
from module.handler.fast_forward import AUTO_SEARCH
from module.handler.assets import MYSTERY_ITEM
from module.logger import logger
from module.map.map_grids import SelectedGrids
Expand Down Expand Up @@ -62,7 +62,7 @@ def map_get_info(self):

# Chapter TH has no map_percentage and no 3_stars
if name.startswith('th') or name.startswith('ht'):
appear = auto_search.appear(main=self)
appear = AUTO_SEARCH.appear(main=self)
self.map_is_100_percent_clear = self.map_is_3_stars = self.map_is_threat_safe = appear
self.map_has_clear_mode = appear
self.map_show_info()
Expand Down
4 changes: 2 additions & 2 deletions campaign/event_20240725_cn/campaign_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from module.logger import logger

MODE_SWITCH_20240725 = ModeSwitch('Mode_switch_20240725', offset=(30, 30))
MODE_SWITCH_20240725.add_status('combat', SWITCH_20240725_COMBAT)
MODE_SWITCH_20240725.add_status('story', SWITCH_20240725_STORY)
MODE_SWITCH_20240725.add_state('combat', SWITCH_20240725_COMBAT)
MODE_SWITCH_20240725.add_state('story', SWITCH_20240725_STORY)


class CampaignBase(CampaignBase_):
Expand Down
4 changes: 2 additions & 2 deletions campaign/event_20240829_cn/campaign_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from module.logger import logger

MODE_SWITCH_20240725 = ModeSwitch('Mode_switch_20240725', offset=(30, 30))
MODE_SWITCH_20240725.add_status('combat', SWITCH_20240725_COMBAT)
MODE_SWITCH_20240725.add_status('story', SWITCH_20240725_STORY)
MODE_SWITCH_20240725.add_state('combat', SWITCH_20240725_COMBAT)
MODE_SWITCH_20240725.add_state('story', SWITCH_20240725_STORY)


class CampaignBase(CampaignBase_):
Expand Down
4 changes: 2 additions & 2 deletions campaign/event_20240912_cn/campaign_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@


MODE_SWITCH_20240912 = ModeSwitch('Mode_switch_20240912', is_selector=True, offset=(30, 30))
MODE_SWITCH_20240912.add_status('combat', SWITCH_20240725_COMBAT, offset=(444, 4))
MODE_SWITCH_20240912.add_status('story', SWITCH_20240725_STORY, offset=(444, 4))
MODE_SWITCH_20240912.add_state('combat', SWITCH_20240725_COMBAT, offset=(444, 4))
MODE_SWITCH_20240912.add_state('story', SWITCH_20240725_STORY, offset=(444, 4))


class CampaignBase(CampaignBase_):
Expand Down
12 changes: 6 additions & 6 deletions campaign/event_20241024_cn/campaign_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
from module.template.assets import TEMPLATE_ENEMY_BOSS

MODE_SWITCH_20240725 = ModeSwitch('Mode_switch_20240725', is_selector=True, offset=(30, 30))
MODE_SWITCH_20240725.add_status('combat', SWITCH_20240725_COMBAT, offset=(444, 4))
MODE_SWITCH_20240725.add_status('story', SWITCH_20240725_STORY, offset=(444, 4))
MODE_SWITCH_20240725.add_state('combat', SWITCH_20240725_COMBAT, offset=(444, 4))
MODE_SWITCH_20240725.add_state('story', SWITCH_20240725_STORY, offset=(444, 4))

CHAPTER_SWITCH_20241024 = ModeSwitch('Chapter_switch_20241024', is_selector=True, offset=(30, 30))
CHAPTER_SWITCH_20241024.add_status('ab', CHAPTER_20241024_AB)
CHAPTER_SWITCH_20241024.add_status('cd', CHAPTER_20241024_CD)
CHAPTER_SWITCH_20241024.add_status('sp', CHAPTER_20241024_SP)
CHAPTER_SWITCH_20241024.add_status('ex', CHAPTER_20241024_EX)
CHAPTER_SWITCH_20241024.add_state('ab', CHAPTER_20241024_AB)
CHAPTER_SWITCH_20241024.add_state('cd', CHAPTER_20241024_CD)
CHAPTER_SWITCH_20241024.add_state('sp', CHAPTER_20241024_SP)
CHAPTER_SWITCH_20241024.add_state('ex', CHAPTER_20241024_EX)


class EventGrid(Grid):
Expand Down
6 changes: 4 additions & 2 deletions config/template.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"ScreenshotMethod": "auto",
"ControlMethod": "MaaTouch",
"ScreenshotDedithering": false,
"AdbRestart": false
"AdbRestart": false,
"SilentStart": "normal"
},
"EmulatorInfo": {
"Emulator": "auto",
Expand All @@ -24,7 +25,8 @@
"ScreenshotInterval": 0.3,
"CombatScreenshotInterval": 1.0,
"TaskHoardingDuration": 0,
"WhenTaskQueueEmpty": "goto_main"
"WhenTaskQueueEmpty": "goto_main",
"ProcessBufferTime": 10
},
"DropRecord": {
"SaveFolder": "./screenshots",
Expand Down
Loading