mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-20 10:57:45 +08:00
refactor: 重构controller,改为同步操作,修改部分接口参数
This commit is contained in:
@@ -20,7 +20,7 @@ extern "C"
|
||||
#ifdef __cplusplus
|
||||
typedef asst::Assistant* AsstHandle;
|
||||
#else
|
||||
typedef void* AsstHandle;
|
||||
typedef void* AsstHandle;
|
||||
#endif
|
||||
typedef int TaskId;
|
||||
typedef void(ASST_CALL* AsstApiCallback)(int msg, const char* detail_json, void* custom_arg);
|
||||
@@ -41,7 +41,7 @@ extern "C"
|
||||
bool ASSTAPI AsstStop(AsstHandle handle);
|
||||
bool ASSTAPI AsstRunning(AsstHandle handle);
|
||||
|
||||
bool ASSTAPI AsstCtrlerClick(AsstHandle handle, int x, int y, bool block);
|
||||
bool ASSTAPI AsstClick(AsstHandle handle, int x, int y);
|
||||
unsigned long long ASSTAPI AsstGetImage(AsstHandle handle, void* buff, unsigned long long buff_size);
|
||||
unsigned long long ASSTAPI AsstGetUUID(AsstHandle handle, char* buff, unsigned long long buff_size);
|
||||
unsigned long long ASSTAPI AsstGetTasksList(AsstHandle handle, TaskId* buff, unsigned long long buff_size);
|
||||
|
||||
@@ -166,15 +166,15 @@ std::vector<uchar> asst::Assistant::get_image() const
|
||||
if (!inited()) {
|
||||
return {};
|
||||
}
|
||||
return m_ctrler->get_image_encode();
|
||||
return m_ctrler->get_encoded_image_cache();
|
||||
}
|
||||
|
||||
bool asst::Assistant::ctrler_click(int x, int y, bool block)
|
||||
bool asst::Assistant::ctrler_click(int x, int y)
|
||||
{
|
||||
if (!inited()) {
|
||||
return false;
|
||||
}
|
||||
m_ctrler->click(Point(x, y), block);
|
||||
m_ctrler->click(Point(x, y));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace asst
|
||||
bool running();
|
||||
|
||||
std::vector<uchar> get_image() const;
|
||||
bool ctrler_click(int x, int y, bool block = true);
|
||||
bool ctrler_click(int x, int y);
|
||||
std::string get_uuid() const;
|
||||
std::vector<TaskId> get_tasks_list() const;
|
||||
|
||||
|
||||
@@ -135,12 +135,12 @@ bool AsstSetTaskParams(AsstHandle handle, TaskId id, const char* params)
|
||||
return handle->set_task_params(id, params ? params : "");
|
||||
}
|
||||
|
||||
bool AsstCtrlerClick(AsstHandle handle, int x, int y, bool block)
|
||||
bool AsstClick(AsstHandle handle, int x, int y)
|
||||
{
|
||||
if (!inited || handle == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return handle->ctrler_click(x, y, block);
|
||||
return handle->ctrler_click(x, y);
|
||||
}
|
||||
|
||||
unsigned long long AsstGetImage(AsstHandle handle, void* buff, unsigned long long buff_size)
|
||||
|
||||
@@ -48,96 +48,32 @@ asst::Controller::Controller(AsstCallback callback, void* callback_arg)
|
||||
}
|
||||
|
||||
#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) {
|
||||
Log.error(__FUNCTION__, "controller pipe created failed", pipe_in_ret, pipe_out_ret);
|
||||
}
|
||||
m_support_socket = true;
|
||||
#endif
|
||||
|
||||
m_cmd_thread = std::thread(&Controller::pipe_working_proc, this);
|
||||
}
|
||||
|
||||
asst::Controller::~Controller()
|
||||
{
|
||||
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
|
||||
|
||||
if (m_cmd_thread.joinable()) {
|
||||
m_cmd_thread.join();
|
||||
}
|
||||
|
||||
set_inited(false);
|
||||
kill_adb_daemon();
|
||||
|
||||
#ifndef _WIN32
|
||||
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
|
||||
}
|
||||
|
||||
// asst::Rect asst::Controller::shaped_correct(const Rect & rect) const
|
||||
//{
|
||||
// if (rect.empty()
|
||||
// || m_scale_size.first == 0
|
||||
// || m_scale_size.second == 0) {
|
||||
// return rect;
|
||||
// }
|
||||
// // 明日方舟在异形屏上,有的地方是按比例缩放的,有的地方又是直接位移。没法整,这里简单粗暴一点截一个长条
|
||||
// Rect dst = rect;
|
||||
// if (m_scale_size.first != WindowWidthDefault) { // 说明是宽屏
|
||||
// if (rect.width <= WindowWidthDefault / 2) {
|
||||
// if (rect.x + rect.width <= WindowWidthDefault / 2) { // 整个矩形都在左半边
|
||||
// dst.x = 0;
|
||||
// dst.width = m_scale_size.first / 2;
|
||||
// }
|
||||
// else if (rect.x >= WindowWidthDefault / 2) { // 整个矩形都在右半边
|
||||
// dst.x = m_scale_size.first / 2;
|
||||
// dst.width = m_scale_size.first / 2;
|
||||
// }
|
||||
// else { // 整个矩形横跨了中线
|
||||
// dst.x = 0;
|
||||
// dst.width = m_scale_size.first;
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// dst.x = 0;
|
||||
// dst.width = m_scale_size.first;
|
||||
// }
|
||||
// }
|
||||
// else if (m_scale_size.second != WindowHeightDefault) { // 说明是偏方形屏
|
||||
// if (rect.height <= WindowHeightDefault / 2) {
|
||||
// if (rect.y + rect.height <= WindowHeightDefault / 2) { // 整个矩形都在上半边
|
||||
// dst.y = 0;
|
||||
// dst.height = m_scale_size.second / 2;
|
||||
// }
|
||||
// else if (rect.y >= WindowHeightDefault / 2) { // 整个矩形都在下半边
|
||||
// dst.y = m_scale_size.second / 2;
|
||||
// dst.height = m_scale_size.second / 2; // 整个矩形横跨了中线
|
||||
// }
|
||||
// else {
|
||||
// dst.y = 0;
|
||||
// dst.height = m_scale_size.second;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// else {
|
||||
// dst.y = 0;
|
||||
// dst.height = m_scale_size.second;
|
||||
// }
|
||||
// }
|
||||
// return dst;
|
||||
// }
|
||||
|
||||
std::pair<int, int> asst::Controller::get_scale_size() const noexcept
|
||||
{
|
||||
return m_scale_size;
|
||||
@@ -148,40 +84,6 @@ bool asst::Controller::need_exit() const
|
||||
return m_exit_flag != nullptr && *m_exit_flag;
|
||||
}
|
||||
|
||||
void asst::Controller::pipe_working_proc()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
while (!m_thread_exit) {
|
||||
std::unique_lock<std::mutex> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> asst::Controller::call_command(const std::string& cmd, int64_t timeout, bool allow_reconnect,
|
||||
bool recv_by_socket)
|
||||
{
|
||||
@@ -247,9 +149,9 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
sockov.hEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
DWORD dummy;
|
||||
if (!m_AcceptEx(m_server_sock, client_socket, sock_buffer.value().get(),
|
||||
(DWORD)sock_buffer.value().size() - ((sizeof(sockaddr_in) + 16) * 2), sizeof(sockaddr_in) + 16,
|
||||
sizeof(sockaddr_in) + 16, &dummy, &sockov)) {
|
||||
if (!m_server_accept_ex(m_server_sock, client_socket, sock_buffer.value().get(),
|
||||
(DWORD)sock_buffer.value().size() - ((sizeof(sockaddr_in) + 16) * 2),
|
||||
sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &dummy, &sockov)) {
|
||||
err = WSAGetLastError();
|
||||
if (err == ERROR_IO_PENDING) {
|
||||
accept_pending = true;
|
||||
@@ -258,15 +160,12 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
Log.trace("AcceptEx failed, err:", err);
|
||||
accept_pending = false;
|
||||
socket_eof = true;
|
||||
closesocket(client_socket);
|
||||
::closesocket(client_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (need_exit()) {
|
||||
break;
|
||||
}
|
||||
while (!need_exit()) {
|
||||
wait_handles.clear();
|
||||
if (process_running) wait_handles.push_back(process_info.hProcess);
|
||||
if (!pipe_eof) wait_handles.push_back(pipeov.hEvent);
|
||||
@@ -346,7 +245,7 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
|
||||
if (len == 0) {
|
||||
socket_eof = true;
|
||||
closesocket(client_socket);
|
||||
::closesocket(client_socket);
|
||||
}
|
||||
else {
|
||||
// reset the overlapped since we reuse it for different handle
|
||||
@@ -367,7 +266,7 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
sock_data.insert(sock_data.end(), sock_buffer.value().get(), sock_buffer.value().get() + len);
|
||||
if (len == 0) {
|
||||
socket_eof = true;
|
||||
closesocket(client_socket);
|
||||
::closesocket(client_socket);
|
||||
}
|
||||
else {
|
||||
(void)ReadFile(reinterpret_cast<HANDLE>(client_socket), sock_buffer.value().get(),
|
||||
@@ -377,7 +276,7 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
else {
|
||||
// err = GetLastError();
|
||||
socket_eof = true;
|
||||
closesocket(client_socket);
|
||||
::closesocket(client_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,13 +299,13 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
};
|
||||
|
||||
int exit_ret = 0;
|
||||
m_child = fork();
|
||||
m_child = ::fork();
|
||||
if (m_child == 0) {
|
||||
// child process
|
||||
|
||||
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]);
|
||||
@@ -415,7 +314,7 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
// close(m_pipe_out[PIPE_WRITE]);
|
||||
|
||||
exit_ret = execlp("sh", "sh", "-c", cmd.c_str(), nullptr);
|
||||
exit(exit_ret);
|
||||
::exit(exit_ret);
|
||||
}
|
||||
else if (m_child > 0) {
|
||||
// parent process
|
||||
@@ -446,7 +345,7 @@ std::optional<std::string> asst::Controller::call_command(const std::string& cmd
|
||||
|
||||
while (read_num > 0) {
|
||||
pipe_data.insert(pipe_data.end(), pipe_buffer.get(), pipe_buffer.get() + read_num);
|
||||
read_num = read(m_pipe_out[PIPE_READ], pipe_buffer.get(), pipe_buffer.size());
|
||||
read_num = ::read(m_pipe_out[PIPE_READ], pipe_buffer.get(), pipe_buffer.size());
|
||||
}
|
||||
} while (::waitpid(m_child, &exit_ret, WNOHANG) == 0 && !check_timeout());
|
||||
}
|
||||
@@ -628,22 +527,12 @@ void asst::Controller::clear_info() noexcept
|
||||
m_scale_size = { WindowWidthDefault, WindowHeightDefault };
|
||||
}
|
||||
|
||||
int asst::Controller::push_cmd(const std::string& cmd)
|
||||
{
|
||||
random_delay();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_cmd_queue_mutex);
|
||||
m_cmd_queue.emplace(cmd);
|
||||
m_cmd_condvar.notify_one();
|
||||
return static_cast<int>(++m_push_id);
|
||||
}
|
||||
|
||||
void asst::Controller::try_to_close_socket() noexcept
|
||||
void asst::Controller::close_socket(SOCKET& sock) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_server_sock != INVALID_SOCKET) {
|
||||
::closesocket(m_server_sock);
|
||||
m_server_sock = INVALID_SOCKET;
|
||||
if (sock != INVALID_SOCKET) {
|
||||
::closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
#else
|
||||
if (m_server_sock >= 0) {
|
||||
@@ -654,7 +543,7 @@ void asst::Controller::try_to_close_socket() noexcept
|
||||
m_server_started = false;
|
||||
}
|
||||
|
||||
std::optional<unsigned short> asst::Controller::try_to_init_socket(const std::string& local_address)
|
||||
std::optional<unsigned short> asst::Controller::init_socket(const std::string& local_address)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
@@ -666,18 +555,18 @@ std::optional<unsigned short> asst::Controller::try_to_init_socket(const std::st
|
||||
}
|
||||
}
|
||||
|
||||
DWORD dummy;
|
||||
GUID GuidAcceptEx = WSAID_ACCEPTEX;
|
||||
auto err = WSAIoctl(m_server_sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx),
|
||||
&m_AcceptEx, sizeof(m_AcceptEx), &dummy, NULL, NULL);
|
||||
DWORD dummy = 0;
|
||||
GUID guid_accept_ex = WSAID_ACCEPTEX;
|
||||
int err = WSAIoctl(m_server_sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid_accept_ex, sizeof(guid_accept_ex),
|
||||
&m_server_accept_ex, sizeof(m_server_accept_ex), &dummy, NULL, NULL);
|
||||
if (err == SOCKET_ERROR) {
|
||||
err = WSAGetLastError();
|
||||
Log.error("failed to resolve AcceptEx, err:", err);
|
||||
closesocket(m_server_sock);
|
||||
::closesocket(m_server_sock);
|
||||
return std::nullopt;
|
||||
}
|
||||
m_server_addr.sin_family = PF_INET;
|
||||
inet_pton(AF_INET, local_address.c_str(), &m_server_addr.sin_addr);
|
||||
m_server_sock_addr.sin_family = PF_INET;
|
||||
::inet_pton(AF_INET, local_address.c_str(), &m_server_sock_addr.sin_addr);
|
||||
#else
|
||||
m_server_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (m_server_sock < 0) {
|
||||
@@ -692,10 +581,10 @@ std::optional<unsigned short> asst::Controller::try_to_init_socket(const std::st
|
||||
uint16_t port_result = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
m_server_addr.sin_port = htons(0);
|
||||
int bind_ret = ::bind(m_server_sock, reinterpret_cast<SOCKADDR*>(&m_server_addr), sizeof(SOCKADDR));
|
||||
int addrlen = sizeof(m_server_addr);
|
||||
int getname_ret = getsockname(m_server_sock, reinterpret_cast<sockaddr*>(&m_server_addr), &addrlen);
|
||||
m_server_sock_addr.sin_port = ::htons(0);
|
||||
int bind_ret = ::bind(m_server_sock, reinterpret_cast<SOCKADDR*>(&m_server_sock_addr), sizeof(SOCKADDR));
|
||||
int addrlen = sizeof(m_server_sock_addr);
|
||||
int getname_ret = ::getsockname(m_server_sock, reinterpret_cast<sockaddr*>(&m_server_sock_addr), &addrlen);
|
||||
int listen_ret = ::listen(m_server_sock, 3);
|
||||
server_start = bind_ret == 0 && getname_ret == 0 && listen_ret == 0;
|
||||
#else
|
||||
@@ -708,34 +597,18 @@ std::optional<unsigned short> asst::Controller::try_to_init_socket(const std::st
|
||||
#endif
|
||||
|
||||
if (!server_start) {
|
||||
Log.info("not supports netcat");
|
||||
Log.info("not supports socket");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
port_result = ntohs(m_server_addr.sin_port);
|
||||
|
||||
Log.info("server_start", local_address, port_result);
|
||||
Log.info("command server start", local_address, port_result);
|
||||
return port_result;
|
||||
}
|
||||
|
||||
void asst::Controller::wait(unsigned id) const noexcept
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
static constexpr auto delay = 10ms;
|
||||
while (id > m_completed_id) {
|
||||
std::this_thread::sleep_for(delay);
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::Controller::screencap(bool allow_reconnect)
|
||||
{
|
||||
// if (true) {
|
||||
// m_inited = true;
|
||||
// std::unique_lock<std::shared_mutex> image_lock(m_image_mutex);
|
||||
// m_cache_image = cv::imread("err/1.png");
|
||||
// return true;
|
||||
// }
|
||||
|
||||
DecodeFunc decode_raw = [&](std::string_view data) -> bool {
|
||||
if (data.empty()) {
|
||||
return false;
|
||||
@@ -907,7 +780,7 @@ void asst::Controller::clear_lf_info()
|
||||
m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::UnknownYet;
|
||||
}
|
||||
|
||||
cv::Mat asst::Controller::get_resized_image() const
|
||||
cv::Mat asst::Controller::get_resized_image_cache() const
|
||||
{
|
||||
const static cv::Size d_size(m_scale_size.first, m_scale_size.second);
|
||||
|
||||
@@ -921,67 +794,55 @@ cv::Mat asst::Controller::get_resized_image() const
|
||||
return resized_mat;
|
||||
}
|
||||
|
||||
std::optional<int> asst::Controller::start_game(const std::string& client_type, bool block)
|
||||
bool asst::Controller::start_game(const std::string& client_type)
|
||||
{
|
||||
if (client_type.empty()) {
|
||||
return std::nullopt;
|
||||
return false;
|
||||
}
|
||||
if (auto intent_name = Configer.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;
|
||||
auto intent_name = Configer.get_intent_name(client_type);
|
||||
if (!intent_name) {
|
||||
return false;
|
||||
}
|
||||
return std::nullopt;
|
||||
std::string cur_cmd = utils::string_replace_all(m_adb.start, "[Intent]", intent_name.value());
|
||||
return call_command(cur_cmd).has_value();
|
||||
}
|
||||
|
||||
std::optional<int> asst::Controller::stop_game(bool block)
|
||||
bool asst::Controller::stop_game()
|
||||
{
|
||||
std::string cur_cmd = m_adb.stop;
|
||||
int id = push_cmd(cur_cmd);
|
||||
if (block) {
|
||||
wait(id);
|
||||
}
|
||||
return id;
|
||||
return call_command(m_adb.stop).has_value();
|
||||
}
|
||||
|
||||
int asst::Controller::click(const Point& p, bool block)
|
||||
bool asst::Controller::click(const Point& p)
|
||||
{
|
||||
int x = static_cast<int>(p.x * m_control_scale);
|
||||
int y = static_cast<int>(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));
|
||||
}
|
||||
|
||||
int asst::Controller::click(const Rect& rect, bool block)
|
||||
bool asst::Controller::click(const Rect& rect)
|
||||
{
|
||||
return click(rand_point_in_rect(rect), block);
|
||||
return click(rand_point_in_rect(rect));
|
||||
}
|
||||
|
||||
int asst::Controller::click_without_scale(const Point& p, bool block)
|
||||
bool asst::Controller::click_without_scale(const Point& p)
|
||||
{
|
||||
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) }, { "[y]", std::to_string(p.y) } });
|
||||
int id = push_cmd(cur_cmd);
|
||||
if (block) {
|
||||
wait(id);
|
||||
}
|
||||
return id;
|
||||
|
||||
return call_command(cur_cmd).has_value();
|
||||
}
|
||||
|
||||
int asst::Controller::click_without_scale(const Rect& rect, bool block)
|
||||
bool asst::Controller::click_without_scale(const Rect& rect)
|
||||
{
|
||||
return click_without_scale(rand_point_in_rect(rect), block);
|
||||
return click_without_scale(rand_point_in_rect(rect));
|
||||
}
|
||||
|
||||
int asst::Controller::swipe(const Point& p1, const Point& p2, int duration, bool block, int extra_delay,
|
||||
bool extra_swipe)
|
||||
bool asst::Controller::swipe(const Point& p1, const Point& p2, int duration, bool extra_swipe)
|
||||
{
|
||||
int x1 = static_cast<int>(p1.x * m_control_scale);
|
||||
int y1 = static_cast<int>(p1.y * m_control_scale);
|
||||
@@ -989,16 +850,15 @@ int asst::Controller::swipe(const Point& p1, const Point& p2, int duration, bool
|
||||
int y2 = static_cast<int>(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, extra_swipe);
|
||||
}
|
||||
|
||||
int asst::Controller::swipe(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay, bool extra_swipe)
|
||||
bool asst::Controller::swipe(const Rect& r1, const Rect& r2, int duration, 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, extra_swipe);
|
||||
}
|
||||
|
||||
int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool block, int extra_delay,
|
||||
bool extra_swipe)
|
||||
bool asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool extra_swipe)
|
||||
{
|
||||
int x1 = p1.x, y1 = p1.y;
|
||||
int x2 = p2.x, y2 = p2.y;
|
||||
@@ -1018,8 +878,8 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int
|
||||
{ "[y2]", std::to_string(y2) },
|
||||
{ "[duration]", duration <= 0 ? "" : std::to_string(duration) },
|
||||
});
|
||||
bool ret = call_command(cur_cmd).has_value();
|
||||
|
||||
int id = 0;
|
||||
// 额外的滑动:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来
|
||||
const auto& opt = Configer.get_options();
|
||||
if (extra_swipe && opt.adb_extra_swipe_duration > 0) {
|
||||
@@ -1031,25 +891,14 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int
|
||||
{ "[y2]", std::to_string(y2 - opt.adb_extra_swipe_dist /* * m_control_scale*/) },
|
||||
{ "[duration]", std::to_string(opt.adb_extra_swipe_duration) },
|
||||
});
|
||||
push_cmd(cur_cmd);
|
||||
id = push_cmd(extra_cmd);
|
||||
ret &= call_command(extra_cmd).has_value();
|
||||
}
|
||||
else {
|
||||
id = push_cmd(cur_cmd);
|
||||
}
|
||||
|
||||
if (block) {
|
||||
wait(id);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(extra_delay));
|
||||
}
|
||||
return id;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay,
|
||||
bool extra_swipe)
|
||||
bool asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int duration, 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, extra_swipe);
|
||||
}
|
||||
|
||||
bool asst::Controller::connect(const std::string& adb_path, const std::string& address, const std::string& config)
|
||||
@@ -1292,22 +1141,17 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a
|
||||
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) {
|
||||
if (nc_address_ret && !m_server_started) {
|
||||
auto& nc_result_str = nc_address_ret.value();
|
||||
if (auto pos = nc_result_str.find(' '); pos != std::string::npos) {
|
||||
nc_address = nc_result_str.substr(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto socket_opt = try_to_init_socket(bind_address);
|
||||
auto socket_opt = init_socket(bind_address);
|
||||
if (socket_opt) {
|
||||
nc_port = socket_opt.value();
|
||||
m_adb.screencap_raw_by_nc = cmd_replace(adb_cfg.screencap_raw_by_nc);
|
||||
@@ -1318,6 +1162,10 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a
|
||||
}
|
||||
}
|
||||
|
||||
if (need_exit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to find the fastest way
|
||||
if (!screencap()) {
|
||||
Log.error("Cannot find a proper way to screencap!");
|
||||
@@ -1364,7 +1212,8 @@ void asst::Controller::kill_adb_daemon()
|
||||
|
||||
bool asst::Controller::release()
|
||||
{
|
||||
try_to_close_socket();
|
||||
close_socket(m_server_sock);
|
||||
m_server_started = false;
|
||||
|
||||
#ifndef _WIN32
|
||||
if (m_child)
|
||||
@@ -1439,14 +1288,14 @@ cv::Mat asst::Controller::get_image(bool raw)
|
||||
return copy;
|
||||
}
|
||||
|
||||
return get_resized_image();
|
||||
return get_resized_image_cache();
|
||||
}
|
||||
|
||||
std::vector<uchar> asst::Controller::get_image_encode() const
|
||||
std::vector<uchar> asst::Controller::get_encoded_image_cache() const
|
||||
{
|
||||
cv::Mat img = get_resized_image();
|
||||
cv::Mat img = get_resized_image_cache();
|
||||
std::vector<uchar> buf;
|
||||
cv::imencode(".png", img, buf);
|
||||
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
@@ -43,32 +43,21 @@ namespace asst
|
||||
|
||||
const std::string& get_uuid() const;
|
||||
cv::Mat get_image(bool raw = false);
|
||||
std::vector<uchar> get_image_encode() const;
|
||||
std::vector<uchar> get_encoded_image_cache() const;
|
||||
|
||||
/* 开启游戏、点击和滑动都是异步执行,返回该任务的id */
|
||||
bool start_game(const std::string& client_type);
|
||||
bool stop_game();
|
||||
|
||||
std::optional<int> start_game(const std::string& client_type, bool block = true);
|
||||
std::optional<int> stop_game(bool block = true);
|
||||
bool click(const Point& p);
|
||||
bool click(const Rect& rect);
|
||||
bool click_without_scale(const Point& p);
|
||||
bool click_without_scale(const Rect& rect);
|
||||
|
||||
int click(const Point& p, bool block = true);
|
||||
int click(const Rect& rect, bool block = true);
|
||||
int click_without_scale(const Point& p, bool block = true);
|
||||
int click_without_scale(const Rect& rect, bool block = true);
|
||||
bool swipe(const Point& p1, const Point& p2, int duration = 0, bool extra_swipe = false);
|
||||
bool swipe(const Rect& r1, const Rect& r2, int duration = 0, bool extra_swipe = false);
|
||||
bool swipe_without_scale(const Point& p1, const Point& p2, int duration = 0, bool extra_swipe = false);
|
||||
bool swipe_without_scale(const Rect& r1, const Rect& r2, int duration = 0, bool extra_swipe = false);
|
||||
|
||||
static constexpr int SwipeExtraDelayDefault = 1000;
|
||||
int swipe(const Point& p1, const Point& p2, int duration = 0, bool block = true,
|
||||
int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false);
|
||||
int swipe(const Rect& r1, const Rect& r2, int duration = 0, bool block = true,
|
||||
int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false);
|
||||
int swipe_without_scale(const Point& p1, const Point& p2, int duration = 0, bool block = true,
|
||||
int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false);
|
||||
int swipe_without_scale(const Rect& r1, const Rect& r2, int duration = 0, bool block = true,
|
||||
int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false);
|
||||
|
||||
void wait(unsigned id) const noexcept;
|
||||
|
||||
// 异形屏矫正
|
||||
// Rect shaped_correct(const Rect& rect) const;
|
||||
std::pair<int, int> get_scale_size() const noexcept;
|
||||
|
||||
Controller& operator=(const Controller&) = delete;
|
||||
@@ -76,23 +65,21 @@ namespace asst
|
||||
|
||||
private:
|
||||
bool need_exit() const;
|
||||
void pipe_working_proc();
|
||||
std::optional<std::string> call_command(const std::string& cmd, int64_t timeout = 20000,
|
||||
bool allow_reconnect = true, bool recv_by_socket = false);
|
||||
int push_cmd(const std::string& cmd);
|
||||
bool release();
|
||||
void kill_adb_daemon();
|
||||
bool set_inited(bool inited);
|
||||
|
||||
void try_to_close_socket() noexcept;
|
||||
std::optional<unsigned short> try_to_init_socket(const std::string& local_address);
|
||||
void close_socket(SOCKET& sock) noexcept;
|
||||
std::optional<unsigned short> init_socket(const std::string& local_address);
|
||||
|
||||
using DecodeFunc = std::function<bool(std::string_view)>;
|
||||
bool screencap(bool allow_reconnect = false);
|
||||
bool screencap(const std::string& cmd, const DecodeFunc& decode_func, bool allow_reconnect = false,
|
||||
bool by_socket = false);
|
||||
void clear_lf_info();
|
||||
cv::Mat get_resized_image() const;
|
||||
cv::Mat get_resized_image_cache() const;
|
||||
|
||||
Point rand_point_in_rect(const Rect& rect);
|
||||
|
||||
@@ -111,13 +98,14 @@ namespace asst
|
||||
std::minstd_rand m_rand_engine;
|
||||
|
||||
std::mutex m_callcmd_mutex;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
ASST_AUTO_DEDUCED_ZERO_INIT_START
|
||||
WSADATA m_wsa_data {};
|
||||
SOCKET m_server_sock = INVALID_SOCKET;
|
||||
sockaddr_in m_server_addr {};
|
||||
LPFN_ACCEPTEX m_AcceptEx = nullptr;
|
||||
sockaddr_in m_server_sock_addr {};
|
||||
LPFN_ACCEPTEX m_server_accept_ex = nullptr;
|
||||
ASST_AUTO_DEDUCED_ZERO_INIT_END
|
||||
|
||||
#else
|
||||
@@ -180,15 +168,6 @@ namespace asst
|
||||
mutable std::shared_mutex m_image_mutex;
|
||||
cv::Mat m_cache_image;
|
||||
|
||||
bool m_thread_exit = false;
|
||||
// bool m_thread_idle = true;
|
||||
std::mutex m_cmd_queue_mutex;
|
||||
std::condition_variable m_cmd_condvar;
|
||||
std::queue<std::string> m_cmd_queue;
|
||||
std::atomic<unsigned> m_completed_id = 0;
|
||||
unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push,肯定是要上锁的,所以没必要原子
|
||||
std::thread m_cmd_thread;
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
// for Windows socket
|
||||
|
||||
@@ -719,15 +719,16 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
|
||||
placed_point, { opt_oper.rect.x + opt_oper.rect.width / 2, opt_oper.rect.y + opt_oper.rect.height / 2 }));
|
||||
// 1000 是随便取的一个系数,把整数的 pre_delay 转成小数用的
|
||||
int duration = static_cast<int>(swipe_oper_task_ptr->pre_delay / 800.0 * dist * log10(dist));
|
||||
m_ctrler->swipe(opt_oper.rect, placed_rect, duration, true, 0);
|
||||
m_ctrler->swipe(opt_oper.rect, placed_rect, duration);
|
||||
sleep(use_oper_task_ptr->post_delay);
|
||||
|
||||
// 将方向转换为实际的 swipe end 坐标点
|
||||
if (direction != Point::zero()) {
|
||||
constexpr int coeff = 500;
|
||||
Point end_point = placed_point + (direction * coeff);
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->post_delay, true, 100);
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->post_delay);
|
||||
}
|
||||
sleep(Task.get("BattleUseOper")->post_delay);
|
||||
|
||||
if (opt_oper.role == BattleRole::Drone) {
|
||||
cancel_oper_selection();
|
||||
|
||||
@@ -9,7 +9,7 @@ bool StartGameTaskPlugin::_run()
|
||||
if (m_client_type.empty()) {
|
||||
return false;
|
||||
}
|
||||
return m_ctrler->start_game(m_client_type).has_value();
|
||||
return m_ctrler->start_game(m_client_type);
|
||||
}
|
||||
|
||||
StartGameTaskPlugin& StartGameTaskPlugin::set_client_type(std::string client_type) noexcept
|
||||
|
||||
@@ -6,5 +6,5 @@ using namespace asst;
|
||||
|
||||
bool StopGameTaskPlugin::_run()
|
||||
{
|
||||
return m_ctrler->stop_game().has_value();
|
||||
return m_ctrler->stop_game();
|
||||
}
|
||||
|
||||
@@ -520,7 +520,7 @@ bool asst::BattleProcessTask::oper_deploy(const BattleAction& action)
|
||||
Point::distance(placed_point, { oper_rect.x + oper_rect.width / 2, oper_rect.y + oper_rect.height / 2 }));
|
||||
// 1000 是随便取的一个系数,把整数的 pre_delay 转成小数用的
|
||||
int duration = static_cast<int>(swipe_oper_task_ptr->pre_delay / 1000.0 * dist * log10(dist));
|
||||
m_ctrler->swipe(oper_rect, placed_rect, duration, true, 0);
|
||||
m_ctrler->swipe(oper_rect, placed_rect, duration);
|
||||
|
||||
sleep(use_oper_task_ptr->post_delay);
|
||||
|
||||
@@ -539,7 +539,8 @@ bool asst::BattleProcessTask::oper_deploy(const BattleAction& action)
|
||||
constexpr int coeff = 500;
|
||||
Point end_point = placed_point + (direction * coeff);
|
||||
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->post_delay, true, 100);
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->post_delay);
|
||||
sleep(Task.get("BattleUseOper")->post_delay);
|
||||
}
|
||||
|
||||
m_used_opers[iter->first] =
|
||||
@@ -910,4 +911,4 @@ std::optional<std::unordered_map<GroupNameType, CharNameType>> asst::BattleProce
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,5 +307,5 @@ void ProcessTask::exec_click_task(const Rect& matched_rect)
|
||||
|
||||
void asst::ProcessTask::exec_swipe_task(const Rect& r1, const Rect& r2, int duration, bool extra_swipe)
|
||||
{
|
||||
m_ctrler->swipe(r1, r2, duration, true, 0, extra_swipe);
|
||||
m_ctrler->swipe(r1, r2, duration, extra_swipe);
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ asst::http::Response asst::ReportDataTask::escape_and_request(const std::string&
|
||||
utils::string_replace_all(format, { { "[body]", body_escape }, { "[extra]", m_extra_param } });
|
||||
|
||||
Log.info("request:\n" + cmd_line);
|
||||
std::string response = utils::callcmd(cmd_line);
|
||||
std::string response = utils::call_command(cmd_line);
|
||||
Log.info("response:\n" + response);
|
||||
|
||||
// Log.info("response:\n" + utils::string_replace_all(response, {
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace asst::utils
|
||||
using platform::path_to_utf8_string;
|
||||
using platform::to_osstring;
|
||||
|
||||
using platform::callcmd;
|
||||
using platform::call_command;
|
||||
|
||||
namespace path_literals
|
||||
{
|
||||
@@ -29,5 +29,4 @@ namespace asst::utils
|
||||
return asst::utils::path(std::string(std::string_view(utf8_str, len)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace asst::utils
|
||||
} // namespace asst::utils
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace asst::platform
|
||||
{
|
||||
std::string callcmd(const std::string&);
|
||||
std::string call_command(const std::string& cmdline, bool* exit_flag = nullptr);
|
||||
|
||||
using os_string = std::filesystem::path::string_type;
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ void* asst::platform::aligned_alloc(size_t len, size_t align)
|
||||
|
||||
void asst::platform::aligned_free(void* ptr)
|
||||
{
|
||||
free(ptr);
|
||||
::free(ptr);
|
||||
}
|
||||
|
||||
std::string asst::platform::callcmd(const std::string& cmdline)
|
||||
std::string asst::platform::call_command(const std::string& cmdline, bool* exit_flag)
|
||||
{
|
||||
constexpr int PipeBuffSize = 4096;
|
||||
std::string pipe_str;
|
||||
@@ -39,49 +39,49 @@ std::string asst::platform::callcmd(const std::string& cmdline)
|
||||
if (pipe_in_ret != 0 || pipe_out_ret != 0) {
|
||||
return {};
|
||||
}
|
||||
fcntl(pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK);
|
||||
::fcntl(pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK);
|
||||
int exit_ret = 0;
|
||||
int child = fork();
|
||||
int child = ::fork();
|
||||
if (child == 0) {
|
||||
// child process
|
||||
dup2(pipe_in[PIPE_READ], STDIN_FILENO);
|
||||
dup2(pipe_out[PIPE_WRITE], STDOUT_FILENO);
|
||||
dup2(pipe_out[PIPE_WRITE], STDERR_FILENO);
|
||||
::dup2(pipe_in[PIPE_READ], STDIN_FILENO);
|
||||
::dup2(pipe_out[PIPE_WRITE], STDOUT_FILENO);
|
||||
::dup2(pipe_out[PIPE_WRITE], STDERR_FILENO);
|
||||
|
||||
// all these are for use by parent only
|
||||
close(pipe_in[PIPE_READ]);
|
||||
close(pipe_in[PIPE_WRITE]);
|
||||
close(pipe_out[PIPE_READ]);
|
||||
close(pipe_out[PIPE_WRITE]);
|
||||
::close(pipe_in[PIPE_READ]);
|
||||
::close(pipe_in[PIPE_WRITE]);
|
||||
::close(pipe_out[PIPE_READ]);
|
||||
::close(pipe_out[PIPE_WRITE]);
|
||||
|
||||
exit_ret = execlp("sh", "sh", "-c", cmdline.c_str(), nullptr);
|
||||
exit(exit_ret);
|
||||
::exit(exit_ret);
|
||||
}
|
||||
else if (child > 0) {
|
||||
// parent process
|
||||
|
||||
// close unused file descriptors, these are for child only
|
||||
close(pipe_in[PIPE_READ]);
|
||||
close(pipe_out[PIPE_WRITE]);
|
||||
::close(pipe_in[PIPE_READ]);
|
||||
::close(pipe_out[PIPE_WRITE]);
|
||||
|
||||
do {
|
||||
ssize_t read_num = read(pipe_out[PIPE_READ], pipe_buffer.get(), PipeBuffSize);
|
||||
ssize_t read_num = ::read(pipe_out[PIPE_READ], pipe_buffer.get(), PipeBuffSize);
|
||||
|
||||
while (read_num > 0) {
|
||||
pipe_str.append(pipe_buffer.get(), pipe_buffer.get() + read_num);
|
||||
read_num = read(pipe_out[PIPE_READ], pipe_buffer.get(), PipeBuffSize);
|
||||
read_num = ::read(pipe_out[PIPE_READ], pipe_buffer.get(), PipeBuffSize);
|
||||
};
|
||||
} while (::waitpid(child, &exit_ret, WNOHANG) == 0);
|
||||
} while (::waitpid(child, &exit_ret, WNOHANG) == 0 && !(exit_flag && *exit_flag));
|
||||
|
||||
close(pipe_in[PIPE_WRITE]);
|
||||
close(pipe_out[PIPE_READ]);
|
||||
::close(pipe_in[PIPE_WRITE]);
|
||||
::close(pipe_out[PIPE_READ]);
|
||||
}
|
||||
else {
|
||||
// failed to create child process
|
||||
close(pipe_in[PIPE_READ]);
|
||||
close(pipe_in[PIPE_WRITE]);
|
||||
close(pipe_out[PIPE_READ]);
|
||||
close(pipe_out[PIPE_WRITE]);
|
||||
::close(pipe_in[PIPE_READ]);
|
||||
::close(pipe_in[PIPE_WRITE]);
|
||||
::close(pipe_out[PIPE_READ]);
|
||||
::close(pipe_out[PIPE_WRITE]);
|
||||
}
|
||||
return pipe_str;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ std::string asst::platform::from_osstring(const os_string& os_str)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string asst::platform::callcmd(const std::string& cmdline)
|
||||
std::string asst::platform::call_command(const std::string& cmdline, bool* exit_flag)
|
||||
{
|
||||
constexpr int PipeBuffSize = 4096;
|
||||
auto pipe_buffer = std::make_unique<char[]>(PipeBuffSize);
|
||||
@@ -164,7 +164,7 @@ std::string asst::platform::callcmd(const std::string& cmdline)
|
||||
OVERLAPPED pipeov { .hEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr) };
|
||||
(void)ReadFile(pipe_parent_read, pipe_buffer.get(), PipeBuffSize, nullptr, &pipeov);
|
||||
|
||||
while (true) {
|
||||
while (!(exit_flag && *exit_flag)) {
|
||||
wait_handles.clear();
|
||||
if (process_running) wait_handles.push_back(process_info.hProcess);
|
||||
if (!pipe_eof) wait_handles.push_back(pipeov.hEvent);
|
||||
|
||||
Reference in New Issue
Block a user