Compare commits

...

34 Commits

Author SHA1 Message Date
MistEO
ae9b6a1265 fixed an error of the cache value 2021-07-14 22:10:08 +08:00
MistEO
b69e7c76e6 update readme 2021-07-14 21:08:52 +08:00
MistEO
e81421579d add cache function to ider 2021-07-14 20:59:36 +08:00
MistEO
1d3c3874ce add config file to vcxproj 2021-07-14 19:58:32 +08:00
MistEO
bfd21e4631 update debugtrace and config 2021-07-14 19:09:48 +08:00
MistEO
27dd02f4f4 update tasks and config 2021-07-14 17:26:36 +08:00
MistEO
30d714b814 update submodule meojson 2021-07-14 15:23:09 +08:00
MistEO
96793ff73a support empty handle class name 2021-07-14 12:44:41 +08:00
MistEO
e591283dea udpate typos and add log 2021-07-14 12:33:47 +08:00
MistEO
c7ac3f0886 update Readme 2021-07-14 00:45:56 +08:00
MistEO
f6a451b3bf support PTRS error 2021-07-13 23:52:10 +08:00
MistEO
92a531b43a add show and hide window function 2021-07-13 23:10:32 +08:00
MistEO
3a4d2e430d support more simulator 2021-07-13 23:10:15 +08:00
MistEO
6a6302cb93 Merge branch 'develop' 2021-07-13 21:30:02 +08:00
MistEO
2454844735 support wide char handle name 2021-07-13 21:29:45 +08:00
MistEO
fba51db8c8 update handle configer 2021-07-13 16:58:21 +08:00
MistEO
06e8a5e6b8 Merge pull request #1 from MistEO/develop
Develop
2021-07-13 01:14:57 +08:00
MistEO
20e5d32d1a update algorithm and configer 2021-07-13 01:02:37 +08:00
MistEO
0169e2c68d rename namespace asst 2021-07-12 20:32:35 +08:00
MistEO
dba404f33b optimze thread 2021-07-12 20:11:10 +08:00
MistEO
839c97c6cd rename Test to Sanity 2021-07-12 19:56:29 +08:00
MistEO
d31b42fd93 update debugprint 2021-07-12 19:35:38 +08:00
MistEO
c52069d59f Merge branch 'develop' 2021-07-12 19:17:37 +08:00
MistEO
8dd30d406a add *.filters 2021-07-12 19:15:49 +08:00
MistEO
af5fcf53ad update tasks queue. add DebugTrace function 2021-07-12 17:44:15 +08:00
MistEO
102ee1cb5c support to use medicine 2021-07-12 01:22:47 +08:00
MistEO
ba9176af93 update reamde 2021-07-11 23:46:55 +08:00
MistEO
f46e2945dc update reamde 2021-07-11 23:44:37 +08:00
MistEO
22cb7ba745 update release 2021-07-11 22:52:09 +08:00
MistEO
e8d91d3e96 update readme ver_alpha_1 2021-07-11 22:35:58 +08:00
MistEO
b2b4daeeed update scale and resource 2021-07-11 22:19:11 +08:00
MistEO
4090400fa4 update vs solution 2021-07-11 21:00:34 +08:00
MistEO
0937be8c02 fix resizeWindow 2021-07-11 18:49:45 +08:00
MistEO
6b216b0cac add configer function, update identification 2021-07-11 07:44:19 +08:00
35 changed files with 1464 additions and 531 deletions

1
.gitignore vendored
View File

@@ -34,6 +34,5 @@
Debug
Release
.vs
*.vcxproj.filters
*.vcxproj.user
*.swp

View File

