mcpywrap 0.4.2__tar.gz → 0.4.3__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.4.2 → mcpywrap-0.4.3}/CHANGELOG.md +7 -0
- {mcpywrap-0.4.2/mcpywrap.egg-info → mcpywrap-0.4.3}/PKG-INFO +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/game-backends.md +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/__init__.py +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/connect_cmd.py +11 -2
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/install.py +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/macos.py +30 -6
- mcpywrap-0.4.3/mcpywrap/engines/macos_network.py +47 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/tui.py +4 -2
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/bridge_payload/Injector.exe +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/bridge_payload/Loader.dll +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/bridge_payload/McpyMcsAuth.dll +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/bridge_payload/manifest.json +3 -3
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_debug.py +4 -2
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/session_worker.py +2 -2
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/sessions.py +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/window_capture/manifest.json +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/window_capture/mcpy-window-capture.exe +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3/mcpywrap.egg-info}/PKG-INFO +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap.egg-info/SOURCES.txt +2 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/pyproject.toml +1 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/SKILL.md +4 -1
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/macos-local.md +26 -1
- mcpywrap-0.4.3/tests/test_macos_network.py +120 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/LICENSE +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/MANIFEST.in +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/README.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/THIRD_PARTY_NOTICES.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/code-libraries.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/development.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/engine-discovery.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/git-dependencies.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/input-sequence.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/launcher-client-python.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/local-dependencies.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/macos-resource-reload.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/mcs-auth.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/qt6-development.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/qumod.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/release-0.4.0.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/release-readiness-0.4.0.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/releasing.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/runtime-debug.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/runtime-input.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/runtime-player.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/runtime-ui.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/docs/shared-log-ui.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/__main__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/AddonsPack.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/MapPack.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/assembly.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/dependency_manager.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/file_merge.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/project_builder.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/builders/watcher.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/cli.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/code_libraries.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/command_context.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/add_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/build_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/default_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/dependency_prompt.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/desktop_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/dev_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/doctor_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/edit_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/engine_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/init_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/mod_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/modsdk_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/package_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/publish_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/record_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/remove_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/run_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/runtime_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/runtime_input_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/runtime_player_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/runtime_ui_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/serve_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/session_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/sync_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/commands/ui_cmd.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/config.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/custom_packaging.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/dependencies.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/apk_profile.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/backend.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/host.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/official.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/preferences.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/engines/windows.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/framework_presets.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/frameworks.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/git_cache.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/git_projects.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/input_executor.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/input_plan.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/auth_interaction.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/bridge_assets.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/bridge_payload/publisher.cer +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/diagnostics.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/discovery.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/editor.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/file_logs.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/game.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/host_input.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/hot_reload.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/input_sequence.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/launcher_python.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/log_colorizer.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/log_protocol.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/mcs.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/mcs_auth.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/network.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/private_logs.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/processes.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/recording_worker.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/recordings.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_input_payload.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_key_timeline_payload.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_player_payload.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_ui.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_ui_outline.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/runtime_ui_payload.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/session_capabilities.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/studio_server.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/studio_server_ui.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/symlinks.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/unified_input.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/mcstudio/window.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/minecraft/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/minecraft/addons.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/minecraft/level_dat.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/minecraft/map.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/minecraft/netease_modsdk.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/minecraft/template/mod_template.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/project_dependencies.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/project_init.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/recording_io.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/remote/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/remote/client.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/remote/http_server.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/remote/service.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/source_files.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/timeline.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/engine_setup.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/log_view.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/log_window.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/project_ui.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/run_session.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/session_controller.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/session_tools.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/session_views.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/ui/world_config.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/utils/__init__.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/utils/pip_error_parser.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/utils/print_guide.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/utils/project_setup.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap/utils/utils.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap.egg-info/requires.txt +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/mcs_auth/Bridge.cs +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/mcs_auth/Injector.cs +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/mcs_auth/Loader.c +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/mcs_auth/publisher.cer +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/mcs_auth/publisher.json +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/runtime_input/README.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/runtime_input/launcher-monotonic.patch +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/runtime_input/steady_clock.h +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/window_capture/video_capture.h +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/native/window_capture/window_capture.cpp +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/scripts/build_all_native.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/scripts/build_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/scripts/build_window_capture.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/scripts/check_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/scripts/check_release.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/scripts/check_window_capture.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/setup.cfg +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/agents/openai.yaml +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/input-sequence.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/input-writing.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/instance-world-settings.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/local-development.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/project-dependencies.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/remote-testing.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/runtime-input.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/runtime-key-timeline.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/runtime-player.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/runtime-ui.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/references/troubleshooting.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/scripts/bootstrap.ps1 +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/scripts/bootstrap.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/scripts/game_window.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/skills/mcpywrap/scripts/smoke.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/README.md +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/client_probe.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/input_probe.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/key_timeline_probe.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/movement.json +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/run.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/server_fixture.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual/runtime_controls/support.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_dependency_boundaries.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_input_window.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_integration.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_native_recording.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_remote_recording.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_runtime_input.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_runtime_input_unified.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_runtime_key_timeline.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_runtime_player.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/manual_runtime_ui.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_auth_interaction.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_automation.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_build_replace_retry.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_code_libraries.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_dependency_boundaries.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_engine_backends.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_engine_compatibility.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_engine_discovery.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_engine_install.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_game_window_skill.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_git_projects.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_host_input.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_input_sequence.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_instance_cppconfig.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_launch_encoding.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_launcher_python.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_local_auth_session.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_local_dependencies.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_manual_runtime_controls.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_mcs_auth.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_mouse.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_native_input_sequence.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_native_preferences.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_native_recording.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_network.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_network_sessions.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_pack_uuid_conflicts.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_package.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_qt_sessions.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_qumod_workflow.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_recordings.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_remote.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_remote_scripts.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_resource_reload.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_runtime_debug.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_runtime_input.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_runtime_key_timeline.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_runtime_player.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_runtime_ui.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_runtime_ui_outline.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_session_capabilities.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_shared_logs.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_skill_workflows.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_unified_skill_contract.py +0 -0
- {mcpywrap-0.4.2 → mcpywrap-0.4.3}/tests/test_window_focus.py +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.4.3
|
|
4
|
+
|
|
5
|
+
- macOS 新增无认证服务器连接,复用 `mcpy connect` 与项目 `[tool.mcpywrap.server]` 的 `run` 入口、cppconfig、日志、偏好和退出机制,不装配本地 Mod 或创建本地世界实例。
|
|
6
|
+
- 配套 macOS 运行包 0.4.3 使用现有引擎 Python 入口进服,无新增游戏二进制补丁;首次安装自动取得配套运行包,旧包给出明确升级命令,保留已有世界绑定。
|
|
7
|
+
- 网络会话仅开放客户端 Python;在 worker 中拒绝服务端 Python 和本地 Mod 热更。macOS 初次连接超时会提示失败并回收游戏,进入 HUD 后记录连接验证结果。
|
|
8
|
+
- Skill 补充离线服务器条件、旧运行包升级、状态验证及客户端调试边界。已在 macOS 26.6.2 / APK 3.10.100.299889 上通过本机 Nemisys + SynapseAPI + Nukkit 验证 connect、项目 run、Python 与失败清理;不包含 MCS/MPay 登录、外部 HTTP 资源包或新版统一输入的验收。
|
|
9
|
+
|
|
3
10
|
## 0.4.2
|
|
4
11
|
|
|
5
12
|
- 新增统一 `runtime input capabilities/observe/run/status/cancel/stop` 与 `mcpy.input`。键盘组合、独立边沿、玩家与单节点 UI 操作共用 JSON steps、动作注册表、排时和清理;单次 key 快捷命令只生成同一份计划。
|
|
@@ -19,7 +19,7 @@ WindowsBackend 调用原有 MC Studio 发现、cppconfig 和 Safaia 流程,保
|
|
|
19
19
|
|
|
20
20
|
macOS 首次 mcpy run 自动从内置发布源安装原生运行包和官方 APK,进度在 stderr;也可提前执行 mcpy engine install。--catalog / --apk 是高级覆盖选项。交互式 run 显示共用调试小窗,ui 打开完整管理页;--no-gui / --detach / --json / --non-interactive 抑制调试小窗;Windows 保持手动安装 MC Studio。默认资源目录 ~/Library/Application Support/mcpy,可设置 MCPY_ENGINE_HOME、MCPY_RUNTIME_CATALOG。
|
|
21
21
|
|
|
22
|
-
macOS
|
|
22
|
+
macOS 支持离线 Addon 世界;无认证服务器连接需 CLI 0.4.3+ 与运行包 0.4.3+ 配套,运行包须声明 network_connect_protocol=1(旧 0.4.0 运行包需要显式升级)。connect 与配置了 server 的 run 共用 cppconfig、会话、日志及退出;网络会话仅支持客户端调试,不装配本地 Mod。macOS 不支持 Map、MCS 登录或 Windows 桌面截图/键鼠/录像;需要这些能力时显式使用 Windows 远程端。最低 macOS 13.0,实际游戏只在 26.6.2 验证。运行包尚为 ad-hoc 签名、公证待完成。
|
|
23
23
|
|
|
24
24
|
图形界面的会话操作集中在 ui/session_controller.py;主界面和小窗仅使用共用 SessionControls/PythonConsole 与 LogView。小窗不再另起 Studio TCP 接收器,详见 [共用日志界面](shared-log-ui.md)。
|
|
25
25
|
|
|
@@ -22,5 +22,14 @@ def connect_cmd(host, port, mcs_auth=False, detach=False, request_id=None, **eng
|
|
|
22
22
|
if request_id:
|
|
23
23
|
raise click.UsageError('--request-id 仅用于远程启动')
|
|
24
24
|
from ..engines.backend import get_backend
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
from ..engines.host import EngineError
|
|
26
|
+
backend = get_backend()
|
|
27
|
+
target = ServerTarget(host, port, 'mcs' if mcs_auth else 'none')
|
|
28
|
+
try:
|
|
29
|
+
return backend.connect(target, engine_overrides=engine_overrides, detach=detach)
|
|
30
|
+
except EngineError as error:
|
|
31
|
+
if error.code != 'setup_required' or not backend.managed_install: raise
|
|
32
|
+
from .engine_cmd import perform_install
|
|
33
|
+
click.echo('首次运行:自动下载并准备本地游戏环境…', err=True)
|
|
34
|
+
perform_install()
|
|
35
|
+
return backend.connect(target, engine_overrides=engine_overrides, detach=detach)
|
|
@@ -24,7 +24,7 @@ from .host import EngineError, describe, require_macos
|
|
|
24
24
|
|
|
25
25
|
CATALOG_ENV = 'MCPY_RUNTIME_CATALOG'
|
|
26
26
|
DEFAULT_CATALOG = ('https://github.com/EaseCation/mcpelauncher-manifest/releases/download/'
|
|
27
|
-
'mcpy-runtime-v0.4.
|
|
27
|
+
'mcpy-runtime-v0.4.3/catalog.json')
|
|
28
28
|
# Public constant from the official ApkDownload component, verified 2026-10-05.
|
|
29
29
|
# https://mcdev.webapp.163.com/static/js/4.05713153ba17aea6db53.js
|
|
30
30
|
CDN_CONSTANT = 'mEE7Cot48r9j2AvEL2N6jpXEc'
|
|
@@ -65,7 +65,8 @@ def pinned_runtime(item):
|
|
|
65
65
|
install.verify_game(game, plan['profile'])
|
|
66
66
|
compatibility = install.preflight_runtime(app, game, plan['profile'], meta, plan['runtime_id'])
|
|
67
67
|
return {'runtime': str(app), 'game': str(game), 'profile': plan['profile'], 'runtime_id': plan['runtime_id'],
|
|
68
|
-
'compat_report': compatibility, 'cppconfig_protocol': meta.get('cppconfig_protocol')
|
|
68
|
+
'compat_report': compatibility, 'cppconfig_protocol': meta.get('cppconfig_protocol'),
|
|
69
|
+
'network_connect_protocol': meta.get('network_connect_protocol')}
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
def run(project, *, new=False, listing=False, delete=None, clean_all=False, force=False,
|
|
@@ -85,9 +86,15 @@ def run(project, *, new=False, listing=False, delete=None, clean_all=False, forc
|
|
|
85
86
|
raise EngineError('MC Studio 身份、EXE 和下载目录参数仅适用于 Windows。', 'unsupported_option',
|
|
86
87
|
'macOS 当前使用开发者 APK 的离线模式;资源由 mcpy engine 管理。')
|
|
87
88
|
config = read_project(project)
|
|
88
|
-
if
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
if not (listing or delete or clean_all):
|
|
90
|
+
from ..mcstudio.network import configured_target, prepare_project
|
|
91
|
+
target = configured_target(config)
|
|
92
|
+
if target is not None:
|
|
93
|
+
if new or instance_prefix or world_config is not None:
|
|
94
|
+
raise click.UsageError('网络模式不支持 --new 或本地世界实例 ID')
|
|
95
|
+
packs = prepare_project(project, config)
|
|
96
|
+
from .macos_network import connect
|
|
97
|
+
return connect(target, project_dir=project, packs=packs, engine_overrides=overrides, detach=detach)
|
|
91
98
|
if config.get('tool', {}).get('mcpywrap', {}).get('project_type', 'addon') != 'addon':
|
|
92
99
|
raise EngineError('macOS 首版支持 Addon 测试,Map 项目尚未接入。', 'unsupported_feature',
|
|
93
100
|
'地图项目请在 Windows 本机运行。')
|
|
@@ -192,6 +199,13 @@ def run(project, *, new=False, listing=False, delete=None, clean_all=False, forc
|
|
|
192
199
|
|
|
193
200
|
def launch_session(data):
|
|
194
201
|
plan = data['launch']
|
|
202
|
+
if data.get('mode') == 'network':
|
|
203
|
+
from types import SimpleNamespace
|
|
204
|
+
from ..mcstudio.network import ServerTarget, unauthenticated_config
|
|
205
|
+
config = unauthenticated_config(
|
|
206
|
+
SimpleNamespace(version=plan['engine_version'], download_dir=plan['game']),
|
|
207
|
+
ServerTarget(**data['network']['target']))
|
|
208
|
+
install.write_json(Path(plan['cppconfig']), config)
|
|
195
209
|
app = Path(plan['runtime'])
|
|
196
210
|
helper = app/'Contents/Resources/launcher/run_netease_dev.py'
|
|
197
211
|
if not helper.is_file():
|
|
@@ -228,7 +242,7 @@ class MacOSBackend(GameBackend):
|
|
|
228
242
|
label = 'macOS · Apple Silicon · Metal'
|
|
229
243
|
capabilities = ('local-worlds', 'macos-local-worlds', 'engine-install', 'macos-engine-install',
|
|
230
244
|
'status', 'logs', 'stop', 'py', 'runtime', 'runtime-ui', 'runtime-player',
|
|
231
|
-
'client-python-requests', 'project-ui', 'reload', 'watch')
|
|
245
|
+
'client-python-requests', 'project-ui', 'reload', 'watch', 'network-sessions')
|
|
232
246
|
managed_install = True
|
|
233
247
|
setup_description = ('在 Apple Silicon Mac 上运行本地 Addon 测试。\n'
|
|
234
248
|
'首次运行会自动下载原生启动器和网易开发者版资源,并在本机完成安装。\n'
|
|
@@ -250,6 +264,10 @@ class MacOSBackend(GameBackend):
|
|
|
250
264
|
options.pop('no_gui', None)
|
|
251
265
|
return run(project, **options)
|
|
252
266
|
|
|
267
|
+
def connect(self, target, **options):
|
|
268
|
+
from .macos_network import connect
|
|
269
|
+
return connect(target, **options)
|
|
270
|
+
|
|
253
271
|
def watch_directory(self, project, data):
|
|
254
272
|
return Path(data['config_path']).parent/'assembled'
|
|
255
273
|
|
|
@@ -380,7 +398,9 @@ else:
|
|
|
380
398
|
request_id = 'startup-' + str(index)
|
|
381
399
|
result = channel._rpc('result', {'request_id': request_id})
|
|
382
400
|
if result.get('request_id') == request_id and result.get('state') in ('failed', 'cancelled'):
|
|
383
|
-
raise EngineError('
|
|
401
|
+
raise EngineError(('服务器连接未完成:' if data.get('mode') == 'network' else '世界启动步骤失败:')
|
|
402
|
+
+ str(result.get('error', result['state'])),
|
|
403
|
+
'connection_failed' if data.get('mode') == 'network' else 'world_start_failed')
|
|
384
404
|
except (OSError, ConnectionError):
|
|
385
405
|
pass
|
|
386
406
|
try:
|
|
@@ -389,9 +409,13 @@ else:
|
|
|
389
409
|
stream.seek(max(0, stream.tell() - 65536))
|
|
390
410
|
if b'"top_screen":"hud_screen"' in stream.read():
|
|
391
411
|
data['world_ready'] = True
|
|
412
|
+
if data.get('mode') == 'network': data['connection_verified'] = True
|
|
392
413
|
return True
|
|
393
414
|
except OSError:
|
|
394
415
|
pass
|
|
416
|
+
if data.get('mode') == 'network' and time.time() - data['created_at'] > 90:
|
|
417
|
+
raise EngineError('90 秒内未进入服务器;请检查地址、端口、协议版本以及服务器是否允许无认证的中国版客户端。',
|
|
418
|
+
'connection_timeout')
|
|
395
419
|
return False
|
|
396
420
|
|
|
397
421
|
def handoff(self, data):
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Anonymous server launch using the shared cppconfig and session lifecycle."""
|
|
2
|
+
from dataclasses import asdict
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from . import install
|
|
8
|
+
from .host import EngineError, require_macos
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def connect(target, *, project_dir=None, packs=(), engine_overrides=None, detach=False):
|
|
12
|
+
from ..command_context import project_dir as current_project, json_output
|
|
13
|
+
from ..mcstudio import sessions
|
|
14
|
+
from ..remote.service import directory_lock
|
|
15
|
+
from .macos import ensure_runtime, pinned_runtime
|
|
16
|
+
require_macos()
|
|
17
|
+
if json_output() and not detach:
|
|
18
|
+
raise click.UsageError('网络运行 --json 需要 --detach;使用 status/logs/stop 管理返回的会话')
|
|
19
|
+
overrides = engine_overrides or {}
|
|
20
|
+
if target.auth != 'none' or overrides.get('game_executable_path') or overrides.get('mcs_download_path'):
|
|
21
|
+
raise EngineError('macOS 服务器连接仅支持无认证模式;MC Studio 身份和 EXE 参数仅适用于 Windows。',
|
|
22
|
+
'unsupported_option')
|
|
23
|
+
root = Path(project_dir or current_project()).resolve()
|
|
24
|
+
with directory_lock(install.home()/'install-lock'):
|
|
25
|
+
state = pinned_runtime({'installation': ensure_runtime()})
|
|
26
|
+
if state.get('network_connect_protocol') != 1:
|
|
27
|
+
raise EngineError('此 macOS 运行包尚不支持连接服务器。', 'runtime_incompatible',
|
|
28
|
+
'请执行 mcpy engine install --catalog ' + install.DEFAULT_CATALOG + ',再重试;已有世界保留原绑定。')
|
|
29
|
+
version = state['profile']['apk']['version']
|
|
30
|
+
requested = overrides.get('engine_version')
|
|
31
|
+
if requested and requested != version:
|
|
32
|
+
raise EngineError('当前资源版本为 ' + version, 'engine_version_mismatch',
|
|
33
|
+
'请使用匹配的资源,或省略 --engine-version。')
|
|
34
|
+
session = uuid.uuid4().hex
|
|
35
|
+
directory = sessions.session_path(root, session)
|
|
36
|
+
plan = {'runtime': state['runtime'], 'game': state['game'],
|
|
37
|
+
'data': str(directory/'native/data'), 'cache': str(directory/'native/cache'),
|
|
38
|
+
'engine_version': version, 'compat_report': state.get('compat_report'),
|
|
39
|
+
'cppconfig': str(directory/'runtime.cppconfig')}
|
|
40
|
+
data = sessions.start(root, backend='macos-arm64', session_id=session, launch=plan,
|
|
41
|
+
network={'target': asdict(target), 'engine': {'version': version}})
|
|
42
|
+
result = sessions.handoff(data)
|
|
43
|
+
if detach:
|
|
44
|
+
return result
|
|
45
|
+
click.echo(f'连接 {target.host}:{target.port}(无认证);不装配本地 Mod。')
|
|
46
|
+
from .tui import watch_session
|
|
47
|
+
return watch_session(root, session)
|
|
@@ -19,10 +19,12 @@ def watch_session(project, session):
|
|
|
19
19
|
backend = get_backend(data.get('backend', 'windows'))
|
|
20
20
|
table = Table.grid(padding=(0, 2))
|
|
21
21
|
table.add_row('平台', backend.label)
|
|
22
|
-
|
|
22
|
+
network = data.get('mode') == 'network'
|
|
23
|
+
table.add_row('状态', ('已进入服务器' if data.get('connection_verified') else '正在连接服务器…')
|
|
24
|
+
if network else ('世界已就绪' if data.get('world_ready') else '引擎已启动,正在准备世界…'))
|
|
23
25
|
table.add_row('会话', session)
|
|
24
26
|
table.add_row('操作', 'Ctrl+C 保存退出;其他终端可使用 logs / runtime py')
|
|
25
|
-
live.update(Panel(table, title='mcpy 本地测试'))
|
|
27
|
+
live.update(Panel(table, title='mcpy 服务器测试' if network else 'mcpy 本地测试'))
|
|
26
28
|
time.sleep(.25)
|
|
27
29
|
except KeyboardInterrupt:
|
|
28
30
|
console.print('正在保存世界并关闭窗口…')
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"Loader.c": "f2f1f781ce82fc30c62a6ce22bb048d3b023f6ff500e6ec8228da28b4dbf1b4a"
|
|
12
12
|
},
|
|
13
13
|
"files": {
|
|
14
|
-
"Injector.exe": "
|
|
15
|
-
"Loader.dll": "
|
|
16
|
-
"McpyMcsAuth.dll": "
|
|
14
|
+
"Injector.exe": "376bbefafdab254e413e6a6d9cb1b880e1e94fd259bae55c8a882e17a383892e",
|
|
15
|
+
"Loader.dll": "a4f4da0a3d65dfd94802ac2dd30e5edea81d34659df451c961de15e44a87f6ac",
|
|
16
|
+
"McpyMcsAuth.dll": "c5f105f2e8bd821aa8a0bc1435cc42bac2ca690343a63f8c773eb593971a5ffb",
|
|
17
17
|
"publisher.cer": "e5aa2ac39df40804ed8b08fe8e43c4a2b8923f0d02a360708486b2db5b6303d7"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -314,6 +314,8 @@ class _ControlHandler(socketserver.BaseRequestHandler):
|
|
|
314
314
|
request = json.loads(recv_exact(self.request, size).decode('utf-8'))
|
|
315
315
|
if request.get('token') != self.server.token:
|
|
316
316
|
raise ValueError('会话控制凭据无效')
|
|
317
|
+
if self.server.mode == 'network' and (request.get('side', 'client') != 'client' or request.get('action') == 'reload'):
|
|
318
|
+
raise ValueError('网络会话仅支持客户端调试,不支持服务端 Python 或本地 Mod 热更')
|
|
317
319
|
if request.get('action') == 'execute':
|
|
318
320
|
options = {'condition': request['condition']} if request.get('condition') is not None else {}
|
|
319
321
|
if options and not hasattr(self.server.channel, 'submit'):
|
|
@@ -357,8 +359,8 @@ class RuntimeControlServer(socketserver.ThreadingTCPServer):
|
|
|
357
359
|
daemon_threads = True
|
|
358
360
|
allow_reuse_address = True
|
|
359
361
|
|
|
360
|
-
def __init__(self, channel, token, project=None, session=None):
|
|
361
|
-
self.channel, self.token = channel, token
|
|
362
|
+
def __init__(self, channel, token, project=None, session=None, mode='local'):
|
|
363
|
+
self.channel, self.token, self.mode = channel, token, mode
|
|
362
364
|
self.input_manager = None
|
|
363
365
|
if project is not None and session is not None:
|
|
364
366
|
from .host_input import HostInputManager
|
|
@@ -57,12 +57,12 @@ def run(project, session):
|
|
|
57
57
|
debug_channel = backend.debug_channel(data, receiver._write)
|
|
58
58
|
debug_channel.start(process.pid)
|
|
59
59
|
token = uuid.uuid4().hex
|
|
60
|
-
control_server = RuntimeControlServer(debug_channel, token, project, session)
|
|
60
|
+
control_server = RuntimeControlServer(debug_channel, token, project, session, mode=data.get('mode', 'local'))
|
|
61
61
|
control_server.start()
|
|
62
62
|
sessions.save(directory/'control.json', {'port': control_server.server_address[1],
|
|
63
63
|
'token': token,
|
|
64
64
|
'unified_input_worker': True,
|
|
65
|
-
'python_reload_sides': ['client', 'server'],
|
|
65
|
+
'python_reload_sides': [] if data.get('mode') == 'network' else ['client', 'server'],
|
|
66
66
|
'client_python_queue': callable(getattr(debug_channel, 'submit', None))})
|
|
67
67
|
data.update(state='running', game=identity(process.pid))
|
|
68
68
|
sessions.save(path, data)
|
|
@@ -114,7 +114,7 @@ def handoff(data):
|
|
|
114
114
|
target = data['network']['target']
|
|
115
115
|
result.update(host=target['host'], port=target['port'], identity_source=target['auth'],
|
|
116
116
|
identity_provided=data['mcs_auth'], authenticated=False,
|
|
117
|
-
connection_verified=False, addons_assembled=False,
|
|
117
|
+
connection_verified=data.get('connection_verified', False), addons_assembled=False,
|
|
118
118
|
engine_version=data['network']['engine']['version'])
|
|
119
119
|
return result
|
|
120
120
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"executable_sha256": "
|
|
2
|
+
"executable_sha256": "81642e2d7c9c5097cd86b139f41f75a6f3ca12cb5b2bddbc0427cb2aee92bb64",
|
|
3
3
|
"source_sha256": "0c0b2a02bfecea68b824a486ac1d88cde6bd9fc8ee8e4c9a6939870bd8e710d1",
|
|
4
4
|
"sources": {
|
|
5
5
|
"video_capture.h": "8daee1b8c1c8273bc8d19d39f0559f82c30d7ff900894bcc2cc197d3881532d7",
|
|
Binary file
|
|
@@ -90,6 +90,7 @@ mcpywrap/engines/backend.py
|
|
|
90
90
|
mcpywrap/engines/host.py
|
|
91
91
|
mcpywrap/engines/install.py
|
|
92
92
|
mcpywrap/engines/macos.py
|
|
93
|
+
mcpywrap/engines/macos_network.py
|
|
93
94
|
mcpywrap/engines/official.py
|
|
94
95
|
mcpywrap/engines/preferences.py
|
|
95
96
|
mcpywrap/engines/tui.py
|
|
@@ -229,6 +230,7 @@ tests/test_launch_encoding.py
|
|
|
229
230
|
tests/test_launcher_python.py
|
|
230
231
|
tests/test_local_auth_session.py
|
|
231
232
|
tests/test_local_dependencies.py
|
|
233
|
+
tests/test_macos_network.py
|
|
232
234
|
tests/test_manual_runtime_controls.py
|
|
233
235
|
tests/test_mcs_auth.py
|
|
234
236
|
tests/test_mouse.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mcpywrap"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.3"
|
|
8
8
|
description = "A wrapper for Minecraft China Edition Addons management"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{name = "boybook", email = "boybook@easecation.net"}]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mcpywrap
|
|
3
|
-
description: 使用 mcpywrap 管理《我的世界》中国版 Addon/地图、依赖和 QuMod,并在 Windows 本机/远程或 Apple Silicon macOS
|
|
3
|
+
description: 使用 mcpywrap 管理《我的世界》中国版 Addon/地图、依赖和 QuMod,并在 Windows 本机/远程或 Apple Silicon macOS 本地世界及无认证联机会话中调试、读取 UI 节点和操控玩家;桌面截图/输入/录制仅 Windows。注入的 mcpy.* 仅限调试,禁止用于业务代码。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# mcpywrap
|
|
@@ -107,9 +107,12 @@ Windows 用户在专用、已登录未锁屏的桌面设置 `MCPY_REMOTE_TOKEN`
|
|
|
107
107
|
| 目标 | 启动命令 |
|
|
108
108
|
|---|---|
|
|
109
109
|
| Windows/macOS 本地 Addon 世界(先准备资源) | `mcpy --local --project "<项目>" --non-interactive run --no-gui --detach --json` |
|
|
110
|
+
| Windows/macOS 本机临时服务器 | `mcpy --local --project "<会话目录>" --non-interactive connect <游戏服务器地址> --port 19132 --detach --json` |
|
|
110
111
|
| 远程临时服务器 | `mcpy --remote <Windows服务地址> --non-interactive connect <游戏服务器地址> --port 19132 --detach --json` |
|
|
111
112
|
| 远程项目目标 | `mcpy --remote <地址> --project "<调用端项目>" --non-interactive run --detach --json` |
|
|
112
113
|
|
|
114
|
+
macOS 联机要求 CLI 0.4.3+ 和运行包 0.4.3+(声明 `network_connect_protocol=1`);仅支持允许无认证中国版客户端的服务器。旧 0.4.0 运行包不具备此能力,更新 Skill 或 CLI 不等于运行包升级。具体检查见 [macOS 本地测试](references/macos-local.md)。
|
|
115
|
+
|
|
113
116
|
固定游戏目标在项目 `[tool.mcpywrap.server]` 填 `host/port`。连接不装配本地 Mod,网络模式不支持 Map、`--new` 或世界实例 ID。
|
|
114
117
|
远程机器路径只在 Windows serve 参数中配置,客户端仅可覆盖 `--engine-version`;本机 connect 可使用原有引擎覆盖参数。
|
|
115
118
|
`MCPY_REMOTE_TOKEN` 是服务默认要求的访问令牌;可信网络可显式用 `mcpy --local serve --no-token` 关闭认证,此时忽略已有令牌,可访问该端口的设备均可执行游戏客户端 Python。先检查 `serve --help` 是否支持该参数。`--mcs-auth` 才是本次游戏的 MCS 登录身份,二者互不替代。仅用户要求登录身份时添加该选项。
|
|
@@ -24,7 +24,7 @@ mcpy --local --project <项目> stop --session <sid> --json
|
|
|
24
24
|
|
|
25
25
|
客户端使用 launcher-jni,启动期间可以 --no-wait 提交并用 runtime py-result 按 request_id 查询;--wait-until 是无副作用的客户端 Python 就绪表达式。超时先查原请求,勿重复提交有副作用代码。服务端仍走原有 Safaia,仅进入世界后可用,不给服务端加客户端队列参数。runtime install/ui/player 继续通过公共客户端通道工作;注入的 mcpy.* 仍严格仅用于临时调试。
|
|
26
26
|
|
|
27
|
-
暂不支持本地 Map
|
|
27
|
+
暂不支持本地 Map、MCS 身份、MCEditor 及 Windows screenshot/key/mouse/input-sequence/record。需要它们时使用显式 Windows --remote,不静默回退或尝试登录。macOS 前台 run 的 Ctrl+C 请求保存退出;一次性测试结束必须 stop 并确认进程退出,关闭自己创建的窗口。
|
|
28
28
|
|
|
29
29
|
最低版本为 macOS 13.0,游戏实测仅 26.6.2;包目前 ad-hoc 签名,未公证,不宣称已验证最低系统或 Gatekeeper。失败时提供实际错误及日志,不能建议全局关闭系统保护。
|
|
30
30
|
|
|
@@ -44,3 +44,28 @@ Windows 与 macOS 共用 PySide6 界面:人工使用 `mcpy --local --project <
|
|
|
44
44
|
FPS/VSync、画质、GUI 缩放、音量和输入偏好由 mcpy worker 按用户保存并在新会话导入,位置是引擎资源根目录下的 `preferences/macos`。这些不是 cppconfig 世界设置;不要为每个新实例重新配置。运行中的其他实例不会即时跟随,重开时读取最新值;先正常保存退出以确保游戏完成原生配置写入。
|
|
45
45
|
|
|
46
46
|
版本发现使用 `mcpy --local engine check-updates --json`:实时查询网易 pe/pe_old,返回真实完整版本和 URL;不得递增版本号或猜测 CDN 路径。该命令只检查,不下载或替换实例。新版本 compatibility=not_checked 需实际 APK 校验与结构兼容检查,不能把“发现版本”当作“可运行”。无需先执行此命令:首次 run 的自动安装已经查询 pe。已有本地版本/实例继续运行;缺失的旧版本若不在官方频道中则不可下载,不猜 CDN 路径。
|
|
47
|
+
|
|
48
|
+
## 无认证服务器连接
|
|
49
|
+
|
|
50
|
+
已安装旧运行包时,先正常结束自己的会话,再升级资源并重新 connect:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
mcpy --local engine install --catalog https://github.com/EaseCation/mcpelauncher-manifest/releases/download/mcpy-runtime-v0.4.3/catalog.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
此命令仍通过网易 pe 发现当前 APK;保留旧资源和原世界绑定。connect 使用当前已选资源,旧单人实例继续使用原运行包。
|
|
57
|
+
|
|
58
|
+
需要 CLI 0.4.3+ 和原生运行包 0.4.3+(声明 `network_connect_protocol=1`)。首次安装使用配套运行包;旧 0.4.0 运行包需要显式升级,更新 CLI 不会替换已有世界的绑定。`doctor --capabilities` 的 network-sessions 表示后端入口,实际 connect 还会核对运行包;旧包返回 runtime_incompatible,不切换到单人世界。
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
mcpy --local --project <已有会话目录> --non-interactive connect 127.0.0.1 --port 19132 --detach --json
|
|
62
|
+
mcpy --local --project <同一目录> status --session <sid> --json
|
|
63
|
+
mcpy --local --project <同一目录> runtime py --session <sid> --side client --code "print('network client')" --json
|
|
64
|
+
mcpy --local --project <同一目录> stop --session <sid> --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
connect 不读取项目配置;固定目标可以在项目 `[tool.mcpywrap.server]` 中设置 host/port,再使用同一套 `run --no-gui --detach --json`。网络会话不会创建世界实例或装配本地 Mod;不能组合 --new/实例 ID/世界选项。每次连接独立保存数据和日志,共享用户画面及输入偏好。
|
|
68
|
+
|
|
69
|
+
`running` 仅表示进程已启动。macOS `connection_verified=true` 表示此会话曾观察到进入 HUD,并非持续在线探活,也不表示已通过身份认证;需要当前状态时用客户端 Python/日志验证。90 秒未进入 HUD 会失败并关闭本次游戏。失败后检查 logs --source engine/worker、服务端日志、协议版本及离线配置,不尝试账号登录。
|
|
70
|
+
|
|
71
|
+
仅支持客户端 Python 与 runtime install;游戏内输入另查当前会话 capabilities(旧 C++ 运行包可能缺少统一输入所需的单调时钟),不因注入成功就断言输入可用。服务端 Python 与本地 Mod 热更禁用,服务端逻辑仍由服务器管理。3.10.100.299889 已用本机 Nemisys + SynapseAPI + Nukkit 验证无认证进服;测试端须关闭 Nemisys xbox-auth 并按 SynapseAPI 的调试开关强制 NetEase 玩家。不能推断公开服务器或其他版本一定接受连接。当前网络策略允许目标 UDP 端口和 DNS,不包含外部 HTTP 资源包下载;需要这种资源分发的服务器尚未验收。
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""macOS network entry points preserve common sessions and reject unsupported modes."""
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import tempfile
|
|
5
|
+
import time
|
|
6
|
+
import unittest
|
|
7
|
+
from unittest.mock import Mock, patch
|
|
8
|
+
from click.testing import CliRunner
|
|
9
|
+
|
|
10
|
+
from mcpywrap.cli import cli
|
|
11
|
+
from mcpywrap.engines.macos import MacOSBackend
|
|
12
|
+
from mcpywrap.engines.host import EngineError
|
|
13
|
+
from mcpywrap.mcstudio import sessions
|
|
14
|
+
from mcpywrap.mcstudio.network import ServerTarget
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MacOSNetworkTests(unittest.TestCase):
|
|
18
|
+
def setUp(self):
|
|
19
|
+
temporary = tempfile.TemporaryDirectory()
|
|
20
|
+
self.addCleanup(temporary.cleanup)
|
|
21
|
+
self.root = Path(temporary.name).resolve()
|
|
22
|
+
self.backend = MacOSBackend()
|
|
23
|
+
self.state = {'runtime': str(self.root/'runtime'), 'game': str(self.root/'game'),
|
|
24
|
+
'profile': {'apk': {'version': '3.10'}}, 'network_connect_protocol': 1}
|
|
25
|
+
for name, value in [('mcpywrap.engines.backend.get_backend', self.backend),
|
|
26
|
+
('mcpywrap.engines.macos.require_macos', {}),
|
|
27
|
+
('mcpywrap.engines.macos_network.require_macos', {}),
|
|
28
|
+
('mcpywrap.engines.macos.ensure_runtime', self.state),
|
|
29
|
+
('mcpywrap.engines.macos.pinned_runtime', self.state),
|
|
30
|
+
('mcpywrap.engines.install.home', self.root/'resources')]:
|
|
31
|
+
obj = patch(name, return_value=value); obj.start(); self.addCleanup(obj.stop)
|
|
32
|
+
obj = patch.object(sessions, 'start', side_effect=self.start)
|
|
33
|
+
self.start_mock = obj.start(); self.addCleanup(obj.stop)
|
|
34
|
+
|
|
35
|
+
def start(self, root, **options):
|
|
36
|
+
return {'session': options['session_id'], 'project': str(root), 'state': 'running',
|
|
37
|
+
'game': {'pid': 123}, 'log_path': 'game.log', 'engine_log_path': 'engine.log',
|
|
38
|
+
'level_id': None, 'mode': 'network', 'mcs_auth': False, **options}
|
|
39
|
+
|
|
40
|
+
def call(self, *args):
|
|
41
|
+
return CliRunner().invoke(cli, ['--local', '--project', str(self.root), '--non-interactive', *args, '--json'])
|
|
42
|
+
|
|
43
|
+
def test_connect_does_not_read_project_or_create_world(self):
|
|
44
|
+
(self.root/'pyproject.toml').write_text('broken = [')
|
|
45
|
+
result = self.call('connect', 'localhost', '--port', '29132', '--detach')
|
|
46
|
+
self.assertEqual(result.exit_code, 0, result.output)
|
|
47
|
+
data = json.loads(result.stdout)
|
|
48
|
+
self.assertEqual((data['mode'], data['host'], data['port']), ('network', 'localhost', 29132))
|
|
49
|
+
self.assertFalse(data['connection_verified'])
|
|
50
|
+
self.assertFalse(data['addons_assembled'])
|
|
51
|
+
self.assertFalse((self.root/'.runtime/macos/instances').exists())
|
|
52
|
+
self.assertIn(data['session'], self.start_mock.call_args.kwargs['launch']['data'])
|
|
53
|
+
|
|
54
|
+
def test_old_runtime_rejected_before_start(self):
|
|
55
|
+
self.state.pop('network_connect_protocol')
|
|
56
|
+
result = self.call('connect', 'localhost', '--detach')
|
|
57
|
+
self.assertEqual(json.loads(result.stdout)['code'], 'runtime_incompatible')
|
|
58
|
+
self.start_mock.assert_not_called()
|
|
59
|
+
|
|
60
|
+
def test_auth_and_version_rejected(self):
|
|
61
|
+
for args, code in [(['--mcs-auth'], 'unsupported_option'), (['--engine-version', 'wrong'], 'engine_version_mismatch')]:
|
|
62
|
+
result = self.call('connect', 'localhost', '--detach', *args)
|
|
63
|
+
self.assertEqual(json.loads(result.stdout)['code'], code)
|
|
64
|
+
self.start_mock.assert_not_called()
|
|
65
|
+
|
|
66
|
+
def test_json_requires_detach(self):
|
|
67
|
+
result = self.call('connect', 'localhost')
|
|
68
|
+
self.assertEqual(result.exit_code, 2)
|
|
69
|
+
self.start_mock.assert_not_called()
|
|
70
|
+
|
|
71
|
+
def test_configured_run_and_world_flags(self):
|
|
72
|
+
(self.root/'pyproject.toml').write_text('[project]\nname="network-test"\nversion="0.0.0"\n[tool.mcpywrap.server]\nhost="localhost"\nport=29132\n')
|
|
73
|
+
with patch('mcpywrap.mcstudio.network.prepare_project', return_value=[]):
|
|
74
|
+
result = self.call('run', '--no-gui', '--detach')
|
|
75
|
+
self.assertEqual(result.exit_code, 0, result.output)
|
|
76
|
+
self.assertEqual(json.loads(result.stdout)['mode'], 'network')
|
|
77
|
+
self.start_mock.reset_mock()
|
|
78
|
+
result = self.call('run', '--new', '--detach')
|
|
79
|
+
self.assertEqual(result.exit_code, 2, result.output)
|
|
80
|
+
self.start_mock.assert_not_called()
|
|
81
|
+
result = self.call('run', '--list')
|
|
82
|
+
self.assertEqual(json.loads(result.stdout)['instances'], [])
|
|
83
|
+
|
|
84
|
+
def test_connect_installs_missing_resources_once(self):
|
|
85
|
+
with patch('mcpywrap.engines.macos.ensure_runtime', side_effect=[EngineError('missing', 'setup_required'), self.state]), \
|
|
86
|
+
patch('mcpywrap.commands.engine_cmd.perform_install') as install:
|
|
87
|
+
result = self.call('connect', 'localhost', '--detach')
|
|
88
|
+
self.assertEqual(result.exit_code, 0, result.output)
|
|
89
|
+
install.assert_called_once()
|
|
90
|
+
|
|
91
|
+
def test_ready_requires_hud_and_failure_times_out(self):
|
|
92
|
+
log = self.root/'engine.log'; log.write_text('connecting')
|
|
93
|
+
data = {'mode': 'network', 'engine_log_path': str(log), 'created_at': time.time()-91}
|
|
94
|
+
with self.assertRaises(EngineError) as error:
|
|
95
|
+
self.backend.refresh(data, Mock())
|
|
96
|
+
self.assertEqual(error.exception.code, 'connection_timeout')
|
|
97
|
+
self.assertFalse(data.get('connection_verified'))
|
|
98
|
+
self.backend._next_probe = 0
|
|
99
|
+
log.write_text('{"top_screen":"hud_screen"}')
|
|
100
|
+
self.assertTrue(self.backend.refresh(data, Mock()))
|
|
101
|
+
self.assertTrue(data['connection_verified'])
|
|
102
|
+
|
|
103
|
+
def test_worker_rejects_server_execution_and_reload(self):
|
|
104
|
+
from mcpywrap.mcstudio.runtime_debug import RuntimeControlServer, recv_exact
|
|
105
|
+
import socket, struct
|
|
106
|
+
channel = Mock()
|
|
107
|
+
server = RuntimeControlServer(channel, 'test', mode='network'); server.start()
|
|
108
|
+
try:
|
|
109
|
+
for action, side in [('execute', 'server'), ('reload', 'client')]:
|
|
110
|
+
with socket.create_connection(server.server_address) as client:
|
|
111
|
+
payload = json.dumps({'token':'test','action':action,'side':side,'code':'1+1'}).encode()
|
|
112
|
+
client.sendall(struct.pack('!I',len(payload))+payload)
|
|
113
|
+
size=struct.unpack('!I',recv_exact(client,4))[0]
|
|
114
|
+
result=json.loads(recv_exact(client,size))
|
|
115
|
+
self.assertEqual(result['state'],'failed')
|
|
116
|
+
channel.execute.assert_not_called()
|
|
117
|
+
finally: server.close()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == '__main__': unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|