mcpywrap 0.3.0__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.
Files changed (104) hide show
  1. mcpywrap-0.3.2/CHANGELOG.md +36 -0
  2. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/MANIFEST.in +2 -0
  3. mcpywrap-0.3.2/PKG-INFO +157 -0
  4. mcpywrap-0.3.2/README.md +130 -0
  5. mcpywrap-0.3.2/docs/development.md +24 -0
  6. mcpywrap-0.3.2/docs/engine-discovery.md +55 -0
  7. mcpywrap-0.3.2/docs/local-dependencies.md +30 -0
  8. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/__init__.py +1 -1
  9. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/cli.py +24 -4
  10. mcpywrap-0.3.2/mcpywrap/command_context.py +130 -0
  11. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/add_cmd.py +5 -3
  12. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/build_cmd.py +8 -8
  13. mcpywrap-0.3.2/mcpywrap/commands/default_cmd.py +14 -0
  14. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/dependency_prompt.py +2 -1
  15. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/dev_cmd.py +9 -4
  16. mcpywrap-0.3.2/mcpywrap/commands/doctor_cmd.py +33 -0
  17. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/edit_cmd.py +31 -11
  18. mcpywrap-0.3.2/mcpywrap/commands/init_cmd.py +42 -0
  19. mcpywrap-0.3.2/mcpywrap/commands/mod_cmd.py +45 -0
  20. mcpywrap-0.3.2/mcpywrap/commands/modsdk_cmd.py +26 -0
  21. mcpywrap-0.3.2/mcpywrap/commands/package_cmd.py +79 -0
  22. mcpywrap-0.3.2/mcpywrap/commands/publish_cmd.py +51 -0
  23. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/remove_cmd.py +12 -5
  24. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/run_cmd.py +74 -42
  25. mcpywrap-0.3.2/mcpywrap/commands/session_cmd.py +29 -0
  26. mcpywrap-0.3.2/mcpywrap/commands/sync_cmd.py +33 -0
  27. mcpywrap-0.3.2/mcpywrap/commands/ui_cmd.py +25 -0
  28. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/config.py +24 -51
  29. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/dependencies.py +1 -1
  30. mcpywrap-0.3.2/mcpywrap/mcstudio/discovery.py +337 -0
  31. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/editor.py +26 -22
  32. mcpywrap-0.3.2/mcpywrap/mcstudio/file_logs.py +65 -0
  33. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/game.py +23 -48
  34. mcpywrap-0.3.2/mcpywrap/mcstudio/log_protocol.py +52 -0
  35. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/mcs.py +15 -50
  36. mcpywrap-0.3.2/mcpywrap/mcstudio/processes.py +28 -0
  37. mcpywrap-0.3.2/mcpywrap/mcstudio/session_worker.py +55 -0
  38. mcpywrap-0.3.2/mcpywrap/mcstudio/sessions.py +106 -0
  39. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/studio_server.py +7 -51
  40. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/minecraft/netease_modsdk.py +4 -4
  41. mcpywrap-0.3.2/mcpywrap/project_init.py +73 -0
  42. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/utils/project_setup.py +3 -3
  43. mcpywrap-0.3.2/mcpywrap.egg-info/PKG-INFO +157 -0
  44. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap.egg-info/SOURCES.txt +26 -1
  45. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap.egg-info/requires.txt +1 -0
  46. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/pyproject.toml +2 -1
  47. mcpywrap-0.3.2/skills/mcpywrap/SKILL.md +89 -0
  48. mcpywrap-0.3.2/skills/mcpywrap/agents/openai.yaml +4 -0
  49. mcpywrap-0.3.2/skills/mcpywrap/references/troubleshooting.md +28 -0
  50. mcpywrap-0.3.2/skills/mcpywrap/scripts/bootstrap.ps1 +65 -0
  51. mcpywrap-0.3.2/skills/mcpywrap/scripts/game_window.py +442 -0
  52. mcpywrap-0.3.2/skills/mcpywrap/scripts/smoke.py +84 -0
  53. mcpywrap-0.3.2/tests/test_automation.py +301 -0
  54. mcpywrap-0.3.2/tests/test_engine_discovery.py +392 -0
  55. mcpywrap-0.3.2/tests/test_game_window_skill.py +226 -0
  56. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/tests/test_local_dependencies.py +7 -5
  57. mcpywrap-0.3.2/tests/test_package.py +159 -0
  58. mcpywrap-0.3.0/CHANGELOG.md +0 -15
  59. mcpywrap-0.3.0/PKG-INFO +0 -232
  60. mcpywrap-0.3.0/README.md +0 -206
  61. mcpywrap-0.3.0/mcpywrap/commands/default_cmd.py +0 -76
  62. mcpywrap-0.3.0/mcpywrap/commands/init_cmd.py +0 -628
  63. mcpywrap-0.3.0/mcpywrap/commands/mod_cmd.py +0 -26
  64. mcpywrap-0.3.0/mcpywrap/commands/modsdk_cmd.py +0 -65
  65. mcpywrap-0.3.0/mcpywrap/commands/publish_cmd.py +0 -45
  66. mcpywrap-0.3.0/mcpywrap/commands/ui_cmd.py +0 -22
  67. mcpywrap-0.3.0/mcpywrap.egg-info/PKG-INFO +0 -232
  68. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/LICENSE +0 -0
  69. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/docs/releasing.md +0 -0
  70. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/__main__.py +0 -0
  71. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/AddonsPack.py +0 -0
  72. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/MapPack.py +0 -0
  73. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/__init__.py +0 -0
  74. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/assembly.py +0 -0
  75. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/dependency_manager.py +0 -0
  76. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/file_merge.py +0 -0
  77. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/project_builder.py +0 -0
  78. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/builders/watcher.py +0 -0
  79. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/commands/__init__.py +0 -0
  80. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/custom_packaging.py +0 -0
  81. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
  82. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/__init__.py +0 -0
  83. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
  84. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
  85. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
  86. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
  87. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/mcstudio/symlinks.py +0 -0
  88. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/minecraft/__init__.py +0 -0
  89. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/minecraft/addons.py +0 -0
  90. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/minecraft/level_dat.py +0 -0
  91. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/minecraft/map.py +0 -0
  92. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
  93. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/minecraft/template/mod_template.py +0 -0
  94. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/ui/__init__.py +0 -0
  95. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/ui/project_ui.py +0 -0
  96. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/utils/__init__.py +0 -0
  97. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/utils/pip_error_parser.py +0 -0
  98. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/utils/print_guide.py +0 -0
  99. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap/utils/utils.py +0 -0
  100. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap.egg-info/dependency_links.txt +0 -0
  101. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap.egg-info/entry_points.txt +0 -0
  102. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/mcpywrap.egg-info/top_level.txt +0 -0
  103. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/setup.cfg +0 -0
  104. {mcpywrap-0.3.0 → mcpywrap-0.3.2}/tests/manual_integration.py +0 -0
