diff --git a/CMakeLists.txt b/CMakeLists.txt index ef27d0810e..d7de4a3c8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,10 @@ option(INSTALL_RESOURCE "install resource" OFF) option(INSTALL_DEVEL "install development files" OFF) option(INSTALL_THIRD_LIBS "install third party libraries" ON) option(USE_MAADEPS "use third-party libraries built by MaaDeps" ON) +option(WITH_THRIFT "build with thrift" OFF) include(${PROJECT_SOURCE_DIR}/cmake/utils.cmake) +include(${PROJECT_SOURCE_DIR}/cmake/thrift-gen.cmake) if(USE_MAADEPS) include(${PROJECT_SOURCE_DIR}/MaaDeps/maadeps.cmake) @@ -27,7 +29,7 @@ if (MSVC) add_compile_options("/MP") add_compile_options("/W4;/WX") add_compile_options("/wd4127") # conditional expression is constant - add_compile_options("/Wv:19.35.32217") # disable warning introduced after this version + add_compile_options("/Wv:19.35.32217") # disable warning introduced after this version set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") else () @@ -79,9 +81,17 @@ find_package(MaaDerpLearning REQUIRED) find_package(asio REQUIRED) find_package(ONNXRuntime) find_package(cpr CONFIG REQUIRED) +if(WITH_THRIFT) + find_package(Thrift CONFIG REQUIRED) +endif(WITH_THRIFT) target_link_libraries(MaaCore ${OpenCV_LIBS} MaaDerpLearning asio::asio ZLIB::ZLIB ONNXRuntime::ONNXRuntime cpr::cpr header_only_libraries) +if(WITH_THRIFT) + add_subdirectory(src/MaaThriftController) + target_link_libraries(MaaCore MaaThriftController) +endif(WITH_THRIFT) + if(WIN32) target_link_libraries(MaaCore ws2_32) endif() diff --git a/MAA.sln b/MAA.sln index c57166257a..5afb2b797a 100644 --- a/MAA.sln +++ b/MAA.sln @@ -10,6 +10,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaaWpfGui", "src\MaaWpfGui\ EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaaCore", "src\MaaCore\MaaCore.vcxproj", "{362D1E30-F5AE-4279-9985-65C27B3BA300}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaaThriftController", "src\MaaThriftController\MaaThriftController.vcxproj", "{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{11F02235-5785-408B-9651-8A4B41FF36F4}" ProjectSection(SolutionItems) = preProject @@ -58,6 +59,18 @@ Global {362D1E30-F5AE-4279-9985-65C27B3BA300}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64 {362D1E30-F5AE-4279-9985-65C27B3BA300}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 {362D1E30-F5AE-4279-9985-65C27B3BA300}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|ARM64.Build.0 = Debug|ARM64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|x64.ActiveCfg = Debug|x64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|x64.Build.0 = Debug|x64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|ARM64.ActiveCfg = Release|ARM64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|ARM64.Build.0 = Release|ARM64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|x64.ActiveCfg = Release|x64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|x64.Build.0 = Release|x64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 + {8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 {F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Debug|ARM64.Build.0 = Debug|ARM64 {F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/cmake/thrift-gen.cmake b/cmake/thrift-gen.cmake new file mode 100644 index 0000000000..ba7ae3e9fb --- /dev/null +++ b/cmake/thrift-gen.cmake @@ -0,0 +1,35 @@ +macro(GENERATE_THRIFT_LIB LIB_NAME FILENAME OUTPUTDIR SOURCES) + file(MAKE_DIRECTORY ${OUTPUTDIR}) + detect_host_triplet(HOST_TRIPLET) + if(EXISTS ${PROJECT_SOURCE_DIR}/MaaDeps/vcpkg/installed/maa-${HOST_TRIPLET}/tools/thrift/thrift${CMAKE_EXECUTABLE_SUFFIX}) + set(THRIFT_COMPILER ${PROJECT_SOURCE_DIR}/MaaDeps/vcpkg/installed/maa-${HOST_TRIPLET}/tools/thrift/thrift${CMAKE_EXECUTABLE_SUFFIX}) + else() + find_program(THRIFT_COMPILER thrift) + endif() + if(NOT THRIFT_COMPILER) + message(FATAL_ERROR "Thrift compiler not found") + endif() + get_filename_component(THRIFT_IDL_NAME ${FILENAME} NAME_WE) + set(THRIFT_IDL_TARGET "${LIB_NAME}_${THRIFT_IDL_NAME}_idl") + set("${THRIFT_IDL_NAME}-gen-cpp" + ${OUTPUTDIR}/${THRIFT_IDL_NAME}.cpp + ${OUTPUTDIR}/${THRIFT_IDL_NAME}.h + ${OUTPUTDIR}/${THRIFT_IDL_NAME}_types.cpp + ${OUTPUTDIR}/${THRIFT_IDL_NAME}_types.h) + + add_custom_command(OUTPUT ${${THRIFT_IDL_NAME}-gen-cpp} + DEPENDS ${FILENAME} + COMMAND ${THRIFT_COMPILER} --gen cpp:no_skeleton -out ${OUTPUTDIR} ${FILENAME} + VERBATIM) + add_custom_target(${THRIFT_IDL_TARGET} DEPENDS ${${THRIFT_IDL_NAME}-gen-cpp}) + add_library(${LIB_NAME} STATIC ${${THRIFT_IDL_NAME}-gen-cpp}) + add_dependencies(${LIB_NAME} ${THRIFT_IDL_TARGET}) + set_target_properties(${LIB_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_link_libraries(${LIB_NAME} PUBLIC thrift::thrift) + target_include_directories(${LIB_NAME} PUBLIC ${OUTPUTDIR}) + set(${SOURCES} ${${SOURCES}} ${GENERATED_SOURCES} PARENT_SCOPE) +endmacro(GENERATE_THRIFT_LIB) + +if (WITH_THRIFT) + add_compile_definitions(WITH_THRIFT) +endif (WITH_THRIFT) diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 10be63281c..3d813967c3 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -24,6 +24,26 @@ function(get_osx_architecture) endif() endfunction() +function(detect_host_triplet outvar) + string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" host_triplet_system) + string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" host_triplet_arch) + if(host_triplet_system STREQUAL "darwin") + set(host_triplet_system "osx") + endif() + message("host_triplet_system: ${host_triplet_system}") + message("host_triplet_arch: ${host_triplet_arch}") + if(host_triplet_arch MATCHES "(amd64|x86_64)") + set(host_triplet_arch "x64") + elseif(host_triplet_arch MATCHES "i[3456]86") + set(host_triplet_arch "x86") + elseif(host_triplet_arch MATCHES "(aarch64|armv8l|arm64)") + set(host_triplet_arch "arm64") + else() + message(FATAL_ERROR "Unrecognized CMAKE_HOST_SYSTEM_PROCESSOR: ${CMAKE_HOST_SYSTEM_PROCESSOR}") + endif() + set(${outvar} "${host_triplet_arch}-${host_triplet_system}" PARENT_SCOPE) +endfunction() + if (APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0) get_osx_architecture() diff --git a/include/interfaces/ThriftController.thrift b/include/interfaces/ThriftController.thrift new file mode 100644 index 0000000000..e1008121d1 --- /dev/null +++ b/include/interfaces/ThriftController.thrift @@ -0,0 +1,71 @@ +namespace cpp ThriftController + +struct Point { + 1: i32 x, + 2: i32 y, +} + +struct ClickParam { + 1: Point point, +} + +struct SwipeParam { + 1: Point point1, + 2: Point point2, + 3: i32 duration, +} + +struct TouchParam { + 1: i32 contact, + 2: Point point, +} + +enum InputEventType { + TOUCH_DOWN = 0, + TOUCH_UP = 1, + TOUCH_MOVE = 2, + TOUCH_RESET = 3, + KEY_DOWN = 4, + KEY_UP = 5, + WAIT_MS = 6, +} + +struct InputEvent { + 1: InputEventType type, + 2: TouchParam touch, + 3: i32 key, + 4: i32 wait_ms, +} + +struct Size { + 1: i32 width, + 2: i32 height, +} + +struct CustomImage { + 1: Size size, + 2: i32 type, + 3: binary data, +} + +service ThriftController { + bool connect(), + + bool set_option(1: string key, 2: string value), + + string get_uuid(), + CustomImage screencap(), + + bool start_game(1: string activity), + bool stop_game(1: string activity), + + bool click(1: ClickParam param), + bool swipe(1: SwipeParam param), + + bool inject_input_events(1: list events), + + bool press_key(1: i32 param), + i64 get_support_features(), + + Size get_screen_res(), +} diff --git a/src/MaaCore/Controller/MaaThriftController.cpp b/src/MaaCore/Controller/MaaThriftController.cpp new file mode 100644 index 0000000000..b6e4dc4194 --- /dev/null +++ b/src/MaaCore/Controller/MaaThriftController.cpp @@ -0,0 +1,580 @@ +#ifdef WITH_THRIFT + +#include "MaaThriftController.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4245 4706) +#endif +#include +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "Config/GeneralConfig.h" +#include "Utils/NoWarningCV.h" + +asst::MaaThriftController::~MaaThriftController() +{ + LogTraceFunction; + close(); +} + +bool asst::MaaThriftController::connect([[maybe_unused]] const std::string& adb_path, + [[maybe_unused]] const std::string& address, const std::string& config) +{ + LogTraceFunction; + + clear_info(); + +#ifdef ASST_DEBUG + if (config == "DEBUG") { + m_inited = true; + return true; + } +#endif + + auto get_info_json = [&]() -> json::value { + return json::object { + { "uuid", m_uuid }, + { "details", + json::object { + { "config", config }, + } }, + }; + }; + + auto config_error = [&]() { + json::value info = get_info_json() | json::object { + { "what", "ConnectFailed" }, + { "why", "ConfigError" }, + { "config", config }, + }; + callback(AsstMsg::ConnectionInfo, info); + }; + + static const std::unordered_map type_map = { + { "Socket", ThriftControllerTypeEnum::MaaThriftControllerType_Socket }, + { "UnixDomainSocket", ThriftControllerTypeEnum::MaaThriftControllerType_UnixDomainSocket }, + }; + + using namespace apache::thrift; + + std::shared_ptr socket; + + auto param_json = json::parse(config); + if (!param_json.has_value()) { + json::value info = get_info_json() | json::object { + { "what", "ConnectFailed" }, + { "why", "ConfigError" }, + { "config", config }, + }; + callback(AsstMsg::ConnectionInfo, info); + return false; + } + + auto type = param_json->at("type").as_string(); + + switch (type_map.at(type)) { + case ThriftControllerTypeEnum::MaaThriftControllerType_Socket: + if (param_json->at("param").is_object()) { + auto host = param_json->at("param").at("host").as_string(); + auto port = param_json->at("param").at("port").as_integer(); + socket = std::make_shared(host, port); + } + else { + config_error(); + return false; + } + break; + case ThriftControllerTypeEnum::MaaThriftControllerType_UnixDomainSocket: + if (param_json->at("param").is_string()) { + auto path = param_json->at("param").as_string(); + socket = std::make_shared(path); + } + else { + config_error(); + return false; + } + break; + default: + config_error(); + return false; + } + + transport_ = std::make_shared(socket); + auto protocol = std::make_shared(transport_); + + client_ = std::make_shared(protocol); + + try { + transport_->open(); + } + catch (const std::exception& e) { + Log.error("Cannot open transport: {}", e.what()); + json::value info = get_info_json() | json::object { + { "what", "ConnectFailed" }, + { "why", "TransportError" }, + { "config", config }, + }; + callback(AsstMsg::ConnectionInfo, info); + return false; + } + + auto connect_ret = false; + try { + connect_ret = client_->connect(); + } + catch (const std::exception& e) { + Log.error("Cannot connect to MaaThriftController:", e.what()); + return false; + } + + if (!connect_ret) { + Log.error("Cannot connect to MaaThriftController"); + json::value info = get_info_json() | json::object { + { "what", "ConnectFailed" }, + { "why", "ConnectError" }, + { "config", config }, + }; + callback(AsstMsg::ConnectionInfo, info); + return false; + } + + m_config = config; + + if (need_exit()) { + return false; + } + + /* uuid */ + { + try { + client_->get_uuid(m_uuid); + } + catch (const std::exception& e) { + Log.error("Cannot get uuid:", e.what()); + return false; + } + + if (!m_uuid.empty()) { + json::value info = get_info_json() | json::object { + { "what", "UuidGot" }, + { "why", "" }, + }; + info["details"]["uuid"] = m_uuid; + callback(AsstMsg::ConnectionInfo, info); + } + else { + json::value info = get_info_json() | json::object { + { "what", "ConnectFailed" }, + { "why", "Uuid Empty" }, + }; + callback(AsstMsg::ConnectionInfo, info); + } + } + + if (need_exit()) { + return false; + } + + /* display */ + { + ThriftController::Size screen_res; + try { + client_->get_screen_res(screen_res); + } + catch (const std::exception& e) { + Log.error("Cannot get screen resolution:", e.what()); + return false; + } + auto width = screen_res.width; + auto height = screen_res.height; + + json::value info = get_info_json() | json::object { + { "what", "ResolutionGot" }, + { "why", "" }, + }; + + info["details"] |= json::object { + { "width", width }, + { "height", height }, + }; + callback(AsstMsg::ConnectionInfo, info); + + if (width == 0 || height == 0) { + info["what"] = "ResolutionError"; + info["why"] = "Get resolution failed"; + callback(AsstMsg::ConnectionInfo, info); + return false; + } + m_screen_size = { width, height }; + } + + if (need_exit()) { + return false; + } + + { + try { + m_support_features = static_cast(client_->get_support_features()); + } + catch (const std::exception& e) { + Log.error("Cannot get support features:", e.what()); + return false; + } + } + + { + json::value info = get_info_json() | json::object { + { "what", "Connected" }, + { "why", "" }, + }; + callback(AsstMsg::ConnectionInfo, info); + } + + return true; +} + +bool asst::MaaThriftController::inited() const noexcept +{ + return m_inited; +} + +void asst::MaaThriftController::set_swipe_with_pause(bool enable) noexcept +{ + m_swipe_with_pause_enabled = enable; +} + +const std::string& asst::MaaThriftController::get_uuid() const +{ + return m_uuid; +} + +bool asst::MaaThriftController::screencap(cv::Mat& image_payload, [[maybe_unused]] bool allow_reconnect) +{ + if (!client_ || !transport_ || !transport_->isOpen()) { + Log.error("client_ is not created or transport_ is not open"); + return false; + } + + ThriftController::CustomImage img; + try { + client_->screencap(img); + } + catch (const std::exception& e) { + Log.error("Cannot get screencap:", e.what()); + return false; + } + + if (img.data.empty()) { + Log.error("client_->screencap() return empty buffer"); + return false; + } + cv::Mat orig_mat(img.size.height, img.size.width, img.type, img.data.data()); + orig_mat.copyTo(image_payload); + return true; +} + +bool asst::MaaThriftController::start_game(const std::string& client_type) +{ + if (!client_ || !transport_ || !transport_->isOpen()) { + Log.error("client_ is not created or transport_ is not open"); + return false; + } + + if (client_type.empty()) { + return false; + } + auto intent_name = Config.get_intent_name(client_type); + if (!intent_name) { + return false; + } + + try { + return client_->start_game(*intent_name); + } + catch (const std::exception& e) { + Log.error("Cannot start game:", e.what()); + return false; + } +} + +bool asst::MaaThriftController::stop_game() +{ + return false; +} + +bool asst::MaaThriftController::click(const Point& p) +{ + if (!client_ || !transport_ || !transport_->isOpen()) { + Log.error("client_ is not created or transport_ is not open"); + return false; + } + + ThriftController::ClickParam click_param; + click_param.point.x = p.x; + click_param.point.y = p.y; + + return client_->click(click_param); +} + +bool asst::MaaThriftController::swipe(const Point& p1, const Point& p2, int duration, bool extra_swipe, double slope_in, + double slope_out, bool with_pause) +{ + if (!client_ || !transport_ || !transport_->isOpen()) { + Log.error("client_ is not created or transport_ is not open"); + return false; + } + + if (!support_precise_swipe()) { + ThriftController::SwipeParam swipe_param; + swipe_param.point1.x = p1.x; + swipe_param.point1.y = p1.y; + swipe_param.point2.x = p2.x; + swipe_param.point2.y = p2.y; + swipe_param.duration = duration; + try { + return client_->swipe(swipe_param); + } + catch (const std::exception& e) { + Log.error("Cannot swipe:", e.what()); + return false; + } + } + + int x1 = p1.x, y1 = p1.y; + int x2 = p2.x, y2 = p2.y; + + const auto width = m_screen_size.first; + const auto height = m_screen_size.second; + + // 起点不能在屏幕外,但是终点可以 + if (x1 < 0 || x1 >= width || y1 < 0 || y1 >= height) { + Log.warn("swipe point1 is out of range", x1, y1); + x1 = std::clamp(x1, 0, width - 1); + y1 = std::clamp(y1, 0, height - 1); + } + + toucher_down(p1); + + constexpr int TimeInterval = DefaultSwipeDelay; + + auto cubic_spline = [](double slope_0, double slope_1, double t) { + const double a = slope_0; + const double b = -(2 * slope_0 + slope_1 - 3); + const double c = -(-slope_0 - slope_1 + 2); + return a * t + b * std::pow(t, 2) + c * std::pow(t, 3); + }; // TODO: move this to math.hpp + + bool need_pause = with_pause && use_swipe_with_pause(); + const auto& opt = Config.get_options(); + std::future pause_future; + auto progressive_move = [&](int _x1, int _y1, int _x2, int _y2, int _duration) { + for (int cur_time = TimeInterval; cur_time < _duration; cur_time += TimeInterval) { + double progress = cubic_spline(slope_in, slope_out, static_cast(cur_time) / duration); + int cur_x = static_cast(std::lerp(_x1, _x2, progress)); + int cur_y = static_cast(std::lerp(_y1, _y2, progress)); + if (need_pause && std::sqrt(std::pow(cur_x - _x1, 2) + std::pow(cur_y - _y1, 2)) > + opt.swipe_with_pause_required_distance) { + need_pause = false; + pause_future = std::async(std::launch::async, [&]() { press_esc(); }); + } + if (cur_x < 0 || cur_x > width || cur_y < 0 || cur_y > height) { + continue; + } + toucher_move({ cur_x, cur_y }); + } + if (_x2 >= 0 && _x2 <= width && _y2 >= 0 && _y2 <= height) { + toucher_move({ _x2, _y2 }); + } + }; + + progressive_move(x1, y1, x2, y2, duration ? duration : opt.minitouch_swipe_default_duration); + + if (extra_swipe && opt.minitouch_extra_swipe_duration > 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(opt.minitouch_swipe_extra_end_delay)); // 停留终点 + progressive_move(x2, y2, x2, y2 - opt.minitouch_extra_swipe_dist, opt.minitouch_extra_swipe_duration); + } + bool ret = toucher_up(p2); + return ret; +} + +bool asst::MaaThriftController::inject_input_event(const InputEvent& event) +{ + if (!client_ || !transport_ || !transport_->isOpen()) { + Log.error("client_ is not created or transport_ is not open"); + return false; + } + + bool ret = false; + + ThriftController::InputEvent input_event; + switch (event.type) { + case InputEvent::Type::TOUCH_DOWN: + input_event.type = ThriftController::InputEventType::TOUCH_DOWN; + break; + case InputEvent::Type::TOUCH_UP: + input_event.type = ThriftController::InputEventType::TOUCH_UP; + break; + case InputEvent::Type::TOUCH_MOVE: + input_event.type = ThriftController::InputEventType::TOUCH_MOVE; + break; + case InputEvent::Type::TOUCH_RESET: + input_event.type = ThriftController::InputEventType::TOUCH_RESET; + break; + case InputEvent::Type::KEY_DOWN: + input_event.type = ThriftController::InputEventType::KEY_DOWN; + break; + case InputEvent::Type::KEY_UP: + input_event.type = ThriftController::InputEventType::KEY_UP; + break; + case InputEvent::Type::WAIT_MS: + input_event.type = ThriftController::InputEventType::WAIT_MS; + break; + case InputEvent::Type::COMMIT: + try { + ret = client_->inject_input_events(m_input_events); + } + catch (const std::exception& e) { + Log.error("Cannot inject input events:", e.what()); + } + m_input_events.clear(); + return ret; + case InputEvent::Type::UNKNOWN: + default: + Log.error("unknown input event type"); + return false; + } + + input_event.touch.point.x = event.point.x; + input_event.touch.point.y = event.point.y; + input_event.touch.contact = event.pointerId; + input_event.key = event.keycode; + input_event.wait_ms = event.milisec; + m_input_events.push_back(input_event); + return true; +} + +bool asst::MaaThriftController::press_esc() +{ + if (!client_ || !transport_ || !transport_->isOpen()) { + Log.error("client_ is not created or transport_ is not open"); + return false; + } + + try { + return client_->press_key(111); + } + catch (const std::exception& e) { + Log.error("Cannot press esc:", e.what()); + return false; + } +} + +asst::ControlFeat::Feat asst::MaaThriftController::support_features() const noexcept +{ + return m_support_features; +} + +std::pair asst::MaaThriftController::get_screen_res() const noexcept +{ + return m_screen_size; +} + +void asst::MaaThriftController::clear_info() noexcept +{ + m_inited = false; + m_screen_size = { 0, 0 }; + m_uuid.clear(); + m_config.clear(); + m_support_features = ControlFeat::NONE; + m_swipe_with_pause_enabled = false; + m_input_events.clear(); + close(); + client_.reset(); + transport_.reset(); +} + +void asst::MaaThriftController::callback(AsstMsg msg, const json::value& details) +{ + if (m_callback) { + m_callback(msg, details, m_inst); + } +} + +bool asst::MaaThriftController::support_precise_swipe() const noexcept +{ + return m_support_features & ControlFeat::PRECISE_SWIPE; +} + +bool asst::MaaThriftController::support_swipe_with_pause() const noexcept +{ + return m_support_features & ControlFeat::SWIPE_WITH_PAUSE; +} + +bool asst::MaaThriftController::use_swipe_with_pause() const noexcept +{ + return m_swipe_with_pause_enabled && support_swipe_with_pause(); +} + +bool asst::MaaThriftController::toucher_down(const Point& p, const int delay, int contact) +{ + InputEvent event; + event.type = InputEvent::Type::TOUCH_DOWN; + event.point = p; + event.pointerId = contact; + inject_input_event(event); + event.type = InputEvent::Type::COMMIT; + auto ret = inject_input_event(event); + if (ret) { + toucher_wait(delay); + } + return ret; +} + +bool asst::MaaThriftController::toucher_move(const Point& p, const int delay, int contact) +{ + InputEvent event; + event.type = InputEvent::Type::TOUCH_MOVE; + event.point = p; + event.pointerId = contact; + inject_input_event(event); + event.type = InputEvent::Type::COMMIT; + auto ret = inject_input_event(event); + if (ret) { + toucher_wait(delay); + } + return ret; +} + +bool asst::MaaThriftController::toucher_up(const Point& p, const int delay, int contact) +{ + InputEvent event; + event.type = InputEvent::Type::TOUCH_UP; + event.point = p; + event.pointerId = contact; + inject_input_event(event); + event.type = InputEvent::Type::COMMIT; + auto ret = inject_input_event(event); + if (ret) { + toucher_wait(delay); + } + return ret; +} + +void asst::MaaThriftController::toucher_wait(const int delay) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(delay)); +} + +void asst::MaaThriftController::close() +{ + if (transport_) { + transport_->close(); + } +} + +#endif // WITH_THRIFT diff --git a/src/MaaCore/Controller/MaaThriftController.h b/src/MaaCore/Controller/MaaThriftController.h new file mode 100644 index 0000000000..dee348bf7e --- /dev/null +++ b/src/MaaCore/Controller/MaaThriftController.h @@ -0,0 +1,103 @@ +#pragma once + +#ifdef WITH_THRIFT + +#include "ControllerAPI.h" + +#include "Platform/PlatformFactory.h" + +#include "Common/AsstMsg.h" +#include "InstHelper.h" + +#include "ThriftController.h" +#include "ThriftController_types.h" + +namespace asst +{ + class MaaThriftController : public ControllerAPI, protected InstHelper + { + public: + MaaThriftController(const AsstCallback& callback, Assistant* inst, [[maybe_unused]] PlatformType type) + : InstHelper(inst), m_callback(callback) + { + LogTraceFunction; + } + MaaThriftController(const MaaThriftController&) = delete; + MaaThriftController(MaaThriftController&&) = delete; + virtual ~MaaThriftController(); + + virtual bool connect([[maybe_unused]] const std::string& adb_path, [[maybe_unused]] const std::string& address, + const std::string& config) override; + virtual bool inited() const noexcept override; + + virtual void set_swipe_with_pause(bool enable) noexcept override; + + virtual const std::string& get_uuid() const override; + + virtual bool screencap(cv::Mat& image_payload, bool allow_reconnect = false) override; + + virtual bool start_game(const std::string& client_type) override; + virtual bool stop_game() override; + + virtual bool click(const Point& p) override; + + virtual bool swipe(const Point& p1, const Point& p2, int duration = 0, bool extra_swipe = false, + double slope_in = 1, double slope_out = 1, bool with_pause = false) override; + + virtual bool inject_input_event(const InputEvent& event) override; + + virtual bool press_esc() override; + virtual ControlFeat::Feat support_features() const noexcept override; + + virtual std::pair get_screen_res() const noexcept override; + + MaaThriftController& operator=(const MaaThriftController&) = delete; + MaaThriftController& operator=(MaaThriftController&&) = delete; + + protected: + virtual void clear_info() noexcept; + void callback(AsstMsg msg, const json::value& details); + + bool support_precise_swipe() const noexcept; + bool support_swipe_with_pause() const noexcept; + bool use_swipe_with_pause() const noexcept; + + AsstCallback m_callback; + + std::string m_config; + std::string m_uuid; + ControlFeat::Feat m_support_features = ControlFeat::NONE; + bool m_inited = false; + bool m_swipe_with_pause_enabled = false; + std::pair m_screen_size = { 0, 0 }; + std::vector m_input_events; + + static constexpr int DefaultClickDelay = 50; + static constexpr int DefaultSwipeDelay = 5; + + bool toucher_down(const Point& p, const int delay = DefaultClickDelay, int contact = 0); + bool toucher_move(const Point& p, const int delay = DefaultSwipeDelay, int contact = 0); + bool toucher_up(const Point& p, const int delay = DefaultClickDelay, int contact = 0); + + void toucher_wait(const int delay); + + std::shared_ptr client_ = nullptr; + std::shared_ptr transport_ = nullptr; + + enum ThriftControllerTypeEnum + { + // param format should be "host:port" + MaaThriftControllerType_Socket = 1, + + // param should be unix domain socket path + MaaThriftControllerType_UnixDomainSocket = 2, + }; + + private: + static constexpr int MinimalVersion = 2; + void close(); + bool open(); + }; +} // namespace asst + +#endif // WITH_THRIFT diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj index d4a786f4a7..39741b1b95 100644 --- a/src/MaaCore/MaaCore.vcxproj +++ b/src/MaaCore/MaaCore.vcxproj @@ -54,6 +54,7 @@ + @@ -223,6 +224,7 @@ + diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters index 10a3abaf35..c2157f5ddc 100644 --- a/src/MaaCore/MaaCore.vcxproj.filters +++ b/src/MaaCore/MaaCore.vcxproj.filters @@ -534,6 +534,9 @@ Source\Controller + + Source\Controller + Source\Controller @@ -971,6 +974,9 @@ Source\Controller\adb-lite + + Source\Controller + Source\Controller diff --git a/src/MaaThriftController/CMakeLists.txt b/src/MaaThriftController/CMakeLists.txt new file mode 100644 index 0000000000..8b65ac5c1e --- /dev/null +++ b/src/MaaThriftController/CMakeLists.txt @@ -0,0 +1,5 @@ +GENERATE_THRIFT_LIB(MaaThriftController + ${PROJECT_SOURCE_DIR}/include/interfaces/ThriftController.thrift + ${CMAKE_CURRENT_BINARY_DIR}/MaaThriftController maa_thrift_controller_src) + +source_group(TREE ${CMAKE_CURRENT_BINARY_DIR} FILES ${maa_thrift_controller_src}) diff --git a/src/MaaThriftController/Directory.Build.props b/src/MaaThriftController/Directory.Build.props new file mode 100644 index 0000000000..e83e25b35e --- /dev/null +++ b/src/MaaThriftController/Directory.Build.props @@ -0,0 +1,6 @@ + + + $(MSBuildThisFileDirectory)..\..\MaaDeps\msbuild\maadeps.props + + + diff --git a/src/MaaThriftController/Directory.Build.targets b/src/MaaThriftController/Directory.Build.targets new file mode 100644 index 0000000000..9d5887d0cd --- /dev/null +++ b/src/MaaThriftController/Directory.Build.targets @@ -0,0 +1,14 @@ + + + $(MSBuildThisFileDirectory)..\..\MaaDeps\msbuild\maadeps.targets + $(MSBuildThisFileDirectory)..\..\MSBUILD_DISABLE_MAADEPS + true + true + Missing third-party dependencies, run `python maadeps-download.py`. / 缺少第三方依赖,请运行 `python maadeps-download.py`。 +Alternatively, run `python maadeps-build.py` to build third-party dependencies from source, or create a file named `MSBUILD_DISABLE_MAADEPS` next to MAA.sln file and bring your own libraries to MSBuild. + + + + + + diff --git a/src/MaaThriftController/MaaThriftController.vcxproj b/src/MaaThriftController/MaaThriftController.vcxproj new file mode 100644 index 0000000000..2c66d64dd2 --- /dev/null +++ b/src/MaaThriftController/MaaThriftController.vcxproj @@ -0,0 +1,305 @@ + + + + + Debug + ARM64 + + + Debug + x64 + + + Release + ARM64 + + + Release + x64 + + + RelWithDebInfo + ARM64 + + + RelWithDebInfo + x64 + + + + + Document + mkdir "$(OutDirFullPath)$(ProjectName)" +..\..\MaaDeps\vcpkg\installed\maa-$(PreferredToolArchitecture)-windows\tools\thrift\thrift.exe --gen cpp:no_skeleton --out "$(OutDirFullPath)$(ProjectName)" "%(FullPath)" + Generate ThriftController + $(OutDirFullPath)$(ProjectName)\%(Filename).cpp;$(OutDirFullPath)$(ProjectName)\%(Filename).h;$(OutDirFullPath)$(ProjectName)\%(Filename)_types.cpp;$(OutDirFullPath)$(ProjectName)\%(Filename)_types.h + true + + + + + + + + + + + + 16.0 + Win32Proj + {8238edb1-19bf-4e3b-af5b-016120d59d7b} + 10.0 + x64 + + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + false + v143 + false + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + false + v143 + false + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + false + $(SolutionDir)\$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)\$(Platform)\$(Configuration)\ + + + + Level4 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + stdc11 + MultiThreadedDLL + MaxSpeed + /utf-8 /MP $(ExternalCompilerOptions) %(AdditionalOptions) + true + 19.35.32217 + $(ProjectDir);%(AdditionalIncludeDirectories) + + + Console + true + true + true + ws2_32.lib;thriftmd.lib;%(AdditionalDependencies) + true + /ignore:4286 %(AdditionalOptions) + + + + + Level4 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + stdc11 + MultiThreadedDLL + MaxSpeed + /utf-8 /MP $(ExternalCompilerOptions) %(AdditionalOptions) + true + 19.35.32217 + + + Console + true + true + true + ws2_32.lib;thriftmd.lib;%(AdditionalDependencies) + true + /ignore:4286 %(AdditionalOptions) + + + + + Level4 + false + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + stdc11 + MultiThreadedDLL + Disabled + EnableFastChecks + /utf-8 /MP $(ExternalCompilerOptions) %(AdditionalOptions) + true + 19.35.32217 + $(ProjectDir);%(AdditionalIncludeDirectories) + + + Console + true + true + true + ws2_32.lib;thriftmd.lib;%(AdditionalDependencies) + true + + + + + Level4 + false + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + stdc11 + MultiThreadedDLL + Disabled + true + EnableFastChecks + /utf-8 /MP $(ExternalCompilerOptions) %(AdditionalOptions) + true + 19.35.32217 + + + Console + true + true + true + ws2_32.lib;thriftmd.lib;%(AdditionalDependencies) + true + + + + + Level4 + false + true + _CONSOLE;_DEBUG;%(PreprocessorDefinitions) + true + stdcpp20 + stdc11 + MultiThreadedDebugDLL + Disabled + EnableFastChecks + /utf-8 /MP $(ExternalCompilerOptions) %(AdditionalOptions) + true + EditAndContinue + $(ProjectDir);%(AdditionalIncludeDirectories) + + + Console + true + ws2_32.lib;thriftmdd.lib;%(AdditionalDependencies) + + + + + Level4 + false + true + _CONSOLE;_DEBUG;%(PreprocessorDefinitions) + true + stdcpp20 + stdc11 + MultiThreadedDebugDLL + Disabled + EnableFastChecks + /utf-8 /MP $(ExternalCompilerOptions) %(AdditionalOptions) + true + EditAndContinue + $(ProjectDir);%(AdditionalIncludeDirectories) + + + Console + true + ws2_32.lib;thriftmdd.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file