mcpywrap 0.6.2__tar.gz → 0.6.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.6.2 → mcpywrap-0.6.3}/CHANGELOG.md +6 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/CONTRIBUTING.md +2 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/PKG-INFO +4 -2
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/RELEASING.md +1 -1
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/THIRD_PARTY_NOTICES.md +10 -0
- mcpywrap-0.6.3/core/mcpywrap_core/_version.py +1 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap.egg-info/PKG-INFO +4 -2
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap.egg-info/SOURCES.txt +4 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap.egg-info/requires.txt +5 -1
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/bridge_payload/Injector.exe +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/bridge_payload/Loader.dll +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/bridge_payload/McpyMcsAuth.dll +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/bridge_payload/manifest.json +3 -3
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/window_activation/manifest.json +1 -1
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/window_activation/mcpy-window-activation.dll +0 -0
- mcpywrap-0.6.3/desktop/mcpywrap_assets/window_capture/manifest.json +11 -0
- mcpywrap-0.6.3/desktop/mcpywrap_assets/window_capture/mcpy-window-capture.exe +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/game-backends.md +2 -0
- mcpywrap-0.6.3/docs/windows-local-multiplayer.md +29 -0
- mcpywrap-0.6.3/native/window_capture/ocr.h +75 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/window_capture/window_capture.cpp +9 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/build_window_capture.py +1 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/check_window_capture.py +3 -2
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/setup.py +2 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/SKILL.md +1 -1
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/application-host.md +10 -6
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/dual-clients.md +12 -4
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/troubleshooting.md +3 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/scripts/smoke.py +1 -1
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/README.md +8 -0
- mcpywrap-0.6.3/tests/manual_windows_local_groups.py +120 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_log_query.py +3 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_groups.py +35 -1
- mcpywrap-0.6.3/tests/test_windows_lan.py +153 -0
- mcpywrap-0.6.2/core/mcpywrap_core/_version.py +0 -1
- mcpywrap-0.6.2/desktop/mcpywrap_assets/window_capture/manifest.json +0 -9
- mcpywrap-0.6.2/desktop/mcpywrap_assets/window_capture/mcpy-window-capture.exe +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/LICENSE +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/MANIFEST.in +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/README.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/__init__.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/desktop/mcpywrap_assets/bridge_payload/publisher.cer +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/code-libraries.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/deployment-recovery.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/engine-discovery.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/git-dependencies.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/input-sequence.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/instance-world-settings.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/launcher-client-python.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/local-dependencies.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/macos-resource-reload.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/mcs-auth.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/project-scripts.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/qt6-development.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/qumod.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/runtime-debug.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/runtime-input.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/runtime-player.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/runtime-ui.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/shared-core.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/docs/shared-log-ui.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/examples/consumer_prepare/README.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/background_window/README.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/background_window/apply.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/background_window/client-background.patch +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/background_window/game-window-background.patch +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/background_window/libc-shim-background.patch +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/background_window/manifest-background.patch +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/background_window/test_window.cpp +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/mcs_auth/Bridge.cs +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/mcs_auth/Injector.cs +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/mcs_auth/Loader.c +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/mcs_auth/publisher.cer +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/mcs_auth/publisher.json +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/runtime_input/README.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/runtime_input/launcher-monotonic.patch +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/runtime_input/manifest-monotonic.patch +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/runtime_input/steady_clock.h +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/window_activation/README.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/window_activation/window_activation.cpp +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/native/window_capture/video_capture.h +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/pyproject.toml +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/build_all_native.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/build_app_icon.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/build_distributions.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/build_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/build_window_activation.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/check_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/check_release.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/check_window_activation.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/pypi_release.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/sign_mcs_auth_ci.ps1 +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/verify_core_release.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/verify_distributions.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/scripts/version.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/setup.cfg +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/agents/openai.yaml +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/framework/behavior_pack/CatalogRuntime/__init__.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/framework/behavior_pack/manifest.json +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/framework/build_tools/example.toml +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/framework/build_tools/prepare.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/framework/build_tools/schema.json +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/framework/pyproject.toml +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/game/.gitignore +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/game/behavior_pack/manifest.json +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/game/data/items.json +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/game/pyproject.toml +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/assets/consumer-prepare/game/resource_pack/manifest.json +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/capture.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/consumer-prepare-author.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/consumer-prepare.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/input-sequence.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/input-writing.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/instance-world-settings.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/local-development.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/macos-local.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/network-sessions.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/project-dependencies.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/project-scripts.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/project-workflow.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/remote-testing.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/runtime-debug.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/runtime-input.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/runtime-key-timeline.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/runtime-player.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/runtime-ui.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/script-example.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/references/setup.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/scripts/bootstrap.ps1 +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/scripts/bootstrap.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/skills/mcpywrap/scripts/game_window.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/core_fixture.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/embedded_upgrade_probe.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/consumer_prepare_acceptance.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/lifecycle_dependency_audit.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/README.md +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/client_probe.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/input_probe.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/key_timeline_probe.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/movement.json +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/run.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/server_fixture.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual/runtime_controls/support.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_background_launch.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_dependency_boundaries.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_input_window.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_integration.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_native_recording.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_remote_recording.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_runtime_input.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_runtime_input_unified.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_runtime_key_timeline.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_runtime_player.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/manual_runtime_ui.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/runtime_support.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_application_icons.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_auth_interaction.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_automation.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_build_replace_retry.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_builder_import_boundary.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_code_libraries.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_consumer_prepare.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_core_api.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_core_updates.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_dependency_boundaries.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_deployment_cli.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_deployment_encoding.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_deployment_recovery.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_distribution_layout.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_engine_backends.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_engine_compatibility.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_engine_discovery.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_engine_install.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_execution_boundaries.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_game_window_skill.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_git_projects.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_host_input.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_host_services.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_input_sequence.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_instance_cppconfig.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_launch_encoding.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_launcher_python.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_lifecycle_audit.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_lifecycle_races.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_local_auth_session.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_local_dependencies.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_lock_storage.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_log_stream.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_macos_network.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_manual_runtime_controls.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_mcs_auth.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_mobile_diagnostics.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_mobile_host.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_mobile_pairing.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_modsdk_environment.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_mouse.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_native_input_sequence.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_native_preferences.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_native_recording.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_network.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_network_sessions.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_pack_uuid_conflicts.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_package.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_project_scripts.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_qt_sessions.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_qumod_workflow.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_recordings.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_remote.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_remote_scripts.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_resource_reload.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_restricted_process_identity.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_debug.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_deployment.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_input.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_key_timeline.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_player.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_profiles.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_ui.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_runtime_ui_outline.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_screenshot.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_session_capabilities.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_shared_logs.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_skill_workflows.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_unified_skill_contract.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_window_activation.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_window_focus.py +0 -0
- {mcpywrap-0.6.2 → mcpywrap-0.6.3}/tests/test_window_policy.py +0 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.6.3
|
|
4
|
+
|
|
5
|
+
- Windows `run --clients 2` 默认自动加入并校验双方玩家;提供 `--join-mode manual` 保留手动操作,状态、Qt 会话页和 smoke 验收支持自动加入阶段。
|
|
6
|
+
- Windows 本地多项目联机按实际主机端口适配客机,通过 x64 指令结构自动定位 NetherNet 配置,无固定版本地址表;无法唯一识别时拒绝注入并保留诊断。
|
|
7
|
+
- 已验证 3.9.0.350466、3.9.0.401155 和 3.10.0.436581 的自动加入、双项目 Mod 加载与客机重启。修复组监控探针争用导致的连接状态误报,以及重启客机后的会话交接提示。
|
|
8
|
+
|
|
3
9
|
## 0.6.2
|
|
4
10
|
|
|
5
11
|
- 日志新增字面过滤与重复消息统计,明确扫描范围;支持结构化日志的 App 宿主另提供端侧和毫秒时间范围筛选。
|
|
@@ -8,6 +8,8 @@ python -X utf8 -m unittest discover -s tests -v
|
|
|
8
8
|
|
|
9
9
|
自动化测试使用临时目录、模拟安装/发布、Qt 离屏界面和模拟游戏进程,不启动真实游戏。
|
|
10
10
|
|
|
11
|
+
Windows 本地自动联机改动需运行 `test_windows_lan.py`、组/会话/调试通道回归,再按 [实机验收入口](tests/README.md) 验证 3.9、3.10 与多个项目。端口定位基于运行映像的 x64 指令结构;不得用固定 RVA、版本字符串或未验证的数值搜索替代唯一候选、进程身份及写前/写后校验。更新 Windows 菜单采集辅助程序后重新构建其 manifest;新的菜单动作必须有当前画面依据和有界的失败路径。
|
|
12
|
+
|
|
11
13
|
Windows 输入编排与游戏内队列共用 `timeline.py`,单次 key/mouse 与编排共用 `window.InputSender`。改动这些公共层时运行旧输入、编排及玩家队列回归;真实接收窗口和游戏 CLI 的时序验收见 [输入编排开发说明](tests/README.md#输入编排)。
|
|
12
14
|
测试进程使用UTF-8以保持文件读取和直接调用内部函数时的诊断输出一致;独立子进程另外验证真实CLI在GBK和UTF-8管道中的行为,不通过强制用户修改系统编码来掩盖兼容问题。
|
|
13
15
|
CLI 使用调用上下文传递项目目录;底层服务接受明确路径。不要为 GUI 切换全局工作目录。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Complete mcpy desktop tools, powered by the shared mcpywrap-core
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,7 @@ Requires-Python: >=3.9
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
License-File: THIRD_PARTY_NOTICES.md
|
|
13
|
-
Requires-Dist: mcpywrap-core==0.6.
|
|
13
|
+
Requires-Dist: mcpywrap-core==0.6.3
|
|
14
14
|
Requires-Dist: pip>=23
|
|
15
15
|
Requires-Dist: build>=1.0
|
|
16
16
|
Requires-Dist: watchdog>=2.0.0
|
|
@@ -19,6 +19,8 @@ Requires-Dist: setuptools
|
|
|
19
19
|
Requires-Dist: jsonschema<5,>=4.18
|
|
20
20
|
Requires-Dist: psutil>=5.8.0
|
|
21
21
|
Requires-Dist: rich>=10.0.0
|
|
22
|
+
Requires-Dist: capstone<6,>=5; sys_platform == "win32"
|
|
23
|
+
Requires-Dist: pefile>=2023; sys_platform == "win32"
|
|
22
24
|
Requires-Dist: PySide6-Essentials<7,>=6.6; sys_platform == "win32" or sys_platform == "darwin"
|
|
23
25
|
Dynamic: license-file
|
|
24
26
|
Dynamic: requires-dist
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
Windows CI 先用 MSVC 从 `native/mcs_auth` 编译 x86 登录桥接,再签名并通过构建产物交给测试和 完整桌面包打包任务;编译后的 EXE/DLL 位于 `desktop/mcpywrap_assets/`,不提交到 Git,也不进入核心 wheel。仓库只保留发布者公钥证书和指纹。
|
|
10
10
|
同一 native job 还会从 `native/window_capture` 编译 x64 后台截图辅助程序,生成 SHA-256 清单;测试和打包阶段校验二进制、源码及 wheel/sdist 内容。辅助程序不可用时运行时回退到前台截图。
|
|
11
11
|
|
|
12
|
-
该辅助程序同时提供 WGC 连续采集、Media Foundation H.264 MP4
|
|
12
|
+
该辅助程序同时提供 WGC 连续采集、Media Foundation H.264 MP4 编码、PNG 提帧和 Windows OCR 菜单识别;截图仍保持原协议与回退行为,录制要求 WGC,不回退到桌面捕获。源码清单覆盖 cpp、video_capture.h 和 ocr.h,sdist 必须全部包含;media_protocol 与 ocr_protocol 分别校验。系统 H.264 编解码能力通过只读探测报告,不捆绑 FFmpeg。发布前在交互式 Windows 桌面执行 `MCPY_NATIVE_TESTS=1` 的原生验收;普通 CI 跳过需要可见桌面的测试。
|
|
13
13
|
|
|
14
14
|
从源码安装时,在 Windows x64 且已安装 Visual Studio x86/x64 C++ 工具的环境中先运行 `python scripts/build_all_native.py`,再执行 `pip install -e ./core -e .` 或 `pip install ./core .`。脚本一次编译 x86 登录桥接和 x64 截图程序到包目录;可用 `--output-root <目录>` 将结果放入隔离目录。登录桥接源码编译产物没有发布签名,正式登录能力仍应使用带签名的发布包。
|
|
15
15
|
|
|
@@ -33,3 +33,13 @@ Upstream licensing and source:
|
|
|
33
33
|
The separately downloaded native launcher has its own GPL licensing and
|
|
34
34
|
corresponding-source distribution. Changing the Python GUI binding does not
|
|
35
35
|
change that launcher's license or permit redistribution of Minecraft binaries.
|
|
36
|
+
|
|
37
|
+
## Windows LAN inspection
|
|
38
|
+
|
|
39
|
+
The Windows desktop package installs Capstone 5 (BSD license) and pefile (MIT
|
|
40
|
+
license) through the package manager for x64 instruction decoding and PE header
|
|
41
|
+
inspection. Their upstream licenses remain in their installed distributions:
|
|
42
|
+
https://github.com/capstone-engine/capstone and https://github.com/erocarrera/pefile.
|
|
43
|
+
No Minecraft executable, memory snapshot, instruction fixture extracted from a
|
|
44
|
+
game, or modified game binary is shipped by this feature. Menu text recognition
|
|
45
|
+
uses Windows.Media.Ocr supplied by Windows.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.6.3'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Complete mcpy desktop tools, powered by the shared mcpywrap-core
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,7 @@ Requires-Python: >=3.9
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
License-File: THIRD_PARTY_NOTICES.md
|
|
13
|
-
Requires-Dist: mcpywrap-core==0.6.
|
|
13
|
+
Requires-Dist: mcpywrap-core==0.6.3
|
|
14
14
|
Requires-Dist: pip>=23
|
|
15
15
|
Requires-Dist: build>=1.0
|
|
16
16
|
Requires-Dist: watchdog>=2.0.0
|
|
@@ -19,6 +19,8 @@ Requires-Dist: setuptools
|
|
|
19
19
|
Requires-Dist: jsonschema<5,>=4.18
|
|
20
20
|
Requires-Dist: psutil>=5.8.0
|
|
21
21
|
Requires-Dist: rich>=10.0.0
|
|
22
|
+
Requires-Dist: capstone<6,>=5; sys_platform == "win32"
|
|
23
|
+
Requires-Dist: pefile>=2023; sys_platform == "win32"
|
|
22
24
|
Requires-Dist: PySide6-Essentials<7,>=6.6; sys_platform == "win32" or sys_platform == "darwin"
|
|
23
25
|
Dynamic: license-file
|
|
24
26
|
Dynamic: requires-dist
|
|
@@ -44,6 +44,7 @@ docs/runtime-player.md
|
|
|
44
44
|
docs/runtime-ui.md
|
|
45
45
|
docs/shared-core.md
|
|
46
46
|
docs/shared-log-ui.md
|
|
47
|
+
docs/windows-local-multiplayer.md
|
|
47
48
|
examples/consumer_prepare/README.md
|
|
48
49
|
native/background_window/README.md
|
|
49
50
|
native/background_window/apply.py
|
|
@@ -63,6 +64,7 @@ native/runtime_input/manifest-monotonic.patch
|
|
|
63
64
|
native/runtime_input/steady_clock.h
|
|
64
65
|
native/window_activation/README.md
|
|
65
66
|
native/window_activation/window_activation.cpp
|
|
67
|
+
native/window_capture/ocr.h
|
|
66
68
|
native/window_capture/video_capture.h
|
|
67
69
|
native/window_capture/window_capture.cpp
|
|
68
70
|
scripts/build_all_native.py
|
|
@@ -134,6 +136,7 @@ tests/manual_runtime_input_unified.py
|
|
|
134
136
|
tests/manual_runtime_key_timeline.py
|
|
135
137
|
tests/manual_runtime_player.py
|
|
136
138
|
tests/manual_runtime_ui.py
|
|
139
|
+
tests/manual_windows_local_groups.py
|
|
137
140
|
tests/runtime_support.py
|
|
138
141
|
tests/test_application_icons.py
|
|
139
142
|
tests/test_auth_interaction.py
|
|
@@ -209,6 +212,7 @@ tests/test_unified_skill_contract.py
|
|
|
209
212
|
tests/test_window_activation.py
|
|
210
213
|
tests/test_window_focus.py
|
|
211
214
|
tests/test_window_policy.py
|
|
215
|
+
tests/test_windows_lan.py
|
|
212
216
|
tests/manual/consumer_prepare_acceptance.py
|
|
213
217
|
tests/manual/lifecycle_dependency_audit.py
|
|
214
218
|
tests/manual/runtime_controls/README.md
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
mcpywrap-core==0.6.
|
|
1
|
+
mcpywrap-core==0.6.3
|
|
2
2
|
pip>=23
|
|
3
3
|
build>=1.0
|
|
4
4
|
watchdog>=2.0.0
|
|
@@ -8,5 +8,9 @@ jsonschema<5,>=4.18
|
|
|
8
8
|
psutil>=5.8.0
|
|
9
9
|
rich>=10.0.0
|
|
10
10
|
|
|
11
|
+
[:sys_platform == "win32"]
|
|
12
|
+
capstone<6,>=5
|
|
13
|
+
pefile>=2023
|
|
14
|
+
|
|
11
15
|
[:sys_platform == "win32" or sys_platform == "darwin"]
|
|
12
16
|
PySide6-Essentials<7,>=6.6
|
|
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": "c5f7279a7669e9d091f21992516c31daf05e0f9d14965bf30da1161a66a12f06",
|
|
15
|
+
"Loader.dll": "f56967fe7ce1151463eed6e0c839a5edc0f6b2ae1fc83821737b46f353b972ae",
|
|
16
|
+
"McpyMcsAuth.dll": "1a5bd521c38a02c1f095bc2c329053b40ad4445a9a4af873f4e5e356b7a8217e",
|
|
17
17
|
"publisher.cer": "e5aa2ac39df40804ed8b08fe8e43c4a2b8923f0d02a360708486b2db5b6303d7"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"background_window_protocol": 1,
|
|
3
3
|
"architecture": "x64",
|
|
4
|
-
"dll_sha256": "
|
|
4
|
+
"dll_sha256": "5f5205893096bef0f223830d93085297cbdbce538bc6771ed1d2904d24ab367f",
|
|
5
5
|
"source_sha256": "03c1aab9e8d46c162e11f9448903c4907fbe3b87d1e77d8118b72c3e6b250ec5"
|
|
6
6
|
}
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"executable_sha256": "43637ac49c8d2ece3406568bd2c2e627741adb1153517478911853f4aaca0b98",
|
|
3
|
+
"source_sha256": "64c9db4f31031fc794e4a41a3287a8382c5ac5599244824b6fd7d44971d68f05",
|
|
4
|
+
"sources": {
|
|
5
|
+
"ocr.h": "0b62eed9a41cdeec451283c24b140da4f333a47b59c581e26dd8f0d803e9cfd6",
|
|
6
|
+
"video_capture.h": "f766d4234e41a554a8eb8ecae93fd818c2fb8877fbd1795f3915dcc8771e7909",
|
|
7
|
+
"window_capture.cpp": "64c9db4f31031fc794e4a41a3287a8382c5ac5599244824b6fd7d44971d68f05"
|
|
8
|
+
},
|
|
9
|
+
"media_protocol": 1,
|
|
10
|
+
"ocr_protocol": 1
|
|
11
|
+
}
|
|
@@ -48,6 +48,8 @@ Windows 后台启动使用独立桌面包提供的进程内线程级 CBT 保护
|
|
|
48
48
|
|
|
49
49
|
两端单人实例共用 cppconfig 的 world_info;存储、启动转换、保存优先级和创建参数见[实例世界设置](instance-world-settings.md)。
|
|
50
50
|
|
|
51
|
+
Windows 本地双端的自动加入、运行映像端口定位、菜单控制与就绪契约见 [Windows 本地自动联机](windows-local-multiplayer.md)。该适配仅作用于本地客机,不改变其他平台或外服连接的协议。
|
|
52
|
+
|
|
51
53
|
版本发现使用 `mcpy --local engine check-updates --json`:实时查询网易 pe/pe_old,返回真实完整版本和 URL;不得递增版本号或猜测 CDN 路径。该命令只检查,不下载或替换实例。新版本 compatibility=not_checked 需实际 APK 校验与结构兼容检查,不能把“发现版本”当作“可运行”。无需先执行此命令:首次 run 的自动安装已经查询 pe。已有本地版本/实例继续运行;缺失的旧版本若不在官方频道中则不可下载,不猜 CDN 路径。
|
|
52
54
|
|
|
53
55
|
## macOS 游戏图标
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Windows 本地自动联机
|
|
2
|
+
|
|
3
|
+
WindowsBackend 的本地组默认采用自动加入;用法以 [双客户端技能](../skills/mcpywrap/references/dual-clients.md) 为准。macOS、外服 connect 和远程执行协议不复用本适配层。旧组未记录 join_mode 时继续使用手动方式。
|
|
4
|
+
|
|
5
|
+
## 端口与引擎兼容
|
|
6
|
+
|
|
7
|
+
NetherNet 的主机监听可以从 7551 回退到临时端口,但默认发现不覆盖所有同机主机。工具在主机就绪后取得其网络身份,并核对所属进程唯一的 IPv6 通配 UDP 监听端口;多个候选会被拒绝,不将 Safaia、OreUI 或配置中的目标端口当作 LAN 监听端口。
|
|
8
|
+
|
|
9
|
+
需要非默认端口时,`lan_locator` 使用运行映像的 PE x64 展开表确定函数边界,再反汇编匹配 TransportConfiguration 初始化:16 位端口字段、相邻广播周期、协议 magic 和对象传参必须同时成立,且候选必须唯一。不维护引擎版本/RVA 表,不复用另一个二进制的地址,也不将任意匹配到的 7551 字节视作指令。
|
|
10
|
+
|
|
11
|
+
`lan_memory` 只操作 worker 刚创建的本地客机,核对 PID 创建时间及可执行文件身份。写入时暂停该进程,复核原指令,只更新端口立即数,读回校验并恢复原页属性、恢复线程。失败会结束本次客机;主机保留。磁盘游戏文件始终不改写,进程退出后适配消失。
|
|
12
|
+
|
|
13
|
+
此识别方式已覆盖 3.9.0.350466、3.9.0.401155 和 3.10.0.436581。相同结构的新构建会自动定位;机器架构、配置结构或代码生成方式超出识别条件时必须失败,不保证任意未来引擎都无需算法更新。定位器的离线测试仅使用人工生成指令,不分发游戏代码、内存快照或修改后的游戏文件。
|
|
14
|
+
|
|
15
|
+
## 自动加入与就绪
|
|
16
|
+
|
|
17
|
+
Safaia 的普通脚本执行依赖进入世界后才初始化的游戏系统。自动客机在隔离角色资料中复用一个工具引导世界,待通道就绪后自动保存退出,再完成目标世界选择和资源下载。此阶段的物理 HUD 标记仅供内部通道就绪判断,不能向组发布 world_ready。
|
|
18
|
+
|
|
19
|
+
菜单由会话所属窗口的后台 WGC 画面提供,Windows OCR 返回当前文字及物理坐标。选择目标采用主机玩家标识,拒绝截断、任意编辑距离或多个候选;只规范化已知像素字形混淆。输入前重新观察布局,限定一次动作并安排引擎内抬起;不移动系统鼠标,不抢前台。未知请求不重放,停滞、不可识别菜单或超时保留诊断并结束自动操作。
|
|
20
|
+
|
|
21
|
+
只有客机进入目标网络 ID 和目标世界的 HUD,才发布 world_ready;随后原有 GroupMonitor 核对双方不同玩家与主机服务端名单,发布 connection_verified。自动加入期间,公共客机 Python/热更/输入请求被拒绝,组热更仍要求所有成员世界就绪。明确未发送的只读探针争用保留最近一次验证及原时间戳;真正观察到离开或身份不符会清除验证。
|
|
22
|
+
|
|
23
|
+
worker 是 session.json 的唯一写入者;自动加入线程单独维护 local-join.json 与最近的 local-join-menu.json。状态包含 initializing、leaving_setup、finding_host、joining、joined、failed、cancelled。公共组响应增加 joining 状态,Qt、CLI、smoke 和重启流程共用它。停止会话或主机身份变化会取消自动操作,只清理所属进程。
|
|
24
|
+
|
|
25
|
+
## 验证与维护
|
|
26
|
+
|
|
27
|
+
菜单覆盖中文和英文,依赖 Windows 可用的 OCR 语言及持续渲染的游戏窗口;识别失败没有前台输入回退。引导世界每个角色至多一个,并与项目世界、真实账户资料隔离。组资源热更的现有限制不因自动加入而改变。
|
|
28
|
+
|
|
29
|
+
维护入口为 [测试指南](../tests/README.md) 中的可选四端实机程序。应同时验证两个项目的 Mod 客户端/服务端系统加载、网络与世界隔离、客机重启、另一组保持连接以及停止清理;只观察窗口、进程存活或端口变化不足以确认联机成功。
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// 局域网自动加入使用 Windows 自带 OCR;坐标来自当前客户区画面。
|
|
2
|
+
|
|
3
|
+
std::string json_text(const std::string& text) {
|
|
4
|
+
std::ostringstream out;
|
|
5
|
+
out << '"';
|
|
6
|
+
for (unsigned char ch : text) {
|
|
7
|
+
if (ch == '"' || ch == '\\') out << '\\' << ch;
|
|
8
|
+
else if (ch < 32) out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(ch) << std::dec;
|
|
9
|
+
else out << ch;
|
|
10
|
+
}
|
|
11
|
+
out << '"';
|
|
12
|
+
return out.str();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
int ocr_pixels(const Pixels& pixels) {
|
|
16
|
+
using namespace winrt::Windows::Graphics::Imaging;
|
|
17
|
+
using namespace winrt::Windows::Media::Ocr;
|
|
18
|
+
using namespace winrt::Windows::Storage::Streams;
|
|
19
|
+
// 缩放到 OCR 上限以内,输出仍采用原客户区物理坐标。
|
|
20
|
+
const double scale = std::min(1.0, static_cast<double>(OcrEngine::MaxImageDimension()) / std::max(pixels.width, pixels.height));
|
|
21
|
+
const int width = static_cast<int>(pixels.width * scale);
|
|
22
|
+
const int height = static_cast<int>(pixels.height * scale);
|
|
23
|
+
std::vector<uint8_t> reduced(static_cast<size_t>(width) * height * 4);
|
|
24
|
+
for (int y=0; y<height; ++y) for (int x=0; x<width; ++x) {
|
|
25
|
+
size_t source = (static_cast<size_t>(y / scale) * pixels.width + static_cast<size_t>(x / scale)) * 4;
|
|
26
|
+
std::copy_n(pixels.bgra.data()+source, 4, reduced.data()+(static_cast<size_t>(y)*width+x)*4);
|
|
27
|
+
}
|
|
28
|
+
DataWriter writer;
|
|
29
|
+
writer.WriteBytes(reduced);
|
|
30
|
+
SoftwareBitmap bitmap(BitmapPixelFormat::Bgra8, width, height, BitmapAlphaMode::Ignore);
|
|
31
|
+
bitmap.CopyFromBuffer(writer.DetachBuffer());
|
|
32
|
+
auto english = OcrEngine::TryCreateFromLanguage(winrt::Windows::Globalization::Language(L"en-US"));
|
|
33
|
+
auto preferred = OcrEngine::TryCreateFromUserProfileLanguages();
|
|
34
|
+
if (!english || !preferred) unavailable("Windows OCR language unavailable (English and display language required)");
|
|
35
|
+
std::cout << "{\"protocol\":1,\"width\":" << pixels.width << ",\"height\":" << pixels.height
|
|
36
|
+
<< ",\"language\":" << json_text(to_string(preferred.RecognizerLanguage().LanguageTag())) << ",\"lines\":[";
|
|
37
|
+
bool first = true;
|
|
38
|
+
std::vector<OcrEngine> engines{english};
|
|
39
|
+
if (english.RecognizerLanguage().LanguageTag()!=preferred.RecognizerLanguage().LanguageTag()) engines.push_back(preferred);
|
|
40
|
+
for (auto const& engine : engines) {
|
|
41
|
+
auto result = engine.RecognizeAsync(bitmap).get();
|
|
42
|
+
for (auto const& line : result.Lines()) {
|
|
43
|
+
double left=width, top=height, right=0, bottom=0;
|
|
44
|
+
for (auto const& word : line.Words()) {
|
|
45
|
+
auto box=word.BoundingRect();
|
|
46
|
+
left=std::min(left, static_cast<double>(box.X)); top=std::min(top, static_cast<double>(box.Y));
|
|
47
|
+
right=std::max(right, static_cast<double>(box.X+box.Width)); bottom=std::max(bottom, static_cast<double>(box.Y+box.Height));
|
|
48
|
+
}
|
|
49
|
+
if (!first) std::cout << ',';
|
|
50
|
+
first=false;
|
|
51
|
+
std::cout << "{\"text\":" << json_text(to_string(line.Text())) << ",\"bounds\":["
|
|
52
|
+
<< left/scale << ',' << top/scale << ',' << right/scale << ',' << bottom/scale << "]}";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
std::cout << "]}" << std::endl;
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
int ocr_file(const std::wstring& path) {
|
|
60
|
+
using namespace winrt::Windows::Storage;
|
|
61
|
+
using namespace winrt::Windows::Storage::Streams;
|
|
62
|
+
using namespace winrt::Windows::Graphics::Imaging;
|
|
63
|
+
auto file=StorageFile::GetFileFromPathAsync(path).get();
|
|
64
|
+
auto stream=file.OpenAsync(FileAccessMode::Read).get();
|
|
65
|
+
auto decoder=BitmapDecoder::CreateAsync(stream).get();
|
|
66
|
+
auto bitmap=decoder.GetSoftwareBitmapAsync(BitmapPixelFormat::Bgra8,BitmapAlphaMode::Ignore).get();
|
|
67
|
+
Pixels pixels;
|
|
68
|
+
pixels.width=bitmap.PixelWidth(); pixels.height=bitmap.PixelHeight();
|
|
69
|
+
if (static_cast<uint64_t>(pixels.width)*pixels.height > 16000000) unavailable("ocr_resolution_limit");
|
|
70
|
+
Buffer buffer(pixels.width*pixels.height*4);
|
|
71
|
+
bitmap.CopyToBuffer(buffer);
|
|
72
|
+
pixels.bgra.resize(buffer.Length());
|
|
73
|
+
DataReader::FromBuffer(buffer).ReadBytes(pixels.bgra);
|
|
74
|
+
return ocr_pixels(pixels);
|
|
75
|
+
}
|
|
@@ -25,8 +25,14 @@
|
|
|
25
25
|
|
|
26
26
|
#include <winrt/base.h>
|
|
27
27
|
#include <winrt/Windows.Foundation.h>
|
|
28
|
+
#include <winrt/Windows.Foundation.Collections.h>
|
|
28
29
|
#include <winrt/Windows.Graphics.Capture.h>
|
|
29
30
|
#include <winrt/Windows.Graphics.DirectX.Direct3D11.h>
|
|
31
|
+
#include <winrt/Windows.Globalization.h>
|
|
32
|
+
#include <winrt/Windows.Graphics.Imaging.h>
|
|
33
|
+
#include <winrt/Windows.Media.Ocr.h>
|
|
34
|
+
#include <winrt/Windows.Storage.Streams.h>
|
|
35
|
+
#include <winrt/Windows.Storage.h>
|
|
30
36
|
#include <windows.graphics.capture.interop.h>
|
|
31
37
|
#include <windows.graphics.directx.direct3d11.interop.h>
|
|
32
38
|
|
|
@@ -282,6 +288,7 @@ Pixels capture(HWND hwnd) {
|
|
|
282
288
|
}
|
|
283
289
|
|
|
284
290
|
#include "video_capture.h"
|
|
291
|
+
#include "ocr.h"
|
|
285
292
|
|
|
286
293
|
} // namespace
|
|
287
294
|
|
|
@@ -292,8 +299,10 @@ int wmain(int argc, wchar_t** argv) {
|
|
|
292
299
|
if (option(argc, argv, L"--capabilities")) return media_capabilities();
|
|
293
300
|
if (option(argc, argv, L"--record")) return record_video(argc, argv);
|
|
294
301
|
if (option(argc, argv, L"--extract")) return extract_frames(argc, argv);
|
|
302
|
+
if (option(argc, argv, L"--ocr-file")) return ocr_file(argument(argc, argv, L"--ocr-file"));
|
|
295
303
|
const HWND hwnd = parse_hwnd(argc, argv);
|
|
296
304
|
const Pixels pixels = capture(hwnd);
|
|
305
|
+
if (option(argc, argv, L"--ocr")) return ocr_pixels(pixels);
|
|
297
306
|
const auto png = encode_png(pixels);
|
|
298
307
|
_setmode(_fileno(stdout), _O_BINARY);
|
|
299
308
|
std::cout << "MCPYCAP1 " << pixels.width << ' ' << pixels.height << ' ' << png.size() << '\n';
|
|
@@ -41,6 +41,7 @@ def main():
|
|
|
41
41
|
'sources': {p.name: hashlib.sha256(p.read_text(encoding='utf-8').encode('utf-8')).hexdigest()
|
|
42
42
|
for p in sorted(source.parent.glob('*')) if p.suffix in ('.cpp', '.h')},
|
|
43
43
|
'media_protocol': 1,
|
|
44
|
+
'ocr_protocol': 1,
|
|
44
45
|
}
|
|
45
46
|
(output / 'manifest.json').write_text(json.dumps(manifest, indent=2), encoding='utf-8')
|
|
46
47
|
print('Built Windows Graphics Capture helper:', target)
|
|
@@ -10,9 +10,10 @@ source = root / 'native/window_capture/window_capture.cpp'
|
|
|
10
10
|
payload = root / 'desktop/mcpywrap_assets/window_capture'
|
|
11
11
|
executable = payload / 'mcpy-window-capture.exe'
|
|
12
12
|
manifest = json.loads((payload / 'manifest.json').read_text(encoding='utf-8'))
|
|
13
|
-
assert set(manifest) == {'executable_sha256', 'source_sha256', 'sources', 'media_protocol'}
|
|
13
|
+
assert set(manifest) == {'executable_sha256', 'source_sha256', 'sources', 'media_protocol', 'ocr_protocol'}
|
|
14
14
|
assert manifest['media_protocol'] == 1
|
|
15
|
-
assert
|
|
15
|
+
assert manifest['ocr_protocol'] == 1
|
|
16
|
+
assert set(manifest['sources']) == {'window_capture.cpp', 'video_capture.h', 'ocr.h'}
|
|
16
17
|
for name, digest in manifest['sources'].items():
|
|
17
18
|
assert hashlib.sha256((source.parent / name).read_text(encoding='utf-8').encode('utf-8')).hexdigest() == digest
|
|
18
19
|
assert hashlib.sha256(source.read_text(encoding='utf-8').encode('utf-8')).hexdigest() == manifest['source_sha256']
|
|
@@ -10,5 +10,7 @@ setup(version=version, install_requires=[
|
|
|
10
10
|
'mcpywrap-core==' + version,
|
|
11
11
|
'pip>=23', 'build>=1.0', 'watchdog>=2.0.0', 'twine>=3.4.0', 'setuptools',
|
|
12
12
|
'jsonschema>=4.18,<5', 'psutil>=5.8.0', 'rich>=10.0.0',
|
|
13
|
+
"capstone>=5,<6; sys_platform == 'win32'",
|
|
14
|
+
"pefile>=2023; sys_platform == 'win32'",
|
|
13
15
|
"PySide6-Essentials>=6.6,<7; sys_platform == 'win32' or sys_platform == 'darwin'",
|
|
14
16
|
])
|
|
@@ -43,7 +43,7 @@ description: 使用 mcpywrap 管理《我的世界》中国版 Addon/地图、
|
|
|
43
43
|
|
|
44
44
|
- 世界保存进度,部署决定代码:`--new` 只创建世界;再次 run 若接管已有会话,不应用新代码,也不执行预处理。具体更新选择见本地生命周期页。
|
|
45
45
|
- 普通 build/dev 不写运行目录;运行资源由受控部署发布。同版多世界可以并行,有其他读者时不能单方面换版。
|
|
46
|
-
- 两名玩家使用组;输入/Python/截图选具体 session,组热更与停止选 group
|
|
46
|
+
- 两名玩家使用组;输入/Python/截图选具体 session,组热更与停止选 group。Windows 新版默认自动加入,`joining` 等待 worker 完成;手动模式的 `waiting_for_join` 需要点击局域网卡片。
|
|
47
47
|
- 项目自己的宿主任务用 scripts/hooks;依赖提供的资源处理用 consumer_prepare;`runtime py` 在游戏内执行。有效准备计划使用 `script list --effective --json`,还需检查 complete/ready,不能只看查询的 ok。
|
|
48
48
|
- 查询与 GUI 依赖刷新只读;关闭管理/调试窗口保留游戏。取消准备与停止已经启动的游戏是不同操作。
|
|
49
49
|
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## 首次连接
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
新版页面名为「开发」,旧版名为「开发工具」;使用同一套命令与协议。
|
|
8
|
+
|
|
9
|
+
1. 用户打开 mcpy App 并授权作品目录后,新版默认开启仅监听 `127.0.0.1` 的开发连接;已主动暂停或使用旧版时,在「开发」恢复连接。页面直接显示本机地址、配对码和有效期,可以复制配对码、标准命令,或可选的 AI 说明。普通终端无需 AI;模型账号只在使用 Agent 时需要。
|
|
8
10
|
2. 开发环境安装 Python、Git 和正式 `mcpywrap`。Minis 的 PRoot 使用 Alpine;优先使用其包管理器提供的 Python,再按标准 pip/uv 安装。解释器与虚拟环境放在该应用私有 Linux 目录,不能放在共享存储上。
|
|
9
11
|
3. 让用户将内部存储 `mcpy` 目录挂载为可读写目录。Minis 1.14 在设置 → Mount External Folders 中选择目录,并授予 Minis 自身的“所有文件访问”;仅有文件夹选择授权仍可能看不到其他 App 写入的文件。常见路径为 `/var/minis/mounts/mcpy`,以实际探针结果为准;不能把同名私有副本当作共享目录。
|
|
10
12
|
4. 完整安装技能并配对:
|
|
@@ -19,7 +21,9 @@ mcpy --project <挂载路径>/projects/<作品> host status --json
|
|
|
19
21
|
|
|
20
22
|
## 日常开发
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
**正常启动直接运行,用户继续留在开发工具中。** 配对后执行 `mcpy --project <作品目录> run --no-gui --detach --json`,App 负责游戏画中画。不要将 `android-open`、`am start`、`mcpy://service/start` 或切换 App 到前台当作启动前置步骤。`background_launch=unknown` 和历史失败都不代表本次必然失败;没有明确 blocker 时正常提交 run,以本次结果为准。恢复 URI 只是失败后的人工入口,出现在能力响应中不意味着需要调用。
|
|
25
|
+
|
|
26
|
+
工程命令绑定后继续使用 `init/mod/add/sync/build/package/run/status/logs/stop/runtime/screenshot` 标准命令,先查宿主能力;不加 `--local`。`modsdk` 补全库管理与 `skill` 安装属于开发端工具操作,不要求宿主在线,也不会作为工程降级入口。CLI 只负责输入、结果和宿主要求的工具准备,解析与组装必须在 App 的核心中执行。运行相关操作始终指定作品与会话。作品在开发环境中通过 `--project` 选择,App 的「开发」页不绑定作品。
|
|
23
27
|
|
|
24
28
|
客户端与 App 核心可能独立更新。需要执行准备阶段时,辅助代码必须与 App 的核心版本一致;按错误指引升级后重试,不修改配置绕过版本核对。Git/pip 安装、项目 hooks 和消费者准备在发起端执行,按 App 给出的阶段顺序交回结果,不能提前执行所有 hooks 或跳过 before_archive。
|
|
25
29
|
|
|
@@ -29,7 +33,7 @@ mcpy --project <挂载路径>/projects/<作品> host status --json
|
|
|
29
33
|
|
|
30
34
|
## 连接失败与恢复
|
|
31
35
|
|
|
32
|
-
- App 不可达:立即暂停自动操作,请用户手动打开 mcpy
|
|
36
|
+
- App 不可达:立即暂停自动操作,请用户手动打开 mcpy,确认开发连接已开启,恢复后即可返回原工具;也可向用户展示恢复链接 `mcpy://service/start`,不自行调用唤起命令。App 恢复此前已启用、尚未主动暂停的开发模式;否则显示连接页供用户开启。系统不允许唤起时请用户打开 App。仅凭连接失败不能判断进程退出、服务关闭还是端口错误;`host status` 的最后已知信息不是实时状态。不会改为本地核心执行。
|
|
33
37
|
- Git 环境:Minis 1.14 的部分 rootfs 带有无效的 `core.createObject=copy`。若 Git 原样报告 `invalid mode for object creation: copy`,在该 Linux 环境检查 `git config --show-origin --get core.createObject`,将报告位置的值修正为 Git 支持的 `rename`(系统配置可用 `git config --system core.createObject rename`);不要修改手机 App 或另一台电脑的 Git 配置。
|
|
34
38
|
- 配对恢复:新宿主声明 `pairing.resume=true`。CLI 将握手保存在私有配置目录,目录挂载修复或连接中断后重试同一条配对命令,会继续原请求;开始请求与验证回复丢失也不会重复授权。邀请有效一小时,发起请求及等待确认各有十五分钟窗口。明确过期或宿主身份变化时才重新生成配对码。
|
|
35
39
|
- 连接中断:`mcpy host status --recent --json` 离线查看本客户端最近的请求、操作 ID、最后观察到的状态及时间;`cached=true` 表示缓存。用 `mcpy host status --operation <id> --json` 查询原任务,或 `mcpy host resume --operation <id> --json` 继续等待并交回已完成的准备结果;不会新建操作。
|
|
@@ -50,7 +54,7 @@ mcpy --project <挂载路径>/projects/<作品> host status --json
|
|
|
50
54
|
|
|
51
55
|
日志可用 `--source all` 聚合 game/engine;持续日志分源维护游标。截图省略 `--output` 时自动保存到作品 `captures/`,返回 `image` 路径且不覆盖已有文件;加载期截图不证明模组已就绪。实例列表使用 `run --list --json`,不要从协议操作名 `instances.list` 推断不存在的 CLI 子命令。
|
|
52
56
|
|
|
53
|
-
补全库版本目录使用 `mcpy modsdk --list --offline --json` 只读缓存,`--refresh` 显式联网刷新,整体最多等待 12 秒。ModSDK
|
|
57
|
+
补全库版本目录使用 `mcpy modsdk --list --offline --json` 只读缓存,`--refresh` 显式联网刷新,整体最多等待 12 秒。ModSDK API 与开发知识使用独立的 https://github.com/EaseCation/netease-modsdk-skill ,按其 README 安装完整技能与查询资料;不加入 mcpy 或游戏包的依赖。
|
|
54
58
|
|
|
55
59
|
|
|
56
60
|
## 筛选日志与会话交接
|
|
@@ -76,13 +80,13 @@ App 的 `run/restart --launch-timeout <秒>` 配置进程及画中画的确认
|
|
|
76
80
|
|
|
77
81
|
`launch_preconditions.checks.background_launch.last_observation` 提供最近后台尝试的结果、时间、年龄和 `same_environment`。前台恢复成功不会覆盖先前后台失败;历史结果不能代替当前权限检查,`unknown` 仍表示无法判断当前权限。App 更新或系统环境变化后的历史只作参考。离线服务信息和最近操作记录提供查询时计算的 `age_ms`,不把缓存包装成实时状态。
|
|
78
82
|
|
|
79
|
-
mcpy 提供工程管理、会话控制和原始诊断证据;ModSDK API 语义、UI
|
|
83
|
+
mcpy 提供工程管理、会话控制和原始诊断证据;ModSDK API 语义、UI 文件规则及业务修复由独立 ModSDK skill 和开发者负责。不要根据“无报错”“画面上有文字”推断业务代码已生效,也不要为模组引入 mcpy 专属健康上报依赖。
|
|
80
84
|
|
|
81
85
|
|
|
82
86
|
需要用户接手的结果使用统一字段:`needs_user_action=true`、`next_action=ask_user`、`retryable=false`、`retry_policy=after_user_confirmation`、`user_message` 和 `agent_instruction`。宿主不可达时也适用,但不能据此声称准备已完成、游戏已启动或权限确实被拒绝。有 `operation_id` 时附带 `resume_command`;先等用户确认,再查询原任务,不重新提交有副作用操作。普通终端和 JSON 输出都包含该指引,即使 Agent 没有加载本 skill,也应能直接看到下一步。
|
|
83
87
|
|
|
84
88
|
|
|
85
|
-
后台连接容易暂停时,请用户在 mcpy
|
|
89
|
+
后台连接容易暂停时,请用户在 mcpy 的“开发”Tab 打开省电设置。小米系统选择“无限制”;该设置影响后台联网和 CPU,与“后台弹出界面”权限分开。只引导有外部工具连接需求的用户,不要求普通编辑或手动运行也授权。后台策略无法通过标准接口可靠核实时保持未知,不把打开过设置当作已经授权。Minis、Termux、编辑器与 HTTP 客户端使用相同的连接机制;AI 只是可选的操作方式。
|
|
86
90
|
|
|
87
91
|
|
|
88
92
|
宿主专用的 `--launch-timeout`、毫秒时间筛选及客户端/服务端日志来源,只在当前工作区已绑定宿主时显示在对应命令帮助中。此选择与运行 CLI 的操作系统无关:桌面也可以操作已绑定手机的工作区,手机中的普通本地工作区也不会自动变成宿主模式。`host status --binding-only --json` 只读当前工作区绑定,不访问宿主。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
用于 Windows 或 Apple Silicon macOS 的两个玩家联调。先检查本机 `doctor --capabilities` 和命令帮助是否提供 `--clients`、`--group`,不要仅凭 Skill 版本推断已安装 CLI 的能力。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
本地主客机需要 Windows 后端能力;`local-dual-client-auto` 表示可使用自动加入,旧版的 `local-dual-client-manual` 仅提供手动加入。外服双客户端按运行包的独立身份能力判断,不由本地主客机支持情况推断。
|
|
6
6
|
|
|
7
7
|
## 选择任务
|
|
8
8
|
|
|
@@ -37,13 +37,21 @@ mcpy --local --project <项目目录> logs --group <组ID> --source game --tail
|
|
|
37
37
|
|
|
38
38
|
`running` 只说明进程阶段。只有 `connection_verified=true` 才表示已确认两个不同玩家加入同一世界;这仍不证明玩法正确。按项目需求在两个客户端分别执行一次验收脚本,并在主机服务端核对玩家列表或权威状态。不要用两个窗口、两张 HUD 截图替代联机验证。
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
支持自动加入的 Windows 版本中,`run --clients 2` 默认返回 `joining`,worker 在后台完成加入和本地主机资源下载,再核验世界、网络身份与服务端玩家名单。等待 `status --group` 的 `connection_verified=true` 后再执行游戏调试。初始化阶段客机暂不接受其他 Python、热更或输入请求;不需要手动点击或抢占桌面焦点。
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
`run --clients 2 --join-mode manual` 保留原来的手动流程,返回 `waiting_for_join`。在客户端 2 选择“开始游戏”,点击指定名称的“局域网世界”卡片,按需下载资源;不要打开同名本地存档。首次菜单只有“开始”和“更多选项”时,选“更多选项”。旧联调组保留原来的加入方式,重启也继续沿用;切换方式先停止客机,再明确使用 `--join-mode`。
|
|
43
|
+
|
|
44
|
+
自动加入仅支持 Windows 本机。需要新版完整包的后台采集组件和 Windows 英文及游戏显示语言的 OCR 支持;当前菜单识别覆盖中文与英文。引擎窗口须持续渲染,最小化或菜单布局无法识别时不会回退前台操作。为初始化调试通道,每个隔离客机资料目录保留一个工具引导世界,随后自动退出并加入指定主机;它不是组件测试世界。
|
|
45
|
+
|
|
46
|
+
多个项目可以各自启动本地组。工具查询所属主机的实际 LAN 端口,并在本次客机内存中按指令结构定位和适配,不改磁盘游戏文件、不抢占其他进程端口。新引擎会自动尝试定位;零候选、多候选、端口归属不明确或读取校验失败都会明确拒绝,不使用旧版本地址猜测。不能保证任意未来引擎都兼容。`room_info.port`、`connect --port` 和 `serve --port` 都不是该监听端口的配置项。
|
|
47
|
+
|
|
48
|
+
失败时检查客机会话目录中的 `local-join.json`、`local-join-menu.json` 和游戏日志。主机仍保留,可修复后 `restart --session <客机>`;不要重放未知输入。手动模式不会自动适配多主机发现端口。
|
|
49
|
+
|
|
50
|
+
先查询平台声明的本地双端能力。Windows 提供自动加入和手动模式;macOS 尚未接入同等适配,会在启动前拒绝本地双客户端。外部服务器连接是独立能力,仍使用 `connect <host> --port <port>`;平台网络条件见[网络会话](network-sessions.md)。不要猜测本地联机端口或另开独立世界替代第二名玩家。
|
|
43
51
|
|
|
44
52
|
冷启动等待超时后,游戏可能仍在加载。先用 `status --group` 重新核验;如果两名玩家已经就绪,不必重开游戏。macOS 双客户端需要支持独立开发身份的原生运行包;旧运行包会明确拒绝此操作。
|
|
45
53
|
|
|
46
|
-
使用 `scripts/smoke.py --game --clients 2`
|
|
54
|
+
使用 `scripts/smoke.py --game --clients 2` 验收时会等待自动加入和玩家校验;旧工具或已有手动组仍需点击卡片。用 `--timeout` 留出初始化与资源下载时间,例如 600 秒。验收脚本结束会清理本次创建的游戏,日常保留会话使用普通 run/status 流程。
|
|
47
55
|
|
|
48
56
|
## 始终明确操作对象
|
|
49
57
|
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
|
|
32
32
|
| 错误或现象 | 下一步 |
|
|
33
33
|
|---|---|
|
|
34
|
+
| joining | Windows 自动加入中;等待 status --group 校验,不并行操作客机菜单或执行客机 Python |
|
|
35
|
+
| local_join_failed / local_join_timeout | 保留客机 local-join.json、local-join-menu.json 和日志;确认窗口仍渲染、OCR 语言及目标主机。修复后重启客机;需要手动方式时先停止客机,再 run --clients 2 --join-mode manual |
|
|
36
|
+
| lan_target_ambiguous / lan_adapter_unavailable | 端口或注入点无法唯一确认;保留现场,不猜 room_info.port 或套用另一版本地址 |
|
|
34
37
|
| waiting_for_join | 客机等待用户点击指定局域网卡片;不要重开第二个独立世界 |
|
|
35
38
|
| group_not_ready / 玩家未确认 | status --group 查成员与加入目标;有界等待,保留仍成功的主机 |
|
|
36
39
|
| group_required | 用 group 更新;单 session 不能绕过组协调 |
|
|
@@ -15,7 +15,7 @@ def verify_group(invoke, args, started, report):
|
|
|
15
15
|
raise ValueError(started.get('error') or '双客户端启动未完成')
|
|
16
16
|
current = invoke('status', '--group', group)
|
|
17
17
|
deadline = time.monotonic() + args.timeout
|
|
18
|
-
while current.get('state')
|
|
18
|
+
while current.get('state') in ('waiting_for_join', 'joining') and time.monotonic() < deadline:
|
|
19
19
|
time.sleep(.5)
|
|
20
20
|
current = invoke('status', '--group', group)
|
|
21
21
|
if not current.get('connection_verified') or len(current.get('members', [])) != 2:
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# 测试入口
|
|
2
2
|
|
|
3
|
+
Windows 本地自动联机可选实机验收:
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
python tests/manual_windows_local_groups.py --output test/lan-acceptance --engine-version 3.9.0.401155 --engine-version 3.10.0.436581
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
输出目录必须尚不存在;两次版本参数可相同。程序经公开 CLI 新建两个独立 Mod 项目、自动启动四端,验证每组世界/网络身份和服务端名单、实际客户端/服务端 Mod 系统加载、客机重启及另一组连接保留,最后停止自己创建的组。它需要完整 Windows 原生组件、对应引擎和可后台渲染的桌面;不进入默认 CI。`test_windows_lan.py` 使用人工指令夹具与模拟调用覆盖自动定位、歧义拒绝、写入回滚、菜单选择和取消;夹具不包含游戏二进制。明确未发送的只读探针争用可有限重试,未知执行结果不重放。
|
|
10
|
+
|
|
3
11
|
仓库通用开发与验证约定见 [CONTRIBUTING.md](../CONTRIBUTING.md)。默认自动化使用临时文件、真实有限子进程、模拟游戏调用端与回环服务,不启动真实游戏:
|
|
4
12
|
|
|
5
13
|
```text
|