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

add: base framework of game restart and instance restart

This commit is contained in:
0O0o0oOoO00
2025-09-02 00:48:40 +08:00
parent f2137c699a
commit 160b6bdb1f
12 changed files with 441 additions and 5 deletions

201
alas.py
View File

@@ -587,6 +587,27 @@ class AzurLaneAutoScript:
from module.luahook.crack import *
from module.counter import *
class FailedTaskCounterManager:
def __init__(self, config: AzurLaneConfig):
self.task_counter = {}
if config.full_config.Restart_GameRestart_Enable:
self.counter_max_count = config.full_config.Restart_GameRestart_MaxRetryTimes
self.counter_class = MaxCounter
else:
self.counter_max_count = 0
self.counter_class = Counter
def count(self, task, throw=True) -> bool:
counter = self.task_counter.get(task, self.counter_class(self.counter_max_count))
return counter.count_once(throw=throw)
def reset(self, task):
counter = self.task_counter.get(task, self.counter_class(self.counter_max_count))
counter.reset()
class AzurLaneAutoScript(AzurLaneAutoScript):
@@ -602,6 +623,8 @@ class AzurLaneAutoScript(AzurLaneAutoScript):
elif self.class_name == "ArknightsAutoScript":
self.is_ark = True
self.failed_task_counter = FailedTaskCounterManager(self.config)
def pre_init(self):
from PIL import Image
Image.init()
@@ -616,10 +639,113 @@ class AzurLaneAutoScript(AzurLaneAutoScript):
Image.register_mime(PngImagePlugin.PngImageFile.format, "image/png")
def handle_TaskEnd(self, e) -> bool:
return True
def handle_GameNotRunningError(self, e) -> bool:
logger.warning(e)
self.config.task_call('Restart')
return False
def handle_GameStuckError(self, e) -> bool:
logger.error(e)
self.save_error_log()
logger.warning(f'Game stuck, {self.device.package} will be restarted in 10 seconds')
logger.warning('If you are playing by hand, please stop Alas')
self.config.task_call('Restart')
self.device.sleep(10)
return False
def handle_GameTooManyClickError(self, e) -> bool:
logger.error(e)
self.save_error_log()
logger.warning(f'Game stuck, {self.device.package} will be restarted in 10 seconds')
logger.warning('If you are playing by hand, please stop Alas')
self.config.task_call('Restart')
self.device.sleep(10)
return False
def handle_GameBugError(self, e) -> bool:
logger.warning(e)
self.save_error_log()
logger.warning('An error has occurred in Azur Lane game client, Alas is unable to handle')
logger.warning(f'Restarting {self.device.package} to fix it')
self.config.task_call('Restart')
self.device.sleep(10)
return False
def handle_GamePageUnknownError(self, e) -> bool:
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
def handle_ScriptError(self, e) -> bool:
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)
def handle_RequestHumanTakeover(self, e) -> bool:
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)
def handle_Exception(self, e) -> bool:
logger.exception(e)
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)
def run(self, command, skip_first_screenshot=False):
if self.is_azur:
luahook_crack_all(self.config, self.device)
super().run(command, skip_first_screenshot=skip_first_screenshot)
try:
if self.is_azur:
luahook_disable_all(self.config, self.device)
luahook_crack_all(self.config, self.device)
if not skip_first_screenshot:
self.device.screenshot()
self.__getattribute__(command)()
return True
except TaskEnd as e:
return self.handle_TaskEnd(e)
except GameNotRunningError as e:
return self.handle_GameNotRunningError(e)
except GameStuckError as e:
return self.handle_GameStuckError(e)
except GameTooManyClickError as e:
return self.handle_GameTooManyClickError(e)
except GameBugError as e:
return self.handle_GameBugError(e)
except GamePageUnknownError as e:
return self.handle_GamePageUnknownError(e)
except ScriptError as e:
return self.handle_ScriptError(e)
except RequestHumanTakeover as e:
return self.handle_RequestHumanTakeover(e)
except Exception as e:
return self.handle_Exception(e)
def main4(self):
from module.campaign.run import CampaignRun
@@ -695,7 +821,74 @@ class AzurLaneAutoScript(AzurLaneAutoScript):
def loop(self):
if self.is_azur:
CrackResource(self.config, self.device).ensure()
super().loop()
logger.set_file_logger(self.config_name)
logger.info(f'Start scheduler loop: {self.config_name}')
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
# Check game server maintenance
self.checker.wait_until_available()
if self.checker.is_recovered():
# There is an accidental bug hard to reproduce
# Sometimes, config won't be updated due to blocking
# even though it has been changed
# So update it once recovered
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()
# Init device and change server
_ = self.device
self.device.config = self.config
# Skip first restart
if self.is_first_task and task == 'Restart':
logger.info('Skip task `Restart` at scheduler start')
self.config.task_delay(server_update=True)
del_cached_property(self, 'config')
continue
# Run
logger.info(f'Scheduler: Start task `{task}`')
self.device.stuck_record_clear()
self.device.click_record_clear()
logger.hr(task, level=0)
success = self.run(inflection.underscore(task))
logger.info(f'Scheduler: End task `{task}`')
self.is_first_task = False
if not success:
if not self.failed_task_counter.count(task, throw=False):
logger.critical(f"Task `{task}` failed 3 or more times.")
logger.critical("Possible reason #1: You haven't used it correctly. "
"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)
if success:
self.failed_task_counter.reset(task)
del_cached_property(self, 'config')
continue
elif self.config.Error_HandleError:
# self.config.task_delay(success=False)
del_cached_property(self, 'config')
self.checker.check_now()
continue
else:
break
if __name__ == '__main__':

View File

@@ -73,6 +73,16 @@
"FailureInterval": 0,
"ServerUpdate": "00:00"
},
"GameRestart": {
"Enable": false,
"MaxRetryTimes": 5,
"Notify": false
},
"InstanceRestart": {
"Enable": false,
"MaxRetryTimes": 5,
"Notify": false
},
"Storage": {
"Storage": {}
}

