mcpywrap 0.2.3__tar.gz → 0.3.1__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/CHANGELOG.md +25 -0
- mcpywrap-0.3.1/MANIFEST.in +7 -0
- mcpywrap-0.3.1/PKG-INFO +124 -0
- mcpywrap-0.3.1/README.md +98 -0
- mcpywrap-0.3.1/docs/development.md +15 -0
- mcpywrap-0.3.1/docs/engine-discovery.md +72 -0
- mcpywrap-0.3.1/docs/local-dependencies.md +65 -0
- mcpywrap-0.3.1/docs/releasing.md +23 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/__init__.py +1 -1
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/builders/AddonsPack.py +12 -23
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/builders/MapPack.py +4 -6
- mcpywrap-0.3.1/mcpywrap/builders/assembly.py +85 -0
- mcpywrap-0.3.1/mcpywrap/builders/dependency_manager.py +158 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/builders/file_merge.py +2 -4
- mcpywrap-0.3.1/mcpywrap/builders/project_builder.py +97 -0
- mcpywrap-0.3.1/mcpywrap/builders/watcher.py +136 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/cli.py +3 -1
- mcpywrap-0.3.1/mcpywrap/commands/add_cmd.py +34 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/build_cmd.py +5 -3
- mcpywrap-0.3.1/mcpywrap/commands/dependency_prompt.py +21 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/dev_cmd.py +4 -4
- mcpywrap-0.3.1/mcpywrap/commands/doctor_cmd.py +42 -0
- mcpywrap-0.3.1/mcpywrap/commands/edit_cmd.py +100 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/init_cmd.py +32 -26
- mcpywrap-0.3.1/mcpywrap/commands/remove_cmd.py +46 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/run_cmd.py +68 -93
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/ui_cmd.py +1 -2
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/config.py +1 -1
- mcpywrap-0.3.1/mcpywrap/dependencies.py +222 -0
- mcpywrap-0.3.1/mcpywrap/mcstudio/discovery.py +337 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/editor.py +23 -16
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/game.py +23 -50
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/mcs.py +18 -53
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/symlinks.py +59 -243
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/ui/project_ui.py +184 -119
- mcpywrap-0.3.1/mcpywrap.egg-info/PKG-INFO +124 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap.egg-info/SOURCES.txt +15 -1
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap.egg-info/requires.txt +2 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/pyproject.toml +10 -5
- mcpywrap-0.3.1/tests/manual_integration.py +278 -0
- mcpywrap-0.3.1/tests/test_engine_discovery.py +390 -0
- mcpywrap-0.3.1/tests/test_local_dependencies.py +487 -0
- mcpywrap-0.2.3/PKG-INFO +0 -146
- mcpywrap-0.2.3/README.md +0 -121
- mcpywrap-0.2.3/mcpywrap/builders/dependency_manager.py +0 -209
- mcpywrap-0.2.3/mcpywrap/builders/project_builder.py +0 -307
- mcpywrap-0.2.3/mcpywrap/builders/watcher.py +0 -314
- mcpywrap-0.2.3/mcpywrap/commands/add_cmd.py +0 -69
- mcpywrap-0.2.3/mcpywrap/commands/edit_cmd.py +0 -130
- mcpywrap-0.2.3/mcpywrap/commands/remove_cmd.py +0 -69
- mcpywrap-0.2.3/mcpywrap.egg-info/PKG-INFO +0 -146
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/LICENSE +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/__main__.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/builders/__init__.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/__init__.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/default_cmd.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/mod_cmd.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/modsdk_cmd.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/commands/publish_cmd.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/custom_packaging.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/__init__.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/studio_server.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/minecraft/__init__.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/minecraft/addons.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/minecraft/level_dat.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/minecraft/map.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/minecraft/netease_modsdk.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/minecraft/template/mod_template.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/ui/__init__.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/utils/__init__.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/utils/pip_error_parser.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/utils/print_guide.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/utils/project_setup.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap/utils/utils.py +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.2.3 → mcpywrap-0.3.1}/setup.cfg +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# 更新记录
|
|
2
|
+
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
- 完善游戏引擎自动发现:优先检查当前用户注册表的两个视图,失败后搜索固定磁盘中的标准下载目录。
|
|
6
|
+
- 仅选择包含游戏 EXE 的有效版本目录,跳过残缺安装和杂项目录,按语义版本稳定排序。
|
|
7
|
+
- 支持通过项目配置、环境变量和 CLI 指定游戏 EXE、下载目录及引擎版本;无效覆盖和版本冲突明确报错。
|
|
8
|
+
- 新增只读 `mcpy doctor` 和 JSON 诊断,分别报告游戏、编辑器及 Safaia 的资源状态。
|
|
9
|
+
- CLI 与 GUI 共用发现结果,运行配置和实际 EXE 保持一致;已有实例默认保留原版本,不再自动切换到最新版。
|
|
10
|
+
- 新增 27 项隔离测试,完整回归共 60 项通过;本机只读验证选中 MCS 引擎 3.10.0.420447。
|
|
11
|
+
- 精简 README 为上手指引,将依赖、引擎配置和开发验证细节整理为独立文档。
|
|
12
|
+
|
|
13
|
+
## 0.3.0
|
|
14
|
+
|
|
15
|
+
- 新增本地 Addon 目录依赖:CLI、初始化向导和 GUI 支持直接添加、预检和移除目录引用,无需初始化或安装被引用项目。
|
|
16
|
+
- Python 包和本地目录共用依赖图,支持相对路径、真实路径去重、循环检测及标准 requirement 版本和环境标记校验。
|
|
17
|
+
- `build/run/edit/dev` 接入本地依赖;运行前重新校验,避免缓存过期的依赖集合。
|
|
18
|
+
- **构建行为变更**:主项目优先,完整与增量构建统一依赖顺序;删除高优先级文件后恢复低优先级来源。
|
|
19
|
+
- 构建前拒绝输出与源目录重叠;无效依赖不清空已有输出。
|
|
20
|
+
- Windows 链接保留其他项目内容,支持无符号链接权限时使用目录 junction;游戏启动使用实际进程句柄。
|
|
21
|
+
- 修正最低 Python 版本为 3.9,与现有运行代码要求一致。
|
|
22
|
+
- 显式依赖 pip,确保通过 uv 安装的隔离工具环境也能执行 Python 包的添加和初始化安装。
|
|
23
|
+
- 新增自动化测试、打包校验和 GitHub Actions → PyPI Trusted Publishing 流程。
|
|
24
|
+
|
|
25
|
+
本机已通过 33 项自动化测试及 MCS 3.10.0.420447 的四组实际游戏加载测试。
|
mcpywrap-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcpywrap
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: A wrapper for Minecraft China Edition Addons management
|
|
5
|
+
Author-email: boybook <boybook@easecation.net>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/EaseCation/mcpywrap
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/EaseCation/mcpywrap/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pip>=21
|
|
15
|
+
Requires-Dist: click>=8.0.0
|
|
16
|
+
Requires-Dist: watchdog>=2.0.0
|
|
17
|
+
Requires-Dist: twine>=3.4.0
|
|
18
|
+
Requires-Dist: tomli
|
|
19
|
+
Requires-Dist: tomli-w
|
|
20
|
+
Requires-Dist: setuptools
|
|
21
|
+
Requires-Dist: packaging>=21
|
|
22
|
+
Requires-Dist: PyQt5>=5.15.0
|
|
23
|
+
Requires-Dist: psutil>=5.8.0
|
|
24
|
+
Requires-Dist: rich>=10.0.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# mcpywrap
|
|
28
|
+
|
|
29
|
+
**用 Python 标准项目与依赖管理方式开发《我的世界》中国版 Mod 和资源包。**
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/mcpywrap/)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
34
|
+
mcpywrap 使用 `pyproject.toml` 管理项目,支持安装 Python 包依赖,也支持直接引用本地 Addon 目录。它将依赖管理、资源构建、游戏运行和 MC Studio 编辑器串成一套开发流程,方便多个项目共享代码与资源。
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
需要 Python 3.9 或更高版本。游戏和编辑器启动功能需要 Windows、MC Studio 及已下载的游戏引擎。
|
|
39
|
+
|
|
40
|
+
推荐通过 [uv](https://docs.astral.sh/uv/) 安装:
|
|
41
|
+
|
|
42
|
+
```powershell
|
|
43
|
+
uv tool install mcpywrap
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
也可以使用 `pip install mcpywrap`。安装完成后,运行 `mcpy --help` 查看命令。
|
|
47
|
+
|
|
48
|
+
## 开始使用
|
|
49
|
+
|
|
50
|
+
进入你的 Addon 或地图项目目录,按向导初始化:
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
mcpy init
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
之后可以直接运行游戏测试,或在 MC Studio 编辑器中打开项目:
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
mcpy run
|
|
60
|
+
mcpy edit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
需要查看项目和管理依赖时,运行 `mcpy ui` 打开图形界面。
|
|
64
|
+
|
|
65
|
+
## 复用代码和资源
|
|
66
|
+
|
|
67
|
+
对于已发布的 Python 包,使用包名添加依赖:
|
|
68
|
+
|
|
69
|
+
```powershell
|
|
70
|
+
mcpy add "package-name>=1.0"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
对于本机已有的 Addon,直接引用它的目录:
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
mcpy add --path "../shared-addon"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
本地 Addon 无需先初始化或安装,可以直接使用 MCS 导出的目录。请选择包含行为包或资源包的 Addon 根目录;相对路径以当前项目目录为基准。
|
|
80
|
+
|
|
81
|
+
移除依赖使用 `mcpy remove <包名>` 或 `mcpy remove --path <目录>`。移除本地引用不会删除源目录。不带参数运行 `mcpy add` / `mcpy remove` 可进入选择向导。
|
|
82
|
+
|
|
83
|
+
本地路径适合同机开发;与他人共享项目时,需要同步这些目录,或将可复用组件发布为 Python 包。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
|
|
84
|
+
|
|
85
|
+
## 构建与日常开发
|
|
86
|
+
|
|
87
|
+
| 命令 | 用途 |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `mcpy build` | 将项目和依赖构建到配置的输出目录 |
|
|
90
|
+
| `mcpy dev` | 监控 Addon 源码与依赖变化,持续更新构建结果 |
|
|
91
|
+
| `mcpy mod` | 通过向导创建 Python Mod 框架 |
|
|
92
|
+
| `mcpy modsdk` | 管理网易 ModSDK |
|
|
93
|
+
| `mcpy run -n` | 创建新的游戏测试实例 |
|
|
94
|
+
| `mcpy run -l` | 查看已有实例 |
|
|
95
|
+
| `mcpy run -d <ID前缀>` | 删除指定实例 |
|
|
96
|
+
|
|
97
|
+
`mcpy run` 默认复用最近创建的实例。构建时主项目内容优先于依赖;修改依赖声明后,请重新启动 `mcpy dev`。
|
|
98
|
+
|
|
99
|
+
## 游戏启动与排查
|
|
100
|
+
|
|
101
|
+
通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
|
|
102
|
+
|
|
103
|
+
遇到找不到游戏或缺少资源的提示,先运行:
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
mcpy doctor
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
需要使用特定版本时,可以临时指定:
|
|
110
|
+
|
|
111
|
+
```powershell
|
|
112
|
+
mcpy run --engine-version 3.10.0.420447
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
已有实例默认保留原引擎版本;显式指定版本可以切换。自定义路径、项目级设置和环境变量的用法见[引擎配置参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/engine-discovery.md)。
|
|
116
|
+
|
|
117
|
+
## 更多信息
|
|
118
|
+
|
|
119
|
+
- 运行 `mcpy <命令> --help` 查看该命令的选项。
|
|
120
|
+
- [更新记录](https://github.com/EaseCation/mcpywrap/blob/main/CHANGELOG.md)
|
|
121
|
+
- [开发与验证](https://github.com/EaseCation/mcpywrap/blob/main/docs/development.md) · [发布流程](https://github.com/EaseCation/mcpywrap/blob/main/docs/releasing.md)
|
|
122
|
+
- 欢迎通过 [Issues](https://github.com/EaseCation/mcpywrap/issues) 反馈问题或提交 PR。
|
|
123
|
+
|
|
124
|
+
[MIT License](LICENSE) © EaseCation
|
mcpywrap-0.3.1/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# mcpywrap
|
|
2
|
+
|
|
3
|
+
**用 Python 标准项目与依赖管理方式开发《我的世界》中国版 Mod 和资源包。**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/mcpywrap/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
mcpywrap 使用 `pyproject.toml` 管理项目,支持安装 Python 包依赖,也支持直接引用本地 Addon 目录。它将依赖管理、资源构建、游戏运行和 MC Studio 编辑器串成一套开发流程,方便多个项目共享代码与资源。
|
|
9
|
+
|
|
10
|
+
## 安装
|
|
11
|
+
|
|
12
|
+
需要 Python 3.9 或更高版本。游戏和编辑器启动功能需要 Windows、MC Studio 及已下载的游戏引擎。
|
|
13
|
+
|
|
14
|
+
推荐通过 [uv](https://docs.astral.sh/uv/) 安装:
|
|
15
|
+
|
|
16
|
+
```powershell
|
|
17
|
+
uv tool install mcpywrap
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
也可以使用 `pip install mcpywrap`。安装完成后,运行 `mcpy --help` 查看命令。
|
|
21
|
+
|
|
22
|
+
## 开始使用
|
|
23
|
+
|
|
24
|
+
进入你的 Addon 或地图项目目录,按向导初始化:
|
|
25
|
+
|
|
26
|
+
```powershell
|
|
27
|
+
mcpy init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
之后可以直接运行游戏测试,或在 MC Studio 编辑器中打开项目:
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
mcpy run
|
|
34
|
+
mcpy edit
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
需要查看项目和管理依赖时,运行 `mcpy ui` 打开图形界面。
|
|
38
|
+
|
|
39
|
+
## 复用代码和资源
|
|
40
|
+
|
|
41
|
+
对于已发布的 Python 包,使用包名添加依赖:
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
mcpy add "package-name>=1.0"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
对于本机已有的 Addon,直接引用它的目录:
|
|
48
|
+
|
|
49
|
+
```powershell
|
|
50
|
+
mcpy add --path "../shared-addon"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
本地 Addon 无需先初始化或安装,可以直接使用 MCS 导出的目录。请选择包含行为包或资源包的 Addon 根目录;相对路径以当前项目目录为基准。
|
|
54
|
+
|
|
55
|
+
移除依赖使用 `mcpy remove <包名>` 或 `mcpy remove --path <目录>`。移除本地引用不会删除源目录。不带参数运行 `mcpy add` / `mcpy remove` 可进入选择向导。
|
|
56
|
+
|
|
57
|
+
本地路径适合同机开发;与他人共享项目时,需要同步这些目录,或将可复用组件发布为 Python 包。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
|
|
58
|
+
|
|
59
|
+
## 构建与日常开发
|
|
60
|
+
|
|
61
|
+
| 命令 | 用途 |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `mcpy build` | 将项目和依赖构建到配置的输出目录 |
|
|
64
|
+
| `mcpy dev` | 监控 Addon 源码与依赖变化,持续更新构建结果 |
|
|
65
|
+
| `mcpy mod` | 通过向导创建 Python Mod 框架 |
|
|
66
|
+
| `mcpy modsdk` | 管理网易 ModSDK |
|
|
67
|
+
| `mcpy run -n` | 创建新的游戏测试实例 |
|
|
68
|
+
| `mcpy run -l` | 查看已有实例 |
|
|
69
|
+
| `mcpy run -d <ID前缀>` | 删除指定实例 |
|
|
70
|
+
|
|
71
|
+
`mcpy run` 默认复用最近创建的实例。构建时主项目内容优先于依赖;修改依赖声明后,请重新启动 `mcpy dev`。
|
|
72
|
+
|
|
73
|
+
## 游戏启动与排查
|
|
74
|
+
|
|
75
|
+
通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
|
|
76
|
+
|
|
77
|
+
遇到找不到游戏或缺少资源的提示,先运行:
|
|
78
|
+
|
|
79
|
+
```powershell
|
|
80
|
+
mcpy doctor
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
需要使用特定版本时,可以临时指定:
|
|
84
|
+
|
|
85
|
+
```powershell
|
|
86
|
+
mcpy run --engine-version 3.10.0.420447
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
已有实例默认保留原引擎版本;显式指定版本可以切换。自定义路径、项目级设置和环境变量的用法见[引擎配置参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/engine-discovery.md)。
|
|
90
|
+
|
|
91
|
+
## 更多信息
|
|
92
|
+
|
|
93
|
+
- 运行 `mcpy <命令> --help` 查看该命令的选项。
|
|
94
|
+
- [更新记录](https://github.com/EaseCation/mcpywrap/blob/main/CHANGELOG.md)
|
|
95
|
+
- [开发与验证](https://github.com/EaseCation/mcpywrap/blob/main/docs/development.md) · [发布流程](https://github.com/EaseCation/mcpywrap/blob/main/docs/releasing.md)
|
|
96
|
+
- 欢迎通过 [Issues](https://github.com/EaseCation/mcpywrap/issues) 反馈问题或提交 PR。
|
|
97
|
+
|
|
98
|
+
[MIT License](LICENSE) © EaseCation
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# 开发与验证
|
|
2
|
+
|
|
3
|
+
```powershell
|
|
4
|
+
python -m unittest discover -s tests -v
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
自动化测试使用临时目录、Click、Qt 离屏及安装/启动替身。本机多项目验收脚本另行执行真实 CLI、pip、watchdog,可加 `--game` 启动已安装的 MCS 引擎。请先关闭已有游戏,使用独立环境和一个尚不存在的输出目录:
|
|
8
|
+
|
|
9
|
+
```powershell
|
|
10
|
+
uv venv test/acceptance-venv
|
|
11
|
+
uv pip install --python test/acceptance-venv/Scripts/python.exe -e . pip
|
|
12
|
+
test/acceptance-venv/Scripts/python.exe tests/manual_integration.py --workspace test/my-acceptance --game
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
脚本保留测试项目、构建结果、CLI/pip 日志和 `report.json`;实际游戏验证检查各 Mod 的服务端/客户端加载标记及游戏生成的包 UUID 列表,并清理本次创建的全局链接。测试存档和 `.runtime` 配置保留供复查。游戏验收仅替换日志窗口为 TCP 文件收集器,真实引擎、目录链接和运行配置均使用产品实现。
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# 游戏引擎发现与诊断
|
|
2
|
+
|
|
3
|
+
`mcpy run` 和 `mcpy edit` 共用引擎发现机制,图形界面使用同样的项目配置和环境变量。
|
|
4
|
+
默认先读取当前用户 MC Studio 注册表的两个视图,选择该安装中最新的完整引擎。
|
|
5
|
+
注册表安装无法满足要求时,再检查各固定磁盘根目录的
|
|
6
|
+
`MCStudioDownload/game/MinecraftPE_Netease`,合并候选后按版本选择。
|
|
7
|
+
不会读取运行进程、访问网络盘或递归扫描全盘,也不会自动保存发现的机器路径。
|
|
8
|
+
|
|
9
|
+
只有版本目录名有效且包含 `Minecraft.Windows.exe` 普通文件的目录才会入选。
|
|
10
|
+
例如 `3.10` 高于 `3.9`;临时目录、备份目录及未下载完成的版本会被跳过。
|
|
11
|
+
同版本按绝对路径排序,结果稳定;每次操作重新发现,操作内复用同一个结果。
|
|
12
|
+
|
|
13
|
+
先运行只读诊断,无需初始化 Mod 项目:
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
mcpy doctor
|
|
17
|
+
mcpy doctor --json
|
|
18
|
+
mcpy doctor --engine-version 3.10.0.420447
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
诊断会显示候选与选中路径、注册表或磁盘来源、跳过原因,以及运行、编辑器、
|
|
22
|
+
Safaia 分别缺少的资源。JSON 包含 `candidates`、`selected`、`diagnostics`、
|
|
23
|
+
`error`、`resources` 和 `ok`。游戏发现或运行资源检查失败返回退出码 1;
|
|
24
|
+
编辑器、Safaia 缺失单独报告,不影响仅运行游戏的就绪状态。
|
|
25
|
+
|
|
26
|
+
### 固定路径和版本
|
|
27
|
+
|
|
28
|
+
以下字段均可选,可加入 Mod 项目的 `pyproject.toml`:
|
|
29
|
+
|
|
30
|
+
```toml
|
|
31
|
+
[tool.mcpywrap]
|
|
32
|
+
game_executable_path = 'D:\MCStudioDownload\game\MinecraftPE_Netease\3.10.0.420447\Minecraft.Windows.exe'
|
|
33
|
+
mcs_download_path = 'D:\MCStudioDownload'
|
|
34
|
+
engine_version = '3.10.0.420447'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
每个字段独立采用 **命令行 > 环境变量 > 项目配置** 的优先级。
|
|
38
|
+
团队项目通常只提交 `engine_version`,本机路径可放在环境变量中:
|
|
39
|
+
|
|
40
|
+
| 项目配置 | 环境变量 | 命令行选项 |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `game_executable_path` | `MCPY_GAME_EXECUTABLE` | `--game-executable` |
|
|
43
|
+
| `mcs_download_path` | `MCPY_MCS_DOWNLOAD_PATH` | `--mcs-download-path` |
|
|
44
|
+
| `engine_version` | `MCPY_ENGINE_VERSION` | `--engine-version` |
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
$env:MCPY_MCS_DOWNLOAD_PATH = 'D:\MCStudioDownload'
|
|
48
|
+
mcpy run --engine-version 3.10.0.420447
|
|
49
|
+
mcpy edit --mcs-download-path 'D:\MCStudioDownload'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
三个选项均适用于 `run`、`edit` 和 `doctor`。项目中的相对路径以
|
|
53
|
+
`pyproject.toml` 所在目录为基准;命令行与环境变量中的相对路径以调用目录为基准。
|
|
54
|
+
不自动向上搜索项目。版本需要填写实际完整版本,`3.10` 不作为 `3.10.*` 通配符。
|
|
55
|
+
|
|
56
|
+
显式路径无效、版本不存在或路径之间冲突时会报错,不悄悄改用其他安装。
|
|
57
|
+
未指定版本时,已有运行实例保留原版本,新实例选择默认最新完整版本;
|
|
58
|
+
显式指定版本可以主动升级已有实例。找不到旧实例版本时,会列出当前可用版本。
|
|
59
|
+
|
|
60
|
+
### 非标准目录与资源要求
|
|
61
|
+
|
|
62
|
+
显式 EXE 位于标准目录时,会反推关联下载目录;若同时指定下载目录,二者必须一致。
|
|
63
|
+
EXE 父目录不是版本号时,必须另外配置 `engine_version`。这里的版本是声明或目录信息,
|
|
64
|
+
不是对二进制内部版本的验证。
|
|
65
|
+
|
|
66
|
+
独立 EXE 可被发现,但仍需通过 `mcs_download_path` 或有效注册表定位资源目录。
|
|
67
|
+
当前本地世界启动需要下载目录中的 `componentcache/support/steve/steve.png`,
|
|
68
|
+
以及 `%APPDATA%/MinecraftPE_Netease/games/com.netease` 用户数据目录。
|
|
69
|
+
编辑器另需下载目录中的 `MCX64Editor/MC_Editor.exe`、`EngineAssert` 和启动器安装目录中的
|
|
70
|
+
`data/inner_res`;Safaia 单独检查。缺少资源时按诊断提示补齐,本工具不自动下载这些资源。
|
|
71
|
+
|
|
72
|
+
此功能不包含指定 IP/端口的第三方网络服启动。
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# 本地目录依赖参考
|
|
2
|
+
|
|
3
|
+
主项目仍使用 `pyproject.toml` 管理项目;引用的 Addon 可以是直接从 MCS 导出的目录,**无需 `mcpy init`、无需 `pyproject.toml`,也无需 pip 安装**。
|
|
4
|
+
|
|
5
|
+
```toml
|
|
6
|
+
[project]
|
|
7
|
+
name = "my-addon"
|
|
8
|
+
version = "0.1.0"
|
|
9
|
+
dependencies = ["published-addon>=1.0"]
|
|
10
|
+
|
|
11
|
+
[tool.mcpywrap]
|
|
12
|
+
project_type = "addon"
|
|
13
|
+
target_dir = "./build"
|
|
14
|
+
local_dependencies = [
|
|
15
|
+
"../shared-addon",
|
|
16
|
+
"D:/Minecraft/common-resources",
|
|
17
|
+
]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
有效目录至少包含一个行为包或资源包及其 `manifest.json` 或 `pack_manifest.json`,例如:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
shared-addon/
|
|
24
|
+
behavior_pack/ # 也支持 behavior_pack_*、BehaviorPack*
|
|
25
|
+
manifest.json
|
|
26
|
+
sharedScript/
|
|
27
|
+
resource_pack/ # 也支持 resource_pack_*、ResourcePack*
|
|
28
|
+
manifest.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
第一版不支持把单独的行为包/资源包、地图根目录、普通源码目录作为本地依赖,也不支持同一 Addon 中多个同类型包。不必同时具备行为包和资源包。
|
|
32
|
+
|
|
33
|
+
相对路径相对于**声明它的项目目录**解析,与执行命令前的其他工作目录无关。目标目录存在 `[tool.mcpywrap]` 时,继续读取该项目的 Python 和本地依赖;否则视为叶节点。读取不会初始化、安装或修改目标目录。重复引用按真实路径去重(包含 Windows 大小写和目录链接);循环引用会报告引用链。同名目录使用不同内部标识,避免链接覆盖。
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
mcpy add --path "../共享 Addon"
|
|
37
|
+
mcpy add --path "D:/Minecraft/common-resources"
|
|
38
|
+
mcpy remove --path "../共享 Addon"
|
|
39
|
+
|
|
40
|
+
# 不带参数进入来源选择/直接依赖选择向导
|
|
41
|
+
mcpy add
|
|
42
|
+
mcpy remove
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
CLI 保留传入的相对或绝对写法,不根据包参数猜测路径。`--path` 不能与包参数同时使用;本地移除不接受 `--uninstall`。目录失效后仍可移除声明。非交互环境必须传入参数,否则返回非零退出码。`mcpy init` 的依赖收集也支持两种来源。
|
|
46
|
+
|
|
47
|
+
添加目录时会先验证结构和依赖图,无效或循环引用不会写入配置。其 Python 依赖未安装或版本不满足时,会显示警告及可复制的 `mcpy add "requirement"` 命令,允许保存本地引用,但**不会自动安装**。构建、运行、编辑器启动前会重新完整校验。Python 包仍通过当前工具环境的 pip 安装,安装失败不保存新增声明;环境标记为假的 requirement 不参与当前平台的依赖图,普通 Python 库也不会当成 Addon 加载。
|
|
48
|
+
|
|
49
|
+
### GUI 添加与移除
|
|
50
|
+
|
|
51
|
+
运行 `mcpy ui`,在“添加新依赖”选择“Python 包”或“本地目录”。包模式支持可编辑下拉框及补全,安装在后台执行,完成前禁用冲突操作;失败会保留输入并显示原因。
|
|
52
|
+
|
|
53
|
+
本地模式可输入路径或“浏览目录”,预览解析后的目录、包结构和将保存的路径。默认保存相对路径;跨盘保存绝对路径,也可勾选“保存为绝对路径”。**选择目录本身不保存,点击“添加依赖”才写入配置**。错误输入保留供修改。
|
|
54
|
+
|
|
55
|
+
依赖列表显示来源、声明和不可用状态,详细原因显示在提示中。移除本地依赖的确认框会说明仅移除引用,源目录保留。每次修改后刷新运行包集合,每次启动前重新校验;失效依赖可继续移除。配置更新只影响后续启动,不自动更改正在运行的游戏。
|
|
56
|
+
|
|
57
|
+
### 构建顺序与迁移
|
|
58
|
+
|
|
59
|
+
旧配置无需迁移,省略 `local_dependencies` 等同于空数组。只有本地依赖、没有 Python 包依赖的项目同样支持 `build/run/edit/dev`(地图项目仍不支持 `dev`)。本地引用只用于此工具,**不会自动成为可通过 pip 分发、安装或锁定的依赖**;分发时需自行安排资源和路径。
|
|
60
|
+
|
|
61
|
+
**行为变更:完整构建和增量构建现在都以主项目为最高优先级。** 顺序为子依赖先于引用者;同级先 Python 包列表、再本地列表,各列表按声明顺序处理,后处理者覆盖重复文件/合并键;主项目最后。共享子依赖只处理一次,采用稳定的依赖优先遍历。保留已有 JSON/语言文件合并规则及主项目 manifest。删除高优先级来源文件后,增量构建会恢复低优先级来源内容。
|
|
62
|
+
|
|
63
|
+
构建在清空输出前校验依赖及路径,拒绝输出覆盖源项目/源包或与依赖目录重叠。`dev` 监控主项目和解析到的所有依赖目录,修改依赖声明后需要重启监控。
|
|
64
|
+
|
|
65
|
+
`run/edit` 仍将依赖作为独立包链接/编辑器包路径传递,以上构建覆盖规则不代表游戏引擎自身的资源包加载优先级。Windows 优先使用符号链接,无符号链接权限时可使用目录 junction;复用已有同目标链接,保留其他项目链接,同名冲突明确报错。
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 发布流程
|
|
2
|
+
|
|
3
|
+
主分支和 PR 运行 Windows Python 3.9、3.12、3.14 的自动化测试。通过后构建 wheel/sdist、运行严格元数据检查、检查产物内容,并在独立环境安装 wheel 验证 CLI 和模块导入。
|
|
4
|
+
|
|
5
|
+
发布凭据使用 PyPI Trusted Publishing,不在仓库或工作流中保存长期 API token。在 PyPI 项目 `mcpywrap` 的 Publishing 设置添加 GitHub publisher:
|
|
6
|
+
|
|
7
|
+
| 字段 | 值 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Owner | `EaseCation` |
|
|
10
|
+
| Repository | `mcpywrap` |
|
|
11
|
+
| Workflow filename | `release.yml` |
|
|
12
|
+
| Environment | `pypi` |
|
|
13
|
+
|
|
14
|
+
每次发布:
|
|
15
|
+
|
|
16
|
+
1. 同步修改 `pyproject.toml` 和 `mcpywrap/__init__.py` 中的版本,更新 CHANGELOG。
|
|
17
|
+
2. 将通过本地验证的提交推送到 `main`,等待 CI 全部通过。
|
|
18
|
+
3. 在该提交创建匹配版本的 `vX.Y.Z` 标签并推送,触发 CI 再次检查及发布。标签版本必须匹配元数据,且提交必须位于 `main` 历史上。
|
|
19
|
+
4. 检查 Actions 的 publish job 和 PyPI 新版本文件。发布失败时先排除原因;若是首次配置 publisher 尚未完成,可完成 PyPI 配置后重跑失败任务。
|
|
20
|
+
|
|
21
|
+
也可手动运行 `release.yml`:选择 `main` 并设置 `publish=true`。默认手动运行只验证,不发布。发布只允许标签推送或显式开启发布的 main 手动运行,PR 和普通 main 推送均不会上传 PyPI。
|
|
22
|
+
|
|
23
|
+
不要覆盖已有 PyPI 版本。发布动作开启 PyPI provenance attestations;各第三方 Action 固定到已核实的提交。GitHub `pypi` environment 与 PyPI publisher 的 environment 必须一致。
|
|
@@ -49,12 +49,11 @@ class AddonsPack(object):
|
|
|
49
49
|
|
|
50
50
|
def __init__(self, pkg_name, path, is_origin=False):
|
|
51
51
|
self.pkg_name = pkg_name
|
|
52
|
-
self.path = path
|
|
52
|
+
self.path = os.path.abspath(path)
|
|
53
53
|
self.is_origin = is_origin
|
|
54
54
|
self.behavior_pack_dir = None
|
|
55
55
|
self.resource_pack_dir = None
|
|
56
56
|
# 进入此目录,查找内部的行为包和资源包的路径
|
|
57
|
-
os.chdir(self.path)
|
|
58
57
|
for item in os.listdir(self.path):
|
|
59
58
|
item_path = os.path.join(self.path, item)
|
|
60
59
|
if os.path.isdir(item_path):
|
|
@@ -280,25 +279,15 @@ class AddonsPack(object):
|
|
|
280
279
|
包类型: "behavior" 或 "resource"
|
|
281
280
|
相对路径: 在包中的相对路径,如果不在包中则为None
|
|
282
281
|
"""
|
|
283
|
-
#
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
parts = file_path.split(os.sep)
|
|
295
|
-
for i, part in enumerate(parts):
|
|
296
|
-
if (is_behavior and ("behavior_pack" in part.lower() or "behaviorpack" in part.lower())) or \
|
|
297
|
-
(is_resource and ("resource_pack" in part.lower() or "resourcepack" in part.lower())):
|
|
298
|
-
if i + 1 < len(parts):
|
|
299
|
-
rel_path = os.path.join(*parts[i+1:])
|
|
300
|
-
return True, pack_type, rel_path
|
|
301
|
-
else:
|
|
302
|
-
return True, pack_type, ""
|
|
303
|
-
|
|
282
|
+
# Watchdog 可能返回短路径或目录链接路径;保留相对文件名大小写。
|
|
283
|
+
absolute = os.path.realpath(file_path)
|
|
284
|
+
for kind, folder in (("behavior", self.behavior_pack_dir), ("resource", self.resource_pack_dir)):
|
|
285
|
+
if not folder:
|
|
286
|
+
continue
|
|
287
|
+
folder = os.path.realpath(folder)
|
|
288
|
+
try:
|
|
289
|
+
if os.path.commonpath([os.path.normcase(absolute), os.path.normcase(folder)]) == os.path.normcase(folder):
|
|
290
|
+
return True, kind, os.path.relpath(absolute, folder)
|
|
291
|
+
except ValueError:
|
|
292
|
+
continue
|
|
304
293
|
return False, None, None
|
|
@@ -21,25 +21,23 @@ class MapPack(object):
|
|
|
21
21
|
def __init__(self, pkg_name: str, path: str):
|
|
22
22
|
self.pkg_name = pkg_name
|
|
23
23
|
self.path = path
|
|
24
|
+
self.behavior_packs = []
|
|
25
|
+
self.resource_packs = []
|
|
24
26
|
|
|
25
27
|
# 读取配置文件
|
|
26
28
|
self.behavior_packs_dir = os.path.join(self.path, "behavior_packs")
|
|
27
29
|
self.resource_packs_dir = os.path.join(self.path, "resource_packs")
|
|
28
30
|
|
|
29
|
-
if not os.path.exists(self.behavior_packs_dir):
|
|
30
|
-
os.makedirs(self.behavior_packs_dir)
|
|
31
31
|
|
|
32
32
|
# 遍历行为包目录
|
|
33
|
-
for pack in os.listdir(self.behavior_packs_dir):
|
|
33
|
+
for pack in sorted(os.listdir(self.behavior_packs_dir)) if os.path.isdir(self.behavior_packs_dir) else []:
|
|
34
34
|
pack_path = os.path.join(self.behavior_packs_dir, pack)
|
|
35
35
|
if os.path.isdir(pack_path):
|
|
36
36
|
self.behavior_packs.append(pack_path)
|
|
37
37
|
|
|
38
|
-
if not os.path.exists(self.resource_packs_dir):
|
|
39
|
-
os.makedirs(self.resource_packs_dir)
|
|
40
38
|
|
|
41
39
|
# 遍历资源包目录
|
|
42
|
-
for pack in os.listdir(self.resource_packs_dir):
|
|
40
|
+
for pack in sorted(os.listdir(self.resource_packs_dir)) if os.path.isdir(self.resource_packs_dir) else []:
|
|
43
41
|
pack_path = os.path.join(self.resource_packs_dir, pack)
|
|
44
42
|
if os.path.isdir(pack_path):
|
|
45
43
|
self.resource_packs.append(pack_path)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""完整构建和增量更新共用相同的文件合并规则。"""
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ..dependencies import DependencyError, canonical_path
|
|
7
|
+
from .AddonsPack import MANIFEST_FILES
|
|
8
|
+
from .file_merge import try_merge_file
|
|
9
|
+
|
|
10
|
+
MERGED_JSON = {'blocks.json', 'terrain_texture.json', 'item_texture.json', 'sounds.json',
|
|
11
|
+
'sound_definitions.json', 'animations.json', 'animation_controllers.json',
|
|
12
|
+
'entity_models.json', 'render_controllers.json', 'materials.json',
|
|
13
|
+
'attachables.json', 'particle_effects.json', '_ui_defs.json'}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_within(path, parent):
|
|
17
|
+
try:
|
|
18
|
+
return os.path.commonpath([canonical_path(path), canonical_path(parent)]) == canonical_path(parent)
|
|
19
|
+
except ValueError:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def validate_target(target, root, packs):
|
|
24
|
+
if not target:
|
|
25
|
+
raise DependencyError('未指定构建输出目录')
|
|
26
|
+
# 根目录下的独立 build 允许存在,但不能覆盖任意源包或依赖项目。
|
|
27
|
+
if is_within(root, target):
|
|
28
|
+
raise DependencyError(f'构建输出会覆盖主项目: {target}')
|
|
29
|
+
for pack in packs:
|
|
30
|
+
protected = [pack.path] if not pack.is_origin else [pack.behavior_pack_dir, pack.resource_pack_dir]
|
|
31
|
+
for source in filter(None, protected):
|
|
32
|
+
if is_within(source, target) or is_within(target, source):
|
|
33
|
+
raise DependencyError(f'构建输出与源目录重叠: {target} <-> {source}')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def pack_files(pack, kind):
|
|
37
|
+
folder = getattr(pack, kind + '_pack_dir')
|
|
38
|
+
if not folder or not os.path.isdir(folder):
|
|
39
|
+
return
|
|
40
|
+
for current, dirs, files in os.walk(folder):
|
|
41
|
+
dirs[:] = sorted(d for d in dirs if not pack.should_exclude(os.path.join(current, d)))
|
|
42
|
+
for name in sorted(files):
|
|
43
|
+
source = Path(current) / name
|
|
44
|
+
if name in MANIFEST_FILES or not pack.should_exclude(str(source)):
|
|
45
|
+
yield source.relative_to(folder).as_posix()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def rebuild_file(packs, kind, relative, destination):
|
|
49
|
+
"""按依赖优先序重建单个产物;所有来源删除后同步删除产物。"""
|
|
50
|
+
destination = Path(destination)
|
|
51
|
+
if destination.is_file() or destination.is_symlink():
|
|
52
|
+
destination.unlink()
|
|
53
|
+
sources = []
|
|
54
|
+
for pack in packs:
|
|
55
|
+
folder = getattr(pack, kind + '_pack_dir')
|
|
56
|
+
if not folder:
|
|
57
|
+
continue
|
|
58
|
+
source = Path(folder) / relative
|
|
59
|
+
if source.is_file() and (source.name in MANIFEST_FILES or not pack.should_exclude(str(source))):
|
|
60
|
+
sources.append((pack, source))
|
|
61
|
+
if relative in MANIFEST_FILES:
|
|
62
|
+
# 优先保留主包 manifest;主包缺少该包类型时使用最后一个依赖的 manifest。
|
|
63
|
+
manifest_owner = next((pack for pack in reversed(packs)
|
|
64
|
+
if getattr(pack, kind + '_pack_dir') and any(
|
|
65
|
+
(Path(getattr(pack, kind + '_pack_dir')) / name).is_file()
|
|
66
|
+
for name in MANIFEST_FILES)), None)
|
|
67
|
+
sources = [(pack, source) for pack, source in sources if pack is manifest_owner]
|
|
68
|
+
for pack, source in sources:
|
|
69
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
if destination.exists() and (source.name in MERGED_JSON or source.suffix == '.lang'):
|
|
71
|
+
success, error = try_merge_file(str(source), str(destination))
|
|
72
|
+
if not success:
|
|
73
|
+
raise DependencyError(error)
|
|
74
|
+
elif source.suffix == '.py':
|
|
75
|
+
pack._copy_with_encoding_check(str(source), str(destination))
|
|
76
|
+
else:
|
|
77
|
+
shutil.copy2(source, destination)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def assemble_addon(packs, target_pack):
|
|
81
|
+
for kind in ('behavior', 'resource'):
|
|
82
|
+
target = getattr(target_pack, kind + '_pack_dir')
|
|
83
|
+
paths = {relative for pack in packs for relative in pack_files(pack, kind)}
|
|
84
|
+
for relative in sorted(paths):
|
|
85
|
+
rebuild_file(packs, kind, relative, Path(target) / relative)
|