From bc5f9e4e4450d4aa64f75fa4649a2439d0ba100b Mon Sep 17 00:00:00 2001 From: LA_DI_DA <11174151+0O0o0oOoO00@users.noreply.github.com> Date: Tue, 18 Mar 2025 22:37:03 +0800 Subject: [PATCH] add: py api for luahook --- module/exception.py | 3 + module/luahook/api.py | 114 +++++++++++++++++++++++++ module/luahook/crack.py | 184 ++++++++++++++++++++++++++++++++++++++++ module/luahook/op.py | 65 ++++++++++++++ 4 files changed, 366 insertions(+) create mode 100644 module/luahook/api.py create mode 100644 module/luahook/crack.py create mode 100644 module/luahook/op.py diff --git a/module/exception.py b/module/exception.py index 81e057e3e..923085cbe 100644 --- a/module/exception.py +++ b/module/exception.py @@ -65,3 +65,6 @@ class AutoSearchSetError(Exception): class RequireRestartGame(Exception): pass + +class CrackerError(Exception): + pass \ No newline at end of file diff --git a/module/luahook/api.py b/module/luahook/api.py new file mode 100644 index 000000000..ae4d46616 --- /dev/null +++ b/module/luahook/api.py @@ -0,0 +1,114 @@ +import requests +import adbutils +from pydantic import BaseModel + +from module.exception import CrackerError + + +class CrackApi: + class ShipProperties(BaseModel): + armor: float = -1 + speed: float = -1 + antiaircraft: float = -1 + oxy_recovery_bench: float = -1 + torpedo: float = -1 + hit: float = -1 + sonarRange: float = -1 + attack_duration: float = -1 + raid_distance: float = -1 + oxy_recovery_surface: float = -1 + oxy_recovery: float = -1 + dodge: float = -1 + luck: float = -1 + reload: float = -1 + oxy_cost: float = -1 + durability: float = -1 + air: float = -1 + oxy_max: float = -1 + cannon: float = -1 + antisub: float = -1 + + class FakePlayerInfo(BaseModel): + name: str = "" + level: str = "" + id: str = "" + + def __init__(self, base_url): + self.api_url = base_url + + def post(self, path, data=None): + url = f'{self.api_url}/{path}' + try: + response = requests.post(url, data=data) # TODO: add timeout + except requests.exceptions.Timeout: + raise CrackerError('CrackApi request timeout') + except Exception as e: + raise CrackerError(f'CrackApi request error: {e}') + if response.status_code != 200: + raise CrackerError(f'CrackApi response error: {response.status_code}') + + def disable_all(self): + self.post('disable_all') + + def enable_global_ship_properties_crack(self): + self.post('enable_global_ship_properties_crack') + + def disable_global_ship_properties_crack(self): + self.post('disable_global_ship_properties_crack') + + def update_global_ship_properties(self, ship_properties: ShipProperties): + self.post('update_global_ship_properties', data=ship_properties.json()) + + def enable_fast_stage_move(self): + self.post("enable_fast_stage_move") + + def disable_fast_stage_move(self): + self.post("disable_fast_stage_move") + + def enable_remove_hard_mode_ship_properties_limit(self): + self.post("enable_remove_hard_mode_ship_properties_limit") + + def disable_remove_hard_mode_ship_properties_limit(self): + self.post("disable_remove_hard_mode_ship_properties_limit") + + def enable_remove_hard_mode_ship_type_limit(self): + self.post("enable_remove_hard_mode_ship_type_limit") + + def disable_remove_hard_mode_ship_type_limit(self): + self.post("disable_remove_hard_mode_ship_type_limit") + + def enable_remove_hard_mode_limit(self): + self.post("enable_remove_hard_mode_limit") + + def disable_remove_hard_mode_limit(self): + self.post("disable_remove_hard_mode_limit") + + def enable_fake_player(self): + self.post("enable_fake_player") + + def disable_fake_player(self): + self.post("disable_fake_player") + + def update_fake_player_info(self, fake_player_info: FakePlayerInfo): + self.post("update_fake_player_info", data=fake_player_info.json()) + + def enable_no_bb_animation(self): + self.post("enable_no_bb_animation") + + def disable_no_bb_animation(self): + self.post("disable_no_bb_animation") + + def enable_no_emotion_warning(self): + self.post("enable_no_emotion_warning") + + def disable_no_emotion_warning(self): + self.post("disable_no_emotion_warning") + + def enable_opsi_fast_move(self): + self.post("enable_opsi_fast_move") + + def disable_opsi_fast_move(self): + self.post("disable_opsi_fast_move") + + def is_alive(self): + self.post("is_alive") diff --git a/module/luahook/crack.py b/module/luahook/crack.py new file mode 100644 index 000000000..a6314ed85 --- /dev/null +++ b/module/luahook/crack.py @@ -0,0 +1,184 @@ +from typing import Union, List, Type + +from module.config.config import AzurLaneConfig +from module.config.utils import deep_get +from module.device.device import Device + +from module.luahook.api import CrackApi +from module.luahook.op import CrackOp + +ALL_ENABLE_OPS = [ + CrackOp.EnableGlobalShipProperties, + CrackOp.EnableChapterFastMove, + CrackOp.EnableRemoveHardModeLimit, + CrackOp.EnableFakePlayer, + CrackOp.EnableNoBBAnimation, + CrackOp.EnableNoEmotionWarning, + CrackOp.EnableOpsiFastMove, +] + +def do_crack_op(config: AzurLaneConfig, device: Device, ops: Union[Type[CrackOp.Op], List[Type[CrackOp.Op]]]): + if deep_get(config.data, "Hook.HookGeneral.Enable", False): + return + if isinstance(ops, list): + l = ops + else: + if ops == CrackOp.EnableAll: + l = ALL_ENABLE_OPS + else: + l = [ops] + + base_url = f"http://127.0.0.1:{device.adb.forward_port(7540)}" + api = CrackApi(base_url) + for op in l: + if op == CrackOp.DisableAll: + api.disable_all() + elif op == CrackOp.EnableGlobalShipProperties: + if not deep_get(config.data, "Hook.ShipProperty.Enable", False): + continue + api.update_global_ship_properties( + CrackApi.ShipProperties( + armor=float(deep_get("Hook.ShipProperty.Armor", -1)), + speed=float(deep_get("Hook.ShipProperty.Speed", -1)), + antiaircraft=float(deep_get("Hook.ShipProperty.AntiAircraft", -1)), + oxy_recovery_bench=float(deep_get("Hook.ShipProperty.OxyRecoveryBench", -1)), + torpedo=float(deep_get("Hook.ShipProperty.Torpedo", -1)), + hit=float(deep_get("Hook.ShipProperty.Hit", -1)), + sonarRange=float(deep_get("Hook.ShipProperty.SonarRange", -1)), + attack_duration=float(deep_get("Hook.ShipProperty.AttackDuration", -1)), + raid_distance=float(deep_get("Hook.ShipProperty.RaidDistance", -1)), + oxy_recovery_surface=float(deep_get("Hook.ShipProperty.OxyRecoverySurface", -1)), + oxy_recovery=float(deep_get("Hook.ShipProperty.OxyRecovery", -1)), + dodge=float(deep_get("Hook.ShipProperty.Dodge", -1)), + luck=float(deep_get("Hook.ShipProperty.Luck", -1)), + reload=float(deep_get("Hook.ShipProperty.Reload", -1)), + oxy_cost=float(deep_get("Hook.ShipProperty.OxyCost", -1)), + durability=float(deep_get("Hook.ShipProperty.Durability", -1)), + air=float(deep_get("Hook.ShipProperty.Air", -1)), + oxy_max=float(deep_get("Hook.ShipProperty.OxyMax", -1)), + cannon=float(deep_get("Hook.ShipProperty.Cannon", -1)), + antisub=float(deep_get("Hook.ShipProperty.AntiSub", -1)), + ) + ) + api.enable_global_ship_properties_crack() + elif op == CrackOp.DisableGlobalShipProperties: + api.disable_global_ship_properties_crack() + elif op == CrackOp.EnableChapterFastMove: + if not deep_get(config.data, "Hook.Misc.ChapterMove", False): + continue + api.enable_fast_stage_move() + elif op == CrackOp.DisableChapterFastMove: + api.disable_fast_stage_move() + elif op == CrackOp.EnableRemoveHardModeShipPropertiesLimit: + if deep_get(config.data, "Hook.Misc.RemoveHardMapLimit", "") != "remove_ship_properties_limit": + continue + api.enable_remove_hard_mode_ship_properties_limit() + elif op == CrackOp.DisableRemoveHardModeShipPropertiesLimit: + api.disable_remove_hard_mode_ship_properties_limit() + elif op == CrackOp.EnableRemoveHardModeShipTypeLimit: + if deep_get(config.data, "Hook.Misc.RemoveHardMapLimit", "") != "remove_ship_type_limit": + continue + api.enable_remove_hard_mode_ship_type_limit() + elif op == CrackOp.DisableRemoveHardModeShipTypeLimit: + api.disable_remove_hard_mode_ship_type_limit() + elif op == CrackOp.EnableRemoveHardModeLimit: + if deep_get(config.data, "Hook.Misc.RemoveHardMapLimit", "") != "remove_all_limit": + continue + api.enable_remove_hard_mode_limit() + elif op == CrackOp.DisableRemoveHardModeLimit: + api.disable_remove_hard_mode_limit() + elif op == CrackOp.EnableFakePlayer: + if not deep_get(config.data, "Hook.FakePlayer.Enable", False): + continue + api.update_fake_player_info( + CrackApi.FakePlayerInfo( + name=str(deep_get("Hook.FakePlayer.Name", "")), + level=str(deep_get("Hook.FakePlayer.Level", "")), + id=str(deep_get("Hook.FakePlayer.Id", "")), + ) + ) + api.enable_fake_player() + elif op == CrackOp.DisableFakePlayer: + api.disable_fake_player() + elif op == CrackOp.EnableNoBBAnimation: + if not deep_get(config.data, "Hook.Misc.NoBBAnimation", False): + continue + api.enable_no_bb_animation() + elif op == CrackOp.DisableNoBBAnimation: + api.disable_no_bb_animation() + elif op == CrackOp.EnableNoEmotionWarning: + if not deep_get(config.data, "Hook.Misc.NoEmotionWarning", False): + continue + api.enable_no_emotion_warning() + elif op == CrackOp.DisableNoEmotionWarning: + api.disable_no_emotion_warning() + elif op == CrackOp.IsAlive: + api.is_alive() + else: + raise ValueError(f"Unknown op: {op}") + + +def crack_op(ops: Union[Type[CrackOp.Op], List[Type[CrackOp.Op]]]): + def inner(f): + def wrapper(*args, **kwargs): + obj = args[0] + do_crack_op(obj.config, obj.device, ops) + return f(*args, **kwargs) + + return wrapper + + return inner + + +def disable_all_crack(f): + def wrapper(*args, **kwargs): + obj = args[0] + do_crack_op(obj.config, obj.device, CrackOp.DisableAll) + return f(*args, **kwargs) + + return wrapper + + +def enable_all_crack(f): + def wrapper(*args, **kwargs): + obj = args[0] + do_crack_op(obj.config, obj.device, CrackOp.EnableAll) + return f(*args, **kwargs) + + return wrapper + + +CHAPTER_CRACK_OPS = [ + CrackOp.EnableChapterFastMove, + CrackOp.EnableNoBBAnimation, + CrackOp.EnableNoEmotionWarning, + CrackOp.EnableGlobalShipProperties, + CrackOp.EnableRemoveHardModeLimit, + CrackOp.EnableFakePlayer, +] + + +def chapter_task_crack(f): + def wrapper(*args, **kwargs): + obj = args[0] + do_crack_op(obj.config, obj.device, CHAPTER_CRACK_OPS) + return f(*args, **kwargs) + + return wrapper + + +OPSI_CRACK_OPS = [ + CrackOp.EnableNoBBAnimation, + CrackOp.EnableOpsiFastMove, + CrackOp.EnableGlobalShipProperties, + CrackOp.EnableFakePlayer, +] + + +def opsi_task_crack(f): + def wrapper(*args, **kwargs): + obj = args[0] + do_crack_op(obj.config, obj.device, OPSI_CRACK_OPS) + return f(*args, **kwargs) + + return wrapper diff --git a/module/luahook/op.py b/module/luahook/op.py new file mode 100644 index 000000000..4ef636d17 --- /dev/null +++ b/module/luahook/op.py @@ -0,0 +1,65 @@ +class CrackOp: + class Op: + ... + + class EnableAll(Op): + ... + class DisableAll(Op): + ... + + class EnableGlobalShipProperties(Op): + ... + + class DisableGlobalShipProperties(Op): + ... + + class EnableChapterFastMove(Op): + ... + + class DisableChapterFastMove(Op): + ... + + class EnableOpsiFastMove(Op): + ... + + class DisableOpsiFastMove(Op): + ... + + class EnableRemoveHardModeShipPropertiesLimit(Op): + ... + + class DisableRemoveHardModeShipPropertiesLimit(Op): + ... + + class EnableRemoveHardModeShipTypeLimit(Op): + ... + + class DisableRemoveHardModeShipTypeLimit(Op): + ... + + class EnableRemoveHardModeLimit(Op): + ... + + class DisableRemoveHardModeLimit(Op): + ... + + class EnableFakePlayer(Op): + ... + + class DisableFakePlayer(Op): + ... + + class EnableNoBBAnimation(Op): + ... + + class DisableNoBBAnimation(Op): + ... + + class EnableNoEmotionWarning(Op): + ... + + class DisableNoEmotionWarning(Op): + ... + + class IsAlive(Op): + ... \ No newline at end of file