View File

@@ -413,6 +413,34 @@
"display": "hide"
}
},
"GameRestart": {
"Enable": {
"type": "checkbox",
"value": false
},
"MaxRetryTimes": {
"type": "input",
"value": 5
},
"Notify": {
"type": "checkbox",
"value": false
}
},
"InstanceRestart": {
"Enable": {
"type": "checkbox",
"value": false
},
"MaxRetryTimes": {
"type": "input",
"value": 5
},
"Notify": {
"type": "checkbox",
"value": false
}
},
"Storage": {
"Storage": {
"type": "storage",

View File

@@ -829,4 +829,15 @@ Misc:
ExerciseMorePower: -1.0
FastWave: false
MonsterKillSelf: false
SkipBattleCelebrate: false
SkipBattleCelebrate: false
# ==================== Cheat ====================
GameRestart:
Enable: false
MaxRetryTimes: 5
Notify: false
InstanceRestart:
Enable: false
MaxRetryTimes: 5
Notify: false

View File

@@ -21,6 +21,8 @@ Alas:
- OldRetire
Restart:
- Scheduler
- GameRestart
- InstanceRestart
# ==================== Cheat ====================

View File

@@ -509,5 +509,15 @@ class GeneratedConfig:
Misc_MonsterKillSelf = False
Misc_SkipBattleCelebrate = False
# Group `GameRestart`
GameRestart_Enable = False
GameRestart_MaxRetryTimes = 5
GameRestart_Notify = False
# Group `InstanceRestart`
InstanceRestart_Enable = False
InstanceRestart_MaxRetryTimes = 5
InstanceRestart_Notify = False
# Group `Storage`
Storage_Storage = {}

View File

@@ -57,6 +57,12 @@ class FullGeneratedConfig:
Restart_Scheduler_SuccessInterval = None
Restart_Scheduler_FailureInterval = None
Restart_Scheduler_ServerUpdate = None
Restart_GameRestart_Enable = None
Restart_GameRestart_MaxRetryTimes = None
Restart_GameRestart_Notify = None
Restart_InstanceRestart_Enable = None
Restart_InstanceRestart_MaxRetryTimes = None
Restart_InstanceRestart_Notify = None
Restart_Storage_Storage = None
# Task `Hook`

View File

@@ -2867,6 +2867,42 @@
"help": "Misc.SkipBattleCelebrate.help"
}
},
"GameRestart": {
"_info": {
"name": "GameRestart._info.name",
"help": "GameRestart._info.help"
},
"Enable": {
"name": "GameRestart.Enable.name",
"help": "GameRestart.Enable.help"
},
"MaxRetryTimes": {
"name": "GameRestart.MaxRetryTimes.name",
"help": "GameRestart.MaxRetryTimes.help"
},
"Notify": {
"name": "GameRestart.Notify.name",
"help": "GameRestart.Notify.help"
}
},
"InstanceRestart": {
"_info": {
"name": "InstanceRestart._info.name",
"help": "InstanceRestart._info.help"
},
"Enable": {
"name": "InstanceRestart.Enable.name",
"help": "InstanceRestart.Enable.help"
},
"MaxRetryTimes": {
"name": "InstanceRestart.MaxRetryTimes.name",
"help": "InstanceRestart.MaxRetryTimes.help"
},
"Notify": {
"name": "InstanceRestart.Notify.name",
"help": "InstanceRestart.Notify.help"
}
},
"Storage": {
"_info": {
"name": "Task status",

View File

@@ -2867,6 +2867,42 @@
"help": "Misc.SkipBattleCelebrate.help"
}
},
"GameRestart": {
"_info": {
"name": "GameRestart._info.name",
"help": "GameRestart._info.help"
},
"Enable": {
"name": "GameRestart.Enable.name",
"help": "GameRestart.Enable.help"
},
"MaxRetryTimes": {
"name": "GameRestart.MaxRetryTimes.name",
"help": "GameRestart.MaxRetryTimes.help"
},
"Notify": {
"name": "GameRestart.Notify.name",
"help": "GameRestart.Notify.help"
}
},
"InstanceRestart": {
"_info": {
"name": "InstanceRestart._info.name",
"help": "InstanceRestart._info.help"
},
"Enable": {
"name": "InstanceRestart.Enable.name",
"help": "InstanceRestart.Enable.help"
},
"MaxRetryTimes": {
"name": "InstanceRestart.MaxRetryTimes.name",
"help": "InstanceRestart.MaxRetryTimes.help"
},
"Notify": {
"name": "InstanceRestart.Notify.name",
"help": "InstanceRestart.Notify.help"
}
},
"Storage": {
"_info": {
"name": "Storage._info.name",

View File

@@ -2867,6 +2867,42 @@
"help": ""
}
},
"GameRestart": {
"_info": {
"name": "游戏重启",
"help": "该功能的启用和关闭需要重启这个Alas实例"
},
"Enable": {
"name": "启用",
"help": ""
},
"MaxRetryTimes": {
"name": "最大尝试重启次数",
"help": ""
},
"Notify": {
"name": "重启时通知",
"help": "不建议开启,防止消息轰炸"
}
},
"InstanceRestart": {
"_info": {
"name": "实例重启",
"help": ""
},
"Enable": {
"name": "启用",
"help": ""
},
"MaxRetryTimes": {
"name": "最大尝试重启次数",
"help": ""
},
"Notify": {
"name": "重启时通知",
"help": "谨慎开启,小心消息轰炸"
}
},
"Storage": {
"_info": {
"name": "任务状态",

View File

@@ -2867,6 +2867,42 @@
"help": "Misc.SkipBattleCelebrate.help"
}
},
"GameRestart": {
"_info": {
"name": "GameRestart._info.name",
"help": "GameRestart._info.help"
},
"Enable": {
"name": "GameRestart.Enable.name",
"help": "GameRestart.Enable.help"
},
"MaxRetryTimes": {
"name": "GameRestart.MaxRetryTimes.name",
"help": "GameRestart.MaxRetryTimes.help"
},
"Notify": {
"name": "GameRestart.Notify.name",
"help": "GameRestart.Notify.help"
}
},
"InstanceRestart": {
"_info": {
"name": "InstanceRestart._info.name",
"help": "InstanceRestart._info.help"
},
"Enable": {
"name": "InstanceRestart.Enable.name",
"help": "InstanceRestart.Enable.help"
},
"MaxRetryTimes": {
"name": "InstanceRestart.MaxRetryTimes.name",
"help": "InstanceRestart.MaxRetryTimes.help"
},
"Notify": {
"name": "InstanceRestart.Notify.name",
"help": "InstanceRestart.Notify.help"
}
},
"Storage": {
"_info": {
"name": "任務狀態",

32
module/counter.py Normal file
View File

@@ -0,0 +1,32 @@
class CounterReachMaxCountException(Exception):
...
class MaxCounter:
def __init__(self, max_count):
self.max_count = max_count
self.current_count = 0
def count_once(self, throw=True) -> bool:
self.current_count += 1
if self.current_count >= self.max_count:
if throw:
raise CounterReachMaxCountException()
else:
return False
return True
def reset(self):
self.current_count = 0
class Counter:
def __init__(self, start=0):
self.current_count = start
def count_once(self, throw=True) -> bool:
self.current_count += 1
return True
def reset(self):
self.current_count = 0