From 84795aa7253b13a1561cb4fa30f5f78fb5227484 Mon Sep 17 00:00:00 2001 From: mrbesen Date: Sun, 6 Mar 2022 01:00:19 +0100 Subject: [PATCH] only screenshot launcher window; organized --- include/lolautoaccept.h | 41 ++++++++++++ include/matcher.h | 10 +++ include/scaleableinputs.h | 29 ++++++++ include/screen.h | 39 +++++++++-- include/util.h | 5 ++ src/lolautoaccept.cpp | 131 ++++++++++++++++++++++++++++++++++++ src/main.cpp | 76 ++++----------------- src/matcher.cpp | 2 +- src/scaleableinputs.cpp | 20 ++++++ src/screen.cpp | 136 ++++++++++++++++++++++++++++++-------- 10 files changed, 392 insertions(+), 97 deletions(-) create mode 100644 include/lolautoaccept.h create mode 100644 include/scaleableinputs.h create mode 100644 include/util.h create mode 100644 src/lolautoaccept.cpp create mode 100644 src/scaleableinputs.cpp diff --git a/include/lolautoaccept.h b/include/lolautoaccept.h new file mode 100644 index 0000000..dcc2bf4 --- /dev/null +++ b/include/lolautoaccept.h @@ -0,0 +1,41 @@ +#pragma once + +#include "scaleableinputs.h" +#include "screen.h" +#include "matcher.h" + +#include + +class LolAutoAccept { +private: + ScreenShot* screen; + Matcher acceptmatcher; + Matcher arrowmatcher; + Matcher::Match lastacceptmatch; + XInputSimulator& sim; + + ScaleableInputs inputs; + + std::string prepick; + std::string ban; + std::string pick; + + enum class State { + LOBBY, + PREPICK, + BAN, + PICK, + GAME + }; + + State state = State::LOBBY; + + void checkForGame(); + void performClick(uint32_t nr); + void enterSearch(const std::string& text); + void pickFirst(const std::string& search); +public: + LolAutoAccept(); + + void run(); +}; \ No newline at end of file diff --git a/include/matcher.h b/include/matcher.h index 3d2cda0..ffc6e9e 100644 --- a/include/matcher.h +++ b/include/matcher.h @@ -19,6 +19,16 @@ public: bool operator==(const Match&) const; bool operator!=(const Match&) const; + + + // get the center of the button + constexpr int getButtonX() const { + return x + (width/2); + } + + constexpr int getButtonY() const { + return y + (height/2); + } }; Match match(const cv::Mat& img); diff --git a/include/scaleableinputs.h b/include/scaleableinputs.h new file mode 100644 index 0000000..3cad328 --- /dev/null +++ b/include/scaleableinputs.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +class ScaleableInputs { +public: + struct Point { + uint32_t x; + uint32_t y; + }; + +private: + double xScale = 1; + double yScale = 1; + double xOffset = 0; + double yOffset = 0; + + std::vector points; + +public: + + void addPoint(Point p); + void setScale(double x, double y); + void setOffset(double x, double y); + + Point get(uint32_t nr) const; + +}; \ No newline at end of file diff --git a/include/screen.h b/include/screen.h index 52ba280..6cb340c 100644 --- a/include/screen.h +++ b/include/screen.h @@ -12,20 +12,49 @@ class ScreenShot { private: Display* display = nullptr; - Window root; - XWindowAttributes window_attributes; + Window window; + XWindowAttributes wattrib; XImage* ximg = nullptr; XShmSegmentInfo shminfo; - int x, y, width, height; - bool init; + bool closeDisp = true; + bool valid = false; + std::string windowname; + bool updateAttrib(); + bool initImg(); public: + static std::vector getWindows(const std::string& name); + + static const uint32_t DEFAULTWIDTH; + static const uint32_t DEFAULTHEIGHT; + ScreenShot(); + ScreenShot(Display* d, Window w); ~ScreenShot(); + void take(cv::Mat& cvimg); void operator() (cv::Mat& cvimg); -}; \ No newline at end of file + constexpr operator bool () const { + return valid; + } + + constexpr int getXOffset() const { + return wattrib.x; + } + + constexpr int getYOffset() const { + return wattrib.y; + } + + constexpr double getXScale() const { + return wattrib.width / DEFAULTWIDTH; + } + + constexpr double getYScale() const { + return wattrib.height / DEFAULTHEIGHT; + } +}; diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..85819ee --- /dev/null +++ b/include/util.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void debugImage(cv::Mat img, const std::string& name = ""); \ No newline at end of file diff --git a/src/lolautoaccept.cpp b/src/lolautoaccept.cpp new file mode 100644 index 0000000..b1bc781 --- /dev/null +++ b/src/lolautoaccept.cpp @@ -0,0 +1,131 @@ +#include "lolautoaccept.h" + +#include +#include + +#include "util.h" + +void debugImage(cv::Mat img, const std::string& name) { + if(img.channels() > 3) { + std::vector channels(4); + cv::split(img, channels); + channels.resize(3); // drop alpha channel + cv::merge(channels, img); + } + + time_t t = time(0); + cv::imwrite("debugimages/" + name + std::to_string(t) + ".png", img); +} + +void LolAutoAccept::checkForGame() { + cv::Mat img; + screen->take(img); + + cv::resize(img, img, cv::Size(ScreenShot::DEFAULTWIDTH, ScreenShot::DEFAULTHEIGHT)); + + Matcher::Match mat = acceptmatcher.match(img); + + if(mat.doesMatch) { + Log::info << "matched"; + cv::rectangle(img, cv::Point(mat.x, mat.y), cv::Point( mat.x + mat.width , mat.y + mat.height ), cv::Scalar(0, 0, 255, 0), 2); + debugImage(img); + + if(lastacceptmatch.doesMatch) { + Log::info << "second match"; + + if(lastacceptmatch != mat) { + Log::warn << "not same match!"; + lastacceptmatch.doesMatch = false; + return; + } + + performClick(0); // accept Game + + // security sleep + std::this_thread::sleep_for(std::chrono::seconds(3)); + } else { + Log::info << "first match"; + } + } else { + // check if next stage is here + Matcher::Match mat = arrowmatcher.match(img); + if(mat.doesMatch) { + Log::info << "Pre pick state"; + + if(!prepick.empty()) + pickFirst(prepick); + + state = State::PREPICK; + } + } + lastacceptmatch = mat; +} + +void LolAutoAccept::performClick(uint32_t nr) { + inputs.setOffset(screen->getXOffset(), screen->getYOffset()); + inputs.setScale(1 / screen->getXScale(), 1 / screen->getYScale()); + + auto p = inputs.get(nr); + Log::info << "click " << nr << " @ " << p.x << " " << p.y; + + sim.mouseMoveTo(p.x, p.y); + + std::this_thread::sleep_for(std::chrono::milliseconds(120)); + sim.mouseClick(XIS::LEFT_MOUSE_BUTTON); + + // move mouse away + std::this_thread::sleep_for(std::chrono::milliseconds(120)); + sim.mouseMoveTo(0, 0); +} + +void LolAutoAccept::enterSearch(const std::string& text) { + performClick(1); // click searchbox + + Log::debug << "enter text: " << text; + + sim.keySequence(text); + std::this_thread::sleep_for(std::chrono::milliseconds(180)); +} + +void LolAutoAccept::pickFirst(const std::string& search) { + enterSearch(search); + + performClick(2); // first champion +} + +LolAutoAccept::LolAutoAccept() : acceptmatcher("imgs/Accept.png"), arrowmatcher("imgs/arrowdown.png"), sim(XInputSimulator::getInstance()) { + inputs.addPoint({645, 560}); // accept game + inputs.addPoint({775, 105}); // search box + inputs.addPoint({180, 160}); // first champ +} + +void LolAutoAccept::run() { + // get window + auto wins = ScreenShot::getWindows("League of Legends"); + if(wins.size() != 1) { + Log::fatal << "invalid count of windows found: " << wins.size(); + return; + } + + screen = (wins.at(0)); // just take the first + + //delete other screens + for(uint32_t i = 1; i < wins.size(); ++i) { + ScreenShot* ss = wins.at(i); + delete ss; + } + + lastacceptmatch = {false}; + while(true) { + switch(state) { + case State::LOBBY: + checkForGame(); + break; + default: break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(800)); + } + + delete wins.at(0); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0d5eaf6..fa8b09c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,24 +1,6 @@ +#include "lolautoaccept.h" -#include "Log.h" - -#include "screen.h" -#include "matcher.h" - -#include - -#include - -static void debugImage(cv::Mat img) { - if(img.channels() > 3) { - std::vector channels(4); - cv::split(img, channels); - channels.resize(3); // drop alpha channel - cv::merge(channels, img); - } - - time_t t = time(0); - cv::imwrite("debugimages/" + std::to_string(t) + ".png", img); -} +#include int main(int argc, const char** argv) { Log::init(); @@ -30,53 +12,21 @@ int main(int argc, const char** argv) { Log::info << "Hello, World!"; - // load template - Matcher matcher("imgs/Accept.png"); + Matcher test1("imgs/arrowdown.png"); + Matcher test2("imgs/blitzcranktest.png"); + auto testimg = cv::imread("imgs/Bildschirmfoto vom 2022-02-28 19-27-59.png"); + auto testimg2 = cv::imread("imgs/Bildschirmfoto vom 2022-02-28 19-27-44.png"); + test1.match(testimg); + test2.match(testimg); - ScreenShot screen; + test1.match(testimg2); + test2.match(testimg2); - XInputSimulator& sim = XInputSimulator::getInstance(); + return 0; - Matcher::Match lastmatch = {false}; - while(true) { - cv::Mat img; - screen(img); - - Matcher::Match mat = matcher.match(img); - - if(mat.doesMatch) { - Log::info << "matched"; - cv::rectangle(img, cv::Point(mat.x, mat.y), cv::Point( mat.x + mat.width , mat.y + mat.height ), cv::Scalar(0, 0, 255, 0), 2); - debugImage(img); - - if(lastmatch.doesMatch) { - Log::info << "second match"; - - if(lastmatch != mat) { - Log::warn << "not same match!"; - lastmatch.doesMatch = false; - continue; - } - - int x = mat.x + (mat.width/2); - int y = mat.y + (mat.height/2); - Log::info << "click"; - sim.mouseMoveTo(x, y); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - sim.mouseClick(XIS::LEFT_MOUSE_BUTTON); - - // security sleep - std::this_thread::sleep_for(std::chrono::seconds(10)); - } else { - Log::info << "first match"; - } - } - lastmatch = mat; - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - } + LolAutoAccept lolaa; + lolaa.run(); Log::stop(); return 0; diff --git a/src/matcher.cpp b/src/matcher.cpp index bf1d54a..eead0fd 100644 --- a/src/matcher.cpp +++ b/src/matcher.cpp @@ -40,7 +40,7 @@ Matcher::Match Matcher::match(const cv::Mat& img) { cv::Point minLoc; cv::Point maxLoc; cv::minMaxLoc( out, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat() ); - Log::debug << "minValbn: " << minValbn << " maxValbn: " << maxValbn << " minVal: " << minVal << " maxVal: " << maxVal << " minLoc: " << minLoc.x << " " << minLoc.y << " maxLoc: " << maxLoc.x << " " << maxLoc.y; + Log::debug << "pixelcount: " << (templ.cols * templ.rows) << " minValbn: " << minValbn << " maxValbn: " << maxValbn << " minVal: " << minVal << " maxVal: " << maxVal << " minLoc: " << minLoc.x << " " << minLoc.y << " maxLoc: " << maxLoc.x << " " << maxLoc.y; bool matched = minValbn < 2e7; diff --git a/src/scaleableinputs.cpp b/src/scaleableinputs.cpp new file mode 100644 index 0000000..90f853a --- /dev/null +++ b/src/scaleableinputs.cpp @@ -0,0 +1,20 @@ +#include "scaleableinputs.h" + +void ScaleableInputs::addPoint(Point p) { + points.push_back(p); +} + +void ScaleableInputs::setScale(double x, double y) { + xScale = x; + yScale = y; +} + +void ScaleableInputs::setOffset(double x, double y) { + xOffset = x; + yOffset = y; +} + +ScaleableInputs::Point ScaleableInputs::get(uint32_t nr) const { + Point p = points.at(nr); + return {(uint32_t) ((p.x * xScale) + xOffset), (uint32_t) ((p.y * yScale) + yOffset)}; +} \ No newline at end of file diff --git a/src/screen.cpp b/src/screen.cpp index 8806c9a..906a0cf 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -4,15 +4,103 @@ #include "screen.h" +#include +#include + +const uint32_t ScreenShot::DEFAULTWIDTH = 1280; +const uint32_t ScreenShot::DEFAULTHEIGHT = 720; + // https://stackoverflow.com/a/39781697/4399859 +static void searchWindows(Display* disp, Window top, const std::string& search, std::vector& result) { + + char* window_name; + if (XFetchName(disp, top, &window_name)) { + if (search == window_name) { + result.push_back(top); + return; // dont look for kids + } + } + + Window* children = nullptr, dummy; + unsigned int nchildren; + if (!XQueryTree(disp, top, &dummy, &dummy, &children, &nchildren)) + return; + + for (unsigned int i = 0; i < nchildren; i++) { + searchWindows(disp, children[i], search, result); + } + if (children) + XFree((char*) children); +} + +std::vector ScreenShot::getWindows(const std::string& name) { + Display* display = XOpenDisplay(nullptr); + + Log::info << "searching for: " << name; + + std::vector list; + searchWindows(display, DefaultRootWindow(display), name, list); + std::vector out; + + out.reserve(list.size()); + for (Window w : list) { + ScreenShot* ss = new ScreenShot(display, w); + if(ss->valid) + out.push_back(ss); + } + + return out; +} + +bool ScreenShot::updateAttrib() { + Status attrb = XGetWindowAttributes(display, window, &wattrib); + + if (attrb == BadWindow || attrb == BadDrawable) { + Log::error << "XGetWindowAttributes() falied"; + return false; + } + + if((wattrib.c_class != InputOutput) || (wattrib.map_state != IsViewable)) { + Log::warn << "Window is not viewable - window class: " << wattrib.c_class << " map state: " << wattrib.map_state << " - ignoring window"; + return false; + } + + return true; +} + +bool ScreenShot::initImg() { + bool upd = updateAttrib(); + if(!upd) return false; + + char* wname = nullptr; + XFetchName(display, window, &wname); + if (wname) + windowname = std::string(wname); + + Log::info << "Window " << std::quoted(windowname) << " x: " << wattrib.x << " y: " << wattrib.y << " w: " << wattrib.width << " h: " << wattrib.height; + + ximg = XShmCreateImage(display, wattrib.visual, wattrib.depth, ZPixmap, NULL, &shminfo, wattrib.width, wattrib.height); + + shminfo.shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT | 0777); + shminfo.shmaddr = ximg->data = (char*) shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = false; + if (shminfo.shmid < 0) + Log::fatal << "Fatal shminfo error!"; + Status s1 = XShmAttach(display, &shminfo); + Log::info << "XShmAttach() " << (s1 ? "success!" : "failure!"); + + init = true; + return true; +} + ScreenShot::ScreenShot() { display = XOpenDisplay(nullptr); - root = DefaultRootWindow(display); + window = DefaultRootWindow(display); int count_screens = ScreenCount(display); - if(count_screens == 0) { + if (count_screens == 0) { puts("no screen info!"); } for (int i = 0; i < count_screens; ++i) { @@ -20,40 +108,32 @@ ScreenShot::ScreenShot() { printf("\tScreen %d: %dX%d\n", i + 1, screen->width, screen->height); } - XGetWindowAttributes(display, root, &window_attributes); - Screen* screen = window_attributes.screen; - width = screen->width; - height = screen->height; - x = 0; - y = 0; - - ximg = XShmCreateImage(display, DefaultVisualOfScreen(screen), DefaultDepthOfScreen(screen), ZPixmap, NULL, &shminfo, width, height); - - shminfo.shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT|0777); - shminfo.shmaddr = ximg->data = (char*)shmat(shminfo.shmid, 0, 0); - shminfo.readOnly = false; - if(shminfo.shmid < 0) - puts("Fatal shminfo error!"); - Status s1 = XShmAttach(display, &shminfo); - printf("XShmAttach() %s\n", s1 ? "success!" : "failure!"); - - init = true; + valid = initImg(); } -void ScreenShot::operator() (cv::Mat& cv_img){ - if(init) +ScreenShot::ScreenShot(Display* d, Window w) : display(d), window(w), closeDisp(false) { + valid = initImg(); +} + +void ScreenShot::take(cv::Mat& cv_img) { + if (init) init = false; - XShmGetImage(display, root, ximg, 0, 0, 0x00ffffff); - cv_img = cv::Mat(height, width, CV_8UC4, ximg->data); + XShmGetImage(display, window, ximg, 0, 0, 0x00ffffff); + cv_img = cv::Mat(wattrib.height, wattrib.width, CV_8UC4, ximg->data); } -ScreenShot::~ScreenShot(){ - if(!init) +void ScreenShot::operator() (cv::Mat& cv_img) { + take(cv_img); +} + +ScreenShot::~ScreenShot() { + if (!init) XDestroyImage(ximg); XShmDetach(display, &shminfo); shmdt(shminfo.shmaddr); - XCloseDisplay(display); -} + if (closeDisp) + XCloseDisplay(display); +}