Compare commits

...

38 Commits

Author SHA1 Message Date
MistEO
e05430ec0e update version id 2021-07-22 23:48:32 +08:00
MistEO
0a081d4fa2 update readme 2021-07-22 23:45:20 +08:00
MistEO
a6488f0ea6 rename config json key 2021-07-22 22:56:06 +08:00
MistEO
8844d35c2e support control random delay 2021-07-22 22:54:58 +08:00
MistEO
0534e35871 update threshold 2021-07-22 21:47:20 +08:00
MistEO
107d3af5c5 header file unlooping 2021-07-22 21:46:32 +08:00
MistEO
1e170e6c1a fixed some warning, update logger 2021-07-22 19:40:25 +08:00
MistEO
f3773d22c6 update log and config 2021-07-22 10:07:40 +08:00
MistEO
7928a21051 update updater gui 2021-07-22 02:16:53 +08:00
MistEO
2a89b01c1b fixed gui 2021-07-22 02:00:51 +08:00
MistEO
d735404766 add log 2021-07-22 01:07:12 +08:00
MistEO
a8173eb82c fixed count error when use stone or medicine 2021-07-22 00:39:13 +08:00
MistEO
67f52b321c update log 2021-07-21 23:43:47 +08:00
MistEO
6c3540b692 move readme.png to resource 2021-07-21 20:40:54 +08:00
MistEO
dd556817ea update version id 2021-07-21 20:36:29 +08:00
MistEO
c393da2046 Merge pull request #3 from MistEO/feature/new_algorithm
Feature/new algorithm
2021-07-21 20:17:11 +08:00
MistEO
5657af6d94 update config and something 2021-07-21 20:16:33 +08:00
MistEO
f35ef18bd2 add log 2021-07-21 20:16:02 +08:00
MistEO
0fef8e2174 merge "Updater interface modified" 2021-07-21 02:05:23 +08:00
MistEO
ee3afb440e modified the interface of Updater, because VS2019 's O2 optimzation is not friendly to std::optional 2021-07-21 01:43:58 +08:00
MistEO
952861c1e5 update icon and window title 2021-07-20 23:51:47 +08:00
MistEO
dab705a3de fixed bug: process cannot exit 2021-07-20 23:37:27 +08:00
MistEO
17f8353a97 fixed bug: process cannot exit 2021-07-20 23:36:57 +08:00
MistEO
53a1297842 update task sleep logic 2021-07-20 23:29:01 +08:00
MistEO
e589a7498c update identify algorithm 2021-07-20 23:17:10 +08:00
MistEO
ca412965ca add icon for GUI 2021-07-20 21:50:26 +08:00
MistEO
adfed6d276 update release body 2021-07-20 00:50:34 +08:00
MistEO
6fddf69e67 add function of check version update 2021-07-20 00:12:00 +08:00
MistEO
fb3cbb0b7a add set max times function 2021-07-19 21:22:35 +08:00
MistEO
a348139df5 update configer 2021-07-18 23:22:47 +08:00
MistEO
08db7f008d Merge branch 'master' of https://github.com/MistEO/MeoAssistance 2021-07-18 17:43:25 +08:00
MistEO
d66f178aec update gui, add use stone label 2021-07-18 17:43:16 +08:00
MistEO
d51882fd1c Update README.md 2021-07-18 17:29:09 +08:00
MistEO
c7b80b4603 support to MuMu (not MuMuAsst) 2021-07-18 15:19:15 +08:00
MistEO
1a0add784d try to supoort MuMu 2021-07-18 02:40:57 +08:00
MistEO
70544e7809 update delayTime 2021-07-17 20:12:55 +08:00
MistEO
3430be908b fixed bugs 2021-07-17 20:07:25 +08:00
MistEO
a617f254ff update counting bug 2021-07-17 11:25:47 +08:00
39 changed files with 1045 additions and 238 deletions

View File

@@ -20,10 +20,13 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\Assistance.h" />
<ClInclude Include="include\AsstAux.h" />
<ClInclude Include="include\AsstCaller.h" />
<ClInclude Include="include\AsstDef.h" />
<ClInclude Include="include\Configer.h" />
<ClInclude Include="include\Identify.h" />
<ClInclude Include="include\Logger.hpp" />
<ClInclude Include="include\Updater.h" />
<ClInclude Include="include\WinMacro.h" />
</ItemGroup>
<ItemGroup>
@@ -31,6 +34,7 @@
<ClCompile Include="src\AsstCaller.cpp" />
<ClCompile Include="src\Configer.cpp" />
<ClCompile Include="src\Identify.cpp" />
<ClCompile Include="src\Updater.cpp" />
<ClCompile Include="src\WinMacro.cpp" />
</ItemGroup>
<ItemGroup>
@@ -145,7 +149,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>opencv_world452d.lib;libmeojson.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>opencv_world452d.lib;libmeojson.lib;WinInet.Lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<AdditionalLibraryDirectories>$(TargetDir)</AdditionalLibraryDirectories>
</Link>
@@ -177,7 +181,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>opencv_core452.lib;opencv_imgcodecs452.lib;opencv_imgproc452.lib;libmeojson.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>opencv_core452.lib;opencv_imgcodecs452.lib;opencv_imgproc452.lib;WinInet.Lib;libmeojson.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<AdditionalLibraryDirectories>$(TargetDir)</AdditionalLibraryDirectories>
</Link>
@@ -197,7 +201,7 @@
</ImportGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties _1_1_4resource_4config_1json__JsonSchema="https://beaujs.com/schema.json" />
<UserProperties _1_1_4resource_4config_1json__JsonSchema="" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@@ -33,6 +33,15 @@
<ClInclude Include="include\AsstCaller.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Updater.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Logger.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\AsstAux.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Configer.cpp">
@@ -50,6 +59,9 @@
<ClCompile Include="src\AsstCaller.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\Updater.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\resource\config.json">

View File

@@ -20,12 +20,12 @@ namespace asst {
~Assistance();
std::optional<std::string> setSimulator(const std::string & simulator_name = std::string());
void start(const std::string & task);
void stop();
void stop(bool block = true);
bool setParam(const std::string& type, const std::string& param, const std::string& value);
std::optional<std::string> getParam(const std::string& type, const std::string& param);
private:
static void workingProc(Assistance* pThis);

View File

@@ -0,0 +1,37 @@
#pragma once
#include <string>
#include <Windows.h>
namespace asst {
static std::string GetCurrentDir()
{
static std::string cur_dir;
if (cur_dir.empty()) {
char exepath_buff[_MAX_PATH] = { 0 };
::GetModuleFileNameA(NULL, exepath_buff, _MAX_PATH);
std::string exepath(exepath_buff);
cur_dir = exepath.substr(0, exepath.find_last_of('\\') + 1);
}
return cur_dir;
}
static std::string GetResourceDir()
{
static std::string res_dir = GetCurrentDir() + "resource\\";
return res_dir;
}
static std::string StringReplaceAll(const std::string& src, const std::string& old_value, const std::string& new_value)
{
std::string str = src;
for (std::string::size_type pos(0); pos != std::string::npos; pos += new_value.length()) {
if ((pos = str.find(old_value, pos)) != std::string::npos)
str.replace(pos, old_value.length(), new_value);
else
break;
}
return str;
}
}

View File

@@ -12,6 +12,8 @@ extern __declspec(dllexport) bool AsstCatchSimulator(asst::Assistance* p_asst);
extern __declspec(dllexport) void AsstStart(asst::Assistance* p_asst, const char* task);
extern __declspec(dllexport) void AsstStop(asst::Assistance* p_asst);
extern __declspec(dllexport) bool AsstSetParam(asst::Assistance* p_asst, const char* type, const char* param, const char* value);
extern __declspec(dllexport) bool AsstGetParam(asst::Assistance* p_asst, const char* type, const char* param, char * buffer, int buffer_size);
extern __declspec(dllexport) bool CheckVersionUpdate(char * tag_buffer, int tag_bufsize, char * html_url_buffer, int html_bufsize, char * body_buffer, int body_bufsize);
#ifdef __cplusplus
}

