mcpywrap 0.3.7__tar.gz → 0.3.9__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.7 → mcpywrap-0.3.9}/CHANGELOG.md +14 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/MANIFEST.in +6 -0
- {mcpywrap-0.3.7/mcpywrap.egg-info → mcpywrap-0.3.9}/PKG-INFO +13 -2
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/README.md +12 -1
- mcpywrap-0.3.9/docs/code-libraries.md +40 -0
- mcpywrap-0.3.9/docs/git-dependencies.md +80 -0
- mcpywrap-0.3.9/docs/qumod.md +93 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/docs/releasing.md +3 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/__init__.py +1 -1
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/assembly.py +9 -2
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/dependency_manager.py +11 -1
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/project_builder.py +43 -6
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/watcher.py +16 -0
- mcpywrap-0.3.9/mcpywrap/code_libraries.py +197 -0
- mcpywrap-0.3.9/mcpywrap/commands/add_cmd.py +66 -0
- mcpywrap-0.3.9/mcpywrap/commands/dependency_prompt.py +45 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/init_cmd.py +7 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/mod_cmd.py +13 -1
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/package_cmd.py +4 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/remove_cmd.py +16 -7
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/run_cmd.py +29 -0
- mcpywrap-0.3.9/mcpywrap/commands/sync_cmd.py +66 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/dependencies.py +48 -4
- mcpywrap-0.3.9/mcpywrap/framework_presets.py +22 -0
- mcpywrap-0.3.9/mcpywrap/frameworks.py +76 -0
- mcpywrap-0.3.9/mcpywrap/git_cache.py +129 -0
- mcpywrap-0.3.9/mcpywrap/git_projects.py +242 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/bridge_payload/Injector.exe +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/bridge_payload/Loader.dll +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/bridge_payload/McpyMcsAuth.dll +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/bridge_payload/manifest.json +3 -3
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/game.py +15 -8
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/symlinks.py +39 -1
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/window.py +98 -17
- mcpywrap-0.3.9/mcpywrap/mcstudio/window_capture/manifest.json +4 -0
- mcpywrap-0.3.9/mcpywrap/mcstudio/window_capture/mcpy-window-capture.exe +0 -0
- mcpywrap-0.3.9/mcpywrap/project_dependencies.py +164 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/remote/client.py +14 -2
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/remote/http_server.py +7 -0
- mcpywrap-0.3.9/mcpywrap/source_files.py +60 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/ui/project_ui.py +147 -12
- {mcpywrap-0.3.7 → mcpywrap-0.3.9/mcpywrap.egg-info}/PKG-INFO +13 -2
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap.egg-info/SOURCES.txt +22 -0
- mcpywrap-0.3.9/native/window_capture/window_capture.cpp +282 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/pyproject.toml +2 -2
- mcpywrap-0.3.9/scripts/build_all_native.py +62 -0
- mcpywrap-0.3.9/scripts/build_window_capture.py +47 -0
- mcpywrap-0.3.9/scripts/check_window_capture.py +20 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/SKILL.md +16 -4
- mcpywrap-0.3.9/skills/mcpywrap/references/project-dependencies.md +125 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/references/troubleshooting.md +1 -1
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/scripts/bootstrap.py +5 -0
- mcpywrap-0.3.9/tests/test_code_libraries.py +185 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_game_window_skill.py +57 -1
- mcpywrap-0.3.9/tests/test_git_projects.py +269 -0
- mcpywrap-0.3.9/tests/test_launch_encoding.py +15 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_local_dependencies.py +14 -0
- mcpywrap-0.3.9/tests/test_pack_uuid_conflicts.py +32 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_package.py +18 -0
- mcpywrap-0.3.9/tests/test_qumod_workflow.py +248 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_remote.py +18 -2
- mcpywrap-0.3.7/mcpywrap/commands/add_cmd.py +0 -36
- mcpywrap-0.3.7/mcpywrap/commands/dependency_prompt.py +0 -22
- mcpywrap-0.3.7/mcpywrap/commands/sync_cmd.py +0 -33
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/LICENSE +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/docs/development.md +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/docs/engine-discovery.md +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/docs/local-dependencies.md +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/docs/mcs-auth.md +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/__main__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/AddonsPack.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/MapPack.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/__init__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/builders/file_merge.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/cli.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/command_context.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/__init__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/build_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/connect_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/default_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/desktop_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/dev_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/doctor_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/edit_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/modsdk_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/publish_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/serve_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/session_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/commands/ui_cmd.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/config.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/custom_packaging.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/__init__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/auth_interaction.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/bridge_assets.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/bridge_payload/publisher.cer +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/diagnostics.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/discovery.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/editor.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/file_logs.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/log_protocol.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/mcs.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/mcs_auth.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/network.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/private_logs.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/processes.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/session_worker.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/sessions.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/studio_server.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/minecraft/__init__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/minecraft/addons.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/minecraft/level_dat.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/minecraft/map.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/minecraft/netease_modsdk.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/minecraft/template/mod_template.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/project_init.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/remote/__init__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/remote/service.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/ui/__init__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/utils/__init__.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/utils/pip_error_parser.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/utils/print_guide.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/utils/project_setup.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap/utils/utils.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap.egg-info/requires.txt +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/native/mcs_auth/Bridge.cs +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/native/mcs_auth/Injector.cs +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/native/mcs_auth/Loader.c +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/native/mcs_auth/publisher.cer +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/native/mcs_auth/publisher.json +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/scripts/build_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/scripts/check_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/setup.cfg +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/agents/openai.yaml +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/references/remote-testing.md +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/scripts/bootstrap.ps1 +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/scripts/game_window.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/skills/mcpywrap/scripts/smoke.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/manual_dependency_boundaries.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/manual_integration.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_auth_interaction.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_automation.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_dependency_boundaries.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_engine_discovery.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_local_auth_session.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_mcs_auth.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_mouse.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_network.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_network_sessions.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_remote_scripts.py +0 -0
- {mcpywrap-0.3.7 → mcpywrap-0.3.9}/tests/test_skill_workflows.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.3.9
|
|
4
|
+
|
|
5
|
+
- 截图默认使用 Windows Graphics Capture 捕获游戏客户区;被其他窗口遮挡时仍可读取画面,且不抢占前台。后台捕获不可用时自动回退到原有前台截图,并返回捕获方式和回退原因。
|
|
6
|
+
- 键盘和鼠标操作继续在执行前激活游戏窗口,操作后不恢复此前的前台窗口。新增 x64 捕获辅助程序及完整性校验;已在 Minecraft 3.10 实机验证 DirectX 画面、遮挡、DPI 尺寸和 F3 输入。
|
|
7
|
+
|
|
8
|
+
## 0.3.8
|
|
9
|
+
|
|
10
|
+
- 新增标准 Git 项目依赖:固定提交、导出结构识别、递归子依赖、循环/冲突检测、内容锁定,以及更新、回退和移除;克隆后通过 `mcpy sync` 恢复。
|
|
11
|
+
- 使用用户级不可变 Git 源码缓存与项目独立注册/组装,支持缓存路径覆盖、并发获取和 Windows 长路径;运行与发布不依赖工具 Python 的 site-packages。
|
|
12
|
+
- CLI 与 GUI 共用依赖服务;界面统一为“Git 依赖”,提供一键添加 QuMod 快捷操作。QuMod 仅为官方来源、已验证提交、布局及入口模板的数据预设。
|
|
13
|
+
- 保留既有 code_libraries 声明兼容;更新 Skill,明确 Python 包、本地 Addon、Git 依赖及新建/克隆/升级工作流。
|
|
14
|
+
- 修复 GBK 启动日志异常、Addon 构建失败破坏旧产物、重复 UUID 加载旧包、共享缓存锁初始化竞态;增量构建复核 Git 快照完整性。
|
|
15
|
+
- 兼容 Python 3.9 的 Windows 扩展路径解析,支持 Git 快照内相对引用与注册节点间的父目录引用;依赖许可证按节点独立保留。
|
|
16
|
+
|
|
3
17
|
## 0.3.7
|
|
4
18
|
|
|
5
19
|
- 明确工具 Python 与游戏内置 Python 的边界:普通包保留为开发依赖,CLI、JSON 与 GUI 提示“仅开发环境”,不会自动进入游戏产物。
|
|
@@ -2,12 +2,18 @@ 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
|
|
9
|
+
include scripts/build_all_native.py
|
|
10
|
+
include scripts/build_window_capture.py
|
|
11
|
+
include scripts/check_window_capture.py
|
|
8
12
|
include scripts/check_mcs_auth_bridge.py
|
|
9
13
|
recursive-include native/mcs_auth *.cs *.c *.cer *.json
|
|
10
14
|
recursive-include mcpywrap/mcstudio/bridge_payload *.exe *.dll *.cer *.json
|
|
15
|
+
recursive-include mcpywrap/mcstudio/window_capture *.exe *.json
|
|
16
|
+
recursive-include native/window_capture *.cpp
|
|
11
17
|
prune test
|
|
12
18
|
prune .git
|
|
13
19
|
global-exclude __pycache__ *.py[cod] *.cppconfig *.log
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.9
|
|
4
4
|
Summary: A wrapper for Minecraft China Edition Addons management
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -46,6 +46,8 @@ uv tool install mcpywrap
|
|
|
46
46
|
|
|
47
47
|
也可以使用 `pip install mcpywrap`。安装完成后,运行 `mcpy --help` 查看命令。
|
|
48
48
|
|
|
49
|
+
从 Windows 源码检出安装时,先安装 Visual Studio 的 x86/x64 C++ 构建工具,再运行 `python scripts/build_all_native.py` 一次编译全部原生组件,最后执行 `pip install -e .`。正式发布包由 CI 编译并附带原生组件,普通安装无需本机编译。
|
|
50
|
+
|
|
49
51
|
## 开始使用
|
|
50
52
|
|
|
51
53
|
进入你的 Addon 或地图项目目录,按向导初始化:
|
|
@@ -65,6 +67,11 @@ mcpy edit
|
|
|
65
67
|
|
|
66
68
|
## 复用代码和资源
|
|
67
69
|
|
|
70
|
+
为 Addon 一键准备 QuMod:`mcpy add --qumod`。新脚本目录自动生成入口,已有业务代码保留;
|
|
71
|
+
QuMod 作为通用 Git 项目依赖的预设管理,不进入源码仓库。团队成员克隆后执行 `mcpy sync` 即可恢复框架。
|
|
72
|
+
CLI/GUI 向导、官方 GitHub/Gitee 来源和版本依据见 [QuMod 指引](docs/qumod.md)。
|
|
73
|
+
其他仓库使用 `mcpy add --git <URL>`;标准导出、递归依赖与共享缓存见 [Git项目依赖](docs/git-dependencies.md)。
|
|
74
|
+
|
|
68
75
|
将 Python 包安装到 mcpy 所在的工具环境并记录声明:
|
|
69
76
|
|
|
70
77
|
```powershell
|
|
@@ -87,6 +94,10 @@ mcpy add --path "../shared-addon"
|
|
|
87
94
|
|
|
88
95
|
## 构建与日常开发
|
|
89
96
|
|
|
97
|
+
Git 项目使用 `[[tool.mcpywrap.git_dependencies]]` 声明,支持 Addon 与纯代码导出、递归子依赖及固定提交。
|
|
98
|
+
`mcpy sync` 获取并注册,运行与发布时组装;共享源码缓存不提交 Git。
|
|
99
|
+
现有 `code_libraries` 声明保持兼容,说明见[代码库依赖](docs/code-libraries.md)。
|
|
100
|
+
|
|
90
101
|
| 命令 | 用途 |
|
|
91
102
|
|---|---|
|
|
92
103
|
| `mcpy build` | 将项目和依赖构建到配置的输出目录 |
|
|
@@ -177,7 +188,7 @@ mcpy --project D:\tests\server stop --session <id> --json
|
|
|
177
188
|
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
178
189
|
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
179
190
|
两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
|
|
180
|
-
推荐使用 mcpywrap 0.3.
|
|
191
|
+
推荐使用 mcpywrap 0.3.8 或更高版本,并从 `v0.3.8` 标签安装配套 Skill,以获得 Git 依赖、QuMod 快捷添加及依赖边界校验。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
|
|
181
192
|
|
|
182
193
|
安装后可直接描述任务:
|
|
183
194
|
|
|
@@ -19,6 +19,8 @@ uv tool install mcpywrap
|
|
|
19
19
|
|
|
20
20
|
也可以使用 `pip install mcpywrap`。安装完成后,运行 `mcpy --help` 查看命令。
|
|
21
21
|
|
|
22
|
+
从 Windows 源码检出安装时,先安装 Visual Studio 的 x86/x64 C++ 构建工具,再运行 `python scripts/build_all_native.py` 一次编译全部原生组件,最后执行 `pip install -e .`。正式发布包由 CI 编译并附带原生组件,普通安装无需本机编译。
|
|
23
|
+
|
|
22
24
|
## 开始使用
|
|
23
25
|
|
|
24
26
|
进入你的 Addon 或地图项目目录,按向导初始化:
|
|
@@ -38,6 +40,11 @@ mcpy edit
|
|
|
38
40
|
|
|
39
41
|
## 复用代码和资源
|
|
40
42
|
|
|
43
|
+
为 Addon 一键准备 QuMod:`mcpy add --qumod`。新脚本目录自动生成入口,已有业务代码保留;
|
|
44
|
+
QuMod 作为通用 Git 项目依赖的预设管理,不进入源码仓库。团队成员克隆后执行 `mcpy sync` 即可恢复框架。
|
|
45
|
+
CLI/GUI 向导、官方 GitHub/Gitee 来源和版本依据见 [QuMod 指引](docs/qumod.md)。
|
|
46
|
+
其他仓库使用 `mcpy add --git <URL>`;标准导出、递归依赖与共享缓存见 [Git项目依赖](docs/git-dependencies.md)。
|
|
47
|
+
|
|
41
48
|
将 Python 包安装到 mcpy 所在的工具环境并记录声明:
|
|
42
49
|
|
|
43
50
|
```powershell
|
|
@@ -60,6 +67,10 @@ mcpy add --path "../shared-addon"
|
|
|
60
67
|
|
|
61
68
|
## 构建与日常开发
|
|
62
69
|
|
|
70
|
+
Git 项目使用 `[[tool.mcpywrap.git_dependencies]]` 声明,支持 Addon 与纯代码导出、递归子依赖及固定提交。
|
|
71
|
+
`mcpy sync` 获取并注册,运行与发布时组装;共享源码缓存不提交 Git。
|
|
72
|
+
现有 `code_libraries` 声明保持兼容,说明见[代码库依赖](docs/code-libraries.md)。
|
|
73
|
+
|
|
63
74
|
| 命令 | 用途 |
|
|
64
75
|
|---|---|
|
|
65
76
|
| `mcpy build` | 将项目和依赖构建到配置的输出目录 |
|
|
@@ -150,7 +161,7 @@ mcpy --project D:\tests\server stop --session <id> --json
|
|
|
150
161
|
也可以下载仓库 ZIP,将完整的 `skills/mcpywrap` 文件夹复制到对应 Agent 的技能目录。
|
|
151
162
|
无需从源码安装 Python 项目;CLI 和 Skill 分别安装,`pip/uv install` 不会自动注册 Skill。
|
|
152
163
|
两端使用跨平台 `scripts/bootstrap.py` 安装或复用各自的 CLI,Windows 也可用 `bootstrap.ps1`。脚本分别报告本机与远端能力;Skill、CLI 与 Windows 服务需要分别检查,Git/可编辑安装可能缺少登录组件。
|
|
153
|
-
推荐使用 mcpywrap 0.3.
|
|
164
|
+
推荐使用 mcpywrap 0.3.8 或更高版本,并从 `v0.3.8` 标签安装配套 Skill,以获得 Git 依赖、QuMod 快捷添加及依赖边界校验。实际能力仍以脚本检测为准;更新 Skill 不会自动更新两端 CLI。
|
|
154
165
|
|
|
155
166
|
安装后可直接描述任务:
|
|
156
167
|
|
|
@@ -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,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)。
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
主分支和 PR 运行 Windows Python 3.9、3.12、3.14 的自动化测试,以及 macOS 的无 Qt 安装、项目构建与远程客户端测试。通过后构建 wheel/sdist、运行严格元数据检查、检查产物内容,并在独立环境安装 wheel 验证 CLI 和模块导入。
|
|
4
4
|
|
|
5
5
|
Windows CI 先用 MSVC 从 `native/mcs_auth` 编译 x86 登录桥接,再签名并通过构建产物交给测试和 Python 打包任务;编译后的 EXE/DLL 不提交到 Git。仓库只保留发布者公钥证书和指纹。
|
|
6
|
+
同一 native job 还会从 `native/window_capture` 编译 x64 后台截图辅助程序,生成 SHA-256 清单;测试和打包阶段校验二进制、源码及 wheel/sdist 内容。辅助程序不可用时运行时回退到前台截图。
|
|
7
|
+
|
|
8
|
+
从源码安装时,在 Windows x64 且已安装 Visual Studio x86/x64 C++ 工具的环境中先运行 `python scripts/build_all_native.py`,再执行 `pip install -e .` 或 `pip install .`。脚本一次编译 x86 登录桥接和 x64 截图程序到包目录;可用 `--output-root <目录>` 将结果放入隔离目录。登录桥接源码编译产物没有发布签名,正式登录能力仍应使用带签名的发布包。
|
|
6
9
|
|
|
7
10
|
仓库 Actions secrets:`MCS_BRIDGE_SIGNING_PFX` 保存 Base64 编码的加密 PFX,`MCS_BRIDGE_SIGNING_PASSWORD` 保存独立的强密码。只有 main/发布标签的签名步骤可读取它们,PR 只构建未签名测试产物。CI 将证书导入临时 runner 的个人证书库,签名后清理,不安装根信任。私钥、PFX 和密码均不进入日志或 artifact。
|
|
8
11
|
|
|
@@ -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
|
-
|
|
59
|
-
|
|
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。
|
|
@@ -46,6 +46,8 @@ class DependencyManager:
|
|
|
46
46
|
self.errors = []
|
|
47
47
|
self.statuses = {}
|
|
48
48
|
self._nodes, self._active, self._ordered = {}, [], []
|
|
49
|
+
self._git_locks = {}
|
|
50
|
+
self.has_git_projects = False
|
|
49
51
|
|
|
50
52
|
def find_dependency_path(self, package_name):
|
|
51
53
|
try:
|
|
@@ -55,9 +57,11 @@ class DependencyManager:
|
|
|
55
57
|
direct = dist.read_text('direct_url.json')
|
|
56
58
|
return _local_url(json.loads(direct).get('url', '')) if direct else None
|
|
57
59
|
|
|
58
|
-
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):
|
|
59
61
|
self.__init__()
|
|
60
62
|
project_path = str(Path(project_path).resolve())
|
|
63
|
+
if git_lock is not None:
|
|
64
|
+
self._git_locks[project_path] = git_lock
|
|
61
65
|
config = read_project(project_path) if config is None else config
|
|
62
66
|
if dependencies is not None:
|
|
63
67
|
config.setdefault('project', {})['dependencies'] = dependencies
|
|
@@ -122,6 +126,11 @@ class DependencyManager:
|
|
|
122
126
|
self._development_only(source, entry)
|
|
123
127
|
except (DependencyError, OSError, ValueError) as exc:
|
|
124
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'])
|
|
125
134
|
|
|
126
135
|
def _missing(self, source, value, reason):
|
|
127
136
|
from ..dependencies import DependencyDeclaration
|
|
@@ -158,6 +167,7 @@ class DependencyManager:
|
|
|
158
167
|
safe_name = re.sub(r'[^\w.-]', '_', name)[:60] or 'addon'
|
|
159
168
|
addon = AddonsPack(f'{safe_name}_{suffix}', path)
|
|
160
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')
|
|
161
171
|
node = DependencyNode(name, addon, parent)
|
|
162
172
|
self._nodes[key] = node
|
|
163
173
|
parent.add_child(node)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""先校验依赖与输出位置,再组装项目。"""
|
|
2
2
|
import os
|
|
3
3
|
import shutil
|
|
4
|
+
import tempfile
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from ..command_context import report_dependency_warnings
|
|
6
7
|
from ..dependencies import DependencyError, DependencyService, addon_directories, read_project
|
|
7
8
|
from .AddonsPack import AddonsPack
|
|
8
9
|
from .MapPack import MapPack
|
|
9
10
|
from .assembly import assemble_addon, validate_target, is_within, pack_files, rebuild_file
|
|
11
|
+
from ..code_libraries import prepare_libraries
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
def _clear_directory(directory):
|
|
@@ -22,23 +24,33 @@ def _clear_directory(directory):
|
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class AddonProjectBuilder:
|
|
25
|
-
def __init__(self, source_dir, target_dir):
|
|
27
|
+
def __init__(self, source_dir, target_dir, include_dependencies=True):
|
|
26
28
|
self.source_dir = str(Path(source_dir).resolve())
|
|
27
29
|
self.target_dir = str(Path(target_dir).resolve()) if target_dir else None
|
|
28
30
|
self.config = read_project(self.source_dir)
|
|
29
31
|
self.project_name = self.config.get('project', {}).get('name', 'current_project')
|
|
32
|
+
self.include_dependencies = include_dependencies
|
|
30
33
|
|
|
31
34
|
def initialize(self):
|
|
32
35
|
addon_directories(self.source_dir)
|
|
33
|
-
|
|
36
|
+
config = None
|
|
37
|
+
if not self.include_dependencies:
|
|
38
|
+
import copy
|
|
39
|
+
config = copy.deepcopy(self.config)
|
|
40
|
+
config.setdefault('project', {})['dependencies'] = []
|
|
41
|
+
config.setdefault('tool', {}).setdefault('mcpywrap', {})['local_dependencies'] = []
|
|
42
|
+
config['tool']['mcpywrap']['git_dependencies'] = []
|
|
43
|
+
self.dependency_manager = DependencyService(self.source_dir).resolve(config)
|
|
34
44
|
report_dependency_warnings(self.dependency_manager)
|
|
35
45
|
self.dependency_tree = self.dependency_manager.root_node
|
|
36
46
|
self.origin_addon = self.dependency_tree.addon_pack
|
|
37
47
|
self.packs = list(self.dependency_manager.get_all_dependencies().values()) + [self.origin_addon]
|
|
48
|
+
prepare_libraries(self.packs)
|
|
38
49
|
validate_target(self.target_dir, self.source_dir, self.packs)
|
|
39
|
-
Path(self.target_dir).
|
|
40
|
-
|
|
41
|
-
self.target_addon = AddonsPack(self.project_name, self.
|
|
50
|
+
if Path(self.target_dir).is_symlink() or (hasattr(Path(self.target_dir), 'is_junction') and Path(self.target_dir).is_junction()):
|
|
51
|
+
raise DependencyError('构建目标不能是目录链接')
|
|
52
|
+
self.target_addon = AddonsPack(self.project_name, self.source_dir)
|
|
53
|
+
self.target_addon.path = self.target_dir
|
|
42
54
|
for kind in ('behavior', 'resource'):
|
|
43
55
|
source = getattr(self.origin_addon, kind + '_pack_dir')
|
|
44
56
|
setattr(self.target_addon, kind + '_pack_dir', str(Path(self.target_dir) / Path(source).name))
|
|
@@ -47,7 +59,24 @@ class AddonProjectBuilder:
|
|
|
47
59
|
def build(self):
|
|
48
60
|
try:
|
|
49
61
|
self.initialize()
|
|
50
|
-
|
|
62
|
+
target = Path(self.target_dir)
|
|
63
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
with tempfile.TemporaryDirectory(prefix='.mcpy-build-', dir=target.parent) as temporary:
|
|
65
|
+
stage = Path(temporary) / 'output'
|
|
66
|
+
stage.mkdir()
|
|
67
|
+
staged_addon = AddonsPack(self.project_name, stage)
|
|
68
|
+
for kind in ('behavior', 'resource'):
|
|
69
|
+
setattr(staged_addon, kind + '_pack_dir', str(stage / Path(getattr(self.target_addon, kind + '_pack_dir')).name))
|
|
70
|
+
assemble_addon(self.packs, staged_addon)
|
|
71
|
+
backup = Path(temporary) / 'previous'
|
|
72
|
+
if target.exists():
|
|
73
|
+
os.replace(target, backup)
|
|
74
|
+
try:
|
|
75
|
+
os.replace(stage, target)
|
|
76
|
+
except OSError:
|
|
77
|
+
if backup.exists():
|
|
78
|
+
os.replace(backup, target)
|
|
79
|
+
raise
|
|
51
80
|
return True, None
|
|
52
81
|
except (DependencyError, OSError, ValueError) as exc:
|
|
53
82
|
return False, str(exc)
|
|
@@ -65,6 +94,11 @@ class MapProjectBuilder:
|
|
|
65
94
|
report_dependency_warnings(manager)
|
|
66
95
|
packs = list(manager.get_all_dependencies().values())
|
|
67
96
|
root = manager.root_node.addon_pack
|
|
97
|
+
if self.merge:
|
|
98
|
+
prepare_libraries(packs + [root])
|
|
99
|
+
else:
|
|
100
|
+
for pack in packs + [root]:
|
|
101
|
+
prepare_libraries([pack])
|
|
68
102
|
validate_target(self.target_dir, self.source_dir, packs + [root])
|
|
69
103
|
for name in ('behavior_packs', 'resource_packs', 'db'):
|
|
70
104
|
if is_within(self.target_dir, Path(self.source_dir) / name):
|
|
@@ -81,6 +115,9 @@ class MapProjectBuilder:
|
|
|
81
115
|
pack.copy_behavior_to(target.behavior_packs_dir, rename=Path(pack.behavior_pack_dir).name + '_' + pack.pkg_name)
|
|
82
116
|
if pack.resource_pack_dir:
|
|
83
117
|
pack.copy_resource_to(target.resource_packs_dir, rename=Path(pack.resource_pack_dir).name + '_' + pack.pkg_name)
|
|
118
|
+
for lib in getattr(pack, 'code_libraries', []):
|
|
119
|
+
destination = Path(target.behavior_packs_dir) / (Path(pack.behavior_pack_dir).name + '_' + pack.pkg_name) / lib['target']
|
|
120
|
+
shutil.copytree(lib['source'], destination)
|
|
84
121
|
source.copy_behavior_packs_to(self.target_dir)
|
|
85
122
|
source.copy_resource_packs_to(self.target_dir)
|
|
86
123
|
else:
|
|
@@ -7,6 +7,7 @@ from watchdog.events import FileSystemEventHandler
|
|
|
7
7
|
from ..dependencies import DependencyService, is_native_file, validate_game_files
|
|
8
8
|
from .AddonsPack import AddonsPack
|
|
9
9
|
from .assembly import is_within, pack_files, rebuild_file, validate_target
|
|
10
|
+
from ..code_libraries import prepare_libraries
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class FileChangeHandler(FileSystemEventHandler):
|
|
@@ -104,6 +105,8 @@ class ProjectWatcher:
|
|
|
104
105
|
self.dependency_manager = DependencyService(self.source_dir).resolve()
|
|
105
106
|
self.main_addon_pack = self.dependency_manager.root_node.addon_pack
|
|
106
107
|
self.packs = list(self.dependency_manager.get_all_dependencies().values()) + [self.main_addon_pack]
|
|
108
|
+
self.has_libraries = prepare_libraries(self.packs)
|
|
109
|
+
self.has_libraries = self.has_libraries or self.dependency_manager.has_git_projects
|
|
107
110
|
validate_target(self.target_dir, self.source_dir, self.packs)
|
|
108
111
|
self.target_addon_pack = AddonsPack(project_name, self.target_dir)
|
|
109
112
|
for kind in ('behavior', 'resource'):
|
|
@@ -118,6 +121,9 @@ class ProjectWatcher:
|
|
|
118
121
|
|
|
119
122
|
def rebuild(self, kind, relative):
|
|
120
123
|
with self.lock:
|
|
124
|
+
if self.has_libraries:
|
|
125
|
+
self._build_libraries()
|
|
126
|
+
return str(Path(getattr(self.target_addon_pack, kind + '_pack_dir')) / relative)
|
|
121
127
|
self.validate_sources()
|
|
122
128
|
return self._rebuild(kind, relative)
|
|
123
129
|
|
|
@@ -129,12 +135,22 @@ class ProjectWatcher:
|
|
|
129
135
|
|
|
130
136
|
def rebuild_all(self):
|
|
131
137
|
with self.lock:
|
|
138
|
+
if self.has_libraries:
|
|
139
|
+
self._build_libraries()
|
|
140
|
+
return
|
|
132
141
|
self.validate_sources()
|
|
133
142
|
current = {(kind, rel) for p in self.packs for kind in ('behavior', 'resource') for rel in pack_files(p, kind)}
|
|
134
143
|
for kind, relative in sorted(self.known_files | current):
|
|
135
144
|
self._rebuild(kind, relative)
|
|
136
145
|
self.known_files = current
|
|
137
146
|
|
|
147
|
+
def _build_libraries(self):
|
|
148
|
+
# 带固定代码库的项目使用原子完整组装,不能绕过摘要或目标冲突校验。
|
|
149
|
+
from .project_builder import AddonProjectBuilder
|
|
150
|
+
success, error = AddonProjectBuilder(self.source_dir, self.target_dir).build()
|
|
151
|
+
if not success:
|
|
152
|
+
raise ValueError(error)
|
|
153
|
+
|
|
138
154
|
def validate_sources(self):
|
|
139
155
|
for pack in self.packs:
|
|
140
156
|
validate_game_files(pack.path, [pack.behavior_pack_dir, pack.resource_pack_dir])
|