mcpywrap-core 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.
Files changed (260) hide show
  1. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/PKG-INFO +1 -1
  2. mcpywrap_core-0.6.3/mcpywrap_core/_version.py +1 -0
  3. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/command_context.py +1 -1
  4. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/run_cmd.py +9 -3
  5. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/windows.py +49 -6
  6. mcpywrap_core-0.6.3/mcpywrap_core/mcstudio/lan_locator.py +94 -0
  7. mcpywrap_core-0.6.3/mcpywrap_core/mcstudio/lan_memory.py +142 -0
  8. mcpywrap_core-0.6.3/mcpywrap_core/mcstudio/lan_window.py +35 -0
  9. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_debug.py +7 -0
  10. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/sessions.py +2 -2
  11. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/client.py +2 -2
  12. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/intervention.py +2 -2
  13. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/groups.py +49 -9
  14. mcpywrap_core-0.6.3/mcpywrap_core/runtime/lan_menu.py +72 -0
  15. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/profiles.py +1 -1
  16. mcpywrap_core-0.6.3/mcpywrap_core/runtime/windows_join.py +193 -0
  17. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/SKILL.md +1 -1
  18. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/application-host.md +10 -6
  19. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/dual-clients.md +12 -4
  20. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/troubleshooting.md +3 -0
  21. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/scripts/smoke.py +1 -1
  22. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/project_ui.py +1 -1
  23. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/session_views.py +2 -0
  24. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core.egg-info/PKG-INFO +1 -1
  25. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core.egg-info/SOURCES.txt +5 -0
  26. mcpywrap_core-0.6.2/mcpywrap_core/_version.py +0 -1
  27. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/LICENSE +0 -0
  28. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/MANIFEST.in +0 -0
  29. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/README.md +0 -0
  30. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/THIRD_PARTY_NOTICES.md +0 -0
  31. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/__init__.py +0 -0
  32. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/__main__.py +0 -0
  33. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/_process_identity.py +0 -0
  34. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/_script_worker.py +0 -0
  35. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/api.py +0 -0
  36. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/AddonsPack.py +0 -0
  37. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/MapPack.py +0 -0
  38. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/__init__.py +0 -0
  39. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/assembly.py +0 -0
  40. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/dependency_manager.py +0 -0
  41. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/file_merge.py +0 -0
  42. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/project_builder.py +0 -0
  43. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/builders/watcher.py +0 -0
  44. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/cli.py +0 -0
  45. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/code_libraries.py +0 -0
  46. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/__init__.py +0 -0
  47. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/add_cmd.py +0 -0
  48. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/build_cmd.py +0 -0
  49. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/connect_cmd.py +0 -0
  50. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/default_cmd.py +0 -0
  51. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/dependency_prompt.py +0 -0
  52. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/deployment_cmd.py +0 -0
  53. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/deployment_output.py +0 -0
  54. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/desktop_cmd.py +0 -0
  55. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/dev_cmd.py +0 -0
  56. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/doctor_cmd.py +0 -0
  57. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/edit_cmd.py +0 -0
  58. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/engine_cmd.py +0 -0
  59. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/host_cmd.py +0 -0
  60. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/init_cmd.py +0 -0
  61. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/mod_cmd.py +0 -0
  62. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/modsdk_cmd.py +0 -0
  63. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/package_cmd.py +0 -0
  64. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/publish_cmd.py +0 -0
  65. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/record_cmd.py +0 -0
  66. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/remove_cmd.py +0 -0
  67. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/runtime_cmd.py +0 -0
  68. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/runtime_input_cmd.py +0 -0
  69. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/runtime_player_cmd.py +0 -0
  70. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/runtime_ui_cmd.py +0 -0
  71. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/script_cmd.py +0 -0
  72. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/serve_cmd.py +0 -0
  73. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/session_cmd.py +0 -0
  74. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/skill_cmd.py +0 -0
  75. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/sync_cmd.py +0 -0
  76. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/commands/ui_cmd.py +0 -0
  77. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/config.py +0 -0
  78. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/consumer_prepare.py +0 -0
  79. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/core-contract.json +0 -0
  80. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/custom_packaging.py +0 -0
  81. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/dependencies.py +0 -0
  82. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/__init__.py +0 -0
  83. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/android_icon.py +0 -0
  84. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/apk_profile.py +0 -0
  85. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/backend.py +0 -0
  86. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/host.py +0 -0
  87. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/install.py +0 -0
  88. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/macos.py +0 -0
  89. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/macos_icons.py +0 -0
  90. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/macos_network.py +0 -0
  91. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/official.py +0 -0
  92. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/preferences.py +0 -0
  93. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/engines/tui.py +0 -0
  94. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/framework_presets.py +0 -0
  95. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/frameworks.py +0 -0
  96. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/git_cache.py +0 -0
  97. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/git_projects.py +0 -0
  98. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/host.py +0 -0
  99. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/host_assets.py +0 -0
  100. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/input_executor.py +0 -0
  101. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/input_plan.py +0 -0
  102. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/SimpleMonitor.py +0 -0
  103. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/__init__.py +0 -0
  104. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/auth_interaction.py +0 -0
  105. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/bridge_assets.py +0 -0
  106. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/deployment_links.py +0 -0
  107. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/diagnostics.py +0 -0
  108. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/discovery.py +0 -0
  109. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/editor.py +0 -0
  110. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/file_logs.py +0 -0
  111. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/game.py +0 -0
  112. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/host_input.py +0 -0
  113. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/hot_reload.py +0 -0
  114. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/image.py +0 -0
  115. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/input_sequence.py +0 -0
  116. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/launcher_control.py +0 -0
  117. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/launcher_python.py +0 -0
  118. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/log_colorizer.py +0 -0
  119. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/log_protocol.py +0 -0
  120. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/log_query.py +0 -0
  121. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/log_stream.py +0 -0
  122. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/mcs.py +0 -0
  123. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/mcs_auth.py +0 -0
  124. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/network.py +0 -0
  125. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/private_logs.py +0 -0
  126. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/processes.py +0 -0
  127. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/recording_worker.py +0 -0
  128. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/recordings.py +0 -0
  129. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_cppconfig.py +0 -0
  130. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_input_payload.py +0 -0
  131. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_key_timeline_payload.py +0 -0
  132. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_player_payload.py +0 -0
  133. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_ui.py +0 -0
  134. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_ui_outline.py +0 -0
  135. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/runtime_ui_payload.py +0 -0
  136. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/screenshot.py +0 -0
  137. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/session_capabilities.py +0 -0
  138. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/session_worker.py +0 -0
  139. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/studio_server.py +0 -0
  140. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/studio_server_ui.py +0 -0
  141. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/symlink_helper_global.py +0 -0
  142. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/symlink_helper_map.py +0 -0
  143. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/symlinks.py +0 -0
  144. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/unified_input.py +0 -0
  145. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/window.py +0 -0
  146. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mcstudio/window_activation.py +0 -0
  147. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/__init__.py +0 -0
  148. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/addons.py +0 -0
  149. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/level_dat.py +0 -0
  150. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/map.py +0 -0
  151. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/modsdk_environment.py +0 -0
  152. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/modsdk_versions.py +0 -0
  153. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/netease_modsdk.py +0 -0
  154. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/template/generate_mod_files.py +0 -0
  155. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/minecraft/template/mod_template.py +0 -0
  156. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/__init__.py +0 -0
  157. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/options.py +0 -0
  158. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/preparation.py +0 -0
  159. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/profiles.py +0 -0
  160. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/receipts.py +0 -0
  161. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/routing.py +0 -0
  162. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/mobile_host/streams.py +0 -0
  163. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/operations/__init__.py +0 -0
  164. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/operations/build.py +0 -0
  165. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/operations/input_snapshot.py +0 -0
  166. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/operations/package.py +0 -0
  167. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/operations/project.py +0 -0
  168. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/operations/sync.py +0 -0
  169. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/prepare_environment.py +0 -0
  170. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/project_dependencies.py +0 -0
  171. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/project_init.py +0 -0
  172. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/project_scripts.py +0 -0
  173. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/recording_io.py +0 -0
  174. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/remote/__init__.py +0 -0
  175. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/remote/client.py +0 -0
  176. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/remote/http_server.py +0 -0
  177. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/remote/protocol.py +0 -0
  178. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/remote/service.py +0 -0
  179. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/__init__.py +0 -0
  180. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/deployment.py +0 -0
  181. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/deployment_inspection.py +0 -0
  182. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/deployment_service.py +0 -0
  183. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/diagnostics.py +0 -0
  184. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/instances.py +0 -0
  185. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/native.py +0 -0
  186. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/occupancy.py +0 -0
  187. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/preparation.py +0 -0
  188. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/storage.py +0 -0
  189. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/targets.py +0 -0
  190. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/watch.py +0 -0
  191. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/window_policy.py +0 -0
  192. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/runtime/windows.py +0 -0
  193. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/agents/openai.yaml +0 -0
  194. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/framework/behavior_pack/CatalogRuntime/__init__.py +0 -0
  195. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/framework/behavior_pack/manifest.json +0 -0
  196. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/framework/build_tools/example.toml +0 -0
  197. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/framework/build_tools/prepare.py +0 -0
  198. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/framework/build_tools/schema.json +0 -0
  199. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/framework/pyproject.toml +0 -0
  200. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/game/.gitignore +0 -0
  201. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/game/behavior_pack/manifest.json +0 -0
  202. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/game/data/items.json +0 -0
  203. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/game/pyproject.toml +0 -0
  204. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/assets/consumer-prepare/game/resource_pack/manifest.json +0 -0
  205. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/capture.md +0 -0
  206. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/consumer-prepare-author.md +0 -0
  207. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/consumer-prepare.md +0 -0
  208. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/input-sequence.md +0 -0
  209. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/input-writing.md +0 -0
  210. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/instance-world-settings.md +0 -0
  211. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/local-development.md +0 -0
  212. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/macos-local.md +0 -0
  213. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/network-sessions.md +0 -0
  214. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/project-dependencies.md +0 -0
  215. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/project-scripts.md +0 -0
  216. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/project-workflow.md +0 -0
  217. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/remote-testing.md +0 -0
  218. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/runtime-debug.md +0 -0
  219. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/runtime-input.md +0 -0
  220. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/runtime-key-timeline.md +0 -0
  221. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/runtime-player.md +0 -0
  222. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/runtime-ui.md +0 -0
  223. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/script-example.md +0 -0
  224. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/references/setup.md +0 -0
  225. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/scripts/bootstrap.ps1 +0 -0
  226. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/scripts/bootstrap.py +0 -0
  227. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/skill/scripts/game_window.py +0 -0
  228. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/source_files.py +0 -0
  229. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/timeline.py +0 -0
  230. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/__init__.py +0 -0
  231. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/application.py +0 -0
  232. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/assets/mcpy.icns +0 -0
  233. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/assets/mcpy.icon/Assets/01-cube.svg +0 -0
  234. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/assets/mcpy.icon/Assets/02-prompt.svg +0 -0
  235. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/assets/mcpy.icon/icon.json +0 -0
  236. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/assets/mcpy.png +0 -0
  237. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/engine_setup.py +0 -0
  238. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/log_view.py +0 -0
  239. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/log_window.py +0 -0
  240. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/run_session.py +0 -0
  241. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/session_controller.py +0 -0
  242. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/session_tools.py +0 -0
  243. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/ui/world_config.py +0 -0
  244. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/__init__.py +0 -0
  245. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/console.py +0 -0
  246. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/link_snapshot.py +0 -0
  247. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/locks.py +0 -0
  248. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/paths.py +0 -0
  249. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/pip_error_parser.py +0 -0
  250. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/print_guide.py +0 -0
  251. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/project_setup.py +0 -0
  252. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core/utils/utils.py +0 -0
  253. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core.egg-info/dependency_links.txt +0 -0
  254. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core.egg-info/requires.txt +0 -0
  255. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_core.egg-info/top_level.txt +0 -0
  256. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_host/__init__.py +0 -0
  257. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/mcpywrap_host/store.py +0 -0
  258. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/pyproject.toml +0 -0
  259. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/setup.cfg +0 -0
  260. {mcpywrap_core-0.6.2 → mcpywrap_core-0.6.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpywrap-core
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Shared mcpy project dependency, build and runtime logic
5
5
  Author-email: boybook <boybook@easecation.net>
6
6
  License-Expression: MIT
@@ -0,0 +1 @@
1
+ __version__ = '0.6.3'
@@ -88,7 +88,7 @@ def require_project():
88
88
 
89
89
  def restart_handoff(result, previous):
90
90
  if not isinstance(result,dict) or not previous: return result
91
- current=result.get('session')
91
+ current=result.get('replacement_session',result.get('session'))
92
92
  if current and current!=previous:
93
93
  result=dict(result,previous_session=previous,session_changed=True)
94
94
  message='会话已更换;后续日志、截图和调试请使用 --session '+current+'。旧会话记录仍保留。'
@@ -69,10 +69,12 @@ def take_world_options(arguments, cppconfig=None):
69
69
  @click.option('--force', '-f', is_flag=True, help='强制删除,不提示确认')
70
70
  @click.option('--clean-all', is_flag=True, help='清空所有游戏实例')
71
71
  @click.option('--clients', type=click.IntRange(1, 2), default=1, show_default=True,
72
- help='本机玩家客户端数量;本地双客户端需在客户端 2 手动点击目标局域网卡片加入')
72
+ help='本机玩家客户端数量;Windows 本地双客户端默认自动加入目标主机')
73
+ @click.option('--join-mode', type=click.Choice(['auto','manual']), default=None,
74
+ help='Windows 本地双客户端加入方式;auto 自动加入,manual 保留手动选择局域网卡片')
73
75
  @click.option('--launch-timeout', cls=HostOption, type=click.IntRange(5,120), default=None, help='App 宿主进程/画中画确认等待秒数;不含世界加载')