@@ -10,6 +10,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MeoAssistance", "MeoAssista
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmeojson", "meojson\test\vsproj\meojson\libmeojson\libmeojson.vcxproj", "{9CC7838E-D5FB-4771-848D-5F83D638C5AD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sanity", "Tools\Sanity\Sanity.vcxproj", "{36BC08F3-71CF-429A-AE69-291BE8962CB2}"
ProjectSection(ProjectDependencies) = postProject
{362D1E30-F5AE-4279-9985-65C27B3BA300} = {362D1E30-F5AE-4279-9985-65C27B3BA300}
{9CC7838E-D5FB-4771-848D-5F83D638C5AD} = {9CC7838E-D5FB-4771-848D-5F83D638C5AD}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -34,6 +40,14 @@ Global
{9CC7838E-D5FB-4771-848D-5F83D638C5AD}.Release|x64.Build.0 = Release|x64
{9CC7838E-D5FB-4771-848D-5F83D638C5AD}.Release|x86.ActiveCfg = Release|Win32
{9CC7838E-D5FB-4771-848D-5F83D638C5AD}.Release|x86.Build.0 = Release|Win32
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Debug|x64.ActiveCfg = Debug|x64
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Debug|x64.Build.0 = Debug|x64
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Debug|x86.ActiveCfg = Debug|Win32
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Debug|x86.Build.0 = Debug|Win32
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Release|x64.ActiveCfg = Release|x64
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Release|x64.Build.0 = Release|x64
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Release|x86.ActiveCfg = Release|Win32
{36BC08F3-71CF-429A-AE69-291BE8962CB2}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,36 +0,0 @@
#pragma once
namespace MeoAssistance {
enum class SimulatorType
{
BlueStacks = 0x100
};
enum class HandleType
{
View = 1,
Control = 2,
Window = 4,
BlueStacksView = 0x100 | 1,
BlueStacksControl = 0x100 | 2,
BlueStacksWindow = 0x100 | 4
};
struct Point
{
Point(int x, int y) : x(x), y(y) {}
int x = 0;
int y = 0;
};
struct Rect
{
Rect(int x, int y, int width, int height)
: x(x), y(y), width(width), height(height) {}
Rect operator*(double rhs) const { return { x, y, static_cast<int>(width * rhs), static_cast<int>(height * rhs) }; }
int x = 0;
int y = 0;
int width = 0;
int height = 0;
};
}

View File

@@ -1,110 +0,0 @@
#include "Assistance.h"
#include "WinMacro.h"
using namespace MeoAssistance;
Assistance::Assistance()
: m_control_thread(control_function, this),
m_identify_thread(identify_function, this)
{
}
Assistance::~Assistance()
{
m_control_exit = true;
m_control_running = false;
m_control_cv.notify_all();
m_identify_exit = true;
m_identify_running = false;
m_identify_cv.notify_all();
if (m_control_thread.joinable()) {
m_control_thread.join();
}
if (m_identify_thread.joinable()) {
m_identify_thread.join();
}
}
bool Assistance::setSimulatorType(SimulatorType type)
{
stop();
std::unique_lock<std::mutex> lock(m_tasks_mutex);
std::queue<Task> empty;
m_tasks.swap(empty);
int int_type = static_cast<int>(type);
m_pCtrl = std::make_shared<WinMacro>(static_cast<HandleType>(int_type | static_cast<int>(HandleType::Control)));
m_pWindow = std::make_shared<WinMacro>(static_cast<HandleType>(int_type | static_cast<int>(HandleType::Window)));
m_pView = std::make_shared<WinMacro>(static_cast<HandleType>(int_type | static_cast<int>(HandleType::View)));
return m_pCtrl->findHandle() && m_pWindow->findHandle() && m_pView->findHandle();
}
void Assistance::start()
{
std::unique_lock<std::mutex> lock(m_tasks_mutex);
m_control_running = true;
m_identify_running = true;
m_control_cv.notify_all();
m_identify_cv.notify_all();
}
void Assistance::stop()
{
std::unique_lock<std::mutex> lock(m_tasks_mutex);
m_control_running = false;
m_identify_running = false;
}
void Assistance::identify_function(Assistance* pThis)
{
while (!pThis->m_identify_exit) {
std::unique_lock<std::mutex> lock(pThis->m_tasks_mutex);
if (pThis->m_identify_running) {
pThis->m_pView->getImage(pThis->m_pView->getWindowRect());
pThis->m_tasks.emplace(Task::StartButton1);
pThis->m_control_cv.notify_all();
pThis->m_identify_cv.wait_for(lock, std::chrono::milliseconds(3000));
}
else {
pThis->m_identify_cv.wait(lock);
}
}
}
void Assistance::control_function(Assistance* pThis)
{
pThis->m_pWindow->resizeWindow(1200, 720);
while (!pThis->m_control_exit) {
std::unique_lock<std::mutex> lock(pThis->m_tasks_mutex);
if (pThis->m_control_running && !pThis->m_tasks.empty()) {
const Task task = pThis->m_tasks.front();
pThis->m_tasks.pop();
lock.unlock();
pThis->run_task(task);
}
else {
pThis->m_control_cv.wait(lock);
}
}
}
bool Assistance::run_task(Task task)
{
switch (task)
{
case MeoAssistance::Assistance::Task::StartButton1:
return m_pCtrl->click({ 1060, 600 });
break;
case MeoAssistance::Assistance::Task::StartButton2:
break;
default:
break;
}
}

View File

@@ -1,52 +0,0 @@
#pragma once
#include <thread>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <queue>
#include "AssDef.h"
namespace MeoAssistance {
class WinMacro;
class Assistance
{
enum class Task {
StartButton1,
StartButton2
};
public:
Assistance();
~Assistance();
bool setSimulatorType(SimulatorType type);
void start();
// void pause();
void stop();
private:
static void identify_function(Assistance* pThis); // 识别线程,生产者
static void control_function(Assistance* pThis); // 控制线程,消费者
bool run_task(Task task);
std::shared_ptr<WinMacro> m_pCtrl = nullptr;
std::shared_ptr<WinMacro> m_pWindow = nullptr;
std::shared_ptr<WinMacro> m_pView = nullptr;
std::queue<Task> m_tasks;
std::mutex m_tasks_mutex;
std::thread m_control_thread;
std::thread m_identify_thread;
bool m_control_exit = false;
bool m_identify_exit = false;
bool m_control_running = false;
bool m_identify_running = false;
std::condition_variable m_control_cv;
std::condition_variable m_identify_cv;
};
}

View File

@@ -1,32 +0,0 @@
#include "Identify.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
using namespace MeoAssistance;
using namespace cv;
double Identify::imgHistComp(const cv::Mat& lhs, const cv::Mat& rhs)
{
cv::Mat lhs_hsv;
cv::Mat rhs_hsv;
cvtColor(lhs, lhs_hsv, COLOR_BGR2HSV);
cvtColor(rhs, rhs_hsv, COLOR_BGR2HSV);
int histSize[] = { 50, 60 };
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
MatND lhs_hist;
MatND rhs_hist;
calcHist(&lhs_hsv, 1, channels, Mat(), lhs_hist, 2, histSize, ranges);
normalize(lhs_hist, lhs_hist, 0, 1, NORM_MINMAX);
calcHist(&rhs_hsv, 1, channels, Mat(), rhs_hist, 2, histSize, ranges);
normalize(rhs_hist, rhs_hist, 0, 1, NORM_MINMAX);
return compareHist(lhs_hist, rhs_hist, CV_COMP_CORREL);
}

View File

@@ -1,17 +0,0 @@
#pragma once
#include <opencv2/opencv.hpp>
namespace MeoAssistance {
class WinMacro;
class Identify
{
public:
Identify() = default;
~Identify() = default;
double imgHistComp(const cv::Mat& lhs, const cv::Mat& rhs);
private:
};
}

View File

@@ -18,6 +18,22 @@
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\AsstDef.h" />
<ClInclude Include="include\Assistance.h" />
<ClInclude Include="include\Configer.h" />
<ClInclude Include="include\Identify.h" />
<ClInclude Include="include\WinMacro.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Assistance.cpp" />
<ClCompile Include="src\Configer.cpp" />
<ClCompile Include="src\Identify.cpp" />
<ClCompile Include="src\WinMacro.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\resource\config.json" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
@@ -40,13 +56,13 @@
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
@@ -78,13 +94,13 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(OPENCV_PATH)\include\opencv2;$(OPENCV_PATH)\include;$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)meojson\test\vsproj\meojson\x64\Debug;$(OPENCV_PATH)\x64\vc15\lib;$(LibraryPath)</LibraryPath>
<IncludePath>$(ProjectDir)include;$(SolutionDir)meojson\include;$(OPENCV_PATH)\include;$(IncludePath)</IncludePath>
<LibraryPath>$(TargetDir);$(OPENCV_PATH)\x64\vc15\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(OPENCV_PATH)\include\opencv2;$(OPENCV_PATH)\include;$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)meojson\test\vsproj\meojson\x64\Release;$(OPENCV_PATH)\x64\vc15\lib;$(LibraryPath)</LibraryPath>
<IncludePath>$(ProjectDir)include;$(SolutionDir)meojson\include;$(OPENCV_PATH)\include;$(IncludePath)</IncludePath>
<LibraryPath>$(TargetDir);$(OPENCV_PATH)\x64\vc15\lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -122,12 +138,25 @@
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>opencv_world452d.lib;libmeojson.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<AdditionalLibraryDirectories>$(TargetDir)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>
</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>xcopy /e /y /i $(SolutionDir)resource $(TargetDir)resource</Command>
</PostBuildEvent>
<PostBuildEvent>
<Message>copy resource</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
@@ -138,6 +167,8 @@
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -145,21 +176,26 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>opencv_world452.lib;libmeojson.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
<AdditionalLibraryDirectories>$(TargetDir)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>
</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>xcopy /e /y /i $(SolutionDir)resource $(TargetDir)resource</Command>
</PostBuildEvent>
<PostBuildEvent>
<Message>copy resource</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AssDef.h" />
<ClInclude Include="Assistance.h" />
<ClInclude Include="Identify.h" />
<ClInclude Include="WinMacro.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Assistance.cpp" />
<ClCompile Include="Identify.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="WinMacro.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties _1_1_4resource_4config_1json__JsonSchema="https://beaujs.com/schema.json" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\Assistance.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Configer.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\Identify.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\WinMacro.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="include\AsstDef.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Assistance.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\Configer.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\Identify.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="src\WinMacro.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\resource\config.json">
<Filter>资源文件</Filter>
</None>
</ItemGroup>
</Project>

