fix: 修复多实例跨服切换时游戏进程未关闭及不必要的强制退出登录#2273
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough修改实例切换以先比较 game_path 并在路径不同情况下缓存上一个 controller;close_game 基于缓存的 变更实例切换与登录行为调整
Estimated code review effort 🎯 3 (Moderate) | ⏱️ ~20 minutes 可能相关的 PR
建议审核人
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/one_dragon/base/operation/one_dragon_app.py (1)
121-130: 💤 Low value建议记录 taskkill 的 stderr 以便调试。
当
returncode != 0时,除了"进程未找到",还可能是"拒绝访问"等其他错误。记录 stderr 有助于排查问题。♻️ 可选的改进方案
result = subprocess.run( ['taskkill', '/F', '/IM', exe_name], capture_output=True, ) if result.returncode == 0: log.info('关闭游戏成功: %s', exe_name) log.info('等待游戏占用文件释放(10s)...') return self.round_success(wait=10) - log.info('未找到游戏进程 %s,可能已关闭', exe_name) + stderr_msg = result.stderr.decode('gbk', errors='ignore').strip() + if stderr_msg: + log.debug('taskkill stderr: %s', stderr_msg) + log.info('未找到游戏进程 %s,可能已关闭', exe_name) return self.round_success()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/one_dragon/base/operation/one_dragon_app.py` around lines 121 - 130, The taskkill subprocess call in the subprocess.run block (variable result) doesn't log stderr when returncode != 0, making failures like "access denied" hard to debug; update the handling around the subprocess.run call in one_dragon_app.py (the block that uses exe_name, result and calls self.round_success()) to capture and log result.stderr (decode bytes safely or use text=True on subprocess.run) and include it in the log (e.g., log.warning or log.error) when result.returncode != 0 so you record the underlying error message while keeping the existing "process not found" message path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/one_dragon/base/operation/one_dragon_app.py`:
- Around line 121-130: The taskkill subprocess call in the subprocess.run block
(variable result) doesn't log stderr when returncode != 0, making failures like
"access denied" hard to debug; update the handling around the subprocess.run
call in one_dragon_app.py (the block that uses exe_name, result and calls
self.round_success()) to capture and log result.stderr (decode bytes safely or
use text=True on subprocess.run) and include it in the log (e.g., log.warning or
log.error) when result.returncode != 0 so you record the underlying error
message while keeping the existing "process not found" message path.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 84a0a0c3-0194-47c8-b1c5-03659f7c2342
📒 Files selected for processing (2)
src/one_dragon/base/operation/one_dragon_app.pysrc/zzz_od/operation/enter_game/enter_game.py
自审 PASS本地测试通过,场景:国服实例运行完毕后切换至国际服实例(两者游戏路径不同,均未在一条龙中配置账号密码)。 修复一验证:检测到路径不同后,旧国服进程被正确关闭,国际服客户端正常启动,无实例冲突。 修复二验证:进入国际服游戏时未触发退出登录操作,直接点击进入游戏,无账号密码输入步骤。 运行记录(关键片段): |
ShadowLemoon
left a comment
There was a problem hiding this comment.
建议不要在 OneDragonApp 中引入 subprocess/taskkill。#1380 的问题根源是关闭游戏发生在 ctx.switch_instance() 之后,controller 的窗口标题已经被 on_switch_instance() 改成新实例,导致 _last_controller 不能稳定代表旧窗口。
更合适的修复是调整流程:先计算 next_instance_idx 并比较路径;如果路径不同,在调用 ctx.switch_instance(next_idx) 之前使用当前 ctx.controller.close_game() 关闭旧游戏;确认关闭后再切换实例并重新打开游戏。这样 OneDragonApp 仍只做流程编排,关闭细节继续走 ControllerBase.close_game() 抽象,也避免 taskkill /IM 按进程名误杀同名客户端。
切换到不同游戏路径的实例前,先通过当前 controller(窗口标题尚未改变) 关闭旧游戏,确认关闭后再调用 ctx.switch_instance()。 移除 subprocess/taskkill 方案,改回走 ControllerBase.close_game() 抽象。
|
已按照建议重构:在 |
|
这里 不过这里建议不要覆盖 建议改成只在普通进入游戏流程里跳过强制切号: if not switch and not cfg.account and not cfg.password and not cfg.bilibili_account_name:
self.force_login = False |
|
已修复:加上 |
|
还有一个边界场景需要处理:最后一个实例跑完后, 按当前流程,如果最后一个实例切回第一个实例且两者 这样会导致流程结束前额外打开第一个实例的游戏。建议在 @node_from(from_name='关闭游戏')
@operation_node(name='切换账号重新打开游戏', screenshot_before_round=False)
def after_close_game(self) -> OperationRoundResult:
self.ctx.switch_instance(self._instance_list[self._instance_idx].idx)
log.info('下一个实例 %s', self.ctx.one_dragon_config.current_active_instance.name)
if self._instance_idx == self._instance_start_idx:
return self.round_success()
if self.op_to_enter_game is None:
return self.round_fail('未提供打开游戏方式')
return self.round_by_op_result(self.op_to_enter_game.execute())这样最后一个实例结束时仍会恢复到起始实例配置,但不会多启动一次游戏。 |
|
已修复:在 |
| self.ctx.switch_instance(self._instance_list[self._instance_idx].idx) | ||
| log.info('下一个实例 %s', self.ctx.one_dragon_config.current_active_instance.name) | ||
| current_game_path = self.ctx.game_account_config.game_path | ||
| next_game_path = GameAccountConfig(next_instance.idx).game_path |
There was a problem hiding this comment.
补充一下:更建议不要把这个判断放到 ctx,也不要做成 GameAccountConfig 实例方法里再实例化另一个 GameAccountConfig。可以在 GameAccountConfig 上提供类方法,比如 GameAccountConfig.is_different_game_path(current_idx, next_idx),由配置类集中读取两个实例的 game_path 并比较。这样 OneDragonApp 只做流程编排,不直接实例化目标实例配置,也不需要提前 ctx.switch_instance() 触发 reload/on_switch_instance 的副作用。
There was a problem hiding this comment.
已按补充建议调整并推送到本 PR:
- 在
GameAccountConfig上新增is_different_game_path(current_idx, next_idx)类方法,由配置类集中读取两个实例的game_path并比较。 OneDragonApp.switch_instance()现在只传当前/下一个实例 idx 并调用该类方法,不再直接实例化目标实例配置,也不需要提前ctx.switch_instance()。
对应提交:4f1bea2
| current_instance = self._instance_list[self._instance_idx] | ||
| next_instance = self._instance_list[next_idx] | ||
|
|
||
| if GameAccountConfig.is_different_game_path(current_instance.idx, next_instance.idx): |
背景
多账号一条龙中,国服与国际服账号各只有一个时,切换服务器后存在两个问题。
问题一:切换到不同路径实例时旧游戏客户端未关闭
现象:国服账号运行完毕后,切换到国际服实例时旧国服客户端没有被关闭,导致国际服客户端无法启动(实例冲突),一条龙卡住。
根本原因:\ctx.switch_instance()\ 内部的 \on_switch_instance()\ 会修改 \controller.game_win.win_title\,而 _last_controller\ 和 \ctx.controller\ 指向同一对象。导致 \close_game()\ 节点用新服务器的窗口标题查找旧游戏窗口,找不到后误判为已关闭,跳过了关闭步骤。
修复:切换前保存旧游戏路径,\close_game()\ 改为通过 \ askkill /F /IM \ 按进程名强制关闭,绕开窗口标题问题。对 exe 名做合法性校验防止注入。
问题二:未配置账号密码时仍强制退出当前登录
现象:多实例模式下 \orce_login\ 固定为 \True\,每次进入游戏都会强制点击「切换账号」退出当前登录,再用存储的账号密码重新登录。对于未在一条龙中配置账号密码、依赖游戏自动保存登录状态的实例,这一操作是多余的,且在国际服会触发人机验证(验证码),导致自动化流程中断。
根本原因:\EnterGame.init()\ 中 \orce_login\ 只检查实例数量,未区分「同服多账号(需退出重登)」和「未配置密码(依赖游戏保存登录状态)」两种情况。
修复:若当前实例的账号、密码、B服用户名均未配置,则将 \orce_login\ 置为 \False\,直接利用游戏保存的登录状态进入。不影响已配置账号密码的用户。
Summary by CodeRabbit
发布说明