#include "lolautoaccept.h" #include #include #include #include LolAutoAccept::Stage::Stage() {} LolAutoAccept::Stage::~Stage() {} LolAutoAccept::LolAutoAccept(Config::RootConfig& config, DataDragon& dd, QObject* parent) : QObject(parent), config(config), dd(dd) { qRegisterMetaType(); std::lock_guard lock(stagesMutex); stages.resize(3); // accept, ban, pick } LolAutoAccept::~LolAutoAccept() { stopJoinThread(); } void LolAutoAccept::setChamps(const std::vector& 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(*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& LolAutoAccept::getRuneAspekts() { if(runeaspekts.empty()) { if(clientapi) { runeaspekts = clientapi->getAllRuneAspekts(); qInfo() << "Loaded " << runeaspekts.size() << " rune aspekts"; } } return runeaspekts; } const std::vector& 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 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 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 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 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& 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& 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& 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 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 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 }