Commit Graph

148 Commits

Author SHA1 Message Date
Loong
b2fa9a1c3e fix: 显式声明 adb-lite client 对应的 serial (#16853)
## 概述

- 在 `AdbController::connect` 确定目标 address/serial 后,通过
`PlatformIO::set_adb_serial` 显式声明当前 adb 设备。
- NativeIO 默认忽略该调用;AdbLiteIO 使用该 serial 初始化或切换内部 adb-lite client。
- adb-lite 的 `shell`、`exec-out`、`push` 和交互式 shell 只使用已声明的
client,不再从命令路径里隐式初始化 client。
- AdbLiteIO 内部集中校验 client/serial,并用 mutex 覆盖 `m_adb_client` /
`m_adb_serial` 以及对 adb-lite client 的调用,避免 `call_command` 与
`interactive_shell` 并发访问同一状态。

## 背景

#16850 已修复 `adb connect` 被跳过后 adb-lite client 未初始化的问题,并补充了内部锁保护。这个 PR
是后续调整:把 client 初始化入口从命令执行路径挪到连接生命周期中,让状态声明更明确。

## 修复方式

连接阶段已经知道当前 address/serial,所以在设置 connect/release 命令后调用:

```cpp
m_platform_io->set_adb_serial(address);
```

对 NativeIO 来说这是 no-op;对 AdbLiteIO 来说,这是初始化或切换 `adb::client` 的入口。

后续 adb-lite 命令路径通过 `lock_adb_client(serial)` 统一校验命令中的 `-s <serial>` 与当前
client 对应的 serial 一致,并在同一把锁仍然持有时调用 adb-lite client。这样避免在
`interactive_shell` 中隐式写入 client/serial 状态,也避免把 `m_adb_client` 暴露给无锁调用。

`set_adb_serial` 也改为先创建临时 `adb::client`,创建成功后再提交 `m_adb_serial` /
`m_adb_client` 状态;如果创建失败则记录错误并清空 adb-lite client 状态,避免 serial 已切换但
client 仍指向旧对象的不一致状态。

## 验证

- `git diff --check --cached`
- `cmake --build /private/tmp/maa-build-8599-core --parallel 8`
- `cmake --install /private/tmp/maa-build-8599-core --config
RelWithDebInfo`
- 本机 `maa` CLI + Android 35 arm64 AVD:
  - `maa run connect-only -p adb-lite-test --no-summary -vvv`
  - 日志显示 `Loading MaaCore from: /private/tmp/maa-test-data-8599/lib`
  - 日志显示 `Setting adb lite enabled to true`
  - 日志到达 `Connected`
  - 日志到达 `FastestWayToScreencap Encode`
  - 未出现 `adb client not initialized`
  - 未出现 `failed to create adb-lite client`
  - 未出现 `fallback to NativeIO`
2026-05-29 00:46:30 +08:00
Alex Gu
45aefd30d7 fix: filenum_ctrl SIGABRT (#16233) + PosixIO fork _exit + CI action SHA pin (#16502)
* fix: PosixIO::call_command 中 fork 后 child 缺少 _exit 导致 unwind 进 parent 代码

当 execlp 失败时,child 分支走 `return std::nullopt`,该子进程会从
PosixIO::call_command 一路 unwind 回 AdbController::call_command 及其
caller,跟 parent 同时跑:

- 在 child 地址空间副本中释放 m_callcmd_mutex(caller 处持有的 unique_lock)
- 后续命中 `kill(m_child, SIGTERM)` 时 child 视角 m_child==0
  → POSIX `kill(0, SIG)` 给整个进程组发信号 → 把 parent 一并杀掉
- 析构时双 close 同一 fd 号(kernel 已重用给别处)→ 关掉 parent 的
  socket / log fd → 后续 read/write 错乱

查看 git 历史发现回归引入于 #7748(perf: update PosixIO::call_command, 2023-12)。
当时 perf 重构把 child 分支结尾的 `::exit(exit_ret)` 替换成
`Log.error(...) + return std::nullopt`。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: 将第三方 action 钉到 commit SHA,符合 GitHub 安全建议

https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: filenum_ctrl 中 std::filesystem 调用缺 error_code 导致 SIGABRT

Windows 上 debug 截图被另一进程占用时,std::filesystem 多个调用会抛 filesystem_error。
异常未被 filenum_ctrl / save_debug_image / AbstractTask::run 任一层
捕获,沿任务栈一路冒到 MaaCore 顶层(issue #16233)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: 清理 PosixIO 子分支 execlp 死赋值

按 Sourcery review 清掉死代码。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: restore release package workflow

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 22:20:33 +08:00
github-actions[bot]
6a286f05a3 chore: Auto update by pre-commit hooks
https://github.com/MaaAssistantArknights/MaaAssistantArknights/actions/runs/26373021279
[skip changelog]
2026-05-24 21:19:51 +00:00
uye
ab7e4f5a4f fix: Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 04:02:24 +08:00
Loong
975981c568 fix: 修复 adb-lite 跳过 adb connect 后未初始化 client (#16850)
## 概述

- 在 adb-lite 处理 `shell`、`exec-out`、`push` 和交互式 shell 命令时,从 `-s <serial>`
参数懒初始化 adb-lite client。
- `adb connect <address>` 路径继续使用同一个 client 获取逻辑。
- 记录当前 adb-lite serial,在切换设备时重新创建 client。

## 问题原因

#15300 之后,`AdbController::connect` 会先检查 `adb devices`。如果目标设备已经在列表中,例如
`emulator-5554 device`,就会跳过 `adb connect <serial>`。

这个行为对普通 adb 路径是合理的,但 adb-lite 之前只在 `adb connect` 分支里初始化
`m_adb_client`。因此当设备已经在线、`adb connect` 被跳过时,后续的 `adb -s emulator-5554
shell ...` 会进入 adb-lite 路径,但此时 client 尚未初始化,日志中会出现:

```text
adb client not initialized
fallback to NativeIO
```

## 修复方式

把 adb-lite client 的初始化从 `adb connect` 分支中解耦出来。只要 adb-lite 收到带有 `-s
<serial>` 的 `shell`、`exec-out`、`push` 或交互式 shell 命令,就根据该 serial 获取或创建对应的
`adb::client`。

这样保留了 #15300 中“设备已在线时跳过 `adb connect`”的行为,同时也保证 adb-lite 不再依赖 `adb
connect` 作为唯一初始化入口。

## 验证

- 使用本机已安装的 maa-cli, MaaCore v6.10.6 和 Android 35 arm64 AVD 复现:
  - `adb devices` 返回 `emulator-5554 device`
  - 日志出现 `adb client not initialized`
  - 随后出现 `fallback to NativeIO`
- 编译并安装修复后的 Core:
  - 日志显示 `Setting adb lite enabled to true`
  - 日志到达 `Connected`
  - 日志到达 `FastestWayToScreencap Encode`
  - 未再出现 `adb client not initialized`
  - 未再出现 `fallback to NativeIO`

## Summary by Sourcery

确保 adb-lite 为每个设备序列号初始化并复用一个客户端,用于 shell、exec-out、push 和交互式 shell 命令,并且与
adb connect 的执行路径无关。

Bug 修复:
- 当已在线设备跳过 adb connect 时,防止 adb-lite 因客户端未初始化而失败。

增强功能:
- 追踪当前的 adb-lite 设备序列号,并在序列号变化时重新创建 adb 客户端,以支持多设备会话。

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Ensure adb-lite initializes and reuses a client per device serial for
shell, exec-out, push, and interactive shell commands, independent of
the adb connect path.

Bug Fixes:
- Prevent adb-lite from failing with an uninitialized client when adb
connect is skipped for already-online devices.

Enhancements:
- Track the current adb-lite device serial and recreate the adb client
when the serial changes to support multiple device sessions.

</details>
2026-05-25 03:50:35 +08:00
Loong
48424a68c5 fix: refactor ADB connection logic to only connect when needed (#15300)
优化 ADB 连接处理逻辑,避免不必要的连接尝试,并在连接不需要或无效时改进错误报告。
2026-05-23 16:24:49 +01:00
github-actions[bot]
1e0d66cf7d chore: Auto update by pre-commit hooks
https://github.com/MaaAssistantArknights/MaaAssistantArknights/actions/runs/25763261294
[skip changelog]
2026-05-12 21:29:51 +00:00
Aliothmoon
c6f930a60d feat: support native android (#16179)
* feat: add android controller support

* feat: add android compile options and add core static options

* fix: add __ANDROID__ macro for Android platform

* fix: move switch break into macro conditional block

* ci: add Android CMake presets (arm64, x64)

* fix: add missing Android TouchMode handling

* fix: remove unnecessary pre-check in android

* fix: restore unnecessary modifications

* fix: unified use __ANDROID__

* refactor: use interpolate_swipe_with_pause in AndroidController

* feat: add Android crash logcat output and native backtrace

* feat: only copy once from external lib screencap

* feat: add Android JNI AttachThread/DetachThread to working_proc

* fix: break control in switch

* fix: update click interval & swipe interval

* ci: restore Android build job with MaaFramework download

* fix: remove thread attach

* ci: temporarily remove MaaAndroidNativeControlUnit.so

* feat(android-ctrl): SWIPE_WITH_PAUSE 由 InstanceOption DeploymentWithPause 控制

* feat: SWIPE_WITH_PAUSE 默认启用

* feat: add android compile options and add core static options

* feat: use maafw controll unit

* fix: minor adjustments

* refactor: adapt maafw android lib

* fix: remove screencap when connect

* chore: change maafw lib use

* feat: adapt maafw control unit click & click_key

* fix: address critical issues in Android native controller

- Restore InstanceOptionKey::ClientType case in set_instance_option,
  which was accidentally removed and broke client type setting on all platforms
- Enable libMaaAndroidNativeControlUnit.so copy step in CI (was commented out),
  so Android artifacts actually include the required control unit library
- Fail connect() early when screen_resolution is missing or invalid in config,
  preventing silent {0,0} resolution that would break swipe and screencap
- Check touch_down() return value in swipe() and abort on failure

* fix: correct bounds_check off-by-one and extract KEYCODE_ESCAPE constant

- bounds_check used <= which allowed coordinates equal to screen width/height
  (off-screen); use < to match the valid range [0, dimension-1]
- Extract magic number 111 into KEYCODE_ESCAPE constexpr in press_esc()

* fix: avoid heap allocation in noexcept terminate/signal handlers

format_signal_reason was returning std::string and using std::format in
its default branch. Called from noexcept custom_terminate_handler before
the outer try block, any allocation failure would throw std::bad_alloc,
triggering recursive std::terminate and aborting crash reporting.

- Change return type to const char* noexcept, returning string literals
- Replace std::format("Signal {}", sig) with "Unknown Signal" (only the
  four registered signals are ever passed, so this branch is unreachable)
- Change signal_info in custom_terminate_handler from std::string to
  const char*, eliminating all allocations on the pre-try path

* fix: suppress unused-variable warning for signal_reason on non-Android
2026-05-12 22:15:15 +08:00
uye
5445962a7b docs: 补全连接阶段的 ClientType 参数说明 2026-05-04 19:13:52 +08:00
1b2c
e83e49f61b fix: 部分场景下无法自动启动游戏 (#16422)
943a798b38 在 AdbConnectionContext::replace_cmd 的统一替换表中加入了 [PackageName],
导致 m_adb.start / m_adb.stop 在 connect 阶段就被提前替换。当调用方未通过
AsstSetInstanceOption(ClientType, ...) 设置 client_type 时(如 maa-cli 等不感知该
新选项的第三方调用方),m_conn_ctx.package_name 为空字符串,m_adb.start 中的
[PackageName] 被替换为空,后续 start_game(client_type) 内的运行时替换沦为 no-op,
最终启动命令缺失包名导致启动游戏失败。

将 [PackageName] 从统一替换表移除,恢复其作为运行时参数的语义;connect 阶段唯一
需要 [PackageName] 的位置(Androws 的 displayId 查询)改为就地显式替换。

Close #16392

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 13:24:54 +08:00
Jin Zhaonian
60cbc68e59 fear: PC 端支持 完成后退出明日方舟 (#16351)
* fix: 修复pc端任务完成后勾选自动关闭游戏但无法关闭的问题

* fix: IsWindow检查与WM_CLOSE关闭

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
2026-04-27 01:36:19 +08:00
Alex Gu
7d3f8bfa2d fix: 若干正确性问题修正(null check / race / clamp / retry 等 9 处) (#16332)
## 概述

代码 review 发现的一批正确性问题批量修正,都是 `src/MaaCore` 下单行 / 局部 logic 级别的
bug,彼此独立,按严重度从高到低逐条 commit。没有功能改动、没有 API 变化、没有 breaking behavior。

每个 commit 单独对应一条 bug,便于逐条 review 或按需
revert。下表也按严重度从高到低排序,附上「原问题」与「修法理由」。

### 发现来源

使用 Claude Code 新版 `/ultrareview` 多 agent review 扫了 `src/MaaCore` 源码(~58k
行),人工逐条核对后提 PR。另有 2 条(AvatarCacheManager 并发保护 / CopilotConfig
默认值语义)涉及设计决策或 breaking change,已单独提 issue 讨论,不在本 PR 内。

---

## Fix 列表(按严重度排序)

### 1. `Controller::back_to_home` 空指针解引用崩溃 — HIGH

**位置**:`src/MaaCore/Controller/Controller.cpp:137`

**原问题**:
```cpp
bool asst::Controller::back_to_home()
{
    m_controller->back_to_home();   // 无 null 检查
    return true;
}
```
未连接设备时 `m_controller == nullptr`,此处直接 segfault。同文件 `sync_params` /
`start_game` / `stop_game` / `click` 等所有公开方法都有 null 保护(前者用 `if
(!m_controller) return`,后者用 `CHECK_EXIST(m_controller, false)` 宏),**只有
`back_to_home` 漏了**。

**触发路径**:任务运行中 ADB 断连使 `m_controller` 被 reset 成 null → 调度走到"回主页"分支 → 崩溃。

**修法**:函数开头加 `CHECK_EXIST(m_controller,
false);`,与同文件其他方法保持一致。最小改动,零行为变化(未连接时 `back_to_home` 返回 `false` 而不是崩溃)。

---

### 2. `MumuExtras::screencap` reload 之后没有真正重试 — MEDIUM-HIGH

**位置**:`src/MaaCore/Controller/MumuExtras.cpp:54-83`

**原问题**:
```cpp
int ret = capture_display_func_(...);       // 第一次 capture
if (ret) {
    // Try reloading once before giving up.
    if (!reload()) { ... return std::nullopt; }
    if (ret) {                              // ← 检查的是同一个 ret,不是重试之后的
        LogError << "Failed to capture display, but reload before retrying capture was successful." ...;
        return std::nullopt;
    }
}
```
注释承诺"失败时 reload 后重试 capture",但第二个 `if (ret)` 检查的还是第一次调用写入的
`ret`,`capture_display_func_` **从未被重新调用过**。reload 成功或失败都必然返回
`std::nullopt`,注释里的"reload before retrying capture was
successful"日志其实**只是字面,retry 压根没发生**。

**后果**:MuMu 模拟器短暂 capture 失败(内存抖动 / 显示切换)时,原本 reload
应能恢复,但代码从不真正重试,上层看到的永远是「截图失败」。

**修法**:在两个 `if (ret)` 之间加上**真正的**重试:重新调用 `capture_display_func_` 并更新
`ret`。reload 可能改变设备状态,所以 `display_id` 也重新 `get_display_id()`
取一次。失败信息相应改为"Failed to capture display even after reload",语义准确。

---

### 3. `Controller` 中 `d_size` 被 `const static` 固化,分辨率变化后缩放错误 — MEDIUM

**位置**:`src/MaaCore/Controller/Controller.cpp:146` 和 `:438`(两处)

**原问题**:
```cpp
const static cv::Size d_size(m_scale_size.first, m_scale_size.second);
```
C++ 函数内 `static` 局部变量的初始化表达式**仅在第一次进入该函数时执行一次**。首次调用时 `m_scale_size`
的值被固化到 `d_size`,之后用户切换模拟器、改分辨率预设、连接新设备等导致 `m_scale_size` 变化时,**`d_size`
不会更新**。

**后果**:
- `cv::resize(..., d_size, ...)` 把图缩到错误的目标尺寸 → OCR / 模板匹配的坐标/尺寸都错
- 截图失败兜底路径 `m_cache_image = cv::Mat(d_size, CV_8UC3)` 也分配错误尺寸的空图
- 用户看到各种"识别不到 / 坐标偏移"但不崩溃,**只能通过重启 MaaCore 恢复**

**修法**:去掉 `static`,每次调用重新构造。`cv::Size` 就是两个
`int`,栈上构造基本无开销,不存在性能回归。两处都修。

---

### 4. `PlayToolsController::swipe` 起点钳制没有应用到 `toucher_down` —
MEDIUM-LOW

**位置**:`src/MaaCore/Controller/PlayToolsController.cpp:230`

**原问题**:
```cpp
int x1 = p1.x, y1 = p1.y;                          // 拷贝到局部变量
// ...
if (x1 < 0 || x1 >= width || y1 < 0 || y1 >= height) {
    x1 = std::clamp(x1, 0, width - 1);             // 只钳制了局部 x1/y1
    y1 = std::clamp(y1, 0, height - 1);
}
// ...
toucher_down(p1);                                  // ← 仍用原始的 const Point& p1
```
注释"起点不能在屏幕外,但是终点可以"已经说明了钳制意图,但钳制结果只存在局部 `x1/y1`,`toucher_down(p1)`
使用的还是未钳制的原始 `p1`。后续 `progressive_move` 用 `x1/y1` 所以中段 swipe 在屏幕内,**但首个
touch-down 事件发到屏幕外坐标**,iOS PlayTools 侧多半会被丢弃或忽略 → 整个手势无效。

**修法**:`toucher_down(p1)` → `toucher_down({ x1, y1 })`,用已钳制的坐标构造临时
`Point`。与同文件下方 `toucher_move({ x, y })` 的风格一致。

---

### 5. `Assistant::m_task_id` 非原子 static 导致跨实例数据竞争 — MEDIUM-LOW

**位置**:`src/MaaCore/Assistant.h:214`(声明)+
`src/MaaCore/Assistant.cpp:295-296`(使用)

**原问题**:
```cpp
// Assistant.h
inline static TaskId m_task_id = 0;                     // 进程级 static,非 atomic
mutable std::mutex m_mutex;                             // 实例级 mutex
inline static std::atomic<AsyncCallId> m_call_id = 0;   // 旁证:同文件另一个 static 是 atomic

// Assistant.cpp:295
std::unique_lock<std::mutex> lock(m_mutex);             // 锁的是当前实例的 mutex
int task_id = ++m_task_id;                              // 改的是进程共享的 static
```
`m_task_id` 是 static(**所有 Assistant 实例共享一个内存地址**),但 `m_mutex`
是每个实例各自的锁。两个不同实例并发 `++m_task_id` 时,各自 lock 各自的 mutex,彼此不互斥 ——
**数据竞争**,`++` 对非原子 `int` 是 RMW 三步,可能让两个实例拿到同一个 `task_id`。

单实例客户端(MaaWpfGui、MaaMacGui)触发不了;多实例 + 并发下任务的客户端(命令行批处理 / 自动化测试)会偶现任务 id
冲突,后续 `AsstSetTaskParams` / `AsstStop` 可能误中另一个实例的任务。

旁证:同文件 line 227 的另一个同类 static `m_call_id` 已经声明成
`std::atomic<AsyncCallId>`,一致性也对不上,显然是作者漏改。

**修法**:`TaskId m_task_id` → `std::atomic<TaskId>
m_task_id`。`++m_task_id` 靠 `std::atomic::operator++` 原子化,语法不变。`m_mutex`
原先是为紧跟的 `m_tasks_list.emplace_back` 服务的,继续保留。

---

### 6. `OcrPack::load` 复制粘贴导致 `rec_label` 变化检测用错字段 — LOW-MEDIUM

**位置**:`src/MaaCore/Config/Miscellaneous/OcrPack.cpp:61`

**原问题**:
```cpp
// 前两块都正确:当前保存路径 != 新路径 时才更新
if (exists(det_model_file) && m_det_model_path != det_model_file) { ... }
if (exists(rec_model_file) && m_rec_model_path != rec_model_file) { ... }
// 第三块:应该检查 m_rec_label_path,却写成了 m_rec_model_path
if (exists(rec_label_file) && m_rec_model_path != rec_label_file) {
    m_rec_label_path = rec_label_file;
    m_rec = nullptr;
}
```
模型路径以 `.onnx` 结尾,标签路径以 `.txt` 结尾,`m_rec_model_path != rec_label_file`
**恒为真**(只要标签文件存在)。结果每次调用 `load()` 都会:
- 无效地重新给 `m_rec_label_path` 赋同样的值
- 把 `m_rec` 置空 → 触发下一次 OCR 时 lazy 重载

不会导致正确性崩塌(`check_and_load` 路径会 lazy 重建
`m_rec`),但"路径没变则保留缓存"的设计目的完全失败,且多次 `load()` 会产生不必要的模型重载开销。

**修法**:`m_rec_model_path != rec_label_file` → `m_rec_label_path !=
rec_label_file`,与上面 `det` / `rec_model` 两块的模式对齐。

---

### 7. `RoguelikeStageEncounterConfig` `fallback_choices` 校验写错变量 — LOW


**位置**:`src/MaaCore/Config/Roguelike/RoguelikeStageEncounterConfig.cpp:77`

**原问题**:
```cpp
int option_num = pair_arr[0].as_integer();
if (option_num < 0) { ... return false; }               // 校验 option_num,OK
int choice = pair_arr[1].as_integer();
if (option_num < 0) {                                   // ← 重复检查 option_num
    Log.error(
        std::format(
            "RoguelikeEncounterConfig | callback choice for event {} with {} option(s) is less than zero",
            ...));
    return false;
}
```
错误信息本身已经说明是在校验 `choice`("callback choice ... is less than
zero"),但实际比较的是上面已经保证过非负的 `option_num`,这个 `if` 永远进不去。

**后果**:坏 JSON 中 `[3, -5]` 这类负数 `choice` 能被静默接受,后续
`static_cast<size_t>(-5)` 产生巨大无符号数,作为索引使用时 UB。正常配置不触发,但这条防御校验已名存实亡。

**修法**:第二个 `if` 判断变量改为 `choice`。错误消息中 `option_num` 作为上下文保留(用于打印 option
数量,语义正确)。

---

### 8. `AsstDestroy` 对值传入的形参赋 `nullptr` 是死代码 — TRIVIAL

**位置**:`src/MaaCore/AsstCaller.cpp:98`

**原问题**:
```cpp
void AsstDestroy(AsstHandle handle)
{
    if (handle == nullptr) return;
    delete handle;
    handle = nullptr;       // ← 改的是局部拷贝,对调用方的变量无影响
}
```
`AsstHandle` 按值传入,`handle = nullptr` 只修改本函数作用域的局部拷贝。`delete handle`
已正确销毁对象,这行既没有替调用方置空(要做到需接受 `AsstHandle*`),也不会影响 delete 行为,纯死代码。

**修法**:删掉这行,保持 `delete` 行为不变,避免给读者错误印象。

---

### 9. `AvatarCacheManager.h` 开头两行重复的 `#pragma once` — TRIVIAL

**位置**:`src/MaaCore/Config/Miscellaneous/AvatarCacheManager.h:1-3`

**原问题**:
```cpp
#pragma once

#pragma once        // ← 重复
#include "Config/AbstractResource.h"
```
pragma once 幂等,没有功能影响,但看起来是编辑时的残留。清理即可。

---

## Test plan

- [x] `git diff dev-v2..HEAD --stat`:8 文件 / +18/-11,只有目标修改,无无关改动
- [x] 每条 fix 都有对应的单独 commit,便于逐条 review 或按需 revert
- [x] **本地 macOS arm64 build 通过**:`cmake --preset macos-arm64 && cmake
--build build --config Release` → 203/203 targets 编译链接成功,0 error,修改的 8
个文件 0 warning(全局唯一 1 条 warning 是 `MaaUtils/Logger.cpp` 的 `#pragma
message` 版本信息打印,与本 PR 无关)
- [ ] 单元测试:macOS preset 默认没有编译 test target(`unit_test/MaaCore/` 下仅 1 个
`AlgorithmTest.cpp`,与本 PR 改动的函数无交集),本地 `ctest` 返回 "No tests were
found"。依赖上游 CI 完整矩阵(Linux / Windows / macOS + 各自测试)验证。

## Notes for reviewers

- 每条 fix 的 commit message 都附了原理说明,结合 PR 正文对应条目阅读
- **没有功能性增加**,也**不动任何 public API / ABI**
- 不涉及 resource / JSON 数据文件
- 合并之后建议开一个跟进 issue 讨论 ultrareview 的另外两条 finding(AvatarCacheManager
并发保护、CopilotConfig 默认值语义)—— 这两条需要设计决策,不适合塞进本 PR

---

_PR 发现来源:Claude Code `/ultrareview` 多 agent 扫描后人工复核。_

## Summary by Sourcery

在不改变公共 API 或行为的前提下,修复了控制器、OCR、肉鸽配置以及工具组件中的多处正确性问题。

Bug 修复:
- 在 `Controller::back_to_home` 中增加空指针检查,以防在底层控制器不可用时发生崩溃。
- 确保 `MumuExtras::screencap` 在成功重新加载后确实会重试屏幕捕获,并且能准确记录失败日志。
- 在 Controller 中为缩放后的图像尺寸使用非静态的 `cv::Size`,以便在无需重启的情况下正确响应分辨率变化。
- 对传递给 `PlayTools toucher_down` 的滑动起始点进行限制,确保初始触控事件始终在屏幕范围内。
- 将 Assistant 的进程级任务 ID 计数器改为原子类型,避免在多实例场景下产生数据竞争。
- 修正 `OcrPack` 标签路径变更检测逻辑,使 OCR 模型只在标签文件实际变更时才会重新加载。
- 修复 `RoguelikeStageEncounterConfig` 的回退选项校验逻辑,按预期拒绝负数选项。
- 移除 `AsstDestroy` 中无效的句柄重置代码,该代码试图将值传递参数置空但实际上无效。
- 移除 `AvatarCacheManager.h` 中多余的 `#pragma once`,以清理头文件。

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Fix multiple correctness issues across controller, OCR, roguelike
config, and utility components without changing public APIs or
behaviors.

Bug Fixes:
- Add a null check in Controller::back_to_home to prevent crashes when
the underlying controller is unavailable.
- Ensure MumuExtras::screencap actually retries display capture after a
successful reload and logs failures accurately.
- Use a non-static cv::Size for scaled image dimensions in Controller so
resolution changes are respected without restart.
- Clamp the swipe starting point passed to PlayTools toucher_down to
keep initial touch events within screen bounds.
- Make Assistant's process-wide task ID counter atomic to avoid data
races across instances.
- Correct OcrPack label path change detection so OCR models are only
reloaded when the label file actually changes.
- Fix RoguelikeStageEncounterConfig fallback choice validation to reject
negative choices as intended.
- Remove ineffective handle reset code in AsstDestroy that tried to null
a by-value parameter.
- Remove a redundant #pragma once in AvatarCacheManager.h to clean up
the header.

</details>
2026-04-27 00:46:12 +08:00
srdr0p
1b1bc15aca feat: 支持腾讯应用宝 5.10.56.xx (#16292)
* feat(connection): 新增腾讯应用宝模拟器支持

在 WinAdapter 和 config.json 中添加应用宝(Androws)模拟器入口。
本次仅支持 ADB Input 方式和官方渠道。

同步更新各语言连接文档(zh/en/ja/ko/tw)及 UI 本地化字符串。

* feat(controller): 为腾讯应用宝新增多渠道与 Minitouch 支持

应用宝的应用与 DisplayId 绑定,需在 connect() 前确定包名。
新增 InstanceOptionKey::ClientType (= 6),经由
AsstSetInstanceOption -> Assistant::set_instance_option
-> Controller::set_client_type 传递渠道信息;
AdbController::connect 在建立连接时通过 ctrler()->get_client_type()
读取并写入 AdbConnectionContext,用于替换命令模板中的 [PackageName]。

引入 AdbConnectionContext 保存 ADB 连接信息供 Minitouch 使用,
并通过 eventId 标识所绑定的输入设备。
2026-04-26 23:48:24 +08:00
github-actions[bot]
5b6dfc3d50 chore: Auto update by pre-commit hooks
https://github.com/MaaAssistantArknights/MaaAssistantArknights/actions/runs/24639205418
[skip changelog]
2026-04-19 21:11:48 +00:00
Alex Gu
d8df4e753e fix: macOS PlayTools/SCK 几处小修正 (#16276)
fix: macOS 相关几处小修正

- PlayToolsController: screencap_rgba/screencap_bgr/toucher_commit 调用
  open() 时未检查返回值,连接失败时继续操作会写坏的 socket
- PlayToolsController: start_game() 日志写成了 InputText,应该是 StartGame
- OcrPack: macOS 分支日志里残留了多余的 "rec" 字样
- MacSCKHelper: 析构时 stopCapture 是异步的,没等回调完成就释放了
  stream/output/queue,可能踩到野指针;改用信号量等停完再释放
2026-04-19 23:18:27 +08:00
Frog Chen
1a13932f05 feat: AVD截图增强 (#15608)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-18 16:11:58 +08:00
uye
fea3b9371f feat: 连接时返回模拟器分辨率,en 服使用非 1080p 时警告 2026-03-11 19:38:51 +08:00
Hao Guan
a33ce2ccbc feat: macOS ScreenCaptureKit (#15939) 2026-03-08 10:15:10 +08:00
Hao Guan
2b9701ba9a perf: 适配PlayCover的BGR优化截图 (#15810) 2026-02-28 17:41:03 +08:00
github-actions[bot]
ecd4a33f8d chore: Auto update by pre-commit hooks
https://github.com/MaaAssistantArknights/MaaAssistantArknights/actions/runs/21118556393
[skip changelog]
2026-01-18 21:04:18 +00:00
MistEO
a11660cbac feat(PC): 支持 PC 端明日方舟 (#15407)
* feat(controller): 集成 MaaWin32ControlUnit 支持 Win32 窗口截图与控制

- 提取公共滑动插值逻辑到 SwipeHelper.hpp,重构 Minitouch/PlayTools 控制器
- 新增 Win32ControlUnitLoader 动态加载 MaaWin32ControlUnit.dll
- 新增 Win32Controller 实现 ControllerAPI,适配 touch_down/move/up 接口
- 添加 AsstAsyncAttachWindow 公开 API 用于绑定 Win32 窗口
- 添加 Win32 截图/输入方式的类型定义和枚举常量

* feat(api): 增加同步版本的 AsstAttachWindow 接口

模仿 AsstConnect 实现同步绑定 Win32 窗口的接口,方便简单场景下直接调用。
该接口标记为 deprecated,建议使用异步版本 AsstAsyncAttachWindow。

* fix: can build MaaCore

* feat: 完成整体适配

* feat(ci): 添加 MaaFramework Win32ControlUnit 下载步骤

使用 robinraju/release-downloader 从 MaaFramework 最新 release 下载
win-x64 版本并提取 MaaWin32ControlUnit 到构建产物中

* docs: 添加 Win32Controller 调试所需 MaaWin32ControlUnit 的说明

在开发文档的环境配置步骤中说明调试 Win32Controller 需要手动下载
MaaWin32ControlUnit.dll,并欢迎社区贡献自动下载脚本

* fix(i18n): 修改 UseAttachWindowWarning 措辞为"仅供尝鲜"

* fix: 小细节修复

* ci: fix maafw filename

* feat: PC 移至连接配置

* feat: 调整描述

* chore: 调整 PC 在连接配置中的顺序

* feat: 设置指引和开始唤醒界面 PC 选项绑定

---------

Co-authored-by: uye <99072975+ABA2396@users.noreply.github.com>
2026-01-18 21:17:59 +08:00
MistEO
fa7c823dde perf: use boost::regex instead of std::regex (#15126) 2025-12-19 13:04:28 +08:00
MistEO
26502d1e54 chore: add logs 2025-11-07 09:38:00 +08:00
MistEO
87ddcdf8e6 perf: 更好的随机点分布 (#14652)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-07 09:33:53 +08:00
MistEO
640eed10e2 refactor: use MaaUtils instead of Utils (#14579)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-11-02 23:19:38 +08:00
uye
625a3f6228 fix: 手动关闭模拟器后未重启 MAA 时 minitouch 可能失效 2025-10-08 16:48:27 +08:00
Rain Yang
30c8fce308 fix: 增强 playtools 关闭连接时的异常处理,确保套接字安全关闭 (#14280)
* fix: 增强playtools关闭连接时的异常处理,确保套接字安全关闭

* chore: Auto update by pre-commit hooks [skip changelog]

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-10-03 14:14:02 +08:00
Constrat
93bbb0ba4b fix: clang 2025-09-26 21:54:06 +02:00
uye
8e8063a066 fix: 怎么还有人在用 adb input 2025-09-27 00:32:11 +08:00
pre-commit-ci[bot]
d734aecbe2 chore: Auto update by pre-commit hooks [skip changelog] 2025-09-15 14:43:19 +00:00
uye
f0e878f291 fix: minitouch 触控 wait ms 2025-09-15 22:38:35 +08:00
MistEO
2db165a158 ci: fix minitouch scale 2025-09-15 16:43:58 +08:00
MistEO
bec0325236 refactor: use std::format instead of sprintf (#14107)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-09-15 01:07:05 +08:00
MistEO
5c0445d3ab refactor: 依赖大更新 (#13908)
Co-authored-by: status102 <102887808+status102@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-09-08 21:17:47 +08:00
uye
6a58d350c6 chore: mumu 使用 7555 端口时禁用 Index 检测,添加日志警告 2025-08-24 10:49:06 +08:00
uye
f460a03859 chore: 调整 LDExtras 文件位置 2025-08-13 15:35:25 +08:00
uye
c5cb5e744e style: format 2025-08-12 22:23:15 +08:00
uye
cf8257b685 feat: 支持 mumu 5555 端口使用控制台退出与截图增强 2025-08-12 21:53:24 +08:00
uye
7b9e1453ad fix: uuid 与 version 返回错误格式时崩溃
fix #13351
2025-07-25 21:20:29 +08:00
Constrat
2972bbfbe9 style: clang
[skip changelog]
2025-07-12 13:38:47 +02:00
uye
d64f4b3aeb feat: 为截图测试日志添加 Tooltip 展示所有截图方式耗时 2025-07-12 18:56:08 +08:00
SherkeyXD
488c203301 chore: no overflow 2025-07-09 20:33:46 +08:00
uye
66f60a206a ci: ubuntu build 2025-07-09 16:49:56 +08:00
uye
5537684305 feat: 新增雷电专版注册表自动检测,新增雷电端口自动检测 2025-07-09 16:26:49 +08:00
晓丶梦丶仁
cead754a6d fix: Mumu5.0 MumuExtra
mumu5.0 正式版似乎在根目录中留了一个 shell\sdk\xxxx.dll,部分用户反应这个 dll 无法连接模拟器,删除后可用;于是检测路径时优先检测 Mumu5.0 的路径,若不存在再向下兼容
2025-07-03 00:22:21 +08:00
SherkeyXD
13e42a7915 fix: no unsigned subtraction 2025-07-02 19:33:17 +08:00
dantmnf
74182f8479 fix: improper ifstream usage in adblite
https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/12199#issuecomment-2974975943
2025-06-18 16:32:54 +08:00
Constrat
bbc2774115 style: fix clang 2025-06-09 23:17:48 +02:00
uye
c0efe8891a fix: 高分辨率下无法进入特定关卡(虽然修了但还是不建议用 2k 4k 2025-06-09 15:44:42 +08:00
uye
589f7bf99f fix: microseconds -> milliseconds 2025-06-07 16:28:03 +08:00