mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-18 18:20:39 +08:00
合并refactor,完成整体框架重构!
This commit is contained in:
@@ -23,6 +23,15 @@ namespace json
|
||||
array(const raw_array &arr);
|
||||
array(raw_array &&arr) noexcept;
|
||||
array(std::initializer_list<raw_array::value_type> init_list);
|
||||
template<typename EleType>
|
||||
array(std::vector<EleType> vec) {
|
||||
static_assert(
|
||||
std::is_constructible<json::value, EleType>::value,
|
||||
"Parameter can't be used to construct a json::value");
|
||||
for (auto&& ele : vec) {
|
||||
_array_data.emplace_back(std::move(ele));
|
||||
}
|
||||
}
|
||||
|
||||
~array() noexcept = default;
|
||||
|
||||
|
||||
@@ -125,7 +125,18 @@ namespace json
|
||||
value &operator[](size_t pos);
|
||||
value &operator[](const std::string &key);
|
||||
value &operator[](std::string &&key);
|
||||
explicit operator bool() const noexcept { return valid(); }
|
||||
//explicit operator bool() const noexcept { return valid(); }
|
||||
|
||||
explicit operator bool() const { return as_boolean(); }
|
||||
explicit operator int() const { return as_integer(); }
|
||||
explicit operator long() const { return as_long(); }
|
||||
explicit operator unsigned long() const { return as_unsigned_long(); }
|
||||
explicit operator long long() const { return as_long_long(); }
|
||||
explicit operator unsigned long long() const { return as_unsigned_long_long(); }
|
||||
explicit operator float() const { return as_float(); }
|
||||
explicit operator double() const { return as_double(); }
|
||||
explicit operator long double() const { return as_long_double(); }
|
||||
explicit operator std::string() const { return as_string(); }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
|
||||
@@ -44,6 +44,7 @@ Global
|
||||
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|Any CPU
|
||||
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|Any CPU
|
||||
{4AC1CFFF-D7B8-4FDA-BCFD-E4754275979B}.Release|x64.ActiveCfg = Release|x64
|
||||
{4AC1CFFF-D7B8-4FDA-BCFD-E4754275979B}.Release|x64.Build.0 = Release|x64
|
||||
{4AC1CFFF-D7B8-4FDA-BCFD-E4754275979B}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
<ClInclude Include="include\Configer.h" />
|
||||
<ClInclude Include="include\Identify.h" />
|
||||
<ClInclude Include="include\Logger.hpp" />
|
||||
<ClInclude Include="include\RecruitConfiger.h" />
|
||||
<ClInclude Include="include\OpenRecruitConfiger.h" />
|
||||
<ClInclude Include="include\Task.h" />
|
||||
<ClInclude Include="include\Updater.h" />
|
||||
<ClInclude Include="include\Version.h" />
|
||||
<ClInclude Include="include\WinMacro.h" />
|
||||
@@ -37,7 +38,8 @@
|
||||
<ClCompile Include="src\AsstCaller.cpp" />
|
||||
<ClCompile Include="src\Configer.cpp" />
|
||||
<ClCompile Include="src\Identify.cpp" />
|
||||
<ClCompile Include="src\RecruitConfiger.cpp" />
|
||||
<ClCompile Include="src\OpenRecruitConfiger.cpp" />
|
||||
<ClCompile Include="src\Task.cpp" />
|
||||
<ClCompile Include="src\Updater.cpp" />
|
||||
<ClCompile Include="src\WinMacro.cpp" />
|
||||
</ItemGroup>
|
||||
@@ -112,7 +114,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(ProjectDir)include;$(SolutionDir)3rdPart\include;$(SolutionDir)3rdPart\include\meojson;$(SolutionDir)\3rdPart\include\opencv;$(SolutionDir)\3rdPart\include\opencv2;$(SolutionDir)3drPart\include\OcrLiteNcnn;;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(ProjectDir)include;$(SolutionDir)3rdPart\include;$(SolutionDir)3rdPart\include\meojson;$(SolutionDir)3rdPart\include\opencv;$(SolutionDir)3rdPart\include\opencv2;$(SolutionDir)3drPart\include\OcrLiteNcnn;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(TargetDir);$(Solution)\3rdPart\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@@ -153,12 +155,12 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;MEO_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;CSHARP_API;MEO_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<LanguageStandard_C>stdc11</LanguageStandard_C>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -190,15 +192,18 @@ xcopy /e /y /i $(SolutionDir)3rdPart\resource $(TargetDir)resource</Command>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<FunctionLevelLinking>
|
||||
</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;MEO_DLL_EXPORTS;LOG_TRACE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;CSHARP_API;MEO_DLL_EXPORTS;LOG_TRACE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<LanguageStandard_C>stdc11</LanguageStandard_C>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
|
||||
@@ -45,12 +45,15 @@
|
||||
<ClInclude Include="include\AsstPort.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\RecruitConfiger.h">
|
||||
<ClInclude Include="include\OpenRecruitConfiger.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\Version.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\Task.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\Configer.cpp">
|
||||
@@ -71,7 +74,10 @@
|
||||
<ClCompile Include="src\Updater.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\RecruitConfiger.cpp">
|
||||
<ClCompile Include="src\OpenRecruitConfiger.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Task.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
||||
19
MeoAssistance/README.md
Normal file
19
MeoAssistance/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# MeoAssistance
|
||||
|
||||
辅助的底层C++模块
|
||||
|
||||
## 控制
|
||||
|
||||
- 窗口控制、点击、截图,使用Win32 Api
|
||||
- 部分不响应句柄消息的模拟器,使用Adb控制
|
||||
|
||||
## 识别
|
||||
|
||||
- 图像识别:[opencv](https://github.com/opencv/opencv.git)
|
||||
- 文字识别:[chineseocr_lite](https://github.com/DayBreak-u/chineseocr_lite.git),进行了封装和魔改的版本[MistEO/chineseocr_lite](https://github.com/MistEO/chineseocr_lite)
|
||||
|
||||
## 解析
|
||||
|
||||
- Json解析:[meojson](https://github.com/MistEO/meojson.git) (我感谢我自己!)
|
||||
- 公招干员信息:[明日方舟工具箱](https://www.bigfun.cn/tools/aktools/hr)
|
||||
- 公招新增干员信息:[PRTS.wiki](http://prts.wiki/w/%E9%A6%96%E9%A1%B5)
|
||||
@@ -6,11 +6,10 @@
|
||||
#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;
|
||||
@@ -20,54 +19,58 @@ namespace asst {
|
||||
class WinMacro;
|
||||
class Identify;
|
||||
|
||||
class MEOAPI_PORT Assistance
|
||||
class Assistance
|
||||
{
|
||||
public:
|
||||
Assistance();
|
||||
Assistance(TaskCallback callback = nullptr, void * callback_arg = nullptr);
|
||||
~Assistance();
|
||||
|
||||
std::optional<std::string> catch_emulator(const std::string& emulator_name = std::string());
|
||||
|
||||
void start(const std::string& task);
|
||||
// 开始刷理智
|
||||
void start_sanity();
|
||||
// 开始访问基建
|
||||
void start_visit();
|
||||
// 开始公开招募操作
|
||||
void start_open_recruit(const std::vector<int>& required_level, bool set_time = true);
|
||||
// 开始匹配任务,调试用
|
||||
void start_match_task(const std::string& task, bool block = true);
|
||||
|
||||
void stop(bool block = true);
|
||||
|
||||
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);
|
||||
|
||||
bool print_window(const std::string& filename, bool block = true);
|
||||
|
||||
// 计算公开招募,需要当前处在选择公招Tag的页面
|
||||
// 参数:
|
||||
// required_level:需要的等级,最优组合的最低等级,在required_level中,才会进行点击
|
||||
// set_time:是否自动设置时间(9小时)
|
||||
// 返回值:
|
||||
// std::vector< std::pair < Tags名vector,这个Tags组合可能出的干员组合 > >
|
||||
std::optional<std::vector<std::pair<std::vector<std::string>, OperCombs>>>
|
||||
open_recruit(const std::vector<int>& required_level, bool set_time = true);
|
||||
private:
|
||||
static void working_proc(Assistance* pThis);
|
||||
static void working_proc(Assistance* p_this);
|
||||
static void msg_proc(Assistance* p_this);
|
||||
static void task_callback(TaskMsg msg, const json::value& detail, void* custom_arg);
|
||||
|
||||
cv::Mat get_format_image();
|
||||
void set_control_scale(int cur_width, int cur_height);
|
||||
void append_match_task(const std::vector<std::string>& tasks);
|
||||
void append_task(const json::value& detail);
|
||||
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;
|
||||
|
||||
bool m_thread_exit = false;
|
||||
std::queue<std::shared_ptr<AbstractTask>> m_tasks_queue;
|
||||
TaskCallback m_callback = nullptr;
|
||||
void* m_callback_arg = nullptr;
|
||||
|
||||
bool m_thread_idle = true;
|
||||
std::thread m_working_thread;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_condvar;
|
||||
bool m_thread_exit = false;
|
||||
bool m_thread_running = false;
|
||||
std::vector<std::string> m_next_tasks;
|
||||
|
||||
Configer m_configer;
|
||||
RecruitConfiger m_recruit_configer;
|
||||
std::thread m_msg_thread;
|
||||
std::queue<std::pair<TaskMsg, json::value>> m_msg_queue;
|
||||
std::mutex m_msg_mutex;
|
||||
std::condition_variable m_msg_condvar;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,19 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Assistance.h"
|
||||
#include "AsstPort.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
namespace asst {
|
||||
class Assistance;
|
||||
}
|
||||
typedef void (MEO_CALL *AsstCallback)(int msg, const char* detail_json, void* custom_arg);
|
||||
|
||||
MEOAPI_PORT asst::Assistance* CreateAsst();
|
||||
MEOAPI_PORT void DestoryAsst(asst::Assistance* p_asst);
|
||||
MEOAPI_PORT asst::Assistance* AsstCreate();
|
||||
MEOAPI_PORT asst::Assistance* AsstCreateEx(AsstCallback callback, void* custom_arg);
|
||||
MEOAPI_PORT void AsstDestory(asst::Assistance* p_asst);
|
||||
MEOAPI_PORT bool AsstCatchEmulator(asst::Assistance* p_asst);
|
||||
MEOAPI_PORT void AsstStart(asst::Assistance* p_asst, const char* task);
|
||||
MEOAPI_PORT void AsstStop(asst::Assistance* p_asst);
|
||||
MEOAPI_PORT bool AsstSetParam(asst::Assistance* p_asst, const char* type, const char* param, const char* value);
|
||||
MEOAPI_PORT bool AsstGetParam(asst::Assistance* p_asst, const char* type, const char* param, char * buffer, int buffer_size);
|
||||
MEOAPI_PORT bool AsstRunOpenRecruit(asst::Assistance* p_asst, const int required_level[], bool set_time, char * result_buffer, int bufsize, int& maybe_level);
|
||||
MEOAPI_PORT bool AsstRunOpenRecruit(asst::Assistance* p_asst, const int required_level[], bool set_time);
|
||||
|
||||
MEOAPI_PORT bool CheckVersionUpdate(char* tag_buffer, int tag_bufsize, char* html_url_buffer, int html_bufsize, char* body_buffer, int body_bufsize);
|
||||
|
||||
|
||||
@@ -25,51 +25,24 @@ namespace asst {
|
||||
return os << _type_name.at(type);
|
||||
}
|
||||
|
||||
enum class TaskType {
|
||||
enum class MatchTaskType {
|
||||
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)
|
||||
{
|
||||
return static_cast<std::underlying_type<TaskType>::type>(lhs) & static_cast<std::underlying_type<TaskType>::type>(rhs);
|
||||
}
|
||||
static std::ostream& operator<<(std::ostream& os, const TaskType& task)
|
||||
{
|
||||
static std::unordered_map<TaskType, std::string> _type_name = {
|
||||
{TaskType::Invalid, "Invalid"},
|
||||
{TaskType::BasicClick, "BasicClick"},
|
||||
{TaskType::ClickSelf, "ClickSelf"},
|
||||
{TaskType::ClickRect, "ClickRect"},
|
||||
{TaskType::ClickRand, "ClickRand"},
|
||||
{TaskType::DoNothing, "DoNothing"},
|
||||
{TaskType::Stop, "Stop"},
|
||||
{TaskType::PrintWindow, "PrintWindow"}
|
||||
};
|
||||
return os << _type_name.at(task);
|
||||
}
|
||||
|
||||
enum class AlgorithmType {
|
||||
JustReturn,
|
||||
MatchTemplate,
|
||||
CompareHist
|
||||
CompareHist,
|
||||
OcrDetect
|
||||
};
|
||||
static std::ostream& operator<<(std::ostream& os, const AlgorithmType& type)
|
||||
{
|
||||
static std::unordered_map<AlgorithmType, std::string> _type_name = {
|
||||
{AlgorithmType::JustReturn, "JustReturn"},
|
||||
{AlgorithmType::MatchTemplate, "MatchTemplate"},
|
||||
{AlgorithmType::CompareHist, "CompareHist"}
|
||||
};
|
||||
return os << _type_name.at(type);
|
||||
}
|
||||
|
||||
struct Point
|
||||
{
|
||||
@@ -147,10 +120,11 @@ namespace asst {
|
||||
};
|
||||
|
||||
struct TaskInfo {
|
||||
std::string name; // 任务名
|
||||
std::string template_filename; // 匹配模板图片文件名
|
||||
double threshold = 0; // 模板匹配阈值
|
||||
double cache_threshold = 0; // 直方图比较阈值
|
||||
TaskType type = TaskType::Invalid; // 任务类型
|
||||
double templ_threshold = 0; // 模板匹配阈值
|
||||
double hist_threshold = 0; // 直方图比较阈值
|
||||
MatchTaskType type = MatchTaskType::Invalid; // 任务类型
|
||||
std::vector<std::string> next; // 下一个可能的任务(列表)
|
||||
int exec_times = 0; // 任务已执行了多少次
|
||||
int max_times = INT_MAX; // 任务最多执行多少次
|
||||
@@ -164,7 +138,8 @@ namespace asst {
|
||||
|
||||
struct Options {
|
||||
bool identify_cache = false; // 图像识别缓存功能:开启后可以大幅降低CPU消耗,但需要保证要识别的按钮每次的位置不会改变
|
||||
int identify_delay = 0; // 图像识别延时:越快操作越快,但会增加CPU消耗
|
||||
int task_identify_delay = 0; // 识别任务间延时:越快操作越快,但会增加CPU消耗
|
||||
int task_control_delay = 0; // 点击任务间延迟:越快点的越快,但是太快了句柄可能不响应
|
||||
int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时
|
||||
int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时
|
||||
bool print_window = false; // 截图功能:开启后每次结算界面会截图到screenshot目录下
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// The way how the function is called
|
||||
#if !defined(MEO_CALL)
|
||||
#if defined(_WIN32)
|
||||
#define MEO_CALL __stdcall
|
||||
#else
|
||||
#define ISSCALL
|
||||
#endif /* _WIN32 */
|
||||
#endif /* ISSCALL */
|
||||
|
||||
// The function exported symbols
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#define MEO_DLL_IMPORT __declspec(dllimport)
|
||||
@@ -22,3 +31,7 @@
|
||||
#else
|
||||
#define MEOAPI_PORT MEO_DLL_IMPORT
|
||||
#endif // MEO_DLL_EXPORTS
|
||||
|
||||
#define MEOAPI MEOAPI_PORT MEO_CALL
|
||||
|
||||
#define MEOLOCAL MEO_DLL_LOCAL MEO_CALL
|
||||
@@ -11,34 +11,36 @@ namespace asst {
|
||||
class Configer
|
||||
{
|
||||
public:
|
||||
Configer() = default;
|
||||
~Configer() = default;
|
||||
|
||||
Configer(const Configer& rhs) = default;
|
||||
Configer(Configer&& rhs) noexcept = default;
|
||||
static Configer& get_instance() {
|
||||
static Configer unique_instance;
|
||||
return unique_instance;
|
||||
}
|
||||
|
||||
bool load(const std::string& filename);
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
Configer() = default;
|
||||
Configer(const Configer & rhs) = default;
|
||||
Configer(Configer && rhs) noexcept = default;
|
||||
|
||||
bool _load(const std::string& filename);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "Version.h"
|
||||
|
||||
namespace asst {
|
||||
class MEOAPI_PORT Logger {
|
||||
class Logger {
|
||||
public:
|
||||
~Logger() = default;
|
||||
|
||||
@@ -110,4 +110,5 @@ namespace asst {
|
||||
#define DebugTrace Logger::get_instance().log_trace
|
||||
#define DebugTraceInfo Logger::get_instance().log_info
|
||||
#define DebugTraceError Logger::get_instance().log_error
|
||||
#define DebugTraceFunction LoggerAux _func_aux(__FUNCTION__)
|
||||
#define DebugTraceFunction LoggerAux _func_aux(__FUNCTION__)
|
||||
#define DebugTraceScope LoggerAux _func_aux
|
||||
40
MeoAssistance/include/OpenRecruitConfiger.h
Normal file
40
MeoAssistance/include/OpenRecruitConfiger.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
class OpenRecruitConfiger
|
||||
{
|
||||
public:
|
||||
~OpenRecruitConfiger() = default;
|
||||
|
||||
static OpenRecruitConfiger& get_instance() {
|
||||
static OpenRecruitConfiger unique_instance;
|
||||
return unique_instance;
|
||||
}
|
||||
|
||||
bool load(const std::string& filename);
|
||||
|
||||
OpenRecruitConfiger& operator=(const OpenRecruitConfiger& rhs) = default;
|
||||
OpenRecruitConfiger& operator=(OpenRecruitConfiger&& rhs) noexcept = default;
|
||||
|
||||
std::unordered_set<std::string> m_all_tags;
|
||||
std::unordered_set<std::string> m_all_types;
|
||||
|
||||
std::vector<OperInfo> m_all_opers;
|
||||
|
||||
constexpr static int CorrectNumberOfTags = 5;
|
||||
|
||||
private:
|
||||
OpenRecruitConfiger() = default;
|
||||
OpenRecruitConfiger(const OpenRecruitConfiger & rhs) = default;
|
||||
OpenRecruitConfiger(OpenRecruitConfiger && rhs) noexcept = default;
|
||||
|
||||
bool _load(const std::string& filename);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "AsstDef.h"
|
||||
|
||||
namespace asst {
|
||||
class RecruitConfiger
|
||||
{
|
||||
public:
|
||||
|
||||
RecruitConfiger() = default;
|
||||
~RecruitConfiger() = default;
|
||||
|
||||
RecruitConfiger(const RecruitConfiger& rhs) = default;
|
||||
RecruitConfiger(RecruitConfiger&& rhs) noexcept = default;
|
||||
|
||||
bool load(const std::string& filename);
|
||||
|
||||
|
||||
RecruitConfiger& operator=(const RecruitConfiger& rhs) = default;
|
||||
RecruitConfiger& operator=(RecruitConfiger&& rhs) noexcept = default;
|
||||
|
||||
std::unordered_set<std::string> m_all_tags;
|
||||
std::unordered_set<std::string> m_all_types;
|
||||
|
||||
std::vector<OperInfo> m_opers;
|
||||
|
||||
private:
|
||||
bool _load(const std::string& filename);
|
||||
};
|
||||
|
||||
}
|
||||
205
MeoAssistance/include/Task.h
Normal file
205
MeoAssistance/include/Task.h
Normal file
@@ -0,0 +1,205 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AsstDef.h"
|
||||
#include "AsstAux.h"
|
||||
|
||||
#include <json.h>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
class Mat;
|
||||
}
|
||||
namespace json
|
||||
{
|
||||
class value;
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
|
||||
class WinMacro;
|
||||
class Identify;
|
||||
class Configer;
|
||||
class OpenRecruitConfiger;
|
||||
|
||||
enum TaskType {
|
||||
TaskTypeInvalid = 0,
|
||||
TaskTypeWindowZoom = 1,
|
||||
TaskTypeRecognition = 2,
|
||||
TaskTypeClick = 4
|
||||
};
|
||||
|
||||
enum class TaskMsg {
|
||||
/* Error Msg */
|
||||
PtrIsNull,
|
||||
ImageIsEmpty,
|
||||
WindowMinimized,
|
||||
/* Info Msg */
|
||||
TaskStart,
|
||||
ImageMatched,
|
||||
TaskMatched,
|
||||
ReachedLimit,
|
||||
ReadyToSleep,
|
||||
EndOfSleep,
|
||||
AppendMatchTask,
|
||||
AppendTask,
|
||||
TaskCompleted,
|
||||
PrintWindow,
|
||||
TaskStop,
|
||||
/* Open Recruit Msg */
|
||||
TextDetected,
|
||||
RecruitTagsDetected,
|
||||
OcrResultError,
|
||||
RecruitSpecialTag,
|
||||
RecruitResult
|
||||
};
|
||||
static std::ostream& operator<<(std::ostream& os, const TaskMsg& type)
|
||||
{
|
||||
static const std::unordered_map<TaskMsg, std::string> _type_name = {
|
||||
{TaskMsg::PtrIsNull, "PtrIsNull"},
|
||||
{TaskMsg::ImageIsEmpty, "ImageIsEmpty"},
|
||||
{TaskMsg::WindowMinimized, "WindowMinimized"},
|
||||
{TaskMsg::TaskStart, "TaskStart"},
|
||||
{TaskMsg::ImageMatched, "ImageMatched"},
|
||||
{TaskMsg::TaskMatched, "TaskMatched"},
|
||||
{TaskMsg::ReachedLimit, "ReachedLimit"},
|
||||
{TaskMsg::ReadyToSleep, "ReadyToSleep"},
|
||||
{TaskMsg::EndOfSleep, "EndOfSleep"},
|
||||
{TaskMsg::AppendMatchTask, "AppendMatchTask"},
|
||||
{TaskMsg::TaskCompleted, "TaskCompleted"},
|
||||
{TaskMsg::PrintWindow, "PrintWindow"},
|
||||
{TaskMsg::TaskStop, "TaskStop"},
|
||||
{TaskMsg::TextDetected, "TextDetected"},
|
||||
{TaskMsg::RecruitTagsDetected, "RecruitTagsDetected"},
|
||||
{TaskMsg::OcrResultError, "OcrResultError"},
|
||||
{TaskMsg::RecruitSpecialTag, "RecruitSpecialTag"},
|
||||
{TaskMsg::RecruitResult, "RecruitResult"},
|
||||
{TaskMsg::AppendTask, "AppendTask"}
|
||||
};
|
||||
return os << _type_name.at(type);
|
||||
}
|
||||
|
||||
// TaskCallback Task的消息回调函数
|
||||
// 参数:
|
||||
// TaskMsg 消息类型
|
||||
// const json::value& 消息详情json,每种消息不同,Todo,需要补充个协议文档啥的
|
||||
// void* 外部调用者自定义参数,每次回调会带出去,建议传个(void*)this指针进来
|
||||
using TaskCallback = std::function<void(TaskMsg, const json::value&, void*)>;
|
||||
|
||||
class AbstractTask
|
||||
{
|
||||
public:
|
||||
AbstractTask(TaskCallback callback, void* callback_arg);
|
||||
~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);
|
||||
virtual int get_task_type() { return m_task_type; }
|
||||
virtual void set_retry_times(int times) { m_retry_times = times; }
|
||||
virtual int get_retry_times() { return m_retry_times; }
|
||||
protected:
|
||||
virtual cv::Mat get_format_image();
|
||||
virtual bool set_control_scale(int cur_width, int cur_height);
|
||||
virtual void sleep(unsigned millisecond);
|
||||
virtual bool print_window(const std::string& dir);
|
||||
|
||||
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;
|
||||
void* m_callback_arg = NULL;
|
||||
bool* m_exit_flag = NULL;
|
||||
int m_task_type = TaskType::TaskTypeInvalid;
|
||||
int m_retry_times = INT_MAX;
|
||||
};
|
||||
|
||||
class ClickTask : public AbstractTask
|
||||
{
|
||||
public:
|
||||
ClickTask(TaskCallback callback, void* callback_arg)
|
||||
: AbstractTask(callback, callback_arg)
|
||||
{
|
||||
m_task_type = TaskType::TaskTypeClick;
|
||||
}
|
||||
virtual bool run() override;
|
||||
void set_rect(asst::Rect rect) { m_rect = std::move(rect); };
|
||||
protected:
|
||||
asst::Rect m_rect;
|
||||
};
|
||||
|
||||
class MatchTask : public AbstractTask
|
||||
{
|
||||
public:
|
||||
MatchTask(TaskCallback callback, void* callback_arg);
|
||||
|
||||
virtual bool run() override;
|
||||
|
||||
virtual void set_tasks(const std::vector<std::string>& cur_tasks_name) {
|
||||
m_cur_tasks_name = cur_tasks_name;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::optional<std::string> match_image(asst::Rect* matched_rect = NULL);
|
||||
void exec_click_task(TaskInfo& task, const asst::Rect& matched_rect);
|
||||
|
||||
std::vector<std::string> m_cur_tasks_name;
|
||||
};
|
||||
|
||||
class OcrAbstractTask : public AbstractTask
|
||||
{
|
||||
public:
|
||||
OcrAbstractTask(TaskCallback callback, void* callback_arg);
|
||||
virtual bool run() override = 0;
|
||||
|
||||
protected:
|
||||
std::vector<TextArea> ocr_detect();
|
||||
|
||||
template<typename FilterArray, typename ReplaceMap>
|
||||
std::vector<TextArea> text_filter(const std::vector<TextArea>& src,
|
||||
const FilterArray& filter_array, const ReplaceMap& replace_map)
|
||||
{
|
||||
std::vector<TextArea> dst;
|
||||
for (const TextArea& text_area : src) {
|
||||
TextArea temp = text_area;
|
||||
for (const auto& [old_str, new_str] : replace_map) {
|
||||
temp.text = StringReplaceAll(temp.text, old_str, new_str);
|
||||
}
|
||||
for (const auto& text : filter_array) {
|
||||
if (temp.text == text) {
|
||||
dst.emplace_back(std::move(temp));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
class OpenRecruitTask : public OcrAbstractTask
|
||||
{
|
||||
public:
|
||||
OpenRecruitTask(TaskCallback callback, void* callback_arg);
|
||||
|
||||
virtual bool run() override;
|
||||
virtual void set_param(std::vector<int> required_level, bool set_time = true);
|
||||
|
||||
protected:
|
||||
std::vector<int> m_required_level;
|
||||
bool m_set_time = false;
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "AsstPort.h"
|
||||
|
||||
namespace asst {
|
||||
struct MEOAPI_PORT VersionInfo {
|
||||
struct VersionInfo {
|
||||
std::string tag_name;
|
||||
std::string html_url;
|
||||
std::string down_url;
|
||||
@@ -17,7 +17,7 @@ namespace asst {
|
||||
std::string body;
|
||||
};
|
||||
|
||||
class MEOAPI_PORT Updater
|
||||
class Updater
|
||||
{
|
||||
static const std::string GithubReleaseLastestApiUrl;
|
||||
static const std::string GithubReleaseApiUrl;
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
#include "Identify.h"
|
||||
#include "Logger.hpp"
|
||||
#include "AsstAux.h"
|
||||
#include "Task.h"
|
||||
#include "OpenRecruitConfiger.h"
|
||||
|
||||
#include <json.h>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#include <time.h>
|
||||
@@ -13,54 +16,63 @@
|
||||
|
||||
using namespace asst;
|
||||
|
||||
Assistance::Assistance()
|
||||
Assistance::Assistance(TaskCallback callback, void *callback_arg)
|
||||
: m_callback(callback), m_callback_arg(callback_arg)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
|
||||
m_configer.load(GetResourceDir() + "config.json");
|
||||
m_recruit_configer.load(GetResourceDir() + "operInfo.json");
|
||||
Configer::get_instance().load(GetResourceDir() + "config.json");
|
||||
OpenRecruitConfiger::get_instance().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] : Configer::get_instance().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(Configer::get_instance().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(Configer::get_instance().m_options.ocr_gpu_index, Configer::get_instance().m_options.ocr_thread_number);
|
||||
m_identify_ptr->ocr_init_models(GetResourceDir() + "OcrLiteNcnn\\models\\");
|
||||
|
||||
m_working_thread = std::thread(working_proc, this);
|
||||
m_msg_thread = std::thread(msg_proc, this);
|
||||
}
|
||||
|
||||
Assistance::~Assistance()
|
||||
{
|
||||
DebugTraceFunction;
|
||||
|
||||
//if (m_pWindow != nullptr) {
|
||||
// m_pWindow->showWindow();
|
||||
//if (m_window_ptr != nullptr) {
|
||||
// m_window_ptr->showWindow();
|
||||
//}
|
||||
|
||||
m_thread_exit = true;
|
||||
m_thread_running = false;
|
||||
m_thread_idle = true;
|
||||
m_condvar.notify_all();
|
||||
m_msg_condvar.notify_all();
|
||||
|
||||
if (m_working_thread.joinable()) {
|
||||
if (m_working_thread.joinable())
|
||||
{
|
||||
m_working_thread.join();
|
||||
}
|
||||
if (m_msg_thread.joinable())
|
||||
{
|
||||
m_msg_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> Assistance::catch_emulator(const std::string& emulator_name)
|
||||
std::optional<std::string> Assistance::catch_emulator(const std::string &emulator_name)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
|
||||
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();
|
||||
auto create_handles = [&](const EmulatorInfo &info) -> bool
|
||||
{
|
||||
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;
|
||||
@@ -69,46 +81,82 @@ std::optional<std::string> Assistance::catch_emulator(const std::string& emulato
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
// 自动匹配模拟器,逐个找
|
||||
if (emulator_name.empty()) {
|
||||
for (const auto& [name, info] : m_configer.m_handles)
|
||||
if (emulator_name.empty())
|
||||
{
|
||||
for (const auto &[name, info] : Configer::get_instance().m_handles)
|
||||
{
|
||||
ret = create_handles(info);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
{
|
||||
cor_name = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // 指定的模拟器
|
||||
ret = create_handles(m_configer.m_handles[emulator_name]);
|
||||
else
|
||||
{ // 指定的模拟器
|
||||
ret = create_handles(Configer::get_instance().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;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
m_inited = false;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void Assistance::start(const std::string& task)
|
||||
void asst::Assistance::start_sanity()
|
||||
{
|
||||
start_match_task("SanityBegin");
|
||||
}
|
||||
|
||||
void asst::Assistance::start_visit()
|
||||
{
|
||||
start_match_task("VisitBegin");
|
||||
}
|
||||
|
||||
void Assistance::start_match_task(const std::string &task, bool block)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
DebugTrace("Start |", task);
|
||||
DebugTrace("Start |", task, block ? "block" : "non block");
|
||||
if (!m_thread_idle || !m_inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock;
|
||||
if (block)
|
||||
{
|
||||
lock = std::unique_lock<std::mutex>(m_mutex);
|
||||
clear_exec_times();
|
||||
m_identify_ptr->clear_cache();
|
||||
}
|
||||
|
||||
if (m_thread_running || !m_inited) {
|
||||
append_match_task({task});
|
||||
|
||||
m_thread_idle = false;
|
||||
m_condvar.notify_one();
|
||||
}
|
||||
|
||||
void asst::Assistance::start_open_recruit(const std::vector<int> &required_level, bool set_time)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
if (!m_thread_idle || !m_inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_configer.clear_exec_times();
|
||||
|
||||
m_pIder->clear_cache();
|
||||
m_next_tasks.clear();
|
||||
m_next_tasks.emplace_back(task);
|
||||
m_thread_running = true;
|
||||
auto task_ptr = std::make_shared<OpenRecruitTask>(task_callback, (void *)this);
|
||||
task_ptr->set_param(required_level, set_time);
|
||||
m_tasks_queue.emplace(task_ptr);
|
||||
|
||||
m_thread_idle = false;
|
||||
m_condvar.notify_one();
|
||||
}
|
||||
|
||||
@@ -117,450 +165,188 @@ void Assistance::stop(bool block)
|
||||
DebugTraceFunction;
|
||||
DebugTrace("Stop |", block ? "block" : "non block");
|
||||
|
||||
m_thread_idle = true;
|
||||
|
||||
std::unique_lock<std::mutex> lock;
|
||||
if (block) { // 外部调用
|
||||
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();
|
||||
decltype(m_tasks_queue) 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)
|
||||
bool Assistance::set_param(const std::string &type, const std::string ¶m, const std::string &value)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
DebugTrace("SetParam |", type, param, value);
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_configer.set_param(type, param, value);
|
||||
return Configer::get_instance().set_param(type, param, value);
|
||||
}
|
||||
|
||||
std::optional<std::string> Assistance::get_param(const std::string& type, const std::string& param)
|
||||
{
|
||||
// DebugTraceFunction;
|
||||
if (type == "status") {
|
||||
if (param == "running") {
|
||||
return std::to_string(m_thread_running);
|
||||
}
|
||||
else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_configer.get_param(type, param);
|
||||
}
|
||||
|
||||
bool asst::Assistance::print_window(const std::string& filename, bool block)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
DebugTrace("print_window |", block ? "block" : "non block");
|
||||
|
||||
std::unique_lock<std::mutex> lock;
|
||||
if (block) { // 外部调用
|
||||
lock = std::unique_lock<std::mutex>(m_mutex);
|
||||
}
|
||||
|
||||
const cv::Mat& image = get_format_image();
|
||||
// 保存的截图额外再裁剪掉一圈,不然企鹅物流识别不出来
|
||||
int offset = m_configer.m_options.print_window_crop_offset;
|
||||
cv::Rect rect(offset, offset, image.cols - offset * 2, image.rows - offset * 2);
|
||||
bool ret = cv::imwrite(filename.c_str(), image(rect));
|
||||
|
||||
if (ret) {
|
||||
DebugTraceInfo("PrintWindow to", filename);
|
||||
}
|
||||
else {
|
||||
DebugTraceError("PrintWindow error", filename);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool asst::Assistance::find_text_and_click(const std::string& text, bool block)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
DebugTrace("find_text_and_click |", Utf8ToGbk(text), block ? "block" : "non block");
|
||||
|
||||
std::unique_lock<std::mutex> lock;
|
||||
if (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);
|
||||
|
||||
if (!result) {
|
||||
DebugTrace("Cannot found", Utf8ToGbk(text));
|
||||
return false;
|
||||
}
|
||||
|
||||
set_control_scale(image.cols, image.rows);
|
||||
return m_pCtrl->click(result.value());
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::pair<std::vector<std::string>, OperCombs>>>
|
||||
asst::Assistance::open_recruit(const std::vector<int>& required_level, bool set_time)
|
||||
void Assistance::working_proc(Assistance *p_this)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
int retry_times = 0;
|
||||
while (!p_this->m_thread_exit)
|
||||
{
|
||||
DebugTraceScope("Assistance::working_proc Loop");
|
||||
std::unique_lock<std::mutex> lock(p_this->m_mutex);
|
||||
if (!p_this->m_thread_idle && !p_this->m_tasks_queue.empty())
|
||||
{
|
||||
|
||||
const cv::Mat& image = get_format_image();
|
||||
set_control_scale(image.cols, image.rows);
|
||||
lock.unlock(); // 后面的计算耗时太长,先解锁
|
||||
|
||||
if (set_time) {
|
||||
start("RecruitTime");
|
||||
}
|
||||
|
||||
std::vector<TextArea> ider_result = m_pIder->ocr_detect(image);
|
||||
std::vector<TextArea> filt_result;
|
||||
std::string ider_str;
|
||||
for (TextArea& res : ider_result) {
|
||||
// 替换一些常见的文字识别错误
|
||||
// TODO: 这块时间复杂度有点高,待优化
|
||||
for (const auto& [src, cor] : m_configer.m_ocr_replace) {
|
||||
res.text = StringReplaceAll(res.text, src, cor);
|
||||
}
|
||||
ider_str += res.text + " ,";
|
||||
for (const std::string& t : m_recruit_configer.m_all_tags) {
|
||||
if (res.text == t) {
|
||||
filt_result.emplace_back(std::move(res));
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
std::shared_ptr<AbstractTask> task_ptr = p_this->m_tasks_queue.front();
|
||||
task_ptr->set_ptr(p_this->m_window_ptr, p_this->m_view_ptr, p_this->m_control_ptr, p_this->m_identify_ptr);
|
||||
task_ptr->set_exit_flag(&p_this->m_thread_idle);
|
||||
bool ret = task_ptr->run();
|
||||
if (ret)
|
||||
{
|
||||
retry_times = 0;
|
||||
p_this->m_tasks_queue.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ider_str.back() == ',') {
|
||||
ider_str.pop_back();
|
||||
}
|
||||
DebugTrace("All ocr text: ", Utf8ToGbk(ider_str));
|
||||
|
||||
if (filt_result.size() != 5) {
|
||||
DebugTraceError("Error, Tags recognition error!!!");
|
||||
stop(true);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::string> tags;
|
||||
for (const TextArea& t_a : filt_result) {
|
||||
tags.emplace_back(t_a.text);
|
||||
}
|
||||
DebugTraceInfo("All Tags", VectorToString(tags, true));
|
||||
|
||||
// Tags全组合
|
||||
std::vector<std::vector<std::string>> all_combs;
|
||||
int len = tags.size();
|
||||
int count = std::pow(2, len);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
std::vector<std::string> temp;
|
||||
for (int j = 0, mask = 1; j < len; ++j) {
|
||||
if ((i & mask) != 0) { // What the fuck???
|
||||
temp.emplace_back(tags.at(j));
|
||||
else // 失败了不pop,一直跑。 Todo: 设一个上限
|
||||
{
|
||||
++retry_times;
|
||||
}
|
||||
mask = mask * 2;
|
||||
}
|
||||
// 游戏里最多选择3个tag
|
||||
if (!temp.empty() && temp.size() <= 3) {
|
||||
all_combs.emplace_back(std::move(temp));
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::vector<std::string>, OperCombs> result_map;
|
||||
for (const std::vector<std::string>& comb : all_combs) {
|
||||
for (const OperInfo& cur_oper : m_recruit_configer.m_opers) {
|
||||
int matched_count = 0;
|
||||
// 组合中每一个tag,是否在干员tags中
|
||||
for (const std::string& tag : comb) {
|
||||
if (cur_oper.tags.find(tag) != cur_oper.tags.cend()) {
|
||||
++matched_count;
|
||||
// 如果下个任务是识别,就按识别的延时来;如果下个任务是点击,就按点击的延时来;……
|
||||
// 如果都符合,就取个最大值
|
||||
int delay = 0;
|
||||
if (!p_this->m_tasks_queue.empty())
|
||||
{
|
||||
int next_type = p_this->m_tasks_queue.front()->get_task_type();
|
||||
std::vector<int> candidate_delay = {0};
|
||||
if (next_type & TaskType::TaskTypeClick)
|
||||
{
|
||||
candidate_delay.emplace_back(Configer::get_instance().m_options.task_control_delay);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
if (next_type & TaskType::TaskTypeRecognition)
|
||||
{
|
||||
candidate_delay.emplace_back(Configer::get_instance().m_options.task_identify_delay);
|
||||
}
|
||||
delay = *std::max_element(candidate_delay.cbegin(), candidate_delay.cend());
|
||||
}
|
||||
if (matched_count == comb.size()) {
|
||||
static const std::string SeniorOper = GbkToUtf8("高级资深干员");
|
||||
// 高资tag和六星强绑定,如果没有高资tag,即使其他tag匹配上了也不可能出六星
|
||||
if (cur_oper.level == 6
|
||||
&& std::find(comb.cbegin(), comb.cend(), SeniorOper) == comb.cend()) {
|
||||
continue;
|
||||
}
|
||||
OperCombs& oper_combs = result_map[comb];
|
||||
oper_combs.opers.emplace_back(cur_oper);
|
||||
|
||||
if (cur_oper.level == 1 || cur_oper.level == 2) {
|
||||
if (oper_combs.min_level == 0) oper_combs.min_level = cur_oper.level;
|
||||
if (oper_combs.max_level == 0) oper_combs.max_level = cur_oper.level;
|
||||
// 一星、二星干员不计入最低等级,因为拉满9小时之后不可能出1、2星
|
||||
continue;
|
||||
}
|
||||
if (oper_combs.min_level == 0 || oper_combs.min_level > cur_oper.level) {
|
||||
oper_combs.min_level = cur_oper.level;
|
||||
}
|
||||
if (oper_combs.max_level == 0 || oper_combs.max_level < cur_oper.level) {
|
||||
oper_combs.max_level = cur_oper.level;
|
||||
}
|
||||
oper_combs.avg_level += cur_oper.level;
|
||||
}
|
||||
p_this->m_condvar.wait_until(lock,
|
||||
start_time + std::chrono::milliseconds(delay),
|
||||
[&]() -> bool
|
||||
{ return p_this->m_thread_idle; });
|
||||
}
|
||||
if (result_map.find(comb) != result_map.cend()) {
|
||||
OperCombs& oper_combs = result_map[comb];
|
||||
oper_combs.avg_level /= oper_combs.opers.size();
|
||||
else
|
||||
{
|
||||
p_this->m_thread_idle = true;
|
||||
p_this->m_condvar.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
// map没法按值排序,转个vector再排
|
||||
std::vector<std::pair<std::vector<std::string>, OperCombs>> result_vector;
|
||||
for (auto&& pair : result_map) {
|
||||
result_vector.emplace_back(std::move(pair));
|
||||
}
|
||||
std::sort(result_vector.begin(), result_vector.end(), [](const auto& lhs, const auto& rhs)
|
||||
->bool {
|
||||
// 最小等级大的,排前面
|
||||
if (lhs.second.min_level != rhs.second.min_level) {
|
||||
return lhs.second.min_level > rhs.second.min_level;
|
||||
}
|
||||
// 最大等级大的,排前面
|
||||
else if (lhs.second.max_level != rhs.second.max_level) {
|
||||
return lhs.second.max_level > rhs.second.max_level;
|
||||
}
|
||||
// 平均等级高的,排前面
|
||||
else if (std::fabs(lhs.second.avg_level - rhs.second.avg_level) < DoubleDiff) {
|
||||
return lhs.second.avg_level > rhs.second.avg_level;
|
||||
}
|
||||
// Tag数量少的,排前面
|
||||
else {
|
||||
return lhs.first.size() < rhs.first.size();
|
||||
}
|
||||
});
|
||||
|
||||
for (const auto& [combs, oper_combs] : result_vector) {
|
||||
std::string tag_str;
|
||||
for (const std::string& tag : combs) {
|
||||
tag_str += tag + " ,";
|
||||
}
|
||||
if (tag_str.back() == ',') {
|
||||
tag_str.pop_back();
|
||||
}
|
||||
|
||||
std::string opers_str;
|
||||
for (const OperInfo& oper : oper_combs.opers) {
|
||||
opers_str += std::to_string(oper.level) + "-" + oper.name + " ,";
|
||||
}
|
||||
if (opers_str.back() == ',') {
|
||||
opers_str.pop_back();
|
||||
}
|
||||
DebugTraceInfo("Tags:", VectorToString(combs, true), "May be recruited: ", Utf8ToGbk(opers_str));
|
||||
}
|
||||
|
||||
stop(true);
|
||||
if (!required_level.empty() && !result_vector.empty()) {
|
||||
if (std::find(required_level.cbegin(), required_level.cend(), result_vector[0].second.min_level)
|
||||
== required_level.cend()) {
|
||||
return result_vector;
|
||||
}
|
||||
const std::vector<std::string>& final_tags = result_vector[0].first;
|
||||
std::vector<TextArea> final_text_areas;
|
||||
|
||||
lock.lock();
|
||||
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);
|
||||
Sleep(300);
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
return result_vector;
|
||||
}
|
||||
|
||||
void Assistance::working_proc(Assistance* pThis)
|
||||
void Assistance::msg_proc(Assistance *p_this)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
|
||||
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);
|
||||
while (!p_this->m_thread_exit)
|
||||
{
|
||||
DebugTraceScope("Assistance::msg_proc Loop");
|
||||
std::unique_lock<std::mutex> lock(p_this->m_msg_mutex);
|
||||
if (!p_this->m_msg_queue.empty())
|
||||
{
|
||||
// 结构化绑定只能是引用,后续的pop会使引用失效,所以需要重新构造一份,这里采用了move的方式
|
||||
auto &&[temp_msg, temp_detail] = p_this->m_msg_queue.front();
|
||||
TaskMsg msg = std::move(temp_msg);
|
||||
json::value detail = std::move(temp_detail);
|
||||
p_this->m_msg_queue.pop();
|
||||
lock.unlock();
|
||||
|
||||
if (cur_image.empty()) {
|
||||
DebugTraceError("Unable to capture window image!!!");
|
||||
pThis->stop(false);
|
||||
continue;
|
||||
if (p_this->m_callback)
|
||||
{
|
||||
p_this->m_callback(msg, detail, p_this->m_callback_arg);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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),
|
||||
[&]() -> bool { return !pThis->m_thread_running; });
|
||||
}
|
||||
else {
|
||||
pThis->m_condvar.wait(lock);
|
||||
else
|
||||
{
|
||||
p_this->m_msg_condvar.wait(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat asst::Assistance::get_format_image()
|
||||
void Assistance::task_callback(TaskMsg msg, const json::value &detail, void *custom_arg)
|
||||
{
|
||||
const cv::Mat& raw_image = m_pView->getImage(m_pView->getWindowRect());
|
||||
if (raw_image.empty() || raw_image.rows < 100) {
|
||||
DebugTraceError("Window image error");
|
||||
return raw_image;
|
||||
DebugTrace("Assistance::task_callback |", msg, detail.to_string());
|
||||
|
||||
Assistance *p_this = (Assistance *)custom_arg;
|
||||
json::value more_detail = detail;
|
||||
switch (msg)
|
||||
{
|
||||
case TaskMsg::PtrIsNull:
|
||||
case TaskMsg::ImageIsEmpty:
|
||||
p_this->stop(false);
|
||||
break;
|
||||
case TaskMsg::WindowMinimized:
|
||||
p_this->m_window_ptr->showWindow();
|
||||
break;
|
||||
case TaskMsg::AppendMatchTask:
|
||||
more_detail["type"] = "MatchTask";
|
||||
[[fallthrough]];
|
||||
case TaskMsg::AppendTask:
|
||||
p_this->append_task(more_detail);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 把模拟器边框的一圈裁剪掉
|
||||
const EmulatorInfo& window_info = m_pView->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;
|
||||
if (p_this->m_callback)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(p_this->m_msg_mutex);
|
||||
p_this->m_msg_queue.emplace(msg, std::move(more_detail));
|
||||
p_this->m_msg_condvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void asst::Assistance::set_control_scale(int cur_width, int cur_height)
|
||||
void asst::Assistance::append_match_task(const std::vector<std::string> &tasks)
|
||||
{
|
||||
double scale_width = static_cast<double>(cur_width) / m_configer.DefaultWindowWidth;
|
||||
double scale_height = static_cast<double>(cur_height) / m_configer.DefaultWindowHeight;
|
||||
// 有些模拟器有可收缩的侧边,会增加宽度。
|
||||
// config.json中设置的是侧边展开后的offset
|
||||
// 如果用户把侧边收起来了,则有侧边的那头会额外裁剪掉一些,长度偏小
|
||||
// 所以按这里面长、宽里大的那个算,大的那边没侧边
|
||||
double scale = std::max(scale_width, scale_height);
|
||||
m_pCtrl->setControlScale(scale);
|
||||
auto task_ptr = std::make_shared<MatchTask>(task_callback, (void *)this);
|
||||
task_ptr->set_tasks(tasks);
|
||||
m_tasks_queue.emplace(task_ptr);
|
||||
}
|
||||
|
||||
void asst::Assistance::append_task(const json::value &detail)
|
||||
{
|
||||
std::string task_type = detail.at("type").as_string();
|
||||
if (task_type == "ClickTask")
|
||||
{
|
||||
auto task_ptr = std::make_shared<ClickTask>(task_callback, (void *)this);
|
||||
json::array rect_json = detail.at("rect").as_array();
|
||||
Rect rect(rect_json[0].as_integer(), rect_json[1].as_integer(), rect_json[2].as_integer(), rect_json[3].as_integer());
|
||||
task_ptr->set_rect(std::move(rect));
|
||||
m_tasks_queue.emplace(task_ptr);
|
||||
}
|
||||
else if (task_type == "MatchTask")
|
||||
{
|
||||
std::vector<std::string> next_vec;
|
||||
if (detail.exist("tasks"))
|
||||
{
|
||||
json::array next_arr = detail.at("tasks").as_array();
|
||||
for (const json::value &next_json : next_arr)
|
||||
{
|
||||
next_vec.emplace_back(next_json.as_string());
|
||||
}
|
||||
}
|
||||
else if (detail.exist("task"))
|
||||
{
|
||||
next_vec.emplace_back(detail.at("task").as_string());
|
||||
}
|
||||
append_match_task(next_vec);
|
||||
}
|
||||
// else if // TODO
|
||||
}
|
||||
|
||||
void Assistance::clear_exec_times()
|
||||
{
|
||||
for (auto &&pair : Configer::get_instance().m_all_tasks_info)
|
||||
{
|
||||
pair.second.exec_times = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,54 @@
|
||||
#include "AsstCaller.h"
|
||||
#include "Updater.h"
|
||||
#include "AsstAux.h"
|
||||
#include "Assistance.h"
|
||||
|
||||
#include <json_value.h>
|
||||
#include <string.h>
|
||||
|
||||
asst::Assistance* CreateAsst()
|
||||
#if 0
|
||||
#if _MSC_VER
|
||||
// Win32平台下Dll的入口
|
||||
BOOL APIENTRY DllMain(HANDLE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(hModule);
|
||||
UNREFERENCED_PARAMETER(lpReserved);
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#elif VA_GNUC
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
AsstCallback _callback = nullptr;
|
||||
|
||||
void CallbackTrans(asst::TaskMsg msg, const json::value& json, void* custom_arg)
|
||||
{
|
||||
_callback(static_cast<int>(msg), json.to_string().c_str(), custom_arg);
|
||||
}
|
||||
|
||||
asst::Assistance* AsstCreate()
|
||||
{
|
||||
return new asst::Assistance();
|
||||
}
|
||||
|
||||
void DestoryAsst(asst::Assistance* p_asst)
|
||||
MEOAPI_PORT asst::Assistance* AsstCreateEx(AsstCallback callback, void* custom_arg)
|
||||
{
|
||||
// 创建多实例回调会有问题,有空再慢慢整
|
||||
_callback = callback;
|
||||
return new asst::Assistance(CallbackTrans, custom_arg);
|
||||
}
|
||||
|
||||
void AsstDestory(asst::Assistance* p_asst)
|
||||
{
|
||||
if (p_asst == NULL) {
|
||||
return;
|
||||
@@ -39,7 +78,7 @@ void AsstStart(asst::Assistance* p_asst, const char* task)
|
||||
if (p_asst == NULL) {
|
||||
}
|
||||
|
||||
p_asst->start(task);
|
||||
p_asst->start_match_task(task);
|
||||
}
|
||||
|
||||
void AsstStop(asst::Assistance* p_asst)
|
||||
@@ -60,20 +99,7 @@ bool AsstSetParam(asst::Assistance* p_asst, const char* type, const char* param,
|
||||
return p_asst->set_param(type, param, value);
|
||||
}
|
||||
|
||||
bool AsstGetParam(asst::Assistance* p_asst, const char* type, const char* param, char* buffer, int buffer_size)
|
||||
{
|
||||
if (p_asst == NULL) {
|
||||
return false;
|
||||
}
|
||||
auto&& ret = p_asst->get_param(type, param);
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
strcpy_s(buffer, buffer_size, ret.value().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsstRunOpenRecruit(asst::Assistance* p_asst, const int required_level[], bool set_time, char* result_buffer, int bufsize, int& maybe_level)
|
||||
bool AsstRunOpenRecruit(asst::Assistance* p_asst, const int required_level[], bool set_time)
|
||||
{
|
||||
if (p_asst == NULL) {
|
||||
return false;
|
||||
@@ -81,30 +107,8 @@ bool AsstRunOpenRecruit(asst::Assistance* p_asst, const int required_level[], bo
|
||||
int len = sizeof required_level / sizeof(int);
|
||||
std::vector<int> level_vector;
|
||||
level_vector.assign(required_level, required_level + len);
|
||||
auto&& ret = p_asst->open_recruit(level_vector, set_time);
|
||||
if (ret) {
|
||||
// <std::vector<std::pair<std::vector<std::string>, OperCombs>>>
|
||||
std::string result_str;
|
||||
maybe_level = 0;
|
||||
for (auto&& [tags, oper_combs] : ret.value())
|
||||
{
|
||||
result_str += std::to_string(oper_combs.min_level) + "ÐÇTags: "
|
||||
+ asst::VectorToString(tags, true) + "\n\t";
|
||||
for (auto&& oper : oper_combs.opers)
|
||||
{
|
||||
result_str += std::to_string(oper.level) + " - " + asst::Utf8ToGbk(oper.name) + " ";
|
||||
}
|
||||
result_str += "\n\n";
|
||||
if (maybe_level == 0) {
|
||||
maybe_level = oper_combs.min_level;
|
||||
}
|
||||
}
|
||||
strcpy_s(result_buffer, bufsize, result_str.c_str());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
p_asst->start_open_recruit(level_vector, set_time);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "json.h"
|
||||
#include <json.h>
|
||||
#include "Logger.hpp"
|
||||
|
||||
using namespace asst;
|
||||
@@ -44,26 +44,26 @@ 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") {
|
||||
task_info.type = TaskType::ClickSelf;
|
||||
task_info.type = MatchTaskType::ClickSelf;
|
||||
}
|
||||
else if (type == "clickrand") {
|
||||
task_info.type = TaskType::ClickRand;
|
||||
task_info.type = MatchTaskType::ClickRand;
|
||||
}
|
||||
else if (type == "donothing" || type.empty()) {
|
||||
task_info.type = TaskType::DoNothing;
|
||||
task_info.type = MatchTaskType::DoNothing;
|
||||
}
|
||||
else if (type == "stop") {
|
||||
task_info.type = TaskType::Stop;
|
||||
task_info.type = MatchTaskType::Stop;
|
||||
}
|
||||
else if (type == "clickrect") {
|
||||
task_info.type = TaskType::ClickRect;
|
||||
task_info.type = MatchTaskType::ClickRect;
|
||||
}
|
||||
else {
|
||||
DebugTraceError("Task", param, "'s type error:", type);
|
||||
@@ -71,30 +71,14 @@ 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
@@ -118,7 +102,8 @@ bool asst::Configer::_load(const std::string& filename)
|
||||
|
||||
json::value& options_json = root["options"];
|
||||
{
|
||||
m_options.identify_delay = options_json["identifyDelay"].as_integer();
|
||||
m_options.task_identify_delay = options_json["taskIdentifyDelay"].as_integer();
|
||||
m_options.task_control_delay = options_json["taskControlDelay"].as_integer();
|
||||
m_options.identify_cache = options_json["identifyCache"].as_boolean();
|
||||
m_options.control_delay_lower = options_json["controlDelayRange"][0].as_integer();
|
||||
m_options.control_delay_upper = options_json["controlDelayRange"][1].as_integer();
|
||||
@@ -132,26 +117,27 @@ 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);
|
||||
if (type == "clickself") {
|
||||
task_info.type = TaskType::ClickSelf;
|
||||
task_info.type = MatchTaskType::ClickSelf;
|
||||
}
|
||||
else if (type == "clickrand") {
|
||||
task_info.type = TaskType::ClickRand;
|
||||
task_info.type = MatchTaskType::ClickRand;
|
||||
}
|
||||
else if (type == "donothing" || type.empty()) {
|
||||
task_info.type = TaskType::DoNothing;
|
||||
task_info.type = MatchTaskType::DoNothing;
|
||||
}
|
||||
else if (type == "stop") {
|
||||
task_info.type = TaskType::Stop;
|
||||
task_info.type = MatchTaskType::Stop;
|
||||
}
|
||||
else if (type == "clickrect") {
|
||||
task_info.type = TaskType::ClickRect;
|
||||
task_info.type = MatchTaskType::ClickRect;
|
||||
json::value & area_json = task_json["specificArea"];
|
||||
task_info.specific_area = Rect(
|
||||
area_json[0].as_integer(),
|
||||
@@ -160,7 +146,7 @@ bool asst::Configer::_load(const std::string& filename)
|
||||
area_json[3].as_integer());
|
||||
}
|
||||
else if (type == "printwindow") {
|
||||
task_info.type = TaskType::PrintWindow;
|
||||
task_info.type = MatchTaskType::PrintWindow;
|
||||
}
|
||||
else {
|
||||
DebugTraceError("Task:", name, "error:", type);
|
||||
@@ -191,7 +177,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))));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
#include "RecruitConfiger.h"
|
||||
#include "OpenRecruitConfiger.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "json.h"
|
||||
#include <json.h>
|
||||
#include "Logger.hpp"
|
||||
|
||||
using namespace asst;
|
||||
|
||||
bool RecruitConfiger::load(const std::string& filename)
|
||||
bool OpenRecruitConfiger::load(const std::string& filename)
|
||||
{
|
||||
DebugTraceFunction;
|
||||
DebugTrace("RecruitConfiger::load | ", filename);
|
||||
DebugTrace("OpenRecruitConfiger::load | ", filename);
|
||||
|
||||
RecruitConfiger temp;
|
||||
OpenRecruitConfiger temp;
|
||||
if (temp._load(filename)) {
|
||||
std::sort(temp.m_opers.begin(), temp.m_opers.end(), [](
|
||||
// °´¸ÉÔ±µÈ¼¶ÅŸöÐò
|
||||
std::sort(temp.m_all_opers.begin(), temp.m_all_opers.end(), [](
|
||||
const auto& lhs,
|
||||
const auto& rhs)
|
||||
-> bool {
|
||||
@@ -30,7 +31,7 @@ bool RecruitConfiger::load(const std::string& filename)
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::RecruitConfiger::_load(const std::string& filename)
|
||||
bool asst::OpenRecruitConfiger::_load(const std::string& filename)
|
||||
{
|
||||
std::ifstream ifs(filename, std::ios::in);
|
||||
if (!ifs.is_open()) {
|
||||
@@ -60,15 +61,15 @@ bool asst::RecruitConfiger::_load(const std::string& filename)
|
||||
m_all_tags.emplace(std::move(type_as_tag));
|
||||
|
||||
oper_temp.level = oper["level"].as_integer();
|
||||
oper_temp.sex = oper["sex"].as_string();
|
||||
oper_temp.sex = oper.get("sex", "unknown");
|
||||
for (const json::value& tag_value : oper["tags"].as_array()) {
|
||||
std::string tag = tag_value.as_string();
|
||||
oper_temp.tags.emplace(tag);
|
||||
m_all_tags.emplace(std::move(tag));
|
||||
}
|
||||
oper_temp.hidden = oper["hidden"].as_boolean();
|
||||
oper_temp.name_en = oper["name-en"].as_string();
|
||||
m_opers.emplace_back(std::move(oper_temp));
|
||||
oper_temp.hidden = oper.get("hidden", false);
|
||||
oper_temp.name_en = oper.get("name-en", "unknown");
|
||||
m_all_opers.emplace_back(std::move(oper_temp));
|
||||
}
|
||||
}
|
||||
catch (json::exception& e) {
|
||||
527
MeoAssistance/src/Task.cpp
Normal file
527
MeoAssistance/src/Task.cpp
Normal file
@@ -0,0 +1,527 @@
|
||||
#include "Task.h"
|
||||
#include "AsstDef.h"
|
||||
#include "WinMacro.h"
|
||||
#include "Identify.h"
|
||||
#include "Configer.h"
|
||||
#include "OpenRecruitConfiger.h"
|
||||
#include <json.h>
|
||||
#include "AsstAux.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace asst;
|
||||
|
||||
AbstractTask::AbstractTask(TaskCallback callback, void* callback_arg)
|
||||
:m_callback(callback),
|
||||
m_callback_arg(callback_arg)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
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 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, json::value(), m_callback_arg);
|
||||
return raw_image;
|
||||
}
|
||||
if (raw_image.rows < 100) {
|
||||
m_callback(TaskMsg::WindowMinimized, json::value(), m_callback_arg);
|
||||
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 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, m_callback_arg);
|
||||
|
||||
while ((m_exit_flag == NULL || *m_exit_flag == false)
|
||||
&& duration < millisecond) {
|
||||
duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - start).count();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
m_callback(TaskMsg::EndOfSleep, callback_json, m_callback_arg);
|
||||
}
|
||||
|
||||
bool AbstractTask::print_window(const std::string& dir)
|
||||
{
|
||||
const cv::Mat& image = get_format_image();
|
||||
if (image.empty()) {
|
||||
return false;
|
||||
}
|
||||
// 保存的截图额外再裁剪掉一圈,不然企鹅物流识别不出来
|
||||
int offset = Configer::get_instance().m_options.print_window_crop_offset;
|
||||
cv::Rect rect(offset, offset, image.cols - offset * 2, image.rows - offset * 2);
|
||||
|
||||
std::filesystem::create_directory(dir);
|
||||
const std::string time_str = StringReplaceAll(StringReplaceAll(GetFormatTimeString(), " ", "_"), ":", "-");
|
||||
const std::string filename = dir + time_str + ".png";
|
||||
|
||||
bool ret = cv::imwrite(filename.c_str(), image(rect));
|
||||
|
||||
json::value callback_json;
|
||||
callback_json["filename"] = StringReplaceAll(filename, "\\", "\\\\");
|
||||
callback_json["ret"] = ret;
|
||||
callback_json["offset"] = offset;
|
||||
m_callback(TaskMsg::PrintWindow, callback_json, m_callback_arg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MatchTask::MatchTask(TaskCallback callback, void* callback_arg)
|
||||
: AbstractTask(callback, callback_arg)
|
||||
{
|
||||
m_task_type = TaskType::TaskTypeRecognition | TaskType::TaskTypeClick;
|
||||
}
|
||||
|
||||
bool MatchTask::run()
|
||||
{
|
||||
if (m_view_ptr == NULL
|
||||
|| m_control_ptr == NULL
|
||||
|| m_identify_ptr == NULL
|
||||
|| m_control_ptr == NULL)
|
||||
{
|
||||
m_callback(TaskMsg::PtrIsNull, json::value(), m_callback_arg);
|
||||
return false;
|
||||
}
|
||||
m_callback(TaskMsg::TaskStart, json::object{ { "task_type", "MatchTask" } }, m_callback_arg);
|
||||
|
||||
Rect rect;
|
||||
auto&& ret = match_image(&rect);
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
TaskInfo& task = Configer::get_instance().m_all_tasks_info[ret.value()];
|
||||
|
||||
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 },
|
||||
{ "task_type", "MatchTask"}
|
||||
};
|
||||
m_callback(TaskMsg::TaskMatched, callback_json, m_callback_arg);
|
||||
|
||||
if (task.exec_times >= task.max_times)
|
||||
{
|
||||
m_callback(TaskMsg::ReachedLimit, callback_json, m_callback_arg);
|
||||
|
||||
json::value next_json = callback_json;
|
||||
next_json["tasks"] = json::array(task.exceeded_next);
|
||||
m_callback(TaskMsg::AppendMatchTask, next_json, m_callback_arg);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 前置固定延时
|
||||
sleep(task.pre_delay);
|
||||
|
||||
bool need_stop = false;
|
||||
switch (task.type) {
|
||||
case MatchTaskType::ClickRect:
|
||||
rect = task.specific_area;
|
||||
[[fallthrough]];
|
||||
case MatchTaskType::ClickSelf:
|
||||
exec_click_task(task, rect);
|
||||
break;
|
||||
case MatchTaskType::DoNothing:
|
||||
break;
|
||||
case MatchTaskType::Stop:
|
||||
m_callback(TaskMsg::TaskStop, json::value(), m_callback_arg);
|
||||
need_stop = true;
|
||||
break;
|
||||
case MatchTaskType::PrintWindow:
|
||||
{
|
||||
sleep(Configer::get_instance().m_options.print_window_delay);
|
||||
static const std::string dirname = GetCurrentDir() + "screenshot\\";
|
||||
print_window(dirname);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
++task.exec_times;
|
||||
|
||||
// 减少其他任务的执行次数
|
||||
// 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效
|
||||
// 所以要给蓝色开始行动的次数减一
|
||||
for (const std::string& reduce : task.reduce_other_times) {
|
||||
--Configer::get_instance().m_all_tasks_info[reduce].exec_times;
|
||||
}
|
||||
|
||||
if (need_stop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
callback_json["exec_times"] = task.exec_times;
|
||||
m_callback(TaskMsg::TaskCompleted, callback_json, m_callback_arg);
|
||||
|
||||
// 后置固定延时
|
||||
sleep(task.rear_delay);
|
||||
|
||||
json::value next_json = callback_json;
|
||||
next_json["tasks"] = json::array(task.next);
|
||||
m_callback(TaskMsg::AppendMatchTask, next_json, m_callback_arg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::optional<std::string> MatchTask::match_image(Rect* matched_rect)
|
||||
{
|
||||
const cv::Mat& cur_image = get_format_image();
|
||||
if (cur_image.empty() || cur_image.rows < 100) {
|
||||
return std::nullopt;
|
||||
}
|
||||
set_control_scale(cur_image.cols, cur_image.rows);
|
||||
|
||||
// 逐个匹配当前可能的图像
|
||||
for (const std::string& task_name : m_cur_tasks_name) {
|
||||
TaskInfo& task_info = Configer::get_instance().m_all_tasks_info[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);
|
||||
|
||||
json::value callback_json;
|
||||
if (algorithm == AlgorithmType::JustReturn) {
|
||||
callback_json["threshold"] = 0.0;
|
||||
callback_json["algorithm"] = "JustReturn";
|
||||
}
|
||||
else if (algorithm == AlgorithmType::MatchTemplate && value >= templ_threshold) {
|
||||
callback_json["threshold"] = templ_threshold;
|
||||
callback_json["algorithm"] = "MatchTemplate";
|
||||
}
|
||||
else if (algorithm == AlgorithmType::CompareHist && value >= hist_threshold) {
|
||||
callback_json["threshold"] = hist_threshold;
|
||||
callback_json["algorithm"] = "CompareHist";
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matched_rect != NULL) {
|
||||
callback_json["rect"] = json::array({ rect.x, rect.y, rect.width, rect.height });
|
||||
*matched_rect = std::move(rect);
|
||||
}
|
||||
callback_json["algorithm_id"] = static_cast<std::underlying_type<MatchTaskType>::type>(algorithm);
|
||||
callback_json["value"] = value;
|
||||
m_callback(TaskMsg::ImageMatched, callback_json, m_callback_arg);
|
||||
|
||||
return task_name;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void MatchTask::exec_click_task(TaskInfo& task, const Rect& matched_rect)
|
||||
{
|
||||
// 随机延时功能
|
||||
if (Configer::get_instance().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(
|
||||
Configer::get_instance().m_options.control_delay_lower,
|
||||
Configer::get_instance().m_options.control_delay_upper);
|
||||
|
||||
unsigned rand_delay = rand_uni(rand_engine);
|
||||
sleep(rand_delay);
|
||||
}
|
||||
|
||||
m_control_ptr->click(matched_rect);
|
||||
}
|
||||
|
||||
|
||||
OcrAbstractTask::OcrAbstractTask(TaskCallback callback, void* callback_arg)
|
||||
: AbstractTask(callback, callback_arg)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
std::vector<TextArea> OcrAbstractTask::ocr_detect()
|
||||
{
|
||||
const cv::Mat& image = get_format_image();
|
||||
|
||||
auto&& dst = m_identify_ptr->ocr_detect(image);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
OpenRecruitTask::OpenRecruitTask(TaskCallback callback, void* callback_arg)
|
||||
: OcrAbstractTask(callback, callback_arg)
|
||||
{
|
||||
m_task_type = TaskType::TaskTypeRecognition;
|
||||
}
|
||||
|
||||
bool OpenRecruitTask::run()
|
||||
{
|
||||
if (m_view_ptr == NULL
|
||||
|| m_identify_ptr == NULL)
|
||||
{
|
||||
m_callback(TaskMsg::PtrIsNull, json::value(), m_callback_arg);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_callback(TaskMsg::TaskStart, json::object{ { "task_type", "OpenRecruitTask" } }, m_callback_arg);
|
||||
|
||||
/* Find all text */
|
||||
std::vector<TextArea> all_text_area = ocr_detect();
|
||||
|
||||
std::vector<json::value> all_text_json_vector;
|
||||
for (const TextArea& text_area : all_text_area) {
|
||||
all_text_json_vector.emplace_back(Utf8ToGbk(text_area.text));
|
||||
}
|
||||
json::value all_text_json;
|
||||
all_text_json["text"] = json::array(all_text_json_vector);
|
||||
m_callback(TaskMsg::TextDetected, all_text_json, m_callback_arg);
|
||||
|
||||
/* Filter out all tags from all text */
|
||||
std::vector<TextArea> all_tags = text_filter(
|
||||
all_text_area, OpenRecruitConfiger::get_instance().m_all_tags, Configer::get_instance().m_ocr_replace);
|
||||
|
||||
std::unordered_set<std::string> all_tags_name;
|
||||
std::vector<json::value> all_tags_json_vector;
|
||||
for (const TextArea& text_area : all_tags) {
|
||||
all_tags_name.emplace(text_area.text);
|
||||
all_tags_json_vector.emplace_back(Utf8ToGbk(text_area.text));
|
||||
}
|
||||
json::value all_tags_json;
|
||||
all_tags_json["tags"] = json::array(all_tags_json_vector);
|
||||
m_callback(TaskMsg::RecruitTagsDetected, all_tags_json, m_callback_arg);
|
||||
|
||||
/* 过滤tags数量不足的情况(可能是识别漏了) */
|
||||
if (all_tags.size() != OpenRecruitConfiger::CorrectNumberOfTags) {
|
||||
all_tags_json["type"] = "OpenRecruit";
|
||||
m_callback(TaskMsg::OcrResultError, all_tags_json, m_callback_arg);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 设置招募时间9小时,加入任务队列*/
|
||||
if (m_set_time) {
|
||||
json::value settime_json;
|
||||
settime_json["task"] = "RecruitTime";
|
||||
m_callback(TaskMsg::AppendMatchTask, settime_json, m_callback_arg);
|
||||
}
|
||||
|
||||
/* 针对一星干员的额外回调消息 */
|
||||
static const std::string SupportMachine_GBK = "支援机械";
|
||||
static const std::string SupportMachine = GbkToUtf8(SupportMachine_GBK);
|
||||
if (std::find(all_tags_name.cbegin(), all_tags_name.cend(), SupportMachine) != all_tags_name.cend()) {
|
||||
json::value special_tag_json;
|
||||
special_tag_json["tag"] = SupportMachine_GBK;
|
||||
m_callback(TaskMsg::RecruitSpecialTag, special_tag_json, m_callback_arg);
|
||||
}
|
||||
|
||||
// 识别到的5个Tags,全组合排列
|
||||
std::vector<std::vector<std::string>> all_combs;
|
||||
int len = all_tags.size();
|
||||
int count = std::pow(2, len);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
std::vector<std::string> temp;
|
||||
for (int j = 0, mask = 1; j < len; ++j) {
|
||||
if ((i & mask) != 0) { // What the fuck???
|
||||
temp.emplace_back(all_tags.at(j).text);
|
||||
}
|
||||
mask = mask * 2;
|
||||
}
|
||||
// 游戏里最多选择3个tag
|
||||
if (!temp.empty() && temp.size() <= 3) {
|
||||
all_combs.emplace_back(std::move(temp));
|
||||
}
|
||||
}
|
||||
|
||||
// key: tags comb, value: 干员组合
|
||||
// 例如 key: { "狙击"、"群攻" },value: OperCombs.opers{ "陨星", "白雪", "空爆" }
|
||||
std::map<std::vector<std::string>, OperCombs> result_map;
|
||||
for (const std::vector<std::string>& comb : all_combs) {
|
||||
for (const OperInfo& cur_oper : OpenRecruitConfiger::get_instance().m_all_opers) {
|
||||
int matched_count = 0;
|
||||
for (const std::string& tag : comb) {
|
||||
if (cur_oper.tags.find(tag) != cur_oper.tags.cend()) {
|
||||
++matched_count;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 单个tags comb中的每一个tag,这个干员都有,才算该干员符合这个tags comb
|
||||
if (matched_count != comb.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur_oper.level == 6) {
|
||||
// 高资tag和六星强绑定,如果没有高资tag,即使其他tag匹配上了也不可能出六星
|
||||
static const std::string SeniorOper = GbkToUtf8("高级资深干员");
|
||||
if (std::find(comb.cbegin(), comb.cend(), SeniorOper) == comb.cend()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
OperCombs& oper_combs = result_map[comb];
|
||||
oper_combs.opers.emplace_back(cur_oper);
|
||||
|
||||
if (cur_oper.level == 1 || cur_oper.level == 2) {
|
||||
if (oper_combs.min_level == 0) oper_combs.min_level = cur_oper.level;
|
||||
if (oper_combs.max_level == 0) oper_combs.max_level = cur_oper.level;
|
||||
// 一星、二星干员不计入最低等级,因为拉满9小时之后不可能出1、2星
|
||||
continue;
|
||||
}
|
||||
if (oper_combs.min_level == 0 || oper_combs.min_level > cur_oper.level) {
|
||||
oper_combs.min_level = cur_oper.level;
|
||||
}
|
||||
if (oper_combs.max_level == 0 || oper_combs.max_level < cur_oper.level) {
|
||||
oper_combs.max_level = cur_oper.level;
|
||||
}
|
||||
oper_combs.avg_level += cur_oper.level;
|
||||
}
|
||||
if (result_map.find(comb) != result_map.cend()) {
|
||||
OperCombs& oper_combs = result_map[comb];
|
||||
oper_combs.avg_level /= oper_combs.opers.size();
|
||||
}
|
||||
}
|
||||
|
||||
// map没法按值排序,转个vector再排序
|
||||
std::vector<std::pair<std::vector<std::string>, OperCombs>> result_vector;
|
||||
for (auto&& pair : result_map) {
|
||||
result_vector.emplace_back(std::move(pair));
|
||||
}
|
||||
std::sort(result_vector.begin(), result_vector.end(), [](const auto& lhs, const auto& rhs)
|
||||
->bool {
|
||||
// 最小等级大的,排前面
|
||||
if (lhs.second.min_level != rhs.second.min_level) {
|
||||
return lhs.second.min_level > rhs.second.min_level;
|
||||
}
|
||||
// 最大等级大的,排前面
|
||||
else if (lhs.second.max_level != rhs.second.max_level) {
|
||||
return lhs.second.max_level > rhs.second.max_level;
|
||||
}
|
||||
// 平均等级高的,排前面
|
||||
else if (std::fabs(lhs.second.avg_level - rhs.second.avg_level) < DoubleDiff) {
|
||||
return lhs.second.avg_level > rhs.second.avg_level;
|
||||
}
|
||||
// Tag数量少的,排前面
|
||||
else {
|
||||
return lhs.first.size() < rhs.first.size();
|
||||
}
|
||||
});
|
||||
|
||||
/* 整理识别结果 */
|
||||
std::vector<json::value> result_json_vector;
|
||||
for (const auto& [tags_comb, oper_comb] : result_vector) {
|
||||
json::value comb_json;
|
||||
|
||||
std::vector<json::value> tags_json_vector;
|
||||
for (const std::string& tag : tags_comb) {
|
||||
tags_json_vector.emplace_back(Utf8ToGbk(tag));
|
||||
}
|
||||
comb_json["tags"] = json::array(std::move(tags_json_vector));
|
||||
|
||||
std::vector<json::value> opers_json_vector;
|
||||
for (const OperInfo& oper_info : oper_comb.opers) {
|
||||
json::value oper_json;
|
||||
oper_json["name"] = Utf8ToGbk(oper_info.name);
|
||||
oper_json["level"] = oper_info.level;
|
||||
opers_json_vector.emplace_back(std::move(oper_json));
|
||||
}
|
||||
comb_json["opers"] = json::array(std::move(opers_json_vector));
|
||||
comb_json["tag_level"] = oper_comb.min_level;
|
||||
result_json_vector.emplace_back(std::move(comb_json));
|
||||
}
|
||||
json::value results_json;
|
||||
results_json["result"] = json::array(std::move(result_json_vector));
|
||||
m_callback(TaskMsg::RecruitResult, results_json, m_callback_arg);
|
||||
|
||||
/* 点击最优解的tags(添加点击任务) */
|
||||
if (!m_required_level.empty() && !result_vector.empty()) {
|
||||
if (std::find(m_required_level.cbegin(), m_required_level.cend(), result_vector[0].second.min_level)
|
||||
== m_required_level.cend()) {
|
||||
return true;
|
||||
}
|
||||
const std::vector<std::string>& final_tags_name = result_vector[0].first;
|
||||
|
||||
json::value task_json;
|
||||
task_json["type"] = "ClickTask";
|
||||
for (const TextArea& text_area : all_tags) {
|
||||
if (std::find(final_tags_name.cbegin(), final_tags_name.cend(), text_area.text) != final_tags_name.cend()) {
|
||||
task_json["rect"] = json::array({ text_area.rect.x, text_area.rect.y, text_area.rect.width, text_area.rect.height });
|
||||
m_callback(TaskMsg::AppendTask, task_json, m_callback_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenRecruitTask::set_param(std::vector<int> required_level, bool set_time)
|
||||
{
|
||||
m_required_level = std::move(required_level);
|
||||
m_set_time = set_time;
|
||||
}
|
||||
|
||||
bool ClickTask::run()
|
||||
{
|
||||
if (m_control_ptr == NULL)
|
||||
{
|
||||
m_callback(TaskMsg::PtrIsNull, json::value(), m_callback_arg);
|
||||
return false;
|
||||
}
|
||||
m_callback(TaskMsg::TaskStart, json::object{ { "task_type", "ClickTask" } }, m_callback_arg);
|
||||
|
||||
m_control_ptr->click(m_rect);
|
||||
return true;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <Windows.h>
|
||||
#include <WinInet.h>
|
||||
|
||||
#include "json.h"
|
||||
#include <json.h>
|
||||
#include "AsstDef.h"
|
||||
#include "Logger.hpp"
|
||||
#include "Version.h"
|
||||
|
||||
@@ -214,7 +214,8 @@ bool WinMacro::showWindow()
|
||||
return false;
|
||||
}
|
||||
|
||||
return ::ShowWindow(m_handle, SW_RESTORE);
|
||||
bool ret = ::ShowWindow(m_handle, SW_RESTORE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WinMacro::hideWindow()
|
||||
@@ -232,7 +233,7 @@ double WinMacro::getScreenScale()
|
||||
if (scale == 0) {
|
||||
// 获取窗口当前显示的监视器
|
||||
// 使用桌面的句柄.
|
||||
HWND hWnd = GetDesktopWindow();
|
||||
HWND hWnd = ::GetDesktopWindow();
|
||||
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
// 获取监视器逻辑宽度与高度
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
xmlns:local="clr-namespace:MeoAsstGui"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
<!--<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml"/>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml"/>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml"/>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Indigo.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>-->
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -14,6 +14,9 @@ using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Threading;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace MeoAsstGui
|
||||
{
|
||||
@@ -23,55 +26,139 @@ namespace MeoAsstGui
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
|
||||
[DllImport("MeoAssistance.dll")] static private extern IntPtr CreateAsst();
|
||||
[DllImport("MeoAssistance.dll")] static private extern void DestoryAsst(IntPtr ptr);
|
||||
[DllImport("MeoAssistance.dll")] static private extern IntPtr AsstCreate();
|
||||
[DllImport("MeoAssistance.dll")] static private extern IntPtr AsstCreateEx(CallbackDelegate callback, IntPtr custom_arg);
|
||||
[DllImport("MeoAssistance.dll")] static private extern void AsstDestory(IntPtr ptr);
|
||||
[DllImport("MeoAssistance.dll")] static private extern bool AsstCatchEmulator(IntPtr ptr);
|
||||
[DllImport("MeoAssistance.dll")] static private extern void AsstStart(IntPtr ptr, string task);
|
||||
[DllImport("MeoAssistance.dll")] static private extern void AsstStop(IntPtr ptr);
|
||||
[DllImport("MeoAssistance.dll")] static private extern bool AsstSetParam(IntPtr p_asst, string type, string param, string value);
|
||||
[DllImport("MeoAssistance.dll")]
|
||||
static private extern bool AsstGetParam(
|
||||
IntPtr p_asst, string type, string param,
|
||||
[In, Out] StringBuilder lp_string, int buffer_size);
|
||||
|
||||
private delegate void CallbackDelegate(int msg, IntPtr json_buffer, IntPtr custom_arg);
|
||||
private delegate void ProcCallbckMsg(TaskMsg msg, JObject detail);
|
||||
|
||||
private static CallbackDelegate callback;
|
||||
|
||||
private IntPtr p_asst;
|
||||
private UpdateDialog updateDialog;
|
||||
private RecruitWindow recuitWindow;
|
||||
private DispatcherTimer update_times = new DispatcherTimer();
|
||||
private RecruitWindow recruitWindow;
|
||||
|
||||
public enum TaskMsg
|
||||
{
|
||||
/* Error Msg */
|
||||
PtrIsNull,
|
||||
ImageIsEmpty,
|
||||
WindowMinimized,
|
||||
/* Info Msg */
|
||||
TaskStart,
|
||||
ImageMatched,
|
||||
TaskMatched,
|
||||
ReachedLimit,
|
||||
ReadyToSleep,
|
||||
EndOfSleep,
|
||||
AppendMatchTask,
|
||||
AppendTask,
|
||||
TaskCompleted,
|
||||
PrintWindow,
|
||||
TaskStop,
|
||||
/* Open Recruit Msg */
|
||||
TextDetected,
|
||||
RecruitTagsDetected,
|
||||
OcrResultError,
|
||||
RecruitSpecialTag,
|
||||
RecruitResult
|
||||
};
|
||||
|
||||
private void CallbackFunction(int msg, IntPtr json_buffer, IntPtr custom_arg)
|
||||
{
|
||||
string json_str = Marshal.PtrToStringAnsi(json_buffer);
|
||||
//Console.WriteLine(json_str);
|
||||
JObject json = (JObject)JsonConvert.DeserializeObject(json_str);
|
||||
ProcCallbckMsg dlg = new ProcCallbckMsg(proc_msg);
|
||||
this.Dispatcher.Invoke(dlg, msg, json);
|
||||
}
|
||||
private void proc_msg(TaskMsg msg, JObject detail)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case TaskMsg.TaskCompleted:
|
||||
string task_name = detail["name"].ToString();
|
||||
if (task_name == "StartButton2")
|
||||
{
|
||||
exec_times.Content = "已开始行动 " + (int)detail["exec_times"] + " 次";
|
||||
}
|
||||
else if (task_name == "StoneConfirm")
|
||||
{
|
||||
stone_times.Content = "已碎石 " + (int)detail["exec_times"] + " 个";
|
||||
}
|
||||
break;
|
||||
case TaskMsg.TaskStart:
|
||||
string task_type = detail["task_type"].ToString();
|
||||
if (task_type == "MatchTask")
|
||||
{
|
||||
label_status.Content = "正在运行中……";
|
||||
}
|
||||
break;
|
||||
case TaskMsg.TaskStop:
|
||||
label_status.Content = "已刷完,自动停止";
|
||||
if (checkBox_shutdown.IsChecked == true)
|
||||
{
|
||||
System.Diagnostics.Process.Start("shutdown.exe", "-s -t 60");
|
||||
|
||||
MessageBoxResult result = MessageBox.Show("已刷完,即将关机,是否取消?", "提示", MessageBoxButton.OK);
|
||||
if (result == MessageBoxResult.OK)
|
||||
{
|
||||
System.Diagnostics.Process.Start("shutdown.exe", "-a");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TaskMsg.TextDetected:
|
||||
case TaskMsg.RecruitTagsDetected:
|
||||
case TaskMsg.OcrResultError:
|
||||
case TaskMsg.RecruitSpecialTag:
|
||||
case TaskMsg.RecruitResult:
|
||||
recruitWindow.proc_msg(msg, detail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
~MainWindow()
|
||||
{
|
||||
DestoryAsst(p_asst);
|
||||
AsstDestory(p_asst);
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
p_asst = CreateAsst();
|
||||
update_times.Tick += new EventHandler(updateExecTimes);
|
||||
update_times.Interval = TimeSpan.FromSeconds(1);
|
||||
callback = CallbackFunction;
|
||||
p_asst = AsstCreateEx(callback, IntPtr.Zero);
|
||||
//update_times.Tick += new EventHandler(updateExecTimes);
|
||||
//update_times.Interval = TimeSpan.FromSeconds(1);
|
||||
|
||||
updateDialog = new UpdateDialog();
|
||||
updateDialog.CheckUpdateAndShowDialog();
|
||||
updateDialog.Close();
|
||||
Dispatcher.BeginInvoke(new Action(delegate
|
||||
{
|
||||
UpdateDialog updateDialog = new UpdateDialog();
|
||||
updateDialog.CheckUpdateAndShowDialog();
|
||||
updateDialog.Close();
|
||||
}));
|
||||
}
|
||||
|
||||
private void button_Click_startSanity(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool catched = AsstCatchEmulator(p_asst);
|
||||
catch_status.Content = "捕获模拟器窗口:" + catched;
|
||||
AsstStart(p_asst, "SanityBegin");
|
||||
update_times.Start();
|
||||
if (checkBox_useStone.IsChecked == true)
|
||||
{
|
||||
stone_times.Content = "已碎石 0 个";
|
||||
}
|
||||
}
|
||||
|
||||
private void button_Click_stop(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AsstStop(p_asst);
|
||||
catch_status.Content = "";
|
||||
update_times.Stop();
|
||||
exec_times.Content = "";
|
||||
stone_times.Content = "";
|
||||
label_status.Content = "";
|
||||
@@ -120,43 +207,6 @@ namespace MeoAsstGui
|
||||
AsstStart(p_asst, "VisitBegin");
|
||||
}
|
||||
|
||||
private void updateExecTimes(object sender, EventArgs e)
|
||||
{
|
||||
StringBuilder buff_start = new StringBuilder(16);
|
||||
AsstGetParam(p_asst, "task.execTimes", "StartButton2", buff_start, 16);
|
||||
exec_times.Content = "已开始行动 " + buff_start + " 次";
|
||||
|
||||
if (checkBox_useStone.IsChecked == true)
|
||||
{
|
||||
StringBuilder buff_stone = new StringBuilder(16);
|
||||
AsstGetParam(p_asst, "task.execTimes", "StoneConfirm", buff_stone, 16);
|
||||
stone_times.Content = "已碎石 " + buff_stone + " 个";
|
||||
}
|
||||
|
||||
|
||||
StringBuilder buff_running = new StringBuilder(4);
|
||||
AsstGetParam(p_asst, "status", "running", buff_running, 4);
|
||||
if (int.Parse(buff_running.ToString()) == 0)
|
||||
{
|
||||
update_times.Stop();
|
||||
label_status.Content = "已刷完,自动停止";
|
||||
if (checkBox_shutdown.IsChecked == true)
|
||||
{
|
||||
System.Diagnostics.Process.Start("shutdown.exe", "-s -t 60");
|
||||
|
||||
MessageBoxResult result = MessageBox.Show("已刷完,即将关机,是否取消?", "提示", MessageBoxButton.OK);
|
||||
if (result == MessageBoxResult.OK)
|
||||
{
|
||||
System.Diagnostics.Process.Start("shutdown.exe", "-a");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
label_status.Content = "正在运行中……";
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBox_maxTimes_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (checkBox_maxTimes.IsChecked == true)
|
||||
@@ -181,9 +231,8 @@ namespace MeoAsstGui
|
||||
|
||||
private void button_recruit_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
recuitWindow = new RecruitWindow(p_asst);
|
||||
recuitWindow.ShowDialog();
|
||||
recuitWindow.Close();
|
||||
recruitWindow = new RecruitWindow(p_asst);
|
||||
recruitWindow.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
<StartupObject>MeoAsstGui.App</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
@@ -124,6 +127,7 @@
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
|
||||
@@ -53,3 +53,4 @@ using System.Windows;
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: NeutralResourcesLanguage("zh")]
|
||||
|
||||
6
MeoAsstGui/README.md
Normal file
6
MeoAsstGui/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# MeoAsstGui
|
||||
|
||||
使用C# WPF编写的界面模块
|
||||
|
||||
第一次用C#写界面,语法都没全搞懂,现学现卖,写的很烂_(:з」∠)_
|
||||
寻一热心大佬帮忙重写GUI,任意语言均可,我给你封动态库,球球了QAQ
|
||||
@@ -1,4 +1,4 @@
|
||||
<Window x:Class="MeoAsstGui.RecruitWindow"
|
||||
<Window x:Class="MeoAsstGui.RecruitWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -7,7 +7,7 @@
|
||||
mc:Ignorable="d"
|
||||
Title="全自动公开招募计算器" Height="450" Width="800">
|
||||
<Grid>
|
||||
<Label x:Name="info" Content="计算结果" HorizontalAlignment="Left" Margin="30,10,-467.667,0" VerticalAlignment="Top" Height="270" Width="1231"/>
|
||||
<Label x:Name="result" Content="" HorizontalAlignment="Left" Margin="30,40,-467.667,0" VerticalAlignment="Top" Height="240" Width="1231"/>
|
||||
<Button x:Name="button_start" Content="开始计算" HorizontalAlignment="Left" Margin="600,0,0,30" VerticalAlignment="Bottom" Width="150" Height="60" Click="button_start_Click"/>
|
||||
<CheckBox x:Name="checkBox_time" Content="自动设置时间" HorizontalAlignment="Left" Margin="30,0,0,80" VerticalAlignment="Bottom" IsChecked="True"/>
|
||||
<CheckBox x:Name="checkBox_level_3" Content="自动选择3星Tags" HorizontalAlignment="Left" Margin="150,0,0,120" VerticalAlignment="Bottom"/>
|
||||
@@ -15,5 +15,6 @@
|
||||
<CheckBox x:Name="checkBox_level_5" Content="自动选择5星Tags" HorizontalAlignment="Left" Margin="150,0,0,60" VerticalAlignment="Bottom"/>
|
||||
<CheckBox x:Name="checkBox_level_6" Content="自动选择6星Tags" HorizontalAlignment="Left" Margin="150,0,0,30" VerticalAlignment="Bottom"/>
|
||||
<Label x:Name="prompt" Content="提示:本辅助仅会帮你选择最优Tags,
但是不会帮你点击确定按钮!!!
请自行检查辅助选择的是否正确,
若辅助出现识别错误,遗漏了高星干员,
作者概不负责哦__(:з」∠)_" HorizontalAlignment="Left" Margin="300,0,0,10" VerticalAlignment="Bottom" Height="121" Width="292" FontSize="16"/>
|
||||
<Label x:Name="info" Content="识别结果" HorizontalAlignment="Left" Margin="30,10,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.025,0.211" Height="30" Width="500"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -12,16 +12,19 @@ using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace MeoAsstGui
|
||||
{
|
||||
using TaskMsg = MainWindow.TaskMsg;
|
||||
/// <summary>
|
||||
/// RecruitWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class RecruitWindow : Window
|
||||
{
|
||||
[DllImport("MeoAssistance.dll")] static private extern bool AsstCatchEmulator(IntPtr ptr);
|
||||
[DllImport("MeoAssistance.dll")] static private extern bool AsstRunOpenRecruit(IntPtr ptr, int[] required_level, bool set_time, [In, Out] StringBuilder result, int buffer_size, ref int maybe_level);
|
||||
[DllImport("MeoAssistance.dll")] static private extern bool AsstRunOpenRecruit(IntPtr ptr, int[] required_level, bool set_time);
|
||||
|
||||
private IntPtr p_asst;
|
||||
public RecruitWindow(IntPtr ptr)
|
||||
@@ -29,10 +32,53 @@ namespace MeoAsstGui
|
||||
InitializeComponent();
|
||||
p_asst = ptr;
|
||||
}
|
||||
public void proc_msg(TaskMsg msg, JObject detail)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case TaskMsg.TextDetected:
|
||||
break;
|
||||
case TaskMsg.RecruitTagsDetected:
|
||||
JArray tags = (JArray)detail["tags"];
|
||||
string info_content = "识别结果: ";
|
||||
foreach (var tag_name in tags)
|
||||
{
|
||||
info_content += tag_name.ToString() + " ";
|
||||
}
|
||||
info.Content = info_content;
|
||||
break;
|
||||
case TaskMsg.OcrResultError:
|
||||
info.Content = "识别错误!";
|
||||
break;
|
||||
case TaskMsg.RecruitSpecialTag:
|
||||
MessageBox.Show("检测到特殊Tag:" + detail["tag"].ToString(), "提示");
|
||||
break;
|
||||
case TaskMsg.RecruitResult:
|
||||
string result_content = "";
|
||||
JArray result_array = (JArray)detail["result"];
|
||||
foreach (var combs in result_array)
|
||||
{
|
||||
result_content += (int)combs["tag_level"] + "星Tags: ";
|
||||
foreach (var tag in (JArray)combs["tags"])
|
||||
{
|
||||
result_content += tag.ToString() + " ";
|
||||
}
|
||||
result_content += "\n\t";
|
||||
foreach (var oper in (JArray)combs["opers"])
|
||||
{
|
||||
result_content += oper["level"].ToString() + " - " + oper["name"].ToString() + " ";
|
||||
}
|
||||
result_content += "\n\n";
|
||||
}
|
||||
result.Content = result_content;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void button_start_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
info.Content = "正在计算……";
|
||||
info.Content = "正在识别……";
|
||||
result.Content = "";
|
||||
|
||||
if (!AsstCatchEmulator(p_asst))
|
||||
{
|
||||
@@ -56,22 +102,9 @@ namespace MeoAsstGui
|
||||
{
|
||||
level_list.Add(6);
|
||||
}
|
||||
StringBuilder result_buff = new StringBuilder(16384);
|
||||
int maybe_level = 0;
|
||||
bool set_time = checkBox_time.IsChecked == true ? true : false;
|
||||
if (AsstRunOpenRecruit(p_asst, level_list.ToArray(), set_time, result_buff, 16384, ref maybe_level))
|
||||
{
|
||||
info.Content = result_buff;
|
||||
if (maybe_level > 4 || maybe_level == 1)
|
||||
{
|
||||
MessageBox.Show("出 " + maybe_level + " 星了哦!", "公招提示");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info.Content = "识别错误!";
|
||||
MessageBox.Show("识别错误!", "公招提示");
|
||||
}
|
||||
|
||||
AsstRunOpenRecruit(p_asst, level_list.ToArray(), set_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
MeoAsstGui/packages.config
Normal file
6
MeoAsstGui/packages.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MaterialDesignColors" version="2.0.1" targetFramework="net472" />
|
||||
<package id="MaterialDesignThemes" version="4.1.0" targetFramework="net472" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
|
||||
</packages>
|
||||
@@ -1,37 +1,21 @@
|
||||
#include "Assistance.h"
|
||||
#include "Updater.h"
|
||||
#include "Logger.hpp"
|
||||
#include "AsstCaller.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace asst;
|
||||
asst::Assistance * ptr = AsstCreate();
|
||||
|
||||
bool up = Updater::get_instance().has_new_version();
|
||||
if (up) {
|
||||
auto && info = Updater::get_instance().get_version_info();
|
||||
std::cout << "ÓÐа汾£º" << info.tag_name << std::endl;
|
||||
std::cout << "µØÖ·£º" << info.html_url << std::endl;
|
||||
}
|
||||
|
||||
Assistance asst;
|
||||
|
||||
auto ret = asst.catch_emulator();
|
||||
auto ret = AsstCatchEmulator(ptr);
|
||||
if (!ret) {
|
||||
DebugTraceError("Can't Find Emulator or Permission denied.");
|
||||
getchar();
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
DebugTraceInfo("Find Emulator:", ret.value());
|
||||
}
|
||||
|
||||
DebugTraceInfo("Start");
|
||||
asst.start("SanityBegin");
|
||||
AsstStart(ptr, "SanityBegin");
|
||||
|
||||
getchar();
|
||||
|
||||
DebugTraceInfo("Stop");
|
||||
asst.stop();
|
||||
AsstStop(ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -37,13 +37,7 @@ void asst_ocr_test()
|
||||
}
|
||||
char c = 0;
|
||||
while (c != 'q') {
|
||||
|
||||
std::ifstream ifs(GetCurrentDir() + "name.txt");
|
||||
std::string person_name;
|
||||
ifs >> person_name;
|
||||
|
||||
asst.open_recruit({ 4, 5 });
|
||||
|
||||
asst.start_open_recruit({ 3, 4, 5 });
|
||||
c = getchar();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
"options": {
|
||||
"identifyCache": true,
|
||||
"identifyCache_Doc": "图像识别缓存功能:开启后可以大幅降低CPU消耗,但需要保证要识别的按钮每次的位置不会改变。true-开启,false-关闭,默认true",
|
||||
"identifyDelay": 1000,
|
||||
"identifyDelay_Doc": "图像识别延时:越快操作越快,但会增加CPU消耗。单位毫秒,默认1000",
|
||||
"taskIdentifyDelay": 1000,
|
||||
"taskIdentifyDelay_Doc": "识别任务间的延迟:越快识别频率越快,但会增加CPU消耗。单位毫秒,默认1000",
|
||||
"taskControlDelay": 300,
|
||||
"taskControlDelay_Doc": "点击任务间延迟:越快点的越快,但是太快了句柄可能不响应。单位毫秒,默认300",
|
||||
"printWindow": true,
|
||||
"printWindow_Doc": "截图功能:开启后每次结算界面会截图到screenshot目录下。true-开启,false-关闭,默认true",
|
||||
"printWindowDelay": 3000,
|
||||
@@ -229,7 +231,7 @@
|
||||
"tasks": {
|
||||
"SanityBegin": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "doNothing",
|
||||
"next": [
|
||||
"UsePrts",
|
||||
@@ -237,14 +239,12 @@
|
||||
"StartButton2",
|
||||
"PRTS",
|
||||
"UseMedicine",
|
||||
"UseStone",
|
||||
"PrtsErrorConfirm",
|
||||
"Random"
|
||||
"UseStone"
|
||||
]
|
||||
},
|
||||
"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,
|
||||
@@ -370,7 +370,7 @@
|
||||
},
|
||||
"VisitBegin": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "doNothing",
|
||||
"next": [
|
||||
"Friends",
|
||||
@@ -383,8 +383,8 @@
|
||||
},
|
||||
"ReturnToFriends": {
|
||||
"template": "Return.png",
|
||||
"threshold": 0.85,
|
||||
"cacheThreshold": 0.85,
|
||||
"templThreshold": 0.85,
|
||||
"histThreshold": 0.85,
|
||||
"type": "clickSelf",
|
||||
"next": [
|
||||
"Friends",
|
||||
@@ -450,7 +450,7 @@
|
||||
},
|
||||
"CreditStore": {
|
||||
"template": "CreditStore.png",
|
||||
"threshold": 0.85,
|
||||
"templThreshold": 0.85,
|
||||
"rearDelay": 1000,
|
||||
"type": "clickSelf",
|
||||
"next": [
|
||||
@@ -467,7 +467,7 @@
|
||||
},
|
||||
"VisitLimited": {
|
||||
"template": "VisitLimited.png",
|
||||
"threshold": 1,
|
||||
"templThreshold": 1,
|
||||
"type": "doNothing",
|
||||
"next": [
|
||||
"ReturnToMall"
|
||||
@@ -482,7 +482,7 @@
|
||||
},
|
||||
"Stop": {
|
||||
"template": "",
|
||||
"threshold": 0,
|
||||
"templThreshold": 0,
|
||||
"type": "stop",
|
||||
"next": []
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user