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:
201
alas.py
201
alas.py
@@ -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__':
|
||||
|
||||
@@ -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": {}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -21,6 +21,8 @@ Alas:
|
||||
- OldRetire
|
||||
Restart:
|
||||
- Scheduler
|
||||
- GameRestart
|
||||
- InstanceRestart
|
||||
|
||||
# ==================== Cheat ====================
|
||||
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "任务状态",
|
||||
|
||||
@@ -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
32
module/counter.py
Normal 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
|
||||
Reference in New Issue
Block a user