step to REST api

This commit is contained in:
mrbesen 2022-06-29 23:09:01 +02:00
parent 07ee1df44e
commit 500b12f756
Signed by: MrBesen
GPG Key ID: 596B2350DCD67504
19 changed files with 183 additions and 761 deletions

View File

@ -5,26 +5,54 @@
class ClientAPI : public RestClient {
public:
enum class SearchState : uint32_t {
enum class ReadyCheckState : uint32_t {
INVALID = 0,
SEARCHING,
FOUND,
ABANDONEDLOWPRIORITYQUEUE,
CANCELED,
ERROR,
SERVICEERROR,
SERVICESHUTDOWN,
NONE,
INPROGRESS,
Accepted,
DECLINED
};
enum class GameflowPhase : uint32_t {
NONE = 0,
LOBBY,
MATCHMAKING,
CHECKEDINTOTOURNAMENT,
READYCHECK,
CHAMPSELECT,
GAMESTART,
FAILEDTOLAUNCH,
INPROGRESS,
RECONNECT,
WAITINGFORSTATS,
PREENDOFGAME,
ENDOFGAME,
TERMINATEDINERROR,
};
struct TimerInfo {
int64_t adjustedTimeLeftInPhase;
int64_t internalNowInEpochMs;
bool isefinite;
std::string phase;
int64_t totalTimeInPhase;
bool valid = false;
};
ClientAPI(const ClientAccess& access);
~ClientAPI();
SearchState getSearchState();
ReadyCheckState getReadyCheckState();
GameflowPhase getGameflowPhase();
void acceptMatch();
void declineMatch();
std::vector<int32_t> getBannableChampIDs();
std::vector<int32_t> getPickableChampIDs();
TimerInfo getTimerInfo();
protected:
@ -32,3 +60,6 @@ protected:
private:
ClientAccess access;
};
std::ostream& operator<<(std::ostream&, const ClientAPI::ReadyCheckState&);
std::ostream& operator<<(std::ostream&, const ClientAPI::GameflowPhase&);

View File

@ -1,21 +0,0 @@
#include "screen.h"
class FakeScreen : public ScreenShot {
private:
int xoffset = 0;
int yoffset = 0;
int xsize = 0;
int ysize = 0;
public:
FakeScreen(int xoffset = 0, int yoffset = 0, int xsize = 0, int ysize = 0);
~FakeScreen();
virtual void take(cv::Mat& cvimg) override;
virtual void operator() (cv::Mat& cvimg) override;
virtual int getXOffset() const override;
virtual int getYOffset() const override;
virtual double getXScale() const override;
virtual double getYScale() const override;
};

View File

@ -1,67 +1,28 @@
#pragma once
#include "scaleableinputs.h"
#include "screen.h"
#include "matcher.h"
#include <xinputsimulator.h>
#include <thread>
#include <memory>
#include "clientapi.h"
class LolAutoAccept {
protected:
struct Stage {
Stage(const std::string& matchertmpl);
Stage();
virtual ~Stage();
Matcher matcher;
std::string champ; // not every stage has a champ
bool enabled = false;
virtual void action(LolAutoAccept& lolaa) = 0;
virtual bool process(LolAutoAccept& lolaa, cv::Mat& img);
virtual void action(LolAutoAccept& lolaa);
};
struct CooldownStage : public Stage {
CooldownStage(const std::string& matchertmpl);
time_t lastused = 0;
uint32_t cooldown = 30; // how long until this stage can be used again (seconds)
virtual bool process(LolAutoAccept& lolaa, cv::Mat& img) override;
};
struct AcceptStage : public Stage {
AcceptStage();
virtual void action(LolAutoAccept& lolaa) override;
};
struct PrePickStage : public CooldownStage {
PrePickStage();
virtual void action(LolAutoAccept& lolaa) override;
};
struct BanStage : public CooldownStage {
BanStage();
virtual void action(LolAutoAccept& lolaa) override;
};
struct PickStage : public CooldownStage {
PickStage();
virtual void action(LolAutoAccept& lolaa) override;
};
ScreenShot* screen = nullptr;
std::vector<Stage*> stages;
XInputSimulator& sim;
ScaleableInputs inputs;
bool shouldrun = false;
std::thread lolaathread;
void performClick(uint32_t nr, bool movemouseaway = true);
void enterSearch(const std::string& text);
void pickFirst(const std::string& search);
std::shared_ptr<ClientAPI> clientapi;
public:
enum class State {
LOBBY = 0,

View File

@ -1,41 +0,0 @@
#pragma once
#include <opencv2/opencv.hpp>
class Matcher {
private:
cv::Mat templ;
cv::Mat mask;
int32_t posx = -1;
int32_t posy = -1;
static std::string pathbase;
public:
static void setPathBase(const std::string& pa);
Matcher(const std::string& filename);
Matcher(const cv::Mat& templ);
~Matcher();
// instead of searching for a match, just look at this position
void setOffset(int32_t x, int32_t y);
struct Match {
bool doesMatch = false;
int x = 0, y = 0;
int width = 0;
int height = 0;
bool operator==(const Match&) const;
bool operator!=(const Match&) const;
};
Match match(const cv::Mat& img);
private:
Match matchAll(const cv::Mat& img);
Match matchPos(const cv::Mat& img);
// when the template has a alpha channel try to create a mask from that
void maskFromTemplate();
};

View File

@ -1,89 +0,0 @@
#pragma once
#include <opencv2/opencv.hpp>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#undef Bool
#undef CursorShape
#undef None
#undef KeyPress
#undef KeyRelease
#undef ButtonPress
#undef ButtonRelease
#undef MotionNotify
#undef EnterNotify
#undef LeaveNotify
#undef FocusIn
#undef FocusOut
#undef KeymapNotify
#undef Expose
#undef GraphicsExpose
#undef NoExpose
#undef VisibilityNotify
#undef CreateNotify
#undef DestroyNotify
#undef UnmapNotify
#undef MapNotify
#undef MapRequest
#undef ReparentNotify
#undef ConfigureNotify
#undef ConfigureRequest
#undef GravityNotify
#undef ResizeRequest
#undef CirculateNotify
#undef CirculateRequest
#undef PropertyNotify
#undef SelectionClear
#undef SelectionRequest
#undef SelectionNotify
#undef ColormapNotify
#undef ClientMessage
#undef MappingNotify
#undef GenericEvent
#undef LASTEvent
#undef FontChange
class ScreenShot {
private:
Display* display = nullptr;
Window window;
XWindowAttributes wattrib;
XImage* ximg = nullptr;
XShmSegmentInfo shminfo;
bool init;
bool closeDisp = true;
bool valid = false;
std::string windowname;
bool updateAttrib();
bool initImg();
public:
static std::vector<ScreenShot*> getWindows(const std::string& name);
static const uint32_t DEFAULTWIDTH;
static const uint32_t DEFAULTHEIGHT;
ScreenShot();
ScreenShot(Display* d, Window w);
virtual ~ScreenShot();
virtual void take(cv::Mat& cvimg);
virtual void operator() (cv::Mat& cvimg);
constexpr operator bool () const {
return valid;
}
virtual int getXOffset() const;
virtual int getYOffset() const;
virtual double getXScale() const;
virtual double getYScale() const;
};

View File

@ -1,5 +0,0 @@
#pragma once
#include <opencv2/opencv.hpp>
void debugImage(cv::Mat img, const std::string& name = "");

View File

@ -29,21 +29,13 @@ SOURCES += \
src/config.cpp \
src/datadragon.cpp \
src/datadragonimagecache.cpp \
src/fakescreen.cpp \
src/files.cpp \
src/json.cpp \
src/lolautoaccept_accept.cpp \
src/lolautoaccept_ban.cpp \
src/lolautoaccept_pick.cpp \
src/lolautoaccept_prepick.cpp \
src/lolautoaccept.cpp \
src/main.cpp \
src/mainwindow.cpp \
src/matcher.cpp \
src/memoryimagecache.cpp \
src/restclient.cpp \
src/scaleableinputs.cpp \
src/screen.cpp \
src/stagesettings.cpp \
thirdparty/Log/Log.cpp
@ -56,16 +48,12 @@ HEADERS += \
include/config.h \
include/datadragon.h \
include/datadragonimagecache.h \
include/fakescreen.h \
include/files.h \
include/json.h \
include/lolautoaccept.h \
include/mainwindow.h \
include/matcher.h \
include/memoryimagecache.h \
include/restclient.h \
include/scaleableinputs.h \
include/screen.h \
include/stagesettings.h \
thirdparty/Log/Log.h

View File

@ -1,19 +1,26 @@
#include "clientapi.h"
#include <iomanip>
#include <QJsonArray>
#include <QJsonObject>
#include <Log.h>
#include "json.h"
static const std::string SearchStateNames[] = {"Invalid", "Searching", "Found", "AbandonedLowPriorityQueue", "Canceled", "Error", "ServiceError", "ServiceShutdown"};
static const uint32_t SearchStateNamesCount = sizeof(SearchStateNames) / sizeof(SearchStateNames[0]);
// calculate the size of a constant sized array
#define ARRSIZE(ARRNAME) (sizeof(ARRNAME) / sizeof(ARRNAME[0]))
static const std::string ReadyCheckStateNames[] = { "Invalid", "None", "InProgress", "Accepted", "Declined" };
static const uint32_t ReadyCheckStateNamesCount = ARRSIZE(ReadyCheckStateNames);
static const std::string GameflowPhaseNames[] = {"None", "Lobby", "Matchmaking", "CheckedIntoTournament", "ReadyCheck", "ChampSelect", "GameStart", "FailedToLaunch", "InProgress", "Reconnect", "WaitingForStats", "PreEndOfGame", "EndOfGame", "TerminatedInError"};
static const uint32_t GameflowPhaseNamesCount = ARRSIZE(GameflowPhaseNames);
template<typename T>
static T mapEnum(const std::string& input, const std::string* names, uint32_t count, T defaul) {
for(uint32_t i = 0; i < count; ++i) {
if(names[i] == input) {
for (uint32_t i = 0; i < count; ++i) {
if (names[i] == input) {
return (T) i;
}
}
@ -28,24 +35,105 @@ ClientAPI::ClientAPI(const ClientAccess& ca) : RestClient(ca.getURL()), access(c
}
ClientAPI::~ClientAPI() {}
ClientAPI::SearchState ClientAPI::getSearchState() {
QJsonDocument doc = request("lol-lobby/v2/lobby/matchmaking/search-state");
ClientAPI::ReadyCheckState ClientAPI::getReadyCheckState() {
QJsonDocument doc = request("lol-matchmaking/v1/ready-check");
if(doc.isObject()) {
if (doc.isObject()) {
QJsonObject obj = doc.object();
std::string searchState = getValue<std::string>(obj, "searchState", "Invalid");
Log::info << "ready check obj: " << doc.toJson().toStdString();
return mapEnum(searchState, SearchStateNames, SearchStateNamesCount, ClientAPI::SearchState::INVALID);
std::string searchState = getValue<std::string>(obj, "state", "Invalid");
std::string playerresponse = getValue<std::string>(obj, "playerResponse", "None");
ClientAPI::ReadyCheckState response = mapEnum(playerresponse, ReadyCheckStateNames, ReadyCheckStateNamesCount, ClientAPI::ReadyCheckState::NONE);
if(response == ClientAPI::ReadyCheckState::NONE) {
auto sstate = mapEnum(searchState, ReadyCheckStateNames, ReadyCheckStateNamesCount, ClientAPI::ReadyCheckState::INVALID);
if(sstate == ClientAPI::ReadyCheckState::INPROGRESS) {
return sstate;
}
}
return response;
}
return ClientAPI::SearchState::INVALID;
return ClientAPI::ReadyCheckState::NONE;
}
ClientAPI::GameflowPhase ClientAPI::getGameflowPhase() {
// it is just a json-string no object
QByteArray data = requestRaw("lol-gameflow/v1/gameflow-phase");
std::string datastr = data.toStdString();
if (data.size() > 2) {
datastr = datastr.substr(1, datastr.size() -2);
return mapEnum(datastr, GameflowPhaseNames, GameflowPhaseNamesCount, ClientAPI::GameflowPhase::NONE);
}
return ClientAPI::GameflowPhase::NONE;
}
void ClientAPI::acceptMatch() {
request("lol-matchmaking/v1/ready-check/accept", Method::POST);
}
void ClientAPI::declineMatch() {
request("lol-matchmaking/v1/ready-check/decline", Method::POST);
}
static std::vector<int32_t> fromArrayToVector(const QJsonArray& arr) {
std::vector<int32_t> out;
out.reserve(arr.size());
for (auto it = arr.begin(); it != arr.end(); ++it) {
if (it->isDouble())
out.push_back(it->toInt());
}
return out;
}
std::vector<int32_t> ClientAPI::getBannableChampIDs() {
QJsonDocument doc = request("lol-champ-select/v1/bannable-champion-ids");
if (doc.isArray()) {
return fromArrayToVector(doc.array());
}
return {}; // empty vector
}
std::vector<int32_t> ClientAPI::getPickableChampIDs() {
QJsonDocument doc = request("lol-champ-select/v1/pickable-champion-ids");
if (doc.isArray()) {
return fromArrayToVector(doc.array());
}
return {}; // empty vector
}
ClientAPI::TimerInfo ClientAPI::getTimerInfo() {
QJsonDocument doc = request("lol-champ-select/v1/session/timer");
if (!doc.isObject()) return {};
TimerInfo ti;
QJsonObject obj = doc.object();
ti.adjustedTimeLeftInPhase = getValue<int64_t>(obj, "adjustedTimeLeftInPhase", 0);
ti.internalNowInEpochMs = getValue<int64_t>(obj, "internalNowInEpochMs", 0);
ti.isefinite = getValue<bool>(obj, "isefinite", 0);
ti.phase = getValue<std::string>(obj, "phase", "");
ti.totalTimeInPhase = getValue<int64_t>(obj, "totalTimeInPhase", 0);
return ti;
}
#define PRINTENUM(ENUMNAME) \
std::ostream& operator<<(std::ostream& str, const ClientAPI:: ENUMNAME & state) { \
return str << ENUMNAME ## Names[(int) state] << " (" << (int) state << ')'; \
}
PRINTENUM(ReadyCheckState)
PRINTENUM(GameflowPhase)

View File

@ -1,38 +0,0 @@
#include "fakescreen.h"
FakeScreen::FakeScreen(int xoffset, int yoffset, int xsize, int ysize) : ScreenShot(),
xoffset(xoffset), yoffset(yoffset), xsize(xsize), ysize(ysize) {
}
FakeScreen::~FakeScreen() {}
void FakeScreen::take(cv::Mat& cvimg) {
// take screenshot from entire screen using base class
cv::Mat entireScreen;
ScreenShot::take(entireScreen);
// only return part of the screenshot
entireScreen({yoffset, yoffset + ysize}, {xoffset, xoffset + xsize}).copyTo(cvimg);
}
void FakeScreen::operator() (cv::Mat& cvimg) {
take(cvimg);
}
int FakeScreen::getXOffset() const {
return xoffset;
}
int FakeScreen::getYOffset() const {
return yoffset;
}
double FakeScreen::getXScale() const {
return xsize / (double) DEFAULTWIDTH;
}
double FakeScreen::getYScale() const {
return ysize / (double) DEFAULTHEIGHT;
}

View File

@ -3,99 +3,18 @@
#include <thread>
#include <Log.h>
#include "util.h"
#include "fakescreen.h"
// set to 1 to use the test screenshot feature
#define TESTSCREEN 0
void debugImage(cv::Mat img, const std::string& name) {
if(img.channels() > 3) {
std::vector<cv::Mat> 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);
}
LolAutoAccept::Stage::Stage(const std::string& matchertmpl) : matcher(matchertmpl) {}
LolAutoAccept::Stage::Stage() {}
LolAutoAccept::Stage::~Stage() {}
bool LolAutoAccept::Stage::process(LolAutoAccept& lolaa, cv::Mat& img) {
if(enabled) {
auto match = matcher.match(img);
if(match.doesMatch) {
action(lolaa);
return true;
}
}
return false;
}
LolAutoAccept::CooldownStage::CooldownStage(const std::string& matchertmpl) : Stage(matchertmpl) {}
bool LolAutoAccept::CooldownStage::process(LolAutoAccept& lolaa, cv::Mat& img) {
if((time(0) - lastused) > cooldown) {
if(Stage::process(lolaa, img)) {
lastused = time(0);
return true;
}
}
return false;
}
void LolAutoAccept::performClick(uint32_t nr, bool movemouseaway) {
inputs.setOffset(screen->getXOffset(), screen->getYOffset());
inputs.setScale(screen->getXScale(), 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(170));
sim.mouseClick(XIS::LEFT_MOUSE_BUTTON);
// move mouse away
if(movemouseaway) {
std::this_thread::sleep_for(std::chrono::milliseconds(170));
p = inputs.get(0);
sim.mouseMoveTo(p.x, p.y);
}
}
void LolAutoAccept::enterSearch(const std::string& text) {
performClick(2, false); // click searchbox
Log::debug << "enter text: " << text;
sim.keySequence(text);
std::this_thread::sleep_for(std::chrono::milliseconds(750));
}
void LolAutoAccept::pickFirst(const std::string& search) {
enterSearch(search);
performClick(3, false); // first champion
}
LolAutoAccept::LolAutoAccept() : sim(XInputSimulator::getInstance()) {
// click positions in 1280x720 scale
inputs.addPoint({0, 0}); // zero zero
inputs.addPoint({645, 560}); // accept game
inputs.addPoint({775, 105}); // search box
inputs.addPoint({380, 160}); // first champ
inputs.addPoint({638, 608}); // pick/ban champ
void LolAutoAccept::Stage::action(LolAutoAccept& lolaa) {}
LolAutoAccept::LolAutoAccept() {
stages.reserve(4);
stages.push_back(new AcceptStage());
stages.push_back(new PrePickStage());
stages.push_back(new BanStage());
stages.push_back(new PickStage());
stages.push_back(new Stage()); // accept
stages.push_back(new Stage()); // prepick
stages.push_back(new Stage()); // ban
stages.push_back(new Stage()); // pick
}
LolAutoAccept::~LolAutoAccept() {
@ -104,9 +23,6 @@ LolAutoAccept::~LolAutoAccept() {
for(auto s : stages) {
delete s;
}
delete screen;
screen = nullptr;
}
void LolAutoAccept::setChamp(const std::string& champ, State s) {
@ -128,37 +44,21 @@ void LolAutoAccept::setEnabled(bool b, State s) {
}
bool LolAutoAccept::init() {
if(screen)
return true;
if(clientapi) return true;
// get window
#if TESTSCREEN == 0
auto wins = ScreenShot::getWindows("League of Legends");
if(wins.size() != 1) {
Log::fatal << "invalid count of windows found: " << wins.size();
for(auto w : wins) {
delete w;
}
return false;
auto ca = ClientAccess::find();
if(ca) {
clientapi = std::make_shared<ClientAPI>(*ca.get());
}
screen = (wins.at(0)); // just take the first
#else
// testing whole screen, but take a part of it, so it behaves like a focused screenshot
screen = new FakeScreen(1, 683, 1280, 720);
#endif
// testing: whole screen:
// screen = new ScreenShot();
return true;
return (bool) clientapi;
}
void LolAutoAccept::run() {
// make sure its not running
stopJoinThread();
if(!screen) return;
if(!clientapi) return; // no client api
lolaathread = std::thread(&LolAutoAccept::innerRun, this);
}
@ -181,24 +81,29 @@ void LolAutoAccept::innerRun() {
while(shouldrun) {
auto start = std::chrono::high_resolution_clock::now();
cv::Mat img;
screen->take(img);
cv::resize(img, img, cv::Size(ScreenShot::DEFAULTWIDTH, ScreenShot::DEFAULTHEIGHT));
auto phase = clientapi->getGameflowPhase();
Log::debug << "current Gameflowphase: " << phase;
// TODO: do processing
if(phase == ClientAPI::GameflowPhase::READYCHECK) {
if(stages.at(0)->enabled) { // auto accept enabled
auto state = clientapi->getReadyCheckState();
Log::info << "readychack state: " << state;
if(state == ClientAPI::ReadyCheckState::INPROGRESS) {
Log::info << "auto accepting";
clientapi->acceptMatch();
}
} else if(phase == ClientAPI::GameflowPhase::CHAMPSELECT) {
for(size_t i = 0; i < stages.size(); ++i) {
Log::trace << "processing stage " << i;
Stage* stage = stages.at(i);
if(stage->process(*this, img)) {
Log::debug << "stage successful: " << i;
break;
}
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> dur = (end-start);
if(dur.count() > 1e-5)
Log::info << "iteration took: " << (dur.count() * 1000) << " ms";
//if(dur.count() > 1e-5)
Log::info << "iteration took: " << (dur.count() * 1000) << " ms";
std::this_thread::sleep_for(std::chrono::milliseconds(800));
std::this_thread::sleep_for(std::chrono::milliseconds(1200));
}
}

View File

@ -1,12 +0,0 @@
#include "lolautoaccept.h"
LolAutoAccept::AcceptStage::AcceptStage() : Stage("imgs/accept.png") {
matcher.setOffset(539, 529);
}
void LolAutoAccept::AcceptStage::action(LolAutoAccept& lolaa) {
lolaa.performClick(1); // accept Game
// security sleep
std::this_thread::sleep_for(std::chrono::seconds(1));
}

View File

@ -1,17 +0,0 @@
#include "lolautoaccept.h"
LolAutoAccept::BanStage::BanStage() : CooldownStage("imgs/ban.png") {
matcher.setOffset(1232, 90);
cooldown = 30;
}
void LolAutoAccept::BanStage::action(LolAutoAccept& lolaa) {
if(!champ.empty()) {
lolaa.pickFirst(champ);
std::this_thread::sleep_for(std::chrono::milliseconds(1800));
// click "ban"
lolaa.performClick(4);
}
}

View File

@ -1,17 +0,0 @@
#include "lolautoaccept.h"
LolAutoAccept::PickStage::PickStage() : CooldownStage("imgs/pick.png") {
matcher.setOffset(538, 587);
cooldown = 30;
}
void LolAutoAccept::PickStage::action(LolAutoAccept& lolaa) {
if(!champ.empty()) {
lolaa.pickFirst(champ);
std::this_thread::sleep_for(std::chrono::milliseconds(1800));
// click "accept"
lolaa.performClick(4);
}
}

View File

@ -1,11 +0,0 @@
#include "lolautoaccept.h"
LolAutoAccept::PrePickStage::PrePickStage() : CooldownStage("imgs/arrowdown.png") {
matcher.setOffset(615, 617);
cooldown = 15;
}
void LolAutoAccept::PrePickStage::action(LolAutoAccept& lolaa) {
if(!champ.empty())
lolaa.pickFirst(champ);
}

View File

@ -12,6 +12,8 @@
#include "arg.h"
#include "mainwindow.h"
#include "lolautoaccept.h"
#include "clientaccess.h"
#include "clientapi.h"
static std::string getBaseString(char** argv) {
std::string base;
@ -51,7 +53,6 @@ int main(int argc, char** argv) {
std::string base = getBaseString(argv);
Log::info << "appbase: " << base;
Matcher::setPathBase(base);
LolAutoAccept lolaa;
QApplication app(argc, argv);
@ -65,21 +66,6 @@ int main(int argc, char** argv) {
QIcon icon(QString::fromStdString(base + "lolautoaccept.png"));
win.setWindowIcon(icon);
while(!lolaa.init()) {
QMessageBox warn;
warn.setWindowIcon(icon);
warn.setWindowTitle(MainWindow::tr("LoL-Auto-Accept"));
warn.setText(QMessageBox::tr("League of Legends Client not found!"));
warn.setInformativeText(QMessageBox::tr("Open the client and retry or close."));
warn.setStandardButtons(QMessageBox::Retry | QMessageBox::Close);
warn.setDefaultButton(QMessageBox::Retry);
warn.setIcon(QMessageBox::Warning);
if(warn.exec() == QMessageBox::Close) {
Log::stop();
return 0;
}
}
win.show();
int ret = app.exec();

View File

@ -1,102 +0,0 @@
#include "matcher.h"
#include <Log.h>
#include "util.h"
std::string Matcher::pathbase;
void Matcher::setPathBase(const std::string& pa) {
pathbase = pa;
}
Matcher::Matcher(const std::string& filename) {
templ = cv::imread(pathbase + filename, cv::IMREAD_UNCHANGED); // unchanged so alpha channel does not get dropped
maskFromTemplate();
}
Matcher::Matcher(const cv::Mat& templ) {
templ.copyTo(this->templ);
maskFromTemplate();
}
Matcher::~Matcher() {}
void Matcher::setOffset(int32_t x, int32_t y) {
posx = x;
posy = y;
}
bool Matcher::Match::operator==(const Match& other) const {
return doesMatch == other.doesMatch && x == other.x && y == other.y && width == other.width && height == other.height;
}
bool Matcher::Match::operator!=(const Match& other) const {
return !(*this == other);
}
Matcher::Match Matcher::match(const cv::Mat& img) {
// remove Alpha channel from input
cv::Mat converted;
cv::cvtColor(img, converted, cv::COLOR_RGBA2RGB);
if(posx < 0 || posy < 0) {
return matchAll(converted);
}
return matchPos(converted);
}
// https://stackoverflow.com/a/43133263/4399859
Matcher::Match Matcher::matchAll(const cv::Mat& img) {
// create out mat
int result_cols = img.cols - templ.cols + 1;
int result_rows = img.rows - templ.rows + 1;
cv::Mat out(result_rows, result_cols, CV_32FC1);
Log::info << "match size: " << result_cols << " " << result_cols;
// match
cv::matchTemplate(img, templ, out, cv::TM_CCORR_NORMED, mask);
double minVal; double maxVal;
cv::Point minLoc; cv::Point maxLoc;
cv::minMaxLoc( out, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
Log::debug << "pixelcount: " << (templ.cols * templ.rows) << " minVal: " << minVal << " maxVal: " << maxVal << " minLoc: " << minLoc.x << " " << minLoc.y << " maxLoc: " << maxLoc.x << " " << maxLoc.y;
bool matched = maxVal > 0.95;
return {matched, maxLoc.x, maxLoc.y, templ.cols, templ.rows};
}
Matcher::Match Matcher::matchPos(const cv::Mat& img) {
cv::Mat matchpart = img(cv::Range(posy, posy + templ.rows), cv::Range(posx, posx + templ.cols));
// create out mat (only one pxl)
cv::Mat out(1, 1, CV_32FC1);
// match
cv::matchTemplate(matchpart, templ, out, cv::TM_CCORR_NORMED, mask);
float val = out.at<float>({0, 0});
bool matched = val > 0.95;
Log::debug << "val: " << val;
return {matched, posx, posy, templ.cols, templ.rows};
}
void Matcher::maskFromTemplate() {
if(templ.channels() == 4) {
// split channels
std::vector<cv::Mat> split;
cv::split(templ, split);
// template without alpha channel
cv::merge(std::vector(split.begin(), split.end()-1), templ);
// 3*alpha channel
cv::Mat alphaChannel = split.at(3);
alphaChannel.convertTo(alphaChannel, CV_8U);
cv::merge(std::vector(3, alphaChannel), mask);
}
}

View File

@ -45,7 +45,9 @@ QByteArray RestClient::requestRaw(const std::string& url, Method m) {
case Method::GET:
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); break;
case Method::POST:
curl_easy_setopt(curl, CURLOPT_POST, 1L); break;
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "");
break;
case Method::PUT:
curl_easy_setopt(curl, CURLOPT_PUT, 1L); break;
case Method::DELETE:

View File

@ -1,25 +0,0 @@
#include "scaleableinputs.h"
#include <Log.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);
Log::debug << "scaling: " << p.x << " " << p.y << " with: " << xOffset << " " << yOffset << " scale: " << xScale << " " << yScale;
return {(uint32_t) ((p.x * xScale) + xOffset), (uint32_t) ((p.y * yScale) + yOffset)};
}

View File

@ -1,161 +0,0 @@
// g++ screena.cpp -o screena -lX11 -lXext -Ofast -mfpmath=both -march=native -m64 -funroll-loops -mavx2 `pkg-config opencv --cflags --libs` && ./screena
// This includes most headers!
#include "screen.h"
#include <X11/cursorfont.h>
#include <Log.h>
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<Window>& 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*> ScreenShot::getWindows(const std::string& name) {
Display* display = XOpenDisplay(nullptr);
Log::info << "searching for: " << name;
std::vector<Window> list;
searchWindows(display, DefaultRootWindow(display), name, list);
std::vector<ScreenShot*> 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);
window = DefaultRootWindow(display);
int count_screens = ScreenCount(display);
if (count_screens == 0) {
puts("no screen info!");
}
for (int i = 0; i < count_screens; ++i) {
Screen* screen = ScreenOfDisplay(display, i);
printf("\tScreen %d: %dX%d\n", i + 1, screen->width, screen->height);
}
valid = initImg();
}
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;
// window not viewable
if(!updateAttrib()) {
cv_img.create(0, 0, 0);
}
XShmGetImage(display, window, ximg, 0, 0, 0x00ffffff);
cv_img = cv::Mat(wattrib.height, wattrib.width, CV_8UC4, ximg->data);
}
void ScreenShot::operator() (cv::Mat& cv_img) {
take(cv_img);
}
ScreenShot::~ScreenShot() {
if (!init)
XDestroyImage(ximg);
XShmDetach(display, &shminfo);
shmdt(shminfo.shmaddr);
if (closeDisp)
XCloseDisplay(display);
}
int ScreenShot::getXOffset() const {
return wattrib.x;
}
int ScreenShot::getYOffset() const {
return wattrib.y;
}
double ScreenShot::getXScale() const {
return wattrib.width / (double) DEFAULTWIDTH;
}
double ScreenShot::getYScale() const {
return wattrib.height / (double) DEFAULTHEIGHT;
}