View File

@@ -1,207 +0,0 @@
#include "WinMacro.h"
#include <vector>
#include <utility>
#include <ctime>
#include <cassert>
#include <algorithm>
#include <stdint.h>
#include <WinUser.h>
#include <iostream>
using namespace MeoAssistance;
WinMacro::WinMacro(HandleType type)
: m_handle_type(type),
m_rand_engine(time(NULL))
{
}
bool WinMacro::findHandle()
{
switch (m_handle_type) {
case HandleType::BlueStacksControl: {
HWND temp_handle = ::FindWindow(L"BS2CHINAUI", L"BlueStacks App Player");
temp_handle = ::FindWindowEx(temp_handle, NULL, L"BS2CHINAUI", L"HOSTWND");
m_handle = ::FindWindowEx(temp_handle, NULL, L"WindowsForms10.Window.8.app.0.34f5582_r6_ad1", L"BlueStacks Android PluginAndroid");
} break;
case HandleType::BlueStacksView:
case HandleType::BlueStacksWindow:
m_handle = ::FindWindow(L"BS2CHINAUI", L"BlueStacks App Player");
break;
default:
std::cerr << "handle type error! " << static_cast<int>(m_handle_type) << std::endl;
break;
}
#ifdef _DEBUG
std::cout << "type: " << static_cast<int>(m_handle_type) << ", handle: " << m_handle << std::endl;
#endif
if (m_handle != NULL) {
return true;
}
else {
return false;
}
}
bool WinMacro::resizeWindow(int width, int height)
{
if (!(static_cast<int>(m_handle_type) & static_cast<int>(HandleType::Window))) {
return false;
}
return ::MoveWindow(m_handle, 0, 0, width, height, true);
}
double WinMacro::getScreenScale()
{
// 获取窗口当前显示的监视器
// 使用桌面的句柄.
HWND hWnd = GetDesktopWindow();
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
// 获取监视器逻辑宽度与高度
MONITORINFOEX miex;
miex.cbSize = sizeof(miex);
GetMonitorInfo(hMonitor, &miex);
int cxLogical = (miex.rcMonitor.right - miex.rcMonitor.left);
int cyLogical = (miex.rcMonitor.bottom - miex.rcMonitor.top);
// 获取监视器物理宽度与高度
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
EnumDisplaySettings(miex.szDevice, ENUM_CURRENT_SETTINGS, &dm);
int cxPhysical = dm.dmPelsWidth;
int cyPhysical = dm.dmPelsHeight;
// 考虑状态栏大小,逻辑尺寸会比实际小
double horzScale = ((double)cxPhysical / (double)cxLogical);
double vertScale = ((double)cyPhysical / (double)cyLogical);
// 考虑状态栏大小,选择里面大的那个
return std::max(horzScale, vertScale);
}
bool WinMacro::click(Point p)
{
if (!(static_cast<int>(m_handle_type) & static_cast<int>(HandleType::Control))) {
return false;
}
#ifdef _DEBUG
std::cout << "click: " << p.x << ", " << p.y << std::endl;
#endif
LPARAM lparam = MAKELPARAM(p.x, p.y);
::SendMessage(m_handle, WM_LBUTTONDOWN, MK_LBUTTON, lparam);
::SendMessage(m_handle, WM_LBUTTONUP, 0, lparam);
return true;
}
bool WinMacro::clickRange(Rect rect)
{
if (!(static_cast<int>(m_handle_type) & static_cast<int>(HandleType::Control))) {
return false;
}
int x = 0, y = 0;
if (rect.width == 0) {
x = rect.x;
}
else {
std::poisson_distribution<int> x_rand(rect.width);
x = x_rand(m_rand_engine) + rect.x;
}
if (rect.height == 0) {
y = rect.y;
}
else {
std::poisson_distribution<int> y_rand(rect.height);
int y = y_rand(m_rand_engine) + rect.y;
}
return click({ x, y });
}
Rect WinMacro::getWindowRect()
{
RECT rect;
bool ret = ::GetWindowRect(m_handle, &rect);
if (!ret) {
return { 0, 0, 0 ,0 };
}
double scale = getScreenScale();
return Rect{ rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top } * scale;
}
cv::Mat WinMacro::getImage(Rect rect)
{
if (!(static_cast<int>(m_handle_type) & static_cast<int>(HandleType::View))) {
return cv::Mat();
}
HDC pDC;// 源DC
//判断是不是窗口句柄如果是的话不能使用GetDC来获取DC 不然截图会是黑屏
if (m_handle == ::GetDesktopWindow())
{
pDC = CreateDCA("DISPLAY", NULL, NULL, NULL);
}
else
{
pDC = ::GetDC(m_handle);//获取屏幕DC(0为全屏句柄则为窗口)
}
int BitPerPixel = ::GetDeviceCaps(pDC, BITSPIXEL);//获得颜色模式
if (rect.width == 0 && rect.height == 0)//默认宽度和高度为全屏
{
rect.width = ::GetDeviceCaps(pDC, HORZRES); //设置图像宽度全屏
rect.height = ::GetDeviceCaps(pDC, VERTRES); //设置图像高度全屏
}
HDC memDC;//内存DC
memDC = ::CreateCompatibleDC(pDC);
HBITMAP memBitmap, oldmemBitmap;//建立和屏幕兼容的bitmap
memBitmap = ::CreateCompatibleBitmap(pDC, rect.width, rect.height);
oldmemBitmap = (HBITMAP)::SelectObject(memDC, memBitmap);//将memBitmap选入内存DC
if (m_handle == ::GetDesktopWindow())
{
BitBlt(memDC, 0, 0, rect.width, rect.height, pDC, rect.x, rect.y, SRCCOPY);//图像宽度高度和截取位置
}
else
{
bool bret = ::PrintWindow(m_handle, memDC, PW_CLIENTONLY);
if (!bret)
{
BitBlt(memDC, 0, 0, rect.width, rect.height, pDC, rect.x, rect.y, SRCCOPY);//图像宽度高度和截取位置
}
}
BITMAP bmp;
GetObject(memBitmap, sizeof(BITMAP), &bmp);
int nChannels = bmp.bmBitsPixel == 1 ? 1 : bmp.bmBitsPixel / 8;
cv::Mat dst_mat;
dst_mat.create(cv::Size(bmp.bmWidth, bmp.bmHeight), CV_MAKETYPE(CV_8U, nChannels));
GetBitmapBits(memBitmap, bmp.bmHeight * bmp.bmWidth * nChannels, dst_mat.data);
DeleteObject(memBitmap);
DeleteDC(memDC);
ReleaseDC(m_handle, pDC);
#ifdef _DEBUG
// 获取程序当前路径
char curpath[_MAX_PATH] = { 0 };
::GetModuleFileNameA(NULL, curpath, _MAX_PATH);
std::string filename(curpath);
filename = filename.substr(0, filename.find_last_of('\\')) + "\\test.bmp";
cv::imwrite(filename, dst_mat);
#endif
return dst_mat;
}

