Skip to content
Merged
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
4 changes: 3 additions & 1 deletion module/device/desktop_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def _desktop_ensure_launched(self) -> bool:
if not hwnd:
hwnd = find_desktop_window_by_pid(
located_pid, desktop_window_titles(self.config))
self.desktop_bind_pid(located_pid, hwnd)
# 认领的是已存在的窗口,可能是用户手动开的且已登录,
# 登录态由 find_desktop_login_popup 实测,不能假设未登录
self.desktop_bind_pid(located_pid, hwnd, just_launched=False)
return True
return self.launch_desktop_client()

Expand Down
4 changes: 3 additions & 1 deletion module/device/handle.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ def __init__(self, config) -> None:
self.root_handle = ''
pid, hwnd = self._desktop_locate_window()
if pid and hwnd:
self.desktop_bind_pid(pid, hwnd)
# 认领的是已存在的窗口,可能早已登录,
# 登录态由 find_desktop_login_popup 实测,不能假设未登录
self.desktop_bind_pid(pid, hwnd, just_launched=False)
logger.info(f'Desktop client window bound: PID={pid}, hwnd={hwnd}')
return
if pid:
Expand Down
14 changes: 11 additions & 3 deletions module/device/method/desktop_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1293,12 +1293,15 @@ def _desktop_locate_window(self):
f'无法区分归属,将重新拉起一个实例')
return None, 0

def desktop_bind_pid(self, pid, hwnd=0) -> None:
def desktop_bind_pid(self, pid, hwnd=0, just_launched=True) -> None:
"""把运行时 PID/HWND 绑定到实例;PID 不写配置,只进档案状态文件。

PID 每次重启都会变,落配置只会认领一个死进程;配了档案目录时写
oas_instance.json 供下次认领。同时强制显示器常亮,防止电源超时关屏导致
BitBlt 截图返回黑帧卡死登录循环。

just_launched=True 表示窗口是本轮新拉起的,必然未登录;
False 表示认领已存在的窗口,登录态由 find_desktop_login_popup() 实测判断。
"""
self.root_handle = str(pid)
if hwnd:
Expand All @@ -1320,8 +1323,13 @@ def desktop_bind_pid(self, pid, hwnd=0) -> None:
self.is_desktop_window = True
# 换了新 hwnd,截图句柄缓存必须同步失效,否则截图仍走上一个客户端的废句柄
self._desktop_clear_handle_cache()
# 新绑定的客户端刚启动,必然未登录,需先走 restart 登录流程
self._desktop_login_done = False
if just_launched:
# 新拉起的客户端必然停在登录页
self._desktop_login_done = False
else:
# 认领已存在的窗口:可能早已登录。已登录的客户端全系统枚举不到
# MPAY_LOGIN 顶层窗口(见 find_desktop_login_popup),以此区分
self._desktop_login_done = not self.find_desktop_login_popup()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 中等 · 任务在登录前运行

如果在现有客户端窗口的 MPay 登录弹窗出现之前就认领该窗口,desktop_bind_pid 会根据初始弹窗扫描结果将 _desktop_login_done 设为 true,而 app_is_running 会信任这个缓存标志。调度器因此跳过 Restart 的登录流程,并将游戏任务发送到加载界面或登录界面。

对于新认领的客户端,在确认其登录状态之前,应保持 _desktop_login_done 为 false,这样如果弹窗在初始扫描后才出现,登录流程仍会运行。

另见 module/device/desktop_device.py:42、module/device/handle.py:187。

面向 AI 智能体的提示
在 `module/device/method/desktop_impl.py` 第 1332 行:

**任务在登录前运行**

如果在现有客户端窗口的 MPay 登录弹窗出现之前就认领该窗口,`desktop_bind_pid` 会根据初始弹窗扫描结果将 `_desktop_login_done` 设为 true,而 `app_is_running` 会信任这个缓存标志。调度器因此跳过 Restart 的登录流程,并将游戏任务发送到加载界面或登录界面。

对于新认领的客户端,在确认其登录状态之前,应保持 `_desktop_login_done` 为 false,这样如果弹窗在初始扫描后才出现,登录流程仍会运行。

另见 `module/device/desktop_device.py:42`、`module/device/handle.py:187`。
Original comment in English

🟡 Medium · Tasks run before login

When an existing client window is claimed before its MPay login popup appears, desktop_bind_pid sets _desktop_login_done to true based on the initial popup scan, and app_is_running trusts that cached flag. The scheduler skips Restart’s login flow and sends gameplay tasks to the loading or login screen.

Keep _desktop_login_done false for a newly claimed client until its login state is confirmed, so the login flow runs if the popup appears after the initial scan.

Also at module/device/desktop_device.py:42, module/device/handle.py:187.

Prompt for AI agents
In `module/device/method/desktop_impl.py` at line 1332:

**Tasks run before login**

When an existing client window is claimed before its MPay login popup appears, `desktop_bind_pid` sets `_desktop_login_done` to true based on the initial popup scan, and `app_is_running` trusts that cached flag. The scheduler skips Restart’s login flow and sends gameplay tasks to the loading or login screen.

Keep `_desktop_login_done` false for a newly claimed client until its login state is confirmed, so the login flow runs if the popup appears after the initial scan.

Also at `module/device/desktop_device.py:42`, `module/device/handle.py:187`.

# 客户端已拉起,强制保持显示器常亮(线程级状态,随客户端关闭解除)
desktop_keep_screen_on(True)
# 档案状态文件:下次启动凭它认领「这个档案的实例是哪个 PID」
Expand Down
9 changes: 8 additions & 1 deletion script.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,14 @@ def run(self, command: str) -> bool:
return False

try:
self.device.screenshot()
try:
self.device.screenshot()
except GameNotRunningError:
# 桌面版窗口不在时预截图必抛;Restart 的职责就是拉起客户端,
# 不能让它被这层拦住(实测恢复循环里 Restart 永远跑不到)
if command != 'Restart':
raise
logger.info('Client window gone, skip pre-screenshot and let Restart recover it')
module_name = 'script_task'
module_path = str(Path.cwd() / 'tasks' / command / (module_name+'.py'))
logger.info(f'module_path: {module_path}, module_name: {module_name}')
Expand Down
Loading