View File

@@ -1,13 +1,14 @@
#pragma once
#include "json_value.h"
#include "json_array.h"
#include <mutex>
#include <process.h>
#include <Windows.h>
#include <string>
#include <unordered_map>
#include <vector>
#include <ostream>
namespace asst {
const static std::string Version = "release.beta.02";
enum class HandleType
{
Window = 1,
@@ -15,6 +16,43 @@ namespace asst {
Control = 4
};
static std::ostream& operator<<(std::ostream& os, const HandleType& type)
{
static std::unordered_map<HandleType, std::string> _type_name = {
{HandleType::Window, "Window"},
{HandleType::View, "View"},
{HandleType::Control, "Control"}
};
return os << _type_name.at(type);
}
enum class TaskType {
Invalid = 0,
BasicClick = 1,
DoNothing = 2,
Stop = 4,
ClickSelf = 8 | BasicClick,
ClickRect = 16 | BasicClick,
ClickRand = 32 | BasicClick
};
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"}
};
return os << _type_name.at(task);
}
struct Point
{
Point() = default;
@@ -32,10 +70,10 @@ namespace asst {
{
return { x, y, static_cast<int>(width * rhs), static_cast<int>(height * rhs) };
}
Rect center_zoom(double scale)
Rect center_zoom(double scale) const
{
int half_width_scale = static_cast<int>(width * (1- scale) / 2) ;
int half_hight_scale = static_cast<int>(height * (1 - scale) / 2) ;
int half_width_scale = static_cast<int>(width * (1 - scale) / 2);
int half_hight_scale = static_cast<int>(height * (1 - scale) / 2);
return { x + half_width_scale, y + half_hight_scale, width - half_width_scale, height - half_hight_scale };
}
int x = 0;
@@ -44,45 +82,48 @@ namespace asst {
int height = 0;
};
static Rect jsonToRect(const json::array& arr)
{
if (arr.size() != 4) {
return { 0, 0, 0, 0 };
}
return Rect(arr[0].as_integer(), arr[1].as_integer(), arr[2].as_integer(), arr[3].as_integer());
}
struct HandleInfo {
std::string className;
std::string windowName;
};
template <typename... Args>
void DebugPrint(const std::string& level, Args &&... args)
{
static std::mutex trace_mutex;
std::unique_lock<std::mutex> trace_lock(trace_mutex);
struct AdbCmd {
std::string path;
std::string connect;
std::string click;
};
SYSTEMTIME curtime;
GetLocalTime(&curtime);
printf("[%04d-%02d-%02d %02d:%02d:%02d.%03d][%s][Px%x][Tx%x] ",
curtime.wYear, curtime.wMonth, curtime.wDay,
curtime.wHour, curtime.wMinute, curtime.wSecond, curtime.wMilliseconds,
level.c_str(), _getpid(), GetCurrentThreadId());
printf(std::forward<Args>(args)...);
printf("\n");
}
struct SimulatorInfo {
std::vector<HandleInfo> window;
std::vector<HandleInfo> view;
std::vector<HandleInfo> control;
bool is_adb = false;
AdbCmd adb;
int width = 0;
int height = 0;
int x_offset = 0;
int y_offset = 0;
};
template <typename... Args>
inline void DebugTrace(Args &&... args)
{
//#ifdef _DEBUG
DebugPrint("TRC", std::forward<Args>(args)...);
//#endif
}
template <typename... Args>
inline void DebugTraceInfo(Args &&... args)
{
DebugPrint("INF", std::forward<Args>(args)...);
}
template <typename... Args>
inline void DebugTraceError(Args &&... args)
{
DebugPrint("ERR", std::forward<Args>(args)...);
}
struct TaskInfo {
std::string filename;
double threshold = 0;
double cache_threshold = 0;
TaskType type = TaskType::Invalid;
std::vector<std::string> next;
int exec_times = 0;
int max_times = INT_MAX;
std::vector<std::string> exceeded_next;
std::vector<std::string> reduce_other_times;
asst::Rect specific_area;
int pre_delay = 0;
int rear_delay = 0;
};
struct Options {
int identify_delay = 0;
bool identify_cache = false;
int control_delay_lower = 0;
int control_delay_upper = 0;
};
}

View File

@@ -3,42 +3,11 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <optional>
#include "AsstDef.h"
namespace asst {
struct HandleInfo {
std::string className;
std::string windowName;
};
struct SimulatorInfo {
std::vector<HandleInfo> window;
std::vector<HandleInfo> view;
std::vector<HandleInfo> control;
int width = 0;
int height = 0;
int xOffset = 0;
int yOffset = 0;
};
enum class TaskType {
ClickSelf,
ClickRand,
DoNothing,
Stop
};
struct TaskInfo {
std::string filename;
double threshold = 0;
TaskType type;
std::vector<std::string> next;
unsigned int exec_times = 0;
unsigned int max_times = UINT_MAX;
};
struct Options {
std::string delayType;
int delayFixedTime = 0;
bool cache = false;
};
class Configer
{
@@ -46,8 +15,6 @@ namespace asst {
~Configer() = default;
static bool reload();
static std::string getCurDir();
static std::string getResDir();
static std::string m_version;
static Options m_options;
@@ -55,6 +22,12 @@ namespace asst {
static std::unordered_map<std::string, SimulatorInfo> m_handles;
static bool setParam(const std::string& type, const std::string& param, const std::string& value);
static std::optional<std::string> getParam(const std::string& type, const std::string& param);
static void clearExecTimes();
constexpr static double DefaultThreshold = 0.9;
constexpr static double DefaultCacheThreshold = 0.9;
private:
Configer() = default;

View File

@@ -1,11 +1,12 @@
#pragma once
#include "AsstDef.h"
#include <opencv2/opencv.hpp>
#include <unordered_map>
#include <utility>
#include <tuple>
#include <opencv2/opencv.hpp>
#include "AsstDef.h"
namespace asst {

View File

@@ -0,0 +1,105 @@
#pragma once
#include <fstream>
#include <mutex>
#include <chrono>
#include <iostream>
#include "AsstAux.h"
namespace asst {
class Logger {
public:
~Logger() = default;
Logger(const Logger&) = delete;
Logger(Logger&&) = delete;
static Logger& get_instance()
{
static Logger _unique_instance;
return _unique_instance;
}
template <typename... Args>
inline void log_trace(Args &&... args)
{
log("TRC", std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_info(Args &&... args)
{
log("INF", std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_error(Args &&... args)
{
log("ERR", std::forward<Args>(args)...);
}
private:
Logger() = default;
template <typename... Args>
void log(const std::string& level, Args &&... args)
{
std::unique_lock<std::mutex> trace_lock(m_trace_mutex);
SYSTEMTIME curtime;
GetLocalTime(&curtime);
char buff[64] = { 0 };
sprintf_s(buff, "[%04d-%02d-%02d %02d:%02d:%02d.%03d][%s][Px%x][Tx%x]",
curtime.wYear, curtime.wMonth, curtime.wDay,
curtime.wHour, curtime.wMinute, curtime.wSecond, curtime.wMilliseconds,
level.c_str(), _getpid(), GetCurrentThreadId());
if (level == "ERR" || level == "INF"
#ifdef _DEBUG
|| level == "TRC"
#endif
) {
stream_args(std::cout, buff, std::forward<Args>(args)...);
}
std::ofstream out_stream(asst::GetCurrentDir() + "asst.log", std::ios::out | std::ios::app);
stream_args(out_stream, buff, std::forward<Args>(args)...);
}
template <typename T, typename... Args>
inline void stream_args(std::ostream& os, T&& first, Args && ...rest)
{
os << first << " ";
stream_args(os, std::forward<Args>(rest)...);
}
inline void stream_args(std::ostream& os)
{
os << std::endl;
}
std::mutex m_trace_mutex;
};
class LoggerAux {
public:
LoggerAux(const std::string& func_name)
: m_func_name(func_name),
m_start_time(std::chrono::system_clock::now())
{
Logger::get_instance().log_trace(m_func_name, " | enter");
}
~LoggerAux()
{
auto duration = std::chrono::system_clock::now() - m_start_time;
Logger::get_instance().log_trace(m_func_name, " | leave,",
std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(), "ms");
}
private:
std::string m_func_name;
std::chrono::time_point<std::chrono::system_clock> m_start_time;
};
}
#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__)

View File

@@ -0,0 +1,37 @@
#pragma once
#include <string>
#include <optional>
#include <mutex>
namespace asst {
struct __declspec(dllexport) VersionInfo {
std::string tag_name;
std::string html_url;
std::string down_url;
std::string author_name;
std::string created_time;
std::string body;
};
class __declspec(dllexport) Updater
{
static const std::string GithubReleaseLastestApiUrl;
static const std::string GithubReleaseApiUrl;
public:
~Updater() = default;
static Updater& instance();
bool has_new_version();
const VersionInfo & get_version_info() const noexcept;
private:
Updater() = default;
std::optional<std::string> request_github_api();
bool m_has_new_version = false;
VersionInfo m_lastest_version;
std::mutex m_mutex;
};
}

View File

@@ -30,10 +30,12 @@ namespace asst {
const std::string m_simulator_name;
const HandleType m_handle_type;
HWND m_handle = NULL;
bool m_is_adb = false;
std::string m_click_cmd;
std::minstd_rand m_rand_engine;
int m_width = 0;
int m_height = 0;
int m_xOffset = 0;
int m_yOffset = 0;
int m_x_offset = 0;
int m_y_offset = 0;
};
}

View File

@@ -3,19 +3,23 @@
#include "WinMacro.h"
#include "Configer.h"
#include "Identify.h"
#include "Logger.hpp"
#include "AsstAux.h"
using namespace asst;
Assistance::Assistance()
{
DebugTraceFunction;
Configer::reload();
m_pIder = std::make_shared<Identify>();
for (auto&& [name, info] : Configer::m_tasks)
{
m_pIder->addImage(name, Configer::getResDir() + info.filename);
m_pIder->addImage(name, GetResourceDir() + info.filename);
}
m_pIder->setUseCache(Configer::m_options.cache);
m_pIder->setUseCache(Configer::m_options.identify_cache);
m_working_thread = std::thread(workingProc, this);
@@ -23,6 +27,8 @@ Assistance::Assistance()
Assistance::~Assistance()
{
DebugTraceFunction;
if (m_pWindow != nullptr) {
m_pWindow->showWindow();
}
@@ -38,6 +44,8 @@ Assistance::~Assistance()
std::optional<std::string> Assistance::setSimulator(const std::string& simulator_name)
{
DebugTraceFunction;
stop();
auto create_handles = [&](const std::string& name) -> bool {
@@ -65,7 +73,7 @@ std::optional<std::string> Assistance::setSimulator(const std::string& simulator
else {
ret = create_handles(simulator_name);
}
if (ret && m_pWindow->resizeWindow()) {
if (ret && m_pWindow->showWindow() && m_pWindow->resizeWindow()) {
m_inited = true;
return cor_name;
}
@@ -77,11 +85,17 @@ std::optional<std::string> Assistance::setSimulator(const std::string& simulator
void Assistance::start(const std::string& task)
{
DebugTraceFunction;
DebugTrace("Start |", task);
if (m_thread_running || !m_inited) {
return;
}
std::unique_lock<std::mutex> lock(m_mutex);
Configer::clearExecTimes();
m_pIder->clear_cache();
m_next_tasks.clear();
m_next_tasks.emplace_back(task);
@@ -89,10 +103,16 @@ void Assistance::start(const std::string& task)
m_condvar.notify_one();
}
void Assistance::stop()
void Assistance::stop(bool block)
{
std::unique_lock<std::mutex> lock(m_mutex);
DebugTraceFunction;
DebugTrace("Stop |", block);
std::unique_lock<std::mutex> lock;
if (block) { // Íⲿµ÷ÓÃ
lock = std::unique_lock<std::mutex>(m_mutex);
Configer::clearExecTimes();
}
m_thread_running = false;
m_next_tasks.clear();
m_pIder->clear_cache();
@@ -100,11 +120,23 @@ void Assistance::stop()
bool Assistance::setParam(const std::string& type, const std::string& param, const std::string& value)
{
DebugTraceFunction;
DebugTrace("SetParam |", type, param, value);
return Configer::setParam(type, param, value);
}
std::optional<std::string> Assistance::getParam(const std::string& type, const std::string& param)
{
// DebugTraceFunction;
return Configer::getParam(type, param);
}
void Assistance::workingProc(Assistance* pThis)
{
DebugTraceFunction;
while (!pThis->m_thread_exit) {
std::unique_lock<std::mutex> lock(pThis->m_mutex);
if (pThis->m_thread_running) {
@@ -113,12 +145,13 @@ void Assistance::workingProc(Assistance* pThis)
std::string matched_task;
Rect matched_rect;
for (auto&& task_name : pThis->m_next_tasks) {
double threshold = Configer::m_tasks[task_name].threshold;
auto&& task = Configer::m_tasks[task_name];
double threshold = task.threshold;
auto&& [algorithm, value, rect] = pThis->m_pIder->findImage(curImg, task_name, threshold);
DebugTrace("%-20s Type:%d, Value:%f", task_name.c_str(), algorithm, value);
DebugTrace(task_name, "Type:", algorithm, "Value:", value);
if (algorithm == 0 ||
(algorithm == 1 && value >= threshold)
|| (algorithm == 2 && value >= 0.9999)) {
|| (algorithm == 2 && value >= task.cache_threshold)) {
matched_task = task_name;
matched_rect = rect;
break;
@@ -126,46 +159,82 @@ void Assistance::workingProc(Assistance* pThis)
}
if (!matched_task.empty()) {
auto task = Configer::m_tasks[matched_task];
DebugTraceInfo("Matched: %s, Type: %d", matched_task.c_str(), task.type);
auto&& task = 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; }
}
switch (task.type) {
case TaskType::ClickSelf:
if (task.exec_times >= task.max_times) {
pThis->m_thread_running = false;
pThis->m_next_tasks.clear();
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)
&& 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(Configer::m_options.control_delay_lower, 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; }
}
switch (task.type) {
case TaskType::ClickRect:
matched_rect = task.specific_area;
[[fallthrough]];
case TaskType::ClickSelf:
pThis->m_pCtrl->clickRange(matched_rect);
break;
case TaskType::ClickRand:
pThis->m_pCtrl->clickRange(pThis->m_pCtrl->getWindowRect());
break;
case TaskType::DoNothing:
break;
case TaskType::Stop:
DebugTrace("TaskType is Stop");
pThis->stop(false);
continue;
break;
default:
DebugTraceError("Unknown option type:", task.type);
break;
}
pThis->m_pCtrl->clickRange(matched_rect);
++task.exec_times;
break;
case TaskType::ClickRand:
pThis->m_pCtrl->clickRange(pThis->m_pCtrl->getWindowRect());
break;
case TaskType::DoNothing:
break;
case TaskType::Stop:
DebugTrace("TaskType is Stop");
pThis->m_thread_running = false;
pThis->m_next_tasks.clear();
continue;
break;
default:
DebugTraceError("Unknown option type: %d", task.type);
break;
for (auto&& reduce : task.reduce_other_times) {
--Configer::m_tasks[reduce].exec_times;
DebugTrace("Reduce exec times", reduce, 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));
auto 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 = Configer::m_tasks[matched_task].next;
}
else {
DebugTraceInfo("Reached limit");
pThis->m_next_tasks = Configer::m_tasks[matched_task].exceeded_next;
}
pThis->m_next_tasks = Configer::m_tasks[matched_task].next;
std::string nexts;
std::string nexts_str;
for (auto&& name : pThis->m_next_tasks) {
nexts += name + ",";
nexts_str += name + ",";
}
if (nexts.back() == ',') {
nexts.pop_back();
if (nexts_str.back() == ',') {
nexts_str.pop_back();
}
DebugTrace("Next: %s", nexts.c_str());
DebugTrace("Next:", nexts_str);
}
pThis->m_condvar.wait_for(lock, std::chrono::milliseconds(Configer::m_options.delayFixedTime));
pThis->m_condvar.wait_for(lock,
std::chrono::milliseconds(Configer::m_options.identify_delay),
[&]() -> bool { return !pThis->m_thread_running; });
}
else {
pThis->m_condvar.wait(lock);

View File

@@ -1,5 +1,7 @@
#include "AsstCaller.h"
#include "Updater.h"
#include <string.h>
asst::Assistance* CreateAsst()
{
@@ -54,5 +56,31 @@ bool AsstSetParam(asst::Assistance* p_asst, const char* type, const char* param,
return false;
}
p_asst->setParam(type, param, value);
return p_asst->setParam(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->getParam(type, param);
if (!ret) {
return false;
}
strcpy_s(buffer, buffer_size, ret.value().c_str());
return true;
}
bool CheckVersionUpdate(char* tag_buffer, int tag_bufsize, char* html_url_buffer, int html_bufsize, char* body_buffer, int body_bufsize)
{
bool ret = asst::Updater::instance().has_new_version();
if (!ret) {
return false;
}
auto && info = asst::Updater::instance().get_version_info();
strcpy_s(tag_buffer, tag_bufsize, info.tag_name.c_str());
strcpy_s(html_url_buffer, html_bufsize, info.html_url.c_str());
strcpy_s(body_buffer, body_bufsize, info.body.c_str());
return true;
}

View File

@@ -7,6 +7,8 @@
#include "json.h"
#include "AsstDef.h"
#include "AsstAux.h"
#include "Logger.hpp"
using namespace asst;
@@ -18,7 +20,7 @@ std::unordered_map<std::string, SimulatorInfo> Configer::m_handles;
bool Configer::reload()
{
std::string filename = getResDir() + "config.json";
std::string filename = GetResourceDir() + "config.json";
std::ifstream ifs(filename, std::ios::in);
if (!ifs.is_open()) {
return false;
@@ -39,15 +41,27 @@ bool Configer::reload()
m_version = root["version"].as_string();
auto options_obj = root["options"].as_object();
m_options.delayType = options_obj["delay"]["type"].as_string();
m_options.delayFixedTime = options_obj["delay"]["fixedTime"].as_integer();
m_options.cache = options_obj["cache"].as_boolean();
m_options.identify_delay = options_obj["identifyDelay"].as_integer();
m_options.identify_cache = options_obj["identifyCache"].as_boolean();
m_options.control_delay_lower = options_obj["controlDelayRange"][0].as_integer();
m_options.control_delay_upper = options_obj["controlDelayRange"][1].as_integer();
auto tasks_obj = root["tasks"].as_object();
for (auto&& [name, task_json] : tasks_obj) {
TaskInfo task_info;
task_info.filename = task_json["filename"].as_string();
task_info.threshold = task_json["threshold"].as_double();
if (task_json.exist("threshold")) {
task_info.threshold = task_json["threshold"].as_double();
}
else {
task_info.threshold = DefaultThreshold;
}
if (task_json.exist("cache_threshold")) {
task_info.cache_threshold = task_json["cache_threshold"].as_double();
}
else {
task_info.cache_threshold = DefaultCacheThreshold;
}
std::string type = task_json["type"].as_string();
std::transform(type.begin(), type.end(), type.begin(), std::tolower);
if (type == "clickself") {
@@ -62,13 +76,45 @@ bool Configer::reload()
else if (type == "stop") {
task_info.type = TaskType::Stop;
}
else if (type == "clickrect") {
task_info.type = TaskType::ClickRect;
auto area_json = task_json["specificArea"].as_array();
task_info.specific_area = Rect(
area_json[0].as_integer(),
area_json[1].as_integer(),
area_json[2].as_integer(),
area_json[3].as_integer());
}
else {
DebugTraceError("Task: %s 's type error: %s", name.c_str(), type.c_str());
DebugTraceError("Task:", name, "error:", type);
return false;
}
if (task_json.as_object().exist("maxTimes")) {
if (task_json.exist("maxTimes")) {
task_info.max_times = task_json["maxTimes"].as_integer();
}
if (task_json.exist("exceededNext")) {
auto next_arr = task_json["exceededNext"].as_array();
for (auto&& name : next_arr) {
task_info.exceeded_next.emplace_back(name.as_string());
}
}
else {
task_info.exceeded_next.emplace_back("Stop");
}
if (task_json.exist("preDelay")) {
task_info.pre_delay = task_json["preDelay"].as_integer();
}
if (task_json.exist("rearDelay")) {
task_info.rear_delay = task_json["rearDelay"].as_integer();
}
if (task_json.exist("reduceOtherTimes")) {
auto reduce_arr = task_json["reduceOtherTimes"].as_array();
for (auto&& reduce : reduce_arr) {
task_info.reduce_other_times.emplace_back(reduce.as_string());
}
}
auto next_arr = task_json["next"].as_array();
for (auto&& name : next_arr) {
task_info.next.emplace_back(name.as_string());
@@ -102,16 +148,23 @@ bool Configer::reload()
handle_info.windowName = info["window"].as_string();
simulator_info.control.emplace_back(handle_info);
}
if (simulator_json.exist("adbControl")) {
simulator_info.is_adb = true;
// meojson的bug暂时没空修先转个字符串
simulator_info.adb.path = StringReplaceAll(simulator_json["adbControl"]["path"].as_string(), "\\\\", "\\");
simulator_info.adb.connect = simulator_json["adbControl"]["connect"].as_string();
simulator_info.adb.click = simulator_json["adbControl"]["click"].as_string();
}
simulator_info.width = simulator_json["width"].as_integer();
simulator_info.height = simulator_json["height"].as_integer();
simulator_info.xOffset = simulator_json["xOffset"].as_integer();
simulator_info.yOffset = simulator_json["yOffset"].as_integer();
simulator_info.x_offset = simulator_json["xOffset"].as_integer();
simulator_info.y_offset = simulator_json["yOffset"].as_integer();
m_handles.emplace(name, simulator_info);
}
}
catch (json::exception& e) {
DebugTraceError("Load config json error!: %s", e.what());
DebugTraceError("Load config json error!", e.what());
return false;
}
@@ -139,8 +192,11 @@ bool Configer::setParam(const std::string& type, const std::string& param, const
else if (type == "stop") {
task_info.type = TaskType::Stop;
}
else if (type == "clickrect") {
task_info.type = TaskType::ClickRect;
}
else {
DebugTraceError("Task: %s 's type error: %s", param.c_str(), type.c_str());
DebugTraceError("Task", param, "'s type error:", type);
return false;
}
}
@@ -153,18 +209,17 @@ bool Configer::setParam(const std::string& type, const std::string& param, const
return true;
}
std::string Configer::getCurDir()
std::optional<std::string> Configer::getParam(const std::string& type, const std::string& param)
{
if (m_curDir.empty()) {
char exepath_buff[_MAX_PATH] = { 0 };
::GetModuleFileNameA(NULL, exepath_buff, _MAX_PATH);
std::string exepath(exepath_buff);
m_curDir = exepath.substr(0, exepath.find_last_of('\\') + 1);
if (type == "task.execTimes" && m_tasks.find(param) != m_tasks.cend()) {
return std::to_string(m_tasks.at(param).exec_times);
}
return m_curDir;
return std::nullopt;
}
std::string Configer::getResDir()
void Configer::clearExecTimes()
{
return getCurDir() + "resource\\";
}
for (auto&& t : m_tasks) {
t.second.exec_times = 0;
}
}

View File

@@ -48,7 +48,8 @@ Mat Identify::image2Hist(const cv::Mat& src)
double Identify::imageHistComp(const cv::Mat& src, const cv::MatND& hist)
{
return compareHist(image2Hist(src), hist, CV_COMP_CORREL);
// keep the interface return value unchanged
return 1 - compareHist(image2Hist(src), hist, CV_COMP_BHATTACHARYYA);
}
std::pair<double, cv::Point> Identify::findImage(const cv::Mat& image, const cv::Mat& templ)
@@ -59,7 +60,7 @@ std::pair<double, cv::Point> Identify::findImage(const cv::Mat& image, const cv:
cvtColor(templ, templ_hsv, COLOR_BGR2HSV);
Mat matched;
matchTemplate(image_hsv, templ_hsv, matched, cv::TM_CCORR_NORMED);
matchTemplate(image_hsv, templ_hsv, matched, cv::TM_CCOEFF_NORMED);
double minVal = 0, maxVal = 0;
cv::Point minLoc, maxLoc;
@@ -82,7 +83,7 @@ std::tuple<int, double, asst::Rect> Identify::findImage(const Mat& cur, const st
auto&& templ_mat = m_mat_map.at(templ);
auto&& [value, point] = findImage(cur, templ_mat);
cv::Rect raw_rect(point.x, point.y, templ_mat.cols, templ_mat.rows);
if (m_use_cache && value >= threshold) {
m_cache_map.emplace(templ, std::make_pair(raw_rect, image2Hist(cur(raw_rect))));
}

View File

@@ -0,0 +1,98 @@
#include "Updater.h"
#include <stdio.h>
#include <Windows.h>
#include <WinInet.h>
#include "json.h"
#include "AsstDef.h"
#include "Logger.hpp"
using namespace asst;
const std::string Updater::GithubReleaseLastestApiUrl = "https://api.github.com/repos/MistEO/MeoAssistance/releases/latest";
const std::string Updater::GithubReleaseApiUrl = "https://api.github.com/repos/MistEO/MeoAssistance/releases/latest";
Updater& Updater::instance()
{
static Updater unique_instance;
return unique_instance;
}
bool Updater::has_new_version()
{
auto req_ret = request_github_api();
if (!req_ret) {
DebugTraceInfo("Requeset Error");
return false;
}
auto parse_ret = json::parser::parse(req_ret.value());
if (!parse_ret) {
DebugTraceInfo("Parse Error");
return false;
}
json::value root = std::move(parse_ret).value();
std::unique_lock<std::mutex> lock(m_mutex);
try {
m_lastest_version.tag_name = root["tag_name"].as_string();
m_lastest_version.html_url = root["html_url"].as_string();
m_lastest_version.author_name = root["author"]["login"].as_string();
m_lastest_version.created_time = root["created_at"].as_string();
m_lastest_version.body = root["body"].as_string();
auto assets = root["assets"].as_array();
if (assets.size() == 1) {
m_lastest_version.down_url = assets[0]["browser_download_url"].as_string();
}
}
catch (json::exception& exp) {
DebugTraceError("Json Error", exp.what());
return false;
}
if (m_lastest_version.tag_name > Version) {
m_has_new_version = true;
return true;
}
else {
return false;
}
}
const VersionInfo & Updater::get_version_info() const noexcept
{
return m_lastest_version;
}
std::optional<std::string> Updater::request_github_api()
{
HINTERNET h_session = ::InternetOpenA("GithubRelease", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (h_session == NULL) {
return std::nullopt;
}
HINTERNET h_http = ::InternetOpenUrlA(h_session, GithubReleaseLastestApiUrl.c_str(), NULL, 0, INTERNET_FLAG_DONT_CACHE, 0);
if (h_http == NULL) {
return std::nullopt;
}
std::string response;
DWORD buff_size = 4096;
char *buffer = new char[buff_size];
DWORD number = 1;
while (number > 0) {
memset(buffer, 0, buff_size);
InternetReadFile(h_http, buffer, buff_size, &number);
if (number != 0) {
response += std::string(buffer, number);
}
}
delete[] buffer;
buffer = NULL;
InternetCloseHandle(h_http);
h_http = NULL;
InternetCloseHandle(h_session);
h_session = NULL;
return response;
}

View File

@@ -2,21 +2,22 @@
#include <vector>
#include <utility>
#include <ctime>
#include <algorithm>
#include <chrono>
#include <stdint.h>
#include <WinUser.h>
#include "Configer.h"
#include "AsstDef.h"
#include "Logger.hpp"
using namespace asst;
WinMacro::WinMacro(const std::string& simulator_name, HandleType type)
: m_simulator_name(simulator_name),
m_handle_type(type),
m_rand_engine(time(NULL))
m_rand_engine(std::chrono::system_clock::now().time_since_epoch().count())
{
findHandle();
}
@@ -40,12 +41,13 @@ bool WinMacro::findHandle()
handle_vec = info.view;
break;
case HandleType::Control:
m_xOffset = info.xOffset;
m_yOffset = info.yOffset;
m_is_adb = info.is_adb;
m_x_offset = info.x_offset;
m_y_offset = info.y_offset;
handle_vec = info.control;
break;
default:
DebugTraceError("Handle type error!: %d", m_handle_type);
DebugTraceError("Handle type error!", m_handle_type);
return false;
}
@@ -77,8 +79,30 @@ bool WinMacro::findHandle()
window_wbuff = NULL;
}
}
if (m_is_adb && m_handle != NULL) {
DebugTrace("Handle: 0x%x, Name: %s, Type: %d", m_handle, m_simulator_name.c_str(), m_handle_type);
DWORD pid = 0;
::GetWindowThreadProcessId(m_handle, &pid);
HANDLE handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
LPSTR path_buff = new CHAR[MAX_PATH];
memset(path_buff, 0, MAX_PATH);
DWORD buff_size = MAX_PATH;
QueryFullProcessImageNameA(handle, 0, path_buff, &buff_size);
std::string adb_dir = path_buff;
if (path_buff != NULL) {
delete[] path_buff;
path_buff = NULL;
}
size_t pos = adb_dir.find_last_of('\\') + 1;
adb_dir = adb_dir.substr(0, pos);
adb_dir = '"' + StringReplaceAll(info.adb.path, "[EmulatorPath]", adb_dir) + '"';
std::string connect_cmd = adb_dir + info.adb.connect;
int ret = system(connect_cmd.c_str());
DebugTrace("Call", connect_cmd, "—— ret", ret);
m_click_cmd = adb_dir + info.adb.click;
}
DebugTrace("Handle:", m_handle, "Name:", m_simulator_name, "Type:", m_handle_type);
if (m_handle != NULL) {
return true;
@@ -111,7 +135,7 @@ bool WinMacro::showWindow()
return false;
}
return ::ShowWindow(m_handle, SW_SHOW);
return ::ShowWindow(m_handle, SW_RESTORE);
}
bool WinMacro::hideWindow()
@@ -163,17 +187,29 @@ bool WinMacro::click(const Point& p)
return false;
}
int x = (p.x + m_xOffset) / getScreenScale();
int y = (p.y + m_yOffset) / getScreenScale();
DebugTrace("click, raw: %d, %d, cor: %d, %d", p.x, p.y, x, y);
LPARAM lparam = MAKELPARAM(x, y);
if (m_is_adb) {
int x = (p.x + m_x_offset);
int y = (p.y + m_y_offset);
std::string cur_cmd = StringReplaceAll(m_click_cmd, "[x]", std::to_string(x));
cur_cmd = StringReplaceAll(cur_cmd, "[y]", std::to_string(y));
int ret = system(cur_cmd.c_str());
DebugTrace("Call", cur_cmd, "—— ret", ret);
return !ret;
}
else {
int x = (p.x + m_x_offset) / getScreenScale();
int y = (p.y + m_y_offset) / getScreenScale();
DebugTrace("Click, raw:", p.x, p.y, "corr:", x, y);
::SendMessage(m_handle, WM_LBUTTONDOWN, MK_LBUTTON, lparam);
::SendMessage(m_handle, WM_LBUTTONUP, 0, lparam);
LPARAM lparam = MAKELPARAM(x, y);
return true;
::SendMessage(m_handle, WM_LBUTTONDOWN, MK_LBUTTON, lparam);
::SendMessage(m_handle, WM_LBUTTONUP, 0, lparam);
return true;
}
}
bool WinMacro::clickRange(const Rect& rect)
@@ -242,10 +278,12 @@ cv::Mat WinMacro::getImage(const Rect& rect)
DeleteDC(memDC);
ReleaseDC(m_handle, pDC);
#ifdef _DEBUG
std::string filename = Configer::getCurDir() + "\\test.bmp";
cv::imwrite(filename, dst_mat);
#endif
/*
#ifdef _DEBUG
std::string filename = GetCurrentDir() + "\\print.bmp";
cv::imwrite(filename, dst_mat);
#endif
*/
return dst_mat;
}

View File

@@ -5,15 +5,20 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MeoAsstGui"
mc:Ignorable="d"
Title="MeoAssistance-明日方舟辅助" Height="500" Width="400">
Title="Meo明日方舟辅助" Height="500" Width="400" Loaded="OnLoaded">
<Grid>
<CheckBox x:Name="checkBox_useMedicine" Content="吃理智药" HorizontalAlignment="Left" Margin="50,100,0,0" VerticalAlignment="Top" Checked="checkBox_useMedicine_Checked" Unchecked="checkBox_useMedicine_Checked" IsChecked="True"/>
<CheckBox x:Name="checkBox_useStone" Content="吃石头" HorizontalAlignment="Left" Margin="50,150,0,0" VerticalAlignment="Top" Checked="checkBox_useStone_Checked" Unchecked="checkBox_useStone_Checked"/>
<TextBox x:Name="textBox_useStone" HorizontalAlignment="Left" Height="23" Margin="112,148,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="47" TextChanged="textBox_useStone_TextChanged" InputMethod.IsInputMethodEnabled="False"/>
<Label x:Name="label_stoneNumber" Content="颗" HorizontalAlignment="Left" Margin="164,144,0,0" VerticalAlignment="Top" RenderTransformOrigin="-5.771,-6.291"/>
<Button x:Name="button_startSanity" Content="开始刷理智" HorizontalAlignment="Left" Margin="50,200,0,0" VerticalAlignment="Top" Width="120" Height="50" Click="button_Click_startSanity"/>
<Button x:Name="button_stop" Content="停止" HorizontalAlignment="Left" Margin="50,280,0,0" VerticalAlignment="Top" Width="300" Height="50" Click="button_Click_stop"/>
<Button x:Name="button_startSanity" Content="开始刷理智" HorizontalAlignment="Left" Margin="50,270,0,0" VerticalAlignment="Top" Width="120" Height="50" Click="button_Click_startSanity"/>
<Button x:Name="button_stop" Content="停止" HorizontalAlignment="Left" Margin="50,350,0,0" VerticalAlignment="Top" Width="300" Height="50" Click="button_Click_stop"/>
<Label x:Name="catch_status" Content="" HorizontalAlignment="Left" Margin="50,30,0,0" VerticalAlignment="Top"/>
<Button x:Name="button_visit" Content="访问基建" HorizontalAlignment="Left" Margin="230,200,0,0" VerticalAlignment="Top" Width="120" Height="50" Click="button_Click_visit"/>
<Button x:Name="button_visit" Content="访问基建" HorizontalAlignment="Left" Margin="230,270,0,0" VerticalAlignment="Top" Width="120" Height="50" Click="button_Click_visit"/>
<Label x:Name="exec_times" Content="" HorizontalAlignment="Left" Margin="230,30,0,0" VerticalAlignment="Top"/>
<Label x:Name="stone_times" Content="" HorizontalAlignment="Left" Margin="230,144,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.702,0.403"/>
<CheckBox x:Name="checkBox_maxTimes" Content="指定次数" HorizontalAlignment="Left" Margin="50,200,0,0" VerticalAlignment="Top" Checked="checkBox_maxTimes_Checked" Unchecked="checkBox_maxTimes_Checked"/>
<TextBox x:Name="textBox_maxTimes" HorizontalAlignment="Left" Height="23" Margin="124,198,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top" Width="47" TextChanged="textBox_maxTimes_TextChanged" InputMethod.IsInputMethodEnabled="False"/>
<Label x:Name="label" Content="次" HorizontalAlignment="Left" Margin="176,193,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.397,-0.745"/>
</Grid>
</Window>

View File

@@ -13,6 +13,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Windows.Threading;
namespace MeoAsstGui
{
@@ -22,38 +23,56 @@ namespace MeoAsstGui
public partial class MainWindow : Window
{
[DllImport("MeoAssistance.dll")] static public extern IntPtr CreateAsst();
[DllImport("MeoAssistance.dll")] static public extern void DestoryAsst(IntPtr ptr);
[DllImport("MeoAssistance.dll")] static public extern bool AsstCatchSimulator(IntPtr ptr);
[DllImport("MeoAssistance.dll")] static public extern void AsstStart(IntPtr ptr, string task);
[DllImport("MeoAssistance.dll")] static public extern void AsstStop(IntPtr ptr);
[DllImport("MeoAssistance.dll")] static public extern bool AsstSetParam(IntPtr p_asst, string type, string param, string value);
[DllImport("MeoAssistance.dll")] static private extern IntPtr CreateAsst();
[DllImport("MeoAssistance.dll")] static private extern void DestoryAsst(IntPtr ptr);
[DllImport("MeoAssistance.dll")] static private extern bool AsstCatchSimulator(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 IntPtr p_asst;
UpdateDialog updateDialog;
private DispatcherTimer update_times = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
p_asst = CreateAsst();
}
~MainWindow()
{
DestoryAsst(p_asst);
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
updateDialog = new UpdateDialog();
updateDialog.CheckUpdateAndShowDialog();
updateDialog.Close();
p_asst = CreateAsst();
update_times.Tick += new EventHandler(updateExecTimes);
update_times.Interval = TimeSpan.FromSeconds(1);
}
private void button_Click_startSanity(object sender, RoutedEventArgs e)
{
bool catched = AsstCatchSimulator(p_asst);
catch_status.Content = "捕获模拟器窗口:" + catched;
AsstStart(p_asst, "SanityBegin");
update_times.Start();
}
private void button_Click_stop(object sender, RoutedEventArgs e)
{
AsstStop(p_asst);
catch_status.Content = "";
update_times.Stop();
exec_times.Content = "";
stone_times.Content = "";
}
private void checkBox_useMedicine_Checked(object sender, RoutedEventArgs e)
@@ -70,18 +89,25 @@ namespace MeoAsstGui
private void textBox_useStone_TextChanged(object sender, TextChangedEventArgs e)
{
AsstSetParam(p_asst, "task.maxTimes", "StoneConfirm", textBox_useStone.Text);
if (checkBox_useStone.IsChecked == true)
{
String text = textBox_useStone.Text != String.Empty ? textBox_useStone.Text : "0";
AsstSetParam(p_asst, "task.maxTimes", "StoneConfirm", text);
}
}
private void checkBox_useStone_Checked(object sender, RoutedEventArgs e)
{
if (checkBox_useMedicine.IsChecked == true)
if (checkBox_useStone.IsChecked == true)
{
AsstSetParam(p_asst, "task.type", "UseStone", "doNothing");
String text = textBox_useStone.Text != String.Empty ? textBox_useStone.Text : "0";
AsstSetParam(p_asst, "task.maxTimes", "StoneConfirm", text);
}
else
{
AsstSetParam(p_asst, "task.type", "UseStone", "stop");
AsstSetParam(p_asst, "task.maxTimes", "StoneConfirm", "0");
}
}
@@ -91,5 +117,41 @@ namespace MeoAsstGui
catch_status.Content = "捕获模拟器窗口:" + catched;
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 + " 个";
}
}
private void checkBox_maxTimes_Checked(object sender, RoutedEventArgs e)
{
if (checkBox_maxTimes.IsChecked == true)
{
String text = textBox_maxTimes.Text != String.Empty ? textBox_maxTimes.Text : "0";
AsstSetParam(p_asst, "task.maxTimes", "StartButton1", text);
}
else
{
AsstSetParam(p_asst, "task.maxTimes", "StartButton1", int.MaxValue.ToString());
}
}
private void textBox_maxTimes_TextChanged(object sender, TextChangedEventArgs e)
{
if (checkBox_maxTimes.IsChecked == true)
{
String text = textBox_maxTimes.Text != String.Empty ? textBox_maxTimes.Text : "0";
AsstSetParam(p_asst, "task.maxTimes", "StartButton1", text);
}
}
}
}

View File

@@ -26,7 +26,7 @@
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<ApplicationVersion>0.2.0.0</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
@@ -54,6 +54,9 @@
<PropertyGroup>
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>favicon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
@@ -75,6 +78,9 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="UpdateDialog.xaml.cs">
<DependentUpon>UpdateDialog.xaml</DependentUpon>
</Compile>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -87,6 +93,10 @@
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="UpdateDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
@@ -126,6 +136,9 @@
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Resource Include="favicon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>xcopy /e /y /i $(SolutionDir)resource $(TargetDir)resource</PostBuildEvent>

View File

@@ -0,0 +1,16 @@
<Window x:Class="MeoAsstGui.UpdateDialog"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MeoAsstGui"
mc:Ignorable="d"
Title="有新版本" Height="500" Width="400">
<Grid>
<Label x:Name="label_tag" Content="" HorizontalAlignment="Center" Margin="10,25,10.333,0" VerticalAlignment="Top" Height="50" Width="373" FontSize="24"/>
<Label x:Name="label_body" Content="" HorizontalAlignment="Left" Margin="50,80,0,0" VerticalAlignment="Top" Height="281" Width="290" FontSize="16"/>
<Button x:Name="button_confirm" Content="前往下载" HorizontalAlignment="Left" Margin="50,366,0,0" VerticalAlignment="Top" Width="120" Height="60" Click="button_confirm_Click"/>
<Button x:Name="button_cancer" Content="下次一定" HorizontalAlignment="Left" Margin="220,366,0,0" VerticalAlignment="Top" Width="120" Height="60" Click="button_cancer_Click"/>
</Grid>
</Window>

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
namespace MeoAsstGui
{
/// <summary>
/// UpdateDialog.xaml 的交互逻辑
/// </summary>
public partial class UpdateDialog : Window
{
[DllImport("MeoAssistance.dll")]
static private extern bool CheckVersionUpdate(
[In, Out] StringBuilder tag, int tag_bufsize,
[In, Out] StringBuilder html_url, int html_bufsize,
[In, Out] StringBuilder body, int body_bufsize);
public UpdateDialog()
{
InitializeComponent();
}
private String m_htmlUrl;
public void CheckUpdateAndShowDialog()
{
StringBuilder tag = new StringBuilder(64);
StringBuilder html_url = new StringBuilder(512);
StringBuilder body = new StringBuilder(4096);
bool ret = CheckVersionUpdate(tag, 64, html_url, 512, body, 4096);
if (!ret)
{
return;
}
label_tag.Content = "新版本:" + tag;
byte[] buffer1 = Encoding.Default.GetBytes(body.ToString());
byte[] buffer2 = Encoding.Convert(Encoding.UTF8, Encoding.Default, buffer1, 0, buffer1.Length);
string strBuffer = Encoding.Default.GetString(buffer2, 0, buffer2.Length);
strBuffer = strBuffer.Replace("\\r\\n", "\r\n");
int len = strBuffer.Length > 128 ? 128 : strBuffer.Length;
label_body.Content = strBuffer.Substring(0, len) + "\n......";
m_htmlUrl = html_url.ToString();
ShowDialog();
}
private void button_cancer_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void button_confirm_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Process.Start(m_htmlUrl);
Close();
}
}
}

BIN
MeoAsstGui/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -4,20 +4,18 @@ A game assistance for Arknights
一款明日方舟的游戏辅助供PC端安卓模拟器使用龟速开发中……
![截图](README/1.png)
![界面截图](readme/gui.png)
## 功能介绍
- 支持自动刷完理智
- 支持自动吃完体力
- 支持根据设置自动吃石头
- 刷完后程序会自动停止
- 自动刷完理智
- 可设置是否吃完理智
- 可设置是否吃石头级数量
- 可设置刷的次数
- 代理失败会自动放弃本次行动
- 支持自动访问好友基建
- 所有操作,都是点击按钮内随机位置,且模拟泊松分布,不会像鼠标宏一样一直是同一个点,没有封号风险
~~(虽然好像也没听说过谁用鼠标宏被封号的)~~
- 模拟器窗口可以被遮挡,即使全屏看视频、玩游戏,也完全不影响辅助运行
~~(但是模拟器还是不能最小化)~~
- 支持自动访问好友基建,访问完了还会贴心的帮你点进信用商店~
- 所有操作,都是点击按钮内随机位置,且支持设置随机延时,没有封号风险
- 模拟器窗口可以被遮挡,即使全屏看视频、玩游戏,也不影响辅助运行
- 支持多款主流模拟器
- 自适应分辨率及屏幕缩放
- 未来更多功能见[Todo](#Todo)
@@ -44,9 +42,14 @@ A game assistance for Arknights
兼容但需要手动设置分辨率设置中心——引擎设置——分辨率设置——1280x720——保存后重启模拟器
#### MuMu手游助手 && MuMu模拟器
#### MuMu模拟器
不兼容,MuMu所有的窗口句柄均不响应SendMessage鼠标消息但官方提供了adb的方式进行控制有时间再做
MuMu是个奇葩,它的所有的窗口句柄均不响应SendMessage鼠标消息
- MuMu模拟器
非常勉强的使用adb控制的方式兼容了刷理智的功能需要在模拟器设置里把分辨率设置成1280x720。建议使用"Sanity.exe"而不是图形化界面因为adb控制台窗口会一直往出弹
- MuMu手游助手星云引擎
不兼容,可见未来内也想不到什么好主意来兼容的
## 使用说明
@@ -69,6 +72,12 @@ A game assistance for Arknights
2. 点击"访问基建"
3. 达到10次上限或者所有可访问的好友都访问完了就会自动停的
### 设置操作延时
请手动修改`resource\config.json`文件中,`options`.`controlDelayRange`字段的值,格式为`[最小延时, 最大延时]`单位为毫秒例如想设置3~5秒的随机延时即设置为`[ 3000, 5000]` 即可。文件保存后请重新打开程序。
![图例](readme\controlDelayRange.png)
## Todo
~~在做了在做了.jpg~~
@@ -78,7 +87,7 @@ A game assistance for Arknights
- [x] 基本图形化界面
- [ ] 图形化界面进一步完善
- [ ] 日志打印,错误提示
- [ ] 使模拟器窗口不可见的按钮
- [ ] 操作随机延时支持设置
- [ ] 刷理智
- [x] 支持剿灭
- [x] 支持使模拟器窗口不可见
@@ -86,8 +95,10 @@ A game assistance for Arknights
- [x] 代理失败的情况
- [x] 支持等级提升
- [x] 支持自动勾选代理指挥
- [ ] 支持刷指定次数
- [x] 支持刷指定次数
- [x] 自动吃石头(根据设置,指定数量)
- [x] 操作随机延时
- [ ] 指定刷XX个某材料
- [ ] 支持凌晨4点更新数据
- [ ] 结束界面自动截图,可用于上传企鹅物流
- [ ] 持续监视理智,一有就自动刷掉
@@ -100,9 +111,13 @@ A game assistance for Arknights
- [x] 更换算法为模板匹配找图,而不是当前的区域相似度对比
- [x] 优化算法效率,添加缓存功能
- [x] 优化任务队列,减少不必要的计算
- [ ] 掉落材料计数
- [ ] 窗口分辨率自适应
- [ ] 进一步优化任务队列效率
- [x] 其他
- [x] 尝试减小程序体积
- [x] 检查程序更新
- [ ] 程序自动更新
## 致谢

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,9 +1,18 @@
#include "Assistance.h"
#include "Updater.h"
#include "Logger.hpp"
int main(int argc, char** argv)
{
using namespace asst;
bool up = Updater::instance().has_new_version();
if (up) {
auto && info = Updater::instance().get_version_info();
std::cout << "ÓÐа汾£º" << info.tag_name << std::endl;
std::cout << "µØÖ·£º" << info.html_url << std::endl;
}
Assistance asst;
auto ret = asst.setSimulator();
@@ -13,7 +22,7 @@ int main(int argc, char** argv)
return -1;
}
else {
DebugTraceInfo("Find Simulator: %s", ret->c_str());
DebugTraceInfo("Find Simulator:", ret.value());
}
DebugTraceInfo("Start");

Submodule meojson updated: e66deff929...d6d07f5e6c

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
readme/gui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
resource/CollectCredit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
resource/CreditStore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
resource/Loading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

BIN
resource/Mall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
resource/PopupCancel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
resource/PopupConfirm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,11 +1,9 @@
{
"version": "0.3",
"version": "0.4",
"options": {
"delay": {
"type": "fixedTime",
"fixedTime": 2000
},
"cache": true
"identifyCache": true,
"identifyDelay": 1000,
"controlDelayRange": [ 0, 0 ]
},
"handle": {
"BlueStacks": {
@@ -147,6 +145,64 @@
"height": 769,
"xOffset": 0,
"yOffset": -42
},
"MuMuAsst": {
"window": [
{
"class": "Qt5QWindowIcon",
"window": "明日方舟 - 星云引擎"
}
],
"view": [
{
"class": "Qt5QWindowIcon",
"window": "明日方舟 - 星云引擎"
}
],
"control": [
{
"class": "Qt5QWindowIcon_DELETE",
"window": "明日方舟 - 星云引擎_DELETE"
}
],
"adbControl": {
"path": "[EmulatorPath]..\\vmonitor\\bin\\adb_server.exe",
"connect": " connect 127.0.0.1:7555",
"click": " shell input tap [x] [y]"
},
"width": 1280,
"height": 809,
"xOffset": 0,
"yOffset": -36
},
"MuMuEmulator": {
"window": [
{
"class": "Qt5QWindowIcon",
"window": "明日方舟 - MuMu模拟器"
}
],
"view": [
{
"class": "Qt5QWindowIcon",
"window": "明日方舟 - MuMu模拟器"
}
],
"control": [
{
"class": "Qt5QWindowIcon",
"window": "明日方舟 - MuMu模拟器"
}
],
"adbControl": {
"path": "[EmulatorPath]..\\vmonitor\\bin\\adb_server.exe",
"connect": " connect 127.0.0.1:7555",
"click": " shell input tap [x] [y]"
},
"width": 1280,
"height": 809,
"xOffset": 0,
"yOffset": -36
}
},
"tasks": {
@@ -167,7 +223,7 @@
},
"UsePrts": {
"filename": "UsePrts.png",
"threshold": 0.97,
"threshold": 0.8,
"type": "clickSelf",
"next": [
"StartButton1"
@@ -175,7 +231,7 @@
},
"StartButton1": {
"filename": "StartButton1.png",
"threshold": 0.94,
"threshold": 0.8,
"type": "clickSelf",
"next": [
"StartButton2",
@@ -184,18 +240,17 @@
]
},
"StartButton2": {
"id": 2,
"filename": "StartButton2.png",
"threshold": 0.99,
"type": "clickSelf",
"rearDelay": 20000,
"next": [
"PRTS"
]
},
"PRTS": {
"filename": "PRTS.png",
"threshold": 0.98,
"type": "doNothing",
"rearDelay": 5000,
"next": [
"PRTS",
"PrtsErrorConfirm",
@@ -205,15 +260,28 @@
"Random": {
"filename": "",
"threshold": 0,
"type": "clickRand",
"type": "clickRect",
"specificArea": [
1100,
700,
150,
30
],
"next": [
"Loading",
"StartButton1",
"Random"
]
},
"Loading": {
"filename": "Loading.png",
"type": "doNothing",
"next": [
"StartButton1"
]
},
"UseMedicine": {
"filename": "UseMedicine.png",
"threshold": 0.99,
"type": "doNothing",
"next": [
"MedicineConfirm"
@@ -221,15 +289,14 @@
},
"MedicineConfirm": {
"filename": "MedicineConfirm.png",
"threshold": 0.98,
"type": "clickSelf",
"reduceOtherTimes": [ "StartButton1" ],
"next": [
"StartButton1"
]
},
"UseStone": {
"filename": "UseStone.png",
"threshold": 0.99,
"type": "doNothing",
"next": [
"StoneConfirm"
@@ -237,16 +304,15 @@
},
"StoneConfirm": {
"filename": "MedicineConfirm.png",
"threshold": 0.98,
"type": "clickSelf",
"maxTimes": 0,
"reduceOtherTimes": [ "StartButton1" ],
"next": [
"StartButton1"
]
},
"PrtsErrorConfirm": {
"filename": "PrtsErrorConfirm.png",
"threshold": 0.98,
"type": "doNothing",
"next": [
"AbandonAction"
@@ -254,8 +320,8 @@
},
"AbandonAction": {
"filename": "AbandonAction.png",
"threshold": 0.98,
"type": "clickSelf",
"reduceOtherTimes": [ "StartButton1", "StartButton2", "Loading" ],
"next": [
"Random"
]
@@ -270,21 +336,19 @@
"StartToVisit",
"VisitNext",
"VisitNextBlack",
"Return"
"ReturnToFriends"
]
},
"Return": {
"ReturnToFriends": {
"filename": "Return.png",
"threshold": 0.98,
"type": "clickSelf",
"next": [
"Friends",
"Return"
"ReturnToFriends"
]
},
"Friends": {
"filename": "Friends.png",
"threshold": 0.98,
"type": "clickSelf",
"next": [
"FriendsList"
@@ -292,7 +356,6 @@
},
"FriendsList": {
"filename": "FriendsList.png",
"threshold": 0.98,
"type": "clickSelf",
"next": [
"StartToVisit"
@@ -300,7 +363,6 @@
},
"StartToVisit": {
"filename": "StartToVisit.png",
"threshold": 0.98,
"type": "clickSelf",
"next": [
"VisitNext"
@@ -308,23 +370,74 @@
},
"VisitNext": {
"filename": "VisitNext.png",
"threshold": 0.98,
"type": "clickSelf",
"threshold": 0.99,
"maxTimes": 21,
"exceededNext": [
"ReturnToMall"
],
"next": [
"VisitLimited",
"VisitNext",
"VisitNextBlack"
]
},
"ReturnToMall": {
"filename": "Return.png",
"type": "clickSelf",
"next": [
"Mall",
"ReturnToMall",
"ReturnToMallConfirm"
]
},
"ReturnToMallConfirm": {
"filename": "PopupConfirm.png",
"type": "clickSelf",
"next": [
"ReturnToMall"
]
},
"Mall": {
"filename": "Mall.png",
"type": "clickSelf",
"next": [
"CreditStore"
]
},
"CreditStore": {
"filename": "CreditStore.png",
"threshold": 0.89,
"rearDelay": 1000,
"type": "clickSelf",
"next": [
"CollectCredit",
"Stop"
]
},
"CollectCredit": {
"filename": "CollectCredit.png",
"type": "clickSelf",
"next": [
"Stop"
]
},
"VisitLimited": {
"filename": "VisitLimited.png",
"threshold": 0.65,
"type": "stop",
"next": []
"type": "doNothing",
"next": [
"ReturnToMall"
]
},
"VisitNextBlack": {
"filename": "VisitNextBlack.png",
"threshold": 0.98,
"type": "doNothing",
"next": [
"ReturnToMall"
]
},
"Stop": {
"filename": "",
"threshold": 0,
"type": "stop",
"next": []
}

2
update_resource.bat Normal file
View File

@@ -0,0 +1,2 @@
xcopy /e /y /i resource x64\Debug\resource
xcopy /e /y /i resource x64\Release\resource