mcpywrap 0.3.6__tar.gz → 0.3.8__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 (155) hide show
  1. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/CHANGELOG.md +16 -0
  2. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/MANIFEST.in +1 -0
  3. {mcpywrap-0.3.6/mcpywrap.egg-info → mcpywrap-0.3.8}/PKG-INFO +19 -6
  4. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/README.md +18 -5
  5. mcpywrap-0.3.8/docs/code-libraries.md +40 -0
  6. mcpywrap-0.3.8/docs/git-dependencies.md +80 -0
  7. mcpywrap-0.3.8/docs/local-dependencies.md +41 -0
  8. mcpywrap-0.3.8/docs/qumod.md +93 -0
  9. mcpywrap-0.3.8/mcpywrap/__init__.py +4 -0
  10. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/assembly.py +9 -2
  11. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/dependency_manager.py +45 -5
  12. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/project_builder.py +46 -6
  13. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/watcher.py +33 -7
  14. mcpywrap-0.3.8/mcpywrap/code_libraries.py +197 -0
  15. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/command_context.py +15 -1
  16. mcpywrap-0.3.8/mcpywrap/commands/add_cmd.py +66 -0
  17. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/build_cmd.py +4 -2
  18. mcpywrap-0.3.8/mcpywrap/commands/dependency_prompt.py +45 -0
  19. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/edit_cmd.py +3 -3
  20. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/init_cmd.py +10 -4
  21. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/mod_cmd.py +13 -1
  22. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/modsdk_cmd.py +1 -1
  23. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/package_cmd.py +1 -1
  24. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/remove_cmd.py +16 -7
  25. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/run_cmd.py +37 -2
  26. mcpywrap-0.3.8/mcpywrap/commands/sync_cmd.py +66 -0
  27. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/dependencies.py +111 -15
  28. mcpywrap-0.3.8/mcpywrap/framework_presets.py +22 -0
  29. mcpywrap-0.3.8/mcpywrap/frameworks.py +76 -0
  30. mcpywrap-0.3.8/mcpywrap/git_cache.py +129 -0
  31. mcpywrap-0.3.8/mcpywrap/git_projects.py +242 -0
  32. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/bridge_payload/Injector.exe +0 -0
  33. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/bridge_payload/Loader.dll +0 -0
  34. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/bridge_payload/McpyMcsAuth.dll +0 -0
  35. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/bridge_payload/manifest.json +3 -3
  36. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/game.py +15 -8
  37. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/network.py +2 -0
  38. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/symlinks.py +39 -1
  39. mcpywrap-0.3.8/mcpywrap/project_dependencies.py +164 -0
  40. mcpywrap-0.3.8/mcpywrap/source_files.py +60 -0
  41. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/ui/project_ui.py +160 -17
  42. {mcpywrap-0.3.6 → mcpywrap-0.3.8/mcpywrap.egg-info}/PKG-INFO +19 -6
  43. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap.egg-info/SOURCES.txt +18 -0
  44. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/pyproject.toml +1 -1
  45. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/SKILL.md +16 -2
  46. mcpywrap-0.3.8/skills/mcpywrap/references/project-dependencies.md +125 -0
  47. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/references/remote-testing.md +1 -1
  48. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/references/troubleshooting.md +2 -0
  49. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/scripts/bootstrap.py +5 -0
  50. mcpywrap-0.3.8/tests/manual_dependency_boundaries.py +118 -0
  51. mcpywrap-0.3.8/tests/test_code_libraries.py +185 -0
  52. mcpywrap-0.3.8/tests/test_dependency_boundaries.py +228 -0
  53. mcpywrap-0.3.8/tests/test_git_projects.py +269 -0
  54. mcpywrap-0.3.8/tests/test_launch_encoding.py +15 -0
  55. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_local_dependencies.py +19 -3
  56. mcpywrap-0.3.8/tests/test_pack_uuid_conflicts.py +32 -0
  57. mcpywrap-0.3.8/tests/test_qumod_workflow.py +248 -0
  58. mcpywrap-0.3.6/docs/local-dependencies.md +0 -30
  59. mcpywrap-0.3.6/mcpywrap/__init__.py +0 -4
  60. mcpywrap-0.3.6/mcpywrap/commands/add_cmd.py +0 -36
  61. mcpywrap-0.3.6/mcpywrap/commands/dependency_prompt.py +0 -22
  62. mcpywrap-0.3.6/mcpywrap/commands/sync_cmd.py +0 -33
  63. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/LICENSE +0 -0
  64. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/docs/development.md +0 -0
  65. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/docs/engine-discovery.md +0 -0
  66. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/docs/mcs-auth.md +0 -0
  67. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/docs/releasing.md +0 -0
  68. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/__main__.py +0 -0
  69. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/AddonsPack.py +0 -0
  70. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/MapPack.py +0 -0
  71. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/__init__.py +0 -0
  72. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/builders/file_merge.py +0 -0
  73. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/cli.py +0 -0
  74. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/__init__.py +0 -0
  75. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/connect_cmd.py +0 -0
  76. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/default_cmd.py +0 -0
  77. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/desktop_cmd.py +0 -0
  78. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/dev_cmd.py +0 -0
  79. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/doctor_cmd.py +0 -0
  80. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/publish_cmd.py +0 -0
  81. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/serve_cmd.py +0 -0
  82. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/session_cmd.py +0 -0
  83. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/commands/ui_cmd.py +0 -0
  84. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/config.py +0 -0
  85. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/custom_packaging.py +0 -0
  86. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
  87. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/__init__.py +0 -0
  88. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/auth_interaction.py +0 -0
  89. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/bridge_assets.py +0 -0
  90. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/bridge_payload/publisher.cer +0 -0
  91. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/diagnostics.py +0 -0
  92. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/discovery.py +0 -0
  93. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/editor.py +0 -0
  94. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/file_logs.py +0 -0
  95. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/log_protocol.py +0 -0
  96. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/mcs.py +0 -0
  97. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/mcs_auth.py +0 -0
  98. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/private_logs.py +0 -0
  99. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/processes.py +0 -0
  100. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
  101. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/session_worker.py +0 -0
  102. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/sessions.py +0 -0
  103. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/studio_server.py +0 -0
  104. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
  105. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
  106. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
  107. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/mcstudio/window.py +0 -0
  108. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/minecraft/__init__.py +0 -0
  109. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/minecraft/addons.py +0 -0
  110. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/minecraft/level_dat.py +0 -0
  111. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/minecraft/map.py +0 -0
  112. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/minecraft/netease_modsdk.py +0 -0
  113. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
  114. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/minecraft/template/mod_template.py +0 -0
  115. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/project_init.py +0 -0
  116. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/remote/__init__.py +0 -0
  117. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/remote/client.py +0 -0
  118. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/remote/http_server.py +0 -0
  119. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/remote/service.py +0 -0
  120. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/ui/__init__.py +0 -0
  121. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/utils/__init__.py +0 -0
  122. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/utils/pip_error_parser.py +0 -0
  123. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/utils/print_guide.py +0 -0
  124. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/utils/project_setup.py +0 -0
  125. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap/utils/utils.py +0 -0
  126. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap.egg-info/dependency_links.txt +0 -0
  127. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap.egg-info/entry_points.txt +0 -0
  128. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap.egg-info/requires.txt +0 -0
  129. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/mcpywrap.egg-info/top_level.txt +0 -0
  130. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/native/mcs_auth/Bridge.cs +0 -0
  131. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/native/mcs_auth/Injector.cs +0 -0
  132. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/native/mcs_auth/Loader.c +0 -0
  133. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/native/mcs_auth/publisher.cer +0 -0
  134. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/native/mcs_auth/publisher.json +0 -0
  135. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/scripts/build_mcs_auth_bridge.py +0 -0
  136. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/scripts/check_mcs_auth_bridge.py +0 -0
  137. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/setup.cfg +0 -0
  138. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/agents/openai.yaml +0 -0
  139. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/scripts/bootstrap.ps1 +0 -0
  140. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/scripts/game_window.py +0 -0
  141. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/skills/mcpywrap/scripts/smoke.py +0 -0
  142. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/manual_integration.py +0 -0
  143. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_auth_interaction.py +0 -0
  144. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_automation.py +0 -0
  145. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_engine_discovery.py +0 -0
  146. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_game_window_skill.py +0 -0
  147. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_local_auth_session.py +0 -0
  148. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_mcs_auth.py +0 -0
  149. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_mouse.py +0 -0
  150. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_network.py +0 -0
  151. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_network_sessions.py +0 -0
  152. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_package.py +0 -0
  153. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_remote.py +0 -0
  154. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_remote_scripts.py +0 -0
  155. {mcpywrap-0.3.6 → mcpywrap-0.3.8}/tests/test_skill_workflows.py +0 -0
