diff --git a/CHANGELOG.md b/CHANGELOG.md index eba5fe8432..4338851dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,7 @@ **若更新后滑动操作没反应、自动战斗没反应、肉鸽不上人等,请进入 `设置` - `连接设置` - `强制替换 ADB`** -- 修复 使用中文路径时,minitouch 出现的一系列错误 @MistEO -- 修复 使用竖屏、旋转 180° 等时,minitouch 的操作错误 @MistEO -- 修复 POSIX 下 socket 超时时间过长的问题 @aa889788 -- 优化 自定义基建 内置 243 3 换作业 @Black1312 -- 新增 自定义基建 `description_post` 字段,整理内置作业格式 @MistEO -- 更新 文档 @DavidWang19 +- 新增 macOS 对 minitouch 的支持(但没完全支持 @horror-proton @hguandl +- 修复 minitouch 在部署竖屏模拟器上的操作错误 @MistEO +- 修复 模拟器断开重连后,minitouch 无法继续工作的问题 @MistEO +- 修复 界面 基建设置心情阈值显示错误的问题 @ABA2396 +- Fix a dead loop when recruiting opers in the auto-IS function of EN client @MistEO diff --git a/docs/ja-jp/1.2-よくある質問.md b/docs/ja-jp/1.2-よくある質問.md index f17905816c..cac57254cc 100644 --- a/docs/ja-jp/1.2-よくある質問.md +++ b/docs/ja-jp/1.2-よくある質問.md @@ -17,7 +17,7 @@ [NoAVX](../3rdparty/ppocr_noavx.zip)バージョンの `PaddleOCR` をダウンロードして解凍し、このアプリで同じ名前のファイルに置き換えてください。これは、`AVX` 命令セットをサポートしていないCPUのユーザー向けのパフォーマンス低下の代替手段であり、必要でない限り使用しないでください。 -_具体的には、[CPU-Z](https://www.cpuid.com/softwares/cpu-z.html)をダウンロードし、「命令セット」に `AVX` があるかどうかで判断できます。_ +_具体的には、[CPU-Z](https://www.cpuid.com/softwares/cpu-z.html) をダウンロードし、「命令セット」に `AVX` があるかどうかで判断できます。_ ### 可能性3:ほかのランタイムライブラリの問題 diff --git a/resource/global/YoStarEN/resource/tasks.json b/resource/global/YoStarEN/resource/tasks.json index 4af4ac75a1..3cb65ab38d 100644 --- a/resource/global/YoStarEN/resource/tasks.json +++ b/resource/global/YoStarEN/resource/tasks.json @@ -162,6 +162,9 @@ "Storage full" ] }, + "Phantom@Roguelike@StageEncounterOptionUnknown": { + "templThreshold": 0.9 + }, "BattleStageName": { "Doc": "该任务的 ocrReplace 被所有涉及Rouge-like的English识别任务复用", "ocrReplace": [ diff --git a/src/MeoAssistant/Controller.cpp b/src/MeoAssistant/Controller.cpp index 3a3129fde5..f11acb8569 100644 --- a/src/MeoAssistant/Controller.cpp +++ b/src/MeoAssistant/Controller.cpp @@ -7,7 +7,11 @@ #include #else #include +#include #include +#ifndef __APPLE__ +#include +#endif #include #include #endif @@ -412,6 +416,7 @@ std::optional asst::Controller::call_command(const std::string& cmd is_reconnect_success = reconnect_str.find("error") == std::string::npos; } if (is_reconnect_success) { + call_and_hup_minitouch(m_adb.call_minitouch); auto recall_ret = call_command(cmd, timeout, false /* 禁止重连避免无限递归 */, recv_by_socket); if (recall_ret) { // 重连并成功执行了 @@ -447,12 +452,20 @@ void asst::Controller::callback(AsstMsg msg, const json::value& details) bool asst::Controller::call_and_hup_minitouch(const std::string& cmd) { LogTraceFunction; + release_minitouch(true); + Log.info(cmd); m_minitouch_avaiable = false; constexpr int PipeReadBuffSize = 4096ULL; constexpr int PipeWriteBuffSize = 64 * 1024ULL; std::string pipe_str; + + auto check_timeout = [&](const auto& start_time) -> bool { + using namespace std::chrono_literals; + return std::chrono::steady_clock::now() - start_time < 3s; + }; + #ifdef _WIN32 SECURITY_ATTRIBUTES sa_attr_inherit { .nLength = sizeof(SECURITY_ATTRIBUTES), @@ -489,20 +502,17 @@ bool asst::Controller::call_and_hup_minitouch(const std::string& cmd) if (!create_ret) { DWORD err = GetLastError(); Log.error("Failed to create process for minitouch, err", err); + release_minitouch(true); return false; } auto start_time = std::chrono::steady_clock::now(); - auto check_timeout = [&]() -> bool { - using namespace std::chrono_literals; - return std::chrono::steady_clock::now() - start_time < 3s; - }; auto pipe_buffer = std::make_unique(PipeReadBuffSize); OVERLAPPED pipeov { .hEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr) }; std::ignore = ReadFile(pipe_parent_read, pipe_buffer.get(), PipeReadBuffSize, nullptr, &pipeov); - while (!need_exit() && check_timeout()) { + while (!need_exit() && check_timeout(start_time)) { if (pipe_str.find('$') != std::string::npos) { break; } @@ -518,12 +528,86 @@ bool asst::Controller::call_and_hup_minitouch(const std::string& cmd) pipe_parent_read = INVALID_HANDLE_VALUE; m_minitouch_parent_write = pipe_parent_write; -#else // !_WIN32 - // TODO - std::ignore = pipe_str; - std::ignore = PipeReadBuffSize; - std::ignore = PipeWriteBuffSize; - return false; +#else // !_WIN32 + + int pipe_to_child[2]; + int pipe_from_child[2]; + + if (::pipe(pipe_to_child)) return false; + if (::pipe(pipe_from_child)) { + ::close(pipe_to_child[0]); + ::close(pipe_to_child[1]); + return false; + } + + ::pid_t pid = fork(); + if (pid < 0) { + Log.error("failed to create process"); + return false; + } + if (pid == 0) { // child process + if (::dup2(pipe_to_child[0], STDIN_FILENO) < 0 || ::close(pipe_to_child[1]) < 0 || + ::close(pipe_from_child[0]) < 0 || ::dup2(pipe_from_child[1], STDOUT_FILENO) < 0 || + ::dup2(pipe_from_child[1], STDERR_FILENO) < 0) { + ::exit(-1); + } + + // set stdin of child to blocking + if (int val = ::fcntl(STDIN_FILENO, F_GETFL); val != -1 && (val & O_NONBLOCK)) { + val &= ~O_NONBLOCK; + ::fcntl(STDIN_FILENO, F_SETFL, val); + } + +#ifndef __APPLE__ + ::prctl(PR_SET_PDEATHSIG, SIGTERM); +#endif + + ::execlp("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); + exit(-1); + } + + m_minitouch_process = pid; + m_write_to_minitouch_fd = pipe_to_child[1]; + + if (::close(pipe_from_child[1]) < 0 || ::close(pipe_to_child[0]) < 0) { + release_minitouch(true); + return false; + } + + // set stdout to non blocking + if (int val = ::fcntl(pipe_from_child[0], F_GETFL); val != -1) { + val |= O_NONBLOCK; + ::fcntl(pipe_from_child[0], F_SETFL, val); + } + else { + release_minitouch(true); + return false; + } + const auto start_time = std::chrono::steady_clock::now(); + + while (true) { + if (need_exit()) { + release_minitouch(true); + return false; + } + if (!check_timeout(start_time)) { + Log.info("unable to find $ from pipe_str:", Logger::separator::newline, pipe_str); + release_minitouch(true); + return false; + } + + if (pipe_str.find('$') != std::string::npos) { + break; + } + + char buf_from_child[PipeReadBuffSize]; + ssize_t ret = ::read(pipe_from_child[0], buf_from_child, PipeReadBuffSize); + if (ret <= 0) continue; + pipe_str.insert(pipe_str.end(), buf_from_child, buf_from_child + ret); + } + + ::dup2(::open("/dev/null", O_WRONLY), pipe_from_child[0]); + #endif // _WIN32 Log.info("pipe str", Logger::separator::newline, pipe_str); @@ -538,13 +622,19 @@ bool asst::Controller::call_and_hup_minitouch(const std::string& cmd) } std::string key_info = pipe_str.substr(s_pos + 1, e_pos - s_pos - 1); Log.info("minitouch key props", key_info); + int size_1 = 0, size_2 = 0; std::stringstream ss; ss << key_info; ss >> m_minitouch_props.max_contacts; - ss >> m_minitouch_props.max_x; - ss >> m_minitouch_props.max_y; + ss >> size_1; + ss >> size_2; ss >> m_minitouch_props.max_pressure; + // 有些模拟器在竖屏分辨率时,这里的输出是反过来的 + // 考虑到应该没人竖屏玩明日方舟,所以取较大值为 x,较小值为 y + m_minitouch_props.max_x = std::max(size_1, size_2); + m_minitouch_props.max_y = std::min(size_1, size_2); + m_minitouch_props.x_scaling = static_cast(m_minitouch_props.max_x) / m_width; m_minitouch_props.y_scaling = static_cast(m_minitouch_props.max_y) / m_height; @@ -554,9 +644,15 @@ bool asst::Controller::call_and_hup_minitouch(const std::string& cmd) bool asst::Controller::input_to_minitouch(const std::string& cmd) { - // Log.info("Input to minitouch", Logger::separator::newline, cmd); +#ifdef ASST_DEBUG + Log.info("Input to minitouch", Logger::separator::newline, cmd); +#endif #ifdef _WIN32 + if (m_minitouch_parent_write == INVALID_HANDLE_VALUE) { + Log.error("minitouch write handle invalid", m_minitouch_parent_write); + return false; + } DWORD written = 0; if (!WriteFile(m_minitouch_parent_write, cmd.c_str(), static_cast(cmd.size() * sizeof(std::string::value_type)), &written, NULL)) { @@ -567,6 +663,9 @@ bool asst::Controller::input_to_minitouch(const std::string& cmd) return cmd.size() == written; #else + if (m_minitouch_process < 0 || m_write_to_minitouch_fd < 0) return false; + if (::write(m_write_to_minitouch_fd, cmd.c_str(), cmd.length()) >= 0) return true; + Log.error("Failed to write to minitouch, err", errno); return false; #endif } @@ -582,19 +681,28 @@ void asst::Controller::release_minitouch(bool force) #ifdef _WIN32 - if (m_minitouch_process_info.hProcess) { + if (m_minitouch_process_info.hProcess != INVALID_HANDLE_VALUE) { CloseHandle(m_minitouch_process_info.hProcess); - m_minitouch_process_info.hProcess = nullptr; + m_minitouch_process_info.hProcess = INVALID_HANDLE_VALUE; } - if (m_minitouch_process_info.hThread) { + if (m_minitouch_process_info.hThread != INVALID_HANDLE_VALUE) { CloseHandle(m_minitouch_process_info.hThread); - m_minitouch_process_info.hThread = nullptr; + m_minitouch_process_info.hThread = INVALID_HANDLE_VALUE; } - if (m_minitouch_parent_write) { + if (m_minitouch_parent_write != INVALID_HANDLE_VALUE) { CloseHandle(m_minitouch_parent_write); - m_minitouch_parent_write = nullptr; + m_minitouch_parent_write = INVALID_HANDLE_VALUE; + } +#else + if (m_write_to_minitouch_fd != -1) { + ::close(m_write_to_minitouch_fd); + m_write_to_minitouch_fd = -1; + } + if (m_minitouch_process > 0) { + ::kill(m_minitouch_process, SIGTERM); + if (force) ::kill(m_minitouch_process, SIGKILL); + m_minitouch_process = -1; } - #endif // _WIN32 } @@ -750,8 +858,8 @@ std::optional asst::Controller::init_socket(const std::string& l socklen_t addrlen = sizeof(m_server_sock_addr); int getname_ret = ::getsockname(m_server_sock, reinterpret_cast(&m_server_sock_addr), &addrlen); int listen_ret = ::listen(m_server_sock, 3); - struct timeval timeout = {6,0}; - int timeout_ret = ::setsockopt(m_server_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)); + struct timeval timeout = { 6, 0 }; + int timeout_ret = ::setsockopt(m_server_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(struct timeval)); server_start = bind_ret == 0 && getname_ret == 0 && listen_ret == 0 && timeout_ret == 0; #endif @@ -1186,7 +1294,6 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a { m_adb.connect = cmd_replace(adb_cfg.connect); auto connect_ret = call_command(m_adb.connect, 60LL * 1000, false /* adb 连接时不允许重试 */); - // 端口即使错误,命令仍然会返回0,TODO 对connect_result进行判断 bool is_connect_success = false; if (connect_ret) { auto& connect_str = connect_ret.value(); @@ -1419,7 +1526,8 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a call_command(minitouch_cmd_rep(adb_cfg.push_minitouch)); call_command(minitouch_cmd_rep(adb_cfg.chmod_minitouch)); - call_and_hup_minitouch(minitouch_cmd_rep(adb_cfg.call_minitouch)); + m_adb.call_minitouch = minitouch_cmd_rep(adb_cfg.call_minitouch); + call_and_hup_minitouch(m_adb.call_minitouch); std::string orientation_str = call_command(cmd_replace(adb_cfg.orientation)).value_or("0"); auto [beg, end] = ranges::remove_if(orientation_str, [](char ch) -> bool { return !std::isdigit(ch); }); diff --git a/src/MeoAssistant/Controller.h b/src/MeoAssistant/Controller.h index c4bdc4b88e..1043baf790 100644 --- a/src/MeoAssistant/Controller.h +++ b/src/MeoAssistant/Controller.h @@ -4,6 +4,7 @@ #include "Utils/Platform/SafeWindows.h" #include #else +#include #include #include #endif @@ -13,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -134,6 +134,7 @@ namespace asst { /* command */ std::string connect; + std::string call_minitouch; std::string click; std::string swipe; @@ -168,11 +169,13 @@ namespace asst bool m_minitouch_avaiable = false; // 状态 #ifdef _WIN32 - HANDLE m_minitouch_parent_write = nullptr; + HANDLE m_minitouch_parent_write = INVALID_HANDLE_VALUE; ASST_AUTO_DEDUCED_ZERO_INIT_START - PROCESS_INFORMATION m_minitouch_process_info = { nullptr }; + PROCESS_INFORMATION m_minitouch_process_info = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; ASST_AUTO_DEDUCED_ZERO_INIT_END #else + ::pid_t m_minitouch_process = -1; + int m_write_to_minitouch_fd = -1; // TODO #endif diff --git a/src/MeoAsstGui/ViewModels/SettingsViewModel.cs b/src/MeoAsstGui/ViewModels/SettingsViewModel.cs index db1bfdf62c..afbd63c5d1 100644 --- a/src/MeoAsstGui/ViewModels/SettingsViewModel.cs +++ b/src/MeoAsstGui/ViewModels/SettingsViewModel.cs @@ -536,8 +536,8 @@ namespace MeoAsstGui get => _dormThreshold; set { - DormThresholdLabel = Localization.GetString("DormThreshold") + ": " + _dormThreshold + "%"; SetAndNotify(ref _dormThreshold, value); + DormThresholdLabel = Localization.GetString("DormThreshold") + ": " + _dormThreshold + "%"; ViewStatusStorage.Set("Infrast.DormThreshold", value.ToString()); } }