@@ -0,0 +1,36 @@
1
+ # 更新记录
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
+
14
+ ## 0.3.1
15
+
16
+ - 完善游戏引擎自动发现:优先检查当前用户注册表的两个视图,失败后搜索固定磁盘中的标准下载目录。
17
+ - 仅选择包含游戏 EXE 的有效版本目录,跳过残缺安装和杂项目录,按语义版本稳定排序。
18
+ - 支持通过项目配置、环境变量和 CLI 指定游戏 EXE、下载目录及引擎版本;无效覆盖和版本冲突明确报错。
19
+ - 新增只读 `mcpy doctor` 和 JSON 诊断,分别报告游戏、编辑器及 Safaia 的资源状态。
20
+ - CLI 与 GUI 共用发现结果,运行配置和实际 EXE 保持一致;已有实例默认保留原版本,不再自动切换到最新版。
21
+ - 新增 27 项隔离测试,完整回归共 60 项通过;本机只读验证选中 MCS 引擎 3.10.0.420447。
22
+ - 精简 README 为上手指引,将依赖、引擎配置和开发验证细节整理为独立文档。
23
+
24
+ ## 0.3.0
25
+
26
+ - 新增本地 Addon 目录依赖:CLI、初始化向导和 GUI 支持直接添加、预检和移除目录引用,无需初始化或安装被引用项目。
27
+ - Python 包和本地目录共用依赖图,支持相对路径、真实路径去重、循环检测及标准 requirement 版本和环境标记校验。
28
+ - `build/run/edit/dev` 接入本地依赖;运行前重新校验,避免缓存过期的依赖集合。
29
+ - **构建行为变更**:主项目优先,完整与增量构建统一依赖顺序;删除高优先级文件后恢复低优先级来源。
30
+ - 构建前拒绝输出与源目录重叠;无效依赖不清空已有输出。
31
+ - Windows 链接保留其他项目内容,支持无符号链接权限时使用目录 junction;游戏启动使用实际进程句柄。
32
+ - 修正最低 Python 版本为 3.9,与现有运行代码要求一致。
33
+ - 显式依赖 pip,确保通过 uv 安装的隔离工具环境也能执行 Python 包的添加和初始化安装。
34
+ - 新增自动化测试、打包校验和 GitHub Actions → PyPI Trusted Publishing 流程。
35
+
36
+ 本机已通过 33 项自动化测试及 MCS 3.10.0.420447 的四组实际游戏加载测试。
@@ -1,6 +1,8 @@
1
1
  include LICENSE README.md CHANGELOG.md
