mcpywrap 0.3.4__tar.gz → 0.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/CHANGELOG.md +14 -0
- {mcpywrap-0.3.4/mcpywrap.egg-info → mcpywrap-0.3.6}/PKG-INFO +31 -7
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/README.md +29 -5
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/docs/development.md +5 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/docs/engine-discovery.md +1 -1
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/docs/mcs-auth.md +3 -1
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/docs/releasing.md +1 -1
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/__init__.py +1 -1
- mcpywrap-0.3.6/mcpywrap/__main__.py +18 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/cli.py +11 -2
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/command_context.py +31 -2
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/connect_cmd.py +7 -2
- mcpywrap-0.3.6/mcpywrap/commands/desktop_cmd.py +67 -0
- mcpywrap-0.3.6/mcpywrap/commands/doctor_cmd.py +30 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/edit_cmd.py +2 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/mod_cmd.py +3 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/run_cmd.py +5 -10
- mcpywrap-0.3.6/mcpywrap/commands/serve_cmd.py +35 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/session_cmd.py +12 -4
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/ui_cmd.py +2 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/bridge_assets.py +24 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/bridge_payload/Injector.exe +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/bridge_payload/Loader.dll +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/bridge_payload/McpyMcsAuth.dll +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/bridge_payload/manifest.json +3 -3
- mcpywrap-0.3.6/mcpywrap/mcstudio/diagnostics.py +21 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/log_protocol.py +22 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/network.py +66 -79
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/private_logs.py +6 -7
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/session_worker.py +25 -11
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/sessions.py +32 -7
- mcpywrap-0.3.4/skills/mcpywrap/scripts/game_window.py → mcpywrap-0.3.6/mcpywrap/mcstudio/window.py +159 -68
- mcpywrap-0.3.6/mcpywrap/remote/__init__.py +1 -0
- mcpywrap-0.3.6/mcpywrap/remote/client.py +164 -0
- mcpywrap-0.3.6/mcpywrap/remote/http_server.py +145 -0
- mcpywrap-0.3.6/mcpywrap/remote/service.py +279 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6/mcpywrap.egg-info}/PKG-INFO +31 -7
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap.egg-info/SOURCES.txt +16 -1
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap.egg-info/requires.txt +3 -1
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/pyproject.toml +2 -2
- mcpywrap-0.3.6/skills/mcpywrap/SKILL.md +94 -0
- mcpywrap-0.3.6/skills/mcpywrap/agents/openai.yaml +4 -0
- mcpywrap-0.3.6/skills/mcpywrap/references/remote-testing.md +80 -0
- mcpywrap-0.3.6/skills/mcpywrap/references/troubleshooting.md +53 -0
- mcpywrap-0.3.6/skills/mcpywrap/scripts/bootstrap.ps1 +31 -0
- mcpywrap-0.3.6/skills/mcpywrap/scripts/bootstrap.py +142 -0
- mcpywrap-0.3.6/skills/mcpywrap/scripts/game_window.py +49 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/skills/mcpywrap/scripts/smoke.py +42 -20
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_auth_interaction.py +1 -1
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_engine_discovery.py +3 -2
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_game_window_skill.py +24 -36
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_mcs_auth.py +24 -14
- mcpywrap-0.3.6/tests/test_mouse.py +81 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_network.py +32 -56
- mcpywrap-0.3.6/tests/test_network_sessions.py +98 -0
- mcpywrap-0.3.6/tests/test_remote.py +332 -0
- mcpywrap-0.3.6/tests/test_remote_scripts.py +150 -0
- mcpywrap-0.3.6/tests/test_skill_workflows.py +222 -0
- mcpywrap-0.3.4/mcpywrap/__main__.py +0 -13
- mcpywrap-0.3.4/mcpywrap/commands/doctor_cmd.py +0 -33
- mcpywrap-0.3.4/skills/mcpywrap/SKILL.md +0 -108
- mcpywrap-0.3.4/skills/mcpywrap/agents/openai.yaml +0 -4
- mcpywrap-0.3.4/skills/mcpywrap/references/troubleshooting.md +0 -28
- mcpywrap-0.3.4/skills/mcpywrap/scripts/bootstrap.ps1 +0 -65
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/LICENSE +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/MANIFEST.in +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/docs/local-dependencies.md +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/AddonsPack.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/MapPack.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/__init__.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/assembly.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/dependency_manager.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/file_merge.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/project_builder.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/builders/watcher.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/__init__.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/add_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/build_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/default_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/dependency_prompt.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/dev_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/init_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/modsdk_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/package_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/publish_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/remove_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/commands/sync_cmd.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/config.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/custom_packaging.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/dependencies.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/__init__.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/auth_interaction.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/bridge_payload/publisher.cer +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/discovery.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/editor.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/file_logs.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/game.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/mcs.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/mcs_auth.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/processes.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/studio_server.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/mcstudio/symlinks.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/minecraft/__init__.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/minecraft/addons.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/minecraft/level_dat.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/minecraft/map.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/minecraft/netease_modsdk.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/minecraft/template/mod_template.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/project_init.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/ui/__init__.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/ui/project_ui.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/utils/__init__.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/utils/pip_error_parser.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/utils/print_guide.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/utils/project_setup.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap/utils/utils.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/native/mcs_auth/Bridge.cs +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/native/mcs_auth/Injector.cs +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/native/mcs_auth/Loader.c +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/native/mcs_auth/publisher.cer +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/native/mcs_auth/publisher.json +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/scripts/build_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/scripts/check_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/setup.cfg +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/manual_integration.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_automation.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_local_auth_session.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_local_dependencies.py +0 -0
- {mcpywrap-0.3.4 → mcpywrap-0.3.6}/tests/test_package.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.3.6
|
|
4
|
+
|
|
5
|
+
- 新增局域网 serve 服务与 --remote/--local 路由;macOS 本机开发、Windows 网络游戏测试共用 CLI 和 Skill。
|
|
6
|
+
- 新增会话截图、键盘、鼠标命令,支持菜单操作与相对视角移动;远端截图保存到调用端。
|
|
7
|
+
- 支持启动去重、所属会话恢复、串行桌面输入与服务退出清理;日志可选择 game/engine/worker。
|
|
8
|
+
- 跨平台 bootstrap.py 共用安装逻辑,PowerShell 入口保留兼容;macOS 安装不依赖 Qt。
|
|
9
|
+
|
|
10
|
+
## 0.3.5
|
|
11
|
+
|
|
12
|
+
- 网络连接接入统一游戏会话:`connect/run --detach --json` 返回进程身份与日志,可复用 status/logs/stop、Skill 截图与按键脚本;网络 JSON 启动现在要求 `--detach`。
|
|
13
|
+
- 新增 `doctor --mcs-auth` 只读组件检查;Skill 安装脚本分别报告 CLI 能力与组件是否具备,按任务显式要求可选能力。
|
|
14
|
+
- 冒烟脚本支持 `--mcs-auth` 与 `--connect`,共用日志标记验证和本次会话清理;精简 Skill 入口并补充编辑器、地图合并及网络指引。
|
|
15
|
+
- 修复 Windows PowerShell 5.1 中文脚本读取与 GBK 管道兼容;JSON 无损保留 Unicode,会话 SDK/引擎日志统一解码为 UTF-8 后脱敏。
|
|
16
|
+
|
|
3
17
|
## 0.3.4
|
|
4
18
|
|
|
5
19
|
- 新增显式 `--mcs-auth`:网络连接、单人测试和单人项目 GUI 可按次使用已登录的 MC Studio 身份;默认仍不读取身份,配置文件不能自动启用。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: A wrapper for Minecraft China Edition Addons management
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -20,7 +20,7 @@ Requires-Dist: tomli
|
|
|
20
20
|
Requires-Dist: tomli-w
|
|
21
21
|
Requires-Dist: setuptools
|
|
22
22
|
Requires-Dist: packaging>=21
|
|
23
|
-
Requires-Dist: PyQt5>=5.15.0
|
|
23
|
+
Requires-Dist: PyQt5>=5.15.0; sys_platform == "win32"
|
|
24
24
|
Requires-Dist: psutil>=5.8.0
|
|
25
25
|
Requires-Dist: rich>=10.0.0
|
|
26
26
|
Dynamic: license-file
|
|
@@ -140,9 +140,18 @@ host = "192.168.31.101"
|
|
|
140
140
|
port = 19132
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
空目录只需上述配置,无需执行 `mcpy init`;已有 Addon
|
|
143
|
+
空目录只需上述配置,无需执行 `mcpy init`;已有 Addon 项目也可添加此表。默认使用未认证连接,不读取登录身份或 token,不保证服务器允许进服;项目依赖仍会校验,但暂不装配本地 Mod。
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
网络模式默认前台输出日志,Ctrl+C 结束本次游戏。增加 `--detach --json` 可返回会话,与本地游戏共用 `status/logs/stop` 及 Skill 截图、按键脚本:
|
|
146
|
+
|
|
147
|
+
```powershell
|
|
148
|
+
mcpy --project D:\tests\server --non-interactive connect example.com --detach --json
|
|
149
|
+
mcpy --project D:\tests\server status --session <id> --json
|
|
150
|
+
mcpy --project D:\tests\server stop --session <id> --json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
目录须已存在;临时 `connect` 不读取项目配置,只在该目录保存会话。后续操作使用启动返回的 `project` 和 `session`。
|
|
154
|
+
网络模式暂不支持 Map、Qt GUI、`--new` 或本地世界实例 ID;进程启动不等于成功进服。
|
|
146
155
|
|
|
147
156
|
### 使用 MC Studio 登录身份(可选)
|
|
148
157
|
|
|
@@ -163,8 +172,8 @@ port = 19132
|
|
|
163
172
|
|
|
164
173
|
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
165
174
|
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
|
|
176
|
+
局域网流程请使用 mcpywrap 0.3.6 或更高版本,可从 `v0.3.6` 标签安装配套 Skill。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
|
|
168
177
|
|
|
169
178
|
安装后可直接描述任务:
|
|
170
179
|
|
|
@@ -172,9 +181,24 @@ Skill 中的 `scripts/bootstrap.ps1` 可使用已有 uv 安装 CLI,并检查
|
|
|
172
181
|
- “启动这个项目,不弹日志界面,检查客户端和服务端的加载日志。”
|
|
173
182
|
- “启动游戏并截图,模拟组合按键移动,检查 F11 输入模式和 F3 调试信息层。”
|
|
174
183
|
|
|
175
|
-
Agent 通过 CLI
|
|
184
|
+
Agent 通过 CLI 管理项目、截图和输入键鼠。远程交互查看下载的图片后继续操作;本机复杂交互可交给 Computer Use。Qt 管理与模板界面用于人工操作。
|
|
176
185
|
常用入口:`mcpy --project <目录> --non-interactive <命令> --json`。
|
|
177
186
|
|
|
187
|
+
## macOS 与 Windows 局域网联调
|
|
188
|
+
|
|
189
|
+
Windows 用户在测试桌面运行 `mcpy --local serve`,默认监听 `0.0.0.0:18765`;macOS 使用同一服务地址调用:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
mcpy --remote http://192.168.1.20:18765 --non-interactive connect 192.168.1.10 --port 19132 --detach --json
|
|
193
|
+
mcpy --remote http://192.168.1.20:18765 screenshot --session <id> --output ./game.png --json
|
|
194
|
+
mcpy --remote http://192.168.1.20:18765 key --session <id> SHIFT+W --hold-ms 1000 --json
|
|
195
|
+
mcpy --remote http://192.168.1.20:18765 stop --session <id> --json
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
项目依赖与构建仍在 macOS,游戏诊断、网络启动、日志和输入转发到 Windows,截图下载到调用端。
|
|
199
|
+
默认免认证,可用两端 `MCPY_REMOTE_TOKEN` 启用令牌;服务 Ctrl+C 清理所属游戏。需要 Windows 保持登录未锁屏,一次只接受一个活动会话。
|
|
200
|
+
bootstrap 检测不会保存远端地址,独立终端调用请每次显式传 `--remote <地址>`。部署、鼠标与恢复见[远程测试指引](skills/mcpywrap/references/remote-testing.md)。
|
|
201
|
+
|
|
178
202
|
## 更多信息
|
|
179
203
|
|
|
180
204
|
- 运行 `mcpy <命令> --help` 查看该命令的选项。
|
|
@@ -113,9 +113,18 @@ host = "192.168.31.101"
|
|
|
113
113
|
port = 19132
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
空目录只需上述配置,无需执行 `mcpy init`;已有 Addon
|
|
116
|
+
空目录只需上述配置,无需执行 `mcpy init`;已有 Addon 项目也可添加此表。默认使用未认证连接,不读取登录身份或 token,不保证服务器允许进服;项目依赖仍会校验,但暂不装配本地 Mod。
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
网络模式默认前台输出日志,Ctrl+C 结束本次游戏。增加 `--detach --json` 可返回会话,与本地游戏共用 `status/logs/stop` 及 Skill 截图、按键脚本:
|
|
119
|
+
|
|
120
|
+
```powershell
|
|
121
|
+
mcpy --project D:\tests\server --non-interactive connect example.com --detach --json
|
|
122
|
+
mcpy --project D:\tests\server status --session <id> --json
|
|
123
|
+
mcpy --project D:\tests\server stop --session <id> --json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
目录须已存在;临时 `connect` 不读取项目配置,只在该目录保存会话。后续操作使用启动返回的 `project` 和 `session`。
|
|
127
|
+
网络模式暂不支持 Map、Qt GUI、`--new` 或本地世界实例 ID;进程启动不等于成功进服。
|
|
119
128
|
|
|
120
129
|
### 使用 MC Studio 登录身份(可选)
|
|
121
130
|
|
|
@@ -136,8 +145,8 @@ port = 19132
|
|
|
136
145
|
|
|
137
146
|
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
138
147
|
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
139
|
-
|
|
140
|
-
|
|
148
|
+
两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
|
|
149
|
+
局域网流程请使用 mcpywrap 0.3.6 或更高版本,可从 `v0.3.6` 标签安装配套 Skill。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
|
|
141
150
|
|
|
142
151
|
安装后可直接描述任务:
|
|
143
152
|
|
|
@@ -145,9 +154,24 @@ Skill 中的 `scripts/bootstrap.ps1` 可使用已有 uv 安装 CLI,并检查
|
|
|
145
154
|
- “启动这个项目,不弹日志界面,检查客户端和服务端的加载日志。”
|
|
146
155
|
- “启动游戏并截图,模拟组合按键移动,检查 F11 输入模式和 F3 调试信息层。”
|
|
147
156
|
|
|
148
|
-
Agent 通过 CLI
|
|
157
|
+
Agent 通过 CLI 管理项目、截图和输入键鼠。远程交互查看下载的图片后继续操作;本机复杂交互可交给 Computer Use。Qt 管理与模板界面用于人工操作。
|
|
149
158
|
常用入口:`mcpy --project <目录> --non-interactive <命令> --json`。
|
|
150
159
|
|
|
160
|
+
## macOS 与 Windows 局域网联调
|
|
161
|
+
|
|
162
|
+
Windows 用户在测试桌面运行 `mcpy --local serve`,默认监听 `0.0.0.0:18765`;macOS 使用同一服务地址调用:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
mcpy --remote http://192.168.1.20:18765 --non-interactive connect 192.168.1.10 --port 19132 --detach --json
|
|
166
|
+
mcpy --remote http://192.168.1.20:18765 screenshot --session <id> --output ./game.png --json
|
|
167
|
+
mcpy --remote http://192.168.1.20:18765 key --session <id> SHIFT+W --hold-ms 1000 --json
|
|
168
|
+
mcpy --remote http://192.168.1.20:18765 stop --session <id> --json
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
项目依赖与构建仍在 macOS,游戏诊断、网络启动、日志和输入转发到 Windows,截图下载到调用端。
|
|
172
|
+
默认免认证,可用两端 `MCPY_REMOTE_TOKEN` 启用令牌;服务 Ctrl+C 清理所属游戏。需要 Windows 保持登录未锁屏,一次只接受一个活动会话。
|
|
173
|
+
bootstrap 检测不会保存远端地址,独立终端调用请每次显式传 `--remote <地址>`。部署、鼠标与恢复见[远程测试指引](skills/mcpywrap/references/remote-testing.md)。
|
|
174
|
+
|
|
151
175
|
## 更多信息
|
|
152
176
|
|
|
153
177
|
- 运行 `mcpy <命令> --help` 查看该命令的选项。
|
|
@@ -19,6 +19,11 @@ python skills/mcpywrap/scripts/smoke.py --project D:\mods\test --game --expect-l
|
|
|
19
19
|
|
|
20
20
|
脚本结束后停止自己创建的会话;日志和产物留在项目中。
|
|
21
21
|
需要手动截图验证时直接运行 `run --no-gui --detach --json`,保存会话 ID,操作完后显式 stop。
|
|
22
|
+
网络验证使用 `smoke.py --project <已有目录> --connect <地址> --expect-log <约定标记>`,不打包项目。单人与网络测试都可显式加 `--mcs-auth`;脚本停止自己创建的会话。
|
|
22
23
|
历史验收记录仅用于追溯,不能代替当前版本测试。
|
|
23
24
|
|
|
24
25
|
发布见 [发布流程](releasing.md)。
|
|
26
|
+
|
|
27
|
+
局域网协议与路由测试使用 `tests/test_remote.py` 的真实回环 HTTP 和模拟游戏,不需要实际登录。
|
|
28
|
+
桌面原生实现集中在 `mcstudio/window.py`,Skill 脚本只组合公开 CLI。鼠标逻辑通过模拟 Win32 测试释放与坐标,实机效果按截图验证。
|
|
29
|
+
Windows CI 保留 3.9/3.12/3.14;macOS CI 验证无 Qt 安装、本机构建与远程客户端。真实跨机验收需另行准备 Windows 桌面和 macOS 客户端。
|
|
@@ -52,4 +52,4 @@ mcpy --project D:\mods\demo stop --session <id> --json
|
|
|
52
52
|
|
|
53
53
|
只打开游戏窗口,日志写入返回的文件。`running` 表示进程存活,加载是否成功应查看项目日志或游戏画面。
|
|
54
54
|
Computer Use 只接手游戏画面;Qt 管理、模板及日志页留给人工操作。
|
|
55
|
-
|
|
55
|
+
以上示例是本地世界会话。网络连接可用 `mcpy connect <地址> --detach --json`,或配置服务器后执行 `run --detach --json`;同样使用返回的项目路径、会话 ID 读取日志、截图、输入和停止。临时 connect 不读取项目引擎配置,使用 CLI 参数或环境变量覆盖。
|
|
@@ -10,7 +10,9 @@ mcpy ui --mcs-auth
|
|
|
10
10
|
mcpy --project D:\mods\demo --non-interactive run --mcs-auth --detach --json
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
本地与网络均支持 `--detach --json`,共用会话查询、日志和停止。GUI 登录支持单人项目,网络目标使用 CLI。
|
|
14
|
+
|
|
15
|
+
`mcpy doctor --mcs-auth --json` 可只读检查组件文件,不读取身份、不写缓存、不修改证书。组件具备不表示已登录或系统策略允许启用。
|
|
14
16
|
|
|
15
17
|
## 用户会看到什么
|
|
16
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 发布流程
|
|
2
2
|
|
|
3
|
-
主分支和 PR 运行 Windows Python 3.9、3.12、3.14
|
|
3
|
+
主分支和 PR 运行 Windows Python 3.9、3.12、3.14 的自动化测试,以及 macOS 的无 Qt 安装、项目构建与远程客户端测试。通过后构建 wheel/sdist、运行严格元数据检查、检查产物内容,并在独立环境安装 wheel 验证 CLI 和模块导入。
|
|
4
4
|
|
|
5
5
|
Windows CI 先用 MSVC 从 `native/mcs_auth` 编译 x86 登录桥接,再签名并通过构建产物交给测试和 Python 打包任务;编译后的 EXE/DLL 不提交到 Git。仓库只保留发布者公钥证书和指纹。
|
|
6
6
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
mcpywrap 命令行工具的入口点
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .cli import cli
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
"""主函数,作为 CLI 入口点"""
|
|
10
|
+
import sys
|
|
11
|
+
# Keep the caller's text encoding; unsupported symbols must not abort an operation.
|
|
12
|
+
for stream in (sys.stdout, sys.stderr):
|
|
13
|
+
if hasattr(stream, 'reconfigure'):
|
|
14
|
+
stream.reconfigure(errors='backslashreplace')
|
|
15
|
+
cli()
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
main()
|
|
@@ -27,11 +27,13 @@ from .commands.session_cmd import status_cmd, logs_cmd, stop_cmd
|
|
|
27
27
|
@click.option("--project", type=click.Path(file_okay=False), help="项目目录,默认当前目录")
|
|
28
28
|
@click.option("--non-interactive", is_flag=True, help="不读取终端输入")
|
|
29
29
|
@click.option("--json", "json_output", is_flag=True, help="输出 JSON 结果")
|
|
30
|
+
@click.option('--remote', help='Windows 测试服务地址,例如 http://192.168.1.20:18765')
|
|
31
|
+
@click.option('--local', 'local_only', is_flag=True, help='本次强制本机执行')
|
|
30
32
|
@click.version_option(__version__)
|
|
31
33
|
@click.pass_context
|
|
32
|
-
def cli(ctx, project, non_interactive, json_output):
|
|
34
|
+
def cli(ctx, project, non_interactive, json_output, remote, local_only):
|
|
33
35
|
"""mcpywrap - 《我的世界》中国版 依赖管理与项目构建工具"""
|
|
34
|
-
configure_context(project, non_interactive)
|
|
36
|
+
configure_context(project, non_interactive, remote, local_only)
|
|
35
37
|
# 如果没有提供子命令,则运行 default_cmd
|
|
36
38
|
if ctx.invoked_subcommand is None:
|
|
37
39
|
# 导入并运行默认命令
|
|
@@ -60,5 +62,12 @@ cli.add_command(status_cmd)
|
|
|
60
62
|
cli.add_command(logs_cmd)
|
|
61
63
|
cli.add_command(stop_cmd)
|
|
62
64
|
|
|
65
|
+
from .commands.desktop_cmd import screenshot_cmd, key_cmd, mouse_cmd
|
|
66
|
+
from .commands.serve_cmd import serve_cmd
|
|
67
|
+
cli.add_command(screenshot_cmd, 'screenshot')
|
|
68
|
+
cli.add_command(key_cmd, 'key')
|
|
69
|
+
cli.add_command(mouse_cmd, 'mouse')
|
|
70
|
+
cli.add_command(serve_cmd, 'serve')
|
|
71
|
+
|
|
63
72
|
if __name__ == '__main__':
|
|
64
73
|
cli()
|
|
@@ -17,6 +17,8 @@ class CommandContext:
|
|
|
17
17
|
project: Path
|
|
18
18
|
non_interactive: bool = False
|
|
19
19
|
json_output: bool = False
|
|
20
|
+
remote: str = None
|
|
21
|
+
local: bool = False
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
_context = contextvars.ContextVar('mcpy_context', default=None)
|
|
@@ -67,6 +69,10 @@ class OperationCommand(click.Command):
|
|
|
67
69
|
|
|
68
70
|
def invoke(self, ctx):
|
|
69
71
|
ctx.params.pop('json_output', None)
|
|
72
|
+
from .remote.client import routed_command
|
|
73
|
+
routed, result = routed_command(ctx.info_name, ctx.params)
|
|
74
|
+
if routed:
|
|
75
|
+
return result
|
|
70
76
|
return super().invoke(ctx)
|
|
71
77
|
|
|
72
78
|
|
|
@@ -111,7 +117,8 @@ class OperationGroup(click.Group):
|
|
|
111
117
|
data = {'ok': code == 0, 'error': None, 'hint': None}
|
|
112
118
|
if isinstance(result, dict):
|
|
113
119
|
data.update(result)
|
|
114
|
-
|
|
120
|
+
# ASCII JSON survives both GBK consoles and UTF-8 subprocess pipes losslessly.
|
|
121
|
+
click.echo(json.dumps(data, ensure_ascii=True), file=output)
|
|
115
122
|
elif code:
|
|
116
123
|
click.echo('错误: ' + str((result or {}).get('error') or '操作失败'), err=True)
|
|
117
124
|
if (result or {}).get('hint'):
|
|
@@ -130,9 +137,31 @@ class OperationGroup(click.Group):
|
|
|
130
137
|
return result
|
|
131
138
|
|
|
132
139
|
|
|
133
|
-
def configure_context(project, interactive_disabled):
|
|
140
|
+
def configure_context(project, interactive_disabled, remote=None, local=False):
|
|
134
141
|
current = _context.get()
|
|
142
|
+
current.remote, current.local = remote, local
|
|
135
143
|
current.project = Path(project or os.getcwd()).expanduser().resolve()
|
|
136
144
|
current.non_interactive = interactive_disabled or not sys.stdin.isatty()
|
|
137
145
|
if not current.project.is_dir():
|
|
138
146
|
raise click.UsageError(f'项目目录不存在: {current.project}')
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def remote_url():
|
|
150
|
+
"""Resolve only for game operations; local project commands never depend on a remote."""
|
|
151
|
+
state = _context.get()
|
|
152
|
+
if state and state.local:
|
|
153
|
+
return None
|
|
154
|
+
if state and state.remote is not None:
|
|
155
|
+
return state.remote
|
|
156
|
+
if os.environ.get('MCPY_REMOTE'):
|
|
157
|
+
return os.environ['MCPY_REMOTE']
|
|
158
|
+
path = project_dir()/'pyproject.toml'
|
|
159
|
+
if path.is_file():
|
|
160
|
+
import tomli
|
|
161
|
+
try:
|
|
162
|
+
with path.open('rb') as stream:
|
|
163
|
+
return tomli.load(stream).get('tool', {}).get('mcpywrap', {}).get('remote_url')
|
|
164
|
+
except tomli.TOMLDecodeError:
|
|
165
|
+
# connect intentionally ignores unrelated malformed project configuration.
|
|
166
|
+
return None
|
|
167
|
+
return None
|
|
@@ -11,10 +11,15 @@ from ..mcstudio.network import ServerTarget, run_network
|
|
|
11
11
|
@click.argument('host')
|
|
12
12
|
@click.option('--port', type=click.IntRange(1, 65535), default=19132, show_default=True)
|
|
13
13
|
@click.option('--mcs-auth', is_flag=True, help='本次连接使用已登录的 MC Studio 身份')
|
|
14
|
-
|
|
14
|
+
@click.option('--detach', is_flag=True, help='后台连接并返回游戏会话;JSON 必须指定')
|
|
15
|
+
@click.option('--request-id', help='远程启动的 32 位十六进制请求 ID,用于找回或重试同一请求')
|
|
16
|
+
def connect_cmd(host, port, mcs_auth=False, detach=False, request_id=None, **engine_overrides):
|
|
15
17
|
"""尝试连接服务器;前台采集日志,Ctrl+C 结束本次游戏。
|
|
16
18
|
|
|
17
19
|
默认不读取登录身份;--mcs-auth 要求 MCS 已登录及本机桥接可用。
|
|
18
20
|
不读取项目配置,不装配本地 Mod,不保证服务器接受连接。
|
|
19
21
|
"""
|
|
20
|
-
|
|
22
|
+
if request_id:
|
|
23
|
+
raise click.UsageError('--request-id 仅用于远程启动')
|
|
24
|
+
return run_network(ServerTarget(host, port, 'mcs' if mcs_auth else 'none'),
|
|
25
|
+
engine_overrides=engine_overrides, detach=detach)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Session-bound desktop operations; the CLI router can execute these remotely."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import click
|
|
4
|
+
from ..command_context import OperationCommand, project_dir
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def output_path(value):
|
|
8
|
+
path = Path(value).expanduser().resolve()
|
|
9
|
+
if path.suffix.lower() != '.png' or path.exists():
|
|
10
|
+
raise ValueError('输出必须是尚不存在的 .png 文件')
|
|
11
|
+
return path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def save_image(path, content):
|
|
15
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
16
|
+
with path.open('xb') as stream:
|
|
17
|
+
stream.write(content)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class KeyCommand(OperationCommand):
|
|
21
|
+
def get_help(self, ctx):
|
|
22
|
+
from ..mcstudio.window import KEYS, ALIASES
|
|
23
|
+
self.epilog = '键名: ' + ', '.join(sorted(set(KEYS) | set(ALIASES) | {'NUMPAD_ENTER'}))
|
|
24
|
+
return super().get_help(ctx)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@click.command(cls=OperationCommand)
|
|
28
|
+
@click.option('--session', required=True)
|
|
29
|
+
@click.option('--output', required=True, help='本机 PNG 路径,不覆盖已有文件')
|
|
30
|
+
def screenshot_cmd(session, output):
|
|
31
|
+
"""截图到调用端;远程模式会下载 Windows 游戏客户区 PNG。"""
|
|
32
|
+
from ..mcstudio.window import operate
|
|
33
|
+
path = output_path(output)
|
|
34
|
+
result = operate(project_dir(), session, 'screenshot')
|
|
35
|
+
save_image(path, result.pop('content'))
|
|
36
|
+
return {'image': str(path), **result}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@click.command(cls=KeyCommand)
|
|
40
|
+
@click.option('--session', required=True)
|
|
41
|
+
@click.argument('keys', nargs=-1, required=True)
|
|
42
|
+
@click.option('--hold-ms', type=click.IntRange(20, 60000), default=80, show_default=True)
|
|
43
|
+
def key_cmd(session, keys, hold_ms):
|
|
44
|
+
"""发送键名、组合键或 VK/SC/E0 扫描码,结束时释放;例如 SHIFT+W。"""
|
|
45
|
+
from ..mcstudio.window import operate
|
|
46
|
+
return operate(project_dir(), session, 'key', {'keys': keys, 'hold_ms': hold_ms})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@click.command(cls=OperationCommand)
|
|
50
|
+
@click.argument('action', type=click.Choice(['move', 'click', 'double-click', 'scroll', 'drag', 'relative']))
|
|
51
|
+
@click.option('--session', required=True)
|
|
52
|
+
@click.option('--x', type=int)
|
|
53
|
+
@click.option('--y', type=int)
|
|
54
|
+
@click.option('--width', type=click.IntRange(1), help='参考截图客户区宽度')
|
|
55
|
+
@click.option('--height', type=click.IntRange(1), help='参考截图客户区高度')
|
|
56
|
+
@click.option('--to-x', type=int, help='拖拽终点')
|
|
57
|
+
@click.option('--to-y', type=int, help='拖拽终点')
|
|
58
|
+
@click.option('--dx', type=int, default=0, help='相对移动总量')
|
|
59
|
+
@click.option('--dy', type=int, default=0)
|
|
60
|
+
@click.option('--delta', type=int, default=0, help='滚轮量,120 为一格,负值向下')
|
|
61
|
+
@click.option('--button', type=click.Choice(['left', 'right', 'middle']), default='left')
|
|
62
|
+
@click.option('--duration-ms', type=click.IntRange(20, 60000), default=80, show_default=True)
|
|
63
|
+
@click.option('--keys', multiple=True, help='同时按住的修饰键,例如 SHIFT 或 CTRL+ALT')
|
|
64
|
+
def mouse_cmd(session, **parameters):
|
|
65
|
+
"""客户区鼠标操作;relative 用于视角转向,其他动作需 x/y/width/height。"""
|
|
66
|
+
from ..mcstudio.window import operate
|
|
67
|
+
return operate(project_dir(), session, 'mouse', parameters)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""不创建项目、不启动程序的本机诊断。"""
|
|
2
|
+
import click
|
|
3
|
+
from ..command_context import OperationCommand, project_dir as current_project
|
|
4
|
+
from ..mcstudio.discovery import engine_options
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.command(cls=OperationCommand)
|
|
8
|
+
@engine_options
|
|
9
|
+
@click.option('--mcs-auth', is_flag=True, help='同时只读检查登录组件;不读取身份或安装证书')
|
|
10
|
+
@click.option('--capabilities', is_flag=True, help='报告本机或远端能力,不启动游戏')
|
|
11
|
+
def doctor_cmd(mcs_auth=False, capabilities=False, **overrides):
|
|
12
|
+
"""诊断游戏发现和运行资源(只读,无需初始化项目)"""
|
|
13
|
+
from ..mcstudio.diagnostics import diagnose
|
|
14
|
+
if capabilities:
|
|
15
|
+
from ..remote.service import capabilities as inspect_capabilities
|
|
16
|
+
return inspect_capabilities()
|
|
17
|
+
data = diagnose(current_project(), overrides, mcs_auth)
|
|
18
|
+
from ..command_context import json_output
|
|
19
|
+
if not json_output():
|
|
20
|
+
for candidate in data['candidates']:
|
|
21
|
+
click.echo(f"候选 [{candidate['source']}] {candidate['version']}: {candidate['executable']}")
|
|
22
|
+
for issue in data['diagnostics']:
|
|
23
|
+
click.echo(f'{issue["path"]}: {issue["message"]}')
|
|
24
|
+
for kind, issues in data['resources'].items():
|
|
25
|
+
for issue in issues:
|
|
26
|
+
click.echo(f'资源 [{kind}]: {issue}')
|
|
27
|
+
if mcs_auth:
|
|
28
|
+
click.echo('登录组件: ' + ('文件齐全,尚未验证登录和系统策略' if data['mcs_auth']['component_available']
|
|
29
|
+
else data['mcs_auth']['error']))
|
|
30
|
+
return data
|
|
@@ -25,6 +25,8 @@ from ..utils.project_setup import find_and_configure_behavior_pack
|
|
|
25
25
|
@click.option("--detach", is_flag=True, help="启动编辑器后返回进程信息")
|
|
26
26
|
def edit_cmd(detach, **engine_overrides):
|
|
27
27
|
"""使用 MC Studio Editor 编辑器进行编辑"""
|
|
28
|
+
if os.name != 'nt':
|
|
29
|
+
raise click.ClickException('此界面仅支持 Windows 人工操作,不支持远程 GUI')
|
|
28
30
|
# 检查项目是否已初始化
|
|
29
31
|
if not config_exists():
|
|
30
32
|
require_project()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""用参数生成 Mod 脚本,GUI 向导仅用于人工交互。"""
|
|
2
2
|
import keyword
|
|
3
|
+
import os
|
|
3
4
|
import click
|
|
4
5
|
from ..command_context import OperationCommand, project_dir, non_interactive, require_project
|
|
5
6
|
from ..minecraft.addons import find_behavior_pack_dir
|
|
@@ -20,6 +21,8 @@ def mod_cmd(name, version, script_dir, server_system, client_system, gui):
|
|
|
20
21
|
if not behavior:
|
|
21
22
|
raise click.ClickException('未找到行为包,请先初始化 Addon')
|
|
22
23
|
if gui or (not name and not non_interactive()):
|
|
24
|
+
if os.name != 'nt':
|
|
25
|
+
raise click.ClickException('Qt 模板向导仅支持 Windows,请使用 --name 等参数')
|
|
23
26
|
from ..command_context import json_output
|
|
24
27
|
if non_interactive() or json_output():
|
|
25
28
|
raise click.UsageError('Qt 模板向导仅供人工使用;请提供 --name 等生成参数')
|
|
@@ -126,7 +126,7 @@ def _build_dependency_tree(node, tree_node):
|
|
|
126
126
|
|
|
127
127
|
def _run_game_with_instance(config_path, level_id, all_packs, wait=True, log_callback=None,
|
|
128
128
|
engine_overrides=None, no_gui=False, logging_port=None, output_path=None,
|
|
129
|
-
auth_context=None, auth_config_path=None):
|
|
129
|
+
auth_context=None, auth_config_path=None, capture_output=False):
|
|
130
130
|
"""使用指定的实例运行游戏
|
|
131
131
|
|
|
132
132
|
Args:
|
|
@@ -274,7 +274,7 @@ def _run_game_with_instance(config_path, level_id, all_packs, wait=True, log_cal
|
|
|
274
274
|
|
|
275
275
|
with console.status("启动游戏中...", spinner="dots"):
|
|
276
276
|
game_process = open_game(launch_config_path, logging_port=logging_port, wait=False, engine=engine,
|
|
277
|
-
**({'capture_output': True} if auth_context is not None else
|
|
277
|
+
**({'capture_output': True} if auth_context is not None or capture_output else
|
|
278
278
|
{'output_path': output_path} if output_path else {}))
|
|
279
279
|
|
|
280
280
|
if not game_process:
|
|
@@ -356,7 +356,7 @@ def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach
|
|
|
356
356
|
配置 [tool.mcpywrap.server] host/port 时改为网络连接;默认未认证,
|
|
357
357
|
可用 --mcs-auth 显式读取 MCS 身份,单人测试同样支持此选项。
|
|
358
358
|
不装配本地 Mod,不保证服务器接受连接。网络模式前台采集日志,
|
|
359
|
-
Ctrl+C
|
|
359
|
+
Ctrl+C 结束本次网络游戏;--detach 返回可查询、操作和停止的会话。
|
|
360
360
|
"""
|
|
361
361
|
base_dir = str(current_project())
|
|
362
362
|
if (delete or clean_all) and non_interactive() and not force:
|
|
@@ -379,11 +379,9 @@ def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach
|
|
|
379
379
|
target = replace(target, auth='mcs')
|
|
380
380
|
if new or instance_prefix:
|
|
381
381
|
raise click.UsageError('网络模式不支持 --new 或本地世界实例 ID')
|
|
382
|
-
if detach:
|
|
383
|
-
raise click.UsageError('网络模式暂不支持 --detach;请在终端会话中前台运行')
|
|
384
382
|
packs = prepare_project(base_dir, config)
|
|
385
383
|
return run_network(target, project_dir=base_dir, packs=packs,
|
|
386
|
-
engine_overrides=engine_overrides)
|
|
384
|
+
engine_overrides=engine_overrides, detach=detach)
|
|
387
385
|
|
|
388
386
|
# 确保 map 项目的 setuptools 配置同步
|
|
389
387
|
ensure_map_setuptools_sync(interactive=False)
|
|
@@ -470,10 +468,7 @@ def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach
|
|
|
470
468
|
import time
|
|
471
469
|
data = sessions.start(base_dir, config_path, level_id, engine_overrides,
|
|
472
470
|
**({'auth_context': auth_context} if auth_context is not None else {}))
|
|
473
|
-
result =
|
|
474
|
-
'state': data['state'], **data['game'], 'log_path': data['log_path'],
|
|
475
|
-
'engine_log_path': data.get('engine_log_path'),
|
|
476
|
-
'window_title_hint': 'Minecraft', 'window_verified': False}
|
|
471
|
+
result = sessions.handoff(data)
|
|
477
472
|
if detach:
|
|
478
473
|
return result
|
|
479
474
|
click.echo('会话: ' + data['session'] + ';日志: ' + data['log_path'])
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Manually hosted LAN endpoint in the logged-in Windows desktop."""
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import click
|
|
5
|
+
from ..command_context import OperationCommand, json_output
|
|
6
|
+
from ..mcstudio.discovery import engine_options
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command(cls=OperationCommand)
|
|
10
|
+
@click.option('--host', default='0.0.0.0', show_default=True)
|
|
11
|
+
@click.option('--port', type=click.IntRange(1, 65535), default=18765, show_default=True)
|
|
12
|
+
@click.option('--data-dir', type=click.Path(file_okay=False), help='服务会话与日志目录')
|
|
13
|
+
@engine_options
|
|
14
|
+
def serve_cmd(host, port, data_dir, **engine_overrides):
|
|
15
|
+
"""在当前 Windows 用户桌面启动局域网测试服务;Ctrl+C 清理所属游戏。"""
|
|
16
|
+
if json_output():
|
|
17
|
+
raise click.UsageError('serve 是持续服务,不支持 --json')
|
|
18
|
+
if os.name != 'nt':
|
|
19
|
+
raise click.ClickException('serve 只能在 Windows 交互桌面启动;macOS 请配置 --remote')
|
|
20
|
+
from ..remote.service import GameService, directory_lock
|
|
21
|
+
from ..remote.http_server import GameHTTPServer
|
|
22
|
+
root = Path(data_dir).expanduser().resolve() if data_dir else Path.home()/'.local/share/mcpywrap/remote'
|
|
23
|
+
with directory_lock(root):
|
|
24
|
+
service = GameService(root, {k: v for k, v in engine_overrides.items() if v is not None})
|
|
25
|
+
try:
|
|
26
|
+
with GameHTTPServer((host, port), service, os.environ.get('MCPY_REMOTE_TOKEN')) as server:
|
|
27
|
+
click.echo(f'测试服务: http://{host}:{port}\n数据目录: {root}\n'
|
|
28
|
+
f'访问令牌: {"已启用" if server.token else "未启用"}\n'
|
|
29
|
+
'保持桌面登录且未锁屏;Ctrl+C 停止服务及所属游戏。')
|
|
30
|
+
try:
|
|
31
|
+
server.serve_forever(poll_interval=.2)
|
|
32
|
+
except KeyboardInterrupt:
|
|
33
|
+
click.echo('正在取消输入并清理所属会话…')
|
|
34
|
+
finally:
|
|
35
|
+
service.close()
|
|
@@ -5,18 +5,26 @@ from ..mcstudio import sessions
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
@click.command(cls=OperationCommand, name='status')
|
|
8
|
-
@click.option('--session'
|
|
9
|
-
|
|
8
|
+
@click.option('--session')
|
|
9
|
+
@click.option('--list', 'list_sessions', is_flag=True, help='列举会话,远端可用于找回启动结果')
|
|
10
|
+
def status_cmd(session, list_sessions):
|
|
10
11
|
"""查询会话;running 只表示进程存活。"""
|
|
12
|
+
if bool(session) == list_sessions:
|
|
13
|
+
raise click.UsageError('指定 --session 或 --list 中的一项')
|
|
14
|
+
if list_sessions:
|
|
15
|
+
root = project_dir()/'.runtime/sessions'
|
|
16
|
+
return {'sessions': [sessions.read(project_dir(), p.parent.name)
|
|
17
|
+
for p in sorted(root.glob('*/session.json'))]}
|
|
11
18
|
return sessions.read(project_dir(), session)
|
|
12
19
|
|
|
13
20
|
|
|
14
21
|
@click.command(cls=OperationCommand, name='logs')
|
|
15
22
|
@click.option('--session', required=True)
|
|
16
23
|
@click.option('--tail', type=click.IntRange(1, 10000), default=100, show_default=True)
|
|
17
|
-
|
|
24
|
+
@click.option('--source', type=click.Choice(['game', 'engine', 'worker']), default='game', show_default=True)
|
|
25
|
+
def logs_cmd(session, tail, source):
|
|
18
26
|
"""读取会话日志末尾,不等待新日志。"""
|
|
19
|
-
text = sessions.logs(project_dir(), session, tail)
|
|
27
|
+
text = sessions.logs(project_dir(), session, tail, source)
|
|
20
28
|
if json_output():
|
|
21
29
|
return {'session': session, 'text': text}
|
|
22
30
|
click.echo(text, nl=False)
|
|
@@ -16,6 +16,8 @@ def ui_cmd(mcs_auth=False):
|
|
|
16
16
|
from ..command_context import json_output
|
|
17
17
|
if non_interactive() or json_output():
|
|
18
18
|
raise click.UsageError("Qt 管理页仅供人工使用;Agent 请调用 run、add、remove 等命令")
|
|
19
|
+
if os.name != 'nt':
|
|
20
|
+
raise click.ClickException('此界面仅支持 Windows 人工操作,不支持远程 GUI')
|
|
19
21
|
# 检查项目是否已初始化
|
|
20
22
|
if not config_exists():
|
|
21
23
|
require_project()
|
|
@@ -9,6 +9,30 @@ BINARIES = ('Injector.exe', 'Loader.dll', 'McpyMcsAuth.dll')
|
|
|
9
9
|
PAYLOAD = Path(__file__).with_name('bridge_payload')
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def inspect_bridge():
|
|
13
|
+
"""只检查组件文件;不复制缓存、不读取身份、不改变证书信任。"""
|
|
14
|
+
override = os.environ.get('MCPY_MCS_BRIDGE_DIR')
|
|
15
|
+
bundled = not override and (PAYLOAD/'manifest.json').is_file()
|
|
16
|
+
directory = (Path(override).expanduser().resolve() if override else PAYLOAD if bundled
|
|
17
|
+
else Path.home()/'.local/share/mcpywrap/mcs-auth-bridge')
|
|
18
|
+
result = {'component_available': False, 'directory': str(directory),
|
|
19
|
+
'source': 'override' if override else 'package' if bundled else 'development',
|
|
20
|
+
'integrity_verified': False, 'trust_verified': False, 'login_verified': False,
|
|
21
|
+
'error': None, 'hint': None}
|
|
22
|
+
try:
|
|
23
|
+
if bundled:
|
|
24
|
+
payload_manifest()
|
|
25
|
+
result['integrity_verified'] = True
|
|
26
|
+
missing = [name for name in BINARIES if not (directory/name).is_file()]
|
|
27
|
+
if missing:
|
|
28
|
+
raise ValueError('缺少登录组件: ' + ', '.join(missing))
|
|
29
|
+
result['component_available'] = True
|
|
30
|
+
except (OSError, ValueError, KeyError, TypeError) as exc:
|
|
31
|
+
result.update(error=str(exc), hint='使用包含桥接组件的正式发布包;Git/可编辑安装需另行构建组件,'
|
|
32
|
+
'并通过 MCPY_MCS_BRIDGE_DIR 指定目录。组件存在不表示已登录或系统策略允许启用。')
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
|
|
12
36
|
def digest(path):
|
|
13
37
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
14
38
|
|
|
Binary file
|
|
Binary file
|