View File

@@ -1,30 +0,0 @@
#pragma once
#include <string>
#include <random>
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include "AssDef.h"
namespace MeoAssistance {
class WinMacro
{
public:
WinMacro(HandleType type);
~WinMacro() = default;
bool findHandle();
bool resizeWindow(int Width, int Height);
bool click(Point p);
bool clickRange(Rect rect);
cv::Mat getImage(Rect rect);
Rect getWindowRect();
double getScreenScale();
private:
HandleType m_handle_type;
HWND m_handle;
std::minstd_rand m_rand_engine;
};
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <thread>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <optional>
#include "AsstDef.h"
namespace asst {
class WinMacro;
class Identify;
class __declspec(dllexport) Assistance
{
public:
Assistance();
~Assistance();
std::optional<std::string> setSimulator(const std::string & simulator_name = std::string());
void start();
// void pause();
void stop();
private:
static void working_proc(Assistance* pThis);
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_Ider = nullptr;
std::thread m_working_thread;
std::mutex m_mutex;
std::condition_variable m_condvar;
bool m_thread_exit = false;
bool m_thread_running = false;
json::array m_next_tasks;
};
}

View File

@@ -0,0 +1,88 @@
#pragma once
#include "json_value.h"
#include "json_array.h"
#include <mutex>
#include <process.h>
#include <Windows.h>
namespace asst {
enum class HandleType
{
Window = 1,
View = 2,
Control = 4
};
struct Point
{
Point() = default;
Point(int x, int y) : x(x), y(y) {}
int x = 0;
int y = 0;
};
struct Rect
{
Rect() = default;
Rect(int x, int y, int width, int height)
: x(x), y(y), width(width), height(height) {}
Rect operator*(double rhs) const
{
return { x, y, static_cast<int>(width * rhs), static_cast<int>(height * rhs) };
}
Rect center_zoom(double scale)
{
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;
int y = 0;
int width = 0;
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());
}
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);
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");
}
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)...);
}
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include "json_object.h"
namespace asst {
class Configer
{
public:
~Configer() = default;
static bool reload();
static std::string getCurDir();
static std::string getResDir();
static json::object tasksJson;
static json::object handleJson;
static json::object optionsJson;
private:
Configer() = default;
static std::string m_curDir;
};
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "AsstDef.h"
#include <opencv2/opencv.hpp>
#include <unordered_map>
#include <utility>
#include <tuple>
namespace asst {
class WinMacro;
class Identify
{
public:
Identify() = default;
~Identify() = default;
void setUseCache(bool b) noexcept;
bool addImage(const std::string& name, const std::string& path);
// return tuple< algorithmType, suitability, scaled asst::rect>
std::tuple<int, double, asst::Rect> findImage(const cv::Mat& image, const std::string& templ, double threshold = 0.99);
private:
cv::Mat image2Hist(const cv::Mat& src);
double imageHistComp(const cv::Mat& src, const cv::MatND& hist);
asst::Rect cvRect2Rect(const cv::Rect& cvRect) {
return asst::Rect(cvRect.x, cvRect.y, cvRect.width, cvRect.height);
}
// return pair< suitability, raw opencv::point>
std::pair<double, cv::Point> findImage(const cv::Mat& cur, const cv::Mat& templ);
std::unordered_map<std::string, cv::Mat> m_matMap;
bool m_use_cache = true;
std::unordered_map<std::string, std::pair<cv::Rect, cv::Mat>> m_cacheMap;
};
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <random>
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include "AsstDef.h"
namespace asst {
class WinMacro
{
public:
WinMacro(const std::string & simulator_name, HandleType type);
~WinMacro() = default;
bool captured() const noexcept;
bool resizeWindow(int Width, int Height);
bool resizeWindow(); // by configer
bool showWindow();
bool hideWindow();
bool click(const Point & p);
bool clickRange(const Rect & rect);
cv::Mat getImage(const Rect& rect);
Rect getWindowRect();
static double getScreenScale();
private:
bool findHandle();
const std::string m_simulator_name;
const HandleType m_handle_type;
HWND m_handle = NULL;
std::minstd_rand m_rand_engine;
int m_width = 0;
int m_height = 0;
int m_xOffset = 0;
int m_yOffset = 0;
};
}

View File

@@ -1,25 +0,0 @@
#include "Assistance.h"
#include "Identify.h"
#include <iostream>
int main(int argc, char** argv)
{
using namespace MeoAssistance;
Assistance ass;
if (!ass.setSimulatorType(SimulatorType::BlueStacks)) {
std::cerr << "failed" << std::endl;
return -1;
}
std::cout << "start" << std::endl;
ass.start();
getchar();
std::cout << "stop" << std::endl;
ass.stop();
return 0;
}

View File

@@ -0,0 +1,151 @@
#include "Assistance.h"
#include "WinMacro.h"
#include "Configer.h"
#include "Identify.h"
using namespace asst;
Assistance::Assistance()
{
Configer::reload();
m_Ider = std::make_shared<Identify>();
for (auto&& pair : Configer::tasksJson)
{
m_Ider->addImage(pair.first, Configer::getResDir() + pair.second["filename"].as_string());
}
m_Ider->setUseCache(Configer::optionsJson["cache"].as_boolean());
m_working_thread = std::thread(working_proc, this);
}
Assistance::~Assistance()
{
m_pWindow->showWindow();
m_thread_exit = true;
m_thread_running = false;
m_condvar.notify_one();
if (m_working_thread.joinable()) {
m_working_thread.join();
}
}
std::optional<std::string> Assistance::setSimulator(const std::string& simulator_name)
{
stop();
auto create_handles = [&](const std::string name) -> bool {
m_pWindow = std::make_shared<WinMacro>(name, HandleType::Window);
m_pView = std::make_shared<WinMacro>(name, HandleType::View);
m_pCtrl = std::make_shared<WinMacro>(name, HandleType::Control);
return m_pWindow->captured() && m_pView->captured() && m_pCtrl->captured();
};
bool ret = false;
std::string cor_name = simulator_name;
std::unique_lock<std::mutex> lock(m_mutex);
if (simulator_name.empty()) {
for (auto&& [name, value] : Configer::handleJson)
{
ret = create_handles(name);
if (ret) {
cor_name = name;
break;
}
}
}
else {
ret = create_handles(simulator_name);
}
if (ret && m_pWindow->resizeWindow()) {
return cor_name;
}
else {
return std::nullopt;
}
}
void Assistance::start()
{
if (m_thread_running) {
return;
}
std::unique_lock<std::mutex> lock(m_mutex);
m_next_tasks.clear();
m_next_tasks.emplace_back("Begin");
m_thread_running = true;
m_condvar.notify_one();
}
void Assistance::stop()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_thread_running = false;
m_next_tasks.clear();
}
void Assistance::working_proc(Assistance* pThis)
{
while (!pThis->m_thread_exit) {
std::unique_lock<std::mutex> lock(pThis->m_mutex);
if (pThis->m_thread_running) {
auto curImg = pThis->m_pView->getImage(pThis->m_pView->getWindowRect());
std::string matched_task;
Rect matched_rect;
for (auto&& task_jstr : pThis->m_next_tasks) {
std::string task_name = task_jstr.as_string();
double threshold = Configer::tasksJson[task_name]["threshold"].as_double();
auto&& [algorithm, value, rect] = pThis->m_Ider->findImage(curImg, task_name, threshold);
DebugTrace("%-20s %f", task_name.c_str(), value);
if ( algorithm == 0 ||
(algorithm == 1 && value >= threshold)
|| (algorithm == 2 && value >= 0.9999)) {
matched_task = task_name;
matched_rect = rect;
break;
}
}
if (!matched_task.empty()) {
auto task = Configer::tasksJson[matched_task].as_object();
std::string opType = task["type"].as_string();
DebugTraceInfo("Matched: %s, type: %s", matched_task.c_str(), opType.c_str());
if (opType == "clickSelf") {
pThis->m_pCtrl->clickRange(matched_rect);
}
else if (opType == "clickRand") {
pThis->m_pCtrl->clickRange(pThis->m_pCtrl->getWindowRect());
}
else if (opType == "doNothing") {
// do nothing
}
else if (opType == "stop") {
DebugTrace("opType == stop");
pThis->m_thread_running = false;
pThis->m_next_tasks.clear();
continue;
}
else {
DebugTraceError("Unknow option type: %s", opType.c_str());
}
pThis->m_next_tasks = Configer::tasksJson[matched_task]["next"].as_array();
DebugTrace("Next: %s", pThis->m_next_tasks.to_string().c_str());
}
pThis->m_condvar.wait_for(lock, std::chrono::milliseconds(Configer::optionsJson["delay"]["fixedTime"].as_integer()));
}
else {
pThis->m_condvar.wait(lock);
}
}
}

View File

@@ -0,0 +1,57 @@
#include "Configer.h"
#include <fstream>
#include <sstream>
#include <Windows.h>
#include "json.h"
using namespace asst;
json::object Configer::tasksJson;
json::object Configer::handleJson;
json::object Configer::optionsJson;
std::string Configer::m_curDir;
bool Configer::reload()
{
std::string filename = getResDir() + "config.json";
std::ifstream ifs(filename, std::ios::in);
if (!ifs.is_open()) {
return false;
}
std::stringstream iss;
iss << ifs.rdbuf();
ifs.close();
std::string content(iss.str());
auto ret = json::parser::parse(content);
if (!ret) {
return false;
}
auto root = std::move(ret).value();
tasksJson = root["tasks"].as_object();
handleJson = root["handle"].as_object();
optionsJson = root["options"].as_object();
return true;
}
std::string Configer::getCurDir()
{
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);
}
return m_curDir;
}
std::string Configer::getResDir()
{
return getCurDir() + "resource\\";
}

