diff --git a/resource/config.json b/resource/config.json index bf793c7fe3..b189652954 100644 --- a/resource/config.json +++ b/resource/config.json @@ -26,115 +26,131 @@ "General": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell dumpsys window displays | grep init=", + "connect": "[Adb] connect [AdbSerial]", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init=", "displayFormat": " init=%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] exec-out \"screencap | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] exec-out screencap -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" }, "BlueStacks": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '", + "connect": "[Adb] connect [AdbSerial]", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '", "displayFormat": "cur=%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] exec-out \"screencap | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] exec-out screencap -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" }, "MuMuEmulator": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '", + "connect": "[Adb] connect [AdbSerial]", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '", "displayFormat": "cur=%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] exec-out \"screencap | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] exec-out screencap -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" }, "LDPlayer": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '", + "connect": "[Adb] connect [AdbSerial]", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '", "displayFormat": "cur=%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] shell \"screencap | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] shell screencap -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] shell \"screencap | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] shell screencap -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" }, "Nox": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell dumpsys window displays | grep init= | awk ' { print $3 } '", + "connect": "[Adb] connect [AdbSerial]", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init= | awk ' { print $3 } '", "displayFormat": "cur=%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] exec-out \"screencap | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] exec-out screencap -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" }, "XYAZ": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell dumpsys window displays | grep init=", + "connect": "[Adb] connect [AdbSerial]", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init=", "displayFormat": " init=%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] shell \"screencap | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] shell screencap -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] shell \"screencap | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] shell screencap -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" }, "WSA": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "displayId": "[Adb] -s [Address] shell dumpsys display | grep mUniqueId=virtual:com.microsoft.windows.systemapp:com.hypergryph.arknights", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input -d [DisplayId] tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input -d [DisplayId] swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell \"wm size -d [DisplayId]\"", + "connect": "[Adb] connect [AdbSerial]", + "displayId": "[Adb] -s [AdbSerial] shell dumpsys display | grep mUniqueId=virtual:com.microsoft.windows.systemapp:com.hypergryph.arknights", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input -d [DisplayId] tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input -d [DisplayId] swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell \"wm size -d [DisplayId]\"", "displayFormat": "Physical size:%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] shell \"screencap -d [DisplayId] | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] shell screencap -d [DisplayId] -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] shell \"screencap -d [DisplayId] | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] shell screencap -d [DisplayId] -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" }, "Compatible": { "devices": "[Adb] devices", "addressRegex": "(.+)\tdevice", - "connect": "[Adb] connect [Address]", - "uuid": "[Adb] -s [Address] shell settings get secure android_id", - "click": "[Adb] -s [Address] shell input tap [x] [y]", - "swipe": "[Adb] -s [Address] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s [Address] shell dumpsys window displays | grep init=", + "connect": "[Adb] connect [AdbSerial]", + "uuid": "[Adb] -s [AdbSerial] shell settings get secure android_id", + "click": "[Adb] -s [AdbSerial] shell input tap [x] [y]", + "swipe": "[Adb] -s [AdbSerial] shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s [AdbSerial] shell dumpsys window displays | grep init=", "displayFormat": " init=%dx%d", - "screencapRawWithGzip": "[Adb] -s [Address] exec-out \"screencap | gzip -1\"", - "screencapEncode": "[Adb] -s [Address] exec-out screencap -p", + "screencapRawByNC": "[Adb] -s [AdbSerial] exec-out \"screencap | nc -w 3 [NcAddress] [NcPort]\"", + "ncPort": 6723, + "screencapRawWithGzip": "[Adb] -s [AdbSerial] exec-out \"screencap | gzip -1\"", + "screencapEncode": "[Adb] -s [AdbSerial] exec-out screencap -p", "release": "[Adb] kill-server", - "start": "[Adb] -s [Address] shell am start -n [Intent]" + "start": "[Adb] -s [AdbSerial] shell am start -n [Intent]" } } } diff --git a/resource/tasks.json b/resource/tasks.json index 69659ff6f6..e60f524c2b 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -4544,9 +4544,18 @@ 30 ] }, - "BattleWaitingToLoad": { - "algorithm": "JustReturn", - "rearDelay": 3000 + "BattleOfficiallyBegin": { + "action": "DoNothing", + "roi": [ + 1165, + 20, + 65, + 65 + ], + "maskRange": [ + 100, + 255 + ] }, "BattleSpeedUp": { "algorithm": "JustReturn", @@ -4590,17 +4599,22 @@ 600, 60 ], - "rectMove": [ - 50, - 0, - 150, - 60 - ], "maskRange": [ 1, 255 ] }, + "BattleKills": { + "algorithm": "OcrDetect", + "text": [ + ], + "roi": [ + 50, + 0, + 100, + 40 + ] + }, "BattleCostData": { "algorithm": "OcrDetect", "text": [], @@ -4639,10 +4653,6 @@ -110, 90, 90 - ], - "next": [ - "BattleSkillReadyOnClick", - "Stop" ] }, "BattleSkillStopOnClick": { @@ -4659,23 +4669,19 @@ -110, 90, 90 - ], - "next": [ - "BattleSkillStopOnClick", - "Stop" ] }, "BattleUseOper": { "algorithm": "JustReturn", - "preDelay": 500, + "preDelay": 200, "rearDelay": 0, "Doc": "pre 是从点击干员,到干员信息弹出来等待的时间;rear 是干员上场,到设置朝向之间等待的时间" }, "BattleSwipeOper": { "algorithm": "JustReturn", - "preDelay": 1000, - "rearDelay": 300, - "Doc": "pre 是将干员滑动到场上的 duration;rear 是设置干员朝向滑动的 duration" + "preDelay": 12000, + "rearDelay": 100, + "Doc": "pre 是将干员滑动到场上的 duration 系数;rear 是设置干员朝向滑动的 duration" }, "BattleOpersFlag": { "templThreshold": 0.85, @@ -4984,25 +4990,9 @@ 40 ] }, - "BattleUseSkillBegin": { - "algorithm": "JustReturn", - "action": "DoNothing", - "rearDelay": 1000, - "next": [ - "BattleUseSkill" - ] - }, "BattleUseSkill": { - "algorithm": "OcrDetect", - "text": [ - "角色" - ], - "roi": [ - 900, - 500, - 380, - 150 - ], + "algorithm": "JustReturn", + "rearDelay": 100, "action": "ClickRect", "specificRect": [ 820, @@ -5011,25 +5001,9 @@ 90 ] }, - "BattleOperRetreatBegin": { - "algorithm": "JustReturn", - "rearDelay": 500, - "next": [ - "BattleOperRetreat" - ] - }, "BattleOperRetreat": { - "algorithm": "OcrDetect", - "text": [ - "角色" - ], - "roi": [ - 900, - 500, - 380, - 150 - ], - "rearDelay": 500, + "algorithm": "JustReturn", + "rearDelay": 100, "action": "ClickRect", "specificRect": [ 563, diff --git a/resource/template/BattleOfficiallyBegin.png b/resource/template/BattleOfficiallyBegin.png new file mode 100644 index 0000000000..7e60d42436 Binary files /dev/null and b/resource/template/BattleOfficiallyBegin.png differ diff --git a/src/MeoAssistant/BattleImageAnalyzer.cpp b/src/MeoAssistant/BattleImageAnalyzer.cpp index 75deef6198..93e53e3a7b 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.cpp +++ b/src/MeoAssistant/BattleImageAnalyzer.cpp @@ -4,8 +4,7 @@ #include "MultiMatchImageAnalyzer.h" #include "MatchImageAnalyzer.h" -#include "OcrImageAnalyzer.h" -#include "OcrWithPreprocessImageAnalyzer.h" +#include "OcrWithFlagTemplImageAnalyzer.h" #include "HashImageAnalyzer.h" #include "Logger.hpp" #include "TaskData.h" @@ -430,20 +429,14 @@ bool asst::BattleImageAnalyzer::hp_analyze() bool asst::BattleImageAnalyzer::kills_analyze() { - const auto task_ptr = Task.get("BattleKillsFlag"); - MatchImageAnalyzer flag_analzyer(m_image); - flag_analzyer.set_task_info(task_ptr); - if (!flag_analzyer.analyze()) { - return false; - } + OcrWithFlagTemplImageAnalyzer kills_analyzer(m_image); + kills_analyzer.set_task_info("BattleKillsFlag", "BattleKills"); + kills_analyzer.set_replace( + std::dynamic_pointer_cast(Task.get("NumberOcrReplace"))->replace_map); - auto kills_roi = flag_analzyer.get_result().rect.move(task_ptr->rect_move); - OcrImageAnalyzer kills_analyzer(m_image); - kills_analyzer.set_roi(kills_roi); if (!kills_analyzer.analyze()) { return false; } - kills_analyzer.sort_result_by_score(); std::string kills_text; size_t pos = std::string::npos; diff --git a/src/MeoAssistant/BattleProcessTask.cpp b/src/MeoAssistant/BattleProcessTask.cpp index cbd4207bfc..c1db41a417 100644 --- a/src/MeoAssistant/BattleProcessTask.cpp +++ b/src/MeoAssistant/BattleProcessTask.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "Controller.h" #include "Resource.h" @@ -53,6 +54,8 @@ bool asst::BattleProcessTask::_run() bool asst::BattleProcessTask::get_stage_info() { + LogTraceFunction; + const auto& tile = Resrc.tile(); m_normal_tile_info = tile.calc(m_stage_name, false); @@ -84,49 +87,64 @@ bool asst::BattleProcessTask::analyze_opers_preview() callback(AsstMsg::SubTaskExtraInfo, info); } + MatchImageAnalyzer officially_begin_analyzer; + officially_begin_analyzer.set_task_info("BattleOfficiallyBegin"); + cv::Mat image; + while (!need_exit()) { + image = m_ctrler->get_image(); + officially_begin_analyzer.set_image(image); + if (officially_begin_analyzer.analyze()) { + break; + } + std::this_thread::yield(); + } + BattleImageAnalyzer oper_analyzer; oper_analyzer.set_target(BattleImageAnalyzer::Target::Oper); - - while (true) { - oper_analyzer.set_image(m_ctrler->get_image()); + while (!need_exit()) { + image = m_ctrler->get_image(); + oper_analyzer.set_image(image); if (oper_analyzer.analyze()) { break; } std::this_thread::yield(); } - // 干员头像出来之后,还要过 2 秒左右才可以点击,这里要加个延时 - sleep(Task.get("BattleWaitingToLoad")->rear_delay); - while (true) { - oper_analyzer.set_image(m_ctrler->get_image()); - if (oper_analyzer.analyze()) { + // 暂停游戏准备识别干员 + // 在刚进入游戏的时候(画面刚刚完全亮起来的时候),点暂停是没反应的 + // 所以这里一直点,直到真的点上了为止 + while (!need_exit()) { + battle_pause(); + image = m_ctrler->get_image(); + officially_begin_analyzer.set_image(image); + if (!officially_begin_analyzer.analyze()) { break; } std::this_thread::yield(); } - { - //#ifdef ASST_DEBUG - auto draw = m_ctrler->get_image(); + + auto draw_future = std::async(std::launch::async, [&]() { + auto draw = image.clone(); for (const auto& [loc, info] : m_normal_tile_info) { std::string text = "( " + std::to_string(loc.x) + ", " + std::to_string(loc.y) + " )"; cv::putText(draw, text, cv::Point(info.pos.x - 30, info.pos.y), 1, 1.2, cv::Scalar(0, 0, 255), 2); } cv::imwrite("map.png", draw); - //#endif - } - battle_pause(); + }); auto opers = oper_analyzer.get_opers(); + Rect cur_rect; + int click_delay = Task.get("BattleUseOper")->pre_delay; for (size_t i = 0; i != opers.size(); ++i) { const auto& cur_oper = oper_analyzer.get_opers(); size_t offset = opers.size() > cur_oper.size() ? opers.size() - cur_oper.size() : 0; - m_ctrler->click(cur_oper.at(i - offset).rect); + cur_rect = cur_oper.at(i - offset).rect; + m_ctrler->click(cur_rect); + sleep(click_delay); - sleep(Task.get("BattleUseOper")->pre_delay); - - auto image = m_ctrler->get_image(); + image = m_ctrler->get_image(); OcrWithPreprocessImageAnalyzer name_analyzer(image); name_analyzer.set_task_info("BattleOperName"); @@ -166,8 +184,12 @@ bool asst::BattleProcessTask::analyze_opers_preview() oper_analyzer.set_image(image); oper_analyzer.analyze(); } + + draw_future.wait(); + + m_ctrler->click(cur_rect); + sleep(click_delay); battle_pause(); - cancel_selection(); return true; } @@ -251,8 +273,9 @@ bool asst::BattleProcessTask::update_opers_info(const cv::Mat& image) oper_name = name_analyzer.get_result().front().text; } m_group_to_oper_mapping[oper_name] = BattleDeployOper{ oper_name }; + m_ctrler->click(cur_oper.rect); + sleep(Task.get("BattleUseOper")->pre_delay); battle_pause(); - cancel_selection(); } else { oper_name = matched_iter->first; @@ -362,7 +385,9 @@ bool asst::BattleProcessTask::wait_condition(const BattleAction& action) if (need_exit()) { return false; } - image = m_ctrler->get_image(); + if (image.empty()) { + image = m_ctrler->get_image(); + } BattleImageAnalyzer analyzer(image); analyzer.set_target(BattleImageAnalyzer::Target::Kills); if (analyzer.analyze()) { @@ -374,6 +399,7 @@ bool asst::BattleProcessTask::wait_condition(const BattleAction& action) try_possible_skill(image); std::this_thread::yield(); + image = m_ctrler->get_image(); } // 部署干员还有额外等待费用够或 CD 转好 @@ -383,7 +409,9 @@ bool asst::BattleProcessTask::wait_condition(const BattleAction& action) if (need_exit()) { return false; } - image = m_ctrler->get_image(); + if (image.empty()) { + image = m_ctrler->get_image(); + } update_opers_info(image); if (auto iter = m_cur_opers_info.find(name); @@ -393,6 +421,7 @@ bool asst::BattleProcessTask::wait_condition(const BattleAction& action) try_possible_skill(image); std::this_thread::yield(); + image = m_ctrler->get_image(); }; } if (image.empty()) { @@ -421,7 +450,12 @@ bool asst::BattleProcessTask::oper_deploy(const BattleAction& action) Point placed_point = m_side_tile_info[action.location].pos; Rect placed_rect{ placed_point.x ,placed_point.y, 1, 1 }; - m_ctrler->swipe(oper_rect, placed_rect, swipe_oper_task_ptr->pre_delay); + int dist = static_cast( + std::sqrt( + (std::abs(placed_point.x - oper_rect.x) << 1) + + (std::abs(placed_point.y - oper_rect.y) << 1))); + int duration = static_cast(swipe_oper_task_ptr->pre_delay / 1000.0 * dist); // 随便取的一个系数 + m_ctrler->swipe(oper_rect, placed_rect, duration, true, 0); sleep(use_oper_task_ptr->rear_delay); @@ -449,7 +483,7 @@ bool asst::BattleProcessTask::oper_deploy(const BattleAction& action) end_point.y = std::max(0, end_point.y); end_point.y = std::min(end_point.y, WindowHeightDefault); - m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->rear_delay); + m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->rear_delay, true, 100); } m_used_opers[iter->first] = BattleDeployInfo{ @@ -559,8 +593,3 @@ bool asst::BattleProcessTask::battle_speedup() { return ProcessTask(*this, { "BattleSpeedUp" }).run(); } - -bool asst::BattleProcessTask::cancel_selection() -{ - return ProcessTask(*this, { "BattleCancelSelection" }).run(); -} diff --git a/src/MeoAssistant/BattleProcessTask.h b/src/MeoAssistant/BattleProcessTask.h index 3b44c1ca27..0e6a3400a4 100644 --- a/src/MeoAssistant/BattleProcessTask.h +++ b/src/MeoAssistant/BattleProcessTask.h @@ -22,7 +22,6 @@ namespace asst virtual bool get_stage_info(); bool battle_pause(); bool battle_speedup(); - bool cancel_selection(); // 取消选择干员 bool analyze_opers_preview(); bool update_opers_info(const cv::Mat& image); diff --git a/src/MeoAssistant/Controller.cpp b/src/MeoAssistant/Controller.cpp index 2e39182a88..0f998bb678 100644 --- a/src/MeoAssistant/Controller.cpp +++ b/src/MeoAssistant/Controller.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -26,6 +27,33 @@ #include "Logger.hpp" #include "Resource.h" +#ifdef _WIN32 +// for Windows socket +class WsaHelper +{ +public: + ~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; +}; +#endif + asst::Controller::Controller(AsstCallback callback, void* callback_arg) : m_callback(callback), m_callback_arg(callback_arg), @@ -55,6 +83,16 @@ asst::Controller::Controller(AsstCallback callback, void* callback_arg) 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 (0); + #else int pipe_in_ret = pipe(m_pipe_in); int pipe_out_ret = pipe(m_pipe_out); @@ -64,11 +102,19 @@ asst::Controller::Controller(AsstCallback callback, void* callback_arg) throw "controller pipe created failed"; } + // 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_cmd_thread = std::thread(&Controller::pipe_working_proc, this); } @@ -86,13 +132,16 @@ asst::Controller::~Controller() m_cmd_thread.join(); } - disconnect(); + 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); + #else close(m_pipe_in[PIPE_READ]); close(m_pipe_in[PIPE_WRITE]); @@ -193,14 +242,13 @@ void asst::Controller::pipe_working_proc() } } -std::optional> asst::Controller::call_command(const std::string& cmd, int64_t timeout) +std::optional> asst::Controller::call_command(const std::string& cmd, int64_t timeout, bool recv_by_socket) { LogTraceFunction; std::vector pipe_data; - static std::mutex pipe_mutex; - std::unique_lock pipe_lock(pipe_mutex); + std::unique_lock pipe_lock(m_pipe_mutex); auto start_time = std::chrono::steady_clock::now(); auto check_timeout = [&]() -> bool { @@ -214,21 +262,39 @@ std::optional> asst::Controller::call_command(const s PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体 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 error, ret", create_ret); return std::nullopt; } - DWORD peek_num = 0; - DWORD read_num = 0; - 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); + if (!recv_by_socket) { + DWORD peek_num = 0; + DWORD read_num = 0; + 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()); - - DWORD exit_ret = 255; + } while (::WaitForSingleObject(process_info.hProcess, 0) == WAIT_TIMEOUT && !check_timeout()); + } + else { + SOCKADDR client_addr = { 0 }; + int size = sizeof(SOCKADDR); + SOCKET client_sock = ::accept(m_server_sock, (SOCKADDR*)&client_addr, &size); + 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, static_cast(timeout)); + } + DWORD exit_ret = 0; ::GetExitCodeProcess(process_info.hProcess, &exit_ret); ::CloseHandle(process_info.hProcess); ::CloseHandle(process_info.hThread); @@ -369,6 +435,12 @@ void asst::Controller::clear_info() noexcept m_height = 0; m_control_scale = 1.0; m_scale_size = { WindowWidthDefault, WindowHeightDefault }; + m_server_started = false; + if (m_support_socket && m_server_started && m_server_sock) { + ::closesocket(m_server_sock); + m_server_sock = 0U; + } + --m_instance_count; } int asst::Controller::push_cmd(const std::string& cmd) @@ -381,6 +453,54 @@ int asst::Controller::push_cmd(const std::string& cmd) return ++m_push_id; } +std::optional asst::Controller::try_init_socket(const std::string& local_address, unsigned short try_port, unsigned short try_times) +{ + 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()); +#else + // Linux, todo +#endif + + 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 bind port", port); + +#ifdef _WIN32 + m_server_addr.sin_port = htons(port); + int bind_ret = ::bind(m_server_sock, (SOCKADDR*)&m_server_addr, sizeof(SOCKADDR)); + int listen_ret = ::listen(m_server_sock, 3); + server_start = bind_ret == 0 && listen_ret == 0; +#else + // todo +#endif + if (server_start) { + port_result = port; + break; + } + } + + if (!server_start) { + Log.info("not supports netcat"); + return std::nullopt; + } + + // Todo: detect remote address, and check remote port + // reference from https://github.com/ArknightsAutoHelper/ArknightsAutoHelper/blob/master/automator/connector/ADBConnector.py#L436 + std::string remote_address = "10.0.2.2"; + Log.info("server_start", remote_address, port_result); + return SocketInfo{ remote_address, port_result }; +} + void asst::Controller::wait(unsigned id) const noexcept { while (id > m_completed_id) { @@ -399,7 +519,35 @@ bool asst::Controller::screencap() // return true; //} - DecodeFunc decode_raw_with_gzip = [&](const std::vector& data) -> bool { + DecodeFunc decode_raw = [&](std::vector& data) -> bool { + if (data.empty()) { + return false; + } + size_t std_size = m_width * m_height * 4; + if (data.size() < std_size) { + return false; + } + size_t header_size = data.size() - std_size; + Log.trace("header size:", header_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()); Log.trace("unzip data size:", unzip_data.size()); if (unzip_data.empty()) { @@ -429,7 +577,7 @@ bool asst::Controller::screencap() return true; }; - DecodeFunc decode_encode = [&](const std::vector& data) -> bool { + DecodeFunc decode_encode = [&](std::vector& data) -> bool { cv::Mat temp = cv::imdecode(data, cv::IMREAD_COLOR); if (temp.empty()) { return false; @@ -442,18 +590,31 @@ bool asst::Controller::screencap() switch (m_adb.screencap_method) { case AdbProperty::ScreencapMethod::UnknownYet: { - if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip)) { + if (m_support_socket && m_server_started && + screencap(m_adb.screencap_raw_by_nc, decode_raw, true)) { + m_adb.screencap_method = AdbProperty::ScreencapMethod::RawByNc; + Log.info("screencap by RawByNc"); + return true; + } + else if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip)) { m_adb.screencap_method = AdbProperty::ScreencapMethod::RawWithGzip; + Log.info("screencap by RawWithGzip"); return true; } else if (screencap(m_adb.screencap_encode, decode_encode)) { m_adb.screencap_method = AdbProperty::ScreencapMethod::Encode; + Log.info("screencap by Encode"); return true; } else { return false; } } + 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); @@ -469,9 +630,13 @@ bool asst::Controller::screencap() return false; } -bool asst::Controller::screencap(const std::string& cmd, DecodeFunc decode_func) +bool asst::Controller::screencap(const std::string& cmd, DecodeFunc decode_func, bool by_socket) { - auto ret = call_command(cmd); + if ((!m_support_socket || !m_server_started) && by_socket) { + return false; + } + + auto ret = call_command(cmd, 20000, by_socket); if (!ret || ret.value().empty()) { Log.error("data is empty!"); @@ -485,6 +650,7 @@ bool asst::Controller::screencap(const std::string& cmd, DecodeFunc decode_func) 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; @@ -498,6 +664,7 @@ bool asst::Controller::screencap(const std::string& cmd, DecodeFunc decode_func) if (decode_func(data)) { m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::CRLF; + Log.info("screencap_end_of_line is CRLF"); return true; } else { @@ -680,12 +847,16 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a const auto adb_cfg = std::move(adb_ret.value()); std::string display_id; + std::string nc_address; + u_short nc_port = 0; auto cmd_replace = [&](const std::string& cfg_cmd) -> std::string { const std::unordered_map replacements = { { "[Adb]", adb_path }, - { "[Address]", address }, - { "[DisplayId]", display_id } + { "[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) { @@ -845,11 +1016,43 @@ bool asst::Controller::connect(const std::string& adb_path, const std::string& a 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"; + } + + auto socket_opt = try_init_socket(bind_address, adb_cfg.nc_port); + if (socket_opt) { + auto& socket_info = socket_opt.value(); + nc_address = socket_info.remote_address; + nc_port = socket_info.remote_port; + m_adb.screencap_raw_by_nc = cmd_replace(adb_cfg.screencap_raw_by_nc); + m_server_started = true; + } + else { + m_server_started = false; + } + } + + ++m_instance_count; return true; } -bool asst::Controller::disconnect() +bool asst::Controller::release() { +#ifdef _WIN32 + if (m_server_sock) { + m_server_started = false; + ::closesocket(m_server_sock); + m_server_sock = 0U; + } +#endif + #ifndef _WIN32 if (m_child) #else @@ -858,7 +1061,7 @@ bool asst::Controller::disconnect() { return call_command(m_adb.release).has_value(); } -} + } const std::string& asst::Controller::get_uuid() const { diff --git a/src/MeoAssistant/Controller.h b/src/MeoAssistant/Controller.h index 77724c85c9..cc29c828aa 100644 --- a/src/MeoAssistant/Controller.h +++ b/src/MeoAssistant/Controller.h @@ -31,7 +31,7 @@ namespace asst ~Controller(); bool connect(const std::string& adb_path, const std::string& address, const std::string& config); - bool disconnect(); + bool release(); const std::string& get_uuid() const; cv::Mat get_image(bool raw = false); @@ -62,12 +62,19 @@ namespace asst private: void pipe_working_proc(); - std::optional> call_command(const std::string& cmd, int64_t timeout = 20000); + std::optional> call_command(const std::string& cmd, int64_t timeout = 20000, bool recv_by_socket = false); int push_cmd(const std::string& cmd); - using DecodeFunc = std::function&)>; + struct SocketInfo + { + std::string remote_address; + unsigned short remote_port = 0U; + }; + std::optional try_init_socket(const std::string& local_address, unsigned short try_port, unsigned short try_times = 10U); + + using DecodeFunc = std::function&)>; bool screencap(); - bool screencap(const std::string& cmd, DecodeFunc decode_func); + bool screencap(const std::string& cmd, DecodeFunc decode_func, bool by_nc = false); cv::Mat get_resized_image() const; Point rand_point_in_rect(const Rect& rect); @@ -84,7 +91,11 @@ namespace asst std::minstd_rand m_rand_engine; constexpr static int PipeBuffSize = 4 * 1024 * 1024; // 管道缓冲区大小 + constexpr static int SocketBuffSize = 4 * 1024 * 1024; // socket 缓冲区大小 std::unique_ptr m_pipe_buffer = nullptr; + std::mutex m_pipe_mutex; + std::unique_ptr m_socket_buffer = nullptr; + std::mutex m_socket_mutex; #ifdef _WIN32 HANDLE m_pipe_read = nullptr; // 读管道句柄 HANDLE m_pipe_write = nullptr; // 写管道句柄 @@ -92,6 +103,10 @@ namespace asst HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄 SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符 STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息 + + WSADATA m_wsa_data = { 0 }; + SOCKET m_server_sock = 0ULL; + sockaddr_in m_server_addr = { 0 }; #else constexpr static int PIPE_READ = 0; constexpr static int PIPE_WRITE = 1; @@ -106,6 +121,7 @@ namespace asst std::string click; std::string swipe; + std::string screencap_raw_by_nc; std::string screencap_raw_with_gzip; std::string screencap_encode; std::string release; @@ -125,6 +141,7 @@ namespace asst { UnknownYet, Default, + RawByNc, RawWithGzip, Encode } screencap_method = ScreencapMethod::UnknownYet; @@ -135,6 +152,10 @@ namespace asst double m_control_scale = 1.0; int m_width = 0; int m_height = 0; + bool m_support_socket = false; + bool m_server_started = false; + + inline static int m_instance_count = 0; mutable std::shared_mutex m_image_mutex; cv::Mat m_cache_image; diff --git a/src/MeoAssistant/GeneralConfiger.cpp b/src/MeoAssistant/GeneralConfiger.cpp index 56c5744f16..cd615473f3 100644 --- a/src/MeoAssistant/GeneralConfiger.cpp +++ b/src/MeoAssistant/GeneralConfiger.cpp @@ -49,6 +49,8 @@ bool asst::GeneralConfiger::parse(const json::value& json) adb.display = cfg_json.at("display").as_string(); adb.display_format = cfg_json.at("displayFormat").as_string(); adb.screencap_raw_with_gzip = cfg_json.at("screencapRawWithGzip").as_string(); + adb.screencap_raw_by_nc = cfg_json.at("screencapRawByNC").as_string(); + adb.nc_port = static_cast(cfg_json.at("ncPort").as_integer()); adb.screencap_encode = cfg_json.at("screencapEncode").as_string(); adb.release = cfg_json.at("release").as_string(); adb.start = cfg_json.at("start").as_string(); diff --git a/src/MeoAssistant/GeneralConfiger.h b/src/MeoAssistant/GeneralConfiger.h index 1dc8dac6db..7a85215e70 100644 --- a/src/MeoAssistant/GeneralConfiger.h +++ b/src/MeoAssistant/GeneralConfiger.h @@ -51,6 +51,8 @@ namespace asst std::string swipe; std::string display; std::string screencap_raw_with_gzip; + std::string screencap_raw_by_nc; + unsigned short nc_port = 0U; std::string screencap_encode; std::string release; std::string start; diff --git a/src/MeoAssistant/MeoAssistant.vcxproj b/src/MeoAssistant/MeoAssistant.vcxproj index 4bef932522..0809b993c1 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj +++ b/src/MeoAssistant/MeoAssistant.vcxproj @@ -247,7 +247,7 @@ true true true - ppocr.lib;opencv_world453.lib;zlibstatic.lib;%(AdditionalDependencies) + ppocr.lib;opencv_world453.lib;zlibstatic.lib;ws2_32.lib;%(AdditionalDependencies) RequireAdministrator @@ -299,7 +299,7 @@ true true true - ppocr.lib;opencv_world453.lib;zlibstatic.lib;%(AdditionalDependencies) + ppocr.lib;opencv_world453.lib;zlibstatic.lib;ws2_32.lib;%(AdditionalDependencies) RequireAdministrator