forked from MrBesen/lolautoaccept
492 lines
14 KiB
C++
492 lines
14 KiB
C++
#include "lolautoaccept.h"
|
|
|
|
#include <algorithm>
|
|
#include <thread>
|
|
#include <Log.h>
|
|
|
|
#include <QDebug>
|
|
|
|
LolAutoAccept::Stage::Stage() {}
|
|
LolAutoAccept::Stage::~Stage() {}
|
|
|
|
LolAutoAccept::LolAutoAccept(Config::RootConfig& config, DataDragon& dd, QObject* parent) : QObject(parent), config(config), dd(dd) {
|
|
qRegisterMetaType<LolAutoAccept::Status>();
|
|
|
|
std::lock_guard lock(stagesMutex);
|
|
stages.resize(3); // accept, ban, pick
|
|
}
|
|
|
|
LolAutoAccept::~LolAutoAccept() {
|
|
stopJoinThread();
|
|
}
|
|
|
|
void LolAutoAccept::setChamps(const std::vector<uint32_t>& champs, State s) {
|
|
if(s == State::LOBBY || s >= State::PICK) {
|
|
qWarning() << "setChamps() called on invalid State";
|
|
return;
|
|
}
|
|
qDebug() << "LolAutoAccept::setChamps";
|
|
|
|
{
|
|
std::lock_guard lock(stagesMutex);
|
|
stages.at((int) s).champids = champs;
|
|
stages.at((int) s).currentOffset = 0;
|
|
}
|
|
qInfo() << "set champs on state: " << (int) s << " count: " << champs.size();
|
|
}
|
|
|
|
void LolAutoAccept::setEnabled(bool b, State s) {
|
|
if(s > State::PICK) {
|
|
qWarning() << "setEnabled() called on invalid State";
|
|
return;
|
|
}
|
|
|
|
std::lock_guard lock(stagesMutex);
|
|
stages.at((int) s).enabled = b;
|
|
}
|
|
|
|
void LolAutoAccept::setSmiteWarn(bool b) {
|
|
smiteWarnEnabled = b;
|
|
}
|
|
|
|
bool LolAutoAccept::init() {
|
|
if(clientapi) return true;
|
|
|
|
auto ca = ClientAccess::find();
|
|
if(ca) {
|
|
clientapi = std::make_shared<ClientAPI>(*ca.get());
|
|
auto selfinfo = clientapi->getSelf();
|
|
qInfo() << "selfinfo: gameName: " << selfinfo.gameName << " name: " << selfinfo.name << " summonerid: " << selfinfo.summonerid << " statusMessage: " << selfinfo.statusMessage << " puuid: " << selfinfo.puuid << " level: " << selfinfo.level;
|
|
}
|
|
|
|
return (bool) clientapi;
|
|
}
|
|
|
|
void LolAutoAccept::run() {
|
|
// make sure its not running
|
|
stopJoinThread();
|
|
|
|
if(!clientapi) return; // no client api
|
|
|
|
lolaathread = std::thread(&LolAutoAccept::innerRun, this);
|
|
}
|
|
|
|
void LolAutoAccept::stop() {
|
|
shouldrun = false;
|
|
}
|
|
|
|
LolAutoAccept::Status LolAutoAccept::getStatus() {
|
|
return shouldrun ? Status::Running : Status::Off;
|
|
}
|
|
|
|
void LolAutoAccept::reload() {
|
|
Log::note << "reload LolAutoAccept";
|
|
if(currentPositionSet)
|
|
loadPosition(currentPosition);
|
|
}
|
|
|
|
const std::vector<RuneAspekt>& LolAutoAccept::getRuneAspekts() {
|
|
if(runeaspekts.empty()) {
|
|
if(clientapi) {
|
|
runeaspekts = clientapi->getAllRuneAspekts();
|
|
qInfo() << "Loaded " << runeaspekts.size() << " rune aspekts";
|
|
}
|
|
}
|
|
|
|
return runeaspekts;
|
|
}
|
|
|
|
const std::vector<RuneStyle>& LolAutoAccept::getRuneStyles() {
|
|
if(runestyles.empty()) {
|
|
if(clientapi) {
|
|
runestyles = clientapi->getAllRuneStyles();
|
|
qInfo() << "Loaded " << runestyles.size() << " rune styles";
|
|
}
|
|
}
|
|
|
|
return runestyles;
|
|
}
|
|
|
|
void LolAutoAccept::setAutoWriteText(bool enabled, const QString& text) {
|
|
if ( enabled && !autoWriteTextEnabled ) {
|
|
// only re-write on rising edge
|
|
autoWriteTextDone = false;
|
|
}
|
|
autoWriteTextEnabled = enabled;
|
|
autoWriteText = text;
|
|
}
|
|
|
|
void LolAutoAccept::stopJoinThread() {
|
|
stop();
|
|
|
|
if(lolaathread.joinable()) {
|
|
lolaathread.join();
|
|
}
|
|
resetAllOffsets();
|
|
}
|
|
|
|
void LolAutoAccept::innerRun() {
|
|
shouldrun = true;
|
|
|
|
try {
|
|
auto convs = clientapi->getAllConversations();
|
|
qInfo() << "got " << convs.size() << " conversations";
|
|
|
|
emit statusChanged(Status::Running);
|
|
while(shouldrun) {
|
|
uint32_t extrasleep = 800;
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
|
|
auto phase = clientapi->getGameflowPhase();
|
|
qInfo() << "current Gameflowphase: " << phase;
|
|
|
|
// do processing
|
|
if(phase == ClientAPI::GameflowPhase::LOBBY) {
|
|
resetAllOffsets();
|
|
} else if(phase == ClientAPI::GameflowPhase::MATCHMAKING) {
|
|
extrasleep = 200;
|
|
resetAllOffsets();
|
|
} else if(phase == ClientAPI::GameflowPhase::READYCHECK) {
|
|
if(stages.at(0).enabled) { // auto accept enabled
|
|
auto state = clientapi->getReadyCheckState();
|
|
|
|
qInfo() << "readychack state: " << state;
|
|
if(state == ClientAPI::ReadyCheckState::INPROGRESS) {
|
|
qInfo() << "auto accepting";
|
|
clientapi->acceptMatch();
|
|
}
|
|
}
|
|
resetAllOffsets();
|
|
extrasleep = 0; // no extra sleep
|
|
} else if(phase == ClientAPI::GameflowPhase::CHAMPSELECT) {
|
|
champSelect();
|
|
extrasleep = 0; // no extra sleep
|
|
} else if(phase == ClientAPI::GameflowPhase::INPROGRESS) {
|
|
extrasleep = 30000; // 30s bonus sleep
|
|
resetAllOffsets();
|
|
} else if(phase == ClientAPI::GameflowPhase::ENDOFGAME) {
|
|
extrasleep = 2000; // 2 s bonus sleep
|
|
resetAllOffsets();
|
|
} else if(phase == ClientAPI::GameflowPhase::PREENDOFGAME) {
|
|
extrasleep = 4000; // 4 s bonus sleep
|
|
resetAllOffsets();
|
|
} else if(phase == ClientAPI::GameflowPhase::WAITINGFORSTATS) {
|
|
extrasleep = 4000; // 4 s bonus sleep
|
|
resetAllOffsets();
|
|
} else if(phase == ClientAPI::GameflowPhase::NONE) {
|
|
extrasleep = 10000; // 10 s bonus sleep - no lobby
|
|
resetAllOffsets();
|
|
}
|
|
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
std::chrono::duration<double> dur = (end-start);
|
|
//if(dur.count() > 1e-5)
|
|
Log::note << "iteration took: " << (dur.count() * 1000) << " ms extrasleep: " << extrasleep;
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(400 + extrasleep));
|
|
}
|
|
emit statusChanged(Status::Off);
|
|
} catch(RestClient::WebException& e) {
|
|
qCritical() << "WebException catched: " << e.curlresponse;
|
|
if(e.curlresponse == CURLE_COULDNT_CONNECT) {
|
|
// free clientapi
|
|
clientapi.reset();
|
|
|
|
// disable this thread
|
|
shouldrun = false;
|
|
|
|
}
|
|
// notify the ui
|
|
emit statusChanged(Status::Failed);
|
|
}
|
|
}
|
|
|
|
void LolAutoAccept::resetPickOffsets() {
|
|
for(Stage& stage : stages) {
|
|
stage.currentOffset = 0;
|
|
}
|
|
lastPickedChamp = 0;
|
|
}
|
|
|
|
void LolAutoAccept::resetAllOffsets() {
|
|
resetPickOffsets();
|
|
currentPosition = Position::INVALID;
|
|
currentPositionSet = false;
|
|
chatid.clear();
|
|
autoWriteTextDone = false;
|
|
}
|
|
|
|
void LolAutoAccept::applyConfigToStage(Stage& stage, const Config::StageConfig& stageconf) {
|
|
stage.champids = dd.resolveChampIDs(stageconf.champs);
|
|
stage.enabled = stageconf.enabled;
|
|
stage.currentOffset = 0;
|
|
}
|
|
|
|
void LolAutoAccept::loadPosition(Position pos) {
|
|
Log::trace << __PRETTY_FUNCTION__ << " pos: " << pos;
|
|
|
|
// reinit the stages
|
|
std::lock_guard lock(stagesMutex);
|
|
stages.resize(1); // first stage does not change
|
|
stages.resize(3);
|
|
|
|
std::shared_ptr<Config::PositionConfig> posconf = config.getPositionConfig(pos);
|
|
|
|
applyConfigToStage(stages.at((int) State::BAN), posconf->ban);
|
|
applyConfigToStage(stages.at((int) State::PICK), posconf->pick);
|
|
|
|
currentPosition = pos;
|
|
currentPositionSet = true;
|
|
}
|
|
|
|
uint32_t LolAutoAccept::getChampOfState(State s) {
|
|
assert(s > State::LOBBY && s <= State::PICK);
|
|
|
|
Stage& stage = stages[(int) s];
|
|
uint32_t size = stage.champids.size();
|
|
if(stage.currentOffset >= size) {
|
|
// no champ to try left
|
|
qWarning() << "no champ left at stage: " << (int) s;
|
|
qWarning() << "stage size: " << stage.champids.size();
|
|
return 0;
|
|
}
|
|
|
|
return stage.champids[stage.currentOffset];
|
|
}
|
|
|
|
void LolAutoAccept::nextChampOfState(State s) {
|
|
assert(s > State::LOBBY && s <= State::PICK);
|
|
|
|
Stage& stage = stages[(int) s];
|
|
uint32_t size = stage.champids.size();
|
|
|
|
stage.currentOffset++;
|
|
|
|
if(stage.currentOffset >= size) {
|
|
// no champ to try left
|
|
qWarning() << "no champ left at stage: " << (int) s;
|
|
qWarning() << "stage size: " << stage.champids.size();
|
|
|
|
stage.currentOffset = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool LolAutoAccept::isChampIntentofTeammate(uint32_t champid, const ClientAPI::ChampSelectSession& session) {
|
|
for(const ClientAPI::ChampSelectCell& player : session.myTeam) {
|
|
if(player.championID == (int32_t) champid || player.championPickIntentID == (int32_t) champid) {
|
|
qInfo() << "player " << player.cellID << " @ " << toString(player.position) << " wants to play " << champid;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LolAutoAccept::isChampBanned(uint32_t champid, const ClientAPI::ChampSelectSession& session) {
|
|
for(const ClientAPI::ChampSelectAction& act : session.actions) {
|
|
if(act.type == ClientAPI::ChampSelectActionType::BAN && act.championID == (int32_t) champid && act.completed) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
LolAutoAccept::ownactions_t LolAutoAccept::getOwnActions(int32_t cellid, const std::vector<ClientAPI::ChampSelectAction> actions) {
|
|
ownactions_t out;
|
|
for(const auto& it : actions) {
|
|
if(it.actorCellID == cellid) {
|
|
qDebug() << "ownaction: id: " << it.id << " champid: " << it.championID << " completed: " << it.completed << " type: " << it.type;
|
|
if(!it.completed) { // completed cant be interacted anyways, so just ignore them.
|
|
out.push_back(it);
|
|
}
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
void LolAutoAccept::prepickPhase(const ownactions_t& ownactions) {
|
|
phase(ownactions, ClientAPI::ChampSelectActionType::PICK, State::PICK, false);
|
|
}
|
|
|
|
void LolAutoAccept::banPhase(const ownactions_t& ownactions, const ClientAPI::ChampSelectSession& session) {
|
|
phase(ownactions, ClientAPI::ChampSelectActionType::BAN, State::BAN, true, [session](uint32_t champid) {
|
|
return !isChampIntentofTeammate(champid, session) && !isChampBanned(champid, session);
|
|
});
|
|
}
|
|
|
|
void LolAutoAccept::pickPhase(const ownactions_t& ownactions) {
|
|
phase(ownactions, ClientAPI::ChampSelectActionType::PICK, State::PICK, true);
|
|
}
|
|
|
|
void LolAutoAccept::phase(const ownactions_t& ownactions, ClientAPI::ChampSelectActionType type, State s, bool complete, std::function<bool(uint32_t)> filter) {
|
|
if ( !( stages.at((int) s).enabled ) ) {
|
|
qDebug() << (int) s << " stage is disabled. skipping";
|
|
return;
|
|
}
|
|
|
|
for(auto it : ownactions) {
|
|
if(it.type == type) {
|
|
qInfo() << type << " action anvailable: " << it.id << " champid: " << it.championID;
|
|
uint32_t champid = getChampOfState(s);
|
|
|
|
if(filter) {
|
|
// filter says no
|
|
if(!filter(champid)) {
|
|
Log::trace << "champid: " << champid << " filter says no - next champ";
|
|
nextChampOfState(s);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if((it.championID != (int32_t) champid || !it.completed) && champid != 0) {
|
|
// try to prepick a champion
|
|
qInfo() << "try to pick champ: " << champid;
|
|
|
|
if(!clientapi->setChampSelectAction(it.id, champid, complete)) {
|
|
nextChampOfState(s);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t LolAutoAccept::getBestRunePage(const std::vector<ClientAPI::RunePage>& pages) {
|
|
qDebug() << "searching RunePages: " << pages.size();
|
|
for(uint32_t i = 0; i < pages.size(); ++i) {
|
|
qDebug() << i << ": " << pages[i].id << " " << pages[i].name << " " << pages[i].isCurrent;
|
|
}
|
|
|
|
// search for a rune page with a name that starts with "AA: "
|
|
for(uint32_t i = 0; i < pages.size(); ++i) {
|
|
const ClientAPI::RunePage& rp = pages.at(i);
|
|
if(rp.name.size() >= 4 && rp.name.left(4) == "AA: ") {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// search for a selected rune page
|
|
for(uint32_t i = 0; i < pages.size(); ++i) {
|
|
const ClientAPI::RunePage& rp = pages.at(i);
|
|
if(rp.isCurrent) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int32_t LolAutoAccept::getMatchingRunePage(const RunePage& rp, const std::vector<ClientAPI::RunePage>& allpages) {
|
|
auto it = std::find_if(allpages.begin(), allpages.end(), [rp](const ClientAPI::RunePage& r){ return r.runepage == rp; });
|
|
|
|
return (it == allpages.end()) ? -1 : it - allpages.begin();
|
|
}
|
|
|
|
void LolAutoAccept::champSelect() {
|
|
auto session = clientapi->getChampSelectSession();
|
|
const int32_t cellid = session.localPlayerCellId;
|
|
|
|
if(cellid != lastCellId && lastCellId != -1) {
|
|
resetPickOffsets();
|
|
}
|
|
lastCellId = cellid;
|
|
|
|
// find own cellid info
|
|
const ClientAPI::ChampSelectCell* me = nullptr;
|
|
uint32_t pickedChamp = 0;
|
|
for(const auto& it : session.myTeam) {
|
|
if(it.cellID == cellid) {
|
|
me = ⁢
|
|
pickedChamp = it.championID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Position pos = me ? me->position : Position::INVALID;
|
|
|
|
// check if runes need adjustment
|
|
if(pickedChamp != lastPickedChamp) {
|
|
qInfo() << "picked champ changed from: " << lastPickedChamp << " to: " << pickedChamp;
|
|
lastPickedChamp = pickedChamp;
|
|
}
|
|
|
|
// reload config based on position if changed
|
|
if(pos != currentPosition || !currentPositionSet) {
|
|
Log::note << "LolAutoAccept reloading config for position: " << pos << " because it was: " << currentPosition;
|
|
loadPosition(pos);
|
|
|
|
emit positionChanged(pos);
|
|
}
|
|
|
|
qDebug() << "cellid: " << cellid << " position: " << toString(pos) << " counter: " << session.counter << " timer: timeleftip: " << session.timer.adjustedTimeLeftInPhase << " totaltimephase: " << session.timer.totalTimeInPhase;
|
|
// find actions for own cell
|
|
auto ownactions = getOwnActions(cellid, session.actions);
|
|
qDebug() << "ownactions: " << ownactions.size();
|
|
|
|
// try to prepick champ
|
|
qInfo() << "champselectphase: " << session.timer.phase;
|
|
|
|
std::lock_guard lock(stagesMutex);
|
|
if(session.timer.phase == ClientAPI::ChampSelectPhase::PLANNING) {
|
|
prepickPhase(ownactions);
|
|
} else if(session.timer.phase == ClientAPI::ChampSelectPhase::BAN_PICK) {
|
|
banPhase(ownactions, session);
|
|
pickPhase(ownactions);
|
|
} else if(session.timer.phase == ClientAPI::ChampSelectPhase::FINALIZATION) {
|
|
// trade?
|
|
|
|
// check for smite
|
|
if(smiteWarnEnabled) {
|
|
smiteWarning(session.myTeam);
|
|
}
|
|
}
|
|
|
|
// check for autowriteText
|
|
if(autoWriteTextEnabled && !autoWriteTextDone && !autoWriteText.isEmpty()) {
|
|
const QString& chatid = getChatid();
|
|
if(!chatid.isEmpty()) {
|
|
clientapi->sendMessage(chatid, autoWriteText);
|
|
autoWriteTextDone = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LolAutoAccept::smiteWarning(const std::vector<ClientAPI::ChampSelectCell>& cells) {
|
|
uint32_t smiteCount = 0;
|
|
for(const ClientAPI::ChampSelectCell& member : cells) {
|
|
qInfo() << "position: " << toString(member.position) << " spells: " << member.spell1Id << " " << member.spell2Id;
|
|
smiteCount += (member.spell1Id == 11 || member.spell2Id == 11);
|
|
}
|
|
|
|
if(smiteCount != 1) {
|
|
// check timeout
|
|
std::chrono::time_point<std::chrono::system_clock> currenttime = std::chrono::system_clock::now();
|
|
if((currenttime - lastMessageSent) < std::chrono::seconds(2)) {
|
|
return;
|
|
}
|
|
|
|
qInfo() << "smite warning: " << smiteCount;
|
|
const QString& chatid = getChatid();
|
|
if(!chatid.isEmpty()) {
|
|
clientapi->sendMessage(chatid, "smite");
|
|
lastMessageSent = currenttime;
|
|
}
|
|
}
|
|
}
|
|
|
|
const QString& LolAutoAccept::getChatid() {
|
|
if(chatid.isEmpty()) {
|
|
std::vector<ClientAPI::Conversation> convs = clientapi->getAllConversations();
|
|
qInfo() << "got " << convs.size() << " conversations";
|
|
|
|
for(const ClientAPI::Conversation& conv : convs) {
|
|
qInfo() << " id: " << conv.id << " type: " << conv.type << " name: " << conv.name << " gameName: " << conv.gameName;
|
|
if(conv.type == "championSelect" && conv.name.isEmpty()) {
|
|
qInfo() << "groupchat found";
|
|
chatid = conv.id;
|
|
return chatid;
|
|
}
|
|
}
|
|
}
|
|
return chatid; //might be empty string
|
|
}
|