feat: 日常任务启动前增加画面预检
每天的首次云会话可能停在不可识别画面约 10 分钟(游戏做每日首登资源处理),MAA 在 每个任务上识别失败并烧掉当次尝试的额度(2026-09-23/09-24 连续两天实测,各损失整天 或近半天进度)。包装脚本现在在 /start 后、运行 MAA 前先观察画面:按截图体积分类 (黑屏 <20KB / 登录 200-350KB / 标题 ~480KB / 公告与主界面 >=650KB),对标题屏与 登录屏自动点按推进,出现可识别画面才交给 MAA;超时(默认 13 分钟)则放弃本轮、 释放会话并重试。分类阈值标定于真实截图样本,可调。
This commit is contained in:
@@ -166,6 +166,13 @@ sudo bin/maa-online-install-systemd
|
||||
`StartUp` 永不跳过,因为每次 `/start` 都是全新的云实例。详见
|
||||
[`user_config/README.md`](user_config/README.md#自动定时与断点续跑)。
|
||||
|
||||
**启动预检**:每次 `/start` 之后、交给 MAA 之前,包装脚本会先观察游戏画面(默认最多 13 分钟,
|
||||
`MAA_ONLINE_PREFLIGHT_MAX_SECONDS` 可调):按截图体积分类画面状态(黑屏加载 / 登录屏 /
|
||||
标题屏 / 公告与主界面),对标题屏和登录屏自动点按推进,直到出现可识别的游戏画面才启动 MAA。
|
||||
每天的首次会话可能停在不可识别画面约 10 分钟(游戏在做每日首登的资源处理),没有这一步时
|
||||
MAA 会在每个任务上识别失败,整次尝试的额度被白白烧掉(2026-09-23 与 09-24 连续两天实测)。
|
||||
分类阈值标定在 `bin/maa-online-daily-run` 的注释里,游戏画面改版后可按需调整。
|
||||
|
||||
查看运行情况:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
# MAA Online daily runner, designed for systemd oneshot execution.
|
||||
#
|
||||
# Flow: wait for the cloud API -> plan remaining tasks from earlier attempts ->
|
||||
# start a cloud session -> run the daily task file under a stall watchdog ->
|
||||
# on failure release the cloud session and retry (up to MAA_ONLINE_MAX_ATTEMPTS).
|
||||
# start a cloud session -> preflight (watch the screen, tap through title/login
|
||||
# until the game is recognizable -- the first session of the day may process
|
||||
# resources for ~10 min on a screen MAA cannot recognize) -> run the daily task
|
||||
# file under a stall watchdog -> on failure release the cloud session and
|
||||
# retry (up to MAA_ONLINE_MAX_ATTEMPTS).
|
||||
#
|
||||
# Retries use resume mode: bin/maa-resume-tasks.py inspects the earlier
|
||||
# attempts' logs and only the tasks that never reported "<Type> Completed" are
|
||||
@@ -26,6 +29,12 @@ RESUME_FILE="${MAA_ONLINE_RESUME_OUT:-$ROOT/config/tasks/daily-resume.toml}"
|
||||
MAX_ATTEMPTS="${MAA_ONLINE_MAX_ATTEMPTS:-3}"
|
||||
STALL_FAILS="${MAA_ONLINE_STALL_FAILS:-12}" # consecutive ScreencapFailed (~24s+)
|
||||
ATTEMPT_MAX_SECONDS="${MAA_ONLINE_ATTEMPT_MAX_SECONDS:-3000}" # per-attempt ceiling
|
||||
# Pre-flight: after /start, watch the game screen and tap through the boot flow
|
||||
# (title -> login -> loading) until a recognizable in-game screen appears.
|
||||
# The first session of the day can sit on an unrecognizable screen for ~10 min
|
||||
# while the game does first-login resource processing; without this, MAA burns
|
||||
# the whole attempt (and its quota) failing recognition on every task.
|
||||
PREFLIGHT_MAX_SECONDS="${MAA_ONLINE_PREFLIGHT_MAX_SECONDS:-780}"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
@@ -87,6 +96,37 @@ if [ -n "${MAA_ONLINE_RESUME_FROM:-}" ]; then
|
||||
echo "[daily-run] seeded resume planning with: ${attempt_logs[*]}"
|
||||
fi
|
||||
|
||||
# Screen classes by PNG size, calibrated on real captures (2026-09-23):
|
||||
# black/loading <20KB | login 200-350KB (开始唤醒) | title ~480KB |
|
||||
# announcement/menu >=650KB (hand off to MAA)
|
||||
preflight() { # arg1: max seconds; returns 0 once the screen looks in-game
|
||||
local max="$1" elapsed=0 last_action=-20
|
||||
local png=/tmp/maa-preflight.png
|
||||
while [ "$elapsed" -lt "$max" ]; do
|
||||
if api GET /screencap > "$png" 2>/dev/null; then
|
||||
local size
|
||||
size=$(stat -c %s "$png" 2>/dev/null || echo 0)
|
||||
if [ "$size" -ge 650000 ]; then
|
||||
echo "[daily-run] preflight: ready after ${elapsed}s (screen ${size}B, in-game class)"
|
||||
return 0
|
||||
fi
|
||||
if [ $((elapsed - last_action)) -ge 12 ]; then
|
||||
if [ "$size" -ge 200000 ] && [ "$size" -lt 350000 ]; then
|
||||
api POST /click '{"x":640,"y":510}' >/dev/null 2>&1
|
||||
echo "[daily-run] preflight: ${elapsed}s login-class screen (${size}B); tapping 开始唤醒"
|
||||
else
|
||||
api POST /click '{"x":640,"y":360}' >/dev/null 2>&1
|
||||
echo "[daily-run] preflight: ${elapsed}s screen ${size}B; tapping center"
|
||||
fi
|
||||
last_action=$elapsed
|
||||
fi
|
||||
fi
|
||||
sleep 6
|
||||
elapsed=$((elapsed + 6))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
plan_tasks() { # echoes the task file to run ("daily", "daily-resume" or "NONE")
|
||||
if [ "${#attempt_logs[@]}" -eq 0 ]; then
|
||||
echo daily
|
||||
@@ -124,6 +164,14 @@ while [ "$attempt" -lt "$MAX_ATTEMPTS" ]; do
|
||||
sleep 20
|
||||
continue
|
||||
fi
|
||||
echo "[daily-run] attempt $attempt: preflight: waiting for a recognizable game screen (max ${PREFLIGHT_MAX_SECONDS}s) ..."
|
||||
if ! preflight "$PREFLIGHT_MAX_SECONDS"; then
|
||||
REASON="preflight timed out after ${PREFLIGHT_MAX_SECONDS}s; game screen never became ready"
|
||||
echo "[daily-run] attempt $attempt failed: $REASON; releasing cloud session" >&2
|
||||
api POST /exit >/dev/null 2>&1 || true
|
||||
sleep 30
|
||||
continue
|
||||
fi
|
||||
RUN_LOG="$LOG_DIR/maa-daily-$(date +%Y%m%d-%H%M%S).log"
|
||||
echo "[daily-run] attempt $attempt: running '$TASK_FILE' (log: $RUN_LOG) ..."
|
||||
attempt_epoch=$(date +%s)
|
||||
|
||||
Reference in New Issue
Block a user