feat: implement handling for pending-update download notice during login
This commit is contained in:
parent
a690e90478
commit
ff240cb104
@ -1588,6 +1588,36 @@ LOGIN_LOADING_BUFFER_TOLERANCE = 12
|
|||||||
# reason to wait the full 4 minutes once it's confidently identified.
|
# reason to wait the full 4 minutes once it's confidently identified.
|
||||||
LOGIN_LOADING_BUFFER_STUCK_SECONDS = 60
|
LOGIN_LOADING_BUFFER_STUCK_SECONDS = 60
|
||||||
|
|
||||||
|
# Pending-update / additional-data download notice -- a real "お知らせ"
|
||||||
|
# (Notice) dialog that can appear immediately after launch, well before the
|
||||||
|
# title screen, reading "必要なコンポーネントをダウンロードします。" with a
|
||||||
|
# "容量"/MB size box and キャンセル/OK buttons. This is the same notice
|
||||||
|
# class as the reference's own main_page_game-download-resource-notice[23]/
|
||||||
|
# main_page_download-additional-resources img_reactions in
|
||||||
|
# core/Baas_thread.py::to_main_page, which the reference answers with a
|
||||||
|
# fixed-coordinate click on a matched image template. This project has no
|
||||||
|
# template asset for it, so OCR on the body text is used instead (matching
|
||||||
|
# CLAUDE.md's OCR policy) -- see login.py's module docstring for the full
|
||||||
|
# live-incident writeup (a real q4h cron run got stuck here 2026-08-04
|
||||||
|
# because this dialog previously fell through to the generic Enter
|
||||||
|
# fallback, which does NOT dismiss it, unlike every other one-off notice
|
||||||
|
# this flow has hit so far).
|
||||||
|
#
|
||||||
|
# Live-captured on nik-gpu at the native 1920x1200
|
||||||
|
# (scratchpad/login_probe_01.png). LOGIN_UPDATE_DIALOG_TEXT_RECT crops the
|
||||||
|
# dialog's own body line; lang="jpn" OCR read it back as
|
||||||
|
# "必要なコンボーネントをダウンロードします。" (a minor ポ->ボ misread,
|
||||||
|
# harmless -- LOGIN_UPDATE_DIALOG_KEYWORD only checks for the substring
|
||||||
|
# "ダウンロード", which read back correctly). LOGIN_UPDATE_OK_BUTTON is a
|
||||||
|
# point on the OK button's own bright-cyan fill (confirmed via a pixel scan
|
||||||
|
# across the button's full width, (107,216,253)-(122,222,255) throughout
|
||||||
|
# except two x-columns landing on the dark-navy "OK" glyph strokes
|
||||||
|
# themselves) -- deliberately not the button's visual center, to stay clear
|
||||||
|
# of the text.
|
||||||
|
LOGIN_UPDATE_DIALOG_TEXT_RECT = (600, 420, 1320, 465)
|
||||||
|
LOGIN_UPDATE_DIALOG_KEYWORD = "ダウンロード"
|
||||||
|
LOGIN_UPDATE_OK_BUTTON = (1040, 851)
|
||||||
|
|
||||||
# Battle Pass (バトルパス) -- ported from baas-reference's
|
# Battle Pass (バトルパス) -- ported from baas-reference's
|
||||||
# module/collect_pass_reward.py. See ba_auto/tasks/battle_pass.py's module
|
# module/collect_pass_reward.py. See ba_auto/tasks/battle_pass.py's module
|
||||||
# docstring for the full reference-mapping writeup; constants below were
|
# docstring for the full reference-mapping writeup; constants below were
|
||||||
|
|||||||
@ -244,10 +244,66 @@ live-confirmed against a fresh real stuck instance recovering via this
|
|||||||
faster path specifically (the instance that prompted this fix had already
|
faster path specifically (the instance that prompted this fix had already
|
||||||
recovered via the pre-existing generic-timeout path by the time the fix was
|
recovered via the pre-existing generic-timeout path by the time the fix was
|
||||||
written).
|
written).
|
||||||
|
|
||||||
|
**Pending-update download notice, `_update_dialog_open` (2026-08-04)**: the
|
||||||
|
user reported "the game contains a pending update... login doesn't adapt to
|
||||||
|
the popup," and a real q4h cron run that same day (17:00 JST) confirmed it
|
||||||
|
first-hand -- `~/ba_logs/q4h.log` showed `[login] not home after the
|
||||||
|
timeout -- killing and relaunching the game` followed by `could not reach
|
||||||
|
home after 2 attempts`, then every subsequent task (cafe, event_sweep, ...)
|
||||||
|
failed to open its own screen, the same cascading-failure shape as the
|
||||||
|
2026-07-17 session-death incident above but with a different root cause.
|
||||||
|
By the time this was investigated the game had been fully killed (exhausted
|
||||||
|
relaunch budget), so it was relaunched deliberately to reproduce the state
|
||||||
|
live rather than guessed at. A real "お知らせ" (Notice) dialog appeared
|
||||||
|
immediately after launch, well before the title screen, reading
|
||||||
|
"必要なコンポーネントをダウンロードします。" with a "容量"/MB size box and
|
||||||
|
キャンセル/OK buttons (`scratchpad/login_probe_01.png`) -- this is the same
|
||||||
|
notice class as the reference's own `main_page_game-download-resource-
|
||||||
|
notice[23]`/`main_page_download-additional-resources` img_reactions in
|
||||||
|
`core/Baas_thread.py::to_main_page`, which the reference answers with a
|
||||||
|
fixed-coordinate click on a matched image template; this project has no
|
||||||
|
template asset for it, so OCR on the body text is used instead (matching
|
||||||
|
CLAUDE.md's OCR policy of porting the reference's actual state-detection
|
||||||
|
approach rather than substituting a pixel-probe/coordinate guess). This
|
||||||
|
dialog previously fell through to the generic Enter-fallback branch like
|
||||||
|
every other unrecognized state -- confirmed live that Enter does NOT
|
||||||
|
dismiss it, unlike every other one-off notice this flow has hit so far,
|
||||||
|
which is exactly why the stuck cron run never resolved within
|
||||||
|
`LOGIN_TIMEOUT_SECONDS` and had to be killed and relaunched, only to hit
|
||||||
|
the same still-pending update again on relaunch. Fixed with
|
||||||
|
`_update_dialog_open`, an OCR check (`config.LOGIN_UPDATE_DIALOG_TEXT_RECT`,
|
||||||
|
lang="jpn") for the "ダウンロード" keyword in the dialog body, gating a
|
||||||
|
real click on `config.LOGIN_UPDATE_OK_BUTTON` -- inserted into
|
||||||
|
`_wait_for_home`'s existing dispatch chain gated on `_logo_state` reading
|
||||||
|
"absent" (rather than as its own check on every iteration), since it can
|
||||||
|
only matter on the exact branch that was getting stuck; "bright"/"dimmed"
|
||||||
|
are already known to be the plain title screen. Clicking OK triggers a real
|
||||||
|
~230MB download with its own progress readout, then a "ゲームデータを
|
||||||
|
イニシャライズしています……" (initializing) screen
|
||||||
|
(`scratchpad/login_probe_02.png`/`_03.png`), landing back on the ordinary
|
||||||
|
title screen afterward (`_04.png`) -- no new handling needed for either
|
||||||
|
intermediate screen, since neither has an interactive element and both
|
||||||
|
already fall through harmlessly to the same Enter fallback (a no-op on a
|
||||||
|
non-interactive splash screen) until the title screen's own bright-logo
|
||||||
|
check takes over. **Confirmed live end-to-end twice the same day**: first
|
||||||
|
via direct `xdotool`/`scrot` calls to calibrate the OCR rect/keyword and
|
||||||
|
button coordinate against the real dialog before writing any code, then
|
||||||
|
again for real via the actual deployed code path -- after this fix was
|
||||||
|
rsynced to nik-gpu, a real `./ba_dailies.sh login` run hit an unrelated
|
||||||
|
stuck-loading-buffer timeout, killed and relaunched the game, and the
|
||||||
|
still-pending update notice reappeared on that fresh relaunch. The log
|
||||||
|
shows it plainly: `[login] update/download notice detected -- clicking OK`
|
||||||
|
immediately followed by `[login] reached home`, and a follow-up screenshot
|
||||||
|
confirmed a genuine, fully-interactive home screen (full HUD: AP, credits,
|
||||||
|
gems, nav bar). Both the detection (`_update_dialog_open`) and the click
|
||||||
|
(`LOGIN_UPDATE_OK_BUTTON`) fired correctly on their first real,
|
||||||
|
un-rehearsed encounter through the normal dispatch path -- not just the
|
||||||
|
manual walkthrough used to calibrate them.
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from ba_auto import navigation
|
from ba_auto import detector, navigation
|
||||||
|
|
||||||
|
|
||||||
def _color_matches(rgb, target, tolerance):
|
def _color_matches(rgb, target, tolerance):
|
||||||
@ -281,6 +337,18 @@ def _loading_buffer_visible(driver, config):
|
|||||||
return all(_color_matches(c, config.LOGIN_LOADING_BUFFER_RGB, config.LOGIN_LOADING_BUFFER_TOLERANCE) for c in colors)
|
return all(_color_matches(c, config.LOGIN_LOADING_BUFFER_RGB, config.LOGIN_LOADING_BUFFER_TOLERANCE) for c in colors)
|
||||||
|
|
||||||
|
|
||||||
|
def _update_dialog_open(driver, config):
|
||||||
|
"""Positive OCR check for the pending-update/additional-data download
|
||||||
|
notice (see config.py's LOGIN_UPDATE_DIALOG_* comment for the full
|
||||||
|
live-incident writeup and calibration). Only called when _logo_state
|
||||||
|
reads "absent" -- "bright"/"dimmed" are already known to be the plain
|
||||||
|
title screen, not this dialog, so there's no reason to pay for an OCR
|
||||||
|
call on every single poll.
|
||||||
|
"""
|
||||||
|
text = detector.read_text(config.LOGIN_UPDATE_DIALOG_TEXT_RECT, psm=6, lang="jpn")
|
||||||
|
return config.LOGIN_UPDATE_DIALOG_KEYWORD in text
|
||||||
|
|
||||||
|
|
||||||
def _true_home(driver, config):
|
def _true_home(driver, config):
|
||||||
return (
|
return (
|
||||||
navigation.is_home_nav_bar_visible(driver)
|
navigation.is_home_nav_bar_visible(driver)
|
||||||
@ -374,10 +442,23 @@ def _wait_for_home(driver, config):
|
|||||||
return True
|
return True
|
||||||
print("[login] home check didn't hold on re-verification -- treating as a coincidental frame, retrying")
|
print("[login] home check didn't hold on re-verification -- treating as a coincidental frame, retrying")
|
||||||
continue
|
continue
|
||||||
|
logo_state = _logo_state(driver, config)
|
||||||
if _news_dialog_open(driver, config):
|
if _news_dialog_open(driver, config):
|
||||||
driver.click(*config.LOGIN_NEWS_CLOSE_BUTTON)
|
driver.click(*config.LOGIN_NEWS_CLOSE_BUTTON)
|
||||||
elif _logo_state(driver, config) == "bright":
|
elif logo_state == "bright":
|
||||||
driver.click(*config.LOGIN_TOUCH_TO_START)
|
driver.click(*config.LOGIN_TOUCH_TO_START)
|
||||||
|
elif logo_state == "absent" and _update_dialog_open(driver, config):
|
||||||
|
# Pending-update/additional-data download notice -- does NOT
|
||||||
|
# respond to Enter (confirmed live 2026-08-04, see module
|
||||||
|
# docstring), unlike every other dialog in this else-branch, so
|
||||||
|
# it needs its own real button click rather than falling
|
||||||
|
# through to the generic fallback below. Gated on logo_state
|
||||||
|
# == "absent" (rather than checked unconditionally) so the OCR
|
||||||
|
# call is only paid for on the one branch it can actually
|
||||||
|
# matter on -- "bright"/"dimmed" are already known to be the
|
||||||
|
# plain title screen, not this dialog.
|
||||||
|
print("[login] update/download notice detected -- clicking OK")
|
||||||
|
driver.click(*config.LOGIN_UPDATE_OK_BUTTON)
|
||||||
else:
|
else:
|
||||||
# Dimmed title-screen notice, a loading transition, the daily
|
# Dimmed title-screen notice, a loading transition, the daily
|
||||||
# attendance card, an infrequent login-bonus card, or any other
|
# attendance card, an infrequent login-bonus card, or any other
|
||||||
|
|||||||
12
plan.md
12
plan.md
@ -1237,6 +1237,18 @@ Implementation: `ba_auto/config.py` (new `SCRIMMAGE_*` section, kept fully decou
|
|||||||
|
|
||||||
**Not yet exercised**: a day where the date-rotation picks ゲヘナ or ミレニアム specifically (only トリニティ was live-tested, since that's what today's rotation picked), an `inadequate_ticket` outcome (the account had 30 tickets, comfortably more than one MAX sweep needed), and a day where the AP guard actually fires and cancels (the monthly pass was active throughout this session, so `ap_cost != 0` was never really hit) -- all implemented per the same defensive shape confirmed working elsewhere in this project (bounty.py's own `_is_ticket_purchase_prompt`/fail-closed conventions), just not yet observed against real divergent game state.
|
**Not yet exercised**: a day where the date-rotation picks ゲヘナ or ミレニアム specifically (only トリニティ was live-tested, since that's what today's rotation picked), an `inadequate_ticket` outcome (the account had 30 tickets, comfortably more than one MAX sweep needed), and a day where the AP guard actually fires and cancels (the monthly pass was active throughout this session, so `ap_cost != 0` was never really hit) -- all implemented per the same defensive shape confirmed working elsewhere in this project (bounty.py's own `_is_ticket_purchase_prompt`/fail-closed conventions), just not yet observed against real divergent game state.
|
||||||
|
|
||||||
|
### Phase 28: Login pending-update download notice (2026-08-04)
|
||||||
|
|
||||||
|
User report: "Currently the game contain pending update. Login feature currently doesn't adapt to the popup in the login screen to install update." A real `q4h` cron run that same day (17:00 JST) confirmed it independently before any code was touched: `~/ba_logs/q4h.log` showed `[login] not home after the timeout -- killing and relaunching the game` then `could not reach home after 2 attempts`, cascading into cafe/event_sweep/mailbox/stamina all failing to open their own screens for the rest of that run -- the same cascading-failure shape as the 2026-07-17 session-death incident (Phase 18 follow-up #3) but a different root cause.
|
||||||
|
|
||||||
|
By the time this was investigated the game had been fully killed (relaunch budget exhausted). Rather than guess at the popup's shape, the game was deliberately relaunched live on nik-gpu to reproduce it. A real "お知らせ" (Notice) dialog appeared immediately after launch, well before the title screen: "必要なコンポーネントをダウンロードします。" with a "容量"/MB size box and キャンセル/OK buttons (`scratchpad/login_probe_01.png`). Per the reference-first rule, checked `core/Baas_thread.py::to_main_page` first -- this is the exact same notice class as its own `main_page_game-download-resource-notice[23]`/`main_page_download-additional-resources` img_reactions, which the reference answers with a fixed-coordinate click on a matched image template. This project has no template asset for it, so OCR on the dialog's own body text is used instead (`config.LOGIN_UPDATE_DIALOG_TEXT_RECT`, lang="jpn", matching on the substring "ダウンロード"), gating a real click on `config.LOGIN_UPDATE_OK_BUTTON` -- both calibrated directly against the live dialog (OCR read back "必要なコンボーネントをダウンロードします。", a harmless ポ->ボ misread; the button coordinate was chosen from a pixel scan across the OK button's full width to land clear of the "OK" glyph itself).
|
||||||
|
|
||||||
|
Confirmed live that Enter -- this flow's established generic fallback for every other one-off notice (network-error, daily attendance card, etc.) -- does NOT dismiss this dialog, which is exactly why the stuck cron run never resolved within `LOGIN_TIMEOUT_SECONDS` and had to be killed and relaunched, only to hit the same still-pending update again. Fixed with `_update_dialog_open` in `ba_auto/tasks/login.py`, inserted into `_wait_for_home`'s existing dispatch chain gated on `_logo_state` reading `"absent"` (not checked unconditionally every poll) -- clicking OK triggers a real ~230MB download with its own progress readout, then a "ゲームデータをイニシャライズしています……" screen, landing back on the ordinary title screen afterward (`scratchpad/login_probe_02.png`/`_03.png`/`_04.png`); neither intermediate screen needed new handling since both already fall through harmlessly to the existing Enter fallback (a no-op on a non-interactive splash) until the title screen's own bright-logo check takes over.
|
||||||
|
|
||||||
|
`py_compile` passed locally; deployed via the standard `rsync` push.
|
||||||
|
|
||||||
|
**Confirmed live end-to-end twice the same day.** First via direct `xdotool`/`scrot` calibration (not through the actual code path). Then, after deploying, a real `./ba_dailies.sh login` run hit an unrelated stuck-loading-buffer timeout, killed and relaunched the game itself, and the still-pending update notice reappeared on that fresh relaunch -- the log shows `[login] update/download notice detected -- clicking OK` immediately followed by `[login] reached home`, and a follow-up screenshot confirmed a genuine, fully-interactive home screen (full HUD: AP 60/240, credits, gems, nav bar). Both the OCR detection and the button click fired correctly on their first real, un-rehearsed encounter through the normal dispatch path, not just the manual calibration walkthrough.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
### OCR
|
### OCR
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user