원클릭으로
pyreact-debug
调试运行在网易我的世界基岩版 ModSDK 中的 Pyreact UI 框架。提供游戏启动、日志流、热重载、UI 树检查、性能 profile 工作流,可以使 AI Agent 更高效地测试,定位,和修复 UI 相关问题。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
调试运行在网易我的世界基岩版 ModSDK 中的 Pyreact UI 框架。提供游戏启动、日志流、热重载、UI 树检查、性能 profile 工作流,可以使 AI Agent 更高效地测试,定位,和修复 UI 相关问题。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | pyreact-debug |
| description | 调试运行在网易我的世界基岩版 ModSDK 中的 Pyreact UI 框架。提供游戏启动、日志流、热重载、UI 树检查、性能 profile 工作流,可以使 AI Agent 更高效地测试,定位,和修复 UI 相关问题。 |
| compatibility | opencode |
| metadata | {"audience":"agents","domain":"pyreact-debug","platform":"netease-minecraft-bedrock-modsdk"} |
.cppconfig,无需依赖 mcpywrap)外部脚本 ──剪切板触发 JSON──▶ 游戏 GameRenderTickEvent 轮询
│ 执行 DebugDump* / DebugClick / DebugSetInput
外部脚本 ◀──结果 JSON ────────┘ SetClipboardContent(result 或 __pyreact_ack__)
外部脚本 ──HTTP POST /send_command──▶ log server ──TCP null-terminated──▶ 游戏
游戏日志 ──TCP stream──────────────▶ log server(日志存内存 + 写文件)
外部脚本 ──HTTP GET /logs──────────▶ log server(从内存返回)
clipboard_ipc.py 封装,使用 pyperclip)reload_pack 等 studio 命令走 HTTP /send_command → log server → TCP 反向通道/logs 从 log server 内存获取,不直接读文件--game-pid 仅用于监控游戏状态,不触发退出)port+1调试功能默认关闭(每帧不做任何操作)。必须在挂载时传入 debug_mode=True:
render_app(root=MyApp, bind=bind, debug_mode=True)
启动 Minecraft 游戏并附带后台常驻 log server,等待 AppReady 信号后退出。启动前自动杀掉残留的游戏进程和 log_server 进程。
python launch_game.py [--project DIR] [--config FILE] [--port PORT] [--log-output FILE]
| 参数 | 默认值 | 说明 |
|---|---|---|
--project DIR | 自动检测 | addon 项目根目录,须含 studio.json(注意:studio.json 在 addon 上一级目录,即 addon 本体目录的父目录) |
--config FILE | 自动发现 | 直接指定 .cppconfig 路径,跳过自动生成 |
--port PORT | 随机 | log server 监听端口 |
--log-output FILE | %TEMP%/pyreact-debug/pyreact_game_<port>.log | 日志持久化路径 |
--project 省略时的自动检测顺序:
studio.json → 直接使用sync_to_test.cmd)→ 解析 TARGET_ROOT,打印 addon 路径后退出提示--config 省略时:扫描 <project>/.runtime/*.cppconfig 取最新文件;找不到则调用 setup_runtime() 自动生成(读 studio.json + 注册表)。
脚本输出端口和日志路径,后续命令需要这两个值:
[launch_game] log server port: 8765
[launch_game] log file: C:\Users\...\AppData\Local\Temp\pyreact-debug\pyreact_game_8765.log
[launch_game] AppReady received, done.
杀掉所有 Minecraft.Windows.exe 进程。log server 检测到游戏 PID 消失后自动退出。
python kill_game.py [--wait]
| 参数 | 说明 |
|---|---|
--wait | 等待进程完全消失后再返回(轮询最多 15 秒) |
通过 HTTP API 从 log server 内存获取游戏日志,支持行号定位、正则过滤和实时跟踪。输出每行带行号,方便用 --since 续读。
python get_logs.py --port PORT
[--tail N | --head N | --lines START[-END] | --since LINENUM]
[--grep PATTERN] [--ignore-case]
[--follow]
| 参数 | 说明 |
|---|---|
--port PORT | 必填,log server 端口(launch_game.py 输出的端口) |
--tail N | 最后 N 行 |
--head N | 前 N 行 |
--lines START[-END] | 行号区间,1-based(如 100-200 或单行 300) |
--since LINENUM | 从第 LINENUM 行起往后,1-based |
--grep PATTERN | 正则过滤,只返回匹配行 |
--ignore-case | 与 --grep 配合,大小写不敏感 |
--follow | 实时跟踪新日志,每 0.5s 轮询一次(Ctrl+C 停止);可与 --tail N 组合先打印最后 N 行再开始跟踪 |
python get_logs.py --port 8765 --tail 50
python get_logs.py --port 8765 --since 500
python get_logs.py --port 8765 --grep "ERROR|WARNING" --ignore-case
# 实时跟踪,先显示最后 20 行
python get_logs.py --port 8765 --follow --tail 20
# 实时跟踪 + 只显示 Pyreact 相关日志
python get_logs.py --port 8765 --follow --grep "Pyreact"
通过 log server HTTP API 向游戏发送 studio 命令。
python send_command.py --port PORT <command> [args...]
| 参数 | 默认值 | 说明 |
|---|---|---|
--port PORT | 必填 | log server 端口(launch_game.py 输出的端口) |
command | 必填 | 命令字符串,多个词会 join 成一条命令 |
python send_command.py --port 8765 reload_pack
python send_command.py --port 8765 restart_local_game
python send_command.py --port 8765 release_mouse
性能 profile 快捷方式,内部调用 send_command.py。
python perf.py --port PORT <action>
| action | 发送的命令组合 | 说明 |
|---|---|---|
start | begin_performance_profile + start_profile | 同时开始 engine + 脚本 profile |
stop | end_performance_profile + stop_profile + log_performance_profile_data | 停止并打印结果 |
script-start | start_profile | 仅脚本 profile |
script-stop | stop_profile | 仅停止脚本 profile |
mem-start | start_mem_profile | 内存 profile |
mem-stop | stop_mem_profile | 停止内存 profile |
dump | log_performance_profile_data | 打印当前数据到游戏日志 |
python perf.py --port 8765 start
# ... 在游戏中操作 ...
python perf.py --port 8765 stop
python get_logs.py --tail 100 --grep "profile"
通过剪切板触发游戏内 UI 树转储,等待结果写回后打印并保存。默认输出美化树形(内部调用 print_ui_tree.py),--json 改为原始 JSON,--quiet 完全静默。
python get_ui_tree.py [--app-id APP_ID] [--node-id NODE_ID]
[--output FILE] [--timeout SECONDS]
[--quiet] [--json]
| 参数 | 默认值 | 说明 |
|---|---|---|
--app-id APP_ID | 第一个已挂载的 app | 目标 app |
--node-id NODE_ID | 无(dump 整棵树) | 检查单个节点(含子树) |
--output FILE | %TEMP%/pyreact-debug/ui_tree.json | 结果 JSON 保存路径 |
--timeout SECONDS | 10 | 等待游戏响应的超时秒数 |
--quiet | false | 不打印到 stdout(文件仍会保存) |
--json | false | 输出原始 JSON 而非美化树形(--quiet 时忽略) |
python get_ui_tree.py # 整棵树,打印美化树形
python get_ui_tree.py --json # 整棵树,打印原始 JSON
python get_ui_tree.py --quiet # 只保存文件,不打印
python get_ui_tree.py --node-id panel_0 # 单节点子树,打印美化树形
节点结构:
{
"id": "panel_0",
"type": "Panel",
"props": {},
"style": {"width": 100},
"opacity": 1.0,
"layout": {"x": 0, "y": 0, "width": 100, "height": 50},
"children": [...]
}
以 UTF-8 安全的树形格式打印已保存的 UI 树 JSON,显示节点类型、id、关键 props(Label 的 content、Image 的 src、Item 的 item_name)、layout 和交互标记([clickable]/[input])。--json 输出原始 JSON。
python print_ui_tree.py [FILE] [--node-id NODE_ID] [--depth N] [--json]
| 参数 | 默认值 | 说明 |
|---|---|---|
FILE | %TEMP%/pyreact-debug/ui_tree.json | UI 树 JSON 文件 |
--node-id NODE_ID | 无(从根打印) | 从指定节点开始打印子树 |
--depth N | 无限制 | 最多打印到第 N 层 |
--json | false | 输出原始 JSON 而非美化树形 |
python print_ui_tree.py # 打印默认文件的树
python print_ui_tree.py --json # 输出原始 JSON
python print_ui_tree.py --node-id panel_left # 只打印子树
python print_ui_tree.py --depth 2 # 只打印前两层
输出示例:
`-- root (Image) 609x429 @(0,0)
|-- p_0 (Panel) 368x429 @(0,0)
| |-- k_mode_bedwar (Button) 80x30 @(10,10) [clickable]
| |-- k_label_title (Label) "空岛战争 · 最后生还·4人" 200x20 @(10,50)
| `-- p_0_8 (Button) 100x36 @(10,380) [clickable]
`-- p_2 (Panel) 240x429 @(369,0)
|-- p_2_2_1 (Input) 200x28 @(10,40) [input]
`-- k_inv_f_nova (Button) 220x36 @(10,100) [clickable]
对比两个 UI 树 JSON 快照,输出新增/删除/变更的节点。节点以完整路径(parent/child)为 key,避免重复 id 覆盖。
python diff_ui_tree.py <before.json> <after.json> [--props] [--layout]
| 参数 | 说明 |
|---|---|
before.json | 交互前的树快照 |
after.json | 交互后的树快照 |
--props | 在 changed 输出中包含 props 对比 |
--layout | 在 changed 输出中包含 layout 对比 |
输出(JSON 到 stdout,摘要到 stderr):
{
"added": ["root/panel_right/k_slot_f_nova"],
"removed": [],
"changed": [
{"path": "root/panel_left/p_0_8", "before": {"type": "Button"}, "after": {"type": "Button"}}
]
}
通过剪切板触发游戏内按钮点击或输入框文本设置,等待游戏写回 __pyreact_ack__ 确认。
python simulate.py <action> --node-id NODE_ID [--app-id APP_ID] [--text TEXT] [--timeout SECONDS]
| 参数 | 默认值 | 说明 |
|---|---|---|
action | 必填 | click(触发 onClick)或 input(设置文本并触发 onChange) |
--node-id NODE_ID | 必填 | 目标节点 id,从 get_ui_tree.py 获取 |
--app-id APP_ID | 第一个已挂载的 app | 目标 app |
--text TEXT | "" | 输入内容(input 专用) |
--timeout SECONDS | 5 | 等待 __pyreact_ack__ 的超时秒数 |
python simulate.py click --node-id submit_btn
python simulate.py input --node-id search_input --text "hello world"
python simulate.py click --node-id ok_btn --app-id my_app --timeout 10
一步完成:操作 + 等待 + diff,是交互测试的推荐工具。内部自动完成 before 快照 → simulate → settle → after 快照 → diff,无需手动管理文件。
python simulate_and_diff.py <action> --node-id NODE_ID [--app-id APP_ID]
[--text TEXT] [--timeout N] [--settle N]
[--props] [--layout]
[--output-before FILE] [--output-after FILE]
| 参数 | 默认值 | 说明 |
|---|---|---|
action | 必填 | click 或 input |
--node-id NODE_ID | 必填 | 目标节点 id |
--app-id APP_ID | 第一个挂载的 app | 目标 app |
--text TEXT | "" | 输入内容(input 专用) |
--timeout N | 5.0 | 等待 ack/树响应的超时秒数 |
--settle N | 0.5 | 操作后等待 UI 稳定的秒数 |
--props | false | diff 输出包含 props 对比 |
--layout | false | diff 输出包含 layout 对比 |
--output-before FILE | 不保存 | 保存 before 快照到文件 |
--output-after FILE | 不保存 | 保存 after 快照到文件 |
# 点击按钮并查看 diff
python simulate_and_diff.py click --node-id k_mode_skywar --props
# 输入文本并查看 diff(含 layout 变化)
python simulate_and_diff.py input --node-id search_input --text "Nova" --props --layout
# 保存快照供后续分析
python simulate_and_diff.py click --node-id ready_btn --output-before before.json --output-after after.json --props
输出(JSON 到 stdout,进度到 stderr):
{
"added": [],
"removed": [],
"changed": [
{
"path": "root/p_0/p_0_8/p_0_8_0",
"before": {"type": "Label", "props": {"content": "✔ 准备", "color": "#ffffffff"}},
"after": {"type": "Label", "props": {"content": "取消准备", "color": "#ffffffff"}}
}
]
}
props 自动过滤:
buttonBuilder/onClick等序列化为<function ...>字符串的闭包 props 会被自动剔除,diff 输出只包含真实数据变化,不会被每次渲染重建的内存地址污染。
外部写入的触发 JSON 格式:
{"pyreact_debug": "dump_tree", "params": {"app_id": "my_app"}}
{"pyreact_debug": "dump_subtree", "params": {"app_id": "my_app", "node_id": "panel_0"}}
{"pyreact_debug": "click", "params": {"node_id": "submit_btn"}}
{"pyreact_debug": "set_input", "params": {"node_id": "search_input", "text": "hello"}}
注意:
dump_node命令已废弃——游戏侧序列化时buttonBuilder(函数)无法 JSON 化,导致报错且不写回剪切板。get_ui_tree.py --node-id已改为统一使用dump_subtree。
游戏在下一个 GameRenderTickEvent 检测到 pyreact_debug 字段后:
__pyreact_ack__ 回剪切板非 pyreact_debug 内容直接跳过,不影响正常剪切板使用。
| 命令 | 作用 |
|---|---|
reload_pack | 热重载行为包脚本 |
reload_cache | 从 pack cache 热重载 |
restart_local_game | 重载当前世界 |
begin_performance_profile | 开始 engine perf profile |
end_performance_profile | 结束 engine perf profile |
log_performance_profile_data | 打印 perf 数据到游戏日志 |
start_profile | 开始脚本 profile |
stop_profile | 停止脚本 profile |
start_mem_profile | 开始内存 profile |
stop_mem_profile | 停止内存 profile |
release_mouse | 释放鼠标捕获 |
create_world | 创建新世界 |
print 为语句)reload_pack 等)通过 HTTP /send_command → log server → TCP 反向通道get_logs.py 必须通过 HTTP API 获取日志,不直接读文件get_ui_tree.py / simulate.py 的剪切板操作通过 clipboard_ipc.py(pyperclip)完成setup_runtime() 依赖 Windows 注册表(HKCU\Software\Netease\MCStudio),仅支持 Windowsdebug_mode=True 必须在挂载时传入,运行时无法动态开启--game-pid 仅用于监控游戏状态port+1 上监听;get_logs.py / send_command.py 均使用此端口pip install pyperclip psutil# 直接打印美化树形(推荐)
python get_ui_tree.py
# 只看前两层
python get_ui_tree.py --node-id p_0 --depth 2
# 需要原始 JSON 时
python get_ui_tree.py --json
# 只保存文件不打印
python get_ui_tree.py --quiet
用 simulate_and_diff.py,一条命令搞定:
# 点击按钮,查看 props 变化
python simulate_and_diff.py click --node-id k_mode_skywar --props
# 输入文本,查看列表过滤结果
python simulate_and_diff.py input --node-id search_input --text "Nova" --props
# 保存快照供后续 debug
python simulate_and_diff.py click --node-id ready_btn --props \
--output-before before.json --output-after after.json
如需手动分步(例如操作和验证之间有复杂交互):
python get_ui_tree.py --quiet --output before.json
python simulate.py click --node-id some_button
python get_ui_tree.py --quiet --output after.json
python diff_ui_tree.py before.json after.json --props
# 第一次拉取,记录输出的 total 行号
python get_logs.py --port 8765 --tail 50
# 之后从上次结束位置续读
python get_logs.py --port 8765 --since <上次 total+1>
# 只看 ERROR/WARNING
python get_logs.py --port 8765 --tail 100 --grep "ERROR|WARNING" --ignore-case