diff --git a/src/MeoAssistant/Controller.cpp b/src/MeoAssistant/Controller.cpp index c5d5c36a38..a70f0d5c06 100644 --- a/src/MeoAssistant/Controller.cpp +++ b/src/MeoAssistant/Controller.cpp @@ -32,121 +32,121 @@ class WsaHelper { public: - ~WsaHelper() - { - WSACleanup(); - } - static WsaHelper& get_instance() - { - static WsaHelper instance; - return instance; - } - bool operator()() const noexcept { return m_supports; } + ~WsaHelper() + { + WSACleanup(); + } + static WsaHelper& get_instance() + { + static WsaHelper instance; + return instance; + } + bool operator()() const noexcept { return m_supports; } private: - WsaHelper() - { - m_supports = - WSAStartup(MAKEWORD(2, 2), &m_wsa_data) == 0 && - m_wsa_data.wVersion == MAKEWORD(2, 2); - } - WSADATA m_wsa_data = { 0 }; - bool m_supports = false; + WsaHelper() + { + m_supports = + WSAStartup(MAKEWORD(2, 2), &m_wsa_data) == 0 && + m_wsa_data.wVersion == MAKEWORD(2, 2); + } + WSADATA m_wsa_data = { 0 }; + bool m_supports = false; }; #endif asst::Controller::Controller(AsstCallback callback, void* callback_arg) - : m_callback(std::move(callback)), - m_callback_arg(callback_arg), - m_rand_engine(static_cast(time(nullptr))) + : m_callback(std::move(callback)), + m_callback_arg(callback_arg), + m_rand_engine(static_cast(time(nullptr))) { - LogTraceFunction; + LogTraceFunction; #ifdef _WIN32 - // 安全属性描述符 - m_pipe_sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES); - m_pipe_sec_attr.lpSecurityDescriptor = nullptr; - m_pipe_sec_attr.bInheritHandle = TRUE; + // 安全属性描述符 + m_pipe_sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES); + m_pipe_sec_attr.lpSecurityDescriptor = nullptr; + m_pipe_sec_attr.bInheritHandle = TRUE; - // 创建管道,本进程读-子进程写 - BOOL pipe_read_ret = ::CreatePipe(&m_pipe_read, &m_pipe_child_write, &m_pipe_sec_attr, PipeBuffSize); - // 创建管道,本进程写-子进程读 - BOOL pipe_write_ret = ::CreatePipe(&m_pipe_write, &m_pipe_child_read, &m_pipe_sec_attr, PipeBuffSize); + // 创建管道,本进程读-子进程写 + BOOL pipe_read_ret = ::CreatePipe(&m_pipe_read, &m_pipe_child_write, &m_pipe_sec_attr, PipeBuffSize); + // 创建管道,本进程写-子进程读 + BOOL pipe_write_ret = ::CreatePipe(&m_pipe_write, &m_pipe_child_read, &m_pipe_sec_attr, PipeBuffSize); - if (!pipe_read_ret || !pipe_write_ret) { - throw "controller pipe created failed"; - } + if (!pipe_read_ret || !pipe_write_ret) { + throw "controller pipe created failed"; + } - m_child_startup_info.cb = sizeof(STARTUPINFO); - m_child_startup_info.dwFlags = STARTF_USESTDHANDLES; - m_child_startup_info.wShowWindow = SW_HIDE; - // 重定向子进程的读写 - m_child_startup_info.hStdInput = m_pipe_child_read; - m_child_startup_info.hStdOutput = m_pipe_child_write; - m_child_startup_info.hStdError = m_pipe_child_write; + m_child_startup_info.cb = sizeof(STARTUPINFO); + m_child_startup_info.dwFlags = STARTF_USESTDHANDLES; + m_child_startup_info.wShowWindow = SW_HIDE; + // 重定向子进程的读写 + m_child_startup_info.hStdInput = m_pipe_child_read; + m_child_startup_info.hStdOutput = m_pipe_child_write; + m_child_startup_info.hStdError = m_pipe_child_write; - m_support_socket = false; - do { - if (!WsaHelper::get_instance()()) { - Log.error("WSA not supports"); - break; - } - m_support_socket = true; - } while (false); + m_support_socket = false; + do { + if (!WsaHelper::get_instance()()) { + Log.error("WSA not supports"); + break; + } + m_support_socket = true; + } while (false); #else - int pipe_in_ret = pipe(m_pipe_in); - int pipe_out_ret = pipe(m_pipe_out); - fcntl(m_pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK); + int pipe_in_ret = pipe(m_pipe_in); + int pipe_out_ret = pipe(m_pipe_out); + fcntl(m_pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK); - if (pipe_in_ret < 0 || pipe_out_ret < 0) { - throw "controller pipe created failed"; - } + if (pipe_in_ret < 0 || pipe_out_ret < 0) { + throw "controller pipe created failed"; + } - // todo - m_support_netcat = false; + // todo + m_support_netcat = false; #endif - m_pipe_buffer = std::make_unique(PipeBuffSize); - if (!m_pipe_buffer) { - throw "controller pipe buffer allocated failed"; - } - if (m_support_socket) { - m_socket_buffer = std::make_unique(SocketBuffSize); - if (!m_socket_buffer) { - throw "controller socket buffer allocated failed"; - } - } + m_pipe_buffer = std::make_unique(PipeBuffSize); + if (!m_pipe_buffer) { + throw "controller pipe buffer allocated failed"; + } + if (m_support_socket) { + m_socket_buffer = std::make_unique(SocketBuffSize); + if (!m_socket_buffer) { + throw "controller socket buffer allocated failed"; + } + } - m_cmd_thread = std::thread(&Controller::pipe_working_proc, this); + m_cmd_thread = std::thread(&Controller::pipe_working_proc, this); } asst::Controller::~Controller() { - LogTraceFunction; + LogTraceFunction; - m_thread_exit = true; - //m_thread_idle = true; - m_cmd_condvar.notify_all(); - m_completed_id = UINT_MAX; // make all WinMacor::wait to exit + m_thread_exit = true; + //m_thread_idle = true; + m_cmd_condvar.notify_all(); + m_completed_id = UINT_MAX; // make all WinMacor::wait to exit - if (m_cmd_thread.joinable()) { - m_cmd_thread.join(); - } + if (m_cmd_thread.joinable()) { + m_cmd_thread.join(); + } - if (--m_instance_count) { - release(); - } + if (--m_instance_count) { + release(); + } #ifdef _WIN32 - ::CloseHandle(m_pipe_read); - ::CloseHandle(m_pipe_write); - ::CloseHandle(m_pipe_child_read); - ::CloseHandle(m_pipe_child_write); + ::CloseHandle(m_pipe_read); + ::CloseHandle(m_pipe_write); + ::CloseHandle(m_pipe_child_read); + ::CloseHandle(m_pipe_child_write); #else - close(m_pipe_in[PIPE_READ]); - close(m_pipe_in[PIPE_WRITE]); - close(m_pipe_out[PIPE_READ]); - close(m_pipe_out[PIPE_WRITE]); + close(m_pipe_in[PIPE_READ]); + close(m_pipe_in[PIPE_WRITE]); + close(m_pipe_out[PIPE_READ]); + close(m_pipe_out[PIPE_WRITE]); #endif } @@ -205,1017 +205,1017 @@ asst::Controller::~Controller() std::pair asst::Controller::get_scale_size() const noexcept { - return m_scale_size; + return m_scale_size; } void asst::Controller::pipe_working_proc() { - LogTraceFunction; + LogTraceFunction; - while (!m_thread_exit) { - std::unique_lock cmd_queue_lock(m_cmd_queue_mutex); + while (!m_thread_exit) { + std::unique_lock cmd_queue_lock(m_cmd_queue_mutex); - if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务 - std::string cmd = m_cmd_queue.front(); - m_cmd_queue.pop(); - cmd_queue_lock.unlock(); - // todo 判断命令是否执行成功 - call_command(cmd); - ++m_completed_id; - } - //else if (!m_thread_idle) { // 队列中没有任务,又不是闲置的时候,就去截图 - // cmd_queue_lock.unlock(); - // auto start_time = std::chrono::steady_clock::now(); - // screencap(); - // cmd_queue_lock.lock(); - // if (!m_cmd_queue.empty()) { - // continue; - // } - // m_cmd_condvar.wait_until( - // cmd_queue_lock, - // start_time + std::chrono::milliseconds(1000)); // todo 时间写到配置文件里 - //} - else { - //m_cmd_condvar.wait(cmd_queue_lock, [&]() -> bool {return !m_thread_idle; }); - m_cmd_condvar.wait(cmd_queue_lock); - } - } + if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务 + std::string cmd = m_cmd_queue.front(); + m_cmd_queue.pop(); + cmd_queue_lock.unlock(); + // todo 判断命令是否执行成功 + call_command(cmd); + ++m_completed_id; + } + //else if (!m_thread_idle) { // 队列中没有任务,又不是闲置的时候,就去截图 + // cmd_queue_lock.unlock(); + // auto start_time = std::chrono::steady_clock::now(); + // screencap(); + // cmd_queue_lock.lock(); + // if (!m_cmd_queue.empty()) { + // continue; + // } + // m_cmd_condvar.wait_until( + // cmd_queue_lock, + // start_time + std::chrono::milliseconds(1000)); // todo 时间写到配置文件里 + //} + else { + //m_cmd_condvar.wait(cmd_queue_lock, [&]() -> bool {return !m_thread_idle; }); + m_cmd_condvar.wait(cmd_queue_lock); + } + } } std::optional> asst::Controller::call_command(const std::string& cmd, int64_t timeout, bool recv_by_socket) { - LogTraceScope(std::string(__FUNCTION__) + " | `" + cmd + "`"); + LogTraceScope(std::string(__FUNCTION__) + " | `" + cmd + "`"); - std::vector pipe_data; + std::vector pipe_data; - auto start_time = std::chrono::steady_clock::now(); - auto check_timeout = [&]() -> bool { - return timeout && - timeout < std::chrono::duration_cast( - std::chrono::steady_clock::now() - start_time) - .count(); - }; + auto start_time = std::chrono::steady_clock::now(); + auto check_timeout = [&]() -> bool { + return timeout && + timeout < std::chrono::duration_cast( + std::chrono::steady_clock::now() - start_time) + .count(); + }; #ifdef _WIN32 - PROCESS_INFORMATION process_info = { nullptr }; // 进程信息结构体 - BOOL create_ret = ::CreateProcessA(nullptr, const_cast(cmd.c_str()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &m_child_startup_info, &process_info); - if (!create_ret) { - Log.error("Call `", cmd, "` create process failed, ret", create_ret); - return std::nullopt; - } - if (!recv_by_socket) { - DWORD peek_num = 0; - DWORD read_num = 0; - std::unique_lock pipe_lock(m_pipe_mutex); - do { - //DWORD write_num = 0; - //WriteFile(parent_write, cmd.c_str(), cmd.size(), &write_num, nullptr); - while (::PeekNamedPipe(m_pipe_read, nullptr, 0, nullptr, &peek_num, nullptr) && peek_num > 0) { - if (::ReadFile(m_pipe_read, m_pipe_buffer.get(), PipeBuffSize, &read_num, nullptr)) { - pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); - } - } - } while (::WaitForSingleObject(process_info.hProcess, 0) == WAIT_TIMEOUT && !check_timeout()); - } - else { - std::unique_lock pipe_lock(m_pipe_mutex); - fd_set fdset = { 0 }; - FD_SET(m_server_sock, &fdset); - constexpr int TimeoutMilliseconds = 5000; - timeval select_timeout = { TimeoutMilliseconds / 1000, (TimeoutMilliseconds % 1000) * 1000 }; - select(static_cast(m_server_sock) + 1, &fdset, NULL, NULL, &select_timeout); - if (FD_ISSET(m_server_sock, &fdset)) { - SOCKET client_sock = ::accept(m_server_sock, NULL, NULL); - setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&TimeoutMilliseconds, sizeof(int)); - int recv_size = 0; - do { - recv_size = ::recv(client_sock, (char*)m_socket_buffer.get(), SocketBuffSize, NULL); - if (recv_size < 0) { - Log.error("recv error", recv_size); - break; - } - pipe_data.insert(pipe_data.end(), m_socket_buffer.get(), m_socket_buffer.get() + recv_size); - } while (recv_size > 0 && !check_timeout()); - ::closesocket(client_sock); - } - ::WaitForSingleObject(process_info.hProcess, TimeoutMilliseconds); - } - DWORD exit_ret = 0; - ::GetExitCodeProcess(process_info.hProcess, &exit_ret); - ::CloseHandle(process_info.hProcess); - ::CloseHandle(process_info.hThread); + PROCESS_INFORMATION process_info = { nullptr }; // 进程信息结构体 + BOOL create_ret = ::CreateProcessA(nullptr, const_cast(cmd.c_str()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &m_child_startup_info, &process_info); + if (!create_ret) { + Log.error("Call `", cmd, "` create process failed, ret", create_ret); + return std::nullopt; + } + if (!recv_by_socket) { + DWORD peek_num = 0; + DWORD read_num = 0; + std::unique_lock pipe_lock(m_pipe_mutex); + do { + //DWORD write_num = 0; + //WriteFile(parent_write, cmd.c_str(), cmd.size(), &write_num, nullptr); + while (::PeekNamedPipe(m_pipe_read, nullptr, 0, nullptr, &peek_num, nullptr) && peek_num > 0) { + if (::ReadFile(m_pipe_read, m_pipe_buffer.get(), PipeBuffSize, &read_num, nullptr)) { + pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); + } + } + } while (::WaitForSingleObject(process_info.hProcess, 0) == WAIT_TIMEOUT && !check_timeout()); + } + else { + std::unique_lock pipe_lock(m_pipe_mutex); + fd_set fdset = { 0 }; + FD_SET(m_server_sock, &fdset); + constexpr int TimeoutMilliseconds = 5000; + timeval select_timeout = { TimeoutMilliseconds / 1000, (TimeoutMilliseconds % 1000) * 1000 }; + select(static_cast(m_server_sock) + 1, &fdset, NULL, NULL, &select_timeout); + if (FD_ISSET(m_server_sock, &fdset)) { + SOCKET client_sock = ::accept(m_server_sock, NULL, NULL); + setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&TimeoutMilliseconds, sizeof(int)); + int recv_size = 0; + do { + recv_size = ::recv(client_sock, (char*)m_socket_buffer.get(), SocketBuffSize, NULL); + if (recv_size < 0) { + Log.error("recv error", recv_size); + break; + } + pipe_data.insert(pipe_data.end(), m_socket_buffer.get(), m_socket_buffer.get() + recv_size); + } while (recv_size > 0 && !check_timeout()); + ::closesocket(client_sock); + } + ::WaitForSingleObject(process_info.hProcess, TimeoutMilliseconds); + } + DWORD exit_ret = 0; + ::GetExitCodeProcess(process_info.hProcess, &exit_ret); + ::CloseHandle(process_info.hProcess); + ::CloseHandle(process_info.hThread); #else - int exit_ret = 0; - m_child = fork(); - if (m_child == 0) { - // child process - LogTraceScope("Child process: " + cmd); - std::cout << ("Child process: " + cmd) << std::endl; + int exit_ret = 0; + m_child = fork(); + if (m_child == 0) { + // child process + LogTraceScope("Child process: " + cmd); + std::cout << ("Child process: " + cmd) << std::endl; - dup2(m_pipe_in[PIPE_READ], STDIN_FILENO); - dup2(m_pipe_out[PIPE_WRITE], STDOUT_FILENO); - dup2(m_pipe_out[PIPE_WRITE], STDERR_FILENO); + dup2(m_pipe_in[PIPE_READ], STDIN_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDOUT_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDERR_FILENO); - // all these are for use by parent only - // close(m_pipe_in[PIPE_READ]); - // close(m_pipe_in[PIPE_WRITE]); - // close(m_pipe_out[PIPE_READ]); - // close(m_pipe_out[PIPE_WRITE]); + // all these are for use by parent only + // close(m_pipe_in[PIPE_READ]); + // close(m_pipe_in[PIPE_WRITE]); + // close(m_pipe_out[PIPE_READ]); + // close(m_pipe_out[PIPE_WRITE]); - exit_ret = execlp("sh", "sh", "-c", cmd.c_str(), nullptr); - exit(exit_ret); - } - else if (m_child > 0) { - // parent process - // LogTraceScope("Parent process: " + cmd); - std::unique_lock pipe_lock(m_pipe_mutex); - do { - ssize_t read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); + exit_ret = execlp("sh", "sh", "-c", cmd.c_str(), nullptr); + exit(exit_ret); + } + else if (m_child > 0) { + // parent process + // LogTraceScope("Parent process: " + cmd); + std::unique_lock pipe_lock(m_pipe_mutex); + do { + ssize_t read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); - while (read_num > 0) { - pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); - read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); - }; - } while (::waitpid(m_child, &exit_ret, WNOHANG) == 0 && !check_timeout()); - } - else { - // failed to create child process - return std::nullopt; - } + while (read_num > 0) { + pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); + read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); + }; + } while (::waitpid(m_child, &exit_ret, WNOHANG) == 0 && !check_timeout()); + } + else { + // failed to create child process + return std::nullopt; + } #endif - Log.trace("Call `", cmd, "` ret", exit_ret, ", output size:", pipe_data.size()); - if (!pipe_data.empty() && pipe_data.size() < 4096) { - Log.trace("output:", std::string(pipe_data.cbegin(), pipe_data.cend())); - } + Log.trace("Call `", cmd, "` ret", exit_ret, ", output size:", pipe_data.size()); + if (!pipe_data.empty() && pipe_data.size() < 4096) { + Log.trace("output:", std::string(pipe_data.cbegin(), pipe_data.cend())); + } - if (!exit_ret) { - return pipe_data; - } - else if (m_inited) { - // 之前可以运行,突然运行不了了,这种情况多半是 adb 炸了。所以重新连接一下 - m_inited = false; - std::this_thread::sleep_for(std::chrono::seconds(10)); - auto reconnect_ret = call_command(m_adb.connect, 60 * 1000); - bool is_reconnect_success = false; - if (reconnect_ret) { - auto& reconnect_val = reconnect_ret.value(); - std::string reconnect_str( - std::make_move_iterator(reconnect_val.begin()), - std::make_move_iterator(reconnect_val.end())); - is_reconnect_success = reconnect_str.find("error") == std::string::npos; - } - if (is_reconnect_success) { - auto recall_ret = call_command(cmd, timeout, recv_by_socket); - if (recall_ret) { - m_inited = true; - return recall_ret; - } - else { - json::value info = json::object{ - { "uuid", m_uuid}, - { "what", "CommandExecFailed" }, - { "why", "I dont fucking know, go look at the log!" }, - { "details", json::object { - { "cmd", cmd }, - { "recv_by_socket", recv_by_socket} - }} }; - m_inited = false; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - } - } - else { - json::value info = json::object{ - { "uuid", m_uuid}, - { "what", "Disconnect" }, - { "why", "Reconnect failed" }, - { "details", json::object { - { "cmd", m_adb.connect } - }} }; - m_inited = false; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - } - } - return std::nullopt; + if (!exit_ret) { + return pipe_data; + } + else if (m_inited) { + // 之前可以运行,突然运行不了了,这种情况多半是 adb 炸了。所以重新连接一下 + m_inited = false; + std::this_thread::sleep_for(std::chrono::seconds(10)); + auto reconnect_ret = call_command(m_adb.connect, 60 * 1000); + bool is_reconnect_success = false; + if (reconnect_ret) { + auto& reconnect_val = reconnect_ret.value(); + std::string reconnect_str( + std::make_move_iterator(reconnect_val.begin()), + std::make_move_iterator(reconnect_val.end())); + is_reconnect_success = reconnect_str.find("error") == std::string::npos; + } + if (is_reconnect_success) { + auto recall_ret = call_command(cmd, timeout, recv_by_socket); + if (recall_ret) { + m_inited = true; + return recall_ret; + } + else { + json::value info = json::object{ + { "uuid", m_uuid}, + { "what", "CommandExecFailed" }, + { "why", "I dont fucking know, go look at the log!" }, + { "details", json::object { + { "cmd", cmd }, + { "recv_by_socket", recv_by_socket} + }} }; + m_inited = false; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + } + } + else { + json::value info = json::object{ + { "uuid", m_uuid}, + { "what", "Disconnect" }, + { "why", "Reconnect failed" }, + { "details", json::object { + { "cmd", m_adb.connect } + }} }; + m_inited = false; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + } + } + return std::nullopt; } void asst::Controller::convert_lf(std::vector& data) { - LogTraceFunction; + LogTraceFunction; - if (data.empty() || data.size() < 2) { - return; - } - using Iter = std::vector::iterator; - auto pred = [](const Iter& cur) -> bool { - return *cur == '\r' && *(cur + 1) == '\n'; - }; - // find the first of "\r\n" - Iter first_iter = data.end(); - for (Iter iter = data.begin(); iter != data.end() - 1; ++iter) { - if (pred(iter)) { - first_iter = iter; - break; - } - } - if (first_iter == data.end()) { - return; - } - // move forward all non-crlf elements - Iter end_r1_iter = data.end() - 1; - Iter next_iter = first_iter; - while (++first_iter != end_r1_iter) { - if (!pred(first_iter)) { - *next_iter = *first_iter; - ++next_iter; - } - } - *next_iter = *end_r1_iter; - ++next_iter; - data.erase(next_iter, data.end()); + if (data.empty() || data.size() < 2) { + return; + } + using Iter = std::vector::iterator; + auto pred = [](const Iter& cur) -> bool { + return *cur == '\r' && *(cur + 1) == '\n'; + }; + // find the first of "\r\n" + Iter first_iter = data.end(); + for (Iter iter = data.begin(); iter != data.end() - 1; ++iter) { + if (pred(iter)) { + first_iter = iter; + break; + } + } + if (first_iter == data.end()) { + return; + } + // move forward all non-crlf elements + Iter end_r1_iter = data.end() - 1; + Iter next_iter = first_iter; + while (++first_iter != end_r1_iter) { + if (!pred(first_iter)) { + *next_iter = *first_iter; + ++next_iter; + } + } + *next_iter = *end_r1_iter; + ++next_iter; + data.erase(next_iter, data.end()); } asst::Point asst::Controller::rand_point_in_rect(const Rect& rect) { - int x = 0, y = 0; - if (rect.width == 0) { - x = rect.x; - } - else { - int x_rand = std::poisson_distribution(rect.width / 2)(m_rand_engine); + int x = 0, y = 0; + if (rect.width == 0) { + x = rect.x; + } + else { + int x_rand = std::poisson_distribution(rect.width / 2)(m_rand_engine); - x = x_rand + rect.x; - } + x = x_rand + rect.x; + } - if (rect.height == 0) { - y = rect.y; - } - else { - int y_rand = std::poisson_distribution(rect.height / 2)(m_rand_engine); - y = y_rand + rect.y; - } + if (rect.height == 0) { + y = rect.y; + } + else { + int y_rand = std::poisson_distribution(rect.height / 2)(m_rand_engine); + y = y_rand + rect.y; + } - return { x, y }; + return { x, y }; } void asst::Controller::random_delay() const { - auto& opt = Resrc.cfg().get_options(); - if (opt.control_delay_upper != 0) { - LogTraceFunction; - static std::default_random_engine rand_engine(static_cast(time(nullptr))); - static std::uniform_int_distribution rand_uni( - opt.control_delay_lower, - opt.control_delay_upper); + auto& opt = Resrc.cfg().get_options(); + if (opt.control_delay_upper != 0) { + LogTraceFunction; + static std::default_random_engine rand_engine(static_cast(time(nullptr))); + static std::uniform_int_distribution rand_uni( + opt.control_delay_lower, + opt.control_delay_upper); - unsigned rand_delay = rand_uni(rand_engine); + unsigned rand_delay = rand_uni(rand_engine); - Log.trace("random_delay |", rand_delay, "ms"); - std::this_thread::sleep_for(std::chrono::milliseconds(rand_delay)); - } + Log.trace("random_delay |", rand_delay, "ms"); + std::this_thread::sleep_for(std::chrono::milliseconds(rand_delay)); + } } void asst::Controller::clear_info() noexcept { - m_inited = false; - m_adb = decltype(m_adb)(); - m_uuid.clear(); - m_width = 0; - m_height = 0; - m_control_scale = 1.0; - m_scale_size = { WindowWidthDefault, WindowHeightDefault }; - if (m_server_sock) { - ::closesocket(m_server_sock); - m_server_sock = 0U; - } - m_server_started = false; - --m_instance_count; + m_inited = false; + m_adb = decltype(m_adb)(); + m_uuid.clear(); + m_width = 0; + m_height = 0; + m_control_scale = 1.0; + m_scale_size = { WindowWidthDefault, WindowHeightDefault }; + if (m_server_sock) { + ::closesocket(m_server_sock); + m_server_sock = 0U; + } + m_server_started = false; + --m_instance_count; } int asst::Controller::push_cmd(const std::string& cmd) { - random_delay(); + random_delay(); - std::unique_lock lock(m_cmd_queue_mutex); - m_cmd_queue.emplace(cmd); - m_cmd_condvar.notify_one(); - return ++m_push_id; + std::unique_lock lock(m_cmd_queue_mutex); + m_cmd_queue.emplace(cmd); + m_cmd_condvar.notify_one(); + return ++m_push_id; } std::optional asst::Controller::try_to_init_socket(const std::string& local_address, unsigned short try_port, unsigned short try_times) { - LogTraceFunction; + LogTraceFunction; #ifdef _WIN32 - m_server_sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_server_sock == INVALID_SOCKET) { - return std::nullopt; - } - m_server_addr.sin_family = PF_INET; - m_server_addr.sin_addr.s_addr = inet_addr(local_address.c_str()); + m_server_sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_server_sock == INVALID_SOCKET) { + return std::nullopt; + } + m_server_addr.sin_family = PF_INET; + m_server_addr.sin_addr.s_addr = inet_addr(local_address.c_str()); #else - // Linux, todo + // Linux, todo #endif - bool server_start = false; - u_short port_result = 0; + bool server_start = false; + u_short port_result = 0; - u_short max_port = try_port + try_times; - for (u_short port = try_port; port < max_port; ++port) { - Log.trace("try to bind port", port); + u_short max_port = try_port + try_times; + for (u_short port = try_port; port < max_port; ++port) { + Log.trace("try to bind port", port); #ifdef _WIN32 - m_server_addr.sin_port = htons(port); - int bind_ret = ::bind(m_server_sock, reinterpret_cast(&m_server_addr), sizeof(SOCKADDR)); - int listen_ret = ::listen(m_server_sock, 3); - server_start = bind_ret == 0 && listen_ret == 0; + m_server_addr.sin_port = htons(port); + int bind_ret = ::bind(m_server_sock, reinterpret_cast(&m_server_addr), sizeof(SOCKADDR)); + int listen_ret = ::listen(m_server_sock, 3); + server_start = bind_ret == 0 && listen_ret == 0; #else - // todo + // todo #endif - if (server_start) { - port_result = port; - break; - } - } + if (server_start) { + port_result = port; + break; + } + } - if (!server_start) { - Log.info("not supports netcat"); - return std::nullopt; - } + if (!server_start) { + Log.info("not supports netcat"); + return std::nullopt; + } - Log.info("server_start", local_address, port_result); - return port_result; + Log.info("server_start", local_address, port_result); + return port_result; } void asst::Controller::wait(unsigned id) const noexcept { - while (id > m_completed_id) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::this_thread::yield(); - } + while (id > m_completed_id) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::this_thread::yield(); + } } bool asst::Controller::screencap() { - LogTraceFunction; + LogTraceFunction; - //if (true) { - // m_inited = true; - // std::unique_lock image_lock(m_image_mutex); - // m_cache_image = cv::imread("err/1.png"); - // return true; - //} + //if (true) { + // m_inited = true; + // std::unique_lock image_lock(m_image_mutex); + // m_cache_image = cv::imread("err/1.png"); + // return true; + //} - DecodeFunc decode_raw = [&](std::vector& data) -> bool { - if (data.empty()) { - return false; - } - size_t std_size = 4ULL * m_width * m_height; - if (data.size() < std_size) { - return false; - } - size_t header_size = data.size() - std_size; - bool is_all_zero = std::all_of(data.data() + header_size, data.data() + std_size, - [](uchar uch) -> bool { - return uch == 0; - }); - if (is_all_zero) { - return false; - } - cv::Mat temp(m_height, m_width, CV_8UC4, data.data() + header_size); - if (temp.empty()) { - return false; - } - cv::cvtColor(temp, temp, cv::COLOR_RGB2BGR); - std::unique_lock image_lock(m_image_mutex); - m_cache_image = temp; - return true; - }; + DecodeFunc decode_raw = [&](std::vector& data) -> bool { + if (data.empty()) { + return false; + } + size_t std_size = 4ULL * m_width * m_height; + if (data.size() < std_size) { + return false; + } + size_t header_size = data.size() - std_size; + bool is_all_zero = std::all_of(data.data() + header_size, data.data() + std_size, + [](uchar uch) -> bool { + return uch == 0; + }); + if (is_all_zero) { + return false; + } + cv::Mat temp(m_height, m_width, CV_8UC4, data.data() + header_size); + if (temp.empty()) { + return false; + } + cv::cvtColor(temp, temp, cv::COLOR_RGB2BGR); + std::unique_lock image_lock(m_image_mutex); + m_cache_image = temp; + return true; + }; - DecodeFunc decode_raw_with_gzip = [&](std::vector& data) -> bool { - auto unzip_data = gzip::decompress(data.data(), data.size()); - if (unzip_data.empty()) { - return false; - } - size_t std_size = 4ULL * m_width * m_height; - if (unzip_data.size() < std_size) { - return false; - } - size_t header_size = unzip_data.size() - std_size; - bool is_all_zero = std::all_of(unzip_data.data() + header_size, unzip_data.data() + std_size, - [](uchar uch) -> bool { - return uch == 0; - }); - if (is_all_zero) { - return false; - } - cv::Mat temp(m_height, m_width, CV_8UC4, unzip_data.data() + header_size); - if (temp.empty()) { - return false; - } - cv::cvtColor(temp, temp, cv::COLOR_RGB2BGR); - std::unique_lock image_lock(m_image_mutex); - m_cache_image = temp; - return true; - }; + DecodeFunc decode_raw_with_gzip = [&](std::vector& data) -> bool { + auto unzip_data = gzip::decompress(data.data(), data.size()); + if (unzip_data.empty()) { + return false; + } + size_t std_size = 4ULL * m_width * m_height; + if (unzip_data.size() < std_size) { + return false; + } + size_t header_size = unzip_data.size() - std_size; + bool is_all_zero = std::all_of(unzip_data.data() + header_size, unzip_data.data() + std_size, + [](uchar uch) -> bool { + return uch == 0; + }); + if (is_all_zero) { + return false; + } + cv::Mat temp(m_height, m_width, CV_8UC4, unzip_data.data() + header_size); + if (temp.empty()) { + return false; + } + cv::cvtColor(temp, temp, cv::COLOR_RGB2BGR); + std::unique_lock image_lock(m_image_mutex); + m_cache_image = temp; + return true; + }; - DecodeFunc decode_encode = [&](std::vector& data) -> bool { - cv::Mat temp = cv::imdecode(data, cv::IMREAD_COLOR); - if (temp.empty()) { - return false; - } - std::unique_lock image_lock(m_image_mutex); - m_cache_image = temp; - return true; - }; + DecodeFunc decode_encode = [&](std::vector& data) -> bool { + cv::Mat temp = cv::imdecode(data, cv::IMREAD_COLOR); + if (temp.empty()) { + return false; + } + std::unique_lock image_lock(m_image_mutex); + m_cache_image = temp; + return true; + }; - switch (m_adb.screencap_method) { - case AdbProperty::ScreencapMethod::UnknownYet: - { - Log.info("Try to find the fastest way to screencap"); - m_inited = false; - auto min_cost = std::chrono::nanoseconds(MAXLONGLONG); + switch (m_adb.screencap_method) { + case AdbProperty::ScreencapMethod::UnknownYet: + { + Log.info("Try to find the fastest way to screencap"); + m_inited = false; + auto min_cost = std::chrono::nanoseconds(MAXLONGLONG); - auto start_time = std::chrono::high_resolution_clock::now(); - if (m_support_socket && m_server_started && - screencap(m_adb.screencap_raw_by_nc, decode_raw, true)) { - auto duration = std::chrono::high_resolution_clock::now() - start_time; - if (duration < min_cost) { - m_adb.screencap_method = AdbProperty::ScreencapMethod::RawByNc; - m_inited = true; - min_cost = duration; - } - Log.info("RawByNc cost", duration.count(), "ns"); - } - else { - Log.info("RawByNc is not supported"); - } - clear_lf_info(); + auto start_time = std::chrono::high_resolution_clock::now(); + if (m_support_socket && m_server_started && + screencap(m_adb.screencap_raw_by_nc, decode_raw, true)) { + auto duration = std::chrono::high_resolution_clock::now() - start_time; + if (duration < min_cost) { + m_adb.screencap_method = AdbProperty::ScreencapMethod::RawByNc; + m_inited = true; + min_cost = duration; + } + Log.info("RawByNc cost", duration.count(), "ns"); + } + else { + Log.info("RawByNc is not supported"); + } + clear_lf_info(); - start_time = std::chrono::high_resolution_clock::now(); - if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip)) { - auto duration = std::chrono::high_resolution_clock::now() - start_time; - if (duration < min_cost) { - m_adb.screencap_method = AdbProperty::ScreencapMethod::RawWithGzip; - m_inited = true; - min_cost = duration; - } - Log.info("RawWithGzip cost", duration.count(), "ns"); - } - else { - Log.info("RawWithGzip is not supported"); - } - clear_lf_info(); + start_time = std::chrono::high_resolution_clock::now(); + if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip)) { + auto duration = std::chrono::high_resolution_clock::now() - start_time; + if (duration < min_cost) { + m_adb.screencap_method = AdbProperty::ScreencapMethod::RawWithGzip; + m_inited = true; + min_cost = duration; + } + Log.info("RawWithGzip cost", duration.count(), "ns"); + } + else { + Log.info("RawWithGzip is not supported"); + } + clear_lf_info(); - start_time = std::chrono::high_resolution_clock::now(); - if (screencap(m_adb.screencap_encode, decode_encode)) { - auto duration = std::chrono::high_resolution_clock::now() - start_time; - if (duration < min_cost) { - m_adb.screencap_method = AdbProperty::ScreencapMethod::Encode; - m_inited = true; - min_cost = duration; - } - Log.info("Encode cost", duration.count(), "ns"); - } - else { - Log.info("Encode is not supported"); - } - Log.info("The fastest way is", static_cast(m_adb.screencap_method), ", cost:", min_cost.count(), "ns"); - clear_lf_info(); - return m_inited; - } - break; - case AdbProperty::ScreencapMethod::RawByNc: - { - return screencap(m_adb.screencap_raw_by_nc, decode_raw, true); - } - break; - case AdbProperty::ScreencapMethod::RawWithGzip: - { - return screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip); - } - break; - case AdbProperty::ScreencapMethod::Encode: - { - return screencap(m_adb.screencap_encode, decode_encode); - } - break; - } + start_time = std::chrono::high_resolution_clock::now(); + if (screencap(m_adb.screencap_encode, decode_encode)) { + auto duration = std::chrono::high_resolution_clock::now() - start_time; + if (duration < min_cost) { + m_adb.screencap_method = AdbProperty::ScreencapMethod::Encode; + m_inited = true; + min_cost = duration; + } + Log.info("Encode cost", duration.count(), "ns"); + } + else { + Log.info("Encode is not supported"); + } + Log.info("The fastest way is", static_cast(m_adb.screencap_method), ", cost:", min_cost.count(), "ns"); + clear_lf_info(); + return m_inited; + } + break; + case AdbProperty::ScreencapMethod::RawByNc: + { + return screencap(m_adb.screencap_raw_by_nc, decode_raw, true); + } + break; + case AdbProperty::ScreencapMethod::RawWithGzip: + { + return screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip); + } + break; + case AdbProperty::ScreencapMethod::Encode: + { + return screencap(m_adb.screencap_encode, decode_encode); + } + break; + } - return false; + return false; } bool asst::Controller::screencap(const std::string& cmd, const DecodeFunc& decode_func, bool by_socket) { - if ((!m_support_socket || !m_server_started) && by_socket) { - return false; - } + if ((!m_support_socket || !m_server_started) && by_socket) { + return false; + } - auto ret = call_command(cmd, 20000, by_socket); + auto ret = call_command(cmd, 20000, by_socket); - if (!ret || ret.value().empty()) { - Log.error("data is empty!"); - return false; - } - auto data = std::move(ret).value(); + if (!ret || ret.value().empty()) { + Log.error("data is empty!"); + return false; + } + auto data = std::move(ret).value(); - if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::CRLF) { - convert_lf(data); - } + if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::CRLF) { + convert_lf(data); + } - if (decode_func(data)) { - if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { - Log.info("screencap_end_of_line is LF"); - m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::LF; - } - return true; - } - else { - Log.info("data is not empty, but image is empty"); + if (decode_func(data)) { + if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { + Log.info("screencap_end_of_line is LF"); + m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::LF; + } + return true; + } + else { + Log.info("data is not empty, but image is empty"); - if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { - Log.info("try to cvt lf"); - convert_lf(data); + if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { + Log.info("try to cvt lf"); + convert_lf(data); - if (decode_func(data)) { - m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::CRLF; - Log.info("screencap_end_of_line is CRLF"); - return true; - } - else { - Log.error("convert lf and retry decode falied!"); - } - } - return false; - } + if (decode_func(data)) { + m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::CRLF; + Log.info("screencap_end_of_line is CRLF"); + return true; + } + else { + Log.error("convert lf and retry decode falied!"); + } + } + return false; + } } void asst::Controller::clear_lf_info() { - m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::UnknownYet; + m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::UnknownYet; } cv::Mat asst::Controller::get_resized_image() const { - const static cv::Size dsize(m_scale_size.first, m_scale_size.second); + const static cv::Size dsize(m_scale_size.first, m_scale_size.second); - std::shared_lock image_lock(m_image_mutex); - if (m_cache_image.empty()) { - Log.error("image is empty"); - return { dsize, CV_8UC3 }; - } - cv::Mat resized_mat; - cv::resize(m_cache_image, resized_mat, dsize, 0.0, 0.0, cv::INTER_AREA); - return resized_mat; + std::shared_lock image_lock(m_image_mutex); + if (m_cache_image.empty()) { + Log.error("image is empty"); + return { dsize, CV_8UC3 }; + } + cv::Mat resized_mat; + cv::resize(m_cache_image, resized_mat, dsize, 0.0, 0.0, cv::INTER_AREA); + return resized_mat; } std::optional asst::Controller::start_game(const std::string& client_type, bool block) { - if (client_type.empty()) { - return std::nullopt; - } - if (auto intent_name = Resrc.cfg().get_intent_name(client_type)) { - std::string cur_cmd = utils::string_replace_all(m_adb.start, "[Intent]", intent_name.value()); - int id = push_cmd(cur_cmd); - if (block) { - wait(id); - } - return id; - } - return std::nullopt; + if (client_type.empty()) { + return std::nullopt; + } + if (auto intent_name = Resrc.cfg().get_intent_name(client_type)) { + std::string cur_cmd = utils::string_replace_all(m_adb.start, "[Intent]", intent_name.value()); + int id = push_cmd(cur_cmd); + if (block) { + wait(id); + } + return id; + } + return std::nullopt; } int asst::Controller::click(const Point& p, bool block) { - int x = static_cast(p.x * m_control_scale); - int y = static_cast(p.y * m_control_scale); - //log.trace("Click, raw:", p.x, p.y, "corr:", x, y); + int x = static_cast(p.x * m_control_scale); + int y = static_cast(p.y * m_control_scale); + //log.trace("Click, raw:", p.x, p.y, "corr:", x, y); - return click_without_scale(Point(x, y), block); + return click_without_scale(Point(x, y), block); } int asst::Controller::click(const Rect& rect, bool block) { - return click(rand_point_in_rect(rect), block); + return click(rand_point_in_rect(rect), block); } int asst::Controller::click_without_scale(const Point& p, bool block) { - if (p.x < 0 || p.x >= m_width || p.y < 0 || p.y >= m_height) { - Log.error("click point out of range"); - } - std::string cur_cmd = utils::string_replace_all(m_adb.click, "[x]", std::to_string(p.x)); - cur_cmd = utils::string_replace_all(cur_cmd, "[y]", std::to_string(p.y)); - int id = push_cmd(cur_cmd); - if (block) { - wait(id); - } - return id; + if (p.x < 0 || p.x >= m_width || p.y < 0 || p.y >= m_height) { + Log.error("click point out of range"); + } + std::string cur_cmd = utils::string_replace_all(m_adb.click, "[x]", std::to_string(p.x)); + cur_cmd = utils::string_replace_all(cur_cmd, "[y]", std::to_string(p.y)); + int id = push_cmd(cur_cmd); + if (block) { + wait(id); + } + return id; } int asst::Controller::click_without_scale(const Rect& rect, bool block) { - return click_without_scale(rand_point_in_rect(rect), block); + return click_without_scale(rand_point_in_rect(rect), block); } int asst::Controller::swipe(const Point& p1, const Point& p2, int duration, bool block, int extra_delay, bool extra_swipe) { - int x1 = static_cast(p1.x * m_control_scale); - int y1 = static_cast(p1.y * m_control_scale); - int x2 = static_cast(p2.x * m_control_scale); - int y2 = static_cast(p2.y * m_control_scale); - //log.trace("Swipe, raw:", p1.x, p1.y, p2.x, p2.y, "corr:", x1, y1, x2, y2); + int x1 = static_cast(p1.x * m_control_scale); + int y1 = static_cast(p1.y * m_control_scale); + int x2 = static_cast(p2.x * m_control_scale); + int y2 = static_cast(p2.y * m_control_scale); + //log.trace("Swipe, raw:", p1.x, p1.y, p2.x, p2.y, "corr:", x1, y1, x2, y2); - return swipe_without_scale(Point(x1, y1), Point(x2, y2), duration, block, extra_delay, extra_swipe); + return swipe_without_scale(Point(x1, y1), Point(x2, y2), duration, block, extra_delay, extra_swipe); } int asst::Controller::swipe(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay, bool extra_swipe) { - return swipe(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); + return swipe(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); } int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool block, int extra_delay, bool extra_swipe) { - if (p1.x < 0 || p1.x >= m_width || p1.y < 0 || p1.y >= m_height || p2.x < 0 || p2.x >= m_width || p2.y < 0 || p2.y >= m_height) { - Log.error("swipe point out of range"); - } - std::string cur_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p1.x)); - cur_cmd = utils::string_replace_all(cur_cmd, "[y1]", std::to_string(p1.y)); - cur_cmd = utils::string_replace_all(cur_cmd, "[x2]", std::to_string(p2.x)); - cur_cmd = utils::string_replace_all(cur_cmd, "[y2]", std::to_string(p2.y)); - if (duration <= 0) { - cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", ""); - } - else { - cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", std::to_string(duration)); - } + if (p1.x < 0 || p1.x >= m_width || p1.y < 0 || p1.y >= m_height || p2.x < 0 || p2.x >= m_width || p2.y < 0 || p2.y >= m_height) { + Log.error("swipe point out of range"); + } + std::string cur_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p1.x)); + cur_cmd = utils::string_replace_all(cur_cmd, "[y1]", std::to_string(p1.y)); + cur_cmd = utils::string_replace_all(cur_cmd, "[x2]", std::to_string(p2.x)); + cur_cmd = utils::string_replace_all(cur_cmd, "[y2]", std::to_string(p2.y)); + if (duration <= 0) { + cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", ""); + } + else { + cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", std::to_string(duration)); + } - int id = 0; + int id = 0; - int extra_swipe_dist = Resrc.cfg().get_options().adb_extra_swipe_dist /* * m_control_scale*/; - int extra_swipe_duration = Resrc.cfg().get_options().adb_extra_swipe_duration; + int extra_swipe_dist = Resrc.cfg().get_options().adb_extra_swipe_dist /* * m_control_scale*/; + int extra_swipe_duration = Resrc.cfg().get_options().adb_extra_swipe_duration; - // 额外的滑动:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来 - if (extra_swipe && extra_swipe_duration >= 0) { - std::string extra_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p2.x)); - extra_cmd = utils::string_replace_all(extra_cmd, "[y1]", std::to_string(p2.y)); - int end_x = 0, end_y = 0; - if (p2.x != p1.x) { - double k = static_cast(p2.y - p1.y) / (p2.x - p1.x); - double temp = extra_swipe_dist / std::sqrt(1 + k * k); - end_x = p2.x + static_cast((p2.x > p1.x ? -1.0 : 1.0) * temp); - end_y = p2.y + static_cast((p2.y > p1.y ? -1.0 : 1.0) * std::fabs(k) * temp); - } - else { - end_x = p2.x; - end_y = p2.y + (p2.y > p1.y ? -1 : 1) * extra_swipe_dist; - } - extra_cmd = utils::string_replace_all(extra_cmd, "[x2]", std::to_string(end_x)); - extra_cmd = utils::string_replace_all(extra_cmd, "[y2]", std::to_string(end_y)); - extra_cmd = utils::string_replace_all(extra_cmd, "[duration]", std::to_string(extra_swipe_duration)); + // 额外的滑动:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来 + if (extra_swipe && extra_swipe_duration >= 0) { + std::string extra_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p2.x)); + extra_cmd = utils::string_replace_all(extra_cmd, "[y1]", std::to_string(p2.y)); + int end_x = 0, end_y = 0; + if (p2.x != p1.x) { + double k = static_cast(p2.y - p1.y) / (p2.x - p1.x); + double temp = extra_swipe_dist / std::sqrt(1 + k * k); + end_x = p2.x + static_cast((p2.x > p1.x ? -1.0 : 1.0) * temp); + end_y = p2.y + static_cast((p2.y > p1.y ? -1.0 : 1.0) * std::fabs(k) * temp); + } + else { + end_x = p2.x; + end_y = p2.y + (p2.y > p1.y ? -1 : 1) * extra_swipe_dist; + } + extra_cmd = utils::string_replace_all(extra_cmd, "[x2]", std::to_string(end_x)); + extra_cmd = utils::string_replace_all(extra_cmd, "[y2]", std::to_string(end_y)); + extra_cmd = utils::string_replace_all(extra_cmd, "[duration]", std::to_string(extra_swipe_duration)); - push_cmd(cur_cmd); - id = push_cmd(extra_cmd); - } - else { - id = push_cmd(cur_cmd); - } + push_cmd(cur_cmd); + id = push_cmd(extra_cmd); + } + else { + id = push_cmd(cur_cmd); + } - if (block) { - wait(id); - std::this_thread::sleep_for(std::chrono::milliseconds(extra_delay)); - } - return id; + if (block) { + wait(id); + std::this_thread::sleep_for(std::chrono::milliseconds(extra_delay)); + } + return id; } int asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay, bool extra_swipe) { - return swipe_without_scale(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); + return swipe_without_scale(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); } bool asst::Controller::connect(const std::string& adb_path, const std::string& address, const std::string& config) { - LogTraceFunction; + LogTraceFunction; - clear_info(); + clear_info(); #ifdef ASST_DEBUG - if (config == "DEBUG") { - m_inited = true; - return true; - } + if (config == "DEBUG") { + m_inited = true; + return true; + } #endif - auto get_info_json = [&]() -> json::value { - return json::object{ - { "uuid", m_uuid}, - { "details", json::object { - { "adb", adb_path }, - { "address", address }, - { "config", config } - }} - }; - }; + auto get_info_json = [&]() -> json::value { + return json::object{ + { "uuid", m_uuid}, + { "details", json::object { + { "adb", adb_path }, + { "address", address }, + { "config", config } + }} + }; + }; - auto adb_ret = Resrc.cfg().get_adb_cfg(config); - if (!adb_ret) { - json::value info = get_info_json() | - json::object{ - { "what", "ConnectFailed" }, - { "why", "ConfigNotFound" } }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } + auto adb_ret = Resrc.cfg().get_adb_cfg(config); + if (!adb_ret) { + json::value info = get_info_json() | + json::object{ + { "what", "ConnectFailed" }, + { "why", "ConfigNotFound" } }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } - const auto adb_cfg = std::move(adb_ret.value()); - std::string display_id; - std::string nc_address = "10.0.2.2"; - u_short nc_port = 0; + const auto adb_cfg = std::move(adb_ret.value()); + std::string display_id; + std::string nc_address = "10.0.2.2"; + u_short nc_port = 0; - auto cmd_replace = [&](const std::string& cfg_cmd) -> std::string { - const std::unordered_map replacements = { - { "[Adb]", adb_path }, - { "[AdbSerial]", address }, - { "[DisplayId]", display_id }, - { "[NcPort]", std::to_string(nc_port) }, - { "[NcAddress]", nc_address }, - }; - std::string formatted = cfg_cmd; - for (const auto& [key, value] : replacements) { - formatted = utils::string_replace_all(formatted, key, value); - } - return formatted; - }; + auto cmd_replace = [&](const std::string& cfg_cmd) -> std::string { + const std::unordered_map replacements = { + { "[Adb]", adb_path }, + { "[AdbSerial]", address }, + { "[DisplayId]", display_id }, + { "[NcPort]", std::to_string(nc_port) }, + { "[NcAddress]", nc_address }, + }; + std::string formatted = cfg_cmd; + for (const auto& [key, value] : replacements) { + formatted = utils::string_replace_all(formatted, key, value); + } + return formatted; + }; - /* connect */ - { - m_adb.connect = cmd_replace(adb_cfg.connect); - auto connect_ret = call_command(m_adb.connect, 60 * 1000); - // 端口即使错误,命令仍然会返回0,TODO 对connect_result进行判断 - bool is_connect_success = false; - if (connect_ret) { - auto& connect_val = connect_ret.value(); - std::string connect_str( - std::make_move_iterator(connect_val.begin()), - std::make_move_iterator(connect_val.end())); - is_connect_success = connect_str.find("error") == std::string::npos; - } - if (!is_connect_success) { - json::value info = get_info_json() | - json::object{ - { "what", "ConnectFailed" }, - { "why", "Connection command failed to exec" } - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } - } + /* connect */ + { + m_adb.connect = cmd_replace(adb_cfg.connect); + auto connect_ret = call_command(m_adb.connect, 60 * 1000); + // 端口即使错误,命令仍然会返回0,TODO 对connect_result进行判断 + bool is_connect_success = false; + if (connect_ret) { + auto& connect_val = connect_ret.value(); + std::string connect_str( + std::make_move_iterator(connect_val.begin()), + std::make_move_iterator(connect_val.end())); + is_connect_success = connect_str.find("error") == std::string::npos; + } + if (!is_connect_success) { + json::value info = get_info_json() | + json::object{ + { "what", "ConnectFailed" }, + { "why", "Connection command failed to exec" } + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } + } - /* get uuid (imei) */ - { - auto uuid_ret = call_command(cmd_replace(adb_cfg.uuid)); - if (!uuid_ret) { - json::value info = get_info_json() | - json::object{ - { "what", "ConnectFailed" }, - { "why", "Uuid command failed to exec" } - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } + /* get uuid (imei) */ + { + auto uuid_ret = call_command(cmd_replace(adb_cfg.uuid)); + if (!uuid_ret) { + json::value info = get_info_json() | + json::object{ + { "what", "ConnectFailed" }, + { "why", "Uuid command failed to exec" } + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } - auto& uuid_result = uuid_ret.value(); - std::string uuid_str( - std::make_move_iterator(uuid_result.begin()), - std::make_move_iterator(uuid_result.end())); - uuid_str.erase(std::remove(uuid_str.begin(), uuid_str.end(), ' '), uuid_str.end()); - m_uuid = std::move(uuid_str); + auto& uuid_result = uuid_ret.value(); + std::string uuid_str( + std::make_move_iterator(uuid_result.begin()), + std::make_move_iterator(uuid_result.end())); + uuid_str.erase(std::remove(uuid_str.begin(), uuid_str.end(), ' '), uuid_str.end()); + m_uuid = std::move(uuid_str); - json::value info = get_info_json() | - json::object{ - { "what", "UuidGetted" }, - { "why", "" } - }; - info["details"]["uuid"] = m_uuid; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - } + json::value info = get_info_json() | + json::object{ + { "what", "UuidGetted" }, + { "why", "" } + }; + info["details"]["uuid"] = m_uuid; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + } - // 按需获取display ID 信息 - if (!adb_cfg.display_id.empty()) { - auto display_id_ret = call_command(cmd_replace(adb_cfg.display_id)); - if (!display_id_ret) { - return false; - } + // 按需获取display ID 信息 + if (!adb_cfg.display_id.empty()) { + auto display_id_ret = call_command(cmd_replace(adb_cfg.display_id)); + if (!display_id_ret) { + return false; + } - auto& display_id_result = display_id_ret.value(); - convert_lf(display_id_result); - std::string display_id_pipe_str( - std::make_move_iterator(display_id_result.begin()), - std::make_move_iterator(display_id_result.end())); - auto last = display_id_pipe_str.rfind(':'); - if (last == std::string::npos) { - return false; - } + auto& display_id_result = display_id_ret.value(); + convert_lf(display_id_result); + std::string display_id_pipe_str( + std::make_move_iterator(display_id_result.begin()), + std::make_move_iterator(display_id_result.end())); + auto last = display_id_pipe_str.rfind(':'); + if (last == std::string::npos) { + return false; + } - display_id = display_id_pipe_str.substr(last + 1); - // 去掉换行 - display_id.pop_back(); - } + display_id = display_id_pipe_str.substr(last + 1); + // 去掉换行 + display_id.pop_back(); + } - /* display */ - { - auto display_ret = call_command(cmd_replace(adb_cfg.display)); - if (!display_ret) { - json::value info = get_info_json() | - json::object{ - { "what", "ConnectFailed" }, - { "why", "Display command failed to exec" } - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } + /* display */ + { + auto display_ret = call_command(cmd_replace(adb_cfg.display)); + if (!display_ret) { + json::value info = get_info_json() | + json::object{ + { "what", "ConnectFailed" }, + { "why", "Display command failed to exec" } + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } - auto& display_result = display_ret.value(); - std::string display_pipe_str( - std::make_move_iterator(display_result.begin()), - std::make_move_iterator(display_result.end())); - int size_value1 = 0; - int size_value2 = 0; + auto& display_result = display_ret.value(); + std::string display_pipe_str( + std::make_move_iterator(display_result.begin()), + std::make_move_iterator(display_result.end())); + int size_value1 = 0; + int size_value2 = 0; #ifdef _MSC_VER - sscanf_s(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); + sscanf_s(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); #else - sscanf(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); + sscanf(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); #endif - // 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高 - // 总不能有人竖屏玩明日方舟吧(? - m_width = (std::max)(size_value1, size_value2); - m_height = (std::min)(size_value1, size_value2); + // 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高 + // 总不能有人竖屏玩明日方舟吧(? + m_width = (std::max)(size_value1, size_value2); + m_height = (std::min)(size_value1, size_value2); - json::value info = get_info_json() | - json::object{ - { "what", "ResolutionGetted" }, - { "why", "" } - }; + json::value info = get_info_json() | + json::object{ + { "what", "ResolutionGetted" }, + { "why", "" } + }; - info["details"] |= json::object{ - { "width", m_width }, - { "height", m_height } - }; + info["details"] |= json::object{ + { "width", m_width }, + { "height", m_height } + }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - if (m_width == 0 || m_height == 0) { - info["what"] = "ResolutionError"; - info["why"] = "Get resolution failed"; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } - else if (m_width < WindowWidthDefault || m_height < WindowHeightDefault) { - info["what"] = "UnsupportedResolution"; - info["why"] = "Low screen resolution"; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } - } + if (m_width == 0 || m_height == 0) { + info["what"] = "ResolutionError"; + info["why"] = "Get resolution failed"; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } + else if (m_width < WindowWidthDefault || m_height < WindowHeightDefault) { + info["what"] = "UnsupportedResolution"; + info["why"] = "Low screen resolution"; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } + } - /* calc ratio */ - { - constexpr double DefaultRatio = - static_cast(WindowWidthDefault) / static_cast(WindowHeightDefault); - double cur_ratio = static_cast(m_width) / static_cast(m_height); + /* calc ratio */ + { + constexpr double DefaultRatio = + static_cast(WindowWidthDefault) / static_cast(WindowHeightDefault); + double cur_ratio = static_cast(m_width) / static_cast(m_height); - if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放 - || std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) { - int scale_width = static_cast(cur_ratio * WindowHeightDefault); - m_scale_size = std::make_pair(scale_width, WindowHeightDefault); - m_control_scale = static_cast(m_height) / static_cast(WindowHeightDefault); - } - else { // 否则可能是偏正方形的屏幕,按宽度计算 - int scale_height = static_cast(WindowWidthDefault / cur_ratio); - m_scale_size = std::make_pair(WindowWidthDefault, scale_height); - m_control_scale = static_cast(m_width) / static_cast(WindowWidthDefault); - } - } + if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放 + || std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) { + int scale_width = static_cast(cur_ratio * WindowHeightDefault); + m_scale_size = std::make_pair(scale_width, WindowHeightDefault); + m_control_scale = static_cast(m_height) / static_cast(WindowHeightDefault); + } + else { // 否则可能是偏正方形的屏幕,按宽度计算 + int scale_height = static_cast(WindowWidthDefault / cur_ratio); + m_scale_size = std::make_pair(WindowWidthDefault, scale_height); + m_control_scale = static_cast(m_width) / static_cast(WindowWidthDefault); + } + } - { - json::value info = get_info_json() | - json::object{ - { "what", "Connected" }, - { "why", "" } - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - } + { + json::value info = get_info_json() | + json::object{ + { "what", "Connected" }, + { "why", "" } + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + } - m_adb.click = cmd_replace(adb_cfg.click); - m_adb.swipe = cmd_replace(adb_cfg.swipe); - m_adb.screencap_raw_with_gzip = cmd_replace(adb_cfg.screencap_raw_with_gzip); - m_adb.screencap_encode = cmd_replace(adb_cfg.screencap_encode); - m_adb.release = cmd_replace(adb_cfg.release); - m_adb.start = cmd_replace(adb_cfg.start); + m_adb.click = cmd_replace(adb_cfg.click); + m_adb.swipe = cmd_replace(adb_cfg.swipe); + m_adb.screencap_raw_with_gzip = cmd_replace(adb_cfg.screencap_raw_with_gzip); + m_adb.screencap_encode = cmd_replace(adb_cfg.screencap_encode); + m_adb.release = cmd_replace(adb_cfg.release); + m_adb.start = cmd_replace(adb_cfg.start); - if (m_support_socket && !m_server_started) { - std::string bind_address; - if (size_t pos = address.rfind(':'); - pos != std::string::npos) { - bind_address = address.substr(0, pos); - } - else { - bind_address = "127.0.0.1"; - } + if (m_support_socket && !m_server_started) { + std::string bind_address; + if (size_t pos = address.rfind(':'); + pos != std::string::npos) { + bind_address = address.substr(0, pos); + } + else { + bind_address = "127.0.0.1"; + } - // Todo: detect remote address, and check remote port - // reference from https://github.com/ArknightsAutoHelper/ArknightsAutoHelper/blob/master/automator/connector/ADBConnector.py#L436 - auto nc_address_ret = call_command(cmd_replace(adb_cfg.nc_address)); - if (nc_address_ret) { - auto& nc_result = nc_address_ret.value(); - std::string nc_result_str( - std::make_move_iterator(nc_result.begin()), - std::make_move_iterator(nc_result.end())); - if (auto pos = nc_result_str.find(' '); - pos != std::string::npos) { - nc_address = nc_result_str.substr(0, pos); - } - } + // Todo: detect remote address, and check remote port + // reference from https://github.com/ArknightsAutoHelper/ArknightsAutoHelper/blob/master/automator/connector/ADBConnector.py#L436 + auto nc_address_ret = call_command(cmd_replace(adb_cfg.nc_address)); + if (nc_address_ret) { + auto& nc_result = nc_address_ret.value(); + std::string nc_result_str( + std::make_move_iterator(nc_result.begin()), + std::make_move_iterator(nc_result.end())); + if (auto pos = nc_result_str.find(' '); + pos != std::string::npos) { + nc_address = nc_result_str.substr(0, pos); + } + } - auto socket_opt = try_to_init_socket(bind_address, adb_cfg.nc_port); - if (socket_opt) { - nc_port = socket_opt.value(); - m_adb.screencap_raw_by_nc = cmd_replace(adb_cfg.screencap_raw_by_nc); - m_server_started = true; - } - else { - m_server_started = false; - } - } + auto socket_opt = try_to_init_socket(bind_address, adb_cfg.nc_port); + if (socket_opt) { + nc_port = socket_opt.value(); + m_adb.screencap_raw_by_nc = cmd_replace(adb_cfg.screencap_raw_by_nc); + m_server_started = true; + } + else { + m_server_started = false; + } + } - // try to find the fastest way - screencap(); + // try to find the fastest way + screencap(); - ++m_instance_count; - return true; + ++m_instance_count; + return true; } bool asst::Controller::release() { #ifdef _WIN32 - if (m_server_sock) { - m_server_started = false; - ::closesocket(m_server_sock); - m_server_sock = 0U; - } + if (m_server_sock) { + m_server_started = false; + ::closesocket(m_server_sock); + m_server_sock = 0U; + } #endif #ifndef _WIN32 - if (m_child) + if (m_child) #else - if (true) + if (true) #endif - { - return call_command(m_adb.release).has_value(); - } + { + return call_command(m_adb.release).has_value(); + } } bool asst::Controller::inited() const noexcept { - return m_inited; + return m_inited; } const std::string& asst::Controller::get_uuid() const { - return m_uuid; + return m_uuid; } cv::Mat asst::Controller::get_image(bool raw) { - bool inited = m_inited; - // 有些模拟器adb偶尔会莫名其妙截图失败,多试几次 - for (int i = 0; i != 20; ++i) { - if (screencap()) { - break; - } - // 截图之前正常,截图之后不正常了,说明截图过程中发现 adb 炸了 - if (inited && !m_inited) { - const static cv::Size dsize(m_scale_size.first, m_scale_size.second); - m_cache_image = cv::Mat(dsize, CV_8UC3); - break; - } - } + bool inited = m_inited; + // 有些模拟器adb偶尔会莫名其妙截图失败,多试几次 + for (int i = 0; i != 20; ++i) { + if (screencap()) { + break; + } + // 截图之前正常,截图之后不正常了,说明截图过程中发现 adb 炸了 + if (inited && !m_inited) { + const static cv::Size dsize(m_scale_size.first, m_scale_size.second); + m_cache_image = cv::Mat(dsize, CV_8UC3); + break; + } + } - if (raw) { - std::shared_lock image_lock(m_image_mutex); - cv::Mat copy = m_cache_image.clone(); - return copy; - } + if (raw) { + std::shared_lock image_lock(m_image_mutex); + cv::Mat copy = m_cache_image.clone(); + return copy; + } - return get_resized_image(); + return get_resized_image(); } std::vector asst::Controller::get_image_encode() const { - cv::Mat img = get_resized_image(); - std::vector buf; - cv::imencode(".png", img, buf); + cv::Mat img = get_resized_image(); + std::vector buf; + cv::imencode(".png", img, buf); - return buf; + return buf; }