View File

@@ -0,0 +1,105 @@
#include "Identify.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
using namespace asst;
using namespace cv;
bool Identify::addImage(const std::string& name, const std::string& path)
{
Mat mat = imread(path);
if (mat.empty()) {
return false;
}
m_matMap.emplace(name, mat);
return true;
}
void Identify::setUseCache(bool b) noexcept
{
if (b) {
m_use_cache = true;
}
else {
m_cacheMap.clear();
m_use_cache = false;
}
}
Mat Identify::image2Hist(const cv::Mat& src)
{
Mat src_hsv;
cvtColor(src, src_hsv, COLOR_BGR2HSV);
int histSize[] = { 50, 60 };
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
MatND src_hist;
calcHist(&src_hsv, 1, channels, Mat(), src_hist, 2, histSize, ranges);
normalize(src_hist, src_hist, 0, 1, NORM_MINMAX);
return src_hist;
}
double Identify::imageHistComp(const cv::Mat& src, const cv::MatND& hist)
{
return compareHist(image2Hist(src), hist, CV_COMP_CORREL);
}
std::pair<double, cv::Point> Identify::findImage(const cv::Mat& image, const cv::Mat& templ)
{
Mat image_hsv;
Mat templ_hsv;
cvtColor(image, image_hsv, COLOR_BGR2HSV);
cvtColor(templ, templ_hsv, COLOR_BGR2HSV);
Mat matched;
matchTemplate(image_hsv, templ_hsv, matched, cv::TM_CCORR_NORMED);
double minVal = 0, maxVal = 0;
cv::Point minLoc, maxLoc;
minMaxLoc(matched, &minVal, &maxVal, &minLoc, &maxLoc);
return { maxVal, maxLoc };
}
std::tuple<int, double, asst::Rect> Identify::findImage(const Mat& cur, const std::string& templ, double threshold)
{
if (m_matMap.find(templ) == m_matMap.cend()) {
return { 0, 0, asst::Rect() };
}
if (m_use_cache && m_cacheMap.find(templ) != m_cacheMap.cend()) {
DebugTrace("Identify | %s get cache", templ.c_str());
auto&& [rect, hist] = m_cacheMap.at(templ);
double value = imageHistComp(cur(rect), hist);
return { 2, value, cvRect2Rect(rect).center_zoom(0.8) };
}
else {
auto&& templ_mat = m_matMap.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) {
DebugTrace("Identify | %s add to cache", templ.c_str());
m_cacheMap.emplace(templ, std::make_pair(raw_rect, image2Hist(cur(raw_rect))));
}
return { 1, value, cvRect2Rect(raw_rect).center_zoom(0.8) };
}
}
/*
std::pair<double, asst::Rect> Identify::findImageWithFile(const cv::Mat& cur, const std::string& filename)
{
Mat mat = imread(filename);
if (mat.empty()) {
return { 0, asst::Rect() };
}
return findImage(cur, mat);
}
*/