@@ -1,5 +1,21 @@
1
1
  # 更新记录
2
2
 
3
+ ## 0.3.8
4
+
5
+ - 新增标准 Git 项目依赖:固定提交、导出结构识别、递归子依赖、循环/冲突检测、内容锁定,以及更新、回退和移除;克隆后通过 `mcpy sync` 恢复。
6
+ - 使用用户级不可变 Git 源码缓存与项目独立注册/组装,支持缓存路径覆盖、并发获取和 Windows 长路径;运行与发布不依赖工具 Python 的 site-packages。
7
+ - CLI 与 GUI 共用依赖服务;界面统一为“Git 依赖”,提供一键添加 QuMod 快捷操作。QuMod 仅为官方来源、已验证提交、布局及入口模板的数据预设。
8
+ - 保留既有 code_libraries 声明兼容;更新 Skill,明确 Python 包、本地 Addon、Git 依赖及新建/克隆/升级工作流。
9
+ - 修复 GBK 启动日志异常、Addon 构建失败破坏旧产物、重复 UUID 加载旧包、共享缓存锁初始化竞态;增量构建复核 Git 快照完整性。
10
+ - 兼容 Python 3.9 的 Windows 扩展路径解析,支持 Git 快照内相对引用与注册节点间的父目录引用;依赖许可证按节点独立保留。
11
+
12
+ ## 0.3.7
13
+
14
+ - 明确工具 Python 与游戏内置 Python 的边界:普通包保留为开发依赖,CLI、JSON 与 GUI 提示“仅开发环境”,不会自动进入游戏产物。
15
+ - 主项目、递归 Addon 依赖与地图包中的原生二进制会在构建、打包、运行或编辑前被拒绝;增量更新同样校验,失败保留已有产物。
16
+ - 包安装后校验候选声明,无效 Addon 不写配置;明确提示已发生的工具环境安装,不自动卸载。
17
+ - README 与 Skill 补充依赖分类、游戏兼容性及实际产物验收要求;开发项目和独立解包产物均已通过本机客户端/服务端共享源码加载测试。
18
+
3
19
  ## 0.3.6
4
20
 
5
21
  - 新增局域网 serve 服务与 --remote/--local 路由;macOS 本机开发、Windows 网络游戏测试共用 CLI 和 Skill。
@@ -2,6 +2,7 @@ include LICENSE README.md CHANGELOG.md
2
2
  include docs/releasing.md
3
3
  include docs/local-dependencies.md docs/engine-discovery.md docs/development.md
4
4
  include docs/mcs-auth.md
5
+ include docs/code-libraries.md docs/git-dependencies.md docs/qumod.md
5
6
  recursive-include tests *.py
6
7
  recursive-include skills *.md *.yaml *.ps1 *.py
7
8
  include scripts/build_mcs_auth_bridge.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpywrap
