Skip to content
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
28 changes: 28 additions & 0 deletions app/api/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,34 @@ async def import_script_config_file(
return OutBase(message="脚本配置文件已导入")


@router.post(
"/maaend/options",
tags=["Get"],
summary="获取 MaaEnd 动态选项",
response_model=MaaEndOptionsOut,
status_code=200,
)
async def get_maaend_options(options: ScriptDeleteIn = Body(...)) -> MaaEndOptionsOut:
try:
data = await Config.get_maaend_options(options.scriptId)
return MaaEndOptionsOut(
controllers=[ComboBoxItem(**item) for item in data["controllers"]],
controllerTypes=data["controllerTypes"],
essenceLocations=[
ComboBoxItem(**item) for item in data["essenceLocations"]
],
)
except Exception as e:
return MaaEndOptionsOut(
code=500,
status="error",
message=f"{type(e).__name__}: {str(e)}",
controllers=[],
controllerTypes={},
essenceLocations=[],
)


@router.post(
"/user/get",
tags=["Get"],
Expand Down
16 changes: 16 additions & 0 deletions app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,22 @@ async def get_script(self, script_id: str | None) -> tuple[list, dict]:
index = data.pop("instances", [])
return list(index), data

async def get_maaend_options(
self, script_id: str
) -> dict[str, list[dict[str, str]]]:
"""读取指定 MaaEnd 安装目录中的动态选项。"""

script_config = self.ScriptConfig[uuid.UUID(script_id)]
if not isinstance(script_config, MaaEndConfig):
raise TypeError("脚本配置类型错误, 不是 MaaEnd 类型")
root_path = str(script_config.get("Info", "Path") or "").strip()
if not root_path:
raise ValueError("MaaEnd 路径未配置")

from app.task.MaaEnd.ScriptConfig import load_maaend_options

return await asyncio.to_thread(load_maaend_options, Path(root_path))

async def update_script(
self, script_id: str, data: Dict[str, Dict[str, Any]]
) -> None:
Expand Down
23 changes: 13 additions & 10 deletions app/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
MATERIALS_MAP,
RESOURCE_STAGE_INFO,
MAA_STAGE_KEY,
MAAEND_AUTO_ESSENCE_LOCATION_OPTIONS,
MAAEND_PROTOCOL_SPACE_TASK_OPTIONS,
MAAEND_SANITY_TASK_DEFAULTS,
MAAEND_SANITY_TASK_DETAIL_LABELS,
Expand Down Expand Up @@ -115,7 +114,7 @@ def init_maaend_task_config(config) -> None:
"Task",
"AutoEssenceSpecifiedLocation",
MAAEND_SANITY_TASK_DEFAULTS["AutoEssenceSpecifiedLocation"],
OptionsValidator(list(MAAEND_AUTO_ESSENCE_LOCATION_OPTIONS)),
StringValidator(),
)

for task_name in MAAEND_TASKS:
Expand Down Expand Up @@ -980,9 +979,14 @@ def getTags(self) -> str:
if sanity_task_type == "Essence"
else task_config[sanity_task_type]
)
detail_label = (
detail_key
if sanity_task_type == "Essence"
else MAAEND_SANITY_TASK_DETAIL_LABELS[detail_key]
)
tags.append(
{
"text": f"详细任务:{MAAEND_SANITY_TASK_DETAIL_LABELS[detail_key]}",
"text": f"详细任务:{detail_label}",
"color": "blue",
}
)
Expand Down Expand Up @@ -1052,13 +1056,12 @@ def __init__(self) -> None:
self.Game_ControllerType = ConfigItem(
"Game",
"ControllerType",
"Win32-Front",
OptionsValidator(
[
"Win32-Front",
"ADB",
]
),
"",
StringValidator(),
)
## 控制器协议类型
self.Game_ControllerProtocol = ConfigItem(
"Game", "ControllerProtocol", "", StringValidator()
)
## 终末地游戏路径
self.Game_Path = ConfigItem("Game", "Path", "", FileValidator())
Expand Down
25 changes: 11 additions & 14 deletions app/models/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ class ComboBoxOut(OutBase):
data: List[ComboBoxItem] = Field(..., description="下拉框选项")


class MaaEndOptionsOut(OutBase):
controllers: List[ComboBoxItem] = Field(..., description="MaaEnd 控制器选项")
controllerTypes: dict[str, str] = Field(..., description="控制器协议类型映射")
essenceLocations: List[ComboBoxItem] = Field(..., description="MaaEnd 基质刷取地点选项")