View File

@@ -0,0 +1,250 @@
#include "WinMacro.h"
#include <vector>
#include <utility>
#include <ctime>
#include <algorithm>
#include <stdint.h>
#include <WinUser.h>
#include "Configer.h"
#include "AsstDef.h"
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))
{
findHandle();
}
bool WinMacro::captured() const noexcept
{
return m_handle != NULL;
}
bool WinMacro::findHandle()
{
json::array handle_arr;
json::value simulator_json = Configer::handleJson[m_simulator_name];
switch (m_handle_type) {
case HandleType::Window:
m_width = simulator_json["Width"].as_integer();
m_height = simulator_json["Height"].as_integer();
handle_arr = simulator_json["Window"].as_array();
break;
case HandleType::View:
handle_arr = simulator_json["View"].as_array();
break;
case HandleType::Control:
m_xOffset = simulator_json["xOffset"].as_integer();
m_yOffset = simulator_json["yOffset"].as_integer();
handle_arr = simulator_json["Control"].as_array();
break;
default:
DebugTraceError("Handle type error!: %d", m_handle_type);
return false;
}
m_handle = NULL;
for (auto&& obj : handle_arr)
{
wchar_t* class_wbuff = NULL;
std::string class_str = obj["class"].as_string();
if (!class_str.empty()) {
size_t class_len = (class_str.size() + 1) * 2;
class_wbuff = new wchar_t[class_len];
::MultiByteToWideChar(CP_UTF8, 0, class_str.c_str(), -1, class_wbuff, class_len);
}
wchar_t* window_wbuff = NULL;
std::string window_str = obj["window"].as_string();
if (!window_str.empty()) {
size_t window_len = (window_str.size() + 1) * 2;
window_wbuff = new wchar_t[window_len];
memset(window_wbuff, 0, window_len);
::MultiByteToWideChar(CP_UTF8, 0, window_str.c_str(), -1, window_wbuff, window_len);
}
m_handle = ::FindWindowExW(m_handle, NULL, class_wbuff, window_wbuff);
if (class_wbuff != NULL) {
delete[] class_wbuff;
class_wbuff = NULL;
}
if (window_wbuff != NULL) {
delete[] window_wbuff;
window_wbuff = NULL;
}
}
DebugTrace("Handle: 0x%x, Name: %s, Type: %d", m_handle, m_simulator_name.c_str(), m_handle_type);
if (m_handle != NULL) {
return true;
}
else {
return false;
}
}
bool WinMacro::resizeWindow(int width, int height)
{
if (m_handle_type != HandleType::Window) {
return false;
}
return ::MoveWindow(m_handle, 0, 0, width / getScreenScale(), height / getScreenScale(), true);
}
bool WinMacro::resizeWindow()
{
return resizeWindow(m_width, m_height);
}
bool WinMacro::showWindow()
{
if (m_handle_type != HandleType::Window) {
return false;
}
return ::ShowWindow(m_handle, SW_SHOW);
}
bool WinMacro::hideWindow()
{
if (m_handle_type != HandleType::Window) {
return false;
}
return ::ShowWindow(m_handle, SW_HIDE);
}
double WinMacro::getScreenScale()
{
static double scale = 0;
if (scale == 0) {
// 获取窗口当前显示的监视器
// 使用桌面的句柄.
HWND hWnd = GetDesktopWindow();
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
// 获取监视器逻辑宽度与高度
MONITORINFOEX miex;
miex.cbSize = sizeof(miex);
GetMonitorInfo(hMonitor, &miex);
int cxLogical = (miex.rcMonitor.right - miex.rcMonitor.left);
int cyLogical = (miex.rcMonitor.bottom - miex.rcMonitor.top);
// 获取监视器物理宽度与高度
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
EnumDisplaySettings(miex.szDevice, ENUM_CURRENT_SETTINGS, &dm);
int cxPhysical = dm.dmPelsWidth;
int cyPhysical = dm.dmPelsHeight;
// 考虑状态栏大小,逻辑尺寸会比实际小
double horzScale = ((double)cxPhysical / (double)cxLogical);
double vertScale = ((double)cyPhysical / (double)cyLogical);
// 考虑状态栏大小,选择里面大的那个
scale = std::max(horzScale, vertScale);
}
return scale;
}
bool WinMacro::click(const Point& p)
{
if (m_handle_type != HandleType::Control) {
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);
::SendMessage(m_handle, WM_LBUTTONDOWN, MK_LBUTTON, lparam);
::SendMessage(m_handle, WM_LBUTTONUP, 0, lparam);
return true;
}
bool WinMacro::clickRange(const Rect& rect)
{
if (m_handle_type != HandleType::Control) {
return false;
}
int x = 0, y = 0;
if (rect.width == 0) {
x = rect.x;
}
else {
int x_rand = std::poisson_distribution<int>(rect.width / 2)(m_rand_engine);
x = x_rand + rect.x;
}
if (rect.height == 0) {
y = rect.y;
}
else {
int y_rand = std::poisson_distribution<int>(rect.height / 2)(m_rand_engine);
y = y_rand + rect.y;
}
return click({ x, y });
}
Rect WinMacro::getWindowRect()
{
RECT rect;
bool ret = ::GetWindowRect(m_handle, &rect);
if (!ret) {
return Rect();
}
return Rect{ rect.left, rect.top,
static_cast<int>((rect.right - rect.left) * getScreenScale()),
static_cast<int>((rect.bottom - rect.top) * getScreenScale()) };
}
cv::Mat WinMacro::getImage(const Rect& rect)
{
if (m_handle_type != HandleType::View) {
return cv::Mat();
}
HDC pDC;// 源DC
pDC = ::GetDC(m_handle);//获取屏幕DC(0为全屏句柄则为窗口)
int BitPerPixel = ::GetDeviceCaps(pDC, BITSPIXEL);//获得颜色模式
HDC memDC;//内存DC
memDC = ::CreateCompatibleDC(pDC);
HBITMAP memBitmap, oldmemBitmap;//建立和屏幕兼容的bitmap
memBitmap = ::CreateCompatibleBitmap(pDC, rect.width, rect.height);
oldmemBitmap = (HBITMAP)::SelectObject(memDC, memBitmap);//将memBitmap选入内存DC
::PrintWindow(m_handle, memDC, PW_CLIENTONLY);
BITMAP bmp;
GetObject(memBitmap, sizeof(BITMAP), &bmp);
int nChannels = bmp.bmBitsPixel == 1 ? 1 : bmp.bmBitsPixel / 8;
cv::Mat dst_mat;
dst_mat.create(cv::Size(bmp.bmWidth, bmp.bmHeight), CV_MAKETYPE(CV_8U, nChannels));
GetBitmapBits(memBitmap, bmp.bmHeight * bmp.bmWidth * nChannels, dst_mat.data);
DeleteObject(memBitmap);
DeleteDC(memDC);
ReleaseDC(m_handle, pDC);
#ifdef _DEBUG
std::string filename = Configer::getCurDir() + "\\test.bmp";
cv::imwrite(filename, dst_mat);
#endif
return dst_mat;
}