3
- Version: 0.3.6
3
+ Version: 0.3.8
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
  [![PyPI Version](https://img.shields.io/pypi/v/mcpywrap)](https://pypi.org/project/mcpywrap/)
33
33
  [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
34
34
 
35
- mcpywrap 使用 `pyproject.toml` 管理项目,支持安装 Python 包依赖,也支持直接引用本地 Addon 目录。它将依赖管理、资源构建、游戏运行和 MC Studio 编辑器串成一套开发流程,方便多个项目共享代码与资源。
35
+ mcpywrap 使用 `pyproject.toml` 管理开发侧依赖,复用本地 Addon 的代码与资源,并串联构建、游戏测试和 MC Studio 编辑器。Python 包管理用于开发环境,不是游戏内的包安装器。
36
36
 
37
37
  ## 安装
38
38
 
@@ -65,7 +65,12 @@ mcpy edit
65
65
 
66
66
  ## 复用代码和资源
67
67
 
68
- 对于已发布的 Python 包,使用包名添加依赖:
68
+ 为 Addon 一键准备 QuMod:`mcpy add --qumod`。新脚本目录自动生成入口,已有业务代码保留;
69
+ QuMod 作为通用 Git 项目依赖的预设管理,不进入源码仓库。团队成员克隆后执行 `mcpy sync` 即可恢复框架。
70
+ CLI/GUI 向导、官方 GitHub/Gitee 来源和版本依据见 [QuMod 指引](docs/qumod.md)。
71
+ 其他仓库使用 `mcpy add --git <URL>`;标准导出、递归依赖与共享缓存见 [Git项目依赖](docs/git-dependencies.md)。
72
+
73
+ 将 Python 包安装到 mcpy 所在的工具环境并记录声明:
69
74
 
70
75
  ```powershell
71
76
  mcpy add "package-name>=1.0"
@@ -81,10 +86,16 @@ mcpy add --path "../shared-addon"
81
86
 
82
87
  移除依赖使用 `mcpy remove <包名>` 或 `mcpy remove --path <目录>`。移除本地引用不会删除源目录。不带参数运行 `mcpy add` / `mcpy remove` 可进入选择向导。
83
88
 
84
- 本地路径适合同机开发;与他人共享项目时,需要同步这些目录,或将可复用组件发布为 Python 包。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
89
+ 本地路径适合同机开发;共享项目时需同步这些目录。工具目前通过包的本地安装来源识别 Addon,普通 PyPI 包或 wheel 安装成功不代表能参与组装;未识别到 Addon 的包会标记为“仅开发环境”。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
90
+
91
+ **游戏运行边界:** 测试和正式游戏都使用内置 Python,不读取 `dependencies`,也不继承工具环境的 `site-packages`。`add`、`sync --install` 和 ModSDK 安装均不向游戏安装模块。游戏所需代码与资源必须随 Addon 携带,或由用户加载相应 Mod;纯 Python 库也不会自动复制,仍需适配游戏解释器和 ModSDK。原生扩展及依赖外部安装步骤、系统环境的库不能直接用于游戏;包内 `.pyd/.dll/.so/.so.*(版本号)/.dylib` 会阻止构建和启动。
85
92
 
86
93
  ## 构建与日常开发
87
94
 
95
+ Git 项目使用 `[[tool.mcpywrap.git_dependencies]]` 声明,支持 Addon 与纯代码导出、递归子依赖及固定提交。
96
+ `mcpy sync` 获取并注册,运行与发布时组装;共享源码缓存不提交 Git。
97
+ 现有 `code_libraries` 声明保持兼容,说明见[代码库依赖](docs/code-libraries.md)。
98
+
88
99
  | 命令 | 用途 |
89
100
  |---|---|
90
101
  | `mcpy build` | 将项目和依赖构建到配置的输出目录 |
@@ -100,12 +111,14 @@ mcpy add --path "../shared-addon"
100
111
 
101
112
  ### 打包分发
102
113
 
103
- 在项目根目录执行 `mcpy package`,会先构建项目及 Python 包/本地 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
114
+ 在项目根目录执行 `mcpy package`,会组装项目及已识别的 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`;仅开发环境的包不会进入 ZIP。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
104
115
 
105
116
  Addon ZIP 内为 `<项目名>_bp/`、`<项目名>_rp/`(仅包含实际构建出的包);地图 ZIP 根目录直接包含存档数据、行为包、资源包及世界包配置。地图默认保留独立包,使用 `mcpy package --merge`(或 `-m`)按构建规则合并依赖资源。构建产物中的空目录会保留。
106
117
 
107
118
  重复打包成功后会替换同名 ZIP;失败时保留已有 ZIP,临时文件自动清理。
108
119
 
120
+ `publish` 向 PyPI 发布 Python 分发包,与游戏 ZIP 分发不同。本地测试可配置多个独立 Addon,游戏不会解析 Python 依赖图;构建成功不保证兼容,发布前应检查产物并在游戏中验证加载日志。
121
+
109
122
  ## 游戏启动与排查
110
123
 
111
124
  通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
@@ -173,7 +186,7 @@ mcpy --project D:\tests\server stop --session <id> --json
173
186
  也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
174
187
  无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
175
188
  两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
176
- 局域网流程请使用 mcpywrap 0.3.6 或更高版本,可从 `v0.3.6` 标签安装配套 Skill。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
189
+ 推荐使用 mcpywrap 0.3.8 或更高版本,并从 `v0.3.8` 标签安装配套 Skill,以获得 Git 依赖、QuMod 快捷添加及依赖边界校验。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
177
190
 
178
191
  安装后可直接描述任务:
179
192
 
@@ -5,7 +5,7 @@
5
5
  [![PyPI Version](https://img.shields.io/pypi/v/mcpywrap)](https://pypi.org/project/mcpywrap/)
6
6
  [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
7
 
8
- mcpywrap 使用 `pyproject.toml` 管理项目,支持安装 Python 包依赖,也支持直接引用本地 Addon 目录。它将依赖管理、资源构建、游戏运行和 MC Studio 编辑器串成一套开发流程,方便多个项目共享代码与资源。
8
+ mcpywrap 使用 `pyproject.toml` 管理开发侧依赖,复用本地 Addon 的代码与资源,并串联构建、游戏测试和 MC Studio 编辑器。Python 包管理用于开发环境,不是游戏内的包安装器。
9
9
 
10
10
  ## 安装
11
11
 
@@ -38,7 +38,12 @@ mcpy edit
38
38
 
39
39
  ## 复用代码和资源
40
40
 
41
- 对于已发布的 Python 包,使用包名添加依赖:
41
+ 为 Addon 一键准备 QuMod:`mcpy add --qumod`。新脚本目录自动生成入口,已有业务代码保留;
42
+ QuMod 作为通用 Git 项目依赖的预设管理,不进入源码仓库。团队成员克隆后执行 `mcpy sync` 即可恢复框架。
43
+ CLI/GUI 向导、官方 GitHub/Gitee 来源和版本依据见 [QuMod 指引](docs/qumod.md)。
44
+ 其他仓库使用 `mcpy add --git <URL>`;标准导出、递归依赖与共享缓存见 [Git项目依赖](docs/git-dependencies.md)。
45
+
46
+ 将 Python 包安装到 mcpy 所在的工具环境并记录声明:
42
47
 
43
48
  ```powershell
44
49
  mcpy add "package-name>=1.0"
@@ -54,10 +59,16 @@ mcpy add --path "../shared-addon"
54
59
 
55
60
  移除依赖使用 `mcpy remove <包名>` 或 `mcpy remove --path <目录>`。移除本地引用不会删除源目录。不带参数运行 `mcpy add` / `mcpy remove` 可进入选择向导。
56
61
 
57
- 本地路径适合同机开发;与他人共享项目时,需要同步这些目录,或将可复用组件发布为 Python 包。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
62
+ 本地路径适合同机开发;共享项目时需同步这些目录。工具目前通过包的本地安装来源识别 Addon,普通 PyPI 包或 wheel 安装成功不代表能参与组装;未识别到 Addon 的包会标记为“仅开发环境”。目录结构、配置及构建规则见[本地依赖参考](https://github.com/EaseCation/mcpywrap/blob/main/docs/local-dependencies.md)。
63
+
64
+ **游戏运行边界:** 测试和正式游戏都使用内置 Python,不读取 `dependencies`,也不继承工具环境的 `site-packages`。`add`、`sync --install` 和 ModSDK 安装均不向游戏安装模块。游戏所需代码与资源必须随 Addon 携带,或由用户加载相应 Mod;纯 Python 库也不会自动复制,仍需适配游戏解释器和 ModSDK。原生扩展及依赖外部安装步骤、系统环境的库不能直接用于游戏;包内 `.pyd/.dll/.so/.so.*(版本号)/.dylib` 会阻止构建和启动。
58
65
 
59
66
  ## 构建与日常开发
60
67
 
68
+ Git 项目使用 `[[tool.mcpywrap.git_dependencies]]` 声明,支持 Addon 与纯代码导出、递归子依赖及固定提交。
69
+ `mcpy sync` 获取并注册,运行与发布时组装;共享源码缓存不提交 Git。
70
+ 现有 `code_libraries` 声明保持兼容,说明见[代码库依赖](docs/code-libraries.md)。
71
+
61
72
  | 命令 | 用途 |
62
73
  |---|---|
63
74
  | `mcpy build` | 将项目和依赖构建到配置的输出目录 |
@@ -73,12 +84,14 @@ mcpy add --path "../shared-addon"
73
84
 
74
85
  ### 打包分发
75
86
 
76
- 在项目根目录执行 `mcpy package`,会先构建项目及 Python 包/本地 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
87
+ 在项目根目录执行 `mcpy package`,会组装项目及已识别的 Addon 依赖,再生成 `dist/<项目名>-<版本>.zip`;仅开发环境的包不会进入 ZIP。名称和版本读取 `pyproject.toml` 的 `[project]`;不需要配置 `target_dir`。
77
88
 
78
89
  Addon ZIP 内为 `<项目名>_bp/`、`<项目名>_rp/`(仅包含实际构建出的包);地图 ZIP 根目录直接包含存档数据、行为包、资源包及世界包配置。地图默认保留独立包,使用 `mcpy package --merge`(或 `-m`)按构建规则合并依赖资源。构建产物中的空目录会保留。
79
90
 
80
91
  重复打包成功后会替换同名 ZIP;失败时保留已有 ZIP,临时文件自动清理。
81
92
 
93
+ `publish` 向 PyPI 发布 Python 分发包,与游戏 ZIP 分发不同。本地测试可配置多个独立 Addon,游戏不会解析 Python 依赖图;构建成功不保证兼容,发布前应检查产物并在游戏中验证加载日志。
94
+
82
95
  ## 游戏启动与排查
83
96
 
84
97
  通常不需要手动指定游戏路径。mcpywrap 会优先查找 MC Studio 登记的安装,必要时搜索固定磁盘中的标准下载目录,并跳过不完整的引擎版本。
@@ -146,7 +159,7 @@ mcpy --project D:\tests\server stop --session <id> --json
146
159
  也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
147
160
  无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
148
161
  两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
149
- 局域网流程请使用 mcpywrap 0.3.6 或更高版本,可从 `v0.3.6` 标签安装配套 Skill。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
162
+ 推荐使用 mcpywrap 0.3.8 或更高版本,并从 `v0.3.8` 标签安装配套 Skill,以获得 Git 依赖、QuMod 快捷添加及依赖边界校验。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
150
163
 
151
164
  安装后可直接描述任务:
152
165
 
@@ -0,0 +1,40 @@
1
+ # 固定版本游戏代码库
2
+
3
+ 本文描述兼容的叶子代码库声明;新项目推荐 [通用Git项目依赖](git-dependencies.md),支持读取项目导出和递归子依赖。
4
+
5
+ QuMod 用户可直接运行 `mcpy add --qumod`,无需手写下方配置;图形界面Git依赖中提供“一键添加 QuMod”快捷按钮。
6
+ 克隆项目后执行 `mcpy sync` 恢复。详见 [QuMod 指引与来源调研](qumod.md)。
7
+
8
+ 代码库由构建工具装入游戏包,独立于 `[project].dependencies`(工具 Python 环境)和 `local_dependencies`(完整 Addon)。不要求上游包含 manifest,不运行上游安装脚本,不自动初始化入口。
9
+
10
+ ```toml
11
+ [[tool.mcpywrap.code_libraries]]
12
+ name = "qumod"
13
+ git = "https://github.com/GitHub-Zero123/QuModLibs.git"
14
+ rev = "a07430aaaa6dce5f1ef68de3fda52ea4b9366c2c"
15
+ subdir = "Scripts/QuModLibs"
16
+ target = "OreDetector/QuModLibs"
17
+ ```
18
+
19
+ `target` 相对行为包;声明代码库的 Addon 须有行为包及 manifest。路径用 `/`,不得越界。每个条目必须包含上述五个字段。支持 HTTPS 或 `file:///...` 本地 Git 仓库;`rev` 必须是完整40位小写提交,不接受浮动分支。目标不能与手写目录、同名 Python 模块或其他库重叠。
20
+
21
+ 运行 `mcpy sync` 显式获取库,共享用户级不可变Git源码,再复制到项目 `.mcpy/libraries/`,写出 `mcpy-code-libraries.lock.json`(声明与内容 SHA-256)。将 `.mcpy/`、`.runtime/`、`build/`、`dist/` 加入 Git 忽略,提交配置与锁文件,不提交上游源码。根目录 LICENSE/COPYING/NOTICE 随库保存。`sync --install` 还会进行原有的工具环境安装;安装游戏库不需要这个参数。
22
+
23
+ `build`、`package` 和 `run` 只消费已同步且通过摘要检查的库;没有缓存时先 `sync`。更新 `rev` 后重新同步,按完整目录替换产物,旧文件不会遗留。回退改回旧提交并同步。不删除旧缓存;损坏缓存报错,不静默覆盖。锁文件与预期内容必须一致,校验和并不等于上游作者身份认证。
24
+
25
+ 带代码库的 Addon 在 `run` 前组装到 `.mcpy/runtime/assembled`;配置、引擎选择、世界和会话仍属于原项目。发布 ZIP 使用同一组装逻辑。游戏内修改原源码不会自动刷新运行产物,重新 `run` 会重建;持续开发可用 `mcpy dev` 更新配置的构建目录。`dev` 遇到代码库项目采用完整原子组装,修改依赖声明后应同步并重启监控。不要把“重建文件”理解为正在运行的游戏会自动重载 Python。
26
+
27
+ QuMod 示例入口:
28
+
29
+ ```python
30
+ # -*- coding: utf-8 -*-
31
+ # QMain 必须保留在 modMain 的命名空间,供网易加载器发现绑定类。
32
+ from .QuModLibs.QuMod import EasyMod, QMain
33
+ MOD = EasyMod()
34
+ MOD.Server('Server')
35
+ MOD.Client('Client')
36
+ ```
37
+
38
+ 同一 Mod 的功能模块共享它的 QuMod;独立 Mod 各自安装到独立脚本根。不能仅按库版本全局合并实例,QuMod 的系统名和通信通道依赖脚本根命名空间。原版 API 与框架生命周期仍需在目标游戏中验证。
39
+
40
+ 范围:固定源码获取和组装;不解析库内部传递依赖、不求解版本范围、不自动裁剪 QuMod、不提供中央仓库或游戏内下载。地图自身不声明代码库,可引用包含代码库的 Addon。离线构建需要已经同步的缓存;干净机器运行 sync 恢复相同提交与摘要。
@@ -0,0 +1,80 @@
1
+ # Git 项目依赖与共享缓存
2
+
3
+ 远程项目是通用依赖来源,QuMod 等框架只是预设。核心不识别框架名称:获取层负责Git源码快照,解析层负责导出描述和依赖图,注册层生成项目独立的组装节点,已有构建器消费这些节点。
4
+
5
+ ## 添加和恢复
6
+
7
+ ```powershell
8
+ # 自动识别已有Addon,或读取仓库的导出描述
9
+ mcpy add --git https://example.com/team/shared-addon.git --ref v1.2.0
10
+
11
+ # 没有项目描述的普通源码目录:显式补充布局
12
+ mcpy add --git https://example.com/team/shared-code.git --kind code --subdir src --target MyMod/SharedLib
13
+
14
+ # 固定某个提交、别名、monorepo项目目录
15
+ mcpy add --git https://example.com/team/modules.git --ref <完整SHA> --dep-name combat --subdir projects/combat
16
+
17
+ # 克隆已配置项目后恢复依赖;有完整共享快照时不需要重新下载
18
+ mcpy sync
19
+ mcpy remove --git combat
20
+ ```
21
+
22
+ 支持HTTPS与file://本地Git仓库;访问权限交给Git凭据机制。首次省略ref会读取当时HEAD并保存完整SHA,重复添加同名来源默认保留版本。显式ref用于更新或回退,不自动选择最新版本,不提供版本范围求解。
23
+
24
+ 图形界面 `mcpy ui` 的“Git依赖”表单使用同一服务。“一键添加 QuMod”等快捷按钮只填入标准Git来源与布局,并可为新脚本目录生成入口;添加后仍是普通Git依赖,不单独分类为框架。
25
+
26
+ ## 消费者与发布者的标准描述
27
+
28
+ 消费者的pyproject.toml:
29
+
30
+ ```toml
31
+ [[tool.mcpywrap.git_dependencies]]
32
+ name = "shared"
33
+ git = "https://example.com/team/shared.git"
34
+ rev = "0123456789abcdef0123456789abcdef01234567"
35
+ subdir = "." # 可选:项目描述所在目录
36
+ kind = "auto" # 可选:auto / addon / code
37
+ # target = "MyMod/SharedLib" # code安装位置,可覆盖上游export.target
38
+ ```
39
+
40
+ 远端项目可在自己的pyproject.toml声明导出:
41
+
42
+ ```toml
43
+ [tool.mcpywrap.export]
44
+ kind = "code"
45
+ path = "src"
46
+ target = "MyMod/SharedLib"
47
+ ```
48
+
49
+ `export.path`相对项目描述所在目录;`target`相对最终行为包。已有有效Addon可省略export,由manifest及现有Addon目录规则识别;Addon不需要target。没有描述且无法识别时明确报错,不猜测任意Git项目的安装结构,不运行setup.py、构建脚本或EXE。
50
+
51
+ 子项目使用相同git_dependencies声明,提交须固定。仓库内部的local_dependencies相对声明项目解析,必须位于同一Git快照内,支持monorepo;外部仓库必须显式用Git依赖。工具递归解析、检测循环、去重共同节点,并拒绝代码安装位置与手写内容或其他依赖重叠。
52
+
53
+ Python的project.dependencies继续表示工具Python依赖,不变成游戏代码安装指令。远端项目中的旧code_libraries仍可使用;未声明的import、Git子模块和任意构建系统不会被自动推断为依赖。
54
+
55
+ ## 混合存储模型
56
+
57
+ | 层 | 位置 | 内容 |
58
+ |---|---|---|
59
+ | 共享源码 | Windows `%LOCALAPPDATA%/mcpywrap`;macOS `~/Library/Caches/mcpywrap`;Linux `$XDG_CACHE_HOME/mcpywrap`或`~/.cache/mcpywrap` | 按来源和完整提交保存不可变快照及摘要 |
60
+ | 项目锁 | `mcpy-git.lock.json` | 直接声明、解析节点、子依赖关系、内容摘要;不记录本机缓存绝对路径 |
61
+ | 项目注册 | `.mcpy/git-projects/` | 从源码复制并标准化后的Addon节点、布局与项目内相对依赖关系 |
62
+ | 游戏产物 | `.mcpy/runtime/`、配置的build目录及dist | 独立组装结果;不指向共享源码 |
63
+
64
+ `MCPY_CACHE_DIR`覆盖共享缓存根目录,CI可以指向临时目录,需要项目内缓存时可指向项目自己的`.mcpy/cache`。这是本机获取策略,不改变依赖声明、锁文件或安装作用域。
65
+
66
+ 同一提交只需下载一次,但每个项目独立选择版本,每个Mod独立选择框架安装位置。产物使用复制,修改产物不会反向写共享缓存。Git源码通过临时目录校验后原子发布,同来源并发下载使用跨进程锁,进程退出后由操作系统释放锁。Windows内部缓存支持长路径,不要求更改系统配置。
67
+
68
+ 项目注册节点另含注册格式版本;工具调整注册布局时,显式sync重建派生节点,源码提交仍保持锁定。各Git依赖的许可/NOTICE分别保存在包内 `mcpy_licenses/<节点ID>/`,避免多依赖组装时互相覆盖。
69
+
70
+ 只有add/sync获取远程内容,build/package/run不隐式联网。共享缓存校验不通过时报错,不覆盖损坏内容,也不删除锁文件来“修复”。本版不自动垃圾回收缓存;移除声明保留源码缓存、手写入口和已有产物,下一次组装更新产物。
71
+
72
+ 提交配置、锁文件、业务源码及.gitignore,忽略.mcpy、.runtime、build和dist。旧code_libraries与mcpy-code-libraries.lock.json兼容,获取层同样复用用户级源码缓存,不要求已有项目迁移。
73
+
74
+ ## 验证范围
75
+
76
+ 自动化测试使用与任何框架无关的本地Git仓库,覆盖Addon自动识别、代码导出、Git子依赖与仓库内引用、循环/越界、项目隔离、离线恢复、并发下载、更新/回退/移除、内容篡改和深层Windows路径。官方QuMod GitHub与Gitee固定提交的完整源码摘要一致,均通过创建框架、打包和干净克隆sync。
77
+
78
+ 这保证依赖获取与组装契约,不自动保证任意第三方代码兼容游戏。Git子模块、跨仓库可变本地路径、版本范围和上游构建钩子不在本版支持范围。
79
+
80
+ 2026-09-27 验证记录:全量246项通过;随后Git依赖UI统一命名、快捷取消和自动导出返回值的针对性回归通过。并发首次获取重复5轮通过,Skill校验及真实CLI能力检测通过。Windows原生Qt窗口已检查,离屏测试不用于证明文字渲染。官方两来源固定提交完整源码摘要均为 `480e7e0bd9b99fc6b9e0f61a96fb68ea8c64752e25d19d8ab72edfbb885d4ebf`。
@@ -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`。
@@ -0,0 +1,93 @@
1
+ # 一条命令准备 QuMod
2
+
3
+ 在已初始化的 Addon 项目中:
4
+
5
+ ```powershell
6
+ mcpy add --qumod
7
+ ```
8
+
9
+ 只有一个 `modMain.py` 时自动选择其脚本目录;没有入口时创建 `MyScript`;有多个 Mod 时要求明确选择:
10
+
11
+ ```powershell
12
+ mcpy add --qumod --script-dir MyMod
13
+ ```
14
+
15
+ 命令会声明外部代码库、获取固定提交、保存内容锁文件,并为**新脚本目录**生成 QuMod 的客户端、服务端及入口。已有目录的业务代码不改写;可根据下方示例手动接入,原版加载器与 QuMod 混用也不必覆盖原入口。
16
+
17
+ 新 Mod 也可沿用模板命令:
18
+
19
+ ```powershell
20
+ mcpy mod --framework qumod --name MyMod --script-dir MyMod
21
+ ```
22
+
23
+ 这两个入口共用同一个安装服务;下载或写入失败不会留下半份依赖声明。已声明的 QuMod 默认保留既有来源和提交,重复添加不会静默升级。
24
+
25
+ ## 团队恢复与日常使用
26
+
27
+ 提交 `pyproject.toml`、`mcpy-git.lock.json`、业务源码和 `.gitignore`。QuMod 的原始源码保存在用户级共享缓存,项目独立注册内容位于被忽略的 `.mcpy/git-projects/`;不会复制到业务源码目录。
28
+
29
+ 其他开发者安装支持此功能的 mcpywrap 后,克隆项目、进入目录,只需:
30
+
31
+ ```powershell
32
+ mcpy sync
33
+ ```
34
+
35
+ 随后 `mcpy run` 或 `mcpy package` 使用同一套组装逻辑,把框架安装到 `<脚本目录>/QuModLibs`。引擎、MC Studio 与工具环境依赖仍按原有方式准备;`sync` 不下载游戏,也不把工具 Python 的 site-packages 注入游戏。
36
+
37
+ 无法访问 GitHub 时,可在首次添加时显式选择官方 Gitee:
38
+
39
+ ```powershell
40
+ mcpy add --qumod --script-dir MyMod --qumod-source gitee
41
+ ```
42
+
43
+ 已有项目也可用该命令切换来源,保留其固定提交;新来源必须能提供该提交。来源变化会更新声明和锁文件,不自动回退到未经选择的第三方镜像。两种源的缓存身份独立,内容摘要可核对。
44
+
45
+ 上述管理流程针对标准git_dependencies。既有code_libraries声明仍可同步恢复;快捷添加识别到它时保留原格式,不创建重复依赖。旧格式切换来源需显式编辑原声明并sync,或明确迁移到Git项目声明,不隐式重写。
46
+
47
+ 移除代码库声明使用依赖列表中显示的名称:
48
+
49
+ ```powershell
50
+ mcpy remove --git qumod-mymod
51
+ ```
52
+
53
+ 缓存与手写入口保留,需同步检查业务中的 QuMod 导入。不要把移除依赖理解为自动重写或删除业务代码。
54
+
55
+ ## 图形和交互向导
56
+
57
+ - `mcpy add` / 交互式 `mcpy init` 中选择“常用Git依赖快捷添加”,再选择 qumod。
58
+ - `mcpy ui` → 添加新依赖 → Git 依赖 → 一键添加 QuMod,点击快捷按钮即可添加;下拉菜单可选来源或指定所属脚本目录。网络操作在后台进行。
59
+ - 依赖列表显示“Git依赖”、同步状态、来源、提交和安装位置,可移除选中声明。
60
+ - 克隆后的项目可点击“同步项目依赖”;等价于 `mcpy sync`,不会偷偷升级框架。
61
+
62
+ 新入口会保留网易加载器需要的 `QMain`:
63
+
64
+ ```python
65
+ # -*- coding: utf-8 -*-
66
+ from .QuModLibs.QuMod import EasyMod, QMain
67
+ MOD = EasyMod()
68
+ MOD.Server("Server")
69
+ MOD.Client("Client")
70
+ ```
71
+
72
+ 一个 Mod 内的功能模块共用其框架,多个独立 Mod 安装到各自的脚本目录。已有手工复制的 QuMod 会明确报冲突,向导不会擅自删除或覆盖它。
73
+
74
+ ## 源码与发布调研(2026-09-27)
75
+
76
+ 官方文档 https://qumod.cc/QuModLibs/modMain.html 同时链接:
77
+
78
+ | 来源 | 仓库 | 本次核对 |
79
+ |---|---|---|
80
+ | GitHub | https://github.com/GitHub-Zero123/QuModLibs | 维护者源码仓库,BSD-3-Clause,未归档 |
81
+ | Gitee | https://gitee.com/bili_zero123/qu_mod_libs | 官方文档指向的另一源码入口;HEAD和全部标签与GitHub一致 |
82
+
83
+ 两边 HEAD 都是 `a07430aaaa6dce5f1ef68de3fda52ea4b9366c2c`(2026-07-08);源码 `Information.py` 标注 `Version = "1.4.3"`、`ApiVersion = 4`。这也是矿物探测鞋实机验收采用的提交。
84
+
85
+ GitHub Releases 数量为0。现有标签为 v1.3.6、v1.3.9、v1.4.0、v1.4.0-MINI,最新源码没有对应的 v1.4.3 标签。因此界面的“1.4.3”表示此已验证源码版本,**不是声称存在官方1.4.3 Release**。默认锁完整SHA,不能将 master 或“最新”视为稳定版本契约。
86
+
87
+ 仓库提供 Scripts/QuModLibs、Optional、Tools、Tests;没有供本工具直接消费的官方包索引或传递依赖描述。框架导入/模块裁剪工具不等于发布依赖仓库。本次以原始源码仓库作为来源,用通用 git_dependencies、导出描述、依赖图与内容锁补上获取、安装与恢复契约;旧code_libraries保持兼容;不要求上游为了我们的工具改造项目。
88
+
89
+ 默认安装标准 `Scripts/QuModLibs`,包含其内置模块;不自动合并 Optional、不根据业务 import 自动裁剪、不运行上游 EXE。可选模块的独立依赖契约留给后续明确设计,不能根据目录名猜测。
90
+
91
+ 稳定性保障来自“官方来源+明确提交+内容锁+本地缓存+验收记录”,不是承诺任一托管平台永远在线。后续升级应先做兼容验证,再更新工具中的推荐提交;已有项目不会随工具升级自动改变版本。
92
+
93
+ 内部实现边界:QuMod只存在于 framework_presets 的数据(来源、提交、目录、模板)和CLI快捷别名中。标准缓存、Git项目解析、依赖管理与组装代码不识别QuMod。任意其他仓库可用 `mcpy add --git` 接入同一流程,详见 [Git依赖结构](git-dependencies.md)。
@@ -0,0 +1,4 @@
1
+ # -*- coding: utf-8 -*-
2
+ """mcpywrap - 我的世界中国版 Addon 开发依赖、构建与游戏测试工具。"""
3
+
4
+ __version__ = '0.3.8'
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from ..dependencies import DependencyError, canonical_path
7
7
  from .AddonsPack import MANIFEST_FILES
8
8
  from .file_merge import try_merge_file
9
+ from ..code_libraries import library_source
9
10
 
10
11
  MERGED_JSON = {'blocks.json', 'terrain_texture.json', 'item_texture.json', 'sounds.json',
11
12
  'sound_definitions.json', 'animations.json', 'animation_controllers.json',
@@ -34,6 +35,11 @@ def validate_target(target, root, packs):
34
35
 
35
36
 
36
37
  def pack_files(pack, kind):
38
+ if kind == 'behavior':
39
+ for library in getattr(pack, 'code_libraries', []):
40
+ for file in sorted(library['source'].rglob('*')):
41
+ if file.is_file():
42
+ yield library['target'] + '/' + file.relative_to(library['source']).as_posix()
37
43
  folder = getattr(pack, kind + '_pack_dir')
38
44
  if not folder or not os.path.isdir(folder):
39
45
  return
@@ -55,8 +61,9 @@ def rebuild_file(packs, kind, relative, destination):
55
61
  folder = getattr(pack, kind + '_pack_dir')
56
62
  if not folder:
57
63
  continue
58
- source = Path(folder) / relative
59
- if source.is_file() and (source.name in MANIFEST_FILES or not pack.should_exclude(str(source))):
64
+ mounted = library_source(pack, kind, relative)
65
+ source = mounted or Path(folder) / relative
66
+ if source.is_file() and (mounted or source.name in MANIFEST_FILES or not pack.should_exclude(str(source))):
60
67
  sources.append((pack, source))
61
68
  if relative in MANIFEST_FILES:
62
69
  # 优先保留主包 manifest;主包缺少该包类型时使用最后一个依赖的 manifest。
@@ -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, declarations, read_project, resolve_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,7 +43,11 @@ 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 = {}, [], []
49
+ self._git_locks = {}
50
+ self.has_git_projects = False
46
51
 
47
52
  def find_dependency_path(self, package_name):
48
53
  try:
@@ -52,14 +57,20 @@ class DependencyManager:
52
57
  direct = dist.read_text('direct_url.json')
53
58
  return _local_url(json.loads(direct).get('url', '')) if direct else None
54
59
 
55
- def build_dependency_tree(self, project_name, project_path, dependencies=None, *, config=None, allow_missing=False):
60
+ def build_dependency_tree(self, project_name, project_path, dependencies=None, *, config=None, allow_missing=False, git_lock=None):
56
61
  self.__init__()
57
62
  project_path = str(Path(project_path).resolve())
63
+ if git_lock is not None:
64
+ self._git_locks[project_path] = git_lock
58
65
  config = read_project(project_path) if config is None else config
59
66
  if dependencies is not None:
60
67
  config.setdefault('project', {})['dependencies'] = dependencies
61
68
  root_id = re.sub(r'[^\w.-]', '_', project_name)[:60] + '_' + hashlib.sha256(canonical_path(project_path).encode('utf-8')).hexdigest()[:12]
62
69
  root = DependencyNode(project_name, AddonsPack(root_id, project_path, is_origin=True))
70
+ folders = [root.addon_pack.behavior_pack_dir, root.addon_pack.resource_pack_dir]
71
+ if config.get('tool', {}).get('mcpywrap', {}).get('project_type') == 'map':
72
+ folders = [Path(project_path) / name for name in ('behavior_packs', 'resource_packs')]
73
+ validate_game_files(project_path, folders)
63
74
  self.root_node = root
64
75
  key = canonical_path(project_path)
65
76
  self._nodes[key] = root
@@ -67,8 +78,8 @@ class DependencyManager:
67
78
  self._process(root, config)
68
79
  self._active.pop()
69
80
  self.dependency_map = {canonical_path(n.addon_pack.path): n.addon_pack for n in self._ordered}
70
- if self.warnings and not allow_missing:
71
- raise DependencyError('\n'.join(self.warnings))
81
+ if self.errors and not allow_missing:
82
+ raise DependencyError('\n'.join(self.errors))
72
83
  return root
73
84
 
74
85
  def _process(self, parent, config):
@@ -78,12 +89,14 @@ class DependencyManager:
78
89
  if entry.kind == 'local':
79
90
  path = str(resolve_path(source, entry.value))
80
91
  self._visit(parent, path, Path(path).name)
92
+ self._status(source, entry, 'addon')
81
93
  continue
82
94
  try:
83
95
  requirement = Requirement(entry.value)
84
96
  except InvalidRequirement as exc:
85
97
  raise DependencyError(f'无效的 Python 包声明: {entry.value}') from exc
86
98
  if requirement.marker and not requirement.marker.evaluate():
99
+ self._status(source, entry, 'inactive', '当前工具 Python 环境不满足环境标记,未启用;不代表游戏兼容性。')
87
100
  continue
88
101
  try:
89
102
  dist = metadata.distribution(requirement.name)
@@ -95,6 +108,7 @@ class DependencyManager:
95
108
  continue
96
109
  path = self.find_dependency_path(entry.value)
97
110
  if not path:
111
+ self._development_only(source, entry)
98
112
  continue
99
113
  direct = dist.read_text('direct_url.json')
100
114
  if not Path(path).is_dir() and direct and 'dir_info' in json.loads(direct):
@@ -107,11 +121,35 @@ class DependencyManager:
107
121
  for p in Path(path).iterdir())
108
122
  if has_mcpy or looks_like_addon:
109
123
  self._visit(parent, path, canonicalize_name(requirement.name))
124
+ self._status(source, entry, 'addon')
125
+ else:
126
+ self._development_only(source, entry)
110
127
  except (DependencyError, OSError, ValueError) as exc:
111
128
  raise DependencyError(f'来源 {source}\n依赖 {entry.value!r}: {exc}') from exc
129
+ from ..git_projects import resolve_projects
130
+ for entry, path, info in resolve_projects(parent.addon_pack.path, config,
131
+ self._git_locks.get(str(Path(parent.addon_pack.path).resolve()))):
132
+ self.has_git_projects = True
133
+ self._visit(parent, str(path), entry['name'])
112
134
 
113
135
  def _missing(self, source, value, reason):
114
- self.warnings.append(f'{source}: {value} {reason};请显式执行 mcpy add "{value}"')
136
+ from ..dependencies import DependencyDeclaration
137
+ message = f'{source}: {value} {reason};请显式执行 mcpy add "{value}"'
138
+ self.errors.append(message)
139
+ self.warnings.append(message)
140
+ self._status(source, DependencyDeclaration('package', value), 'unavailable', message)
141
+
142
+ def _status(self, source, entry, state, message=''):
143
+ self.statuses[(canonical_path(source), entry.kind, entry.value)] = DependencyStatus(state, message)
144
+
145
+ def status_for(self, source, entry):
146
+ return self.statuses[(canonical_path(source), entry.kind, entry.value)]
147
+
148
+ def _development_only(self, source, entry):
149
+ message = (f'来源 {source},依赖 {entry.value!r}: 未识别到可组装内容;'
150
+ '仅安装于工具环境,不会被打包或供游戏导入。')
151
+ self.warnings.append(message)
152
+ self._status(source, entry, 'development_only', message)
115
153
 
116
154
  def _visit(self, parent, path, name):
117
155
  key = canonical_path(path)
@@ -124,10 +162,12 @@ class DependencyManager:
124
162
  if config.get('tool', {}).get('mcpywrap', {}).get('project_type', 'addon') != 'addon':
125
163
  raise DependencyError(f'仅支持 Addon 目录依赖: {path}')
126
164
  folders = addon_directories(path)
165
+ validate_game_files(path, folders.values())
127
166
  suffix = hashlib.sha256(key.encode('utf-8')).hexdigest()[:12]
128
167
  safe_name = re.sub(r'[^\w.-]', '_', name)[:60] or 'addon'
129
168
  addon = AddonsPack(f'{safe_name}_{suffix}', path)
130
169
  addon.behavior_pack_dir, addon.resource_pack_dir = folders.get('behavior'), folders.get('resource')
170
+ addon.code_target = config.get('tool', {}).get('mcpywrap', {}).get('_registered_code_target')
131
171
  node = DependencyNode(name, addon, parent)
132
172
  self._nodes[key] = node
133
173
  parent.add_child(node)