mcpywrap 0.3.6__tar.gz → 0.3.7__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.6 → mcpywrap-0.3.7}/CHANGELOG.md +7 -0
- {mcpywrap-0.3.6/mcpywrap.egg-info → mcpywrap-0.3.7}/PKG-INFO +10 -6
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/README.md +9 -5
- mcpywrap-0.3.7/docs/local-dependencies.md +41 -0
- mcpywrap-0.3.7/mcpywrap/__init__.py +4 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/dependency_manager.py +34 -4
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/project_builder.py +3 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/watcher.py +17 -7
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/command_context.py +15 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/add_cmd.py +9 -9
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/build_cmd.py +4 -2
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/dependency_prompt.py +1 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/edit_cmd.py +3 -3
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/init_cmd.py +3 -4
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/modsdk_cmd.py +1 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/package_cmd.py +1 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/run_cmd.py +8 -2
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/sync_cmd.py +1 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/dependencies.py +63 -11
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/bridge_payload/Injector.exe +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/bridge_payload/Loader.dll +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/bridge_payload/McpyMcsAuth.dll +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/bridge_payload/manifest.json +3 -3
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/network.py +2 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/ui/project_ui.py +17 -9
- {mcpywrap-0.3.6 → mcpywrap-0.3.7/mcpywrap.egg-info}/PKG-INFO +10 -6
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap.egg-info/SOURCES.txt +2 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/pyproject.toml +1 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/SKILL.md +4 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/references/remote-testing.md +1 -1
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/references/troubleshooting.md +2 -0
- mcpywrap-0.3.7/tests/manual_dependency_boundaries.py +118 -0
- mcpywrap-0.3.7/tests/test_dependency_boundaries.py +228 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_local_dependencies.py +5 -3
- mcpywrap-0.3.6/docs/local-dependencies.md +0 -30
- mcpywrap-0.3.6/mcpywrap/__init__.py +0 -4
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/LICENSE +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/MANIFEST.in +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/docs/development.md +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/docs/engine-discovery.md +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/docs/mcs-auth.md +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/docs/releasing.md +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/__main__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/AddonsPack.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/MapPack.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/__init__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/assembly.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/builders/file_merge.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/cli.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/__init__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/connect_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/default_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/desktop_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/dev_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/doctor_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/mod_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/publish_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/remove_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/serve_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/session_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/commands/ui_cmd.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/config.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/custom_packaging.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/__init__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/auth_interaction.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/bridge_assets.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/bridge_payload/publisher.cer +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/diagnostics.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/discovery.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/editor.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/file_logs.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/game.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/log_protocol.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/mcs.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/mcs_auth.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/private_logs.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/processes.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/session_worker.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/sessions.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/studio_server.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/symlinks.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/mcstudio/window.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/minecraft/__init__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/minecraft/addons.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/minecraft/level_dat.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/minecraft/map.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/minecraft/netease_modsdk.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/minecraft/template/mod_template.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/project_init.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/remote/__init__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/remote/client.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/remote/http_server.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/remote/service.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/ui/__init__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/utils/__init__.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/utils/pip_error_parser.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/utils/print_guide.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/utils/project_setup.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap/utils/utils.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap.egg-info/requires.txt +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/native/mcs_auth/Bridge.cs +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/native/mcs_auth/Injector.cs +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/native/mcs_auth/Loader.c +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/native/mcs_auth/publisher.cer +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/native/mcs_auth/publisher.json +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/scripts/build_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/scripts/check_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/setup.cfg +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/agents/openai.yaml +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/scripts/bootstrap.ps1 +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/scripts/bootstrap.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/scripts/game_window.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/skills/mcpywrap/scripts/smoke.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/manual_integration.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_auth_interaction.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_automation.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_engine_discovery.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_game_window_skill.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_local_auth_session.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_mcs_auth.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_mouse.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_network.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_network_sessions.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_package.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_remote.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_remote_scripts.py +0 -0
- {mcpywrap-0.3.6 → mcpywrap-0.3.7}/tests/test_skill_workflows.py +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.3.7
|
|
4
|
+
|
|
5
|
+
- 明确工具 Python 与游戏内置 Python 的边界:普通包保留为开发依赖,CLI、JSON 与 GUI 提示“仅开发环境”,不会自动进入游戏产物。
|
|
6
|
+
- 主项目、递归 Addon 依赖与地图包中的原生二进制会在构建、打包、运行或编辑前被拒绝;增量更新同样校验,失败保留已有产物。
|
|
7
|
+
- 包安装后校验候选声明,无效 Addon 不写配置;明确提示已发生的工具环境安装,不自动卸载。
|
|
8
|
+
- README 与 Skill 补充依赖分类、游戏兼容性及实际产物验收要求;开发项目和独立解包产物均已通过本机客户端/服务端共享源码加载测试。
|
|
9
|
+
|
|
3
10
|
## 0.3.6
|
|
4
11
|
|
|
5
12
|
- 新增局域网 serve 服务与 --remote/--local 路由;macOS 本机开发、Windows 网络游戏测试共用 CLI 和 Skill。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: A wrapper for Minecraft China Edition Addons management
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,7 +32,7 @@ Dynamic: license-file
|
|
|
32
32
|
[](https://pypi.org/project/mcpywrap/)
|
|
33
33
|
[](LICENSE)
|
|
34
34
|
|
|
35
|
-
mcpywrap 使用 `pyproject.toml`
|
|
35
|
+
mcpywrap 使用 `pyproject.toml` 管理开发侧依赖,复用本地 Addon 的代码与资源,并串联构建、游戏测试和 MC Studio 编辑器。Python 包管理用于开发环境,不是游戏内的包安装器。
|
|
36
36
|
|
|
37
37
|
## 安装
|
|
38
38
|
|
|
@@ -65,7 +65,7 @@ mcpy edit
|
|
|
65
65
|
|
|
66
66
|
## 复用代码和资源
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
将 Python 包安装到 mcpy 所在的工具环境并记录声明:
|
|
69
69
|
|
|
70
70
|
```powershell
|
|
71
71
|
mcpy add "package-name>=1.0"
|
|
@@ -81,7 +81,9 @@ mcpy add --path "../shared-addon"
|
|
|
81
81
|
|
|
82
82
|
移除依赖使用 `mcpy remove <包名>` 或 `mcpy remove --path <目录>`。移除本地引用不会删除源目录。不带参数运行 `mcpy add` / `mcpy remove` 可进入选择向导。
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
本地路径适合同机开发;共享项目时需同步这些目录。工具目前通过包的本地安装来源识别 Addon,普通 PyPI 包或 wheel 安装成功不代表能参与组装;未识别到 Addon 的包会标记为“仅开发环境”。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
|
|
85
|
+
|
|
86
|
+
**游戏运行边界:** 测试和正式游戏都使用内置 Python,不读取 `dependencies`,也不继承工具环境的 `site-packages`。`add`、`sync --install` 和 ModSDK 安装均不向游戏安装模块。游戏所需代码与资源必须随 Addon 携带,或由用户加载相应 Mod;纯 Python 库也不会自动复制,仍需适配游戏解释器和 ModSDK。原生扩展及依赖外部安装步骤、系统环境的库不能直接用于游戏;包内 `.pyd/.dll/.so/.so.*(版本号)/.dylib` 会阻止构建和启动。
|
|
85
87
|
|
|
86
88
|
## 构建与日常开发
|
|
87
89
|
|
|
@@ -100,12 +102,14 @@ mcpy add --path "../shared-addon"
|
|
|
100
102
|
|
|
101
103
|
### 打包分发
|
|
102
104
|
|
|
103
|
-
在项目根目录执行 `mcpy package
|
|
105
|
+
在项目根目录执行 `mcpy package`,会组装项目及已识别的 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`;仅开发环境的包不会进入 ZIP。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
|
|
104
106
|
|
|
105
107
|
Addon ZIP 内为 `<项目名>_bp/`、`<项目名>_rp/`(仅包含实际构建出的包);地图 ZIP 根目录直接包含存档数据、行为包、资源包及世界包配置。地图默认保留独立包,使用 `mcpy package --merge`(或 `-m`)按构建规则合并依赖资源。构建产物中的空目录会保留。
|
|
106
108
|
|
|
107
109
|
重复打包成功后会替换同名 ZIP;失败时保留已有 ZIP,临时文件自动清理。
|
|
108
110
|
|
|
111
|
+
`publish` 向 PyPI 发布 Python 分发包,与游戏 ZIP 分发不同。本地测试可配置多个独立 Addon,游戏不会解析 Python 依赖图;构建成功不保证兼容,发布前应检查产物并在游戏中验证加载日志。
|
|
112
|
+
|
|
109
113
|
## 游戏启动与排查
|
|
110
114
|
|
|
111
115
|
通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
|
|
@@ -173,7 +177,7 @@ mcpy --project D:\tests\server stop --session <id> --json
|
|
|
173
177
|
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
174
178
|
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
175
179
|
两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
|
|
176
|
-
|
|
180
|
+
推荐使用 mcpywrap 0.3.7 或更高版本,并从 `v0.3.7` 标签安装配套 Skill,以获得依赖边界提示与校验。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
|
|
177
181
|
|
|
178
182
|
安装后可直接描述任务:
|
|
179
183
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://pypi.org/project/mcpywrap/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
mcpywrap 使用 `pyproject.toml`
|
|
8
|
+
mcpywrap 使用 `pyproject.toml` 管理开发侧依赖,复用本地 Addon 的代码与资源,并串联构建、游戏测试和 MC Studio 编辑器。Python 包管理用于开发环境,不是游戏内的包安装器。
|
|
9
9
|
|
|
10
10
|
## 安装
|
|
11
11
|
|
|
@@ -38,7 +38,7 @@ mcpy edit
|
|
|
38
38
|
|
|
39
39
|
## 复用代码和资源
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
将 Python 包安装到 mcpy 所在的工具环境并记录声明:
|
|
42
42
|
|
|
43
43
|
```powershell
|
|
44
44
|
mcpy add "package-name>=1.0"
|
|
@@ -54,7 +54,9 @@ mcpy add --path "../shared-addon"
|
|
|
54
54
|
|
|
55
55
|
移除依赖使用 `mcpy remove <包名>` 或 `mcpy remove --path <目录>`。移除本地引用不会删除源目录。不带参数运行 `mcpy add` / `mcpy remove` 可进入选择向导。
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
本地路径适合同机开发;共享项目时需同步这些目录。工具目前通过包的本地安装来源识别 Addon,普通 PyPI 包或 wheel 安装成功不代表能参与组装;未识别到 Addon 的包会标记为“仅开发环境”。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
|
|
58
|
+
|
|
59
|
+
**游戏运行边界:** 测试和正式游戏都使用内置 Python,不读取 `dependencies`,也不继承工具环境的 `site-packages`。`add`、`sync --install` 和 ModSDK 安装均不向游戏安装模块。游戏所需代码与资源必须随 Addon 携带,或由用户加载相应 Mod;纯 Python 库也不会自动复制,仍需适配游戏解释器和 ModSDK。原生扩展及依赖外部安装步骤、系统环境的库不能直接用于游戏;包内 `.pyd/.dll/.so/.so.*(版本号)/.dylib` 会阻止构建和启动。
|
|
58
60
|
|
|
59
61
|
## 构建与日常开发
|
|
60
62
|
|
|
@@ -73,12 +75,14 @@ mcpy add --path "../shared-addon"
|
|
|
73
75
|
|
|
74
76
|
### 打包分发
|
|
75
77
|
|
|
76
|
-
在项目根目录执行 `mcpy package
|
|
78
|
+
在项目根目录执行 `mcpy package`,会组装项目及已识别的 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`;仅开发环境的包不会进入 ZIP。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
|
|
77
79
|
|
|
78
80
|
Addon ZIP 内为 `<项目名>_bp/`、`<项目名>_rp/`(仅包含实际构建出的包);地图 ZIP 根目录直接包含存档数据、行为包、资源包及世界包配置。地图默认保留独立包,使用 `mcpy package --merge`(或 `-m`)按构建规则合并依赖资源。构建产物中的空目录会保留。
|
|
79
81
|
|
|
80
82
|
重复打包成功后会替换同名 ZIP;失败时保留已有 ZIP,临时文件自动清理。
|
|
81
83
|
|
|
84
|
+
`publish` 向 PyPI 发布 Python 分发包,与游戏 ZIP 分发不同。本地测试可配置多个独立 Addon,游戏不会解析 Python 依赖图;构建成功不保证兼容,发布前应检查产物并在游戏中验证加载日志。
|
|
85
|
+
|
|
82
86
|
## 游戏启动与排查
|
|
83
87
|
|
|
84
88
|
通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
|
|
@@ -146,7 +150,7 @@ mcpy --project D:\tests\server stop --session <id> --json
|
|
|
146
150
|
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
147
151
|
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
148
152
|
两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
|
|
149
|
-
|
|
153
|
+
推荐使用 mcpywrap 0.3.7 或更高版本,并从 `v0.3.7` 标签安装配套 Skill,以获得依赖边界提示与校验。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
|
|
150
154
|
|
|
151
155
|
安装后可直接描述任务:
|
|
152
156
|
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
`mcpy add "package-name>=1.0"` 在 mcpy 工具环境安装包。只有能从本地安装来源解析出有效 Addon 的内容参与组装;
|
|
25
|
+
普通 Python 包或未识别到 Addon 内容的 wheel 标记为“仅开发环境”,不会复制 `site-packages`。
|
|
26
|
+
本地路径不会自动转换为可分发的包依赖;共享项目时需同步引用目录。
|
|
27
|
+
本地引用的包依赖未安装时,按警告显式安装;版本约束与环境标记检查的是工具 Python,不代表游戏兼容性。
|
|
28
|
+
|
|
29
|
+
测试和正式游戏都使用游戏内置 Python,不读取 `dependencies`,不自动安装或加载其他 Mod。
|
|
30
|
+
需要的纯 Python 代码及资源必须包含在 Addon 中,并适配游戏解释器、标准库及 ModSDK。
|
|
31
|
+
游戏包内的 `.pyd`、`.dll`、`.so`、版本化 `.so.*`、`.dylib` 会在构建、打包、运行和编辑前报错,
|
|
32
|
+
不再静默过滤;`dev` 检测到这类文件时保留原输出,移除问题文件后可恢复更新。
|
|
33
|
+
开发工具可以使用二进制包,只要这些文件不放在游戏包中。依赖外部安装步骤或系统环境的库也不能直接用于游戏;
|
|
34
|
+
当前校验不会分析所有 Python import,仍需检查 ZIP 内容和实际游戏日志。
|
|
35
|
+
|
|
36
|
+
验收解包产物时,避免同时加载相同 manifest UUID 的源码包与产物,防止游戏选中旧内容。
|
|
37
|
+
|
|
38
|
+
构建时主项目优先于依赖;同级依赖按声明顺序处理,后者覆盖重复内容。
|
|
39
|
+
这不代表游戏运行时的资源包优先级。修改依赖声明后重启 `mcpy dev`。
|
|
40
|
+
|
|
41
|
+
遇到结构错误先核对目录和清单;查看参数使用 `mcpy add --help`。
|
|
@@ -9,7 +9,8 @@ from urllib.request import url2pathname
|
|
|
9
9
|
|
|
10
10
|
from packaging.requirements import Requirement, InvalidRequirement
|
|
11
11
|
from packaging.utils import canonicalize_name
|
|
12
|
-
from ..dependencies import DependencyError, addon_directories, canonical_path,
|
|
12
|
+
from ..dependencies import (DependencyError, DependencyStatus, addon_directories, canonical_path,
|
|
13
|
+
declarations, read_project, resolve_path, validate_game_files)
|
|
13
14
|
from .AddonsPack import AddonsPack
|
|
14
15
|
|
|
15
16
|
|
|
@@ -42,6 +43,8 @@ class DependencyManager:
|
|
|
42
43
|
self.dependency_map = {}
|
|
43
44
|
self.root_node = None
|
|
44
45
|
self.warnings = []
|
|
46
|
+
self.errors = []
|
|
47
|
+
self.statuses = {}
|
|
45
48
|
self._nodes, self._active, self._ordered = {}, [], []
|
|
46
49
|
|
|
47
50
|
def find_dependency_path(self, package_name):
|
|
@@ -60,6 +63,10 @@ class DependencyManager:
|
|
|
60
63
|
config.setdefault('project', {})['dependencies'] = dependencies
|
|
61
64
|
root_id = re.sub(r'[^\w.-]', '_', project_name)[:60] + '_' + hashlib.sha256(canonical_path(project_path).encode('utf-8')).hexdigest()[:12]
|
|
62
65
|
root = DependencyNode(project_name, AddonsPack(root_id, project_path, is_origin=True))
|
|
66
|
+
folders = [root.addon_pack.behavior_pack_dir, root.addon_pack.resource_pack_dir]
|
|
67
|
+
if config.get('tool', {}).get('mcpywrap', {}).get('project_type') == 'map':
|
|
68
|
+
folders = [Path(project_path) / name for name in ('behavior_packs', 'resource_packs')]
|
|
69
|
+
validate_game_files(project_path, folders)
|
|
63
70
|
self.root_node = root
|
|
64
71
|
key = canonical_path(project_path)
|
|
65
72
|
self._nodes[key] = root
|
|
@@ -67,8 +74,8 @@ class DependencyManager:
|
|
|
67
74
|
self._process(root, config)
|
|
68
75
|
self._active.pop()
|
|
69
76
|
self.dependency_map = {canonical_path(n.addon_pack.path): n.addon_pack for n in self._ordered}
|
|
70
|
-
if self.
|
|
71
|
-
raise DependencyError('\n'.join(self.
|
|
77
|
+
if self.errors and not allow_missing:
|
|
78
|
+
raise DependencyError('\n'.join(self.errors))
|
|
72
79
|
return root
|
|
73
80
|
|
|
74
81
|
def _process(self, parent, config):
|
|
@@ -78,12 +85,14 @@ class DependencyManager:
|
|
|
78
85
|
if entry.kind == 'local':
|
|
79
86
|
path = str(resolve_path(source, entry.value))
|
|
80
87
|
self._visit(parent, path, Path(path).name)
|
|
88
|
+
self._status(source, entry, 'addon')
|
|
81
89
|
continue
|
|
82
90
|
try:
|
|
83
91
|
requirement = Requirement(entry.value)
|
|
84
92
|
except InvalidRequirement as exc:
|
|
85
93
|
raise DependencyError(f'无效的 Python 包声明: {entry.value}') from exc
|
|
86
94
|
if requirement.marker and not requirement.marker.evaluate():
|
|
95
|
+
self._status(source, entry, 'inactive', '当前工具 Python 环境不满足环境标记,未启用;不代表游戏兼容性。')
|
|
87
96
|
continue
|
|
88
97
|
try:
|
|
89
98
|
dist = metadata.distribution(requirement.name)
|
|
@@ -95,6 +104,7 @@ class DependencyManager:
|
|
|
95
104
|
continue
|
|
96
105
|
path = self.find_dependency_path(entry.value)
|
|
97
106
|
if not path:
|
|
107
|
+
self._development_only(source, entry)
|
|
98
108
|
continue
|
|
99
109
|
direct = dist.read_text('direct_url.json')
|
|
100
110
|
if not Path(path).is_dir() and direct and 'dir_info' in json.loads(direct):
|
|
@@ -107,11 +117,30 @@ class DependencyManager:
|
|
|
107
117
|
for p in Path(path).iterdir())
|
|
108
118
|
if has_mcpy or looks_like_addon:
|
|
109
119
|
self._visit(parent, path, canonicalize_name(requirement.name))
|
|
120
|
+
self._status(source, entry, 'addon')
|
|
121
|
+
else:
|
|
122
|
+
self._development_only(source, entry)
|
|
110
123
|
except (DependencyError, OSError, ValueError) as exc:
|
|
111
124
|
raise DependencyError(f'来源 {source}\n依赖 {entry.value!r}: {exc}') from exc
|
|
112
125
|
|
|
113
126
|
def _missing(self, source, value, reason):
|
|
114
|
-
|
|
127
|
+
from ..dependencies import DependencyDeclaration
|
|
128
|
+
message = f'{source}: {value} {reason};请显式执行 mcpy add "{value}"'
|
|
129
|
+
self.errors.append(message)
|
|
130
|
+
self.warnings.append(message)
|
|
131
|
+
self._status(source, DependencyDeclaration('package', value), 'unavailable', message)
|
|
132
|
+
|
|
133
|
+
def _status(self, source, entry, state, message=''):
|
|
134
|
+
self.statuses[(canonical_path(source), entry.kind, entry.value)] = DependencyStatus(state, message)
|
|
135
|
+
|
|
136
|
+
def status_for(self, source, entry):
|
|
137
|
+
return self.statuses[(canonical_path(source), entry.kind, entry.value)]
|
|
138
|
+
|
|
139
|
+
def _development_only(self, source, entry):
|
|
140
|
+
message = (f'来源 {source},依赖 {entry.value!r}: 未识别到可组装内容;'
|
|
141
|
+
'仅安装于工具环境,不会被打包或供游戏导入。')
|
|
142
|
+
self.warnings.append(message)
|
|
143
|
+
self._status(source, entry, 'development_only', message)
|
|
115
144
|
|
|
116
145
|
def _visit(self, parent, path, name):
|
|
117
146
|
key = canonical_path(path)
|
|
@@ -124,6 +153,7 @@ class DependencyManager:
|
|
|
124
153
|
if config.get('tool', {}).get('mcpywrap', {}).get('project_type', 'addon') != 'addon':
|
|
125
154
|
raise DependencyError(f'仅支持 Addon 目录依赖: {path}')
|
|
126
155
|
folders = addon_directories(path)
|
|
156
|
+
validate_game_files(path, folders.values())
|
|
127
157
|
suffix = hashlib.sha256(key.encode('utf-8')).hexdigest()[:12]
|
|
128
158
|
safe_name = re.sub(r'[^\w.-]', '_', name)[:60] or 'addon'
|
|
129
159
|
addon = AddonsPack(f'{safe_name}_{suffix}', path)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import os
|
|
3
3
|
import shutil
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from ..command_context import report_dependency_warnings
|
|
5
6
|
from ..dependencies import DependencyError, DependencyService, addon_directories, read_project
|
|
6
7
|
from .AddonsPack import AddonsPack
|
|
7
8
|
from .MapPack import MapPack
|
|
@@ -30,6 +31,7 @@ class AddonProjectBuilder:
|
|
|
30
31
|
def initialize(self):
|
|
31
32
|
addon_directories(self.source_dir)
|
|
32
33
|
self.dependency_manager = DependencyService(self.source_dir).resolve()
|
|
34
|
+
report_dependency_warnings(self.dependency_manager)
|
|
33
35
|
self.dependency_tree = self.dependency_manager.root_node
|
|
34
36
|
self.origin_addon = self.dependency_tree.addon_pack
|
|
35
37
|
self.packs = list(self.dependency_manager.get_all_dependencies().values()) + [self.origin_addon]
|
|
@@ -60,6 +62,7 @@ class MapProjectBuilder:
|
|
|
60
62
|
def build(self):
|
|
61
63
|
try:
|
|
62
64
|
manager = DependencyService(self.source_dir).resolve()
|
|
65
|
+
report_dependency_warnings(manager)
|
|
63
66
|
packs = list(manager.get_all_dependencies().values())
|
|
64
67
|
root = manager.root_node.addon_pack
|
|
65
68
|
validate_target(self.target_dir, self.source_dir, packs + [root])
|
|
@@ -4,7 +4,7 @@ import threading
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from watchdog.observers import Observer
|
|
6
6
|
from watchdog.events import FileSystemEventHandler
|
|
7
|
-
from ..dependencies import DependencyService
|
|
7
|
+
from ..dependencies import DependencyService, is_native_file, validate_game_files
|
|
8
8
|
from .AddonsPack import AddonsPack
|
|
9
9
|
from .assembly import is_within, pack_files, rebuild_file, validate_target
|
|
10
10
|
|
|
@@ -17,7 +17,8 @@ class FileChangeHandler(FileSystemEventHandler):
|
|
|
17
17
|
self.is_dependency, self.dependency_name = is_dependency, dependency_name
|
|
18
18
|
|
|
19
19
|
def _should_ignore_path(self, path):
|
|
20
|
-
return is_within(path, self.target_dir) or self.addon_pack.should_exclude(path)
|
|
20
|
+
return is_within(path, self.target_dir) or (self.addon_pack.should_exclude(path)
|
|
21
|
+
and Path(path).name not in ('manifest.json', 'pack_manifest.json') and not is_native_file(path))
|
|
21
22
|
|
|
22
23
|
def _process_event(self, event, event_type):
|
|
23
24
|
if self._should_ignore_path(event.src_path):
|
|
@@ -117,18 +118,27 @@ class ProjectWatcher:
|
|
|
117
118
|
|
|
118
119
|
def rebuild(self, kind, relative):
|
|
119
120
|
with self.lock:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
self.validate_sources()
|
|
122
|
+
return self._rebuild(kind, relative)
|
|
123
|
+
|
|
124
|
+
def _rebuild(self, kind, relative):
|
|
125
|
+
destination = Path(getattr(self.target_addon_pack, kind + '_pack_dir')) / relative
|
|
126
|
+
rebuild_file(self.packs, kind, relative, destination)
|
|
127
|
+
self.known_files.add((kind, relative))
|
|
128
|
+
return str(destination)
|
|
124
129
|
|
|
125
130
|
def rebuild_all(self):
|
|
126
131
|
with self.lock:
|
|
132
|
+
self.validate_sources()
|
|
127
133
|
current = {(kind, rel) for p in self.packs for kind in ('behavior', 'resource') for rel in pack_files(p, kind)}
|
|
128
134
|
for kind, relative in sorted(self.known_files | current):
|
|
129
|
-
self.
|
|
135
|
+
self._rebuild(kind, relative)
|
|
130
136
|
self.known_files = current
|
|
131
137
|
|
|
138
|
+
def validate_sources(self):
|
|
139
|
+
for pack in self.packs:
|
|
140
|
+
validate_game_files(pack.path, [pack.behavior_pack_dir, pack.resource_pack_dir])
|
|
141
|
+
|
|
132
142
|
def start(self):
|
|
133
143
|
self.multi_watcher.start_all()
|
|
134
144
|
|
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
import sys
|
|
7
7
|
import tempfile
|
|
8
8
|
import traceback
|
|
9
|
-
from dataclasses import dataclass
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
import click
|
|
@@ -19,6 +19,7 @@ class CommandContext:
|
|
|
19
19
|
json_output: bool = False
|
|
20
20
|
remote: str = None
|
|
21
21
|
local: bool = False
|
|
22
|
+
warnings: list = field(default_factory=list)
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
_context = contextvars.ContextVar('mcpy_context', default=None)
|
|
@@ -44,6 +45,17 @@ def human_interaction():
|
|
|
44
45
|
return current is not None and not non_interactive() and not json_output()
|
|
45
46
|
|
|
46
47
|
|
|
48
|
+
def report_dependency_warnings(manager):
|
|
49
|
+
"""CLI 展示与 JSON 共用诊断;解析服务本身不进行输出。"""
|
|
50
|
+
current = _context.get()
|
|
51
|
+
for message in manager.warnings:
|
|
52
|
+
if current is not None:
|
|
53
|
+
if message in current.warnings:
|
|
54
|
+
continue
|
|
55
|
+
current.warnings.append(message)
|
|
56
|
+
click.secho(message, fg='yellow', err=True)
|
|
57
|
+
|
|
58
|
+
|
|
47
59
|
@contextlib.contextmanager
|
|
48
60
|
def project_scope(path):
|
|
49
61
|
previous = _context.get()
|
|
@@ -117,6 +129,8 @@ class OperationGroup(click.Group):
|
|
|
117
129
|
data = {'ok': code == 0, 'error': None, 'hint': None}
|
|
118
130
|
if isinstance(result, dict):
|
|
119
131
|
data.update(result)
|
|
132
|
+
if state.warnings:
|
|
133
|
+
data['warnings'] = list(dict.fromkeys([*data.get('warnings', []), *state.warnings]))
|
|
120
134
|
# ASCII JSON survives both GBK consoles and UTF-8 subprocess pipes losslessly.
|
|
121
135
|
click.echo(json.dumps(data, ensure_ascii=True), file=output)
|
|
122
136
|
elif code:
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"""添加 Python 包或本地 Addon 目录依赖。"""
|
|
2
|
-
import os
|
|
3
|
-
from pathlib import Path
|
|
4
2
|
import click
|
|
5
|
-
from ..command_context import OperationCommand, project_dir as current_project,
|
|
3
|
+
from ..command_context import OperationCommand, project_dir as current_project, report_dependency_warnings
|
|
6
4
|
from ..dependencies import DependencyDeclaration, DependencyError, DependencyService
|
|
7
5
|
from .dependency_prompt import prompt_dependency, require_interactive
|
|
8
6
|
|
|
@@ -11,7 +9,7 @@ from .dependency_prompt import prompt_dependency, require_interactive
|
|
|
11
9
|
@click.argument('package', required=False)
|
|
12
10
|
@click.option('--path', 'local_path', help='直接引用本地 Addon 根目录,不安装目录')
|
|
13
11
|
def add_cmd(package, local_path):
|
|
14
|
-
"""
|
|
12
|
+
"""添加依赖;包安装到 mcpy 工具环境,不安装到游戏。无参数进入向导。"""
|
|
15
13
|
if package is not None and local_path is not None:
|
|
16
14
|
raise click.UsageError('包名与 --path 不能同时使用')
|
|
17
15
|
if not (current_project() / 'pyproject.toml').exists():
|
|
@@ -24,13 +22,15 @@ def add_cmd(package, local_path):
|
|
|
24
22
|
entry = DependencyDeclaration('local' if local_path is not None else 'package', local_path if local_path is not None else package)
|
|
25
23
|
service = DependencyService(str(current_project()))
|
|
26
24
|
if entry.kind == 'local':
|
|
27
|
-
changed,
|
|
28
|
-
for warning in warnings:
|
|
29
|
-
click.secho(warning, fg='yellow')
|
|
25
|
+
changed, _ = service.add_local(entry.value)
|
|
30
26
|
else:
|
|
31
|
-
click.echo(f'
|
|
27
|
+
click.echo(f'正在向 mcpy 工具环境安装 {entry.value}(非游戏环境)...')
|
|
32
28
|
changed = service.add_package(entry.value)
|
|
29
|
+
manager = service.last_resolution
|
|
30
|
+
report_dependency_warnings(manager)
|
|
33
31
|
click.secho('依赖已添加' if changed else '依赖已存在', fg='green')
|
|
34
|
-
return {'changed': changed, 'dependency': entry.value, 'source': entry.kind,
|
|
32
|
+
return {'changed': changed, 'dependency': entry.value, 'source': entry.kind,
|
|
33
|
+
'classification': 'addon' if entry.kind == 'local' else manager.status_for(current_project(), entry).state,
|
|
34
|
+
'warnings': manager.warnings}
|
|
35
35
|
except (DependencyError, OSError) as exc:
|
|
36
36
|
raise click.ClickException(str(exc)) from exc
|
|
@@ -33,12 +33,12 @@ def build_cmd(merge):
|
|
|
33
33
|
target_dir = os.path.normpath(os.path.join(str(current_project()), target_dir))
|
|
34
34
|
|
|
35
35
|
# 实际构建
|
|
36
|
-
if not build(str(current_project()), target_dir, force_merge=merge):
|
|
36
|
+
if not build(str(current_project()), target_dir, force_merge=merge, raise_errors=True):
|
|
37
37
|
raise click.ClickException('构建失败')
|
|
38
38
|
return {'output': target_dir}
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
def build(source_dir, target_dir, force_merge: bool = False):
|
|
41
|
+
def build(source_dir, target_dir, force_merge: bool = False, raise_errors=False):
|
|
42
42
|
"""
|
|
43
43
|
执行项目构建
|
|
44
44
|
|
|
@@ -72,6 +72,8 @@ def build(source_dir, target_dir, force_merge: bool = False):
|
|
|
72
72
|
click.secho('✅ 构建成功!项目已生成到目标目录。', fg="green")
|
|
73
73
|
return True
|
|
74
74
|
else:
|
|
75
|
+
if raise_errors:
|
|
76
|
+
raise click.ClickException(f'构建失败: {error}')
|
|
75
77
|
click.secho(f'❌ 构建失败: ', fg="red", nl=False)
|
|
76
78
|
click.secho(f'{error}', fg="bright_red")
|
|
77
79
|
return False
|
|
@@ -12,7 +12,7 @@ def require_interactive():
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def prompt_dependency():
|
|
15
|
-
kind = click.prompt('依赖类型:1 Python
|
|
15
|
+
kind = click.prompt('依赖类型:1 Python 包(安装于工具环境),2 本地 Addon 目录', type=click.Choice(['1', '2']), default='1')
|
|
16
16
|
value = click.prompt('包名/版本约束' if kind == '1' else 'Addon 目录').strip()
|
|
17
17
|
if kind == '1':
|
|
18
18
|
try:
|
|
@@ -42,6 +42,9 @@ def open_edit(project_dir=None, engine_overrides=None, raise_errors=False):
|
|
|
42
42
|
config = read_project(base_dir)
|
|
43
43
|
project_name = config.get('project', {}).get('name', 'project')
|
|
44
44
|
project_type = config.get('tool', {}).get('mcpywrap', {}).get('project_type', 'addon')
|
|
45
|
+
all_packs = _setup_dependencies(project_name, base_dir, raise_errors=raise_errors)
|
|
46
|
+
if all_packs is None:
|
|
47
|
+
return False
|
|
45
48
|
try:
|
|
46
49
|
engine = discover_engines(base_dir, engine_overrides).require_engine()
|
|
47
50
|
require_resources(engine, 'editor')
|
|
@@ -53,9 +56,6 @@ def open_edit(project_dir=None, engine_overrides=None, raise_errors=False):
|
|
|
53
56
|
raise
|
|
54
57
|
click.echo(str(exc))
|
|
55
58
|
return False
|
|
56
|
-
all_packs = _setup_dependencies(project_name, base_dir)
|
|
57
|
-
if all_packs is None:
|
|
58
|
-
return False
|
|
59
59
|
studio_config_path = os.path.join(base_dir, 'studio.json')
|
|
60
60
|
addon_packs_dirs = []
|
|
61
61
|
for pack in all_packs:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""初始化项目:向导与显式参数使用同一服务。"""
|
|
2
2
|
import click
|
|
3
|
-
from ..command_context import OperationCommand, project_dir, non_interactive
|
|
3
|
+
from ..command_context import OperationCommand, project_dir, non_interactive, report_dependency_warnings
|
|
4
4
|
from ..project_init import initialize_project, detect_project
|
|
5
5
|
from ..dependencies import DependencyService
|
|
6
6
|
from .dependency_prompt import prompt_dependency
|
|
@@ -33,10 +33,9 @@ def init():
|
|
|
33
33
|
while click.confirm('是否添加依赖?', default=False):
|
|
34
34
|
entry = prompt_dependency()
|
|
35
35
|
if entry.kind == 'local':
|
|
36
|
-
|
|
37
|
-
for warning in warnings:
|
|
38
|
-
click.echo(warning, err=True)
|
|
36
|
+
service.add_local(entry.value)
|
|
39
37
|
else:
|
|
40
38
|
service.add_package(entry.value)
|
|
39
|
+
report_dependency_warnings(service.last_resolution)
|
|
41
40
|
click.echo('项目已初始化;使用 mod 生成脚本、modsdk 安装 SDK、sync --install 安装项目。')
|
|
42
41
|
return result
|
|
@@ -9,7 +9,7 @@ from ..minecraft.netease_modsdk import get_available_versions, download_and_inst
|
|
|
9
9
|
@click.option('--version', help='安装指定版本')
|
|
10
10
|
@click.option('--latest', is_flag=True, help='显式选择最新版本')
|
|
11
11
|
def modsdk_cmd(list_versions, version, latest):
|
|
12
|
-
"""
|
|
12
|
+
"""在工具环境管理网易 ModSDK,不安装到游戏;非交互必须指定操作。"""
|
|
13
13
|
if sum((list_versions, bool(version), latest)) > 1:
|
|
14
14
|
raise click.UsageError('--list、--version、--latest 不能同时使用')
|
|
15
15
|
if not any((list_versions, version, latest)) and non_interactive():
|
|
@@ -15,7 +15,7 @@ from ..builders.project_builder import AddonProjectBuilder, MapProjectBuilder
|
|
|
15
15
|
@click.command(cls=OperationCommand)
|
|
16
16
|
@click.option('--merge', '-m', is_flag=True, help='强制合并所有资源文件(仅地图项目)')
|
|
17
17
|
def package_cmd(merge):
|
|
18
|
-
"""
|
|
18
|
+
"""组装 Addon/地图并生成分发 ZIP;游戏兼容性需实际测试。"""
|
|
19
19
|
if not config_exists():
|
|
20
20
|
raise click.ClickException('未找到配置文件。请先运行 `mcpy init` 初始化项目。')
|
|
21
21
|
|
|
@@ -97,7 +97,7 @@ def _generate_new_instance_config(base_dir, project_name):
|
|
|
97
97
|
|
|
98
98
|
return level_id, config_path
|
|
99
99
|
|
|
100
|
-
def _setup_dependencies(project_name, base_dir, raise_errors=False):
|
|
100
|
+
def _setup_dependencies(project_name, base_dir, raise_errors=False, report=None):
|
|
101
101
|
"""设置项目依赖"""
|
|
102
102
|
from ..dependencies import DependencyService, DependencyError, addon_directories, read_project
|
|
103
103
|
try:
|
|
@@ -106,6 +106,12 @@ def _setup_dependencies(project_name, base_dir, raise_errors=False):
|
|
|
106
106
|
if project_type == 'addon':
|
|
107
107
|
addon_directories(base_dir)
|
|
108
108
|
manager = DependencyService(base_dir).resolve()
|
|
109
|
+
from ..command_context import report_dependency_warnings
|
|
110
|
+
if report is None:
|
|
111
|
+
report_dependency_warnings(manager)
|
|
112
|
+
else:
|
|
113
|
+
for message in manager.warnings:
|
|
114
|
+
report(message)
|
|
109
115
|
packs = list(manager.get_all_dependencies().values())
|
|
110
116
|
if project_type == 'addon':
|
|
111
117
|
packs.append(manager.root_node.addon_pack)
|
|
@@ -407,7 +413,7 @@ def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach
|
|
|
407
413
|
return
|
|
408
414
|
|
|
409
415
|
# 设置依赖
|
|
410
|
-
all_packs = _setup_dependencies(project_name, base_dir)
|
|
416
|
+
all_packs = _setup_dependencies(project_name, base_dir, raise_errors=True)
|
|
411
417
|
if all_packs is None:
|
|
412
418
|
raise click.ClickException('依赖校验失败')
|
|
413
419
|
auth_context = None
|
|
@@ -9,7 +9,7 @@ from ..dependencies import read_project, write_project
|
|
|
9
9
|
@click.command(cls=OperationCommand)
|
|
10
10
|
@click.option('--install', is_flag=True, help='同时在当前工具环境中可编辑安装项目')
|
|
11
11
|
def sync_cmd(install):
|
|
12
|
-
"""同步包配置;--install
|
|
12
|
+
"""同步包配置;--install 安装到 mcpy 工具环境,不安装到游戏。"""
|
|
13
13
|
require_project()
|
|
14
14
|
return sync_project(project_dir(), install)
|
|
15
15
|
|