View File

@@ -2,6 +2,85 @@
A game assistance for Arknights
一款明日方舟的游戏辅助,目前仅支持蓝叠模拟器
一款明日方舟的游戏辅助,供PC端安卓模拟器使用龟速开发中……
使用C++ WinAPI龟速开发中……
## 功能介绍
- 目前版本支持自动刷完所有体力+自动吃完体力药
- 刷完后程序会自动停止
- 代理失败会自动放弃本次行动
- 所有操作,都是点击按钮内随机位置,且模拟泊松分布,不会像鼠标宏一样一直是同一个点,没有封号风险
~~(虽然好像也没听说过谁用鼠标宏被封号的)~~
- 模拟器窗口可以被遮挡,即使全屏看视频、玩游戏,也完全不影响辅助运行
~~(但是模拟器还是不能最小化)~~
- 支持多款主流模拟器
- 未来更多功能见[Todo](#Todo)
### 模拟器支持
#### 蓝叠模拟器
完美兼容,作者绝大部分测试均在蓝叠上进行,稳定性最能保障
#### 逍遥模拟器
完美兼容
#### 雷电模拟器
完美兼容,需要收起右侧侧边栏后使用
#### 夜神模拟器
兼容,但辅助开始后不可操作模拟器窗口
#### 腾讯手游助手
兼容但需要手动设置分辨率设置中心——引擎设置——分辨率设置——1280x720——保存后重启模拟器
#### MuMu手游助手 && MuMu模拟器
不兼容MuMu所有的窗口句柄均不响应SendMessage鼠标消息但官方提供了adb的方式进行控制有时间再做
## 使用说明
1. 使用蓝叠模拟器打开明日方舟,进入有**蓝色的开始行动按钮**的界面,勾上代理指挥
2. 解压压缩包,**使用管理员权限**,打开"Sanity.exe"
3. 目前只有最基本的功能,刷完体力+体力药就会自动停了……
## Todo
~~在做了在做了.jpg~~
- [ ] 功能
- [ ] 图形化界面
- [ ] 刷理智
- [x] 支持剿灭
- [ ] 支持刷指定次数
- [x] 支持使模拟器窗口不可见
- [x] 自动吃体力药
- [ ] 自动吃石头(根据设置,指定数量)
- [x] 代理失败的情况
- [x] 支持等级提升
- [ ] 支持凌晨4点更新数据
- [ ] 结束界面自动截图,可用于上传企鹅物流
- [ ] 持续监视理智,一有就自动刷掉
- [x] 支持更多模拟器
- [ ] 信用访问
- [ ] 基建收菜
- [ ] 当前公招可能干员一览
- [x] 算法
- [x] 更换算法为模板匹配找图,而不是当前的区域相似度对比
- [x] 优化算法效率,添加缓存功能
- [x] 优化任务队列,减少不必要的计算
- [ ] 其他
- [ ] 尝试减小程序体积
## 致谢
感谢以下开源库/API
- WinAPI
- OpenCV
- MeoJson

161
Tools/Sanity/Sanity.vcxproj Normal file
View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{36bc08f3-71cf-429a-ae69-291be8962cb2}</ProjectGuid>
<RootNamespace>Test</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>Sanity</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(SolutionDir)meojson\include;$(SolutionDir)MeoAssistance\include;$(IncludePath)</IncludePath>
<LibraryPath>$(TargetDir);$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir)meojson\include;$(SolutionDir)MeoAssistance\include;$(IncludePath)</IncludePath>
<LibraryPath>$(TargetDir);$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>MeoAssistance.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(TargetDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>MeoAssistance.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(TargetDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>

28
Tools/Sanity/main.cpp Normal file
View File

@@ -0,0 +1,28 @@
#include "Assistance.h"
int main(int argc, char** argv)
{
using namespace asst;
Assistance asst;
auto ret = asst.setSimulator();
if (!ret) {
DebugTraceError("Can't Find Simulator or Permission denied.");
getchar();
return -1;
}
else {
DebugTraceInfo("Find Simulator: %s", ret->c_str());
}
DebugTraceInfo("Start");
asst.start();
getchar();
DebugTraceInfo("Stop");
asst.stop();
return 0;
}

Submodule meojson updated: 0a86b19d2a...b530ddcffa

BIN
resource/AbandonAction.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
resource/PRTS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

BIN
resource/StartButton1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
resource/StartButton2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
resource/UseMedicine.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
resource/UseStone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

254
resource/config.json Normal file
View File

@@ -0,0 +1,254 @@
{
"version": 0.2,
"options": {
"delay": {
"type": "fixedTime",
"fixedTime": 2000
},
"cache": true
},
"handle": {
"BlueStacks": {
"Control": [
{
"class": "BS2CHINAUI",
"window": "BlueStacks App Player"
},
{
"class": "BS2CHINAUI",
"window": "HOSTWND"
},
{
"class": "",
"window": "BlueStacks Android PluginAndroid"
}
],
"View": [
{
"class": "BS2CHINAUI",
"window": "BlueStacks App Player"
}
],
"Window": [
{
"class": "BS2CHINAUI",
"window": "BlueStacks App Player"
}
],
"Width": 1294,
"Height": 773,
"xOffset": -7,
"yOffset": -47
},
"Nox": {
"Control": [
{
"class": "Qt5QWindowIcon",
"window": "夜神模拟器"
}
],
"View": [
{
"class": "Qt5QWindowIcon",
"window": "夜神模拟器"
}
],
"Window": [
{
"class": "Qt5QWindowIcon",
"window": "夜神模拟器"
}
],
"Width": 1298,
"Height": 754,
"xOffset": 0,
"yOffset": -32
},
"LDPlayer": {
"Control": [
{
"class": "LDPlayerMainFrame",
"window": "雷电模拟器"
},
{
"class": "RenderWindow",
"window": "TheRender"
}
],
"View": [
{
"class": "LDPlayerMainFrame",
"window": "雷电模拟器"
}
],
"Window": [
{
"class": "LDPlayerMainFrame",
"window": "雷电模拟器"
}
],
"Width": 1304,
"Height": 756,
"xOffset": 0,
"yOffset": -35
},
"XYAZ": {
"Control": [
{
"class": "Qt5QWindowIcon",
"window": "逍遥模拟器"
},
{
"class": "Qt5QWindowIcon",
"window": "MainWindowWindow"
}
],
"View": [
{
"class": "Qt5QWindowIcon",
"window": "逍遥模拟器"
}
],
"Window": [
{
"class": "Qt5QWindowIcon",
"window": "逍遥模拟器"
}
],
"Width": 1282,
"Height": 769,
"xOffset": 0,
"yOffset": -47
},
"Tencent": {
"Control": [
{
"class": "TXGuiFoundation",
"window": "腾讯手游助手"
},
{
"class": "AEngineRenderWindowClass",
"window": "AEngineRenderWindow"
}
],
"View": [
{
"class": "TXGuiFoundation",
"window": "腾讯手游助手"
}
],
"Window": [
{
"class": "TXGuiFoundation",
"window": "腾讯手游助手"
}
],
"Width": 1282,
"Height": 769,
"xOffset": 0,
"yOffset": -42
}
},
"tasks": {
"Begin": {
"filename": "",
"threshold": 0,
"type": "doNothing",
"next": [
"StartButton1",
"StartButton2",
"PRTS",
"UseMedicine",
"UseStone",
"PrtsErrorConfirm",
"Random"
]
},
"StartButton1": {
"filename": "StartButton1.png",
"threshold": 0.99,
"type": "clickSelf",
"next": [
"StartButton2",
"UseMedicine",
"UseStone"
]
},
"StartButton2": {
"id": 2,
"filename": "StartButton2.png",
"threshold": 0.99,
"type": "clickSelf",
"next": [
"PRTS"
]
},
"PRTS": {
"filename": "PRTS.png",
"threshold": 0.98,
"type": "doNothing",
"next": [
"PRTS",
"PrtsErrorConfirm",
"Random"
]
},
"Random": {
"filename": "",
"threshold": 0,
"type": "clickRand",
"next": [
"StartButton1",
"Random"
]
},
"UseMedicine": {
"filename": "UseMedicine.png",
"threshold": 0.99,
"type": "doNothing",
"next": [
"MedicineConfirm"
]
},
"MedicineConfirm": {
"filename": "MedicineConfirm.png",
"threshold": 0.98,
"type": "clickSelf",
"next": [
"StartButton1"
]
},
"UseStone": {
"filename": "UseStone.png",
"threshold": 0.99,
"type": "stop",
"next": [
"StoneConfirm"
]
},
"StoneConfirm": {
"filename": "MedicineConfirm.png",
"threshold": 0.98,
"type": "clickSelf",
"next": [
"StartButton1"
]
},
"PrtsErrorConfirm": {
"filename": "PrtsErrorConfirm.png",
"threshold": 0.98,
"type": "doNothing",
"next": [
"AbandonAction"
]
},
"AbandonAction": {
"filename": "AbandonAction.png",
"threshold": 0.98,
"type": "clickSelf",
"next": [
"Random"
]
}
}
}