forked from MrBesen/lolautoaccept
clientapi basics
This commit is contained in:
parent
c1e92c1706
commit
00f6cc4a7d
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class ClientAccess {
|
||||
ClientAccess();
|
||||
ClientAccess(const std::string& token, uint16_t port);
|
||||
public:
|
||||
static std::shared_ptr<ClientAccess> find(bool uselockfile = true);
|
||||
private:
|
||||
// returns the name and value of a argument or empty string if it could not be parsed
|
||||
static std::string parseArg(const std::string& input, std::string& value);
|
||||
static std::shared_ptr<ClientAccess> findUsingArgs(const std::vector<std::string>& cmdline);
|
||||
static std::shared_ptr<ClientAccess> findUsingLockfile(const std::vector<std::string>& cmdline, pid_t pid);
|
||||
|
||||
public:
|
||||
std::string getBasicAuth() const;
|
||||
uint16_t getPort() const;
|
||||
|
||||
std::string getURL() const;
|
||||
|
||||
private:
|
||||
|
||||
std::string authcode;
|
||||
uint16_t port = 0;
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "clientaccess.h"
|
||||
#include "restclient.h"
|
||||
|
||||
class ClientAPI : public RestClient {
|
||||
public:
|
||||
enum class SearchState : uint32_t {
|
||||
INVALID = 0,
|
||||
SEARCHING,
|
||||
FOUND,
|
||||
|
||||
ABANDONEDLOWPRIORITYQUEUE,
|
||||
CANCELED,
|
||||
ERROR,
|
||||
SERVICEERROR,
|
||||
SERVICESHUTDOWN,
|
||||
};
|
||||
|
||||
|
||||
ClientAPI(const ClientAccess& access);
|
||||
~ClientAPI();
|
||||
|
||||
SearchState getSearchState();
|
||||
void acceptMatch();
|
||||
void declineMatch();
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
private:
|
||||
ClientAccess access;
|
||||
};
|
|
@ -6,14 +6,14 @@
|
|||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <curl/curl.h>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#include "datadragonimagecache.h"
|
||||
#include "memoryimagecache.h"
|
||||
#include "restclient.h"
|
||||
|
||||
class DataDragon {
|
||||
class DataDragon : public RestClient {
|
||||
public:
|
||||
using notifyImgfunc_t = std::function<void(cv::Mat)>;
|
||||
|
||||
|
@ -54,8 +54,6 @@ public:
|
|||
protected:
|
||||
std::string getImageUrl(const std::string& champid, ImageType type);
|
||||
std::string getCDNString() const;
|
||||
QByteArray requestRaw(const std::string& url);
|
||||
QJsonDocument request(const std::string& url);
|
||||
|
||||
void getVersionInternal();
|
||||
void getChampsInternal();
|
||||
|
@ -77,7 +75,6 @@ private:
|
|||
ImageType type;
|
||||
};
|
||||
|
||||
CURL* curl = nullptr; // the curl (does curling)
|
||||
|
||||
DataDragonImageCache cache[3];
|
||||
MemoryImageCache memcache;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <QJsonDocument>
|
||||
#include <curl/curl.h>
|
||||
|
||||
class RestClient {
|
||||
public:
|
||||
RestClient(const std::string& base);
|
||||
virtual ~RestClient();
|
||||
|
||||
protected:
|
||||
QByteArray requestRaw(const std::string& url);
|
||||
QJsonDocument request(const std::string& url);
|
||||
|
||||
std::string baseurl;
|
||||
|
||||
CURL* curl = nullptr; // the curl (does curling)
|
||||
|
||||
std::string basicauth; // basic auth code (user:pw) or empty string to disable
|
||||
bool disableCertCheck = false;
|
||||
};
|
|
@ -24,6 +24,8 @@ defineReplace(prependAll) {
|
|||
|
||||
SOURCES += \
|
||||
src/arg.cpp \
|
||||
src/clientaccess.cpp \
|
||||
src/clientapi.cpp \
|
||||
src/config.cpp \
|
||||
src/datadragon.cpp \
|
||||
src/datadragonimagecache.cpp \
|
||||
|
@ -39,6 +41,7 @@ SOURCES += \
|
|||
src/mainwindow.cpp \
|
||||
src/matcher.cpp \
|
||||
src/memoryimagecache.cpp \
|
||||
src/restclient.cpp \
|
||||
src/scaleableinputs.cpp \
|
||||
src/screen.cpp \
|
||||
src/stagesettings.cpp \
|
||||
|
@ -48,6 +51,8 @@ SOURCES += \
|
|||
|
||||
HEADERS += \
|
||||
include/arg.h \
|
||||
include/clientaccess.h \
|
||||
include/clientapi.h \
|
||||
include/config.h \
|
||||
include/datadragon.h \
|
||||
include/datadragonimagecache.h \
|
||||
|
@ -58,6 +63,7 @@ HEADERS += \
|
|||
include/mainwindow.h \
|
||||
include/matcher.h \
|
||||
include/memoryimagecache.h \
|
||||
include/restclient.h \
|
||||
include/scaleableinputs.h \
|
||||
include/screen.h \
|
||||
include/stagesettings.h \
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
#include "clientaccess.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <Log.h>
|
||||
|
||||
static bool endsWith(const std::string& all, const std::string& end) {
|
||||
return all.rfind(end) == all.size() - end.size();
|
||||
}
|
||||
|
||||
// reads a procfile into a vector (strings are \0 seperated)
|
||||
static std::vector<std::string> readProcFile(const std::string& path) {
|
||||
std::ifstream in(path);
|
||||
std::vector<std::string> out;
|
||||
std::string line;
|
||||
while(std::getline(in, line, '\0')) {
|
||||
if(!line.empty()) {
|
||||
out.push_back(line);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
ClientAccess::ClientAccess() {}
|
||||
|
||||
ClientAccess::ClientAccess(const std::string& token, uint16_t port) : authcode(token), port(port) {}
|
||||
|
||||
std::shared_ptr<ClientAccess> ClientAccess::find(bool uselockfile) {
|
||||
|
||||
DIR* procdir = opendir("/proc");
|
||||
if(!procdir) return nullptr;
|
||||
|
||||
dirent* entry = nullptr;
|
||||
while ((entry = readdir(procdir)) != NULL) {
|
||||
if (entry->d_type != DT_DIR) continue;
|
||||
|
||||
std::string name(entry->d_name);
|
||||
|
||||
pid_t pid = -1;
|
||||
try {
|
||||
pid = std::stoi(name);
|
||||
} catch(std::exception& e) {
|
||||
// could not parse -> not a pid
|
||||
continue;
|
||||
}
|
||||
|
||||
// get info on the exe
|
||||
std::string cmdfile = "/proc/" + std::to_string(pid) + "/cmdline";
|
||||
std::vector<std::string> args = readProcFile(cmdfile);
|
||||
|
||||
Log::debug << "process: " << pid << " has " << args.size() << " args";
|
||||
|
||||
if(args.empty()) continue;
|
||||
|
||||
std::string& exename = args.at(0);
|
||||
if(endsWith(exename, "LeagueClientUx.exe")) {
|
||||
Log::info << "LeagueClientUx.exe found";
|
||||
|
||||
std::shared_ptr<ClientAccess> out;
|
||||
|
||||
if(uselockfile) {
|
||||
out = findUsingLockfile(args, pid);
|
||||
} else {
|
||||
out = findUsingArgs(args);
|
||||
}
|
||||
|
||||
if(out) {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(procdir);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string ClientAccess::parseArg(const std::string& input, std::string& value) {
|
||||
if(input.find("--") != 0) return {};
|
||||
|
||||
size_t pos = input.find('=');
|
||||
if(pos == std::string::npos) return {};
|
||||
|
||||
value = input.substr(pos+1);
|
||||
return input.substr(2, pos-2);
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientAccess> ClientAccess::findUsingArgs(const std::vector<std::string>& cmdline) {
|
||||
// parse args
|
||||
std::shared_ptr<ClientAccess> access(new ClientAccess());
|
||||
for(const std::string& arg : cmdline) {
|
||||
std::string value;
|
||||
std::string argname = parseArg(arg, value);
|
||||
|
||||
if(argname == "riotclient-auth-token") {
|
||||
access->authcode = value;
|
||||
} else if(argname == "riotclient-app-port") {
|
||||
try {
|
||||
access->port = std::stoi(value);
|
||||
} catch(std::exception& e) {
|
||||
Log::warn << "could not parse port: " << std::quoted(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(access->port > 0 && !access->authcode.empty()) {
|
||||
return access;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientAccess> ClientAccess::findUsingLockfile(const std::vector<std::string>& cmdline, pid_t pid) {
|
||||
// get WINEPREFIX env
|
||||
std::vector<std::string> envs = readProcFile("/proc/" + std::to_string(pid) + "/environ");
|
||||
|
||||
const static std::string WINEPREFIX = "WINEPREFIX=";
|
||||
|
||||
// find WINEPREFIX
|
||||
auto found = std::find_if(envs.begin(), envs.end(), [](const std::string& s) {
|
||||
return s.find(WINEPREFIX) == 0;
|
||||
});
|
||||
|
||||
// WINEPREFIX env not present
|
||||
if(found == envs.end()) {
|
||||
Log::debug << "WINEPREFIX environment variable not set";
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string wineprefix = found->substr(WINEPREFIX.size());
|
||||
const std::string binarypath = cmdline.at(0);
|
||||
std::string gamefolder = binarypath.substr(2, binarypath.rfind('/')-1); // remove the "C:" and the name of the binary
|
||||
// TODO: gamefoldre could contain '\' so replacing every occurance with '/' would be a good idea
|
||||
const std::string lockfilepath = wineprefix + "/drive_c/" + gamefolder + "/lockfile";
|
||||
|
||||
Log::debug << "lockfilepath: " << std::quoted(lockfilepath);
|
||||
|
||||
// read lockfile
|
||||
std::ifstream lockfile(lockfilepath);
|
||||
std::vector<std::string> parts;
|
||||
std::string content;
|
||||
while(std::getline(lockfile, content, ':')) {
|
||||
parts.push_back(content);
|
||||
}
|
||||
|
||||
if(parts.size() != 5) {
|
||||
Log::error << "lockfile contained " << parts.size() << " parts, expected 5";
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string portstr = parts.at(2);
|
||||
const std::string token = parts.at(3);
|
||||
|
||||
// try to parse port
|
||||
try {
|
||||
uint16_t port = std::stoi(portstr);
|
||||
|
||||
return std::shared_ptr<ClientAccess>(new ClientAccess(token, port));
|
||||
} catch(std::exception& e) {
|
||||
Log::error << "could not parse port: " << std::quoted(portstr);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string ClientAccess::getBasicAuth() const {
|
||||
return "riot:" + authcode;
|
||||
}
|
||||
|
||||
uint16_t ClientAccess::getPort() const {
|
||||
return port;
|
||||
}
|
||||
|
||||
std::string ClientAccess::getURL() const {
|
||||
return "https://127.0.0.1:" + std::to_string(port) + "/";
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#include "clientapi.h"
|
||||
|
||||
#include <iomanip>
|
||||
#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]);
|
||||
|
||||
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) {
|
||||
return (T) i;
|
||||
}
|
||||
}
|
||||
|
||||
Log::warn << "no mapping of enum-string: " << std::quoted(input);
|
||||
return defaul;
|
||||
}
|
||||
|
||||
ClientAPI::ClientAPI(const ClientAccess& ca) : RestClient(ca.getURL()), access(ca) {
|
||||
basicauth = ca.getBasicAuth();
|
||||
disableCertCheck = true;
|
||||
}
|
||||
ClientAPI::~ClientAPI() {}
|
||||
|
||||
ClientAPI::SearchState ClientAPI::getSearchState() {
|
||||
QJsonDocument doc = request("lol-lobby/v2/lobby/matchmaking/search-state");
|
||||
|
||||
if(doc.isObject()) {
|
||||
QJsonObject obj = doc.object();
|
||||
std::string searchState = getValue<std::string>(obj, "searchState", "Invalid");
|
||||
|
||||
return mapEnum(searchState, SearchStateNames, SearchStateNamesCount, ClientAPI::SearchState::INVALID);
|
||||
}
|
||||
|
||||
return ClientAPI::SearchState::INVALID;
|
||||
}
|
||||
|
||||
void ClientAPI::acceptMatch() {
|
||||
|
||||
}
|
||||
|
||||
void ClientAPI::declineMatch() {
|
||||
|
||||
}
|
||||
|
|
@ -16,27 +16,12 @@
|
|||
static const std::string BASEURL = "https://ddragon.leagueoflegends.com/";
|
||||
const DataDragon::ChampData DataDragon::EMPTYCHAMP;
|
||||
|
||||
static size_t arrayWriteCallback(char* contents, size_t size, size_t nmemb, void* userdata) {
|
||||
if(userdata) {
|
||||
QByteArray* arr = (QByteArray*) userdata;
|
||||
arr->append(contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DataDragon::DataDragon(const std::string& locale) : locale(locale), cache({{"square", ".png"}, {"loading", "_0.jpg"}, {"splash", "_0.jpg"}}) {
|
||||
curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, arrayWriteCallback);
|
||||
|
||||
DataDragon::DataDragon(const std::string& locale) : RestClient(BASEURL), locale(locale), cache({{"square", ".png"}, {"loading", "_0.jpg"}, {"splash", "_0.jpg"}}) {
|
||||
startThread();
|
||||
}
|
||||
|
||||
DataDragon::~DataDragon() {
|
||||
stopAndJoinThread();
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
DataDragon::ChampData::ChampData() : key(-1) {}
|
||||
|
@ -204,47 +189,6 @@ std::string DataDragon::getCDNString() const {
|
|||
return "cdn/" + version + "/";
|
||||
}
|
||||
|
||||
QByteArray DataDragon::requestRaw(const std::string& url) {
|
||||
if(!curl) return {};
|
||||
|
||||
std::string requrl = BASEURL + url;
|
||||
QByteArray ba; //buffer
|
||||
// std::cout << "[DEBUG] requrl is: " << requrl << std::endl;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, requrl.c_str());
|
||||
|
||||
// curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
|
||||
// set callback data
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ba);
|
||||
|
||||
// Check for errors
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
if(res == CURLE_HTTP_RETURNED_ERROR) {
|
||||
long responsecode = -1;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responsecode);
|
||||
Log::warn << "DataDragon request failed: " << url << " -> " << responsecode ;
|
||||
} else {
|
||||
Log::warn << "DataDragon request failed: " << url << " " << curl_easy_strerror(res);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
|
||||
QJsonDocument DataDragon::request(const std::string& url) {
|
||||
QByteArray arr = requestRaw(url);
|
||||
if(arr.isEmpty()) return {};
|
||||
|
||||
QJsonParseError err;
|
||||
QJsonDocument parsed = QJsonDocument::fromJson(arr, &err);
|
||||
if(parsed.isNull() || err.error != QJsonParseError::NoError) {
|
||||
Log::error << "DataDragon Json parse error " << err.errorString().toStdString() << " offset: " << err.offset;
|
||||
return {};
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
void DataDragon::getVersionInternal() {
|
||||
std::unique_lock lock(cachedatamutex);
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
#include "restclient.h"
|
||||
|
||||
#include <Log.h>
|
||||
|
||||
static size_t arrayWriteCallback(char* contents, size_t size, size_t nmemb, void* userdata) {
|
||||
if(userdata) {
|
||||
QByteArray* arr = (QByteArray*) userdata;
|
||||
arr->append(contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
RestClient::RestClient(const std::string& base) : baseurl(base) {
|
||||
curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, arrayWriteCallback);
|
||||
}
|
||||
|
||||
RestClient::~RestClient() {
|
||||
curl_easy_cleanup(curl);
|
||||
curl = nullptr;
|
||||
}
|
||||
|
||||
QByteArray RestClient::requestRaw(const std::string& url) {
|
||||
if(!curl) return {};
|
||||
|
||||
std::string requrl = baseurl + url;
|
||||
QByteArray ba; //buffer
|
||||
// std::cout << "[DEBUG] requrl is: " << requrl << std::endl;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, requrl.c_str());
|
||||
|
||||
// curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
|
||||
// set callback data
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ba);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_USERPWD, basicauth.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
if(disableCertCheck) {
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
}
|
||||
|
||||
// Check for errors
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
if(res == CURLE_HTTP_RETURNED_ERROR) {
|
||||
long responsecode = -1;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responsecode);
|
||||
Log::warn << "API request failed: " << baseurl << " " << url << " -> " << responsecode ;
|
||||
} else {
|
||||
Log::warn << "API request failed: " << baseurl << " " << url << " " << curl_easy_strerror(res);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
|
||||
QJsonDocument RestClient::request(const std::string& url) {
|
||||
QByteArray arr = requestRaw(url);
|
||||
if(arr.isEmpty()) return {};
|
||||
|
||||
QJsonParseError err;
|
||||
QJsonDocument parsed = QJsonDocument::fromJson(arr, &err);
|
||||
if(parsed.isNull() || err.error != QJsonParseError::NoError) {
|
||||
Log::error << "API Json parse error " << err.errorString().toStdString() << " offset: " << err.offset;
|
||||
return {};
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
Loading…
Reference in New Issue