74
76
  @click.argument('instance_prefix', required=False)
75
- def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach, mcs_auth=False, cppconfig=None, clients=1, background=None, launch_timeout=None, **engine_overrides):
77
+ def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach, mcs_auth=False, cppconfig=None, clients=1, background=None, launch_timeout=None, join_mode=None, **engine_overrides):
76
78
  """游戏实例运行与管理
77
79
 
78
80
  可直接运行 'mcpy run' 启动最新实例,或使用选项管理实例。
@@ -89,6 +91,8 @@ def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach
89
91
  raise click.UsageError('run --json 需要 --detach')
90
92
  if clients != 1 and (list or delete or clean_all):
91
93
  raise click.UsageError('--clients 仅用于启动游戏')
94
+ if join_mode is not None and clients != 2:
95
+ raise click.UsageError('--join-mode 仅用于 --clients 2 的本地联机')
92
96
  world_config = take_world_options(engine_overrides, cppconfig)
93
97
  if world_config is not None and (not new or instance_prefix or list or delete or clean_all):
94
98
  raise click.UsageError('世界创建参数仅用于 --new,不覆盖已有实例')
@@ -121,13 +125,15 @@ def run_cmd(new, list, delete, force, clean_all, instance_prefix, no_gui, detach
121
125
  from ..mcstudio.network import configured_target
122
126
  target = configured_target(read_project(base_dir))
123
127
  if target:
128
+ if join_mode is not None:
129
+ raise click.UsageError('--join-mode 不用于外部服务器连接')
124
130
  if new or instance_prefix or world_config is not None:
125
131
  raise click.UsageError('网络联调不支持世界创建参数')
126
132
  if mcs_auth:
127
133
  raise click.UsageError('双客户端进服暂不支持双账号认证')
128
134
  return groups.start_network(base_dir, backend, target, engine_overrides,
129
135
  background=options['background'])
130
- return groups.start_local(base_dir, backend, options)
136
+ return groups.start_local(base_dir, backend, options, join_mode=join_mode)
131
137
  recovered = False
132
138
  try:
133
139
  try:
@@ -8,7 +8,7 @@ class WindowsBackend(GameBackend):
8
8
  capabilities = ('local-worlds', 'network-sessions', 'status', 'logs', 'stop', 'py',
9
9
  'runtime', 'runtime-ui', 'runtime-player', 'reload', 'watch',
10
10
  'screenshot', 'key', 'mouse', 'input-sequence', 'mcs-auth', 'serve', 'project-ui', 'editor',
11
- 'local-dual-client-manual')
11
+ 'local-dual-client-manual', 'local-dual-client-auto')
12
12
  setup_description = '请在 MC Studio 下载游戏引擎;使用 mcpy doctor 检查资源,可用 --engine-version 选择版本。'
13
13
 
14
14
  def debug_channel(self, data, write_log):
@@ -16,9 +16,25 @@ class WindowsBackend(GameBackend):
16
16
  self._world_ready = False
17
17
  self._ready_log_cursors = {}
18
18
  data['world_ready'] = False
19
- return SafaiaChannel(write_log, ready=lambda: self._world_ready)
19
+ channel = SafaiaChannel(write_log, ready=lambda: self._world_ready)
20
+ self._joiner = None
21
+ if (data.get('network') or {}).get('join') == 'auto':
22
+ from ..runtime.windows_join import AutoJoin
23
+ self._joiner = channel.local_join = AutoJoin(data, channel).start()
24
+ return channel
20
25
 
21
26
  def refresh(self, data, channel):
27
+ changed = self._refresh_world(data, channel)
28
+ joiner = getattr(self, '_joiner', None)
29
+ if joiner:
30
+ state = joiner.status()
31
+ ready = state['state'] == 'joined'
32
+ if data.get('local_join') != state or data.get('world_ready') != ready:
33
+ data.update(local_join=state, world_ready=ready)
34
+ changed = True
35
+ return changed
36
+
37
+ def _refresh_world(self, data, channel):
22
38
  if self._world_ready: return False
23
39
  from pathlib import Path
24
40
  import re
@@ -74,7 +90,7 @@ class WindowsBackend(GameBackend):
74
90
  def prepare_guest(self, host):
75
91
  return None
76
92
 
77
- def start_guest(self, host, *, project_dir, session_id, group, role, player, background=False):
93
+ def start_guest(self, host, *, project_dir, session_id, group, role, player, background=False, join_mode='manual'):
78
94
  from ..mcstudio import sessions
79
95
  from ..mcstudio.discovery import require_resources, Engine
80
96
  from ..runtime.deployment import Deployment
@@ -91,9 +107,15 @@ class WindowsBackend(GameBackend):
91
107
  version = record.get('fingerprint') if isinstance(record, dict) else None
92
108
  if not version:
93
109
  raise EngineError('无法确认主机加载的版本,请重启联调组。', 'deployment_unavailable')
110
+ lan_target = None
111
+ if join_mode == 'auto':
112
+ from ..mcstudio.lan_window import helper
113
+ from ..runtime.windows_join import target
114
+ helper()
115
+ lan_target = target(host)
94
116
  data = sessions.start(project_dir, session_id=session_id, group=group, role=role, background=background,
95
117
  network={'target': None, 'engine': engine, 'player': player,
96
- 'local_host': host['session'], 'join': 'manual',
118
+ 'local_host': host['session'], 'join': join_mode, 'lan_target': lan_target,
97
119
  'deployment_fingerprint': version})
98
120
  return sessions.handoff(data)
99
121
 
@@ -125,15 +147,36 @@ class WindowsBackend(GameBackend):
125
147
  sessions.require_launch_requested(data['project'], data['session'])
126
148
  data['phase'] = 'prepared'
127
149
  sessions.save(directory/'session.json', data)
128
- if data['network'].get('join') == 'manual':
150
+ if data['network'].get('join') in ('manual','auto'):
129
151
  from ..runtime.groups import is_local_guest
130
152
  from ..mcstudio.game import open_game
131
153
  if not is_local_guest(data['project'], data):
132
154
  raise ValueError('本地主机已退出或联调成员已变更,取消客机启动')
133
- process = open_game(None, engine=engine, logging_ip='127.0.0.1',
155
+ launch_config = None
156
+ if data['network']['join'] == 'auto':
157
+ from ..runtime.windows_join import bootstrap
158
+ launch_config = str(bootstrap(data, engine, directory))
159
+ sessions.save(directory/'session.json', data)
160
+ process = open_game(launch_config, engine=engine, logging_ip='127.0.0.1',
134
161
  logging_port=receiver.port, wait=False, use_system_color=False,
135
162
  capture_output=True, environment=environment,
136
163
  background=data.get('background', False))
164
+ if process and data['network']['join'] == 'auto':
165
+ from ..mcstudio.processes import identity
166
+ from ..mcstudio.lan_memory import adapt
167
+ data['game'] = identity(process.pid)
168
+ sessions.save(directory/'session.json', data)
169
+ try:
170
+ data['network']['lan_adapter'] = adapt(data['game'], data['network']['lan_target']['port'],
171
+ cancelled=lambda: (directory/'stop').exists())
172
+ except BaseException as error:
173
+ process.terminate()
174
+ process.wait(timeout=10)
175
+ if isinstance(error, Exception):
176
+ from .host import EngineError
177
+ raise EngineError('Windows LAN 自动适配未完成:'+str(error), 'lan_adapter_unavailable',
178
+ '保留会话日志;不会使用其他版本地址重试。可停止客机后选择 --join-mode manual。') from error
179
+ raise
137
180
  else:
138
181
  from ..mcstudio.network import launch_network, ServerTarget
139
182
  process = launch_network(engine, ServerTarget(**data['network']['target']),
@@ -0,0 +1,94 @@
1
+ """识别 NetherNet TransportConfiguration 的默认 LAN 端口,不保存版本地址表。"""
2
+ from bisect import bisect_right
3
+ from dataclasses import dataclass
4
+ import re
5
+ import struct
6
+
7
+
8
+ class LocatorError(ValueError):
9
+ pass
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class PortSite:
14
+ instruction_rva: int
15
+ operand_rva: int
16
+ function_rva: int
17
+ expected: bytes
18
+
19
+
20
+ def locate_port(image):
21
+ """image 提供 sections、functions 和 read(rva, size),可用于静态夹具和活动映像。
22
+
23
+ 从 Windows x64 展开表确定指令边界,再识别端口、广播周期及协议 magic
24
+ 的同一栈对象初始化。必须恰好找到一处;不把 CALL 位移或数据中的 7551 当指令。
25
+ """
26
+ from capstone import Cs, CS_ARCH_X86, CS_MODE_64
27
+ from capstone.x86_const import X86_OP_IMM, X86_OP_MEM, X86_OP_REG
28
+ decoder = Cs(CS_ARCH_X86, CS_MODE_64)
29
+ decoder.detail = True
30
+ functions = sorted(image.functions)
31
+ starts = [start for start, end in functions]
32
+ seen, candidates = set(), []
33
+ for rva, size, executable in image.sections:
34
+ if not executable:
35
+ continue
36
+ code = image.read(rva, size)
37
+ for match in re.finditer(re.escape(struct.pack('<I', 7551)), code):
38
+ position = rva + match.start()
39
+ index = bisect_right(starts, position) - 1
40
+ if index < 0:
41
+ continue
42
+ start, end = functions[index]
43
+ if not start <= position < end or end-start > 32768 or start in seen:
44
+ continue
45
+ seen.add(start)
46
+ instructions = list(decoder.disasm(image.read(start, end-start), start))
47
+ if not instructions or instructions[-1].address + instructions[-1].size != end:
48
+ continue
49
+ for i, instruction in enumerate(instructions[:-1]):
50
+ operands = instruction.operands
51
+ if (instruction.mnemonic != 'mov' or len(operands) != 2 or
52
+ operands[0].type != X86_OP_REG or operands[0].size != 4 or
53
+ operands[1].type != X86_OP_IMM or operands[1].imm != 7551 or
54
+ instruction.imm_size != 4):
55
+ continue
56
+ store = instructions[i+1]
57
+ if (store.mnemonic != 'mov' or len(store.operands) != 2 or
58
+ store.operands[0].type != X86_OP_MEM or store.operands[0].size != 2 or
59
+ store.operands[1].type != X86_OP_REG):
60
+ continue
61
+ # 本组配置的 port 在 magic 后 8 字节,interval 紧接 port 后 4 字节。
62
+ port = store.operands[0].mem
63
+ register = instruction.reg_name(operands[0].reg)
64
+ low_register = store.reg_name(store.operands[1].reg)
65
+ aliases = {'eax': 'ax', 'ebx': 'bx', 'ecx': 'cx', 'edx': 'dx',
66
+ 'esi': 'si', 'edi': 'di', **{'r%sd' % n: 'r%sw' % n for n in range(8,16)}}
67
+ if aliases.get(register) != low_register or store.reg_name(port.base) not in ('rsp','rbp') or port.index:
68
+ continue
69
+ interval, magic, passed = False, False, False
70
+ magic_register = None
71
+ for following in instructions[i+2:i+35]:
72
+ ops = following.operands
73
+ if following.mnemonic == 'mov' and len(ops) == 2:
74
+ if (ops[0].type == X86_OP_MEM and ops[0].mem.base == port.base and
75
+ ops[0].mem.disp == port.disp+4 and ops[1].type == X86_OP_IMM and ops[1].imm == 2000):
76
+ interval = True
77
+ if ops[0].type == X86_OP_REG and ops[1].type == X86_OP_IMM and ops[1].imm == 0xdeadbeef:
78
+ magic_register = following.reg_name(ops[0].reg)
79
+ if (ops[0].type == X86_OP_MEM and ops[0].mem.base == port.base and
80
+ ops[0].mem.disp == port.disp-8 and ops[1].type == X86_OP_REG and
81
+ magic_register and following.reg_name(ops[1].reg) in
82
+ (magic_register, magic_register[:-1] if magic_register.startswith('r') and magic_register.endswith('d') else 'r'+magic_register[1:])):
83
+ magic = True
84
+ if (following.mnemonic == 'lea' and len(ops) == 2 and
85
+ ops[0].type == X86_OP_REG and following.reg_name(ops[0].reg) == 'rcx' and
86
+ ops[1].type == X86_OP_MEM and ops[1].mem.base == port.base and
87
+ ops[1].mem.disp == port.disp-8):
88
+ passed = True
89
+ if interval and magic and passed:
90
+ candidates.append(PortSite(instruction.address, instruction.address+instruction.imm_offset,
91
+ start, bytes(instruction.bytes)))
92
+ if len(candidates) != 1:
93
+ raise LocatorError('无法唯一识别 NetherNet LAN 配置初始化(候选数:%s)。' % len(candidates))
94
+ return candidates[0]
@@ -0,0 +1,142 @@
1
+ """Windows 本地客机的进程内 LAN 默认值适配;不修改磁盘引擎。"""
2
+ from contextlib import contextmanager
3
+ import ctypes as C
4
+ from ctypes import wintypes as W
5
+ import struct
6
+ import time
7
+
8
+ from .processes import checked_process
9
+ from .lan_locator import LocatorError, locate_port
10
+
11
+
12
+ class ProcessImage:
13
+ def __init__(self, identity):
14
+ import pefile
15
+ self.identity = identity
16
+ if not checked_process(identity):
17
+ raise LocatorError('游戏进程已退出。')
18
+ self.kernel = C.WinDLL('kernel32', use_last_error=True)
19
+ self.psapi = C.WinDLL('psapi', use_last_error=True)
20
+ k = self.kernel
21
+ k.OpenProcess.argtypes = [W.DWORD, W.BOOL, W.DWORD]
22
+ k.OpenProcess.restype = W.HANDLE
23
+ k.CloseHandle.argtypes = [W.HANDLE]
24
+ k.ReadProcessMemory.argtypes = [W.HANDLE, C.c_void_p, C.c_void_p, C.c_size_t, C.POINTER(C.c_size_t)]
25
+ k.WriteProcessMemory.argtypes = k.ReadProcessMemory.argtypes
26
+ k.VirtualProtectEx.argtypes = [W.HANDLE, C.c_void_p, C.c_size_t, W.DWORD, C.POINTER(W.DWORD)]
27
+ k.FlushInstructionCache.argtypes = [W.HANDLE, C.c_void_p, C.c_size_t]
28
+ self.psapi.EnumProcessModulesEx.argtypes = [W.HANDLE, C.POINTER(C.c_void_p), W.DWORD, C.POINTER(W.DWORD), W.DWORD]
29
+ self.handle = k.OpenProcess(0x438 | 0x800, False, identity['pid'])
30
+ if not self.handle:
31
+ raise C.WinError(C.get_last_error())
32
+ try:
33
+ modules = (C.c_void_p * 1024)()
34
+ needed = W.DWORD()
35
+ if not self.psapi.EnumProcessModulesEx(self.handle, modules, C.sizeof(modules), C.byref(needed), 3):
36
+ raise C.WinError(C.get_last_error())
37
+ self.base = modules[0]
38
+ self.limit = 65536
39
+ pe = pefile.PE(data=self.read(0, 65536), fast_load=True)
40
+ if pe.FILE_HEADER.Machine != 0x8664 or not 65536 <= pe.OPTIONAL_HEADER.SizeOfImage <= 1024**3:
41
+ raise LocatorError('自动联机仅支持可识别的 Windows x64 引擎映像。')
42
+ self.limit = pe.OPTIONAL_HEADER.SizeOfImage
43
+ # 加壳入口和可写数据不是引擎代码候选;只取映像主体的可执行节。
44
+ self.sections = [(s.VirtualAddress, s.Misc_VirtualSize, bool(s.Characteristics & 0x20000000))
45
+ for s in pe.sections if s.Name.rstrip(b'\0') not in (b'.boot', b'.winlice', b'.reloc')]
46
+ directory = pe.OPTIONAL_HEADER.DATA_DIRECTORY[3]
47
+ if not directory.Size or directory.Size % 12 or directory.Size > 32*1024**2:
48
+ raise LocatorError('引擎缺少有效的 x64 展开表。')
49
+ self.functions = [(start, end) for start, end, unwind in struct.iter_unpack(
50
+ '<III', self.read(directory.VirtualAddress, directory.Size)) if 0 < start < end < self.limit]
51
+ if not self.functions:
52
+ raise LocatorError('引擎代码尚未解包或展开表不可识别。')
53
+ except BaseException:
54
+ self.close()
55
+ raise
56
+
57
+ def read(self, rva, size):
58
+ if rva < 0 or size < 0 or rva+size > self.limit:
59
+ raise LocatorError('引擎映像地址超出边界。')
60
+ buffer = C.create_string_buffer(size)
61
+ count = C.c_size_t()
62
+ if not self.kernel.ReadProcessMemory(self.handle, self.base+rva, buffer, size, C.byref(count)) or count.value != size:
63
+ raise LocatorError('引擎映像尚不可完整读取。')
64
+ return buffer.raw
65
+
66
+ @contextmanager
67
+ def suspended(self):
68
+ ntdll = C.WinDLL('ntdll')
69
+ ntdll.NtSuspendProcess.argtypes = [W.HANDLE]
70
+ ntdll.NtResumeProcess.argtypes = [W.HANDLE]
71
+ if not checked_process(self.identity) or ntdll.NtSuspendProcess(self.handle) < 0:
72
+ raise LocatorError('无法暂停本次游戏以原子更新 LAN 配置。')
73
+ try:
74
+ yield
75
+ finally:
76
+ if ntdll.NtResumeProcess(self.handle) < 0:
77
+ raise LocatorError('游戏恢复执行失败,需要停止本次客机。')
78
+
79
+ def patch(self, site, port):
80
+ if type(port) is not int or not 1 <= port <= 65535:
81
+ raise ValueError('无效的 LAN 端口')
82
+ with self.suspended():
83
+ if self.read(site.instruction_rva, len(site.expected)) != site.expected:
84
+ raise LocatorError('目标指令已变化,未写入。')
85
+ address = self.base+site.operand_rva
86
+ original = self.read(site.operand_rva, 4)
87
+ previous = W.DWORD()
88
+ if not self.kernel.VirtualProtectEx(self.handle, address, 4, 0x40, C.byref(previous)):
89
+ raise C.WinError(C.get_last_error())
90
+ try:
91
+ count = C.c_size_t()
92
+ replacement = struct.pack('<I', port)
93
+ if not self.kernel.WriteProcessMemory(self.handle, address, replacement, 4, C.byref(count)) or count.value != 4:
94
+ raise LocatorError('LAN 端口写入失败。')
95
+ self.kernel.FlushInstructionCache(self.handle, address, 4)
96
+ if self.read(site.operand_rva, 4) != replacement:
97
+ raise LocatorError('LAN 端口读回校验失败。')
98
+ except BaseException:
99
+ self.kernel.WriteProcessMemory(self.handle, address, original, 4, C.byref(count))
100
+ self.kernel.FlushInstructionCache(self.handle, address, 4)
101
+ raise
102
+ finally:
103
+ ignored = W.DWORD()
104
+ if not self.kernel.VirtualProtectEx(self.handle, address, 4, previous.value, C.byref(ignored)):
105
+ raise LocatorError('无法恢复引擎内存页属性。')
106
+
107
+ def close(self):
108
+ if self.handle:
109
+ self.kernel.CloseHandle(self.handle)
110
+ self.handle = None
111
+
112
+
113
+ def adapt(identity, port, cancelled=lambda: False, timeout=35):
114
+ """等待解包后按结构定位;失败由调用者结束自己刚创建的客机。"""
115
+ if port == 7551:
116
+ return {'strategy': 'engine-default', 'port': port, 'modified': False}
117
+ deadline = time.monotonic()+timeout
118
+ error = None
119
+ while time.monotonic() < deadline:
120
+ if cancelled() or not checked_process(identity):
121
+ raise LocatorError('客机启动已取消或进程退出。')
122
+ image = None
123
+ try:
124
+ image = ProcessImage(identity)
125
+ site = locate_port(image)
126
+ except (LocatorError, OSError) as exc:
127
+ error = exc
128
+ else:
129
+ # 一旦找到唯一候选,写入错误不重试,不允许重复变更未知状态。
130
+ try:
131
+ if cancelled():
132
+ raise LocatorError('客机启动已取消,未写入。')
133
+ image.patch(site, port)
134
+ return {'strategy': 'nethernet-configuration-v1', 'port': port, 'modified': True,
135
+ 'instruction_rva': site.instruction_rva, 'function_rva': site.function_rva}
136
+ finally:
137
+ image.close()
138
+ finally:
139
+ if image:
140
+ image.close()
141
+ time.sleep(.1)
142
+ raise LocatorError('自动定位 LAN 配置失败:' + str(error))
@@ -0,0 +1,35 @@
1
+ """后台采集菜单文字;不移动系统指针、不激活窗口。"""
2
+ import hashlib
3
+ import json
4
+ import subprocess
5
+
6
+ from .window import GameWindow
7
+
8
+
9
+ def helper():
10
+ from ..host_assets import asset_directory
11
+ root = asset_directory('window_capture')
12
+ path = root/'mcpy-window-capture.exe'
13
+ manifest = json.loads((root/'manifest.json').read_text(encoding='utf-8'))
14
+ if manifest.get('ocr_protocol') != 1 or hashlib.sha256(path.read_bytes()).hexdigest() != manifest.get('executable_sha256'):
15
+ raise ValueError('自动加入需要新版 Windows 菜单识别组件;请更新完整 mcpywrap,或使用 --join-mode manual。')
16
+ return path
17
+
18
+
19
+ def observe(game):
20
+ window = GameWindow(game)
21
+ try:
22
+ result = subprocess.run([str(helper()), '--hwnd',str(window.hwnd),'--ocr'],
23
+ stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,
24
+ creationflags=subprocess.CREATE_NO_WINDOW,timeout=12)
25
+ window.validate_process()
26
+ if result.returncode:
27
+ raise ValueError('菜单识别不可用:'+result.stderr.decode('utf-8','replace'))
28
+ if len(result.stdout)>512*1024:
29
+ raise ValueError('菜单识别结果过大')
30
+ data=json.loads(result.stdout)
31
+ if data.get('protocol') != 1:
32
+ raise ValueError('菜单识别协议不兼容')
33
+ return data
34
+ finally:
35
+ window.close()
@@ -310,6 +310,9 @@ class SafaiaChannel:
310
310
  self.serial.release()
311
311
 
312
312
  def close(self):
313
+ joiner = getattr(self, 'local_join', None)
314
+ if joiner:
315
+ joiner.stop.set()
313
316
  self.stop_event.set()
314
317
  with self.changed:
315
318
  if self.client:
@@ -321,6 +324,8 @@ class SafaiaChannel:
321
324
  for thread in (self.thread, self.discovery):
322
325
  if thread.is_alive():
323
326
  thread.join(timeout=1)
327
+ if joiner:
328
+ joiner.close()
324
329
 
325
330
 
326
331
  class _ControlHandler(socketserver.BaseRequestHandler):
@@ -345,6 +350,8 @@ class _ControlHandler(socketserver.BaseRequestHandler):
345
350
  group = membership(self.server.project, self.server.session)
346
351
  if not group or not group.get('connection_verified') or request.get('kind') != 'python':
347
352
  raise ValueError('客机尚未确认加入指定主机,或请求了未支持的资源热更')
353
+ if getattr(self.server.channel, 'joining', False) is True:
354
+ raise ValueError('客机正在自动加入,请等待连接校验完成后再执行调试操作。')
348
355
  if request.get('action') == 'execute':
349
356
  options = {'condition': request['condition']} if request.get('condition') is not None else {}
350
357
  if options and not hasattr(self.server.channel, 'submit'):
@@ -200,8 +200,8 @@ def handoff(data):
200
200
  '已有世界继续使用存档中的设置。')
201
201
  from ..engines.backend import get_backend
202
202
  result.update(get_backend(data.get('backend', 'windows')).handoff(data))
203
- if data.get('network') and data['network'].get('join') == 'manual':
204
- result.update(join='manual', local_host=data['network']['local_host'],
203
+ if data.get('network') and data['network'].get('join') in ('manual','auto'):
204
+ result.update(join=data['network']['join'], local_host=data['network']['local_host'],
205
205
  connection_verified=False, addons_assembled=False,
206
206
  engine_version=data['network']['engine']['version'])
207
207
  elif data.get('network'):
@@ -43,7 +43,7 @@ class Client:
43
43
  except (URLError, TimeoutError, OSError) as error:
44
44
  if isinstance(error, RemoteError): raise
45
45
  failure = RemoteError('无法连接 mcpy App;连接失败不足以区分服务关闭、进程退出或端口错误', 'host_unavailable',
46
- hint='打开 mcpy://service/start 恢复开发连接;主动暂停的服务需在开发工具页重新开启。已提交的操作先查询原任务。')
46
+ hint='请用户手动恢复开发连接(mcpy://service/start);不要自动调用唤起命令。主动暂停的服务需在“开发”页重新开启,已有操作先查询原任务。')
47
47
  failure.details = {'endpoint': self.endpoint, 'recovery_uri': 'mcpy://service/start',
48
48
  'last_known_service': profiles.cached_service(self.profile), 'live_state': 'unavailable'}
49
49
  require_user(failure)
@@ -216,7 +216,7 @@ def pair(url, code, workspace, label='mcpy CLI', timeout=900):
216
216
  saved['expires'] = time.time()+900
217
217
  if resumable: profiles.write_private(receipt, saved)
218
218
  import sys
219
- print('等待你允许连接:可直接点击 mcpy 弹出的确认卡或通知中的「允许连接」。没有提示时再打开 mcpy 的 开发工具页。', file=sys.stderr)
219
+ print('等待你允许连接:可直接点击 mcpy 弹出的确认卡或通知中的「允许连接」。没有提示时再打开 mcpy 的“开发”页。', file=sys.stderr)
220
220
  end = time.monotonic()+timeout
221
221
  while time.monotonic() < end:
222
222
  result = request(base+'/result', credentials)
@@ -13,14 +13,14 @@ def require_user(error, operation_id=None):
13
13
  if details.get('operation_state')!='waiting_user' and getattr(error,'code',None) not in USER_ACTION_CODES:
14
14
  return False
15
15
  offline=getattr(error,'code',None)=='host_unavailable'
16
- message=('请在手机上手动打开 mcpy,在“开发工具”页确认开发连接已开启,并暂时保持前台;完成后告诉我。若切换应用后又断连,请在该页打开省电设置,小米选择“无限制”。'
16
+ message=('请在手机上手动打开 mcpy,在“开发”页确认开发连接已开启,连接恢复后可返回当前工具,再告诉我。若切换应用后又断连,请在该页打开省电设置,小米选择“无限制”。'
17
17
  if offline else
18
18
  '请在手机上手动切回 mcpy,继续这次启动或恢复游戏画面;完成后告诉我。')
19
19
  if not offline:
20
20
  message+='如果希望以后直接从开发工具启动,请在系统应用权限中允许 mcpy 的“后台弹出界面”,并检查画中画权限;各品牌入口可能不同。'
21
21
  if getattr(error,'code',None)=='device_locked': message='请先解锁手机。'+message
22
22
  instruction=('立即停止自动尝试,把 user_message 告诉用户并等待用户明确确认。'
23
- '不要自动 run、restart、重新构建、新建世界、循环轮询或尝试其他唤起/权限绕过方式。'
23
+ '不要自动 run、restart、重新构建、新建世界、循环轮询或执行 android-open、am start、恢复 URI 或其他唤起/权限绕过方式。'
24
24
  '收到用户确认后,只查询或恢复原任务;不要把用户未回复当作已处理。')
25
25
  details.update(needs_user_action=True,next_action='ask_user',retryable=False,
26
26
  retry_policy='after_user_confirmation',user_message=message,agent_instruction=instruction)