class GetStageIn(BaseModel):
type: Literal[
"User",
Expand Down Expand Up @@ -892,17 +898,9 @@ class MaaEndUserConfig_Task(BaseModel):
RewardsSetOption: Optional[Literal["RewardsSetA", "RewardsSetB"]] = Field(
default=None, description="奖励组选项"
)
AutoEssenceSpecifiedLocation: Optional[
Literal[
"VFTheHub",
"VFOriginiumSciencePark",
"VFOriginLodespring",
"VFPowerPlateau",
"WLWulingCity",
"WLQingboStockade",
"WLMarkerStone",
]
] = Field(default=None, description="基质刷取指定地点")
AutoEssenceSpecifiedLocation: Optional[str] = Field(
default=None, description="基质刷取指定地点"
)
IfSanity: Optional[bool] = Field(default=None, description="理智任务")
IfAutoUseSpMedication: Optional[bool] = Field(
default=None, description="应急理智加强剂"
Expand Down Expand Up @@ -974,9 +972,8 @@ class MaaEndConfig_Run(BaseModel):


class MaaEndConfig_Game(BaseModel):
ControllerType: Optional[Literal["Win32-Front", "ADB"]] = Field(
default=None, description="控制器类型"
)
ControllerType: Optional[str] = Field(default=None, description="控制器类型")
ControllerProtocol: Optional[str] = Field(default=None, description="控制器协议类型")
Path: Optional[str] = Field(default=None, description="终末地客户端路径")
Arguments: Optional[str] = Field(default=None, description="游戏启动参数")
WaitTime: Optional[int] = Field(default=None, ge=60, description="游戏等待时间")
Expand Down
37 changes: 14 additions & 23 deletions app/task/MaaEnd/AutoProxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import re
import uuid
import json
import json5
import shutil
import asyncio
from pathlib import Path
Expand All @@ -38,6 +37,7 @@
from app.tools import skland_sign_in
from app.utils.constants import UTC4, UTC8, MAAEND_SANITY_TASK_FIELDS, MAAEND_TASKS
from .tools import login, push_notification, replace_account_switch_task
from .resource_loader import load_maaend_task_i18n
from app.task.general.tools import execute_script_task

logger = get_logger("MaaEnd 自动代理")
Expand Down Expand Up @@ -240,7 +240,7 @@ async def main_task(self):
controller_type = self.script_config.get("Game", "ControllerType")
try:
if self.emulator_manager is None:
if controller_type != "ADB" and is_process_running("Endfield.exe"):
if is_process_running("Endfield.exe"):
logger.info(
"检测到终末地客户端进程已在运行,跳过由 MAS 重复启动游戏"
)
Expand Down Expand Up @@ -407,8 +407,11 @@ async def main_task(self):
"脚本后任务",
)

if "游戏分辨率设置错误" in self.cur_user_log.status:
logger.info("检测到游戏分辨率设置错误,跳过后续重试")
if (
"游戏分辨率设置错误" in self.cur_user_log.status
or "颜色识别失败" in self.cur_user_log.status
):
logger.info("检测到游戏画面参数错误,跳过后续重试")
break

async def handle_pre_maaend_error(
Expand Down Expand Up @@ -559,26 +562,12 @@ async def set_maaend(self, device_info: DeviceInfo | None) -> None:
settings = maaend_set["settings"]
if settings["language"] == "system":
settings["language"] = "zh-CN"
maaend_i18n_raw = json.loads(
(
self.maaend_root_path
/ f"locales/interface/{settings['language'].lower().replace('-', '_')}.json"
).read_text(encoding="utf-8")
maaend_i18n = await asyncio.to_thread(
load_maaend_task_i18n,
self.maaend_root_path,
str(settings["language"]),
)

maaend_i18n: dict[str, str] = {}
for task_definition_file in self.maaend_root_path.glob("tasks/*.json"):
task_definition = json5.loads( # type: ignore
task_definition_file.read_text(encoding="utf-8")
)["task"][0]
if task_definition["label"].startswith("$"):
locale_text = maaend_i18n_raw.get(task_definition["label"].lstrip("$"))
if locale_text is None:
raise RuntimeError("MaaEnd 文件不完整,卸载后重新安装MaaEnd")
maaend_i18n[task_definition["name"]] = locale_text
else:
maaend_i18n[task_definition["name"]] = task_definition["label"]

if_quick_config = self.cur_user_config.get("Info", "IfQuickConfig")

def get_task_book_name(task: dict[str, object]) -> str:
Expand Down Expand Up @@ -741,7 +730,7 @@ def get_task_book_name(task: dict[str, object]) -> str:
and target_task_name == "AutoEssence"
):
task.setdefault("optionValues", {})
task["optionValues"]["AutoEssenceSpecifiedLocation"] = {
task["optionValues"]["AutoEssenceChooseLocation"] = {
"type": "select",
"caseName": sanity_task_config["AutoEssenceSpecifiedLocation"],
}
Expand Down Expand Up @@ -816,6 +805,8 @@ async def check_log(self, log_content: list[str], latest_time: datetime) -> None
self.cur_user_log.status = "MaaEnd 任务启动失败"
elif "resolution check failed" in log or "分辨率不符合要求" in log:
self.cur_user_log.status = "游戏分辨率设置错误,请重设分辨率比例为16:9"
elif "识别颜色失败" in log or "Color identification failed" in log:
self.cur_user_log.status = "MaaEnd 颜色识别失败,请关闭滤镜或 HDR"
elif "任务失败: AccountSwitch" in log:
self.cur_user_log.status = "MaaEnd 账号切换失败"
elif (
Expand Down
2 changes: 1 addition & 1 deletion app/task/MaaEnd/ManualReview.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ async def _run_maaend_account_switch(self, account_id: str) -> None:
controller_type=str(
self.script_config.get("Game", "ControllerType")
),
template_set=local_config,
fallback_set=local_config,
)
maaend_instance = maaend_set["instances"][0]
maaend_instance["tasks"] = []
Expand Down
30 changes: 9 additions & 21 deletions app/task/MaaEnd/ScriptConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@
from app.models.emulator import DeviceBase
from app.services import System
from app.utils import get_logger, ProcessManager
from .resource_loader import load_maaend_options

logger = get_logger("MaaEnd 脚本设置")


def normalize_maaend_config(
maaend_set: dict[str, Any],
controller_type: str,
template_set: dict[str, Any] | None = None,
fallback_set: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""将 MaaEnd 配置收束为 AUTO-MAS 单实例配置"""

Expand All @@ -61,8 +62,8 @@ def select_instance(source_set: dict[str, Any]) -> dict[str, Any] | None:
return None

selected_instance = select_instance(maaend_set)
if selected_instance is None and template_set is not None:
selected_instance = select_instance(template_set)
if selected_instance is None and fallback_set is not None:
selected_instance = select_instance(fallback_set)
if selected_instance is None:
raise ValueError("MaaEnd 配置文件中未找到可用实例")

Expand Down Expand Up @@ -131,16 +132,12 @@ async def set_maaend(self):
if (self.config_file_path / "mxu-MaaEnd.json").exists():
shutil.rmtree(self.maaend_set_path, ignore_errors=True)
shutil.copytree(self.config_file_path, self.maaend_set_path)
else:
maaend_template_path = (
Path.cwd() / "res/templates/MaaEnd/config/mxu-MaaEnd.json"
elif self.maaend_set_path.exists():
shutil.copytree(
self.maaend_set_path,
self.config_file_path,
dirs_exist_ok=True,
)
if maaend_template_path.exists():
shutil.rmtree(self.maaend_set_path, ignore_errors=True)
self.maaend_set_path.mkdir(parents=True, exist_ok=True)
shutil.copy2(
maaend_template_path, self.maaend_set_path / "mxu-MaaEnd.json"
)

maaend_set_path = self.maaend_set_path / "mxu-MaaEnd.json"
if not maaend_set_path.exists():
Expand All @@ -149,18 +146,9 @@ async def set_maaend(self):
)

maaend_set = json.loads(maaend_set_path.read_text(encoding="utf-8"))
maaend_template_path = (
Path.cwd() / "res/templates/MaaEnd/config/mxu-MaaEnd.json"
)
template_config = (
json.loads(maaend_template_path.read_text(encoding="utf-8"))
if maaend_template_path.exists()
else None
)
maaend_set = normalize_maaend_config(
maaend_set,
self.script_config.get("Game", "ControllerType"),
template_config,
)

maaend_set_path.write_text(
Expand Down
16 changes: 10 additions & 6 deletions app/task/MaaEnd/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def __init__(self, script_info: ScriptItem):
self.task_info = script_info.task_info
self.script_info = script_info
self.check_result = "-"
self.controller_protocol = ""

async def check(self) -> str:
if self.task_info.mode not in METHOD_BOOK:
Expand All @@ -70,16 +71,19 @@ async def check(self) -> str:
if not (Path(script_config.get("Info", "Path")) / "MaaEnd.exe").exists():
return "MaaEnd.exe文件不存在, 请检查MaaEnd路径设置!"

if (script_config.get("Game", "ControllerType") == "ADB") and (
self.controller_protocol = script_config.get("Game", "ControllerProtocol")

if self.controller_protocol == "Adb" and (
script_config.get("Game", "EmulatorId") == "-"
or script_config.get("Game", "EmulatorIndex") in ["", "-"]
):
return "未完成模拟器配置, 请检查脚本配置中的模拟器设置!"
elif (
script_config.get("Game", "ControllerType").startswith("Win32")
and not Path(script_config.get("Game", "Path")).exists()
):
elif self.controller_protocol == "Win32" and not Path(
script_config.get("Game", "Path")
).exists():
return "未完成游戏配置, 请检查脚本配置中的游戏设置!"
elif self.controller_protocol not in ("Adb", "Win32"):
return "MaaEnd 控制器协议未配置, 请重新选择控制器!"

if self.task_info.mode == "AutoProxy" and not (
Path(
Expand All @@ -106,7 +110,7 @@ async def prepare(self):
self.temp_path = Path.cwd() / f"data/{self.script_info.script_id}/Temp"

# 初始化模拟器管理器
if self.script_config.get("Game", "ControllerType") == "ADB":
if self.controller_protocol == "Adb":
self.emulator_manager = await EmulatorManager.get_emulator_instance(
self.script_config.get("Game", "EmulatorId")
)
Expand Down
Loading
Loading