返回主界面的界面识别中 添加 差分宇宙4.0#586
Hidden character warning
Conversation
概览本变更为模拟宇宙系统增加了"差分宇宙4.0"的支持,包括新增四个屏幕状态(选择站点卡、选择下一站、愿力满盈、欢愉假面),更新了屏幕坐标检测逻辑,扩展了枚举成员,并调整了退出流程。 变更内容
预估代码审查工作量🎯 3 (中等) | ⏱️ ~20 分钟 诗歌
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/sr_od/operations/back_to_normal_world_plus.py (1)
72-111: 考虑将差分宇宙4.0的选择操作沉淀为 screen_info 配置。当前使用的
Point(...)坐标会通过game2win_pos()自动适配窗口和分辨率,不会因此失效。但这些硬编码坐标对应的按钮(站点卡、下一站、奇迹、面具)在sim_uni.yml中尚无配置,建议补充相关区域定义并使用round_by_click_area替换,便于游戏更新时维护。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sr_od/operations/back_to_normal_world_plus.py` around lines 72 - 111, The hardcoded Point(...) clicks in back_to_normal_world_plus.py for sim_uni_screen_state.ScreenState.SELECT_STATION, SELECT_NEXT_STATION, CHOOSE_WILL_POWER and AHA_MASK should be replaced with area-based clicks backed by sim_uni.yml; add appropriate area definitions (e.g., select_station_area, select_next_station_area, choose_will_power_area, aha_mask_area) to sim_uni.yml and update the handlers in the method (currently using self.ctx.controller.click(Point(...)) and then return self.round_wait(...)) to call the reusable round_by_click_area helper (or equivalent area-click wrapper) with those area names and preserve the existing short sleeps and round_wait calls so behavior and timing remain intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/sr_od/app/sim_uni/operations/sim_uni_exit.py`:
- Around line 73-75: The new attempt to click '差分宇宙-暂离' inside SimUniExit via
round_by_find_and_click_area prematurely returns and skips the settlement flow
(click_confirm()/click_empty()), breaking cases that must finish and claim
rewards; remove or relocate that '差分宇宙-暂离' branch from SimUniExit so SimUniExit
always proceeds through the confirmation and settlement steps (ensure
click_confirm() and click_empty() are executed), and instead implement the
"temporarily leave" behavior in a dedicated fallback like BackToNormalWorldPlus
or add an explicit parameter/mode to SimUniExit (e.g., exit_mode or
leave_vs_settle) so callers such as SimUniRunEliteRouteV2._boss_exit() can
choose the settle path.
In `@src/sr_od/app/sim_uni/sim_uni_const.py`:
- Around line 122-123: The new ANY SimUniLevelType ('ANY') causes a runtime
AttributeError because match_next_level_entry() iterates SimUniLevelTypeEnum and
calls get_template('sim_uni', 'level_type_any') which returns None and then
accesses template.features; update match_next_level_entry() to explicitly skip
the ANY enum (check for the SimUniLevelType name or value, e.g., 'any') before
calling get_template(), or alternatively add the missing template
assets/template/sim_uni/level_type_any/raw.png so get_template(...) returns a
valid template; ensure the fix prevents accessing template.features when
get_template returns None.
In `@src/sr_od/app/sim_uni/sim_uni_screen_state.py`:
- Around line 124-132: The LCS matcher find_best_match_by_lcs returns an index
(0 is a valid match) so the current if checks treat 0 as False and miss matches
at titles[0]; update each conditional that calls find_best_match_by_lcs (for
ScreenState.SELECT_STATION.value, SELECT_NEXT_STATION.value,
CHOOSE_WILL_POWER.value, AHA_MASK.value) to check explicitly for None (e.g.,
result is not None) or compare against None, ensuring a returned 0 is treated as
a successful match and the corresponding ScreenState is returned.
- Around line 52-57: get_level_type currently only uses strict substring
matching (targets = [i for i, w in enumerate(target_list) if w in region_name])
which returns None on small OCR errors; change it to first attempt the strict
substring check and if no targets are found, fall back to the original
fuzzy/approximate matching logic to pick the best candidate. Specifically, keep
the existing substring pass using target_list, region_name and level_type_list;
if that yields no result, run the prior fuzzy matching (e.g.,
difflib.SequenceMatcher or the previous similarity function) across target_list
vs region_name to pick the highest-scoring index above a sensible threshold
(e.g., 0.6) and return level_type_list for that index, otherwise return None.
Ensure you update get_level_type and reuse the same variables (targets,
target_list, region_name, level_type_list) so callers are unaffected.
---
Nitpick comments:
In `@src/sr_od/operations/back_to_normal_world_plus.py`:
- Around line 72-111: The hardcoded Point(...) clicks in
back_to_normal_world_plus.py for
sim_uni_screen_state.ScreenState.SELECT_STATION, SELECT_NEXT_STATION,
CHOOSE_WILL_POWER and AHA_MASK should be replaced with area-based clicks backed
by sim_uni.yml; add appropriate area definitions (e.g., select_station_area,
select_next_station_area, choose_will_power_area, aha_mask_area) to sim_uni.yml
and update the handlers in the method (currently using
self.ctx.controller.click(Point(...)) and then return self.round_wait(...)) to
call the reusable round_by_click_area helper (or equivalent area-click wrapper)
with those area names and preserve the existing short sleeps and round_wait
calls so behavior and timing remain intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5a64c10d-336a-4195-bde8-4ee8b65dca81
📒 Files selected for processing (5)
assets/game_data/screen_info/sim_uni.ymlsrc/sr_od/app/sim_uni/operations/sim_uni_exit.pysrc/sr_od/app/sim_uni/sim_uni_const.pysrc/sr_od/app/sim_uni/sim_uni_screen_state.pysrc/sr_od/operations/back_to_normal_world_plus.py
| result = self.round_by_find_and_click_area(screen, '模拟宇宙', '差分宇宙-暂离') | ||
| if result.is_success: | ||
| return self.round_success(result.status) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 期望:如果 SimUniExit 被非“返回主界面”路径调用,需要保留结束结算语义,不应默认点击“暂离”。
rg -n -C4 "SimUniExit\\(|sim_uni_exit\\("Repository: OneDragon-Anything/StarRailOneDragon
Length of output: 5403
🏁 Script executed:
cat -n src/sr_od/app/sim_uni/operations/sim_uni_exit.pyRepository: OneDragon-Anything/StarRailOneDragon
Length of output: 5108
🏁 Script executed:
# Check the context of each SimUniExit call to understand their requirements
echo "=== BackToNormalWorldPlus usage ==="
sed -n '30,50p' src/sr_od/operations/back_to_normal_world_plus.py
echo ""
echo "=== SimUniApp usage ==="
sed -n '165,175p' src/sr_od/app/sim_uni/sim_uni_app.py
echo ""
echo "=== SimUniRunEliteRouteV2 usage ==="
sed -n '220,230p' src/sr_od/app/sim_uni/operations/move_v2/sim_uni_run_elite_route_v2.py
echo ""
echo "=== SimUniRunWorld usage ==="
sed -n '65,73p' src/sr_od/app/sim_uni/operations/auto_run/sim_uni_run_world.pyRepository: OneDragon-Anything/StarRailOneDragon
Length of output: 2012
🏁 Script executed:
# Check if there are any specific comments or documentation about "暂离" (temporary leave)
rg -n "暂离" src/sr_od/app/sim_uni/operations/sim_uni_exit.py -B2 -A2
# Also check what UI areas are defined for "暂离"
rg -n "差分宇宙-暂离" --type yaml --type json --type txtRepository: OneDragon-Anything/StarRailOneDragon
Length of output: 360
不应在"结束并结算"操作中优先尝试"暂离"。
SimUniExit 的明确语义是"结束并结算"。新增的"差分宇宙-暂离"点击(第73-75行)如果成功会直接返回,绕过后续的确认对话框(click_confirm())和结算画面(click_empty()),导致跳过整个结算流程。这在需要真正结算获得奖励的场景(如 SimUniRunEliteRouteV2._boss_exit() 战胜首领后)会造成功能破坏。
建议:将"暂离"逻辑移至 BackToNormalWorldPlus 等专门处理返回的兜底操作,或为 SimUniExit 增加显式参数区分"暂离"和"结算"两种模式。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/sr_od/app/sim_uni/operations/sim_uni_exit.py` around lines 73 - 75, The
new attempt to click '差分宇宙-暂离' inside SimUniExit via
round_by_find_and_click_area prematurely returns and skips the settlement flow
(click_confirm()/click_empty()), breaking cases that must finish and claim
rewards; remove or relocate that '差分宇宙-暂离' branch from SimUniExit so SimUniExit
always proceeds through the confirmation and settlement steps (ensure
click_confirm() and click_empty() are executed), and instead implement the
"temporarily leave" behavior in a dedicated fallback like BackToNormalWorldPlus
or add an explicit parameter/mode to SimUniExit (e.g., exit_mode or
leave_vs_settle) so callers such as SimUniRunEliteRouteV2._boss_exit() can
choose the settle path.
| # 在差分宇宙4.0中的识别标志 | ||
| ANY = SimUniLevelType('any', '位面') |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 期望:能找到 level_type_any 模板,或能看到 match_next_level_entry 对 ANY 的显式跳过逻辑。
fd -i 'level_type_any'
rg -n -C4 "get_template\\('sim_uni', level_type\\.template_id\\)|SimUniLevelTypeEnum\\.ANY|type_id == 'any'|level_type\\.type_id == 'any'"Repository: OneDragon-Anything/StarRailOneDragon
Length of output: 880
🏁 Script executed:
# 查看 SimUniLevelType 类定义,确认 template_id 如何生成
rg -n "class SimUniLevelType|def template_id|self.template_id" src/sr_od/app/sim_uni/sim_uni_const.py -A 3
# 查看 match_next_level_entry 的完整实现,包括异常处理
rg -n "def match_next_level_entry" src/sr_od/app/sim_uni/ -A 30
# 查看模板目录中存在的 level_type 模板
find . -type f -name "level_type_*" 2>/dev/null | head -20Repository: OneDragon-Anything/StarRailOneDragon
Length of output: 3124
🏁 Script executed:
# 查看 template_loader.get_template 的实现
rg -n "def get_template|raise|except|None" src/sr_od/ -B 2 -A 10 | grep -A 15 "def get_template"
# 查看 match_next_level_entry 是否被 try-except 包裹
rg -n "match_next_level_entry" src/sr_od/ -B 5 -A 35 | grep -E "(try|except|raise)"
# 查看实际存在的 level_type_* 模板文件
find . -path "*/sim_uni*" -type f -name "level_type_*" 2>/dev/null
find . -type d -name "sim_uni" 2>/dev/nullRepository: OneDragon-Anything/StarRailOneDragon
Length of output: 4205
🏁 Script executed:
# 列出 sim_uni 模板目录中的所有文件
find ./assets/template/sim_uni -type f 2>/dev/null | sort
# 查看 template_loader.get_template 的实现
rg -n "def get_template" src/ -A 15
# 检查 ANY 枚举在其他文件中的使用情况
rg "SimUniLevelTypeEnum.ANY|type_id == 'any'|ANY" src/sr_od/app/sim_uni/ -nRepository: OneDragon-Anything/StarRailOneDragon
Length of output: 21976
🏁 Script executed:
# 查看 load_template 的完整实现
rg -n "def load_template" src/one_dragon/base/screen/template_loader.py -A 20Repository: OneDragon-Anything/StarRailOneDragon
Length of output: 862
🏁 Script executed:
# 查看 get_template 的完整实现
rg -n "def get_template" src/one_dragon/base/screen/template_loader.py -A 12 | head -30
# 查看 is_template_existed 的实现
rg -n "def is_template_existed" src/one_dragon/base/screen/ -A 5Repository: OneDragon-Anything/StarRailOneDragon
Length of output: 1464
新增的 ANY 枚举会导致运行时崩溃,因为缺失对应的 level_type_any 模板。
在 match_next_level_entry() 中,代码遍历所有 SimUniLevelTypeEnum 并尝试加载对应的模板。当迭代到新增的 ANY 枚举时,会调用 get_template('sim_uni', 'level_type_any'),但该模板不存在。此时 get_template() 返回 None,后续访问 template.features 会触发 AttributeError。
需要以下其中一种方案:
- 在
match_next_level_entry()中跳过ANY枚举,或 - 为
level_type_any添加对应的模板文件(assets/template/sim_uni/level_type_any/raw.png)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/sr_od/app/sim_uni/sim_uni_const.py` around lines 122 - 123, The new ANY
SimUniLevelType ('ANY') causes a runtime AttributeError because
match_next_level_entry() iterates SimUniLevelTypeEnum and calls
get_template('sim_uni', 'level_type_any') which returns None and then accesses
template.features; update match_next_level_entry() to explicitly skip the ANY
enum (check for the SimUniLevelType name or value, e.g., 'any') before calling
get_template(), or alternatively add the missing template
assets/template/sim_uni/level_type_any/raw.png so get_template(...) returns a
valid template; ensure the fix prevents accessing template.features when
get_template returns None.
| targets = [i for i, w in enumerate(target_list) if w in region_name] | ||
|
|
||
| if target_idx is None or target_idx < 0: | ||
| if len(targets) == 0: | ||
| return None | ||
| else: | ||
| return level_type_list[target_idx] | ||
| return level_type_list[targets[0]] |
There was a problem hiding this comment.
保留 OCR 模糊匹配兜底。
这里从模糊匹配改成严格子串包含后,只要 OCR 出现空格、漏字或错字,get_level_type() 就会返回 None,下游返回大世界逻辑会误判不在模拟宇宙。建议先做子串命中,再 fallback 到原来的近似匹配。
建议调整
- targets = [i for i, w in enumerate(target_list) if w in region_name]
-
- if len(targets) == 0:
- return None
- else:
- return level_type_list[targets[0]]
+ targets = [i for i, w in enumerate(target_list) if w in region_name]
+ if len(targets) > 0:
+ return level_type_list[targets[0]]
+
+ idx = str_utils.find_best_match_by_difflib(region_name, target_list)
+ if idx is None:
+ return None
+ return level_type_list[idx]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/sr_od/app/sim_uni/sim_uni_screen_state.py` around lines 52 - 57,
get_level_type currently only uses strict substring matching (targets = [i for
i, w in enumerate(target_list) if w in region_name]) which returns None on small
OCR errors; change it to first attempt the strict substring check and if no
targets are found, fall back to the original fuzzy/approximate matching logic to
pick the best candidate. Specifically, keep the existing substring pass using
target_list, region_name and level_type_list; if that yields no result, run the
prior fuzzy matching (e.g., difflib.SequenceMatcher or the previous similarity
function) across target_list vs region_name to pick the highest-scoring index
above a sensible threshold (e.g., 0.6) and return level_type_list for that
index, otherwise return None. Ensure you update get_level_type and reuse the
same variables (targets, target_list, region_name, level_type_list) so callers
are unaffected.
| # region 差分宇宙4.0 | ||
| if str_utils.find_best_match_by_lcs(ScreenState.SELECT_STATION.value, titles, lcs_percent_threshold=1): | ||
| return ScreenState.SELECT_STATION.value | ||
| if str_utils.find_best_match_by_lcs(ScreenState.SELECT_NEXT_STATION.value, titles, lcs_percent_threshold=1): | ||
| return ScreenState.SELECT_NEXT_STATION.value | ||
| if str_utils.find_best_match_by_lcs(ScreenState.CHOOSE_WILL_POWER.value, titles, lcs_percent_threshold=0.7): | ||
| return ScreenState.CHOOSE_WILL_POWER.value | ||
| if str_utils.find_best_match_by_lcs(ScreenState.AHA_MASK.value, titles, lcs_percent_threshold=0.7): | ||
| return ScreenState.AHA_MASK.value |
There was a problem hiding this comment.
修正 LCS 返回下标为 0 时识别失败的问题。
find_best_match_by_lcs() 返回的是下标;当匹配结果是 0 时,当前 if 判断会走 False,导致这些新状态在标题列表首位时无法识别。这里应统一使用 is not None。
建议调整
- if str_utils.find_best_match_by_lcs(ScreenState.SELECT_STATION.value, titles, lcs_percent_threshold=1):
+ if str_utils.find_best_match_by_lcs(ScreenState.SELECT_STATION.value, titles, lcs_percent_threshold=1) is not None:
return ScreenState.SELECT_STATION.value
- if str_utils.find_best_match_by_lcs(ScreenState.SELECT_NEXT_STATION.value, titles, lcs_percent_threshold=1):
+ if str_utils.find_best_match_by_lcs(ScreenState.SELECT_NEXT_STATION.value, titles, lcs_percent_threshold=1) is not None:
return ScreenState.SELECT_NEXT_STATION.value
- if str_utils.find_best_match_by_lcs(ScreenState.CHOOSE_WILL_POWER.value, titles, lcs_percent_threshold=0.7):
+ if str_utils.find_best_match_by_lcs(ScreenState.CHOOSE_WILL_POWER.value, titles, lcs_percent_threshold=0.7) is not None:
return ScreenState.CHOOSE_WILL_POWER.value
- if str_utils.find_best_match_by_lcs(ScreenState.AHA_MASK.value, titles, lcs_percent_threshold=0.7):
+ if str_utils.find_best_match_by_lcs(ScreenState.AHA_MASK.value, titles, lcs_percent_threshold=0.7) is not None:
return ScreenState.AHA_MASK.value🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/sr_od/app/sim_uni/sim_uni_screen_state.py` around lines 124 - 132, The
LCS matcher find_best_match_by_lcs returns an index (0 is a valid match) so the
current if checks treat 0 as False and miss matches at titles[0]; update each
conditional that calls find_best_match_by_lcs (for
ScreenState.SELECT_STATION.value, SELECT_NEXT_STATION.value,
CHOOSE_WILL_POWER.value, AHA_MASK.value) to check explicitly for None (e.g.,
result is not None) or compare against None, ensuring a returned 0 is treated as
a successful match and the corresponding ScreenState is returned.
Summary by CodeRabbit
发布说明
新功能
优化