mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-15 17:30:27 +08:00
初步重构工作线程,临时保存
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
<ClInclude Include="include\Identify.h" />
|
||||
<ClInclude Include="include\Logger.hpp" />
|
||||
<ClInclude Include="include\RecruitConfiger.h" />
|
||||
<ClInclude Include="include\Task.h" />
|
||||
<ClInclude Include="include\Updater.h" />
|
||||
<ClInclude Include="include\Version.h" />
|
||||
<ClInclude Include="include\WinMacro.h" />
|
||||
@@ -38,6 +39,7 @@
|
||||
<ClCompile Include="src\Configer.cpp" />
|
||||
<ClCompile Include="src\Identify.cpp" />
|
||||
<ClCompile Include="src\RecruitConfiger.cpp" />
|
||||
<ClCompile Include="src\Task.cpp" />
|
||||
<ClCompile Include="src\Updater.cpp" />
|
||||
<ClCompile Include="src\WinMacro.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -51,6 +51,9 @@
|
||||
<ClInclude Include="include\Version.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\Task.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\Configer.cpp">
|
||||
@@ -74,6 +77,9 @@
|
||||
<ClCompile Include="src\RecruitConfiger.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Task.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\resource\config.json">
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
|
||||
#include "AsstPort.h"
|
||||
#include "AsstDef.h"
|
||||
#include "Configer.h"
|
||||
#include "RecruitConfiger.h"
|
||||
#include "Task.h"
|
||||
|
||||
namespace cv {
|
||||
class Mat;
|
||||
@@ -46,17 +48,19 @@ namespace asst {
|
||||
open_recruit(const std::vector<int>& required_level, bool set_time = true);
|
||||
private:
|
||||
static void working_proc(Assistance* pThis);
|
||||
static void task_callback(TaskMsg msg, const std::string& detail_json = std::string());
|
||||
|
||||
cv::Mat get_format_image();
|
||||
void set_control_scale(int cur_width, int cur_height);
|
||||
void clear_exec_times();
|
||||
|
||||
// for debug
|
||||
bool find_text_and_click(const std::string& text, bool block = true);
|
||||
|
||||
std::shared_ptr<WinMacro> m_pWindow = nullptr;
|
||||
std::shared_ptr<WinMacro> m_pView = nullptr;
|
||||
std::shared_ptr<WinMacro> m_pCtrl = nullptr;
|
||||
std::shared_ptr<Identify> m_pIder = nullptr;
|
||||
std::shared_ptr<WinMacro> m_window_ptr = nullptr;
|
||||
std::shared_ptr<WinMacro> m_view_ptr = nullptr;
|
||||
std::shared_ptr<WinMacro> m_control_ptr = nullptr;
|
||||
std::shared_ptr<Identify> m_identify_ptr = nullptr;
|
||||
bool m_inited = false;
|
||||
|
||||
std::thread m_working_thread;
|
||||
@@ -64,8 +68,9 @@ namespace asst {
|
||||
std::condition_variable m_condvar;
|
||||
bool m_thread_exit = false;
|
||||
bool m_thread_running = false;
|
||||
std::vector<std::string> m_next_tasks;
|
||||
std::queue<std::shared_ptr<AbstractTask>> m_tasks_queue;
|
||||
|
||||
std::unordered_map<std::string, TaskInfo> m_all_tasks_info;
|
||||
Configer m_configer;
|
||||
RecruitConfiger m_recruit_configer;
|
||||
};
|
||||
|
||||
@@ -27,15 +27,14 @@ namespace asst {
|
||||
|
||||
enum class TaskType {
|
||||
Invalid = 0,
|
||||
BasicClick = 1,
|
||||
DoNothing = 2,
|
||||
Stop = 4,
|
||||
PrintWindow,
|
||||
ClickSelf = 8 | BasicClick,
|
||||
ClickRect = 16 | BasicClick,
|
||||
ClickRand = 32 | BasicClick,
|
||||
Ocr = 64,
|
||||
OcrAndClick = Ocr | BasicClick
|
||||
BasicClick = 0x100,
|
||||
ClickSelf = BasicClick | 1, // 点击模板自身位置
|
||||
ClickRect = BasicClick | 2, // 点击指定区域
|
||||
ClickRand = BasicClick | 4, // 点击随机区域
|
||||
DoNothing = 0x200, // 什么都不做
|
||||
Stop = 0x400, // 停止工作线程
|
||||
PrintWindow = 0x800, // 截图功能
|
||||
OpenRecruit = 0x1000 // 公开招募功能
|
||||
};
|
||||
static bool operator&(const TaskType& lhs, const TaskType& rhs)
|
||||
{
|
||||
@@ -51,7 +50,8 @@ namespace asst {
|
||||
{TaskType::ClickRand, "ClickRand"},
|
||||
{TaskType::DoNothing, "DoNothing"},
|
||||
{TaskType::Stop, "Stop"},
|
||||
{TaskType::PrintWindow, "PrintWindow"}
|
||||
{TaskType::PrintWindow, "PrintWindow"},
|
||||
{TaskType::OpenRecruit, "OpenRecruit"}
|
||||
};
|
||||
return os << _type_name.at(task);
|
||||
}
|
||||
@@ -147,9 +147,10 @@ namespace asst {
|
||||
};
|
||||
|
||||
struct TaskInfo {
|
||||
std::string name; // 任务名
|
||||
std::string template_filename; // 匹配模板图片文件名
|
||||
double threshold = 0; // 模板匹配阈值
|
||||
double cache_threshold = 0; // 直方图比较阈值
|
||||
double templ_threshold = 0; // 模板匹配阈值
|
||||
double hist_threshold = 0; // 直方图比较阈值
|
||||
TaskType type = TaskType::Invalid; // 任务类型
|
||||
std::vector<std::string> next; // 下一个可能的任务(列表)
|
||||
int exec_times = 0; // 任务已执行了多少次
|
||||
|
||||
@@ -22,19 +22,17 @@ namespace asst {
|
||||
bool set_param(const std::string& type, const std::string& param, const std::string& value);
|
||||
std::optional<std::string> get_param(const std::string& type, const std::string& param);
|
||||
|
||||
void clear_exec_times();
|
||||
|
||||
Configer& operator=(const Configer& rhs) = default;
|
||||
Configer& operator=(Configer&& rhs) noexcept = default;
|
||||
|
||||
constexpr static int DefaultWindowWidth = 1280;
|
||||
constexpr static int DefaultWindowHeight = 720;
|
||||
constexpr static double DefaultThreshold = 0.9;
|
||||
constexpr static double DefaultCacheThreshold = 0.9;
|
||||
constexpr static double Defaulttempl_threshold = 0.9;
|
||||
constexpr static double DefaultCachetempl_threshold = 0.9;
|
||||
|
||||
std::string m_version;
|
||||
Options m_options;
|
||||
std::unordered_map<std::string, TaskInfo> m_tasks;
|
||||
std::unordered_map<std::string, TaskInfo> m_all_tasks_info;
|
||||
std::unordered_map<std::string, EmulatorInfo> m_handles;
|
||||
std::unordered_map<std::string, std::string> m_ocr_replace;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace asst {
|
||||
bool add_image(const std::string& name, const std::string& path);
|
||||
|
||||
// return tuple< algorithmType, suitability, matched asst::rect>
|
||||
std::tuple<AlgorithmType, double, asst::Rect> find_image(const cv::Mat& image, const std::string& templ, double threshold = 0.99);
|
||||
std::tuple<AlgorithmType, double, asst::Rect> find_image(const cv::Mat& image, const std::string& templ, double templ_threshold = 0.99);
|
||||
|
||||
void clear_cache();
|
||||
|
||||
|
||||
87
MeoAssistance/include/Task.h
Normal file
87
MeoAssistance/include/Task.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
class Mat;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
|
||||
class WinMacro;
|
||||
class Identify;
|
||||
class Configer;
|
||||
|
||||
enum class TaskMsg {
|
||||
/* Error Msg */
|
||||
PtrIsNull,
|
||||
ImageIsEmpty,
|
||||
WindowMinimized,
|
||||
/* Info Msg */
|
||||
TaskMatched,
|
||||
ReachedLimit,
|
||||
ReadyToSleep,
|
||||
TaskCompleted,
|
||||
MissionStop
|
||||
/* Trace Msg */
|
||||
// Todo
|
||||
};
|
||||
|
||||
typedef void (*TaskCallback)(TaskMsg msg, const std::string& detail_json);
|
||||
|
||||
class AbstractTask
|
||||
{
|
||||
public:
|
||||
AbstractTask(TaskCallback callback);
|
||||
~AbstractTask() = default;
|
||||
AbstractTask(const AbstractTask&) = default;
|
||||
AbstractTask(AbstractTask&&) = default;
|
||||
|
||||
virtual void set_ptr(
|
||||
std::shared_ptr<WinMacro> window_ptr,
|
||||
std::shared_ptr<WinMacro> view_ptr,
|
||||
std::shared_ptr<WinMacro> control_ptr,
|
||||
std::shared_ptr<Identify> identify_ptr);
|
||||
virtual bool run() = 0;
|
||||
|
||||
virtual void set_exit_flag(bool* exit_flag);
|
||||
protected:
|
||||
virtual cv::Mat get_format_image();
|
||||
virtual bool set_control_scale(int cur_width, int cur_height);
|
||||
virtual void sleep(unsigned millisecond);
|
||||
|
||||
std::shared_ptr<WinMacro> m_window_ptr = nullptr;
|
||||
std::shared_ptr<WinMacro> m_view_ptr = nullptr;
|
||||
std::shared_ptr<WinMacro> m_control_ptr = nullptr;
|
||||
std::shared_ptr<Identify> m_identify_ptr = nullptr;
|
||||
|
||||
TaskCallback m_callback = NULL;
|
||||
bool* m_exit_flag = NULL;
|
||||
};
|
||||
|
||||
class MatchTask : public AbstractTask
|
||||
{
|
||||
public:
|
||||
MatchTask(TaskCallback callback,
|
||||
std::unordered_map<std::string, TaskInfo>* all_tasks_ptr,
|
||||
Configer* configer_ptr);
|
||||
|
||||
virtual bool run() override;
|
||||
|
||||
virtual void set_tasks(const std::vector<std::string>& cur_tasks_name) { m_cur_tasks_name = cur_tasks_name; }
|
||||
|
||||
protected:
|
||||
bool match_image(TaskInfo * task_info, asst::Rect* matched_rect = NULL);
|
||||
void exec_click_task(TaskInfo& task, const asst::Rect& matched_rect);
|
||||
|
||||
std::unordered_map<std::string, TaskInfo>* m_all_tasks_ptr = NULL;
|
||||
Configer* m_configer_ptr = NULL;
|
||||
std::vector<std::string> m_cur_tasks_name;
|
||||
};
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "Identify.h"
|
||||
#include "Logger.hpp"
|
||||
#include "AsstAux.h"
|
||||
#include "Task.h"
|
||||
#include "json.h"
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
@@ -18,17 +20,18 @@ Assistance::Assistance()
|
||||
DebugTraceFunction;
|
||||
|
||||
m_configer.load(GetResourceDir() + "config.json");
|
||||
m_all_tasks_info = std::move(m_configer.m_all_tasks_info);
|
||||
m_recruit_configer.load(GetResourceDir() + "operInfo.json");
|
||||
|
||||
m_pIder = std::make_shared<Identify>();
|
||||
for (const auto& [name, info] : m_configer.m_tasks)
|
||||
m_identify_ptr = std::make_shared<Identify>();
|
||||
for (const auto& [name, info] : m_all_tasks_info)
|
||||
{
|
||||
m_pIder->add_image(name, GetResourceDir() + "template\\" + info.template_filename);
|
||||
m_identify_ptr->add_image(name, GetResourceDir() + "template\\" + info.template_filename);
|
||||
}
|
||||
m_pIder->set_use_cache(m_configer.m_options.identify_cache);
|
||||
m_identify_ptr->set_use_cache(m_configer.m_options.identify_cache);
|
||||
|
||||
m_pIder->set_ocr_param(m_configer.m_options.ocr_gpu_index, m_configer.m_options.ocr_thread_number);
|
||||
m_pIder->ocr_init_models(GetResourceDir() + "OcrLiteNcnn\\models\\");
|
||||
m_identify_ptr->set_ocr_param(m_configer.m_options.ocr_gpu_index, m_configer.m_options.ocr_thread_number);
|
||||
m_identify_ptr->ocr_init_models(GetResourceDir() + "OcrLiteNcnn\\models\\");
|
||||
|
||||
m_working_thread = std::thread(working_proc, this);
|
||||
}
|
||||
@@ -37,8 +40,8 @@ Assistance::~Assistance()
|
||||
{
|
||||
DebugTraceFunction;
|
||||
|
||||
//if (m_pWindow != nullptr) {
|
||||
// m_pWindow->showWindow();
|
||||
//if (m_window_ptr != nullptr) {
|
||||
// m_window_ptr->showWindow();
|
||||
//}
|
||||
|
||||
m_thread_exit = true;
|
||||
@@ -57,10 +60,10 @@ std::optional<std::string> Assistance::catch_emulator(const std::string& emulato
|
||||
stop();
|
||||
|
||||
auto create_handles = [&](const EmulatorInfo& info) -> bool {
|
||||
m_pWindow = std::make_shared<WinMacro>(info, HandleType::Window);
|
||||
m_pView = std::make_shared<WinMacro>(info, HandleType::View);
|
||||
m_pCtrl = std::make_shared<WinMacro>(info, HandleType::Control);
|
||||
return m_pWindow->captured() && m_pView->captured() && m_pCtrl->captured();
|
||||
m_window_ptr = std::make_shared<WinMacro>(info, HandleType::Window);
|
||||
m_view_ptr = std::make_shared<WinMacro>(info, HandleType::View);
|
||||
m_control_ptr = std::make_shared<WinMacro>(info, HandleType::Control);
|
||||
return m_window_ptr->captured() && m_view_ptr->captured() && m_control_ptr->captured();
|
||||
};
|
||||
|
||||
bool ret = false;
|
||||
@@ -82,7 +85,7 @@ std::optional<std::string> Assistance::catch_emulator(const std::string& emulato
|
||||
else { // 指定的模拟器
|
||||
ret = create_handles(m_configer.m_handles[emulator_name]);
|
||||
}
|
||||
if (ret && m_pWindow->showWindow() && m_pWindow->resizeWindow()) {
|
||||
if (ret && m_window_ptr->showWindow() && m_window_ptr->resizeWindow()) {
|
||||
m_inited = true;
|
||||
return cor_name;
|
||||
}
|
||||
@@ -96,18 +99,18 @@ void Assistance::start(const std::string& task)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
DebugTrace("Start |", task);
|
||||
|
||||
|
||||
if (m_thread_running || !m_inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_configer.clear_exec_times();
|
||||
clear_exec_times();
|
||||
m_identify_ptr->clear_cache();
|
||||
|
||||
auto task_ptr = std::make_shared<MatchTask>(task_callback, &m_all_tasks_info, &m_configer);
|
||||
task_ptr->set_tasks({ task });
|
||||
m_tasks_queue.emplace(task_ptr);
|
||||
|
||||
m_pIder->clear_cache();
|
||||
m_next_tasks.clear();
|
||||
m_next_tasks.emplace_back(task);
|
||||
m_thread_running = true;
|
||||
m_condvar.notify_one();
|
||||
}
|
||||
@@ -120,11 +123,12 @@ void Assistance::stop(bool block)
|
||||
std::unique_lock<std::mutex> lock;
|
||||
if (block) { // 外部调用
|
||||
lock = std::unique_lock<std::mutex>(m_mutex);
|
||||
m_configer.clear_exec_times();
|
||||
clear_exec_times();
|
||||
}
|
||||
m_thread_running = false;
|
||||
m_next_tasks.clear();
|
||||
m_pIder->clear_cache();
|
||||
std::queue<std::shared_ptr<AbstractTask>> empty;
|
||||
m_tasks_queue.swap(empty);
|
||||
m_identify_ptr->clear_cache();
|
||||
}
|
||||
|
||||
bool Assistance::set_param(const std::string& type, const std::string& param, const std::string& value)
|
||||
@@ -187,7 +191,7 @@ bool asst::Assistance::find_text_and_click(const std::string& text, bool block)
|
||||
lock = std::unique_lock<std::mutex>(m_mutex);
|
||||
}
|
||||
const cv::Mat& image = get_format_image();
|
||||
std::optional<Rect>&& result = m_pIder->find_text(image, text);
|
||||
std::optional<Rect>&& result = m_identify_ptr->find_text(image, text);
|
||||
|
||||
if (!result) {
|
||||
DebugTrace("Cannot found", Utf8ToGbk(text));
|
||||
@@ -195,7 +199,7 @@ bool asst::Assistance::find_text_and_click(const std::string& text, bool block)
|
||||
}
|
||||
|
||||
set_control_scale(image.cols, image.rows);
|
||||
return m_pCtrl->click(result.value());
|
||||
return m_control_ptr->click(result.value());
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::pair<std::vector<std::string>, OperCombs>>>
|
||||
@@ -213,7 +217,7 @@ std::optional<std::vector<std::pair<std::vector<std::string>, OperCombs>>>
|
||||
start("RecruitTime");
|
||||
}
|
||||
|
||||
std::vector<TextArea> ider_result = m_pIder->ocr_detect(image);
|
||||
std::vector<TextArea> ider_result = m_identify_ptr->ocr_detect(image);
|
||||
std::vector<TextArea> filt_result;
|
||||
std::string ider_str;
|
||||
for (TextArea& res : ider_result) {
|
||||
@@ -358,7 +362,7 @@ std::optional<std::vector<std::pair<std::vector<std::string>, OperCombs>>>
|
||||
for (const TextArea& text_area : filt_result) {
|
||||
if (std::find(final_tags.cbegin(), final_tags.cend(), text_area.text) != final_tags.cend()) {
|
||||
final_text_areas.emplace_back(text_area);
|
||||
m_pCtrl->click(text_area.rect);
|
||||
m_control_ptr->click(text_area.rect);
|
||||
Sleep(300);
|
||||
}
|
||||
}
|
||||
@@ -373,148 +377,18 @@ void Assistance::working_proc(Assistance* pThis)
|
||||
|
||||
while (!pThis->m_thread_exit) {
|
||||
std::unique_lock<std::mutex> lock(pThis->m_mutex);
|
||||
if (pThis->m_thread_running) {
|
||||
const cv::Mat& cur_image = pThis->get_format_image();
|
||||
pThis->set_control_scale(cur_image.cols, cur_image.rows);
|
||||
if (pThis->m_thread_running && !pThis->m_tasks_queue.empty()) {
|
||||
|
||||
if (cur_image.empty()) {
|
||||
DebugTraceError("Unable to capture window image!!!");
|
||||
pThis->stop(false);
|
||||
continue;
|
||||
}
|
||||
if (cur_image.rows < 100) {
|
||||
DebugTraceInfo("Window Could not be minimized!!!");
|
||||
pThis->m_pWindow->showWindow();
|
||||
pThis->m_condvar.wait_for(lock,
|
||||
std::chrono::milliseconds(pThis->m_configer.m_options.identify_delay),
|
||||
[&]() -> bool { return !pThis->m_thread_running; });
|
||||
continue;
|
||||
}
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
std::shared_ptr<AbstractTask> task_ptr = pThis->m_tasks_queue.front();
|
||||
task_ptr->set_ptr(pThis->m_window_ptr, pThis->m_view_ptr, pThis->m_control_ptr, pThis->m_identify_ptr);
|
||||
bool ret = task_ptr->run();
|
||||
if (ret) {
|
||||
pThis->m_tasks_queue.pop();
|
||||
} // else {} 失败了不pop,一直跑。 Todo: 设一个上限
|
||||
|
||||
std::string matched_task;
|
||||
Rect matched_rect;
|
||||
// 逐个匹配当前可能的图像
|
||||
for (const std::string& task_name : pThis->m_next_tasks) {
|
||||
|
||||
double threshold = pThis->m_configer.m_tasks[task_name].threshold;
|
||||
double cache_threshold = pThis->m_configer.m_tasks[task_name].cache_threshold;
|
||||
|
||||
auto&& [algorithm, value, rect] = pThis->m_pIder->find_image(cur_image, task_name, threshold);
|
||||
DebugTrace(task_name, "Type:", algorithm, "Value:", value);
|
||||
if (algorithm == AlgorithmType::JustReturn ||
|
||||
(algorithm == AlgorithmType::MatchTemplate && value >= threshold)
|
||||
|| (algorithm == AlgorithmType::CompareHist && value >= cache_threshold)) {
|
||||
matched_task = task_name;
|
||||
matched_rect = std::move(rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 执行任务
|
||||
if (!matched_task.empty()) {
|
||||
TaskInfo& task = pThis->m_configer.m_tasks[matched_task];
|
||||
DebugTraceInfo("***Matched***", matched_task, "Type:", task.type);
|
||||
// 前置固定延时
|
||||
if (task.pre_delay > 0) {
|
||||
DebugTrace("PreDelay", task.pre_delay);
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(task.pre_delay));
|
||||
bool cv_ret = pThis->m_condvar.wait_for(lock, std::chrono::milliseconds(task.pre_delay),
|
||||
[&]() -> bool { return !pThis->m_thread_running; });
|
||||
if (cv_ret) { continue; }
|
||||
}
|
||||
|
||||
if (task.max_times != INT_MAX) {
|
||||
DebugTrace("CurTimes:", task.exec_times, "MaxTimes:", task.max_times);
|
||||
}
|
||||
if (task.exec_times < task.max_times) {
|
||||
// 随机延时功能
|
||||
if ((task.type & TaskType::BasicClick)
|
||||
&& pThis->m_configer.m_options.control_delay_upper != 0) {
|
||||
static std::default_random_engine rand_engine(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
static std::uniform_int_distribution<unsigned> rand_uni(pThis->m_configer.m_options.control_delay_lower, pThis->m_configer.m_options.control_delay_upper);
|
||||
int delay = rand_uni(rand_engine);
|
||||
DebugTraceInfo("Random Delay", delay, "ms");
|
||||
bool cv_ret = pThis->m_condvar.wait_for(lock, std::chrono::milliseconds(delay),
|
||||
[&]() -> bool { return !pThis->m_thread_running; });
|
||||
if (cv_ret) { continue; }
|
||||
}
|
||||
if (!pThis->m_thread_running) {
|
||||
continue;
|
||||
}
|
||||
switch (task.type) {
|
||||
case TaskType::ClickRect:
|
||||
matched_rect = task.specific_area;
|
||||
[[fallthrough]];
|
||||
case TaskType::ClickSelf:
|
||||
pThis->m_pCtrl->click(matched_rect);
|
||||
break;
|
||||
case TaskType::ClickRand:
|
||||
pThis->m_pCtrl->click(pThis->m_pCtrl->getWindowRect());
|
||||
break;
|
||||
case TaskType::DoNothing:
|
||||
break;
|
||||
case TaskType::Stop:
|
||||
DebugTrace("TaskType is Stop");
|
||||
pThis->stop(false);
|
||||
continue;
|
||||
break;
|
||||
case TaskType::PrintWindow:
|
||||
if (pThis->m_configer.m_options.print_window) {
|
||||
// 每次到结算界面,掉落物品不是一次性出来的,有个动画,所以需要等一会再截图
|
||||
int print_delay = pThis->m_configer.m_options.print_window_delay;
|
||||
DebugTraceInfo("Ready to print window, delay", print_delay);
|
||||
pThis->m_condvar.wait_for(lock,
|
||||
std::chrono::milliseconds(print_delay),
|
||||
[&]() -> bool { return !pThis->m_thread_running; });
|
||||
|
||||
const std::string dirname = GetCurrentDir() + "screenshot\\";
|
||||
std::filesystem::create_directory(dirname);
|
||||
const std::string time_str = StringReplaceAll(StringReplaceAll(GetFormatTimeString(), " ", "_"), ":", "-");
|
||||
const std::string filename = dirname + time_str + ".png";
|
||||
pThis->print_window(filename, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DebugTraceError("Unknown option type:", task.type);
|
||||
break;
|
||||
}
|
||||
++task.exec_times;
|
||||
|
||||
// 减少其他任务的执行次数
|
||||
// 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效
|
||||
// 所以要给蓝色开始行动的次数减一
|
||||
for (const std::string& reduce : task.reduce_other_times) {
|
||||
--pThis->m_configer.m_tasks[reduce].exec_times;
|
||||
DebugTrace("Reduce exec times", reduce, pThis->m_configer.m_tasks[reduce].exec_times);
|
||||
}
|
||||
// 后置固定延时
|
||||
if (task.rear_delay > 0) {
|
||||
DebugTrace("RearDelay", task.rear_delay);
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(task.rear_delay));
|
||||
bool cv_ret = pThis->m_condvar.wait_for(lock, std::chrono::milliseconds(task.rear_delay),
|
||||
[&]() -> bool { return !pThis->m_thread_running; });
|
||||
if (cv_ret) { continue; }
|
||||
}
|
||||
pThis->m_next_tasks = pThis->m_configer.m_tasks[matched_task].next;
|
||||
}
|
||||
else {
|
||||
DebugTraceInfo("Reached limit");
|
||||
pThis->m_next_tasks = pThis->m_configer.m_tasks[matched_task].exceeded_next;
|
||||
}
|
||||
|
||||
// 单纯为了打印日志。。感觉可以优化下
|
||||
std::string nexts_str;
|
||||
for (const std::string& name : pThis->m_next_tasks) {
|
||||
nexts_str += name + " ,";
|
||||
}
|
||||
if (nexts_str.back() == ',') {
|
||||
nexts_str.pop_back();
|
||||
}
|
||||
DebugTrace("Next:", nexts_str);
|
||||
}
|
||||
|
||||
pThis->m_condvar.wait_for(lock,
|
||||
std::chrono::milliseconds(pThis->m_configer.m_options.identify_delay),
|
||||
pThis->m_condvar.wait_until(lock,
|
||||
start_time + std::chrono::milliseconds(pThis->m_configer.m_options.identify_delay),
|
||||
[&]() -> bool { return !pThis->m_thread_running; });
|
||||
}
|
||||
else {
|
||||
@@ -523,15 +397,48 @@ void Assistance::working_proc(Assistance* pThis)
|
||||
}
|
||||
}
|
||||
|
||||
void asst::Assistance::task_callback(TaskMsg msg, const std::string& detail_json)
|
||||
{
|
||||
auto&& json_ret = json::parser::parse(detail_json);
|
||||
json::value detail;
|
||||
if (json_ret) {
|
||||
detail = json_ret.value();
|
||||
}
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case asst::TaskMsg::PtrIsNull:
|
||||
DebugTraceError("PtrIsNull");
|
||||
break;
|
||||
case asst::TaskMsg::ImageIsEmpty:
|
||||
DebugTraceError("ImageIsEmpty");
|
||||
break;
|
||||
case asst::TaskMsg::WindowMinimized:
|
||||
break;
|
||||
case asst::TaskMsg::TaskMatched:
|
||||
break;
|
||||
case asst::TaskMsg::ReachedLimit:
|
||||
break;
|
||||
case asst::TaskMsg::ReadyToSleep:
|
||||
break;
|
||||
case asst::TaskMsg::TaskCompleted:
|
||||
break;
|
||||
case asst::TaskMsg::MissionStop:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat asst::Assistance::get_format_image()
|
||||
{
|
||||
const cv::Mat& raw_image = m_pView->getImage(m_pView->getWindowRect());
|
||||
const cv::Mat& raw_image = m_view_ptr->getImage(m_view_ptr->getWindowRect());
|
||||
if (raw_image.empty() || raw_image.rows < 100) {
|
||||
DebugTraceError("Window image error");
|
||||
return raw_image;
|
||||
}
|
||||
// 把模拟器边框的一圈裁剪掉
|
||||
const EmulatorInfo& window_info = m_pView->getEmulatorInfo();
|
||||
const EmulatorInfo& window_info = m_view_ptr->getEmulatorInfo();
|
||||
int x_offset = window_info.x_offset;
|
||||
int y_offset = window_info.y_offset;
|
||||
int width = raw_image.cols - x_offset - window_info.right_offset;
|
||||
@@ -555,5 +462,12 @@ void asst::Assistance::set_control_scale(int cur_width, int cur_height)
|
||||
// 如果用户把侧边收起来了,则有侧边的那头会额外裁剪掉一些,长度偏小
|
||||
// 所以按这里面长、宽里大的那个算,大的那边没侧边
|
||||
double scale = std::max(scale_width, scale_height);
|
||||
m_pCtrl->setControlScale(scale);
|
||||
m_control_ptr->setControlScale(scale);
|
||||
}
|
||||
|
||||
void Assistance::clear_exec_times()
|
||||
{
|
||||
for (auto&& pair : m_all_tasks_info) {
|
||||
pair.second.exec_times = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ bool Configer::set_param(const std::string& type, const std::string& param, cons
|
||||
{
|
||||
// 暂时只用到了这些,总的参数太多了,后面要用啥再加上
|
||||
if (type == "task.type") {
|
||||
if (m_tasks.find(param) == m_tasks.cend()) {
|
||||
if (m_all_tasks_info.find(param) == m_all_tasks_info.cend()) {
|
||||
return false;
|
||||
}
|
||||
auto& task_info = m_tasks[param];
|
||||
auto& task_info = m_all_tasks_info[param];
|
||||
std::string type = value;
|
||||
std::transform(type.begin(), type.end(), type.begin(), std::tolower);
|
||||
if (type == "clickself") {
|
||||
@@ -71,10 +71,10 @@ bool Configer::set_param(const std::string& type, const std::string& param, cons
|
||||
}
|
||||
}
|
||||
else if (type == "task.maxTimes") {
|
||||
if (m_tasks.find(param) == m_tasks.cend()) {
|
||||
if (m_all_tasks_info.find(param) == m_all_tasks_info.cend()) {
|
||||
return false;
|
||||
}
|
||||
m_tasks[param].max_times = std::stoi(value);
|
||||
m_all_tasks_info[param].max_times = std::stoi(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -82,19 +82,12 @@ bool Configer::set_param(const std::string& type, const std::string& param, cons
|
||||
std::optional<std::string> Configer::get_param(const std::string& type, const std::string& param)
|
||||
{
|
||||
// 暂时只用到了这些,总的参数太多了,后面要用啥再加上
|
||||
if (type == "task.execTimes" && m_tasks.find(param) != m_tasks.cend()) {
|
||||
return std::to_string(m_tasks.at(param).exec_times);
|
||||
if (type == "task.execTimes" && m_all_tasks_info.find(param) != m_all_tasks_info.cend()) {
|
||||
return std::to_string(m_all_tasks_info.at(param).exec_times);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Configer::clear_exec_times()
|
||||
{
|
||||
for (auto&& pair : m_tasks) {
|
||||
pair.second.exec_times = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::Configer::_load(const std::string& filename)
|
||||
{
|
||||
std::ifstream ifs(filename, std::ios::in);
|
||||
@@ -132,9 +125,10 @@ bool asst::Configer::_load(const std::string& filename)
|
||||
|
||||
for (auto&& [name, task_json] : root["tasks"].as_object()) {
|
||||
TaskInfo task_info;
|
||||
task_info.name = name;
|
||||
task_info.template_filename = task_json["template"].as_string();
|
||||
task_info.threshold = task_json.get("threshold", DefaultThreshold);
|
||||
task_info.cache_threshold = task_json.get("cacheThreshold", DefaultCacheThreshold);
|
||||
task_info.templ_threshold = task_json.get("templThreshold", Defaulttempl_threshold);
|
||||
task_info.hist_threshold = task_json.get("histThreshold", DefaultCachetempl_threshold);
|
||||
|
||||
std::string type = task_json["type"].as_string();
|
||||
std::transform(type.begin(), type.end(), type.begin(), std::tolower);
|
||||
@@ -191,7 +185,7 @@ bool asst::Configer::_load(const std::string& filename)
|
||||
task_info.next.emplace_back(next.as_string());
|
||||
}
|
||||
|
||||
m_tasks.emplace(name, std::move(task_info));
|
||||
m_all_tasks_info.emplace(name, std::move(task_info));
|
||||
}
|
||||
|
||||
for (auto&& [name, emulator_json] : root["handle"].as_object()) {
|
||||
|
||||
@@ -94,7 +94,7 @@ std::pair<double, cv::Point> Identify::match_template(const cv::Mat& image, cons
|
||||
return { maxVal, maxLoc };
|
||||
}
|
||||
|
||||
std::tuple<AlgorithmType, double, asst::Rect> Identify::find_image(const Mat& cur, const std::string& templ, double threshold)
|
||||
std::tuple<AlgorithmType, double, asst::Rect> Identify::find_image(const Mat& cur, const std::string& templ, double templ_threshold)
|
||||
{
|
||||
if (m_mat_map.find(templ) == m_mat_map.cend()) {
|
||||
return { AlgorithmType::JustReturn, 0, asst::Rect() };
|
||||
@@ -111,7 +111,7 @@ std::tuple<AlgorithmType, double, asst::Rect> Identify::find_image(const Mat& cu
|
||||
const auto& [value, point] = match_template(cur, templ_mat);
|
||||
cv::Rect raw_rect(point.x, point.y, templ_mat.cols, templ_mat.rows);
|
||||
|
||||
if (m_use_cache && value >= threshold) {
|
||||
if (m_use_cache && value >= templ_threshold) {
|
||||
m_cache_map.emplace(templ, std::make_pair(raw_rect, image_2_hist(cur(raw_rect))));
|
||||
}
|
||||
|
||||
|
||||
219
MeoAssistance/src/Task.cpp
Normal file
219
MeoAssistance/src/Task.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
#include "Task.h"
|
||||
#include "AsstDef.h"
|
||||
#include "WinMacro.h"
|
||||
#include "Identify.h"
|
||||
#include "Configer.h"
|
||||
#include "RecruitConfiger.h"
|
||||
#include "json.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
using namespace asst;
|
||||
|
||||
AbstractTask::AbstractTask(TaskCallback callback)
|
||||
:m_callback(callback)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void AbstractTask::set_ptr(
|
||||
std::shared_ptr<WinMacro> window_ptr,
|
||||
std::shared_ptr<WinMacro> view_ptr,
|
||||
std::shared_ptr<WinMacro> control_ptr,
|
||||
std::shared_ptr<Identify> identify_ptr)
|
||||
{
|
||||
m_window_ptr = window_ptr;
|
||||
m_view_ptr = view_ptr;
|
||||
m_control_ptr = control_ptr;
|
||||
m_identify_ptr = identify_ptr;
|
||||
}
|
||||
|
||||
void asst::AbstractTask::set_exit_flag(bool* exit_flag)
|
||||
{
|
||||
m_exit_flag = exit_flag;
|
||||
}
|
||||
|
||||
cv::Mat AbstractTask::get_format_image()
|
||||
{
|
||||
const cv::Mat& raw_image = m_view_ptr->getImage(m_view_ptr->getWindowRect());
|
||||
if (raw_image.empty()) {
|
||||
m_callback(TaskMsg::ImageIsEmpty, std::string());
|
||||
return raw_image;
|
||||
}
|
||||
if (raw_image.rows < 100) {
|
||||
m_callback(TaskMsg::WindowMinimized, std::string());
|
||||
return raw_image;
|
||||
}
|
||||
|
||||
// 把模拟器边框的一圈裁剪掉
|
||||
const EmulatorInfo& window_info = m_view_ptr->getEmulatorInfo();
|
||||
int x_offset = window_info.x_offset;
|
||||
int y_offset = window_info.y_offset;
|
||||
int width = raw_image.cols - x_offset - window_info.right_offset;
|
||||
int height = raw_image.rows - y_offset - window_info.bottom_offset;
|
||||
|
||||
cv::Mat cropped(raw_image, cv::Rect(x_offset, y_offset, width, height));
|
||||
|
||||
//// 调整尺寸,与资源中截图的标准尺寸一致
|
||||
//cv::Mat dst;
|
||||
//cv::resize(cropped, dst, cv::Size(m_configer.DefaultWindowWidth, m_configer.DefaultWindowHeight));
|
||||
|
||||
return cropped;
|
||||
}
|
||||
|
||||
bool AbstractTask::set_control_scale(int cur_width, int cur_height)
|
||||
{
|
||||
double scale_width = static_cast<double>(cur_width) / Configer::DefaultWindowWidth;
|
||||
double scale_height = static_cast<double>(cur_height) / Configer::DefaultWindowHeight;
|
||||
double scale = std::max(scale_width, scale_height);
|
||||
m_control_ptr->setControlScale(scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::AbstractTask::sleep(unsigned millisecond)
|
||||
{
|
||||
if (millisecond == 0) {
|
||||
return;
|
||||
}
|
||||
auto start = std::chrono::system_clock::now();
|
||||
unsigned duration = 0;
|
||||
|
||||
json::value callback_json;
|
||||
callback_json["time"] = millisecond;
|
||||
m_callback(TaskMsg::ReadyToSleep, callback_json.to_string());
|
||||
|
||||
while ((m_exit_flag != NULL && *m_exit_flag == false)
|
||||
|| duration < millisecond) {
|
||||
duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
start - std::chrono::system_clock::now()).count();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
MatchTask::MatchTask(TaskCallback callback,
|
||||
std::unordered_map<std::string, TaskInfo>* all_tasks_ptr,
|
||||
Configer* configer_ptr)
|
||||
: AbstractTask(callback),
|
||||
m_all_tasks_ptr(all_tasks_ptr),
|
||||
m_configer_ptr(configer_ptr)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
bool MatchTask::run()
|
||||
{
|
||||
if (m_view_ptr == NULL
|
||||
|| m_control_ptr == NULL
|
||||
|| m_identify_ptr == NULL
|
||||
|| m_all_tasks_ptr == NULL
|
||||
|| m_control_ptr == NULL)
|
||||
{
|
||||
m_callback(TaskMsg::PtrIsNull, std::string());
|
||||
return false;
|
||||
}
|
||||
|
||||
TaskInfo task;
|
||||
Rect rect;
|
||||
bool ret = match_image(&task, &rect);
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
json::value callback_json = json::object{
|
||||
{ "name", task.name },
|
||||
{ "type", static_cast<int>(task.type) },
|
||||
{ "exec_times", task.exec_times },
|
||||
{ "max_times", task.max_times }
|
||||
};
|
||||
m_callback(TaskMsg::TaskMatched, callback_json.to_string());
|
||||
|
||||
if (task.exec_times < task.max_times)
|
||||
{
|
||||
m_callback(TaskMsg::ReachedLimit, callback_json.to_string());
|
||||
return true;
|
||||
}
|
||||
|
||||
// 前置固定延时
|
||||
sleep(task.pre_delay);
|
||||
|
||||
switch (task.type) {
|
||||
case TaskType::ClickRect:
|
||||
rect = task.specific_area;
|
||||
[[fallthrough]];
|
||||
case TaskType::ClickSelf:
|
||||
exec_click_task(task, rect);
|
||||
break;
|
||||
case TaskType::DoNothing:
|
||||
break;
|
||||
case TaskType::Stop:
|
||||
m_callback(TaskMsg::MissionStop, std::string());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
++task.exec_times;
|
||||
|
||||
// 减少其他任务的执行次数
|
||||
// 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效
|
||||
// 所以要给蓝色开始行动的次数减一
|
||||
for (const std::string& reduce : task.reduce_other_times) {
|
||||
--(*m_all_tasks_ptr)[reduce].exec_times;
|
||||
}
|
||||
|
||||
// 后置固定延时
|
||||
sleep(task.rear_delay);
|
||||
|
||||
callback_json["exec_times"] = task.exec_times;
|
||||
m_callback(TaskMsg::TaskCompleted, callback_json.to_string());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool MatchTask::match_image(TaskInfo* task_info, asst::Rect* matched_rect)
|
||||
{
|
||||
const cv::Mat& cur_image = get_format_image();
|
||||
if (cur_image.empty() || cur_image.rows < 100) {
|
||||
return false;
|
||||
}
|
||||
set_control_scale(cur_image.cols, cur_image.rows);
|
||||
|
||||
// 逐个匹配当前可能的图像
|
||||
for (const std::string& task_name : m_cur_tasks_name) {
|
||||
*task_info = (*m_all_tasks_ptr)[task_name];
|
||||
double templ_threshold = task_info->templ_threshold;
|
||||
double hist_threshold = task_info->hist_threshold;
|
||||
|
||||
auto&& [algorithm, value, rect] = m_identify_ptr->find_image(cur_image, task_name, templ_threshold);
|
||||
|
||||
if (algorithm == AlgorithmType::JustReturn
|
||||
|| (algorithm == AlgorithmType::MatchTemplate && value >= templ_threshold)
|
||||
|| (algorithm == AlgorithmType::CompareHist && value >= hist_threshold)) {
|
||||
|
||||
if (matched_rect != NULL) {
|
||||
*matched_rect = std::move(rect);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MatchTask::exec_click_task(TaskInfo& task, const asst::Rect& matched_rect)
|
||||
{
|
||||
// 随机延时功能
|
||||
if (m_configer_ptr->m_options.control_delay_upper != 0) {
|
||||
static std::default_random_engine rand_engine(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
static std::uniform_int_distribution<unsigned> rand_uni(
|
||||
m_configer_ptr->m_options.control_delay_lower,
|
||||
m_configer_ptr->m_options.control_delay_upper);
|
||||
|
||||
unsigned rand_delay = rand_uni(rand_engine);
|
||||
sleep(rand_delay);
|
||||
}
|
||||
|
||||
m_control_ptr->click(matched_rect);
|
||||
}
|
||||
@@ -232,7 +232,7 @@ double WinMacro::getScreenScale()
|
||||
if (scale == 0) {
|
||||
// 获取窗口当前显示的监视器
|
||||
// 使用桌面的句柄.
|
||||
HWND hWnd = GetDesktopWindow();
|
||||
HWND hWnd = ::GetDesktopWindow();
|
||||
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
// 获取监视器逻辑宽度与高度
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
"tasks": {
|
||||
"SanityBegin": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "doNothing",
|
||||
"next": [
|
||||
"UsePrts",
|
||||
@@ -244,7 +244,7 @@
|
||||
},
|
||||
"UsePrts": {
|
||||
"template": "UsePrts.png",
|
||||
"threshold": 0.8,
|
||||
"templThreshold": 0.8,
|
||||
"type": "clickSelf",
|
||||
"next": [
|
||||
"StartButton1"
|
||||
@@ -252,7 +252,7 @@
|
||||
},
|
||||
"StartButton1": {
|
||||
"template": "StartButton1.png",
|
||||
"threshold": 0.8,
|
||||
"templThreshold": 0.8,
|
||||
"type": "clickSelf",
|
||||
"next": [
|
||||
"StartButton2",
|
||||
@@ -281,7 +281,7 @@
|
||||
},
|
||||
"EndOfAction": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "printWindow",
|
||||
"next": [
|
||||
"Random"
|
||||
@@ -289,7 +289,7 @@
|
||||
},
|
||||
"Random": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "clickRect",
|
||||
"specificArea": [
|
||||
1100,
|
||||
@@ -366,7 +366,7 @@
|
||||
},
|
||||
"VisitBegin": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "doNothing",
|
||||
"next": [
|
||||
"Friends",
|
||||
@@ -379,8 +379,8 @@
|
||||
},
|
||||
"ReturnToFriends": {
|
||||
"template": "Return.png",
|
||||
"threshold": 0.85,
|
||||
"cacheThreshold": 0.85,
|
||||
"templThreshold": 0.85,
|
||||
"histThreshold": 0.85,
|
||||
"type": "clickSelf",
|
||||
"next": [
|
||||
"Friends",
|
||||
@@ -446,7 +446,7 @@
|
||||
},
|
||||
"CreditStore": {
|
||||
"template": "CreditStore.png",
|
||||
"threshold": 0.85,
|
||||
"templThreshold": 0.85,
|
||||
"rearDelay": 1000,
|
||||
"type": "clickSelf",
|
||||
"next": [
|
||||
@@ -463,7 +463,7 @@
|
||||
},
|
||||
"VisitLimited": {
|
||||
"template": "VisitLimited.png",
|
||||
"threshold": 1,
|
||||
"templThreshold": 1,
|
||||
"type": "doNothing",
|
||||
"next": [
|
||||
"ReturnToMall"
|
||||
@@ -478,7 +478,7 @@
|
||||
},
|
||||
"Stop": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "stop",
|
||||
"next": []
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user