2
2
  include docs/releasing.md
3
+ include docs/local-dependencies.md docs/engine-discovery.md docs/development.md
3
4
  recursive-include tests *.py
5
+ recursive-include skills *.md *.yaml *.ps1 *.py
4
6
  prune test
5
7
  prune .git
6
8
  global-exclude __pycache__ *.py[cod] *.cppconfig *.log
@@ -0,0 +1,157 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcpywrap
3
+ Version: 0.3.2
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: build>=1.0
16
+ Requires-Dist: click>=8.0.0
17
+ Requires-Dist: watchdog>=2.0.0
18
+ Requires-Dist: twine>=3.4.0
19
+ Requires-Dist: tomli
20
+ Requires-Dist: tomli-w
21
+ Requires-Dist: setuptools
22
+ Requires-Dist: packaging>=21
23
+ Requires-Dist: PyQt5>=5.15.0
24
+ Requires-Dist: psutil>=5.8.0
25
+ Requires-Dist: rich>=10.0.0
26
+ Dynamic: license-file
27
+
28
+ # mcpywrap
29
+
30
+ **用 Python 标准项目与依赖管理方式开发《我的世界》中国版 Mod 和资源包。**
31
+
32
+ [![PyPI Version](https://img.shields.io/pypi/v/mcpywrap)](https://pypi.org/project/mcpywrap/)
33
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
34
+
35
+ mcpywrap 使用 `pyproject.toml` 管理项目,支持安装 Python 包依赖,也支持直接引用本地 Addon 目录。它将依赖管理、资源构建、游戏运行和 MC Studio 编辑器串成一套开发流程,方便多个项目共享代码与资源。
36
+
37
+ ## 安装
38
+
39
+ 需要 Python 3.9 或更高版本。游戏和编辑器启动功能需要 Windows、MC Studio 及已下载的游戏引擎。
40
+
41
+ 推荐通过 [uv](https://docs.astral.sh/uv/) 安装:
42
+
43
+ ```powershell
44
+ uv tool install mcpywrap
45
+ ```
46
+
47
+ 也可以使用 `pip install mcpywrap`。安装完成后,运行 `mcpy --help` 查看命令。
48
+
49
+ ## 开始使用
50
+
51
+ 进入你的 Addon 或地图项目目录,按向导初始化:
52
+
53
+ ```powershell
54
+ mcpy init
55
+ ```
56
+
57
+ 之后可以直接运行游戏测试,或在 MC Studio 编辑器中打开项目:
58
+
59
+ ```powershell
60
+ mcpy run
61
+ mcpy edit
62
+ ```
63
+
64
+ 需要查看项目和管理依赖时,运行 `mcpy ui` 打开图形界面。
65
+
66
+ ## 复用代码和资源
67
+
68
+ 对于已发布的 Python 包,使用包名添加依赖:
69
+
70
+ ```powershell
71
+ mcpy add "package-name>=1.0"
72
+ ```
73
+
74
+ 对于本机已有的 Addon,直接引用它的目录:
75
+
76
+ ```powershell
77
+ mcpy add --path "../shared-addon"
78
+ ```
79
+
80
+ 本地 Addon 无需先初始化或安装,可以直接使用 MCS 导出的目录。请选择包含行为包或资源包的 Addon 根目录;相对路径以当前项目目录为基准。
81
+
82
+ 移除依赖使用 `mcpy remove <包名>` 或 `mcpy remove --path <目录>`。移除本地引用不会删除源目录。不带参数运行 `mcpy add` / `mcpy remove` 可进入选择向导。
83
+
84
+ 本地路径适合同机开发;与他人共享项目时,需要同步这些目录,或将可复用组件发布为 Python 包。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
85
+
86
+ ## 构建与日常开发
87
+
88
+ | 命令 | 用途 |
89
+ |---|---|
90
+ | `mcpy build` | 将项目和依赖构建到配置的输出目录 |
91
+ | `mcpy package` | 构建项目和依赖,在 `dist` 中生成可分发 ZIP |
92
+ | `mcpy dev` | 监控 Addon 源码与依赖变化,持续更新构建结果 |
93
+ | `mcpy mod` | 通过向导创建 Python Mod 框架 |
94
+ | `mcpy modsdk` | 管理网易 ModSDK |
95
+ | `mcpy run -n` | 创建新的游戏测试实例 |
96
+ | `mcpy run -l` | 查看已有实例 |
97
+ | `mcpy run -d <ID前缀>` | 删除指定实例 |
98
+
99
+ `mcpy run` 默认复用最近创建的实例。构建时主项目内容优先于依赖;修改依赖声明后,请重新启动 `mcpy dev`。
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
+
109
+ ## 游戏启动与排查
110
+
111
+ 通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
112
+
113
+ 遇到找不到游戏或缺少资源的提示,先运行:
114
+
115
+ ```powershell
116
+ mcpy doctor
117
+ ```
118
+
119
+ 需要使用特定版本时,可以临时指定:
120
+
121
+ ```powershell
122
+ mcpy run --engine-version 3.10.0.420447
123
+ ```
124
+
125
+ 已有实例默认保留原引擎版本;显式指定版本可以切换。自定义路径、项目级设置和环境变量的用法见[引擎配置参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/engine-discovery.md)。
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
+
150
+ ## 更多信息
151
+
152
+ - 运行 `mcpy <命令> --help` 查看该命令的选项。
153
+ - [更新记录](https://github.com/EaseCation/mcpywrap/blob/main/CHANGELOG.md)
154
+ - [开发与验证](https://github.com/EaseCation/mcpywrap/blob/main/docs/development.md) · [发布流程](https://github.com/EaseCation/mcpywrap/blob/main/docs/releasing.md)
155
+ - 欢迎通过 [Issues](https://github.com/EaseCation/mcpywrap/issues) 反馈问题或提交 PR。
156
+
157
+ [MIT License](LICENSE) © EaseCation
@@ -0,0 +1,130 @@
1
+ # mcpywrap
2
+
3
+ **用 Python 标准项目与依赖管理方式开发《我的世界》中国版 Mod 和资源包。**
4
+
5
+ [![PyPI Version](https://img.shields.io/pypi/v/mcpywrap)](https://pypi.org/project/mcpywrap/)
6
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](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 package` | 构建项目和依赖,在 `dist` 中生成可分发 ZIP |
65
+ | `mcpy dev` | 监控 Addon 源码与依赖变化,持续更新构建结果 |
66
+ | `mcpy mod` | 通过向导创建 Python Mod 框架 |
67
+ | `mcpy modsdk` | 管理网易 ModSDK |
68
+ | `mcpy run -n` | 创建新的游戏测试实例 |
69
+ | `mcpy run -l` | 查看已有实例 |
70
+ | `mcpy run -d <ID前缀>` | 删除指定实例 |
71
+
72
+ `mcpy run` 默认复用最近创建的实例。构建时主项目内容优先于依赖;修改依赖声明后,请重新启动 `mcpy dev`。
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
+
82
+ ## 游戏启动与排查
83
+
84
+ 通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
85
+
86
+ 遇到找不到游戏或缺少资源的提示,先运行:
87
+
88
+ ```powershell
89
+ mcpy doctor
90
+ ```
91
+
92
+ 需要使用特定版本时,可以临时指定:
93
+
94
+ ```powershell
95
+ mcpy run --engine-version 3.10.0.420447
96
+ ```
97
+
98
+ 已有实例默认保留原引擎版本;显式指定版本可以切换。自定义路径、项目级设置和环境变量的用法见[引擎配置参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/engine-discovery.md)。
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
+
123
+ ## 更多信息
124
+
125
+ - 运行 `mcpy <命令> --help` 查看该命令的选项。
126
+ - [更新记录](https://github.com/EaseCation/mcpywrap/blob/main/CHANGELOG.md)
127
+ - [开发与验证](https://github.com/EaseCation/mcpywrap/blob/main/docs/development.md) · [发布流程](https://github.com/EaseCation/mcpywrap/blob/main/docs/releasing.md)
128
+ - 欢迎通过 [Issues](https://github.com/EaseCation/mcpywrap/issues) 反馈问题或提交 PR。
129
+
130
+ [MIT License](LICENSE) © EaseCation
@@ -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,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """mcpywrap - 将 Python 3 代码转换为 Python 2 代码的工具"""
3
3
 
4
- __version__ = '0.3.0'
4
+ __version__ = '0.3.2'
@@ -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,22 +10,34 @@ 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
14
17
  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
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
17
23
 
18
24
 
19
- @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__)
20
30
  @click.pass_context
21
- def cli(ctx):
31
+ def cli(ctx, project, non_interactive, json_output):
22
32
  """mcpywrap - 《我的世界》中国版 依赖管理与项目构建工具"""
33
+ configure_context(project, non_interactive)
23
34
  # 如果没有提供子命令,则运行 default_cmd
24
35
  if ctx.invoked_subcommand is None:
25
36
  # 导入并运行默认命令
26
- default_cmd()
37
+ from .command_context import non_interactive as unattended
38
+ if unattended():
39
+ raise click.UsageError("请指定子命令,例如 doctor、init 或 sync")
40
+ return default_cmd.callback()
27
41
 
28
42
  # 注册其他子命令
29
43
  cli.add_command(modsdk_cmd, name='modsdk')
@@ -32,11 +46,17 @@ cli.add_command(add_cmd, name='add')
32
46
  cli.add_command(remove_cmd, name='remove')
33
47
  cli.add_command(build_cmd, name='build')
34
48
  cli.add_command(dev_cmd, name='dev')
49
+ cli.add_command(package_cmd, name='package')
35
50
  cli.add_command(publish_cmd, name='publish')
36
51
  cli.add_command(mod_cmd, name='mod')
37
52
  cli.add_command(run_cmd, name='run')
38
53
  cli.add_command(edit_cmd, name='edit')
39
54
  cli.add_command(ui_cmd, name='ui')
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)
40
60
 
41
61
  if __name__ == '__main__':
42
- cli()
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}')