mcpywrap 0.3.1__tar.gz → 0.3.2__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.1 → mcpywrap-0.3.2}/CHANGELOG.md +11 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/MANIFEST.in +1 -0
- {mcpywrap-0.3.1/mcpywrap.egg-info → mcpywrap-0.3.2}/PKG-INFO +34 -1
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/README.md +32 -0
- mcpywrap-0.3.2/docs/development.md +24 -0
- mcpywrap-0.3.2/docs/engine-discovery.md +55 -0
- mcpywrap-0.3.2/docs/local-dependencies.md +30 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/__init__.py +1 -1
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/cli.py +21 -3
- mcpywrap-0.3.2/mcpywrap/command_context.py +130 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/add_cmd.py +5 -3
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/build_cmd.py +8 -8
- mcpywrap-0.3.2/mcpywrap/commands/default_cmd.py +14 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/dependency_prompt.py +2 -1
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/dev_cmd.py +9 -4
- mcpywrap-0.3.2/mcpywrap/commands/doctor_cmd.py +33 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/edit_cmd.py +14 -8
- mcpywrap-0.3.2/mcpywrap/commands/init_cmd.py +42 -0
- mcpywrap-0.3.2/mcpywrap/commands/mod_cmd.py +45 -0
- mcpywrap-0.3.2/mcpywrap/commands/modsdk_cmd.py +26 -0
- mcpywrap-0.3.2/mcpywrap/commands/package_cmd.py +79 -0
- mcpywrap-0.3.2/mcpywrap/commands/publish_cmd.py +51 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/remove_cmd.py +12 -5
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/run_cmd.py +52 -24
- mcpywrap-0.3.2/mcpywrap/commands/session_cmd.py +29 -0
- mcpywrap-0.3.2/mcpywrap/commands/sync_cmd.py +33 -0
- mcpywrap-0.3.2/mcpywrap/commands/ui_cmd.py +25 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/config.py +24 -51
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/dependencies.py +1 -1
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/editor.py +3 -6
- mcpywrap-0.3.2/mcpywrap/mcstudio/file_logs.py +65 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/game.py +9 -6
- mcpywrap-0.3.2/mcpywrap/mcstudio/log_protocol.py +52 -0
- mcpywrap-0.3.2/mcpywrap/mcstudio/processes.py +28 -0
- mcpywrap-0.3.2/mcpywrap/mcstudio/session_worker.py +55 -0
- mcpywrap-0.3.2/mcpywrap/mcstudio/sessions.py +106 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/studio_server.py +7 -51
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/minecraft/netease_modsdk.py +4 -4
- mcpywrap-0.3.2/mcpywrap/project_init.py +73 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/utils/project_setup.py +3 -3
- {mcpywrap-0.3.1 → mcpywrap-0.3.2/mcpywrap.egg-info}/PKG-INFO +34 -1
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap.egg-info/SOURCES.txt +20 -1
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap.egg-info/requires.txt +1 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/pyproject.toml +2 -1
- mcpywrap-0.3.2/skills/mcpywrap/SKILL.md +89 -0
- mcpywrap-0.3.2/skills/mcpywrap/agents/openai.yaml +4 -0
- mcpywrap-0.3.2/skills/mcpywrap/references/troubleshooting.md +28 -0
- mcpywrap-0.3.2/skills/mcpywrap/scripts/bootstrap.ps1 +65 -0
- mcpywrap-0.3.2/skills/mcpywrap/scripts/game_window.py +442 -0
- mcpywrap-0.3.2/skills/mcpywrap/scripts/smoke.py +84 -0
- mcpywrap-0.3.2/tests/test_automation.py +301 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/tests/test_engine_discovery.py +8 -6
- mcpywrap-0.3.2/tests/test_game_window_skill.py +226 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/tests/test_local_dependencies.py +2 -2
- mcpywrap-0.3.2/tests/test_package.py +159 -0
- mcpywrap-0.3.1/docs/development.md +0 -15
- mcpywrap-0.3.1/docs/engine-discovery.md +0 -72
- mcpywrap-0.3.1/docs/local-dependencies.md +0 -65
- mcpywrap-0.3.1/mcpywrap/commands/default_cmd.py +0 -76
- mcpywrap-0.3.1/mcpywrap/commands/doctor_cmd.py +0 -42
- mcpywrap-0.3.1/mcpywrap/commands/init_cmd.py +0 -628
- mcpywrap-0.3.1/mcpywrap/commands/mod_cmd.py +0 -26
- mcpywrap-0.3.1/mcpywrap/commands/modsdk_cmd.py +0 -65
- mcpywrap-0.3.1/mcpywrap/commands/publish_cmd.py +0 -45
- mcpywrap-0.3.1/mcpywrap/commands/ui_cmd.py +0 -22
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/LICENSE +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/docs/releasing.md +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/__main__.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/AddonsPack.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/MapPack.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/__init__.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/assembly.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/dependency_manager.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/file_merge.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/project_builder.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/builders/watcher.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/commands/__init__.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/custom_packaging.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/__init__.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/discovery.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/mcs.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/mcstudio/symlinks.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/minecraft/__init__.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/minecraft/addons.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/minecraft/level_dat.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/minecraft/map.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/minecraft/template/mod_template.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/ui/__init__.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/ui/project_ui.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/utils/__init__.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/utils/pip_error_parser.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/utils/print_guide.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap/utils/utils.py +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/setup.cfg +0 -0
- {mcpywrap-0.3.1 → mcpywrap-0.3.2}/tests/manual_integration.py +0 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
- 新增 `package`,构建项目及依赖并生成分发 ZIP,失败保留已有产物。
|
|
6
|
+
- CLI 共用 `--project`、`--non-interactive`、`--json` 与明确退出码;初始化、Mod 模板和 SDK 安装可通过参数操作。
|
|
7
|
+
- 新增 `sync`;发布命令改用标准 Python 构建及 Twine,预检凭据并只上传本次构建产物。
|
|
8
|
+
- 新增无辅助 GUI 的游戏会话与 `status/logs/stop`,核对进程身份、提前监听日志、分离引擎输出,并兼容中文 SDK 日志。
|
|
9
|
+
- 提供可单独安装的标准 Skill,包含 uv 安装、冒烟验证、游戏截图和键盘输入脚本;支持组合键、扫描码和有界长按,异常时释放按键。
|
|
10
|
+
- 精简操作文档,补充 Skill 安装入口;Computer Use 只接手复杂游戏画面操作,不接手 Qt 管理页。
|
|
11
|
+
- **使用变化**:非交互环境缺少参数立即失败;初始化不再隐式安装项目或 SDK,分别使用 `sync --install`、`modsdk`;`ui` 仅供人工交互。
|
|
12
|
+
- 112 项回归测试通过,并完成本机游戏日志、截图、组合移动和会话停止验证。
|
|
13
|
+
|
|
3
14
|
## 0.3.1
|
|
4
15
|
|
|
5
16
|
- 完善游戏引擎自动发现:优先检查当前用户注册表的两个视图,失败后搜索固定磁盘中的标准下载目录。
|
|
@@ -2,6 +2,7 @@ include LICENSE README.md CHANGELOG.md
|
|
|
2
2
|
include docs/releasing.md
|
|
3
3
|
include docs/local-dependencies.md docs/engine-discovery.md docs/development.md
|
|
4
4
|
recursive-include tests *.py
|
|
5
|
+
recursive-include skills *.md *.yaml *.ps1 *.py
|
|
5
6
|
prune test
|
|
6
7
|
prune .git
|
|
7
8
|
global-exclude __pycache__ *.py[cod] *.cppconfig *.log
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: A wrapper for Minecraft China Edition Addons management
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,6 +12,7 @@ Requires-Python: >=3.9
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: pip>=21
|
|
15
|
+
Requires-Dist: build>=1.0
|
|
15
16
|
Requires-Dist: click>=8.0.0
|
|
16
17
|
Requires-Dist: watchdog>=2.0.0
|
|
17
18
|
Requires-Dist: twine>=3.4.0
|
|
@@ -87,6 +88,7 @@ mcpy add --path "../shared-addon"
|
|
|
87
88
|
| 命令 | 用途 |
|
|
88
89
|
|---|---|
|
|
89
90
|
| `mcpy build` | 将项目和依赖构建到配置的输出目录 |
|
|
91
|
+
| `mcpy package` | 构建项目和依赖,在 `dist` 中生成可分发 ZIP |
|
|
90
92
|
| `mcpy dev` | 监控 Addon 源码与依赖变化,持续更新构建结果 |
|
|
91
93
|
| `mcpy mod` | 通过向导创建 Python Mod 框架 |
|
|
92
94
|
| `mcpy modsdk` | 管理网易 ModSDK |
|
|
@@ -96,6 +98,14 @@ mcpy add --path "../shared-addon"
|
|
|
96
98
|
|
|
97
99
|
`mcpy run` 默认复用最近创建的实例。构建时主项目内容优先于依赖;修改依赖声明后,请重新启动 `mcpy dev`。
|
|
98
100
|
|
|
101
|
+
### 打包分发
|
|
102
|
+
|
|
103
|
+
在项目根目录执行 `mcpy package`,会先构建项目及 Python 包/本地 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
|
|
104
|
+
|
|
105
|
+
Addon ZIP 内为 `<项目名>_bp/`、`<项目名>_rp/`(仅包含实际构建出的包);地图 ZIP 根目录直接包含存档数据、行为包、资源包及世界包配置。地图默认保留独立包,使用 `mcpy package --merge`(或 `-m`)按构建规则合并依赖资源。构建产物中的空目录会保留。
|
|
106
|
+
|
|
107
|
+
重复打包成功后会替换同名 ZIP;失败时保留已有 ZIP,临时文件自动清理。
|
|
108
|
+
|
|
99
109
|
## 游戏启动与排查
|
|
100
110
|
|
|
101
111
|
通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
|
|
@@ -114,6 +124,29 @@ mcpy run --engine-version 3.10.0.420447
|
|
|
114
124
|
|
|
115
125
|
已有实例默认保留原引擎版本;显式指定版本可以切换。自定义路径、项目级设置和环境变量的用法见[引擎配置参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/engine-discovery.md)。
|
|
116
126
|
|
|
127
|
+
## AI Agent 使用
|
|
128
|
+
|
|
129
|
+
仓库提供标准 [mcpywrap Skill](https://github.com/EaseCation/mcpywrap/tree/main/skills/mcpywrap),帮助 Agent 安装工具、管理依赖、
|
|
130
|
+
构建打包、启动游戏和检查日志,并通过自带脚本截图、发送组合键与模拟移动。
|
|
131
|
+
|
|
132
|
+
**安装 Skill**:可对支持 Skill 安装的 Agent 说:
|
|
133
|
+
|
|
134
|
+
> 请安装 GitHub 仓库 EaseCation/mcpywrap 中 skills/mcpywrap 目录下的 Skill。
|
|
135
|
+
|
|
136
|
+
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
137
|
+
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
138
|
+
Skill 中的 `scripts/bootstrap.ps1` 可使用已有 uv 安装 CLI,并检查是否具备所需命令能力。
|
|
139
|
+
请使用 mcpywrap 0.3.2 或更高版本;需要固定组合时,可从 `v0.3.2` 标签安装对应 Skill。
|
|
140
|
+
|
|
141
|
+
安装后可直接描述任务:
|
|
142
|
+
|
|
143
|
+
- “使用 mcpywrap 为这个 Addon 添加本地依赖,并生成分发 ZIP。”
|
|
144
|
+
- “启动这个项目,不弹日志界面,检查客户端和服务端的加载日志。”
|
|
145
|
+
- “启动游戏并截图,模拟组合按键移动,检查 F11 输入模式和 F3 调试信息层。”
|
|
146
|
+
|
|
147
|
+
Agent 通过 CLI 管理项目,Skill 自带游戏截图与键盘输入脚本;复杂游戏交互交给 Computer Use。Qt 管理与模板界面用于人工操作。
|
|
148
|
+
常用入口:`mcpy --project <目录> --non-interactive <命令> --json`。
|
|
149
|
+
|
|
117
150
|
## 更多信息
|
|
118
151
|
|
|
119
152
|
- 运行 `mcpy <命令> --help` 查看该命令的选项。
|
|
@@ -61,6 +61,7 @@ mcpy add --path "../shared-addon"
|
|
|
61
61
|
| 命令 | 用途 |
|
|
62
62
|
|---|---|
|
|
63
63
|
| `mcpy build` | 将项目和依赖构建到配置的输出目录 |
|
|
64
|
+
| `mcpy package` | 构建项目和依赖,在 `dist` 中生成可分发 ZIP |
|
|
64
65
|
| `mcpy dev` | 监控 Addon 源码与依赖变化,持续更新构建结果 |
|
|
65
66
|
| `mcpy mod` | 通过向导创建 Python Mod 框架 |
|
|
66
67
|
| `mcpy modsdk` | 管理网易 ModSDK |
|
|
@@ -70,6 +71,14 @@ mcpy add --path "../shared-addon"
|
|
|
70
71
|
|
|
71
72
|
`mcpy run` 默认复用最近创建的实例。构建时主项目内容优先于依赖;修改依赖声明后,请重新启动 `mcpy dev`。
|
|
72
73
|
|
|
74
|
+
### 打包分发
|
|
75
|
+
|
|
76
|
+
在项目根目录执行 `mcpy package`,会先构建项目及 Python 包/本地 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
|
|
77
|
+
|
|
78
|
+
Addon ZIP 内为 `<项目名>_bp/`、`<项目名>_rp/`(仅包含实际构建出的包);地图 ZIP 根目录直接包含存档数据、行为包、资源包及世界包配置。地图默认保留独立包,使用 `mcpy package --merge`(或 `-m`)按构建规则合并依赖资源。构建产物中的空目录会保留。
|
|
79
|
+
|
|
80
|
+
重复打包成功后会替换同名 ZIP;失败时保留已有 ZIP,临时文件自动清理。
|
|
81
|
+
|
|
73
82
|
## 游戏启动与排查
|
|
74
83
|
|
|
75
84
|
通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
|
|
@@ -88,6 +97,29 @@ mcpy run --engine-version 3.10.0.420447
|
|
|
88
97
|
|
|
89
98
|
已有实例默认保留原引擎版本;显式指定版本可以切换。自定义路径、项目级设置和环境变量的用法见[引擎配置参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/engine-discovery.md)。
|
|
90
99
|
|
|
100
|
+
## AI Agent 使用
|
|
101
|
+
|
|
102
|
+
仓库提供标准 [mcpywrap Skill](https://github.com/EaseCation/mcpywrap/tree/main/skills/mcpywrap),帮助 Agent 安装工具、管理依赖、
|
|
103
|
+
构建打包、启动游戏和检查日志,并通过自带脚本截图、发送组合键与模拟移动。
|
|
104
|
+
|
|
105
|
+
**安装 Skill**:可对支持 Skill 安装的 Agent 说:
|
|
106
|
+
|
|
107
|
+
> 请安装 GitHub 仓库 EaseCation/mcpywrap 中 skills/mcpywrap 目录下的 Skill。
|
|
108
|
+
|
|
109
|
+
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
110
|
+
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
111
|
+
Skill 中的 `scripts/bootstrap.ps1` 可使用已有 uv 安装 CLI,并检查是否具备所需命令能力。
|
|
112
|
+
请使用 mcpywrap 0.3.2 或更高版本;需要固定组合时,可从 `v0.3.2` 标签安装对应 Skill。
|
|
113
|
+
|
|
114
|
+
安装后可直接描述任务:
|
|
115
|
+
|
|
116
|
+
- “使用 mcpywrap 为这个 Addon 添加本地依赖,并生成分发 ZIP。”
|
|
117
|
+
- “启动这个项目,不弹日志界面,检查客户端和服务端的加载日志。”
|
|
118
|
+
- “启动游戏并截图,模拟组合按键移动,检查 F11 输入模式和 F3 调试信息层。”
|
|
119
|
+
|
|
120
|
+
Agent 通过 CLI 管理项目,Skill 自带游戏截图与键盘输入脚本;复杂游戏交互交给 Computer Use。Qt 管理与模板界面用于人工操作。
|
|
121
|
+
常用入口:`mcpy --project <目录> --non-interactive <命令> --json`。
|
|
122
|
+
|
|
91
123
|
## 更多信息
|
|
92
124
|
|
|
93
125
|
- 运行 `mcpy <命令> --help` 查看该命令的选项。
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# 维护者:开发与验证
|
|
2
|
+
|
|
3
|
+
```powershell
|
|
4
|
+
python -m unittest discover -s tests -v
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
自动化测试使用临时目录、模拟安装/发布、Qt 离屏界面和模拟游戏进程,不启动真实游戏。
|
|
8
|
+
CLI 使用调用上下文传递项目目录;底层服务接受明确路径。不要为 GUI 切换全局工作目录。
|
|
9
|
+
|
|
10
|
+
有限命令返回数据或抛出错误,公共 CLI 层负责 JSON、stderr 和退出码。
|
|
11
|
+
新增命令使用相同入口,避免打印错误后返回成功。
|
|
12
|
+
|
|
13
|
+
Skill 的 bootstrap、smoke 脚本只使用公开 CLI;Skill 的 game_window 脚本负责绑定会话的截图和键盘输入;复杂交互使用环境的 Computer Use。
|
|
14
|
+
真实游戏验证请使用独立测试项目:
|
|
15
|
+
|
|
16
|
+
```powershell
|
|
17
|
+
python skills/mcpywrap/scripts/smoke.py --project D:\mods\test --game --expect-log "服务端已加载" --expect-log "客户端已加载"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
脚本结束后停止自己创建的会话;日志和产物留在项目中。
|
|
21
|
+
需要手动截图验证时直接运行 `run --no-gui --detach --json`,保存会话 ID,操作完后显式 stop。
|
|
22
|
+
历史验收记录仅用于追溯,不能代替当前版本测试。
|
|
23
|
+
|
|
24
|
+
发布见 [发布流程](releasing.md)。
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 游戏启动与排查
|
|
2
|
+
|
|
3
|
+
先检查本机环境:
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
mcpy doctor
|
|
7
|
+
mcpy doctor --json
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
诊断分别显示游戏、编辑器、Safaia 是否就绪。编辑器缺失不等于无法运行游戏。
|
|
11
|
+
默认优先使用 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录;不完整的版本会被跳过。
|
|
12
|
+
|
|
13
|
+
## 找不到游戏
|
|
14
|
+
|
|
15
|
+
确认已通过 MC Studio 下载引擎;自定义位置可指定下载目录或 EXE:
|
|
16
|
+
|
|
17
|
+
```powershell
|
|
18
|
+
mcpy doctor --mcs-download-path 'D:\MCStudioDownload'
|
|
19
|
+
mcpy run --game-executable 'D:\MCStudioDownload\game\MinecraftPE_Netease\3.10.0.420447\Minecraft.Windows.exe'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
选择某个完整版本:
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
mcpy run --engine-version 3.10.0.420447
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
已有实例保留原版本;显式指定可切换版本。`3.10` 不是 `3.10.*` 通配符;不存在时明确报错。
|
|
29
|
+
|
|
30
|
+
## 保存配置
|
|
31
|
+
|
|
32
|
+
| 项目中的 `[tool.mcpywrap]` 字段 | 本机环境变量 |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `game_executable_path` | `MCPY_GAME_EXECUTABLE` |
|
|
35
|
+
| `mcs_download_path` | `MCPY_MCS_DOWNLOAD_PATH` |
|
|
36
|
+
| `engine_version` | `MCPY_ENGINE_VERSION` |
|
|
37
|
+
|
|
38
|
+
命令行优先于环境变量,环境变量优先于项目配置。团队通常只提交版本,本机路径放在环境变量。
|
|
39
|
+
项目配置的相对路径以项目为基准;CLI 和环境变量的相对路径以调用目录为基准。
|
|
40
|
+
|
|
41
|
+
EXE 不在版本目录时,需要另外声明 `engine_version`;这不验证二进制内部版本。
|
|
42
|
+
独立 EXE 仍需要下载目录中的皮肤和游戏用户数据,缺项按 doctor 的实际路径提示补齐。
|
|
43
|
+
同时配置 EXE 与下载目录时不能指向不同的标准安装。
|
|
44
|
+
|
|
45
|
+
## 不打开辅助界面
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
mcpy --project D:\mods\demo --non-interactive run --no-gui --detach --json
|
|
49
|
+
mcpy --project D:\mods\demo logs --session <id> --tail 100 --json
|
|
50
|
+
mcpy --project D:\mods\demo stop --session <id> --json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
只打开游戏窗口,日志写入返回的文件。`running` 表示进程存活,加载是否成功应查看项目日志或游戏画面。
|
|
54
|
+
Computer Use 只接手游戏画面;Qt 管理、模板及日志页留给人工操作。
|
|
55
|
+
本功能不包含第三方网络服 IP/端口连接。
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 复用本地 Addon
|
|
2
|
+
|
|
3
|
+
已有的 Addon 可以直接作为依赖,不需要先初始化或安装。
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
mcpy add --path "../shared-addon"
|
|
7
|
+
mcpy remove --path "../shared-addon"
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
请选择 Addon 根目录,例如:
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
shared-addon/
|
|
14
|
+
behavior_pack/manifest.json
|
|
15
|
+
resource_pack/manifest.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
可以只有行为包或资源包;目录名也支持工具识别的 `BehaviorPack*`、`ResourcePack*` 等名称。
|
|
19
|
+
不支持直接引用单个包目录、地图、普通源码目录,或同一 Addon 中多个同类型包。
|
|
20
|
+
|
|
21
|
+
引用保存在 `tool.mcpywrap.local_dependencies`,相对路径以声明它的项目为基准。
|
|
22
|
+
移除引用不会删除源目录。已有 mcpywrap 项目的子依赖会一并解析;循环依赖或失效路径会报错。
|
|
23
|
+
|
|
24
|
+
需要发布的 Python 包使用 `mcpy add "package-name>=1.0"`。本地路径不会自动转换为可分发的包依赖;
|
|
25
|
+
共享项目时需同步引用目录,或把组件发布为 Python 包。本地引用的包依赖未安装时,按警告显式安装。
|
|
26
|
+
|
|
27
|
+
构建时主项目优先于依赖;同级依赖按声明顺序处理,后者覆盖重复内容。
|
|
28
|
+
这不代表游戏运行时的资源包优先级。修改依赖声明后重启 `mcpy dev`。
|
|
29
|
+
|
|
30
|
+
遇到结构错误先核对目录和清单;查看参数使用 `mcpy add --help`。
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
|
+
from . import __version__
|
|
5
|
+
from .command_context import OperationGroup, configure_context
|
|
4
6
|
|
|
5
7
|
from .commands.run_cmd import run_cmd
|
|
6
8
|
from .commands.init_cmd import init_cmd
|
|
@@ -8,6 +10,7 @@ from .commands.add_cmd import add_cmd
|
|
|
8
10
|
from .commands.remove_cmd import remove_cmd
|
|
9
11
|
from .commands.build_cmd import build_cmd
|
|
10
12
|
from .commands.dev_cmd import dev_cmd
|
|
13
|
+
from .commands.package_cmd import package_cmd
|
|
11
14
|
from .commands.publish_cmd import publish_cmd
|
|
12
15
|
from .commands.default_cmd import default_cmd
|
|
13
16
|
from .commands.modsdk_cmd import modsdk_cmd
|
|
@@ -15,16 +18,26 @@ from .commands.mod_cmd import mod_cmd
|
|
|
15
18
|
from .commands.edit_cmd import edit_cmd
|
|
16
19
|
from .commands.ui_cmd import ui_cmd
|
|
17
20
|
from .commands.doctor_cmd import doctor_cmd
|
|
21
|
+
from .commands.sync_cmd import sync_cmd
|
|
22
|
+
from .commands.session_cmd import status_cmd, logs_cmd, stop_cmd
|
|
18
23
|
|
|
19
24
|
|
|
20
|
-
@click.group(invoke_without_command=True)
|
|
25
|
+
@click.group(cls=OperationGroup, invoke_without_command=True)
|
|
26
|
+
@click.option("--project", type=click.Path(file_okay=False), help="项目目录,默认当前目录")
|
|
27
|
+
@click.option("--non-interactive", is_flag=True, help="不读取终端输入")
|
|
28
|
+
@click.option("--json", "json_output", is_flag=True, help="输出 JSON 结果")
|
|
29
|
+
@click.version_option(__version__)
|
|
21
30
|
@click.pass_context
|
|
22
|
-
def cli(ctx):
|
|
31
|
+
def cli(ctx, project, non_interactive, json_output):
|
|
23
32
|
"""mcpywrap - 《我的世界》中国版 依赖管理与项目构建工具"""
|
|
33
|
+
configure_context(project, non_interactive)
|
|
24
34
|
# 如果没有提供子命令,则运行 default_cmd
|
|
25
35
|
if ctx.invoked_subcommand is None:
|
|
26
36
|
# 导入并运行默认命令
|
|
27
|
-
|
|
37
|
+
from .command_context import non_interactive as unattended
|
|
38
|
+
if unattended():
|
|
39
|
+
raise click.UsageError("请指定子命令,例如 doctor、init 或 sync")
|
|
40
|
+
return default_cmd.callback()
|
|
28
41
|
|
|
29
42
|
# 注册其他子命令
|
|
30
43
|
cli.add_command(modsdk_cmd, name='modsdk')
|
|
@@ -33,12 +46,17 @@ cli.add_command(add_cmd, name='add')
|
|
|
33
46
|
cli.add_command(remove_cmd, name='remove')
|
|
34
47
|
cli.add_command(build_cmd, name='build')
|
|
35
48
|
cli.add_command(dev_cmd, name='dev')
|
|
49
|
+
cli.add_command(package_cmd, name='package')
|
|
36
50
|
cli.add_command(publish_cmd, name='publish')
|
|
37
51
|
cli.add_command(mod_cmd, name='mod')
|
|
38
52
|
cli.add_command(run_cmd, name='run')
|
|
39
53
|
cli.add_command(edit_cmd, name='edit')
|
|
40
54
|
cli.add_command(ui_cmd, name='ui')
|
|
41
55
|
cli.add_command(doctor_cmd, name='doctor')
|
|
56
|
+
cli.add_command(sync_cmd, name='sync')
|
|
57
|
+
cli.add_command(status_cmd)
|
|
58
|
+
cli.add_command(logs_cmd)
|
|
59
|
+
cli.add_command(stop_cmd)
|
|
42
60
|
|
|
43
61
|
if __name__ == '__main__':
|
|
44
62
|
cli()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""CLI 的项目上下文、交互策略和统一输出;不改变进程工作目录。"""
|
|
2
|
+
import contextlib
|
|
3
|
+
import contextvars
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import traceback
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class CommandContext:
|
|
17
|
+
project: Path
|
|
18
|
+
non_interactive: bool = False
|
|
19
|
+
json_output: bool = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_context = contextvars.ContextVar('mcpy_context', default=None)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def project_dir():
|
|
26
|
+
current = _context.get()
|
|
27
|
+
return current.project if current else Path.cwd()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def non_interactive():
|
|
31
|
+
current = _context.get()
|
|
32
|
+
return bool(current and current.non_interactive) or not sys.stdin.isatty()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def json_output():
|
|
36
|
+
current = _context.get()
|
|
37
|
+
return bool(current and current.json_output)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@contextlib.contextmanager
|
|
41
|
+
def project_scope(path):
|
|
42
|
+
previous = _context.get()
|
|
43
|
+
token = _context.set(CommandContext(Path(path).resolve(),
|
|
44
|
+
previous.non_interactive if previous else False,
|
|
45
|
+
previous.json_output if previous else False))
|
|
46
|
+
try:
|
|
47
|
+
yield
|
|
48
|
+
finally:
|
|
49
|
+
_context.reset(token)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def require_project():
|
|
53
|
+
if not (project_dir() / 'pyproject.toml').is_file():
|
|
54
|
+
raise click.ClickException('未找到 pyproject.toml;请先执行 mcpy init --type addon 或 --type map')
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class OperationCommand(click.Command):
|
|
58
|
+
def __init__(self, *args, **kwargs):
|
|
59
|
+
super().__init__(*args, **kwargs)
|
|
60
|
+
self.params.append(click.Option(['--json', 'json_output'], is_flag=True,
|
|
61
|
+
help='输出单个 JSON 结果'))
|
|
62
|
+
|
|
63
|
+
def invoke(self, ctx):
|
|
64
|
+
ctx.params.pop('json_output', None)
|
|
65
|
+
return super().invoke(ctx)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class OperationGroup(click.Group):
|
|
69
|
+
def main(self, args=None, prog_name=None, complete_var=None, standalone_mode=True, **extra):
|
|
70
|
+
args = list(sys.argv[1:] if args is None else args)
|
|
71
|
+
structured = '--json' in args and '--help' not in args
|
|
72
|
+
output = sys.stdout
|
|
73
|
+
state = CommandContext(Path.cwd(), '--non-interactive' in args, structured)
|
|
74
|
+
token = _context.set(state)
|
|
75
|
+
code, result = 0, None
|
|
76
|
+
try:
|
|
77
|
+
with contextlib.redirect_stdout(sys.stderr) if structured else contextlib.nullcontext():
|
|
78
|
+
try:
|
|
79
|
+
result = super().main(args=args, prog_name=prog_name, complete_var=complete_var,
|
|
80
|
+
standalone_mode=False, **extra)
|
|
81
|
+
if isinstance(result, dict) and result.get('ok') is False:
|
|
82
|
+
code = 1
|
|
83
|
+
except click.ClickException as exc:
|
|
84
|
+
code = exc.exit_code
|
|
85
|
+
result = {'ok': False, 'error': exc.format_message(),
|
|
86
|
+
'hint': '使用 mcpy <命令> --help 查看所需参数。'}
|
|
87
|
+
except (KeyboardInterrupt, click.Abort):
|
|
88
|
+
code = 130
|
|
89
|
+
result = {'ok': False, 'error': '操作已中断', 'hint': None}
|
|
90
|
+
except (ValueError, OSError) as exc:
|
|
91
|
+
code = 1
|
|
92
|
+
result = {'ok': False, 'error': str(exc), 'hint': '请核对项目配置和相关文件路径。'}
|
|
93
|
+
except Exception as exc:
|
|
94
|
+
code = 1
|
|
95
|
+
with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', prefix='mcpy-error-',
|
|
96
|
+
suffix='.log', delete=False) as log:
|
|
97
|
+
traceback.print_exc(file=log)
|
|
98
|
+
result = {'ok': False, 'error': str(exc), 'hint': f'意外错误详情: {log.name}'}
|
|
99
|
+
if isinstance(result, int):
|
|
100
|
+
code = result
|
|
101
|
+
result = None
|
|
102
|
+
if structured:
|
|
103
|
+
data = {'ok': code == 0, 'error': None, 'hint': None}
|
|
104
|
+
if isinstance(result, dict):
|
|
105
|
+
data.update(result)
|
|
106
|
+
click.echo(json.dumps(data, ensure_ascii=False), file=output)
|
|
107
|
+
elif code:
|
|
108
|
+
click.echo('错误: ' + str((result or {}).get('error') or '操作失败'), err=True)
|
|
109
|
+
if (result or {}).get('hint'):
|
|
110
|
+
click.echo(result['hint'], err=True)
|
|
111
|
+
elif isinstance(result, dict):
|
|
112
|
+
# 保留命令原有进度输出,为交接和产物结果补充可读信息。
|
|
113
|
+
for key, value in result.items():
|
|
114
|
+
if key not in ('ok', 'error', 'hint') and value is not None:
|
|
115
|
+
click.echo(f'{key}: {value}')
|
|
116
|
+
finally:
|
|
117
|
+
_context.reset(token)
|
|
118
|
+
if standalone_mode:
|
|
119
|
+
raise SystemExit(code)
|
|
120
|
+
if code:
|
|
121
|
+
raise click.exceptions.Exit(code)
|
|
122
|
+
return result
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def configure_context(project, interactive_disabled):
|
|
126
|
+
current = _context.get()
|
|
127
|
+
current.project = Path(project or os.getcwd()).expanduser().resolve()
|
|
128
|
+
current.non_interactive = interactive_disabled or not sys.stdin.isatty()
|
|
129
|
+
if not current.project.is_dir():
|
|
130
|
+
raise click.UsageError(f'项目目录不存在: {current.project}')
|
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
import click
|
|
5
|
+
from ..command_context import OperationCommand, project_dir as current_project, non_interactive, require_project
|
|
5
6
|
from ..dependencies import DependencyDeclaration, DependencyError, DependencyService
|
|
6
7
|
from .dependency_prompt import prompt_dependency, require_interactive
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
@click.command()
|
|
10
|
+
@click.command(cls=OperationCommand)
|
|
10
11
|
@click.argument('package', required=False)
|
|
11
12
|
@click.option('--path', 'local_path', help='直接引用本地 Addon 根目录,不安装目录')
|
|
12
13
|
def add_cmd(package, local_path):
|
|
13
14
|
"""添加依赖;不带参数时进入类型选择向导。"""
|
|
14
15
|
if package is not None and local_path is not None:
|
|
15
16
|
raise click.UsageError('包名与 --path 不能同时使用')
|
|
16
|
-
if not (
|
|
17
|
+
if not (current_project() / 'pyproject.toml').exists():
|
|
17
18
|
raise click.ClickException('项目尚未初始化,请先运行 mcpy init')
|
|
18
19
|
try:
|
|
19
20
|
if package is None and local_path is None:
|
|
@@ -21,7 +22,7 @@ def add_cmd(package, local_path):
|
|
|
21
22
|
entry = prompt_dependency()
|
|
22
23
|
else:
|
|
23
24
|
entry = DependencyDeclaration('local' if local_path is not None else 'package', local_path if local_path is not None else package)
|
|
24
|
-
service = DependencyService(
|
|
25
|
+
service = DependencyService(str(current_project()))
|
|
25
26
|
if entry.kind == 'local':
|
|
26
27
|
changed, warnings = service.add_local(entry.value)
|
|
27
28
|
for warning in warnings:
|
|
@@ -30,5 +31,6 @@ def add_cmd(package, local_path):
|
|
|
30
31
|
click.echo(f'正在安装 {entry.value}...')
|
|
31
32
|
changed = service.add_package(entry.value)
|
|
32
33
|
click.secho('依赖已添加' if changed else '依赖已存在', fg='green')
|
|
34
|
+
return {'changed': changed, 'dependency': entry.value, 'source': entry.kind, 'warnings': warnings if entry.kind == 'local' else []}
|
|
33
35
|
except (DependencyError, OSError) as exc:
|
|
34
36
|
raise click.ClickException(str(exc)) from exc
|
|
@@ -4,22 +4,21 @@
|
|
|
4
4
|
"""
|
|
5
5
|
import os
|
|
6
6
|
import click
|
|
7
|
+
from ..command_context import OperationCommand, project_dir as current_project, non_interactive, require_project
|
|
7
8
|
from ..config import config_exists, get_mcpywrap_config, get_project_type, ensure_map_setuptools_sync
|
|
8
9
|
from ..builders.project_builder import AddonProjectBuilder, MapProjectBuilder
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
base_dir = os.getcwd()
|
|
12
12
|
|
|
13
|
-
@click.command()
|
|
13
|
+
@click.command(cls=OperationCommand)
|
|
14
14
|
@click.option('--merge', '-m', is_flag=True, help='强制合并所有资源文件')
|
|
15
15
|
def build_cmd(merge):
|
|
16
16
|
"""构建为 MCStudio 工程"""
|
|
17
17
|
if not config_exists():
|
|
18
|
-
|
|
19
|
-
return False
|
|
18
|
+
require_project()
|
|
20
19
|
|
|
21
20
|
# 确保 map 项目的 setuptools 配置同步
|
|
22
|
-
ensure_map_setuptools_sync(interactive=
|
|
21
|
+
ensure_map_setuptools_sync(interactive=False)
|
|
23
22
|
|
|
24
23
|
# 获取mcpywrap特定配置
|
|
25
24
|
mcpywrap_config = get_mcpywrap_config()
|
|
@@ -28,14 +27,15 @@ def build_cmd(merge):
|
|
|
28
27
|
|
|
29
28
|
if not target_dir:
|
|
30
29
|
click.secho('❌ 错误: 配置文件中未找到target_dir。请手动添加。', fg="red")
|
|
31
|
-
|
|
30
|
+
raise click.ClickException("缺少 target_dir,请设置 tool.mcpywrap.target_dir")
|
|
32
31
|
|
|
33
32
|
# 转换为绝对路径
|
|
34
|
-
target_dir = os.path.normpath(os.path.join(
|
|
33
|
+
target_dir = os.path.normpath(os.path.join(str(current_project()), target_dir))
|
|
35
34
|
|
|
36
35
|
# 实际构建
|
|
37
|
-
if not build(
|
|
36
|
+
if not build(str(current_project()), target_dir, force_merge=merge):
|
|
38
37
|
raise click.ClickException('构建失败')
|
|
38
|
+
return {'output': target_dir}
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def build(source_dir, target_dir, force_merge: bool = False):
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""交互入口复用初始化与项目同步。"""
|
|
2
|
+
import click
|
|
3
|
+
from ..command_context import OperationCommand, project_dir, non_interactive
|
|
4
|
+
from .init_cmd import init
|
|
5
|
+
from .sync_cmd import sync_project
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command(cls=OperationCommand)
|
|
9
|
+
def default_cmd():
|
|
10
|
+
if non_interactive():
|
|
11
|
+
raise click.UsageError('请明确指定 init 或 sync 子命令')
|
|
12
|
+
if not (project_dir() / 'pyproject.toml').exists():
|
|
13
|
+
return init()
|
|
14
|
+
return sync_project(project_dir(), install=True)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"""命令行依赖类型选择;显式参数路径不会进入向导。"""
|
|
2
2
|
import sys
|
|
3
3
|
import click
|
|
4
|
+
from ..command_context import non_interactive
|
|
4
5
|
from packaging.requirements import Requirement, InvalidRequirement
|
|
5
6
|
from ..dependencies import DependencyDeclaration, DependencyError
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def require_interactive():
|
|
9
|
-
if
|
|
10
|
+
if non_interactive():
|
|
10
11
|
raise click.UsageError('请提供包名或 --path 目录;交互向导需要终端。')
|
|
11
12
|
|
|
12
13
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import os
|
|
7
7
|
import time
|
|
8
8
|
import click
|
|
9
|
+
from ..command_context import OperationCommand, project_dir as current_project, non_interactive, require_project
|
|
9
10
|
from ..config import get_mcpywrap_config, config_exists, read_config, get_project_type, CONFIG_FILE, ensure_map_setuptools_sync
|
|
10
11
|
from ..builders.watcher import ProjectWatcher
|
|
11
12
|
from .build_cmd import build
|
|
@@ -24,22 +25,25 @@ def file_change_callback(src_path, dest_path, success, output, is_python, is_dep
|
|
|
24
25
|
else:
|
|
25
26
|
click.secho(f'❌ 处理失败 {output}', fg="red")
|
|
26
27
|
|
|
27
|
-
@click.command()
|
|
28
|
+
@click.command(cls=OperationCommand)
|
|
28
29
|
def dev_cmd():
|
|
29
30
|
"""使用watch模式,实时构建为 MCStudio 工程,代码更新时,自动构建"""
|
|
31
|
+
from ..command_context import json_output
|
|
32
|
+
if json_output():
|
|
33
|
+
raise click.UsageError("dev 持续输出文本日志,不支持 --json")
|
|
30
34
|
if not config_exists():
|
|
31
35
|
click.secho('❌ 错误: 未找到配置文件。请先运行 `mcpywrap init` 初始化项目。', fg="red")
|
|
32
36
|
raise click.ClickException('无法启动开发监控')
|
|
33
37
|
|
|
34
38
|
# 确保 map 项目的 setuptools 配置同步
|
|
35
|
-
ensure_map_setuptools_sync(interactive=
|
|
39
|
+
ensure_map_setuptools_sync(interactive=False)
|
|
36
40
|
|
|
37
41
|
# 获取mcpywrap特定配置
|
|
38
42
|
mcpywrap_config = get_mcpywrap_config()
|
|
39
43
|
|
|
40
44
|
if get_project_type() == "addon":
|
|
41
45
|
# 源代码目录固定为当前目录
|
|
42
|
-
source_dir =
|
|
46
|
+
source_dir = str(current_project())
|
|
43
47
|
# 目标目录从配置中读取
|
|
44
48
|
target_dir = mcpywrap_config.get('target_dir')
|
|
45
49
|
|
|
@@ -83,6 +87,7 @@ def dev_cmd():
|
|
|
83
87
|
except KeyboardInterrupt:
|
|
84
88
|
project_watcher.stop()
|
|
85
89
|
click.secho("🛑 监控已停止", fg="bright_yellow")
|
|
90
|
+
raise
|
|
86
91
|
else:
|
|
87
92
|
click.secho('❌ 暂未支持: 当前仅支持Addons项目的构建', fg="red")
|
|
88
|
-
raise click.ClickException('无法启动开发监控')
|
|
93
|
+
raise click.ClickException('无法启动开发监控')
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""不创建项目、不启动程序的本机诊断。"""
|
|
2
|
+
import click
|
|
3
|
+
from ..command_context import OperationCommand, project_dir as current_project, non_interactive, require_project
|
|
4
|
+
|
|
5
|
+
from ..mcstudio.discovery import (
|
|
6
|
+
discover_engines, engine_options, resource_issues, studio_installation,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command(cls=OperationCommand)
|
|
11
|
+
@engine_options
|
|
12
|
+
def doctor_cmd(**overrides):
|
|
13
|
+
"""诊断游戏发现和运行资源(只读,无需初始化项目)"""
|
|
14
|
+
result = discover_engines(current_project(), overrides=overrides)
|
|
15
|
+
data = result.to_dict()
|
|
16
|
+
data['resources'] = {'run': [], 'editor': [], 'safaia': []}
|
|
17
|
+
if result.selected:
|
|
18
|
+
data['resources']['run'] = resource_issues(result.selected)
|
|
19
|
+
data['resources']['editor'] = resource_issues(result.selected, 'editor')
|
|
20
|
+
for purpose in ('editor', 'safaia'):
|
|
21
|
+
_, issues = studio_installation(purpose)
|
|
22
|
+
data['resources'][purpose].extend(issues)
|
|
23
|
+
data['ok'] = bool(result.selected and not result.error and not data['resources']['run'])
|
|
24
|
+
from ..command_context import json_output
|
|
25
|
+
if not json_output():
|
|
26
|
+
for candidate in result.candidates:
|
|
27
|
+
click.echo(f'候选 [{candidate.source}] {candidate.version}: {candidate.executable}')
|
|
28
|
+
for issue in result.diagnostics:
|
|
29
|
+
click.echo(f'{issue["path"]}: {issue["message"]}')
|
|
30
|
+
for kind, issues in data['resources'].items():
|
|
31
|
+
for issue in issues:
|
|
32
|
+
click.echo(f'资源 [{kind}]: {issue}')
|
|
33
|
+
return data
|